linux/arch/x86/xen/enlighten_hvm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <linux/acpi.h>
   4#include <linux/cpu.h>
   5#include <linux/kexec.h>
   6#include <linux/memblock.h>
   7
   8#include <xen/features.h>
   9#include <xen/events.h>
  10#include <xen/interface/memory.h>
  11
  12#include <asm/cpu.h>
  13#include <asm/smp.h>
  14#include <asm/io_apic.h>
  15#include <asm/reboot.h>
  16#include <asm/setup.h>
  17#include <asm/idtentry.h>
  18#include <asm/hypervisor.h>
  19#include <asm/e820/api.h>
  20#include <asm/early_ioremap.h>
  21
  22#include <asm/xen/cpuid.h>
  23#include <asm/xen/hypervisor.h>
  24#include <asm/xen/page.h>
  25
  26#include "xen-ops.h"
  27#include "mmu.h"
  28#include "smp.h"
  29
  30static unsigned long shared_info_pfn;
  31
  32void xen_hvm_init_shared_info(void)
  33{
  34        struct xen_add_to_physmap xatp;
  35
  36        xatp.domid = DOMID_SELF;
  37        xatp.idx = 0;
  38        xatp.space = XENMAPSPACE_shared_info;
  39        xatp.gpfn = shared_info_pfn;
  40        if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
  41                BUG();
  42}
  43
  44static void __init reserve_shared_info(void)
  45{
  46        u64 pa;
  47
  48        /*
  49         * Search for a free page starting at 4kB physical address.
  50         * Low memory is preferred to avoid an EPT large page split up
  51         * by the mapping.
  52         * Starting below X86_RESERVE_LOW (usually 64kB) is fine as
  53         * the BIOS used for HVM guests is well behaved and won't
  54         * clobber memory other than the first 4kB.
  55         */
  56        for (pa = PAGE_SIZE;
  57             !e820__mapped_all(pa, pa + PAGE_SIZE, E820_TYPE_RAM) ||
  58             memblock_is_reserved(pa);
  59             pa += PAGE_SIZE)
  60                ;
  61
  62        shared_info_pfn = PHYS_PFN(pa);
  63
  64        memblock_reserve(pa, PAGE_SIZE);
  65        HYPERVISOR_shared_info = early_memremap(pa, PAGE_SIZE);
  66}
  67
  68static void __init xen_hvm_init_mem_mapping(void)
  69{
  70        early_memunmap(HYPERVISOR_shared_info, PAGE_SIZE);
  71        HYPERVISOR_shared_info = __va(PFN_PHYS(shared_info_pfn));
  72
  73        /*
  74         * The virtual address of the shared_info page has changed, so
  75         * the vcpu_info pointer for VCPU 0 is now stale.
  76         *
  77         * The prepare_boot_cpu callback will re-initialize it via
  78         * xen_vcpu_setup, but we can't rely on that to be called for
  79         * old Xen versions (xen_have_vector_callback == 0).
  80         *
  81         * It is, in any case, bad to have a stale vcpu_info pointer
  82         * so reset it now.
  83         */
  84        xen_vcpu_info_reset(0);
  85}
  86
  87static void __init init_hvm_pv_info(void)
  88{
  89        int major, minor;
  90        uint32_t eax, ebx, ecx, edx, base;
  91
  92        base = xen_cpuid_base();
  93        eax = cpuid_eax(base + 1);
  94
  95        major = eax >> 16;
  96        minor = eax & 0xffff;
  97        printk(KERN_INFO "Xen version %d.%d.\n", major, minor);
  98
  99        xen_domain_type = XEN_HVM_DOMAIN;
 100
 101        /* PVH set up hypercall page in xen_prepare_pvh(). */
 102        if (xen_pvh_domain())
 103                pv_info.name = "Xen PVH";
 104        else {
 105                u64 pfn;
 106                uint32_t msr;
 107
 108                pv_info.name = "Xen HVM";
 109                msr = cpuid_ebx(base + 2);
 110                pfn = __pa(hypercall_page);
 111                wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32));
 112        }
 113
 114        xen_setup_features();
 115
 116        cpuid(base + 4, &eax, &ebx, &ecx, &edx);
 117        if (eax & XEN_HVM_CPUID_VCPU_ID_PRESENT)
 118                this_cpu_write(xen_vcpu_id, ebx);
 119        else
 120                this_cpu_write(xen_vcpu_id, smp_processor_id());
 121}
 122
 123DEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_callback)
 124{
 125        struct pt_regs *old_regs = set_irq_regs(regs);
 126
 127        inc_irq_stat(irq_hv_callback_count);
 128
 129        xen_hvm_evtchn_do_upcall();
 130
 131        set_irq_regs(old_regs);
 132}
 133
 134#ifdef CONFIG_KEXEC_CORE
 135static void xen_hvm_shutdown(void)
 136{
 137        native_machine_shutdown();
 138        if (kexec_in_progress)
 139                xen_reboot(SHUTDOWN_soft_reset);
 140}
 141
 142static void xen_hvm_crash_shutdown(struct pt_regs *regs)
 143{
 144        native_machine_crash_shutdown(regs);
 145        xen_reboot(SHUTDOWN_soft_reset);
 146}
 147#endif
 148
 149static int xen_cpu_up_prepare_hvm(unsigned int cpu)
 150{
 151        int rc = 0;
 152
 153        /*
 154         * This can happen if CPU was offlined earlier and
 155         * offlining timed out in common_cpu_die().
 156         */
 157        if (cpu_report_state(cpu) == CPU_DEAD_FROZEN) {
 158                xen_smp_intr_free(cpu);
 159                xen_uninit_lock_cpu(cpu);
 160        }
 161
 162        if (cpu_acpi_id(cpu) != U32_MAX)
 163                per_cpu(xen_vcpu_id, cpu) = cpu_acpi_id(cpu);
 164        else
 165                per_cpu(xen_vcpu_id, cpu) = cpu;
 166        rc = xen_vcpu_setup(cpu);
 167        if (rc || !xen_have_vector_callback)
 168                return rc;
 169
 170        if (xen_feature(XENFEAT_hvm_safe_pvclock))
 171                xen_setup_timer(cpu);
 172
 173        rc = xen_smp_intr_init(cpu);
 174        if (rc) {
 175                WARN(1, "xen_smp_intr_init() for CPU %d failed: %d\n",
 176                     cpu, rc);
 177        }
 178        return rc;
 179}
 180
 181static int xen_cpu_dead_hvm(unsigned int cpu)
 182{
 183        xen_smp_intr_free(cpu);
 184
 185        if (xen_have_vector_callback && xen_feature(XENFEAT_hvm_safe_pvclock))
 186                xen_teardown_timer(cpu);
 187
 188       return 0;
 189}
 190
 191static bool no_vector_callback __initdata;
 192
 193static void __init xen_hvm_guest_init(void)
 194{
 195        if (xen_pv_domain())
 196                return;
 197
 198        init_hvm_pv_info();
 199
 200        reserve_shared_info();
 201        xen_hvm_init_shared_info();
 202
 203        /*
 204         * xen_vcpu is a pointer to the vcpu_info struct in the shared_info
 205         * page, we use it in the event channel upcall and in some pvclock
 206         * related functions.
 207         */
 208        xen_vcpu_info_reset(0);
 209
 210        xen_panic_handler_init();
 211
 212        if (!no_vector_callback && xen_feature(XENFEAT_hvm_callback_vector))
 213                xen_have_vector_callback = 1;
 214
 215        xen_hvm_smp_init();
 216        WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_hvm, xen_cpu_dead_hvm));
 217        xen_unplug_emulated_devices();
 218        x86_init.irqs.intr_init = xen_init_IRQ;
 219        xen_hvm_init_time_ops();
 220        xen_hvm_init_mmu_ops();
 221
 222#ifdef CONFIG_KEXEC_CORE
 223        machine_ops.shutdown = xen_hvm_shutdown;
 224        machine_ops.crash_shutdown = xen_hvm_crash_shutdown;
 225#endif
 226}
 227
 228static __init int xen_parse_nopv(char *arg)
 229{
 230        pr_notice("\"xen_nopv\" is deprecated, please use \"nopv\" instead\n");
 231
 232        if (xen_cpuid_base())
 233                nopv = true;
 234        return 0;
 235}
 236early_param("xen_nopv", xen_parse_nopv);
 237
 238static __init int xen_parse_no_vector_callback(char *arg)
 239{
 240        no_vector_callback = true;
 241        return 0;
 242}
 243early_param("xen_no_vector_callback", xen_parse_no_vector_callback);
 244
 245bool __init xen_hvm_need_lapic(void)
 246{
 247        if (xen_pv_domain())
 248                return false;
 249        if (!xen_hvm_domain())
 250                return false;
 251        if (xen_feature(XENFEAT_hvm_pirqs) && xen_have_vector_callback)
 252                return false;
 253        return true;
 254}
 255
 256static __init void xen_hvm_guest_late_init(void)
 257{
 258#ifdef CONFIG_XEN_PVH
 259        /* Test for PVH domain (PVH boot path taken overrides ACPI flags). */
 260        if (!xen_pvh &&
 261            (x86_platform.legacy.rtc || !x86_platform.legacy.no_vga))
 262                return;
 263
 264        /* PVH detected. */
 265        xen_pvh = true;
 266
 267        if (nopv)
 268                panic("\"nopv\" and \"xen_nopv\" parameters are unsupported in PVH guest.");
 269
 270        /* Make sure we don't fall back to (default) ACPI_IRQ_MODEL_PIC. */
 271        if (!nr_ioapics && acpi_irq_model == ACPI_IRQ_MODEL_PIC)
 272                acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
 273
 274        machine_ops.emergency_restart = xen_emergency_restart;
 275        pv_info.name = "Xen PVH";
 276#endif
 277}
 278
 279static uint32_t __init xen_platform_hvm(void)
 280{
 281        uint32_t xen_domain = xen_cpuid_base();
 282        struct x86_hyper_init *h = &x86_hyper_xen_hvm.init;
 283
 284        if (xen_pv_domain())
 285                return 0;
 286
 287        if (xen_pvh_domain() && nopv) {
 288                /* Guest booting via the Xen-PVH boot entry goes here */
 289                pr_info("\"nopv\" parameter is ignored in PVH guest\n");
 290                nopv = false;
 291        } else if (nopv && xen_domain) {
 292                /*
 293                 * Guest booting via normal boot entry (like via grub2) goes
 294                 * here.
 295                 *
 296                 * Use interface functions for bare hardware if nopv,
 297                 * xen_hvm_guest_late_init is an exception as we need to
 298                 * detect PVH and panic there.
 299                 */
 300                h->init_platform = x86_init_noop;
 301                h->x2apic_available = bool_x86_init_noop;
 302                h->init_mem_mapping = x86_init_noop;
 303                h->init_after_bootmem = x86_init_noop;
 304                h->guest_late_init = xen_hvm_guest_late_init;
 305                x86_hyper_xen_hvm.runtime.pin_vcpu = x86_op_int_noop;
 306        }
 307        return xen_domain;
 308}
 309
 310struct hypervisor_x86 x86_hyper_xen_hvm __initdata = {
 311        .name                   = "Xen HVM",
 312        .detect                 = xen_platform_hvm,
 313        .type                   = X86_HYPER_XEN_HVM,
 314        .init.init_platform     = xen_hvm_guest_init,
 315        .init.x2apic_available  = xen_x2apic_para_available,
 316        .init.init_mem_mapping  = xen_hvm_init_mem_mapping,
 317        .init.guest_late_init   = xen_hvm_guest_late_init,
 318        .runtime.pin_vcpu       = xen_pin_vcpu,
 319        .ignore_nopv            = true,
 320};
 321