162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/acpi.h> 462306a36Sopenharmony_ci#include <linux/cpu.h> 562306a36Sopenharmony_ci#include <linux/kexec.h> 662306a36Sopenharmony_ci#include <linux/memblock.h> 762306a36Sopenharmony_ci#include <linux/virtio_anchor.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <xen/features.h> 1062306a36Sopenharmony_ci#include <xen/events.h> 1162306a36Sopenharmony_ci#include <xen/hvm.h> 1262306a36Sopenharmony_ci#include <xen/interface/hvm/hvm_op.h> 1362306a36Sopenharmony_ci#include <xen/interface/memory.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/apic.h> 1662306a36Sopenharmony_ci#include <asm/cpu.h> 1762306a36Sopenharmony_ci#include <asm/smp.h> 1862306a36Sopenharmony_ci#include <asm/io_apic.h> 1962306a36Sopenharmony_ci#include <asm/reboot.h> 2062306a36Sopenharmony_ci#include <asm/setup.h> 2162306a36Sopenharmony_ci#include <asm/idtentry.h> 2262306a36Sopenharmony_ci#include <asm/hypervisor.h> 2362306a36Sopenharmony_ci#include <asm/e820/api.h> 2462306a36Sopenharmony_ci#include <asm/early_ioremap.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/xen/cpuid.h> 2762306a36Sopenharmony_ci#include <asm/xen/hypervisor.h> 2862306a36Sopenharmony_ci#include <asm/xen/page.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "xen-ops.h" 3162306a36Sopenharmony_ci#include "mmu.h" 3262306a36Sopenharmony_ci#include "smp.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned long shared_info_pfn; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci__ro_after_init bool xen_percpu_upcall; 3762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_percpu_upcall); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid xen_hvm_init_shared_info(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct xen_add_to_physmap xatp; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci xatp.domid = DOMID_SELF; 4462306a36Sopenharmony_ci xatp.idx = 0; 4562306a36Sopenharmony_ci xatp.space = XENMAPSPACE_shared_info; 4662306a36Sopenharmony_ci xatp.gpfn = shared_info_pfn; 4762306a36Sopenharmony_ci if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 4862306a36Sopenharmony_ci BUG(); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void __init reserve_shared_info(void) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci u64 pa; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* 5662306a36Sopenharmony_ci * Search for a free page starting at 4kB physical address. 5762306a36Sopenharmony_ci * Low memory is preferred to avoid an EPT large page split up 5862306a36Sopenharmony_ci * by the mapping. 5962306a36Sopenharmony_ci * Starting below X86_RESERVE_LOW (usually 64kB) is fine as 6062306a36Sopenharmony_ci * the BIOS used for HVM guests is well behaved and won't 6162306a36Sopenharmony_ci * clobber memory other than the first 4kB. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci for (pa = PAGE_SIZE; 6462306a36Sopenharmony_ci !e820__mapped_all(pa, pa + PAGE_SIZE, E820_TYPE_RAM) || 6562306a36Sopenharmony_ci memblock_is_reserved(pa); 6662306a36Sopenharmony_ci pa += PAGE_SIZE) 6762306a36Sopenharmony_ci ; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci shared_info_pfn = PHYS_PFN(pa); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci memblock_reserve(pa, PAGE_SIZE); 7262306a36Sopenharmony_ci HYPERVISOR_shared_info = early_memremap(pa, PAGE_SIZE); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void __init xen_hvm_init_mem_mapping(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci early_memunmap(HYPERVISOR_shared_info, PAGE_SIZE); 7862306a36Sopenharmony_ci HYPERVISOR_shared_info = __va(PFN_PHYS(shared_info_pfn)); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * The virtual address of the shared_info page has changed, so 8262306a36Sopenharmony_ci * the vcpu_info pointer for VCPU 0 is now stale. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * The prepare_boot_cpu callback will re-initialize it via 8562306a36Sopenharmony_ci * xen_vcpu_setup, but we can't rely on that to be called for 8662306a36Sopenharmony_ci * old Xen versions (xen_have_vector_callback == 0). 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * It is, in any case, bad to have a stale vcpu_info pointer 8962306a36Sopenharmony_ci * so reset it now. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci xen_vcpu_info_reset(0); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void __init init_hvm_pv_info(void) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int major, minor; 9762306a36Sopenharmony_ci uint32_t eax, ebx, ecx, edx, base; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci base = xen_cpuid_base(); 10062306a36Sopenharmony_ci eax = cpuid_eax(base + 1); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci major = eax >> 16; 10362306a36Sopenharmony_ci minor = eax & 0xffff; 10462306a36Sopenharmony_ci printk(KERN_INFO "Xen version %d.%d.\n", major, minor); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci xen_domain_type = XEN_HVM_DOMAIN; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* PVH set up hypercall page in xen_prepare_pvh(). */ 10962306a36Sopenharmony_ci if (xen_pvh_domain()) 11062306a36Sopenharmony_ci pv_info.name = "Xen PVH"; 11162306a36Sopenharmony_ci else { 11262306a36Sopenharmony_ci u64 pfn; 11362306a36Sopenharmony_ci uint32_t msr; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci pv_info.name = "Xen HVM"; 11662306a36Sopenharmony_ci msr = cpuid_ebx(base + 2); 11762306a36Sopenharmony_ci pfn = __pa(hypercall_page); 11862306a36Sopenharmony_ci wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32)); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci xen_setup_features(); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci cpuid(base + 4, &eax, &ebx, &ecx, &edx); 12462306a36Sopenharmony_ci if (eax & XEN_HVM_CPUID_VCPU_ID_PRESENT) 12562306a36Sopenharmony_ci this_cpu_write(xen_vcpu_id, ebx); 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci this_cpu_write(xen_vcpu_id, smp_processor_id()); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciDEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_callback) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct pt_regs *old_regs = set_irq_regs(regs); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (xen_percpu_upcall) 13562306a36Sopenharmony_ci apic_eoi(); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci inc_irq_stat(irq_hv_callback_count); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci xen_evtchn_do_upcall(); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci set_irq_regs(old_regs); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 14562306a36Sopenharmony_cistatic void xen_hvm_shutdown(void) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci native_machine_shutdown(); 14862306a36Sopenharmony_ci if (kexec_in_progress) 14962306a36Sopenharmony_ci xen_reboot(SHUTDOWN_soft_reset); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void xen_hvm_crash_shutdown(struct pt_regs *regs) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci native_machine_crash_shutdown(regs); 15562306a36Sopenharmony_ci xen_reboot(SHUTDOWN_soft_reset); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci#endif 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int xen_cpu_up_prepare_hvm(unsigned int cpu) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int rc = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * If a CPU was offlined earlier and offlining timed out then the 16562306a36Sopenharmony_ci * lock mechanism is still initialized. Uninit it unconditionally 16662306a36Sopenharmony_ci * as it's safe to call even if already uninited. Interrupts and 16762306a36Sopenharmony_ci * timer have already been handled in xen_cpu_dead_hvm(). 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci xen_uninit_lock_cpu(cpu); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (cpu_acpi_id(cpu) != U32_MAX) 17262306a36Sopenharmony_ci per_cpu(xen_vcpu_id, cpu) = cpu_acpi_id(cpu); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci per_cpu(xen_vcpu_id, cpu) = cpu; 17562306a36Sopenharmony_ci xen_vcpu_setup(cpu); 17662306a36Sopenharmony_ci if (!xen_have_vector_callback) 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (xen_percpu_upcall) { 18062306a36Sopenharmony_ci rc = xen_set_upcall_vector(cpu); 18162306a36Sopenharmony_ci if (rc) { 18262306a36Sopenharmony_ci WARN(1, "HVMOP_set_evtchn_upcall_vector" 18362306a36Sopenharmony_ci " for CPU %d failed: %d\n", cpu, rc); 18462306a36Sopenharmony_ci return rc; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (xen_feature(XENFEAT_hvm_safe_pvclock)) 18962306a36Sopenharmony_ci xen_setup_timer(cpu); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci rc = xen_smp_intr_init(cpu); 19262306a36Sopenharmony_ci if (rc) { 19362306a36Sopenharmony_ci WARN(1, "xen_smp_intr_init() for CPU %d failed: %d\n", 19462306a36Sopenharmony_ci cpu, rc); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci return rc; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int xen_cpu_dead_hvm(unsigned int cpu) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci xen_smp_intr_free(cpu); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (xen_have_vector_callback && xen_feature(XENFEAT_hvm_safe_pvclock)) 20462306a36Sopenharmony_ci xen_teardown_timer(cpu); 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void __init xen_hvm_guest_init(void) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (xen_pv_domain()) 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_XEN_VIRTIO_FORCE_GRANT)) 21462306a36Sopenharmony_ci virtio_set_mem_acc_cb(xen_virtio_restricted_mem_acc); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci init_hvm_pv_info(); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci reserve_shared_info(); 21962306a36Sopenharmony_ci xen_hvm_init_shared_info(); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * xen_vcpu is a pointer to the vcpu_info struct in the shared_info 22362306a36Sopenharmony_ci * page, we use it in the event channel upcall and in some pvclock 22462306a36Sopenharmony_ci * related functions. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci xen_vcpu_info_reset(0); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci xen_panic_handler_init(); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci xen_hvm_smp_init(); 23162306a36Sopenharmony_ci WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_hvm, xen_cpu_dead_hvm)); 23262306a36Sopenharmony_ci xen_unplug_emulated_devices(); 23362306a36Sopenharmony_ci x86_init.irqs.intr_init = xen_init_IRQ; 23462306a36Sopenharmony_ci xen_hvm_init_time_ops(); 23562306a36Sopenharmony_ci xen_hvm_init_mmu_ops(); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE 23862306a36Sopenharmony_ci machine_ops.shutdown = xen_hvm_shutdown; 23962306a36Sopenharmony_ci machine_ops.crash_shutdown = xen_hvm_crash_shutdown; 24062306a36Sopenharmony_ci#endif 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic __init int xen_parse_nopv(char *arg) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci pr_notice("\"xen_nopv\" is deprecated, please use \"nopv\" instead\n"); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (xen_cpuid_base()) 24862306a36Sopenharmony_ci nopv = true; 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ciearly_param("xen_nopv", xen_parse_nopv); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic __init int xen_parse_no_vector_callback(char *arg) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci xen_have_vector_callback = false; 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ciearly_param("xen_no_vector_callback", xen_parse_no_vector_callback); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic __init bool xen_x2apic_available(void) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci return x2apic_supported(); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic bool __init msi_ext_dest_id(void) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci return cpuid_eax(xen_cpuid_base() + 4) & XEN_HVM_CPUID_EXT_DEST_ID; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic __init void xen_hvm_guest_late_init(void) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci#ifdef CONFIG_XEN_PVH 27362306a36Sopenharmony_ci /* Test for PVH domain (PVH boot path taken overrides ACPI flags). */ 27462306a36Sopenharmony_ci if (!xen_pvh && 27562306a36Sopenharmony_ci (x86_platform.legacy.rtc || !x86_platform.legacy.no_vga)) 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* PVH detected. */ 27962306a36Sopenharmony_ci xen_pvh = true; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (nopv) 28262306a36Sopenharmony_ci panic("\"nopv\" and \"xen_nopv\" parameters are unsupported in PVH guest."); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Make sure we don't fall back to (default) ACPI_IRQ_MODEL_PIC. */ 28562306a36Sopenharmony_ci if (!nr_ioapics && acpi_irq_model == ACPI_IRQ_MODEL_PIC) 28662306a36Sopenharmony_ci acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci machine_ops.emergency_restart = xen_emergency_restart; 28962306a36Sopenharmony_ci pv_info.name = "Xen PVH"; 29062306a36Sopenharmony_ci#endif 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic uint32_t __init xen_platform_hvm(void) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci uint32_t xen_domain = xen_cpuid_base(); 29662306a36Sopenharmony_ci struct x86_hyper_init *h = &x86_hyper_xen_hvm.init; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (xen_pv_domain()) 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (xen_pvh_domain() && nopv) { 30262306a36Sopenharmony_ci /* Guest booting via the Xen-PVH boot entry goes here */ 30362306a36Sopenharmony_ci pr_info("\"nopv\" parameter is ignored in PVH guest\n"); 30462306a36Sopenharmony_ci nopv = false; 30562306a36Sopenharmony_ci } else if (nopv && xen_domain) { 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * Guest booting via normal boot entry (like via grub2) goes 30862306a36Sopenharmony_ci * here. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * Use interface functions for bare hardware if nopv, 31162306a36Sopenharmony_ci * xen_hvm_guest_late_init is an exception as we need to 31262306a36Sopenharmony_ci * detect PVH and panic there. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci h->init_platform = x86_init_noop; 31562306a36Sopenharmony_ci h->x2apic_available = bool_x86_init_noop; 31662306a36Sopenharmony_ci h->init_mem_mapping = x86_init_noop; 31762306a36Sopenharmony_ci h->init_after_bootmem = x86_init_noop; 31862306a36Sopenharmony_ci h->guest_late_init = xen_hvm_guest_late_init; 31962306a36Sopenharmony_ci x86_hyper_xen_hvm.runtime.pin_vcpu = x86_op_int_noop; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci return xen_domain; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistruct hypervisor_x86 x86_hyper_xen_hvm __initdata = { 32562306a36Sopenharmony_ci .name = "Xen HVM", 32662306a36Sopenharmony_ci .detect = xen_platform_hvm, 32762306a36Sopenharmony_ci .type = X86_HYPER_XEN_HVM, 32862306a36Sopenharmony_ci .init.init_platform = xen_hvm_guest_init, 32962306a36Sopenharmony_ci .init.x2apic_available = xen_x2apic_available, 33062306a36Sopenharmony_ci .init.init_mem_mapping = xen_hvm_init_mem_mapping, 33162306a36Sopenharmony_ci .init.guest_late_init = xen_hvm_guest_late_init, 33262306a36Sopenharmony_ci .init.msi_ext_dest_id = msi_ext_dest_id, 33362306a36Sopenharmony_ci .runtime.pin_vcpu = xen_pin_vcpu, 33462306a36Sopenharmony_ci .ignore_nopv = true, 33562306a36Sopenharmony_ci}; 336