1 /* 2 * Copyright (c) 2013, Google Inc. 3 * 4 * (C) Copyright 2008 Semihalf 5 * 6 * (C) Copyright 2000-2006 7 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 8 * 9 * SPDX-License-Identifier: GPL-2.0+ 10 */ 11 12 #include <common.h> 13 #include <fdtdec.h> 14 #include <fdt_support.h> 15 #include <errno.h> 16 #include <image.h> 17 #include <linux/libfdt.h> 18 #include <mapmem.h> 19 #include <asm/io.h> 20 #include <sysmem.h> 21 22 #ifndef CONFIG_SYS_FDT_PAD 23 #define CONFIG_SYS_FDT_PAD 0x3000 24 #endif 25 26 /* adding a ramdisk needs 0x44 bytes in version 2008.10 */ 27 #define FDT_RAMDISK_OVERHEAD 0x80 28 29 DECLARE_GLOBAL_DATA_PTR; 30 31 static void fdt_error(const char *msg) 32 { 33 puts("ERROR: "); 34 puts(msg); 35 puts(" - must RESET the board to recover.\n"); 36 } 37 38 #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 39 static const image_header_t *image_get_fdt(ulong fdt_addr) 40 { 41 const image_header_t *fdt_hdr = map_sysmem(fdt_addr, 0); 42 43 image_print_contents(fdt_hdr); 44 45 puts(" Verifying Checksum ... "); 46 if (!image_check_hcrc(fdt_hdr)) { 47 fdt_error("fdt header checksum invalid"); 48 return NULL; 49 } 50 51 if (!image_check_dcrc(fdt_hdr)) { 52 fdt_error("fdt checksum invalid"); 53 return NULL; 54 } 55 puts("OK\n"); 56 57 /* 58 * default image mkimage conflicts with fit mkimage on param: -T "flat_dt". 59 * 60 * error message: 61 * "./tools/mkimage: Can't set header for FIT Image support: Success" 62 */ 63 #if 0 64 if (!image_check_type(fdt_hdr, IH_TYPE_FLATDT)) { 65 fdt_error("uImage is not a fdt"); 66 return NULL; 67 } 68 #endif 69 if (image_get_comp(fdt_hdr) != IH_COMP_NONE) { 70 fdt_error("uImage is compressed"); 71 return NULL; 72 } 73 if (fdt_check_header((void *)image_get_data(fdt_hdr)) != 0) { 74 fdt_error("uImage data is not a fdt"); 75 return NULL; 76 } 77 return fdt_hdr; 78 } 79 #endif 80 81 void boot_mem_rsv_regions(struct lmb *lmb, void *fdt_blob) 82 { 83 int rsv_offset, offset; 84 fdt_size_t rsv_size; 85 fdt_addr_t rsv_addr; 86 const void *prop; 87 int i = 0; 88 int parent; 89 int na, ns; 90 91 if (fdt_check_header(fdt_blob) != 0) 92 return; 93 94 rsv_offset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory"); 95 if (rsv_offset == -FDT_ERR_NOTFOUND) 96 return; 97 98 parent = fdt_parent_offset(fdt_blob, rsv_offset); 99 if (parent < 0) 100 return; 101 102 na = fdt_address_cells(fdt_blob, parent); 103 ns = fdt_size_cells(fdt_blob, parent); 104 if (na < 1 || ns < 0) 105 return; 106 107 for (offset = fdt_first_subnode(fdt_blob, rsv_offset); 108 offset >= 0; 109 offset = fdt_next_subnode(fdt_blob, offset)) { 110 prop = fdt_getprop(fdt_blob, offset, "status", NULL); 111 if (prop && !strcmp(prop, "disabled")) 112 continue; 113 114 rsv_addr = fdtdec_get_addr_size_fixed(fdt_blob, offset, 115 "reg", 0, na, ns, &rsv_size, false); 116 117 if (rsv_addr == FDT_ADDR_T_NONE || !rsv_size) 118 continue; 119 120 i++; 121 /* be quiet while reserve */ 122 if (lmb) { 123 lmb_reserve(lmb, rsv_addr, rsv_size); 124 } else { 125 if (i == 1) 126 printf("## reserved-memory:\n"); 127 128 printf(" %s: addr=%llx size=%llx\n", 129 fdt_get_name(fdt_blob, offset, NULL), 130 (unsigned long long)rsv_addr, (unsigned long long)rsv_size); 131 } 132 } 133 } 134 135 /** 136 * boot_fdt_add_mem_rsv_regions - Mark the memreserve sections as unusable 137 * @lmb: pointer to lmb handle, will be used for memory mgmt 138 * @fdt_blob: pointer to fdt blob base address 139 * 140 * Adds the memreserve regions in the dtb to the lmb block. Adding the 141 * memreserve regions prevents u-boot from using them to store the initrd 142 * or the fdt blob. 143 */ 144 void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob) 145 { 146 uint64_t addr, size; 147 int i, total; 148 /* we needn't repeat do reserve, do_bootm_linux would call this again */ 149 static int rsv_done; 150 151 if (fdt_check_header(fdt_blob) != 0 || rsv_done) 152 return; 153 154 rsv_done = 1; 155 156 total = fdt_num_mem_rsv(fdt_blob); 157 for (i = 0; i < total; i++) { 158 if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0) 159 continue; 160 printf(" reserving fdt memory region: addr=%llx size=%llx\n", 161 (unsigned long long)addr, (unsigned long long)size); 162 lmb_reserve(lmb, addr, size); 163 } 164 165 /* lmb_reserve() for "reserved-memory" */ 166 boot_mem_rsv_regions(lmb, fdt_blob); 167 } 168 169 #ifdef CONFIG_SYSMEM 170 /** 171 * boot_fdt_add_mem_rsv_regions - Mark the memreserve sections as unusable 172 * @sysmem: pointer to sysmem handle, will be used for memory mgmt 173 * @fdt_blob: pointer to fdt blob base address 174 */ 175 int boot_fdt_add_sysmem_rsv_regions(void *fdt_blob) 176 { 177 uint64_t addr, size; 178 int i, total; 179 int rsv_offset, offset; 180 fdt_size_t rsv_size; 181 fdt_addr_t rsv_addr; 182 static int rsv_done; 183 char resvname[32]; 184 const void *prop; 185 int parent; 186 int na, ns; 187 188 if (fdt_check_header(fdt_blob) != 0 || rsv_done) 189 return -EINVAL; 190 191 rsv_done = 1; 192 193 total = fdt_num_mem_rsv(fdt_blob); 194 for (i = 0; i < total; i++) { 195 if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0) 196 continue; 197 debug(" sysmem: reserving fdt memory region: addr=%llx size=%llx\n", 198 (unsigned long long)addr, (unsigned long long)size); 199 sprintf(resvname, "fdt-memory-reserved%d", i); 200 if (!sysmem_fdt_reserve_alloc_base(resvname, addr, size)) 201 return -ENOMEM; 202 } 203 204 rsv_offset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory"); 205 if (rsv_offset == -FDT_ERR_NOTFOUND) 206 return -EINVAL; 207 208 parent = fdt_parent_offset(fdt_blob, rsv_offset); 209 if (parent < 0) 210 return -EINVAL; 211 212 na = fdt_address_cells(fdt_blob, parent); 213 ns = fdt_size_cells(fdt_blob, parent); 214 if (na < 1 || ns < 0) 215 return -EINVAL; 216 217 for (offset = fdt_first_subnode(fdt_blob, rsv_offset); 218 offset >= 0; 219 offset = fdt_next_subnode(fdt_blob, offset)) { 220 prop = fdt_getprop(fdt_blob, offset, "status", NULL); 221 if (prop && !strcmp(prop, "disabled")) 222 continue; 223 224 rsv_addr = fdtdec_get_addr_size_fixed(fdt_blob, offset, 225 "reg", 0, na, ns, &rsv_size, false); 226 /* 227 * kernel will alloc reserved memory dynamically for the node 228 * with start address from 0. 229 */ 230 if (rsv_addr == FDT_ADDR_T_NONE || !rsv_addr || !rsv_size) 231 continue; 232 debug(" sysmem: 'reserved-memory' %s: addr=%llx size=%llx\n", 233 fdt_get_name(fdt_blob, offset, NULL), 234 (unsigned long long)rsv_addr, (unsigned long long)rsv_size); 235 if (!sysmem_fdt_reserve_alloc_base(fdt_get_name(fdt_blob, offset, NULL), 236 rsv_addr, rsv_size)) 237 return -ENOMEM; 238 } 239 240 return 0; 241 } 242 #endif 243 244 /** 245 * boot_relocate_fdt - relocate flat device tree 246 * @lmb: pointer to lmb handle, will be used for memory mgmt 247 * @of_flat_tree: pointer to a char* variable, will hold fdt start address 248 * @of_size: pointer to a ulong variable, will hold fdt length 249 * 250 * boot_relocate_fdt() allocates a region of memory within the bootmap and 251 * relocates the of_flat_tree into that region, even if the fdt is already in 252 * the bootmap. It also expands the size of the fdt by CONFIG_SYS_FDT_PAD 253 * bytes. 254 * 255 * of_flat_tree and of_size are set to final (after relocation) values 256 * 257 * returns: 258 * 0 - success 259 * 1 - failure 260 */ 261 int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size) 262 { 263 void *fdt_blob = *of_flat_tree; 264 void *of_start = NULL; 265 char *fdt_high; 266 ulong of_len = 0; 267 int err; 268 int disable_relocation = 0; 269 270 /* nothing to do */ 271 if (*of_size == 0) 272 return 0; 273 274 if (fdt_check_header(fdt_blob) != 0) { 275 fdt_error("image is not a fdt"); 276 goto error; 277 } 278 279 /* position on a 4K boundary before the alloc_current */ 280 /* Pad the FDT by a specified amount */ 281 of_len = *of_size + CONFIG_SYS_FDT_PAD; 282 283 /* If fdt_high is set use it to select the relocation address */ 284 fdt_high = env_get("fdt_high"); 285 if (fdt_high) { 286 void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16); 287 288 if (((ulong) desired_addr) == ~0UL) { 289 /* All ones means use fdt in place */ 290 of_start = fdt_blob; 291 lmb_reserve(lmb, (ulong)of_start, of_len); 292 disable_relocation = 1; 293 } else if (desired_addr) { 294 295 if (desired_addr && env_get_yesno("bootm-reloc-at")) 296 desired_addr += of_len; 297 298 of_start = 299 (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, 300 (ulong)desired_addr); 301 if (of_start == NULL) { 302 puts("Failed using fdt_high value for Device Tree"); 303 goto error; 304 } 305 } else { 306 of_start = 307 (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000); 308 } 309 } else { 310 of_start = 311 (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, 312 env_get_bootm_mapsize() 313 + env_get_bootm_low()); 314 } 315 316 if (of_start == NULL) { 317 puts("device tree - allocation error\n"); 318 goto error; 319 } 320 321 if (disable_relocation) { 322 /* 323 * We assume there is space after the existing fdt to use 324 * for padding 325 */ 326 fdt_set_totalsize(of_start, of_len); 327 printf(" Using Device Tree in place at %p, end %p\n", 328 of_start, of_start + of_len - 1); 329 } else { 330 debug("## device tree at %p ... %p (len=%ld [0x%lX])\n", 331 fdt_blob, fdt_blob + *of_size - 1, of_len, of_len); 332 333 printf(" Loading Device Tree to %p, end %p ... ", 334 of_start, of_start + of_len - 1); 335 336 err = fdt_open_into(fdt_blob, of_start, of_len); 337 if (err != 0) { 338 fdt_error("fdt move failed"); 339 goto error; 340 } 341 puts("OK\n"); 342 } 343 344 *of_flat_tree = of_start; 345 *of_size = of_len; 346 347 #ifdef CONFIG_CMD_FDT 348 set_working_fdt_addr((ulong)*of_flat_tree); 349 #endif 350 return 0; 351 352 error: 353 return 1; 354 } 355 356 /** 357 * boot_get_fdt - main fdt handling routine 358 * @argc: command argument count 359 * @argv: command argument list 360 * @arch: architecture (IH_ARCH_...) 361 * @images: pointer to the bootm images structure 362 * @of_flat_tree: pointer to a char* variable, will hold fdt start address 363 * @of_size: pointer to a ulong variable, will hold fdt length 364 * 365 * boot_get_fdt() is responsible for finding a valid flat device tree image. 366 * Curently supported are the following ramdisk sources: 367 * - multicomponent kernel/ramdisk image, 368 * - commandline provided address of decicated ramdisk image. 369 * 370 * returns: 371 * 0, if fdt image was found and valid, or skipped 372 * of_flat_tree and of_size are set to fdt start address and length if 373 * fdt image is found and valid 374 * 375 * 1, if fdt image is found but corrupted 376 * of_flat_tree and of_size are set to 0 if no fdt exists 377 */ 378 int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch, 379 bootm_headers_t *images, char **of_flat_tree, ulong *of_size) 380 { 381 #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 382 const image_header_t *fdt_hdr; 383 ulong load, load_end; 384 ulong image_start, image_data, image_end; 385 #endif 386 ulong fdt_addr; 387 char *fdt_blob = NULL; 388 void *buf; 389 #if CONFIG_IS_ENABLED(FIT) 390 const char *fit_uname_config = images->fit_uname_cfg; 391 const char *fit_uname_fdt = NULL; 392 ulong default_addr; 393 int fdt_noffset; 394 #endif 395 const char *select = NULL; 396 int ok_no_fdt = 0; 397 398 *of_flat_tree = NULL; 399 *of_size = 0; 400 401 if (argc > 2) 402 select = argv[2]; 403 if (select || genimg_has_config(images)) { 404 #if CONFIG_IS_ENABLED(FIT) 405 if (select) { 406 /* 407 * If the FDT blob comes from the FIT image and the 408 * FIT image address is omitted in the command line 409 * argument, try to use ramdisk or os FIT image 410 * address or default load address. 411 */ 412 if (images->fit_uname_rd) 413 default_addr = (ulong)images->fit_hdr_rd; 414 else if (images->fit_uname_os) 415 default_addr = (ulong)images->fit_hdr_os; 416 else 417 default_addr = load_addr; 418 419 if (fit_parse_conf(select, default_addr, 420 &fdt_addr, &fit_uname_config)) { 421 debug("* fdt: config '%s' from image at 0x%08lx\n", 422 fit_uname_config, fdt_addr); 423 } else if (fit_parse_subimage(select, default_addr, 424 &fdt_addr, &fit_uname_fdt)) { 425 debug("* fdt: subimage '%s' from image at 0x%08lx\n", 426 fit_uname_fdt, fdt_addr); 427 } else 428 #endif 429 { 430 fdt_addr = simple_strtoul(select, NULL, 16); 431 debug("* fdt: cmdline image address = 0x%08lx\n", 432 fdt_addr); 433 } 434 #if CONFIG_IS_ENABLED(FIT) 435 } else { 436 /* use FIT configuration provided in first bootm 437 * command argument 438 */ 439 fdt_addr = map_to_sysmem(images->fit_hdr_os); 440 fdt_noffset = fit_get_node_from_config(images, 441 FIT_FDT_PROP, 442 fdt_addr); 443 if (fdt_noffset == -ENOENT) 444 return 0; 445 else if (fdt_noffset < 0) 446 return 1; 447 } 448 #endif 449 debug("## Checking for 'FDT'/'FDT Image' at %08lx\n", 450 fdt_addr); 451 452 /* 453 * Check if there is an FDT image at the 454 * address provided in the second bootm argument 455 * check image type, for FIT images get a FIT node. 456 */ 457 buf = map_sysmem(fdt_addr, 0); 458 switch (genimg_get_format(buf)) { 459 #if defined(CONFIG_IMAGE_FORMAT_LEGACY) 460 case IMAGE_FORMAT_LEGACY: 461 /* verify fdt_addr points to a valid image header */ 462 printf("## Flattened Device Tree from Legacy Image at %08lx\n", 463 fdt_addr); 464 fdt_hdr = image_get_fdt(fdt_addr); 465 if (!fdt_hdr) 466 goto no_fdt; 467 468 /* 469 * move image data to the load address, 470 * make sure we don't overwrite initial image 471 */ 472 image_start = (ulong)fdt_hdr; 473 image_data = (ulong)image_get_data(fdt_hdr); 474 image_end = image_get_image_end(fdt_hdr); 475 476 load = image_get_load(fdt_hdr); 477 load_end = load + image_get_data_size(fdt_hdr); 478 479 if (load == image_start || 480 load == image_data) { 481 fdt_addr = load; 482 break; 483 } 484 485 if ((load < image_end) && (load_end > image_start)) { 486 fdt_error("fdt overwritten"); 487 goto error; 488 } 489 490 debug(" Loading FDT from 0x%08lx to 0x%08lx\n", 491 image_data, load); 492 493 memmove((void *)load, 494 (void *)image_data, 495 image_get_data_size(fdt_hdr)); 496 497 fdt_addr = load; 498 break; 499 #endif 500 case IMAGE_FORMAT_FIT: 501 /* 502 * This case will catch both: new uImage format 503 * (libfdt based) and raw FDT blob (also libfdt 504 * based). 505 */ 506 #if CONFIG_IS_ENABLED(FIT) 507 /* check FDT blob vs FIT blob */ 508 if (fit_check_format(buf)) { 509 ulong load, len; 510 511 fdt_noffset = boot_get_fdt_fit(images, 512 fdt_addr, &fit_uname_fdt, 513 &fit_uname_config, 514 arch, &load, &len); 515 516 images->fit_hdr_fdt = map_sysmem(fdt_addr, 0); 517 images->fit_uname_fdt = fit_uname_fdt; 518 images->fit_noffset_fdt = fdt_noffset; 519 fdt_addr = load; 520 521 break; 522 } else 523 #endif 524 { 525 /* 526 * FDT blob 527 */ 528 debug("* fdt: raw FDT blob\n"); 529 printf("## Flattened Device Tree blob at %#010lx\n", 530 (long)fdt_addr); 531 } 532 break; 533 default: 534 puts("ERROR: Did not find a cmdline Flattened Device Tree\n"); 535 goto no_fdt; 536 } 537 538 printf(" Booting using the fdt blob at %#010lx\n", fdt_addr); 539 fdt_blob = map_sysmem(fdt_addr, 0); 540 } else if (images->legacy_hdr_valid && 541 image_check_type(&images->legacy_hdr_os_copy, 542 IH_TYPE_MULTI)) { 543 ulong fdt_data, fdt_len; 544 545 /* 546 * Now check if we have a legacy multi-component image, 547 * get second entry data start address and len. 548 */ 549 printf("## Flattened Device Tree from multi component Image at %08lX\n", 550 (ulong)images->legacy_hdr_os); 551 552 image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data, 553 &fdt_len); 554 if (fdt_len) { 555 fdt_blob = (char *)fdt_data; 556 printf(" Booting using the fdt at 0x%p\n", fdt_blob); 557 558 if (fdt_check_header(fdt_blob) != 0) { 559 fdt_error("image is not a fdt"); 560 goto error; 561 } 562 563 if (fdt_totalsize(fdt_blob) != fdt_len) { 564 fdt_error("fdt size != image size"); 565 goto error; 566 } 567 } else { 568 debug("## No Flattened Device Tree\n"); 569 goto no_fdt; 570 } 571 } else { 572 debug("## No Flattened Device Tree\n"); 573 goto no_fdt; 574 } 575 576 *of_flat_tree = fdt_blob; 577 *of_size = fdt_totalsize(fdt_blob); 578 debug(" of_flat_tree at 0x%08lx size 0x%08lx\n", 579 (ulong)*of_flat_tree, *of_size); 580 581 return 0; 582 583 no_fdt: 584 ok_no_fdt = 1; 585 error: 586 *of_flat_tree = NULL; 587 *of_size = 0; 588 if (!select && ok_no_fdt) { 589 debug("Continuing to boot without FDT\n"); 590 return 0; 591 } 592 return 1; 593 } 594 595 /* 596 * Verify the device tree. 597 * 598 * This function is called after all device tree fix-ups have been enacted, 599 * so that the final device tree can be verified. The definition of "verified" 600 * is up to the specific implementation. However, it generally means that the 601 * addresses of some of the devices in the device tree are compared with the 602 * actual addresses at which U-Boot has placed them. 603 * 604 * Returns 1 on success, 0 on failure. If 0 is returned, U-Boot will halt the 605 * boot process. 606 */ 607 __weak int ft_verify_fdt(void *fdt) 608 { 609 return 1; 610 } 611 612 __weak int arch_fixup_fdt(void *blob) 613 { 614 return 0; 615 } 616 617 int image_setup_libfdt(bootm_headers_t *images, void *blob, 618 int of_size, struct lmb *lmb) 619 { 620 ulong *initrd_start = &images->initrd_start; 621 ulong *initrd_end = &images->initrd_end; 622 int ret = -EPERM; 623 int fdt_ret; 624 625 if (arch_fixup_fdt(blob) < 0) { 626 printf("ERROR: arch-specific fdt fixup failed\n"); 627 goto err; 628 } 629 630 #if defined(CONFIG_PASS_DEVICE_SERIAL_BY_FDT) 631 if (fdt_root(blob) < 0) { 632 printf("ERROR: root node setup failed\n"); 633 goto err; 634 } 635 #endif 636 if (fdt_chosen(blob) < 0) { 637 printf("ERROR: /chosen node create failed\n"); 638 goto err; 639 } 640 641 /* Update ethernet nodes */ 642 fdt_fixup_ethernet(blob); 643 if (IMAGE_OF_BOARD_SETUP) { 644 fdt_ret = ft_board_setup(blob, gd->bd); 645 if (fdt_ret) { 646 printf("ERROR: board-specific fdt fixup failed: %s\n", 647 fdt_strerror(fdt_ret)); 648 goto err; 649 } 650 } 651 if (IMAGE_OF_SYSTEM_SETUP) { 652 fdt_ret = ft_system_setup(blob, gd->bd); 653 if (fdt_ret) { 654 printf("ERROR: system-specific fdt fixup failed: %s\n", 655 fdt_strerror(fdt_ret)); 656 goto err; 657 } 658 } 659 660 /* Delete the old LMB reservation */ 661 if (lmb) 662 lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, 663 (phys_size_t)fdt_totalsize(blob)); 664 665 ret = fdt_shrink_to_minimum(blob, 0); 666 if (ret < 0) 667 goto err; 668 of_size = ret; 669 670 if (*initrd_start && *initrd_end) { 671 of_size += FDT_RAMDISK_OVERHEAD; 672 fdt_set_totalsize(blob, of_size); 673 } 674 /* Create a new LMB reservation */ 675 if (lmb) 676 lmb_reserve(lmb, (ulong)blob, of_size); 677 678 fdt_initrd(blob, *initrd_start, *initrd_end); 679 if (!ft_verify_fdt(blob)) 680 goto err; 681 682 #if defined(CONFIG_SOC_KEYSTONE) 683 if (IMAGE_OF_BOARD_SETUP) 684 ft_board_setup_ex(blob, gd->bd); 685 #endif 686 687 return 0; 688 err: 689 printf(" - must RESET the board to recover.\n\n"); 690 691 return ret; 692 } 693