xref: /kernel/linux/linux-6.6/arch/x86/xen/smp_pv.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Xen SMP support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file implements the Xen versions of smp_ops.  SMP under Xen is
662306a36Sopenharmony_ci * very straightforward.  Bringing a CPU up is simply a matter of
762306a36Sopenharmony_ci * loading its initial context and setting it running.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * IPIs are handled through the Xen event mechanism.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Because virtual CPUs can be scheduled onto any real CPU, there's no
1262306a36Sopenharmony_ci * useful topology information for the kernel to make use of.  As a
1362306a36Sopenharmony_ci * result, all CPUs are treated as if they're single-core and
1462306a36Sopenharmony_ci * single-threaded.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci#include <linux/sched.h>
1762306a36Sopenharmony_ci#include <linux/sched/task_stack.h>
1862306a36Sopenharmony_ci#include <linux/err.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/smp.h>
2162306a36Sopenharmony_ci#include <linux/irq_work.h>
2262306a36Sopenharmony_ci#include <linux/tick.h>
2362306a36Sopenharmony_ci#include <linux/nmi.h>
2462306a36Sopenharmony_ci#include <linux/cpuhotplug.h>
2562306a36Sopenharmony_ci#include <linux/stackprotector.h>
2662306a36Sopenharmony_ci#include <linux/pgtable.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <asm/paravirt.h>
2962306a36Sopenharmony_ci#include <asm/idtentry.h>
3062306a36Sopenharmony_ci#include <asm/desc.h>
3162306a36Sopenharmony_ci#include <asm/cpu.h>
3262306a36Sopenharmony_ci#include <asm/io_apic.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <xen/interface/xen.h>
3562306a36Sopenharmony_ci#include <xen/interface/vcpu.h>
3662306a36Sopenharmony_ci#include <xen/interface/xenpmu.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <asm/spec-ctrl.h>
3962306a36Sopenharmony_ci#include <asm/xen/interface.h>
4062306a36Sopenharmony_ci#include <asm/xen/hypercall.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include <xen/xen.h>
4362306a36Sopenharmony_ci#include <xen/page.h>
4462306a36Sopenharmony_ci#include <xen/events.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <xen/hvc-console.h>
4762306a36Sopenharmony_ci#include "xen-ops.h"
4862306a36Sopenharmony_ci#include "mmu.h"
4962306a36Sopenharmony_ci#include "smp.h"
5062306a36Sopenharmony_ci#include "pmu.h"
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cicpumask_var_t xen_cpu_initialized_map;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct xen_common_irq, xen_irq_work) = { .irq = -1 };
5562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct xen_common_irq, xen_pmu_irq) = { .irq = -1 };
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic irqreturn_t xen_irq_work_interrupt(int irq, void *dev_id);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void cpu_bringup(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	int cpu;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	cr4_init();
6462306a36Sopenharmony_ci	cpuhp_ap_sync_alive();
6562306a36Sopenharmony_ci	cpu_init();
6662306a36Sopenharmony_ci	fpu__init_cpu();
6762306a36Sopenharmony_ci	touch_softlockup_watchdog();
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* PVH runs in ring 0 and allows us to do native syscalls. Yay! */
7062306a36Sopenharmony_ci	if (!xen_feature(XENFEAT_supervisor_mode_kernel)) {
7162306a36Sopenharmony_ci		xen_enable_sysenter();
7262306a36Sopenharmony_ci		xen_enable_syscall();
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci	cpu = smp_processor_id();
7562306a36Sopenharmony_ci	smp_store_cpu_info(cpu);
7662306a36Sopenharmony_ci	cpu_data(cpu).x86_max_cores = 1;
7762306a36Sopenharmony_ci	set_cpu_sibling_map(cpu);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	speculative_store_bypass_ht_init();
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	xen_setup_cpu_clockevents();
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	notify_cpu_starting(cpu);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	set_cpu_online(cpu, true);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	smp_mb();
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* We can take interrupts now: we're officially "up". */
9062306a36Sopenharmony_ci	local_irq_enable();
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciasmlinkage __visible void cpu_bringup_and_idle(void)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	cpu_bringup();
9662306a36Sopenharmony_ci	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_civoid xen_smp_intr_free_pv(unsigned int cpu)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	kfree(per_cpu(xen_irq_work, cpu).name);
10262306a36Sopenharmony_ci	per_cpu(xen_irq_work, cpu).name = NULL;
10362306a36Sopenharmony_ci	if (per_cpu(xen_irq_work, cpu).irq >= 0) {
10462306a36Sopenharmony_ci		unbind_from_irqhandler(per_cpu(xen_irq_work, cpu).irq, NULL);
10562306a36Sopenharmony_ci		per_cpu(xen_irq_work, cpu).irq = -1;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	kfree(per_cpu(xen_pmu_irq, cpu).name);
10962306a36Sopenharmony_ci	per_cpu(xen_pmu_irq, cpu).name = NULL;
11062306a36Sopenharmony_ci	if (per_cpu(xen_pmu_irq, cpu).irq >= 0) {
11162306a36Sopenharmony_ci		unbind_from_irqhandler(per_cpu(xen_pmu_irq, cpu).irq, NULL);
11262306a36Sopenharmony_ci		per_cpu(xen_pmu_irq, cpu).irq = -1;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciint xen_smp_intr_init_pv(unsigned int cpu)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int rc;
11962306a36Sopenharmony_ci	char *callfunc_name, *pmu_name;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	callfunc_name = kasprintf(GFP_KERNEL, "irqwork%d", cpu);
12262306a36Sopenharmony_ci	per_cpu(xen_irq_work, cpu).name = callfunc_name;
12362306a36Sopenharmony_ci	rc = bind_ipi_to_irqhandler(XEN_IRQ_WORK_VECTOR,
12462306a36Sopenharmony_ci				    cpu,
12562306a36Sopenharmony_ci				    xen_irq_work_interrupt,
12662306a36Sopenharmony_ci				    IRQF_PERCPU|IRQF_NOBALANCING,
12762306a36Sopenharmony_ci				    callfunc_name,
12862306a36Sopenharmony_ci				    NULL);
12962306a36Sopenharmony_ci	if (rc < 0)
13062306a36Sopenharmony_ci		goto fail;
13162306a36Sopenharmony_ci	per_cpu(xen_irq_work, cpu).irq = rc;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (is_xen_pmu) {
13462306a36Sopenharmony_ci		pmu_name = kasprintf(GFP_KERNEL, "pmu%d", cpu);
13562306a36Sopenharmony_ci		per_cpu(xen_pmu_irq, cpu).name = pmu_name;
13662306a36Sopenharmony_ci		rc = bind_virq_to_irqhandler(VIRQ_XENPMU, cpu,
13762306a36Sopenharmony_ci					     xen_pmu_irq_handler,
13862306a36Sopenharmony_ci					     IRQF_PERCPU|IRQF_NOBALANCING,
13962306a36Sopenharmony_ci					     pmu_name, NULL);
14062306a36Sopenharmony_ci		if (rc < 0)
14162306a36Sopenharmony_ci			goto fail;
14262306a36Sopenharmony_ci		per_cpu(xen_pmu_irq, cpu).irq = rc;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci fail:
14862306a36Sopenharmony_ci	xen_smp_intr_free_pv(cpu);
14962306a36Sopenharmony_ci	return rc;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void __init _get_smp_config(unsigned int early)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int i, rc;
15562306a36Sopenharmony_ci	unsigned int subtract = 0;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (early)
15862306a36Sopenharmony_ci		return;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	num_processors = 0;
16162306a36Sopenharmony_ci	disabled_cpus = 0;
16262306a36Sopenharmony_ci	for (i = 0; i < nr_cpu_ids; i++) {
16362306a36Sopenharmony_ci		rc = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL);
16462306a36Sopenharmony_ci		if (rc >= 0) {
16562306a36Sopenharmony_ci			num_processors++;
16662306a36Sopenharmony_ci			set_cpu_possible(i, true);
16762306a36Sopenharmony_ci		} else {
16862306a36Sopenharmony_ci			set_cpu_possible(i, false);
16962306a36Sopenharmony_ci			set_cpu_present(i, false);
17062306a36Sopenharmony_ci			subtract++;
17162306a36Sopenharmony_ci		}
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
17462306a36Sopenharmony_ci	/* This is akin to using 'nr_cpus' on the Linux command line.
17562306a36Sopenharmony_ci	 * Which is OK as when we use 'dom0_max_vcpus=X' we can only
17662306a36Sopenharmony_ci	 * have up to X, while nr_cpu_ids is greater than X. This
17762306a36Sopenharmony_ci	 * normally is not a problem, except when CPU hotplugging
17862306a36Sopenharmony_ci	 * is involved and then there might be more than X CPUs
17962306a36Sopenharmony_ci	 * in the guest - which will not work as there is no
18062306a36Sopenharmony_ci	 * hypercall to expand the max number of VCPUs an already
18162306a36Sopenharmony_ci	 * running guest has. So cap it up to X. */
18262306a36Sopenharmony_ci	if (subtract)
18362306a36Sopenharmony_ci		set_nr_cpu_ids(nr_cpu_ids - subtract);
18462306a36Sopenharmony_ci#endif
18562306a36Sopenharmony_ci	/* Pretend to be a proper enumerated system */
18662306a36Sopenharmony_ci	smp_found_config = 1;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic void __init xen_pv_smp_prepare_boot_cpu(void)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	BUG_ON(smp_processor_id() != 0);
19262306a36Sopenharmony_ci	native_smp_prepare_boot_cpu();
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!xen_feature(XENFEAT_writable_page_tables))
19562306a36Sopenharmony_ci		/* We've switched to the "real" per-cpu gdt, so make
19662306a36Sopenharmony_ci		 * sure the old memory can be recycled. */
19762306a36Sopenharmony_ci		make_lowmem_page_readwrite(xen_initial_gdt);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	xen_setup_vcpu_info_placement();
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * The alternative logic (which patches the unlock/lock) runs before
20362306a36Sopenharmony_ci	 * the smp bootup up code is activated. Hence we need to set this up
20462306a36Sopenharmony_ci	 * the core kernel is being patched. Otherwise we will have only
20562306a36Sopenharmony_ci	 * modules patched but not core code.
20662306a36Sopenharmony_ci	 */
20762306a36Sopenharmony_ci	xen_init_spinlocks();
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void __init xen_pv_smp_prepare_cpus(unsigned int max_cpus)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	unsigned cpu;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (ioapic_is_disabled) {
21562306a36Sopenharmony_ci		char *m = (max_cpus == 0) ?
21662306a36Sopenharmony_ci			"The nosmp parameter is incompatible with Xen; " \
21762306a36Sopenharmony_ci			"use Xen dom0_max_vcpus=1 parameter" :
21862306a36Sopenharmony_ci			"The noapic parameter is incompatible with Xen";
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		xen_raw_printk(m);
22162306a36Sopenharmony_ci		panic(m);
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	xen_init_lock_cpu(0);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	smp_prepare_cpus_common();
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	cpu_data(0).x86_max_cores = 1;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	speculative_store_bypass_ht_init();
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	xen_pmu_init(0);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (xen_smp_intr_init(0) || xen_smp_intr_init_pv(0))
23462306a36Sopenharmony_ci		BUG();
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (!alloc_cpumask_var(&xen_cpu_initialized_map, GFP_KERNEL))
23762306a36Sopenharmony_ci		panic("could not allocate xen_cpu_initialized_map\n");
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	cpumask_copy(xen_cpu_initialized_map, cpumask_of(0));
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Restrict the possible_map according to max_cpus. */
24262306a36Sopenharmony_ci	while ((num_possible_cpus() > 1) && (num_possible_cpus() > max_cpus)) {
24362306a36Sopenharmony_ci		for (cpu = nr_cpu_ids - 1; !cpu_possible(cpu); cpu--)
24462306a36Sopenharmony_ci			continue;
24562306a36Sopenharmony_ci		set_cpu_possible(cpu, false);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	for_each_possible_cpu(cpu)
24962306a36Sopenharmony_ci		set_cpu_present(cpu, true);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int
25362306a36Sopenharmony_cicpu_initialize_context(unsigned int cpu, struct task_struct *idle)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct vcpu_guest_context *ctxt;
25662306a36Sopenharmony_ci	struct desc_struct *gdt;
25762306a36Sopenharmony_ci	unsigned long gdt_mfn;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (cpumask_test_and_set_cpu(cpu, xen_cpu_initialized_map))
26062306a36Sopenharmony_ci		return 0;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL);
26362306a36Sopenharmony_ci	if (ctxt == NULL) {
26462306a36Sopenharmony_ci		cpumask_clear_cpu(cpu, xen_cpu_initialized_map);
26562306a36Sopenharmony_ci		return -ENOMEM;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	gdt = get_cpu_gdt_rw(cpu);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/*
27162306a36Sopenharmony_ci	 * Bring up the CPU in cpu_bringup_and_idle() with the stack
27262306a36Sopenharmony_ci	 * pointing just below where pt_regs would be if it were a normal
27362306a36Sopenharmony_ci	 * kernel entry.
27462306a36Sopenharmony_ci	 */
27562306a36Sopenharmony_ci	ctxt->user_regs.eip = (unsigned long)asm_cpu_bringup_and_idle;
27662306a36Sopenharmony_ci	ctxt->flags = VGCF_IN_KERNEL;
27762306a36Sopenharmony_ci	ctxt->user_regs.eflags = 0x1000; /* IOPL_RING1 */
27862306a36Sopenharmony_ci	ctxt->user_regs.ds = __USER_DS;
27962306a36Sopenharmony_ci	ctxt->user_regs.es = __USER_DS;
28062306a36Sopenharmony_ci	ctxt->user_regs.ss = __KERNEL_DS;
28162306a36Sopenharmony_ci	ctxt->user_regs.cs = __KERNEL_CS;
28262306a36Sopenharmony_ci	ctxt->user_regs.esp = (unsigned long)task_pt_regs(idle);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	xen_copy_trap_info(ctxt->trap_ctxt);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	BUG_ON((unsigned long)gdt & ~PAGE_MASK);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	gdt_mfn = arbitrary_virt_to_mfn(gdt);
28962306a36Sopenharmony_ci	make_lowmem_page_readonly(gdt);
29062306a36Sopenharmony_ci	make_lowmem_page_readonly(mfn_to_virt(gdt_mfn));
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ctxt->gdt_frames[0] = gdt_mfn;
29362306a36Sopenharmony_ci	ctxt->gdt_ents      = GDT_ENTRIES;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/*
29662306a36Sopenharmony_ci	 * Set SS:SP that Xen will use when entering guest kernel mode
29762306a36Sopenharmony_ci	 * from guest user mode.  Subsequent calls to load_sp0() can
29862306a36Sopenharmony_ci	 * change this value.
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	ctxt->kernel_ss = __KERNEL_DS;
30162306a36Sopenharmony_ci	ctxt->kernel_sp = task_top_of_stack(idle);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ctxt->gs_base_kernel = per_cpu_offset(cpu);
30462306a36Sopenharmony_ci	ctxt->event_callback_eip    =
30562306a36Sopenharmony_ci		(unsigned long)xen_asm_exc_xen_hypervisor_callback;
30662306a36Sopenharmony_ci	ctxt->failsafe_callback_eip =
30762306a36Sopenharmony_ci		(unsigned long)xen_failsafe_callback;
30862306a36Sopenharmony_ci	per_cpu(xen_cr3, cpu) = __pa(swapper_pg_dir);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	ctxt->ctrlreg[3] = xen_pfn_to_cr3(virt_to_gfn(swapper_pg_dir));
31162306a36Sopenharmony_ci	if (HYPERVISOR_vcpu_op(VCPUOP_initialise, xen_vcpu_nr(cpu), ctxt))
31262306a36Sopenharmony_ci		BUG();
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	kfree(ctxt);
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int xen_pv_kick_ap(unsigned int cpu, struct task_struct *idle)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int rc;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	rc = common_cpu_up(cpu, idle);
32362306a36Sopenharmony_ci	if (rc)
32462306a36Sopenharmony_ci		return rc;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	xen_setup_runstate_info(cpu);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* make sure interrupts start blocked */
32962306a36Sopenharmony_ci	per_cpu(xen_vcpu, cpu)->evtchn_upcall_mask = 1;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	rc = cpu_initialize_context(cpu, idle);
33262306a36Sopenharmony_ci	if (rc)
33362306a36Sopenharmony_ci		return rc;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	xen_pmu_init(cpu);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/*
33862306a36Sopenharmony_ci	 * Why is this a BUG? If the hypercall fails then everything can be
33962306a36Sopenharmony_ci	 * rolled back, no?
34062306a36Sopenharmony_ci	 */
34162306a36Sopenharmony_ci	BUG_ON(HYPERVISOR_vcpu_op(VCPUOP_up, xen_vcpu_nr(cpu), NULL));
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void xen_pv_poll_sync_state(void)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
35262306a36Sopenharmony_cistatic int xen_pv_cpu_disable(void)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
35562306a36Sopenharmony_ci	if (cpu == 0)
35662306a36Sopenharmony_ci		return -EBUSY;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	cpu_disable_common();
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	load_cr3(swapper_pg_dir);
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void xen_pv_cpu_die(unsigned int cpu)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	while (HYPERVISOR_vcpu_op(VCPUOP_is_up, xen_vcpu_nr(cpu), NULL)) {
36762306a36Sopenharmony_ci		__set_current_state(TASK_UNINTERRUPTIBLE);
36862306a36Sopenharmony_ci		schedule_timeout(HZ/10);
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void xen_pv_cleanup_dead_cpu(unsigned int cpu)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	xen_smp_intr_free(cpu);
37562306a36Sopenharmony_ci	xen_uninit_lock_cpu(cpu);
37662306a36Sopenharmony_ci	xen_teardown_timer(cpu);
37762306a36Sopenharmony_ci	xen_pmu_finish(cpu);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void __noreturn xen_pv_play_dead(void) /* used only with HOTPLUG_CPU */
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	play_dead_common();
38362306a36Sopenharmony_ci	HYPERVISOR_vcpu_op(VCPUOP_down, xen_vcpu_nr(smp_processor_id()), NULL);
38462306a36Sopenharmony_ci	xen_cpu_bringup_again((unsigned long)task_pt_regs(current));
38562306a36Sopenharmony_ci	BUG();
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci#else /* !CONFIG_HOTPLUG_CPU */
38962306a36Sopenharmony_cistatic int xen_pv_cpu_disable(void)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	return -ENOSYS;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic void xen_pv_cpu_die(unsigned int cpu)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	BUG();
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void xen_pv_cleanup_dead_cpu(unsigned int cpu)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	BUG();
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void __noreturn xen_pv_play_dead(void)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	BUG();
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci#endif
41062306a36Sopenharmony_cistatic void stop_self(void *v)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	int cpu = smp_processor_id();
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* make sure we're not pinning something down */
41562306a36Sopenharmony_ci	load_cr3(swapper_pg_dir);
41662306a36Sopenharmony_ci	/* should set up a minimal gdt */
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	set_cpu_online(cpu, false);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	HYPERVISOR_vcpu_op(VCPUOP_down, xen_vcpu_nr(cpu), NULL);
42162306a36Sopenharmony_ci	BUG();
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void xen_pv_stop_other_cpus(int wait)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	smp_call_function(stop_self, NULL, wait);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic irqreturn_t xen_irq_work_interrupt(int irq, void *dev_id)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	irq_work_run();
43262306a36Sopenharmony_ci	inc_irq_stat(apic_irq_work_irqs);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return IRQ_HANDLED;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic const struct smp_ops xen_smp_ops __initconst = {
43862306a36Sopenharmony_ci	.smp_prepare_boot_cpu = xen_pv_smp_prepare_boot_cpu,
43962306a36Sopenharmony_ci	.smp_prepare_cpus = xen_pv_smp_prepare_cpus,
44062306a36Sopenharmony_ci	.smp_cpus_done = xen_smp_cpus_done,
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	.kick_ap_alive = xen_pv_kick_ap,
44362306a36Sopenharmony_ci	.cpu_die = xen_pv_cpu_die,
44462306a36Sopenharmony_ci	.cleanup_dead_cpu = xen_pv_cleanup_dead_cpu,
44562306a36Sopenharmony_ci	.poll_sync_state = xen_pv_poll_sync_state,
44662306a36Sopenharmony_ci	.cpu_disable = xen_pv_cpu_disable,
44762306a36Sopenharmony_ci	.play_dead = xen_pv_play_dead,
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	.stop_other_cpus = xen_pv_stop_other_cpus,
45062306a36Sopenharmony_ci	.smp_send_reschedule = xen_smp_send_reschedule,
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	.send_call_func_ipi = xen_smp_send_call_function_ipi,
45362306a36Sopenharmony_ci	.send_call_func_single_ipi = xen_smp_send_call_function_single_ipi,
45462306a36Sopenharmony_ci};
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_civoid __init xen_smp_init(void)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	smp_ops = xen_smp_ops;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Avoid searching for BIOS MP tables */
46162306a36Sopenharmony_ci	x86_init.mpparse.find_smp_config = x86_init_noop;
46262306a36Sopenharmony_ci	x86_init.mpparse.get_smp_config = _get_smp_config;
46362306a36Sopenharmony_ci}
464