1*e84206a6SAaron LI /*
2*e84206a6SAaron LI * Copyright (c) 2018-2021 Maxime Villard, m00nbsd.net
3*e84206a6SAaron LI * All rights reserved.
4*e84206a6SAaron LI *
5*e84206a6SAaron LI * This code is part of the NVMM hypervisor.
6*e84206a6SAaron LI *
7*e84206a6SAaron LI * Redistribution and use in source and binary forms, with or without
8*e84206a6SAaron LI * modification, are permitted provided that the following conditions
9*e84206a6SAaron LI * are met:
10*e84206a6SAaron LI * 1. Redistributions of source code must retain the above copyright
11*e84206a6SAaron LI * notice, this list of conditions and the following disclaimer.
12*e84206a6SAaron LI * 2. Redistributions in binary form must reproduce the above copyright
13*e84206a6SAaron LI * notice, this list of conditions and the following disclaimer in the
14*e84206a6SAaron LI * documentation and/or other materials provided with the distribution.
15*e84206a6SAaron LI *
16*e84206a6SAaron LI * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17*e84206a6SAaron LI * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*e84206a6SAaron LI * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19*e84206a6SAaron LI * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20*e84206a6SAaron LI * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*e84206a6SAaron LI * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22*e84206a6SAaron LI * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*e84206a6SAaron LI * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*e84206a6SAaron LI * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*e84206a6SAaron LI * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*e84206a6SAaron LI * SUCH DAMAGE.
27*e84206a6SAaron LI */
28*e84206a6SAaron LI
29a4da4a90SAaron LI #include <stdlib.h>
30a4da4a90SAaron LI #include <stdio.h>
31a4da4a90SAaron LI #include <string.h>
32a4da4a90SAaron LI #include <err.h>
33a4da4a90SAaron LI #include <fcntl.h>
34a4da4a90SAaron LI #include <sys/mman.h>
35a4da4a90SAaron LI #include <nvmm.h>
36a4da4a90SAaron LI
37a4da4a90SAaron LI #define PAGE_SIZE 4096
38a4da4a90SAaron LI
39a4da4a90SAaron LI /*
40*e84206a6SAaron LI * A simple calculator that creates a VM which performs the addition of the
41*e84206a6SAaron LI * two ints given as arguments.
42a4da4a90SAaron LI *
43a4da4a90SAaron LI * The guest does EBX+=EAX, followed by HLT. We set EAX and EBX, and then
44a4da4a90SAaron LI * fetch the result in EBX. HLT is our shutdown point, we stop the VM there.
45a4da4a90SAaron LI *
46a4da4a90SAaron LI * We give one single page to the guest, and copy there the instructions it
47a4da4a90SAaron LI * must execute. The guest runs in 16bit real mode, and its initial state is
48a4da4a90SAaron LI * the x86 RESET state (default state). The instruction pointer uses CS.base
49a4da4a90SAaron LI * as base, and this base value is 0xFFFF0000. So we make it our GPA, and set
50a4da4a90SAaron LI * RIP=0, which means "RIP=0xFFFF0000+0". The guest therefore executes the
51a4da4a90SAaron LI * instructions at GPA 0xFFFF0000.
52a4da4a90SAaron LI *
53e15b0a44SAaron LI * $ cc -g -Wall -Wextra -o calc-vm calc-vm.c -lnvmm
54e15b0a44SAaron LI * $ ./calc-vm 3 5
55a4da4a90SAaron LI * Result: 8
56a4da4a90SAaron LI *
57a4da4a90SAaron LI * Don't forget to load the nvmm(4) kernel module beforehand!
58a4da4a90SAaron LI *
59a4da4a90SAaron LI * From:
60a4da4a90SAaron LI * https://www.netbsd.org/~maxv/nvmm/calc-vm.c
61a4da4a90SAaron LI * https://blog.netbsd.org/tnf/entry/from_zero_to_nvmm
62a4da4a90SAaron LI */
63e15b0a44SAaron LI
main(int argc,char * argv[])64a4da4a90SAaron LI int main(int argc, char *argv[])
65a4da4a90SAaron LI {
66a4da4a90SAaron LI const uint8_t instr[] = {
67a4da4a90SAaron LI 0x01, 0xc3, /* add %eax,%ebx */
68a4da4a90SAaron LI 0xf4 /* hlt */
69a4da4a90SAaron LI };
70a4da4a90SAaron LI struct nvmm_machine mach;
71a4da4a90SAaron LI struct nvmm_vcpu vcpu;
72a4da4a90SAaron LI uintptr_t hva;
73a4da4a90SAaron LI gpaddr_t gpa = 0xFFFF0000;
74a4da4a90SAaron LI int num1, num2, ret;
75a4da4a90SAaron LI
76a4da4a90SAaron LI if (argc != 3) {
77a4da4a90SAaron LI fprintf(stderr, "usage: %s <int#1> <int#2>\n", argv[0]);
78a4da4a90SAaron LI exit(EXIT_FAILURE);
79a4da4a90SAaron LI }
80a4da4a90SAaron LI
81a4da4a90SAaron LI num1 = atoi(argv[1]);
82a4da4a90SAaron LI num2 = atoi(argv[2]);
83a4da4a90SAaron LI
84a4da4a90SAaron LI /* Init NVMM. */
85a4da4a90SAaron LI if (nvmm_init() == -1)
86a4da4a90SAaron LI err(EXIT_FAILURE, "unable to init NVMM");
872e818704SAaron LI printf("[+] Initialized NVMM\n");
88a4da4a90SAaron LI
89a4da4a90SAaron LI /* Create the VM. */
90a4da4a90SAaron LI if (nvmm_machine_create(&mach) == -1)
91a4da4a90SAaron LI err(EXIT_FAILURE, "unable to create the VM");
922e818704SAaron LI printf("[+] Created machine\n");
93e15b0a44SAaron LI if (nvmm_vcpu_create(&mach, 0, &vcpu) == -1)
94e15b0a44SAaron LI err(EXIT_FAILURE, "unable to create VCPU");
952e818704SAaron LI printf("[+] Created VCPU\n");
96a4da4a90SAaron LI
97a4da4a90SAaron LI /* Allocate a HVA. The HVA is writable. */
98a4da4a90SAaron LI hva = (uintptr_t)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
99a4da4a90SAaron LI MAP_ANON|MAP_PRIVATE, -1, 0);
100e15b0a44SAaron LI if ((void *)hva == MAP_FAILED)
101e15b0a44SAaron LI err(EXIT_FAILURE, "unable to mmap");
102e15b0a44SAaron LI if (nvmm_hva_map(&mach, hva, PAGE_SIZE) == -1)
103e15b0a44SAaron LI err(EXIT_FAILURE, "unable to map HVA");
1042e818704SAaron LI printf("[+] Mapped HVA\n");
105a4da4a90SAaron LI
106a4da4a90SAaron LI /* Link the GPA towards the HVA. The GPA is executable. */
107e15b0a44SAaron LI if (nvmm_gpa_map(&mach, hva, gpa, PAGE_SIZE, PROT_READ|PROT_EXEC) == -1)
108e15b0a44SAaron LI err(EXIT_FAILURE, "unable to map GPA");
1092e818704SAaron LI printf("[+] Mapped GPA\n");
110a4da4a90SAaron LI
111a4da4a90SAaron LI /* Install the guest instructions there. */
112a4da4a90SAaron LI memcpy((void *)hva, instr, sizeof(instr));
113a4da4a90SAaron LI
114a4da4a90SAaron LI /* Reset the instruction pointer, and set EAX/EBX. */
115e15b0a44SAaron LI if (nvmm_vcpu_getstate(&mach, &vcpu, NVMM_X64_STATE_GPRS) == -1)
116e15b0a44SAaron LI err(EXIT_FAILURE, "unable to get VCPU state");
1172e818704SAaron LI printf("[+] Got VCPU states\n");
118a4da4a90SAaron LI vcpu.state->gprs[NVMM_X64_GPR_RIP] = 0;
119a4da4a90SAaron LI vcpu.state->gprs[NVMM_X64_GPR_RAX] = num1;
120a4da4a90SAaron LI vcpu.state->gprs[NVMM_X64_GPR_RBX] = num2;
121a4da4a90SAaron LI nvmm_vcpu_setstate(&mach, &vcpu, NVMM_X64_STATE_GPRS);
1222e818704SAaron LI printf("[+] Set VCPU states\n");
123a4da4a90SAaron LI
124a4da4a90SAaron LI while (1) {
125a4da4a90SAaron LI /* Run VCPU0. */
1262e818704SAaron LI printf("[+] Running VCPU\n");
127e15b0a44SAaron LI if (nvmm_vcpu_run(&mach, &vcpu) == -1)
128e15b0a44SAaron LI err(EXIT_FAILURE, "unable to run VCPU");
1292e818704SAaron LI printf("[+] VCPU exited\n");
130a4da4a90SAaron LI
131a4da4a90SAaron LI /* Process the exit reasons. */
132a4da4a90SAaron LI switch (vcpu.exit->reason) {
133a4da4a90SAaron LI case NVMM_VCPU_EXIT_NONE:
134a4da4a90SAaron LI /* Nothing to do, keep rolling. */
135a4da4a90SAaron LI break;
136a4da4a90SAaron LI case NVMM_VCPU_EXIT_HALTED:
137a4da4a90SAaron LI /* Our shutdown point. Fetch the result. */
138a4da4a90SAaron LI nvmm_vcpu_getstate(&mach, &vcpu, NVMM_X64_STATE_GPRS);
139a4da4a90SAaron LI ret = vcpu.state->gprs[NVMM_X64_GPR_RBX];
140a4da4a90SAaron LI printf("Result: %d\n", ret);
141a4da4a90SAaron LI return 0;
142a4da4a90SAaron LI /* THE PROCESS EXITS, THE VM GETS DESTROYED. */
143a4da4a90SAaron LI default:
144e15b0a44SAaron LI errx(EXIT_FAILURE, "unknown exit reason: 0x%lx",
145e15b0a44SAaron LI vcpu.exit->reason);
146a4da4a90SAaron LI }
147a4da4a90SAaron LI }
148a4da4a90SAaron LI
149a4da4a90SAaron LI return 0;
150a4da4a90SAaron LI }
151