• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &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 == 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