gk7205v200-uboot/product/update/auto_update.c
2025-08-07 17:13:54 +08:00

554 lines
13 KiB
C
Executable File

/*
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
*/
#include <common.h>
#include <environment.h>
#include <command.h>
#include <malloc.h>
#include <image.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <spi_flash.h>
#include <linux/mtd/mtd.h>
#include <fat.h>
#if (CONFIG_AUTO_UPDATE == 1) /* cover the whole file */
#ifdef CONFIG_AUTO_SD_UPDATE
#ifndef CONFIG_MMC
#error "should have defined CONFIG_MMC"
#endif
#include <mmc.h>
#include "mmc_init.c"
#endif
#if defined CONFIG_AUTO_USB_UPDATE
#if !defined CONFIG_USB_OHCI && !defined CONFIG_USB_XHCI
#error "should have defined CONFIG_USB_OHCI or CONFIG_USB_XHCI"
#endif
#ifndef CONFIG_USB_STORAGE
#error "should have defined CONFIG_USB_STORAGE"
#endif
#include <usb.h>
#include "usb_init.c"
#endif
#undef AU_DEBUG
#undef debug
#ifdef AU_DEBUG
#define debug(fmt, args...) printf(fmt, ##args)
#else
#define debug(fmt, args...)
#endif /* AU_DEBUG */
/* possible names of files on the medium. */
#define AU_FIRMWARE "u-boot"
#define AU_KERNEL "kernel"
#define AU_ROOTFS "rootfs"
#define NAME_LEN 20
#define ENV_LEN 20
#define PERCENT 100
#define HEX 16
#define COUNT 3
#define MMC2 2
#define MAX_HZ 1000000UL
#define SPI_MODE3 3
struct flash_layout {
long start;
long end;
};
static struct spi_flash *flash;
struct medium_interface {
char name[NAME_LEN];
int (*init)(void);
void (*exit)(void);
};
#define MAX_UPDATE_INTF 3
static struct medium_interface s_intf[MAX_UPDATE_INTF] = {
#ifdef CONFIG_AUTO_SD_UPDATE
{.name = "mmc", .init = mmc_stor_init, .exit = mmc_stor_exit, },
#endif
#ifdef CONFIG_AUTO_USB_UPDATE
{.name = "usb", .init = usb_stor_init, .exit = usb_stor_exit, },
#endif
};
/* layout of the FLASH. ST = start address, ND = end address. */
#define AU_FL_FIRMWARE_ST 0x0
#define AU_FL_FIRMWARE_ND 0x7FFFF
#define AU_FL_KERNEL_ST 0x100000
#define AU_FL_KERNEL_ND 0x5FFFFF
#define AU_FL_ROOTFS_ST 0x600000
#define AU_FL_ROOTFS_ND 0xbFFFFF
static int au_stor_curr_dev; /* current device */
/* index of each file in the following arrays */
#define IDX_FIRMWARE 0
#define IDX_KERNEL 1
#define IDX_ROOTFS 2
/* max. number of files which could interest us */
#define AU_MAXFILES 3
/* pointers to file names */
char *aufile[AU_MAXFILES] = {
AU_FIRMWARE,
AU_KERNEL,
AU_ROOTFS
};
/* sizes of flash areas for each file */
long ausize[AU_MAXFILES] = {
(AU_FL_FIRMWARE_ND + 1) - AU_FL_FIRMWARE_ST,
(AU_FL_KERNEL_ND + 1) - AU_FL_KERNEL_ST,
(AU_FL_ROOTFS_ND + 1) - AU_FL_ROOTFS_ST,
};
/* array of flash areas start and end addresses */
struct flash_layout aufl_layout[AU_MAXFILES] = {
{ AU_FL_FIRMWARE_ST, AU_FL_FIRMWARE_ND, },
{ AU_FL_KERNEL_ST, AU_FL_KERNEL_ND, },
{ AU_FL_ROOTFS_ST, AU_FL_ROOTFS_ND, },
};
#define LOAD_ADDR ((unsigned char *)0x82000000)
/* the app is the largest image */
#define MAX_LOADSZ ausize[IDX_ROOTFS]
static int au_check_cksum_valid(int idx, long nbytes)
{
image_header_t *hdr;
unsigned long checksum;
hdr = (image_header_t *)LOAD_ADDR;
if (nbytes != (sizeof(*hdr) + ntohl(hdr->ih_size))) {
printf("Image %s bad total SIZE\n", aufile[idx]);
return -1;
}
/* check the data CRC */
checksum = ntohl(hdr->ih_dcrc);
if (crc32(0, (unsigned char const *)(LOAD_ADDR + sizeof(*hdr)),
ntohl(hdr->ih_size)) != checksum) {
printf("Image %s bad data checksum\n", aufile[idx]);
return -1;
}
return 0;
}
static int au_check_header_valid(int idx, long nbytes)
{
image_header_t *hdr;
unsigned long checksum;
char env[ENV_LEN];
char auversion[ENV_LEN];
hdr = (image_header_t *)LOAD_ADDR;
/* check the easy ones first */
#undef CHECK_VALID_DEBUG
#ifdef CHECK_VALID_DEBUG
printf("\nmagic %#x %#x\n", ntohl(hdr->ih_magic), IH_MAGIC);
printf("arch %#x %#x\n", hdr->ih_arch, IH_ARCH_ARM);
printf("size %#x %#lx\n", ntohl(hdr->ih_size), nbytes);
printf("type %#x %#x\n", hdr->ih_type, IH_TYPE_KERNEL);
#endif
if (nbytes < sizeof(*hdr)) {
printf("Image %s bad header SIZE\n", aufile[idx]);
return -1;
}
if (ntohl(hdr->ih_magic) != IH_MAGIC || hdr->ih_arch != IH_ARCH_ARM) {
printf("Image %s bad MAGIC or ARCH\n", aufile[idx]);
return -1;
}
/* check the hdr CRC */
checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0;
if (crc32(0, (unsigned char const *)hdr, sizeof(*hdr)) != checksum) {
printf("Image %s bad header checksum\n", aufile[idx]);
return -1;
}
hdr->ih_hcrc = htonl(checksum);
/* check the type - could do this all in one gigantic if() */
if ((idx == IDX_FIRMWARE) && (hdr->ih_type != IH_TYPE_FIRMWARE)) {
printf("Image %s wrong type\n", aufile[idx]);
return -1;
}
if ((idx == IDX_KERNEL) && (hdr->ih_type != IH_TYPE_KERNEL)) {
printf("Image %s wrong type\n", aufile[idx]);
return -1;
}
if ((idx == IDX_ROOTFS) &&
(hdr->ih_type != IH_TYPE_RAMDISK) &&
(hdr->ih_type != IH_TYPE_FILESYSTEM)) {
printf("Image %s wrong type\n", aufile[idx]);
ausize[idx] = 0;
return -1;
}
/* recycle checksum */
checksum = ntohl(hdr->ih_size);
/* for kernel and app the image header must also fit into flash */
if ((idx == IDX_KERNEL) || (hdr->ih_type == IH_TYPE_RAMDISK))
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", aufile[idx]);
return -1;
}
sprintf(env, "%lx", (unsigned long)ntohl(hdr->ih_time));
setenv(auversion, env);
return 0;
}
static void schedule_notify(unsigned long offset, unsigned long len,
unsigned long off_start)
{
int percent_complete = -1;
do {
unsigned long long n =
(unsigned long long)(offset - off_start) * PERCENT;
int percent;
do_div(n, len);
percent = (int)n;
/* output progress message only at whole percent
* steps to reduce the number of messages
* printed on (slow) serial consoles
*/
if (percent != percent_complete) {
percent_complete = percent;
printf("\rOperation at 0x%lx -- %3d%% complete",
offset, percent);
}
} while (0);
}
static int spi_flash_erase_op(struct spi_flash *flash, unsigned long offset,
unsigned long len)
{
int ret;
struct mtd_info_ex *spiflash_info = get_spiflash_info();
unsigned long erase_start, erase_len, erase_step;
erase_start = offset;
erase_len = len;
erase_step = spiflash_info->erasesize;
while (len > 0) {
if (len < erase_step)
erase_step = len;
ret = flash->erase(flash, (u32)offset, erase_step);
if (ret)
return 1;
len -= erase_step;
offset += erase_step;
/* notify real time schedule */
schedule_notify(offset, erase_len, erase_start);
}
return ret;
}
static int spi_flash_write_op(struct spi_flash *flash, unsigned long offset,
unsigned long len, char *buf)
{
int ret;
unsigned long write_start, write_len, write_step;
char *pbuf = buf;
struct mtd_info_ex *spiflash_info = get_spiflash_info();
write_start = offset;
write_len = len;
write_step = spiflash_info->erasesize;
while (len > 0) {
if (len < write_step)
write_step = len;
ret = flash->write(flash, offset, write_step, pbuf);
if (ret)
break;
offset += write_step;
pbuf += write_step;
len -= write_step;
/* notify real time schedule */
schedule_notify(offset, write_len, write_start);
}
return ret;
}
static int au_do_update(int idx, long sz)
{
image_header_t *hdr;
unsigned long start, len;
unsigned long write_len;
int rc;
void *buf;
char *pbuf;
hdr = (image_header_t *)LOAD_ADDR;
start = aufl_layout[idx].start;
len = aufl_layout[idx].end - aufl_layout[idx].start + 1;
/*
* erase the address range.
*/
printf("flash erase...\n");
rc = spi_flash_erase_op(flash, start, len);
if (rc) {
printf("SPI flash sector erase failed\n");
return 1;
}
buf = map_physmem((unsigned long)LOAD_ADDR, len, MAP_WRBACK);
if (!buf) {
puts("Failed to map physical memory\n");
return 1;
}
/* strip the header - except for the kernel and ramdisk */
if (hdr->ih_type == IH_TYPE_KERNEL || hdr->ih_type == IH_TYPE_RAMDISK) {
pbuf = buf;
write_len = sizeof(*hdr) + ntohl(hdr->ih_size);
} else {
pbuf = (buf + sizeof(*hdr));
write_len = ntohl(hdr->ih_size);
}
/* copy the data from RAM to FLASH */
printf("\nflash write...\n");
rc = spi_flash_write_op(flash, start, write_len, pbuf);
if (rc) {
printf("SPI flash write failed, return %d\n", rc);
return 1;
}
/* check the dcrc of the copy */
if (crc32(0, (unsigned char const *)(buf + sizeof(*hdr)),
ntohl(hdr->ih_size)) != ntohl(hdr->ih_dcrc)) {
printf("Image %s Bad Data Checksum After COPY\n", aufile[idx]);
return -1;
}
unmap_physmem(buf, len);
return 0;
}
static void get_update_env(char *img_start, char *img_end)
{
long start = -1;
long end = 0;
char *env;
/*
* check whether start and end are defined in environment
* variables.
*/
env = getenv(img_start);
if (env != NULL)
start = simple_strtoul(env, NULL, HEX);
env = getenv(img_end);
if (env != NULL)
end = simple_strtoul(env, NULL, HEX);
if (start >= 0 && end && end > start) {
ausize[IDX_FIRMWARE] = (end + 1) - start;
aufl_layout[0].start = start;
aufl_layout[0].end = end;
}
}
/*
* If none of the update file(u-boot, kernel or rootfs) was found
* in the medium, return -1;
* If u-boot has been updated, return 1;
* Others, return 0;
*/
static int update_to_flash(void)
{
int i;
long sz;
int res, cnt;
int uboot_updated;
int image_found;
/* just loop thru all the possible files */
for (i = 0; i < AU_MAXFILES; i++) {
/* just read the header */
sz = file_fat_read(aufile[i], LOAD_ADDR, sizeof(image_header_t));
debug("read %s sz %ld hdr %d\n", aufile[i], sz, sizeof(image_header_t));
if (sz <= 0 || sz < sizeof(image_header_t)) {
debug("%s not found\n", aufile[i]);
continue;
}
image_found = 1;
if (au_check_header_valid(i, sz) < 0) {
debug("%s header not valid\n", aufile[i]);
continue;
}
sz = file_fat_read(aufile[i], LOAD_ADDR, MAX_LOADSZ);
debug("read %s sz %ld hdr %d\n", aufile[i], sz, sizeof(image_header_t));
if (sz <= 0 || sz <= sizeof(image_header_t)) {
debug("%s not found\n", aufile[i]);
continue;
}
if (au_check_cksum_valid(i, sz) < 0) {
debug("%s checksum not valid\n", aufile[i]);
continue;
}
/* If u-boot had been updated, we need to
* save current env to flash
*/
if (strcmp((char *)AU_FIRMWARE, aufile[i]) == 0)
uboot_updated = 1;
/* this is really not a good idea, but it's what the customer wants. */
cnt = 0;
do {
res = au_do_update(i, sz);
/* let the user break out of the loop */
if (ctrlc() || had_ctrlc()) {
clear_ctrlc();
break;
}
cnt++;
} while (res < 0);
}
if (uboot_updated == 1)
return 1;
if (image_found == 1)
return 0;
return -1;
}
/*
* This is called by board_init() after the hardware has been set up
* and is usable. Only if SPI flash initialization failed will this function
* return -1, otherwise it will return 0;
*/
int do_auto_update(void)
{
struct blk_desc *stor_dev;
int old_ctrlc;
int j;
int state = -1;
int dev;
au_stor_curr_dev = -1;
for (j = 0; j < MAX_UPDATE_INTF; j++) {
if ((unsigned long)s_intf[j].name[0] != 0) {
au_stor_curr_dev = s_intf[j].init();
if (au_stor_curr_dev == -1) {
debug("No %s storage device found!\n",
s_intf[j].name);
continue;
}
dev = 0;
debug("device name %s!\n", s_intf[j].name);
stor_dev = blk_get_dev(s_intf[j].name, dev);
if (stor_dev == NULL) {
debug("Unknow device type!\n");
continue;
}
if (fat_register_device(stor_dev, 1) != 0) {
debug("Unable to use %s %d:%d for fatls\n",
s_intf[j].name, au_stor_curr_dev, 1);
continue;
}
if (file_fat_detectfs() != 0) {
debug("file_fat_detectfs failed\n");
continue;
}
/*
* Get image layout from environment.
* If the start address and the end address
* were not definedin environment virables,
* use the default value
*/
get_update_env("firmware_st", "firmware_nd");
get_update_env("kernel_st", "kernel_nd");
get_update_env("rootfs_st", "rootfs_nd");
/*
* make sure that we see CTRL-C
* and save the old state
*/
old_ctrlc = disable_ctrlc(0);
/*
* CONFIG_SF_DEFAULT_SPEED equals 1000000,
* CONFIG_SF_DEFAULT_MODE equals 0x3
*/
flash = spi_flash_probe(0, 0, MAX_HZ, SPI_MODE3);
if (!flash) {
printf("Failed to initialize SPI flash\n");
return -1;
}
state = update_to_flash();
/* restore the old state */
disable_ctrlc(old_ctrlc);
s_intf[j].exit();
/*
* no update file found
*/
if (state == -1)
continue;
/*
* update files have been found on current medium,
* so just break here
*/
break;
}
}
/*
* If u-boot has been updated, it's better to save environment to flash
*/
if (state == 1)
saveenv();
return 0;
}
#endif /* CONFIG_AUTO_UPDATE */