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 <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 <time.h>
18 #include <unistd.h>
19
20 #include "crosvm.h"
21
22 #ifndef F_LINUX_SPECIFIC_BASE
23 #define F_LINUX_SPECIFIC_BASE 1024
24 #endif
25
26 #ifndef F_ADD_SEALS
27 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
28 #endif
29
30 #ifndef F_SEAL_SHRINK
31 #define F_SEAL_SHRINK 0x0002
32 #endif
33
34 #define SERIAL_ADDRESS 0x3f8
35 #define KILL_ADDRESS 0x3f9
36
37 char g_serial_out[16];
38 int g_kill_evt;
39
vcpu_thread(void * arg)40 void *vcpu_thread(void *arg) {
41 struct crosvm_vcpu *vcpu = arg;
42 struct crosvm_vcpu_event evt;
43 int i = 0;
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 == SERIAL_ADDRESS &&
65 evt.io_access.is_write &&
66 evt.io_access.length == 1) {
67 g_serial_out[i] = evt.io_access.data[0];
68 i++;
69 }
70 if (evt.io_access.address_space == CROSVM_ADDRESS_SPACE_IOPORT &&
71 evt.io_access.address == KILL_ADDRESS &&
72 evt.io_access.is_write &&
73 evt.io_access.length == 1 &&
74 evt.io_access.data[0] == 1)
75 {
76 uint64_t dummy = 1;
77 write(g_kill_evt, &dummy, sizeof(dummy));
78 return NULL;
79 }
80 }
81
82 crosvm_vcpu_resume(vcpu);
83 }
84
85 return NULL;
86 }
87
main(int argc,char ** argv)88 int main(int argc, char** argv) {
89 const uint8_t code[] = {
90 /*
91 0000 BAF803 mov dx,0x3f8
92 0003 00D8 add al,bl
93 0005 0430 add al,0x30
94 0007 EE out dx,al
95 0008 B05C mov al,0x0a
96 000A EE out dx,al
97 000B BAF903 mov dx,0x3f9
98 000E B001 mov al,0x1
99 0010 EE out dx,al
100 0011 F4 hlt
101 */
102 0xba, 0xf8, 0x03,
103 0x00, 0xd8,
104 0x04, '0',
105 0xee,
106 0xb0, '\n',
107 0xee,
108 0xba, 0xf9, 0x03,
109 0xb0, 0x01,
110 0xee,
111 0xf4
112 };
113
114 struct crosvm *crosvm;
115 int ret = crosvm_connect(&crosvm);
116 if (ret) {
117 fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
118 return 1;
119 }
120
121 /*
122 * Not strictly necessary, but demonstrates we can have as many connections
123 * as we please.
124 */
125 struct crosvm *extra_crosvm;
126 ret = crosvm_new_connection(crosvm, &extra_crosvm);
127 if (ret) {
128 fprintf(stderr, "failed to make new socket: %d\n", ret);
129 return 1;
130 }
131
132 /* We needs this eventfd to know when to exit before being killed. */
133 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
134 if (g_kill_evt < 0) {
135 fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
136 return 1;
137 }
138
139 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, SERIAL_ADDRESS, 1);
140 if (ret) {
141 fprintf(stderr, "failed to reserve ioport range: %d\n", ret);
142 return 1;
143 }
144
145 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, KILL_ADDRESS, 1);
146 if (ret) {
147 fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
148 return 1;
149 }
150
151 int mem_size = 0x2000;
152 int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
153 if (mem_fd < 0) {
154 fprintf(stderr, "failed to create guest memfd: %d\n", errno);
155 return 1;
156 }
157 ret = ftruncate(mem_fd, mem_size);
158 if (ret) {
159 fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
160 return 1;
161 }
162 uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0x1000);
163 if (mem == MAP_FAILED) {
164 fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
165 return 1;
166 }
167 fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
168 memcpy(mem, code, sizeof(code));
169
170 struct crosvm_memory *mem_obj;
171 ret = crosvm_create_memory(crosvm, mem_fd, 0x1000, 0x1000, 0x1000, false, false, &mem_obj);
172 if (ret) {
173 fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
174 return 1;
175 }
176
177 /* get and creat a thread for each vcpu */
178 struct crosvm_vcpu *vcpus[32];
179 pthread_t vcpu_threads[32];
180 uint32_t vcpu_count;
181 for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
182 ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
183 if (ret == -ENOENT)
184 break;
185
186 if (ret) {
187 fprintf(stderr, "error while getting all vcpus: %d\n", ret);
188 return 1;
189 }
190 pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, vcpus[vcpu_count]);
191 }
192
193 ret = crosvm_start(extra_crosvm);
194 if (ret) {
195 fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
196 return 1;
197 }
198
199 /* Wait for crosvm to request that we exit otherwise we will be killed. */
200 uint64_t dummy;
201 read(g_kill_evt, &dummy, 8);
202
203 ret = crosvm_destroy_memory(crosvm, &mem_obj);
204 if (ret) {
205 fprintf(stderr, "failed to destroy memory in crosvm: %d\n", ret);
206 return 1;
207 }
208
209 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, SERIAL_ADDRESS, 0);
210 if (ret) {
211 fprintf(stderr, "failed to unreserve ioport range: %d\n", ret);
212 return 1;
213 }
214
215 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, KILL_ADDRESS, 0);
216 if (ret) {
217 fprintf(stderr, "failed to unreserve mmio range: %d\n", ret);
218 return 1;
219 }
220
221 return strcmp(g_serial_out, "9\n");
222 }
223