lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <346561f8-df45-c3aa-a6b9-1328abe80e8f@redhat.com>
Date:   Sat, 2 Apr 2022 08:15:50 -0700
From:   Tom Rix <trix@...hat.com>
To:     Russ Weight <russell.h.weight@...el.com>, mcgrof@...nel.org,
        gregkh@...uxfoundation.org, rafael@...nel.org,
        linux-kernel@...r.kernel.org
Cc:     lgoncalv@...hat.com, yilun.xu@...el.com, hao.wu@...el.com,
        matthew.gerlach@...el.com, basheer.ahmed.muddebihal@...el.com,
        tianfei.zhang@...el.com
Subject: Re: [RESEND PATCH v1 3/8] firmware_loader: Split sysfs support from
 fallback


On 3/23/22 4:33 PM, Russ Weight wrote:
> In preparation for sharing the "loading" and "data" sysfs nodes with the
> new firmware upload support, split out sysfs functionality from fallback.c
> and fallback.h into sysfs.c and sysfs.h. This includes the firmware
> class driver code that is associated with the sysfs files and the
> fw_fallback_config support for the timeout sysfs node.
>
> CONFIG_FW_LOADER_SYSFS is created and is selected by
> CONFIG_FW_LOADER_USER_HELPER in order to include sysfs.o in
> firmware_class-objs.
>
> This is mostly just a code reorganization. There are a few symbols that
> change in scope, and these can be identified by looking at the header
> file changes. A few white-space warnings from checkpatch are also
> addressed in this patch.
>
> Signed-off-by: Russ Weight <russell.h.weight@...el.com>
> ---
> v1:
>    - Renamed files fw_sysfs.c and fw_sysfs.h to sysfs.c and sysfs.h
>    - Moved "MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE);" from sysfs.c to
>      sysfs.h to address an error identified by the kernel test robot
>      <lkp@...el.com>
> ---
>   drivers/base/firmware_loader/Kconfig    |   4 +
>   drivers/base/firmware_loader/Makefile   |   1 +
>   drivers/base/firmware_loader/fallback.c | 430 ------------------------
>   drivers/base/firmware_loader/fallback.h |  46 +--
>   drivers/base/firmware_loader/sysfs.c    | 411 ++++++++++++++++++++++
>   drivers/base/firmware_loader/sysfs.h    |  96 ++++++
>   6 files changed, 513 insertions(+), 475 deletions(-)
>   create mode 100644 drivers/base/firmware_loader/sysfs.c
>   create mode 100644 drivers/base/firmware_loader/sysfs.h
>
> diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig
> index 38f3b66bf52b..9e03178eee00 100644
> --- a/drivers/base/firmware_loader/Kconfig
> +++ b/drivers/base/firmware_loader/Kconfig
> @@ -29,6 +29,9 @@ if FW_LOADER
>   config FW_LOADER_PAGED_BUF
>   	bool
>   
> +config FW_LOADER_SYSFS
> +	bool
> +
>   config EXTRA_FIRMWARE
>   	string "Build named firmware blobs into the kernel binary"
>   	help
> @@ -72,6 +75,7 @@ config EXTRA_FIRMWARE_DIR
>   
>   config FW_LOADER_USER_HELPER
>   	bool "Enable the firmware sysfs fallback mechanism"
> +	select FW_LOADER_SYSFS

Is this code reordering necessary ?

This config is not removed or renamed later and has the same configs are 
the later FW_UPLOAD.

Maybe leave fallback.c as-is and rename FW_LOADER_USER_HELPER to 
FW_LOADER_SYSFS because the name is more descriptive.

The 'sorry we suck' help message replaced with a shorter message to 
indicate this is now a more capable config.

The later FW_UPLOAD would have a 'depends on FW_LOADER_SYSFS'

If you end up needing to do the reorder, move it to patch 1 because 
bisecting-wise it should not depend on improvements in the current 1,2 
patches.

Tom

>   	select FW_LOADER_PAGED_BUF
>   	help
>   	  This option enables a sysfs loading facility to enable firmware
> diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile
> index e87843408fe6..aab213f82288 100644
> --- a/drivers/base/firmware_loader/Makefile
> +++ b/drivers/base/firmware_loader/Makefile
> @@ -6,5 +6,6 @@ obj-$(CONFIG_FW_LOADER)	+= firmware_class.o
>   firmware_class-objs := main.o
>   firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
>   firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o
> +firmware_class-$(CONFIG_FW_LOADER_SYSFS) += sysfs.o
>   
>   obj-y += builtin/
> diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
> index d82e055a4297..bf68e3947814 100644
> --- a/drivers/base/firmware_loader/fallback.c
> +++ b/drivers/base/firmware_loader/fallback.c
> @@ -3,12 +3,9 @@
>   #include <linux/types.h>
>   #include <linux/kconfig.h>
>   #include <linux/list.h>
> -#include <linux/slab.h>
>   #include <linux/security.h>
> -#include <linux/highmem.h>
>   #include <linux/umh.h>
>   #include <linux/sysctl.h>
> -#include <linux/vmalloc.h>
>   #include <linux/module.h>
>   
>   #include "fallback.h"
> @@ -18,22 +15,6 @@
>    * firmware fallback mechanism
>    */
>   
> -MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE);
> -
> -extern struct firmware_fallback_config fw_fallback_config;
> -
> -/* These getters are vetted to use int properly */
> -static inline int __firmware_loading_timeout(void)
> -{
> -	return fw_fallback_config.loading_timeout;
> -}
> -
> -/* These setters are vetted to use int properly */
> -static void __fw_fallback_set_timeout(int timeout)
> -{
> -	fw_fallback_config.loading_timeout = timeout;
> -}
> -
>   /*
>    * use small loading timeout for caching devices' firmware because all these
>    * firmware images have been loaded successfully at lease once, also system is
> @@ -58,52 +39,11 @@ static long firmware_loading_timeout(void)
>   		__firmware_loading_timeout() * HZ : MAX_JIFFY_OFFSET;
>   }
>   
> -static inline bool fw_sysfs_done(struct fw_priv *fw_priv)
> -{
> -	return __fw_state_check(fw_priv, FW_STATUS_DONE);
> -}
> -
> -static inline bool fw_sysfs_loading(struct fw_priv *fw_priv)
> -{
> -	return __fw_state_check(fw_priv, FW_STATUS_LOADING);
> -}
> -
>   static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv,  long timeout)
>   {
>   	return __fw_state_wait_common(fw_priv, timeout);
>   }
>   
> -struct fw_sysfs {
> -	bool nowait;
> -	struct device dev;
> -	struct fw_priv *fw_priv;
> -	struct firmware *fw;
> -};
> -
> -static struct fw_sysfs *to_fw_sysfs(struct device *dev)
> -{
> -	return container_of(dev, struct fw_sysfs, dev);
> -}
> -
> -static void __fw_load_abort(struct fw_priv *fw_priv)
> -{
> -	/*
> -	 * There is a small window in which user can write to 'loading'
> -	 * between loading done/aborted and disappearance of 'loading'
> -	 */
> -	if (fw_state_is_aborted(fw_priv) || fw_sysfs_done(fw_priv))
> -		return;
> -
> -	fw_state_aborted(fw_priv);
> -}
> -
> -static void fw_load_abort(struct fw_sysfs *fw_sysfs)
> -{
> -	struct fw_priv *fw_priv = fw_sysfs->fw_priv;
> -
> -	__fw_load_abort(fw_priv);
> -}
> -
>   static LIST_HEAD(pending_fw_head);
>   
>   void kill_pending_fw_fallback_reqs(bool only_kill_custom)
> @@ -120,376 +60,6 @@ void kill_pending_fw_fallback_reqs(bool only_kill_custom)
>   	mutex_unlock(&fw_lock);
>   }
>   
> -static ssize_t timeout_show(struct class *class, struct class_attribute *attr,
> -			    char *buf)
> -{
> -	return sysfs_emit(buf, "%d\n", __firmware_loading_timeout());
> -}
> -
> -/**
> - * timeout_store() - set number of seconds to wait for firmware
> - * @class: device class pointer
> - * @attr: device attribute pointer
> - * @buf: buffer to scan for timeout value
> - * @count: number of bytes in @buf
> - *
> - *	Sets the number of seconds to wait for the firmware.  Once
> - *	this expires an error will be returned to the driver and no
> - *	firmware will be provided.
> - *
> - *	Note: zero means 'wait forever'.
> - **/
> -static ssize_t timeout_store(struct class *class, struct class_attribute *attr,
> -			     const char *buf, size_t count)
> -{
> -	int tmp_loading_timeout = simple_strtol(buf, NULL, 10);
> -
> -	if (tmp_loading_timeout < 0)
> -		tmp_loading_timeout = 0;
> -
> -	__fw_fallback_set_timeout(tmp_loading_timeout);
> -
> -	return count;
> -}
> -static CLASS_ATTR_RW(timeout);
> -
> -static struct attribute *firmware_class_attrs[] = {
> -	&class_attr_timeout.attr,
> -	NULL,
> -};
> -ATTRIBUTE_GROUPS(firmware_class);
> -
> -static void fw_dev_release(struct device *dev)
> -{
> -	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> -
> -	kfree(fw_sysfs);
> -}
> -
> -static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
> -{
> -	if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
> -		return -ENOMEM;
> -	if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout()))
> -		return -ENOMEM;
> -	if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
> -		return -ENOMEM;
> -
> -	return 0;
> -}
> -
> -static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
> -{
> -	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> -	int err = 0;
> -
> -	mutex_lock(&fw_lock);
> -	if (fw_sysfs->fw_priv)
> -		err = do_firmware_uevent(fw_sysfs, env);
> -	mutex_unlock(&fw_lock);
> -	return err;
> -}
> -
> -static struct class firmware_class = {
> -	.name		= "firmware",
> -	.class_groups	= firmware_class_groups,
> -	.dev_uevent	= firmware_uevent,
> -	.dev_release	= fw_dev_release,
> -};
> -
> -int register_sysfs_loader(void)
> -{
> -	int ret = class_register(&firmware_class);
> -
> -	if (ret != 0)
> -		return ret;
> -	return register_firmware_config_sysctl();
> -}
> -
> -void unregister_sysfs_loader(void)
> -{
> -	unregister_firmware_config_sysctl();
> -	class_unregister(&firmware_class);
> -}
> -
> -static ssize_t firmware_loading_show(struct device *dev,
> -				     struct device_attribute *attr, char *buf)
> -{
> -	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> -	int loading = 0;
> -
> -	mutex_lock(&fw_lock);
> -	if (fw_sysfs->fw_priv)
> -		loading = fw_sysfs_loading(fw_sysfs->fw_priv);
> -	mutex_unlock(&fw_lock);
> -
> -	return sysfs_emit(buf, "%d\n", loading);
> -}
> -
> -/**
> - * firmware_loading_store() - set value in the 'loading' control file
> - * @dev: device pointer
> - * @attr: device attribute pointer
> - * @buf: buffer to scan for loading control value
> - * @count: number of bytes in @buf
> - *
> - *	The relevant values are:
> - *
> - *	 1: Start a load, discarding any previous partial load.
> - *	 0: Conclude the load and hand the data to the driver code.
> - *	-1: Conclude the load with an error and discard any written data.
> - **/
> -static ssize_t firmware_loading_store(struct device *dev,
> -				      struct device_attribute *attr,
> -				      const char *buf, size_t count)
> -{
> -	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> -	struct fw_priv *fw_priv;
> -	ssize_t written = count;
> -	int loading = simple_strtol(buf, NULL, 10);
> -
> -	mutex_lock(&fw_lock);
> -	fw_priv = fw_sysfs->fw_priv;
> -	if (fw_state_is_aborted(fw_priv) || fw_state_is_done(fw_priv))
> -		goto out;
> -
> -	switch (loading) {
> -	case 1:
> -		/* discarding any previous partial load */
> -		if (!fw_sysfs_done(fw_priv)) {
> -			fw_free_paged_buf(fw_priv);
> -			fw_state_start(fw_priv);
> -		}
> -		break;
> -	case 0:
> -		if (fw_sysfs_loading(fw_priv)) {
> -			int rc;
> -
> -			/*
> -			 * Several loading requests may be pending on
> -			 * one same firmware buf, so let all requests
> -			 * see the mapped 'buf->data' once the loading
> -			 * is completed.
> -			 * */
> -			rc = fw_map_paged_buf(fw_priv);
> -			if (rc)
> -				dev_err(dev, "%s: map pages failed\n",
> -					__func__);
> -			else
> -				rc = security_kernel_post_load_data(fw_priv->data,
> -						fw_priv->size,
> -						LOADING_FIRMWARE, "blob");
> -
> -			/*
> -			 * Same logic as fw_load_abort, only the DONE bit
> -			 * is ignored and we set ABORT only on failure.
> -			 */
> -			if (rc) {
> -				fw_state_aborted(fw_priv);
> -				written = rc;
> -			} else {
> -				fw_state_done(fw_priv);
> -			}
> -			break;
> -		}
> -		fallthrough;
> -	default:
> -		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
> -		fallthrough;
> -	case -1:
> -		fw_load_abort(fw_sysfs);
> -		break;
> -	}
> -out:
> -	mutex_unlock(&fw_lock);
> -	return written;
> -}
> -
> -static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
> -
> -static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer,
> -			   loff_t offset, size_t count, bool read)
> -{
> -	if (read)
> -		memcpy(buffer, fw_priv->data + offset, count);
> -	else
> -		memcpy(fw_priv->data + offset, buffer, count);
> -}
> -
> -static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
> -			loff_t offset, size_t count, bool read)
> -{
> -	while (count) {
> -		void *page_data;
> -		int page_nr = offset >> PAGE_SHIFT;
> -		int page_ofs = offset & (PAGE_SIZE-1);
> -		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
> -
> -		page_data = kmap(fw_priv->pages[page_nr]);
> -
> -		if (read)
> -			memcpy(buffer, page_data + page_ofs, page_cnt);
> -		else
> -			memcpy(page_data + page_ofs, buffer, page_cnt);
> -
> -		kunmap(fw_priv->pages[page_nr]);
> -		buffer += page_cnt;
> -		offset += page_cnt;
> -		count -= page_cnt;
> -	}
> -}
> -
> -static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
> -				  struct bin_attribute *bin_attr,
> -				  char *buffer, loff_t offset, size_t count)
> -{
> -	struct device *dev = kobj_to_dev(kobj);
> -	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> -	struct fw_priv *fw_priv;
> -	ssize_t ret_count;
> -
> -	mutex_lock(&fw_lock);
> -	fw_priv = fw_sysfs->fw_priv;
> -	if (!fw_priv || fw_sysfs_done(fw_priv)) {
> -		ret_count = -ENODEV;
> -		goto out;
> -	}
> -	if (offset > fw_priv->size) {
> -		ret_count = 0;
> -		goto out;
> -	}
> -	if (count > fw_priv->size - offset)
> -		count = fw_priv->size - offset;
> -
> -	ret_count = count;
> -
> -	if (fw_priv->data)
> -		firmware_rw_data(fw_priv, buffer, offset, count, true);
> -	else
> -		firmware_rw(fw_priv, buffer, offset, count, true);
> -
> -out:
> -	mutex_unlock(&fw_lock);
> -	return ret_count;
> -}
> -
> -static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
> -{
> -	int err;
> -
> -	err = fw_grow_paged_buf(fw_sysfs->fw_priv,
> -				PAGE_ALIGN(min_size) >> PAGE_SHIFT);
> -	if (err)
> -		fw_load_abort(fw_sysfs);
> -	return err;
> -}
> -
> -/**
> - * firmware_data_write() - write method for firmware
> - * @filp: open sysfs file
> - * @kobj: kobject for the device
> - * @bin_attr: bin_attr structure
> - * @buffer: buffer being written
> - * @offset: buffer offset for write in total data store area
> - * @count: buffer size
> - *
> - *	Data written to the 'data' attribute will be later handed to
> - *	the driver as a firmware image.
> - **/
> -static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
> -				   struct bin_attribute *bin_attr,
> -				   char *buffer, loff_t offset, size_t count)
> -{
> -	struct device *dev = kobj_to_dev(kobj);
> -	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> -	struct fw_priv *fw_priv;
> -	ssize_t retval;
> -
> -	if (!capable(CAP_SYS_RAWIO))
> -		return -EPERM;
> -
> -	mutex_lock(&fw_lock);
> -	fw_priv = fw_sysfs->fw_priv;
> -	if (!fw_priv || fw_sysfs_done(fw_priv)) {
> -		retval = -ENODEV;
> -		goto out;
> -	}
> -
> -	if (fw_priv->data) {
> -		if (offset + count > fw_priv->allocated_size) {
> -			retval = -ENOMEM;
> -			goto out;
> -		}
> -		firmware_rw_data(fw_priv, buffer, offset, count, false);
> -		retval = count;
> -	} else {
> -		retval = fw_realloc_pages(fw_sysfs, offset + count);
> -		if (retval)
> -			goto out;
> -
> -		retval = count;
> -		firmware_rw(fw_priv, buffer, offset, count, false);
> -	}
> -
> -	fw_priv->size = max_t(size_t, offset + count, fw_priv->size);
> -out:
> -	mutex_unlock(&fw_lock);
> -	return retval;
> -}
> -
> -static struct bin_attribute firmware_attr_data = {
> -	.attr = { .name = "data", .mode = 0644 },
> -	.size = 0,
> -	.read = firmware_data_read,
> -	.write = firmware_data_write,
> -};
> -
> -static struct attribute *fw_dev_attrs[] = {
> -	&dev_attr_loading.attr,
> -	NULL
> -};
> -
> -static struct bin_attribute *fw_dev_bin_attrs[] = {
> -	&firmware_attr_data,
> -	NULL
> -};
> -
> -static const struct attribute_group fw_dev_attr_group = {
> -	.attrs = fw_dev_attrs,
> -	.bin_attrs = fw_dev_bin_attrs,
> -};
> -
> -static const struct attribute_group *fw_dev_attr_groups[] = {
> -	&fw_dev_attr_group,
> -	NULL
> -};
> -
> -static struct fw_sysfs *
> -fw_create_instance(struct firmware *firmware, const char *fw_name,
> -		   struct device *device, u32 opt_flags)
> -{
> -	struct fw_sysfs *fw_sysfs;
> -	struct device *f_dev;
> -
> -	fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL);
> -	if (!fw_sysfs) {
> -		fw_sysfs = ERR_PTR(-ENOMEM);
> -		goto exit;
> -	}
> -
> -	fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT);
> -	fw_sysfs->fw = firmware;
> -	f_dev = &fw_sysfs->dev;
> -
> -	device_initialize(f_dev);
> -	dev_set_name(f_dev, "%s", fw_name);
> -	f_dev->parent = device;
> -	f_dev->class = &firmware_class;
> -	f_dev->groups = fw_dev_attr_groups;
> -exit:
> -	return fw_sysfs;
> -}
> -
>   /**
>    * fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism
>    * @fw_sysfs: firmware sysfs information for the firmware to load
> diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h
> index 9f3055d3b4ca..144148595660 100644
> --- a/drivers/base/firmware_loader/fallback.h
> +++ b/drivers/base/firmware_loader/fallback.h
> @@ -6,29 +6,7 @@
>   #include <linux/device.h>
>   
>   #include "firmware.h"
> -
> -/**
> - * struct firmware_fallback_config - firmware fallback configuration settings
> - *
> - * Helps describe and fine tune the fallback mechanism.
> - *
> - * @force_sysfs_fallback: force the sysfs fallback mechanism to be used
> - * 	as if one had enabled CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y.
> - * 	Useful to help debug a CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
> - * 	functionality on a kernel where that config entry has been disabled.
> - * @ignore_sysfs_fallback: force to disable the sysfs fallback mechanism.
> - * 	This emulates the behaviour as if we had set the kernel
> - * 	config CONFIG_FW_LOADER_USER_HELPER=n.
> - * @old_timeout: for internal use
> - * @loading_timeout: the timeout to wait for the fallback mechanism before
> - * 	giving up, in seconds.
> - */
> -struct firmware_fallback_config {
> -	unsigned int force_sysfs_fallback;
> -	unsigned int ignore_sysfs_fallback;
> -	int old_timeout;
> -	int loading_timeout;
> -};
> +#include "sysfs.h"
>   
>   #ifdef CONFIG_FW_LOADER_USER_HELPER
>   int firmware_fallback_sysfs(struct firmware *fw, const char *name,
> @@ -40,19 +18,6 @@ void kill_pending_fw_fallback_reqs(bool only_kill_custom);
>   void fw_fallback_set_cache_timeout(void);
>   void fw_fallback_set_default_timeout(void);
>   
> -int register_sysfs_loader(void);
> -void unregister_sysfs_loader(void);
> -#ifdef CONFIG_SYSCTL
> -extern int register_firmware_config_sysctl(void);
> -extern void unregister_firmware_config_sysctl(void);
> -#else
> -static inline int register_firmware_config_sysctl(void)
> -{
> -	return 0;
> -}
> -static inline void unregister_firmware_config_sysctl(void) { }
> -#endif /* CONFIG_SYSCTL */
> -
>   #else /* CONFIG_FW_LOADER_USER_HELPER */
>   static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name,
>   					  struct device *device,
> @@ -66,15 +31,6 @@ static inline int firmware_fallback_sysfs(struct firmware *fw, const char *name,
>   static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
>   static inline void fw_fallback_set_cache_timeout(void) { }
>   static inline void fw_fallback_set_default_timeout(void) { }
> -
> -static inline int register_sysfs_loader(void)
> -{
> -	return 0;
> -}
> -
> -static inline void unregister_sysfs_loader(void)
> -{
> -}
>   #endif /* CONFIG_FW_LOADER_USER_HELPER */
>   
>   #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
> diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c
> new file mode 100644
> index 000000000000..49aeff45b123
> --- /dev/null
> +++ b/drivers/base/firmware_loader/sysfs.c
> @@ -0,0 +1,411 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/highmem.h>
> +#include <linux/module.h>
> +#include <linux/security.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include "firmware.h"
> +#include "sysfs.h"
> +
> +/*
> + * sysfs support for firmware loader
> + */
> +
> +static inline bool fw_sysfs_done(struct fw_priv *fw_priv)
> +{
> +	return __fw_state_check(fw_priv, FW_STATUS_DONE);
> +}
> +
> +static inline bool fw_sysfs_loading(struct fw_priv *fw_priv)
> +{
> +	return __fw_state_check(fw_priv, FW_STATUS_LOADING);
> +}
> +
> +void __fw_load_abort(struct fw_priv *fw_priv)
> +{
> +	/*
> +	 * There is a small window in which user can write to 'loading'
> +	 * between loading done/aborted and disappearance of 'loading'
> +	 */
> +	if (fw_state_is_aborted(fw_priv) || fw_sysfs_done(fw_priv))
> +		return;
> +
> +	fw_state_aborted(fw_priv);
> +}
> +
> +static ssize_t timeout_show(struct class *class, struct class_attribute *attr,
> +			    char *buf)
> +{
> +	return sysfs_emit(buf, "%d\n", __firmware_loading_timeout());
> +}
> +
> +/**
> + * timeout_store() - set number of seconds to wait for firmware
> + * @class: device class pointer
> + * @attr: device attribute pointer
> + * @buf: buffer to scan for timeout value
> + * @count: number of bytes in @buf
> + *
> + *	Sets the number of seconds to wait for the firmware.  Once
> + *	this expires an error will be returned to the driver and no
> + *	firmware will be provided.
> + *
> + *	Note: zero means 'wait forever'.
> + **/
> +static ssize_t timeout_store(struct class *class, struct class_attribute *attr,
> +			     const char *buf, size_t count)
> +{
> +	int tmp_loading_timeout = simple_strtol(buf, NULL, 10);
> +
> +	if (tmp_loading_timeout < 0)
> +		tmp_loading_timeout = 0;
> +
> +	__fw_fallback_set_timeout(tmp_loading_timeout);
> +
> +	return count;
> +}
> +static CLASS_ATTR_RW(timeout);
> +
> +static struct attribute *firmware_class_attrs[] = {
> +	&class_attr_timeout.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(firmware_class);
> +
> +static void fw_dev_release(struct device *dev)
> +{
> +	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> +
> +	kfree(fw_sysfs);
> +}
> +
> +#ifdef CONFIG_FW_LOADER_USER_HELPER
> +static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
> +{
> +	if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
> +		return -ENOMEM;
> +	if (add_uevent_var(env, "TIMEOUT=%i", __firmware_loading_timeout()))
> +		return -ENOMEM;
> +	if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
> +{
> +	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> +	int err = 0;
> +
> +	mutex_lock(&fw_lock);
> +	if (fw_sysfs->fw_priv)
> +		err = do_firmware_uevent(fw_sysfs, env);
> +	mutex_unlock(&fw_lock);
> +	return err;
> +}
> +#endif /* CONFIG_FW_LOADER_USER_HELPER */
> +
> +static struct class firmware_class = {
> +	.name		= "firmware",
> +	.class_groups	= firmware_class_groups,
> +#ifdef CONFIG_FW_LOADER_USER_HELPER
> +	.dev_uevent	= firmware_uevent,
> +#endif
> +	.dev_release	= fw_dev_release,
> +};
> +
> +int register_sysfs_loader(void)
> +{
> +	int ret = class_register(&firmware_class);
> +
> +	if (ret != 0)
> +		return ret;
> +	return register_firmware_config_sysctl();
> +}
> +
> +void unregister_sysfs_loader(void)
> +{
> +	unregister_firmware_config_sysctl();
> +	class_unregister(&firmware_class);
> +}
> +
> +static ssize_t firmware_loading_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> +	int loading = 0;
> +
> +	mutex_lock(&fw_lock);
> +	if (fw_sysfs->fw_priv)
> +		loading = fw_sysfs_loading(fw_sysfs->fw_priv);
> +	mutex_unlock(&fw_lock);
> +
> +	return sysfs_emit(buf, "%d\n", loading);
> +}
> +
> +/**
> + * firmware_loading_store() - set value in the 'loading' control file
> + * @dev: device pointer
> + * @attr: device attribute pointer
> + * @buf: buffer to scan for loading control value
> + * @count: number of bytes in @buf
> + *
> + *	The relevant values are:
> + *
> + *	 1: Start a load, discarding any previous partial load.
> + *	 0: Conclude the load and hand the data to the driver code.
> + *	-1: Conclude the load with an error and discard any written data.
> + **/
> +static ssize_t firmware_loading_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t count)
> +{
> +	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> +	struct fw_priv *fw_priv;
> +	ssize_t written = count;
> +	int loading = simple_strtol(buf, NULL, 10);
> +
> +	mutex_lock(&fw_lock);
> +	fw_priv = fw_sysfs->fw_priv;
> +	if (fw_state_is_aborted(fw_priv) || fw_state_is_done(fw_priv))
> +		goto out;
> +
> +	switch (loading) {
> +	case 1:
> +		/* discarding any previous partial load */
> +		if (!fw_sysfs_done(fw_priv)) {
> +			fw_free_paged_buf(fw_priv);
> +			fw_state_start(fw_priv);
> +		}
> +		break;
> +	case 0:
> +		if (fw_sysfs_loading(fw_priv)) {
> +			int rc;
> +
> +			/*
> +			 * Several loading requests may be pending on
> +			 * one same firmware buf, so let all requests
> +			 * see the mapped 'buf->data' once the loading
> +			 * is completed.
> +			 */
> +			rc = fw_map_paged_buf(fw_priv);
> +			if (rc)
> +				dev_err(dev, "%s: map pages failed\n",
> +					__func__);
> +			else
> +				rc = security_kernel_post_load_data(fw_priv->data,
> +								    fw_priv->size,
> +								    LOADING_FIRMWARE,
> +								    "blob");
> +
> +			/*
> +			 * Same logic as fw_load_abort, only the DONE bit
> +			 * is ignored and we set ABORT only on failure.
> +			 */
> +			if (rc) {
> +				fw_state_aborted(fw_priv);
> +				written = rc;
> +			} else {
> +				fw_state_done(fw_priv);
> +			}
> +			break;
> +		}
> +		fallthrough;
> +	default:
> +		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
> +		fallthrough;
> +	case -1:
> +		fw_load_abort(fw_sysfs);
> +		break;
> +	}
> +out:
> +	mutex_unlock(&fw_lock);
> +	return written;
> +}
> +
> +static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
> +
> +static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer,
> +			     loff_t offset, size_t count, bool read)
> +{
> +	if (read)
> +		memcpy(buffer, fw_priv->data + offset, count);
> +	else
> +		memcpy(fw_priv->data + offset, buffer, count);
> +}
> +
> +static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
> +			loff_t offset, size_t count, bool read)
> +{
> +	while (count) {
> +		void *page_data;
> +		int page_nr = offset >> PAGE_SHIFT;
> +		int page_ofs = offset & (PAGE_SIZE - 1);
> +		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
> +
> +		page_data = kmap(fw_priv->pages[page_nr]);
> +
> +		if (read)
> +			memcpy(buffer, page_data + page_ofs, page_cnt);
> +		else
> +			memcpy(page_data + page_ofs, buffer, page_cnt);
> +
> +		kunmap(fw_priv->pages[page_nr]);
> +		buffer += page_cnt;
> +		offset += page_cnt;
> +		count -= page_cnt;
> +	}
> +}
> +
> +static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
> +				  struct bin_attribute *bin_attr,
> +				  char *buffer, loff_t offset, size_t count)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> +	struct fw_priv *fw_priv;
> +	ssize_t ret_count;
> +
> +	mutex_lock(&fw_lock);
> +	fw_priv = fw_sysfs->fw_priv;
> +	if (!fw_priv || fw_sysfs_done(fw_priv)) {
> +		ret_count = -ENODEV;
> +		goto out;
> +	}
> +	if (offset > fw_priv->size) {
> +		ret_count = 0;
> +		goto out;
> +	}
> +	if (count > fw_priv->size - offset)
> +		count = fw_priv->size - offset;
> +
> +	ret_count = count;
> +
> +	if (fw_priv->data)
> +		firmware_rw_data(fw_priv, buffer, offset, count, true);
> +	else
> +		firmware_rw(fw_priv, buffer, offset, count, true);
> +
> +out:
> +	mutex_unlock(&fw_lock);
> +	return ret_count;
> +}
> +
> +static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
> +{
> +	int err;
> +
> +	err = fw_grow_paged_buf(fw_sysfs->fw_priv,
> +				PAGE_ALIGN(min_size) >> PAGE_SHIFT);
> +	if (err)
> +		fw_load_abort(fw_sysfs);
> +	return err;
> +}
> +
> +/**
> + * firmware_data_write() - write method for firmware
> + * @filp: open sysfs file
> + * @kobj: kobject for the device
> + * @bin_attr: bin_attr structure
> + * @buffer: buffer being written
> + * @offset: buffer offset for write in total data store area
> + * @count: buffer size
> + *
> + *	Data written to the 'data' attribute will be later handed to
> + *	the driver as a firmware image.
> + **/
> +static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
> +				   struct bin_attribute *bin_attr,
> +				   char *buffer, loff_t offset, size_t count)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
> +	struct fw_priv *fw_priv;
> +	ssize_t retval;
> +
> +	if (!capable(CAP_SYS_RAWIO))
> +		return -EPERM;
> +
> +	mutex_lock(&fw_lock);
> +	fw_priv = fw_sysfs->fw_priv;
> +	if (!fw_priv || fw_sysfs_done(fw_priv)) {
> +		retval = -ENODEV;
> +		goto out;
> +	}
> +
> +	if (fw_priv->data) {
> +		if (offset + count > fw_priv->allocated_size) {
> +			retval = -ENOMEM;
> +			goto out;
> +		}
> +		firmware_rw_data(fw_priv, buffer, offset, count, false);
> +		retval = count;
> +	} else {
> +		retval = fw_realloc_pages(fw_sysfs, offset + count);
> +		if (retval)
> +			goto out;
> +
> +		retval = count;
> +		firmware_rw(fw_priv, buffer, offset, count, false);
> +	}
> +
> +	fw_priv->size = max_t(size_t, offset + count, fw_priv->size);
> +out:
> +	mutex_unlock(&fw_lock);
> +	return retval;
> +}
> +
> +static struct bin_attribute firmware_attr_data = {
> +	.attr = { .name = "data", .mode = 0644 },
> +	.size = 0,
> +	.read = firmware_data_read,
> +	.write = firmware_data_write,
> +};
> +
> +static struct attribute *fw_dev_attrs[] = {
> +	&dev_attr_loading.attr,
> +	NULL
> +};
> +
> +static struct bin_attribute *fw_dev_bin_attrs[] = {
> +	&firmware_attr_data,
> +	NULL
> +};
> +
> +static const struct attribute_group fw_dev_attr_group = {
> +	.attrs = fw_dev_attrs,
> +	.bin_attrs = fw_dev_bin_attrs,
> +};
> +
> +static const struct attribute_group *fw_dev_attr_groups[] = {
> +	&fw_dev_attr_group,
> +	NULL
> +};
> +
> +struct fw_sysfs *
> +fw_create_instance(struct firmware *firmware, const char *fw_name,
> +		   struct device *device, u32 opt_flags)
> +{
> +	struct fw_sysfs *fw_sysfs;
> +	struct device *f_dev;
> +
> +	fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL);
> +	if (!fw_sysfs) {
> +		fw_sysfs = ERR_PTR(-ENOMEM);
> +		goto exit;
> +	}
> +
> +	fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT);
> +	fw_sysfs->fw = firmware;
> +	f_dev = &fw_sysfs->dev;
> +
> +	device_initialize(f_dev);
> +	dev_set_name(f_dev, "%s", fw_name);
> +	f_dev->parent = device;
> +	f_dev->class = &firmware_class;
> +	f_dev->groups = fw_dev_attr_groups;
> +exit:
> +	return fw_sysfs;
> +}
> diff --git a/drivers/base/firmware_loader/sysfs.h b/drivers/base/firmware_loader/sysfs.h
> new file mode 100644
> index 000000000000..5e2aff7bf6e7
> --- /dev/null
> +++ b/drivers/base/firmware_loader/sysfs.h
> @@ -0,0 +1,96 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __FIRMWARE_SYSFS_H
> +#define __FIRMWARE_SYSFS_H
> +
> +#include <linux/device.h>
> +
> +MODULE_IMPORT_NS(FIRMWARE_LOADER_PRIVATE);
> +
> +extern struct firmware_fallback_config fw_fallback_config;
> +
> +#ifdef CONFIG_FW_LOADER_USER_HELPER
> +/**
> + * struct firmware_fallback_config - firmware fallback configuration settings
> + *
> + * Helps describe and fine tune the fallback mechanism.
> + *
> + * @force_sysfs_fallback: force the sysfs fallback mechanism to be used
> + *	as if one had enabled CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y.
> + *	Useful to help debug a CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
> + *	functionality on a kernel where that config entry has been disabled.
> + * @ignore_sysfs_fallback: force to disable the sysfs fallback mechanism.
> + *	This emulates the behaviour as if we had set the kernel
> + *	config CONFIG_FW_LOADER_USER_HELPER=n.
> + * @old_timeout: for internal use
> + * @loading_timeout: the timeout to wait for the fallback mechanism before
> + *	giving up, in seconds.
> + */
> +struct firmware_fallback_config {
> +	unsigned int force_sysfs_fallback;
> +	unsigned int ignore_sysfs_fallback;
> +	int old_timeout;
> +	int loading_timeout;
> +};
> +
> +int register_sysfs_loader(void);
> +void unregister_sysfs_loader(void);
> +#ifdef CONFIG_SYSCTL
> +int register_firmware_config_sysctl(void);
> +void unregister_firmware_config_sysctl(void);
> +#else
> +static inline int register_firmware_config_sysctl(void)
> +{
> +	return 0;
> +}
> +
> +static inline void unregister_firmware_config_sysctl(void) { }
> +#endif /* CONFIG_SYSCTL */
> +#else /* CONFIG_FW_LOADER_USER_HELPER */
> +static inline int register_sysfs_loader(void)
> +{
> +	return 0;
> +}
> +
> +static inline void unregister_sysfs_loader(void)
> +{
> +}
> +#endif /* CONFIG_FW_LOADER_USER_HELPER */
> +
> +struct fw_sysfs {
> +	bool nowait;
> +	struct device dev;
> +	struct fw_priv *fw_priv;
> +	struct firmware *fw;
> +};
> +
> +static inline struct fw_sysfs *to_fw_sysfs(struct device *dev)
> +{
> +	return container_of(dev, struct fw_sysfs, dev);
> +}
> +
> +/* These getters are vetted to use int properly */
> +static inline int __firmware_loading_timeout(void)
> +{
> +	return fw_fallback_config.loading_timeout;
> +}
> +
> +/* These setters are vetted to use int properly */
> +static inline void __fw_fallback_set_timeout(int timeout)
> +{
> +	fw_fallback_config.loading_timeout = timeout;
> +}
> +
> +void __fw_load_abort(struct fw_priv *fw_priv);
> +
> +static inline void fw_load_abort(struct fw_sysfs *fw_sysfs)
> +{
> +	struct fw_priv *fw_priv = fw_sysfs->fw_priv;
> +
> +	__fw_load_abort(fw_priv);
> +}
> +
> +struct fw_sysfs *
> +fw_create_instance(struct firmware *firmware, const char *fw_name,
> +		   struct device *device, u32 opt_flags);
> +
> +#endif /* __FIRMWARE_SYSFS_H */

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ