fdtdec 扁平设备树解析
设备树BLLOB结构
1 | struct fdt_header { |
- magic: 该字段的值为0xd00dfeed(大端位)
- total size: 该字段应包含设备树数据结构的总大小(以字节为单位)。
- 这个大小应该包括结构的所有部分:头,内存保留块,结构块和字符串块,以及块之间或最后块之后的任何空闲空间间隙。
- 0x0035f3dd = 3535837 = 3452kb = 3.37MB
- off_dt_struct:这个字段应该包含从报头开始的结构块的字节偏移量
- 0x00000038 = 56
- off_dt_strings:这个字段应该包含从报头开始的字符串块(参见第5.5节)的字节偏移量
- 0x0035eff8 = 3452kb + 0x00000038 = 56 + 3452kb
- off_mem_rsvmap:这个字段应该包含从报头开始的内存保留块(参见5.3节)的字节偏移量
- 0x00000028 = 40
- version:该字段应包含设备树数据结构的版本。
- 如果使用本文档中定义的结构,则版本为17。
- DTSpec引导程序可以提供更高版本的设备树,在这种情况下,该字段应包含在提供该版本详细信息的后续文档中定义的版本号。
- 0x00000011 = 17
- last_comp_version:该字段应包含与所使用的版本向后兼容的设备树数据结构的最低版本。
- 因此,对于本文档(版本17)中定义的结构,该字段应包含16个17向后兼容版本16,但不兼容更早的版本。按照5.1节的规定,DTSpec引导程序应该以向后兼容版本16的格式提供设备树,因此该字段应始终包含16。
- 0x00000010 = 16
- boot_cpuid_phys:该字段应该包含系统启动CPU的物理ID。它应该与设备树中CPU节点的reg属性中给出的物理ID相同。
- 0x00000000 = 0
- size_dt_strings:该字段应包含devicetree blob的字符串块部分的字节长度
- 0x00000065 = 101
- size_dt_struct:该字段应包含devicetree blob的结构块部分的字节长度。
- 0x0035efc0 = 3534784 = 3451kb = 3.37MB
- free space: 不存在,该区域应该是空的
- memory reservation block:
1
2
3
4struct fdt_reserve_entry {
uint64_t address;
uint64_t size;
};- address: 保留的内存地址 0x0000000000000000
- size: 保留的内存大小 0x0000000000000000
- structure block: 结构块由一系列片段组成,每个片段以一个令牌开头,即一个大端位32位整数。一些令牌后面跟着额外的数据,其格式由令牌值决定。所有令牌都应在32位边界上对齐,这可能需要在前一个令牌的数据之后插入填充字节(值为0x0)
- FDT_BEGIN_NODE (0x00000001) FDT_BEGIN_NODE标记一个节点表示的开始。
- 后面应该加上节点的单位名称作为额外的数据。
- 名称存储为一个以空结束的字符串,如果有的话,应该包括单元地址(见第2.2.1节)。
- 节点名后面跟着零填充字节(如果对齐需要的话),然后是下一个令牌,可以是除FDT_END以外的任何令牌。
- FDT_END_NODE (0x00000002) FDT_END_NODE标记一个节点表示的结束。这个令牌没有额外的数据;所以它后面紧跟着下一个令牌,这个令牌可以是除FDT_PROP以外的任何令牌
- FDT_PROP (0x00000003): FDT_PROP标志着设备树中一个属性表示的开始。其后应附有描述该属性的额外数据。该数据首先由属性的长度和名称组成,表示为以下C结构:
1
2
3
4struct {
uint32_t len;
uint32_t nameoff;
}- len以字节为单位给出属性值的长度(可以为零,表示属性为空,参见第2.2.4节)。
- 0x00000004 = 4
- nameoff在字符串块中给出一个偏移量(参见第5.5节),属性的名称存储在该块中,作为一个以空结束的字符串。
- 0x00000055 = 85
- 0x0035eff8 + 0x00000055 = 0x0035f04d
- 字符串中查找为:0x74696d657374616d700 = “timestamp “
- 属性的值:在此结构之后,该属性的值作为长度为len的字节串给出。该值后面是零填充字节(如果有必要),以对齐下一个32位边界,然后是下一个令牌,可以是除FDT_END之外的任何令牌。
- value: 0x625ea451: 2022-04-13 09:57:37
- len以字节为单位给出属性值的长度(可以为零,表示属性为空,参见第2.2.4节)。
- 根节点: 空字符串(表示根节点)
- 属性: timestamp: 2022-04-13 09:57:37
- 属性: description: “RT-Thread STM32H750i-ART-PI board”
- 节点:images:
- 节点:kernel:
- 属性:description: “Linux kernel for RT-Thread STM32H750i-ART-PI board”
- 属性:data: 长度:15A9D0= 1416KB = 1.38MB
- 属性:type: kernel
- 属性arch:arm
- 节点:kernel:
- 完整解析如下
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54/ {
description = "RT-Thread STM32H750i-ART-PI board";
images {
kernel {
description = "Linux kernel for RT-Thread STM32H750i-ART-PI board";
data = /incbin/("./zImage");
type = "kernel";
arch = "arm";
os = "linux";
compression = "none";
load = <0xC0080000>;
entry = <0xC0080000>;
hash {
algo = "sha1";
};
};
fdt {
description = "Flattened Device Tree blob for for RT-Thread STM32H750i-ART-PI board";
data = /incbin/("./stm32h750i-art-pi.dtb");
type = "flat_dt";
arch = "arm";
os = "linux";
compression = "none";
hash {
algo = "sha1";
};
};
ramdisk {
description = "Ramdisk for for RT-Thread STM32H750i-ART-PI board";
data = /incbin/("./rootfs.ext2");
type = "ramdisk";
arch = "arm";
os = "linux";
compression = "none";
hash {
algo = "sha1";
};
};
};
configurations {
default = "conf0";
conf0 {
description = "Boot Linux kernel with FDT blob";
kernel = "kernel";
fdt = "fdt";
ramdisk = "ramdisk";
};
};
}; - FDT_BEGIN_NODE (0x00000001) FDT_BEGIN_NODE标记一个节点表示的开始。
1 | magic: d0 0d fe ed |
lib
fdtdec.c
fdtdec_setup 初始化fdtdec 写入fdt_blob和fdt_src
1 | int fdtdec_setup(void) |
fdt_find_separate 查找分离的fdt 返回在_end处的fdt,并检查是否正确
1 |
|
fdtdec_prepare_fdt 检查是否有可用于控制 U-Boot 的有效 fdt
1 |
|
fdtdec_setup_mem_size_base
- 从设备树中读取
memory
节点,并设置gd->ram_size
和gd->ram_base
1 |
|
fdtdec_parse_phandle_with_args 解析phandle和参数
- struct fdtdec_phandle_args *out_args: 输出参数
- out_args.node:list_name的phandle索引到的节点
- out_args.args_count:list_name的phandle索引到的节点下的cells_name属性的值
- out_args.uint32_t args[MAX_PHANDLE_ARGS]:src_node的list_name属性的值
1 | int fdtdec_parse_phandle_with_args(const void *blob, int src_node, |
libfdt
fdt.c
fdt_check_header 检查fdt头部是否正确
fdt_next_node 根据规则查找下一个节点
1 | int fdt_next_node(const void *fdt, int offset, int *depth) |
fdt_next_tag 查找下一个标签
fdt_strerror.c
1 |
|
fdt_ro.c
fdt_path_offset_namelen 查找路径的偏移量
1 | int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) |
fdt_subnode_offset_namelen 查找子节点的偏移量
1 | /* fdt:设备树指针 |
fdt_getprop_namelen 查找属性的值
fdt_num_mem_rsv 查找保留的内存块数量
fdt_get_mem_rsv 查找保留的内存块的地址和大小
fdt_rw.c
fdt_open_into
1 | int fdt_open_into(const void *fdt, void *buf, int bufsize) |
libfdt_internal.h
FDT_ASSUME_MASK
- 定义了FDT_ASSUME_MASK,用于判断是否进行额外的检查;每个位代表一个检查
1 |
|
cmd
fdt.c
set_working_fdt_addr
1 | /* |
boot
boot/fdt_support.c
fdt_initrd 在FDT中设置initrd的开始和结束地址
1 | int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) |
fdt_shrink_to_minimum 将给定的 blob 转换成空的 FDT,大小保存不变
- 将给定的 blob 缩小到“最小”大小 。 新大小足以容纳现有内容加上。 加上 5 个内存预留。
- 此外,FDT 的末尾与 4KB 边界对齐,因此它最终可能会比需要大 4KB。
- 如果 FDT 中存在 @blob 的现有内存预留,则会将其更新为新大小。
- 最终的FDT是一个完整的FDT,大小与之前的FDT相同,但是都被分配为内存预留。
fdt
fdt_first_subnode
- 给出下一个节点的偏移量
1 | int fdt_first_subnode(const void *fdt, int offset) |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 wdfk-prog的个人博客!
评论