common: decompress_ext4: support multiple chain downloads
authorChanho Park <chanho61.park@samsung.com>
Tue, 4 Aug 2015 04:51:27 +0000 (13:51 +0900)
committerChanho Park <chanho61.park@samsung.com>
Tue, 4 Aug 2015 05:14:04 +0000 (14:14 +0900)
This patch supports multiple chain downloads which splits a large buffer to
small buffers. The sparse image of android consists of file header,
chunk header and raw data. The file header is the first 28 bytes of the
buffer and it indicates meta information like total size, total chunk
count. Following the file header, there is multiple chunks which has a
chunk header and raw data. The types of the chunks are RAW, FILL and
NONE. The RAW type has raw data in the chunk and the FILL type means
there is no raw data thus we could skip the sectors.

The original scheme of fastboot is flashing after we download the whole
data. However, there is no way to store a huge image if we don't have
sufficient ram buffers. Thus, we need to flash the image into small size
buffers. To support chain download, we need to keep meta information
like current sector position.

|------------------------80MB---------------------|
|-----32MB---------|-------32MB-----|----16MB-----|
|FH|CH|----RAW------|CH|FILL|CH|-------RAW--------|

For example, there is 80MB image to download and flash, we could split
them into three chunks which the max is 32MB or predefined size. We
should consider residue bytes in boundary of chunks. The residue bytes
should be stored a buffer and flash them when next chunks will be
flashed.

Change-Id: Idabb738fa1c2fac1b1e2c561e3cbdb7fa5d5ac8f
Signed-off-by: Chanho Park <chanho61.park@samsung.com>
common/decompress_ext4.c
include/decompress_ext4.h

index 9c1fa183bb8643c15ff07241a29699b404a76a20..5b666af2c156dad3fa26f52c12132868ed7d96dd 100644 (file)
 #include <mmc.h>
 #include <div64.h>
 
+static ext4_chunk_header chunk_header;
+static ext4_file_header *file_header;
+static int total_chunks;
+static int remain_chunk_sectors;
+static unsigned int sector_base;
+
+static int residue_bytes;
+static char residue_buf[512];
+
 #define SECTOR_BITS            9       /* 512B */
+#define SECTOR_SIZE            (1 << SECTOR_BITS)
 
 #define ext4_printf(args, ...)
 
@@ -86,53 +96,145 @@ int write_raw_chunk(char* data, unsigned int sector, unsigned int sector_size) {
        return 0;
 }
 
-int write_compressed_ext4(char* img_base, unsigned int sector_base) {
-       unsigned int sector_size;
-       int total_chunks;
-       ext4_chunk_header *chunk_header;
-       ext4_file_header *file_header;
+ext4_file_header *init_compressed_ext4(char *img_base)
+{
+       ext4_file_header *f_hdr;
 
-       file_header = (ext4_file_header*)img_base;
-       total_chunks = file_header->total_chunks;
+       f_hdr = (ext4_file_header *)malloc(sizeof(ext4_file_header));
+       if (!f_hdr)
+               return NULL;
 
-       ext4_printf("total chunk = %d \n", total_chunks);
+       memcpy(f_hdr, img_base, sizeof(ext4_file_header));
+       total_chunks = f_hdr->total_chunks;
+       ext4_printf("total chunk = %d\n", total_chunks);
 
-       img_base += EXT4_FILE_HEADER_SIZE;
+       return f_hdr;
+}
 
-       while(total_chunks) {
-               chunk_header = (ext4_chunk_header*)img_base;
-               sector_size = (chunk_header->chunk_size * file_header->block_size) >> SECTOR_BITS;
+void exit_compressed_ext4(void)
+{
+       if (file_header)
+               free(file_header);
+       file_header = NULL;
+       total_chunks = 0;
+       remain_chunk_sectors = 0;
+       residue_bytes = 0;
+       sector_base = 0;
+}
 
-               switch(chunk_header->type)
-               {
+int write_compressed_ext4(char *img_base, unsigned int buflen,
+                         unsigned int base, unsigned int length)
+{
+       unsigned int sector_size;
+       int remain_bytes = buflen;
+
+       if (!file_header) {
+               /* If there is no file_header, it means the first download
+                * stage. First of all, we need to parse the file header */
+               file_header = init_compressed_ext4(img_base);
+               if (!file_header)
+                       return -1;
+               img_base += file_header->file_header_size;
+               remain_bytes -= file_header->file_header_size;
+               sector_base = base;
+       }
+
+       /* Flash buffer until there is no remain buffer */
+       while (remain_bytes > 0) {
+               if (!remain_chunk_sectors) {
+                       if (remain_bytes < sizeof(ext4_chunk_header)) {
+                               printf("Cannot parse chunk_header\n");
+                               return -1;
+                       }
+
+                       memcpy(&chunk_header, img_base,
+                                       sizeof(ext4_chunk_header));
+                       img_base += file_header->chunk_header_size;
+                       remain_bytes -= file_header->chunk_header_size;
+
+                       sector_size = (chunk_header.chunk_size *
+                               file_header->block_size) >> SECTOR_BITS;
+                       remain_chunk_sectors = sector_size;
+               } else
+                       sector_size = remain_chunk_sectors;
+
+               switch (chunk_header.type) {
                case EXT4_CHUNK_TYPE_RAW:
-                       ext4_printf("raw_chunk \n");
-                       write_raw_chunk(img_base + EXT4_CHUNK_HEADER_SIZE,
-                                                       sector_base, sector_size);
-                       sector_base += sector_size;
+                       ext4_printf("raw_chunk\n");
+                       if (residue_bytes) {
+                               /* If there is residue buffer, fill a sector
+                                * and flash it */
+                               memcpy(residue_buf + residue_bytes, img_base,
+                                               SECTOR_SIZE - residue_bytes);
+                               write_raw_chunk(residue_buf, sector_base, 1);
+                               /* Move a location of sector base */
+                               sector_base++;
+                               img_base += (SECTOR_SIZE - residue_bytes);
+                               sector_size--;
+                               remain_chunk_sectors--;
+                               remain_bytes -= (SECTOR_SIZE - residue_bytes);
+                               residue_bytes = 0;
+                       }
+
+                       if (sector_size > (remain_bytes >> SECTOR_BITS)) {
+                               /* If we need to keep residue buffer, we store
+                                * them into residue_buf and flash them during
+                                * next download */
+                               residue_bytes = remain_bytes -
+                                       ((remain_bytes >> SECTOR_BITS)
+                                        << SECTOR_BITS);
+                               sector_size = remain_bytes >> SECTOR_BITS;
+                               if (residue_bytes) {
+                                       memcpy(residue_buf, img_base +
+                                               (sector_size << SECTOR_BITS),
+                                               residue_bytes);
+                               }
+                       }
+
+                       if (sector_size) {
+                               write_raw_chunk(img_base, sector_base,
+                                               sector_size);
+                               sector_base += sector_size;
+                               img_base += (sector_size << SECTOR_BITS);
+                               remain_bytes -= ((sector_size << SECTOR_BITS)
+                                               + residue_bytes);
+                       }
                        break;
 
                case EXT4_CHUNK_TYPE_FILL:
-                       printf("*** fill_chunk ***\n");
                        sector_base += sector_size;
+                       img_base += (chunk_header.total_size -
+                                       file_header->chunk_header_size);
+                       remain_bytes -= (chunk_header.total_size -
+                                       file_header->chunk_header_size);
                        break;
 
                case EXT4_CHUNK_TYPE_NONE:
-                       ext4_printf("none chunk \n");
+                       ext4_printf("none chunk\n");
                        sector_base += sector_size;
+                       img_base += (chunk_header.total_size -
+                                       file_header->chunk_header_size);
+                       remain_bytes -= (chunk_header.total_size -
+                                       file_header->chunk_header_size);
                        break;
 
                default:
-                       printf("*** unknown chunk type ***\n");
-                       sector_base += sector_size;
-                       break;
+                       ext4_printf("*** unknown chunk type ***\n");
+                       /* If unknown chunk type is founded, return error */
+                       exit_compressed_ext4();
+                       return -1;
                }
-               total_chunks--;
-               ext4_printf("remain chunks = %d \n", total_chunks);
-
-               img_base += chunk_header->total_size;
+               remain_chunk_sectors -= sector_size;
+               if (remain_chunk_sectors == 0)
+                       total_chunks--;
+
+               if (total_chunks == 0) {
+                       ext4_printf("write done\n");
+                       exit_compressed_ext4();
+                       return 0;
+               }
+               ext4_printf("remain chunks = %d\n", total_chunks);
        };
 
-       ext4_printf("write done \n");
        return 0;
 }
index ddc5732a3a5068369237ef9542240e0bd78d095e..bea94d2554da32881769a54aba23e6d08b7b4e79 100644 (file)
@@ -39,6 +39,9 @@ typedef struct _ext4_chunk_header {
 #define EXT4_CHUNK_TYPE_FILL           0xCAC2
 #define EXT4_CHUNK_TYPE_NONE           0xCAC3
 
-int write_compressed_ext4(char* img_base, unsigned int sector_base);
+ext4_file_header *init_compressed_ext4(char *img_base);
+void exit_compressed_ext4(void);
+int write_compressed_ext4(char *img_base, unsigned int buflen,
+                       unsigned int sector_base, unsigned int length);
 int check_compress_ext4(char *img_base, unsigned long long parti_size);