summaryrefslogtreecommitdiff
path: root/sys/arch/armv7/stand/efiboot/fdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/armv7/stand/efiboot/fdt.c')
-rw-r--r--sys/arch/armv7/stand/efiboot/fdt.c553
1 files changed, 553 insertions, 0 deletions
diff --git a/sys/arch/armv7/stand/efiboot/fdt.c b/sys/arch/armv7/stand/efiboot/fdt.c
new file mode 100644
index 00000000000..a0ceca48a66
--- /dev/null
+++ b/sys/arch/armv7/stand/efiboot/fdt.c
@@ -0,0 +1,553 @@
+/* $OpenBSD: fdt.c,v 1.1 2016/05/17 21:26:32 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
+ * Copyright (c) 2009, 2016 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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/param.h>
+#include <sys/systm.h>
+
+#include "fdt.h"
+
+unsigned int fdt_check_head(void *);
+char *fdt_get_str(uint32_t);
+void *skip_property(uint32_t *);
+void *skip_props(uint32_t *);
+void *skip_node_name(uint32_t *);
+void *skip_node(void *);
+void *fdt_parent_node_recurse(void *, void *);
+
+static int tree_inited = 0;
+static struct fdt tree;
+
+unsigned int
+fdt_check_head(void *fdt)
+{
+ struct fdt_head *fh;
+ uint32_t *ptr;
+
+ fh = fdt;
+ ptr = (uint32_t *)fdt;
+
+ if (betoh32(fh->fh_magic) != FDT_MAGIC)
+ return 0;
+
+ if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
+ return 0;
+
+ if (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4))) !=
+ FDT_NODE_BEGIN)
+ return 0;
+
+ /* check for end signature on version 17 blob */
+ if ((betoh32(fh->fh_version) >= 17) &&
+ (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
+ (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
+ return 0;
+
+ return betoh32(fh->fh_version);
+}
+
+/*
+ * Initializes internal structures of module.
+ * Has to be called once.
+ */
+int
+fdt_init(void *fdt)
+{
+ int version;
+
+ memset(&tree, 0, 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 + betoh32(tree.header->fh_struct_off);
+ tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
+ tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
+ tree.end = (char *)fdt + betoh32(tree.header->fh_size);
+ tree.version = version;
+ tree.strings_size = betoh32(tree.header->fh_strings_size);
+ if (tree.version >= 17)
+ tree.struct_size = betoh32(tree.header->fh_struct_size);
+ tree_inited = 1;
+
+ return version;
+}
+
+void
+fdt_finalize(void)
+{
+ char *start = (char *)tree.header;
+
+ tree.header->fh_size = htobe32(tree.end - start);
+ tree.header->fh_struct_off = htobe32(tree.tree - start);
+ tree.header->fh_strings_off = htobe32(tree.strings - start);
+ tree.header->fh_reserve_off = htobe32(tree.memory - start);
+ tree.header->fh_strings_size = htobe32(tree.strings_size);
+ if (tree.version >= 17)
+ tree.header->fh_struct_size = htobe32(tree.struct_size);
+}
+
+/*
+ * Return the size of the FDT.
+ */
+size_t
+fdt_get_size(void *fdt)
+{
+ if (!fdt)
+ return 0;
+
+ if (!fdt_check_head(fdt))
+ return 0;
+
+ return betoh32(((struct fdt_head *)fdt)->fh_size);
+}
+
+/*
+ * Retrieve string pointer from strings table.
+ */
+char *
+fdt_get_str(uint32_t num)
+{
+ if (num > tree.strings_size)
+ return NULL;
+ return (tree.strings) ? (tree.strings + num) : NULL;
+}
+
+int
+fdt_add_str(char *name)
+{
+ size_t len = roundup(strlen(name) + 1, sizeof(uint32_t));
+ char *end = tree.strings + tree.strings_size;
+
+ memmove(end + len, end, tree.end - end);
+ tree.strings_size += len;
+ if (tree.tree > tree.strings)
+ tree.tree += len;
+ if (tree.memory > tree.strings)
+ tree.memory += len;
+ tree.end += len;
+ memcpy(end, name, len);
+
+ return (end - tree.strings);
+}
+
+/*
+ * Utility functions for skipping parts of tree.
+ */
+void *
+skip_property(uint32_t *ptr)
+{
+ uint32_t size;
+
+ size = betoh32(*(ptr + 1));
+ /* move forward by magic + size + nameid + rounded up property size */
+ ptr += 3 + roundup(size, sizeof(uint32_t)) / sizeof(uint32_t);
+
+ return ptr;
+}
+
+void *
+skip_props(uint32_t *ptr)
+{
+ while (betoh32(*ptr) == FDT_PROPERTY) {
+ ptr = skip_property(ptr);
+ }
+ return ptr;
+}
+
+void *
+skip_node_name(uint32_t *ptr)
+{
+ /* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
+ return ptr + roundup(strlen((char *)ptr) + 1,
+ sizeof(uint32_t)) / sizeof(uint32_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)
+{
+ uint32_t *ptr;
+ uint32_t nameid;
+ char *tmp;
+
+ if (!tree_inited)
+ return 0;
+
+ ptr = (uint32_t *)node;
+
+ if (betoh32(*ptr) != FDT_NODE_BEGIN)
+ return 0;
+
+ ptr = skip_node_name(ptr + 1);
+
+ while (betoh32(*ptr) == FDT_PROPERTY) {
+ nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
+ tmp = fdt_get_str(nameid);
+ if (!strcmp(name, tmp)) {
+ *out = (char *)(ptr + 3); /* beginning of the value */
+ return betoh32(*(ptr + 1)); /* size of value */
+ }
+ ptr = skip_property(ptr);
+ }
+ return 0;
+}
+
+int
+fdt_node_set_property(void *node, char *name, char *data, int len)
+{
+ uint32_t *ptr, *next;
+ uint32_t nameid;
+ uint32_t curlen;
+ size_t delta;
+ char *tmp;
+
+ if (!tree_inited)
+ return 0;
+
+ ptr = (uint32_t *)node;
+
+ if (betoh32(*ptr) != FDT_NODE_BEGIN)
+ return 0;
+
+ ptr = skip_node_name(ptr + 1);
+
+ while (betoh32(*ptr) == FDT_PROPERTY) {
+ nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
+ tmp = fdt_get_str(nameid);
+ next = skip_property(ptr);
+ if (!strcmp(name, tmp)) {
+ curlen = betoh32(*(ptr + 1));
+ delta = roundup(len, sizeof(uint32_t)) -
+ roundup(curlen, sizeof(uint32_t));
+ memmove((char *)next + delta, next,
+ tree.end - (char *)next);
+ tree.struct_size += delta;
+ if (tree.strings > tree.tree)
+ tree.strings += delta;
+ if (tree.memory > tree.tree)
+ tree.memory += delta;
+ tree.end += delta;
+ *(ptr + 1) = htobe32(len);
+ memcpy(ptr + 3, data, len);
+ return;
+ }
+ ptr = next;
+ }
+ return 0;
+}
+
+int
+fdt_node_add_property(void *node, char *name, char *data, int len)
+{
+ uint32_t *ptr;
+
+ if (!tree_inited)
+ return 0;
+
+ ptr = (uint32_t *)node;
+
+ if (betoh32(*ptr) != FDT_NODE_BEGIN)
+ return 0;
+
+ ptr = skip_node_name(ptr + 1);
+
+ memmove(ptr + 3, ptr, tree.end - (char *)ptr);
+ tree.struct_size += 3 * sizeof(uint32_t);
+ if (tree.strings > tree.tree)
+ tree.strings += 3 * sizeof(uint32_t);
+ if (tree.memory > tree.tree)
+ tree.memory += 3 * sizeof(uint32_t);
+ tree.end += 3 * sizeof(uint32_t);
+ *ptr++ = htobe32(FDT_PROPERTY);
+ *ptr++ = htobe32(0);
+ *ptr++ = htobe32(fdt_add_str(name));
+
+ return fdt_node_set_property(node, name, data, len);
+}
+
+/*
+ * Retrieves next node, skipping all the children nodes of the pointed node,
+ * returns pointer to next node, no matter if it exists or not.
+ */
+void *
+skip_node(void *node)
+{
+ uint32_t *ptr = node;
+
+ ptr++;
+
+ ptr = skip_node_name(ptr);
+ ptr = skip_props(ptr);
+
+ /* skip children */
+ while (betoh32(*ptr) == FDT_NODE_BEGIN)
+ ptr = skip_node(ptr);
+
+ return (ptr + 1);
+}
+
+/*
+ * Retrieves next node, skipping all the children nodes of the pointed node,
+ * returns pointer to next node if exists, otherwise returns NULL.
+ * If passed 0 will return first node of the tree (root).
+ */
+void *
+fdt_next_node(void *node)
+{
+ uint32_t *ptr;
+
+ if (!tree_inited)
+ return NULL;
+
+ ptr = node;
+
+ if (node == NULL) {
+ ptr = (uint32_t *)tree.tree;
+ return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
+ }
+
+ if (betoh32(*ptr) != FDT_NODE_BEGIN)
+ return NULL;
+
+ ptr++;
+
+ ptr = skip_node_name(ptr);
+ ptr = skip_props(ptr);
+
+ /* skip children */
+ while (betoh32(*ptr) == FDT_NODE_BEGIN)
+ ptr = skip_node(ptr);
+
+ if (betoh32(*ptr) != FDT_NODE_END)
+ return NULL;
+
+ if (betoh32(*(ptr + 1)) != FDT_NODE_BEGIN)
+ return NULL;
+
+ return (ptr + 1);
+}
+
+/*
+ * Retrieves next node, skipping all the children nodes of the pointed node
+ */
+void *
+fdt_child_node(void *node)
+{
+ uint32_t *ptr;
+
+ if (!tree_inited)
+ return NULL;
+
+ ptr = node;
+
+ if (betoh32(*ptr) != FDT_NODE_BEGIN)
+ return NULL;
+
+ ptr++;
+
+ ptr = skip_node_name(ptr);
+ ptr = skip_props(ptr);
+ /* check if there is a child node */
+ return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
+}
+
+/*
+ * Retrieves node name.
+ */
+char *
+fdt_node_name(void *node)
+{
+ uint32_t *ptr;
+
+ if (!tree_inited)
+ return NULL;
+
+ ptr = node;
+
+ if (betoh32(*ptr) != FDT_NODE_BEGIN)
+ return NULL;
+
+ return (char *)(ptr + 1);
+}
+
+void *
+fdt_find_node(char *name)
+{
+ void *node = fdt_next_node(0);
+ const char *p = name;
+
+ if (!tree_inited)
+ return NULL;
+
+ if (*p != '/')
+ return NULL;
+
+ while (*p) {
+ void *child;
+ const char *q;
+
+ while (*p == '/')
+ p++;
+ if (*p == 0)
+ return node;
+ q = strchr(p, '/');
+ if (q == NULL)
+ q = p + strlen(p);
+
+ for (child = fdt_child_node(node); child;
+ child = fdt_next_node(child)) {
+ if (strncmp(p, fdt_node_name(child), q - p) == 0) {
+ node = child;
+ break;
+ }
+ }
+
+ p = q;
+ }
+
+ return node;
+}
+
+void *
+fdt_parent_node_recurse(void *pnode, void *child)
+{
+ void *node = fdt_child_node(pnode);
+ void *tmp;
+
+ while (node && (node != child)) {
+ if ((tmp = fdt_parent_node_recurse(node, child)))
+ return tmp;
+ node = fdt_next_node(node);
+ }
+ return (node) ? pnode : NULL;
+}
+
+void *
+fdt_parent_node(void *node)
+{
+ void *pnode = fdt_next_node(0);
+
+ if (!tree_inited)
+ return NULL;
+
+ if (node == pnode)
+ return NULL;
+
+ return fdt_parent_node_recurse(pnode, node);
+}
+
+#ifdef DEBUG
+/*
+ * Debug methods for printing whole tree, particular odes and properies
+ */
+void *
+fdt_print_property(void *node, int level)
+{
+ uint32_t *ptr;
+ char *tmp, *value;
+ int cnt;
+ uint32_t nameid, size;
+
+ ptr = (uint32_t *)node;
+
+ if (!tree_inited)
+ return NULL;
+
+ if (betoh32(*ptr) != FDT_PROPERTY)
+ return ptr; /* should never happen */
+
+ /* extract property name_id and size */
+ size = betoh32(*++ptr);
+ nameid = betoh32(*++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", betoh32(*((unsigned int *)value)));
+ } else {
+ for (cnt = 0; cnt < size; cnt++) {
+ if ((cnt % sizeof(uint32_t)) == 0)
+ printf(" ");
+ printf("%x%x", value[cnt] >> 4, value[cnt] & 0xf);
+ }
+ }
+ ptr += roundup(size, sizeof(uint32_t)) / sizeof(uint32_t);
+ printf("\n");
+
+ return ptr;
+}
+
+void
+fdt_print_node(void *node, int level)
+{
+ uint32_t *ptr;
+ int cnt;
+
+ ptr = (uint32_t *)node;
+
+ if (betoh32(*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 (betoh32(*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