DTB (fdt API)

DTB 관련 API는 다음과 같이 구분된다.

  • fdt로 시작하는 API는 DTB(Device Tree Blob)를 대상으로 동작한다.
  • of로 시작하는 API는 링크드 리스트 구조의 expanded format으로 변환된 Device Tree를 대상으로 동작한다.
    • DTB는 unflatten_device_tree() 함수에의해 변환된다.

 

fdt_get_name()

scripts/dtc/libfdt/fdt_ro.c

const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
        const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
        int err;

        if (((err = fdt_check_header(fdt)) != 0)
            || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 
                        goto fail;

        if (len)
                *len = strlen(nh->name);

        return nh->name;

 fail:
        if (len)
                *len = err;
        return NULL;
}
  • fdt가 가리키는 DTB에서 nodeoffset에 위치한 노드에서 노드명 주소와 노드명 길이를 알아온다.

 

fdt_get_name-1a

 

_fdt_offset_ptr()

scripts/dtc/libfdt/libfdt_internal.h

static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
{
        return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
}

DTB의 structure 블럭 시작 주소에 offset를 더한 주소를 리턴한다..

__fdt_offset_ptr-1a

 

fdt_offset_ptr()

scripts/dtc/libfdt/fdt.c

const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
{
        const char *p; 

        if (fdt_version(fdt) >= 0x11)
                if (((offset + len) < offset)
                    || ((offset + len) > fdt_size_dt_struct(fdt)))
                        return NULL;

        p = _fdt_offset_ptr(fdt, offset);

        if (p + len < p)
                return NULL;
        return p;
}

DTB structure 블럭에서 offset 만큼 떨어진 주소를 알아오는데 그 주소의 위치가 DTB structure 블럭을 벗어난 경우 에러로 null을 리턴한다.

  • if (fdt_version(fdt) >= 0x11
    • fdt 주소에 위치한 DTB 버전이 0x11이상인 경우 len이 음수이거나 물리주소를 초과한 경우
  • if (((offset + len) < offset) || ((offset + len) > fdt_size_dt_struct(fdt))) return NULL;
    • offset+len이 DTB structure 블럭 사이즈를 초과한 경우 null로 리턴
  • p = _fdt_offset_ptr(fdt, offset);
    • DTB structure 블럭에서 offset 만큼 떨어진 주소를 알아온다.
  •  if (p + len < p) return NULL;
    • 알아온 주소가 물리 주소를 초과한 경우 null을 리턴한다.

 

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에 사이즈를 더한다.

 

fdt_next_tag()

scripts/dtc/libfdt/fdt.c

uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
{
        const uint32_t *tagp, *lenp;
        uint32_t tag;
        int offset = startoffset;
        const char *p;

        *nextoffset = -FDT_ERR_TRUNCATED;
        tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
        if (!tagp)
                return FDT_END; /* premature end */
        tag = fdt32_to_cpu(*tagp);
        offset += FDT_TAGSIZE;

        *nextoffset = -FDT_ERR_BADSTRUCTURE;
        switch (tag) {
        case FDT_BEGIN_NODE:
                /* skip name */
                do {
                        p = fdt_offset_ptr(fdt, offset++, 1);
                } while (p && (*p != '\0'));
                if (!p)
                        return FDT_END; /* premature end */
                break;

        case FDT_PROP:
                lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
                if (!lenp)
                        return FDT_END; /* premature end */
                /* skip-name offset, length and value */
                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
                        + fdt32_to_cpu(*lenp);
                break;

        case FDT_END:
        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;
}

startoffset에 위치한 태그의 값을 알아오고 startoffset 다음에 위치한 태그 offset를 출력 인수 nextoffset에 저장한다.

  • startoffset 위치의 태그번호를 리턴하는데 태그 종류에 따라 출력 인수 nextoffset에 저장되는 offset가 다음과 같이 다르다.
    • FDT_BEGIN_NODE인 경우 4 byte 태그 및 이름을 skip 한 4 byte round up한 offset
    • FDT_PROP인 경우 4 byte 태그, 속성 길이, name offset, 속성명을 skip한 4 byte round up한 offset
    • 그 외의 태그는 4 byte 태그만 더한 offset

아래 그림은 fdt_next_tag()를 두 번 반복 수행하는 모습을 보여준다.

fdt_next_tag-1b

 

 

fdt_first_property_offset()

scripts/dtc/libfdt/fdt_ro.c

int fdt_first_property_offset(const void *fdt, int nodeoffset)
{
        int offset;

        if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
                return offset;

        return _nextprop(fdt, offset);
}

노드 내의 처음 속성 offset를 리턴한다. 속성이 없으면 -값 에러를 리턴한다.

  • if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) return offset;
    • nodeoffset에 위치한 태그가 노드 시작이 아닌 경우 -값 에러(offset)를 리턴한다.
  • return _nextprop(fdt, offset);
    • 처음 발견되는 속성 offset를 리턴한다.

 

아래 그림은 노드에서 처음 발견되는 속성에 대한 offset를 알아오는 것을 표현하였다.

fdt_first_property_offset-1

 

 

_fdt_check_node_offset()

scripts/dtc/libfdt/fdt.c

int _fdt_check_node_offset(const void *fdt, int offset)
{
        if ((offset < 0) || (offset % FDT_TAGSIZE)
            || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
                return -FDT_ERR_BADOFFSET;

        return offset;
}

주어진 offset에 있는 태그가 시작 노드인 경우에만 offset를 리턴하고 그렇지 않으면 -값 에러를 리턴한다.

  • offset가 0보다 작거나 4 byte align 되어 있지 않거나 현재 태그가 노드 시작이 아니면 -값의 에러를 리턴하고 그렇지 않은 경우 offset를 리턴한다.

 

_nextprop()

scripts/dtc/libfdt/fdt_ro.c

static int _nextprop(const void *fdt, int offset)
{
        uint32_t tag;
        int nextoffset;

        do {
                tag = fdt_next_tag(fdt, offset, &nextoffset);

                switch (tag) {
                case FDT_END:
                        if (nextoffset >= 0)
                                return -FDT_ERR_BADSTRUCTURE;
                        else
                                return nextoffset;

                case FDT_PROP:
                        return offset;
                }
                offset = nextoffset;
        } while (tag == FDT_NOP);

        return -FDT_ERR_NOTFOUND;
}

해당 노드 내에서 다음 속성 태그 offset을 알아온다. 만일 노드가 끝나면 -값으로 에러를 리턴한다.

  • 태그가 FDT_NOP인 경우에 루프를 돌며 다음 태그를 읽어오다가 FDT_PROP를 만나면 해당 offset를 리턴한다. 만일 FDT_END를 만나는 경우 -값으로 에러를 리턴한다.

 

아래 그림은 _nextprop()를 호출할 때 해당 노드내에서 다음 속성에 대한 offset를 읽어 오는데 노드가 바뀌거나 끝나는 경우 에러를 리턴하는 것을 보여준다.

_nextprop-1

 

fdt_next_property_offset()

scripts/dtc/libfdt/fdt_ro.c

int fdt_next_property_offset(const void *fdt, int offset)
{
        if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 
                return offset;
                
        return _nextprop(fdt, offset);
}

노드 내의 다음 속성 offset를 리턴한다. 속성이 없으면 -값 에러를 리턴한다.

  • if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) return offset;
    • offset에 위치한 태그가 속성이 아닌 경우 -값 에러(offset)를 리턴한다.
  • return _nextprop(fdt, offset);
    • 다음 발견되는 속성 offset를 리턴한다.

 

_fdt_check_prop_offset()

scripts/dtc/libfdt/fdt.c()

int _fdt_check_prop_offset(const void *fdt, int offset)
{
        if ((offset < 0) || (offset % FDT_TAGSIZE)
            || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
                return -FDT_ERR_BADOFFSET;

        return offset;
}

주어진 offset에 있는 태그가 속성인 경우에만 offset를 리턴하고 그렇지 않으면 -값 에러를 리턴한다.

  • offset가 0보다 작거나 4 byte align 되어 있지 않거나 현재 태그가 속성이 아니면 -값의 에러를 리턴하고 그렇지 않은 경우 offset를 리턴한다.

 

fdt_getprop_by_offset()

scripts/dtc/libfdt/fdt_ro.c

const void *fdt_getprop_by_offset(const void *fdt, int offset,
                                  const char **namep, int *lenp)
{
        const struct fdt_property *prop;
                
        prop = fdt_get_property_by_offset(fdt, offset, lenp);
        if (!prop)      
                return NULL;
        if (namep)
                *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
        return prop->data;
}

속성 값을 리턴하는데 namep 출력 인수는 속성명이 저장된 string 블록의 주소를 저장하고 , lenp에는 데이터 길이를 저장한다. 리턴 값이 음수인 경우는 에러이다.

 

fdt_get_property_by_offset()

scripts/dtc/libfdt/fdt_ro.c

const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
                                                      int offset,
                                                      int *lenp)
{
        int err;
        const struct fdt_property *prop;

        if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
                if (lenp)
                        *lenp = err;
                return NULL;
        }

        prop = _fdt_offset_ptr(fdt, offset);

        if (lenp)
                *lenp = fdt32_to_cpu(prop->len);

        return prop;
}

offset 위치의 속성 태그에 위치한 주소를 fdt_property 구조체 포인터로 캐스트하여 리턴하고 lenp 출력 인수에는 데이터 길이를 리턴한다.

 

아래 그림은 속성값이 있는 곳의 주소를 fdt_property 구조체 포인터로 캐스트하여 알아오는 것을 보여준다.

fdt_get_property_by_offset-1

 

fdt_string()

scripts/dtc/libfdt/fdt_ro.c

const char *fdt_string(const void *fdt, int stroffset)
{
        return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
}

DTB에서 주어진 문자열 offset(stroffset)에 대한 주소를 알아온다.

 

fdt_next_node()

scripts/dtc/libfdt/fdt.c

int fdt_next_node(const void *fdt, int offset, int *depth)
{
        int nextoffset = 0;
        uint32_t tag;

        if (offset >= 0)
                if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
                        return nextoffset;

        do {
                offset = nextoffset;
                tag = fdt_next_tag(fdt, offset, &nextoffset); 

                switch (tag) {
                case FDT_PROP:
                case FDT_NOP:
                        break;

                case FDT_BEGIN_NODE:
                        if (depth)
                                (*depth)++;
                        break;

                case FDT_END_NODE:
                        if (depth && ((--(*depth)) < 0))
                                return nextoffset;
                        break;

                case FDT_END:
                        if ((nextoffset >= 0)
                            || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
                                return -FDT_ERR_NOTFOUND;
                        else
                                return nextoffset;
                }
        } while (tag != FDT_BEGIN_NODE);

        return offset;
}

다음 노드의 offset를 리턴하고 출력 인수에 depth를 저장한다.

  • if (offset >= 0) if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) return nextoffset;
    • 주어진 offset가 노드가 아닌 경우 null을 리턴한다.
  • 루프를 돌며 태그 번호와 다음 태그 offset을 알아오고 태그에 따라 다음과 같이 동작한다.
    • FDT_PROP이거나 FDT_NOP인 경우 skip 한다.
    • FDT_BEGIN_NODE인 경우 depth 증가하게 되고 루프를 벗어나게 된다.
    • FDT_END_NODE인 경우 depth가 0이상이면서 감소 시킨 depth가 0보다 작은 경우 nextoffset를 리턴한다. (보통 또 다른 시작 노드)
    • FDT_END인 경우 음수 값인 에러를 리턴한다.

 

DTB 헤더 관련 API

scripts/dtc/libfdt/libfdt.h


#define fdt_get_header(fdt, field) \
        (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
#define fdt_magic(fdt)                  (fdt_get_header(fdt, magic))
#define fdt_totalsize(fdt)              (fdt_get_header(fdt, totalsize))
#define fdt_off_dt_struct(fdt)          (fdt_get_header(fdt, off_dt_struct))
#define fdt_off_dt_strings(fdt)         (fdt_get_header(fdt, off_dt_strings))
#define fdt_off_mem_rsvmap(fdt)         (fdt_get_header(fdt, off_mem_rsvmap))
#define fdt_version(fdt)                (fdt_get_header(fdt, version))
#define fdt_last_comp_version(fdt)      (fdt_get_header(fdt, last_comp_version))
#define fdt_boot_cpuid_phys(fdt)        (fdt_get_header(fdt, boot_cpuid_phys))
#define fdt_size_dt_strings(fdt)        (fdt_get_header(fdt, size_dt_strings))
#define fdt_size_dt_struct(fdt)         (fdt_get_header(fdt, size_dt_struct))
  • 디바이스 트리의 헤더에는 10가지의 정보가 있는데 각각에 대해 함수로 제공된다..
  • fdt_magic()
    • DTB 매직 넘버를 알아온다.
  • fdt_totalsize()
    • DTB 전체 사이즈를 알아온다.
  • fdt_off_dt_struct()
    • structure 블럭 시작 offset를 알아온다.
  • fdt_off_dt_strings()
    • string 블럭 시작 offset를 알아온다.
  • fdt_off_mem_rsvmap()
    • memory reserved 블럭 시작 offset를 알아온다.
  • fdt_version()
    • DTB 버전 정보를 알아온다.
  • fdt_last_comp_version
    • DTB 마지막 호환 버전을 알아온다.
  • fdt_boot_cpuid_phys()
  • fdt_size_dt_strings()
    • string 블럭 사이즈를 알아온다.
  • fdt_sizae_dt_struct()
    • structure 블럭 사이즈를 알아온다.

다음은 rpi2 DTB 헤더의 hex 값이다.

fdt_header-1

 

구조체

fdt_header 구조체

scripts/dtc/libfdt/fdt.h

struct fdt_header { 
        uint32_t magic;                  /* magic word FDT_MAGIC */
        uint32_t totalsize;              /* total size of DT block */
        uint32_t off_dt_struct;          /* offset to structure */
        uint32_t off_dt_strings;         /* offset to strings */
        uint32_t off_mem_rsvmap;         /* offset to memory reserve map */
        uint32_t version;                /* format version */
        uint32_t last_comp_version;      /* last compatible version */
                
        /* version 2 fields below */
        uint32_t boot_cpuid_phys;        /* Which physical CPU id we're
                                            booting on */
        /* version 3 fields below */
        uint32_t size_dt_strings;        /* size of the strings block */
                
        /* version 17 fields below */
        uint32_t size_dt_struct;         /* size of the structure block */
};

 

 

fdt_node_header 구조체

scripts/dtc/libfdt/fdt.h

struct fdt_node_header {
        uint32_t tag;
        char name[0];
};
  • tag
    • 1=FDT_BEGIN_NODE
    • 2=FDT_END_NODE
    • 3=FDT_PROP
    • 4=FDT_NOP
    • 9=FDT_END
  • name[0]
    • tag가 1번인 경우 노드명의 첫 글자

 

device_node 구조체

include/linux/of.h

struct device_node {
        const char *name;
        const char *type;
        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;
        struct  kobject kobj;
        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
};
  • name
    • 주소 없는 compact 스타일 노드명이 담긴다.
    • 예) “”, “chosen”, “uart”
  • type
    • 디바이스 타입명이 담긴다.
    • 예) “memory”, “cpu”, “pci”, “ethernet-phy”, “network”
  •  phandle
    • 노드내 “phandle” 속성이 가리키는 노드
  • full_name
    • full path 노드명이 담긴다.
    • 예) “/”, “/chosen”, “/soc/uart@7e201000”
  • fwnode
    • 0=FWNODE_INVALID
    • 1=FWNODE_OF (초기값)
    • 2=FWNODE_ACPI
  • properties
    •  속성값이 있는 경우 property 노드를 가리키고 없으면 null
  • deadprops
  • parent
    • 부모 노드를 가리키고 자신이 루트 노드인 경우 null
  • child
    • 자식 노드들 중 첫 노드를 가리키고 없으면 null
  • sibling
    • 바로 다음 형재 노드를 가리키고 없으면 null
  • kboj
    • object 라이프 사이클을 관리
  • _flags
  • data
    • 4바이트로 표현 가능한 노드 값이 있거나 데이터 위치를 가리키고 없으면 null

 

property 구조체

include/linux/of.h

struct property {
        char    *name;
        int     length;
        void    *value;
        struct property *next;
        unsigned long _flags;
        unsigned int unique_id;
        struct bin_attribute attr;
};
  • name
    • 속성명
    • 예) “name”, “stdout-path”, “reg”
  • length
    • 속성 값 길이
  • value
    • 속성 값이 위치한 곳을 가리키고 없으면 null
  • next
    • 노드내의 다음 속성을 가리키고 없으면 null
  • _flags
  • unique_id
  • attr

 

참고

 

 

댓글 남기기