四, uboot 中如何获取 dtb
1, 整体说明
在 uboot 初始化过程中, 需要对 dtb 做两个操作:
获取 dtb 的地址, 并且验证 dtb 的合法性
因为我们使用的 dtb 并没有集成到 uboot 的 bin 文件中, 也就是使用的 CONFIG_OF_SEPARATE 方式. 因此, 在 relocate uboot 的过程中并不会去 relocate dtb. 因此, 这里我们还需要自行为 dtb 预留内存空间并进行 relocate. 关于 uboot relocate 的内容请参考《[uboot] (番外篇)uboot relocation 介绍》.
relocate 之后, 还需要重新获取一次 dtb 的地址.
这部分过程是在 init_board_f 中实现, 参考《[uboot] (第五章)uboot 流程 --uboot 启动流程》.
对应代码 common/board_f.c
- static init_fnc_t init_sequence_f[] = {
- ...
- #ifdef CONFIG_OF_CONTROL
- fdtdec_setup, // 获取 dtb 的地址, 并且验证 dtb 的合法性
- #endif
- ...
- reserve_fdt, // 为 dtb 分配新的内存地址空间
- ...
- reloc_fdt, // relocate dtb
- ...
- }
后面进行具体函数的分析.
2, 获取 dtb 的地址, 并且验证 dtb 的合法性 (fdtdec_setup)
对应代码如下:
- lib/fdtdec.c
- int fdtdec_setup(void)
- {
- #if CONFIG_IS_ENABLED(OF_CONTROL) // 确保 CONFIG_OF_CONTROL 宏是打开的
- # ifdef CONFIG_OF_EMBED
- /* Get a pointer to the FDT */
- gd->fdt_blob = __dtb_dt_begin;
- // 当使用 CONFIG_OF_EMBED 的方式时, 也就是 dtb 集成到 uboot 的 bin 文件中时, 通过__dtb_dt_begin 符号来获取 dtb 地址.
- # elif defined CONFIG_OF_SEPARATE
- /* FDT is at end of image */
- gd->fdt_blob = (ulong *)&_end;
- // 当使用 CONFIG_OF_SEPARATE 的方式时, 也就是 dtb 追加到 uboot 的 bin 文件后面时, 通过_end 符号来获取 dtb 地址.
- # endif
- /* Allow the early environment to override the fdt address */
- gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
- (uintptr_t)gd->fdt_blob);
- // 可以通过环境变量 fdtcontroladdr 来指定 gd->fdt_blob, 也就是指定 fdt 的地址.
- #endif
- // 最终都把 dtb 的地址存储在 gd->fdt_blob 中
- return fdtdec_prepare_fdt();
- // 在 fdtdec_prepare_fdt 中检查 fdt 的合法性
- }
- /* fdtdec_prepare_fdt 实现如下 */
- int fdtdec_prepare_fdt(void)
- {
- if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
- fdt_check_header(gd->fdt_blob)) {
- puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
- return -1;
- // 判断 dtb 是否存在, 以及是否有四个字节对齐.
- // 然后再调用 fdt_check_header 看看头部是否正常. fdt_check_header 主要是检查 dtb 的 magic 是否正确.
- }
- return 0;
- }
验证 dtb 的部分可以参考《[kernel 启动流程] (第四章) 第一阶段之 --dtb 的验证》.
3, 为 dtb 分配新的内存地址空间 (reserve_fdt)
relocate 的内容请参考《[uboot] (番外篇)uboot relocation 介绍》.
common/board_f.c 中
- static int reserve_fdt(void)
- {
- #ifndef CONFIG_OF_EMBED
- // 当使用 CONFIG_OF_EMBED 方式时, 也就是 dtb 集成在 uboot 中的时候, relocate uboot 过程中也会把 dtb 一起 relocate, 所以这里就不需要处理.
- // 当使用 CONFIG_OF_SEPARATE 方式时, 就需要在这里地方进行 relocate
- if (gd->fdt_blob) {
- gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
- // 获取 dtb 的 size
- gd->start_addr_sp -= gd->fdt_size;
- gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
- // 为 dtb 分配新的内存空间
- debug("Reserving %lu Bytes for FDT at: %08lx\n",
- gd->fdt_size, gd->start_addr_sp);
- }
- #endif
- return 0;
- }
- 4,relocate dtb(reloc_fdt)
relocate 的内容请参考《[uboot] (番外篇)uboot relocation 介绍》.
common/board_f.c 中
- static int reloc_fdt(void)
- {
- #ifndef CONFIG_OF_EMBED
- // 当使用 CONFIG_OF_EMBED 方式时, 也就是 dtb 集成在 uboot 中的时候, relocate uboot 过程中也会把 dtb 一起 relocate, 所以这里就不需要处理.
- // 当使用 CONFIG_OF_SEPARATE 方式时, 就需要在这里地方进行 relocate
- if (gd->flags & GD_FLG_SKIP_RELOC)
- // 检查 GD_FLG_SKIP_RELOC 标识
- return 0;
- if (gd->new_fdt) {
- memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);
- // relocate dtb 空间
- gd->fdt_blob = gd->new_fdt;
- // 切换 gd->fdt_blob 到 dtb 的新的地址空间上
- }
- #endif
- return 0;
- }
五, uboot 中 dtb 解析的常用接口
gd->fdt_blob 已经设置成了 dtb 的地址了.
注意, fdt 提供的接口都是以 gd->fdt_blob(dtb 的地址) 为参数的.
1, 接口功能
以下只简单说明几个接口的功能, 没有深究到实现原理. 先说明几个, 后续继续补充.
另外, 用节点在 dtb 中的偏移地址来表示一个节点. 也就是节点变量 node 中, 存放的是节点的偏移地址
lib/fdtdec.c 中
- fdt_path_offset
- int fdt_path_offset(const void *fdt, const char *path)
- eg:node = fdt_path_offset(gd->fdt_blob, "/aliases");
功能: 获得 dtb 下某个节点的路径 path 的偏移. 这个偏移就代表了这个节点.
- fdt_getprop
- const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp)
- eg: Mac = fdt_getprop(gd->fdt_blob, node, "mac-address", &len);
功能: 获得节点 node 的某个字符串属性值.
- fdtdec_get_int_array,fdtdec_get_byte_array
- int fdtdec_get_int_array(const void *blob, int node, const char *prop_name, u32 *array, int count)
- eg: ret = fdtdec_get_int_array(blob, node, "interrupts", cell, ARRAY_SIZE(cell));
功能: 获得节点 node 的某个整形数组属性值.
- fdtdec_get_addr
- fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name)
- eg:fdtdec_get_addr(blob, node, "reg");
功能: 获得节点 node 的地址属性值.
fdtdec_get_config_int,fdtdec_get_config_bool,fdtdec_get_config_string
功能: 获得 config 节点下的整形属性, bool 属性, 字符串等等.
- fdtdec_get_chosen_node
- int fdtdec_get_chosen_node(const void *blob, const char *name)
功能: 获得 chosen 下的 name 节点的偏移
- fdtdec_get_chosen_prop
- const char *fdtdec_get_chosen_prop(const void *blob, const char *name)
功能: 获得 chosen 下 name 属性的值
lib/fdtdec_common.c 中
- fdtdec_get_int
- int fdtdec_get_int(const void *blob, int node, const char *prop_name, int default_val)
- eg: bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us", DEFAULT_UDELAY);
功能: 获得节点 node 的某个整形属性值.
fdtdec_get_uint
功能: 获得节点 node 的某个无符号整形属性值.
----------------
来源: http://www.bubuko.com/infodetail-3325814.html