From: Chanho Park Date: Tue, 4 Aug 2015 04:51:27 +0000 (+0900) Subject: common: decompress_ext4: support multiple chain downloads X-Git-Tag: submit/tizen/20160318.071304~90 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2e0dd8765bcaf69800c1a97a98133cd2f232626b;p=profile%2Fcommon%2Fplatform%2Fkernel%2Fu-boot-artik.git common: decompress_ext4: support multiple chain downloads 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 --- diff --git a/common/decompress_ext4.c b/common/decompress_ext4.c index 9c1fa183b..5b666af2c 100644 --- a/common/decompress_ext4.c +++ b/common/decompress_ext4.c @@ -18,7 +18,17 @@ #include #include +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; } diff --git a/include/decompress_ext4.h b/include/decompress_ext4.h index ddc5732a3..bea94d255 100644 --- a/include/decompress_ext4.h +++ b/include/decompress_ext4.h @@ -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);