On 18/04/2014 16:08, C?dric Le Goater wrote: > The zImage* executable files on ppc64 use a special section called > ".kernel:vmlinux.strip" which contains a compressed vmlinux executable > file. > > This patch adds a service to detect and unzip such a section in a > malloc'ed buffer. The buffer is then used in elf_ppc64_load() to > load the new vmlinux. > > Signed-off-by: C?dric Le Goater <clg at fr.ibm.com> > --- > kexec/arch/ppc64/kexec-elf-ppc64.c | 15 ++++ > kexec/arch/ppc64/kexec-ppc64.h | 2 + > kexec/arch/ppc64/kexec-zImage-ppc64.c | 123 +++++++++++++++++++++++++++++++++ > 3 files changed, 140 insertions(+) > > diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c > index ce1036762582..069d8ba6e690 100644 > --- a/kexec/arch/ppc64/kexec-elf-ppc64.c > +++ b/kexec/arch/ppc64/kexec-elf-ppc64.c > @@ -116,6 +116,8 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, > uint64_t toc_addr; > uint32_t my_run_at_load; > unsigned int slave_code[256/sizeof (unsigned int)], master_entry; > + void *vmlinux_addr; > + int vmlinux_size; > > /* See options.h -- add any more there, too. */ > static const struct option options[] = { > @@ -184,6 +186,7 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, > modified_cmdline_len = strlen(modified_cmdline); > } > > +retry: > /* Parse the Elf file */ > result = build_elf_exec_info(buf, len, &ehdr, 0); > if (result < 0) { > @@ -191,6 +194,18 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, > return result; > } > > + /* If this is a zimage boot wrapper, rebuild the elf info on > + * the new vmlinux which has been unzipped and go on with the > + * kernel load. > + */ > + if (!zImage_ppc64_unzip(&ehdr, &vmlinux_addr, &vmlinux_size)) { > + free_elf_info(&ehdr); > + free((void *) buf); > + buf = vmlinux_addr; > + len = vmlinux_size; > + goto retry; > + } > + > /* Load the Elf data. Physical load addresses in elf64 header do not > * show up correctly. Use user supplied address for now to patch the > * elf header > diff --git a/kexec/arch/ppc64/kexec-ppc64.h b/kexec/arch/ppc64/kexec-ppc64.h > index 9a0aecff7b33..e546d4737bfa 100644 > --- a/kexec/arch/ppc64/kexec-ppc64.h > +++ b/kexec/arch/ppc64/kexec-ppc64.h > @@ -37,4 +37,6 @@ typedef struct mem_rgns { > > extern mem_rgns_t usablemem_rgns; > > +int zImage_ppc64_unzip(struct mem_ehdr *ehdr, void **buf, int *len); > + > #endif /* KEXEC_PPC64_H */ > diff --git a/kexec/arch/ppc64/kexec-zImage-ppc64.c b/kexec/arch/ppc64/kexec-zImage-ppc64.c > index d084ee587be5..67d751f19d7d 100644 > --- a/kexec/arch/ppc64/kexec-zImage-ppc64.c > +++ b/kexec/arch/ppc64/kexec-zImage-ppc64.c > @@ -30,6 +30,9 @@ > #include <getopt.h> > #include <linux/elf.h> > #include "../../kexec.h" > +#include "kexec-ppc64.h" > + > +#include <zlib.h> > > #define MAX_HEADERS 32 > > @@ -175,3 +178,123 @@ void zImage_ppc64_usage(void) > { > fprintf(stderr, "zImage support is still broken\n"); > } > + > +#define HEAD_CRC 2 > +#define EXTRA_FIELD 4 > +#define ORIG_NAME 8 > +#define COMMENT 0x10 > +#define RESERVED 0xe0 > + > +static int get_header_len(const char *header) > +{ > + int len = 10; > + int flags = header[3]; > + > + /* check for gzip header */ > + if ((header[0] != 0x1f) || (header[1] != 0x8b) || > + (header[2] != Z_DEFLATED) || (flags & RESERVED) != 0) { > + fprintf(stderr, "bad gzip header\n"); > + return -1; > + } > + > + if ((flags & EXTRA_FIELD) != 0) > + len = 12 + header[10] + (header[11] << 8); > + > + if ((flags & ORIG_NAME) != 0) > + while (header[len++] != 0) > + ; > + if ((flags & COMMENT) != 0) > + while (header[len++] != 0) > + ; > + if ((flags & HEAD_CRC) != 0) > + len += 2; > + > + return len; > +} > + > +static int gunzip(void *src, int srclen, void *dst, int dstlen) > +{ > + z_stream strm; > + int hdrlen; > + int len; > + int ret; > + > + strm.zalloc = Z_NULL; > + strm.zfree = Z_NULL; > + strm.opaque = Z_NULL; > + strm.avail_in = 0; > + strm.next_in = Z_NULL; > + > + hdrlen = get_header_len(src); > + if (hdrlen == -1) > + return -1; > + > + if (hdrlen >= srclen) { > + fprintf(stderr, "gzip header too large : %d\n", hdrlen); > + return -1; > + } > + > + ret = inflateInit2(&strm, -MAX_WBITS); > + if (ret != Z_OK) { > + fprintf(stderr, "inflateInit2 failed : %d\n", ret); > + return -1; > + } > + > + /* skip gzip header */ > + strm.total_in = hdrlen; > + strm.next_in = src + hdrlen; > + strm.avail_in = srclen - hdrlen; > + > + strm.next_out = dst; > + strm.avail_out = dstlen; > + > + ret = inflate(&strm, Z_FULL_FLUSH); > + if (ret != Z_OK && ret != Z_STREAM_END) { > + fprintf(stderr, "inflate failed: %d %s\n", ret, strm.msg); > + return -1; > + } > + > + len = strm.next_out - (unsigned char *) dst; > + > + inflateEnd(&strm); > + > + return len; > +} > + > +int zImage_ppc64_unzip(struct mem_ehdr *ehdr, void **buf, int *len) > +{ > + struct mem_shdr *shdr; > + void *vmlinuz_addr; > + unsigned long vmlinuz_size; > + unsigned int *vmlinux_sizep; > + > + void *vmlinux_addr; > + int vmlinux_size; > + > + shdr = elf_rel_find_section(ehdr, ".kernel:vmlinux.strip"); > + if (!shdr) > + return -1; > + > + vmlinuz_addr = (void *) shdr->sh_data; > + vmlinuz_size = shdr->sh_size; > + > + /* The size of the uncompressed file is stored in the last 4 > + * bytes. The vmlinux size should be less than 4G ... */ > + vmlinux_sizep = (vmlinuz_addr + vmlinuz_size) - 4; > + > + fprintf(stderr, "Found vmlinuz at %p, unzipping %d bytes\n", > + vmlinuz_addr, *vmlinux_sizep); Hi C?dric, I'd rather use dbgprintf instead of directly call fprintf here. Laurent. > + vmlinux_addr = xmalloc(*vmlinux_sizep); > + > + vmlinux_size = gunzip(vmlinuz_addr, vmlinuz_size, > + vmlinux_addr, *vmlinux_sizep); > + if (vmlinux_size != *vmlinux_sizep) { > + fprintf(stderr, "gunzip failed : only got %d of %d bytes.\n", > + vmlinux_size, *vmlinux_sizep); > + return -1; > + } > + > + *buf = vmlinux_addr; > + *len = vmlinux_size; > + return 0; > +} >