1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
4 | * |
5 | * Portions from U-Boot cmd_fdt.c (C) Copyright 2007 |
6 | * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com |
7 | * Based on code written by: |
8 | * Pantelis Antoniou <pantelis.antoniou@gmail.com> and |
9 | * Matthew McClintock <msm@freescale.com> |
10 | */ |
11 | |
12 | #include <assert.h> |
13 | #include <ctype.h> |
14 | #include <getopt.h> |
15 | #include <stdio.h> |
16 | #include <stdlib.h> |
17 | #include <string.h> |
18 | |
19 | #include <libfdt.h> |
20 | |
21 | #include "util.h" |
22 | |
23 | enum display_mode { |
24 | MODE_SHOW_VALUE, /* show values for node properties */ |
25 | MODE_LIST_PROPS, /* list the properties for a node */ |
26 | MODE_LIST_SUBNODES, /* list the subnodes of a node */ |
27 | }; |
28 | |
29 | /* Holds information which controls our output and options */ |
30 | struct display_info { |
31 | int type; /* data type (s/i/u/x or 0 for default) */ |
32 | int size; /* data size (1/2/4) */ |
33 | enum display_mode mode; /* display mode that we are using */ |
34 | const char *default_val; /* default value if node/property not found */ |
35 | }; |
36 | |
37 | static void report_error(const char *where, int err) |
38 | { |
39 | fprintf(stderr, format: "Error at '%s': %s\n" , where, fdt_strerror(err)); |
40 | } |
41 | |
42 | /** |
43 | * Displays data of a given length according to selected options |
44 | * |
45 | * If a specific data type is provided in disp, then this is used. Otherwise |
46 | * we try to guess the data type / size from the contents. |
47 | * |
48 | * @param disp Display information / options |
49 | * @param data Data to display |
50 | * @param len Maximum length of buffer |
51 | * @return 0 if ok, -1 if data does not match format |
52 | */ |
53 | static int show_data(struct display_info *disp, const char *data, int len) |
54 | { |
55 | int i, size; |
56 | const uint8_t *p = (const uint8_t *)data; |
57 | const char *s; |
58 | int value; |
59 | int is_string; |
60 | char fmt[3]; |
61 | |
62 | /* no data, don't print */ |
63 | if (len == 0) |
64 | return 0; |
65 | |
66 | is_string = (disp->type) == 's' || |
67 | (!disp->type && util_is_printable_string(data, len)); |
68 | if (is_string) { |
69 | if (data[len - 1] != '\0') { |
70 | fprintf(stderr, format: "Unterminated string\n" ); |
71 | return -1; |
72 | } |
73 | for (s = data; s - data < len; s += strlen(s: s) + 1) { |
74 | if (s != data) |
75 | printf(format: " " ); |
76 | printf(format: "%s" , (const char *)s); |
77 | } |
78 | return 0; |
79 | } |
80 | size = disp->size; |
81 | if (size == -1) { |
82 | size = (len % 4) == 0 ? 4 : 1; |
83 | } else if (len % size) { |
84 | fprintf(stderr, format: "Property length must be a multiple of " |
85 | "selected data size\n" ); |
86 | return -1; |
87 | } |
88 | fmt[0] = '%'; |
89 | fmt[1] = disp->type ? disp->type : 'd'; |
90 | fmt[2] = '\0'; |
91 | for (i = 0; i < len; i += size, p += size) { |
92 | if (i) |
93 | printf(format: " " ); |
94 | value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) : |
95 | size == 2 ? (*p << 8) | p[1] : *p; |
96 | printf(format: fmt, value); |
97 | } |
98 | return 0; |
99 | } |
100 | |
101 | /** |
102 | * List all properties in a node, one per line. |
103 | * |
104 | * @param blob FDT blob |
105 | * @param node Node to display |
106 | * @return 0 if ok, or FDT_ERR... if not. |
107 | */ |
108 | static int list_properties(const void *blob, int node) |
109 | { |
110 | const struct fdt_property *data; |
111 | const char *name; |
112 | int prop; |
113 | |
114 | prop = fdt_first_property_offset(blob, node); |
115 | do { |
116 | /* Stop silently when there are no more properties */ |
117 | if (prop < 0) |
118 | return prop == -FDT_ERR_NOTFOUND ? 0 : prop; |
119 | data = fdt_get_property_by_offset(blob, prop, NULL); |
120 | name = fdt_string(blob, fdt32_to_cpu(data->nameoff)); |
121 | if (name) |
122 | puts(s: name); |
123 | prop = fdt_next_property_offset(blob, prop); |
124 | } while (1); |
125 | } |
126 | |
127 | #define MAX_LEVEL 32 /* how deeply nested we will go */ |
128 | |
129 | /** |
130 | * List all subnodes in a node, one per line |
131 | * |
132 | * @param blob FDT blob |
133 | * @param node Node to display |
134 | * @return 0 if ok, or FDT_ERR... if not. |
135 | */ |
136 | static int list_subnodes(const void *blob, int node) |
137 | { |
138 | int nextoffset; /* next node offset from libfdt */ |
139 | uint32_t tag; /* current tag */ |
140 | int level = 0; /* keep track of nesting level */ |
141 | const char *pathp; |
142 | int depth = 1; /* the assumed depth of this node */ |
143 | |
144 | while (level >= 0) { |
145 | tag = fdt_next_tag(blob, node, &nextoffset); |
146 | switch (tag) { |
147 | case FDT_BEGIN_NODE: |
148 | pathp = fdt_get_name(blob, node, NULL); |
149 | if (level <= depth) { |
150 | if (pathp == NULL) |
151 | pathp = "/* NULL pointer error */" ; |
152 | if (*pathp == '\0') |
153 | pathp = "/" ; /* root is nameless */ |
154 | if (level == 1) |
155 | puts(s: pathp); |
156 | } |
157 | level++; |
158 | if (level >= MAX_LEVEL) { |
159 | printf(format: "Nested too deep, aborting.\n" ); |
160 | return 1; |
161 | } |
162 | break; |
163 | case FDT_END_NODE: |
164 | level--; |
165 | if (level == 0) |
166 | level = -1; /* exit the loop */ |
167 | break; |
168 | case FDT_END: |
169 | return 1; |
170 | case FDT_PROP: |
171 | break; |
172 | default: |
173 | if (level <= depth) |
174 | printf(format: "Unknown tag 0x%08X\n" , tag); |
175 | return 1; |
176 | } |
177 | node = nextoffset; |
178 | } |
179 | return 0; |
180 | } |
181 | |
182 | /** |
183 | * Show the data for a given node (and perhaps property) according to the |
184 | * display option provided. |
185 | * |
186 | * @param blob FDT blob |
187 | * @param disp Display information / options |
188 | * @param node Node to display |
189 | * @param property Name of property to display, or NULL if none |
190 | * @return 0 if ok, -ve on error |
191 | */ |
192 | static int show_data_for_item(const void *blob, struct display_info *disp, |
193 | int node, const char *property) |
194 | { |
195 | const void *value = NULL; |
196 | int len, err = 0; |
197 | |
198 | switch (disp->mode) { |
199 | case MODE_LIST_PROPS: |
200 | err = list_properties(blob, node); |
201 | break; |
202 | |
203 | case MODE_LIST_SUBNODES: |
204 | err = list_subnodes(blob, node); |
205 | break; |
206 | |
207 | default: |
208 | assert(property); |
209 | value = fdt_getprop(blob, node, property, &len); |
210 | if (value) { |
211 | if (show_data(disp, data: value, len)) |
212 | err = -1; |
213 | else |
214 | printf(format: "\n" ); |
215 | } else if (disp->default_val) { |
216 | puts(s: disp->default_val); |
217 | } else { |
218 | report_error(where: property, err: len); |
219 | err = -1; |
220 | } |
221 | break; |
222 | } |
223 | |
224 | return err; |
225 | } |
226 | |
227 | /** |
228 | * Run the main fdtget operation, given a filename and valid arguments |
229 | * |
230 | * @param disp Display information / options |
231 | * @param filename Filename of blob file |
232 | * @param arg List of arguments to process |
233 | * @param arg_count Number of arguments |
234 | * @param return 0 if ok, -ve on error |
235 | */ |
236 | static int do_fdtget(struct display_info *disp, const char *filename, |
237 | char **arg, int arg_count, int args_per_step) |
238 | { |
239 | char *blob; |
240 | const char *prop; |
241 | int i, node; |
242 | |
243 | blob = utilfdt_read(filename); |
244 | if (!blob) |
245 | return -1; |
246 | |
247 | for (i = 0; i + args_per_step <= arg_count; i += args_per_step) { |
248 | node = fdt_path_offset(blob, arg[i]); |
249 | if (node < 0) { |
250 | if (disp->default_val) { |
251 | puts(s: disp->default_val); |
252 | continue; |
253 | } else { |
254 | report_error(where: arg[i], err: node); |
255 | return -1; |
256 | } |
257 | } |
258 | prop = args_per_step == 1 ? NULL : arg[i + 1]; |
259 | |
260 | if (show_data_for_item(blob, disp, node, property: prop)) |
261 | return -1; |
262 | } |
263 | return 0; |
264 | } |
265 | |
266 | static const char *usage_msg = |
267 | "fdtget - read values from device tree\n" |
268 | "\n" |
269 | "Each value is printed on a new line.\n\n" |
270 | "Usage:\n" |
271 | " fdtget <options> <dt file> [<node> <property>]...\n" |
272 | " fdtget -p <options> <dt file> [<node> ]...\n" |
273 | "Options:\n" |
274 | "\t-t <type>\tType of data\n" |
275 | "\t-p\t\tList properties for each node\n" |
276 | "\t-l\t\tList subnodes for each node\n" |
277 | "\t-d\t\tDefault value to display when the property is " |
278 | "missing\n" |
279 | "\t-h\t\tPrint this help\n\n" |
280 | USAGE_TYPE_MSG; |
281 | |
282 | static void usage(const char *msg) |
283 | { |
284 | if (msg) |
285 | fprintf(stderr, format: "Error: %s\n\n" , msg); |
286 | |
287 | fprintf(stderr, format: "%s" , usage_msg); |
288 | exit(status: 2); |
289 | } |
290 | |
291 | int main(int argc, char *argv[]) |
292 | { |
293 | char *filename = NULL; |
294 | struct display_info disp; |
295 | int args_per_step = 2; |
296 | |
297 | /* set defaults */ |
298 | memset(s: &disp, c: '\0', n: sizeof(disp)); |
299 | disp.size = -1; |
300 | disp.mode = MODE_SHOW_VALUE; |
301 | for (;;) { |
302 | int c = getopt(argc: argc, argv: argv, shortopts: "d:hlpt:" ); |
303 | if (c == -1) |
304 | break; |
305 | |
306 | switch (c) { |
307 | case 'h': |
308 | case '?': |
309 | usage(NULL); |
310 | |
311 | case 't': |
312 | if (utilfdt_decode_type(fmt: optarg, type: &disp.type, |
313 | size: &disp.size)) |
314 | usage("Invalid type string" ); |
315 | break; |
316 | |
317 | case 'p': |
318 | disp.mode = MODE_LIST_PROPS; |
319 | args_per_step = 1; |
320 | break; |
321 | |
322 | case 'l': |
323 | disp.mode = MODE_LIST_SUBNODES; |
324 | args_per_step = 1; |
325 | break; |
326 | |
327 | case 'd': |
328 | disp.default_val = optarg; |
329 | break; |
330 | } |
331 | } |
332 | |
333 | if (optind < argc) |
334 | filename = argv[optind++]; |
335 | if (!filename) |
336 | usage("Missing filename" ); |
337 | |
338 | argv += optind; |
339 | argc -= optind; |
340 | |
341 | /* Allow no arguments, and silently succeed */ |
342 | if (!argc) |
343 | return 0; |
344 | |
345 | /* Check for node, property arguments */ |
346 | if (args_per_step == 2 && (argc % 2)) |
347 | usage("Must have an even number of arguments" ); |
348 | |
349 | if (do_fdtget(disp: &disp, filename, arg: argv, arg_count: argc, args_per_step)) |
350 | return 1; |
351 | return 0; |
352 | } |
353 | |