xref: /openbmc/u-boot/common/image.c (revision 274cea2b)
1b97a2a0aSMarian Balakowicz /*
2b97a2a0aSMarian Balakowicz  * (C) Copyright 2008 Semihalf
3b97a2a0aSMarian Balakowicz  *
4b97a2a0aSMarian Balakowicz  * (C) Copyright 2000-2006
5b97a2a0aSMarian Balakowicz  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6b97a2a0aSMarian Balakowicz  *
7b97a2a0aSMarian Balakowicz  * See file CREDITS for list of people who contributed to this
8b97a2a0aSMarian Balakowicz  * project.
9b97a2a0aSMarian Balakowicz  *
10b97a2a0aSMarian Balakowicz  * This program is free software; you can redistribute it and/or
11b97a2a0aSMarian Balakowicz  * modify it under the terms of the GNU General Public License as
12b97a2a0aSMarian Balakowicz  * published by the Free Software Foundation; either version 2 of
13b97a2a0aSMarian Balakowicz  * the License, or (at your option) any later version.
14b97a2a0aSMarian Balakowicz  *
15b97a2a0aSMarian Balakowicz  * This program is distributed in the hope that it will be useful,
16b97a2a0aSMarian Balakowicz  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17b97a2a0aSMarian Balakowicz  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18b97a2a0aSMarian Balakowicz  * GNU General Public License for more details.
19b97a2a0aSMarian Balakowicz  *
20b97a2a0aSMarian Balakowicz  * You should have received a copy of the GNU General Public License
21b97a2a0aSMarian Balakowicz  * along with this program; if not, write to the Free Software
22b97a2a0aSMarian Balakowicz  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23b97a2a0aSMarian Balakowicz  * MA 02111-1307 USA
24b97a2a0aSMarian Balakowicz  */
25ceaed2b1SMarian Balakowicz 
26ceaed2b1SMarian Balakowicz #define DEBUG
27ceaed2b1SMarian Balakowicz 
28b97a2a0aSMarian Balakowicz #ifndef USE_HOSTCC
29b97a2a0aSMarian Balakowicz #include <common.h>
30b97a2a0aSMarian Balakowicz #include <watchdog.h>
315ad03eb3SMarian Balakowicz 
325ad03eb3SMarian Balakowicz #ifdef CONFIG_SHOW_BOOT_PROGRESS
335ad03eb3SMarian Balakowicz #include <status_led.h>
345ad03eb3SMarian Balakowicz #endif
355ad03eb3SMarian Balakowicz 
365ad03eb3SMarian Balakowicz #ifdef CONFIG_HAS_DATAFLASH
375ad03eb3SMarian Balakowicz #include <dataflash.h>
385ad03eb3SMarian Balakowicz #endif
395ad03eb3SMarian Balakowicz 
40ceaed2b1SMarian Balakowicz #ifdef CONFIG_LOGBUFFER
41ceaed2b1SMarian Balakowicz #include <logbuff.h>
42ceaed2b1SMarian Balakowicz #endif
43ceaed2b1SMarian Balakowicz 
442242f536SMarian Balakowicz #if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE)
452242f536SMarian Balakowicz #include <rtc.h>
462242f536SMarian Balakowicz #endif
472242f536SMarian Balakowicz 
48fff888a1SMarian Balakowicz #if defined(CONFIG_FIT)
49fff888a1SMarian Balakowicz #include <fdt.h>
50fff888a1SMarian Balakowicz #include <libfdt.h>
51fff888a1SMarian Balakowicz #include <fdt_support.h>
52fff888a1SMarian Balakowicz #endif
53fff888a1SMarian Balakowicz 
54b6b0fe64SMarian Balakowicz #ifdef CONFIG_CMD_BDI
55b6b0fe64SMarian Balakowicz extern int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
56b6b0fe64SMarian Balakowicz #endif
57b6b0fe64SMarian Balakowicz 
58b6b0fe64SMarian Balakowicz DECLARE_GLOBAL_DATA_PTR;
598a5ea3e6SMarian Balakowicz 
608a5ea3e6SMarian Balakowicz static image_header_t* image_get_ramdisk (cmd_tbl_t *cmdtp, int flag,
618a5ea3e6SMarian Balakowicz 		int argc, char *argv[],
628a5ea3e6SMarian Balakowicz 		ulong rd_addr, uint8_t arch, int verify);
63b97a2a0aSMarian Balakowicz #else
64b97a2a0aSMarian Balakowicz #include "mkimage.h"
65b6b0fe64SMarian Balakowicz #endif /* USE_HOSTCC*/
66b97a2a0aSMarian Balakowicz 
67b97a2a0aSMarian Balakowicz #include <image.h>
68b97a2a0aSMarian Balakowicz 
69b97a2a0aSMarian Balakowicz unsigned long crc32 (unsigned long, const unsigned char *, unsigned int);
70b97a2a0aSMarian Balakowicz 
71b97a2a0aSMarian Balakowicz int image_check_hcrc (image_header_t *hdr)
72b97a2a0aSMarian Balakowicz {
73b97a2a0aSMarian Balakowicz 	ulong hcrc;
74b97a2a0aSMarian Balakowicz 	ulong len = image_get_header_size ();
75b97a2a0aSMarian Balakowicz 	image_header_t header;
76b97a2a0aSMarian Balakowicz 
77b97a2a0aSMarian Balakowicz 	/* Copy header so we can blank CRC field for re-calculation */
78b97a2a0aSMarian Balakowicz 	memmove (&header, (char *)hdr, image_get_header_size ());
79b97a2a0aSMarian Balakowicz 	image_set_hcrc (&header, 0);
80b97a2a0aSMarian Balakowicz 
81b97a2a0aSMarian Balakowicz 	hcrc = crc32 (0, (unsigned char *)&header, len);
82b97a2a0aSMarian Balakowicz 
83b97a2a0aSMarian Balakowicz 	return (hcrc == image_get_hcrc (hdr));
84b97a2a0aSMarian Balakowicz }
85b97a2a0aSMarian Balakowicz 
86b97a2a0aSMarian Balakowicz int image_check_dcrc (image_header_t *hdr)
87b97a2a0aSMarian Balakowicz {
88b97a2a0aSMarian Balakowicz 	ulong data = image_get_data (hdr);
89b97a2a0aSMarian Balakowicz 	ulong len = image_get_data_size (hdr);
90b97a2a0aSMarian Balakowicz 	ulong dcrc = crc32 (0, (unsigned char *)data, len);
91b97a2a0aSMarian Balakowicz 
92b97a2a0aSMarian Balakowicz 	return (dcrc == image_get_dcrc (hdr));
93b97a2a0aSMarian Balakowicz }
94b97a2a0aSMarian Balakowicz 
95af13cdbcSMarian Balakowicz #ifndef USE_HOSTCC
96b97a2a0aSMarian Balakowicz int image_check_dcrc_wd (image_header_t *hdr, ulong chunksz)
97b97a2a0aSMarian Balakowicz {
98b97a2a0aSMarian Balakowicz 	ulong dcrc = 0;
99b97a2a0aSMarian Balakowicz 	ulong len = image_get_data_size (hdr);
100b97a2a0aSMarian Balakowicz 	ulong data = image_get_data (hdr);
101b97a2a0aSMarian Balakowicz 
102b97a2a0aSMarian Balakowicz #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
103b97a2a0aSMarian Balakowicz 	ulong cdata = data;
104b97a2a0aSMarian Balakowicz 	ulong edata = cdata + len;
105b97a2a0aSMarian Balakowicz 
106b97a2a0aSMarian Balakowicz 	while (cdata < edata) {
107b97a2a0aSMarian Balakowicz 		ulong chunk = edata - cdata;
108b97a2a0aSMarian Balakowicz 
109b97a2a0aSMarian Balakowicz 		if (chunk > chunksz)
110b97a2a0aSMarian Balakowicz 			chunk = chunksz;
111b97a2a0aSMarian Balakowicz 		dcrc = crc32 (dcrc, (unsigned char *)cdata, chunk);
112b97a2a0aSMarian Balakowicz 		cdata += chunk;
113b97a2a0aSMarian Balakowicz 
114b97a2a0aSMarian Balakowicz 		WATCHDOG_RESET ();
115b97a2a0aSMarian Balakowicz 	}
116b97a2a0aSMarian Balakowicz #else
117b97a2a0aSMarian Balakowicz 	dcrc = crc32 (0, (unsigned char *)data, len);
118b97a2a0aSMarian Balakowicz #endif
119b97a2a0aSMarian Balakowicz 
120b97a2a0aSMarian Balakowicz 	return (dcrc == image_get_dcrc (hdr));
121b97a2a0aSMarian Balakowicz }
122b97a2a0aSMarian Balakowicz 
123b97a2a0aSMarian Balakowicz int getenv_verify (void)
124b97a2a0aSMarian Balakowicz {
125b97a2a0aSMarian Balakowicz 	char *s = getenv ("verify");
126b97a2a0aSMarian Balakowicz 	return (s && (*s == 'n')) ? 0 : 1;
127b97a2a0aSMarian Balakowicz }
128af13cdbcSMarian Balakowicz 
129af13cdbcSMarian Balakowicz void memmove_wd (void *to, void *from, size_t len, ulong chunksz)
130af13cdbcSMarian Balakowicz {
131af13cdbcSMarian Balakowicz #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
132af13cdbcSMarian Balakowicz 	while (len > 0) {
133af13cdbcSMarian Balakowicz 		size_t tail = (len > chunksz) ? chunksz : len;
134af13cdbcSMarian Balakowicz 		WATCHDOG_RESET ();
135af13cdbcSMarian Balakowicz 		memmove (to, from, tail);
136af13cdbcSMarian Balakowicz 		to += tail;
137af13cdbcSMarian Balakowicz 		from += tail;
138af13cdbcSMarian Balakowicz 		len -= tail;
139af13cdbcSMarian Balakowicz 	}
140af13cdbcSMarian Balakowicz #else	/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
141af13cdbcSMarian Balakowicz 	memmove (to, from, len);
142af13cdbcSMarian Balakowicz #endif	/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
143af13cdbcSMarian Balakowicz }
144af13cdbcSMarian Balakowicz #endif /* USE_HOSTCC */
145f13e7b2eSMarian Balakowicz 
146f13e7b2eSMarian Balakowicz /**
147f13e7b2eSMarian Balakowicz  * image_multi_count - get component (sub-image) count
148f13e7b2eSMarian Balakowicz  * @hdr: pointer to the header of the multi component image
149f13e7b2eSMarian Balakowicz  *
150f13e7b2eSMarian Balakowicz  * image_multi_count() returns number of components in a multi
151f13e7b2eSMarian Balakowicz  * component image.
152f13e7b2eSMarian Balakowicz  *
153f13e7b2eSMarian Balakowicz  * Note: no checking of the image type is done, caller must pass
154f13e7b2eSMarian Balakowicz  * a valid multi component image.
155f13e7b2eSMarian Balakowicz  *
156f13e7b2eSMarian Balakowicz  * returns:
157f13e7b2eSMarian Balakowicz  *     number of components
158f13e7b2eSMarian Balakowicz  */
159f13e7b2eSMarian Balakowicz ulong image_multi_count (image_header_t *hdr)
160f13e7b2eSMarian Balakowicz {
161f13e7b2eSMarian Balakowicz 	ulong i, count = 0;
162f13e7b2eSMarian Balakowicz 	ulong *size;
163f13e7b2eSMarian Balakowicz 
164f13e7b2eSMarian Balakowicz 	/* get start of the image payload, which in case of multi
165f13e7b2eSMarian Balakowicz 	 * component images that points to a table of component sizes */
166f13e7b2eSMarian Balakowicz 	size = (ulong *)image_get_data (hdr);
167f13e7b2eSMarian Balakowicz 
168f13e7b2eSMarian Balakowicz 	/* count non empty slots */
169f13e7b2eSMarian Balakowicz 	for (i = 0; size[i]; ++i)
170f13e7b2eSMarian Balakowicz 		count++;
171f13e7b2eSMarian Balakowicz 
172f13e7b2eSMarian Balakowicz 	return count;
173f13e7b2eSMarian Balakowicz }
174f13e7b2eSMarian Balakowicz 
175f13e7b2eSMarian Balakowicz /**
176f13e7b2eSMarian Balakowicz  * image_multi_getimg - get component data address and size
177f13e7b2eSMarian Balakowicz  * @hdr: pointer to the header of the multi component image
178f13e7b2eSMarian Balakowicz  * @idx: index of the requested component
179f13e7b2eSMarian Balakowicz  * @data: pointer to a ulong variable, will hold component data address
180f13e7b2eSMarian Balakowicz  * @len: pointer to a ulong variable, will hold component size
181f13e7b2eSMarian Balakowicz  *
182f13e7b2eSMarian Balakowicz  * image_multi_getimg() returns size and data address for the requested
183f13e7b2eSMarian Balakowicz  * component in a multi component image.
184f13e7b2eSMarian Balakowicz  *
185f13e7b2eSMarian Balakowicz  * Note: no checking of the image type is done, caller must pass
186f13e7b2eSMarian Balakowicz  * a valid multi component image.
187f13e7b2eSMarian Balakowicz  *
188f13e7b2eSMarian Balakowicz  * returns:
189f13e7b2eSMarian Balakowicz  *     data address and size of the component, if idx is valid
190f13e7b2eSMarian Balakowicz  *     0 in data and len, if idx is out of range
191f13e7b2eSMarian Balakowicz  */
192f13e7b2eSMarian Balakowicz void image_multi_getimg (image_header_t *hdr, ulong idx,
193f13e7b2eSMarian Balakowicz 			ulong *data, ulong *len)
194f13e7b2eSMarian Balakowicz {
195f13e7b2eSMarian Balakowicz 	int i;
196f13e7b2eSMarian Balakowicz 	ulong *size;
197f13e7b2eSMarian Balakowicz 	ulong offset, tail, count, img_data;
198f13e7b2eSMarian Balakowicz 
199f13e7b2eSMarian Balakowicz 	/* get number of component */
200f13e7b2eSMarian Balakowicz 	count = image_multi_count (hdr);
201f13e7b2eSMarian Balakowicz 
202f13e7b2eSMarian Balakowicz 	/* get start of the image payload, which in case of multi
203f13e7b2eSMarian Balakowicz 	 * component images that points to a table of component sizes */
204f13e7b2eSMarian Balakowicz 	size = (ulong *)image_get_data (hdr);
205f13e7b2eSMarian Balakowicz 
206f13e7b2eSMarian Balakowicz 	/* get address of the proper component data start, which means
207f13e7b2eSMarian Balakowicz 	 * skipping sizes table (add 1 for last, null entry) */
208f13e7b2eSMarian Balakowicz 	img_data = image_get_data (hdr) + (count + 1) * sizeof (ulong);
209f13e7b2eSMarian Balakowicz 
210f13e7b2eSMarian Balakowicz 	if (idx < count) {
211f13e7b2eSMarian Balakowicz 		*len = size[idx];
212f13e7b2eSMarian Balakowicz 		offset = 0;
213f13e7b2eSMarian Balakowicz 		tail = 0;
214f13e7b2eSMarian Balakowicz 
215f13e7b2eSMarian Balakowicz 		/* go over all indices preceding requested component idx */
216f13e7b2eSMarian Balakowicz 		for (i = 0; i < idx; i++) {
217f13e7b2eSMarian Balakowicz 			/* add up i-th component size */
218f13e7b2eSMarian Balakowicz 			offset += size[i];
219f13e7b2eSMarian Balakowicz 
220f13e7b2eSMarian Balakowicz 			/* add up alignment for i-th component */
221f13e7b2eSMarian Balakowicz 			tail += (4 - size[i] % 4);
222f13e7b2eSMarian Balakowicz 		}
223f13e7b2eSMarian Balakowicz 
224f13e7b2eSMarian Balakowicz 		/* calculate idx-th component data address */
225f13e7b2eSMarian Balakowicz 		*data = img_data + offset + tail;
226f13e7b2eSMarian Balakowicz 	} else {
227f13e7b2eSMarian Balakowicz 		*len = 0;
228f13e7b2eSMarian Balakowicz 		*data = 0;
229f13e7b2eSMarian Balakowicz 	}
230f13e7b2eSMarian Balakowicz }
23142b73e8eSMarian Balakowicz 
23242b73e8eSMarian Balakowicz #ifndef USE_HOSTCC
23342b73e8eSMarian Balakowicz const char* image_get_os_name (uint8_t os)
23442b73e8eSMarian Balakowicz {
23542b73e8eSMarian Balakowicz 	const char *name;
23642b73e8eSMarian Balakowicz 
23742b73e8eSMarian Balakowicz 	switch (os) {
23842b73e8eSMarian Balakowicz 	case IH_OS_INVALID:	name = "Invalid OS";		break;
23942b73e8eSMarian Balakowicz 	case IH_OS_NETBSD:	name = "NetBSD";		break;
24042b73e8eSMarian Balakowicz 	case IH_OS_LINUX:	name = "Linux";			break;
24142b73e8eSMarian Balakowicz 	case IH_OS_VXWORKS:	name = "VxWorks";		break;
24242b73e8eSMarian Balakowicz 	case IH_OS_QNX:		name = "QNX";			break;
24342b73e8eSMarian Balakowicz 	case IH_OS_U_BOOT:	name = "U-Boot";		break;
24442b73e8eSMarian Balakowicz 	case IH_OS_RTEMS:	name = "RTEMS";			break;
24542b73e8eSMarian Balakowicz #ifdef CONFIG_ARTOS
24642b73e8eSMarian Balakowicz 	case IH_OS_ARTOS:	name = "ARTOS";			break;
24742b73e8eSMarian Balakowicz #endif
24842b73e8eSMarian Balakowicz #ifdef CONFIG_LYNXKDI
24942b73e8eSMarian Balakowicz 	case IH_OS_LYNXOS:	name = "LynxOS";		break;
25042b73e8eSMarian Balakowicz #endif
25142b73e8eSMarian Balakowicz 	default:		name = "Unknown OS";		break;
25242b73e8eSMarian Balakowicz 	}
25342b73e8eSMarian Balakowicz 
25442b73e8eSMarian Balakowicz 	return name;
25542b73e8eSMarian Balakowicz }
25642b73e8eSMarian Balakowicz 
25742b73e8eSMarian Balakowicz const char* image_get_arch_name (uint8_t arch)
25842b73e8eSMarian Balakowicz {
25942b73e8eSMarian Balakowicz 	const char *name;
26042b73e8eSMarian Balakowicz 
26142b73e8eSMarian Balakowicz 	switch (arch) {
26242b73e8eSMarian Balakowicz 	case IH_ARCH_INVALID:	name = "Invalid Architecture";	break;
26342b73e8eSMarian Balakowicz 	case IH_ARCH_ALPHA:	name = "Alpha";			break;
26442b73e8eSMarian Balakowicz 	case IH_ARCH_ARM:	name = "ARM";			break;
26542b73e8eSMarian Balakowicz 	case IH_ARCH_AVR32:	name = "AVR32";			break;
26642b73e8eSMarian Balakowicz 	case IH_ARCH_BLACKFIN:	name = "Blackfin";		break;
26742b73e8eSMarian Balakowicz 	case IH_ARCH_I386:	name = "Intel x86";		break;
26842b73e8eSMarian Balakowicz 	case IH_ARCH_IA64:	name = "IA64";			break;
26942b73e8eSMarian Balakowicz 	case IH_ARCH_M68K:	name = "M68K"; 			break;
27042b73e8eSMarian Balakowicz 	case IH_ARCH_MICROBLAZE:name = "Microblaze"; 		break;
27142b73e8eSMarian Balakowicz 	case IH_ARCH_MIPS64:	name = "MIPS 64 Bit";		break;
27242b73e8eSMarian Balakowicz 	case IH_ARCH_MIPS:	name = "MIPS";			break;
27342b73e8eSMarian Balakowicz 	case IH_ARCH_NIOS2:	name = "Nios-II";		break;
27442b73e8eSMarian Balakowicz 	case IH_ARCH_NIOS:	name = "Nios";			break;
27542b73e8eSMarian Balakowicz 	case IH_ARCH_PPC:	name = "PowerPC";		break;
27642b73e8eSMarian Balakowicz 	case IH_ARCH_S390:	name = "IBM S390";		break;
27742b73e8eSMarian Balakowicz 	case IH_ARCH_SH:	name = "SuperH";		break;
27842b73e8eSMarian Balakowicz 	case IH_ARCH_SPARC64:	name = "SPARC 64 Bit";		break;
27942b73e8eSMarian Balakowicz 	case IH_ARCH_SPARC:	name = "SPARC";			break;
28042b73e8eSMarian Balakowicz 	default:		name = "Unknown Architecture";	break;
28142b73e8eSMarian Balakowicz 	}
28242b73e8eSMarian Balakowicz 
28342b73e8eSMarian Balakowicz 	return name;
28442b73e8eSMarian Balakowicz }
28542b73e8eSMarian Balakowicz 
28642b73e8eSMarian Balakowicz const char* image_get_type_name (uint8_t type)
28742b73e8eSMarian Balakowicz {
28842b73e8eSMarian Balakowicz 	const char *name;
28942b73e8eSMarian Balakowicz 
29042b73e8eSMarian Balakowicz 	switch (type) {
29142b73e8eSMarian Balakowicz 	case IH_TYPE_INVALID:	name = "Invalid Image";		break;
29242b73e8eSMarian Balakowicz 	case IH_TYPE_STANDALONE:name = "Standalone Program";	break;
29342b73e8eSMarian Balakowicz 	case IH_TYPE_KERNEL:	name = "Kernel Image";		break;
29442b73e8eSMarian Balakowicz 	case IH_TYPE_RAMDISK:	name = "RAMDisk Image";		break;
29542b73e8eSMarian Balakowicz 	case IH_TYPE_MULTI:	name = "Multi-File Image";	break;
29642b73e8eSMarian Balakowicz 	case IH_TYPE_FIRMWARE:	name = "Firmware";		break;
29742b73e8eSMarian Balakowicz 	case IH_TYPE_SCRIPT:	name = "Script";		break;
29842b73e8eSMarian Balakowicz 	case IH_TYPE_FLATDT:	name = "Flat Device Tree";	break;
29942b73e8eSMarian Balakowicz 	default:		name = "Unknown Image";		break;
30042b73e8eSMarian Balakowicz 	}
30142b73e8eSMarian Balakowicz 
30242b73e8eSMarian Balakowicz 	return name;
30342b73e8eSMarian Balakowicz }
30442b73e8eSMarian Balakowicz 
30542b73e8eSMarian Balakowicz const char* image_get_comp_name (uint8_t comp)
30642b73e8eSMarian Balakowicz {
30742b73e8eSMarian Balakowicz 	const char *name;
30842b73e8eSMarian Balakowicz 
30942b73e8eSMarian Balakowicz 	switch (comp) {
31042b73e8eSMarian Balakowicz 	case IH_COMP_NONE:	name = "uncompressed";		break;
31142b73e8eSMarian Balakowicz 	case IH_COMP_GZIP:	name = "gzip compressed";	break;
31242b73e8eSMarian Balakowicz 	case IH_COMP_BZIP2:	name = "bzip2 compressed";	break;
31342b73e8eSMarian Balakowicz 	default:		name = "unknown compression";	break;
31442b73e8eSMarian Balakowicz 	}
31542b73e8eSMarian Balakowicz 
31642b73e8eSMarian Balakowicz 	return name;
31742b73e8eSMarian Balakowicz }
3185ad03eb3SMarian Balakowicz 
3192242f536SMarian Balakowicz static void image_print_type (image_header_t *hdr)
3202242f536SMarian Balakowicz {
3212242f536SMarian Balakowicz 	const char *os, *arch, *type, *comp;
3222242f536SMarian Balakowicz 
3232242f536SMarian Balakowicz 	os = image_get_os_name (image_get_os (hdr));
3242242f536SMarian Balakowicz 	arch = image_get_arch_name (image_get_arch (hdr));
3252242f536SMarian Balakowicz 	type = image_get_type_name (image_get_type (hdr));
3262242f536SMarian Balakowicz 	comp = image_get_comp_name (image_get_comp (hdr));
3272242f536SMarian Balakowicz 
3282242f536SMarian Balakowicz 	printf ("%s %s %s (%s)", arch, os, type, comp);
3292242f536SMarian Balakowicz }
3302242f536SMarian Balakowicz 
3312242f536SMarian Balakowicz void image_print_contents (image_header_t *hdr)
3322242f536SMarian Balakowicz {
3332242f536SMarian Balakowicz #if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE)
3342242f536SMarian Balakowicz 	time_t timestamp = (time_t)image_get_time (hdr);
3352242f536SMarian Balakowicz 	struct rtc_time tm;
3362242f536SMarian Balakowicz #endif
3372242f536SMarian Balakowicz 
3382242f536SMarian Balakowicz 	printf ("   Image Name:   %.*s\n", IH_NMLEN, image_get_name (hdr));
3392242f536SMarian Balakowicz 
3402242f536SMarian Balakowicz #if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE)
3412242f536SMarian Balakowicz 	to_tm (timestamp, &tm);
3422242f536SMarian Balakowicz 	printf ("   Created:      %4d-%02d-%02d  %2d:%02d:%02d UTC\n",
3432242f536SMarian Balakowicz 		tm.tm_year, tm.tm_mon, tm.tm_mday,
3442242f536SMarian Balakowicz 		tm.tm_hour, tm.tm_min, tm.tm_sec);
3452242f536SMarian Balakowicz #endif
3462242f536SMarian Balakowicz 	puts ("   Image Type:   ");
3472242f536SMarian Balakowicz 	image_print_type (hdr);
3482242f536SMarian Balakowicz 
3492242f536SMarian Balakowicz 	printf ("\n   Data Size:    %d Bytes = ", image_get_data_size (hdr));
3502242f536SMarian Balakowicz 	print_size (image_get_data_size (hdr), "\n");
3512242f536SMarian Balakowicz 	printf ("   Load Address: %08x\n"
3522242f536SMarian Balakowicz 		"   Entry Point:  %08x\n",
3532242f536SMarian Balakowicz 		 image_get_load (hdr), image_get_ep (hdr));
3542242f536SMarian Balakowicz 
3552242f536SMarian Balakowicz 	if (image_check_type (hdr, IH_TYPE_MULTI)) {
3562242f536SMarian Balakowicz 		int i;
3572242f536SMarian Balakowicz 		ulong data, len;
3582242f536SMarian Balakowicz 		ulong count = image_multi_count (hdr);
3592242f536SMarian Balakowicz 
3602242f536SMarian Balakowicz 		puts ("   Contents:\n");
3612242f536SMarian Balakowicz 		for (i = 0; i < count; i++) {
3622242f536SMarian Balakowicz 			image_multi_getimg (hdr, i, &data, &len);
3632242f536SMarian Balakowicz 			printf ("   Image %d: %8ld Bytes = ", i, len);
3642242f536SMarian Balakowicz 			print_size (len, "\n");
3652242f536SMarian Balakowicz 		}
3662242f536SMarian Balakowicz 	}
3672242f536SMarian Balakowicz }
3682242f536SMarian Balakowicz 
3695ad03eb3SMarian Balakowicz /**
370fff888a1SMarian Balakowicz  * gen_image_get_format - get image format type
371fff888a1SMarian Balakowicz  * @img_addr: image start address
372fff888a1SMarian Balakowicz  *
373fff888a1SMarian Balakowicz  * gen_image_get_format() checks whether provided address points to a valid
374fff888a1SMarian Balakowicz  * legacy or FIT image.
375fff888a1SMarian Balakowicz  *
3764efbe9dbSMarian Balakowicz  * New uImage format and FDT blob are based on a libfdt. FDT blob
3774efbe9dbSMarian Balakowicz  * may be passed directly or embedded in a FIT image. In both situations
3784efbe9dbSMarian Balakowicz  * gen_image_get_format() must be able to dectect libfdt header.
3794efbe9dbSMarian Balakowicz  *
380fff888a1SMarian Balakowicz  * returns:
381fff888a1SMarian Balakowicz  *     image format type or IMAGE_FORMAT_INVALID if no image is present
382fff888a1SMarian Balakowicz  */
383fff888a1SMarian Balakowicz int gen_image_get_format (void *img_addr)
384fff888a1SMarian Balakowicz {
385fff888a1SMarian Balakowicz 	ulong		format = IMAGE_FORMAT_INVALID;
386fff888a1SMarian Balakowicz 	image_header_t	*hdr;
3874efbe9dbSMarian Balakowicz #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT)
388fff888a1SMarian Balakowicz 	char		*fit_hdr;
389fff888a1SMarian Balakowicz #endif
390fff888a1SMarian Balakowicz 
391fff888a1SMarian Balakowicz 	hdr = (image_header_t *)img_addr;
392fff888a1SMarian Balakowicz 	if (image_check_magic(hdr))
393fff888a1SMarian Balakowicz 		format = IMAGE_FORMAT_LEGACY;
3944efbe9dbSMarian Balakowicz #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT)
395fff888a1SMarian Balakowicz 	else {
396fff888a1SMarian Balakowicz 		fit_hdr = (char *)img_addr;
397fff888a1SMarian Balakowicz 		if (fdt_check_header (fit_hdr) == 0)
398fff888a1SMarian Balakowicz 			format = IMAGE_FORMAT_FIT;
399fff888a1SMarian Balakowicz 	}
400fff888a1SMarian Balakowicz #endif
401fff888a1SMarian Balakowicz 
402fff888a1SMarian Balakowicz 	return format;
403fff888a1SMarian Balakowicz }
404fff888a1SMarian Balakowicz 
405fff888a1SMarian Balakowicz /**
406fff888a1SMarian Balakowicz  * gen_get_image - get image from special storage (if necessary)
407fff888a1SMarian Balakowicz  * @img_addr: image start address
408fff888a1SMarian Balakowicz  *
409fff888a1SMarian Balakowicz  * gen_get_image() checks if provided image start adddress is located
410fff888a1SMarian Balakowicz  * in a dataflash storage. If so, image is moved to a system RAM memory.
411fff888a1SMarian Balakowicz  *
412fff888a1SMarian Balakowicz  * returns:
413fff888a1SMarian Balakowicz  *     image start address after possible relocation from special storage
414fff888a1SMarian Balakowicz  */
415fff888a1SMarian Balakowicz ulong gen_get_image (ulong img_addr)
416fff888a1SMarian Balakowicz {
4176f0f9dfcSMarian Balakowicz 	ulong ram_addr = img_addr;
418fff888a1SMarian Balakowicz 
4196f0f9dfcSMarian Balakowicz #ifdef CONFIG_HAS_DATAFLASH
4206f0f9dfcSMarian Balakowicz 	ulong h_size, d_size;
4216f0f9dfcSMarian Balakowicz 
4226f0f9dfcSMarian Balakowicz 	if (addr_dataflash (img_addr)){
4236f0f9dfcSMarian Balakowicz 		/* ger RAM address */
4246f0f9dfcSMarian Balakowicz 		ram_addr = CFG_LOAD_ADDR;
4256f0f9dfcSMarian Balakowicz 
4266f0f9dfcSMarian Balakowicz 		/* get header size */
427fff888a1SMarian Balakowicz 		h_size = image_get_header_size ();
428fff888a1SMarian Balakowicz #if defined(CONFIG_FIT)
429fff888a1SMarian Balakowicz 		if (sizeof(struct fdt_header) > h_size)
430fff888a1SMarian Balakowicz 			h_size = sizeof(struct fdt_header);
431fff888a1SMarian Balakowicz #endif
432fff888a1SMarian Balakowicz 
4336f0f9dfcSMarian Balakowicz 		/* read in header */
434fff888a1SMarian Balakowicz 		debug ("   Reading image header from dataflash address "
435fff888a1SMarian Balakowicz 			"%08lx to RAM address %08lx\n", img_addr, ram_addr);
4366f0f9dfcSMarian Balakowicz 
437fff888a1SMarian Balakowicz 		read_dataflash (img_addr, h_size, (char *)ram_addr);
438fff888a1SMarian Balakowicz 
4396f0f9dfcSMarian Balakowicz 		/* get data size */
440fff888a1SMarian Balakowicz 		switch (gen_image_get_format ((void *)ram_addr)) {
441fff888a1SMarian Balakowicz 		case IMAGE_FORMAT_LEGACY:
442fff888a1SMarian Balakowicz 			d_size = image_get_data_size ((image_header_t *)ram_addr);
443fff888a1SMarian Balakowicz 			debug ("   Legacy format image found at 0x%08lx, size 0x%08lx\n",
444fff888a1SMarian Balakowicz 					ram_addr, d_size);
445fff888a1SMarian Balakowicz 			break;
446fff888a1SMarian Balakowicz #if defined(CONFIG_FIT)
447fff888a1SMarian Balakowicz 		case IMAGE_FORMAT_FIT:
448fff888a1SMarian Balakowicz 			d_size = fdt_totalsize((void *)ram_addr) - h_size;
449fff888a1SMarian Balakowicz 			debug ("   FIT/FDT format image found at 0x%08lx, size 0x%08lx\n",
450fff888a1SMarian Balakowicz 					ram_addr, d_size);
451fff888a1SMarian Balakowicz 			break;
452fff888a1SMarian Balakowicz #endif
453fff888a1SMarian Balakowicz 		default:
454fff888a1SMarian Balakowicz 			printf ("   No valid image found at 0x%08lx\n", img_addr);
455fff888a1SMarian Balakowicz 			return ram_addr;
456fff888a1SMarian Balakowicz 		}
457fff888a1SMarian Balakowicz 
4586f0f9dfcSMarian Balakowicz 		/* read in image data */
459fff888a1SMarian Balakowicz 		debug ("   Reading image remaining data from dataflash address "
460fff888a1SMarian Balakowicz 			"%08lx to RAM address %08lx\n", img_addr + h_size,
461fff888a1SMarian Balakowicz 			ram_addr + h_size);
462fff888a1SMarian Balakowicz 
463fff888a1SMarian Balakowicz 		read_dataflash (img_addr + h_size, d_size,
464fff888a1SMarian Balakowicz 				(char *)(ram_addr + h_size));
4656f0f9dfcSMarian Balakowicz 
466fff888a1SMarian Balakowicz 	}
4676f0f9dfcSMarian Balakowicz #endif /* CONFIG_HAS_DATAFLASH */
468fff888a1SMarian Balakowicz 
469fff888a1SMarian Balakowicz 	return ram_addr;
470fff888a1SMarian Balakowicz }
471fff888a1SMarian Balakowicz 
472fff888a1SMarian Balakowicz /**
4735ad03eb3SMarian Balakowicz  * image_get_ramdisk - get and verify ramdisk image
4745ad03eb3SMarian Balakowicz  * @cmdtp: command table pointer
4755ad03eb3SMarian Balakowicz  * @flag: command flag
4765ad03eb3SMarian Balakowicz  * @argc: command argument count
4775ad03eb3SMarian Balakowicz  * @argv: command argument list
4785ad03eb3SMarian Balakowicz  * @rd_addr: ramdisk image start address
4795ad03eb3SMarian Balakowicz  * @arch: expected ramdisk architecture
4805ad03eb3SMarian Balakowicz  * @verify: checksum verification flag
4815ad03eb3SMarian Balakowicz  *
4825ad03eb3SMarian Balakowicz  * image_get_ramdisk() returns a pointer to the verified ramdisk image
4835ad03eb3SMarian Balakowicz  * header. Routine receives image start address and expected architecture
4845ad03eb3SMarian Balakowicz  * flag. Verification done covers data and header integrity and os/type/arch
4855ad03eb3SMarian Balakowicz  * fields checking.
4865ad03eb3SMarian Balakowicz  *
4875ad03eb3SMarian Balakowicz  * If dataflash support is enabled routine checks for dataflash addresses
4885ad03eb3SMarian Balakowicz  * and handles required dataflash reads.
4895ad03eb3SMarian Balakowicz  *
4905ad03eb3SMarian Balakowicz  * returns:
4915ad03eb3SMarian Balakowicz  *     pointer to a ramdisk image header, if image was found and valid
492*274cea2bSKumar Gala  *     otherwise, return NULL
4935ad03eb3SMarian Balakowicz  */
4948a5ea3e6SMarian Balakowicz static image_header_t* image_get_ramdisk (cmd_tbl_t *cmdtp, int flag,
4955ad03eb3SMarian Balakowicz 		int argc, char *argv[],
4965ad03eb3SMarian Balakowicz 		ulong rd_addr, uint8_t arch, int verify)
4975ad03eb3SMarian Balakowicz {
4985ad03eb3SMarian Balakowicz 	image_header_t *rd_hdr;
4995ad03eb3SMarian Balakowicz 
5005ad03eb3SMarian Balakowicz 	show_boot_progress (9);
5015ad03eb3SMarian Balakowicz 	rd_hdr = (image_header_t *)rd_addr;
5025ad03eb3SMarian Balakowicz 
5035ad03eb3SMarian Balakowicz 	if (!image_check_magic (rd_hdr)) {
5045ad03eb3SMarian Balakowicz 		puts ("Bad Magic Number\n");
5055ad03eb3SMarian Balakowicz 		show_boot_progress (-10);
506*274cea2bSKumar Gala 		return NULL;
5075ad03eb3SMarian Balakowicz 	}
5085ad03eb3SMarian Balakowicz 
5095ad03eb3SMarian Balakowicz 	if (!image_check_hcrc (rd_hdr)) {
5105ad03eb3SMarian Balakowicz 		puts ("Bad Header Checksum\n");
5115ad03eb3SMarian Balakowicz 		show_boot_progress (-11);
512*274cea2bSKumar Gala 		return NULL;
5135ad03eb3SMarian Balakowicz 	}
5145ad03eb3SMarian Balakowicz 
5155ad03eb3SMarian Balakowicz 	show_boot_progress (10);
5162242f536SMarian Balakowicz 	image_print_contents (rd_hdr);
5175ad03eb3SMarian Balakowicz 
5185ad03eb3SMarian Balakowicz 	if (verify) {
5195ad03eb3SMarian Balakowicz 		puts("   Verifying Checksum ... ");
5205ad03eb3SMarian Balakowicz 		if (!image_check_dcrc_wd (rd_hdr, CHUNKSZ)) {
5215ad03eb3SMarian Balakowicz 			puts ("Bad Data CRC\n");
5225ad03eb3SMarian Balakowicz 			show_boot_progress (-12);
523*274cea2bSKumar Gala 			return NULL;
5245ad03eb3SMarian Balakowicz 		}
5255ad03eb3SMarian Balakowicz 		puts("OK\n");
5265ad03eb3SMarian Balakowicz 	}
5275ad03eb3SMarian Balakowicz 
5285ad03eb3SMarian Balakowicz 	show_boot_progress (11);
5295ad03eb3SMarian Balakowicz 
5305ad03eb3SMarian Balakowicz 	if (!image_check_os (rd_hdr, IH_OS_LINUX) ||
5315ad03eb3SMarian Balakowicz 	    !image_check_arch (rd_hdr, arch) ||
5325ad03eb3SMarian Balakowicz 	    !image_check_type (rd_hdr, IH_TYPE_RAMDISK)) {
5335ad03eb3SMarian Balakowicz 		printf ("No Linux %s Ramdisk Image\n",
5345ad03eb3SMarian Balakowicz 				image_get_arch_name(arch));
5355ad03eb3SMarian Balakowicz 		show_boot_progress (-13);
536*274cea2bSKumar Gala 		return NULL;
5375ad03eb3SMarian Balakowicz 	}
5385ad03eb3SMarian Balakowicz 
5395ad03eb3SMarian Balakowicz 	return rd_hdr;
5405ad03eb3SMarian Balakowicz }
5415ad03eb3SMarian Balakowicz 
5425ad03eb3SMarian Balakowicz /**
5435ad03eb3SMarian Balakowicz  * get_ramdisk - main ramdisk handling routine
5445ad03eb3SMarian Balakowicz  * @cmdtp: command table pointer
5455ad03eb3SMarian Balakowicz  * @flag: command flag
5465ad03eb3SMarian Balakowicz  * @argc: command argument count
5475ad03eb3SMarian Balakowicz  * @argv: command argument list
5488a5ea3e6SMarian Balakowicz  * @images: pointer to the bootm images structure
5495ad03eb3SMarian Balakowicz  * @arch: expected ramdisk architecture
5505ad03eb3SMarian Balakowicz  * @rd_start: pointer to a ulong variable, will hold ramdisk start address
5515ad03eb3SMarian Balakowicz  * @rd_end: pointer to a ulong variable, will hold ramdisk end
5525ad03eb3SMarian Balakowicz  *
5535ad03eb3SMarian Balakowicz  * get_ramdisk() is responsible for finding a valid ramdisk image.
5545ad03eb3SMarian Balakowicz  * Curently supported are the following ramdisk sources:
5555ad03eb3SMarian Balakowicz  *      - multicomponent kernel/ramdisk image,
5565ad03eb3SMarian Balakowicz  *      - commandline provided address of decicated ramdisk image.
5575ad03eb3SMarian Balakowicz  *
5585ad03eb3SMarian Balakowicz  * returns:
5595ad03eb3SMarian Balakowicz  *     rd_start and rd_end are set to ramdisk start/end addresses if
5605ad03eb3SMarian Balakowicz  *     ramdisk image is found and valid
5615ad03eb3SMarian Balakowicz  *     rd_start and rd_end are set to 0 if no ramdisk exists
562*274cea2bSKumar Gala  *     return 1 if ramdisk image is found but corrupted
5635ad03eb3SMarian Balakowicz  */
564*274cea2bSKumar Gala int get_ramdisk (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
5658a5ea3e6SMarian Balakowicz 		bootm_headers_t *images, uint8_t arch,
5665ad03eb3SMarian Balakowicz 		ulong *rd_start, ulong *rd_end)
5675ad03eb3SMarian Balakowicz {
568d5934ad7SMarian Balakowicz 	ulong rd_addr, rd_load;
5695ad03eb3SMarian Balakowicz 	ulong rd_data, rd_len;
5705ad03eb3SMarian Balakowicz 	image_header_t *rd_hdr;
571d5934ad7SMarian Balakowicz #if defined(CONFIG_FIT)
572d5934ad7SMarian Balakowicz 	void		*fit_hdr;
573d5934ad7SMarian Balakowicz 	const char	*fit_uname_config = NULL;
574d5934ad7SMarian Balakowicz 	const char	*fit_uname_ramdisk = NULL;
575d5934ad7SMarian Balakowicz 	ulong		default_addr;
576d5934ad7SMarian Balakowicz #endif
5775ad03eb3SMarian Balakowicz 
5785ad03eb3SMarian Balakowicz 	/*
5795ad03eb3SMarian Balakowicz 	 * Look for a '-' which indicates to ignore the
5805ad03eb3SMarian Balakowicz 	 * ramdisk argument
5815ad03eb3SMarian Balakowicz 	 */
582d5934ad7SMarian Balakowicz 	if ((argc >= 3) && (strcmp(argv[2], "-") ==  0)) {
5835ad03eb3SMarian Balakowicz 		debug ("## Skipping init Ramdisk\n");
5845ad03eb3SMarian Balakowicz 		rd_len = rd_data = 0;
585d5934ad7SMarian Balakowicz 	} else if (argc >= 3) {
586d5934ad7SMarian Balakowicz #if defined(CONFIG_FIT)
587d5934ad7SMarian Balakowicz 		/*
588d5934ad7SMarian Balakowicz 		 * If the init ramdisk comes from the FIT image and the FIT image
589d5934ad7SMarian Balakowicz 		 * address is omitted in the command line argument, try to use
590d5934ad7SMarian Balakowicz 		 * os FIT image address or default load address.
591d5934ad7SMarian Balakowicz 		 */
592d5934ad7SMarian Balakowicz 		if (images->fit_uname_os)
593d5934ad7SMarian Balakowicz 			default_addr = (ulong)images->fit_hdr_os;
594d5934ad7SMarian Balakowicz 		else
595d5934ad7SMarian Balakowicz 			default_addr = load_addr;
596d5934ad7SMarian Balakowicz 
597d5934ad7SMarian Balakowicz 		if (fit_parse_conf (argv[2], default_addr,
598d5934ad7SMarian Balakowicz 					&rd_addr, &fit_uname_config)) {
599d5934ad7SMarian Balakowicz 			debug ("*  ramdisk: config '%s' from image at 0x%08lx\n",
600d5934ad7SMarian Balakowicz 					fit_uname_config, rd_addr);
601d5934ad7SMarian Balakowicz 		} else if (fit_parse_subimage (argv[2], default_addr,
602d5934ad7SMarian Balakowicz 					&rd_addr, &fit_uname_ramdisk)) {
603d5934ad7SMarian Balakowicz 			debug ("*  ramdisk: subimage '%s' from image at 0x%08lx\n",
604d5934ad7SMarian Balakowicz 					fit_uname_ramdisk, rd_addr);
605d5934ad7SMarian Balakowicz 		} else
606d5934ad7SMarian Balakowicz #endif
607d5934ad7SMarian Balakowicz 		{
608d5934ad7SMarian Balakowicz 			rd_addr = simple_strtoul(argv[2], NULL, 16);
609d5934ad7SMarian Balakowicz 			debug ("*  ramdisk: cmdline image address = 0x%08lx\n",
610d5934ad7SMarian Balakowicz 					rd_addr);
611d5934ad7SMarian Balakowicz 		}
612d5934ad7SMarian Balakowicz 
613d5934ad7SMarian Balakowicz 		/* copy from dataflash if needed */
614d5934ad7SMarian Balakowicz 		printf ("## Loading init Ramdisk Image at %08lx ...\n",
615d5934ad7SMarian Balakowicz 				rd_addr);
616d5934ad7SMarian Balakowicz 		rd_addr = gen_get_image (rd_addr);
617d5934ad7SMarian Balakowicz 
6185ad03eb3SMarian Balakowicz 		/*
6195ad03eb3SMarian Balakowicz 		 * Check if there is an initrd image at the
6205ad03eb3SMarian Balakowicz 		 * address provided in the second bootm argument
621d5934ad7SMarian Balakowicz 		 * check image type, for FIT images get FIT node.
6225ad03eb3SMarian Balakowicz 		 */
623d5934ad7SMarian Balakowicz 		switch (gen_image_get_format ((void *)rd_addr)) {
624d5934ad7SMarian Balakowicz 		case IMAGE_FORMAT_LEGACY:
625d5934ad7SMarian Balakowicz 
626d5934ad7SMarian Balakowicz 			debug ("*  ramdisk: legacy format image\n");
6275ad03eb3SMarian Balakowicz 
6285ad03eb3SMarian Balakowicz 			rd_hdr = image_get_ramdisk (cmdtp, flag, argc, argv,
6298a5ea3e6SMarian Balakowicz 						rd_addr, arch, images->verify);
6305ad03eb3SMarian Balakowicz 
631*274cea2bSKumar Gala 			if (rd_hdr == NULL) {
632*274cea2bSKumar Gala 				*rd_start = 0;
633*274cea2bSKumar Gala 				*rd_end = 0;
634*274cea2bSKumar Gala 				return 1;
635*274cea2bSKumar Gala 			}
636*274cea2bSKumar Gala 
6375ad03eb3SMarian Balakowicz 			rd_data = image_get_data (rd_hdr);
6385ad03eb3SMarian Balakowicz 			rd_len = image_get_data_size (rd_hdr);
639d5934ad7SMarian Balakowicz 			rd_load = image_get_load (rd_hdr);
640d5934ad7SMarian Balakowicz 			break;
641d5934ad7SMarian Balakowicz #if defined(CONFIG_FIT)
642d5934ad7SMarian Balakowicz 		case IMAGE_FORMAT_FIT:
643d5934ad7SMarian Balakowicz 			fit_hdr = (void *)rd_addr;
644d5934ad7SMarian Balakowicz 			debug ("*  ramdisk: FIT format image\n");
645d5934ad7SMarian Balakowicz 			fit_unsupported_reset ("ramdisk");
646*274cea2bSKumar Gala 			return 1;
647d5934ad7SMarian Balakowicz #endif
648d5934ad7SMarian Balakowicz 		default:
649d5934ad7SMarian Balakowicz 			printf ("Wrong Image Format for %s command\n",
650d5934ad7SMarian Balakowicz 					cmdtp->name);
651d5934ad7SMarian Balakowicz 			rd_data = rd_len = 0;
652d5934ad7SMarian Balakowicz 		}
6535ad03eb3SMarian Balakowicz 
6545ad03eb3SMarian Balakowicz #if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
6555ad03eb3SMarian Balakowicz 		/*
656d5934ad7SMarian Balakowicz 		 * We need to copy the ramdisk to SRAM to let Linux boot
6575ad03eb3SMarian Balakowicz 		 */
658d5934ad7SMarian Balakowicz 		if (rd_data) {
659d5934ad7SMarian Balakowicz 			memmove ((void *)rd_load, (uchar *)rd_data, rd_len);
660d5934ad7SMarian Balakowicz 			rd_data = rd_load;
6615ad03eb3SMarian Balakowicz 		}
662d5934ad7SMarian Balakowicz #endif /* CONFIG_B2 || CONFIG_EVB4510 || CONFIG_ARMADILLO */
6635ad03eb3SMarian Balakowicz 
664d5934ad7SMarian Balakowicz 	} else if (images->legacy_hdr_valid &&
665d5934ad7SMarian Balakowicz 			image_check_type (images->legacy_hdr_os, IH_TYPE_MULTI)) {
6665ad03eb3SMarian Balakowicz 		/*
667d5934ad7SMarian Balakowicz 		 * Now check if we have a legacy mult-component image,
668d5934ad7SMarian Balakowicz 		 * get second entry data start address and len.
6695ad03eb3SMarian Balakowicz 		 */
6705ad03eb3SMarian Balakowicz 		show_boot_progress (13);
6715ad03eb3SMarian Balakowicz 		printf ("## Loading init Ramdisk from multi component "
672d5934ad7SMarian Balakowicz 				"Image at %08lx ...\n",
673d5934ad7SMarian Balakowicz 				(ulong)images->legacy_hdr_os);
674d5934ad7SMarian Balakowicz 
675d5934ad7SMarian Balakowicz 		image_multi_getimg (images->legacy_hdr_os, 1, &rd_data, &rd_len);
6765ad03eb3SMarian Balakowicz 	} else {
6775ad03eb3SMarian Balakowicz 		/*
6785ad03eb3SMarian Balakowicz 		 * no initrd image
6795ad03eb3SMarian Balakowicz 		 */
6805ad03eb3SMarian Balakowicz 		show_boot_progress (14);
6815ad03eb3SMarian Balakowicz 		rd_len = rd_data = 0;
6825ad03eb3SMarian Balakowicz 	}
6835ad03eb3SMarian Balakowicz 
6845ad03eb3SMarian Balakowicz 	if (!rd_data) {
6855ad03eb3SMarian Balakowicz 		debug ("## No init Ramdisk\n");
6865ad03eb3SMarian Balakowicz 		*rd_start = 0;
6875ad03eb3SMarian Balakowicz 		*rd_end = 0;
6885ad03eb3SMarian Balakowicz 	} else {
6895ad03eb3SMarian Balakowicz 		*rd_start = rd_data;
6905ad03eb3SMarian Balakowicz 		*rd_end = rd_data + rd_len;
6915ad03eb3SMarian Balakowicz 	}
6925ad03eb3SMarian Balakowicz 	debug ("   ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n",
6935ad03eb3SMarian Balakowicz 			*rd_start, *rd_end);
694*274cea2bSKumar Gala 
695*274cea2bSKumar Gala 	return 0;
6965ad03eb3SMarian Balakowicz }
697ceaed2b1SMarian Balakowicz 
698ceaed2b1SMarian Balakowicz #if defined(CONFIG_PPC) || defined(CONFIG_M68K)
699ceaed2b1SMarian Balakowicz /**
700ceaed2b1SMarian Balakowicz  * ramdisk_high - relocate init ramdisk
701ceaed2b1SMarian Balakowicz  * @rd_data: ramdisk data start address
702ceaed2b1SMarian Balakowicz  * @rd_len: ramdisk data length
703ceaed2b1SMarian Balakowicz  * @sp_limit: stack pointer limit (including BOOTMAPSZ)
704ceaed2b1SMarian Balakowicz  * @sp: current stack pointer
705ceaed2b1SMarian Balakowicz  * @initrd_start: pointer to a ulong variable, will hold final init ramdisk
706ceaed2b1SMarian Balakowicz  *      start address (after possible relocation)
707ceaed2b1SMarian Balakowicz  * @initrd_end: pointer to a ulong variable, will hold final init ramdisk
708ceaed2b1SMarian Balakowicz  *      end address (after possible relocation)
709ceaed2b1SMarian Balakowicz  *
710ceaed2b1SMarian Balakowicz  * ramdisk_high() takes a relocation hint from "initrd_high" environement
711ceaed2b1SMarian Balakowicz  * variable and if requested ramdisk data is moved to a specified location.
712ceaed2b1SMarian Balakowicz  *
713ceaed2b1SMarian Balakowicz  * returns:
714b6b0fe64SMarian Balakowicz  *     - initrd_start and initrd_end are set to final (after relocation) ramdisk
715ceaed2b1SMarian Balakowicz  *     start/end addresses if ramdisk image start and len were provided
716b6b0fe64SMarian Balakowicz  *     otherwise set initrd_start and initrd_end set to zeros
717b6b0fe64SMarian Balakowicz  *     - returns new allc_current, next free address below BOOTMAPSZ
718ceaed2b1SMarian Balakowicz  */
719b6b0fe64SMarian Balakowicz ulong ramdisk_high (ulong alloc_current, ulong rd_data, ulong rd_len,
720a6612bdfSKumar Gala 		ulong sp_limit, ulong sp,
721b6b0fe64SMarian Balakowicz 		ulong *initrd_start, ulong *initrd_end)
722ceaed2b1SMarian Balakowicz {
723ceaed2b1SMarian Balakowicz 	char	*s;
724ceaed2b1SMarian Balakowicz 	ulong	initrd_high;
725ceaed2b1SMarian Balakowicz 	int	initrd_copy_to_ram = 1;
726b6b0fe64SMarian Balakowicz 	ulong	new_alloc_current = alloc_current;
727ceaed2b1SMarian Balakowicz 
728ceaed2b1SMarian Balakowicz 	if ((s = getenv ("initrd_high")) != NULL) {
729ceaed2b1SMarian Balakowicz 		/* a value of "no" or a similar string will act like 0,
730ceaed2b1SMarian Balakowicz 		 * turning the "load high" feature off. This is intentional.
731ceaed2b1SMarian Balakowicz 		 */
732ceaed2b1SMarian Balakowicz 		initrd_high = simple_strtoul (s, NULL, 16);
733ceaed2b1SMarian Balakowicz 		if (initrd_high == ~0)
734ceaed2b1SMarian Balakowicz 			initrd_copy_to_ram = 0;
735ceaed2b1SMarian Balakowicz 	} else {
736ceaed2b1SMarian Balakowicz 		/* not set, no restrictions to load high */
737ceaed2b1SMarian Balakowicz 		initrd_high = ~0;
738ceaed2b1SMarian Balakowicz 	}
739ceaed2b1SMarian Balakowicz 
740ceaed2b1SMarian Balakowicz #ifdef CONFIG_LOGBUFFER
741ceaed2b1SMarian Balakowicz 	/* Prevent initrd from overwriting logbuffer */
742a6612bdfSKumar Gala 	if (initrd_high < (gd->bd->bi_memsize - LOGBUFF_LEN - LOGBUFF_OVERHEAD))
743a6612bdfSKumar Gala 	    initrd_high = gd->bd->bi_memsize - LOGBUFF_LEN - LOGBUFF_OVERHEAD;
744a6612bdfSKumar Gala 	debug ("## Logbuffer at 0x%08lx ", gd->bd->bi_memsize - LOGBUFF_LEN);
745ceaed2b1SMarian Balakowicz #endif
746ceaed2b1SMarian Balakowicz 	debug ("## initrd_high = 0x%08lx, copy_to_ram = %d\n",
747ceaed2b1SMarian Balakowicz 			initrd_high, initrd_copy_to_ram);
748ceaed2b1SMarian Balakowicz 
749ceaed2b1SMarian Balakowicz 	if (rd_data) {
750ceaed2b1SMarian Balakowicz 		if (!initrd_copy_to_ram) {	/* zero-copy ramdisk support */
751ceaed2b1SMarian Balakowicz 			debug ("   in-place initrd\n");
752ceaed2b1SMarian Balakowicz 			*initrd_start = rd_data;
753ceaed2b1SMarian Balakowicz 			*initrd_end = rd_data + rd_len;
754ceaed2b1SMarian Balakowicz 		} else {
755b6b0fe64SMarian Balakowicz 			new_alloc_current = alloc_current - rd_len;
756b6b0fe64SMarian Balakowicz 			*initrd_start  = new_alloc_current;
757ceaed2b1SMarian Balakowicz 			*initrd_start &= ~(4096 - 1);	/* align on page */
758ceaed2b1SMarian Balakowicz 
759ceaed2b1SMarian Balakowicz 			if (initrd_high) {
760ceaed2b1SMarian Balakowicz 				ulong nsp;
761ceaed2b1SMarian Balakowicz 
762ceaed2b1SMarian Balakowicz 				/*
763ceaed2b1SMarian Balakowicz 				 * the inital ramdisk does not need to be within
764ceaed2b1SMarian Balakowicz 				 * CFG_BOOTMAPSZ as it is not accessed until after
765ceaed2b1SMarian Balakowicz 				 * the mm system is initialised.
766ceaed2b1SMarian Balakowicz 				 *
767ceaed2b1SMarian Balakowicz 				 * do the stack bottom calculation again and see if
768ceaed2b1SMarian Balakowicz 				 * the initrd will fit just below the monitor stack
769ceaed2b1SMarian Balakowicz 				 * bottom without overwriting the area allocated
770ceaed2b1SMarian Balakowicz 				 * for command line args and board info.
771ceaed2b1SMarian Balakowicz 				 */
772ceaed2b1SMarian Balakowicz 				nsp = sp;
773ceaed2b1SMarian Balakowicz 				nsp -= 2048;		/* just to be sure */
774ceaed2b1SMarian Balakowicz 				nsp &= ~0xF;
775ceaed2b1SMarian Balakowicz 
776ceaed2b1SMarian Balakowicz 				if (nsp > initrd_high)	/* limit as specified */
777ceaed2b1SMarian Balakowicz 					nsp = initrd_high;
778ceaed2b1SMarian Balakowicz 
779ceaed2b1SMarian Balakowicz 				nsp -= rd_len;
780ceaed2b1SMarian Balakowicz 				nsp &= ~(4096 - 1);	/* align on page */
781ceaed2b1SMarian Balakowicz 
782b6b0fe64SMarian Balakowicz 				if (nsp >= sp_limit) {
783ceaed2b1SMarian Balakowicz 					*initrd_start = nsp;
784b6b0fe64SMarian Balakowicz 					new_alloc_current = alloc_current;
785b6b0fe64SMarian Balakowicz 				}
786ceaed2b1SMarian Balakowicz 			}
787ceaed2b1SMarian Balakowicz 
788ceaed2b1SMarian Balakowicz 			show_boot_progress (12);
789ceaed2b1SMarian Balakowicz 
790ceaed2b1SMarian Balakowicz 			*initrd_end = *initrd_start + rd_len;
791ceaed2b1SMarian Balakowicz 			printf ("   Loading Ramdisk to %08lx, end %08lx ... ",
792ceaed2b1SMarian Balakowicz 					*initrd_start, *initrd_end);
793ceaed2b1SMarian Balakowicz 
794ceaed2b1SMarian Balakowicz 			memmove_wd((void *)*initrd_start,
795ceaed2b1SMarian Balakowicz 					(void *)rd_data, rd_len, CHUNKSZ);
796ceaed2b1SMarian Balakowicz 
797ceaed2b1SMarian Balakowicz 			puts ("OK\n");
798ceaed2b1SMarian Balakowicz 		}
799ceaed2b1SMarian Balakowicz 	} else {
800ceaed2b1SMarian Balakowicz 		*initrd_start = 0;
801ceaed2b1SMarian Balakowicz 		*initrd_end = 0;
802ceaed2b1SMarian Balakowicz 	}
803ceaed2b1SMarian Balakowicz 	debug ("   ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n",
804ceaed2b1SMarian Balakowicz 			*initrd_start, *initrd_end);
805b6b0fe64SMarian Balakowicz 
806b6b0fe64SMarian Balakowicz 	return new_alloc_current;
807b6b0fe64SMarian Balakowicz }
808b6b0fe64SMarian Balakowicz 
809b6b0fe64SMarian Balakowicz /**
810b6b0fe64SMarian Balakowicz  * get_boot_sp_limit - calculate stack pointer limit
811b6b0fe64SMarian Balakowicz  * @sp: current stack pointer
812b6b0fe64SMarian Balakowicz  *
813b6b0fe64SMarian Balakowicz  * get_boot_sp_limit() takes current stack pointer adrress and calculates
814b6b0fe64SMarian Balakowicz  * stack pointer limit, below which kernel boot data (cmdline, board info,
815b6b0fe64SMarian Balakowicz  * etc.) will be allocated.
816b6b0fe64SMarian Balakowicz  *
817b6b0fe64SMarian Balakowicz  * returns:
818b6b0fe64SMarian Balakowicz  *     stack pointer limit
819b6b0fe64SMarian Balakowicz  */
820b6b0fe64SMarian Balakowicz ulong get_boot_sp_limit(ulong sp)
821b6b0fe64SMarian Balakowicz {
822b6b0fe64SMarian Balakowicz 	ulong sp_limit = sp;
823b6b0fe64SMarian Balakowicz 
824b6b0fe64SMarian Balakowicz 	sp_limit -= 2048;	/* just to be sure */
825b6b0fe64SMarian Balakowicz 
826b6b0fe64SMarian Balakowicz 	/* make sure sp_limit is within kernel mapped space */
827b6b0fe64SMarian Balakowicz 	if (sp_limit > CFG_BOOTMAPSZ)
828b6b0fe64SMarian Balakowicz 		sp_limit = CFG_BOOTMAPSZ;
829b6b0fe64SMarian Balakowicz 	sp_limit &= ~0xF;
830b6b0fe64SMarian Balakowicz 
831b6b0fe64SMarian Balakowicz 	return sp_limit;
832b6b0fe64SMarian Balakowicz }
833b6b0fe64SMarian Balakowicz 
834b6b0fe64SMarian Balakowicz /**
835b6b0fe64SMarian Balakowicz  * get_boot_cmdline - allocate and initialize kernel cmdline
836b6b0fe64SMarian Balakowicz  * @alloc_current: current boot allocation address (counting down
837b6b0fe64SMarian Balakowicz  *      from sp_limit)
838b6b0fe64SMarian Balakowicz  * @cmd_start: pointer to a ulong variable, will hold cmdline start
839b6b0fe64SMarian Balakowicz  * @cmd_end: pointer to a ulong variable, will hold cmdline end
840b6b0fe64SMarian Balakowicz  *
841b6b0fe64SMarian Balakowicz  * get_boot_cmdline() allocates space for kernel command line below
842b6b0fe64SMarian Balakowicz  * provided alloc_current address. If "bootargs" U-boot environemnt
843b6b0fe64SMarian Balakowicz  * variable is present its contents is copied to allocated kernel
844b6b0fe64SMarian Balakowicz  * command line.
845b6b0fe64SMarian Balakowicz  *
846b6b0fe64SMarian Balakowicz  * returns:
847b6b0fe64SMarian Balakowicz  *     alloc_current after cmdline allocation
848b6b0fe64SMarian Balakowicz  */
849b6b0fe64SMarian Balakowicz ulong get_boot_cmdline (ulong alloc_current, ulong *cmd_start, ulong *cmd_end)
850b6b0fe64SMarian Balakowicz {
851b6b0fe64SMarian Balakowicz 	char *cmdline;
852b6b0fe64SMarian Balakowicz 	char *s;
853b6b0fe64SMarian Balakowicz 
854b6b0fe64SMarian Balakowicz 	cmdline = (char *)((alloc_current - CFG_BARGSIZE) & ~0xF);
855b6b0fe64SMarian Balakowicz 
856b6b0fe64SMarian Balakowicz 	if ((s = getenv("bootargs")) == NULL)
857b6b0fe64SMarian Balakowicz 		s = "";
858b6b0fe64SMarian Balakowicz 
859b6b0fe64SMarian Balakowicz 	strcpy(cmdline, s);
860b6b0fe64SMarian Balakowicz 
861b6b0fe64SMarian Balakowicz 	*cmd_start = (ulong) & cmdline[0];
862b6b0fe64SMarian Balakowicz 	*cmd_end = *cmd_start + strlen(cmdline);
863b6b0fe64SMarian Balakowicz 
864b6b0fe64SMarian Balakowicz 	debug ("## cmdline at 0x%08lx ... 0x%08lx\n", *cmd_start, *cmd_end);
865b6b0fe64SMarian Balakowicz 
866b6b0fe64SMarian Balakowicz 	return (ulong)cmdline;
867b6b0fe64SMarian Balakowicz }
868b6b0fe64SMarian Balakowicz 
869b6b0fe64SMarian Balakowicz /**
870b6b0fe64SMarian Balakowicz  * get_boot_kbd - allocate and initialize kernel copy of board info
871b6b0fe64SMarian Balakowicz  * @alloc_current: current boot allocation address (counting down
872b6b0fe64SMarian Balakowicz  *      from sp_limit)
873b6b0fe64SMarian Balakowicz  * @kbd: double pointer to board info data
874b6b0fe64SMarian Balakowicz  *
875b6b0fe64SMarian Balakowicz  * get_boot_kbd() - allocates space for kernel copy of board info data.
876b6b0fe64SMarian Balakowicz  * Space is allocated below provided alloc_current address and kernel
877b6b0fe64SMarian Balakowicz  * board info is initialized with the current u-boot board info data.
878b6b0fe64SMarian Balakowicz  *
879b6b0fe64SMarian Balakowicz  * returns:
880b6b0fe64SMarian Balakowicz  *     alloc_current after kbd allocation
881b6b0fe64SMarian Balakowicz  */
882b6b0fe64SMarian Balakowicz ulong get_boot_kbd (ulong alloc_current, bd_t **kbd)
883b6b0fe64SMarian Balakowicz {
884b6b0fe64SMarian Balakowicz 	*kbd = (bd_t *) (((ulong)alloc_current - sizeof(bd_t)) & ~0xF);
885b6b0fe64SMarian Balakowicz 	**kbd = *(gd->bd);
886b6b0fe64SMarian Balakowicz 
887b6b0fe64SMarian Balakowicz 	debug ("## kernel board info at 0x%08lx\n", (ulong)*kbd);
888b6b0fe64SMarian Balakowicz 
889b6b0fe64SMarian Balakowicz #if defined(DEBUG) && defined(CONFIG_CMD_BDI)
890b6b0fe64SMarian Balakowicz 	do_bdinfo(NULL, 0, 0, NULL);
891b6b0fe64SMarian Balakowicz #endif
892b6b0fe64SMarian Balakowicz 
893b6b0fe64SMarian Balakowicz 	return (ulong)*kbd;
894ceaed2b1SMarian Balakowicz }
895ceaed2b1SMarian Balakowicz #endif /* CONFIG_PPC || CONFIG_M68K */
8965ad03eb3SMarian Balakowicz 
897f50433d6SMarian Balakowicz #if defined(CONFIG_FIT)
898f50433d6SMarian Balakowicz /*****************************************************************************/
899f50433d6SMarian Balakowicz /* New uImage format routines */
900f50433d6SMarian Balakowicz /*****************************************************************************/
901f50433d6SMarian Balakowicz static int fit_parse_spec (const char *spec, char sepc, ulong addr_curr,
902f50433d6SMarian Balakowicz 		ulong *addr, const char **name)
903f50433d6SMarian Balakowicz {
904f50433d6SMarian Balakowicz 	const char *sep;
905f50433d6SMarian Balakowicz 
906f50433d6SMarian Balakowicz 	*addr = addr_curr;
907f50433d6SMarian Balakowicz 	*name = NULL;
908f50433d6SMarian Balakowicz 
909f50433d6SMarian Balakowicz 	sep = strchr (spec, sepc);
910f50433d6SMarian Balakowicz 	if (sep) {
911f50433d6SMarian Balakowicz 		if (sep - spec > 0)
912f50433d6SMarian Balakowicz 			*addr = simple_strtoul (spec, NULL, 16);
913f50433d6SMarian Balakowicz 
914f50433d6SMarian Balakowicz 		*name = sep + 1;
915f50433d6SMarian Balakowicz 		return 1;
916f50433d6SMarian Balakowicz 	}
917f50433d6SMarian Balakowicz 
918f50433d6SMarian Balakowicz 	return 0;
919f50433d6SMarian Balakowicz }
920f50433d6SMarian Balakowicz 
921f50433d6SMarian Balakowicz /**
922f50433d6SMarian Balakowicz  * fit_parse_conf - parse FIT configuration spec
923f50433d6SMarian Balakowicz  * @spec: input string, containing configuration spec
924f50433d6SMarian Balakowicz  * @add_curr: current image address (to be used as a possible default)
925f50433d6SMarian Balakowicz  * @addr: pointer to a ulong variable, will hold FIT image address of a given
926f50433d6SMarian Balakowicz  * configuration
927f50433d6SMarian Balakowicz  * @conf_name double pointer to a char, will hold pointer to a configuration
928f50433d6SMarian Balakowicz  * unit name
929f50433d6SMarian Balakowicz  *
930f50433d6SMarian Balakowicz  * fit_parse_conf() expects configuration spec in the for of [<addr>]#<conf>,
931f50433d6SMarian Balakowicz  * where <addr> is a FIT image address that contains configuration
932f50433d6SMarian Balakowicz  * with a <conf> unit name.
933f50433d6SMarian Balakowicz  *
934f50433d6SMarian Balakowicz  * Address part is optional, and if omitted default add_curr will
935f50433d6SMarian Balakowicz  * be used instead.
936f50433d6SMarian Balakowicz  *
937f50433d6SMarian Balakowicz  * returns:
938f50433d6SMarian Balakowicz  *     1 if spec is a valid configuration string,
939f50433d6SMarian Balakowicz  *     addr and conf_name are set accordingly
940f50433d6SMarian Balakowicz  *     0 otherwise
941f50433d6SMarian Balakowicz  */
942f50433d6SMarian Balakowicz inline int fit_parse_conf (const char *spec, ulong addr_curr,
943f50433d6SMarian Balakowicz 		ulong *addr, const char **conf_name)
944f50433d6SMarian Balakowicz {
945f50433d6SMarian Balakowicz 	return fit_parse_spec (spec, '#', addr_curr, addr, conf_name);
946f50433d6SMarian Balakowicz }
947f50433d6SMarian Balakowicz 
948f50433d6SMarian Balakowicz /**
949f50433d6SMarian Balakowicz  * fit_parse_subimage - parse FIT subimage spec
950f50433d6SMarian Balakowicz  * @spec: input string, containing subimage spec
951f50433d6SMarian Balakowicz  * @add_curr: current image address (to be used as a possible default)
952f50433d6SMarian Balakowicz  * @addr: pointer to a ulong variable, will hold FIT image address of a given
953f50433d6SMarian Balakowicz  * subimage
954f50433d6SMarian Balakowicz  * @image_name: double pointer to a char, will hold pointer to a subimage name
955f50433d6SMarian Balakowicz  *
956f50433d6SMarian Balakowicz  * fit_parse_subimage() expects subimage spec in the for of
957f50433d6SMarian Balakowicz  * [<addr>]:<subimage>, where <addr> is a FIT image address that contains
958f50433d6SMarian Balakowicz  * subimage with a <subimg> unit name.
959f50433d6SMarian Balakowicz  *
960f50433d6SMarian Balakowicz  * Address part is optional, and if omitted default add_curr will
961f50433d6SMarian Balakowicz  * be used instead.
962f50433d6SMarian Balakowicz  *
963f50433d6SMarian Balakowicz  * returns:
964f50433d6SMarian Balakowicz  *     1 if spec is a valid subimage string,
965f50433d6SMarian Balakowicz  *     addr and image_name are set accordingly
966f50433d6SMarian Balakowicz  *     0 otherwise
967f50433d6SMarian Balakowicz  */
968f50433d6SMarian Balakowicz inline int fit_parse_subimage (const char *spec, ulong addr_curr,
969f50433d6SMarian Balakowicz 		ulong *addr, const char **image_name)
970f50433d6SMarian Balakowicz {
971f50433d6SMarian Balakowicz 	return fit_parse_spec (spec, ':', addr_curr, addr, image_name);
972f50433d6SMarian Balakowicz }
973d5934ad7SMarian Balakowicz 
974f50433d6SMarian Balakowicz #endif /* CONFIG_FIT */
975f50433d6SMarian Balakowicz 
976b6b0fe64SMarian Balakowicz #endif /* USE_HOSTCC */
977