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