diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-06-03 11:48:47 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-06-03 11:48:47 -0700 |
| commit | 500a434fc593f1fdb274c0e6fe09a0b9c0711a4b (patch) | |
| tree | e05efb88b90b6fa631bc22f2794233529b6c1de3 /drivers | |
| parent | 6f9b5ed8caddfbc94af8307c557ed57a8ec5c65c (diff) | |
| parent | b232b02bf3c205b13a26dcec08e53baddd8e59ed (diff) | |
Merge tag 'driver-core-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH:
"Here is the set of driver core changes for 5.19-rc1.
Lots of tiny driver core changes and cleanups happened this cycle, but
the two major things are:
- firmware_loader reorganization and additions including the ability
to have XZ compressed firmware images and the ability for userspace
to initiate the firmware load when it needs to, instead of being
always initiated by the kernel. FPGA devices specifically want this
ability to have their firmware changed over the lifetime of the
system boot, and this allows them to work without having to come up
with yet-another-custom-uapi interface for loading firmware for
them.
- physical location support added to sysfs so that devices that know
this information, can tell userspace where they are located in a
common way. Some ACPI devices already support this today, and more
bus types should support this in the future.
Smaller changes include:
- driver_override api cleanups and fixes
- error path cleanups and fixes
- get_abi script fixes
- deferred probe timeout changes.
It's that last change that I'm the most worried about. It has been
reported to cause boot problems for a number of systems, and I have a
tested patch series that resolves this issue. But I didn't get it
merged into my tree before 5.18-final came out, so it has not gotten
any linux-next testing.
I'll send the fixup patches (there are 2) as a follow-on series to this
pull request.
All have been tested in linux-next for weeks, with no reported issues
other than the above-mentioned boot time-outs"
* tag 'driver-core-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (55 commits)
driver core: fix deadlock in __device_attach
kernfs: Separate kernfs_pr_cont_buf and rename_lock.
topology: Remove unused cpu_cluster_mask()
driver core: Extend deferred probe timeout on driver registration
MAINTAINERS: add Russ Weight as a firmware loader maintainer
driver: base: fix UAF when driver_attach failed
test_firmware: fix end of loop test in upload_read_show()
driver core: location: Add "back" as a possible output for panel
driver core: location: Free struct acpi_pld_info *pld
driver core: Add "*" wildcard support to driver_async_probe cmdline param
driver core: location: Check for allocations failure
arch_topology: Trace the update thermal pressure
kernfs: Rename kernfs_put_open_node to kernfs_unlink_open_file.
export: fix string handling of namespace in EXPORT_SYMBOL_NS
rpmsg: use local 'dev' variable
rpmsg: Fix calling device_lock() on non-initialized device
firmware_loader: describe 'module' parameter of firmware_upload_register()
firmware_loader: Move definitions from sysfs_upload.h to sysfs.h
firmware_loader: Fix configs for sysfs split
selftests: firmware: Add firmware upload selftests
...
Diffstat (limited to 'drivers')
33 files changed, 1500 insertions, 709 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 3a2adeaef5ce..0e3ed5eb367b 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -98,31 +98,11 @@ static ssize_t driver_override_store(struct device *_dev, const char *buf, size_t count) { struct amba_device *dev = to_amba_device(_dev); - char *driver_override, *old, *cp; - - /* We need to keep extra room for a newline */ - if (count >= (PAGE_SIZE - 1)) - return -EINVAL; - - driver_override = kstrndup(buf, count, GFP_KERNEL); - if (!driver_override) - return -ENOMEM; - - cp = strchr(driver_override, '\n'); - if (cp) - *cp = '\0'; - - device_lock(_dev); - old = dev->driver_override; - if (strlen(driver_override)) { - dev->driver_override = driver_override; - } else { - kfree(driver_override); - dev->driver_override = NULL; - } - device_unlock(_dev); + int ret; - kfree(old); + ret = driver_set_override(_dev, &dev->driver_override, buf, count); + if (ret) + return ret; return count; } diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 02f7f1358e86..83217d243c25 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o +obj-$(CONFIG_ACPI) += physical_location.o obj-y += test/ diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index f73b836047cf..579c851a2bd7 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -19,6 +19,9 @@ #include <linux/rcupdate.h> #include <linux/sched.h> +#define CREATE_TRACE_POINTS +#include <trace/events/thermal_pressure.h> + static DEFINE_PER_CPU(struct scale_freq_data __rcu *, sft_data); static struct cpumask scale_freq_counters_mask; static bool scale_freq_invariant; @@ -195,6 +198,8 @@ void topology_update_thermal_pressure(const struct cpumask *cpus, th_pressure = max_capacity - capacity; + trace_thermal_pressure_update(cpu, th_pressure); + for_each_cpu(cpu, cpus) WRITE_ONCE(per_cpu(thermal_pressure, cpu), th_pressure); } diff --git a/drivers/base/base.h b/drivers/base/base.h index 2882af26392a..ab71403d102f 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -159,6 +159,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); extern void device_block_probing(void); extern void device_unblock_probing(void); +extern void deferred_probe_extend_timeout(void); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 97936ec49bde..7ca47e5b3c1f 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -617,7 +617,7 @@ int bus_add_driver(struct device_driver *drv) if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) - goto out_unregister; + goto out_del_list; } module_add_driver(drv->owner, drv); @@ -644,6 +644,8 @@ int bus_add_driver(struct device_driver *drv) return 0; +out_del_list: + klist_del(&priv->knode_bus); out_unregister: kobject_put(&priv->kobj); /* drv->p is freed in driver_release() */ diff --git a/drivers/base/core.c b/drivers/base/core.c index 2eede2ec3d64..7cd789c4985d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -32,6 +32,7 @@ #include <linux/dma-map-ops.h> /* for dma_default_coherent */ #include "base.h" +#include "physical_location.h" #include "power/power.h" #ifdef CONFIG_SYSFS_DEPRECATED @@ -2649,8 +2650,17 @@ static int device_add_attrs(struct device *dev) goto err_remove_dev_waiting_for_supplier; } + if (dev_add_physical_location(dev)) { + error = device_add_group(dev, + &dev_attr_physical_location_group); + if (error) + goto err_remove_dev_removable; + } + return 0; + err_remove_dev_removable: + device_remove_file(dev, &dev_attr_removable); err_remove_dev_waiting_for_supplier: device_remove_file(dev, &dev_attr_waiting_for_supplier); err_remove_dev_online: @@ -2672,6 +2682,11 @@ static void device_remove_attrs(struct device *dev) struct class *class = dev->class; const struct device_type *type = dev->type; + if (dev->physical_location) { + device_remove_group(dev, &dev_attr_physical_location_group); + kfree(dev->physical_location); + } + device_remove_file(dev, &dev_attr_removable); device_remove_file(dev, &dev_attr_waiting_for_supplier); device_remove_file(dev, &dev_attr_online); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 94b7ac9bf459..5443b09eef8b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -60,6 +60,7 @@ static bool initcalls_done; /* Save the async probe drivers' name from kernel cmdline */ #define ASYNC_DRV_NAMES_MAX_LEN 256 static char async_probe_drv_names[ASYNC_DRV_NAMES_MAX_LEN]; +static bool async_probe_default; /* * In some cases, like suspend to RAM or hibernation, It might be reasonable @@ -255,7 +256,12 @@ static int deferred_devs_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(deferred_devs); +#ifdef CONFIG_MODULES +int driver_deferred_probe_timeout = 10; +#else int driver_deferred_probe_timeout; +#endif + EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout); static DECLARE_WAIT_QUEUE_HEAD(probe_timeout_waitqueue); @@ -274,10 +280,10 @@ __setup("deferred_probe_timeout=", deferred_probe_timeout_setup); * @dev: device to check * * Return: - * -ENODEV if initcalls have completed and modules are disabled. - * -ETIMEDOUT if the deferred probe timeout was set and has expired - * and modules are enabled. - * -EPROBE_DEFER in other cases. + * * -ENODEV if initcalls have completed and modules are disabled. + * * -ETIMEDOUT if the deferred probe timeout was set and has expired + * and modules are enabled. + * * -EPROBE_DEFER in other cases. * * Drivers or subsystems can opt-in to calling this function instead of directly * returning -EPROBE_DEFER. @@ -316,6 +322,20 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) } static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func); +void deferred_probe_extend_timeout(void) +{ + /* + * If the work hasn't been queued yet or if the work expired, don't + * start a new one. + */ + if (cancel_delayed_work(&deferred_probe_timeout_work)) { + schedule_delayed_work(&deferred_probe_timeout_work, + driver_deferred_probe_timeout * HZ); + pr_debug("Extended deferred probe timeout by %d secs\n", + driver_deferred_probe_timeout); + } +} + /** * deferred_probe_initcall() - Enable probing of deferred devices * @@ -799,7 +819,11 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev) static inline bool cmdline_requested_async_probing(const char *drv_name) { - return parse_option_str(async_probe_drv_names, drv_name); + bool async_drv; + + async_drv = parse_option_str(async_probe_drv_names, drv_name); + + return (async_probe_default != async_drv); } /* The option format is "driver_async_probe=drv_name1,drv_name2,..." */ @@ -809,6 +833,8 @@ static int __init save_async_options(char *buf) pr_warn("Too long list of driver names for 'driver_async_probe'!\n"); strlcpy(async_probe_drv_names, buf, ASYNC_DRV_NAMES_MAX_LEN); + async_probe_default = parse_option_str(async_probe_drv_names, "*"); + return 1; } __setup("driver_async_probe=", save_async_options); @@ -943,6 +969,7 @@ out_unlock: static int __device_attach(struct device *dev, bool allow_async) { int ret = 0; + bool async = false; device_lock(dev); if (dev->p->dead) { @@ -981,7 +1008,7 @@ static int __device_attach(struct device *dev, bool allow_async) */ dev_dbg(dev, "scheduling asynchronous probe\n"); get_device(dev); - async_schedule_dev(__device_attach_async_helper, dev); + async = true; } else { pm_request_idle(dev); } @@ -991,6 +1018,8 @@ static int __device_attach(struct device *dev, bool allow_async) } out_unlock: device_unlock(dev); + if (async) + async_schedule_dev(__device_attach_async_helper, dev); return ret; } @@ -1084,6 +1113,7 @@ static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie) __device_driver_lock(dev, dev->parent); drv = dev->p->async_driver; + dev->p->async_driver = NULL; ret = driver_probe_device(drv, dev); __device_driver_unlock(dev, dev->parent); @@ -1130,7 +1160,7 @@ static int __driver_attach(struct device *dev, void *data) */ dev_dbg(dev, "probing driver %s asynchronously\n", drv->name); device_lock(dev); - if (!dev->driver) { + if (!dev->driver && !dev->p->async_driver) { get_device(dev); dev->p->async_driver = drv; async_schedule_dev(__driver_attach_async_helper, dev); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 8c0d33e182fd..15a75afe6b84 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -31,6 +31,75 @@ static struct device *next_device(struct klist_iter *i) } /** + * driver_set_override() - Helper to set or clear driver override. + * @dev: Device to change + * @override: Address of string to change (e.g. &device->driver_override); + * The contents will be freed and hold newly allocated override. + * @s: NUL-terminated string, new driver name to force a match, pass empty + * string to clear it ("" or "\n", where the latter is only for sysfs + * interface). + * @len: length of @s + * + * Helper to set or clear driver override in a device, intended for the cases + * when the driver_override field is allocated by driver/bus code. + * + * Returns: 0 on success or a negative error code on failure. + */ +int driver_set_override(struct device *dev, const char **override, + const char *s, size_t len) +{ + const char *new, *old; + char *cp; + + if (!override || !s) + return -EINVAL; + + /* + * The stored value will be used in sysfs show callback (sysfs_emit()), + * which has a length limit of PAGE_SIZE and adds a trailing newline. + * Thus we can store one character less to avoid truncation during sysfs + * show. + */ + if (len >= (PAGE_SIZE - 1)) + return -EINVAL; + + if (!len) { + /* Empty string passed - clear override */ + device_lock(dev); + old = *override; + *override = NULL; + device_unlock(dev); + kfree(old); + + return 0; + } + + cp = strnchr(s, len, '\n'); + if (cp) + len = cp - s; + + new = kstrndup(s, len, GFP_KERNEL); + if (!new) + return -ENOMEM; + + device_lock(dev); + old = *override; + if (cp != s) { + *override = new; + } else { + /* "\n" passed - clear override */ + kfree(new); + *override = NULL; + } + device_unlock(dev); + + kfree(old); + + return 0; +} +EXPORT_SYMBOL_GPL(driver_set_override); + +/** * driver_for_each_device - Iterator for devices bound to a driver. * @drv: Driver we're iterating. * @start: Device to begin with @@ -177,6 +246,7 @@ int driver_register(struct device_driver *drv) return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); + deferred_probe_extend_timeout(); return ret; } diff --git a/drivers/base/firmware_loader/Kconfig b/drivers/base/firmware_loader/Kconfig index 38f3b66bf52b..7e663dd972a7 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 select FW_LOADER_PAGED_BUF help This option enables a sysfs loading facility to enable firmware @@ -159,21 +163,33 @@ config FW_LOADER_USER_HELPER_FALLBACK config FW_LOADER_COMPRESS bool "Enable compressed firmware support" - select FW_LOADER_PAGED_BUF - select XZ_DEC help This option enables the support for loading compressed firmware files. The caller of firmware API receives the decompressed file content. The compressed file is loaded as a fallback, only after 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 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 bool "Enable firmware caching during suspend" depends on PM_SLEEP @@ -186,5 +202,19 @@ config FW_CACHE If unsure, say Y. +config FW_UPLOAD + bool "Enable users to initiate firmware updates using sysfs" + select FW_LOADER_SYSFS + select FW_LOADER_PAGED_BUF + help + Enabling this option will allow device drivers to expose a persistent + sysfs interface that allows firmware updates to be initiated from + userspace. For example, FPGA based PCIe cards load firmware and FPGA + images from local FLASH when the card boots. The images in FLASH may + be updated with new images provided by the user. Enable this device + to support cards that rely on user-initiated updates for firmware files. + + If unsure, say N. + endif # FW_LOADER endmenu diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile index e87843408fe6..60d19f9e0ddc 100644 --- a/drivers/base/firmware_loader/Makefile +++ b/drivers/base/firmware_loader/Makefile @@ -6,5 +6,7 @@ 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 +firmware_class-$(CONFIG_FW_UPLOAD) += sysfs_upload.o obj-y += builtin/ diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c index 4afb0e9312c0..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)) - 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); |
