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  * Copyright (c) 2004 Christian Limpach.
    3  * Copyright (c) 2004-2006,2008 Kip Macy
    4  * Copyright (c) 2008 The NetBSD Foundation, Inc.
    5  * Copyright (c) 2013 Roger Pau Monné <roger.pau@citrix.com>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD: releng/11.2/sys/x86/xen/pv.c 329462 2018-02-17 18:00:01Z kib $");
   32 
   33 #include "opt_ddb.h"
   34 #include "opt_kstack_pages.h"
   35 
   36 #include <sys/param.h>
   37 #include <sys/bus.h>
   38 #include <sys/kernel.h>
   39 #include <sys/reboot.h>
   40 #include <sys/systm.h>
   41 #include <sys/malloc.h>
   42 #include <sys/linker.h>
   43 #include <sys/lock.h>
   44 #include <sys/rwlock.h>
   45 #include <sys/boot.h>
   46 #include <sys/ctype.h>
   47 #include <sys/mutex.h>
   48 #include <sys/smp.h>
   49 
   50 #include <vm/vm.h>
   51 #include <vm/vm_extern.h>
   52 #include <vm/vm_kern.h>
   53 #include <vm/vm_page.h>
   54 #include <vm/vm_map.h>
   55 #include <vm/vm_object.h>
   56 #include <vm/vm_pager.h>
   57 #include <vm/vm_param.h>
   58 
   59 #include <machine/intr_machdep.h>
   60 #include <x86/apicvar.h>
   61 #include <x86/init.h>
   62 #include <machine/pc/bios.h>
   63 #include <machine/smp.h>
   64 #include <machine/intr_machdep.h>
   65 #include <machine/metadata.h>
   66 
   67 #include <xen/xen-os.h>
   68 #include <xen/hypervisor.h>
   69 #include <xen/xenstore/xenstorevar.h>
   70 #include <xen/xen_pv.h>
   71 #include <xen/xen_msi.h>
   72 
   73 #include <xen/interface/vcpu.h>
   74 
   75 #include <dev/xen/timer/timer.h>
   76 
   77 #ifdef DDB
   78 #include <ddb/ddb.h>
   79 #endif
   80 
   81 /* Native initial function */
   82 extern u_int64_t hammer_time(u_int64_t, u_int64_t);
   83 /* Xen initial function */
   84 uint64_t hammer_time_xen(start_info_t *, uint64_t);
   85 
   86 #define MAX_E820_ENTRIES        128
   87 
   88 /*--------------------------- Forward Declarations ---------------------------*/
   89 static caddr_t xen_pv_parse_preload_data(u_int64_t);
   90 static void xen_pv_parse_memmap(caddr_t, vm_paddr_t *, int *);
   91 
   92 #ifdef SMP
   93 static int xen_pv_start_all_aps(void);
   94 #endif
   95 
   96 /*---------------------------- Extern Declarations ---------------------------*/
   97 #ifdef SMP
   98 /* Variables used by amd64 mp_machdep to start APs */
   99 extern char *doublefault_stack;
  100 extern char *mce_stack;
  101 extern char *nmi_stack;
  102 #endif
  103 
  104 /*
  105  * Placed by the linker at the end of the bss section, which is the last
  106  * section loaded by Xen before loading the symtab and strtab.
  107  */
  108 extern uint32_t end;
  109 
  110 /*-------------------------------- Global Data -------------------------------*/
  111 /* Xen init_ops implementation. */
  112 struct init_ops xen_init_ops = {
  113         .parse_preload_data             = xen_pv_parse_preload_data,
  114         .early_clock_source_init        = xen_clock_init,
  115         .early_delay                    = xen_delay,
  116         .parse_memmap                   = xen_pv_parse_memmap,
  117 #ifdef SMP
  118         .start_all_aps                  = xen_pv_start_all_aps,
  119 #endif
  120         .msi_init =                     xen_msi_init,
  121 };
  122 
  123 static struct bios_smap xen_smap[MAX_E820_ENTRIES];
  124 
  125 /*-------------------------------- Xen PV init -------------------------------*/
  126 /*
  127  * First function called by the Xen PVH boot sequence.
  128  *
  129  * Set some Xen global variables and prepare the environment so it is
  130  * as similar as possible to what native FreeBSD init function expects.
  131  */
  132 uint64_t
  133 hammer_time_xen(start_info_t *si, uint64_t xenstack)
  134 {
  135         uint64_t physfree;
  136         uint64_t *PT4 = (u_int64_t *)xenstack;
  137         uint64_t *PT3 = (u_int64_t *)(xenstack + PAGE_SIZE);
  138         uint64_t *PT2 = (u_int64_t *)(xenstack + 2 * PAGE_SIZE);
  139         int i;
  140 
  141         xen_domain_type = XEN_PV_DOMAIN;
  142         vm_guest = VM_GUEST_XEN;
  143 
  144         if ((si == NULL) || (xenstack == 0)) {
  145                 xc_printf("ERROR: invalid start_info or xen stack, halting\n");
  146                 HYPERVISOR_shutdown(SHUTDOWN_crash);
  147         }
  148 
  149         xc_printf("FreeBSD PVH running on %s\n", si->magic);
  150 
  151         /* We use 3 pages of xen stack for the boot pagetables */
  152         physfree = xenstack + 3 * PAGE_SIZE - KERNBASE;
  153 
  154         /* Setup Xen global variables */
  155         HYPERVISOR_start_info = si;
  156         HYPERVISOR_shared_info =
  157             (shared_info_t *)(si->shared_info + KERNBASE);
  158 
  159         /*
  160          * Setup some misc global variables for Xen devices
  161          *
  162          * XXX: Devices that need these specific variables should
  163          *      be rewritten to fetch this info by themselves from the
  164          *      start_info page.
  165          */
  166         xen_store = (struct xenstore_domain_interface *)
  167             (ptoa(si->store_mfn) + KERNBASE);
  168         console_page = (char *)(ptoa(si->console.domU.mfn) + KERNBASE);
  169 
  170         /*
  171          * Use the stack Xen gives us to build the page tables
  172          * as native FreeBSD expects to find them (created
  173          * by the boot trampoline).
  174          */
  175         for (i = 0; i < (PAGE_SIZE / sizeof(uint64_t)); i++) {
  176                 /*
  177                  * Each slot of the level 4 pages points
  178                  * to the same level 3 page
  179                  */
  180                 PT4[i] = ((uint64_t)&PT3[0]) - KERNBASE;
  181                 PT4[i] |= PG_V | PG_RW | PG_U;
  182 
  183                 /*
  184                  * Each slot of the level 3 pages points
  185                  * to the same level 2 page
  186                  */
  187                 PT3[i] = ((uint64_t)&PT2[0]) - KERNBASE;
  188                 PT3[i] |= PG_V | PG_RW | PG_U;
  189 
  190                 /*
  191                  * The level 2 page slots are mapped with
  192                  * 2MB pages for 1GB.
  193                  */
  194                 PT2[i] = i * (2 * 1024 * 1024);
  195                 PT2[i] |= PG_V | PG_RW | PG_PS | PG_U;
  196         }
  197         load_cr3(((uint64_t)&PT4[0]) - KERNBASE);
  198 
  199         /* Set the hooks for early functions that diverge from bare metal */
  200         init_ops = xen_init_ops;
  201         apic_ops = xen_apic_ops;
  202 
  203         /* Now we can jump into the native init function */
  204         return (hammer_time(0, physfree));
  205 }
  206 
  207 /*-------------------------------- PV specific -------------------------------*/
  208 #ifdef SMP
  209 static bool
  210 start_xen_ap(int cpu)
  211 {
  212         struct vcpu_guest_context *ctxt;
  213         int ms, cpus = mp_naps;
  214         const size_t stacksize = kstack_pages * PAGE_SIZE;
  215 
  216         /* allocate and set up an idle stack data page */
  217         bootstacks[cpu] =
  218             (void *)kmem_malloc(kernel_arena, stacksize, M_WAITOK | M_ZERO);
  219         doublefault_stack =
  220             (char *)kmem_malloc(kernel_arena, PAGE_SIZE, M_WAITOK | M_ZERO);
  221         mce_stack =
  222             (char *)kmem_malloc(kernel_arena, PAGE_SIZE, M_WAITOK | M_ZERO);
  223         nmi_stack =
  224             (char *)kmem_malloc(kernel_arena, PAGE_SIZE, M_WAITOK | M_ZERO);
  225         dpcpu =
  226             (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, M_WAITOK | M_ZERO);
  227 
  228         bootSTK = (char *)bootstacks[cpu] + kstack_pages * PAGE_SIZE - 8;
  229         bootAP = cpu;
  230 
  231         ctxt = malloc(sizeof(*ctxt), M_TEMP, M_WAITOK | M_ZERO);
  232 
  233         ctxt->flags = VGCF_IN_KERNEL;
  234         ctxt->user_regs.rip = (unsigned long) init_secondary;
  235         ctxt->user_regs.rsp = (unsigned long) bootSTK;
  236 
  237         /* Set the AP to use the same page tables */
  238         ctxt->ctrlreg[3] = KPML4phys;
  239 
  240         if (HYPERVISOR_vcpu_op(VCPUOP_initialise, cpu, ctxt))
  241                 panic("unable to initialize AP#%d", cpu);
  242 
  243         free(ctxt, M_TEMP);
  244 
  245         /* Launch the vCPU */
  246         if (HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL))
  247                 panic("unable to start AP#%d", cpu);
  248 
  249         /* Wait up to 5 seconds for it to start. */
  250         for (ms = 0; ms < 5000; ms++) {
  251                 if (mp_naps > cpus)
  252                         return (true);
  253                 DELAY(1000);
  254         }
  255 
  256         return (false);
  257 }
  258 
  259 static int
  260 xen_pv_start_all_aps(void)
  261 {
  262         int cpu;
  263 
  264         mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN);
  265 
  266         for (cpu = 1; cpu < mp_ncpus; cpu++) {
  267 
  268                 /* attempt to start the Application Processor */
  269                 if (!start_xen_ap(cpu))
  270                         panic("AP #%d failed to start!", cpu);
  271 
  272                 CPU_SET(cpu, &all_cpus);        /* record AP in CPU map */
  273         }
  274 
  275         return (mp_naps);
  276 }
  277 #endif /* SMP */
  278 
  279 /*
  280  * Functions to convert the "extra" parameters passed by Xen
  281  * into FreeBSD boot options.
  282  */
  283 static void
  284 xen_pv_set_env(void)
  285 {
  286         char *cmd_line_next, *cmd_line;
  287         size_t env_size;
  288 
  289         cmd_line = HYPERVISOR_start_info->cmd_line;
  290         env_size = sizeof(HYPERVISOR_start_info->cmd_line);
  291 
  292         /* Skip leading spaces */
  293         for (; isspace(*cmd_line) && (env_size != 0); cmd_line++)
  294                 env_size--;
  295 
  296         /* Replace ',' with '\0' */
  297         for (cmd_line_next = cmd_line; strsep(&cmd_line_next, ",") != NULL;)
  298                 ;
  299 
  300         init_static_kenv(cmd_line, 0);
  301 }
  302 
  303 static void
  304 xen_pv_set_boothowto(void)
  305 {
  306         int i;
  307         char *env;
  308 
  309         /* get equivalents from the environment */
  310         for (i = 0; howto_names[i].ev != NULL; i++) {
  311                 if ((env = kern_getenv(howto_names[i].ev)) != NULL) {
  312                         boothowto |= howto_names[i].mask;
  313                         freeenv(env);
  314                 }
  315         }
  316 }
  317 
  318 #ifdef DDB
  319 /*
  320  * The way Xen loads the symtab is different from the native boot loader,
  321  * because it's tailored for NetBSD. So we have to adapt and use the same
  322  * method as NetBSD. Portions of the code below have been picked from NetBSD:
  323  * sys/kern/kern_ksyms.c CVS Revision 1.71.
  324  */
  325 static void
  326 xen_pv_parse_symtab(void)
  327 {
  328         Elf_Ehdr *ehdr;
  329         Elf_Shdr *shdr;
  330         vm_offset_t sym_end;
  331         uint32_t size;
  332         int i, j;
  333 
  334         size = end;
  335         sym_end = HYPERVISOR_start_info->mod_start != 0 ?
  336             HYPERVISOR_start_info->mod_start :
  337             HYPERVISOR_start_info->mfn_list;
  338 
  339         /*
  340          * Make sure the size is right headed, sym_end is just a
  341          * high boundary, but at least allows us to fail earlier.
  342          */
  343         if ((vm_offset_t)&end + size > sym_end) {
  344                 xc_printf("Unable to load ELF symtab: size mismatch\n");
  345                 return;
  346         }
  347 
  348         ehdr = (Elf_Ehdr *)(&end + 1);
  349         if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
  350             ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
  351             ehdr->e_version > 1) {
  352                 xc_printf("Unable to load ELF symtab: invalid symbol table\n");
  353                 return;
  354         }
  355 
  356         shdr = (Elf_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff);
  357         /* Find the symbol table and the corresponding string table. */
  358         for (i = 1; i < ehdr->e_shnum; i++) {
  359                 if (shdr[i].sh_type != SHT_SYMTAB)
  360                         continue;
  361                 if (shdr[i].sh_offset == 0)
  362                         continue;
  363                 ksymtab = (uintptr_t)((uint8_t *)ehdr + shdr[i].sh_offset);
  364                 ksymtab_size = shdr[i].sh_size;
  365                 j = shdr[i].sh_link;
  366                 if (shdr[j].sh_offset == 0)
  367                         continue; /* Can this happen? */
  368                 kstrtab = (uintptr_t)((uint8_t *)ehdr + shdr[j].sh_offset);
  369                 break;
  370         }
  371 
  372         if (ksymtab == 0 || kstrtab == 0) {
  373                 xc_printf(
  374     "Unable to load ELF symtab: could not find symtab or strtab\n");
  375                 return;
  376         }
  377 }
  378 #endif
  379 
  380 static caddr_t
  381 xen_pv_parse_preload_data(u_int64_t modulep)
  382 {
  383         caddr_t          kmdp;
  384         vm_ooffset_t     off;
  385         vm_paddr_t       metadata;
  386         char             *envp;
  387 
  388         if (HYPERVISOR_start_info->mod_start != 0) {
  389                 preload_metadata = (caddr_t)(HYPERVISOR_start_info->mod_start);
  390 
  391                 kmdp = preload_search_by_type("elf kernel");
  392                 if (kmdp == NULL)
  393                         kmdp = preload_search_by_type("elf64 kernel");
  394                 KASSERT(kmdp != NULL, ("unable to find kernel"));
  395 
  396                 /*
  397                  * Xen has relocated the metadata and the modules,
  398                  * so we need to recalculate it's position. This is
  399                  * done by saving the original modulep address and
  400                  * then calculating the offset with mod_start,
  401                  * which contains the relocated modulep address.
  402                  */
  403                 metadata = MD_FETCH(kmdp, MODINFOMD_MODULEP, vm_paddr_t);
  404                 off = HYPERVISOR_start_info->mod_start - metadata;
  405 
  406                 preload_bootstrap_relocate(off);
  407 
  408                 boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int);
  409                 envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *);
  410                 if (envp != NULL)
  411                         envp += off;
  412                 init_static_kenv(envp, 0);
  413         } else {
  414                 /* Parse the extra boot information given by Xen */
  415                 xen_pv_set_env();
  416                 xen_pv_set_boothowto();
  417                 kmdp = NULL;
  418         }
  419 
  420 #ifdef DDB
  421         xen_pv_parse_symtab();
  422 #endif
  423         return (kmdp);
  424 }
  425 
  426 static void
  427 xen_pv_parse_memmap(caddr_t kmdp, vm_paddr_t *physmap, int *physmap_idx)
  428 {
  429         struct xen_memory_map memmap;
  430         u_int32_t size;
  431         int rc;
  432 
  433         /* Fetch the E820 map from Xen */
  434         memmap.nr_entries = MAX_E820_ENTRIES;
  435         set_xen_guest_handle(memmap.buffer, xen_smap);
  436         rc = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap);
  437         if (rc)
  438                 panic("unable to fetch Xen E820 memory map");
  439         size = memmap.nr_entries * sizeof(xen_smap[0]);
  440 
  441         bios_add_smap_entries(xen_smap, size, physmap, physmap_idx);
  442 }

Cache object: 5ce88668cb35494bc5fcfde8dbbeb0f1


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