blob: eeee4be10f413a5784178796cb1203cf9c286355 [file] [log] [blame]
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <image.h>
#include <getopt.h>
#include <arch/options.h>
#include "kexec.h"
#include <kexec-uImage.h>
#ifdef HAVE_LIBZ
#include <zlib.h>
#endif
/*
* Basic uImage loader. Not rocket science.
*/
/*
* Returns the image type if everything goes well. This would
* allow the user to decide if the image is of their interest.
*
* Returns -1 on a corrupted image
*
* Returns 0 if this is not a uImage
*/
int uImage_probe(const char *buf, off_t len, unsigned int arch)
{
struct image_header header;
#ifdef HAVE_LIBZ
unsigned int crc;
unsigned int hcrc;
#endif
if ((uintmax_t)len < (uintmax_t)sizeof(header))
return -1;
memcpy(&header, buf, sizeof(header));
if (be32_to_cpu(header.ih_magic) != IH_MAGIC)
return 0;
#ifdef HAVE_LIBZ
hcrc = be32_to_cpu(header.ih_hcrc);
header.ih_hcrc = 0;
crc = crc32(0, (void *)&header, sizeof(header));
if (crc != hcrc) {
printf("Header checksum of the uImage does not match\n");
return -1;
}
#endif
switch (header.ih_type) {
case IH_TYPE_KERNEL:
case IH_TYPE_KERNEL_NOLOAD:
break;
case IH_TYPE_RAMDISK:
break;
default:
printf("uImage type %d unsupported\n", header.ih_type);
return -1;
}
if (header.ih_os != IH_OS_LINUX) {
printf("uImage os %d unsupported\n", header.ih_os);
return -1;
}
if (header.ih_arch != arch) {
printf("uImage arch %d unsupported\n", header.ih_arch);
return -1;
}
switch (header.ih_comp) {
case IH_COMP_NONE:
#ifdef HAVE_LIBZ
case IH_COMP_GZIP:
#endif
break;
default:
printf("uImage uses unsupported compression method\n");
return -1;
}
if (be32_to_cpu(header.ih_size) > len - sizeof(header)) {
printf("uImage header claims that image has %d bytes\n",
be32_to_cpu(header.ih_size));
printf("we read only %lld bytes.\n",
(long long)len - sizeof(header));
return -1;
}
#ifdef HAVE_LIBZ
crc = crc32(0, (void *)buf + sizeof(header), be32_to_cpu(header.ih_size));
if (crc != be32_to_cpu(header.ih_dcrc)) {
printf("uImage: The data CRC does not match. Computed: %08x "
"expected %08x\n", crc,
be32_to_cpu(header.ih_dcrc));
return -1;
}
#endif
return (int)header.ih_type;
}
/*
* To conform to the 'probe' routine in file_type struct,
* we return :
* 0 - If the image is valid 'type' image.
*
* Now, we have to pass on the 'errors' in the image. So,
*
* -1 - If the image is corrupted.
* 1 - If the image is not a uImage.
*/
int uImage_probe_kernel(const char *buf, off_t len, unsigned int arch)
{
int type = uImage_probe(buf, len, arch);
if (type < 0)
return -1;
return !(type == IH_TYPE_KERNEL || type == IH_TYPE_KERNEL_NOLOAD);
}
int uImage_probe_ramdisk(const char *buf, off_t len, unsigned int arch)
{
int type = uImage_probe(buf, len, arch);
if (type < 0)
return -1;
return !(type == IH_TYPE_RAMDISK);
}
#ifdef HAVE_LIBZ
/* gzip flag byte */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define COMMENT 0x10 /* bit 4 set: file comment present */
#define RESERVED 0xE0 /* bits 5..7: reserved */
static int uImage_gz_load(const char *buf, off_t len,
struct Image_info *image)
{
int ret;
z_stream strm;
unsigned int skip;
unsigned int flags;
unsigned char *uncomp_buf;
unsigned int mem_alloc;
mem_alloc = 10 * 1024 * 1024;
uncomp_buf = malloc(mem_alloc);
if (!uncomp_buf)
return -1;
memset(&strm, 0, sizeof(strm));
/* Skip magic, method, time, flags, os code ... */
skip = 10;
/* check GZ magic */
if (buf[0] != 0x1f || buf[1] != 0x8b)
return -1;
flags = buf[3];
if (buf[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
puts ("Error: Bad gzipped data\n");
return -1;
}
if (flags & EXTRA_FIELD) {
skip += 2;
skip += buf[10];
skip += buf[11] << 8;
}
if (flags & ORIG_NAME) {
while (buf[skip++])
;
}
if (flags & COMMENT) {
while (buf[skip++])
;
}
if (flags & HEAD_CRC)
skip += 2;
strm.avail_in = len - skip;
strm.next_in = (void *)buf + skip;
/* - activates parsing gz headers */
ret = inflateInit2(&strm, -MAX_WBITS);
if (ret != Z_OK)
return -1;
strm.next_out = uncomp_buf;
strm.avail_out = mem_alloc;
do {
ret = inflate(&strm, Z_FINISH);
if (ret == Z_STREAM_END)
break;
if (ret == Z_OK || ret == Z_BUF_ERROR) {
void *new_buf;
int inc_buf = 5 * 1024 * 1024;
mem_alloc += inc_buf;
new_buf = realloc(uncomp_buf, mem_alloc);
if (!new_buf) {
inflateEnd(&strm);
free(uncomp_buf);
return -1;
}
uncomp_buf = new_buf;
strm.next_out = uncomp_buf + mem_alloc - inc_buf;
strm.avail_out = inc_buf;
} else {
printf("Error during decompression %d\n", ret);
return -1;
}
} while (1);
inflateEnd(&strm);
image->buf = (char *)uncomp_buf;
image->len = mem_alloc - strm.avail_out;
return 0;
}
#else
static int uImage_gz_load(const char *UNUSED(buf), off_t UNUSED(len),
struct Image_info *UNUSED(image))
{
return -1;
}
#endif
int uImage_load(const char *buf, off_t len, struct Image_info *image)
{
const struct image_header *header = (const struct image_header *)buf;
const char *img_buf = buf + sizeof(struct image_header);
off_t img_len = be32_to_cpu(header->ih_size);
/*
* Prevent loading a modified image.
* CRC check is perfomed only when zlib is compiled
* in. This check will help us to detect
* size related vulnerabilities.
*/
if (img_len != (len - sizeof(struct image_header))) {
printf("Image size doesn't match the header\n");
return -1;
}
image->base = cpu_to_be32(header->ih_load);
image->ep = cpu_to_be32(header->ih_ep);
switch (header->ih_comp) {
case IH_COMP_NONE:
image->buf = img_buf;
image->len = img_len;
return 0;
break;
case IH_COMP_GZIP:
/*
* uboot doesn't decompress the RAMDISK images.
* Comply to the uboot behaviour.
*/
if (header->ih_type == IH_TYPE_RAMDISK) {
image->buf = img_buf;
image->len = img_len;
return 0;
} else
return uImage_gz_load(img_buf, img_len, image);
break;
default:
return -1;
}
}