crosvm Hypervisor:创建虚拟机

Architecture

根据架构文档,在main.rs启动一个典型的crosvm,首先解析命令行参数,然后构建一个”config”结构, linux.rs中的run_config使用config组装和执行VM:
1.从elf文件加载kernel
2.为虚拟设备创建控制socket
3.构建构架相关的VM builder “Arch::build_vm”(`x86_64/src/lib.rs` or `aarch64/src/lib.rs`)
4.Arch::build_vm中创建调用linux.rs中create_devices
5.create_devices创建每个PCI设备,包含我们在config中配置的virtio设备
6.Arch::generate_pci_root如果开启minijail,可以劫持PCI设备并与之通信
7.一旦VM构建完成,VCPUs和control loop使用RunnableLinuxVm结构响应请求,直到关机.

概念介绍

GuestMemory

GuestMemory与VolatileMemory,VolatileSlice,MemoryMapping,SharedMemory相似,都是用于crosvm与Guest内存交互,他们各有使用场景:
GuestMemory:可以用于发送的对所有Guest内存的引用。它可以自由克隆,但底层Guest内存始终相同。在内部,它是使用“MemoryMapping”和“SharedMemory”实现的.GuestMemory被映射到Host地址空间,但它是非连续的。设备内存(如映射的DMA BUF)不在GuestMemory范围内.

模拟设备

CMOS/RTC - Used to get the current calendar time.
i8042 - Used by the guest kernel to exit crosvm.
serial - x86 I/O port driven serial devices that print to stdout and take input from stdin.

VirtIO device

balloon - Allows the host to reclaim the guest's memories.
block - Basic read/write block device.
console - Input and outputs on console.
fs - Shares file systems over the FUSE protocol.
gpu - Graphics adapter.
input - Creates virtual human interface devices such as keyboards.
iommu - Emulates an IOMMU device to manage DMA from endpoints in the guest.
net - Device to interface the host and guest networks.
p9 - Shares file systems over the 9P protocol.
pmem - Persistent memory.
rng - Entropy source used to seed guest OS's entropy pool.
snd - Encodes and decodes audio streams.
tpm - Creates a TPM (Trusted Platform Module) device backed by libtpm2 simulator.
video - Encodes and decodes video streams.
wayland - Allows the guest to use the host's Wayland socket.
vsock - Enables use of virtual sockets for the guest.
vhost-user - VirtIO devices which offloads the device implementation to another process through the vhost-user protocol
vmm side: Shares its virtqueues.
device side: Consumes virtqueues.

Protected_vm

说明:crosvm对pKVM的支持
现状:crosvm使能pKVM启动失败
结论:内核还不支持ARM pKVM
/* 带--protected_vm参数 */
./crosvm-nopci run --disable-sandbox --protected_vm --rwroot ./rootfs-ext4-5m.img --rwdisk disk-1g.img ./Image
流程 :hypervisor/src/kvm/aarch64.rs :102
build_vm 
--> enable_protected_vm
    --> self.check_capability(VmCap::Protected) 
        --> check_raw_capability
    -->  ioctl(KVM_CHECK_EXTENSION(),KVM_CAP_ARM_PROTECTED_VM)
内核virt/kvm/kvm_main.c没有这个ioctl接口

crosvm流程

主要有以下流程:
1.crosvm_main. 解析crosvm命令行入参.
2.kvm_run. 解析crosvm run之后的命令行入参.
3.run_config. 创建虚拟机内存,创建虚拟机,打开内核镜像,创建VGICV3虚拟化.中断控制器.
4.run_control. 创建vcpu线程,监听多种消息,虚拟机退出后资源回收..
5.run_vcpu. 依赖KVM_RUN运行虚拟机,并处理VmExit事件.

crosvm_main

主要功能:初始化日志,注册panic调测,解析入参
syslog::init   #系统日志初始化,模拟环境中,需要先后台启动syslogd
set_panic_hook #保证系统panic后将panic信息和栈打到syslog,生成minidump. 
match args #解析crosvm命令行第一个入参,如crosvm run ...
ballon
create_qcow2
disk
...
run => run_vm  #根据入参运行虚拟机

run_vm

主要功能:解析run之后的入参,最终调用run_config
crosvm::run_vm() main.rs:2160 #解析run后入参,常见入参如下:
     #--disable-sandbox --rwroot rootfs-5m.img vmlinux
     #--c vcpu个数,如--cpus=4
     #--cpu-affinity 设置cpu亲和,如--cpu-affinity=0=0:1=1:2=2:3=3
     #--per-vm-core-scheduling 所有vcpu共享相同cookie的core调度
     #no-smt Guest禁用SMT
     #rt-cpus vcpu运行的cpu范围,默认关闭
     #delay-rt 不设置VCPUs实时
     #--m Guest内存大小,默认256M
     #hugepage Guest内核使用大页
     #--r 根文件系统
     #--rwroot 可写的根文件系统
     #--d 磁盘镜像路径
     #--rwdisk 可写的磁盘镜像路径,使用/dev/vdb挂载
 #pstore pstore buffer
 #host_ip host ip地址
 #netmask VM子网掩码
 #mac VM Mac地址
 #net-vq-pairs virtio net virtual queue pairs
 #serial 串口设备
 #sound Vios server socket for setting up virtio-snd devices
 #s 控制socket,用于关闭crosvm
 #disable-sandbox 所有设备运行在一个进程,没有沙箱
 #gpu 设置virtio-gpu设备显示
 #gdb gdb在指定端口
 #vfio sysfs路径下pci设备或mdev设备
 .....
     --> run_config() src/main.rs:2397   

run_config

主要功能:打开内核;创建Guest内存;创建VM;创建中断控制器;调研run_vm.
crosvm::platform::run_config linux.rs:2432 
       setup_vm_component #解析配置
           #解析是否有initrd,当前没有
           #解析是否有kernel,当前用vmlinux,并只读打开
           #cfg.protected_vm 64M 决定后面内存策略是否使用MLOCK
           #创建一个VmComponent结构体,包含Image,Vcpu等,内存布局要用
       Arch:guest_memory_layout #创建Guest内存布局,ARM64返回DRAM 2G开始后的256M
       GuestMemory:new #创建256M Guest内存,映射。概念参考6.2.1
       set_memory_policy #大页,内存锁,实际都没设置
       Kvm:new_with_path #打开/dev/kvm设备
       KvmVm:new #依赖内核创建VM
           --> KVM_CREATE_VM
      irq_chip #创建irq芯片,依赖内核和架构
          KvmKernelIrqChip::new #创建irq控制器
          create_device #创建ArmVgicV3设备 (ARM64)
              --> KVM_CREATE_DEVICE 
          --> KVM_SET_DEVICE_ATTR #设置dist,redist地址,最多64中断
              ioctl(KVM_CREATE_IRQCHIP) #创建kernel中irq控制器
          create_pit(X86)
              ioctl(KVM_CREATE_PIT2)
          get_ioapic_num_pins #返回KVM_CAP_IOAPIC_NUM_PINS
              ioctl(KVM_CHECK_EXTENSION)
      --> run_vm::<KvmVcpu, KvmVm>

run_vm

主要功能:创建各种tube,解析rootfs,创建设备,build_vm,run_control
crosvm::platform::run_vm src/linux.rs:2507
    control_server_socket #创建/run/crosvm.sock,可在对端关闭crosvm
create tubes #创建各种tube,wayland/vhost_gpu/ballon/disk/pmem
               #/gpu_host/ioapic_host/fs_device.其中ballon tube       
               #设置超时时间100ms,在create_devices时用
create_system_allocator #创系统内存分配器,用于管理地址和irq号
                             #(X86)维护以下(基址,尺寸)
                             ##high mmio (0x100000000:4G,508G)
                             ##low mmio  (0xD0000000:3328M,640M)
                             ##io addr   (0xc000:48k,64K)
                             ##first irq:9
                             #(ARM64)维护以下(基址,尺寸)
                             ##high mmio (0x90800000:太大)
                             ##low mmio  (0x1010000:16M,1M)
                             ##plat mmio (0x90000000:8M)
                             ##first irq:3
crosvm::platform::create_devices #创建各种virtio设备,后面介绍
VirtioPciDevice #给上文中的设备创virtio pci设备,加入devices列表
    generate_acpi #给上文创的device生成ACPI table,共4个
    register_pci_device  #注册PCI bridge设备和BAR,模拟环境注释了此函数
    arch::build_vm src/linux.rs:2687 #与架构相关,后面介绍
    crosvm::platform::run_control #组装和运行VCPU,后面介绍

create_devices

主要功能:创建基础的VIRTIO设备:block,rng,ballon
crosvm::platform::create_devices src/linux.rs:1702
    create_virtio_devices #创建virtio设备
    console_device              #没创
    vhost_user_block_device   #没创
    vhost_user_console_device #没创
    pmem_device                  #没创
    vhost_user_net_device      #没创
    vinput_device                #没创
    vhost_user_gpu_device      #没创
    video-decoder                #没创
    video-encoder                #没创
    wayland_device               #没创
    sound_device                 #没创
    ...
   create_block_device #创了,用于加载rootfs并解析
   create_rng_device #创了,向GuestOS暴露entropy熵的virtio设备
   create_balloon_device #创了,内存气球设备
       #创了的设备都会加到devs列表
   VirtioPciDevice #为上述创建的设备创建virtio pci设备,加入devices列表

build_vm

主要功能:创造一个RunnableLinuxVm,拥有运行linux元素:cmdline,设置内核加载地址,dtb(Arm64),image和e820表(X86),
功能类似UBOOT,与架构相关.
build_vm & X86
build_vm x86_64/src/lib.rs:385
    #获取vcpu_count为1
    vm.set_tss_addr #设置VM TSS地址0xFFFBD000
    #创建mmio_bus,io_bus
    generate_pci_root #为VM创建pci设备树 arch/src/lib.rs:490
        #申请PCI设备,在low MMIO区域申请一块,设置irq,注册ioevent
        #创建新线程?
    io_bus.insert #创建pci_bus并加到io_bus
    #创建suspend_evt,用于向crosvm通知guest os suspend
    setup_serial_devices #设置串口设备
    setup_acpi_devices #设置acpi设备
get_base_linux_cmdline #获取该架构下一个最小cmdline为”reboot=k  
                            #panic=-1 pcie_ports=native”
    #给cmdline加”acpi=noirq”
    #给cmdline加串口参数”console=ttyS0”
    #给cmdline加额外内核参数”root=/dev/vda rw”
    #经拼接,cmdline最终为:
$44 = kernel_cmdline::Cmdline {line: "reboot=k panic=-1 pcie_ports=native acpi=noirq console=ttyS0 root=/dev/vda rw", capacity: 1966080} 
    load_kernel #从vmlinux加载kernel,KERNEL_START_OFFSET为0x200000(2M),最终结果是将kernel 加载到guest mem region,保存kernel_end地址(44M).
    setup_system_memory #在启动vcpu线程前配置系统内存空间(涉及内存尺寸(256M),cmdline(78字节),initrd,kernel_end,boot_params)
    load_cmdline #将cmdline写到guest mem中CMDLINE_OFFSET(0x20000 128K)
    #在kernel(0x2C2C000)后找第一块空闲区域,用于加载dtb或initrd
    #配置e820表
    #将kernel信息,cmdline信息写到ZERO_PAGE_OFFSET(0x7000 28K)处
如此就组装完成一个X86 RunnableLinuxVm.
build_vm & Arm64
主要功能:创建并初始化VCPU,初始化VGIC,PVTIME,PMU,cmdline,设置kernel,fdt地址到虚拟机内存和寄存器,创建fdt
build_vm aarch64/src/lib.rs:255
load_image #加载kernel
create_vcpu #依赖内核创建vcpu
     --> KVM_CREATE_VCPU
configure_vcpu_early #配置VCPU,包含psci、pmu、pvtime
    vcpu.init
        --> KVM_ARM_VCPU_INIT #设置psciv0_2,pmuv3,poweroff 
    set_one_reg 
        --> KVM_SET_ONE_REG #设置pstate,mask所有vcpu中断
        #PSTATE保存当前CPU状态信息的一组寄存器,.I可以屏蔽中断
        --> KVM_SET_ONE_REG #设置kernel地址到x1(pKVM)/pc
        --> KVM_SET_ONE_REG #设置fdt地址到x0
        --> KVM_SET_ONE_REG #设置image尺寸到x2(pKVM)
    has_pvtime_support #检查内核是否支持pvtime
    irq_chip.finalize #初始化gic
    add_memory_region #设置PVTIME地址
        set_user_memory_region
    vcpu.init_pmu #设置VCPU PMU_V3 IRQ 为23
    vcpu.init_pvtime #设置pvtime_ipa=0x7FDF0000
add_arch_devs #创建早期平台设备
    register_irq_event #为RTC注册一个可以触发GSI中断的事件
    register_irq_event #为串口设备注册SPI中断
    vcpus[0].get_psci_version() #获取psci版本
get_serial_cmdline #”panic=-1 console=ttyS0 root=/dev/vda rw”
create_fdt #创建fdt,包含内核需要的所有内容,并加载到guest内存(2G+253mM)位置
    #创建每节点,包含interrupt/cpus/chosen/memory/psci/pci等等
    #将fdt写到Guest地址为:AARCH64_PHYS_MEM_START(0x80000000:2G) + offset(0xFDF0000:253M)
/* 附 crosvm arm64 dts */
/dts-v1/;
/ {
        interrupt-parent = <0x01>;
        compatible = "linux,dummy-virt";
        #address-cells = <0x02>;
        #size-cells = <0x02>;

        chosen {
                linux,pci-probe-only = <0x01>;
                bootargs = "panic=-1 console=ttyS0 root=/dev/vda rw";
                stdout-path = "/U6_16550A@3f8";
                kaslr-seed = <0x00 0x00>;
        };

        memory {
                device_type = "memory";
                reg = <0x00 0x80000000 0x00 0x10000000>;
        };

        cpus {
                #address-cells = <0x01>;
                #size-cells = <0x00>;
                cpu@0 {
                        device_type = "cpu";
                        compatible = "arm,arm-v8";
                        reg = <0x00>;
                        phandle = <0x100>;
                };
        };

        intc {
                compatible = "arm,gic-v3";
                #interrupt-cells = <0x03>;
                interrupt-controller;
                reg = <0x00 0x3fff0000 0x00 0x10000 0x00 0x3ffd0000 0x00 0x20000>;
                phandle = <0x01>;
                #address-cells = <0x02>;
                #size-cells = <0x02>;
        };

        timer {
                compatible = "arm,armv8-timer";
                interrupts = <0x01 0x0d 0x108 0x01 0x0e 0x108 0x01 0x0b 0x108 0x01 0x0a 0x108>;
                always-on;
        };

        U6_16550A@3f8 {
                compatible = "ns16550a";
                reg = <0x00 0x3f8 0x00 0x08>;
                clock-frequency = <0x1c2000>;
                interrupts = <0x00 0x00 0x01>;
        };

        U6_16550A@2f8 {
                compatible = "ns16550a";
                reg = <0x00 0x2f8 0x00 0x08>;
                clock-frequency = <0x1c2000>;
                interrupts = <0x00 0x02 0x01>;
        };

        U6_16550A@3e8 {
                compatible = "ns16550a";
                reg = <0x00 0x3e8 0x00 0x08>;
                clock-frequency = <0x1c2000>;
                interrupts = <0x00 0x00 0x01>;
        };

        U6_16550A@2e8 {
                compatible = "ns16550a";
                reg = <0x00 0x2e8 0x00 0x08>;
                clock-frequency = <0x1c2000>;
                interrupts = <0x00 0x02 0x01>;
        };

        psci {
                compatible = "arm,psci-1.0\0arm,psci-0.2";
                method = "hvc";
        };

        pci {
                compatible = "pci-host-cam-generic";
                device_type = "pci";
                ranges = <0x3000000 0x00 0x1010000 0x00 0x1010000 0x00 0x100000 0x3000000 0x00 0x90800000 0x00 0x90800000 0xffffffff 0x6f7fffff>;
                bus-range = <0x00 0x00>;
                #address-cells = <0x03>;
                #size-cells = <0x02>;
                reg = <0x00 0x10000 0x00 0x1000000>;
                #interrupt-cells = <0x01>;
                interrupt-map = <0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x2000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04>;
                interrupt-map-mask = <0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07 0xf800 0x00 0x00 0x07>;
                dma-coherent;
        };

        pclk@3M {
                #clock-cells = <0x00>;
                compatible = "fixed-clock";
                clock-frequency = <0x2fefd8>;
                phandle = <0x18>;
        };

        rtc@2000 {
                compatible = "arm,primecell";
                arm,primecell-periphid = <0x41030>;
                reg = <0x00 0x2000 0x00 0x1000>;
                interrupts = <0x00 0x01 0x04>;
                clocks = <0x18>;
                clock-names = "apb_pclk";
        };
};  

run_control

主要功能:组装vcpu并运行vcpu线程,在主线程loop中监听tube消息并处理
crosvm::platform::run_control src/linux.rs:2936
    #设置终端raw模式
    #将exit_evt,suspend_evt,sigchld_fd,socket_server,VmControl等加入到wait.ctx,后面在loop中会监听这些事件
setup_vcpu_signal_handler #设置vcpu信号处理回调
    register_rt_signal_handler #调用sigaction对SIGRTMIN进行回调
    #设置vcpu scheduling/affinity;当前默认只有一个vcpu,id=0
--> crosvm::platform::run_vcpu #创建并运行crosvm_vcpu线程,线程
     #创建完,主线程继续执行loop流程,crosvm_vcpu0线程参考run_vcpu
    #保存vcpu线程句柄
    Wait:loop #在主线程开启loop,等待wait.ctx中的事件
           #处理delay_irq事件,实际空实现
           #处理从wait.ctx中监听的以下6类event
           1. Exit:#打印vcpu关闭,退出loop
           2. Suspend:#给每个VCPU发消息,保存状态到VcpuControl
           3. ChildSignal: #打印所有siginfo fd,退出loop
           4. IrqFd: service_irq_event
           5. VmControlServer: #把control socket加入accept并wait_ctx
           6. VmControl: #对control_tubes里事件进行处理,有5类
               a. Vm(tube)#返回VM状态和其他状态
               b. VmMemory(tube)#用于申请内存
               c. VmIrq(tube)#申请一个MSI中断号
               d. VmMsync(tube)#同步内存
               e. Fs(tube)#申请内存,映射
           ...
  #当VM退出,也就是loop结束,开始资源回收
  vm_control_indices_to_remove #回收control_tube
  kick_all_vcpu #并通知VcpuControl状态Exiting
  pthread_join #回收vcpu线程资源
  mem::drop #销毁VM结构体,设备清理
模拟举例

举个VmControl:VmIrq例子:
Linux VM启动过程中vcpu线程VIRTIO-PCI设备需要写configure register,先触发VmExit退出到vcpu线程,vcpu线程处理IoOut事件write,write_config_register触发VmIrq消息发送到到crosvm主线程,主线程loop中对该事件进行处理,而vcpu线程等待tube:recv。
crosvm-vcpu0线程栈:
crosvm主线程loop收到事件
crosvm主线程处理该请求,申请一个gsi:13中断,通过tube.send()发送response到vcpu线程,接着VM继续运行.

TaggedControlTube::VmIrq --> request.execute -->
    pub fn execute<F>(&self, set_up_irq: F, sys_allocator: &mut SystemAllocator) -> VmIrqResponse
    where
        F: FnOnce(IrqSetup) -> Result<()>,
    {
        use self::VmIrqRequest::*;
        match *self {
            AllocateOneMsi { ref irqfd } => {
                if let Some(irq_num) = sys_allocator.allocate_irq() {
                    match set_up_irq(IrqSetup::Event(irq_num, irqfd)) {
                        Ok(_) => VmIrqResponse::AllocateOneMsi { gsi: irq_num },
                        Err(e) => VmIrqResponse::Err(e),
                    }
                }
            }
} 

run_vcpu

主要功能:创建vcpu线程,运行虚拟机,处理VmExit事件
#入参20多个,cpu_id=0,vcpu_id=0,vcpus=1,run_rt(关),delay_rt(关)等特性
crosvm::platform::run_vcpu src/linux.rs:2082
#创建thread,名称为crosvm_vcpu0,下面流程都在新线程里
#VCPU线程必须能被触发exit_evt事件
#此时gdb info threads;thread n我们切到新创的thread
runnable_vcpu #设置vcpu,并将其转换为runnable vcpu,调用很多IOCTL 
    create_vcpu #创建vcpu_id=0 (X86)
        ioctl_with_val(self, KVM_CREATE_VCPU()..)
    add_vcpu #将vcpu加到irq chip
    configure_vcpu #配置vcpu
    enable_per_vm_core_scheduling #使能core scheduling
#设置vcpu的thread id
#设置VM状态Running
loop #开启vcpu循环,主要处理消息和CPU状态  
    vcpu.run(&vcpu_run_handle) #hypervisor/src/kvm/mod.rs:885
                                    #运行vm,run里也会处理事件
        ioctl(KVM_RUN()) #运行VM,从kvm_run内存结构读取run结果
        run.exit_reason  #处理exit_reason,准备相关数据后
                            #交由vcpu loop继续处理
            KVM_EXIT_IO:
            KVM_EXIT_MMIO:
            KVM_EXIT_IOAPIC_EOI:
            KVM_EXIT_HYPERV:
            ...
    #当VcpuExit发生,模拟以下事件:
        IoIn:            #io_bus读指定内容
        IoOut:           #io_bus写指定内容
        MmioRead:       #mmio_bus读指定内容
        MmioWrite:      #mmio_bus写指定内容
        IoapicEoi:      #在指定vcpu broadcast eoi中断
        IrqWindowOpen: #空
        Hlt:             #通知irq chip指定vcpu挂住     
        Shutdown:       #退出loop
        FailEntry:      #打印并退出loop
        SyetemEvent:    #退出loop
        ...
    inject_interrupts(&vcpu) #完成模拟后,中断注入vcpu,空实现
    继续循环...
模拟举例
举个模拟MmioRead事件的例子:
Linux VM启动过程中PCI设备初始化,触发VmExit,流程回到vcpu线程
vcpu线程从内核获取exit_reason为:KVM_EXIT_MMIO,准备mmio addr和size.

let run = unsafe { &*(self.run_mmap.as_ptr() as *const kvm_run) };
match run.exit_reason {
    KVM_EXIT_MMIO => {
        let mmio = unsafe { &run.__bindgen_anon_1.mmio };
        let address = mmio.phys_addr;
        let size = min(mmio.len as usize, mmio.data.len());
        if mmio.is_write != 0 {
            Ok(VcpuExit::MmioWrite {
                address,
                size,
                data: mmio.data,
            })
        } else {
            Ok(VcpuExit::MmioRead { address, size })
        }
    }  

Vcpu线程loop中,继续处理MmioRead:从mmio_bus读取指定内容,通过vcpu.set_data写回去,inject_interrupts(空实现),继续loop循环,vcpu.run继续运行虚拟机.

           match vcpu.run(&vcpu_run_handle) { 
               Ok(VcpuExit::MmioRead { address, size }) => {
                    let mut data = [0; 8];
                    mmio_bus.read(address, &mut data[..size]);
                    // Setting data for mmio can not fail.
                    let _ = vcpu.set_data(&data[..size]);
                }  

crosvm & Ioctl

crosvm创建虚拟机阶段与内核KVM/pKVM交互。
在crosvm主线程和crosvm-vcpu线程中主要通过以下ioctl接口与内核交互 (ARM64斜体区分)
  • 在crosvm主线程中
crosvm::crosvm_main
crosvm::run_vm
run_config
hypervisor::kvm::KvmVm::new 
    get_vm_type(ARM64)
        --> KVM_CHECK_EXTENSION 获取ARM_VM_IPA_SIZE
        --> KVM_CREATE_VM
        hypervisor::kvm::set_user_memory_region #256M,实现GPA<->HVA映射关系
            --> KVM_SET_USER_MEMORY_REGION
KvmKernelIrqChip::new()
    create_device(ARM64)
        --> KVM_CREATE_DEVICE #创建ArmVgicV3
    --> KVM_SET_DEVICE_ATTR #设置dist,redist地址,支持64个中断
        --> KVM_CREATE_IRQCHIP(X86)
        create_pit
            --> KVM_CREATE_PIT2
        get_ioapic_num_pins,返回KVM_CAP_IOAPIC_NUM_PINS
            -->KVM_CHECK_EXTENSION
    run_vm
    build_vm
            *check_capability (ARM64)
                --> KVM_CHECK_EXTENSION
            create_vcpu 
                --> KVM_CREATE_VCPU
            configure_vcpu_early
                vcpu.init
                    --> KVM_ARM_PREFERRED_TARGET
                    --> KVM_ARM_VCPU_INIT #设置psciv0_2,pmuv3,poweroff 
                set_one_reg 
                    --> KVM_SET_ONE_REG #设置pstate,mask所有vcpu中断
                    --> KVM_SET_ONE_REG #设置kernel地址到x1(pKVM)/pc
                    --> KVM_SET_ONE_REG #设置fdt地址到x0
                    --> KVM_SET_ONE_REG #设置image尺寸到x2(pKVM)
            has_pvtime_support #检查内核是否支持pvtime
                --> KVM_HAS_DEVICE_ATTR 
            irq_chip.finalize #初始化gic
                --> KVM_SET_DEVICE_ATTR 
            add_memory_region #设置PVTIME地址
                set_user_memory_region #实现HPA<->HVA映射关系
                    --> KVM_SET_USER_MEMORY_REGION
            vcpu.init_pmu #设置VCPU PMU_V3 IRQ 为23
                    --> KVM_HAS_DEVICE_ATTR 
            vcpu.init_pvtime #设置pvtime_ipa=0x7FDF0000
                    --> KVM_HAS_DEVICE_ATTR* 
        set_tss_addr 在VM地址空间设置3-page区域的地址(X86)
            --> KVM_SET_TSS_ADDR 
        generate_pci_root
            register_irq_event
                register_irqfd 循环4次
                    --> KVM_IRQFD
            register_ioevent 循环几十次(X86)
                --> KVM_IOEVENTFD
        *add_arch_devs #创建早期平台设备(ARM64)
            register_irq_event #为RTC注册一个可以触发GSI中断的事件
                register_irqfd
                    --> KVM_IRQFD
        register_irq_event #为串口设备注册SPI中断
            register_irqfd
                --> KVM_IRQFD
        vcpus[0].get_psci_version() #获取psci版本
            get_one_reg 
                --> KVM_GET_ONE_REG*
     run_control
         check_capability
             --> KVM_CHECK_EXTENSION
在crosvm_vcpu线程中,x86需要创建和配置VCPU,ARM64在build_vm中已经创建好VCPU,最后是陷入内核运行虚拟机.(x86斜体)
run_vcpu src/linux.rs:2082
      *runnable_vcpu(X86)
          create_vcpu
              get_vcpu_mmap_size 12KB
                  --> KVM_GET_VCPU_MMAP_SIZE
              --> KVM_CREATE_VCPU 返回fd,后面都用VCPU fd
      configure_vcpu
      setup_cpuid
          get_supported_cpuid
              get_cpuid
                  --> IoctlNr
      filter_cpuid
          check_capability
              --> KVM_CHECK_EXTENSION
          set_cpuid
              --> KVM_SET_CPUID2
          setup_msrs
              create_msr_entries
                  --> KVM_GET_MSRS
          setup_regs
              --> KVM_SET_REGS
          setup_fpu
              --> KVM_SET_FPU
          set_lint
              get_lapic_state
                  --> KVM_GET_LAPIC*
  vcpu.run
      --> KVM_RUN  会一直往复下去。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值