Driver core patches for 5.7-rc1
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. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXoHLIg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yle2ACgjJJzRJl9Ckae3ms+9CS4OSFFZPsAoKSrXmFc Z7goYQdZo1zz8c0RYDrJ =Y91m -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
59838093be
|
@ -1360,6 +1360,24 @@
|
||||||
can be changed at run time by the max_graph_depth file
|
can be changed at run time by the max_graph_depth file
|
||||||
in the tracefs tracing directory. default: 0 (no limit)
|
in the tracefs tracing directory. default: 0 (no limit)
|
||||||
|
|
||||||
|
fw_devlink= [KNL] Create device links between consumer and supplier
|
||||||
|
devices by scanning the firmware to infer the
|
||||||
|
consumer/supplier relationships. This feature is
|
||||||
|
especially useful when drivers are loaded as modules as
|
||||||
|
it ensures proper ordering of tasks like device probing
|
||||||
|
(suppliers first, then consumers), supplier boot state
|
||||||
|
clean up (only after all consumers have probed),
|
||||||
|
suspend/resume & runtime PM (consumers first, then
|
||||||
|
suppliers).
|
||||||
|
Format: { off | permissive | on | rpm }
|
||||||
|
off -- Don't create device links from firmware info.
|
||||||
|
permissive -- Create device links from firmware info
|
||||||
|
but use it only for ordering boot state clean
|
||||||
|
up (sync_state() calls).
|
||||||
|
on -- Create device links from firmware info and use it
|
||||||
|
to enforce probe and suspend/resume ordering.
|
||||||
|
rpm -- Like "on", but also use to order runtime PM.
|
||||||
|
|
||||||
gamecon.map[2|3]=
|
gamecon.map[2|3]=
|
||||||
[HW,JOY] Multisystem joystick and NES/SNES/PSX pad
|
[HW,JOY] Multisystem joystick and NES/SNES/PSX pad
|
||||||
support via parallel port (up to 5 devices per port)
|
support via parallel port (up to 5 devices per port)
|
||||||
|
@ -3291,12 +3309,6 @@
|
||||||
This can be set from sysctl after boot.
|
This can be set from sysctl after boot.
|
||||||
See Documentation/admin-guide/sysctl/vm.rst for details.
|
See Documentation/admin-guide/sysctl/vm.rst for details.
|
||||||
|
|
||||||
of_devlink [OF, KNL] Create device links between consumer and
|
|
||||||
supplier devices by scanning the devictree to infer the
|
|
||||||
consumer/supplier relationships. A consumer device
|
|
||||||
will not be probed until all the supplier devices have
|
|
||||||
probed successfully.
|
|
||||||
|
|
||||||
ohci1394_dma=early [HW] enable debugging via the ohci1394 driver.
|
ohci1394_dma=early [HW] enable debugging via the ohci1394 driver.
|
||||||
See Documentation/debugging-via-ohci1394.txt for more
|
See Documentation/debugging-via-ohci1394.txt for more
|
||||||
info.
|
info.
|
||||||
|
|
|
@ -202,3 +202,106 @@ the following file:
|
||||||
|
|
||||||
If you echo 0 into it means MAX_JIFFY_OFFSET will be used. The data type
|
If you echo 0 into it means MAX_JIFFY_OFFSET will be used. The data type
|
||||||
for the timeout is an int.
|
for the timeout is an int.
|
||||||
|
|
||||||
|
EFI embedded firmware fallback mechanism
|
||||||
|
========================================
|
||||||
|
|
||||||
|
On some devices the system's EFI code / ROM may contain an embedded copy
|
||||||
|
of firmware for some of the system's integrated peripheral devices and
|
||||||
|
the peripheral's Linux device-driver needs to access this firmware.
|
||||||
|
|
||||||
|
Device drivers which need such firmware can use the
|
||||||
|
firmware_request_platform() function for this, note that this is a
|
||||||
|
separate fallback mechanism from the other fallback mechanisms and
|
||||||
|
this does not use the sysfs interface.
|
||||||
|
|
||||||
|
A device driver which needs this can describe the firmware it needs
|
||||||
|
using an efi_embedded_fw_desc struct:
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/efi_embedded_fw.h
|
||||||
|
:functions: efi_embedded_fw_desc
|
||||||
|
|
||||||
|
The EFI embedded-fw code works by scanning all EFI_BOOT_SERVICES_CODE memory
|
||||||
|
segments for an eight byte sequence matching prefix; if the prefix is found it
|
||||||
|
then does a sha256 over length bytes and if that matches makes a copy of length
|
||||||
|
bytes and adds that to its list with found firmwares.
|
||||||
|
|
||||||
|
To avoid doing this somewhat expensive scan on all systems, dmi matching is
|
||||||
|
used. Drivers are expected to export a dmi_system_id array, with each entries'
|
||||||
|
driver_data pointing to an efi_embedded_fw_desc.
|
||||||
|
|
||||||
|
To register this array with the efi-embedded-fw code, a driver needs to:
|
||||||
|
|
||||||
|
1. Always be builtin to the kernel or store the dmi_system_id array in a
|
||||||
|
separate object file which always gets builtin.
|
||||||
|
|
||||||
|
2. Add an extern declaration for the dmi_system_id array to
|
||||||
|
include/linux/efi_embedded_fw.h.
|
||||||
|
|
||||||
|
3. Add the dmi_system_id array to the embedded_fw_table in
|
||||||
|
drivers/firmware/efi/embedded-firmware.c wrapped in a #ifdef testing that
|
||||||
|
the driver is being builtin.
|
||||||
|
|
||||||
|
4. Add "select EFI_EMBEDDED_FIRMWARE if EFI_STUB" to its Kconfig entry.
|
||||||
|
|
||||||
|
The firmware_request_platform() function will always first try to load firmware
|
||||||
|
with the specified name directly from the disk, so the EFI embedded-fw can
|
||||||
|
always be overridden by placing a file under /lib/firmware.
|
||||||
|
|
||||||
|
Note that:
|
||||||
|
|
||||||
|
1. The code scanning for EFI embedded-firmware runs near the end
|
||||||
|
of start_kernel(), just before calling rest_init(). For normal drivers and
|
||||||
|
subsystems using subsys_initcall() to register themselves this does not
|
||||||
|
matter. This means that code running earlier cannot use EFI
|
||||||
|
embedded-firmware.
|
||||||
|
|
||||||
|
2. At the moment the EFI embedded-fw code assumes that firmwares always start at
|
||||||
|
an offset which is a multiple of 8 bytes, if this is not true for your case
|
||||||
|
send in a patch to fix this.
|
||||||
|
|
||||||
|
3. At the moment the EFI embedded-fw code only works on x86 because other archs
|
||||||
|
free EFI_BOOT_SERVICES_CODE before the EFI embedded-fw code gets a chance to
|
||||||
|
scan it.
|
||||||
|
|
||||||
|
4. The current brute-force scanning of EFI_BOOT_SERVICES_CODE is an ad-hoc
|
||||||
|
brute-force solution. There has been discussion to use the UEFI Platform
|
||||||
|
Initialization (PI) spec's Firmware Volume protocol. This has been rejected
|
||||||
|
because the FV Protocol relies on *internal* interfaces of the PI spec, and:
|
||||||
|
1. The PI spec does not define peripheral firmware at all
|
||||||
|
2. The internal interfaces of the PI spec do not guarantee any backward
|
||||||
|
compatibility. Any implementation details in FV may be subject to change,
|
||||||
|
and may vary system to system. Supporting the FV Protocol would be
|
||||||
|
difficult as it is purposely ambiguous.
|
||||||
|
|
||||||
|
Example how to check for and extract embedded firmware
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
To check for, for example Silead touchscreen controller embedded firmware,
|
||||||
|
do the following:
|
||||||
|
|
||||||
|
1. Boot the system with efi=debug on the kernel commandline
|
||||||
|
|
||||||
|
2. cp /sys/kernel/debug/efi/boot_services_code? to your home dir
|
||||||
|
|
||||||
|
3. Open the boot_services_code? files in a hex-editor, search for the
|
||||||
|
magic prefix for Silead firmware: F0 00 00 00 02 00 00 00, this gives you
|
||||||
|
the beginning address of the firmware inside the boot_services_code? file.
|
||||||
|
|
||||||
|
4. The firmware has a specific pattern, it starts with a 8 byte page-address,
|
||||||
|
typically F0 00 00 00 02 00 00 00 for the first page followed by 32-bit
|
||||||
|
word-address + 32-bit value pairs. With the word-address incrementing 4
|
||||||
|
bytes (1 word) for each pair until a page is complete. A complete page is
|
||||||
|
followed by a new page-address, followed by more word + value pairs. This
|
||||||
|
leads to a very distinct pattern. Scroll down until this pattern stops,
|
||||||
|
this gives you the end of the firmware inside the boot_services_code? file.
|
||||||
|
|
||||||
|
5. "dd if=boot_services_code? of=firmware bs=1 skip=<begin-addr> count=<len>"
|
||||||
|
will extract the firmware for you. Inspect the firmware file in a
|
||||||
|
hexeditor to make sure you got the dd parameters correct.
|
||||||
|
|
||||||
|
6. Copy it to /lib/firmware under the expected name to test it.
|
||||||
|
|
||||||
|
7. If the extracted firmware works, you can use the found info to fill an
|
||||||
|
efi_embedded_fw_desc struct to describe it, run "sha256sum firmware"
|
||||||
|
to get the sha256sum to put in the sha256 field.
|
||||||
|
|
|
@ -12,6 +12,8 @@ a driver issues a firmware API call.
|
||||||
return it immediately
|
return it immediately
|
||||||
* The ''Direct filesystem lookup'' is performed next, if found we
|
* The ''Direct filesystem lookup'' is performed next, if found we
|
||||||
return it immediately
|
return it immediately
|
||||||
|
* The ''Platform firmware fallback'' is performed next, but only when
|
||||||
|
firmware_request_platform() is used, if found we return it immediately
|
||||||
* If no firmware has been found and the fallback mechanism was enabled
|
* If no firmware has been found and the fallback mechanism was enabled
|
||||||
the sysfs interface is created. After this either a kobject uevent
|
the sysfs interface is created. After this either a kobject uevent
|
||||||
is issued or the custom firmware loading is relied upon for firmware
|
is issued or the custom firmware loading is relied upon for firmware
|
||||||
|
|
|
@ -25,6 +25,11 @@ firmware_request_nowarn
|
||||||
.. kernel-doc:: drivers/base/firmware_loader/main.c
|
.. kernel-doc:: drivers/base/firmware_loader/main.c
|
||||||
:functions: firmware_request_nowarn
|
:functions: firmware_request_nowarn
|
||||||
|
|
||||||
|
firmware_request_platform
|
||||||
|
-------------------------
|
||||||
|
.. kernel-doc:: drivers/base/firmware_loader/main.c
|
||||||
|
:functions: firmware_request_platform
|
||||||
|
|
||||||
request_firmware_direct
|
request_firmware_direct
|
||||||
-----------------------
|
-----------------------
|
||||||
.. kernel-doc:: drivers/base/firmware_loader/main.c
|
.. kernel-doc:: drivers/base/firmware_loader/main.c
|
||||||
|
|
|
@ -62,7 +62,7 @@ missing.
|
||||||
Create a file with an initial size, the following function can be used
|
Create a file with an initial size, the following function can be used
|
||||||
instead::
|
instead::
|
||||||
|
|
||||||
struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
|
void debugfs_create_file_size(const char *name, umode_t mode,
|
||||||
struct dentry *parent, void *data,
|
struct dentry *parent, void *data,
|
||||||
const struct file_operations *fops,
|
const struct file_operations *fops,
|
||||||
loff_t file_size);
|
loff_t file_size);
|
||||||
|
|
|
@ -243,6 +243,7 @@ int __init efi_memblock_x86_reserve_range(void)
|
||||||
efi.memmap.desc_version);
|
efi.memmap.desc_version);
|
||||||
|
|
||||||
memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size);
|
memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size);
|
||||||
|
set_bit(EFI_PRESERVE_BS_REGIONS, &efi.flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -943,6 +944,7 @@ static void __init __efi_enter_virtual_mode(void)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
efi_check_for_embedded_firmwares();
|
||||||
efi_free_boot_services();
|
efi_free_boot_services();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -410,6 +410,10 @@ void __init efi_free_boot_services(void)
|
||||||
int num_entries = 0;
|
int num_entries = 0;
|
||||||
void *new, *new_md;
|
void *new, *new_md;
|
||||||
|
|
||||||
|
/* Keep all regions for /sys/kernel/debug/efi */
|
||||||
|
if (efi_enabled(EFI_DBG))
|
||||||
|
return;
|
||||||
|
|
||||||
for_each_efi_memory_desc(md) {
|
for_each_efi_memory_desc(md) {
|
||||||
unsigned long long start = md->phys_addr;
|
unsigned long long start = md->phys_addr;
|
||||||
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
|
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
|
||||||
|
|
|
@ -94,7 +94,7 @@ static void update_topology_flags_workfn(struct work_struct *work)
|
||||||
update_topology = 0;
|
update_topology = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 capacity_scale;
|
static DEFINE_PER_CPU(u32, freq_factor) = 1;
|
||||||
static u32 *raw_capacity;
|
static u32 *raw_capacity;
|
||||||
|
|
||||||
static int free_raw_capacity(void)
|
static int free_raw_capacity(void)
|
||||||
|
@ -108,17 +108,23 @@ static int free_raw_capacity(void)
|
||||||
void topology_normalize_cpu_scale(void)
|
void topology_normalize_cpu_scale(void)
|
||||||
{
|
{
|
||||||
u64 capacity;
|
u64 capacity;
|
||||||
|
u64 capacity_scale;
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
if (!raw_capacity)
|
if (!raw_capacity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
|
capacity_scale = 1;
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n",
|
capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
|
||||||
cpu, raw_capacity[cpu]);
|
capacity_scale = max(capacity, capacity_scale);
|
||||||
capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
|
}
|
||||||
/ 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);
|
topology_set_cpu_scale(cpu, capacity);
|
||||||
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
|
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
|
||||||
cpu, topology_get_cpu_scale(cpu));
|
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)
|
bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
|
||||||
{
|
{
|
||||||
|
struct clk *cpu_clk;
|
||||||
static bool cap_parsing_failed;
|
static bool cap_parsing_failed;
|
||||||
int ret;
|
int ret;
|
||||||
u32 cpu_capacity;
|
u32 cpu_capacity;
|
||||||
|
@ -146,10 +153,22 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
capacity_scale = max(cpu_capacity, capacity_scale);
|
|
||||||
raw_capacity[cpu] = cpu_capacity;
|
raw_capacity[cpu] = cpu_capacity;
|
||||||
pr_debug("cpu_capacity: %pOF cpu_capacity=%u (raw)\n",
|
pr_debug("cpu_capacity: %pOF cpu_capacity=%u (raw)\n",
|
||||||
cpu_node, raw_capacity[cpu]);
|
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 {
|
} else {
|
||||||
if (raw_capacity) {
|
if (raw_capacity) {
|
||||||
pr_err("cpu_capacity: missing %pOF raw capacity\n",
|
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);
|
cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus);
|
||||||
|
|
||||||
for_each_cpu(cpu, policy->related_cpus) {
|
for_each_cpu(cpu, policy->related_cpus)
|
||||||
raw_capacity[cpu] = topology_get_cpu_scale(cpu) *
|
per_cpu(freq_factor, cpu) = policy->cpuinfo.max_freq / 1000;
|
||||||
policy->cpuinfo.max_freq / 1000UL;
|
|
||||||
capacity_scale = max(raw_capacity[cpu], capacity_scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpumask_empty(cpus_to_visit)) {
|
if (cpumask_empty(cpus_to_visit)) {
|
||||||
topology_normalize_cpu_scale();
|
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,
|
static int __init parse_core(struct device_node *core, int package_id,
|
||||||
int core_id)
|
int core_id)
|
||||||
{
|
{
|
||||||
char name[10];
|
char name[20];
|
||||||
bool leaf = true;
|
bool leaf = true;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int cpu;
|
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)
|
static int __init parse_cluster(struct device_node *cluster, int depth)
|
||||||
{
|
{
|
||||||
char name[10];
|
char name[20];
|
||||||
bool leaf = true;
|
bool leaf = true;
|
||||||
bool has_cores = false;
|
bool has_cores = false;
|
||||||
struct device_node *c;
|
struct device_node *c;
|
||||||
|
|
|
@ -528,6 +528,7 @@ static void component_unbind(struct component *component,
|
||||||
{
|
{
|
||||||
WARN_ON(!component->bound);
|
WARN_ON(!component->bound);
|
||||||
|
|
||||||
|
if (component->ops && component->ops->unbind)
|
||||||
component->ops->unbind(component->dev, master->dev, data);
|
component->ops->unbind(component->dev, master->dev, data);
|
||||||
component->bound = false;
|
component->bound = false;
|
||||||
|
|
||||||
|
|
|
@ -64,12 +64,12 @@ static inline void device_links_write_unlock(void)
|
||||||
mutex_unlock(&device_links_lock);
|
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);
|
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);
|
srcu_read_unlock(&device_links_srcu, idx);
|
||||||
}
|
}
|
||||||
|
@ -523,9 +523,13 @@ static void device_link_add_missing_supplier_links(void)
|
||||||
|
|
||||||
mutex_lock(&wfs_lock);
|
mutex_lock(&wfs_lock);
|
||||||
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
|
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
|
||||||
links.needs_suppliers)
|
links.needs_suppliers) {
|
||||||
if (!fwnode_call_int_op(dev->fwnode, add_links, dev))
|
int ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
|
||||||
|
if (!ret)
|
||||||
list_del_init(&dev->links.needs_suppliers);
|
list_del_init(&dev->links.needs_suppliers);
|
||||||
|
else if (ret != -ENODEV)
|
||||||
|
dev->links.need_for_probe = false;
|
||||||
|
}
|
||||||
mutex_unlock(&wfs_lock);
|
mutex_unlock(&wfs_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2341,6 +2345,31 @@ static int device_private_init(struct device *dev)
|
||||||
return 0;
|
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.
|
* device_add - add device to device hierarchy.
|
||||||
* @dev: device.
|
* @dev: device.
|
||||||
|
@ -2375,6 +2404,7 @@ int device_add(struct device *dev)
|
||||||
struct class_interface *class_intf;
|
struct class_interface *class_intf;
|
||||||
int error = -EINVAL, fw_ret;
|
int error = -EINVAL, fw_ret;
|
||||||
struct kobject *glue_dir = NULL;
|
struct kobject *glue_dir = NULL;
|
||||||
|
bool is_fwnode_dev = false;
|
||||||
|
|
||||||
dev = get_device(dev);
|
dev = get_device(dev);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
|
@ -2472,8 +2502,10 @@ int device_add(struct device *dev)
|
||||||
|
|
||||||
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
||||||
|
|
||||||
if (dev->fwnode && !dev->fwnode->dev)
|
if (dev->fwnode && !dev->fwnode->dev) {
|
||||||
dev->fwnode->dev = dev;
|
dev->fwnode->dev = dev;
|
||||||
|
is_fwnode_dev = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if any of the other devices (consumers) have been waiting for
|
* 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();
|
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);
|
fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
|
||||||
if (fw_ret == -ENODEV)
|
if (fw_ret == -ENODEV)
|
||||||
device_link_wait_for_mandatory_supplier(dev);
|
device_link_wait_for_mandatory_supplier(dev);
|
||||||
|
|
|
@ -231,8 +231,7 @@ static struct cpu_attr cpu_attrs[] = {
|
||||||
static ssize_t print_cpus_kernel_max(struct device *dev,
|
static ssize_t print_cpus_kernel_max(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
|
return sprintf(buf, "%d\n", NR_CPUS - 1);
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
|
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++] = ',';
|
buf[n++] = ',';
|
||||||
|
|
||||||
if (nr_cpu_ids == total_cpus-1)
|
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
|
else
|
||||||
n += snprintf(&buf[n], len - n, "%u-%d",
|
n += scnprintf(&buf[n], len - n, "%u-%d",
|
||||||
nr_cpu_ids, total_cpus-1);
|
nr_cpu_ids, total_cpus-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
n += snprintf(&buf[n], len - n, "\n");
|
n += scnprintf(&buf[n], len - n, "\n");
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
|
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,
|
static ssize_t print_cpus_isolated(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
int n = 0, len = PAGE_SIZE-2;
|
int n;
|
||||||
cpumask_var_t isolated;
|
cpumask_var_t isolated;
|
||||||
|
|
||||||
if (!alloc_cpumask_var(&isolated, GFP_KERNEL))
|
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,
|
cpumask_andnot(isolated, cpu_possible_mask,
|
||||||
housekeeping_cpumask(HK_FLAG_DOMAIN));
|
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);
|
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,
|
static ssize_t print_cpus_nohz_full(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
int n = 0, len = PAGE_SIZE-2;
|
return sprintf(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
|
||||||
|
|
||||||
n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
|
static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -224,76 +224,52 @@ static int deferred_devs_show(struct seq_file *s, void *data)
|
||||||
}
|
}
|
||||||
DEFINE_SHOW_ATTRIBUTE(deferred_devs);
|
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)
|
static int __init deferred_probe_timeout_setup(char *str)
|
||||||
{
|
{
|
||||||
int timeout;
|
int timeout;
|
||||||
|
|
||||||
if (!kstrtoint(str, 10, &timeout))
|
if (!kstrtoint(str, 10, &timeout))
|
||||||
deferred_probe_timeout = timeout;
|
driver_deferred_probe_timeout = timeout;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
__setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
|
__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
|
* driver_deferred_probe_check_state() - Check deferred probe state
|
||||||
* @dev: device to check
|
* @dev: device to check
|
||||||
*
|
*
|
||||||
* Returns -ENODEV if init is done and all built-in drivers have had a chance
|
* Return:
|
||||||
* to probe (i.e. initcalls are done), -ETIMEDOUT if deferred probe debug
|
* -ENODEV if initcalls have completed and modules are disabled.
|
||||||
* timeout has expired, or -EPROBE_DEFER if none of those conditions are met.
|
* -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
|
* Drivers or subsystems can opt-in to calling this function instead of directly
|
||||||
* returning -EPROBE_DEFER.
|
* returning -EPROBE_DEFER.
|
||||||
*/
|
*/
|
||||||
int driver_deferred_probe_check_state(struct device *dev)
|
int driver_deferred_probe_check_state(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) {
|
||||||
|
|
||||||
ret = __driver_deferred_probe_check_state(dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
dev_warn(dev, "ignoring dependency for device, assuming no driver");
|
dev_warn(dev, "ignoring dependency for device, assuming no driver");
|
||||||
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (!driver_deferred_probe_timeout) {
|
||||||
* driver_deferred_probe_check_state_continue() - check deferred probe state
|
dev_WARN(dev, "deferred probe timeout, ignoring dependency");
|
||||||
* @dev: device to check
|
return -ETIMEDOUT;
|
||||||
*
|
}
|
||||||
* 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;
|
|
||||||
|
|
||||||
ret = __driver_deferred_probe_check_state(dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
@ -302,7 +278,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct device_private *private, *p;
|
struct device_private *private, *p;
|
||||||
|
|
||||||
deferred_probe_timeout = 0;
|
driver_deferred_probe_timeout = 0;
|
||||||
driver_deferred_probe_trigger();
|
driver_deferred_probe_trigger();
|
||||||
flush_work(&deferred_probe_work);
|
flush_work(&deferred_probe_work);
|
||||||
|
|
||||||
|
@ -336,9 +312,9 @@ static int deferred_probe_initcall(void)
|
||||||
driver_deferred_probe_trigger();
|
driver_deferred_probe_trigger();
|
||||||
flush_work(&deferred_probe_work);
|
flush_work(&deferred_probe_work);
|
||||||
|
|
||||||
if (deferred_probe_timeout > 0) {
|
if (driver_deferred_probe_timeout > 0) {
|
||||||
schedule_delayed_work(&deferred_probe_timeout_work,
|
schedule_delayed_work(&deferred_probe_timeout_work,
|
||||||
deferred_probe_timeout * HZ);
|
driver_deferred_probe_timeout * HZ);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -668,9 +644,10 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv)
|
||||||
*/
|
*/
|
||||||
int driver_probe_done(void)
|
int driver_probe_done(void)
|
||||||
{
|
{
|
||||||
pr_debug("%s: probe_count = %d\n", __func__,
|
int local_probe_count = atomic_read(&probe_count);
|
||||||
atomic_read(&probe_count));
|
|
||||||
if (atomic_read(&probe_count))
|
pr_debug("%s: probe_count = %d\n", __func__, local_probe_count);
|
||||||
|
if (local_probe_count)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1222,7 +1199,7 @@ void driver_detach(struct device_driver *drv)
|
||||||
spin_unlock(&drv->p->klist_devices.k_lock);
|
spin_unlock(&drv->p->klist_devices.k_lock);
|
||||||
break;
|
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,
|
struct device_private,
|
||||||
knode_driver.n_node);
|
knode_driver.n_node);
|
||||||
dev = dev_prv->device;
|
dev = dev_prv->device;
|
||||||
|
|
|
@ -5,5 +5,6 @@ obj-$(CONFIG_FW_LOADER_USER_HELPER) += fallback_table.o
|
||||||
obj-$(CONFIG_FW_LOADER) += firmware_class.o
|
obj-$(CONFIG_FW_LOADER) += firmware_class.o
|
||||||
firmware_class-objs := main.o
|
firmware_class-objs := main.o
|
||||||
firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
|
firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
|
||||||
|
firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o
|
||||||
|
|
||||||
obj-y += builtin/
|
obj-y += builtin/
|
||||||
|
|
|
@ -525,7 +525,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = fw_sysfs_wait_timeout(fw_priv, timeout);
|
retval = fw_sysfs_wait_timeout(fw_priv, timeout);
|
||||||
if (retval < 0) {
|
if (retval < 0 && retval != -ENOENT) {
|
||||||
mutex_lock(&fw_lock);
|
mutex_lock(&fw_lock);
|
||||||
fw_load_abort(fw_sysfs);
|
fw_load_abort(fw_sysfs);
|
||||||
mutex_unlock(&fw_lock);
|
mutex_unlock(&fw_lock);
|
||||||
|
|
|
@ -66,4 +66,14 @@ static inline void unregister_sysfs_loader(void)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_FW_LOADER_USER_HELPER */
|
#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 */
|
#endif /* __FIRMWARE_FALLBACK_H */
|
||||||
|
|
36
drivers/base/firmware_loader/fallback_platform.c
Normal file
36
drivers/base/firmware_loader/fallback_platform.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -29,6 +29,9 @@
|
||||||
* firmware caching mechanism.
|
* firmware caching mechanism.
|
||||||
* @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes
|
* @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes
|
||||||
* precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER.
|
* 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 {
|
enum fw_opt {
|
||||||
FW_OPT_UEVENT = BIT(0),
|
FW_OPT_UEVENT = BIT(0),
|
||||||
|
@ -37,6 +40,7 @@ enum fw_opt {
|
||||||
FW_OPT_NO_WARN = BIT(3),
|
FW_OPT_NO_WARN = BIT(3),
|
||||||
FW_OPT_NOCACHE = BIT(4),
|
FW_OPT_NOCACHE = BIT(4),
|
||||||
FW_OPT_NOFALLBACK_SYSFS = BIT(5),
|
FW_OPT_NOFALLBACK_SYSFS = BIT(5),
|
||||||
|
FW_OPT_FALLBACK_PLATFORM = BIT(6),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum fw_status {
|
enum fw_status {
|
||||||
|
|
|
@ -493,8 +493,10 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
|
||||||
}
|
}
|
||||||
|
|
||||||
fw_priv->size = 0;
|
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) {
|
||||||
if (rc != -ENOENT)
|
if (rc != -ENOENT)
|
||||||
dev_warn(device, "loading %s failed with error %d\n",
|
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);
|
fw_decompress_xz);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (ret == -ENOENT)
|
||||||
|
ret = firmware_fallback_platform(fw->priv, opt_flags);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (!(opt_flags & FW_OPT_NO_WARN))
|
if (!(opt_flags & FW_OPT_NO_WARN))
|
||||||
dev_warn(device,
|
dev_warn(device,
|
||||||
|
@ -883,6 +888,30 @@ int request_firmware_direct(const struct firmware **firmware_p,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(request_firmware_direct);
|
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
|
* firmware_request_cache() - cache firmware for suspend so resume can use it
|
||||||
* @name: name of firmware file
|
* @name: name of firmware file
|
||||||
|
|
|
@ -239,6 +239,11 @@ config EFI_DISABLE_PCI_DMA
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
config EFI_EMBEDDED_FIRMWARE
|
||||||
|
bool
|
||||||
|
depends on EFI
|
||||||
|
select CRYPTO_LIB_SHA256
|
||||||
|
|
||||||
config UEFI_CPER
|
config UEFI_CPER
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ obj-$(CONFIG_EFI_TEST) += test/
|
||||||
obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
|
obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
|
||||||
obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o
|
obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o
|
||||||
obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.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-y += fake_mem.o
|
||||||
fake_map-$(CONFIG_X86) += x86_fake_mem.o
|
fake_map-$(CONFIG_X86) += x86_fake_mem.o
|
||||||
|
|
|
@ -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
|
* If this fails, retrying this function at a later point won't
|
||||||
* change anything. So, don't return an error after this.
|
* 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");
|
dev_warn(dev, "device_link_add() failed\n");
|
||||||
|
|
||||||
put_device(sup_dev);
|
put_device(sup_dev);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
@ -325,6 +326,59 @@ free_entry:
|
||||||
static inline int efivar_ssdt_load(void) { return 0; }
|
static inline int efivar_ssdt_load(void) { return 0; }
|
||||||
#endif
|
#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
|
* We register the efi subsystem with the firmware subsystem and the
|
||||||
* efivars subsystem with the efi subsystem, if the system was booted with
|
* 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;
|
goto err_remove_group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS))
|
||||||
|
efi_debugfs_init();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_remove_group:
|
err_remove_group:
|
||||||
|
|
150
drivers/firmware/efi/embedded-firmware.c
Normal file
150
drivers/firmware/efi/embedded-firmware.c
Normal file
|
@ -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);
|
|
@ -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
|
* we may need it at resume. Having loaded it once will make the
|
||||||
* firmware class code cache it at suspend/resume.
|
* 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) {
|
if (error) {
|
||||||
dev_err(dev, "Firmware request error %d\n", error);
|
dev_err(dev, "Firmware request error %d\n", error);
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -288,7 +288,7 @@ static int silead_ts_load_fw(struct i2c_client *client)
|
||||||
|
|
||||||
dev_dbg(dev, "Firmware file name: %s", data->fw_name);
|
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) {
|
if (error) {
|
||||||
dev_err(dev, "Firmware request error %d\n", error);
|
dev_err(dev, "Firmware request error %d\n", error);
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -1262,7 +1262,7 @@ static int of_link_property(struct device *dev, struct device_node *con_np,
|
||||||
u32 dl_flags;
|
u32 dl_flags;
|
||||||
|
|
||||||
if (dev->of_node == con_np)
|
if (dev->of_node == con_np)
|
||||||
dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
|
dl_flags = fw_devlink_get_flags();
|
||||||
else
|
else
|
||||||
dl_flags = DL_FLAG_SYNC_STATE_ONLY;
|
dl_flags = DL_FLAG_SYNC_STATE_ONLY;
|
||||||
|
|
||||||
|
@ -1299,15 +1299,9 @@ static int of_link_to_suppliers(struct device *dev,
|
||||||
return ret;
|
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,
|
static int of_fwnode_add_links(const struct fwnode_handle *fwnode,
|
||||||
struct device *dev)
|
struct device *dev)
|
||||||
{
|
{
|
||||||
if (!of_devlink)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (unlikely(!is_of_node(fwnode)))
|
if (unlikely(!is_of_node(fwnode)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -127,11 +127,12 @@ static int dt_to_map_one_config(struct pinctrl *p,
|
||||||
np_pctldev = of_get_next_parent(np_pctldev);
|
np_pctldev = of_get_next_parent(np_pctldev);
|
||||||
if (!np_pctldev || of_node_is_root(np_pctldev)) {
|
if (!np_pctldev || of_node_is_root(np_pctldev)) {
|
||||||
of_node_put(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 */
|
/* keep deferring if modules are enabled unless we've timed out */
|
||||||
if (IS_ENABLED(CONFIG_MODULES) && !allow_default)
|
if (IS_ENABLED(CONFIG_MODULES) && !allow_default &&
|
||||||
return driver_deferred_probe_check_state_continue(p->dev);
|
(ret == -ENODEV))
|
||||||
|
ret = -EPROBE_DEFER;
|
||||||
return driver_deferred_probe_check_state(p->dev);
|
return ret;
|
||||||
}
|
}
|
||||||
/* If we're creating a hog we can use the passed pctldev */
|
/* If we're creating a hog we can use the passed pctldev */
|
||||||
if (hog_pctldev && (np_pctldev == p->dev->of_node)) {
|
if (hog_pctldev && (np_pctldev == p->dev->of_node)) {
|
||||||
|
|
|
@ -1252,6 +1252,7 @@ config INTEL_TURBO_MAX_3
|
||||||
config TOUCHSCREEN_DMI
|
config TOUCHSCREEN_DMI
|
||||||
bool "DMI based touchscreen configuration info"
|
bool "DMI based touchscreen configuration info"
|
||||||
depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD
|
depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD
|
||||||
|
select EFI_EMBEDDED_FIRMWARE if EFI
|
||||||
---help---
|
---help---
|
||||||
Certain ACPI based tablets with e.g. Silead or Chipone touchscreens
|
Certain ACPI based tablets with e.g. Silead or Chipone touchscreens
|
||||||
do not have enough data in ACPI tables for the touchscreen driver to
|
do not have enough data in ACPI tables for the touchscreen driver to
|
||||||
|
|
|
@ -11,12 +11,15 @@
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/efi_embedded_fw.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
|
||||||
struct ts_dmi_data {
|
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 char *acpi_name;
|
||||||
const struct property_entry *properties;
|
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 = {
|
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",
|
.acpi_name = "MSSL1680:00",
|
||||||
.properties = chuwi_hi8_pro_props,
|
.properties = chuwi_hi8_pro_props,
|
||||||
};
|
};
|
||||||
|
@ -120,6 +132,18 @@ static const struct ts_dmi_data chuwi_vi8_data = {
|
||||||
.properties = chuwi_vi8_props,
|
.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[] = {
|
static const struct property_entry chuwi_vi10_props[] = {
|
||||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 0),
|
PROPERTY_ENTRY_U32("touchscreen-min-x", 0),
|
||||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 4),
|
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 = {
|
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",
|
.acpi_name = "MSSL1680:00",
|
||||||
.properties = cube_iwork8_air_props,
|
.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 = {
|
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",
|
.acpi_name = "MSSL1680:00",
|
||||||
.properties = onda_v80_plus_v3_props,
|
.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 = {
|
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",
|
.acpi_name = "MSSL1680:00",
|
||||||
.properties = pipo_w2s_props,
|
.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 */
|
/* 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 */
|
/* Chuwi Hi8 */
|
||||||
.driver_data = (void *)&chuwi_hi8_data,
|
.driver_data = (void *)&chuwi_hi8_data,
|
||||||
|
@ -703,6 +754,15 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||||
DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"),
|
DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/* 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) */
|
/* Chuwi Vi10 (CWI505) */
|
||||||
.driver_data = (void *)&chuwi_vi10_data,
|
.driver_data = (void *)&chuwi_vi10_data,
|
||||||
|
@ -1106,6 +1166,9 @@ static int __init ts_dmi_init(void)
|
||||||
return 0; /* Not an error */
|
return 0; /* Not an error */
|
||||||
|
|
||||||
ts_data = dmi_id->driver_data;
|
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);
|
error = bus_register_notifier(&i2c_bus_type, &ts_dmi_notifier);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
|
@ -5757,6 +5757,10 @@ static DECLARE_DELAYED_WORK(regulator_init_complete_work,
|
||||||
|
|
||||||
static int __init regulator_init_complete(void)
|
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
|
* Since DT doesn't provide an idiomatic mechanism for
|
||||||
* enabling full constraints and since it's much more natural
|
* 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;
|
has_full_constraints = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We punt completion for an arbitrary amount of time since
|
* If driver_deferred_probe_timeout is set, we punt
|
||||||
* systems like distros will load many drivers from userspace
|
* completion for that many seconds since systems like
|
||||||
* so consumers might not always be ready yet, this is
|
* distros will load many drivers from userspace so consumers
|
||||||
* particularly an issue with laptops where this might bounce
|
* might not always be ready yet, this is particularly an
|
||||||
* the display off then on. Ideally we'd get a notification
|
* issue with laptops where this might bounce the display off
|
||||||
* from userspace when this happens but we don't so just wait
|
* then on. Ideally we'd get a notification from userspace
|
||||||
* a bit and hope we waited long enough. It'd be better if
|
* when this happens but we don't so just wait a bit and hope
|
||||||
* we'd only do this on systems that need it, and a kernel
|
* we waited long enough. It'd be better if we'd only do
|
||||||
* command line option might be useful.
|
* this on systems that need it.
|
||||||
*/
|
*/
|
||||||
schedule_delayed_work(®ulator_init_complete_work,
|
schedule_delayed_work(®ulator_init_complete_work, delay * HZ);
|
||||||
msecs_to_jiffies(30000));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,8 +175,13 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
real_fops = fops_get(real_fops);
|
if (!fops_get(real_fops)) {
|
||||||
if (!real_fops) {
|
#ifdef MODULE
|
||||||
|
if (real_fops->owner &&
|
||||||
|
real_fops->owner->state == MODULE_STATE_GOING)
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Huh? Module did not clean up after itself at exit? */
|
/* Huh? Module did not clean up after itself at exit? */
|
||||||
WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
||||||
dentry);
|
dentry);
|
||||||
|
@ -305,8 +310,13 @@ static int full_proxy_open(struct inode *inode, struct file *filp)
|
||||||
if (r)
|
if (r)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
real_fops = fops_get(real_fops);
|
if (!fops_get(real_fops)) {
|
||||||
if (!real_fops) {
|
#ifdef MODULE
|
||||||
|
if (real_fops->owner &&
|
||||||
|
real_fops->owner->state == MODULE_STATE_GOING)
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Huh? Module did not cleanup after itself at exit? */
|
/* Huh? Module did not cleanup after itself at exit? */
|
||||||
WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
WARN(1, "debugfs file owner did not clean up at exit: %pd",
|
||||||
dentry);
|
dentry);
|
||||||
|
|
|
@ -501,17 +501,8 @@ EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe);
|
||||||
* wide range of flexibility in creating a file, or a directory (if you want
|
* wide range of flexibility in creating a file, or a directory (if you want
|
||||||
* to create a directory, the debugfs_create_dir() function is
|
* to create a directory, the debugfs_create_dir() function is
|
||||||
* recommended to be used instead.)
|
* recommended to be used instead.)
|
||||||
*
|
|
||||||
* This function will return a pointer to a dentry if it succeeds. This
|
|
||||||
* pointer must be passed to the debugfs_remove() function when the file is
|
|
||||||
* to be removed (no automatic cleanup happens if your module is unloaded,
|
|
||||||
* you are responsible here.) If an error occurs, ERR_PTR(-ERROR) will be
|
|
||||||
* returned.
|
|
||||||
*
|
|
||||||
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
|
|
||||||
* returned.
|
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
|
void debugfs_create_file_size(const char *name, umode_t mode,
|
||||||
struct dentry *parent, void *data,
|
struct dentry *parent, void *data,
|
||||||
const struct file_operations *fops,
|
const struct file_operations *fops,
|
||||||
loff_t file_size)
|
loff_t file_size)
|
||||||
|
@ -520,7 +511,6 @@ struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
|
||||||
|
|
||||||
if (de)
|
if (de)
|
||||||
d_inode(de)->i_size = file_size;
|
d_inode(de)->i_size = file_size;
|
||||||
return de;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(debugfs_create_file_size);
|
EXPORT_SYMBOL_GPL(debugfs_create_file_size);
|
||||||
|
|
||||||
|
|
26
fs/exec.c
26
fs/exec.c
|
@ -985,6 +985,32 @@ int kernel_read_file_from_path(const char *path, void **buf, loff_t *size,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
|
EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
|
||||||
|
|
||||||
|
int kernel_read_file_from_path_initns(const char *path, void **buf,
|
||||||
|
loff_t *size, loff_t max_size,
|
||||||
|
enum kernel_read_file_id id)
|
||||||
|
{
|
||||||
|
struct file *file;
|
||||||
|
struct path root;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!path || !*path)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
task_lock(&init_task);
|
||||||
|
get_fs_root(init_task.fs, &root);
|
||||||
|
task_unlock(&init_task);
|
||||||
|
|
||||||
|
file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0);
|
||||||
|
path_put(&root);
|
||||||
|
if (IS_ERR(file))
|
||||||
|
return PTR_ERR(file);
|
||||||
|
|
||||||
|
ret = kernel_read_file(file, buf, size, max_size, id);
|
||||||
|
fput(file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
|
||||||
|
|
||||||
int kernel_read_file_from_fd(int fd, void **buf, loff_t *size, loff_t max_size,
|
int kernel_read_file_from_fd(int fd, void **buf, loff_t *size, loff_t max_size,
|
||||||
enum kernel_read_file_id id)
|
enum kernel_read_file_id id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -891,7 +891,7 @@ int simple_attr_open(struct inode *inode, struct file *file,
|
||||||
{
|
{
|
||||||
struct simple_attr *attr;
|
struct simple_attr *attr;
|
||||||
|
|
||||||
attr = kmalloc(sizeof(*attr), GFP_KERNEL);
|
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
||||||
if (!attr)
|
if (!attr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -931,9 +931,11 @@ ssize_t simple_attr_read(struct file *file, char __user *buf,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (*ppos) { /* continued read */
|
if (*ppos && attr->get_buf[0]) {
|
||||||
|
/* continued read */
|
||||||
size = strlen(attr->get_buf);
|
size = strlen(attr->get_buf);
|
||||||
} else { /* first read */
|
} else {
|
||||||
|
/* first read */
|
||||||
u64 val;
|
u64 val;
|
||||||
ret = attr->get(attr->data, &val);
|
ret = attr->get(attr->data, &val);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -16,9 +16,7 @@ bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu);
|
||||||
|
|
||||||
DECLARE_PER_CPU(unsigned long, cpu_scale);
|
DECLARE_PER_CPU(unsigned long, cpu_scale);
|
||||||
|
|
||||||
struct sched_domain;
|
static inline unsigned long topology_get_cpu_scale(int cpu)
|
||||||
static inline
|
|
||||||
unsigned long topology_get_cpu_scale(int cpu)
|
|
||||||
{
|
{
|
||||||
return per_cpu(cpu_scale, cpu);
|
return per_cpu(cpu_scale, cpu);
|
||||||
}
|
}
|
||||||
|
@ -27,8 +25,7 @@ void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity);
|
||||||
|
|
||||||
DECLARE_PER_CPU(unsigned long, freq_scale);
|
DECLARE_PER_CPU(unsigned long, freq_scale);
|
||||||
|
|
||||||
static inline
|
static inline unsigned long topology_get_freq_scale(int cpu)
|
||||||
unsigned long topology_get_freq_scale(int cpu)
|
|
||||||
{
|
{
|
||||||
return per_cpu(freq_scale, cpu);
|
return per_cpu(freq_scale, cpu);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
|
||||||
struct dentry *parent, void *data,
|
struct dentry *parent, void *data,
|
||||||
const struct file_operations *fops);
|
const struct file_operations *fops);
|
||||||
|
|
||||||
struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
|
void debugfs_create_file_size(const char *name, umode_t mode,
|
||||||
struct dentry *parent, void *data,
|
struct dentry *parent, void *data,
|
||||||
const struct file_operations *fops,
|
const struct file_operations *fops,
|
||||||
loff_t file_size);
|
loff_t file_size);
|
||||||
|
@ -181,13 +181,11 @@ static inline struct dentry *debugfs_create_file_unsafe(const char *name,
|
||||||
return ERR_PTR(-ENODEV);
|
return ERR_PTR(-ENODEV);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
|
static inline void debugfs_create_file_size(const char *name, umode_t mode,
|
||||||
struct dentry *parent, void *data,
|
struct dentry *parent, void *data,
|
||||||
const struct file_operations *fops,
|
const struct file_operations *fops,
|
||||||
loff_t file_size)
|
loff_t file_size)
|
||||||
{
|
{ }
|
||||||
return ERR_PTR(-ENODEV);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct dentry *debugfs_create_dir(const char *name,
|
static inline struct dentry *debugfs_create_dir(const char *name,
|
||||||
struct dentry *parent)
|
struct dentry *parent)
|
||||||
|
|
|
@ -236,9 +236,9 @@ driver_find_device_by_acpi_dev(struct device_driver *drv, const void *adev)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern int driver_deferred_probe_timeout;
|
||||||
void driver_deferred_probe_add(struct device *dev);
|
void driver_deferred_probe_add(struct device *dev);
|
||||||
int driver_deferred_probe_check_state(struct device *dev);
|
int driver_deferred_probe_check_state(struct device *dev);
|
||||||
int driver_deferred_probe_check_state_continue(struct device *dev);
|
|
||||||
void driver_init(void);
|
void driver_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1124,6 +1124,7 @@ extern int __init efi_setup_pcdp_console(char *);
|
||||||
#define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */
|
#define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */
|
||||||
#define EFI_MEM_ATTR 10 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
|
#define EFI_MEM_ATTR 10 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
|
||||||
#define EFI_MEM_NO_SOFT_RESERVE 11 /* Is the kernel configured to ignore soft reservations? */
|
#define EFI_MEM_NO_SOFT_RESERVE 11 /* Is the kernel configured to ignore soft reservations? */
|
||||||
|
#define EFI_PRESERVE_BS_REGIONS 12 /* Are EFI boot-services memory segments available? */
|
||||||
|
|
||||||
#ifdef CONFIG_EFI
|
#ifdef CONFIG_EFI
|
||||||
/*
|
/*
|
||||||
|
@ -1553,6 +1554,12 @@ static inline void
|
||||||
efi_enable_reset_attack_mitigation(void) { }
|
efi_enable_reset_attack_mitigation(void) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
|
||||||
|
void efi_check_for_embedded_firmwares(void);
|
||||||
|
#else
|
||||||
|
static inline void efi_check_for_embedded_firmwares(void) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
efi_status_t efi_random_get_seed(void);
|
efi_status_t efi_random_get_seed(void);
|
||||||
|
|
||||||
void efi_retrieve_tpm2_eventlog(void);
|
void efi_retrieve_tpm2_eventlog(void);
|
||||||
|
|
43
include/linux/efi_embedded_fw.h
Normal file
43
include/linux/efi_embedded_fw.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _LINUX_EFI_EMBEDDED_FW_H
|
||||||
|
#define _LINUX_EFI_EMBEDDED_FW_H
|
||||||
|
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
|
||||||
|
#define EFI_EMBEDDED_FW_PREFIX_LEN 8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This struct and efi_embedded_fw_list are private to the efi-embedded fw
|
||||||
|
* implementation they are in this header for use by lib/test_firmware.c only!
|
||||||
|
*/
|
||||||
|
struct efi_embedded_fw {
|
||||||
|
struct list_head list;
|
||||||
|
const char *name;
|
||||||
|
const u8 *data;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct list_head efi_embedded_fw_list;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct efi_embedded_fw_desc - This struct is used by the EFI embedded-fw
|
||||||
|
* code to search for embedded firmwares.
|
||||||
|
*
|
||||||
|
* @name: Name to register the firmware with if found
|
||||||
|
* @prefix: First 8 bytes of the firmware
|
||||||
|
* @length: Length of the firmware in bytes including prefix
|
||||||
|
* @sha256: SHA256 of the firmware
|
||||||
|
*/
|
||||||
|
struct efi_embedded_fw_desc {
|
||||||
|
const char *name;
|
||||||
|
u8 prefix[EFI_EMBEDDED_FW_PREFIX_LEN];
|
||||||
|
u32 length;
|
||||||
|
u8 sha256[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct dmi_system_id touchscreen_dmi_table[];
|
||||||
|
|
||||||
|
int efi_get_embedded_fw(const char *name, const u8 **dat, size_t *sz);
|
||||||
|
|
||||||
|
#endif
|
|
@ -44,6 +44,8 @@ int request_firmware(const struct firmware **fw, const char *name,
|
||||||
struct device *device);
|
struct device *device);
|
||||||
int firmware_request_nowarn(const struct firmware **fw, const char *name,
|
int firmware_request_nowarn(const struct firmware **fw, const char *name,
|
||||||
struct device *device);
|
struct device *device);
|
||||||
|
int firmware_request_platform(const struct firmware **fw, const char *name,
|
||||||
|
struct device *device);
|
||||||
int request_firmware_nowait(
|
int request_firmware_nowait(
|
||||||
struct module *module, bool uevent,
|
struct module *module, bool uevent,
|
||||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||||
|
@ -69,6 +71,13 @@ static inline int firmware_request_nowarn(const struct firmware **fw,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int firmware_request_platform(const struct firmware **fw,
|
||||||
|
const char *name,
|
||||||
|
struct device *device)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int request_firmware_nowait(
|
static inline int request_firmware_nowait(
|
||||||
struct module *module, bool uevent,
|
struct module *module, bool uevent,
|
||||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||||
|
|
|
@ -2982,6 +2982,7 @@ extern int do_pipe_flags(int *, int);
|
||||||
id(UNKNOWN, unknown) \
|
id(UNKNOWN, unknown) \
|
||||||
id(FIRMWARE, firmware) \
|
id(FIRMWARE, firmware) \
|
||||||
id(FIRMWARE_PREALLOC_BUFFER, firmware) \
|
id(FIRMWARE_PREALLOC_BUFFER, firmware) \
|
||||||
|
id(FIRMWARE_EFI_EMBEDDED, firmware) \
|
||||||
id(MODULE, kernel-module) \
|
id(MODULE, kernel-module) \
|
||||||
id(KEXEC_IMAGE, kexec-image) \
|
id(KEXEC_IMAGE, kexec-image) \
|
||||||
id(KEXEC_INITRAMFS, kexec-initramfs) \
|
id(KEXEC_INITRAMFS, kexec-initramfs) \
|
||||||
|
@ -3012,6 +3013,8 @@ extern int kernel_read_file(struct file *, void **, loff_t *, loff_t,
|
||||||
enum kernel_read_file_id);
|
enum kernel_read_file_id);
|
||||||
extern int kernel_read_file_from_path(const char *, void **, loff_t *, loff_t,
|
extern int kernel_read_file_from_path(const char *, void **, loff_t *, loff_t,
|
||||||
enum kernel_read_file_id);
|
enum kernel_read_file_id);
|
||||||
|
extern int kernel_read_file_from_path_initns(const char *, void **, loff_t *, loff_t,
|
||||||
|
enum kernel_read_file_id);
|
||||||
extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t,
|
extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t,
|
||||||
enum kernel_read_file_id);
|
enum kernel_read_file_id);
|
||||||
extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *);
|
extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *);
|
||||||
|
|
|
@ -170,4 +170,6 @@ struct fwnode_operations {
|
||||||
} while (false)
|
} while (false)
|
||||||
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
|
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
|
||||||
|
|
||||||
|
extern u32 fw_devlink_get_flags(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -92,7 +92,7 @@ struct platform_device_info {
|
||||||
size_t size_data;
|
size_t size_data;
|
||||||
u64 dma_mask;
|
u64 dma_mask;
|
||||||
|
|
||||||
struct property_entry *properties;
|
const struct property_entry *properties;
|
||||||
};
|
};
|
||||||
extern struct platform_device *platform_device_register_full(
|
extern struct platform_device *platform_device_register_full(
|
||||||
const struct platform_device_info *pdevinfo);
|
const struct platform_device_info *pdevinfo);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/efi_embedded_fw.h>
|
||||||
|
|
||||||
#define TEST_FIRMWARE_NAME "test-firmware.bin"
|
#define TEST_FIRMWARE_NAME "test-firmware.bin"
|
||||||
#define TEST_FIRMWARE_NUM_REQS 4
|
#define TEST_FIRMWARE_NUM_REQS 4
|
||||||
|
@ -507,6 +508,57 @@ out:
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_WO(trigger_request);
|
static DEVICE_ATTR_WO(trigger_request);
|
||||||
|
|
||||||
|
#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
|
||||||
|
static ssize_t trigger_request_platform_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
static const u8 test_data[] = {
|
||||||
|
0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
|
||||||
|
0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
|
||||||
|
0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
|
||||||
|
};
|
||||||
|
struct efi_embedded_fw efi_embedded_fw;
|
||||||
|
const struct firmware *firmware = NULL;
|
||||||
|
char *name;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
name = kstrndup(buf, count, GFP_KERNEL);
|
||||||
|
if (!name)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
|
pr_info("inserting test platform fw '%s'\n", name);
|
||||||
|
efi_embedded_fw.name = name;
|
||||||
|
efi_embedded_fw.data = (void *)test_data;
|
||||||
|
efi_embedded_fw.length = sizeof(test_data);
|
||||||
|
list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
|
||||||
|
|
||||||
|
pr_info("loading '%s'\n", name);
|
||||||
|
rc = firmware_request_platform(&firmware, name, dev);
|
||||||
|
if (rc) {
|
||||||
|
pr_info("load of '%s' failed: %d\n", name, rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (firmware->size != sizeof(test_data) ||
|
||||||
|
memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
|
||||||
|
pr_info("firmware contents mismatch for '%s'\n", name);
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pr_info("loaded: %zu\n", firmware->size);
|
||||||
|
rc = count;
|
||||||
|
|
||||||
|
out:
|
||||||
|
release_firmware(firmware);
|
||||||
|
list_del(&efi_embedded_fw.list);
|
||||||
|
kfree(name);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_WO(trigger_request_platform);
|
||||||
|
#endif
|
||||||
|
|
||||||
static DECLARE_COMPLETION(async_fw_done);
|
static DECLARE_COMPLETION(async_fw_done);
|
||||||
|
|
||||||
static void trigger_async_request_cb(const struct firmware *fw, void *context)
|
static void trigger_async_request_cb(const struct firmware *fw, void *context)
|
||||||
|
@ -903,6 +955,9 @@ static struct attribute *test_dev_attrs[] = {
|
||||||
TEST_FW_DEV_ATTR(trigger_request),
|
TEST_FW_DEV_ATTR(trigger_request),
|
||||||
TEST_FW_DEV_ATTR(trigger_async_request),
|
TEST_FW_DEV_ATTR(trigger_async_request),
|
||||||
TEST_FW_DEV_ATTR(trigger_custom_fallback),
|
TEST_FW_DEV_ATTR(trigger_custom_fallback),
|
||||||
|
#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
|
||||||
|
TEST_FW_DEV_ATTR(trigger_request_platform),
|
||||||
|
#endif
|
||||||
|
|
||||||
/* These use the config and can use the test_result */
|
/* These use the config and can use the test_result */
|
||||||
TEST_FW_DEV_ATTR(trigger_batched_requests),
|
TEST_FW_DEV_ATTR(trigger_batched_requests),
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
# Makefile for firmware loading selftests
|
# Makefile for firmware loading selftests
|
||||||
|
CFLAGS = -Wall \
|
||||||
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
|
-O2
|
||||||
all:
|
|
||||||
|
|
||||||
TEST_PROGS := fw_run_tests.sh
|
TEST_PROGS := fw_run_tests.sh
|
||||||
TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_lib.sh
|
TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_lib.sh
|
||||||
|
TEST_GEN_FILES := fw_namespace
|
||||||
|
|
||||||
include ../lib.mk
|
include ../lib.mk
|
||||||
|
|
||||||
# Nothing to clean up.
|
|
||||||
clean:
|
|
||||||
|
|
|
@ -86,6 +86,29 @@ else
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Try platform (EFI embedded fw) loading too
|
||||||
|
if [ ! -e "$DIR"/trigger_request_platform ]; then
|
||||||
|
echo "$0: firmware loading: platform trigger not present, ignoring test" >&2
|
||||||
|
else
|
||||||
|
if printf '\000' >"$DIR"/trigger_request_platform 2> /dev/null; then
|
||||||
|
echo "$0: empty filename should not succeed (platform)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Note we echo a non-existing name, since files on the file-system
|
||||||
|
# are preferred over firmware embedded inside the platform's firmware
|
||||||
|
# The test adds a fake entry with the requested name to the platform's
|
||||||
|
# fw list, so the name does not matter as long as it does not exist
|
||||||
|
if ! echo -n "nope-$NAME" >"$DIR"/trigger_request_platform ; then
|
||||||
|
echo "$0: could not trigger request platform" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The test verifies itself that the loaded firmware contents matches
|
||||||
|
# the contents for the fake platform fw entry it added.
|
||||||
|
echo "$0: platform loading works"
|
||||||
|
fi
|
||||||
|
|
||||||
### Batched requests tests
|
### Batched requests tests
|
||||||
test_config_present()
|
test_config_present()
|
||||||
{
|
{
|
||||||
|
|
151
tools/testing/selftests/firmware/fw_namespace.c
Normal file
151
tools/testing/selftests/firmware/fw_namespace.c
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/* Test triggering of loading of firmware from different mount
|
||||||
|
* namespaces. Expect firmware to be always loaded from the mount
|
||||||
|
* namespace of PID 1. */
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef CLONE_NEWNS
|
||||||
|
# define CLONE_NEWNS 0x00020000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static char *fw_path = NULL;
|
||||||
|
|
||||||
|
static void die(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (fw_path)
|
||||||
|
unlink(fw_path);
|
||||||
|
umount("/lib/firmware");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trigger_fw(const char *fw_name, const char *sys_path)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open(sys_path, O_WRONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
die("open failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name))
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_fw(const char *fw_path)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
const char fw[] = "ABCD0123";
|
||||||
|
|
||||||
|
fd = open(fw_path, O_WRONLY | O_CREAT, 0600);
|
||||||
|
if (fd < 0)
|
||||||
|
die("open failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1)
|
||||||
|
die("write failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns)
|
||||||
|
{
|
||||||
|
pid_t child;
|
||||||
|
|
||||||
|
if (block_fw_in_parent_ns)
|
||||||
|
if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1)
|
||||||
|
die("blocking firmware in parent ns failed\n");
|
||||||
|
|
||||||
|
child = fork();
|
||||||
|
if (child == -1) {
|
||||||
|
die("fork failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
if (child != 0) { /* parent */
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
pid = waitpid(child, &status, 0);
|
||||||
|
if (pid == -1) {
|
||||||
|
die("waitpid failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
if (pid != child) {
|
||||||
|
die("waited for %d got %d\n",
|
||||||
|
child, pid);
|
||||||
|
}
|
||||||
|
if (!WIFEXITED(status)) {
|
||||||
|
die("child did not terminate cleanly\n");
|
||||||
|
}
|
||||||
|
if (block_fw_in_parent_ns)
|
||||||
|
umount("/lib/firmware");
|
||||||
|
return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unshare(CLONE_NEWNS) != 0) {
|
||||||
|
die("unshare(CLONE_NEWNS) failed: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1)
|
||||||
|
die("remount root in child ns failed\n");
|
||||||
|
|
||||||
|
if (!block_fw_in_parent_ns) {
|
||||||
|
if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1)
|
||||||
|
die("blocking firmware in child ns failed\n");
|
||||||
|
} else
|
||||||
|
umount("/lib/firmware");
|
||||||
|
|
||||||
|
trigger_fw(fw_name, sys_path);
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const char *fw_name = "test-firmware.bin";
|
||||||
|
char *sys_path;
|
||||||
|
if (argc != 2)
|
||||||
|
die("usage: %s sys_path\n", argv[0]);
|
||||||
|
|
||||||
|
/* Mount tmpfs to /lib/firmware so we don't have to assume
|
||||||
|
that it is writable for us.*/
|
||||||
|
if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1)
|
||||||
|
die("mounting tmpfs to /lib/firmware failed\n");
|
||||||
|
|
||||||
|
sys_path = argv[1];
|
||||||
|
asprintf(&fw_path, "/lib/firmware/%s", fw_name);
|
||||||
|
|
||||||
|
setup_fw(fw_path);
|
||||||
|
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
/* Positive case: firmware in PID1 mount namespace */
|
||||||
|
printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n");
|
||||||
|
if (!test_fw_in_ns(fw_name, sys_path, false))
|
||||||
|
die("error: failed to access firmware\n");
|
||||||
|
|
||||||
|
/* Negative case: firmware in child mount namespace, expected to fail */
|
||||||
|
printf("Testing with firmware in child namespace\n");
|
||||||
|
if (test_fw_in_ns(fw_name, sys_path, true))
|
||||||
|
die("error: firmware access did not fail\n");
|
||||||
|
|
||||||
|
unlink(fw_path);
|
||||||
|
free(fw_path);
|
||||||
|
umount("/lib/firmware");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
|
@ -61,6 +61,10 @@ run_test_config_0003()
|
||||||
check_mods
|
check_mods
|
||||||
check_setup
|
check_setup
|
||||||
|
|
||||||
|
echo "Running namespace test: "
|
||||||
|
$TEST_DIR/fw_namespace $DIR/trigger_request
|
||||||
|
echo "OK"
|
||||||
|
|
||||||
if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
|
if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
|
||||||
run_test_config_0001
|
run_test_config_0001
|
||||||
run_test_config_0002
|
run_test_config_0002
|
||||||
|
|
Loading…
Reference in a new issue