1 /* 2 * Copyright (c) 2018-2021 Maxime Villard, m00nbsd.net 3 * All rights reserved. 4 * 5 * This code is part of the NVMM hypervisor. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/mman.h> 31 32 #include <assert.h> 33 #include <err.h> 34 #include <pthread.h> 35 #include <stdbool.h> 36 #include <stdint.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 41 #include "common.h" 42 43 #undef MSR_APICBASE 44 #define MSR_APICBASE 0x01b 45 #undef APICBASE_BSP 46 #define APICBASE_BSP 0x00000100 /* bootstrap processor */ 47 #undef APICBASE_EN 48 #define APICBASE_EN 0x00000800 /* software enable */ 49 50 /* -------------------------------------------------------------------------- */ 51 52 uintptr_t 53 toyvirt_mem_add(struct nvmm_machine *mach, gpaddr_t gpa, size_t size) 54 { 55 uintptr_t hva; 56 void *buf; 57 58 assert(size > 0); 59 60 buf = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); 61 if (buf == MAP_FAILED) 62 err(EXIT_FAILURE, "mmap"); 63 64 hva = (uintptr_t)buf; 65 if (nvmm_hva_map(mach, hva, size) == -1) 66 err(EXIT_FAILURE, "nvmm_hva_map"); 67 if (nvmm_gpa_map(mach, hva, gpa, size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1) 68 err(EXIT_FAILURE, "nvmm_gpa_map"); 69 70 return hva; 71 } 72 73 /* -------------------------------------------------------------------------- */ 74 75 static bool can_take_int = false; 76 static bool can_take_nmi = false; 77 static bool has_int_pending = false; 78 static bool has_nmi_pending = false; 79 static struct nvmm_vcpu_event pending_int; 80 static struct nvmm_vcpu_event pending_nmi; 81 82 static void 83 toyvirt_event_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 84 struct nvmm_vcpu_event *event) 85 { 86 memcpy(vcpu->event, event, sizeof(*event)); 87 88 /* INT. */ 89 if (event->vector != 2) { 90 if (can_take_int) { 91 if (nvmm_vcpu_inject(mach, vcpu) == -1) 92 err(EXIT_FAILURE, "nvmm_vcpu_inject"); 93 has_int_pending = false; 94 } else { 95 memcpy(&pending_int, event, sizeof(pending_int)); 96 has_int_pending = true; 97 } 98 } 99 100 /* NMI. */ 101 if (event->vector == 2) { 102 if (can_take_nmi) { 103 if (nvmm_vcpu_inject(mach, vcpu) == -1) 104 err(EXIT_FAILURE, "nvmm_vcpu_inject"); 105 has_nmi_pending = false; 106 } else { 107 memcpy(&pending_nmi, event, sizeof(pending_nmi)); 108 has_nmi_pending = true; 109 } 110 } 111 } 112 113 static void 114 toyvirt_event_reinject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 115 { 116 struct nvmm_vcpu_exit *exit = vcpu->exit; 117 118 if (exit->reason == NVMM_VCPU_EXIT_INT_READY) { 119 if (!has_int_pending) 120 errx(EXIT_FAILURE, "no INT pending!"); 121 toyvirt_event_inject(mach, vcpu, &pending_int); 122 } else { 123 if (!has_nmi_pending) 124 errx(EXIT_FAILURE, "no NMI pending!"); 125 toyvirt_event_inject(mach, vcpu, &pending_nmi); 126 } 127 } 128 129 /* -------------------------------------------------------------------------- */ 130 131 static void 132 toycpu_io_callback(struct nvmm_io *io) 133 { 134 /* Hand over to toydev. */ 135 toydev_io(io->port, io->in, io->data, io->size); 136 } 137 138 static void 139 toyvirt_mem_callback(struct nvmm_mem *mem) 140 { 141 /* Hand over to toydev. */ 142 toydev_mmio(mem->gpa, mem->write, mem->data, mem->size); 143 } 144 145 static struct nvmm_assist_callbacks callbacks = { 146 .io = toycpu_io_callback, 147 .mem = toyvirt_mem_callback 148 }; 149 150 /* -------------------------------------------------------------------------- */ 151 152 static void 153 toyvirt_vcpu_configure(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 154 { 155 struct nvmm_vcpu_conf_cpuid cpuid; 156 int ret; 157 158 /* 159 * Register the assist callbacks. 160 */ 161 ret = nvmm_vcpu_configure(mach, vcpu, NVMM_VCPU_CONF_CALLBACKS, 162 &callbacks); 163 if (ret == -1) 164 err(EXIT_FAILURE, "nvmm_vcpu_configure"); 165 166 /* 167 * Hide the No-Execute bit. No particular reason, just to demonstrate. 168 */ 169 memset(&cpuid, 0, sizeof(cpuid)); 170 cpuid.mask = 1; 171 cpuid.leaf = 0x80000001; 172 cpuid.u.mask.del.edx = CPUID_8_01_EDX_XD; 173 ret = nvmm_vcpu_configure(mach, vcpu, NVMM_VCPU_CONF_CPUID, &cpuid); 174 if (ret == -1) 175 err(EXIT_FAILURE, "nvmm_vcpu_configure"); 176 } 177 178 static void 179 toyvirt_init_seg(struct nvmm_x64_state_seg *seg, int type, int sel, int limit) 180 { 181 seg->selector = sel; 182 seg->attrib.type = type; 183 seg->attrib.s = (type & 0b10000) != 0; 184 seg->attrib.dpl = 0; 185 seg->attrib.p = 1; 186 seg->attrib.avl = 1; 187 seg->attrib.l = 0; 188 seg->attrib.def = 1; 189 seg->attrib.g = 1; 190 seg->limit = limit; 191 seg->base = 0x00000000; 192 } 193 194 static void 195 toyvirt_init(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 196 const char *path) 197 { 198 struct nvmm_vcpu_state *state = vcpu->state; 199 uint64_t rip; 200 201 if (nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) 202 errx(EXIT_FAILURE, "nvmm_vcpu_getstate"); 203 204 /* Default. */ 205 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_CS], 206 27 /* memory execute read accessed */, 0, 0xFFFFFFFF); 207 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_SS], 208 19 /* memory read write accessed */, 0, 0xFFFFFFFF); 209 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_DS], 210 19 /* memory read write accessed */, 0, 0xFFFFFFFF); 211 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_ES], 212 19 /* memory read write accessed */, 0, 0xFFFFFFFF); 213 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_FS], 214 19 /* memory read write accessed */, 0, 0xFFFFFFFF); 215 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_GS], 216 19 /* memory read write accessed */, 0, 0xFFFFFFFF); 217 218 /* Blank. */ 219 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_GDT], 0, 0, 0x0000FFFF); 220 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_IDT], 0, 0, 0x0000FFFF); 221 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_LDT], 2, 0, 0xFFFFFFFF); 222 toyvirt_init_seg(&state->segs[NVMM_X64_SEG_TR], 11, 0, 0xFFFFFFFF); 223 224 /* Protected mode enabled. */ 225 state->crs[NVMM_X64_CR_CR0] = CR0_PE | CR0_ET | CR0_NW | CR0_CD; 226 227 /* Map the VM. */ 228 if (elf_map(mach, path, &rip) != 0) 229 errx(EXIT_FAILURE, "unable to map the vm"); 230 231 state->gprs[NVMM_X64_GPR_RIP] = rip; /* jump here */ 232 233 if (nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) 234 err(EXIT_FAILURE, "nvmm_vcpu_setstate"); 235 } 236 237 /* -------------------------------------------------------------------------- */ 238 239 static uint8_t toyvirt_prio = 0; 240 241 static struct { 242 struct nvmm_machine *mach; 243 struct nvmm_vcpu *vcpu; 244 } toyvirt; 245 246 /* 247 * Create mess in the VCPU. Inject random events at regular intervals. 248 */ 249 static void * 250 toyvirt_mess(void *arg __unused) 251 { 252 struct nvmm_machine *mach = toyvirt.mach; 253 struct nvmm_vcpu *vcpu = toyvirt.vcpu; 254 struct nvmm_vcpu_event event; 255 256 while (1) { 257 sleep(3); 258 259 /* Inject a #GP */ 260 printf("[+] Inject #GP event\n"); 261 event.type = NVMM_VCPU_EVENT_EXCP; 262 event.vector = 13; 263 event.u.excp.error = 0; 264 toyvirt_event_inject(mach, vcpu, &event); 265 266 sleep(3); 267 268 /* Inject an #NMI */ 269 printf("[+] Inject #NMI event\n"); 270 event.type = NVMM_VCPU_EVENT_INTR; 271 event.vector = 2; 272 toyvirt_event_inject(mach, vcpu, &event); 273 274 sleep(3); 275 276 /* Inject an interrupt */ 277 if (15 > toyvirt_prio) { 278 printf("[+] Inject hardware interrupt event\n"); 279 event.type = NVMM_VCPU_EVENT_INTR; 280 event.vector = 200; 281 toyvirt_event_inject(mach, vcpu, &event); 282 } 283 } 284 285 pthread_exit(NULL); 286 } 287 288 /* -------------------------------------------------------------------------- */ 289 290 /* 291 * Support one MSR: MSR_APICBASE. 292 */ 293 static int 294 toycpu_rdmsr(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 295 { 296 struct nvmm_vcpu_state *state = vcpu->state; 297 struct nvmm_vcpu_exit *exit = vcpu->exit; 298 uint64_t val; 299 300 if (exit->u.rdmsr.msr != MSR_APICBASE) { 301 printf("Unknown MSR!\n"); 302 return -1; 303 } 304 305 if (nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_GPRS) == -1) 306 err(EXIT_FAILURE, "nvmm_vcpu_getstate"); 307 308 val = APICBASE_BSP | APICBASE_EN | 0xfee00000; 309 310 state->gprs[NVMM_X64_GPR_RAX] = (val & 0xFFFFFFFF); 311 state->gprs[NVMM_X64_GPR_RDX] = (val >> 32); 312 state->gprs[NVMM_X64_GPR_RIP] = exit->u.rdmsr.npc; 313 314 if (nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_GPRS) == -1) 315 err(EXIT_FAILURE, "nvmm_vcpu_setstate"); 316 317 return 0; 318 } 319 320 static void 321 toyvirt_invalid(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 322 { 323 struct nvmm_vcpu_state *state = vcpu->state; 324 325 if (nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_GPRS) == -1) 326 err(EXIT_FAILURE, "nvmm_vcpu_getstate"); 327 328 printf("[!] Invalid exit: rip=%p\n", 329 (void *)state->gprs[NVMM_X64_GPR_RIP]); 330 } 331 332 static void 333 toyvirt_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 334 { 335 struct nvmm_vcpu_exit *exit = vcpu->exit; 336 pthread_t thid; 337 int ret; 338 339 toyvirt.mach = mach; 340 toyvirt.vcpu = vcpu; 341 pthread_create(&thid, NULL, toyvirt_mess, NULL); 342 343 while (1) { 344 if (nvmm_vcpu_run(mach, vcpu) == -1) 345 err(EXIT_FAILURE, "nvmm_vcpu_run"); 346 347 toyvirt_prio = exit->exitstate.cr8; 348 can_take_int = !exit->exitstate.int_window_exiting; 349 can_take_nmi = !exit->exitstate.nmi_window_exiting; 350 351 switch (exit->reason) { 352 case NVMM_VCPU_EXIT_NONE: 353 /* 354 * A VMEXIT caused by whatever internal reason, that 355 * we shouldn't take care of. Keep rolling. 356 */ 357 continue; 358 359 case NVMM_VCPU_EXIT_IO: 360 ret = nvmm_assist_io(mach, vcpu); 361 if (ret == -1) 362 err(EXIT_FAILURE, "nvmm_assist_io"); 363 continue; 364 365 case NVMM_VCPU_EXIT_RDMSR: 366 toycpu_rdmsr(mach, vcpu); 367 continue; 368 369 case NVMM_VCPU_EXIT_MEMORY: 370 ret = nvmm_assist_mem(mach, vcpu); 371 if (ret == -1) 372 err(EXIT_FAILURE, "nvmm_assist_mem"); 373 continue; 374 375 case NVMM_VCPU_EXIT_INT_READY: 376 case NVMM_VCPU_EXIT_NMI_READY: 377 printf("[+] Machine ready to INT/NMI\n"); 378 toyvirt_event_reinject(mach, vcpu); 379 return; 380 381 case NVMM_VCPU_EXIT_SHUTDOWN: 382 /* Stop the VM here. */ 383 printf("[+] Machine received shutdown\n"); 384 return; 385 386 case NVMM_VCPU_EXIT_INVALID: 387 default: 388 toyvirt_invalid(mach, vcpu); 389 return; 390 } 391 } 392 } 393 394 int main(int argc, char *argv[]) 395 { 396 struct nvmm_machine mach; 397 struct nvmm_vcpu vcpu; 398 399 if (argc != 2) 400 errx(EXIT_FAILURE, "usage: %s file-path", argv[0]); 401 402 if (nvmm_init() == -1) 403 err(EXIT_FAILURE, "nvmm_init"); 404 printf("[+] NVMM initialization succeeded\n"); 405 406 if (nvmm_machine_create(&mach) == -1) 407 err(EXIT_FAILURE, "nvmm_machine_create"); 408 printf("[+] Machine creation succeeded\n"); 409 410 if (nvmm_vcpu_create(&mach, 120, &vcpu) == -1) 411 err(EXIT_FAILURE, "nvmm_vcpu_create"); 412 printf("[+] VCPU creation succeeded\n"); 413 414 toyvirt_vcpu_configure(&mach, &vcpu); 415 printf("[+] VCPU configuration succeeded\n"); 416 417 toyvirt_init(&mach, &vcpu, argv[1]); 418 printf("[+] State set\n"); 419 420 toyvirt_run(&mach, &vcpu); 421 printf("[+] Machine execution successful\n"); 422 423 if (nvmm_machine_destroy(&mach) == -1) 424 err(EXIT_FAILURE, "nvmm_machine_destroy"); 425 426 printf("[+] Machine destroyed\n"); 427 428 return 0; 429 } 430