The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/x86/xen/hvm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 2008, 2013 Citrix Systems, Inc.
    3  * Copyright (c) 2012 Spectra Logic Corporation
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD: releng/11.2/sys/x86/xen/hvm.c 305672 2016-09-09 19:57:32Z jhb $");
   30 
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/kernel.h>
   34 #include <sys/malloc.h>
   35 #include <sys/proc.h>
   36 #include <sys/smp.h>
   37 #include <sys/systm.h>
   38 
   39 #include <vm/vm.h>
   40 #include <vm/pmap.h>
   41 
   42 #include <dev/pci/pcivar.h>
   43 
   44 #include <machine/cpufunc.h>
   45 #include <machine/cpu.h>
   46 #include <machine/smp.h>
   47 
   48 #include <x86/apicreg.h>
   49 
   50 #include <xen/xen-os.h>
   51 #include <xen/features.h>
   52 #include <xen/gnttab.h>
   53 #include <xen/hypervisor.h>
   54 #include <xen/hvm.h>
   55 #include <xen/xen_intr.h>
   56 
   57 #include <xen/interface/hvm/params.h>
   58 #include <xen/interface/vcpu.h>
   59 
   60 /*--------------------------- Forward Declarations ---------------------------*/
   61 static void xen_hvm_cpu_init(void);
   62 
   63 /*-------------------------------- Local Types -------------------------------*/
   64 enum xen_hvm_init_type {
   65         XEN_HVM_INIT_COLD,
   66         XEN_HVM_INIT_CANCELLED_SUSPEND,
   67         XEN_HVM_INIT_RESUME
   68 };
   69 
   70 /*-------------------------------- Global Data -------------------------------*/
   71 enum xen_domain_type xen_domain_type = XEN_NATIVE;
   72 
   73 #ifdef SMP
   74 struct cpu_ops xen_hvm_cpu_ops = {
   75         .cpu_init       = xen_hvm_cpu_init,
   76         .cpu_resume     = xen_hvm_cpu_init
   77 };
   78 #endif
   79 
   80 static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV Support");
   81 
   82 /**
   83  * If non-zero, the hypervisor has been configured to use a direct
   84  * IDT event callback for interrupt injection.
   85  */
   86 int xen_vector_callback_enabled;
   87 
   88 /*------------------------------- Per-CPU Data -------------------------------*/
   89 DPCPU_DEFINE(struct vcpu_info, vcpu_local_info);
   90 DPCPU_DEFINE(struct vcpu_info *, vcpu_info);
   91 
   92 /*------------------ Hypervisor Access Shared Memory Regions -----------------*/
   93 shared_info_t *HYPERVISOR_shared_info;
   94 start_info_t *HYPERVISOR_start_info;
   95 
   96 
   97 /*------------------------------ Sysctl tunables -----------------------------*/
   98 int xen_disable_pv_disks = 0;
   99 int xen_disable_pv_nics = 0;
  100 TUNABLE_INT("hw.xen.disable_pv_disks", &xen_disable_pv_disks);
  101 TUNABLE_INT("hw.xen.disable_pv_nics", &xen_disable_pv_nics);
  102 
  103 /*---------------------- XEN Hypervisor Probe and Setup ----------------------*/
  104 static uint32_t
  105 xen_hvm_cpuid_base(void)
  106 {
  107         uint32_t base, regs[4];
  108 
  109         for (base = 0x40000000; base < 0x40010000; base += 0x100) {
  110                 do_cpuid(base, regs);
  111                 if (!memcmp("XenVMMXenVMM", &regs[1], 12)
  112                     && (regs[0] - base) >= 2)
  113                         return (base);
  114         }
  115         return (0);
  116 }
  117 
  118 /*
  119  * Allocate and fill in the hypcall page.
  120  */
  121 static int
  122 xen_hvm_init_hypercall_stubs(enum xen_hvm_init_type init_type)
  123 {
  124         uint32_t base, regs[4];
  125         int i;
  126 
  127         if (xen_pv_domain()) {
  128                 /* hypercall page is already set in the PV case */
  129                 return (0);
  130         }
  131 
  132         base = xen_hvm_cpuid_base();
  133         if (base == 0)
  134                 return (ENXIO);
  135 
  136         if (init_type == XEN_HVM_INIT_COLD) {
  137                 int major, minor;
  138 
  139                 do_cpuid(base + 1, regs);
  140 
  141                 major = regs[0] >> 16;
  142                 minor = regs[0] & 0xffff;
  143                 printf("XEN: Hypervisor version %d.%d detected.\n", major,
  144                         minor);
  145 
  146 #ifdef SMP
  147                 if (((major < 4) || (major == 4 && minor <= 5)) &&
  148                     msix_disable_migration == -1) {
  149                         /*
  150                          * Xen hypervisors prior to 4.6.0 do not properly
  151                          * handle updates to enabled MSI-X table entries,
  152                          * so disable MSI-X interrupt migration in that
  153                          * case.
  154                          */
  155                         if (bootverbose)
  156                                 printf(
  157 "Disabling MSI-X interrupt migration due to Xen hypervisor bug.\n"
  158 "Set machdep.msix_disable_migration=0 to forcefully enable it.\n");
  159                         msix_disable_migration = 1;
  160                 }
  161 #endif
  162         }
  163 
  164         /*
  165          * Find the hypercall pages.
  166          */
  167         do_cpuid(base + 2, regs);
  168 
  169         for (i = 0; i < regs[0]; i++)
  170                 wrmsr(regs[1], vtophys(&hypercall_page + i * PAGE_SIZE) + i);
  171 
  172         return (0);
  173 }
  174 
  175 static void
  176 xen_hvm_init_shared_info_page(void)
  177 {
  178         struct xen_add_to_physmap xatp;
  179 
  180         if (xen_pv_domain()) {
  181                 /*
  182                  * Already setup in the PV case, shared_info is passed inside
  183                  * of the start_info struct at start of day.
  184                  */
  185                 return;
  186         }
  187 
  188         if (HYPERVISOR_shared_info == NULL) {
  189                 HYPERVISOR_shared_info = malloc(PAGE_SIZE, M_XENHVM, M_NOWAIT);
  190                 if (HYPERVISOR_shared_info == NULL)
  191                         panic("Unable to allocate Xen shared info page");
  192         }
  193 
  194         xatp.domid = DOMID_SELF;
  195         xatp.idx = 0;
  196         xatp.space = XENMAPSPACE_shared_info;
  197         xatp.gpfn = vtophys(HYPERVISOR_shared_info) >> PAGE_SHIFT;
  198         if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
  199                 panic("HYPERVISOR_memory_op failed");
  200 }
  201 
  202 /*
  203  * Tell the hypervisor how to contact us for event channel callbacks.
  204  */
  205 void
  206 xen_hvm_set_callback(device_t dev)
  207 {
  208         struct xen_hvm_param xhp;
  209         int irq;
  210 
  211         if (xen_vector_callback_enabled)
  212                 return;
  213 
  214         xhp.domid = DOMID_SELF;
  215         xhp.index = HVM_PARAM_CALLBACK_IRQ;
  216         if (xen_feature(XENFEAT_hvm_callback_vector) != 0) {
  217                 int error;
  218 
  219                 xhp.value = HVM_CALLBACK_VECTOR(IDT_EVTCHN);
  220                 error = HYPERVISOR_hvm_op(HVMOP_set_param, &xhp);
  221                 if (error == 0) {
  222                         xen_vector_callback_enabled = 1;
  223                         return;
  224                 }
  225                 printf("Xen HVM callback vector registration failed (%d). "
  226                     "Falling back to emulated device interrupt\n", error);
  227         }
  228         xen_vector_callback_enabled = 0;
  229         if (dev == NULL) {
  230                 /*
  231                  * Called from early boot or resume.
  232                  * xenpci will invoke us again later.
  233                  */
  234                 return;
  235         }
  236 
  237         irq = pci_get_irq(dev);
  238         if (irq < 16) {
  239                 xhp.value = HVM_CALLBACK_GSI(irq);
  240         } else {
  241                 u_int slot;
  242                 u_int pin;
  243 
  244                 slot = pci_get_slot(dev);
  245                 pin = pci_get_intpin(dev) - 1;
  246                 xhp.value = HVM_CALLBACK_PCI_INTX(slot, pin);
  247         }
  248 
  249         if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp) != 0)
  250                 panic("Can't set evtchn callback");
  251 }
  252 
  253 #define XEN_MAGIC_IOPORT 0x10
  254 enum {
  255         XMI_MAGIC                        = 0x49d2,
  256         XMI_UNPLUG_IDE_DISKS             = 0x01,
  257         XMI_UNPLUG_NICS                  = 0x02,
  258         XMI_UNPLUG_IDE_EXCEPT_PRI_MASTER = 0x04
  259 };
  260 
  261 static void
  262 xen_hvm_disable_emulated_devices(void)
  263 {
  264         u_short disable_devs = 0;
  265 
  266         if (xen_pv_domain()) {
  267                 /*
  268                  * No emulated devices in the PV case, so no need to unplug
  269                  * anything.
  270                  */
  271                 if (xen_disable_pv_disks != 0 || xen_disable_pv_nics != 0)
  272                         printf("PV devices cannot be disabled in PV guests\n");
  273                 return;
  274         }
  275 
  276         if (inw(XEN_MAGIC_IOPORT) != XMI_MAGIC)
  277                 return;
  278 
  279         if (xen_disable_pv_disks == 0) {
  280                 if (bootverbose)
  281                         printf("XEN: disabling emulated disks\n");
  282                 disable_devs |= XMI_UNPLUG_IDE_DISKS;
  283         }
  284         if (xen_disable_pv_nics == 0) {
  285                 if (bootverbose)
  286                         printf("XEN: disabling emulated nics\n");
  287                 disable_devs |= XMI_UNPLUG_NICS;
  288         }
  289 
  290         if (disable_devs != 0)
  291                 outw(XEN_MAGIC_IOPORT, disable_devs);
  292 }
  293 
  294 static void
  295 xen_hvm_init(enum xen_hvm_init_type init_type)
  296 {
  297         int error;
  298         int i;
  299 
  300         if (init_type == XEN_HVM_INIT_CANCELLED_SUSPEND)
  301                 return;
  302 
  303         error = xen_hvm_init_hypercall_stubs(init_type);
  304 
  305         switch (init_type) {
  306         case XEN_HVM_INIT_COLD:
  307                 if (error != 0)
  308                         return;
  309 
  310                 /*
  311                  * If xen_domain_type is not set at this point
  312                  * it means we are inside a (PV)HVM guest, because
  313                  * for PVH the guest type is set much earlier
  314                  * (see hammer_time_xen).
  315                  */
  316                 if (!xen_domain()) {
  317                         xen_domain_type = XEN_HVM_DOMAIN;
  318                         vm_guest = VM_GUEST_XEN;
  319                 }
  320 
  321                 setup_xen_features();
  322 #ifdef SMP
  323                 cpu_ops = xen_hvm_cpu_ops;
  324 #endif
  325                 break;
  326         case XEN_HVM_INIT_RESUME:
  327                 if (error != 0)
  328                         panic("Unable to init Xen hypercall stubs on resume");
  329 
  330                 /* Clear stale vcpu_info. */
  331                 CPU_FOREACH(i)
  332                         DPCPU_ID_SET(i, vcpu_info, NULL);
  333                 break;
  334         default:
  335                 panic("Unsupported HVM initialization type");
  336         }
  337 
  338         xen_vector_callback_enabled = 0;
  339         xen_hvm_set_callback(NULL);
  340 
  341         /*
  342          * On (PV)HVM domains we need to request the hypervisor to
  343          * fill the shared info page, for PVH guest the shared_info page
  344          * is passed inside the start_info struct and is already set, so this
  345          * functions are no-ops.
  346          */
  347         xen_hvm_init_shared_info_page();
  348         xen_hvm_disable_emulated_devices();
  349 } 
  350 
  351 void
  352 xen_hvm_suspend(void)
  353 {
  354 }
  355 
  356 void
  357 xen_hvm_resume(bool suspend_cancelled)
  358 {
  359 
  360         xen_hvm_init(suspend_cancelled ?
  361             XEN_HVM_INIT_CANCELLED_SUSPEND : XEN_HVM_INIT_RESUME);
  362 
  363         /* Register vcpu_info area for CPU#0. */
  364         xen_hvm_cpu_init();
  365 }
  366  
  367 static void
  368 xen_hvm_sysinit(void *arg __unused)
  369 {
  370         xen_hvm_init(XEN_HVM_INIT_COLD);
  371 }
  372 
  373 static void
  374 xen_set_vcpu_id(void)
  375 {
  376         struct pcpu *pc;
  377         int i;
  378 
  379         if (!xen_hvm_domain())
  380                 return;
  381 
  382         /* Set vcpu_id to acpi_id */
  383         CPU_FOREACH(i) {
  384                 pc = pcpu_find(i);
  385                 pc->pc_vcpu_id = pc->pc_acpi_id;
  386                 if (bootverbose)
  387                         printf("XEN: CPU %u has VCPU ID %u\n",
  388                                i, pc->pc_vcpu_id);
  389         }
  390 }
  391 
  392 static void
  393 xen_hvm_cpu_init(void)
  394 {
  395         struct vcpu_register_vcpu_info info;
  396         struct vcpu_info *vcpu_info;
  397         int cpu, rc;
  398 
  399         if (!xen_domain())
  400                 return;
  401 
  402         if (DPCPU_GET(vcpu_info) != NULL) {
  403                 /*
  404                  * vcpu_info is already set.  We're resuming
  405                  * from a failed migration and our pre-suspend
  406                  * configuration is still valid.
  407                  */
  408                 return;
  409         }
  410 
  411         vcpu_info = DPCPU_PTR(vcpu_local_info);
  412         cpu = PCPU_GET(vcpu_id);
  413         info.mfn = vtophys(vcpu_info) >> PAGE_SHIFT;
  414         info.offset = vtophys(vcpu_info) - trunc_page(vtophys(vcpu_info));
  415 
  416         rc = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info);
  417         if (rc != 0)
  418                 DPCPU_SET(vcpu_info, &HYPERVISOR_shared_info->vcpu_info[cpu]);
  419         else
  420                 DPCPU_SET(vcpu_info, vcpu_info);
  421 }
  422 
  423 SYSINIT(xen_hvm_init, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, xen_hvm_sysinit, NULL);
  424 SYSINIT(xen_hvm_cpu_init, SI_SUB_INTR, SI_ORDER_FIRST, xen_hvm_cpu_init, NULL);
  425 SYSINIT(xen_set_vcpu_id, SI_SUB_CPU, SI_ORDER_ANY, xen_set_vcpu_id, NULL);

Cache object: b940948d31b4c63927b4eaf16c17fbcf


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.