xref: /linux-6.15/lib/test_firmware.c (revision be37bed7)
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>
25f7d85515SChristophe JAILLET #include <linux/kstrtox.h>
26c92316bfSLuis R. Rodriguez #include <linux/kthread.h>
27514c6032SRandy Dunlap #include <linux/vmalloc.h>
28548193cbSHans de Goede #include <linux/efi_embedded_fw.h>
29c92316bfSLuis R. Rodriguez 
30baaabecfSKees Cook MODULE_IMPORT_NS(TEST_FIRMWARE);
31baaabecfSKees Cook 
32c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NAME	"test-firmware.bin"
33c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NUM_REQS	4
347feebfa4SScott Branden #define TEST_FIRMWARE_BUF_SIZE	SZ_1K
35a31ad463SRuss Weight #define TEST_UPLOAD_MAX_SIZE	SZ_2K
36a31ad463SRuss Weight #define TEST_UPLOAD_BLK_SIZE	37	/* Avoid powers of two in testing */
370a8adf58SKees Cook 
380a8adf58SKees Cook static DEFINE_MUTEX(test_fw_mutex);
390a8adf58SKees Cook static const struct firmware *test_firmware;
40a31ad463SRuss Weight static LIST_HEAD(test_upload_list);
410a8adf58SKees Cook 
42c92316bfSLuis R. Rodriguez struct test_batched_req {
43c92316bfSLuis R. Rodriguez 	u8 idx;
44c92316bfSLuis R. Rodriguez 	int rc;
45c92316bfSLuis R. Rodriguez 	bool sent;
46c92316bfSLuis R. Rodriguez 	const struct firmware *fw;
47c92316bfSLuis R. Rodriguez 	const char *name;
48c92316bfSLuis R. Rodriguez 	struct completion completion;
49c92316bfSLuis R. Rodriguez 	struct task_struct *task;
50c92316bfSLuis R. Rodriguez 	struct device *dev;
51c92316bfSLuis R. Rodriguez };
52c92316bfSLuis R. Rodriguez 
53c92316bfSLuis R. Rodriguez /**
541ad5288fSRandy Dunlap  * struct test_config - represents configuration for the test for different triggers
55c92316bfSLuis R. Rodriguez  *
56c92316bfSLuis R. Rodriguez  * @name: the name of the firmware file to look for
577feebfa4SScott Branden  * @into_buf: when the into_buf is used if this is true
587feebfa4SScott Branden  *	request_firmware_into_buf() will be used instead.
595d90e05cSScott Branden  * @buf_size: size of buf to allocate when into_buf is true
605d90e05cSScott Branden  * @file_offset: file offset to request when calling request_firmware_into_buf
615d90e05cSScott Branden  * @partial: partial read opt when calling request_firmware_into_buf
62c92316bfSLuis R. Rodriguez  * @sync_direct: when the sync trigger is used if this is true
63c92316bfSLuis R. Rodriguez  *	request_firmware_direct() will be used instead.
64c92316bfSLuis R. Rodriguez  * @send_uevent: whether or not to send a uevent for async requests
65c92316bfSLuis R. Rodriguez  * @num_requests: number of requests to try per test case. This is trigger
66c92316bfSLuis R. Rodriguez  *	specific.
67c92316bfSLuis R. Rodriguez  * @reqs: stores all requests information
68c92316bfSLuis R. Rodriguez  * @read_fw_idx: index of thread from which we want to read firmware results
69c92316bfSLuis R. Rodriguez  *	from through the read_fw trigger.
70a31ad463SRuss Weight  * @upload_name: firmware name to be used with upload_read sysfs node
71c92316bfSLuis R. Rodriguez  * @test_result: a test may use this to collect the result from the call
72c92316bfSLuis R. Rodriguez  *	of the request_firmware*() calls used in their tests. In order of
73c92316bfSLuis R. Rodriguez  *	priority we always keep first any setup error. If no setup errors were
74c92316bfSLuis R. Rodriguez  *	found then we move on to the first error encountered while running the
75c92316bfSLuis R. Rodriguez  *	API. Note that for async calls this typically will be a successful
76c92316bfSLuis R. Rodriguez  *	result (0) unless of course you've used bogus parameters, or the system
77c92316bfSLuis R. Rodriguez  *	is out of memory.  In the async case the callback is expected to do a
78c92316bfSLuis R. Rodriguez  *	bit more homework to figure out what happened, unfortunately the only
79c92316bfSLuis R. Rodriguez  *	information passed today on error is the fact that no firmware was
80c92316bfSLuis R. Rodriguez  *	found so we can only assume -ENOENT on async calls if the firmware is
81c92316bfSLuis R. Rodriguez  *	NULL.
82c92316bfSLuis R. Rodriguez  *
83c92316bfSLuis R. Rodriguez  *	Errors you can expect:
84c92316bfSLuis R. Rodriguez  *
85c92316bfSLuis R. Rodriguez  *	API specific:
86c92316bfSLuis R. Rodriguez  *
87c92316bfSLuis R. Rodriguez  *	0:		success for sync, for async it means request was sent
88c92316bfSLuis R. Rodriguez  *	-EINVAL:	invalid parameters or request
89c92316bfSLuis R. Rodriguez  *	-ENOENT:	files not found
90c92316bfSLuis R. Rodriguez  *
91c92316bfSLuis R. Rodriguez  *	System environment:
92c92316bfSLuis R. Rodriguez  *
93c92316bfSLuis R. Rodriguez  *	-ENOMEM:	memory pressure on system
94c92316bfSLuis R. Rodriguez  *	-ENODEV:	out of number of devices to test
95c92316bfSLuis R. Rodriguez  *	-EINVAL:	an unexpected error has occurred
96c92316bfSLuis R. Rodriguez  * @req_firmware: if @sync_direct is true this is set to
97c92316bfSLuis R. Rodriguez  *	request_firmware_direct(), otherwise request_firmware()
98c92316bfSLuis R. Rodriguez  */
99c92316bfSLuis R. Rodriguez struct test_config {
100c92316bfSLuis R. Rodriguez 	char *name;
1017feebfa4SScott Branden 	bool into_buf;
1025d90e05cSScott Branden 	size_t buf_size;
1035d90e05cSScott Branden 	size_t file_offset;
1045d90e05cSScott Branden 	bool partial;
105c92316bfSLuis R. Rodriguez 	bool sync_direct;
106c92316bfSLuis R. Rodriguez 	bool send_uevent;
107c92316bfSLuis R. Rodriguez 	u8 num_requests;
108c92316bfSLuis R. Rodriguez 	u8 read_fw_idx;
109a31ad463SRuss Weight 	char *upload_name;
110c92316bfSLuis R. Rodriguez 
111c92316bfSLuis R. Rodriguez 	/*
112c92316bfSLuis R. Rodriguez 	 * These below don't belong her but we'll move them once we create
113c92316bfSLuis R. Rodriguez 	 * a struct fw_test_device and stuff the misc_dev under there later.
114c92316bfSLuis R. Rodriguez 	 */
115c92316bfSLuis R. Rodriguez 	struct test_batched_req *reqs;
116c92316bfSLuis R. Rodriguez 	int test_result;
117c92316bfSLuis R. Rodriguez 	int (*req_firmware)(const struct firmware **fw, const char *name,
118c92316bfSLuis R. Rodriguez 			    struct device *device);
119c92316bfSLuis R. Rodriguez };
120c92316bfSLuis R. Rodriguez 
1214a4e975bSRuss Weight struct upload_inject_err {
1224a4e975bSRuss Weight 	const char *prog;
1234a4e975bSRuss Weight 	enum fw_upload_err err_code;
1244a4e975bSRuss Weight };
1254a4e975bSRuss Weight 
126a31ad463SRuss Weight struct test_firmware_upload {
127a31ad463SRuss Weight 	char *name;
128a31ad463SRuss Weight 	struct list_head node;
129a31ad463SRuss Weight 	char *buf;
130a31ad463SRuss Weight 	size_t size;
131a31ad463SRuss Weight 	bool cancel_request;
1324a4e975bSRuss Weight 	struct upload_inject_err inject;
133a31ad463SRuss Weight 	struct fw_upload *fwl;
134a31ad463SRuss Weight };
135a31ad463SRuss Weight 
13676f8ab1bSWei Yongjun static struct test_config *test_fw_config;
137c92316bfSLuis R. Rodriguez 
138a31ad463SRuss Weight static struct test_firmware_upload *upload_lookup_name(const char *name)
139a31ad463SRuss Weight {
140a31ad463SRuss Weight 	struct test_firmware_upload *tst;
141a31ad463SRuss Weight 
142a31ad463SRuss Weight 	list_for_each_entry(tst, &test_upload_list, node)
143a31ad463SRuss Weight 		if (strncmp(name, tst->name, strlen(tst->name)) == 0)
144a31ad463SRuss Weight 			return tst;
145a31ad463SRuss Weight 
146a31ad463SRuss Weight 	return NULL;
147a31ad463SRuss Weight }
148a31ad463SRuss Weight 
1490a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
1500a8adf58SKees Cook 				 size_t size, loff_t *offset)
1510a8adf58SKees Cook {
1520a8adf58SKees Cook 	ssize_t rc = 0;
1530a8adf58SKees Cook 
1540a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
1550a8adf58SKees Cook 	if (test_firmware)
1560a8adf58SKees Cook 		rc = simple_read_from_buffer(buf, size, offset,
1570a8adf58SKees Cook 					     test_firmware->data,
1580a8adf58SKees Cook 					     test_firmware->size);
1590a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
1600a8adf58SKees Cook 	return rc;
1610a8adf58SKees Cook }
1620a8adf58SKees Cook 
1630a8adf58SKees Cook static const struct file_operations test_fw_fops = {
1640a8adf58SKees Cook 	.owner          = THIS_MODULE,
1650a8adf58SKees Cook 	.read           = test_fw_misc_read,
1660a8adf58SKees Cook };
1670a8adf58SKees Cook 
168c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void)
169c92316bfSLuis R. Rodriguez {
170c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
171c92316bfSLuis R. Rodriguez 	u8 i;
172c92316bfSLuis R. Rodriguez 
173c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs)
174c92316bfSLuis R. Rodriguez 		return;
175c92316bfSLuis R. Rodriguez 
176c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
177c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
178c92316bfSLuis R. Rodriguez 		if (req->fw)
179c92316bfSLuis R. Rodriguez 			release_firmware(req->fw);
180c92316bfSLuis R. Rodriguez 	}
181c92316bfSLuis R. Rodriguez 
182c92316bfSLuis R. Rodriguez 	vfree(test_fw_config->reqs);
183c92316bfSLuis R. Rodriguez 	test_fw_config->reqs = NULL;
184c92316bfSLuis R. Rodriguez }
185c92316bfSLuis R. Rodriguez 
186c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void)
187c92316bfSLuis R. Rodriguez {
188c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
189c92316bfSLuis R. Rodriguez 	__test_release_all_firmware();
190c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
191c92316bfSLuis R. Rodriguez }
192c92316bfSLuis R. Rodriguez 
193c92316bfSLuis R. Rodriguez 
194c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void)
195c92316bfSLuis R. Rodriguez {
196c92316bfSLuis R. Rodriguez 	__test_release_all_firmware();
197c92316bfSLuis R. Rodriguez 	kfree_const(test_fw_config->name);
198c92316bfSLuis R. Rodriguez 	test_fw_config->name = NULL;
199c92316bfSLuis R. Rodriguez }
200c92316bfSLuis R. Rodriguez 
201c92316bfSLuis R. Rodriguez /*
202c92316bfSLuis R. Rodriguez  * XXX: move to kstrncpy() once merged.
203c92316bfSLuis R. Rodriguez  *
204c92316bfSLuis R. Rodriguez  * Users should use kfree_const() when freeing these.
205c92316bfSLuis R. Rodriguez  */
206c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
207c92316bfSLuis R. Rodriguez {
208c92316bfSLuis R. Rodriguez 	*dst = kstrndup(name, count, gfp);
209c92316bfSLuis R. Rodriguez 	if (!*dst)
210c92316bfSLuis R. Rodriguez 		return -ENOSPC;
211c92316bfSLuis R. Rodriguez 	return count;
212c92316bfSLuis R. Rodriguez }
213c92316bfSLuis R. Rodriguez 
214c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void)
215c92316bfSLuis R. Rodriguez {
216c92316bfSLuis R. Rodriguez 	int ret;
217c92316bfSLuis R. Rodriguez 
218c92316bfSLuis R. Rodriguez 	ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
219c92316bfSLuis R. Rodriguez 			 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
220c92316bfSLuis R. Rodriguez 	if (ret < 0)
221c92316bfSLuis R. Rodriguez 		goto out;
222c92316bfSLuis R. Rodriguez 
223c92316bfSLuis R. Rodriguez 	test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
224c92316bfSLuis R. Rodriguez 	test_fw_config->send_uevent = true;
2257feebfa4SScott Branden 	test_fw_config->into_buf = false;
2265d90e05cSScott Branden 	test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
2275d90e05cSScott Branden 	test_fw_config->file_offset = 0;
2285d90e05cSScott Branden 	test_fw_config->partial = false;
229c92316bfSLuis R. Rodriguez 	test_fw_config->sync_direct = false;
230c92316bfSLuis R. Rodriguez 	test_fw_config->req_firmware = request_firmware;
231c92316bfSLuis R. Rodriguez 	test_fw_config->test_result = 0;
232c92316bfSLuis R. Rodriguez 	test_fw_config->reqs = NULL;
233a31ad463SRuss Weight 	test_fw_config->upload_name = NULL;
234c92316bfSLuis R. Rodriguez 
235c92316bfSLuis R. Rodriguez 	return 0;
236c92316bfSLuis R. Rodriguez 
237c92316bfSLuis R. Rodriguez out:
238c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
239c92316bfSLuis R. Rodriguez 	return ret;
240c92316bfSLuis R. Rodriguez }
241c92316bfSLuis R. Rodriguez 
242c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev,
243c92316bfSLuis R. Rodriguez 			   struct device_attribute *attr,
244c92316bfSLuis R. Rodriguez 			   const char *buf, size_t count)
245c92316bfSLuis R. Rodriguez {
246c92316bfSLuis R. Rodriguez 	int ret;
247c92316bfSLuis R. Rodriguez 
248c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
249c92316bfSLuis R. Rodriguez 
250c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
251c92316bfSLuis R. Rodriguez 
252c92316bfSLuis R. Rodriguez 	ret = __test_firmware_config_init();
253c92316bfSLuis R. Rodriguez 	if (ret < 0) {
254c92316bfSLuis R. Rodriguez 		ret = -ENOMEM;
255c92316bfSLuis R. Rodriguez 		pr_err("could not alloc settings for config trigger: %d\n",
256c92316bfSLuis R. Rodriguez 		       ret);
257c92316bfSLuis R. Rodriguez 		goto out;
258c92316bfSLuis R. Rodriguez 	}
259c92316bfSLuis R. Rodriguez 
260c92316bfSLuis R. Rodriguez 	pr_info("reset\n");
261c92316bfSLuis R. Rodriguez 	ret = count;
262c92316bfSLuis R. Rodriguez 
263c92316bfSLuis R. Rodriguez out:
264c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
265c92316bfSLuis R. Rodriguez 
266c92316bfSLuis R. Rodriguez 	return ret;
267c92316bfSLuis R. Rodriguez }
268c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset);
269c92316bfSLuis R. Rodriguez 
270c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev,
271c92316bfSLuis R. Rodriguez 			   struct device_attribute *attr,
272c92316bfSLuis R. Rodriguez 			   char *buf)
273c92316bfSLuis R. Rodriguez {
274c92316bfSLuis R. Rodriguez 	int len = 0;
275c92316bfSLuis R. Rodriguez 
276c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
277c92316bfSLuis R. Rodriguez 
278bd17cc5aSDan Carpenter 	len += scnprintf(buf, PAGE_SIZE - len,
279c92316bfSLuis R. Rodriguez 			"Custom trigger configuration for: %s\n",
280c92316bfSLuis R. Rodriguez 			dev_name(dev));
281c92316bfSLuis R. Rodriguez 
282c92316bfSLuis R. Rodriguez 	if (test_fw_config->name)
283bd17cc5aSDan Carpenter 		len += scnprintf(buf + len, PAGE_SIZE - len,
284c92316bfSLuis R. Rodriguez 				"name:\t%s\n",
285c92316bfSLuis R. Rodriguez 				test_fw_config->name);
286c92316bfSLuis R. Rodriguez 	else
287bd17cc5aSDan Carpenter 		len += scnprintf(buf + len, PAGE_SIZE - len,
288d88bd098SColin Ian King 				"name:\tEMPTY\n");
289c92316bfSLuis R. Rodriguez 
290bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
291c92316bfSLuis R. Rodriguez 			"num_requests:\t%u\n", test_fw_config->num_requests);
292c92316bfSLuis R. Rodriguez 
293bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
294c92316bfSLuis R. Rodriguez 			"send_uevent:\t\t%s\n",
295c92316bfSLuis R. Rodriguez 			test_fw_config->send_uevent ?
2960733d839SShawn Guo 			"FW_ACTION_UEVENT" :
2970733d839SShawn Guo 			"FW_ACTION_NOUEVENT");
298bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
2997feebfa4SScott Branden 			"into_buf:\t\t%s\n",
3007feebfa4SScott Branden 			test_fw_config->into_buf ? "true" : "false");
3017feebfa4SScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
3025d90e05cSScott Branden 			"buf_size:\t%zu\n", test_fw_config->buf_size);
3035d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
3045d90e05cSScott Branden 			"file_offset:\t%zu\n", test_fw_config->file_offset);
3055d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
3065d90e05cSScott Branden 			"partial:\t\t%s\n",
3075d90e05cSScott Branden 			test_fw_config->partial ? "true" : "false");
3085d90e05cSScott Branden 	len += scnprintf(buf + len, PAGE_SIZE - len,
309c92316bfSLuis R. Rodriguez 			"sync_direct:\t\t%s\n",
310c92316bfSLuis R. Rodriguez 			test_fw_config->sync_direct ? "true" : "false");
311bd17cc5aSDan Carpenter 	len += scnprintf(buf + len, PAGE_SIZE - len,
312c92316bfSLuis R. Rodriguez 			"read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
313a31ad463SRuss Weight 	if (test_fw_config->upload_name)
314a31ad463SRuss Weight 		len += scnprintf(buf + len, PAGE_SIZE - len,
315a31ad463SRuss Weight 				"upload_name:\t%s\n",
316a31ad463SRuss Weight 				test_fw_config->upload_name);
317a31ad463SRuss Weight 	else
318a31ad463SRuss Weight 		len += scnprintf(buf + len, PAGE_SIZE - len,
319d88bd098SColin Ian King 				"upload_name:\tEMPTY\n");
320c92316bfSLuis R. Rodriguez 
321c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
322c92316bfSLuis R. Rodriguez 
323c92316bfSLuis R. Rodriguez 	return len;
324c92316bfSLuis R. Rodriguez }
325c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config);
326c92316bfSLuis R. Rodriguez 
327c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev,
328c92316bfSLuis R. Rodriguez 				 struct device_attribute *attr,
329c92316bfSLuis R. Rodriguez 				 const char *buf, size_t count)
330c92316bfSLuis R. Rodriguez {
331c92316bfSLuis R. Rodriguez 	int ret;
332c92316bfSLuis R. Rodriguez 
333c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
334c92316bfSLuis R. Rodriguez 	kfree_const(test_fw_config->name);
335c92316bfSLuis R. Rodriguez 	ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
336c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
337c92316bfSLuis R. Rodriguez 
338c92316bfSLuis R. Rodriguez 	return ret;
339c92316bfSLuis R. Rodriguez }
340c92316bfSLuis R. Rodriguez 
341c92316bfSLuis R. Rodriguez /*
342c92316bfSLuis R. Rodriguez  * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
343c92316bfSLuis R. Rodriguez  */
344c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst,
345c92316bfSLuis R. Rodriguez 				    char *src)
346c92316bfSLuis R. Rodriguez {
347c92316bfSLuis R. Rodriguez 	int len;
348c92316bfSLuis R. Rodriguez 
349c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
350c92316bfSLuis R. Rodriguez 	len = snprintf(dst, PAGE_SIZE, "%s\n", src);
351c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
352c92316bfSLuis R. Rodriguez 
353c92316bfSLuis R. Rodriguez 	return len;
354c92316bfSLuis R. Rodriguez }
355c92316bfSLuis R. Rodriguez 
3564acfe3dfSMirsad Goran Todorovac static inline int __test_dev_config_update_bool(const char *buf, size_t size,
3574acfe3dfSMirsad Goran Todorovac 				       bool *cfg)
3584acfe3dfSMirsad Goran Todorovac {
3594acfe3dfSMirsad Goran Todorovac 	int ret;
3604acfe3dfSMirsad Goran Todorovac 
3614acfe3dfSMirsad Goran Todorovac 	if (kstrtobool(buf, cfg) < 0)
3624acfe3dfSMirsad Goran Todorovac 		ret = -EINVAL;
3634acfe3dfSMirsad Goran Todorovac 	else
3644acfe3dfSMirsad Goran Todorovac 		ret = size;
3654acfe3dfSMirsad Goran Todorovac 
3664acfe3dfSMirsad Goran Todorovac 	return ret;
3674acfe3dfSMirsad Goran Todorovac }
3684acfe3dfSMirsad Goran Todorovac 
369c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size,
370c92316bfSLuis R. Rodriguez 				       bool *cfg)
371c92316bfSLuis R. Rodriguez {
372c92316bfSLuis R. Rodriguez 	int ret;
373c92316bfSLuis R. Rodriguez 
374c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
3754acfe3dfSMirsad Goran Todorovac 	ret = __test_dev_config_update_bool(buf, size, cfg);
376c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
377c92316bfSLuis R. Rodriguez 
378c92316bfSLuis R. Rodriguez 	return ret;
379c92316bfSLuis R. Rodriguez }
380c92316bfSLuis R. Rodriguez 
38155623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val)
382c92316bfSLuis R. Rodriguez {
383c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
384c92316bfSLuis R. Rodriguez }
385c92316bfSLuis R. Rodriguez 
3864acfe3dfSMirsad Goran Todorovac static int __test_dev_config_update_size_t(
3874acfe3dfSMirsad Goran Todorovac 					 const char *buf,
3885d90e05cSScott Branden 					 size_t size,
3895d90e05cSScott Branden 					 size_t *cfg)
3905d90e05cSScott Branden {
3915d90e05cSScott Branden 	int ret;
3925d90e05cSScott Branden 	long new;
3935d90e05cSScott Branden 
3945d90e05cSScott Branden 	ret = kstrtol(buf, 10, &new);
3955d90e05cSScott Branden 	if (ret)
3965d90e05cSScott Branden 		return ret;
3975d90e05cSScott Branden 
3985d90e05cSScott Branden 	*(size_t *)cfg = new;
3995d90e05cSScott Branden 
4005d90e05cSScott Branden 	/* Always return full write size even if we didn't consume all */
4015d90e05cSScott Branden 	return size;
4025d90e05cSScott Branden }
4035d90e05cSScott Branden 
4045d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
4055d90e05cSScott Branden {
4065d90e05cSScott Branden 	return snprintf(buf, PAGE_SIZE, "%zu\n", val);
4075d90e05cSScott Branden }
4085d90e05cSScott Branden 
40955623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val)
410c92316bfSLuis R. Rodriguez {
411c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%d\n", val);
412c92316bfSLuis R. Rodriguez }
413c92316bfSLuis R. Rodriguez 
4144acfe3dfSMirsad Goran Todorovac static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
415c92316bfSLuis R. Rodriguez {
416506dfc99SAlexey Dobriyan 	u8 val;
417c92316bfSLuis R. Rodriguez 	int ret;
418c92316bfSLuis R. Rodriguez 
419506dfc99SAlexey Dobriyan 	ret = kstrtou8(buf, 10, &val);
420c92316bfSLuis R. Rodriguez 	if (ret)
421c92316bfSLuis R. Rodriguez 		return ret;
422c92316bfSLuis R. Rodriguez 
423506dfc99SAlexey Dobriyan 	*(u8 *)cfg = val;
424c92316bfSLuis R. Rodriguez 
425c92316bfSLuis R. Rodriguez 	/* Always return full write size even if we didn't consume all */
426c92316bfSLuis R. Rodriguez 	return size;
427c92316bfSLuis R. Rodriguez }
428c92316bfSLuis R. Rodriguez 
4294acfe3dfSMirsad Goran Todorovac static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
4304acfe3dfSMirsad Goran Todorovac {
4314acfe3dfSMirsad Goran Todorovac 	int ret;
4324acfe3dfSMirsad Goran Todorovac 
4334acfe3dfSMirsad Goran Todorovac 	mutex_lock(&test_fw_mutex);
4344acfe3dfSMirsad Goran Todorovac 	ret = __test_dev_config_update_u8(buf, size, cfg);
4354acfe3dfSMirsad Goran Todorovac 	mutex_unlock(&test_fw_mutex);
4364acfe3dfSMirsad Goran Todorovac 
4374acfe3dfSMirsad Goran Todorovac 	return ret;
4384acfe3dfSMirsad Goran Todorovac }
4394acfe3dfSMirsad Goran Todorovac 
44055623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val)
441c92316bfSLuis R. Rodriguez {
442c92316bfSLuis R. Rodriguez 	return snprintf(buf, PAGE_SIZE, "%u\n", val);
443c92316bfSLuis R. Rodriguez }
444c92316bfSLuis R. Rodriguez 
445c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev,
446c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
447c92316bfSLuis R. Rodriguez 				char *buf)
448c92316bfSLuis R. Rodriguez {
449c92316bfSLuis R. Rodriguez 	return config_test_show_str(buf, test_fw_config->name);
450c92316bfSLuis R. Rodriguez }
451b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name);
452c92316bfSLuis R. Rodriguez 
453a31ad463SRuss Weight static ssize_t config_upload_name_store(struct device *dev,
454a31ad463SRuss Weight 					struct device_attribute *attr,
455a31ad463SRuss Weight 					const char *buf, size_t count)
456a31ad463SRuss Weight {
457a31ad463SRuss Weight 	struct test_firmware_upload *tst;
458a31ad463SRuss Weight 	int ret = count;
459a31ad463SRuss Weight 
460a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
461a31ad463SRuss Weight 	tst = upload_lookup_name(buf);
462a31ad463SRuss Weight 	if (tst)
463a31ad463SRuss Weight 		test_fw_config->upload_name = tst->name;
464a31ad463SRuss Weight 	else
465a31ad463SRuss Weight 		ret = -EINVAL;
466a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
467a31ad463SRuss Weight 
468a31ad463SRuss Weight 	return ret;
469a31ad463SRuss Weight }
470a31ad463SRuss Weight 
471a31ad463SRuss Weight static ssize_t config_upload_name_show(struct device *dev,
472a31ad463SRuss Weight 				       struct device_attribute *attr,
473a31ad463SRuss Weight 				       char *buf)
474a31ad463SRuss Weight {
475a31ad463SRuss Weight 	return config_test_show_str(buf, test_fw_config->upload_name);
476a31ad463SRuss Weight }
477a31ad463SRuss Weight static DEVICE_ATTR_RW(config_upload_name);
478a31ad463SRuss Weight 
479c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev,
480c92316bfSLuis R. Rodriguez 					 struct device_attribute *attr,
481c92316bfSLuis R. Rodriguez 					 const char *buf, size_t count)
482c92316bfSLuis R. Rodriguez {
483c92316bfSLuis R. Rodriguez 	int rc;
484c92316bfSLuis R. Rodriguez 
485c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
486c92316bfSLuis R. Rodriguez 	if (test_fw_config->reqs) {
487c92316bfSLuis R. Rodriguez 		pr_err("Must call release_all_firmware prior to changing config\n");
488c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
489a5e19233SWei Yongjun 		mutex_unlock(&test_fw_mutex);
490c92316bfSLuis R. Rodriguez 		goto out;
491c92316bfSLuis R. Rodriguez 	}
492c92316bfSLuis R. Rodriguez 
4934acfe3dfSMirsad Goran Todorovac 	rc = __test_dev_config_update_u8(buf, count,
494c92316bfSLuis R. Rodriguez 					 &test_fw_config->num_requests);
4954acfe3dfSMirsad Goran Todorovac 	mutex_unlock(&test_fw_mutex);
496c92316bfSLuis R. Rodriguez 
497c92316bfSLuis R. Rodriguez out:
498c92316bfSLuis R. Rodriguez 	return rc;
499c92316bfSLuis R. Rodriguez }
500c92316bfSLuis R. Rodriguez 
501c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev,
502c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
503c92316bfSLuis R. Rodriguez 					char *buf)
504c92316bfSLuis R. Rodriguez {
505c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->num_requests);
506c92316bfSLuis R. Rodriguez }
507b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests);
508c92316bfSLuis R. Rodriguez 
5097feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev,
5107feebfa4SScott Branden 				     struct device_attribute *attr,
5117feebfa4SScott Branden 				     const char *buf, size_t count)
5127feebfa4SScott Branden {
5137feebfa4SScott Branden 	return test_dev_config_update_bool(buf,
5147feebfa4SScott Branden 					   count,
5157feebfa4SScott Branden 					   &test_fw_config->into_buf);
5167feebfa4SScott Branden }
5177feebfa4SScott Branden 
5187feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev,
5197feebfa4SScott Branden 				    struct device_attribute *attr,
5207feebfa4SScott Branden 				    char *buf)
5217feebfa4SScott Branden {
5227feebfa4SScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->into_buf);
5237feebfa4SScott Branden }
5247feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf);
5257feebfa4SScott Branden 
5265d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev,
5275d90e05cSScott Branden 				     struct device_attribute *attr,
5285d90e05cSScott Branden 				     const char *buf, size_t count)
5295d90e05cSScott Branden {
5305d90e05cSScott Branden 	int rc;
5315d90e05cSScott Branden 
5325d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
5335d90e05cSScott Branden 	if (test_fw_config->reqs) {
5345d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
5355d90e05cSScott Branden 		rc = -EINVAL;
5365d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
5375d90e05cSScott Branden 		goto out;
5385d90e05cSScott Branden 	}
5395d90e05cSScott Branden 
5404acfe3dfSMirsad Goran Todorovac 	rc = __test_dev_config_update_size_t(buf, count,
5415d90e05cSScott Branden 					     &test_fw_config->buf_size);
5424acfe3dfSMirsad Goran Todorovac 	mutex_unlock(&test_fw_mutex);
5435d90e05cSScott Branden 
5445d90e05cSScott Branden out:
5455d90e05cSScott Branden 	return rc;
5465d90e05cSScott Branden }
5475d90e05cSScott Branden 
5485d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev,
5495d90e05cSScott Branden 				    struct device_attribute *attr,
5505d90e05cSScott Branden 				    char *buf)
5515d90e05cSScott Branden {
5525d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
5535d90e05cSScott Branden }
5545d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size);
5555d90e05cSScott Branden 
5565d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev,
5575d90e05cSScott Branden 					struct device_attribute *attr,
5585d90e05cSScott Branden 					const char *buf, size_t count)
5595d90e05cSScott Branden {
5605d90e05cSScott Branden 	int rc;
5615d90e05cSScott Branden 
5625d90e05cSScott Branden 	mutex_lock(&test_fw_mutex);
5635d90e05cSScott Branden 	if (test_fw_config->reqs) {
5645d90e05cSScott Branden 		pr_err("Must call release_all_firmware prior to changing config\n");
5655d90e05cSScott Branden 		rc = -EINVAL;
5665d90e05cSScott Branden 		mutex_unlock(&test_fw_mutex);
5675d90e05cSScott Branden 		goto out;
5685d90e05cSScott Branden 	}
5695d90e05cSScott Branden 
5704acfe3dfSMirsad Goran Todorovac 	rc = __test_dev_config_update_size_t(buf, count,
5715d90e05cSScott Branden 					     &test_fw_config->file_offset);
5724acfe3dfSMirsad Goran Todorovac 	mutex_unlock(&test_fw_mutex);
5735d90e05cSScott Branden 
5745d90e05cSScott Branden out:
5755d90e05cSScott Branden 	return rc;
5765d90e05cSScott Branden }
5775d90e05cSScott Branden 
5785d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev,
5795d90e05cSScott Branden 				       struct device_attribute *attr,
5805d90e05cSScott Branden 				       char *buf)
5815d90e05cSScott Branden {
5825d90e05cSScott Branden 	return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
5835d90e05cSScott Branden }
5845d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset);
5855d90e05cSScott Branden 
5865d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev,
5875d90e05cSScott Branden 				    struct device_attribute *attr,
5885d90e05cSScott Branden 				    const char *buf, size_t count)
5895d90e05cSScott Branden {
5905d90e05cSScott Branden 	return test_dev_config_update_bool(buf,
5915d90e05cSScott Branden 					   count,
5925d90e05cSScott Branden 					   &test_fw_config->partial);
5935d90e05cSScott Branden }
5945d90e05cSScott Branden 
5955d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev,
5965d90e05cSScott Branden 				   struct device_attribute *attr,
5975d90e05cSScott Branden 				   char *buf)
5985d90e05cSScott Branden {
5995d90e05cSScott Branden 	return test_dev_config_show_bool(buf, test_fw_config->partial);
6005d90e05cSScott Branden }
6015d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial);
6025d90e05cSScott Branden 
603c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev,
604c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
605c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
606c92316bfSLuis R. Rodriguez {
607c92316bfSLuis R. Rodriguez 	int rc = test_dev_config_update_bool(buf, count,
608c92316bfSLuis R. Rodriguez 					     &test_fw_config->sync_direct);
609c92316bfSLuis R. Rodriguez 
610c92316bfSLuis R. Rodriguez 	if (rc == count)
611c92316bfSLuis R. Rodriguez 		test_fw_config->req_firmware = test_fw_config->sync_direct ?
612c92316bfSLuis R. Rodriguez 				       request_firmware_direct :
613c92316bfSLuis R. Rodriguez 				       request_firmware;
614c92316bfSLuis R. Rodriguez 	return rc;
615c92316bfSLuis R. Rodriguez }
616c92316bfSLuis R. Rodriguez 
617c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev,
618c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
619c92316bfSLuis R. Rodriguez 				       char *buf)
620c92316bfSLuis R. Rodriguez {
621c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
622c92316bfSLuis R. Rodriguez }
623b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct);
624c92316bfSLuis R. Rodriguez 
625c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev,
626c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
627c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
628c92316bfSLuis R. Rodriguez {
629c92316bfSLuis R. Rodriguez 	return test_dev_config_update_bool(buf, count,
630c92316bfSLuis R. Rodriguez 					   &test_fw_config->send_uevent);
631c92316bfSLuis R. Rodriguez }
632c92316bfSLuis R. Rodriguez 
633c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev,
634c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
635c92316bfSLuis R. Rodriguez 				       char *buf)
636c92316bfSLuis R. Rodriguez {
637c92316bfSLuis R. Rodriguez 	return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
638c92316bfSLuis R. Rodriguez }
639b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent);
640c92316bfSLuis R. Rodriguez 
641c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev,
642c92316bfSLuis R. Rodriguez 					struct device_attribute *attr,
643c92316bfSLuis R. Rodriguez 					const char *buf, size_t count)
644c92316bfSLuis R. Rodriguez {
645c92316bfSLuis R. Rodriguez 	return test_dev_config_update_u8(buf, count,
646c92316bfSLuis R. Rodriguez 					 &test_fw_config->read_fw_idx);
647c92316bfSLuis R. Rodriguez }
648c92316bfSLuis R. Rodriguez 
649c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev,
650c92316bfSLuis R. Rodriguez 				       struct device_attribute *attr,
651c92316bfSLuis R. Rodriguez 				       char *buf)
652c92316bfSLuis R. Rodriguez {
653c92316bfSLuis R. Rodriguez 	return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
654c92316bfSLuis R. Rodriguez }
655b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx);
656c92316bfSLuis R. Rodriguez 
657c92316bfSLuis R. Rodriguez 
6580a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev,
6590a8adf58SKees Cook 				     struct device_attribute *attr,
6600a8adf58SKees Cook 				     const char *buf, size_t count)
6610a8adf58SKees Cook {
6620a8adf58SKees Cook 	int rc;
6630a8adf58SKees Cook 	char *name;
6640a8adf58SKees Cook 
665be4a1326SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
6660a8adf58SKees Cook 	if (!name)
6670a8adf58SKees Cook 		return -ENOSPC;
6680a8adf58SKees Cook 
6690a8adf58SKees Cook 	pr_info("loading '%s'\n", name);
6700a8adf58SKees Cook 
6710a8adf58SKees Cook 	mutex_lock(&test_fw_mutex);
6720a8adf58SKees Cook 	release_firmware(test_firmware);
6730a8adf58SKees Cook 	test_firmware = NULL;
6740a8adf58SKees Cook 	rc = request_firmware(&test_firmware, name, dev);
67547e0bbb7SBrian Norris 	if (rc) {
6760a8adf58SKees Cook 		pr_info("load of '%s' failed: %d\n", name, rc);
67747e0bbb7SBrian Norris 		goto out;
67847e0bbb7SBrian Norris 	}
67947e0bbb7SBrian Norris 	pr_info("loaded: %zu\n", test_firmware->size);
68047e0bbb7SBrian Norris 	rc = count;
68147e0bbb7SBrian Norris 
68247e0bbb7SBrian Norris out:
6830a8adf58SKees Cook 	mutex_unlock(&test_fw_mutex);
6840a8adf58SKees Cook 
6850a8adf58SKees Cook 	kfree(name);
6860a8adf58SKees Cook 
68747e0bbb7SBrian Norris 	return rc;
6880a8adf58SKees Cook }
6890a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request);
6900a8adf58SKees Cook 
691548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
692baaabecfSKees Cook extern struct list_head efi_embedded_fw_list;
693baaabecfSKees Cook extern bool efi_embedded_fw_checked;
694baaabecfSKees Cook 
695548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev,
696548193cbSHans de Goede 					      struct device_attribute *attr,
697548193cbSHans de Goede 					      const char *buf, size_t count)
698548193cbSHans de Goede {
699548193cbSHans de Goede 	static const u8 test_data[] = {
700548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
701548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
702548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
703548193cbSHans de Goede 		0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
704548193cbSHans de Goede 	};
705548193cbSHans de Goede 	struct efi_embedded_fw efi_embedded_fw;
706548193cbSHans de Goede 	const struct firmware *firmware = NULL;
707baaabecfSKees Cook 	bool saved_efi_embedded_fw_checked;
708548193cbSHans de Goede 	char *name;
709548193cbSHans de Goede 	int rc;
710548193cbSHans de Goede 
711548193cbSHans de Goede 	name = kstrndup(buf, count, GFP_KERNEL);
712548193cbSHans de Goede 	if (!name)
713548193cbSHans de Goede 		return -ENOSPC;
714548193cbSHans de Goede 
715548193cbSHans de Goede 	pr_info("inserting test platform fw '%s'\n", name);
716548193cbSHans de Goede 	efi_embedded_fw.name = name;
717548193cbSHans de Goede 	efi_embedded_fw.data = (void *)test_data;
718548193cbSHans de Goede 	efi_embedded_fw.length = sizeof(test_data);
719548193cbSHans de Goede 	list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
720baaabecfSKees Cook 	saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
721baaabecfSKees Cook 	efi_embedded_fw_checked = true;
722548193cbSHans de Goede 
723548193cbSHans de Goede 	pr_info("loading '%s'\n", name);
724548193cbSHans de Goede 	rc = firmware_request_platform(&firmware, name, dev);
725548193cbSHans de Goede 	if (rc) {
726548193cbSHans de Goede 		pr_info("load of '%s' failed: %d\n", name, rc);
727548193cbSHans de Goede 		goto out;
728548193cbSHans de Goede 	}
729548193cbSHans de Goede 	if (firmware->size != sizeof(test_data) ||
730548193cbSHans de Goede 	    memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
731548193cbSHans de Goede 		pr_info("firmware contents mismatch for '%s'\n", name);
732548193cbSHans de Goede 		rc = -EINVAL;
733548193cbSHans de Goede 		goto out;
734548193cbSHans de Goede 	}
735548193cbSHans de Goede 	pr_info("loaded: %zu\n", firmware->size);
736548193cbSHans de Goede 	rc = count;
737548193cbSHans de Goede 
738548193cbSHans de Goede out:
739baaabecfSKees Cook 	efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
740548193cbSHans de Goede 	release_firmware(firmware);
741548193cbSHans de Goede 	list_del(&efi_embedded_fw.list);
742548193cbSHans de Goede 	kfree(name);
743548193cbSHans de Goede 
744548193cbSHans de Goede 	return rc;
745548193cbSHans de Goede }
746548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform);
747548193cbSHans de Goede #endif
748548193cbSHans de Goede 
749eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done);
750eb910947SBrian Norris 
751eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context)
752eb910947SBrian Norris {
753eb910947SBrian Norris 	test_firmware = fw;
754eb910947SBrian Norris 	complete(&async_fw_done);
755eb910947SBrian Norris }
756eb910947SBrian Norris 
757eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev,
758eb910947SBrian Norris 					   struct device_attribute *attr,
759eb910947SBrian Norris 					   const char *buf, size_t count)
760eb910947SBrian Norris {
761eb910947SBrian Norris 	int rc;
762eb910947SBrian Norris 	char *name;
763eb910947SBrian Norris 
764eb910947SBrian Norris 	name = kstrndup(buf, count, GFP_KERNEL);
765eb910947SBrian Norris 	if (!name)
766eb910947SBrian Norris 		return -ENOSPC;
767eb910947SBrian Norris 
768eb910947SBrian Norris 	pr_info("loading '%s'\n", name);
769eb910947SBrian Norris 
770eb910947SBrian Norris 	mutex_lock(&test_fw_mutex);
771eb910947SBrian Norris 	release_firmware(test_firmware);
772eb910947SBrian Norris 	test_firmware = NULL;
773eb910947SBrian Norris 	rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
774eb910947SBrian Norris 				     NULL, trigger_async_request_cb);
775eb910947SBrian Norris 	if (rc) {
776eb910947SBrian Norris 		pr_info("async load of '%s' failed: %d\n", name, rc);
777eb910947SBrian Norris 		kfree(name);
778eb910947SBrian Norris 		goto out;
779eb910947SBrian Norris 	}
780eb910947SBrian Norris 	/* Free 'name' ASAP, to test for race conditions */
781eb910947SBrian Norris 	kfree(name);
782eb910947SBrian Norris 
783eb910947SBrian Norris 	wait_for_completion(&async_fw_done);
784eb910947SBrian Norris 
785eb910947SBrian Norris 	if (test_firmware) {
786eb910947SBrian Norris 		pr_info("loaded: %zu\n", test_firmware->size);
787eb910947SBrian Norris 		rc = count;
788eb910947SBrian Norris 	} else {
789eb910947SBrian Norris 		pr_err("failed to async load firmware\n");
7907feebfa4SScott Branden 		rc = -ENOMEM;
791eb910947SBrian Norris 	}
792eb910947SBrian Norris 
793eb910947SBrian Norris out:
794eb910947SBrian Norris 	mutex_unlock(&test_fw_mutex);
795eb910947SBrian Norris 
796eb910947SBrian Norris 	return rc;
797eb910947SBrian Norris }
798eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request);
799eb910947SBrian Norris 
800061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev,
801061132d2SLuis R. Rodriguez 					     struct device_attribute *attr,
802061132d2SLuis R. Rodriguez 					     const char *buf, size_t count)
803061132d2SLuis R. Rodriguez {
804061132d2SLuis R. Rodriguez 	int rc;
805061132d2SLuis R. Rodriguez 	char *name;
806061132d2SLuis R. Rodriguez 
807061132d2SLuis R. Rodriguez 	name = kstrndup(buf, count, GFP_KERNEL);
808061132d2SLuis R. Rodriguez 	if (!name)
809061132d2SLuis R. Rodriguez 		return -ENOSPC;
810061132d2SLuis R. Rodriguez 
811061132d2SLuis R. Rodriguez 	pr_info("loading '%s' using custom fallback mechanism\n", name);
812061132d2SLuis R. Rodriguez 
813061132d2SLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
814061132d2SLuis R. Rodriguez 	release_firmware(test_firmware);
815061132d2SLuis R. Rodriguez 	test_firmware = NULL;
8160733d839SShawn Guo 	rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, name,
817061132d2SLuis R. Rodriguez 				     dev, GFP_KERNEL, NULL,
818061132d2SLuis R. Rodriguez 				     trigger_async_request_cb);
819061132d2SLuis R. Rodriguez 	if (rc) {
820061132d2SLuis R. Rodriguez 		pr_info("async load of '%s' failed: %d\n", name, rc);
821061132d2SLuis R. Rodriguez 		kfree(name);
822061132d2SLuis R. Rodriguez 		goto out;
823061132d2SLuis R. Rodriguez 	}
824061132d2SLuis R. Rodriguez 	/* Free 'name' ASAP, to test for race conditions */
825061132d2SLuis R. Rodriguez 	kfree(name);
826061132d2SLuis R. Rodriguez 
827061132d2SLuis R. Rodriguez 	wait_for_completion(&async_fw_done);
828061132d2SLuis R. Rodriguez 
829061132d2SLuis R. Rodriguez 	if (test_firmware) {
830061132d2SLuis R. Rodriguez 		pr_info("loaded: %zu\n", test_firmware->size);
831061132d2SLuis R. Rodriguez 		rc = count;
832061132d2SLuis R. Rodriguez 	} else {
833061132d2SLuis R. Rodriguez 		pr_err("failed to async load firmware\n");
834061132d2SLuis R. Rodriguez 		rc = -ENODEV;
835061132d2SLuis R. Rodriguez 	}
836061132d2SLuis R. Rodriguez 
837061132d2SLuis R. Rodriguez out:
838061132d2SLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
839061132d2SLuis R. Rodriguez 
840061132d2SLuis R. Rodriguez 	return rc;
841061132d2SLuis R. Rodriguez }
842061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback);
843061132d2SLuis R. Rodriguez 
844c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data)
845c92316bfSLuis R. Rodriguez {
846c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = data;
847c92316bfSLuis R. Rodriguez 
848c92316bfSLuis R. Rodriguez 	if (!req) {
849c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
850c92316bfSLuis R. Rodriguez 		return -EINVAL;
851c92316bfSLuis R. Rodriguez 	}
852c92316bfSLuis R. Rodriguez 
8537feebfa4SScott Branden 	if (test_fw_config->into_buf) {
8547feebfa4SScott Branden 		void *test_buf;
8557feebfa4SScott Branden 
8567feebfa4SScott Branden 		test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
8577feebfa4SScott Branden 		if (!test_buf)
8587feebfa4SScott Branden 			return -ENOSPC;
8597feebfa4SScott Branden 
8605d90e05cSScott Branden 		if (test_fw_config->partial)
8615d90e05cSScott Branden 			req->rc = request_partial_firmware_into_buf
8625d90e05cSScott Branden 						(&req->fw,
8637feebfa4SScott Branden 						 req->name,
8647feebfa4SScott Branden 						 req->dev,
8657feebfa4SScott Branden 						 test_buf,
8665d90e05cSScott Branden 						 test_fw_config->buf_size,
8675d90e05cSScott Branden 						 test_fw_config->file_offset);
8685d90e05cSScott Branden 		else
8695d90e05cSScott Branden 			req->rc = request_firmware_into_buf
8705d90e05cSScott Branden 						(&req->fw,
8715d90e05cSScott Branden 						 req->name,
8725d90e05cSScott Branden 						 req->dev,
8735d90e05cSScott Branden 						 test_buf,
8745d90e05cSScott Branden 						 test_fw_config->buf_size);
8757feebfa4SScott Branden 		if (!req->fw)
8767feebfa4SScott Branden 			kfree(test_buf);
8777feebfa4SScott Branden 	} else {
8787feebfa4SScott Branden 		req->rc = test_fw_config->req_firmware(&req->fw,
8797feebfa4SScott Branden 						       req->name,
8807feebfa4SScott Branden 						       req->dev);
8817feebfa4SScott Branden 	}
8827feebfa4SScott Branden 
883c92316bfSLuis R. Rodriguez 	if (req->rc) {
884c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync load failed: %d\n",
885c92316bfSLuis R. Rodriguez 			req->idx, req->rc);
886c92316bfSLuis R. Rodriguez 		if (!test_fw_config->test_result)
887c92316bfSLuis R. Rodriguez 			test_fw_config->test_result = req->rc;
888c92316bfSLuis R. Rodriguez 	} else if (req->fw) {
889c92316bfSLuis R. Rodriguez 		req->sent = true;
890c92316bfSLuis R. Rodriguez 		pr_info("#%u: batched sync loaded %zu\n",
891c92316bfSLuis R. Rodriguez 			req->idx, req->fw->size);
892c92316bfSLuis R. Rodriguez 	}
893c92316bfSLuis R. Rodriguez 	complete(&req->completion);
894c92316bfSLuis R. Rodriguez 
895c92316bfSLuis R. Rodriguez 	req->task = NULL;
896c92316bfSLuis R. Rodriguez 
897c92316bfSLuis R. Rodriguez 	return 0;
898c92316bfSLuis R. Rodriguez }
899c92316bfSLuis R. Rodriguez 
900c92316bfSLuis R. Rodriguez /*
901c92316bfSLuis R. Rodriguez  * We use a kthread as otherwise the kernel serializes all our sync requests
902c92316bfSLuis R. Rodriguez  * and we would not be able to mimic batched requests on a sync call. Batched
903c92316bfSLuis R. Rodriguez  * requests on a sync call can for instance happen on a device driver when
904c92316bfSLuis R. Rodriguez  * multiple cards are used and firmware loading happens outside of probe.
905c92316bfSLuis R. Rodriguez  */
906c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev,
907c92316bfSLuis R. Rodriguez 					      struct device_attribute *attr,
908c92316bfSLuis R. Rodriguez 					      const char *buf, size_t count)
909c92316bfSLuis R. Rodriguez {
910c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
911c92316bfSLuis R. Rodriguez 	int rc;
912c92316bfSLuis R. Rodriguez 	u8 i;
913c92316bfSLuis R. Rodriguez 
914c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
915c92316bfSLuis R. Rodriguez 
916*be37bed7SMirsad Goran Todorovac 	if (test_fw_config->reqs) {
917*be37bed7SMirsad Goran Todorovac 		rc = -EBUSY;
918*be37bed7SMirsad Goran Todorovac 		goto out_bail;
919*be37bed7SMirsad Goran Todorovac 	}
920*be37bed7SMirsad Goran Todorovac 
921fad953ceSKees Cook 	test_fw_config->reqs =
922fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
923fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
924c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
925c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
926c92316bfSLuis R. Rodriguez 		goto out_unlock;
927c92316bfSLuis R. Rodriguez 	}
928c92316bfSLuis R. Rodriguez 
929c92316bfSLuis R. Rodriguez 	pr_info("batched sync firmware loading '%s' %u times\n",
930c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
931c92316bfSLuis R. Rodriguez 
932c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
933c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
934c92316bfSLuis R. Rodriguez 		req->fw = NULL;
935c92316bfSLuis R. Rodriguez 		req->idx = i;
936c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
937c92316bfSLuis R. Rodriguez 		req->dev = dev;
938c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
939c92316bfSLuis R. Rodriguez 		req->task = kthread_run(test_fw_run_batch_request, req,
940c92316bfSLuis R. Rodriguez 					     "%s-%u", KBUILD_MODNAME, req->idx);
941c92316bfSLuis R. Rodriguez 		if (!req->task || IS_ERR(req->task)) {
942c92316bfSLuis R. Rodriguez 			pr_err("Setting up thread %u failed\n", req->idx);
943c92316bfSLuis R. Rodriguez 			req->task = NULL;
944c92316bfSLuis R. Rodriguez 			rc = -ENOMEM;
945c92316bfSLuis R. Rodriguez 			goto out_bail;
946c92316bfSLuis R. Rodriguez 		}
947c92316bfSLuis R. Rodriguez 	}
948c92316bfSLuis R. Rodriguez 
949c92316bfSLuis R. Rodriguez 	rc = count;
950c92316bfSLuis R. Rodriguez 
951c92316bfSLuis R. Rodriguez 	/*
952c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
953c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
954c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
955c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
956c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
957c92316bfSLuis R. Rodriguez 	 */
958c92316bfSLuis R. Rodriguez 
959c92316bfSLuis R. Rodriguez out_bail:
960c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
961c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
962c92316bfSLuis R. Rodriguez 		if (req->task || req->sent)
963c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
964c92316bfSLuis R. Rodriguez 	}
965c92316bfSLuis R. Rodriguez 
966c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
967c92316bfSLuis R. Rodriguez 	if (rc < 0)
968c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
969c92316bfSLuis R. Rodriguez 
970c92316bfSLuis R. Rodriguez out_unlock:
971c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
972c92316bfSLuis R. Rodriguez 
973c92316bfSLuis R. Rodriguez 	return rc;
974c92316bfSLuis R. Rodriguez }
975c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests);
976c92316bfSLuis R. Rodriguez 
977c92316bfSLuis R. Rodriguez /*
978c92316bfSLuis R. Rodriguez  * We wait for each callback to return with the lock held, no need to lock here
979c92316bfSLuis R. Rodriguez  */
980c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context)
981c92316bfSLuis R. Rodriguez {
982c92316bfSLuis R. Rodriguez 	struct test_batched_req *req = context;
983c92316bfSLuis R. Rodriguez 
984c92316bfSLuis R. Rodriguez 	if (!req) {
985c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -EINVAL;
986c92316bfSLuis R. Rodriguez 		return;
987c92316bfSLuis R. Rodriguez 	}
988c92316bfSLuis R. Rodriguez 
989c92316bfSLuis R. Rodriguez 	/* forces *some* batched requests to queue up */
990c92316bfSLuis R. Rodriguez 	if (!req->idx)
991c92316bfSLuis R. Rodriguez 		ssleep(2);
992c92316bfSLuis R. Rodriguez 
993c92316bfSLuis R. Rodriguez 	req->fw = fw;
994c92316bfSLuis R. Rodriguez 
995c92316bfSLuis R. Rodriguez 	/*
996c92316bfSLuis R. Rodriguez 	 * Unfortunately the firmware API gives us nothing other than a null FW
997c92316bfSLuis R. Rodriguez 	 * if the firmware was not found on async requests.  Best we can do is
998c92316bfSLuis R. Rodriguez 	 * just assume -ENOENT. A better API would pass the actual return
999c92316bfSLuis R. Rodriguez 	 * value to the callback.
1000c92316bfSLuis R. Rodriguez 	 */
1001c92316bfSLuis R. Rodriguez 	if (!fw && !test_fw_config->test_result)
1002c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = -ENOENT;
1003c92316bfSLuis R. Rodriguez 
1004c92316bfSLuis R. Rodriguez 	complete(&req->completion);
1005c92316bfSLuis R. Rodriguez }
1006c92316bfSLuis R. Rodriguez 
1007c92316bfSLuis R. Rodriguez static
1008c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev,
1009c92316bfSLuis R. Rodriguez 					     struct device_attribute *attr,
1010c92316bfSLuis R. Rodriguez 					     const char *buf, size_t count)
1011c92316bfSLuis R. Rodriguez {
1012c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
1013c92316bfSLuis R. Rodriguez 	bool send_uevent;
1014c92316bfSLuis R. Rodriguez 	int rc;
1015c92316bfSLuis R. Rodriguez 	u8 i;
1016c92316bfSLuis R. Rodriguez 
1017c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
1018c92316bfSLuis R. Rodriguez 
1019*be37bed7SMirsad Goran Todorovac 	if (test_fw_config->reqs) {
1020*be37bed7SMirsad Goran Todorovac 		rc = -EBUSY;
1021*be37bed7SMirsad Goran Todorovac 		goto out_bail;
1022*be37bed7SMirsad Goran Todorovac 	}
1023*be37bed7SMirsad Goran Todorovac 
1024fad953ceSKees Cook 	test_fw_config->reqs =
1025fad953ceSKees Cook 		vzalloc(array3_size(sizeof(struct test_batched_req),
1026fad953ceSKees Cook 				    test_fw_config->num_requests, 2));
1027c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
1028c92316bfSLuis R. Rodriguez 		rc = -ENOMEM;
1029c92316bfSLuis R. Rodriguez 		goto out;
1030c92316bfSLuis R. Rodriguez 	}
1031c92316bfSLuis R. Rodriguez 
1032c92316bfSLuis R. Rodriguez 	pr_info("batched loading '%s' custom fallback mechanism %u times\n",
1033c92316bfSLuis R. Rodriguez 		test_fw_config->name, test_fw_config->num_requests);
1034c92316bfSLuis R. Rodriguez 
10350733d839SShawn Guo 	send_uevent = test_fw_config->send_uevent ? FW_ACTION_UEVENT :
10360733d839SShawn Guo 		FW_ACTION_NOUEVENT;
1037c92316bfSLuis R. Rodriguez 
1038c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
1039c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
1040c92316bfSLuis R. Rodriguez 		req->name = test_fw_config->name;
1041c92316bfSLuis R. Rodriguez 		req->fw = NULL;
1042c92316bfSLuis R. Rodriguez 		req->idx = i;
1043c92316bfSLuis R. Rodriguez 		init_completion(&req->completion);
1044c92316bfSLuis R. Rodriguez 		rc = request_firmware_nowait(THIS_MODULE, send_uevent,
1045c92316bfSLuis R. Rodriguez 					     req->name,
1046c92316bfSLuis R. Rodriguez 					     dev, GFP_KERNEL, req,
1047c92316bfSLuis R. Rodriguez 					     trigger_batched_cb);
1048c92316bfSLuis R. Rodriguez 		if (rc) {
1049c92316bfSLuis R. Rodriguez 			pr_info("#%u: batched async load failed setup: %d\n",
1050c92316bfSLuis R. Rodriguez 				i, rc);
1051c92316bfSLuis R. Rodriguez 			req->rc = rc;
1052c92316bfSLuis R. Rodriguez 			goto out_bail;
1053c92316bfSLuis R. Rodriguez 		} else
1054c92316bfSLuis R. Rodriguez 			req->sent = true;
1055c92316bfSLuis R. Rodriguez 	}
1056c92316bfSLuis R. Rodriguez 
1057c92316bfSLuis R. Rodriguez 	rc = count;
1058c92316bfSLuis R. Rodriguez 
1059c92316bfSLuis R. Rodriguez out_bail:
1060c92316bfSLuis R. Rodriguez 
1061c92316bfSLuis R. Rodriguez 	/*
1062c92316bfSLuis R. Rodriguez 	 * We require an explicit release to enable more time and delay of
1063c92316bfSLuis R. Rodriguez 	 * calling release_firmware() to improve our chances of forcing a
1064c92316bfSLuis R. Rodriguez 	 * batched request. If we instead called release_firmware() right away
1065c92316bfSLuis R. Rodriguez 	 * then we might miss on an opportunity of having a successful firmware
1066c92316bfSLuis R. Rodriguez 	 * request pass on the opportunity to be come a batched request.
1067c92316bfSLuis R. Rodriguez 	 */
1068c92316bfSLuis R. Rodriguez 
1069c92316bfSLuis R. Rodriguez 	for (i = 0; i < test_fw_config->num_requests; i++) {
1070c92316bfSLuis R. Rodriguez 		req = &test_fw_config->reqs[i];
1071c92316bfSLuis R. Rodriguez 		if (req->sent)
1072c92316bfSLuis R. Rodriguez 			wait_for_completion(&req->completion);
1073c92316bfSLuis R. Rodriguez 	}
1074c92316bfSLuis R. Rodriguez 
1075c92316bfSLuis R. Rodriguez 	/* Override any worker error if we had a general setup error */
1076c92316bfSLuis R. Rodriguez 	if (rc < 0)
1077c92316bfSLuis R. Rodriguez 		test_fw_config->test_result = rc;
1078c92316bfSLuis R. Rodriguez 
1079c92316bfSLuis R. Rodriguez out:
1080c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1081c92316bfSLuis R. Rodriguez 
1082c92316bfSLuis R. Rodriguez 	return rc;
1083c92316bfSLuis R. Rodriguez }
1084c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async);
1085c92316bfSLuis R. Rodriguez 
1086a31ad463SRuss Weight static void upload_release(struct test_firmware_upload *tst)
1087a31ad463SRuss Weight {
1088a31ad463SRuss Weight 	firmware_upload_unregister(tst->fwl);
1089a31ad463SRuss Weight 	kfree(tst->buf);
1090a31ad463SRuss Weight 	kfree(tst->name);
1091a31ad463SRuss Weight 	kfree(tst);
1092a31ad463SRuss Weight }
1093a31ad463SRuss Weight 
1094a31ad463SRuss Weight static void upload_release_all(void)
1095a31ad463SRuss Weight {
1096a31ad463SRuss Weight 	struct test_firmware_upload *tst, *tmp;
1097a31ad463SRuss Weight 
1098a31ad463SRuss Weight 	list_for_each_entry_safe(tst, tmp, &test_upload_list, node) {
1099a31ad463SRuss Weight 		list_del(&tst->node);
1100a31ad463SRuss Weight 		upload_release(tst);
1101a31ad463SRuss Weight 	}
1102a31ad463SRuss Weight 	test_fw_config->upload_name = NULL;
1103a31ad463SRuss Weight }
1104a31ad463SRuss Weight 
11054a4e975bSRuss Weight /*
11064a4e975bSRuss Weight  * This table is replicated from .../firmware_loader/sysfs_upload.c
11074a4e975bSRuss Weight  * and needs to be kept in sync.
11084a4e975bSRuss Weight  */
11094a4e975bSRuss Weight static const char * const fw_upload_err_str[] = {
11104a4e975bSRuss Weight 	[FW_UPLOAD_ERR_NONE]	     = "none",
11114a4e975bSRuss Weight 	[FW_UPLOAD_ERR_HW_ERROR]     = "hw-error",
11124a4e975bSRuss Weight 	[FW_UPLOAD_ERR_TIMEOUT]	     = "timeout",
11134a4e975bSRuss Weight 	[FW_UPLOAD_ERR_CANCELED]     = "user-abort",
11144a4e975bSRuss Weight 	[FW_UPLOAD_ERR_BUSY]	     = "device-busy",
11154a4e975bSRuss Weight 	[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
11164a4e975bSRuss Weight 	[FW_UPLOAD_ERR_RW_ERROR]     = "read-write-error",
11174a4e975bSRuss Weight 	[FW_UPLOAD_ERR_WEAROUT]	     = "flash-wearout",
11184a4e975bSRuss Weight };
11194a4e975bSRuss Weight 
11204a4e975bSRuss Weight static void upload_err_inject_error(struct test_firmware_upload *tst,
11214a4e975bSRuss Weight 				    const u8 *p, const char *prog)
11224a4e975bSRuss Weight {
11234a4e975bSRuss Weight 	enum fw_upload_err err;
11244a4e975bSRuss Weight 
11254a4e975bSRuss Weight 	for (err = FW_UPLOAD_ERR_NONE + 1; err < FW_UPLOAD_ERR_MAX; err++) {
11264a4e975bSRuss Weight 		if (strncmp(p, fw_upload_err_str[err],
11274a4e975bSRuss Weight 			    strlen(fw_upload_err_str[err])) == 0) {
11284a4e975bSRuss Weight 			tst->inject.prog = prog;
11294a4e975bSRuss Weight 			tst->inject.err_code = err;
11304a4e975bSRuss Weight 			return;
11314a4e975bSRuss Weight 		}
11324a4e975bSRuss Weight 	}
11334a4e975bSRuss Weight }
11344a4e975bSRuss Weight 
11354a4e975bSRuss Weight static void upload_err_inject_prog(struct test_firmware_upload *tst,
11364a4e975bSRuss Weight 				   const u8 *p)
11374a4e975bSRuss Weight {
11384a4e975bSRuss Weight 	static const char * const progs[] = {
11394a4e975bSRuss Weight 		"preparing:", "transferring:", "programming:"
11404a4e975bSRuss Weight 	};
11414a4e975bSRuss Weight 	int i;
11424a4e975bSRuss Weight 
11434a4e975bSRuss Weight 	for (i = 0; i < ARRAY_SIZE(progs); i++) {
11444a4e975bSRuss Weight 		if (strncmp(p, progs[i], strlen(progs[i])) == 0) {
11454a4e975bSRuss Weight 			upload_err_inject_error(tst, p + strlen(progs[i]),
11464a4e975bSRuss Weight 						progs[i]);
11474a4e975bSRuss Weight 			return;
11484a4e975bSRuss Weight 		}
11494a4e975bSRuss Weight 	}
11504a4e975bSRuss Weight }
11514a4e975bSRuss Weight 
11524a4e975bSRuss Weight #define FIVE_MINUTES_MS	(5 * 60 * 1000)
11534a4e975bSRuss Weight static enum fw_upload_err
11544a4e975bSRuss Weight fw_upload_wait_on_cancel(struct test_firmware_upload *tst)
11554a4e975bSRuss Weight {
11564a4e975bSRuss Weight 	int ms_delay;
11574a4e975bSRuss Weight 
11584a4e975bSRuss Weight 	for (ms_delay = 0; ms_delay < FIVE_MINUTES_MS; ms_delay += 100) {
11594a4e975bSRuss Weight 		msleep(100);
11604a4e975bSRuss Weight 		if (tst->cancel_request)
11614a4e975bSRuss Weight 			return FW_UPLOAD_ERR_CANCELED;
11624a4e975bSRuss Weight 	}
11634a4e975bSRuss Weight 	return FW_UPLOAD_ERR_NONE;
11644a4e975bSRuss Weight }
11654a4e975bSRuss Weight 
1166a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_prepare(struct fw_upload *fwl,
1167a31ad463SRuss Weight 						 const u8 *data, u32 size)
1168a31ad463SRuss Weight {
1169a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
11704a4e975bSRuss Weight 	enum fw_upload_err ret = FW_UPLOAD_ERR_NONE;
11714a4e975bSRuss Weight 	const char *progress = "preparing:";
1172a31ad463SRuss Weight 
1173a31ad463SRuss Weight 	tst->cancel_request = false;
1174a31ad463SRuss Weight 
11754a4e975bSRuss Weight 	if (!size || size > TEST_UPLOAD_MAX_SIZE) {
11764a4e975bSRuss Weight 		ret = FW_UPLOAD_ERR_INVALID_SIZE;
11774a4e975bSRuss Weight 		goto err_out;
11784a4e975bSRuss Weight 	}
11794a4e975bSRuss Weight 
11804a4e975bSRuss Weight 	if (strncmp(data, "inject:", strlen("inject:")) == 0)
11814a4e975bSRuss Weight 		upload_err_inject_prog(tst, data + strlen("inject:"));
1182a31ad463SRuss Weight 
1183a31ad463SRuss Weight 	memset(tst->buf, 0, TEST_UPLOAD_MAX_SIZE);
1184a31ad463SRuss Weight 	tst->size = size;
1185a31ad463SRuss Weight 
11864a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
11874a4e975bSRuss Weight 	    strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
1188a31ad463SRuss Weight 		return FW_UPLOAD_ERR_NONE;
11894a4e975bSRuss Weight 
11904a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
11914a4e975bSRuss Weight 		ret = fw_upload_wait_on_cancel(tst);
11924a4e975bSRuss Weight 	else
11934a4e975bSRuss Weight 		ret = tst->inject.err_code;
11944a4e975bSRuss Weight 
11954a4e975bSRuss Weight err_out:
11964a4e975bSRuss Weight 	/*
11974a4e975bSRuss Weight 	 * The cleanup op only executes if the prepare op succeeds.
11984a4e975bSRuss Weight 	 * If the prepare op fails, it must do it's own clean-up.
11994a4e975bSRuss Weight 	 */
12004a4e975bSRuss Weight 	tst->inject.err_code = FW_UPLOAD_ERR_NONE;
12014a4e975bSRuss Weight 	tst->inject.prog = NULL;
12024a4e975bSRuss Weight 
12034a4e975bSRuss Weight 	return ret;
1204a31ad463SRuss Weight }
1205a31ad463SRuss Weight 
1206a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl,
1207a31ad463SRuss Weight 					       const u8 *data, u32 offset,
1208a31ad463SRuss Weight 					       u32 size, u32 *written)
1209a31ad463SRuss Weight {
1210a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
12114a4e975bSRuss Weight 	const char *progress = "transferring:";
1212a31ad463SRuss Weight 	u32 blk_size;
1213a31ad463SRuss Weight 
1214a31ad463SRuss Weight 	if (tst->cancel_request)
1215a31ad463SRuss Weight 		return FW_UPLOAD_ERR_CANCELED;
1216a31ad463SRuss Weight 
1217a31ad463SRuss Weight 	blk_size = min_t(u32, TEST_UPLOAD_BLK_SIZE, size);
1218a31ad463SRuss Weight 	memcpy(tst->buf + offset, data + offset, blk_size);
1219a31ad463SRuss Weight 
1220a31ad463SRuss Weight 	*written = blk_size;
12214a4e975bSRuss Weight 
12224a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
12234a4e975bSRuss Weight 	    strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
1224a31ad463SRuss Weight 		return FW_UPLOAD_ERR_NONE;
12254a4e975bSRuss Weight 
12264a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
12274a4e975bSRuss Weight 		return fw_upload_wait_on_cancel(tst);
12284a4e975bSRuss Weight 
12294a4e975bSRuss Weight 	return tst->inject.err_code;
1230a31ad463SRuss Weight }
1231a31ad463SRuss Weight 
1232a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_complete(struct fw_upload *fwl)
1233a31ad463SRuss Weight {
1234a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
12354a4e975bSRuss Weight 	const char *progress = "programming:";
1236a31ad463SRuss Weight 
1237a31ad463SRuss Weight 	if (tst->cancel_request)
1238a31ad463SRuss Weight 		return FW_UPLOAD_ERR_CANCELED;
1239a31ad463SRuss Weight 
12404a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_NONE ||
12414a4e975bSRuss Weight 	    strncmp(tst->inject.prog, progress, strlen(progress)) != 0)
1242a31ad463SRuss Weight 		return FW_UPLOAD_ERR_NONE;
12434a4e975bSRuss Weight 
12444a4e975bSRuss Weight 	if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED)
12454a4e975bSRuss Weight 		return fw_upload_wait_on_cancel(tst);
12464a4e975bSRuss Weight 
12474a4e975bSRuss Weight 	return tst->inject.err_code;
1248a31ad463SRuss Weight }
1249a31ad463SRuss Weight 
1250a31ad463SRuss Weight static void test_fw_upload_cancel(struct fw_upload *fwl)
1251a31ad463SRuss Weight {
1252a31ad463SRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
1253a31ad463SRuss Weight 
1254a31ad463SRuss Weight 	tst->cancel_request = true;
1255a31ad463SRuss Weight }
1256a31ad463SRuss Weight 
12574a4e975bSRuss Weight static void test_fw_cleanup(struct fw_upload *fwl)
12584a4e975bSRuss Weight {
12594a4e975bSRuss Weight 	struct test_firmware_upload *tst = fwl->dd_handle;
12604a4e975bSRuss Weight 
12614a4e975bSRuss Weight 	tst->inject.err_code = FW_UPLOAD_ERR_NONE;
12624a4e975bSRuss Weight 	tst->inject.prog = NULL;
12634a4e975bSRuss Weight }
12644a4e975bSRuss Weight 
1265a31ad463SRuss Weight static const struct fw_upload_ops upload_test_ops = {
1266a31ad463SRuss Weight 	.prepare = test_fw_upload_prepare,
1267a31ad463SRuss Weight 	.write = test_fw_upload_write,
1268a31ad463SRuss Weight 	.poll_complete = test_fw_upload_complete,
1269a31ad463SRuss Weight 	.cancel = test_fw_upload_cancel,
12704a4e975bSRuss Weight 	.cleanup = test_fw_cleanup
1271a31ad463SRuss Weight };
1272a31ad463SRuss Weight 
1273a31ad463SRuss Weight static ssize_t upload_register_store(struct device *dev,
1274a31ad463SRuss Weight 				     struct device_attribute *attr,
1275a31ad463SRuss Weight 				     const char *buf, size_t count)
1276a31ad463SRuss Weight {
1277a31ad463SRuss Weight 	struct test_firmware_upload *tst;
1278a31ad463SRuss Weight 	struct fw_upload *fwl;
1279a31ad463SRuss Weight 	char *name;
1280a31ad463SRuss Weight 	int ret;
1281a31ad463SRuss Weight 
1282a31ad463SRuss Weight 	name = kstrndup(buf, count, GFP_KERNEL);
1283a31ad463SRuss Weight 	if (!name)
1284a31ad463SRuss Weight 		return -ENOMEM;
1285a31ad463SRuss Weight 
1286a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
1287a31ad463SRuss Weight 	tst = upload_lookup_name(name);
1288a31ad463SRuss Weight 	if (tst) {
1289a31ad463SRuss Weight 		ret = -EEXIST;
1290a31ad463SRuss Weight 		goto free_name;
1291a31ad463SRuss Weight 	}
1292a31ad463SRuss Weight 
1293a31ad463SRuss Weight 	tst = kzalloc(sizeof(*tst), GFP_KERNEL);
1294a31ad463SRuss Weight 	if (!tst) {
1295a31ad463SRuss Weight 		ret = -ENOMEM;
1296a31ad463SRuss Weight 		goto free_name;
1297a31ad463SRuss Weight 	}
1298a31ad463SRuss Weight 
1299a31ad463SRuss Weight 	tst->name = name;
1300a31ad463SRuss Weight 	tst->buf = kzalloc(TEST_UPLOAD_MAX_SIZE, GFP_KERNEL);
1301a31ad463SRuss Weight 	if (!tst->buf) {
1302a31ad463SRuss Weight 		ret = -ENOMEM;
1303a31ad463SRuss Weight 		goto free_tst;
1304a31ad463SRuss Weight 	}
1305a31ad463SRuss Weight 
1306a31ad463SRuss Weight 	fwl = firmware_upload_register(THIS_MODULE, dev, tst->name,
1307a31ad463SRuss Weight 				       &upload_test_ops, tst);
1308a31ad463SRuss Weight 	if (IS_ERR(fwl)) {
1309a31ad463SRuss Weight 		ret = PTR_ERR(fwl);
1310a31ad463SRuss Weight 		goto free_buf;
1311a31ad463SRuss Weight 	}
1312a31ad463SRuss Weight 
1313a31ad463SRuss Weight 	tst->fwl = fwl;
1314a31ad463SRuss Weight 	list_add_tail(&tst->node, &test_upload_list);
1315a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1316a31ad463SRuss Weight 	return count;
1317a31ad463SRuss Weight 
1318a31ad463SRuss Weight free_buf:
1319a31ad463SRuss Weight 	kfree(tst->buf);
1320a31ad463SRuss Weight 
1321a31ad463SRuss Weight free_tst:
1322a31ad463SRuss Weight 	kfree(tst);
1323a31ad463SRuss Weight 
1324a31ad463SRuss Weight free_name:
1325a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1326a31ad463SRuss Weight 	kfree(name);
1327a31ad463SRuss Weight 
1328a31ad463SRuss Weight 	return ret;
1329a31ad463SRuss Weight }
1330a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_register);
1331a31ad463SRuss Weight 
1332a31ad463SRuss Weight static ssize_t upload_unregister_store(struct device *dev,
1333a31ad463SRuss Weight 				       struct device_attribute *attr,
1334a31ad463SRuss Weight 				       const char *buf, size_t count)
1335a31ad463SRuss Weight {
1336a31ad463SRuss Weight 	struct test_firmware_upload *tst;
1337a31ad463SRuss Weight 	int ret = count;
1338a31ad463SRuss Weight 
1339a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
1340a31ad463SRuss Weight 	tst = upload_lookup_name(buf);
1341a31ad463SRuss Weight 	if (!tst) {
1342a31ad463SRuss Weight 		ret = -EINVAL;
1343a31ad463SRuss Weight 		goto out;
1344a31ad463SRuss Weight 	}
1345a31ad463SRuss Weight 
1346a31ad463SRuss Weight 	if (test_fw_config->upload_name == tst->name)
1347a31ad463SRuss Weight 		test_fw_config->upload_name = NULL;
1348a31ad463SRuss Weight 
1349a31ad463SRuss Weight 	list_del(&tst->node);
1350a31ad463SRuss Weight 	upload_release(tst);
1351a31ad463SRuss Weight 
1352a31ad463SRuss Weight out:
1353a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1354a31ad463SRuss Weight 	return ret;
1355a31ad463SRuss Weight }
1356a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_unregister);
1357a31ad463SRuss Weight 
1358c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev,
1359c92316bfSLuis R. Rodriguez 				struct device_attribute *attr,
1360c92316bfSLuis R. Rodriguez 				char *buf)
1361c92316bfSLuis R. Rodriguez {
1362c92316bfSLuis R. Rodriguez 	return test_dev_config_show_int(buf, test_fw_config->test_result);
1363c92316bfSLuis R. Rodriguez }
1364c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result);
1365c92316bfSLuis R. Rodriguez 
1366c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev,
1367c92316bfSLuis R. Rodriguez 					  struct device_attribute *attr,
1368c92316bfSLuis R. Rodriguez 					  const char *buf, size_t count)
1369c92316bfSLuis R. Rodriguez {
1370c92316bfSLuis R. Rodriguez 	test_release_all_firmware();
1371c92316bfSLuis R. Rodriguez 	return count;
1372c92316bfSLuis R. Rodriguez }
1373c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware);
1374c92316bfSLuis R. Rodriguez 
1375c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev,
1376c92316bfSLuis R. Rodriguez 				  struct device_attribute *attr,
1377c92316bfSLuis R. Rodriguez 				  char *buf)
1378c92316bfSLuis R. Rodriguez {
1379c92316bfSLuis R. Rodriguez 	struct test_batched_req *req;
1380c92316bfSLuis R. Rodriguez 	u8 idx;
1381c92316bfSLuis R. Rodriguez 	ssize_t rc = 0;
1382c92316bfSLuis R. Rodriguez 
1383c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
1384c92316bfSLuis R. Rodriguez 
1385c92316bfSLuis R. Rodriguez 	idx = test_fw_config->read_fw_idx;
1386c92316bfSLuis R. Rodriguez 	if (idx >= test_fw_config->num_requests) {
1387c92316bfSLuis R. Rodriguez 		rc = -ERANGE;
1388c92316bfSLuis R. Rodriguez 		goto out;
1389c92316bfSLuis R. Rodriguez 	}
1390c92316bfSLuis R. Rodriguez 
1391c92316bfSLuis R. Rodriguez 	if (!test_fw_config->reqs) {
1392c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
1393c92316bfSLuis R. Rodriguez 		goto out;
1394c92316bfSLuis R. Rodriguez 	}
1395c92316bfSLuis R. Rodriguez 
1396c92316bfSLuis R. Rodriguez 	req = &test_fw_config->reqs[idx];
1397c92316bfSLuis R. Rodriguez 	if (!req->fw) {
1398c92316bfSLuis R. Rodriguez 		pr_err("#%u: failed to async load firmware\n", idx);
1399c92316bfSLuis R. Rodriguez 		rc = -ENOENT;
1400c92316bfSLuis R. Rodriguez 		goto out;
1401c92316bfSLuis R. Rodriguez 	}
1402c92316bfSLuis R. Rodriguez 
1403c92316bfSLuis R. Rodriguez 	pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1404c92316bfSLuis R. Rodriguez 
1405c92316bfSLuis R. Rodriguez 	if (req->fw->size > PAGE_SIZE) {
1406c92316bfSLuis R. Rodriguez 		pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1407c92316bfSLuis R. Rodriguez 		rc = -EINVAL;
14088bb0a886SColin Ian King 		goto out;
1409c92316bfSLuis R. Rodriguez 	}
1410c92316bfSLuis R. Rodriguez 	memcpy(buf, req->fw->data, req->fw->size);
1411c92316bfSLuis R. Rodriguez 
1412c92316bfSLuis R. Rodriguez 	rc = req->fw->size;
1413c92316bfSLuis R. Rodriguez out:
1414c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1415c92316bfSLuis R. Rodriguez 
1416c92316bfSLuis R. Rodriguez 	return rc;
1417c92316bfSLuis R. Rodriguez }
1418c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware);
1419c92316bfSLuis R. Rodriguez 
1420a31ad463SRuss Weight static ssize_t upload_read_show(struct device *dev,
1421a31ad463SRuss Weight 				struct device_attribute *attr,
1422a31ad463SRuss Weight 				char *buf)
1423a31ad463SRuss Weight {
1424185b29c6SDan Carpenter 	struct test_firmware_upload *tst = NULL;
1425185b29c6SDan Carpenter 	struct test_firmware_upload *tst_iter;
1426a31ad463SRuss Weight 	int ret = -EINVAL;
1427a31ad463SRuss Weight 
1428a31ad463SRuss Weight 	if (!test_fw_config->upload_name) {
1429a31ad463SRuss Weight 		pr_err("Set config_upload_name before using upload_read\n");
1430a31ad463SRuss Weight 		return -EINVAL;
1431a31ad463SRuss Weight 	}
1432a31ad463SRuss Weight 
1433a31ad463SRuss Weight 	mutex_lock(&test_fw_mutex);
1434185b29c6SDan Carpenter 	list_for_each_entry(tst_iter, &test_upload_list, node)
1435185b29c6SDan Carpenter 		if (tst_iter->name == test_fw_config->upload_name) {
1436185b29c6SDan Carpenter 			tst = tst_iter;
1437a31ad463SRuss Weight 			break;
1438185b29c6SDan Carpenter 		}
1439a31ad463SRuss Weight 
1440185b29c6SDan Carpenter 	if (!tst) {
1441a31ad463SRuss Weight 		pr_err("Firmware name not found: %s\n",
1442a31ad463SRuss Weight 		       test_fw_config->upload_name);
1443a31ad463SRuss Weight 		goto out;
1444a31ad463SRuss Weight 	}
1445a31ad463SRuss Weight 
1446a31ad463SRuss Weight 	if (tst->size > PAGE_SIZE) {
1447a31ad463SRuss Weight 		pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1448a31ad463SRuss Weight 		goto out;
1449a31ad463SRuss Weight 	}
1450a31ad463SRuss Weight 
1451a31ad463SRuss Weight 	memcpy(buf, tst->buf, tst->size);
1452a31ad463SRuss Weight 	ret = tst->size;
1453a31ad463SRuss Weight out:
1454a31ad463SRuss Weight 	mutex_unlock(&test_fw_mutex);
1455a31ad463SRuss Weight 	return ret;
1456a31ad463SRuss Weight }
1457a31ad463SRuss Weight static DEVICE_ATTR_RO(upload_read);
1458a31ad463SRuss Weight 
1459083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name)          &dev_attr_##name.attr
1460083a93b0SLuis R. Rodriguez 
1461083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = {
1462c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(reset),
1463c92316bfSLuis R. Rodriguez 
1464c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config),
1465c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_name),
1466c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_num_requests),
14677feebfa4SScott Branden 	TEST_FW_DEV_ATTR(config_into_buf),
14685d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_buf_size),
14695d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_file_offset),
14705d90e05cSScott Branden 	TEST_FW_DEV_ATTR(config_partial),
1471c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_sync_direct),
1472c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_send_uevent),
1473c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(config_read_fw_idx),
1474a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(config_upload_name),
1475c92316bfSLuis R. Rodriguez 
1476c92316bfSLuis R. Rodriguez 	/* These don't use the config at all - they could be ported! */
1477083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_request),
1478083a93b0SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_async_request),
1479061132d2SLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_custom_fallback),
1480548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1481548193cbSHans de Goede 	TEST_FW_DEV_ATTR(trigger_request_platform),
1482548193cbSHans de Goede #endif
1483c92316bfSLuis R. Rodriguez 
1484c92316bfSLuis R. Rodriguez 	/* These use the config and can use the test_result */
1485c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests),
1486c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1487c92316bfSLuis R. Rodriguez 
1488c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(release_all_firmware),
1489c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(test_result),
1490c92316bfSLuis R. Rodriguez 	TEST_FW_DEV_ATTR(read_firmware),
1491a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(upload_read),
1492a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(upload_register),
1493a31ad463SRuss Weight 	TEST_FW_DEV_ATTR(upload_unregister),
1494083a93b0SLuis R. Rodriguez 	NULL,
1495083a93b0SLuis R. Rodriguez };
1496083a93b0SLuis R. Rodriguez 
1497083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev);
1498083a93b0SLuis R. Rodriguez 
149967fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = {
150067fd553cSLuis R. Rodriguez 	.minor          = MISC_DYNAMIC_MINOR,
150167fd553cSLuis R. Rodriguez 	.name           = "test_firmware",
150267fd553cSLuis R. Rodriguez 	.fops           = &test_fw_fops,
1503083a93b0SLuis R. Rodriguez 	.groups 	= test_dev_groups,
150467fd553cSLuis R. Rodriguez };
150567fd553cSLuis R. Rodriguez 
15060a8adf58SKees Cook static int __init test_firmware_init(void)
15070a8adf58SKees Cook {
15080a8adf58SKees Cook 	int rc;
15090a8adf58SKees Cook 
1510c92316bfSLuis R. Rodriguez 	test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1511c92316bfSLuis R. Rodriguez 	if (!test_fw_config)
1512c92316bfSLuis R. Rodriguez 		return -ENOMEM;
1513c92316bfSLuis R. Rodriguez 
1514c92316bfSLuis R. Rodriguez 	rc = __test_firmware_config_init();
1515d4fddac5SWenwen Wang 	if (rc) {
1516d4fddac5SWenwen Wang 		kfree(test_fw_config);
1517d4fddac5SWenwen Wang 		pr_err("could not init firmware test config: %d\n", rc);
1518c92316bfSLuis R. Rodriguez 		return rc;
1519d4fddac5SWenwen Wang 	}
1520c92316bfSLuis R. Rodriguez 
15210a8adf58SKees Cook 	rc = misc_register(&test_fw_misc_device);
15220a8adf58SKees Cook 	if (rc) {
15237610615eSZhengchao Shao 		__test_firmware_config_free();
1524c92316bfSLuis R. Rodriguez 		kfree(test_fw_config);
15250a8adf58SKees Cook 		pr_err("could not register misc device: %d\n", rc);
15260a8adf58SKees Cook 		return rc;
15270a8adf58SKees Cook 	}
1528eb910947SBrian Norris 
15290a8adf58SKees Cook 	pr_warn("interface ready\n");
15300a8adf58SKees Cook 
15310a8adf58SKees Cook 	return 0;
15320a8adf58SKees Cook }
15330a8adf58SKees Cook 
15340a8adf58SKees Cook module_init(test_firmware_init);
15350a8adf58SKees Cook 
15360a8adf58SKees Cook static void __exit test_firmware_exit(void)
15370a8adf58SKees Cook {
1538c92316bfSLuis R. Rodriguez 	mutex_lock(&test_fw_mutex);
15390a8adf58SKees Cook 	release_firmware(test_firmware);
15400a8adf58SKees Cook 	misc_deregister(&test_fw_misc_device);
1541a31ad463SRuss Weight 	upload_release_all();
1542c92316bfSLuis R. Rodriguez 	__test_firmware_config_free();
1543c92316bfSLuis R. Rodriguez 	kfree(test_fw_config);
1544c92316bfSLuis R. Rodriguez 	mutex_unlock(&test_fw_mutex);
1545c92316bfSLuis R. Rodriguez 
15460a8adf58SKees Cook 	pr_warn("removed interface\n");
15470a8adf58SKees Cook }
15480a8adf58SKees Cook 
15490a8adf58SKees Cook module_exit(test_firmware_exit);
15500a8adf58SKees Cook 
15510a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>");
15520a8adf58SKees Cook MODULE_LICENSE("GPL");
1553