app: aboot: Add support to decompress compressed 64bit kernel

Add decompression function to decompress compressed 64bit kernel
image.

Change-Id: I6bcf3092eefca168d58f638cc127ddca70331f58
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index e8021d8..bce7ec1 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -57,6 +57,7 @@
 #include <boot_device.h>
 #include <boot_verifier.h>
 #include <image_verify.h>
+#include <decompress.h>
 #if USE_RPMB_FOR_DEVINFO
 #include <rpmb.h>
 #endif
@@ -858,6 +859,14 @@
 	unsigned imagesize_actual;
 	unsigned second_actual = 0;
 
+	unsigned int out_len = 0;
+	unsigned int out_avai_len = 0;
+	unsigned char *out_addr = NULL;
+	uint32_t dtb_offset = 0;
+	unsigned char *kernel_start_addr = NULL;
+	unsigned int kernel_size = 0;
+	int rc;
+
 #if DEVICE_TREE
 	struct dt_table *table;
 	struct dt_entry dt_entry;
@@ -948,9 +957,20 @@
 		return -1;
 	}
 
+	/*
+	 * Update loading flow of bootimage to support compressed/uncompressed
+	 * bootimage on both 64bit and 32bit platform.
+	 * 1. Load bootimage from emmc partition onto DDR.
+	 * 2. Check if bootimage is gzip format. If yes, decompress compressed kernel
+	 * 3. Check kernel header and update kernel load addr for 64bit and 32bit
+	 *    platform accordingly.
+	 * 4. Sanity Check on kernel_addr and ramdisk_addr and copy data.
+	 */
+
 	dprintf(INFO, "Loading boot image (%d): start\n", imagesize_actual);
 	bs_set_timestamp(BS_KERNEL_LOAD_START);
 
+	/* Read image without signature */
 	if (mmc_read(ptn + offset, (void *)image_addr, imagesize_actual))
 	{
 		dprintf(CRITICAL, "ERROR: Cannot read boot image\n");
@@ -991,11 +1011,38 @@
 	}
 
 	/*
+	 * Check if the kernel image is a gzip package. If yes, need to decompress it.
+	 * If not, continue booting.
+	 */
+	if (is_gzip_package((unsigned char *)(image_addr + page_size), hdr->kernel_size))
+	{
+		out_addr = (unsigned char *)(image_addr + imagesize_actual + page_size);
+		out_avai_len = target_get_max_flash_size() - imagesize_actual - page_size;
+		dprintf(INFO, "decompress image start\n");
+		rc = decompress((unsigned char *)(image_addr + page_size),
+				hdr->kernel_size, out_addr, out_avai_len,
+				&dtb_offset, &out_len);
+		if (rc)
+		{
+			dprintf(INFO, "decompress image failed!!!\n");
+			ASSERT(0);
+		}
+
+		dprintf(INFO, "decompressed image finished.\n");
+		kptr = (struct kernel64_hdr *)out_addr;
+		kernel_start_addr = out_addr;
+		kernel_size = out_len;
+	} else {
+		kptr = (struct kernel64_hdr *)(image_addr + page_size);
+		kernel_start_addr = (unsigned char *)(image_addr + page_size);
+		kernel_size = hdr->kernel_size;
+	}
+
+	/*
 	 * Update the kernel/ramdisk/tags address if the boot image header
 	 * has default values, these default values come from mkbootimg when
 	 * the boot image is flashed using fastboot flash:raw
 	 */
-	kptr = (void *)(image_addr + page_size);
 	update_ker_tags_rdisk_addr(hdr, IS_ARM64(kptr));
 
 	/* Get virtual addresses since the hdr saves physical addresses. */
@@ -1003,8 +1050,9 @@
 	hdr->ramdisk_addr = VA((addr_t)(hdr->ramdisk_addr));
 	hdr->tags_addr = VA((addr_t)(hdr->tags_addr));
 
+	kernel_size = ROUND_TO_PAGE(kernel_size,  page_mask);
 	/* Check if the addresses in the header are valid. */
-	if (check_aboot_addr_range_overlap(hdr->kernel_addr, kernel_actual) ||
+	if (check_aboot_addr_range_overlap(hdr->kernel_addr, kernel_size) ||
 		check_aboot_addr_range_overlap(hdr->ramdisk_addr, ramdisk_actual))
 	{
 		dprintf(CRITICAL, "kernel/ramdisk addresses overlap with aboot addresses.\n");
@@ -1019,9 +1067,8 @@
 	}
 #endif
 
-
 	/* Move kernel, ramdisk and device tree to correct address */
-	memmove((void*) hdr->kernel_addr, (char *)(image_addr + page_size), hdr->kernel_size);
+	memmove((void*) hdr->kernel_addr, kernel_start_addr, kernel_size);
 	memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size);
 
 	#if DEVICE_TREE
@@ -1061,8 +1108,8 @@
 		 * Else update with the atags address in the kernel header
 		 */
 		void *dtb;
-		dtb = dev_tree_appended((void*) hdr->kernel_addr,
-					kernel_actual,
+		dtb = dev_tree_appended((void*)(image_addr + page_size),
+					hdr->kernel_size, dtb_offset,
 					(void *)hdr->tags_addr);
 		if (!dtb) {
 			dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");
@@ -1225,8 +1272,8 @@
 		verify_signed_bootimg((uint32_t)image_addr, imagesize_actual);
 
 		/* Move kernel and ramdisk to correct address */
-		memmove((void*) hdr->kernel_addr, (char *)(image_addr + page_size), hdr->kernel_size);
-		memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size);
+		memmove((void*) hdr->kernel_addr, (char*) (image_addr + page_size), hdr->kernel_size);
+		memmove((void*) hdr->ramdisk_addr, (char*) (image_addr + page_size + kernel_actual), hdr->ramdisk_size);
 #if DEVICE_TREE
 		/* Validate and Read device device tree in the "tags_add */
 		if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size))
@@ -1571,7 +1618,6 @@
 	struct boot_img_hdr *hdr = (struct boot_img_hdr *) (boot_image_start);
 
 	if(hdr->dt_size != 0) {
-
 		/* add kernel offset */
 		dt_image_offset += page_size;
 		n = ROUND_TO_PAGE(hdr->kernel_size, page_mask);
@@ -1626,11 +1672,18 @@
 	uint32_t image_actual;
 	uint32_t dt_actual = 0;
 	uint32_t sig_actual = SIGNATURE_SIZE;
-	struct boot_img_hdr *hdr;
-	struct kernel64_hdr *kptr;
+	struct boot_img_hdr *hdr = NULL;
+	struct kernel64_hdr *kptr = NULL;
 	char *ptr = ((char*) data);
 	int ret = 0;
 	uint8_t dtb_copied = 0;
+	unsigned int out_len = 0;
+	unsigned int out_avai_len = 0;
+	unsigned char *out_addr = NULL;
+	uint32_t dtb_offset = 0;
+	unsigned char *kernel_start_addr = NULL;
+	unsigned int kernel_size = 0;
+
 
 #ifdef MDTP_SUPPORT
 	/* Go through Firmware Lock verification before continue with boot process */
@@ -1690,11 +1743,39 @@
 		verify_signed_bootimg((uint32_t)data, (image_actual - sig_actual));
 
 	/*
+	 * Check if the kernel image is a gzip package. If yes, need to decompress it.
+	 * If not, continue booting.
+	 */
+	if (is_gzip_package((unsigned char *)(data + page_size), hdr->kernel_size))
+	{
+		out_addr = (unsigned char *)target_get_scratch_address();
+		out_addr = (unsigned char *)(out_addr + image_actual + page_size);
+		out_avai_len = target_get_max_flash_size() - image_actual - page_size;
+		dprintf(INFO, "decompress image start\n");
+		ret = decompress((unsigned char *)(ptr + page_size),
+				hdr->kernel_size, out_addr, out_avai_len,
+				&dtb_offset, &out_len);
+		if (ret)
+		{
+			dprintf(INFO, "decompress image failed!!!\n");
+			ASSERT(0);
+		}
+
+		dprintf(INFO, "decompressed image finished.\n");
+		kptr = (struct kernel64_hdr *)out_addr;
+		kernel_start_addr = out_addr;
+		kernel_size = out_len;
+	} else {
+		kptr = (struct kernel64_hdr*)((char *)data + page_size);
+		kernel_start_addr = (unsigned char *)((char *)data + page_size);
+		kernel_size = hdr->kernel_size;
+	}
+
+	/*
 	 * Update the kernel/ramdisk/tags address if the boot image header
 	 * has default values, these default values come from mkbootimg when
 	 * the boot image is flashed using fastboot flash:raw
 	 */
-	kptr = (struct kernel64_hdr*)((char*) data + page_size);
 	update_ker_tags_rdisk_addr(hdr, IS_ARM64(kptr));
 
 	/* Get virtual addresses since the hdr saves physical addresses. */
@@ -1702,8 +1783,9 @@
 	hdr->ramdisk_addr = VA(hdr->ramdisk_addr);
 	hdr->tags_addr = VA(hdr->tags_addr);
 
+	kernel_size  = ROUND_TO_PAGE(kernel_size,  page_mask);
 	/* Check if the addresses in the header are valid. */
-	if (check_aboot_addr_range_overlap(hdr->kernel_addr, kernel_actual) ||
+	if (check_aboot_addr_range_overlap(hdr->kernel_addr, kernel_size) ||
 		check_aboot_addr_range_overlap(hdr->ramdisk_addr, ramdisk_actual))
 	{
 		dprintf(CRITICAL, "kernel/ramdisk addresses overlap with aboot addresses.\n");
@@ -1725,9 +1807,15 @@
 
 	/* Load ramdisk & kernel */
 	memmove((void*) hdr->ramdisk_addr, ptr + page_size + kernel_actual, hdr->ramdisk_size);
-	memmove((void*) hdr->kernel_addr, ptr + page_size, hdr->kernel_size);
+	memmove((void*) hdr->kernel_addr, (char*) (kernel_start_addr), kernel_size);
 
 #if DEVICE_TREE
+	if (check_aboot_addr_range_overlap(hdr->tags_addr, kernel_actual))
+	{
+		dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");
+		return;
+	}
+
 	/*
 	 * If dtb is not found look for appended DTB in the kernel.
 	 * If appended dev tree is found, update the atags with
@@ -1736,7 +1824,8 @@
 	 */
 	if (!dtb_copied) {
 		void *dtb;
-		dtb = dev_tree_appended((void *)hdr->kernel_addr, hdr->kernel_size,
+		dtb = dev_tree_appended((void*)(ptr + page_size),
+					hdr->kernel_size, dtb_offset,
 					(void *)hdr->tags_addr);
 		if (!dtb) {
 			fastboot_fail("dtb not found");
@@ -1745,14 +1834,6 @@
 	}
 #endif
 
-#ifndef DEVICE_TREE
-	if (check_aboot_addr_range_overlap(hdr->tags_addr, MAX_TAGS_SIZE))
-	{
-		dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");
-		return;
-	}
-#endif
-
 	fastboot_okay("");
 	fastboot_stop();