1/* 2 * Wrapper around the kernel's pre-boot decompression library. 3 * 4 * Copyright (C) IBM Corporation 2016. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12#include "elf.h" 13#include "page.h" 14#include "string.h" 15#include "stdio.h" 16#include "ops.h" 17#include "reg.h" 18#include "types.h" 19 20/* 21 * The decompressor_*.c files play #ifdef games so they can be used in both 22 * pre-boot and regular kernel code. We need these definitions to make the 23 * includes work. 24 */ 25 26#define STATIC static 27#define INIT 28#define __always_inline inline 29 30/* 31 * The build process will copy the required zlib source files and headers 32 * out of lib/ and "fix" the includes so they do not pull in other kernel 33 * headers. 34 */ 35 36#ifdef CONFIG_KERNEL_GZIP 37# include "decompress_inflate.c" 38#endif 39 40#ifdef CONFIG_KERNEL_XZ 41# include "xz_config.h" 42# include "../../../lib/decompress_unxz.c" 43#endif 44 45/* globals for tracking the state of the decompression */ 46static unsigned long decompressed_bytes; 47static unsigned long limit; 48static unsigned long skip; 49static char *output_buffer; 50 51/* 52 * flush() is called by __decompress() when the decompressor's scratch buffer is 53 * full. 54 */ 55static long flush(void *v, unsigned long buffer_size) 56{ 57 unsigned long end = decompressed_bytes + buffer_size; 58 unsigned long size = buffer_size; 59 unsigned long offset = 0; 60 char *in = v; 61 char *out; 62 63 /* 64 * if we hit our decompression limit, we need to fake an error to abort 65 * the in-progress decompression. 66 */ 67 if (decompressed_bytes >= limit) 68 return -1; 69 70 /* skip this entire block */ 71 if (end <= skip) { 72 decompressed_bytes += buffer_size; 73 return buffer_size; 74 } 75 76 /* skip some data at the start, but keep the rest of the block */ 77 if (decompressed_bytes < skip && end > skip) { 78 offset = skip - decompressed_bytes; 79 80 in += offset; 81 size -= offset; 82 decompressed_bytes += offset; 83 } 84 85 out = &output_buffer[decompressed_bytes - skip]; 86 size = min(decompressed_bytes + size, limit) - decompressed_bytes; 87 88 memcpy(out, in, size); 89 decompressed_bytes += size; 90 91 return buffer_size; 92} 93 94static void print_err(char *s) 95{ 96 /* suppress the "error" when we terminate the decompressor */ 97 if (decompressed_bytes >= limit) 98 return; 99 100 printf("Decompression error: '%s'\n\r", s); 101} 102 103/** 104 * partial_decompress - decompresses part or all of a compressed buffer 105 * @inbuf: input buffer 106 * @input_size: length of the input buffer 107 * @outbuf: input buffer 108 * @output_size: length of the input buffer 109 * @skip number of output bytes to ignore 110 * 111 * This function takes compressed data from inbuf, decompresses and write it to 112 * outbuf. Once output_size bytes are written to the output buffer, or the 113 * stream is exhausted the function will return the number of bytes that were 114 * decompressed. Otherwise it will return whatever error code the decompressor 115 * reported (NB: This is specific to each decompressor type). 116 * 117 * The skip functionality is mainly there so the program and discover 118 * the size of the compressed image so that it can ask firmware (if present) 119 * for an appropriately sized buffer. 120 */ 121long partial_decompress(void *inbuf, unsigned long input_size, 122 void *outbuf, unsigned long output_size, unsigned long _skip) 123{ 124 int ret; 125 126 /* 127 * The skipped bytes needs to be included in the size of data we want 128 * to decompress. 129 */ 130 output_size += _skip; 131 132 decompressed_bytes = 0; 133 output_buffer = outbuf; 134 limit = output_size; 135 skip = _skip; 136 137 ret = __decompress(inbuf, input_size, NULL, flush, outbuf, 138 output_size, NULL, print_err); 139 140 /* 141 * If decompression was aborted due to an actual error rather than 142 * a fake error that we used to abort, then we should report it. 143 */ 144 if (decompressed_bytes < limit) 145 return ret; 146 147 return decompressed_bytes - skip; 148} 149