1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * XIP kernel .data segment decompressor |
4 | * |
5 | * Created by: Nicolas Pitre, August 2017 |
6 | * Copyright: (C) 2017 Linaro Limited |
7 | */ |
8 | |
9 | #include <linux/init.h> |
10 | #include <linux/zutil.h> |
11 | #include "head.h" |
12 | |
13 | /* for struct inflate_state */ |
14 | #include "../../../lib/zlib_inflate/inftrees.h" |
15 | #include "../../../lib/zlib_inflate/inflate.h" |
16 | #include "../../../lib/zlib_inflate/infutil.h" |
17 | |
18 | /* |
19 | * This code is called very early during the boot process to decompress |
20 | * the .data segment stored compressed in ROM. Therefore none of the global |
21 | * variables are valid yet, hence no kernel services such as memory |
22 | * allocation is available. Everything must be allocated on the stack and |
23 | * we must avoid any global data access. We use a temporary stack located |
24 | * in the .bss area. The linker script makes sure the .bss is big enough |
25 | * to hold our stack frame plus some room for called functions. |
26 | * |
27 | * We mimic the code in lib/decompress_inflate.c to use the smallest work |
28 | * area possible. And because everything is statically allocated on the |
29 | * stack then there is no need to clean up before returning. |
30 | */ |
31 | |
32 | int __init __inflate_kernel_data(void) |
33 | { |
34 | struct z_stream_s stream, *strm = &stream; |
35 | struct inflate_state state; |
36 | char *in = __data_loc; |
37 | int rc; |
38 | |
39 | /* Check and skip gzip header (assume no filename) */ |
40 | if (in[0] != 0x1f || in[1] != 0x8b || in[2] != 0x08 || in[3] & ~3) |
41 | return -1; |
42 | in += 10; |
43 | |
44 | strm->workspace = &state; |
45 | strm->next_in = in; |
46 | strm->avail_in = _edata_loc - __data_loc; /* upper bound */ |
47 | strm->next_out = _sdata; |
48 | strm->avail_out = _edata_loc - __data_loc; |
49 | zlib_inflateInit2(strm, windowBits: -MAX_WBITS); |
50 | WS(strm)->inflate_state.wsize = 0; |
51 | WS(strm)->inflate_state.window = NULL; |
52 | rc = zlib_inflate(strm, Z_FINISH); |
53 | if (rc == Z_OK || rc == Z_STREAM_END) |
54 | rc = strm->avail_out; /* should be 0 */ |
55 | return rc; |
56 | } |
57 |