Due to the popular demands on ZSTD, here is a patch to add a support
of ZSTD-compressed firmware files via the direct firmware loader.
It's just like XZ-compressed file support, providing a decompressor
with ZSTD.  Since ZSTD API can give the decompression size beforehand,
the code is even simpler than XZ.

Signed-off-by: Takashi Iwai <[email protected]>
---
 drivers/base/firmware_loader/Kconfig | 21 ++++++--
 drivers/base/firmware_loader/main.c  | 74 ++++++++++++++++++++++++++--
 2 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/drivers/base/firmware_loader/Kconfig 
b/drivers/base/firmware_loader/Kconfig
index 5b24f3959255..f5307978927c 100644
--- a/drivers/base/firmware_loader/Kconfig
+++ b/drivers/base/firmware_loader/Kconfig
@@ -157,17 +157,28 @@ 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"
diff --git a/drivers/base/firmware_loader/main.c 
b/drivers/base/firmware_loader/main.c
index 78355095e00d..71332ed4959d 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -34,6 +34,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>
@@ -362,10 +363,72 @@ 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->data) {
+               out_size = fw_priv->allocated_size;
+               out_buf = fw_priv->data;
+       } else {
+               out_size = ZSTD_findDecompressedSize(in_buffer, in_size);
+               if (out_size == ZSTD_CONTENTSIZE_UNKNOWN ||
+                   out_size == ZSTD_CONTENTSIZE_ERROR) {
+                       dev_dbg(dev, "%s: invalid decompression size\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;
+       }
+
+       fw_priv->size = len;
+       if (!fw_priv->data)
+               fw_priv->data = out_buf;
+       err = 0;
+
+ error:
+       kvfree(workspace);
+       if (!fw_priv->data)
+               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)
 {
@@ -459,7 +522,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];
@@ -814,7 +877,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);
-- 
2.26.2

Reply via email to