xref: /linux-6.15/lib/test_firmware.c (revision 506dfc99)
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.
555d90e05cSScott Branden  * @buf_size: size of buf to allocate when into_buf is true
565d90e05cSScott Branden  * @file_offset: file offset to request when calling request_firmware_into_buf
575d90e05cSScott 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;
975d90e05cSScott Branden 	size_t buf_size;
985d90e05cSScott Branden 	size_t file_offset;
995d90e05cSScott 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;
1945d90e05cSScott Branden 	test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
1955d90e05cSScott Branden 	test_fw_config->file_offset = 0;
1965d90e05cSScott 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,
2695d90e05cSScott Branden 			"buf_size:\t%zu\n", test_fw_config->buf_size);
2705d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
2715d90e05cSScott Branden 			"file_offset:\t%zu\n", test_fw_config->file_offset);
2725d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
2735d90e05cSScott Branden 			"partial:\t\t%s\n",
2745d90e05cSScott Branden 			test_fw_config->partial ? "true" : "false");
2755d90e05cSScott 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 
3365d90e05cSScott Branden static int test_dev_config_update_size_t(const char *buf,
3375d90e05cSScott Branden 					 size_t size,
3385d90e05cSScott Branden 					 size_t *cfg)
3395d90e05cSScott Branden {
3405d90e05cSScott Branden 	int ret;
3415d90e05cSScott Branden 	long new;
3425d90e05cSScott Branden 
3435d90e05cSScott Branden 	ret = kstrtol(buf, 10, &new);
3445d90e05cSScott Branden 	if (ret)
3455d90e05cSScott Branden 		return ret;
3465d90e05cSScott Branden 
3475d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
3485d90e05cSScott Branden 	*(size_t *)cfg = new;
3495d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
3505d90e05cSScott Branden 
3515d90e05cSScott Branden 	/* Always return full write size even if we didn't consume all */
3525d90e05cSScott Branden 	return size;
3535d90e05cSScott Branden }
3545d90e05cSScott Branden 
3555d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
3565d90e05cSScott Branden {
3575d90e05cSScott Branden 	return snprintf(buf, PAGE_SIZE, "%zu\n", val);
3585d90e05cSScott Branden }
3595d90e05cSScott 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 {
367*506dfc99SAlexey Dobriyan 	u8 val;
368c92316bfSLuis R. Rodriguez 	int ret;
369c92316bfSLuis R. Rodriguez 
370*506dfc99SAlexey Dobriyan 	ret = kstrtou8(buf, 10, &val);
371c92316bfSLuis R. Rodriguez 	if (ret)
372c92316bfSLuis R. Rodriguez 		return ret;
373c92316bfSLuis R. Rodriguez 
374c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
375*506dfc99SAlexey Dobriyan 	*(u8 *)cfg = val;
376c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
377c92316bfSLuis R. Rodriguez 
378c92316bfSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
379c92316bfSLuis R. Rodriguez 	return size;
380c92316bfSLuis R. Rodriguez }
381c92316bfSLuis R. Rodriguez 
38255623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val)
383c92316bfSLuis R. Rodriguez {
384c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%u\n", val);
385c92316bfSLuis R. Rodriguez }
386c92316bfSLuis R. Rodriguez 
387c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev,
388c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
389c92316bfSLuis R. Rodriguez 				char *buf)
390c92316bfSLuis R. Rodriguez {
391c92316bfSLuis R. Rodriguez 	return config_test_show_str(buf, test_fw_config->name);
392c92316bfSLuis R. Rodriguez }
393b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name);
394c92316bfSLuis R. Rodriguez 
395c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev,
396c92316bfSLuis R. Rodriguez 					 struct device_attribute *attr,
397c92316bfSLuis R. Rodriguez 					 const char *buf, size_t count)
398c92316bfSLuis R. Rodriguez {
399c92316bfSLuis R. Rodriguez 	int rc;
400c92316bfSLuis R. Rodriguez 
401c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
402c92316bfSLuis R. Rodriguez 	if (test_fw_config->reqs) {
403c92316bfSLuis R. Rodriguez 		pr_err("Must call release_all_firmware prior to changing config\n");
404c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
405a5e19233SWei Yongjun 		mutex_unlock(&test_fw_mutex);
406c92316bfSLuis R. Rodriguez 		goto out;
407c92316bfSLuis R. Rodriguez 	}
408c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
409c92316bfSLuis R. Rodriguez 
410c92316bfSLuis R. Rodriguez 	rc = test_dev_config_update_u8(buf, count,
411c92316bfSLuis R. Rodriguez 				       &test_fw_config->num_requests);
412c92316bfSLuis R. Rodriguez 
413c92316bfSLuis R. Rodriguez out:
414c92316bfSLuis R. Rodriguez 	return rc;
415c92316bfSLuis R. Rodriguez }
416c92316bfSLuis R. Rodriguez 
417c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev,
418c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
419c92316bfSLuis R. Rodriguez 					char *buf)
420c92316bfSLuis R. Rodriguez {
421c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->num_requests);
422c92316bfSLuis R. Rodriguez }
423b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests);
424c92316bfSLuis R. Rodriguez 
4257feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev,
4267feebfa4SScott Branden 				     struct device_attribute *attr,
4277feebfa4SScott Branden 				     const char *buf, size_t count)
4287feebfa4SScott Branden {
4297feebfa4SScott Branden 	return test_dev_config_update_bool(buf,
4307feebfa4SScott Branden 					   count,
4317feebfa4SScott Branden 					   &test_fw_config->into_buf);
4327feebfa4SScott Branden }
4337feebfa4SScott Branden 
4347feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev,
4357feebfa4SScott Branden 				    struct device_attribute *attr,
4367feebfa4SScott Branden 				    char *buf)
4377feebfa4SScott Branden {
4387feebfa4SScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->into_buf);
4397feebfa4SScott Branden }
4407feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf);
4417feebfa4SScott Branden 
4425d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev,
4435d90e05cSScott Branden 				     struct device_attribute *attr,
4445d90e05cSScott Branden 				     const char *buf, size_t count)
4455d90e05cSScott Branden {
4465d90e05cSScott Branden 	int rc;
4475d90e05cSScott Branden 
4485d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
4495d90e05cSScott Branden 	if (test_fw_config->reqs) {
4505d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
4515d90e05cSScott Branden 		rc = -EINVAL;
4525d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
4535d90e05cSScott Branden 		goto out;
4545d90e05cSScott Branden 	}
4555d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
4565d90e05cSScott Branden 
4575d90e05cSScott Branden 	rc = test_dev_config_update_size_t(buf, count,
4585d90e05cSScott Branden 					   &test_fw_config->buf_size);
4595d90e05cSScott Branden 
4605d90e05cSScott Branden out:
4615d90e05cSScott Branden 	return rc;
4625d90e05cSScott Branden }
4635d90e05cSScott Branden 
4645d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev,
4655d90e05cSScott Branden 				    struct device_attribute *attr,
4665d90e05cSScott Branden 				    char *buf)
4675d90e05cSScott Branden {
4685d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
4695d90e05cSScott Branden }
4705d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size);
4715d90e05cSScott Branden 
4725d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev,
4735d90e05cSScott Branden 					struct device_attribute *attr,
4745d90e05cSScott Branden 					const char *buf, size_t count)
4755d90e05cSScott Branden {
4765d90e05cSScott Branden 	int rc;
4775d90e05cSScott Branden 
4785d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
4795d90e05cSScott Branden 	if (test_fw_config->reqs) {
4805d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
4815d90e05cSScott Branden 		rc = -EINVAL;
4825d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
4835d90e05cSScott Branden 		goto out;
4845d90e05cSScott Branden 	}
4855d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
4865d90e05cSScott Branden 
4875d90e05cSScott Branden 	rc = test_dev_config_update_size_t(buf, count,
4885d90e05cSScott Branden 					   &test_fw_config->file_offset);
4895d90e05cSScott Branden 
4905d90e05cSScott Branden out:
4915d90e05cSScott Branden 	return rc;
4925d90e05cSScott Branden }
4935d90e05cSScott Branden 
4945d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev,
4955d90e05cSScott Branden 				       struct device_attribute *attr,
4965d90e05cSScott Branden 				       char *buf)
4975d90e05cSScott Branden {
4985d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
4995d90e05cSScott Branden }
5005d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset);
5015d90e05cSScott Branden 
5025d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev,
5035d90e05cSScott Branden 				    struct device_attribute *attr,
5045d90e05cSScott Branden 				    const char *buf, size_t count)
5055d90e05cSScott Branden {
5065d90e05cSScott Branden 	return test_dev_config_update_bool(buf,
5075d90e05cSScott Branden 					   count,
5085d90e05cSScott Branden 					   &test_fw_config->partial);
5095d90e05cSScott Branden }
5105d90e05cSScott Branden 
5115d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev,
5125d90e05cSScott Branden 				   struct device_attribute *attr,
5135d90e05cSScott Branden 				   char *buf)
5145d90e05cSScott Branden {
5155d90e05cSScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->partial);
5165d90e05cSScott Branden }
5175d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial);
5185d90e05cSScott Branden 
519c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev,
520c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
521c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
522c92316bfSLuis R. Rodriguez {
523c92316bfSLuis R. Rodriguez 	int rc = test_dev_config_update_bool(buf, count,
524c92316bfSLuis R. Rodriguez 					     &test_fw_config->sync_direct);
525c92316bfSLuis R. Rodriguez 
526c92316bfSLuis R. Rodriguez 	if (rc == count)
527c92316bfSLuis R. Rodriguez 		test_fw_config->req_firmware = test_fw_config->sync_direct ?
528c92316bfSLuis R. Rodriguez 				       request_firmware_direct :
529c92316bfSLuis R. Rodriguez 				       request_firmware;
530c92316bfSLuis R. Rodriguez 	return rc;
531c92316bfSLuis R. Rodriguez }
532c92316bfSLuis R. Rodriguez 
533c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev,
534c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
535c92316bfSLuis R. Rodriguez 				       char *buf)
536c92316bfSLuis R. Rodriguez {
537c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
538c92316bfSLuis R. Rodriguez }
539b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct);
540c92316bfSLuis R. Rodriguez 
541c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev,
542c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
543c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
544c92316bfSLuis R. Rodriguez {
545c92316bfSLuis R. Rodriguez 	return test_dev_config_update_bool(buf, count,
546c92316bfSLuis R. Rodriguez 					   &test_fw_config->send_uevent);
547c92316bfSLuis R. Rodriguez }
548c92316bfSLuis R. Rodriguez 
549c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev,
550c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
551c92316bfSLuis R. Rodriguez 				       char *buf)
552c92316bfSLuis R. Rodriguez {
553c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
554c92316bfSLuis R. Rodriguez }
555b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent);
556c92316bfSLuis R. Rodriguez 
557c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev,
558c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
559c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
560c92316bfSLuis R. Rodriguez {
561c92316bfSLuis R. Rodriguez 	return test_dev_config_update_u8(buf, count,
562c92316bfSLuis R. Rodriguez 					 &test_fw_config->read_fw_idx);
563c92316bfSLuis R. Rodriguez }
564c92316bfSLuis R. Rodriguez 
565c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev,
566c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
567c92316bfSLuis R. Rodriguez 				       char *buf)
568c92316bfSLuis R. Rodriguez {
569c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
570c92316bfSLuis R. Rodriguez }
571b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx);
572c92316bfSLuis R. Rodriguez 
573c92316bfSLuis R. Rodriguez 
5740a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev,
5750a8adf58SKees Cook 				     struct device_attribute *attr,
5760a8adf58SKees Cook 				     const char *buf, size_t count)
5770a8adf58SKees Cook {
5780a8adf58SKees Cook 	int rc;
5790a8adf58SKees Cook 	char *name;
5800a8adf58SKees Cook 
581be4a1326SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
5820a8adf58SKees Cook 	if (!name)
5830a8adf58SKees Cook 		return -ENOSPC;
5840a8adf58SKees Cook 
5850a8adf58SKees Cook 	pr_info("loading '%s'\n", name);
5860a8adf58SKees Cook 
5870a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
5880a8adf58SKees Cook 	release_firmware(test_firmware);
5890a8adf58SKees Cook 	test_firmware = NULL;
5900a8adf58SKees Cook 	rc = request_firmware(&test_firmware, name, dev);
59147e0bbb7SBrian Norris 	if (rc) {
5920a8adf58SKees Cook 		pr_info("load of '%s' failed: %d\n", name, rc);
59347e0bbb7SBrian Norris 		goto out;
59447e0bbb7SBrian Norris 	}
59547e0bbb7SBrian Norris 	pr_info("loaded: %zu\n", test_firmware->size);
59647e0bbb7SBrian Norris 	rc = count;
59747e0bbb7SBrian Norris 
59847e0bbb7SBrian Norris out:
5990a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
6000a8adf58SKees Cook 
6010a8adf58SKees Cook 	kfree(name);
6020a8adf58SKees Cook 
60347e0bbb7SBrian Norris 	return rc;
6040a8adf58SKees Cook }
6050a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request);
6060a8adf58SKees Cook 
607548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
608baaabecfSKees Cook extern struct list_head efi_embedded_fw_list;
609baaabecfSKees Cook extern bool efi_embedded_fw_checked;
610baaabecfSKees Cook 
611548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev,
612548193cbSHans de Goede 					      struct device_attribute *attr,
613548193cbSHans de Goede 					      const char *buf, size_t count)
614548193cbSHans de Goede {
615548193cbSHans de Goede 	static const u8 test_data[] = {
616548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
617548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
618548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
619548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
620548193cbSHans de Goede 	};
621548193cbSHans de Goede 	struct efi_embedded_fw efi_embedded_fw;
622548193cbSHans de Goede 	const struct firmware *firmware = NULL;
623baaabecfSKees Cook 	bool saved_efi_embedded_fw_checked;
624548193cbSHans de Goede 	char *name;
625548193cbSHans de Goede 	int rc;
626548193cbSHans de Goede 
627548193cbSHans de Goede 	name = kstrndup(buf, count, GFP_KERNEL);
628548193cbSHans de Goede 	if (!name)
629548193cbSHans de Goede 		return -ENOSPC;
630548193cbSHans de Goede 
631548193cbSHans de Goede 	pr_info("inserting test platform fw '%s'\n", name);
632548193cbSHans de Goede 	efi_embedded_fw.name = name;
633548193cbSHans de Goede 	efi_embedded_fw.data = (void *)test_data;
634548193cbSHans de Goede 	efi_embedded_fw.length = sizeof(test_data);
635548193cbSHans de Goede 	list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
636baaabecfSKees Cook 	saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
637baaabecfSKees Cook 	efi_embedded_fw_checked = true;
638548193cbSHans de Goede 
639548193cbSHans de Goede 	pr_info("loading '%s'\n", name);
640548193cbSHans de Goede 	rc = firmware_request_platform(&firmware, name, dev);
641548193cbSHans de Goede 	if (rc) {
642548193cbSHans de Goede 		pr_info("load of '%s' failed: %d\n", name, rc);
643548193cbSHans de Goede 		goto out;
644548193cbSHans de Goede 	}
645548193cbSHans de Goede 	if (firmware->size != sizeof(test_data) ||
646548193cbSHans de Goede 	    memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
647548193cbSHans de Goede 		pr_info("firmware contents mismatch for '%s'\n", name);
648548193cbSHans de Goede 		rc = -EINVAL;
649548193cbSHans de Goede 		goto out;
650548193cbSHans de Goede 	}
651548193cbSHans de Goede 	pr_info("loaded: %zu\n", firmware->size);
652548193cbSHans de Goede 	rc = count;
653548193cbSHans de Goede 
654548193cbSHans de Goede out:
655baaabecfSKees Cook 	efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
656548193cbSHans de Goede 	release_firmware(firmware);
657548193cbSHans de Goede 	list_del(&efi_embedded_fw.list);
658548193cbSHans de Goede 	kfree(name);
659548193cbSHans de Goede 
660548193cbSHans de Goede 	return rc;
661548193cbSHans de Goede }
662548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform);
663548193cbSHans de Goede #endif
664548193cbSHans de Goede 
665eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done);
666eb910947SBrian Norris 
667eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context)
668eb910947SBrian Norris {
669eb910947SBrian Norris 	test_firmware = fw;
670eb910947SBrian Norris 	complete(&async_fw_done);
671eb910947SBrian Norris }
672eb910947SBrian Norris 
673eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev,
674eb910947SBrian Norris 					   struct device_attribute *attr,
675eb910947SBrian Norris 					   const char *buf, size_t count)
676eb910947SBrian Norris {
677eb910947SBrian Norris 	int rc;
678eb910947SBrian Norris 	char *name;
679eb910947SBrian Norris 
680eb910947SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
681eb910947SBrian Norris 	if (!name)
682eb910947SBrian Norris 		return -ENOSPC;
683eb910947SBrian Norris 
684eb910947SBrian Norris 	pr_info("loading '%s'\n", name);
685eb910947SBrian Norris 
686eb910947SBrian Norris 	mutex_lock(&test_fw_mutex);
687eb910947SBrian Norris 	release_firmware(test_firmware);
688eb910947SBrian Norris 	test_firmware = NULL;
689eb910947SBrian Norris 	rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
690eb910947SBrian Norris 				     NULL, trigger_async_request_cb);
691eb910947SBrian Norris 	if (rc) {
692eb910947SBrian Norris 		pr_info("async load of '%s' failed: %d\n", name, rc);
693eb910947SBrian Norris 		kfree(name);
694eb910947SBrian Norris 		goto out;
695eb910947SBrian Norris 	}
696eb910947SBrian Norris 	/* Free 'name' ASAP, to test for race conditions */
697eb910947SBrian Norris 	kfree(name);
698eb910947SBrian Norris 
699eb910947SBrian Norris 	wait_for_completion(&async_fw_done);
700eb910947SBrian Norris 
701eb910947SBrian Norris 	if (test_firmware) {
702eb910947SBrian Norris 		pr_info("loaded: %zu\n", test_firmware->size);
703eb910947SBrian Norris 		rc = count;
704eb910947SBrian Norris 	} else {
705eb910947SBrian Norris 		pr_err("failed to async load firmware\n");
7067feebfa4SScott Branden 		rc = -ENOMEM;
707eb910947SBrian Norris 	}
708eb910947SBrian Norris 
709eb910947SBrian Norris out:
710eb910947SBrian Norris 	mutex_unlock(&test_fw_mutex);
711eb910947SBrian Norris 
712eb910947SBrian Norris 	return rc;
713eb910947SBrian Norris }
714eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request);
715eb910947SBrian Norris 
716061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev,
717061132d2SLuis R. Rodriguez 					     struct device_attribute *attr,
718061132d2SLuis R. Rodriguez 					     const char *buf, size_t count)
719061132d2SLuis R. Rodriguez {
720061132d2SLuis R. Rodriguez 	int rc;
721061132d2SLuis R. Rodriguez 	char *name;
722061132d2SLuis R. Rodriguez 
723061132d2SLuis R. Rodriguez 	name = kstrndup(buf, count, GFP_KERNEL);
724061132d2SLuis R. Rodriguez 	if (!name)
725061132d2SLuis R. Rodriguez 		return -ENOSPC;
726061132d2SLuis R. Rodriguez 
727061132d2SLuis R. Rodriguez 	pr_info("loading '%s' using custom fallback mechanism\n", name);
728061132d2SLuis R. Rodriguez 
729061132d2SLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
730061132d2SLuis R. Rodriguez 	release_firmware(test_firmware);
731061132d2SLuis R. Rodriguez 	test_firmware = NULL;
732061132d2SLuis R. Rodriguez 	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
733061132d2SLuis R. Rodriguez 				     dev, GFP_KERNEL, NULL,
734061132d2SLuis R. Rodriguez 				     trigger_async_request_cb);
735061132d2SLuis R. Rodriguez 	if (rc) {
736061132d2SLuis R. Rodriguez 		pr_info("async load of '%s' failed: %d\n", name, rc);
737061132d2SLuis R. Rodriguez 		kfree(name);
738061132d2SLuis R. Rodriguez 		goto out;
739061132d2SLuis R. Rodriguez 	}
740061132d2SLuis R. Rodriguez 	/* Free 'name' ASAP, to test for race conditions */
741061132d2SLuis R. Rodriguez 	kfree(name);
742061132d2SLuis R. Rodriguez 
743061132d2SLuis R. Rodriguez 	wait_for_completion(&async_fw_done);
744061132d2SLuis R. Rodriguez 
745061132d2SLuis R. Rodriguez 	if (test_firmware) {
746061132d2SLuis R. Rodriguez 		pr_info("loaded: %zu\n", test_firmware->size);
747061132d2SLuis R. Rodriguez 		rc = count;
748061132d2SLuis R. Rodriguez 	} else {
749061132d2SLuis R. Rodriguez 		pr_err("failed to async load firmware\n");
750061132d2SLuis R. Rodriguez 		rc = -ENODEV;
751061132d2SLuis R. Rodriguez 	}
752061132d2SLuis R. Rodriguez 
753061132d2SLuis R. Rodriguez out:
754061132d2SLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
755061132d2SLuis R. Rodriguez 
756061132d2SLuis R. Rodriguez 	return rc;
757061132d2SLuis R. Rodriguez }
758061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback);
759061132d2SLuis R. Rodriguez 
760c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data)
761c92316bfSLuis R. Rodriguez {
762c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = data;
763c92316bfSLuis R. Rodriguez 
764c92316bfSLuis R. Rodriguez 	if (!req) {
765c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
766c92316bfSLuis R. Rodriguez 		return -EINVAL;
767c92316bfSLuis R. Rodriguez 	}
768c92316bfSLuis R. Rodriguez 
7697feebfa4SScott Branden 	if (test_fw_config->into_buf) {
7707feebfa4SScott Branden 		void *test_buf;
7717feebfa4SScott Branden 
7727feebfa4SScott Branden 		test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
7737feebfa4SScott Branden 		if (!test_buf)
7747feebfa4SScott Branden 			return -ENOSPC;
7757feebfa4SScott Branden 
7765d90e05cSScott Branden 		if (test_fw_config->partial)
7775d90e05cSScott Branden 			req->rc = request_partial_firmware_into_buf
7785d90e05cSScott Branden 						(&req->fw,
7797feebfa4SScott Branden 						 req->name,
7807feebfa4SScott Branden 						 req->dev,
7817feebfa4SScott Branden 						 test_buf,
7825d90e05cSScott Branden 						 test_fw_config->buf_size,
7835d90e05cSScott Branden 						 test_fw_config->file_offset);
7845d90e05cSScott Branden 		else
7855d90e05cSScott Branden 			req->rc = request_firmware_into_buf
7865d90e05cSScott Branden 						(&req->fw,
7875d90e05cSScott Branden 						 req->name,
7885d90e05cSScott Branden 						 req->dev,
7895d90e05cSScott Branden 						 test_buf,
7905d90e05cSScott Branden 						 test_fw_config->buf_size);
7917feebfa4SScott Branden 		if (!req->fw)
7927feebfa4SScott Branden 			kfree(test_buf);
7937feebfa4SScott Branden 	} else {
7947feebfa4SScott Branden 		req->rc = test_fw_config->req_firmware(&req->fw,
7957feebfa4SScott Branden 						       req->name,
7967feebfa4SScott Branden 						       req->dev);
7977feebfa4SScott Branden 	}
7987feebfa4SScott Branden 
799c92316bfSLuis R. Rodriguez 	if (req->rc) {
800c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync load failed: %d\n",
801c92316bfSLuis R. Rodriguez 			req->idx, req->rc);
802c92316bfSLuis R. Rodriguez 		if (!test_fw_config->test_result)
803c92316bfSLuis R. Rodriguez 			test_fw_config->test_result = req->rc;
804c92316bfSLuis R. Rodriguez 	} else if (req->fw) {
805c92316bfSLuis R. Rodriguez 		req->sent = true;
806c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync loaded %zu\n",
807c92316bfSLuis R. Rodriguez 			req->idx, req->fw->size);
808c92316bfSLuis R. Rodriguez 	}
809c92316bfSLuis R. Rodriguez 	complete(&req->completion);
810c92316bfSLuis R. Rodriguez 
811c92316bfSLuis R. Rodriguez 	req->task = NULL;
812c92316bfSLuis R. Rodriguez 
813c92316bfSLuis R. Rodriguez 	return 0;
814c92316bfSLuis R. Rodriguez }
815c92316bfSLuis R. Rodriguez 
816c92316bfSLuis R. Rodriguez /*
817c92316bfSLuis R. Rodriguez  * We use a kthread as otherwise the kernel serializes all our sync requests
818c92316bfSLuis R. Rodriguez  * and we would not be able to mimic batched requests on a sync call. Batched
819c92316bfSLuis R. Rodriguez  * requests on a sync call can for instance happen on a device driver when
820c92316bfSLuis R. Rodriguez  * multiple cards are used and firmware loading happens outside of probe.
821c92316bfSLuis R. Rodriguez  */
822c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev,
823c92316bfSLuis R. Rodriguez 					      struct device_attribute *attr,
824c92316bfSLuis R. Rodriguez 					      const char *buf, size_t count)
825c92316bfSLuis R. Rodriguez {
826c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
827c92316bfSLuis R. Rodriguez 	int rc;
828c92316bfSLuis R. Rodriguez 	u8 i;
829c92316bfSLuis R. Rodriguez 
830c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
831c92316bfSLuis R. Rodriguez 
832fad953ceSKees Cook 	test_fw_config->reqs =
833fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
834fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
835c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
836c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
837c92316bfSLuis R. Rodriguez 		goto out_unlock;
838c92316bfSLuis R. Rodriguez 	}
839c92316bfSLuis R. Rodriguez 
840c92316bfSLuis R. Rodriguez 	pr_info("batched sync firmware loading '%s' %u times\n",
841c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
842c92316bfSLuis R. Rodriguez 
843c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
844c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
845c92316bfSLuis R. Rodriguez 		req->fw = NULL;
846c92316bfSLuis R. Rodriguez 		req->idx = i;
847c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
848c92316bfSLuis R. Rodriguez 		req->dev = dev;
849c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
850c92316bfSLuis R. Rodriguez 		req->task = kthread_run(test_fw_run_batch_request, req,
851c92316bfSLuis R. Rodriguez 					     "%s-%u", KBUILD_MODNAME, req->idx);
852c92316bfSLuis R. Rodriguez 		if (!req->task || IS_ERR(req->task)) {
853c92316bfSLuis R. Rodriguez 			pr_err("Setting up thread %u failed\n", req->idx);
854c92316bfSLuis R. Rodriguez 			req->task = NULL;
855c92316bfSLuis R. Rodriguez 			rc = -ENOMEM;
856c92316bfSLuis R. Rodriguez 			goto out_bail;
857c92316bfSLuis R. Rodriguez 		}
858c92316bfSLuis R. Rodriguez 	}
859c92316bfSLuis R. Rodriguez 
860c92316bfSLuis R. Rodriguez 	rc = count;
861c92316bfSLuis R. Rodriguez 
862c92316bfSLuis R. Rodriguez 	/*
863c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
864c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
865c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
866c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
867c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
868c92316bfSLuis R. Rodriguez 	 */
869c92316bfSLuis R. Rodriguez 
870c92316bfSLuis R. Rodriguez out_bail:
871c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
872c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
873c92316bfSLuis R. Rodriguez 		if (req->task || req->sent)
874c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
875c92316bfSLuis R. Rodriguez 	}
876c92316bfSLuis R. Rodriguez 
877c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
878c92316bfSLuis R. Rodriguez 	if (rc < 0)
879c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
880c92316bfSLuis R. Rodriguez 
881c92316bfSLuis R. Rodriguez out_unlock:
882c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
883c92316bfSLuis R. Rodriguez 
884c92316bfSLuis R. Rodriguez 	return rc;
885c92316bfSLuis R. Rodriguez }
886c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests);
887c92316bfSLuis R. Rodriguez 
888c92316bfSLuis R. Rodriguez /*
889c92316bfSLuis R. Rodriguez  * We wait for each callback to return with the lock held, no need to lock here
890c92316bfSLuis R. Rodriguez  */
891c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context)
892c92316bfSLuis R. Rodriguez {
893c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = context;
894c92316bfSLuis R. Rodriguez 
895c92316bfSLuis R. Rodriguez 	if (!req) {
896c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
897c92316bfSLuis R. Rodriguez 		return;
898c92316bfSLuis R. Rodriguez 	}
899c92316bfSLuis R. Rodriguez 
900c92316bfSLuis R. Rodriguez 	/* forces *some* batched requests to queue up */
901c92316bfSLuis R. Rodriguez 	if (!req->idx)
902c92316bfSLuis R. Rodriguez 		ssleep(2);
903c92316bfSLuis R. Rodriguez 
904c92316bfSLuis R. Rodriguez 	req->fw = fw;
905c92316bfSLuis R. Rodriguez 
906c92316bfSLuis R. Rodriguez 	/*
907c92316bfSLuis R. Rodriguez 	 * Unfortunately the firmware API gives us nothing other than a null FW
908c92316bfSLuis R. Rodriguez 	 * if the firmware was not found on async requests.  Best we can do is
909c92316bfSLuis R. Rodriguez 	 * just assume -ENOENT. A better API would pass the actual return
910c92316bfSLuis R. Rodriguez 	 * value to the callback.
911c92316bfSLuis R. Rodriguez 	 */
912c92316bfSLuis R. Rodriguez 	if (!fw && !test_fw_config->test_result)
913c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -ENOENT;
914c92316bfSLuis R. Rodriguez 
915c92316bfSLuis R. Rodriguez 	complete(&req->completion);
916c92316bfSLuis R. Rodriguez }
917c92316bfSLuis R. Rodriguez 
918c92316bfSLuis R. Rodriguez static
919c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev,
920c92316bfSLuis R. Rodriguez 					     struct device_attribute *attr,
921c92316bfSLuis R. Rodriguez 					     const char *buf, size_t count)
922c92316bfSLuis R. Rodriguez {
923c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
924c92316bfSLuis R. Rodriguez 	bool send_uevent;
925c92316bfSLuis R. Rodriguez 	int rc;
926c92316bfSLuis R. Rodriguez 	u8 i;
927c92316bfSLuis R. Rodriguez 
928c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
929c92316bfSLuis R. Rodriguez 
930fad953ceSKees Cook 	test_fw_config->reqs =
931fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
932fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
933c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
934c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
935c92316bfSLuis R. Rodriguez 		goto out;
936c92316bfSLuis R. Rodriguez 	}
937c92316bfSLuis R. Rodriguez 
938c92316bfSLuis R. Rodriguez 	pr_info("batched loading '%s' custom fallback mechanism %u times\n",
939c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
940c92316bfSLuis R. Rodriguez 
941c92316bfSLuis R. Rodriguez 	send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG :
942c92316bfSLuis R. Rodriguez 		FW_ACTION_NOHOTPLUG;
943c92316bfSLuis R. Rodriguez 
944c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
945c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
946c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
947c92316bfSLuis R. Rodriguez 		req->fw = NULL;
948c92316bfSLuis R. Rodriguez 		req->idx = i;
949c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
950c92316bfSLuis R. Rodriguez 		rc = request_firmware_nowait(THIS_MODULE, send_uevent,
951c92316bfSLuis R. Rodriguez 					     req->name,
952c92316bfSLuis R. Rodriguez 					     dev, GFP_KERNEL, req,
953c92316bfSLuis R. Rodriguez 					     trigger_batched_cb);
954c92316bfSLuis R. Rodriguez 		if (rc) {
955c92316bfSLuis R. Rodriguez 			pr_info("#%u: batched async load failed setup: %d\n",
956c92316bfSLuis R. Rodriguez 				i, rc);
957c92316bfSLuis R. Rodriguez 			req->rc = rc;
958c92316bfSLuis R. Rodriguez 			goto out_bail;
959c92316bfSLuis R. Rodriguez 		} else
960c92316bfSLuis R. Rodriguez 			req->sent = true;
961c92316bfSLuis R. Rodriguez 	}
962c92316bfSLuis R. Rodriguez 
963c92316bfSLuis R. Rodriguez 	rc = count;
964c92316bfSLuis R. Rodriguez 
965c92316bfSLuis R. Rodriguez out_bail:
966c92316bfSLuis R. Rodriguez 
967c92316bfSLuis R. Rodriguez 	/*
968c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
969c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
970c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
971c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
972c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
973c92316bfSLuis R. Rodriguez 	 */
974c92316bfSLuis R. Rodriguez 
975c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
976c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
977c92316bfSLuis R. Rodriguez 		if (req->sent)
978c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
979c92316bfSLuis R. Rodriguez 	}
980c92316bfSLuis R. Rodriguez 
981c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
982c92316bfSLuis R. Rodriguez 	if (rc < 0)
983c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
984c92316bfSLuis R. Rodriguez 
985c92316bfSLuis R. Rodriguez out:
986c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
987c92316bfSLuis R. Rodriguez 
988c92316bfSLuis R. Rodriguez 	return rc;
989c92316bfSLuis R. Rodriguez }
990c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async);
991c92316bfSLuis R. Rodriguez 
992c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev,
993c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
994c92316bfSLuis R. Rodriguez 				char *buf)
995c92316bfSLuis R. Rodriguez {
996c92316bfSLuis R. Rodriguez 	return test_dev_config_show_int(buf, test_fw_config->test_result);
997c92316bfSLuis R. Rodriguez }
998c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result);
999c92316bfSLuis R. Rodriguez 
1000c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev,
1001c92316bfSLuis R. Rodriguez 					  struct device_attribute *attr,
1002c92316bfSLuis R. Rodriguez 					  const char *buf, size_t count)
1003c92316bfSLuis R. Rodriguez {
1004c92316bfSLuis R. Rodriguez 	test_release_all_firmware();
1005c92316bfSLuis R. Rodriguez 	return count;
1006c92316bfSLuis R. Rodriguez }
1007c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware);
1008c92316bfSLuis R. Rodriguez 
1009c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev,
1010c92316bfSLuis R. Rodriguez 				  struct device_attribute *attr,
1011c92316bfSLuis R. Rodriguez 				  char *buf)
1012c92316bfSLuis R. Rodriguez {
1013c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
1014c92316bfSLuis R. Rodriguez 	u8 idx;
1015c92316bfSLuis R. Rodriguez 	ssize_t rc = 0;
1016c92316bfSLuis R. Rodriguez 
1017c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
1018c92316bfSLuis R. Rodriguez 
1019c92316bfSLuis R. Rodriguez 	idx = test_fw_config->read_fw_idx;
1020c92316bfSLuis R. Rodriguez 	if (idx >= test_fw_config->num_requests) {
1021c92316bfSLuis R. Rodriguez 		rc = -ERANGE;
1022c92316bfSLuis R. Rodriguez 		goto out;
1023c92316bfSLuis R. Rodriguez 	}
1024c92316bfSLuis R. Rodriguez 
1025c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
1026c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
1027c92316bfSLuis R. Rodriguez 		goto out;
1028c92316bfSLuis R. Rodriguez 	}
1029c92316bfSLuis R. Rodriguez 
1030c92316bfSLuis R. Rodriguez 	req = &test_fw_config->reqs[idx];
1031c92316bfSLuis R. Rodriguez 	if (!req->fw) {
1032c92316bfSLuis R. Rodriguez 		pr_err("#%u: failed to async load firmware\n", idx);
1033c92316bfSLuis R. Rodriguez 		rc = -ENOENT;
1034c92316bfSLuis R. Rodriguez 		goto out;
1035c92316bfSLuis R. Rodriguez 	}
1036c92316bfSLuis R. Rodriguez 
1037c92316bfSLuis R. Rodriguez 	pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1038c92316bfSLuis R. Rodriguez 
1039c92316bfSLuis R. Rodriguez 	if (req->fw->size > PAGE_SIZE) {
1040c92316bfSLuis R. Rodriguez 		pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1041c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
10428bb0a886SColin Ian King 		goto out;
1043c92316bfSLuis R. Rodriguez 	}
1044c92316bfSLuis R. Rodriguez 	memcpy(buf, req->fw->data, req->fw->size);
1045c92316bfSLuis R. Rodriguez 
1046c92316bfSLuis R. Rodriguez 	rc = req->fw->size;
1047c92316bfSLuis R. Rodriguez out:
1048c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1049c92316bfSLuis R. Rodriguez 
1050c92316bfSLuis R. Rodriguez 	return rc;
1051c92316bfSLuis R. Rodriguez }
1052c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware);
1053c92316bfSLuis R. Rodriguez 
1054083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name)          &dev_attr_##name.attr
1055083a93b0SLuis R. Rodriguez 
1056083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = {
1057c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(reset),
1058c92316bfSLuis R. Rodriguez 
1059c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config),
1060c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_name),
1061c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_num_requests),
10627feebfa4SScott Branden 	TEST_FW_DEV_ATTR(config_into_buf),
10635d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_buf_size),
10645d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_file_offset),
10655d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_partial),
1066c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_sync_direct),
1067c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_send_uevent),
1068c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_read_fw_idx),
1069c92316bfSLuis R. Rodriguez 
1070c92316bfSLuis R. Rodriguez 	/* These don't use the config at all - they could be ported! */
1071083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_request),
1072083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_async_request),
1073061132d2SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_custom_fallback),
1074548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1075548193cbSHans de Goede 	TEST_FW_DEV_ATTR(trigger_request_platform),
1076548193cbSHans de Goede #endif
1077c92316bfSLuis R. Rodriguez 
1078c92316bfSLuis R. Rodriguez 	/* These use the config and can use the test_result */
1079c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests),
1080c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1081c92316bfSLuis R. Rodriguez 
1082c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(release_all_firmware),
1083c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(test_result),
1084c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(read_firmware),
1085083a93b0SLuis R. Rodriguez 	NULL,
1086083a93b0SLuis R. Rodriguez };
1087083a93b0SLuis R. Rodriguez 
1088083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev);
1089083a93b0SLuis R. Rodriguez 
109067fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = {
109167fd553cSLuis R. Rodriguez 	.minor          = MISC_DYNAMIC_MINOR,
109267fd553cSLuis R. Rodriguez 	.name           = "test_firmware",
109367fd553cSLuis R. Rodriguez 	.fops           = &test_fw_fops,
1094083a93b0SLuis R. Rodriguez 	.groups 	= test_dev_groups,
109567fd553cSLuis R. Rodriguez };
109667fd553cSLuis R. Rodriguez 
10970a8adf58SKees Cook static int __init test_firmware_init(void)
10980a8adf58SKees Cook {
10990a8adf58SKees Cook 	int rc;
11000a8adf58SKees Cook 
1101c92316bfSLuis R. Rodriguez 	test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1102c92316bfSLuis R. Rodriguez 	if (!test_fw_config)
1103c92316bfSLuis R. Rodriguez 		return -ENOMEM;
1104c92316bfSLuis R. Rodriguez 
1105c92316bfSLuis R. Rodriguez 	rc = __test_firmware_config_init();
1106d4fddac5SWenwen Wang 	if (rc) {
1107d4fddac5SWenwen Wang 		kfree(test_fw_config);
1108d4fddac5SWenwen Wang 		pr_err("could not init firmware test config: %d\n", rc);
1109c92316bfSLuis R. Rodriguez 		return rc;
1110d4fddac5SWenwen Wang 	}
1111c92316bfSLuis R. Rodriguez 
11120a8adf58SKees Cook 	rc = misc_register(&test_fw_misc_device);
11130a8adf58SKees Cook 	if (rc) {
1114c92316bfSLuis R. Rodriguez 		kfree(test_fw_config);
11150a8adf58SKees Cook 		pr_err("could not register misc device: %d\n", rc);
11160a8adf58SKees Cook 		return rc;
11170a8adf58SKees Cook 	}
1118eb910947SBrian Norris 
11190a8adf58SKees Cook 	pr_warn("interface ready\n");
11200a8adf58SKees Cook 
11210a8adf58SKees Cook 	return 0;
11220a8adf58SKees Cook }
11230a8adf58SKees Cook 
11240a8adf58SKees Cook module_init(test_firmware_init);
11250a8adf58SKees Cook 
11260a8adf58SKees Cook static void __exit test_firmware_exit(void)
11270a8adf58SKees Cook {
1128c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
11290a8adf58SKees Cook 	release_firmware(test_firmware);
11300a8adf58SKees Cook 	misc_deregister(&test_fw_misc_device);
1131c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
1132c92316bfSLuis R. Rodriguez 	kfree(test_fw_config);
1133c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1134c92316bfSLuis R. Rodriguez 
11350a8adf58SKees Cook 	pr_warn("removed interface\n");
11360a8adf58SKees Cook }
11370a8adf58SKees Cook 
11380a8adf58SKees Cook module_exit(test_firmware_exit);
11390a8adf58SKees Cook 
11400a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>");
11410a8adf58SKees Cook MODULE_LICENSE("GPL");
1142