unflatten_device_tree()

<kernel v5.0>

디바이스 트리(FDT) -> Expanded 포맷으로 변환

  • device_node와 property 구조체를 사용하여 트리 구조로 각 노드와 속성을 연결한다.
  • 기존에 사용하던 DTB 바이너리들도 문자열등을 그대로 사용하므로 삭제되지 않고 유지된다.

 

노드 명

다음 디바이스 트리를 보고 3 가지 노드 명 분류를 알아본다.

  • Full path 노드명
  • Compact 노드명
  • Alias 명

arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi

/ {
        compatible = "brcm,ns2";
        interrupt-parent = <&gic>;
        #address-cells = <2>;
        #size-cells = <2>;

        cpus {
                #address-cells = <2>;
                #size-cells = <0>;

                A57_0: cpu@0 {
                        device_type = "cpu";
                        compatible = "arm,cortex-a57", "arm,armv8";
                        reg = <0 0>;
                        enable-method = "psci";
                        next-level-cache = <&CLUSTER0_L2>;
                };
Full path 노드명
  • 디렉토리 구조로 표현한다.
    • /cpus/cpu@0
Compact 노드명
Alias 명
  • Compact 노드명 앞에 alias 명을 둘 수 있다.
    • A57_0

 


FDT -> Expanded Format으로 변환 수행

워드(4바이트) 단위로 정렬된 DTB를 unflatten_device_tree( ) 함수를 통해 unflatten 과정으로 변환하면 각 노드는 device_node 구조체로 변환된다. 전역 of_root 노드가 루트 노드를 가리킨다. 각 노드에 있는 속성들도 property 구조체로 변환되어 해당 노드에 등록된다. 이렇게 바이너리 형태로 존재하다가 device_node 구조체와 property 구조체를 할당받아 트리 형태로 구성된 것을 확장 포맷(expanded format)이라고 부른다.
노드와 속성은 of_로 시작되는 API에 의해 관리되어 사용한다. unflatten된 구조체들은 슬랩 캐시 할당자에서 할당받아 만들어진다.

 

unflatten_device_tree()

drivers/of/fdt.c

/**
 * 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();
}

DTB를 확장 포맷으로 변환하고 관련 전역 변수가 적절한 노드를 가리키도록 초기화한다.

  • 코드 라인 3~4에서 4바이트 단위의 바이너리로 구성된 DTB를 파싱하여 확장 포맷으로 변환한 후 of_root 전역 변수가 가리키게 한다.
  • 코드 라인 7에서 전역 aliases_lookup 리스트에 alias_prop들을 추가한다.
    • 전역 변수 of_aliases가 “/aliases” 노드를 가리키도록 설정한다.
    • 전역 변수 of_chosen이 “/chosen” 노드를 가리키도록 설정한다.
    • 전역 변수 of_stdout을 “/chosen” 노드의 “stdout-path” 속성 값에 대응하는 노드로 설정한다.

 

다음 그림과 같이 주요 노드들은 of_로 시작되는 전역 변수가 가리키는 것을 보여준다.

of_alias_scan-1d

  • of_stdout
    • /soc/uart@7e201000 노드를 가리킨다.
    • /chosen 노드의 stdout-path가 가리키는 노드가 출력 디바이스로 사용된다.
  •  of_aliases
    • /aliases 노드를 가리킨다.
  •  aliases_lookup
    • /aliases 노드에 담겨있는 모든 속성들을 alias_prop 구조체 형태로 변환한 후 그 들이 연결되어 있다.
  •  of_root
    • 루트 노드를 가리킨다.
  •  of_chosen
    • /chosen 노드를 가리킨다.
  •  of_stdout_option
    • 출력 노드명에 사용된 옵션(‘:’문자로 시작하는) 문자열이 저장된다.

 

__unflatten_device_tree()

drivers/of/fdt.c

/**
 * __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;
        }

        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 */
        unflatten_dt_nodes(blob, mem, dad, mynodes);
        if (be32_to_cpup(mem + size) != 0xdeadbeef)
                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;
}

DTB를 파싱하여 확장 포맷으로 변환한 후 of_root 전역 변수가 가리키게 한다.

  • 코드 라인 22~25에서 DTB의 첫 부분에 위치한 헤더에서 첫 워드를 통해 DTB 데이터 여부를 체크한다. 추가로 지원 가능한 DTB 버전이 0x02 ~ 0x11인지 확인하여 체크하고, 다른 경우 에러를 출력하고 처리를 하지 않는다.
  • 코드 라인 28~30에서 가장 마지막 인자 dryrun을 true로 전달하여 실제 컨버팅 동작을 하지 않고 DTB를 unflatten할 때 만들어질 device_node 구조체들과 properties 구조체들의 구성에 필요한 전체 크기의 크기만을 구한다. 그리고 최종 산출된 크기를 워드(4바이트) 단위로 정렬한다.
  • 코드 라인 36~38에서 인자로 전달받은 (*dt_alloc) 함수를 통해 메모리를 할당받는다. 할당 시의 크기로 위에서 산출한 크기에 추가로 끝부분을 나타내기 위한 4바이트만큼을 추가한다. 또한 정렬 단위는 시스템의 최소 정렬 단위가 주어지는데 ARM, ARM64는 4바이트다.
  • 코드 라인 42에서 할당된 메모리의 마지막 4바이트에 0xdeadbeef를 저장한다. 이 값은 경계 침범을 모니터링하기 위해 사용한다.
  • 코드 라인 47에서  DTB를 파싱하여 device_node, property 구조체 배열로 변환한다.
  • 코드 라인 48~50에서 할당된 메모리의 끝에 설치한 경계 침범 값이 오염되었는지 확인하여 경고 출력을 한다.

 

다음 그림은 바이너리 형태의 DTB를 unflatten하여 확장 포맷으로 변환하는 모습을 보여준다.

unflatten_device_tree-1a

 

unflatten_dt_node()

drivers/of/fdt.c

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

                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;
}

FDT 형태의 디바이스 트리를 파싱하고 확장(expand)하여 디바이스 노드로 변환한다. @blob에 디바이스 트리(FDT)의 시작 주소를 지정하고, 확장된 디바이스 노드가 저장될 @mem을 지정한다. @dad에는 부모 디바이스 노드를 지정하고, 출력 인자 @mynode는 이 함수가 호출되어 생성될 디바이스 노드이다. 처음 호출될 때 @dad에는 null, @mynode에는 루트 디바이스 노드를 가리키는 &of_root가 주어진다.

  • 코드 라인 13~14에서 먼저 출력 인자 @nodepp에 null을 대입한다.
  • 코드 라인 23~24에서 @dad가 지정된 경우에 한해 depth와 초기 depth를 1부터 시작한다.
  • 코드 라인 29~33에서 다음(next) 노드를 읽고 offset을 알아온다. 이 때 읽은 노드의 depth도 알아온다.
  • 코드 라인 35~37에서 노드가  enable 또는 ok 상태가 아닌 경우는 skip 한다.
  • 코드 라인 39~41에서 노드를 활성화한다. 지금까지 변환한 사이즈를 반환한다.
  • 코드 라인 43~44에서 2nd pass에서 @nodepp에 현재 노드를 지정한다. 단 한 번만 지정한다.
  • 코드 라인 45~46에서 2nd pass의 루트가 아니고 아직 루트가 지정되지 않은 경우 현재 노드를 루트로 지정한다.
  • 코드 라인 49~52에서 노드 파싱에 문제가 있는 경우 에러를 반환한다.
  • 코드 라인 58~59에서 2nd pass인 경우 노드를 reverse 한다.
  • 코드 라인 61에서 지금까지 변환한 사이즈를 반환한다.

 


디바이스 노드와 속성 활성화

populate_node()

drivers/of/fdt.c

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);
                if (!np->name)
                        np->name = "<NULL>";
        }

        *pnp = np;
        return true;
}

노드를 파싱하여 디바이스 노드로 변환한다. 성공 시 true를 반환한다.

  • 코드 라인 12~16에서 노드 명이 null인 경우 출력 인자 @pnp에 null을 대입한 후 더 이상 처리하지 않고 false를 반환한다.
  • 코드 라인 20~21에서 @mem에서 노드가 저장될 영역을 확보한다.
  • 코드 라인 22~34에서 2nd pass인 경우 노드를 초기화 하고, 노드 명을 지정한 후 노드 간의 관계를 연결한다.
  • 코드 라인 36~41에서 속성을 파싱하여 속성 정보로 변환한다. 속성 이름이 없는 경우 “<NULL>” 문자열을 이름으로 지정한다.
  • 코드 라인 43~44에서 출력 인자 @pnp에 디바이스 노드를 지정하고, true를 반환한다.

 

populate_properties()

drivers/of/fdt.c

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;
        }

 

 

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

 

 

아래 그림은 노드명이 full path name으로 바뀌어 저장되는 과정을 설명하였다.

unflatten_dt_node-3a

 

아래 그림은 a@1000 노드의 서브 노드로 a2 노드가 추가될 때의 상황이다.

unflatten_dt_node-2c

 

아래 그림은 cpu@0 노드에 속한 속성들이 연결된 모습을 보여준다.

unflatten_dt_node-4a

 

다음 그림은 속성명에 name이 없는 경우 마지막에 추가되는 모습을 보여준다.

unflatten_dt_node-5c

 

child 노드가 있는 경우 DTB 순서대로 만들기 위해 각 child 노드를 reverse 한다.

unflatten_dt_node-1c

 

unflatten_dt_alloc()

drivers/of/fdt.c

static void *unflatten_dt_alloc(void **mem, unsigned long size,
                                       unsigned long align)
{
        void *res;

        *mem = PTR_ALIGN(*mem, align);
        res = *mem;
        *mem += size;

        return res; 
}

mem 값을 align 단위로 round up 하고 리턴하며 입출력 인수 mem 값은 size만큼 증가시킨다.

 

early_init_dt_alloc_memory_arch()

drivers/of/fdt.c

static void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
        return memblock_alloc(size, align);
}

align 단위로 size 만큼의 공간을 memblock으로 부터 할당 받고 그 가상 주소를 리턴한다.

 


alias 노드 스캔

of_alias_scan()

drivers/of/base.c

/**
 * of_alias_scan - Scan all properties of the 'aliases' node
 *
 * The function scans all the properties of the 'aliases' node and populates
 * the global lookup table with the properties.  It returns the
 * number of alias properties found, or an error code in case of failure.
 *
 * @dt_alloc:   An allocator that provides a virtual address to memory
 *              for storing the resulting tree
 */
void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
        struct property *pp;

        of_aliases = of_find_node_by_path("/aliases");
        of_chosen = of_find_node_by_path("/chosen");
        if (of_chosen == NULL)
                of_chosen = of_find_node_by_path("/chosen@0");

        if (of_chosen) {
                /* linux,stdout-path and /aliases/stdout are for legacy compatibility */
                const char *name = NULL;

                if (of_property_read_string(of_chosen, "stdout-path", &name))
                        of_property_read_string(of_chosen, "linux,stdout-path",
                                                &name);
                if (IS_ENABLED(CONFIG_PPC) && !name)
                        of_property_read_string(of_aliases, "stdout", &name);
                if (name)
                        of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
        }

        if (!of_aliases)
                return;

        for_each_property_of_node(of_aliases, pp) {
                const char *start = pp->name;
                const char *end = start + strlen(start);
                struct device_node *np;
                struct alias_prop *ap;
                int id, len;

                /* Skip those we do not want to proceed */
                if (!strcmp(pp->name, "name") ||
                    !strcmp(pp->name, "phandle") ||
                    !strcmp(pp->name, "linux,phandle"))
                        continue;

                np = of_find_node_by_path(pp->value);
                if (!np)
                        continue;

                /* walk the alias backwards to extract the id and work out
                 * the 'stem' string */
                while (isdigit(*(end-1)) && end > start)
                        end--;
                len = end - start;

                if (kstrtoint(end, 10, &id) < 0)
                        continue;

                /* Allocate an alias_prop with enough space for the stem */
                ap = dt_alloc(sizeof(*ap) + len + 1, __alignof__(*ap));
                if (!ap)
                        continue;
                memset(ap, 0, sizeof(*ap) + len + 1);
                ap->alias = start;
                of_alias_add(ap, np, id, start, len);
        }
}

“/chosen” 노드를 검색하여 전역 변수 of_stdout을 “stdout-path”에 연결된 노드로 설정한다. 그리고 “/aliases” 노드의 속성 중 “name” 및 “phandle”을 찾아 aliases_prop 구조체로 구성하여 리스트 aliases_lookup에 추가한다.

  • 코드 라인 5에서 “/aliases” 노드를 찾아 전역 of_aliases에 설정한다.
  • 코드 라인 6~8에서 “/chosen” 노드를 찾아 전역 of_chosen에 설정한다. 만일 of_chosen 노드가 발견되지 않으면 “/chosen@0”으로 다시 한번 검색한다.
  • 코드 라인 10~18에서 “/chosen” 노드가 발견된 경우 of_chosen 노드에 있는 속성들에서 “stdout-path” 속성명으로 검색하여 찾은 속성 value 값을 name에 저장한다. 검색 결과가 없으면 레거시 호환을 위해 “linux,stdout-path” 및 “stdout” 속성명으로도 검색한다.
  • 코드 라인 19~20에서 name(of_chosen 노드에서 검색한 “stdout-path” 속성의 value 값)으로 노드를 검색하고 전역 of_stdout_options에는 name 문자열에 옵션(‘:’ 문자로 시작하는 문자열) 값이 있다면 저장한다.
  • 코드 라인 23~24에서 등록된 aliases가 없다면 함수를 빠져나간다.
  • 코드 라인 26~31에서 of_aliases에 속한 모든 속성에 대해 루프를 돈다.
  • 코드 라인 34~37에서 속성명이 “name”, “phandle”, “linux,phandle”인 경우에는 스킵한다.
  • 코드 라인 39~41에서 속성의 value 값으로 노드를 검색하고 노드가 발견되지 않으면 스킵한다.
  • 코드 라인 45~46에서 속성 값에 있는 노드명이 숫자가 있다면 숫자가 시작되는 위치를 end에 설정한다.
    • 예 “/abc/def@1000”
      • end = ‘1’ 문자를 가리킴
  • 코드 라인 47에서 len에 노드명의 마지막에 있는 주소를 제외한 ‘@’ 문자까지의 노드명 길이가 담긴다.
    • 예) “/abc/def@1000”
      • len = 9
    • 예) “/abc”
      • len = 4
  • 코드 라인 49~50에서 주소에 대한 문자열을 10진수로 변환하여 id 변수에 저장을 하는데, 에러인 경우에는 스킵한다.
  • 코드 라인 53~55에서 alias_prop 구조체 크기 + len + 1 크기만큼 memblock을 할당한다. 만일 할당이 실패하면 스킵한다.
  • 코드 라인 56에서 할당받은 메모리를 0으로 초기화한다.
  • 코드 라인 57에서 alias가 속성명을 가리키게 한다. /alias 노드의 각 속성명은 ‘ / ’ 문자로 시작하지 않는다.
    • 예 name = “uart0”
  • 코드 라인 58에서 np, id, stem 값을 저장하고 전역 aliases_lookup 리스트에 추가한다. stem 문자열은 속성명에서 주소 부분을 제외했다.
    • 예) 속성 데이터 = “/soc/uart@7e201000”
      • stem = “/soc/uart@”

구조체

device_node 구조체

include/linux/of.h

struct device_node {
        const char *name;
        phandle phandle;
        const char *full_name;
        struct fwnode_handle fwnode;

        struct  property *properties;
        struct  property *deadprops;    /* removed properties */
        struct  device_node *parent;
        struct  device_node *child;
        struct  device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
        struct  kobject kobj;
#endif
        unsigned long _flags;
        void    *data;
#if defined(CONFIG_SPARC)
        unsigned int unique_id;
        struct of_irq_controller *irq_trans;
#endif
};

 

property 구조체

include/linux/of.h

struct property {
        char    *name;
        int     length;
        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
};

 

alias_prop 구조체

drivers/of/of_private.h

/**
 * struct alias_prop - Alias property in 'aliases' node
 * @link:       List node to link the structure in aliases_lookup list
 * @alias:      Alias property name
 * @np:         Pointer to device_node that the alias stands for
 * @id:         Index value from end of alias name
 * @stem:       Alias string without the index
 *
 * The structure represents one alias property of 'aliases' node as
 * an entry in aliases_lookup list.
 */
struct alias_prop {
        struct list_head link;
        const char *alias;
        struct device_node *np;
        int id;
        char stem[0];
};
  • link
    • 링크드 리스트
  • alias
    • alias 속성명
      • 예) “chosen”, “uart0”
  • np
    • 노드(device_node)를 가리킨다.
  • id
    • 노드의 메모리 또는 포트가 사용하는 주소
      • 예) 노드명이 serial@12000 인 경우 id=12000
  • stem
    • index(id)를 제외한 full path 노드명
      • 예) “/soc/uart@”

 

각 구조체의 Memblock 할당 시 사이즈

  • device_node
    • 생성할 때 마다 full path 노드명 공간이 추가 할당된다.
    • name, type, data 등은 기존 DTB에 있는 문자열이나 값을 가리킨다.
  • property
    • 생성할 때 마다 property 사이즈만큼 공간이 할당된다. 그러나 DTB에 없는 name 속성을 추가 생성해야 하는 경우에는 property 속성 이외에도 주소제외 노드명 공간을 추가 할당한다.
  • alias_prop
    • 생성할 때 마다 주소제외 노드명 공간이 추가 할당되고 이 공간은 stem 문자열이 사용하는 공간이다.

unflatten_device_tree-2

 

참고

 

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다