At several occasions it would be useful to dump the fdt blob being passed to the second (kexec/kdump) kernel when '-d' flag is specified while invoking kexec/kdump.
This allows one to look at the device-tree fields that is being passed to the secondary kernel and accordingly debug issues. This can be specially useful for the arm64 case, where kexec_load() or kdump passes important information like 'linux,usable-memory' ranges to the second kernel, and the correctness of the ranges can be verified by looking at the device-tree dump with '-d' flag specified. Signed-off-by: Bhupesh Sharma <bhsha...@redhat.com> --- kexec/dt-ops.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kexec/dt-ops.h | 1 + 2 files changed, 142 insertions(+) diff --git a/kexec/dt-ops.c b/kexec/dt-ops.c index 915dbf55afd2..80ebfd31b4b6 100644 --- a/kexec/dt-ops.c +++ b/kexec/dt-ops.c @@ -8,6 +8,10 @@ #include "kexec.h" #include "dt-ops.h" +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) +#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a)))) +#define GET_CELL(p) (p += 4, *((const uint32_t *)(p - 4))) + static const char n_chosen[] = "/chosen"; static const char p_bootargs[] = "bootargs"; @@ -143,3 +147,140 @@ int dtb_delete_property(char *dtb, const char *node, const char *prop) return result; } + +static uint64_t is_printable_string(const void* data, uint64_t len) +{ + const char *s = data; + const char *ss; + + /* Check for zero length strings */ + if (len == 0) + return 0; + + /* String must be terminated with a '\0' */ + if (s[len - 1] != '\0') + return 0; + + ss = s; + while (*s) + s++; + + /* Traverse till we hit a '\0' or reach 'len' */ + if (*s != '\0') + return 0; + + if ((s + 1 - ss) < len) { + /* Handle special cases such as 'bootargs' properties + * in dtb which are actually strings, but they may have + * a format where (s + 1 - ss) < len remains true. + * + * We can catch such cases by checking if (s + 1 - ss) + * is greater than 1 + */ + if ((s + 1 - ss) > 1) + return 1; + + return 0; + } + + return 1; +} + +static void print_data(const char* data, uint64_t len) +{ + uint64_t i; + const char *p_data = data; + + /* Check for non-zero length */ + if (len == 0) + return; + + if (is_printable_string(data, len)) { + dbgprintf(" = \"%s\"", (const char *)data); + } else if ((len % 4) == 0) { + dbgprintf(" = <"); + for (i = 0; i < len; i += 4) { + dbgprintf("0x%08x%s", + fdt32_to_cpu(GET_CELL(p_data)), + i < (len - 4) ? " " : ""); + } + dbgprintf(">"); + } else { + dbgprintf(" = ["); + for (i = 0; i < len; i++) + dbgprintf("%02x%s", *p_data++, + i < len - 1 ? " " : ""); + dbgprintf("]"); + } +} + +void dump_fdt(void* fdt) +{ + struct fdt_header *bph; + const char* p_struct; + const char* p_strings; + const char* p_data; + const char* s_data; + uint32_t off_dt; + uint32_t off_str; + uint32_t tag; + uint64_t sz; + uint64_t depth; + uint64_t shift; + uint32_t version; + + depth = 0; + shift = 4; + + bph = fdt; + off_dt = fdt32_to_cpu(bph->off_dt_struct); + off_str = fdt32_to_cpu(bph->off_dt_strings); + p_struct = (const char*)fdt + off_dt; + p_strings = (const char*)fdt + off_str; + version = fdt32_to_cpu(bph->version); + + p_data = p_struct; + while ((tag = fdt32_to_cpu(GET_CELL(p_data))) != FDT_END) { + if (tag == FDT_BEGIN_NODE) { + s_data = p_data; + p_data = PALIGN(p_data + strlen(s_data) + 1, 4); + + if (*s_data == '\0') + s_data = "/"; + + dbgprintf("%*s%s {\n", (int)(depth * shift), " ", s_data); + + depth++; + continue; + } + + if (tag == FDT_END_NODE) { + depth--; + + dbgprintf("%*s};\n", (int)(depth * shift), " "); + continue; + } + + if (tag == FDT_NOP) { + dbgprintf("%*s// [NOP]\n", (int)(depth * shift), " "); + continue; + } + + if (tag != FDT_PROP) { + dbgprintf("%*s ** Unknown tag 0x%08x\n", + (int)(depth * shift), " ", tag); + break; + } + + sz = fdt32_to_cpu(GET_CELL(p_data)); + s_data = p_strings + fdt32_to_cpu(GET_CELL(p_data)); + if (version < 16 && sz >= 8) + p_data = PALIGN(p_data, 8); + + dbgprintf("%*s%s", (int)(depth * shift), " ", s_data); + print_data(p_data, sz); + dbgprintf(";\n"); + + p_data = PALIGN(p_data + sz, 4); + } +} diff --git a/kexec/dt-ops.h b/kexec/dt-ops.h index e70d15d8ffbf..25b9b569f2b7 100644 --- a/kexec/dt-ops.h +++ b/kexec/dt-ops.h @@ -9,5 +9,6 @@ int dtb_set_property(char **dtb, off_t *dtb_size, const char *node, const char *prop, const void *value, int value_len); int dtb_delete_property(char *dtb, const char *node, const char *prop); +void dump_fdt(void *fdt) ; #endif -- 2.7.4 _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec