From 7ba551f96629a5273370329658a6e6b3a87068aa Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 1 Dec 2006 16:59:43 +1100 Subject: [PATCH] libfdt: Read-write support This patch adds support for random access, read-write support on flat trees. --- Makefile | 2 +- fdt_rw.c | 310 +++++++++++++++++++++++++++++++++++++++++++ fdt_wip.c | 25 ++-- libfdt.h | 28 ++-- libfdt_internal.h | 8 ++ tests/Makefile | 3 +- tests/del_node.c | 114 ++++++++++++++++ tests/del_property.c | 88 ++++++++++++ tests/open_pack.c | 68 ++++++++++ tests/run_tests.sh | 13 ++ tests/rw_tree1.c | 98 ++++++++++++++ tests/setprop.c | 75 +++++++++++ tests/tests.h | 2 + 13 files changed, 815 insertions(+), 19 deletions(-) create mode 100644 fdt_rw.c create mode 100644 tests/del_node.c create mode 100644 tests/del_property.c create mode 100644 tests/open_pack.c create mode 100644 tests/rw_tree1.c create mode 100644 tests/setprop.c diff --git a/Makefile b/Makefile index 0445192..d464a82 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PREFIX = /usr/local TARGETLIBS = libfdt.a -LIBOBJS = fdt.o fdt_ro.o fdt_wip.o fdt_sw.o +LIBOBJS = fdt.o fdt_ro.o fdt_wip.o fdt_sw.o fdt_rw.o SOURCE = $(shell find . -maxdepth 1 ! -name version.h -a -name '*.[h]') SOURCE += *.c Makefile diff --git a/fdt_rw.c b/fdt_rw.c new file mode 100644 index 0000000..45f1fa4 --- /dev/null +++ b/fdt_rw.c @@ -0,0 +1,310 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * 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 "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int rw_check_header(struct fdt_header *fdt) +{ + int err; + + if ((err = _fdt_check_header(fdt))) + return err; + if (fdt_version(fdt) < 0x11) + return FDT_ERR_BADVERSION; + if (fdt_off_mem_rsvmap(fdt) < ALIGN(sizeof(*fdt), 8)) + return FDT_ERR_BADLAYOUT; + if (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + sizeof(struct fdt_reserve_entry))) + return FDT_ERR_BADLAYOUT; + if (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt))) + return FDT_ERR_BADLAYOUT; + if (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))) + return FDT_ERR_BADLAYOUT; + return 0; +} + +#define RW_OFFSET_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = rw_check_header(fdt)) != 0) \ + return OFFSET_ERROR(err); \ + } + +static inline int _blob_data_size(struct fdt_header *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int _blob_splice(struct fdt_header *fdt, void *p, int oldlen, int newlen) +{ + void *blob = fdt; + void *end = blob + _blob_data_size(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > (blob + fdt_totalsize(fdt))) + return FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int _blob_splice_struct(struct fdt_header *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = _blob_splice(fdt, p, oldlen, newlen))) + return err; + + fdt->size_dt_struct = cpu_to_fdt32(fdt_size_dt_struct(fdt) + delta); + fdt->off_dt_strings = cpu_to_fdt32(fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _blob_splice_string(struct fdt_header *fdt, int newlen) +{ + void *blob = fdt; + void *p = blob + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = _blob_splice(fdt, p, 0, newlen))) + return err; + + fdt->size_dt_strings = cpu_to_fdt32(fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +static int _find_add_string(struct fdt_header *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = _blob_splice_string(fdt, len); + if (err) + return -err; + + memcpy(new, s, len); + return (new - strtab); +} + +static struct fdt_property *_resize_property(struct fdt_header *fdt, int nodeoffset, + const char *name, int len) +{ + struct fdt_property *prop; + int oldlen; + int err; + + prop = _fdt_getprop(fdt, nodeoffset, name, &oldlen); + if ((err = fdt_ptr_error(prop))) + return PTR_ERROR(err); + + if ((err = _blob_splice_struct(fdt, prop->data, + ALIGN(oldlen, FDT_TAGSIZE), + ALIGN(len, FDT_TAGSIZE)))) + return PTR_ERROR(err); + + prop->len = cpu_to_fdt32(len); + return prop; +} + +static struct fdt_property *_add_property(struct fdt_header *fdt, int nodeoffset, + const char *name, int len) +{ + uint32_t tag; + struct fdt_property *prop; + int proplen; + int nextoffset; + int namestroff; + int err; + + tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return PTR_ERROR(FDT_ERR_BADOFFSET); + + namestroff = _find_add_string(fdt, name); + if (namestroff < 0) + return PTR_ERROR(-namestroff); + + prop = fdt_offset_ptr(fdt, nextoffset, 0); + if ((err = fdt_ptr_error(prop))) + return PTR_ERROR(err); + + proplen = sizeof(*prop) + ALIGN(len, FDT_TAGSIZE); + + err = _blob_splice_struct(fdt, prop, 0, proplen); + if (err) + return PTR_ERROR(err); + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(namestroff); + prop->len = cpu_to_fdt32(len); + return prop; +} + +int fdt_setprop(struct fdt_header *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + if ((err = rw_check_header(fdt))) + return err; + + prop = _resize_property(fdt, nodeoffset, name, len); + err = fdt_ptr_error(prop); + if (err == FDT_ERR_NOTFOUND) { + prop = _add_property(fdt, nodeoffset, name, len); + err = fdt_ptr_error(prop); + } + if (err) + return err; + + memcpy(prop->data, val, len); + return 0; +} + +int fdt_delprop(struct fdt_header *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + int err; + + RW_OFFSET_CHECK_HEADER(fdt); + + prop = _fdt_getprop(fdt, nodeoffset, name, &len); + if ((err = fdt_ptr_error(prop))) + return err; + + proplen = sizeof(*prop) + ALIGN(len, FDT_TAGSIZE); + return _blob_splice_struct(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(struct fdt_header *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + uint32_t *endtag; + + RW_OFFSET_CHECK_HEADER(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if ((err = fdt_offset_error(offset)) == 0) + return OFFSET_ERROR(FDT_ERR_EXISTS); + else if (err != FDT_ERR_NOTFOUND) + return OFFSET_ERROR(err); + + /* Try to place the new node after the parent's properties */ + _fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = _fdt_next_tag(fdt, offset, &nextoffset); + } while (tag == FDT_PROP); + + nh = fdt_offset_ptr(fdt, offset, 0); + if ((err = fdt_ptr_error(nh))) + return OFFSET_ERROR(err); + + nodelen = sizeof(*nh) + ALIGN(namelen+1, FDT_TAGSIZE) + FDT_TAGSIZE; + err = _blob_splice_struct(fdt, nh, 0, nodelen); + if (err) + return OFFSET_ERROR(err); + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, ALIGN(namelen+1, FDT_TAGSIZE)); + memcpy(nh->name, name, namelen); + endtag = (uint32_t *)((void *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(struct fdt_header *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(struct fdt_header *fdt, int nodeoffset) +{ + struct fdt_node_header *nh; + int endoffset; + int err; + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if ((err = fdt_offset_error(endoffset))) + return err; + + nh = fdt_offset_ptr(fdt, nodeoffset, 0); + if ((err = fdt_ptr_error(nh))) + return err; + + return _blob_splice_struct(fdt, nh, endoffset - nodeoffset, 0); +} + +struct fdt_header *fdt_open_into(struct fdt_header *fdt, void *buf, int bufsize) +{ + int err; + + fdt = fdt_move(fdt, buf, bufsize); + if ((err = fdt_ptr_error(fdt))) + return PTR_ERROR(err); + + fdt->totalsize = cpu_to_fdt32(bufsize); + + /* FIXME: re-order if necessary */ + + err = rw_check_header(fdt); + if (err) + return PTR_ERROR(err); + + return fdt; +} + +struct fdt_header *fdt_pack(struct fdt_header *fdt) +{ + int err; + + err = rw_check_header(fdt); + if (err) + return PTR_ERROR(err); + + /* FIXME: pack components */ + + fdt->totalsize = cpu_to_fdt32(_blob_data_size(fdt)); + return fdt; +} diff --git a/fdt_wip.c b/fdt_wip.c index c8d07b3..8d68a96 100644 --- a/fdt_wip.c +++ b/fdt_wip.c @@ -64,26 +64,22 @@ int fdt_nop_property(struct fdt_header *fdt, int nodeoffset, const char *name) return 0; } -int fdt_nop_node(struct fdt_header *fdt, int nodeoffset) +int _fdt_node_end_offset(struct fdt_header *fdt, int nodeoffset) { int level = 0; - int err = 0; uint32_t tag; int offset, nextoffset; tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset); if (tag != FDT_BEGIN_NODE) return FDT_ERR_BADOFFSET; - do { offset = nextoffset; tag = _fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_END: - level = -1; - err = FDT_ERR_TRUNCATED; - break; + return offset; case FDT_BEGIN_NODE: level++; @@ -102,7 +98,18 @@ int fdt_nop_node(struct fdt_header *fdt, int nodeoffset) } } while (level >= 0); - nop_region(fdt_offset_ptr(fdt, nodeoffset, 0), nextoffset - nodeoffset); - - return err; + return nextoffset; +} + +int fdt_nop_node(struct fdt_header *fdt, int nodeoffset) +{ + int endoffset; + int err; + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if ((err = fdt_offset_error(endoffset))) + return err; + + nop_region(fdt_offset_ptr(fdt, nodeoffset, 0), endoffset - nodeoffset); + return 0; } diff --git a/libfdt.h b/libfdt.h index a3da19c..3872eea 100644 --- a/libfdt.h +++ b/libfdt.h @@ -40,8 +40,10 @@ #define FDT_ERR_BADSTATE 11 #define FDT_ERR_SIZE_MISMATCH 12 #define FDT_ERR_INTERNAL 13 +#define FDT_ERR_BADLAYOUT 14 +#define FDT_ERR_EXISTS 15 -#define FDT_ERR_MAX 13 +#define FDT_ERR_MAX 14 #define fdt_magic(fdt) (fdt32_to_cpu(fdt)->magic) #define fdt_totalsize(fdt) (fdt32_to_cpu(fdt)->totalsize) @@ -111,13 +113,23 @@ int fdt_property(struct fdt_header *fdt, const char *name, const void *val, int int fdt_end_node(struct fdt_header *fdt); int fdt_finish(struct fdt_header *fdt); -#if 0 /* Read-write functions */ -struct fdt_header *fdt_open(struct fdt_header *fdt, int bufsize); -int fdt_add_subnode(struct fdt_header *fdtx, void *node, const char *name); -int fdt_set_property(struct fdt_header *fdtx, void *node, const char *name, - const void *val, int len); -int fdt_del_property(struct fdt_header *fdtx, void *node, const char *name); -#endif +struct fdt_header *fdt_open_into(struct fdt_header *fdt, void *buf, int bufsize); +struct fdt_header *fdt_pack(struct fdt_header *fdt); + +int fdt_setprop(struct fdt_header *fdt, int nodeoffset, const char *name, + const void *val, int len); +#define fdt_setprop_typed(fdt, nodeoffset, name, val) \ + ({ \ + typeof(val) x = (val); \ + fdt_setprop((fdt), (nodeoffset), (name), &x, sizeof(x)); \ + }) +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) +int fdt_delprop(struct fdt_header *fdt, int nodeoffset, const char *name); +int fdt_add_subnode_namelen(struct fdt_header *fdt, int parentoffset, + const char *name, int namelen); +int fdt_add_subnode(struct fdt_header *fdt, int parentoffset, const char *name); +int fdt_del_node(struct fdt_header *fdt, int nodeoffset); #endif /* _LIBFDT_H */ diff --git a/libfdt_internal.h b/libfdt_internal.h index 67ef91d..0d3e0a5 100644 --- a/libfdt_internal.h +++ b/libfdt_internal.h @@ -26,11 +26,19 @@ #define memeq(p, q, n) (memcmp((p), (q), (n)) == 0) #define streq(p, q) (strcmp((p), (q)) == 0) +static inline int _ptr_offset(struct fdt_header *fdt, void *p) +{ + void *blob = fdt; + + return (p - blob) - fdt_off_dt_struct(fdt); +} + int _fdt_check_header(const struct fdt_header *fdt); uint32_t _fdt_next_tag(const struct fdt_header *fdt, int startoffset, int *nextoffset); struct fdt_property *_fdt_getprop(const struct fdt_header *fdt, int nodeoffset, const char *name, int *lenp); const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); +int _fdt_node_end_offset(struct fdt_header *fdt, int nodeoffset); #define OFFSET_ERROR(code) -(code) #define PTR_ERROR(code) (void *)(-(code)) diff --git a/tests/Makefile b/tests/Makefile index b430a68..c9271a7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,7 +4,8 @@ LIB_TESTS = root_node property_offset subnode_offset path_offset getprop \ notfound \ setprop_inplace nop_property nop_node \ sw_tree1 \ - move_and_save + move_and_save \ + open_pack rw_tree1 setprop del_property del_node TESTS = $(LIB_TESTS) UTILS = dumptrees diff --git a/tests/del_node.c b/tests/del_node.c new file mode 100644 index 0000000..71d6831 --- /dev/null +++ b/tests/del_node.c @@ -0,0 +1,114 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * 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" + +int main(int argc, char *argv[]) +{ + struct fdt_header *fdt; + int subnode1_offset, subnode2_offset, subsubnode2_offset; + int err; + int oldsize, delsize, newsize; + + test_init(argc, argv); + fdt = load_blob_arg(argc, argv); + + oldsize = fdt_totalsize(fdt); + + subnode1_offset = fdt_path_offset(fdt, "/subnode1"); + if ((err = fdt_offset_error(subnode1_offset))) + FAIL("Couldn't find \"/subnode1\": %s", fdt_strerror(err)); + check_getprop_typed(fdt, subnode1_offset, "prop-int", TEST_VALUE_1); + + subnode2_offset = fdt_path_offset(fdt, "/subnode2"); + if ((err = fdt_offset_error(subnode2_offset))) + FAIL("Couldn't find \"/subnode2\": %s", fdt_strerror(err)); + check_getprop_typed(fdt, subnode2_offset, "prop-int", TEST_VALUE_2); + + subsubnode2_offset = fdt_path_offset(fdt, "/subnode2/subsubnode"); + if ((err = fdt_offset_error(subsubnode2_offset))) + FAIL("Couldn't find \"/subnode2/subsubnode\": %s", + fdt_strerror(err)); + check_getprop_typed(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2); + + err = fdt_del_node(fdt, subnode1_offset); + if (err) + FAIL("fdt_del_node(subnode1): %s", fdt_strerror(err)); + + subnode1_offset = fdt_path_offset(fdt, "/subnode1"); + if ((err = fdt_offset_error(subnode1_offset)) != FDT_ERR_NOTFOUND) + FAIL("fdt_path_offset(subnode1) returned \"%s\" instead of \"%s\"", + fdt_strerror(err), fdt_strerror(FDT_ERR_NOTFOUND)); + + subnode2_offset = fdt_path_offset(fdt, "/subnode2"); + if ((err = fdt_offset_error(subnode2_offset))) + FAIL("Couldn't find \"/subnode2\": %s", fdt_strerror(err)); + check_getprop_typed(fdt, subnode2_offset, "prop-int", TEST_VALUE_2); + + subsubnode2_offset = fdt_path_offset(fdt, "/subnode2/subsubnode"); + if ((err = fdt_offset_error(subsubnode2_offset))) + FAIL("Couldn't find \"/subnode2/subsubnode\": %s", + fdt_strerror(err)); + check_getprop_typed(fdt, subsubnode2_offset, "prop-int", TEST_VALUE_2); + + err = fdt_del_node(fdt, subnode2_offset); + if (err) + FAIL("fdt_del_node(subnode2): %s", fdt_strerror(err)); + + subnode1_offset = fdt_path_offset(fdt, "/subnode1"); + if ((err = fdt_offset_error(subnode1_offset)) != FDT_ERR_NOTFOUND) + FAIL("fdt_path_offset(subnode1) returned \"%s\" instead of \"%s\"", + fdt_strerror(err), fdt_strerror(FDT_ERR_NOTFOUND)); + + subnode2_offset = fdt_path_offset(fdt, "/subnode2"); + if ((err = fdt_offset_error(subnode2_offset)) != FDT_ERR_NOTFOUND) + FAIL("fdt_path_offset(subnode2) returned \"%s\" instead of \"%s\"", + fdt_strerror(err), fdt_strerror(FDT_ERR_NOTFOUND)); + + subsubnode2_offset = fdt_path_offset(fdt, "/subnode2/subsubnode"); + if ((err = fdt_offset_error(subsubnode2_offset)) != FDT_ERR_NOTFOUND) + FAIL("fdt_path_offset(subsubnode2) returned \"%s\" instead of \"%s\"", + fdt_strerror(err), fdt_strerror(FDT_ERR_NOTFOUND)); + + delsize = fdt_totalsize(fdt); + + fdt = fdt_pack(fdt); + if ((err = fdt_ptr_error(fdt))) + FAIL("fdt_pack(): %s", fdt_strerror(err)); + + newsize = fdt_totalsize(fdt); + + verbose_printf("oldsize = %d, delsize = %d, newsize = %d\n", + oldsize, delsize, newsize); + + if (newsize >= oldsize) + FAIL("Tree failed to shrink after deletions"); + + PASS(); +} diff --git a/tests/del_property.c b/tests/del_property.c new file mode 100644 index 0000000..94bf47a --- /dev/null +++ b/tests/del_property.c @@ -0,0 +1,88 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_delprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * 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" + +int main(int argc, char *argv[]) +{ + struct fdt_header *fdt; + uint32_t *intp; + char *strp; + int err; + int oldsize, delsize, newsize; + + test_init(argc, argv); + fdt = load_blob_arg(argc, argv); + + oldsize = fdt_totalsize(fdt); + + intp = check_getprop_typed(fdt, 0, "prop-int", TEST_VALUE_1); + verbose_printf("int value was 0x%08x\n", *intp); + + err = fdt_delprop(fdt, 0, "prop-int"); + if (err) + FAIL("Failed to delete \"prop-int\": %s", fdt_strerror(err)); + + intp = fdt_getprop(fdt, 0, "prop-int", NULL); + err = fdt_ptr_error(intp); + if (! err) + FAIL("prop-int still present after deletion"); + if (err != FDT_ERR_NOTFOUND) + FAIL("Unexpected error on second getprop: %s", fdt_strerror(err)); + + strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1, + TEST_STRING_1); + verbose_printf("string value was \"%s\"\n", strp); + err = fdt_delprop(fdt, 0, "prop-str"); + if (err) + FAIL("Failed to delete \"prop-str\": %s", fdt_strerror(err)); + + strp = fdt_getprop(fdt, 0, "prop-str", NULL); + err = fdt_ptr_error(strp); + if (! err) + FAIL("prop-str still present after deletion"); + if (err != FDT_ERR_NOTFOUND) + FAIL("Unexpected error on second getprop: %s", fdt_strerror(err)); + + delsize = fdt_totalsize(fdt); + + fdt = fdt_pack(fdt); + if ((err = fdt_ptr_error(fdt))) + FAIL("fdt_pack(): %s\n", fdt_strerror(err)); + + newsize = fdt_totalsize(fdt); + + verbose_printf("oldsize = %d, delsize = %d, newsize = %d\n", + oldsize, delsize, newsize); + + if (newsize >= oldsize) + FAIL("Tree failed to shrink after deletions"); + + PASS(); +} diff --git a/tests/open_pack.c b/tests/open_pack.c new file mode 100644 index 0000000..c651694 --- /dev/null +++ b/tests/open_pack.c @@ -0,0 +1,68 @@ +/* + * libfdt - Flat Device Tree manipulation + * Basic testcase for read-only access + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * 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" + +int main(int argc, char *argv[]) +{ + struct fdt_header *fdt, *fdt1, *fdt2; + void *buf; + int oldsize, bufsize, packsize; + int err; + const char *inname; + char outname[PATH_MAX]; + + test_init(argc, argv); + fdt = load_blob_arg(argc, argv); + inname = argv[1]; + + oldsize = fdt_totalsize(fdt); + + bufsize = oldsize * 2; + + buf = xmalloc(bufsize); + + fdt1 = fdt_open_into(fdt, buf, bufsize); + if ((err = fdt_ptr_error(fdt1))) + FAIL("fdt_open_into(): %s", fdt_strerror(err)); + sprintf(outname, "opened.%s", inname); + save_blob(outname, fdt1); + + fdt2 = fdt_pack(fdt1); + if ((err = fdt_ptr_error(fdt2))) + FAIL("fdt_pack(): %s", fdt_strerror(err)); + sprintf(outname, "repacked.%s", inname); + save_blob(outname, fdt2); + + packsize = fdt_totalsize(fdt2); + + verbose_printf("oldsize = %d, bufsize = %d, packsize = %d\n", + oldsize, bufsize, packsize); + PASS(); +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index fba7413..1e2da40 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -45,6 +45,19 @@ functional_tests () { tree1_tests shunted.$tree tree1_tests deshunted.$tree done + + # Read-write tests + for tree in test_tree1.dtb sw_tree1.test.dtb; do + rm -f opened.$tree repacked.$tree + run_test open_pack $tree + tree1_tests opened.$tree + tree1_tests repacked.$tree + done + run_test setprop test_tree1.dtb + run_test del_property test_tree1.dtb + run_test del_node test_tree1.dtb + run_test rw_tree1 + tree1_tests rw_tree1.test.dtb } stress_tests () { diff --git a/tests/rw_tree1.c b/tests/rw_tree1.c new file mode 100644 index 0000000..8c5582a --- /dev/null +++ b/tests/rw_tree1.c @@ -0,0 +1,98 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_nop_node() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * 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(code) \ + { \ + err = (code); \ + if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } + +#define PTR_CHECK(ptr, code) \ + { \ + err = fdt_ptr_error((ptr) = (code)); \ + if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } + +#define OFF_CHECK(off, code) \ + { \ + err = fdt_offset_error((off) = (code)); \ + if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } + +int main(int argc, char *argv[]) +{ + void *buf; + struct fdt_header *fdt; + int err; + int offset; + + test_init(argc, argv); + + buf = xmalloc(SPACE); + + /* First create empty tree with SW */ + fdt = fdt_create(buf, SPACE); + + CHECK(fdt_finish_reservemap(fdt)); + CHECK(fdt_begin_node(fdt, "")); + CHECK(fdt_end_node(fdt)); + CHECK(fdt_finish(fdt)); + + verbose_printf("Built empty tree, totalsize = %d\n", + fdt32_to_cpu(fdt->totalsize)); + + PTR_CHECK(fdt, fdt_open_into(fdt, buf, SPACE)); + + CHECK(fdt_setprop_typed(fdt, 0, "prop-int", TEST_VALUE_1)); + CHECK(fdt_setprop_string(fdt, 0, "prop-str", TEST_STRING_1)); + + OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode1")); + CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_1)); + OFF_CHECK(offset, fdt_add_subnode(fdt, offset, "subsubnode")); + CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_1)); + + OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode2")); + CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_2)); + OFF_CHECK(offset, fdt_add_subnode(fdt, offset, "subsubnode")); + + CHECK(fdt_setprop_typed(fdt, offset, "prop-int", TEST_VALUE_2)); + + PTR_CHECK(fdt, fdt_pack(fdt)); + + save_blob("rw_tree1.test.dtb", fdt); + + PASS(); +} diff --git a/tests/setprop.c b/tests/setprop.c new file mode 100644 index 0000000..3df854d --- /dev/null +++ b/tests/setprop.c @@ -0,0 +1,75 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for fdt_setprop() + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * 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 NEW_STRING "here is quite a long test string, blah blah blah" + +int main(int argc, char *argv[]) +{ + struct fdt_header *fdt; + void *buf; + uint32_t *intp; + char *strp; + int err; + + test_init(argc, argv); + fdt = load_blob_arg(argc, argv); + + buf = xmalloc(SPACE); + + fdt = fdt_open_into(fdt, buf, SPACE); + if ((err = fdt_ptr_error(fdt))) + FAIL("fdt_open_into(): %s", fdt_strerror(err)); + + intp = check_getprop_typed(fdt, 0, "prop-int", TEST_VALUE_1); + + verbose_printf("Old int value was 0x%08x\n", *intp); + err = fdt_setprop_string(fdt, 0, "prop-int", NEW_STRING); + if (err) + FAIL("Failed to set \"prop-int\" to \"%s\": %s", + NEW_STRING, fdt_strerror(err)); + + strp = check_getprop_string(fdt, 0, "prop-int", NEW_STRING); + verbose_printf("New value is \"%s\"\n", strp); + + strp = check_getprop(fdt, 0, "prop-str", strlen(TEST_STRING_1)+1, + TEST_STRING_1); + + verbose_printf("Old string value was \"%s\"\n", strp); + err = fdt_setprop(fdt, 0, "prop-str", NULL, 0); + if (err) + FAIL("Failed to empty \"prop-str\": %s", + fdt_strerror(err)); + + check_getprop(fdt, 0, "prop-str", 0, NULL); + + PASS(); +} diff --git a/tests/tests.h b/tests/tests.h index 732e6a4..a673b61 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -125,6 +125,8 @@ void *check_getprop(struct fdt_header *fdt, int nodeoffset, const char *name, typeof(val) x = val; \ check_getprop(fdt, nodeoffset, name, sizeof(x), &x); \ }) +#define check_getprop_string(fdt, nodeoffset, name, s) \ + check_getprop((fdt), (nodeoffset), (name), strlen(s)+1, (s)) //void *load_blob(const char *filename); void *load_blob_arg(int argc, char *argv[]); void save_blob(const char *filename, struct fdt_header *fdt);