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