diff options
author | Dariusz Swiderski <dms@cvs.openbsd.org> | 2009-08-25 13:18:12 +0000 |
---|---|---|
committer | Dariusz Swiderski <dms@cvs.openbsd.org> | 2009-08-25 13:18:12 +0000 |
commit | 895439024f30dc6dbc517f1df13579600b88f19e (patch) | |
tree | 2cd7814bad17d22f5eb26c54f949da54239e5940 | |
parent | 3b82430ff83eff28d489555fa5840fb1e0735d19 (diff) |
Add parser for 'Flattened Device Tree' which was introduced in
bootloaders such as u-boot, which is generally a simplified memory
dump of an OpenFirmware device tree. Tested on RB600 by me.
This is not used in the code ATM.
ok kettenis@
-rw-r--r-- | sys/arch/socppc/conf/files.socppc | 3 | ||||
-rw-r--r-- | sys/arch/socppc/include/fdt.h | 59 | ||||
-rw-r--r-- | sys/arch/socppc/socppc/fdt.c | 394 |
3 files changed, 455 insertions, 1 deletions
diff --git a/sys/arch/socppc/conf/files.socppc b/sys/arch/socppc/conf/files.socppc index e3fae7412eb..335992c3e18 100644 --- a/sys/arch/socppc/conf/files.socppc +++ b/sys/arch/socppc/conf/files.socppc @@ -1,4 +1,4 @@ -# $OpenBSD: files.socppc,v 1.6 2009/08/24 17:48:14 kettenis Exp $ +# $OpenBSD: files.socppc,v 1.7 2009/08/25 13:18:11 dms Exp $ # # macppc-specific configuration info @@ -16,6 +16,7 @@ file arch/socppc/socppc/mem.c file arch/socppc/socppc/dma.c file dev/cninit.c file arch/socppc/socppc/db_interface.c ddb +file arch/socppc/socppc/fdt.c define mainbus {} diff --git a/sys/arch/socppc/include/fdt.h b/sys/arch/socppc/include/fdt.h new file mode 100644 index 00000000000..5d6fb699d4d --- /dev/null +++ b/sys/arch/socppc/include/fdt.h @@ -0,0 +1,59 @@ + +/* + * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct fdt_head { + u_int32_t fh_magic; + u_int32_t fh_size; + u_int32_t fh_struct_off; + u_int32_t fh_strings_off; + u_int32_t fh_reserve_off; + u_int32_t fh_version; + u_int32_t fh_comp_ver; /* last compatible version */ + u_int32_t fh_boot_cpu_id; /* fh_version >=2 */ + u_int32_t fh_strings_size; /* fh_version >=3 */ + u_int32_t fh_struct_size; /* fh_version >=17 */ +}; + +struct fdt { + struct fdt_head *header; + void * tree; + void * strings; + void * memory; + int version; + int strings_size; +}; + +#define FDT_MAGIC 0xd00dfeed +#define FDT_NODE_BEGIN 0x01 +#define FDT_NODE_END 0x02 +#define FDT_PROPERTY 0x03 +#define FDT_NOP 0x04 +#define FDT_END 0x09 + +#define FDT_CODE_VERSION 0x11 + +int fdt_init(void *); +void *fdt_next_node(void *); +void *fdt_child_node(void *); +char *fdt_node_name(void *); +void *fdt_find_node(char *); +int fdt_node_property(void *, char *, char **); +#ifdef DEBUG +void *fdt_print_property(void *, int); +void fdt_print_node(void *, int); +void fdt_print_tree(void); +#endif diff --git a/sys/arch/socppc/socppc/fdt.c b/sys/arch/socppc/socppc/fdt.c new file mode 100644 index 00000000000..52cb0d28b87 --- /dev/null +++ b/sys/arch/socppc/socppc/fdt.c @@ -0,0 +1,394 @@ + +/* + * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/user.h> +#include <machine/fdt.h> + +unsigned int fdt_check_head(void *); +char *fdt_get_str(u_int32_t); +void *skip_property(u_int32_t *); +void *skip_props(u_int32_t *); +void *skip_node_name(u_int32_t *); +void *fdt_find_node_recurse(void *node, char *name); +#ifdef DEBUG +void fdt_print_node_recurse(void *, int); +#endif + +static int tree_inited = 0; +static struct fdt tree; + +unsigned int +fdt_check_head(void *fdt) +{ + struct fdt_head *fh; + u_int32_t *ptr; + + fh = fdt; + ptr = (u_int32_t *)fdt; + + if (fh->fh_magic != FDT_MAGIC) + return 0; + + if (fh->fh_version > FDT_CODE_VERSION) + return 0; + + if (*(ptr + (fh->fh_struct_off / 4)) != FDT_NODE_BEGIN) + return 0; + + /* check for end signature on version 17 blob */ + if ((fh->fh_version >= 17) & (*(ptr + fh->fh_struct_size) != FDT_END)) + return 0; + + return fh->fh_version; +} + +/* + * Initializes internal structures of module. + * Has to be called once, preferably in machdep.c. + */ +int +fdt_init(void *fdt) +{ + int version; + + bzero(&tree, sizeof(struct fdt)); + tree_inited = 0; + + if (!fdt) + return 0; + + if (!(version = fdt_check_head(fdt))) + return 0; + + tree.header = (struct fdt_head *)fdt; + tree.tree = (char *)fdt + tree.header->fh_struct_off; + tree.strings = (char *)fdt + tree.header->fh_strings_off; + tree.memory = (char *)fdt + tree.header->fh_reserve_off; + tree.version = version; + + if (version < 3) { + if ((tree.strings < tree.tree) && (tree.tree < tree.memory)) + tree.strings_size = tree.tree - tree.strings; + else if ((tree.strings < tree.memory) && (tree.memory < + tree.tree)) + tree.strings_size = tree.memory - tree.strings; + else if ((tree.strings < tree.tree) && (tree.memory < + tree.strings)) + tree.strings_size = tree.tree - tree.strings; + else if ((tree.strings < tree.memory) && (tree.tree < + tree.strings)) + tree.strings_size = tree.memory - tree.strings; + else + tree.strings_size = tree.header->fh_size - + (int)tree.strings; + } else + tree.strings_size = tree.header->fh_strings_size; + + tree.strings_size = tree.header->fh_strings_size; + tree_inited = 1; + + return version; +} + +/* + * Retrieve string pointer from srtings table. + */ +char * +fdt_get_str(u_int32_t num) +{ + if (num > tree.strings_size) + return NULL; + return (tree.strings) ? (tree.strings + num) : NULL; +} + +/* + * Utility functions for skipping parts of tree. + */ +void * +skip_property(u_int32_t *ptr) +{ + u_int32_t size; + + size = *(ptr + 1); + /* move forward by magic + size + nameid + rounded up property size */ + ptr += 3 + roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t); + + return ptr; +} + +void * +skip_props(u_int32_t *ptr) +{ + while (*ptr == FDT_PROPERTY) { + ptr = skip_property(ptr); + } + return ptr; +} + +void * +skip_node_name(u_int32_t *ptr) +{ + /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */ + return ptr + roundup(strlen((char *)ptr) + 1, + sizeof(u_int32_t)) / sizeof(u_int32_t); +} + +/* + * Retrieves node property, the returned pointer is inside the fdt tree, + * so we should not modify content pointed by it directly. + */ +int +fdt_node_property(void *node, char *name, char **out) +{ + u_int32_t *ptr; + u_int32_t nameid; + char *tmp; + + if (!tree_inited) + return 0; + + ptr = (u_int32_t *)node; + + if (*ptr != FDT_NODE_BEGIN) + return 0; + + ptr = skip_node_name(ptr + 1); + + while (*ptr == FDT_PROPERTY) { + nameid = *(ptr + 2); /* id of name in strings table */ + tmp = fdt_get_str(nameid); + if (!strcmp(name, tmp)) { + *out = (char *)(ptr + 3); /* begining of the value */ + return *(ptr + 1); /* size of value */ + } + ptr = skip_property(ptr); + } + return 0; +} + +/* + * Retrieves next node, skipping all the children nodes of the pointed node + * if passed 0 wil return first node of the tree (root) + */ +void * +fdt_next_node(void *node) +{ + u_int32_t *ptr; + + if (!tree_inited) + return NULL; + + ptr = node; + + if (!node) { + ptr = tree.tree; + return (*ptr == FDT_NODE_BEGIN) ? ptr : NULL; + } + + if (*ptr != FDT_NODE_BEGIN) + return NULL; + + ptr++; + + ptr = skip_node_name(ptr); + ptr = skip_props(ptr); + + /* skip children */ + while (*ptr == FDT_NODE_BEGIN) + ptr = fdt_next_node(ptr); + + return (*ptr == FDT_NODE_END) ? (ptr + 1) : NULL; +} + +/* + * Retrieves next node, skipping all the children nodes of the pointed node + */ +void * +fdt_child_node(void *node) +{ + u_int32_t *ptr; + + if (!tree_inited) + return NULL; + + ptr = node; + + if (*ptr != FDT_NODE_BEGIN) + return NULL; + + ptr++; + + ptr = skip_node_name(ptr); + ptr = skip_props(ptr); + /* check if there is a child node */ + return (*ptr == FDT_NODE_BEGIN) ? (ptr) : NULL; +} + +/* + * Retrieves node name. + */ +char * +fdt_node_name(void *node) +{ + u_int32_t *ptr; + + if (!tree_inited) + return NULL; + + ptr = node; + + if (*ptr != FDT_NODE_BEGIN) + return NULL; + + return (char *)(ptr + 1); +} + +void * +fdt_find_node_recurse(void *node, char *name) +{ + void *child; + void *tmp; + char *nname; + int len; + + tmp = 0; + + if (!(nname = fdt_node_name(node))) + return NULL; + + /* root directory for version >= 16 can be null */ + if ((strlen(nname) == 0) && (tree.version >= 16)) { + if (!strcmp(name, "/")) + return node; + } else { + len = (strlen(name) > strlen(nname)) ? strlen(nname) : + strlen(name); + if (!strncmp(name, nname, len)) { + return node; + } + } + + for (child = fdt_child_node(node); child && (!tmp); + child = fdt_next_node(child)) + tmp = fdt_find_node_recurse(child, name); + + return (tmp) ? tmp : NULL; +} + +void * +fdt_find_node(char *name) +{ + if (!tree_inited) + return NULL; + + return fdt_find_node_recurse(fdt_next_node(0), name); +} + +#ifdef DEBUG +/* + * Debug methods for printing whole tree, particular odes and properies + */ +void * +fdt_print_property(void *node, int level) +{ + u_int32_t *ptr; + char *tmp, *value; + int cnt; + u_int32_t nameid, size; + + ptr = (u_int32_t *)node; + + if (!tree_inited) + return NULL; + + if (*ptr != FDT_PROPERTY) + return ptr; /* should never happen */ + + /* extract property name_id and size */ + size = *++ptr; + nameid = *++ptr; + + for (cnt = 0; cnt < level; cnt++) + printf("\t"); + + tmp = fdt_get_str(nameid); + printf("\t%s : ", tmp ? tmp : "NO_NAME"); + + ptr++; + value = (char *)ptr; + + if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") || + !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") || + !strcmp(tmp, "linux,stdout-path")) { + printf("%s", value); + } else if (!strcmp(tmp, "clock-frequency") || + !strcmp(tmp, "timebase-frequency")) { + printf("%d", *((unsigned int *)value)); + } else { + for (cnt = 0; cnt < size; cnt++) { + if ((cnt % sizeof(u_int32_t)) == 0) + printf(" "); + printf("%02x", value[cnt]); + } + } + ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t); + printf("\n"); + + return ptr; +} + +void +fdt_print_node(void *node, int level) +{ + u_int32_t *ptr; + int cnt; + + ptr = (u_int32_t *)node; + + if (*ptr != FDT_NODE_BEGIN) + return; + + ptr++; + + for (cnt = 0; cnt < level; cnt++) + printf("\t"); + printf("%s :\n", fdt_node_name(node)); + ptr = skip_node_name(ptr); + + while (*ptr == FDT_PROPERTY) + ptr = fdt_print_property(ptr, level); +} + +void +fdt_print_node_recurse(void *node, int level) +{ + void *child; + + fdt_print_node(node, level); + for (child = fdt_child_node(node); child; child = fdt_next_node(child)) + fdt_print_node_recurse(child, level + 1); +} + +void +fdt_print_tree(void) +{ + fdt_print_node_recurse(fdt_next_node(0), 0); +} +#endif |