虚拟化 之一 详解 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。同时在构建时需要注意以下几点:

  1. 需要使能 CONFIG_JAILHOUSE_GUEST
  2. 禁用 CONFIG_SERIOCONFIG_PM_TRACE_RTC
  3. 一般还应该禁用所有不需要的驱动程序和特性,以避免不需要的 probe,并且使镜像大小和内存占用最小化

  在 ARM / ARM64 平台上,引导 Linux 内核要比在 x86 平台上简单得多,因此在这种情况下,我们不需要为 Non-root Cell 特别修改 Linux 内核。

内存布局

  Jailhouse 的实现需要使用一块连续内存,这块内存需要在启动 Linux 时保留出来。至于内存具体用哪一块,则取决于自己的设备,如下以 0x100000000 为例的示意图如下所示:
在这里插入图片描述

Cell 间通信

  虽然 Jailhouse 将硬件资源进行了划分到了不同的 Cell 中,通过虚拟机监控器实现了相互隔离,但在实际应用过程中,Cell 间也需进行通信。为此,Jailhouse 通过虚拟 ivshmem(Inter-VM shared memory) PCI 设备在 Cell 之间提供共享内存和信号机制。一个通道将两个分区 1:1 对应地连接起来。

环境要求

  Jailhouse 是一个依托于 Linux Kernel 的开放性从而直接使用硬件平台提供的虚拟化技术来实现的虚拟化解决方案。因此,Jailhouse 需要 Linux Kernel 的支持及硬件平台的虚拟化技术。

硬件要求

  Jailhouse 利用 Intel 的 VT-x、AMD 的 AMD-V 和 ARM 虚拟化扩展等硬件辅助虚拟化技术来划分物理资源和限制虚拟机对这些资源的访问,从而实现对系统资源的隔离控制。

x86 架构

  • 针对 Intel 平台,需要 64 位架构以及 VMX(Virtual Machine Extensions,Intel CPU 中使用 vmx 标识 Intel 的 VT-x 虚拟化技术) 技术,具体细节:
    • 具备 EPT(Extended 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_reason,trap 到 VMM 中。

    • 支持中断重映射的 Intel IOMMU(Intel 官方称为 VT-d(Virtualization Technology for Directed I/O))

  • 针对 AMD 平台,需要 64 位架构以及 SVM(AMD Secure Virtual Machine(这个是 AMD 内部的研发代号,后来统一使用 AMD-V 作为对外名称),AMD CPU 中使用 svm 来表示 AMD 的 AMD-V 虚拟化技术)技术:
    • 必须具备 NPT(Nested Page Tables,嵌套页表)支持。嵌套页表现在称为快速虚拟化索引(Rapid Virtualization Indexing,RVI)是 AMD 第二代处理器内存管理单元(MMU)硬件辅助虚拟化技术。
    • 推荐具备 Decode Assists 支持
    • AMD IOMMU(AMD 官方称为 AMD-Vi(AMD’s I/O Virtualization Technology))目前不被支持,但后续需要
  • 至少两个逻辑 CPU 核心。注意这里是逻辑 CPU 核心,不是物理 CPU 核心。
  • 在 x86 平台下,需要在 BIOS 或 UEFI 中开启虚拟化功能!
    在这里插入图片描述

  EPT 和 NPT 是 Intel 和 AMD 两家对于 Second Level Address Translation(SLAT,二级地址转换)在自家 CPU 上的具体实现。SLAT 是一种硬件辅助虚拟化技术,可以避免与软件管理的影子页表相关的开销。此外,IOMMU 也经常被我们称为 PCI 直通。

ARM 架构

  • ARMv8 架构或者是带有虚拟化扩展的 ARMv7 架构。ARM 的虚拟化扩展支持 SLAT,即由 Stage-2 MMU 提供的 Stage-2 页表。客户机使用 Stage-1 MMU。该支持在 ARMv7ve 架构中作为可选添加,并且在 ARMv8(32 位和 64 位)架构中也受支持。

  • 至少两个逻辑 CPU 核心

  • 支持如下 AArch32 架构的开发板

    • Banana Pi (see more)
    • Orange Pi Zero (256 MB version)
    • NVIDIA Jetson TK1
    • ARM 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 3000
    • LeMaker HiKey
    • NVIDIA Jetson TX1 and TX2
    • Xilinx ZCU102 (ZynqMP evaluation board)
    • NXP MCIMX8M-EVK

内核要求

  Jailhouse 的构建是依赖于 Linux Kernel 的,因此,必须使用对应的 Linux Kernel。但是,不同版本的 Linux Kernel 对于 Jailhouse 的支持情况不尽相同。在使用比较新的 Linux Kernel 时,需要对 Linux Kernel 进行打补丁,否则将出现各种错误!

x86 架构

  1. 必须禁用 Linux Kernel 对 VT-d IOMMU 的使用(DMAR)。这个可以通过在 GRUB 配置文件中设置 GRUB_CMDLINE_LINUX = "intel_iommu=off"(IOMMU 硬件有 intel/amd/arm 的等,一般用 intel 的硬件) 这个启动参数来处理。

    1. kvm 一定要用 intel_iommu=on,DPDK/SPDK 如果绑定 vfio-pci 那也一定要求 intel_iommu=on,如果绑定 uio/igb_uio 那么就不需要intel_iommu=on
    1. dmesg | grep -E "DMAR|IOMMU" 查看
  2. 要利用更快的 x2APIC,需要在内核中打开中断重新映射。这个需要再构建内核时启用 CONFIG_IRQ_REMAP 这个配置项

  3. Jailhouse 本身和每个 Cell 都需要一块连续的 RAM,这个必须在 Kernel 启动之前配置。通常是使用 memmapmem 这两个启动命令来实现。在 x86 平台上,这通常是在 GRUB 配置文件中通过添加 GRUB_CMDLINE_LINUX="memmap=82M\\\$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 Call(SVC)指令使用户模式程序可以请求操作系统服务。
    • Hypervisor Call(HVC)指令使客户操作系统能够请求 Hypervisor 服务。
    • Secure monitor Call(SMC)指令使普通世界能够请求安全世界服务。
  • PSCI 对 CPU 离线的支持。PSCI(Power 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 = kernel boot 参数)或修改 Device Tree(即保留内存节点)来实现。

源码

  Jailhouse 是由西门子的 Jan Kiszka 在 2013年以 GPLv2 协议开源的一个虚拟化解决方案。并很快得到了官方 Linux 内核的支持,在开源社区中获得了知名度和吸引力。

源文件

  Jailhouse 作为一个极度精简的虚拟化实现方案,其代码量还是非常小的,几万行代码量就实现了一个功能强大的虚拟机。

  • .githubci:这两个目录下是用来持续集成环境中使用的构建 Jailhouse 所需的 Linux Kernel 的相关配置文件,目前仅支持 GitHub Actions。

  • config:这个目录下就是针对 armarm64x86 架构下用于生成 Jailhouse 的 Cell 的配置文件对应的源文件。在对应架构下构建 Jailhouse 时,对应架构目录下的每个 .c 就会被构建为对应的 .cell 文件。

    1. 配置文件的内容其实就是一个 C 语言的结构体变量,因此使用的就是 .c 扩展名
    2. 该目录下的 .c 文件实际上都是一些示例,在真正使用时,我们需要提供自己的配置文件!
  • Documentation:Jailhouse 的文档对应的源码,它使用的是 Doxygen 文档系统。使用 sudo apt install doxygen 后,使用命令 make docs 就可以在 Documentation/generated/ 构建出对应的文档。
    在这里插入图片描述

  • driver:Jailhouse 的驱动源码,最终会被编译为 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 将重新编程 BAR(Base Address Registers)并定位到我们不希望/不允许的资源位置。
        所以,Jailhouse 充当了一个 PCI 虚拟驱动程序,在其他 Cell 使用设备时它会声明这些设备。在创建 Cell 时,设备将从其驱动程序中解绑,并绑定到 Jailhouse。当 Cell 被销毁时,Jailhouse 将释放其设备。当禁用 Jailhouse 时,它将释放所有已分配的设备。
        当释放设备时,它们将不再绑定到任何驱动程序,从 Linux 的角度来看,Jailhouse 虚拟驱动程序仍然会被视为有效的驱动程序。将设备重新分配给原始驱动程序必须手动完成。
    • sysfs.c/h:还通过 sysfs 虚拟文件系统(/sys/devices/jailhouse)向用户空间暴露 Jailhouse 的数据结构。
    • main.c/h:驱动入口
  • hypervisor:Jailhouse 用于管理各个虚拟机的工具的源码代码,最终会被编译为 jailhouse*.bin,被放到 /lib/firmware 目录下

  • include:Jailhouse 对外提供的各种 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 1
    
  • inmates:这里面是 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 的构建过程非常简单,但是由于不同的平台下的虚拟化技术的差异,在构建时遇到的问题也不一样,受限于博文篇幅,我们将在后续博文中详细学习在不同平台下的构建及使用。

基本组件

  完整的 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_MAJOR(10))。使用命令 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) 函数就会进行各种初始化,主要就干了以下四个事:

  1. 通过 root_device_register("jailhouse") 创建 /sys/devices/jailhouse 这个设备,然后调用 jailhouse_sysfs_init(jailhouse_dev) 初始化其中的内容,此后,用户空间就可以通过 /sys/devices/jailhouse 访问 Jailhouse 的数据结构。
    zcs@zcs-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 的 ID,Root 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								# 各种事件
    
    1. Root 设备是一个虚拟设备,以该 Root 设备为父设备调用 kobject_create_and_add 就可以让其他设备可以挂在它的下面
    2. 其中有些节点需要在执行相应的命令后才会有具体的内容
  2. 通过 misc_register(&jailhouse_misc_dev); 注册 struct miscdevice 设备。加载驱动之后,就会创建 /dev/jailhouse 这个设备。用户空间的 Jailhouse 的管理工具使用 ioctl() 系统调用通过 jailhouse.ko 创建的 /dev/jailhouse 这个文件向 jailhouse.ko 发送各种请求。
  3. 调用 jailhouse_pci_register() 将自身注册为一个虚拟的 PCI 设备驱动程序,以便它可以获取分配的设备。
  4. 调用 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 在内存中的布局:
在这里插入图片描述

arch_entry()

  jailhouse*.bin 本身不是一个可以直接运行的程序,所以它没有显示定义入口点。作为虚拟机管理程序,它本身来处理虚拟化相关的问题。无论何种架构,都是通过 int entry(unsigned int cpu_id) 这个接口来开启虚拟化配置。

  arch_entry() 函数必须在每个在线的 CPU 上被调用,以便将系统控制权交给 Jailhouse。Jailhouse 将等待指定数量(由 struct jailhouse_header 中的 .online_cpus 指定)的 CPU 都完成初始化,并且在所有启动初始化的 CPU 都完成之前,该函数不会返回。在虚拟机监控程序激活期间未初始化的 CPU 在 Jailhouse 再次停用之前不能被任何单元使用。

  • 函数原型: int entry(unsigned int cpu_id)
  • 参数:
    • cpu_id - unique logical ID of caller’s CPU
  • 返回值: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

entry()

  arch_entry() 内部最终通过架构无关的 entry() 函数最终实现启动虚拟化功能。

hypercall()

  jailhouse*.bin 作为虚拟机管理程序,对外提供了一系列的超级调用,这些调用首先由 hypercall() 来进行分发然后进一步来处理。

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;}
}

管理工具

  管理工具主要由源码 tools 目录下的 jailhouse.c 在构建之后生成的 jailhouse 可执行程序以及该目录下的一些 Python 脚本 jailhouse-* 组成。jailhouse 会被放到 /usr/local/sbin/jailhouse 目录下,而那些 Python 脚本最终会被安装到 usr/local/libexec/jailhouse 目录下。
在这里插入图片描述

可执行程序 jailhouse

  jailhouse 这个可执行程序就是所有管理命令的入口,它就是一个标准的用户空间 Linux C 程序。当我们执行 Jailhouse 命令时,命令首先来到了 tools/jailhouse.cint 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)。gcov(GNU Coverage) 是一个测试代码覆盖率的工具,工作原理是基于代码插桩(code instrumentation)技术。在编译源代码时,通过添加 -ftest-coverage-fprofile-arcs 选项这两个 GCC 编译器选项,编译器会在生成的可执行文件中插入特殊的监控代码。这些监控代码将跟踪源代码中的每个执行路径,并记录下来它们被执行的次数。

  1. 为了使用该特性,必须新建 include/jailhouse/config.h 文件,并在文件中将 CONFIG_JAILHOUSE_GCOV 定义为 1
  2. 首先正常运行一个 Jailhouse 虚拟机,最后 sudo jailhouse disable 禁用 Jailhouse,但是不要卸载 jailhouse.ko
  3. 执行 ./tools/jailhouse-gcov-extract 提取数据生成 *.gcda 文件
  4. 使用其他上层工具(例如,lcov)来处理生成 *.gcda 文件即可

配置文件

  在 Jailhouse 中,所有的 Cell 的硬件资源必须是静态分配的。因此,在启动 Cell 之前,我们必须要有一个配置文件,这个配置文件告诉 Jailhouse 每个 Cell 可以使用哪些硬件资源。

  Jailhouse 采用以 .cell 为扩展名的二进制文件作为配置文件,而 .cell 文件是由一个包含一个 C 语言结构体变量来描述硬件资源的 .c 文件生成的。而我们需要根据 Jailhouse 给出的一些示例(configs)目录下书写自己的 .c 文件,并进一步编译为 .cell 文件来使用。

Root Cell

  Root Cell 对应的配置文件就是全局配置文件,它告诉 Jailhouse 当前系统下所有可用的资源有哪些。对于 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 文件,以下是一个完整的操作过程。

Non-root Cell

  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 其中之一。对于 Linux,Jailhous 无法运行未经修改的 Linux 内核!

参考

  1. https://software-dl.ti.com/processor-sdk-linux/esd/docs/06_03_00_106/linux/Foundational_Components/Virtualization/Jailhouse.html
  2. https://www.elecfans.com/d/2338769.html
  3. https://variwiki.com/index.php?title=Jailhouse_Guide
  4. https://blog.csdn.net/v6543210/article/details/113890847
  5. https://www.21ic.com/a/933932.html
  6. https://blog.csdn.net/v6543210/article/details/118031563
  7. https://www.21ic.com/a/933932.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/25887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软如何打造数字零售力航母系列科普13 - Prime Focus Technologies在NAB 2024上推出CLEAR®对话人工智能联合试点

Prime Focus Technologies在NAB 2024上推出CLEAR对话人工智能联合试点 彻底改变您与内容的互动方式&#xff0c;从内容的创建到分发 洛杉矶&#xff0c;2024年4月9日/PRNewswire/-媒体和娱乐&#xff08;M&E&#xff09;行业人工智能技术解决方案的先驱Prime Focus Techn…

人工智能在医学领域的应用及技术实现

欢迎来到 Papicatch的博客 目录 &#x1f349;引言 &#x1f349; 医学影像分析 &#x1f348;技术实现 &#x1f34d;数据准备 &#x1f34d;模型构建 &#x1f34d;模型训练 &#x1f34d;模型评估 &#x1f34d;应用部署 &#x1f348;示例代码 &#x1f349; 基因…

操作系统真象还原:内存管理系统

第8章-内存管理系统 这是一个网站有所有小节的代码实现&#xff0c;同时也包含了Bochs等文件 8.1 Makefile简介 8.1.1 Makefile是什么 8.1.2 makefile基本语法 make 给咱们提供了方法&#xff0c;可以在命令之前加个字符’&#xff20;’&#xff0c;这样就不会输出命令本身…

微信小程序使用 “云函数“ 获取 “openid“

文章目录 1.前期准备2.具体操作步骤 1.前期准备 必须使用云开发已经配置好云开发 2.具体操作步骤 1.进入小程序开发工具→在云函数目录上右键→选中新建云函数 创建结束&#xff0c;自动上传&#xff08;必须确认已经上传才生效&#xff09; 2.进入对应页面的js文件&#…

QT 信号和槽 信号关联到信号示例 信号除了可以绑定槽以外,信号还可以绑定信号

信号除了可以关联到槽函数&#xff0c;还可以关联到类型匹配的信号&#xff0c;实现信号的接力触发。上个示例中因为 clicked 信号没有参数&#xff0c;而 SendMsg 信号有参数&#xff0c;所以不方便直接关联。本小节示范一个信号到信号的关联&#xff0c;将按钮的 clicked 信号…

【优化过往代码】关于vue自定义事件的运用

【优化过往代码】关于vue自定义事件的运用 需求说明过往代码优化思路优化后代码&#xff08;Vue2&#xff09;遇到问题记录 Vue2官方自定义指令说明文档 Vue3官方自定义指令说明文档 需求说明 进入某些页面需要加载一些外部资源&#xff0c;并在资源加载完后进行一些处理&…

【栈】2751. 机器人碰撞

本文涉及知识点 栈 LeetCode2751. 机器人碰撞 现有 n 个机器人&#xff0c;编号从 1 开始&#xff0c;每个机器人包含在路线上的位置、健康度和移动方向。 给你下标从 0 开始的两个整数数组 positions、healths 和一个字符串 directions&#xff08;directions[i] 为 ‘L’ …

MySQL-数据处理函数

026-distinct去重 select job from emp;加个 distinct 就行了 select distinct job from emp;注意&#xff1a;这个去重只是将显示的结果去重&#xff0c;原表数据不会被更改。 select 永远不会改变原数据 select distinct deptno, job from emp order by deptno asc;027-数…

步态控制之足旋转点(Foot Rotation Indicator, FRI)

足旋转点(Foot Rotation Indicator, FRI) 足旋转点是人形机器人步态规划中的一个关键概念,用于描述步态过程中机器人脚部的旋转和稳定性。FRI 可以帮助确定机器人在行走时是否稳定,以及如何调整步态以保持稳定。下面详细介绍FRI的原理,并举例说明其应用。 足旋转点(FRI…

R语言统计分析——图形的简单示例

参考资料&#xff1a;R语言实战【第2版】 1、示例一 # 绑定数据框mtcars attach(mtcars)# 打开一个图形窗口并生成一个散点图plot(wt,mpg)# 添加一条最优拟合曲线abline(lm(mpg~wt))# 添加标题title("Regression of MPG on weight") # 解除数据框绑定 detach(mtcar…

ES8.13 _bulk报错Malformed content, found extra data after parsing: START_OBJECT解决

在使用elaticsearch8.13.0使用批量创建索引时&#xff0c;根据谷粒中说的es7.9方法去批量操作请求&#xff1a; http://127.0.0.1:9200/shop/_doc/_bulk 注意1&#xff1a;设置header为Content-Type:application/x-ndjson,否则请求报错&#xff1a; {"error": &qu…

机器学习笔记:focal loss

1 介绍 Focal Loss 是一种在类别不平衡的情况下改善模型性能的损失函数最初在 2017 年的论文《Focal Loss for Dense Object Detection》中提出这种损失函数主要用于解决在有挑战性的对象检测任务中&#xff0c;易分类的负样本占据主导地位的问题&#xff0c;从而导致模型难以…

【recast-navigation-js】使用three.js辅助绘制Agent寻路路径

目录 说在前面setAgentTarget绘制寻路路径结果问题其他 说在前面 操作系统&#xff1a;windows 11浏览器&#xff1a;edge版本 124.0.2478.97recast-navigation-js版本&#xff1a;0.29.0golang版本&#xff1a;1.21.5上一篇&#xff1a;【recast-navigation-js】使用three.js辅…

linux:centos7升级libstdc++版本到3.4.26

下载&#xff0c;解压 wget http://www.vuln.cn/wp-content/uploads/2019/08/libstdc.so_.6.0.26.zip unzip libstdc.so_.6.0.26.zip 复制到【/usr/lib64】&#xff1a; cp libstdc.so.6.0.26 /usr/lib64创建软链接 cd /usr/lib64 sln libstdc.so.6.0.26 libstdc.so.6查看一…

Python | Leetcode Python题解之第144题二叉树的前序遍历

题目&#xff1a; 题解&#xff1a; class Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:res list()if not root:return resp1 rootwhile p1:p2 p1.leftif p2:while p2.right and p2.right ! p1:p2 p2.rightif not p2.right:res.append(p1.val)…

机器学习笔记 - LoRA:大型语言模型的低秩适应

一、简述 1、模型微调 随着大型语言模型 (LLM) 的规模增加到数千亿,对这些模型进行微调成为一项挑战。传统上,要微调模型,我们需要更新所有模型参数。这也称为完全微调 (FFT) 。下图详细概述了此方法的工作原理。 完全微调FFT 的计算成本和资源需求很大,因为更新每…

Vmess协议是什么意思? VLESS与VMess有什么区别?

VMess 是一个基于 TCP 的加密传输协议&#xff0c;所有数据使用 TCP 传输&#xff0c;是由 V2Ray 原创并使用于 V2Ray 的加密传输协议&#xff0c;它分为入站和出站两部分&#xff0c;其作用是帮助客户端跟服务器之间建立通信。在 V2Ray 上客户端与服务器的通信主要是通过 VMes…

【InternLM实战营第二期笔记】06:Lagent AgentLego 智能体应用搭建

文章目录 讲解为什么要有智能体什么是 Agent智能体的组成智能体框架AutoGPTReWooReAct Lagent & Agent LegoAgentLego 实操Lagent Web Demo自定义工具 AgentLego&#xff1a;组装智能体“乐高”直接使用作为智能体&#xff0c;WebUI文生图测试 Agent 工具能力微调 讲解 为…

idea如何使用git reset进行回退以及如何使用git stash将暂存区文件储藏,打包后重新恢复暂存区文件

最近遇到一个棘手的问题&#xff0c;本来按照计划表开发&#xff0c;但是项目经理突然让你改一个小bug&#xff0c;改完需要马上部署到线上&#xff0c;但是你手上的活做到一半还没做完&#xff0c;提交上去那肯定是不可行的。这时就可以使用git stash命令先把当前进度&#xf…

Discuz! X3.4发帖时间修改插件批量操作版

下载地址&#xff1a;Discuz! X3.4发帖时间修改插件批量操作版 发帖时间与回复时间说明 1、使用本插件修改发帖时间&#xff0c;则帖子中的回复楼层的时间会保持同步同间隔修改&#xff0c;所谓同步同间隔就是如果某个回复是在主题发布之后一小时回复的&#xff0c;那么修改之…