mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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…
	
		Reference in a new issue