Index: head/sys/boot/fdt/Makefile =================================================================== --- head/sys/boot/fdt/Makefile +++ head/sys/boot/fdt/Makefile @@ -10,7 +10,7 @@ fdt_empty_tree.c # Loader's fdt commands extension sources. -SRCS+= fdt_loader_cmd.c +SRCS+= fdt_loader_cmd.c fdt_overlay.c CFLAGS+= -I${.CURDIR}/../../contrib/libfdt/ -I${.CURDIR}/../common/ Index: head/sys/boot/fdt/fdt_loader_cmd.c =================================================================== --- head/sys/boot/fdt/fdt_loader_cmd.c +++ head/sys/boot/fdt/fdt_loader_cmd.c @@ -39,6 +39,7 @@ #include "bootstrap.h" #include "fdt_platform.h" +#include "fdt_overlay.h" #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ @@ -276,6 +277,128 @@ return (0); } +static int +fdt_load_dtb_overlay(const char * filename) +{ + struct preloaded_file *bfp, *oldbfp; + struct fdt_header header; + int err; + + debugf("fdt_load_dtb_overlay(%s)\n", filename); + + oldbfp = file_findfile(filename, "dtbo"); + + /* Attempt to load and validate a new dtb from a file. */ + if ((bfp = file_loadraw(filename, "dtbo", 1)) == NULL) { + printf("failed to load file '%s'\n", filename); + return (1); + } + + COPYOUT(bfp->f_addr, &header, sizeof(header)); + err = fdt_check_header(&header); + + if (err < 0) { + file_discard(bfp); + if (err == -FDT_ERR_BADVERSION) + printf("incompatible blob version: %d, should be: %d\n", + fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION); + + else + printf("error validating blob: %s\n", + fdt_strerror(err)); + return (1); + } + + /* A new dtb was validated, discard any previous file. */ + if (oldbfp) + file_discard(oldbfp); + + return (0); +} + +int +fdt_load_dtb_overlays(const char * filenames) +{ + char *names; + char *name; + char *comaptr; + + debugf("fdt_load_dtb_overlay(%s)\n", filenames); + + names = strdup(filenames); + if (names == NULL) + return (1); + name = names; + do { + comaptr = strchr(name, ','); + if (comaptr) + *comaptr = '\0'; + fdt_load_dtb_overlay(name); + name = comaptr + 1; + } while(comaptr); + + free(names); + return (0); +} + +void +fdt_apply_overlays() +{ + struct preloaded_file *fp; + size_t overlays_size, max_overlay_size, new_fdtp_size; + void *new_fdtp; + void *overlay; + int rv; + + if ((fdtp == NULL) || (fdtp_size == 0)) + return; + + overlays_size = 0; + max_overlay_size = 0; + for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) { + if (max_overlay_size < fp->f_size) + max_overlay_size = fp->f_size; + overlays_size += fp->f_size; + } + + /* Nothing to apply */ + if (overlays_size == 0) + return; + + /* It's actually more than enough */ + new_fdtp_size = fdtp_size + overlays_size; + new_fdtp = malloc(new_fdtp_size); + if (new_fdtp == NULL) { + printf("failed to allocate memory for DTB blob with overlays\n"); + return; + } + + overlay = malloc(max_overlay_size); + if (overlay == NULL) { + printf("failed to allocate memory for DTB blob with overlays\n"); + free(new_fdtp); + return; + } + + rv = fdt_open_into(fdtp, new_fdtp, new_fdtp_size); + if (rv != 0) { + printf("failed to open DTB blob for applying overlays\n"); + return; + } + + for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) { + printf("applying DTB overlay '%s'\n", fp->f_name); + COPYOUT(fp->f_addr, overlay, fp->f_size); + fdt_overlay_apply(new_fdtp, overlay, fp->f_size); + } + + free(fdtp); + fdtp = new_fdtp; + fdtp_size = new_fdtp_size; + + free(overlay); +} + int fdt_setup_fdtp() { Index: head/sys/boot/fdt/fdt_overlay.h =================================================================== --- head/sys/boot/fdt/fdt_overlay.h +++ head/sys/boot/fdt/fdt_overlay.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef FDT_OVERLAY_H +#define FDT_OVERLAY_H + +int fdt_overlay_apply(void *main_fdtp, void *overlay_fdtp, size_t overlay_length); + +#endif /* FDT_OVERLAY_H */ Index: head/sys/boot/fdt/fdt_overlay.c =================================================================== --- head/sys/boot/fdt/fdt_overlay.c +++ head/sys/boot/fdt/fdt_overlay.c @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "fdt_overlay.h" + +/* + * Get max phandle + */ +static uint32_t +fdt_max_phandle(void *fdtp) +{ + int o, depth; + uint32_t max_phandle, phandle; + + depth = 1; + o = fdt_path_offset(fdtp, "/"); + max_phandle = fdt_get_phandle(fdtp, o); + for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) { + phandle = fdt_get_phandle(fdtp, o); + if (max_phandle < phandle) + max_phandle = phandle; + } + + return max_phandle; +} + +/* + * Returns exact memory location specified by fixup in format + * /path/to/node:property:offset + */ +static void * +fdt_get_fixup_location(void *fdtp, const char *fixup) +{ + char *path, *prop, *offsetp, *endp; + int prop_offset, o, proplen; + void *result; + + result = 0; + + path = strdup(fixup); + prop = strchr(path, ':'); + if (prop == NULL) { + printf("missing property part in \"%s\"\n", fixup); + result = NULL; + goto out; + } + + *prop = 0; + prop++; + + offsetp = strchr(prop, ':'); + if (offsetp == NULL) { + printf("missing offset part in \"%s\"\n", fixup); + result = NULL; + goto out; + } + + *offsetp = 0; + offsetp++; + + prop_offset = strtoul(offsetp, &endp, 10); + if (*endp != '\0') { + printf("\"%s\" is not valid number\n", offsetp); + result = NULL; + goto out; + } + + o = fdt_path_offset(fdtp, path); + if (o < 0) { + printf("path \"%s\" not found\n", path); + result = NULL; + goto out; + } + + result = fdt_getprop_w(fdtp, o, prop, &proplen); + if (result == NULL){ + printf("property \"%s\" not found in \"%s\" node\n", prop, path); + result = NULL; + goto out; + } + + if (proplen < prop_offset + sizeof(uint32_t)) { + printf("%s: property length is too small for fixup\n", fixup); + result = NULL; + goto out; + } + + result = (char*)result + prop_offset; + +out: + free(path); + return (result); +} + +/* + * Process one entry in __fixups__ { } node + * @fixups is property value, array of NUL-terminated strings + * with fixup locations + * @fixups_len length of the fixups array in bytes + * @phandle is value for these locations + */ +static int +fdt_do_one_fixup(void *fdtp, const char *fixups, int fixups_len, int phandle) +{ + void *fixup_pos; + uint32_t val; + + val = cpu_to_fdt32(phandle); + + while (fixups_len > 0) { + fixup_pos = fdt_get_fixup_location(fdtp, fixups); + if (fixup_pos != NULL) + memcpy(fixup_pos, &val, sizeof(val)); + + fixups_len -= strlen(fixups) + 1; + fixups += strlen(fixups) + 1; + } + + return (0); +} + +/* + * Increase u32 value at pos by offset + */ +static void +fdt_increase_u32(void *pos, uint32_t offset) +{ + uint32_t val; + + memcpy(&val, pos, sizeof(val)); + val = cpu_to_fdt32(fdt32_to_cpu(val) + offset); + memcpy(pos, &val, sizeof(val)); +} + +/* + * Process local fixups + * @fixups is property value, array of NUL-terminated strings + * with fixup locations + * @fixups_len length of the fixups array in bytes + * @offset value these locations should be increased by + */ +static int +fdt_do_local_fixup(void *fdtp, const char *fixups, int fixups_len, int offset) +{ + void *fixup_pos; + + while (fixups_len > 0) { + fixup_pos = fdt_get_fixup_location(fdtp, fixups); + if (fixup_pos != NULL) + fdt_increase_u32(fixup_pos, offset); + + fixups_len -= strlen(fixups) + 1; + fixups += strlen(fixups) + 1; + } + + return (0); +} + +/* + * Increase node phandle by phandle_offset + */ +static void +fdt_increase_phandle(void *fdtp, int node_offset, uint32_t phandle_offset) +{ + int proplen; + void *phandle_pos, *node_pos; + + node_pos = (char*)fdtp + node_offset; + + phandle_pos = fdt_getprop_w(fdtp, node_offset, "phandle", &proplen); + if (phandle_pos) + fdt_increase_u32(phandle_pos, phandle_offset); + phandle_pos = fdt_getprop_w(fdtp, node_offset, "linux,phandle", &proplen); + if (phandle_pos) + fdt_increase_u32(phandle_pos, phandle_offset); +} + +/* + * Increase all phandles by offset + */ +static void +fdt_increase_phandles(void *fdtp, uint32_t offset) +{ + int o, depth; + + o = fdt_path_offset(fdtp, "/"); + for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) { + fdt_increase_phandle(fdtp, o, offset); + } +} + +/* + * Overlay one node defined by over + */ +static void +fdt_overlay_node(void *main_fdtp, int target_o, void *overlay_fdtp, int overlay_o) +{ + int len, o, depth; + const char *name; + const void *val; + int target_subnode_o; + + /* Overlay properties */ + for (o = fdt_first_property_offset(overlay_fdtp, overlay_o); + o >= 0; o = fdt_next_property_offset(overlay_fdtp, o)) { + val = fdt_getprop_by_offset(overlay_fdtp, o, &name, &len); + if (val) + fdt_setprop(main_fdtp, target_o, name, val, len); + } + + /* Now overlay nodes */ + o = overlay_o; + for (depth = 0; (o >= 0) && (depth >= 0); + o = fdt_next_node(overlay_fdtp, o, &depth)) { + if (depth != 1) + continue; + /* Check if there is node with the same name */ + name = fdt_get_name(overlay_fdtp, o, NULL); + target_subnode_o = fdt_subnode_offset(main_fdtp, target_o, name); + if (target_subnode_o < 0) { + /* create new subnode and run merge recursively */ + target_subnode_o = fdt_add_subnode(main_fdtp, target_o, name); + if (target_subnode_o < 0) { + printf("failed to create subnode \"%s\": %d\n", + name, target_subnode_o); + return; + } + } + + fdt_overlay_node(main_fdtp, target_subnode_o, + overlay_fdtp, o); + } +} + +/* + * Apply one overlay fragment + */ +static void +fdt_apply_fragment(void *main_fdtp, void *overlay_fdtp, int fragment_o) +{ + uint32_t target; + const char *target_path; + const void *val; + int target_node_o, overlay_node_o; + + target_node_o = -1; + val = fdt_getprop(overlay_fdtp, fragment_o, "target", NULL); + if (val) { + memcpy(&target, val, sizeof(target)); + target = fdt32_to_cpu(target); + target_node_o = fdt_node_offset_by_phandle(main_fdtp, target); + if (target_node_o < 0) { + printf("failed to find target %04x\n", target); + return; + } + } + + if (target_node_o < 0) { + target_path = fdt_getprop(overlay_fdtp, fragment_o, "target-path", NULL); + if (target_path == NULL) + return; + + target_node_o = fdt_path_offset(main_fdtp, target_path); + if (target_node_o < 0) { + printf("failed to find target-path %s\n", target_path); + return; + } + } + + if (target_node_o < 0) + return; + + overlay_node_o = fdt_subnode_offset(overlay_fdtp, fragment_o, "__overlay__"); + if (overlay_node_o < 0) { + printf("missing __overlay__ sub-node\n"); + return; + } + + fdt_overlay_node(main_fdtp, target_node_o, overlay_fdtp, overlay_node_o); +} + +/* + * Handle __fixups__ node in overlay DTB + */ +static int +fdt_overlay_do_fixups(void *main_fdtp, void *overlay_fdtp) +{ + int main_symbols_o, symbol_o, overlay_fixups_o; + int fixup_prop_o; + int len; + const char *fixups, *name; + const char *symbol_path; + uint32_t phandle; + + main_symbols_o = fdt_path_offset(main_fdtp, "/__symbols__"); + overlay_fixups_o = fdt_path_offset(overlay_fdtp, "/__fixups__"); + + if (main_symbols_o < 0) + return (-1); + if (overlay_fixups_o < 0) + return (-1); + + for (fixup_prop_o = fdt_first_property_offset(overlay_fdtp, overlay_fixups_o); + fixup_prop_o >= 0; + fixup_prop_o = fdt_next_property_offset(overlay_fdtp, fixup_prop_o)) { + fixups = fdt_getprop_by_offset(overlay_fdtp, fixup_prop_o, &name, &len); + symbol_path = fdt_getprop(main_fdtp, main_symbols_o, name, NULL); + if (symbol_path == NULL) { + printf("couldn't find \"%s\" symbol in main dtb\n", name); + return (-1); + } + symbol_o = fdt_path_offset(main_fdtp, symbol_path); + if (symbol_o < 0) { + printf("couldn't find \"%s\" path in main dtb\n", symbol_path); + return (-1); + } + phandle = fdt_get_phandle(main_fdtp, symbol_o); + if (fdt_do_one_fixup(overlay_fdtp, fixups, len, phandle) < 0) + return (-1); + } + + return (0); +} + +/* + * Handle __local_fixups__ node in overlay DTB + */ +static int +fdt_overlay_do_local_fixups(void *main_fdtp, void *overlay_fdtp) +{ + int overlay_local_fixups_o; + int len; + const char *fixups; + uint32_t phandle_offset; + + overlay_local_fixups_o = fdt_path_offset(overlay_fdtp, "/__local_fixups__"); + + if (overlay_local_fixups_o < 0) + return (-1); + + phandle_offset = fdt_max_phandle(main_fdtp); + fdt_increase_phandles(overlay_fdtp, phandle_offset); + fixups = fdt_getprop_w(overlay_fdtp, overlay_local_fixups_o, "fixup", &len); + if (fixups) { + if (fdt_do_local_fixup(overlay_fdtp, fixups, len, phandle_offset) < 0) + return (-1); + } + + return (0); +} + +/* + * Apply all fragments to main DTB + */ +static int +fdt_overlay_apply_fragments(void *main_fdtp, void *overlay_fdtp) +{ + int o, depth; + + o = fdt_path_offset(overlay_fdtp, "/"); + for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(overlay_fdtp, o, &depth)) { + if (depth != 1) + continue; + + fdt_apply_fragment(main_fdtp, overlay_fdtp, o); + } + + return (0); +} + +int +fdt_overlay_apply(void *main_fdtp, void *overlay_fdtp, size_t overlay_length) +{ + void *overlay_copy; + int rv; + + rv = 0; + + /* We modify overlay in-place, so we need writable copy */ + overlay_copy = malloc(overlay_length); + if (overlay_copy == NULL) { + printf("failed to allocate memory for overlay copy\n"); + return (-1); + } + + memcpy(overlay_copy, overlay_fdtp, overlay_length); + + if (fdt_overlay_do_fixups(main_fdtp, overlay_copy) < 0) { + printf("failed to perform fixups in overlay\n"); + rv = -1; + goto out; + } + + if (fdt_overlay_do_local_fixups(main_fdtp, overlay_copy) < 0) { + printf("failed to perform local fixups in overlay\n"); + rv = -1; + goto out; + } + + if (fdt_overlay_apply_fragments(main_fdtp, overlay_copy) < 0) { + printf("failed to apply fragments\n"); + rv = -1; + } + +out: + free(overlay_copy); + + return (rv); +} Index: head/sys/boot/fdt/fdt_platform.h =================================================================== --- head/sys/boot/fdt/fdt_platform.h +++ head/sys/boot/fdt/fdt_platform.h @@ -43,8 +43,10 @@ void fdt_fixup_ethernet(const char *, char *, int); void fdt_fixup_memory(struct fdt_mem_region *, size_t); void fdt_fixup_stdout(const char *); +void fdt_apply_overlays(void); int fdt_load_dtb_addr(struct fdt_header *); int fdt_load_dtb_file(const char *); +int fdt_load_dtb_overlays(const char *); int fdt_setup_fdtp(void); /* The platform library needs to implement these functions */ Index: head/sys/boot/uboot/fdt/uboot_fdt.c =================================================================== --- head/sys/boot/uboot/fdt/uboot_fdt.c +++ head/sys/boot/uboot/fdt/uboot_fdt.c @@ -45,6 +45,7 @@ struct fdt_header *hdr; const char *s; char *p; + int rv; /* * If the U-boot environment contains a variable giving the address of a @@ -68,6 +69,8 @@ } } + rv = 1; + /* * Try to get FDT filename first from loader env and then from u-boot env */ @@ -79,11 +82,21 @@ if (s != NULL && *s != '\0') { if (fdt_load_dtb_file(s) == 0) { printf("Loaded DTB from file '%s'.\n", s); - return (0); + rv = 0; } } - return (1); + if (rv == 0) { + s = getenv("fdt_overlays"); + if (s == NULL) + s = ub_env_get("fdt_overlays"); + if (s != NULL && *s != '\0') { + printf("Loading DTB overlays: '%s'\n", s); + fdt_load_dtb_overlays(s); + } + } + + return (rv); } void @@ -99,6 +112,9 @@ eth_no = 0; ethstr = NULL; + /* Apply overlays before anything else */ + fdt_apply_overlays(); + /* Acquire sys_info */ si = ub_get_sys_info();