/* * Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #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 #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 */