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,即保存客户机运行的时候的寄存器,就加载宿主机运行的时候的寄存器。