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