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/pv.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  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
    3  *
    4  * Copyright (c) 2004 Christian Limpach.
    5  * Copyright (c) 2004-2006,2008 Kip Macy
    6  * Copyright (c) 2008 The NetBSD Foundation, Inc.
    7  * Copyright (c) 2013 Roger Pau Monné <roger.pau@citrix.com>
    8  * All rights reserved.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include "opt_ddb.h"
   36 #include "opt_kstack_pages.h"
   37 
   38 #include <sys/param.h>
   39 #include <sys/bus.h>
   40 #include <sys/kernel.h>
   41 #include <sys/reboot.h>
   42 #include <sys/systm.h>
   43 #include <sys/malloc.h>
   44 #include <sys/linker.h>
   45 #include <sys/lock.h>
   46 #include <sys/rwlock.h>
   47 #include <sys/boot.h>
   48 #include <sys/ctype.h>
   49 #include <sys/mutex.h>
   50 #include <sys/smp.h>
   51 #include <sys/efi.h>
   52 
   53 #include <vm/vm.h>
   54 #include <vm/vm_extern.h>
   55 #include <vm/vm_kern.h>
   56 #include <vm/vm_page.h>
   57 #include <vm/vm_map.h>
   58 #include <vm/vm_object.h>
   59 #include <vm/vm_pager.h>
   60 #include <vm/vm_param.h>
   61 
   62 #include <machine/_inttypes.h>
   63 #include <machine/intr_machdep.h>
   64 #include <x86/apicvar.h>
   65 #include <x86/init.h>
   66 #include <machine/pc/bios.h>
   67 #include <machine/smp.h>
   68 #include <machine/intr_machdep.h>
   69 #include <machine/md_var.h>
   70 #include <machine/metadata.h>
   71 #include <machine/cpu.h>
   72 
   73 #include <xen/xen-os.h>
   74 #include <xen/hvm.h>
   75 #include <xen/hypervisor.h>
   76 #include <xen/xenstore/xenstorevar.h>
   77 #include <xen/xen_pv.h>
   78 
   79 #include <contrib/xen/arch-x86/cpuid.h>
   80 #include <contrib/xen/arch-x86/hvm/start_info.h>
   81 #include <contrib/xen/vcpu.h>
   82 
   83 #include <dev/xen/timer/timer.h>
   84 
   85 #ifdef DDB
   86 #include <ddb/ddb.h>
   87 #endif
   88 
   89 /* Native initial function */
   90 extern u_int64_t hammer_time(u_int64_t, u_int64_t);
   91 /* Xen initial function */
   92 uint64_t hammer_time_xen(vm_paddr_t);
   93 
   94 #define MAX_E820_ENTRIES        128
   95 
   96 /*--------------------------- Forward Declarations ---------------------------*/
   97 static caddr_t xen_pvh_parse_preload_data(uint64_t);
   98 static void pvh_parse_memmap(caddr_t, vm_paddr_t *, int *);
   99 
  100 /*---------------------------- Extern Declarations ---------------------------*/
  101 /*
  102  * Placed by the linker at the end of the bss section, which is the last
  103  * section loaded by Xen before loading the symtab and strtab.
  104  */
  105 extern uint32_t end;
  106 
  107 /*-------------------------------- Global Data -------------------------------*/
  108 struct init_ops xen_pvh_init_ops = {
  109         .parse_preload_data             = xen_pvh_parse_preload_data,
  110         .early_clock_source_init        = xen_clock_init,
  111         .early_delay                    = xen_delay,
  112         .parse_memmap                   = pvh_parse_memmap,
  113 };
  114 
  115 static struct bios_smap xen_smap[MAX_E820_ENTRIES];
  116 
  117 static struct hvm_start_info *start_info;
  118 
  119 /*-------------------------------- Xen PV init -------------------------------*/
  120 
  121 static int
  122 isxen(void)
  123 {
  124         static int xen = -1;
  125         uint32_t base;
  126         u_int regs[4];
  127 
  128         if (xen != -1)
  129                 return (xen);
  130 
  131         /*
  132          * The full code for identifying which hypervisor we're running under
  133          * is in sys/x86/x86/identcpu.c and runs later in the boot process;
  134          * this is sufficient to distinguish Xen PVH booting from non-Xen PVH
  135          * and skip some very early Xen-specific code in the non-Xen case.
  136          */
  137         xen = 0;
  138         for (base = 0x40000000; base < 0x40010000; base += 0x100) {
  139                 do_cpuid(base, regs);
  140                 if (regs[1] == XEN_CPUID_SIGNATURE_EBX &&
  141                     regs[2] == XEN_CPUID_SIGNATURE_ECX &&
  142                     regs[3] == XEN_CPUID_SIGNATURE_EDX) {
  143                         xen = 1;
  144                         break;
  145                 }
  146         }
  147         return (xen);
  148 }
  149 
  150 #define CRASH(...) do {                                 \
  151         if (isxen()) {                                  \
  152                 xc_printf(__VA_ARGS__);                 \
  153                 HYPERVISOR_shutdown(SHUTDOWN_crash);    \
  154         } else {                                        \
  155                 halt();                                 \
  156         }                                               \
  157 } while (0)
  158 
  159 uint64_t
  160 hammer_time_xen(vm_paddr_t start_info_paddr)
  161 {
  162         struct hvm_modlist_entry *mod;
  163         struct xen_add_to_physmap xatp;
  164         uint64_t physfree;
  165         char *kenv;
  166         int rc;
  167 
  168         if (isxen()) {
  169                 xen_domain_type = XEN_HVM_DOMAIN;
  170                 vm_guest = VM_GUEST_XEN;
  171                 rc = xen_hvm_init_hypercall_stubs(XEN_HVM_INIT_EARLY);
  172                 if (rc) {
  173                         xc_printf("ERROR: failed to initialize hypercall page: %d\n",
  174                             rc);
  175                         HYPERVISOR_shutdown(SHUTDOWN_crash);
  176                 }
  177         }
  178 
  179         start_info = (struct hvm_start_info *)(start_info_paddr + KERNBASE);
  180         if (start_info->magic != XEN_HVM_START_MAGIC_VALUE) {
  181                 CRASH("Unknown magic value in start_info struct: %#x\n",
  182                     start_info->magic);
  183         }
  184 
  185         /*
  186          * Select the higher address to use as physfree: either after
  187          * start_info, after the kernel, after the memory map or after any of
  188          * the modules.  We assume enough memory to be available after the
  189          * selected address for the needs of very early memory allocations.
  190          */
  191         physfree = roundup2(start_info_paddr + sizeof(struct hvm_start_info),
  192             PAGE_SIZE);
  193         physfree = MAX(roundup2((vm_paddr_t)_end - KERNBASE, PAGE_SIZE),
  194             physfree);
  195 
  196         if (start_info->memmap_paddr != 0)
  197                 physfree = MAX(roundup2(start_info->memmap_paddr +
  198                     start_info->memmap_entries *
  199                     sizeof(struct hvm_memmap_table_entry), PAGE_SIZE),
  200                     physfree);
  201 
  202         if (start_info->modlist_paddr != 0) {
  203                 unsigned int i;
  204 
  205                 if (start_info->nr_modules == 0) {
  206                         CRASH(
  207                             "ERROR: modlist_paddr != 0 but nr_modules == 0\n");
  208                 }
  209                 mod = (struct hvm_modlist_entry *)
  210                     (start_info->modlist_paddr + KERNBASE);
  211                 for (i = 0; i < start_info->nr_modules; i++)
  212                         physfree = MAX(roundup2(mod[i].paddr + mod[i].size,
  213                             PAGE_SIZE), physfree);
  214         }
  215 
  216         if (isxen()) {
  217                 xatp.domid = DOMID_SELF;
  218                 xatp.idx = 0;
  219                 xatp.space = XENMAPSPACE_shared_info;
  220                 xatp.gpfn = atop(physfree);
  221                 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) {
  222                         xc_printf("ERROR: failed to setup shared_info page\n");
  223                         HYPERVISOR_shutdown(SHUTDOWN_crash);
  224                 }
  225                 HYPERVISOR_shared_info = (shared_info_t *)(physfree + KERNBASE);
  226                 physfree += PAGE_SIZE;
  227         }
  228 
  229         /*
  230          * Init a static kenv using a free page. The contents will be filled
  231          * from the parse_preload_data hook.
  232          */
  233         kenv = (void *)(physfree + KERNBASE);
  234         physfree += PAGE_SIZE;
  235         bzero_early(kenv, PAGE_SIZE);
  236         init_static_kenv(kenv, PAGE_SIZE);
  237 
  238         /* Set the hooks for early functions that diverge from bare metal */
  239         init_ops = xen_pvh_init_ops;
  240         hvm_start_flags = start_info->flags;
  241 
  242         /* Now we can jump into the native init function */
  243         return (hammer_time(0, physfree));
  244 }
  245 
  246 /*-------------------------------- PV specific -------------------------------*/
  247 
  248 /*
  249  * When booted as a PVH guest FreeBSD needs to avoid using the RSDP address
  250  * hint provided by the loader because it points to the native set of ACPI
  251  * tables instead of the ones crafted by Xen. The acpi.rsdp env variable is
  252  * removed from kenv if present, and a new acpi.rsdp is added to kenv that
  253  * points to the address of the Xen crafted RSDP.
  254  */
  255 static bool reject_option(const char *option)
  256 {
  257         static const char *reject[] = {
  258                 "acpi.rsdp",
  259         };
  260         unsigned int i;
  261 
  262         for (i = 0; i < nitems(reject); i++)
  263                 if (strncmp(option, reject[i], strlen(reject[i])) == 0)
  264                         return (true);
  265 
  266         return (false);
  267 }
  268 
  269 static void
  270 xen_pvh_set_env(char *env, bool (*filter)(const char *))
  271 {
  272         char *option;
  273 
  274         if (env == NULL)
  275                 return;
  276 
  277         option = env;
  278         while (*option != 0) {
  279                 char *value;
  280 
  281                 if (filter != NULL && filter(option)) {
  282                         option += strlen(option) + 1;
  283                         continue;
  284                 }
  285 
  286                 value = option;
  287                 option = strsep(&value, "=");
  288                 if (kern_setenv(option, value) != 0 && isxen())
  289                         xc_printf("unable to add kenv %s=%s\n", option, value);
  290                 option = value + strlen(value) + 1;
  291         }
  292 }
  293 
  294 #ifdef DDB
  295 /*
  296  * The way Xen loads the symtab is different from the native boot loader,
  297  * because it's tailored for NetBSD. So we have to adapt and use the same
  298  * method as NetBSD. Portions of the code below have been picked from NetBSD:
  299  * sys/kern/kern_ksyms.c CVS Revision 1.71.
  300  */
  301 static void
  302 xen_pvh_parse_symtab(void)
  303 {
  304         Elf_Ehdr *ehdr;
  305         Elf_Shdr *shdr;
  306         int i, j;
  307 
  308         ehdr = (Elf_Ehdr *)(&end + 1);
  309         if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
  310             ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
  311             ehdr->e_version > 1) {
  312                 if (isxen())
  313                         xc_printf("Unable to load ELF symtab: invalid symbol table\n");
  314                 return;
  315         }
  316 
  317         shdr = (Elf_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff);
  318         /* Find the symbol table and the corresponding string table. */
  319         for (i = 1; i < ehdr->e_shnum; i++) {
  320                 if (shdr[i].sh_type != SHT_SYMTAB)
  321                         continue;
  322                 if (shdr[i].sh_offset == 0)
  323                         continue;
  324                 ksymtab = (uintptr_t)((uint8_t *)ehdr + shdr[i].sh_offset);
  325                 ksymtab_size = shdr[i].sh_size;
  326                 j = shdr[i].sh_link;
  327                 if (shdr[j].sh_offset == 0)
  328                         continue; /* Can this happen? */
  329                 kstrtab = (uintptr_t)((uint8_t *)ehdr + shdr[j].sh_offset);
  330                 break;
  331         }
  332 
  333         if ((ksymtab == 0 || kstrtab == 0) && isxen())
  334                 xc_printf(
  335     "Unable to load ELF symtab: could not find symtab or strtab\n");
  336 }
  337 #endif
  338 
  339 static caddr_t
  340 xen_pvh_parse_preload_data(uint64_t modulep)
  341 {
  342         caddr_t kmdp;
  343         vm_ooffset_t off;
  344         vm_paddr_t metadata;
  345         char *envp;
  346         char acpi_rsdp[19];
  347 
  348         if (start_info->modlist_paddr != 0) {
  349                 struct hvm_modlist_entry *mod;
  350                 const char *cmdline;
  351 
  352                 mod = (struct hvm_modlist_entry *)
  353                     (start_info->modlist_paddr + KERNBASE);
  354                 cmdline = mod[0].cmdline_paddr ?
  355                     (const char *)(mod[0].cmdline_paddr + KERNBASE) : NULL;
  356 
  357                 if (strcmp(cmdline, "header") == 0) {
  358                         struct xen_header *header;
  359 
  360                         header = (struct xen_header *)(mod[0].paddr + KERNBASE);
  361 
  362                         if ((header->flags & XENHEADER_HAS_MODULEP_OFFSET) !=
  363                             XENHEADER_HAS_MODULEP_OFFSET) {
  364                                 xc_printf("Unable to load module metadata\n");
  365                                 HYPERVISOR_shutdown(SHUTDOWN_crash);
  366                         }
  367 
  368                         preload_metadata = (caddr_t)(mod[0].paddr +
  369                             header->modulep_offset + KERNBASE);
  370 
  371                         kmdp = preload_search_by_type("elf kernel");
  372                         if (kmdp == NULL)
  373                                 kmdp = preload_search_by_type("elf64 kernel");
  374                         if (kmdp == NULL) {
  375                                 xc_printf("Unable to find kernel\n");
  376                                 HYPERVISOR_shutdown(SHUTDOWN_crash);
  377                         }
  378 
  379                         /*
  380                          * Xen has relocated the metadata and the modules, so
  381                          * we need to recalculate it's position. This is done
  382                          * by saving the original modulep address and then
  383                          * calculating the offset from the real modulep
  384                          * position.
  385                          */
  386                         metadata = MD_FETCH(kmdp, MODINFOMD_MODULEP,
  387                             vm_paddr_t);
  388                         off = mod[0].paddr + header->modulep_offset - metadata +
  389                             KERNBASE;
  390                 } else {
  391                         preload_metadata = (caddr_t)(mod[0].paddr + KERNBASE);
  392 
  393                         kmdp = preload_search_by_type("elf kernel");
  394                         if (kmdp == NULL)
  395                                 kmdp = preload_search_by_type("elf64 kernel");
  396                         if (kmdp == NULL) {
  397                                 xc_printf("Unable to find kernel\n");
  398                                 HYPERVISOR_shutdown(SHUTDOWN_crash);
  399                         }
  400 
  401                         metadata = MD_FETCH(kmdp, MODINFOMD_MODULEP, vm_paddr_t);
  402                         off = mod[0].paddr + KERNBASE - metadata;
  403                 }
  404 
  405                 preload_bootstrap_relocate(off);
  406 
  407                 boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
  408                 envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
  409                 if (envp != NULL)
  410                         envp += off;
  411                 xen_pvh_set_env(envp, reject_option);
  412 
  413                 if (MD_FETCH(kmdp, MODINFOMD_EFI_MAP, void *) != NULL)
  414                     strlcpy(bootmethod, "UEFI", sizeof(bootmethod));
  415                 else
  416                     strlcpy(bootmethod, "BIOS", sizeof(bootmethod));
  417         } else {
  418                 /* Parse the extra boot information given by Xen */
  419                 if (start_info->cmdline_paddr != 0)
  420                         boot_parse_cmdline_delim(
  421                             (char *)(start_info->cmdline_paddr + KERNBASE),
  422                             ", \t\n");
  423                 kmdp = NULL;
  424                 strlcpy(bootmethod, "PVH", sizeof(bootmethod));
  425         }
  426 
  427         boothowto |= boot_env_to_howto();
  428 
  429         snprintf(acpi_rsdp, sizeof(acpi_rsdp), "%#" PRIx64,
  430             start_info->rsdp_paddr);
  431         kern_setenv("acpi.rsdp", acpi_rsdp);
  432 
  433 #ifdef DDB
  434         xen_pvh_parse_symtab();
  435 #endif
  436         return (kmdp);
  437 }
  438 
  439 static void
  440 pvh_parse_memmap_start_info(caddr_t kmdp, vm_paddr_t *physmap,
  441     int *physmap_idx)
  442 {
  443         const struct hvm_memmap_table_entry * entries;
  444         size_t nentries;
  445         size_t i;
  446 
  447         /* Extract from HVM start_info. */
  448         entries = (struct hvm_memmap_table_entry *)(start_info->memmap_paddr + KERNBASE);
  449         nentries = start_info->memmap_entries;
  450 
  451         /* Convert into E820 format and handle one by one. */
  452         for (i = 0; i < nentries; i++) {
  453                 struct bios_smap entry;
  454 
  455                 entry.base = entries[i].addr;
  456                 entry.length = entries[i].size;
  457 
  458                 /*
  459                  * Luckily for us, the XEN_HVM_MEMMAP_TYPE_* values exactly
  460                  * match the SMAP_TYPE_* values so we don't need to translate
  461                  * anything here.
  462                  */
  463                 entry.type = entries[i].type;
  464 
  465                 bios_add_smap_entries(&entry, 1, physmap, physmap_idx);
  466         }
  467 }
  468 
  469 static void
  470 xen_pvh_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx)
  471 {
  472         struct xen_memory_map memmap;
  473         u_int32_t size;
  474         int rc;
  475 
  476         /* We should only reach here if we're running under Xen. */
  477         KASSERT(isxen(), ("xen_pvh_parse_memmap reached when !Xen"));
  478 
  479         /* Fetch the E820 map from Xen */
  480         memmap.nr_entries = MAX_E820_ENTRIES;
  481         set_xen_guest_handle(memmap.buffer, xen_smap);
  482         rc = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap);
  483         if (rc) {
  484                 xc_printf("ERROR: unable to fetch Xen E820 memory map: %d\n",
  485                     rc);
  486                 HYPERVISOR_shutdown(SHUTDOWN_crash);
  487         }
  488 
  489         size = memmap.nr_entries * sizeof(xen_smap[0]);
  490 
  491         bios_add_smap_entries(xen_smap, size, physmap, physmap_idx);
  492 }
  493 
  494 static void
  495 pvh_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx)
  496 {
  497 
  498         /*
  499          * If version >= 1 and memmap_paddr != 0, use the memory map provided
  500          * in the start_info structure; if not, we're running under legacy
  501          * Xen and need to use the Xen hypercall.
  502          */
  503         if ((start_info->version >= 1) && (start_info->memmap_paddr != 0))
  504                 pvh_parse_memmap_start_info(kmdp, physmap, physmap_idx);
  505         else
  506                 xen_pvh_parse_memmap(kmdp, physmap, physmap_idx);
  507 }

Cache object: 8e773634837b838f1ae878ba87348982


[ 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.