嵌入式 设备树解析

              /* add by starby */
       name = "chosen";
       bootargs = "root=/dev/ram rw console=ttyS0,115200";
       linux,stdout-path = "/soc8349@e0000000/serial@4500";
   };

};


3.1 根节点

设备树的起始点称之为根节点"/"。属性model指明了目标板平台或模块的名称,属性compatible值指明和目标板为同一系列的兼容的开发板名称。对于大多数32位平台,属性#address-cells和#size-cells的值一般为1。

3.2 CPU节点

/cpus节点是根节点的子节点,对于系统中的每一个CPU,都有相应的节点。/cpus节点没有必须指明的属性,但指明#address-cells = <1>和 #size-cells = <0>是个好习惯,这同时指明了每个CPU节点的reg属性格式,方便为物理CPU编号。
此节点应包含板上每个CPU的属性。CPU名称一般写作PowerPC,<name>,例如Freescale会使用PowerPC,8349来描述本文的MPC8349E处理器。CPU节点的单元名应该是cpu@0的格式,此节点一般要指定device_type(固定为"cpu"),一级数据/指令缓存的表项大小,一级数据/指令缓存的大小,核心、总线时钟频率等。在上面的示例中通过系统引导代码动态填写时钟频率相关项。

3.3 系统内存节点

此节点用于描述目标板上物理内存范围,一般称作/memory节点,可以有一个或多个。当有多个节点时,需要后跟单元地址予以区分;只有一个单元地址时,可以不写单元地址,默认为0。
此节点包含板上物理内存的属性,一般要指定device_type(固定为"memory")和reg属性。其中reg的属性值以<起始地址 空间大小>的形式给出,如上示例中目标板内存起始地址为0,大小为256M字节。

3.4 /chosen节点

这个节点有一点特殊。通常,这里由Open Firmware存放可变的环境信息,例如参数,默认输入输出设备。
这个节点中一般指定bootargs及linux,stdout-path属性值。bootargs属性设置为传递给内核命令行的参数字符串。linux,stdout-path常常为标准终端设备的节点路径名,内核会以此作为默认终端。
U-Boot在1.3.0版本后添加了对扁平设备树FDT的支持,U-Boot加载Linux内核、Ramdisk文件系统(如果使用的话)和设备树二进制镜像到物理内存之后,在启动执行Linux内核之前,它会修改设备树二进制文件。它会填充必要的信息到设备树中,例如MAC地址、PCI总线数目等。U-Boot也会填写设备树文件中的“/chosen”节点,包含了诸如串口、根设备(Ramdisk、硬盘或NFS启动)等相关信息。
U-Boot源码common/cmd_bootm.c的如下代码,显示了在执行内核代码前将调用ft_setup函数填写设备树。
  1. if defined(CONFIG_OF_FLAT_TREE)
   /*
    * Create the /chosen node and modify the blob with board specific
    * values as needed.
    */
   ft_setup(of_flat_tree, kbd, initrd_start, initrd_end);
   /* ft_dump_blob(of_flat_tree); */
  1. endif

3.5 片上系统SOC节点

此节点用来描述片上系统SOC,如果处理器是SOC,则此节点必须存在。顶级SOC节点包含的信息对此SOC上的所有设备可见。节点名应该包含此SOC的单元地址,即此SOC内存映射寄存器的基址。SOC节点名以/soc<SOCname>的形式命名,例如MPC8349的SOC节点是"soc8349"。
在属性中应该指定device_type(固定为"soc")、ranges、bus-frequency等属性。ranges属性值以<bus_addr parent_bus_addr size>的形式指定。SOC节点还包含目标板使用的每个SOC设备子节点,应该在设备树中尽可能详细地描述此SOC上的外围设备。如下给出带有看门狗设备的SOC节点DTS示例。
      soc8349@e0000000 {
          #address-cells = <1>;
          #size-cells = <1>;
          device_type = "soc";
          compatible = "simple-bus";
          ranges = <0 e0000000 100000>;        /* 1MB size */
          reg = <e0000000 00000200>;
          bus-frequency = <0>;              /* From bootloader */
           wdt@200 {
              device_type = "watchdog";
              compatible = "mpc83xx_wdt";
              reg = <200 100>;                    /* Offset: 0x200 */
         };

3.6 其他设备节点

分级节点用来描述系统上的总线和设备,类似物理总线拓扑,能很方便的描述设备间的关系。对于系统上的每个总线和设备,在设备树中都有其节点。对于这些设备属性的描述和定义请详细参考IEEE 1275及Linux内核文档booting-without-of.txt。
设备树的中断系统稍显复杂,设备节点利用interrupt-parent和interrupts属性描述到中断控制器的中断连接。其中interrupt-parent属性值为中断控制器节点的指针,#interrupts属性值描述可触发的中断信号,其值格式与中断控制器的interrupt-cells属性值有关。一般#interrupt-cells属性值为2,interrupts属性就对应为一对描述硬件中断号和中断触发方式的十六进制值。其具体内容见参考文献[x](Open Firmware Recommended Practice: Interrupt Mapping Version 0.9".  The document is available at: <http://playground.sun.com/1275/practice>.)


4 设备树编译

由于内核只识别二进制格式的扁平设备树,因此需要一个特殊的设备树编译器"dtc",将设备树源码文件(.dts)编译二进制文件(.dtb)。dtc编译器会对输入文件进行语法和语义检查,并根据Linux内核的要求检查各节点及属性,将设备树源码文件(.dts)编译二进制文件(.dtb),以保证内核能正常启动。最新的dtc编译器Git仓库位于www.gdl.com,可以通过如下命令获得源码
$git clone http://www.gdl.com/projects/dtc.git
dtc编译器的使用方法如下所示[6]:
dtc [-I <input_fomat>] [-O <ouput_fomat>] [-o output_filename] [-V output_version] input_filename
input_format可以使用以下三个参数:
dtb: 表示输入文件为dtb文件;
dts: 表示输入文件为dts文件;
fs: 表示输入文件为与/proc/device-tree文件的格式相同。
output_format可以使用以下三个参数:
dtb: 表示输出文件为dtb文件;
dts: 表示输出文件为dts文件;
asm: 表示输出文件为汇编语言文件;
如果output_format为“dtb”是,ouput_version用来规定生成的dtb文件的版本号,目前dtb文件可用的版本号为1,2,3,16或17,output_format的缺省值为17。-S指定的是生成的dtb文件的大小,需要适当地扩大以供u-boot 创建/chose节点时使用。input_filename和output_filename分别为输入和输出文件名。从dtc编译器的使用方法中发现,dtc编译器不仅可以实现dts文件到dtb文件的转换,也可以实现dtb文件到dts文件的转换。
Linux源码的arch/powerpc/boot/dts/目录下存放了很多dts文件,可以作为参考文件。另外dtc编译器在内核源码2.6.25版本之后已经被包含进去。在2.6.26版本之后,生成blob的简单规则已经加入makefile,如下命令:
$ make ARCH=powerpc canyonlands.dtb
也可以根据自己的硬件修改好dts文件后,用下面类似命令生成dtb文件。
$ dtc -f -I dts -O dtb -R 8 -S 0x3000 test.dts  > mpc836x_mds.dtb
$ mkimage -A ppc -O Linux -T flat_dt -C none -a 0x300000 -e 0 -d mpc836x_mds.dtb mpc836x_mds.dtu
注:最新的U-Boot使用dtb镜像文件。而freescale的U-Boot需要如上使用mkimage为dtb添加镜像信息。

5 U-Boot相关设置

为使U-Boot支持设备树,需要在板子配置头文件中设置一系列宏变量。如本文在MPC8349E处理器目标板中移植的U-Boot配置如下:

/* pass open firmware flat tree */

  1. define CONFIG_OF_LIBFDT 1
  2. undef CONFIG_OF_FLAT_TREE
  3. define CONFIG_OF_BOARD_SETUP 1
  4. define CONFIG_OF_HAS_BD_T 1
  5. define CONFIG_OF_HAS_UBOOT_ENV 1

启动引导代码U-Boot在完成自己的工作之后,会加载Linux内核,并将扁平设备树的地址传递给内核,其代码形式如下:

  1. if defined(CONFIG_OF_FLAT_TREE) || defined(CONFIG_OF_LIBFDT)
   if (of_flat_tree) { /* device tree; boot new style */
       /*
        * Linux Kernel Parameters (passing device tree):
        *   r3: pointer to the fdt, followed by the board info data
        *   r4: physical pointer to the kernel itself
        *   r5: NULL
        *   r6: NULL
        *   r7: NULL
        */
       (*kernel) ((bd_t *)of_flat_tree, (ulong)kernel, 0, 0, 0);
       /* does not return */
   }
  1. endif
arch/powerpc内核的入口有且只有一个,入口点为内核镜像的起始。此入口支持两种调用方式,一种是支持Open Firmware启动,另一种对于没有OF的引导代码,需要使用扁平设备树块,如上示例代码。寄存器r3保存指向设备树的物理地址指针,寄存器r4保存为内核在物理内存中的地址,r5为NULL。其中的隐含意思为:假设开启了mmu,那么这个mmu的映射关系是1:1的映射,即虚拟地址和物理地址是相同的。

6 Linux内核对设备的解析。

扁平设备树描述了目标板平台中的设备树信息。每个设备都有一个节点来描述其信息,每个节点又可以有子节点及其相应的属性。内核源码中include/linux/of.h及drivers/of/base.c等文件中提供了一些Open Firmware API,通过这些API,内核及设备驱动可以查找到相应的设备节点,读取其属性值,利用这些信息正确地初始化和驱动硬件。

文件:FDT100125133436.jpg 分析Linux内核的源码,可以看到其对扁平设备树的解析流程[7 待详细分析]如下: (1)首先在内核入口处将从u-boot传递过来的镜像基地址和dtb文件映像基地址保存通用寄存器r30和r31。 (2)通过调用machine_init()、early_init_devtree()函数来获取内核前期初始化所需的bootargs,cmd_line等系统引导参数。 (3)根据bootargs,cmd_line等系统引导参数进入start_kernel()函数,进行内核的第二阶段初始化。 (4)调用start_kernel()、setup_arch()、unflatten_device_tree()函数来解析dtb文件,构建一个由device_node结构连接而成的单项链表,并使用全局变量allnodes 指针来保存这个链表的头指针。 (5)内核调用OF 提供的API函数获取allnodes链表信息来初始化内核其他子系统、设备等。


7 内核折中

引入了设备树FDT blob,相应的会占用一定的存储空间,同时引入了FDT API代码,相比之下内核代码有所增加,但是一个内核镜像可以支持多个硬件平台,不同的平台间只需修改设备树FDT blob即可。

8 结束语

本文介绍了设备树的起源及其优点,进而阐述了设备树的数据存储格式以及源码描述语法,给出了设备树的编译方法,最后引出了移植过程中的U-Boot相关设置说明及内核的解析过程分析。设备树为嵌入式系统向Linux内核传递参数的动态接口,希望本文对嵌入式PowerPC Linux开发者具有一定的参考价值,加快嵌入式PowerPC Linux开发中的设备树DTS移植过程。

参考文献 [1] Matt Tyrlik. Booting Linux on Embedded PowerPCTM Systems [R]. Embedded Linux Developers Conference, 2007 [2] Benjamin Herrenschmidt, Becky Bruce, et al. Booting the Linux/ppc kernel without Open Firmware [Z]. bootint-without-of.txt from the Linux kernel source tree , 2006 [3] DENX. Flattened Device Tree Blob [EB/OL]. http://www.denx.de/wiki/view/DULG/LinuxFDTBlob, 2009 [4] Grant Likely, Josh Boyer. A Symphony of Flavours: Using the device tree to describe embedded hardware [R]. Ottawa, Canada: Linux Symposium, 2008 [5] SUN. The Open Firmware Home Page [EB/OL].http://playground.sun.com/1275/home.html, 2005 [6] David Gibson, Benjamin Herrenschmidt, OzLabs. Device trees everywhere [R]. 2006 [7] 邱文华,邱珍珍. 基于扁平设备树的Linux内核启动方式[J]. 现代计算机,2009年第3期,115-118. 2009.3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值