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 <command.h>
13#include <fdt_support.h>
14#include <fdtdec.h>
15#include <env.h>
16#include <errno.h>
17#include <image.h>
18#include <lmb.h>
19#include <log.h>
20#include <malloc.h>
21#include <asm/global_data.h>
22#include <linux/libfdt.h>
23#include <mapmem.h>
24#include <asm/io.h>
25#include <dm/ofnode.h>
26#include <tee/optee.h>
27
28DECLARE_GLOBAL_DATA_PTR;
29
30static 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 CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
38static const struct legacy_img_hdr *image_get_fdt(ulong fdt_addr)
39{
40	const struct legacy_img_hdr *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
72static void boot_fdt_reserve_region(struct lmb *lmb, uint64_t addr,
73				    uint64_t size, enum lmb_flags flags)
74{
75	long ret;
76
77	ret = lmb_reserve_flags(lmb, addr, size, flags);
78	if (ret >= 0) {
79		debug("   reserving fdt memory region: addr=%llx size=%llx flags=%x\n",
80		      (unsigned long long)addr,
81		      (unsigned long long)size, flags);
82	} else {
83		puts("ERROR: reserving fdt memory region failed ");
84		printf("(addr=%llx size=%llx flags=%x)\n",
85		       (unsigned long long)addr,
86		       (unsigned long long)size, flags);
87	}
88}
89
90/**
91 * boot_fdt_add_mem_rsv_regions - Mark the memreserve and reserved-memory
92 * sections as unusable
93 * @lmb: pointer to lmb handle, will be used for memory mgmt
94 * @fdt_blob: pointer to fdt blob base address
95 *
96 * Adds the and reserved-memorymemreserve regions in the dtb to the lmb block.
97 * Adding the memreserve regions prevents u-boot from using them to store the
98 * initrd or the fdt blob.
99 */
100void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob)
101{
102	uint64_t addr, size;
103	int i, total, ret;
104	int nodeoffset, subnode;
105	struct fdt_resource res;
106	enum lmb_flags flags;
107
108	if (fdt_check_header(fdt_blob) != 0)
109		return;
110
111	/* process memreserve sections */
112	total = fdt_num_mem_rsv(fdt_blob);
113	for (i = 0; i < total; i++) {
114		if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
115			continue;
116		boot_fdt_reserve_region(lmb, addr, size, LMB_NONE);
117	}
118
119	/* process reserved-memory */
120	nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory");
121	if (nodeoffset >= 0) {
122		subnode = fdt_first_subnode(fdt_blob, nodeoffset);
123		while (subnode >= 0) {
124			/* check if this subnode has a reg property */
125			ret = fdt_get_resource(fdt_blob, subnode, "reg", 0,
126					       &res);
127			if (!ret && fdtdec_get_is_enabled(fdt_blob, subnode)) {
128				flags = LMB_NONE;
129				if (fdtdec_get_bool(fdt_blob, subnode,
130						    "no-map"))
131					flags = LMB_NOMAP;
132				addr = res.start;
133				size = res.end - res.start + 1;
134				boot_fdt_reserve_region(lmb, addr, size, flags);
135			}
136
137			subnode = fdt_next_subnode(fdt_blob, subnode);
138		}
139	}
140}
141
142/**
143 * boot_relocate_fdt - relocate flat device tree
144 * @lmb: pointer to lmb handle, will be used for memory mgmt
145 * @of_flat_tree: pointer to a char* variable, will hold fdt start address
146 * @of_size: pointer to a ulong variable, will hold fdt length
147 *
148 * boot_relocate_fdt() allocates a region of memory within the bootmap and
149 * relocates the of_flat_tree into that region, even if the fdt is already in
150 * the bootmap.  It also expands the size of the fdt by CONFIG_SYS_FDT_PAD
151 * bytes.
152 *
153 * of_flat_tree and of_size are set to final (after relocation) values
154 *
155 * returns:
156 *      0 - success
157 *      1 - failure
158 */
159int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size)
160{
161	u64	start, size, usable, addr, low, mapsize;
162	void	*fdt_blob = *of_flat_tree;
163	void	*of_start = NULL;
164	char	*fdt_high;
165	ulong	of_len = 0;
166	int	bank;
167	int	err;
168	int	disable_relocation = 0;
169
170	/* nothing to do */
171	if (*of_size == 0)
172		return 0;
173
174	if (fdt_check_header(fdt_blob) != 0) {
175		fdt_error("image is not a fdt");
176		goto error;
177	}
178
179	/* position on a 4K boundary before the alloc_current */
180	/* Pad the FDT by a specified amount */
181	of_len = *of_size + CONFIG_SYS_FDT_PAD;
182
183	/* If fdt_high is set use it to select the relocation address */
184	fdt_high = env_get("fdt_high");
185	if (fdt_high) {
186		ulong desired_addr = hextoul(fdt_high, NULL);
187
188		if (desired_addr == ~0UL) {
189			/* All ones means use fdt in place */
190			of_start = fdt_blob;
191			lmb_reserve(lmb, map_to_sysmem(of_start), of_len);
192			disable_relocation = 1;
193		} else if (desired_addr) {
194			addr = lmb_alloc_base(lmb, of_len, 0x1000,
195					      desired_addr);
196			of_start = map_sysmem(addr, of_len);
197			if (of_start == NULL) {
198				puts("Failed using fdt_high value for Device Tree");
199				goto error;
200			}
201		} else {
202			addr = lmb_alloc(lmb, of_len, 0x1000);
203			of_start = map_sysmem(addr, of_len);
204		}
205	} else {
206		mapsize = env_get_bootm_mapsize();
207		low = env_get_bootm_low();
208		of_start = NULL;
209
210		for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
211			start = gd->bd->bi_dram[bank].start;
212			size = gd->bd->bi_dram[bank].size;
213
214			/* DRAM bank addresses are too low, skip it. */
215			if (start + size < low)
216				continue;
217
218			/*
219			 * At least part of this DRAM bank is usable, try
220			 * using the DRAM bank up to 'usable' address limit
221			 * for LMB allocation.
222			 */
223			usable = min(start + size, low + mapsize);
224			addr = lmb_alloc_base(lmb, of_len, 0x1000, usable);
225			of_start = map_sysmem(addr, of_len);
226			/* Allocation succeeded, use this block. */
227			if (of_start != NULL)
228				break;
229
230			/*
231			 * Reduce the mapping size in the next bank
232			 * by the size of attempt in current bank.
233			 */
234			mapsize -= usable - max(start, low);
235			if (!mapsize)
236				break;
237		}
238	}
239
240	if (of_start == NULL) {
241		puts("device tree - allocation error\n");
242		goto error;
243	}
244
245	if (disable_relocation) {
246		/*
247		 * We assume there is space after the existing fdt to use
248		 * for padding
249		 */
250		fdt_set_totalsize(of_start, of_len);
251		printf("   Using Device Tree in place at %p, end %p\n",
252		       of_start, of_start + of_len - 1);
253	} else {
254		debug("## device tree at %p ... %p (len=%ld [0x%lX])\n",
255		      fdt_blob, fdt_blob + *of_size - 1, of_len, of_len);
256
257		printf("   Loading Device Tree to %p, end %p ... ",
258		       of_start, of_start + of_len - 1);
259
260		err = fdt_open_into(fdt_blob, of_start, of_len);
261		if (err != 0) {
262			fdt_error("fdt move failed");
263			goto error;
264		}
265		puts("OK\n");
266	}
267
268	*of_flat_tree = of_start;
269	*of_size = of_len;
270
271	if (IS_ENABLED(CONFIG_CMD_FDT))
272		set_working_fdt_addr(map_to_sysmem(*of_flat_tree));
273	return 0;
274
275error:
276	return 1;
277}
278
279/**
280 * select_fdt() - Select and locate the FDT to use
281 *
282 * @images: pointer to the bootm images structure
283 * @select: name of FDT to select, or NULL for any
284 * @arch: expected FDT architecture
285 * @fdt_addrp: pointer to a ulong variable, will hold FDT pointer
286 * Return: 0 if OK, -ENOPKG if no FDT (but an error should not be reported),
287 *	other -ve value on other error
288 */
289
290static int select_fdt(struct bootm_headers *images, const char *select, u8 arch,
291		      ulong *fdt_addrp)
292{
293	const char *buf;
294	ulong fdt_addr;
295
296#if CONFIG_IS_ENABLED(FIT)
297	const char *fit_uname_config = images->fit_uname_cfg;
298	const char *fit_uname_fdt = NULL;
299	ulong default_addr;
300	int fdt_noffset;
301
302	if (select) {
303			/*
304			 * If the FDT blob comes from the FIT image and the
305			 * FIT image address is omitted in the command line
306			 * argument, try to use ramdisk or os FIT image
307			 * address or default load address.
308			 */
309			if (images->fit_uname_rd)
310				default_addr = (ulong)images->fit_hdr_rd;
311			else if (images->fit_uname_os)
312				default_addr = (ulong)images->fit_hdr_os;
313			else
314				default_addr = image_load_addr;
315
316			if (fit_parse_conf(select, default_addr, &fdt_addr,
317					   &fit_uname_config)) {
318				debug("*  fdt: config '%s' from image at 0x%08lx\n",
319				      fit_uname_config, fdt_addr);
320			} else if (fit_parse_subimage(select, default_addr, &fdt_addr,
321				   &fit_uname_fdt)) {
322				debug("*  fdt: subimage '%s' from image at 0x%08lx\n",
323				      fit_uname_fdt, fdt_addr);
324			} else
325#endif
326		{
327			fdt_addr = hextoul(select, NULL);
328			debug("*  fdt: cmdline image address = 0x%08lx\n",
329			      fdt_addr);
330		}
331#if CONFIG_IS_ENABLED(FIT)
332	} else {
333		/* use FIT configuration provided in first bootm
334		 * command argument
335		 */
336		fdt_addr = map_to_sysmem(images->fit_hdr_os);
337		fdt_noffset = fit_get_node_from_config(images, FIT_FDT_PROP,
338						       fdt_addr);
339		if (fdt_noffset == -ENOENT)
340			return -ENOPKG;
341		else if (fdt_noffset < 0)
342			return fdt_noffset;
343	}
344#endif
345	debug("## Checking for 'FDT'/'FDT Image' at %08lx\n",
346	      fdt_addr);
347
348	/*
349	 * Check if there is an FDT image at the
350	 * address provided in the second bootm argument
351	 * check image type, for FIT images get a FIT node.
352	 */
353	buf = map_sysmem(fdt_addr, 0);
354	switch (genimg_get_format(buf)) {
355#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
356	case IMAGE_FORMAT_LEGACY: {
357			const struct legacy_img_hdr *fdt_hdr;
358			ulong load, load_end;
359			ulong image_start, image_data, image_end;
360
361			/* verify fdt_addr points to a valid image header */
362			printf("## Flattened Device Tree from Legacy Image at %08lx\n",
363			       fdt_addr);
364			fdt_hdr = image_get_fdt(fdt_addr);
365			if (!fdt_hdr)
366				return -ENOPKG;
367
368			/*
369			 * move image data to the load address,
370			 * make sure we don't overwrite initial image
371			 */
372			image_start = (ulong)fdt_hdr;
373			image_data = (ulong)image_get_data(fdt_hdr);
374			image_end = image_get_image_end(fdt_hdr);
375
376			load = image_get_load(fdt_hdr);
377			load_end = load + image_get_data_size(fdt_hdr);
378
379			if (load == image_start ||
380			    load == image_data) {
381				fdt_addr = load;
382				break;
383			}
384
385			if ((load < image_end) && (load_end > image_start)) {
386				fdt_error("fdt overwritten");
387				return -EFAULT;
388			}
389
390			debug("   Loading FDT from 0x%08lx to 0x%08lx\n",
391			      image_data, load);
392
393			memmove((void *)load,
394				(void *)image_data,
395				image_get_data_size(fdt_hdr));
396
397			fdt_addr = load;
398			break;
399		}
400#endif
401	case IMAGE_FORMAT_FIT:
402		/*
403		 * This case will catch both: new uImage format
404		 * (libfdt based) and raw FDT blob (also libfdt
405		 * based).
406		 */
407#if CONFIG_IS_ENABLED(FIT)
408			/* check FDT blob vs FIT blob */
409			if (!fit_check_format(buf, IMAGE_SIZE_INVAL)) {
410				ulong load, len;
411
412				fdt_noffset = boot_get_fdt_fit(images, fdt_addr,
413							       &fit_uname_fdt,
414							       &fit_uname_config,
415							       arch, &load, &len);
416
417				if (fdt_noffset < 0)
418					return -ENOENT;
419
420				images->fit_hdr_fdt = map_sysmem(fdt_addr, 0);
421				images->fit_uname_fdt = fit_uname_fdt;
422				images->fit_noffset_fdt = fdt_noffset;
423				fdt_addr = load;
424
425				break;
426		} else
427#endif
428		{
429			/*
430			 * FDT blob
431			 */
432			debug("*  fdt: raw FDT blob\n");
433			printf("## Flattened Device Tree blob at %08lx\n",
434			       (long)fdt_addr);
435		}
436		break;
437	default:
438		puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
439		return -ENOENT;
440	}
441	*fdt_addrp = fdt_addr;
442
443	return 0;
444}
445
446int boot_get_fdt(void *buf, const char *select, uint arch,
447		 struct bootm_headers *images, char **of_flat_tree,
448		 ulong *of_size)
449{
450	char *fdt_blob = NULL;
451	ulong fdt_addr;
452
453	*of_flat_tree = NULL;
454	*of_size = 0;
455
456	if (select || genimg_has_config(images)) {
457		int ret;
458
459		ret = select_fdt(images, select, arch, &fdt_addr);
460		if (ret == -ENOPKG)
461			goto no_fdt;
462		else if (ret)
463			return 1;
464		printf("   Booting using the fdt blob at %#08lx\n", fdt_addr);
465		fdt_blob = map_sysmem(fdt_addr, 0);
466	} else if (images->legacy_hdr_valid &&
467			image_check_type(&images->legacy_hdr_os_copy,
468					 IH_TYPE_MULTI)) {
469		ulong fdt_data, fdt_len;
470
471		/*
472		 * Now check if we have a legacy multi-component image,
473		 * get second entry data start address and len.
474		 */
475		printf("## Flattened Device Tree from multi component Image at %08lX\n",
476		       (ulong)images->legacy_hdr_os);
477
478		image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data,
479				   &fdt_len);
480		if (fdt_len) {
481			fdt_blob = (char *)fdt_data;
482			printf("   Booting using the fdt at 0x%p\n", fdt_blob);
483
484			if (fdt_check_header(fdt_blob) != 0) {
485				fdt_error("image is not a fdt");
486				goto error;
487			}
488
489			if (fdt_totalsize(fdt_blob) != fdt_len) {
490				fdt_error("fdt size != image size");
491				goto error;
492			}
493		} else {
494			debug("## No Flattened Device Tree\n");
495			goto no_fdt;
496		}
497#ifdef CONFIG_ANDROID_BOOT_IMAGE
498	} else if (genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) {
499		void *hdr = buf;
500		ulong		fdt_data, fdt_len;
501		u32			fdt_size, dtb_idx;
502		/*
503		 * Firstly check if this android boot image has dtb field.
504		 */
505		dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0);
506		if (android_image_get_dtb_by_index((ulong)hdr, 0,
507						   dtb_idx, &fdt_addr, &fdt_size)) {
508			fdt_blob = (char *)map_sysmem(fdt_addr, 0);
509			if (fdt_check_header(fdt_blob))
510				goto no_fdt;
511
512			debug("## Using FDT in Android image dtb area with idx %u\n", dtb_idx);
513		} else if (!android_image_get_second(hdr, &fdt_data, &fdt_len) &&
514			!fdt_check_header((char *)fdt_data)) {
515			fdt_blob = (char *)fdt_data;
516			if (fdt_totalsize(fdt_blob) != fdt_len)
517				goto error;
518
519			debug("## Using FDT in Android image second area\n");
520		} else {
521			fdt_addr = env_get_hex("fdtaddr", 0);
522			if (!fdt_addr)
523				goto no_fdt;
524
525			fdt_blob = map_sysmem(fdt_addr, 0);
526			if (fdt_check_header(fdt_blob))
527				goto no_fdt;
528
529			debug("## Using FDT at ${fdtaddr}=Ox%lx\n", fdt_addr);
530		}
531#endif
532	} else {
533		debug("## No Flattened Device Tree\n");
534		goto no_fdt;
535	}
536
537	*of_flat_tree = fdt_blob;
538	*of_size = fdt_totalsize(fdt_blob);
539	debug("   of_flat_tree at 0x%08lx size 0x%08lx\n",
540	      (ulong)*of_flat_tree, *of_size);
541
542	return 0;
543
544no_fdt:
545	debug("Continuing to boot without FDT\n");
546	return 0;
547error:
548	return 1;
549}
550
551/*
552 * Verify the device tree.
553 *
554 * This function is called after all device tree fix-ups have been enacted,
555 * so that the final device tree can be verified.  The definition of "verified"
556 * is up to the specific implementation.  However, it generally means that the
557 * addresses of some of the devices in the device tree are compared with the
558 * actual addresses at which U-Boot has placed them.
559 *
560 * Returns 1 on success, 0 on failure.  If 0 is returned, U-Boot will halt the
561 * boot process.
562 */
563__weak int ft_verify_fdt(void *fdt)
564{
565	return 1;
566}
567
568__weak int arch_fixup_fdt(void *blob)
569{
570	return 0;
571}
572
573int image_setup_libfdt(struct bootm_headers *images, void *blob,
574		       struct lmb *lmb)
575{
576	ulong *initrd_start = &images->initrd_start;
577	ulong *initrd_end = &images->initrd_end;
578	int ret, fdt_ret, of_size;
579
580	if (IS_ENABLED(CONFIG_OF_ENV_SETUP)) {
581		const char *fdt_fixup;
582
583		fdt_fixup = env_get("fdt_fixup");
584		if (fdt_fixup) {
585			set_working_fdt_addr(map_to_sysmem(blob));
586			ret = run_command_list(fdt_fixup, -1, 0);
587			if (ret)
588				printf("WARNING: fdt_fixup command returned %d\n",
589				       ret);
590		}
591	}
592
593	ret = -EPERM;
594
595	if (fdt_root(blob) < 0) {
596		printf("ERROR: root node setup failed\n");
597		goto err;
598	}
599	if (fdt_chosen(blob) < 0) {
600		printf("ERROR: /chosen node create failed\n");
601		goto err;
602	}
603	if (arch_fixup_fdt(blob) < 0) {
604		printf("ERROR: arch-specific fdt fixup failed\n");
605		goto err;
606	}
607
608	fdt_ret = optee_copy_fdt_nodes(blob);
609	if (fdt_ret) {
610		printf("ERROR: transfer of optee nodes to new fdt failed: %s\n",
611		       fdt_strerror(fdt_ret));
612		goto err;
613	}
614
615	/* Store name of configuration node as u-boot,bootconf in /chosen node */
616	if (images->fit_uname_cfg)
617		fdt_find_and_setprop(blob, "/chosen", "u-boot,bootconf",
618					images->fit_uname_cfg,
619					strlen(images->fit_uname_cfg) + 1, 1);
620
621	/* Update ethernet nodes */
622	fdt_fixup_ethernet(blob);
623#if IS_ENABLED(CONFIG_CMD_PSTORE)
624	/* Append PStore configuration */
625	fdt_fixup_pstore(blob);
626#endif
627	if (IS_ENABLED(CONFIG_OF_BOARD_SETUP)) {
628		const char *skip_board_fixup;
629
630		skip_board_fixup = env_get("skip_board_fixup");
631		if (skip_board_fixup && ((int)simple_strtol(skip_board_fixup, NULL, 10) == 1)) {
632			printf("skip board fdt fixup\n");
633		} else {
634			fdt_ret = ft_board_setup(blob, gd->bd);
635			if (fdt_ret) {
636				printf("ERROR: board-specific fdt fixup failed: %s\n",
637				       fdt_strerror(fdt_ret));
638				goto err;
639			}
640		}
641	}
642	if (IS_ENABLED(CONFIG_OF_SYSTEM_SETUP)) {
643		fdt_ret = ft_system_setup(blob, gd->bd);
644		if (fdt_ret) {
645			printf("ERROR: system-specific fdt fixup failed: %s\n",
646			       fdt_strerror(fdt_ret));
647			goto err;
648		}
649	}
650
651	if (fdt_initrd(blob, *initrd_start, *initrd_end))
652		goto err;
653
654	if (!ft_verify_fdt(blob))
655		goto err;
656
657	/* after here we are using a livetree */
658	if (!of_live_active() && CONFIG_IS_ENABLED(EVENT)) {
659		struct event_ft_fixup fixup;
660
661		fixup.tree = oftree_from_fdt(blob);
662		fixup.images = images;
663		if (oftree_valid(fixup.tree)) {
664			ret = event_notify(EVT_FT_FIXUP, &fixup, sizeof(fixup));
665			if (ret) {
666				printf("ERROR: fdt fixup event failed: %d\n",
667				       ret);
668				goto err;
669			}
670		}
671	}
672
673	/* Delete the old LMB reservation */
674	if (lmb)
675		lmb_free(lmb, map_to_sysmem(blob), fdt_totalsize(blob));
676
677	ret = fdt_shrink_to_minimum(blob, 0);
678	if (ret < 0)
679		goto err;
680	of_size = ret;
681
682	/* Create a new LMB reservation */
683	if (lmb)
684		lmb_reserve(lmb, map_to_sysmem(blob), of_size);
685
686#if defined(CONFIG_ARCH_KEYSTONE)
687	if (IS_ENABLED(CONFIG_OF_BOARD_SETUP))
688		ft_board_setup_ex(blob, gd->bd);
689#endif
690
691	return 0;
692err:
693	printf(" - must RESET the board to recover.\n\n");
694
695	return ret;
696}
697