如何查看内核 device tree

如何查看内核 device tree,第1张

你好,查看方法如下:

内核初始化的时候,dtb被转换成device_node的树状结构,以便后续 *** 作。具体的代码流程如下:

start_kernel->setup_arch->unflatten_device_tree

void __init unflatten_device_tree(void)

{

__unflatten_device_tree(initial_boot_params, &of_allnodes,

early_init_dt_alloc_memory_arch)

/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */

of_alias_scan(early_init_dt_alloc_memory_arch)

}12345678

将dtb展开,并将其组成成一个树状结构,主要功能在__unflatten_device_tree函数中实现,具体代码如下:

static void __unflatten_device_tree(struct boot_param_header *blob, //dtb在内存中的虚拟地址

struct device_node **mynodes, //一个全局的指针,后续可以通过这个指针变量dtb所有的节点

void * (*dt_alloc)(u64 size, u64 align)) //内存分配回调函数

{

unsigned long size

void *start, *mem

struct device_node **allnextp = mynodes

if (!blob) {

pr_debug("No device tree pointer\n")

return

}

...

if (be32_to_cpu(blob->magic) != OF_DT_HEADER) {

pr_err("Invalid device tree blob header\n")

return

}

/* First pass, scan for size */

start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct)

size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0)

size = ALIGN(size, 4)

pr_debug(" size is %lx, allocating...\n", size)

/* Allocate memory for the expanded device tree */

mem = dt_alloc(size + 4, __alignof__(struct device_node))

memset(mem, 0, size)

*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef)

pr_debug(" unflattening %p...\n", mem)

/* Second pass, do actual unflattening */

start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct)

unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0) //重点在这里

if (be32_to_cpup(start) != OF_DT_END)

pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start))

if (be32_to_cpup(mem + size) != 0xdeadbeef)

pr_warning("End of tree marker overwritten: %08x\n",

be32_to_cpup(mem + size))

*allnextp = NULL

pr_debug(" <- unflatten_device_tree()\n")

}

12345678910111213141516171819202122232425262728293031323334353637383940414243444546

unflatten_dt_node函数真正完成了解析dtb的任务,首先找到dtb的根节点,并创建一个struct device_node ,然后把这个根节点对应的struct device_node 赋值给all_nodes全局变量。再通过递归的方法遍历根节点下面的子节点,最终创建一个树结构。

unflatten_dt_node具体代码如下:

static void * unflatten_dt_node(struct boot_param_header *blob,

void *mem,

void **p,

struct device_node *dad,

struct device_node ***allnextpp,

unsigned long fpsize)

{

struct device_node *np

struct property *pp, **prev_pp = NULL

char *pathp

u32 tag

unsigned int l, allocl

int has_name = 0

int new_format = 0

tag = be32_to_cpup(*p)

if (tag != OF_DT_BEGIN_NODE) {

pr_err("Weird tag at start of node: %x\n", tag)

return mem

}

*p += 4

pathp = *p

l = allocl = strlen(pathp) + 1

*p = PTR_ALIGN(*p + l, 4)

/* version 0x10 has a more compact unit name here instead of the full

* path. we accumulate the full path size using "fpsize", we'll rebuild

* it later. We detect this because the first character of the name is

* not '/'.

*/

if ((*pathp) != '/') {

new_format = 1

if (fpsize == 0) {

/* root node: special case. fpsize accounts for path

* plus terminating zero. root node only has '/', so

* fpsize should be 2, but we want to avoid the first

* level nodes to have two '/' so we use fpsize 1 here

*/

fpsize = 1

allocl = 2

l = 1

*pathp = '\0'

} else {

/* account for '/' and path size minus terminal 0

* already in 'l'

*/

fpsize += l

allocl = fpsize

}

}

np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,

__alignof__(struct device_node))

if (allnextpp) {

char *fn

np->full_name = fn = ((char *)np) + sizeof(*np)

if (new_format) {

/* rebuild full path for new format */

if (dad &&dad->parent) {

strcpy(fn, dad->full_name)

#ifdef DEBUG

if ((strlen(fn) + l + 1) != allocl) {

pr_debug("%s: p: %d, l: %d, a: %d\n",

pathp, (int)strlen(fn),

l, allocl)

}

#endif

fn += strlen(fn)

}

*(fn++) = '/'

}

memcpy(fn, pathp, l)

prev_pp = &np->properties

**allnextpp = np

*allnextpp = &np->allnext

if (dad != NULL) {

np->parent = dad

/* we temporarily use the next field as `last_child'*/

if (dad->next == NULL)

dad->child = np

else

dad->next->sibling = np

dad->next = np

}

kref_init(&np->kref)

}

/* process properties */

while (1) {

u32 sz, noff

char *pname

tag = be32_to_cpup(*p)

if (tag == OF_DT_NOP) {

*p += 4

continue

}

if (tag != OF_DT_PROP)

break

*p += 4

sz = be32_to_cpup(*p)

noff = be32_to_cpup(*p + 4)

*p += 8

if (be32_to_cpu(blob->version) <0x10)

*p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4)

pname = of_fdt_get_string(blob, noff)

if (pname == NULL) {

pr_info("Can't find property name in list !\n")

break

}

if (strcmp(pname, "name") == 0)

has_name = 1

l = strlen(pname) + 1

pp = unflatten_dt_alloc(&mem, sizeof(struct property),

__alignof__(struct property))

if (allnextpp) {

/* 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") == 0) ||

(strcmp(pname, "linux,phandle") == 0)) {

if (np->phandle == 0)

np->phandle = be32_to_cpup((__be32*)*p)

}

/* And we process the "ibm,phandle" property

* used in pSeries dynamic device tree

* stuff */

if (strcmp(pname, "ibm,phandle") == 0)

np->phandle = be32_to_cpup((__be32 *)*p)

pp->name = pname

pp->length = sz

pp->value = *p

*prev_pp = pp

prev_pp = &pp->next

}

*p = PTR_ALIGN((*p) + sz, 4)

}

/* with version 0x10 we may not have the name property, recreate

* it here from the unit name if absent

*/

if (!has_name) {

char *p1 = pathp, *ps = pathp, *pa = NULL

int sz

while (*p1) {

if ((*p1) == '@')

pa = p1

if ((*p1) == '/')

ps = p1 + 1

p1++

}

if (pa <ps)

pa = p1

sz = (pa - ps) + 1

pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,

__alignof__(struct property))

if (allnextpp) {

pp->name = "name"

pp->length = sz

pp->value = pp + 1

*prev_pp = pp

prev_pp = &pp->next

memcpy(pp->value, ps, sz - 1)

((char *)pp->value)[sz - 1] = 0

pr_debug("fixed up name for %s ->%s\n", pathp,

(char *)pp->value)

}

}

if (allnextpp) {

*prev_pp = NULL

np->name = of_get_property(np, "name", NULL)

np->type = of_get_property(np, "device_type", NULL)

if (!np->name)

np->name = "<NULL>"

if (!np->type)

np->type = "<NULL>"

}

while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {

if (tag == OF_DT_NOP)

*p += 4

else

mem = unflatten_dt_node(blob, mem, p, np, allnextpp,

fpsize)

tag = be32_to_cpup(*p)

}

if (tag != OF_DT_END_NODE) {

pr_err("Weird tag at end of node: %x\n", tag)

return mem

}

*p += 4

return mem

}

1、kernel最早加入设备树的历史得追溯到v2.6.23,从这个版本开始,在driver目录下多了一个of目录。当然,此时只是引入一些新想法而已。这距离linus大怒说出(2011年3月17日):this whole ARM thing is a f*cking pain in the ass,还早着。

2、于是从2011年3月开始,内核在PowerPC、ARM等体系里正式打算使用设备树。以ARM体系为例,加入设备树的版本就是v3.1,可以在arch/arm/boot/目录下看到dts目录的出现。

在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。读者有兴趣可以统计下常见的s3c2410、s3c6410等板级目录,代码量在数万行。

为了改变这种局面,于是PowerPC等其他体系架构下已经使用的Flattened Device Tree(FDT)进入ARM社区的视野。Device Tree是一种描述硬件的数据结构,它起源于 OpenFirmware (OF)。在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/7614962.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-07
下一篇 2023-04-07

发表评论

登录后才能评论

评论列表(0条)

保存