boot_android: add runtime compatible for un/compressed kernel image

This patch add auto parse the compress format of kernel image.
Then decompress the compressed kernel image for arm64, as for
arm32 zImage, we only need to load it to a higher memory, then
the kernel will handle the decompress itself.

Test on RK3308 AARCH64 mode (Cortex A35 816 MHZ) boot with eMMC:

------------------------------------------------------------------
Format    |  Size(Byte) | Ratio | Decomp time(ms) | Boot time(ms) |
-------------------------------------------------------------------
Image     | 7720968     |       |                 |     488       |
-------------------------------------------------------------------
Image.lz4 | 4119448     | 53%   |       59        |     455       |
-------------------------------------------------------------------
Image.lzo | 3858322     | 49%   |       141       |     536       |
-------------------------------------------------------------------
Image.gz  | 3529108     | 45%   |       222       |     609       |
-------------------------------------------------------------------
Image.bz2 | 3295914     | 42%   |       2940      |               |
-------------------------------------------------------------------
Image.lzma| 2683750     | 34%   |                 |               |
-------------------------------------------------------------------

Note: the boot time is counted from first ddr init log to first Kernel log.

Change-Id: I73b12ec944fbc8238b0e061a37e2f31aa3093231
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
This commit is contained in:
Andy Yan 2018-06-12 14:21:17 +08:00 committed by Jianhong Chen
parent d5d77aebb0
commit 008aee876a
7 changed files with 123 additions and 16 deletions

View File

@ -42,10 +42,12 @@ static int do_boot_android(cmd_tbl_t *cmdtp, int flag, int argc,
load_address = CONFIG_SYS_LOAD_ADDR;
}
#if defined(CONFIG_ARM64)
/* ARM64 kernel load addr need to align to 0x80000, and android boot.img
* have a 2KB header, need to reserve space for it.
*/
load_address &= ~0x7ffff;
#endif
load_address -= 0x800; /* default page size for boot header */
dev_desc = blk_get_dev(argv[1], simple_strtoul(argv[2], NULL, 16));
if (!dev_desc) {

View File

@ -569,11 +569,11 @@ int android_bootloader_boot_flow(struct blk_desc *dev_desc,
debug("ANDROID: Loading kernel from \"%s\", partition %d.\n",
boot_part_info.name, part_num);
ret = android_image_load(dev_desc, &boot_part_info, load_address,
load_address = android_image_load(dev_desc, &boot_part_info, load_address,
-1UL);
if (ret < 0) {
if (load_address < 0) {
printf("%s %s part load fail\n", __func__, boot_part_info.name);
return ret;
return load_address;
}
#endif

View File

@ -162,7 +162,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
#ifdef CONFIG_ANDROID_BOOT_IMAGE
case IMAGE_FORMAT_ANDROID:
images.os.type = IH_TYPE_KERNEL;
images.os.comp = IH_COMP_NONE;
images.os.comp = android_image_get_comp(os_hdr);
images.os.os = IH_OS_LINUX;
images.os.end = android_image_get_end(os_hdr);
@ -344,6 +344,33 @@ static int handle_decomp_error(int comp_type, size_t uncomp_size,
return BOOTM_ERR_RESET;
}
int bootm_parse_comp(const unsigned char *hdr)
{
#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
ulong start, end;
if (!bootz_setup((ulong)hdr, &start, &end))
return IH_COMP_ZIMAGE;
#endif
#if defined(CONFIG_LZ4)
if (lz4_is_valid_header(hdr))
return IH_COMP_LZ4;
#endif
#if defined(CONFIG_LZO)
if (lzop_is_valid_header(hdr))
return IH_COMP_LZO;
#endif
#if defined(CONFIG_GZIP)
if (gzip_parse_header(hdr, 0xffff) > 0)
return IH_COMP_GZIP;
#endif
#if defined(CONFIG_BZIP2)
if ((hdr[0] == 'B') && (hdr[1] == 'Z') && (hdr[2] == 'h'))
return IH_COMP_BZIP2;
#endif
return IH_COMP_NONE;
}
int bootm_decomp_image(int comp, ulong load, ulong image_start, int type,
void *load_buf, void *image_buf, ulong image_len,
uint unc_len, ulong *load_end)

View File

@ -18,6 +18,7 @@
#define ANDROID_ARG_FDT_FILENAME "rk-kernel.dtb"
static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1];
static u32 android_kernel_comp_type = IH_COMP_NONE;
static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr)
{
@ -38,6 +39,23 @@ static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr)
return hdr->kernel_addr;
}
void android_image_set_comp(struct andr_img_hdr *hdr, u32 comp)
{
android_kernel_comp_type = comp;
}
u32 android_image_get_comp(const struct andr_img_hdr *hdr)
{
return android_kernel_comp_type;
}
int android_image_parse_kernel_comp(const struct andr_img_hdr *hdr)
{
ulong kaddr = android_image_get_kernel_addr(hdr);
return bootm_parse_comp((const unsigned char *)kaddr);
}
/**
* android_image_get_kernel() - processes kernel part of Android boot images
* @hdr: Pointer to image header, which is at the start
@ -127,6 +145,16 @@ ulong android_image_get_end(const struct andr_img_hdr *hdr)
return end;
}
u32 android_image_get_ksize(const struct andr_img_hdr *hdr)
{
return hdr->kernel_size;
}
void android_image_set_kload(struct andr_img_hdr *hdr, u32 load_address)
{
hdr->kernel_addr = load_address;
}
ulong android_image_get_kload(const struct andr_img_hdr *hdr)
{
return android_image_get_kernel_addr(hdr);
@ -179,7 +207,10 @@ long android_image_load(struct blk_desc *dev_desc,
unsigned long load_address,
unsigned long max_size) {
void *buf;
long blk_cnt, blk_read = 0;
long blk_cnt = 0;
long blk_read = 0;
u32 comp;
u32 kload_addr;
if (max_size < part_info->blksz)
return -1;
@ -189,17 +220,33 @@ long android_image_load(struct blk_desc *dev_desc,
*/
buf = map_sysmem(load_address, 0 /* size */);
/* Read the Android header first and then read the rest. */
if (blk_dread(dev_desc, part_info->start, 1, buf) != 1)
/* Read the Android boot.img header and a few parts of
* the head of kernel image.
*/
if (blk_dread(dev_desc, part_info->start, 8, buf) != 8)
blk_read = -1;
if (!blk_read && android_image_check_header(buf) != 0) {
printf("** Invalid Android Image header **\n");
blk_read = -1;
}
comp = android_image_parse_kernel_comp(buf);
if (!blk_read) {
blk_cnt = (android_image_get_end(buf) - (ulong)buf +
part_info->blksz - 1) / part_info->blksz;
/*
* We should load a compressed kernel Image
* to high memory
*/
if (comp != IH_COMP_NONE) {
load_address += android_image_get_ksize(buf) * 3;
load_address = env_get_ulong("kernel_addr_c", 16, load_address);
unmap_sysmem(buf);
buf = map_sysmem(load_address, 0 /* size */);
}
if (blk_cnt * part_info->blksz > max_size) {
debug("Android Image too big (%lu bytes, max %lu)\n",
android_image_get_end(buf) - (ulong)buf,
@ -213,15 +260,26 @@ long android_image_load(struct blk_desc *dev_desc,
}
}
/*
* zImage is not need to decompress
* kernel will handle decompress itself
*/
if (comp != IH_COMP_NONE && comp != IH_COMP_ZIMAGE) {
kload_addr = env_get_ulong("kernel_addr_r", 16, 0x02080000);
android_image_set_kload(buf, kload_addr);
android_image_set_comp(buf, comp);
} else {
android_image_set_comp(buf, IH_COMP_NONE);
}
unmap_sysmem(buf);
if (blk_read < 0)
return blk_read;
debug("%lu blocks read: %s\n",
blk_read, (blk_read == blk_cnt) ? "OK" : "ERROR");
if (blk_read != blk_cnt)
return -1;
return blk_read;
return load_address;
}
#if !defined(CONFIG_SPL_BUILD)

View File

@ -664,6 +664,7 @@ int gzwrite(unsigned char *src, int len,
u64 szexpected);
/* lib/lz4_wrapper.c */
bool lz4_is_valid_header(const unsigned char *h);
int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn);
/* lib/qsort.c */

View File

@ -290,6 +290,7 @@ enum {
IH_COMP_LZMA, /* lzma Compression Used */
IH_COMP_LZO, /* lzo Compression Used */
IH_COMP_LZ4, /* lz4 Compression Used */
IH_COMP_ZIMAGE, /* zImage Decompressed itself */
IH_COMP_COUNT,
};
@ -1263,23 +1264,25 @@ int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
ulong *rd_data, ulong *rd_len);
int android_image_get_fdt(const struct andr_img_hdr *hdr,
ulong *rd_data);
u32 android_image_get_comp(const struct andr_img_hdr *hdr);
ulong android_image_get_end(const struct andr_img_hdr *hdr);
ulong android_image_get_kload(const struct andr_img_hdr *hdr);
void android_print_contents(const struct andr_img_hdr *hdr);
/** android_image_load - Load an Android Image from storage.
*
* Load an Android Image based on the header size in the storage. Return the
* number of bytes read from storage, which could be bigger than the actual
* Android Image as described in the header size. In case of error reading the
* image or if the image size needed to be read from disk is bigger than the
* the passed |max_size| a negative number is returned.
* Load an Android Image based on the header size in the storage.
* Return the final load address, which could be a different address
* of argument load_address, if the Kernel Image is compressed. In case
* of error reading the image or if the image size needed to be read
* from disk is bigger than the passed |max_size| a negative number
* is returned.
*
* @dev_desc: The device where to read the image from
* @part_info: The partition in |dev_desc| where to read the image from
* @load_address: The address where the image will be loaded
* @max_size: The maximum loaded size, in bytes
* @return the number of bytes read or a negative number in case of error.
* @return the final load address or a negative number in case of error.
*/
long android_image_load(struct blk_desc *dev_desc,
const disk_partition_t *part_info,
@ -1288,6 +1291,8 @@ long android_image_load(struct blk_desc *dev_desc,
#endif /* CONFIG_ANDROID_BOOT_IMAGE */
int bootm_parse_comp(const unsigned char *hdr);
/**
* board_fit_config_name_match() - Check for a matching board name
*

View File

@ -63,6 +63,20 @@ struct lz4_block_header {
/* + u32 block_checksum iff has_block_checksum is set */
} __packed;
bool lz4_is_valid_header(const unsigned char *h)
{
const struct lz4_frame_header *hdr = (const struct lz4_frame_header *)h;
/* We assume there's always only a single, standard frame. */
if (le32_to_cpu(hdr->magic) != LZ4F_MAGIC || hdr->version != 1)
return false; /* unknown format */
if (hdr->reserved0 || hdr->reserved1 || hdr->reserved2)
return false; /* reserved must be zero */
if (!hdr->independent_blocks)
return false; /* we can't support this yet */
return true;
}
int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn)
{
const void *end = dst + *dstn;