162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/types.h>
462306a36Sopenharmony_ci#include <linux/kconfig.h>
562306a36Sopenharmony_ci#include <linux/list.h>
662306a36Sopenharmony_ci#include <linux/security.h>
762306a36Sopenharmony_ci#include <linux/umh.h>
862306a36Sopenharmony_ci#include <linux/sysctl.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "fallback.h"
1262306a36Sopenharmony_ci#include "firmware.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * firmware fallback mechanism
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * use small loading timeout for caching devices' firmware because all these
2062306a36Sopenharmony_ci * firmware images have been loaded successfully at lease once, also system is
2162306a36Sopenharmony_ci * ready for completing firmware loading now. The maximum size of firmware in
2262306a36Sopenharmony_ci * current distributions is about 2M bytes, so 10 secs should be enough.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_civoid fw_fallback_set_cache_timeout(void)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	fw_fallback_config.old_timeout = __firmware_loading_timeout();
2762306a36Sopenharmony_ci	__fw_fallback_set_timeout(10);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Restores the timeout to the value last configured during normal operation */
3162306a36Sopenharmony_civoid fw_fallback_set_default_timeout(void)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	__fw_fallback_set_timeout(fw_fallback_config.old_timeout);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic long firmware_loading_timeout(void)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return __firmware_loading_timeout() > 0 ?
3962306a36Sopenharmony_ci		__firmware_loading_timeout() * HZ : MAX_JIFFY_OFFSET;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv,  long timeout)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return __fw_state_wait_common(fw_priv, timeout);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic LIST_HEAD(pending_fw_head);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid kill_pending_fw_fallback_reqs(bool only_kill_custom)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct fw_priv *fw_priv;
5262306a36Sopenharmony_ci	struct fw_priv *next;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	mutex_lock(&fw_lock);
5562306a36Sopenharmony_ci	list_for_each_entry_safe(fw_priv, next, &pending_fw_head,
5662306a36Sopenharmony_ci				 pending_list) {
5762306a36Sopenharmony_ci		if (!fw_priv->need_uevent || !only_kill_custom)
5862306a36Sopenharmony_ci			 __fw_load_abort(fw_priv);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**
6462306a36Sopenharmony_ci * fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism
6562306a36Sopenharmony_ci * @fw_sysfs: firmware sysfs information for the firmware to load
6662306a36Sopenharmony_ci * @timeout: timeout to wait for the load
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * In charge of constructing a sysfs fallback interface for firmware loading.
6962306a36Sopenharmony_ci **/
7062306a36Sopenharmony_cistatic int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	int retval = 0;
7362306a36Sopenharmony_ci	struct device *f_dev = &fw_sysfs->dev;
7462306a36Sopenharmony_ci	struct fw_priv *fw_priv = fw_sysfs->fw_priv;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* fall back on userspace loading */
7762306a36Sopenharmony_ci	if (!fw_priv->data)
7862306a36Sopenharmony_ci		fw_priv->is_paged_buf = true;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	dev_set_uevent_suppress(f_dev, true);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	retval = device_add(f_dev);
8362306a36Sopenharmony_ci	if (retval) {
8462306a36Sopenharmony_ci		dev_err(f_dev, "%s: device_register failed\n", __func__);
8562306a36Sopenharmony_ci		goto err_put_dev;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	mutex_lock(&fw_lock);
8962306a36Sopenharmony_ci	if (fw_state_is_aborted(fw_priv)) {
9062306a36Sopenharmony_ci		mutex_unlock(&fw_lock);
9162306a36Sopenharmony_ci		retval = -EINTR;
9262306a36Sopenharmony_ci		goto out;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci	list_add(&fw_priv->pending_list, &pending_fw_head);
9562306a36Sopenharmony_ci	mutex_unlock(&fw_lock);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (fw_priv->opt_flags & FW_OPT_UEVENT) {
9862306a36Sopenharmony_ci		fw_priv->need_uevent = true;
9962306a36Sopenharmony_ci		dev_set_uevent_suppress(f_dev, false);
10062306a36Sopenharmony_ci		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
10162306a36Sopenharmony_ci		kobject_uevent(&fw_sysfs->dev.kobj, KOBJ_ADD);
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		timeout = MAX_JIFFY_OFFSET;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	retval = fw_sysfs_wait_timeout(fw_priv, timeout);
10762306a36Sopenharmony_ci	if (retval < 0 && retval != -ENOENT) {
10862306a36Sopenharmony_ci		mutex_lock(&fw_lock);
10962306a36Sopenharmony_ci		fw_load_abort(fw_sysfs);
11062306a36Sopenharmony_ci		mutex_unlock(&fw_lock);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (fw_state_is_aborted(fw_priv)) {
11462306a36Sopenharmony_ci		if (retval == -ERESTARTSYS)
11562306a36Sopenharmony_ci			retval = -EINTR;
11662306a36Sopenharmony_ci	} else if (fw_priv->is_paged_buf && !fw_priv->data)
11762306a36Sopenharmony_ci		retval = -ENOMEM;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciout:
12062306a36Sopenharmony_ci	device_del(f_dev);
12162306a36Sopenharmony_cierr_put_dev:
12262306a36Sopenharmony_ci	put_device(f_dev);
12362306a36Sopenharmony_ci	return retval;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int fw_load_from_user_helper(struct firmware *firmware,
12762306a36Sopenharmony_ci				    const char *name, struct device *device,
12862306a36Sopenharmony_ci				    u32 opt_flags)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct fw_sysfs *fw_sysfs;
13162306a36Sopenharmony_ci	long timeout;
13262306a36Sopenharmony_ci	int ret;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	timeout = firmware_loading_timeout();
13562306a36Sopenharmony_ci	if (opt_flags & FW_OPT_NOWAIT) {
13662306a36Sopenharmony_ci		timeout = usermodehelper_read_lock_wait(timeout);
13762306a36Sopenharmony_ci		if (!timeout) {
13862306a36Sopenharmony_ci			dev_dbg(device, "firmware: %s loading timed out\n",
13962306a36Sopenharmony_ci				name);
14062306a36Sopenharmony_ci			return -EBUSY;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci	} else {
14362306a36Sopenharmony_ci		ret = usermodehelper_read_trylock();
14462306a36Sopenharmony_ci		if (WARN_ON(ret)) {
14562306a36Sopenharmony_ci			dev_err(device, "firmware: %s will not be loaded\n",
14662306a36Sopenharmony_ci				name);
14762306a36Sopenharmony_ci			return ret;
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	fw_sysfs = fw_create_instance(firmware, name, device, opt_flags);
15262306a36Sopenharmony_ci	if (IS_ERR(fw_sysfs)) {
15362306a36Sopenharmony_ci		ret = PTR_ERR(fw_sysfs);
15462306a36Sopenharmony_ci		goto out_unlock;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	fw_sysfs->fw_priv = firmware->priv;
15862306a36Sopenharmony_ci	ret = fw_load_sysfs_fallback(fw_sysfs, timeout);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (!ret)
16162306a36Sopenharmony_ci		ret = assign_fw(firmware, device);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciout_unlock:
16462306a36Sopenharmony_ci	usermodehelper_read_unlock();
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return ret;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic bool fw_force_sysfs_fallback(u32 opt_flags)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	if (fw_fallback_config.force_sysfs_fallback)
17262306a36Sopenharmony_ci		return true;
17362306a36Sopenharmony_ci	if (!(opt_flags & FW_OPT_USERHELPER))
17462306a36Sopenharmony_ci		return false;
17562306a36Sopenharmony_ci	return true;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic bool fw_run_sysfs_fallback(u32 opt_flags)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (fw_fallback_config.ignore_sysfs_fallback) {
18362306a36Sopenharmony_ci		pr_info_once("Ignoring firmware sysfs fallback due to sysctl knob\n");
18462306a36Sopenharmony_ci		return false;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if ((opt_flags & FW_OPT_NOFALLBACK_SYSFS))
18862306a36Sopenharmony_ci		return false;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* Also permit LSMs and IMA to fail firmware sysfs fallback */
19162306a36Sopenharmony_ci	ret = security_kernel_load_data(LOADING_FIRMWARE, true);
19262306a36Sopenharmony_ci	if (ret < 0)
19362306a36Sopenharmony_ci		return false;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return fw_force_sysfs_fallback(opt_flags);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/**
19962306a36Sopenharmony_ci * firmware_fallback_sysfs() - use the fallback mechanism to find firmware
20062306a36Sopenharmony_ci * @fw: pointer to firmware image
20162306a36Sopenharmony_ci * @name: name of firmware file to look for
20262306a36Sopenharmony_ci * @device: device for which firmware is being loaded
20362306a36Sopenharmony_ci * @opt_flags: options to control firmware loading behaviour, as defined by
20462306a36Sopenharmony_ci *	       &enum fw_opt
20562306a36Sopenharmony_ci * @ret: return value from direct lookup which triggered the fallback mechanism
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * This function is called if direct lookup for the firmware failed, it enables
20862306a36Sopenharmony_ci * a fallback mechanism through userspace by exposing a sysfs loading
20962306a36Sopenharmony_ci * interface. Userspace is in charge of loading the firmware through the sysfs
21062306a36Sopenharmony_ci * loading interface. This sysfs fallback mechanism may be disabled completely
21162306a36Sopenharmony_ci * on a system by setting the proc sysctl value ignore_sysfs_fallback to true.
21262306a36Sopenharmony_ci * If this is false we check if the internal API caller set the
21362306a36Sopenharmony_ci * @FW_OPT_NOFALLBACK_SYSFS flag, if so it would also disable the fallback
21462306a36Sopenharmony_ci * mechanism. A system may want to enforce the sysfs fallback mechanism at all
21562306a36Sopenharmony_ci * times, it can do this by setting ignore_sysfs_fallback to false and
21662306a36Sopenharmony_ci * force_sysfs_fallback to true.
21762306a36Sopenharmony_ci * Enabling force_sysfs_fallback is functionally equivalent to build a kernel
21862306a36Sopenharmony_ci * with CONFIG_FW_LOADER_USER_HELPER_FALLBACK.
21962306a36Sopenharmony_ci **/
22062306a36Sopenharmony_ciint firmware_fallback_sysfs(struct firmware *fw, const char *name,
22162306a36Sopenharmony_ci			    struct device *device,
22262306a36Sopenharmony_ci			    u32 opt_flags,
22362306a36Sopenharmony_ci			    int ret)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	if (!fw_run_sysfs_fallback(opt_flags))
22662306a36Sopenharmony_ci		return ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!(opt_flags & FW_OPT_NO_WARN))
22962306a36Sopenharmony_ci		dev_warn(device, "Falling back to sysfs fallback for: %s\n",
23062306a36Sopenharmony_ci				 name);
23162306a36Sopenharmony_ci	else
23262306a36Sopenharmony_ci		dev_dbg(device, "Falling back to sysfs fallback for: %s\n",
23362306a36Sopenharmony_ci				name);
23462306a36Sopenharmony_ci	return fw_load_from_user_helper(fw, name, device, opt_flags);
23562306a36Sopenharmony_ci}
236