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