1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * fdtdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
4 */
5
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <inttypes.h>
13
14 #include <libfdt.h>
15 #include <libfdt_env.h>
16 #include <fdt.h>
17
18 #include "util.h"
19
20 #define FDT_MAGIC_SIZE 4
21 #define MAX_VERSION 17U
22
23 #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
24 #define PALIGN(p, a) ((void *)(ALIGN((uintptr_t)(p), (a))))
25 #define GET_CELL(p) (p += 4, *((const fdt32_t *)(p-4)))
26
tagname(uint32_t tag)27 static const char *tagname(uint32_t tag)
28 {
29 static const char * const names[] = {
30 #define TN(t) [t] = #t
31 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
dump_blob(void * blob,bool debug)47 static void dump_blob(void *blob, bool debug)
48 {
49 uintptr_t blob_off = (uintptr_t)blob;
50 struct fdt_header *bph = blob;
51 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);
54 struct fdt_reserve_entry *p_rsvmap =
55 (struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap);
56 const char *p_struct = (const char *)blob + off_dt;
57 const char *p_strings = (const char *)blob + off_str;
58 uint32_t version = fdt32_to_cpu(bph->version);
59 uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
60 uint32_t tag;
61 const char *p, *s, *t;
62 int depth, sz, shift;
63 int i;
64 uint64_t addr, size;
65
66 depth = 0;
67 shift = 4;
68
69 printf("/dts-v1/;\n");
70 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",
78 fdt32_to_cpu(bph->last_comp_version));
79 if (version >= 2)
80 printf("// boot_cpuid_phys:\t0x%"PRIx32"\n",
81 fdt32_to_cpu(bph->boot_cpuid_phys));
82
83 if (version >= 3)
84 printf("// size_dt_strings:\t0x%"PRIx32"\n",
85 fdt32_to_cpu(bph->size_dt_strings));
86 if (version >= 17)
87 printf("// size_dt_struct:\t0x%"PRIx32"\n",
88 fdt32_to_cpu(bph->size_dt_struct));
89 printf("\n");
90
91 for (i = 0; ; i++) {
92 addr = fdt64_to_cpu(p_rsvmap[i].address);
93 size = fdt64_to_cpu(p_rsvmap[i].size);
94 if (addr == 0 && size == 0)
95 break;
96
97 printf("/memreserve/ %#"PRIx64" %#"PRIx64";\n",
98 addr, size);
99 }
100
101 p = p_struct;
102 while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {
103
104 dumpf("%04"PRIxPTR": tag: 0x%08"PRIx32" (%s)\n",
105 (uintptr_t)p - blob_off - 4, tag, tagname(tag));
106
107 if (tag == FDT_BEGIN_NODE) {
108 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
120 if (tag == FDT_END_NODE) {
121 depth--;
122
123 printf("%*s};\n", depth * shift, "");
124 continue;
125 }
126
127 if (tag == FDT_NOP) {
128 printf("%*s// [NOP]\n", depth * shift, "");
129 continue;
130 }
131
132 if (tag != FDT_PROP) {
133 fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag);
134 break;
135 }
136 sz = fdt32_to_cpu(GET_CELL(p));
137 s = p_strings + fdt32_to_cpu(GET_CELL(p));
138 if (version < 16 && sz >= 8)
139 p = PALIGN(p, 8);
140 t = p;
141
142 p = PALIGN(p + sz, 4);
143
144 dumpf("%04"PRIxPTR": string: %s\n", (uintptr_t)s - blob_off, s);
145 dumpf("%04"PRIxPTR": value\n", (uintptr_t)t - blob_off);
146 printf("%*s%s", depth * shift, "", s);
147 utilfdt_print_data(t, sz);
148 printf(";\n");
149 }
150 }
151
152 /* Usage related data. */
153 static const char usage_synopsis[] = "fdtdump [options] <file>";
154 static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
155 static struct option const usage_long_opts[] = {
156 {"debug", no_argument, NULL, 'd'},
157 {"scan", no_argument, NULL, 's'},
158 USAGE_COMMON_LONG_OPTS
159 };
160 static const char * const usage_opts_help[] = {
161 "Dump debug information while decoding the file",
162 "Scan for an embedded fdt in file",
163 USAGE_COMMON_OPTS_HELP
164 };
165
valid_header(char * p,size_t len)166 static bool valid_header(char *p, size_t len)
167 {
168 if (len < sizeof(struct fdt_header) ||
169 fdt_magic(p) != FDT_MAGIC ||
170 fdt_version(p) > MAX_VERSION ||
171 fdt_last_comp_version(p) > MAX_VERSION ||
172 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
main(int argc,char * argv[])180 int main(int argc, char *argv[])
181 {
182 int opt;
183 const char *file;
184 char *buf;
185 bool debug = false;
186 bool scan = false;
187 size_t len;
188
189 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 );
194 while ((opt = util_getopt_long()) != EOF) {
195 switch (opt) {
196 case_USAGE_COMMON_FLAGS
197
198 case 'd':
199 debug = true;
200 break;
201 case 's':
202 scan = true;
203 break;
204 }
205 }
206 if (optind != argc - 1)
207 usage("missing input filename");
208 file = argv[optind];
209
210 buf = utilfdt_read(file, &len);
211 if (!buf)
212 die("could not read: %s\n", file);
213
214 /* try and locate an embedded fdt in a bigger blob */
215 if (scan) {
216 unsigned char smagic[FDT_MAGIC_SIZE];
217 char *p = buf;
218 char *endp = buf + len;
219
220 fdt32_st(smagic, FDT_MAGIC);
221
222 /* poor man's memmem */
223 while ((endp - p) >= FDT_MAGIC_SIZE) {
224 p = memchr(p, smagic[0], endp - p - FDT_MAGIC_SIZE);
225 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;
230 if (valid_header(p, this_len))
231 break;
232 if (debug)
233 printf("%s: skipping fdt magic at offset %#tx\n",
234 file, p - buf);
235 }
236 ++p;
237 }
238 if (!p || (size_t)(endp - p) < sizeof(struct fdt_header))
239 die("%s: could not locate fdt magic\n", file);
240 printf("%s: found fdt at offset %#tx\n", file, p - buf);
241 buf = p;
242 } else if (!valid_header(buf, len))
243 die("%s: header is not valid\n", file);
244
245 dump_blob(buf, debug);
246
247 return 0;
248 }
249