Exynos4412 内核移植(六)—— 设备树解析

一、描述

        ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。为了改变这种局面,Linux社区的大牛们参考了PowerPC等体系架构中使用的Flattened Device Tree(FDT),也采用了Device Tree结构,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。  

       Device Tree是一种描述硬件的数据结构,它起源于 OpenFirmware (OF)。在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。  

      Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):

  • CPU的数量和类别
  • 内存基地址和大小
  • 总线和桥
  • 外设连接
  • 中断控制器和中断使用情况
  • GPIO控制器和GPIO使用情况
  • Clock控制器和Clock使用情况

     它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

     通常.dts文件以文本方式对系统设备树进行描述,经过Device Tree Compiler(dtc)将dts文件转换成二进制文件binary device tree blob(dtb),.dtb文件可由Linux内核解析,有了device tree就可以在不改动Linux内核的情况下,对不同的平台实现无差异的支持,只需更换相应的dts文件,即可满足。


二、相关结构体

1、U-Boot需要将设备树在内存中的存储地址传给内核。该树主要由三大部分组成:头(Header)、结构块(Structure block)、字符串块(Strings block)。

设备树在内存中的存储布局图:



1.1 头(header)
      头主要描述设备树的一些基本信息,例如设备树大小,结构块偏移地址,字符串块偏移地址等。偏移地址是相对于设备树头的起始地址计算的。

struct boot_param_header {
    __be32 magic;                //设备树魔数,固定为0xd00dfeed
    __be32 totalsize;            //整个设备树的大小
    __be32 off_dt_struct;        //保存结构块在整个设备树中的偏移
    __be32 off_dt_strings;        //保存的字符串块在设备树中的偏移
    __be32 off_mem_rsvmap;        //保留内存区,该区保留了不能被内核动态分配的内存空间
    __be32 version;            //设备树版本
    __be32 last_comp_version;    //向下兼容版本号
    __be32 boot_cpuid_phys;    //为在多核处理器中用于启动的主cpu的物理id
    __be32 dt_strings_size;    //字符串块大小
    __be32 dt_struct_size;     //结构块大小
};

1.2 结构块(struct block)

       设备树结构块是一个线性化的结构体,是设备树的主体,以节点node的形式保存了目标单板上的设备信息。

       在结构块中以宏OF_DT_BEGIN_NODE标志一个节点的开始,以宏OF_DT_END_NODE标识一个节点的结束,整个结构块以宏OF_DT_END结束。一个节点主要由以下几部分组成。

(1)节点开始标志:一般为OF_DT_BEGIN_NODE。
(2)节点路径或者节点的单元名(ersion<3以节点路径表示,version>=0x10以节点单元名表示)
(3)填充字段(对齐到四字节)
(4)节点属性。每个属性以宏OF_DT_PROP开始,后面依次为属性值的字节长度(4字节)、属性名称在字符串块中的偏移量(4字节)、属性值和填充(对齐到四字节)。
(5)如果存在子节点,则定义子节点。
(6)节点结束标志OF_DT_END_NODE。


1.3 字符串块

       通过节点的定义知道节点都有若干属性,而不同的节点的属性又有大量相同的属性名称,因此将这些属性名称提取出一张表,当节点需要应用某个属性名称时直接在属性名字段保存该属性名称在字符串块中的偏移量。

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宏来初始化,在代码中, 通过在start_kernel->setup_arch中调用setup_machine_fdt来获取。

struct machine_desc {
    unsigned int nr; /* architecture number */
    const char *name; /* architecture name */
    unsigned long atag_offset; /* tagged list (relative) */
    const char *const *dt_compat; /* array of device tree* 'compatible' strings */
    unsigned int nr_irqs; /* number of IRQs */
    
#ifdef CONFIG_ZONE_DMA
    phys_addr_t dma_zone_size; /* size of DMA-able area */
#endif
    
    unsigned int video_start; /* start of video RAM */
    unsigned int video_end; /* end of video RAM */
    
    unsigned char reserve_lp0 :1; /* never has lp0 */
    unsigned char reserve_lp1 :1; /* never has lp1 */
    unsigned char reserve_lp2 :1; /* never has lp2 */
    enum reboot_mode reboot_mode; /* default restart mode */
    struct smp_operations *smp; /* SMP operations */
    bool (*smp_init)(void);
    void (*fixup)(struct tag *, char **,struct meminfo *);
    void (*init_meminfo)(void);
    void (*reserve)(void);/* reserve mem blocks */
    void (*map_io)(void);/* IO mapping function */
    void (*init_early)(void);
    void (*init_irq)(void);
    void (*init_time)(void);
    void (*init_machine)(void);
    void (*init_late)(void);
#ifdef CONFIG_MULTI_IRQ_HANDLER
    void (*handle_irq)(struct pt_regs *);
#endif
    void (*restart)(enum reboot_mode, const char *);
};

1.6 设备节点结构体
struct device_node {
    const char *name;    //设备name
    const char *type; //设备类型
    phandle phandle;
    const char *full_name; //设备全称,包括父设备名

    struct property *properties; //设备属性链表
    struct property *deadprops; //removed properties
    struct device_node *parent; //指向父节点
    struct device_node *child; //指向子节点
    struct device_node *sibling; //指向兄弟节点
    struct device_node *next; //相同设备类型的下一个节点
    struct device_node *allnext; //next in list of all nodes
    struct proc_dir_entry *pde; //该节点对应的proc
    struct kref kref;
    unsigned long _flags;
    void *data;
#if defined(CONFIG_SPARC)
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

1.7 属性结构体

struct property {
    char *name;        //属性名
    int length;        //属性值长度
    void *value;        //属性值
    struct property *next; //指向下一个属性
    unsigned long _flags; //标志
    unsigned int unique_id;
};


三、设备树初始化及解析

        分析Linux内核的源码,可以看到其对扁平设备树的解析流程如下:

  • 17
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值