blob: a561939b4f046a66e90902fde6977b19442e9f92 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 Stefan Roese <sr@denx.de>
*/
#include <common.h>
#include <image.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
#include <asm/sections.h>
#include <spl.h>
#include <lzma/LzmaTypes.h>
#include <lzma/LzmaDec.h>
#include <lzma/LzmaTools.h>
#define LZMA_LEN (1 << 20)
static void spl_parse_legacy_validate(uintptr_t start, uintptr_t size)
{
uintptr_t spl_start = (uintptr_t)_start;
uintptr_t spl_end = (uintptr_t)&_image_binary_end;
uintptr_t end = start + size;
if ((start >= spl_start && start < spl_end) ||
(end > spl_start && end <= spl_end) ||
(start < spl_start && end >= spl_end) ||
(start > end && end > spl_start))
panic("SPL: Image overlaps SPL\n");
if (size > CONFIG_SYS_BOOTM_LEN)
panic("SPL: Image too large\n");
}
int spl_parse_legacy_header(struct spl_image_info *spl_image,
const struct legacy_img_hdr *header)
{
u32 header_size = sizeof(struct legacy_img_hdr);
/* check uImage header CRC */
if (IS_ENABLED(CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK) &&
!image_check_hcrc(header)) {
puts("SPL: Image header CRC check failed!\n");
return -EINVAL;
}
if (spl_image->flags & SPL_COPY_PAYLOAD_ONLY) {
/*
* On some system (e.g. powerpc), the load-address and
* entry-point is located at address 0. We can't load
* to 0-0x40. So skip header in this case.
*/
spl_image->load_addr = image_get_load(header);
spl_image->entry_point = image_get_ep(header);
spl_image->size = image_get_data_size(header);
} else {
spl_image->entry_point = image_get_ep(header);
/* Load including the header */
spl_image->load_addr = image_get_load(header) -
header_size;
spl_image->size = image_get_data_size(header) +
header_size;
}
#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK
/* store uImage data length and CRC to check later */
spl_image->dcrc_data = image_get_load(header);
spl_image->dcrc_length = image_get_data_size(header);
spl_image->dcrc = image_get_dcrc(header);
#endif
spl_image->os = image_get_os(header);
spl_image->name = image_get_name(header);
debug(SPL_TPL_PROMPT
"payload image: %32s load addr: 0x%lx size: %d\n",
spl_image->name, spl_image->load_addr, spl_image->size);
spl_parse_legacy_validate(spl_image->load_addr, spl_image->size);
spl_parse_legacy_validate(spl_image->entry_point, 0);
return 0;
}
int spl_load_legacy_lzma(struct spl_image_info *spl_image,
struct spl_load_info *load, ulong offset)
{
SizeT lzma_len = LZMA_LEN;
void *src;
ulong dataptr, overhead, size;
int ret;
/* dataptr points to compressed payload */
dataptr = ALIGN_DOWN(sizeof(struct legacy_img_hdr),
spl_get_bl_len(load));
overhead = sizeof(struct legacy_img_hdr) - dataptr;
size = ALIGN(spl_image->size + overhead, spl_get_bl_len(load));
dataptr += offset;
debug("LZMA: Decompressing %08lx to %08lx\n",
dataptr, spl_image->load_addr);
src = malloc(size);
if (!src) {
printf("Unable to allocate %d bytes for LZMA\n",
spl_image->size);
return -ENOMEM;
}
load->read(load, dataptr, size, src);
ret = lzmaBuffToBuffDecompress(map_sysmem(spl_image->load_addr,
spl_image->size), &lzma_len,
src + overhead, spl_image->size);
if (ret) {
printf("LZMA decompression error: %d\n", ret);
return ret;
}
spl_image->size = lzma_len;
return 0;
}
/*
* This function is added explicitly to avoid code size increase, when
* no compression method is enabled. The compiler will optimize the
* following switch/case statement in spl_load_legacy_img() away due to
* Dead Code Elimination.
*/
static inline int spl_image_get_comp(const struct legacy_img_hdr *hdr)
{
if (IS_ENABLED(CONFIG_SPL_LZMA))
return image_get_comp(hdr);
return IH_COMP_NONE;
}
int spl_load_legacy_img(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev,
struct spl_load_info *load, ulong offset,
struct legacy_img_hdr *hdr)
{
ulong dataptr;
int ret;
/*
* If the payload is compressed, the decompressed data should be
* directly write to its load address.
*/
if (spl_image_get_comp(hdr) != IH_COMP_NONE)
spl_image->flags |= SPL_COPY_PAYLOAD_ONLY;
ret = spl_parse_image_header(spl_image, bootdev, hdr);
if (ret)
return ret;
/* Read image */
switch (spl_image_get_comp(hdr)) {
case IH_COMP_NONE:
dataptr = offset;
/*
* Image header will be skipped only if SPL_COPY_PAYLOAD_ONLY
* is set
*/
if (spl_image->flags & SPL_COPY_PAYLOAD_ONLY)
dataptr += sizeof(*hdr);
load->read(load, dataptr, spl_image->size,
map_sysmem(spl_image->load_addr, spl_image->size));
break;
case IH_COMP_LZMA:
return spl_load_legacy_lzma(spl_image, load, offset);
default:
debug("Compression method %s is not supported\n",
genimg_get_comp_short_name(image_get_comp(hdr)));
return -EINVAL;
}
return 0;
}