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 LOAD_ADDRESS 0x1000
35 #define DATAMATCH_VAL 0x88
36 #define KILL_ADDRESS 0x4000
37
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.rflags = 2;
57 regs.rip = LOAD_ADDRESS;
58 regs.rax = DATAMATCH_VAL;
59 regs.rbx = DATAMATCH_VAL - 1;
60 crosvm_vcpu_set_regs(vcpu, ®s);
61 }
62
63 if (evt.kind == CROSVM_VCPU_EVENT_KIND_IO_ACCESS &&
64 evt.io_access.address_space == CROSVM_ADDRESS_SPACE_MMIO &&
65 evt.io_access.address == KILL_ADDRESS &&
66 evt.io_access.is_write &&
67 evt.io_access.length == 1 &&
68 evt.io_access.data[0] == 1)
69 {
70 uint64_t dummy = 1;
71 write(g_kill_evt, &dummy, sizeof(dummy));
72 return NULL;
73 }
74
75 crosvm_vcpu_resume(vcpu);
76 }
77
78 return NULL;
79 }
80
main(int argc,char ** argv)81 int main(int argc, char** argv) {
82 const uint8_t code[] = {
83 /*
84 0000 BAF803 mov dx,0x3f8
85 0003 88C3 mov bl,al
86 0005 EE out dx,al
87 0006 B000 mov al,0x0
88 0008 EE out dx,al
89 0009 88D8 mov al,bl
90 000B EE out dx,al
91 0014 26C606000001 mov byte [es:0x0],0x1
92 000C F4 hlt
93 */
94 0xba, 0xf8, 0x03,
95 0x88, 0xc3,
96 0xee,
97 0xb0, 0x00,
98 0xee,
99 0x88, 0xd8,
100 0xee,
101 0x26, 0xc6, 0x06, 0x00, 0x00, 0x01,
102 0xf4,
103 };
104
105 struct crosvm *crosvm;
106 int ret = crosvm_connect(&crosvm);
107 if (ret) {
108 fprintf(stderr, "failed to connect to crosvm: %d\n", ret);
109 return 1;
110 }
111
112 g_kill_evt = crosvm_get_shutdown_eventfd(crosvm);
113 if (g_kill_evt < 0) {
114 fprintf(stderr, "failed to get kill eventfd: %d\n", g_kill_evt);
115 return 1;
116 }
117
118 ret = crosvm_reserve_range(crosvm, CROSVM_ADDRESS_SPACE_MMIO, KILL_ADDRESS, 1);
119 if (ret) {
120 fprintf(stderr, "failed to reserve mmio range: %d\n", ret);
121 return 1;
122 }
123
124 uint8_t datamatch = DATAMATCH_VAL;
125 struct crosvm_io *io;
126 ret = crosvm_create_io_event(crosvm, CROSVM_ADDRESS_SPACE_IOPORT, 0x3f8, 1, &datamatch, &io);
127 if (ret) {
128 fprintf(stderr, "failed to create ioevent: %d\n", ret);
129 return 1;
130 }
131
132 int ioeventfd = crosvm_io_event_fd(io);
133
134 int mem_size = 0x4000;
135 int mem_fd = syscall(SYS_memfd_create, "guest_mem", MFD_CLOEXEC | MFD_ALLOW_SEALING);
136 if (mem_fd < 0) {
137 fprintf(stderr, "failed to create guest memfd: %d\n", errno);
138 return 1;
139 }
140 ret = ftruncate(mem_fd, mem_size);
141 if (ret) {
142 fprintf(stderr, "failed to set size of guest memory: %d\n", errno);
143 return 1;
144 }
145 uint8_t *mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
146 if (mem == MAP_FAILED) {
147 fprintf(stderr, "failed to mmap guest memory: %d\n", errno);
148 return 1;
149 }
150 fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK);
151 memcpy(mem + LOAD_ADDRESS, code, sizeof(code));
152
153 struct crosvm_memory *mem_obj;
154 ret = crosvm_create_memory(crosvm, mem_fd, 0, mem_size, 0, false, false, &mem_obj);
155 if (ret) {
156 fprintf(stderr, "failed to create memory in crosvm: %d\n", ret);
157 return 1;
158 }
159
160 /* get and creat a thread for each vcpu */
161 struct crosvm_vcpu *vcpus[32];
162 pthread_t vcpu_threads[32];
163 uint32_t vcpu_count;
164 for (vcpu_count = 0; vcpu_count < 32; vcpu_count++) {
165 ret = crosvm_get_vcpu(crosvm, vcpu_count, &vcpus[vcpu_count]);
166 if (ret == -ENOENT)
167 break;
168
169 if (ret) {
170 fprintf(stderr, "error while getting all vcpus: %d\n", ret);
171 return 1;
172 }
173 pthread_create(&vcpu_threads[vcpu_count], NULL, vcpu_thread, vcpus[vcpu_count]);
174 }
175
176 ret = crosvm_start(crosvm);
177 if (ret) {
178 fprintf(stderr, "failed to tell crosvm to start: %d\n", ret);
179 return 1;
180 }
181
182 uint64_t dummy;
183 read(g_kill_evt, &dummy, 8);
184
185 ret = read(ioeventfd, &dummy, sizeof(dummy));
186 if (ret == -1) {
187 fprintf(stderr, "failed to read ioeventfd: %d\n", errno);
188 return 1;
189 }
190
191 if (dummy != 2) {
192 fprintf(stderr, "ioeventfd was not triggered the expected number of times: %d\n", dummy);
193 return 1;
194 }
195
196 return 0;
197 }
198