mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-18 18:59:44 +00:00
d5934ad775
This patch adds framework for dual format images. Format detection is added and the bootm controll flow is updated to include cases for new FIT format uImages. When the legacy (image_header based) format is detected appropriate legacy specific handling is invoked. For the new (FIT based) format uImages dual boot framework has a minial support, that will only print out a corresponding debug messages. Implementation of the FIT specific handling will be added in following patches. Signed-off-by: Marian Balakowicz <m8@semihalf.com>
572 lines
14 KiB
C
572 lines
14 KiB
C
/*
|
|
* (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 <common.h>
|
|
|
|
#include <command.h>
|
|
#include <image.h>
|
|
#include <asm/byteorder.h>
|
|
#if defined(CFG_NAND_LEGACY)
|
|
#include <linux/mtd/nand_legacy.h>
|
|
#endif
|
|
#include <fat.h>
|
|
#include <part.h>
|
|
|
|
#include "auto_update.h"
|
|
|
|
#ifdef CONFIG_AUTO_UPDATE
|
|
|
|
#if !defined(CONFIG_CMD_FAT)
|
|
#error "must define CONFIG_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);
|
|
|
|
#if defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY)
|
|
/* 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
|
|
|
|
extern block_dev_desc_t ide_dev_desc[CFG_IDE_MAXDEVICE];
|
|
|
|
|
|
int au_check_cksum_valid(int i, long nbytes)
|
|
{
|
|
image_header_t *hdr;
|
|
|
|
hdr = (image_header_t *)LOAD_ADDR;
|
|
#if defined(CONFIG_FIT)
|
|
if (gen_image_get_format ((void *)hdr) != IMAGE_FORMAT_LEGACY) {
|
|
puts ("Non legacy image format not supported\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
if ((au_image[i].type == AU_FIRMWARE) &&
|
|
(au_image[i].size != image_get_data_size (hdr))) {
|
|
printf ("Image %s has wrong size\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
|
|
if (nbytes != (image_get_image_size (hdr))) {
|
|
printf ("Image %s bad total SIZE\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
|
|
/* check the data CRC */
|
|
if (!image_check_dcrc (hdr)) {
|
|
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;
|
|
#if defined(CONFIG_FIT)
|
|
if (gen_image_get_format ((void *)hdr) != IMAGE_FORMAT_LEGACY) {
|
|
puts ("Non legacy image format not supported\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* check the easy ones first */
|
|
#undef CHECK_VALID_DEBUG
|
|
#ifdef CHECK_VALID_DEBUG
|
|
printf("magic %#x %#x ", image_get_magic (hdr), IH_MAGIC);
|
|
printf("arch %#x %#x ", image_get_arch (hdr), IH_ARCH_PPC);
|
|
printf("size %#x %#lx ", image_get_data_size (hdr), nbytes);
|
|
printf("type %#x %#x ", image_get_type (hdr), IH_TYPE_KERNEL);
|
|
#endif
|
|
if (nbytes < image_get_header_size ())
|
|
{
|
|
printf ("Image %s bad header SIZE\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
if (!image_check_magic (hdr) || !image_check_arch (hdr, IH_ARCH_PPC))
|
|
{
|
|
printf ("Image %s bad MAGIC or ARCH\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
if (!image_check_hcrc (hdr)) {
|
|
printf ("Image %s bad header checksum\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
|
|
/* check the type - could do this all in one gigantic if() */
|
|
if ((au_image[i].type == AU_FIRMWARE) && !image_check_type (hdr, IH_TYPE_FIRMWARE)) {
|
|
printf ("Image %s wrong type\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
if ((au_image[i].type == AU_SCRIPT) && !image_check_type (hdr, IH_TYPE_SCRIPT)) {
|
|
printf ("Image %s wrong type\n", au_image[i].name);
|
|
return -1;
|
|
}
|
|
|
|
/* recycle checksum */
|
|
checksum = image_get_data_size (hdr);
|
|
|
|
#if 0 /* test-only */
|
|
/* for kernel and app the image header must also fit into flash */
|
|
if (idx != IDX_DISK)
|
|
checksum += image_get_header_size ();
|
|
/* 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 defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY)
|
|
int total;
|
|
#endif
|
|
|
|
hdr = (image_header_t *)LOAD_ADDR;
|
|
#if defined(CONFIG_FIT)
|
|
if (gen_image_get_format ((void *)hdr) != IMAGE_FORMAT_LEGACY) {
|
|
puts ("Non legacy image format not supported\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
switch (au_image[i].type) {
|
|
case AU_SCRIPT:
|
|
printf("Executing script %s\n", au_image[i].name);
|
|
|
|
/* execute a script */
|
|
if (image_check_type (hdr, IH_TYPE_SCRIPT)) {
|
|
addr = (char *)((char *)hdr + image_get_header_size ());
|
|
/* stick a NULL at the end of the script, otherwise */
|
|
/* parse_string_outer() runs off the end. */
|
|
addr[image_get_data_size (hdr)] = 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 + image_get_header_size ());
|
|
nbytes = image_get_data_size (hdr);
|
|
|
|
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 defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY)
|
|
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 = image_get_header_size ();
|
|
nbytes = image_get_image_size (hdr);
|
|
} else {
|
|
addr = (char *)((char *)hdr + image_get_header_size ());
|
|
off = 0;
|
|
nbytes = image_get_data_size (hdr);
|
|
}
|
|
|
|
/*
|
|
* 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 defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY)
|
|
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);
|
|
#else
|
|
rc = -1;
|
|
#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), image_get_data_size (hdr));
|
|
} else {
|
|
#if defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY)
|
|
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), image_get_data_size (hdr));
|
|
#endif
|
|
}
|
|
if (rc != image_get_dcrc (hdr)) {
|
|
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, image_get_header_size (), LS_NO);
|
|
debug ("read %s sz %ld hdr %d\n",
|
|
au_image[i].name, sz, image_get_header_size ());
|
|
if (sz <= 0 || sz < image_get_header_size ()) {
|
|
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, image_get_header_size ());
|
|
if (sz <= 0 || sz <= image_get_header_size ()) {
|
|
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 */
|