blob: 3edbc17d92db5e2928f78d8e188ad56dc0835fc4 [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;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +020044 const char *fw_buf;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070045 struct completion completion;
46 struct task_struct *task;
47 struct device *dev;
48};
49
50/**
51 * test_config - represents configuration for the test for different triggers
52 *
53 * @name: the name of the firmware file to look for
Scott Branden7feebfa2019-08-22 11:40:04 -070054 * @into_buf: when the into_buf is used if this is true
55 * request_firmware_into_buf() will be used instead.
Scott Branden5d90e052020-10-02 10:38:28 -070056 * @buf_size: size of buf to allocate when into_buf is true
57 * @file_offset: file offset to request when calling request_firmware_into_buf
58 * @partial: partial read opt when calling request_firmware_into_buf
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070059 * @sync_direct: when the sync trigger is used if this is true
60 * request_firmware_direct() will be used instead.
61 * @send_uevent: whether or not to send a uevent for async requests
62 * @num_requests: number of requests to try per test case. This is trigger
63 * specific.
64 * @reqs: stores all requests information
65 * @read_fw_idx: index of thread from which we want to read firmware results
66 * from through the read_fw trigger.
67 * @test_result: a test may use this to collect the result from the call
68 * of the request_firmware*() calls used in their tests. In order of
69 * priority we always keep first any setup error. If no setup errors were
70 * found then we move on to the first error encountered while running the
71 * API. Note that for async calls this typically will be a successful
72 * result (0) unless of course you've used bogus parameters, or the system
73 * is out of memory. In the async case the callback is expected to do a
74 * bit more homework to figure out what happened, unfortunately the only
75 * information passed today on error is the fact that no firmware was
76 * found so we can only assume -ENOENT on async calls if the firmware is
77 * NULL.
78 *
79 * Errors you can expect:
80 *
81 * API specific:
82 *
83 * 0: success for sync, for async it means request was sent
84 * -EINVAL: invalid parameters or request
85 * -ENOENT: files not found
86 *
87 * System environment:
88 *
89 * -ENOMEM: memory pressure on system
90 * -ENODEV: out of number of devices to test
91 * -EINVAL: an unexpected error has occurred
92 * @req_firmware: if @sync_direct is true this is set to
93 * request_firmware_direct(), otherwise request_firmware()
94 */
95struct test_config {
96 char *name;
Scott Branden7feebfa2019-08-22 11:40:04 -070097 bool into_buf;
Scott Branden5d90e052020-10-02 10:38:28 -070098 size_t buf_size;
99 size_t file_offset;
100 bool partial;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700101 bool sync_direct;
102 bool send_uevent;
103 u8 num_requests;
104 u8 read_fw_idx;
105
106 /*
107 * These below don't belong her but we'll move them once we create
108 * a struct fw_test_device and stuff the misc_dev under there later.
109 */
110 struct test_batched_req *reqs;
111 int test_result;
112 int (*req_firmware)(const struct firmware **fw, const char *name,
113 struct device *device);
114};
115
Wei Yongjun76f8ab12018-01-11 11:13:45 +0000116static struct test_config *test_fw_config;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700117
Kees Cook0a8adf52014-07-14 14:38:12 -0700118static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
119 size_t size, loff_t *offset)
120{
121 ssize_t rc = 0;
122
123 mutex_lock(&test_fw_mutex);
124 if (test_firmware)
125 rc = simple_read_from_buffer(buf, size, offset,
126 test_firmware->data,
127 test_firmware->size);
128 mutex_unlock(&test_fw_mutex);
129 return rc;
130}
131
132static const struct file_operations test_fw_fops = {
133 .owner = THIS_MODULE,
134 .read = test_fw_misc_read,
135};
136
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700137static void __test_release_all_firmware(void)
138{
139 struct test_batched_req *req;
140 u8 i;
141
142 if (!test_fw_config->reqs)
143 return;
144
145 for (i = 0; i < test_fw_config->num_requests; i++) {
146 req = &test_fw_config->reqs[i];
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200147 if (req->fw) {
148 if (req->fw_buf) {
149 kfree_const(req->fw_buf);
150 req->fw_buf = NULL;
151 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700152 release_firmware(req->fw);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200153 req->fw = NULL;
154 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700155 }
156
157 vfree(test_fw_config->reqs);
158 test_fw_config->reqs = NULL;
159}
160
161static void test_release_all_firmware(void)
162{
163 mutex_lock(&test_fw_mutex);
164 __test_release_all_firmware();
165 mutex_unlock(&test_fw_mutex);
166}
167
168
169static void __test_firmware_config_free(void)
170{
171 __test_release_all_firmware();
172 kfree_const(test_fw_config->name);
173 test_fw_config->name = NULL;
174}
175
176/*
177 * XXX: move to kstrncpy() once merged.
178 *
179 * Users should use kfree_const() when freeing these.
180 */
181static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
182{
183 *dst = kstrndup(name, count, gfp);
184 if (!*dst)
185 return -ENOSPC;
186 return count;
187}
188
189static int __test_firmware_config_init(void)
190{
191 int ret;
192
193 ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
194 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
195 if (ret < 0)
196 goto out;
197
198 test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
199 test_fw_config->send_uevent = true;
Scott Branden7feebfa2019-08-22 11:40:04 -0700200 test_fw_config->into_buf = false;
Scott Branden5d90e052020-10-02 10:38:28 -0700201 test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
202 test_fw_config->file_offset = 0;
203 test_fw_config->partial = false;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700204 test_fw_config->sync_direct = false;
205 test_fw_config->req_firmware = request_firmware;
206 test_fw_config->test_result = 0;
207 test_fw_config->reqs = NULL;
208
209 return 0;
210
211out:
212 __test_firmware_config_free();
213 return ret;
214}
215
216static ssize_t reset_store(struct device *dev,
217 struct device_attribute *attr,
218 const char *buf, size_t count)
219{
220 int ret;
221
222 mutex_lock(&test_fw_mutex);
223
224 __test_firmware_config_free();
225
226 ret = __test_firmware_config_init();
227 if (ret < 0) {
228 ret = -ENOMEM;
229 pr_err("could not alloc settings for config trigger: %d\n",
230 ret);
231 goto out;
232 }
233
234 pr_info("reset\n");
235 ret = count;
236
237out:
238 mutex_unlock(&test_fw_mutex);
239
240 return ret;
241}
242static DEVICE_ATTR_WO(reset);
243
244static ssize_t config_show(struct device *dev,
245 struct device_attribute *attr,
246 char *buf)
247{
248 int len = 0;
249
250 mutex_lock(&test_fw_mutex);
251
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300252 len += scnprintf(buf, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700253 "Custom trigger configuration for: %s\n",
254 dev_name(dev));
255
256 if (test_fw_config->name)
Scott Branden5d90e052020-10-02 10:38:28 -0700257 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700258 "name:\t%s\n",
259 test_fw_config->name);
260 else
Scott Branden5d90e052020-10-02 10:38:28 -0700261 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700262 "name:\tEMTPY\n");
263
Scott Branden5d90e052020-10-02 10:38:28 -0700264 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700265 "num_requests:\t%u\n", test_fw_config->num_requests);
266
Scott Branden5d90e052020-10-02 10:38:28 -0700267 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700268 "send_uevent:\t\t%s\n",
269 test_fw_config->send_uevent ?
270 "FW_ACTION_HOTPLUG" :
271 "FW_ACTION_NOHOTPLUG");
Scott Branden5d90e052020-10-02 10:38:28 -0700272 len += scnprintf(buf + len, PAGE_SIZE - len,
Scott Branden7feebfa2019-08-22 11:40:04 -0700273 "into_buf:\t\t%s\n",
274 test_fw_config->into_buf ? "true" : "false");
Scott Branden5d90e052020-10-02 10:38:28 -0700275 len += scnprintf(buf + len, PAGE_SIZE - len,
276 "buf_size:\t%zu\n", test_fw_config->buf_size);
277 len += scnprintf(buf + len, PAGE_SIZE - len,
278 "file_offset:\t%zu\n", test_fw_config->file_offset);
279 len += scnprintf(buf + len, PAGE_SIZE - len,
280 "partial:\t\t%s\n",
281 test_fw_config->partial ? "true" : "false");
282 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700283 "sync_direct:\t\t%s\n",
284 test_fw_config->sync_direct ? "true" : "false");
Scott Branden5d90e052020-10-02 10:38:28 -0700285 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700286 "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
287
288 mutex_unlock(&test_fw_mutex);
289
290 return len;
291}
292static DEVICE_ATTR_RO(config);
293
294static ssize_t config_name_store(struct device *dev,
295 struct device_attribute *attr,
296 const char *buf, size_t count)
297{
298 int ret;
299
300 mutex_lock(&test_fw_mutex);
301 kfree_const(test_fw_config->name);
302 ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
303 mutex_unlock(&test_fw_mutex);
304
305 return ret;
306}
307
308/*
309 * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
310 */
311static ssize_t config_test_show_str(char *dst,
312 char *src)
313{
314 int len;
315
316 mutex_lock(&test_fw_mutex);
317 len = snprintf(dst, PAGE_SIZE, "%s\n", src);
318 mutex_unlock(&test_fw_mutex);
319
320 return len;
321}
322
323static int test_dev_config_update_bool(const char *buf, size_t size,
324 bool *cfg)
325{
326 int ret;
327
328 mutex_lock(&test_fw_mutex);
329 if (strtobool(buf, cfg) < 0)
330 ret = -EINVAL;
331 else
332 ret = size;
333 mutex_unlock(&test_fw_mutex);
334
335 return ret;
336}
337
Scott Branden55623262020-04-14 17:25:17 -0700338static ssize_t test_dev_config_show_bool(char *buf, bool val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700339{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700340 return snprintf(buf, PAGE_SIZE, "%d\n", val);
341}
342
Scott Branden5d90e052020-10-02 10:38:28 -0700343static int test_dev_config_update_size_t(const char *buf,
344 size_t size,
345 size_t *cfg)
346{
347 int ret;
348 long new;
349
350 ret = kstrtol(buf, 10, &new);
351 if (ret)
352 return ret;
353
354 mutex_lock(&test_fw_mutex);
355 *(size_t *)cfg = new;
356 mutex_unlock(&test_fw_mutex);
357
358 /* Always return full write size even if we didn't consume all */
359 return size;
360}
361
362static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
363{
364 return snprintf(buf, PAGE_SIZE, "%zu\n", val);
365}
366
Scott Branden55623262020-04-14 17:25:17 -0700367static ssize_t test_dev_config_show_int(char *buf, int val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700368{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700369 return snprintf(buf, PAGE_SIZE, "%d\n", val);
370}
371
372static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
373{
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800374 u8 val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700375 int ret;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700376
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800377 ret = kstrtou8(buf, 10, &val);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700378 if (ret)
379 return ret;
380
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700381 mutex_lock(&test_fw_mutex);
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800382 *(u8 *)cfg = val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700383 mutex_unlock(&test_fw_mutex);
384
385 /* Always return full write size even if we didn't consume all */
386 return size;
387}
388
Scott Branden55623262020-04-14 17:25:17 -0700389static ssize_t test_dev_config_show_u8(char *buf, u8 val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700390{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700391 return snprintf(buf, PAGE_SIZE, "%u\n", val);
392}
393
394static ssize_t config_name_show(struct device *dev,
395 struct device_attribute *attr,
396 char *buf)
397{
398 return config_test_show_str(buf, test_fw_config->name);
399}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800400static DEVICE_ATTR_RW(config_name);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700401
402static ssize_t config_num_requests_store(struct device *dev,
403 struct device_attribute *attr,
404 const char *buf, size_t count)
405{
406 int rc;
407
408 mutex_lock(&test_fw_mutex);
409 if (test_fw_config->reqs) {
410 pr_err("Must call release_all_firmware prior to changing config\n");
411 rc = -EINVAL;
Wei Yongjuna5e19232018-01-11 11:12:55 +0000412 mutex_unlock(&test_fw_mutex);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700413 goto out;
414 }
415 mutex_unlock(&test_fw_mutex);
416
417 rc = test_dev_config_update_u8(buf, count,
418 &test_fw_config->num_requests);
419
420out:
421 return rc;
422}
423
424static ssize_t config_num_requests_show(struct device *dev,
425 struct device_attribute *attr,
426 char *buf)
427{
428 return test_dev_config_show_u8(buf, test_fw_config->num_requests);
429}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800430static DEVICE_ATTR_RW(config_num_requests);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700431
Scott Branden7feebfa2019-08-22 11:40:04 -0700432static ssize_t config_into_buf_store(struct device *dev,
433 struct device_attribute *attr,
434 const char *buf, size_t count)
435{
436 return test_dev_config_update_bool(buf,
437 count,
438 &test_fw_config->into_buf);
439}
440
441static ssize_t config_into_buf_show(struct device *dev,
442 struct device_attribute *attr,
443 char *buf)
444{
445 return test_dev_config_show_bool(buf, test_fw_config->into_buf);
446}
447static DEVICE_ATTR_RW(config_into_buf);
448
Scott Branden5d90e052020-10-02 10:38:28 -0700449static ssize_t config_buf_size_store(struct device *dev,
450 struct device_attribute *attr,
451 const char *buf, size_t count)
452{
453 int rc;
454
455 mutex_lock(&test_fw_mutex);
456 if (test_fw_config->reqs) {
457 pr_err("Must call release_all_firmware prior to changing config\n");
458 rc = -EINVAL;
459 mutex_unlock(&test_fw_mutex);
460 goto out;
461 }
462 mutex_unlock(&test_fw_mutex);
463
464 rc = test_dev_config_update_size_t(buf, count,
465 &test_fw_config->buf_size);
466
467out:
468 return rc;
469}
470
471static ssize_t config_buf_size_show(struct device *dev,
472 struct device_attribute *attr,
473 char *buf)
474{
475 return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
476}
477static DEVICE_ATTR_RW(config_buf_size);
478
479static ssize_t config_file_offset_store(struct device *dev,
480 struct device_attribute *attr,
481 const char *buf, size_t count)
482{
483 int rc;
484
485 mutex_lock(&test_fw_mutex);
486 if (test_fw_config->reqs) {
487 pr_err("Must call release_all_firmware prior to changing config\n");
488 rc = -EINVAL;
489 mutex_unlock(&test_fw_mutex);
490 goto out;
491 }
492 mutex_unlock(&test_fw_mutex);
493
494 rc = test_dev_config_update_size_t(buf, count,
495 &test_fw_config->file_offset);
496
497out:
498 return rc;
499}
500
501static ssize_t config_file_offset_show(struct device *dev,
502 struct device_attribute *attr,
503 char *buf)
504{
505 return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
506}
507static DEVICE_ATTR_RW(config_file_offset);
508
509static ssize_t config_partial_store(struct device *dev,
510 struct device_attribute *attr,
511 const char *buf, size_t count)
512{
513 return test_dev_config_update_bool(buf,
514 count,
515 &test_fw_config->partial);
516}
517
518static ssize_t config_partial_show(struct device *dev,
519 struct device_attribute *attr,
520 char *buf)
521{
522 return test_dev_config_show_bool(buf, test_fw_config->partial);
523}
524static DEVICE_ATTR_RW(config_partial);
525
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700526static ssize_t config_sync_direct_store(struct device *dev,
527 struct device_attribute *attr,
528 const char *buf, size_t count)
529{
530 int rc = test_dev_config_update_bool(buf, count,
531 &test_fw_config->sync_direct);
532
533 if (rc == count)
534 test_fw_config->req_firmware = test_fw_config->sync_direct ?
535 request_firmware_direct :
536 request_firmware;
537 return rc;
538}
539
540static ssize_t config_sync_direct_show(struct device *dev,
541 struct device_attribute *attr,
542 char *buf)
543{
544 return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
545}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800546static DEVICE_ATTR_RW(config_sync_direct);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700547
548static ssize_t config_send_uevent_store(struct device *dev,
549 struct device_attribute *attr,
550 const char *buf, size_t count)
551{
552 return test_dev_config_update_bool(buf, count,
553 &test_fw_config->send_uevent);
554}
555
556static ssize_t config_send_uevent_show(struct device *dev,
557 struct device_attribute *attr,
558 char *buf)
559{
560 return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
561}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800562static DEVICE_ATTR_RW(config_send_uevent);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700563
564static ssize_t config_read_fw_idx_store(struct device *dev,
565 struct device_attribute *attr,
566 const char *buf, size_t count)
567{
568 return test_dev_config_update_u8(buf, count,
569 &test_fw_config->read_fw_idx);
570}
571
572static ssize_t config_read_fw_idx_show(struct device *dev,
573 struct device_attribute *attr,
574 char *buf)
575{
576 return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
577}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800578static DEVICE_ATTR_RW(config_read_fw_idx);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700579
580
Kees Cook0a8adf52014-07-14 14:38:12 -0700581static ssize_t trigger_request_store(struct device *dev,
582 struct device_attribute *attr,
583 const char *buf, size_t count)
584{
585 int rc;
586 char *name;
587
Brian Norrisbe4a1322015-12-09 14:50:26 -0800588 name = kstrndup(buf, count, GFP_KERNEL);
Kees Cook0a8adf52014-07-14 14:38:12 -0700589 if (!name)
590 return -ENOSPC;
Kees Cook0a8adf52014-07-14 14:38:12 -0700591
592 pr_info("loading '%s'\n", name);
593
594 mutex_lock(&test_fw_mutex);
595 release_firmware(test_firmware);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200596 if (test_fw_config->reqs)
597 __test_release_all_firmware();
Kees Cook0a8adf52014-07-14 14:38:12 -0700598 test_firmware = NULL;
599 rc = request_firmware(&test_firmware, name, dev);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800600 if (rc) {
Kees Cook0a8adf52014-07-14 14:38:12 -0700601 pr_info("load of '%s' failed: %d\n", name, rc);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800602 goto out;
603 }
604 pr_info("loaded: %zu\n", test_firmware->size);
605 rc = count;
606
607out:
Kees Cook0a8adf52014-07-14 14:38:12 -0700608 mutex_unlock(&test_fw_mutex);
609
610 kfree(name);
611
Brian Norris47e0bbb2015-12-09 14:50:25 -0800612 return rc;
Kees Cook0a8adf52014-07-14 14:38:12 -0700613}
614static DEVICE_ATTR_WO(trigger_request);
615
Hans de Goede548193c2020-01-15 17:35:49 +0100616#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
Kees Cookbaaabec2020-09-09 15:53:54 -0700617extern struct list_head efi_embedded_fw_list;
618extern bool efi_embedded_fw_checked;
619
Hans de Goede548193c2020-01-15 17:35:49 +0100620static ssize_t trigger_request_platform_store(struct device *dev,
621 struct device_attribute *attr,
622 const char *buf, size_t count)
623{
624 static const u8 test_data[] = {
625 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
626 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
627 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
628 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
629 };
630 struct efi_embedded_fw efi_embedded_fw;
631 const struct firmware *firmware = NULL;
Kees Cookbaaabec2020-09-09 15:53:54 -0700632 bool saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100633 char *name;
634 int rc;
635
636 name = kstrndup(buf, count, GFP_KERNEL);
637 if (!name)
638 return -ENOSPC;
639
640 pr_info("inserting test platform fw '%s'\n", name);
641 efi_embedded_fw.name = name;
642 efi_embedded_fw.data = (void *)test_data;
643 efi_embedded_fw.length = sizeof(test_data);
644 list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
Kees Cookbaaabec2020-09-09 15:53:54 -0700645 saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
646 efi_embedded_fw_checked = true;
Hans de Goede548193c2020-01-15 17:35:49 +0100647
648 pr_info("loading '%s'\n", name);
649 rc = firmware_request_platform(&firmware, name, dev);
650 if (rc) {
651 pr_info("load of '%s' failed: %d\n", name, rc);
652 goto out;
653 }
654 if (firmware->size != sizeof(test_data) ||
655 memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
656 pr_info("firmware contents mismatch for '%s'\n", name);
657 rc = -EINVAL;
658 goto out;
659 }
660 pr_info("loaded: %zu\n", firmware->size);
661 rc = count;
662
663out:
Kees Cookbaaabec2020-09-09 15:53:54 -0700664 efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100665 release_firmware(firmware);
666 list_del(&efi_embedded_fw.list);
667 kfree(name);
668
669 return rc;
670}
671static DEVICE_ATTR_WO(trigger_request_platform);
672#endif
673
Brian Norriseb910942015-12-09 14:50:27 -0800674static DECLARE_COMPLETION(async_fw_done);
675
676static void trigger_async_request_cb(const struct firmware *fw, void *context)
677{
678 test_firmware = fw;
679 complete(&async_fw_done);
680}
681
682static ssize_t trigger_async_request_store(struct device *dev,
683 struct device_attribute *attr,
684 const char *buf, size_t count)
685{
686 int rc;
687 char *name;
688
689 name = kstrndup(buf, count, GFP_KERNEL);
690 if (!name)
691 return -ENOSPC;
692
693 pr_info("loading '%s'\n", name);
694
695 mutex_lock(&test_fw_mutex);
696 release_firmware(test_firmware);
697 test_firmware = NULL;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200698 if (test_fw_config->reqs)
699 __test_release_all_firmware();
Brian Norriseb910942015-12-09 14:50:27 -0800700 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
701 NULL, trigger_async_request_cb);
702 if (rc) {
703 pr_info("async load of '%s' failed: %d\n", name, rc);
704 kfree(name);
705 goto out;
706 }
707 /* Free 'name' ASAP, to test for race conditions */
708 kfree(name);
709
710 wait_for_completion(&async_fw_done);
711
712 if (test_firmware) {
713 pr_info("loaded: %zu\n", test_firmware->size);
714 rc = count;
715 } else {
716 pr_err("failed to async load firmware\n");
Scott Branden7feebfa2019-08-22 11:40:04 -0700717 rc = -ENOMEM;
Brian Norriseb910942015-12-09 14:50:27 -0800718 }
719
720out:
721 mutex_unlock(&test_fw_mutex);
722
723 return rc;
724}
725static DEVICE_ATTR_WO(trigger_async_request);
726
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800727static ssize_t trigger_custom_fallback_store(struct device *dev,
728 struct device_attribute *attr,
729 const char *buf, size_t count)
730{
731 int rc;
732 char *name;
733
734 name = kstrndup(buf, count, GFP_KERNEL);
735 if (!name)
736 return -ENOSPC;
737
738 pr_info("loading '%s' using custom fallback mechanism\n", name);
739
740 mutex_lock(&test_fw_mutex);
741 release_firmware(test_firmware);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200742 if (test_fw_config->reqs)
743 __test_release_all_firmware();
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800744 test_firmware = NULL;
745 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
746 dev, GFP_KERNEL, NULL,
747 trigger_async_request_cb);
748 if (rc) {
749 pr_info("async load of '%s' failed: %d\n", name, rc);
750 kfree(name);
751 goto out;
752 }
753 /* Free 'name' ASAP, to test for race conditions */
754 kfree(name);
755
756 wait_for_completion(&async_fw_done);
757
758 if (test_firmware) {
759 pr_info("loaded: %zu\n", test_firmware->size);
760 rc = count;
761 } else {
762 pr_err("failed to async load firmware\n");
763 rc = -ENODEV;
764 }
765
766out:
767 mutex_unlock(&test_fw_mutex);
768
769 return rc;
770}
771static DEVICE_ATTR_WO(trigger_custom_fallback);
772
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700773static int test_fw_run_batch_request(void *data)
774{
775 struct test_batched_req *req = data;
776
777 if (!req) {
778 test_fw_config->test_result = -EINVAL;
779 return -EINVAL;
780 }
781
Scott Branden7feebfa2019-08-22 11:40:04 -0700782 if (test_fw_config->into_buf) {
783 void *test_buf;
784
785 test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
786 if (!test_buf)
787 return -ENOSPC;
788
Scott Branden5d90e052020-10-02 10:38:28 -0700789 if (test_fw_config->partial)
790 req->rc = request_partial_firmware_into_buf
791 (&req->fw,
792 req->name,
793 req->dev,
794 test_buf,
795 test_fw_config->buf_size,
796 test_fw_config->file_offset);
797 else
798 req->rc = request_firmware_into_buf
799 (&req->fw,
800 req->name,
801 req->dev,
802 test_buf,
803 test_fw_config->buf_size);
Scott Branden7feebfa2019-08-22 11:40:04 -0700804 if (!req->fw)
805 kfree(test_buf);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200806 else
807 req->fw_buf = test_buf;
Scott Branden7feebfa2019-08-22 11:40:04 -0700808 } else {
809 req->rc = test_fw_config->req_firmware(&req->fw,
810 req->name,
811 req->dev);
812 }
813
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700814 if (req->rc) {
815 pr_info("#%u: batched sync load failed: %d\n",
816 req->idx, req->rc);
817 if (!test_fw_config->test_result)
818 test_fw_config->test_result = req->rc;
819 } else if (req->fw) {
820 req->sent = true;
821 pr_info("#%u: batched sync loaded %zu\n",
822 req->idx, req->fw->size);
823 }
824 complete(&req->completion);
825
826 req->task = NULL;
827
828 return 0;
829}
830
831/*
832 * We use a kthread as otherwise the kernel serializes all our sync requests
833 * and we would not be able to mimic batched requests on a sync call. Batched
834 * requests on a sync call can for instance happen on a device driver when
835 * multiple cards are used and firmware loading happens outside of probe.
836 */
837static ssize_t trigger_batched_requests_store(struct device *dev,
838 struct device_attribute *attr,
839 const char *buf, size_t count)
840{
841 struct test_batched_req *req;
842 int rc;
843 u8 i;
844
845 mutex_lock(&test_fw_mutex);
846
Kees Cookfad953c2018-06-12 14:27:37 -0700847 test_fw_config->reqs =
848 vzalloc(array3_size(sizeof(struct test_batched_req),
849 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700850 if (!test_fw_config->reqs) {
851 rc = -ENOMEM;
852 goto out_unlock;
853 }
854
855 pr_info("batched sync firmware loading '%s' %u times\n",
856 test_fw_config->name, test_fw_config->num_requests);
857
858 for (i = 0; i < test_fw_config->num_requests; i++) {
859 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700860 req->fw = NULL;
861 req->idx = i;
862 req->name = test_fw_config->name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200863 req->fw_buf = NULL;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700864 req->dev = dev;
865 init_completion(&req->completion);
866 req->task = kthread_run(test_fw_run_batch_request, req,
867 "%s-%u", KBUILD_MODNAME, req->idx);
868 if (!req->task || IS_ERR(req->task)) {
869 pr_err("Setting up thread %u failed\n", req->idx);
870 req->task = NULL;
871 rc = -ENOMEM;
872 goto out_bail;
873 }
874 }
875
876 rc = count;
877
878 /*
879 * We require an explicit release to enable more time and delay of
880 * calling release_firmware() to improve our chances of forcing a
881 * batched request. If we instead called release_firmware() right away
882 * then we might miss on an opportunity of having a successful firmware
883 * request pass on the opportunity to be come a batched request.
884 */
885
886out_bail:
887 for (i = 0; i < test_fw_config->num_requests; i++) {
888 req = &test_fw_config->reqs[i];
889 if (req->task || req->sent)
890 wait_for_completion(&req->completion);
891 }
892
893 /* Override any worker error if we had a general setup error */
894 if (rc < 0)
895 test_fw_config->test_result = rc;
896
897out_unlock:
898 mutex_unlock(&test_fw_mutex);
899
900 return rc;
901}
902static DEVICE_ATTR_WO(trigger_batched_requests);
903
904/*
905 * We wait for each callback to return with the lock held, no need to lock here
906 */
907static void trigger_batched_cb(const struct firmware *fw, void *context)
908{
909 struct test_batched_req *req = context;
910
911 if (!req) {
912 test_fw_config->test_result = -EINVAL;
913 return;
914 }
915
916 /* forces *some* batched requests to queue up */
917 if (!req->idx)
918 ssleep(2);
919
920 req->fw = fw;
921
922 /*
923 * Unfortunately the firmware API gives us nothing other than a null FW
924 * if the firmware was not found on async requests. Best we can do is
925 * just assume -ENOENT. A better API would pass the actual return
926 * value to the callback.
927 */
928 if (!fw && !test_fw_config->test_result)
929 test_fw_config->test_result = -ENOENT;
930
931 complete(&req->completion);
932}
933
934static
935ssize_t trigger_batched_requests_async_store(struct device *dev,
936 struct device_attribute *attr,
937 const char *buf, size_t count)
938{
939 struct test_batched_req *req;
940 bool send_uevent;
941 int rc;
942 u8 i;
943
944 mutex_lock(&test_fw_mutex);
945
Kees Cookfad953c2018-06-12 14:27:37 -0700946 test_fw_config->reqs =
947 vzalloc(array3_size(sizeof(struct test_batched_req),
948 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700949 if (!test_fw_config->reqs) {
950 rc = -ENOMEM;
951 goto out;
952 }
953
954 pr_info("batched loading '%s' custom fallback mechanism %u times\n",
955 test_fw_config->name, test_fw_config->num_requests);
956
957 send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG :
958 FW_ACTION_NOHOTPLUG;
959
960 for (i = 0; i < test_fw_config->num_requests; i++) {
961 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700962 req->name = test_fw_config->name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200963 req->fw_buf = NULL;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700964 req->fw = NULL;
965 req->idx = i;
966 init_completion(&req->completion);
967 rc = request_firmware_nowait(THIS_MODULE, send_uevent,
968 req->name,
969 dev, GFP_KERNEL, req,
970 trigger_batched_cb);
971 if (rc) {
972 pr_info("#%u: batched async load failed setup: %d\n",
973 i, rc);
974 req->rc = rc;
975 goto out_bail;
976 } else
977 req->sent = true;
978 }
979
980 rc = count;
981
982out_bail:
983
984 /*
985 * We require an explicit release to enable more time and delay of
986 * calling release_firmware() to improve our chances of forcing a
987 * batched request. If we instead called release_firmware() right away
988 * then we might miss on an opportunity of having a successful firmware
989 * request pass on the opportunity to be come a batched request.
990 */
991
992 for (i = 0; i < test_fw_config->num_requests; i++) {
993 req = &test_fw_config->reqs[i];
994 if (req->sent)
995 wait_for_completion(&req->completion);
996 }
997
998 /* Override any worker error if we had a general setup error */
999 if (rc < 0)
1000 test_fw_config->test_result = rc;
1001
1002out:
1003 mutex_unlock(&test_fw_mutex);
1004
1005 return rc;
1006}
1007static DEVICE_ATTR_WO(trigger_batched_requests_async);
1008
1009static ssize_t test_result_show(struct device *dev,
1010 struct device_attribute *attr,
1011 char *buf)
1012{
1013 return test_dev_config_show_int(buf, test_fw_config->test_result);
1014}
1015static DEVICE_ATTR_RO(test_result);
1016
1017static ssize_t release_all_firmware_store(struct device *dev,
1018 struct device_attribute *attr,
1019 const char *buf, size_t count)
1020{
1021 test_release_all_firmware();
1022 return count;
1023}
1024static DEVICE_ATTR_WO(release_all_firmware);
1025
1026static ssize_t read_firmware_show(struct device *dev,
1027 struct device_attribute *attr,
1028 char *buf)
1029{
1030 struct test_batched_req *req;
1031 u8 idx;
1032 ssize_t rc = 0;
1033
1034 mutex_lock(&test_fw_mutex);
1035
1036 idx = test_fw_config->read_fw_idx;
1037 if (idx >= test_fw_config->num_requests) {
1038 rc = -ERANGE;
1039 goto out;
1040 }
1041
1042 if (!test_fw_config->reqs) {
1043 rc = -EINVAL;
1044 goto out;
1045 }
1046
1047 req = &test_fw_config->reqs[idx];
1048 if (!req->fw) {
1049 pr_err("#%u: failed to async load firmware\n", idx);
1050 rc = -ENOENT;
1051 goto out;
1052 }
1053
1054 pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1055
1056 if (req->fw->size > PAGE_SIZE) {
1057 pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1058 rc = -EINVAL;
Colin Ian King8bb0a882018-10-19 13:58:01 +01001059 goto out;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001060 }
1061 memcpy(buf, req->fw->data, req->fw->size);
1062
1063 rc = req->fw->size;
1064out:
1065 mutex_unlock(&test_fw_mutex);
1066
1067 return rc;
1068}
1069static DEVICE_ATTR_RO(read_firmware);
1070
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001071#define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr
1072
1073static struct attribute *test_dev_attrs[] = {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001074 TEST_FW_DEV_ATTR(reset),
1075
1076 TEST_FW_DEV_ATTR(config),
1077 TEST_FW_DEV_ATTR(config_name),
1078 TEST_FW_DEV_ATTR(config_num_requests),
Scott Branden7feebfa2019-08-22 11:40:04 -07001079 TEST_FW_DEV_ATTR(config_into_buf),
Scott Branden5d90e052020-10-02 10:38:28 -07001080 TEST_FW_DEV_ATTR(config_buf_size),
1081 TEST_FW_DEV_ATTR(config_file_offset),
1082 TEST_FW_DEV_ATTR(config_partial),
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001083 TEST_FW_DEV_ATTR(config_sync_direct),
1084 TEST_FW_DEV_ATTR(config_send_uevent),
1085 TEST_FW_DEV_ATTR(config_read_fw_idx),
1086
1087 /* These don't use the config at all - they could be ported! */
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001088 TEST_FW_DEV_ATTR(trigger_request),
1089 TEST_FW_DEV_ATTR(trigger_async_request),
Luis R. Rodriguez061132d2017-01-23 08:11:10 -08001090 TEST_FW_DEV_ATTR(trigger_custom_fallback),
Hans de Goede548193c2020-01-15 17:35:49 +01001091#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1092 TEST_FW_DEV_ATTR(trigger_request_platform),
1093#endif
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001094
1095 /* These use the config and can use the test_result */
1096 TEST_FW_DEV_ATTR(trigger_batched_requests),
1097 TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1098
1099 TEST_FW_DEV_ATTR(release_all_firmware),
1100 TEST_FW_DEV_ATTR(test_result),
1101 TEST_FW_DEV_ATTR(read_firmware),
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001102 NULL,
1103};
1104
1105ATTRIBUTE_GROUPS(test_dev);
1106
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001107static struct miscdevice test_fw_misc_device = {
1108 .minor = MISC_DYNAMIC_MINOR,
1109 .name = "test_firmware",
1110 .fops = &test_fw_fops,
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001111 .groups = test_dev_groups,
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001112};
1113
Kees Cook0a8adf52014-07-14 14:38:12 -07001114static int __init test_firmware_init(void)
1115{
1116 int rc;
1117
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001118 test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1119 if (!test_fw_config)
1120 return -ENOMEM;
1121
1122 rc = __test_firmware_config_init();
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001123 if (rc) {
1124 kfree(test_fw_config);
1125 pr_err("could not init firmware test config: %d\n", rc);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001126 return rc;
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001127 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001128
Kees Cook0a8adf52014-07-14 14:38:12 -07001129 rc = misc_register(&test_fw_misc_device);
1130 if (rc) {
Zhengchao Shao0b5a89e2022-11-19 11:57:21 +08001131 __test_firmware_config_free();
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001132 kfree(test_fw_config);
Kees Cook0a8adf52014-07-14 14:38:12 -07001133 pr_err("could not register misc device: %d\n", rc);
1134 return rc;
1135 }
Brian Norriseb910942015-12-09 14:50:27 -08001136
Kees Cook0a8adf52014-07-14 14:38:12 -07001137 pr_warn("interface ready\n");
1138
1139 return 0;
Kees Cook0a8adf52014-07-14 14:38:12 -07001140}
1141
1142module_init(test_firmware_init);
1143
1144static void __exit test_firmware_exit(void)
1145{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001146 mutex_lock(&test_fw_mutex);
Kees Cook0a8adf52014-07-14 14:38:12 -07001147 release_firmware(test_firmware);
Kees Cook0a8adf52014-07-14 14:38:12 -07001148 misc_deregister(&test_fw_misc_device);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001149 __test_firmware_config_free();
1150 kfree(test_fw_config);
1151 mutex_unlock(&test_fw_mutex);
1152
Kees Cook0a8adf52014-07-14 14:38:12 -07001153 pr_warn("removed interface\n");
1154}
1155
1156module_exit(test_firmware_exit);
1157
1158MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
1159MODULE_LICENSE("GPL");