1 /*
2  * Copyright 2020 The Chromium OS Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <linux/kvm.h>
10 #include <linux/memfd.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/mman.h>
16 #include <sys/syscall.h>
17 #include <unistd.h>
18 
19 #include "crosvm.h"
20 
21 #define KILL_ADDRESS 0x3f9
22 
23 #ifndef F_LINUX_SPECIFIC_BASE
24 #define F_LINUX_SPECIFIC_BASE 1024
25 #endif
26 
27 #ifndef F_ADD_SEALS
28 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
29 #endif
30 
31 #ifndef F_SEAL_SHRINK
32 #define F_SEAL_SHRINK 0x0002
33 #endif
34 
35 const uint8_t code[] = {
36     // Set a non-zero value for HV_X64_MSR_GUEST_OS_ID
37     // to enable hypercalls.
38 
39     // mov edx, 0xffffffff
40     0x66, 0xba, 0xff, 0xff, 0xff, 0xff,
41 
42     // mov eax, 0xffffffff
43     0x66, 0xb8, 0xff, 0xff, 0xff, 0xff,
44 
45     // mov ecx, 0x40000000 # HV_X64_MSR_GUEST_OS_ID
46     0x66, 0xb9, 0x00, 0x00, 0x00, 0x40,
47 
48     // wrmsr
49     0x0f, 0x30,
50 
51     // Establish page at 0x2000 as the hypercall page.
52 
53     // mov edx, 0x00000000
54     0x66, 0xba, 0x00, 0x00, 0x00, 0x00,
55 
56     // mov eax, 0x00002001 # lowest bit is enable bit
57     0x66, 0xb8, 0x01, 0x20, 0x00, 0x00,
58 
59     // mov ecx, 0x40000001 # HV_X64_MSR_HYPERCALL
60     0x66, 0xb9, 0x01, 0x00, 0x00, 0x40,
61 
62     // wrmsr
63     0x0f, 0x30,
64 
65     // We can't test generic hypercalls since they're
66     // defined to UD for processors running in real mode.
67 
68     // for HV_X64_MSR_CONTROL:
69     // edx:eax gets transferred as 'control'
70 
71     // mov edx, 0x05060708
72     0x66, 0xba, 0x08, 0x07, 0x06, 0x05,
73 
74     // mov eax, 0x01020304
75     0x66, 0xb8, 0x04, 0x03, 0x02, 0x01,
76 
77     // mov ecx, 0x40000080 # HV_X64_MSR_SCONTROL
78     0x66, 0xb9, 0x80, 0x00, 0x00, 0x40,
79 
80     // wrmsr
81     0x0f, 0x30,
82 
83     // Establish page at 0x3000 as the evt_page.
84 
85     // mov edx, 0x00000000
86     0x66, 0xba, 0x00, 0x00, 0x00, 0x00,
87 
88     // mov eax, 0x00003000
89     0x66, 0xb8, 0x00, 0x30, 0x00, 0x00,
90 
91     // mov ecx, 0x40000082 # HV_X64_MSR_SIEFP
92     0x66, 0xb9, 0x82, 0x00, 0x00, 0x40,
93 
94     // wrmsr
95     0x0f, 0x30,
96 
97     // Establish page at 0x4000 as the 'msg_page'.
98 
99     // mov edx, 0x00000000
100     0x66, 0xba, 0x00, 0x00, 0x00, 0x00,
101 
102     // mov eax, 0x00004000
103     0x66, 0xb8, 0x00, 0x40, 0x00, 0x00,
104 
105     // mov ecx, 0x40000083 # HV_X64_MSR_SIMP
106     0x66, 0xb9, 0x83, 0x00, 0x00, 0x40,
107 
108     // wrmsr
109     0x0f, 0x30,
110 
111     // Request a kill.
112 
113     // mov dx, 0x3f9
114     0xba, 0xf9, 0x03,
115 
116     // mov al, 0x1
117     0xb0, 0x01,
118 
119     // out dx, al
120     0xee,
121 
122     // hlt
123     0xf4
124 };
125 
check_synic_access(struct crosvm_vcpu * vcpu,struct crosvm_vcpu_event * evt,uint32_t msr,uint64_t control,uint64_t evt_page,uint64_t msg_page,const char * phase)126 int check_synic_access(struct crosvm_vcpu* vcpu, struct crosvm_vcpu_event *evt,
127                        uint32_t msr, uint64_t control, uint64_t evt_page,
128                        uint64_t msg_page, const char *phase) {
129     if (evt->kind != CROSVM_VCPU_EVENT_KIND_HYPERV_SYNIC) {
130         fprintf(stderr, "Got incorrect exit type before %s: %d\n", phase,
131                 evt->kind);
132         return 1;
133     }
134     if (evt->hyperv_synic.msr != msr ||
135         evt->hyperv_synic._reserved != 0 ||
136         evt->hyperv_synic.control != control ||
137         evt->hyperv_synic.evt_page != evt_page ||
138         evt->hyperv_synic.msg_page != msg_page) {
139         fprintf(stderr, "Got unexpected synic message after %s: "
140                 "0x%x vs 0x%x, 0x%lx vs 0x%lx, 0x%lx vs 0x%lx, "
141                 "0x%lx vs 0x%lx\n",
142                 phase, msr, evt->hyperv_synic.msr,
143                 control, evt->hyperv_synic.control,
144                 evt_page, evt->hyperv_synic.evt_page,
145                 msg_page, evt->hyperv_synic.msg_page);
146         return 1;
147     }
148 
149     if (crosvm_vcpu_resume(vcpu) != 0) {
150         fprintf(stderr, "Failed to resume after %s\n", phase);
151         return 1;
152     }
153 
154     if (crosvm_vcpu_wait(vcpu, evt) != 0) {
155         fprintf(stderr, "Failed to wait after %s\n", phase);
156         return 1;
157     }
158     return 0;
159 }
160 
main(int argc,char ** argv)161 int main(int argc, char** argv) {
162     struct crosvm* crosvm = NULL;
163     uint64_t cap_args[4] = {0};
164 
165     int ret = crosvm_connect(&crosvm);
166     if (ret) {
167         fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
168         return 1;
169     }
170 
171     ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
172                                KILL_ADDRESS, 1);
173     if (ret) {
174         fprintf(stderr, "failed to reserve kill port: %d\n", ret);
175         return 1;
176     }
177 
178     // VM mem layout:
179     // null page, code page, hypercall page, synic evt_page, synic msg_page
180     int mem_size = 0x4000;
181     int mem_fd = syscall(SYS_memfd_create, "guest_mem",
182                          MFD_CLOEXEC | MFD_ALLOW_SEALING);
183     if (mem_fd < 0) {
184         fprintf(stderr, "failed to create guest memfd: %d\n", errno);
185         return 1;
186     }
187     ret = ftruncate(mem_fd, mem_size);
188     if (ret) {
189         fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
190         return 1;
191     }
192     uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED,
193                         mem_fd, 0x0);
194     if (mem == MAP_FAILED) {
195         fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
196         return 1;
197     }
198     fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
199     memcpy(mem, code, sizeof(code));
200 
201     // Before MSR verify hypercall page is zero
202     int i;
203     for (i = 0; i < 5; ++i) {
204         if (mem[0x1000 + i]) {
205             fprintf(stderr, "Hypercall page isn't zero\n");
206             return 1;
207         }
208     }
209 
210     struct crosvm_memory *mem_obj;
211     ret = crosvm_create_memory(crosvm, mem_fd, 0x0, mem_size, 0x1000,
212                                false, false, &mem_obj);
213     if (ret) {
214         fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
215         return 1;
216     }
217 
218     struct crosvm_vcpu* vcpu = NULL;
219     ret = crosvm_get_vcpu(crosvm, 0, &vcpu);
220     if (ret) {
221         fprintf(stderr, "failed to get vcpu #0: %d\n", ret);
222         return 1;
223     }
224 
225     ret = crosvm_start(crosvm);
226     if (ret) {
227         fprintf(stderr, "failed to start vm: %d\n", ret);
228         return 1;
229     }
230 
231     struct crosvm_vcpu_event evt = {0};
232     ret = crosvm_vcpu_wait(vcpu, &evt);
233     if (ret) {
234         fprintf(stderr, "failed to wait for vm start: %d\n", ret);
235         return 1;
236     }
237     if (evt.kind != CROSVM_VCPU_EVENT_KIND_INIT) {
238         fprintf(stderr, "Got unexpected exit type: %d\n", evt.kind);
239         return 1;
240     }
241 
242     ret = crosvm_enable_capability(crosvm, 0, 0, cap_args);
243     if (ret != -EINVAL) {
244         fprintf(stderr, "Unexpected crosvm_enable_capability result: %d\n",
245                 ret);
246         return 1;
247     }
248 
249     ret = crosvm_vcpu_enable_capability(vcpu, KVM_CAP_HYPERV_SYNIC, 0,
250                                         cap_args);
251     if (ret) {
252         fprintf(stderr, "crosvm_vcpu_enable_capability() failed: %d\n", ret);
253         return 1;
254     }
255 
256     {
257         struct kvm_sregs sregs = {0};
258         crosvm_vcpu_get_sregs(vcpu, &sregs);
259         sregs.cs.base = 0;
260         sregs.cs.selector = 0;
261         sregs.es.base = 0;
262         sregs.es.selector = 0;
263         crosvm_vcpu_set_sregs(vcpu, &sregs);
264 
265         struct kvm_regs regs = {0};
266         crosvm_vcpu_get_regs(vcpu, &regs);
267         regs.rip = 0x1000;
268         regs.rflags = 2;
269         crosvm_vcpu_set_regs(vcpu, &regs);
270     }
271 
272     if (crosvm_vcpu_resume(vcpu) != 0) {
273         fprintf(stderr, "Failed to resume after init\n");
274         return 1;
275     }
276 
277     if (crosvm_vcpu_wait(vcpu, &evt) != 0) {
278         fprintf(stderr, "Failed to wait after init\n");
279         return 1;
280     }
281     if (check_synic_access(vcpu, &evt, 0x40000080, 0x506070801020304, 0, 0,
282                            "synic msg #1")) {
283         return 1;
284     }
285 
286     // After first MSR verify hypercall page is non-zero
287     uint8_t value = 0;
288     for (i = 0; i < 5; ++i) {
289         value |= mem[0x1000+i];
290     }
291     if (value == 0) {
292         fprintf(stderr, "Hypercall page is still zero\n");
293         return 1;
294     }
295 
296     if (check_synic_access(vcpu, &evt, 0x40000082, 0x506070801020304, 0x3000,
297                            0, "synic msg #2")) {
298         return 1;
299     }
300 
301     if (check_synic_access(vcpu, &evt, 0x40000083, 0x506070801020304, 0x3000,
302                            0x4000, "synic msg #3")) {
303         return 1;
304     }
305 
306     if (evt.kind != CROSVM_VCPU_EVENT_KIND_IO_ACCESS) {
307         fprintf(stderr, "Got incorrect exit type after synic #3: %d\n",
308                 evt.kind);
309         return 1;
310     }
311     if (evt.io_access.address_space != CROSVM_ADDRESS_SPACE_IOPORT ||
312         evt.io_access.address != KILL_ADDRESS ||
313         !evt.io_access.is_write ||
314         evt.io_access.length != 1 ||
315         evt.io_access.data[0] != 1) {
316         fprintf(stderr, "Didn't see kill request from VM\n");
317         return 1;
318     }
319 
320     fprintf(stderr, "Saw kill request from VM, exiting\n");
321 
322     return 0;
323 }
324