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