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