xref: /linux-6.15/lib/test_firmware.c (revision d88bd098)
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
34a31ad463SRuss Weight #define TEST_UPLOAD_MAX_SIZE	SZ_2K
35a31ad463SRuss Weight #define TEST_UPLOAD_BLK_SIZE	37	/* Avoid powers of two in testing */
360a8adf58SKees Cook 
370a8adf58SKees Cook static DEFINE_MUTEX(test_fw_mutex);
380a8adf58SKees Cook static const struct firmware *test_firmware;
39a31ad463SRuss Weight static LIST_HEAD(test_upload_list);
400a8adf58SKees Cook 
41c92316bfSLuis R. Rodriguez struct test_batched_req {
42c92316bfSLuis R. Rodriguez 	u8 idx;
43c92316bfSLuis R. Rodriguez 	int rc;
44c92316bfSLuis R. Rodriguez 	bool sent;
45c92316bfSLuis R. Rodriguez 	const struct firmware *fw;
46c92316bfSLuis R. Rodriguez 	const char *name;
47c92316bfSLuis R. Rodriguez 	struct completion completion;
48c92316bfSLuis R. Rodriguez 	struct task_struct *task;
49c92316bfSLuis R. Rodriguez 	struct device *dev;
50c92316bfSLuis R. Rodriguez };
51c92316bfSLuis R. Rodriguez 
52c92316bfSLuis R. Rodriguez /**
53c92316bfSLuis R. Rodriguez  * test_config - represents configuration for the test for different triggers
54c92316bfSLuis R. Rodriguez  *
55c92316bfSLuis R. Rodriguez  * @name: the name of the firmware file to look for
567feebfa4SScott Branden  * @into_buf: when the into_buf is used if this is true
577feebfa4SScott Branden  *	request_firmware_into_buf() will be used instead.
585d90e05cSScott Branden  * @buf_size: size of buf to allocate when into_buf is true
595d90e05cSScott Branden  * @file_offset: file offset to request when calling request_firmware_into_buf
605d90e05cSScott Branden  * @partial: partial read opt when calling request_firmware_into_buf
61c92316bfSLuis R. Rodriguez  * @sync_direct: when the sync trigger is used if this is true
62c92316bfSLuis R. Rodriguez  *	request_firmware_direct() will be used instead.
63c92316bfSLuis R. Rodriguez  * @send_uevent: whether or not to send a uevent for async requests
64c92316bfSLuis R. Rodriguez  * @num_requests: number of requests to try per test case. This is trigger
65c92316bfSLuis R. Rodriguez  *	specific.
66c92316bfSLuis R. Rodriguez  * @reqs: stores all requests information
67c92316bfSLuis R. Rodriguez  * @read_fw_idx: index of thread from which we want to read firmware results
68c92316bfSLuis R. Rodriguez  *	from through the read_fw trigger.
69a31ad463SRuss Weight  * @upload_name: firmware name to be used with upload_read sysfs node
70c92316bfSLuis R. Rodriguez  * @test_result: a test may use this to collect the result from the call
71c92316bfSLuis R. Rodriguez  *	of the request_firmware*() calls used in their tests. In order of
72c92316bfSLuis R. Rodriguez  *	priority we always keep first any setup error. If no setup errors were
73c92316bfSLuis R. Rodriguez  *	found then we move on to the first error encountered while running the
74c92316bfSLuis R. Rodriguez  *	API. Note that for async calls this typically will be a successful
75c92316bfSLuis R. Rodriguez  *	result (0) unless of course you've used bogus parameters, or the system
76c92316bfSLuis R. Rodriguez  *	is out of memory.  In the async case the callback is expected to do a
77c92316bfSLuis R. Rodriguez  *	bit more homework to figure out what happened, unfortunately the only
78c92316bfSLuis R. Rodriguez  *	information passed today on error is the fact that no firmware was
79c92316bfSLuis R. Rodriguez  *	found so we can only assume -ENOENT on async calls if the firmware is
80c92316bfSLuis R. Rodriguez  *	NULL.
81c92316bfSLuis R. Rodriguez  *
82c92316bfSLuis R. Rodriguez  *	Errors you can expect:
83c92316bfSLuis R. Rodriguez  *
84c92316bfSLuis R. Rodriguez  *	API specific:
85c92316bfSLuis R. Rodriguez  *
86c92316bfSLuis R. Rodriguez  *	0:		success for sync, for async it means request was sent
87c92316bfSLuis R. Rodriguez  *	-EINVAL:	invalid parameters or request
88c92316bfSLuis R. Rodriguez  *	-ENOENT:	files not found
89c92316bfSLuis R. Rodriguez  *
90c92316bfSLuis R. Rodriguez  *	System environment:
91c92316bfSLuis R. Rodriguez  *
92c92316bfSLuis R. Rodriguez  *	-ENOMEM:	memory pressure on system
93c92316bfSLuis R. Rodriguez  *	-ENODEV:	out of number of devices to test
94c92316bfSLuis R. Rodriguez  *	-EINVAL:	an unexpected error has occurred
95c92316bfSLuis R. Rodriguez  * @req_firmware: if @sync_direct is true this is set to
96c92316bfSLuis R. Rodriguez  *	request_firmware_direct(), otherwise request_firmware()
97c92316bfSLuis R. Rodriguez  */
98c92316bfSLuis R. Rodriguez struct test_config {
99c92316bfSLuis R. Rodriguez 	char *name;
1007feebfa4SScott Branden 	bool into_buf;
1015d90e05cSScott Branden 	size_t buf_size;
1025d90e05cSScott Branden 	size_t file_offset;
1035d90e05cSScott Branden 	bool partial;
104c92316bfSLuis R. Rodriguez 	bool sync_direct;
105c92316bfSLuis R. Rodriguez 	bool send_uevent;
106c92316bfSLuis R. Rodriguez 	u8 num_requests;
107c92316bfSLuis R. Rodriguez 	u8 read_fw_idx;
108a31ad463SRuss Weight 	char *upload_name;
109c92316bfSLuis R. Rodriguez 
110c92316bfSLuis R. Rodriguez 	/*
111c92316bfSLuis R. Rodriguez 	 * These below don't belong her but we'll move them once we create
112c92316bfSLuis R. Rodriguez 	 * a struct fw_test_device and stuff the misc_dev under there later.
113c92316bfSLuis R. Rodriguez 	 */
114c92316bfSLuis R. Rodriguez 	struct test_batched_req *reqs;
115c92316bfSLuis R. Rodriguez 	int test_result;
116c92316bfSLuis R. Rodriguez 	int (*req_firmware)(const struct firmware **fw, const char *name,
117c92316bfSLuis R. Rodriguez 			    struct device *device);
118c92316bfSLuis R. Rodriguez };
119c92316bfSLuis R. Rodriguez 
1204a4e975bSRuss Weight struct upload_inject_err {
1214a4e975bSRuss Weight 	const char *prog;
1224a4e975bSRuss Weight 	enum fw_upload_err err_code;
1234a4e975bSRuss Weight };
1244a4e975bSRuss Weight 
125a31ad463SRuss Weight struct test_firmware_upload {
126a31ad463SRuss Weight 	char *name;
127a31ad463SRuss Weight 	struct list_head node;
128a31ad463SRuss Weight 	char *buf;
129a31ad463SRuss Weight 	size_t size;
130a31ad463SRuss Weight 	bool cancel_request;
1314a4e975bSRuss Weight 	struct upload_inject_err inject;
132a31ad463SRuss Weight 	struct fw_upload *fwl;
133a31ad463SRuss Weight };
134a31ad463SRuss Weight 
13576f8ab1bSWei Yongjun static struct test_config *test_fw_config;
136c92316bfSLuis R. Rodriguez 
137a31ad463SRuss Weight static struct test_firmware_upload *upload_lookup_name(const char *name)
138a31ad463SRuss Weight {
139a31ad463SRuss Weight 	struct test_firmware_upload *tst;
140a31ad463SRuss Weight 
141a31ad463SRuss Weight 	list_for_each_entry(tst, &test_upload_list, node)
142a31ad463SRuss Weight 		if (strncmp(name, tst->name, strlen(tst->name)) == 0)
143a31ad463SRuss Weight 			return tst;
144a31ad463SRuss Weight 
145a31ad463SRuss Weight 	return NULL;
146a31ad463SRuss Weight }
147a31ad463SRuss Weight 
1480a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
1490a8adf58SKees Cook 				 size_t size, loff_t *offset)
1500a8adf58SKees Cook {
1510a8adf58SKees Cook 	ssize_t rc = 0;
1520a8adf58SKees Cook 
1530a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
1540a8adf58SKees Cook 	if (test_firmware)
1550a8adf58SKees Cook 		rc = simple_read_from_buffer(buf, size, offset,
1560a8adf58SKees Cook 					     test_firmware->data,
1570a8adf58SKees Cook 					     test_firmware->size);
1580a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
1590a8adf58SKees Cook 	return rc;
1600a8adf58SKees Cook }
1610a8adf58SKees Cook 
1620a8adf58SKees Cook static const struct file_operations test_fw_fops = {
1630a8adf58SKees Cook 	.owner          = THIS_MODULE,
1640a8adf58SKees Cook 	.read           = test_fw_misc_read,
1650a8adf58SKees Cook };
1660a8adf58SKees Cook 
167c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void)
168c92316bfSLuis R. Rodriguez {
169c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
170c92316bfSLuis R. Rodriguez 	u8 i;
171c92316bfSLuis R. Rodriguez 
172c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs)
173c92316bfSLuis R. Rodriguez 		return;
174c92316bfSLuis R. Rodriguez 
175c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
176c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
177c92316bfSLuis R. Rodriguez 		if (req->fw)
178c92316bfSLuis R. Rodriguez 			release_firmware(req->fw);
179c92316bfSLuis R. Rodriguez 	}
180c92316bfSLuis R. Rodriguez 
181c92316bfSLuis R. Rodriguez 	vfree(test_fw_config->reqs);
182c92316bfSLuis R. Rodriguez 	test_fw_config->reqs = NULL;
183c92316bfSLuis R. Rodriguez }
184c92316bfSLuis R. Rodriguez 
185c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void)
186c92316bfSLuis R. Rodriguez {
187c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
188c92316bfSLuis R. Rodriguez 	__test_release_all_firmware();
189c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
190c92316bfSLuis R. Rodriguez }
191c92316bfSLuis R. Rodriguez 
192c92316bfSLuis R. Rodriguez 
193c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void)
194c92316bfSLuis R. Rodriguez {
195c92316bfSLuis R. Rodriguez 	__test_release_all_firmware();
196c92316bfSLuis R. Rodriguez 	kfree_const(test_fw_config->name);
197c92316bfSLuis R. Rodriguez 	test_fw_config->name = NULL;
198c92316bfSLuis R. Rodriguez }
199c92316bfSLuis R. Rodriguez 
200c92316bfSLuis R. Rodriguez /*
201c92316bfSLuis R. Rodriguez  * XXX: move to kstrncpy() once merged.
202c92316bfSLuis R. Rodriguez  *
203c92316bfSLuis R. Rodriguez  * Users should use kfree_const() when freeing these.
204c92316bfSLuis R. Rodriguez  */
205c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
206c92316bfSLuis R. Rodriguez {
207c92316bfSLuis R. Rodriguez 	*dst = kstrndup(name, count, gfp);
208c92316bfSLuis R. Rodriguez 	if (!*dst)
209c92316bfSLuis R. Rodriguez 		return -ENOSPC;
210c92316bfSLuis R. Rodriguez 	return count;
211c92316bfSLuis R. Rodriguez }
212c92316bfSLuis R. Rodriguez 
213c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void)
214c92316bfSLuis R. Rodriguez {
215c92316bfSLuis R. Rodriguez 	int ret;
216c92316bfSLuis R. Rodriguez 
217c92316bfSLuis R. Rodriguez 	ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
218c92316bfSLuis R. Rodriguez 			 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
219c92316bfSLuis R. Rodriguez 	if (ret < 0)
220c92316bfSLuis R. Rodriguez 		goto out;
221c92316bfSLuis R. Rodriguez 
222c92316bfSLuis R. Rodriguez 	test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
223c92316bfSLuis R. Rodriguez 	test_fw_config->send_uevent = true;
2247feebfa4SScott Branden 	test_fw_config->into_buf = false;
2255d90e05cSScott Branden 	test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
2265d90e05cSScott Branden 	test_fw_config->file_offset = 0;
2275d90e05cSScott Branden 	test_fw_config->partial = false;
228c92316bfSLuis R. Rodriguez 	test_fw_config->sync_direct = false;
229c92316bfSLuis R. Rodriguez 	test_fw_config->req_firmware = request_firmware;
230c92316bfSLuis R. Rodriguez 	test_fw_config->test_result = 0;
231c92316bfSLuis R. Rodriguez 	test_fw_config->reqs = NULL;
232a31ad463SRuss Weight 	test_fw_config->upload_name = NULL;
233c92316bfSLuis R. Rodriguez 
234c92316bfSLuis R. Rodriguez 	return 0;
235c92316bfSLuis R. Rodriguez 
236c92316bfSLuis R. Rodriguez out:
237c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
238c92316bfSLuis R. Rodriguez 	return ret;
239c92316bfSLuis R. Rodriguez }
240c92316bfSLuis R. Rodriguez 
241c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev,
242c92316bfSLuis R. Rodriguez 			   struct device_attribute *attr,
243c92316bfSLuis R. Rodriguez 			   const char *buf, size_t count)
244c92316bfSLuis R. Rodriguez {
245c92316bfSLuis R. Rodriguez 	int ret;
246c92316bfSLuis R. Rodriguez 
247c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
248c92316bfSLuis R. Rodriguez 
249c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
250c92316bfSLuis R. Rodriguez 
251c92316bfSLuis R. Rodriguez 	ret = __test_firmware_config_init();
252c92316bfSLuis R. Rodriguez 	if (ret < 0) {
253c92316bfSLuis R. Rodriguez 		ret = -ENOMEM;
254c92316bfSLuis R. Rodriguez 		pr_err("could not alloc settings for config trigger: %d\n",
255c92316bfSLuis R. Rodriguez 		       ret);
256c92316bfSLuis R. Rodriguez 		goto out;
257c92316bfSLuis R. Rodriguez 	}
258c92316bfSLuis R. Rodriguez 
259c92316bfSLuis R. Rodriguez 	pr_info("reset\n");
260c92316bfSLuis R. Rodriguez 	ret = count;
261c92316bfSLuis R. Rodriguez 
262c92316bfSLuis R. Rodriguez out:
263c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
264c92316bfSLuis R. Rodriguez 
265c92316bfSLuis R. Rodriguez 	return ret;
266c92316bfSLuis R. Rodriguez }
267c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset);
268c92316bfSLuis R. Rodriguez 
269c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev,
270c92316bfSLuis R. Rodriguez 			   struct device_attribute *attr,
271c92316bfSLuis R. Rodriguez 			   char *buf)
272c92316bfSLuis R. Rodriguez {
273c92316bfSLuis R. Rodriguez 	int len = 0;
274c92316bfSLuis R. Rodriguez 
275c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
276c92316bfSLuis R. Rodriguez 
277bd17cc5aSDan Carpenter 	len += scnprintf(buf, PAGE_SIZE - len,
278c92316bfSLuis R. Rodriguez 			"Custom trigger configuration for: %s\n",
279c92316bfSLuis R. Rodriguez 			dev_name(dev));
280c92316bfSLuis R. Rodriguez 
281c92316bfSLuis R. Rodriguez 	if (test_fw_config->name)
282bd17cc5aSDan Carpenter 		len += scnprintf(buf + len, PAGE_SIZE - len,
283c92316bfSLuis R. Rodriguez 				"name:\t%s\n",
284c92316bfSLuis R. Rodriguez 				test_fw_config->name);
285c92316bfSLuis R. Rodriguez 	else
286bd17cc5aSDan Carpenter 		len += scnprintf(buf + len, PAGE_SIZE - len,
287*d88bd098SColin Ian King 				"name:\tEMPTY\n");
288c92316bfSLuis R. Rodriguez 
289bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
290c92316bfSLuis R. Rodriguez 			"num_requests:\t%u\n", test_fw_config->num_requests);
291c92316bfSLuis R. Rodriguez 
292bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
293c92316bfSLuis R. Rodriguez 			"send_uevent:\t\t%s\n",
294c92316bfSLuis R. Rodriguez 			test_fw_config->send_uevent ?
2950733d839SShawn Guo 			"FW_ACTION_UEVENT" :
2960733d839SShawn Guo 			"FW_ACTION_NOUEVENT");
297bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
2987feebfa4SScott Branden 			"into_buf:\t\t%s\n",
2997feebfa4SScott Branden 			test_fw_config->into_buf ? "true" : "false");
3007feebfa4SScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
3015d90e05cSScott Branden 			"buf_size:\t%zu\n", test_fw_config->buf_size);
3025d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
3035d90e05cSScott Branden 			"file_offset:\t%zu\n", test_fw_config->file_offset);
3045d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
3055d90e05cSScott Branden 			"partial:\t\t%s\n",
3065d90e05cSScott Branden 			test_fw_config->partial ? "true" : "false");
3075d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
308c92316bfSLuis R. Rodriguez 			"sync_direct:\t\t%s\n",
309c92316bfSLuis R. Rodriguez 			test_fw_config->sync_direct ? "true" : "false");
310bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
311c92316bfSLuis R. Rodriguez 			"read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
312a31ad463SRuss Weight 	if (test_fw_config->upload_name)
313a31ad463SRuss Weight 		len += scnprintf(buf + len, PAGE_SIZE - len,
314a31ad463SRuss Weight 				"upload_name:\t%s\n",
315a31ad463SRuss Weight 				test_fw_config->upload_name);
316a31ad463SRuss Weight 	else
317a31ad463SRuss Weight 		len += scnprintf(buf + len, PAGE_SIZE - len,
318*d88bd098SColin Ian King 				"upload_name:\tEMPTY\n");
319c92316bfSLuis R. Rodriguez 
320c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
321c92316bfSLuis R. Rodriguez 
322c92316bfSLuis R. Rodriguez 	return len;
323c92316bfSLuis R. Rodriguez }
324c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config);
325c92316bfSLuis R. Rodriguez 
326c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev,
327c92316bfSLuis R. Rodriguez 				 struct device_attribute *attr,
328c92316bfSLuis R. Rodriguez 				 const char *buf, size_t count)
329c92316bfSLuis R. Rodriguez {
330c92316bfSLuis R. Rodriguez 	int ret;
331c92316bfSLuis R. Rodriguez 
332c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
333c92316bfSLuis R. Rodriguez 	kfree_const(test_fw_config->name);
334c92316bfSLuis R. Rodriguez 	ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
335c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
336c92316bfSLuis R. Rodriguez 
337c92316bfSLuis R. Rodriguez 	return ret;
338c92316bfSLuis R. Rodriguez }
339c92316bfSLuis R. Rodriguez 
340c92316bfSLuis R. Rodriguez /*
341c92316bfSLuis R. Rodriguez  * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
342c92316bfSLuis R. Rodriguez  */
343c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst,
344c92316bfSLuis R. Rodriguez 				    char *src)
345c92316bfSLuis R. Rodriguez {
346c92316bfSLuis R. Rodriguez 	int len;
347c92316bfSLuis R. Rodriguez 
348c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
349c92316bfSLuis R. Rodriguez 	len = snprintf(dst, PAGE_SIZE, "%s\n", src);
350c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
351c92316bfSLuis R. Rodriguez 
352c92316bfSLuis R. Rodriguez 	return len;
353c92316bfSLuis R. Rodriguez }
354c92316bfSLuis R. Rodriguez 
355c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size,
356c92316bfSLuis R. Rodriguez 				       bool *cfg)
357c92316bfSLuis R. Rodriguez {
358c92316bfSLuis R. Rodriguez 	int ret;
359c92316bfSLuis R. Rodriguez 
360c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
361c92316bfSLuis R. Rodriguez 	if (strtobool(buf, cfg) < 0)
362c92316bfSLuis R. Rodriguez 		ret = -EINVAL;
363c92316bfSLuis R. Rodriguez 	else
364c92316bfSLuis R. Rodriguez 		ret = size;
365c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
366c92316bfSLuis R. Rodriguez 
367c92316bfSLuis R. Rodriguez 	return ret;
368c92316bfSLuis R. Rodriguez }
369c92316bfSLuis R. Rodriguez 
37055623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val)
371c92316bfSLuis R. Rodriguez {
372c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
373c92316bfSLuis R. Rodriguez }
374c92316bfSLuis R. Rodriguez 
3755d90e05cSScott Branden static int test_dev_config_update_size_t(const char *buf,
3765d90e05cSScott Branden 					 size_t size,
3775d90e05cSScott Branden 					 size_t *cfg)
3785d90e05cSScott Branden {
3795d90e05cSScott Branden 	int ret;
3805d90e05cSScott Branden 	long new;
3815d90e05cSScott Branden 
3825d90e05cSScott Branden 	ret = kstrtol(buf, 10, &new);
3835d90e05cSScott Branden 	if (ret)
3845d90e05cSScott Branden 		return ret;
3855d90e05cSScott Branden 
3865d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
3875d90e05cSScott Branden 	*(size_t *)cfg = new;
3885d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
3895d90e05cSScott Branden 
3905d90e05cSScott Branden 	/* Always return full write size even if we didn't consume all */
3915d90e05cSScott Branden 	return size;
3925d90e05cSScott Branden }
3935d90e05cSScott Branden 
3945d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
3955d90e05cSScott Branden {
3965d90e05cSScott Branden 	return snprintf(buf, PAGE_SIZE, "%zu\n", val);
3975d90e05cSScott Branden }
3985d90e05cSScott Branden 
39955623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val)
400c92316bfSLuis R. Rodriguez {
401c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
402c92316bfSLuis R. Rodriguez }
403c92316bfSLuis R. Rodriguez 
404c92316bfSLuis R. Rodriguez static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
405c92316bfSLuis R. Rodriguez {
406506dfc99SAlexey Dobriyan 	u8 val;
407c92316bfSLuis R. Rodriguez 	int ret;
408c92316bfSLuis R. Rodriguez 
409506dfc99SAlexey Dobriyan 	ret = kstrtou8(buf, 10, &val);
410c92316bfSLuis R. Rodriguez 	if (ret)
411c92316bfSLuis R. Rodriguez 		return ret;
412c92316bfSLuis R. Rodriguez 
413c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
414506dfc99SAlexey Dobriyan 	*(u8 *)cfg = val;
415c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
416c92316bfSLuis R. Rodriguez 
417c92316bfSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
418c92316bfSLuis R. Rodriguez 	return size;
419c92316bfSLuis R. Rodriguez }
420c92316bfSLuis R. Rodriguez 
42155623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val)
422c92316bfSLuis R. Rodriguez {
423c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%u\n", val);
424c92316bfSLuis R. Rodriguez }
425c92316bfSLuis R. Rodriguez 
426c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev,
427c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
428c92316bfSLuis R. Rodriguez 				char *buf)
429c92316bfSLuis R. Rodriguez {
430c92316bfSLuis R. Rodriguez 	return config_test_show_str(buf, test_fw_config->name);
431c92316bfSLuis R. Rodriguez }
432b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name);
433c92316bfSLuis R. Rodriguez 
434a31ad463SRuss Weight static ssize_t config_upload_name_store(struct device *dev,
435a31ad463SRuss Weight 					struct device_attribute *attr,
436a31ad463SRuss Weight 					const char *buf, size_t count)
437a31ad463SRuss Weight {
438a31ad463SRuss Weight 	struct test_firmware_upload *tst;
439a31ad463SRuss Weight 	int ret = count;
440a31ad463SRuss Weight 
441a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
442a31ad463SRuss Weight 	tst = upload_lookup_name(buf);
443a31ad463SRuss Weight 	if (tst)
444a31ad463SRuss Weight 		test_fw_config->upload_name = tst->name;
445a31ad463SRuss Weight 	else
446a31ad463SRuss Weight 		ret = -EINVAL;
447a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
448a31ad463SRuss Weight 
449a31ad463SRuss Weight 	return ret;
450a31ad463SRuss Weight }
451a31ad463SRuss Weight 
452a31ad463SRuss Weight static ssize_t config_upload_name_show(struct device *dev,
453a31ad463SRuss Weight 				       struct device_attribute *attr,
454a31ad463SRuss Weight 				       char *buf)
455a31ad463SRuss Weight {
456a31ad463SRuss Weight 	return config_test_show_str(buf, test_fw_config->upload_name);
457a31ad463SRuss Weight }
458a31ad463SRuss Weight static DEVICE_ATTR_RW(config_upload_name);
459a31ad463SRuss Weight 
460c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev,
461c92316bfSLuis R. Rodriguez 					 struct device_attribute *attr,
462c92316bfSLuis R. Rodriguez 					 const char *buf, size_t count)
463c92316bfSLuis R. Rodriguez {
464c92316bfSLuis R. Rodriguez 	int rc;
465c92316bfSLuis R. Rodriguez 
466c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
467c92316bfSLuis R. Rodriguez 	if (test_fw_config->reqs) {
468c92316bfSLuis R. Rodriguez 		pr_err("Must call release_all_firmware prior to changing config\n");
469c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
470a5e19233SWei Yongjun 		mutex_unlock(&test_fw_mutex);
471c92316bfSLuis R. Rodriguez 		goto out;
472c92316bfSLuis R. Rodriguez 	}
473c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
474c92316bfSLuis R. Rodriguez 
475c92316bfSLuis R. Rodriguez 	rc = test_dev_config_update_u8(buf, count,
476c92316bfSLuis R. Rodriguez 				       &test_fw_config->num_requests);
477c92316bfSLuis R. Rodriguez 
478c92316bfSLuis R. Rodriguez out:
479c92316bfSLuis R. Rodriguez 	return rc;
480c92316bfSLuis R. Rodriguez }
481c92316bfSLuis R. Rodriguez 
482c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev,
483c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
484c92316bfSLuis R. Rodriguez 					char *buf)
485c92316bfSLuis R. Rodriguez {
486c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->num_requests);
487c92316bfSLuis R. Rodriguez }
488b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests);
489c92316bfSLuis R. Rodriguez 
4907feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev,
4917feebfa4SScott Branden 				     struct device_attribute *attr,
4927feebfa4SScott Branden 				     const char *buf, size_t count)
4937feebfa4SScott Branden {
4947feebfa4SScott Branden 	return test_dev_config_update_bool(buf,
4957feebfa4SScott Branden 					   count,
4967feebfa4SScott Branden 					   &test_fw_config->into_buf);
4977feebfa4SScott Branden }
4987feebfa4SScott Branden 
4997feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev,
5007feebfa4SScott Branden 				    struct device_attribute *attr,
5017feebfa4SScott Branden 				    char *buf)
5027feebfa4SScott Branden {
5037feebfa4SScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->into_buf);
5047feebfa4SScott Branden }
5057feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf);
5067feebfa4SScott Branden 
5075d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev,
5085d90e05cSScott Branden 				     struct device_attribute *attr,
5095d90e05cSScott Branden 				     const char *buf, size_t count)
5105d90e05cSScott Branden {
5115d90e05cSScott Branden 	int rc;
5125d90e05cSScott Branden 
5135d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
5145d90e05cSScott Branden 	if (test_fw_config->reqs) {
5155d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
5165d90e05cSScott Branden 		rc = -EINVAL;
5175d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
5185d90e05cSScott Branden 		goto out;
5195d90e05cSScott Branden 	}
5205d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
5215d90e05cSScott Branden 
5225d90e05cSScott Branden 	rc = test_dev_config_update_size_t(buf, count,
5235d90e05cSScott Branden 					   &test_fw_config->buf_size);
5245d90e05cSScott Branden 
5255d90e05cSScott Branden out:
5265d90e05cSScott Branden 	return rc;
5275d90e05cSScott Branden }
5285d90e05cSScott Branden 
5295d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev,
5305d90e05cSScott Branden 				    struct device_attribute *attr,
5315d90e05cSScott Branden 				    char *buf)
5325d90e05cSScott Branden {
5335d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
5345d90e05cSScott Branden }
5355d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size);
5365d90e05cSScott Branden 
5375d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev,
5385d90e05cSScott Branden 					struct device_attribute *attr,
5395d90e05cSScott Branden 					const char *buf, size_t count)
5405d90e05cSScott Branden {
5415d90e05cSScott Branden 	int rc;
5425d90e05cSScott Branden 
5435d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
5445d90e05cSScott Branden 	if (test_fw_config->reqs) {
5455d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
5465d90e05cSScott Branden 		rc = -EINVAL;
5475d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
5485d90e05cSScott Branden 		goto out;
5495d90e05cSScott Branden 	}
5505d90e05cSScott Branden 	mutex_unlock(&test_fw_mutex);
5515d90e05cSScott Branden 
5525d90e05cSScott Branden 	rc = test_dev_config_update_size_t(buf, count,
5535d90e05cSScott Branden 					   &test_fw_config->file_offset);
5545d90e05cSScott Branden 
5555d90e05cSScott Branden out:
5565d90e05cSScott Branden 	return rc;
5575d90e05cSScott Branden }
5585d90e05cSScott Branden 
5595d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev,
5605d90e05cSScott Branden 				       struct device_attribute *attr,
5615d90e05cSScott Branden 				       char *buf)
5625d90e05cSScott Branden {
5635d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
5645d90e05cSScott Branden }
5655d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset);
5665d90e05cSScott Branden 
5675d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev,
5685d90e05cSScott Branden 				    struct device_attribute *attr,
5695d90e05cSScott Branden 				    const char *buf, size_t count)
5705d90e05cSScott Branden {
5715d90e05cSScott Branden 	return test_dev_config_update_bool(buf,
5725d90e05cSScott Branden 					   count,
5735d90e05cSScott Branden 					   &test_fw_config->partial);
5745d90e05cSScott Branden }
5755d90e05cSScott Branden 
5765d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev,
5775d90e05cSScott Branden 				   struct device_attribute *attr,
5785d90e05cSScott Branden 				   char *buf)
5795d90e05cSScott Branden {
5805d90e05cSScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->partial);
5815d90e05cSScott Branden }
5825d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial);
5835d90e05cSScott Branden 
584c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev,
585c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
586c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
587c92316bfSLuis R. Rodriguez {
588c92316bfSLuis R. Rodriguez 	int rc = test_dev_config_update_bool(buf, count,
589c92316bfSLuis R. Rodriguez 					     &test_fw_config->sync_direct);
590c92316bfSLuis R. Rodriguez 
591c92316bfSLuis R. Rodriguez 	if (rc == count)
592c92316bfSLuis R. Rodriguez 		test_fw_config->req_firmware = test_fw_config->sync_direct ?
593c92316bfSLuis R. Rodriguez 				       request_firmware_direct :
594c92316bfSLuis R. Rodriguez 				       request_firmware;
595c92316bfSLuis R. Rodriguez 	return rc;
596c92316bfSLuis R. Rodriguez }
597c92316bfSLuis R. Rodriguez 
598c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev,
599c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
600c92316bfSLuis R. Rodriguez 				       char *buf)
601c92316bfSLuis R. Rodriguez {
602c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
603c92316bfSLuis R. Rodriguez }
604b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct);
605c92316bfSLuis R. Rodriguez 
606c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev,
607c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
608c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
609c92316bfSLuis R. Rodriguez {
610c92316bfSLuis R. Rodriguez 	return test_dev_config_update_bool(buf, count,
611c92316bfSLuis R. Rodriguez 					   &test_fw_config->send_uevent);
612c92316bfSLuis R. Rodriguez }
613c92316bfSLuis R. Rodriguez 
614c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev,
615c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
616c92316bfSLuis R. Rodriguez 				       char *buf)
617c92316bfSLuis R. Rodriguez {
618c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
619c92316bfSLuis R. Rodriguez }
620b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent);
621c92316bfSLuis R. Rodriguez 
622c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev,
623c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
624c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
625c92316bfSLuis R. Rodriguez {
626c92316bfSLuis R. Rodriguez 	return test_dev_config_update_u8(buf, count,
627c92316bfSLuis R. Rodriguez 					 &test_fw_config->read_fw_idx);
628c92316bfSLuis R. Rodriguez }
629c92316bfSLuis R. Rodriguez 
630c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev,
631c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
632c92316bfSLuis R. Rodriguez 				       char *buf)
633c92316bfSLuis R. Rodriguez {
634c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
635c92316bfSLuis R. Rodriguez }
636b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx);
637c92316bfSLuis R. Rodriguez 
638c92316bfSLuis R. Rodriguez 
6390a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev,
6400a8adf58SKees Cook 				     struct device_attribute *attr,
6410a8adf58SKees Cook 				     const char *buf, size_t count)
6420a8adf58SKees Cook {
6430a8adf58SKees Cook 	int rc;
6440a8adf58SKees Cook 	char *name;
6450a8adf58SKees Cook 
646be4a1326SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
6470a8adf58SKees Cook 	if (!name)
6480a8adf58SKees Cook 		return -ENOSPC;
6490a8adf58SKees Cook 
6500a8adf58SKees Cook 	pr_info("loading '%s'\n", name);
6510a8adf58SKees Cook 
6520a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
6530a8adf58SKees Cook 	release_firmware(test_firmware);
6540a8adf58SKees Cook 	test_firmware = NULL;
6550a8adf58SKees Cook 	rc = request_firmware(&test_firmware, name, dev);
65647e0bbb7SBrian Norris 	if (rc) {
6570a8adf58SKees Cook 		pr_info("load of '%s' failed: %d\n", name, rc);
65847e0bbb7SBrian Norris 		goto out;
65947e0bbb7SBrian Norris 	}
66047e0bbb7SBrian Norris 	pr_info("loaded: %zu\n", test_firmware->size);
66147e0bbb7SBrian Norris 	rc = count;
66247e0bbb7SBrian Norris 
66347e0bbb7SBrian Norris out:
6640a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
6650a8adf58SKees Cook 
6660a8adf58SKees Cook 	kfree(name);
6670a8adf58SKees Cook 
66847e0bbb7SBrian Norris 	return rc;
6690a8adf58SKees Cook }
6700a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request);
6710a8adf58SKees Cook 
672548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
673baaabecfSKees Cook extern struct list_head efi_embedded_fw_list;
674baaabecfSKees Cook extern bool efi_embedded_fw_checked;
675baaabecfSKees Cook 
676548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev,
677548193cbSHans de Goede 					      struct device_attribute *attr,
678548193cbSHans de Goede 					      const char *buf, size_t count)
679548193cbSHans de Goede {
680548193cbSHans de Goede 	static const u8 test_data[] = {
681548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
682548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
683548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
684548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
685548193cbSHans de Goede 	};
686548193cbSHans de Goede 	struct efi_embedded_fw efi_embedded_fw;
687548193cbSHans de Goede 	const struct firmware *firmware = NULL;
688baaabecfSKees Cook 	bool saved_efi_embedded_fw_checked;
689548193cbSHans de Goede 	char *name;
690548193cbSHans de Goede 	int rc;
691548193cbSHans de Goede 
692548193cbSHans de Goede 	name = kstrndup(buf, count, GFP_KERNEL);
693548193cbSHans de Goede 	if (!name)
694548193cbSHans de Goede 		return -ENOSPC;
695548193cbSHans de Goede 
696548193cbSHans de Goede 	pr_info("inserting test platform fw '%s'\n", name);
697548193cbSHans de Goede 	efi_embedded_fw.name = name;
698548193cbSHans de Goede 	efi_embedded_fw.data = (void *)test_data;
699548193cbSHans de Goede 	efi_embedded_fw.length = sizeof(test_data);
700548193cbSHans de Goede 	list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
701baaabecfSKees Cook 	saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
702baaabecfSKees Cook 	efi_embedded_fw_checked = true;
703548193cbSHans de Goede 
704548193cbSHans de Goede 	pr_info("loading '%s'\n", name);
705548193cbSHans de Goede 	rc = firmware_request_platform(&firmware, name, dev);
706548193cbSHans de Goede 	if (rc) {
707548193cbSHans de Goede 		pr_info("load of '%s' failed: %d\n", name, rc);
708548193cbSHans de Goede 		goto out;
709548193cbSHans de Goede 	}
710548193cbSHans de Goede 	if (firmware->size != sizeof(test_data) ||
711548193cbSHans de Goede 	    memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
712548193cbSHans de Goede 		pr_info("firmware contents mismatch for '%s'\n", name);
713548193cbSHans de Goede 		rc = -EINVAL;
714548193cbSHans de Goede 		goto out;
715548193cbSHans de Goede 	}
716548193cbSHans de Goede 	pr_info("loaded: %zu\n", firmware->size);
717548193cbSHans de Goede 	rc = count;
718548193cbSHans de Goede 
719548193cbSHans de Goede out:
720baaabecfSKees Cook 	efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
721548193cbSHans de Goede 	release_firmware(firmware);
722548193cbSHans de Goede 	list_del(&efi_embedded_fw.list);
723548193cbSHans de Goede 	kfree(name);
724548193cbSHans de Goede 
725548193cbSHans de Goede 	return rc;
726548193cbSHans de Goede }
727548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform);
728548193cbSHans de Goede #endif
729548193cbSHans de Goede 
730eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done);
731eb910947SBrian Norris 
732eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context)
733eb910947SBrian Norris {
734eb910947SBrian Norris 	test_firmware = fw;
735eb910947SBrian Norris 	complete(&async_fw_done);
736eb910947SBrian Norris }
737eb910947SBrian Norris 
738eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev,
739eb910947SBrian Norris 					   struct device_attribute *attr,
740eb910947SBrian Norris 					   const char *buf, size_t count)
741eb910947SBrian Norris {
742eb910947SBrian Norris 	int rc;
743eb910947SBrian Norris 	char *name;
744eb910947SBrian Norris 
745eb910947SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
746eb910947SBrian Norris 	if (!name)
747eb910947SBrian Norris 		return -ENOSPC;
748eb910947SBrian Norris 
749eb910947SBrian Norris 	pr_info("loading '%s'\n", name);
750eb910947SBrian Norris 
751eb910947SBrian Norris 	mutex_lock(&test_fw_mutex);
752eb910947SBrian Norris 	release_firmware(test_firmware);
753eb910947SBrian Norris 	test_firmware = NULL;
754eb910947SBrian Norris 	rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
755eb910947SBrian Norris 				     NULL, trigger_async_request_cb);
756eb910947SBrian Norris 	if (rc) {
757eb910947SBrian Norris 		pr_info("async load of '%s' failed: %d\n", name, rc);
758eb910947SBrian Norris 		kfree(name);
759eb910947SBrian Norris 		goto out;
760eb910947SBrian Norris 	}
761eb910947SBrian Norris 	/* Free 'name' ASAP, to test for race conditions */
762eb910947SBrian Norris 	kfree(name);
763eb910947SBrian Norris 
764eb910947SBrian Norris 	wait_for_completion(&async_fw_done);
765eb910947SBrian Norris 
766eb910947SBrian Norris 	if (test_firmware) {
767eb910947SBrian Norris 		pr_info("loaded: %zu\n", test_firmware->size);
768eb910947SBrian Norris 		rc = count;
769eb910947SBrian Norris 	} else {
770eb910947SBrian Norris 		pr_err("failed to async load firmware\n");
7717feebfa4SScott Branden 		rc = -ENOMEM;
772eb910947SBrian Norris 	}
773eb910947SBrian Norris 
774eb910947SBrian Norris out:
775eb910947SBrian Norris 	mutex_unlock(&test_fw_mutex);
776eb910947SBrian Norris 
777eb910947SBrian Norris 	return rc;
778eb910947SBrian Norris }
779eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request);
780eb910947SBrian Norris 
781061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev,
782061132d2SLuis R. Rodriguez 					     struct device_attribute *attr,
783061132d2SLuis R. Rodriguez 					     const char *buf, size_t count)
784061132d2SLuis R. Rodriguez {
785061132d2SLuis R. Rodriguez 	int rc;
786061132d2SLuis R. Rodriguez 	char *name;
787061132d2SLuis R. Rodriguez 
788061132d2SLuis R. Rodriguez 	name = kstrndup(buf, count, GFP_KERNEL);
789061132d2SLuis R. Rodriguez 	if (!name)
790061132d2SLuis R. Rodriguez 		return -ENOSPC;
791061132d2SLuis R. Rodriguez 
792061132d2SLuis R. Rodriguez 	pr_info("loading '%s' using custom fallback mechanism\n", name);
793061132d2SLuis R. Rodriguez 
794061132d2SLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
795061132d2SLuis R. Rodriguez 	release_firmware(test_firmware);
796061132d2SLuis R. Rodriguez 	test_firmware = NULL;
7970733d839SShawn Guo 	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, name,
798061132d2SLuis R. Rodriguez 				     dev, GFP_KERNEL, NULL,
799061132d2SLuis R. Rodriguez 				     trigger_async_request_cb);
800061132d2SLuis R. Rodriguez 	if (rc) {
801061132d2SLuis R. Rodriguez 		pr_info("async load of '%s' failed: %d\n", name, rc);
802061132d2SLuis R. Rodriguez 		kfree(name);
803061132d2SLuis R. Rodriguez 		goto out;
804061132d2SLuis R. Rodriguez 	}
805061132d2SLuis R. Rodriguez 	/* Free 'name' ASAP, to test for race conditions */
806061132d2SLuis R. Rodriguez 	kfree(name);
807061132d2SLuis R. Rodriguez 
808061132d2SLuis R. Rodriguez 	wait_for_completion(&async_fw_done);
809061132d2SLuis R. Rodriguez 
810061132d2SLuis R. Rodriguez 	if (test_firmware) {
811061132d2SLuis R. Rodriguez 		pr_info("loaded: %zu\n", test_firmware->size);
812061132d2SLuis R. Rodriguez 		rc = count;
813061132d2SLuis R. Rodriguez 	} else {
814061132d2SLuis R. Rodriguez 		pr_err("failed to async load firmware\n");
815061132d2SLuis R. Rodriguez 		rc = -ENODEV;
816061132d2SLuis R. Rodriguez 	}
817061132d2SLuis R. Rodriguez 
818061132d2SLuis R. Rodriguez out:
819061132d2SLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
820061132d2SLuis R. Rodriguez 
821061132d2SLuis R. Rodriguez 	return rc;
822061132d2SLuis R. Rodriguez }
823061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback);
824061132d2SLuis R. Rodriguez 
825c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data)
826c92316bfSLuis R. Rodriguez {
827c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = data;
828c92316bfSLuis R. Rodriguez 
829c92316bfSLuis R. Rodriguez 	if (!req) {
830c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
831c92316bfSLuis R. Rodriguez 		return -EINVAL;
832c92316bfSLuis R. Rodriguez 	}
833c92316bfSLuis R. Rodriguez 
8347feebfa4SScott Branden 	if (test_fw_config->into_buf) {
8357feebfa4SScott Branden 		void *test_buf;
8367feebfa4SScott Branden 
8377feebfa4SScott Branden 		test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
8387feebfa4SScott Branden 		if (!test_buf)
8397feebfa4SScott Branden 			return -ENOSPC;
8407feebfa4SScott Branden 
8415d90e05cSScott Branden 		if (test_fw_config->partial)
8425d90e05cSScott Branden 			req->rc = request_partial_firmware_into_buf
8435d90e05cSScott Branden 						(&req->fw,
8447feebfa4SScott Branden 						 req->name,
8457feebfa4SScott Branden 						 req->dev,
8467feebfa4SScott Branden 						 test_buf,
8475d90e05cSScott Branden 						 test_fw_config->buf_size,
8485d90e05cSScott Branden 						 test_fw_config->file_offset);
8495d90e05cSScott Branden 		else
8505d90e05cSScott Branden 			req->rc = request_firmware_into_buf
8515d90e05cSScott Branden 						(&req->fw,
8525d90e05cSScott Branden 						 req->name,
8535d90e05cSScott Branden 						 req->dev,
8545d90e05cSScott Branden 						 test_buf,
8555d90e05cSScott Branden 						 test_fw_config->buf_size);
8567feebfa4SScott Branden 		if (!req->fw)
8577feebfa4SScott Branden 			kfree(test_buf);
8587feebfa4SScott Branden 	} else {
8597feebfa4SScott Branden 		req->rc = test_fw_config->req_firmware(&req->fw,
8607feebfa4SScott Branden 						       req->name,
8617feebfa4SScott Branden 						       req->dev);
8627feebfa4SScott Branden 	}
8637feebfa4SScott Branden 
864c92316bfSLuis R. Rodriguez 	if (req->rc) {
865c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync load failed: %d\n",
866c92316bfSLuis R. Rodriguez 			req->idx, req->rc);
867c92316bfSLuis R. Rodriguez 		if (!test_fw_config->test_result)
868c92316bfSLuis R. Rodriguez 			test_fw_config->test_result = req->rc;
869c92316bfSLuis R. Rodriguez 	} else if (req->fw) {
870c92316bfSLuis R. Rodriguez 		req->sent = true;
871c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync loaded %zu\n",
872c92316bfSLuis R. Rodriguez 			req->idx, req->fw->size);
873c92316bfSLuis R. Rodriguez 	}
874c92316bfSLuis R. Rodriguez 	complete(&req->completion);
875c92316bfSLuis R. Rodriguez 
876c92316bfSLuis R. Rodriguez 	req->task = NULL;
877c92316bfSLuis R. Rodriguez 
878c92316bfSLuis R. Rodriguez 	return 0;
879c92316bfSLuis R. Rodriguez }
880c92316bfSLuis R. Rodriguez 
881c92316bfSLuis R. Rodriguez /*
882c92316bfSLuis R. Rodriguez  * We use a kthread as otherwise the kernel serializes all our sync requests
883c92316bfSLuis R. Rodriguez  * and we would not be able to mimic batched requests on a sync call. Batched
884c92316bfSLuis R. Rodriguez  * requests on a sync call can for instance happen on a device driver when
885c92316bfSLuis R. Rodriguez  * multiple cards are used and firmware loading happens outside of probe.
886c92316bfSLuis R. Rodriguez  */
887c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev,
888c92316bfSLuis R. Rodriguez 					      struct device_attribute *attr,
889c92316bfSLuis R. Rodriguez 					      const char *buf, size_t count)
890c92316bfSLuis R. Rodriguez {
891c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
892c92316bfSLuis R. Rodriguez 	int rc;
893c92316bfSLuis R. Rodriguez 	u8 i;
894c92316bfSLuis R. Rodriguez 
895c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
896c92316bfSLuis R. Rodriguez 
897fad953ceSKees Cook 	test_fw_config->reqs =
898fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
899fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
900c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
901c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
902c92316bfSLuis R. Rodriguez 		goto out_unlock;
903c92316bfSLuis R. Rodriguez 	}
904c92316bfSLuis R. Rodriguez 
905c92316bfSLuis R. Rodriguez 	pr_info("batched sync firmware loading '%s' %u times\n",
906c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
907c92316bfSLuis R. Rodriguez 
908c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
909c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
910c92316bfSLuis R. Rodriguez 		req->fw = NULL;
911c92316bfSLuis R. Rodriguez 		req->idx = i;
912c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
913c92316bfSLuis R. Rodriguez 		req->dev = dev;
914c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
915c92316bfSLuis R. Rodriguez 		req->task = kthread_run(test_fw_run_batch_request, req,
916c92316bfSLuis R. Rodriguez 					     "%s-%u", KBUILD_MODNAME, req->idx);
917c92316bfSLuis R. Rodriguez 		if (!req->task || IS_ERR(req->task)) {
918c92316bfSLuis R. Rodriguez 			pr_err("Setting up thread %u failed\n", req->idx);
919c92316bfSLuis R. Rodriguez 			req->task = NULL;
920c92316bfSLuis R. Rodriguez 			rc = -ENOMEM;
921c92316bfSLuis R. Rodriguez 			goto out_bail;
922c92316bfSLuis R. Rodriguez 		}
923c92316bfSLuis R. Rodriguez 	}
924c92316bfSLuis R. Rodriguez 
925c92316bfSLuis R. Rodriguez 	rc = count;
926c92316bfSLuis R. Rodriguez 
927c92316bfSLuis R. Rodriguez 	/*
928c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
929c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
930c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
931c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
932c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
933c92316bfSLuis R. Rodriguez 	 */
934c92316bfSLuis R. Rodriguez 
935c92316bfSLuis R. Rodriguez out_bail:
936c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
937c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
938c92316bfSLuis R. Rodriguez 		if (req->task || req->sent)
939c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
940c92316bfSLuis R. Rodriguez 	}
941c92316bfSLuis R. Rodriguez 
942c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
943c92316bfSLuis R. Rodriguez 	if (rc < 0)
944c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
945c92316bfSLuis R. Rodriguez 
946c92316bfSLuis R. Rodriguez out_unlock:
947c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
948c92316bfSLuis R. Rodriguez 
949c92316bfSLuis R. Rodriguez 	return rc;
950c92316bfSLuis R. Rodriguez }
951c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests);
952c92316bfSLuis R. Rodriguez 
953c92316bfSLuis R. Rodriguez /*
954c92316bfSLuis R. Rodriguez  * We wait for each callback to return with the lock held, no need to lock here
955c92316bfSLuis R. Rodriguez  */
956c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context)
957c92316bfSLuis R. Rodriguez {
958c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = context;
959c92316bfSLuis R. Rodriguez 
960c92316bfSLuis R. Rodriguez 	if (!req) {
961c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
962c92316bfSLuis R. Rodriguez 		return;
963c92316bfSLuis R. Rodriguez 	}
964c92316bfSLuis R. Rodriguez 
965c92316bfSLuis R. Rodriguez 	/* forces *some* batched requests to queue up */
966c92316bfSLuis R. Rodriguez 	if (!req->idx)
967c92316bfSLuis R. Rodriguez 		ssleep(2);
968c92316bfSLuis R. Rodriguez 
969c92316bfSLuis R. Rodriguez 	req->fw = fw;
970c92316bfSLuis R. Rodriguez 
971c92316bfSLuis R. Rodriguez 	/*
972c92316bfSLuis R. Rodriguez 	 * Unfortunately the firmware API gives us nothing other than a null FW
973c92316bfSLuis R. Rodriguez 	 * if the firmware was not found on async requests.  Best we can do is
974c92316bfSLuis R. Rodriguez 	 * just assume -ENOENT. A better API would pass the actual return
975c92316bfSLuis R. Rodriguez 	 * value to the callback.
976c92316bfSLuis R. Rodriguez 	 */
977c92316bfSLuis R. Rodriguez 	if (!fw && !test_fw_config->test_result)
978c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -ENOENT;
979c92316bfSLuis R. Rodriguez 
980c92316bfSLuis R. Rodriguez 	complete(&req->completion);
981c92316bfSLuis R. Rodriguez }
982c92316bfSLuis R. Rodriguez 
983c92316bfSLuis R. Rodriguez static
984c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev,
985c92316bfSLuis R. Rodriguez 					     struct device_attribute *attr,
986c92316bfSLuis R. Rodriguez 					     const char *buf, size_t count)
987c92316bfSLuis R. Rodriguez {
988c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
989c92316bfSLuis R. Rodriguez 	bool send_uevent;
990c92316bfSLuis R. Rodriguez 	int rc;
991c92316bfSLuis R. Rodriguez 	u8 i;
992c92316bfSLuis R. Rodriguez 
993c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
994c92316bfSLuis R. Rodriguez 
995fad953ceSKees Cook 	test_fw_config->reqs =
996fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
997fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
998c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
999c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
1000c92316bfSLuis R. Rodriguez 		goto out;
1001c92316bfSLuis R. Rodriguez 	}
1002c92316bfSLuis R. Rodriguez 
1003c92316bfSLuis R. Rodriguez 	pr_info("batched loading '%s' custom fallback mechanism %u times\n",
1004c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
1005c92316bfSLuis R. Rodriguez 
10060733d839SShawn Guo 	send_uevent = test_fw_config->send_uevent ? FW_ACTION_UEVENT :
10070733d839SShawn Guo 		FW_ACTION_NOUEVENT;
1008c92316bfSLuis R. Rodriguez 
1009c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
1010c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
1011c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
1012c92316bfSLuis R. Rodriguez 		req->fw = NULL;
1013c92316bfSLuis R. Rodriguez 		req->idx = i;
1014c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
1015c92316bfSLuis R. Rodriguez 		rc = request_firmware_nowait(THIS_MODULE, send_uevent,
1016c92316bfSLuis R. Rodriguez 					     req->name,
1017c92316bfSLuis R. Rodriguez 					     dev, GFP_KERNEL, req,
1018c92316bfSLuis R. Rodriguez 					     trigger_batched_cb);
1019c92316bfSLuis R. Rodriguez 		if (rc) {
1020c92316bfSLuis R. Rodriguez 			pr_info("#%u: batched async load failed setup: %d\n",
1021c92316bfSLuis R. Rodriguez 				i, rc);
1022c92316bfSLuis R. Rodriguez 			req->rc = rc;
1023c92316bfSLuis R. Rodriguez 			goto out_bail;
1024c92316bfSLuis R. Rodriguez 		} else
1025c92316bfSLuis R. Rodriguez 			req->sent = true;
1026c92316bfSLuis R. Rodriguez 	}
1027c92316bfSLuis R. Rodriguez 
1028c92316bfSLuis R. Rodriguez 	rc = count;
1029c92316bfSLuis R. Rodriguez 
1030c92316bfSLuis R. Rodriguez out_bail:
1031c92316bfSLuis R. Rodriguez 
1032c92316bfSLuis R. Rodriguez 	/*
1033c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
1034c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
1035c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
1036c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
1037c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
1038c92316bfSLuis R. Rodriguez 	 */
1039c92316bfSLuis R. Rodriguez 
1040c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
1041c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
1042c92316bfSLuis R. Rodriguez 		if (req->sent)
1043c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
1044c92316bfSLuis R. Rodriguez 	}
1045c92316bfSLuis R. Rodriguez 
1046c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
1047c92316bfSLuis R. Rodriguez 	if (rc < 0)
1048c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
1049c92316bfSLuis R. Rodriguez 
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_WO(trigger_batched_requests_async);
1056c92316bfSLuis R. Rodriguez 
1057a31ad463SRuss Weight static void upload_release(struct test_firmware_upload *tst)
1058a31ad463SRuss Weight {
1059a31ad463SRuss Weight 	firmware_upload_unregister(tst->fwl);
1060a31ad463SRuss Weight 	kfree(tst->buf);
1061a31ad463SRuss Weight 	kfree(tst->name);
1062a31ad463SRuss Weight 	kfree(tst);
1063a31ad463SRuss Weight }
1064a31ad463SRuss Weight 
1065a31ad463SRuss Weight static void upload_release_all(void)
1066a31ad463SRuss Weight {
1067a31ad463SRuss Weight 	struct test_firmware_upload *tst, *tmp;
1068a31ad463SRuss Weight 
1069a31ad463SRuss Weight 	list_for_each_entry_safe(tst, tmp, &test_upload_list, node) {
1070a31ad463SRuss Weight 		list_del(&tst->node);
1071a31ad463SRuss Weight 		upload_release(tst);
1072a31ad463SRuss Weight 	}
1073a31ad463SRuss Weight 	test_fw_config->upload_name = NULL;
1074a31ad463SRuss Weight }
1075a31ad463SRuss Weight 
10764a4e975bSRuss Weight /*
10774a4e975bSRuss Weight  * This table is replicated from .../firmware_loader/sysfs_upload.c
10784a4e975bSRuss Weight  * and needs to be kept in sync.
10794a4e975bSRuss Weight  */
10804a4e975bSRuss Weight static const char * const fw_upload_err_str[] = {
10814a4e975bSRuss Weight 	[FW_UPLOAD_ERR_NONE]	     = "none",
10824a4e975bSRuss Weight 	[FW_UPLOAD_ERR_HW_ERROR]     = "hw-error",
10834a4e975bSRuss Weight 	[FW_UPLOAD_ERR_TIMEOUT]	     = "timeout",
10844a4e975bSRuss Weight 	[FW_UPLOAD_ERR_CANCELED]     = "user-abort",
10854a4e975bSRuss Weight 	[FW_UPLOAD_ERR_BUSY]	     = "device-busy",
10864a4e975bSRuss Weight 	[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
10874a4e975bSRuss Weight 	[FW_UPLOAD_ERR_RW_ERROR]     = "read-write-error",
10884a4e975bSRuss Weight 	[FW_UPLOAD_ERR_WEAROUT]	     = "flash-wearout",
10894a4e975bSRuss Weight };
10904a4e975bSRuss Weight 
10914a4e975bSRuss Weight static void upload_err_inject_error(struct test_firmware_upload *tst,
10924a4e975bSRuss Weight 				    const u8 *p, const char *prog)
10934a4e975bSRuss Weight {
10944a4e975bSRuss Weight 	enum fw_upload_err err;
10954a4e975bSRuss Weight 
10964a4e975bSRuss Weight 	for (err = FW_UPLOAD_ERR_NONE + 1; err < FW_UPLOAD_ERR_MAX; err++) {
10974a4e975bSRuss Weight 		if (strncmp(p, fw_upload_err_str[err],
10984a4e975bSRuss Weight 			    strlen(fw_upload_err_str[err])) == 0) {
10994a4e975bSRuss Weight 			tst->inject.prog = prog;
11004a4e975bSRuss Weight 			tst->inject.err_code = err;
11014a4e975bSRuss Weight 			return;
11024a4e975bSRuss Weight 		}
11034a4e975bSRuss Weight 	}
11044a4e975bSRuss Weight }
11054a4e975bSRuss Weight 
11064a4e975bSRuss Weight static void upload_err_inject_prog(struct test_firmware_upload *tst,
11074a4e975bSRuss Weight 				   const u8 *p)
11084a4e975bSRuss Weight {
11094a4e975bSRuss Weight 	static const char * const progs[] = {
11104a4e975bSRuss Weight 		"preparing:", "transferring:", "programming:"
11114a4e975bSRuss Weight 	};
11124a4e975bSRuss Weight 	int i;
11134a4e975bSRuss Weight 
11144a4e975bSRuss Weight 	for (i = 0; i < ARRAY_SIZE(progs); i++) {
11154a4e975bSRuss Weight 		if (strncmp(p, progs[i], strlen(progs[i])) == 0) {
11164a4e975bSRuss Weight 			upload_err_inject_error(tst, p + strlen(progs[i]),
11174a4e975bSRuss Weight 						progs[i]);
11184a4e975bSRuss Weight 			return;
11194a4e975bSRuss Weight 		}
11204a4e975bSRuss Weight 	}
11214a4e975bSRuss Weight }
11224a4e975bSRuss Weight 
11234a4e975bSRuss Weight #define FIVE_MINUTES_MS	(5 * 60 * 1000)
11244a4e975bSRuss Weight static enum fw_upload_err
11254a4e975bSRuss Weight fw_upload_wait_on_cancel(struct test_firmware_upload *tst)
11264a4e975bSRuss Weight {
11274a4e975bSRuss Weight 	int ms_delay;
11284a4e975bSRuss Weight 
11294a4e975bSRuss Weight 	for (ms_delay = 0; ms_delay < FIVE_MINUTES_MS; ms_delay += 100) {
11304a4e975bSRuss Weight 		msleep(100);
11314a4e975bSRuss Weight 		if (tst->cancel_request)
11324a4e975bSRuss Weight 			return FW_UPLOAD_ERR_CANCELED;
11334a4e975bSRuss Weight 	}
11344a4e975bSRuss Weight 	return FW_UPLOAD_ERR_NONE;
11354a4e975bSRuss Weight }
11364a4e975bSRuss Weight 
1137a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_prepare(struct fw_upload *fwl,
1138a31ad463SRuss Weight 						 const u8 *data, u32 size)
1139a31ad463SRuss Weight {
1140a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
11414a4e975bSRuss Weight 	enum fw_upload_err ret = FW_UPLOAD_ERR_NONE;
11424a4e975bSRuss Weight 	const char *progress = "preparing:";
1143a31ad463SRuss Weight 
1144a31ad463SRuss Weight 	tst->cancel_request = false;
1145a31ad463SRuss Weight 
11464a4e975bSRuss Weight 	if (!size || size > TEST_UPLOAD_MAX_SIZE) {
11474a4e975bSRuss Weight 		ret = FW_UPLOAD_ERR_INVALID_SIZE;
11484a4e975bSRuss Weight 		goto err_out;
11494a4e975bSRuss Weight 	}
11504a4e975bSRuss Weight 
11514a4e975bSRuss Weight 	if (strncmp(data, "inject:", strlen("inject:")) == 0)
11524a4e975bSRuss Weight 		upload_err_inject_prog(tst, data + strlen("inject:"));
1153a31ad463SRuss Weight 
1154a31ad463SRuss Weight 	memset(tst->buf, 0, TEST_UPLOAD_MAX_SIZE);
1155a31ad463SRuss Weight 	tst->size = size;
1156a31ad463SRuss Weight 
11574a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
11584a4e975bSRuss Weight 	    strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
1159a31ad463SRuss Weight 		return FW_UPLOAD_ERR_NONE;
11604a4e975bSRuss Weight 
11614a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
11624a4e975bSRuss Weight 		ret = fw_upload_wait_on_cancel(tst);
11634a4e975bSRuss Weight 	else
11644a4e975bSRuss Weight 		ret = tst->inject.err_code;
11654a4e975bSRuss Weight 
11664a4e975bSRuss Weight err_out:
11674a4e975bSRuss Weight 	/*
11684a4e975bSRuss Weight 	 * The cleanup op only executes if the prepare op succeeds.
11694a4e975bSRuss Weight 	 * If the prepare op fails, it must do it's own clean-up.
11704a4e975bSRuss Weight 	 */
11714a4e975bSRuss Weight 	tst->inject.err_code = FW_UPLOAD_ERR_NONE;
11724a4e975bSRuss Weight 	tst->inject.prog = NULL;
11734a4e975bSRuss Weight 
11744a4e975bSRuss Weight 	return ret;
1175a31ad463SRuss Weight }
1176a31ad463SRuss Weight 
1177a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl,
1178a31ad463SRuss Weight 					       const u8 *data, u32 offset,
1179a31ad463SRuss Weight 					       u32 size, u32 *written)
1180a31ad463SRuss Weight {
1181a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
11824a4e975bSRuss Weight 	const char *progress = "transferring:";
1183a31ad463SRuss Weight 	u32 blk_size;
1184a31ad463SRuss Weight 
1185a31ad463SRuss Weight 	if (tst->cancel_request)
1186a31ad463SRuss Weight 		return FW_UPLOAD_ERR_CANCELED;
1187a31ad463SRuss Weight 
1188a31ad463SRuss Weight 	blk_size = min_t(u32, TEST_UPLOAD_BLK_SIZE, size);
1189a31ad463SRuss Weight 	memcpy(tst->buf + offset, data + offset, blk_size);
1190a31ad463SRuss Weight 
1191a31ad463SRuss Weight 	*written = blk_size;
11924a4e975bSRuss Weight 
11934a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
11944a4e975bSRuss Weight 	    strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
1195a31ad463SRuss Weight 		return FW_UPLOAD_ERR_NONE;
11964a4e975bSRuss Weight 
11974a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
11984a4e975bSRuss Weight 		return fw_upload_wait_on_cancel(tst);
11994a4e975bSRuss Weight 
12004a4e975bSRuss Weight 	return tst->inject.err_code;
1201a31ad463SRuss Weight }
1202a31ad463SRuss Weight 
1203a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_complete(struct fw_upload *fwl)
1204a31ad463SRuss Weight {
1205a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
12064a4e975bSRuss Weight 	const char *progress = "programming:";
1207a31ad463SRuss Weight 
1208a31ad463SRuss Weight 	if (tst->cancel_request)
1209a31ad463SRuss Weight 		return FW_UPLOAD_ERR_CANCELED;
1210a31ad463SRuss Weight 
12114a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
12124a4e975bSRuss Weight 	    strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
1213a31ad463SRuss Weight 		return FW_UPLOAD_ERR_NONE;
12144a4e975bSRuss Weight 
12154a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
12164a4e975bSRuss Weight 		return fw_upload_wait_on_cancel(tst);
12174a4e975bSRuss Weight 
12184a4e975bSRuss Weight 	return tst->inject.err_code;
1219a31ad463SRuss Weight }
1220a31ad463SRuss Weight 
1221a31ad463SRuss Weight static void test_fw_upload_cancel(struct fw_upload *fwl)
1222a31ad463SRuss Weight {
1223a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
1224a31ad463SRuss Weight 
1225a31ad463SRuss Weight 	tst->cancel_request = true;
1226a31ad463SRuss Weight }
1227a31ad463SRuss Weight 
12284a4e975bSRuss Weight static void test_fw_cleanup(struct fw_upload *fwl)
12294a4e975bSRuss Weight {
12304a4e975bSRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
12314a4e975bSRuss Weight 
12324a4e975bSRuss Weight 	tst->inject.err_code = FW_UPLOAD_ERR_NONE;
12334a4e975bSRuss Weight 	tst->inject.prog = NULL;
12344a4e975bSRuss Weight }
12354a4e975bSRuss Weight 
1236a31ad463SRuss Weight static const struct fw_upload_ops upload_test_ops = {
1237a31ad463SRuss Weight 	.prepare = test_fw_upload_prepare,
1238a31ad463SRuss Weight 	.write = test_fw_upload_write,
1239a31ad463SRuss Weight 	.poll_complete = test_fw_upload_complete,
1240a31ad463SRuss Weight 	.cancel = test_fw_upload_cancel,
12414a4e975bSRuss Weight 	.cleanup = test_fw_cleanup
1242a31ad463SRuss Weight };
1243a31ad463SRuss Weight 
1244a31ad463SRuss Weight static ssize_t upload_register_store(struct device *dev,
1245a31ad463SRuss Weight 				     struct device_attribute *attr,
1246a31ad463SRuss Weight 				     const char *buf, size_t count)
1247a31ad463SRuss Weight {
1248a31ad463SRuss Weight 	struct test_firmware_upload *tst;
1249a31ad463SRuss Weight 	struct fw_upload *fwl;
1250a31ad463SRuss Weight 	char *name;
1251a31ad463SRuss Weight 	int ret;
1252a31ad463SRuss Weight 
1253a31ad463SRuss Weight 	name = kstrndup(buf, count, GFP_KERNEL);
1254a31ad463SRuss Weight 	if (!name)
1255a31ad463SRuss Weight 		return -ENOMEM;
1256a31ad463SRuss Weight 
1257a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
1258a31ad463SRuss Weight 	tst = upload_lookup_name(name);
1259a31ad463SRuss Weight 	if (tst) {
1260a31ad463SRuss Weight 		ret = -EEXIST;
1261a31ad463SRuss Weight 		goto free_name;
1262a31ad463SRuss Weight 	}
1263a31ad463SRuss Weight 
1264a31ad463SRuss Weight 	tst = kzalloc(sizeof(*tst), GFP_KERNEL);
1265a31ad463SRuss Weight 	if (!tst) {
1266a31ad463SRuss Weight 		ret = -ENOMEM;
1267a31ad463SRuss Weight 		goto free_name;
1268a31ad463SRuss Weight 	}
1269a31ad463SRuss Weight 
1270a31ad463SRuss Weight 	tst->name = name;
1271a31ad463SRuss Weight 	tst->buf = kzalloc(TEST_UPLOAD_MAX_SIZE, GFP_KERNEL);
1272a31ad463SRuss Weight 	if (!tst->buf) {
1273a31ad463SRuss Weight 		ret = -ENOMEM;
1274a31ad463SRuss Weight 		goto free_tst;
1275a31ad463SRuss Weight 	}
1276a31ad463SRuss Weight 
1277a31ad463SRuss Weight 	fwl = firmware_upload_register(THIS_MODULE, dev, tst->name,
1278a31ad463SRuss Weight 				       &upload_test_ops, tst);
1279a31ad463SRuss Weight 	if (IS_ERR(fwl)) {
1280a31ad463SRuss Weight 		ret = PTR_ERR(fwl);
1281a31ad463SRuss Weight 		goto free_buf;
1282a31ad463SRuss Weight 	}
1283a31ad463SRuss Weight 
1284a31ad463SRuss Weight 	tst->fwl = fwl;
1285a31ad463SRuss Weight 	list_add_tail(&tst->node, &test_upload_list);
1286a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1287a31ad463SRuss Weight 	return count;
1288a31ad463SRuss Weight 
1289a31ad463SRuss Weight free_buf:
1290a31ad463SRuss Weight 	kfree(tst->buf);
1291a31ad463SRuss Weight 
1292a31ad463SRuss Weight free_tst:
1293a31ad463SRuss Weight 	kfree(tst);
1294a31ad463SRuss Weight 
1295a31ad463SRuss Weight free_name:
1296a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1297a31ad463SRuss Weight 	kfree(name);
1298a31ad463SRuss Weight 
1299a31ad463SRuss Weight 	return ret;
1300a31ad463SRuss Weight }
1301a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_register);
1302a31ad463SRuss Weight 
1303a31ad463SRuss Weight static ssize_t upload_unregister_store(struct device *dev,
1304a31ad463SRuss Weight 				       struct device_attribute *attr,
1305a31ad463SRuss Weight 				       const char *buf, size_t count)
1306a31ad463SRuss Weight {
1307a31ad463SRuss Weight 	struct test_firmware_upload *tst;
1308a31ad463SRuss Weight 	int ret = count;
1309a31ad463SRuss Weight 
1310a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
1311a31ad463SRuss Weight 	tst = upload_lookup_name(buf);
1312a31ad463SRuss Weight 	if (!tst) {
1313a31ad463SRuss Weight 		ret = -EINVAL;
1314a31ad463SRuss Weight 		goto out;
1315a31ad463SRuss Weight 	}
1316a31ad463SRuss Weight 
1317a31ad463SRuss Weight 	if (test_fw_config->upload_name == tst->name)
1318a31ad463SRuss Weight 		test_fw_config->upload_name = NULL;
1319a31ad463SRuss Weight 
1320a31ad463SRuss Weight 	list_del(&tst->node);
1321a31ad463SRuss Weight 	upload_release(tst);
1322a31ad463SRuss Weight 
1323a31ad463SRuss Weight out:
1324a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1325a31ad463SRuss Weight 	return ret;
1326a31ad463SRuss Weight }
1327a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_unregister);
1328a31ad463SRuss Weight 
1329c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev,
1330c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
1331c92316bfSLuis R. Rodriguez 				char *buf)
1332c92316bfSLuis R. Rodriguez {
1333c92316bfSLuis R. Rodriguez 	return test_dev_config_show_int(buf, test_fw_config->test_result);
1334c92316bfSLuis R. Rodriguez }
1335c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result);
1336c92316bfSLuis R. Rodriguez 
1337c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev,
1338c92316bfSLuis R. Rodriguez 					  struct device_attribute *attr,
1339c92316bfSLuis R. Rodriguez 					  const char *buf, size_t count)
1340c92316bfSLuis R. Rodriguez {
1341c92316bfSLuis R. Rodriguez 	test_release_all_firmware();
1342c92316bfSLuis R. Rodriguez 	return count;
1343c92316bfSLuis R. Rodriguez }
1344c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware);
1345c92316bfSLuis R. Rodriguez 
1346c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev,
1347c92316bfSLuis R. Rodriguez 				  struct device_attribute *attr,
1348c92316bfSLuis R. Rodriguez 				  char *buf)
1349c92316bfSLuis R. Rodriguez {
1350c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
1351c92316bfSLuis R. Rodriguez 	u8 idx;
1352c92316bfSLuis R. Rodriguez 	ssize_t rc = 0;
1353c92316bfSLuis R. Rodriguez 
1354c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
1355c92316bfSLuis R. Rodriguez 
1356c92316bfSLuis R. Rodriguez 	idx = test_fw_config->read_fw_idx;
1357c92316bfSLuis R. Rodriguez 	if (idx >= test_fw_config->num_requests) {
1358c92316bfSLuis R. Rodriguez 		rc = -ERANGE;
1359c92316bfSLuis R. Rodriguez 		goto out;
1360c92316bfSLuis R. Rodriguez 	}
1361c92316bfSLuis R. Rodriguez 
1362c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
1363c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
1364c92316bfSLuis R. Rodriguez 		goto out;
1365c92316bfSLuis R. Rodriguez 	}
1366c92316bfSLuis R. Rodriguez 
1367c92316bfSLuis R. Rodriguez 	req = &test_fw_config->reqs[idx];
1368c92316bfSLuis R. Rodriguez 	if (!req->fw) {
1369c92316bfSLuis R. Rodriguez 		pr_err("#%u: failed to async load firmware\n", idx);
1370c92316bfSLuis R. Rodriguez 		rc = -ENOENT;
1371c92316bfSLuis R. Rodriguez 		goto out;
1372c92316bfSLuis R. Rodriguez 	}
1373c92316bfSLuis R. Rodriguez 
1374c92316bfSLuis R. Rodriguez 	pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1375c92316bfSLuis R. Rodriguez 
1376c92316bfSLuis R. Rodriguez 	if (req->fw->size > PAGE_SIZE) {
1377c92316bfSLuis R. Rodriguez 		pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1378c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
13798bb0a886SColin Ian King 		goto out;
1380c92316bfSLuis R. Rodriguez 	}
1381c92316bfSLuis R. Rodriguez 	memcpy(buf, req->fw->data, req->fw->size);
1382c92316bfSLuis R. Rodriguez 
1383c92316bfSLuis R. Rodriguez 	rc = req->fw->size;
1384c92316bfSLuis R. Rodriguez out:
1385c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1386c92316bfSLuis R. Rodriguez 
1387c92316bfSLuis R. Rodriguez 	return rc;
1388c92316bfSLuis R. Rodriguez }
1389c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware);
1390c92316bfSLuis R. Rodriguez 
1391a31ad463SRuss Weight static ssize_t upload_read_show(struct device *dev,
1392a31ad463SRuss Weight 				struct device_attribute *attr,
1393a31ad463SRuss Weight 				char *buf)
1394a31ad463SRuss Weight {
1395185b29c6SDan Carpenter 	struct test_firmware_upload *tst = NULL;
1396185b29c6SDan Carpenter 	struct test_firmware_upload *tst_iter;
1397a31ad463SRuss Weight 	int ret = -EINVAL;
1398a31ad463SRuss Weight 
1399a31ad463SRuss Weight 	if (!test_fw_config->upload_name) {
1400a31ad463SRuss Weight 		pr_err("Set config_upload_name before using upload_read\n");
1401a31ad463SRuss Weight 		return -EINVAL;
1402a31ad463SRuss Weight 	}
1403a31ad463SRuss Weight 
1404a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
1405185b29c6SDan Carpenter 	list_for_each_entry(tst_iter, &test_upload_list, node)
1406185b29c6SDan Carpenter 		if (tst_iter->name == test_fw_config->upload_name) {
1407185b29c6SDan Carpenter 			tst = tst_iter;
1408a31ad463SRuss Weight 			break;
1409185b29c6SDan Carpenter 		}
1410a31ad463SRuss Weight 
1411185b29c6SDan Carpenter 	if (!tst) {
1412a31ad463SRuss Weight 		pr_err("Firmware name not found: %s\n",
1413a31ad463SRuss Weight 		       test_fw_config->upload_name);
1414a31ad463SRuss Weight 		goto out;
1415a31ad463SRuss Weight 	}
1416a31ad463SRuss Weight 
1417a31ad463SRuss Weight 	if (tst->size > PAGE_SIZE) {
1418a31ad463SRuss Weight 		pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1419a31ad463SRuss Weight 		goto out;
1420a31ad463SRuss Weight 	}
1421a31ad463SRuss Weight 
1422a31ad463SRuss Weight 	memcpy(buf, tst->buf, tst->size);
1423a31ad463SRuss Weight 	ret = tst->size;
1424a31ad463SRuss Weight out:
1425a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1426a31ad463SRuss Weight 	return ret;
1427a31ad463SRuss Weight }
1428a31ad463SRuss Weight static DEVICE_ATTR_RO(upload_read);
1429a31ad463SRuss Weight 
1430083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name)          &dev_attr_##name.attr
1431083a93b0SLuis R. Rodriguez 
1432083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = {
1433c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(reset),
1434c92316bfSLuis R. Rodriguez 
1435c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config),
1436c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_name),
1437c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_num_requests),
14387feebfa4SScott Branden 	TEST_FW_DEV_ATTR(config_into_buf),
14395d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_buf_size),
14405d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_file_offset),
14415d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_partial),
1442c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_sync_direct),
1443c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_send_uevent),
1444c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_read_fw_idx),
1445a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(config_upload_name),
1446c92316bfSLuis R. Rodriguez 
1447c92316bfSLuis R. Rodriguez 	/* These don't use the config at all - they could be ported! */
1448083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_request),
1449083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_async_request),
1450061132d2SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_custom_fallback),
1451548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1452548193cbSHans de Goede 	TEST_FW_DEV_ATTR(trigger_request_platform),
1453548193cbSHans de Goede #endif
1454c92316bfSLuis R. Rodriguez 
1455c92316bfSLuis R. Rodriguez 	/* These use the config and can use the test_result */
1456c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests),
1457c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1458c92316bfSLuis R. Rodriguez 
1459c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(release_all_firmware),
1460c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(test_result),
1461c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(read_firmware),
1462a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(upload_read),
1463a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(upload_register),
1464a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(upload_unregister),
1465083a93b0SLuis R. Rodriguez 	NULL,
1466083a93b0SLuis R. Rodriguez };
1467083a93b0SLuis R. Rodriguez 
1468083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev);
1469083a93b0SLuis R. Rodriguez 
147067fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = {
147167fd553cSLuis R. Rodriguez 	.minor          = MISC_DYNAMIC_MINOR,
147267fd553cSLuis R. Rodriguez 	.name           = "test_firmware",
147367fd553cSLuis R. Rodriguez 	.fops           = &test_fw_fops,
1474083a93b0SLuis R. Rodriguez 	.groups 	= test_dev_groups,
147567fd553cSLuis R. Rodriguez };
147667fd553cSLuis R. Rodriguez 
14770a8adf58SKees Cook static int __init test_firmware_init(void)
14780a8adf58SKees Cook {
14790a8adf58SKees Cook 	int rc;
14800a8adf58SKees Cook 
1481c92316bfSLuis R. Rodriguez 	test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1482c92316bfSLuis R. Rodriguez 	if (!test_fw_config)
1483c92316bfSLuis R. Rodriguez 		return -ENOMEM;
1484c92316bfSLuis R. Rodriguez 
1485c92316bfSLuis R. Rodriguez 	rc = __test_firmware_config_init();
1486d4fddac5SWenwen Wang 	if (rc) {
1487d4fddac5SWenwen Wang 		kfree(test_fw_config);
1488d4fddac5SWenwen Wang 		pr_err("could not init firmware test config: %d\n", rc);
1489c92316bfSLuis R. Rodriguez 		return rc;
1490d4fddac5SWenwen Wang 	}
1491c92316bfSLuis R. Rodriguez 
14920a8adf58SKees Cook 	rc = misc_register(&test_fw_misc_device);
14930a8adf58SKees Cook 	if (rc) {
1494c92316bfSLuis R. Rodriguez 		kfree(test_fw_config);
14950a8adf58SKees Cook 		pr_err("could not register misc device: %d\n", rc);
14960a8adf58SKees Cook 		return rc;
14970a8adf58SKees Cook 	}
1498eb910947SBrian Norris 
14990a8adf58SKees Cook 	pr_warn("interface ready\n");
15000a8adf58SKees Cook 
15010a8adf58SKees Cook 	return 0;
15020a8adf58SKees Cook }
15030a8adf58SKees Cook 
15040a8adf58SKees Cook module_init(test_firmware_init);
15050a8adf58SKees Cook 
15060a8adf58SKees Cook static void __exit test_firmware_exit(void)
15070a8adf58SKees Cook {
1508c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
15090a8adf58SKees Cook 	release_firmware(test_firmware);
15100a8adf58SKees Cook 	misc_deregister(&test_fw_misc_device);
1511a31ad463SRuss Weight 	upload_release_all();
1512c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
1513c92316bfSLuis R. Rodriguez 	kfree(test_fw_config);
1514c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1515c92316bfSLuis R. Rodriguez 
15160a8adf58SKees Cook 	pr_warn("removed interface\n");
15170a8adf58SKees Cook }
15180a8adf58SKees Cook 
15190a8adf58SKees Cook module_exit(test_firmware_exit);
15200a8adf58SKees Cook 
15210a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>");
15220a8adf58SKees Cook MODULE_LICENSE("GPL");
1523