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






