xref: /dragonfly/test/nvmm/calc-vm.c (revision 7a511437)
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