root/drivers/firmware/efi/libstub/zboot-decompress-gzip.c
// SPDX-License-Identifier: GPL-2.0

#include <linux/efi.h>
#include <linux/zlib.h>

#include <asm/efi.h>

#include "efistub.h"

#include "inftrees.c"
#include "inffast.c"
#include "inflate.c"

extern unsigned char _gzdata_start[], _gzdata_end[];
extern u32 __aligned(1) payload_size;

static struct z_stream_s stream;

efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size)
{
        efi_status_t status;
        int rc;

        /* skip the 10 byte header, assume no recorded filename */
        stream.next_in = _gzdata_start + 10;
        stream.avail_in = _gzdata_end - stream.next_in;

        status = efi_allocate_pages(zlib_inflate_workspacesize(),
                                    (unsigned long *)&stream.workspace,
                                    ULONG_MAX);
        if (status != EFI_SUCCESS)
                return status;

        rc = zlib_inflateInit2(&stream, -MAX_WBITS);
        if (rc != Z_OK) {
                efi_err("failed to initialize GZIP decompressor: %d\n", rc);
                status = EFI_LOAD_ERROR;
                goto out;
        }

        *alloc_size = payload_size;
        return EFI_SUCCESS;
out:
        efi_free(zlib_inflate_workspacesize(), (unsigned long)stream.workspace);
        return status;
}

efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen)
{
        int rc;

        stream.next_out = out;
        stream.avail_out = outlen;

        rc = zlib_inflate(&stream, 0);
        zlib_inflateEnd(&stream);

        efi_free(zlib_inflate_workspacesize(), (unsigned long)stream.workspace);

        if (rc != Z_STREAM_END) {
                efi_err("GZIP decompression failed with status %d\n", rc);
                return EFI_LOAD_ERROR;
        }

        efi_cache_sync_image((unsigned long)out, outlen);

        return EFI_SUCCESS;
}