xref: /linux-6.15/lib/test_firmware.c (revision 5d90e05c)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20a8adf58SKees Cook /*
30a8adf58SKees Cook  * This module provides an interface to trigger and test firmware loading.
40a8adf58SKees Cook  *
50a8adf58SKees Cook  * It is designed to be used for basic evaluation of the firmware loading
60a8adf58SKees Cook  * subsystem (for example when validating firmware verification). It lacks
70a8adf58SKees Cook  * any extra dependencies, and will not normally be loaded by the system
80a8adf58SKees Cook  * unless explicitly requested by name.
90a8adf58SKees Cook  */
100a8adf58SKees Cook 
110a8adf58SKees Cook #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
120a8adf58SKees Cook 
130a8adf58SKees Cook #include <linux/init.h>
140a8adf58SKees Cook #include <linux/module.h>
150a8adf58SKees Cook #include <linux/printk.h>
16eb910947SBrian Norris #include <linux/completion.h>
170a8adf58SKees Cook #include <linux/firmware.h>
180a8adf58SKees Cook #include <linux/device.h>
190a8adf58SKees Cook #include <linux/fs.h>
200a8adf58SKees Cook #include <linux/miscdevice.h>
217feebfa4SScott Branden #include <linux/sizes.h>
220a8adf58SKees Cook #include <linux/slab.h>
230a8adf58SKees Cook #include <linux/uaccess.h>
24c92316bfSLuis R. Rodriguez #include <linux/delay.h>
25c92316bfSLuis R. Rodriguez #include <linux/kthread.h>
26514c6032SRandy Dunlap #include <linux/vmalloc.h>
27548193cbSHans de Goede #include <linux/efi_embedded_fw.h>
28c92316bfSLuis R. Rodriguez 
29baaabecfSKees Cook MODULE_IMPORT_NS(TEST_FIRMWARE);
30baaabecfSKees Cook 
31c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NAME	"test-firmware.bin"
32c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NUM_REQS	4
337feebfa4SScott Branden #define TEST_FIRMWARE_BUF_SIZE	SZ_1K
340a8adf58SKees Cook 
350a8adf58SKees Cook static DEFINE_MUTEX(test_fw_mutex);
360a8adf58SKees Cook static const struct firmware *test_firmware;
370a8adf58SKees Cook 
38c92316bfSLuis R. Rodriguez struct test_batched_req {
39c92316bfSLuis R. Rodriguez 	u8 idx;
40c92316bfSLuis R. Rodriguez 	int rc;
41c92316bfSLuis R. Rodriguez 	bool sent;
42c92316bfSLuis R. Rodriguez 	const struct firmware *fw;
43c92316bfSLuis R. Rodriguez 	const char *name;
44c92316bfSLuis R. Rodriguez 	struct completion completion;
45c92316bfSLuis R. Rodriguez 	struct task_struct *task;
46c92316bfSLuis R. Rodriguez 	struct device *dev;
47c92316bfSLuis R. Rodriguez };
48c92316bfSLuis R. Rodriguez 
49c92316bfSLuis R. Rodriguez /**
50c92316bfSLuis R. Rodriguez  * test_config - represents configuration for the test for different triggers
51c92316bfSLuis R. Rodriguez  *
52c92316bfSLuis R. Rodriguez  * @name: the name of the firmware file to look for
537feebfa4SScott Branden  * @into_buf: when the into_buf is used if this is true
547feebfa4SScott Branden  *	request_firmware_into_buf() will be used instead.
55*5d90e05cSScott Branden  * @buf_size: size of buf to allocate when into_buf is true
56*5d90e05cSScott Branden  * @file_offset: file offset to request when calling request_firmware_into_buf
57*5d90e05cSScott Branden  * @partial: partial read opt when calling request_firmware_into_buf
58c92316bfSLuis R. Rodriguez  * @sync_direct: when the sync trigger is used if this is true
59c92316bfSLuis R. Rodriguez  *	request_firmware_direct() will be used instead.
60c92316bfSLuis R. Rodriguez  * @send_uevent: whether or not to send a uevent for async requests
61c92316bfSLuis R. Rodriguez  * @num_requests: number of requests to try per test case. This is trigger
62c92316bfSLuis R. Rodriguez  *	specific.
63c92316bfSLuis R. Rodriguez  * @reqs: stores all requests information
64c92316bfSLuis R. Rodriguez  * @read_fw_idx: index of thread from which we want to read firmware results
65c92316bfSLuis R. Rodriguez  *	from through the read_fw trigger.
66c92316bfSLuis R. Rodriguez  * @test_result: a test may use this to collect the result from the call
67c92316bfSLuis R. Rodriguez  *	of the request_firmware*() calls used in their tests. In order of
68c92316bfSLuis R. Rodriguez  *	priority we always keep first any setup error. If no setup errors were
69c92316bfSLuis R. Rodriguez  *	found then we move on to the first error encountered while running the
70c92316bfSLuis R. Rodriguez  *	API. Note that for async calls this typically will be a successful
71c92316bfSLuis R. Rodriguez  *	result (0) unless of course you've used bogus parameters, or the system
72c92316bfSLuis R. Rodriguez  *	is out of memory.  In the async case the callback is expected to do a
73c92316bfSLuis R. Rodriguez  *	bit more homework to figure out what happened, unfortunately the only
74c92316bfSLuis R. Rodriguez  *	information passed today on error is the fact that no firmware was
75c92316bfSLuis R. Rodriguez  *	found so we can only assume -ENOENT on async calls if the firmware is
76c92316bfSLuis R. Rodriguez  *	NULL.
77c92316bfSLuis R. Rodriguez  *
78c92316bfSLuis R. Rodriguez  *	Errors you can expect:
79c92316bfSLuis R. Rodriguez  *
80c92316bfSLuis R. Rodriguez  *	API specific:
81c92316bfSLuis R. Rodriguez  *
82c92316bfSLuis R. Rodriguez  *	0:		success for sync, for async it means request was sent
83c92316bfSLuis R. Rodriguez  *	-EINVAL:	invalid parameters or request
84c92316bfSLuis R. Rodriguez  *	-ENOENT:	files not found
85c92316bfSLuis R. Rodriguez  *
86c92316bfSLuis R. Rodriguez  *	System environment:
87c92316bfSLuis R. Rodriguez  *
88c92316bfSLuis R. Rodriguez  *	-ENOMEM:	memory pressure on system
89c92316bfSLuis R. Rodriguez  *	-ENODEV:	out of number of devices to test
90c92316bfSLuis R. Rodriguez  *	-EINVAL:	an unexpected error has occurred
91c92316bfSLuis R. Rodriguez  * @req_firmware: if @sync_direct is true this is set to
92c92316bfSLuis R. Rodriguez  *	request_firmware_direct(), otherwise request_firmware()
93c92316bfSLuis R. Rodriguez  */
94c92316bfSLuis R. Rodriguez struct test_config {
95c92316bfSLuis R. Rodriguez 	char *name;
967feebfa4SScott Branden 	bool into_buf;
97*5d90e05cSScott Branden 	size_t buf_size;
98*5d90e05cSScott Branden 	size_t file_offset;
99*5d90e05cSScott Branden 	bool partial;
100c92316bfSLuis R. Rodriguez 	bool sync_direct;
101c92316bfSLuis R. Rodriguez 	bool send_uevent;
102c92316bfSLuis R. Rodriguez 	u8 num_requests;
103c92316bfSLuis R. Rodriguez 	u8 read_fw_idx;
104c92316bfSLuis R. Rodriguez 
105c92316bfSLuis R. Rodriguez 	/*
106c92316bfSLuis R. Rodriguez 	 * These below don't belong her but we'll move them once we create
107c92316bfSLuis R. Rodriguez 	 * a struct fw_test_device and stuff the misc_dev under there later.
108c92316bfSLuis R. Rodriguez 	 */
109c92316bfSLuis R. Rodriguez 	struct test_batched_req *reqs;
110c92316bfSLuis R. Rodriguez 	int test_result;
111c92316bfSLuis R. Rodriguez 	int (*req_firmware)(const struct firmware **fw, const char *name,
112c92316bfSLuis R. Rodriguez 			    struct device *device);
113c92316bfSLuis R. Rodriguez };
114c92316bfSLuis R. Rodriguez 
11576f8ab1bSWei Yongjun static struct test_config *test_fw_config;
116c92316bfSLuis R. Rodriguez 
1170a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
1180a8adf58SKees Cook 				 size_t size, loff_t *offset)
1190a8adf58SKees Cook {
1200a8adf58SKees Cook 	ssize_t rc = 0;
1210a8adf58SKees Cook 
1220a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
1230a8adf58SKees Cook 	if (test_firmware)
1240a8adf58SKees Cook 		rc = simple_read_from_buffer(buf, size, offset,
1250a8adf58SKees Cook 					     test_firmware->data,
1260a8adf58SKees Cook 					     test_firmware->size);
1270a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
1280a8adf58SKees Cook 	return rc;
1290a8adf58SKees Cook }
1300a8adf58SKees Cook 
1310a8adf58SKees Cook static const struct file_operations test_fw_fops = {
1320a8adf58SKees Cook 	.owner          = THIS_MODULE,
1330a8adf58SKees Cook 	.read           = test_fw_misc_read,
1340a8adf58SKees Cook };
1350a8adf58SKees Cook 
136c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void)
137c92316bfSLuis R. Rodriguez {
138c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
139c92316bfSLuis R. Rodriguez 	u8 i;
140c92316bfSLuis R. Rodriguez 
141c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs)
142c92316bfSLuis R. Rodriguez 		return;
143c92316bfSLuis R. Rodriguez 
144c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
145c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
146c92316bfSLuis R. Rodriguez 		if (req->fw)
147c92316bfSLuis R. Rodriguez 			release_firmware(req->fw);
148c92316bfSLuis R. Rodriguez 	}
149c92316bfSLuis R. Rodriguez 
150c92316bfSLuis R. Rodriguez 	vfree(test_fw_config->reqs);
151c92316bfSLuis R. Rodriguez 	test_fw_config->reqs = NULL;
152c92316bfSLuis R. Rodriguez }
153c92316bfSLuis R. Rodriguez 
154c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void)
155c92316bfSLuis R. Rodriguez {
156c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
157c92316bfSLuis R. Rodriguez 	__test_release_all_firmware();
158c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
159c92316bfSLuis R. Rodriguez }
160c92316bfSLuis R. Rodriguez 
161c92316bfSLuis R. Rodriguez 
162c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void)
163c92316bfSLuis R. Rodriguez {
164c92316bfSLuis R. Rodriguez 	__test_release_all_firmware();
165c92316bfSLuis R. Rodriguez 	kfree_const(test_fw_config->name);
166c92316bfSLuis R. Rodriguez 	test_fw_config->name = NULL;
167c92316bfSLuis R. Rodriguez }
168c92316bfSLuis R. Rodriguez 
169c92316bfSLuis R. Rodriguez /*
170c92316bfSLuis R. Rodriguez  * XXX: move to kstrncpy() once merged.
171c92316bfSLuis R. Rodriguez  *
172c92316bfSLuis R. Rodriguez  * Users should use kfree_const() when freeing these.
173c92316bfSLuis R. Rodriguez  */
174c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
175c92316bfSLuis R. Rodriguez {
176c92316bfSLuis R. Rodriguez 	*dst = kstrndup(name, count, gfp);
177c92316bfSLuis R. Rodriguez 	if (!*dst)
178c92316bfSLuis R. Rodriguez 		return -ENOSPC;
179c92316bfSLuis R. Rodriguez 	return count;
180c92316bfSLuis R. Rodriguez }
181c92316bfSLuis R. Rodriguez 
182c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void)
183c92316bfSLuis R. Rodriguez {
184c92316bfSLuis R. Rodriguez 	int ret;
185c92316bfSLuis R. Rodriguez 
186c92316bfSLuis R. Rodriguez 	ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
187c92316bfSLuis R. Rodriguez 			 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
188c92316bfSLuis R. Rodriguez 	if (ret < 0)
189c92316bfSLuis R. Rodriguez 		goto out;
190c92316bfSLuis R. Rodriguez 
191c92316bfSLuis R. Rodriguez 	test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
192c92316bfSLuis R. Rodriguez 	test_fw_config->send_uevent = true;
1937feebfa4SScott Branden 	test_fw_config->into_buf = false;
194*5d90e05cSScott Branden 	test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
195*5d90e05cSScott Branden 	test_fw_config->file_offset = 0;
196*5d90e05cSScott Branden 	test_fw_config->partial = false;
197c92316bfSLuis R. Rodriguez 	test_fw_config->sync_direct = false;
198c92316bfSLuis R. Rodriguez 	test_fw_config->req_firmware = request_firmware;
199c92316bfSLuis R. Rodriguez 	test_fw_config->test_result = 0;
200c92316bfSLuis R. Rodriguez 	test_fw_config->reqs = NULL;
201c92316bfSLuis R. Rodriguez 
202c92316bfSLuis R. Rodriguez 	return 0;
203c92316bfSLuis R. Rodriguez 
204c92316bfSLuis R. Rodriguez out:
205c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
206c92316bfSLuis R. Rodriguez 	return ret;
207c92316bfSLuis R. Rodriguez }
208c92316bfSLuis R. Rodriguez 
209c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev,
210c92316bfSLuis R. Rodriguez 			   struct device_attribute *attr,
211c92316bfSLuis R. Rodriguez 			   const char *buf, size_t count)
212c92316bfSLuis R. Rodriguez {
213c92316bfSLuis R. Rodriguez 	int ret;
214c92316bfSLuis R. Rodriguez 
215c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
216c92316bfSLuis R. Rodriguez 
217c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
218c92316bfSLuis R. Rodriguez 
219c92316bfSLuis R. Rodriguez 	ret = __test_firmware_config_init();
220c92316bfSLuis R. Rodriguez 	if (ret < 0) {
221c92316bfSLuis R. Rodriguez 		ret = -ENOMEM;
222c92316bfSLuis R. Rodriguez 		pr_err("could not alloc settings for config trigger: %d\n",
223c92316bfSLuis R. Rodriguez 		       ret);
224c92316bfSLuis R. Rodriguez 		goto out;
225c92316bfSLuis R. Rodriguez 	}
226c92316bfSLuis R. Rodriguez 
227c92316bfSLuis R. Rodriguez 	pr_info("reset\n");
228c92316bfSLuis R. Rodriguez 	ret = count;
229c92316bfSLuis R. Rodriguez 
230c92316bfSLuis R. Rodriguez out:
231c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
232c92316bfSLuis R. Rodriguez 
233c92316bfSLuis R. Rodriguez 	return ret;
234c92316bfSLuis R. Rodriguez }
235c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset);
236c92316bfSLuis R. Rodriguez 
237c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev,
238c92316bfSLuis R. Rodriguez 			   struct device_attribute *attr,
239c92316bfSLuis R. Rodriguez 			   char *buf)
240c92316bfSLuis R. Rodriguez {
241c92316bfSLuis R. Rodriguez 	int len = 0;
242c92316bfSLuis R. Rodriguez 
243c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
244c92316bfSLuis R. Rodriguez 
245bd17cc5aSDan Carpenter 	len += scnprintf(buf, PAGE_SIZE - len,
246c92316bfSLuis R. Rodriguez 			"Custom trigger configuration for: %s\n",
247c92316bfSLuis R. Rodriguez 			dev_name(dev));
248c92316bfSLuis R. Rodriguez 
249c92316bfSLuis R. Rodriguez 	if (test_fw_config->name)
250bd17cc5aSDan Carpenter 		len += scnprintf(buf + len, PAGE_SIZE - len,
251c92316bfSLuis R. Rodriguez 				"name:\t%s\n",
252c92316bfSLuis R. Rodriguez 				test_fw_config->name);
253c92316bfSLuis R. Rodriguez 	else
254bd17cc5aSDan Carpenter 		len += scnprintf(buf + len, PAGE_SIZE - len,
255c92316bfSLuis R. Rodriguez 				"name:\tEMTPY\n");
256c92316bfSLuis R. Rodriguez 
257bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
258c92316bfSLuis R. Rodriguez 			"num_requests:\t%u\n", test_fw_config->num_requests);
259c92316bfSLuis R. Rodriguez 
260bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
261c92316bfSLuis R. Rodriguez 			"send_uevent:\t\t%s\n",
262c92316bfSLuis R. Rodriguez 			test_fw_config->send_uevent ?
263c92316bfSLuis R. Rodriguez 			"FW_ACTION_HOTPLUG" :
264c92316bfSLuis R. Rodriguez 			"FW_ACTION_NOHOTPLUG");
265bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
2667feebfa4SScott Branden 			"into_buf:\t\t%s\n",
2677feebfa4SScott Branden 			test_fw_config->into_buf ? "true" : "false");
2687feebfa4SScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
269*5d90e05cSScott Branden 			"buf_size:\t%zu\n", test_fw_config->buf_size);
270*5d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
271*5d90e05cSScott Branden 			"file_offset:\t%zu\n", test_fw_config->file_offset);
272*5d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
273*5d90e05cSScott Branden 			"partial:\t\t%s\n",
274*5d90e05cSScott Branden 			test_fw_config->partial ? "true" : "false");
275*5d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
276c92316bfSLuis R. Rodriguez 			"sync_direct:\t\t%s\n",
277c92316bfSLuis R. Rodriguez 			test_fw_config->sync_direct ? "true" : "false");
278bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
279c92316bfSLuis R. Rodriguez 			"read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
280c92316bfSLuis R. Rodriguez 
281c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
282c92316bfSLuis R. Rodriguez 
283c92316bfSLuis R. Rodriguez 	return len;
284c92316bfSLuis R. Rodriguez }
285c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config);
286c92316bfSLuis R. Rodriguez 
287c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev,
288c92316bfSLuis R. Rodriguez 				 struct device_attribute *attr,
289c92316bfSLuis R. Rodriguez 				 const char *buf, size_t count)
290c92316bfSLuis R. Rodriguez {
291c92316bfSLuis R. Rodriguez 	int ret;
292c92316bfSLuis R. Rodriguez 
293c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
294c92316bfSLuis R. Rodriguez 	kfree_const(test_fw_config->name);
295c92316bfSLuis R. Rodriguez 	ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
296c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
297c92316bfSLuis R. Rodriguez 
298c92316bfSLuis R. Rodriguez 	return ret;
299c92316bfSLuis R. Rodriguez }
300c92316bfSLuis R. Rodriguez 
301c92316bfSLuis R. Rodriguez /*
302c92316bfSLuis R. Rodriguez  * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
303c92316bfSLuis R. Rodriguez  */
304c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst,
305c92316bfSLuis R. Rodriguez 				    char *src)
306c92316bfSLuis R. Rodriguez {
307c92316bfSLuis R. Rodriguez 	int len;
308c92316bfSLuis R. Rodriguez 
309c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
310c92316bfSLuis R. Rodriguez 	len = snprintf(dst, PAGE_SIZE, "%s\n", src);
311c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
312c92316bfSLuis R. Rodriguez 
313c92316bfSLuis R. Rodriguez 	return len;
314c92316bfSLuis R. Rodriguez }
315c92316bfSLuis R. Rodriguez 
316c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size,
317c92316bfSLuis R. Rodriguez 				       bool *cfg)
318c92316bfSLuis R. Rodriguez {
319c92316bfSLuis R. Rodriguez 	int ret;
320c92316bfSLuis R. Rodriguez 
321c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
322c92316bfSLuis R. Rodriguez 	if (strtobool(buf, cfg) < 0)
323c92316bfSLuis R. Rodriguez 		ret = -EINVAL;
324c92316bfSLuis R. Rodriguez 	else
325c92316bfSLuis R. Rodriguez 		ret = size;
326c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
327c92316bfSLuis R. Rodriguez 
328c92316bfSLuis R. Rodriguez 	return ret;
329c92316bfSLuis R. Rodriguez }
330c92316bfSLuis R. Rodriguez 
33155623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val)
332c92316bfSLuis R. Rodriguez {
333c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
334c92316bfSLuis R. Rodriguez }
335c92316bfSLuis R. Rodriguez 
336*5d90e05cSScott Branden static int test_dev_config_update_size_t(const char *buf,
337*5d90e05cSScott Branden 					 size_t size,
338*5d90e05cSScott Branden 					 size_t *cfg)
339*5d90e05cSScott Branden {
340*5d90e05cSScott Branden 	int ret;
341*5d90e05cSScott Branden 	long new;
342*5d90e05cSScott Branden 
343*5d90e05cSScott Branden 	ret = kstrtol(buf, 10, &new);
344*5d90e05cSScott Branden 	if (ret)
345*5d90e05cSScott Branden 		return ret;
346*5d90e05cSScott Branden 
347*5d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
348*5d90e05cSScott Branden 	*(size_t *)cfg = new;
349*5d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
350*5d90e05cSScott Branden 
351*5d90e05cSScott Branden 	/* Always return full write size even if we didn't consume all */
352*5d90e05cSScott Branden 	return size;
353*5d90e05cSScott Branden }
354*5d90e05cSScott Branden 
355*5d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
356*5d90e05cSScott Branden {
357*5d90e05cSScott Branden 	return snprintf(buf, PAGE_SIZE, "%zu\n", val);
358*5d90e05cSScott Branden }
359*5d90e05cSScott Branden 
36055623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val)
361c92316bfSLuis R. Rodriguez {
362c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
363c92316bfSLuis R. Rodriguez }
364c92316bfSLuis R. Rodriguez 
365c92316bfSLuis R. Rodriguez static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
366c92316bfSLuis R. Rodriguez {
367c92316bfSLuis R. Rodriguez 	int ret;
368c92316bfSLuis R. Rodriguez 	long new;
369c92316bfSLuis R. Rodriguez 
370c92316bfSLuis R. Rodriguez 	ret = kstrtol(buf, 10, &new);
371c92316bfSLuis R. Rodriguez 	if (ret)
372c92316bfSLuis R. Rodriguez 		return ret;
373c92316bfSLuis R. Rodriguez 
374c92316bfSLuis R. Rodriguez 	if (new > U8_MAX)
375c92316bfSLuis R. Rodriguez 		return -EINVAL;
376c92316bfSLuis R. Rodriguez 
377c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
378c92316bfSLuis R. Rodriguez 	*(u8 *)cfg = new;
379c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
380c92316bfSLuis R. Rodriguez 
381c92316bfSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
382c92316bfSLuis R. Rodriguez 	return size;
383c92316bfSLuis R. Rodriguez }
384c92316bfSLuis R. Rodriguez 
38555623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val)
386c92316bfSLuis R. Rodriguez {
387c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%u\n", val);
388c92316bfSLuis R. Rodriguez }
389c92316bfSLuis R. Rodriguez 
390c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev,
391c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
392c92316bfSLuis R. Rodriguez 				char *buf)
393c92316bfSLuis R. Rodriguez {
394c92316bfSLuis R. Rodriguez 	return config_test_show_str(buf, test_fw_config->name);
395c92316bfSLuis R. Rodriguez }
396b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name);
397c92316bfSLuis R. Rodriguez 
398c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev,
399c92316bfSLuis R. Rodriguez 					 struct device_attribute *attr,
400c92316bfSLuis R. Rodriguez 					 const char *buf, size_t count)
401c92316bfSLuis R. Rodriguez {
402c92316bfSLuis R. Rodriguez 	int rc;
403c92316bfSLuis R. Rodriguez 
404c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
405c92316bfSLuis R. Rodriguez 	if (test_fw_config->reqs) {
406c92316bfSLuis R. Rodriguez 		pr_err("Must call release_all_firmware prior to changing config\n");
407c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
408a5e19233SWei Yongjun 		mutex_unlock(&test_fw_mutex);
409c92316bfSLuis R. Rodriguez 		goto out;
410c92316bfSLuis R. Rodriguez 	}
411c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
412c92316bfSLuis R. Rodriguez 
413c92316bfSLuis R. Rodriguez 	rc = test_dev_config_update_u8(buf, count,
414c92316bfSLuis R. Rodriguez 				       &test_fw_config->num_requests);
415c92316bfSLuis R. Rodriguez 
416c92316bfSLuis R. Rodriguez out:
417c92316bfSLuis R. Rodriguez 	return rc;
418c92316bfSLuis R. Rodriguez }
419c92316bfSLuis R. Rodriguez 
420c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev,
421c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
422c92316bfSLuis R. Rodriguez 					char *buf)
423c92316bfSLuis R. Rodriguez {
424c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->num_requests);
425c92316bfSLuis R. Rodriguez }
426b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests);
427c92316bfSLuis R. Rodriguez 
4287feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev,
4297feebfa4SScott Branden 				     struct device_attribute *attr,
4307feebfa4SScott Branden 				     const char *buf, size_t count)
4317feebfa4SScott Branden {
4327feebfa4SScott Branden 	return test_dev_config_update_bool(buf,
4337feebfa4SScott Branden 					   count,
4347feebfa4SScott Branden 					   &test_fw_config->into_buf);
4357feebfa4SScott Branden }
4367feebfa4SScott Branden 
4377feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev,
4387feebfa4SScott Branden 				    struct device_attribute *attr,
4397feebfa4SScott Branden 				    char *buf)
4407feebfa4SScott Branden {
4417feebfa4SScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->into_buf);
4427feebfa4SScott Branden }
4437feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf);
4447feebfa4SScott Branden 
445*5d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev,
446*5d90e05cSScott Branden 				     struct device_attribute *attr,
447*5d90e05cSScott Branden 				     const char *buf, size_t count)
448*5d90e05cSScott Branden {
449*5d90e05cSScott Branden 	int rc;
450*5d90e05cSScott Branden 
451*5d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
452*5d90e05cSScott Branden 	if (test_fw_config->reqs) {
453*5d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
454*5d90e05cSScott Branden 		rc = -EINVAL;
455*5d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
456*5d90e05cSScott Branden 		goto out;
457*5d90e05cSScott Branden 	}
458*5d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
459*5d90e05cSScott Branden 
460*5d90e05cSScott Branden 	rc = test_dev_config_update_size_t(buf, count,
461*5d90e05cSScott Branden 					   &test_fw_config->buf_size);
462*5d90e05cSScott Branden 
463*5d90e05cSScott Branden out:
464*5d90e05cSScott Branden 	return rc;
465*5d90e05cSScott Branden }
466*5d90e05cSScott Branden 
467*5d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev,
468*5d90e05cSScott Branden 				    struct device_attribute *attr,
469*5d90e05cSScott Branden 				    char *buf)
470*5d90e05cSScott Branden {
471*5d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
472*5d90e05cSScott Branden }
473*5d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size);
474*5d90e05cSScott Branden 
475*5d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev,
476*5d90e05cSScott Branden 					struct device_attribute *attr,
477*5d90e05cSScott Branden 					const char *buf, size_t count)
478*5d90e05cSScott Branden {
479*5d90e05cSScott Branden 	int rc;
480*5d90e05cSScott Branden 
481*5d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
482*5d90e05cSScott Branden 	if (test_fw_config->reqs) {
483*5d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
484*5d90e05cSScott Branden 		rc = -EINVAL;
485*5d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
486*5d90e05cSScott Branden 		goto out;
487*5d90e05cSScott Branden 	}
488*5d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
489*5d90e05cSScott Branden 
490*5d90e05cSScott Branden 	rc = test_dev_config_update_size_t(buf, count,
491*5d90e05cSScott Branden 					   &test_fw_config->file_offset);
492*5d90e05cSScott Branden 
493*5d90e05cSScott Branden out:
494*5d90e05cSScott Branden 	return rc;
495*5d90e05cSScott Branden }
496*5d90e05cSScott Branden 
497*5d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev,
498*5d90e05cSScott Branden 				       struct device_attribute *attr,
499*5d90e05cSScott Branden 				       char *buf)
500*5d90e05cSScott Branden {
501*5d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
502*5d90e05cSScott Branden }
503*5d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset);
504*5d90e05cSScott Branden 
505*5d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev,
506*5d90e05cSScott Branden 				    struct device_attribute *attr,
507*5d90e05cSScott Branden 				    const char *buf, size_t count)
508*5d90e05cSScott Branden {
509*5d90e05cSScott Branden 	return test_dev_config_update_bool(buf,
510*5d90e05cSScott Branden 					   count,
511*5d90e05cSScott Branden 					   &test_fw_config->partial);
512*5d90e05cSScott Branden }
513*5d90e05cSScott Branden 
514*5d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev,
515*5d90e05cSScott Branden 				   struct device_attribute *attr,
516*5d90e05cSScott Branden 				   char *buf)
517*5d90e05cSScott Branden {
518*5d90e05cSScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->partial);
519*5d90e05cSScott Branden }
520*5d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial);
521*5d90e05cSScott Branden 
522c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev,
523c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
524c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
525c92316bfSLuis R. Rodriguez {
526c92316bfSLuis R. Rodriguez 	int rc = test_dev_config_update_bool(buf, count,
527c92316bfSLuis R. Rodriguez 					     &test_fw_config->sync_direct);
528c92316bfSLuis R. Rodriguez 
529c92316bfSLuis R. Rodriguez 	if (rc == count)
530c92316bfSLuis R. Rodriguez 		test_fw_config->req_firmware = test_fw_config->sync_direct ?
531c92316bfSLuis R. Rodriguez 				       request_firmware_direct :
532c92316bfSLuis R. Rodriguez 				       request_firmware;
533c92316bfSLuis R. Rodriguez 	return rc;
534c92316bfSLuis R. Rodriguez }
535c92316bfSLuis R. Rodriguez 
536c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev,
537c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
538c92316bfSLuis R. Rodriguez 				       char *buf)
539c92316bfSLuis R. Rodriguez {
540c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
541c92316bfSLuis R. Rodriguez }
542b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct);
543c92316bfSLuis R. Rodriguez 
544c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev,
545c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
546c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
547c92316bfSLuis R. Rodriguez {
548c92316bfSLuis R. Rodriguez 	return test_dev_config_update_bool(buf, count,
549c92316bfSLuis R. Rodriguez 					   &test_fw_config->send_uevent);
550c92316bfSLuis R. Rodriguez }
551c92316bfSLuis R. Rodriguez 
552c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev,
553c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
554c92316bfSLuis R. Rodriguez 				       char *buf)
555c92316bfSLuis R. Rodriguez {
556c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
557c92316bfSLuis R. Rodriguez }
558b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent);
559c92316bfSLuis R. Rodriguez 
560c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev,
561c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
562c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
563c92316bfSLuis R. Rodriguez {
564c92316bfSLuis R. Rodriguez 	return test_dev_config_update_u8(buf, count,
565c92316bfSLuis R. Rodriguez 					 &test_fw_config->read_fw_idx);
566c92316bfSLuis R. Rodriguez }
567c92316bfSLuis R. Rodriguez 
568c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev,
569c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
570c92316bfSLuis R. Rodriguez 				       char *buf)
571c92316bfSLuis R. Rodriguez {
572c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
573c92316bfSLuis R. Rodriguez }
574b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx);
575c92316bfSLuis R. Rodriguez 
576c92316bfSLuis R. Rodriguez 
5770a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev,
5780a8adf58SKees Cook 				     struct device_attribute *attr,
5790a8adf58SKees Cook 				     const char *buf, size_t count)
5800a8adf58SKees Cook {
5810a8adf58SKees Cook 	int rc;
5820a8adf58SKees Cook 	char *name;
5830a8adf58SKees Cook 
584be4a1326SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
5850a8adf58SKees Cook 	if (!name)
5860a8adf58SKees Cook 		return -ENOSPC;
5870a8adf58SKees Cook 
5880a8adf58SKees Cook 	pr_info("loading '%s'\n", name);
5890a8adf58SKees Cook 
5900a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
5910a8adf58SKees Cook 	release_firmware(test_firmware);
5920a8adf58SKees Cook 	test_firmware = NULL;
5930a8adf58SKees Cook 	rc = request_firmware(&test_firmware, name, dev);
59447e0bbb7SBrian Norris 	if (rc) {
5950a8adf58SKees Cook 		pr_info("load of '%s' failed: %d\n", name, rc);
59647e0bbb7SBrian Norris 		goto out;
59747e0bbb7SBrian Norris 	}
59847e0bbb7SBrian Norris 	pr_info("loaded: %zu\n", test_firmware->size);
59947e0bbb7SBrian Norris 	rc = count;
60047e0bbb7SBrian Norris 
60147e0bbb7SBrian Norris out:
6020a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
6030a8adf58SKees Cook 
6040a8adf58SKees Cook 	kfree(name);
6050a8adf58SKees Cook 
60647e0bbb7SBrian Norris 	return rc;
6070a8adf58SKees Cook }
6080a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request);
6090a8adf58SKees Cook 
610548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
611baaabecfSKees Cook extern struct list_head efi_embedded_fw_list;
612baaabecfSKees Cook extern bool efi_embedded_fw_checked;
613baaabecfSKees Cook 
614548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev,
615548193cbSHans de Goede 					      struct device_attribute *attr,
616548193cbSHans de Goede 					      const char *buf, size_t count)
617548193cbSHans de Goede {
618548193cbSHans de Goede 	static const u8 test_data[] = {
619548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
620548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
621548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
622548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
623548193cbSHans de Goede 	};
624548193cbSHans de Goede 	struct efi_embedded_fw efi_embedded_fw;
625548193cbSHans de Goede 	const struct firmware *firmware = NULL;
626baaabecfSKees Cook 	bool saved_efi_embedded_fw_checked;
627548193cbSHans de Goede 	char *name;
628548193cbSHans de Goede 	int rc;
629548193cbSHans de Goede 
630548193cbSHans de Goede 	name = kstrndup(buf, count, GFP_KERNEL);
631548193cbSHans de Goede 	if (!name)
632548193cbSHans de Goede 		return -ENOSPC;
633548193cbSHans de Goede 
634548193cbSHans de Goede 	pr_info("inserting test platform fw '%s'\n", name);
635548193cbSHans de Goede 	efi_embedded_fw.name = name;
636548193cbSHans de Goede 	efi_embedded_fw.data = (void *)test_data;
637548193cbSHans de Goede 	efi_embedded_fw.length = sizeof(test_data);
638548193cbSHans de Goede 	list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
639baaabecfSKees Cook 	saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
640baaabecfSKees Cook 	efi_embedded_fw_checked = true;
641548193cbSHans de Goede 
642548193cbSHans de Goede 	pr_info("loading '%s'\n", name);
643548193cbSHans de Goede 	rc = firmware_request_platform(&firmware, name, dev);
644548193cbSHans de Goede 	if (rc) {
645548193cbSHans de Goede 		pr_info("load of '%s' failed: %d\n", name, rc);
646548193cbSHans de Goede 		goto out;
647548193cbSHans de Goede 	}
648548193cbSHans de Goede 	if (firmware->size != sizeof(test_data) ||
649548193cbSHans de Goede 	    memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
650548193cbSHans de Goede 		pr_info("firmware contents mismatch for '%s'\n", name);
651548193cbSHans de Goede 		rc = -EINVAL;
652548193cbSHans de Goede 		goto out;
653548193cbSHans de Goede 	}
654548193cbSHans de Goede 	pr_info("loaded: %zu\n", firmware->size);
655548193cbSHans de Goede 	rc = count;
656548193cbSHans de Goede 
657548193cbSHans de Goede out:
658baaabecfSKees Cook 	efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
659548193cbSHans de Goede 	release_firmware(firmware);
660548193cbSHans de Goede 	list_del(&efi_embedded_fw.list);
661548193cbSHans de Goede 	kfree(name);
662548193cbSHans de Goede 
663548193cbSHans de Goede 	return rc;
664548193cbSHans de Goede }
665548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform);
666548193cbSHans de Goede #endif
667548193cbSHans de Goede 
668eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done);
669eb910947SBrian Norris 
670eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context)
671eb910947SBrian Norris {
672eb910947SBrian Norris 	test_firmware = fw;
673eb910947SBrian Norris 	complete(&async_fw_done);
674eb910947SBrian Norris }
675eb910947SBrian Norris 
676eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev,
677eb910947SBrian Norris 					   struct device_attribute *attr,
678eb910947SBrian Norris 					   const char *buf, size_t count)
679eb910947SBrian Norris {
680eb910947SBrian Norris 	int rc;
681eb910947SBrian Norris 	char *name;
682eb910947SBrian Norris 
683eb910947SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
684eb910947SBrian Norris 	if (!name)
685eb910947SBrian Norris 		return -ENOSPC;
686eb910947SBrian Norris 
687eb910947SBrian Norris 	pr_info("loading '%s'\n", name);
688eb910947SBrian Norris 
689eb910947SBrian Norris 	mutex_lock(&test_fw_mutex);
690eb910947SBrian Norris 	release_firmware(test_firmware);
691eb910947SBrian Norris 	test_firmware = NULL;
692eb910947SBrian Norris 	rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
693eb910947SBrian Norris 				     NULL, trigger_async_request_cb);
694eb910947SBrian Norris 	if (rc) {
695eb910947SBrian Norris 		pr_info("async load of '%s' failed: %d\n", name, rc);
696eb910947SBrian Norris 		kfree(name);
697eb910947SBrian Norris 		goto out;
698eb910947SBrian Norris 	}
699eb910947SBrian Norris 	/* Free 'name' ASAP, to test for race conditions */
700eb910947SBrian Norris 	kfree(name);
701eb910947SBrian Norris 
702eb910947SBrian Norris 	wait_for_completion(&async_fw_done);
703eb910947SBrian Norris 
704eb910947SBrian Norris 	if (test_firmware) {
705eb910947SBrian Norris 		pr_info("loaded: %zu\n", test_firmware->size);
706eb910947SBrian Norris 		rc = count;
707eb910947SBrian Norris 	} else {
708eb910947SBrian Norris 		pr_err("failed to async load firmware\n");
7097feebfa4SScott Branden 		rc = -ENOMEM;
710eb910947SBrian Norris 	}
711eb910947SBrian Norris 
712eb910947SBrian Norris out:
713eb910947SBrian Norris 	mutex_unlock(&test_fw_mutex);
714eb910947SBrian Norris 
715eb910947SBrian Norris 	return rc;
716eb910947SBrian Norris }
717eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request);
718eb910947SBrian Norris 
719061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev,
720061132d2SLuis R. Rodriguez 					     struct device_attribute *attr,
721061132d2SLuis R. Rodriguez 					     const char *buf, size_t count)
722061132d2SLuis R. Rodriguez {
723061132d2SLuis R. Rodriguez 	int rc;
724061132d2SLuis R. Rodriguez 	char *name;
725061132d2SLuis R. Rodriguez 
726061132d2SLuis R. Rodriguez 	name = kstrndup(buf, count, GFP_KERNEL);
727061132d2SLuis R. Rodriguez 	if (!name)
728061132d2SLuis R. Rodriguez 		return -ENOSPC;
729061132d2SLuis R. Rodriguez 
730061132d2SLuis R. Rodriguez 	pr_info("loading '%s' using custom fallback mechanism\n", name);
731061132d2SLuis R. Rodriguez 
732061132d2SLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
733061132d2SLuis R. Rodriguez 	release_firmware(test_firmware);
734061132d2SLuis R. Rodriguez 	test_firmware = NULL;
735061132d2SLuis R. Rodriguez 	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
736061132d2SLuis R. Rodriguez 				     dev, GFP_KERNEL, NULL,
737061132d2SLuis R. Rodriguez 				     trigger_async_request_cb);
738061132d2SLuis R. Rodriguez 	if (rc) {
739061132d2SLuis R. Rodriguez 		pr_info("async load of '%s' failed: %d\n", name, rc);
740061132d2SLuis R. Rodriguez 		kfree(name);
741061132d2SLuis R. Rodriguez 		goto out;
742061132d2SLuis R. Rodriguez 	}
743061132d2SLuis R. Rodriguez 	/* Free 'name' ASAP, to test for race conditions */
744061132d2SLuis R. Rodriguez 	kfree(name);
745061132d2SLuis R. Rodriguez 
746061132d2SLuis R. Rodriguez 	wait_for_completion(&async_fw_done);
747061132d2SLuis R. Rodriguez 
748061132d2SLuis R. Rodriguez 	if (test_firmware) {
749061132d2SLuis R. Rodriguez 		pr_info("loaded: %zu\n", test_firmware->size);
750061132d2SLuis R. Rodriguez 		rc = count;
751061132d2SLuis R. Rodriguez 	} else {
752061132d2SLuis R. Rodriguez 		pr_err("failed to async load firmware\n");
753061132d2SLuis R. Rodriguez 		rc = -ENODEV;
754061132d2SLuis R. Rodriguez 	}
755061132d2SLuis R. Rodriguez 
756061132d2SLuis R. Rodriguez out:
757061132d2SLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
758061132d2SLuis R. Rodriguez 
759061132d2SLuis R. Rodriguez 	return rc;
760061132d2SLuis R. Rodriguez }
761061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback);
762061132d2SLuis R. Rodriguez 
763c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data)
764c92316bfSLuis R. Rodriguez {
765c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = data;
766c92316bfSLuis R. Rodriguez 
767c92316bfSLuis R. Rodriguez 	if (!req) {
768c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
769c92316bfSLuis R. Rodriguez 		return -EINVAL;
770c92316bfSLuis R. Rodriguez 	}
771c92316bfSLuis R. Rodriguez 
7727feebfa4SScott Branden 	if (test_fw_config->into_buf) {
7737feebfa4SScott Branden 		void *test_buf;
7747feebfa4SScott Branden 
7757feebfa4SScott Branden 		test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
7767feebfa4SScott Branden 		if (!test_buf)
7777feebfa4SScott Branden 			return -ENOSPC;
7787feebfa4SScott Branden 
779*5d90e05cSScott Branden 		if (test_fw_config->partial)
780*5d90e05cSScott Branden 			req->rc = request_partial_firmware_into_buf
781*5d90e05cSScott Branden 						(&req->fw,
7827feebfa4SScott Branden 						 req->name,
7837feebfa4SScott Branden 						 req->dev,
7847feebfa4SScott Branden 						 test_buf,
785*5d90e05cSScott Branden 						 test_fw_config->buf_size,
786*5d90e05cSScott Branden 						 test_fw_config->file_offset);
787*5d90e05cSScott Branden 		else
788*5d90e05cSScott Branden 			req->rc = request_firmware_into_buf
789*5d90e05cSScott Branden 						(&req->fw,
790*5d90e05cSScott Branden 						 req->name,
791*5d90e05cSScott Branden 						 req->dev,
792*5d90e05cSScott Branden 						 test_buf,
793*5d90e05cSScott Branden 						 test_fw_config->buf_size);
7947feebfa4SScott Branden 		if (!req->fw)
7957feebfa4SScott Branden 			kfree(test_buf);
7967feebfa4SScott Branden 	} else {
7977feebfa4SScott Branden 		req->rc = test_fw_config->req_firmware(&req->fw,
7987feebfa4SScott Branden 						       req->name,
7997feebfa4SScott Branden 						       req->dev);
8007feebfa4SScott Branden 	}
8017feebfa4SScott Branden 
802c92316bfSLuis R. Rodriguez 	if (req->rc) {
803c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync load failed: %d\n",
804c92316bfSLuis R. Rodriguez 			req->idx, req->rc);
805c92316bfSLuis R. Rodriguez 		if (!test_fw_config->test_result)
806c92316bfSLuis R. Rodriguez 			test_fw_config->test_result = req->rc;
807c92316bfSLuis R. Rodriguez 	} else if (req->fw) {
808c92316bfSLuis R. Rodriguez 		req->sent = true;
809c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync loaded %zu\n",
810c92316bfSLuis R. Rodriguez 			req->idx, req->fw->size);
811c92316bfSLuis R. Rodriguez 	}
812c92316bfSLuis R. Rodriguez 	complete(&req->completion);
813c92316bfSLuis R. Rodriguez 
814c92316bfSLuis R. Rodriguez 	req->task = NULL;
815c92316bfSLuis R. Rodriguez 
816c92316bfSLuis R. Rodriguez 	return 0;
817c92316bfSLuis R. Rodriguez }
818c92316bfSLuis R. Rodriguez 
819c92316bfSLuis R. Rodriguez /*
820c92316bfSLuis R. Rodriguez  * We use a kthread as otherwise the kernel serializes all our sync requests
821c92316bfSLuis R. Rodriguez  * and we would not be able to mimic batched requests on a sync call. Batched
822c92316bfSLuis R. Rodriguez  * requests on a sync call can for instance happen on a device driver when
823c92316bfSLuis R. Rodriguez  * multiple cards are used and firmware loading happens outside of probe.
824c92316bfSLuis R. Rodriguez  */
825c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev,
826c92316bfSLuis R. Rodriguez 					      struct device_attribute *attr,
827c92316bfSLuis R. Rodriguez 					      const char *buf, size_t count)
828c92316bfSLuis R. Rodriguez {
829c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
830c92316bfSLuis R. Rodriguez 	int rc;
831c92316bfSLuis R. Rodriguez 	u8 i;
832c92316bfSLuis R. Rodriguez 
833c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
834c92316bfSLuis R. Rodriguez 
835fad953ceSKees Cook 	test_fw_config->reqs =
836fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
837fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
838c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
839c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
840c92316bfSLuis R. Rodriguez 		goto out_unlock;
841c92316bfSLuis R. Rodriguez 	}
842c92316bfSLuis R. Rodriguez 
843c92316bfSLuis R. Rodriguez 	pr_info("batched sync firmware loading '%s' %u times\n",
844c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
845c92316bfSLuis R. Rodriguez 
846c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
847c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
848c92316bfSLuis R. Rodriguez 		req->fw = NULL;
849c92316bfSLuis R. Rodriguez 		req->idx = i;
850c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
851c92316bfSLuis R. Rodriguez 		req->dev = dev;
852c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
853c92316bfSLuis R. Rodriguez 		req->task = kthread_run(test_fw_run_batch_request, req,
854c92316bfSLuis R. Rodriguez 					     "%s-%u", KBUILD_MODNAME, req->idx);
855c92316bfSLuis R. Rodriguez 		if (!req->task || IS_ERR(req->task)) {
856c92316bfSLuis R. Rodriguez 			pr_err("Setting up thread %u failed\n", req->idx);
857c92316bfSLuis R. Rodriguez 			req->task = NULL;
858c92316bfSLuis R. Rodriguez 			rc = -ENOMEM;
859c92316bfSLuis R. Rodriguez 			goto out_bail;
860c92316bfSLuis R. Rodriguez 		}
861c92316bfSLuis R. Rodriguez 	}
862c92316bfSLuis R. Rodriguez 
863c92316bfSLuis R. Rodriguez 	rc = count;
864c92316bfSLuis R. Rodriguez 
865c92316bfSLuis R. Rodriguez 	/*
866c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
867c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
868c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
869c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
870c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
871c92316bfSLuis R. Rodriguez 	 */
872c92316bfSLuis R. Rodriguez 
873c92316bfSLuis R. Rodriguez out_bail:
874c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
875c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
876c92316bfSLuis R. Rodriguez 		if (req->task || req->sent)
877c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
878c92316bfSLuis R. Rodriguez 	}
879c92316bfSLuis R. Rodriguez 
880c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
881c92316bfSLuis R. Rodriguez 	if (rc < 0)
882c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
883c92316bfSLuis R. Rodriguez 
884c92316bfSLuis R. Rodriguez out_unlock:
885c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
886c92316bfSLuis R. Rodriguez 
887c92316bfSLuis R. Rodriguez 	return rc;
888c92316bfSLuis R. Rodriguez }
889c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests);
890c92316bfSLuis R. Rodriguez 
891c92316bfSLuis R. Rodriguez /*
892c92316bfSLuis R. Rodriguez  * We wait for each callback to return with the lock held, no need to lock here
893c92316bfSLuis R. Rodriguez  */
894c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context)
895c92316bfSLuis R. Rodriguez {
896c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = context;
897c92316bfSLuis R. Rodriguez 
898c92316bfSLuis R. Rodriguez 	if (!req) {
899c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
900c92316bfSLuis R. Rodriguez 		return;
901c92316bfSLuis R. Rodriguez 	}
902c92316bfSLuis R. Rodriguez 
903c92316bfSLuis R. Rodriguez 	/* forces *some* batched requests to queue up */
904c92316bfSLuis R. Rodriguez 	if (!req->idx)
905c92316bfSLuis R. Rodriguez 		ssleep(2);
906c92316bfSLuis R. Rodriguez 
907c92316bfSLuis R. Rodriguez 	req->fw = fw;
908c92316bfSLuis R. Rodriguez 
909c92316bfSLuis R. Rodriguez 	/*
910c92316bfSLuis R. Rodriguez 	 * Unfortunately the firmware API gives us nothing other than a null FW
911c92316bfSLuis R. Rodriguez 	 * if the firmware was not found on async requests.  Best we can do is
912c92316bfSLuis R. Rodriguez 	 * just assume -ENOENT. A better API would pass the actual return
913c92316bfSLuis R. Rodriguez 	 * value to the callback.
914c92316bfSLuis R. Rodriguez 	 */
915c92316bfSLuis R. Rodriguez 	if (!fw && !test_fw_config->test_result)
916c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -ENOENT;
917c92316bfSLuis R. Rodriguez 
918c92316bfSLuis R. Rodriguez 	complete(&req->completion);
919c92316bfSLuis R. Rodriguez }
920c92316bfSLuis R. Rodriguez 
921c92316bfSLuis R. Rodriguez static
922c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev,
923c92316bfSLuis R. Rodriguez 					     struct device_attribute *attr,
924c92316bfSLuis R. Rodriguez 					     const char *buf, size_t count)
925c92316bfSLuis R. Rodriguez {
926c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
927c92316bfSLuis R. Rodriguez 	bool send_uevent;
928c92316bfSLuis R. Rodriguez 	int rc;
929c92316bfSLuis R. Rodriguez 	u8 i;
930c92316bfSLuis R. Rodriguez 
931c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
932c92316bfSLuis R. Rodriguez 
933fad953ceSKees Cook 	test_fw_config->reqs =
934fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
935fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
936c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
937c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
938c92316bfSLuis R. Rodriguez 		goto out;
939c92316bfSLuis R. Rodriguez 	}
940c92316bfSLuis R. Rodriguez 
941c92316bfSLuis R. Rodriguez 	pr_info("batched loading '%s' custom fallback mechanism %u times\n",
942c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
943c92316bfSLuis R. Rodriguez 
944c92316bfSLuis R. Rodriguez 	send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG :
945c92316bfSLuis R. Rodriguez 		FW_ACTION_NOHOTPLUG;
946c92316bfSLuis R. Rodriguez 
947c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
948c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
949c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
950c92316bfSLuis R. Rodriguez 		req->fw = NULL;
951c92316bfSLuis R. Rodriguez 		req->idx = i;
952c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
953c92316bfSLuis R. Rodriguez 		rc = request_firmware_nowait(THIS_MODULE, send_uevent,
954c92316bfSLuis R. Rodriguez 					     req->name,
955c92316bfSLuis R. Rodriguez 					     dev, GFP_KERNEL, req,
956c92316bfSLuis R. Rodriguez 					     trigger_batched_cb);
957c92316bfSLuis R. Rodriguez 		if (rc) {
958c92316bfSLuis R. Rodriguez 			pr_info("#%u: batched async load failed setup: %d\n",
959c92316bfSLuis R. Rodriguez 				i, rc);
960c92316bfSLuis R. Rodriguez 			req->rc = rc;
961c92316bfSLuis R. Rodriguez 			goto out_bail;
962c92316bfSLuis R. Rodriguez 		} else
963c92316bfSLuis R. Rodriguez 			req->sent = true;
964c92316bfSLuis R. Rodriguez 	}
965c92316bfSLuis R. Rodriguez 
966c92316bfSLuis R. Rodriguez 	rc = count;
967c92316bfSLuis R. Rodriguez 
968c92316bfSLuis R. Rodriguez out_bail:
969c92316bfSLuis R. Rodriguez 
970c92316bfSLuis R. Rodriguez 	/*
971c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
972c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
973c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
974c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
975c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
976c92316bfSLuis R. Rodriguez 	 */
977c92316bfSLuis R. Rodriguez 
978c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
979c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
980c92316bfSLuis R. Rodriguez 		if (req->sent)
981c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
982c92316bfSLuis R. Rodriguez 	}
983c92316bfSLuis R. Rodriguez 
984c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
985c92316bfSLuis R. Rodriguez 	if (rc < 0)
986c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
987c92316bfSLuis R. Rodriguez 
988c92316bfSLuis R. Rodriguez out:
989c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
990c92316bfSLuis R. Rodriguez 
991c92316bfSLuis R. Rodriguez 	return rc;
992c92316bfSLuis R. Rodriguez }
993c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async);
994c92316bfSLuis R. Rodriguez 
995c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev,
996c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
997c92316bfSLuis R. Rodriguez 				char *buf)
998c92316bfSLuis R. Rodriguez {
999c92316bfSLuis R. Rodriguez 	return test_dev_config_show_int(buf, test_fw_config->test_result);
1000c92316bfSLuis R. Rodriguez }
1001c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result);
1002c92316bfSLuis R. Rodriguez 
1003c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev,
1004c92316bfSLuis R. Rodriguez 					  struct device_attribute *attr,
1005c92316bfSLuis R. Rodriguez 					  const char *buf, size_t count)
1006c92316bfSLuis R. Rodriguez {
1007c92316bfSLuis R. Rodriguez 	test_release_all_firmware();
1008c92316bfSLuis R. Rodriguez 	return count;
1009c92316bfSLuis R. Rodriguez }
1010c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware);
1011c92316bfSLuis R. Rodriguez 
1012c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev,
1013c92316bfSLuis R. Rodriguez 				  struct device_attribute *attr,
1014c92316bfSLuis R. Rodriguez 				  char *buf)
1015c92316bfSLuis R. Rodriguez {
1016c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
1017c92316bfSLuis R. Rodriguez 	u8 idx;
1018c92316bfSLuis R. Rodriguez 	ssize_t rc = 0;
1019c92316bfSLuis R. Rodriguez 
1020c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
1021c92316bfSLuis R. Rodriguez 
1022c92316bfSLuis R. Rodriguez 	idx = test_fw_config->read_fw_idx;
1023c92316bfSLuis R. Rodriguez 	if (idx >= test_fw_config->num_requests) {
1024c92316bfSLuis R. Rodriguez 		rc = -ERANGE;
1025c92316bfSLuis R. Rodriguez 		goto out;
1026c92316bfSLuis R. Rodriguez 	}
1027c92316bfSLuis R. Rodriguez 
1028c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
1029c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
1030c92316bfSLuis R. Rodriguez 		goto out;
1031c92316bfSLuis R. Rodriguez 	}
1032c92316bfSLuis R. Rodriguez 
1033c92316bfSLuis R. Rodriguez 	req = &test_fw_config->reqs[idx];
1034c92316bfSLuis R. Rodriguez 	if (!req->fw) {
1035c92316bfSLuis R. Rodriguez 		pr_err("#%u: failed to async load firmware\n", idx);
1036c92316bfSLuis R. Rodriguez 		rc = -ENOENT;
1037c92316bfSLuis R. Rodriguez 		goto out;
1038c92316bfSLuis R. Rodriguez 	}
1039c92316bfSLuis R. Rodriguez 
1040c92316bfSLuis R. Rodriguez 	pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1041c92316bfSLuis R. Rodriguez 
1042c92316bfSLuis R. Rodriguez 	if (req->fw->size > PAGE_SIZE) {
1043c92316bfSLuis R. Rodriguez 		pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1044c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
10458bb0a886SColin Ian King 		goto out;
1046c92316bfSLuis R. Rodriguez 	}
1047c92316bfSLuis R. Rodriguez 	memcpy(buf, req->fw->data, req->fw->size);
1048c92316bfSLuis R. Rodriguez 
1049c92316bfSLuis R. Rodriguez 	rc = req->fw->size;
1050c92316bfSLuis R. Rodriguez out:
1051c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1052c92316bfSLuis R. Rodriguez 
1053c92316bfSLuis R. Rodriguez 	return rc;
1054c92316bfSLuis R. Rodriguez }
1055c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware);
1056c92316bfSLuis R. Rodriguez 
1057083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name)          &dev_attr_##name.attr
1058083a93b0SLuis R. Rodriguez 
1059083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = {
1060c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(reset),
1061c92316bfSLuis R. Rodriguez 
1062c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config),
1063c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_name),
1064c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_num_requests),
10657feebfa4SScott Branden 	TEST_FW_DEV_ATTR(config_into_buf),
1066*5d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_buf_size),
1067*5d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_file_offset),
1068*5d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_partial),
1069c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_sync_direct),
1070c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_send_uevent),
1071c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_read_fw_idx),
1072c92316bfSLuis R. Rodriguez 
1073c92316bfSLuis R. Rodriguez 	/* These don't use the config at all - they could be ported! */
1074083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_request),
1075083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_async_request),
1076061132d2SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_custom_fallback),
1077548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1078548193cbSHans de Goede 	TEST_FW_DEV_ATTR(trigger_request_platform),
1079548193cbSHans de Goede #endif
1080c92316bfSLuis R. Rodriguez 
1081c92316bfSLuis R. Rodriguez 	/* These use the config and can use the test_result */
1082c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests),
1083c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1084c92316bfSLuis R. Rodriguez 
1085c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(release_all_firmware),
1086c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(test_result),
1087c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(read_firmware),
1088083a93b0SLuis R. Rodriguez 	NULL,
1089083a93b0SLuis R. Rodriguez };
1090083a93b0SLuis R. Rodriguez 
1091083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev);
1092083a93b0SLuis R. Rodriguez 
109367fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = {
109467fd553cSLuis R. Rodriguez 	.minor          = MISC_DYNAMIC_MINOR,
109567fd553cSLuis R. Rodriguez 	.name           = "test_firmware",
109667fd553cSLuis R. Rodriguez 	.fops           = &test_fw_fops,
1097083a93b0SLuis R. Rodriguez 	.groups 	= test_dev_groups,
109867fd553cSLuis R. Rodriguez };
109967fd553cSLuis R. Rodriguez 
11000a8adf58SKees Cook static int __init test_firmware_init(void)
11010a8adf58SKees Cook {
11020a8adf58SKees Cook 	int rc;
11030a8adf58SKees Cook 
1104c92316bfSLuis R. Rodriguez 	test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1105c92316bfSLuis R. Rodriguez 	if (!test_fw_config)
1106c92316bfSLuis R. Rodriguez 		return -ENOMEM;
1107c92316bfSLuis R. Rodriguez 
1108c92316bfSLuis R. Rodriguez 	rc = __test_firmware_config_init();
1109d4fddac5SWenwen Wang 	if (rc) {
1110d4fddac5SWenwen Wang 		kfree(test_fw_config);
1111d4fddac5SWenwen Wang 		pr_err("could not init firmware test config: %d\n", rc);
1112c92316bfSLuis R. Rodriguez 		return rc;
1113d4fddac5SWenwen Wang 	}
1114c92316bfSLuis R. Rodriguez 
11150a8adf58SKees Cook 	rc = misc_register(&test_fw_misc_device);
11160a8adf58SKees Cook 	if (rc) {
1117c92316bfSLuis R. Rodriguez 		kfree(test_fw_config);
11180a8adf58SKees Cook 		pr_err("could not register misc device: %d\n", rc);
11190a8adf58SKees Cook 		return rc;
11200a8adf58SKees Cook 	}
1121eb910947SBrian Norris 
11220a8adf58SKees Cook 	pr_warn("interface ready\n");
11230a8adf58SKees Cook 
11240a8adf58SKees Cook 	return 0;
11250a8adf58SKees Cook }
11260a8adf58SKees Cook 
11270a8adf58SKees Cook module_init(test_firmware_init);
11280a8adf58SKees Cook 
11290a8adf58SKees Cook static void __exit test_firmware_exit(void)
11300a8adf58SKees Cook {
1131c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
11320a8adf58SKees Cook 	release_firmware(test_firmware);
11330a8adf58SKees Cook 	misc_deregister(&test_fw_misc_device);
1134c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
1135c92316bfSLuis R. Rodriguez 	kfree(test_fw_config);
1136c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1137c92316bfSLuis R. Rodriguez 
11380a8adf58SKees Cook 	pr_warn("removed interface\n");
11390a8adf58SKees Cook }
11400a8adf58SKees Cook 
11410a8adf58SKees Cook module_exit(test_firmware_exit);
11420a8adf58SKees Cook 
11430a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>");
11440a8adf58SKees Cook MODULE_LICENSE("GPL");
1145