用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 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, &regs) < 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}


关注本公众号,了解更多关于云计算虚拟化的知识。

原文发布于微信公众号 - 虚拟化云计算(openstack_openstack)

原文发表时间:2018-03-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐沙弥的世界

Oracle 11g RAC crs_stat 命令结果完整显示

Oracle 11g RAC中crs_stat命令较之前的版本多出了很多新的不同的资源类型,缺省情况下,使用crs_stat -t来查看资源是密密麻麻一大片,看...

30910
来自专栏A周立SpringCloud

Docker系列教程25-练习:使用Docker Compose编排WordPress博客

16220
来自专栏散尽浮华

openstack虚拟机迁移的操作记录

需求说明: 计算节点linux-node1.openstack:192.168.1.8   计算节点linux-node2.openstack:192.168....

86290
来自专栏前端萌媛的成长之路

Setting up your own Ghost theme

15820
来自专栏杨建荣的学习笔记

使用shell定制awr脚本(r3笔记第32天)

大家在做性能问题诊断的时候,awr是不可或缺的工具,使用?/rdbms/admin/awrrpt.sql可能大家使用的多了,可能有时候感觉输入参数还是有些太繁琐...

29840
来自专栏乐沙弥的世界

CRS-1006 , CRS-0215 故障一例

    安装好sles 10 sp3 + Oracle 10g RAC之后,在配置监听器时,总是提示主机bo2dbp上的监听服务已经在运行,忽略错误之后手动在b...

7530
来自专栏大数据学习笔记

Spark2.x学习笔记:15、Spark SQL的SQL

15、 Spark SQL的SQL 15.1 Spark SQL所支持的SQL语法 select [distinct] [column names]|[wild...

26480
来自专栏琯琯博客

docker-resources资源汇集相关项目博文

docker资源汇总。英文版本链接 资源汇集 书籍 第一本Docker书 (7.4分) Docker —— 从入门到实践 (内容一般) The Docker B...

53570
来自专栏Golang语言社区

使用Docker和热加载运行Go API

This is a quick discussion of how to set up a local development environment for ...

14910
来自专栏王亚昌的专栏

How to build your own ubuntu image with docker?

docker run -d -p 222:22 ubuntu-sshd-admin

12220

扫码关注云+社区

领取腾讯云代金券