aboot: Add support for "compressed ext4" sparse images

Compressed ext4 file system sparse image format is defined by
AOSP and generation of these images is done using ext4_utils
which is also part of Android Open Source Project.

In order to transfer and flash large, empty ext4 filesystems
(userdata) in a reasonable amount of time, we need to store
the image in a sparse format that contains information about
which areas of the filesystem can be left unwritten.

Compressed ext4 file format:

All fields are unsigned little-endian File contains a file header,
followed by a series of chunks File header, chunk header, and chunk
data are all multiples of 4 bytes long

Header:
32 bit magic: 0xed26ff3a
16 bit major version (0x1) - reject images with higher major versions
16 bit minor version (0x0) - allow images with higher minor versions
16 bit file header size in bytes (28 in v1.0)
16 bit chunk header size in bytes (12 in v1.0)
32 bit block size in bytes, must be multiple of 4
32 bit total blocks in output file
32 bit total chunks in input file

Chunk:
16 bit chunk type:
  0xCAC1 raw
  0xCAC2 fill
  0xCAC3 don't care
  0xCAC4 crc
16 bits reserved (write as 0, ignore on read)
32 bit chunk size in blocks in output image
32 bit total size in bytes of chunk input file including chunk header and data
Data:
  for raw, raw data, size in blocks * block size in bytes
  for fill, 4 bytes of fill data

Change-Id: I5ee5c288dae9a8ae364502ae8f32fcab65d2274b
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index e574fb4..392bdae 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -47,6 +47,7 @@
 #include "recovery.h"
 #include "bootimg.h"
 #include "fastboot.h"
+#include "sparse_format.h"
 
 #define EXPAND(NAME) #NAME
 #define TARGET(NAME) EXPAND(NAME)
@@ -556,7 +557,7 @@
 }
 
 
-void cmd_flash_mmc(const char *arg, void *data, unsigned sz)
+void cmd_flash_mmc_img(const char *arg, void *data, unsigned sz)
 {
 	unsigned long long ptn = 0;
 	unsigned long long size = 0;
@@ -588,6 +589,122 @@
 	return;
 }
 
+void cmd_flash_mmc_sparse_img(const char *arg, void *data, unsigned sz)
+{
+	unsigned int chunk;
+	unsigned int chunk_data_sz;
+	sparse_header_t *sparse_header;
+	chunk_header_t *chunk_header;
+	uint32_t crc32 = 0;
+	uint32_t total_blocks = 0;
+	unsigned long long ptn = 0;
+	unsigned long long size = 0;
+
+	ptn = mmc_ptn_offset(arg);
+	if(ptn == 0) {
+		fastboot_fail("partition table doesn't exist");
+		return;
+	}
+
+	/* 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));
+	}
+
+	dprintf (INFO, "=== Sparse Image Header ===\n");
+	dprintf (INFO, "magic: 0x%x\n", sparse_header->magic);
+	dprintf (INFO, "major_version: 0x%x\n", sparse_header->major_version);
+	dprintf (INFO, "minor_version: 0x%x\n", sparse_header->minor_version);
+	dprintf (INFO, "file_hdr_sz: %d\n", sparse_header->file_hdr_sz);
+	dprintf (INFO, "chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz);
+	dprintf (INFO, "blk_sz: %d\n", sparse_header->blk_sz);
+	dprintf (INFO, "total_blks: %d\n", sparse_header->total_blks);
+	dprintf (INFO, "total_chunks: %d\n", sparse_header->total_chunks);
+
+	/* Start processing chunks */
+	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);
+
+		dprintf (SPEW, "=== Chunk Header ===\n");
+		dprintf (SPEW, "chunk_type: 0x%x\n", chunk_header->chunk_type);
+		dprintf (SPEW, "chunk_data_sz: 0x%x\n", chunk_header->chunk_sz);
+		dprintf (SPEW, "total_size: 0x%x\n", chunk_header->total_sz);
+
+		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));
+		}
+
+		chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz;
+		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(mmc_write(ptn + (total_blocks*sparse_header->blk_sz),
+								chunk_data_sz,
+								(unsigned int*)data))
+			{
+				fastboot_fail("flash write failure");
+				return;
+			}
+			total_blocks += chunk_header->chunk_sz;
+			data += chunk_data_sz;
+			break;
+
+			case CHUNK_TYPE_DONT_CARE:
+			case CHUNK_TYPE_CRC:
+			if(chunk_header->total_sz != sparse_header->chunk_hdr_sz)
+			{
+				fastboot_fail("Bogus chunk size for chunk type Dont Care");
+				return;
+			}
+			total_blocks += chunk_header->chunk_sz;
+			data += chunk_data_sz;
+			break;
+
+			fastboot_fail("Unknown chunk type");
+			return;
+		}
+	}
+
+    dprintf(INFO, "Wrote %d blocks, expected to write %d blocks\n",
+             total_blocks, sparse_header->total_blks);
+
+	fastboot_okay("");
+	return;
+}
+
+void cmd_flash_mmc(const char *arg, void *data, unsigned sz)
+{
+	sparse_header_t *sparse_header;
+	sparse_header = (sparse_header_t *) data;
+
+	if (sparse_header->magic != SPARSE_HEADER_MAGIC)
+		cmd_flash_mmc_img(arg, data, sz);
+	else
+		cmd_flash_mmc_sparse_img(arg, data, sz);
+
+	return;
+}
+
 void cmd_flash(const char *arg, void *data, unsigned sz)
 {
 	struct ptentry *ptn;
diff --git a/app/aboot/sparse_format.h b/app/aboot/sparse_format.h
new file mode 100644
index 0000000..9d995bf
--- /dev/null
+++ b/app/aboot/sparse_format.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+typedef struct sparse_header {
+  uint32_t  magic;		/* 0xed26ff3a */
+  uint16_t	major_version;	/* (0x1) - reject images with higher major versions */
+  uint16_t	minor_version;	/* (0x0) - allow images with higer minor versions */
+  uint16_t	file_hdr_sz;	/* 28 bytes for first revision of the file format */
+  uint16_t	chunk_hdr_sz;	/* 12 bytes for first revision of the file format */
+  uint32_t	blk_sz;		/* block size in bytes, must be a multiple of 4 (4096) */
+  uint32_t	total_blks;	/* total blocks in the non-sparse output image */
+  uint32_t	total_chunks;	/* total chunks in the sparse input image */
+  uint32_t	image_checksum; /* CRC32 checksum of the original data, counting "don't care" */
+				/* as 0. Standard 802.3 polynomial, use a Public Domain */
+				/* table implementation */
+} sparse_header_t;
+
+#define SPARSE_HEADER_MAGIC	0xed26ff3a
+
+#define CHUNK_TYPE_RAW		0xCAC1
+#define CHUNK_TYPE_FILL		0xCAC2
+#define CHUNK_TYPE_DONT_CARE	0xCAC3
+#define CHUNK_TYPE_CRC		0xCAC4
+
+typedef struct chunk_header {
+  uint16_t	chunk_type;	/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
+  uint16_t	reserved1;
+  uint32_t	chunk_sz;	/* in blocks in output image */
+  uint32_t	total_sz;	/* in bytes of chunk input file including chunk header and data */
+} chunk_header_t;
+
+/* Following a Raw or Fill chunk is data.  For a Raw chunk, it's the data in chunk_sz * blk_sz.
+ *  For a Fill chunk, it's 4 bytes of the fill data.
+ */
+