On Wed, Jan 27, 2021 at 04:49:36PM +0100, Takashi Iwai wrote:
> 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 <ti...@suse.de>
> ---
>  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);

Since this is growing, it would be nicer if we avoid the #ifdef mess and
simply have code iterate over supported compressions, and this is
observed here in one line.

  Luis

Reply via email to