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, ®s);
267 regs.rip = 0x1000;
268 regs.rflags = 2;
269 crosvm_vcpu_set_regs(vcpu, ®s);
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