149 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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;
 | |
| }
 |