1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000-2004
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  *
6  * (C) Copyright 2003
7  * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
8  */
9 
10 /*
11  * Multi Image extract
12  */
13 #include <command.h>
14 #include <cpu_func.h>
15 #include <env.h>
16 #include <gzip.h>
17 #if IS_ENABLED(CONFIG_ZSTD)
18 #include <linux/zstd.h>
19 #endif
20 #include <image.h>
21 #include <malloc.h>
22 #include <mapmem.h>
23 #include <watchdog.h>
24 #if defined(CONFIG_BZIP2)
25 #include <bzlib.h>
26 #endif
27 #include <asm/byteorder.h>
28 #include <asm/cache.h>
29 #include <asm/io.h>
30 #include <u-boot/zlib.h>
31 
32 static int
do_imgextract(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])33 do_imgextract(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
34 {
35 	ulong		addr = image_load_addr;
36 	ulong		dest = 0;
37 	ulong		data, len;
38 	int		verify;
39 	int		part = 0;
40 #if defined(CONFIG_LEGACY_IMAGE_FORMAT)
41 	ulong		count;
42 	struct legacy_img_hdr	*hdr = NULL;
43 #endif
44 #if defined(CONFIG_FIT)
45 	const char	*uname = NULL;
46 	const void*	fit_hdr;
47 	int		noffset;
48 	const void	*fit_data;
49 	size_t		fit_len;
50 #endif
51 #ifdef CONFIG_GZIP
52 	uint		unc_len = CONFIG_SYS_XIMG_LEN;
53 #endif
54 	uint8_t		comp;
55 
56 	verify = env_get_yesno("verify");
57 
58 	if (argc > 1) {
59 		addr = hextoul(argv[1], NULL);
60 	}
61 	if (argc > 2) {
62 		part = hextoul(argv[2], NULL);
63 #if defined(CONFIG_FIT)
64 		uname = argv[2];
65 #endif
66 	}
67 	if (argc > 3) {
68 		dest = hextoul(argv[3], NULL);
69 	}
70 
71 	switch (genimg_get_format((void *)addr)) {
72 #if defined(CONFIG_LEGACY_IMAGE_FORMAT)
73 	case IMAGE_FORMAT_LEGACY:
74 
75 		printf("## Copying part %d from legacy image "
76 			"at %08lx ...\n", part, addr);
77 
78 		hdr = (struct legacy_img_hdr *)addr;
79 		if (!image_check_magic(hdr)) {
80 			printf("Bad Magic Number\n");
81 			return 1;
82 		}
83 
84 		if (!image_check_hcrc(hdr)) {
85 			printf("Bad Header Checksum\n");
86 			return 1;
87 		}
88 #ifdef DEBUG
89 		image_print_contents(hdr);
90 #endif
91 
92 		if (!image_check_type(hdr, IH_TYPE_MULTI) &&
93 		    !image_check_type(hdr, IH_TYPE_SCRIPT)) {
94 			printf("Wrong Image Type for %s command\n",
95 					cmdtp->name);
96 			return 1;
97 		}
98 
99 		comp = image_get_comp(hdr);
100 		if ((comp != IH_COMP_NONE) && (argc < 4)) {
101 			printf("Must specify load address for %s command "
102 					"with compressed image\n",
103 					cmdtp->name);
104 			return 1;
105 		}
106 
107 		if (verify) {
108 			printf("   Verifying Checksum ... ");
109 			if (!image_check_dcrc(hdr)) {
110 				printf("Bad Data CRC\n");
111 				return 1;
112 			}
113 			printf("OK\n");
114 		}
115 
116 		count = image_multi_count(hdr);
117 		if (part >= count) {
118 			printf("Bad Image Part\n");
119 			return 1;
120 		}
121 
122 		image_multi_getimg(hdr, part, &data, &len);
123 		break;
124 #endif
125 #if defined(CONFIG_FIT)
126 	case IMAGE_FORMAT_FIT:
127 		if (uname == NULL) {
128 			puts("No FIT subimage unit name\n");
129 			return 1;
130 		}
131 
132 		printf("## Copying '%s' subimage from FIT image "
133 			"at %08lx ...\n", uname, addr);
134 
135 		fit_hdr = (const void *)addr;
136 		if (fit_check_format(fit_hdr, IMAGE_SIZE_INVAL)) {
137 			puts("Bad FIT image format\n");
138 			return 1;
139 		}
140 
141 		/* get subimage node offset */
142 		noffset = fit_image_get_node(fit_hdr, uname);
143 		if (noffset < 0) {
144 			printf("Can't find '%s' FIT subimage\n", uname);
145 			return 1;
146 		}
147 
148 		if (!fit_image_check_comp(fit_hdr, noffset, IH_COMP_NONE)
149 		    && (argc < 4)) {
150 			printf("Must specify load address for %s command "
151 				"with compressed image\n",
152 				cmdtp->name);
153 			return 1;
154 		}
155 
156 		/* verify integrity */
157 		if (verify) {
158 			if (!fit_image_verify(fit_hdr, noffset)) {
159 				puts("Bad Data Hash\n");
160 				return 1;
161 			}
162 		}
163 
164 		/* get subimage/external data address and length */
165 		if (fit_image_get_data(fit_hdr, noffset, &fit_data, &fit_len)) {
166 			puts("Could not find script subimage data\n");
167 			return 1;
168 		}
169 
170 		if (fit_image_get_comp(fit_hdr, noffset, &comp))
171 			comp = IH_COMP_NONE;
172 
173 		data = (ulong)fit_data;
174 		len = (ulong)fit_len;
175 		break;
176 #endif
177 	default:
178 		puts("Invalid image type for imxtract\n");
179 		return 1;
180 	}
181 
182 	if (argc > 3) {
183 		switch (comp) {
184 		case IH_COMP_NONE:
185 #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
186 			{
187 				size_t l = len;
188 				size_t tail;
189 				void *to = (void *) dest;
190 				void *from = (void *)data;
191 
192 				printf("   Loading part %d ... ", part);
193 
194 				while (l > 0) {
195 					tail = (l > CHUNKSZ) ? CHUNKSZ : l;
196 					schedule();
197 					memmove(to, from, tail);
198 					to += tail;
199 					from += tail;
200 					l -= tail;
201 				}
202 			}
203 #else	/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
204 			printf("   Loading part %d ... ", part);
205 			memmove((char *) dest, (char *)data, len);
206 #endif	/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
207 			break;
208 #ifdef CONFIG_GZIP
209 		case IH_COMP_GZIP:
210 			{
211 				int ret = 0;
212 				printf("   Uncompressing part %d ... ", part);
213 				ret = gunzip((void *)dest, unc_len,
214 					     (uchar *)data, &len);
215 				if (ret == Z_BUF_ERROR) {
216 					puts("Image too large: increase CONFIG_SYS_XIMG_LEN\n");
217 					return 1;
218 				} else if (ret != 0) {
219 					puts("GUNZIP ERROR - image not loaded\n");
220 					return 1;
221 				}
222 			}
223 			break;
224 #endif
225 #if defined(CONFIG_BZIP2) && defined(CONFIG_LEGACY_IMAGE_FORMAT)
226 		case IH_COMP_BZIP2:
227 			{
228 				int i;
229 
230 				printf("   Uncompressing part %d ... ", part);
231 				/*
232 				 * If we've got less than 4 MB of malloc()
233 				 * space, use slower decompression algorithm
234 				 * which requires at most 2300 KB of memory.
235 				 */
236 				i = BZ2_bzBuffToBuffDecompress(
237 					map_sysmem(ntohl(hdr->ih_load), 0),
238 					&unc_len, (char *)data, len,
239 					CONFIG_SYS_MALLOC_LEN < (4096 * 1024),
240 					0);
241 				if (i != BZ_OK) {
242 					printf("BUNZIP2 ERROR %d - "
243 						"image not loaded\n", i);
244 					return 1;
245 				}
246 			}
247 			break;
248 #endif /* CONFIG_BZIP2 */
249 #if IS_ENABLED(CONFIG_ZSTD)
250 		case IH_COMP_ZSTD:
251 			{
252 				int ret;
253 				struct abuf in, out;
254 
255 				printf("   Uncompressing part %d ... ", part);
256 
257 				abuf_init_set(&in, (void *)data, len);
258 				abuf_init_set(&out, (void *)dest, unc_len);
259 				ret = zstd_decompress(&in, &out);
260 				if (ret < 0) {
261 					printf("ZSTD ERROR %d - "
262 					       "image not loaded\n", ret);
263 					return 1;
264 				}
265 				len = ret;
266 			}
267 			break;
268 #endif
269 		default:
270 			printf("Unimplemented compression type %d\n", comp);
271 			return 1;
272 		}
273 		puts("OK\n");
274 	}
275 
276 	flush_cache(dest, ALIGN(len, ARCH_DMA_MINALIGN));
277 
278 	env_set_hex("fileaddr", data);
279 	env_set_hex("filesize", len);
280 
281 	return 0;
282 }
283 
284 U_BOOT_LONGHELP(imgextract,
285 	"addr part [dest]\n"
286 	"    - extract <part> from legacy image at <addr> and copy to <dest>"
287 #if defined(CONFIG_FIT)
288 	"\n"
289 	"addr uname [dest]\n"
290 	"    - extract <uname> subimage from FIT image at <addr> and copy to <dest>"
291 #endif
292 	);
293 
294 U_BOOT_CMD(
295 	imxtract, 4, 1, do_imgextract,
296 	"extract a part of a multi-image", imgextract_help_text
297 );
298