diff options
author | Steve Rae <srae@broadcom.com> | 2016-06-07 11:19:36 -0700 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-06-27 16:37:36 -0400 |
commit | cc0f08cd347ea9741375a70c490c6bee684f7bac (patch) | |
tree | b1a4d8c4dfb3cfb62439355726e570e7f52fd7d9 /common/image-sparse.c | |
parent | 64ece84854ae49f40e9b9d4d88502247774f9d2f (diff) |
fastboot: sparse: resync common/image-sparse.c (part 1)
This file originally came from upstream code.
While retaining the storage abstraction feature, this is the first
set of the changes required to resync with the
cmd_flash_mmc_sparse_img()
in the file
aboot.c
from
https://us.codeaurora.org/cgit/quic/la/kernel/lk/plain/app/aboot/aboot.c?h=LE.BR.1.2.1
Signed-off-by: Steve Rae <srae@broadcom.com>
Diffstat (limited to 'common/image-sparse.c')
-rw-r--r-- | common/image-sparse.c | 449 |
1 files changed, 157 insertions, 292 deletions
diff --git a/common/image-sparse.c b/common/image-sparse.c index 893c68b35f7..924cc63797e 100644 --- a/common/image-sparse.c +++ b/common/image-sparse.c @@ -36,54 +36,44 @@ #include <config.h> #include <common.h> -#include <div64.h> -#include <errno.h> #include <image-sparse.h> +#include <div64.h> #include <malloc.h> #include <part.h> #include <sparse_format.h> +#include <fastboot.h> #include <linux/math64.h> -typedef struct sparse_buffer { - void *data; - u32 length; - u32 repeat; - u16 type; -} sparse_buffer_t; - -static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse, - chunk_header_t *chunk) +void write_sparse_image( + struct sparse_storage *info, const char *part_name, + void *data, unsigned sz, char *response_str) { - return chunk->total_sz - sparse->chunk_hdr_sz; -} - -static unsigned int sparse_block_size_to_storage(unsigned int size, - sparse_storage_t *storage, - sparse_header_t *sparse) -{ - return (unsigned int)lldiv((uint64_t)size * sparse->blk_sz, - storage->block_sz); -} - -static bool sparse_chunk_has_buffer(chunk_header_t *chunk) -{ - switch (chunk->chunk_type) { - case CHUNK_TYPE_RAW: - case CHUNK_TYPE_FILL: - return true; - - default: - return false; - } -} + lbaint_t blk; + lbaint_t blkcnt; + lbaint_t blks; + uint32_t bytes_written = 0; + unsigned int chunk; + unsigned int offset; + unsigned int chunk_data_sz; + uint32_t *fill_buf = NULL; + uint32_t fill_val; + sparse_header_t *sparse_header; + chunk_header_t *chunk_header; + uint32_t total_blocks = 0; + int i; -static sparse_header_t *sparse_parse_header(void **data) -{ /* Read and skip over sparse image header */ - sparse_header_t *sparse_header = (sparse_header_t *) *data; + sparse_header = (sparse_header_t *)data; - *data += sparse_header->file_hdr_sz; + data += sparse_header->file_hdr_sz; + if (sparse_header->file_hdr_sz > sizeof(sparse_header_t)) { + /* + * Skip the remaining bytes in a header that is longer than + * we expected. + */ + data += (sparse_header->file_hdr_sz - sizeof(sparse_header_t)); + } debug("=== Sparse Image Header ===\n"); debug("magic: 0x%x\n", sparse_header->magic); @@ -95,289 +85,164 @@ static sparse_header_t *sparse_parse_header(void **data) debug("total_blks: %d\n", sparse_header->total_blks); debug("total_chunks: %d\n", sparse_header->total_chunks); - return sparse_header; -} - -static int sparse_parse_fill_chunk(sparse_header_t *sparse, - chunk_header_t *chunk) -{ - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); - - if (chunk_data_sz != sizeof(uint32_t)) - return -EINVAL; - - return 0; -} - -static int sparse_parse_raw_chunk(sparse_header_t *sparse, - chunk_header_t *chunk) -{ - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); - - /* Check if the data size is a multiple of the main block size */ - if (chunk_data_sz % sparse->blk_sz) - return -EINVAL; - - /* Check that the chunk size is consistent */ - if ((chunk_data_sz / sparse->blk_sz) != chunk->chunk_sz) - return -EINVAL; - - return 0; -} - -static chunk_header_t *sparse_parse_chunk(sparse_header_t *sparse, - void **image) -{ - chunk_header_t *chunk = (chunk_header_t *) *image; - int ret; - - debug("=== Chunk Header ===\n"); - debug("chunk_type: 0x%x\n", chunk->chunk_type); - debug("chunk_data_sz: 0x%x\n", chunk->chunk_sz); - debug("total_size: 0x%x\n", chunk->total_sz); - - switch (chunk->chunk_type) { - case CHUNK_TYPE_RAW: - ret = sparse_parse_raw_chunk(sparse, chunk); - if (ret) - return NULL; - break; - - case CHUNK_TYPE_FILL: - ret = sparse_parse_fill_chunk(sparse, chunk); - if (ret) - return NULL; - break; - - case CHUNK_TYPE_DONT_CARE: - case CHUNK_TYPE_CRC32: - debug("Ignoring chunk\n"); - break; - - default: - printf("%s: Unknown chunk type: %x\n", __func__, - chunk->chunk_type); - return NULL; - } - - *image += sparse->chunk_hdr_sz; - - return chunk; -} - -static int sparse_get_fill_buffer(sparse_header_t *sparse, - chunk_header_t *chunk, - sparse_buffer_t *buffer, - unsigned int blk_sz, - void *data) -{ - int i; - - buffer->type = CHUNK_TYPE_FILL; - - /* - * We create a buffer of one block, and ask it to be - * repeated as many times as needed. - */ - buffer->length = blk_sz; - buffer->repeat = (chunk->chunk_sz * sparse->blk_sz) / blk_sz; - - buffer->data = memalign(ARCH_DMA_MINALIGN, - ROUNDUP(blk_sz, - ARCH_DMA_MINALIGN)); - if (!buffer->data) - return -ENOMEM; - - for (i = 0; i < (buffer->length / sizeof(uint32_t)); i++) - ((uint32_t *)buffer->data)[i] = *(uint32_t *)(data); - - return 0; -} - -static int sparse_get_raw_buffer(sparse_header_t *sparse, - chunk_header_t *chunk, - sparse_buffer_t *buffer, - unsigned int blk_sz, - void *data) -{ - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); - - buffer->type = CHUNK_TYPE_RAW; - buffer->length = chunk_data_sz; - buffer->data = data; - buffer->repeat = 1; - - return 0; -} - -static sparse_buffer_t *sparse_get_data_buffer(sparse_header_t *sparse, - chunk_header_t *chunk, - unsigned int blk_sz, - void **image) -{ - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); - sparse_buffer_t *buffer; - void *data = *image; - int ret; - - *image += chunk_data_sz; - - if (!sparse_chunk_has_buffer(chunk)) - return NULL; - - buffer = calloc(sizeof(sparse_buffer_t), 1); - if (!buffer) - return NULL; - - switch (chunk->chunk_type) { - case CHUNK_TYPE_RAW: - ret = sparse_get_raw_buffer(sparse, chunk, buffer, blk_sz, - data); - if (ret) - return NULL; - break; - - case CHUNK_TYPE_FILL: - ret = sparse_get_fill_buffer(sparse, chunk, buffer, blk_sz, - data); - if (ret) - return NULL; - break; - - default: - return NULL; - } - - debug("=== Buffer ===\n"); - debug("length: 0x%x\n", buffer->length); - debug("repeat: 0x%x\n", buffer->repeat); - debug("type: 0x%x\n", buffer->type); - debug("data: 0x%p\n", buffer->data); - - return buffer; -} - -static void sparse_put_data_buffer(sparse_buffer_t *buffer) -{ - if (buffer->type == CHUNK_TYPE_FILL) - free(buffer->data); - - free(buffer); -} - -int store_sparse_image(sparse_storage_t *storage, - void *storage_priv, void *data) -{ - unsigned int chunk, offset; - sparse_header_t *sparse_header; - chunk_header_t *chunk_header; - sparse_buffer_t *buffer; - uint32_t start; - uint32_t total_blocks = 0; - int i; - - debug("=== Storage ===\n"); - debug("name: %s\n", storage->name); - debug("block_size: 0x%x\n", storage->block_sz); - debug("start: 0x%x\n", storage->start); - debug("size: 0x%x\n", storage->size); - debug("write: 0x%p\n", storage->write); - debug("priv: 0x%p\n", storage_priv); - - sparse_header = sparse_parse_header(&data); - if (!sparse_header) { - printf("sparse header issue\n"); - return -EINVAL; - } - /* * Verify that the sparse block size is a multiple of our * storage backend block size */ - div_u64_rem(sparse_header->blk_sz, storage->block_sz, &offset); + div_u64_rem(sparse_header->blk_sz, info->blksz, &offset); if (offset) { printf("%s: Sparse image block size issue [%u]\n", __func__, sparse_header->blk_sz); - return -EINVAL; + fastboot_fail(response_str, "sparse image block size issue"); + return; } puts("Flashing Sparse Image\n"); /* Start processing chunks */ - start = storage->start; + blk = info->start; for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { - uint32_t blkcnt; - - chunk_header = sparse_parse_chunk(sparse_header, &data); - if (!chunk_header) { - printf("Unknown chunk type"); - return -EINVAL; + /* Read and skip over chunk header */ + chunk_header = (chunk_header_t *)data; + data += sizeof(chunk_header_t); + + if (chunk_header->chunk_type != CHUNK_TYPE_RAW) { + debug("=== Chunk Header ===\n"); + debug("chunk_type: 0x%x\n", chunk_header->chunk_type); + debug("chunk_data_sz: 0x%x\n", chunk_header->chunk_sz); + debug("total_size: 0x%x\n", chunk_header->total_sz); } - /* - * If we have a DONT_CARE type, just skip the blocks - * and go on parsing the rest of the chunks - */ - if (chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE) { - blkcnt = sparse_block_size_to_storage(chunk_header->chunk_sz, - storage, - sparse_header); -#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV - total_blocks += blkcnt; -#endif - continue; + if (sparse_header->chunk_hdr_sz > sizeof(chunk_header_t)) { + /* + * Skip the remaining bytes in a header that is longer + * than we expected. + */ + data += (sparse_header->chunk_hdr_sz - + sizeof(chunk_header_t)); } - /* Retrieve the buffer we're going to write */ - buffer = sparse_get_data_buffer(sparse_header, chunk_header, - storage->block_sz, &data); - if (!buffer) - continue; + chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz; + blkcnt = chunk_data_sz / info->blksz; + switch (chunk_header->chunk_type) { + case CHUNK_TYPE_RAW: + if (chunk_header->total_sz != + (sparse_header->chunk_hdr_sz + chunk_data_sz)) { + fastboot_fail(response_str, + "Bogus chunk size for chunk type Raw"); + return; + } + + if (blk + blkcnt > info->start + info->size) { + printf( + "%s: Request would exceed partition size!\n", + __func__); + fastboot_fail(response_str, + "Request would exceed partition size!"); + return; + } - blkcnt = (buffer->length / storage->block_sz) * buffer->repeat; + blks = info->write(info, blk, blkcnt, data); + /* blks might be > blkcnt (eg. NAND bad-blocks) */ + if (blks < blkcnt) { + printf("%s: %s" LBAFU " [" LBAFU "]\n", + __func__, "Write failed, block #", + blk, blks); + fastboot_fail(response_str, + "flash write failure"); + return; + } + blk += blks; + bytes_written += blkcnt * info->blksz; + total_blocks += chunk_header->chunk_sz; + data += chunk_data_sz; + break; + + case CHUNK_TYPE_FILL: + if (chunk_header->total_sz != + (sparse_header->chunk_hdr_sz + sizeof(uint32_t))) { + fastboot_fail(response_str, + "Bogus chunk size for chunk type FILL"); + return; + } - if ((start + total_blocks + blkcnt) > - (storage->start + storage->size)) { - printf("%s: Request would exceed partition size!\n", - __func__); - return -EINVAL; - } + fill_buf = (uint32_t *) + memalign(ARCH_DMA_MINALIGN, + ROUNDUP(info->blksz, + ARCH_DMA_MINALIGN)); + if (!fill_buf) { + fastboot_fail(response_str, + "Malloc failed for: CHUNK_TYPE_FILL"); + return; + } - for (i = 0; i < buffer->repeat; i++) { - unsigned long buffer_blk_cnt; - int ret; + fill_val = *(uint32_t *)data; + data = (char *)data + sizeof(uint32_t); - buffer_blk_cnt = buffer->length / storage->block_sz; + for (i = 0; i < (info->blksz / sizeof(fill_val)); i++) + fill_buf[i] = fill_val; - ret = storage->write(storage, storage_priv, - start + total_blocks, - buffer_blk_cnt, - buffer->data); - if (ret < 0) { - printf("%s: Write %d failed %d\n", - __func__, i, ret); - return ret; + if (blk + blkcnt > info->start + info->size) { + printf( + "%s: Request would exceed partition size!\n", + __func__); + fastboot_fail(response_str, + "Request would exceed partition size!"); + return; } - total_blocks += ret; - } + for (i = 0; i < blkcnt; i++) { + blks = info->write(info, blk, 1, fill_buf); + /* blks might be > 1 (eg. NAND bad-blocks) */ + if (blks < 1) { + printf("%s: %s, block # " LBAFU "\n", + __func__, "Write failed", blk); + fastboot_fail(response_str, + "flash write failure"); + free(fill_buf); + return; + } + blk += blks; + } + bytes_written += blkcnt * info->blksz; + total_blocks += chunk_data_sz / sparse_header->blk_sz; + free(fill_buf); + break; - sparse_put_data_buffer(buffer); + case CHUNK_TYPE_DONT_CARE: +#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV + blk += blkcnt; + total_blocks += chunk_header->chunk_sz; +#endif + break; + + case CHUNK_TYPE_CRC32: + if (chunk_header->total_sz != + sparse_header->chunk_hdr_sz) { + fastboot_fail(response_str, + "Bogus chunk size for chunk type Dont Care"); + return; + } + total_blocks += chunk_header->chunk_sz; + data += chunk_data_sz; + break; + + default: + printf("%s: Unknown chunk type: %x\n", __func__, + chunk_header->chunk_type); + fastboot_fail(response_str, "Unknown chunk type"); + return; + } } debug("Wrote %d blocks, expected to write %d blocks\n", - total_blocks, - sparse_block_size_to_storage(sparse_header->total_blks, - storage, sparse_header)); - printf("........ wrote %d blocks to '%s'\n", total_blocks, - storage->name); + total_blocks, sparse_header->total_blks); + printf("........ wrote %u bytes to '%s'\n", bytes_written, part_name); - if (total_blocks != - sparse_block_size_to_storage(sparse_header->total_blks, - storage, sparse_header)) { - printf("sparse image write failure\n"); - return -EIO; - } + if (total_blocks != sparse_header->total_blks) + fastboot_fail(response_str, "sparse image write failure"); + else + fastboot_okay(response_str, ""); - return 0; + return; } |