Linux - 设备树解析

概要

   用途: 用于描述计算机板级硬件设备信息,以便于操作系统管理和操作这些硬件,由于这些信息对于每一款硬件均具有差异,故此类描述信息存在内核属于冗余代码,故将其独立出来。

   描述方式:使用文本方式描述,以后缀.dts存在

   计算机使用格式:通过设备树编译器转换为二进制文件使用

   获取方式:通过引导程序传递一个位置给内核

数据结构

struct fdt_header {
    fdt32_t magic;             /* magic word FDT_MAGIC */
    fdt32_t totalsize;         /* total size of DT block */
    fdt32_t off_dt_struct;         /* offset to structure */
    fdt32_t off_dt_strings;         /* offset to strings */
    fdt32_t off_mem_rsvmap;         /* offset to memory reserve map */
    fdt32_t version;         /* format version */
    fdt32_t last_comp_version;     /* last compatible version */

    /* version 2 fields below */
    fdt32_t boot_cpuid_phys;     /* Which physical CPU id we're
                        booting on */
    /* version 3 fields below */
    fdt32_t size_dt_strings;     /* size of the strings block */

    /* version 17 fields below */
    fdt32_t size_dt_struct;         /* size of the structure block */
};

struct fdt_reserve_entry {
    fdt64_t address;
    fdt64_t size;
};

struct fdt_node_header {
    fdt32_t tag;    
    char name[]; // 节点的名字
};

struct fdt_property {
    fdt32_t tag;
    fdt32_t len;   // 描述data 数组的长度
    fdt32_t nameoff;  //相对于字符串表起始位置的相对偏移
    char data[];  //存储属性的值
};

#define FDT_MAGIC    0xd00dfeed    /* 4: version, 4: total size */
#define FDT_TAGSIZE    sizeof(fdt32_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 */          // 特殊Tag,一般处理是当空处理
#define FDT_END        0x9                             //标记着整个流处理完成后续没有更多数据需要处理
 

二进制文件结构

其中最关键的dt_struct 用于描述fdt_node_header 其规则如下:

  • 每一个fdt_node_header 的Tag为FDT_BEGIN_NODE
  • 每一个fdt_node_header可以包含任意个fdt_property
  • 每一个fdt_property的Tag为FDT_PROP
  • 每一个fdt_node_header可以包含任意个子fdt_node_header
  • 每一个fdt_node_header最后以FDT_END_NODE结束
  • FDT_NOP为空白tag可以跳过

解析代码

解析过程以不断的查找dt_struct中的Tag,然后依据Tag的定义解析后续的内容,关键函数如下:

fdt 为二进制设备树的内存起始位置

startoffset 为当前tag的偏移位置

nextoffset为返回下一个Tag的偏移位置

返回当前Tag的值

uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
{
    const fdt32_t *tagp, *lenp;
    uint32_t tag, len, sum;
    int offset = startoffset;
    const char *p;

    *nextoffset = -FDT_ERR_TRUNCATED;
    tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);  //获取当前偏移的Tag
    if (!can_assume(VALID_DTB) && !tagp)
        return FDT_END; /* premature end */
    tag = fdt32_to_cpu(*tagp);  //大小端调整
    offset += FDT_TAGSIZE;

    *nextoffset = -FDT_ERR_BADSTRUCTURE;
    switch (tag) {//依据Tag类型处理 Tag所代表的对象,并得出下一个Tag的位置
    case FDT_BEGIN_NODE:
        /* skip name */
        do {
            p = fdt_offset_ptr(fdt, offset++, 1);
        } while (p && (*p != '\0'));//跳过Node的名字
        if (!can_assume(VALID_DTB) && !p)
            return FDT_END; /* premature end */
        break;

    case FDT_PROP:
        lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); //获取property值的长度
        if (!can_assume(VALID_DTB) && !lenp)
            return FDT_END; /* premature end */

        len = fdt32_to_cpu(*lenp);
        sum = len + offset;
        if (!can_assume(VALID_DTB) &&
            (INT_MAX <= sum || sum < (uint32_t) offset))
            return FDT_END; /* premature end */

        /* skip-name offset, length and value */
        offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len;  //计算property的长度从而获得下一个Tag的偏移

        if (!can_assume(LATEST) &&
            fdt_version(fdt) < 0x10 && len >= 8 &&
            ((offset - len) % 8) != 0)
            offset += 4;
        break;

    case FDT_END:        // 结束或空白Tag,只需跳过这个Tag即可
    case FDT_END_NODE:
    case FDT_NOP: 
        break;

    default:
        return FDT_END;
    }

    if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
        return FDT_END; /* premature end */

    *nextoffset = FDT_TAGALIGN(offset); //对齐处理
    return tag;
}

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值