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, &regs);
56             regs.rip = 0x1000;
57             regs.rax = 2;
58             regs.rbx = 7;
59             regs.rflags = 2;
60             crosvm_vcpu_set_regs(vcpu, &regs);
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