aboutsummaryrefslogtreecommitdiff
path: root/lib/dbg_fdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dbg_fdt.c')
-rw-r--r--lib/dbg_fdt.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/lib/dbg_fdt.c b/lib/dbg_fdt.c
new file mode 100644
index 0000000..6d02442
--- /dev/null
+++ b/lib/dbg_fdt.c
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: copyleft-next-0.3.1 */
+/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
+
+/**
+ * @file dbg_fdt.c
+ * Dump fdt info to serial.
+ */
+
+#include <kmi/debug.h>
+#include <libfdt.h>
+
+#if defined(DEBUG)
+/**
+ * Print single character \c depth number of times.
+ *
+ * @param c Character to print.
+ * @param depth Number of times to print character.
+ */
+static void __print_char(char c, int depth)
+{
+ /* lol, ugly but good enough for now */
+ for (int i = 0; i < depth; ++i)
+ dbg("%c", c);
+}
+
+/**
+ * Check if ASCII character is in printable range.
+ *
+ * In this case, printable is anything that isn't a control character, line
+ * feed, bell, etc.
+ *
+ * @param c Character to check.
+ * @return \ref true if in printable range, \ref false otherwise.
+ */
+static int __printable(char c)
+{
+ return (c >= 32) && (c <= 126);
+}
+
+/**
+ * Check if \c data and \c len bytes from it form a reasonable string.
+ *
+ * @param data Pointer to some binary data to check.
+ * @param len Assumed length of string.
+ * @return \c true if data is likely some string, \c false otherwise.
+ */
+static int __is_string(const void *data, int len)
+{
+ const char *s = data;
+
+ /* zero length is not */
+ if (len == 0)
+ return 0;
+
+ /* must terminate with zero or '\n' */
+ if (s[len - 1] != '\0' && s[len - 1] != '\n')
+ return 0;
+
+ /* printable or a null byte (concatenated strings) */
+ while (((*s == '\0') || __printable(*s)) && (len > 0)) {
+ /*
+ * If we see a null, there are three possibilities:
+ * 1) If len == 1, it is the end of the string, printable
+ * 2) Next character also a null, not printable.
+ * 3) Next character not a null, continue to check.
+ */
+ if (s[0] == '\0') {
+ if (len == 1)
+ return 1;
+ if (s[1] == '\0')
+ return 0;
+ }
+ s++;
+ len--;
+ }
+
+ /* Not the null termination, or not done yet: not printable */
+ if (*s != '\0' || (len != 0))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Print value of FDT property.
+ *
+ * @param data Pointer to FDT property.
+ * @param len Length of property.
+ */
+static void __print_prop_value(const void *data, int len)
+{
+ if (len == 0)
+ return;
+
+ dbg(" = ");
+
+ /* heavily inspired by u-boot's fdt print */
+ if (__is_string(data, len)) {
+ dbg("\"");
+ int i = 0;
+ while (i < len) {
+ if (i > 0)
+ dbg("\", \"");
+
+ dbg("%s", (const char *)data);
+ i += strlen(data) + 1;
+ data += strlen(data) + 1;
+ }
+ dbg("\"");
+
+ } else if ((len % 4) == 0) {
+ const int32_t *p = (const int32_t *)data;
+ dbg("<");
+ for (int i = 0; i < len / 4; ++i)
+ dbg("%#08x%s", (unsigned int)fdt32_to_cpu(p[i]),
+ i < (len / 4 - 1) ? " " : "");
+
+ dbg(">");
+ } else {
+ const int32_t *p = (const int32_t *)data;
+ dbg("[");
+ for (int i = 0; i < len / 4; ++i)
+ dbg("%#02x%s", (unsigned int)fdt32_to_cpu(p[i]),
+ i < (len / 4 - 1) ? " " : "");
+
+ dbg("]");
+ }
+}
+
+void __dbg_fdt(const void *fdt, int node_offset, int depth)
+{
+ int node = 0;
+ fdt_for_each_subnode(node, fdt, node_offset)
+ {
+ __print_char('\t', depth);
+ dbg("%s: {\n", fdt_get_name(fdt, node, 0));
+
+ int property = 0;
+ fdt_for_each_property_offset(property, fdt, node)
+ {
+ int len = 0;
+ const char *name = 0;
+ const void *data = fdt_getprop_by_offset(fdt, property,
+ &name, &len);
+
+ __print_char('\t', depth + 1);
+ dbg("%s", name);
+
+ __print_prop_value(data, len);
+ dbg(";\n");
+ }
+
+ __dbg_fdt(fdt, node, depth + 1);
+
+ __print_char('\t', depth);
+ dbg("};\n\n");
+ }
+}
+#endif