blob: b99cf0a50a698254f30d2fe7cec83f1c0ae28b68 [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>
Christophe JAILLET682ca602023-01-14 10:22:03 +010025#include <linux/kstrtox.h>
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070026#include <linux/kthread.h>
Randy Dunlap514c6032018-04-05 16:25:34 -070027#include <linux/vmalloc.h>
Hans de Goede548193c2020-01-15 17:35:49 +010028#include <linux/efi_embedded_fw.h>
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070029
Kees Cookbaaabec2020-09-09 15:53:54 -070030MODULE_IMPORT_NS(TEST_FIRMWARE);
31
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070032#define TEST_FIRMWARE_NAME "test-firmware.bin"
33#define TEST_FIRMWARE_NUM_REQS 4
Scott Branden7feebfa2019-08-22 11:40:04 -070034#define TEST_FIRMWARE_BUF_SIZE SZ_1K
Kees Cook0a8adf52014-07-14 14:38:12 -070035
36static DEFINE_MUTEX(test_fw_mutex);
37static const struct firmware *test_firmware;
38
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070039struct test_batched_req {
40 u8 idx;
41 int rc;
42 bool sent;
43 const struct firmware *fw;
44 const char *name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +020045 const char *fw_buf;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070046 struct completion completion;
47 struct task_struct *task;
48 struct device *dev;
49};
50
51/**
52 * test_config - represents configuration for the test for different triggers
53 *
54 * @name: the name of the firmware file to look for
Scott Branden7feebfa2019-08-22 11:40:04 -070055 * @into_buf: when the into_buf is used if this is true
56 * request_firmware_into_buf() will be used instead.
Scott Branden5d90e052020-10-02 10:38:28 -070057 * @buf_size: size of buf to allocate when into_buf is true
58 * @file_offset: file offset to request when calling request_firmware_into_buf
59 * @partial: partial read opt when calling request_firmware_into_buf
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070060 * @sync_direct: when the sync trigger is used if this is true
61 * request_firmware_direct() will be used instead.
62 * @send_uevent: whether or not to send a uevent for async requests
63 * @num_requests: number of requests to try per test case. This is trigger
64 * specific.
65 * @reqs: stores all requests information
66 * @read_fw_idx: index of thread from which we want to read firmware results
67 * from through the read_fw trigger.
68 * @test_result: a test may use this to collect the result from the call
69 * of the request_firmware*() calls used in their tests. In order of
70 * priority we always keep first any setup error. If no setup errors were
71 * found then we move on to the first error encountered while running the
72 * API. Note that for async calls this typically will be a successful
73 * result (0) unless of course you've used bogus parameters, or the system
74 * is out of memory. In the async case the callback is expected to do a
75 * bit more homework to figure out what happened, unfortunately the only
76 * information passed today on error is the fact that no firmware was
77 * found so we can only assume -ENOENT on async calls if the firmware is
78 * NULL.
79 *
80 * Errors you can expect:
81 *
82 * API specific:
83 *
84 * 0: success for sync, for async it means request was sent
85 * -EINVAL: invalid parameters or request
86 * -ENOENT: files not found
87 *
88 * System environment:
89 *
90 * -ENOMEM: memory pressure on system
91 * -ENODEV: out of number of devices to test
92 * -EINVAL: an unexpected error has occurred
93 * @req_firmware: if @sync_direct is true this is set to
94 * request_firmware_direct(), otherwise request_firmware()
95 */
96struct test_config {
97 char *name;
Scott Branden7feebfa2019-08-22 11:40:04 -070098 bool into_buf;
Scott Branden5d90e052020-10-02 10:38:28 -070099 size_t buf_size;
100 size_t file_offset;
101 bool partial;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700102 bool sync_direct;
103 bool send_uevent;
104 u8 num_requests;
105 u8 read_fw_idx;
106
107 /*
108 * These below don't belong her but we'll move them once we create
109 * a struct fw_test_device and stuff the misc_dev under there later.
110 */
111 struct test_batched_req *reqs;
112 int test_result;
113 int (*req_firmware)(const struct firmware **fw, const char *name,
114 struct device *device);
115};
116
Wei Yongjun76f8ab12018-01-11 11:13:45 +0000117static struct test_config *test_fw_config;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700118
Kees Cook0a8adf52014-07-14 14:38:12 -0700119static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
120 size_t size, loff_t *offset)
121{
122 ssize_t rc = 0;
123
124 mutex_lock(&test_fw_mutex);
125 if (test_firmware)
126 rc = simple_read_from_buffer(buf, size, offset,
127 test_firmware->data,
128 test_firmware->size);
129 mutex_unlock(&test_fw_mutex);
130 return rc;
131}
132
133static const struct file_operations test_fw_fops = {
134 .owner = THIS_MODULE,
135 .read = test_fw_misc_read,
136};
137
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700138static void __test_release_all_firmware(void)
139{
140 struct test_batched_req *req;
141 u8 i;
142
143 if (!test_fw_config->reqs)
144 return;
145
146 for (i = 0; i < test_fw_config->num_requests; i++) {
147 req = &test_fw_config->reqs[i];
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200148 if (req->fw) {
149 if (req->fw_buf) {
150 kfree_const(req->fw_buf);
151 req->fw_buf = NULL;
152 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700153 release_firmware(req->fw);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200154 req->fw = NULL;
155 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700156 }
157
158 vfree(test_fw_config->reqs);
159 test_fw_config->reqs = NULL;
160}
161
162static void test_release_all_firmware(void)
163{
164 mutex_lock(&test_fw_mutex);
165 __test_release_all_firmware();
166 mutex_unlock(&test_fw_mutex);
167}
168
169
170static void __test_firmware_config_free(void)
171{
172 __test_release_all_firmware();
173 kfree_const(test_fw_config->name);
174 test_fw_config->name = NULL;
175}
176
177/*
178 * XXX: move to kstrncpy() once merged.
179 *
180 * Users should use kfree_const() when freeing these.
181 */
182static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
183{
184 *dst = kstrndup(name, count, gfp);
185 if (!*dst)
186 return -ENOSPC;
187 return count;
188}
189
190static int __test_firmware_config_init(void)
191{
192 int ret;
193
194 ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
195 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
196 if (ret < 0)
197 goto out;
198
199 test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
200 test_fw_config->send_uevent = true;
Scott Branden7feebfa2019-08-22 11:40:04 -0700201 test_fw_config->into_buf = false;
Scott Branden5d90e052020-10-02 10:38:28 -0700202 test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
203 test_fw_config->file_offset = 0;
204 test_fw_config->partial = false;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700205 test_fw_config->sync_direct = false;
206 test_fw_config->req_firmware = request_firmware;
207 test_fw_config->test_result = 0;
208 test_fw_config->reqs = NULL;
209
210 return 0;
211
212out:
213 __test_firmware_config_free();
214 return ret;
215}
216
217static ssize_t reset_store(struct device *dev,
218 struct device_attribute *attr,
219 const char *buf, size_t count)
220{
221 int ret;
222
223 mutex_lock(&test_fw_mutex);
224
225 __test_firmware_config_free();
226
227 ret = __test_firmware_config_init();
228 if (ret < 0) {
229 ret = -ENOMEM;
230 pr_err("could not alloc settings for config trigger: %d\n",
231 ret);
232 goto out;
233 }
234
235 pr_info("reset\n");
236 ret = count;
237
238out:
239 mutex_unlock(&test_fw_mutex);
240
241 return ret;
242}
243static DEVICE_ATTR_WO(reset);
244
245static ssize_t config_show(struct device *dev,
246 struct device_attribute *attr,
247 char *buf)
248{
249 int len = 0;
250
251 mutex_lock(&test_fw_mutex);
252
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300253 len += scnprintf(buf, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700254 "Custom trigger configuration for: %s\n",
255 dev_name(dev));
256
257 if (test_fw_config->name)
Scott Branden5d90e052020-10-02 10:38:28 -0700258 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700259 "name:\t%s\n",
260 test_fw_config->name);
261 else
Scott Branden5d90e052020-10-02 10:38:28 -0700262 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700263 "name:\tEMTPY\n");
264
Scott Branden5d90e052020-10-02 10:38:28 -0700265 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700266 "num_requests:\t%u\n", test_fw_config->num_requests);
267
Scott Branden5d90e052020-10-02 10:38:28 -0700268 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700269 "send_uevent:\t\t%s\n",
270 test_fw_config->send_uevent ?
271 "FW_ACTION_HOTPLUG" :
272 "FW_ACTION_NOHOTPLUG");
Scott Branden5d90e052020-10-02 10:38:28 -0700273 len += scnprintf(buf + len, PAGE_SIZE - len,
Scott Branden7feebfa2019-08-22 11:40:04 -0700274 "into_buf:\t\t%s\n",
275 test_fw_config->into_buf ? "true" : "false");
Scott Branden5d90e052020-10-02 10:38:28 -0700276 len += scnprintf(buf + len, PAGE_SIZE - len,
277 "buf_size:\t%zu\n", test_fw_config->buf_size);
278 len += scnprintf(buf + len, PAGE_SIZE - len,
279 "file_offset:\t%zu\n", test_fw_config->file_offset);
280 len += scnprintf(buf + len, PAGE_SIZE - len,
281 "partial:\t\t%s\n",
282 test_fw_config->partial ? "true" : "false");
283 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700284 "sync_direct:\t\t%s\n",
285 test_fw_config->sync_direct ? "true" : "false");
Scott Branden5d90e052020-10-02 10:38:28 -0700286 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700287 "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
288
289 mutex_unlock(&test_fw_mutex);
290
291 return len;
292}
293static DEVICE_ATTR_RO(config);
294
295static ssize_t config_name_store(struct device *dev,
296 struct device_attribute *attr,
297 const char *buf, size_t count)
298{
299 int ret;
300
301 mutex_lock(&test_fw_mutex);
302 kfree_const(test_fw_config->name);
303 ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
304 mutex_unlock(&test_fw_mutex);
305
306 return ret;
307}
308
309/*
310 * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
311 */
312static ssize_t config_test_show_str(char *dst,
313 char *src)
314{
315 int len;
316
317 mutex_lock(&test_fw_mutex);
318 len = snprintf(dst, PAGE_SIZE, "%s\n", src);
319 mutex_unlock(&test_fw_mutex);
320
321 return len;
322}
323
324static int test_dev_config_update_bool(const char *buf, size_t size,
325 bool *cfg)
326{
327 int ret;
328
329 mutex_lock(&test_fw_mutex);
Christophe JAILLET682ca602023-01-14 10:22:03 +0100330 if (kstrtobool(buf, cfg) < 0)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700331 ret = -EINVAL;
332 else
333 ret = size;
334 mutex_unlock(&test_fw_mutex);
335
336 return ret;
337}
338
Scott Branden55623262020-04-14 17:25:17 -0700339static ssize_t test_dev_config_show_bool(char *buf, bool val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700340{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700341 return snprintf(buf, PAGE_SIZE, "%d\n", val);
342}
343
Scott Branden5d90e052020-10-02 10:38:28 -0700344static int test_dev_config_update_size_t(const char *buf,
345 size_t size,
346 size_t *cfg)
347{
348 int ret;
349 long new;
350
351 ret = kstrtol(buf, 10, &new);
352 if (ret)
353 return ret;
354
355 mutex_lock(&test_fw_mutex);
356 *(size_t *)cfg = new;
357 mutex_unlock(&test_fw_mutex);
358
359 /* Always return full write size even if we didn't consume all */
360 return size;
361}
362
363static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
364{
365 return snprintf(buf, PAGE_SIZE, "%zu\n", val);
366}
367
Scott Branden55623262020-04-14 17:25:17 -0700368static ssize_t test_dev_config_show_int(char *buf, int val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700369{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700370 return snprintf(buf, PAGE_SIZE, "%d\n", val);
371}
372
373static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
374{
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800375 u8 val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700376 int ret;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700377
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800378 ret = kstrtou8(buf, 10, &val);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700379 if (ret)
380 return ret;
381
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700382 mutex_lock(&test_fw_mutex);
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800383 *(u8 *)cfg = val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700384 mutex_unlock(&test_fw_mutex);
385
386 /* Always return full write size even if we didn't consume all */
387 return size;
388}
389
Scott Branden55623262020-04-14 17:25:17 -0700390static ssize_t test_dev_config_show_u8(char *buf, u8 val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700391{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700392 return snprintf(buf, PAGE_SIZE, "%u\n", val);
393}
394
395static ssize_t config_name_show(struct device *dev,
396 struct device_attribute *attr,
397 char *buf)
398{
399 return config_test_show_str(buf, test_fw_config->name);
400}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800401static DEVICE_ATTR_RW(config_name);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700402
403static ssize_t config_num_requests_store(struct device *dev,
404 struct device_attribute *attr,
405 const char *buf, size_t count)
406{
407 int rc;
408
409 mutex_lock(&test_fw_mutex);
410 if (test_fw_config->reqs) {
411 pr_err("Must call release_all_firmware prior to changing config\n");
412 rc = -EINVAL;
Wei Yongjuna5e19232018-01-11 11:12:55 +0000413 mutex_unlock(&test_fw_mutex);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700414 goto out;
415 }
416 mutex_unlock(&test_fw_mutex);
417
418 rc = test_dev_config_update_u8(buf, count,
419 &test_fw_config->num_requests);
420
421out:
422 return rc;
423}
424
425static ssize_t config_num_requests_show(struct device *dev,
426 struct device_attribute *attr,
427 char *buf)
428{
429 return test_dev_config_show_u8(buf, test_fw_config->num_requests);
430}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800431static DEVICE_ATTR_RW(config_num_requests);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700432
Scott Branden7feebfa2019-08-22 11:40:04 -0700433static ssize_t config_into_buf_store(struct device *dev,
434 struct device_attribute *attr,
435 const char *buf, size_t count)
436{
437 return test_dev_config_update_bool(buf,
438 count,
439 &test_fw_config->into_buf);
440}
441
442static ssize_t config_into_buf_show(struct device *dev,
443 struct device_attribute *attr,
444 char *buf)
445{
446 return test_dev_config_show_bool(buf, test_fw_config->into_buf);
447}
448static DEVICE_ATTR_RW(config_into_buf);
449
Scott Branden5d90e052020-10-02 10:38:28 -0700450static ssize_t config_buf_size_store(struct device *dev,
451 struct device_attribute *attr,
452 const char *buf, size_t count)
453{
454 int rc;
455
456 mutex_lock(&test_fw_mutex);
457 if (test_fw_config->reqs) {
458 pr_err("Must call release_all_firmware prior to changing config\n");
459 rc = -EINVAL;
460 mutex_unlock(&test_fw_mutex);
461 goto out;
462 }
463 mutex_unlock(&test_fw_mutex);
464
465 rc = test_dev_config_update_size_t(buf, count,
466 &test_fw_config->buf_size);
467
468out:
469 return rc;
470}
471
472static ssize_t config_buf_size_show(struct device *dev,
473 struct device_attribute *attr,
474 char *buf)
475{
476 return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
477}
478static DEVICE_ATTR_RW(config_buf_size);
479
480static ssize_t config_file_offset_store(struct device *dev,
481 struct device_attribute *attr,
482 const char *buf, size_t count)
483{
484 int rc;
485
486 mutex_lock(&test_fw_mutex);
487 if (test_fw_config->reqs) {
488 pr_err("Must call release_all_firmware prior to changing config\n");
489 rc = -EINVAL;
490 mutex_unlock(&test_fw_mutex);
491 goto out;
492 }
493 mutex_unlock(&test_fw_mutex);
494
495 rc = test_dev_config_update_size_t(buf, count,
496 &test_fw_config->file_offset);
497
498out:
499 return rc;
500}
501
502static ssize_t config_file_offset_show(struct device *dev,
503 struct device_attribute *attr,
504 char *buf)
505{
506 return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
507}
508static DEVICE_ATTR_RW(config_file_offset);
509
510static ssize_t config_partial_store(struct device *dev,
511 struct device_attribute *attr,
512 const char *buf, size_t count)
513{
514 return test_dev_config_update_bool(buf,
515 count,
516 &test_fw_config->partial);
517}
518
519static ssize_t config_partial_show(struct device *dev,
520 struct device_attribute *attr,
521 char *buf)
522{
523 return test_dev_config_show_bool(buf, test_fw_config->partial);
524}
525static DEVICE_ATTR_RW(config_partial);
526
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700527static ssize_t config_sync_direct_store(struct device *dev,
528 struct device_attribute *attr,
529 const char *buf, size_t count)
530{
531 int rc = test_dev_config_update_bool(buf, count,
532 &test_fw_config->sync_direct);
533
534 if (rc == count)
535 test_fw_config->req_firmware = test_fw_config->sync_direct ?
536 request_firmware_direct :
537 request_firmware;
538 return rc;
539}
540
541static ssize_t config_sync_direct_show(struct device *dev,
542 struct device_attribute *attr,
543 char *buf)
544{
545 return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
546}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800547static DEVICE_ATTR_RW(config_sync_direct);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700548
549static ssize_t config_send_uevent_store(struct device *dev,
550 struct device_attribute *attr,
551 const char *buf, size_t count)
552{
553 return test_dev_config_update_bool(buf, count,
554 &test_fw_config->send_uevent);
555}
556
557static ssize_t config_send_uevent_show(struct device *dev,
558 struct device_attribute *attr,
559 char *buf)
560{
561 return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
562}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800563static DEVICE_ATTR_RW(config_send_uevent);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700564
565static ssize_t config_read_fw_idx_store(struct device *dev,
566 struct device_attribute *attr,
567 const char *buf, size_t count)
568{
569 return test_dev_config_update_u8(buf, count,
570 &test_fw_config->read_fw_idx);
571}
572
573static ssize_t config_read_fw_idx_show(struct device *dev,
574 struct device_attribute *attr,
575 char *buf)
576{
577 return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
578}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800579static DEVICE_ATTR_RW(config_read_fw_idx);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700580
581
Kees Cook0a8adf52014-07-14 14:38:12 -0700582static ssize_t trigger_request_store(struct device *dev,
583 struct device_attribute *attr,
584 const char *buf, size_t count)
585{
586 int rc;
587 char *name;
588
Brian Norrisbe4a1322015-12-09 14:50:26 -0800589 name = kstrndup(buf, count, GFP_KERNEL);
Kees Cook0a8adf52014-07-14 14:38:12 -0700590 if (!name)
591 return -ENOSPC;
Kees Cook0a8adf52014-07-14 14:38:12 -0700592
593 pr_info("loading '%s'\n", name);
594
595 mutex_lock(&test_fw_mutex);
596 release_firmware(test_firmware);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200597 if (test_fw_config->reqs)
598 __test_release_all_firmware();
Kees Cook0a8adf52014-07-14 14:38:12 -0700599 test_firmware = NULL;
600 rc = request_firmware(&test_firmware, name, dev);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800601 if (rc) {
Kees Cook0a8adf52014-07-14 14:38:12 -0700602 pr_info("load of '%s' failed: %d\n", name, rc);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800603 goto out;
604 }
605 pr_info("loaded: %zu\n", test_firmware->size);
606 rc = count;
607
608out:
Kees Cook0a8adf52014-07-14 14:38:12 -0700609 mutex_unlock(&test_fw_mutex);
610
611 kfree(name);
612
Brian Norris47e0bbb2015-12-09 14:50:25 -0800613 return rc;
Kees Cook0a8adf52014-07-14 14:38:12 -0700614}
615static DEVICE_ATTR_WO(trigger_request);
616
Hans de Goede548193c2020-01-15 17:35:49 +0100617#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
Kees Cookbaaabec2020-09-09 15:53:54 -0700618extern struct list_head efi_embedded_fw_list;
619extern bool efi_embedded_fw_checked;
620
Hans de Goede548193c2020-01-15 17:35:49 +0100621static ssize_t trigger_request_platform_store(struct device *dev,
622 struct device_attribute *attr,
623 const char *buf, size_t count)
624{
625 static const u8 test_data[] = {
626 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
627 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
628 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
629 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
630 };
631 struct efi_embedded_fw efi_embedded_fw;
632 const struct firmware *firmware = NULL;
Kees Cookbaaabec2020-09-09 15:53:54 -0700633 bool saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100634 char *name;
635 int rc;
636
637 name = kstrndup(buf, count, GFP_KERNEL);
638 if (!name)
639 return -ENOSPC;
640
641 pr_info("inserting test platform fw '%s'\n", name);
642 efi_embedded_fw.name = name;
643 efi_embedded_fw.data = (void *)test_data;
644 efi_embedded_fw.length = sizeof(test_data);
645 list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
Kees Cookbaaabec2020-09-09 15:53:54 -0700646 saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
647 efi_embedded_fw_checked = true;
Hans de Goede548193c2020-01-15 17:35:49 +0100648
649 pr_info("loading '%s'\n", name);
650 rc = firmware_request_platform(&firmware, name, dev);
651 if (rc) {
652 pr_info("load of '%s' failed: %d\n", name, rc);
653 goto out;
654 }
655 if (firmware->size != sizeof(test_data) ||
656 memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
657 pr_info("firmware contents mismatch for '%s'\n", name);
658 rc = -EINVAL;
659 goto out;
660 }
661 pr_info("loaded: %zu\n", firmware->size);
662 rc = count;
663
664out:
Kees Cookbaaabec2020-09-09 15:53:54 -0700665 efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100666 release_firmware(firmware);
667 list_del(&efi_embedded_fw.list);
668 kfree(name);
669
670 return rc;
671}
672static DEVICE_ATTR_WO(trigger_request_platform);
673#endif
674
Brian Norriseb910942015-12-09 14:50:27 -0800675static DECLARE_COMPLETION(async_fw_done);
676
677static void trigger_async_request_cb(const struct firmware *fw, void *context)
678{
679 test_firmware = fw;
680 complete(&async_fw_done);
681}
682
683static ssize_t trigger_async_request_store(struct device *dev,
684 struct device_attribute *attr,
685 const char *buf, size_t count)
686{
687 int rc;
688 char *name;
689
690 name = kstrndup(buf, count, GFP_KERNEL);
691 if (!name)
692 return -ENOSPC;
693
694 pr_info("loading '%s'\n", name);
695
696 mutex_lock(&test_fw_mutex);
697 release_firmware(test_firmware);
698 test_firmware = NULL;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200699 if (test_fw_config->reqs)
700 __test_release_all_firmware();
Brian Norriseb910942015-12-09 14:50:27 -0800701 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
702 NULL, trigger_async_request_cb);
703 if (rc) {
704 pr_info("async load of '%s' failed: %d\n", name, rc);
705 kfree(name);
706 goto out;
707 }
708 /* Free 'name' ASAP, to test for race conditions */
709 kfree(name);
710
711 wait_for_completion(&async_fw_done);
712
713 if (test_firmware) {
714 pr_info("loaded: %zu\n", test_firmware->size);
715 rc = count;
716 } else {
717 pr_err("failed to async load firmware\n");
Scott Branden7feebfa2019-08-22 11:40:04 -0700718 rc = -ENOMEM;
Brian Norriseb910942015-12-09 14:50:27 -0800719 }
720
721out:
722 mutex_unlock(&test_fw_mutex);
723
724 return rc;
725}
726static DEVICE_ATTR_WO(trigger_async_request);
727
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800728static ssize_t trigger_custom_fallback_store(struct device *dev,
729 struct device_attribute *attr,
730 const char *buf, size_t count)
731{
732 int rc;
733 char *name;
734
735 name = kstrndup(buf, count, GFP_KERNEL);
736 if (!name)
737 return -ENOSPC;
738
739 pr_info("loading '%s' using custom fallback mechanism\n", name);
740
741 mutex_lock(&test_fw_mutex);
742 release_firmware(test_firmware);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200743 if (test_fw_config->reqs)
744 __test_release_all_firmware();
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800745 test_firmware = NULL;
746 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
747 dev, GFP_KERNEL, NULL,
748 trigger_async_request_cb);
749 if (rc) {
750 pr_info("async load of '%s' failed: %d\n", name, rc);
751 kfree(name);
752 goto out;
753 }
754 /* Free 'name' ASAP, to test for race conditions */
755 kfree(name);
756
757 wait_for_completion(&async_fw_done);
758
759 if (test_firmware) {
760 pr_info("loaded: %zu\n", test_firmware->size);
761 rc = count;
762 } else {
763 pr_err("failed to async load firmware\n");
764 rc = -ENODEV;
765 }
766
767out:
768 mutex_unlock(&test_fw_mutex);
769
770 return rc;
771}
772static DEVICE_ATTR_WO(trigger_custom_fallback);
773
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700774static int test_fw_run_batch_request(void *data)
775{
776 struct test_batched_req *req = data;
777
778 if (!req) {
779 test_fw_config->test_result = -EINVAL;
780 return -EINVAL;
781 }
782
Scott Branden7feebfa2019-08-22 11:40:04 -0700783 if (test_fw_config->into_buf) {
784 void *test_buf;
785
786 test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
787 if (!test_buf)
788 return -ENOSPC;
789
Scott Branden5d90e052020-10-02 10:38:28 -0700790 if (test_fw_config->partial)
791 req->rc = request_partial_firmware_into_buf
792 (&req->fw,
793 req->name,
794 req->dev,
795 test_buf,
796 test_fw_config->buf_size,
797 test_fw_config->file_offset);
798 else
799 req->rc = request_firmware_into_buf
800 (&req->fw,
801 req->name,
802 req->dev,
803 test_buf,
804 test_fw_config->buf_size);
Scott Branden7feebfa2019-08-22 11:40:04 -0700805 if (!req->fw)
806 kfree(test_buf);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200807 else
808 req->fw_buf = test_buf;
Scott Branden7feebfa2019-08-22 11:40:04 -0700809 } else {
810 req->rc = test_fw_config->req_firmware(&req->fw,
811 req->name,
812 req->dev);
813 }
814
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700815 if (req->rc) {
816 pr_info("#%u: batched sync load failed: %d\n",
817 req->idx, req->rc);
818 if (!test_fw_config->test_result)
819 test_fw_config->test_result = req->rc;
820 } else if (req->fw) {
821 req->sent = true;
822 pr_info("#%u: batched sync loaded %zu\n",
823 req->idx, req->fw->size);
824 }
825 complete(&req->completion);
826
827 req->task = NULL;
828
829 return 0;
830}
831
832/*
833 * We use a kthread as otherwise the kernel serializes all our sync requests
834 * and we would not be able to mimic batched requests on a sync call. Batched
835 * requests on a sync call can for instance happen on a device driver when
836 * multiple cards are used and firmware loading happens outside of probe.
837 */
838static ssize_t trigger_batched_requests_store(struct device *dev,
839 struct device_attribute *attr,
840 const char *buf, size_t count)
841{
842 struct test_batched_req *req;
843 int rc;
844 u8 i;
845
846 mutex_lock(&test_fw_mutex);
847
Kees Cookfad953c2018-06-12 14:27:37 -0700848 test_fw_config->reqs =
849 vzalloc(array3_size(sizeof(struct test_batched_req),
850 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700851 if (!test_fw_config->reqs) {
852 rc = -ENOMEM;
853 goto out_unlock;
854 }
855
856 pr_info("batched sync firmware loading '%s' %u times\n",
857 test_fw_config->name, test_fw_config->num_requests);
858
859 for (i = 0; i < test_fw_config->num_requests; i++) {
860 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700861 req->fw = NULL;
862 req->idx = i;
863 req->name = test_fw_config->name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200864 req->fw_buf = NULL;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700865 req->dev = dev;
866 init_completion(&req->completion);
867 req->task = kthread_run(test_fw_run_batch_request, req,
868 "%s-%u", KBUILD_MODNAME, req->idx);
869 if (!req->task || IS_ERR(req->task)) {
870 pr_err("Setting up thread %u failed\n", req->idx);
871 req->task = NULL;
872 rc = -ENOMEM;
873 goto out_bail;
874 }
875 }
876
877 rc = count;
878
879 /*
880 * We require an explicit release to enable more time and delay of
881 * calling release_firmware() to improve our chances of forcing a
882 * batched request. If we instead called release_firmware() right away
883 * then we might miss on an opportunity of having a successful firmware
884 * request pass on the opportunity to be come a batched request.
885 */
886
887out_bail:
888 for (i = 0; i < test_fw_config->num_requests; i++) {
889 req = &test_fw_config->reqs[i];
890 if (req->task || req->sent)
891 wait_for_completion(&req->completion);
892 }
893
894 /* Override any worker error if we had a general setup error */
895 if (rc < 0)
896 test_fw_config->test_result = rc;
897
898out_unlock:
899 mutex_unlock(&test_fw_mutex);
900
901 return rc;
902}
903static DEVICE_ATTR_WO(trigger_batched_requests);
904
905/*
906 * We wait for each callback to return with the lock held, no need to lock here
907 */
908static void trigger_batched_cb(const struct firmware *fw, void *context)
909{
910 struct test_batched_req *req = context;
911
912 if (!req) {
913 test_fw_config->test_result = -EINVAL;
914 return;
915 }
916
917 /* forces *some* batched requests to queue up */
918 if (!req->idx)
919 ssleep(2);
920
921 req->fw = fw;
922
923 /*
924 * Unfortunately the firmware API gives us nothing other than a null FW
925 * if the firmware was not found on async requests. Best we can do is
926 * just assume -ENOENT. A better API would pass the actual return
927 * value to the callback.
928 */
929 if (!fw && !test_fw_config->test_result)
930 test_fw_config->test_result = -ENOENT;
931
932 complete(&req->completion);
933}
934
935static
936ssize_t trigger_batched_requests_async_store(struct device *dev,
937 struct device_attribute *attr,
938 const char *buf, size_t count)
939{
940 struct test_batched_req *req;
941 bool send_uevent;
942 int rc;
943 u8 i;
944
945 mutex_lock(&test_fw_mutex);
946
Kees Cookfad953c2018-06-12 14:27:37 -0700947 test_fw_config->reqs =
948 vzalloc(array3_size(sizeof(struct test_batched_req),
949 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700950 if (!test_fw_config->reqs) {
951 rc = -ENOMEM;
952 goto out;
953 }
954
955 pr_info("batched loading '%s' custom fallback mechanism %u times\n",
956 test_fw_config->name, test_fw_config->num_requests);
957
958 send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG :
959 FW_ACTION_NOHOTPLUG;
960
961 for (i = 0; i < test_fw_config->num_requests; i++) {
962 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700963 req->name = test_fw_config->name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200964 req->fw_buf = NULL;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700965 req->fw = NULL;
966 req->idx = i;
967 init_completion(&req->completion);
968 rc = request_firmware_nowait(THIS_MODULE, send_uevent,
969 req->name,
970 dev, GFP_KERNEL, req,
971 trigger_batched_cb);
972 if (rc) {
973 pr_info("#%u: batched async load failed setup: %d\n",
974 i, rc);
975 req->rc = rc;
976 goto out_bail;
977 } else
978 req->sent = true;
979 }
980
981 rc = count;
982
983out_bail:
984
985 /*
986 * We require an explicit release to enable more time and delay of
987 * calling release_firmware() to improve our chances of forcing a
988 * batched request. If we instead called release_firmware() right away
989 * then we might miss on an opportunity of having a successful firmware
990 * request pass on the opportunity to be come a batched request.
991 */
992
993 for (i = 0; i < test_fw_config->num_requests; i++) {
994 req = &test_fw_config->reqs[i];
995 if (req->sent)
996 wait_for_completion(&req->completion);
997 }
998
999 /* Override any worker error if we had a general setup error */
1000 if (rc < 0)
1001 test_fw_config->test_result = rc;
1002
1003out:
1004 mutex_unlock(&test_fw_mutex);
1005
1006 return rc;
1007}
1008static DEVICE_ATTR_WO(trigger_batched_requests_async);
1009
1010static ssize_t test_result_show(struct device *dev,
1011 struct device_attribute *attr,
1012 char *buf)
1013{
1014 return test_dev_config_show_int(buf, test_fw_config->test_result);
1015}
1016static DEVICE_ATTR_RO(test_result);
1017
1018static ssize_t release_all_firmware_store(struct device *dev,
1019 struct device_attribute *attr,
1020 const char *buf, size_t count)
1021{
1022 test_release_all_firmware();
1023 return count;
1024}
1025static DEVICE_ATTR_WO(release_all_firmware);
1026
1027static ssize_t read_firmware_show(struct device *dev,
1028 struct device_attribute *attr,
1029 char *buf)
1030{
1031 struct test_batched_req *req;
1032 u8 idx;
1033 ssize_t rc = 0;
1034
1035 mutex_lock(&test_fw_mutex);
1036
1037 idx = test_fw_config->read_fw_idx;
1038 if (idx >= test_fw_config->num_requests) {
1039 rc = -ERANGE;
1040 goto out;
1041 }
1042
1043 if (!test_fw_config->reqs) {
1044 rc = -EINVAL;
1045 goto out;
1046 }
1047
1048 req = &test_fw_config->reqs[idx];
1049 if (!req->fw) {
1050 pr_err("#%u: failed to async load firmware\n", idx);
1051 rc = -ENOENT;
1052 goto out;
1053 }
1054
1055 pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1056
1057 if (req->fw->size > PAGE_SIZE) {
1058 pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1059 rc = -EINVAL;
Colin Ian King8bb0a882018-10-19 13:58:01 +01001060 goto out;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001061 }
1062 memcpy(buf, req->fw->data, req->fw->size);
1063
1064 rc = req->fw->size;
1065out:
1066 mutex_unlock(&test_fw_mutex);
1067
1068 return rc;
1069}
1070static DEVICE_ATTR_RO(read_firmware);
1071
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001072#define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr
1073
1074static struct attribute *test_dev_attrs[] = {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001075 TEST_FW_DEV_ATTR(reset),
1076
1077 TEST_FW_DEV_ATTR(config),
1078 TEST_FW_DEV_ATTR(config_name),
1079 TEST_FW_DEV_ATTR(config_num_requests),
Scott Branden7feebfa2019-08-22 11:40:04 -07001080 TEST_FW_DEV_ATTR(config_into_buf),
Scott Branden5d90e052020-10-02 10:38:28 -07001081 TEST_FW_DEV_ATTR(config_buf_size),
1082 TEST_FW_DEV_ATTR(config_file_offset),
1083 TEST_FW_DEV_ATTR(config_partial),
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001084 TEST_FW_DEV_ATTR(config_sync_direct),
1085 TEST_FW_DEV_ATTR(config_send_uevent),
1086 TEST_FW_DEV_ATTR(config_read_fw_idx),
1087
1088 /* These don't use the config at all - they could be ported! */
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001089 TEST_FW_DEV_ATTR(trigger_request),
1090 TEST_FW_DEV_ATTR(trigger_async_request),
Luis R. Rodriguez061132d2017-01-23 08:11:10 -08001091 TEST_FW_DEV_ATTR(trigger_custom_fallback),
Hans de Goede548193c2020-01-15 17:35:49 +01001092#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1093 TEST_FW_DEV_ATTR(trigger_request_platform),
1094#endif
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001095
1096 /* These use the config and can use the test_result */
1097 TEST_FW_DEV_ATTR(trigger_batched_requests),
1098 TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1099
1100 TEST_FW_DEV_ATTR(release_all_firmware),
1101 TEST_FW_DEV_ATTR(test_result),
1102 TEST_FW_DEV_ATTR(read_firmware),
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001103 NULL,
1104};
1105
1106ATTRIBUTE_GROUPS(test_dev);
1107
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001108static struct miscdevice test_fw_misc_device = {
1109 .minor = MISC_DYNAMIC_MINOR,
1110 .name = "test_firmware",
1111 .fops = &test_fw_fops,
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001112 .groups = test_dev_groups,
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001113};
1114
Kees Cook0a8adf52014-07-14 14:38:12 -07001115static int __init test_firmware_init(void)
1116{
1117 int rc;
1118
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001119 test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1120 if (!test_fw_config)
1121 return -ENOMEM;
1122
1123 rc = __test_firmware_config_init();
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001124 if (rc) {
1125 kfree(test_fw_config);
1126 pr_err("could not init firmware test config: %d\n", rc);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001127 return rc;
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001128 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001129
Kees Cook0a8adf52014-07-14 14:38:12 -07001130 rc = misc_register(&test_fw_misc_device);
1131 if (rc) {
Zhengchao Shao0b5a89e2022-11-19 11:57:21 +08001132 __test_firmware_config_free();
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001133 kfree(test_fw_config);
Kees Cook0a8adf52014-07-14 14:38:12 -07001134 pr_err("could not register misc device: %d\n", rc);
1135 return rc;
1136 }
Brian Norriseb910942015-12-09 14:50:27 -08001137
Kees Cook0a8adf52014-07-14 14:38:12 -07001138 pr_warn("interface ready\n");
1139
1140 return 0;
Kees Cook0a8adf52014-07-14 14:38:12 -07001141}
1142
1143module_init(test_firmware_init);
1144
1145static void __exit test_firmware_exit(void)
1146{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001147 mutex_lock(&test_fw_mutex);
Kees Cook0a8adf52014-07-14 14:38:12 -07001148 release_firmware(test_firmware);
Kees Cook0a8adf52014-07-14 14:38:12 -07001149 misc_deregister(&test_fw_misc_device);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001150 __test_firmware_config_free();
1151 kfree(test_fw_config);
1152 mutex_unlock(&test_fw_mutex);
1153
Kees Cook0a8adf52014-07-14 14:38:12 -07001154 pr_warn("removed interface\n");
1155}
1156
1157module_exit(test_firmware_exit);
1158
1159MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
1160MODULE_LICENSE("GPL");