当前位置:网站首页>【Jailhouse 文章】Jailhouse Hypervisor

【Jailhouse 文章】Jailhouse Hypervisor

2022-07-05 05:28:00 Jia ming

作者:Maxim Baryshnikov,布拉格捷克技术大学

本文描述了Jailhouse Hypervisor的主要概念和原理。
本文实现了简单的演示应用程序(与高精度事件定时器交互)。
本文将一个小型操作系统(L4 Fiasco)移植到Jailhouse cell中。
本文评估了共享内存系统对于Jailhouse中运行程序的实时属性的影响。benchmark 测试结果表明,两个相互交互的CPU,在最坏情况下,由于对于 L3 的共享,导致内存访问带宽降低。

1. Jailhouse 特点

Jailhouse 由首席开发人员 Jan Kiszka 负责,专注于与安全相关的示例(工业流程、航空航天、医学等),其应用场景是特殊的。Jailhouse 的主要特点是:并不是在 GuestOS 间对称地共享多核处理器资源,而是划分不同的资源给 GuestOS 独立使用。

Vitaliy Sinitsyn 这样评价,Jailhouse 在现有 Linux 设置之上启用非对称多处理方式,并将系统拆分成称为 cell 的独立分区。每个 cell 运行一个 GuestOS 并拥有一组完全控制的分配资源(CPU、内存区域、PCI设备)。Jailhouse的工作是管理 cell 并保持它们彼此隔离。

假设本文有一个多核系统,本文想用一个物理 CPU 来处理硬实时任务,另一个 core 用于用户交互(GUI/HMI),其它物理 CPU 用来收集传感器数据。显然,GUI 应用程序不能影响第一个物理 CPU 的工作。如何防止不被干扰,这就是 Jailhouse 的工作。

Jailhouse 的目标是安全关键的工业应用。此类应用程序通常需要由独立机构根据众多安全标准进行认证,认证的目标是获得系统足够可靠以安全地执行其预期功能的信心。安全标准根据所需的可靠性程度将安全功能分为多个级别(安全完整性级别),对关键性更高的系统提高更严格的要求。复杂的系统很难(很昂贵)或无法认证。这就是为什么 Jailhouse 是一个非常简约的 Hypervisor,它只包含隔离功能以隔离 GuestOS 所需要的功能。没有一个 cell 可以访问不属于它们的设备,因为 Hypervisor 会阻止它。因此,实时应用程序不会受其它分区中发生的任何事情的影响。

Jailhouse 是在用户空间通过向 Jailhouse VMM 发出超级调用的设备驱动程序来管理的。Jailhouse不是内核的一部分,它运行在最低级别。内核模块仅用于将 Jailhouse 的二进制镜像加载到内核所保留的内存。

请添加图片描述

2. celll configuration

每个 cell(root cell/non-root cell)必须在启动之前进行静态配置,此配置确定 cell 可以访问哪些硬件资源。Jailhouse 使用 *.c 文件作为配置文件,其中必须将参数分配给特殊的 C 结构的字段,这些字段定义在 hypervisor/include/jailhouse/cell-config.h 中。

non-root cell 字段如图下,root cell 字段如图后图。

请添加图片描述

对于 root cell,结构体中需要包含(struct jailhouse_system),而不是 struct jailhouse_cell_desc,其余内容二者没有区别。

请添加图片描述

配置 non-root 只能手写配置文件,而 root cell,存在这样的工具,分为以下两个步骤:

1、jailhouse config collect <name-of-arch.tar> 收集目标平台信息。

2、jailhouse config create -r <name-of-arch.tar> <name-of-conf.c> 需要安装 python-mako 库。创建后,配置被编译为原始二进制文件,Jailhouse 在创建cell 时会使用。

3. Jailhouse enabling

Jailhouse 启动需要如下步骤:

1、在目标平台上安装 Linux>=3.18

2、在目标平台上编译安装 Jailhouse。

3、在 Linux 内核启动参数中添加对于内存区域的保留选项(如memmap=),该值必须和 .phys_start 和在 root cell 配置中的 .size 字段值一致。

4、加载 jailhouse.ko 内核模块。这可以创建 /dev/jailhouse 设备,该设备可以通过用户空间的工具访问。Jailhouse 用户空间程序向 /dev/jailhouse 发送JAILHOUSE_ENABLE 请求,该请求指示驱动程序调用 jailhouse_cmd_enable()(driver/main.c)。在此函数中,驱动程序首先进行一些验证。它检查 CPU 标志以确定该 CPU 使用哪种虚拟化技术,然后对配置的二进制文件进行基本验证。之后调用request_firmware()函数在 /lib/firmware 文件夹中搜索 jailhouse-inter.bin 或者 jailhouse-amd.bin。驱动程序将步骤2中保留的内存区域重新映射到内核地址空间内存(使用ioremap_page_range(…)),因此可以从用户空间访问Jailhouse,驱动程序在此内存区域的开头复制该二进制文件,并在其后面复制 cell 的配置。然后,调用 jailhouse_cell_create() 函数。

5、执行 hypervisor 启动命令:jailhouse enable <path/to/cell/conf.cell>。enable 的最后阶段是 CPU 初始化。Jailhouse 为每个 CPU 调用entry_hypervisor()函数来启动(hypervisor/x86/entry.S中的arch_entry)。Jailhouse 需要成为 cell 和 CPU 之间的接口,因此它会保存系统的状态,然后在 CPU0 初始化时设置其环境。包括:为 Jailhouse 和 APIC 设置分页、创建中断描述符表、创建配置虚机扩展(VME),它还设置 UART 通信以将调试信息写入其中,因此可以在 ttyS0 上看到输出信息。对于所有 CPU,此过程都是一致的:更新 IDT 和 GDT,重置 CR3,设置 VMCS。最后 Hypervisor 发送VMLAUNCH 指令。从此开始,Linux不再在裸机上运行,而是在 Jailhouse 的 cell 中运行。

请添加图片描述

4. Cell initialization and start process

当用户执行 jailhouse cell create <path/to/conf.cell> 时,它将配置文件读入内存,并将 JAILHOUSE_CELL_CREATE 命令发送给驱动程序,调用 jailhouse_cmd_cell_create()(driver/control.c)将上述配置文件从用户空间内存复制到内核空间,并对cell desc进行检查。然后,根据配置内容为guest得到image,在 sysfs 中显示新的cell的信息,并将请求的CPU从Linux root cell中分配出去,从Linux中卸载PCI设备。Jailhouse模拟PCI虚拟驱动程序将其从Linux中删除,因为无法执行真正的拔出。当驱动程序发出 JAILHOUSE_HC_CELL_CREATE 超级调用时,Jailhouse调用 hypervisor/control.c中的cell_create(),发出命令,让所有新cell的处理器暂停,除了当前的处理器,它可以防止它们之间的竞争。

然后,开始cell初始化过程。cell_init()函数用I/O端口位图的值填充cell结构中的cpu_set字段,并调用历程来保存PCI、IOAPIC和IOMMU等内存映射设备的位置和处理程序。然后,检查CPU没有被分配出去后,跳转到arch_cell_create()(hypervisor/arch/x86/control.c),开始"shrinking"。这里的重点是遵循一对一分配的概念:如果root cell有初始的cell需要的东西,那么Linux cell(root cell)的访问将被拒绝,而新的cell得到它,在这种情况下,Linux cell将在第一次访问时停止。在重新分配I/O端口、IOAPIC、IOMMU、PCI等资源后,配置通信区域(这是hypervisor和特定的cell都可以读取和写入的每个cell共享内存区域)。它还包含有关PM计时器地址的信息,分配的CPU数量,包含当前cell的状态(例如,running、running/locked)。

最后,cell 将被提交到cell list,通信区域中的cell 状态将设置为 JAILHOUSE_CELL_SHUT_DOWN,并且对每个cell的CPU,Jailhouse发出arch_cpu_resume()。

要在新的cell中执行inmate,需要将inmate移动到cell的内存区域:jailhouse cell load <name-of-cell> <inmate.bin> -a <off-in-guest>

所有的inmates都被视为原始二进制文件。此二进制文件的大小必须小于或等于将加载它的guest内存区域。驱动程序将 JAILHOUSE_HC_CELL_SET_LOADABLE发送到hypervisor,并将标记为可加载的 guest 区域重新映射到 root cell 的地址空间。可以看到串口信息:“Cell can be loaded”。驱动程序将二进制文件存储在给定的地址。

最后,要启动它,用户应该调用:jailhouse cell start <name-of-cell>,这会导致HyperCall —— JAILHOUSE_HC_CELL_START,从它的角度来看,它会导致Jailhouse的cell_start()执行从root cell到Guest的取消映射所有可加载区域。cell的状态变为JAILHOUSE_CELL_RUNNING,并且在cell的每个CPU上调用arch_cpu_reset()。这会向cell中的每个CPU发送虚假的启动处理器间中断(SIPI)。在下一个#VMEXIT时,Guest指令指针将设置为0xFFFF0,并且inmate开始执行。

5. Inmate demos

Jailhouse 提供了一个小型的框架,可以轻松地开发简单的无操作系统的应用程序。所依赖的 library 头文件定义在 inmate.h 中,其中包含了内存分配和重新映射、APIC 和 IOAPIC 的初始化、中断处理程序设置、与 PCI 设备的一些交互甚至是一些基本的 SMP 操作(smp_wait_for_all_cpus()、smp_start_cpu())。

inmates 的启动代码(header.S for 64-bit、header-32.S for 32-bit)如Listing 2.4所示。Jailhouse 中 inmates 的入口点在 0xFFFF0,这里需要一个小技巧来跳转到16位代码部分(用于GDT和保护模式标志设置)。inmate二进制文件以偏移量0xF0000加载到GuestOS中。

Listing 2.5中显示的链接描述文件(inmate.lds)确保了以下内容。16位的startup段绑定在二进制文件的最开头(0x0000)。boot段紧随startup段之后,固定在0xFFF0,因此加上偏移地址之后,会给出正确的入口地址。.text、.data、.rodata部分有它们的虚拟内存地址(VMA,运行output file时,该部分的虚拟地址),其中包括加载偏移量。但是它们的加载内存地址(LMA,加载该部分的地址)没有加载偏移量。

因此,在0xFFF0处的.boot段只有一条指令:ljmp $0xf000, $start16,它使instruction pointer在物理地址0xf0000上移动。之后,当GDT和保护模式标志被设置时,它会跳转到32位代码,在此处进行分页设置,最后是inmate_main()函数入口。

5.1 APIC demo

APIC-高级可编程中断控制器。这是一个典型的inmate,通常用于演示Jailhouse功能。它为APIC计时器设置中断并测量事件发生之间的实际时间。除此之外,它还展示了使用域间通信和 manipulate the cell state。

该 demo 的配置文件如 Listing 2.8 所示,configs/apic-demo.c,定义了两个内存区域:加载 inmate 的内存区域(1MB),紧挨着的用于通信的区域(4KB),后者有一个额外的标志JAILHOUSE_MEM_CONMM_REGION,让Jailhouse知道在哪里读/写消息,它会在串口0上打印一个日志。

请添加图片描述

Launching apic-demo cell:

jailhouse cell create /jailhouse/configs/apic-demo.cell

jailhouse cell load apic-demo /jailhouse/inmates/apic-demo.bin -a 0xf000

jailhouse cell start apic-demo

jailhouse cell shutdown apic-demo

jailhouse cell shutdonw apic-demo

demo开始运行之后,cell state被设置为 JAILHOUSE_CELL_RUNNING_LOCKED,这是通过设置commo_region->cell_state实现的,通常,这意味着Jailhouse无法shrink cell。之后,应用程序校准时间戳计数器(inmates/lib/x86/timing.c),并初始化APIC定时器。然后为定时器的中断设置处理程序,因此,每次下一个中断发生时,都会计算抖动。“抖动是预期时间和时间(延迟)之间的差异,就性能而言,越小意味着jailhouse越不感知”。程序在通信区域中等待消息,如果出现关闭请求,则程序发送一条消息,表明现在无法关闭,如果此请求出现第二次,apic-demo会中断循环。将apic-demo的cell状态更改为JAILHOUSE_CELL_SHUT_DOWN,所以Jailhouse知道关闭过程进展顺利。

5.2 HPET demo

作者实现的高精度事件定时器。

6. L4 Fiasco.OC launch

在cell中使用裸机程序时,可能有助于解决简单问题,但是在大多数情况下,当必须实现更复杂的东西(网络协议栈、自动驾驶等)时,需要运行操作系统,因此,将某些操作系统移植为 inmate 是有必要的。

目前,可以在non-root cell中启动Linux,并且Documentation/non-root-linux.txt中描述了如何做,必须以特定的方式修改和配置内核,用于引导内核的用户空间工具也必须创建。但是,面对实时任务,Linux便不能胜任。

选择Fiasco.OC进行移植就是为了满足实时要求。

6.1 Overview

Fiasco.OC是德累斯顿大学Fiasco团队开发的基于微内核的操作系统。它由L4的微内核和与L4运行时环境(L4Re)相关的用户级程序组成。内核本身非常简约。它还提供了作为进程间通信(IPC)、创建/删除地址空间(任务)和线程等基本功能。

Fiasco的极简主义:微内核总共提供了7个系统调用,换句话说,微内核只用了7个系统调用就统治了世界。

可以启动操作系统的最小配置必须包含Fiasco内核、称为Sigma0的Root-Pager、Root-Task(Moe)以及至少一个在其之上运行的用户空间应用程序。Sigma0为用户空间程序提供了使用内存(重新映射、分配等)的API。Moe在分页管理器之上运行,是内核首先启动的任务。它为所有其他用户空间应用程序提供更抽象的接口。

请添加图片描述

6.2 Fiasco 启动过程

为了移植Jailhouse,Fiasco系统配置、启动过程配置、内核配置。

7. Benchmarks

评估内存系统对不同cell中运行的软件的性能影响。测试当不同cell同时访问内存时的影响。

一个普通的多核处理器中,core共享L3缓存,并且内存系统中的其他部分也是共享的。现在假设,每个core执行完全不同的程序,因此,每个程序都需要访问不同的内存区域,在这种情况下,core之间将相互竞争cache,并且内存访问时间将增加。此外,当一个core想要访问内存的同时另一个core也想访问内存,势必会有一个core需要等待,在这种core竞争的情况下,某一个core上的内存密集型应用程序会显著减慢其它core上的应用程序。所有提到的这些问题都会对系统的实时性和安全性产生负面影响。

实现了一个简单的benchmark,该benchmark可以在cell中、裸机上运行。目的是研究在使用Jailhouse时,内存访问是否出现减慢的现象。

原网站

版权声明
本文为[Jia ming]所创,转载请带上原文链接,感谢
https://jiaming.blog.csdn.net/article/details/125604870