前言
前面介绍了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了。
异常等级 选项 EL0 EL0t EL1 EL1t, EL1h EL2 EL2t, EL2h EL3 EL3t, EL3h t表明选择了SP_EL0寄存器,h表明选择了SP_ELn寄存器。
3 创建页表
除了被页表、DTB、Xen镜像占用和使用的内存,及其它一些保留内存,所有剩下的内存都被Xen映射到XENHEAP_VIRT_START开始的堆,该堆被内存分配器进行管理(划分给虚拟机内存也从堆分配)。
4 初始化调度器
Xen内置一个调度器,负责虚拟机的调度:
- 初始化中断控制器
- 初始化定时器中断
- 初始化调度器,并创建Idle Domain
Xen目前支持4种调度算法:
Credit:传统的信用调度器,是一种通用的调度器;
Credit2:针对更低延迟和更高虚拟机密度进行了优化,默认调度算法;
RTDS:适用于多核的软硬实时调度程序,针对嵌入式、汽车、云端图形和游戏以及一般低延迟工作负载;
ARINC653:单核硬实时调度器,面向航空电子设备、无人机和医疗设备;
5 Xen域和IO域
根据设备树的配置,对DTS中的资源打标签,分配不同的资源到Xen域或IO域。
6 检测虚拟机特性 - 内存+IO
检测虚拟机的内存虚拟化能力:
- 虚拟机支持多大的物理地址(IPA)范围;
- 虚拟机支持的stage2页表翻译粒度是4KB;
- 虚拟机支持多少个VMID (8-bit or 16-bit);
- Stage 2页表的翻译级数最少可以是多少级(4级 or 3级);
- 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)。这是因为:
- Domain 0本身可以被看做Xen的一部分,没有必要对它隐藏太多的细节。
- Domain 0本身要负担各种硬件的驱动,这些驱动的信息都是描述在DTB或者ACPI table里。如果Domain 0内的地址空间做了重映射,那么就要按照Domain 0的地址空间重新生成一份DTB或者ACPI table。而不能像现在这样直接从主机的DTB或者ACPI table做少量的修改得到。
在保持IPA=PA的前提下,尝试从Bank#0开始分配连续的物理内存给Domain#0:
- Bank#0的地址范围可能是0~4GB,可以兼容32-bit no-LPAE Domain-0系统和32-bit DMA设备;
- Linux希望讲内核镜像、device tree和RAMDISK在启动时都加载地址最低的同一块bank中;
- Linux zImage启动方式要求内核被加载到内存的第一个128MB区间;
- 分配连续的内存可以让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虚拟机的构成:
- 必要部分:支撑一个只有简单idle task的操作系统运行的最少组件
- 可选部分:支撑一个支持网络和文件系统的操作系统需要的组件
ARM虚拟机的创建分两部分:
- arch_create_domain:构建一个虚拟SoC,但是不包括CPU(含紧耦合在CPU内部的GICC, GICR以及ARCH_TIMER);
- 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支持两种管理方式:
- CPU Pinning
- Assign a vCPU to run on specific pCPUs only;
- 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转发:
- 如果IRQ属于虚拟机本身,则在虚拟机内部完成处理,否则;
- 如果IRQ属于当前运行的Guest,则通过vGIC注入中断并返回GusetOS完成中断处理。否则;
- 将IRQ加入到Pending队列,触发虚拟机调度并调度到目标Guest,在目标GuestOS中完成中断处理
总结
就分析到这里吧,看不懂只能自己撸代码了。