mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
3.6 KiB
143 lines
3.6 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Wrapper around the kernel's pre-boot decompression library. |
|
* |
|
* Copyright (C) IBM Corporation 2016. |
|
*/ |
|
|
|
#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 |
|
|
|
/* |
|
* 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: output buffer |
|
* @output_size: length of the output 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; |
|
}
|
|
|