summaryrefslogtreecommitdiff
path: root/common/sparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/sparse.c')
-rwxr-xr-xcommon/sparse.c295
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;
+}
+