dsynth - Conditionally avoid passing PKG_SUFX make env
[dragonfly.git] / test / nvmm / calc-vm.c
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 }