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_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()
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_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_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를 읽어 오는데 노드가 바뀌거나 끝나는 경우 에러를 리턴하는 것을 보여준다.
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_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 구조체
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
참고
- DTB – 구조 | 문c
- DTB – 라즈베리파이2 샘플 소스 | 문c
- DTB (of API) | 문c