转自:http://blog.csdn.net/fight_onlyfor_you/article/details/78092204
U-Boot需要将设备树在内存中的存储地址传给内核。该树主要由三大部分组成:头(Header)、结构块(Structure block)、字符串块(Strings block)。
设备树在内存中的存储布局图如下
1.1 头(header)
1.2 结构块(struct block)
扁平设备树结构块是线性化的树形结构,和字符串块一起组成了设备树的主体,以节点形式保存目标板的设备信息
在结构块中,节点起始标志为32位常值宏OF_DT_BEGIN_NODE,节点结束标志为宏OF_DT_END_NODE
子节点定义在节点结束标志前。一个节点的基本结构如下所示:
(1).
节点起始标志OF_DT_BEGIN_NODE(即0x0000_0001);
(2). 节点路径或者节点单元名(version < 3以及节点路径表示,version > 16时以节点单元名表示);
(3). 填充字节保证四字节对齐;
(4). 节点属性。
每个属性以常值宏OF_DT_PROP 开始,后面依次为属性值的字节长度、属性名在字符串块中的偏移值, (5).
如果存在子节点,则定义子节点
(6). 节点结束标志OF_DT_END_NODE(即0x0000_0002)。
1.3 字符串块(Strings block)
1.4 设备树源码 DTS 表示
设备树源码文件(.dts)以可读可编辑的文本形式描述系统硬件配置设备树,支持
C/C++方式的注释,该结构有一个唯一的根节点“/”,每个节点都有自己的名字并可以包含多个子节点。设备树的数据格式遵循了 Open
Firmware IEEE standard
1275。这个设备树中有很多节点,每个节点都指定了节点单元名称。每一个属性后面都给出相应的值。以双引号引出的内容为 ASCII
字符串,以尖括号给出的是 32 位的16进制值。这个树结构是启动 Linux 内核所需节点和属性简化后的集合,包括了根节点的基本模式信息、CPU
和物理内存布局,它还包括通过/chosen 节点传递给内核的命令行参数信息。
1.5 machine_desc结构
内核提供了一个重要的结构体struct machine_desc
,这个结构体在内核移植中起到相当重要的作用,内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。machine_desc结构体通过MACHINE_START宏来初始化,在代码中,
通过在mian->setup_arch中调用setup_machine_fdt来获取。
1.6 设备节点结构体
1.7 属性结构体
三、设备树初始化及解析
分析Linux内核的源码,可以看到其对扁平设备树的解析流程如下
of_allnodes
源码如下
在init目录下的main.c中的start_kernel函数中
进入这个setup_arch
其余代码先省略
查看setup_machine_fdt函数
这里调用了early_init_dt_scan_nodes函数
来看看这个函数
看看这个fdt_next_node函数
上面还有一个很重要的地方
这里,这里对这个initial_boot_parems进行赋值,这里这个指针指向了设备树的虚拟起始地址
在这里进行偏移,详细信息看这里的代码
chosen属性
chosen
节点并不代表一个真实的设备,只是作为一个为固件和操作系统之间传递数据的地方,比如引导参数。chosen
节点里的数据也不代表硬件。通常,chosen 节点在.dts 源文件中为空,并在启动时填充。在我们的示例系统中,固件可以往 chosen
节点添加以下信息:
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; //节点属性
linux,initrd-start = <0x85500000>; //节点属性
linux,initrd-end = <0x855a3212>; //节点属性
};
1.2 根节点"/"
设备树有且仅有一个根节点,即“/”,根节点下包含很多子节点,例入下图,根节点为"/",根节点的子节点为"chosen",根节点的属性包含"compatible","#address-cells","#size-cells","interrupt-parent"等。属性model指明了目标板平台或模块的名称,属性compatible值指明和目标板为同一系列的兼容的开发板名称。对于大多数32位平台,属性#address-cells和#size-cells的值一般为1。#address-cells
= <1>; 1表示地址32位,2表示地址64位。#size-cells =
<1>;1表示rangs的每部分占一个cell,依此类推
{
compatible = "sprd,spx15";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&gic>;
chosen {
bootargs = "loglevel=8 console=ttyS1,115200n8 init=/init root=/dev/ram0 rw";
linux,initrd-start = <0x85500000>;
linux,initrd-end = <0x855a3212>;
};
}
所以本函数就是读取根节点的"#address-cells","#size-cells"属性
1.3 memory节点
memory节点用于描述目标板上物理内存范围,一般称作/memory节点,可以有一个或多个。当有多个节点时,需要后跟单元地址予以区分;只有一个单元地址时,可以不写单元地址,默认为0。此节点包含板上物理内存的属性,一般要指定device_type(固定为"memory")和reg属性。其中reg的属性值以<起始地址
空间大小>的形式给出,如下示例中目标板内存起始地址为0x80000000,大小为0x20000000字节。
memory {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
在看看解析设备树这一块
原文地址:https://www.cnblogs.com/sky-heaven/p/8534724.html