1、QEMU创建虚拟机发起:kvm_ioctl(s, KVM_CREATE_VM, type);
KVM中kvm_dev_ioctl判断参数-》kvm_dev_ioctl_create_vm-》kvm_create_vm该函数中创建并初始化了对应qemu模拟的内存条模型kvm->memslots【kvm结构体】
2、QEMU创建vcpu发起:kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)kvm_arch_vcpu_id(cpu));
KVM中kvm_vm_ioctl判断参数-》kvm_vm_ioctl_create_vcpu-》
(1)kvm_arch_vcpu_create借助kvm_x86_ops->vcpu_create即vmx_create_vcpu完成任务:(1.1)kvm_vcpu_init初始化,主要是填充结构体【kvm_vcpu】,注意vcpu->run分派了一页内存,该函数继续kvm_arch_vcpu_init负责填充x86 CPU结构体【kvm_vcpu_arch】,该函数还kvm_mmu_create则是初始化MMU的函数,每个MMU都是vcpu独有。(1.2)分配一页给vmcs(执行vm entry的时候将vmm状态保存到vmcs的host area,并加载对应vm的vmcs guest area信息到CPU中,vm exit的时候则反之,vmcs具体结构分配由硬件实现,程序员只需要通过VMWRITE和VMREAD指令去访问)(1.3)vmx_vcpu_load加载VCPU的信息,切换到指定cpu,进入到vmx模式,将loaded_vmcs的vmcs和当前cpu的vmcs绑定到一起(1.4)vmx_vcpu_setup则是初始化vmcs内容,主要是赋值计算
(2)kvm_arch_vcpu_setup-》kvm_x86_ops->vcpu_load(vcpu, cpu)即vmx_vcpu_load,就是进入vcpu模式下准备工作。
(3)create_vcpu_fd为proc创建控制fd,让qemu使用
3、QEMU要运行vcpu发起:kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
KVM中kvm_vcpu_ioctl判断参数-》kvm_arch_vcpu_ioctl_run-》__vcpu_run-》
(1)在while循环里面调用vcpu_enter_guest进入guest模式,该函数(1.1)首先处理vcpu->requests,对应的request做处理,kvm_mmu_reload加载mmu,通过kvm_x86_ops->prepare_guest_switch(vcpu)准备陷入到guest,prepare_guest_switch实现是vmx_save_host_state,顾名思义,就是保存host的当前状态(1.2)然后加载guest的寄存器等信息,fpu,xcr0,将vcpu模式设置为guest状态,屏蔽中断响应,准备进入guest。但仍进行一次检查,vcpu->mode和vcpu->requests等,如果有问题,则恢复host状态。(1.3)kvm_guest_enter做了两件事:account_system_vtime计算虚拟机;rcu_virt_note_context_switch对rcu锁数据进行保护,完成上下文切换。(1.4)准备工作搞定,kvm_x86_ops->run(vcpu),开始运行guest,由vmx_vcpu_run实现,该函数主要是内联汇编。(1.5)vmx_vcpu_run退出后返回到vcpu_enter_guest通过hw_breakpoint_restore恢复(1.6)走到kvm_x86_ops->handle_exit(vcpu);即vmx_handle_exit处理虚拟机的退出:主要设置vcpu->run->exit_reason,让外部感知退出原因,并对应handle_exit函数集处理(有handle_task_switch进行,handle_io处理qemu的外部模拟IO等)。
(2)退回到__vcpu_run函数,在while (r > 0)中,循环受vcpu_enter_guest返回值控制,只有运行异常的时候才退出循环,否则通过kvm_resched一直运行下去。
(3)再退就到了kvm_arch_vcpu_ioctl_run函数,return到kvm_vcpu_ioctl,就ioctl返回到qemu的kvm_cpu_exec中,此时kvm run的执行也结束。
4、QEMU初始化虚拟机内存发起:kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
KVM中kvm_vm_ioctl把参数copy_from_user复制后-》kvm_vm_ioctl_set_memory_region逐层调用到__kvm_set_memory_region在KVM中建立与QEMU相对应的内存槽结构-》
(1)id_to_memslot根据qemu的内存槽号得到kvm结构下的内存槽号 kvm_memory_slot,转换关系来自id_to_index数组(在kvm_create_vm中,kvm_init_memslots_id初始化对应关系slots->id_to_index[i] = slots->memslots[i].id = i)。
(2)根据slot中的值和要设置的值,决定要操作的类别KVM_MR_CREATE/DELETE/MOVE/_FLAGS_ONLY。如果是CREATE则kvm_arch_create_memslot函数,里面主要是一个循环做了一个3级软件页表;无论删除还是移动, 先申请一个slots,把kvm->memslots暂存到这里,通过id_to_memslot获取kvm_memory_slot,并将应标记为KVM_MEMSLOT_INVALID,然后是install_new_memslots,其实就是更新了一下slots->generation的值(也就是把刚新申请的slots装载到kvm->memslots)(这里为何先不用原来slot而是申请新的slot “如果添加的section的属性变了,如从RAM变成了ROM,那么重新进行添加也是必要的”并不理解 )。