1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <string.h> 4 #include <err.h> 5 #include <fcntl.h> 6 #include <sys/mman.h> 7 #include <nvmm.h> 8 9 #define PAGE_SIZE 4096 10 11 /* 12 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 13 * ---------------------------------------------------------------------------- 14 * Below is an updated version of this example, because the libnvmm API has 15 * changed since I posted the blog entry. See: 16 * https://mail-index.netbsd.org/tech-kern/2019/06/05/msg025101.html 17 * The original version (using the previous API) is available here: 18 * https://www.netbsd.org/~maxv/nvmm/calc-vm-old-api.c 19 * ---------------------------------------------------------------------------- 20 */ 21 22 /* 23 * A simple calculator. Creates a VM which performs the addition of the two 24 * ints given as argument. 25 * 26 * The guest does EBX+=EAX, followed by HLT. We set EAX and EBX, and then 27 * fetch the result in EBX. HLT is our shutdown point, we stop the VM there. 28 * 29 * We give one single page to the guest, and copy there the instructions it 30 * must execute. The guest runs in 16bit real mode, and its initial state is 31 * the x86 RESET state (default state). The instruction pointer uses CS.base 32 * as base, and this base value is 0xFFFF0000. So we make it our GPA, and set 33 * RIP=0, which means "RIP=0xFFFF0000+0". The guest therefore executes the 34 * instructions at GPA 0xFFFF0000. 35 * 36 * $ cc -g -Wall -Wextra -o calc-vm calc-vm.c -lnvmm 37 * $ ./calc-vm 3 5 38 * Result: 8 39 * 40 * Don't forget to load the nvmm(4) kernel module beforehand! 41 * 42 * From: 43 * https://www.netbsd.org/~maxv/nvmm/calc-vm.c 44 * https://blog.netbsd.org/tnf/entry/from_zero_to_nvmm 45 */ 46 47 int main(int argc, char *argv[]) 48 { 49 const uint8_t instr[] = { 50 0x01, 0xc3, /* add %eax,%ebx */ 51 0xf4 /* hlt */ 52 }; 53 struct nvmm_machine mach; 54 struct nvmm_vcpu vcpu; 55 uintptr_t hva; 56 gpaddr_t gpa = 0xFFFF0000; 57 int num1, num2, ret; 58 59 if (argc != 3) { 60 fprintf(stderr, "usage: %s <int#1> <int#2>\n", argv[0]); 61 exit(EXIT_FAILURE); 62 } 63 64 num1 = atoi(argv[1]); 65 num2 = atoi(argv[2]); 66 67 /* Init NVMM. */ 68 if (nvmm_init() == -1) 69 err(EXIT_FAILURE, "unable to init NVMM"); 70 printf("[+] Initialized NVMM\n"); 71 72 /* Create the VM. */ 73 if (nvmm_machine_create(&mach) == -1) 74 err(EXIT_FAILURE, "unable to create the VM"); 75 printf("[+] Created machine\n"); 76 if (nvmm_vcpu_create(&mach, 0, &vcpu) == -1) 77 err(EXIT_FAILURE, "unable to create VCPU"); 78 printf("[+] Created VCPU\n"); 79 80 /* Allocate a HVA. The HVA is writable. */ 81 hva = (uintptr_t)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, 82 MAP_ANON|MAP_PRIVATE, -1, 0); 83 if ((void *)hva == MAP_FAILED) 84 err(EXIT_FAILURE, "unable to mmap"); 85 if (nvmm_hva_map(&mach, hva, PAGE_SIZE) == -1) 86 err(EXIT_FAILURE, "unable to map HVA"); 87 printf("[+] Mapped HVA\n"); 88 89 /* Link the GPA towards the HVA. The GPA is executable. */ 90 if (nvmm_gpa_map(&mach, hva, gpa, PAGE_SIZE, PROT_READ|PROT_EXEC) == -1) 91 err(EXIT_FAILURE, "unable to map GPA"); 92 printf("[+] Mapped GPA\n"); 93 94 /* Install the guest instructions there. */ 95 memcpy((void *)hva, instr, sizeof(instr)); 96 97 /* Reset the instruction pointer, and set EAX/EBX. */ 98 if (nvmm_vcpu_getstate(&mach, &vcpu, NVMM_X64_STATE_GPRS) == -1) 99 err(EXIT_FAILURE, "unable to get VCPU state"); 100 printf("[+] Got VCPU states\n"); 101 vcpu.state->gprs[NVMM_X64_GPR_RIP] = 0; 102 vcpu.state->gprs[NVMM_X64_GPR_RAX] = num1; 103 vcpu.state->gprs[NVMM_X64_GPR_RBX] = num2; 104 nvmm_vcpu_setstate(&mach, &vcpu, NVMM_X64_STATE_GPRS); 105 printf("[+] Set VCPU states\n"); 106 107 while (1) { 108 /* Run VCPU0. */ 109 printf("[+] Running VCPU\n"); 110 if (nvmm_vcpu_run(&mach, &vcpu) == -1) 111 err(EXIT_FAILURE, "unable to run VCPU"); 112 printf("[+] VCPU exited\n"); 113 114 /* Process the exit reasons. */ 115 switch (vcpu.exit->reason) { 116 case NVMM_VCPU_EXIT_NONE: 117 /* Nothing to do, keep rolling. */ 118 break; 119 case NVMM_VCPU_EXIT_HALTED: 120 /* Our shutdown point. Fetch the result. */ 121 nvmm_vcpu_getstate(&mach, &vcpu, NVMM_X64_STATE_GPRS); 122 ret = vcpu.state->gprs[NVMM_X64_GPR_RBX]; 123 printf("Result: %d\n", ret); 124 return 0; 125 /* THE PROCESS EXITS, THE VM GETS DESTROYED. */ 126 default: 127 errx(EXIT_FAILURE, "unknown exit reason: 0x%lx", 128 vcpu.exit->reason); 129 } 130 } 131 132 return 0; 133 } 134