diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c index 9f6fe20..178b365 100644 --- a/libfdt/fdt_sw.c +++ b/libfdt/fdt_sw.c @@ -57,9 +57,10 @@ static int fdt_sw_probe_(void *fdt) { - if (fdt_magic(fdt) != FDT_SW_MAGIC) + if (fdt_magic(fdt) == FDT_MAGIC) + return -FDT_ERR_BADSTATE; + else if (fdt_magic(fdt) != FDT_SW_MAGIC) return -FDT_ERR_BADMAGIC; - /* FIXME: should check more details about the header state */ return 0; } @@ -70,6 +71,61 @@ static int fdt_sw_probe_(void *fdt) return err; \ } +/* 'memrsv' state: Initial state after fdt_create() + * + * Allowed functions: + * fdt_add_reservmap_entry() + * fdt_finish_reservemap() [moves to 'struct' state] + */ +static int fdt_sw_probe_memrsv_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (fdt_off_dt_strings(fdt) != 0) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_MEMRSV(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ + return err; \ + } + +/* 'struct' state: Enter this state after fdt_finish_reservemap() + * + * Allowed functions: + * fdt_begin_node() + * fdt_end_node() + * fdt_property*() + * fdt_finish() [moves to 'complete' state] + */ +static int fdt_sw_probe_struct_(void *fdt) +{ + int err = fdt_sw_probe_(fdt); + if (err) + return err; + + if (fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) + return -FDT_ERR_BADSTATE; + return 0; +} + +#define FDT_SW_PROBE_STRUCT(fdt) \ + { \ + int err; \ + if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ + return err; \ + } + +/* 'complete' state: Enter this state after fdt_finish() + * + * Allowed functions: none + */ + static void *fdt_grab_space_(void *fdt, size_t len) { int offset = fdt_size_dt_struct(fdt); @@ -102,7 +158,7 @@ int fdt_create(void *buf, int bufsize) fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), sizeof(struct fdt_reserve_entry))); fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); - fdt_set_off_dt_strings(fdt, bufsize); + fdt_set_off_dt_strings(fdt, 0); return 0; } @@ -133,8 +189,9 @@ int fdt_resize(void *fdt, void *buf, int bufsize) memmove(buf, fdt, headsize); } - fdt_set_off_dt_strings(buf, bufsize); fdt_set_totalsize(buf, bufsize); + if (fdt_off_dt_strings(buf)) + fdt_set_off_dt_strings(buf, bufsize); return 0; } @@ -144,10 +201,7 @@ int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) struct fdt_reserve_entry *re; int offset; - FDT_SW_PROBE(fdt); - - if (fdt_size_dt_struct(fdt)) - return -FDT_ERR_BADSTATE; + FDT_SW_PROBE_MEMRSV(fdt); offset = fdt_off_dt_struct(fdt); if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) @@ -164,16 +218,23 @@ int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) int fdt_finish_reservemap(void *fdt) { - return fdt_add_reservemap_entry(fdt, 0, 0); + int err = fdt_add_reservemap_entry(fdt, 0, 0); + + if (err) + return err; + + fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); + return 0; } int fdt_begin_node(void *fdt, const char *name) { struct fdt_node_header *nh; - int namelen = strlen(name) + 1; + int namelen; - FDT_SW_PROBE(fdt); + FDT_SW_PROBE_STRUCT(fdt); + namelen = strlen(name) + 1; nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); if (! nh) return -FDT_ERR_NOSPACE; @@ -187,7 +248,7 @@ int fdt_end_node(void *fdt) { fdt32_t *en; - FDT_SW_PROBE(fdt); + FDT_SW_PROBE_STRUCT(fdt); en = fdt_grab_space_(fdt, FDT_TAGSIZE); if (! en) @@ -225,7 +286,7 @@ int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) struct fdt_property *prop; int nameoff; - FDT_SW_PROBE(fdt); + FDT_SW_PROBE_STRUCT(fdt); nameoff = fdt_find_add_string_(fdt, name); if (nameoff == 0) @@ -262,7 +323,7 @@ int fdt_finish(void *fdt) uint32_t tag; int offset, nextoffset; - FDT_SW_PROBE(fdt); + FDT_SW_PROBE_STRUCT(fdt); /* Add terminator */ end = fdt_grab_space_(fdt, sizeof(*end)); diff --git a/tests/.gitignore b/tests/.gitignore index 9e209d5..c79496f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -58,6 +58,7 @@ tmp.* /subnode_offset /supernode_atdepth_offset /sw_tree1 +/sw_states /truncated_property /utilfdt_test /value-labels diff --git a/tests/Makefile.tests b/tests/Makefile.tests index 262944a..ed7efbc 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -11,7 +11,7 @@ LIB_TESTS_L = get_mem_rsv \ addr_size_cells \ stringlist \ setprop_inplace nop_property nop_node \ - sw_tree1 \ + sw_tree1 sw_states \ move_and_save mangle-layout nopulate \ open_pack rw_tree1 set_name setprop del_property del_node \ appendprop1 appendprop2 propname_escapes \ diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 670eeca..61ec80b 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -345,6 +345,7 @@ libfdt_tests () { tree1_tests sw_tree1.test.dtb tree1_tests unfinished_tree1.test.dtb run_test dtbs_equal_ordered test_tree1.dtb sw_tree1.test.dtb + run_test sw_states # Resizing tests for mode in resize realloc; do diff --git a/tests/sw_states.c b/tests/sw_states.c new file mode 100644 index 0000000..2fd471d --- /dev/null +++ b/tests/sw_states.c @@ -0,0 +1,140 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for error handling with sequential write states + * Copyright (C) 2018 David Gibson, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include + +#include "tests.h" +#include "testdata.h" + +#define SPACE 65536 + +#define CHECK_OK(code) \ + do { \ + verbose_printf(" OK: %s\n", #code); \ + err = (code); \ + if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } while (0) + +#define CHECK_BADSTATE(code) \ + do { \ + verbose_printf("BAD: %s\n", #code); \ + err = (code); \ + if (err == 0) \ + FAIL(#code ": succeeded in bad state"); \ + else if (err != -FDT_ERR_BADSTATE) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } while (0) + +int main(int argc, char *argv[]) +{ + void *fdt = NULL; + int err; + + test_init(argc, argv); + + fdt = xmalloc(SPACE); + + err = fdt_create(fdt, SPACE); + if (err) + FAIL("fdt_create(): %s", fdt_strerror(err)); + + /* Memory reserve state */ + + CHECK_BADSTATE(fdt_begin_node(fdt, "")); + CHECK_BADSTATE(fdt_property_string(fdt, "bad-str", "TEST_STRING_1")); + CHECK_BADSTATE(fdt_end_node(fdt)); + CHECK_BADSTATE(fdt_finish(fdt)); + + CHECK_OK(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1)); + CHECK_OK(fdt_add_reservemap_entry(fdt, TEST_ADDR_2, TEST_SIZE_2)); + + CHECK_BADSTATE(fdt_begin_node(fdt, "")); + CHECK_BADSTATE(fdt_property_string(fdt, "bad-str", "TEST_STRING_1")); + CHECK_BADSTATE(fdt_end_node(fdt)); + CHECK_BADSTATE(fdt_finish(fdt)); + + CHECK_OK(fdt_finish_reservemap(fdt)); + + /* Structure state */ + + CHECK_BADSTATE(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1)); + CHECK_BADSTATE(fdt_finish_reservemap(fdt)); + + CHECK_OK(fdt_begin_node(fdt, "")); + CHECK_OK(fdt_property_string(fdt, "compatible", "test_tree1")); + CHECK_OK(fdt_property_u32(fdt, "prop-int", TEST_VALUE_1)); + CHECK_OK(fdt_property_u64(fdt, "prop-int64", TEST_VALUE64_1)); + CHECK_OK(fdt_property_string(fdt, "prop-str", TEST_STRING_1)); + CHECK_OK(fdt_property_u32(fdt, "#address-cells", 1)); + CHECK_OK(fdt_property_u32(fdt, "#size-cells", 0)); + + CHECK_OK(fdt_begin_node(fdt, "subnode@1")); + CHECK_OK(fdt_property_string(fdt, "compatible", "subnode1")); + CHECK_OK(fdt_property_u32(fdt, "reg", 1)); + CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1)); + CHECK_OK(fdt_begin_node(fdt, "subsubnode")); + CHECK_OK(fdt_property(fdt, "compatible", "subsubnode1\0subsubnode", + 23)); + CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_1)); + CHECK_OK(fdt_end_node(fdt)); + CHECK_OK(fdt_begin_node(fdt, "ss1")); + CHECK_OK(fdt_end_node(fdt)); + CHECK_OK(fdt_end_node(fdt)); + + CHECK_OK(fdt_begin_node(fdt, "subnode@2")); + CHECK_OK(fdt_property_u32(fdt, "reg", 2)); + CHECK_OK(fdt_property_cell(fdt, "linux,phandle", PHANDLE_1)); + CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2)); + CHECK_OK(fdt_property_u32(fdt, "#address-cells", 1)); + CHECK_OK(fdt_property_u32(fdt, "#size-cells", 0)); + CHECK_OK(fdt_begin_node(fdt, "subsubnode@0")); + CHECK_OK(fdt_property_u32(fdt, "reg", 0)); + CHECK_OK(fdt_property_cell(fdt, "phandle", PHANDLE_2)); + CHECK_OK(fdt_property(fdt, "compatible", "subsubnode2\0subsubnode", + 23)); + CHECK_OK(fdt_property_cell(fdt, "prop-int", TEST_VALUE_2)); + CHECK_OK(fdt_end_node(fdt)); + CHECK_OK(fdt_begin_node(fdt, "ss2")); + CHECK_OK(fdt_end_node(fdt)); + + CHECK_OK(fdt_end_node(fdt)); + + CHECK_OK(fdt_end_node(fdt)); + + CHECK_OK(fdt_finish(fdt)); + + /* Completed state */ + + CHECK_BADSTATE(fdt_add_reservemap_entry(fdt, TEST_ADDR_1, TEST_SIZE_1)); + CHECK_BADSTATE(fdt_finish_reservemap(fdt)); + CHECK_BADSTATE(fdt_begin_node(fdt, "")); + CHECK_BADSTATE(fdt_property_string(fdt, "bad-str", "TEST_STRING_1")); + CHECK_BADSTATE(fdt_end_node(fdt)); + CHECK_BADSTATE(fdt_finish(fdt)); + + PASS(); +}