diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 51ac9af..dc29004 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -138,6 +138,9 @@ u32 end; + if (data->oftree) + return -ENXIO; + header = &__header; ret = read(fd, header, sizeof(*header)); if (ret < sizeof(*header)) @@ -402,12 +405,6 @@ if (!getenv("aimage_noverwrite_tags")) armlinux_set_bootparams((void*)header->tags_addr); - if (IS_ENABLED(CONFIG_OFTREE) && data->oftree) { - ret = of_fix_tree(data->oftree); - if (ret) - goto err_out; - } - cmp = &header->second_stage; if (cmp->size) { void (*second)(void); diff --git a/arch/arm/lib/bootu.c b/arch/arm/lib/bootu.c index 75e0de3..fdb0362 100644 --- a/arch/arm/lib/bootu.c +++ b/arch/arm/lib/bootu.c @@ -23,7 +23,7 @@ kernel = (void *)simple_strtoul(argv[1], NULL, 0); #ifdef CONFIG_OFTREE - oftree = of_get_fixed_tree(); + oftree = of_get_fixed_tree(NULL); #endif start_linux(kernel, 0, 0, 0, oftree); diff --git a/arch/arm/lib/bootz.c b/arch/arm/lib/bootz.c index 9f5b3b4..e32a77b 100644 --- a/arch/arm/lib/bootz.c +++ b/arch/arm/lib/bootz.c @@ -109,7 +109,7 @@ printf("loaded zImage from %s with size %d\n", argv[1], end); #ifdef CONFIG_OFTREE - oftree = of_get_fixed_tree(); + oftree = of_get_fixed_tree(NULL); #endif start_linux(zimage, swap, 0, 0, oftree); diff --git a/commands/Kconfig b/commands/Kconfig index 1addd91..d3c338c 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -481,18 +481,28 @@ config CMD_OFTREE tristate select OFTREE + select OFDEVICE prompt "oftree" help The oftree command has support for dumping devicetrees and, if enabled, to probe devices from the devicetree -config CMD_OFTREE_PROBE - bool - depends on CMD_OFTREE +config CMD_OF_PROPERTY + tristate + select OFTREE select OFDEVICE - prompt "oftree probe support" + prompt "of_property" help - This enables the -p option to probe devices from the devicetree + The of_property command allows setting and deleting of properties in + the currently loaded devicetree. + +config CMD_OF_NODE + tristate + select OFTREE + select OFDEVICE + prompt "of_node" + help + The of_property command allows adding and removing devicetree nodes. endmenu diff --git a/commands/Makefile b/commands/Makefile index c4baf6a..0ae6b95 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -67,6 +67,8 @@ obj-$(CONFIG_CMD_USB) += usb.o obj-$(CONFIG_CMD_TIME) += time.o obj-$(CONFIG_CMD_OFTREE) += oftree.o +obj-$(CONFIG_CMD_OF_PROPERTY) += of_property.o +obj-$(CONFIG_CMD_OF_NODE) += of_node.o obj-$(CONFIG_CMD_MAGICVAR) += magicvar.o obj-$(CONFIG_CMD_IOMEM) += iomem.o obj-$(CONFIG_CMD_LINUX_EXEC) += linux_exec.o diff --git a/commands/bootm.c b/commands/bootm.c index 5ccf237..4d3f022 100644 --- a/commands/bootm.c +++ b/commands/bootm.c @@ -139,9 +139,7 @@ { enum filetype ft; struct fdt_header *fdt, *fixfdt; - int ret; size_t size; - unsigned int align; printf("Loading devicetree from '%s'\n", oftree); @@ -189,36 +187,18 @@ file_type_to_string(ft)); } - /* - * ARM Linux uses a single 1MiB section (with 1MiB alignment) - * for mapping the devicetree, so we are not allowed to cross - * 1MiB boundaries. - */ - align = 1 << fls(size + OFTREE_SIZE_INCREASE - 1); - - fixfdt = xmemalign(align, size + OFTREE_SIZE_INCREASE); - memcpy(fixfdt, fdt, size); - - - ret = fdt_open_into(fdt, fixfdt, size + OFTREE_SIZE_INCREASE); + fixfdt = of_get_fixed_tree(fdt); + if (!fixfdt) + return -EINVAL; free(fdt); - if (ret) { - printf("unable to parse %s\n", oftree); - return -ENODEV; - } - - ret = of_fix_tree(fixfdt); - if (ret) - return ret; - if (bootm_verbose(data) > 1) fdt_print(fixfdt, "/"); data->oftree = fixfdt; - return ret; + return 0; } #endif @@ -420,6 +400,10 @@ ret = bootm_open_oftree(&data, oftree, oftree_num); if (ret) goto err_out; + } else { + data.oftree = of_get_fixed_tree(NULL); + if (bootm_verbose(&data) && data.oftree) + printf("using internal devicetree\n"); } #endif if (data.os_address == UIMAGE_SOME_ADDRESS) diff --git a/commands/of_node.c b/commands/of_node.c new file mode 100644 index 0000000..a370e26 --- /dev/null +++ b/commands/of_node.c @@ -0,0 +1,111 @@ +/* + * of_node.c - device tree node handling support + * + * Copyright (c) 2013 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int do_of_node(int argc, char *argv[]) +{ + int opt; + int delete = 0; + int create = 0; + char *path = NULL; + struct device_node *node = NULL; + + while ((opt = getopt(argc, argv, "cd")) > 0) { + switch (opt) { + case 'c': + create = 1; + break; + case 'd': + delete = 1; + break; + default: + return COMMAND_ERROR_USAGE; + } + } + + if (optind < argc) { + path = argv[optind]; + } + + if (create) { + char *name; + + if (!path) + return COMMAND_ERROR_USAGE; + + name = xstrdup(basename(path)); + path = dirname(path); + + node = of_find_node_by_path(path); + if (!node) { + printf("Cannot find nodepath %s\n", path); + free(name); + return -ENOENT; + } + + debug("create node \"%s\" \"%s\"\n", path, name); + + of_new_node(node, name); + + free(name); + + return 0; + } + + if (delete) { + if (!path) + return COMMAND_ERROR_USAGE; + + node = of_find_node_by_path(path); + if (!node) { + printf("Cannot find nodepath %s\n", path); + return -ENOENT; + } + + of_free(node); + } + + return 0; +} + +BAREBOX_CMD_HELP_START(of_node) +BAREBOX_CMD_HELP_USAGE("of_node [OPTIONS] [NODE] [NAME]\n") +BAREBOX_CMD_HELP_OPT ("-c", "create a new node\n") +BAREBOX_CMD_HELP_OPT ("-d", "delete a node\n") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(of_node) + .cmd = do_of_node, + .usage = "handle of nodes", + BAREBOX_CMD_HELP(cmd_of_node_help) +BAREBOX_CMD_END diff --git a/commands/of_property.c b/commands/of_property.c new file mode 100644 index 0000000..42b6f11 --- /dev/null +++ b/commands/of_property.c @@ -0,0 +1,280 @@ +/* + * of_property.c - device tree property handling support + * + * Copyright (c) 2013 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int of_parse_prop_cells(char * const *newval, int count, char *data, int *len) +{ + char *cp; + unsigned long tmp; /* holds converted values */ + int stridx = 0; + char *newp = newval[0]; + + newp++; + + while (1) { + if (*newp == '>') + return 0; + + if (*newp == '\0') { + newp = newval[++stridx]; + + if (stridx == count) { + printf("missing '>'\n"); + return -EINVAL; + } + + continue; + } + + cp = newp; + tmp = simple_strtoul(cp, &newp, 0); + *(__be32 *)data = __cpu_to_be32(tmp); + data += 4; + *len += 4; + + /* If the ptr didn't advance, something went wrong */ + if ((newp - cp) <= 0) { + printf("cannot not convert \"%s\"\n", cp); + return -EINVAL; + } + + while (*newp == ' ') + newp++; + } +} + +static int of_parse_prop_stream(char * const *newval, int count, char *data, int *len) +{ + char *cp; + unsigned long tmp; /* holds converted values */ + int stridx = 0; + char *newp = newval[0]; + + newp++; + + while (1) { + if (*newp == ']') + return 0; + + while (*newp == ' ') + newp++; + + if (*newp == '\0') { + newp = newval[++stridx]; + + if (stridx == count) { + printf("missing ']'\n"); + return -EINVAL; + } + + continue; + } + + cp = newp; + tmp = simple_strtoul(newp, &newp, 16); + *data++ = tmp & 0xff; + *len = *len + 1; + + /* If the ptr didn't advance, something went wrong */ + if ((newp - cp) <= 0) { + printf("cannot not convert \"%s\"\n", cp); + return -EINVAL; + } + } +} + +static int of_parse_prop_string(char * const *newval, int count, char *data, int *len) +{ + int stridx = 0; + char *newp = newval[0]; + + /* + * Assume it is one or more strings. Copy it into our + * data area for convenience (including the + * terminating '\0's). + */ + while (stridx < count) { + size_t length = strlen(newp) + 1; + + strcpy(data, newp); + data += length; + *len += length; + newp = newval[++stridx]; + } + + return 0; +} + +/* + * Parse the user's input, partially heuristic. Valid formats: + * <0x00112233 4 05> - an array of cells. Numbers follow standard + * C conventions. + * [00 11 22 .. nn] - byte stream + * "string" - If the the value doesn't start with "<" or "[", it is + * treated as a string. Note that the quotes are + * stripped by the parser before we get the string. + * newval: An array of strings containing the new property as specified + * on the command line + * count: The number of strings in the array + * data: A bytestream to be placed in the property + * len: The length of the resulting bytestream + */ +static int of_parse_prop(char * const *newval, int count, char *data, int *len) +{ + char *newp; /* temporary newval char pointer */ + + *len = 0; + + if (!count) + return 0; + + newp = newval[0]; + + switch (*newp) { + case '<': + return of_parse_prop_cells(newval, count, data, len); + case '[': + return of_parse_prop_stream(newval, count, data, len); + default: + return of_parse_prop_string(newval, count, data, len); + } +} + +static int do_of_property(int argc, char *argv[]) +{ + int opt; + int delete = 0; + int set = 0; + int ret; + char *path = NULL, *propname = NULL; + struct device_node *node = NULL; + struct property *pp = NULL; + + while ((opt = getopt(argc, argv, "ds")) > 0) { + switch (opt) { + case 'd': + delete = 1; + break; + case 's': + set = 1; + break; + default: + return COMMAND_ERROR_USAGE; + } + } + + if (optind < argc) { + path = argv[optind]; + node = of_find_node_by_path(path); + if (!node) { + printf("Cannot find nodepath %s\n", path); + return -ENOENT; + } + } + + if (optind + 1 < argc) { + propname = argv[optind + 1]; + + pp = of_find_property(node, propname); + if (!set && !pp) { + printf("Cannot find property %s\n", propname); + return -ENOENT; + } + } + + debug("path: %s propname: %s\n", path, propname); + + if (delete) { + if (!node || !pp) + return COMMAND_ERROR_USAGE; + + of_delete_property(pp); + + return 0; + } + + if (set) { + int num_args = argc - optind - 2; + int len; + void *data; + + if (!node) + return COMMAND_ERROR_USAGE; + + /* + * standard console buffer size. The result won't be bigger than the + * string input. + */ + data = malloc(1024); + if (!data) + return -ENOMEM; + + ret = of_parse_prop(&argv[optind + 2], num_args, data, &len); + if (ret) { + free(data); + return ret; + } + + if (pp) { + free(pp->value); + /* limit property data to the actual size */ + data = xrealloc(data, len); + pp->value = data; + pp->length = len; + } else { + pp = of_new_property(node, propname, data, len); + if (!pp) { + printf("Cannot create property %s\n", propname); + free(data); + return 1; + } + } + } + + return 0; +} + +BAREBOX_CMD_HELP_START(of_property) +BAREBOX_CMD_HELP_USAGE("of_property [OPTIONS] [NODE] [PROPERTY] [VALUES]\n") +BAREBOX_CMD_HELP_OPT ("-s", "set property to value\n") +BAREBOX_CMD_HELP_OPT ("-d", "delete property\n") +BAREBOX_CMD_HELP_TEXT ("\nvalid formats for values:\n") +BAREBOX_CMD_HELP_TEXT ("<0x00112233 4 05> - an array of cells\n") +BAREBOX_CMD_HELP_TEXT ("[00 11 22 .. nn] - byte stream\n") +BAREBOX_CMD_HELP_TEXT ("If the value does not start with '<' or '[' it is interpreted as strings\n") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(of_property) + .cmd = do_of_property, + .usage = "handle of properties", + BAREBOX_CMD_HELP(cmd_of_property_help) +BAREBOX_CMD_END diff --git a/commands/oftree.c b/commands/oftree.c index 7404db5..ddbff3e 100644 --- a/commands/oftree.c +++ b/commands/oftree.c @@ -36,20 +36,28 @@ #include #include #include +#include static int do_oftree(int argc, char *argv[]) { - struct fdt_header *fdt; + struct fdt_header *fdt = NULL; + void *fdt_free = NULL; int size; int opt; char *file = NULL; const char *node = "/"; int dump = 0; int probe = 0; + int load = 0; + int save = 0; + int free_of = 0; int ret; - while ((opt = getopt(argc, argv, "dpfn:")) > 0) { + while ((opt = getopt(argc, argv, "dpfn:ls")) > 0) { switch (opt) { + case 'l': + load = 1; + break; case 'd': dump = 1; break; @@ -62,73 +70,121 @@ } break; case 'f': - free(barebox_fdt); - barebox_fdt = NULL; - return 0; + free_of = 1; + break; case 'n': node = optarg; break; + case 's': + save = 1; + break; } } + if (free_of) { + struct device_node *root = of_get_root_node(); + + if (root) + of_free(root); + + return 0; + } + if (optind < argc) file = argv[optind]; - if (!dump && !probe) + if (!dump && !probe && !load && !save) return COMMAND_ERROR_USAGE; + if (save) { + if (!file) { + printf("no file given\n"); + ret = -ENOENT; + + goto out; + } + + fdt = of_get_fixed_tree(NULL); + if (!fdt) { + printf("no devicetree available\n"); + ret = -EINVAL; + + goto out; + } + + ret = write_file(file, fdt, fdt_totalsize(fdt)); + + goto out; + } + + if (file) { + fdt = read_file(file, &size); + if (!fdt) { + printf("unable to read %s\n", file); + return 1; + } + + fdt_free = fdt; + } + + if (load) { + if (!fdt) { + printf("no fdt given\n"); + ret = -ENOENT; + + goto out; + } + + ret = of_unflatten_dtb(fdt); + if (ret) { + printf("parse oftree: %s\n", strerror(-ret)); + goto out; + } + } + if (dump) { - if (file) { - fdt = read_file(file, &size); - if (!fdt) { - printf("unable to read %s\n", file); - return 1; + if (fdt) { + ret = fdt_print(fdt, node); + } else { + struct device_node *n = of_find_node_by_path(node); + + if (!n) { + ret = -ENOENT; + goto out; } - fdt_print(fdt, node); - free(fdt); - } else { - if (barebox_fdt) { - fdt_print(barebox_fdt, node); - return 0; - } else { - return 1; - } + of_print_nodes(n, 0); + + ret = 0; } - return 0; + + goto out; } if (probe) { - if (!file) - return COMMAND_ERROR_USAGE; - - fdt = read_file(file, &size); - if (!fdt) { - perror("open"); - return 1; - } - - ret = of_parse_dtb(fdt); - if (ret) { - printf("parse oftree: %s\n", strerror(-ret)); - return 1; - } - - of_probe(); + ret = of_probe(); + if (ret) + goto out; } - return 0; + ret = 0; +out: + free(fdt_free); + + return ret; } BAREBOX_CMD_HELP_START(oftree) -BAREBOX_CMD_HELP_USAGE("oftree [OPTIONS]\n") -BAREBOX_CMD_HELP_OPT ("-p ", "probe devices in oftree from \n") -BAREBOX_CMD_HELP_OPT ("-d [FILE]", "dump oftree from [FILE] or the parsed tree if no file is given\n") -BAREBOX_CMD_HELP_OPT ("-f", "free stored oftree\n") +BAREBOX_CMD_HELP_USAGE("oftree [OPTIONS] [DTB]\n") +BAREBOX_CMD_HELP_OPT ("-l", "Load [DTB] to internal devicetree\n") +BAREBOX_CMD_HELP_OPT ("-p", "probe devices from stored devicetree\n") +BAREBOX_CMD_HELP_OPT ("-d", "dump oftree from [DTB] or the parsed tree if no dtb is given\n") +BAREBOX_CMD_HELP_OPT ("-f", "free stored devicetree\n") +BAREBOX_CMD_HELP_OPT ("-n ", "specify root devicenode to dump for -d\n") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(oftree) .cmd = do_oftree, - .usage = "handle oftrees", + .usage = "handle devicetrees", BAREBOX_CMD_HELP(cmd_oftree_help) BAREBOX_CMD_END diff --git a/common/oftree.c b/common/oftree.c index 3e8c6f8..0df5209 100644 --- a/common/oftree.c +++ b/common/oftree.c @@ -246,8 +246,6 @@ return nodeoffset; } -struct fdt_header *barebox_fdt; - static int of_fixup_bootargs(struct fdt_header *fdt) { int nodeoffset; @@ -294,6 +292,10 @@ return 0; } +/* + * Apply registered fixups for the given fdt. The fdt must have + * enough free space to apply the fixups. + */ int of_fix_tree(struct fdt_header *fdt) { struct of_fixup *of_fixup; @@ -308,14 +310,55 @@ return 0; } -struct fdt_header *of_get_fixed_tree(void) +/* + * The size by which we increase the dtb to have space for additional + * fixups. Ideally this would be done by libfdt automatically + */ +#define OFTREE_SIZE_INCREASE 0x8000 + +/* + * Get the fixed fdt. This function uses the fdt input pointer + * if provided or the barebox internal devicetree if not. + * It increases the size of the tree and applies the registered + * fixups. + */ +struct fdt_header *of_get_fixed_tree(struct fdt_header *fdt) { int ret; + void *fixfdt, *internalfdt = NULL; + int size, align; - if (!barebox_fdt) - return NULL; - ret = of_fix_tree(barebox_fdt); + if (!fdt) { + fdt = internalfdt = of_flatten_dtb(); + if (!fdt) + return NULL; + } + + size = fdt_totalsize(fdt); + + /* + * ARM Linux uses a single 1MiB section (with 1MiB alignment) + * for mapping the devicetree, so we are not allowed to cross + * 1MiB boundaries. + */ + align = 1 << fls(size + OFTREE_SIZE_INCREASE - 1); + + fixfdt = xmemalign(align, size + OFTREE_SIZE_INCREASE); + ret = fdt_open_into(fdt, fixfdt, size + OFTREE_SIZE_INCREASE); + + free(internalfdt); + if (ret) - return NULL; - return barebox_fdt; + goto out_free; + + ret = of_fix_tree(fixfdt); + if (ret) + goto out_free; + + return fixfdt; + +out_free: + free(fixfdt); + + return NULL; } diff --git a/drivers/of/base.c b/drivers/of/base.c index 576841d..eaaeaf4 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /** @@ -138,6 +139,12 @@ void of_alias_scan(void) { struct property *pp; + struct alias_prop *app, *tmp; + + list_for_each_entry_safe(app, tmp, &aliases_lookup, link) + free(app); + + INIT_LIST_HEAD(&aliases_lookup); of_aliases = of_find_node_by_path("/aliases"); if (!of_aliases) @@ -485,10 +492,14 @@ { struct device_node *np; + if (!strcmp(path, "/")) + return root_node; + list_for_each_entry(np, &allnodes, list) { if (np->full_name && (strcmp(np->full_name, path) == 0)) return np; } + return NULL; } EXPORT_SYMBOL(of_find_node_by_path); @@ -574,22 +585,37 @@ printf("};\n"); } -static struct device_node *new_device_node(struct device_node *parent) +struct device_node *of_new_node(struct device_node *parent, const char *name) { struct device_node *node; + if (!parent && root_node) + return NULL; + node = xzalloc(sizeof(*node)); node->parent = parent; if (parent) list_add_tail(&node->parent_list, &parent->children); + else + root_node = node; INIT_LIST_HEAD(&node->children); INIT_LIST_HEAD(&node->properties); + if (parent) { + node->name = xstrdup(name); + node->full_name = asprintf("%s/%s", node->parent->full_name, name); + } else { + node->name = xstrdup(""); + node->full_name = xstrdup(""); + } + + list_add_tail(&node->list, &allnodes); + return node; } -static struct property *new_property(struct device_node *node, const char *name, +struct property *of_new_property(struct device_node *node, const char *name, const void *data, int len) { struct property *prop; @@ -606,6 +632,15 @@ return prop; } +void of_delete_property(struct property *pp) +{ + list_del(&pp->list); + + free(pp->name); + free(pp->value); + free(pp); +} + static struct device_d *add_of_device(struct device_node *node) { struct device_d *dev; @@ -754,6 +789,8 @@ if (!node) return; + list_del(&node->list); + list_for_each_entry_safe(p, pt, &node->properties, list) { list_del(&p->list); free(p->name); @@ -776,6 +813,11 @@ free(node->name); free(node->full_name); free(node); + + if (node == root_node) + root_node = NULL; + + of_alias_scan(); } static void __of_probe(struct device_node *node) @@ -812,11 +854,32 @@ return 0; } +static struct device_node *of_find_child(struct device_node *node, const char *name) +{ + struct device_node *_n; + + if (!root_node) + return NULL; + + if (!node && !*name) + return root_node; + + if (!node) + node = root_node; + + list_for_each_entry(_n, &node->children, parent_list) { + if (!strcmp(_n->name, name)) + return _n; + } + + return NULL; +} + /* * Parse a flat device tree binary blob and store it in the barebox * internal tree format, */ -int of_parse_dtb(struct fdt_header *fdt) +int of_unflatten_dtb(struct fdt_header *fdt) { const void *nodep; /* property node pointer */ int nodeoffset; /* node offset from libfdt */ @@ -827,12 +890,8 @@ const struct fdt_property *fdt_prop; const char *pathp; int depth = 10000; - struct device_node *node = NULL; - char buf[1024]; - int ret; - - if (root_node) - return -EBUSY; + struct device_node *node = NULL, *n; + struct property *p; nodeoffset = fdt_path_offset(fdt, "/"); if (nodeoffset < 0) { @@ -853,16 +912,12 @@ if (pathp == NULL) pathp = "/* NULL pointer error */"; - ret = fdt_get_path(fdt, nodeoffset, buf, 1024); - if (ret) - return -EINVAL; - - node = new_device_node(node); - if (!node->parent) - root_node = node; - node->full_name = xstrdup(buf); - node->name = xstrdup(pathp); - list_add_tail(&node->list, &allnodes); + n = of_find_child(node, pathp); + if (n) { + node = n; + } else { + node = of_new_node(node, pathp); + } break; case FDT_END_NODE: node = node->parent; @@ -874,7 +929,15 @@ fdt32_to_cpu(fdt_prop->nameoff)); len = fdt32_to_cpu(fdt_prop->len); nodep = fdt_prop->data; - new_property(node, pathp, nodep, len); + + p = of_find_property(node, pathp); + if (p) { + free(p->value); + p->value = xzalloc(len); + memcpy(p->value, nodep, len); + } else { + of_new_property(node, pathp, nodep, len); + } break; case FDT_NOP: break; @@ -892,6 +955,71 @@ return 0; } +static int __of_flatten_dtb(void *fdt, struct device_node *node) +{ + struct property *p; + struct device_node *n; + int ret; + + ret = fdt_begin_node(fdt, node->name); + if (ret) + return ret; + + list_for_each_entry(p, &node->properties, list) { + ret = fdt_property(fdt, p->name, p->value, p->length); + if (ret) + return ret; + } + + list_for_each_entry(n, &node->children, parent_list) { + ret = __of_flatten_dtb(fdt, n); + if (ret) + return ret; + } + + ret = fdt_end_node(fdt); + + return ret; +} + +#define DTB_SIZE SZ_128K + +void *of_flatten_dtb(void) +{ + void *fdt; + int ret; + + if (!root_node) + return NULL; + + fdt = malloc(DTB_SIZE); + if (!fdt) + return NULL; + + memset(fdt, 0, DTB_SIZE); + + ret = fdt_create(fdt, DTB_SIZE); + if (ret) + goto out_free; + + ret = fdt_finish_reservemap(fdt); + if (ret) + goto out_free; + + ret = __of_flatten_dtb(fdt, root_node); + if (ret) + goto out_free; + + fdt_finish(fdt); + + return fdt; + +out_free: + free(fdt); + + return NULL; +} + int of_device_is_stdout_path(struct device_d *dev) { struct device_node *dn; diff --git a/fs/fs.c b/fs/fs.c index 04331fc..f840516 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -67,6 +67,25 @@ EXPORT_SYMBOL(read_file); +int write_file(const char *filename, void *buf, size_t size) +{ + int fd, ret; + + fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT); + if (fd < 0) + return fd; + + ret = write_full(fd, buf, size); + + close(fd); + + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(write_file); + char *mkmodestr(unsigned long mode, char *str) { static const char *l = "xwr"; diff --git a/include/fs.h b/include/fs.h index 8ff7300..919daab 100644 --- a/include/fs.h +++ b/include/fs.h @@ -167,6 +167,11 @@ void *read_file(const char *filename, size_t *size); /* + * Write a buffer to a file. This file is newly created. + */ +int write_file(const char *filename, void *buf, size_t size); + +/* * This function turns 'path' into an absolute path and removes all occurrences * of "..", "." and double slashes. The returned string must be freed wit free(). */ diff --git a/include/of.h b/include/of.h index 58b4590..d3a310f 100644 --- a/include/of.h +++ b/include/of.h @@ -5,11 +5,9 @@ #include #include -extern struct fdt_header *barebox_fdt; - int fdt_print(struct fdt_header *working_fdt, const char *pathp); -struct fdt_header *of_get_fixed_tree(void); +struct fdt_header *of_get_fixed_tree(struct fdt_header *fdt); int of_fix_tree(struct fdt_header *fdt); int of_register_fixup(int (*fixup)(struct fdt_header *)); @@ -107,6 +105,12 @@ void of_print_nodes(struct device_node *node, int indent); int of_probe(void); int of_parse_dtb(struct fdt_header *fdt); +void of_free(struct device_node *node); +int of_unflatten_dtb(struct fdt_header *fdt); +struct device_node *of_new_node(struct device_node *parent, const char *name); +struct property *of_new_property(struct device_node *node, const char *name, + const void *data, int len); +void of_delete_property(struct property *pp); int of_property_read_string(struct device_node *np, const char *propname, const char **out_string); @@ -119,6 +123,7 @@ int of_alias_get_id(struct device_node *np, const char *stem); int of_device_is_stdout_path(struct device_d *dev); const char *of_get_model(void); +void *of_flatten_dtb(void); #else static inline int of_parse_partitions(const char *cdevname, struct device_node *node) @@ -145,6 +150,11 @@ { return NULL; } + +static inline void *of_flatten_dtb(void) +{ + return NULL; +} #endif #endif /* __OF_H */