Dash:
[t2-trunk.git] / architecture / riscv / package / linux / zstd-firmware.patch
blob184d65ff8c0fec119b18f711d82860e536b5655b
1 # --- T2-COPYRIGHT-NOTE-BEGIN ---
2 # This copyright note is auto-generated by scripts/Create-CopyPatch.
3 #
4 # T2 SDE: architecture/riscv/package/*/zstd-firmware.patch
5 # Copyright (C) 2020 - 2021 The T2 SDE Project
6 #
7 # More information can be found in the files COPYING and README.
8 #
9 # This patch file is dual-licensed. It is available under the license the
10 # patched project is licensed under, as long as it is an OpenSource license
11 # as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms
12 # of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 # --- T2-COPYRIGHT-NOTE-END ---
17 Zstd compressed firmware loading, because it's the best obviously.
19 Signed-off-by: René Rebe <rene@exactcode.de>
21 diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
22 index 5b24f3959255..92e6bec4605a 100644
23 --- a/drivers/base/firmware_loader/Kconfig
24 +++ b/drivers/base/firmware_loader/Kconfig
25 @@ -155,8 +155,8 @@ config FW_LOADER_USER_HELPER_FALLBACK
27 If you are unsure about this, say N here.
29 -config FW_LOADER_COMPRESS
30 - bool "Enable compressed firmware support"
31 +config FW_LOADER_COMPRESS_XZ
32 + bool "Enable XZ compressed firmware support"
33 select FW_LOADER_PAGED_BUF
34 select XZ_DEC
35 help
36 @@ -169,6 +169,16 @@ config FW_LOADER_COMPRESS
37 be compressed with either none or crc32 integrity check type (pass
38 "-C crc32" option to xz command).
40 +config FW_LOADER_COMPRESS_ZSTD
41 + bool "Enable Zstd compressed firmware support"
42 + select FW_LOADER_PAGED_BUF
43 + select ZSTD_DECOMPRESS
44 + help
45 + This option enables the support for loading Zstd compressed firmware
46 + files. The caller of firmware API receives the decompressed file
47 + content. The compressed file is loaded as a fallback, only after
48 + loading the raw file failed at first.
50 config FW_CACHE
51 bool "Enable firmware caching during suspend"
52 depends on PM_SLEEP
53 diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
54 index 9da0c9d5f538..a3406eebae86 100644
55 --- a/drivers/base/firmware_loader/main.c
56 +++ b/drivers/base/firmware_loader/main.c
57 @@ -33,7 +33,9 @@
58 #include <linux/syscore_ops.h>
59 #include <linux/reboot.h>
60 #include <linux/security.h>
61 +#include <linux/decompress/mm.h>
62 #include <linux/xz.h>
63 +#include <linux/zstd.h>
65 #include <generated/utsrelease.h>
67 @@ -339,7 +341,7 @@ int fw_map_paged_buf(struct fw_priv *fw_priv)
69 * XZ-compressed firmware support
71 -#ifdef CONFIG_FW_LOADER_COMPRESS
72 +#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
73 /* show an error and return the standard error code */
74 static int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret)
76 @@ -433,7 +435,186 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
77 else
78 return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);
80 -#endif /* CONFIG_FW_LOADER_COMPRESS */
81 +#endif /* CONFIG_FW_LOADER_COMPRESS_XZ */
83 +/*
84 + * Zstd-compressed firmware support
85 + */
86 +#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
87 +/* show an error and return the standard error code */
88 +static int handle_zstd_error(size_t ret)
90 + const int err = ZSTD_getErrorCode(ret);
92 + if (!ZSTD_isError(ret))
93 + return 0;
95 + switch (err) {
96 + case ZSTD_error_memory_allocation:
97 + printk("ZSTD decompressor ran out of memory");
98 + break;
99 + case ZSTD_error_prefix_unknown:
100 + printk("Input is not in the ZSTD format (wrong magic bytes)");
101 + break;
102 + case ZSTD_error_dstSize_tooSmall:
103 + case ZSTD_error_corruption_detected:
104 + case ZSTD_error_checksum_wrong:
105 + printk("ZSTD-compressed data is corrupt");
106 + break;
107 + default:
108 + printk("ZSTD-compressed data is probably corrupt");
109 + break;
111 + return -1;
114 +/* single-shot decompression onto the pre-allocated buffer */
115 +static int fw_decompress_zstd_single(struct device *dev, struct fw_priv *fw_priv,
116 + size_t in_size, const void *in_buffer)
118 + const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
119 + void *wksp = large_malloc(wksp_size);
120 + ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size);
121 + int err;
122 + size_t ret;
124 + if (dctx == NULL) {
125 + dev_warn(dev, "Out of memory while allocating ZSTD_DCtx");
126 + err = -1;
127 + goto out;
129 + /* Find out how large the frame actually is, there may be junk at
130 + * the end of the frame that ZSTD_decompressDCtx() can't handle.
131 + */
132 + ret = ZSTD_findFrameCompressedSize(in_buffer, in_size);
133 + err = handle_zstd_error(ret);
134 + if (err)
135 + goto out;
136 + in_size = (long)ret;
138 + ret = ZSTD_decompressDCtx(dctx, fw_priv->data, fw_priv->allocated_size, in_buffer, in_size);
139 + err = handle_zstd_error(ret);
140 + if (err)
141 + goto out;
143 + fw_priv->size = ret;
145 +out:
146 + if (wksp != NULL)
147 + large_free(wksp);
148 + return err;
151 +/* decompression on paged buffer and map it */
152 +static int fw_decompress_zstd_pages(struct device *dev, struct fw_priv *fw_priv,
153 + size_t in_size, const void *in_buffer)
155 + ZSTD_inBuffer in;
156 + ZSTD_outBuffer out;
157 + ZSTD_frameParams params;
158 + void *wksp = NULL;
159 + size_t wksp_size;
160 + ZSTD_DStream *dstream;
161 + int err = 0;
162 + size_t ret;
164 + struct page *page;
166 + fw_priv->is_paged_buf = true;
167 + fw_priv->size = 0;
169 + /* Set the first non-empty input buffer. */
170 + in.src = in_buffer;
171 + in.pos = 0;
172 + in.size = in_size;
174 + /*
175 + * We need to know the window size to allocate the ZSTD_DStream.
176 + * Since we are streaming, we need to allocate a buffer for the sliding
177 + * window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX
178 + * (8 MB), so it is important to use the actual value so as not to
179 + * waste memory when it is smaller.
180 + */
181 + ret = ZSTD_getFrameParams(&params, in.src, in.size);
182 + err = handle_zstd_error(ret);
183 + if (err)
184 + goto out;
185 + if (ret != 0) {
186 + printk("ZSTD-compressed data has an incomplete frame header");
187 + err = -1;
188 + goto out;
190 + if (params.windowSize > (1 << ZSTD_WINDOWLOG_MAX)) {
191 + printk("ZSTD-compressed data has too large a window size");
192 + err = -1;
193 + goto out;
196 + /*
197 + * Allocate the ZSTD_DStream now that we know how much memory is
198 + * required.
199 + */
200 + wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize);
201 + wksp = large_malloc(wksp_size);
202 + dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size);
203 + if (dstream == NULL) {
204 + printk("Out of memory while allocating ZSTD_DStream");
205 + err = -1;
206 + goto out;
209 + /*
210 + * Decompression loop:
211 + * Read more data if necessary (error if no more data can be read).
212 + * Call the decompression function, which returns 0 when finished.
213 + * Flush any data produced if using flush().
214 + */
215 + do {
216 + /* If we need to reload data the input is truncated. */
217 + if (in.pos == in.size) {
218 + printk("ZSTD-compressed data is truncated");
219 + err = -1;
220 + goto out;
223 + /* Allocate the output buffer */
224 + if (fw_grow_paged_buf(fw_priv, fw_priv->nr_pages + 1)) {
225 + err = -ENOMEM;
226 + goto out;
229 + /* Decompress into the newly allocated page */
230 + page = fw_priv->pages[fw_priv->nr_pages - 1];
231 + out.dst = kmap(page);
232 + out.pos = 0;
233 + out.size = PAGE_SIZE;
235 + /* Returns zero when the frame is complete. */
236 + ret = ZSTD_decompressStream(dstream, &out, &in);
237 + kunmap(page);
238 + err = handle_zstd_error(ret);
239 + if (err)
240 + goto out;
241 + fw_priv->size += out.pos;
242 + } while (ret != 0);
244 + err = fw_map_paged_buf(fw_priv);
245 +out:
246 + if (wksp != NULL)
247 + large_free(wksp);
248 + return err;
251 +static int fw_decompress_zstd(struct device *dev, struct fw_priv *fw_priv,
252 + size_t in_size, const void *in_buffer)
254 + /* if the buffer is pre-allocated, we can perform in single-shot mode */
255 + if (fw_priv->data)
256 + return fw_decompress_zstd_single(dev, fw_priv, in_size, in_buffer);
257 + else
258 + return fw_decompress_zstd_pages(dev, fw_priv, in_size, in_buffer);
260 +#endif /* CONFIG_FW_LOADER_COMPRESS_ZSTD */
262 /* direct firmware loading support */
263 static char fw_path_para[256];
264 @@ -768,11 +995,16 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
265 if (!(opt_flags & FW_OPT_PARTIAL))
266 nondirect = true;
268 -#ifdef CONFIG_FW_LOADER_COMPRESS
269 +#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
270 if (ret == -ENOENT && nondirect)
271 ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
272 fw_decompress_xz);
273 #endif
274 +#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
275 + if (ret == -ENOENT && nondirect)
276 + ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
277 + fw_decompress_zstd);
278 +#endif
279 if (ret == -ENOENT && nondirect)
280 ret = firmware_fallback_platform(fw->priv);
282 diff --git a/tools/testing/selftests/firmware/fw_lib.sh b/tools/testing/selftests/firmware/fw_lib.sh
283 index 5b8c0fedee76..0eafd6bd0aba 100755
284 --- a/tools/testing/selftests/firmware/fw_lib.sh
285 +++ b/tools/testing/selftests/firmware/fw_lib.sh
286 @@ -62,7 +62,7 @@ check_setup()
288 HAS_FW_LOADER_USER_HELPER="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER=y)"
289 HAS_FW_LOADER_USER_HELPER_FALLBACK="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y)"
290 - HAS_FW_LOADER_COMPRESS="$(kconfig_has CONFIG_FW_LOADER_COMPRESS=y)"
291 + HAS_FW_LOADER_COMPRESS_XZ="$(kconfig_has CONFIG_FW_LOADER_COMPRESS_XZ=y)"
292 PROC_FW_IGNORE_SYSFS_FALLBACK="0"
293 PROC_FW_FORCE_SYSFS_FALLBACK="0"
295 @@ -98,9 +98,9 @@ check_setup()
297 OLD_FWPATH="$(cat /sys/module/firmware_class/parameters/path)"
299 - if [ "$HAS_FW_LOADER_COMPRESS" = "yes" ]; then
300 + if [ "$HAS_FW_LOADER_COMPRESS_XZ" = "yes" ]; then
301 if ! which xz 2> /dev/null > /dev/null; then
302 - HAS_FW_LOADER_COMPRESS=""
303 + HAS_FW_LOADER_COMPRESS_XZ=""