summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fainelli <florian@openwrt.org>2009-11-19 00:17:22 +0100
committerSimon Horman <horms@verge.net.au>2009-11-30 16:06:14 +1100
commit4c2d9b93150d36d15bf75b050ed97cabd9a94774 (patch)
tree32d6b5e8180ebc0b88c3df1bdce8cf825e97e3bc
parentd61381a70a57a01b87afee90c976675f047d447d (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.ac10
-rw-r--r--kexec/Makefile2
-rw-r--r--kexec/kexec-lzma.h29
-rw-r--r--kexec/kexec-zlib.h10
-rw-r--r--kexec/kexec.c68
-rw-r--r--kexec/lzma.c187
-rw-r--r--kexec/zlib.c78
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 */