First cut at a "libdt" set of routines for extracting things from the
flattened tree. Could be used in firmware.main
parent
9ad4587c90
commit
ab870cadb4
2
Makefile
2
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
|
||||
|
|
|
@ -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_ */
|
34
flattree.c
34
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
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue