[PATCH v4 2/6] Add fdt read/write utility functions

Simon Glass sjg at chromium.org
Thu Sep 22 06:32:45 EST 2011


This adds higher-level libfdt operations for reading/writing an fdt
blob from/to a file, as well s a function to decode a data type string
as will be used by fdtget, fdtput.

Add utilfdt tests

This adds a few tests for the simple type argument supported by
utilfdt_decode_type.

I assume this will be squashed in with utilfdt, but I have left it out
for easier review here.

Signed-off-by: Simon Glass <sjg at chromium.org>
---
Changes in v2:
- Remove util_decode_key
- Add utilfdt_decode_type to be used by fdtget/put
- Remove limits on device tree binary size

Changes in v3:
- Change format of -t argument to be more like printf
- Move utilfdt into its own directory and make it a library
- Use code closer to testutils.c implementation
- Use open/close instead of fopen/fclose
- Add tests for utilfdt

Changes in v4:
- Squash utilfdt functions into util.c
- Squash utilfdt tests into main commit (this is getting very squashed)
- Remove xmalloc() etc. from tests.h since they are in util.h

 Makefile             |    2 +-
 tests/Makefile.tests |    5 +-
 tests/run_tests.sh   |    9 +++-
 tests/tests.h        |   18 +------
 tests/utilfdt_test.c |  128 +++++++++++++++++++++++++++++++++++++++++++++
 util.c               |  142 ++++++++++++++++++++++++++++++++++++++++++++++++++
 util.h               |   68 ++++++++++++++++++++++++
 7 files changed, 352 insertions(+), 20 deletions(-)
 create mode 100644 tests/utilfdt_test.c

diff --git a/Makefile b/Makefile
index 380a705..b32409b 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ EXTRAVERSION =
 LOCAL_VERSION =
 CONFIG_LOCALVERSION =
 
-CPPFLAGS = -I libfdt
+CPPFLAGS = -I libfdt -I .
 WARNINGS = -Werror -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \
 	-Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls
 CFLAGS = -g -Os -fPIC -Werror $(WARNINGS)
diff --git a/tests/Makefile.tests b/tests/Makefile.tests
index c564e72..4df4017 100644
--- a/tests/Makefile.tests
+++ b/tests/Makefile.tests
@@ -15,7 +15,8 @@ LIB_TESTS_L = get_mem_rsv \
 	extra-terminating-null \
 	dtbs_equal_ordered \
 	dtb_reverse dtbs_equal_unordered \
-	add_subnode_with_nops path_offset_aliases
+	add_subnode_with_nops path_offset_aliases \
+	utilfdt_test
 LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
 
 LIBTREE_TESTS_L = truncated_property
@@ -41,7 +42,7 @@ TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
 .PHONY: tests
 tests:	$(TESTS) $(TESTS_TREES)
 
-$(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o $(LIBFDT_archive)
+$(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_archive)
 
 $(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o $(LIBFDT_archive)
 	@$(VECHO) LD [libdl] $@
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 72dda32..486ceae 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -388,6 +388,10 @@ dtbs_equal_tests () {
 cmp_tests test_tree1.dtb $WRONG_TREE1
 }
 
+utilfdt_tests () {
+    run_test utilfdt_test
+}
+
 while getopts "vt:m" ARG ; do
 case $ARG in
 	"v")
@@ -403,7 +407,7 @@ while getopts "vt:m" ARG ; do
 done
 
 if [ -z "$TESTSETS" ]; then
-    TESTSETS="libfdt dtc dtbs_equal"
+    TESTSETS="libfdt utilfdt dtc dtbs_equal"
 fi
 
 # Make sure we don't have stale blobs lying around
@@ -414,6 +418,9 @@ for set in $TESTSETS; do
 	"libfdt")
 	    libfdt_tests
 	    ;;
+	"utilfdt")
+	    utilfdt_tests
+	    ;;
 	"dtc")
 	    dtc_tests
 	    ;;
diff --git a/tests/tests.h b/tests/tests.h
index fcb2b2a..a51556d 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -93,22 +93,6 @@ void cleanup(void);
 		exit(RC_BUG);				\
 	} while (0)
 
-static inline void *xmalloc(size_t size)
-{
-	void *p = malloc(size);
-	if (! p)
-		FAIL("malloc() failure");
-	return p;
-}
-
-static inline void *xrealloc(void *p, size_t size)
-{
-	p = realloc(p, size);
-	if (! p)
-		FAIL("realloc() failure");
-	return p;
-}
-
 void check_mem_rsv(void *fdt, int n, uint64_t addr, uint64_t size);
 
 void check_property(void *fdt, int nodeoffset, const char *name,
@@ -135,4 +119,6 @@ void *load_blob_arg(int argc, char *argv[]);
 void save_blob(const char *filename, void *blob);
 void *open_blob_rw(void *blob);
 
+#include "util.h"
+
 #endif /* _TESTS_H */
diff --git a/tests/utilfdt_test.c b/tests/utilfdt_test.c
new file mode 100644
index 0000000..36b4aa5
--- /dev/null
+++ b/tests/utilfdt_test.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
+ *
+ * utilfdt_test - Tests for utilfdt library
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include <fdt.h>
+#include <libfdt.h>
+#include <util.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+static void check(const char *fmt, int expect_type, int expect_size)
+{
+	int type;
+	int size;
+
+	if (utilfdt_decode_type(fmt, &type, &size))
+		FAIL("format '%s': valid format string returned failure", fmt);
+	if (expect_type != type)
+		FAIL("format '%s': expected type='%c', got type='%c'", fmt,
+		     expect_type, type);
+	if (expect_size != size)
+		FAIL("format '%s': expected size=%d, got size=%d", fmt,
+		     expect_size, size);
+}
+
+static void checkfail(const char *fmt)
+{
+	int type;
+	int size;
+
+	if (!utilfdt_decode_type(fmt, &type, &size))
+		FAIL("format '%s': invalid format string returned success",
+		     fmt);
+}
+
+/**
+ * Add the given modifier to each of the valid sizes, and check that we get
+ * correct values.
+ *
+ * \param modifier	Modifer string to use as a prefix
+ * \param expected_size	The size (in bytes) that we expect (ignored for
+ *			strings)
+ */
+static void check_sizes(char *modifier, int expected_size)
+{
+	char fmt[10], *ptr;
+
+	/* set up a string with a hole in it for the format character */
+	if (strlen(modifier) + 2 >= sizeof(fmt))
+		FAIL("modifier string '%s' too long", modifier);
+	strcpy(fmt, modifier);
+	ptr = fmt + strlen(fmt);
+	ptr[1] = '\0';
+
+	/* now try each format character in turn */
+	*ptr = 'i';
+	check(fmt, 'i', expected_size);
+
+	*ptr = 'u';
+	check(fmt, 'u', expected_size);
+
+	*ptr = 'x';
+	check(fmt, 'x', expected_size);
+
+	*ptr = 's';
+	check(fmt, 's', -1);
+}
+
+static void test_utilfdt_decode_type(void)
+{
+	char fmt[10];
+	int ch;
+
+	/* check all the valid modifiers and sizes */
+	check_sizes("", -1);
+	check_sizes("b", 1);
+	check_sizes("hh", 1);
+	check_sizes("h", 2);
+	check_sizes("l", 4);
+
+	/* try every other character */
+	checkfail("");
+	for (ch = ' '; ch < 127; ch++) {
+		if (!strchr("iuxs", ch)) {
+			*fmt = ch;
+			fmt[1] = '\0';
+			checkfail(fmt);
+		}
+	}
+
+	/* try a few modifiers at the end */
+	checkfail("sx");
+	checkfail("ihh");
+	checkfail("xb");
+
+	/* and one for the doomsday archives */
+	checkfail("He has all the virtues I dislike and none of the vices "
+			"I admire.");
+}
+
+int main(int argc, char *argv[])
+{
+	test_utilfdt_decode_type();
+	PASS();
+}
diff --git a/util.c b/util.c
index 994436f..a0885bf 100644
--- a/util.c
+++ b/util.c
@@ -1,4 +1,5 @@
 /*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
  * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
  *
  * util_is_printable_string contributed by
@@ -26,6 +27,11 @@
 #include <stdarg.h>
 #include <string.h>
 
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libfdt.h"
 #include "util.h"
 
 char *xstrdup(const char *s)
@@ -85,3 +91,139 @@ int util_is_printable_string(const void *data, int len)
 
 	return 1;
 }
+
+int utilfdt_read_err(const char *filename, char **buffp)
+{
+	int fd = 0;	/* assume stdin */
+	char *buf = NULL;
+	off_t bufsize = 1024, offset = 0;
+	int ret = 0;
+
+	*buffp = NULL;
+	if (strcmp(filename, "-") != 0) {
+		fd = open(filename, O_RDONLY);
+		if (fd < 0)
+			return errno;
+	}
+
+	/* Loop until we have read everything */
+	buf = malloc(bufsize);
+	do {
+		/* Expand the buffer to hold the next chunk */
+		if (offset == bufsize) {
+			bufsize *= 2;
+			buf = realloc(buf, bufsize);
+			if (!buf) {
+				ret = ENOMEM;
+				break;
+			}
+		}
+
+		ret = read(fd, &buf[offset], bufsize - offset);
+		if (ret < 0) {
+			ret = errno;
+			break;
+		}
+		offset += ret;
+	} while (ret != 0);
+
+	/* Clean up, including closing stdin; return errno on error */
+	close(fd);
+	if (ret)
+		free(buf);
+	else
+		*buffp = buf;
+	return ret;
+}
+
+char *utilfdt_read(const char *filename)
+{
+	char *buff;
+	int ret = utilfdt_read_err(filename, &buff);
+
+	if (ret) {
+		fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
+			strerror(ret));
+		return NULL;
+	}
+	/* Successful read */
+	return buff;
+}
+
+int utilfdt_write_err(const char *filename, const void *blob)
+{
+	int fd = 1;	/* assume stdout */
+	int totalsize;
+	int offset;
+	int ret = 0;
+	const char *ptr = blob;
+
+	if (strcmp(filename, "-") != 0) {
+		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (fd < 0)
+			return errno;
+	}
+
+	totalsize = fdt_totalsize(blob);
+	offset = 0;
+
+	while (offset < totalsize) {
+		ret = write(fd, ptr + offset, totalsize - offset);
+		if (ret < 0) {
+			ret = -errno;
+			break;
+		}
+		offset += ret;
+	}
+	/* Close the file/stdin; return errno on error */
+	if (fd != 1)
+		close(fd);
+	return ret < 0 ? -ret : 0;
+}
+
+
+int utilfdt_write(const char *filename, const void *blob)
+{
+	int ret = utilfdt_write_err(filename, blob);
+
+	if (ret) {
+		fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename,
+			strerror(ret));
+	}
+	return ret ? -1 : 0;
+}
+
+int utilfdt_decode_type(const char *fmt, int *type, int *size)
+{
+	int qualifier = 0;
+
+	/* get the conversion qualifier */
+	*size = -1;
+	if (strchr("hlLb", *fmt)) {
+		qualifier = *fmt++;
+		if (qualifier == *fmt) {
+			switch (*fmt++) {
+/* TODO:		case 'l': qualifier = 'L'; break;*/
+			case 'h':
+				qualifier = 'b';
+				break;
+			}
+		}
+	}
+
+	/* we should now have a type */
+	if (!strchr("iuxs", *fmt))
+		return -1;
+
+	/* convert qualifier (bhL) to byte size */
+	if (*fmt != 's')
+		*size = qualifier == 'b' ? 1 :
+				qualifier == 'h' ? 2 :
+				qualifier == 'l' ? 4 : -1;
+	*type = *fmt++;
+
+	/* that should be it! */
+	if (*fmt)
+		return -1;
+	return 0;
+}
diff --git a/util.h b/util.h
index cc68933..1822d9b 100644
--- a/util.h
+++ b/util.h
@@ -4,6 +4,7 @@
 #include <stdarg.h>
 
 /*
+ * Copyright 2011 The Chromium Authors, All Rights Reserved.
  * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
  *
  * This program is free software; you can redistribute it and/or
@@ -64,4 +65,71 @@ extern char *join_path(const char *path, const char *name);
  * @return 1 if a valid printable string, 0 if not */
 int util_is_printable_string(const void *data, int len);
 
+/**
+ * Read a device tree file into a buffer. This will report any errors on
+ * stderr.
+ *
+ * @param filename	The filename to read, or - for stdin
+ * @return Pointer to allocated buffer containing fdt, or NULL on error
+ */
+char *utilfdt_read(const char *filename);
+
+/**
+ * Read a device tree file into a buffer. Does not report errors, but only
+ * returns them. The value returned can be passed to strerror() to obtain
+ * an error message for the user.
+ *
+ * @param filename	The filename to read, or - for stdin
+ * @param buffp		Returns pointer to buffer containing fdt
+ * @return 0 if ok, else an errno value representing the error
+ */
+int utilfdt_read_err(const char *filename, char **buffp);
+
+
+/**
+ * Write a device tree buffer to a file. This will report any errors on
+ * stderr.
+ *
+ * @param filename	The filename to write, or - for stdout
+ * @param blob		Poiner to buffer containing fdt
+ * @return 0 if ok, -1 on error
+ */
+int utilfdt_write(const char *filename, const void *blob);
+
+/**
+ * Write a device tree buffer to a file. Does not report errors, but only
+ * returns them. The value returned can be passed to strerror() to obtain
+ * an error message for the user.
+ *
+ * @param filename	The filename to write, or - for stdout
+ * @param blob		Poiner to buffer containing fdt
+ * @return 0 if ok, else an errno value representing the error
+ */
+int utilfdt_write_err(const char *filename, const void *blob);
+
+/**
+ * Decode a data type string. The purpose of this string
+ *
+ * The string consists of an optional character followed by the type:
+ *	Modifier characters:
+ *		hh or b	1 byte
+ *		h	2 byte
+ *		l	4 byte, default
+ *
+ *	Type character:
+ *		s	string
+ *		i	signed integer
+ *		u	unsigned integer
+ *		x	hex
+ *
+ * TODO: Implement ll modifier (8 bytes)
+ * TODO: Implement o type (octal)
+ *
+ * @param fmt		Format string to process
+ * @param type		Returns type found(s/d/u/x), or 0 if none
+ * @param size		Returns size found(1,2,4,8) or 4 if none
+ * @return 0 if ok, -1 on error (no type given, or other invalid format)
+ */
+int utilfdt_decode_type(const char *fmt, int *type, int *size);
+
 #endif /* _UTIL_H */
-- 
1.7.3.1



More information about the devicetree-discuss mailing list