diff options
author | Florian Fainelli <florian@openwrt.org> | 2009-11-19 00:17:22 +0100 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2009-11-30 16:06:14 +1100 |
commit | 4c2d9b93150d36d15bf75b050ed97cabd9a94774 (patch) | |
tree | 32d6b5e8180ebc0b88c3df1bdce8cf825e97e3bc | |
parent | d61381a70a57a01b87afee90c976675f047d447d (diff) |
add support for loading lzma compressed kernels
This patch allows one to load a lzma compressed kernel using kexec -l.
As I wanted the lzma code to be very similar to the existing
zlib slurp_decompress I took lzread and associated routines
from the cpio lzma support. Tested on my x86 laptop using the
following commands:
lzma e bzImage bzImage.lzma
kexec -l bzImage.lzma
Having lzma support is particularly useful on some embedded
systems on which we have the kernel already lzma compressed
and available on a mtd partition.
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | kexec/Makefile | 2 | ||||
-rw-r--r-- | kexec/kexec-lzma.h | 29 | ||||
-rw-r--r-- | kexec/kexec-zlib.h | 10 | ||||
-rw-r--r-- | kexec/kexec.c | 68 | ||||
-rw-r--r-- | kexec/lzma.c | 187 | ||||
-rw-r--r-- | kexec/zlib.c | 78 |
7 files changed, 325 insertions, 59 deletions
diff --git a/configure.ac b/configure.ac index af6bbad..a911f8a 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,9 @@ AC_ARG_WITH([gamecube], AC_ARG_WITH([zlib], AC_HELP_STRING([--without-zlib],[disable zlib support]), [ with_zlib="$withval"], [ with_zlib=yes ] ) +AC_ARG_WITH([lzma], AC_HELP_STRING([--without-lzma],[disable lzma support]), + [ with_lzma="$withval"], [ with_lzma=yes ] ) + AC_ARG_WITH([xen], AC_HELP_STRING([--without-xen], [disable extended xen support]), [ with_xen="$withval"], [ with_xen=yes ] ) @@ -142,6 +145,13 @@ if test "$with_zlib" = yes ; then AC_MSG_NOTICE([zlib support disabled]))) fi +dnl See if I have a usable copy of lzma available +if test "$with_lzma" = yes ; then + AC_CHECK_HEADER(lzma.h, + AC_CHECK_LIB(lzma, lzma_code, , + AC_MSG_NOTICE([lzma support disabled]))) +fi + dnl find Xen control stack libraries if test "$with_xen" = yes ; then AC_CHECK_HEADER(xenctrl.h, diff --git a/kexec/Makefile b/kexec/Makefile index 45787ac..2a985cf 100644 --- a/kexec/Makefile +++ b/kexec/Makefile @@ -22,6 +22,8 @@ KEXEC_SRCS += kexec/firmware_memmap.c KEXEC_SRCS += kexec/crashdump.c KEXEC_SRCS += kexec/crashdump-xen.c KEXEC_SRCS += kexec/phys_arch.c +KEXEC_SRCS += kexec/lzma.c +KEXEC_SRCS += kexec/zlib.c KEXEC_GENERATED_SRCS += $(PURGATORY_HEX_C) diff --git a/kexec/kexec-lzma.h b/kexec/kexec-lzma.h new file mode 100644 index 0000000..c78cdf7 --- /dev/null +++ b/kexec/kexec-lzma.h @@ -0,0 +1,29 @@ +#ifndef __KEXEC_LZMA_H +#define __KEXEC_LZMA_H + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <inttypes.h> +#include <lzma.h> + +#include "config.h" + +#ifdef HAVE_LIBLZMA +#define kBufferSize (1 << 15) + +typedef struct lzfile { + uint8_t buf[kBufferSize]; + lzma_stream strm; + FILE *file; + int encoding; + int eof; +} LZFILE; + +LZFILE *lzopen(const char *path, const char *mode); +int lzclose(LZFILE *lzfile); +ssize_t lzread(LZFILE *lzfile, void *buf, size_t len); +#endif /* HAVE_LIBLZMA */ + +char *lzma_decompress_file(const char *filename, off_t *r_size); +#endif /* __KEXEC_LZMA_H */ diff --git a/kexec/kexec-zlib.h b/kexec/kexec-zlib.h new file mode 100644 index 0000000..43c107b --- /dev/null +++ b/kexec/kexec-zlib.h @@ -0,0 +1,10 @@ +#ifndef __KEXEC_ZLIB_H +#define __KEXEC_ZLIB_H + +#include <stdio.h> +#include <sys/types.h> + +#include "config.h" + +char *zlib_decompress_file(const char *filename, off_t *r_size); +#endif /* __KEXEC_ZLIB_H */ diff --git a/kexec/kexec.c b/kexec/kexec.c index 4480d9a..a1cec86 100644 --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -38,14 +38,13 @@ #include "config.h" -#ifdef HAVE_LIBZ -#include <zlib.h> -#endif #include <sha256.h> #include "kexec.h" #include "kexec-syscall.h" #include "kexec-elf.h" #include "kexec-sha256.h" +#include "kexec-zlib.h" +#include "kexec-lzma.h" #include <arch/options.h> unsigned long long mem_min = 0; @@ -554,67 +553,18 @@ char *slurp_file_len(const char *filename, off_t size) return buf; } -#if HAVE_LIBZ char *slurp_decompress_file(const char *filename, off_t *r_size) { - gzFile fp; - int errnum; - const char *msg; - char *buf; - off_t size, allocated; - ssize_t result; - - if (!filename) { - *r_size = 0; - return 0; - } - fp = gzopen(filename, "rb"); - if (fp == 0) { - msg = gzerror(fp, &errnum); - if (errnum == Z_ERRNO) { - msg = strerror(errno); - } - die("Cannot open `%s': %s\n", filename, msg); - } - size = 0; - allocated = 65536; - buf = xmalloc(allocated); - do { - if (size == allocated) { - allocated <<= 1; - buf = xrealloc(buf, allocated); - } - result = gzread(fp, buf + size, allocated - size); - if (result < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; + char *kernel_buf; - msg = gzerror(fp, &errnum); - if (errnum == Z_ERRNO) { - msg = strerror(errno); - } - die ("read on %s of %ld bytes failed: %s\n", - filename, (allocated - size) + 0UL, msg); - } - size += result; - } while(result > 0); - result = gzclose(fp); - if (result != Z_OK) { - msg = gzerror(fp, &errnum); - if (errnum == Z_ERRNO) { - msg = strerror(errno); - } - die ("Close of %s failed: %s\n", filename, msg); + kernel_buf = zlib_decompress_file(filename, r_size); + if (!kernel_buf) { + kernel_buf = lzma_decompress_file(filename, r_size); + if (!kernel_buf) + return slurp_file(filename, r_size); } - *r_size = size; - return buf; + return kernel_buf; } -#else -char *slurp_decompress_file(const char *filename, off_t *r_size) -{ - return slurp_file(filename, r_size); -} -#endif static void update_purgatory(struct kexec_info *info) { diff --git a/kexec/lzma.c b/kexec/lzma.c new file mode 100644 index 0000000..5329cd8 --- /dev/null +++ b/kexec/lzma.c @@ -0,0 +1,187 @@ +#include "kexec-lzma.h" +#ifdef HAVE_LIBLZMA +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <ctype.h> +#include <lzma.h> + +#include "kexec.h" + +static LZFILE *lzopen_internal(const char *path, const char *mode, int fd) +{ + int level = 5; + int encoding = 0; + FILE *fp; + LZFILE *lzfile; + lzma_ret ret; + lzma_stream lzma_strm_tmp = LZMA_STREAM_INIT; + + for (; *mode; mode++) { + if (*mode == 'w') + encoding = 1; + else if (*mode == 'r') + encoding = 0; + else if (*mode >= '1' && *mode <= '9') + level = *mode - '0'; + } + if (fd != -1) + fp = fdopen(fd, encoding ? "w" : "r"); + else + fp = fopen(path, encoding ? "w" : "r"); + if (!fp) + return NULL; + + lzfile = calloc(1, sizeof(*lzfile)); + + if (!lzfile) { + fclose(fp); + return NULL; + } + + lzfile->file = fp; + lzfile->encoding = encoding; + lzfile->eof = 0; + lzfile->strm = lzma_strm_tmp; + if (encoding) { + lzma_options_lzma opt_lzma; + if (lzma_lzma_preset(&opt_lzma, level - 1)) + return NULL; + ret = lzma_alone_encoder(&lzfile->strm, &opt_lzma); + } else { + ret = lzma_auto_decoder(&lzfile->strm, + UINT64_C(64) * 1024 * 1024, 0); + } + if (ret != LZMA_OK) { + fclose(fp); + free(lzfile); + return NULL; + } + return lzfile; +} + +LZFILE *lzopen(const char *path, const char *mode) +{ + return lzopen_internal(path, mode, -1); +} + +int lzclose(LZFILE *lzfile) +{ + lzma_ret ret; + int n; + + if (!lzfile) + return -1; + + if (lzfile->encoding) { + for (;;) { + lzfile->strm.avail_out = kBufferSize; + lzfile->strm.next_out = lzfile->buf; + ret = lzma_code(&lzfile->strm, LZMA_FINISH); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + return -1; + n = kBufferSize - lzfile->strm.avail_out; + if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n) + return -1; + if (ret == LZMA_STREAM_END) + break; + } + } + lzma_end(&lzfile->strm); + + return fclose(lzfile->file); + free(lzfile); +} + +ssize_t lzread(LZFILE *lzfile, void *buf, size_t len) +{ + lzma_ret ret; + int eof = 0; + + if (!lzfile || lzfile->encoding) + return -1; + + if (lzfile->eof) + return 0; + + lzfile->strm.next_out = buf; + lzfile->strm.avail_out = len; + + for (;;) { + if (!lzfile->strm.avail_in) { + lzfile->strm.next_in = lzfile->buf; + lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file); + if (!lzfile->strm.avail_in) + eof = 1; + } + + ret = lzma_code(&lzfile->strm, LZMA_RUN); + if (ret == LZMA_STREAM_END) { + lzfile->eof = 1; + return len - lzfile->strm.avail_out; + } + + if (ret != LZMA_OK) + return -1; + + if (!lzfile->strm.avail_out) + return len; + + if (eof) + return -1; + } +} + +char *lzma_decompress_file(const char *filename, off_t *r_size) +{ + LZFILE *fp; + char *buf; + off_t size, allocated; + ssize_t result; + + if (!filename) { + *r_size = 0; + return 0; + } + fp = lzopen(filename, "rb"); + if (fp == 0) { + die("Cannot open `%s': %s\n", filename); + } + size = 0; + allocated = 65536; + buf = xmalloc(allocated); + do { + if (size == allocated) { + allocated <<= 1; + buf = xrealloc(buf, allocated); + } + result = lzread(fp, buf + size, allocated - size); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + + die ("read on %s of %ld bytes failed\n", + filename, (allocated - size) + 0UL); + } + size += result; + } while(result > 0); + result = lzclose(fp); + if (result != LZMA_OK) { + die ("Close of %s failed\n", filename); + } + *r_size = size; + return buf; +} +#else +char *lzma_decompress_file(const char *filename, off_t *r_size) +{ + return NULL; +} +#endif /* HAVE_LIBLZMA */ diff --git a/kexec/zlib.c b/kexec/zlib.c new file mode 100644 index 0000000..3968494 --- /dev/null +++ b/kexec/zlib.c @@ -0,0 +1,78 @@ +#include "kexec-zlib.h" +#ifdef HAVE_LIBZ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <ctype.h> +#include <zlib.h> + +#include "kexec.h" + +char *zlib_decompress_file(const char *filename, off_t *r_size) +{ + gzFile fp; + int errnum; + const char *msg; + char *buf; + off_t size, allocated; + ssize_t result; + + if (!filename) { + *r_size = 0; + return 0; + } + fp = gzopen(filename, "rb"); + if (fp == 0) { + msg = gzerror(fp, &errnum); + if (errnum == Z_ERRNO) { + msg = strerror(errno); + } + fprintf(stderr, "Cannot open `%s': %s\n", filename, msg); + return NULL; + } + size = 0; + allocated = 65536; + buf = xmalloc(allocated); + do { + if (size == allocated) { + allocated <<= 1; + buf = xrealloc(buf, allocated); + } + result = gzread(fp, buf + size, allocated - size); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + + msg = gzerror(fp, &errnum); + if (errnum == Z_ERRNO) { + msg = strerror(errno); + } + die ("read on %s of %ld bytes failed: %s\n", + filename, (allocated - size) + 0UL, msg); + } + size += result; + } while(result > 0); + result = gzclose(fp); + if (result != Z_OK) { + msg = gzerror(fp, &errnum); + if (errnum == Z_ERRNO) { + msg = strerror(errno); + } + die ("Close of %s failed: %s\n", filename, msg); + } + *r_size = size; + return buf; +} +#else +char *zlib_decompress_file(const char *filename, off_t *r_size) +{ + return NULL; +} +#endif /* HAVE_ZLIB */ |