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 <[email protected]>
> ---
>  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;
> +}
> 


_______________________________________________
kexec mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kexec

Reply via email to