Xen杂烩-2


前言

前面介绍了Xen的框架,接下来介绍Xen的工作原理(说是介绍,其实就是把别人的内容拷贝过来,侵权必删)。


一、工作原理

1. 初始化

xen/arch/arm/arm64/head.S

       msr DAIFSet, 0xf /* Disable all interrupts */


      /* Save the bootloader arguments in less-clobberable registers */
      mov x21, x0 /* x21 := DTB, physical address */


      PRINT("- Boot CPU booting -\r\n")
…...


      bl check_cpu_mode         /* 检查当前CPU所在的exception Level是否为EL2 */
      bl zero_bss                       /* 在CPU_INIT中设置TCR和MAIR为建立页表做好准备 */
      bl cpu_init
      bl create_page_tables     
 /* Xen实际加载的地址和编译时指定的XEN_START_ADDR不 

                                                 一定移植,所以先建立一个简单的线性映射页表用于系统初始

                                                化,该页表后面会被替换*/
      bl enable_mmu                /* 使能MMU */


      /* We are still in the 1:1 mapping. Jump to the runtime Virtual Address. */
      ldr x0, =primary_switched
      br x0
primary_switched:
      bl setup_fixmap         /* 为early_console做了固定映射,用于debug */
      b launch                    /* Launch这边为调用C代码做准备,设置好boottime stack我们就可

                                           以进入start_xen了 */
ENDPROC(real_start)

        Xen遵循Arm64 Linux Boot Protocol,可以兼容支持Arm64 Linux Image格式的Bootloader:X0寄存器用于存放Device Tree的地址(x21寄存器为x0的备份)。

launch:
      PRINT("- Ready -\r\n")


      ldr x0, =init_data
      add x0, x0, #INITINFO_stack         /* Find the boot-time stack */
      ldr x0, [x0]
      add x0, x0, #STACK_SIZE            /* (which grows down from the top). */
      sub x0, x0, #CPUINFO_sizeof      /* Make room for CPU save record */
      mov sp, x0


      cbnz x22, 1f


      mov x0, x20                                 /* Marshal args: - phys_offset */
      mov x1, x21                                 /* - FDT */
      b start_xen                                 /* and disappear into the land of C */

2. start_xen - 虚拟机初始化

2.1 初始化陷阱

2.2 堆栈选择

        SPSel_ELx (x可以是1,2,3),可以选择EL1,EL2和EL3使用独立的栈还是和EL0公用一个堆栈。当SPSel_Elx为1时表示Elx使用独立的栈。举个例子:Xen EL2执行msr spsel, #1就是选择在EL2使用独立的栈。

/*
* Ensure that any exceptions encountered at EL2
* are handled using the EL2 stack pointer, rather
* than SP_EL0.
*/
msr spsel, #1

ARM SP寄存器(堆栈指针)
        每个异常等级都有自己的SP寄存器(SP_EL0, SP_EL1, SP_EL2, SP_EL3)。但是,除了EL0以外,其它异常等级都有两个选择,或者用自己等级的SP_ELn,或者用EL0等级的SP_EL0;EL0就只能用SP_EL0了。

异常等级选项
EL0EL0t
EL1EL1t, EL1h
EL2EL2t, EL2h
EL3EL3t, EL3h

t表明选择了SP_EL0寄存器,h表明选择了SP_ELn寄存器。

3 创建页表

        除了被页表、DTB、Xen镜像占用和使用的内存,及其它一些保留内存,所有剩下的内存都被Xen映射到XENHEAP_VIRT_START开始的堆,该堆被内存分配器进行管理(划分给虚拟机内存也从堆分配)。

4 初始化调度器

Xen内置一个调度器,负责虚拟机的调度:

  1. 初始化中断控制器
  2. 初始化定时器中断
  3. 初始化调度器,并创建Idle Domain

Xen目前支持4种调度算法:

Credit:传统的信用调度器,是一种通用的调度器;

Credit2:针对更低延迟和更高虚拟机密度进行了优化,默认调度算法;

RTDS:适用于多核的软硬实时调度程序,针对嵌入式、汽车、云端图形和游戏以及一般低延迟工作负载;

ARINC653:单核硬实时调度器,面向航空电子设备、无人机和医疗设备;

5 Xen域和IO域

根据设备树的配置,对DTS中的资源打标签,分配不同的资源到Xen域或IO域。

6 检测虚拟机特性 - 内存+IO

检测虚拟机的内存虚拟化能力:

  1. 虚拟机支持多大的物理地址(IPA)范围;
  2. 虚拟机支持的stage2页表翻译粒度是4KB;
  3. 虚拟机支持多少个VMID (8-bit or 16-bit);
  4. Stage 2页表的翻译级数最少可以是多少级(4级 or 3级);
  5. Stage 2实际需要负责翻译的虚拟机物理地址范围大小;

检测虚拟机的IO虚拟化能力:
1. 是否支持IOMMU进行DMA内存地址转换;
2. IOMMU是否支持interrupt remapping;

3. start_xen - 虚拟机创建和运行

3.1 创建Domain 0

        Domain 0是Xen功能的一个延伸,没有Domain 0 Xen无法正常工作。所以Domain 0是伴随着Xen的启动而启动的。只有Domain 0正常启动完成,Xen才算真正的初始化完成。
        分配给Domain 0的内存和外设地址和实际的物理地址是1:1映射的(PA = IPA)。这是因为:

  1. Domain 0本身可以被看做Xen的一部分,没有必要对它隐藏太多的细节。
  2. Domain 0本身要负担各种硬件的驱动,这些驱动的信息都是描述在DTB或者ACPI table里。如果Domain 0内的地址空间做了重映射,那么就要按照Domain 0的地址空间重新生成一份DTB或者ACPI table。而不能像现在这样直接从主机的DTB或者ACPI table做少量的修改得到。

在保持IPA=PA的前提下,尝试从Bank#0开始分配连续的物理内存给Domain#0:

  1. Bank#0的地址范围可能是0~4GB,可以兼容32-bit no-LPAE Domain-0系统和32-bit DMA设备;
  2. Linux希望讲内核镜像、device tree和RAMDISK在启动时都加载地址最低的同一块bank中;
  3.  Linux zImage启动方式要求内核被加载到内存的第一个128MB区间;
  4. 分配连续的内存可以让Domain0拥有少量大块的bank,而不是大量碎片化的bank.

如果只支持64-bit的Domain 0,从bank#0开始分配内存就不是必须的。

3.2 创建Domain U

        如果Xen在启动时,除了Domain 0之外还指定了其它的Guest作为Domain U伴随启动。Create DomUs就会创建这些Guest。可以不依赖于Domain 0里的工具链启动。

3.2.1 虚拟机构成

一个ARM虚拟机的构成:

  1. 必要部分:支撑一个只有简单idle task的操作系统运行的最少组件
  2. 可选部分:支撑一个支持网络和文件系统的操作系统需要的组件

ARM虚拟机的创建分两部分:

  1. arch_create_domain:构建一个虚拟SoC,但是不包括CPU(含紧耦合在CPU内部的GICC, GICR以及ARCH_TIMER);
  2. arch_vcpu_create:构建虚拟CPU,包含用于保存通用寄存器,浮点寄存器,EL1,EL0可以被修改的系统寄存器以及GICC,GICR和ARCH_TIMER;

3.2.2 初始化Stage2页表

3.2.3 初始化虚拟IO空间

3.2.4 初始化虚拟GICD

        我们在创建一个虚拟设备时会向Virtual MMIO Device List里注册虚拟设备,包含该虚拟设备的寄存器地址范围,以及读/写操作对应的回调函数。

        假设vGICD的寄存器地址范围为0xFC000000 – 0xFC010000,虚拟机在stage2页表中不去设置该GPA范围对应的物理地址(PA),因此当虚拟机访问该GPA地址范围时会产生Stage 2转换异常,然后被虚拟机捕获。

3.2.5 初始化虚拟Timer

3.2.5 创建虚拟CPU

虚拟CPU支持两种管理方式:

  1. CPU Pinning

                 - Assign a vCPU to run on specific pCPUs only;

  1. CPU Pools

                - Group pCPUs to be managed by an scheduler instance;

                - Allows for multiple schedulers to be run at the same time; (This flexibility allows the system to be configured to run SMP or Heterogeneous or Homogeneous AMP as needed)

3.3 VM运行在Xen

虚拟机的运行单元时VCPU,VCPU的执行是由Xen的调度器决定的。

3.3.1 IO 陷阱

3.3.2 ISR 陷阱

所有的中断都会由Hypervisor转发:

  1. 如果IRQ属于虚拟机本身,则在虚拟机内部完成处理,否则;
  2. 如果IRQ属于当前运行的Guest,则通过vGIC注入中断并返回GusetOS完成中断处理。否则;
  3. 将IRQ加入到Pending队列,触发虚拟机调度并调度到目标Guest,在目标GuestOS中完成中断处理


总结

        就分析到这里吧,看不懂只能自己撸代码了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值