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

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