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