分析内核源码,设备树

转自: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>;
};



在看看解析设备树这一块

猜你喜欢

转载自blog.csdn.net/vc66vcc/article/details/80422392
今日推荐