/* * (C) Copyright 2003-2004 * Gary Jennejohn, DENX Software Engineering, gj@denx.de. * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #ifndef CFG_NAND_LEGACY #error CFG_NAND_LEGACY not defined in a file using the legacy NAND support! #endif #include #include #include #include #include #include "auto_update.h" #ifdef CONFIG_AUTO_UPDATE #if !(CONFIG_COMMANDS & CFG_CMD_FAT) #error "must define CFG_CMD_FAT" #endif extern au_image_t au_image[]; extern int N_AU_IMAGES; #define AU_DEBUG #undef AU_DEBUG #undef debug #ifdef AU_DEBUG #define debug(fmt,args...) printf (fmt ,##args) #else #define debug(fmt,args...) #endif /* AU_DEBUG */ #define LOAD_ADDR ((unsigned char *)0x100000) /* where to load files into memory */ #define MAX_LOADSZ 0x1e00000 /* externals */ extern int fat_register_device(block_dev_desc_t *, int); extern int file_fat_detectfs(void); extern long file_fat_read(const char *, void *, unsigned long); long do_fat_read (const char *filename, void *buffer, unsigned long maxsize, int dols); #ifdef CONFIG_VFD extern int trab_vfd (ulong); extern int transfer_pic(unsigned char, unsigned char *, int, int); #endif extern int flash_sect_erase(ulong, ulong); extern int flash_sect_protect (int, ulong, ulong); extern int flash_write (char *, ulong, ulong); /* change char* to void* to shutup the compiler */ extern block_dev_desc_t *get_dev (char*, int); #if (CONFIG_COMMANDS & CFG_CMD_NAND) /* references to names in cmd_nand.c */ #define NANDRW_READ 0x01 #define NANDRW_WRITE 0x00 #define NANDRW_JFFS2 0x02 #define NANDRW_JFFS2_SKIP 0x04 extern struct nand_chip nand_dev_desc[]; extern int nand_legacy_rw(struct nand_chip* nand, int cmd, size_t start, size_t len, size_t * retlen, u_char * buf); extern int nand_legacy_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean); #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ extern block_dev_desc_t ide_dev_desc[CFG_IDE_MAXDEVICE]; int au_check_cksum_valid(int i, long nbytes) { image_header_t *hdr; unsigned long checksum; hdr = (image_header_t *)LOAD_ADDR; if ((au_image[i].type == AU_FIRMWARE) && (au_image[i].size != ntohl(hdr->ih_size))) { printf ("Image %s has wrong size\n", au_image[i].name); return -1; } if (nbytes != (sizeof(*hdr) + ntohl(hdr->ih_size))) { printf ("Image %s bad total SIZE\n", au_image[i].name); return -1; } /* check the data CRC */ checksum = ntohl(hdr->ih_dcrc); if (crc32 (0, (uchar *)(LOAD_ADDR + sizeof(*hdr)), ntohl(hdr->ih_size)) != checksum) { printf ("Image %s bad data checksum\n", au_image[i].name); return -1; } return 0; } int au_check_header_valid(int i, long nbytes) { image_header_t *hdr; unsigned long checksum; hdr = (image_header_t *)LOAD_ADDR; /* check the easy ones first */ #undef CHECK_VALID_DEBUG #ifdef CHECK_VALID_DEBUG printf("magic %#x %#x ", ntohl(hdr->ih_magic), IH_MAGIC); printf("arch %#x %#x ", hdr->ih_arch, IH_CPU_PPC); printf("size %#x %#lx ", ntohl(hdr->ih_size), nbytes); printf("type %#x %#x ", hdr->ih_type, IH_TYPE_KERNEL); #endif if (nbytes < sizeof(*hdr)) { printf ("Image %s bad header SIZE\n", au_image[i].name); return -1; } if (ntohl(hdr->ih_magic) != IH_MAGIC || hdr->ih_arch != IH_CPU_PPC) { printf ("Image %s bad MAGIC or ARCH\n", au_image[i].name); return -1; } /* check the hdr CRC */ checksum = ntohl(hdr->ih_hcrc); hdr->ih_hcrc = 0; if (crc32 (0, (uchar *)hdr, sizeof(*hdr)) != checksum) { printf ("Image %s bad header checksum\n", au_image[i].name); return -1; } hdr->ih_hcrc = htonl(checksum); /* check the type - could do this all in one gigantic if() */ if ((au_image[i].type == AU_FIRMWARE) && (hdr->ih_type != IH_TYPE_FIRMWARE)) { printf ("Image %s wrong type\n", au_image[i].name); return -1; } if ((au_image[i].type == AU_SCRIPT) && (hdr->ih_type != IH_TYPE_SCRIPT)) { printf ("Image %s wrong type\n", au_image[i].name); return -1; } /* recycle checksum */ checksum = ntohl(hdr->ih_size); #if 0 /* test-only */ /* for kernel and app the image header must also fit into flash */ if (idx != IDX_DISK) checksum += sizeof(*hdr); /* check the size does not exceed space in flash. HUSH scripts */ /* all have ausize[] set to 0 */ if ((ausize[idx] != 0) && (ausize[idx] < checksum)) { printf ("Image %s is bigger than FLASH\n", au_image[i].name); return -1; } #endif return 0; } int au_do_update(int i, long sz) { image_header_t *hdr; char *addr; long start, end; int off, rc; uint nbytes; int k; #if (CONFIG_COMMANDS & CFG_CMD_NAND) int total; #endif hdr = (image_header_t *)LOAD_ADDR; switch (au_image[i].type) { case AU_SCRIPT: printf("Executing script %s\n", au_image[i].name); /* execute a script */ if (hdr->ih_type == IH_TYPE_SCRIPT) { addr = (char *)((char *)hdr + sizeof(*hdr)); /* stick a NULL at the end of the script, otherwise */ /* parse_string_outer() runs off the end. */ addr[ntohl(hdr->ih_size)] = 0; addr += 8; /* * Replace cr/lf with ; */ k = 0; while (addr[k] != 0) { if ((addr[k] == 10) || (addr[k] == 13)) { addr[k] = ';'; } k++; } run_command(addr, 0); return 0; } break; case AU_FIRMWARE: case AU_NOR: case AU_NAND: start = au_image[i].start; end = au_image[i].start + au_image[i].size - 1; /* * do not update firmware when image is already in flash. */ if (au_image[i].type == AU_FIRMWARE) { char *orig = (char*)start; char *new = (char *)((char *)hdr + sizeof(*hdr)); nbytes = ntohl(hdr->ih_size); while(--nbytes) { if (*orig++ != *new++) { break; } } if (!nbytes) { printf("Skipping firmware update - images are identical\n"); break; } } /* unprotect the address range */ /* this assumes that ONLY the firmware is protected! */ if (au_image[i].type == AU_FIRMWARE) { flash_sect_protect(0, start, end); } /* * erase the address range. */ if (au_image[i].type != AU_NAND) { printf("Updating NOR FLASH with image %s\n", au_image[i].name); debug ("flash_sect_erase(%lx, %lx);\n", start, end); flash_sect_erase(start, end); } else { #if (CONFIG_COMMANDS & CFG_CMD_NAND) printf("Updating NAND FLASH with image %s\n", au_image[i].name); debug ("nand_legacy_erase(%lx, %lx);\n", start, end); rc = nand_legacy_erase (nand_dev_desc, start, end - start + 1, 0); debug ("nand_legacy_erase returned %x\n", rc); #endif } udelay(10000); /* strip the header - except for the kernel and ramdisk */ if (au_image[i].type != AU_FIRMWARE) { addr = (char *)hdr; off = sizeof(*hdr); nbytes = sizeof(*hdr) + ntohl(hdr->ih_size); } else { addr = (char *)((char *)hdr + sizeof(*hdr)); off = 0; nbytes = ntohl(hdr->ih_size); } /* * copy the data from RAM to FLASH */ if (au_image[i].type != AU_NAND) { debug ("flash_write(%p, %lx %x)\n", addr, start, nbytes); rc = flash_write((char *)addr, start, nbytes); } else { #if (CONFIG_COMMANDS & CFG_CMD_NAND) debug ("nand_legacy_rw(%p, %lx %x)\n", addr, start, nbytes); rc = nand_legacy_rw(nand_dev_desc, NANDRW_WRITE | NANDRW_JFFS2, start, nbytes, (size_t *)&total, (uchar *)addr); debug ("nand_legacy_rw: ret=%x total=%d nbytes=%d\n", rc, total, nbytes); #endif } if (rc != 0) { printf("Flashing failed due to error %d\n", rc); return -1; } /* * check the dcrc of the copy */ if (au_image[i].type != AU_NAND) { rc = crc32 (0, (uchar *)(start + off), ntohl(hdr->ih_size)); } else { #if (CONFIG_COMMANDS & CFG_CMD_NAND) rc = nand_legacy_rw(nand_dev_desc, NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP, start, nbytes, (size_t *)&total, (uchar *)addr); rc = crc32 (0, (uchar *)(addr + off), ntohl(hdr->ih_size)); #endif } if (rc != ntohl(hdr->ih_dcrc)) { printf ("Image %s Bad Data Checksum After COPY\n", au_image[i].name); return -1; } /* protect the address range */ /* this assumes that ONLY the firmware is protected! */ if (au_image[i].type == AU_FIRMWARE) { flash_sect_protect(1, start, end); } break; default: printf("Wrong image type selected!\n"); } return 0; } static void process_macros (const char *input, char *output) { char c, prev; const char *varname_start = NULL; int inputcnt = strlen (input); int outputcnt = CFG_CBSIZE; int state = 0; /* 0 = waiting for '$' */ /* 1 = waiting for '(' or '{' */ /* 2 = waiting for ')' or '}' */ /* 3 = waiting for ''' */ #ifdef DEBUG_PARSER char *output_start = output; printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input), input); #endif prev = '\0'; /* previous character */ while (inputcnt && outputcnt) { c = *input++; inputcnt--; if (state!=3) { /* remove one level of escape characters */ if ((c == '\\') && (prev != '\\')) { if (inputcnt-- == 0) break; prev = c; c = *input++; } } switch (state) { case 0: /* Waiting for (unescaped) $ */ if ((c == '\'') && (prev != '\\')) { state = 3; break; } if ((c == '$') && (prev != '\\')) { state++; } else { *(output++) = c; outputcnt--; } break; case 1: /* Waiting for ( */ if (c == '(' || c == '{') { state++; varname_start = input; } else { state = 0; *(output++) = '$'; outputcnt--; if (outputcnt) { *(output++) = c; outputcnt--; } } break; case 2: /* Waiting for ) */ if (c == ')' || c == '}') { int i; char envname[CFG_CBSIZE], *envval; int envcnt = input-varname_start-1; /* Varname # of chars */ /* Get the varname */ for (i = 0; i < envcnt; i++) { envname[i] = varname_start[i]; } envname[i] = 0; /* Get its value */ envval = getenv (envname); /* Copy into the line if it exists */ if (envval != NULL) while ((*envval) && outputcnt) { *(output++) = *(envval++); outputcnt--; } /* Look for another '$' */ state = 0; } break; case 3: /* Waiting for ' */ if ((c == '\'') && (prev != '\\')) { state = 0; } else { *(output++) = c; outputcnt--; } break; } prev = c; } if (outputcnt) *output = 0; #ifdef DEBUG_PARSER printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", strlen(output_start), output_start); #endif } /* * this is called from board_init() after the hardware has been set up * and is usable. That seems like a good time to do this. * Right now the return value is ignored. */ int do_auto_update(void) { block_dev_desc_t *stor_dev; long sz; int i, res, cnt, old_ctrlc, got_ctrlc; char buffer[32]; char str[80]; /* * Check whether a CompactFlash is inserted */ if (ide_dev_desc[0].type == DEV_TYPE_UNKNOWN) { return -1; /* no disk detected! */ } /* check whether it has a partition table */ stor_dev = get_dev("ide", 0); if (stor_dev == NULL) { debug ("Uknown device type\n"); return -1; } if (fat_register_device(stor_dev, 1) != 0) { debug ("Unable to register ide disk 0:1 for fatls\n"); return -1; } /* * Check if magic file is present */ if (do_fat_read(AU_MAGIC_FILE, buffer, sizeof(buffer), LS_NO) <= 0) { return -1; } #ifdef CONFIG_AUTO_UPDATE_SHOW board_auto_update_show(1); #endif puts("\nAutoUpdate Disk detected! Trying to update system...\n"); /* make sure that we see CTRL-C and save the old state */ old_ctrlc = disable_ctrlc(0); /* just loop thru all the possible files */ for (i = 0; i < N_AU_IMAGES; i++) { /* * Try to expand the environment var in the fname */ process_macros(au_image[i].name, str); strcpy(au_image[i].name, str); printf("Reading %s ...", au_image[i].name); /* just read the header */ sz = do_fat_read(au_image[i].name, LOAD_ADDR, sizeof(image_header_t), LS_NO); debug ("read %s sz %ld hdr %d\n", au_image[i].name, sz, sizeof(image_header_t)); if (sz <= 0 || sz < sizeof(image_header_t)) { puts(" not found\n"); continue; } if (au_check_header_valid(i, sz) < 0) { puts(" header not valid\n"); continue; } sz = do_fat_read(au_image[i].name, LOAD_ADDR, MAX_LOADSZ, LS_NO); debug ("read %s sz %ld hdr %d\n", au_image[i].name, sz, sizeof(image_header_t)); if (sz <= 0 || sz <= sizeof(image_header_t)) { puts(" not found\n"); continue; } if (au_check_cksum_valid(i, sz) < 0) { puts(" checksum not valid\n"); continue; } puts(" done\n"); do { res = au_do_update(i, sz); /* let the user break out of the loop */ if (ctrlc() || had_ctrlc()) { clear_ctrlc(); if (res < 0) got_ctrlc = 1; break; } cnt++; } while (res < 0); } /* restore the old state */ disable_ctrlc(old_ctrlc); puts("AutoUpdate finished\n\n"); #ifdef CONFIG_AUTO_UPDATE_SHOW board_auto_update_show(0); #endif return 0; } int auto_update(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { do_auto_update(); return 0; } U_BOOT_CMD( autoupd, 1, 1, auto_update, "autoupd - Automatically update images\n", NULL ); #endif /* CONFIG_AUTO_UPDATE */