1.主线程初始化
- qemu层kvm初始化类
//KVM初始化的入口的类
static const TypeInfo kvm_accel_type = {
.name = TYPE_KVM_ACCEL,
.parent = TYPE_ACCEL,
.instance_init = kvm_accel_instance_init,
.class_init = kvm_accel_class_init,
.instance_size = sizeof(KVMState),
};
说明:
1.QOM模型,先执行class_init初始化函数,类似C++中构造函数,在new对象的时候调用
2.QOM模型,instance_finalize实例化函数,本类的具体实现
- 主线程kvm初始化
|-qemu_init
|-module_call_init(MODULE_INIT_QOM); //vl.c
|-kvm_type_init //初始化注册kvm类类型
|-configure_accelerators //vl.c
|-do_configure_accelerator //设置为kvm加速,比如有hax、hvf、tcg、kvm、xen、whpx不同加速等
|-accel_init_machine
|-acc->init_machine(ms); //调用kvm_init接口,这个是kvm真正初始化的入口
|-kvm_init //kvm-all.c
|-machine_run_board_init //主板初始化,此时运行状态即将设置为running或者prelaunch
|-machine_class->init
|-pc_init1 //i386/pc_piix.c 进行CPU、Memory、VGA、NIC、PCI等的初始化
|-x86_cpus_init //x86的CPU初始化---cpu虚拟化
|-pc_guest_info_init //guest信息初始化
|-smbios_set_defaults //bios设置
|-pc_memory_init //分配内存和加载rom和bios --内存虚拟化
|-pc_gsi_create //中断控制器
|-i440fx_init //初始化pci总线
|-piix3_create //初始化isa总线
|-pc_i8259_create //中断控制器初始化
|-pc_vga_init //显卡初始化
|-pc_basic_device_init //基本硬件初始化
|-pc_nic_init //网卡初始化
|-pci_create_simple //创建pci主桥
关注的两个热点:
1.x86_cpus_init //cpu虚拟化
2.pc_memory_init //内存虚拟化
- 调用堆栈
(gdb) bt
#0 pc_init1 (machine=0x555556e39800, pci_type=0x555556164c06 "i440FX", host_type=0x555556163b61 "i440FX-pcihost") at /home/cloud/qemu/qemu-5.0.0/hw/i386/pc_piix.c:74
#1 0x0000555555a4362b in machine_run_board_init (machine=0x555556e39800) at hw/core/machine.c:1140
#2 0x00005555559749ca in qemu_init (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/vl.c:4897
#3 0x000055555585cd19 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/main.c:48
2.Qemu层vcpu初始化
- x86_vcpu初始化类
static const TypeInfo x86_cpu_type_info = {
.name = TYPE_X86_CPU,
.parent = TYPE_CPU,
.instance_size = sizeof(X86CPU),
.instance_init = x86_cpu_initfn,
.abstract = true,
.class_size = sizeof(X86CPUClass),
.class_init = x86_cpu_common_class_init,
};
x86_cpu类型注册:
|-type_init //初始化的时候注册,main函数运行前
|-x86_cpu_register_types
|-x86_cpu_type_info //class_init类似构造函数
|-x86_cpu_common_class_init
|-device_class_set_parent_realize //设置cpu初始化函数realize=x86_cpu_realizefn
|-qemu_init_vcpu //开始初始化vcpu
- vcpu初始化逻辑
|-x86_cpus_init
|-x86_cpu_new //CPUState *cs = CPU(dev);
|-object_new
|-object_new_with_type
|-type_initialize
|-object_initialize_with_type
|-type_initialize
|-class_init //调用x86_cpu_type_info的x86_cpu_common_class_init初始化x86_cpu
|-object_property_set_bool
|-object_property_set_qobject
|-property_set_bool
|-device_set_realized
|-x86_cpu_realizefn //vcpu初始化的实现,先初始化struct X86CPU再初始化struct CPUState
|-qemu_init_vcpu //struct CPUState
|-qemu_kvm_start_vcpu
|-qemu_kvm_cpu_thread_fn
|-kvm_init_vcpu
|-kvm_get_vcpu //通知kvm创建一个vcpu,返回一个kvm_fd
|-kvm_ioctl //从kvm获取映射mmap_size
|-mmap //对kvm_fd进行以一个mmap_size大小映射
|-kvm_arch_init_vcpu //前面vcpu创建成功之后现在来真正的初始化
|-kvm_arch_set_tsc_khz //时钟设置
|-kvm_vcpu_ioctl //KVM_GET_TSC_KHZ时钟可以用户设置和从kvm获取,这里从KVM获取再保存下来
|-hyperv_handle_properties //设置cpuids
|-cpu_x86_cpuid //获取所有的CPUID信息,填充结构体,设置到寄存器中
|-kvm_vcpu_ioctl //前面构造KVM需要的cpuid_data数据,再通过KVM_SET_CPUID2给kvm设置cpuid
|-kvm_init_msrs //通知kvm设置msr寄存器
|-hyperv_init_vcpu //Hyper-V初始化vcpu保存一些信息在后续迁移时候恢复使用
|-kvm_init_cpu_signals
|-kvm_cpu_exec //每个vcpu对应一个线程,该线程循环执行
x86_cpu类继承关系:子类->父类
struct X86CPU -> struct CPUState -> struct DeviceState
struct X86CPUClass -> struct CPUClass
struct CPUX86State -> (struct X86CPU env)
DeviceClass 可以通过DeviceState获取得到DEVICE_GET_CLASS(dev)。
说明:
1.这里x86_cpu_new调用比较抽象,涉及QOM
2.这里cpu虚拟的具体流程还要结合kvm的实现才能分析透彻
3.先初始化父类再初始化子类,先初始化Class,再初始化Device,再初始化State如先初始化x86CPU数据结构,再去针对CPUState进行初始化,CPUState初始化结合KVM完成。CPUState记录的是和kvm交互的状态信息,CPUX86State记录的是一些寄存器、cpu状态之类信息。
- 调用堆栈
(gdb) bt
#0 x86_cpu_realizefn (dev=0x5555572bb6c0, errp=0x7fffffffca30) at /home/cloud/qemu/qemu-5.0.0/target/i386/cpu.c:6528
#1 0x0000555555a3ab11 in device_set_realized (obj=<optimized out>, value=<optimized out>, errp=0x7fffffffcb20) at hw/core/qdev.c:891
#2 0x0000555555bd744e in property_set_bool (obj=0x5555572bb6c0, v=<optimized out>, name=<optimized out>, opaque=0x555556c71020, errp=0x7fffffffcb20) at qom/object.c:2243
#3 0x0000555555bdc10f in object_property_set_qobject (obj=obj@entry=0x5555572bb6c0, value=value@entry=0x5555572d68d0, name=name@entry=0x55555619ea48 "realized", errp=errp@entry=0x7fffffffcb20) at qom/qom-qobject.c:26
#4 0x0000555555bd98d5 in object_property_set_bool (obj=0x5555572bb6c0, value=<optimized out>, name=0x55555619ea48 "realized", errp=0x7fffffffcb20) at qom/object.c:1395
#5 0x0000555555944ebf in x86_cpu_new (x86ms=x86ms@entry=0x555556e39850, apic_id=0, errp=0x555556b7e6b0 <error_fatal>) at /home/cloud/qemu/qemu-5.0.0/hw/i386/x86.c:129
#6 0x0000555555944ff9 in x86_cpus_init (x86ms=0x555556e39850, default_cpu_version=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/hw/i386/x86.c:168
#7 0x000055555594c2b9 in pc_init1 (machine=0x555556e39850, pci_type=0x555556164c06 "i440FX", host_type=0x555556163b61 "i440FX-pcihost") at /home/cloud/qemu/qemu-5.0.0/hw/i386/pc_piix.c:156
#8 0x0000555555a4362b in machine_run_board_init (machine=0x555556e39850) at hw/core/machine.c:1140
#9 0x00005555559749ca in qemu_init (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/vl.c:4897
#10 0x000055555585cd19 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/main.c:48
3.VCPU运行
由于虚拟机涉及VM-Entey和VM-exit,这里从Qemu和KVM角度来分析vcpu运行主要机制。
- Qemu
|-kvm_cpu_exec
|-kvm_arch_process_async_events
|—cpu_exec_start
|-kvm_arch_pre_run
|-kvm_vcpu_ioctl //ioctl下发KVM_RUN给KVM,进入了VM-Entry模式
|-kvm_arch_post_run
|-switch (run->exit_reason) //处理VM-Exit事件
|-kvm_handle_io
|-kvm_handle_cpuid
|-kvm_arch_handle_exit
|-......
|-kvm_arch_handle_exit
|-cpu_exec_end
1.kvm_cpu_exec中能看到一个循环,在循环中kvm_vcpu_ioctl(KVM_RUN)运行这个虚拟机,这个时候CPU进入VM-Entry即进入客户机模式。
2.如果一直是客户机的操作系统占用这个CPU,则会一直停留在这一行运行,一旦这个调用返回了,就说明CPU进入VM-Exit退出客户机模式。
3.退出客户机模式之后将CPU交还给宿主机。在循环中会对退出的原因exit_reason进行分析处理,是因为有了I/O、还是有了中断等,并做相应的处理。处理完毕之后再次循环,再次通过VM-Entry进入客户机模式,如此循环直到虚拟机正常或者异常退出。
- KVM
KVM: //virt/kvm/kvm_main.c
|-kvm_vcpu_ioctl
|-kvm_arch_vcpu_ioctl_run //arch/x86/kvm/x86.c
|-vcpu_run
|-vcpu_enter_guest
|- 判断是否需要退出,如果退出则设置退出的reason
|-kvm_x86_ops->prepare_guest_switch
|-kvm_x86_ops->run //进入guest模式
|-kvm_x86_ops->handle_exit //处理eixt,可能要退出guest模式
vcpu运行函数:
static int vcpu_run(struct kvm_vcpu *vcpu)
{
int r;
struct kvm *kvm = vcpu->kvm;
...
for (;;) {
if (kvm_vcpu_running(vcpu)) {
r = vcpu_enter_guest(vcpu);
} else {
r = vcpu_block(kvm, vcpu);
}
...
}
}
x86架构操作函数集://arch/x86/kvm/vmx/vmx.c
static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
...
.run = vmx_vcpu_run,
.handle_exit = vmx_handle_exit,
...
}
1.首先是Store host registers,要从宿主机模式变为客户机模式了,所以原来宿主机运行时候的寄存器要保存下来。
2.接下来是Load guest registers,将原来客户机运行时的寄存器加载进来。
3.接下来是Enter guest mode,调用ASM_VMX_VMLAUNCH进入客户机模型运行,或者ASM_VMX_VMRESUME恢复客户机模型运行。
4.如果客户机因为某种原因退出,Save guest registers, load host registers,即保存客户机运行的时候的寄存器,就加载宿主机运行的时候的寄存器。