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>
This commit is contained in:
parent
ce522ba9ef
commit
23cfbc6ec4
2 changed files with 91 additions and 9 deletions
|
@ -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 @@
|
||||||
#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)
|
||||||
}
|
}
|
||||||
#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(¶ms, 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,
|
||||||
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,
|
||||||
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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue