[U-Boot] [PATCH 03/13] sparse: Refactor chunk parsing function

Maxime Ripard
2015-08-31T14:46:03Z

The chunk parsing code was duplicating a lot of code among the various
chunk types, while all of them could be covered by generic and simple
functions.

Refactor the current code to reuse as much code as possible and hopefully
make the chunk parsing loop more readable and concise.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
common/aboot.c | 351 ++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 221 insertions(+), 130 deletions(-)

diff --git a/common/aboot.c b/common/aboot.c
index 7f12677d13a1..65e633acfcb9 100644
--- a/common/aboot.c
+++ b/common/aboot.c
@@ -37,10 +37,29 @@
#include <config.h>
#include <common.h>
#include <aboot.h>
+#include <errno.h>
#include <malloc.h>
#include <part.h>
#include <sparse_format.h>

+static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse,
+ chunk_header_t *chunk)
+{
+ return chunk->total_sz - sparse->chunk_hdr_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;
+ }
+}
+
static sparse_header_t *sparse_parse_header(void **data)
{
/* Read and skip over sparse image header */
@@ -61,6 +80,173 @@ static sparse_header_t *sparse_parse_header(void **data)
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);
+}
+
void write_sparse_image(block_dev_desc_t *dev_desc,
disk_partition_t *info, const char *part_name,
void *data, unsigned sz)
@@ -69,12 +255,10 @@ void write_sparse_image(block_dev_desc_t *dev_desc,
lbaint_t blkcnt;
lbaint_t blks;
uint32_t bytes_written = 0;
- unsigned int chunk;
- unsigned int chunk_data_sz;
- uint32_t *fill_buf = NULL;
- uint32_t fill_val;
+ unsigned int chunk;
sparse_header_t *sparse_header;
- chunk_header_t *chunk_header;
+ chunk_header_t *chunk_header;
+ sparse_buffer_t *buffer;
uint32_t total_blocks = 0;
int i;

@@ -97,142 +281,49 @@ void write_sparse_image(block_dev_desc_t *dev_desc,

/* Start processing chunks */
blk = info->start;
- for (chunk=0; chunk<sparse_header->total_chunks; chunk++)
- {
- /* 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);
+ for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) {
+ chunk_header = sparse_parse_chunk(sparse_header, &data);
+ if (!chunk_header) {
+ fastboot_fail("Unknown chunk type");
+ return;
}

- 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,
+ info->blksz, &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(
- "Bogus chunk size for chunk type Raw");
- return;
- }
+ blkcnt = (buffer->length / info->blksz) * buffer->repeat;

- if (blk + blkcnt > info->start + info->size) {
- printf(
- "%s: Request would exceed partition size!\n",
- __func__);
- fastboot_fail(
- "Request would exceed partition size!");
- return;
- }
-
- blks = dev_desc->block_write(dev_desc->dev, blk, blkcnt,
- data);
- if (blks != blkcnt) {
- printf("%s: Write failed " LBAFU "\n",
- __func__, blks);
- fastboot_fail("flash write failure");
- return;
- }
- blk += blkcnt;
- 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(
- "Bogus chunk size for chunk type FILL");
- return;
- }
-
- fill_buf = (uint32_t *)
- memalign(ARCH_DMA_MINALIGN,
- ROUNDUP(info->blksz,
- ARCH_DMA_MINALIGN));
- if (!fill_buf)
- {
- fastboot_fail(
- "Malloc failed for: CHUNK_TYPE_FILL");
- return;
- }
-
- fill_val = *(uint32_t *)data;
- data = (char *) data + sizeof(uint32_t);
+ if (blk + blkcnt > info->start + info->size) {
+ printf("%s: Request would exceed partition size!\n",
+ __func__);
+ fastboot_fail("Request would exceed partition size!");
+ return;
+ }

- for (i = 0; i < (info->blksz / sizeof(fill_val)); i++)
- fill_buf[i] = fill_val;
+ for (i = 0; i < buffer->repeat; i++) {
+ unsigned int buffer_blk_cnt;
+ unsigned int buffer_blks;

- if (blk + blkcnt > info->start + info->size) {
- printf(
- "%s: Request would exceed partition size!\n",
- __func__);
- fastboot_fail(
- "Request would exceed partition size!");
- return;
- }
+ buffer_blk_cnt = buffer->length / storage->block_sz;

- for (i = 0; i < blkcnt; i++) {
- blks = dev_desc->block_write(dev_desc->dev,
- blk, 1, fill_buf);
- if (blks != 1) {
- printf(
- "%s: Write failed, block # " LBAFU "\n",
- __func__, blkcnt);
- fastboot_fail("flash write failure");
- free(fill_buf);
- return;
- }
- blk++;
- }
- bytes_written += blkcnt * info->blksz;
- total_blocks += chunk_data_sz / sparse_header->blk_sz;
-
- free(fill_buf);
- break;
-
- case CHUNK_TYPE_DONT_CARE:
- blk += blkcnt;
- total_blocks += chunk_header->chunk_sz;
- break;
-
- case CHUNK_TYPE_CRC32:
- if (chunk_header->total_sz !=
- sparse_header->chunk_hdr_sz)
- {
- fastboot_fail(
- "Bogus chunk size for chunk type Dont Care");
+ buffer_blks = dev_desc->block_write(dev_desc->dev, blk,
+ buffer_blk_cnt, buffer->data);
+ if (buffer_blks != buffer_blk_cnt) {
+ printf("%s: Write %d failed " LBAFU "\n",
+ __func__, i, buffer_blks);
+ fastboot_fail("flash write failure");
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("Unknown chunk type");
- return;
+ blk += buffer_blk_cnt;
+ bytes_written += buffer->length;
+ total_blocks += buffer_blk_cnt;
}
+
+ sparse_put_data_buffer(buffer);
}

debug("Wrote %d blocks, expected to write %d blocks\n",
--
2.5.0