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> 13 #include <errno.h> 14 #include <getopt.h> 15 #include <fcntl.h> 16 #include <stdbool.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 22 #include "fdt_host.h" 23 #include "libfdt_internal.h" 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 */ 33 struct 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 */ 41 enum 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 */ 48 struct 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 71 static 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 */ 77 enum { 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 */ 96 static 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 */ 114 static 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 151 static 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 181 static 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 */ 230 static 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 * 411 * FDT_REG_SUPERNODES: ensures that subnodes are preceded by their 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 */ 426 static 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 = ®ion[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 */ 499 static 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 510 static 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 */ 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; 542 } 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 */ 586 static 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 641 static 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 648 static 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 ®ion[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 ? ®ion[count] : NULL, 665 path, path_len, disp->flags, &state); 666 if (!ret) 667 count++; 668 } 669 if (ret && ret != -FDT_ERR_NOTFOUND) 670 return ret; 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); 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) { 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); 691 } 692 } 693 694 return count; 695 } 696 697 int 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 742 int utilfdt_read_err(const char *filename, char **buffp) 743 { 744 off_t len; 745 return utilfdt_read_err_len(filename, buffp, &len); 746 } 747 748 char *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 762 char *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 */ 775 static 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 */ 805 for (i = 0; i < 3; i++) { 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 } 880 err: 881 free(blob); 882 free(region); 883 884 return ret; 885 } 886 887 static 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 921 static const char usage_short_opts[] = 922 "haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTv" 923 USAGE_COMMON_SHORT_OPTS; 924 static 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 }; 955 static 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 995 void 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 1064 void util_version(void) 1065 { 1066 printf("Version: %s\n", "(U-Boot)"); 1067 exit(0); 1068 } 1069 1070 static 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 1182 int 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 } 1232