Commit cdb97a6678826f85e7c69eae6a1c113d034c9b10
1 parent
dd520bf314
Exists in
master
and in
54 other branches
automatic update mechanism
Showing 4 changed files with 478 additions and 2 deletions Side-by-side Diff
board/mcc200/Makefile
board/mcc200/auto_update.c
1 | +/* | |
2 | + * (C) Copyright 2006 | |
3 | + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
4 | + * | |
5 | + * This program is free software; you can redistribute it and/or | |
6 | + * modify it under the terms of the GNU General Public License as | |
7 | + * published by the Free Software Foundation; either version 2 of | |
8 | + * the License, or (at your option) any later version. | |
9 | + * | |
10 | + * This program is distributed in the hope that it will be useful, | |
11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | + * GNU General Public License for more details. | |
14 | + * | |
15 | + * You should have received a copy of the GNU General Public License | |
16 | + * along with this program; if not, write to the Free Software | |
17 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
18 | + * MA 02111-1307 USA | |
19 | + */ | |
20 | +#include <common.h> | |
21 | +#include <command.h> | |
22 | +#include <malloc.h> | |
23 | +#include <image.h> | |
24 | +#include <asm/byteorder.h> | |
25 | +#include <usb.h> | |
26 | + | |
27 | +#ifdef CFG_HUSH_PARSER | |
28 | +#include <hush.h> | |
29 | +#endif | |
30 | + | |
31 | + | |
32 | +#ifdef CONFIG_AUTO_UPDATE | |
33 | + | |
34 | +#ifndef CONFIG_USB_OHCI | |
35 | +#error "must define CONFIG_USB_OHCI" | |
36 | +#endif | |
37 | + | |
38 | +#ifndef CONFIG_USB_STORAGE | |
39 | +#error "must define CONFIG_USB_STORAGE" | |
40 | +#endif | |
41 | + | |
42 | +#ifndef CFG_HUSH_PARSER | |
43 | +#error "must define CFG_HUSH_PARSER" | |
44 | +#endif | |
45 | + | |
46 | +#if !(CONFIG_COMMANDS & CFG_CMD_FAT) | |
47 | +#error "must define CFG_CMD_FAT" | |
48 | +#endif | |
49 | + | |
50 | +/* | |
51 | + * Check whether a USB memory stick is plugged in. | |
52 | + * If one is found: | |
53 | + * 1) if prepare.img ist found load it into memory. If it is | |
54 | + * valid then run it. | |
55 | + * 2) if preinst.img is found load it into memory. If it is | |
56 | + * valid then run it. Update the EEPROM. | |
57 | + * 3) if firmw_01.img is found load it into memory. If it is valid, | |
58 | + * burn it into FLASH and update the EEPROM. | |
59 | + * 4) if kernl_01.img is found load it into memory. If it is valid, | |
60 | + * burn it into FLASH and update the EEPROM. | |
61 | + * 5) if app.img is found load it into memory. If it is valid, | |
62 | + * burn it into FLASH and update the EEPROM. | |
63 | + * 6) if disk.img is found load it into memory. If it is valid, | |
64 | + * burn it into FLASH and update the EEPROM. | |
65 | + * 7) if postinst.img is found load it into memory. If it is | |
66 | + * valid then run it. Update the EEPROM. | |
67 | + */ | |
68 | + | |
69 | +#undef AU_DEBUG | |
70 | + | |
71 | +#undef debug | |
72 | +#ifdef AU_DEBUG | |
73 | +#define debug(fmt,args...) printf (fmt ,##args) | |
74 | +#else | |
75 | +#define debug(fmt,args...) | |
76 | +#endif /* AU_DEBUG */ | |
77 | + | |
78 | +/* possible names of files on the USB stick. */ | |
79 | +#define AU_FIRMWARE "u-boot.img" | |
80 | +#define AU_KERNEL "kernel.img" | |
81 | + | |
82 | +struct flash_layout | |
83 | +{ | |
84 | + long start; | |
85 | + long end; | |
86 | +}; | |
87 | + | |
88 | +/* layout of the FLASH. ST = start address, ND = end address. */ | |
89 | +#define AU_FL_FIRMWARE_ST 0xfC000000 | |
90 | +#define AU_FL_FIRMWARE_ND 0xfC03FFFF | |
91 | +#define AU_FL_KERNEL_ST 0xfC0C0000 | |
92 | +#define AU_FL_KERNEL_ND 0xfC1BFFFF | |
93 | + | |
94 | +static int au_usb_stor_curr_dev; /* current device */ | |
95 | + | |
96 | +/* index of each file in the following arrays */ | |
97 | +#define IDX_FIRMWARE 0 | |
98 | +#define IDX_KERNEL 1 | |
99 | + | |
100 | +/* max. number of files which could interest us */ | |
101 | +#define AU_MAXFILES 2 | |
102 | + | |
103 | +/* pointers to file names */ | |
104 | +char *aufile[AU_MAXFILES]; | |
105 | + | |
106 | +/* sizes of flash areas for each file */ | |
107 | +long ausize[AU_MAXFILES]; | |
108 | + | |
109 | +/* array of flash areas start and end addresses */ | |
110 | +struct flash_layout aufl_layout[AU_MAXFILES] = { \ | |
111 | + {AU_FL_FIRMWARE_ST, AU_FL_FIRMWARE_ND,}, \ | |
112 | + {AU_FL_KERNEL_ST, AU_FL_KERNEL_ND,}, \ | |
113 | +}; | |
114 | + | |
115 | +/* where to load files into memory */ | |
116 | +#define LOAD_ADDR ((unsigned char *)0x00200000) | |
117 | + | |
118 | +/* the app is the largest image */ | |
119 | +#define MAX_LOADSZ ausize[IDX_KERNEL] | |
120 | + | |
121 | +/*i2c address of the keypad status*/ | |
122 | +#define I2C_PSOC_KEYPAD_ADDR 0x53 | |
123 | + | |
124 | +/* keypad mask */ | |
125 | +#define KEYPAD_ROW 3 | |
126 | +#define KEYPAD_COL 3 | |
127 | +#define KEYPAD_MASK_LO ((1<<(KEYPAD_COL-1+(KEYPAD_ROW*4-4)))&0xFF) | |
128 | +#define KEYPAD_MASK_HI ((1<<(KEYPAD_COL-1+(KEYPAD_ROW*4-4)))>>8) | |
129 | + | |
130 | +/* externals */ | |
131 | +extern int fat_register_device(block_dev_desc_t *, int); | |
132 | +extern int file_fat_detectfs(void); | |
133 | +extern long file_fat_read(const char *, void *, unsigned long); | |
134 | +extern int i2c_read (unsigned char, unsigned int, int , unsigned char* , int); | |
135 | +extern int flash_sect_erase(ulong, ulong); | |
136 | +extern int flash_sect_protect (int, ulong, ulong); | |
137 | +extern int flash_write (char *, ulong, ulong); | |
138 | +/* change char* to void* to shutup the compiler */ | |
139 | +extern block_dev_desc_t *get_dev (char*, int); | |
140 | +extern int u_boot_hush_start(void); | |
141 | + | |
142 | +int | |
143 | +au_check_cksum_valid(int idx, long nbytes) | |
144 | +{ | |
145 | + image_header_t *hdr; | |
146 | + unsigned long checksum; | |
147 | + | |
148 | + hdr = (image_header_t *)LOAD_ADDR; | |
149 | + | |
150 | + if (nbytes != (sizeof(*hdr) + ntohl(hdr->ih_size))) | |
151 | + { | |
152 | + printf ("Image %s bad total SIZE\n", aufile[idx]); | |
153 | + return -1; | |
154 | + } | |
155 | + /* check the data CRC */ | |
156 | + checksum = ntohl(hdr->ih_dcrc); | |
157 | + | |
158 | + if (crc32 (0, (uchar *)(LOAD_ADDR + sizeof(*hdr)), ntohl(hdr->ih_size)) | |
159 | + != checksum) | |
160 | + { | |
161 | + printf ("Image %s bad data checksum\n", aufile[idx]); | |
162 | + return -1; | |
163 | + } | |
164 | + return 0; | |
165 | +} | |
166 | + | |
167 | +int | |
168 | +au_check_header_valid(int idx, long nbytes) | |
169 | +{ | |
170 | + image_header_t *hdr; | |
171 | + unsigned long checksum; | |
172 | + unsigned char buf[4]; | |
173 | + | |
174 | + hdr = (image_header_t *)LOAD_ADDR; | |
175 | + /* check the easy ones first */ | |
176 | +#undef CHECK_VALID_DEBUG | |
177 | +#ifdef CHECK_VALID_DEBUG | |
178 | + printf("magic %#x %#x ", ntohl(hdr->ih_magic), IH_MAGIC); | |
179 | + printf("arch %#x %#x ", hdr->ih_arch, IH_CPU_ARM); | |
180 | + printf("size %#x %#lx ", ntohl(hdr->ih_size), nbytes); | |
181 | + printf("type %#x %#x ", hdr->ih_type, IH_TYPE_KERNEL); | |
182 | +#endif | |
183 | + if (nbytes < sizeof(*hdr)) | |
184 | + { | |
185 | + printf ("Image %s bad header SIZE\n", aufile[idx]); | |
186 | + return -1; | |
187 | + } | |
188 | + if (ntohl(hdr->ih_magic) != IH_MAGIC || hdr->ih_arch != IH_CPU_PPC) | |
189 | + { | |
190 | + printf ("Image %s bad MAGIC or ARCH\n", aufile[idx]); | |
191 | + return -1; | |
192 | + } | |
193 | + /* check the hdr CRC */ | |
194 | + checksum = ntohl(hdr->ih_hcrc); | |
195 | + hdr->ih_hcrc = 0; | |
196 | + | |
197 | + if (crc32 (0, (uchar *)hdr, sizeof(*hdr)) != checksum) { | |
198 | + printf ("Image %s bad header checksum\n", aufile[idx]); | |
199 | + return -1; | |
200 | + } | |
201 | + hdr->ih_hcrc = htonl(checksum); | |
202 | + /* check the type - could do this all in one gigantic if() */ | |
203 | + if ((idx == IDX_FIRMWARE) && (hdr->ih_type != IH_TYPE_FIRMWARE)) { | |
204 | + printf ("Image %s wrong type\n", aufile[idx]); | |
205 | + return -1; | |
206 | + } | |
207 | + if ((idx == IDX_KERNEL) && (hdr->ih_type != IH_TYPE_KERNEL)) { | |
208 | + printf ("Image %s wrong type\n", aufile[idx]); | |
209 | + return -1; | |
210 | + } | |
211 | + /* recycle checksum */ | |
212 | + checksum = ntohl(hdr->ih_size); | |
213 | + /* for kernel and app the image header must also fit into flash */ | |
214 | + if (idx != IDX_FIRMWARE) | |
215 | + checksum += sizeof(*hdr); | |
216 | + /* check the size does not exceed space in flash. HUSH scripts */ | |
217 | + /* all have ausize[] set to 0 */ | |
218 | + if ((ausize[idx] != 0) && (ausize[idx] < checksum)) { | |
219 | + printf ("Image %s is bigger than FLASH\n", aufile[idx]); | |
220 | + return -1; | |
221 | + } | |
222 | + return 0; | |
223 | +} | |
224 | + | |
225 | + | |
226 | +int | |
227 | +au_do_update(int idx, long sz) | |
228 | +{ | |
229 | + image_header_t *hdr; | |
230 | + char *addr; | |
231 | + long start, end; | |
232 | + int off, rc; | |
233 | + uint nbytes; | |
234 | + | |
235 | + hdr = (image_header_t *)LOAD_ADDR; | |
236 | + | |
237 | + /* execute a script */ | |
238 | + if (hdr->ih_type == IH_TYPE_SCRIPT) { | |
239 | + addr = (char *)((char *)hdr + sizeof(*hdr)); | |
240 | + /* stick a NULL at the end of the script, otherwise */ | |
241 | + /* parse_string_outer() runs off the end. */ | |
242 | + addr[ntohl(hdr->ih_size)] = 0; | |
243 | + addr += 8; | |
244 | + parse_string_outer(addr, FLAG_PARSE_SEMICOLON); | |
245 | + return 0; | |
246 | + } | |
247 | + | |
248 | + start = aufl_layout[idx].start; | |
249 | + end = aufl_layout[idx].end; | |
250 | + | |
251 | + /* unprotect the address range */ | |
252 | + /* this assumes that ONLY the firmware is protected! */ | |
253 | + if (idx == IDX_FIRMWARE) { | |
254 | +#undef AU_UPDATE_TEST | |
255 | +#ifdef AU_UPDATE_TEST | |
256 | + /* erase it where Linux goes */ | |
257 | + start = aufl_layout[1].start; | |
258 | + end = aufl_layout[1].end; | |
259 | +#endif | |
260 | + flash_sect_protect(0, start, end); | |
261 | + } | |
262 | + | |
263 | + /* | |
264 | + * erase the address range. | |
265 | + */ | |
266 | + debug ("flash_sect_erase(%lx, %lx);\n", start, end); | |
267 | + flash_sect_erase(start, end); | |
268 | + wait_ms(100); | |
269 | + /* strip the header - except for the kernel and ramdisk */ | |
270 | + if (hdr->ih_type == IH_TYPE_KERNEL) { | |
271 | + addr = (char *)hdr; | |
272 | + off = sizeof(*hdr); | |
273 | + nbytes = sizeof(*hdr) + ntohl(hdr->ih_size); | |
274 | + } else { | |
275 | + addr = (char *)((char *)hdr + sizeof(*hdr)); | |
276 | +#ifdef AU_UPDATE_TEST | |
277 | + /* copy it to where Linux goes */ | |
278 | + if (idx == IDX_FIRMWARE) | |
279 | + start = aufl_layout[1].start; | |
280 | +#endif | |
281 | + off = 0; | |
282 | + nbytes = ntohl(hdr->ih_size); | |
283 | + } | |
284 | + | |
285 | + /* copy the data from RAM to FLASH */ | |
286 | + debug ("flash_write(%p, %lx %x)\n", addr, start, nbytes); | |
287 | + rc = flash_write(addr, start, nbytes); | |
288 | + if (rc != 0) { | |
289 | + printf("Flashing failed due to error %d\n", rc); | |
290 | + return -1; | |
291 | + } | |
292 | + | |
293 | + /* check the dcrc of the copy */ | |
294 | + if (crc32 (0, (uchar *)(start + off), ntohl(hdr->ih_size)) != ntohl(hdr->ih_dcrc)) { | |
295 | + printf ("Image %s Bad Data Checksum After COPY\n", aufile[idx]); | |
296 | + return -1; | |
297 | + } | |
298 | + | |
299 | + /* protect the address range */ | |
300 | + /* this assumes that ONLY the firmware is protected! */ | |
301 | + if (idx == IDX_FIRMWARE) | |
302 | + flash_sect_protect(1, start, end); | |
303 | + return 0; | |
304 | +} | |
305 | + | |
306 | + | |
307 | +/* | |
308 | + * this is called from board_init() after the hardware has been set up | |
309 | + * and is usable. That seems like a good time to do this. | |
310 | + * Right now the return value is ignored. | |
311 | + */ | |
312 | +int | |
313 | +do_auto_update(void) | |
314 | +{ | |
315 | + block_dev_desc_t *stor_dev; | |
316 | + long sz; | |
317 | + int i, res, bitmap_first, cnt, old_ctrlc, got_ctrlc; | |
318 | + char *env; | |
319 | + long start, end; | |
320 | + char keypad_status1[2] = {0,0}, keypad_status2[2] = {0,0}; | |
321 | + | |
322 | + /* | |
323 | + * Read keypad status | |
324 | + */ | |
325 | + i2c_read(I2C_PSOC_KEYPAD_ADDR, 0, 0, keypad_status1, 2); | |
326 | + wait_ms(500); | |
327 | + i2c_read(I2C_PSOC_KEYPAD_ADDR, 0, 0, keypad_status2, 2); | |
328 | + | |
329 | + /* | |
330 | + * Check keypad | |
331 | + */ | |
332 | + if ( !(keypad_status1[0] & KEYPAD_MASK_HI) || | |
333 | + (keypad_status1[0] != keypad_status2[0])) { | |
334 | + return 0; | |
335 | + } | |
336 | + if ( !(keypad_status1[1] & KEYPAD_MASK_LO) || | |
337 | + (keypad_status1[1] != keypad_status2[1])) { | |
338 | + return 0; | |
339 | + } | |
340 | + au_usb_stor_curr_dev = -1; | |
341 | + /* start USB */ | |
342 | + if (usb_stop() < 0) { | |
343 | + debug ("usb_stop failed\n"); | |
344 | + return -1; | |
345 | + } | |
346 | + if (usb_init() < 0) { | |
347 | + debug ("usb_init failed\n"); | |
348 | + return -1; | |
349 | + } | |
350 | + /* | |
351 | + * check whether a storage device is attached (assume that it's | |
352 | + * a USB memory stick, since nothing else should be attached). | |
353 | + */ | |
354 | + au_usb_stor_curr_dev = usb_stor_scan(0); | |
355 | + if (au_usb_stor_curr_dev == -1) { | |
356 | + debug ("No device found. Not initialized?\n"); | |
357 | + return -1; | |
358 | + } | |
359 | + /* check whether it has a partition table */ | |
360 | + stor_dev = get_dev("usb", 0); | |
361 | + if (stor_dev == NULL) { | |
362 | + debug ("uknown device type\n"); | |
363 | + return -1; | |
364 | + } | |
365 | + if (fat_register_device(stor_dev, 1) != 0) { | |
366 | + debug ("Unable to use USB %d:%d for fatls\n", | |
367 | + au_usb_stor_curr_dev, 1); | |
368 | + return -1; | |
369 | + } | |
370 | + if (file_fat_detectfs() != 0) { | |
371 | + debug ("file_fat_detectfs failed\n"); | |
372 | + } | |
373 | + | |
374 | + /* initialize the array of file names */ | |
375 | + memset(aufile, 0, sizeof(aufile)); | |
376 | + aufile[IDX_FIRMWARE] = AU_FIRMWARE; | |
377 | + aufile[IDX_KERNEL] = AU_KERNEL; | |
378 | + /* initialize the array of flash sizes */ | |
379 | + memset(ausize, 0, sizeof(ausize)); | |
380 | + ausize[IDX_FIRMWARE] = (AU_FL_FIRMWARE_ND + 1) - AU_FL_FIRMWARE_ST; | |
381 | + ausize[IDX_KERNEL] = (AU_FL_KERNEL_ND + 1) - AU_FL_KERNEL_ST; | |
382 | + /* | |
383 | + * now check whether start and end are defined using environment | |
384 | + * variables. | |
385 | + */ | |
386 | + start = -1; | |
387 | + end = 0; | |
388 | + env = getenv("firmware_st"); | |
389 | + if (env != NULL) | |
390 | + start = simple_strtoul(env, NULL, 16); | |
391 | + env = getenv("firmware_nd"); | |
392 | + if (env != NULL) | |
393 | + end = simple_strtoul(env, NULL, 16); | |
394 | + if (start >= 0 && end && end > start) { | |
395 | + ausize[IDX_FIRMWARE] = (end + 1) - start; | |
396 | + aufl_layout[0].start = start; | |
397 | + aufl_layout[0].end = end; | |
398 | + } | |
399 | + start = -1; | |
400 | + end = 0; | |
401 | + env = getenv("kernel_st"); | |
402 | + if (env != NULL) | |
403 | + start = simple_strtoul(env, NULL, 16); | |
404 | + env = getenv("kernel_nd"); | |
405 | + if (env != NULL) | |
406 | + end = simple_strtoul(env, NULL, 16); | |
407 | + if (start >= 0 && end && end > start) { | |
408 | + ausize[IDX_KERNEL] = (end + 1) - start; | |
409 | + aufl_layout[1].start = start; | |
410 | + aufl_layout[1].end = end; | |
411 | + } | |
412 | + /* make certain that HUSH is runnable */ | |
413 | + u_boot_hush_start(); | |
414 | + /* make sure that we see CTRL-C and save the old state */ | |
415 | + old_ctrlc = disable_ctrlc(0); | |
416 | + | |
417 | + bitmap_first = 0; | |
418 | + /* just loop thru all the possible files */ | |
419 | + for (i = 0; i < AU_MAXFILES; i++) { | |
420 | + /* just read the header */ | |
421 | + sz = file_fat_read(aufile[i], LOAD_ADDR, sizeof(image_header_t)); | |
422 | + debug ("read %s sz %ld hdr %d\n", | |
423 | + aufile[i], sz, sizeof(image_header_t)); | |
424 | + if (sz <= 0 || sz < sizeof(image_header_t)) { | |
425 | + debug ("%s not found\n", aufile[i]); | |
426 | + continue; | |
427 | + } | |
428 | + if (au_check_header_valid(i, sz) < 0) { | |
429 | + debug ("%s header not valid\n", aufile[i]); | |
430 | + continue; | |
431 | + } | |
432 | + sz = file_fat_read(aufile[i], LOAD_ADDR, MAX_LOADSZ); | |
433 | + debug ("read %s sz %ld hdr %d\n", | |
434 | + aufile[i], sz, sizeof(image_header_t)); | |
435 | + if (sz <= 0 || sz <= sizeof(image_header_t)) { | |
436 | + debug ("%s not found\n", aufile[i]); | |
437 | + continue; | |
438 | + } | |
439 | + if (au_check_cksum_valid(i, sz) < 0) { | |
440 | + debug ("%s checksum not valid\n", aufile[i]); | |
441 | + continue; | |
442 | + } | |
443 | + /* this is really not a good idea, but it's what the */ | |
444 | + /* customer wants. */ | |
445 | + cnt = 0; | |
446 | + got_ctrlc = 0; | |
447 | + do { | |
448 | + res = au_do_update(i, sz); | |
449 | + /* let the user break out of the loop */ | |
450 | + if (ctrlc() || had_ctrlc()) { | |
451 | + clear_ctrlc(); | |
452 | + if (res < 0) | |
453 | + got_ctrlc = 1; | |
454 | + break; | |
455 | + } | |
456 | + cnt++; | |
457 | +#ifdef AU_TEST_ONLY | |
458 | + } while (res < 0 && cnt < 3); | |
459 | + if (cnt < 3) | |
460 | +#else | |
461 | + } while (res < 0); | |
462 | +#endif | |
463 | + } | |
464 | + usb_stop(); | |
465 | + /* restore the old state */ | |
466 | + disable_ctrlc(old_ctrlc); | |
467 | + return 0; | |
468 | +} | |
469 | +#endif /* CONFIG_AUTO_UPDATE */ |
board/mcc200/mcc200.c
... | ... | @@ -44,6 +44,7 @@ |
44 | 44 | |
45 | 45 | extern flash_info_t flash_info[]; /* FLASH chips info */ |
46 | 46 | |
47 | +extern int do_auto_update(void); | |
47 | 48 | ulong flash_get_size (ulong base, int banknum); |
48 | 49 | |
49 | 50 | #ifndef CFG_RAMBOOT |
... | ... | @@ -227,6 +228,10 @@ |
227 | 228 | { |
228 | 229 | ulong flash_sup_end, snum; |
229 | 230 | |
231 | +#ifdef CONFIG_AUTO_UPDATE | |
232 | + /* this has priority over all else */ | |
233 | + do_auto_update(); | |
234 | +#endif | |
230 | 235 | /* |
231 | 236 | * Adjust flash start and offset to detected values |
232 | 237 | */ |
include/configs/mcc200.h
... | ... | @@ -94,6 +94,8 @@ |
94 | 94 | #define CONFIG_USB_OHCI |
95 | 95 | #define ADD_USB_CMD CFG_CMD_USB | CFG_CMD_FAT |
96 | 96 | #define CONFIG_USB_STORAGE |
97 | +/* automatic software updates (see board/mcc200/auto_update.c) */ | |
98 | +#define CONFIG_AUTO_UPDATE 1 | |
97 | 99 | |
98 | 100 | /* |
99 | 101 | * Supported commands |
... | ... | @@ -173,7 +175,7 @@ |
173 | 175 | * I2C configuration |
174 | 176 | */ |
175 | 177 | #define CONFIG_HARD_I2C 1 /* I2C with hardware support */ |
176 | -#define CFG_I2C_MODULE 1 /* Select I2C module #1 or #2 */ | |
178 | +#define CFG_I2C_MODULE 2 /* Select I2C module #1 or #2 */ | |
177 | 179 | |
178 | 180 | #define CFG_I2C_SPEED 100000 /* 100 kHz */ |
179 | 181 | #define CFG_I2C_SLAVE 0x7F |