Commit 23cfbc6e authored by Takashi Iwai's avatar Takashi Iwai Committed by Greg Kroah-Hartman

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: default avatarPiotr Gorski <lucjan.lucjanov@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Link: https://lore.kernel.org/r/20220421152908.4718-2-tiwai@suse.deSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ce522ba9
...@@ -159,21 +159,33 @@ config FW_LOADER_USER_HELPER_FALLBACK ...@@ -159,21 +159,33 @@ config FW_LOADER_USER_HELPER_FALLBACK
config FW_LOADER_COMPRESS config FW_LOADER_COMPRESS
bool "Enable compressed firmware support" bool "Enable compressed firmware support"
select FW_LOADER_PAGED_BUF
select XZ_DEC
help help
This option enables the support for loading compressed firmware This option enables the support for loading compressed firmware
files. The caller of firmware API receives the decompressed file files. The caller of firmware API receives the decompressed file
content. The compressed file is loaded as a fallback, only after content. The compressed file is loaded as a fallback, only after
loading the raw file failed at first. 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).
Compressed firmware support does not apply to firmware images Compressed firmware support does not apply to firmware images
that are built into the kernel image (CONFIG_EXTRA_FIRMWARE). that are built into the kernel image (CONFIG_EXTRA_FIRMWARE).
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 config FW_CACHE
bool "Enable firmware caching during suspend" bool "Enable firmware caching during suspend"
depends on PM_SLEEP depends on PM_SLEEP
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/zstd.h>
#include <linux/xz.h> #include <linux/xz.h>
#include <generated/utsrelease.h> #include <generated/utsrelease.h>
...@@ -304,10 +305,74 @@ int fw_map_paged_buf(struct fw_priv *fw_priv) ...@@ -304,10 +305,74 @@ int fw_map_paged_buf(struct fw_priv *fw_priv)
} }
#endif #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 {
zstd_frame_header params;
if (zstd_get_frame_header(&params, in_buffer, in_size) ||
params.frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN) {
dev_dbg(dev, "%s: invalid zstd header\n", __func__);
return -EINVAL;
}
out_size = params.frameContentSize;
out_buf = vzalloc(out_size);
if (!out_buf)
return -ENOMEM;
}
workspace_size = zstd_dctx_workspace_bound();
workspace = kvzalloc(workspace_size, GFP_KERNEL);
if (!workspace) {
err = -ENOMEM;
goto error;
}
ctx = zstd_init_dctx(workspace, workspace_size);
if (!ctx) {
dev_dbg(dev, "%s: failed to initialize context\n", __func__);
err = -EINVAL;
goto error;
}
len = zstd_decompress_dctx(ctx, out_buf, out_size, in_buffer, in_size);
if (zstd_is_error(len)) {
dev_dbg(dev, "%s: failed to decompress: %d\n", __func__,
zstd_get_error_code(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 * XZ-compressed firmware support
*/ */
#ifdef CONFIG_FW_LOADER_COMPRESS #ifdef CONFIG_FW_LOADER_COMPRESS_XZ
/* show an error and return the standard error code */ /* show an error and return the standard error code */
static int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret) 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, ...@@ -401,7 +466,7 @@ static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
else else
return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer); 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 */ /* direct firmware loading support */
static char fw_path_para[256]; static char fw_path_para[256];
...@@ -757,7 +822,12 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ...@@ -757,7 +822,12 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
if (!(opt_flags & FW_OPT_PARTIAL)) if (!(opt_flags & FW_OPT_PARTIAL))
nondirect = true; 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) if (ret == -ENOENT && nondirect)
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
fw_decompress_xz); fw_decompress_xz);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment