1e285ef8dSPeter Grehan /*- 2e285ef8dSPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3e285ef8dSPeter Grehan * All rights reserved. 4e285ef8dSPeter Grehan * 5e285ef8dSPeter Grehan * Redistribution and use in source and binary forms, with or without 6e285ef8dSPeter Grehan * modification, are permitted provided that the following conditions 7e285ef8dSPeter Grehan * are met: 8e285ef8dSPeter Grehan * 1. Redistributions of source code must retain the above copyright 9e285ef8dSPeter Grehan * notice, this list of conditions and the following disclaimer. 10e285ef8dSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11e285ef8dSPeter Grehan * notice, this list of conditions and the following disclaimer in the 12e285ef8dSPeter Grehan * documentation and/or other materials provided with the distribution. 13e285ef8dSPeter Grehan * 14e285ef8dSPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15e285ef8dSPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16e285ef8dSPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17e285ef8dSPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18e285ef8dSPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19e285ef8dSPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20e285ef8dSPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21e285ef8dSPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22e285ef8dSPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23e285ef8dSPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24e285ef8dSPeter Grehan * SUCH DAMAGE. 25e285ef8dSPeter Grehan * 26e285ef8dSPeter Grehan * $FreeBSD$ 27e285ef8dSPeter Grehan */ 28e285ef8dSPeter Grehan 29e285ef8dSPeter Grehan #include <sys/cdefs.h> 30e285ef8dSPeter Grehan __FBSDID("$FreeBSD$"); 31e285ef8dSPeter Grehan 32e285ef8dSPeter Grehan #include <sys/types.h> 33e285ef8dSPeter Grehan #include <sys/mman.h> 34e285ef8dSPeter Grehan #include <sys/time.h> 35e285ef8dSPeter Grehan 36e285ef8dSPeter Grehan #include <machine/segments.h> 37e285ef8dSPeter Grehan 38e285ef8dSPeter Grehan #include <stdio.h> 39e285ef8dSPeter Grehan #include <stdlib.h> 40e285ef8dSPeter Grehan #include <libgen.h> 41e285ef8dSPeter Grehan #include <unistd.h> 42e285ef8dSPeter Grehan #include <assert.h> 43e285ef8dSPeter Grehan #include <errno.h> 44e285ef8dSPeter Grehan #include <signal.h> 45e285ef8dSPeter Grehan #include <pthread.h> 46e285ef8dSPeter Grehan #include <pthread_np.h> 47e285ef8dSPeter Grehan 48e285ef8dSPeter Grehan #include <machine/vmm.h> 49e285ef8dSPeter Grehan #include <vmmapi.h> 50e285ef8dSPeter Grehan 51e285ef8dSPeter Grehan #include "bhyverun.h" 52e285ef8dSPeter Grehan #include "acpi.h" 53e285ef8dSPeter Grehan #include "inout.h" 54e285ef8dSPeter Grehan #include "dbgport.h" 55e285ef8dSPeter Grehan #include "mem.h" 56e285ef8dSPeter Grehan #include "mevent.h" 57e285ef8dSPeter Grehan #include "mptbl.h" 58e285ef8dSPeter Grehan #include "pci_emul.h" 59e285ef8dSPeter Grehan #include "xmsr.h" 60e285ef8dSPeter Grehan #include "ioapic.h" 61e285ef8dSPeter Grehan #include "spinup_ap.h" 62*9d6be09fSPeter Grehan #include "rtc.h" 63e285ef8dSPeter Grehan 64e285ef8dSPeter Grehan #define DEFAULT_GUEST_HZ 100 65e285ef8dSPeter Grehan #define DEFAULT_GUEST_TSLICE 200 66e285ef8dSPeter Grehan 67e285ef8dSPeter Grehan #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ 68e285ef8dSPeter Grehan 69e285ef8dSPeter Grehan #define VMEXIT_SWITCH 0 /* force vcpu switch in mux mode */ 70e285ef8dSPeter Grehan #define VMEXIT_CONTINUE 1 /* continue from next instruction */ 71e285ef8dSPeter Grehan #define VMEXIT_RESTART 2 /* restart current instruction */ 72e285ef8dSPeter Grehan #define VMEXIT_ABORT 3 /* abort the vm run loop */ 73e285ef8dSPeter Grehan #define VMEXIT_RESET 4 /* guest machine has reset */ 74e285ef8dSPeter Grehan 75e285ef8dSPeter Grehan #define MB (1024UL * 1024) 76e285ef8dSPeter Grehan #define GB (1024UL * MB) 77e285ef8dSPeter Grehan 78e285ef8dSPeter Grehan typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); 79e285ef8dSPeter Grehan 80e285ef8dSPeter Grehan int guest_tslice = DEFAULT_GUEST_TSLICE; 81e285ef8dSPeter Grehan int guest_hz = DEFAULT_GUEST_HZ; 82e285ef8dSPeter Grehan char *vmname; 83e285ef8dSPeter Grehan 84e285ef8dSPeter Grehan int guest_ncpus; 85e285ef8dSPeter Grehan 86e285ef8dSPeter Grehan static int pincpu = -1; 87e285ef8dSPeter Grehan static int guest_vcpu_mux; 88e285ef8dSPeter Grehan static int guest_vmexit_on_hlt, guest_vmexit_on_pause, disable_x2apic; 89e285ef8dSPeter Grehan 90e285ef8dSPeter Grehan static int foundcpus; 91e285ef8dSPeter Grehan 92e285ef8dSPeter Grehan static int strictio; 93e285ef8dSPeter Grehan 94e285ef8dSPeter Grehan static int acpi; 95e285ef8dSPeter Grehan 96e285ef8dSPeter Grehan static char *progname; 97e285ef8dSPeter Grehan static const int BSP = 0; 98e285ef8dSPeter Grehan 99e285ef8dSPeter Grehan static int cpumask; 100e285ef8dSPeter Grehan 101e285ef8dSPeter Grehan static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip); 102e285ef8dSPeter Grehan 103e285ef8dSPeter Grehan struct vm_exit vmexit[VM_MAXCPU]; 104e285ef8dSPeter Grehan 105e285ef8dSPeter Grehan struct fbsdstats { 106e285ef8dSPeter Grehan uint64_t vmexit_bogus; 107e285ef8dSPeter Grehan uint64_t vmexit_bogus_switch; 108e285ef8dSPeter Grehan uint64_t vmexit_hlt; 109e285ef8dSPeter Grehan uint64_t vmexit_pause; 110e285ef8dSPeter Grehan uint64_t vmexit_mtrap; 111e285ef8dSPeter Grehan uint64_t vmexit_paging; 112e285ef8dSPeter Grehan uint64_t cpu_switch_rotate; 113e285ef8dSPeter Grehan uint64_t cpu_switch_direct; 114e285ef8dSPeter Grehan int io_reset; 115e285ef8dSPeter Grehan } stats; 116e285ef8dSPeter Grehan 117e285ef8dSPeter Grehan struct mt_vmm_info { 118e285ef8dSPeter Grehan pthread_t mt_thr; 119e285ef8dSPeter Grehan struct vmctx *mt_ctx; 120e285ef8dSPeter Grehan int mt_vcpu; 121e285ef8dSPeter Grehan } mt_vmm_info[VM_MAXCPU]; 122e285ef8dSPeter Grehan 123e285ef8dSPeter Grehan static void 124e285ef8dSPeter Grehan usage(int code) 125e285ef8dSPeter Grehan { 126e285ef8dSPeter Grehan 127e285ef8dSPeter Grehan fprintf(stderr, 128e285ef8dSPeter Grehan "Usage: %s [-aehABHIP][-g <gdb port>][-z <hz>][-s <pci>]" 12924be8623SNeel Natu "[-S <pci>][-p pincpu][-n <pci>][-m lowmem][-M highmem]" 13024be8623SNeel Natu " <vmname>\n" 131e285ef8dSPeter Grehan " -a: local apic is in XAPIC mode (default is X2APIC)\n" 132e285ef8dSPeter Grehan " -A: create an ACPI table\n" 133e285ef8dSPeter Grehan " -g: gdb port (default is %d and 0 means don't open)\n" 134e285ef8dSPeter Grehan " -c: # cpus (default 1)\n" 135e285ef8dSPeter Grehan " -p: pin vcpu 'n' to host cpu 'pincpu + n'\n" 136e285ef8dSPeter Grehan " -B: inject breakpoint exception on vm entry\n" 137e285ef8dSPeter Grehan " -H: vmexit from the guest on hlt\n" 138e285ef8dSPeter Grehan " -I: present an ioapic to the guest\n" 139e285ef8dSPeter Grehan " -P: vmexit from the guest on pause\n" 140e285ef8dSPeter Grehan " -e: exit on unhandled i/o access\n" 141e285ef8dSPeter Grehan " -h: help\n" 142e285ef8dSPeter Grehan " -z: guest hz (default is %d)\n" 143e285ef8dSPeter Grehan " -s: <slot,driver,configinfo> PCI slot config\n" 144e285ef8dSPeter Grehan " -S: <slot,driver,configinfo> legacy PCI slot config\n" 145b060ba50SNeel Natu " -m: memory size in MB\n" 146e285ef8dSPeter Grehan " -x: mux vcpus to 1 hcpu\n" 147e285ef8dSPeter Grehan " -t: mux vcpu timeslice hz (default %d)\n", 148e285ef8dSPeter Grehan progname, DEFAULT_GDB_PORT, DEFAULT_GUEST_HZ, 149e285ef8dSPeter Grehan DEFAULT_GUEST_TSLICE); 150e285ef8dSPeter Grehan exit(code); 151e285ef8dSPeter Grehan } 152e285ef8dSPeter Grehan 153e285ef8dSPeter Grehan void * 154b060ba50SNeel Natu paddr_guest2host(struct vmctx *ctx, uintptr_t gaddr, size_t len) 155e285ef8dSPeter Grehan { 156e285ef8dSPeter Grehan 157b060ba50SNeel Natu return (vm_map_gpa(ctx, gaddr, len)); 158e285ef8dSPeter Grehan } 159e285ef8dSPeter Grehan 160e285ef8dSPeter Grehan int 161e285ef8dSPeter Grehan fbsdrun_disable_x2apic(void) 162e285ef8dSPeter Grehan { 163e285ef8dSPeter Grehan 164e285ef8dSPeter Grehan return (disable_x2apic); 165e285ef8dSPeter Grehan } 166e285ef8dSPeter Grehan 167e285ef8dSPeter Grehan int 168e285ef8dSPeter Grehan fbsdrun_vmexit_on_pause(void) 169e285ef8dSPeter Grehan { 170e285ef8dSPeter Grehan 171e285ef8dSPeter Grehan return (guest_vmexit_on_pause); 172e285ef8dSPeter Grehan } 173e285ef8dSPeter Grehan 174e285ef8dSPeter Grehan int 175e285ef8dSPeter Grehan fbsdrun_vmexit_on_hlt(void) 176e285ef8dSPeter Grehan { 177e285ef8dSPeter Grehan 178e285ef8dSPeter Grehan return (guest_vmexit_on_hlt); 179e285ef8dSPeter Grehan } 180e285ef8dSPeter Grehan 181e285ef8dSPeter Grehan int 182e285ef8dSPeter Grehan fbsdrun_muxed(void) 183e285ef8dSPeter Grehan { 184e285ef8dSPeter Grehan 185e285ef8dSPeter Grehan return (guest_vcpu_mux); 186e285ef8dSPeter Grehan } 187e285ef8dSPeter Grehan 188e285ef8dSPeter Grehan static void * 189e285ef8dSPeter Grehan fbsdrun_start_thread(void *param) 190e285ef8dSPeter Grehan { 191e285ef8dSPeter Grehan char tname[MAXCOMLEN + 1]; 192e285ef8dSPeter Grehan struct mt_vmm_info *mtp; 193e285ef8dSPeter Grehan int vcpu; 194e285ef8dSPeter Grehan 195e285ef8dSPeter Grehan mtp = param; 196e285ef8dSPeter Grehan vcpu = mtp->mt_vcpu; 197e285ef8dSPeter Grehan 198e285ef8dSPeter Grehan snprintf(tname, sizeof(tname), "%s vcpu %d", vmname, vcpu); 199e285ef8dSPeter Grehan pthread_set_name_np(mtp->mt_thr, tname); 200e285ef8dSPeter Grehan 201e285ef8dSPeter Grehan vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip); 202e285ef8dSPeter Grehan 203e285ef8dSPeter Grehan /* not reached */ 204e285ef8dSPeter Grehan exit(1); 205e285ef8dSPeter Grehan return (NULL); 206e285ef8dSPeter Grehan } 207e285ef8dSPeter Grehan 208e285ef8dSPeter Grehan void 209e285ef8dSPeter Grehan fbsdrun_addcpu(struct vmctx *ctx, int vcpu, uint64_t rip) 210e285ef8dSPeter Grehan { 211e285ef8dSPeter Grehan int error; 212e285ef8dSPeter Grehan 213e285ef8dSPeter Grehan if (cpumask & (1 << vcpu)) { 214e285ef8dSPeter Grehan fprintf(stderr, "addcpu: attempting to add existing cpu %d\n", 215e285ef8dSPeter Grehan vcpu); 216e285ef8dSPeter Grehan exit(1); 217e285ef8dSPeter Grehan } 218e285ef8dSPeter Grehan 219e285ef8dSPeter Grehan cpumask |= 1 << vcpu; 220e285ef8dSPeter Grehan foundcpus++; 221e285ef8dSPeter Grehan 222e285ef8dSPeter Grehan /* 223e285ef8dSPeter Grehan * Set up the vmexit struct to allow execution to start 224e285ef8dSPeter Grehan * at the given RIP 225e285ef8dSPeter Grehan */ 226e285ef8dSPeter Grehan vmexit[vcpu].rip = rip; 227e285ef8dSPeter Grehan vmexit[vcpu].inst_length = 0; 228e285ef8dSPeter Grehan 229e285ef8dSPeter Grehan if (vcpu == BSP || !guest_vcpu_mux){ 230e285ef8dSPeter Grehan mt_vmm_info[vcpu].mt_ctx = ctx; 231e285ef8dSPeter Grehan mt_vmm_info[vcpu].mt_vcpu = vcpu; 232e285ef8dSPeter Grehan 233e285ef8dSPeter Grehan error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL, 234e285ef8dSPeter Grehan fbsdrun_start_thread, &mt_vmm_info[vcpu]); 235e285ef8dSPeter Grehan assert(error == 0); 236e285ef8dSPeter Grehan } 237e285ef8dSPeter Grehan } 238e285ef8dSPeter Grehan 239e285ef8dSPeter Grehan static int 240e285ef8dSPeter Grehan fbsdrun_get_next_cpu(int curcpu) 241e285ef8dSPeter Grehan { 242e285ef8dSPeter Grehan 243e285ef8dSPeter Grehan /* 244e285ef8dSPeter Grehan * Get the next available CPU. Assumes they arrive 245e285ef8dSPeter Grehan * in ascending order with no gaps. 246e285ef8dSPeter Grehan */ 247e285ef8dSPeter Grehan return ((curcpu + 1) % foundcpus); 248e285ef8dSPeter Grehan } 249e285ef8dSPeter Grehan 250e285ef8dSPeter Grehan static int 251e285ef8dSPeter Grehan vmexit_catch_reset(void) 252e285ef8dSPeter Grehan { 253e285ef8dSPeter Grehan stats.io_reset++; 254e285ef8dSPeter Grehan return (VMEXIT_RESET); 255e285ef8dSPeter Grehan } 256e285ef8dSPeter Grehan 257e285ef8dSPeter Grehan static int 258e285ef8dSPeter Grehan vmexit_catch_inout(void) 259e285ef8dSPeter Grehan { 260e285ef8dSPeter Grehan return (VMEXIT_ABORT); 261e285ef8dSPeter Grehan } 262e285ef8dSPeter Grehan 263e285ef8dSPeter Grehan static int 264e285ef8dSPeter Grehan vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu, 265e285ef8dSPeter Grehan uint32_t eax) 266e285ef8dSPeter Grehan { 267e285ef8dSPeter Grehan #if PG_DEBUG /* put all types of debug here */ 268e285ef8dSPeter Grehan if (eax == 0) { 269e285ef8dSPeter Grehan pause_noswitch = 1; 270e285ef8dSPeter Grehan } else if (eax == 1) { 271e285ef8dSPeter Grehan pause_noswitch = 0; 272e285ef8dSPeter Grehan } else { 273e285ef8dSPeter Grehan pause_noswitch = 0; 274e285ef8dSPeter Grehan if (eax == 5) { 275e285ef8dSPeter Grehan vm_set_capability(ctx, *pvcpu, VM_CAP_MTRAP_EXIT, 1); 276e285ef8dSPeter Grehan } 277e285ef8dSPeter Grehan } 278e285ef8dSPeter Grehan #endif 279e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 280e285ef8dSPeter Grehan } 281e285ef8dSPeter Grehan 282e285ef8dSPeter Grehan static int 283e285ef8dSPeter Grehan vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 284e285ef8dSPeter Grehan { 285e285ef8dSPeter Grehan int error; 286e285ef8dSPeter Grehan int bytes, port, in, out; 287e285ef8dSPeter Grehan uint32_t eax; 288e285ef8dSPeter Grehan int vcpu; 289e285ef8dSPeter Grehan 290e285ef8dSPeter Grehan vcpu = *pvcpu; 291e285ef8dSPeter Grehan 292e285ef8dSPeter Grehan port = vme->u.inout.port; 293e285ef8dSPeter Grehan bytes = vme->u.inout.bytes; 294e285ef8dSPeter Grehan eax = vme->u.inout.eax; 295e285ef8dSPeter Grehan in = vme->u.inout.in; 296e285ef8dSPeter Grehan out = !in; 297e285ef8dSPeter Grehan 298e285ef8dSPeter Grehan /* We don't deal with these */ 299e285ef8dSPeter Grehan if (vme->u.inout.string || vme->u.inout.rep) 300e285ef8dSPeter Grehan return (VMEXIT_ABORT); 301e285ef8dSPeter Grehan 302e285ef8dSPeter Grehan /* Special case of guest reset */ 303e285ef8dSPeter Grehan if (out && port == 0x64 && (uint8_t)eax == 0xFE) 304e285ef8dSPeter Grehan return (vmexit_catch_reset()); 305e285ef8dSPeter Grehan 306e285ef8dSPeter Grehan /* Extra-special case of host notifications */ 307e285ef8dSPeter Grehan if (out && port == GUEST_NIO_PORT) 308e285ef8dSPeter Grehan return (vmexit_handle_notify(ctx, vme, pvcpu, eax)); 309e285ef8dSPeter Grehan 310e285ef8dSPeter Grehan error = emulate_inout(ctx, vcpu, in, port, bytes, &eax, strictio); 311e285ef8dSPeter Grehan if (error == 0 && in) 312e285ef8dSPeter Grehan error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, eax); 313e285ef8dSPeter Grehan 314e285ef8dSPeter Grehan if (error == 0) 315e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 316e285ef8dSPeter Grehan else { 317e285ef8dSPeter Grehan fprintf(stderr, "Unhandled %s%c 0x%04x\n", 318e285ef8dSPeter Grehan in ? "in" : "out", 319e285ef8dSPeter Grehan bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), port); 320e285ef8dSPeter Grehan return (vmexit_catch_inout()); 321e285ef8dSPeter Grehan } 322e285ef8dSPeter Grehan } 323e285ef8dSPeter Grehan 324e285ef8dSPeter Grehan static int 325e285ef8dSPeter Grehan vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 326e285ef8dSPeter Grehan { 327e285ef8dSPeter Grehan fprintf(stderr, "vm exit rdmsr 0x%x, cpu %d\n", vme->u.msr.code, 328e285ef8dSPeter Grehan *pvcpu); 329e285ef8dSPeter Grehan return (VMEXIT_ABORT); 330e285ef8dSPeter Grehan } 331e285ef8dSPeter Grehan 332e285ef8dSPeter Grehan static int 333e285ef8dSPeter Grehan vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 334e285ef8dSPeter Grehan { 335e285ef8dSPeter Grehan int newcpu; 336e285ef8dSPeter Grehan int retval = VMEXIT_CONTINUE; 337e285ef8dSPeter Grehan 338e285ef8dSPeter Grehan newcpu = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code,vme->u.msr.wval); 339e285ef8dSPeter Grehan 340e285ef8dSPeter Grehan if (guest_vcpu_mux && *pvcpu != newcpu) { 341e285ef8dSPeter Grehan retval = VMEXIT_SWITCH; 342e285ef8dSPeter Grehan *pvcpu = newcpu; 343e285ef8dSPeter Grehan } 344e285ef8dSPeter Grehan 345e285ef8dSPeter Grehan return (retval); 346e285ef8dSPeter Grehan } 347e285ef8dSPeter Grehan 348e285ef8dSPeter Grehan static int 349e285ef8dSPeter Grehan vmexit_spinup_ap(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 350e285ef8dSPeter Grehan { 351e285ef8dSPeter Grehan int newcpu; 352e285ef8dSPeter Grehan int retval = VMEXIT_CONTINUE; 353e285ef8dSPeter Grehan 354e285ef8dSPeter Grehan newcpu = spinup_ap(ctx, *pvcpu, 355e285ef8dSPeter Grehan vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip); 356e285ef8dSPeter Grehan 357e285ef8dSPeter Grehan if (guest_vcpu_mux && *pvcpu != newcpu) { 358e285ef8dSPeter Grehan retval = VMEXIT_SWITCH; 359e285ef8dSPeter Grehan *pvcpu = newcpu; 360e285ef8dSPeter Grehan } 361e285ef8dSPeter Grehan 362e285ef8dSPeter Grehan return (retval); 363e285ef8dSPeter Grehan } 364e285ef8dSPeter Grehan 365e285ef8dSPeter Grehan static int 366e285ef8dSPeter Grehan vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 367e285ef8dSPeter Grehan { 368e285ef8dSPeter Grehan 369e285ef8dSPeter Grehan fprintf(stderr, "vm exit[%d]\n", *pvcpu); 370e285ef8dSPeter Grehan fprintf(stderr, "\treason\t\tVMX\n"); 371e285ef8dSPeter Grehan fprintf(stderr, "\trip\t\t0x%016lx\n", vmexit->rip); 372e285ef8dSPeter Grehan fprintf(stderr, "\tinst_length\t%d\n", vmexit->inst_length); 373e285ef8dSPeter Grehan fprintf(stderr, "\terror\t\t%d\n", vmexit->u.vmx.error); 374e285ef8dSPeter Grehan fprintf(stderr, "\texit_reason\t%u\n", vmexit->u.vmx.exit_reason); 375e285ef8dSPeter Grehan fprintf(stderr, "\tqualification\t0x%016lx\n", 376e285ef8dSPeter Grehan vmexit->u.vmx.exit_qualification); 377e285ef8dSPeter Grehan 378e285ef8dSPeter Grehan return (VMEXIT_ABORT); 379e285ef8dSPeter Grehan } 380e285ef8dSPeter Grehan 381e285ef8dSPeter Grehan static int bogus_noswitch = 1; 382e285ef8dSPeter Grehan 383e285ef8dSPeter Grehan static int 384e285ef8dSPeter Grehan vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 385e285ef8dSPeter Grehan { 386e285ef8dSPeter Grehan stats.vmexit_bogus++; 387e285ef8dSPeter Grehan 388e285ef8dSPeter Grehan if (!guest_vcpu_mux || guest_ncpus == 1 || bogus_noswitch) { 389e285ef8dSPeter Grehan return (VMEXIT_RESTART); 390e285ef8dSPeter Grehan } else { 391e285ef8dSPeter Grehan stats.vmexit_bogus_switch++; 392e285ef8dSPeter Grehan vmexit->inst_length = 0; 393e285ef8dSPeter Grehan *pvcpu = -1; 394e285ef8dSPeter Grehan return (VMEXIT_SWITCH); 395e285ef8dSPeter Grehan } 396e285ef8dSPeter Grehan } 397e285ef8dSPeter Grehan 398e285ef8dSPeter Grehan static int 399e285ef8dSPeter Grehan vmexit_hlt(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 400e285ef8dSPeter Grehan { 401e285ef8dSPeter Grehan stats.vmexit_hlt++; 402e285ef8dSPeter Grehan if (fbsdrun_muxed()) { 403e285ef8dSPeter Grehan *pvcpu = -1; 404e285ef8dSPeter Grehan return (VMEXIT_SWITCH); 405e285ef8dSPeter Grehan } else { 406e285ef8dSPeter Grehan /* 407e285ef8dSPeter Grehan * Just continue execution with the next instruction. We use 408e285ef8dSPeter Grehan * the HLT VM exit as a way to be friendly with the host 409e285ef8dSPeter Grehan * scheduler. 410e285ef8dSPeter Grehan */ 411e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 412e285ef8dSPeter Grehan } 413e285ef8dSPeter Grehan } 414e285ef8dSPeter Grehan 415e285ef8dSPeter Grehan static int pause_noswitch; 416e285ef8dSPeter Grehan 417e285ef8dSPeter Grehan static int 418e285ef8dSPeter Grehan vmexit_pause(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 419e285ef8dSPeter Grehan { 420e285ef8dSPeter Grehan stats.vmexit_pause++; 421e285ef8dSPeter Grehan 422e285ef8dSPeter Grehan if (fbsdrun_muxed() && !pause_noswitch) { 423e285ef8dSPeter Grehan *pvcpu = -1; 424e285ef8dSPeter Grehan return (VMEXIT_SWITCH); 425e285ef8dSPeter Grehan } else { 426e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 427e285ef8dSPeter Grehan } 428e285ef8dSPeter Grehan } 429e285ef8dSPeter Grehan 430e285ef8dSPeter Grehan static int 431e285ef8dSPeter Grehan vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 432e285ef8dSPeter Grehan { 433e285ef8dSPeter Grehan stats.vmexit_mtrap++; 434e285ef8dSPeter Grehan 435e285ef8dSPeter Grehan return (VMEXIT_RESTART); 436e285ef8dSPeter Grehan } 437e285ef8dSPeter Grehan 438e285ef8dSPeter Grehan static int 439e285ef8dSPeter Grehan vmexit_paging(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 440e285ef8dSPeter Grehan { 441e285ef8dSPeter Grehan int err; 442e285ef8dSPeter Grehan stats.vmexit_paging++; 443e285ef8dSPeter Grehan 444e285ef8dSPeter Grehan err = emulate_mem(ctx, *pvcpu, vmexit->u.paging.gpa, 445e285ef8dSPeter Grehan &vmexit->u.paging.vie); 446e285ef8dSPeter Grehan 447e285ef8dSPeter Grehan if (err) { 448e285ef8dSPeter Grehan if (err == EINVAL) { 449e285ef8dSPeter Grehan fprintf(stderr, 450e285ef8dSPeter Grehan "Failed to emulate instruction at 0x%lx\n", 451e285ef8dSPeter Grehan vmexit->rip); 452e285ef8dSPeter Grehan } else if (err == ESRCH) { 453e285ef8dSPeter Grehan fprintf(stderr, "Unhandled memory access to 0x%lx\n", 454e285ef8dSPeter Grehan vmexit->u.paging.gpa); 455e285ef8dSPeter Grehan } 456e285ef8dSPeter Grehan 457e285ef8dSPeter Grehan return (VMEXIT_ABORT); 458e285ef8dSPeter Grehan } 459e285ef8dSPeter Grehan 460e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 461e285ef8dSPeter Grehan } 462e285ef8dSPeter Grehan 463e285ef8dSPeter Grehan static void 464e285ef8dSPeter Grehan sigalrm(int sig) 465e285ef8dSPeter Grehan { 466e285ef8dSPeter Grehan return; 467e285ef8dSPeter Grehan } 468e285ef8dSPeter Grehan 469e285ef8dSPeter Grehan static void 470e285ef8dSPeter Grehan setup_timeslice(void) 471e285ef8dSPeter Grehan { 472e285ef8dSPeter Grehan struct sigaction sa; 473e285ef8dSPeter Grehan struct itimerval itv; 474e285ef8dSPeter Grehan int error; 475e285ef8dSPeter Grehan 476e285ef8dSPeter Grehan /* 477e285ef8dSPeter Grehan * Setup a realtime timer to generate a SIGALRM at a 478e285ef8dSPeter Grehan * frequency of 'guest_tslice' ticks per second. 479e285ef8dSPeter Grehan */ 480e285ef8dSPeter Grehan sigemptyset(&sa.sa_mask); 481e285ef8dSPeter Grehan sa.sa_flags = 0; 482e285ef8dSPeter Grehan sa.sa_handler = sigalrm; 483e285ef8dSPeter Grehan 484e285ef8dSPeter Grehan error = sigaction(SIGALRM, &sa, NULL); 485e285ef8dSPeter Grehan assert(error == 0); 486e285ef8dSPeter Grehan 487e285ef8dSPeter Grehan itv.it_interval.tv_sec = 0; 488e285ef8dSPeter Grehan itv.it_interval.tv_usec = 1000000 / guest_tslice; 489e285ef8dSPeter Grehan itv.it_value.tv_sec = 0; 490e285ef8dSPeter Grehan itv.it_value.tv_usec = 1000000 / guest_tslice; 491e285ef8dSPeter Grehan 492e285ef8dSPeter Grehan error = setitimer(ITIMER_REAL, &itv, NULL); 493e285ef8dSPeter Grehan assert(error == 0); 494e285ef8dSPeter Grehan } 495e285ef8dSPeter Grehan 496e285ef8dSPeter Grehan static vmexit_handler_t handler[VM_EXITCODE_MAX] = { 497e285ef8dSPeter Grehan [VM_EXITCODE_INOUT] = vmexit_inout, 498e285ef8dSPeter Grehan [VM_EXITCODE_VMX] = vmexit_vmx, 499e285ef8dSPeter Grehan [VM_EXITCODE_BOGUS] = vmexit_bogus, 500e285ef8dSPeter Grehan [VM_EXITCODE_RDMSR] = vmexit_rdmsr, 501e285ef8dSPeter Grehan [VM_EXITCODE_WRMSR] = vmexit_wrmsr, 502e285ef8dSPeter Grehan [VM_EXITCODE_MTRAP] = vmexit_mtrap, 503e285ef8dSPeter Grehan [VM_EXITCODE_PAGING] = vmexit_paging, 504e285ef8dSPeter Grehan [VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap, 505e285ef8dSPeter Grehan }; 506e285ef8dSPeter Grehan 507e285ef8dSPeter Grehan static void 508e285ef8dSPeter Grehan vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip) 509e285ef8dSPeter Grehan { 510485b3300SNeel Natu cpuset_t mask; 511e285ef8dSPeter Grehan int error, rc, prevcpu; 512e285ef8dSPeter Grehan 513e285ef8dSPeter Grehan if (guest_vcpu_mux) 514e285ef8dSPeter Grehan setup_timeslice(); 515e285ef8dSPeter Grehan 516e285ef8dSPeter Grehan if (pincpu >= 0) { 517485b3300SNeel Natu CPU_ZERO(&mask); 518485b3300SNeel Natu CPU_SET(pincpu + vcpu, &mask); 519485b3300SNeel Natu error = pthread_setaffinity_np(pthread_self(), 520485b3300SNeel Natu sizeof(mask), &mask); 521e285ef8dSPeter Grehan assert(error == 0); 522e285ef8dSPeter Grehan } 523e285ef8dSPeter Grehan 524e285ef8dSPeter Grehan while (1) { 525e285ef8dSPeter Grehan error = vm_run(ctx, vcpu, rip, &vmexit[vcpu]); 526e285ef8dSPeter Grehan if (error != 0) { 527e285ef8dSPeter Grehan /* 528e285ef8dSPeter Grehan * It is possible that 'vmmctl' or some other process 529e285ef8dSPeter Grehan * has transitioned the vcpu to CANNOT_RUN state right 530e285ef8dSPeter Grehan * before we tried to transition it to RUNNING. 531e285ef8dSPeter Grehan * 532e285ef8dSPeter Grehan * This is expected to be temporary so just retry. 533e285ef8dSPeter Grehan */ 534e285ef8dSPeter Grehan if (errno == EBUSY) 535e285ef8dSPeter Grehan continue; 536e285ef8dSPeter Grehan else 537e285ef8dSPeter Grehan break; 538e285ef8dSPeter Grehan } 539e285ef8dSPeter Grehan 540e285ef8dSPeter Grehan prevcpu = vcpu; 541e285ef8dSPeter Grehan rc = (*handler[vmexit[vcpu].exitcode])(ctx, &vmexit[vcpu], 542e285ef8dSPeter Grehan &vcpu); 543e285ef8dSPeter Grehan switch (rc) { 544e285ef8dSPeter Grehan case VMEXIT_SWITCH: 545e285ef8dSPeter Grehan assert(guest_vcpu_mux); 546e285ef8dSPeter Grehan if (vcpu == -1) { 547e285ef8dSPeter Grehan stats.cpu_switch_rotate++; 548e285ef8dSPeter Grehan vcpu = fbsdrun_get_next_cpu(prevcpu); 549e285ef8dSPeter Grehan } else { 550e285ef8dSPeter Grehan stats.cpu_switch_direct++; 551e285ef8dSPeter Grehan } 552e285ef8dSPeter Grehan /* fall through */ 553e285ef8dSPeter Grehan case VMEXIT_CONTINUE: 554e285ef8dSPeter Grehan rip = vmexit[vcpu].rip + vmexit[vcpu].inst_length; 555e285ef8dSPeter Grehan break; 556e285ef8dSPeter Grehan case VMEXIT_RESTART: 557e285ef8dSPeter Grehan rip = vmexit[vcpu].rip; 558e285ef8dSPeter Grehan break; 559e285ef8dSPeter Grehan case VMEXIT_RESET: 560e285ef8dSPeter Grehan exit(0); 561e285ef8dSPeter Grehan default: 562e285ef8dSPeter Grehan exit(1); 563e285ef8dSPeter Grehan } 564e285ef8dSPeter Grehan } 565e285ef8dSPeter Grehan fprintf(stderr, "vm_run error %d, errno %d\n", error, errno); 566e285ef8dSPeter Grehan } 567e285ef8dSPeter Grehan 5685f0677d3SNeel Natu static int 5695f0677d3SNeel Natu num_vcpus_allowed(struct vmctx *ctx) 5705f0677d3SNeel Natu { 5715f0677d3SNeel Natu int tmp, error; 5725f0677d3SNeel Natu 5735f0677d3SNeel Natu error = vm_get_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, &tmp); 5745f0677d3SNeel Natu 5755f0677d3SNeel Natu /* 5765f0677d3SNeel Natu * The guest is allowed to spinup more than one processor only if the 5775f0677d3SNeel Natu * UNRESTRICTED_GUEST capability is available. 5785f0677d3SNeel Natu */ 5795f0677d3SNeel Natu if (error == 0) 5805f0677d3SNeel Natu return (VM_MAXCPU); 5815f0677d3SNeel Natu else 5825f0677d3SNeel Natu return (1); 5835f0677d3SNeel Natu } 584e285ef8dSPeter Grehan 585e285ef8dSPeter Grehan int 586e285ef8dSPeter Grehan main(int argc, char *argv[]) 587e285ef8dSPeter Grehan { 588e285ef8dSPeter Grehan int c, error, gdb_port, inject_bkpt, tmp, err, ioapic, bvmcons; 5895f0677d3SNeel Natu int max_vcpus; 590e285ef8dSPeter Grehan struct vmctx *ctx; 591e285ef8dSPeter Grehan uint64_t rip; 592b060ba50SNeel Natu size_t memsize; 593e285ef8dSPeter Grehan 594e285ef8dSPeter Grehan bvmcons = 0; 595e285ef8dSPeter Grehan inject_bkpt = 0; 596e285ef8dSPeter Grehan progname = basename(argv[0]); 597e285ef8dSPeter Grehan gdb_port = DEFAULT_GDB_PORT; 598e285ef8dSPeter Grehan guest_ncpus = 1; 599e285ef8dSPeter Grehan ioapic = 0; 600b060ba50SNeel Natu memsize = 256 * MB; 601e285ef8dSPeter Grehan 602b060ba50SNeel Natu while ((c = getopt(argc, argv, "abehABHIPxp:g:c:z:s:S:n:m:")) != -1) { 603e285ef8dSPeter Grehan switch (c) { 604e285ef8dSPeter Grehan case 'a': 605e285ef8dSPeter Grehan disable_x2apic = 1; 606e285ef8dSPeter Grehan break; 607e285ef8dSPeter Grehan case 'A': 608e285ef8dSPeter Grehan acpi = 1; 609e285ef8dSPeter Grehan break; 610e285ef8dSPeter Grehan case 'b': 611e285ef8dSPeter Grehan bvmcons = 1; 612e285ef8dSPeter Grehan break; 613e285ef8dSPeter Grehan case 'B': 614e285ef8dSPeter Grehan inject_bkpt = 1; 615e285ef8dSPeter Grehan break; 616e285ef8dSPeter Grehan case 'x': 617e285ef8dSPeter Grehan guest_vcpu_mux = 1; 618e285ef8dSPeter Grehan break; 619e285ef8dSPeter Grehan case 'p': 620e285ef8dSPeter Grehan pincpu = atoi(optarg); 621e285ef8dSPeter Grehan break; 622e285ef8dSPeter Grehan case 'c': 623e285ef8dSPeter Grehan guest_ncpus = atoi(optarg); 624e285ef8dSPeter Grehan break; 625e285ef8dSPeter Grehan case 'g': 626e285ef8dSPeter Grehan gdb_port = atoi(optarg); 627e285ef8dSPeter Grehan break; 628e285ef8dSPeter Grehan case 'z': 629e285ef8dSPeter Grehan guest_hz = atoi(optarg); 630e285ef8dSPeter Grehan break; 631e285ef8dSPeter Grehan case 't': 632e285ef8dSPeter Grehan guest_tslice = atoi(optarg); 633e285ef8dSPeter Grehan break; 634e285ef8dSPeter Grehan case 's': 635b05c77ffSNeel Natu if (pci_parse_slot(optarg, 0) != 0) 636b05c77ffSNeel Natu exit(1); 637b05c77ffSNeel Natu else 638e285ef8dSPeter Grehan break; 639e285ef8dSPeter Grehan case 'S': 640b05c77ffSNeel Natu if (pci_parse_slot(optarg, 1) != 0) 641b05c77ffSNeel Natu exit(1); 642b05c77ffSNeel Natu else 643e285ef8dSPeter Grehan break; 644e285ef8dSPeter Grehan case 'm': 645b060ba50SNeel Natu memsize = strtoul(optarg, NULL, 0) * MB; 646e285ef8dSPeter Grehan break; 647e285ef8dSPeter Grehan case 'H': 648e285ef8dSPeter Grehan guest_vmexit_on_hlt = 1; 649e285ef8dSPeter Grehan break; 650e285ef8dSPeter Grehan case 'I': 651e285ef8dSPeter Grehan ioapic = 1; 652e285ef8dSPeter Grehan break; 653e285ef8dSPeter Grehan case 'P': 654e285ef8dSPeter Grehan guest_vmexit_on_pause = 1; 655e285ef8dSPeter Grehan break; 656e285ef8dSPeter Grehan case 'e': 657e285ef8dSPeter Grehan strictio = 1; 658e285ef8dSPeter Grehan break; 659e285ef8dSPeter Grehan case 'h': 660e285ef8dSPeter Grehan usage(0); 661e285ef8dSPeter Grehan default: 662e285ef8dSPeter Grehan usage(1); 663e285ef8dSPeter Grehan } 664e285ef8dSPeter Grehan } 665e285ef8dSPeter Grehan argc -= optind; 666e285ef8dSPeter Grehan argv += optind; 667e285ef8dSPeter Grehan 668e285ef8dSPeter Grehan if (argc != 1) 669e285ef8dSPeter Grehan usage(1); 670e285ef8dSPeter Grehan 671e285ef8dSPeter Grehan /* No need to mux if guest is uni-processor */ 672e285ef8dSPeter Grehan if (guest_ncpus <= 1) 673e285ef8dSPeter Grehan guest_vcpu_mux = 0; 674e285ef8dSPeter Grehan 675e285ef8dSPeter Grehan /* vmexit on hlt if guest is muxed */ 676e285ef8dSPeter Grehan if (guest_vcpu_mux) { 677e285ef8dSPeter Grehan guest_vmexit_on_hlt = 1; 678e285ef8dSPeter Grehan guest_vmexit_on_pause = 1; 679e285ef8dSPeter Grehan } 680e285ef8dSPeter Grehan 681e285ef8dSPeter Grehan vmname = argv[0]; 682e285ef8dSPeter Grehan 683e285ef8dSPeter Grehan ctx = vm_open(vmname); 684e285ef8dSPeter Grehan if (ctx == NULL) { 685e285ef8dSPeter Grehan perror("vm_open"); 686e285ef8dSPeter Grehan exit(1); 687e285ef8dSPeter Grehan } 688e285ef8dSPeter Grehan 6895f0677d3SNeel Natu max_vcpus = num_vcpus_allowed(ctx); 6905f0677d3SNeel Natu if (guest_ncpus > max_vcpus) { 6915f0677d3SNeel Natu fprintf(stderr, "%d vCPUs requested but only %d available\n", 6925f0677d3SNeel Natu guest_ncpus, max_vcpus); 6935f0677d3SNeel Natu exit(1); 6945f0677d3SNeel Natu } 6955f0677d3SNeel Natu 696e285ef8dSPeter Grehan if (fbsdrun_vmexit_on_hlt()) { 697e285ef8dSPeter Grehan err = vm_get_capability(ctx, BSP, VM_CAP_HALT_EXIT, &tmp); 698e285ef8dSPeter Grehan if (err < 0) { 699e285ef8dSPeter Grehan fprintf(stderr, "VM exit on HLT not supported\n"); 700e285ef8dSPeter Grehan exit(1); 701e285ef8dSPeter Grehan } 702e285ef8dSPeter Grehan vm_set_capability(ctx, BSP, VM_CAP_HALT_EXIT, 1); 703e285ef8dSPeter Grehan handler[VM_EXITCODE_HLT] = vmexit_hlt; 704e285ef8dSPeter Grehan } 705e285ef8dSPeter Grehan 706e285ef8dSPeter Grehan if (fbsdrun_vmexit_on_pause()) { 707e285ef8dSPeter Grehan /* 708e285ef8dSPeter Grehan * pause exit support required for this mode 709e285ef8dSPeter Grehan */ 710e285ef8dSPeter Grehan err = vm_get_capability(ctx, BSP, VM_CAP_PAUSE_EXIT, &tmp); 711e285ef8dSPeter Grehan if (err < 0) { 712e285ef8dSPeter Grehan fprintf(stderr, 713e285ef8dSPeter Grehan "SMP mux requested, no pause support\n"); 714e285ef8dSPeter Grehan exit(1); 715e285ef8dSPeter Grehan } 716e285ef8dSPeter Grehan vm_set_capability(ctx, BSP, VM_CAP_PAUSE_EXIT, 1); 717e285ef8dSPeter Grehan handler[VM_EXITCODE_PAUSE] = vmexit_pause; 718e285ef8dSPeter Grehan } 719e285ef8dSPeter Grehan 720e285ef8dSPeter Grehan if (fbsdrun_disable_x2apic()) 721e285ef8dSPeter Grehan err = vm_set_x2apic_state(ctx, BSP, X2APIC_DISABLED); 722e285ef8dSPeter Grehan else 723e285ef8dSPeter Grehan err = vm_set_x2apic_state(ctx, BSP, X2APIC_ENABLED); 724e285ef8dSPeter Grehan 725e285ef8dSPeter Grehan if (err) { 726e285ef8dSPeter Grehan fprintf(stderr, "Unable to set x2apic state (%d)\n", err); 727e285ef8dSPeter Grehan exit(1); 728e285ef8dSPeter Grehan } 729e285ef8dSPeter Grehan 730b060ba50SNeel Natu err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); 731b060ba50SNeel Natu if (err) { 732b060ba50SNeel Natu fprintf(stderr, "Unable to setup memory (%d)\n", err); 733b060ba50SNeel Natu exit(1); 734e285ef8dSPeter Grehan } 735e285ef8dSPeter Grehan 7360e2ca4e6SNeel Natu init_mem(); 737e285ef8dSPeter Grehan init_inout(); 738a38e2a64SPeter Grehan 739*9d6be09fSPeter Grehan rtc_init(ctx); 740*9d6be09fSPeter Grehan 741a38e2a64SPeter Grehan /* 742a38e2a64SPeter Grehan * Exit if a device emulation finds an error in it's initilization 743a38e2a64SPeter Grehan */ 744a38e2a64SPeter Grehan if (init_pci(ctx) != 0) 745a38e2a64SPeter Grehan exit(1); 746a38e2a64SPeter Grehan 747e285ef8dSPeter Grehan if (ioapic) 748e285ef8dSPeter Grehan ioapic_init(0); 749e285ef8dSPeter Grehan 750e285ef8dSPeter Grehan if (gdb_port != 0) 751e285ef8dSPeter Grehan init_dbgport(gdb_port); 752e285ef8dSPeter Grehan 753e285ef8dSPeter Grehan if (bvmcons) 754e285ef8dSPeter Grehan init_bvmcons(); 755e285ef8dSPeter Grehan 756e285ef8dSPeter Grehan error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip); 757e285ef8dSPeter Grehan assert(error == 0); 758e285ef8dSPeter Grehan 759e285ef8dSPeter Grehan if (inject_bkpt) { 760e285ef8dSPeter Grehan error = vm_inject_event(ctx, BSP, VM_HW_EXCEPTION, IDT_BP); 761e285ef8dSPeter Grehan assert(error == 0); 762e285ef8dSPeter Grehan } 763e285ef8dSPeter Grehan 764e285ef8dSPeter Grehan /* 765e285ef8dSPeter Grehan * build the guest tables, MP etc. 766e285ef8dSPeter Grehan */ 767e285ef8dSPeter Grehan mptable_build(ctx, guest_ncpus, ioapic); 768e285ef8dSPeter Grehan 769e285ef8dSPeter Grehan if (acpi) { 770e285ef8dSPeter Grehan error = acpi_build(ctx, guest_ncpus, ioapic); 771e285ef8dSPeter Grehan assert(error == 0); 772e285ef8dSPeter Grehan } 773e285ef8dSPeter Grehan 774e285ef8dSPeter Grehan /* 775e285ef8dSPeter Grehan * Add CPU 0 776e285ef8dSPeter Grehan */ 777e285ef8dSPeter Grehan fbsdrun_addcpu(ctx, BSP, rip); 778e285ef8dSPeter Grehan 779e285ef8dSPeter Grehan /* 780e285ef8dSPeter Grehan * Head off to the main event dispatch loop 781e285ef8dSPeter Grehan */ 782e285ef8dSPeter Grehan mevent_dispatch(); 783e285ef8dSPeter Grehan 784e285ef8dSPeter Grehan exit(1); 785e285ef8dSPeter Grehan } 786