]>
Commit | Line | Data |
---|---|---|
b97a2a0a MB |
1 | /* |
2 | * (C) Copyright 2008 Semihalf | |
3 | * | |
4 | * (C) Copyright 2000-2006 | |
5 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
6 | * | |
7 | * See file CREDITS for list of people who contributed to this | |
8 | * project. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU General Public License as | |
12 | * published by the Free Software Foundation; either version 2 of | |
13 | * the License, or (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
23 | * MA 02111-1307 USA | |
24 | */ | |
25 | #ifndef USE_HOSTCC | |
5ad03eb3 MB |
26 | #include <common.h> |
27 | #include <watchdog.h> | |
28 | ||
29 | #ifdef CONFIG_SHOW_BOOT_PROGRESS | |
30 | #include <status_led.h> | |
31 | #endif | |
32 | ||
33 | #ifdef CONFIG_HAS_DATAFLASH | |
34 | #include <dataflash.h> | |
35 | #endif | |
36 | ||
37 | extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); | |
b97a2a0a | 38 | #else |
5ad03eb3 | 39 | #include "mkimage.h" |
b97a2a0a MB |
40 | #endif |
41 | ||
42 | #include <image.h> | |
43 | ||
44 | unsigned long crc32 (unsigned long, const unsigned char *, unsigned int); | |
45 | ||
46 | int image_check_hcrc (image_header_t *hdr) | |
47 | { | |
48 | ulong hcrc; | |
49 | ulong len = image_get_header_size (); | |
50 | image_header_t header; | |
51 | ||
52 | /* Copy header so we can blank CRC field for re-calculation */ | |
53 | memmove (&header, (char *)hdr, image_get_header_size ()); | |
54 | image_set_hcrc (&header, 0); | |
55 | ||
56 | hcrc = crc32 (0, (unsigned char *)&header, len); | |
57 | ||
58 | return (hcrc == image_get_hcrc (hdr)); | |
59 | } | |
60 | ||
61 | int image_check_dcrc (image_header_t *hdr) | |
62 | { | |
63 | ulong data = image_get_data (hdr); | |
64 | ulong len = image_get_data_size (hdr); | |
65 | ulong dcrc = crc32 (0, (unsigned char *)data, len); | |
66 | ||
67 | return (dcrc == image_get_dcrc (hdr)); | |
68 | } | |
69 | ||
af13cdbc | 70 | #ifndef USE_HOSTCC |
b97a2a0a MB |
71 | int image_check_dcrc_wd (image_header_t *hdr, ulong chunksz) |
72 | { | |
73 | ulong dcrc = 0; | |
74 | ulong len = image_get_data_size (hdr); | |
75 | ulong data = image_get_data (hdr); | |
76 | ||
77 | #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) | |
78 | ulong cdata = data; | |
79 | ulong edata = cdata + len; | |
80 | ||
81 | while (cdata < edata) { | |
82 | ulong chunk = edata - cdata; | |
83 | ||
84 | if (chunk > chunksz) | |
85 | chunk = chunksz; | |
86 | dcrc = crc32 (dcrc, (unsigned char *)cdata, chunk); | |
87 | cdata += chunk; | |
88 | ||
89 | WATCHDOG_RESET (); | |
90 | } | |
91 | #else | |
92 | dcrc = crc32 (0, (unsigned char *)data, len); | |
93 | #endif | |
94 | ||
95 | return (dcrc == image_get_dcrc (hdr)); | |
96 | } | |
97 | ||
98 | int getenv_verify (void) | |
99 | { | |
100 | char *s = getenv ("verify"); | |
101 | return (s && (*s == 'n')) ? 0 : 1; | |
102 | } | |
af13cdbc MB |
103 | |
104 | void memmove_wd (void *to, void *from, size_t len, ulong chunksz) | |
105 | { | |
106 | #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) | |
107 | while (len > 0) { | |
108 | size_t tail = (len > chunksz) ? chunksz : len; | |
109 | WATCHDOG_RESET (); | |
110 | memmove (to, from, tail); | |
111 | to += tail; | |
112 | from += tail; | |
113 | len -= tail; | |
114 | } | |
115 | #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */ | |
116 | memmove (to, from, len); | |
117 | #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ | |
118 | } | |
119 | #endif /* USE_HOSTCC */ | |
f13e7b2e MB |
120 | |
121 | /** | |
122 | * image_multi_count - get component (sub-image) count | |
123 | * @hdr: pointer to the header of the multi component image | |
124 | * | |
125 | * image_multi_count() returns number of components in a multi | |
126 | * component image. | |
127 | * | |
128 | * Note: no checking of the image type is done, caller must pass | |
129 | * a valid multi component image. | |
130 | * | |
131 | * returns: | |
132 | * number of components | |
133 | */ | |
134 | ulong image_multi_count (image_header_t *hdr) | |
135 | { | |
136 | ulong i, count = 0; | |
137 | ulong *size; | |
138 | ||
139 | /* get start of the image payload, which in case of multi | |
140 | * component images that points to a table of component sizes */ | |
141 | size = (ulong *)image_get_data (hdr); | |
142 | ||
143 | /* count non empty slots */ | |
144 | for (i = 0; size[i]; ++i) | |
145 | count++; | |
146 | ||
147 | return count; | |
148 | } | |
149 | ||
150 | /** | |
151 | * image_multi_getimg - get component data address and size | |
152 | * @hdr: pointer to the header of the multi component image | |
153 | * @idx: index of the requested component | |
154 | * @data: pointer to a ulong variable, will hold component data address | |
155 | * @len: pointer to a ulong variable, will hold component size | |
156 | * | |
157 | * image_multi_getimg() returns size and data address for the requested | |
158 | * component in a multi component image. | |
159 | * | |
160 | * Note: no checking of the image type is done, caller must pass | |
161 | * a valid multi component image. | |
162 | * | |
163 | * returns: | |
164 | * data address and size of the component, if idx is valid | |
165 | * 0 in data and len, if idx is out of range | |
166 | */ | |
167 | void image_multi_getimg (image_header_t *hdr, ulong idx, | |
168 | ulong *data, ulong *len) | |
169 | { | |
170 | int i; | |
171 | ulong *size; | |
172 | ulong offset, tail, count, img_data; | |
173 | ||
174 | /* get number of component */ | |
175 | count = image_multi_count (hdr); | |
176 | ||
177 | /* get start of the image payload, which in case of multi | |
178 | * component images that points to a table of component sizes */ | |
179 | size = (ulong *)image_get_data (hdr); | |
180 | ||
181 | /* get address of the proper component data start, which means | |
182 | * skipping sizes table (add 1 for last, null entry) */ | |
183 | img_data = image_get_data (hdr) + (count + 1) * sizeof (ulong); | |
184 | ||
185 | if (idx < count) { | |
186 | *len = size[idx]; | |
187 | offset = 0; | |
188 | tail = 0; | |
189 | ||
190 | /* go over all indices preceding requested component idx */ | |
191 | for (i = 0; i < idx; i++) { | |
192 | /* add up i-th component size */ | |
193 | offset += size[i]; | |
194 | ||
195 | /* add up alignment for i-th component */ | |
196 | tail += (4 - size[i] % 4); | |
197 | } | |
198 | ||
199 | /* calculate idx-th component data address */ | |
200 | *data = img_data + offset + tail; | |
201 | } else { | |
202 | *len = 0; | |
203 | *data = 0; | |
204 | } | |
205 | } | |
42b73e8e MB |
206 | |
207 | #ifndef USE_HOSTCC | |
208 | const char* image_get_os_name (uint8_t os) | |
209 | { | |
210 | const char *name; | |
211 | ||
212 | switch (os) { | |
213 | case IH_OS_INVALID: name = "Invalid OS"; break; | |
214 | case IH_OS_NETBSD: name = "NetBSD"; break; | |
215 | case IH_OS_LINUX: name = "Linux"; break; | |
216 | case IH_OS_VXWORKS: name = "VxWorks"; break; | |
217 | case IH_OS_QNX: name = "QNX"; break; | |
218 | case IH_OS_U_BOOT: name = "U-Boot"; break; | |
219 | case IH_OS_RTEMS: name = "RTEMS"; break; | |
220 | #ifdef CONFIG_ARTOS | |
221 | case IH_OS_ARTOS: name = "ARTOS"; break; | |
222 | #endif | |
223 | #ifdef CONFIG_LYNXKDI | |
224 | case IH_OS_LYNXOS: name = "LynxOS"; break; | |
225 | #endif | |
226 | default: name = "Unknown OS"; break; | |
227 | } | |
228 | ||
229 | return name; | |
230 | } | |
231 | ||
232 | const char* image_get_arch_name (uint8_t arch) | |
233 | { | |
234 | const char *name; | |
235 | ||
236 | switch (arch) { | |
237 | case IH_ARCH_INVALID: name = "Invalid Architecture"; break; | |
238 | case IH_ARCH_ALPHA: name = "Alpha"; break; | |
239 | case IH_ARCH_ARM: name = "ARM"; break; | |
240 | case IH_ARCH_AVR32: name = "AVR32"; break; | |
241 | case IH_ARCH_BLACKFIN: name = "Blackfin"; break; | |
242 | case IH_ARCH_I386: name = "Intel x86"; break; | |
243 | case IH_ARCH_IA64: name = "IA64"; break; | |
244 | case IH_ARCH_M68K: name = "M68K"; break; | |
245 | case IH_ARCH_MICROBLAZE:name = "Microblaze"; break; | |
246 | case IH_ARCH_MIPS64: name = "MIPS 64 Bit"; break; | |
247 | case IH_ARCH_MIPS: name = "MIPS"; break; | |
248 | case IH_ARCH_NIOS2: name = "Nios-II"; break; | |
249 | case IH_ARCH_NIOS: name = "Nios"; break; | |
250 | case IH_ARCH_PPC: name = "PowerPC"; break; | |
251 | case IH_ARCH_S390: name = "IBM S390"; break; | |
252 | case IH_ARCH_SH: name = "SuperH"; break; | |
253 | case IH_ARCH_SPARC64: name = "SPARC 64 Bit"; break; | |
254 | case IH_ARCH_SPARC: name = "SPARC"; break; | |
255 | default: name = "Unknown Architecture"; break; | |
256 | } | |
257 | ||
258 | return name; | |
259 | } | |
260 | ||
261 | const char* image_get_type_name (uint8_t type) | |
262 | { | |
263 | const char *name; | |
264 | ||
265 | switch (type) { | |
266 | case IH_TYPE_INVALID: name = "Invalid Image"; break; | |
267 | case IH_TYPE_STANDALONE:name = "Standalone Program"; break; | |
268 | case IH_TYPE_KERNEL: name = "Kernel Image"; break; | |
269 | case IH_TYPE_RAMDISK: name = "RAMDisk Image"; break; | |
270 | case IH_TYPE_MULTI: name = "Multi-File Image"; break; | |
271 | case IH_TYPE_FIRMWARE: name = "Firmware"; break; | |
272 | case IH_TYPE_SCRIPT: name = "Script"; break; | |
273 | case IH_TYPE_FLATDT: name = "Flat Device Tree"; break; | |
274 | default: name = "Unknown Image"; break; | |
275 | } | |
276 | ||
277 | return name; | |
278 | } | |
279 | ||
280 | const char* image_get_comp_name (uint8_t comp) | |
281 | { | |
282 | const char *name; | |
283 | ||
284 | switch (comp) { | |
285 | case IH_COMP_NONE: name = "uncompressed"; break; | |
286 | case IH_COMP_GZIP: name = "gzip compressed"; break; | |
287 | case IH_COMP_BZIP2: name = "bzip2 compressed"; break; | |
288 | default: name = "unknown compression"; break; | |
289 | } | |
290 | ||
291 | return name; | |
292 | } | |
5ad03eb3 MB |
293 | |
294 | /** | |
295 | * image_get_ramdisk - get and verify ramdisk image | |
296 | * @cmdtp: command table pointer | |
297 | * @flag: command flag | |
298 | * @argc: command argument count | |
299 | * @argv: command argument list | |
300 | * @rd_addr: ramdisk image start address | |
301 | * @arch: expected ramdisk architecture | |
302 | * @verify: checksum verification flag | |
303 | * | |
304 | * image_get_ramdisk() returns a pointer to the verified ramdisk image | |
305 | * header. Routine receives image start address and expected architecture | |
306 | * flag. Verification done covers data and header integrity and os/type/arch | |
307 | * fields checking. | |
308 | * | |
309 | * If dataflash support is enabled routine checks for dataflash addresses | |
310 | * and handles required dataflash reads. | |
311 | * | |
312 | * returns: | |
313 | * pointer to a ramdisk image header, if image was found and valid | |
314 | * otherwise, board is reset | |
315 | */ | |
316 | image_header_t* image_get_ramdisk (cmd_tbl_t *cmdtp, int flag, | |
317 | int argc, char *argv[], | |
318 | ulong rd_addr, uint8_t arch, int verify) | |
319 | { | |
320 | image_header_t *rd_hdr; | |
321 | ||
322 | show_boot_progress (9); | |
323 | ||
324 | #ifdef CONFIG_HAS_DATAFLASH | |
325 | if (addr_dataflash (rd_addr)) { | |
326 | rd_hdr = (image_header_t *)CFG_LOAD_ADDR; | |
327 | debug (" Reading Ramdisk image header from dataflash address " | |
328 | "%08lx to %08lx\n", rd_addr, (ulong)rd_hdr); | |
329 | read_dataflash (rd_addr, image_get_header_size (), | |
330 | (char *)rd_hdr); | |
331 | } else | |
332 | #endif | |
333 | rd_hdr = (image_header_t *)rd_addr; | |
334 | ||
335 | if (!image_check_magic (rd_hdr)) { | |
336 | puts ("Bad Magic Number\n"); | |
337 | show_boot_progress (-10); | |
338 | do_reset (cmdtp, flag, argc, argv); | |
339 | } | |
340 | ||
341 | if (!image_check_hcrc (rd_hdr)) { | |
342 | puts ("Bad Header Checksum\n"); | |
343 | show_boot_progress (-11); | |
344 | do_reset (cmdtp, flag, argc, argv); | |
345 | } | |
346 | ||
347 | show_boot_progress (10); | |
348 | print_image_hdr (rd_hdr); | |
349 | ||
350 | #ifdef CONFIG_HAS_DATAFLASH | |
351 | if (addr_dataflash (rd_addr)) { | |
352 | debug (" Reading Ramdisk image data from dataflash address " | |
353 | "%08lx to %08lx\n", rd_addr + image_get_header_size, | |
354 | (ulong)image_get_data (rd_hdr)); | |
355 | ||
356 | read_dataflash (rd_addr + image_get_header_size (), | |
357 | image_get_data_size (rd_hdr), | |
358 | (char *)image_get_data (rd_hdr)); | |
359 | } | |
42b73e8e | 360 | #endif |
5ad03eb3 MB |
361 | |
362 | if (verify) { | |
363 | puts(" Verifying Checksum ... "); | |
364 | if (!image_check_dcrc_wd (rd_hdr, CHUNKSZ)) { | |
365 | puts ("Bad Data CRC\n"); | |
366 | show_boot_progress (-12); | |
367 | do_reset (cmdtp, flag, argc, argv); | |
368 | } | |
369 | puts("OK\n"); | |
370 | } | |
371 | ||
372 | show_boot_progress (11); | |
373 | ||
374 | if (!image_check_os (rd_hdr, IH_OS_LINUX) || | |
375 | !image_check_arch (rd_hdr, arch) || | |
376 | !image_check_type (rd_hdr, IH_TYPE_RAMDISK)) { | |
377 | printf ("No Linux %s Ramdisk Image\n", | |
378 | image_get_arch_name(arch)); | |
379 | show_boot_progress (-13); | |
380 | do_reset (cmdtp, flag, argc, argv); | |
381 | } | |
382 | ||
383 | return rd_hdr; | |
384 | } | |
385 | ||
386 | /** | |
387 | * get_ramdisk - main ramdisk handling routine | |
388 | * @cmdtp: command table pointer | |
389 | * @flag: command flag | |
390 | * @argc: command argument count | |
391 | * @argv: command argument list | |
392 | * @hdr: pointer to the posiibly multi componet kernel image | |
393 | * @verify: checksum verification flag | |
394 | * @arch: expected ramdisk architecture | |
395 | * @rd_start: pointer to a ulong variable, will hold ramdisk start address | |
396 | * @rd_end: pointer to a ulong variable, will hold ramdisk end | |
397 | * | |
398 | * get_ramdisk() is responsible for finding a valid ramdisk image. | |
399 | * Curently supported are the following ramdisk sources: | |
400 | * - multicomponent kernel/ramdisk image, | |
401 | * - commandline provided address of decicated ramdisk image. | |
402 | * | |
403 | * returns: | |
404 | * rd_start and rd_end are set to ramdisk start/end addresses if | |
405 | * ramdisk image is found and valid | |
406 | * rd_start and rd_end are set to 0 if no ramdisk exists | |
407 | * board is reset if ramdisk image is found but corrupted | |
408 | */ | |
409 | void get_ramdisk (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], | |
410 | image_header_t *hdr, int verify, uint8_t arch, | |
411 | ulong *rd_start, ulong *rd_end) | |
412 | { | |
413 | ulong rd_addr; | |
414 | ulong rd_data, rd_len; | |
415 | image_header_t *rd_hdr; | |
416 | ||
417 | if (argc >= 3) { | |
418 | /* | |
419 | * Look for a '-' which indicates to ignore the | |
420 | * ramdisk argument | |
421 | */ | |
422 | if (strcmp(argv[2], "-") == 0) { | |
423 | debug ("## Skipping init Ramdisk\n"); | |
424 | rd_len = rd_data = 0; | |
425 | } else { | |
426 | /* | |
427 | * Check if there is an initrd image at the | |
428 | * address provided in the second bootm argument | |
429 | */ | |
430 | rd_addr = simple_strtoul (argv[2], NULL, 16); | |
431 | printf ("## Loading init Ramdisk Image at %08lx ...\n", | |
432 | rd_addr); | |
433 | ||
434 | rd_hdr = image_get_ramdisk (cmdtp, flag, argc, argv, | |
435 | rd_addr, arch, verify); | |
436 | ||
437 | rd_data = image_get_data (rd_hdr); | |
438 | rd_len = image_get_data_size (rd_hdr); | |
439 | ||
440 | #if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO) | |
441 | /* | |
442 | *we need to copy the ramdisk to SRAM to let Linux boot | |
443 | */ | |
444 | memmove ((void *)image_get_load (rd_hdr), | |
445 | (uchar *)rd_data, rd_len); | |
446 | ||
447 | rd_data = image_get_load (rd_hdr); | |
448 | #endif /* CONFIG_B2 || CONFIG_EVB4510 || CONFIG_ARMADILLO */ | |
449 | } | |
450 | ||
451 | } else if (image_check_type (hdr, IH_TYPE_MULTI)) { | |
452 | /* | |
453 | * Now check if we have a multifile image | |
454 | * Get second entry data start address and len | |
455 | */ | |
456 | show_boot_progress (13); | |
457 | printf ("## Loading init Ramdisk from multi component " | |
458 | "Image at %08lx ...\n", (ulong)hdr); | |
459 | image_multi_getimg (hdr, 1, &rd_data, &rd_len); | |
460 | } else { | |
461 | /* | |
462 | * no initrd image | |
463 | */ | |
464 | show_boot_progress (14); | |
465 | rd_len = rd_data = 0; | |
466 | } | |
467 | ||
468 | if (!rd_data) { | |
469 | debug ("## No init Ramdisk\n"); | |
470 | *rd_start = 0; | |
471 | *rd_end = 0; | |
472 | } else { | |
473 | *rd_start = rd_data; | |
474 | *rd_end = rd_data + rd_len; | |
475 | } | |
476 | debug (" ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n", | |
477 | *rd_start, *rd_end); | |
478 | } | |
479 | #endif /* USE_HOSTCC */ | |
480 |