6.7 KiB
6.7 KiB
Jailhouse:cell 配置 / park / loadable 内存 / ivshmem
Jailhouse 是**静态分区** hypervisor:**配置文件比 inmate 的 ELF 本身更重要**。它没有"VM 运行时申请资源"这回事——.cell 配置直接规定 CPU 集合、内存区、IRQ 芯片、PIO、PCI 与权限。
所以分析一个 inmate:
inmate 镜像 + .cell 配置 + root cell 配置 + 宿主 DT/ACPI
缺了配置那一半,关于"它为什么能/不能访问某个地址"的判断基本都是猜。 </CORE RULE>
一、资源归属在配置里,不在运行时协商
- cell 描述符包含:CPU set、内存区(含
phys_start/virt_start/size/ 标志)、IRQ、PIO、PCI 归属与权限 - 内存区标志是判据:
READ/WRITE/EXECUTE/DMA/COMM_REGION/LOADABLE(注意DMA标志缺失会让非 root Linux cell 不把这些区识别为 RAM) jailhouse cell load会把镜像装进配置指定的内存区,装载地址必须落在映射区内(例如未设virt_start而镜像落在低内存之外,就会以 "Invalid argument" 失败——这是配置问题,不是镜像问题)
二、越权访问 → CPU 被 park(不是 guest 崩溃)
- 访问未映射给该 cell 的内存/外设时,hypervisor 记录 "Unhandled data read" / "FATAL: unhandled trap",随后 "Parking CPU n (Cell: ...)"
- 被 park 的 CPU/cell 等于停止运行
所以看到"程序突然不动了"、又找不到 page fault/总线错误的常规证据,先对照 cell.mem_regions、PCI 与 IRQ 归属:非法访问被 hypervisor 拦下并停机是一等公民的解释。
三、JAILHOUSE_MEM_LOADABLE:装载窗口是一次性的
- 标记为
LOADABLE的内存区可以映射进 root cell,用于装载/重装镜像 - cell 启动时,这些映射会被撤销("对标记为 loadable 的区域的 root cell 访问被收回")
- 需要重装时走 Cell Set Loadable 超话:关停运行中的 cell,并把其 loadable 区域重新映射回 root cell
- 相关的错误码有区分度:非 root cell 发起或被目标 cell 拒绝关停 →
-EPERM;cell 不存在 →-ENOENT;root cell 不能被设为 loadable →-EINVAL - 后来还加入了在 Cell Set Loadable 时复位 PCI 设备的行为(此前活跃的设备先静默、停止 DMA,避免与装载冲突)
典型现象:
root Linux 启动前能直接访问 0xXXXX
inmate 启动后再访问 → fault
这是设计如此,不是"内存被保护起来了/被 hook 了"。
四、ivshmem:状态从共享内存协议里读,不要指望 pending 位
ivshmem v2 的寄存器语义与标准 PCI 设备故意不同:
- Interrupt Control Register:bit 0 使能中断;若启用 one-shot 模式,设备在每次投递中断时把 bit 0 清零——这是给 UIO 类驱动的自动节流,免去一次 VM-exit
- 设备没有 pending interrupt 的概念:读 MSI-X 的 Pending Bit Array 永远返回 0;INTx 方面 Status Register 的 Interrupt Status 位从不置位(与 PCI 规范有偏差),没有"pending INTx"信息
- 规范的做法是:事件状态从共享内存里的协议信息推导
- Doorbell Register:只写;低 16 位是向量号,高 16 位是目标 ID;写一个目标未使能的向量没有效果,写不存在的目标也没有效果;读该寄存器未定义
- State Register:写入会更新本地状态,并在远端设备上触发 MSI-X 向量 0(或 INTx),前提是新值与旧值不同;对端通过比较本地副本区分"状态变化"与"doorbell 事件"
- State Table:共享内存起始处的只读区,每个 peer 一个 32 位状态值;复位/断开时清零
- 协议类型编码在 PCI Class Code 的 interface/sub-class 里(例如虚拟点对点以太网、用户定义区间、Virtio over shared memory)
推论:
- "收到中断但 PBA 恒为 0" 不是 hypervisor bug;"没有 pending 位可查"也不能推导出"没有事件"
- 排查要以共享内存里的协议状态为准,必要时结合 State Table/State Register
五、hypervisor 地址空间里扫不到 inmate 内存是正常的
Jailhouse 不会把整个 cell 永久映射进 hypervisor 地址空间:只有显式共享页、以及处理 MMIO 等场景下临时映射的页才可见,而且临时映射可能是 per-CPU 的。
所以"用 hypervisor VA 直接遍历所有 VM RAM"的取证/调试工具会漏掉大量内容——这不是内存被藏了。
决策树
拿到 Jailhouse 目标
├─ 先拿配置:.cell / root cell config / 宿主 DT·ACPI
│ ├─ 内存区标志(含 DMA)与 virt_start 是否与镜像匹配
│ └─ PCI / IRQ / PIO 归属
├─ 访问某地址后程序停住
│ ├─ 查 hypervisor 日志:Unhandled trap → Parking CPU n
│ └─ 对照该地址是否属于本 cell(不是 → 违规,不是驱动 bug)
├─ root cell 能访问 → cell 启动后不能
│ └─ LOADABLE 映射在 cell 启动时被撤销;重装需 Cell Set Loadable
├─ ivshmem 中断相关
│ ├─ PBA 恒 0 / Status 位不置位 → 设备本就不维护 pending 语义
│ ├─ 事件状态去共享内存协议里读(State Table / State Register)
│ └─ one-shot 模式下 Interrupt Control bit0 会被设备清零
└─ 在 hypervisor VA 里找不到某 cell 的内存
└─ 只有共享页与临时映射可见,且临时映射可能是 per-CPU
工具与验证
- 配置:cell 描述符(内存区与标志、PCI/IRQ/PIO 归属)、root cell 配置、宿主 DT/ACPI
- 违规判定:hypervisor 控制台输出(Unhandled trap / Parking CPU)
- ivshmem:寄存器区(Interrupt Control / Doorbell / State)、State Table、共享内存协议状态
- 验证:能同时说清「资源归属(配置里怎么分的)+ 访问为何被拦(违规 vs 映射撤销)+ 事件状态从哪里读」
该平台的坑(汇总)
- 只看 inmate ELF 不看 cell 配置:资源归属全在配置里
- 把"访问后停机"当 guest 崩溃/驱动 bug:越权访问会被 hypervisor park 掉 CPU
- 把 cell 启动后 root cell 失去访问当异常:
LOADABLE映射在启动时被撤销,重装走 Cell Set Loadable - 忘了内存区要标
DMA:否则非 root cell 不把该区当 RAM,症状像"内存不见了" - 用 MSI-X PBA 判断有无事件:ivshmem 不维护 pending 语义,PBA 恒 0
- 把 Status Register 的 Interrupt Status 位当 pending 依据:该位从不置位
- 读 Doorbell 寄存器:它只写,读未定义
- 用 hypervisor VA 遍历所有 VM 内存:只有共享页与临时映射可见
- 把 one-shot 模式下 bit0 被清零当"中断被吞":那是设备端的自动节流设计