xref: /openbmc/linux/arch/powerpc/boot/decompress.c (revision feac8c8b)
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 */
46 static unsigned long decompressed_bytes;
47 static unsigned long limit;
48 static unsigned long skip;
49 static char *output_buffer;
50 
51 /*
52  * flush() is called by __decompress() when the decompressor's scratch buffer is
53  * full.
54  */
55 static 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 
94 static 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  */
121 long 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