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

1756 lines
41 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>
#include <console.h>
#include <mmc.h>
#include <sparse_format.h>
#include <linux/kernel.h>
#include <nand.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
#ifndef CONFIG_GK_MC
#if !defined CONFIG_USB_OHCI && !defined CONFIG_USB_XHCI_HCD
#error "should have defined CONFIG_USB_OHCI or CONFIG_USB_XHCI"
#endif
#endif
#ifndef CONFIG_USB_STORAGE
#error "should have defined CONFIG_USB_STORAGE"
#endif
#include <usb.h>
#include "usb_init.c"
#endif
#include "../../fs/ext4/unsparse.h"
#undef AU_DEBUG
#undef debug_print
#ifdef AU_DEBUG
#define debug_print(fmt, args...) printf(fmt, ##args)
#else
#define debug_print(fmt, args...)
#endif /* AU_DEBUG */
/* possible names of files on the medium. */
#define AU_CONFIG "config"
/* config file's size < 1K */
#define CONFIG_MAX_SIZE 2048
#define BLOCK_SIZE 512
#define STR_LEN 80
#define LINE 16
#define EMMC_BLOCK_SHIFT 9
#define NAME_LEN 20
#define SIZE_M (1024 * 1024)
#define SIZE_K 1024
#define DECI_VALUE 10
#define STR_STEPS 2
#define PERCENT_VALUE 100
#define STOR_DEV_OFFSET 16
#define STOR_DEV_MASK 0x03
#define STOR_PART 18
#define STOR_PART_MASK 0x1f
#ifdef CONFIG_GK_UPGRADE_BY_SEGMENT
#define SECTION_SIZE 0x1000000UL /* 16M */
#define BLOCK_SIZE 512
#define DEFAULT_BLOCK_SIZE 4096
#define CHUNK_HEAD_SIZE 12
#define BOUNDARY (DEFAULT_BLOCK_SIZE + CHUNK_HEAD_SIZE)
#define HEX_DUMP_LEN 128
unsigned long map_size;
#endif
#define AU_FIRMWARE "u-boot.bin"
#define AU_KERNEL "kernel"
int boot_medium_type = 0xff;
#include "nand.h"
struct flash_layout {
long start;
long end;
};
struct seg_update_parm {
loff_t actread;
loff_t pos;
int extra;
int nsect;
int uboot_updated;
int updatefile_found;
unsigned long sz;
unsigned int section_size;
unsigned int remain;
};
struct update_medium_interface {
char name[NAME_LEN];
int (*init)(void);
int (*erase)(unsigned long offset, unsigned long len);
int (*write)(unsigned long offset, unsigned long len,
unsigned char *buf);
int (*write_yaffs)(unsigned long offset, unsigned long len,
unsigned char *buf);
int (*write_ext4)(unsigned long offset, unsigned long len,
unsigned char *buf);
};
#if defined(CONFIG_CMD_SF) || defined(CONFIG_CMD_NAND)
static void schedule_notify(unsigned long offset, unsigned long len,
unsigned long off_start)
{
int percent_complete = -1;
unsigned long long n;
do {
n = (unsigned long long)(offset - off_start) * PERCENT_VALUE;
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)
printf("\rOperation at 0x%lx -- %3d%% complete.",
offset, percent);
} while (0);
}
#endif
#ifdef CONFIG_CMD_SF
static struct spi_flash *spinor_flash;
static int spinor_flash_init(void)
{
spinor_flash = spi_flash_probe(0, 0, 0, 0);
return 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 spinor_flash_erase(unsigned long offset, unsigned long len)
{
return spi_flash_erase_op(spinor_flash, offset, len);
}
static int spi_flash_write_op(struct spi_flash *flash, unsigned long offset,
unsigned long len, unsigned char *buf)
{
int ret;
unsigned long write_start, write_len, write_step;
unsigned 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 spinor_flash_write(unsigned long offset, unsigned long len,
unsigned char *buf)
{
return spi_flash_write_op(spinor_flash, offset, len, (unsigned char *)buf);
}
#endif
#ifdef CONFIG_CMD_NAND
struct mtd_info *nand_flash;
static int nand_flash_init(void)
{
nand_flash = nand_info[0];
return 0;
}
static int nand_flash_erase(unsigned long offset, unsigned long len)
{
int ret;
unsigned long erase_len;
unsigned long erase_step;
unsigned long length;
nand_erase_options_t opts;
memset(&opts, 0, sizeof(opts));
length = len;
erase_step = nand_flash->erasesize;
erase_len = length;
opts.length = erase_step;
opts.offset = offset;
opts.quiet = 1;
while (length > 0) {
if (length < erase_step)
erase_step = length;
ret = nand_erase_opts(nand_flash, &opts);
if (ret)
return 1;
length -= erase_step;
opts.offset += erase_step;
/* notify real time schedule */
schedule_notify(opts.offset, erase_len, offset);
}
return ret;
}
static int nand_flash_write(unsigned long offset, unsigned long len,
unsigned char *buf)
{
int ret;
unsigned long offset_notify;
unsigned long write_start;
unsigned long write_len;
unsigned long write_step;
size_t length;
unsigned char *pbuf = buf;
if (offset == 0) {
/* Make sure the length is block size algin */
length = len & (nand_flash->erasesize - 1) ? (size_t)(len +
(nand_flash->erasesize - len %
nand_flash->erasesize)) : len;
write_step = nand_flash->erasesize;
} else {
/* Make sure the length is writesize algin */
length = len & (nand_flash->erasesize - 1) ? (size_t)(len +
(nand_flash->writesize - len %
nand_flash->writesize)) : len;
write_step = nand_flash->writesize;
}
write_start = offset;
offset_notify = offset;
write_len = length;
while (length > 0) {
size_t block_offset = offset & (nand_flash->erasesize - 1);
size_t *rw_size;
if (nand_flash->_block_isbad(nand_flash, offset & ~
(nand_flash->erasesize - 1))) {
printf("Skip bad block 0x%08llx\n",
offset & ~(loff_t)(nand_flash->erasesize - 1));
offset += nand_flash->erasesize - block_offset;
continue;
}
rw_size = (size_t *)&write_step;
ret = nand_flash->_write(nand_flash, (size_t)offset, *rw_size,
rw_size, (u_char *)pbuf);
if (ret) {
printf("NAND write to offset %lx failed %d\n",
offset, ret);
break;
}
offset += write_step;
pbuf += write_step;
length -= write_step;
offset_notify += write_step;
/* notify real time schedule */
schedule_notify(offset_notify, write_len, write_start);
}
return ret;
}
static int nand_flash_yaffs_write(unsigned long offset, unsigned long len,
unsigned char *buf)
{
int ret;
size_t rw_size = len;
ret = nand_write_yaffs_skip_bad(nand_flash, offset, &rw_size,
(u_char *)buf);
if (ret)
printk("Write yaffs fail !!\n");
printk("Write yaffs done\n");
return ret;
}
/* get count of area's bad block for nand flash */
int get_bad_block_count(unsigned long offset, unsigned long len)
{
int count = 0;
unsigned long block_offset = 0;
if (offset & (nand_flash->erasesize - 1))
block_offset = offset & (nand_flash->erasesize - 1);
if (len & (nand_flash->erasesize - 1))
len = ALIGN(len, nand_flash->erasesize);
for (int i = 0; i < len / nand_flash->erasesize; i++) {
if (nand_block_isbad(nand_flash, offset))
count++;
offset += nand_flash->erasesize - block_offset;
}
return count;
}
#endif
void hex_dump(unsigned char *buf, int len, int addr)
{
int i;
int j;
int k;
char binstr[STR_LEN];
for (i = 0; i < len; i++) {
if ((i % LINE) == 0) {
sprintf(binstr, "%08x -", i + addr);
sprintf(binstr, "%s %02x", binstr,
(unsigned char)buf[i]);
} else if ((i % LINE) == (LINE - 1)) {
sprintf(binstr, "%s %02x", binstr,
(unsigned char)buf[i]);
sprintf(binstr, "%s ", binstr);
for (j = i - (LINE - 1); j <= i; j++)
sprintf(binstr, "%s%c", binstr,
(buf[j] > '!' &&
buf[j] <= '~') ? buf[j] : '.');
printf("%s\n", binstr);
} else {
sprintf(binstr, "%s %02x", binstr,
(unsigned char)buf[i]);
}
}
if ((i % LINE) != 0) {
k = LINE - (i % LINE);
for (j = 0; j < k; j++)
sprintf(binstr, "%s ", binstr);
sprintf(binstr, "%s ", binstr);
k = LINE - k;
for (j = i - k; j < i; j++)
sprintf(binstr, "%s%c", binstr,
(buf[j] > '!' && buf[j] <= '~') ? buf[j] : '.');
printf("%s\n", binstr);
}
}
#ifdef CONFIG_SUPPORT_EMMC_BOOT
static int mmc_save_init(void)
{
struct mmc *mmc = find_mmc_device(0);
if (!mmc) {
printf("%s:find mmc device failed\n", __func__);
return -1;
}
(void)mmc_init(mmc);
return 0;
}
static int mmc_save_write(unsigned long offset, unsigned long len,
unsigned char *buf)
{
struct mmc *mmc = find_mmc_device(0);
if (!mmc) {
printf("%s:find mmc device failed\n", __func__);
return -1;
}
if (len % MMC_MAX_BLOCK_LEN)
blk_dwrite(mmc_get_blk_desc(mmc), (offset >> EMMC_BLOCK_SHIFT),
(len >> EMMC_BLOCK_SHIFT) + 1, buf);
else
blk_dwrite(mmc_get_blk_desc(mmc), (offset >> EMMC_BLOCK_SHIFT),
(len >> EMMC_BLOCK_SHIFT), buf);
return 0;
}
#ifndef CONFIG_GK_UPGRADE_BY_SEGMENT
static int mmc_save_write_ext4(unsigned long offset, unsigned long len,
unsigned char *buf)
{
struct mmc *mmc = find_mmc_device(0);
int retlen;
if (!mmc) {
printf("%s:find mmc device failed\n", __func__);
return -1;
}
if (len % MMC_MAX_BLOCK_LEN)
retlen = ext4_unsparse(mmc, 0, buf,
(offset >> EMMC_BLOCK_SHIFT),
(len >> EMMC_BLOCK_SHIFT) + 1);
else
retlen = ext4_unsparse(mmc, 0, buf,
(offset >> EMMC_BLOCK_SHIFT),
(len >> EMMC_BLOCK_SHIFT));
return retlen;
}
#endif
#endif
#define UPDATE_MEDIUM_SPINOR 0
#define UPDATE_MEDIUM_NAND 1
#define UPDATE_MEDIUM_EMMC 2
static struct update_medium_interface update_intf[3] = {
#ifdef CONFIG_CMD_SF
{"spinor", spinor_flash_init, spinor_flash_erase, spinor_flash_write,
NULL, NULL},
#else
{"none", NULL, NULL, NULL, NULL, NULL},
#endif
#ifdef CONFIG_CMD_NAND
{"nand", nand_flash_init, nand_flash_erase, nand_flash_write,
nand_flash_yaffs_write, NULL},
#else
{"none", NULL, NULL, NULL, NULL, NULL},
#endif
#ifdef CONFIG_SUPPORT_EMMC_BOOT
#ifndef CONFIG_GK_UPGRADE_BY_SEGMENT
{"emmc", mmc_save_init, NULL, mmc_save_write, NULL,
mmc_save_write_ext4}
#else
{"emmc", mmc_save_init, NULL, mmc_save_write, NULL, mmc_save_write}
#endif
#endif
};
static struct update_medium_interface *update_intf_p;
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
};
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 32
/* pointers to file names */
char *aufile[AU_MAXFILES] = {
0
};
#define NAME_MAX_LEN 0x20
char aufile_table[AU_MAXFILES][NAME_MAX_LEN] = {{0, }, };
unsigned long aufile_size[AU_MAXFILES] = {0};
/* sizes of flash areas for each file */
long ausize[AU_MAXFILES] = {0};
/* array of flash areas start and end addresses */
struct flash_layout aufl_layout[AU_MAXFILES] = {
{ 0, 0, }
};
#ifdef CONFIG_GK_UPGRADE_BY_SEGMENT
typedef struct buff_layout {
unsigned long offset; /* chunk header offset in next buffer */
u32 count; /* chunk header count */
u32 remain; /* remain add offset equals chunk_len */
u32 len; /* all the data length we need to handle in buffer */
u32 type; /* the last chunk type */
u8 flag; /* 0: filesize < SECTION_SIZE 1: filesize > SECTION_SIZE */
} buff_layout_t;
buff_layout_t layout; /* current buffer chunk information */
buff_layout_t perv_layout; /* the previous buffer chunk information */
sparse_header_t sparse_header; /* Sparse file header information */
#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS)
int get_buffer_chunk_layout(unsigned long offset, unsigned char *pbuf,
unsigned long filesize, unsigned long pos)
{
chunk_header_t *chunk = NULL;
unsigned long off;
unsigned long chunk_len;
off = offset;
/* big chunk handle */
if (off > SECTION_SIZE) {
layout.offset = off - (SECTION_SIZE - 1);
layout.len = SECTION_SIZE;
layout.type = layout.type;
layout.count = 1;
layout.remain = SECTION_SIZE;
debug_print("big chunk: layout.count:%u,layout.remain:%u,\
layout.offset:%lu,layout.len:%u,\
layout.type: % #x\n", layout.count, layout.remain, \
layout.offset, layout.len, layout.type);
return 0;
}
memset(&layout, 0, sizeof(buff_layout_t));
if (filesize <= SECTION_SIZE)
layout.flag = 0;
else
layout.flag = 1;
do {
chunk = (chunk_header_t *)(pbuf + off);
print_chunk_info(chunk);
if ((chunk->chunk_type != CHUNK_TYPE_RAW) &&
(chunk->chunk_type != CHUNK_TYPE_DONT_CARE) &&
(chunk->chunk_type != CHUNK_TYPE_FILL)) {
printf("This type of chunk:%x is not supported\n",\
chunk->chunk_type);
hex_dump(pbuf + off, HEX_DUMP_LEN, 0);
return 1;
}
chunk_len = chunk->chunk_sz * sparse_header.blk_sz;
off += chunk->total_sz;
if ((off <= SECTION_SIZE - 1)) {
layout.count++;
layout.len = off;
} else {
/* Special case handle: just have only one
* chunk header in buffer
*/
if (layout.count == 0)
layout.len = offset;
if (((SECTION_SIZE - 1 - layout.len) >=
BOUNDARY)) {
layout.count += 1;
layout.remain = SECTION_SIZE - 1 -
layout.len - sparse_header.chunk_hdr_sz;
layout.remain = layout.remain &
~(DEFAULT_BLOCK_SIZE - 1);
layout.offset = chunk_len - layout.remain;
layout.len += layout.remain +
sparse_header.chunk_hdr_sz;
layout.type = chunk->chunk_type;
}
}
} while (((pos + layout.len) != filesize) &&
((SECTION_SIZE - layout.len > DEFAULT_BLOCK_SIZE)));
printf("layout.count:%u,layout.remain:%u,layout.offset:%lu,\
layout.len: % u, layout.type: % #x\n", layout.count, \
layout.remain, layout.offset, layout.len, layout.type);
return 0;
}
#endif
#endif
/* where to load files into memory */
#if defined(CONFIG_TARGET_GK7205V200) || \
defined(CONFIG_TARGET_GK7205V300) || defined(CONFIG_TARGET_GK7202V300) || \
defined(CONFIG_TARGET_GK7605V100)
#define LOAD_ADDR ((unsigned char *)0x42000000)
#endif
/* the app is the largest image */
#define MAX_LOADSZ ausize[IDX_ROOTFS]
#ifdef CONFIG_GK_UPGRADE_BY_SEGMENT
int write_yaffs2_fs(int idx, unsigned int sz, unsigned char *pbuf)
{
#ifdef CONFIG_CMD_NAND
int bad_block_count;
unsigned long pages_len;
int ret;
unsigned long tmp_start_addr = aufl_layout[idx].start;
unsigned int sec_yaffs = nand_flash->writesize + nand_flash->oobsize;
if (update_intf_p->write_yaffs) {
pages_len = (sz / sec_yaffs) * nand_flash->writesize;
bad_block_count = get_bad_block_count(tmp_start_addr, pages_len);
debug_print("write offset:%ld, bad block count:%d\n",
aufl_layout[idx].start, bad_block_count);
tmp_start_addr += pages_len + bad_block_count *
nand_flash->erasesize;
if (tmp_start_addr > aufl_layout[idx].end) {
printf("The actual length of the partition is greater \
than the specified value due to the \
address offset caused by bad blocks.\n");
return 1;
}
ret = update_intf_p->write_yaffs(aufl_layout[idx].start, sz,
pbuf);
if (ret) {
printf("write yaffs failed\n");
return ret;
}
aufl_layout[idx].start += pages_len + bad_block_count *
nand_flash->erasesize;
}
#endif
return 0;
}
int chunk_write(int idx, int chunk_count, chunk_header_t *chunk,
unsigned int write_len, unsigned int chunk_len, unsigned char *pbuf)
{
unsigned int ret;
switch (chunk->chunk_type) {
case CHUNK_TYPE_RAW:
if (!layout.flag) {
write_len = chunk_len;
} else {
if (chunk_count == layout.count - 1) {
write_len = layout.remain;
debug_print("layout.remian:%u\n", layout.remain);
} else {
write_len = chunk_len;
}
}
ret = update_intf_p->write_ext4(aufl_layout[idx].start, write_len, pbuf);
if (ret) {
printf("write chunk error\n");
return 1;
}
aufl_layout[idx].start += write_len;
pbuf += write_len;
break;
case CHUNK_TYPE_DONT_CARE:
aufl_layout[idx].start += chunk_len;
break;
default:
printf("sparse:unknow chunk type %04x,chunk_count:%d,\
pbuf: %p\n", chunk->chunk_type, chunk_count, pbuf);
return 1;
}
return 0;
}
int write_ext4_fs(int idx, unsigned int offset, unsigned short type,
unsigned char *pbuf)
{
unsigned int write_len;
unsigned int chunk_len;
int chunk_count;
int ret;
chunk_header_t *chunk;
write_len = 0;
if (offset && (type == CHUNK_TYPE_RAW)) {
if (offset < SECTION_SIZE) {
write_len = offset;
ret = update_intf_p->write_ext4(
aufl_layout[idx].start, write_len, pbuf);
if (ret) {
printf("write chunk error\n");
return 1;
}
aufl_layout[idx].start += write_len;
pbuf += write_len;
} else {
/* big chunk write */
write_len = SECTION_SIZE;
ret = update_intf_p->write_ext4(
aufl_layout[idx].start, write_len, pbuf);
if (ret) {
printf("write chunk error\n");
return 1;
}
aufl_layout[idx].start += write_len;
return 0;
}
} else {
/* skip the sparse header */
pbuf += offset;
}
for (chunk_count = 0; chunk_count < layout.count; chunk_count++) {
chunk = (chunk_header_t *)(pbuf);
pbuf += sparse_header.chunk_hdr_sz;
chunk_len = chunk->chunk_sz * sparse_header.blk_sz;
if (chunk_write(idx, chunk_count, chunk, write_len, chunk_len,
pbuf)) {
printf("chunk write error\n");
return 1;
}
}
printf("write a part of file %s finished\n", aufile[idx]);
return 0;
}
int write_ubi_fs(int idx, unsigned int sz, unsigned char *pbuf)
{
#ifdef CONFIG_CMD_NAND
int bad_block_count;
unsigned long tmp_start_addr;
int ret;
#endif
if (boot_medium_type == BOOT_FROM_NAND) {
#ifdef CONFIG_CMD_NAND
tmp_start_addr = aufl_layout[idx].start;
while (sz & (nand_flash->writesize - 1))
sz = ALIGN(sz, nand_flash->writesize);
bad_block_count = get_bad_block_count(tmp_start_addr, sz);
debug_print("write offset: start:%ld, bad block count:%d\n",
tmp_start_addr, bad_block_count);
tmp_start_addr += sz + bad_block_count * nand_flash->erasesize;
if (tmp_start_addr > aufl_layout[idx].end) {
printf("The actual length of the partition is greater\
than the specified value due to the address\
offset caused by bad blocks.\n");
return 1;
}
ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf);
if (ret) {
printf("spi nand write ubifs error.\n");
return 1;
}
aufl_layout[idx].start += sz + bad_block_count *
nand_flash->erasesize;
#endif
} else {
/* ubifs for spi nor */
update_intf_p->write((aufl_layout[idx].start), sz, pbuf);
aufl_layout[idx].start += sz;
}
return 0;
}
int write_other_files(int idx, unsigned int sz, unsigned char *pbuf)
{
int ret = 0;
#ifdef CONFIG_CMD_NAND
int bad_block_count;
unsigned long tmp_start_addr;
#endif
if (boot_medium_type == BOOT_FROM_NAND) {
#ifdef CONFIG_CMD_NAND
tmp_start_addr = aufl_layout[idx].start;
while (sz & (nand_flash->writesize - 1))
sz = ALIGN(sz, nand_flash->writesize);
debug_print("write start:%ld, len:%u,pagesize:%u\n",
aufl_layout[idx].start, sz, nand_flash->writesize);
bad_block_count = get_bad_block_count(tmp_start_addr, sz);
tmp_start_addr += sz + bad_block_count * nand_flash->erasesize;
/* u-boot */
if (strcmp((char *)AU_FIRMWARE, aufile[idx]) == 0) {
if (tmp_start_addr > aufl_layout[idx].end) {
printf("The address offset caused by bad\
blocks causes the u-boot.bin file to overwrite the environment variable.\n");
return 1;
}
} else if (strcmp((char *)AU_KERNEL, aufile[idx]) == 0) {
if (tmp_start_addr > aufl_layout[idx].end) {
printf("The address offset caused by bad blocks causes the kernel file to\
overwrite the File system.\n");
return 1;
}
}
ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf);
aufl_layout[idx].start += sz + bad_block_count *
nand_flash->erasesize;
#endif
} else if (boot_medium_type == BOOT_FROM_EMMC) {
if (sz & (BLOCK_SIZE - 1))
sz = ALIGN(sz, BLOCK_SIZE);
debug_print("write start:%ld, len:%#x\n", aufl_layout[idx].start, sz);
ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf);
aufl_layout[idx].start += sz;
} else if (boot_medium_type == BOOT_FROM_SPI) {
debug_print("write start:%ld, len:%#x\n", aufl_layout[idx].start, sz);
ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf);
aufl_layout[idx].start += sz;
}
if (ret) {
printf("write file %s failed\n", aufile[idx]);
return ret;
}
return 0;
}
#endif
#ifndef CONFIG_GK_UPGRADE_BY_SEGMENT
static int au_do_update(int idx, long sz)
{
unsigned long start;
unsigned long len;
unsigned long write_len;
int rc = 0;
char *buf;
void *pbuf;
start = aufl_layout[idx].start;
len = aufl_layout[idx].end - aufl_layout[idx].start + 1;
/* erase the address range. */
if (boot_medium_type == BOOT_FROM_SPI || boot_medium_type == BOOT_FROM_NAND) {
printf("%s erase...\n", update_intf_p->name);
rc = update_intf_p->erase(start, len);
if (rc) {
printf("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;
}
/* write the whole file to flash;uboot and rootfs image have head
* kernel has head,it's head also be writed to flash
*/
pbuf = buf;
write_len = aufile_size[idx];
/* copy the data from RAM to FLASH */
printf("\n%s write...\n", update_intf_p->name);
if (strstr(aufile[idx], "yaffs")) {
if (update_intf_p->write_yaffs)
rc = update_intf_p->write_yaffs(start, write_len, pbuf);
} else if (strstr(aufile[idx], "ext4")) {
if (update_intf_p->write_ext4)
rc = update_intf_p->write_ext4(start, len, pbuf);
} else {
rc = update_intf_p->write(start, write_len, pbuf);
}
if (rc) {
printf("write failed, return %d\n", rc);
return 1;
}
unmap_physmem(buf, len);
return 0;
}
#else
static int au_do_update(int idx, unsigned int sz, int segment, u32 offset,
u16 type)
{
int rc;
void *buf;
unsigned char *pbuf;
unsigned long erase_len;
if (segment > 0)
goto write_op;
if (boot_medium_type == BOOT_FROM_SPI ||
boot_medium_type == BOOT_FROM_NAND) {
printf("%s erase...\n", update_intf_p->name);
erase_len = aufl_layout[idx].end - aufl_layout[idx].start + 1;
rc = update_intf_p->erase(aufl_layout[idx].start, erase_len);
debug_print("erase start:%lx erase end:%lx erase len:%lx\n",
aufl_layout[idx].start, aufl_layout[idx].end, erase_len);
if (rc) {
printf("sector erase failed\n");
return 1;
}
}
write_op:
buf = map_physmem((unsigned long)LOAD_ADDR, map_size, MAP_WRBACK);
if (!buf) {
puts("Failed to map physical memory\n");
return 1;
}
/*
* write the whole file to flash;uboot and rootfs image have head
* kernel has head,it's head also be writed to flash
*/
pbuf = buf;
/* copy the data from RAM to FLASH */
printf("%s write...\n", update_intf_p->name);
if (strstr(aufile[idx], "yaffs")) {
rc = write_yaffs2_fs(idx, sz, pbuf);
if (rc) {
printf("write yaffs fs failed\n");
return rc;
}
} else if (strstr(aufile[idx], "ext4")) {
rc = write_ext4_fs(idx, offset, type, pbuf);
if (rc) {
printf("write ext4 fs failed\n");
return rc;
}
} else if (strstr(aufile[idx], "ubifs")) {
rc = write_ubi_fs(idx, sz, pbuf);
if (rc) {
printf("write ubi fs failed\n");
return rc;
}
} else {
rc = write_other_files(idx, sz, pbuf);
if (rc) {
printf("write file operation failed\n");
return rc;
}
}
unmap_physmem(buf, map_size);
return 0;
}
#endif
/*
* 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;
*/
#ifndef CONFIG_GK_UPGRADE_BY_SEGMENT
static int update_to_flash(void)
{
int i;
loff_t sz;
int res;
int cnt;
int uboot_updated = 0;
/* just loop thru all the possible files */
for (i = 0; i < AU_MAXFILES && aufile[i] != NULL; i++) {
printf("\n");
if (!fat_exists(aufile[i])) {
printf("%s not found!\n", aufile[i]);
continue;
}
/* get file's real size */
if (!fat_size(aufile[i], &sz)) {
aufile_size[i] = ALIGN((unsigned long )sz, CONFIG_SYS_CACHELINE_SIZE);
} else {
printf("get size of %s failed!\n", aufile[i]);
continue;
}
printf("aligned size=0x%08lx\n", aufile_size[i]);
memset(LOAD_ADDR, 0xff, aufile_size[i]);
sz = file_fat_read(aufile[i], LOAD_ADDR, (unsigned long)sz);
debug_print("read %s sz %ld hdr %lu\n",
aufile[i], (unsigned long)sz, (unsigned long)sizeof(image_header_t));
if (sz <= 0) {
printf("read %s failed!\n", aufile[i]);
continue;
}
/* get file's real size */
aufile_size[i] = (unsigned long)sz;
printf("size=0x%08lx\n", aufile_size[i]);
/* 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, (unsigned long)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;
else
return -1;
}
#else
void get_sparse_header(int file_idx, struct seg_update_parm *update_parm)
{
#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS)
int ret;
if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) {
ret = file_fat_read_at(aufile[file_idx],
update_parm->pos, LOAD_ADDR, BLOCK_SIZE, &update_parm->actread);
if (ret)
printf("read %s failed, actread:%lld\n",
aufile[file_idx], update_parm->actread);
memset(&sparse_header, 0, sizeof(sparse_header_t));
get_unspare_header_info(LOAD_ADDR, &sparse_header);
layout.offset = sparse_header.file_hdr_sz;
}
#endif
return ;
}
void get_section_size(int file_idx, struct seg_update_parm *update_parm)
{
#ifdef CONFIG_CMD_NAND
if (strstr(aufile[file_idx], "yaffs2")) {
if (update_parm->sz > SECTION_SIZE)
update_parm->section_size = (SECTION_SIZE / (nand_flash->writesize +
nand_flash->oobsize)) * (nand_flash->writesize + nand_flash->oobsize);
else
update_parm->section_size = update_parm->sz;
debug_print("pagesize:%u, oobsize:%u, section_size:%u\n", nand_flash->writesize,
nand_flash->oobsize, update_parm->section_size);
} else {
update_parm->section_size = SECTION_SIZE;
}
#endif
#ifndef CONFIG_CMD_NAND
update_parm->section_size = SECTION_SIZE;
#endif
return ;
}
void loop_au_do_update(int file_idx, unsigned int opsz, int segment)
{
int cnt;
int res;
do {
res = au_do_update(file_idx, opsz, segment, perv_layout.offset, perv_layout.type);
/* let the user break out of the loop */
if (ctrlc() || had_ctrlc()) {
clear_ctrlc();
break;
}
cnt++;
} while (res < 0);
return ;
}
void segment_data_save(int file_idx, struct seg_update_parm *update_parm)
{
unsigned int opsz;
int ret;
for (int segment = 0; segment < (update_parm->nsect + update_parm->extra); segment++) {
if ((segment == update_parm->nsect) && update_parm->remain)
opsz = update_parm->remain;
else
opsz = update_parm->section_size;
printf("\nnsect:%d, remain:%u, section_size:%u, pos:%llu, opsz:%u, map_size:%lu\n",
update_parm->nsect, update_parm->remain,
update_parm->section_size, update_parm->pos, opsz, map_size);
memset(LOAD_ADDR, 0xff, map_size);
ret = file_fat_read_at(aufile[file_idx], update_parm->pos, LOAD_ADDR, opsz,
&update_parm->actread);
if (ret)
printf("read %s failed,opsz:%d,actread:%lld,pos:%lld\n",
aufile[file_idx], opsz, update_parm->actread, update_parm->pos);
#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS)
memset(&perv_layout, 0, sizeof(buff_layout_t));
if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) {
perv_layout.offset = layout.offset;
perv_layout.type = layout.type;
ret = get_buffer_chunk_layout(perv_layout.offset, LOAD_ADDR,
aufile_size[file_idx], update_parm->pos);
if (ret)
printf("Failed to parse the buffer.\n");
}
#endif
printf("reading %d part of %s ,size:%u, start:%lld\n", segment,
aufile[file_idx], opsz, update_parm->pos);
update_parm->updatefile_found = 1;
/* If u-boot had been updated, we need to
* save current env to flash */
if (strcmp((char *)AU_FIRMWARE, aufile[file_idx]) == 0)
update_parm->uboot_updated = 1;
loop_au_do_update(file_idx, opsz, segment);
#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS)
if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) {
update_parm->pos += layout.len;
update_parm->remain = aufile_size[file_idx] - update_parm->pos;
debug_print("nsect:%d,remain:%u,pos:%lld\n", update_parm->nsect,
update_parm->remain, update_parm->pos);
}
if ((segment == update_parm->nsect) && (update_parm->remain > SECTION_SIZE))
update_parm->extra += 1;
#else
update_parm->pos += update_parm->section_size;
#endif
}
return ;
}
static int update_to_flash(void)
{
int i;
struct seg_update_parm update_parm = {0};
memset(&layout, 0, sizeof(buff_layout_t));
/* just loop thru all the possible files */
for (i = 0; i < AU_MAXFILES && aufile[i] != NULL; i++) {
printf("\n\n");
update_parm.pos = 0;
update_parm.extra = 0;
if (!fat_exists(aufile[i])) {
printf("%s not found!\n", aufile[i]);
continue;
}
/* get file's real size */
if (!fat_size(aufile[i], (loff_t *)&update_parm.sz)) {
aufile_size[i] = update_parm.sz;
} else {
printf("get size of %s failed!\n", aufile[i]);
continue;
}
printf("filesize size= %lu\n", aufile_size[i]);
map_size = SECTION_SIZE;
memset(LOAD_ADDR, 0xff, map_size);
get_sparse_header(i, &update_parm);
get_section_size(i, &update_parm);
update_parm.nsect = aufile_size[i] / update_parm.section_size;
update_parm.remain = aufile_size[i] % update_parm.section_size;
if (update_parm.remain)
update_parm.extra = 1;
segment_data_save(i, &update_parm);
}
if (update_parm.uboot_updated == 1)
return 1;
else if (update_parm.updatefile_found == 1)
return 0;
else
return -1;
}
#endif
#define ENV_MAX_LEN (CONFIG_MAX_SIZE / 2)
/*
* pick up env form config file and set env
* fail:return -1;
* ok: return 0;
*/
static int env_pick_up(const char *envname, const char *buffer)
{
char *str, *str_s, *str_e;
char env[ENV_MAX_LEN];
str = strstr(buffer, envname);
if (!str)
goto env_err;
str_s = strchr(str, '\'');
if (!str_s)
goto env_err;
str_e = strchr(++str_s, '\'');
if (!str_e)
goto env_err;
if ((unsigned long)(str_e - str_s) > ENV_MAX_LEN) {
printf("ERROR:%s too long!\n", envname);
goto err;
}
strncpy(env, str_s, (size_t)(str_e - str_s));
env[(size_t)(str_e - str_s)] = '\0';
setenv((char *)envname, env);
return 0;
env_err:
printf("ERROR:%s not found!\n", envname);
err:
return -1;
}
/*
* pick up bootargs and bootcmd from config file and save env
* fail:return -1;
* ok: return 0;
*/
static int get_env_from_config(void)
{
char *aufile = AU_CONFIG;
int ret;
long sz = file_fat_read(aufile, (void *)LOAD_ADDR, CONFIG_MAX_SIZE);
if (sz <= 0) {
printf("ERROR:%s not found!\n", aufile);
goto err;
}
ret = env_pick_up("bootargs", (char *)LOAD_ADDR);
if (ret < 0)
goto err;
ret = env_pick_up("bootcmd", (char *)LOAD_ADDR);
if (ret < 0)
goto err;
return 0;
err:
return -1;
}
struct mtd_part_s {
char *str;
int unit;
struct mtd_part_s *next;
};
/*
* insert node to list
*/
struct mtd_part_s *list_insert(struct mtd_part_s *head, struct mtd_part_s *node)
{
struct mtd_part_s *p = head;
if (!head)
head = node;
while (p) {
if (p->next == NULL) {
p->next = node;
break;
}
p = p->next;
}
return head;
}
/*
* sort list by str
*/
struct mtd_part_s *mtd_list_sort(struct mtd_part_s *head)
{
int flag;
struct mtd_part_s *p, *pt1, *pt2;
if (!head)
return NULL;
if (head->next == NULL)
return head;
do {
flag = 0;
if ((uintptr_t)(head->str) >= (uintptr_t)
(head->next->str)) {
pt1 = head->next->next;
pt2 = head->next;
pt2->next = head;
head->next = pt1;
head = pt2;
}
for (p = head; p->next->next != NULL; p = p->next) {
if ((uintptr_t)(p->next->str) >= (uintptr_t)
(p->next->next->str)) {
pt1 = p->next->next->next;
pt2 = p->next->next;
pt2->next = p->next;
p->next->next = pt1;
p->next = pt2;
flag = 1;
}
}
} while (flag);
return head;
}
/*
* get mtd parttion info list from env string
*/
struct mtd_part_s *get_mtd_parts(char *env)
{
char *str = NULL;
struct mtd_part_s *part_p = NULL;
struct mtd_part_s *head = NULL;
if (env == NULL) {
printf("env is null!\n");
return NULL;
}
str = env;
while ((str = strstr(str, "M("))) {
part_p = malloc(sizeof(struct mtd_part_s));
if (part_p == NULL)
return NULL;
part_p->str = str;
part_p->unit = SIZE_M;
part_p->next = NULL;
head = list_insert(head, part_p);
str++;
}
str = env;
while ((str = strstr(str, "K("))) {
part_p = malloc(sizeof(struct mtd_part_s));
if (part_p == NULL)
return NULL;
part_p->str = str;
part_p->unit = SIZE_K;
part_p->next = NULL;
head = list_insert(head, part_p);
str++;
}
head = mtd_list_sort(head);
return head;
}
unsigned long get_mtd_part_size(const char *str_num)
{
unsigned long size = 0;
int k;
int j = 0;
int num;
while (*str_num >= '0' && *str_num <= '9') {
k = j;
num = *str_num - '0';
while (k) {
num *= DECI_VALUE;
k--;
}
size += num;
j++;
str_num--;
}
return size;
}
int bootargs_analyze(void)
{
char *str;
char *str_0;
int i;
long start = 0;
struct mtd_part_s *part_p = NULL;
char env[ENV_MAX_LEN];
char *envp = getenv("bootargs");
if(envp == NULL){
printf("ERROR:bootargs is NULL!\n");
return -1;
}
if (strlen(envp) > ENV_MAX_LEN - 1) {
printf("ERROR:bootargs is too long!\n");
return -1;
}
strcpy(env, envp);
str = env;
str_0 = env;
i = 0;
part_p = get_mtd_parts(env);
while (part_p) {
if (i >= AU_MAXFILES) {
printf("ERROR:Num of partition is more than %0d!\n",
AU_MAXFILES);
break;
}
str = part_p->str;
ausize[i] = get_mtd_part_size(str - 1) * part_p->unit;
aufl_layout[i].start = start;
aufl_layout[i].end = start + ausize[i] - 1;
start += ausize[i];
str += STR_STEPS;
str_0 = strstr(str, ")");
if ((unsigned long)(str_0 - str) > NAME_MAX_LEN) {
printf("file name len is too long\n");
return -1;
}
strncpy(aufile_table[i], str, (unsigned long)(str_0 - str));
aufile_table[i][str_0 - str] = '\0';
aufile[i] = &(aufile_table[i][0]);
printf("\n[%0d]=%-16s start=0x%08lx end=0x%08lx size=0x%08lx",
i, aufile_table[i],
(unsigned long)(aufl_layout[i].start),
(unsigned long)(aufl_layout[i].end),
(unsigned long)ausize[i]);
i++;
part_p = part_p->next;
}
if (i == 0) {
printf("ERROR:Can't find valid partition info!\n");
return -1;
}
return 0;
}
#ifdef CONFIG_EMMC
unsigned int dos_start_lba = 0;
/* 0:emmc 1:sd */
int target_dev = 1;
/* the user's upgrade image stores the partition number in the eMMC medium */
int target_paratition;
void *get_target_dev_value(void)
{
return &target_dev;
}
void *get_target_paratition_dev(void)
{
return &target_paratition;
}
void *get_dos_start_lba_value(void)
{
return &dos_start_lba;
}
/*
* get mtd parttion info list from env string in order
*/
struct mtd_part_s *get_mtd_parts_inorder(char *env, int *total_paratition)
{
char *str = env;
struct mtd_part_s *part_p;
struct mtd_part_s *head = NULL;
int num = 0;
str = env;
str = strstr(str, "(");
while (str != NULL) {
printf("get_mtd_parts_inorder str=%s\n", str);
part_p = malloc(sizeof(struct mtd_part_s));
if (part_p == NULL)
return NULL;
if ((*(str - 1) == 'M') || (*(str - 1) == 'm'))
part_p->unit = SIZE_M;
else if ((*(str - 1) == 'K') || (*(str - 1) == 'k'))
part_p->unit = SIZE_K;
else
part_p->unit = 1;
part_p->str = str;
part_p->next = NULL;
head = list_insert(head, part_p);
str++;
str = strstr(str, "(");
num++;
}
head = mtd_list_sort(head);
if (total_paratition != NULL)
*total_paratition = num;
return head;
}
int bootargs_getend(void)
{
char *str;
int i;
unsigned long start = 0;
char filename[NAME_MAX_LEN] = {0};
int cur_target_paratition;
struct mtd_part_s *part_p = NULL;
char env[ENV_MAX_LEN];
char *envp = getenv("bootargs");
if(envp == NULL){
printf("ERROR:bootargs is NULL!\n");
return -1;
}
if (strlen(envp) > ENV_MAX_LEN - 1) {
printf("ERROR:bootargs is too long!\n");
return -1;
}
strcpy(env, envp);
str = env;
i = 0;
part_p = get_mtd_parts_inorder(env, NULL);
cur_target_paratition = target_paratition;
while (part_p) {
if (i >= AU_MAXFILES) {
printf("ERROR:Num of partition is more than %0d!\n",\
AU_MAXFILES);
break;
}
str = part_p->str;
start += get_mtd_part_size(str - 1) * part_p->unit;
str += 1;
if (strstr(str, ")") == NULL || (unsigned long)(strstr(str, ")") - str) >
(NAME_MAX_LEN - 1)) {
printf("file name is more than NAME_MAX_LEN\n");
return -1;
}
strncpy(filename, str, (unsigned long)(strstr(str, ")") - str));
filename[strstr(str, ")") - str] = '\0';
printf("\n[%0d]=%-16s start=0x%08lx", i, filename, start);
i++;
part_p = part_p->next;
if (i >= cur_target_paratition)
break;
}
if (i == 0) {
printf("ERROR:Can't find valid partition info!\n");
dos_start_lba = 0;
return -1;
}
if (target_dev == 0)
/* emmc device */
dos_start_lba = start >> EMMC_BLOCK_SHIFT;
else
/* sd device */
dos_start_lba = 0;
printf("\n dos_start_lba = %d\n", dos_start_lba);
return 0;
}
#endif
void get_boot_info(void)
{
unsigned int reg;
reg = readl((void *)(SYS_CTRL_REG_BASE + REG_SYSSTAT));
if (get_sys_boot_mode(reg) == BOOT_FROM_SPI) {
/* SFC_DEVICE_MODE:0:SPINOR 1:SPINAND */
if (get_spi_device_type(reg)) {
boot_medium_type = BOOT_FROM_NAND;
update_intf_p = &update_intf[UPDATE_MEDIUM_NAND];
} else {
boot_medium_type = BOOT_FROM_SPI;
update_intf_p = &update_intf[UPDATE_MEDIUM_SPINOR];
}
} else if (get_sys_boot_mode(reg) == BOOT_FROM_NAND) {
boot_medium_type = BOOT_FROM_NAND;
update_intf_p = &update_intf[UPDATE_MEDIUM_NAND];
} else if (get_sys_boot_mode(reg) == BOOT_FROM_EMMC) {
boot_medium_type = BOOT_FROM_EMMC;
update_intf_p = &update_intf[UPDATE_MEDIUM_EMMC];
}
}
/*
* This is called by board_init() after the hardware has been set up
* and is usable.
* 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;
int updateflg = 0;
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_print("%s storage device not found!\n", s_intf[j].name);
continue;
}
dev = 0;
if (strncmp("mmc", s_intf[j].name, sizeof("mmc")) == 0) {
#ifdef CONFIG_EMMC
dev = target_dev;
#else
dev = 0;
#endif
}
debug_print("device name %s!\n", s_intf[j].name);
stor_dev = blk_get_dev(s_intf[j].name, dev);
if (stor_dev == NULL) {
debug_print("Unknow device type!\n");
continue;
}
#ifdef CONFIG_EMMC
if (target_dev == 0) {
/* emmc upgrade */
bootargs_getend();
/* re-do the entry->test(dev_desc) to set the
* dev_desc->part_type to PART_TYPE_DOS
*/
part_init(stor_dev);
}
#endif
if (fat_register_device(stor_dev, 1) != 0) {
debug_print("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_print("file_fat_detectfs failed\n");
continue;
}
get_boot_info();
update_intf_p->init();
if (get_env_from_config()) {
printf("Warning:no config!\n");
printf("Try to use old env!\n");
}
if (bootargs_analyze()) {
printf("ERROR:bootargs analyze fail!\n");
continue;
}
/*
* make sure that we see CTRL-C
* and save the old state
*/
old_ctrlc = disable_ctrlc(0);
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
*/
updateflg = 1;
break;
}
}
/*
* If u-boot has been updated, it's better to save environment to flash
*/
if (state == 1)
saveenv();
if (updateflg == 1)
return 0;
else
return -1;
}
#endif /* CONFIG_AUTO_UPDATE */