1 /*
2 * Copyright 2019 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/memfd.h>
10 #include <pthread.h>
11 #include <signal.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/mman.h>
17 #include <sys/syscall.h>
18 #include <time.h>
19 #include <unistd.h>
20
21 #include "crosvm.h"
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 #define KILL_ADDRESS 0x3f9
36 #define ASYNC_ADDRESS 0x500
37
38 int g_kill_evt;
39 int got_error = 0;
40
vcpu_thread(void * arg)41 void *vcpu_thread(void *arg) {
42 struct crosvm_vcpu *vcpu = arg;
43 struct crosvm_vcpu_event evt;
44 while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
45 if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
46 struct kvm_sregs sregs;
47 crosvm_vcpu_get_sregs(vcpu, &sregs);
48 sregs.cs.base = 0;
49 sregs.cs.selector = 0;
50 sregs.es.base = KILL_ADDRESS;
51 sregs.es.selector = 0;
52 crosvm_vcpu_set_sregs(vcpu, &sregs);
53
54 struct kvm_regs regs;
55 crosvm_vcpu_get_regs(vcpu, ®s);
56 regs.rip = 0x1000;
57 regs.rax = 2;
58 regs.rbx = 7;
59 regs.rflags = 2;
60 crosvm_vcpu_set_regs(vcpu, ®s);
61 }
62 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS) {
63 if (evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
64 evt.io_access.address == ASYNC_ADDRESS &&
65 evt.io_access.is_write &&
66 evt.io_access.length == 1) {
67 int ret;
68 if (!evt.io_access.no_resume) {
69 fprintf(stderr, "should have been told not to resume\n");
70 got_error = 1;
71 }
72
73 ret = crosvm_vcpu_wait(vcpu, &evt);
74 if (ret == 0) {
75 if (evt.kind != CROSVM_VCPU_EVENT_KIND_IO_ACCESS ||
76 evt.io_access.address_space !=
77 CROSVM_ADDRESS_SPACE_IOPORT ||
78 evt.io_access.address != ASYNC_ADDRESS ||
79 !evt.io_access.is_write ||
80 !evt.io_access.no_resume ||
81 evt.io_access.length != 1) {
82 fprintf(stderr, "got unexpected wait #1 result\n");
83 got_error = 1;
84 }
85 } else {
86 fprintf(stderr, "crosvm_vcpu_wait() #1 failed: %d\n", ret);
87 got_error = 1;
88 }
89
90 ret = crosvm_vcpu_wait(vcpu, &evt);
91 if (ret == 0) {
92 if (evt.kind != CROSVM_VCPU_EVENT_KIND_IO_ACCESS ||
93 evt.io_access.address_space !=
94 CROSVM_ADDRESS_SPACE_IOPORT ||
95 evt.io_access.address != ASYNC_ADDRESS ||
96 !evt.io_access.is_write ||
97 !evt.io_access.no_resume ||
98 evt.io_access.length != 1) {
99 fprintf(stderr, "got unexpected wait #2 result\n");
100 got_error = 1;
101 }
102 } else {
103 fprintf(stderr, "crosvm_vcpu_wait() #2 failed: %d\n", ret);
104 got_error = 1;
105 }
106
107 // skip the crosvm_vcpu_resume()
108 continue;
109 }
110 if (evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
111 evt.io_access.address == KILL_ADDRESS &&
112 evt.io_access.is_write &&
113 evt.io_access.length == 1 &&
114 evt.io_access.data[0] == 1)
115 {
116 uint64_t dummy = 1;
117 write(g_kill_evt, &dummy, sizeof(dummy));
118 return NULL;
119 }
120 }
121
122 crosvm_vcpu_resume(vcpu);
123 }
124
125 return NULL;
126 }
127
main(int argc,char ** argv)128 int main(int argc, char** argv) {
129 const uint8_t code[] = {
130 /*
131 B007 mov al,0x7
132 BA0005 mov dx,0x500
133 EE out dx,al
134 EE out dx,al
135 EE out dx,al
136 BAF903 mov dx,0x3f9
137 B001 mov al,0x1
138 EE out dx,al
139 F4 hlt
140 */
141 0xb0, 0x7,
142 0xba, (ASYNC_ADDRESS & 0xFF), ((ASYNC_ADDRESS >> 8) & 0xFF),
143 0xee,
144 0xee,
145 0xee,
146 0xba, (KILL_ADDRESS & 0xFF), ((KILL_ADDRESS >> 8) & 0xFF),
147 0xb0, 0x01,
148 0xee,
149 0xf4
150 };
151
152 struct crosvm *crosvm;
153 int ret = crosvm_connect(&crosvm);
154 if (ret) {
155 fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
156 return 1;
157 }
158
159 /*
160 * Not strictly necessary, but demonstrates we can have as many connections
161 * as we please.
162 */
163 struct crosvm *extra_crosvm;
164 ret = crosvm_new_connection(crosvm, &extra_crosvm);
165 if (ret) {
166 fprintf(stderr, "failed to make new socket: %d\n", ret);
167 return 1;
168 }
169
170 /* We needs this eventfd to know when to exit before being killed. */
171 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
172 if (g_kill_evt < 0) {
173 fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
174 return 1;
175 }
176
177 ret = crosvm_reserve_async_write_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
178 ASYNC_ADDRESS, 1);
179 if (ret) {
180 fprintf(stderr, "failed to reserve async ioport range: %d\n", ret);
181 return 1;
182 }
183
184 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
185 KILL_ADDRESS, 1);
186 if (ret) {
187 fprintf(stderr, "failed to reserve kill ioport range: %d\n", ret);
188 return 1;
189 }
190
191 int mem_size = 0x2000;
192 int mem_fd = syscall(SYS_memfd_create, "guest_mem",
193 MFD_CLOEXEC | MFD_ALLOW_SEALING);
194 if (mem_fd < 0) {
195 fprintf(stderr, "failed to create guest memfd: %d\n", errno);
196 return 1;
197 }
198 ret = ftruncate(mem_fd, mem_size);
199 if (ret) {
200 fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
201 return 1;
202 }
203 uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED,
204 mem_fd, 0x1000);
205 if (mem == MAP_FAILED) {
206 fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
207 return 1;
208 }
209 fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
210 memcpy(mem, code, sizeof(code));
211
212 struct crosvm_memory *mem_obj;
213 ret = crosvm_create_memory(crosvm, mem_fd, 0x1000, 0x1000, 0x1000, false,
214 false, &mem_obj);
215 if (ret) {
216 fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
217 return 1;
218 }
219
220 /* get and creat a thread for each vcpu */
221 struct crosvm_vcpu *vcpus[32];
222 pthread_t vcpu_threads[32];
223 uint32_t vcpu_count;
224 for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
225 ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
226 if (ret == -ENOENT)
227 break;
228
229 if (ret) {
230 fprintf(stderr, "error while getting all vcpus: %d\n", ret);
231 return 1;
232 }
233 pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread,
234 vcpus[vcpu_count]);
235 }
236
237 ret = crosvm_start(extra_crosvm);
238 if (ret) {
239 fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
240 return 1;
241 }
242
243 /* Wait for crosvm to request that we exit otherwise we will be killed. */
244 uint64_t dummy;
245 read(g_kill_evt, &dummy, 8);
246
247 ret = crosvm_destroy_memory(crosvm, &mem_obj);
248 if (ret) {
249 fprintf(stderr, "failed to destroy memory in crosvm: %d\n", ret);
250 return 1;
251 }
252
253 ret = crosvm_reserve_async_write_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
254 ASYNC_ADDRESS, 0);
255 if (ret) {
256 fprintf(stderr, "failed to unreserve async ioport range: %d\n", ret);
257 return 1;
258 }
259
260 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
261 KILL_ADDRESS, 0);
262 if (ret) {
263 fprintf(stderr, "failed to unreserve kill ioport range: %d\n", ret);
264 return 1;
265 }
266
267 if (got_error) {
268 fprintf(stderr, "vm ran to completion but with an error\n");
269 return 1;
270 }
271
272 return 0;
273 }
274