怎么看网站建设时间,外卖网站设计,海兴网站建设公司,电商平台是干什么的Jailhouse 是一个基于 Linux 实现的针对创建工业级应用程序的小型 Hypervisor#xff0c;是由西门子公司的 Jan Kiszka 于 2013 年开发的#xff0c;并得到了官方 Linux 内核的支持#xff0c;在开源社区中获得了知名度和吸引力。
Jailhouse Jailhouse 是一种轻量级的虚拟化… Jailhouse 是一个基于 Linux 实现的针对创建工业级应用程序的小型 Hypervisor是由西门子公司的 Jan Kiszka 于 2013 年开发的并得到了官方 Linux 内核的支持在开源社区中获得了知名度和吸引力。
Jailhouse Jailhouse 是一种轻量级的虚拟化技术可以将多个操作系统或者裸机程序同时运行在同一台硬件上。它是一个基于 Linux 的静态分区的 Hypervisor但本身并不改造 Linux 内核而是利用 Linux 系统的开放性增加一个或多个实时操作系统实现多系统在一个多核处理器上运行。 Jailhouse 不会模拟不存在的硬件资源也不包含任何的调度处理而是利用虚拟化技术将硬件资源划分为多个被称为 Cell 的独立空间并将每个 Cell 分配给不同的虚拟机。每个 Cell 独占自己的处理器核心、内存、I/O 设备和中断控制器等资源。这样可以确保不同虚拟机之间的资源互相隔离提高系统的可靠性。
Root Cell 当 Jailhouse 启动之后原来的负责启动 Jailhouse 的 Linux 系统所在空间就被称为 Root Cell。对于所有 Jailhouse 虚拟机的管理都是在 Root Cell 中的 Linux 系统中通过 Jailhouse 提供的命令行工具来实现的。
Non-root Cell 除了 Root Cell 之外的每个独立的 Cell 就是一个 Non-root Cell每个 Non-root Cell 就对应一个虚拟机。Non-root Cell 通过 Root Cell 进行管理只要资源够用Non-root Cell 可以有任意多个。
inmate Non-root Cell 虚拟机中运行的系统也可能是一个裸机程序被称为 inmate目前可以是 Linux、FreeRTOS、ERIKA3 RTOS、Zephyr 其中之一。Jailhouse 都是直接将其作为原始二进制文件不是 ELF 文件直接加载到其对应的配置文件中指定的内存中去运行。 在 x86 平台下由于 Jailhouse 只向 Non-root Cell 的 inmate 暴露最小环境可用的资源不足以在不进行修改的情况下引导标准的 Linux 系统。为此西门子官方提供了一个补丁队列以便在 x86 平台下的 Non-root Cell 中启动 Linux。同时在构建时需要注意以下几点
需要使能 CONFIG_JAILHOUSE_GUEST禁用 CONFIG_SERIO 和 CONFIG_PM_TRACE_RTC一般还应该禁用所有不需要的驱动程序和特性以避免不需要的 probe并且使镜像大小和内存占用最小化 在 ARM / ARM64 平台上引导 Linux 内核要比在 x86 平台上简单得多因此在这种情况下我们不需要为 Non-root Cell 特别修改 Linux 内核。
内存布局 Jailhouse 的实现需要使用一块连续内存这块内存需要在启动 Linux 时保留出来。至于内存具体用哪一块则取决于自己的设备如下以 0x100000000 为例的示意图如下所示
Cell 间通信 虽然 Jailhouse 将硬件资源进行了划分到了不同的 Cell 中通过虚拟机监控器实现了相互隔离但在实际应用过程中Cell 间也需进行通信。为此Jailhouse 通过虚拟 ivshmemInter-VM shared memory PCI 设备在 Cell 之间提供共享内存和信号机制。一个通道将两个分区 11 对应地连接起来。
环境要求 Jailhouse 是一个依托于 Linux Kernel 的开放性从而直接使用硬件平台提供的虚拟化技术来实现的虚拟化解决方案。因此Jailhouse 需要 Linux Kernel 的支持及硬件平台的虚拟化技术。
硬件要求 Jailhouse 利用 Intel 的 VT-x、AMD 的 AMD-V 和 ARM 虚拟化扩展等硬件辅助虚拟化技术来划分物理资源和限制虚拟机对这些资源的访问从而实现对系统资源的隔离控制。
x86 架构
针对 Intel 平台需要 64 位架构以及 VMXVirtual Machine ExtensionsIntel CPU 中使用 vmx 标识 Intel 的 VT-x 虚拟化技术 技术具体细节 具备 EPTExtended Page Tables扩展页表支持。扩展页表是用于内存管理单元MMU的 Intel 第二代 x86 虚拟化技术。 直接在实模式下启动逻辑 CPU 就需要 EPT 的支持这一功能在英特尔的行话中称为无限制访客模式并在 Westmere 微体系结构中引入。 Intel 的 Core i3、Core i5、Core i7 和 Core i9 CPU 等均支持 EPT Preemption Timer 是一种可以周期性使 VM 触发 VMEXIT 的一种机制。即设置了 Preemption Timer 之后可以使得虚拟机在指定的 TSC cycle 之后产生一次 VMEXIT 并设置对应的 exit_reasontrap 到 VMM 中。 支持中断重映射的 Intel IOMMUIntel 官方称为 VT-dVirtualization Technology for Directed I/O 针对 AMD 平台需要 64 位架构以及 SVMAMD Secure Virtual Machine这个是 AMD 内部的研发代号后来统一使用 AMD-V 作为对外名称AMD CPU 中使用 svm 来表示 AMD 的 AMD-V 虚拟化技术技术: 必须具备 NPTNested Page Tables嵌套页表支持。嵌套页表现在称为快速虚拟化索引Rapid Virtualization IndexingRVI是 AMD 第二代处理器内存管理单元MMU硬件辅助虚拟化技术。推荐具备 Decode Assists 支持AMD IOMMUAMD 官方称为 AMD-ViAMD’s I/O Virtualization Technology目前不被支持但后续需要 至少两个逻辑 CPU 核心。注意这里是逻辑 CPU 核心不是物理 CPU 核心。在 x86 平台下需要在 BIOS 或 UEFI 中开启虚拟化功能 EPT 和 NPT 是 Intel 和 AMD 两家对于 Second Level Address TranslationSLAT二级地址转换在自家 CPU 上的具体实现。SLAT 是一种硬件辅助虚拟化技术可以避免与软件管理的影子页表相关的开销。此外IOMMU 也经常被我们称为 PCI 直通。
ARM 架构 ARMv8 架构或者是带有虚拟化扩展的 ARMv7 架构。ARM 的虚拟化扩展支持 SLAT即由 Stage-2 MMU 提供的 Stage-2 页表。客户机使用 Stage-1 MMU。该支持在 ARMv7ve 架构中作为可选添加并且在 ARMv832 位和 64 位架构中也受支持。 至少两个逻辑 CPU 核心 支持如下 AArch32 架构的开发板 Banana Pi (see more)Orange Pi Zero (256 MB version)NVIDIA Jetson TK1ARM Versatile Express with Cortex-A15 or A7 cores (includes ARM Fast Model)emtrion emCON-RZ/G1x series based on Renesas RZ/G (see more) 支持如下 AArch64 架构的开发板 AMD Seattle / SoftIron Overdrive 3000LeMaker HiKeyNVIDIA Jetson TX1 and TX2Xilinx ZCU102 (ZynqMP evaluation board)NXP MCIMX8M-EVK
内核要求 Jailhouse 的构建是依赖于 Linux Kernel 的因此必须使用对应的 Linux Kernel。但是不同版本的 Linux Kernel 对于 Jailhouse 的支持情况不尽相同。在使用比较新的 Linux Kernel 时需要对 Linux Kernel 进行打补丁否则将出现各种错误
x86 架构 必须禁用 Linux Kernel 对 VT-d IOMMU 的使用DMAR。这个可以通过在 GRUB 配置文件中设置 GRUB_CMDLINE_LINUX intel_iommuoffIOMMU 硬件有 intel/amd/arm 的等一般用 intel 的硬件 这个启动参数来处理。 kvm 一定要用 intel_iommuonDPDK/SPDK 如果绑定 vfio-pci 那也一定要求 intel_iommuon如果绑定 uio/igb_uio 那么就不需要intel_iommuon dmesg | grep -E DMAR|IOMMU 查看 要利用更快的 x2APIC需要在内核中打开中断重新映射。这个需要再构建内核时启用 CONFIG_IRQ_REMAP 这个配置项 Jailhouse 本身和每个 Cell 都需要一块连续的 RAM这个必须在 Kernel 启动之前配置。通常是使用 memmap 或 mem 这两个启动命令来实现。在 x86 平台上这通常是在 GRUB 配置文件中通过添加 GRUB_CMDLINE_LINUXmemmap82M\\\$0x3a000000 这个启动参数来处理。
ARM 架构 针对于 AArch32 架构需要 Linux Kernel 版本大于等于 3.19而对于 AArch64 架构则需要 Linux Kernel 版本大于等于 4.7。此外还需要适当的的引导程序例如 U-Boot的支持。
Linux Kernel 必须以 HYP 模式启动工作在 HYP 模式下的 CPU 上默认是 SVC 模式。这里就涉及到了 ARM 架构中的不同特权等级ARMv7 中使用的是 Privilege level 的概念ARMv8 中则使用 Exception Level 这个概念 Supervisor CallSVC指令使用户模式程序可以请求操作系统服务。Hypervisor CallHVC指令使客户操作系统能够请求 Hypervisor 服务。Secure monitor CallSMC指令使普通世界能够请求安全世界服务。 PSCI 对 CPU 离线的支持。PSCIPower State Coordination Interface电源状态协调接口 是一个接口这个接口实现了电源管理用例。The ARM Trusted Firmware 实现了 PSCI(Power State Coordination Interface) 接口作为运行时服务。Normal world software 可以通过 ARM SMC(Secure Monitor Call) 指令来访问 ARM Trusted Firmware 服务Jailhouse 本身和每个 Cell 都需要一块连续的 RAM这个必须在 Kernel 启动之前配置。在 ARM 平台上这可以通过减少内核看到的内存量通过 mem 内核启动参数或修改 Device Tree即保留内存节点来实现。
源码 Jailhouse 是由西门子的 Jan Kiszka 在 2013年以 GPLv2 协议开源的一个虚拟化解决方案。并很快得到了官方 Linux 内核的支持在开源社区中获得了知名度和吸引力。本文及后续博文以目前最新提交e57d1eff6d55aeed5f977fe4e2acfb6ccbdd7560版作为学习对象。
源码文件 Jailhouse 作为一个极度精简的虚拟化实现方案其代码量还是非常小的几万行代码量就实现了一个功能强大的虚拟机。 .github 和 ci这两个目录下是用来持续集成环境中使用的构建 Jailhouse 所需的 Linux Kernel 的相关配置文件目前仅支持 GitHub Actions。 config这个目录下就是针对 arm、arm64、x86 架构下用于生成 Jailhouse 的 Cell 的配置文件对应的源文件。在对应架构下构建 Jailhouse 时对应架构目录下的每个 .c 就会被构建为对应的 .cell 文件。 配置文件的内容其实就是一个 C 语言的结构体变量因此使用的就是 .c 扩展名该目录下的 .c 文件实际上都是一些示例在真正使用时我们需要提供自己的配置文件 DocumentationJailhouse 的文档对应的源码它使用的是 Doxygen 文档系统。使用 sudo apt install doxygen 后使用命令 make docs 就可以在 Documentation/generated/ 构建出对应的文档。 driverJailhouse 的驱动源码最终会被编译为 jailhouse.ko并被放到 /lib/modules/$(uname -r)/extra/driver/ 目录下来使用 cell.c/h实现 Cell 相关命令的处理pci.c/h实现对 PCI 设备的处理。在将 PCI 设备分配给 Non-root Cell 时我们需要确保 Root Cell 中的 Linux 不再使用这些设备。只要设备被分配给了其他 Cell Root Cell 就不得再访问这些设备。不幸的是我们不能仅仅使用 PCI 热插拔来在运行时删除/重新添加设备因为Linux 将重新编程 BARBase Address Registers并定位到我们不希望/不允许的资源位置。 所以Jailhouse 充当了一个 PCI 虚拟驱动程序在其他 Cell 使用设备时它会声明这些设备。在创建 Cell 时设备将从其驱动程序中解绑并绑定到 Jailhouse。当 Cell 被销毁时Jailhouse 将释放其设备。当禁用 Jailhouse 时它将释放所有已分配的设备。 当释放设备时它们将不再绑定到任何驱动程序从 Linux 的角度来看Jailhouse 虚拟驱动程序仍然会被视为有效的驱动程序。将设备重新分配给原始驱动程序必须手动完成。sysfs.c/h还通过 sysfs 虚拟文件系统/sys/devices/jailhouse向用户空间暴露 Jailhouse 的数据结构。main.c/h驱动入口 hypervisorJailhouse 用于管理各个虚拟机的工具的源码代码最终会被编译为 jailhouse*.bin被放到 /lib/firmware 目录下 arch再实际使用中架构相关的代码先被执行其中调用架构无关的代码其他架构无关的代码其中的接口被 arch 中对应的接口调用 includeJailhouse 对外提供的各种 C 头文件其中包含了各种数据结构的定义例如cell-config.h 中就定义了各种设备的数据结构、Cell 的描述符 jailhouse_cell_desc 等等 Jailhouse 允许用户定义一些在编译时启用的特定于平台的设置或者调试配置参数。方法是新增 include/courahouse/config.h 这个文件然后在该文件中将对应的配置项定义为 1。如下是当前可用的配置项 /* Print error sources with filename and line number to debug console */
#define CONFIG_TRACE_ERROR 1/** Set instruction pointer to 0 if cell CPU has caused an access violation.* Linux inmates will dump a stack trace in this case.*/
#define CONFIG_CRASH_CELL_ON_PANIC 1/* Enable code coverage data collection (see Documentation/gcov.txt) */
#define CONFIG_JAILHOUSE_GCOV 1/** Link inmates against a custom base address. Only supported on ARM* architectures. If this parameter is defined, inmates must be loaded to* the appropriate location.*/
#define CONFIG_INMATE_BASE 0x90000000/** Only available on x86. This debugging option that needs to be activated* when running mmio-access tests.*/
#define CONFIG_TEST_DEVICE 1inmates这里面是 Jailhouse 虚拟机本身的固件源码他们会被编译为 .bin 文件。 demos这里面就是一些 inmate 的源码编译之后就是一个个的 inmate 镜像.bin 文件。其都有一个对应的 .cell 文件位于 configs/ 目录下。lib该目录下是一些可以在 inmate 的源码中使用的库函数的实现。tests该目录下是一些验证 Jailhouse 的用例tools该目录下是一些用来辅助处理 inmate 的工具的源码每个 .c 文件经过编译之后成为一个 xxx.bin最终所有的 .bin 会被安装到 /usr/local/libexec/jailhouse 目录下。目前该目录下就只有一个 linux-loader 的源码。 pyjailhouse这里面是用来处理在 Non-root Cell 中运行 Linux 虚拟机时对 Linux Kernel 进行处理的一个 Python 脚本库。 scripts编译系统使用的相关脚本 tools这里面是一些 Jailhouse 实用工具jailhouse 的源码其中有些是 Python 脚本。其中Python 脚本会被放到 /usr/local/libexec/jailhouse 目录下jailhouse 则被放到 /usr/local/sbin 目录下。 Makefile构建系统的入口 Kbuild也是个 Makefile 文件 setup.py用来打包及安装 pyjailhouse 的脚本文件。 其他其他
构建 Jailhouse 的构建过程非常简单但是由于不同的平台下的虚拟化技术的差异在构建时遇到的问题也不一样受限于博文篇幅我们将在后续博文中详细学习在不同平台下的构建及使用。
虚拟化 之二 详解 jailhousex86 平台的构建过程、配置及使用虚拟化 之三 详解 jailhouseARM 平台的构建过程、配置及使用
基本组件 完整的 Jailhouse 组件主要由内核模块jailhouse.ko、虚拟机管理程序固件jailhouse*.bin、管理工具jailhouse 命令行程序及一些 Python 脚本以及配置文件.cell这四部分组成。用户使用它们来启用虚拟机管理程序、创建 Cell、加载 inmate 二进制文件以及运行和停止它等。
jailhouse.ko jailhouse.ko 由源码根目录中的 driver 目录中源码在构建之后生成最终会被安装到 /lib/modules/$(uname -r)/extra/driver/ 目录下。它就是一个标准的 Linux Driver 程序实现为一个 struct miscdevice 设备主设备号 MISC_MAJOR10。使用命令 cat /proc/misc 可以查看各杂项设备。 Linux 中将设备分为字符设备I2C、USB、SPI等、块设备存储器相关的设备如EMMC、SD卡、U盘等和网络设备网络相关的设备WIFI等三大类其中杂项设备归属于字符设备。每个设备节点都有主设备号和次设备号 杂项设备的主设备号固定为10次设备号根据设备不同而不同。
jailhouse_init() 当加载 jailhouse.ko 之后驱动源码 driver/main.c 中的 static int __init jailhouse_init(void) 函数就会进行各种初始化主要就干了以下几个事
开头的这一堆的宏定义主要就是为了解决 Jailhouse 用的一些符号 Linux Kernel 没有导出的问题。其核心就是通过 kallsyms_lookup_name 这个内核接口来查找需要的符号。 但是5.7.0 以上版本的内核不再导出 kallsyms_lookup_name对于在高版本内核不在导出的原因请参考 https://lwn.net/Articles/813350/。实际上在 5.7.0 以上仍旧可以用 struct kprobe 来获取 kallsyms_lookup_name 函数的地址然后再进一步获取到想要的符号。通过 root_device_register(jailhouse) 创建 /sys/devices/jailhouse 这个设备然后调用 jailhouse_sysfs_init(jailhouse_dev) 初始化其中的内容此后用户空间就可以通过 /sys/devices/jailhouse 访问 Jailhouse 的数据结构。zcszcs-MassDatas-GXXA203:~/WORKSPACE/Jailhouse/jailhouse$ tree -L 3 -p /sys/devices/jailhouse
/sys/devices/jailhouse
├── [drwxr-xr-x] cells # 这个目录中包含了我们创建的那些 Cell 的信息
│ ├── [drwxr-xr-x] 0 # 这个是 Cell 的 IDRoot Cell 的 ID 为 0后续每创建一个 Cell ID 自动增 1
│ │ ├── [-r--r--r--] cpus_assigned # 这个是我们在 Cell 的配置文件中分配给 Cell 的 CPU 原始的配置参数按位使用置 1 表示使用例如fffb
│ │ ├── [-r--r--r--] cpus_assigned_list # 这个是分配给 Cell 的 CPU 的方便我们阅读的列表。例如 0-1,3-15
│ │ ├── [-r--r--r--] cpus_failed # 这个是分配给 Cell 的 CPU 中失败的那些
│ │ ├── [-r--r--r--] cpus_failed_list # 这个是分配给 Cell 的 CPU 中失败的那些的列表
│ │ ├── [-r--r--r--] name # Cell 的名字
│ │ ├── [-r--r--r--] state # Cell 的状态。running, running/locked, shut down, 或 failed 之一
│ │ └── [drwxr-xr-x] statistics # Cell 的统计数据
│ │ ├── [drwxr-xr-x] cpu0
│ │ ├── [drwxr-xr-x] cpu1
│ │ ├── [drwxr-xr-x] cpu10
│ │ ├── [drwxr-xr-x] cpu11
│ │ ├── [drwxr-xr-x] cpu12
│ │ ├── [drwxr-xr-x] cpu13
│ │ ├── [drwxr-xr-x] cpu14
│ │ ├── [drwxr-xr-x] cpu15
│ │ ├── [drwxr-xr-x] cpu3
│ │ ├── [drwxr-xr-x] cpu4
│ │ ├── [drwxr-xr-x] cpu5
│ │ ├── [drwxr-xr-x] cpu6
│ │ ├── [drwxr-xr-x] cpu7
│ │ ├── [drwxr-xr-x] cpu8
│ │ ├── [drwxr-xr-x] cpu9 # 以上这些是分配给当前 Cell 使用的所有逻辑 CPU。每个 CPU 节点展开后的内容和下面这些是一样只不过表示的是单个 CPU 的下面这些是以上所有 CPU 的汇总
│ │ ├── [-r--r--r--] vmexits_cpuid
│ │ ├── [-r--r--r--] vmexits_cr
│ │ ├── [-r--r--r--] vmexits_exception
│ │ ├── [-r--r--r--] vmexits_hypercall
│ │ ├── [-r--r--r--] vmexits_management
│ │ ├── [-r--r--r--] vmexits_mmio
│ │ ├── [-r--r--r--] vmexits_msr_other
│ │ ├── [-r--r--r--] vmexits_msr_x2apic_icr
│ │ ├── [-r--r--r--] vmexits_pio
│ │ ├── [-r--r--r--] vmexits_total # 全部 CPU 上发生的 VM Exits 总次数其他的 _xxx 则表示由于 xxx 原因产生的 VM Exits 数量。例如vmexits_xapic 就表示由于 xapic 产生的 VM Exits 次数
│ │ ├── [-r--r--r--] vmexits_xapic
│ │ └── [-r--r--r--] vmexits_xsetbv
│ └── [drwxr-xr-x] 1 # 第二个 Cell其中的内容与上面的一样
│ ├── [-r--r--r--] cpus_assigned
│ ├── [-r--r--r--] cpus_assigned_list
│ ├── [-r--r--r--] cpus_failed
│ ├── [-r--r--r--] cpus_failed_list
│ ├── [-r--r--r--] name
│ ├── [-r--r--r--] state
│ └── [drwxr-xr-x] statistics
│ ├── [drwxr-xr-x] cpu2 # 分配给当前 Cell 使用的所有逻辑 CPU。每个 CPU 节点展开后的内容和下面这些是一样
│ ├── [-r--r--r--] vmexits_cpuid
│ ├── [-r--r--r--] vmexits_cr
│ ├── [-r--r--r--] vmexits_exception
│ ├── [-r--r--r--] vmexits_hypercall
│ ├── [-r--r--r--] vmexits_management
│ ├── [-r--r--r--] vmexits_mmio
│ ├── [-r--r--r--] vmexits_msr_other
│ ├── [-r--r--r--] vmexits_msr_x2apic_icr
│ ├── [-r--r--r--] vmexits_pio
│ ├── [-r--r--r--] vmexits_total # 全部 CPU 上发生的 VM Exits 总次数其他的 _xxx 则表示由于 xxx 原因产生的 VM Exits 数量
│ ├── [-r--r--r--] vmexits_xapic
│ └── [-r--r--r--] vmexits_xsetbv
├── [-r--r--r--] console # 这个是 Jailhouse 的终端我们可以从中直接读取 Jailhouse 的 Log
├── [-r--------] core # 这里面是 Jailhouse 固件以及配置信息可以使用 tools/jailhouse-gcov-extract 来解析。访问时确保是 jailhouse disable 状态
├── [-r--r--r--] enabled # 指示 Jailhouse 是否启用。 1 表示启用0 表示未启用
├── [-r--r--r--] mem_pool_size # 内存池中的页数
├── [-r--r--r--] mem_pool_used # 内存池中已用的页数
├── [lrwxrwxrwx] module - ../../module/jailhouse # 这是一个由内核机制自动创建的符号链接指向当前目录的所有者创建者
├── [drwxr-xr-x] power # 这个是与电源管理相关的内容
│ ├── [-rw-r--r--] async
│ ├── [-rw-r--r--] autosuspend_delay_ms
│ ├── [-rw-r--r--] control
│ ├── [-r--r--r--] runtime_active_kids
│ ├── [-r--r--r--] runtime_active_time
│ ├── [-r--r--r--] runtime_enabled
│ ├── [-r--r--r--] runtime_status
│ ├── [-r--r--r--] runtime_suspended_time
│ └── [-r--r--r--] runtime_usage
├── [-r--r--r--] remap_pool_size # 重映射池中的页数
├── [-r--r--r--] remap_pool_used # 重映射池中已用的页数
└── [-rw-r--r--] uevent # 各种事件Root 设备是一个虚拟设备以该 Root 设备为父设备调用 kobject_create_and_add 就可以让其他设备可以挂在它的下面其中有些节点需要在执行相应的命令后才会有具体的内容 通过 misc_register(jailhouse_misc_dev); 注册 struct miscdevice 设备。加载驱动之后就会创建 /dev/jailhouse 这个设备。用户空间的 Jailhouse 的管理工具使用 ioctl() 系统调用通过 jailhouse.ko 创建的 /dev/jailhouse 这个文件向 jailhouse.ko 发送各种请求。调用 jailhouse_pci_register() 将自身注册为一个虚拟的 PCI 设备驱动程序以便它可以获取分配的设备。调用 register_reboot_notifier(jailhouse_shutdown_nb); 注册重启回调接口当内核出现 Kernel Halt、Kernel Restart 或 Kernel Power Off 时就会调用我们注册的回调函数。Jailhouse 注册之后主要用来关闭自身
jailhouse_ioctl() 各种请求通过内核最终到达 driver/main.c 中的 jailhouse_ioctl 这个函数。static long jailhouse_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) 这个函数解析收到的请求然后调用对应的接口来进一步处理。
static long jailhouse_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
{long err;switch (ioctl) {case JAILHOUSE_ENABLE:err jailhouse_cmd_enable((struct jailhouse_system __user *)arg);break;case JAILHOUSE_DISABLE:err jailhouse_cmd_disable();break;case JAILHOUSE_CELL_CREATE:err jailhouse_cmd_cell_create((struct jailhouse_cell_create __user *)arg);break;case JAILHOUSE_CELL_LOAD:err jailhouse_cmd_cell_load((struct jailhouse_cell_load __user *)arg);break;case JAILHOUSE_CELL_START:err jailhouse_cmd_cell_start((const char __user *)arg);break;case JAILHOUSE_CELL_DESTROY:err jailhouse_cmd_cell_destroy((const char __user *)arg);break;default:err -EINVAL;break;}return err;
}jailhouse*.bin jailhouse*.bin 是由源码根目录中的 hypervisor 目录中的源码在构建之后会生成针对不同的架构名字会有些区别它最终会被安装到 /lib/firmware 目录下。jailhouse*.bin 接收 jailhouse.ko 发来的超级调用用于硬件资源的分配。
内存布局 jailhouse*.bin 是一个具体特定结构的二进制文件在 jailhouse*.bin 的开头是一个 struct jailhouse_header 结构。这个 BIN 文件中的部分内容是在构建时就填充好的还有一部分是在驱动加载它时有驱动程序动态填充的。如下是 jailhouse*.bin 在内存中的布局 .header 定义于 hypervisor/setup.c 中并被链接文件强制放到了 BIN 的开头。链接脚本文件 hypervisor.lds 会再构建时被构建系统根据hypervisor/hypervisor.lds.S 自动创建生成就是简单的展开各种宏架构不同宏值不同。
JAILHOUSE_BASE JAILHOUSE_BASE 是 jailhouse*.bin 的链接地址它的具体值被定义到了 hypervisor/arch 中不同架构的 jailhouse_header.h 中。针对同一架构它一个固定的虚拟地址。 x86平台反汇编 objdump --source --all-headers --demangle --line-numbers --wide hypervisor/hypervisor-intel.o hypervisor/hypervisor-intel.lst 查看 ARM64 平台反汇编 aarch64-none-linux-gnu-objdump --source --all-headers --demangle --line-numbers --wide hypervisor/hypervisor.o hypervisor/hypervisor.lst 查看
入口点 jailhouse*.bin 本身不是一个可以直接运行的程序所以它没有显示定义入口点。作为虚拟机管理程序它本身来处理虚拟化相关的问题。无论何种架构都是通过特定架构 hypervisor/arch/*/entry.S 中的 int arch_entry(unsigned int cpu_id) 这个接口来开启虚拟化配置。
arch_entry() arch_entry() 函数必须在每个在线的 CPU 上被调用以便将系统控制权交给 Jailhouse。Jailhouse 将等待指定数量由 struct jailhouse_header 中的 .online_cpus 指定的 CPU 都完成初始化并且在所有启动初始化的 CPU 都完成之前该函数不会返回。在虚拟机监控程序激活期间未初始化的 CPU 在 Jailhouse 再次停用之前不能被任何单元使用。
函数原型 int arch_entry(unsigned int cpu_id)参数 cpu_id调用方 CPU 的唯一逻辑 ID 返回值0 表示成功其他值表示失败通常取值如下 -EIO (-5)lacking hardware capabilities or unsupported hardware state (as configured by Linux)-ENOMEM (-12) insufficient hypervisor-internal memory-EBUSY (-16) a required hardware resource is already in use-ENODEV (-19) CPU or I/O virtualization unit missing-EINVAL (-22) invalid system configuration-ERANGE (-34) a resource ID is out of supported range 对于一次初始化尝试初始化函数将始终在所有 CPU 上返回相同的代码。
entry() arch_entry() 内部最终通过调用定义于 hypervisor/setup.c 中的架构无关的 int entry(unsigned int cpu_id, struct per_cpu *cpu_data) 函数最终实现启动虚拟化功能。
Hypervisor 与 Cell 间接口 Jailhouse 虚拟机管理程序在运行时提供了三种与 Cell 交互的接口。第一种是只读检测接口。第二种是一组超调用Cell 可以通过执行特定于体系结构的指令来同步调用这些超调用从而切换到 Hypervisor 模式。第三种接口由位于每个 Cell 内存区域中的变量组成该内存区域在 Hypervisor 和特定 Cell 之间共享。
只读检测接口 这种接口对于那些不仅仅在 Jailhouse 的 Cell 内部工作的 Cell 代码非常有用。该 ABI 是特定于体系结构的到目前为止它仅适用于 x86 架构。在 x8 6架构上Jailhouse 在执行 cpuid 指令时修改返回的寄存器值如下所示
Hypercalls 超调用通常通过指定的指令发出该指令会导致从客户模式切换到虚拟机管理程序模式。在引起模式切换之前Cell 必须在预定义的寄存器或已知的内存位置准备好调用的参数。完成的超调用的返回码通过类似的通道传递。超调用 ABI 的详细信息是特定于体系结构的将在以下部分中定义。 这些调用会由定义于 hypervisor/control.c 中的 long hypercall(unsigned long code, unsigned long arg1, unsigned long arg2) 来进行分发然后进一步来处理。
long hypercall(unsigned long code, unsigned long arg1, unsigned long arg2)
{struct per_cpu *cpu_data this_cpu_data();cpu_data-public.stats[JAILHOUSE_CPU_STAT_VMEXITS_HYPERCALL];switch (code) {case JAILHOUSE_HC_DISABLE:return hypervisor_disable(cpu_data);case JAILHOUSE_HC_CELL_CREATE:return cell_create(cpu_data, arg1);case JAILHOUSE_HC_CELL_START:return cell_start(cpu_data, arg1);case JAILHOUSE_HC_CELL_SET_LOADABLE:return cell_set_loadable(cpu_data, arg1);case JAILHOUSE_HC_CELL_DESTROY:return cell_destroy(cpu_data, arg1);case JAILHOUSE_HC_HYPERVISOR_GET_INFO:return hypervisor_get_info(cpu_data, arg1);case JAILHOUSE_HC_CELL_GET_STATE:return cell_get_state(cpu_data, arg1);case JAILHOUSE_HC_CPU_GET_INFO:return cpu_get_info(cpu_data, arg1, arg2);case JAILHOUSE_HC_DEBUG_CONSOLE_PUTC:if (!CELL_FLAGS_VIRTUAL_CONSOLE_PERMITTED(cpu_data-public.cell-config-flags))return trace_error(-EPERM);printk(%c, (char)arg1);return 0;default:return -ENOSYS;}
}通信区域 通信区域是一个每个单元内的内存区域默认情况下虚拟机管理程序和特定单元都可以对其进行读写。这是一种可选的通信机制。如果某个单元需要使用该区域则必须通过其配置将该区域映射到单元的地址空间。如果单元配置为在通信区域方面是被动的单元标志 JAILHOUSE_CELL_PASSIVE_COMMREG并且该区域已被映射那么必须在单元配置中将其声明为只读。
管理工具 管理工具主要由源码 tools 目录下的 jailhouse.c 在构建之后生成的 jailhouse 可执行程序以及该目录下的一些 Python 脚本 jailhouse-* 组成。jailhouse 会被放到 /usr/local/sbin/jailhouse 目录下而那些 Python 脚本最终会被安装到 usr/local/libexec/jailhouse 目录下。
可执行程序 jailhouse jailhouse 这个可执行程序就是所有管理命令的入口它就是一个标准的用户空间 Linux C 程序。当我们执行 Jailhouse 命令时命令首先来到了 tools/jailhouse.c 的 int main(int argc, char *argv[])函数它负责解析传入的各个选项及参数然后调用相应的接口进一步处理。
int main(int argc, char *argv[])
{int fd;int err;if (argc 2)help(argv[0], 1);if (strcmp(argv[1], enable) 0) {err enable(argc, argv);} else if (strcmp(argv[1], disable) 0) {fd open_dev();err ioctl(fd, JAILHOUSE_DISABLE);if (err)perror(JAILHOUSE_DISABLE);close(fd);} else if (strcmp(argv[1], cell) 0) {err cell_management(argc, argv);} else if (strcmp(argv[1], console) 0) {err console(argc, argv);} else if (strcmp(argv[1], config) 0 ||strcmp(argv[1], hardware) 0) {call_extension_script(argv[1], argc, argv);help(argv[0], 1);} else if (strcmp(argv[1], --version) 0) {printf(Jailhouse management tool %s\n, JAILHOUSE_VERSION);return 0;} else if (strcmp(argv[1], --help) 0) {help(argv[0], 0);} else {help(argv[0], 1);}return err ? 1 : 0;
}Python 脚本 jailhouse-* 对于那些 Python 脚本我们也不直接使用而是则由 jailhouse 来帮我们调用的。具体就在 tools/jailhouse.c 中的 static void call_extension_script(const char *cmd, int argc, char *argv[]) 函数中通过 Linux 系统的进程调用函数 execvp 来实现。
static void call_extension_script(const char *cmd, int argc, char *argv[])
{const struct extension *ext;char new_path[PATH_MAX];char script[64];if (argc 3)return;for (ext extensions; ext-cmd; ext) {if (strcmp(ext-cmd, cmd) ! 0 ||strcmp(ext-subcmd, argv[2]) ! 0)continue;snprintf(new_path, sizeof(new_path), PATH%s:%s:%s,dirname(argv[0]), JAILHOUSE_EXEC_DIR,getenv(PATH) ? : );putenv(new_path);snprintf(script, sizeof(script), jailhouse-%s-%s,cmd, ext-subcmd);execvp(script, argv[2]);perror(execvp);exit(1);}
}jailhouse-gcov-extract Jailhouse 支持在运行时收集代码覆盖率信息gcov。gcovGNU Coverage 是一个测试代码覆盖率的工具工作原理是基于代码插桩code instrumentation技术。在编译源代码时通过添加 -ftest-coverage 和 -fprofile-arcs 选项这两个 GCC 编译器选项编译器会在生成的可执行文件中插入特殊的监控代码。这些监控代码将跟踪源代码中的每个执行路径并记录下来它们被执行的次数。
为了使用该特性必须新建 include/jailhouse/config.h 文件并在文件中将 CONFIG_JAILHOUSE_GCOV 定义为 1首先正常运行一个 Jailhouse 虚拟机最后 sudo jailhouse disable 禁用 Jailhouse但是不要卸载 jailhouse.ko执行 ./tools/jailhouse-gcov-extract 提取数据生成 *.gcda 文件使用其他上层工具例如lcov来处理生成 *.gcda 文件即可
配置文件 在 Jailhouse 中所有的 Cell 的硬件资源必须是静态分配的。因此在启动 Cell 之前我们必须要有一个配置文件这个配置文件告诉 Jailhouse 每个 Cell 可以使用哪些硬件资源。 Jailhouse 采用以 .cell 为扩展名的二进制文件作为配置文件而 .cell 文件是由一个包含一个 C 语言结构体变量来描述硬件资源的 .c 文件生成的。而我们需要根据 Jailhouse 给出的一些示例configs目录下书写自己的 .c 文件并进一步编译为 .cell 文件来使用。
使用命令 jailhouse config check [-h] SYSCONFIG [CELLCONFIG [CELLCONFIG ...]] 可以检查我们的配置文件
SYSCONFIG SYSCONFIG全局配置文件就是 Root Cell 对应的配置文件它告诉 Jailhouse 当前系统下所有可用的资源有哪些。当我们执行 jailhouse enable SYSCONFIG 时Jailhouse 就会将 SYSCONFIG 中描述符的资源放到 Root Cell 中。
对于 x86 架构Jailhouse 提供了 sudo jailhouse hardware check 命令来自动检测当前系统配置并提供了 sudo jailhouse config create sysconfig.csysconfig.c 名字可自定义 来自动生成针对当前系统的配置文件。注意通过以上命令生成的 sysconfig.c 文件的用户是 root我们可以使用命令 sudo chown zcs:zcs sysconfig.c 更改为自己的用户名和用户组这样再后续编辑是比较方便。 我们需要将生成的 sysconfig.c 文件放在 Jailhouse 源码的 configs/x86/ 目录中重新构建 Jailhouse 时构建系统会将自动为其中的 .c 生成一个相应的 .cell 文件。
CELLCONFIG CELLCONFIGCELL 配置文件就是 Non-root Cell 使用的配置文件定义了 Non-root Cell 可以使用的物理资源当我们创建 Non-root Cell 时Jailhouse 就会根据 Non-root Cell 的配置文件从全局配置文件Root Cell中分离出指定的资源。 对于 Non-root Cell 的配置文件需要参考 configs 目录下对应架构下的 .c 文件来手动创建。同样写好的 .c 文件需要放到 configs/x86/ 目录中重新构建 Jailhouse 时构建系统会将自动为其中的 .c 生成一个相应的 .cell 文件。
虚拟机固件 Jailhouse 虚拟机中运行的系统镜像或者是一个裸机程序固件被称为 inmate目前可以是 Linux、FreeRTOS、ERIKA3 RTOS、Zephyr 其中之一。对于 LinuxJailhouse 无法运行未经修改的 Linux 内核
参考
https://software-dl.ti.com/processor-sdk-linux/esd/docs/06_03_00_106/linux/Foundational_Components/Virtualization/Jailhouse.htmlhttps://www.elecfans.com/d/2338769.htmlhttps://variwiki.com/index.php?titleJailhouse_Guidehttps://blog.csdn.net/v6543210/article/details/113890847https://www.21ic.com/a/933932.htmlhttps://blog.csdn.net/v6543210/article/details/118031563https://www.21ic.com/a/933932.html