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