[U-Boot] [PATCH v2 00/14] Implement fastboot over NAND and sparse transfers

Hi everyone,
Here is the second attempt at getting fastboot flashing functions working on top of a NAND, for arbitraly large images.
While the NAND support itself was quite easy to do, the support for the Android sparse images was quite difficult to add, and ended up being a quite huge refactoring of the sparse parse already in tree, that was tied to the MMC layer.
This serie has been tested on a CHIP and an A13-Olinuxino, two Allwinner devices, the CHIP for the NAND, and the Cubietruck to test the MMC.
Let me know what you think, Maxime
Changes from v1: - Added support for multiple chunks transfers - fixed a bug in the image parsing block counts - Take the flashing errors into account now, instead of ignoring them. - Changed the name of the sparse images file to image-sparse.* - Fixed the floating point computations issues - Added the CONFIG_MUSB_GADGET option - Take NAND bad blocks into account now
Maxime Ripard (14): mtd: uboot: Add meaningful error message sparse: Move main header parsing to a function of its own sparse: Refactor chunk parsing function sparse: Simplify multiple logic fastboot: Move fastboot response functions to fastboot core sparse: Implement storage abstraction fastboot: Implement flashing session counter sparse: Implement several chunks flashing fastboot: Implement NAND backend fastboot: nand: Add pre erase and write hooks sparse: Rename the file and header sunxi: Add support for android boot image sunxi: A13-Olinuxino: Enable the USB OTG controller sunxi: cubietruck: Enable the USB OTG controller
common/Makefile | 7 +- common/aboot.c | 244 ---------------------- common/fb_mmc.c | 96 +++++---- common/fb_nand.c | 213 +++++++++++++++++++ common/image-sparse.c | 393 ++++++++++++++++++++++++++++++++++++ configs/A13-OLinuXino_defconfig | 3 + configs/Cubietruck_defconfig | 5 + drivers/mtd/mtd_uboot.c | 2 +- drivers/usb/gadget/f_fastboot.c | 54 ++++- include/configs/sunxi-common.h | 1 + include/fastboot.h | 22 ++ include/fb_mmc.h | 5 +- include/fb_nand.h | 11 + include/{aboot.h => image-sparse.h} | 17 +- 14 files changed, 776 insertions(+), 297 deletions(-) delete mode 100644 common/aboot.c create mode 100644 common/fb_nand.c create mode 100644 common/image-sparse.c create mode 100644 include/fastboot.h create mode 100644 include/fb_nand.h rename include/{aboot.h => image-sparse.h} (53%)

The current error message in get_part if CONFIG_MTDPARTS is disabled is "offset is not a number" which is confusing and doesn't help at all.
Change that for something that might give a hint on what's going on.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com --- drivers/mtd/mtd_uboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c index c517b9c65d68..21386951efd5 100644 --- a/drivers/mtd/mtd_uboot.c +++ b/drivers/mtd/mtd_uboot.c @@ -37,7 +37,7 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size,
return 0; #else - puts("offset is not a number\n"); + puts("mtdparts support missing.\n"); return -1; #endif }

On Thu, Oct 15, 2015 at 02:34:09PM +0200, Maxime Ripard wrote:
The current error message in get_part if CONFIG_MTDPARTS is disabled is "offset is not a number" which is confusing and doesn't help at all.
Change that for something that might give a hint on what's going on.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The current sparse image format parser is quite tangled, with a lot of code duplication.
Start refactoring it by moving the header parsing function to a function of its own.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com --- common/aboot.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-)
diff --git a/common/aboot.c b/common/aboot.c index fba8e3e683e7..cd1b6a6ac8e0 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -41,6 +41,26 @@ #include <part.h> #include <sparse_format.h>
+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; + + *data += sparse_header->file_hdr_sz; + + debug("=== Sparse Image Header ===\n"); + debug("magic: 0x%x\n", sparse_header->magic); + debug("major_version: 0x%x\n", sparse_header->major_version); + debug("minor_version: 0x%x\n", sparse_header->minor_version); + debug("file_hdr_sz: %d\n", sparse_header->file_hdr_sz); + debug("chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz); + debug("blk_sz: %d\n", sparse_header->blk_sz); + debug("total_blks: %d\n", sparse_header->total_blks); + debug("total_chunks: %d\n", sparse_header->total_chunks); + + return sparse_header; +} + void write_sparse_image(block_dev_desc_t *dev_desc, disk_partition_t *info, const char *part_name, void *data, unsigned sz) @@ -58,29 +78,12 @@ void write_sparse_image(block_dev_desc_t *dev_desc, uint32_t total_blocks = 0; int i;
- /* Read and skip over sparse image header */ - sparse_header = (sparse_header_t *) data; - - 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)); + sparse_header = sparse_parse_header(&data); + if (!sparse_header) { + fastboot_fail("sparse header issue\n"); + return; }
- debug("=== Sparse Image Header ===\n"); - debug("magic: 0x%x\n", sparse_header->magic); - debug("major_version: 0x%x\n", sparse_header->major_version); - debug("minor_version: 0x%x\n", sparse_header->minor_version); - debug("file_hdr_sz: %d\n", sparse_header->file_hdr_sz); - debug("chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz); - debug("blk_sz: %d\n", sparse_header->blk_sz); - debug("total_blks: %d\n", sparse_header->total_blks); - debug("total_chunks: %d\n", sparse_header->total_chunks); - /* verify sparse_header->blk_sz is an exact multiple of info->blksz */ if (sparse_header->blk_sz != (sparse_header->blk_sz & ~(info->blksz - 1))) {

On Thu, Oct 15, 2015 at 02:34:10PM +0200, Maxime Ripard wrote:
The current sparse image format parser is quite tangled, with a lot of code duplication.
Start refactoring it by moving the header parsing function to a function of its own.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

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 Reviewed-by: Tom Rini trini@konsulko.com --- common/aboot.c | 372 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 239 insertions(+), 133 deletions(-)
diff --git a/common/aboot.c b/common/aboot.c index cd1b6a6ac8e0..243b330d9126 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -37,10 +37,36 @@ #include <config.h> #include <common.h> #include <aboot.h> +#include <errno.h> #include <malloc.h> #include <part.h> #include <sparse_format.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) +{ + 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,21 +87,185 @@ 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) { - lbaint_t blk; + lbaint_t start; 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; sparse_header_t *sparse_header; chunk_header_t *chunk_header; + sparse_buffer_t *buffer; uint32_t total_blocks = 0; + uint32_t skipped = 0; int i;
sparse_header = sparse_parse_header(&data); @@ -96,148 +286,64 @@ void write_sparse_image(block_dev_desc_t *dev_desc, puts("Flashing Sparse Image\n");
/* 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); + start = info->start; + 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)); + /* + * 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) { + skipped += chunk_header->chunk_sz; + 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; - } - - if (blk + blkcnt > info->start + info->size) { - printf( - "%s: Request would exceed partition size!\n", - __func__); - fastboot_fail( - "Request would exceed partition size!"); - return; - } + /* Retrieve the buffer we're going to write */ + buffer = sparse_get_data_buffer(sparse_header, chunk_header, + info->blksz, &data); + if (!buffer) + continue;
- 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; - } + blkcnt = (buffer->length / info->blksz) * buffer->repeat;
- 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 ((start + total_blocks + 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 long buffer_blk_cnt; + unsigned long 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 / info->blksz;
- 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, + start + total_blocks, + 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; + total_blocks += buffer_blk_cnt; } + + sparse_put_data_buffer(buffer); }
- debug("Wrote %d blocks, expected to write %d blocks\n", - total_blocks, sparse_header->total_blks); - printf("........ wrote %u bytes to '%s'\n", bytes_written, part_name); + debug("Wrote %d blocks, skipped %d, expected to write %d blocks\n", + total_blocks, skipped, sparse_header->total_blks); + printf("........ wrote %d blocks to '%s'\n", total_blocks, part_name);
if (total_blocks != sparse_header->total_blks) fastboot_fail("sparse image write failure");

On Thu, Oct 15, 2015 at 02:34:11PM +0200, Maxime Ripard wrote:
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 Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

To check the alignment of the image blocks to the storage blocks, the current code uses a convoluted syntax, while a simple mod also does the work.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- common/aboot.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/common/aboot.c b/common/aboot.c index 243b330d9126..2775254c742e 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -37,11 +37,14 @@ #include <config.h> #include <common.h> #include <aboot.h> +#include <div64.h> #include <errno.h> #include <malloc.h> #include <part.h> #include <sparse_format.h>
+#include <linux/math64.h> + typedef struct sparse_buffer { void *data; u32 length; @@ -260,7 +263,7 @@ void write_sparse_image(block_dev_desc_t *dev_desc, { lbaint_t start; lbaint_t blkcnt; - unsigned int chunk; + unsigned int chunk, offset; sparse_header_t *sparse_header; chunk_header_t *chunk_header; sparse_buffer_t *buffer; @@ -274,9 +277,12 @@ void write_sparse_image(block_dev_desc_t *dev_desc, return; }
- /* verify sparse_header->blk_sz is an exact multiple of info->blksz */ - if (sparse_header->blk_sz != - (sparse_header->blk_sz & ~(info->blksz - 1))) { + /* + * Verify that the sparse block size is a multiple of our + * storage backend block size + */ + 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); fastboot_fail("sparse image block size issue");

On Thu, Oct 15, 2015 at 02:34:12PM +0200, Maxime Ripard wrote:
To check the alignment of the image blocks to the storage blocks, the current code uses a convoluted syntax, while a simple mod also does the work.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Reviewed-by: Tom Rini trini@konsulko.com

On Thu, Oct 15, 2015 at 02:34:12PM +0200, Maxime Ripard wrote:
To check the alignment of the image blocks to the storage blocks, the current code uses a convoluted syntax, while a simple mod also does the work.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The functions and a few define to generate a fastboot message to be sent back to the host were so far duplicated among the users.
Move them all to a common place.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- common/aboot.c | 32 ++++++++++++++--------------- common/fb_mmc.c | 45 ++++++++++++++++------------------------- drivers/usb/gadget/f_fastboot.c | 27 +++++++++++++++++-------- include/aboot.h | 9 +++------ include/fastboot.h | 22 ++++++++++++++++++++ 5 files changed, 76 insertions(+), 59 deletions(-) create mode 100644 include/fastboot.h
diff --git a/common/aboot.c b/common/aboot.c index 2775254c742e..5f5fb6b62bfa 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -257,9 +257,9 @@ static void sparse_put_data_buffer(sparse_buffer_t *buffer) free(buffer); }
-void write_sparse_image(block_dev_desc_t *dev_desc, - disk_partition_t *info, const char *part_name, - void *data, unsigned sz) +int write_sparse_image(block_dev_desc_t *dev_desc, + disk_partition_t *info, const char *part_name, + void *data, unsigned sz) { lbaint_t start; lbaint_t blkcnt; @@ -273,8 +273,8 @@ void write_sparse_image(block_dev_desc_t *dev_desc,
sparse_header = sparse_parse_header(&data); if (!sparse_header) { - fastboot_fail("sparse header issue\n"); - return; + printf("sparse header issue\n"); + return -EINVAL; }
/* @@ -285,8 +285,7 @@ void write_sparse_image(block_dev_desc_t *dev_desc, if (offset) { printf("%s: Sparse image block size issue [%u]\n", __func__, sparse_header->blk_sz); - fastboot_fail("sparse image block size issue"); - return; + return -EINVAL; }
puts("Flashing Sparse Image\n"); @@ -296,8 +295,8 @@ void write_sparse_image(block_dev_desc_t *dev_desc, 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; + printf("Unknown chunk type"); + return -EINVAL; }
/* @@ -321,8 +320,7 @@ void write_sparse_image(block_dev_desc_t *dev_desc, (info->start + info->size)) { printf("%s: Request would exceed partition size!\n", __func__); - fastboot_fail("Request would exceed partition size!"); - return; + return -EINVAL; }
for (i = 0; i < buffer->repeat; i++) { @@ -337,8 +335,7 @@ void write_sparse_image(block_dev_desc_t *dev_desc, if (buffer_blks != buffer_blk_cnt) { printf("%s: Write %d failed " LBAFU "\n", __func__, i, buffer_blks); - fastboot_fail("flash write failure"); - return; + return -EIO; }
total_blocks += buffer_blk_cnt; @@ -351,9 +348,10 @@ void write_sparse_image(block_dev_desc_t *dev_desc, total_blocks, skipped, sparse_header->total_blks); printf("........ wrote %d blocks to '%s'\n", total_blocks, part_name);
- if (total_blocks != sparse_header->total_blks) - fastboot_fail("sparse image write failure"); + if (total_blocks != sparse_header->total_blks) { + printf("sparse image write failure\n"); + return -EIO; + }
- fastboot_okay(""); - return; + return 0; } diff --git a/common/fb_mmc.c b/common/fb_mmc.c index 0c48cf929f8f..b31a9fb929c5 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -6,6 +6,7 @@
#include <config.h> #include <common.h> +#include <fastboot.h> #include <fb_mmc.h> #include <part.h> #include <aboot.h> @@ -16,23 +17,8 @@ #define CONFIG_FASTBOOT_GPT_NAME GPT_ENTRY_NAME #endif
-/* The 64 defined bytes plus the '\0' */ -#define RESPONSE_LEN (64 + 1) - static char *response_str;
-void fastboot_fail(const char *s) -{ - strncpy(response_str, "FAIL\0", 5); - strncat(response_str, s, RESPONSE_LEN - 4 - 1); -} - -void fastboot_okay(const char *s) -{ - strncpy(response_str, "OKAY\0", 5); - strncat(response_str, s, RESPONSE_LEN - 4 - 1); -} - static int get_partition_info_efi_by_name_or_alias(block_dev_desc_t *dev_desc, const char *name, disk_partition_t *info) { @@ -68,7 +54,7 @@ static void write_raw_image(block_dev_desc_t *dev_desc, disk_partition_t *info,
if (blkcnt > info->size) { error("too large for partition: '%s'\n", part_name); - fastboot_fail("too large for partition"); + fastboot_fail(response_str, "too large for partition"); return; }
@@ -78,13 +64,13 @@ static void write_raw_image(block_dev_desc_t *dev_desc, disk_partition_t *info, buffer); if (blks != blkcnt) { error("failed writing to device %d\n", dev_desc->dev); - fastboot_fail("failed writing to device"); + fastboot_fail(response_str, "failed writing to device"); return; }
printf("........ wrote " LBAFU " bytes to '%s'\n", blkcnt * info->blksz, part_name); - fastboot_okay(""); + fastboot_okay(response_str, ""); }
void fb_mmc_flash_write(const char *cmd, void *download_buffer, @@ -99,7 +85,7 @@ void fb_mmc_flash_write(const char *cmd, void *download_buffer, dev_desc = get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV); if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) { error("invalid mmc device\n"); - fastboot_fail("invalid mmc device"); + fastboot_fail(response_str, "invalid mmc device"); return; }
@@ -109,20 +95,21 @@ void fb_mmc_flash_write(const char *cmd, void *download_buffer, if (is_valid_gpt_buf(dev_desc, download_buffer)) { printf("%s: invalid GPT - refusing to write to flash\n", __func__); - fastboot_fail("invalid GPT partition"); + fastboot_fail(response_str, "invalid GPT partition"); return; } if (write_mbr_and_gpt_partitions(dev_desc, download_buffer)) { printf("%s: writing GPT partitions failed\n", __func__); - fastboot_fail("writing GPT partitions failed"); + fastboot_fail(response_str, + "writing GPT partitions failed"); return; } printf("........ success\n"); - fastboot_okay(""); + fastboot_okay(response_str, ""); return; } else if (get_partition_info_efi_by_name_or_alias(dev_desc, cmd, &info)) { error("cannot find partition: '%s'\n", cmd); - fastboot_fail("cannot find partition"); + fastboot_fail(response_str, "cannot find partition"); return; }
@@ -132,6 +119,8 @@ void fb_mmc_flash_write(const char *cmd, void *download_buffer, else write_raw_image(dev_desc, &info, cmd, download_buffer, download_bytes); + + fastboot_okay(response_str, ""); }
void fb_mmc_erase(const char *cmd, char *response) @@ -144,7 +133,7 @@ void fb_mmc_erase(const char *cmd, char *response)
if (mmc == NULL) { error("invalid mmc device"); - fastboot_fail("invalid mmc device"); + fastboot_fail(response_str, "invalid mmc device"); return; }
@@ -154,14 +143,14 @@ void fb_mmc_erase(const char *cmd, char *response) dev_desc = get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV); if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) { error("invalid mmc device"); - fastboot_fail("invalid mmc device"); + fastboot_fail(response_str, "invalid mmc device"); return; }
ret = get_partition_info_efi_by_name_or_alias(dev_desc, cmd, &info); if (ret) { error("cannot find partition: '%s'", cmd); - fastboot_fail("cannot find partition"); + fastboot_fail(response_str, "cannot find partition"); return; }
@@ -180,11 +169,11 @@ void fb_mmc_erase(const char *cmd, char *response) blks = dev_desc->block_erase(dev_desc->dev, blks_start, blks_size); if (blks != blks_size) { error("failed erasing from device %d", dev_desc->dev); - fastboot_fail("failed erasing from device"); + fastboot_fail(response_str, "failed erasing from device"); return; }
printf("........ erased " LBAFU " bytes from '%s'\n", blks_size * info.blksz, cmd); - fastboot_okay(""); + fastboot_okay(response_str, ""); } diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index ca01a018b5d1..5703decfd360 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -13,6 +13,7 @@ #include <config.h> #include <common.h> #include <errno.h> +#include <fastboot.h> #include <malloc.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -34,9 +35,6 @@ #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) #define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040)
-/* The 64 defined bytes plus \0 */ -#define RESPONSE_LEN (64 + 1) - #define EP_BUFFER_SIZE 4096
struct f_fastboot { @@ -125,6 +123,19 @@ static struct usb_gadget_strings *fastboot_strings[] = { static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); static int strcmp_l1(const char *s1, const char *s2);
+ +void fastboot_fail(char *response, const char *reason) +{ + strncpy(response, "FAIL\0", 5); + strncat(response, reason, FASTBOOT_RESPONSE_LEN - 4 - 1); +} + +void fastboot_okay(char *response, const char *reason) +{ + strncpy(response, "OKAY\0", 5); + strncat(response, reason, FASTBOOT_RESPONSE_LEN - 4 - 1); +} + static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) { int status = req->status; @@ -358,7 +369,7 @@ static int strcmp_l1(const char *s1, const char *s2) static void cb_getvar(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN]; const char *s; size_t chars_left;
@@ -415,7 +426,7 @@ static unsigned int rx_bytes_expected(unsigned int maxpacket) #define BYTES_PER_DOT 0x20000 static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) { - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN]; unsigned int transfer_size = download_size - download_bytes; const unsigned char *buffer = req->buf; unsigned int buffer_size = req->actual; @@ -472,7 +483,7 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) static void cb_download(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN]; unsigned int max;
strsep(&cmd, ":"); @@ -533,7 +544,7 @@ static void cb_continue(struct usb_ep *ep, struct usb_request *req) static void cb_flash(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN];
strsep(&cmd, ":"); if (!cmd) { @@ -577,7 +588,7 @@ static void cb_oem(struct usb_ep *ep, struct usb_request *req) static void cb_erase(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; - char response[RESPONSE_LEN]; + char response[FASTBOOT_RESPONSE_LEN];
strsep(&cmd, ":"); if (!cmd) { diff --git a/include/aboot.h b/include/aboot.h index 30e4d36df8ba..18ff07a5dc23 100644 --- a/include/aboot.h +++ b/include/aboot.h @@ -9,9 +9,6 @@
#define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
-void fastboot_fail(const char *s); -void fastboot_okay(const char *s); - static inline int is_sparse_image(void *buf) { sparse_header_t *s_header = (sparse_header_t *)buf; @@ -23,6 +20,6 @@ static inline int is_sparse_image(void *buf) return 0; }
-void write_sparse_image(block_dev_desc_t *dev_desc, - disk_partition_t *info, const char *part_name, - void *data, unsigned sz); +int write_sparse_image(block_dev_desc_t *dev_desc, + disk_partition_t *info, const char *part_name, + void *data, unsigned sz); diff --git a/include/fastboot.h b/include/fastboot.h new file mode 100644 index 000000000000..db826d20bfef --- /dev/null +++ b/include/fastboot.h @@ -0,0 +1,22 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring robh@kernel.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _FASTBOOT_H_ +#define _FASTBOOT_H_ + +/* The 64 defined bytes plus \0 */ +#define FASTBOOT_RESPONSE_LEN (64 + 1) + +void fastboot_fail(char *response, const char *reason); +void fastboot_okay(char *response, const char *reason); + +#endif /* _FASTBOOT_H_ */

On Thu, Oct 15, 2015 at 02:34:13PM +0200, Maxime Ripard wrote:
The functions and a few define to generate a fastboot message to be sent back to the host were so far duplicated among the users.
Move them all to a common place.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Reviewed-by: Tom Rini trini@konsulko.com

On Thu, Oct 15, 2015 at 02:34:13PM +0200, Maxime Ripard wrote:
The functions and a few define to generate a fastboot message to be sent back to the host were so far duplicated among the users.
Move them all to a common place.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The current sparse image parser relies heavily on the MMC layer, and doesn't allow any other kind of storage medium to be used.
Rework the parser to support any kind of storage medium, as long as there is an implementation for it.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com --- common/aboot.c | 77 +++++++++++++++++++++++++++++++++++++-------------------- common/fb_mmc.c | 45 ++++++++++++++++++++++++++++++--- include/aboot.h | 16 +++++++++--- 3 files changed, 104 insertions(+), 34 deletions(-)
diff --git a/common/aboot.c b/common/aboot.c index 5f5fb6b62bfa..6c35d8226903 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -58,6 +58,13 @@ static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse, 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 size * sparse->blk_sz / storage->block_sz; +} + static bool sparse_chunk_has_buffer(chunk_header_t *chunk) { switch (chunk->chunk_type) { @@ -257,20 +264,26 @@ static void sparse_put_data_buffer(sparse_buffer_t *buffer) free(buffer); }
-int write_sparse_image(block_dev_desc_t *dev_desc, - disk_partition_t *info, const char *part_name, - void *data, unsigned sz) +int store_sparse_image(sparse_storage_t *storage, + void *storage_priv, void *data) { - lbaint_t start; - lbaint_t blkcnt; 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; uint32_t skipped = 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"); @@ -281,7 +294,7 @@ int write_sparse_image(block_dev_desc_t *dev_desc, * Verify that the sparse block size is a multiple of our * storage backend block size */ - div_u64_rem(sparse_header->blk_sz, info->blksz, &offset); + div_u64_rem(sparse_header->blk_sz, storage->block_sz, &offset); if (offset) { printf("%s: Sparse image block size issue [%u]\n", __func__, sparse_header->blk_sz); @@ -291,8 +304,10 @@ int write_sparse_image(block_dev_desc_t *dev_desc, puts("Flashing Sparse Image\n");
/* Start processing chunks */ - start = info->start; + start = storage->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"); @@ -304,20 +319,22 @@ int write_sparse_image(block_dev_desc_t *dev_desc, * and go on parsing the rest of the chunks */ if (chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE) { - skipped += chunk_header->chunk_sz; + skipped += sparse_block_size_to_storage(chunk_header->chunk_sz, + storage, + sparse_header); continue; }
/* Retrieve the buffer we're going to write */ buffer = sparse_get_data_buffer(sparse_header, chunk_header, - info->blksz, &data); + storage->block_sz, &data); if (!buffer) continue;
- blkcnt = (buffer->length / info->blksz) * buffer->repeat; + blkcnt = (buffer->length / storage->block_sz) * buffer->repeat;
if ((start + total_blocks + blkcnt) > - (info->start + info->size)) { + (storage->start + storage->size)) { printf("%s: Request would exceed partition size!\n", __func__); return -EINVAL; @@ -325,30 +342,36 @@ int write_sparse_image(block_dev_desc_t *dev_desc,
for (i = 0; i < buffer->repeat; i++) { unsigned long buffer_blk_cnt; - unsigned long buffer_blks; - - buffer_blk_cnt = buffer->length / info->blksz; - - buffer_blks = dev_desc->block_write(dev_desc->dev, - start + total_blocks, - buffer_blk_cnt, buffer->data); - if (buffer_blks != buffer_blk_cnt) { - printf("%s: Write %d failed " LBAFU "\n", - __func__, i, buffer_blks); - return -EIO; + int ret; + + buffer_blk_cnt = buffer->length / storage->block_sz; + + 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; }
- total_blocks += buffer_blk_cnt; + total_blocks += ret; }
sparse_put_data_buffer(buffer); }
debug("Wrote %d blocks, skipped %d, expected to write %d blocks\n", - total_blocks, skipped, sparse_header->total_blks); - printf("........ wrote %d blocks to '%s'\n", total_blocks, part_name); - - if (total_blocks != sparse_header->total_blks) { + total_blocks, skipped, + sparse_block_size_to_storage(sparse_header->total_blks, + storage, sparse_header)); + printf("........ wrote %d blocks to '%s'\n", total_blocks, + storage->name); + + if ((total_blocks + skipped) != + sparse_block_size_to_storage(sparse_header->total_blks, + storage, sparse_header)) { printf("sparse image write failure\n"); return -EIO; } diff --git a/common/fb_mmc.c b/common/fb_mmc.c index b31a9fb929c5..c9f2ed6b5863 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -6,6 +6,7 @@
#include <config.h> #include <common.h> +#include <errno.h> #include <fastboot.h> #include <fb_mmc.h> #include <part.h> @@ -19,6 +20,10 @@
static char *response_str;
+struct fb_mmc_sparse { + block_dev_desc_t *dev_desc; +}; + static int get_partition_info_efi_by_name_or_alias(block_dev_desc_t *dev_desc, const char *name, disk_partition_t *info) { @@ -41,6 +46,24 @@ static int get_partition_info_efi_by_name_or_alias(block_dev_desc_t *dev_desc, return ret; }
+ +static int fb_mmc_sparse_write(struct sparse_storage *storage, + void *priv, + unsigned int offset, + unsigned int size, + char *data) +{ + struct fb_mmc_sparse *sparse = priv; + block_dev_desc_t *dev_desc = sparse->dev_desc; + int ret; + + ret = dev_desc->block_write(dev_desc->dev, offset, size, data); + if (!ret) + return -EIO; + + return ret; +} + static void write_raw_image(block_dev_desc_t *dev_desc, disk_partition_t *info, const char *part_name, void *buffer, unsigned int download_bytes) @@ -113,12 +136,26 @@ void fb_mmc_flash_write(const char *cmd, void *download_buffer, return; }
- if (is_sparse_image(download_buffer)) - write_sparse_image(dev_desc, &info, cmd, download_buffer, - download_bytes); - else + if (is_sparse_image(download_buffer)) { + struct fb_mmc_sparse sparse_priv; + sparse_storage_t sparse; + + sparse_priv.dev_desc = dev_desc; + + sparse.block_sz = info.blksz; + sparse.start = info.start; + sparse.size = info.size; + sparse.name = cmd; + sparse.write = fb_mmc_sparse_write; + + printf("Flashing sparse image at offset " LBAFU "\n", + info.start); + + store_sparse_image(&sparse, &sparse_priv, download_buffer); + } else { write_raw_image(dev_desc, &info, cmd, download_buffer, download_bytes); + }
fastboot_okay(response_str, ""); } diff --git a/include/aboot.h b/include/aboot.h index 18ff07a5dc23..a2b0694190d9 100644 --- a/include/aboot.h +++ b/include/aboot.h @@ -9,6 +9,17 @@
#define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
+typedef struct sparse_storage { + unsigned int block_sz; + unsigned int start; + unsigned int size; + const char *name; + + int (*write)(struct sparse_storage *storage, void *priv, + unsigned int offset, unsigned int size, + char *data); +} sparse_storage_t; + static inline int is_sparse_image(void *buf) { sparse_header_t *s_header = (sparse_header_t *)buf; @@ -20,6 +31,5 @@ static inline int is_sparse_image(void *buf) return 0; }
-int write_sparse_image(block_dev_desc_t *dev_desc, - disk_partition_t *info, const char *part_name, - void *data, unsigned sz); +int store_sparse_image(sparse_storage_t *storage, void *storage_priv, + void *data);

On Thu, Oct 15, 2015 at 02:34:14PM +0200, Maxime Ripard wrote:
The current sparse image parser relies heavily on the MMC layer, and doesn't allow any other kind of storage medium to be used.
Rework the parser to support any kind of storage medium, as long as there is an implementation for it.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The fastboot flash command that writes an image to a partition works in several steps:
1 - Retrieve the maximum size the device can download through the "max-download-size" variable
2 - Retrieve the partition type through the "partition-type:%s" variable, that indicates whether or not the partition needs to be erased (even though the fastboot client has minimal support for that)
3a - If the image is smaller than what the device can handle, send the image and flash it.
3b - If the image is larger than what the device can handle, create a sparse image, and split it in several chunks that would fit. Send the chunk, flash it, repeat until we have no more data to send.
However, in the 3b case, the subsequent transfers have no particular identifiers, the protocol just assumes that you would resume the writes where you left it.
While doing so works well, it also means that flashing two subsequent images on the same partition (for example because the user made a mistake) would not work withouth flashing another partition or rebooting the board, which is not really intuitive.
Since we have always the same pattern, we can however maintain a counter that will be reset every time the client will retrieve max-download-size, and incremented after each buffer will be flashed, that will allow us to tell whether we should simply resume the flashing where we were, or start back at the beginning of the partition.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- common/aboot.c | 4 ++-- common/fb_mmc.c | 8 +++++--- drivers/usb/gadget/f_fastboot.c | 14 +++++++++++++- include/aboot.h | 2 +- include/fb_mmc.h | 5 +++-- 5 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/common/aboot.c b/common/aboot.c index 6c35d8226903..7f412ac0ad4f 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -264,8 +264,8 @@ static void sparse_put_data_buffer(sparse_buffer_t *buffer) free(buffer); }
-int store_sparse_image(sparse_storage_t *storage, - void *storage_priv, void *data) +int store_sparse_image(sparse_storage_t *storage, void *storage_priv, + unsigned int session_id, void *data) { unsigned int chunk, offset; sparse_header_t *sparse_header; diff --git a/common/fb_mmc.c b/common/fb_mmc.c index c9f2ed6b5863..c6989668ae5b 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -96,8 +96,9 @@ static void write_raw_image(block_dev_desc_t *dev_desc, disk_partition_t *info, fastboot_okay(response_str, ""); }
-void fb_mmc_flash_write(const char *cmd, void *download_buffer, - unsigned int download_bytes, char *response) +void fb_mmc_flash_write(const char *cmd, unsigned int session_id, + void *download_buffer, unsigned int download_bytes, + char *response) { block_dev_desc_t *dev_desc; disk_partition_t info; @@ -151,7 +152,8 @@ void fb_mmc_flash_write(const char *cmd, void *download_buffer, printf("Flashing sparse image at offset " LBAFU "\n", info.start);
- store_sparse_image(&sparse, &sparse_priv, download_buffer); + store_sparse_image(&sparse, &sparse_priv, session_id, + download_buffer); } else { write_raw_image(dev_desc, &info, cmd, download_buffer, download_bytes); diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 5703decfd360..9af0372007bb 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -51,6 +51,7 @@ static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) }
static struct f_fastboot *fastboot_func; +static unsigned int fastboot_flash_session_id; static unsigned int download_size; static unsigned int download_bytes; static bool is_high_speed; @@ -393,6 +394,15 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
sprintf(str_num, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE); strncat(response, str_num, chars_left); + + /* + * This also indicates the start of a new flashing + * "session", in which we could have 1-N buffers to + * write to a partition. + * + * Reset our session counter. + */ + fastboot_flash_session_id = 0; } else if (!strcmp_l1("serialno", cmd)) { s = getenv("serial#"); if (s) @@ -555,9 +565,11 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req)
strcpy(response, "FAILno flash device defined"); #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV - fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR, + fb_mmc_flash_write(cmd, fastboot_flash_session_id, + (void *)CONFIG_FASTBOOT_BUF_ADDR, download_bytes, response); #endif + fastboot_flash_session_id++; fastboot_tx_write_str(response); } #endif diff --git a/include/aboot.h b/include/aboot.h index a2b0694190d9..0382f5bd2639 100644 --- a/include/aboot.h +++ b/include/aboot.h @@ -32,4 +32,4 @@ static inline int is_sparse_image(void *buf) }
int store_sparse_image(sparse_storage_t *storage, void *storage_priv, - void *data); + unsigned int session_id, void *data); diff --git a/include/fb_mmc.h b/include/fb_mmc.h index 402ba9b1b470..978a1395a185 100644 --- a/include/fb_mmc.h +++ b/include/fb_mmc.h @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-2.0+ */
-void fb_mmc_flash_write(const char *cmd, void *download_buffer, - unsigned int download_bytes, char *response); +void fb_mmc_flash_write(const char *cmd, unsigned int session_id, + void *download_buffer, unsigned int download_bytes, + char *response); void fb_mmc_erase(const char *cmd, char *response);

On Thu, Oct 15, 2015 at 02:34:15PM +0200, Maxime Ripard wrote:
The fastboot flash command that writes an image to a partition works in several steps:
1 - Retrieve the maximum size the device can download through the "max-download-size" variable
2 - Retrieve the partition type through the "partition-type:%s" variable, that indicates whether or not the partition needs to be erased (even though the fastboot client has minimal support for that)
3a - If the image is smaller than what the device can handle, send the image and flash it.
3b - If the image is larger than what the device can handle, create a sparse image, and split it in several chunks that would fit. Send the chunk, flash it, repeat until we have no more data to send.
However, in the 3b case, the subsequent transfers have no particular identifiers, the protocol just assumes that you would resume the writes where you left it.
While doing so works well, it also means that flashing two subsequent images on the same partition (for example because the user made a mistake) would not work withouth flashing another partition or rebooting the board, which is not really intuitive.
Since we have always the same pattern, we can however maintain a counter that will be reset every time the client will retrieve max-download-size, and incremented after each buffer will be flashed, that will allow us to tell whether we should simply resume the flashing where we were, or start back at the beginning of the partition.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Reviewed-by: Tom Rini trini@konsulko.com

On Thu, Oct 15, 2015 at 02:34:15PM +0200, Maxime Ripard wrote:
The fastboot flash command that writes an image to a partition works in several steps:
1 - Retrieve the maximum size the device can download through the "max-download-size" variable
2 - Retrieve the partition type through the "partition-type:%s" variable, that indicates whether or not the partition needs to be erased (even though the fastboot client has minimal support for that)
3a - If the image is smaller than what the device can handle, send the image and flash it.
3b - If the image is larger than what the device can handle, create a sparse image, and split it in several chunks that would fit. Send the chunk, flash it, repeat until we have no more data to send.
However, in the 3b case, the subsequent transfers have no particular identifiers, the protocol just assumes that you would resume the writes where you left it.
While doing so works well, it also means that flashing two subsequent images on the same partition (for example because the user made a mistake) would not work withouth flashing another partition or rebooting the board, which is not really intuitive.
Since we have always the same pattern, we can however maintain a counter that will be reset every time the client will retrieve max-download-size, and incremented after each buffer will be flashed, that will allow us to tell whether we should simply resume the flashing where we were, or start back at the beginning of the partition.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The fastboot client will split the sparse images into several chunks if the image that it tries to flash is bigger than what the device can handle.
In such a case, the bootloader is supposed to retain the last offset to which it wrote to, so that it can resume the writes at the right offset when flashing the next chunk.
Retain the last offset we used, and use the session ID to know if we need it or not.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- common/aboot.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/common/aboot.c b/common/aboot.c index 7f412ac0ad4f..6d76c1e758f9 100644 --- a/common/aboot.c +++ b/common/aboot.c @@ -52,6 +52,8 @@ typedef struct sparse_buffer { u16 type; } sparse_buffer_t;
+static uint32_t last_offset; + static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse, chunk_header_t *chunk) { @@ -301,10 +303,19 @@ int store_sparse_image(sparse_storage_t *storage, void *storage_priv, return -EINVAL; }
- puts("Flashing Sparse Image\n"); + /* + * If it's a new flashing session, start at the beginning of + * the partition. If not, then simply resume where we were. + */ + if (session_id > 0) + start = last_offset; + else + start = storage->start; + + printf("Flashing sparse image on partition %s at offset 0x%x (ID: %d)\n", + storage->name, start * storage->block_sz, session_id);
/* Start processing chunks */ - start = storage->start; for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { uint32_t blkcnt;
@@ -376,5 +387,7 @@ int store_sparse_image(sparse_storage_t *storage, void *storage_priv, return -EIO; }
+ last_offset = start + total_blocks; + return 0; }

On Thu, Oct 15, 2015 at 02:34:16PM +0200, Maxime Ripard wrote:
The fastboot client will split the sparse images into several chunks if the image that it tries to flash is bigger than what the device can handle.
In such a case, the bootloader is supposed to retain the last offset to which it wrote to, so that it can resume the writes at the right offset when flashing the next chunk.
Retain the last offset we used, and use the session ID to know if we need it or not.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Reviewed-by: Tom Rini trini@konsulko.com

On Thu, Oct 15, 2015 at 02:34:16PM +0200, Maxime Ripard wrote:
The fastboot client will split the sparse images into several chunks if the image that it tries to flash is bigger than what the device can handle.
In such a case, the bootloader is supposed to retain the last offset to which it wrote to, so that it can resume the writes at the right offset when flashing the next chunk.
Retain the last offset we used, and use the session ID to know if we need it or not.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

So far the fastboot code was only supporting MMC-backed devices for its flashing operations (flash and erase).
Add a storage backend for NAND-backed devices.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- common/Makefile | 7 +- common/fb_nand.c | 195 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_fastboot.c | 13 ++- include/fb_nand.h | 11 +++ 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 common/fb_nand.c create mode 100644 include/fb_nand.h
diff --git a/common/Makefile b/common/Makefile index 491c56552f40..4f6e6cea596b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -274,10 +274,15 @@ obj-y += memsize.o obj-y += stdio.o
# This option is not just y/n - it can have a numeric value -ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV +ifdef CONFIG_FASTBOOT_FLASH obj-y += aboot.o +ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV obj-y += fb_mmc.o endif +ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV +obj-y += fb_nand.o +endif +endif
obj-$(CONFIG_CMD_BLOB) += cmd_blob.o
diff --git a/common/fb_nand.c b/common/fb_nand.c new file mode 100644 index 000000000000..1c80ba9edabc --- /dev/null +++ b/common/fb_nand.c @@ -0,0 +1,195 @@ +/* + * Copyright 2014 Broadcom Corporation. + * Copyright 2015 Free Electrons. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> + +#include <aboot.h> +#include <fastboot.h> +#include <sparse_format.h> + +#include <linux/mtd/mtd.h> +#include <jffs2/jffs2.h> +#include <nand.h> + +static char *response_str; + +struct fb_nand_sparse { + nand_info_t *nand; + struct part_info *part; +}; + +static int fb_nand_lookup(const char *partname, char *response, + nand_info_t **nand, + struct part_info **part) +{ + struct mtd_device *dev; + int ret; + u8 pnum; + + ret = mtdparts_init(); + if (ret) { + error("Cannot initialize MTD partitions\n"); + fastboot_fail(response_str, "cannot init mtdparts"); + return ret; + } + + ret = find_dev_and_part(partname, &dev, &pnum, part); + if (ret) { + error("cannot find partition: '%s'", partname); + fastboot_fail(response_str, "cannot find partition"); + return ret; + } + + if (dev->id->type != MTD_DEV_TYPE_NAND) { + error("partition '%s' is not stored on a NAND device", + partname); + fastboot_fail(response_str, "not a NAND device"); + return -EINVAL; + } + + *nand = &nand_info[dev->id->num]; + + return 0; +} + +static int _fb_nand_erase(nand_info_t *nand, struct part_info *part) +{ + nand_erase_options_t opts; + int ret; + + memset(&opts, 0, sizeof(opts)); + opts.offset = part->offset; + opts.length = part->size; + opts.quiet = 1; + + printf("Erasing blocks 0x%llx to 0x%llx\n", + part->offset, part->offset + part->size); + + ret = nand_erase_opts(nand, &opts); + if (ret) + return ret; + + printf("........ erased 0x%llx bytes from '%s'\n", + part->size, part->name); + + return 0; +} + +static int _fb_nand_write(nand_info_t *nand, struct part_info *part, + void *buffer, unsigned int offset, + unsigned int length, size_t *written) +{ + int flags = WITH_WR_VERIFY; + +#ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS + flags |= WITH_DROP_FFS; +#endif + + return nand_write_skip_bad(nand, offset, &length, written, + part->size - (offset - part->offset), + buffer, flags); +} + +static int fb_nand_sparse_write(struct sparse_storage *storage, + void *priv, + unsigned int offset, + unsigned int size, + char *data) +{ + struct fb_nand_sparse *sparse = priv; + size_t written; + int ret; + + ret = _fb_nand_write(sparse->nand, sparse->part, data, + offset * storage->block_sz, + size * storage->block_sz, &written); + if (ret < 0) { + printf("Failed to write sparse chunk\n"); + return ret; + } + + return written / storage->block_sz; +} + +void fb_nand_flash_write(const char *partname, unsigned int session_id, + void *download_buffer, unsigned int download_bytes, + char *response) +{ + struct part_info *part; + nand_info_t *nand = NULL; + int ret; + + /* initialize the response buffer */ + response_str = response; + + ret = fb_nand_lookup(partname, response, &nand, &part); + if (ret) { + error("invalid NAND device"); + fastboot_fail(response_str, "invalid NAND device"); + return; + } + + if (is_sparse_image(download_buffer)) { + struct fb_nand_sparse sparse_priv; + sparse_storage_t sparse; + + sparse_priv.nand = nand; + sparse_priv.part = part; + + sparse.block_sz = nand->writesize; + sparse.start = part->offset / sparse.block_sz; + sparse.size = part->size / sparse.block_sz; + sparse.name = part->name; + sparse.write = fb_nand_sparse_write; + + ret = store_sparse_image(&sparse, &sparse_priv, session_id, + download_buffer); + } else { + printf("Flashing raw image at offset 0x%llx\n", + part->offset); + + ret = _fb_nand_write(nand, part, download_buffer, part->offset, + download_bytes, NULL); + + printf("........ wrote %u bytes to '%s'\n", + download_bytes, part->name); + } + + if (ret) { + fastboot_fail(response_str, "error writing the image"); + return; + } + + fastboot_okay(response_str, ""); +} + +void fb_nand_erase(const char *partname, char *response) +{ + struct part_info *part; + nand_info_t *nand = NULL; + int ret; + + /* initialize the response buffer */ + response_str = response; + + ret = fb_nand_lookup(partname, response, &nand, &part); + if (ret) { + error("invalid NAND device"); + fastboot_fail(response_str, "invalid NAND device"); + return; + } + + ret = _fb_nand_erase(nand, part); + if (ret) { + error("failed erasing from device %s", nand->name); + fastboot_fail(response_str, "failed erasing from device"); + return; + } + + fastboot_okay(response_str, ""); +} diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 9af0372007bb..20b6c18b9cfd 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -24,6 +24,9 @@ #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV #include <fb_mmc.h> #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV +#include <fb_nand.h> +#endif
#define FASTBOOT_VERSION "0.4"
@@ -569,6 +572,11 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req) (void *)CONFIG_FASTBOOT_BUF_ADDR, download_bytes, response); #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV + fb_nand_flash_write(cmd, fastboot_flash_session_id, + (void *)CONFIG_FASTBOOT_BUF_ADDR, + download_bytes, response); +#endif fastboot_flash_session_id++; fastboot_tx_write_str(response); } @@ -577,7 +585,7 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req) static void cb_oem(struct usb_ep *ep, struct usb_request *req) { char *cmd = req->buf; -#ifdef CONFIG_FASTBOOT_FLASH +#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV if (strncmp("format", cmd + 4, 6) == 0) { char cmdbuf[32]; sprintf(cmdbuf, "gpt write mmc %x $partitions", @@ -614,6 +622,9 @@ static void cb_erase(struct usb_ep *ep, struct usb_request *req) #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV fb_mmc_erase(cmd, response); #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV + fb_nand_erase(cmd, response); +#endif fastboot_tx_write_str(response); } #endif diff --git a/include/fb_nand.h b/include/fb_nand.h new file mode 100644 index 000000000000..80ddef5656a8 --- /dev/null +++ b/include/fb_nand.h @@ -0,0 +1,11 @@ +/* + * Copyright 2014 Broadcom Corporation. + * Copyright 2015 Free Electrons. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +void fb_nand_flash_write(const char *cmd, unsigned int session_id, + void *download_buffer, unsigned int download_bytes, + char *response); +void fb_nand_erase(const char *cmd, char *response);

On Thu, Oct 15, 2015 at 02:34:17PM +0200, Maxime Ripard wrote:
So far the fastboot code was only supporting MMC-backed devices for its flashing operations (flash and erase).
Add a storage backend for NAND-backed devices.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
This is mutually exclusive with MMC support, right? If so, it's not quite written that way in the #ifdef's so I think it need to be down in say:
@@ -569,6 +572,11 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req) (void *)CONFIG_FASTBOOT_BUF_ADDR, download_bytes, response); #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
- fb_nand_flash_write(cmd, fastboot_flash_session_id,
(void *)CONFIG_FASTBOOT_BUF_ADDR,
download_bytes, response);
+#endif
With #elif/#error "Fastboot enabled but without any backing store support" and we drop the #error once this is Kconfig'zied.

Hi Tom,
On Tue, Oct 20, 2015 at 03:46:36PM -0400, Tom Rini wrote:
On Thu, Oct 15, 2015 at 02:34:17PM +0200, Maxime Ripard wrote:
So far the fastboot code was only supporting MMC-backed devices for its flashing operations (flash and erase).
Add a storage backend for NAND-backed devices.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
This is mutually exclusive with MMC support, right? If so, it's not quite written that way in the #ifdef's so I think it need to be down in say:
@@ -569,6 +572,11 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req) (void *)CONFIG_FASTBOOT_BUF_ADDR, download_bytes, response); #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
- fb_nand_flash_write(cmd, fastboot_flash_session_id,
(void *)CONFIG_FASTBOOT_BUF_ADDR,
download_bytes, response);
+#endif
With #elif/#error "Fastboot enabled but without any backing store support" and we drop the #error once this is Kconfig'zied.
We said in the precedent version that the end goal would be to support both at the same time, if relevant.
I was willing to treat that as a followup patch, but I guess I should do as you suggest for the time being.
Thanks! Maxime

On Wed, Oct 21, 2015 at 04:57:04PM +0200, Maxime Ripard wrote:
Hi Tom,
On Tue, Oct 20, 2015 at 03:46:36PM -0400, Tom Rini wrote:
On Thu, Oct 15, 2015 at 02:34:17PM +0200, Maxime Ripard wrote:
So far the fastboot code was only supporting MMC-backed devices for its flashing operations (flash and erase).
Add a storage backend for NAND-backed devices.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
This is mutually exclusive with MMC support, right? If so, it's not quite written that way in the #ifdef's so I think it need to be down in say:
@@ -569,6 +572,11 @@ static void cb_flash(struct usb_ep *ep, struct usb_request *req) (void *)CONFIG_FASTBOOT_BUF_ADDR, download_bytes, response); #endif +#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
- fb_nand_flash_write(cmd, fastboot_flash_session_id,
(void *)CONFIG_FASTBOOT_BUF_ADDR,
download_bytes, response);
+#endif
With #elif/#error "Fastboot enabled but without any backing store support" and we drop the #error once this is Kconfig'zied.
We said in the precedent version that the end goal would be to support both at the same time, if relevant.
I was willing to treat that as a followup patch, but I guess I should do as you suggest for the time being.
Yeah, we want to make sure that until it's supported it can't be configured to try and do it.

On Thu, Oct 15, 2015 at 02:34:17PM +0200, Maxime Ripard wrote:
So far the fastboot code was only supporting MMC-backed devices for its flashing operations (flash and erase).
Add a storage backend for NAND-backed devices.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Applied to u-boot/master, thanks!

Some devices might need to do some per-partition initialization (ECC/Randomizer settings change for example) before actually accessing it.
Add some hooks before the write and erase operations to let the boards define what they need to do if needed.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com --- common/fb_nand.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/common/fb_nand.c b/common/fb_nand.c index 1c80ba9edabc..57cbde078340 100644 --- a/common/fb_nand.c +++ b/common/fb_nand.c @@ -23,6 +23,16 @@ struct fb_nand_sparse { struct part_info *part; };
+__weak int board_fastboot_erase_partition_setup(char *name) +{ + return 0; +} + +__weak int board_fastboot_write_partition_setup(char *name) +{ + return 0; +} + static int fb_nand_lookup(const char *partname, char *response, nand_info_t **nand, struct part_info **part) @@ -134,6 +144,10 @@ void fb_nand_flash_write(const char *partname, unsigned int session_id, return; }
+ ret = board_fastboot_write_partition_setup(part->name); + if (ret) + return; + if (is_sparse_image(download_buffer)) { struct fb_nand_sparse sparse_priv; sparse_storage_t sparse; @@ -184,6 +198,10 @@ void fb_nand_erase(const char *partname, char *response) return; }
+ ret = board_fastboot_erase_partition_setup(part->name); + if (ret) + return; + ret = _fb_nand_erase(nand, part); if (ret) { error("failed erasing from device %s", nand->name);

On Thu, Oct 15, 2015 at 02:34:18PM +0200, Maxime Ripard wrote:
Some devices might need to do some per-partition initialization (ECC/Randomizer settings change for example) before actually accessing it.
Add some hooks before the write and erase operations to let the boards define what they need to do if needed.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

The Android sparse image format is currently supported through a file called aboot, which isn't really such a great name, since the sparse image format is only used for transferring data with fastboot.
Rename the file and header to a file called "sparse", which also makes it consistent with the header defining the image structures.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- common/Makefile | 2 +- common/fb_mmc.c | 2 +- common/fb_nand.c | 2 +- common/{aboot.c => image-sparse.c} | 2 +- include/{aboot.h => image-sparse.h} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename common/{aboot.c => image-sparse.c} (99%) rename include/{aboot.h => image-sparse.h} (100%)
diff --git a/common/Makefile b/common/Makefile index 4f6e6cea596b..9398942932fd 100644 --- a/common/Makefile +++ b/common/Makefile @@ -275,7 +275,7 @@ obj-y += stdio.o
# This option is not just y/n - it can have a numeric value ifdef CONFIG_FASTBOOT_FLASH -obj-y += aboot.o +obj-y += image-sparse.o ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV obj-y += fb_mmc.o endif diff --git a/common/fb_mmc.c b/common/fb_mmc.c index c6989668ae5b..e4fbd8dfa756 100644 --- a/common/fb_mmc.c +++ b/common/fb_mmc.c @@ -9,8 +9,8 @@ #include <errno.h> #include <fastboot.h> #include <fb_mmc.h> +#include <image-sparse.h> #include <part.h> -#include <aboot.h> #include <sparse_format.h> #include <mmc.h>
diff --git a/common/fb_nand.c b/common/fb_nand.c index 57cbde078340..9ca8602a73ec 100644 --- a/common/fb_nand.c +++ b/common/fb_nand.c @@ -8,8 +8,8 @@ #include <config.h> #include <common.h>
-#include <aboot.h> #include <fastboot.h> +#include <image-sparse.h> #include <sparse_format.h>
#include <linux/mtd/mtd.h> diff --git a/common/aboot.c b/common/image-sparse.c similarity index 99% rename from common/aboot.c rename to common/image-sparse.c index 6d76c1e758f9..dffe844d548a 100644 --- a/common/aboot.c +++ b/common/image-sparse.c @@ -36,9 +36,9 @@
#include <config.h> #include <common.h> -#include <aboot.h> #include <div64.h> #include <errno.h> +#include <image-sparse.h> #include <malloc.h> #include <part.h> #include <sparse_format.h> diff --git a/include/aboot.h b/include/image-sparse.h similarity index 100% rename from include/aboot.h rename to include/image-sparse.h

On Thu, Oct 15, 2015 at 02:34:19PM +0200, Maxime Ripard wrote:
The Android sparse image format is currently supported through a file called aboot, which isn't really such a great name, since the sparse image format is only used for transferring data with fastboot.
Rename the file and header to a file called "sparse", which also makes it consistent with the header defining the image structures.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Reviewed-by: Tom Rini trini@konsulko.com

On Thu, Oct 15, 2015 at 02:34:19PM +0200, Maxime Ripard wrote:
The Android sparse image format is currently supported through a file called aboot, which isn't really such a great name, since the sparse image format is only used for transferring data with fastboot.
Rename the file and header to a file called "sparse", which also makes it consistent with the header defining the image structures.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com Reviewed-by: Tom Rini trini@konsulko.com
Applied to u-boot/master, thanks!

When using the fastboot boot command, the image sent to U-Boot will be an Android boot image. If the support is missing, that won't obviously work, so we need it in our configuration.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- include/configs/sunxi-common.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index e1ba7914cda1..d7fc9ea7d099 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -347,6 +347,7 @@ extern int soft_i2c_gpio_scl; #define CONFIG_CMD_FASTBOOT #define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR #define CONFIG_FASTBOOT_BUF_SIZE 0x2000000 +#define CONFIG_ANDROID_BOOT_IMAGE
#define CONFIG_FASTBOOT_FLASH #define CONFIG_FASTBOOT_FLASH_MMC_DEV 0

On Thu, 2015-10-15 at 14:34 +0200, Maxime Ripard wrote:
When using the fastboot boot command, the image sent to U-Boot will be an Android boot image. If the support is missing, that won't obviously work,
obviously won't ??
so we need it in our configuration.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Acked-by: Ian Campbell ijc@hellion.org.uk
include/configs/sunxi-common.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi -common.h index e1ba7914cda1..d7fc9ea7d099 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -347,6 +347,7 @@ extern int soft_i2c_gpio_scl; #define CONFIG_CMD_FASTBOOT #define CONFIG_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR #define CONFIG_FASTBOOT_BUF_SIZE 0x2000000 +#define CONFIG_ANDROID_BOOT_IMAGE
#define CONFIG_FASTBOOT_FLASH #define CONFIG_FASTBOOT_FLASH_MMC_DEV 0

The A13-Olinuxino has a mini-USB connector that can be used to power up the board and as an OTG connector.
Since we have already some USB host-only ports right beside this one, enable it in gadget mode
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- configs/A13-OLinuXino_defconfig | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/configs/A13-OLinuXino_defconfig b/configs/A13-OLinuXino_defconfig index 1f68d98cbab4..85eb6f06170e 100644 --- a/configs/A13-OLinuXino_defconfig +++ b/configs/A13-OLinuXino_defconfig @@ -4,6 +4,7 @@ CONFIG_MACH_SUN5I=y CONFIG_DRAM_CLK=408 CONFIG_DRAM_EMR1=0 CONFIG_MMC0_CD_PIN="PG0" +CONFIG_USB0_VBUS_DET="PG1" CONFIG_USB1_VBUS_PIN="PG11" CONFIG_AXP_GPIO=y # CONFIG_VIDEO_HDMI is not set @@ -11,6 +12,8 @@ CONFIG_VIDEO_VGA_VIA_LCD=y CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH=y CONFIG_VIDEO_LCD_POWER="AXP0-0" CONFIG_VIDEO_LCD_BL_PWM="PB2" +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB_MUSB_GADGET=y CONFIG_DEFAULT_DEVICE_TREE="sun5i-a13-olinuxino" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL=y

On Thu, Oct 15, 2015 at 02:34:21PM +0200, Maxime Ripard wrote:
The A13-Olinuxino has a mini-USB connector that can be used to power up the board and as an OTG connector.
Since we have already some USB host-only ports right beside this one, enable it in gadget mode
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Applied to u-boot/master, thanks!

The Cubietruck has a mini-USB connector that can be used to power up the board and as an OTG connector.
Since we have already some USB host-only ports right beside this one, enable it in gadget mode
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- configs/Cubietruck_defconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/configs/Cubietruck_defconfig b/configs/Cubietruck_defconfig index e1b76ce78c8f..cf64bac353fe 100644 --- a/configs/Cubietruck_defconfig +++ b/configs/Cubietruck_defconfig @@ -14,3 +14,8 @@ CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_GMAC,RGMII,AHCI,SATAPWR=SUNXI_GPH(1 # CONFIG_CMD_FPGA is not set CONFIG_ETH_DESIGNWARE=y CONFIG_USB_EHCI_HCD=y +CONFIG_USB0_ID_DET="PH19" +CONFIG_USB0_VBUS_DET="PH22" +CONFIG_USB0_VBUS_PIN="PH17" +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB_MUSB_GADGET=y

On Thu, Oct 15, 2015 at 02:34:22PM +0200, Maxime Ripard wrote:
The Cubietruck has a mini-USB connector that can be used to power up the board and as an OTG connector.
Since we have already some USB host-only ports right beside this one, enable it in gadget mode
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
Applied to u-boot/master, thanks!

Hi,
On 15-10-15 14:34, Maxime Ripard wrote:
Hi everyone,
Here is the second attempt at getting fastboot flashing functions working on top of a NAND, for arbitraly large images.
While the NAND support itself was quite easy to do, the support for the Android sparse images was quite difficult to add, and ended up being a quite huge refactoring of the sparse parse already in tree, that was tied to the MMC layer.
This serie has been tested on a CHIP and an A13-Olinuxino, two Allwinner devices, the CHIP for the NAND, and the Cubietruck to test the MMC.
Let me know what you think, Maxime
Thanks, I've applied the 3 sunxi patches (12 - 14) to u-boot-sunxi/next, with the commit msg fixup suggested by Ian.
Regards,
Hans

Hi,
On 17-10-15 15:59, Hans de Goede wrote:
Hi,
On 15-10-15 14:34, Maxime Ripard wrote:
Hi everyone,
Here is the second attempt at getting fastboot flashing functions working on top of a NAND, for arbitraly large images.
While the NAND support itself was quite easy to do, the support for the Android sparse images was quite difficult to add, and ended up being a quite huge refactoring of the sparse parse already in tree, that was tied to the MMC layer.
This serie has been tested on a CHIP and an A13-Olinuxino, two Allwinner devices, the CHIP for the NAND, and the Cubietruck to test the MMC.
Let me know what you think, Maxime
Thanks, I've applied the 3 sunxi patches (12 - 14) to u-boot-sunxi/next, with the commit msg fixup suggested by Ian.
Looks like I've spoken too soon, the patch for enabling otg on the cubietruck causes build errors:
arm-linux-gnu-ld.bfd: error: /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) uses VFP register arguments, u-boot does not arm-linux-gnu-ld.bfd: failed to merge target specific data of file /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) Makefile:1173: recipe for target 'u-boot' failed make: *** [u-boot] Error 1
Usually this means that some code is using 64 bit int division without using the proper macros for this.
For some reason I'm not seeing the same on the A13-OlinuxIno, that patch for that seems to be missing the id pin, which could have something to do with this.
I think this is caused by some 64 bit math in the fastboot code somewhere ...
For now I've dropped these 2 patches from u-boot-sunxi/next
Regards,
Hans

On Sat, Oct 17, 2015 at 04:42:09PM +0200, Hans de Goede wrote:
Hi,
On 17-10-15 15:59, Hans de Goede wrote:
Hi,
On 15-10-15 14:34, Maxime Ripard wrote:
Hi everyone,
Here is the second attempt at getting fastboot flashing functions working on top of a NAND, for arbitraly large images.
While the NAND support itself was quite easy to do, the support for the Android sparse images was quite difficult to add, and ended up being a quite huge refactoring of the sparse parse already in tree, that was tied to the MMC layer.
This serie has been tested on a CHIP and an A13-Olinuxino, two Allwinner devices, the CHIP for the NAND, and the Cubietruck to test the MMC.
Let me know what you think, Maxime
Thanks, I've applied the 3 sunxi patches (12 - 14) to u-boot-sunxi/next, with the commit msg fixup suggested by Ian.
Looks like I've spoken too soon, the patch for enabling otg on the cubietruck causes build errors:
arm-linux-gnu-ld.bfd: error: /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) uses VFP register arguments, u-boot does not arm-linux-gnu-ld.bfd: failed to merge target specific data of file /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) Makefile:1173: recipe for target 'u-boot' failed make: *** [u-boot] Error 1
Usually this means that some code is using 64 bit int division without using the proper macros for this.
For some reason I'm not seeing the same on the A13-OlinuxIno, that patch for that seems to be missing the id pin, which could have something to do with this.
I think this is caused by some 64 bit math in the fastboot code somewhere ...
For now I've dropped these 2 patches from u-boot-sunxi/next
Which 2 patches then? Cubietruck and this one?
Do you have a toolchain tarball for this so that I can test easily?
Thanks! Maxime

On Mon, Oct 19, 2015 at 10:17:46AM +0200, Maxime Ripard wrote:
On Sat, Oct 17, 2015 at 04:42:09PM +0200, Hans de Goede wrote:
Hi,
On 17-10-15 15:59, Hans de Goede wrote:
Hi,
On 15-10-15 14:34, Maxime Ripard wrote:
Hi everyone,
Here is the second attempt at getting fastboot flashing functions working on top of a NAND, for arbitraly large images.
While the NAND support itself was quite easy to do, the support for the Android sparse images was quite difficult to add, and ended up being a quite huge refactoring of the sparse parse already in tree, that was tied to the MMC layer.
This serie has been tested on a CHIP and an A13-Olinuxino, two Allwinner devices, the CHIP for the NAND, and the Cubietruck to test the MMC.
Let me know what you think, Maxime
Thanks, I've applied the 3 sunxi patches (12 - 14) to u-boot-sunxi/next, with the commit msg fixup suggested by Ian.
Looks like I've spoken too soon, the patch for enabling otg on the cubietruck causes build errors:
arm-linux-gnu-ld.bfd: error: /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) uses VFP register arguments, u-boot does not arm-linux-gnu-ld.bfd: failed to merge target specific data of file /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) Makefile:1173: recipe for target 'u-boot' failed make: *** [u-boot] Error 1
Usually this means that some code is using 64 bit int division without using the proper macros for this.
For some reason I'm not seeing the same on the A13-OlinuxIno, that patch for that seems to be missing the id pin, which could have something to do with this.
I think this is caused by some 64 bit math in the fastboot code somewhere ...
For now I've dropped these 2 patches from u-boot-sunxi/next
Which 2 patches then? Cubietruck and this one?
Do you have a toolchain tarball for this so that I can test easily?
The arm-none-eabi toolchain that's apt-get'able should show this. You could even fudge this by (I _think_) forcing U-Boot to build with -march=armv5te rather than armv7. I _might_ even be able to spot it in the patch review :)

On Tue, Oct 20, 2015 at 03:32:27PM -0400, Tom Rini wrote:
On Mon, Oct 19, 2015 at 10:17:46AM +0200, Maxime Ripard wrote:
On Sat, Oct 17, 2015 at 04:42:09PM +0200, Hans de Goede wrote:
Hi,
On 17-10-15 15:59, Hans de Goede wrote:
Hi,
On 15-10-15 14:34, Maxime Ripard wrote:
Hi everyone,
Here is the second attempt at getting fastboot flashing functions working on top of a NAND, for arbitraly large images.
While the NAND support itself was quite easy to do, the support for the Android sparse images was quite difficult to add, and ended up being a quite huge refactoring of the sparse parse already in tree, that was tied to the MMC layer.
This serie has been tested on a CHIP and an A13-Olinuxino, two Allwinner devices, the CHIP for the NAND, and the Cubietruck to test the MMC.
Let me know what you think, Maxime
Thanks, I've applied the 3 sunxi patches (12 - 14) to u-boot-sunxi/next, with the commit msg fixup suggested by Ian.
Looks like I've spoken too soon, the patch for enabling otg on the cubietruck causes build errors:
arm-linux-gnu-ld.bfd: error: /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) uses VFP register arguments, u-boot does not arm-linux-gnu-ld.bfd: failed to merge target specific data of file /usr/lib/gcc/arm-linux-gnueabi/5.2.1/libgcc.a(_udivmoddi4.o) Makefile:1173: recipe for target 'u-boot' failed make: *** [u-boot] Error 1
Usually this means that some code is using 64 bit int division without using the proper macros for this.
For some reason I'm not seeing the same on the A13-OlinuxIno, that patch for that seems to be missing the id pin, which could have something to do with this.
I think this is caused by some 64 bit math in the fastboot code somewhere ...
For now I've dropped these 2 patches from u-boot-sunxi/next
Which 2 patches then? Cubietruck and this one?
Do you have a toolchain tarball for this so that I can test easily?
The arm-none-eabi toolchain that's apt-get'able should show this. You could even fudge this by (I _think_) forcing U-Boot to build with -march=armv5te rather than armv7. I _might_ even be able to spot it in the patch review :)
... I take it back, I couldn't locally reproduce this with the arm-none-eabi toolchain I have around and 12-14 applied in a local branch. Hans, last time I saw this error (last night even) there was a bit more in the log about which file/function was doing the mis-match. Do you have the full error log handy? Thanks!
participants (4)
-
Hans de Goede
-
Ian Campbell
-
Maxime Ripard
-
Tom Rini