| /* | |
| * 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 | |
| #ifdef CONFIG_KERNEL_XZ | |
| # include "xz_config.h" | |
| # include "../../../lib/decompress_unxz.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; | |
| } |