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