18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Wrapper around the kernel's pre-boot decompression library. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation 2016. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "elf.h" 98c2ecf20Sopenharmony_ci#include "page.h" 108c2ecf20Sopenharmony_ci#include "string.h" 118c2ecf20Sopenharmony_ci#include "stdio.h" 128c2ecf20Sopenharmony_ci#include "ops.h" 138c2ecf20Sopenharmony_ci#include "reg.h" 148c2ecf20Sopenharmony_ci#include "types.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * The decompressor_*.c files play #ifdef games so they can be used in both 188c2ecf20Sopenharmony_ci * pre-boot and regular kernel code. We need these definitions to make the 198c2ecf20Sopenharmony_ci * includes work. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define STATIC static 238c2ecf20Sopenharmony_ci#define INIT 248c2ecf20Sopenharmony_ci#define __always_inline inline 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * The build process will copy the required zlib source files and headers 288c2ecf20Sopenharmony_ci * out of lib/ and "fix" the includes so they do not pull in other kernel 298c2ecf20Sopenharmony_ci * headers. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#ifdef CONFIG_KERNEL_GZIP 338c2ecf20Sopenharmony_ci# include "decompress_inflate.c" 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#ifdef CONFIG_KERNEL_XZ 378c2ecf20Sopenharmony_ci# include "xz_config.h" 388c2ecf20Sopenharmony_ci# include "../../../lib/decompress_unxz.c" 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* globals for tracking the state of the decompression */ 428c2ecf20Sopenharmony_cistatic unsigned long decompressed_bytes; 438c2ecf20Sopenharmony_cistatic unsigned long limit; 448c2ecf20Sopenharmony_cistatic unsigned long skip; 458c2ecf20Sopenharmony_cistatic char *output_buffer; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * flush() is called by __decompress() when the decompressor's scratch buffer is 498c2ecf20Sopenharmony_ci * full. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic long flush(void *v, unsigned long buffer_size) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci unsigned long end = decompressed_bytes + buffer_size; 548c2ecf20Sopenharmony_ci unsigned long size = buffer_size; 558c2ecf20Sopenharmony_ci unsigned long offset = 0; 568c2ecf20Sopenharmony_ci char *in = v; 578c2ecf20Sopenharmony_ci char *out; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * if we hit our decompression limit, we need to fake an error to abort 618c2ecf20Sopenharmony_ci * the in-progress decompression. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci if (decompressed_bytes >= limit) 648c2ecf20Sopenharmony_ci return -1; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* skip this entire block */ 678c2ecf20Sopenharmony_ci if (end <= skip) { 688c2ecf20Sopenharmony_ci decompressed_bytes += buffer_size; 698c2ecf20Sopenharmony_ci return buffer_size; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* skip some data at the start, but keep the rest of the block */ 738c2ecf20Sopenharmony_ci if (decompressed_bytes < skip && end > skip) { 748c2ecf20Sopenharmony_ci offset = skip - decompressed_bytes; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci in += offset; 778c2ecf20Sopenharmony_ci size -= offset; 788c2ecf20Sopenharmony_ci decompressed_bytes += offset; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci out = &output_buffer[decompressed_bytes - skip]; 828c2ecf20Sopenharmony_ci size = min(decompressed_bytes + size, limit) - decompressed_bytes; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci memcpy(out, in, size); 858c2ecf20Sopenharmony_ci decompressed_bytes += size; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return buffer_size; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void print_err(char *s) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci /* suppress the "error" when we terminate the decompressor */ 938c2ecf20Sopenharmony_ci if (decompressed_bytes >= limit) 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci printf("Decompression error: '%s'\n\r", s); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/** 1008c2ecf20Sopenharmony_ci * partial_decompress - decompresses part or all of a compressed buffer 1018c2ecf20Sopenharmony_ci * @inbuf: input buffer 1028c2ecf20Sopenharmony_ci * @input_size: length of the input buffer 1038c2ecf20Sopenharmony_ci * @outbuf: input buffer 1048c2ecf20Sopenharmony_ci * @output_size: length of the input buffer 1058c2ecf20Sopenharmony_ci * @skip number of output bytes to ignore 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * This function takes compressed data from inbuf, decompresses and write it to 1088c2ecf20Sopenharmony_ci * outbuf. Once output_size bytes are written to the output buffer, or the 1098c2ecf20Sopenharmony_ci * stream is exhausted the function will return the number of bytes that were 1108c2ecf20Sopenharmony_ci * decompressed. Otherwise it will return whatever error code the decompressor 1118c2ecf20Sopenharmony_ci * reported (NB: This is specific to each decompressor type). 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * The skip functionality is mainly there so the program and discover 1148c2ecf20Sopenharmony_ci * the size of the compressed image so that it can ask firmware (if present) 1158c2ecf20Sopenharmony_ci * for an appropriately sized buffer. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cilong partial_decompress(void *inbuf, unsigned long input_size, 1188c2ecf20Sopenharmony_ci void *outbuf, unsigned long output_size, unsigned long _skip) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 1238c2ecf20Sopenharmony_ci * The skipped bytes needs to be included in the size of data we want 1248c2ecf20Sopenharmony_ci * to decompress. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci output_size += _skip; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci decompressed_bytes = 0; 1298c2ecf20Sopenharmony_ci output_buffer = outbuf; 1308c2ecf20Sopenharmony_ci limit = output_size; 1318c2ecf20Sopenharmony_ci skip = _skip; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = __decompress(inbuf, input_size, NULL, flush, outbuf, 1348c2ecf20Sopenharmony_ci output_size, NULL, print_err); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * If decompression was aborted due to an actual error rather than 1388c2ecf20Sopenharmony_ci * a fake error that we used to abort, then we should report it. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci if (decompressed_bytes < limit) 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return decompressed_bytes - skip; 1448c2ecf20Sopenharmony_ci} 145