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