forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
firmware: revamp firmware documentation
Understanding this code is getting out of control without any notes. Give the firmware_class driver a much needed documentation love, and while at it convert it to the new sphinx documentation format. v2: typos and small fixes Signed-off-by: Luis R. Rodriguez <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
- Loading branch information
Showing
12 changed files
with
474 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
================= | ||
Built-in firmware | ||
================= | ||
|
||
Firmware can be built-in to the kernel, this means building the firmware | ||
into vmlinux directly, to enable avoiding having to look for firmware from | ||
the filesystem. Instead, firmware can be looked for inside the kernel | ||
directly. You can enable built-in firmware using the kernel configuration | ||
options: | ||
|
||
* CONFIG_EXTRA_FIRMWARE | ||
* CONFIG_EXTRA_FIRMWARE_DIR | ||
|
||
This should not be confused with CONFIG_FIRMWARE_IN_KERNEL, this is for drivers | ||
which enables firmware to be built as part of the kernel build process. This | ||
option, CONFIG_FIRMWARE_IN_KERNEL, will build all firmware for all drivers | ||
enabled which ship its firmware inside the Linux kernel source tree. | ||
|
||
There are a few reasons why you might want to consider building your firmware | ||
into the kernel with CONFIG_EXTRA_FIRMWARE though: | ||
|
||
* Speed | ||
* Firmware is needed for accessing the boot device, and the user doesn't | ||
want to stuff the firmware into the boot initramfs. | ||
|
||
Even if you have these needs there are a few reasons why you may not be | ||
able to make use of built-in firmware: | ||
|
||
* Legalese - firmware is non-GPL compatible | ||
* Some firmware may be optional | ||
* Firmware upgrades are possible, therefore a new firmware would implicate | ||
a complete kernel rebuild. | ||
* Some firmware files may be really large in size. The remote-proc subsystem | ||
is an example subsystem which deals with these sorts of firmware | ||
* The firmware may need to be scraped out from some device specific location | ||
dynamically, an example is calibration data for for some WiFi chipsets. This | ||
calibration data can be unique per sold device. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
========================== | ||
Firmware API core features | ||
========================== | ||
|
||
The firmware API has a rich set of core features available. This section | ||
documents these features. | ||
|
||
.. toctree:: | ||
|
||
fw_search_path | ||
built-in-fw | ||
firmware_cache | ||
direct-fs-lookup | ||
fallback-mechanisms | ||
lookup-order | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
======================== | ||
Direct filesystem lookup | ||
======================== | ||
|
||
Direct filesystem lookup is the most common form of firmware lookup performed | ||
by the kernel. The kernel looks for the firmware directly on the root | ||
filesystem in the paths documented in the section 'Firmware search paths'. | ||
The filesystem lookup is implemented in fw_get_filesystem_firmware(), it | ||
uses common core kernel file loader facility kernel_read_file_from_path(). | ||
The max path allowed is PATH_MAX -- currently this is 4096 characters. | ||
|
||
It is recommended you keep /lib/firmware paths on your root filesystem, | ||
avoid having a separate partition for them in order to avoid possible | ||
races with lookups and avoid uses of the custom fallback mechanisms | ||
documented below. | ||
|
||
Firmware and initramfs | ||
---------------------- | ||
|
||
Drivers which are built-in to the kernel should have the firmware integrated | ||
also as part of the initramfs used to boot the kernel given that otherwise | ||
a race is possible with loading the driver and the real rootfs not yet being | ||
available. Stuffing the firmware into initramfs resolves this race issue, | ||
however note that using initrd does not suffice to address the same race. | ||
|
||
There are circumstances that justify not wanting to include firmware into | ||
initramfs, such as dealing with large firmware firmware files for the | ||
remote-proc subsystem. For such cases using a userspace fallback mechanism | ||
is currently the only viable solution as only userspace can know for sure | ||
when the real rootfs is ready and mounted. |
195 changes: 195 additions & 0 deletions
195
Documentation/driver-api/firmware/fallback-mechanisms.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
=================== | ||
Fallback mechanisms | ||
=================== | ||
|
||
A fallback mechanism is supported to allow to overcome failures to do a direct | ||
filesystem lookup on the root filesystem or when the firmware simply cannot be | ||
installed for practical reasons on the root filesystem. The kernel | ||
configuration options related to supporting the firmware fallback mechanism are: | ||
|
||
* CONFIG_FW_LOADER_USER_HELPER: enables building the firmware fallback | ||
mechanism. Most distributions enable this option today. If enabled but | ||
CONFIG_FW_LOADER_USER_HELPER_FALLBACK is disabled, only the custom fallback | ||
mechanism is available and for the request_firmware_nowait() call. | ||
* CONFIG_FW_LOADER_USER_HELPER_FALLBACK: force enables each request to | ||
enable the kobject uevent fallback mechanism on all firmware API calls | ||
except request_firmware_direct(). Most distributions disable this option | ||
today. The call request_firmware_nowait() allows for one alternative | ||
fallback mechanism: if this kconfig option is enabled and your second | ||
argument to request_firmware_nowait(), uevent, is set to false you are | ||
informing the kernel that you have a custom fallback mechanism and it will | ||
manually load the firmware. Read below for more details. | ||
|
||
Note that this means when having this configuration: | ||
|
||
CONFIG_FW_LOADER_USER_HELPER=y | ||
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=n | ||
|
||
the kobject uevent fallback mechanism will never take effect even | ||
for request_firmware_nowait() when uevent is set to true. | ||
|
||
Justifying the firmware fallback mechanism | ||
========================================== | ||
|
||
Direct filesystem lookups may fail for a variety of reasons. Known reasons for | ||
this are worth itemizing and documenting as it justifies the need for the | ||
fallback mechanism: | ||
|
||
* Race against access with the root filesystem upon bootup. | ||
|
||
* Races upon resume from suspend. This is resolved by the firmware cache, but | ||
the firmware cache is only supported if you use uevents, and its not | ||
supported for request_firmware_into_buf(). | ||
|
||
* Firmware is not accessible through typical means: | ||
* It cannot be installed into the root filesystem | ||
* The firmware provides very unique device specific data tailored for | ||
the unit gathered with local information. An example is calibration | ||
data for WiFi chipsets for mobile devices. This calibration data is | ||
not common to all units, but tailored per unit. Such information may | ||
be installed on a separate flash partition other than where the root | ||
filesystem is provided. | ||
|
||
Types of fallback mechanisms | ||
============================ | ||
|
||
There are really two fallback mechanisms available using one shared sysfs | ||
interface as a loading facility: | ||
|
||
* Kobject uevent fallback mechanism | ||
* Custom fallback mechanism | ||
|
||
First lets document the shared sysfs loading facility. | ||
|
||
Firmware sysfs loading facility | ||
=============================== | ||
|
||
In order to help device drivers upload firmware using a fallback mechanism | ||
the firmware infrastructure creates a sysfs interface to enable userspace | ||
to load and indicate when firmware is ready. The sysfs directory is created | ||
via fw_create_instance(). This call creates a new struct device named after | ||
the firmware requested, and establishes it in the device hierarchy by | ||
associating the device used to make the request as the device's parent. | ||
The sysfs directory's file attributes are defined and controlled through | ||
the new device's class (firmare_class) and group (fw_dev_attr_groups). | ||
This is actually where the original firmware_class.c file name comes from, | ||
as originally the only firmware loading mechanism available was the | ||
mechanism we now use as a fallback mechanism. | ||
|
||
To load firmware using the sysfs interface we expose a loading indicator, | ||
and a file upload firmware into: | ||
|
||
* /sys/$DEVPATH/loading | ||
* /sys/$DEVPATH/data | ||
|
||
To upload firmware you will echo 1 onto the loading file to indicate | ||
you are loading firmware. You then cat the firmware into the data file, | ||
and you notify the kernel the firmware is ready by echo'ing 0 onto | ||
the loading file. | ||
|
||
The firmware device used to help load firmware using sysfs is only created if | ||
direct firmware loading fails and if the fallback mechanism is enabled for your | ||
firmware request, this is set up with fw_load_from_user_helper(). It is | ||
important to re-iterate that no device is created if a direct filesystem lookup | ||
succeeded. | ||
|
||
Using:: | ||
|
||
echo 1 > /sys/$DEVPATH/loading | ||
|
||
Will clean any previous partial load at once and make the firmware API | ||
return an error. When loading firmware the firmware_class grows a buffer | ||
for the firmware in PAGE_SIZE increments to hold the image as it comes in. | ||
|
||
firmware_data_read() and firmware_loading_show() are just provided for the | ||
test_firmware driver for testing, they are not called in normal use or | ||
expected to be used regularly by userspace. | ||
|
||
Firmware kobject uevent fallback mechanism | ||
========================================== | ||
|
||
Since a device is created for the sysfs interface to help load firmware as a | ||
fallback mechanism userspace can be informed of the addition of the device by | ||
relying on kobject uevents. The addition of the device into the device | ||
hierarchy means the fallback mechanism for firmware loading has been initiated. | ||
For details of implementation refer to _request_firmware_load(), in particular | ||
on the use of dev_set_uevent_suppress() and kobject_uevent(). | ||
|
||
The kernel's kobject uevent mechanism is implemented in lib/kobject_uevent.c, | ||
it issues uevents to userspace. As a supplement to kobject uevents Linux | ||
distributions could also enable CONFIG_UEVENT_HELPER_PATH, which makes use of | ||
core kernel's usermode helper (UMH) functionality to call out to a userspace | ||
helper for kobject uevents. In practice though no standard distribution has | ||
ever used the CONFIG_UEVENT_HELPER_PATH. If CONFIG_UEVENT_HELPER_PATH is | ||
enabled this binary would be called each time kobject_uevent_env() gets called | ||
in the kernel for each kobject uevent triggered. | ||
|
||
Different implementations have been supported in userspace to take advantage of | ||
this fallback mechanism. When firmware loading was only possible using the | ||
sysfs mechanism the userspace component "hotplug" provided the functionality of | ||
monitoring for kobject events. Historically this was superseded be systemd's | ||
udev, however firmware loading support was removed from udev as of systemd | ||
commit be2ea723b1d0 ("udev: remove userspace firmware loading support") | ||
as of v217 on August, 2014. This means most Linux distributions today are | ||
not using or taking advantage of the firmware fallback mechanism provided | ||
by kobject uevents. This is specially exacerbated due to the fact that most | ||
distributions today disable CONFIG_FW_LOADER_USER_HELPER_FALLBACK. | ||
|
||
Refer to do_firmware_uevent() for details of the kobject event variables | ||
setup. Variables passwdd with a kobject add event: | ||
|
||
* FIRMWARE=firmware name | ||
* TIMEOUT=timeout value | ||
* ASYNC=whether or not the API request was asynchronous | ||
|
||
By default DEVPATH is set by the internal kernel kobject infrastructure. | ||
Below is an example simple kobject uevent script:: | ||
|
||
# Both $DEVPATH and $FIRMWARE are already provided in the environment. | ||
MY_FW_DIR=/lib/firmware/ | ||
echo 1 > /sys/$DEVPATH/loading | ||
cat $MY_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data | ||
echo 0 > /sys/$DEVPATH/loading | ||
|
||
Firmware custom fallback mechanism | ||
================================== | ||
|
||
Users of the request_firmware_nowait() call have yet another option available | ||
at their disposal: rely on the sysfs fallback mechanism but request that no | ||
kobject uevents be issued to userspace. The original logic behind this | ||
was that utilities other than udev might be required to lookup firmware | ||
in non-traditional paths -- paths outside of the listing documented in the | ||
section 'Direct filesystem lookup'. This option is not available to any of | ||
the other API calls as uevents are always forced for them. | ||
|
||
Since uevents are only meaningful if the fallback mechanism is enabled | ||
in your kernel it would seem odd to enable uevents with kernels that do not | ||
have the fallback mechanism enabled in their kernels. Unfortunately we also | ||
rely on the uevent flag which can be disabled by request_firmware_nowait() to | ||
also setup the firmware cache for firmware requests. As documented above, | ||
the firmware cache is only set up if uevent is enabled for an API call. | ||
Although this can disable the firmware cache for request_firmware_nowait() | ||
calls, users of this API should not use it for the purposes of disabling | ||
the cache as that was not the original purpose of the flag. Not setting | ||
the uevent flag means you want to opt-in for the firmware fallback mechanism | ||
but you want to suppress kobject uevents, as you have a custom solution which | ||
will monitor for your device addition into the device hierarchy somehow and | ||
load firmware for you through a custom path. | ||
|
||
Firmware fallback timeout | ||
========================= | ||
|
||
The firmware fallback mechanism has a timeout. If firmware is not loaded | ||
onto the sysfs interface by the timeout value an error is sent to the | ||
driver. By default the timeout is set to 60 seconds if uevents are | ||
desirable, otherwise MAX_JIFFY_OFFSET is used (max timeout possible). | ||
The logic behind using MAX_JIFFY_OFFSET for non-uevents is that a custom | ||
solution will have as much time as it needs to load firmware. | ||
|
||
You can customize the firmware timeout by echo'ing your desired timeout into | ||
the following file: | ||
|
||
* /sys/class/firmware/timeout | ||
|
||
If you echo 0 into it means MAX_JIFFY_OFFSET will be used. The data type | ||
for the timeout is an int. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
============== | ||
Firmware cache | ||
============== | ||
|
||
When Linux resumes from suspend some device drivers require firmware lookups to | ||
re-initialize devices. During resume there may be a period of time during which | ||
firmware lookups are not possible, during this short period of time firmware | ||
requests will fail. Time is of essence though, and delaying drivers to wait for | ||
the root filesystem for firmware delays user experience with device | ||
functionality. In order to support these requirements the firmware | ||
infrastructure implements a firmware cache for device drivers for most API | ||
calls, automatically behind the scenes. | ||
|
||
The firmware cache makes using certain firmware API calls safe during a device | ||
driver's suspend and resume callback. Users of these API calls needn't cache | ||
the firmware by themselves for dealing with firmware loss during system resume. | ||
|
||
The firmware cache works by requesting for firmware prior to suspend and | ||
caching it in memory. Upon resume device drivers using the firmware API will | ||
have access to the firmware immediately, without having to wait for the root | ||
filesystem to mount or dealing with possible race issues with lookups as the | ||
root filesystem mounts. | ||
|
||
Some implementation details about the firmware cache setup: | ||
|
||
* The firmware cache is setup by adding a devres entry for each device that | ||
uses all synchronous call except :c:func:`request_firmware_into_buf`. | ||
|
||
* If an asynchronous call is used the firmware cache is only set up for a | ||
device if if the second argument (uevent) to request_firmware_nowait() is | ||
true. When uevent is true it requests that a kobject uevent be sent to | ||
userspace for the firmware request. For details refer to the Fackback | ||
mechanism documented below. | ||
|
||
* If the firmware cache is determined to be needed as per the above two | ||
criteria the firmware cache is setup by adding a devres entry for the | ||
device making the firmware request. | ||
|
||
* The firmware devres entry is maintained throughout the lifetime of the | ||
device. This means that even if you release_firmware() the firmware cache | ||
will still be used on resume from suspend. | ||
|
||
* The timeout for the fallback mechanism is temporarily reduced to 10 seconds | ||
as the firmware cache is set up during suspend, the timeout is set back to | ||
the old value you had configured after the cache is set up. | ||
|
||
* Upon suspend any pending non-uevent firmware requests are killed to avoid | ||
stalling the kernel, this is done with kill_requests_without_uevent(). Kernel | ||
calls requiring the non-uevent therefore need to implement their own firmware | ||
cache mechanism but must not use the firmware API on suspend. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
===================== | ||
Firmware search paths | ||
===================== | ||
|
||
The following search paths are used to look for firmware on your | ||
root filesystem. | ||
|
||
* fw_path_para - module parameter - default is empty so this is ignored | ||
* /lib/firmware/updates/UTS_RELEASE/ | ||
* /lib/firmware/updates/ | ||
* /lib/firmware/UTS_RELEASE/ | ||
* /lib/firmware/ | ||
|
||
The module parameter ''path'' can be passed to the firmware_class module | ||
to activate the first optional custom fw_path_para. The custom path can | ||
only be up to 256 characters long. The kernel parameter passed would be: | ||
|
||
* 'firmware_class.path=$CUSTOMIZED_PATH' | ||
|
||
There is an alternative to customize the path at run time after bootup, you | ||
can use the file: | ||
|
||
* /sys/module/firmware_class/parameters/path | ||
|
||
You would echo into it your custom path and firmware requested will be | ||
searched for there first. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
================== | ||
Linux Firmware API | ||
================== | ||
|
||
.. toctree:: | ||
|
||
introduction | ||
core | ||
request_firmware | ||
|
||
.. only:: subproject and html | ||
|
||
Indices | ||
======= | ||
|
||
* :ref:`genindex` |
Oops, something went wrong.