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