Skip to content

精简客户机配置文件 #227

@bullhh

Description

@bullhh

设备树节点直通客户机设计

一、直通核心原则

设备树节点直通无需传递完整设备树,仅需精准识别并处理目标设备的核心节点依赖资源,确保客户机可正常驱动设备。若遗漏关键依赖(如中断、时钟),会导致设备初始化失败或功能异常。

术语解释

术语 解释
设备树 (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>0x0cclock节点的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@30800000interrupt-parentclocks等属性,无额外依赖;

  • 时钟源依赖检查clk100mhzfixed-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-mapmsi-parentranges),此类依赖需结合芯片手册识别,由开发人员手动添加至直通配置,示例如下:

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_devicesexcluded_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)关键节点补全

无论设备树是动态生成还是指定的,都需根据客户机配置中的cpumemory 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. 优先级规则

客户机配置文件中关于cpumemory的参数优先级最高,会覆盖设备树中的默认值;其次是配置文件中客户机设备树文件;最后是配置文件中[devices]参数。

6. 生成设备树流程图

Image

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

Status

处理中

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions