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 361c052192SNeel Natu #include <machine/atomic.h> 37e285ef8dSPeter Grehan #include <machine/segments.h> 38e285ef8dSPeter Grehan 39e285ef8dSPeter Grehan #include <stdio.h> 40e285ef8dSPeter Grehan #include <stdlib.h> 41b5331f4dSNeel Natu #include <string.h> 42200758f1SNeel Natu #include <err.h> 43e285ef8dSPeter Grehan #include <libgen.h> 44e285ef8dSPeter Grehan #include <unistd.h> 45e285ef8dSPeter Grehan #include <assert.h> 46e285ef8dSPeter Grehan #include <errno.h> 47e285ef8dSPeter Grehan #include <pthread.h> 48e285ef8dSPeter Grehan #include <pthread_np.h> 49200758f1SNeel Natu #include <sysexits.h> 50e285ef8dSPeter Grehan 51e285ef8dSPeter Grehan #include <machine/vmm.h> 52e285ef8dSPeter Grehan #include <vmmapi.h> 53e285ef8dSPeter Grehan 54e285ef8dSPeter Grehan #include "bhyverun.h" 55e285ef8dSPeter Grehan #include "acpi.h" 56e285ef8dSPeter Grehan #include "inout.h" 57e285ef8dSPeter Grehan #include "dbgport.h" 58ea7f1c8cSNeel Natu #include "legacy_irq.h" 59e285ef8dSPeter Grehan #include "mem.h" 60e285ef8dSPeter Grehan #include "mevent.h" 61e285ef8dSPeter Grehan #include "mptbl.h" 62e285ef8dSPeter Grehan #include "pci_emul.h" 63ea7f1c8cSNeel Natu #include "pci_lpc.h" 64e285ef8dSPeter Grehan #include "xmsr.h" 65e285ef8dSPeter Grehan #include "spinup_ap.h" 669d6be09fSPeter Grehan #include "rtc.h" 67e285ef8dSPeter Grehan 68e285ef8dSPeter Grehan #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ 69e285ef8dSPeter Grehan 70e285ef8dSPeter Grehan #define VMEXIT_SWITCH 0 /* force vcpu switch in mux mode */ 71e285ef8dSPeter Grehan #define VMEXIT_CONTINUE 1 /* continue from next instruction */ 72e285ef8dSPeter Grehan #define VMEXIT_RESTART 2 /* restart current instruction */ 73e285ef8dSPeter Grehan #define VMEXIT_ABORT 3 /* abort the vm run loop */ 74e285ef8dSPeter Grehan #define VMEXIT_RESET 4 /* guest machine has reset */ 75e285ef8dSPeter Grehan 76e285ef8dSPeter Grehan #define MB (1024UL * 1024) 77e285ef8dSPeter Grehan #define GB (1024UL * MB) 78e285ef8dSPeter Grehan 79e285ef8dSPeter Grehan typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); 80e285ef8dSPeter Grehan 81e285ef8dSPeter Grehan char *vmname; 82e285ef8dSPeter Grehan 83e285ef8dSPeter Grehan int guest_ncpus; 84e285ef8dSPeter Grehan 85e285ef8dSPeter Grehan static int pincpu = -1; 86e285ef8dSPeter Grehan static int guest_vmexit_on_hlt, guest_vmexit_on_pause, disable_x2apic; 87062b878fSPeter Grehan static int virtio_msix = 1; 88e285ef8dSPeter Grehan 89e285ef8dSPeter Grehan static int strictio; 90851d84f1SNeel Natu static int strictmsr = 1; 91e285ef8dSPeter Grehan 92e285ef8dSPeter Grehan static int acpi; 93e285ef8dSPeter Grehan 94e285ef8dSPeter Grehan static char *progname; 95e285ef8dSPeter Grehan static const int BSP = 0; 96e285ef8dSPeter Grehan 97e285ef8dSPeter Grehan static int cpumask; 98e285ef8dSPeter Grehan 99e285ef8dSPeter Grehan static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip); 100e285ef8dSPeter Grehan 101e285ef8dSPeter Grehan struct vm_exit vmexit[VM_MAXCPU]; 102e285ef8dSPeter Grehan 10394c3b3bfSPeter Grehan struct bhyvestats { 104e285ef8dSPeter Grehan uint64_t vmexit_bogus; 105e285ef8dSPeter Grehan uint64_t vmexit_bogus_switch; 106e285ef8dSPeter Grehan uint64_t vmexit_hlt; 107e285ef8dSPeter Grehan uint64_t vmexit_pause; 108e285ef8dSPeter Grehan uint64_t vmexit_mtrap; 109318224bbSNeel Natu uint64_t vmexit_inst_emul; 110e285ef8dSPeter Grehan uint64_t cpu_switch_rotate; 111e285ef8dSPeter Grehan uint64_t cpu_switch_direct; 112e285ef8dSPeter Grehan int io_reset; 113e285ef8dSPeter Grehan } stats; 114e285ef8dSPeter Grehan 115e285ef8dSPeter Grehan struct mt_vmm_info { 116e285ef8dSPeter Grehan pthread_t mt_thr; 117e285ef8dSPeter Grehan struct vmctx *mt_ctx; 118e285ef8dSPeter Grehan int mt_vcpu; 119e285ef8dSPeter Grehan } mt_vmm_info[VM_MAXCPU]; 120e285ef8dSPeter Grehan 121e285ef8dSPeter Grehan static void 122e285ef8dSPeter Grehan usage(int code) 123e285ef8dSPeter Grehan { 124e285ef8dSPeter Grehan 125e285ef8dSPeter Grehan fprintf(stderr, 126851d84f1SNeel Natu "Usage: %s [-aehwAHIPW] [-g <gdb port>] [-s <pci>] [-S <pci>]\n" 127ea7f1c8cSNeel Natu " %*s [-c vcpus] [-p pincpu] [-m mem] [-l <lpc>] <vm>\n" 128e285ef8dSPeter Grehan " -a: local apic is in XAPIC mode (default is X2APIC)\n" 129e285ef8dSPeter Grehan " -A: create an ACPI table\n" 1304a06a0feSNeel Natu " -g: gdb port\n" 131e285ef8dSPeter Grehan " -c: # cpus (default 1)\n" 132e285ef8dSPeter Grehan " -p: pin vcpu 'n' to host cpu 'pincpu + n'\n" 133e285ef8dSPeter Grehan " -H: vmexit from the guest on hlt\n" 134e285ef8dSPeter Grehan " -P: vmexit from the guest on pause\n" 135062b878fSPeter Grehan " -W: force virtio to use single-vector MSI\n" 136b5331f4dSNeel Natu " -e: exit on unhandled I/O access\n" 137e285ef8dSPeter Grehan " -h: help\n" 138e285ef8dSPeter Grehan " -s: <slot,driver,configinfo> PCI slot config\n" 139e285ef8dSPeter Grehan " -S: <slot,driver,configinfo> legacy PCI slot config\n" 140ea7f1c8cSNeel Natu " -l: LPC device configuration\n" 141851d84f1SNeel Natu " -m: memory size in MB\n" 142851d84f1SNeel Natu " -w: ignore unimplemented MSRs\n", 143b5331f4dSNeel Natu progname, (int)strlen(progname), ""); 14494c3b3bfSPeter Grehan 145e285ef8dSPeter Grehan exit(code); 146e285ef8dSPeter Grehan } 147e285ef8dSPeter Grehan 148e285ef8dSPeter Grehan void * 149b060ba50SNeel Natu paddr_guest2host(struct vmctx *ctx, uintptr_t gaddr, size_t len) 150e285ef8dSPeter Grehan { 151e285ef8dSPeter Grehan 152b060ba50SNeel Natu return (vm_map_gpa(ctx, gaddr, len)); 153e285ef8dSPeter Grehan } 154e285ef8dSPeter Grehan 155e285ef8dSPeter Grehan int 156e285ef8dSPeter Grehan fbsdrun_disable_x2apic(void) 157e285ef8dSPeter Grehan { 158e285ef8dSPeter Grehan 159e285ef8dSPeter Grehan return (disable_x2apic); 160e285ef8dSPeter Grehan } 161e285ef8dSPeter Grehan 162e285ef8dSPeter Grehan int 163e285ef8dSPeter Grehan fbsdrun_vmexit_on_pause(void) 164e285ef8dSPeter Grehan { 165e285ef8dSPeter Grehan 166e285ef8dSPeter Grehan return (guest_vmexit_on_pause); 167e285ef8dSPeter Grehan } 168e285ef8dSPeter Grehan 169e285ef8dSPeter Grehan int 170e285ef8dSPeter Grehan fbsdrun_vmexit_on_hlt(void) 171e285ef8dSPeter Grehan { 172e285ef8dSPeter Grehan 173e285ef8dSPeter Grehan return (guest_vmexit_on_hlt); 174e285ef8dSPeter Grehan } 175e285ef8dSPeter Grehan 176062b878fSPeter Grehan int 177062b878fSPeter Grehan fbsdrun_virtio_msix(void) 178062b878fSPeter Grehan { 179062b878fSPeter Grehan 180062b878fSPeter Grehan return (virtio_msix); 181062b878fSPeter Grehan } 182062b878fSPeter Grehan 183e285ef8dSPeter Grehan static void * 184e285ef8dSPeter Grehan fbsdrun_start_thread(void *param) 185e285ef8dSPeter Grehan { 186e285ef8dSPeter Grehan char tname[MAXCOMLEN + 1]; 187e285ef8dSPeter Grehan struct mt_vmm_info *mtp; 188e285ef8dSPeter Grehan int vcpu; 189e285ef8dSPeter Grehan 190e285ef8dSPeter Grehan mtp = param; 191e285ef8dSPeter Grehan vcpu = mtp->mt_vcpu; 192e285ef8dSPeter Grehan 1937f5487acSPeter Grehan snprintf(tname, sizeof(tname), "vcpu %d", vcpu); 194e285ef8dSPeter Grehan pthread_set_name_np(mtp->mt_thr, tname); 195e285ef8dSPeter Grehan 196e285ef8dSPeter Grehan vm_loop(mtp->mt_ctx, vcpu, vmexit[vcpu].rip); 197e285ef8dSPeter Grehan 198e285ef8dSPeter Grehan /* not reached */ 199e285ef8dSPeter Grehan exit(1); 200e285ef8dSPeter Grehan return (NULL); 201e285ef8dSPeter Grehan } 202e285ef8dSPeter Grehan 203e285ef8dSPeter Grehan void 204e285ef8dSPeter Grehan fbsdrun_addcpu(struct vmctx *ctx, int vcpu, uint64_t rip) 205e285ef8dSPeter Grehan { 206e285ef8dSPeter Grehan int error; 207e285ef8dSPeter Grehan 208e285ef8dSPeter Grehan if (cpumask & (1 << vcpu)) { 209e285ef8dSPeter Grehan fprintf(stderr, "addcpu: attempting to add existing cpu %d\n", 210e285ef8dSPeter Grehan vcpu); 211e285ef8dSPeter Grehan exit(1); 212e285ef8dSPeter Grehan } 213e285ef8dSPeter Grehan 2141c052192SNeel Natu atomic_set_int(&cpumask, 1 << vcpu); 215e285ef8dSPeter Grehan 216e285ef8dSPeter Grehan /* 217e285ef8dSPeter Grehan * Set up the vmexit struct to allow execution to start 218e285ef8dSPeter Grehan * at the given RIP 219e285ef8dSPeter Grehan */ 220e285ef8dSPeter Grehan vmexit[vcpu].rip = rip; 221e285ef8dSPeter Grehan vmexit[vcpu].inst_length = 0; 222e285ef8dSPeter Grehan 223e285ef8dSPeter Grehan mt_vmm_info[vcpu].mt_ctx = ctx; 224e285ef8dSPeter Grehan mt_vmm_info[vcpu].mt_vcpu = vcpu; 225e285ef8dSPeter Grehan 226e285ef8dSPeter Grehan error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL, 227e285ef8dSPeter Grehan fbsdrun_start_thread, &mt_vmm_info[vcpu]); 228e285ef8dSPeter Grehan assert(error == 0); 229e285ef8dSPeter Grehan } 230e285ef8dSPeter Grehan 231e285ef8dSPeter Grehan static int 2321c052192SNeel Natu fbsdrun_deletecpu(struct vmctx *ctx, int vcpu) 2331c052192SNeel Natu { 2341c052192SNeel Natu 2351c052192SNeel Natu if ((cpumask & (1 << vcpu)) == 0) { 2361c052192SNeel Natu fprintf(stderr, "addcpu: attempting to delete unknown cpu %d\n", 2371c052192SNeel Natu vcpu); 2381c052192SNeel Natu exit(1); 2391c052192SNeel Natu } 2401c052192SNeel Natu 2411c052192SNeel Natu atomic_clear_int(&cpumask, 1 << vcpu); 2421c052192SNeel Natu return (cpumask == 0); 2431c052192SNeel Natu } 2441c052192SNeel Natu 2451c052192SNeel Natu static int 246e285ef8dSPeter Grehan vmexit_catch_reset(void) 247e285ef8dSPeter Grehan { 248e285ef8dSPeter Grehan stats.io_reset++; 249e285ef8dSPeter Grehan return (VMEXIT_RESET); 250e285ef8dSPeter Grehan } 251e285ef8dSPeter Grehan 252e285ef8dSPeter Grehan static int 253e285ef8dSPeter Grehan vmexit_catch_inout(void) 254e285ef8dSPeter Grehan { 255e285ef8dSPeter Grehan return (VMEXIT_ABORT); 256e285ef8dSPeter Grehan } 257e285ef8dSPeter Grehan 258e285ef8dSPeter Grehan static int 259e285ef8dSPeter Grehan vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu, 260e285ef8dSPeter Grehan uint32_t eax) 261e285ef8dSPeter Grehan { 26294c3b3bfSPeter Grehan #if BHYVE_DEBUG 26394c3b3bfSPeter Grehan /* 26494c3b3bfSPeter Grehan * put guest-driven debug here 26594c3b3bfSPeter Grehan */ 266e285ef8dSPeter Grehan #endif 267e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 268e285ef8dSPeter Grehan } 269e285ef8dSPeter Grehan 270e285ef8dSPeter Grehan static int 271e285ef8dSPeter Grehan vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 272e285ef8dSPeter Grehan { 273e285ef8dSPeter Grehan int error; 274e285ef8dSPeter Grehan int bytes, port, in, out; 275e285ef8dSPeter Grehan uint32_t eax; 276e285ef8dSPeter Grehan int vcpu; 277e285ef8dSPeter Grehan 278e285ef8dSPeter Grehan vcpu = *pvcpu; 279e285ef8dSPeter Grehan 280e285ef8dSPeter Grehan port = vme->u.inout.port; 281e285ef8dSPeter Grehan bytes = vme->u.inout.bytes; 282e285ef8dSPeter Grehan eax = vme->u.inout.eax; 283e285ef8dSPeter Grehan in = vme->u.inout.in; 284e285ef8dSPeter Grehan out = !in; 285e285ef8dSPeter Grehan 286e285ef8dSPeter Grehan /* We don't deal with these */ 287e285ef8dSPeter Grehan if (vme->u.inout.string || vme->u.inout.rep) 288e285ef8dSPeter Grehan return (VMEXIT_ABORT); 289e285ef8dSPeter Grehan 290e285ef8dSPeter Grehan /* Special case of guest reset */ 291e285ef8dSPeter Grehan if (out && port == 0x64 && (uint8_t)eax == 0xFE) 292e285ef8dSPeter Grehan return (vmexit_catch_reset()); 293e285ef8dSPeter Grehan 294e285ef8dSPeter Grehan /* Extra-special case of host notifications */ 295e285ef8dSPeter Grehan if (out && port == GUEST_NIO_PORT) 296e285ef8dSPeter Grehan return (vmexit_handle_notify(ctx, vme, pvcpu, eax)); 297e285ef8dSPeter Grehan 298e285ef8dSPeter Grehan error = emulate_inout(ctx, vcpu, in, port, bytes, &eax, strictio); 299e285ef8dSPeter Grehan if (error == 0 && in) 300e285ef8dSPeter Grehan error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, eax); 301e285ef8dSPeter Grehan 302e285ef8dSPeter Grehan if (error == 0) 303e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 304e285ef8dSPeter Grehan else { 305e285ef8dSPeter Grehan fprintf(stderr, "Unhandled %s%c 0x%04x\n", 306e285ef8dSPeter Grehan in ? "in" : "out", 307e285ef8dSPeter Grehan bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), port); 308e285ef8dSPeter Grehan return (vmexit_catch_inout()); 309e285ef8dSPeter Grehan } 310e285ef8dSPeter Grehan } 311e285ef8dSPeter Grehan 312e285ef8dSPeter Grehan static int 313e285ef8dSPeter Grehan vmexit_rdmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 314e285ef8dSPeter Grehan { 315851d84f1SNeel Natu uint64_t val; 316851d84f1SNeel Natu uint32_t eax, edx; 317851d84f1SNeel Natu int error; 318851d84f1SNeel Natu 319851d84f1SNeel Natu val = 0; 320851d84f1SNeel Natu error = emulate_rdmsr(ctx, *pvcpu, vme->u.msr.code, &val); 321851d84f1SNeel Natu if (error != 0) { 322851d84f1SNeel Natu fprintf(stderr, "rdmsr to register %#x on vcpu %d\n", 323851d84f1SNeel Natu vme->u.msr.code, *pvcpu); 324851d84f1SNeel Natu if (strictmsr) 325e285ef8dSPeter Grehan return (VMEXIT_ABORT); 326e285ef8dSPeter Grehan } 327e285ef8dSPeter Grehan 328851d84f1SNeel Natu eax = val; 329851d84f1SNeel Natu error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RAX, eax); 330851d84f1SNeel Natu assert(error == 0); 331851d84f1SNeel Natu 332851d84f1SNeel Natu edx = val >> 32; 333851d84f1SNeel Natu error = vm_set_register(ctx, *pvcpu, VM_REG_GUEST_RDX, edx); 334851d84f1SNeel Natu assert(error == 0); 335851d84f1SNeel Natu 336851d84f1SNeel Natu return (VMEXIT_CONTINUE); 337851d84f1SNeel Natu } 338851d84f1SNeel Natu 339e285ef8dSPeter Grehan static int 340e285ef8dSPeter Grehan vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 341e285ef8dSPeter Grehan { 342851d84f1SNeel Natu int error; 343e285ef8dSPeter Grehan 344851d84f1SNeel Natu error = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code, vme->u.msr.wval); 345851d84f1SNeel Natu if (error != 0) { 346851d84f1SNeel Natu fprintf(stderr, "wrmsr to register %#x(%#lx) on vcpu %d\n", 347851d84f1SNeel Natu vme->u.msr.code, vme->u.msr.wval, *pvcpu); 348851d84f1SNeel Natu if (strictmsr) 349851d84f1SNeel Natu return (VMEXIT_ABORT); 350851d84f1SNeel Natu } 351851d84f1SNeel Natu return (VMEXIT_CONTINUE); 352e285ef8dSPeter Grehan } 353e285ef8dSPeter Grehan 354e285ef8dSPeter Grehan static int 355e285ef8dSPeter Grehan vmexit_spinup_ap(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 356e285ef8dSPeter Grehan { 357e285ef8dSPeter Grehan int newcpu; 358e285ef8dSPeter Grehan int retval = VMEXIT_CONTINUE; 359e285ef8dSPeter Grehan 360e285ef8dSPeter Grehan newcpu = spinup_ap(ctx, *pvcpu, 361e285ef8dSPeter Grehan vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip); 362e285ef8dSPeter Grehan 363e285ef8dSPeter Grehan return (retval); 364e285ef8dSPeter Grehan } 365e285ef8dSPeter Grehan 366e285ef8dSPeter Grehan static int 3671c052192SNeel Natu vmexit_spindown_cpu(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) 3681c052192SNeel Natu { 3691c052192SNeel Natu int lastcpu; 3701c052192SNeel Natu 3711c052192SNeel Natu lastcpu = fbsdrun_deletecpu(ctx, *pvcpu); 3721c052192SNeel Natu if (!lastcpu) 3731c052192SNeel Natu pthread_exit(NULL); 3741c052192SNeel Natu return (vmexit_catch_reset()); 3751c052192SNeel Natu } 3761c052192SNeel Natu 3771c052192SNeel Natu static int 378e285ef8dSPeter Grehan vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 379e285ef8dSPeter Grehan { 380e285ef8dSPeter Grehan 381e285ef8dSPeter Grehan fprintf(stderr, "vm exit[%d]\n", *pvcpu); 382e285ef8dSPeter Grehan fprintf(stderr, "\treason\t\tVMX\n"); 383e285ef8dSPeter Grehan fprintf(stderr, "\trip\t\t0x%016lx\n", vmexit->rip); 384e285ef8dSPeter Grehan fprintf(stderr, "\tinst_length\t%d\n", vmexit->inst_length); 385e285ef8dSPeter Grehan fprintf(stderr, "\terror\t\t%d\n", vmexit->u.vmx.error); 386e285ef8dSPeter Grehan fprintf(stderr, "\texit_reason\t%u\n", vmexit->u.vmx.exit_reason); 387e285ef8dSPeter Grehan fprintf(stderr, "\tqualification\t0x%016lx\n", 388e285ef8dSPeter Grehan vmexit->u.vmx.exit_qualification); 389e285ef8dSPeter Grehan 390e285ef8dSPeter Grehan return (VMEXIT_ABORT); 391e285ef8dSPeter Grehan } 392e285ef8dSPeter Grehan 393e285ef8dSPeter Grehan static int 394e285ef8dSPeter Grehan vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 395e285ef8dSPeter Grehan { 39694c3b3bfSPeter Grehan 397e285ef8dSPeter Grehan stats.vmexit_bogus++; 398e285ef8dSPeter Grehan 399e285ef8dSPeter Grehan return (VMEXIT_RESTART); 400e285ef8dSPeter Grehan } 401e285ef8dSPeter Grehan 402e285ef8dSPeter Grehan static int 403e285ef8dSPeter Grehan vmexit_hlt(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 404e285ef8dSPeter Grehan { 40594c3b3bfSPeter Grehan 406e285ef8dSPeter Grehan stats.vmexit_hlt++; 40794c3b3bfSPeter Grehan 408e285ef8dSPeter Grehan /* 409e285ef8dSPeter Grehan * Just continue execution with the next instruction. We use 410e285ef8dSPeter Grehan * the HLT VM exit as a way to be friendly with the host 411e285ef8dSPeter Grehan * scheduler. 412e285ef8dSPeter Grehan */ 413e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 414e285ef8dSPeter Grehan } 415e285ef8dSPeter Grehan 416e285ef8dSPeter Grehan static int 417e285ef8dSPeter Grehan vmexit_pause(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 418e285ef8dSPeter Grehan { 41994c3b3bfSPeter Grehan 420e285ef8dSPeter Grehan stats.vmexit_pause++; 421e285ef8dSPeter Grehan 422e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 423e285ef8dSPeter Grehan } 424e285ef8dSPeter Grehan 425e285ef8dSPeter Grehan static int 426e285ef8dSPeter Grehan vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 427e285ef8dSPeter Grehan { 42894c3b3bfSPeter Grehan 429e285ef8dSPeter Grehan stats.vmexit_mtrap++; 430e285ef8dSPeter Grehan 431e285ef8dSPeter Grehan return (VMEXIT_RESTART); 432e285ef8dSPeter Grehan } 433e285ef8dSPeter Grehan 434e285ef8dSPeter Grehan static int 435318224bbSNeel Natu vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) 436e285ef8dSPeter Grehan { 437e285ef8dSPeter Grehan int err; 438318224bbSNeel Natu stats.vmexit_inst_emul++; 439e285ef8dSPeter Grehan 440318224bbSNeel Natu err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa, 441318224bbSNeel Natu &vmexit->u.inst_emul.vie); 442e285ef8dSPeter Grehan 443e285ef8dSPeter Grehan if (err) { 444e285ef8dSPeter Grehan if (err == EINVAL) { 445e285ef8dSPeter Grehan fprintf(stderr, 446e285ef8dSPeter Grehan "Failed to emulate instruction at 0x%lx\n", 447e285ef8dSPeter Grehan vmexit->rip); 448e285ef8dSPeter Grehan } else if (err == ESRCH) { 449e285ef8dSPeter Grehan fprintf(stderr, "Unhandled memory access to 0x%lx\n", 450318224bbSNeel Natu vmexit->u.inst_emul.gpa); 451e285ef8dSPeter Grehan } 452e285ef8dSPeter Grehan 453e285ef8dSPeter Grehan return (VMEXIT_ABORT); 454e285ef8dSPeter Grehan } 455e285ef8dSPeter Grehan 456e285ef8dSPeter Grehan return (VMEXIT_CONTINUE); 457e285ef8dSPeter Grehan } 458e285ef8dSPeter Grehan 459e285ef8dSPeter Grehan static vmexit_handler_t handler[VM_EXITCODE_MAX] = { 460e285ef8dSPeter Grehan [VM_EXITCODE_INOUT] = vmexit_inout, 461e285ef8dSPeter Grehan [VM_EXITCODE_VMX] = vmexit_vmx, 462e285ef8dSPeter Grehan [VM_EXITCODE_BOGUS] = vmexit_bogus, 463e285ef8dSPeter Grehan [VM_EXITCODE_RDMSR] = vmexit_rdmsr, 464e285ef8dSPeter Grehan [VM_EXITCODE_WRMSR] = vmexit_wrmsr, 465e285ef8dSPeter Grehan [VM_EXITCODE_MTRAP] = vmexit_mtrap, 466318224bbSNeel Natu [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, 467e285ef8dSPeter Grehan [VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap, 4681c052192SNeel Natu [VM_EXITCODE_SPINDOWN_CPU] = vmexit_spindown_cpu, 469e285ef8dSPeter Grehan }; 470e285ef8dSPeter Grehan 471e285ef8dSPeter Grehan static void 472e285ef8dSPeter Grehan vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip) 473e285ef8dSPeter Grehan { 474485b3300SNeel Natu cpuset_t mask; 475e285ef8dSPeter Grehan int error, rc, prevcpu; 4768b271170SPeter Grehan enum vm_exitcode exitcode; 477e285ef8dSPeter Grehan 478e285ef8dSPeter Grehan if (pincpu >= 0) { 479485b3300SNeel Natu CPU_ZERO(&mask); 480485b3300SNeel Natu CPU_SET(pincpu + vcpu, &mask); 481485b3300SNeel Natu error = pthread_setaffinity_np(pthread_self(), 482485b3300SNeel Natu sizeof(mask), &mask); 483e285ef8dSPeter Grehan assert(error == 0); 484e285ef8dSPeter Grehan } 485e285ef8dSPeter Grehan 486e285ef8dSPeter Grehan while (1) { 487e285ef8dSPeter Grehan error = vm_run(ctx, vcpu, rip, &vmexit[vcpu]); 488*f80330a8SNeel Natu if (error != 0) 489e285ef8dSPeter Grehan break; 490e285ef8dSPeter Grehan 491e285ef8dSPeter Grehan prevcpu = vcpu; 4928b271170SPeter Grehan 4938b271170SPeter Grehan exitcode = vmexit[vcpu].exitcode; 4948b271170SPeter Grehan if (exitcode >= VM_EXITCODE_MAX || handler[exitcode] == NULL) { 4958b271170SPeter Grehan fprintf(stderr, "vm_loop: unexpected exitcode 0x%x\n", 4968b271170SPeter Grehan exitcode); 4978b271170SPeter Grehan exit(1); 4988b271170SPeter Grehan } 4998b271170SPeter Grehan 5008b271170SPeter Grehan rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu); 5018b271170SPeter Grehan 502e285ef8dSPeter Grehan switch (rc) { 503e285ef8dSPeter Grehan case VMEXIT_CONTINUE: 504e285ef8dSPeter Grehan rip = vmexit[vcpu].rip + vmexit[vcpu].inst_length; 505e285ef8dSPeter Grehan break; 506e285ef8dSPeter Grehan case VMEXIT_RESTART: 507e285ef8dSPeter Grehan rip = vmexit[vcpu].rip; 508e285ef8dSPeter Grehan break; 509e285ef8dSPeter Grehan case VMEXIT_RESET: 510e285ef8dSPeter Grehan exit(0); 511e285ef8dSPeter Grehan default: 512e285ef8dSPeter Grehan exit(1); 513e285ef8dSPeter Grehan } 514e285ef8dSPeter Grehan } 515e285ef8dSPeter Grehan fprintf(stderr, "vm_run error %d, errno %d\n", error, errno); 516e285ef8dSPeter Grehan } 517e285ef8dSPeter Grehan 5185f0677d3SNeel Natu static int 5195f0677d3SNeel Natu num_vcpus_allowed(struct vmctx *ctx) 5205f0677d3SNeel Natu { 5215f0677d3SNeel Natu int tmp, error; 5225f0677d3SNeel Natu 5235f0677d3SNeel Natu error = vm_get_capability(ctx, BSP, VM_CAP_UNRESTRICTED_GUEST, &tmp); 5245f0677d3SNeel Natu 5255f0677d3SNeel Natu /* 5265f0677d3SNeel Natu * The guest is allowed to spinup more than one processor only if the 5275f0677d3SNeel Natu * UNRESTRICTED_GUEST capability is available. 5285f0677d3SNeel Natu */ 5295f0677d3SNeel Natu if (error == 0) 5305f0677d3SNeel Natu return (VM_MAXCPU); 5315f0677d3SNeel Natu else 5325f0677d3SNeel Natu return (1); 5335f0677d3SNeel Natu } 534e285ef8dSPeter Grehan 53549cc03daSNeel Natu void 53649cc03daSNeel Natu fbsdrun_set_capabilities(struct vmctx *ctx, int cpu) 53749cc03daSNeel Natu { 53849cc03daSNeel Natu int err, tmp; 53949cc03daSNeel Natu 54049cc03daSNeel Natu if (fbsdrun_vmexit_on_hlt()) { 54149cc03daSNeel Natu err = vm_get_capability(ctx, cpu, VM_CAP_HALT_EXIT, &tmp); 54249cc03daSNeel Natu if (err < 0) { 54349cc03daSNeel Natu fprintf(stderr, "VM exit on HLT not supported\n"); 54449cc03daSNeel Natu exit(1); 54549cc03daSNeel Natu } 54649cc03daSNeel Natu vm_set_capability(ctx, cpu, VM_CAP_HALT_EXIT, 1); 54749cc03daSNeel Natu if (cpu == BSP) 54849cc03daSNeel Natu handler[VM_EXITCODE_HLT] = vmexit_hlt; 54949cc03daSNeel Natu } 55049cc03daSNeel Natu 55149cc03daSNeel Natu if (fbsdrun_vmexit_on_pause()) { 55249cc03daSNeel Natu /* 55349cc03daSNeel Natu * pause exit support required for this mode 55449cc03daSNeel Natu */ 55549cc03daSNeel Natu err = vm_get_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, &tmp); 55649cc03daSNeel Natu if (err < 0) { 55749cc03daSNeel Natu fprintf(stderr, 55849cc03daSNeel Natu "SMP mux requested, no pause support\n"); 55949cc03daSNeel Natu exit(1); 56049cc03daSNeel Natu } 56149cc03daSNeel Natu vm_set_capability(ctx, cpu, VM_CAP_PAUSE_EXIT, 1); 56249cc03daSNeel Natu if (cpu == BSP) 56349cc03daSNeel Natu handler[VM_EXITCODE_PAUSE] = vmexit_pause; 56449cc03daSNeel Natu } 56549cc03daSNeel Natu 56649cc03daSNeel Natu if (fbsdrun_disable_x2apic()) 56749cc03daSNeel Natu err = vm_set_x2apic_state(ctx, cpu, X2APIC_DISABLED); 56849cc03daSNeel Natu else 56949cc03daSNeel Natu err = vm_set_x2apic_state(ctx, cpu, X2APIC_ENABLED); 57049cc03daSNeel Natu 57149cc03daSNeel Natu if (err) { 57249cc03daSNeel Natu fprintf(stderr, "Unable to set x2apic state (%d)\n", err); 57349cc03daSNeel Natu exit(1); 57449cc03daSNeel Natu } 57549cc03daSNeel Natu 57649cc03daSNeel Natu vm_set_capability(ctx, cpu, VM_CAP_ENABLE_INVPCID, 1); 57749cc03daSNeel Natu } 57849cc03daSNeel Natu 579e285ef8dSPeter Grehan int 580e285ef8dSPeter Grehan main(int argc, char *argv[]) 581e285ef8dSPeter Grehan { 582a1a4cbeaSNeel Natu int c, error, gdb_port, err, bvmcons; 5835f0677d3SNeel Natu int max_vcpus; 584e285ef8dSPeter Grehan struct vmctx *ctx; 585e285ef8dSPeter Grehan uint64_t rip; 586b060ba50SNeel Natu size_t memsize; 587e285ef8dSPeter Grehan 588e285ef8dSPeter Grehan bvmcons = 0; 589e285ef8dSPeter Grehan progname = basename(argv[0]); 5904a06a0feSNeel Natu gdb_port = 0; 591e285ef8dSPeter Grehan guest_ncpus = 1; 592b060ba50SNeel Natu memsize = 256 * MB; 593e285ef8dSPeter Grehan 594851d84f1SNeel Natu while ((c = getopt(argc, argv, "abehwAHIPWp:g:c:s:S:m:l:")) != -1) { 595e285ef8dSPeter Grehan switch (c) { 596e285ef8dSPeter Grehan case 'a': 597e285ef8dSPeter Grehan disable_x2apic = 1; 598e285ef8dSPeter Grehan break; 599e285ef8dSPeter Grehan case 'A': 600e285ef8dSPeter Grehan acpi = 1; 601e285ef8dSPeter Grehan break; 602e285ef8dSPeter Grehan case 'b': 603e285ef8dSPeter Grehan bvmcons = 1; 604e285ef8dSPeter Grehan break; 605e285ef8dSPeter Grehan case 'p': 606e285ef8dSPeter Grehan pincpu = atoi(optarg); 607e285ef8dSPeter Grehan break; 608e285ef8dSPeter Grehan case 'c': 609e285ef8dSPeter Grehan guest_ncpus = atoi(optarg); 610e285ef8dSPeter Grehan break; 611e285ef8dSPeter Grehan case 'g': 612e285ef8dSPeter Grehan gdb_port = atoi(optarg); 613e285ef8dSPeter Grehan break; 614ea7f1c8cSNeel Natu case 'l': 615ea7f1c8cSNeel Natu if (lpc_device_parse(optarg) != 0) { 616ea7f1c8cSNeel Natu errx(EX_USAGE, "invalid lpc device " 617ea7f1c8cSNeel Natu "configuration '%s'", optarg); 618ea7f1c8cSNeel Natu } 619ea7f1c8cSNeel Natu break; 620e285ef8dSPeter Grehan case 's': 621b05c77ffSNeel Natu if (pci_parse_slot(optarg, 0) != 0) 622b05c77ffSNeel Natu exit(1); 623b05c77ffSNeel Natu else 624e285ef8dSPeter Grehan break; 625e285ef8dSPeter Grehan case 'S': 626b05c77ffSNeel Natu if (pci_parse_slot(optarg, 1) != 0) 627b05c77ffSNeel Natu exit(1); 628b05c77ffSNeel Natu else 629e285ef8dSPeter Grehan break; 630e285ef8dSPeter Grehan case 'm': 631200758f1SNeel Natu error = vm_parse_memsize(optarg, &memsize); 632200758f1SNeel Natu if (error) 633200758f1SNeel Natu errx(EX_USAGE, "invalid memsize '%s'", optarg); 634e285ef8dSPeter Grehan break; 635e285ef8dSPeter Grehan case 'H': 636e285ef8dSPeter Grehan guest_vmexit_on_hlt = 1; 637e285ef8dSPeter Grehan break; 638e285ef8dSPeter Grehan case 'I': 639a1a4cbeaSNeel Natu /* 640a1a4cbeaSNeel Natu * The "-I" option was used to add an ioapic to the 641a1a4cbeaSNeel Natu * virtual machine. 642a1a4cbeaSNeel Natu * 643a1a4cbeaSNeel Natu * An ioapic is now provided unconditionally for each 644a1a4cbeaSNeel Natu * virtual machine and this option is now deprecated. 645a1a4cbeaSNeel Natu */ 646e285ef8dSPeter Grehan break; 647e285ef8dSPeter Grehan case 'P': 648e285ef8dSPeter Grehan guest_vmexit_on_pause = 1; 649e285ef8dSPeter Grehan break; 650e285ef8dSPeter Grehan case 'e': 651e285ef8dSPeter Grehan strictio = 1; 652e285ef8dSPeter Grehan break; 653851d84f1SNeel Natu case 'w': 654851d84f1SNeel Natu strictmsr = 0; 655851d84f1SNeel Natu break; 656062b878fSPeter Grehan case 'W': 657062b878fSPeter Grehan virtio_msix = 0; 658062b878fSPeter 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 vmname = argv[0]; 672e285ef8dSPeter Grehan 673e285ef8dSPeter Grehan ctx = vm_open(vmname); 674e285ef8dSPeter Grehan if (ctx == NULL) { 675e285ef8dSPeter Grehan perror("vm_open"); 676e285ef8dSPeter Grehan exit(1); 677e285ef8dSPeter Grehan } 678e285ef8dSPeter Grehan 6795f0677d3SNeel Natu max_vcpus = num_vcpus_allowed(ctx); 6805f0677d3SNeel Natu if (guest_ncpus > max_vcpus) { 6815f0677d3SNeel Natu fprintf(stderr, "%d vCPUs requested but only %d available\n", 6825f0677d3SNeel Natu guest_ncpus, max_vcpus); 6835f0677d3SNeel Natu exit(1); 6845f0677d3SNeel Natu } 6855f0677d3SNeel Natu 68649cc03daSNeel Natu fbsdrun_set_capabilities(ctx, BSP); 687e285ef8dSPeter Grehan 688b060ba50SNeel Natu err = vm_setup_memory(ctx, memsize, VM_MMAP_ALL); 689b060ba50SNeel Natu if (err) { 690b060ba50SNeel Natu fprintf(stderr, "Unable to setup memory (%d)\n", err); 691b060ba50SNeel Natu exit(1); 692e285ef8dSPeter Grehan } 693e285ef8dSPeter Grehan 6940e2ca4e6SNeel Natu init_mem(); 695e285ef8dSPeter Grehan init_inout(); 696ea7f1c8cSNeel Natu legacy_irq_init(); 697a38e2a64SPeter Grehan 6989d6be09fSPeter Grehan rtc_init(ctx); 6999d6be09fSPeter Grehan 700a38e2a64SPeter Grehan /* 701a38e2a64SPeter Grehan * Exit if a device emulation finds an error in it's initilization 702a38e2a64SPeter Grehan */ 703a38e2a64SPeter Grehan if (init_pci(ctx) != 0) 704a38e2a64SPeter Grehan exit(1); 705a38e2a64SPeter Grehan 706e285ef8dSPeter Grehan if (gdb_port != 0) 707e285ef8dSPeter Grehan init_dbgport(gdb_port); 708e285ef8dSPeter Grehan 709e285ef8dSPeter Grehan if (bvmcons) 710e285ef8dSPeter Grehan init_bvmcons(); 711e285ef8dSPeter Grehan 712e285ef8dSPeter Grehan error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip); 713e285ef8dSPeter Grehan assert(error == 0); 714e285ef8dSPeter Grehan 715e285ef8dSPeter Grehan /* 716e285ef8dSPeter Grehan * build the guest tables, MP etc. 717e285ef8dSPeter Grehan */ 718a1a4cbeaSNeel Natu mptable_build(ctx, guest_ncpus); 719e285ef8dSPeter Grehan 720e285ef8dSPeter Grehan if (acpi) { 721a1a4cbeaSNeel Natu error = acpi_build(ctx, guest_ncpus); 722e285ef8dSPeter Grehan assert(error == 0); 723e285ef8dSPeter Grehan } 724e285ef8dSPeter Grehan 725e285ef8dSPeter Grehan /* 7267f5487acSPeter Grehan * Change the proc title to include the VM name. 7277f5487acSPeter Grehan */ 7287f5487acSPeter Grehan setproctitle("%s", vmname); 7297f5487acSPeter Grehan 7307f5487acSPeter Grehan /* 731e285ef8dSPeter Grehan * Add CPU 0 732e285ef8dSPeter Grehan */ 733e285ef8dSPeter Grehan fbsdrun_addcpu(ctx, BSP, rip); 734e285ef8dSPeter Grehan 735e285ef8dSPeter Grehan /* 736e285ef8dSPeter Grehan * Head off to the main event dispatch loop 737e285ef8dSPeter Grehan */ 738e285ef8dSPeter Grehan mevent_dispatch(); 739e285ef8dSPeter Grehan 740e285ef8dSPeter Grehan exit(1); 741e285ef8dSPeter Grehan } 742