概要
用途: 用于描述计算机板级硬件设备信息,以便于操作系统管理和操作这些硬件,由于这些信息对于每一款硬件均具有差异,故此类描述信息存在内核属于冗余代码,故将其独立出来。
描述方式:使用文本方式描述,以后缀.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;
}