|
|
|
@@ -0,0 +1,173 @@
|
|
|
|
|
Modified to work on the older version of the zstd API used in 5.10
|
|
|
|
|
|
|
|
|
|
From 23cfbc6ec44e5e80d5522976ff45ffcdcddfb230 Mon Sep 17 00:00:00 2001
|
|
|
|
|
From: Takashi Iwai <tiwai@suse.de>
|
|
|
|
|
Date: Thu, 21 Apr 2022 17:29:04 +0200
|
|
|
|
|
Subject: [PATCH] firmware: Add the support for ZSTD-compressed firmware files
|
|
|
|
|
|
|
|
|
|
As the growing demand on ZSTD compressions, there have been requests
|
|
|
|
|
for the support of ZSTD-compressed firmware files, so here it is:
|
|
|
|
|
this patch extends the firmware loader code to allow loading ZSTD
|
|
|
|
|
files. The implementation is fairly straightforward, it just adds a
|
|
|
|
|
ZSTD decompression routine for the file expander. (And the code is
|
|
|
|
|
even simpler than XZ thanks to the ZSTD API that gives the original
|
|
|
|
|
decompressed size from the header.)
|
|
|
|
|
|
|
|
|
|
Link: https://lore.kernel.org/all/20210127154939.13288-1-tiwai@suse.de/
|
|
|
|
|
Tested-by: Piotr Gorski <lucjan.lucjanov@gmail.com>
|
|
|
|
|
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
|
|
|
|
Link: https://lore.kernel.org/r/20220421152908.4718-2-tiwai@suse.de
|
|
|
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
|
|
|
---
|
|
|
|
|
drivers/base/firmware_loader/Kconfig | 24 ++++++---
|
|
|
|
|
drivers/base/firmware_loader/main.c | 76 ++++++++++++++++++++++++++--
|
|
|
|
|
2 files changed, 91 insertions(+), 9 deletions(-)
|
|
|
|
|
|
|
|
|
|
diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
|
|
|
|
|
index 38f3b66bf52b27..08bb50451a969f 100644
|
|
|
|
|
--- a/drivers/base/firmware_loader/Kconfig
|
|
|
|
|
+++ b/drivers/base/firmware_loader/Kconfig
|
|
|
|
|
@@ -159,18 +159,30 @@ config FW_LOADER_USER_HELPER_FALLBACK
|
|
|
|
|
|
|
|
|
|
config FW_LOADER_COMPRESS
|
|
|
|
|
bool "Enable compressed firmware support"
|
|
|
|
|
- select FW_LOADER_PAGED_BUF
|
|
|
|
|
- select XZ_DEC
|
|
|
|
|
help
|
|
|
|
|
This option enables the support for loading compressed firmware
|
|
|
|
|
files. The caller of firmware API receives the decompressed file
|
|
|
|
|
content. The compressed file is loaded as a fallback, only after
|
|
|
|
|
loading the raw file failed at first.
|
|
|
|
|
|
|
|
|
|
- Currently only XZ-compressed files are supported, and they have to
|
|
|
|
|
- be compressed with either none or crc32 integrity check type (pass
|
|
|
|
|
- "-C crc32" option to xz command).
|
|
|
|
|
-
|
|
|
|
|
+if FW_LOADER_COMPRESS
|
|
|
|
|
+config FW_LOADER_COMPRESS_XZ
|
|
|
|
|
+ bool "Enable XZ-compressed firmware support"
|
|
|
|
|
+ select FW_LOADER_PAGED_BUF
|
|
|
|
|
+ select XZ_DEC
|
|
|
|
|
+ help
|
|
|
|
|
+ This option adds the support for XZ-compressed files.
|
|
|
|
|
+ The files have to be compressed with either none or crc32
|
|
|
|
|
+ integrity check type (pass "-C crc32" option to xz command).
|
|
|
|
|
+
|
|
|
|
|
+config FW_LOADER_COMPRESS_ZSTD
|
|
|
|
|
+ bool "Enable ZSTD-compressed firmware support"
|
|
|
|
|
+ select ZSTD_DECOMPRESS
|
|
|
|
|
+ help
|
|
|
|
|
+ This option adds the support for ZSTD-compressed files.
|
|
|
|
|
+
|
|
|
|
|
+endif # FW_LOADER_COMPRESS
|
|
|
|
|
+
|
|
|
|
|
config FW_CACHE
|
|
|
|
|
bool "Enable firmware caching during suspend"
|
|
|
|
|
depends on PM_SLEEP
|
|
|
|
|
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
|
|
|
|
|
index 94d1789a233e0f..74830aeec7f656 100644
|
|
|
|
|
--- a/drivers/base/firmware_loader/main.c
|
|
|
|
|
+++ b/drivers/base/firmware_loader/main.c
|
|
|
|
|
@@ -35,6 +35,7 @@
|
|
|
|
|
#include <linux/syscore_ops.h>
|
|
|
|
|
#include <linux/reboot.h>
|
|
|
|
|
#include <linux/security.h>
|
|
|
|
|
+#include <linux/zstd.h>
|
|
|
|
|
#include <linux/xz.h>
|
|
|
|
|
|
|
|
|
|
#include <generated/utsrelease.h>
|
|
|
|
|
@@ -304,10 +305,70 @@ int fw_map_paged_buf(struct fw_priv *fw_priv)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
|
+ * ZSTD-compressed firmware support
|
|
|
|
|
+ */
|
|
|
|
|
+#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
|
|
|
|
|
+static int fw_decompress_zstd(struct device *dev, struct fw_priv *fw_priv,
|
|
|
|
|
+ size_t in_size, const void *in_buffer)
|
|
|
|
|
+{
|
|
|
|
|
+ size_t len, out_size, workspace_size;
|
|
|
|
|
+ void *workspace, *out_buf;
|
|
|
|
|
+ ZSTD_DCtx *ctx;
|
|
|
|
|
+ int err;
|
|
|
|
|
+
|
|
|
|
|
+ if (fw_priv->allocated_size) {
|
|
|
|
|
+ out_size = fw_priv->allocated_size;
|
|
|
|
|
+ out_buf = fw_priv->data;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if ((out_size = ZSTD_getFrameContentSize(in_buffer, in_size)) == ZSTD_CONTENTSIZE_UNKNOWN) {
|
|
|
|
|
+ dev_dbg(dev, "%s: invalid zstd header\n", __func__);
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+ out_buf = vzalloc(out_size);
|
|
|
|
|
+ if (!out_buf)
|
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ workspace_size = ZSTD_DCtxWorkspaceBound();
|
|
|
|
|
+ workspace = kvzalloc(workspace_size, GFP_KERNEL);
|
|
|
|
|
+ if (!workspace) {
|
|
|
|
|
+ err = -ENOMEM;
|
|
|
|
|
+ goto error;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ctx = ZSTD_initDCtx(workspace, workspace_size);
|
|
|
|
|
+ if (!ctx) {
|
|
|
|
|
+ dev_dbg(dev, "%s: failed to initialize context\n", __func__);
|
|
|
|
|
+ err = -EINVAL;
|
|
|
|
|
+ goto error;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ len = ZSTD_decompressDCtx(ctx, out_buf, out_size, in_buffer, in_size);
|
|
|
|
|
+ if (ZSTD_isError(len)) {
|
|
|
|
|
+ dev_dbg(dev, "%s: failed to decompress: %d\n", __func__,
|
|
|
|
|
+ ZSTD_getErrorCode(len));
|
|
|
|
|
+ err = -EINVAL;
|
|
|
|
|
+ goto error;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!fw_priv->allocated_size)
|
|
|
|
|
+ fw_priv->data = out_buf;
|
|
|
|
|
+ fw_priv->size = len;
|
|
|
|
|
+ err = 0;
|
|
|
|
|
+
|
|
|
|
|
+ error:
|
|
|
|
|
+ kvfree(workspace);
|
|
|
|
|
+ if (err && !fw_priv->allocated_size)
|
|
|
|
|
+ vfree(out_buf);
|
|
|
|
|
+ return err;
|
|
|
|
|
+}
|
|
|
|
|
+#endif /* CONFIG_FW_LOADER_COMPRESS_ZSTD */
|
|
|
|
|
+
|
|
|
|
|
/*
|
|
|
|
|
* XZ-compressed firmware support
|
|
|
|
|
*/
|
|
|
|
|
-#ifdef CONFIG_FW_LOADER_COMPRESS
|
|
|
|
|
+#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
|
|
|
|
|
/* show an error and return the standard error code */
|
|
|
|
|
static int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret)
|
|
|
|
|
{
|
|
|
|
|
@@ -401,7 +466,7 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
|
|
|
|
|
else
|
|
|
|
|
return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);
|
|
|
|
|
}
|
|
|
|
|
-#endif /* CONFIG_FW_LOADER_COMPRESS */
|
|
|
|
|
+#endif /* CONFIG_FW_LOADER_COMPRESS_XZ */
|
|
|
|
|
|
|
|
|
|
/* direct firmware loading support */
|
|
|
|
|
static char fw_path_para[256];
|
|
|
|
|
@@ -757,7 +822,12 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
|
|
|
|
|
if (!(opt_flags & FW_OPT_PARTIAL))
|
|
|
|
|
nondirect = true;
|
|
|
|
|
|
|
|
|
|
-#ifdef CONFIG_FW_LOADER_COMPRESS
|
|
|
|
|
+#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
|
|
|
|
|
+ if (ret == -ENOENT && nondirect)
|
|
|
|
|
+ ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
|
|
|
|
|
+ fw_decompress_zstd);
|
|
|
|
|
+#endif
|
|
|
|
|
+#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
|
|
|
|
|
if (ret == -ENOENT && nondirect)
|
|
|
|
|
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
|
|
|
|
|
fw_decompress_xz);
|