diff options
Diffstat (limited to 'sys/arch/armv7/stand/efiboot/fdt.c')
-rw-r--r-- | sys/arch/armv7/stand/efiboot/fdt.c | 553 |
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 |