From ad9d2716cfc1cda5a7e0d7bc0db45e3af8a4adbb Mon Sep 17 00:00:00 2001 From: David Gibson Date: Mon, 5 Mar 2007 14:24:52 +1100 Subject: [POWERPC] zImage: Add more flexible gunzip convenience functions At present, arch/powerpc/boot/main.c includes a gunzip() function which is a convenient wrapper around zlib. However, it doesn't conveniently allow decompressing part of an image to one location, then the remainder to a different address. This patch adds a new set of more flexible convenience wrappers around zlib, moving them to their own file, gunzip_util.c, in the process. These wrappers allow decompressing sections of the compressed image to different locations. In addition, they transparently handle uncompressed data, avoiding special case code to handle uncompressed vmlinux images. The patch also converts main.c to use the new wrappers, using the new flexibility to avoid decompressing the vmlinux's ELF header twice as we did previously. That in turn means we avoid extending our allocations for the vmlinux to allow space for the extra copy of the ELF header. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/gunzip_util.c | 140 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 arch/powerpc/boot/gunzip_util.c (limited to 'arch/powerpc/boot/gunzip_util.c') diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c new file mode 100644 index 000000000000..3d9ff8f081c6 --- /dev/null +++ b/arch/powerpc/boot/gunzip_util.c @@ -0,0 +1,140 @@ +/* + * Copyright 2007 David Gibson, IBM Corporation. + * Based on earlier work, Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include "string.h" +#include "stdio.h" +#include "ops.h" +#include "gunzip_util.h" + +struct gunzip_state state; + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +void gunzip_start(struct gunzip_state *state, void *src, int srclen) +{ + char *hdr = src; + int hdrlen = 0; + + memset(state, 0, sizeof(*state)); + + /* Check for gzip magic number */ + if ((hdr[0] == 0x1f) && (hdr[1] == 0x8b)) { + /* gzip data, initialize zlib parameters */ + int r, flags; + + state->s.workspace = state->scratch; + if (zlib_inflate_workspacesize() > sizeof(state->scratch)) { + printf("insufficient scratch space for gunzip\n\r"); + exit(); + } + + /* skip header */ + hdrlen = 10; + flags = hdr[3]; + if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + hdrlen = 12 + hdr[10] + (hdr[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (hdr[hdrlen++] != 0) + ; + if ((flags & COMMENT) != 0) + while (hdr[hdrlen++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + hdrlen += 2; + if (hdrlen >= srclen) { + printf("gunzip_start: ran out of data in header\n\r"); + exit(); + } + + r = zlib_inflateInit2(&state->s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n\r", r); + exit(); + } + } + + state->s.next_in = src + hdrlen; + state->s.avail_in = srclen - hdrlen; +} + +int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) +{ + int len; + + if (state->s.workspace) { + /* gunzipping */ + int r; + + state->s.next_out = dst; + state->s.avail_out = dstlen; + r = zlib_inflate(&state->s, Z_FULL_FLUSH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d msg: %s\n\r", r, state->s.msg); + exit(); + } + len = state->s.next_out - (unsigned char *)dst; + } else { + /* uncompressed image */ + len = min(state->s.avail_in, (unsigned)dstlen); + memcpy(dst, state->s.next_in, len); + state->s.next_in += len; + state->s.avail_in -= len; + } + return len; +} + +void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) +{ + int len; + + len = gunzip_partial(state, dst, dstlen); + if (len < dstlen) { + printf("gunzip_block: ran out of data\n\r"); + exit(); + } +} + +void gunzip_discard(struct gunzip_state *state, int len) +{ + static char discard_buf[128]; + + while (len > sizeof(discard_buf)) { + gunzip_exactly(state, discard_buf, sizeof(discard_buf)); + len -= sizeof(discard_buf); + } + + if (len > 0) + gunzip_exactly(state, discard_buf, len); +} + +int gunzip_finish(struct gunzip_state *state, void *dst, int dstlen) +{ + int len; + + if (state->s.workspace) { + len = gunzip_partial(state, dst, dstlen); + zlib_inflateEnd(&state->s); + } else { + /* uncompressed image */ + len = min(state->s.avail_in, (unsigned)dstlen); + memcpy(dst, state->s.next_in, len); + } + + return len; +} -- cgit v1.2.3 From 7850ad5c39a40ae14ab37e030357e2ae8252af2b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 14 Mar 2007 16:32:17 +1100 Subject: [POWERPC] Add documentation for the zImage's gunzip convenience functions This patch adds documenting comments to the gunzip convenience functions added in commit ad9d2716cfc1cda5a7e0d7bc0db45e3af8a4adbb. It also removes a stray newline, and an unused global variable. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/gunzip_util.c | 81 ++++++++++++++++++++++++++++++++++++++++- arch/powerpc/boot/gunzip_util.h | 17 ++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/boot/gunzip_util.c') diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c index 3d9ff8f081c6..f7c95f24fcdd 100644 --- a/arch/powerpc/boot/gunzip_util.c +++ b/arch/powerpc/boot/gunzip_util.c @@ -14,14 +14,31 @@ #include "ops.h" #include "gunzip_util.h" -struct gunzip_state state; - #define HEAD_CRC 2 #define EXTRA_FIELD 4 #define ORIG_NAME 8 #define COMMENT 0x10 #define RESERVED 0xe0 +/** + * gunzip_start - prepare to decompress gzip data + * @state: decompressor state structure to be initialized + * @src: buffer containing gzip compressed or uncompressed data + * @srclen: size in bytes of the buffer at src + * + * If the buffer at @src contains a gzip header, this function + * initializes zlib to decompress the data, storing the decompression + * state in @state. The other functions in this file can then be used + * to decompress data from the gzipped stream. + * + * If the buffer at @src does not contain a gzip header, it is assumed + * to contain uncompressed data. The buffer information is recorded + * in @state and the other functions in this file will simply copy + * data from the uncompressed data stream at @src. + * + * Any errors, such as bad compressed data, cause an error to be + * printed an the platform's exit() function to be called. + */ void gunzip_start(struct gunzip_state *state, void *src, int srclen) { char *hdr = src; @@ -73,6 +90,22 @@ void gunzip_start(struct gunzip_state *state, void *src, int srclen) state->s.avail_in = srclen - hdrlen; } +/** + * gunzip_partial - extract bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @dst: buffer to store extracted data + * @dstlen: maximum number of bytes to extract + * + * This function extracts at most @dstlen bytes from the data stream + * previously associated with @state by gunzip_start(), decompressing + * if necessary. Exactly @dstlen bytes are extracted unless the data + * stream doesn't contain enough bytes, in which case the entire + * remainder of the stream is decompressed. + * + * Returns the actual number of bytes extracted. If any errors occur, + * such as a corrupted compressed stream, an error is printed an the + * platform's exit() function is called. + */ int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) { int len; @@ -99,6 +132,20 @@ int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) return len; } +/** + * gunzip_exactly - extract a fixed number of bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @dst: buffer to store extracted data + * @dstlen: number of bytes to extract + * + * This function extracts exactly @dstlen bytes from the data stream + * previously associated with @state by gunzip_start(), decompressing + * if necessary. + * + * If there are less @dstlen bytes available in the data stream, or if + * any other errors occur, such as a corrupted compressed stream, an + * error is printed an the platform's exit() function is called. + */ void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) { int len; @@ -110,6 +157,21 @@ void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) } } +/** + * gunzip_discard - discard bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @len: number of bytes to discard + * + * This function extracts, then discards exactly @len bytes from the + * data stream previously associated with @state by gunzip_start(). + * Subsequent gunzip_partial(), gunzip_exactly() or gunzip_finish() + * calls will extract the data following the discarded bytes in the + * data stream. + * + * If there are less @len bytes available in the data stream, or if + * any other errors occur, such as a corrupted compressed stream, an + * error is printed an the platform's exit() function is called. + */ void gunzip_discard(struct gunzip_state *state, int len) { static char discard_buf[128]; @@ -123,6 +185,21 @@ void gunzip_discard(struct gunzip_state *state, int len) gunzip_exactly(state, discard_buf, len); } +/** + * gunzip_finish - extract all remaining bytes from a gzip data stream + * @state: gzip state structure previously initialized by gunzip_start() + * @dst: buffer to store extracted data + * @dstlen: maximum number of bytes to extract + * + * This function extracts all remaining data, or at most @dstlen + * bytes, from the stream previously associated with @state by + * gunzip_start(). zlib is then shut down, so it is an error to use + * any of the functions in this file on @state until it is + * re-initialized with another call to gunzip_start(). + * + * If any errors occur, such as a corrupted compressed stream, an + * error is printed an the platform's exit() function is called. + */ int gunzip_finish(struct gunzip_state *state, void *dst, int dstlen) { int len; diff --git a/arch/powerpc/boot/gunzip_util.h b/arch/powerpc/boot/gunzip_util.h index 950f62fe0a6d..b3dfa6e87b3a 100644 --- a/arch/powerpc/boot/gunzip_util.h +++ b/arch/powerpc/boot/gunzip_util.h @@ -12,6 +12,22 @@ #include "zlib.h" +/* + * These functions are designed to make life easy for decompressing + * kernel images, initrd images or any other gzip compressed image, + * particularly if its useful to decompress part of the image (e.g. to + * examine headers) before decompressing the remainder. + * + * To use: + * - declare a gunzip_state structure + * - use gunzip_start() to initialize the state, associating it + * with a stream of compressed data + * - use gunzip_partial(), gunzip_exactly() and gunzip_discard() + * in any combination to extract pieces of data from the stream + * - Finally use gunzip_finish() to extract the tail of the + * compressed stream and wind up zlib + */ + /* scratch space for gunzip; 46912 is from zlib_inflate_workspacesize() */ #define GUNZIP_SCRATCH_SIZE 46912 @@ -27,4 +43,3 @@ void gunzip_discard(struct gunzip_state *state, int len); int gunzip_finish(struct gunzip_state *state, void *dst, int len); #endif /* _PPC_BOOT_GUNZIP_UTIL_H_ */ - -- cgit v1.2.3 From 6a923216aac01d0f3eeea606377b81541f1a2773 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Wed, 21 Mar 2007 09:02:44 -0600 Subject: [POWERPC] bootwrapper: Add a fatal error helper Add a macro fatal that calls printf then exit. User must include stdio.h. Typically replaces 3 lines with 1, although I added back some whitespace. Signed-off-by: Milton Miller Signed-off-by: Paul Mackerras --- arch/powerpc/boot/gunzip_util.c | 36 ++++++++++++------------------------ arch/powerpc/boot/main.c | 30 +++++++++++------------------- arch/powerpc/boot/of.c | 7 +++---- arch/powerpc/boot/ops.h | 2 ++ 4 files changed, 28 insertions(+), 47 deletions(-) (limited to 'arch/powerpc/boot/gunzip_util.c') diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c index f7c95f24fcdd..8a97adfac659 100644 --- a/arch/powerpc/boot/gunzip_util.c +++ b/arch/powerpc/boot/gunzip_util.c @@ -52,18 +52,14 @@ void gunzip_start(struct gunzip_state *state, void *src, int srclen) int r, flags; state->s.workspace = state->scratch; - if (zlib_inflate_workspacesize() > sizeof(state->scratch)) { - printf("insufficient scratch space for gunzip\n\r"); - exit(); - } + if (zlib_inflate_workspacesize() > sizeof(state->scratch)) + fatal("insufficient scratch space for gunzip\n\r"); /* skip header */ hdrlen = 10; flags = hdr[3]; - if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) { - printf("bad gzipped data\n\r"); - exit(); - } + if (hdr[2] != Z_DEFLATED || (flags & RESERVED) != 0) + fatal("bad gzipped data\n\r"); if ((flags & EXTRA_FIELD) != 0) hdrlen = 12 + hdr[10] + (hdr[11] << 8); if ((flags & ORIG_NAME) != 0) @@ -74,16 +70,12 @@ void gunzip_start(struct gunzip_state *state, void *src, int srclen) ; if ((flags & HEAD_CRC) != 0) hdrlen += 2; - if (hdrlen >= srclen) { - printf("gunzip_start: ran out of data in header\n\r"); - exit(); - } + if (hdrlen >= srclen) + fatal("gunzip_start: ran out of data in header\n\r"); r = zlib_inflateInit2(&state->s, -MAX_WBITS); - if (r != Z_OK) { - printf("inflateInit2 returned %d\n\r", r); - exit(); - } + if (r != Z_OK) + fatal("inflateInit2 returned %d\n\r", r); } state->s.next_in = src + hdrlen; @@ -117,10 +109,8 @@ int gunzip_partial(struct gunzip_state *state, void *dst, int dstlen) state->s.next_out = dst; state->s.avail_out = dstlen; r = zlib_inflate(&state->s, Z_FULL_FLUSH); - if (r != Z_OK && r != Z_STREAM_END) { - printf("inflate returned %d msg: %s\n\r", r, state->s.msg); - exit(); - } + if (r != Z_OK && r != Z_STREAM_END) + fatal("inflate returned %d msg: %s\n\r", r, state->s.msg); len = state->s.next_out - (unsigned char *)dst; } else { /* uncompressed image */ @@ -151,10 +141,8 @@ void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) int len; len = gunzip_partial(state, dst, dstlen); - if (len < dstlen) { - printf("gunzip_block: ran out of data\n\r"); - exit(); - } + if (len < dstlen) + fatal("gunzip_block: ran out of data\n\r"); } /** diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index d872b758ef14..df9e95a84015 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -118,10 +118,9 @@ static struct addr_range prep_kernel(void) gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size); gunzip_exactly(&gzstate, elfheader, sizeof(elfheader)); - if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) { - printf("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); - exit(); - } + if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei)) + fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r"); + if (platform_ops.image_hdr) platform_ops.image_hdr(elfheader); @@ -135,11 +134,9 @@ static struct addr_range prep_kernel(void) if (platform_ops.vmlinux_alloc) { addr = platform_ops.vmlinux_alloc(ei.memsize); } else { - if ((unsigned long)_start < ei.memsize) { - printf("Insufficient memory for kernel at address 0!" + if ((unsigned long)_start < ei.memsize) + fatal("Insufficient memory for kernel at address 0!" " (_start=%lx)\n\r", _start); - exit(); - } } /* Finally, gunzip the kernel */ @@ -189,11 +186,9 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd_size); initrd_addr = (unsigned long)malloc(initrd_size); - if (! initrd_addr) { - printf("Can't allocate memory for initial " + if (! initrd_addr) + fatal("Can't allocate memory for initial " "ramdisk !\n\r"); - exit(); - } printf("Relocating initrd 0x%p <- 0x%p (0x%lx bytes)\n\r", initrd_addr, old_addr, initrd_size); memmove((void *)initrd_addr, old_addr, initrd_size); @@ -203,10 +198,8 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, /* Tell the kernel initrd address via device tree */ devp = finddevice("/chosen"); - if (! devp) { - printf("Device tree has no chosen node!\n\r"); - exit(); - } + if (! devp) + fatal("Device tree has no chosen node!\n\r"); initrd_start = (u32)initrd_addr; initrd_end = (u32)initrd_addr + initrd_size; @@ -303,7 +296,6 @@ void start(void *sp) kentry((unsigned long)initrd.addr, initrd.size, loader_info.promptr); - /* console closed so printf below may not work */ - printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); - exit(); + /* console closed so printf in fatal below may not work */ + fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r"); } diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index c6f0d9701485..2cec5c17fb60 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c @@ -212,10 +212,9 @@ static void *of_vmlinux_alloc(unsigned long size) { void *p = malloc(size); - if (!p) { - printf("Can't allocate memory for kernel image!\n\r"); - exit(); - } + if (!p) + fatal("Can't allocate memory for kernel image!\n\r"); + return p; } diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 93608b772db5..ea5368caca59 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -158,6 +158,8 @@ static inline void exit(void) platform_ops.exit(); for(;;); } +#define fatal(args...) { printf(args); exit(); } + #define BSS_STACK(size) \ static char _bss_stack[size]; \ -- cgit v1.2.3 From 3c5f6162549b9045a2925dff64c140c7f49ea344 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Wed, 11 Apr 2007 18:32:36 +1000 Subject: [POWERPC] boot: More verbose gunzip error message Change the error message in gunzip_exactly to be more verbose. Besides the identifier being unrelated to the current function name, the user had no indication if the corruption was near the beginning or the end. Signed-off-by: Milton Miller Acked-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/gunzip_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/boot/gunzip_util.c') diff --git a/arch/powerpc/boot/gunzip_util.c b/arch/powerpc/boot/gunzip_util.c index 8a97adfac659..df8ab07e9ff4 100644 --- a/arch/powerpc/boot/gunzip_util.c +++ b/arch/powerpc/boot/gunzip_util.c @@ -142,7 +142,8 @@ void gunzip_exactly(struct gunzip_state *state, void *dst, int dstlen) len = gunzip_partial(state, dst, dstlen); if (len < dstlen) - fatal("gunzip_block: ran out of data\n\r"); + fatal("\n\rgunzip_exactly: ran out of data!" + " Wanted %d, got %d.\n\r", dstlen, len); } /** -- cgit v1.2.3