ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK SDK U-Boot Kernel DTB 机制分析

RK SDK U-Boot Kernel DTB 机制分析 RK SDK U-Boot Kernel DTB 机制分析1. Kernel DTB 机制概述U-Boot proper 启动后提前加载一份由 Linux Kernel DTS 编译出来的 Kernel DTB使用其中的板级硬件信息初始化 U-Boot 设备最后再把处理后的 Kernel DTB 传递给 Linux。Rockchip 官方开发指南将两份设备树的基础职责划分为U-Boot DTB 负责存储设备和打印串口等早期设备Kernel DTB 负责其余板级外设。SDK 在这一基础上实现了 Kernel DTB V1 和 V2 两种机制具体版本由 U-Boot 配置决定。设备树来源主要用途U-Boot DTBCONFIG_DEFAULT_DEVICE_TREE指定的 U-Boot DTS保证 U-Boot 能够完成串口、时钟、启动介质等早期初始化Kernel DTBKernel 源码中的板级 DTS为 U-Boot 提供完整板级配置并最终传递给 Linux整体流程如下U-Boot 自带 DTB │ ├─ 完成早期串口、时钟和启动介质初始化 └─ 建立第一批 Driver Model 设备 │ ▼ 从启动介质提前加载 Kernel DTB │ ├─ 校验 DTB ├─ 执行 early fixup 和 DT overlay ├─ 建立第二棵 live tree ├─ 将 Kernel DTB 中的设备绑定到 Driver Model │ ├─ V1保留必要的 U-Boot 设备其余设备转向 Kernel DTB ├─ V2两套设备共存并按 uclass 调整优先级 │ └─ 将处理后的 Kernel DTB 传递给 LinuxV1 和 V2 的 Kernel DTB 来源、加载时机及镜像读取过程基本相同主要区别在于 Kernel DTB 扫描后如何处理已经由 U-Boot DTB 创建的设备。2. Kernel DTB 相关配置2.1 U-Boot DTB 配置U-Boot 自带设备树通常由以下配置决定CONFIG_DEFAULT_DEVICE_TREEu-boot-board CONFIG_OF_SEPARATEy CONFIG_OF_LIVEyCONFIG_DEFAULT_DEVICE_TREE指定 U-Boot 自带设备树为u-boot/arch/arm/dts/u-boot-board.dtsCONFIG_OF_SEPARATE表示 U-Boot DTB 作为独立数据放在 U-Boot proper 映像末尾。CONFIG_OF_LIVE表示 U-Boot 会把扁平设备树转换成 live tree供 Driver Model 和设备树接口使用。2.1.1 扁平设备树 FDTFDT 是 Flattened Device Tree 的缩写中文通常称为扁平设备树。DTS 经过 DTC 编译后生成的 DTB就是 FDT 的二进制文件形式board.dts │ │ DTC 编译 ▼ board.dtb │ └─ 加载到一段连续内存成为 FDT blobFDT 把整棵设备树紧凑地保存在一块连续内存中内部主要包含FDT blob ├─ Header │ ├─ magic │ ├─ totalsize │ ├─ 结构块偏移 │ └─ 字符串块偏移 ├─ Memory Reservation Block ├─ Structure Block │ ├─ 节点开始标记 │ ├─ 属性 │ ├─ 节点结束标记 │ └─ 整棵树结束标记 └─ Strings Block └─ compatible、reg、clocks、status 等属性名FDT 中的节点通常使用相对于 DTB 起始地址的 offset 表示而不是使用节点结构体指针。U-Boot 主要通过libfdt提供的fdt_*()接口访问fdt_check_header(fdt);fdt_path_offset(fdt,/chosen);fdt_getprop(fdt,node,bootargs,NULL);fdt_setprop_string(fdt,node,bootargs,bootargs);FDT 的特点是体积小、内存连续适合存放在boot.img、resource.img或独立分区中也适合在 U-Boot 启动 Linux 时直接传递给内核。但由于节点和属性紧密排列增加节点、扩大属性时可能需要扩展 DTB 空间并移动后续数据。2.1.2 live treelive tree 是 U-Boot 将 FDT 解析并展开后形成的动态内存树。每个节点和属性都对应独立的 C 数据结构再通过指针连接成父子、兄弟和属性链表关系struct device_node ├─ parent ────── 指向父节点 ├─ child ─────── 指向第一个子节点 ├─ sibling ───── 指向兄弟节点 └─ properties ── 指向属性链表 ├─ struct property ├─ struct property └─ struct propertylive tree 的逻辑结构更接近 DTS 中看到的节点层级/ ├─ cpus │ ├─ cpu0 │ └─ cpu1 ├─ serial... ├─ mmc... └─ i2c... ├─ pmic... └─ codec...U-Boot 启用CONFIG_OF_LIVE后可以通过 Linux 风格的of_*()接口访问 live treestructdevice_node*np;u32 value;npof_find_node_by_path(/serial...);of_property_read_u32(np,clock-frequency,value);live tree 使用节点指针完成查找和遍历动态增加节点、修改属性更加方便但会占用更多内存。其内部包含只在当前 U-Boot 地址空间有效的指针因此不能直接保存到镜像也不能把struct device_node指针直接传给 Linux。2.1.3 FDT 与 live tree 的关系U-Boot 使用of_live_build()将 FDT 展开为 live treeof_live_build(gd-fdt_blob,gd-of_root);对应关系如下FDT/DTB gd-fdt_blob │ │ of_live_build() ▼ live tree gd-of_root建立 live tree 不会自动删除原来的 FDT所以两种形式可以同时存在gd-fdt_blob ── 指向连续内存中的扁平 DTB gd-of_root ── 指向展开后的 live tree 根节点二者的主要区别如下对比项FDT扁平设备树live tree内存形式一块连续的二进制数据节点、属性和指针组成的动态树根对象DTB 起始地址struct device_node *节点表示相对于 DTB 起始地址的 offset节点结构体指针常用接口fdt_*()、libfdtof_*()、ofnode_*()内存占用较小较大动态修改扩容或移动数据相对麻烦查找、增加和修改节点更方便保存到镜像可以不能直接保存传递给 Linux可以直接传递 DTB 地址不能直接传递 U-Boot 内部指针2.1.4 live tree 与 Driver Model 的关系live tree 是设备树在内存中的一种表示形式Driver Model 则是根据设备树节点匹配驱动后创建的设备对象两者不能混为一谈FDT │ │ of_live_build() ▼ live tree │ │ dm_scan_fdt() ▼ Driver Model 设备设备树节点通常对应struct device_node而成功绑定到驱动后的设备对应struct udevice。设备树中存在节点并不代表 Driver Model 中一定存在相应设备还需要 U-Boot 中存在匹配的驱动且父总线、时钟、复位和电源等依赖能够正确绑定。在 Kernel DTB V2 中两份 FDT 和两棵 live tree 的对应关系为U-Boot FDT Kernel FDT gd-ufdt_blob gd-fdt_blob │ │ ▼ ▼ U-Boot live tree Kernel live tree gd-of_root_f gd-of_root │ │ └──────────┬───────────────┘ ▼ 同一套 Driver Model 中的设备最终启动 Linux 时传递的是经过 U-Boot 修正的扁平 Kernel DTB而不是 U-Boot 内存中的 live tree。2.2 Kernel DTB 配置Kernel DTB 机制的核心配置为CONFIG_USING_KERNEL_DTBy # CONFIG_USING_KERNEL_DTB_V2 is not set # V1 # 或者 CONFIG_USING_KERNEL_DTBy CONFIG_USING_KERNEL_DTB_V2y # V2各配置项的作用如下配置项作用CONFIG_USING_KERNEL_DTB允许 U-Boot 提前读取并使用 Kernel DTBCONFIG_USING_KERNEL_DTB_V2在 Kernel DTB 基础功能上选择 V2 设备共存机制CONFIG_ROCKCHIP_RESOURCE_IMAGE支持从 Rockchip resource 镜像读取 DTB、Logo 等资源CONFIG_ROCKCHIP_DTB_VERIFY对 resource 中的 DTB 执行 hash 完整性检查CONFIG_ROCKCHIP_FIT_IMAGE支持 Rockchip FIT 格式的boot.img后三项是否启用取决于芯片和产品使用的镜像格式不是 Kernel DTB 机制本身的强制条件。2.3 V1 与 V2 的配置选择配置状态实际机制CONFIG_USING_KERNEL_DTBnU-Boot 只使用自己的 DTB不提前切换到 Kernel DTBCONFIG_USING_KERNEL_DTByCONFIG_USING_KERNEL_DTB_V2n使用 Kernel DTB V1CONFIG_USING_KERNEL_DTByCONFIG_USING_KERNEL_DTB_V2y使用 Kernel DTB V2CONFIG_USING_KERNEL_DTB_V2依赖CONFIG_USING_KERNEL_DTB。V1 是兼容旧平台的机制V2 主要用于需要完整保留两套 Driver Model 设备的新平台。具体使用哪个版本应以目标板最终生成的.config为准。2.4 产品使用的 Kernel DTSRockchip SDK 的板级配置通常使用RK_KERNEL_DTS_NAME指定产品对应的 Kernel DTSRK_KERNEL_DTS_NAMEkernel-board对应源码为kernel/arch/arm64/boot/dts/rockchip/ └── kernel-board.dts编译产物为kernel/arch/arm64/boot/dts/rockchip/ └── kernel-board.dtb32 位平台的实际目录可能是kernel/arch/arm/boot/dts/。不同 SDK 版本的变量和目录略有差异但最终目标都是将产品 DTS 编译为 Kernel DTB。3. Kernel DTB 加载前的 U-Boot 设备树3.1 定位 U-Boot 自带 DTBU-Boot proper 刚开始运行时Kernel DTB 尚未从存储设备读取。由于启用了CONFIG_OF_SEPARATEfdtdec_setup()从 U-Boot 映像末尾取得自带 DTBgd-fdt_blob(ulong*)_end;此时gd-fdt_blob │ └─ U-Boot 自带的 u-boot-board.dtbU-Boot 依靠这份 DTB 完成串口初始化。时钟和复位控制器初始化。pinctrl 和 syscon 初始化。eMMC、SD、UFS 等启动介质初始化。块设备和分区访问。早期 Driver Model 建立。3.2 U-Boot DTB 的裁剪启用 Kernel DTB 机制后U-Boot 构建系统会使用fdtgrep对 U-Boot DTB 进行裁剪主要保留带有以下属性的早期节点u-boot,dm-pre-reloc u-boot,dm-spl因此U-Boot 自带 DTB 不需要完整描述板上所有外设。它的主要任务是保证 U-Boot 在 Kernel DTB 尚未读取时仍然能够访问串口和启动介质。按照 Rockchip 官方开发指南的建议普通板级外设调整通常应修改 Kernel DTS只有更换打印串口、启动存储或其他早期关键硬件时才需要同步检查和修改 U-Boot DTS。3.3 建立第一棵 live tree进入board_init_r()后先执行initr_of_live() initr_dm()其中initr_of_live()将 U-Boot DTB 转换为 live tree。initr_dm()根据 U-Boot live tree 绑定第一批 Driver Model 设备。此时系统中只有 U-Boot DTB 创建的设备Kernel DTB 尚未加入。4. Kernel DTB 的加载时机4.1board_init_r()中的调用顺序与 Kernel DTB 相关的初始化顺序如下board_init_r() │ ├─ initr_of_live() │ └─ 建立 U-Boot live tree │ ├─ initr_dm() │ └─ 绑定 U-Boot DTB 设备 │ ├─ initr_env_nowhere() │ └─ 建立最小临时环境变量 │ ├─ board_init() │ └─ init_kernel_dtb() │ └─ initr_env_switch() └─ 切换到存储介质中的正式环境变量Kernel DTB 在进入main_loop()、执行bootcmd之前就已经读取而不是等到启动 Linux 时才读取。4.2 为什么先建立临时环境变量从存储介质读取 Kernel DTB 需要知道启动设备和加载地址例如devtype devnum rkimg_bootdev fdt_addr_r但是正式环境变量通常保存在 eMMC 等存储设备中读取正式环境变量之前又需要初始化启动设备。因此 SDK 先通过initr_env_nowhere()建立一组最小临时环境变量。这组最小环境通常包含scriptaddr # 启动脚本如 boot.scr的内存加载地址 pxefile_addr_r # PXE 启动配置文件的内存加载地址 fdt_addr_r # Kernel DTB 的内存加载地址 kernel_addr_r # 内核镜像的内存加载地址使用 kernel_addr_c 时通常为解压目标地址 kernel_addr_c # 压缩内核镜像的内存加载地址即解压前的源缓冲区 ramdisk_addr_r # 初始内存盘镜像initrd/initramfs的内存加载地址 devtype # 启动存储设备类型如 mmc、mtd devnum # 对应设备类型下的设备编号 rkimg_bootdev # 探测并选择启动设备、设置 devtype/devnum 的命令脚本各变量的实际地址和值由平台头文件、环境配置及启动设备探测结果决定不能跨芯片直接套用。Kernel DTB 的正常加载地址由fdt_addr_r指定。4.3board_init()中的位置Rockchip 平台的board_init()典型调用顺序如下不同芯片可能根据所启用的功能增减步骤board_init() │ ├─ board_debug_init() ├─ optee_client_init() ├─ init_kernel_dtb() ├─ early_download() ├─ clks_probe() ├─ regulators_enable_boot_on() ├─ io_domain_init() ├─ set_armclk_rate() ├─ dvfs_init() └─ rk_board_init()init_kernel_dtb()位于调压器、IO 电压域和 DVFS 等板级设备初始化之前。这些设备需要读取板级 DTS 中的电源、时钟和工作点配置因此必须先准备好 Kernel DTB。5.init_kernel_dtb()详细流程5.1 确定加载地址init_kernel_dtb()首先读取 DTB 加载地址if(gd-ram_sizeSZ_128M)fdt_addrenv_get_ulong(fdt_addr1_r,16,0);if(!fdt_addr)fdt_addrenv_get_ulong(fdt_addr_r,16,0);内存不超过 128 MiB 的平台优先尝试fdt_addr1_r其他平台通常使用fdt_addr_r。具体地址由各平台的ENV_MEM_LAYOUT_SETTINGS等配置定义。5.2 查找 Kernel DTB读取入口为rockchip_read_dtb_file((void*)fdt_addr);rockchip_read_dtb_file()的逻辑查找顺序为1. Distro DTB 2. Resource DTB 3. FIT FDT实际可用路径由配置决定。常见的 Resource FIT 路径为启动设备 └─ boot 分区 └─ boot.imgFIT └─ /images/resource └─ resource.img └─ rk-kernel.dtb └─ 复制到 fdt_addr_rrockchip_read_dtb_file()外层查找顺序如下顺序来源生效条件1可启动文件系统中的 Distro DTB启用CONFIG_ROCKCHIP_EARLY_DISTRO_DTB2Rockchip Resource DTB启用CONFIG_ROCKCHIP_RESOURCE_IMAGE3FIT 的 FDT 子镜像启用CONFIG_ROCKCHIP_FIT_IMAGE且未启用 Resource Image进入 Resource 路径后内部来源优先级为1. FIT boot.img 中的 resource 子镜像 2. Android boot/recovery 镜像中的 resource 数据 3. 独立 resource 分区如果上述外部来源均失败并启用了CONFIG_EMBED_KERNEL_DTBinit_kernel_dtb()还可以使用追加在 U-Boot 映像中的备用 Kernel DTB。启用CONFIG_EMBED_KERNEL_DTB_ALWAYS时则始终使用该嵌入 DTB。5.3 检查 DTBKernel DTB 读取完成后会执行检查 FDT 头和 magic。使用 Resource Image 时根据配置检查 resource 条目中的 DTB hash。为 DTB 所在地址预留sysmem区域。启用CONFIG_ROCKCHIP_DTB_VERIFY后Resource 工具会为 DTB 条目保存 SHA-1 或 SHA-256 hashU-Boot 读取后重新计算并比较。这里的 hash 用于发现数据损坏属于完整性检查。只有启用 FIT 签名、安全启动或其他可信验证链时才能进一步提供镜像来源认证不能只看到 hash 检查成功就认定已经完成数字签名验证。5.4 执行板级修正与 DT OverlayDTB 检查通过后继续执行rk_board_early_fdt_fixup(fdt);android_fdt_overlay_apply(fdt);其中rk_board_early_fdt_fixup()在建立 Kernel live tree 前修改板级 DTB。android_fdt_overlay_apply()在存在有效 DTBO 且满足条件时应用设备树覆盖。因此后续 U-Boot 使用的不是存储介质中完全原始的 DTB而是已经执行过 early fixup 和可选 overlay 的 DTB。6. Kernel DTB V1 机制6.1 V1 的总体思路Kernel DTB V1 的目标是保留 U-Boot 启动所必需的少量设备将其余设备逐步切换为由 Kernel DTB 创建的设备。V1 可以概括为U-Boot DTB 创建早期设备 │ ├─ 存储、Crypto、Watchdog 等必要设备继续保留或复用 │ └─ 其他同名早期设备退出 uclass 有效列表 │ ▼ Kernel DTB 重新绑定设备因此V1 完成后并不是两套完整设备同时存在。Driver Model 主要使用 Kernel DTB 创建的设备只留下少量必须继续工作的 U-Boot 设备。6.2 保存原来的 U-Boot DTB进入init_kernel_dtb()时V1 先保存原来的 U-Boot DTB 地址#ifndefCONFIG_USING_KERNEL_DTB_V2void*ufdt_blob(void*)gd-fdt_blob;#endifKernel DTB 加载完成后gd-fdt_blob改为指向 Kernel DTB。此前由 U-Boot DTB 建立的 live node 和设备不会立即全部销毁而是在后续绑定过程中按设备类别处理。6.3 启动设备的复用策略V1 对以下设备类别执行特殊处理UCLASS_MMC UCLASS_RKNAND UCLASS_SPI_FLASH UCLASS_MTD UCLASS_PCI UCLASS_AHCI处理规则为Kernel DTB 扫描到 MMC 节点时不再绑定新的 MMC 设备继续使用 U-Boot DTB 已经初始化的 MMC。对 RKNAND、SPI Flash、MTD、PCI 和 AHCI如果发现同名 U-Boot 设备则不创建第二个设备而是把原设备的dev-node更新为 Kernel DTB 中的新节点。如果没有找到可复用的同名设备才按正常流程绑定 Kernel DTB 设备。这一策略避免正在使用的启动存储设备被重新创建也尽量让保留下来的设备能够读取 Kernel DTB 中的板级属性。6.4 普通设备的替换策略对于上述存储类之外的设备V1 在绑定 Kernel DTB 节点时会查找同一 uclass 中名字相同、并带有以下早期属性的 U-Boot 设备u-boot,dm-pre-reloc u-boot,dm-spl如果找到同名设备Crypto 和 Watchdog 继续使用 U-Boot DTB 设备不绑定对应的 Kernel DTB 设备。其他设备从原来的 uclass 有效设备列表中移出再绑定 Kernel DTB 设备。所以 V1 的主要结果是U-Boot DTB 设备 ├─ MMC保留原设备和原节点 ├─ NAND/MTD/SPI Flash/PCI/AHCI尽量复用原设备节点改为 Kernel 节点 ├─ Crypto/Watchdog保留原设备 └─ 其他同名早期设备让位给 Kernel DTB 设备6.5 V1 的 phandle 修正V1 会保留或复用部分由 U-Boot DTB 创建的设备但gd-fdt_blob和主要设备树上下文已经切换成 Kernel DTB。两份 DTB 中同一控制器的 phandle 数值不一定相同因此旧设备节点中的引用可能无法在 Kernel DTB 中正确解析。V1 为此执行两类修正。6.5.1 CRU phandle 修正phandles_fixup_cru()在 Kernel DTB 中查找compatible以-cru结尾的时钟复位控制器并取得其 phandle 和#clock-cells然后检查保留下来的 U-Boot 设备节点中的clocks assigned-clocks resets将其中原本引用 U-Boot CRU 的 phandle 改成 Kernel DTB 中 CRU 的 phandle。6.5.2 GPIO phandle 修正phandles_fixup_gpio()读取 Kernel DTB/pinctrl下的 GPIO Bank 节点再根据节点名称把 U-Boot 按键设备gpios属性中的旧 phandle 改成 Kernel DTB 中对应 GPIO Bank 的 phandle。6.6 V1 的特点与限制V1 的优点是保留设备数量较少适合早期平台的设备模型。但它也有明显限制依赖设备名称判断是否为同一设备。需要通过专门的修正代码处理跨设备树的 phandle 引用现有规则不能通用覆盖所有设备依赖。复用设备时可能出现“设备对象来自 U-Boot、节点来自 Kernel DTB”的混合状态。新增需要保留的设备类别时可能需要继续扩展特殊处理代码。两份 DTS 的节点名称、alias 或依赖关系变化后更容易出现兼容问题。V2 的主要改进方向就是尽量保留两套完整设备及各自的节点关系减少这种跨设备树替换和 phandle 修正。7. Kernel DTB V2 双树机制7.1 切换当前 FDT 指针Kernel DTB 准备完成后执行gd-fdt_blob(void*)fdt_addr;hotkey_run(HK_FDT);gd-flags|GD_FLG_KDTB_READY;此后普通的gd-fdt_blob指向 Kernel DTB不再指向 U-Boot DTB。U-Boot DTB 并没有被销毁。U-Boot 重定位 DTB 时已将它保存在gd-ufdt_blob该指针仍可用于 FIT 验证密钥等必须由可信 U-Boot DTB 提供的数据避免从外部 Kernel DTB 获取验证密钥。7.2 保存 U-Boot live treeV2 机制在建立 Kernel live tree 前保存原来的 U-Boot live treegd-of_root_fgd-of_root;of_live_build(gd-fdt_blob,gd-of_root);执行后Flat DTB ├── gd-ufdt_blob │ └─ U-Boot 自带 DTB └── gd-fdt_blob └─ Kernel DTB Live tree ├── gd-of_root_f │ └─ U-Boot live tree └── gd-of_root └─ Kernel live treeof_find_node_by_phandle()默认在 Kernel live tree 中查找。如果没有找到V2 机制还会继续遍历gd-of_root_f也就是 U-Boot live tree。7.3 扫描 Kernel DTB 设备建立 Kernel live tree 后执行dm_scan_fdt((void*)gd-fdt_blob,false);此时GD_FLG_KDTB_READY已经设置因此新绑定的设备会被标记为DM_FLAG_KNRL_DTBDriver Model 中于是同时存在DM 设备树 ├── 来自 U-Boot DTB 的设备 └── 来自 Kernel DTB 的设备V2 机制不是根据设备名字简单覆盖而是保留两套设备再通过 uclass 链表顺序和特殊删除规则确定使用哪一个。7.4 V1 与 V2 对比对比项V1V2核心策略保留必要 U-Boot 设备其余设备转向 Kernel DTB两套设备基本完整共存再调整设备顺序MMC拒绝绑定 Kernel MMC使用原 U-Boot MMC两套 MMC 可同时存在U-Boot MMC 排在前面NAND、MTD、SPI Flash、PCI、AHCI尽量复用 U-Boot 设备并把dev-node改为 Kernel 节点保留两套设备U-Boot 设备优先普通同名设备原 U-Boot 设备退出有效 uclass 列表由 Kernel 设备替代Kernel 设备通常排在 U-Boot 设备前面Crypto保留 U-Boot 设备拒绝 Kernel 设备Kernel Crypto 绑定后再删除保留 U-Boot CryptoWatchdog保留 U-Boot 设备拒绝 Kernel 设备两套设备可存在但 U-Boot Watchdog 优先phandle需要修正 CRU 和 GPIO 等跨树引用两棵 live tree 各自保留通常不需要 V1 的 phandle 修正主要风险依赖同名匹配和跨树节点复用设备数量增加需要明确 uclass 顺序和重复设备策略两种版本使用相同的 Kernel DTB 加载入口。CONFIG_USING_KERNEL_DTB_V2主要改变kernel_dtb.c和 Driver Model 在扫描第二棵设备树时的处理策略。8. V2 两套 Driver Model 设备的优先级8.1 U-Boot 设备优先的类别以下 uclass 会把 U-Boot DTB 创建的设备保留在链表前面UCLASS_AHCI UCLASS_BLK UCLASS_MMC UCLASS_MTD UCLASS_PCI UCLASS_RKNAND UCLASS_SPI_FLASH UCLASS_CRYPTO UCLASS_FIRMWARE UCLASS_RNG UCLASS_SYSCON UCLASS_SYSRESET UCLASS_WDT这些设备主要关系到当前启动介质和块设备编号。内核和 DTB 的继续读取。镜像校验和安全功能。系统复位和看门狗。GRF、PMUGRF 等基础寄存器控制。例如MMC 已经由 U-Boot DTB 初始化并用于读取boot.img。如果 Kernel DTB 扫描后直接替换 MMC 设备或改变设备编号后续分区访问可能失效。因此MMC 等启动关键设备继续优先使用 U-Boot DTB 创建的实例。8.2 Kernel DTB 设备优先的类别不在上述列表中的普通设备Kernel DTB 创建的设备通常被插到 U-Boot 设备之前例如PMIC。regulator。display。USB。板级 I2C 和 SPI 外设。其他依赖完整板级配置的设备。调用uclass_get_device_xxx()获取设备时通常会先取得 Kernel DTB 创建的实例。8.3 Crypto 设备处理Kernel DTB 扫描完成后SDK 会删除 Kernel DTB 创建的 Crypto 设备继续使用 U-Boot DTB 中的 Crypto 设备U-Boot Crypto 设备 ── 保留 Kernel Crypto 设备 ── 删除这样可以保证镜像验证继续使用 U-Boot 已经初始化并确认可用的加密设备。8.4 Ethernet 设备处理如果 Kernel DTB 中的网卡节点成功匹配 U-Boot 驱动并绑定成带有DM_FLAG_KNRL_DTB的 ETH 设备SDK 会删除旧的 U-Boot ETH 设备Kernel DTB 成功绑定 ETH │ ▼ 删除旧的 U-Boot ETH 设备仅仅在 Kernel DTB 中存在网卡节点并不足以触发该行为。如果 U-Boot 中没有兼容驱动或者绑定失败就不会生成 Kernel ETH 设备。8.5 MMC 重新处理Kernel DTB 设备扫描完成后还会调用mmc_dm_reinit();其目的不是重新创建 MMC 设备而是允许平台重新处理 MMC 与时钟等设备之间的绑定关系例如让原有 U-Boot MMC 设备重新取得 Kernel DTB 中建立的时钟设备。9. 切换到正式环境变量Kernel DTB 和启动介质相关设备初始化完成后board_init_r()执行initr_env_switch();执行过程为导出早期最小环境 │ ▼ 销毁临时环境 │ ▼ 从存储介质读取持久化环境 │ ▼ 将早期最小环境追加到持久化环境 │ └─ bootargs 使用单独的追加逻辑这不是两个环境变量集合的简单无条件合并。部分同名变量会由早期环境覆盖bootargs则通过env_update()追加处理。10. Kernel DTB 如何传递给 Linux10.1 不能重新使用未经处理的原始 DTBKernel DTB 提前加载后U-Boot 可能已经对它执行early fixup。DT overlay。reserved-memory 处理。板级硬件信息修改。如果执行bootm时重新使用启动镜像中未经处理的原始 DTB前面的修改就会丢失。使用 FIT 镜像时board_fit_image_post_process()检测到GD_FLG_KDTB_READY后会把 FIT FDT 的数据源替换为已经处理过的 Kernel DTB*src_addr(void*)gd-fdt_blob;*src_len(size_t)fdt_totalsize(gd-fdt_blob);通过命令行地址启动 Android、FIT 或 uImage 等内存镜像时平台的bootm_image_populate_dtb()和rockchip_ram_read_dtb_file()负责从相应镜像结构中取得 DTB如果早期 Kernel DTB 已经准备好则继续使用或更新gd-fdt_blob所指向的 DTB。10.2 最终交接流程完整流程为Kernel DTB │ ├─ 提前加载到 fdt_addr_r ├─ 检查 FDT 和 hash ├─ early fixup ├─ DT overlay ├─ 建立 Kernel live tree ├─ 供 U-Boot 初始化板级设备 ├─ bootm 阶段继续执行通用 fixup └─ 作为启动参数传递给 Linux正常情况下U-Boot 后半段使用的 Kernel DTB 与最终传递给 Linux 的 DTB是同一份经过处理的 DTB。11. 总结RK SDK 的 Kernel DTB 机制可以概括为U-Boot 自带 DTB │ ├─ 建立早期 live tree ├─ 初始化串口、时钟和启动介质 └─ 创建关键 U-Boot DM 设备 │ ▼ 从启动介质加载 Kernel DTB │ ├─ 检查 DTB 和 hash ├─ 执行 fixup 与 overlay ├─ 建立 Kernel live tree ├─ 绑定 Kernel DM 设备 │ ├─ V1保留必要设备其余设备切换为 Kernel DTB 设备 ├─ V2两套设备共存按 uclass 调整优先级 │ └─ 将处理后的 DTB 传递给 Linux这一机制的核心是分阶段使用两份设备树U-Boot DTB 提供稳定、可控的早期启动环境。Kernel DTB 提供完整、准确的板级硬件配置。V1 主要通过复用、移除和重新绑定设备完成切换并修正跨设备树 phandle。V2 保留两套 Driver Model 设备并按设备类别决定优先使用哪一套。最终交给 Linux 的仍是经过 U-Boot 修正和处理后的 Kernel DTB。
返回列表