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