Magenta- 支持虚拟化

来源:互联网 发布:傲剑紫霞神功数据 编辑:程序博客网 时间:2024/06/05 07:35

Magenta- 支持虚拟化


Magneta实现了类似于Kvm+Qemu一套东西,但要简单得多,当前只支持在物理magenta上虚拟运行另一个magenta实例。

Mageta目前只支持x86,类似于Kvm,利用了x86的Intel-VT (Virtualization Technology)技术,在VMX root operation 和 VMX non-root operation间切换。类似于Qemu,Magenta也有user space的service,当前只支持I/O操作和空间访问异常操作。

Magenta提供了guest命令作为虚拟化运行的入口。命令的使用方式如下:

usage:guest kernel.bin [ramdisk.bin]kernel.bin:   指magenta.bin文件ramdisk.bin:  指ramdisk image文件

接下来我们看看命令guest的流程。


创建Hypervisor object


Magenta分别为Hypervisor和Guset创建对应的Context,以方便管理各自的状态和2者间的切换。

using HypervisorContext = VmxonContext;using GuestContext = VmcsContext;

Hypervisor object实际指向的就是VmxonContext,这是个单实例。

  • VmxonContext为每个CPU分配一个VmxonPerCpu实例;
  • VmxonPerCpu做如下初始化:
    • 其会为本CPU分配1个page,作为将来指令vmxon的参数之用;
    • 每个CPU使能vmxon

至此,CPU进入了VMX root operation模式。


创建guest物理mem


在当前进程的地址空间上为guest创建物理mem空间,大小为1GB。

  • 创建1个大小为1GB的vmo;
  • 将此vmo映射至当前进程。在映射时,会分配物理页填满此1G空间。

创建guest object


guest的context是VmcsContext,所以其object指向此context。

  • 创建一个Fifo,作为将来kernel和user的交互通道;
  • 创建VmcsContext:
    • 为每个CPU分配VmcsPerCpu实例;
    • 基于前面所创建的guest物理mem空间,创建GuestPhysicalAddressSpace对象
      • 分配1个page,作为guest空间的页目录,为guest创建256G的虚拟地址空间;
      • 将 guest物理mem映射进guest地址空间,是以ExtendedPageTable(EPT)的方式映射;
    • 分配1个page,将其映射到guest空间的APIC_PHYS_BASE=0xfee00000处,后期在设置vmcs的APIC_ACCESS_ADDRESS时需要使用到此page;
    • 将IO-Apic物理基地址0xfec00000的映射从guest空间中去除,后期guest在访问IO-Apic时会trap进kernel,由kernel通过Fifo将消息传递至user,从而由user处理此事件;
    • 为每个CPU初始化VmcsPerCpu;
    • 配置vmcs,这部分太细,想了解的可以看手册《64-ia-32-architectures-software-developer-vol-3c-part-3-manual.pdf》。这里列几部分如下:
      • 配置EPT_POINTER指向前面所创建的guest页目录;
      • 配置HOST_RIP指向vmx_exit_entry,这是从guest从non-roo进入root模式的入口;
  • 在guest物理mem起始地址处创建页表,页表的覆盖地址范围是1GB;
  • 紧接着页表,创建acpi able;
  • 在物理mem偏移0x800000处创建bootdata参数,供guest os启动之用。启动参数包括:
    • ACPI table;
    • E820 table用于描述物理mem分布;
  • 将kernel.bin读取至物理mem的偏移0x100000处;解析kernel.bin的header,得到其entry函数地址;
  • 如果提供了ramdisk,则将其加载至bootdata之后;
  • 配置VmcsPerCpu的general purpose register (gpr),即配置guest的gpr。将寄存器rsi指向0x800000处,即bootdata处。这是因为Magenta启动时会默认认为rsi寄存器中保存的就是bootdata的地址;
  • 将kernel.bin的entry地址设置到guest的寄存器IP;
  • 设置guest cr3为0,这是因为页表位于guest物理mem的起始地址,而物理mem在guest空间是从0开始的。
  • 创建长度是1个page的vmo,让guest的Local-Apic即VIRTUAL_APIC_ADDRESS指向此vmo的物理页地址;并将此物理页也映射进本进程,以便后期user可以处理虚拟中断;

创建service线程


此线程监听Fifo消息,如果接收到kernel的消息,则解析消息并处理。当前只支持3种消息类型:

PORT_IN  PORT_OUTMEM_TRAP

当前MEM_TRAP只处理3中类型的trap:

Local-APIC访问异常IO-APIC访问异常TPM访问异常

vm enter


追后走至VmcsPerCpu的Enter函数:

  • 保存host现场,包括CR3,FS 的基地址等等;
  • 设置guest的CR3和IP至vmcs;
  • 调用函数vmx_enter
    • 保存host的gpr,载入guest的gpr(还记得之前设置的rsi吗);将vmx_enter的返回地址保存为host RIP,从而后期可正常返回;
    • 调用vmlaunch,进入non-root模式

vmx exit handler


vm的退出,并不是直接返回指令vmlaunch之后,而是返回至vmx_exit_entry:

  • 保存guest的gprs,并加载host的gprs;
  • 将返回地址host RIP压栈,以便可以ret返回;
  • 重新加载TSS,重新加载IDT。重新加载都是因为2者的描述符的VMX所设的limit默认值不合适;

做完以上的配置后,执行ret后就可返回至vmx_ente之后。然后处理退出事件,当前包括如下事件:

EXTERNAL_INTERRUPT          = 1u,INTERRUPT_WINDOW            = 7u,CPUID                       = 10u,HLT                         = 12u,VMCALL                      = 18u,IO_INSTRUCTION              = 30u,RDMSR                       = 31u,WRMSR                       = 32u,ENTRY_FAILURE_GUEST_STATE   = 33u,ENTRY_FAILURE_MSR_LOADING   = 34u,APIC_ACCESS                 = 44u,EPT_VIOLATION               = 48u,XSETBV                      = 55u,

关于访问Local-APIC和IO-APIC引起的vm exit,两者使用了不同的机制。Local-APIC是利用了VMX的固有功能从而触发了APIC_ACCESS:

44: APIC access. Guest software attempted to access memory at a physical address on the APIC-access page and the“virtualize APIC accesses” VM-execution control was 1 (see Section 29.4). 

而IO-APIC是利用了缺页中断的方式触发了EPT_VIOLATION。

这2种事件经过打包成package后,通过Fifo发送到user的service thread,并等待service的回应;service处理完后再通过Fifo发送结果至handler。