xref: /kernel/linux/linux-6.6/arch/x86/xen/enlighten.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
462306a36Sopenharmony_ci#include <linux/memblock.h>
562306a36Sopenharmony_ci#endif
662306a36Sopenharmony_ci#include <linux/console.h>
762306a36Sopenharmony_ci#include <linux/cpu.h>
862306a36Sopenharmony_ci#include <linux/kexec.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/panic_notifier.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <xen/xen.h>
1362306a36Sopenharmony_ci#include <xen/features.h>
1462306a36Sopenharmony_ci#include <xen/interface/sched.h>
1562306a36Sopenharmony_ci#include <xen/interface/version.h>
1662306a36Sopenharmony_ci#include <xen/page.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <asm/xen/hypercall.h>
1962306a36Sopenharmony_ci#include <asm/xen/hypervisor.h>
2062306a36Sopenharmony_ci#include <asm/cpu.h>
2162306a36Sopenharmony_ci#include <asm/e820/api.h>
2262306a36Sopenharmony_ci#include <asm/setup.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "xen-ops.h"
2562306a36Sopenharmony_ci#include "smp.h"
2662306a36Sopenharmony_ci#include "pmu.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hypercall_page);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * Pointer to the xen_vcpu_info structure or
3262306a36Sopenharmony_ci * &HYPERVISOR_shared_info->vcpu_info[cpu]. See xen_hvm_init_shared_info
3362306a36Sopenharmony_ci * and xen_vcpu_setup for details. By default it points to share_info->vcpu_info
3462306a36Sopenharmony_ci * but during boot it is switched to point to xen_vcpu_info.
3562306a36Sopenharmony_ci * The pointer is used in xen_evtchn_do_upcall to acknowledge pending events.
3662306a36Sopenharmony_ci * Make sure that xen_vcpu_info doesn't cross a page boundary by making it
3762306a36Sopenharmony_ci * cache-line aligned (the struct is guaranteed to have a size of 64 bytes,
3862306a36Sopenharmony_ci * which matches the cache line size of 64-bit x86 processors).
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ciDEFINE_PER_CPU(struct vcpu_info *, xen_vcpu);
4162306a36Sopenharmony_ciDEFINE_PER_CPU_ALIGNED(struct vcpu_info, xen_vcpu_info);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Linux <-> Xen vCPU id mapping */
4462306a36Sopenharmony_ciDEFINE_PER_CPU(uint32_t, xen_vcpu_id);
4562306a36Sopenharmony_ciEXPORT_PER_CPU_SYMBOL(xen_vcpu_id);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciunsigned long *machine_to_phys_mapping = (void *)MACH2PHYS_VIRT_START;
4862306a36Sopenharmony_ciEXPORT_SYMBOL(machine_to_phys_mapping);
4962306a36Sopenharmony_ciunsigned long  machine_to_phys_nr;
5062306a36Sopenharmony_ciEXPORT_SYMBOL(machine_to_phys_nr);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct start_info *xen_start_info;
5362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_start_info);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct shared_info xen_dummy_shared_info;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci__read_mostly bool xen_have_vector_callback = true;
5862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_have_vector_callback);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * NB: These need to live in .data or alike because they're used by
6262306a36Sopenharmony_ci * xen_prepare_pvh() which runs before clearing the bss.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cienum xen_domain_type __ro_after_init xen_domain_type = XEN_NATIVE;
6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_domain_type);
6662306a36Sopenharmony_ciuint32_t __ro_after_init xen_start_flags;
6762306a36Sopenharmony_ciEXPORT_SYMBOL(xen_start_flags);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Point at some empty memory to start with. We map the real shared_info
7162306a36Sopenharmony_ci * page as soon as fixmap is up and running.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistruct shared_info *HYPERVISOR_shared_info = &xen_dummy_shared_info;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int xen_cpu_up_online(unsigned int cpu)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	xen_init_lock_cpu(cpu);
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciint xen_cpuhp_setup(int (*cpu_up_prepare_cb)(unsigned int),
8262306a36Sopenharmony_ci		    int (*cpu_dead_cb)(unsigned int))
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	int rc;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	rc = cpuhp_setup_state_nocalls(CPUHP_XEN_PREPARE,
8762306a36Sopenharmony_ci				       "x86/xen/guest:prepare",
8862306a36Sopenharmony_ci				       cpu_up_prepare_cb, cpu_dead_cb);
8962306a36Sopenharmony_ci	if (rc >= 0) {
9062306a36Sopenharmony_ci		rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
9162306a36Sopenharmony_ci					       "x86/xen/guest:online",
9262306a36Sopenharmony_ci					       xen_cpu_up_online, NULL);
9362306a36Sopenharmony_ci		if (rc < 0)
9462306a36Sopenharmony_ci			cpuhp_remove_state_nocalls(CPUHP_XEN_PREPARE);
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return rc >= 0 ? 0 : rc;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void xen_vcpu_setup_restore(int cpu)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	/* Any per_cpu(xen_vcpu) is stale, so reset it */
10362306a36Sopenharmony_ci	xen_vcpu_info_reset(cpu);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/*
10662306a36Sopenharmony_ci	 * For PVH and PVHVM, setup online VCPUs only. The rest will
10762306a36Sopenharmony_ci	 * be handled by hotplug.
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci	if (xen_pv_domain() ||
11062306a36Sopenharmony_ci	    (xen_hvm_domain() && cpu_online(cpu)))
11162306a36Sopenharmony_ci		xen_vcpu_setup(cpu);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/*
11562306a36Sopenharmony_ci * On restore, set the vcpu placement up again.
11662306a36Sopenharmony_ci * If it fails, then we're in a bad state, since
11762306a36Sopenharmony_ci * we can't back out from using it...
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_civoid xen_vcpu_restore(void)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int cpu;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
12462306a36Sopenharmony_ci		bool other_cpu = (cpu != smp_processor_id());
12562306a36Sopenharmony_ci		bool is_up;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		if (xen_vcpu_nr(cpu) == XEN_VCPU_ID_INVALID)
12862306a36Sopenharmony_ci			continue;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		/* Only Xen 4.5 and higher support this. */
13162306a36Sopenharmony_ci		is_up = HYPERVISOR_vcpu_op(VCPUOP_is_up,
13262306a36Sopenharmony_ci					   xen_vcpu_nr(cpu), NULL) > 0;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		if (other_cpu && is_up &&
13562306a36Sopenharmony_ci		    HYPERVISOR_vcpu_op(VCPUOP_down, xen_vcpu_nr(cpu), NULL))
13662306a36Sopenharmony_ci			BUG();
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		if (xen_pv_domain() || xen_feature(XENFEAT_hvm_safe_pvclock))
13962306a36Sopenharmony_ci			xen_setup_runstate_info(cpu);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		xen_vcpu_setup_restore(cpu);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (other_cpu && is_up &&
14462306a36Sopenharmony_ci		    HYPERVISOR_vcpu_op(VCPUOP_up, xen_vcpu_nr(cpu), NULL))
14562306a36Sopenharmony_ci			BUG();
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid xen_vcpu_info_reset(int cpu)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	if (xen_vcpu_nr(cpu) < MAX_VIRT_CPUS) {
15262306a36Sopenharmony_ci		per_cpu(xen_vcpu, cpu) =
15362306a36Sopenharmony_ci			&HYPERVISOR_shared_info->vcpu_info[xen_vcpu_nr(cpu)];
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		/* Set to NULL so that if somebody accesses it we get an OOPS */
15662306a36Sopenharmony_ci		per_cpu(xen_vcpu, cpu) = NULL;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_civoid xen_vcpu_setup(int cpu)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct vcpu_register_vcpu_info info;
16362306a36Sopenharmony_ci	int err;
16462306a36Sopenharmony_ci	struct vcpu_info *vcpup;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(*vcpup) > SMP_CACHE_BYTES);
16762306a36Sopenharmony_ci	BUG_ON(HYPERVISOR_shared_info == &xen_dummy_shared_info);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/*
17062306a36Sopenharmony_ci	 * This path is called on PVHVM at bootup (xen_hvm_smp_prepare_boot_cpu)
17162306a36Sopenharmony_ci	 * and at restore (xen_vcpu_restore). Also called for hotplugged
17262306a36Sopenharmony_ci	 * VCPUs (cpu_init -> xen_hvm_cpu_prepare_hvm).
17362306a36Sopenharmony_ci	 * However, the hypercall can only be done once (see below) so if a VCPU
17462306a36Sopenharmony_ci	 * is offlined and comes back online then let's not redo the hypercall.
17562306a36Sopenharmony_ci	 *
17662306a36Sopenharmony_ci	 * For PV it is called during restore (xen_vcpu_restore) and bootup
17762306a36Sopenharmony_ci	 * (xen_setup_vcpu_info_placement). The hotplug mechanism does not
17862306a36Sopenharmony_ci	 * use this function.
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	if (xen_hvm_domain()) {
18162306a36Sopenharmony_ci		if (per_cpu(xen_vcpu, cpu) == &per_cpu(xen_vcpu_info, cpu))
18262306a36Sopenharmony_ci			return;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	vcpup = &per_cpu(xen_vcpu_info, cpu);
18662306a36Sopenharmony_ci	info.mfn = arbitrary_virt_to_mfn(vcpup);
18762306a36Sopenharmony_ci	info.offset = offset_in_page(vcpup);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/*
19062306a36Sopenharmony_ci	 * N.B. This hypercall can _only_ be called once per CPU.
19162306a36Sopenharmony_ci	 * Subsequent calls will error out with -EINVAL. This is due to
19262306a36Sopenharmony_ci	 * the fact that hypervisor has no unregister variant and this
19362306a36Sopenharmony_ci	 * hypercall does not allow to over-write info.mfn and
19462306a36Sopenharmony_ci	 * info.offset.
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, xen_vcpu_nr(cpu),
19762306a36Sopenharmony_ci				 &info);
19862306a36Sopenharmony_ci	if (err)
19962306a36Sopenharmony_ci		panic("register_vcpu_info failed: cpu=%d err=%d\n", cpu, err);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	per_cpu(xen_vcpu, cpu) = vcpup;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_civoid __init xen_banner(void)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	unsigned version = HYPERVISOR_xen_version(XENVER_version, NULL);
20762306a36Sopenharmony_ci	struct xen_extraversion extra;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	HYPERVISOR_xen_version(XENVER_extraversion, &extra);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	pr_info("Booting kernel on %s\n", pv_info.name);
21262306a36Sopenharmony_ci	pr_info("Xen version: %u.%u%s%s\n",
21362306a36Sopenharmony_ci		version >> 16, version & 0xffff, extra.extraversion,
21462306a36Sopenharmony_ci		xen_feature(XENFEAT_mmu_pt_update_preserve_ad)
21562306a36Sopenharmony_ci		? " (preserve-AD)" : "");
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* Check if running on Xen version (major, minor) or later */
21962306a36Sopenharmony_cibool xen_running_on_version_or_later(unsigned int major, unsigned int minor)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	unsigned int version;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!xen_domain())
22462306a36Sopenharmony_ci		return false;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	version = HYPERVISOR_xen_version(XENVER_version, NULL);
22762306a36Sopenharmony_ci	if ((((version >> 16) == major) && ((version & 0xffff) >= minor)) ||
22862306a36Sopenharmony_ci		((version >> 16) > major))
22962306a36Sopenharmony_ci		return true;
23062306a36Sopenharmony_ci	return false;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_civoid __init xen_add_preferred_consoles(void)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	add_preferred_console("xenboot", 0, NULL);
23662306a36Sopenharmony_ci	if (!boot_params.screen_info.orig_video_isVGA)
23762306a36Sopenharmony_ci		add_preferred_console("tty", 0, NULL);
23862306a36Sopenharmony_ci	add_preferred_console("hvc", 0, NULL);
23962306a36Sopenharmony_ci	if (boot_params.screen_info.orig_video_isVGA)
24062306a36Sopenharmony_ci		add_preferred_console("tty", 0, NULL);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_civoid xen_reboot(int reason)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct sched_shutdown r = { .reason = reason };
24662306a36Sopenharmony_ci	int cpu;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	for_each_online_cpu(cpu)
24962306a36Sopenharmony_ci		xen_pmu_finish(cpu);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (HYPERVISOR_sched_op(SCHEDOP_shutdown, &r))
25262306a36Sopenharmony_ci		BUG();
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int reboot_reason = SHUTDOWN_reboot;
25662306a36Sopenharmony_cistatic bool xen_legacy_crash;
25762306a36Sopenharmony_civoid xen_emergency_restart(void)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	xen_reboot(reboot_reason);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int
26362306a36Sopenharmony_cixen_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	if (!kexec_crash_loaded()) {
26662306a36Sopenharmony_ci		if (xen_legacy_crash)
26762306a36Sopenharmony_ci			xen_reboot(SHUTDOWN_crash);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		reboot_reason = SHUTDOWN_crash;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		/*
27262306a36Sopenharmony_ci		 * If panic_timeout==0 then we are supposed to wait forever.
27362306a36Sopenharmony_ci		 * However, to preserve original dom0 behavior we have to drop
27462306a36Sopenharmony_ci		 * into hypervisor. (domU behavior is controlled by its
27562306a36Sopenharmony_ci		 * config file)
27662306a36Sopenharmony_ci		 */
27762306a36Sopenharmony_ci		if (panic_timeout == 0)
27862306a36Sopenharmony_ci			panic_timeout = -1;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	return NOTIFY_DONE;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int __init parse_xen_legacy_crash(char *arg)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	xen_legacy_crash = true;
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ciearly_param("xen_legacy_crash", parse_xen_legacy_crash);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic struct notifier_block xen_panic_block = {
29162306a36Sopenharmony_ci	.notifier_call = xen_panic_event,
29262306a36Sopenharmony_ci	.priority = INT_MIN
29362306a36Sopenharmony_ci};
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciint xen_panic_handler_init(void)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list, &xen_panic_block);
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_civoid xen_pin_vcpu(int cpu)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	static bool disable_pinning;
30462306a36Sopenharmony_ci	struct sched_pin_override pin_override;
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (disable_pinning)
30862306a36Sopenharmony_ci		return;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	pin_override.pcpu = cpu;
31162306a36Sopenharmony_ci	ret = HYPERVISOR_sched_op(SCHEDOP_pin_override, &pin_override);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* Ignore errors when removing override. */
31462306a36Sopenharmony_ci	if (cpu < 0)
31562306a36Sopenharmony_ci		return;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	switch (ret) {
31862306a36Sopenharmony_ci	case -ENOSYS:
31962306a36Sopenharmony_ci		pr_warn("Unable to pin on physical cpu %d. In case of problems consider vcpu pinning.\n",
32062306a36Sopenharmony_ci			cpu);
32162306a36Sopenharmony_ci		disable_pinning = true;
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	case -EPERM:
32462306a36Sopenharmony_ci		WARN(1, "Trying to pin vcpu without having privilege to do so\n");
32562306a36Sopenharmony_ci		disable_pinning = true;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case -EINVAL:
32862306a36Sopenharmony_ci	case -EBUSY:
32962306a36Sopenharmony_ci		pr_warn("Physical cpu %d not available for pinning. Check Xen cpu configuration.\n",
33062306a36Sopenharmony_ci			cpu);
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case 0:
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	default:
33562306a36Sopenharmony_ci		WARN(1, "rc %d while trying to pin vcpu\n", ret);
33662306a36Sopenharmony_ci		disable_pinning = true;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
34162306a36Sopenharmony_civoid xen_arch_register_cpu(int num)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	arch_register_cpu(num);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ciEXPORT_SYMBOL(xen_arch_register_cpu);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_civoid xen_arch_unregister_cpu(int num)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	arch_unregister_cpu(num);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ciEXPORT_SYMBOL(xen_arch_unregister_cpu);
35262306a36Sopenharmony_ci#endif
353