xref: /openbmc/u-boot/common/image-fdt.c (revision 6f4dbc21)
144d3a306SSimon Glass /*
244d3a306SSimon Glass  * Copyright (c) 2013, Google Inc.
344d3a306SSimon Glass  *
444d3a306SSimon Glass  * (C) Copyright 2008 Semihalf
544d3a306SSimon Glass  *
644d3a306SSimon Glass  * (C) Copyright 2000-2006
744d3a306SSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
844d3a306SSimon Glass  *
91a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
1044d3a306SSimon Glass  */
1144d3a306SSimon Glass 
1244d3a306SSimon Glass #include <common.h>
1344d3a306SSimon Glass #include <fdt_support.h>
1444d3a306SSimon Glass #include <errno.h>
1544d3a306SSimon Glass #include <image.h>
1644d3a306SSimon Glass #include <libfdt.h>
1744d3a306SSimon Glass #include <asm/io.h>
1844d3a306SSimon Glass 
1944d3a306SSimon Glass #ifndef CONFIG_SYS_FDT_PAD
2044d3a306SSimon Glass #define CONFIG_SYS_FDT_PAD 0x3000
2144d3a306SSimon Glass #endif
2244d3a306SSimon Glass 
2344d3a306SSimon Glass DECLARE_GLOBAL_DATA_PTR;
2444d3a306SSimon Glass 
2544d3a306SSimon Glass static void fdt_error(const char *msg)
2644d3a306SSimon Glass {
2744d3a306SSimon Glass 	puts("ERROR: ");
2844d3a306SSimon Glass 	puts(msg);
2944d3a306SSimon Glass 	puts(" - must RESET the board to recover.\n");
3044d3a306SSimon Glass }
3144d3a306SSimon Glass 
3221d29f7fSHeiko Schocher #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
3344d3a306SSimon Glass static const image_header_t *image_get_fdt(ulong fdt_addr)
3444d3a306SSimon Glass {
3544d3a306SSimon Glass 	const image_header_t *fdt_hdr = map_sysmem(fdt_addr, 0);
3644d3a306SSimon Glass 
3744d3a306SSimon Glass 	image_print_contents(fdt_hdr);
3844d3a306SSimon Glass 
3944d3a306SSimon Glass 	puts("   Verifying Checksum ... ");
4044d3a306SSimon Glass 	if (!image_check_hcrc(fdt_hdr)) {
4144d3a306SSimon Glass 		fdt_error("fdt header checksum invalid");
4244d3a306SSimon Glass 		return NULL;
4344d3a306SSimon Glass 	}
4444d3a306SSimon Glass 
4544d3a306SSimon Glass 	if (!image_check_dcrc(fdt_hdr)) {
4644d3a306SSimon Glass 		fdt_error("fdt checksum invalid");
4744d3a306SSimon Glass 		return NULL;
4844d3a306SSimon Glass 	}
4944d3a306SSimon Glass 	puts("OK\n");
5044d3a306SSimon Glass 
5144d3a306SSimon Glass 	if (!image_check_type(fdt_hdr, IH_TYPE_FLATDT)) {
5244d3a306SSimon Glass 		fdt_error("uImage is not a fdt");
5344d3a306SSimon Glass 		return NULL;
5444d3a306SSimon Glass 	}
5544d3a306SSimon Glass 	if (image_get_comp(fdt_hdr) != IH_COMP_NONE) {
5644d3a306SSimon Glass 		fdt_error("uImage is compressed");
5744d3a306SSimon Glass 		return NULL;
5844d3a306SSimon Glass 	}
592f0877c7SMasahiro Yamada 	if (fdt_check_header((void *)image_get_data(fdt_hdr)) != 0) {
6044d3a306SSimon Glass 		fdt_error("uImage data is not a fdt");
6144d3a306SSimon Glass 		return NULL;
6244d3a306SSimon Glass 	}
6344d3a306SSimon Glass 	return fdt_hdr;
6444d3a306SSimon Glass }
6521d29f7fSHeiko Schocher #endif
6644d3a306SSimon Glass 
6744d3a306SSimon Glass /**
6844d3a306SSimon Glass  * boot_fdt_add_mem_rsv_regions - Mark the memreserve sections as unusable
6944d3a306SSimon Glass  * @lmb: pointer to lmb handle, will be used for memory mgmt
7044d3a306SSimon Glass  * @fdt_blob: pointer to fdt blob base address
7144d3a306SSimon Glass  *
7244d3a306SSimon Glass  * Adds the memreserve regions in the dtb to the lmb block.  Adding the
7344d3a306SSimon Glass  * memreserve regions prevents u-boot from using them to store the initrd
7444d3a306SSimon Glass  * or the fdt blob.
7544d3a306SSimon Glass  */
7644d3a306SSimon Glass void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob)
7744d3a306SSimon Glass {
7844d3a306SSimon Glass 	uint64_t addr, size;
7944d3a306SSimon Glass 	int i, total;
8044d3a306SSimon Glass 
8144d3a306SSimon Glass 	if (fdt_check_header(fdt_blob) != 0)
8244d3a306SSimon Glass 		return;
8344d3a306SSimon Glass 
8444d3a306SSimon Glass 	total = fdt_num_mem_rsv(fdt_blob);
8544d3a306SSimon Glass 	for (i = 0; i < total; i++) {
8644d3a306SSimon Glass 		if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
8744d3a306SSimon Glass 			continue;
8844d3a306SSimon Glass 		printf("   reserving fdt memory region: addr=%llx size=%llx\n",
8944d3a306SSimon Glass 		       (unsigned long long)addr, (unsigned long long)size);
9044d3a306SSimon Glass 		lmb_reserve(lmb, addr, size);
9144d3a306SSimon Glass 	}
9244d3a306SSimon Glass }
9344d3a306SSimon Glass 
9444d3a306SSimon Glass /**
9544d3a306SSimon Glass  * boot_relocate_fdt - relocate flat device tree
9644d3a306SSimon Glass  * @lmb: pointer to lmb handle, will be used for memory mgmt
9744d3a306SSimon Glass  * @of_flat_tree: pointer to a char* variable, will hold fdt start address
9844d3a306SSimon Glass  * @of_size: pointer to a ulong variable, will hold fdt length
9944d3a306SSimon Glass  *
10044d3a306SSimon Glass  * boot_relocate_fdt() allocates a region of memory within the bootmap and
10144d3a306SSimon Glass  * relocates the of_flat_tree into that region, even if the fdt is already in
10244d3a306SSimon Glass  * the bootmap.  It also expands the size of the fdt by CONFIG_SYS_FDT_PAD
10344d3a306SSimon Glass  * bytes.
10444d3a306SSimon Glass  *
10544d3a306SSimon Glass  * of_flat_tree and of_size are set to final (after relocation) values
10644d3a306SSimon Glass  *
10744d3a306SSimon Glass  * returns:
10844d3a306SSimon Glass  *      0 - success
10944d3a306SSimon Glass  *      1 - failure
11044d3a306SSimon Glass  */
11144d3a306SSimon Glass int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size)
11244d3a306SSimon Glass {
11344d3a306SSimon Glass 	void	*fdt_blob = *of_flat_tree;
11444d3a306SSimon Glass 	void	*of_start = NULL;
11544d3a306SSimon Glass 	char	*fdt_high;
11644d3a306SSimon Glass 	ulong	of_len = 0;
11744d3a306SSimon Glass 	int	err;
11844d3a306SSimon Glass 	int	disable_relocation = 0;
11944d3a306SSimon Glass 
12044d3a306SSimon Glass 	/* nothing to do */
12144d3a306SSimon Glass 	if (*of_size == 0)
12244d3a306SSimon Glass 		return 0;
12344d3a306SSimon Glass 
12444d3a306SSimon Glass 	if (fdt_check_header(fdt_blob) != 0) {
12544d3a306SSimon Glass 		fdt_error("image is not a fdt");
12644d3a306SSimon Glass 		goto error;
12744d3a306SSimon Glass 	}
12844d3a306SSimon Glass 
12944d3a306SSimon Glass 	/* position on a 4K boundary before the alloc_current */
13044d3a306SSimon Glass 	/* Pad the FDT by a specified amount */
13144d3a306SSimon Glass 	of_len = *of_size + CONFIG_SYS_FDT_PAD;
13244d3a306SSimon Glass 
13344d3a306SSimon Glass 	/* If fdt_high is set use it to select the relocation address */
13444d3a306SSimon Glass 	fdt_high = getenv("fdt_high");
13544d3a306SSimon Glass 	if (fdt_high) {
13644d3a306SSimon Glass 		void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16);
13744d3a306SSimon Glass 
13844d3a306SSimon Glass 		if (((ulong) desired_addr) == ~0UL) {
13944d3a306SSimon Glass 			/* All ones means use fdt in place */
14044d3a306SSimon Glass 			of_start = fdt_blob;
14144d3a306SSimon Glass 			lmb_reserve(lmb, (ulong)of_start, of_len);
14244d3a306SSimon Glass 			disable_relocation = 1;
14344d3a306SSimon Glass 		} else if (desired_addr) {
14444d3a306SSimon Glass 			of_start =
14544d3a306SSimon Glass 			    (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,
14644d3a306SSimon Glass 							   (ulong)desired_addr);
14744d3a306SSimon Glass 			if (of_start == NULL) {
14844d3a306SSimon Glass 				puts("Failed using fdt_high value for Device Tree");
14944d3a306SSimon Glass 				goto error;
15044d3a306SSimon Glass 			}
15144d3a306SSimon Glass 		} else {
15244d3a306SSimon Glass 			of_start =
15344d3a306SSimon Glass 			    (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000);
15444d3a306SSimon Glass 		}
15544d3a306SSimon Glass 	} else {
15644d3a306SSimon Glass 		of_start =
15744d3a306SSimon Glass 		    (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,
15844d3a306SSimon Glass 						   getenv_bootm_mapsize()
15944d3a306SSimon Glass 						   + getenv_bootm_low());
16044d3a306SSimon Glass 	}
16144d3a306SSimon Glass 
16244d3a306SSimon Glass 	if (of_start == NULL) {
16344d3a306SSimon Glass 		puts("device tree - allocation error\n");
16444d3a306SSimon Glass 		goto error;
16544d3a306SSimon Glass 	}
16644d3a306SSimon Glass 
16744d3a306SSimon Glass 	if (disable_relocation) {
16844d3a306SSimon Glass 		/*
16944d3a306SSimon Glass 		 * We assume there is space after the existing fdt to use
17044d3a306SSimon Glass 		 * for padding
17144d3a306SSimon Glass 		 */
17244d3a306SSimon Glass 		fdt_set_totalsize(of_start, of_len);
17344d3a306SSimon Glass 		printf("   Using Device Tree in place at %p, end %p\n",
17444d3a306SSimon Glass 		       of_start, of_start + of_len - 1);
17544d3a306SSimon Glass 	} else {
17644d3a306SSimon Glass 		debug("## device tree at %p ... %p (len=%ld [0x%lX])\n",
17744d3a306SSimon Glass 		      fdt_blob, fdt_blob + *of_size - 1, of_len, of_len);
17844d3a306SSimon Glass 
17944d3a306SSimon Glass 		printf("   Loading Device Tree to %p, end %p ... ",
18044d3a306SSimon Glass 		       of_start, of_start + of_len - 1);
18144d3a306SSimon Glass 
18244d3a306SSimon Glass 		err = fdt_open_into(fdt_blob, of_start, of_len);
18344d3a306SSimon Glass 		if (err != 0) {
18444d3a306SSimon Glass 			fdt_error("fdt move failed");
18544d3a306SSimon Glass 			goto error;
18644d3a306SSimon Glass 		}
18744d3a306SSimon Glass 		puts("OK\n");
18844d3a306SSimon Glass 	}
18944d3a306SSimon Glass 
19044d3a306SSimon Glass 	*of_flat_tree = of_start;
19144d3a306SSimon Glass 	*of_size = of_len;
19244d3a306SSimon Glass 
19344d3a306SSimon Glass 	set_working_fdt_addr(*of_flat_tree);
19444d3a306SSimon Glass 	return 0;
19544d3a306SSimon Glass 
19644d3a306SSimon Glass error:
19744d3a306SSimon Glass 	return 1;
19844d3a306SSimon Glass }
19944d3a306SSimon Glass 
20044d3a306SSimon Glass /**
20144d3a306SSimon Glass  * boot_get_fdt - main fdt handling routine
20244d3a306SSimon Glass  * @argc: command argument count
20344d3a306SSimon Glass  * @argv: command argument list
20453f375faSSimon Glass  * @arch: architecture (IH_ARCH_...)
20544d3a306SSimon Glass  * @images: pointer to the bootm images structure
20644d3a306SSimon Glass  * @of_flat_tree: pointer to a char* variable, will hold fdt start address
20744d3a306SSimon Glass  * @of_size: pointer to a ulong variable, will hold fdt length
20844d3a306SSimon Glass  *
20944d3a306SSimon Glass  * boot_get_fdt() is responsible for finding a valid flat device tree image.
21044d3a306SSimon Glass  * Curently supported are the following ramdisk sources:
21144d3a306SSimon Glass  *      - multicomponent kernel/ramdisk image,
21244d3a306SSimon Glass  *      - commandline provided address of decicated ramdisk image.
21344d3a306SSimon Glass  *
21444d3a306SSimon Glass  * returns:
21544d3a306SSimon Glass  *     0, if fdt image was found and valid, or skipped
21644d3a306SSimon Glass  *     of_flat_tree and of_size are set to fdt start address and length if
21744d3a306SSimon Glass  *     fdt image is found and valid
21844d3a306SSimon Glass  *
21944d3a306SSimon Glass  *     1, if fdt image is found but corrupted
22044d3a306SSimon Glass  *     of_flat_tree and of_size are set to 0 if no fdt exists
22144d3a306SSimon Glass  */
22253f375faSSimon Glass int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
22344d3a306SSimon Glass 		bootm_headers_t *images, char **of_flat_tree, ulong *of_size)
22444d3a306SSimon Glass {
22521d29f7fSHeiko Schocher #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
22644d3a306SSimon Glass 	const image_header_t *fdt_hdr;
22721d29f7fSHeiko Schocher 	ulong		load, load_end;
22821d29f7fSHeiko Schocher 	ulong		image_start, image_data, image_end;
22921d29f7fSHeiko Schocher #endif
23044d3a306SSimon Glass 	ulong		fdt_addr;
23144d3a306SSimon Glass 	char		*fdt_blob = NULL;
23244d3a306SSimon Glass 	void		*buf;
23344d3a306SSimon Glass #if defined(CONFIG_FIT)
234f320a4d8SSimon Glass 	const char	*fit_uname_config = images->fit_uname_cfg;
23544d3a306SSimon Glass 	const char	*fit_uname_fdt = NULL;
23644d3a306SSimon Glass 	ulong		default_addr;
23744d3a306SSimon Glass 	int		fdt_noffset;
23844d3a306SSimon Glass #endif
239983c72f4SSimon Glass 	const char *select = NULL;
24044d3a306SSimon Glass 
24144d3a306SSimon Glass 	*of_flat_tree = NULL;
24244d3a306SSimon Glass 	*of_size = 0;
24344d3a306SSimon Glass 
244983c72f4SSimon Glass 	if (argc > 2)
245983c72f4SSimon Glass 		select = argv[2];
246983c72f4SSimon Glass 	if (select || genimg_has_config(images)) {
24744d3a306SSimon Glass #if defined(CONFIG_FIT)
248983c72f4SSimon Glass 		if (select) {
24944d3a306SSimon Glass 			/*
25044d3a306SSimon Glass 			 * If the FDT blob comes from the FIT image and the
25144d3a306SSimon Glass 			 * FIT image address is omitted in the command line
25244d3a306SSimon Glass 			 * argument, try to use ramdisk or os FIT image
25344d3a306SSimon Glass 			 * address or default load address.
25444d3a306SSimon Glass 			 */
25544d3a306SSimon Glass 			if (images->fit_uname_rd)
25644d3a306SSimon Glass 				default_addr = (ulong)images->fit_hdr_rd;
25744d3a306SSimon Glass 			else if (images->fit_uname_os)
25844d3a306SSimon Glass 				default_addr = (ulong)images->fit_hdr_os;
25944d3a306SSimon Glass 			else
26044d3a306SSimon Glass 				default_addr = load_addr;
26144d3a306SSimon Glass 
262983c72f4SSimon Glass 			if (fit_parse_conf(select, default_addr,
26344d3a306SSimon Glass 					   &fdt_addr, &fit_uname_config)) {
26444d3a306SSimon Glass 				debug("*  fdt: config '%s' from image at 0x%08lx\n",
26544d3a306SSimon Glass 				      fit_uname_config, fdt_addr);
266983c72f4SSimon Glass 			} else if (fit_parse_subimage(select, default_addr,
26744d3a306SSimon Glass 				   &fdt_addr, &fit_uname_fdt)) {
26844d3a306SSimon Glass 				debug("*  fdt: subimage '%s' from image at 0x%08lx\n",
26944d3a306SSimon Glass 				      fit_uname_fdt, fdt_addr);
27044d3a306SSimon Glass 			} else
27144d3a306SSimon Glass #endif
27244d3a306SSimon Glass 			{
273983c72f4SSimon Glass 				fdt_addr = simple_strtoul(select, NULL, 16);
27444d3a306SSimon Glass 				debug("*  fdt: cmdline image address = 0x%08lx\n",
27544d3a306SSimon Glass 				      fdt_addr);
27644d3a306SSimon Glass 			}
27744d3a306SSimon Glass #if defined(CONFIG_FIT)
27844d3a306SSimon Glass 		} else {
27944d3a306SSimon Glass 			/* use FIT configuration provided in first bootm
28044d3a306SSimon Glass 			 * command argument
28144d3a306SSimon Glass 			 */
28244d3a306SSimon Glass 			fdt_addr = map_to_sysmem(images->fit_hdr_os);
28353f375faSSimon Glass 			fdt_noffset = fit_get_node_from_config(images,
28453f375faSSimon Glass 							       FIT_FDT_PROP,
28553f375faSSimon Glass 							       fdt_addr);
28653f375faSSimon Glass 			if (fdt_noffset == -ENOLINK)
28744d3a306SSimon Glass 				return 0;
28853f375faSSimon Glass 			else if (fdt_noffset < 0)
28953f375faSSimon Glass 				return 1;
29044d3a306SSimon Glass 		}
29144d3a306SSimon Glass #endif
29244d3a306SSimon Glass 		debug("## Checking for 'FDT'/'FDT Image' at %08lx\n",
29344d3a306SSimon Glass 		      fdt_addr);
29444d3a306SSimon Glass 
29544d3a306SSimon Glass 		/* copy from dataflash if needed */
29644d3a306SSimon Glass 		fdt_addr = genimg_get_image(fdt_addr);
29744d3a306SSimon Glass 
29844d3a306SSimon Glass 		/*
29944d3a306SSimon Glass 		 * Check if there is an FDT image at the
30044d3a306SSimon Glass 		 * address provided in the second bootm argument
30144d3a306SSimon Glass 		 * check image type, for FIT images get a FIT node.
30244d3a306SSimon Glass 		 */
30344d3a306SSimon Glass 		buf = map_sysmem(fdt_addr, 0);
30444d3a306SSimon Glass 		switch (genimg_get_format(buf)) {
30521d29f7fSHeiko Schocher #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
30644d3a306SSimon Glass 		case IMAGE_FORMAT_LEGACY:
30744d3a306SSimon Glass 			/* verify fdt_addr points to a valid image header */
30844d3a306SSimon Glass 			printf("## Flattened Device Tree from Legacy Image at %08lx\n",
30944d3a306SSimon Glass 			       fdt_addr);
31044d3a306SSimon Glass 			fdt_hdr = image_get_fdt(fdt_addr);
31144d3a306SSimon Glass 			if (!fdt_hdr)
31244d3a306SSimon Glass 				goto error;
31344d3a306SSimon Glass 
31444d3a306SSimon Glass 			/*
31544d3a306SSimon Glass 			 * move image data to the load address,
31644d3a306SSimon Glass 			 * make sure we don't overwrite initial image
31744d3a306SSimon Glass 			 */
31844d3a306SSimon Glass 			image_start = (ulong)fdt_hdr;
31944d3a306SSimon Glass 			image_data = (ulong)image_get_data(fdt_hdr);
32044d3a306SSimon Glass 			image_end = image_get_image_end(fdt_hdr);
32144d3a306SSimon Glass 
32253f375faSSimon Glass 			load = image_get_load(fdt_hdr);
32353f375faSSimon Glass 			load_end = load + image_get_data_size(fdt_hdr);
32444d3a306SSimon Glass 
32553f375faSSimon Glass 			if (load == image_start ||
32653f375faSSimon Glass 			    load == image_data) {
32744d3a306SSimon Glass 				fdt_blob = (char *)image_data;
32844d3a306SSimon Glass 				break;
32944d3a306SSimon Glass 			}
33044d3a306SSimon Glass 
33153f375faSSimon Glass 			if ((load < image_end) && (load_end > image_start)) {
33244d3a306SSimon Glass 				fdt_error("fdt overwritten");
33344d3a306SSimon Glass 				goto error;
33444d3a306SSimon Glass 			}
33544d3a306SSimon Glass 
33644d3a306SSimon Glass 			debug("   Loading FDT from 0x%08lx to 0x%08lx\n",
33753f375faSSimon Glass 			      image_data, load);
33844d3a306SSimon Glass 
33953f375faSSimon Glass 			memmove((void *)load,
34044d3a306SSimon Glass 				(void *)image_data,
34144d3a306SSimon Glass 				image_get_data_size(fdt_hdr));
34244d3a306SSimon Glass 
34353f375faSSimon Glass 			fdt_addr = load;
34444d3a306SSimon Glass 			break;
34521d29f7fSHeiko Schocher #endif
34644d3a306SSimon Glass 		case IMAGE_FORMAT_FIT:
34744d3a306SSimon Glass 			/*
34844d3a306SSimon Glass 			 * This case will catch both: new uImage format
34944d3a306SSimon Glass 			 * (libfdt based) and raw FDT blob (also libfdt
35044d3a306SSimon Glass 			 * based).
35144d3a306SSimon Glass 			 */
35244d3a306SSimon Glass #if defined(CONFIG_FIT)
35344d3a306SSimon Glass 			/* check FDT blob vs FIT blob */
35444d3a306SSimon Glass 			if (fit_check_format(buf)) {
35553f375faSSimon Glass 				ulong load, len;
35644d3a306SSimon Glass 
35753f375faSSimon Glass 				fdt_noffset = fit_image_load(images,
35853f375faSSimon Glass 					fdt_addr, &fit_uname_fdt,
359f320a4d8SSimon Glass 					&fit_uname_config,
36053f375faSSimon Glass 					arch, IH_TYPE_FLATDT,
36153f375faSSimon Glass 					BOOTSTAGE_ID_FIT_FDT_START,
36253f375faSSimon Glass 					FIT_LOAD_OPTIONAL, &load, &len);
36344d3a306SSimon Glass 
36453f375faSSimon Glass 				images->fit_hdr_fdt = map_sysmem(fdt_addr, 0);
36544d3a306SSimon Glass 				images->fit_uname_fdt = fit_uname_fdt;
36644d3a306SSimon Glass 				images->fit_noffset_fdt = fdt_noffset;
36753f375faSSimon Glass 				fdt_addr = load;
36844d3a306SSimon Glass 				break;
36944d3a306SSimon Glass 			} else
37044d3a306SSimon Glass #endif
37144d3a306SSimon Glass 			{
37244d3a306SSimon Glass 				/*
37344d3a306SSimon Glass 				 * FDT blob
37444d3a306SSimon Glass 				 */
37544d3a306SSimon Glass 				debug("*  fdt: raw FDT blob\n");
37644d3a306SSimon Glass 				printf("## Flattened Device Tree blob at %08lx\n",
37744d3a306SSimon Glass 				       (long)fdt_addr);
37844d3a306SSimon Glass 			}
37944d3a306SSimon Glass 			break;
38044d3a306SSimon Glass 		default:
38144d3a306SSimon Glass 			puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
38244d3a306SSimon Glass 			goto error;
38344d3a306SSimon Glass 		}
38444d3a306SSimon Glass 
38553f375faSSimon Glass 		printf("   Booting using the fdt blob at %#08lx\n", fdt_addr);
38653f375faSSimon Glass 		fdt_blob = map_sysmem(fdt_addr, 0);
38744d3a306SSimon Glass 	} else if (images->legacy_hdr_valid &&
38844d3a306SSimon Glass 			image_check_type(&images->legacy_hdr_os_copy,
38944d3a306SSimon Glass 					 IH_TYPE_MULTI)) {
39044d3a306SSimon Glass 		ulong fdt_data, fdt_len;
39144d3a306SSimon Glass 
39244d3a306SSimon Glass 		/*
39344d3a306SSimon Glass 		 * Now check if we have a legacy multi-component image,
39444d3a306SSimon Glass 		 * get second entry data start address and len.
39544d3a306SSimon Glass 		 */
39644d3a306SSimon Glass 		printf("## Flattened Device Tree from multi component Image at %08lX\n",
39744d3a306SSimon Glass 		       (ulong)images->legacy_hdr_os);
39844d3a306SSimon Glass 
39944d3a306SSimon Glass 		image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data,
40044d3a306SSimon Glass 				   &fdt_len);
40144d3a306SSimon Glass 		if (fdt_len) {
40244d3a306SSimon Glass 			fdt_blob = (char *)fdt_data;
40344d3a306SSimon Glass 			printf("   Booting using the fdt at 0x%p\n", fdt_blob);
40444d3a306SSimon Glass 
40544d3a306SSimon Glass 			if (fdt_check_header(fdt_blob) != 0) {
40644d3a306SSimon Glass 				fdt_error("image is not a fdt");
40744d3a306SSimon Glass 				goto error;
40844d3a306SSimon Glass 			}
40944d3a306SSimon Glass 
41044d3a306SSimon Glass 			if (fdt_totalsize(fdt_blob) != fdt_len) {
41144d3a306SSimon Glass 				fdt_error("fdt size != image size");
41244d3a306SSimon Glass 				goto error;
41344d3a306SSimon Glass 			}
41444d3a306SSimon Glass 		} else {
41544d3a306SSimon Glass 			debug("## No Flattened Device Tree\n");
416c6150aafSNoam Camus 			goto error;
41744d3a306SSimon Glass 		}
41844d3a306SSimon Glass 	} else {
41944d3a306SSimon Glass 		debug("## No Flattened Device Tree\n");
420c6150aafSNoam Camus 		goto error;
42144d3a306SSimon Glass 	}
42244d3a306SSimon Glass 
42344d3a306SSimon Glass 	*of_flat_tree = fdt_blob;
42444d3a306SSimon Glass 	*of_size = fdt_totalsize(fdt_blob);
42544d3a306SSimon Glass 	debug("   of_flat_tree at 0x%08lx size 0x%08lx\n",
42644d3a306SSimon Glass 	      (ulong)*of_flat_tree, *of_size);
42744d3a306SSimon Glass 
42844d3a306SSimon Glass 	return 0;
42944d3a306SSimon Glass 
43044d3a306SSimon Glass error:
43144d3a306SSimon Glass 	*of_flat_tree = NULL;
43244d3a306SSimon Glass 	*of_size = 0;
43344d3a306SSimon Glass 	return 1;
43444d3a306SSimon Glass }
43513d06981SSimon Glass 
43613d06981SSimon Glass /*
43713d06981SSimon Glass  * Verify the device tree.
43813d06981SSimon Glass  *
43913d06981SSimon Glass  * This function is called after all device tree fix-ups have been enacted,
44013d06981SSimon Glass  * so that the final device tree can be verified.  The definition of "verified"
44113d06981SSimon Glass  * is up to the specific implementation.  However, it generally means that the
44213d06981SSimon Glass  * addresses of some of the devices in the device tree are compared with the
44313d06981SSimon Glass  * actual addresses at which U-Boot has placed them.
44413d06981SSimon Glass  *
44513d06981SSimon Glass  * Returns 1 on success, 0 on failure.  If 0 is returned, U-boot will halt the
44613d06981SSimon Glass  * boot process.
44713d06981SSimon Glass  */
44813d06981SSimon Glass __weak int ft_verify_fdt(void *fdt)
44913d06981SSimon Glass {
45013d06981SSimon Glass 	return 1;
45113d06981SSimon Glass }
45213d06981SSimon Glass 
453e29607edSMa Haijun __weak int arch_fixup_fdt(void *blob)
45413d06981SSimon Glass {
45513d06981SSimon Glass 	return 0;
45613d06981SSimon Glass }
45713d06981SSimon Glass 
45813d06981SSimon Glass int image_setup_libfdt(bootm_headers_t *images, void *blob,
45913d06981SSimon Glass 		       int of_size, struct lmb *lmb)
46013d06981SSimon Glass {
46113d06981SSimon Glass 	ulong *initrd_start = &images->initrd_start;
46213d06981SSimon Glass 	ulong *initrd_end = &images->initrd_end;
463*6f4dbc21SSimon Glass 	int ret = -EPERM;
464*6f4dbc21SSimon Glass 	int fdt_ret;
46513d06981SSimon Glass 
466bc6ed0f9SMasahiro Yamada 	if (fdt_chosen(blob) < 0) {
467*6f4dbc21SSimon Glass 		printf("ERROR: /chosen node create failed\n");
468*6f4dbc21SSimon Glass 		goto err;
46913d06981SSimon Glass 	}
470e29607edSMa Haijun 	if (arch_fixup_fdt(blob) < 0) {
471*6f4dbc21SSimon Glass 		printf("ERROR: arch-specific fdt fixup failed\n");
472*6f4dbc21SSimon Glass 		goto err;
473e29607edSMa Haijun 	}
474*6f4dbc21SSimon Glass 	if (IMAGE_OF_BOARD_SETUP) {
475*6f4dbc21SSimon Glass 		fdt_ret = ft_board_setup(blob, gd->bd);
476*6f4dbc21SSimon Glass 		if (fdt_ret) {
477*6f4dbc21SSimon Glass 			printf("ERROR: board-specific fdt fixup failed: %s\n",
478*6f4dbc21SSimon Glass 			       fdt_strerror(fdt_ret));
479*6f4dbc21SSimon Glass 			goto err;
480*6f4dbc21SSimon Glass 		}
481*6f4dbc21SSimon Glass 	}
48213d06981SSimon Glass 	fdt_fixup_ethernet(blob);
48313d06981SSimon Glass 
48413d06981SSimon Glass 	/* Delete the old LMB reservation */
48513d06981SSimon Glass 	lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob,
48613d06981SSimon Glass 		 (phys_size_t)fdt_totalsize(blob));
48713d06981SSimon Glass 
4885bf58cccSSimon Glass 	ret = fdt_shrink_to_minimum(blob);
48913d06981SSimon Glass 	if (ret < 0)
490*6f4dbc21SSimon Glass 		goto err;
49113d06981SSimon Glass 	of_size = ret;
49213d06981SSimon Glass 
49313d06981SSimon Glass 	if (*initrd_start && *initrd_end) {
49413d06981SSimon Glass 		of_size += FDT_RAMDISK_OVERHEAD;
49513d06981SSimon Glass 		fdt_set_totalsize(blob, of_size);
49613d06981SSimon Glass 	}
49713d06981SSimon Glass 	/* Create a new LMB reservation */
49813d06981SSimon Glass 	lmb_reserve(lmb, (ulong)blob, of_size);
49913d06981SSimon Glass 
500dbe963aeSMasahiro Yamada 	fdt_initrd(blob, *initrd_start, *initrd_end);
50113d06981SSimon Glass 	if (!ft_verify_fdt(blob))
502*6f4dbc21SSimon Glass 		goto err;
50313d06981SSimon Glass 
5047c387646SKhoronzhuk, Ivan #if defined(CONFIG_SOC_KEYSTONE)
50500c200f1SVitaly Andrianov 	if (IMAGE_OF_BOARD_SETUP)
50600c200f1SVitaly Andrianov 		ft_board_setup_ex(blob, gd->bd);
50700c200f1SVitaly Andrianov #endif
50800c200f1SVitaly Andrianov 
50913d06981SSimon Glass 	return 0;
510*6f4dbc21SSimon Glass err:
511*6f4dbc21SSimon Glass 	printf(" - must RESET the board to recover.\n\n");
512*6f4dbc21SSimon Glass 
513*6f4dbc21SSimon Glass 	return ret;
51413d06981SSimon Glass }
515