用qemu中最少的代码实现一个kvm模拟器
时间:2022-05-07
本文章向大家介绍用qemu中最少的代码实现一个kvm模拟器,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
本文依据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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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 failedn");
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_RUNn");
108 run_ret = ioctl(cpu->kvm_fd, KVM_RUN, 0);
109 if (run_ret < 0) {
110 fprintf(stderr, "error: kvm run failed %sn",
111 strerror(-run_ret));
112 ret = -1;
113 break;
114 }
115 switch (run->exit_reason) {
116 case KVM_EXIT_IO:
117 DPRINTF("handle_ion");
118 DPRINTF("out port: %d, data: %dn",
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_mmion");
125 ret = 0;
126 break;
127 case KVM_EXIT_IRQ_WINDOW_OPEN:
128 DPRINTF("irq_window_openn");
129 ret = -1;
130 break;
131 case KVM_EXIT_SHUTDOWN:
132 DPRINTF("shutdownn");
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_errorn");
142 break;
143 case KVM_EXIT_SYSTEM_EVENT:
144 DPRINTF("system_eventn");
145 break;
146 default:
147 DPRINTF("kvm_arch_handle_exitn");
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 failedn");
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 modulen");
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 supportedn");
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 %sn", -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 threadn");
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 errorn", 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 failedn");
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 failedn");
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 failedn");
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}
关注本公众号,了解更多关于云计算虚拟化的知识。
- ASP.NET 4 AppFabric 输出缓存提供程序
- WordPress 中禁止编辑“已发布”的文章
- Windows Server 2008 R2 网络负载平衡 (NLB)资料汇总
- 0.[Andriod]之从零安装配置Android Studio并编写第一个Android App
- WordPress 后台编辑文章页面添加自定义提示文字
- 使用json 和jQuery制作级联dropdownlist
- 在64位Windows 7/2008操作系统上部署32位的Web应用程序错误
- 云计算浪潮
- 2.[Andriod]Andriod Studio结合Visual Studio Emulator for Android调试Android App
- Windows Server AppFabric Caching
- zepto 基础知识(2)
- DeepMind回顾2017年:除了战胜柯洁还有哪些大事
- [认证授权] 1.OAuth2授权
- 机器学习(四)——梯度下降算法解释以及求解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法