blob: 06c95505775644d44af3dde867d72131128ee07e [file] [log] [blame]
Thomas Gleixner09c434b2019-05-19 13:08:20 +01001// SPDX-License-Identifier: GPL-2.0-only
Kees Cook0a8adf52014-07-14 14:38:12 -07002/*
3 * This module provides an interface to trigger and test firmware loading.
4 *
5 * It is designed to be used for basic evaluation of the firmware loading
6 * subsystem (for example when validating firmware verification). It lacks
7 * any extra dependencies, and will not normally be loaded by the system
8 * unless explicitly requested by name.
9 */
10
11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/printk.h>
Brian Norriseb910942015-12-09 14:50:27 -080016#include <linux/completion.h>
Kees Cook0a8adf52014-07-14 14:38:12 -070017#include <linux/firmware.h>
18#include <linux/device.h>
19#include <linux/fs.h>
20#include <linux/miscdevice.h>
Scott Branden7feebfa2019-08-22 11:40:04 -070021#include <linux/sizes.h>
Kees Cook0a8adf52014-07-14 14:38:12 -070022#include <linux/slab.h>
23#include <linux/uaccess.h>
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070024#include <linux/delay.h>
25#include <linux/kthread.h>
Randy Dunlap514c6032018-04-05 16:25:34 -070026#include <linux/vmalloc.h>
Hans de Goede548193c2020-01-15 17:35:49 +010027#include <linux/efi_embedded_fw.h>
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070028
Kees Cookbaaabec2020-09-09 15:53:54 -070029MODULE_IMPORT_NS(TEST_FIRMWARE);
30
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070031#define TEST_FIRMWARE_NAME "test-firmware.bin"
32#define TEST_FIRMWARE_NUM_REQS 4
Scott Branden7feebfa2019-08-22 11:40:04 -070033#define TEST_FIRMWARE_BUF_SIZE SZ_1K
Kees Cook0a8adf52014-07-14 14:38:12 -070034
35static DEFINE_MUTEX(test_fw_mutex);
36static const struct firmware *test_firmware;
37
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070038struct test_batched_req {
39 u8 idx;
40 int rc;
41 bool sent;
42 const struct firmware *fw;
43 const char *name;
44 struct completion completion;
45 struct task_struct *task;
46 struct device *dev;
47};
48
49/**
50 * test_config - represents configuration for the test for different triggers
51 *
52 * @name: the name of the firmware file to look for
Scott Branden7feebfa2019-08-22 11:40:04 -070053 * @into_buf: when the into_buf is used if this is true
54 * request_firmware_into_buf() will be used instead.
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070055 * @sync_direct: when the sync trigger is used if this is true
56 * request_firmware_direct() will be used instead.
57 * @send_uevent: whether or not to send a uevent for async requests
58 * @num_requests: number of requests to try per test case. This is trigger
59 * specific.
60 * @reqs: stores all requests information
61 * @read_fw_idx: index of thread from which we want to read firmware results
62 * from through the read_fw trigger.
63 * @test_result: a test may use this to collect the result from the call
64 * of the request_firmware*() calls used in their tests. In order of
65 * priority we always keep first any setup error. If no setup errors were
66 * found then we move on to the first error encountered while running the
67 * API. Note that for async calls this typically will be a successful
68 * result (0) unless of course you've used bogus parameters, or the system
69 * is out of memory. In the async case the callback is expected to do a
70 * bit more homework to figure out what happened, unfortunately the only
71 * information passed today on error is the fact that no firmware was
72 * found so we can only assume -ENOENT on async calls if the firmware is
73 * NULL.
74 *
75 * Errors you can expect:
76 *
77 * API specific:
78 *
79 * 0: success for sync, for async it means request was sent
80 * -EINVAL: invalid parameters or request
81 * -ENOENT: files not found
82 *
83 * System environment:
84 *
85 * -ENOMEM: memory pressure on system
86 * -ENODEV: out of number of devices to test
87 * -EINVAL: an unexpected error has occurred
88 * @req_firmware: if @sync_direct is true this is set to
89 * request_firmware_direct(), otherwise request_firmware()
90 */
91struct test_config {
92 char *name;
Scott Branden7feebfa2019-08-22 11:40:04 -070093 bool into_buf;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070094 bool sync_direct;
95 bool send_uevent;
96 u8 num_requests;
97 u8 read_fw_idx;
98
99 /*
100 * These below don't belong her but we'll move them once we create
101 * a struct fw_test_device and stuff the misc_dev under there later.
102 */
103 struct test_batched_req *reqs;
104 int test_result;
105 int (*req_firmware)(const struct firmware **fw, const char *name,
106 struct device *device);
107};
108
Wei Yongjun76f8ab12018-01-11 11:13:45 +0000109static struct test_config *test_fw_config;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700110
Kees Cook0a8adf52014-07-14 14:38:12 -0700111static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
112 size_t size, loff_t *offset)
113{
114 ssize_t rc = 0;
115
116 mutex_lock(&test_fw_mutex);
117 if (test_firmware)
118 rc = simple_read_from_buffer(buf, size, offset,
119 test_firmware->data,
120 test_firmware->size);
121 mutex_unlock(&test_fw_mutex);
122 return rc;
123}
124
125static const struct file_operations test_fw_fops = {
126 .owner = THIS_MODULE,
127 .read = test_fw_misc_read,
128};
129
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700130static void __test_release_all_firmware(void)
131{
132 struct test_batched_req *req;
133 u8 i;
134
135 if (!test_fw_config->reqs)
136 return;
137
138 for (i = 0; i < test_fw_config->num_requests; i++) {
139 req = &test_fw_config->reqs[i];
140 if (req->fw)
141 release_firmware(req->fw);
142 }
143
144 vfree(test_fw_config->reqs);
145 test_fw_config->reqs = NULL;
146}
147
148static void test_release_all_firmware(void)
149{
150 mutex_lock(&test_fw_mutex);
151 __test_release_all_firmware();
152 mutex_unlock(&test_fw_mutex);
153}
154
155
156static void __test_firmware_config_free(void)
157{
158 __test_release_all_firmware();
159 kfree_const(test_fw_config->name);
160 test_fw_config->name = NULL;
161}
162
163/*
164 * XXX: move to kstrncpy() once merged.
165 *
166 * Users should use kfree_const() when freeing these.
167 */
168static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
169{
170 *dst = kstrndup(name, count, gfp);
171 if (!*dst)
172 return -ENOSPC;
173 return count;
174}
175
176static int __test_firmware_config_init(void)
177{
178 int ret;
179
180 ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
181 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
182 if (ret < 0)
183 goto out;
184
185 test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
186 test_fw_config->send_uevent = true;
Scott Branden7feebfa2019-08-22 11:40:04 -0700187 test_fw_config->into_buf = false;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700188 test_fw_config->sync_direct = false;
189 test_fw_config->req_firmware = request_firmware;
190 test_fw_config->test_result = 0;
191 test_fw_config->reqs = NULL;
192
193 return 0;
194
195out:
196 __test_firmware_config_free();
197 return ret;
198}
199
200static ssize_t reset_store(struct device *dev,
201 struct device_attribute *attr,
202 const char *buf, size_t count)
203{
204 int ret;
205
206 mutex_lock(&test_fw_mutex);
207
208 __test_firmware_config_free();
209
210 ret = __test_firmware_config_init();
211 if (ret < 0) {
212 ret = -ENOMEM;
213 pr_err("could not alloc settings for config trigger: %d\n",
214 ret);
215 goto out;
216 }
217
218 pr_info("reset\n");
219 ret = count;
220
221out:
222 mutex_unlock(&test_fw_mutex);
223
224 return ret;
225}
226static DEVICE_ATTR_WO(reset);
227
228static ssize_t config_show(struct device *dev,
229 struct device_attribute *attr,
230 char *buf)
231{
232 int len = 0;
233
234 mutex_lock(&test_fw_mutex);
235
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300236 len += scnprintf(buf, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700237 "Custom trigger configuration for: %s\n",
238 dev_name(dev));
239
240 if (test_fw_config->name)
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300241 len += scnprintf(buf+len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700242 "name:\t%s\n",
243 test_fw_config->name);
244 else
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300245 len += scnprintf(buf+len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700246 "name:\tEMTPY\n");
247
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300248 len += scnprintf(buf+len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700249 "num_requests:\t%u\n", test_fw_config->num_requests);
250
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300251 len += scnprintf(buf+len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700252 "send_uevent:\t\t%s\n",
253 test_fw_config->send_uevent ?
254 "FW_ACTION_HOTPLUG" :
255 "FW_ACTION_NOHOTPLUG");
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300256 len += scnprintf(buf+len, PAGE_SIZE - len,
Scott Branden7feebfa2019-08-22 11:40:04 -0700257 "into_buf:\t\t%s\n",
258 test_fw_config->into_buf ? "true" : "false");
259 len += scnprintf(buf+len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700260 "sync_direct:\t\t%s\n",
261 test_fw_config->sync_direct ? "true" : "false");
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300262 len += scnprintf(buf+len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700263 "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
264
265 mutex_unlock(&test_fw_mutex);
266
267 return len;
268}
269static DEVICE_ATTR_RO(config);
270
271static ssize_t config_name_store(struct device *dev,
272 struct device_attribute *attr,
273 const char *buf, size_t count)
274{
275 int ret;
276
277 mutex_lock(&test_fw_mutex);
278 kfree_const(test_fw_config->name);
279 ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
280 mutex_unlock(&test_fw_mutex);
281
282 return ret;
283}
284
285/*
286 * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
287 */
288static ssize_t config_test_show_str(char *dst,
289 char *src)
290{
291 int len;
292
293 mutex_lock(&test_fw_mutex);
294 len = snprintf(dst, PAGE_SIZE, "%s\n", src);
295 mutex_unlock(&test_fw_mutex);
296
297 return len;
298}
299
300static int test_dev_config_update_bool(const char *buf, size_t size,
301 bool *cfg)
302{
303 int ret;
304
305 mutex_lock(&test_fw_mutex);
306 if (strtobool(buf, cfg) < 0)
307 ret = -EINVAL;
308 else
309 ret = size;
310 mutex_unlock(&test_fw_mutex);
311
312 return ret;
313}
314
Scott Branden55623262020-04-14 17:25:17 -0700315static ssize_t test_dev_config_show_bool(char *buf, bool val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700316{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700317 return snprintf(buf, PAGE_SIZE, "%d\n", val);
318}
319
Scott Branden55623262020-04-14 17:25:17 -0700320static ssize_t test_dev_config_show_int(char *buf, int val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700321{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700322 return snprintf(buf, PAGE_SIZE, "%d\n", val);
323}
324
325static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
326{
327 int ret;
328 long new;
329
330 ret = kstrtol(buf, 10, &new);
331 if (ret)
332 return ret;
333
334 if (new > U8_MAX)
335 return -EINVAL;
336
337 mutex_lock(&test_fw_mutex);
338 *(u8 *)cfg = new;
339 mutex_unlock(&test_fw_mutex);
340
341 /* Always return full write size even if we didn't consume all */
342 return size;
343}
344
Scott Branden55623262020-04-14 17:25:17 -0700345static ssize_t test_dev_config_show_u8(char *buf, u8 val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700346{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700347 return snprintf(buf, PAGE_SIZE, "%u\n", val);
348}
349
350static ssize_t config_name_show(struct device *dev,
351 struct device_attribute *attr,
352 char *buf)
353{
354 return config_test_show_str(buf, test_fw_config->name);
355}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800356static DEVICE_ATTR_RW(config_name);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700357
358static ssize_t config_num_requests_store(struct device *dev,
359 struct device_attribute *attr,
360 const char *buf, size_t count)
361{
362 int rc;
363
364 mutex_lock(&test_fw_mutex);
365 if (test_fw_config->reqs) {
366 pr_err("Must call release_all_firmware prior to changing config\n");
367 rc = -EINVAL;
Wei Yongjuna5e19232018-01-11 11:12:55 +0000368 mutex_unlock(&test_fw_mutex);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700369 goto out;
370 }
371 mutex_unlock(&test_fw_mutex);
372
373 rc = test_dev_config_update_u8(buf, count,
374 &test_fw_config->num_requests);
375
376out:
377 return rc;
378}
379
380static ssize_t config_num_requests_show(struct device *dev,
381 struct device_attribute *attr,
382 char *buf)
383{
384 return test_dev_config_show_u8(buf, test_fw_config->num_requests);
385}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800386static DEVICE_ATTR_RW(config_num_requests);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700387
Scott Branden7feebfa2019-08-22 11:40:04 -0700388static ssize_t config_into_buf_store(struct device *dev,
389 struct device_attribute *attr,
390 const char *buf, size_t count)
391{
392 return test_dev_config_update_bool(buf,
393 count,
394 &test_fw_config->into_buf);
395}
396
397static ssize_t config_into_buf_show(struct device *dev,
398 struct device_attribute *attr,
399 char *buf)
400{
401 return test_dev_config_show_bool(buf, test_fw_config->into_buf);
402}
403static DEVICE_ATTR_RW(config_into_buf);
404
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700405static ssize_t config_sync_direct_store(struct device *dev,
406 struct device_attribute *attr,
407 const char *buf, size_t count)
408{
409 int rc = test_dev_config_update_bool(buf, count,
410 &test_fw_config->sync_direct);
411
412 if (rc == count)
413 test_fw_config->req_firmware = test_fw_config->sync_direct ?
414 request_firmware_direct :
415 request_firmware;
416 return rc;
417}
418
419static ssize_t config_sync_direct_show(struct device *dev,
420 struct device_attribute *attr,
421 char *buf)
422{
423 return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
424}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800425static DEVICE_ATTR_RW(config_sync_direct);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700426
427static ssize_t config_send_uevent_store(struct device *dev,
428 struct device_attribute *attr,
429 const char *buf, size_t count)
430{
431 return test_dev_config_update_bool(buf, count,
432 &test_fw_config->send_uevent);
433}
434
435static ssize_t config_send_uevent_show(struct device *dev,
436 struct device_attribute *attr,
437 char *buf)
438{
439 return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
440}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800441static DEVICE_ATTR_RW(config_send_uevent);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700442
443static ssize_t config_read_fw_idx_store(struct device *dev,
444 struct device_attribute *attr,
445 const char *buf, size_t count)
446{
447 return test_dev_config_update_u8(buf, count,
448 &test_fw_config->read_fw_idx);
449}
450
451static ssize_t config_read_fw_idx_show(struct device *dev,
452 struct device_attribute *attr,
453 char *buf)
454{
455 return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
456}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800457static DEVICE_ATTR_RW(config_read_fw_idx);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700458
459
Kees Cook0a8adf52014-07-14 14:38:12 -0700460static ssize_t trigger_request_store(struct device *dev,
461 struct device_attribute *attr,
462 const char *buf, size_t count)
463{
464 int rc;
465 char *name;
466
Brian Norrisbe4a1322015-12-09 14:50:26 -0800467 name = kstrndup(buf, count, GFP_KERNEL);
Kees Cook0a8adf52014-07-14 14:38:12 -0700468 if (!name)
469 return -ENOSPC;
Kees Cook0a8adf52014-07-14 14:38:12 -0700470
471 pr_info("loading '%s'\n", name);
472
473 mutex_lock(&test_fw_mutex);
474 release_firmware(test_firmware);
475 test_firmware = NULL;
476 rc = request_firmware(&test_firmware, name, dev);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800477 if (rc) {
Kees Cook0a8adf52014-07-14 14:38:12 -0700478 pr_info("load of '%s' failed: %d\n", name, rc);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800479 goto out;
480 }
481 pr_info("loaded: %zu\n", test_firmware->size);
482 rc = count;
483
484out:
Kees Cook0a8adf52014-07-14 14:38:12 -0700485 mutex_unlock(&test_fw_mutex);
486
487 kfree(name);
488
Brian Norris47e0bbb2015-12-09 14:50:25 -0800489 return rc;
Kees Cook0a8adf52014-07-14 14:38:12 -0700490}
491static DEVICE_ATTR_WO(trigger_request);
492
Hans de Goede548193c2020-01-15 17:35:49 +0100493#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
Kees Cookbaaabec2020-09-09 15:53:54 -0700494extern struct list_head efi_embedded_fw_list;
495extern bool efi_embedded_fw_checked;
496
Hans de Goede548193c2020-01-15 17:35:49 +0100497static ssize_t trigger_request_platform_store(struct device *dev,
498 struct device_attribute *attr,
499 const char *buf, size_t count)
500{
501 static const u8 test_data[] = {
502 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
503 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
504 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
505 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
506 };
507 struct efi_embedded_fw efi_embedded_fw;
508 const struct firmware *firmware = NULL;
Kees Cookbaaabec2020-09-09 15:53:54 -0700509 bool saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100510 char *name;
511 int rc;
512
513 name = kstrndup(buf, count, GFP_KERNEL);
514 if (!name)
515 return -ENOSPC;
516
517 pr_info("inserting test platform fw '%s'\n", name);
518 efi_embedded_fw.name = name;
519 efi_embedded_fw.data = (void *)test_data;
520 efi_embedded_fw.length = sizeof(test_data);
521 list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
Kees Cookbaaabec2020-09-09 15:53:54 -0700522 saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
523 efi_embedded_fw_checked = true;
Hans de Goede548193c2020-01-15 17:35:49 +0100524
525 pr_info("loading '%s'\n", name);
526 rc = firmware_request_platform(&firmware, name, dev);
527 if (rc) {
528 pr_info("load of '%s' failed: %d\n", name, rc);
529 goto out;
530 }
531 if (firmware->size != sizeof(test_data) ||
532 memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
533 pr_info("firmware contents mismatch for '%s'\n", name);
534 rc = -EINVAL;
535 goto out;
536 }
537 pr_info("loaded: %zu\n", firmware->size);
538 rc = count;
539
540out:
Kees Cookbaaabec2020-09-09 15:53:54 -0700541 efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100542 release_firmware(firmware);
543 list_del(&efi_embedded_fw.list);
544 kfree(name);
545
546 return rc;
547}
548static DEVICE_ATTR_WO(trigger_request_platform);
549#endif
550
Brian Norriseb910942015-12-09 14:50:27 -0800551static DECLARE_COMPLETION(async_fw_done);
552
553static void trigger_async_request_cb(const struct firmware *fw, void *context)
554{
555 test_firmware = fw;
556 complete(&async_fw_done);
557}
558
559static ssize_t trigger_async_request_store(struct device *dev,
560 struct device_attribute *attr,
561 const char *buf, size_t count)
562{
563 int rc;
564 char *name;
565
566 name = kstrndup(buf, count, GFP_KERNEL);
567 if (!name)
568 return -ENOSPC;
569
570 pr_info("loading '%s'\n", name);
571
572 mutex_lock(&test_fw_mutex);
573 release_firmware(test_firmware);
574 test_firmware = NULL;
575 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
576 NULL, trigger_async_request_cb);
577 if (rc) {
578 pr_info("async load of '%s' failed: %d\n", name, rc);
579 kfree(name);
580 goto out;
581 }
582 /* Free 'name' ASAP, to test for race conditions */
583 kfree(name);
584
585 wait_for_completion(&async_fw_done);
586
587 if (test_firmware) {
588 pr_info("loaded: %zu\n", test_firmware->size);
589 rc = count;
590 } else {
591 pr_err("failed to async load firmware\n");
Scott Branden7feebfa2019-08-22 11:40:04 -0700592 rc = -ENOMEM;
Brian Norriseb910942015-12-09 14:50:27 -0800593 }
594
595out:
596 mutex_unlock(&test_fw_mutex);
597
598 return rc;
599}
600static DEVICE_ATTR_WO(trigger_async_request);
601
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800602static ssize_t trigger_custom_fallback_store(struct device *dev,
603 struct device_attribute *attr,
604 const char *buf, size_t count)
605{
606 int rc;
607 char *name;
608
609 name = kstrndup(buf, count, GFP_KERNEL);
610 if (!name)
611 return -ENOSPC;
612
613 pr_info("loading '%s' using custom fallback mechanism\n", name);
614
615 mutex_lock(&test_fw_mutex);
616 release_firmware(test_firmware);
617 test_firmware = NULL;
618 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
619 dev, GFP_KERNEL, NULL,
620 trigger_async_request_cb);
621 if (rc) {
622 pr_info("async load of '%s' failed: %d\n", name, rc);
623 kfree(name);
624 goto out;
625 }
626 /* Free 'name' ASAP, to test for race conditions */
627 kfree(name);
628
629 wait_for_completion(&async_fw_done);
630
631 if (test_firmware) {
632 pr_info("loaded: %zu\n", test_firmware->size);
633 rc = count;
634 } else {
635 pr_err("failed to async load firmware\n");
636 rc = -ENODEV;
637 }
638
639out:
640 mutex_unlock(&test_fw_mutex);
641
642 return rc;
643}
644static DEVICE_ATTR_WO(trigger_custom_fallback);
645
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700646static int test_fw_run_batch_request(void *data)
647{
648 struct test_batched_req *req = data;
649
650 if (!req) {
651 test_fw_config->test_result = -EINVAL;
652 return -EINVAL;
653 }
654
Scott Branden7feebfa2019-08-22 11:40:04 -0700655 if (test_fw_config->into_buf) {
656 void *test_buf;
657
658 test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
659 if (!test_buf)
660 return -ENOSPC;
661
662 req->rc = request_firmware_into_buf(&req->fw,
663 req->name,
664 req->dev,
665 test_buf,
666 TEST_FIRMWARE_BUF_SIZE);
667 if (!req->fw)
668 kfree(test_buf);
669 } else {
670 req->rc = test_fw_config->req_firmware(&req->fw,
671 req->name,
672 req->dev);
673 }
674
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700675 if (req->rc) {
676 pr_info("#%u: batched sync load failed: %d\n",
677 req->idx, req->rc);
678 if (!test_fw_config->test_result)
679 test_fw_config->test_result = req->rc;
680 } else if (req->fw) {
681 req->sent = true;
682 pr_info("#%u: batched sync loaded %zu\n",
683 req->idx, req->fw->size);
684 }
685 complete(&req->completion);
686
687 req->task = NULL;
688
689 return 0;
690}
691
692/*
693 * We use a kthread as otherwise the kernel serializes all our sync requests
694 * and we would not be able to mimic batched requests on a sync call. Batched
695 * requests on a sync call can for instance happen on a device driver when
696 * multiple cards are used and firmware loading happens outside of probe.
697 */
698static ssize_t trigger_batched_requests_store(struct device *dev,
699 struct device_attribute *attr,
700 const char *buf, size_t count)
701{
702 struct test_batched_req *req;
703 int rc;
704 u8 i;
705
706 mutex_lock(&test_fw_mutex);
707
Kees Cookfad953c2018-06-12 14:27:37 -0700708 test_fw_config->reqs =
709 vzalloc(array3_size(sizeof(struct test_batched_req),
710 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700711 if (!test_fw_config->reqs) {
712 rc = -ENOMEM;
713 goto out_unlock;
714 }
715
716 pr_info("batched sync firmware loading '%s' %u times\n",
717 test_fw_config->name, test_fw_config->num_requests);
718
719 for (i = 0; i < test_fw_config->num_requests; i++) {
720 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700721 req->fw = NULL;
722 req->idx = i;
723 req->name = test_fw_config->name;
724 req->dev = dev;
725 init_completion(&req->completion);
726 req->task = kthread_run(test_fw_run_batch_request, req,
727 "%s-%u", KBUILD_MODNAME, req->idx);
728 if (!req->task || IS_ERR(req->task)) {
729 pr_err("Setting up thread %u failed\n", req->idx);
730 req->task = NULL;
731 rc = -ENOMEM;
732 goto out_bail;
733 }
734 }
735
736 rc = count;
737
738 /*
739 * We require an explicit release to enable more time and delay of
740 * calling release_firmware() to improve our chances of forcing a
741 * batched request. If we instead called release_firmware() right away
742 * then we might miss on an opportunity of having a successful firmware
743 * request pass on the opportunity to be come a batched request.
744 */
745
746out_bail:
747 for (i = 0; i < test_fw_config->num_requests; i++) {
748 req = &test_fw_config->reqs[i];
749 if (req->task || req->sent)
750 wait_for_completion(&req->completion);
751 }
752
753 /* Override any worker error if we had a general setup error */
754 if (rc < 0)
755 test_fw_config->test_result = rc;
756
757out_unlock:
758 mutex_unlock(&test_fw_mutex);
759
760 return rc;
761}
762static DEVICE_ATTR_WO(trigger_batched_requests);
763
764/*
765 * We wait for each callback to return with the lock held, no need to lock here
766 */
767static void trigger_batched_cb(const struct firmware *fw, void *context)
768{
769 struct test_batched_req *req = context;
770
771 if (!req) {
772 test_fw_config->test_result = -EINVAL;
773 return;
774 }
775
776 /* forces *some* batched requests to queue up */
777 if (!req->idx)
778 ssleep(2);
779
780 req->fw = fw;
781
782 /*
783 * Unfortunately the firmware API gives us nothing other than a null FW
784 * if the firmware was not found on async requests. Best we can do is
785 * just assume -ENOENT. A better API would pass the actual return
786 * value to the callback.
787 */
788 if (!fw && !test_fw_config->test_result)
789 test_fw_config->test_result = -ENOENT;
790
791 complete(&req->completion);
792}
793
794static
795ssize_t trigger_batched_requests_async_store(struct device *dev,
796 struct device_attribute *attr,
797 const char *buf, size_t count)
798{
799 struct test_batched_req *req;
800 bool send_uevent;
801 int rc;
802 u8 i;
803
804 mutex_lock(&test_fw_mutex);
805
Kees Cookfad953c2018-06-12 14:27:37 -0700806 test_fw_config->reqs =
807 vzalloc(array3_size(sizeof(struct test_batched_req),
808 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700809 if (!test_fw_config->reqs) {
810 rc = -ENOMEM;
811 goto out;
812 }
813
814 pr_info("batched loading '%s' custom fallback mechanism %u times\n",
815 test_fw_config->name, test_fw_config->num_requests);
816
817 send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG :
818 FW_ACTION_NOHOTPLUG;
819
820 for (i = 0; i < test_fw_config->num_requests; i++) {
821 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700822 req->name = test_fw_config->name;
823 req->fw = NULL;
824 req->idx = i;
825 init_completion(&req->completion);
826 rc = request_firmware_nowait(THIS_MODULE, send_uevent,
827 req->name,
828 dev, GFP_KERNEL, req,
829 trigger_batched_cb);
830 if (rc) {
831 pr_info("#%u: batched async load failed setup: %d\n",
832 i, rc);
833 req->rc = rc;
834 goto out_bail;
835 } else
836 req->sent = true;
837 }
838
839 rc = count;
840
841out_bail:
842
843 /*
844 * We require an explicit release to enable more time and delay of
845 * calling release_firmware() to improve our chances of forcing a
846 * batched request. If we instead called release_firmware() right away
847 * then we might miss on an opportunity of having a successful firmware
848 * request pass on the opportunity to be come a batched request.
849 */
850
851 for (i = 0; i < test_fw_config->num_requests; i++) {
852 req = &test_fw_config->reqs[i];
853 if (req->sent)
854 wait_for_completion(&req->completion);
855 }
856
857 /* Override any worker error if we had a general setup error */
858 if (rc < 0)
859 test_fw_config->test_result = rc;
860
861out:
862 mutex_unlock(&test_fw_mutex);
863
864 return rc;
865}
866static DEVICE_ATTR_WO(trigger_batched_requests_async);
867
868static ssize_t test_result_show(struct device *dev,
869 struct device_attribute *attr,
870 char *buf)
871{
872 return test_dev_config_show_int(buf, test_fw_config->test_result);
873}
874static DEVICE_ATTR_RO(test_result);
875
876static ssize_t release_all_firmware_store(struct device *dev,
877 struct device_attribute *attr,
878 const char *buf, size_t count)
879{
880 test_release_all_firmware();
881 return count;
882}
883static DEVICE_ATTR_WO(release_all_firmware);
884
885static ssize_t read_firmware_show(struct device *dev,
886 struct device_attribute *attr,
887 char *buf)
888{
889 struct test_batched_req *req;
890 u8 idx;
891 ssize_t rc = 0;
892
893 mutex_lock(&test_fw_mutex);
894
895 idx = test_fw_config->read_fw_idx;
896 if (idx >= test_fw_config->num_requests) {
897 rc = -ERANGE;
898 goto out;
899 }
900
901 if (!test_fw_config->reqs) {
902 rc = -EINVAL;
903 goto out;
904 }
905
906 req = &test_fw_config->reqs[idx];
907 if (!req->fw) {
908 pr_err("#%u: failed to async load firmware\n", idx);
909 rc = -ENOENT;
910 goto out;
911 }
912
913 pr_info("#%u: loaded %zu\n", idx, req->fw->size);
914
915 if (req->fw->size > PAGE_SIZE) {
916 pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
917 rc = -EINVAL;
Colin Ian King8bb0a882018-10-19 13:58:01 +0100918 goto out;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700919 }
920 memcpy(buf, req->fw->data, req->fw->size);
921
922 rc = req->fw->size;
923out:
924 mutex_unlock(&test_fw_mutex);
925
926 return rc;
927}
928static DEVICE_ATTR_RO(read_firmware);
929
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -0800930#define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr
931
932static struct attribute *test_dev_attrs[] = {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700933 TEST_FW_DEV_ATTR(reset),
934
935 TEST_FW_DEV_ATTR(config),
936 TEST_FW_DEV_ATTR(config_name),
937 TEST_FW_DEV_ATTR(config_num_requests),
Scott Branden7feebfa2019-08-22 11:40:04 -0700938 TEST_FW_DEV_ATTR(config_into_buf),
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700939 TEST_FW_DEV_ATTR(config_sync_direct),
940 TEST_FW_DEV_ATTR(config_send_uevent),
941 TEST_FW_DEV_ATTR(config_read_fw_idx),
942
943 /* These don't use the config at all - they could be ported! */
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -0800944 TEST_FW_DEV_ATTR(trigger_request),
945 TEST_FW_DEV_ATTR(trigger_async_request),
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800946 TEST_FW_DEV_ATTR(trigger_custom_fallback),
Hans de Goede548193c2020-01-15 17:35:49 +0100947#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
948 TEST_FW_DEV_ATTR(trigger_request_platform),
949#endif
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700950
951 /* These use the config and can use the test_result */
952 TEST_FW_DEV_ATTR(trigger_batched_requests),
953 TEST_FW_DEV_ATTR(trigger_batched_requests_async),
954
955 TEST_FW_DEV_ATTR(release_all_firmware),
956 TEST_FW_DEV_ATTR(test_result),
957 TEST_FW_DEV_ATTR(read_firmware),
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -0800958 NULL,
959};
960
961ATTRIBUTE_GROUPS(test_dev);
962
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -0800963static struct miscdevice test_fw_misc_device = {
964 .minor = MISC_DYNAMIC_MINOR,
965 .name = "test_firmware",
966 .fops = &test_fw_fops,
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -0800967 .groups = test_dev_groups,
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -0800968};
969
Kees Cook0a8adf52014-07-14 14:38:12 -0700970static int __init test_firmware_init(void)
971{
972 int rc;
973
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700974 test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
975 if (!test_fw_config)
976 return -ENOMEM;
977
978 rc = __test_firmware_config_init();
Wenwen Wangd4fddac2019-07-14 01:11:35 -0500979 if (rc) {
980 kfree(test_fw_config);
981 pr_err("could not init firmware test config: %d\n", rc);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700982 return rc;
Wenwen Wangd4fddac2019-07-14 01:11:35 -0500983 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700984
Kees Cook0a8adf52014-07-14 14:38:12 -0700985 rc = misc_register(&test_fw_misc_device);
986 if (rc) {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700987 kfree(test_fw_config);
Kees Cook0a8adf52014-07-14 14:38:12 -0700988 pr_err("could not register misc device: %d\n", rc);
989 return rc;
990 }
Brian Norriseb910942015-12-09 14:50:27 -0800991
Kees Cook0a8adf52014-07-14 14:38:12 -0700992 pr_warn("interface ready\n");
993
994 return 0;
Kees Cook0a8adf52014-07-14 14:38:12 -0700995}
996
997module_init(test_firmware_init);
998
999static void __exit test_firmware_exit(void)
1000{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001001 mutex_lock(&test_fw_mutex);
Kees Cook0a8adf52014-07-14 14:38:12 -07001002 release_firmware(test_firmware);
Kees Cook0a8adf52014-07-14 14:38:12 -07001003 misc_deregister(&test_fw_misc_device);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001004 __test_firmware_config_free();
1005 kfree(test_fw_config);
1006 mutex_unlock(&test_fw_mutex);
1007
Kees Cook0a8adf52014-07-14 14:38:12 -07001008 pr_warn("removed interface\n");
1009}
1010
1011module_exit(test_firmware_exit);
1012
1013MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
1014MODULE_LICENSE("GPL");