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

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.de


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ce522ba9
Loading
Loading
Loading
Loading
+18 −6
Original line number Original line Diff line number Diff line
@@ -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
+73 −3
Original line number Original line Diff line number Diff line
@@ -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(&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,
	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);