diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-03-30 13:59:52 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-03-30 13:59:52 -0700 |
commit | 59838093be51ee9447f6ad05483d697b6fa0368d (patch) | |
tree | b06fd711079732138c66db8579aa1e96c3e62ddc /drivers | |
parent | db34c5ffee649e2c4c870d1031a996398a187cf5 (diff) | |
parent | 18555cb6db2373b9a5ec1f7572773fd58c77f9ba (diff) |
Merge tag 'driver-core-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH:
"Here is the "big" set of driver core changes for 5.7-rc1.
Nothing huge in here, just lots of little firmware core changes and
use of new apis, a libfs fix, a debugfs api change, and some driver
core deferred probe rework.
All of these have been in linux-next for a while with no reported
issues"
* tag 'driver-core-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (44 commits)
Revert "driver core: Set fw_devlink to "permissive" behavior by default"
driver core: Set fw_devlink to "permissive" behavior by default
driver core: Replace open-coded list_last_entry()
driver core: Read atomic counter once in driver_probe_done()
libfs: fix infoleak in simple_attr_read()
driver core: Add device links from fwnode only for the primary device
platform/x86: touchscreen_dmi: Add info for the Chuwi Vi8 Plus tablet
platform/x86: touchscreen_dmi: Add EFI embedded firmware info support
Input: icn8505 - Switch to firmware_request_platform for retreiving the fw
Input: silead - Switch to firmware_request_platform for retreiving the fw
selftests: firmware: Add firmware_request_platform tests
test_firmware: add support for firmware_request_platform
firmware: Add new platform fallback mechanism and firmware_request_platform()
Revert "drivers: base: power: wakeup.c: Use built-in RCU list checking"
drivers: base: power: wakeup.c: Use built-in RCU list checking
component: allow missing unbind callback
debugfs: remove return value of debugfs_create_file_size()
debugfs: Check module state before warning in {full/open}_proxy_open()
firmware: fix a double abort case with fw_load_sysfs_fallback
arch_topology: Fix putting invalid cpu clk
...
Diffstat (limited to 'drivers')
23 files changed, 496 insertions, 119 deletions
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 6119e11a9f95..e5d691cf824c 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -94,7 +94,7 @@ static void update_topology_flags_workfn(struct work_struct *work) update_topology = 0; } -static u32 capacity_scale; +static DEFINE_PER_CPU(u32, freq_factor) = 1; static u32 *raw_capacity; static int free_raw_capacity(void) @@ -108,17 +108,23 @@ static int free_raw_capacity(void) void topology_normalize_cpu_scale(void) { u64 capacity; + u64 capacity_scale; int cpu; if (!raw_capacity) return; - pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale); + capacity_scale = 1; for_each_possible_cpu(cpu) { - pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n", - cpu, raw_capacity[cpu]); - capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT) - / capacity_scale; + capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu); + capacity_scale = max(capacity, capacity_scale); + } + + pr_debug("cpu_capacity: capacity_scale=%llu\n", capacity_scale); + for_each_possible_cpu(cpu) { + capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu); + capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT, + capacity_scale); topology_set_cpu_scale(cpu, capacity); pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n", cpu, topology_get_cpu_scale(cpu)); @@ -127,6 +133,7 @@ void topology_normalize_cpu_scale(void) bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu) { + struct clk *cpu_clk; static bool cap_parsing_failed; int ret; u32 cpu_capacity; @@ -146,10 +153,22 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu) return false; } } - capacity_scale = max(cpu_capacity, capacity_scale); raw_capacity[cpu] = cpu_capacity; pr_debug("cpu_capacity: %pOF cpu_capacity=%u (raw)\n", cpu_node, raw_capacity[cpu]); + + /* + * Update freq_factor for calculating early boot cpu capacities. + * For non-clk CPU DVFS mechanism, there's no way to get the + * frequency value now, assuming they are running at the same + * frequency (by keeping the initial freq_factor value). + */ + cpu_clk = of_clk_get(cpu_node, 0); + if (!PTR_ERR_OR_ZERO(cpu_clk)) { + per_cpu(freq_factor, cpu) = + clk_get_rate(cpu_clk) / 1000; + clk_put(cpu_clk); + } } else { if (raw_capacity) { pr_err("cpu_capacity: missing %pOF raw capacity\n", @@ -188,11 +207,8 @@ init_cpu_capacity_callback(struct notifier_block *nb, cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus); - for_each_cpu(cpu, policy->related_cpus) { - raw_capacity[cpu] = topology_get_cpu_scale(cpu) * - policy->cpuinfo.max_freq / 1000UL; - capacity_scale = max(raw_capacity[cpu], capacity_scale); - } + for_each_cpu(cpu, policy->related_cpus) + per_cpu(freq_factor, cpu) = policy->cpuinfo.max_freq / 1000; if (cpumask_empty(cpus_to_visit)) { topology_normalize_cpu_scale(); @@ -281,7 +297,7 @@ static int __init get_cpu_for_node(struct device_node *node) static int __init parse_core(struct device_node *core, int package_id, int core_id) { - char name[10]; + char name[20]; bool leaf = true; int i = 0; int cpu; @@ -327,7 +343,7 @@ static int __init parse_core(struct device_node *core, int package_id, static int __init parse_cluster(struct device_node *cluster, int depth) { - char name[10]; + char name[20]; bool leaf = true; bool has_cores = false; struct device_node *c; diff --git a/drivers/base/component.c b/drivers/base/component.c index c7879f5ae2fb..e97704104784 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -528,7 +528,8 @@ static void component_unbind(struct component *component, { WARN_ON(!component->bound); - component->ops->unbind(component->dev, master->dev, data); + if (component->ops && component->ops->unbind) + component->ops->unbind(component->dev, master->dev, data); component->bound = false; /* Release all resources claimed in the binding of this component */ diff --git a/drivers/base/core.c b/drivers/base/core.c index dbb0f9130f42..5e3cc1651c78 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -64,12 +64,12 @@ static inline void device_links_write_unlock(void) mutex_unlock(&device_links_lock); } -int device_links_read_lock(void) +int device_links_read_lock(void) __acquires(&device_links_srcu) { return srcu_read_lock(&device_links_srcu); } -void device_links_read_unlock(int idx) +void device_links_read_unlock(int idx) __releases(&device_links_srcu) { srcu_read_unlock(&device_links_srcu, idx); } @@ -523,9 +523,13 @@ static void device_link_add_missing_supplier_links(void) mutex_lock(&wfs_lock); list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) - if (!fwnode_call_int_op(dev->fwnode, add_links, dev)) + links.needs_suppliers) { + int ret = fwnode_call_int_op(dev->fwnode, add_links, dev); + if (!ret) list_del_init(&dev->links.needs_suppliers); + else if (ret != -ENODEV) + dev->links.need_for_probe = false; + } mutex_unlock(&wfs_lock); } @@ -2341,6 +2345,31 @@ static int device_private_init(struct device *dev) return 0; } +static u32 fw_devlink_flags; +static int __init fw_devlink_setup(char *arg) +{ + if (!arg) + return -EINVAL; + + if (strcmp(arg, "off") == 0) { + fw_devlink_flags = 0; + } else if (strcmp(arg, "permissive") == 0) { + fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY; + } else if (strcmp(arg, "on") == 0) { + fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER; + } else if (strcmp(arg, "rpm") == 0) { + fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER | + DL_FLAG_PM_RUNTIME; + } + return 0; +} +early_param("fw_devlink", fw_devlink_setup); + +u32 fw_devlink_get_flags(void) +{ + return fw_devlink_flags; +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -2375,6 +2404,7 @@ int device_add(struct device *dev) struct class_interface *class_intf; int error = -EINVAL, fw_ret; struct kobject *glue_dir = NULL; + bool is_fwnode_dev = false; dev = get_device(dev); if (!dev) @@ -2472,8 +2502,10 @@ int device_add(struct device *dev) kobject_uevent(&dev->kobj, KOBJ_ADD); - if (dev->fwnode && !dev->fwnode->dev) + if (dev->fwnode && !dev->fwnode->dev) { dev->fwnode->dev = dev; + is_fwnode_dev = true; + } /* * Check if any of the other devices (consumers) have been waiting for @@ -2489,7 +2521,8 @@ int device_add(struct device *dev) */ device_link_add_missing_supplier_links(); - if (fwnode_has_op(dev->fwnode, add_links)) { + if (fw_devlink_flags && is_fwnode_dev && + fwnode_has_op(dev->fwnode, add_links)) { fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); if (fw_ret == -ENODEV) device_link_wait_for_mandatory_supplier(dev); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 6265871a4af2..df19c002d747 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -231,8 +231,7 @@ static struct cpu_attr cpu_attrs[] = { static ssize_t print_cpus_kernel_max(struct device *dev, struct device_attribute *attr, char *buf) { - int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1); - return n; + return sprintf(buf, "%d\n", NR_CPUS - 1); } static DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL); @@ -258,13 +257,13 @@ static ssize_t print_cpus_offline(struct device *dev, buf[n++] = ','; if (nr_cpu_ids == total_cpus-1) - n += snprintf(&buf[n], len - n, "%u", nr_cpu_ids); + n += scnprintf(&buf[n], len - n, "%u", nr_cpu_ids); else - n += snprintf(&buf[n], len - n, "%u-%d", + n += scnprintf(&buf[n], len - n, "%u-%d", nr_cpu_ids, total_cpus-1); } - n += snprintf(&buf[n], len - n, "\n"); + n += scnprintf(&buf[n], len - n, "\n"); return n; } static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); @@ -272,7 +271,7 @@ static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); static ssize_t print_cpus_isolated(struct device *dev, struct device_attribute *attr, char *buf) { - int n = 0, len = PAGE_SIZE-2; + int n; cpumask_var_t isolated; if (!alloc_cpumask_var(&isolated, GFP_KERNEL)) @@ -280,7 +279,7 @@ static ssize_t print_cpus_isolated(struct device *dev, cpumask_andnot(isolated, cpu_possible_mask, housekeeping_cpumask(HK_FLAG_DOMAIN)); - n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(isolated)); + n = sprintf(buf, "%*pbl\n", cpumask_pr_args(isolated)); free_cpumask_var(isolated); @@ -292,11 +291,7 @@ static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL); static ssize_t print_cpus_nohz_full(struct device *dev, struct device_attribute *attr, char *buf) { - int n = 0, len = PAGE_SIZE-2; - - n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); - - return n; + return sprintf(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); } static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL); #endif diff --git a/drivers/base/dd.c b/drivers/base/dd.c index b25bcab2a26b..06ec0e851fa1 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -224,76 +224,52 @@ static int deferred_devs_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(deferred_devs); -static int deferred_probe_timeout = -1; +#ifdef CONFIG_MODULES +/* + * In the case of modules, set the default probe timeout to + * 30 seconds to give userland some time to load needed modules + */ +int driver_deferred_probe_timeout = 30; +#else +/* In the case of !modules, no probe timeout needed */ +int driver_deferred_probe_timeout = -1; +#endif +EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout); + static int __init deferred_probe_timeout_setup(char *str) { int timeout; if (!kstrtoint(str, 10, &timeout)) - deferred_probe_timeout = timeout; + driver_deferred_probe_timeout = timeout; return 1; } __setup("deferred_probe_timeout=", deferred_probe_timeout_setup); -static int __driver_deferred_probe_check_state(struct device *dev) -{ - if (!initcalls_done) - return -EPROBE_DEFER; - - if (!deferred_probe_timeout) { - dev_WARN(dev, "deferred probe timeout, ignoring dependency"); - return -ETIMEDOUT; - } - - return 0; -} - /** * driver_deferred_probe_check_state() - Check deferred probe state * @dev: device to check * - * Returns -ENODEV if init is done and all built-in drivers have had a chance - * to probe (i.e. initcalls are done), -ETIMEDOUT if deferred probe debug - * timeout has expired, or -EPROBE_DEFER if none of those conditions are met. + * 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. * * Drivers or subsystems can opt-in to calling this function instead of directly * returning -EPROBE_DEFER. */ int driver_deferred_probe_check_state(struct device *dev) { - int ret; - - ret = __driver_deferred_probe_check_state(dev); - if (ret < 0) - return ret; - - dev_warn(dev, "ignoring dependency for device, assuming no driver"); - - return -ENODEV; -} - -/** - * driver_deferred_probe_check_state_continue() - check deferred probe state - * @dev: device to check - * - * Returns -ETIMEDOUT if deferred probe debug timeout has expired, or - * -EPROBE_DEFER otherwise. - * - * Drivers or subsystems can opt-in to calling this function instead of - * directly returning -EPROBE_DEFER. - * - * This is similar to driver_deferred_probe_check_state(), but it allows the - * subsystem to keep deferring probe after built-in drivers have had a chance - * to probe. One scenario where that is useful is if built-in drivers rely on - * resources that are provided by modular drivers. - */ -int driver_deferred_probe_check_state_continue(struct device *dev) -{ - int ret; + if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) { + dev_warn(dev, "ignoring dependency for device, assuming no driver"); + return -ENODEV; + } - ret = __driver_deferred_probe_check_state(dev); - if (ret < 0) - return ret; + if (!driver_deferred_probe_timeout) { + dev_WARN(dev, "deferred probe timeout, ignoring dependency"); + return -ETIMEDOUT; + } return -EPROBE_DEFER; } @@ -302,7 +278,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) { struct device_private *private, *p; - deferred_probe_timeout = 0; + driver_deferred_probe_timeout = 0; driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); @@ -336,9 +312,9 @@ static int deferred_probe_initcall(void) driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); - if (deferred_probe_timeout > 0) { + if (driver_deferred_probe_timeout > 0) { schedule_delayed_work(&deferred_probe_timeout_work, - deferred_probe_timeout * HZ); + driver_deferred_probe_timeout * HZ); } return 0; } @@ -668,9 +644,10 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv) */ int driver_probe_done(void) { - pr_debug("%s: probe_count = %d\n", __func__, - atomic_read(&probe_count)); - if (atomic_read(&probe_count)) + int local_probe_count = atomic_read(&probe_count); + + pr_debug("%s: probe_count = %d\n", __func__, local_probe_count); + if (local_probe_count) return -EBUSY; return 0; } @@ -1222,7 +1199,7 @@ void driver_detach(struct device_driver *drv) spin_unlock(&drv->p->klist_devices.k_lock); break; } - dev_prv = list_entry(drv->p->klist_devices.k_list.prev, + dev_prv = list_last_entry(&drv->p->klist_devices.k_list, struct device_private, knode_driver.n_node); dev = dev_prv->device; diff --git a/drivers/base/firmware_loader/Makefile b/drivers/base/firmware_loader/Makefile index 0b2dfa6259c9..e87843408fe6 100644 --- a/drivers/base/firmware_loader/Makefile +++ b/drivers/base/firmware_loader/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_FW_LOADER_USER_HELPER) += fallback_table.o 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 obj-y += builtin/ diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c index 8704e1bae175..1e9c96e3ed63 100644 --- a/drivers/base/firmware_loader/fallback.c +++ b/drivers/base/firmware_loader/fallback.c @@ -525,7 +525,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, } retval = fw_sysfs_wait_timeout(fw_priv, timeout); - if (retval < 0) { + if (retval < 0 && retval != -ENOENT) { mutex_lock(&fw_lock); fw_load_abort(fw_sysfs); mutex_unlock(&fw_lock); diff --git a/drivers/base/firmware_loader/fallback.h b/drivers/base/firmware_loader/fallback.h index 21063503e4ea..06f4577733a8 100644 --- a/drivers/base/firmware_loader/fallback.h +++ b/drivers/base/firmware_loader/fallback.h @@ -66,4 +66,14 @@ static inline void unregister_sysfs_loader(void) } #endif /* CONFIG_FW_LOADER_USER_HELPER */ +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE +int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags); +#else +static inline int firmware_fallback_platform(struct fw_priv *fw_priv, + enum fw_opt opt_flags) +{ + return -ENOENT; +} +#endif + #endif /* __FIRMWARE_FALLBACK_H */ diff --git a/drivers/base/firmware_loader/fallback_platform.c b/drivers/base/firmware_loader/fallback_platform.c new file mode 100644 index 000000000000..c88c745590fe --- /dev/null +++ b/drivers/base/firmware_loader/fallback_platform.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/efi_embedded_fw.h> +#include <linux/property.h> +#include <linux/security.h> +#include <linux/vmalloc.h> + +#include "fallback.h" +#include "firmware.h" + +int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags) +{ + const u8 *data; + size_t size; + int rc; + + if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM)) + return -ENOENT; + + rc = security_kernel_load_data(LOADING_FIRMWARE_EFI_EMBEDDED); + if (rc) + return rc; + + rc = efi_get_embedded_fw(fw_priv->fw_name, &data, &size); + if (rc) + return rc; /* rc == -ENOENT when the fw was not found */ + + fw_priv->data = vmalloc(size); + if (!fw_priv->data) + return -ENOMEM; + + memcpy(fw_priv->data, data, size); + fw_priv->size = size; + fw_state_done(fw_priv); + return 0; +} diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h index 8656e5239a80..25836a6afc9f 100644 --- a/drivers/base/firmware_loader/firmware.h +++ b/drivers/base/firmware_loader/firmware.h @@ -29,6 +29,9 @@ * firmware caching mechanism. * @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes * precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER. + * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in + * the platform's main firmware. If both this fallback and the sysfs + * fallback are enabled, then this fallback will be tried first. */ enum fw_opt { FW_OPT_UEVENT = BIT(0), @@ -37,6 +40,7 @@ enum fw_opt { FW_OPT_NO_WARN = BIT(3), FW_OPT_NOCACHE = BIT(4), FW_OPT_NOFALLBACK_SYSFS = BIT(5), + FW_OPT_FALLBACK_PLATFORM = BIT(6), }; enum fw_status { diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 57133a9dad09..76f79913916d 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -493,8 +493,10 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, } fw_priv->size = 0; - rc = kernel_read_file_from_path(path, &buffer, &size, - msize, id); + + /* load firmware files from the mount namespace of init */ + rc = kernel_read_file_from_path_initns(path, &buffer, + &size, msize, id); if (rc) { if (rc != -ENOENT) dev_warn(device, "loading %s failed with error %d\n", @@ -776,6 +778,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name, fw_decompress_xz); #endif + if (ret == -ENOENT) + ret = firmware_fallback_platform(fw->priv, opt_flags); + if (ret) { if (!(opt_flags & FW_OPT_NO_WARN)) dev_warn(device, @@ -884,6 +889,30 @@ int request_firmware_direct(const struct firmware **firmware_p, EXPORT_SYMBOL_GPL(request_firmware_direct); /** + * firmware_request_platform() - request firmware with platform-fw fallback + * @firmware: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * + * This function is similar in behaviour to request_firmware, except that if + * direct filesystem lookup fails, it will fallback to looking for a copy of the + * requested firmware embedded in the platform's main (e.g. UEFI) firmware. + **/ +int firmware_request_platform(const struct firmware **firmware, + const char *name, struct device *device) +{ + int ret; + + /* Need to pin this module until return */ + __module_get(THIS_MODULE); + ret = _request_firmware(firmware, name, device, NULL, 0, + FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(firmware_request_platform); + +/** * firmware_request_cache() - cache firmware for suspend so resume can use it * @name: name of firmware file * @device: device for which firmware should be cached for diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index ecc83e2f032c..613828d3f106 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -239,6 +239,11 @@ config EFI_DISABLE_PCI_DMA endmenu +config EFI_EMBEDDED_FIRMWARE + bool + depends on EFI + select CRYPTO_LIB_SHA256 + config UEFI_CPER bool diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 554d795270d9..256d6121b2b3 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o +obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o fake_map-y += fake_mem.o fake_map-$(CONFIG_X86) += x86_fake_mem.o diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index d99f5b0c8a09..6703bedfa9e1 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -349,7 +349,7 @@ static int efifb_add_links(const struct fwnode_handle *fwnode, * If this fails, retrying this function at a later point won't * change anything. So, don't return an error after this. */ - if (!device_link_add(dev, sup_dev, 0)) + if (!device_link_add(dev, sup_dev, fw_devlink_get_flags())) dev_warn(dev, "device_link_add() failed\n"); put_device(sup_dev); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 21ea99f65113..0c2d99dfd3c5 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -17,6 +17,7 @@ #include <linux/kobject.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/efi.h> #include <linux/of.h> @@ -325,6 +326,59 @@ free_entry: static inline int efivar_ssdt_load(void) { return 0; } #endif +#ifdef CONFIG_DEBUG_FS + +#define EFI_DEBUGFS_MAX_BLOBS 32 + +static struct debugfs_blob_wrapper debugfs_blob[EFI_DEBUGFS_MAX_BLOBS]; + +static void __init efi_debugfs_init(void) +{ + struct dentry *efi_debugfs; + efi_memory_desc_t *md; + char name[32]; + int type_count[EFI_BOOT_SERVICES_DATA + 1] = {}; + int i = 0; + + efi_debugfs = debugfs_create_dir("efi", NULL); + if (IS_ERR_OR_NULL(efi_debugfs)) + return; + + for_each_efi_memory_desc(md) { + switch (md->type) { + case EFI_BOOT_SERVICES_CODE: + snprintf(name, sizeof(name), "boot_services_code%d", + type_count[md->type]++); + break; + case EFI_BOOT_SERVICES_DATA: + snprintf(name, sizeof(name), "boot_services_data%d", + type_count[md->type]++); + break; + default: + continue; + } + + if (i >= EFI_DEBUGFS_MAX_BLOBS) { + pr_warn("More then %d EFI boot service segments, only showing first %d in debugfs\n", + EFI_DEBUGFS_MAX_BLOBS, EFI_DEBUGFS_MAX_BLOBS); + break; + } + + debugfs_blob[i].size = md->num_pages << EFI_PAGE_SHIFT; + debugfs_blob[i].data = memremap(md->phys_addr, + debugfs_blob[i].size, + MEMREMAP_WB); + if (!debugfs_blob[i].data) + continue; + + debugfs_create_blob(name, 0400, efi_debugfs, &debugfs_blob[i]); + i++; + } +} +#else +static inline void efi_debugfs_init(void) {} +#endif + /* * We register the efi subsystem with the firmware subsystem and the * efivars subsystem with the efi subsystem, if the system was booted with @@ -381,6 +435,9 @@ static int __init efisubsys_init(void) goto err_remove_group; } + if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) + efi_debugfs_init(); + return 0; err_remove_group: diff --git a/drivers/firmware/efi/embedded-firmware.c b/drivers/firmware/efi/embedded-firmware.c new file mode 100644 index 000000000000..a1b199de9006 --- /dev/null +++ b/drivers/firmware/efi/embedded-firmware.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for extracting embedded firmware for peripherals from EFI code, + * + * Copyright (c) 2018 Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/dmi.h> +#include <linux/efi.h> +#include <linux/efi_embedded_fw.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/vmalloc.h> +#include <crypto/sha.h> + +/* Exported for use by lib/test_firmware.c only */ +LIST_HEAD(efi_embedded_fw_list); +EXPORT_SYMBOL_GPL(efi_embedded_fw_list); + +static bool checked_for_fw; + +static const struct dmi_system_id * const embedded_fw_table[] = { +#ifdef CONFIG_TOUCHSCREEN_DMI + touchscreen_dmi_table, +#endif + NULL +}; + +/* + * Note the efi_check_for_embedded_firmwares() code currently makes the + * following 2 assumptions. This may needs to be revisited if embedded firmware + * is found where this is not true: + * 1) The firmware is only found in EFI_BOOT_SERVICES_CODE memory segments + * 2) The firmware always starts at an offset which is a multiple of 8 bytes + */ +static int __init efi_check_md_for_embedded_firmware( + efi_memory_desc_t *md, const struct efi_embedded_fw_desc *desc) +{ + struct sha256_state sctx; + struct efi_embedded_fw *fw; + u8 sha256[32]; + u64 i, size; + u8 *map; + + size = md->num_pages << EFI_PAGE_SHIFT; + map = memremap(md->phys_addr, size, MEMREMAP_WB); + if (!map) { + pr_err("Error mapping EFI mem at %#llx\n", md->phys_addr); + return -ENOMEM; + } + + for (i = 0; (i + desc->length) <= size; i += 8) { + if (memcmp(map + i, desc->prefix, EFI_EMBEDDED_FW_PREFIX_LEN)) + continue; + + sha256_init(&sctx); + sha256_update(&sctx, map + i, desc->length); + sha256_final(&sctx, sha256); + if (memcmp(sha256, desc->sha256, 32) == 0) + break; + } + if ((i + desc->length) > size) { + memunmap(map); + return -ENOENT; + } + + pr_info("Found EFI embedded fw '%s'\n", desc->name); + + fw = kmalloc(sizeof(*fw), GFP_KERNEL); + if (!fw) { + memunmap(map); + return -ENOMEM; + } + + fw->data = kmemdup(map + i, desc->length, GFP_KERNEL); + memunmap(map); + if (!fw->data) { + kfree(fw); + return -ENOMEM; + } + + fw->name = desc->name; + fw->length = desc->length; + list_add(&fw->list, &efi_embedded_fw_list); + + return 0; +} + +void __init efi_check_for_embedded_firmwares(void) +{ + const struct efi_embedded_fw_desc *fw_desc; + const struct dmi_system_id *dmi_id; + efi_memory_desc_t *md; + int i, r; + + for (i = 0; embedded_fw_table[i]; i++) { + dmi_id = dmi_first_match(embedded_fw_table[i]); + if (!dmi_id) + continue; + + fw_desc = dmi_id->driver_data; + + /* + * In some drivers the struct driver_data contains may contain + * other driver specific data after the fw_desc struct; and + * the fw_desc struct itself may be empty, skip these. + */ + if (!fw_desc->name) + continue; + + for_each_efi_memory_desc(md) { + if (md->type != EFI_BOOT_SERVICES_CODE) + continue; + + r = efi_check_md_for_embedded_firmware(md, fw_desc); + if (r == 0) + break; + } + } + + checked_for_fw = true; +} + +int efi_get_embedded_fw(const char *name, const u8 **data, size_t *size) +{ + struct efi_embedded_fw *iter, *fw = NULL; + + if (!checked_for_fw) { + pr_warn("Warning %s called while we did not check for embedded fw\n", + __func__); + return -ENOENT; + } + + list_for_each_entry(iter, &efi_embedded_fw_list, list) { + if (strcmp(name, iter->name) == 0) { + fw = iter; + break; + } + } + + if (!fw) + return -ENOENT; + + *data = fw->data; + *size = fw->length; + + return 0; +} +EXPORT_SYMBOL_GPL(efi_get_embedded_fw); diff --git a/drivers/input/touchscreen/chipone_icn8505.c b/drivers/input/touchscreen/chipone_icn8505.c index c768186ce856..f9ca5502ac8c 100644 --- a/drivers/input/touchscreen/chipone_icn8505.c +++ b/drivers/input/touchscreen/chipone_icn8505.c @@ -288,7 +288,7 @@ static int icn8505_upload_fw(struct icn8505_data *icn8505) * we may need it at resume. Having loaded it once will make the * firmware class code cache it at suspend/resume. */ - error = request_firmware(&fw, icn8505->firmware_name, dev); + error = firmware_request_platform(&fw, icn8505->firmware_name, dev); if (error) { dev_err(dev, "Firmware request error %d\n", error); return error; diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index ad8b6a2bfd36..8fa2f3b7cfd8 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -288,7 +288,7 @@ static int silead_ts_load_fw(struct i2c_client *client) dev_dbg(dev, "Firmware file name: %s", data->fw_name); - error = request_firmware(&fw, data->fw_name, dev); + error = firmware_request_platform(&fw, data->fw_name, dev); if (error) { dev_err(dev, "Firmware request error %d\n", error); return error; diff --git a/drivers/of/property.c b/drivers/of/property.c index e851c57a15b0..f104f15b57fb 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1262,7 +1262,7 @@ static int of_link_property(struct device *dev, struct device_node *con_np, u32 dl_flags; if (dev->of_node == con_np) - dl_flags = DL_FLAG_AUTOPROBE_CONSUMER; + dl_flags = fw_devlink_get_flags(); else dl_flags = DL_FLAG_SYNC_STATE_ONLY; @@ -1299,15 +1299,9 @@ static int of_link_to_suppliers(struct device *dev, return ret; } -static bool of_devlink; -core_param(of_devlink, of_devlink, bool, 0); - static int of_fwnode_add_links(const struct fwnode_handle *fwnode, struct device *dev) { - if (!of_devlink) - return 0; - if (unlikely(!is_of_node(fwnode))) return 0; diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 9357f7c46cf3..1ed20ac2243f 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -127,11 +127,12 @@ static int dt_to_map_one_config(struct pinctrl *p, np_pctldev = of_get_next_parent(np_pctldev); if (!np_pctldev || of_node_is_root(np_pctldev)) { of_node_put(np_pctldev); + ret = driver_deferred_probe_check_state(p->dev); /* keep deferring if modules are enabled unless we've timed out */ - if (IS_ENABLED(CONFIG_MODULES) && !allow_default) - return driver_deferred_probe_check_state_continue(p->dev); - - return driver_deferred_probe_check_state(p->dev); + if (IS_ENABLED(CONFIG_MODULES) && !allow_default && + (ret == -ENODEV)) + ret = -EPROBE_DEFER; + return ret; } /* If we're creating a hog we can use the passed pctldev */ if (hog_pctldev && (np_pctldev == p->dev->of_node)) { diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 587403c44598..cd9e2758c479 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1252,6 +1252,7 @@ config INTEL_TURBO_MAX_3 config TOUCHSCREEN_DMI bool "DMI based touchscreen configuration info" depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD + select EFI_EMBEDDED_FIRMWARE if EFI ---help--- Certain ACPI based tablets with e.g. Silead or Chipone touchscreens do not have enough data in ACPI tables for the touchscreen driver to diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 93177e6e5ecd..6ec8923dec1a 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -11,12 +11,15 @@ #include <linux/acpi.h> #include <linux/device.h> #include <linux/dmi.h> +#include <linux/efi_embedded_fw.h> #include <linux/i2c.h> #include <linux/notifier.h> #include <linux/property.h> #include <linux/string.h> struct ts_dmi_data { + /* The EFI embedded-fw code expects this to be the first member! */ + struct efi_embedded_fw_desc embedded_fw; const char *acpi_name; const struct property_entry *properties; }; @@ -64,6 +67,15 @@ static const struct property_entry chuwi_hi8_pro_props[] = { }; static const struct ts_dmi_data chuwi_hi8_pro_data = { + .embedded_fw = { + .name = "silead/gsl3680-chuwi-hi8-pro.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 39864, + .sha256 = { 0xc0, 0x88, 0xc5, 0xef, 0xd1, 0x70, 0x77, 0x59, + 0x4e, 0xe9, 0xc4, 0xd8, 0x2e, 0xcd, 0xbf, 0x95, + 0x32, 0xd9, 0x03, 0x28, 0x0d, 0x48, 0x9f, 0x92, + 0x35, 0x37, 0xf6, 0x8b, 0x2a, 0xe4, 0x73, 0xff }, + }, .acpi_name = "MSSL1680:00", .properties = chuwi_hi8_pro_props, }; @@ -120,6 +132,18 @@ static const struct ts_dmi_data chuwi_vi8_data = { .properties = chuwi_vi8_props, }; +static const struct ts_dmi_data chuwi_vi8_plus_data = { + .embedded_fw = { + .name = "chipone/icn8505-HAMP0002.fw", + .prefix = { 0xb0, 0x07, 0x00, 0x00, 0xe4, 0x07, 0x00, 0x00 }, + .length = 35012, + .sha256 = { 0x93, 0xe5, 0x49, 0xe0, 0xb6, 0xa2, 0xb4, 0xb3, + 0x88, 0x96, 0x34, 0x97, 0x5e, 0xa8, 0x13, 0x78, + 0x72, 0x98, 0xb8, 0x29, 0xeb, 0x5c, 0xa7, 0xf1, + 0x25, 0x13, 0x43, 0xf4, 0x30, 0x7c, 0xfc, 0x7c }, + }, +}; + static const struct property_entry chuwi_vi10_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 0), PROPERTY_ENTRY_U32("touchscreen-min-y", 4), @@ -181,6 +205,15 @@ static const struct property_entry cube_iwork8_air_props[] = { }; static const struct ts_dmi_data cube_iwork8_air_data = { + .embedded_fw = { + .name = "silead/gsl3670-cube-iwork8-air.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 38808, + .sha256 = { 0xff, 0x62, 0x2d, 0xd1, 0x8a, 0x78, 0x04, 0x7b, + 0x33, 0x06, 0xb0, 0x4f, 0x7f, 0x02, 0x08, 0x9c, + 0x96, 0xd4, 0x9f, 0x04, 0xe1, 0x47, 0x25, 0x25, + 0x60, 0x77, 0x41, 0x33, 0xeb, 0x12, 0x82, 0xfc }, + }, .acpi_name = "MSSL1680:00", .properties = cube_iwork8_air_props, }; @@ -387,6 +420,15 @@ static const struct property_entry onda_v80_plus_v3_props[] = { }; static const struct ts_dmi_data onda_v80_plus_v3_data = { + .embedded_fw = { + .name = "silead/gsl3676-onda-v80-plus-v3.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 37224, + .sha256 = { 0x8f, 0xbd, 0x8f, 0x0c, 0x6b, 0xba, 0x5b, 0xf5, + 0xa3, 0xc7, 0xa3, 0xc0, 0x4f, 0xcd, 0xdf, 0x32, + 0xcc, 0xe4, 0x70, 0xd6, 0x46, 0x9c, 0xd7, 0xa7, + 0x4b, 0x82, 0x3f, 0xab, 0xc7, 0x90, 0xea, 0x23 }, + }, .acpi_name = "MSSL1680:00", .properties = onda_v80_plus_v3_props, }; @@ -449,6 +491,15 @@ static const struct property_entry pipo_w2s_props[] = { }; static const struct ts_dmi_data pipo_w2s_data = { + .embedded_fw = { + .name = "silead/gsl1680-pipo-w2s.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 39072, + .sha256 = { 0xd0, 0x58, 0xc4, 0x7d, 0x55, 0x2d, 0x62, 0x18, + 0xd1, 0x6a, 0x71, 0x73, 0x0b, 0x3f, 0xbe, 0x60, + 0xbb, 0x45, 0x8c, 0x52, 0x27, 0xb7, 0x18, 0xf4, + 0x31, 0x00, 0x6a, 0x49, 0x76, 0xd8, 0x7c, 0xd3 }, + }, .acpi_name = "MSSL1680:00", .properties = pipo_w2s_props, }; @@ -641,7 +692,7 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = { }; /* NOTE: Please keep this table sorted alphabetically */ -static const struct dmi_system_id touchscreen_dmi_table[] = { +const struct dmi_system_id touchscreen_dmi_table[] = { { /* Chuwi Hi8 */ .driver_data = (void *)&chuwi_hi8_data, @@ -704,6 +755,15 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Chuwi Vi8 Plus (CWI519) */ + .driver_data = (void *)&chuwi_vi8_plus_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + }, + { /* Chuwi Vi10 (CWI505) */ .driver_data = (void *)&chuwi_vi10_data, .matches = { @@ -1106,6 +1166,9 @@ static int __init ts_dmi_init(void) return 0; /* Not an error */ ts_data = dmi_id->driver_data; + /* Some dmi table entries only provide an efi_embedded_fw_desc */ + if (!ts_data->properties) + return 0; error = bus_register_notifier(&i2c_bus_type, &ts_dmi_notifier); if (error) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d015d99cb59d..51b6a2dea717 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5757,6 +5757,10 @@ static DECLARE_DELAYED_WORK(regulator_init_complete_work, static int __init regulator_init_complete(void) { + int delay = driver_deferred_probe_timeout; + + if (delay < 0) + delay = 0; /* * Since DT doesn't provide an idiomatic mechanism for * enabling full constraints and since it's much more natural @@ -5767,18 +5771,17 @@ static int __init regulator_init_complete(void) has_full_constraints = true; /* - * We punt completion for an arbitrary amount of time since - * systems like distros will load many drivers from userspace - * so consumers might not always be ready yet, this is - * particularly an issue with laptops where this might bounce - * the display off then on. Ideally we'd get a notification - * from userspace when this happens but we don't so just wait - * a bit and hope we waited long enough. It'd be better if - * we'd only do this on systems that need it, and a kernel - * command line option might be useful. + * If driver_deferred_probe_timeout is set, we punt + * completion for that many seconds since systems like + * distros will load many drivers from userspace so consumers + * might not always be ready yet, this is particularly an + * issue with laptops where this might bounce the display off + * then on. Ideally we'd get a notification from userspace + * when this happens but we don't so just wait a bit and hope + * we waited long enough. It'd be better if we'd only do + * this on systems that need it. */ - schedule_delayed_work(®ulator_init_complete_work, - msecs_to_jiffies(30000)); + schedule_delayed_work(®ulator_init_complete_work, delay * HZ); return 0; } |