首先我们看一下内核中对一个设备节点的表示
struct device_node {
const char *name; //节点的名字
const char *type; //device_type属性的值
phandle phandle; //对应该节点的phandle属性
const char *full_name; //节点的名字, node-name[@unit-address]从“/”开始的,表示该node的full path
struct fwnode_handle fwnode;
struct property *properties; // 节点的属性
struct property *deadprops; /* removed properties 如果需要删除某些属性,kernel并非真的删除,而是挂入到deadprops的列表 */
struct device_node *parent; // 节点的父亲
struct device_node *child; // 节点的孩子(子节点)
struct device_node *sibling; // 节点的兄弟(同级节点)
#if defined(CONFIG_OF_KOBJ) // 在sys文件系统表示
struct kobject kobj;
#endif
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
};
从这里我们就可以看到一个设备节点的大概组织形式了。通过下面这三个组织成一个树形结构。
struct device_node *parent; // 节点的父亲
struct device_node *child; // 节点的孩子(子节点)
struct device_node *sibling; // 节点的兄弟(同级节点)
device_node结构体中有properties, 用来表示该节点的属性
struct property {
char *name; //属性名字
int length; //value的长度
void *value; //属性值
struct property *next; //指向统一节点的下一个属性
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
struct bin_attribute attr;
#endif
};
接下来我以第三节中的例子为例来组织一下设备树在内核中子节点中怎么表示的。
/dts-v1/;
/memreserve/ 0x4ff00000 0x100000;
/ {
model = "YIC System SMDKV210 based on S5PV210";
compatible = "yic,smdkv210", "samsung,s5pv210";
#address-cells = <1>;
#size-cells = <1>;
chosen {
bootargs = "console=ttySAC2,115200n8 root=/dev/nfs nfsroot=192.168.0.101:/home/run/work/rootfs/";
};
memory@30000000 {
device_type = "memory";
reg = <0x30000000 0x20000000>;
};
};
在dtb中是如下表示的
下图表示上面的根节点和其里面的两个子节点。
接下来向里面填充属性值
首先是把根节点里的4个属性进行填充
在添加上其它两个节点的属性后如下。
接着前面几节细化分析:
void __init setup_arch(char **cmdline_p)
{
const struct machine_desc *mdesc;
setup_processor();
mdesc = setup_machine_fdt(__atags_pointer); //确认设备树文件,找到最匹配machine_desc
if (!mdesc)
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
if (!mdesc) {
early_print("\nError: invalid dtb and unrecognized/unsupported machine ID\n");
early_print(" r1=0x%08x, r2=0x%08x\n", __machine_arch_type,
__atags_pointer);
if (__atags_pointer)
early_print(" r2[]=%*ph\n", 16,
phys_to_virt(__atags_pointer));
dump_machine_table();
}
machine_desc = mdesc;
machine_name = mdesc->name;
dump_stack_set_arch_desc("%s", mdesc->name);
if (mdesc->reboot_mode != REBOOT_HARD)
reboot_mode = mdesc->reboot_mode;
//标记内核程序在内存的位置
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = (unsigned long) _end;
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line; //保存命令行参数
early_fixmap_init();
early_ioremap_init();
parse_early_param(); //早期的解析命令行参数
#ifdef CONFIG_MMU
early_mm_init(mdesc); //早期的内存管理相关的页表始化
#endif
setup_dma_zone(mdesc);
xen_early_init();
efi_init();
/*
* Make sure the calculation for lowmem/highmem is set appropriately
* before reserving/allocating any mmeory
*/
adjust_lowmem_bounds();
arm_memblock_init(mdesc); //保留内存块,即这些地址保留
/* Memory may have been removed so recalculate the bounds. */
adjust_lowmem_bounds(); //更新可用的内存
early_ioremap_reset();
paging_init(mdesc); //建立页表
request_standard_resources(mdesc);
if (mdesc->restart)
arm_pm_restart = mdesc->restart;
unflatten_device_tree(); //解析转化dtb到属性结构
arm_dt_init_cpu_maps();
psci_dt_init();
#ifdef CONFIG_SMP
if (is_smp()) {
if (!mdesc->smp_init || !mdesc->smp_init()) {
if (psci_smp_available())
smp_set_ops(&psci_smp_ops);
else if (mdesc->smp)
smp_set_ops(mdesc->smp);
}
smp_init_cpus();
smp_build_mpidr_hash();
}
#endif
if (!is_smp())
hyp_mode_check();
reserve_crashkernel();
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
handle_arch_irq = mdesc->handle_irq;
#endif
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
if (mdesc->init_early)
mdesc->init_early();
}
void __init arm_memblock_init(const struct machine_desc *mdesc)
{
/* Register the kernel text, kernel data and initrd with memblock. */
/* 把内核代码段,数据段,initrd段的物理地址保留,即不允许被覆盖或替换出去 */
memblock_reserve(__pa(KERNEL_START), KERNEL_END - KERNEL_START);
arm_initrd_init(); //initrd是内核运行的第一个进程,因为运行时文件系统还没启动,所以这里是内核自带的一个简化的inited进程的初始化
arm_mm_memblock_reserve(); //保留页表所在范围
/* reserve any platform specific memblock areas,平台相关的保留 */
if (mdesc->reserve)
mdesc->reserve();
early_init_fdt_reserve_self(); //设备树dtb文件所在内存的地址保留,不能被使用
early_init_fdt_scan_reserved_mem(); //此函数从早期分配器中获取内存以供设备独占使用
/* reserve memory for DMA contiguous allocations */
dma_contiguous_reserve(arm_dma_limit);
arm_memblock_steal_permitted = false;
memblock_dump_all();
}
/**
* early_init_fdt_reserve_self() - reserve the memory used by the FDT blob
*/
void __init early_init_fdt_reserve_self(void)
{
if (!initial_boot_params)
return;
/* Reserve the dtb region,保留区域的大小 */
early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
fdt_totalsize(initial_boot_params),
0);
}
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
if (nomap)
return memblock_remove(base, size);
return memblock_reserve(base, size);
}
//memblock_remove这个函数是把页表中,参数所在物理地址的页表项删除,即不不使用虚拟地址映射这段内存
//memblock_reserve 这个函数是把页表中,参数所在物理地址的页表项标记成保留,即在运行期间不能使用kmalloc之类函数使用
/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
*/
void __init unflatten_device_tree(void)
{
//从initial_boot_params地址开始存放设备树,解析后把设备树挂载of_root节点下面
__unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
unittest_unflatten_overlay_base();
}
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device-tree, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
* @blob: The blob to expand
* @dad: Parent device node
* @mynodes: The device_node tree created by the call
* @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree
* @detached: if true set OF_DETACHED on @mynodes
*
* Returns NULL on failure or the memory chunk containing the unflattened
* device tree on success.
*/
void *__unflatten_device_tree(const void *blob,
struct device_node *dad,
struct device_node **mynodes,
void *(*dt_alloc)(u64 size, u64 align),
bool detached)
{
int size;
void *mem;
pr_debug(" -> unflatten_device_tree()\n");
if (!blob) {
pr_debug("No device tree pointer\n");
return NULL;
}
//打印dtb头的部分提示信息
pr_debug("Unflattening device tree:\n");
pr_debug("magic: %08x\n", fdt_magic(blob));
pr_debug("size: %08x\n", fdt_totalsize(blob));
pr_debug("version: %08x\n", fdt_version(blob));
//检测头结点
if (fdt_check_header(blob)) {
pr_err("Invalid device tree blob header\n");
return NULL;
}
/* First pass, scan for size,扫描并计算设备节点+属性总的大小--这个计算很有意思 */
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
if (size < 0)
return NULL;
size = ALIGN(size, 4); //4字节对齐
pr_debug(" size is %d, allocating...\n", size);
/* Allocate memory for the expanded device tree,对设备树节点结构体,分配内存 */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
if (!mem)
return NULL;
memset(mem, 0, size); //清空内存
*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); //对最后一个地址存放一个模式,检查溢出
pr_debug(" unflattening %p...\n", mem);
/* Second pass, do actual unflattening,建立设备树节点 */
unflatten_dt_nodes(blob, mem, dad, mynodes);
if (be32_to_cpup(mem + size) != 0xdeadbeef) //确认覆盖最高一个4字节空间
pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
if (detached && mynodes) {
of_node_set_flag(*mynodes, OF_DETACHED);
pr_debug("unflattened tree is detached\n");
}
pr_debug(" <- unflatten_device_tree()\n");
return mem;
}
//在上面函数unflatten_dt_nodes函数调用了2次,一次设扫描节点,获取节点个数,一个是扫描并初始化节点
/**
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
* @blob: The parent device tree blob
* @mem: Memory chunk to use for allocating device nodes and properties
* @dad: Parent struct device_node
* @nodepp: The device_node tree created by the call,跟节点
*
* It returns the size of unflattened device tree or error code
*/
static int unflatten_dt_nodes(const void *blob,
void *mem,
struct device_node *dad,
struct device_node **nodepp)
{
struct device_node *root;
int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH 64
struct device_node *nps[FDT_MAX_DEPTH]; //设备树最大深度64
//注意这里传的mem为NULL时,dryrun 为1,表示干运行,不初始化,不建立父子关系,只做统计
void *base = mem;
bool dryrun = !base;
if (nodepp)
*nodepp = NULL;
/*
* We're unflattening device sub-tree if @dad is valid. There are
* possibly multiple nodes in the first level of depth. We need
* set @depth to 1 to make fdt_next_node() happy as it bails
* immediately when negative @depth is found. Otherwise, the device
* nodes except the first one won't be unflattened successfully.
*/
if (dad)
depth = initial_depth = 1;
root = dad;
nps[depth] = dad;
for (offset = 0;
offset >= 0 && depth >= initial_depth;
offset = fdt_next_node(blob, offset, &depth)) { //遍历dtb的所有节点
if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
continue;
if (!IS_ENABLED(CONFIG_OF_KOBJ) && //在sys文件系统也加入设备树节点
!of_fdt_device_is_available(blob, offset))
continue;
//分配并初始化设备树节点
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
return mem - base;
if (!dryrun && nodepp && !*nodepp)
*nodepp = nps[depth+1];
if (!dryrun && !root)
root = nps[depth+1];
}
if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
pr_err("Error %d processing FDT\n", offset);
return -EINVAL;
}
/*
* Reverse the child list. Some drivers assumes node order matches .dts
* node order
*/
if (!dryrun)
reverse_nodes(root); //建立节点之间的关系,同父节点
return mem - base;
}
static bool populate_node(const void *blob,
int offset,
void **mem,
struct device_node *dad,
struct device_node **pnp,
bool dryrun)
{
struct device_node *np;
const char *pathp;
unsigned int l, allocl;
pathp = fdt_get_name(blob, offset, &l); //获取设备树节点的名字
if (!pathp) {
*pnp = NULL;
return false;
}
allocl = ++l; //增加结束符
//从前面一次性为所有设备树节点申请的mem内存中分配一个device_node和名字长度
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np); //初始化刚分为的设备节点
np->full_name = fn = ((char *)np) + sizeof(*np); //设置设备节点名字所在地址
memcpy(fn, pathp, l); //把名字放到具体地址
//建立树形关系
if (dad != NULL) {
np->parent = dad;
np->sibling = dad->child;
dad->child = np;
}
}
//获取这个节点的属性,并进行初始化设置
populate_properties(blob, offset, mem, np, pathp, dryrun);
if (!dryrun) {
//设置属性"name"的值设置device_node->name,设置属性"device_type"的值设置device_node->type,
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
if (!np->name)
np->name = "";
if (!np->type)
np->type = "";
}
*pnp = np;
return true;
}
//初始化每个设备树节点的基本操作函数
static inline void of_node_init(struct device_node *node)
{
#if defined(CONFIG_OF_KOBJ)
kobject_init(&node->kobj, &of_node_ktype);
#endif
node->fwnode.ops = &of_fwnode_ops;
}
const struct fwnode_operations of_fwnode_ops = {
.get = of_fwnode_get,
.put = of_fwnode_put,
.device_is_available = of_fwnode_device_is_available,
.device_get_match_data = of_fwnode_device_get_match_data,
.property_present = of_fwnode_property_present,
.property_read_int_array = of_fwnode_property_read_int_array,
.property_read_string_array = of_fwnode_property_read_string_array,
.get_parent = of_fwnode_get_parent,
.get_next_child_node = of_fwnode_get_next_child_node,
.get_named_child_node = of_fwnode_get_named_child_node,
.get_reference_args = of_fwnode_get_reference_args,
.graph_get_next_endpoint = of_fwnode_graph_get_next_endpoint,
.graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint,
.graph_get_port_parent = of_fwnode_graph_get_port_parent,
.graph_parse_endpoint = of_fwnode_graph_parse_endpoint,
};
EXPORT_SYMBOL_GPL(of_fwnode_ops);
//分配属性,设置属性
static void populate_properties(const void *blob,
int offset,
void **mem,
struct device_node *np,
const char *nodename,
bool dryrun)
{
struct property *pp, **pprev = NULL;
int cur;
bool has_name = false;
//遍历np节点中的所有属性,并分配属性和初始化
pprev = &np->properties;
for (cur = fdt_first_property_offset(blob, offset);
cur >= 0;
cur = fdt_next_property_offset(blob, cur)) {
const __be32 *val;
const char *pname;
u32 sz;
//获取属性名字,value和value的大小
val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
if (!val) {
pr_warn("Cannot locate property at 0x%x\n", cur);
continue;
}
if (!pname) {
pr_warn("Cannot find property name at 0x%x\n", cur);
continue;
}
if (!strcmp(pname, "name")) //属性名字为name,这里做一下标记
has_name = true;
//从mem内存池分配一个属性结构体
pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property));
if (dryrun)
continue;
/* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties. If both
* appear and have different values, things
* will get weird. Don't do that.
*/
if (!strcmp(pname, "phandle") || //如果属性名字是phandle,那设置属性的phandle为一个整数值
!strcmp(pname, "linux,phandle")) {
if (!np->phandle)
np->phandle = be32_to_cpup(val);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff
*/
if (!strcmp(pname, "ibm,phandle"))
np->phandle = be32_to_cpup(val);
//初始化属性参数
pp->name = (char *)pname;
pp->length = sz; //属性值的字节数
pp->value = (__be32 *)val;
*pprev = pp;
pprev = &pp->next; //属性的同级连接
}
/* With version 0x10 we may not have the name property,
* recreate it here from the unit name if absent,16版本没name属性,这里就不看了,我们现在都是17版本的
*/
if (!has_name) {
const char *p = nodename, *ps = p, *pa = NULL;
int len;
while (*p) {
if ((*p) == '@')
pa = p;
else if ((*p) == '/')
ps = p + 1;
p++;
}
if (pa < ps)
pa = p;
len = (pa - ps) + 1;
pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
__alignof__(struct property));
if (!dryrun) {
pp->name = "name";
pp->length = len;
pp->value = pp + 1;
*pprev = pp;
pprev = &pp->next;
memcpy(pp->value, ps, len - 1);
((char *)pp->value)[len - 1] = 0;
pr_debug("fixed up name for %s -> %s\n",
nodename, (char *)pp->value);
}
}
if (!dryrun)
*pprev = NULL;
}
设备树总大小的计算分析:
/* First pass, scan for size */
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
//主要这里mem是NULL !!!!!!!!!!!!!
/**
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
* @blob: The parent device tree blob
* @mem: Memory chunk to use for allocating device nodes and properties
* @dad: Parent struct device_node
* @nodepp: The device_node tree created by the call
*
* It returns the size of unflattened device tree or error code
*/
static int unflatten_dt_nodes(const void *blob,
void *mem,
struct device_node *dad,
struct device_node **nodepp)
{
struct device_node *root;
int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH 64
struct device_node *nps[FDT_MAX_DEPTH];
void *base = mem; //即base也为 NULL
bool dryrun = !base;
if (nodepp)
*nodepp = NULL;
/*
* We're unflattening device sub-tree if @dad is valid. There are
* possibly multiple nodes in the first level of depth. We need
* set @depth to 1 to make fdt_next_node() happy as it bails
* immediately when negative @depth is found. Otherwise, the device
* nodes except the first one won't be unflattened successfully.
*/
if (dad)
depth = initial_depth = 1;
root = dad;
nps[depth] = dad;
for (offset = 0;
offset >= 0 && depth >= initial_depth;
offset = fdt_next_node(blob, offset, &depth)) {
if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
continue;
if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
!of_fdt_device_is_available(blob, offset))
continue;
//主要就是这句统计节点的大小,这里mem是取得NULL的地址(0的地址当然也是NULL)
if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
return mem - base;
if (!dryrun && nodepp && !*nodepp)
*nodepp = nps[depth+1];
if (!dryrun && !root)
root = nps[depth+1];
}
if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
pr_err("Error %d processing FDT\n", offset);
return -EINVAL;
}
/*
* Reverse the child list. Some drivers assumes node order matches .dts
* node order
*/
if (!dryrun)
reverse_nodes(root);
return mem - base;
}
//这里以及是0的地址了
static bool populate_node(const void *blob,
int offset,
void **mem,
struct device_node *dad,
struct device_node **pnp,
bool dryrun)
{
struct device_node *np;
const char *pathp;
unsigned int l, allocl;
pathp = fdt_get_name(blob, offset, &l); //名称长度
if (!pathp) {
*pnp = NULL;
return false;
}
allocl = ++l; //名称增加结束符
//具体的增加一个节点的大小
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
memcpy(fn, pathp, l);
if (dad != NULL) {
np->parent = dad;
np->sibling = dad->child;
dad->child = np;
}
}
populate_properties(blob, offset, mem, np, pathp, dryrun);
if (!dryrun) {
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
if (!np->name)
np->name = "";
if (!np->type)
np->type = "";
}
*pnp = np;
return true;
}
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
static void *unflatten_dt_alloc(void **mem, unsigned long size,
unsigned long align)
{
void *res;
*mem = PTR_ALIGN(*mem, align); //这里的mem是&NULL,即*mem第一次进来是0,对齐后还是0。所以*mem = aa代表在0地址赋值
res = *mem;
*mem += size; //用0地址存放统计的长度
return res; //返回值是从0地址的偏移长度
}
所以这里是在0地址的内存中存放每次统计的数据长度。
最后总结一下设备树的节点形成过程。
setup_arch(&command_line);
mdesc = setup_machine_fdt(__atags_pointer);
arm_memblock_init(mdesc); //保存设备树所在内存区域,以及其它保留的内存区域
unflatten_device_tree(); //解析设备树,组织设备节点等
__unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
/* First pass, scan for size,扫描设备节点,统计总的设备树需要的内存大小 */
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
/* Allocate memory for the expanded device tree,申请内存 */
mem = dt_alloc(size + 4, __alignof__(struct device_node));
/* Second pass, do actual unflattening,初始化并建立组织树形关系 */
unflatten_dt_nodes(blob, mem, dad, mynodes);