$(obj)/treeboot-akebono.o: BOOTCFLAGS += -mcpu=405
 $(obj)/virtex405-head.o: BOOTAFLAGS += -mcpu=405
 
-# the kernel's version of zlib pulls in a lot of other kernel headers
-# which we don't provide inside the wrapper.
+# The pre-boot decompressors pull in a lot of kernel headers and other source
+# files. This creates a bit of a dependency headache since we need to copy
+# these files into the build dir, fix up any includes and ensure that dependent
+# files are copied in the right order.
+
+# these need to be seperate variables because they are copied out of different
+# directories in the kernel tree. Sure you COULd merge them, but it's a
+# cure-is-worse-than-disease situation.
+zlib-decomp-$(CONFIG_KERNEL_GZIP) := decompress_inflate.c
 zlib-$(CONFIG_KERNEL_GZIP) := inffast.c inflate.c inftrees.c
 zlibheader-$(CONFIG_KERNEL_GZIP) := inffast.h inffixed.h inflate.h inftrees.h infutil.h
 zliblinuxheader-$(CONFIG_KERNEL_GZIP) := zlib.h zconf.h zutil.h
 
-$(addprefix $(obj)/,$(zlib-y) cuboot-c2k.o gunzip_util.o main.o): \
+$(addprefix $(obj)/, decompress.o): \
+       $(addprefix $(obj)/,$(zlib-decomp-y))
+
+$(addprefix $(obj)/, $(zlib-decomp-y)): \
+       $(addprefix $(obj)/,$(zliblinuxheader-y)) \
+       $(addprefix $(obj)/,$(zlibheader-y)) \
+       $(addprefix $(obj)/,$(zlib-y))
+
+$(addprefix $(obj)/,$(zlib-y)): \
        $(addprefix $(obj)/,$(zliblinuxheader-y)) \
        $(addprefix $(obj)/,$(zlibheader-y))
 
 $(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o epapr.o opal.o): \
        $(addprefix $(obj)/,$(libfdtheader))
 
-src-wlib-y := string.S crt0.S crtsavres.S stdio.c main.c \
+src-wlib-y := string.S crt0.S crtsavres.S stdio.c decompress.c main.c \
                $(libfdt) libfdt-wrapper.c \
                ns16550.c serial.c simple_alloc.c div64.S util.S \
-               gunzip_util.c elf_util.c $(zlib-y) devtree.c stdlib.c \
+               elf_util.c $(zlib-y) devtree.c stdlib.c \
                oflib.c ofconsole.c cuboot.c mpsc.c cpm-serial.c \
                uartlite.c mpc52xx-psc.c opal.c opal-calls.S
 src-wlib-$(CONFIG_40x) += 4xx.c planetcore.c
 $(addprefix $(obj)/,$(zliblinuxheader-y)): $(obj)/%: $(srctree)/include/linux/%
        $(call cmd,copy_kern_src)
 
+$(addprefix $(obj)/,$(zlib-decomp-y)): $(obj)/%: $(srctree)/lib/%
+       $(call cmd,copy_kern_src)
+
 quiet_cmd_copy_libfdt = COPY    $@
       cmd_copy_libfdt = cp $< $@
 
        $(Q)cp $< $@
 
 clean-files := $(zlib-) $(zlibheader-) $(zliblinuxheader-) \
-               $(libfdt) $(libfdtheader) \
+               $(zlib-decomp-) $(libfdt) $(libfdtheader) \
                empty.c zImage.coff.lds zImage.ps3.lds zImage.lds
 
 quiet_cmd_bootcc = BOOTCC  $@
        zImage.maple simpleImage.* otheros.bld *.dtb
 
 # clean up files cached by wrapper
-clean-kernel := vmlinux.strip vmlinux.bin
-clean-kernel += $(addsuffix .gz,$(clean-kernel))
+clean-kernel-base := vmlinux.strip vmlinux.bin
+clean-kernel := $(addsuffix .gz,$(clean-kernel-base))
 # If not absolute clean-files are relative to $(obj).
 clean-files += $(addprefix $(objtree)/, $(clean-kernel))
 
 
--- /dev/null
+/*
+ * Wrapper around the kernel's pre-boot decompression library.
+ *
+ * Copyright (C) IBM Corporation 2016.
+ *
+ * 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 "elf.h"
+#include "page.h"
+#include "string.h"
+#include "stdio.h"
+#include "ops.h"
+#include "reg.h"
+#include "types.h"
+
+/*
+ * The decompressor_*.c files play #ifdef games so they can be used in both
+ * pre-boot and regular kernel code. We need these definitions to make the
+ * includes work.
+ */
+
+#define STATIC static
+#define INIT
+#define __always_inline inline
+
+/*
+ * The build process will copy the required zlib source files and headers
+ * out of lib/ and "fix" the includes so they do not pull in other kernel
+ * headers.
+ */
+
+#ifdef CONFIG_KERNEL_GZIP
+#      include "decompress_inflate.c"
+#endif
+
+/* globals for tracking the state of the decompression */
+static unsigned long decompressed_bytes;
+static unsigned long limit;
+static unsigned long skip;
+static char *output_buffer;
+
+/*
+ * flush() is called by __decompress() when the decompressor's scratch buffer is
+ * full.
+ */
+static long flush(void *v, unsigned long buffer_size)
+{
+       unsigned long end = decompressed_bytes + buffer_size;
+       unsigned long size = buffer_size;
+       unsigned long offset = 0;
+       char *in = v;
+       char *out;
+
+       /*
+        * if we hit our decompression limit, we need to fake an error to abort
+        * the in-progress decompression.
+        */
+       if (decompressed_bytes >= limit)
+               return -1;
+
+       /* skip this entire block */
+       if (end <= skip) {
+               decompressed_bytes += buffer_size;
+               return buffer_size;
+       }
+
+       /* skip some data at the start, but keep the rest of the block */
+       if (decompressed_bytes < skip && end > skip) {
+               offset = skip - decompressed_bytes;
+
+               in += offset;
+               size -= offset;
+               decompressed_bytes += offset;
+       }
+
+       out = &output_buffer[decompressed_bytes - skip];
+       size = min(decompressed_bytes + size, limit) - decompressed_bytes;
+
+       memcpy(out, in, size);
+       decompressed_bytes += size;
+
+       return buffer_size;
+}
+
+static void print_err(char *s)
+{
+       /* suppress the "error" when we terminate the decompressor */
+       if (decompressed_bytes >= limit)
+               return;
+
+       printf("Decompression error: '%s'\n\r", s);
+}
+
+/**
+ * partial_decompress - decompresses part or all of a compressed buffer
+ * @inbuf:       input buffer
+ * @input_size:  length of the input buffer
+ * @outbuf:      input buffer
+ * @output_size: length of the input buffer
+ * @skip         number of output bytes to ignore
+ *
+ * This function takes compressed data from inbuf, decompresses and write it to
+ * outbuf. Once output_size bytes are written to the output buffer, or the
+ * stream is exhausted the function will return the number of bytes that were
+ * decompressed. Otherwise it will return whatever error code the decompressor
+ * reported (NB: This is specific to each decompressor type).
+ *
+ * The skip functionality is mainly there so the program and discover
+ * the size of the compressed image so that it can ask firmware (if present)
+ * for an appropriately sized buffer.
+ */
+long partial_decompress(void *inbuf, unsigned long input_size,
+       void *outbuf, unsigned long output_size, unsigned long _skip)
+{
+       int ret;
+
+       /*
+        * The skipped bytes needs to be included in the size of data we want
+        * to decompress.
+        */
+       output_size += _skip;
+
+       decompressed_bytes = 0;
+       output_buffer = outbuf;
+       limit = output_size;
+       skip = _skip;
+
+       ret = __decompress(inbuf, input_size, NULL, flush, outbuf,
+               output_size, NULL, print_err);
+
+       /*
+        * If decompression was aborted due to an actual error rather than
+        * a fake error that we used to abort, then we should report it.
+        */
+       if (decompressed_bytes < limit)
+               return ret;
+
+       return decompressed_bytes - skip;
+}
 
 #include "string.h"
 #include "stdio.h"
 #include "ops.h"
-#include "gunzip_util.h"
 #include "reg.h"
 
-static struct gunzip_state gzstate;
-
 struct addr_range {
        void *addr;
        unsigned long size;
 static struct addr_range prep_kernel(void)
 {
        char elfheader[256];
-       void *vmlinuz_addr = _vmlinux_start;
+       unsigned char *vmlinuz_addr = (unsigned char *)_vmlinux_start;
        unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
        void *addr = 0;
        struct elf_info ei;
-       int len;
+       long len;
 
-       /* gunzip the ELF header of the kernel */
-       gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
-       gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
+       partial_decompress(vmlinuz_addr, vmlinuz_size,
+               elfheader, sizeof(elfheader), 0);
 
        if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
                fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
         * the kernel bss must be claimed (it will be zero'd by the
         * kernel itself)
         */
-       printf("Allocating 0x%lx bytes for kernel ...\n\r", ei.memsize);
+       printf("Allocating 0x%lx bytes for kernel...\n\r", ei.memsize);
 
        if (platform_ops.vmlinux_alloc) {
                addr = platform_ops.vmlinux_alloc(ei.memsize);
                                        "device tree\n\r");
        }
 
-       /* Finally, gunzip the kernel */
-       printf("gunzipping (0x%p <- 0x%p:0x%p)...", addr,
+       /* Finally, decompress the kernel */
+       printf("Decompressing (0x%p <- 0x%p:0x%p)...\n\r", addr,
               vmlinuz_addr, vmlinuz_addr+vmlinuz_size);
-       /* discard up to the actual load data */
-       gunzip_discard(&gzstate, ei.elfoffset - sizeof(elfheader));
-       len = gunzip_finish(&gzstate, addr, ei.loadsize);
+
+       len = partial_decompress(vmlinuz_addr, vmlinuz_size,
+               addr, ei.loadsize, ei.elfoffset);
+
+       if (len < 0)
+               fatal("Decompression failed with error code %ld\n\r", len);
+
        if (len != ei.loadsize)
-               fatal("ran out of data!  only got 0x%x of 0x%lx bytes.\n\r",
-                               len, ei.loadsize);
-       printf("done 0x%x bytes\n\r", len);
+                fatal("Decompression error: got 0x%lx bytes, expected 0x%lx.\n\r",
+                        len, ei.loadsize);
+
+       printf("Done! Decompressed 0x%lx bytes\n\r", len);
 
        flush_cache(addr, ei.loadsize);
 
 
        return 31 - bit;
 }
 
+long partial_decompress(void *inbuf, unsigned long input_size, void *outbuf,
+       unsigned long output_size, unsigned long skip);
+
 #endif /* _PPC_BOOT_OPS_H_ */