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