From e41746b0dd51f353630112558835aa62c1eb883e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 14 Dec 2016 15:33:39 +0000 Subject: debugfs: improve formatting of debugfs_real_fops() Type of debugfs_real_fops() is longer than parameters and the name, so there is no way to break the declaration nicely. We have to go over 80 characters. Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- include/linux/debugfs.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 014cc564d1c4..7574e86ff1eb 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -52,8 +52,7 @@ extern struct srcu_struct debugfs_srcu; * Must only be called under the protection established by * debugfs_use_file_start(). */ -static inline const struct file_operations * -debugfs_real_fops(const struct file *filp) +static inline const struct file_operations *debugfs_real_fops(const struct file *filp) __must_hold(&debugfs_srcu) { /* -- cgit v1.2.3-70-g09d2 From a0244a8df15d8caee8831872b6fc97b676cb15bd Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 15 Dec 2016 19:55:54 +0100 Subject: kref: prefer atomic_inc_not_zero to atomic_add_unless On most platforms, there exists this ifdef: #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) This makes this patch functionally useless. However, on PPC, there is actually an explicit definition of atomic_inc_not_zero with its own assembly that is slightly more optimized than atomic_add_unless. So, this patch changes kref to use atomic_inc_not_zero instead, for PPC and any future platforms that might provide an explicit implementation. This also puts this usage of kref more in line with a verbatim reading of the examples in Paul McKenney's paper [1] in the section titled "2.4 Atomic Counting With Check and Release Memory Barrier", which uses atomic_inc_not_zero. [1] http://open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2167.pdf Signed-off-by: Jason A. Donenfeld Reviewed-by: Thomas Hellstrom Reviewed-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- include/linux/kref.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/kref.h b/include/linux/kref.h index e15828fd71f1..62f0a84ae94e 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -133,6 +133,6 @@ static inline int kref_put_mutex(struct kref *kref, */ static inline int __must_check kref_get_unless_zero(struct kref *kref) { - return atomic_add_unless(&kref->refcount, 1, 0); + return atomic_inc_not_zero(&kref->refcount); } #endif /* _KREF_H_ */ -- cgit v1.2.3-70-g09d2 From 8a18f4284ec94ec56189e7f14495359d3b892a52 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 21 Dec 2016 17:24:55 +0800 Subject: platform: Print the resource range if device failed to claim Sometimes we have the following error message: platform MSFT0101:00: failed to claim resource 1 acpi MSFT0101:00: platform device creation failed: -16 But there is not enough information to figure out which resource range failed to claim. Thus print the resource range at first-place thus /proc/iomem or ioports should tell us who already claimed this resource, then the driver bug or incorrect resource assignment which is running into this conflict can be diagnosed: platform MSFT0101:00: failed to claim resource 1: [mem 0xfed40000-0xfed40fff] acpi MSFT0101:00: platform device creation failed: -16 Suggested-by: Len Brown Reported-by: Wendy Wang Signed-off-by: Chen Yu Reviewed-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c4af00385502..22a6430aadc3 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -396,7 +396,7 @@ int platform_device_add(struct platform_device *pdev) } if (p && insert_resource(p, r)) { - dev_err(&pdev->dev, "failed to claim resource %d\n", i); + dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r); ret = -EBUSY; goto failed; } -- cgit v1.2.3-70-g09d2 From f5786313f091a19eff934d1074642906c71c0c66 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 16 Dec 2016 03:10:34 -0800 Subject: selftests: firmware: only modprobe if driver is missing No need to load test_firmware if its already there. Also use a more generic form to recommend what is required to be built. Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/firmware/fw_filesystem.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index 5c495ad7958a..c8ccdaa78479 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -5,9 +5,24 @@ # know so we can be sure we're not accidentally testing the user helper. set -e -modprobe test_firmware - DIR=/sys/devices/virtual/misc/test_firmware +TEST_DIR=$(dirname $0) + +test_modprobe() +{ + if [ ! -d $DIR ]; then + echo "$0: $DIR not present" + echo "You must have the following enabled in your kernel:" + cat $TEST_DIR/config + exit 1 + fi +} + +trap "test_modprobe" EXIT + +if [ ! -d $DIR ]; then + modprobe test_firmware +fi # CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ # These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that -- cgit v1.2.3-70-g09d2 From 880444e214cfd293a2e8cc4bd3505f7ffa6ce33a Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 16 Dec 2016 03:10:35 -0800 Subject: selftests: firmware: send expected errors to /dev/null Error that we expect should not be spilled to stdout. Without this we get: ./fw_filesystem.sh: line 58: printf: write error: Invalid argument ./fw_filesystem.sh: line 63: printf: write error: No such device ./fw_filesystem.sh: line 69: echo: write error: No such file or directory ./fw_filesystem.sh: filesystem loading works ./fw_filesystem.sh: async filesystem loading works With it: ./fw_filesystem.sh: filesystem loading works ./fw_filesystem.sh: async filesystem loading works Signed-off-by: Luis R. Rodriguez Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/firmware/fw_filesystem.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index c8ccdaa78479..e35691239350 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -63,18 +63,18 @@ echo "ABCD0123" >"$FW" NAME=$(basename "$FW") -if printf '\000' >"$DIR"/trigger_request; then +if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then echo "$0: empty filename should not succeed" >&2 exit 1 fi -if printf '\000' >"$DIR"/trigger_async_request; then +if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then echo "$0: empty filename should not succeed (async)" >&2 exit 1 fi # Request a firmware that doesn't exist, it should fail. -if echo -n "nope-$NAME" >"$DIR"/trigger_request; then +if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then echo "$0: firmware shouldn't have loaded" >&2 exit 1 fi -- cgit v1.2.3-70-g09d2 From 113ccc38378b6f0b24c0993040c6044e35163a51 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 16 Dec 2016 03:10:36 -0800 Subject: 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 Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/firmware/built-in-fw.rst | 38 ++++ Documentation/driver-api/firmware/core.rst | 16 ++ .../driver-api/firmware/direct-fs-lookup.rst | 30 ++++ .../driver-api/firmware/fallback-mechanisms.rst | 195 +++++++++++++++++++++ .../driver-api/firmware/firmware_cache.rst | 51 ++++++ .../driver-api/firmware/fw_search_path.rst | 26 +++ Documentation/driver-api/firmware/index.rst | 16 ++ Documentation/driver-api/firmware/introduction.rst | 27 +++ Documentation/driver-api/firmware/lookup-order.rst | 18 ++ .../driver-api/firmware/request_firmware.rst | 56 ++++++ Documentation/driver-api/index.rst | 1 + Documentation/firmware_class/README | 128 -------------- 12 files changed, 474 insertions(+), 128 deletions(-) create mode 100644 Documentation/driver-api/firmware/built-in-fw.rst create mode 100644 Documentation/driver-api/firmware/core.rst create mode 100644 Documentation/driver-api/firmware/direct-fs-lookup.rst create mode 100644 Documentation/driver-api/firmware/fallback-mechanisms.rst create mode 100644 Documentation/driver-api/firmware/firmware_cache.rst create mode 100644 Documentation/driver-api/firmware/fw_search_path.rst create mode 100644 Documentation/driver-api/firmware/index.rst create mode 100644 Documentation/driver-api/firmware/introduction.rst create mode 100644 Documentation/driver-api/firmware/lookup-order.rst create mode 100644 Documentation/driver-api/firmware/request_firmware.rst delete mode 100644 Documentation/firmware_class/README diff --git a/Documentation/driver-api/firmware/built-in-fw.rst b/Documentation/driver-api/firmware/built-in-fw.rst new file mode 100644 index 000000000000..7300e66857f8 --- /dev/null +++ b/Documentation/driver-api/firmware/built-in-fw.rst @@ -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. + diff --git a/Documentation/driver-api/firmware/core.rst b/Documentation/driver-api/firmware/core.rst new file mode 100644 index 000000000000..1d1688cbc078 --- /dev/null +++ b/Documentation/driver-api/firmware/core.rst @@ -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 + diff --git a/Documentation/driver-api/firmware/direct-fs-lookup.rst b/Documentation/driver-api/firmware/direct-fs-lookup.rst new file mode 100644 index 000000000000..82b4d585a213 --- /dev/null +++ b/Documentation/driver-api/firmware/direct-fs-lookup.rst @@ -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. diff --git a/Documentation/driver-api/firmware/fallback-mechanisms.rst b/Documentation/driver-api/firmware/fallback-mechanisms.rst new file mode 100644 index 000000000000..d19354794e67 --- /dev/null +++ b/Documentation/driver-api/firmware/fallback-mechanisms.rst @@ -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. diff --git a/Documentation/driver-api/firmware/firmware_cache.rst b/Documentation/driver-api/firmware/firmware_cache.rst new file mode 100644 index 000000000000..2210e5bfb332 --- /dev/null +++ b/Documentation/driver-api/firmware/firmware_cache.rst @@ -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. + diff --git a/Documentation/driver-api/firmware/fw_search_path.rst b/Documentation/driver-api/firmware/fw_search_path.rst new file mode 100644 index 000000000000..a360f1009fa3 --- /dev/null +++ b/Documentation/driver-api/firmware/fw_search_path.rst @@ -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. diff --git a/Documentation/driver-api/firmware/index.rst b/Documentation/driver-api/firmware/index.rst new file mode 100644 index 000000000000..1abe01793031 --- /dev/null +++ b/Documentation/driver-api/firmware/index.rst @@ -0,0 +1,16 @@ +================== +Linux Firmware API +================== + +.. toctree:: + + introduction + core + request_firmware + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/driver-api/firmware/introduction.rst b/Documentation/driver-api/firmware/introduction.rst new file mode 100644 index 000000000000..211cb44eb972 --- /dev/null +++ b/Documentation/driver-api/firmware/introduction.rst @@ -0,0 +1,27 @@ +============ +Introduction +============ + +The firmware API enables kernel code to request files required +for functionality from userspace, the uses vary: + +* Microcode for CPU errata +* Device driver firmware, required to be loaded onto device + microcontrollers +* Device driver information data (calibration data, EEPROM overrides), + some of which can be completely optional. + +Types of firmware requests +========================== + +There are two types of calls: + +* Synchronous +* Asynchronous + +Which one you use vary depending on your requirements, the rule of thumb +however is you should strive to use the asynchronous APIs unless you also +are already using asynchronous initialization mechanisms which will not +stall or delay boot. Even if loading firmware does not take a lot of time +processing firmware might, and this can still delay boot or initialization, +as such mechanisms such as asynchronous probe can help supplement drivers. diff --git a/Documentation/driver-api/firmware/lookup-order.rst b/Documentation/driver-api/firmware/lookup-order.rst new file mode 100644 index 000000000000..88c81739683c --- /dev/null +++ b/Documentation/driver-api/firmware/lookup-order.rst @@ -0,0 +1,18 @@ +===================== +Firmware lookup order +===================== + +Different functionality is available to enable firmware to be found. +Below is chronological order of how firmware will be looked for once +a driver issues a firmware API call. + +* The ''Built-in firmware'' is checked first, if the firmware is present we + return it immediately +* The ''Firmware cache'' is looked at next. If the firmware is found we + return it immediately +* The ''Direct filesystem lookup'' is performed next, if found we + return it immediately +* If no firmware has been found and the fallback mechanism was enabled + the sysfs interface is created. After this either a kobject uevent + is issued or the custom firmware loading is relied upon for firmware + loading up to the timeout value. diff --git a/Documentation/driver-api/firmware/request_firmware.rst b/Documentation/driver-api/firmware/request_firmware.rst new file mode 100644 index 000000000000..cc0aea880824 --- /dev/null +++ b/Documentation/driver-api/firmware/request_firmware.rst @@ -0,0 +1,56 @@ +==================== +request_firmware API +==================== + +You would typically load firmware and then load it into your device somehow. +The typical firmware work flow is reflected below:: + + if(request_firmware(&fw_entry, $FIRMWARE, device) == 0) + copy_fw_to_device(fw_entry->data, fw_entry->size); + release_firmware(fw_entry); + +Synchronous firmware requests +============================= + +Synchronous firmware requests will wait until the firmware is found or until +an error is returned. + +request_firmware +---------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware + +request_firmware_direct +----------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware_direct + +request_firmware_into_buf +------------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware_into_buf + +Asynchronous firmware requests +============================== + +Asynchronous firmware requests allow driver code to not have to wait +until the firmware or an error is returned. Function callbacks are +provided so that when the firmware or an error is found the driver is +informed through the callback. request_firmware_nowait() cannot be called +in atomic contexts. + +request_firmware_nowait +----------------------- +.. kernel-doc:: drivers/base/firmware_class.c + :functions: request_firmware_nowait + +request firmware API expected driver use +======================================== + +Once an API call returns you process the firmware and then release the +firmware. For example if you used request_firmware() and it returns, +the driver has the firmware image accessible in fw_entry->{data,size}. +If something went wrong request_firmware() returns non-zero and fw_entry +is set to NULL. Once your driver is done with processing the firmware it +can call call release_firmware(fw_entry) to release the firmware image +and any related resource. diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 5475a2807e7a..d6f4ad1a872d 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -30,6 +30,7 @@ available subsections can be seen below. miscellaneous vme 80211/index + firmware/index .. only:: subproject and html diff --git a/Documentation/firmware_class/README b/Documentation/firmware_class/README deleted file mode 100644 index cafdca8b3b15..000000000000 --- a/Documentation/firmware_class/README +++ /dev/null @@ -1,128 +0,0 @@ - - request_firmware() hotplug interface: - ------------------------------------ - Copyright (C) 2003 Manuel Estrada Sainz - - Why: - --- - - Today, the most extended way to use firmware in the Linux kernel is linking - it statically in a header file. Which has political and technical issues: - - 1) Some firmware is not legal to redistribute. - 2) The firmware occupies memory permanently, even though it often is just - used once. - 3) Some people, like the Debian crowd, don't consider some firmware free - enough and remove entire drivers (e.g.: keyspan). - - High level behavior (mixed): - ============================ - - 1), kernel(driver): - - calls request_firmware(&fw_entry, $FIRMWARE, device) - - kernel searches the firmware image with name $FIRMWARE directly - in the below search path of root filesystem: - User customized search path by module parameter 'path'[1] - "/lib/firmware/updates/" UTS_RELEASE, - "/lib/firmware/updates", - "/lib/firmware/" UTS_RELEASE, - "/lib/firmware" - - If found, goto 7), else goto 2) - - [1], the 'path' is a string parameter which length should be less - than 256, user should pass 'firmware_class.path=$CUSTOMIZED_PATH' - if firmware_class is built in kernel(the general situation) - - 2), userspace: - - /sys/class/firmware/xxx/{loading,data} appear. - - hotplug gets called with a firmware identifier in $FIRMWARE - and the usual hotplug environment. - - hotplug: echo 1 > /sys/class/firmware/xxx/loading - - 3), kernel: Discard any previous partial load. - - 4), userspace: - - hotplug: cat appropriate_firmware_image > \ - /sys/class/firmware/xxx/data - - 5), kernel: grows a buffer in PAGE_SIZE increments to hold the image as it - comes in. - - 6), userspace: - - hotplug: echo 0 > /sys/class/firmware/xxx/loading - - 7), kernel: request_firmware() returns and the driver has the firmware - image in fw_entry->{data,size}. If something went wrong - request_firmware() returns non-zero and fw_entry is set to - NULL. - - 8), kernel(driver): Driver code calls release_firmware(fw_entry) releasing - the firmware image and any related resource. - - High level behavior (driver code): - ================================== - - if(request_firmware(&fw_entry, $FIRMWARE, device) == 0) - copy_fw_to_device(fw_entry->data, fw_entry->size); - release_firmware(fw_entry); - - Sample/simple hotplug script: - ============================ - - # Both $DEVPATH and $FIRMWARE are already provided in the environment. - - HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/ - - echo 1 > /sys/$DEVPATH/loading - cat $HOTPLUG_FW_DIR/$FIRMWARE > /sys/$DEVPATH/data - echo 0 > /sys/$DEVPATH/loading - - Random notes: - ============ - - - "echo -1 > /sys/class/firmware/xxx/loading" will cancel the load at - once and make request_firmware() return with error. - - - firmware_data_read() and firmware_loading_show() are just provided - for testing and completeness, they are not called in normal use. - - - There is also /sys/class/firmware/timeout which holds a timeout in - seconds for the whole load operation. - - - request_firmware_nowait() is also provided for convenience in - user contexts to request firmware asynchronously, but can't be called - in atomic contexts. - - - about in-kernel persistence: - --------------------------- - Under some circumstances, as explained below, it would be interesting to keep - firmware images in non-swappable kernel memory or even in the kernel image - (probably within initramfs). - - Note that this functionality has not been implemented. - - - Why OPTIONAL in-kernel persistence may be a good idea sometimes: - - - If the device that needs the firmware is needed to access the - filesystem. When upon some error the device has to be reset and the - firmware reloaded, it won't be possible to get it from userspace. - e.g.: - - A diskless client with a network card that needs firmware. - - The filesystem is stored in a disk behind an scsi device - that needs firmware. - - Replacing buggy DSDT/SSDT ACPI tables on boot. - Note: this would require the persistent objects to be included - within the kernel image, probably within initramfs. - - And the same device can be needed to access the filesystem or not depending - on the setup, so I think that the choice on what firmware to make - persistent should be left to userspace. - - about firmware cache: - -------------------- - After firmware cache mechanism is introduced during system sleep, - request_firmware can be called safely inside device's suspend and - resume callback, and callers needn't cache the firmware by - themselves any more for dealing with firmware loss during system - resume. -- cgit v1.2.3-70-g09d2 From 6d2c5d6c46dd3d9924831efd6c913fdf4d484985 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 16 Jan 2017 17:50:02 +0100 Subject: kmod: make usermodehelper path a const string This is in preparation for making it so that usermode helper programs can't be changed, if desired, by userspace. We will tackle the mess of cleaning up the write-ability of argv and env later, that's going to take more work, for much less gain... Signed-off-by: Greg Kroah-Hartman --- include/linux/kmod.h | 7 ++++--- kernel/kmod.c | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/linux/kmod.h b/include/linux/kmod.h index fcfd2bf14d3f..c4e441e00db5 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -56,7 +56,7 @@ struct file; struct subprocess_info { struct work_struct work; struct completion *complete; - char *path; + const char *path; char **argv; char **envp; int wait; @@ -67,10 +67,11 @@ struct subprocess_info { }; extern int -call_usermodehelper(char *path, char **argv, char **envp, int wait); +call_usermodehelper(const char *path, char **argv, char **envp, int wait); extern struct subprocess_info * -call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t gfp_mask, +call_usermodehelper_setup(const char *path, char **argv, char **envp, + gfp_t gfp_mask, int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *), void *data); diff --git a/kernel/kmod.c b/kernel/kmod.c index d45c96073afb..426a614e97fe 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -516,7 +516,7 @@ static void helper_unlock(void) * Function must be runnable in either a process context or the * context in which call_usermodehelper_exec is called. */ -struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, +struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv, char **envp, gfp_t gfp_mask, int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *info), @@ -613,7 +613,7 @@ EXPORT_SYMBOL(call_usermodehelper_exec); * This function is the equivalent to use call_usermodehelper_setup() and * call_usermodehelper_exec(). */ -int call_usermodehelper(char *path, char **argv, char **envp, int wait) +int call_usermodehelper(const char *path, char **argv, char **envp, int wait) { struct subprocess_info *info; gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; -- cgit v1.2.3-70-g09d2 From 377e7a27c049d6df9c1804454904e438ed12f1a4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 11 Dec 2016 18:00:43 +0100 Subject: Make static usermode helper binaries constant There are a number of usermode helper binaries that are "hard coded" in the kernel today, so mark them as "const" to make it harder for someone to change where the variables point to. Cc: Benjamin Herrenschmidt Cc: Thomas Sailer Cc: "Rafael J. Wysocki" Cc: Johan Hovold Cc: Alex Elder Cc: "J. Bruce Fields" Cc: Jeff Layton Cc: David Howells Signed-off-by: Greg Kroah-Hartman --- drivers/macintosh/windfarm_core.c | 4 ++-- drivers/net/hamradio/baycom_epp.c | 10 +++++++--- drivers/pnp/pnpbios/core.c | 5 +++-- drivers/staging/greybus/svc_watchdog.c | 4 ++-- drivers/staging/rtl8192e/rtl8192e/rtl_dm.c | 8 ++++---- fs/nfsd/nfs4layouts.c | 6 ++++-- security/keys/request_key.c | 7 ++++--- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 465d770ab0bb..5e013d781a74 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -74,8 +74,8 @@ static inline void wf_notify(int event, void *param) static int wf_critical_overtemp(void) { - static char * critical_overtemp_path = "/sbin/critical_overtemp"; - char *argv[] = { critical_overtemp_path, NULL }; + static char const critical_overtemp_path[] = "/sbin/critical_overtemp"; + char *argv[] = { (char *)critical_overtemp_path, NULL }; static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 7d054697b199..594fa1407e29 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -299,7 +299,7 @@ static inline void baycom_int_freq(struct baycom_state *bc) * eppconfig_path should be setable via /proc/sys. */ -static char eppconfig_path[256] = "/usr/sbin/eppfpga"; +static char const eppconfig_path[] = "/usr/sbin/eppfpga"; static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; @@ -308,8 +308,12 @@ static int eppconfig(struct baycom_state *bc) { char modearg[256]; char portarg[16]; - char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, - NULL }; + char *argv[] = { + (char *)eppconfig_path, + "-s", + "-p", portarg, + "-m", modearg, + NULL }; /* set up arguments */ sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat", diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index c38a5b9733c8..0ced908e7aa8 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -98,6 +98,7 @@ static struct completion unload_sem; */ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) { + static char const sbin_pnpbios[] = "/sbin/pnpbios"; char *argv[3], **envp, *buf, *scratch; int i = 0, value; @@ -112,7 +113,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) * integrated into the driver core and use the usual infrastructure * like sysfs and uevents */ - argv[0] = "/sbin/pnpbios"; + argv[0] = (char *)sbin_pnpbios; argv[1] = "dock"; argv[2] = NULL; @@ -139,7 +140,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) info->location_id, info->serial, info->capabilities); envp[i] = NULL; - value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC); + value = call_usermodehelper(sbin_pnpbios, argv, envp, UMH_WAIT_EXEC); kfree(buf); kfree(envp); return 0; diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c index 3729460fb954..12cef5c06e27 100644 --- a/drivers/staging/greybus/svc_watchdog.c +++ b/drivers/staging/greybus/svc_watchdog.c @@ -44,14 +44,14 @@ static int svc_watchdog_pm_notifier(struct notifier_block *notifier, static void greybus_reset(struct work_struct *work) { - static char start_path[256] = "/system/bin/start"; + static char const start_path[] = "/system/bin/start"; static char *envp[] = { "HOME=/", "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin", NULL, }; static char *argv[] = { - start_path, + (char *)start_path, "unipro_reset", NULL, }; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c index 9bc284812c30..dbb58fb16482 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c @@ -268,8 +268,8 @@ void rtl92e_dm_watchdog(struct net_device *dev) static void _rtl92e_dm_check_ac_dc_power(struct net_device *dev) { struct r8192_priv *priv = rtllib_priv(dev); - static char *ac_dc_script = "/etc/acpi/wireless-rtl-ac-dc-power.sh"; - char *argv[] = {ac_dc_script, DRV_NAME, NULL}; + static char const ac_dc_script[] = "/etc/acpi/wireless-rtl-ac-dc-power.sh"; + char *argv[] = {(char *)ac_dc_script, DRV_NAME, NULL}; static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", @@ -1823,7 +1823,7 @@ static void _rtl92e_dm_check_rf_ctrl_gpio(void *data) enum rt_rf_power_state eRfPowerStateToSet; bool bActuallySet = false; char *argv[3]; - static char *RadioPowerPath = "/etc/acpi/events/RadioPower.sh"; + static char const RadioPowerPath[] = "/etc/acpi/events/RadioPower.sh"; static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL}; @@ -1862,7 +1862,7 @@ static void _rtl92e_dm_check_rf_ctrl_gpio(void *data) else argv[1] = "RFON"; - argv[0] = RadioPowerPath; + argv[0] = (char *)RadioPowerPath; argv[2] = NULL; call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC); } diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index 596205d939a1..e06a4ae5f3ad 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -613,6 +613,7 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls) { struct nfs4_client *clp = ls->ls_stid.sc_client; char addr_str[INET6_ADDRSTRLEN]; + static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed"; static char *envp[] = { "HOME=/", "TERM=linux", @@ -628,12 +629,13 @@ nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls) "nfsd: client %s failed to respond to layout recall. " " Fencing..\n", addr_str); - argv[0] = "/sbin/nfsd-recall-failed"; + argv[0] = (char *)nfsd_recall_failed; argv[1] = addr_str; argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id; argv[3] = NULL; - error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); + error = call_usermodehelper(nfsd_recall_failed, argv, envp, + UMH_WAIT_PROC); if (error) { printk(KERN_ERR "nfsd: fence failed for client %s: %d!\n", addr_str, error); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 43affcf10b22..9822e500d50d 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -72,7 +72,7 @@ static void umh_keys_cleanup(struct subprocess_info *info) /* * Call a usermode helper with a specific session keyring. */ -static int call_usermodehelper_keys(char *path, char **argv, char **envp, +static int call_usermodehelper_keys(const char *path, char **argv, char **envp, struct key *session_keyring, int wait) { struct subprocess_info *info; @@ -95,6 +95,7 @@ static int call_sbin_request_key(struct key_construction *cons, const char *op, void *aux) { + static char const request_key[] = "/sbin/request-key"; const struct cred *cred = current_cred(); key_serial_t prkey, sskey; struct key *key = cons->key, *authkey = cons->authkey, *keyring, @@ -161,7 +162,7 @@ static int call_sbin_request_key(struct key_construction *cons, /* set up the argument list */ i = 0; - argv[i++] = "/sbin/request-key"; + argv[i++] = (char *)request_key; argv[i++] = (char *) op; argv[i++] = key_str; argv[i++] = uid_str; @@ -172,7 +173,7 @@ static int call_sbin_request_key(struct key_construction *cons, argv[i] = NULL; /* do it */ - ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, + ret = call_usermodehelper_keys(request_key, argv, envp, keyring, UMH_WAIT_PROC); kdebug("usermode -> 0x%x", ret); if (ret >= 0) { -- cgit v1.2.3-70-g09d2 From 64e90a8acb8590c2468c919f803652f081e3a4bf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 16 Jan 2017 16:22:39 +0100 Subject: Introduce STATIC_USERMODEHELPER to mediate call_usermodehelper() Some usermode helper applications are defined at kernel build time, while others can be changed at runtime. To provide a sane way to filter these, add a new kernel option "STATIC_USERMODEHELPER". This option routes all call_usermodehelper() calls through this binary, no matter what the caller wishes to have called. The new binary (by default set to /sbin/usermode-helper, but can be changed through the STATIC_USERMODEHELPER_PATH option) can properly filter the requested programs to be run by the kernel by looking at the first argument that is passed to it. All other options should then be passed onto the proper program if so desired. To disable all call_usermodehelper() calls by the kernel, set STATIC_USERMODEHELPER_PATH to an empty string. Thanks to Neil Brown for the idea of this feature. Cc: NeilBrown Signed-off-by: Greg Kroah-Hartman --- kernel/kmod.c | 14 ++++++++++++++ security/Kconfig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/kernel/kmod.c b/kernel/kmod.c index 426a614e97fe..0c407f905ca4 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -528,7 +528,12 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv, goto out; INIT_WORK(&sub_info->work, call_usermodehelper_exec_work); + +#ifdef CONFIG_STATIC_USERMODEHELPER + sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH; +#else sub_info->path = path; +#endif sub_info->argv = argv; sub_info->envp = envp; @@ -566,6 +571,15 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) retval = -EBUSY; goto out; } + + /* + * If there is no binary for us to call, then just return and get out of + * here. This allows us to set STATIC_USERMODEHELPER_PATH to "" and + * disable all call_usermodehelper() calls. + */ + if (strlen(sub_info->path) == 0) + goto out; + /* * Set the completion pointer only if there is a waiter. * This makes it possible to use umh_complete to free diff --git a/security/Kconfig b/security/Kconfig index 118f4549404e..d900f47eaa68 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -158,6 +158,41 @@ config HARDENED_USERCOPY_PAGESPAN been removed. This config is intended to be used only while trying to find such users. +config STATIC_USERMODEHELPER + bool "Force all usermode helper calls through a single binary" + help + By default, the kernel can call many different userspace + binary programs through the "usermode helper" kernel + interface. Some of these binaries are statically defined + either in the kernel code itself, or as a kernel configuration + option. However, some of these are dynamically created at + runtime, or can be modified after the kernel has started up. + To provide an additional layer of security, route all of these + calls through a single executable that can not have its name + changed. + + Note, it is up to this single binary to then call the relevant + "real" usermode helper binary, based on the first argument + passed to it. If desired, this program can filter and pick + and choose what real programs are called. + + If you wish for all usermode helper programs are to be + disabled, choose this option and then set + STATIC_USERMODEHELPER_PATH to an empty string. + +config STATIC_USERMODEHELPER_PATH + string "Path to the static usermode helper binary" + depends on STATIC_USERMODEHELPER + default "/sbin/usermode-helper" + help + The binary called by the kernel when any usermode helper + program is wish to be run. The "real" application's name will + be in the first argument passed to this program on the command + line. + + If you wish for all usermode helper programs to be disabled, + specify an empty string here (i.e. ""). + source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig -- cgit v1.2.3-70-g09d2 From 17627157cda13089d8a6c1c2d35acb07334b899c Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Wed, 8 Feb 2017 14:28:55 +0300 Subject: kernfs: handle null pointers while printing node name and path Null kernfs nodes could be found at cgroups during construction. It seems safer to handle these null pointers right in kernfs in the same way as printf prints "(null)" for null pointer string. Signed-off-by: Konstantin Khlebnikov Acked-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 10 ++++++++++ include/trace/events/cgroup.h | 20 ++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index cf4c636ff4da..439b946c4808 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -41,6 +41,9 @@ static bool kernfs_lockdep(struct kernfs_node *kn) static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen) { + if (!kn) + return strlcpy(buf, "(null)", buflen); + return strlcpy(buf, kn->parent ? kn->name : "/", buflen); } @@ -110,6 +113,8 @@ static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a, * kn_to: /n1/n2/n3 [depth=3] * result: /../.. * + * [3] when @kn_to is NULL result will be "(null)" + * * Returns the length of the full path. If the full length is equal to or * greater than @buflen, @buf contains the truncated path with the trailing * '\0'. On error, -errno is returned. @@ -123,6 +128,9 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to, size_t depth_from, depth_to, len = 0; int i, j; + if (!kn_to) + return strlcpy(buf, "(null)", buflen); + if (!kn_from) kn_from = kernfs_root(kn_to)->kn; @@ -166,6 +174,8 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to, * similar to strlcpy(). It returns the length of @kn's name and if @buf * isn't long enough, it's filled upto @buflen-1 and nul terminated. * + * Fills buffer with "(null)" if @kn is NULL. + * * This function can be called from any context. */ int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen) diff --git a/include/trace/events/cgroup.h b/include/trace/events/cgroup.h index ab68640a18d0..c226f50e88fa 100644 --- a/include/trace/events/cgroup.h +++ b/include/trace/events/cgroup.h @@ -61,19 +61,15 @@ DECLARE_EVENT_CLASS(cgroup, __field( int, id ) __field( int, level ) __dynamic_array(char, path, - cgrp->kn ? cgroup_path(cgrp, NULL, 0) + 1 - : strlen("(null)")) + cgroup_path(cgrp, NULL, 0) + 1) ), TP_fast_assign( __entry->root = cgrp->root->hierarchy_id; __entry->id = cgrp->id; __entry->level = cgrp->level; - if (cgrp->kn) - cgroup_path(cgrp, __get_dynamic_array(path), - __get_dynamic_array_len(path)); - else - __assign_str(path, "(null)"); + cgroup_path(cgrp, __get_dynamic_array(path), + __get_dynamic_array_len(path)); ), TP_printk("root=%d id=%d level=%d path=%s", @@ -119,8 +115,7 @@ DECLARE_EVENT_CLASS(cgroup_migrate, __field( int, dst_id ) __field( int, dst_level ) __dynamic_array(char, dst_path, - dst_cgrp->kn ? cgroup_path(dst_cgrp, NULL, 0) + 1 - : strlen("(null)")) + cgroup_path(dst_cgrp, NULL, 0) + 1) __field( int, pid ) __string( comm, task->comm ) ), @@ -129,11 +124,8 @@ DECLARE_EVENT_CLASS(cgroup_migrate, __entry->dst_root = dst_cgrp->root->hierarchy_id; __entry->dst_id = dst_cgrp->id; __entry->dst_level = dst_cgrp->level; - if (dst_cgrp->kn) - cgroup_path(dst_cgrp, __get_dynamic_array(dst_path), - __get_dynamic_array_len(dst_path)); - else - __assign_str(dst_path, "(null)"); + cgroup_path(dst_cgrp, __get_dynamic_array(dst_path), + __get_dynamic_array_len(dst_path)); __entry->pid = task->pid; __assign_str(comm, task->comm); ), -- cgit v1.2.3-70-g09d2