-
Notifications
You must be signed in to change notification settings - Fork 43
Description
设备树节点直通客户机设计
一、直通核心原则
设备树节点直通无需传递完整设备树,仅需精准识别并处理目标设备的核心节点与依赖资源,确保客户机可正常驱动设备。若遗漏关键依赖(如中断、时钟),会导致设备初始化失败或功能异常。
术语解释:
术语 | 解释 |
---|---|
设备树 (Device Tree) | 一种描述硬件组件及其连接关系的数据结构,用于将硬件信息传递给操作系统。 |
节点 (Node) | 设备树中的基本单元,代表一个硬件设备或组件。 |
属性 (Property) | 节点内的键值对,用于描述设备的特性和配置(如地址、中断号)。 |
直通 (Passthrough) | 一种虚拟化技术,允许虚拟机直接访问和控制物理硬件设备。 |
Phandle | 设备树中用于唯一标识和引用节点的32位整数值,类似于指针。 |
DTS/DTSI | 设备树源文件(.dts)和包含文件(.dtsi),后者类似于头文件,用于共享通用配置。 |
DTB | 由DTS编译生成的二进制设备树文件(.dtb),由Bootloader加载并传递给内核。 |
二、直通策略核心要点
处理对象 | 具体要求 | 说明 |
---|---|---|
核心设备节点 | 必须直通 | 如目标 UART 节点/soc/uart@2800c000 ,需完整映射其reg (寄存器内存区域)属性 |
中断控制器 | 直通或虚拟化 | 设备中断需通过控制器分发,可选择直通物理控制器或由 Hypervisor 提供虚拟中断控制器 |
时钟源 | 直通或模拟 | 若设备依赖特定时钟(如 UART 的uartclk /apb_pclk ),需直通时钟节点或由 Hypervisor 模拟时钟信号 |
内存区域 | 强制映射 | 设备reg 属性定义的寄存器地址空间,必须完整映射到客户机地址空间 |
父总线节点 | 按需处理 | 如soc 总线节点,仅当包含共享资源(如总线仲裁器)时需处理,否则无需直通 |
三、设备树节点依赖关系识别基础:phandle
1. phandle
的声明方式
(1)显式声明:
在节点中使用 phandle
属性直接指定一个唯一整数:
node {
...
phandle = <0x100>; // 显式设置 phandle 值为 0x100
};
(2)隐式声明:
使用 label
(标签)为节点命名,编译器会自动生成 phandle
。其他节点可以通过 &label
来引用该节点,编译器会将 &label
替换为对应的 phandle
。
clock: clock-controller@fdd20000 { // 定义标签 clock
compatible = "rockchip,rk3568-cru";
reg = <0x0 0xfdd20000 0x0 0x1000>;
};
i2c@fe5b0000 {
clocks = <&clock 0x14a>; // 通过 &clock 引用时钟节点
};
clock
是节点的标签。&clock
会被编译器替换为clock
节点对应的phandle
值。
2. phandle 引用规则
-
源码级(dts 文件):使用
&label
引用,如clocks = <&clock 0x14a>
,&clock
指向标签为clock
的时钟节点 -
编译后(dtb 文件):
&label
自动转换为phandle
数值,如clocks = <0x0c 0x14a>
,0x0c
即clock
节点的phandle
值 -
逆向解析(dtb 反编译):需通过
phandle
数值匹配对应的节点声明,还原节点引用关系
四、设备树相关节点查找流程
以目标节点/soc/uart@2800c000
为例,完整查找流程如下:
1. 识别目标节点
/ {
compatible = "phytium,pe2204";
interrupt-parent = <0x01>;
#address-cells = <0x02>;
#size-cells = <0x02>;
model = "Phytium Pi Board";
soc {
compatible = "simple-bus";
#address-cells = <0x02>;
#size-cells = <0x02>;
dma-coherent;
ranges;
uart@2800c000 {
compatible = "arm,pl011\0arm,primecell";
reg = <0x00 0x2800c000 0x00 0x1000>;
interrupts = <0x00 0x53 0x04>;
clocks = <0x0c 0x0c>;
clock-names = "uartclk\0apb_pclk";
status = "okay";
};
...
};
...
};
2. 解析依赖属性(phandle 引用)
(1)查找中断控制器:
interrupt-parent = <0x01>
指向phandle = 0x01
。- 搜索设备树中
phandle = <0x01>
的节点:
interrupt-controller@30800000 {
compatible = "arm,gic-v3";
phandle = <0x01>; // 匹配 phandle 0x01
...
};
- 结论:需处理中断控制器节点
interrupt-controller@30800000
(直通或虚拟化)。
(2)查找时钟源:
clocks = <0x0c 0x0c>
指向phandle = <0x0c>
(两个时钟源相同)。- 搜索
phandle = <0x0c>
的节点:
clk100mhz {
compatible = "fixed-clock";
phandle = <0x0c>; // 匹配 phandle 0x0c
clock-frequency = <0x5f5e100>; // 100 MHz
};
- 结论:需处理时钟节点
clk100mhz
(直通或模拟)。
(3)其他依赖
- 若目标节点包含
dmas
(DMA 依赖)、power-domains
(电源域依赖)、resets
(复位依赖)等属性,需按上述流程逐一解析,本例中 UART 节点无此类依赖。
3. 递归检查进一步依赖
直接依赖节点可能存在自身依赖,需递归追踪至 “无依赖的根节点”,避免遗漏层级依赖。
-
中断控制器依赖检查:
interrupt-controller@30800000
无interrupt-parent
、clocks
等属性,无额外依赖; -
时钟源依赖检查:
clk100mhz
为fixed-clock
(固定时钟),无父时钟节点,无额外依赖; -
若存在复杂依赖(如时钟节点依赖 PLL 时钟),需继续追踪父时钟节点,直至根时钟源(如晶振节点)。
五、节点示例及说明
1. 直接依赖属性(必查)
这些属性直接引用其他节点:
(1) 时钟依赖
clocks = <&cru 0x14a>, <&cru 0x149>;
clocks = <0x23 0x14a>;
- 查找所有被引用的时钟节点(通过
&cru
标签或phandle
值) - 检查时钟节点的依赖链(如父时钟、PLL 等)
项目 | 说明 |
---|---|
&cru |
时钟控制器的标签引用(源码级) |
0x23 |
时钟控制器的 phandle 值(编译后) |
0x14a |
时钟控制器内部的时钟线 ID(具体含义查芯片手册),查找父节点时不需要 |
直通要求 | 必须包含时钟控制器节点及其所有父时钟节点 |
(2) 中断依赖
interrupt-parent = <&gic>; // 中断控制器
interrupts = <0x00 0x30 0x04>; // 中断号
- 查找中断控制器节点(如
interrupt-controller@fd400000
) - 检查中断扩展(
interrupts-extended
)
(3) Pin 控制依赖
i2c@fe5b0000 {
pinctrl-names = "default", "sleep"; // 定义两个状态
pinctrl-0 = <&i2c2m0_xfer>; // 默认状态配置
pinctrl-1 = <&i2c2_sleep>; // 睡眠状态配置
};
- 查找所有被引用的 pinctrl 节点(如
i2c2m0_xfer
) - 检查 pinctrl 的父节点(如
pinctrl
控制器) - 通过pinctrl-names后面的参数数量可判断有多少个
pinctrl-<n>
,通过pinctrl-<n>
查找关联节点
(4) DMA 依赖
dmas = <&dmac 0x10>, <&dmac 0x11>;
- 查找 DMA 控制器节点(如
dmac@fe530000
)
(5) 电源管理依赖
power-domains = <&power RK3568_PD_BUS>;
- 查找电源域控制器节点(如
power-controller
)
2. 间接依赖关系(关键扩展)
(1) 时钟链依赖
对每个时钟节点递归检查:
cru: clock-controller@fdd20000 {
clocks = <&pmucru CLK_PMU_24M_OTP>; // 父时钟
clock-names = "xin24m";
};
- 追踪时钟的父时钟节点(直到根时钟)
- 检查时钟提供者的依赖(如晶振节点)
(2) 中断级联
对中断控制器递归检查:
gic: interrupt-controller@fd400000 {
interrupt-parent = <&gic>; // 自引用或级联
};
- 对于级联中断控制器,追踪到根中断控制器
- 检查 MSI 父节点(如 PCIe 场景)
3. 总线拓扑依赖(结构关系)
(1) 父总线节点
soc {
bus@ff800000 {
i2c@fe5b0000 { ... };
};
};
- 确认直接父节点(如
bus@ff800000
) - 检查父总线的
compatible
和状态
(2) 兄弟节点冲突
查找同总线上的节点:
bus@ff800000 {
i2c@fe5b0000 { ... };
spi@fe610000 { ... }; // 可能共享资源
};
- 检查地址范围是否有重叠
- 检查共享资源(如时钟、中断号)
4. 特殊依赖
(1) 复位控制器
resets = <&cru SRST_I2C2>, <&cru SRST_I2C2_APB>;
reset-names = "i2c", "apb";
- 查找复位控制器节点(常与时钟控制器相同)
(2) IOMMU 依赖
iommus = <&iommu 0>;
- 查找 IOMMU 控制器节点
- 检查 IOMMU 的电源域和时钟
(3) 物理层(PHY)依赖
phys = <&usb2phy 0>;
phy-names = "usb2-phy";
- 查找 PHY 控制器节点
- 检查 PHY 的时钟和复位
六、节点依赖完整性检查表
为确保无依赖遗漏,需按以下类别逐项检查,覆盖直接、间接、拓扑及特殊依赖场景。
检查类别 | 检查项 | 示例属性 | 处理要求 |
---|---|---|---|
直接依赖 | 时钟引用 | clocks = <&cru 0x14a> |
追踪所有引用的时钟节点,及时钟节点的父时钟链 |
中断控制器 | interrupt-parent = <&gic> |
定位中断控制器,若为级联控制器需追踪至根控制器 | |
Pin 控制配置 | pinctrl-0 = <&i2c2m0_xfer> |
查找 Pin 控制节点,及对应的 Pin 控制器节点 | |
DMA 控制器 | dmas = <&dmac 0x10> |
直通 DMA 控制器节点,确保 DMA 通道可分配 | |
电源域 | power-domains = <&power PD_BUS> |
处理电源域控制器,确保设备供电可管控 | |
间接依赖 | 时钟父节点 | clocks = <&pmucru CLK_PMU_24M> |
从目标时钟节点递归追踪至根时钟(如 24M 晶振) |
中断级联 | interrupt-parent = <&intc> |
若中断控制器有父控制器,需一并处理 | |
寄存器父总线 | ranges = <0x0 0xfe000000> |
确认父总线状态为 “okay”,无需额外直通 | |
总线拓扑 | 直接父节点状态 | bus@ff800000 { status = "okay" } |
确保父总线处于使能状态,不影响设备访问 |
兄弟节点资源冲突 | 同总线节点的reg 属性 |
检查地址是否重叠,避免资源抢占 | |
特殊依赖 | 复位控制器 | resets = <&cru SRST_I2C2> |
处理复位控制器,支持设备复位功能 |
IOMMU | iommus = <&vpu_iommu> |
直通 IOMMU 节点,确保内存访问隔离 | |
PHY 控制器 | phys = <&usb2phy 0> |
处理 PHY 节点,支持 USB/PCIe 等高速接口 | |
保留内存 | memory-region = <&reserved_mem> |
映射保留内存区域,供设备专用 |
特殊说明:复杂设备(如 PCIe/USB3)
对 PCIe、USB3 等复杂设备,需额外处理独有的属性(如interrupt-map
、msi-parent
、ranges
),此类依赖需结合芯片手册识别,由开发人员手动添加至直通配置,示例如下:
pcie@40000000 {
compatible = "pci-host-ecam-generic";
device_type = "pci";
#address-cells = <0x03>;
#size-cells = <0x02>;
#interrupt-cells = <0x01>;
reg = <0x00 0x40000000 0x00 0x10000000>;
msi-parent = <0x0f>; //MSI中断父节点,需处理
bus-range = <0x00 0xff>;
interrupt-map-mask = <0x00 0x00 0x00 0x07>;
interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 ...>; //中断映射表,需同步配置
ranges = <0x1000000 0x00 0x00 0x00 0x50000000...>; //地址范围,需确保客户机可访问
iommu-map = <0x00 0x10 0x00 0x10000>;
status = "okay";
};
- 对复杂设备检查项目较多,复杂的和独有的需由开发人员识别加入到配置文件的直通设备中,如
interrupt-map
等
七、axvisor配置文件的使用
1. 设计思路
(1)在 axvisor 实际应用场景中,设备数量庞大,如phytium e2000开发板中设备节点有100个以上。若将所有设备均直通至客户机并逐一在passthrough_devices
中配置,不仅工程量巨大,还极易出现配置错误;而若仅选择性直通部分设备,又可能因遗漏相关依赖设备,导致客户机运行过程中发生故障。因此,需优先设计一套可精准查找直通设备依赖项的方案,解决设备直通的完整性与准确性问题。
(2)考虑到 axvisor 实际使用时,通常仅有少数设备无需直通。为提升配置效率,需设计一种更便捷、高效的操作方法,快速完成 “排除少数非直通设备、确认多数直通设备” 的配置流程,减少人工操作成本。
(3)主机设备树具备完整的硬件拓扑与配置信息,若能基于主机设备树进行针对性修改,再将其提供给客户机使用,可直接复用现有硬件信息框架,降低客户机设备配置的复杂度,该方案具备较高便利性,可予以保留并整合至整体设计中。
2. 配置文件:
[base]
id = 1
phys_cpu_ids = [0x00, 0x100, 0x200, 0x300]
...
[kernel]
entry_point = 0x4000_0000
kernel_load_addr = 0x4000_0000
dtb_load_addr = 0x4008_0000
dtb_path = "linux-qemu_gicv3.dtb"
...
[memory]
regions = [
[0x4000_0000, 0x4000_0000, 0x7, 1]
]
[devices]
passthrough_devices = [
["intc@8000000", 0x800_0000, 0x800_0000, 0x50_000, 0x1], #[name, base_gpa, base_hpa, length, irq_id]
["pl011@9000000", 0x900_0000, 0x900_0000, 0x1000, 0x1],
["pl031@9010000", 0x901_0000, 0x901_0000, 0x1000, 0x1],
]
emu_devices = [ ]
修改后配置文件:
[devices]
passthrough_devices = [
["all"], #[全部设备]
["pl011@9000000"], #[name]
["pl031@9010000"],
]
# 新增:明确排除、不直通给客户机的设备列表
excluded_devices = [
"interrupt-controller@30800000", # 中断器,由 Hypervisor 模拟
"reserved-memory", # 预留内存区域
]
emu_devices = [ ]
3. 设备树生成流程:
(1)初始判断:dtb_path
是否为空
分支 | 触发条件 | 后续动作 |
---|---|---|
是(dtb_path 为空) |
未指定客户机设备树路径,需动态生成 | 进入 “设备直通规则解析” 分支,根据passthrough_devices 和excluded_devices 筛选直通设备 |
否(dtb_path 非空) |
已指定客户机设备树路径,直接使用 | 直接基于指定设备树和配置文件补全参数 |
(2)设备直通规则解析(dtb_path
为空时)
(a)根据passthrough_devices
筛选设备
- 当
passthrough_devices = [["all"], ]
:表示除excluded_devices
外,主机所有设备均直通给客户机; - 当
passthrough_devices
为具体设备列表:仅筛选列表内设备及关联依赖设备(如 UART 设备需关联中断控制器)。
(b)排除excluded_devices
中设备
excluded_devices
定义 “不希望直通给客户机的设备”,例如:需关联中断控制器才能直通 UART,但又不想将该中断控制器本身直通时,可将中断控制器加入excluded_devices
。
(c)记录最终直通设备
汇总筛选后需直通的设备,用于补全配置文件参数和后续生成客户机设备树。
(3)客户机设备树生成与补全
(a)dtb_path
为空时
- 读取主机设备树文件,补全客户机配置文件中
passthrough_devices
对应的设备参数(如寄存器地址、中断号等); - 生成客户机设备树文件。
(b)dtb_path
非空时
直接使用指定的客户机设备树文件,但仍需根据客户机配置文件补全passthrough_devices
相关参数(保证配置与设备树的一致性)。
(4)关键节点补全
无论设备树是动态生成还是指定的,都需根据客户机配置中的cpu
和memory regions
参数,补全设备树中对应的cpu
节点(如核心数)和内存区域节点memory。
(5)进入原流程
完成设备树生成与补全后,继续执行虚拟化系统的原有启动 / 配置流程(如客户机启动、设备初始化)。
4. 核心配置项说明
配置项 | 取值与作用 | 示例 |
---|---|---|
dtb_path |
- 为空:动态生成客户机设备树 - 非空:使用指定的客户机设备树 | 空:dtb_path = "" 指定:dtb_path = "/path/to/guest.dtb" |
excluded_devices |
声明不直通给客户机的设备(支持设备路径或节点名) | excluded_devices = ["interrupt-controller@30800000", "clk_unwanted"] |
passthrough_devices |
- [["all"], ] :直通除excluded_devices 外的所有主机设备 - 具体列表:仅直通列表内设备及关联依赖 |
全直通:passthrough_devices = [["all"], ] 指定设备:passthrough_devices = ["uart@2800c000", "eth@30000000"] |
5. 优先级规则
客户机配置文件中关于cpu
、memory
的参数优先级最高,会覆盖设备树中的默认值;其次是配置文件中客户机设备树文件;最后是配置文件中[devices]参数。
6. 生成设备树流程图

Metadata
Metadata
Assignees
Labels
Type
Projects
Status