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, &regs);
68             regs.rip = 0x1000;
69             regs.rax = 2;
70             regs.rbx = 7;
71             regs.rflags = 2;
72             crosvm_vcpu_set_regs(vcpu, &regs);
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, &regs);
102                 regs.rbx = 1;
103                 crosvm_vcpu_set_regs(vcpu, &regs);
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