summaryrefslogtreecommitdiff
path: root/drivers/base/firmware_loader/fallback.c
blob: 3ef0b312ae71905e924ae929ef3d64f0705e9b23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// SPDX-License-Identifier: GPL-2.0

#include <linux/types.h>
#include <linux/kconfig.h>
#include <linux/list.h>
#include <linux/security.h>
#include <linux/umh.h>
#include <linux/sysctl.h>
#include <linux/module.h>

#include "fallback.h"
#include "firmware.h"

/*
 * firmware fallback mechanism
 */

/*
 * use small loading timeout for caching devices' firmware because all these
 * firmware images have been loaded successfully at lease once, also system is
 * ready for completing firmware loading now. The maximum size of firmware in
 * current distributions is about 2M bytes, so 10 secs should be enough.
 */
void fw_fallback_set_cache_timeout(void)
{
	fw_fallback_config.old_timeout = __firmware_loading_timeout();
	__fw_fallback_set_timeout(10);
}

/* Restores the timeout to the value last configured during normal operation */
void fw_fallback_set_default_timeout(void)
{
	__fw_fallback_set_timeout(fw_fallback_config.old_timeout);
}

static long firmware_loading_timeout(void)
{
	return __firmware_loading_timeout() > 0 ?
		__firmware_loading_timeout() * HZ : MAX_JIFFY_OFFSET;
}

static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv,  long timeout)
{
	return __fw_state_wait_common(fw_priv, timeout);
}

static LIST_HEAD(pending_fw_head);

void kill_pending_fw_fallback_reqs(bool kill_all)
{
	struct fw_priv *fw_priv;
	struct fw_priv *next;

	mutex_lock(&fw_lock);
	list_for_each_entry_safe(fw_priv, next, &pending_fw_head,
				 pending_list) {
		if (kill_all || !fw_priv->need_uevent)
			 __fw_load_abort(fw_priv);
	}

	if (kill_all)
		fw_load_abort_all = true;

	mutex_unlock(&fw_lock);
}

/**
 * fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism
 * @fw_sysfs: firmware sysfs information for the firmware to load
 * @timeout: timeout to wait for the load
 *
 * In charge of constructing a sysfs fallback interface for firmware loading.
 **/
static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
{
	int retval = 0;
	struct device *f_dev = &fw_sysfs->dev;
	struct fw_priv *fw_priv = fw_sysfs->fw_priv;

	/* fall back on userspace loading */
	if (!fw_priv->data)
		fw_priv->is_paged_buf = true;

	dev_set_uevent_suppress(f_dev, true);

	retval = device_add(f_dev);
	if (retval) {
		dev_err(f_dev, "%s: device_register failed\n", __func__);
		goto err_put_dev;
	}

	mutex_lock(&fw_lock);
	if (fw_load_abort_all || fw_state_is_aborted(fw_priv)) {
		mutex_unlock(&fw_lock);
		retval = -EINTR;
		goto out;
	}
	list_add(&fw_priv->pending_list, &pending_fw_head);
	mutex_unlock(&fw_lock);

	if (fw_priv->opt_flags & FW_OPT_UEVENT) {
		fw_priv->need_uevent = true;
		dev_set_uevent_suppress(f_dev, false);
		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
		kobject_uevent(&fw_sysfs->dev.kobj, KOBJ_ADD);
	} else {
		timeout = MAX_JIFFY_OFFSET;
	}

	retval = fw_sysfs_wait_timeout(fw_priv, timeout);
	if (retval < 0 && retval != -ENOENT) {
		mutex_lock(&fw_lock);
		fw_load_abort(fw_sysfs);
		mutex_unlock(&fw_lock);
	}

	if (fw_state_is_aborted(fw_priv)) {
		if (retval == -ERESTARTSYS)
			retval = -EINTR;
	} else if (fw_priv->is_paged_buf && !fw_priv->data)
		retval = -ENOMEM;

out:
	device_del(f_dev);
err_put_dev:
	put_device(f_dev);
	return retval;
}

static int fw_load_from_user_helper(struct firmware *firmware,
				    const char *name, struct device *device,
				    u32 opt_flags)
{
	struct fw_sysfs *fw_sysfs;
	long timeout;
	int ret;

	timeout = firmware_loading_timeout();
	if (opt_flags & FW_OPT_NOWAIT) {
		timeout = usermodehelper_read_lock_wait(timeout);
		if (!timeout) {
			dev_dbg(device, "firmware: %s loading timed out\n",
				name);
			return -EBUSY;
		}
	} else {
		ret = usermodehelper_read_trylock();
		if (WARN_ON(ret)) {
			dev_err(device, "firmware: %s will not be loaded\n",
				name);
			return ret;
		}
	}

	fw_sysfs = fw_create_instance(firmware, name, device, opt_flags);
	if (IS_ERR(fw_sysfs)) {
		ret = PTR_ERR(fw_sysfs);
		goto out_unlock;
	}

	fw_sysfs->fw_priv = firmware->priv;
	ret = fw_load_sysfs_fallback(fw_sysfs, timeout);

	if (!ret)
		ret = assign_fw(firmware, device);

out_unlock:
	usermodehelper_read_unlock();

	return ret;
}

static bool fw_force_sysfs_fallback(u32 opt_flags)
{
	if (fw_fallback_config.force_sysfs_fallback)
		return true;
	if (!(opt_flags & FW_OPT_USERHELPER))
		return false;
	return true;
}

static bool fw_run_sysfs_fallback(u32 opt_flags)
{
	int ret;

	if (fw_fallback_config.ignore_sysfs_fallback) {
		pr_info_once("Ignoring firmware sysfs fallback due to sysctl knob\n");
		return false;
	}

	if ((opt_flags & FW_OPT_NOFALLBACK_SYSFS))
		return false;

	/* Also permit LSMs and IMA to fail firmware sysfs fallback */
	ret = security_kernel_load_data(LOADING_FIRMWARE, true);
	if (ret < 0)
		return false;

	return fw_force_sysfs_fallback(opt_flags);
}

/**
 * firmware_fallback_sysfs() - use the fallback mechanism to find firmware
 * @fw: pointer to firmware image
 * @name: name of firmware file to look for
 * @device: device for which firmware is being loaded
 * @opt_flags: options to control firmware loading behaviour, as defined by
 *	       &enum fw_opt
 * @ret: return value from direct lookup which triggered the fallback mechanism
 *
 * This function is called if direct lookup for the firmware failed, it enables
 * a fallback mechanism through userspace by exposing a sysfs loading
 * interface. Userspace is in charge of loading the firmware through the sysfs
 * loading interface. This sysfs fallback mechanism may be disabled completely
 * on a system by setting the proc sysctl value ignore_sysfs_fallback to true.
 * If this is false we check if the internal API caller set the
 * @FW_OPT_NOFALLBACK_SYSFS flag, if so it would also disable the fallback
 * mechanism. A system may want to enforce the sysfs fallback mechanism at all
 * times, it can do this by setting ignore_sysfs_fallback to false and
 * force_sysfs_fallback to true.
 * Enabling force_sysfs_fallback is functionally equivalent to build a kernel
 * with CONFIG_FW_LOADER_USER_HELPER_FALLBACK.
 **/
int firmware_fallback_sysfs(struct firmware *fw, const char *name,
			    struct device *device,
			    u32 opt_flags,
			    int ret)
{
	if (!fw_run_sysfs_fallback(opt_flags))
		return ret;

	if (!(opt_flags & FW_OPT_NO_WARN))
		dev_warn(device, "Falling back to sysfs fallback for: %s\n",
				 name);
	else
		dev_dbg(device, "Falling back to sysfs fallback for: %s\n",
				name);
	return fw_load_from_user_helper(fw, name, device, opt_flags);
}