unflatten_device_tree -> __unflatten_device_tree 분석
device tree 파일을 자료구조에 넣는 함수이다
/**
* 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)
{
__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();
}
* 3 devicd tree를 파싱하여 자료구조에 넣은 후 of_root와 연결한다
* 6 device tree에서 alias로 작성된 내용을 자료구조에 넣는다
* 8 device tree에서 overlay 관련된 내용을 처리한다
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
* @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
*
* 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.
*
* Return: 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;
int ret;
if (mynodes)
*mynodes = NULL;
pr_debug(" -> unflatten_device_tree()\n");
if (!blob) {
pr_debug("No device tree pointer\n");
return NULL;
}
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);
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 */
ret = unflatten_dt_nodes(blob, mem, dad, mynodes);
if (be32_to_cpup(mem + size) != 0xdeadbeef)
pr_warn("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
if (ret <= 0)
return NULL;
if (detached && mynodes && *mynodes) {
of_node_set_flag(*mynodes, OF_DETACHED);
pr_debug("unflattened tree is detached\n");
}
pr_debug(" <- unflatten_device_tree()\n");
return mem;
}
* 26~29 device tree 파일이 정상적인지 확인한다
* 32 device tree의 size를 알아낸다
* 40 memblock에서 device tree size 만큼 메모리를 할당한다
* 44 할당받은 영역을 0으로 초기화한다
* 46 memblock으로 부터 할당맏은 메모리 마지막에 magic number를 write 한다
* 51 device tree의 값을 자료구조에 삽입한다
* 52 삽입한 후 magic number가 깨졌는지 확인한다
* 66 memblock에서 할당받은 메모리를 return한다
위 함수에서 unflatten_dt_nodes 함수가 2번 호출된다
unflatten_dt_nodes의 2번째 인자 mem으로 인해 device tree의 size만 가져올지 자료구조에 삽입할지를 결정한다.
/**
* 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
*
* Return: 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;
bool dryrun = !base;
int ret;
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 - 1))
continue;
if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
!of_fdt_device_is_available(blob, offset))
continue;
ret = populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun);
if (ret < 0)
return ret;
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;
}
* 24 dad 는 __unflatten_device_tree() 의 2번재 인자인데 항상 NULL이므로 depth, initial_depth는 1이다
* 30~49 device tree node를 계속 순회 하면서 offset을 가져온다
* 33 depth가 FDT_MAX_DEPTH(64)-1 보다 깊다면 자료구조 삽입하지 않는다
* 37 device tree node의 'state' 문자를 알아와 "ok" or "okay"로 작성 되었을때 true를 반환하여 자료구조에 삽입 할지 결정한다
* 40 node를 자료구조에 삽입한다
* 45 nodepp가 root node 다음 depth를 가리킨게 한다. (root node populate_node가 끝나고 한번만 호출한다)
nodepp -> of_root(전역변수)
device_node nps[] 배열은
* 47 dryrun이 아니면서 root가 NULL이라면 다음 depth를 가리키도록 한다. (root node populate_node가 끝나고 한번만 호출한다)
* 61 node를 반전 시킨다
dryrun은 호출 __unflatten_device_tree() 의 2번재 호출 시 '1'로 set 된다
Parent
/ | \
A B C
/ \ / \ |
D E F G H
함수 호출 후
Parent
/ | \
C B A
| / \ / \
H G F E D
static int 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;
int len;
pathp = fdt_get_name(blob, offset, &len);
if (!pathp) {
*pnp = NULL;
return len;
}
len++;
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + len,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
memcpy(fn, pathp, len);
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);
if (!np->name)
np->name = "<NULL>";
}
*pnp = np;
return 0;
}
* 12 device tree node의 name을 가져온다
* 13~16 name이 없다면 종료한다
* 20 나중에 device tree size만큼 메모리 할당할때 사용 할 size(mem)를 위해 device_node + name size 만큼 증가시킨다
* 22~34 __unflatten_device_tree() 2번째 호출 시 node 생성을 위한 작업
* 24 node init한다
* 25 node의 full_name을 복사한다
* 29~33 node를 부모와 연결한다
* 36 property node에 대한 정보를 parsing 한다
* 37~41 name을 가져오면 nodename을 가져온다(populate_properties 함수 마지막에 마지막 struct property 뒤에 nodename을 작성 하였다) , name이 없다면 NULL로 저장한다
* 43 새로 생성한 node를 pnp에 넣는다.
line 35 코드를 그림을 그리면 아래와 같다
struct device_node 뒤에 full_name을 write후 device_node->full_name에서 가리키게 한다.
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;
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;
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"))
has_name = true;
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") ||
!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;
}
* 13~15 node의 첫번째 property의 offset을 찾고 다음 property offset을 계속 순회한다
* 20 property 의 인자값에 값을 채우고 property data를 return한다
cur : property 위치
pname : property name
sz : property size
예를들면
compatible=arm,cortex-a53
cur : compatible 의 시작위치('c')
pname : arm,cortex-a53
sz : 'arm,cortex-a53' size
* 31 property를 모두 순회하고 맨 마지막에는 pname이 'name'을 return한다.
* 34 메모리 할당할 size를 만드는 mem변수에 struct property 만큼 더한다
* 36 처음 unflatten_dt_nodes 함수 할 때는 size만 확인하여 더이상 실행하지 않고 다음 property를 순회한다
* 45 pname이 phandle or linux,phandle 일 경우 값을 device_node에 저장한다
phandle = <0x00000035>;
* 55 pname이 ibm,phandle일 경우 값을 device_node에 저장한다
* 58~60 property에 name, size, val값을 저장한다
* 61~62 생성한 property를 device_node에 연결한다
마지막 property는 마지막 next에 연결된다
/* With version 0x10 we may not have the name property,
* recreate it here from the unit name if absent
*/
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;
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);
}
}
}
* 4 property에 name이 없으면 실행한다
* 5 node의 name(nodenoae)을 가져온다
* 8~14 nodename 문자열을 하나씩 확인하면서 '@' 와 '/'+1 의 위치를 알아낸다
pa : @ 위치
ps : /+1 위치
* 16 nodename에 '@'가 '/'보다 앞에 있다면
예를 들면 아래처럼 된다
ps pa p
apple@2000'NULL' -> '@' 앞글자 size를 알아낸다 : apple
ps p,pa , pa=NULL -> pa=p, because if(pa<ps) pa=p;
apple/banana'NULL' -> '/' 뒤에 문자 size를 안아낸다 : banana
ps pa p
apple/banana@2000'NULL' -> '/' 와 '@' 사이에 문자 size를 알아낸다 : banana
* 19 struct property + name 길이만큼 할당 한다
* 21~30 할당받은 영역에 name, len(name size)값을 넣는다, value에 struct property 끝 위치를 가리키고 거기에 nodename에서 구한 문자열을 넣는다.
'populate_node' 함수에서 마지막에 그려놓은 그림과 유사하게 nodename을 저장한다
참고자료