]> git.ipfire.org Git - people/ms/u-boot.git/blame - tools/fdtgrep.c
sh: Do not provide strncmp
[people/ms/u-boot.git] / tools / fdtgrep.c
CommitLineData
1043d0a0
SG
1/*
2 * Copyright (c) 2013, Google Inc.
3 * Written by Simon Glass <sjg@chromium.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 *
7 * Perform a grep of an FDT either displaying the source subset or producing
8 * a new .dtb subset which can be used as required.
9 */
10
11#include <assert.h>
12#include <ctype.h>
d2bf1152 13#include <errno.h>
1043d0a0 14#include <getopt.h>
d2bf1152
MY
15#include <fcntl.h>
16#include <stdbool.h>
1043d0a0
SG
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21
ae9ace70 22#include "fdt_host.h"
b95a5190 23#include "libfdt_internal.h"
1043d0a0
SG
24
25/* Define DEBUG to get some debugging output on stderr */
26#ifdef DEBUG
27#define debug(a, b...) fprintf(stderr, a, ## b)
28#else
29#define debug(a, b...)
30#endif
31
32/* A linked list of values we are grepping for */
33struct value_node {
34 int type; /* Types this value matches (FDT_IS... mask) */
35 int include; /* 1 to include matches, 0 to exclude */
36 const char *string; /* String to match */
37 struct value_node *next; /* Pointer to next node, or NULL */
38};
39
40/* Output formats we support */
41enum output_t {
42 OUT_DTS, /* Device tree source */
43 OUT_DTB, /* Valid device tree binary */
44 OUT_BIN, /* Fragment of .dtb, for hashing */
45};
46
47/* Holds information which controls our output and options */
48struct display_info {
49 enum output_t output; /* Output format */
50 int add_aliases; /* Add aliases node to output */
51 int all; /* Display all properties/nodes */
52 int colour; /* Display output in ANSI colour */
53 int region_list; /* Output a region list */
54 int flags; /* Flags (FDT_REG_...) */
55 int list_strings; /* List strings in string table */
56 int show_offset; /* Show offset */
57 int show_addr; /* Show address */
58 int header; /* Output an FDT header */
59 int diff; /* Show +/- diff markers */
60 int include_root; /* Include the root node and all properties */
61 int remove_strings; /* Remove unused strings */
62 int show_dts_version; /* Put '/dts-v1/;' on the first line */
63 int types_inc; /* Mask of types that we include (FDT_IS...) */
64 int types_exc; /* Mask of types that we exclude (FDT_IS...) */
65 int invert; /* Invert polarity of match */
66 struct value_node *value_head; /* List of values to match */
67 const char *output_fname; /* Output filename */
68 FILE *fout; /* File to write dts/dtb output */
69};
70
71static void report_error(const char *where, int err)
72{
73 fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
74}
75
76/* Supported ANSI colours */
77enum {
78 COL_BLACK,
79 COL_RED,
80 COL_GREEN,
81 COL_YELLOW,
82 COL_BLUE,
83 COL_MAGENTA,
84 COL_CYAN,
85 COL_WHITE,
86
87 COL_NONE = -1,
88};
89
90/**
91 * print_ansi_colour() - Print out the ANSI sequence for a colour
92 *
93 * @fout: Output file
94 * @col: Colour to output (COL_...), or COL_NONE to reset colour
95 */
96static void print_ansi_colour(FILE *fout, int col)
97{
98 if (col == COL_NONE)
99 fprintf(fout, "\033[0m");
100 else
101 fprintf(fout, "\033[1;%dm", col + 30);
102}
103
104
105/**
106 * value_add() - Add a new value to our list of things to grep for
107 *
108 * @disp: Display structure, holding info about our options
109 * @headp: Pointer to header pointer of list
110 * @type: Type of this value (FDT_IS_...)
111 * @include: 1 if we want to include matches, 0 to exclude
112 * @str: String value to match
113 */
114static int value_add(struct display_info *disp, struct value_node **headp,
115 int type, int include, const char *str)
116{
117 struct value_node *node;
118
119 /*
120 * Keep track of which types we are excluding/including. We don't
121 * allow both including and excluding things, because it doesn't make
122 * sense. 'Including' means that everything not mentioned is
123 * excluded. 'Excluding' means that everything not mentioned is
124 * included. So using the two together would be meaningless.
125 */
126 if (include)
127 disp->types_inc |= type;
128 else
129 disp->types_exc |= type;
130 if (disp->types_inc & disp->types_exc & type) {
131 fprintf(stderr,
132 "Cannot use both include and exclude for '%s'\n", str);
133 return -1;
134 }
135
136 str = strdup(str);
137 node = malloc(sizeof(*node));
138 if (!str || !node) {
139 fprintf(stderr, "Out of memory\n");
140 return -1;
141 }
142 node->next = *headp;
143 node->type = type;
144 node->include = include;
145 node->string = str;
146 *headp = node;
147
148 return 0;
149}
150
151static bool util_is_printable_string(const void *data, int len)
152{
153 const char *s = data;
154 const char *ss, *se;
155
156 /* zero length is not */
157 if (len == 0)
158 return 0;
159
160 /* must terminate with zero */
161 if (s[len - 1] != '\0')
162 return 0;
163
164 se = s + len;
165
166 while (s < se) {
167 ss = s;
168 while (s < se && *s && isprint((unsigned char)*s))
169 s++;
170
171 /* not zero, or not done yet */
172 if (*s != '\0' || s == ss)
173 return 0;
174
175 s++;
176 }
177
178 return 1;
179}
180
181static void utilfdt_print_data(const char *data, int len)
182{
183 int i;
184 const char *p = data;
185 const char *s;
186
187 /* no data, don't print */
188 if (len == 0)
189 return;
190
191 if (util_is_printable_string(data, len)) {
192 printf(" = ");
193
194 s = data;
195 do {
196 printf("\"%s\"", s);
197 s += strlen(s) + 1;
198 if (s < data + len)
199 printf(", ");
200 } while (s < data + len);
201
202 } else if ((len % 4) == 0) {
203 const uint32_t *cell = (const uint32_t *)data;
204
205 printf(" = <");
206 for (i = 0, len /= 4; i < len; i++)
207 printf("0x%08x%s", fdt32_to_cpu(cell[i]),
208 i < (len - 1) ? " " : "");
209 printf(">");
210 } else {
211 printf(" = [");
212 for (i = 0; i < len; i++)
213 printf("%02x%s", *p++, i < len - 1 ? " " : "");
214 printf("]");
215 }
216}
217
218/**
219 * display_fdt_by_regions() - Display regions of an FDT source
220 *
221 * This dumps an FDT as source, but only certain regions of it. This is the
222 * final stage of the grep - we have a list of regions we want to display,
223 * and this function displays them.
224 *
225 * @disp: Display structure, holding info about our options
226 * @blob: FDT blob to display
227 * @region: List of regions to display
228 * @count: Number of regions
229 */
230static int display_fdt_by_regions(struct display_info *disp, const void *blob,
231 struct fdt_region region[], int count)
232{
233 struct fdt_region *reg = region, *reg_end = region + count;
234 uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(blob);
235 int base = fdt_off_dt_struct(blob);
236 int version = fdt_version(blob);
237 int offset, nextoffset;
238 int tag, depth, shift;
239 FILE *f = disp->fout;
240 uint64_t addr, size;
241 int in_region;
242 int file_ofs;
243 int i;
244
245 if (disp->show_dts_version)
246 fprintf(f, "/dts-v1/;\n");
247
248 if (disp->header) {
249 fprintf(f, "// magic:\t\t0x%x\n", fdt_magic(blob));
250 fprintf(f, "// totalsize:\t\t0x%x (%d)\n", fdt_totalsize(blob),
251 fdt_totalsize(blob));
252 fprintf(f, "// off_dt_struct:\t0x%x\n",
253 fdt_off_dt_struct(blob));
254 fprintf(f, "// off_dt_strings:\t0x%x\n",
255 fdt_off_dt_strings(blob));
256 fprintf(f, "// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap);
257 fprintf(f, "// version:\t\t%d\n", version);
258 fprintf(f, "// last_comp_version:\t%d\n",
259 fdt_last_comp_version(blob));
260 if (version >= 2) {
261 fprintf(f, "// boot_cpuid_phys:\t0x%x\n",
262 fdt_boot_cpuid_phys(blob));
263 }
264 if (version >= 3) {
265 fprintf(f, "// size_dt_strings:\t0x%x\n",
266 fdt_size_dt_strings(blob));
267 }
268 if (version >= 17) {
269 fprintf(f, "// size_dt_struct:\t0x%x\n",
270 fdt_size_dt_struct(blob));
271 }
272 fprintf(f, "\n");
273 }
274
275 if (disp->flags & FDT_REG_ADD_MEM_RSVMAP) {
276 const struct fdt_reserve_entry *p_rsvmap;
277
278 p_rsvmap = (const struct fdt_reserve_entry *)
279 ((const char *)blob + off_mem_rsvmap);
280 for (i = 0; ; i++) {
281 addr = fdt64_to_cpu(p_rsvmap[i].address);
282 size = fdt64_to_cpu(p_rsvmap[i].size);
283 if (addr == 0 && size == 0)
284 break;
285
286 fprintf(f, "/memreserve/ %llx %llx;\n",
287 (unsigned long long)addr,
288 (unsigned long long)size);
289 }
290 }
291
292 depth = 0;
293 nextoffset = 0;
294 shift = 4; /* 4 spaces per indent */
295 do {
296 const struct fdt_property *prop;
297 const char *name;
298 int show;
299 int len;
300
301 offset = nextoffset;
302
303 /*
304 * Work out the file offset of this offset, and decide
305 * whether it is in the region list or not
306 */
307 file_ofs = base + offset;
308 if (reg < reg_end && file_ofs >= reg->offset + reg->size)
309 reg++;
310 in_region = reg < reg_end && file_ofs >= reg->offset &&
311 file_ofs < reg->offset + reg->size;
312 tag = fdt_next_tag(blob, offset, &nextoffset);
313
314 if (tag == FDT_END)
315 break;
316 show = in_region || disp->all;
317 if (show && disp->diff)
318 fprintf(f, "%c", in_region ? '+' : '-');
319
320 if (!show) {
321 /* Do this here to avoid 'if (show)' in every 'case' */
322 if (tag == FDT_BEGIN_NODE)
323 depth++;
324 else if (tag == FDT_END_NODE)
325 depth--;
326 continue;
327 }
328 if (tag != FDT_END) {
329 if (disp->show_addr)
330 fprintf(f, "%4x: ", file_ofs);
331 if (disp->show_offset)
332 fprintf(f, "%4x: ", file_ofs - base);
333 }
334
335 /* Green means included, red means excluded */
336 if (disp->colour)
337 print_ansi_colour(f, in_region ? COL_GREEN : COL_RED);
338
339 switch (tag) {
340 case FDT_PROP:
341 prop = fdt_get_property_by_offset(blob, offset, NULL);
342 name = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
343 fprintf(f, "%*s%s", depth * shift, "", name);
344 utilfdt_print_data(prop->data,
345 fdt32_to_cpu(prop->len));
346 fprintf(f, ";");
347 break;
348
349 case FDT_NOP:
350 fprintf(f, "%*s// [NOP]", depth * shift, "");
351 break;
352
353 case FDT_BEGIN_NODE:
354 name = fdt_get_name(blob, offset, &len);
355 fprintf(f, "%*s%s {", depth++ * shift, "",
356 *name ? name : "/");
357 break;
358
359 case FDT_END_NODE:
360 fprintf(f, "%*s};", --depth * shift, "");
361 break;
362 }
363
364 /* Reset colour back to normal before end of line */
365 if (disp->colour)
366 print_ansi_colour(f, COL_NONE);
367 fprintf(f, "\n");
368 } while (1);
369
370 /* Print a list of strings if requested */
371 if (disp->list_strings) {
372 const char *str;
373 int str_base = fdt_off_dt_strings(blob);
374
375 for (offset = 0; offset < fdt_size_dt_strings(blob);
376 offset += strlen(str) + 1) {
377 str = fdt_string(blob, offset);
378 int len = strlen(str) + 1;
379 int show;
380
381 /* Only print strings that are in the region */
382 file_ofs = str_base + offset;
383 in_region = reg < reg_end &&
384 file_ofs >= reg->offset &&
385 file_ofs + len < reg->offset +
386 reg->size;
387 show = in_region || disp->all;
388 if (show && disp->diff)
389 printf("%c", in_region ? '+' : '-');
390 if (disp->show_addr)
391 printf("%4x: ", file_ofs);
392 if (disp->show_offset)
393 printf("%4x: ", offset);
394 printf("%s\n", str);
395 }
396 }
397
398 return 0;
399}
400
401/**
402 * dump_fdt_regions() - Dump regions of an FDT as binary data
403 *
404 * This dumps an FDT as binary, but only certain regions of it. This is the
405 * final stage of the grep - we have a list of regions we want to dump,
406 * and this function dumps them.
407 *
408 * The output of this function may or may not be a valid FDT. To ensure it
409 * is, these disp->flags must be set:
410 *
fc0b5948 411 * FDT_REG_SUPERNODES: ensures that subnodes are preceded by their
1043d0a0
SG
412 * parents. Without this option, fragments of subnode data may be
413 * output without the supernodes above them. This is useful for
414 * hashing but cannot produce a valid FDT.
415 * FDT_REG_ADD_STRING_TAB: Adds a string table to the end of the FDT.
416 * Without this none of the properties will have names
417 * FDT_REG_ADD_MEM_RSVMAP: Adds a mem_rsvmap table - an FDT is invalid
418 * without this.
419 *
420 * @disp: Display structure, holding info about our options
421 * @blob: FDT blob to display
422 * @region: List of regions to display
423 * @count: Number of regions
424 * @out: Output destination
425 */
426static int dump_fdt_regions(struct display_info *disp, const void *blob,
427 struct fdt_region region[], int count, char *out)
428{
429 struct fdt_header *fdt;
430 int size, struct_start;
431 int ptr;
432 int i;
433
434 /* Set up a basic header (even if we don't actually write it) */
435 fdt = (struct fdt_header *)out;
436 memset(fdt, '\0', sizeof(*fdt));
437 fdt_set_magic(fdt, FDT_MAGIC);
438 struct_start = FDT_ALIGN(sizeof(struct fdt_header),
439 sizeof(struct fdt_reserve_entry));
440 fdt_set_off_mem_rsvmap(fdt, struct_start);
441 fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
442 fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
443
444 /*
445 * Calculate the total size of the regions we are writing out. The
446 * first will be the mem_rsvmap if the FDT_REG_ADD_MEM_RSVMAP flag
447 * is set. The last will be the string table if FDT_REG_ADD_STRING_TAB
448 * is set.
449 */
450 for (i = size = 0; i < count; i++)
451 size += region[i].size;
452
453 /* Bring in the mem_rsvmap section from the old file if requested */
454 if (count > 0 && (disp->flags & FDT_REG_ADD_MEM_RSVMAP)) {
455 struct_start += region[0].size;
456 size -= region[0].size;
457 }
458 fdt_set_off_dt_struct(fdt, struct_start);
459
460 /* Update the header to have the correct offsets/sizes */
461 if (count >= 2 && (disp->flags & FDT_REG_ADD_STRING_TAB)) {
462 int str_size;
463
464 str_size = region[count - 1].size;
465 fdt_set_size_dt_struct(fdt, size - str_size);
466 fdt_set_off_dt_strings(fdt, struct_start + size - str_size);
467 fdt_set_size_dt_strings(fdt, str_size);
468 fdt_set_totalsize(fdt, struct_start + size);
469 }
470
471 /* Write the header if required */
472 ptr = 0;
473 if (disp->header) {
474 ptr = sizeof(*fdt);
475 while (ptr < fdt_off_mem_rsvmap(fdt))
476 out[ptr++] = '\0';
477 }
478
479 /* Output all the nodes including any mem_rsvmap/string table */
480 for (i = 0; i < count; i++) {
481 struct fdt_region *reg = &region[i];
482
483 memcpy(out + ptr, (const char *)blob + reg->offset, reg->size);
484 ptr += reg->size;
485 }
486
487 return ptr;
488}
489
490/**
491 * show_region_list() - Print out a list of regions
492 *
493 * The list includes the region offset (absolute offset from start of FDT
494 * blob in bytes) and size
495 *
496 * @reg: List of regions to print
497 * @count: Number of regions
498 */
499static void show_region_list(struct fdt_region *reg, int count)
500{
501 int i;
502
503 printf("Regions: %d\n", count);
504 for (i = 0; i < count; i++, reg++) {
505 printf("%d: %-10x %-10x\n", i, reg->offset,
506 reg->offset + reg->size);
507 }
508}
509
510static int check_type_include(void *priv, int type, const char *data, int size)
511{
512 struct display_info *disp = priv;
513 struct value_node *val;
514 int match, none_match = FDT_IS_ANY;
515
516 /* If none of our conditions mention this type, we know nothing */
517 debug("type=%x, data=%s\n", type, data ? data : "(null)");
518 if (!((disp->types_inc | disp->types_exc) & type)) {
519 debug(" - not in any condition\n");
520 return -1;
521 }
522
523 /*
524 * Go through the list of conditions. For inclusive conditions, we
525 * return 1 at the first match. For exclusive conditions, we must
526 * check that there are no matches.
527 */
96725153
SG
528 if (data) {
529 for (val = disp->value_head; val; val = val->next) {
530 if (!(type & val->type))
531 continue;
532 match = fdt_stringlist_contains(data, size,
533 val->string);
534 debug(" - val->type=%x, str='%s', match=%d\n",
535 val->type, val->string, match);
536 if (match && val->include) {
537 debug(" - match inc %s\n", val->string);
538 return 1;
539 }
540 if (match)
541 none_match &= ~val->type;
1043d0a0 542 }
1043d0a0
SG
543 }
544
545 /*
546 * If this is an exclusive condition, and nothing matches, then we
547 * should return 1.
548 */
549 if ((type & disp->types_exc) && (none_match & type)) {
550 debug(" - match exc\n");
551 /*
552 * Allow FDT_IS_COMPAT to make the final decision in the
553 * case where there is no specific type
554 */
555 if (type == FDT_IS_NODE && disp->types_exc == FDT_ANY_GLOBAL) {
556 debug(" - supressed exc node\n");
557 return -1;
558 }
559 return 1;
560 }
561
562 /*
563 * Allow FDT_IS_COMPAT to make the final decision in the
564 * case where there is no specific type (inclusive)
565 */
566 if (type == FDT_IS_NODE && disp->types_inc == FDT_ANY_GLOBAL)
567 return -1;
568
569 debug(" - no match, types_inc=%x, types_exc=%x, none_match=%x\n",
570 disp->types_inc, disp->types_exc, none_match);
571
572 return 0;
573}
574
575/**
576 * h_include() - Include handler function for fdt_find_regions()
577 *
578 * This function decides whether to include or exclude a node, property or
579 * compatible string. The function is defined by fdt_find_regions().
580 *
581 * The algorithm is documented in the code - disp->invert is 0 for normal
582 * operation, and 1 to invert the sense of all matches.
583 *
584 * See
585 */
586static int h_include(void *priv, const void *fdt, int offset, int type,
587 const char *data, int size)
588{
589 struct display_info *disp = priv;
590 int inc, len;
591
592 inc = check_type_include(priv, type, data, size);
593 if (disp->include_root && type == FDT_IS_PROP && offset == 0 && inc)
594 return 1;
595
596 /*
597 * If the node name does not tell us anything, check the
598 * compatible string
599 */
600 if (inc == -1 && type == FDT_IS_NODE) {
601 debug(" - checking compatible2\n");
602 data = fdt_getprop(fdt, offset, "compatible", &len);
603 inc = check_type_include(priv, FDT_IS_COMPAT, data, len);
604 }
605
606 /* If we still have no idea, check for properties in the node */
607 if (inc != 1 && type == FDT_IS_NODE &&
608 (disp->types_inc & FDT_NODE_HAS_PROP)) {
609 debug(" - checking node '%s'\n",
610 fdt_get_name(fdt, offset, NULL));
611 for (offset = fdt_first_property_offset(fdt, offset);
612 offset > 0 && inc != 1;
613 offset = fdt_next_property_offset(fdt, offset)) {
614 const struct fdt_property *prop;
615 const char *str;
616
617 prop = fdt_get_property_by_offset(fdt, offset, NULL);
618 if (!prop)
619 continue;
620 str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
621 inc = check_type_include(priv, FDT_NODE_HAS_PROP, str,
622 strlen(str));
623 }
624 if (inc == -1)
625 inc = 0;
626 }
627
628 switch (inc) {
629 case 1:
630 inc = !disp->invert;
631 break;
632 case 0:
633 inc = disp->invert;
634 break;
635 }
636 debug(" - returning %d\n", inc);
637
638 return inc;
639}
640
641static int h_cmp_region(const void *v1, const void *v2)
642{
643 const struct fdt_region *region1 = v1, *region2 = v2;
644
645 return region1->offset - region2->offset;
646}
647
648static int fdtgrep_find_regions(const void *fdt,
649 int (*include_func)(void *priv, const void *fdt, int offset,
650 int type, const char *data, int size),
651 struct display_info *disp, struct fdt_region *region,
652 int max_regions, char *path, int path_len, int flags)
653{
654 struct fdt_region_state state;
655 int count;
656 int ret;
657
658 count = 0;
659 ret = fdt_first_region(fdt, include_func, disp,
660 &region[count++], path, path_len,
661 disp->flags, &state);
662 while (ret == 0) {
663 ret = fdt_next_region(fdt, include_func, disp,
664 count < max_regions ? &region[count] : NULL,
665 path, path_len, disp->flags, &state);
666 if (!ret)
667 count++;
668 }
9404fc85
SG
669 if (ret && ret != -FDT_ERR_NOTFOUND)
670 return ret;
1043d0a0
SG
671
672 /* Find all the aliases and add those regions back in */
673 if (disp->add_aliases && count < max_regions) {
674 int new_count;
675
676 new_count = fdt_add_alias_regions(fdt, region, count,
677 max_regions, &state);
9404fc85
SG
678 if (new_count == -FDT_ERR_NOTFOUND) {
679 /* No alias node found */
680 } else if (new_count < 0) {
681 return new_count;
682 } else if (new_count <= max_regions) {
f403914d
SG
683 /*
684 * The alias regions will now be at the end of the list.
685 * Sort the regions by offset to get things into the
686 * right order
687 */
688 count = new_count;
689 qsort(region, count, sizeof(struct fdt_region),
690 h_cmp_region);
1043d0a0 691 }
1043d0a0
SG
692 }
693
1043d0a0
SG
694 return count;
695}
696
697int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len)
698{
699 int fd = 0; /* assume stdin */
700 char *buf = NULL;
701 off_t bufsize = 1024, offset = 0;
702 int ret = 0;
703
704 *buffp = NULL;
705 if (strcmp(filename, "-") != 0) {
706 fd = open(filename, O_RDONLY);
707 if (fd < 0)
708 return errno;
709 }
710
711 /* Loop until we have read everything */
712 buf = malloc(bufsize);
713 if (!buf)
714 return -ENOMEM;
715 do {
716 /* Expand the buffer to hold the next chunk */
717 if (offset == bufsize) {
718 bufsize *= 2;
719 buf = realloc(buf, bufsize);
720 if (!buf)
721 return -ENOMEM;
722 }
723
724 ret = read(fd, &buf[offset], bufsize - offset);
725 if (ret < 0) {
726 ret = errno;
727 break;
728 }
729 offset += ret;
730 } while (ret != 0);
731
732 /* Clean up, including closing stdin; return errno on error */
733 close(fd);
734 if (ret)
735 free(buf);
736 else
737 *buffp = buf;
738 *len = bufsize;
739 return ret;
740}
741
742int utilfdt_read_err(const char *filename, char **buffp)
743{
744 off_t len;
745 return utilfdt_read_err_len(filename, buffp, &len);
746}
747
748char *utilfdt_read_len(const char *filename, off_t *len)
749{
750 char *buff;
751 int ret = utilfdt_read_err_len(filename, &buff, len);
752
753 if (ret) {
754 fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
755 strerror(ret));
756 return NULL;
757 }
758 /* Successful read */
759 return buff;
760}
761
762char *utilfdt_read(const char *filename)
763{
764 off_t len;
765 return utilfdt_read_len(filename, &len);
766}
767
768/**
769 * Run the main fdtgrep operation, given a filename and valid arguments
770 *
771 * @param disp Display information / options
772 * @param filename Filename of blob file
773 * @param return 0 if ok, -ve on error
774 */
775static int do_fdtgrep(struct display_info *disp, const char *filename)
776{
777 struct fdt_region *region;
778 int max_regions;
779 int count = 100;
780 char path[1024];
781 char *blob;
782 int i, ret;
783
784 blob = utilfdt_read(filename);
785 if (!blob)
786 return -1;
787 ret = fdt_check_header(blob);
788 if (ret) {
789 fprintf(stderr, "Error: %s\n", fdt_strerror(ret));
790 return ret;
791 }
792
793 /* Allow old files, but they are untested */
794 if (fdt_version(blob) < 17 && disp->value_head) {
795 fprintf(stderr,
796 "Warning: fdtgrep does not fully support version %d files\n",
797 fdt_version(blob));
798 }
799
800 /*
801 * We do two passes, since we don't know how many regions we need.
802 * The first pass will count the regions, but if it is too many,
803 * we do another pass to actually record them.
804 */
f403914d 805 for (i = 0; i < 3; i++) {
1043d0a0
SG
806 region = malloc(count * sizeof(struct fdt_region));
807 if (!region) {
808 fprintf(stderr, "Out of memory for %d regions\n",
809 count);
810 return -1;
811 }
812 max_regions = count;
813 count = fdtgrep_find_regions(blob,
814 h_include, disp,
815 region, max_regions, path, sizeof(path),
816 disp->flags);
817 if (count < 0) {
818 report_error("fdt_find_regions", count);
819 return -1;
820 }
821 if (count <= max_regions)
822 break;
823 free(region);
824 }
825
826 /* Optionally print a list of regions */
827 if (disp->region_list)
828 show_region_list(region, count);
829
830 /* Output either source .dts or binary .dtb */
831 if (disp->output == OUT_DTS) {
832 ret = display_fdt_by_regions(disp, blob, region, count);
833 } else {
834 void *fdt;
835 /* Allow reserved memory section to expand slightly */
836 int size = fdt_totalsize(blob) + 16;
837
838 fdt = malloc(size);
839 if (!fdt) {
840 fprintf(stderr, "Out_of_memory\n");
841 ret = -1;
842 goto err;
843 }
844 size = dump_fdt_regions(disp, blob, region, count, fdt);
845 if (disp->remove_strings) {
846 void *out;
847
848 out = malloc(size);
849 if (!out) {
850 fprintf(stderr, "Out_of_memory\n");
851 ret = -1;
852 goto err;
853 }
854 ret = fdt_remove_unused_strings(fdt, out);
855 if (ret < 0) {
856 fprintf(stderr,
857 "Failed to remove unused strings: err=%d\n",
858 ret);
859 goto err;
860 }
861 free(fdt);
862 fdt = out;
863 ret = fdt_pack(fdt);
864 if (ret < 0) {
865 fprintf(stderr, "Failed to pack: err=%d\n",
866 ret);
867 goto err;
868 }
869 size = fdt_totalsize(fdt);
870 }
871
872 if (size != fwrite(fdt, 1, size, disp->fout)) {
873 fprintf(stderr, "Write failure, %d bytes\n", size);
874 free(fdt);
875 ret = 1;
876 goto err;
877 }
878 free(fdt);
879 }
880err:
881 free(blob);
882 free(region);
883
884 return ret;
885}
886
887static const char usage_synopsis[] =
888 "fdtgrep - extract portions from device tree\n"
889 "\n"
890 "Usage:\n"
891 " fdtgrep <options> <dt file>|-\n\n"
892 "Output formats are:\n"
893 "\tdts - device tree soure text\n"
894 "\tdtb - device tree blob (sets -Hmt automatically)\n"
895 "\tbin - device tree fragment (may not be a valid .dtb)";
896
897/* Helper for usage_short_opts string constant */
898#define USAGE_COMMON_SHORT_OPTS "hV"
899
900/* Helper for aligning long_opts array */
901#define a_argument required_argument
902
903/* Helper for usage_long_opts option array */
904#define USAGE_COMMON_LONG_OPTS \
905 {"help", no_argument, NULL, 'h'}, \
906 {"version", no_argument, NULL, 'V'}, \
907 {NULL, no_argument, NULL, 0x0}
908
909/* Helper for usage_opts_help array */
910#define USAGE_COMMON_OPTS_HELP \
911 "Print this help and exit", \
912 "Print version and exit", \
913 NULL
914
915/* Helper for getopt case statements */
916#define case_USAGE_COMMON_FLAGS \
917 case 'h': usage(NULL); \
918 case 'V': util_version(); \
919 case '?': usage("unknown option");
920
921static const char usage_short_opts[] =
922 "haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTv"
923 USAGE_COMMON_SHORT_OPTS;
924static struct option const usage_long_opts[] = {
925 {"show-address", no_argument, NULL, 'a'},
926 {"colour", no_argument, NULL, 'A'},
927 {"include-node-with-prop", a_argument, NULL, 'b'},
928 {"include-compat", a_argument, NULL, 'c'},
929 {"exclude-compat", a_argument, NULL, 'C'},
930 {"diff", no_argument, NULL, 'd'},
931 {"enter-node", no_argument, NULL, 'e'},
932 {"show-offset", no_argument, NULL, 'f'},
933 {"include-match", a_argument, NULL, 'g'},
934 {"exclude-match", a_argument, NULL, 'G'},
935 {"show-header", no_argument, NULL, 'H'},
936 {"show-version", no_argument, NULL, 'I'},
937 {"list-regions", no_argument, NULL, 'l'},
938 {"list-strings", no_argument, NULL, 'L'},
939 {"include-mem", no_argument, NULL, 'm'},
940 {"include-node", a_argument, NULL, 'n'},
941 {"exclude-node", a_argument, NULL, 'N'},
942 {"include-prop", a_argument, NULL, 'p'},
943 {"exclude-prop", a_argument, NULL, 'P'},
944 {"remove-strings", no_argument, NULL, 'r'},
945 {"include-root", no_argument, NULL, 'R'},
946 {"show-subnodes", no_argument, NULL, 's'},
947 {"skip-supernodes", no_argument, NULL, 'S'},
948 {"show-stringtab", no_argument, NULL, 't'},
949 {"show-aliases", no_argument, NULL, 'T'},
950 {"out", a_argument, NULL, 'o'},
951 {"out-format", a_argument, NULL, 'O'},
952 {"invert-match", no_argument, NULL, 'v'},
953 USAGE_COMMON_LONG_OPTS,
954};
955static const char * const usage_opts_help[] = {
956 "Display address",
957 "Show all nodes/tags, colour those that match",
958 "Include contains containing property",
959 "Compatible nodes to include in grep",
960 "Compatible nodes to exclude in grep",
961 "Diff: Mark matching nodes with +, others with -",
962 "Enter direct subnode names of matching nodes",
963 "Display offset",
964 "Node/property/compatible string to include in grep",
965 "Node/property/compatible string to exclude in grep",
966 "Output a header",
967 "Put \"/dts-v1/;\" on first line of dts output",
968 "Output a region list",
969 "List strings in string table",
970 "Include mem_rsvmap section in binary output",
971 "Node to include in grep",
972 "Node to exclude in grep",
973 "Property to include in grep",
974 "Property to exclude in grep",
975 "Remove unused strings from string table",
976 "Include root node and all properties",
977 "Show all subnodes matching nodes",
978 "Don't include supernodes of matching nodes",
979 "Include string table in binary output",
980 "Include matching aliases in output",
981 "-o <output file>",
982 "-O <output format>",
983 "Invert the sense of matching (select non-matching lines)",
984 USAGE_COMMON_OPTS_HELP
985};
986
987/**
988 * Call getopt_long() with standard options
989 *
990 * Since all util code runs getopt in the same way, provide a helper.
991 */
992#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \
993 usage_long_opts, NULL)
994
995void util_usage(const char *errmsg, const char *synopsis,
996 const char *short_opts, struct option const long_opts[],
997 const char * const opts_help[])
998{
999 FILE *fp = errmsg ? stderr : stdout;
1000 const char a_arg[] = "<arg>";
1001 size_t a_arg_len = strlen(a_arg) + 1;
1002 size_t i;
1003 int optlen;
1004
1005 fprintf(fp,
1006 "Usage: %s\n"
1007 "\n"
1008 "Options: -[%s]\n", synopsis, short_opts);
1009
1010 /* prescan the --long opt length to auto-align */
1011 optlen = 0;
1012 for (i = 0; long_opts[i].name; ++i) {
1013 /* +1 is for space between --opt and help text */
1014 int l = strlen(long_opts[i].name) + 1;
1015 if (long_opts[i].has_arg == a_argument)
1016 l += a_arg_len;
1017 if (optlen < l)
1018 optlen = l;
1019 }
1020
1021 for (i = 0; long_opts[i].name; ++i) {
1022 /* helps when adding new applets or options */
1023 assert(opts_help[i] != NULL);
1024
1025 /* first output the short flag if it has one */
1026 if (long_opts[i].val > '~')
1027 fprintf(fp, " ");
1028 else
1029 fprintf(fp, " -%c, ", long_opts[i].val);
1030
1031 /* then the long flag */
1032 if (long_opts[i].has_arg == no_argument) {
1033 fprintf(fp, "--%-*s", optlen, long_opts[i].name);
1034 } else {
1035 fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg,
1036 (int)(optlen - strlen(long_opts[i].name) -
1037 a_arg_len), "");
1038 }
1039
1040 /* finally the help text */
1041 fprintf(fp, "%s\n", opts_help[i]);
1042 }
1043
1044 if (errmsg) {
1045 fprintf(fp, "\nError: %s\n", errmsg);
1046 exit(EXIT_FAILURE);
1047 } else {
1048 exit(EXIT_SUCCESS);
1049 }
1050}
1051
1052/**
1053 * Show usage and exit
1054 *
1055 * If you name all your usage variables with usage_xxx, then you can call this
1056 * help macro rather than expanding all arguments yourself.
1057 *
1058 * @param errmsg If non-NULL, an error message to display
1059 */
1060#define usage(errmsg) \
1061 util_usage(errmsg, usage_synopsis, usage_short_opts, \
1062 usage_long_opts, usage_opts_help)
1063
1064void util_version(void)
1065{
1066 printf("Version: %s\n", "(U-Boot)");
1067 exit(0);
1068}
1069
1070static void scan_args(struct display_info *disp, int argc, char *argv[])
1071{
1072 int opt;
1073
1074 while ((opt = util_getopt_long()) != EOF) {
1075 int type = 0;
1076 int inc = 1;
1077
1078 switch (opt) {
1079 case_USAGE_COMMON_FLAGS
1080 case 'a':
1081 disp->show_addr = 1;
1082 break;
1083 case 'A':
1084 disp->all = 1;
1085 break;
1086 case 'b':
1087 type = FDT_NODE_HAS_PROP;
1088 break;
1089 case 'C':
1090 inc = 0;
1091 /* no break */
1092 case 'c':
1093 type = FDT_IS_COMPAT;
1094 break;
1095 case 'd':
1096 disp->diff = 1;
1097 break;
1098 case 'e':
1099 disp->flags |= FDT_REG_DIRECT_SUBNODES;
1100 break;
1101 case 'f':
1102 disp->show_offset = 1;
1103 break;
1104 case 'G':
1105 inc = 0;
1106 /* no break */
1107 case 'g':
1108 type = FDT_ANY_GLOBAL;
1109 break;
1110 case 'H':
1111 disp->header = 1;
1112 break;
1113 case 'l':
1114 disp->region_list = 1;
1115 break;
1116 case 'L':
1117 disp->list_strings = 1;
1118 break;
1119 case 'm':
1120 disp->flags |= FDT_REG_ADD_MEM_RSVMAP;
1121 break;
1122 case 'N':
1123 inc = 0;
1124 /* no break */
1125 case 'n':
1126 type = FDT_IS_NODE;
1127 break;
1128 case 'o':
1129 disp->output_fname = optarg;
1130 break;
1131 case 'O':
1132 if (!strcmp(optarg, "dtb"))
1133 disp->output = OUT_DTB;
1134 else if (!strcmp(optarg, "dts"))
1135 disp->output = OUT_DTS;
1136 else if (!strcmp(optarg, "bin"))
1137 disp->output = OUT_BIN;
1138 else
1139 usage("Unknown output format");
1140 break;
1141 case 'P':
1142 inc = 0;
1143 /* no break */
1144 case 'p':
1145 type = FDT_IS_PROP;
1146 break;
1147 case 'r':
1148 disp->remove_strings = 1;
1149 break;
1150 case 'R':
1151 disp->include_root = 1;
1152 break;
1153 case 's':
1154 disp->flags |= FDT_REG_ALL_SUBNODES;
1155 break;
1156 case 'S':
1157 disp->flags &= ~FDT_REG_SUPERNODES;
1158 break;
1159 case 't':
1160 disp->flags |= FDT_REG_ADD_STRING_TAB;
1161 break;
1162 case 'T':
1163 disp->add_aliases = 1;
1164 break;
1165 case 'v':
1166 disp->invert = 1;
1167 break;
1168 case 'I':
1169 disp->show_dts_version = 1;
1170 break;
1171 }
1172
1173 if (type && value_add(disp, &disp->value_head, type, inc,
1174 optarg))
1175 usage("Cannot add value");
1176 }
1177
1178 if (disp->invert && disp->types_exc)
1179 usage("-v has no meaning when used with 'exclude' conditions");
1180}
1181
1182int main(int argc, char *argv[])
1183{
1184 char *filename = NULL;
1185 struct display_info disp;
1186 int ret;
1187
1188 /* set defaults */
1189 memset(&disp, '\0', sizeof(disp));
1190 disp.flags = FDT_REG_SUPERNODES; /* Default flags */
1191
1192 scan_args(&disp, argc, argv);
1193
1194 /* Show matched lines in colour if we can */
1195 disp.colour = disp.all && isatty(0);
1196
1197 /* Any additional arguments can match anything, just like -g */
1198 while (optind < argc - 1) {
1199 if (value_add(&disp, &disp.value_head, FDT_IS_ANY, 1,
1200 argv[optind++]))
1201 usage("Cannot add value");
1202 }
1203
1204 if (optind < argc)
1205 filename = argv[optind++];
1206 if (!filename)
1207 usage("Missing filename");
1208
1209 /* If a valid .dtb is required, set flags to ensure we get one */
1210 if (disp.output == OUT_DTB) {
1211 disp.header = 1;
1212 disp.flags |= FDT_REG_ADD_MEM_RSVMAP | FDT_REG_ADD_STRING_TAB;
1213 }
1214
1215 if (disp.output_fname) {
1216 disp.fout = fopen(disp.output_fname, "w");
1217 if (!disp.fout)
1218 usage("Cannot open output file");
1219 } else {
1220 disp.fout = stdout;
1221 }
1222
1223 /* Run the grep and output the results */
1224 ret = do_fdtgrep(&disp, filename);
1225 if (disp.output_fname)
1226 fclose(disp.fout);
1227 if (ret)
1228 return 1;
1229
1230 return 0;
1231}