diff options
Diffstat (limited to 'common/sparse.c')
-rwxr-xr-x | common/sparse.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/common/sparse.c b/common/sparse.c new file mode 100755 index 0000000..d38b1bc --- /dev/null +++ b/common/sparse.c @@ -0,0 +1,295 @@ +/* + * (C) Copyright 2011 + * Texas Instruments, <www.ti.com> + * Author: Vikram Pandita <vikram.pandita@ti.com> + * + * 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 <config.h> +#include <common.h> +#include <sparse.h> +#include <mmc.h> +#include <fastboot.h> + +//#define SDEBUG + +#define SPARSE_HEADER_MAJOR_VER 1 + +#define SZ_128M 0x08000000 +#define SZ_512M 0x20000000 +#define SZ_640M 0x28000000 +#define SZ_768M 0x30000000 +#define SZ_896M 0x38000000 +extern struct cmd_fastboot_interface priv; +int _unsparse(unsigned char init, unsigned char *source_input, u32 sector_input, u32 section_size, + unsigned mmcc, int (*WRITE)(unsigned mmcc, unsigned long sector, unsigned long len, unsigned char *src)) +{ + static sparse_header_t *header = (void*)0; + static u32 i = 0, outlen = 0, len_mmc = 0, sector = 0; + static unsigned char *source; + static u32 left; + static unsigned char *BOUNDARY = (void*)SZ_640M ; /*512M+128M*/ + //printf("_unsparse: write to mmc slot[%d] @ %x ptn address, header->total_chunks=%d\n", mmcc, sector, header->total_chunks); + if (init == 2) { + BOUNDARY = (void*)0xffffffff; + } + printf("init:%d, source:0x%08X, sector:%d, section_size:%d, BOUNDARY:0x%x\n", + init, source_input, sector_input, section_size, BOUNDARY); + + if (init >= 1) { + i = 0, outlen = 0, len_mmc = 0; + source = source_input; /* Only keep the 1st time source_input */ + sector = sector_input; + header = (void*) source; + printf("total_blks:%d, blk_sz:%d(%d sectors)\n", + header->total_blks, + header->blk_sz, + (header->total_blks * (header->blk_sz / 512))); + if ((header->total_blks * (header->blk_sz / 512)) > section_size ) { + printf("sparse: section size %d sectors limit: exceeded\n", + section_size); + return 1; + } + + if (header->magic != SPARSE_HEADER_MAGIC) { + printf("sparse: bad magic\n"); + return 1; + } + + if ((header->major_version != SPARSE_HEADER_MAJOR_VER) || + (header->file_hdr_sz != sizeof(sparse_header_t)) || + (header->chunk_hdr_sz != sizeof(chunk_header_t))) { + printf("sparse: incompatible format\n"); + return 1; + } + /** + * backup the sparse header in 126M + */ + memcpy((void*)(SZ_128M-0x200000), header, sizeof(sparse_header_t)); + header = (void*) (SZ_128M-0x200000); + /* todo: ensure image will fit */ + + /* Skip the header now */ + source += header->file_hdr_sz; + } + + for (; i < header->total_chunks; i++) { + unsigned int len = 0; + int r; + + chunk_header_t *chunk = (void*) source; + /* move to next chunk */ + source += sizeof(chunk_header_t); + + if (chunk->chunk_type == CHUNK_TYPE_RAW) { + /** + * +----------+-------------+ + * | |chunk_header | source + * +----------+-------------+ + * + * +------------+-----------+ + * | |chunk_header | source + * +------------+-----------+ + */ + /*init source address eq or ge boundary*/ + if (source >= BOUNDARY) { + left = (unsigned int)(BOUNDARY -(unsigned char *) chunk); + memcpy((void *)(SZ_128M - left), (void *)chunk, left); + source = (unsigned char *)(SZ_128M - left); + printf(" Case 1 chunk header source >= BOUNDARY left :0x%x ,source:0x%x\n", left ,source); + return 0; + } + /** + * +--+---------------------+ + * | |chunk_header | source| + * +--+---------------------+ + */ + /*chunk end eq boundary*/ + if ((source + (chunk->chunk_sz * header->blk_sz)) == BOUNDARY) { + ///////////////////////////////////////////////// + len = chunk->chunk_sz * header->blk_sz; + + if (chunk->total_sz != (len + sizeof(chunk_header_t))) { + printf(" 1 _unsparse: bad chunk size for chunk 0x%x, type Raw\n", i); + return 1; + } + + outlen += len/512; + if (outlen > section_size) { + printf("_unsparse: section size %d sector limit: exceeded\n", section_size); + return 1; + } +#ifdef SDEBUG + printf("source eq boundary _unsparse: RAW chunk->chunk_sz=%x header->blk_sz=0x%x: write(sector=0x%x,len=0x%x),outlen=0x%x KB\n", + chunk->chunk_sz, header->blk_sz, sector, len, outlen/2); +#endif + len_mmc = len /512; + + if (len % 512) + len_mmc++; + + r = WRITE(mmcc, sector, len_mmc, source); + if (r < 0) { + printf("_unsparse: mmc write failed\n"); + return 1; + } + + sector += len_mmc; + source += len; + source = (unsigned char *)SZ_128M; + i++; + printf(" Case 2 data end = BOUNDARY left :0x%x ,source:0x%x\n", left ,source); + return 0; //success for next image + } + /** + * + * +--------+---------------+ + * | |chunk_header | source| + * +--------+---------------+ + * + */ + /*chunk data gt boundary */ + if (source + (chunk->chunk_sz * header->blk_sz) > BOUNDARY) { + ///////////////////////////////////////////////// + len = chunk->chunk_sz * header->blk_sz; + + if (chunk->total_sz != (len + sizeof(chunk_header_t))) { + printf(" 2 _unsparse: bad chunk size for chunk 0x%x, type Raw,chunk->total_sz :0x%x ,len :0x%x\n", i ,chunk->total_sz, len); + return 1; + } + + len = (unsigned int)((BOUNDARY - source) / header->blk_sz) * header->blk_sz; + + left = (unsigned int)(BOUNDARY - source) % header->blk_sz; + + outlen += len/512; + if (outlen > section_size) { + printf("_unsparse: section size %d MB limit: exceeded\n", section_size /(1024*1024)); + return 1; + } +#ifdef SDEBUG + printf("source over boundary _unsparse: RAW chunk->chunk_sz=0x%x header->blk_sz=0x%x: write(sector=0x%x,len=0x%x),outlen=0x%x KB ,i =0x%x\n", + chunk->chunk_sz, header->blk_sz, sector, len, outlen/1024 ,i ); +#endif + len_mmc = len /512; + + r = WRITE(mmcc, sector, len_mmc, source); + if (r < 0) { + printf("_unsparse: mmc write failed\n"); + return 1; + } + + sector += len_mmc; + chunk->chunk_sz -= (len / header->blk_sz); + chunk->total_sz -= len; + memcpy((void *)(SZ_128M - sizeof(chunk_header_t) - left), (void *)chunk, sizeof(chunk_header_t)); + memcpy((void *)(SZ_128M - left), (void *)(source + len), left); + source =(unsigned char *)( SZ_128M - sizeof(chunk_header_t) - left); + chunk = (void *)source; + printf(" Case 3 data >= BOUNDARY left :0x%x ,source:0x%x\n", left ,source); + return 0; //success for next image + } + } + + if (chunk->chunk_type == CHUNK_TYPE_DONT_CARE) { + /** + * +----------+-------------+ + * | |chunk_header | source + * +----------+-------------+ + * + * +------------+-----------+ + * | |chunk_header | source + * +------------+-----------+ + */ + if (source >= BOUNDARY) { + left = (unsigned int)(BOUNDARY - (unsigned char *)chunk); + memcpy((void *)(SZ_128M - left), (void *)chunk, left); + source =(unsigned char *)( SZ_128M - left); + printf("Case 4 x type source >= BOUNDARY left :0x%x ,source:0x%x\n", left ,source); + return 0; + } + } + + switch (chunk->chunk_type) { + case CHUNK_TYPE_RAW: + len = chunk->chunk_sz * header->blk_sz; + + if (chunk->total_sz != (len + sizeof(chunk_header_t))) { + printf(" 3 _unsparse: bad RAW chunk size for chunk 0x%x, chunk->total_sz :0x%x ,len :0x%x\n", i, chunk->total_sz, len ); + return 1; + } + + outlen += len/512; + if (outlen > section_size) { + printf("_unsparse: section size %d sector limit: exceeded\n", section_size); + return 1; + } +#ifdef SDEBUG + printf("_unsparse: RAW chunk->chunk_sz=%x header->blk_sz=0x%x: write(sector=0x%x,len=0x%x),outlen=0x%x KB\n", + chunk->chunk_sz, header->blk_sz, sector, len, outlen/2); +#endif + len_mmc = len /512; + if (len % 512) + len_mmc++; + + r = WRITE(mmcc, sector, len_mmc, source); + if (r < 0) { + printf("_unsparse: mmc write failed\n"); + return 1; + } + + sector += len_mmc; + source += len; + break; + + case CHUNK_TYPE_DONT_CARE: + if (chunk->total_sz != sizeof(chunk_header_t)) { + printf(" _unsparse: bogus DONT CARE chunk\n"); + return 1; + } + len = chunk->chunk_sz * header->blk_sz; +#ifdef SDEBUG + printf("_unsparse: DONT_CARE blk=%d bsz=%d: skip(sector=%d,len=%d)\n", + chunk->chunk_sz, header->blk_sz, sector, len); +#endif + + outlen += len/512; + if (outlen > section_size) { + printf("_unsparse: section size %d sector limit: exceeded\n", section_size); + return 1; + } + sector += (len / 512); + break; + + default: + printf("_unsparse: unknown chunk ID %04x\n", chunk->chunk_type); + return 1; + } + } + + return 0; +} + +u8 do_unsparse(unsigned char init, unsigned char *source, u32 sector, u32 section_size, char *slot_no) +{ + unsigned mmcc = simple_strtoul(slot_no, NULL, 16); + //printf("%s, slot_no:%x, mmcc:%x,sector:0x%x,section_size:0x%x \n",__FUNCTION__, slot_no, mmcc, sector, section_size); + if (_unsparse(init, source, sector, section_size, mmcc, mmc_fb_write)) + return 1; + return 0; +} + |