本文依据qemu2.11的源码,把整个初始化和运行虚拟机的代码拿出来,完成一个可以运行的模拟器demo。从中可以很清晰的看出qemu-kvm的初始化以及虚拟机的运行过程。
编译运行:
1# gcc main.c -o qemu-kvm -lpthread
2# ./qemu-kvm /usr/share/seabios/bios.bin
代码中的数据结构和函数与qemu源码的对应关系做了注释,代码如下:
1#include <stdio.h>
2#include <stdlib.h>
3#include <fcntl.h>
4#include <inttypes.h>
5#include <pthread.h>
6#include <sys/mman.h>
7#include <linux/kvm.h>
8#include <linux/errno.h>
9#define KVM_API_VERSION 12
10#define RAM_SIZE 128000000
11#define VCPU_ID 0
12#define DPRINTF(fmt, ...) \
13 do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
14// accel/kvm/kvm-all.c KVMState
15struct KVMState {
16 int fd;
17 int vmfd;
18};
19// include/sysemu/kvm_int.h KVMSlot
20typedef struct KVMSlot
21{
22 uint64_t start_addr;
23 uint64_t memory_size;
24 void *ram;
25 int slot;
26 int flags;
27} KVMSlot;
28// include/qom/cpu.h CPUState
29// target/i386/cpu.h X86CPU
30typedef struct CPUState {
31 int kvm_fd;
32 struct kvm_run *kvm_run;
33} X86CPU;
34struct KVMState *kvm_state;
35// target/i386/kvm.c kvm_put_sregs
36static int kvm_put_sregs(X86CPU *cpu) {
37 struct kvm_sregs sregs;
38 if (ioctl(cpu->kvm_fd, KVM_GET_SREGS, &sregs) < 0) {
39 fprintf(stderr, "KVM_GET_SREGS failed\n");
40 exit(1);
41 }
42 sregs.cs.base = 0x1000;
43 if (ioctl(cpu->kvm_fd, KVM_SET_SREGS, &sregs) < 0) {
44 fprintf(stderr, "KVM_SET_SREGS failed\n");
45 exit(1);
46 }
47}
48// target/i386/kvm.c kvm_getput_regs
49static int kvm_getput_regs(X86CPU *cpu, int set) {
50 if(set) {
51 struct kvm_regs regs;
52 regs.rflags = 0x2;
53 if (ioctl(cpu->kvm_fd, KVM_SET_REGS, ®s) < 0) {
54 fprintf(stderr, "KVM_SET_REGS failed\n");
55 exit(1);
56 }
57 }
58}
59// target/i386/kvm.c kvm_arch_put_registers
60int kvm_arch_put_registers(struct CPUState *cpu) {
61 int ret = 0;
62 kvm_put_sregs(cpu);
63 kvm_getput_regs(cpu, 1);
64 return ret;
65}
66/********************************************************************/
67/*kvm-all*/
68/********************************************************************/
69// accel/kvm/kvm-all.c kvm_init_vcpu
70int kvm_init_vcpu(struct CPUState *cpu) {
71 int ret = 0;
72 long mmap_size;
73 cpu->kvm_fd = ioctl(kvm_state->vmfd, KVM_CREATE_VCPU, VCPU_ID);
74 if (cpu->kvm_fd < 0) {
75 fprintf(stderr, "kvm_create_vcpu failed\n");
76 ret = -1;
77 goto err;
78 }
79 mmap_size = ioctl(kvm_state->fd, KVM_GET_VCPU_MMAP_SIZE, 0);
80 if (mmap_size < 0) {
81 ret = mmap_size;
82 fprintf(stderr, "KVM_GET_VCPU_MMAP_SIZE failed\n");
83 goto err;
84 }
85 cpu->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
86 cpu->kvm_fd, 0);
87 if (cpu->kvm_run == MAP_FAILED) {
88 ret = -1;
89 fprintf(stderr, "mmap'ing vcpu state failed\n");
90 goto err;
91 }
92 return ret;
93err:
94 if (cpu->kvm_fd >= 0) {
95 close(cpu->kvm_fd);
96 }
97 return ret;
98}
99// accel/kvm/kvm-all.c kvm_cpu_exec
100int kvm_cpu_exec(struct CPUState *cpu)
101{
102 struct kvm_run *run = cpu->kvm_run;
103 int ret, run_ret;
104 kvm_arch_put_registers(cpu);
105 do{
106 sleep(1);
107 DPRINTF("start KVM_RUN\n");
108 run_ret = ioctl(cpu->kvm_fd, KVM_RUN, 0);
109 if (run_ret < 0) {
110 fprintf(stderr, "error: kvm run failed %s\n",
111 strerror(-run_ret));
112 ret = -1;
113 break;
114 }
115 switch (run->exit_reason) {
116 case KVM_EXIT_IO:
117 DPRINTF("handle_io\n");
118 DPRINTF("out port: %d, data: %d\n",
119 run->io.port,
120 *(int *)((char *)run + run->io.data_offset));
121 ret = 0;
122 break;
123 case KVM_EXIT_MMIO:
124 DPRINTF("handle_mmio\n");
125 ret = 0;
126 break;
127 case KVM_EXIT_IRQ_WINDOW_OPEN:
128 DPRINTF("irq_window_open\n");
129 ret = -1;
130 break;
131 case KVM_EXIT_SHUTDOWN:
132 DPRINTF("shutdown\n");
133 ret = -1;
134 break;
135 case KVM_EXIT_UNKNOWN:
136 fprintf(stderr, "KVM: unknown exit, hardware reason %" PRIx64 "\n",
137 (uint64_t)run->hw.hardware_exit_reason);
138 ret = -1;
139 break;
140 case KVM_EXIT_INTERNAL_ERROR:
141 DPRINTF("internal_error\n");
142 break;
143 case KVM_EXIT_SYSTEM_EVENT:
144 DPRINTF("system_event\n");
145 break;
146 default:
147 DPRINTF("kvm_arch_handle_exit\n");
148 break;
149 }
150 }while (ret == 0);
151 return ret;
152}
153// accel/kvm/kvm-all.c kvm_destroy_vcpu
154int kvm_destroy_vcpu(struct CPUState *cpu) {
155 int ret = 0;
156 long mmap_size;
157 mmap_size = ioctl(kvm_state->fd, KVM_GET_VCPU_MMAP_SIZE, 0);
158 if (mmap_size < 0) {
159 ret = mmap_size;
160 fprintf(stderr, "KVM_GET_VCPU_MMAP_SIZE failed\n");
161 goto err;
162 }
163 ret = munmap(cpu->kvm_run, mmap_size);
164 if (ret < 0) {
165 goto err;
166 }
167err:
168 close(cpu->kvm_fd);
169 return ret;
170}
171// vl.c main ->
172// cccel/accel.c configure_accelerator -> accel_init_machine ->
173// accel/kvm/kvm-all.c init_machine -> kvm_init
174static int kvm_init() {
175 int ret;
176 //open /dev/kvm
177 kvm_state->fd = open("/dev/kvm", O_RDWR);
178 if (kvm_state->fd < 0) {
179 fprintf(stderr, "Could not access KVM kernel module\n");
180 return -1;
181 }
182 //check api version
183 if (ioctl(kvm_state->fd, KVM_GET_API_VERSION, 0) != KVM_API_VERSION) {
184 fprintf(stderr, "kvm version not supported\n");
185 return -1;
186 }
187 //create vm
188 do {
189 ret = ioctl(kvm_state->fd, KVM_CREATE_VM, 0);
190 } while (ret == -EINTR);
191 if (ret < 0) {
192 fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %s\n", -ret,
193 strerror(-ret));
194 return -1;
195 }
196 kvm_state->vmfd = ret;
197}
198// accel/kvm/kvm-all.c kvm_set_user_memory_region
199static int kvm_set_user_memory_region(KVMSlot *slot) {
200 int ret = 0;
201 struct kvm_userspace_memory_region mem;
202 mem.flags = slot->flags;
203 mem.slot = slot->slot;
204 mem.guest_phys_addr = slot->start_addr;
205 mem.memory_size = slot->memory_size;
206 mem.userspace_addr = (unsigned long)slot->ram;
207 ret = ioctl(kvm_state->vmfd, KVM_SET_USER_MEMORY_REGION, &mem);
208 return ret;
209}
210/********************************************************************/
211/*cpus*/
212/********************************************************************/
213// cpus.c qemu_kvm_cpu_thread_fn
214static void *qemu_kvm_cpu_thread_fn(void *arg)
215{
216 int ret = 0;
217 struct CPUState *cpu = arg;
218 ret = kvm_init_vcpu(cpu);
219 if (ret < 0) {
220 fprintf(stderr, "kvm_init_vcpu failed: %s", strerror(-ret));
221 exit(1);
222 }
223 kvm_cpu_exec(cpu);
224 kvm_destroy_vcpu(cpu);
225}
226// cpus.c qemu_kvm_start_vcpu
227void qemu_kvm_start_vcpu(struct CPUState *vcpu) {
228 pthread_t vcpu_thread;
229 if (pthread_create(&(vcpu_thread), (const pthread_attr_t *)NULL,
230 qemu_kvm_cpu_thread_fn, vcpu) != 0) {
231 fprintf(stderr, "can not create kvm cpu thread\n");
232 exit(1);
233 }
234 pthread_join(vcpu_thread, NULL);
235}
236// hw/i386/pc_piix.c DEFINE_I440FX_MACHINE -> pc_init1 ->
237// hw/i386/pc.c pc_cpus_init -> pc_new_cpu ->
238// target/i386/cpu.c x86_cpu_realizefn ->
239// cpus.c qemu_init_vcpu
240void qemu_init_vcpu(struct CPUState *cpu) {
241 qemu_kvm_start_vcpu(cpu);
242}
243/********************************************************************/
244/*main*/
245/********************************************************************/
246// hw/core/loader.c rom_add_file
247int rom_add_file(uint64_t ram_start, uint64_t ram_size, char *file) {
248 int ret = 0;
249 int fd = open(file, O_RDONLY);
250 if (fd == -1) {
251 fprintf(stderr, "Could not open option rom '%s'\n", file);
252 ret = -1;
253 goto err;
254 }
255 int datasize = lseek(fd, 0, SEEK_END);
256 if (datasize == -1) {
257 fprintf(stderr, "rom: file %-20s: get size error\n", file);
258 ret = -1;
259 goto err;
260 }
261 if (datasize > ram_size) {
262 fprintf(stderr, "rom: file %-20s: datasize=%d > ramsize=%zd)\n",
263 file, datasize, ram_size);
264 ret = -1;
265 goto err;
266 }
267 lseek(fd, 0, SEEK_SET);
268 int rc = read(fd, ram_start, datasize);
269 if (rc != datasize) {
270 fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
271 file, rc, datasize);
272 ret = -1;
273 goto err;
274 }
275err:
276 if (fd != -1)
277 close(fd);
278 return ret;
279}
280int mem_init(struct KVMSlot *slot, char *file) {
281 slot->ram = mmap(NULL, slot->memory_size, PROT_READ | PROT_WRITE,
282 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
283 -1, 0);
284 if ((void *)slot->ram == MAP_FAILED) {
285 fprintf(stderr, "mmap vm ram failed\n");
286 return -1;
287 }
288 //set vm's mem region
289 if (kvm_set_user_memory_region(slot) < 0) {
290 fprintf(stderr, "set user memory region failed\n");
291 return -1;
292 }
293 //load binary to vm's ram
294 if (rom_add_file((uint64_t)slot->ram, slot->memory_size, file) < 0) {
295 fprintf(stderr, "load rom file failed\n");
296 return -1;
297 }
298}
299int main(int argc, char **argv) {
300 kvm_state = malloc(sizeof(struct KVMState));
301 struct CPUState *vcpu = malloc(sizeof(struct CPUState));
302 struct KVMSlot *slot = malloc(sizeof(struct KVMSlot));
303 slot->memory_size = RAM_SIZE;
304 slot->start_addr = 0;
305 slot->slot = 0;
306 kvm_init();
307 mem_init(slot, argv[1]);
308 qemu_init_vcpu(vcpu);
309 munmap((void *)slot->ram, slot->memory_size);
310 close(kvm_state->vmfd);
311 close(kvm_state->fd);
312 free(slot);
313 free(vcpu);
314 free(kvm_state);
315}
关注本公众号,了解更多关于云计算虚拟化的知识。