1 /*
2 * Copyright 2017 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 <stdbool.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/eventfd.h>
17 #include <sys/mman.h>
18 #include <sys/syscall.h>
19 #include <sys/types.h>
20 #include <time.h>
21 #include <unistd.h>
22
23 #include "crosvm.h"
24
25 #ifndef F_LINUX_SPECIFIC_BASE
26 #define F_LINUX_SPECIFIC_BASE 1024
27 #endif
28
29 #ifndef F_ADD_SEALS
30 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
31 #endif
32
33 #ifndef F_SEAL_SHRINK
34 #define F_SEAL_SHRINK 0x0002
35 #endif
36
37 #define SERIAL_ADDRESS 0x3f8
38 #define KILL_ADDRESS 0x3f9
39
40 static char g_serial_out[16];
41 static int g_next_evt;
42 static int g_kill_evt;
43
44 static bool g_paused;
45 static bool g_exit_loop;
46 static pthread_mutex_t g_pause_mutex = PTHREAD_MUTEX_INITIALIZER;
47 static pthread_cond_t g_pause_cond = PTHREAD_COND_INITIALIZER;
48
49 static volatile int count;
50
vcpu_thread_fn(void * arg)51 static void *vcpu_thread_fn(void *arg) {
52 struct crosvm_vcpu *vcpu = arg;
53 struct crosvm_vcpu_event evt;
54 int i = 0;
55
56 while (crosvm_vcpu_wait(vcpu, &evt) == 0) {
57 if (evt.kind == CROSVM_VCPU_EVENT_KIND_INIT) {
58 struct kvm_sregs sregs;
59 crosvm_vcpu_get_sregs(vcpu, &sregs);
60 sregs.cs.base = 0;
61 sregs.cs.selector = 0;
62 sregs.es.base = KILL_ADDRESS;
63 sregs.es.selector = 0;
64 crosvm_vcpu_set_sregs(vcpu, &sregs);
65
66 struct kvm_regs regs;
67 crosvm_vcpu_get_regs(vcpu, ®s);
68 regs.rip = 0x1000;
69 regs.rax = 2;
70 regs.rbx = 7;
71 regs.rflags = 2;
72 crosvm_vcpu_set_regs(vcpu, ®s);
73
74 /* Signal the main thread that init is done */
75 uint64_t dummy = 1;
76 write(g_next_evt, &dummy, sizeof(dummy));
77 }
78 else if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
79 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
80 evt.io_access.address == KILL_ADDRESS &&
81 evt.io_access.is_write &&
82 evt.io_access.length == 1 &&
83 evt.io_access.data[0] == 1) {
84 uint64_t dummy = 1;
85 write(g_kill_evt, &dummy, sizeof(dummy));
86 return NULL;
87 }
88 else if (evt.kind == CROSVM_VCPU_EVENT_KIND_PAUSED) {
89 /* Signal that we paused */
90 uint64_t dummy = 1;
91 write(g_next_evt, &dummy, sizeof(dummy));
92
93 /* Wait till we can continue again */
94 pthread_mutex_lock(&g_pause_mutex);
95 while (g_paused)
96 pthread_cond_wait(&g_pause_cond, &g_pause_mutex);
97
98 /* Kick the VM from infinite loop if requested */
99 if (g_exit_loop) {
100 struct kvm_regs regs;
101 crosvm_vcpu_get_regs(vcpu, ®s);
102 regs.rbx = 1;
103 crosvm_vcpu_set_regs(vcpu, ®s);
104 }
105
106 /* Signal that we are no longer paused */
107 write(g_next_evt, &dummy, sizeof(dummy));
108
109 pthread_mutex_unlock(&g_pause_mutex);
110 }
111 crosvm_vcpu_resume(vcpu);
112 }
113
114 return NULL;
115 }
116
signal_pause(struct crosvm * crosvm)117 static int signal_pause(struct crosvm *crosvm) {
118 pthread_mutex_lock(&g_pause_mutex);
119 g_paused = true;
120 pthread_mutex_unlock(&g_pause_mutex);
121
122 return crosvm_pause_vcpus(crosvm, 1, NULL);
123 }
124
signal_unpause(struct crosvm * crosvm,bool exit_loop)125 static void signal_unpause(struct crosvm *crosvm, bool exit_loop) {
126 pthread_mutex_lock(&g_pause_mutex);
127 g_paused = false;
128 g_exit_loop = exit_loop;
129 pthread_cond_broadcast(&g_pause_cond);
130 pthread_mutex_unlock(&g_pause_mutex);
131 }
132
main(int argc,char ** argv)133 int main(int argc, char** argv) {
134 const uint8_t code[] = {
135 /*
136 0000 00D8 add al,bl
137 0002 80FB01 cmp bl, 0x1
138 0005 75F9 jne 0x0
139 0007 BAF903 mov dx,0x3f8
140 000A B001 mov al,0x1
141 000C EE out dx,al
142 000D F4 hlt
143 */
144 0x00, 0xd8,
145 0x80, 0xfb, 0x01,
146 0x75, 0xf9,
147 0xba, 0xf9, 0x03,
148 0xb0, 0x01,
149 0xee,
150 0xf4
151 };
152
153 g_next_evt = eventfd(0, 0);
154 if (g_next_evt == -1) {
155 fprintf(stderr, "failed to create eventfd: %d\n", errno);
156 return 1;
157 }
158
159 struct crosvm *crosvm;
160 int ret = crosvm_connect(&crosvm);
161 if (ret) {
162 fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
163 return 1;
164 }
165
166 /* We needs this eventfd to know when to exit before being killed. */
167 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
168 if (g_kill_evt < 0) {
169 fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
170 return 1;
171 }
172
173 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
174 KILL_ADDRESS, 1);
175 if (ret) {
176 fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
177 return 1;
178 }
179
180 int mem_size = 0x2000;
181 int mem_fd = syscall(SYS_memfd_create,
182 "guest_mem", 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, 0x1000);
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 struct crosvm_memory *mem_obj;
202 ret = crosvm_create_memory(crosvm, mem_fd, 0x1000, 0x1000, 0x1000,
203 false, false, &mem_obj);
204 if (ret) {
205 fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
206 return 1;
207 }
208
209 /* get and create a thread for the vcpu */
210 struct crosvm_vcpu *vcpu;
211 ret = crosvm_get_vcpu(crosvm, 0, &vcpu);
212 if (ret) {
213 fprintf(stderr, "error while getting vcpu: %d\n", ret);
214 return 1;
215 }
216
217 pthread_t vcpu_thread;
218 ret = pthread_create(&vcpu_thread, NULL, vcpu_thread_fn, vcpu);
219 if (ret) {
220 fprintf(stderr, "failed to createvcpu thread\n");
221 return 1;
222 }
223
224 ret = crosvm_start(crosvm);
225 if (ret) {
226 fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
227 return 1;
228 }
229
230 /* Wait till VCPU thread tells us that its initialization is done */
231 uint64_t dummy;
232 read(g_next_evt, &dummy, sizeof(dummy));
233
234 ret = signal_pause(crosvm);
235 if (ret) {
236 fprintf(stderr, "failed to pause vcpus (1st time): %d\n", ret);
237 return 1;
238 }
239
240 /* Wait till VCPU thread tells us it is paused */
241 read(g_next_evt, &dummy, sizeof(dummy));
242
243 /* Try pausing VCPUs 2nd time to make sure we do not deadlock */
244 ret = signal_pause(crosvm);
245 if (ret) {
246 fprintf(stderr, "failed to pause vcpus (2nd time): %d\n", ret);
247 return 1;
248 }
249
250 signal_unpause(crosvm, false);
251
252 /* Wait until VCPU thread tells us that it is no longer paused */
253 read(g_next_evt, &dummy, sizeof(dummy));
254
255 /*
256 * Try pausing VCPUs 3rd time to see if we will miss pause
257 * request as we are exiting previous pause.
258 */
259 ret = signal_pause(crosvm);
260 if (ret) {
261 fprintf(stderr, "failed to pause vcpus (2nd time): %d\n", ret);
262 return 1;
263 }
264
265 signal_unpause(crosvm, true);
266
267 /* Wait for crosvm to request that we exit otherwise we will be killed. */
268 read(g_kill_evt, &dummy, sizeof(dummy));
269
270 ret = crosvm_destroy_memory(crosvm, &mem_obj);
271 if (ret) {
272 fprintf(stderr, "failed to destroy memory in crosvm: %d\n", ret);
273 return 1;
274 }
275
276 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT,
277 KILL_ADDRESS, 0);
278 if (ret) {
279 fprintf(stderr, "failed to unreserve mmio range: %d\n", ret);
280 return 1;
281 }
282
283 return 0;
284 }
285