[uboot] (番外篇)uboot之fdt介绍

以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例

[uboot] uboot流程系列: 
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2) 
[project X] tiny210(s5pv210)从存储设备加载代码到DDR 
[uboot] (第一章)uboot流程——概述 
[uboot] (第二章)uboot流程——uboot-spl编译流程 
[uboot] (第三章)uboot流程——uboot-spl代码流程 
[uboot] (第四章)uboot流程——uboot编译流程 
[uboot] (第五章)uboot流程——uboot启动流程 
[uboot] (番外篇)global_data介绍 
[uboot] (番外篇)uboot relocation介绍

建议先看《[[uboot] (番外篇)uboot relocation介绍》和《[uboot] (第四章)uboot流程——uboot编译流程》

=================================================================================

因为在学习uboot的driver module,发现有必要先把uboot的fdt整明白点。所以这里就先学习一下fdt咯。

一、介绍

FDT,flatted device tree,扁平设备树。熟悉linux的人对这个概念应该不陌生。 
简单理解为将部分设备信息结构存放到device tree文件中。 
uboot最终将其device tree编译成dtb文件,使用过程中通过解析该dtb来获取板级设备信息。 
uboot的dtb和kernel中的dtb是一致的。这部分建议直接参考wowo的dtb的文章 
Device Tree(一):背景介绍 
Device Tree(二):基本概念 
Device Tree(三):代码分析

关于uboot的fdt,可以参考doc/README.fdt-control。

二、dtb介绍

1、dtb结构介绍

结构体如下
DTB header
alignment gap
memory reserve map
alignment gap
device-tree structure
alignment gap
device-tree string

dtb header结构如下:

结构体如下
magic
totalsize
off_dt_struct
off_dt_strings
off_mem_rsvmap
version
……

其中,magic是一个固定的值,0xd00dfeed(大端)或者0xedfe0dd0(小端)。 
以s5pv210-tiny210.dtb为例: 
执行”hexdump -C s5pv210-tiny210.dtb | more”命令

@:dts$ hexdump -C s5pv210-tiny210.dtb | more
00000000  d0 0d fe ed 00 00 5a cc  00 00 00 38 00 00 58 14  |......Z....8..X.|
00000010  00 00 00 28 00 00 00 11  00 00 00 10 00 00 00 00  |...(............|
  • 1
  • 2
  • 3

可以看到dtb的前面4个字节就是0xd00dfeed,也就是magic。 
综上,我们只要提取待验证dtb的地址上的数据的前四个字节,与0xd00dfeed(大端)或者0xedfe0dd0(小端)进行比较,如果匹配的话,就说明对应待验证dtb就是一个合法的dtb。

2、dtb在uboot中的位置

dtb可以以两种形式编译到uboot的镜像中。

  • dtb和uboot的bin文件分离

    • 如何使能 
      需要打开CONFIG_OF_SEPARATE宏来使能。
    • 编译说明 
      在这种方式下,uboot的编译和dtb的编译是分开的,先生成uboot的bin文件,然后再另外生成dtb文件。 
      具体参考《[uboot] (第四章)uboot流程——uboot编译流程》。
    • 最终位置 
      dtb最终会追加到uboot的bin文件的最后面。也就是uboot.img的最后一部分。 
      因此,可以通过uboot的结束地址符号,也就是_end符号来获取dtb的地址。 
      具体参考《[uboot] (第四章)uboot流程——uboot编译流程》。
  • dtb集成到uboot的bin文件内部

    • 如何使能 
      需要打开CONFIG_OF_EMBED宏来使能。
    • 编译说明 
      在这种方式下,在编译uboot的过程中,也会编译dtb。
    • 最终位置 
      注意:最终dtb是包含到了uboot的bin文件内部的。 
      dtb会位于uboot的.dtb.init.rodata段中,并且在代码中可以通过__dtb_dt_begin符号获取其符号。 
      因为这种方式不够灵活,文档上也不推荐,所以后续也不具体研究,简单了解一下即可。
  • 另外,也可以通过fdtcontroladdr环境变量来指定dtb的地址 
    可以通过直接把dtb加载到内存的某个位置,并在环境变量中设置fdtcontroladdr为这个地址,达到动态指定dtb的目的。 
    在调试中使用。

三、uboot中如何支持fdt

1、相关的宏

  • CONFIG_OF_CONTROL 
    用于配置是否使能FDT。 
    ./source/configs/tiny210_defconfig:312:CONFIG_OF_CONTROL=y

  • CONFIG_OF_SEPARATE、CONFIG_OF_EMBED 
    配置dtb是否集成到uboot的bin文件中。具体参考上述。一般都是使用分离的方式。

2、如何添加一个dtb

以tiny210为例,具体可以参考project X项目中uboot的git记录:8a371676710cc0572a0a863255e25c35c82bb928 
(1)在Makefile中添加对应的目标dtb 
arch/arm/dts/Makefile

dtb-$(CONFIG_TARGET_TINY210) += \s5pv210-tiny210.dtb
  • 1
  • 2

(2)创建对应的dts文件 
arch/arm/dts/s5pv210-tiny210.dts,注意文件名要和Makefile中的dtb名一致

/dts-v1/;
/{
};
  • 1
  • 2
  • 3

(3)打开对应的宏 
configs/tiny210_defconfig

CONFIG_OF_CONTROL=y
CONFIG_OF_SEPARATE=y  ##其实这里不用配,arm默认就是指定这种方式
  • 1
  • 2

(4)因为最终的编译出来的dtb可能会多个,这里需要为tiny210指定一个dtb 
configs/tiny210_defconfig

CONFIG_DEFAULT_DEVICE_TREE="s5pv210-tiny210"
  • 1

编译,解决一些编译错误,就可以发现最终生成了u-boot.dtb文件。 
通过如下“hexdump -C u-boot.dtb | more”命令可以查看我们的dtb文件,得到部分内容如下:

hlos@node4:u-boot$ hexdump -C u-boot.dtb | more
00000000  d0 0d fe ed 00 00 01 a4  00 00 00 38 00 00 01 58  |...........8...X|
00000010  00 00 00 28 00 00 00 11  00 00 00 10 00 00 00 00  |...(............|
00000020  00 00 00 4c 00 00 01 20  00 00 00 00 00 00 00 00  |...L... ........|
00000030  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
  • 1
  • 2
  • 3
  • 4
  • 5

四、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_CONTROLfdtdec_setup, // 获取dtb的地址,并且验证dtb的合法性
#endif
...reserve_fdt, // 为dtb分配新的内存地址空间
...reloc_fdt, // relocate dtb
...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

后面进行具体函数的分析。

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

验证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方式时,就需要在这里地方进行relocateif (gd->fdt_blob) {gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
// 获取dtb的sizegd->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);}
#endifreturn 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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方式时,就需要在这里地方进行relocateif (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的新的地址空间上}
#endifreturn 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

五、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的某个无符号整形属性值。

uboot之fdt介绍相关推荐

  1. [uboot] (番外篇)uboot之fdt介绍

    以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例 [uboot] uboot流程系列: [project X] tiny210(s5pv210)上电启动流程( ...

  2. dm365启动分析以及RBL、UBL、Uboot的简单介绍

    dm365启动方式有许多种,我们的板子上面有一个拨码开关,EM_A[13:11]用来选择启动方式,参照tm320dm365的原理图,启动方式有: EM_A[13:11] BOOT from 000 R ...

  3. u-boot FIT image介绍

    1. 前言 Linux kernel在ARM架构中引入device tree(全称是flattened device tree,后续将会以FDT代称)的时候[1],其实怀揣了一个Unify Kerne ...

  4. 嵌入式Linux开发-uboot常用命令介绍(上篇)

    1. 前言 U-boot是一种开源bootloader, 作用是用来引导操作,以及给开发人员提供测试调试工具.本身算是个精简的Linux系统,主要是负责硬件的初始化和引导,本身带有一些工具,作为引导程 ...

  5. Davinci及U-boot的一些介绍

    TI推出的数字多媒体平台DM系列,集成了ARM与DSP双核处理器:DSP处理器运行DSP/BIOS操作系统,负责音视频编解码算法以及其他图形处理算法:ARM处理器运行MontaVista Linux操 ...

  6. uboot的一般性介绍

    以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除. 一.uboot和内核的实质 1.uboot是一个裸机程序 uboot的本质就是一个复杂点的裸机程序: 2.内核是一个裸机程序 操作系统内核也 ...

  7. 主线剧情-番外02-设备树详解

    设备树详解 本文 续接 <[主线剧情03]NXP i.MX 系列 u-boot 移植基础详解> 一文中移植过程小节中有关设备树的内容. 编辑整理 By Staok,如有错误恭谢指出,侵删. ...

  8. 龙芯PMON(2K1000)启动流程(三、C语言部分③)

    3.6 NAND初始化 dbginit(NULL)执行完成后,说明重要的核心设备初始化过程已经告一段落.接下来设置把BEV清零. BEV1 BEV0 BEV in SR set to zero. (内 ...

  9. 用kernel FDT的思路来研究u-boot FIT

    u-boot FIT image介绍 1. 前言 Linux kernel在ARM架构中引入device tree(全称是flattened device tree,后续将会以FDT代称)的时候[1] ...

最新文章

  1. 趣学python3(38)--多项式最小二乘法拟合
  2. [转载] 杜拉拉升职记——07 管理者关心细节吗?
  3. 南京晓庄学院java实验五_讲座|城市文化与南京世界“文学之都”的建设
  4. 开发RESTful WebService
  5. 2018实用前端面试问题集锦
  6. 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: 命名管
  7. 图片处理拓展篇 : 图片转字符画(ascii)
  8. mft按钮设计_火力发电厂典型MFT逻辑控制实现.pdf
  9. 小鹏的全语音车载系统什么名堂?
  10. 【报告分享】2020年教育行业OMO模式转型现状研究报告.pdf(附下载链接)
  11. 第三:Pytest框架之命令行参数(一)
  12. “表达式必须包含 bool 类型(或可转换为 bool)”
  13. nacos默认用户名密码_Docker下,两分钟极速体验Nacos配置中心
  14. 计算机网络的主要功能四种,计算机网络应用基础概述
  15. 阅读不懂,图书之过——《大话设计模式》创作历程
  16. 必备的网络常用测试命令(ping命令)
  17. 苹果开发者公司账号的申请步骤
  18. 学习前端的第三十二天——ES6
  19. VC无负担实现XP风格界面 [转]
  20. 有未经处理的异常: 0xC0000005: 写入位置 0x00000014 时发生访问冲突

热门文章

  1. 四面向对象的学习、反射、异常处理
  2. 最简单! Python判断ip地址是否格式正确
  3. 什么是NB(门磁) IoT技术?
  4. 翻译项目管理:memoQ报告
  5. linux高级编程实验
  6. 记录KMP算法,记录其经典之处。。。
  7. Unity插件 EasyTouch
  8. 考研机试真题 日期差值 模拟
  9. 无法打开项目文件。 无法找到 .NET SDK。请检查确保已安装此项且 global.json 中指定的版本(如有)与所安装的版本相匹配
  10. Java读取Excel文件汇总