]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
cd296721 SW |
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> | |
cd296721 SW |
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, "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, "Unterminated string\n"); | |
71 | return -1; | |
72 | } | |
73 | for (s = data; s - data < len; s += strlen(s) + 1) { | |
74 | if (s != data) | |
75 | printf(" "); | |
76 | printf("%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, "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(" "); | |
94 | value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) : | |
95 | size == 2 ? (*p << 8) | p[1] : *p; | |
96 | printf(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(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(pathp); | |
156 | } | |
157 | level++; | |
158 | if (level >= MAX_LEVEL) { | |
159 | printf("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("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, value, len)) | |
212 | err = -1; | |
213 | else | |
214 | printf("\n"); | |
215 | } else if (disp->default_val) { | |
216 | puts(disp->default_val); | |
217 | } else { | |
218 | report_error(property, 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(disp->default_val); | |
252 | continue; | |
253 | } else { | |
254 | report_error(arg[i], 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, 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, "Error: %s\n\n", msg); | |
286 | ||
287 | fprintf(stderr, "%s", usage_msg); | |
288 | exit(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(&disp, '\0', sizeof(disp)); | |
299 | disp.size = -1; | |
300 | disp.mode = MODE_SHOW_VALUE; | |
301 | for (;;) { | |
302 | int c = getopt(argc, argv, "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(optarg, &disp.type, | |
313 | &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, filename, argv, argc, args_per_step)) | |
350 | return 1; | |
351 | return 0; | |
352 | } |