[RFC PATCH 10/15] fdtdump: Handle unknown tags

Herve Codina posted 15 patches 6 hours ago
[RFC PATCH 10/15] fdtdump: Handle unknown tags
Posted by Herve Codina 6 hours ago
The structured tag value definition introduced recently gives the
ability to ignore unknown tags without any error when they are read.

Handle those structured tag.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 fdtdump.c                              |  45 +++++++++-
 tests/dumptrees.c                      |   4 +-
 tests/run_tests.sh                     |  41 +++++++++
 tests/testdata.h                       |   2 +
 tests/trees.S                          | 110 +++++++++++++++++++++++++
 tests/unknown_tags_can_skip.dtb.expect |  26 ++++++
 6 files changed, 224 insertions(+), 4 deletions(-)
 create mode 100644 tests/unknown_tags_can_skip.dtb.expect

diff --git a/fdtdump.c b/fdtdump.c
index 0e7a265..eb8cda9 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -44,7 +44,7 @@ static const char *tagname(uint32_t tag)
 #define dumpf(fmt, args...) \
 	do { if (debug) printf("// " fmt, ## args); } while (0)
 
-static void dump_blob(void *blob, bool debug)
+static void dump_blob(void *blob, bool debug, int dump_unknown)
 {
 	uintptr_t blob_off = (uintptr_t)blob;
 	struct fdt_header *bph = blob;
@@ -146,20 +146,55 @@ static void dump_blob(void *blob, bool debug)
 			continue;
 		}
 
+		if ((tag & FDT_TAG_STRUCTURED) && (tag & FDT_TAG_SKIP_SAFE)) {
+			sz = 0;
+			switch (tag & FDT_TAG_DATA_MASK) {
+			case FDT_TAG_DATA_NONE:
+				break;
+			case FDT_TAG_DATA_1CELL:
+				sz = FDT_CELLSIZE;
+				break;
+			case FDT_TAG_DATA_2CELLS:
+				sz = 2 * FDT_CELLSIZE;
+				break;
+			case FDT_TAG_DATA_LNG:
+				/* Get the length */
+				sz = fdt32_to_cpu(GET_CELL(p));
+				break;
+			}
+
+			if (dump_unknown) {
+				printf("%*s// Unknown tag ignored: 0x%08"PRIx32", data lng %d",
+				       depth * shift, "", tag, sz);
+				if (dump_unknown > 1 && sz != 0) {
+					printf(" ");
+					for (i = 0; i < sz; i++)
+						printf("%02hhx", *(p + i));
+				}
+				printf("\n");
+			}
+
+			/* Skip the data bytes */
+			p = PALIGN(p + sz, 4);
+			continue;
+		}
+
 		die("** Unknown tag 0x%08"PRIx32"\n", tag);
 	}
 }
 
 /* Usage related data. */
 static const char usage_synopsis[] = "fdtdump [options] <file>";
-static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
+static const char usage_short_opts[] = "dus" USAGE_COMMON_SHORT_OPTS;
 static struct option const usage_long_opts[] = {
 	{"debug",            no_argument, NULL, 'd'},
+	{"unknown",          no_argument, NULL, 'u'},
 	{"scan",             no_argument, NULL, 's'},
 	USAGE_COMMON_LONG_OPTS
 };
 static const char * const usage_opts_help[] = {
 	"Dump debug information while decoding the file",
+	"Dump unknown tags information while decoding the file (-uu to have data)",
 	"Scan for an embedded fdt in file",
 	USAGE_COMMON_OPTS_HELP
 };
@@ -183,6 +218,7 @@ int main(int argc, char *argv[])
 	const char *file;
 	char *buf;
 	bool debug = false;
+	int dump_unknown = 0;
 	bool scan = false;
 	size_t len;
 
@@ -198,6 +234,9 @@ int main(int argc, char *argv[])
 		case 'd':
 			debug = true;
 			break;
+		case 'u':
+			dump_unknown++;
+			break;
 		case 's':
 			scan = true;
 			break;
@@ -242,7 +281,7 @@ int main(int argc, char *argv[])
 	} else if (!valid_header(buf, len))
 		die("%s: header is not valid\n", file);
 
-	dump_blob(buf, debug);
+	dump_blob(buf, debug, dump_unknown);
 
 	return 0;
 }
diff --git a/tests/dumptrees.c b/tests/dumptrees.c
index 08967b3..4732fff 100644
--- a/tests/dumptrees.c
+++ b/tests/dumptrees.c
@@ -25,7 +25,9 @@ static struct {
 	TREE(truncated_property), TREE(truncated_string),
 	TREE(truncated_memrsv),
 	TREE(two_roots),
-	TREE(named_root)
+	TREE(named_root),
+	TREE(unknown_tags_can_skip),
+	TREE(unknown_tags_no_skip)
 };
 
 #define NUM_TREES	(sizeof(trees) / sizeof(trees[0]))
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index f07092b..b69b61b 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -196,6 +196,40 @@ check_align () {
     )
 }
 
+# $1: f1 file
+# $2: f2 file
+check_diff () {
+    printf "diff $1 $2:	"
+    local f1="$1"
+    local f2="$2"
+    (
+        if diff $f1 $f2 >/dev/null; then
+            PASS
+        else
+            if [ -z "$QUIET_TEST" ]; then
+                echo "DIFF :-:"
+                diff -u $f1 $f2
+            fi
+            FAIL "Results differ from expected"
+        fi
+    )
+}
+
+# $1: dtb file
+# $2: out file
+wrap_fdtdump () {
+    printf "wrap_fdtdump -uu $1:	"
+    local dtb="$1"
+    local out="$2"
+    (
+        if $FDTDUMP -uu ${dtb} 2>/dev/null >${out}; then
+            PASS
+        else
+            FAIL
+        fi
+    )
+}
+
 run_dtc_test () {
     printf "dtc $*:	"
     base_run_test wrap_test $VALGRIND $DTC "$@"
@@ -1007,6 +1041,13 @@ utilfdt_tests () {
 
 fdtdump_tests () {
     run_fdtdump_test "$SRCDIR/fdtdump.dts"
+
+    base_run_test wrap_fdtdump unknown_tags_can_skip.dtb unknown_tags_can_skip.dtb.out
+    # Remove unneeded comments
+    sed -i '/^\/\/ /d' unknown_tags_can_skip.dtb.out
+    base_run_test check_diff unknown_tags_can_skip.dtb.out "$SRCDIR/unknown_tags_can_skip.dtb.expect"
+
+    run_wrap_error_test $FDTDUMP unknown_tags_no_skip.dtb
 }
 
 fdtoverlay_tests() {
diff --git a/tests/testdata.h b/tests/testdata.h
index fcebc2c..aef04ab 100644
--- a/tests/testdata.h
+++ b/tests/testdata.h
@@ -57,4 +57,6 @@ extern struct fdt_header truncated_string;
 extern struct fdt_header truncated_memrsv;
 extern struct fdt_header two_roots;
 extern struct fdt_header named_root;
+extern struct fdt_header unknown_tags_can_skip;
+extern struct fdt_header unknown_tags_no_skip;
 #endif /* ! __ASSEMBLER__ */
diff --git a/tests/trees.S b/tests/trees.S
index 4db2b9b..221c9fd 100644
--- a/tests/trees.S
+++ b/tests/trees.S
@@ -328,3 +328,113 @@ named_root_strings:
 named_root_strings_end:
 
 named_root_end:
+
+
+	/* Tree with "unknown" tags that can be skipped
+	 * Use a really future dtb version to check version downgrade on
+	 * modification.
+	 */
+	treehdr_vers	unknown_tags_can_skip 0xffffffff 0x10
+	empty_rsvmap	unknown_tags_can_skip
+
+unknown_tags_can_skip_struct:
+	fdtlong	FDT_TEST_1CELL_CAN_SKIP
+	fdtlong	0x1
+
+	beginn	""
+		fdtlong	FDT_TEST_NONE_CAN_SKIP
+
+		propu32	unknown_tags_can_skip, prop_int, 1
+
+		fdtlong	FDT_TEST_1CELL_CAN_SKIP
+		fdtlong	0x11
+
+		propstr	unknown_tags_can_skip, prop_str, "abcd"
+
+		fdtlong	FDT_TEST_2CELLS_CAN_SKIP
+		fdtlong	0x12
+		fdtlong	0x12
+
+		fdtlong	FDT_TEST_LNG_CAN_SKIP
+		fdtlong	3
+		.byte 0x13
+		.byte 0x13
+		.byte 0x13
+		.byte 0 /* padding */
+
+		beginn	"subnode1"
+			propu64	unknown_tags_can_skip, prop_int, 1, 2
+			fdtlong	FDT_TEST_NONE_CAN_SKIP
+		endn
+
+		beginn	"subnode2"
+			fdtlong	FDT_TEST_1CELL_CAN_SKIP
+			fdtlong	0x121
+			propu64	unknown_tags_can_skip, prop_int1, 1, 2
+			fdtlong	FDT_TEST_1CELL_CAN_SKIP
+			fdtlong	0x122
+			propu64	unknown_tags_can_skip, prop_int2, 1, 2
+			beginn	"subsubnode"
+				fdtlong	FDT_TEST_1CELL_CAN_SKIP
+				fdtlong	0x123
+				propu64	unknown_tags_can_skip, prop_int, 1, 2
+			endn
+			fdtlong	FDT_TEST_1CELL_CAN_SKIP
+			fdtlong	0x124
+		endn
+
+		fdtlong	FDT_TEST_LNG_CAN_SKIP
+		fdtlong	5
+		.byte 0x14
+		.byte 0x14
+		.byte 0x14
+		.byte 0x14
+		.byte 0x14
+		.byte 0 /* padding */
+		.byte 0 /* padding */
+		.byte 0 /* padding */
+	endn
+
+	fdtlong	FDT_TEST_1CELL_CAN_SKIP
+	fdtlong	0x2
+
+	fdtlong	FDT_TEST_LNG_CAN_SKIP
+	fdtlong	2
+	.byte 0x3
+	.byte 0x3
+	.byte 0 /* padding */
+	.byte 0 /* padding */
+
+	fdtlong	FDT_END
+
+unknown_tags_can_skip_struct_end:
+
+unknown_tags_can_skip_strings:
+	string	unknown_tags_can_skip, prop_int, "prop-int"
+	string	unknown_tags_can_skip, prop_int1, "prop-int1"
+	string	unknown_tags_can_skip, prop_int2, "prop-int2"
+	string	unknown_tags_can_skip, prop_str, "prop-str"
+unknown_tags_can_skip_strings_end:
+
+unknown_tags_can_skip_end:
+
+
+	/* Tree with "unknown" tags that cannot be skipped */
+	treehdr	unknown_tags_no_skip
+	empty_rsvmap	unknown_tags_no_skip
+
+unknown_tags_no_skip_struct:
+	beginn	""
+	fdtlong	FDT_TEST_NONE_NO_SKIP
+		beginn	"subnode1"
+			propu64	unknown_tags_no_skip, prop_int, 1, 2
+		endn
+	endn
+	fdtlong	FDT_END
+unknown_tags_no_skip_struct_end:
+
+unknown_tags_no_skip_strings:
+	string	unknown_tags_no_skip, prop_int, "prop-int"
+unknown_tags_no_skip_strings_end:
+
+unknown_tags_no_skip_end:
diff --git a/tests/unknown_tags_can_skip.dtb.expect b/tests/unknown_tags_can_skip.dtb.expect
new file mode 100644
index 0000000..32e3f46
--- /dev/null
+++ b/tests/unknown_tags_can_skip.dtb.expect
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+    // Unknown tag ignored: 0xc0000000, data lng 0
+    prop-int = <0x00000001>;
+    // Unknown tag ignored: 0xd0000000, data lng 4 00000011
+    prop-str = "abcd";
+    // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012
+    // Unknown tag ignored: 0xf0000000, data lng 3 131313
+    subnode1 {
+        prop-int = <0x00000001 0x00000002>;
+        // Unknown tag ignored: 0xc0000000, data lng 0
+    };
+    subnode2 {
+        // Unknown tag ignored: 0xd0000000, data lng 4 00000121
+        prop-int1 = <0x00000001 0x00000002>;
+        // Unknown tag ignored: 0xd0000000, data lng 4 00000122
+        prop-int2 = <0x00000001 0x00000002>;
+        subsubnode {
+            // Unknown tag ignored: 0xd0000000, data lng 4 00000123
+            prop-int = <0x00000001 0x00000002>;
+        };
+        // Unknown tag ignored: 0xd0000000, data lng 4 00000124
+    };
+    // Unknown tag ignored: 0xf0000000, data lng 5 1414141414
+};
-- 
2.52.0