blob: d424869eda472e700ab45822424abb134832a09e [file] [log] [blame]
Rob Herring4097bbf2019-06-20 15:19:43 -06001// SPDX-License-Identifier: GPL-2.0-or-later
David Gibsonb2543fc2005-08-29 14:58:27 +10002/*
Mike Frysinger8f459c52011-10-25 17:29:24 -04003 * fdtdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
David Gibsonb2543fc2005-08-29 14:58:27 +10004 */
5
Mike Frysingerfdc73872013-04-15 22:13:13 -04006#include <stdbool.h>
David Gibsonb2543fc2005-08-29 14:58:27 +10007#include <stdint.h>
8#include <stdio.h>
David Gibson0ef21052009-11-17 17:00:53 +11009#include <stdlib.h>
David Gibsonb2543fc2005-08-29 14:58:27 +100010#include <string.h>
11#include <ctype.h>
David Gibson180a9392018-06-05 09:55:15 +100012#include <inttypes.h>
David Gibsonb2543fc2005-08-29 14:58:27 +100013
Mike Frysingerfdc73872013-04-15 22:13:13 -040014#include <libfdt.h>
David Gibson11d71002008-06-26 10:43:06 +100015#include <libfdt_env.h>
Kim Phillips20b866a2012-11-13 18:34:09 -060016#include <fdt.h>
David Gibsonb2543fc2005-08-29 14:58:27 +100017
Simon Glass492f9d52011-07-05 12:02:49 -070018#include "util.h"
19
Jean-Christophe Duboise24d39a2016-07-13 02:31:13 +020020#define FDT_MAGIC_SIZE 4
Pierre-Clément Tosief1978a2022-05-30 20:57:34 +010021#define MAX_VERSION 17U
Jean-Christophe Duboise24d39a2016-07-13 02:31:13 +020022
David Gibsonb2543fc2005-08-29 14:58:27 +100023#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
Pierre-Clément Tosief1978a2022-05-30 20:57:34 +010024#define PALIGN(p, a) ((void *)(ALIGN((uintptr_t)(p), (a))))
David Gibsonbad5b282017-03-06 12:08:53 +110025#define GET_CELL(p) (p += 4, *((const fdt32_t *)(p-4)))
David Gibsonb2543fc2005-08-29 14:58:27 +100026
Mike Frysinger8ec013a2013-04-15 22:13:17 -040027static const char *tagname(uint32_t tag)
David Gibsonb2543fc2005-08-29 14:58:27 +100028{
Mike Frysinger8ec013a2013-04-15 22:13:17 -040029 static const char * const names[] = {
Phil Elwell242c2642014-10-17 23:22:11 +010030#define TN(t) [t] = #t
Mike Frysinger8ec013a2013-04-15 22:13:17 -040031 TN(FDT_BEGIN_NODE),
32 TN(FDT_END_NODE),
33 TN(FDT_PROP),
34 TN(FDT_NOP),
35 TN(FDT_END),
36#undef TN
37 };
38 if (tag < ARRAY_SIZE(names))
39 if (names[tag])
40 return names[tag];
41 return "FDT_???";
42}
43
44#define dumpf(fmt, args...) \
45 do { if (debug) printf("// " fmt, ## args); } while (0)
46
47static void dump_blob(void *blob, bool debug)
48{
49 uintptr_t blob_off = (uintptr_t)blob;
David Gibsonfb7c7ac2007-09-26 13:11:05 +100050 struct fdt_header *bph = blob;
David Gibson11d71002008-06-26 10:43:06 +100051 uint32_t off_mem_rsvmap = fdt32_to_cpu(bph->off_mem_rsvmap);
52 uint32_t off_dt = fdt32_to_cpu(bph->off_dt_struct);
53 uint32_t off_str = fdt32_to_cpu(bph->off_dt_strings);
David Gibsonfb7c7ac2007-09-26 13:11:05 +100054 struct fdt_reserve_entry *p_rsvmap =
David Gibson36786db2008-07-07 10:10:48 +100055 (struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap);
David Gibson0ef21052009-11-17 17:00:53 +110056 const char *p_struct = (const char *)blob + off_dt;
57 const char *p_strings = (const char *)blob + off_str;
David Gibson11d71002008-06-26 10:43:06 +100058 uint32_t version = fdt32_to_cpu(bph->version);
59 uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
David Gibsonb2543fc2005-08-29 14:58:27 +100060 uint32_t tag;
David Gibson0ef21052009-11-17 17:00:53 +110061 const char *p, *s, *t;
David Gibsonb2543fc2005-08-29 14:58:27 +100062 int depth, sz, shift;
63 int i;
64 uint64_t addr, size;
65
66 depth = 0;
67 shift = 4;
68
David Gibson0ef21052009-11-17 17:00:53 +110069 printf("/dts-v1/;\n");
David Gibson180a9392018-06-05 09:55:15 +100070 printf("// magic:\t\t0x%"PRIx32"\n", fdt32_to_cpu(bph->magic));
71 printf("// totalsize:\t\t0x%"PRIx32" (%"PRIu32")\n",
72 totalsize, totalsize);
73 printf("// off_dt_struct:\t0x%"PRIx32"\n", off_dt);
74 printf("// off_dt_strings:\t0x%"PRIx32"\n", off_str);
75 printf("// off_mem_rsvmap:\t0x%"PRIx32"\n", off_mem_rsvmap);
76 printf("// version:\t\t%"PRIu32"\n", version);
77 printf("// last_comp_version:\t%"PRIu32"\n",
David Gibson11d71002008-06-26 10:43:06 +100078 fdt32_to_cpu(bph->last_comp_version));
David Gibson3bb78bf2008-01-03 15:48:43 +110079 if (version >= 2)
David Gibson180a9392018-06-05 09:55:15 +100080 printf("// boot_cpuid_phys:\t0x%"PRIx32"\n",
David Gibson11d71002008-06-26 10:43:06 +100081 fdt32_to_cpu(bph->boot_cpuid_phys));
David Gibson3bb78bf2008-01-03 15:48:43 +110082
83 if (version >= 3)
David Gibson180a9392018-06-05 09:55:15 +100084 printf("// size_dt_strings:\t0x%"PRIx32"\n",
David Gibson11d71002008-06-26 10:43:06 +100085 fdt32_to_cpu(bph->size_dt_strings));
David Gibson3bb78bf2008-01-03 15:48:43 +110086 if (version >= 17)
David Gibson180a9392018-06-05 09:55:15 +100087 printf("// size_dt_struct:\t0x%"PRIx32"\n",
David Gibson11d71002008-06-26 10:43:06 +100088 fdt32_to_cpu(bph->size_dt_struct));
David Gibson3bb78bf2008-01-03 15:48:43 +110089 printf("\n");
90
David Gibsonb2543fc2005-08-29 14:58:27 +100091 for (i = 0; ; i++) {
David Gibson11d71002008-06-26 10:43:06 +100092 addr = fdt64_to_cpu(p_rsvmap[i].address);
93 size = fdt64_to_cpu(p_rsvmap[i].size);
David Gibsonb2543fc2005-08-29 14:58:27 +100094 if (addr == 0 && size == 0)
95 break;
96
David Gibson180a9392018-06-05 09:55:15 +100097 printf("/memreserve/ %#"PRIx64" %#"PRIx64";\n",
98 addr, size);
David Gibsonb2543fc2005-08-29 14:58:27 +100099 }
100
101 p = p_struct;
David Gibson11d71002008-06-26 10:43:06 +1000102 while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {
David Gibsonb2543fc2005-08-29 14:58:27 +1000103
Andreas Schwab78e113e2019-01-02 12:30:13 +0100104 dumpf("%04"PRIxPTR": tag: 0x%08"PRIx32" (%s)\n",
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400105 (uintptr_t)p - blob_off - 4, tag, tagname(tag));
David Gibsonb2543fc2005-08-29 14:58:27 +1000106
David Gibsonfb7c7ac2007-09-26 13:11:05 +1000107 if (tag == FDT_BEGIN_NODE) {
David Gibsonb2543fc2005-08-29 14:58:27 +1000108 s = p;
109 p = PALIGN(p + strlen(s) + 1, 4);
110
111 if (*s == '\0')
112 s = "/";
113
114 printf("%*s%s {\n", depth * shift, "", s);
115
116 depth++;
117 continue;
118 }
119
David Gibsonfb7c7ac2007-09-26 13:11:05 +1000120 if (tag == FDT_END_NODE) {
David Gibsonb2543fc2005-08-29 14:58:27 +1000121 depth--;
122
123 printf("%*s};\n", depth * shift, "");
124 continue;
125 }
126
David Gibsonfb7c7ac2007-09-26 13:11:05 +1000127 if (tag == FDT_NOP) {
David Gibsonb2543fc2005-08-29 14:58:27 +1000128 printf("%*s// [NOP]\n", depth * shift, "");
129 continue;
130 }
131
David Gibsonfb7c7ac2007-09-26 13:11:05 +1000132 if (tag != FDT_PROP) {
David Gibson180a9392018-06-05 09:55:15 +1000133 fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag);
David Gibsonb2543fc2005-08-29 14:58:27 +1000134 break;
135 }
David Gibson11d71002008-06-26 10:43:06 +1000136 sz = fdt32_to_cpu(GET_CELL(p));
137 s = p_strings + fdt32_to_cpu(GET_CELL(p));
David Gibson46c88df2007-03-14 11:02:40 +1100138 if (version < 16 && sz >= 8)
David Gibsonb2543fc2005-08-29 14:58:27 +1000139 p = PALIGN(p, 8);
140 t = p;
141
142 p = PALIGN(p + sz, 4);
143
Andreas Schwab78e113e2019-01-02 12:30:13 +0100144 dumpf("%04"PRIxPTR": string: %s\n", (uintptr_t)s - blob_off, s);
145 dumpf("%04"PRIxPTR": value\n", (uintptr_t)t - blob_off);
David Gibsonb2543fc2005-08-29 14:58:27 +1000146 printf("%*s%s", depth * shift, "", s);
Simon Glassd20391d2013-01-21 12:59:16 -0800147 utilfdt_print_data(t, sz);
David Gibsonb2543fc2005-08-29 14:58:27 +1000148 printf(";\n");
149 }
150}
151
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400152/* Usage related data. */
153static const char usage_synopsis[] = "fdtdump [options] <file>";
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400154static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400155static struct option const usage_long_opts[] = {
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400156 {"debug", no_argument, NULL, 'd'},
Mike Frysingerfdc73872013-04-15 22:13:13 -0400157 {"scan", no_argument, NULL, 's'},
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400158 USAGE_COMMON_LONG_OPTS
159};
160static const char * const usage_opts_help[] = {
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400161 "Dump debug information while decoding the file",
Mike Frysingerfdc73872013-04-15 22:13:13 -0400162 "Scan for an embedded fdt in file",
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400163 USAGE_COMMON_OPTS_HELP
164};
David Gibsonb2543fc2005-08-29 14:58:27 +1000165
Pierre-Clément Tosief1978a2022-05-30 20:57:34 +0100166static bool valid_header(char *p, size_t len)
Heinrich Schuchardt0931cea2016-12-22 00:59:06 +0100167{
168 if (len < sizeof(struct fdt_header) ||
169 fdt_magic(p) != FDT_MAGIC ||
170 fdt_version(p) > MAX_VERSION ||
David Gibsonc2258842017-04-18 12:52:08 +1000171 fdt_last_comp_version(p) > MAX_VERSION ||
Heinrich Schuchardt0931cea2016-12-22 00:59:06 +0100172 fdt_totalsize(p) >= len ||
173 fdt_off_dt_struct(p) >= len ||
174 fdt_off_dt_strings(p) >= len)
175 return 0;
176 else
177 return 1;
178}
179
David Gibsonb2543fc2005-08-29 14:58:27 +1000180int main(int argc, char *argv[])
181{
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400182 int opt;
183 const char *file;
David Gibson0ef21052009-11-17 17:00:53 +1100184 char *buf;
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400185 bool debug = false;
Mike Frysingerfdc73872013-04-15 22:13:13 -0400186 bool scan = false;
David Gibsonfb9c6ab2018-03-17 14:53:23 +1100187 size_t len;
David Gibsonb2543fc2005-08-29 14:58:27 +1000188
David Gibson548aea22017-04-18 13:05:08 +1000189 fprintf(stderr, "\n"
190"**** fdtdump is a low-level debugging tool, not meant for general use.\n"
191"**** If you want to decompile a dtb, you probably want\n"
192"**** dtc -I dtb -O dts <filename>\n\n"
193 );
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400194 while ((opt = util_getopt_long()) != EOF) {
195 switch (opt) {
196 case_USAGE_COMMON_FLAGS
Mike Frysingerfdc73872013-04-15 22:13:13 -0400197
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400198 case 'd':
199 debug = true;
200 break;
Mike Frysingerfdc73872013-04-15 22:13:13 -0400201 case 's':
202 scan = true;
203 break;
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400204 }
David Gibsonb2543fc2005-08-29 14:58:27 +1000205 }
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400206 if (optind != argc - 1)
Mike Frysingerb9e80652013-05-24 18:04:43 +1000207 usage("missing input filename");
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400208 file = argv[optind];
David Gibsonb2543fc2005-08-29 14:58:27 +1000209
David Gibson6473a212018-03-16 18:27:03 +1100210 buf = utilfdt_read(file, &len);
Mike Frysingerbe8d1c82013-04-15 22:13:12 -0400211 if (!buf)
212 die("could not read: %s\n", file);
213
Mike Frysingerfdc73872013-04-15 22:13:13 -0400214 /* try and locate an embedded fdt in a bigger blob */
215 if (scan) {
Jean-Christophe Duboise24d39a2016-07-13 02:31:13 +0200216 unsigned char smagic[FDT_MAGIC_SIZE];
Mike Frysingerfdc73872013-04-15 22:13:13 -0400217 char *p = buf;
218 char *endp = buf + len;
219
Pierre-Clément Tosief1978a2022-05-30 20:57:34 +0100220 fdt32_st(smagic, FDT_MAGIC);
Mike Frysingerfdc73872013-04-15 22:13:13 -0400221
222 /* poor man's memmem */
Jean-Christophe Duboise24d39a2016-07-13 02:31:13 +0200223 while ((endp - p) >= FDT_MAGIC_SIZE) {
224 p = memchr(p, smagic[0], endp - p - FDT_MAGIC_SIZE);
Mike Frysingerfdc73872013-04-15 22:13:13 -0400225 if (!p)
226 break;
227 if (fdt_magic(p) == FDT_MAGIC) {
228 /* try and validate the main struct */
229 off_t this_len = endp - p;
Heinrich Schuchardt0931cea2016-12-22 00:59:06 +0100230 if (valid_header(p, this_len))
Mike Frysingerfdc73872013-04-15 22:13:13 -0400231 break;
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400232 if (debug)
Dan Horák49903ae2018-10-22 12:14:44 +0200233 printf("%s: skipping fdt magic at offset %#tx\n",
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400234 file, p - buf);
Mike Frysingerfdc73872013-04-15 22:13:13 -0400235 }
236 ++p;
237 }
Pierre-Clément Tosief1978a2022-05-30 20:57:34 +0100238 if (!p || (size_t)(endp - p) < sizeof(struct fdt_header))
Mike Frysingerfdc73872013-04-15 22:13:13 -0400239 die("%s: could not locate fdt magic\n", file);
Dan Horák49903ae2018-10-22 12:14:44 +0200240 printf("%s: found fdt at offset %#tx\n", file, p - buf);
Mike Frysingerfdc73872013-04-15 22:13:13 -0400241 buf = p;
Heinrich Schuchardt0931cea2016-12-22 00:59:06 +0100242 } else if (!valid_header(buf, len))
243 die("%s: header is not valid\n", file);
Mike Frysingerfdc73872013-04-15 22:13:13 -0400244
Mike Frysinger8ec013a2013-04-15 22:13:17 -0400245 dump_blob(buf, debug);
David Gibsonb2543fc2005-08-29 14:58:27 +1000246
247 return 0;
248}