Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: Add new platform fallback mechanism and firmware_request_platform()

In some cases the platform's main firmware (e.g. the UEFI fw) may contain
an embedded copy of device firmware which needs to be (re)loaded into the
peripheral. Normally such firmware would be part of linux-firmware, but in
some cases this is not feasible, for 2 reasons:

1) The firmware is customized for a specific use-case of the chipset / use
with a specific hardware model, so we cannot have a single firmware file
for the chipset. E.g. touchscreen controller firmwares are compiled
specifically for the hardware model they are used with, as they are
calibrated for a specific model digitizer.

2) Despite repeated attempts we have failed to get permission to
redistribute the firmware. This is especially a problem with customized
firmwares, these get created by the chip vendor for a specific ODM and the
copyright may partially belong with the ODM, so the chip vendor cannot
give a blanket permission to distribute these.

This commit adds a new platform fallback mechanism to the firmware loader
which will try to lookup a device fw copy embedded in the platform's main
firmware if direct filesystem lookup fails.

Drivers which need such embedded fw copies can enable this fallback
mechanism by using the new firmware_request_platform() function.

Note that for now this is only supported on EFI platforms and even on
these platforms firmware_fallback_platform() only works if
CONFIG_EFI_EMBEDDED_FIRMWARE is enabled (this gets selected by drivers
which need this), in all other cases firmware_fallback_platform() simply
always returns -ENOENT.

Reported-by: Dave Olsthoorn <dave@bewaar.me>
Suggested-by: Peter Jones <pjones@redhat.com>
Acked-by: Luis Chamberlain <mcgrof@kernel.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20200115163554.101315-5-hdegoede@redhat.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Hans de Goede and committed by
Greg Kroah-Hartman
e4c2c0ff 4445eb6d

+198
+103
Documentation/driver-api/firmware/fallback-mechanisms.rst
··· 202 202 203 203 If you echo 0 into it means MAX_JIFFY_OFFSET will be used. The data type 204 204 for the timeout is an int. 205 + 206 + EFI embedded firmware fallback mechanism 207 + ======================================== 208 + 209 + On some devices the system's EFI code / ROM may contain an embedded copy 210 + of firmware for some of the system's integrated peripheral devices and 211 + the peripheral's Linux device-driver needs to access this firmware. 212 + 213 + Device drivers which need such firmware can use the 214 + firmware_request_platform() function for this, note that this is a 215 + separate fallback mechanism from the other fallback mechanisms and 216 + this does not use the sysfs interface. 217 + 218 + A device driver which needs this can describe the firmware it needs 219 + using an efi_embedded_fw_desc struct: 220 + 221 + .. kernel-doc:: include/linux/efi_embedded_fw.h 222 + :functions: efi_embedded_fw_desc 223 + 224 + The EFI embedded-fw code works by scanning all EFI_BOOT_SERVICES_CODE memory 225 + segments for an eight byte sequence matching prefix; if the prefix is found it 226 + then does a sha256 over length bytes and if that matches makes a copy of length 227 + bytes and adds that to its list with found firmwares. 228 + 229 + To avoid doing this somewhat expensive scan on all systems, dmi matching is 230 + used. Drivers are expected to export a dmi_system_id array, with each entries' 231 + driver_data pointing to an efi_embedded_fw_desc. 232 + 233 + To register this array with the efi-embedded-fw code, a driver needs to: 234 + 235 + 1. Always be builtin to the kernel or store the dmi_system_id array in a 236 + separate object file which always gets builtin. 237 + 238 + 2. Add an extern declaration for the dmi_system_id array to 239 + include/linux/efi_embedded_fw.h. 240 + 241 + 3. Add the dmi_system_id array to the embedded_fw_table in 242 + drivers/firmware/efi/embedded-firmware.c wrapped in a #ifdef testing that 243 + the driver is being builtin. 244 + 245 + 4. Add "select EFI_EMBEDDED_FIRMWARE if EFI_STUB" to its Kconfig entry. 246 + 247 + The firmware_request_platform() function will always first try to load firmware 248 + with the specified name directly from the disk, so the EFI embedded-fw can 249 + always be overridden by placing a file under /lib/firmware. 250 + 251 + Note that: 252 + 253 + 1. The code scanning for EFI embedded-firmware runs near the end 254 + of start_kernel(), just before calling rest_init(). For normal drivers and 255 + subsystems using subsys_initcall() to register themselves this does not 256 + matter. This means that code running earlier cannot use EFI 257 + embedded-firmware. 258 + 259 + 2. At the moment the EFI embedded-fw code assumes that firmwares always start at 260 + an offset which is a multiple of 8 bytes, if this is not true for your case 261 + send in a patch to fix this. 262 + 263 + 3. At the moment the EFI embedded-fw code only works on x86 because other archs 264 + free EFI_BOOT_SERVICES_CODE before the EFI embedded-fw code gets a chance to 265 + scan it. 266 + 267 + 4. The current brute-force scanning of EFI_BOOT_SERVICES_CODE is an ad-hoc 268 + brute-force solution. There has been discussion to use the UEFI Platform 269 + Initialization (PI) spec's Firmware Volume protocol. This has been rejected 270 + because the FV Protocol relies on *internal* interfaces of the PI spec, and: 271 + 1. The PI spec does not define peripheral firmware at all 272 + 2. The internal interfaces of the PI spec do not guarantee any backward 273 + compatibility. Any implementation details in FV may be subject to change, 274 + and may vary system to system. Supporting the FV Protocol would be 275 + difficult as it is purposely ambiguous. 276 + 277 + Example how to check for and extract embedded firmware 278 + ------------------------------------------------------ 279 + 280 + To check for, for example Silead touchscreen controller embedded firmware, 281 + do the following: 282 + 283 + 1. Boot the system with efi=debug on the kernel commandline 284 + 285 + 2. cp /sys/kernel/debug/efi/boot_services_code? to your home dir 286 + 287 + 3. Open the boot_services_code? files in a hex-editor, search for the 288 + magic prefix for Silead firmware: F0 00 00 00 02 00 00 00, this gives you 289 + the beginning address of the firmware inside the boot_services_code? file. 290 + 291 + 4. The firmware has a specific pattern, it starts with a 8 byte page-address, 292 + typically F0 00 00 00 02 00 00 00 for the first page followed by 32-bit 293 + word-address + 32-bit value pairs. With the word-address incrementing 4 294 + bytes (1 word) for each pair until a page is complete. A complete page is 295 + followed by a new page-address, followed by more word + value pairs. This 296 + leads to a very distinct pattern. Scroll down until this pattern stops, 297 + this gives you the end of the firmware inside the boot_services_code? file. 298 + 299 + 5. "dd if=boot_services_code? of=firmware bs=1 skip=<begin-addr> count=<len>" 300 + will extract the firmware for you. Inspect the firmware file in a 301 + hexeditor to make sure you got the dd parameters correct. 302 + 303 + 6. Copy it to /lib/firmware under the expected name to test it. 304 + 305 + 7. If the extracted firmware works, you can use the found info to fill an 306 + efi_embedded_fw_desc struct to describe it, run "sha256sum firmware" 307 + to get the sha256sum to put in the sha256 field.
+2
Documentation/driver-api/firmware/lookup-order.rst
··· 12 12 return it immediately 13 13 * The ''Direct filesystem lookup'' is performed next, if found we 14 14 return it immediately 15 + * The ''Platform firmware fallback'' is performed next, but only when 16 + firmware_request_platform() is used, if found we return it immediately 15 17 * If no firmware has been found and the fallback mechanism was enabled 16 18 the sysfs interface is created. After this either a kobject uevent 17 19 is issued or the custom firmware loading is relied upon for firmware
+5
Documentation/driver-api/firmware/request_firmware.rst
··· 25 25 .. kernel-doc:: drivers/base/firmware_loader/main.c 26 26 :functions: firmware_request_nowarn 27 27 28 + firmware_request_platform 29 + ------------------------- 30 + .. kernel-doc:: drivers/base/firmware_loader/main.c 31 + :functions: firmware_request_platform 32 + 28 33 request_firmware_direct 29 34 ----------------------- 30 35 .. kernel-doc:: drivers/base/firmware_loader/main.c
+1
drivers/base/firmware_loader/Makefile
··· 5 5 obj-$(CONFIG_FW_LOADER) += firmware_class.o 6 6 firmware_class-objs := main.o 7 7 firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o 8 + firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o 8 9 9 10 obj-y += builtin/
+10
drivers/base/firmware_loader/fallback.h
··· 66 66 } 67 67 #endif /* CONFIG_FW_LOADER_USER_HELPER */ 68 68 69 + #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 70 + int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags); 71 + #else 72 + static inline int firmware_fallback_platform(struct fw_priv *fw_priv, 73 + enum fw_opt opt_flags) 74 + { 75 + return -ENOENT; 76 + } 77 + #endif 78 + 69 79 #endif /* __FIRMWARE_FALLBACK_H */
+36
drivers/base/firmware_loader/fallback_platform.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/efi_embedded_fw.h> 4 + #include <linux/property.h> 5 + #include <linux/security.h> 6 + #include <linux/vmalloc.h> 7 + 8 + #include "fallback.h" 9 + #include "firmware.h" 10 + 11 + int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags) 12 + { 13 + const u8 *data; 14 + size_t size; 15 + int rc; 16 + 17 + if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM)) 18 + return -ENOENT; 19 + 20 + rc = security_kernel_load_data(LOADING_FIRMWARE_EFI_EMBEDDED); 21 + if (rc) 22 + return rc; 23 + 24 + rc = efi_get_embedded_fw(fw_priv->fw_name, &data, &size); 25 + if (rc) 26 + return rc; /* rc == -ENOENT when the fw was not found */ 27 + 28 + fw_priv->data = vmalloc(size); 29 + if (!fw_priv->data) 30 + return -ENOMEM; 31 + 32 + memcpy(fw_priv->data, data, size); 33 + fw_priv->size = size; 34 + fw_state_done(fw_priv); 35 + return 0; 36 + }
+4
drivers/base/firmware_loader/firmware.h
··· 29 29 * firmware caching mechanism. 30 30 * @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes 31 31 * precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER. 32 + * @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in 33 + * the platform's main firmware. If both this fallback and the sysfs 34 + * fallback are enabled, then this fallback will be tried first. 32 35 */ 33 36 enum fw_opt { 34 37 FW_OPT_UEVENT = BIT(0), ··· 40 37 FW_OPT_NO_WARN = BIT(3), 41 38 FW_OPT_NOCACHE = BIT(4), 42 39 FW_OPT_NOFALLBACK_SYSFS = BIT(5), 40 + FW_OPT_FALLBACK_PLATFORM = BIT(6), 43 41 }; 44 42 45 43 enum fw_status {
+27
drivers/base/firmware_loader/main.c
··· 778 778 fw_decompress_xz); 779 779 #endif 780 780 781 + if (ret == -ENOENT) 782 + ret = firmware_fallback_platform(fw->priv, opt_flags); 783 + 781 784 if (ret) { 782 785 if (!(opt_flags & FW_OPT_NO_WARN)) 783 786 dev_warn(device, ··· 887 884 return ret; 888 885 } 889 886 EXPORT_SYMBOL_GPL(request_firmware_direct); 887 + 888 + /** 889 + * firmware_request_platform() - request firmware with platform-fw fallback 890 + * @firmware: pointer to firmware image 891 + * @name: name of firmware file 892 + * @device: device for which firmware is being loaded 893 + * 894 + * This function is similar in behaviour to request_firmware, except that if 895 + * direct filesystem lookup fails, it will fallback to looking for a copy of the 896 + * requested firmware embedded in the platform's main (e.g. UEFI) firmware. 897 + **/ 898 + int firmware_request_platform(const struct firmware **firmware, 899 + const char *name, struct device *device) 900 + { 901 + int ret; 902 + 903 + /* Need to pin this module until return */ 904 + __module_get(THIS_MODULE); 905 + ret = _request_firmware(firmware, name, device, NULL, 0, 906 + FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); 907 + module_put(THIS_MODULE); 908 + return ret; 909 + } 910 + EXPORT_SYMBOL_GPL(firmware_request_platform); 890 911 891 912 /** 892 913 * firmware_request_cache() - cache firmware for suspend so resume can use it
+9
include/linux/firmware.h
··· 44 44 struct device *device); 45 45 int firmware_request_nowarn(const struct firmware **fw, const char *name, 46 46 struct device *device); 47 + int firmware_request_platform(const struct firmware **fw, const char *name, 48 + struct device *device); 47 49 int request_firmware_nowait( 48 50 struct module *module, bool uevent, 49 51 const char *name, struct device *device, gfp_t gfp, void *context, ··· 67 65 static inline int firmware_request_nowarn(const struct firmware **fw, 68 66 const char *name, 69 67 struct device *device) 68 + { 69 + return -EINVAL; 70 + } 71 + 72 + static inline int firmware_request_platform(const struct firmware **fw, 73 + const char *name, 74 + struct device *device) 70 75 { 71 76 return -EINVAL; 72 77 }
+1
include/linux/fs.h
··· 2982 2982 id(UNKNOWN, unknown) \ 2983 2983 id(FIRMWARE, firmware) \ 2984 2984 id(FIRMWARE_PREALLOC_BUFFER, firmware) \ 2985 + id(FIRMWARE_EFI_EMBEDDED, firmware) \ 2985 2986 id(MODULE, kernel-module) \ 2986 2987 id(KEXEC_IMAGE, kexec-image) \ 2987 2988 id(KEXEC_INITRAMFS, kexec-initramfs) \