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