summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDariusz Swiderski <dms@cvs.openbsd.org>2009-08-25 13:18:12 +0000
committerDariusz Swiderski <dms@cvs.openbsd.org>2009-08-25 13:18:12 +0000
commit895439024f30dc6dbc517f1df13579600b88f19e (patch)
tree2cd7814bad17d22f5eb26c54f949da54239e5940
parent3b82430ff83eff28d489555fa5840fb1e0735d19 (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.socppc3
-rw-r--r--sys/arch/socppc/include/fdt.h59
-rw-r--r--sys/arch/socppc/socppc/fdt.c394
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