1 /* 2 * (C) Copyright 2007 3 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com 4 * Based on code written by: 5 * Pantelis Antoniou <pantelis.antoniou@gmail.com> and 6 * Matthew McClintock <msm@freescale.com> 7 * 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 11 #include <common.h> 12 #include <command.h> 13 #include <linux/ctype.h> 14 #include <linux/types.h> 15 #include <asm/global_data.h> 16 #include <libfdt.h> 17 #include <fdt_support.h> 18 #include <mapmem.h> 19 #include <asm/io.h> 20 21 #define MAX_LEVEL 32 /* how deeply nested we will go */ 22 #define SCRATCHPAD 1024 /* bytes of scratchpad memory */ 23 #define CMD_FDT_MAX_DUMP 64 24 25 /* 26 * Global data (for the gd->bd) 27 */ 28 DECLARE_GLOBAL_DATA_PTR; 29 30 static int fdt_valid(struct fdt_header **blobp); 31 static int fdt_parse_prop(char *const*newval, int count, char *data, int *len); 32 static int fdt_print(const char *pathp, char *prop, int depth); 33 static int is_printable_string(const void *data, int len); 34 35 /* 36 * The working_fdt points to our working flattened device tree. 37 */ 38 struct fdt_header *working_fdt; 39 40 void set_working_fdt_addr(ulong addr) 41 { 42 void *buf; 43 44 buf = map_sysmem(addr, 0); 45 working_fdt = buf; 46 env_set_hex("fdtaddr", addr); 47 } 48 49 /* 50 * Get a value from the fdt and format it to be set in the environment 51 */ 52 static int fdt_value_env_set(const void *nodep, int len, const char *var) 53 { 54 if (is_printable_string(nodep, len)) 55 env_set(var, (void *)nodep); 56 else if (len == 4) { 57 char buf[11]; 58 59 sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep)); 60 env_set(var, buf); 61 } else if (len%4 == 0 && len <= 20) { 62 /* Needed to print things like sha1 hashes. */ 63 char buf[41]; 64 int i; 65 66 for (i = 0; i < len; i += sizeof(unsigned int)) 67 sprintf(buf + (i * 2), "%08x", 68 *(unsigned int *)(nodep + i)); 69 env_set(var, buf); 70 } else { 71 printf("error: unprintable value\n"); 72 return 1; 73 } 74 return 0; 75 } 76 77 /* 78 * Flattened Device Tree command, see the help for parameter definitions. 79 */ 80 static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 81 { 82 if (argc < 2) 83 return CMD_RET_USAGE; 84 85 /* 86 * Set the address of the fdt 87 */ 88 if (strncmp(argv[1], "ad", 2) == 0) { 89 unsigned long addr; 90 int control = 0; 91 struct fdt_header *blob; 92 /* 93 * Set the address [and length] of the fdt. 94 */ 95 argc -= 2; 96 argv += 2; 97 /* Temporary #ifdef - some archs don't have fdt_blob yet */ 98 #ifdef CONFIG_OF_CONTROL 99 if (argc && !strcmp(*argv, "-c")) { 100 control = 1; 101 argc--; 102 argv++; 103 } 104 #endif 105 if (argc == 0) { 106 if (control) 107 blob = (struct fdt_header *)gd->fdt_blob; 108 else 109 blob = working_fdt; 110 if (!blob || !fdt_valid(&blob)) 111 return 1; 112 printf("The address of the fdt is %#08lx\n", 113 control ? (ulong)map_to_sysmem(blob) : 114 env_get_hex("fdtaddr", 0)); 115 return 0; 116 } 117 118 addr = simple_strtoul(argv[0], NULL, 16); 119 blob = map_sysmem(addr, 0); 120 if (!fdt_valid(&blob)) 121 return 1; 122 if (control) 123 gd->fdt_blob = blob; 124 else 125 set_working_fdt_addr(addr); 126 127 if (argc >= 2) { 128 int len; 129 int err; 130 /* 131 * Optional new length 132 */ 133 len = simple_strtoul(argv[1], NULL, 16); 134 if (len < fdt_totalsize(blob)) { 135 printf ("New length %d < existing length %d, " 136 "ignoring.\n", 137 len, fdt_totalsize(blob)); 138 } else { 139 /* 140 * Open in place with a new length. 141 */ 142 err = fdt_open_into(blob, blob, len); 143 if (err != 0) { 144 printf ("libfdt fdt_open_into(): %s\n", 145 fdt_strerror(err)); 146 } 147 } 148 } 149 150 return CMD_RET_SUCCESS; 151 } 152 153 if (!working_fdt) { 154 puts( 155 "No FDT memory address configured. Please configure\n" 156 "the FDT address via \"fdt addr <address>\" command.\n" 157 "Aborting!\n"); 158 return CMD_RET_FAILURE; 159 } 160 161 /* 162 * Move the working_fdt 163 */ 164 if (strncmp(argv[1], "mo", 2) == 0) { 165 struct fdt_header *newaddr; 166 int len; 167 int err; 168 169 if (argc < 4) 170 return CMD_RET_USAGE; 171 172 /* 173 * Set the address and length of the fdt. 174 */ 175 working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); 176 if (!fdt_valid(&working_fdt)) 177 return 1; 178 179 newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); 180 181 /* 182 * If the user specifies a length, use that. Otherwise use the 183 * current length. 184 */ 185 if (argc <= 4) { 186 len = fdt_totalsize(working_fdt); 187 } else { 188 len = simple_strtoul(argv[4], NULL, 16); 189 if (len < fdt_totalsize(working_fdt)) { 190 printf ("New length 0x%X < existing length " 191 "0x%X, aborting.\n", 192 len, fdt_totalsize(working_fdt)); 193 return 1; 194 } 195 } 196 197 /* 198 * Copy to the new location. 199 */ 200 err = fdt_open_into(working_fdt, newaddr, len); 201 if (err != 0) { 202 printf ("libfdt fdt_open_into(): %s\n", 203 fdt_strerror(err)); 204 return 1; 205 } 206 working_fdt = newaddr; 207 #ifdef CONFIG_OF_SYSTEM_SETUP 208 /* Call the board-specific fixup routine */ 209 } else if (strncmp(argv[1], "sys", 3) == 0) { 210 int err = ft_system_setup(working_fdt, gd->bd); 211 212 if (err) { 213 printf("Failed to add system information to FDT: %s\n", 214 fdt_strerror(err)); 215 return CMD_RET_FAILURE; 216 } 217 #endif 218 /* 219 * Make a new node 220 */ 221 } else if (strncmp(argv[1], "mk", 2) == 0) { 222 char *pathp; /* path */ 223 char *nodep; /* new node to add */ 224 int nodeoffset; /* node offset from libfdt */ 225 int err; 226 227 /* 228 * Parameters: Node path, new node to be appended to the path. 229 */ 230 if (argc < 4) 231 return CMD_RET_USAGE; 232 233 pathp = argv[2]; 234 nodep = argv[3]; 235 236 nodeoffset = fdt_path_offset (working_fdt, pathp); 237 if (nodeoffset < 0) { 238 /* 239 * Not found or something else bad happened. 240 */ 241 printf ("libfdt fdt_path_offset() returned %s\n", 242 fdt_strerror(nodeoffset)); 243 return 1; 244 } 245 err = fdt_add_subnode(working_fdt, nodeoffset, nodep); 246 if (err < 0) { 247 printf ("libfdt fdt_add_subnode(): %s\n", 248 fdt_strerror(err)); 249 return 1; 250 } 251 252 /* 253 * Set the value of a property in the working_fdt. 254 */ 255 } else if (argv[1][0] == 's') { 256 char *pathp; /* path */ 257 char *prop; /* property */ 258 int nodeoffset; /* node offset from libfdt */ 259 static char data[SCRATCHPAD]; /* storage for the property */ 260 const void *ptmp; 261 int len; /* new length of the property */ 262 int ret; /* return value */ 263 264 /* 265 * Parameters: Node path, property, optional value. 266 */ 267 if (argc < 4) 268 return CMD_RET_USAGE; 269 270 pathp = argv[2]; 271 prop = argv[3]; 272 273 nodeoffset = fdt_path_offset (working_fdt, pathp); 274 if (nodeoffset < 0) { 275 /* 276 * Not found or something else bad happened. 277 */ 278 printf ("libfdt fdt_path_offset() returned %s\n", 279 fdt_strerror(nodeoffset)); 280 return 1; 281 } 282 283 if (argc == 4) { 284 len = 0; 285 } else { 286 ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len); 287 if (!ptmp) { 288 printf("prop (%s) not found!\n", prop); 289 return 1; 290 } 291 if (len > SCRATCHPAD) { 292 printf("prop (%d) doesn't fit in scratchpad!\n", 293 len); 294 return 1; 295 } 296 memcpy(data, ptmp, len); 297 ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); 298 if (ret != 0) 299 return ret; 300 } 301 302 ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len); 303 if (ret < 0) { 304 printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); 305 return 1; 306 } 307 308 /******************************************************************** 309 * Get the value of a property in the working_fdt. 310 ********************************************************************/ 311 } else if (argv[1][0] == 'g') { 312 char *subcmd; /* sub-command */ 313 char *pathp; /* path */ 314 char *prop; /* property */ 315 char *var; /* variable to store result */ 316 int nodeoffset; /* node offset from libfdt */ 317 const void *nodep; /* property node pointer */ 318 int len = 0; /* new length of the property */ 319 320 /* 321 * Parameters: Node path, property, optional value. 322 */ 323 if (argc < 5) 324 return CMD_RET_USAGE; 325 326 subcmd = argv[2]; 327 328 if (argc < 6 && subcmd[0] != 's') 329 return CMD_RET_USAGE; 330 331 var = argv[3]; 332 pathp = argv[4]; 333 prop = argv[5]; 334 335 nodeoffset = fdt_path_offset(working_fdt, pathp); 336 if (nodeoffset < 0) { 337 /* 338 * Not found or something else bad happened. 339 */ 340 printf("libfdt fdt_path_offset() returned %s\n", 341 fdt_strerror(nodeoffset)); 342 return 1; 343 } 344 345 if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) { 346 int reqIndex = -1; 347 int startDepth = fdt_node_depth( 348 working_fdt, nodeoffset); 349 int curDepth = startDepth; 350 int curIndex = -1; 351 int nextNodeOffset = fdt_next_node( 352 working_fdt, nodeoffset, &curDepth); 353 354 if (subcmd[0] == 'n') 355 reqIndex = simple_strtoul(argv[5], NULL, 16); 356 357 while (curDepth > startDepth) { 358 if (curDepth == startDepth + 1) 359 curIndex++; 360 if (subcmd[0] == 'n' && curIndex == reqIndex) { 361 const char *node_name; 362 363 node_name = fdt_get_name(working_fdt, 364 nextNodeOffset, 365 NULL); 366 env_set(var, node_name); 367 return 0; 368 } 369 nextNodeOffset = fdt_next_node( 370 working_fdt, nextNodeOffset, &curDepth); 371 if (nextNodeOffset < 0) 372 break; 373 } 374 if (subcmd[0] == 's') { 375 /* get the num nodes at this level */ 376 env_set_ulong(var, curIndex + 1); 377 } else { 378 /* node index not found */ 379 printf("libfdt node not found\n"); 380 return 1; 381 } 382 } else { 383 nodep = fdt_getprop( 384 working_fdt, nodeoffset, prop, &len); 385 if (len == 0) { 386 /* no property value */ 387 env_set(var, ""); 388 return 0; 389 } else if (nodep && len > 0) { 390 if (subcmd[0] == 'v') { 391 int ret; 392 393 ret = fdt_value_env_set(nodep, len, 394 var); 395 if (ret != 0) 396 return ret; 397 } else if (subcmd[0] == 'a') { 398 /* Get address */ 399 char buf[11]; 400 401 sprintf(buf, "0x%p", nodep); 402 env_set(var, buf); 403 } else if (subcmd[0] == 's') { 404 /* Get size */ 405 char buf[11]; 406 407 sprintf(buf, "0x%08X", len); 408 env_set(var, buf); 409 } else 410 return CMD_RET_USAGE; 411 return 0; 412 } else { 413 printf("libfdt fdt_getprop(): %s\n", 414 fdt_strerror(len)); 415 return 1; 416 } 417 } 418 419 /* 420 * Print (recursive) / List (single level) 421 */ 422 } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { 423 int depth = MAX_LEVEL; /* how deep to print */ 424 char *pathp; /* path */ 425 char *prop; /* property */ 426 int ret; /* return value */ 427 static char root[2] = "/"; 428 429 /* 430 * list is an alias for print, but limited to 1 level 431 */ 432 if (argv[1][0] == 'l') { 433 depth = 1; 434 } 435 436 /* 437 * Get the starting path. The root node is an oddball, 438 * the offset is zero and has no name. 439 */ 440 if (argc == 2) 441 pathp = root; 442 else 443 pathp = argv[2]; 444 if (argc > 3) 445 prop = argv[3]; 446 else 447 prop = NULL; 448 449 ret = fdt_print(pathp, prop, depth); 450 if (ret != 0) 451 return ret; 452 453 /* 454 * Remove a property/node 455 */ 456 } else if (strncmp(argv[1], "rm", 2) == 0) { 457 int nodeoffset; /* node offset from libfdt */ 458 int err; 459 460 /* 461 * Get the path. The root node is an oddball, the offset 462 * is zero and has no name. 463 */ 464 nodeoffset = fdt_path_offset (working_fdt, argv[2]); 465 if (nodeoffset < 0) { 466 /* 467 * Not found or something else bad happened. 468 */ 469 printf ("libfdt fdt_path_offset() returned %s\n", 470 fdt_strerror(nodeoffset)); 471 return 1; 472 } 473 /* 474 * Do the delete. A fourth parameter means delete a property, 475 * otherwise delete the node. 476 */ 477 if (argc > 3) { 478 err = fdt_delprop(working_fdt, nodeoffset, argv[3]); 479 if (err < 0) { 480 printf("libfdt fdt_delprop(): %s\n", 481 fdt_strerror(err)); 482 return err; 483 } 484 } else { 485 err = fdt_del_node(working_fdt, nodeoffset); 486 if (err < 0) { 487 printf("libfdt fdt_del_node(): %s\n", 488 fdt_strerror(err)); 489 return err; 490 } 491 } 492 493 /* 494 * Display header info 495 */ 496 } else if (argv[1][0] == 'h') { 497 u32 version = fdt_version(working_fdt); 498 printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt)); 499 printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt), 500 fdt_totalsize(working_fdt)); 501 printf("off_dt_struct:\t\t0x%x\n", 502 fdt_off_dt_struct(working_fdt)); 503 printf("off_dt_strings:\t\t0x%x\n", 504 fdt_off_dt_strings(working_fdt)); 505 printf("off_mem_rsvmap:\t\t0x%x\n", 506 fdt_off_mem_rsvmap(working_fdt)); 507 printf("version:\t\t%d\n", version); 508 printf("last_comp_version:\t%d\n", 509 fdt_last_comp_version(working_fdt)); 510 if (version >= 2) 511 printf("boot_cpuid_phys:\t0x%x\n", 512 fdt_boot_cpuid_phys(working_fdt)); 513 if (version >= 3) 514 printf("size_dt_strings:\t0x%x\n", 515 fdt_size_dt_strings(working_fdt)); 516 if (version >= 17) 517 printf("size_dt_struct:\t\t0x%x\n", 518 fdt_size_dt_struct(working_fdt)); 519 printf("number mem_rsv:\t\t0x%x\n", 520 fdt_num_mem_rsv(working_fdt)); 521 printf("\n"); 522 523 /* 524 * Set boot cpu id 525 */ 526 } else if (strncmp(argv[1], "boo", 3) == 0) { 527 unsigned long tmp = simple_strtoul(argv[2], NULL, 16); 528 fdt_set_boot_cpuid_phys(working_fdt, tmp); 529 530 /* 531 * memory command 532 */ 533 } else if (strncmp(argv[1], "me", 2) == 0) { 534 uint64_t addr, size; 535 int err; 536 addr = simple_strtoull(argv[2], NULL, 16); 537 size = simple_strtoull(argv[3], NULL, 16); 538 err = fdt_fixup_memory(working_fdt, addr, size); 539 if (err < 0) 540 return err; 541 542 /* 543 * mem reserve commands 544 */ 545 } else if (strncmp(argv[1], "rs", 2) == 0) { 546 if (argv[2][0] == 'p') { 547 uint64_t addr, size; 548 int total = fdt_num_mem_rsv(working_fdt); 549 int j, err; 550 printf("index\t\t start\t\t size\n"); 551 printf("-------------------------------" 552 "-----------------\n"); 553 for (j = 0; j < total; j++) { 554 err = fdt_get_mem_rsv(working_fdt, j, &addr, &size); 555 if (err < 0) { 556 printf("libfdt fdt_get_mem_rsv(): %s\n", 557 fdt_strerror(err)); 558 return err; 559 } 560 printf(" %x\t%08x%08x\t%08x%08x\n", j, 561 (u32)(addr >> 32), 562 (u32)(addr & 0xffffffff), 563 (u32)(size >> 32), 564 (u32)(size & 0xffffffff)); 565 } 566 } else if (argv[2][0] == 'a') { 567 uint64_t addr, size; 568 int err; 569 addr = simple_strtoull(argv[3], NULL, 16); 570 size = simple_strtoull(argv[4], NULL, 16); 571 err = fdt_add_mem_rsv(working_fdt, addr, size); 572 573 if (err < 0) { 574 printf("libfdt fdt_add_mem_rsv(): %s\n", 575 fdt_strerror(err)); 576 return err; 577 } 578 } else if (argv[2][0] == 'd') { 579 unsigned long idx = simple_strtoul(argv[3], NULL, 16); 580 int err = fdt_del_mem_rsv(working_fdt, idx); 581 582 if (err < 0) { 583 printf("libfdt fdt_del_mem_rsv(): %s\n", 584 fdt_strerror(err)); 585 return err; 586 } 587 } else { 588 /* Unrecognized command */ 589 return CMD_RET_USAGE; 590 } 591 } 592 #ifdef CONFIG_OF_BOARD_SETUP 593 /* Call the board-specific fixup routine */ 594 else if (strncmp(argv[1], "boa", 3) == 0) { 595 int err = ft_board_setup(working_fdt, gd->bd); 596 597 if (err) { 598 printf("Failed to update board information in FDT: %s\n", 599 fdt_strerror(err)); 600 return CMD_RET_FAILURE; 601 } 602 } 603 #endif 604 /* Create a chosen node */ 605 else if (strncmp(argv[1], "cho", 3) == 0) { 606 unsigned long initrd_start = 0, initrd_end = 0; 607 608 if ((argc != 2) && (argc != 4)) 609 return CMD_RET_USAGE; 610 611 if (argc == 4) { 612 initrd_start = simple_strtoul(argv[2], NULL, 16); 613 initrd_end = simple_strtoul(argv[3], NULL, 16); 614 } 615 616 fdt_chosen(working_fdt); 617 fdt_initrd(working_fdt, initrd_start, initrd_end); 618 619 #if defined(CONFIG_FIT_SIGNATURE) 620 } else if (strncmp(argv[1], "che", 3) == 0) { 621 int cfg_noffset; 622 int ret; 623 unsigned long addr; 624 struct fdt_header *blob; 625 626 if (!working_fdt) 627 return CMD_RET_FAILURE; 628 629 if (argc > 2) { 630 addr = simple_strtoul(argv[2], NULL, 16); 631 blob = map_sysmem(addr, 0); 632 } else { 633 blob = (struct fdt_header *)gd->fdt_blob; 634 } 635 if (!fdt_valid(&blob)) 636 return 1; 637 638 gd->fdt_blob = blob; 639 cfg_noffset = fit_conf_get_node(working_fdt, NULL); 640 if (!cfg_noffset) { 641 printf("Could not find configuration node: %s\n", 642 fdt_strerror(cfg_noffset)); 643 return CMD_RET_FAILURE; 644 } 645 646 ret = fit_config_verify(working_fdt, cfg_noffset); 647 if (ret == 0) 648 return CMD_RET_SUCCESS; 649 else 650 return CMD_RET_FAILURE; 651 #endif 652 653 } 654 #ifdef CONFIG_OF_LIBFDT_OVERLAY 655 /* apply an overlay */ 656 else if (strncmp(argv[1], "ap", 2) == 0) { 657 unsigned long addr; 658 struct fdt_header *blob; 659 int ret; 660 661 if (argc != 3) 662 return CMD_RET_USAGE; 663 664 if (!working_fdt) 665 return CMD_RET_FAILURE; 666 667 addr = simple_strtoul(argv[2], NULL, 16); 668 blob = map_sysmem(addr, 0); 669 if (!fdt_valid(&blob)) 670 return CMD_RET_FAILURE; 671 672 ret = fdt_overlay_apply(working_fdt, blob); 673 if (ret) { 674 printf("fdt_overlay_apply(): %s\n", fdt_strerror(ret)); 675 return CMD_RET_FAILURE; 676 } 677 } 678 #endif 679 /* resize the fdt */ 680 else if (strncmp(argv[1], "re", 2) == 0) { 681 uint extrasize; 682 if (argc > 2) 683 extrasize = simple_strtoul(argv[2], NULL, 16); 684 else 685 extrasize = 0; 686 fdt_shrink_to_minimum(working_fdt, extrasize); 687 } 688 else { 689 /* Unrecognized command */ 690 return CMD_RET_USAGE; 691 } 692 693 return 0; 694 } 695 696 /****************************************************************************/ 697 698 /** 699 * fdt_valid() - Check if an FDT is valid. If not, change it to NULL 700 * 701 * @blobp: Pointer to FDT pointer 702 * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL) 703 */ 704 static int fdt_valid(struct fdt_header **blobp) 705 { 706 const void *blob = *blobp; 707 int err; 708 709 if (blob == NULL) { 710 printf ("The address of the fdt is invalid (NULL).\n"); 711 return 0; 712 } 713 714 err = fdt_check_header(blob); 715 if (err == 0) 716 return 1; /* valid */ 717 718 if (err < 0) { 719 printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); 720 /* 721 * Be more informative on bad version. 722 */ 723 if (err == -FDT_ERR_BADVERSION) { 724 if (fdt_version(blob) < 725 FDT_FIRST_SUPPORTED_VERSION) { 726 printf (" - too old, fdt %d < %d", 727 fdt_version(blob), 728 FDT_FIRST_SUPPORTED_VERSION); 729 } 730 if (fdt_last_comp_version(blob) > 731 FDT_LAST_SUPPORTED_VERSION) { 732 printf (" - too new, fdt %d > %d", 733 fdt_version(blob), 734 FDT_LAST_SUPPORTED_VERSION); 735 } 736 } 737 printf("\n"); 738 *blobp = NULL; 739 return 0; 740 } 741 return 1; 742 } 743 744 /****************************************************************************/ 745 746 /* 747 * Parse the user's input, partially heuristic. Valid formats: 748 * <0x00112233 4 05> - an array of cells. Numbers follow standard 749 * C conventions. 750 * [00 11 22 .. nn] - byte stream 751 * "string" - If the the value doesn't start with "<" or "[", it is 752 * treated as a string. Note that the quotes are 753 * stripped by the parser before we get the string. 754 * newval: An array of strings containing the new property as specified 755 * on the command line 756 * count: The number of strings in the array 757 * data: A bytestream to be placed in the property 758 * len: The length of the resulting bytestream 759 */ 760 static int fdt_parse_prop(char * const *newval, int count, char *data, int *len) 761 { 762 char *cp; /* temporary char pointer */ 763 char *newp; /* temporary newval char pointer */ 764 unsigned long tmp; /* holds converted values */ 765 int stridx = 0; 766 767 *len = 0; 768 newp = newval[0]; 769 770 /* An array of cells */ 771 if (*newp == '<') { 772 newp++; 773 while ((*newp != '>') && (stridx < count)) { 774 /* 775 * Keep searching until we find that last ">" 776 * That way users don't have to escape the spaces 777 */ 778 if (*newp == '\0') { 779 newp = newval[++stridx]; 780 continue; 781 } 782 783 cp = newp; 784 tmp = simple_strtoul(cp, &newp, 0); 785 if (*cp != '?') 786 *(fdt32_t *)data = cpu_to_fdt32(tmp); 787 else 788 newp++; 789 790 data += 4; 791 *len += 4; 792 793 /* If the ptr didn't advance, something went wrong */ 794 if ((newp - cp) <= 0) { 795 printf("Sorry, I could not convert \"%s\"\n", 796 cp); 797 return 1; 798 } 799 800 while (*newp == ' ') 801 newp++; 802 } 803 804 if (*newp != '>') { 805 printf("Unexpected character '%c'\n", *newp); 806 return 1; 807 } 808 } else if (*newp == '[') { 809 /* 810 * Byte stream. Convert the values. 811 */ 812 newp++; 813 while ((stridx < count) && (*newp != ']')) { 814 while (*newp == ' ') 815 newp++; 816 if (*newp == '\0') { 817 newp = newval[++stridx]; 818 continue; 819 } 820 if (!isxdigit(*newp)) 821 break; 822 tmp = simple_strtoul(newp, &newp, 16); 823 *data++ = tmp & 0xFF; 824 *len = *len + 1; 825 } 826 if (*newp != ']') { 827 printf("Unexpected character '%c'\n", *newp); 828 return 1; 829 } 830 } else { 831 /* 832 * Assume it is one or more strings. Copy it into our 833 * data area for convenience (including the 834 * terminating '\0's). 835 */ 836 while (stridx < count) { 837 size_t length = strlen(newp) + 1; 838 strcpy(data, newp); 839 data += length; 840 *len += length; 841 newp = newval[++stridx]; 842 } 843 } 844 return 0; 845 } 846 847 /****************************************************************************/ 848 849 /* 850 * Heuristic to guess if this is a string or concatenated strings. 851 */ 852 853 static int is_printable_string(const void *data, int len) 854 { 855 const char *s = data; 856 857 /* zero length is not */ 858 if (len == 0) 859 return 0; 860 861 /* must terminate with zero or '\n' */ 862 if (s[len - 1] != '\0' && s[len - 1] != '\n') 863 return 0; 864 865 /* printable or a null byte (concatenated strings) */ 866 while (((*s == '\0') || isprint(*s) || isspace(*s)) && (len > 0)) { 867 /* 868 * If we see a null, there are three possibilities: 869 * 1) If len == 1, it is the end of the string, printable 870 * 2) Next character also a null, not printable. 871 * 3) Next character not a null, continue to check. 872 */ 873 if (s[0] == '\0') { 874 if (len == 1) 875 return 1; 876 if (s[1] == '\0') 877 return 0; 878 } 879 s++; 880 len--; 881 } 882 883 /* Not the null termination, or not done yet: not printable */ 884 if (*s != '\0' || (len != 0)) 885 return 0; 886 887 return 1; 888 } 889 890 891 /* 892 * Print the property in the best format, a heuristic guess. Print as 893 * a string, concatenated strings, a byte, word, double word, or (if all 894 * else fails) it is printed as a stream of bytes. 895 */ 896 static void print_data(const void *data, int len) 897 { 898 int j; 899 900 /* no data, don't print */ 901 if (len == 0) 902 return; 903 904 /* 905 * It is a string, but it may have multiple strings (embedded '\0's). 906 */ 907 if (is_printable_string(data, len)) { 908 puts("\""); 909 j = 0; 910 while (j < len) { 911 if (j > 0) 912 puts("\", \""); 913 puts(data); 914 j += strlen(data) + 1; 915 data += strlen(data) + 1; 916 } 917 puts("\""); 918 return; 919 } 920 921 if ((len %4) == 0) { 922 if (len > CMD_FDT_MAX_DUMP) 923 printf("* 0x%p [0x%08x]", data, len); 924 else { 925 const __be32 *p; 926 927 printf("<"); 928 for (j = 0, p = data; j < len/4; j++) 929 printf("0x%08x%s", fdt32_to_cpu(p[j]), 930 j < (len/4 - 1) ? " " : ""); 931 printf(">"); 932 } 933 } else { /* anything else... hexdump */ 934 if (len > CMD_FDT_MAX_DUMP) 935 printf("* 0x%p [0x%08x]", data, len); 936 else { 937 const u8 *s; 938 939 printf("["); 940 for (j = 0, s = data; j < len; j++) 941 printf("%02x%s", s[j], j < len - 1 ? " " : ""); 942 printf("]"); 943 } 944 } 945 } 946 947 /****************************************************************************/ 948 949 /* 950 * Recursively print (a portion of) the working_fdt. The depth parameter 951 * determines how deeply nested the fdt is printed. 952 */ 953 static int fdt_print(const char *pathp, char *prop, int depth) 954 { 955 static char tabs[MAX_LEVEL+1] = 956 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" 957 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; 958 const void *nodep; /* property node pointer */ 959 int nodeoffset; /* node offset from libfdt */ 960 int nextoffset; /* next node offset from libfdt */ 961 uint32_t tag; /* tag */ 962 int len; /* length of the property */ 963 int level = 0; /* keep track of nesting level */ 964 const struct fdt_property *fdt_prop; 965 966 nodeoffset = fdt_path_offset (working_fdt, pathp); 967 if (nodeoffset < 0) { 968 /* 969 * Not found or something else bad happened. 970 */ 971 printf ("libfdt fdt_path_offset() returned %s\n", 972 fdt_strerror(nodeoffset)); 973 return 1; 974 } 975 /* 976 * The user passed in a property as well as node path. 977 * Print only the given property and then return. 978 */ 979 if (prop) { 980 nodep = fdt_getprop (working_fdt, nodeoffset, prop, &len); 981 if (len == 0) { 982 /* no property value */ 983 printf("%s %s\n", pathp, prop); 984 return 0; 985 } else if (nodep && len > 0) { 986 printf("%s = ", prop); 987 print_data (nodep, len); 988 printf("\n"); 989 return 0; 990 } else { 991 printf ("libfdt fdt_getprop(): %s\n", 992 fdt_strerror(len)); 993 return 1; 994 } 995 } 996 997 /* 998 * The user passed in a node path and no property, 999 * print the node and all subnodes. 1000 */ 1001 while(level >= 0) { 1002 tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset); 1003 switch(tag) { 1004 case FDT_BEGIN_NODE: 1005 pathp = fdt_get_name(working_fdt, nodeoffset, NULL); 1006 if (level <= depth) { 1007 if (pathp == NULL) 1008 pathp = "/* NULL pointer error */"; 1009 if (*pathp == '\0') 1010 pathp = "/"; /* root is nameless */ 1011 printf("%s%s {\n", 1012 &tabs[MAX_LEVEL - level], pathp); 1013 } 1014 level++; 1015 if (level >= MAX_LEVEL) { 1016 printf("Nested too deep, aborting.\n"); 1017 return 1; 1018 } 1019 break; 1020 case FDT_END_NODE: 1021 level--; 1022 if (level <= depth) 1023 printf("%s};\n", &tabs[MAX_LEVEL - level]); 1024 if (level == 0) { 1025 level = -1; /* exit the loop */ 1026 } 1027 break; 1028 case FDT_PROP: 1029 fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset, 1030 sizeof(*fdt_prop)); 1031 pathp = fdt_string(working_fdt, 1032 fdt32_to_cpu(fdt_prop->nameoff)); 1033 len = fdt32_to_cpu(fdt_prop->len); 1034 nodep = fdt_prop->data; 1035 if (len < 0) { 1036 printf ("libfdt fdt_getprop(): %s\n", 1037 fdt_strerror(len)); 1038 return 1; 1039 } else if (len == 0) { 1040 /* the property has no value */ 1041 if (level <= depth) 1042 printf("%s%s;\n", 1043 &tabs[MAX_LEVEL - level], 1044 pathp); 1045 } else { 1046 if (level <= depth) { 1047 printf("%s%s = ", 1048 &tabs[MAX_LEVEL - level], 1049 pathp); 1050 print_data (nodep, len); 1051 printf(";\n"); 1052 } 1053 } 1054 break; 1055 case FDT_NOP: 1056 printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]); 1057 break; 1058 case FDT_END: 1059 return 1; 1060 default: 1061 if (level <= depth) 1062 printf("Unknown tag 0x%08X\n", tag); 1063 return 1; 1064 } 1065 nodeoffset = nextoffset; 1066 } 1067 return 0; 1068 } 1069 1070 /********************************************************************/ 1071 #ifdef CONFIG_SYS_LONGHELP 1072 static char fdt_help_text[] = 1073 "addr [-c] <addr> [<length>] - Set the [control] fdt location to <addr>\n" 1074 #ifdef CONFIG_OF_LIBFDT_OVERLAY 1075 "fdt apply <addr> - Apply overlay to the DT\n" 1076 #endif 1077 #ifdef CONFIG_OF_BOARD_SETUP 1078 "fdt boardsetup - Do board-specific set up\n" 1079 #endif 1080 #ifdef CONFIG_OF_SYSTEM_SETUP 1081 "fdt systemsetup - Do system-specific set up\n" 1082 #endif 1083 "fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n" 1084 "fdt resize [<extrasize>] - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n" 1085 "fdt print <path> [<prop>] - Recursive print starting at <path>\n" 1086 "fdt list <path> [<prop>] - Print one level starting at <path>\n" 1087 "fdt get value <var> <path> <prop> - Get <property> and store in <var>\n" 1088 "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n" 1089 "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n" 1090 "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n" 1091 "fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n" 1092 "fdt mknode <path> <node> - Create a new node after <path>\n" 1093 "fdt rm <path> [<prop>] - Delete the node or <property>\n" 1094 "fdt header - Display header info\n" 1095 "fdt bootcpu <id> - Set boot cpuid\n" 1096 "fdt memory <addr> <size> - Add/Update memory node\n" 1097 "fdt rsvmem print - Show current mem reserves\n" 1098 "fdt rsvmem add <addr> <size> - Add a mem reserve\n" 1099 "fdt rsvmem delete <index> - Delete a mem reserves\n" 1100 "fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree\n" 1101 " <start>/<end> - initrd start/end addr\n" 1102 #if defined(CONFIG_FIT_SIGNATURE) 1103 "fdt checksign [<addr>] - check FIT signature\n" 1104 " <start> - addr of key blob\n" 1105 " default gd->fdt_blob\n" 1106 #endif 1107 "NOTE: Dereference aliases by omitting the leading '/', " 1108 "e.g. fdt print ethernet0."; 1109 #endif 1110 1111 U_BOOT_CMD( 1112 fdt, 255, 0, do_fdt, 1113 "flattened device tree utility commands", fdt_help_text 1114 ); 1115