uboot支持设备树要早于Linux,Linux内核移植 part2:uboot设备树--生成过程分析

本文从设备树软件控制相关代码进行分析,进而理清设备树相关的知识。

先放一个设备树在内存中的结构图:

2bff3c48bc5bc5d7b0bf25b03b03126b.png

分析来源为$(tree)/lib/fdtdec_test.c

一、数据结构

1.1 文件头

每个dtb都包含如下结构的文件头,用来表示设备树的基础信息。

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 */

};

1.2 节点头

struct fdt_node_header {

fdt32_t tag;

char name[0];

};

二、生成设备树

static int make_fdt(void *fdt, int size, const char *aliases,

const char *nodes)

{

char name[20], value[20];

const char *s;

int fd;

// 根据size创建fdt对象,此处size=16KB

CHECK(fdt_create(fdt, size));

// 设置保留内存段

CHECK(fdt_finish_reservemap(fdt));

// 添加开始节点

CHECK(fdt_begin_node(fdt, ""));

CHECK(fdt_begin_node(fdt, "aliases"));

for (s = aliases; *s;) {

sprintf(name, "i2c%c", *s);

sprintf(value, "/i2c%d@0", s[1] - 'a');

CHECK(fdt_property_string(fdt, name, value));

s += 2 + (s[2] != '\0');

}

CHECK(fdt_end_node(fdt));

for (s = nodes; *s; s++) {

sprintf(value, "i2c%d@0", (*s & 0xdf) - 'A');

CHECK(fdt_begin_node(fdt, value));

CHECK(fdt_property_string(fdt, "compatible",

fdtdec_get_compatible(COMPAT_UNKNOWN)));

if (*s <= 'Z')

CHECK(fdt_property_string(fdt, "status", "disabled"));

CHECK(fdt_end_node(fdt));

}

CHECK(fdt_end_node(fdt));

CHECK(fdt_finish(fdt));

CHECK(fdt_pack(fdt));

#if defined(DEBUG) && defined(CONFIG_SANDBOX)

fd = os_open("/tmp/fdtdec-text.dtb", OS_O_CREAT | OS_O_WRONLY);

if (fd == -1) {

printf("Could not open .dtb file to write\n");

return -1;

}

os_write(fd, fdt, size);

os_close(fd);

#endif

return 0;

}

2.1 节点分配

2.1.1 开始分配节点

/* 根据空余空间分配新的节点,新分配的node位于dt_struct下面 */

int fdt_begin_node(void *fdt, const char *name)

{

struct fdt_node_header *nh;

int namelen = strlen(name) + 1;

FDT_SW_CHECK_HEADER(fdt);

// 分配存放node空间

nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));

if (! nh)

return -FDT_ERR_NOSPACE;

// 设置节点tag,name

nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);

memcpy(nh->name, name, namelen);

return 0;

}

2.1.2 属性节点和字符串块

/* include/libfdt.h */

#define fdt_property_string(fdt, name, str) \

fdt_property(fdt, name, str, strlen(str)+1)

/* lib/libfdt/fdt_sw.c */

int fdt_property(void *fdt, const char *name, const void *val, int len)

{

struct fdt_property *prop;

int nameoff;

FDT_SW_CHECK_HEADER(fdt);

/* 在字符串块中搜索有没有名字为name的字符串,返回存放的字符串的地址

* 这个地址是从字符串块的底部开始计算偏移的。

*/

nameoff = _fdt_find_add_string(fdt, name);

if (nameoff == 0)

return -FDT_ERR_NOSPACE;

prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));

if (! prop)

return -FDT_ERR_NOSPACE;

/* 属性节点存放了tag,value以及name的偏移和长度 */

prop->tag = cpu_to_fdt32(FDT_PROP);

prop->nameoff = cpu_to_fdt32(nameoff);

prop->len = cpu_to_fdt32(len);

memcpy(prop->data, val, len);

return 0;

}

注意:属性name和value的格式

aliases = "1e 3d"

for (s = aliases; *s;) {

/* 名字是字符串加数字 */

sprintf(name, "i2c%c", *s);

/* value是路径@地址 */

sprintf(value, "/i2c%d@0", s[1] - 'a');

CHECK(fdt_property_string(fdt, name, value));

s += 2 + (s[2] != '\0');

}

2.1.3 节点结束标记

int fdt_end_node(void *fdt);

执行这个意味着该节点到此结束了。其实就是在node末尾写了一个标识符FDT_END_NODE, 说明该节点结束了,下面开始新的节点定义。

每一个节点的定义都经历了2.1节这样的过程。

2.2 完成设备树

int fdt_finish(void *fdt)

这个函数首先在未分配空间顶部写入FDT_END,然后通过把字符串块移动到结构块下面,消除剩余的未分配空间,再重新计算字符串块中的各个name的偏移,最后调整文件头,因为totalsize变了。

2.3 设备树打包

这个时候还要计算reserved mem大小,reserved mem位于header和dt_struct之间。然后重新计算结构块和字符串块的偏移。基本上就是重复2.2的过程。打包完成后就生成了最终的dtb文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值