💡前言

如果手边有一台高通设备,没有内核源码,又想知道 Gunyah Hypervisor 到底占了多少内存、管理着哪些 VM、VM 之间怎么通信。那么用 adb 黑盒手段试试能摸到什么:

  • 从设备中提取 DTB 并反编译为完整 DTS,全文搜索 Gunyah 相关节点
  • reserved-memoryguestvm_loader 节点定位所有 VM 的内存布局和 VMID
  • /proc/iomemdmesg 交叉验证

背景

Gunyah 是高通自研的 Type-1 Hypervisor,运行在 ARMv9EL2,比 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 声明/hypervisorGunyah Hypervisor 1.0 主节点
预留内存/reserved-memory/hyp_region@80000000Hypervisor 代码/数据
预留内存/reserved-memory/hyp_reserved_region@e0a00000Hypervisor 附加预留
VM 加载/soc/qcom,guestvm_loader@e0b00000Trusted UI VM 加载器
VM 加载/soc/qcom,guestvm_loader@e0600000CPUSYS VM 加载器
跨 VM 通信/soc/qrtr-gunyahQRTR over Gunyah
跨 VM 通信/soc/gunyah-vsockvsock over Gunyah
内存共享/soc/qcom,mem-buf动态内存供给(HLOS 为 supplier)
Virtio 后端/soc/qcom,virtio_backend@0块设备透传给 Trust UI VM
CPU 调度/soc/qcom,hyp-core-ctlHypervisor 核心调度控制
内存热插拔/mem-offline运行时内存回收,4 MB 粒度

DTS 全文搜索比逐节点 ls /proc/device-tree/ 高效得多 — guestvm_loaderqrtr-gunyahmem-bufvirtio_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-size0x00F0240 字节
rx-message-size0x00F0240 字节
tx/rx-queue-depth0x0008各 8 条
free-irq-start0x03C0IRQ 960 起
interrupts0x03A0, 0x03A1GIC 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_region0x800000006 MB
qheebsp_reserved0xE00000006 MB
hyp_reserved0xE0A000001 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_vm0xE06000004 MB
trust_ui_vm0xE0B00000~75 MB
oem_vm0xBB00000080 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。

安全子系统与其他

节点基地址大小说明
qtee0xE9B000005 MBQTEE 安全环境
smem0x809000002 MB多处理器共享内存
tz_stat0xE88000001 MBTrustZone 统计
tags0xE890000018 MBTrustZone tags
cdsp_secure_heap0x80C0000070 MBCDSP 安全堆
mpss0x8BC00000306 MBModem 子系统
adsp0x85E0000033 MBAudio DSP
slpi0x8800000025 MBSensor Low Power Island
cdsp0x8990000032 MBCompute 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: 2Gunyah 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 vmid50CPUSYS VM 没有 qcom,isolate-cpus,不需要核心预留
mem-offline: offline segment at 0x980000000Gunyah 以 4 MB 粒度从 HLOS 回收物理内存页,地址在高位 DDR

对照 DTS 里 guestvm_loader 的 VMID 定义,dmesg 中之前不确定的 vmid 50 现在确认就是 CPUSYS VMmem-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 列表

VMID名称内存区域固件名备注
3HLOS (Android)主系统内存高级操作系统,mem-buf supplier
45 (0x2D)Trusted UI VM0xE0B00000 (~75 MB)trustedvm独占 CPU 5/6,PAS ID 28
50 (0x32)CPUSYS VM0xE0600000 (4 MB)cpusys_vmPAS ID 35,secure loader 带 no-shutdown
OEM VM0xBB000000 (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 通信栈拆解。

参考:

  1. 高通骁龙SM8450平台简介:Snapdragon 8 Gen 1 Mobile Platform
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐