一、什么是设备树
设备树(DeviceTree)是用来保存系统的是设备信息,用于系统启动时,OS可以通过解析设备树信息,加载对应的设备资源(包括但加载驱动和设置关键设备参数)。
设备树的出现,解决了硬件资源被写死在代码里,每次变更都需要重新编译镜像的尴尬场景。采用设备树之后,可以通过修改设备树内容,动态加载对应设备,而不需要重新编译镜像。
通常我们修改的设备树的扩展名为dts或dtsi,它是文本文件,记事本就可以打开。
设备树文件我上传了两个,dts和dtsi,需要的同学自行下载。
aspeed-g5.dtsi-OS文档类资源-CSDN下载aspeed-2500的通用配置设备树文件更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/yjj350418592/54149828aspeed-ast2500-evb.dts-OS文档类资源-CSDN下载aspeed2500的设备树文件更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/yjj350418592/54149527
二、设备树的体系和文法
2.1 设备树文件类型
设备树有自己单独的语法规则,自成一套体系,但不复杂。设备树体系主要分:设备树定义dts/dtsi,设备树编译工具dtc,设备树二进制文件dtb。
2.1.1 dts
硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份xxxx.dts,一般在Linux源码中存在大量的dts文件,对于arm架构可以在arch/arm/boot/dts找到相应的dts,一个dts文件对应一个ARM的machie。
2.1.2 dtsi
值得一提的是,对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于C语言的方式可以include到dts文件中,对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置。
2.1.3 dtc
dtc是编译dts的工具,可以在Ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工,不过在内核源码scripts/dtc路径下已经包含了dtc工具;
2.1.4 dtb
dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序加载到内核。所以Bootloader需要支持设备树才行;Kernel也需要加入设备树的支持;
2.2 dts/dtsi的语法规则
这里我不罗列所有的语法规则,只把几个重要的点讲一下,其他的可以参考设备树的官方文档。文档下载地址:
- dtsi通过include方式包含在dts中,如果文件中有相同项,dts中的配置会覆盖dtsi的相同配置。
#include "aspeed-g5.dtsi"
另外dtsi中,也可以包含.h的c语言头文件。
2. 必须有根节点且只有一个根节点。/
3.设备树的通用大小是uint32即4个字节单位。通常我们看到的数据大小 <2>形式,表示该字段是2个单位的uint32即8个字节表示。
4.所有语句需要以分号(;)结束。
5.字符串主要以下几种类型:
- 设备节点名称
- 设备类型
- 属性节点名称 属性值
- 标签名称
6.属性值的表示通常有两类:
尖括号< >,可以有多个值,多个值用空格隔开
英文双引号””,也可以有多个值,用逗号分割
7.几个重要的属性说明
#address-cells = <1>: 基地址、片选号等绝对起始地址所占字长,单位uint32
#size-cells = <1>: 长度所占字长,单位uint32
compatible属性值是,指定了系统的名称,是一个字符串列表,它包含了一个“<制造商>,<型号>”形式的字符串。重要的是要指定一个确切的设备,并且包括制造商的名字,以避免命名空间冲突。
下面用一张图为例,可以参考。
2.3 dtb的文件格式
2.3.1 Dtb的文件格式如下:
可以参考下面图片文件,详细说明了dtb的格式以及对应的文件的对应关系。
2.3.2 文件头boot_param_header
结构体定义如下:
struct boot_param_header {
u32 magic;----------------用于标dtb文件头,等于OF_DT_HEADER=“0xd00dfeed”
u32 totalsize;------------dtb文件大小
u32 off_dt_struct;--------DT structure偏移
u32 off_dt_strings;-------DT strings偏移
u32 off_mem_rsvmap;-------memory reserve map偏移
u32 version;--------------版本号
u32 last_comp_version;----兼容最早版本号
/* version 2 fields below */
u32 boot_cpuid_phys;------physical CPU id
/* version 3 fields below */
u32 size_dt_strings;------size of the strings block
/* version 17 fields below */
u32 size_dt_struct;-----------size of the DT structure block
};
2.3.3 保留内存memory reserve map
这段保存的是一个保留内存映射列表,每个表由一对64位的物理地址和大小组成
2.3.4 device-tree structure&strings
由于某些属性(比如compatible)在大多数节点下都会存在,为了减少dtb文件大小,就需要把这些属性字符串只指定一个存储位置即可,这样每个节点的属性只需要按照位置找到属性字符串的位置就可以得到是哪个属性,所以dtb把device-tree strings单独列出来存储,下图是device-tree structure的格式,节点嵌套节点
上面的宏定义如下
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
#define FDT_TAGSIZE sizeof(uint32_t)
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: name off, size, content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
#define FDT_V1_SIZE (7*sizeof(uint32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
#define FDT_V16_SIZE FDT_V3_SIZE
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t))
三、设备树的编译和反编译
3.1 dtc的用法总结
DTC编译:
dtc编译器能够把 dts 文件生成为dtb文件,也能把dtb文件生成为dts文件
dtc [-I input-format] [-O output-format] [-o output-filename] [-V output_version] input_filename
input-format:
- “dtb”: “blob” format
- “dts”: “source” format.
- “fs” format.
output-format:
- “dtb”: “blob” format
- “dts”: “source” format
- “asm”: assembly language file
output_version:
定义”blob”的版本,在dtb文件的字段中有表示,支持1 2 3和16,默认是3,在16版本上有许多特性改变
3.2编译dts文件为dtb文件
由上图可以知道,OS引导文件使用的文件类型是dtb,而非dts,所以我们需要将dts文件编译成dtb。
使用编译命令dtc:
./dtc -I dts -O dtb -o test.dtb test.dts
3.3反编译dtb文件为dts文件
使用编译命令dtc:
./dtc -I dts -O dtb -o test.dtb test.dts
使用dtc反编译得到的dts文件将不在有include dtsi的内容,会全部替换成对应的设备树内容到新的dts中,有时经常使用这种反编译得到一个完整的文件,进行设备树查看和搜索。
四、修改dtb设备树命令
设备树的修改除了可以修改dts再用dtc编译生成dtb外,还可以使用工具命令直接修改设备树。
修改dtb的命令是fdt。
4.1 uboot支持fdt
修改是在引导程序中,所以需要uboot支持fdt,uboot支持fdt只需要加入一句宏定义。
#define CONFIG_OF_LIBFDT /* Device Tree support */
重新编译uboot既可以支持fdt。
4.2 fdt的用法
U-Boot> fdt
fdt - flattened device tree utility commands
Usage:
fdt addr <addr> [<length>] - Set the fdt location to <addr> 设置fdt地址
fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active 移动fdt到指定地址上
fdt resize - Resize fdt to size + padding to 4k addr
fdt print <path> [<prop>] - Recursive print starting at <path> 打印设备树指定路径的节点
fdt list <path> [<prop>] - Print one level starting at <path> 打印设备树指定路径的一级节点
fdt set <path> <prop> [<val>] - Set <property> [to <val>] 设置属性值
fdt mknode <path> <node> - Create a new node after <path> 在路径上创建节点
fdt rm <path> [<prop>] - Delete the node or <property> 删除节点(属性)
fdt header - Display header info 打印fdt头部信息
fdt bootcpu <id> - Set boot cpuid 设置启动cpuid
fdt memory <addr> <size> - Add/Update memory node 添加或更新内存节点
fdt rsvmem print - Show current mem reserves 显示当前保留内存
fdt rsvmem add <addr> <size> - Add a mem reserve 添加保留内存
fdt rsvmem delete <index> - Delete a mem reserves 删除保留内存
fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree<start>/<end> - initrd start/end addr
NOTE: Dereference aliases by omiting the leading '/', e.g. fdt print ethernet0.
五、好的参考文档推荐
1,参考韦东山的设备树讲解。
2,参考设备树的官方文档:Specifications - DeviceTree
3,linux设备树文档:\linux-aspeed\Documentation\devicetree\
最后:点赞是美德,关注是缘分,收藏是肯定,打赏你随意,你的鼓励是我世界善的一部分,爱你们!