diff --git a/Makefile b/Makefile index f9a1050..8cd8a12 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ lex.yy.c: dtc-lexer.l lex.yy.o: lex.yy.c dtc-parser.tab.h -dtc-parser.c: dtc-lexer.c +livetree.o: flat_dt.h check: all cd tests && $(MAKE) check diff --git a/flat_dt.h b/flat_dt.h new file mode 100644 index 0000000..28b4d03 --- /dev/null +++ b/flat_dt.h @@ -0,0 +1,44 @@ +#ifndef _FLAT_DT_H_ +#define _FLAT_DT_H_ + + +#define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */ + +#define OF_DT_BEGIN_NODE 0x1 /* Start node: full name */ +#define OF_DT_END_NODE 0x2 /* End node */ +#define OF_DT_PROP 0x3 /* Property: name off, + size, content */ +#define OF_DT_END 0x9 + +struct boot_param_header { + uint32_t magic; /* magic word OF_DT_HEADER */ + 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 */ +}; + +#define BPH_V1_SIZE (7*sizeof(uint32_t)) +#define BPH_V2_SIZE (BPH_V1_SIZE + sizeof(uint32_t)) +#define BPH_V3_SIZE (BPH_V2_SIZE + sizeof(uint32_t)) + +struct reserve_entry { + uint64_t address; + uint64_t size; +}; + +struct flat_dt_property { + uint32_t nameoff; + uint32_t len; + char data[0]; +}; + +#endif /* _FLAT_DT_H_ */ diff --git a/flattree.c b/flattree.c index 28427ab..2b8efdd 100644 --- a/flattree.c +++ b/flattree.c @@ -19,39 +19,7 @@ */ #include "dtc.h" - -#define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */ - -#define OF_DT_BEGIN_NODE 0x1 /* Start node: full name */ -#define OF_DT_END_NODE 0x2 /* End node */ -#define OF_DT_PROP 0x3 /* Property: name off, - size, content */ -#define OF_DT_END 0x9 - -struct boot_param_header { - u32 magic; /* magic word OF_DT_HEADER */ - u32 totalsize; /* total size of DT block */ - u32 off_dt_struct; /* offset to structure */ - u32 off_dt_strings; /* offset to strings */ - u32 off_mem_rsvmap; /* offset to memory reserve map */ - u32 version; /* format version */ - u32 last_comp_version; /* last compatible version */ - - /* version 2 fields below */ - u32 boot_cpuid_phys; /* Which physical CPU id we're - booting on */ - /* version 3 fields below */ - u32 size_dt_strings; /* size of the strings block */ -}; - -#define BPH_V1_SIZE (7*sizeof(u32)) -#define BPH_V2_SIZE (BPH_V1_SIZE + sizeof(u32)) -#define BPH_V3_SIZE (BPH_V2_SIZE + sizeof(u32)) - -struct reserve_entry { - u64 address; - u64 size; -}; +#include "flat_dt.h" #define FTF_FULLPATH 0x1 #define FTF_VARALIGN 0x2 diff --git a/libdt.c b/libdt.c new file mode 100644 index 0000000..df50402 --- /dev/null +++ b/libdt.c @@ -0,0 +1,213 @@ +#include +#include + +#include "flat_dt.h" + +typedef uint32_t u32; +typedef uint64_t u64; + +#define ALIGN(x, a) (((x) + ((a) - 1)) & ((a) - 1)) +#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a)))) + +#define GET_CELL(p) (p += 4, *((u32 *)(p-4))) + +static char *skip_name(char *p) +{ + while (*p != '\0') + p++; + + return PALIGN(p, sizeof(u32)); +} + +static char *skip_prop(void *blob, char *p) +{ + struct boot_param_header *bph = blob; + u32 len, nameoff; + + len = GET_CELL(p); + nameoff = GET_CELL(p); + if ((bph->version < 0x10) && (len >= sizeof(u64))) + p = PALIGN(p, sizeof(u64)); + return PALIGN(p + len, sizeof(u32)); +} + +static char *get_unit(char *dtpath) +{ + char *p; + + if (dtpath[0] != '/') + return dtpath; + + p = dtpath + strlen(dtpath); + while (*p != '/') + p--; + + return p+1; +} + +static int first_seg_len(char *dtpath) +{ + int len = 0; + + while ((dtpath[len] != '/') && (dtpath[len] != '\0')) + len++; + + return len; +} + +char *flat_dt_get_string(void *blob, u32 offset) +{ + struct boot_param_header *bph = blob; + + return (char *)blob + bph->off_dt_strings + offset; +} + +void *flat_dt_get_subnode(void *blob, void *node, char *uname, int unamelen) +{ + struct boot_param_header *bph = blob; + char *p = node; + u32 tag; + int depth = 0; + char *nuname; + + if (! unamelen) + unamelen = strlen(uname); + + do { + tag = GET_CELL(p); + + switch (tag) { + case OF_DT_PROP: + p = skip_prop(blob, p); + break; + + case OF_DT_BEGIN_NODE: + if (depth == 0) { + nuname = p; + + if (bph->version < 0x10) + nuname = get_unit(nuname); + + p = skip_name(p); + + if (strncmp(nuname, uname, unamelen) == 0) + return p; + } + depth++; + break; + + case OF_DT_END_NODE: + depth--; + break; + + case OF_DT_END: + /* looks like a malformed tree */ + return NULL; + break; + + default: + /* FIXME: throw some sort of error */ + return NULL; + } + } while (depth >= 0); + + return NULL; +} + +void *flat_dt_get_node(void *blob, char *path) +{ + struct boot_param_header *bph = blob; + char *node; + int seglen; + + node = blob + bph->off_dt_struct; + node += sizeof(u32); /* skip initial OF_DT_BEGIN_NODE */ + node = skip_name(node); /* skip root node name */ + + while (node && (*path)) { + if (path[0] == '/') + path++; + + seglen = first_seg_len(path); + + node = flat_dt_get_subnode(blob, node, path, seglen); + + path += seglen; + } + + return node; +} + +void flat_dt_traverse(void *blob, int (*fn)(void *blob, void *node, void *priv), + void *private) +{ + struct boot_param_header *bph = blob; + char *p; + u32 tag; + int depth = 0; + char *uname; + + p = (char *)blob + bph->off_dt_struct; + + tag = GET_CELL(p); + while (tag != OF_DT_END) { + switch (tag) { + case OF_DT_BEGIN_NODE: + uname = p; + + if (bph->version < 0x10) + uname = get_unit(uname); + + p = skip_name(p); + + (*fn)(blob, p, private); + depth++; + break; + + case OF_DT_END_NODE: + depth--; + break; + + case OF_DT_PROP: + p = skip_prop(blob, p); + break; + + default: + /* FIXME: badly formed tree */ + return; + } + } +} + +void *flat_dt_get_prop(void *blob, void *node, char *name, u32 *len) +{ + struct boot_param_header *bph = blob; + char *p = node; + + do { + u32 tag = GET_CELL(p); + u32 sz, noff; + const char *nstr; + + if (tag != OF_DT_PROP) + return NULL; + + sz = GET_CELL(p); + noff = GET_CELL(p); + + /* Old versions have variable alignment of the + * property value */ + if ((bph->version < 0x10) && (sz >= 8)) + p = PALIGN(p, 8); + + nstr = flat_dt_get_string(blob, noff); + + if (strcmp(name, nstr) == 0) { + if (len) + *len = sz; + return (void *)p; + } + + p = PALIGN(p + sz, sizeof(u32)); + } while(1); +}