diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c index f422754..6a80485 100644 --- a/libfdt/fdt_sw.c +++ b/libfdt/fdt_sw.c @@ -107,6 +107,38 @@ int fdt_create(void *buf, int bufsize) return 0; } +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_CHECK_HEADER(fdt); + + headsize = fdt_off_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if ((headsize + tailsize) > bufsize) + return -FDT_ERR_NOSPACE; + + oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; + newtail = (char *)buf + bufsize - tailsize; + + /* Two cases to avoid clobbering data if the old and new + * buffers partially overlap */ + if (buf <= fdt) { + memmove(buf, fdt, headsize); + memmove(newtail, oldtail, tailsize); + } else { + memmove(newtail, oldtail, tailsize); + memmove(buf, fdt, headsize); + } + + fdt_set_off_dt_strings(buf, bufsize); + fdt_set_totalsize(buf, bufsize); + + return 0; +} + int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) { struct fdt_reserve_entry *re; diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index 02baa84..c4d5a91 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -1023,6 +1023,7 @@ int fdt_nop_node(void *fdt, int nodeoffset); /**********************************************************************/ int fdt_create(void *buf, int bufsize); +int fdt_resize(void *fdt, void *buf, int bufsize); int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); int fdt_finish_reservemap(void *fdt); int fdt_begin_node(void *fdt, const char *name); diff --git a/tests/run_tests.sh b/tests/run_tests.sh index c0a136b..a8905a7 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -190,6 +190,14 @@ libfdt_tests () { tree1_tests unfinished_tree1.test.dtb run_test dtbs_equal_ordered test_tree1.dtb sw_tree1.test.dtb + # Resizing tests + for mode in resize realloc; do + run_test sw_tree1 $mode + tree1_tests sw_tree1.test.dtb + tree1_tests unfinished_tree1.test.dtb + run_test dtbs_equal_ordered test_tree1.dtb sw_tree1.test.dtb + done + # fdt_move tests for tree in test_tree1.dtb sw_tree1.test.dtb unfinished_tree1.test.dtb; do rm -f moved.$tree shunted.$tree deshunted.$tree diff --git a/tests/sw_tree1.c b/tests/sw_tree1.c index e9ea7e7..de00707 100644 --- a/tests/sw_tree1.c +++ b/tests/sw_tree1.c @@ -31,22 +31,87 @@ #define SPACE 65536 -#define CHECK(code) \ - { \ - err = (code); \ - if (err) \ - FAIL(#code ": %s", fdt_strerror(err)); \ +static enum { + FIXED = 0, + RESIZE, + REALLOC, +} alloc_mode; + +static void realloc_fdt(void **fdt, size_t *size) +{ + switch (alloc_mode) { + case FIXED: + if (!(*fdt)) + fdt = xmalloc(*size); + else + FAIL("Ran out of space"); + return; + + case RESIZE: + if (!(*fdt)) { + fdt = xmalloc(SPACE); + } else if (*size < SPACE) { + *size += 1; + fdt_resize(*fdt, *fdt, *size); + } else { + FAIL("Ran out of space"); + } + return; + + case REALLOC: + *size += 1; + *fdt = xrealloc(*fdt, *size); + fdt_resize(*fdt, *fdt, *size); + return; + + default: + CONFIG("Bad allocation mode"); } +} + +#define CHECK(code) \ + do { \ + err = (code); \ + if (err == -FDT_ERR_NOSPACE) \ + realloc_fdt(&fdt, &size); \ + else if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } while (err != 0) int main(int argc, char *argv[]) { - void *fdt; + void *fdt = NULL; + size_t size; int err; test_init(argc, argv); - fdt = xmalloc(SPACE); - CHECK(fdt_create(fdt, SPACE)); + if (argc == 1) { + alloc_mode = FIXED; + size = SPACE; + } else if (argc == 2) { + if (streq(argv[1], "resize")) { + alloc_mode = REALLOC; + size = 0; + } else if (streq(argv[1], "realloc")) { + alloc_mode = REALLOC; + size = 0; + } else { + char *endp; + + size = strtoul(argv[1], &endp, 0); + if (*endp == '\0') + alloc_mode = FIXED; + else + CONFIG("Bad allocation mode \"%s\" specified", + argv[1]); + } + } + + realloc_fdt(&fdt, &size); + + fdt = xmalloc(size); + CHECK(fdt_create(fdt, size)); CHECK(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1)); CHECK(fdt_add_reservemap_entry(fdt, TEST_ADDR_2, TEST_SIZE_2));