💡前言
如果手边有一台高通设备,没有内核源码,又想知道 Gunyah Hypervisor 到底占了多少内存、管理着哪些 VM、VM 之间怎么通信。那么用 adb 黑盒手段试试能摸到什么:
- 从设备中提取 DTB 并反编译为完整 DTS,全文搜索 Gunyah 相关节点
- 从 reserved-memory 和 guestvm_loader 节点定位所有 VM 的内存布局和 VMID
- 用 /proc/iomem 和 dmesg 交叉验证
背景
Gunyah 是高通自研的 Type-1 Hypervisor,运行在 ARMv9 的 EL2,比 Linux 内核 (EL1) 更高特权。它在内核启动之前就已经占据了 EL2,并通过修改 DTB(Device Tree Blob) 的 reserved-memory 节点,告知 Linux 哪些物理内存区域不可触碰。Linux 解析设备树后会主动避开这些区域。
设备树就是切入点。本文以骁龙SM8450平台为例,层层拨开迷雾吧。
一、确认平台
adb devices List of devices attached 442daf63 device
adb shell “getprop ro.board.platform; getprop ro.build.type” taro userdebug
taro 是骁龙 SM8450 平台的内部代号。userdebug 构建可以通过 su 获取 root。
二、提取并反编译设备树
先 adb root,不然 adb pull 会因为 DAC 权限不足报 Permission denied:
adb root restarting adbd as root
adb pull /sys/firmware/fdt sm8450_live.dtb /sys/firmware/fdt: 1 file pulled, 0 skipped. 11.0 MB/s (746676 bytes in 0.065s)
746 KB 完整 DTB。用 dtc 反编译:
dtc -I dtb -O dts -o sm8450_live.dts sm8450_live.dtb
dtc 没装的话:sudo apt install device-tree-compiler。
拿到约 30000 行的 DTS,直接 grep 搜 Gunyah 相关节点:
grep -ni “gunyah|hyp_|guestvm|mem-buf|mem-offline” sm8450_live.dts
命中的节点(按功能分组):
| Hyperviso 声明 | /hypervisor | Gunyah Hypervisor 1.0 主节点 |
| 预留内存 | /reserved-memory/hyp_region@80000000 | Hypervisor 代码/数据 |
| 预留内存 | /reserved-memory/hyp_reserved_region@e0a00000 | Hypervisor 附加预留 |
| VM 加载 | /soc/qcom,guestvm_loader@e0b00000 | Trusted UI VM 加载器 |
| VM 加载 | /soc/qcom,guestvm_loader@e0600000 | CPUSYS VM 加载器 |
| 跨 VM 通信 | /soc/qrtr-gunyah | QRTR over Gunyah |
| 跨 VM 通信 | /soc/gunyah-vsock | vsock over Gunyah |
| 内存共享 | /soc/qcom,mem-buf | 动态内存供给(HLOS 为 supplier) |
| Virtio 后端 | /soc/qcom,virtio_backend@0 | 块设备透传给 Trust UI VM |
| CPU 调度 | /soc/qcom,hyp-core-ctl | Hypervisor 核心调度控制 |
| 内存热插拔 | /mem-offline | 运行时内存回收,4 MB 粒度 |
DTS 全文搜索比逐节点 ls /proc/device-tree/ 高效得多 — guestvm_loader、qrtr-gunyah、mem-buf、virtio_backend 这些散落在 /soc 下的节点,光靠目录遍历很难发现。
三、读取 hypervisor 节点
根目录下直接有个 hypervisor 节点,展开看看里面有什么:
adb shell “ls -R /proc/device-tree/hypervisor/” /proc/device-tree/hypervisor: compatible name qcom,gh-watchdog qcom,gunyah-vm qcom,resource-manager-rpc@ca6f43103373c01c
/proc/device-tree/hypervisor/qcom,gunyah-vm: compatible name qcom,vendor qcom,vmid
/proc/device-tree/hypervisor/qcom,resource-manager-rpc@ca6f43103373c01c: compatible interrupts name qcom,free-irq-start qcom,rx-message-size qcom,rx-queue-depth qcom,tx-message-size qcom,tx-queue-depth reg
三个子节点:Watchdog、VM 身份、Resource Manager RPC 通道。直接从 DTS 里看完整定义:
// DTS: /hypervisor
hypervisor {
compatible = "qcom,gunyah-hypervisor-1.0", "qcom,gunyah-hypervisor", "simple-bus";
qcom,gunyah-vm {
compatible = "qcom,gunyah-vm-id-1.0", "qcom,gunyah-vm-id";
qcom,vmid = <0x03>;
qcom,vendor = "Qualcomm";
};
qcom,gh-watchdog {
compatible = "qcom,gh-watchdog";
interrupts = <0x00 0x00 0x01>; // GIC SPI 0,Hypervisor 级看门狗
};
qcom,resource-manager-rpc@ca6f43103373c01c {
compatible = "qcom,resource-manager-1-0", "qcom,resource-manager",
"qcom,gunyah-message-queue", "qcom,gunyah-capability";
reg = <0xca6f4310 0x3373c01c 0xca6f4310 0x33737e8e>; // TX/RX capability ID
interrupts = <0x00 0x03a0 0x01 0x00 0x03a1 0x01>; // GIC SPI 928/929
qcom,is-full-duplex;
…
};
};
“VMID = 3” 即当前 Android 所在的 HLOS。不是 0 或 1,说明 Gunyah 在 Linux 启动前已经创建了更高优先级的 VM。
Resource Manager RPC 通道
reg 中两个 64-bit 值是 Gunyah capability ID,分别对应 TX/RX 消息队列。RM-RPC 底层基于 Gunyah Message Queue,通过 Capability(能力令牌)做权限控制。
消息队列配置(xxd 输出省略重复格式):
| tx-message-size | 0x00F0 | 240 字节 |
| rx-message-size | 0x00F0 | 240 字节 |
| tx/rx-queue-depth | 0x0008 | 各 8 条 |
| free-irq-start | 0x03C0 | IRQ 960 起 |
| interrupts | 0x03A0, 0x03A1 | GIC SPI 928 (TX), 929 (RX) |
| is-full-duplex | 存在 | TX/RX 同时工作,不需要半双工仲裁 |
DTS 中还能看到 qcom,gh-watchdog 子节点,中断号 GIC SPI 0 即 Hypervisor 级别的看门狗,用于检测 VM 挂死。
四、Guest VM 加载器与跨 VM 通信
Guest VM 加载器
// DTS 节点(简化格式)
qcom,guestvm_loader@e0b00000 {
compatible = "qcom,guestvm-loader";
qcom,pas-id = <0x1c>; // PAS ID 28
qcom,vmid = <0x2d>; // VMID 45 → Trusted UI VM
qcom,firmware-name = "trustedvm";
qcom,isolate-cpus; // 启动时隔离 CPU
qcom,reserved-cpus = <5 6>; // 独占 CPU 5、6
qcom,unisolate-timeout-ms = <12000>; // 12 秒后释放 CPU
memory-region = <&trust_ui_vm_region>;
};
qcom,guestvm_loader@e0600000 {
compatible = "qcom,guestvm-loader";
qcom,pas-id = <0x23>; // PAS ID 35
qcom,vmid = <0x32>; // VMID 50 → CPUSYS VM
qcom,firmware-name = "cpusys_vm";
memory-region = <&cpusys_vm_region>;
};
从这两个节点直接得到了之前缺失的信息:
- **VMID 45 (0x2D) = Trusted UI VM **:之前 dmesg 里 qcom_guestvm_loader@e0b00000 报的就是它
- VMID 50 (0x32) = CPUSYS VM:之前 dmesg 里 hyp_core_ctl: skipped for vmid50 就是这个
- Trusted UI VM 独占 CPU 5、6: qcom,reserved-cpus 加上 qcom,isolate-cpus 意味着启动该 VM 时,Linux 的调度器会被要求让出这两个核心
- 两个 VM 还各有一个 qcom,gh_vm_loader_sec 安全加载器对应节点,CPUSYS VM 的安全加载器额外带 qcom,no-shutdown
跨 VM 通信节点
qrtr-gunyah {
compatible = "qcom,qrtr-gunyah";
qcom,master;
gunyah-label = <3>;
peer-name = <2>; // 对端 peer ID
shared-buffer = <&trust_ui_vm_qrtr>; // 共享缓冲区 0xe55f3000
};
gunyah-vsock {
compatible = "qcom,gunyah-vsock";
qcom,master;
peer-name = <2>;
msgq-label = <3>;
};
HLOS 和 Trusted UI VM 之间有两条独立通信链路:
- QRTR over Gunyah :Qualcomm IPC Router 协议,走共享内存 trust_ui_vm_qrtr@e55f3000(36 KB),gunyah-label = 3 标识通道
- Gunyah vsock :类 Linux AF_VSOCK 的虚拟套接字,同样走 Gunyah Message Queue 两条链路的 peer-name = 2 指向同一个对端(Trusted UI VM 的内部标识)。
Virtio 块设备透传
qcom,trust_ui_vm@e55fc000 {
vm_name = "trustedvm";
shared-buffers = <&trust_ui_vm_vblk0_ring &trust_ui_vm_swiotlb>;
};
qcom,virtio_backend@0 {
compatible = "qcom,virtio_backend";
qcom,vm = <&trust_ui_vm>;
qcom,label = <0x11>; // 对应 vblk0_ring 的 gunyah-label
};
HLOS 为 Trusted UI VM 提供了一个 Virtio block 后端。vblk0_ring(16 KB)是 Virtio vring 描述符环,swiotlb(1 MB)是 DMA bounce buffer — Trusted UI VM 通过这个通道读写块设备,典型用途是加载安全界面所需的资源文件。
动态内存管理
qcom,mem-buf { compatible = “qcom,mem-buf”; qcom,mem-buf-capabilities = “supplier”; qcom,vmid = <3>; // HLOS 是内存供给方 };
mem-buf 让 HLOS 作为 “supplier” 向其他 VM 按需提供物理内存页。配合根节点下的 mem-offline 节点:
mem-offline { compatible = “qcom,mem-offline”; offline-sizes = <…>; granule = <0x400>; // 1024 页 = 4 MB };
Hypervisor 以 4 MB 粒度 从 HLOS 热移除物理内存段,转交其他 VM 使用,用完后再归还。dmesg 中 mem-offline: sent msg successfully to offline segment at phys addr 0x980000000 就是这个机制在工作。
五、reserved-memory 完整解析
DTS 里 reserved-memory 下的所有节点一次性全部可见。reg 属性格式为 <addr_hi addr_lo size_hi size_lo>,每个字段 32 位。以下数据直接从反编译后的 DTS 提取:
Hypervisor 核心区域
// DTS hyp_region@80000000 { no-map; reg = <0x00 0x80000000 0x00 0x00600000>; }; qheebsp_reserved_region@e0000000 { no-map; reg = <0x00 0xe0000000 0x00 0x00600000>; }; hyp_reserved_region@e0a00000 { no-map; reg = <0x00 0xe0a00000 0x00 0x00100000>; };
| hyp_region | 0x80000000 | 6 MB |
| qheebsp_reserved | 0xE0000000 | 6 MB |
| hyp_reserved | 0xE0A00000 | 1 MB |
虚拟机区域
cpusys_vm_region@e0600000 { no-map; reg = <0x00 0xe0600000 0x00 0x00400000>; }; trust_ui_vm_region@e0b00000 { no-map; reg = <0x00 0xe0b00000 0x00 0x04af3000>; }; oem_vm_region@bb000000 { no-map; reg = <0x00 0xbb000000 0x00 0x05000000>; };
| cpusys_vm | 0xE0600000 | 4 MB |
| trust_ui_vm | 0xE0B00000 | ~75 MB |
| oem_vm | 0xBB000000 | 80 MB |
Trusted UI VM 区域内部还有三个子区域,DTS 里带 gunyah-label 标记:
trust_ui_vm_qrtr@e55f3000 { no-map; reg = <0x00 0xe55f3000 0x00 0x00009000>; // 36 KB, QRTR 共享缓冲 }; trust_ui_vm_vblk0_ring@e55fc000 { no-map; reg = <0x00 0xe55fc000 0x00 0x00004000>; // 16 KB, Virtio vring gunyah-label = <0x11>; }; trust_ui_vm_swiotlb@e5600000 { no-map; reg = <0x00 0xe5600000 0x00 0x00100000>; // 1 MB, DMA bounce buffer gunyah-label = <0x12>; };
gunyah-label 是 Gunyah Hypervisor 用于匹配共享内存段与 Virtio 后端的标识。0x11 对应 /soc/qcom,virtio_backend@0 的 qcom,label。
安全子系统与其他
| qtee | 0xE9B00000 | 5 MB | QTEE 安全环境 |
| smem | 0x80900000 | 2 MB | 多处理器共享内存 |
| tz_stat | 0xE8800000 | 1 MB | TrustZone 统计 |
| tags | 0xE8900000 | 18 MB | TrustZone tags |
| cdsp_secure_heap | 0x80C00000 | 70 MB | CDSP 安全堆 |
| mpss | 0x8BC00000 | 306 MB | Modem 子系统 |
| adsp | 0x85E00000 | 33 MB | Audio DSP |
| slpi | 0x88000000 | 25 MB | Sensor Low Power Island |
| cdsp | 0x89900000 | 32 MB | Compute DSP |
六、/proc/iomem 交叉验证
adb shell cat /proc/iomem
输出很长,摘出 Hypervisor/VM 相关的段(省略外设寄存器部分):
80000000-808f3fff : reserved 80900000-851fffff : reserved 85700000-87efffff : reserved 88000000-8b91bfff : reserved 8ba00000-9fcfffff : reserved … bb000000-bfffffff : reserved ← oem_vm (80 MB) … e0000000-e56fffff : reserved ← qheebsp + cpusys_vm + hyp_reserved + trust_ui_vm e55f3000-e55fbfff : soc:qrtr-gunyah trust_ui_vm_qrtr@e55f3000 e8800000-e9ffffff : reserved ← tz_stat + tags + qtee
/proc/iomem 中唯一直接出现 gunyah 字样的条目是 soc:qrtr-gunyah trust_ui_vm_qrtr@e55f3000,这是 Trusted UI VM 与 HLOS 之间的 QRTR 通信缓冲区。
以 oem_vm_region 为例验证地址对应关系:
0xBB00_0000 + 0x0500_0000 – 1 = 0xBFFF_FFFF
与 iomem 中的 bb000000-bfffffff 完全吻合。
七、dmesg 运行时日志
adb shell dmesg | grep -i “gh_|hyp_core|guestvm|mem-offline|vmid” [ 10.591707] gh_msgq: Registered client for label: 2 [ 34.661075] qcom_guestvm_loader soc:qcom,guestvm_loader@e0b00000: Expected a notification from vmid = 45, but received one from vmid = 50 [ 34.681893] hyp_core_ctl: Reservation scheme skipped for other VM vmid50 [ 122.181100] mem-offline: sent msg successfully to offline segment at phys addr 0x980000000 [ 122.317201] mem-offline: sent msg successfully to offline segment at phys addr 0x9c0000000
| gh_msgq: Registered client for label: 2 | Gunyah Message Queue 驱动注册,label 2 对应 RM-RPC 通道 |
| qcom_guestvm_loader@e0b00000 | 加载 Trusted UI VM(VMID 45),对应 DTS 中 guestvm_loader 节点 |
| Expected vmid=45, received vmid=50 | 先收到了 CPUSYS VM (vmid 50) 的通知,再收到 Trusted UI VM (vmid 45) |
| hyp_core_ctl: skipped for vmid50 | CPUSYS VM 没有 qcom,isolate-cpus,不需要核心预留 |
| mem-offline: offline segment at 0x980000000 | Gunyah 以 4 MB 粒度从 HLOS 回收物理内存页,地址在高位 DDR |
对照 DTS 里 guestvm_loader 的 VMID 定义,dmesg 中之前不确定的 vmid 50 现在确认就是 CPUSYS VM。mem-offline 配合 DTS 中 granule = <0x400> (4 MB),说明 Gunyah 支持运行时内存热插拔 — Hypervisor 动态从 HLOS 回收物理内存段转交其他 VM,用完后归还。
八、内存布局总览
物理地址空间 (Gunyah 相关)
┌─────────────────────────────────────┐ 0x80000000
│ hyp_region (6 MB) │ Gunyah Hypervisor 代码/数据
├─────────────────────────────────────┤ 0x80600000
│ xbl_dt_log (256 KB) │ 引导日志
├─────────────────────────────────────┤ 0x80900000
│ smem_region (2 MB) │ 共享内存
├─────────────────────────────────────┤
│ (ADSP/SLPI/CDSP/MPSS 等子系统) │
├─────────────────────────────────────┤ 0xBB000000
│ oem_vm_region (80 MB) │ OEM 虚拟机 (VMID ?)
├═════════════════════════════════════╡ 0xE0000000
│ qheebsp_reserved (6 MB) │ QHEE BSP
├─────────────────────────────────────┤ 0xE0600000
│ cpusys_vm_region (4 MB) │ CPUSYS 虚拟机 (VMID 50)
├─────────────────────────────────────┤ 0xE0A00000
│ hyp_reserved (1 MB) │ Hypervisor 附加预留
├─────────────────────────────────────┤ 0xE0B00000
│ trust_ui_vm_region (~75 MB) │ Trusted UI 虚拟机 (VMID 45)
│ ├ qrtr@e55f3000 (36 KB) │ QRTR 共享缓冲
│ ├ vblk0_ring@e55fc000 (16 KB) │ Virtio vring [label=0x11]
│ └ swiotlb@e5600000 (1 MB) │ DMA bounce [label=0x12]
├─────────────────────────────────────┤ 0xE8800000
│ tz_stat (1 MB) + tags (18 MB) │ TrustZone
├─────────────────────────────────────┤ 0xE9B00000
│ qtee_region (5 MB) │ QTEE 安全环境
└─────────────────────────────────────┘ 0xEA000000
仅 Hypervisor + VM 区域合计约 172 MB,加上 TZ、QTEE 和各子系统固件,总预留内存超过 500 MB。
九、已确认的 VM 列表
| 3 | HLOS (Android) | 主系统内存 | — | 高级操作系统,mem-buf supplier |
| 45 (0x2D) | Trusted UI VM | 0xE0B00000 (~75 MB) | trustedvm | 独占 CPU 5/6,PAS ID 28 |
| 50 (0x32) | CPUSYS VM | 0xE0600000 (4 MB) | cpusys_vm | PAS ID 35,secure loader 带 no-shutdown |
| — | OEM VM | 0xBB000000 (80 MB) | — | DTS 中仅有 reserved-memory 定义 |
| — | Resource Manager | 运行在 EL2 | — | 资源协调,通过 RM-RPC 与 HLOS 通信 |
特权层级和通信关系:
EL3: TrustZone / QTEE
↓ │
EL2: Gunyah Hypervisor + Resource Manager
│
└─ RM-RPC (全双工消息队列)
↓ │
EL1: HLOS VM (VMID 3) ←→ Trusted UI VM (VMID 45)
│ CPU 5,6
│ mem-buf (supplier→consumer)
│
├─ CPUSYS VM (VMID 50)
└─ OEM VM (VMID ?)
↓
EL0: Android 用户空间
附录 — 命令速查
本文所有 adb shell 命令均在 adb root 之后执行。不 adb root 的话,adb pull fdt 和属性读取会 Permission denied,/proc/iomem 地址显示全零,dmesg 报 klogctl 权限不足。ls /proc/device-tree/ 是唯一不需要 root 就能用的。
# 平台确认
adb shell "getprop ro.board.platform; getprop ro.build.type"
# 提取并反编译设备树
adb root
adb pull /sys/firmware/fdt sm8450_live.dtb
dtc -I dtb -O dts -o sm8450_live.dts sm8450_live.dtb
# DTS 全文搜索
grep -ni "gunyah\\|hyp_\\|guestvm\\|mem-buf\\|mem-offline" sm8450_live.dts
# 交叉验证
adb shell cat /proc/iomem
adb shell dmesg | grep -i "gh_\\|hyp_core\\|gunyah\\|guestvm\\|vmid\\|mem-offline"
📢下一篇介绍
下一篇,我们将详细介绍从 HVC 指令到消息队列:Gunyah 通信栈拆解。

