blob: 1bccd6cd5f482f0c467c0b99a5f492ccff3efde7 [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.
Scott Branden5d90e052020-10-02 10:38:28 -070055 * @buf_size: size of buf to allocate when into_buf is true
56 * @file_offset: file offset to request when calling request_firmware_into_buf
57 * @partial: partial read opt when calling request_firmware_into_buf
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -070058 * @sync_direct: when the sync trigger is used if this is true
59 * request_firmware_direct() will be used instead.
60 * @send_uevent: whether or not to send a uevent for async requests
61 * @num_requests: number of requests to try per test case. This is trigger
62 * specific.
63 * @reqs: stores all requests information
64 * @read_fw_idx: index of thread from which we want to read firmware results
65 * from through the read_fw trigger.
66 * @test_result: a test may use this to collect the result from the call
67 * of the request_firmware*() calls used in their tests. In order of
68 * priority we always keep first any setup error. If no setup errors were
69 * found then we move on to the first error encountered while running the
70 * API. Note that for async calls this typically will be a successful
71 * result (0) unless of course you've used bogus parameters, or the system
72 * is out of memory. In the async case the callback is expected to do a
73 * bit more homework to figure out what happened, unfortunately the only
74 * information passed today on error is the fact that no firmware was
75 * found so we can only assume -ENOENT on async calls if the firmware is
76 * NULL.
77 *
78 * Errors you can expect:
79 *
80 * API specific:
81 *
82 * 0: success for sync, for async it means request was sent
83 * -EINVAL: invalid parameters or request
84 * -ENOENT: files not found
85 *
86 * System environment:
87 *
88 * -ENOMEM: memory pressure on system
89 * -ENODEV: out of number of devices to test
90 * -EINVAL: an unexpected error has occurred
91 * @req_firmware: if @sync_direct is true this is set to
92 * request_firmware_direct(), otherwise request_firmware()
93 */
94struct test_config {
95 char *name;
Scott Branden7feebfa2019-08-22 11:40:04 -070096 bool into_buf;
Scott Branden5d90e052020-10-02 10:38:28 -070097 size_t buf_size;
98 size_t file_offset;
99 bool partial;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700100 bool sync_direct;
101 bool send_uevent;
102 u8 num_requests;
103 u8 read_fw_idx;
104
105 /*
106 * These below don't belong her but we'll move them once we create
107 * a struct fw_test_device and stuff the misc_dev under there later.
108 */
109 struct test_batched_req *reqs;
110 int test_result;
111 int (*req_firmware)(const struct firmware **fw, const char *name,
112 struct device *device);
113};
114
Wei Yongjun76f8ab12018-01-11 11:13:45 +0000115static struct test_config *test_fw_config;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700116
Kees Cook0a8adf52014-07-14 14:38:12 -0700117static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
118 size_t size, loff_t *offset)
119{
120 ssize_t rc = 0;
121
122 mutex_lock(&test_fw_mutex);
123 if (test_firmware)
124 rc = simple_read_from_buffer(buf, size, offset,
125 test_firmware->data,
126 test_firmware->size);
127 mutex_unlock(&test_fw_mutex);
128 return rc;
129}
130
131static const struct file_operations test_fw_fops = {
132 .owner = THIS_MODULE,
133 .read = test_fw_misc_read,
134};
135
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700136static void __test_release_all_firmware(void)
137{
138 struct test_batched_req *req;
139 u8 i;
140
141 if (!test_fw_config->reqs)
142 return;
143
144 for (i = 0; i < test_fw_config->num_requests; i++) {
145 req = &test_fw_config->reqs[i];
146 if (req->fw)
147 release_firmware(req->fw);
148 }
149
150 vfree(test_fw_config->reqs);
151 test_fw_config->reqs = NULL;
152}
153
154static void test_release_all_firmware(void)
155{
156 mutex_lock(&test_fw_mutex);
157 __test_release_all_firmware();
158 mutex_unlock(&test_fw_mutex);
159}
160
161
162static void __test_firmware_config_free(void)
163{
164 __test_release_all_firmware();
165 kfree_const(test_fw_config->name);
166 test_fw_config->name = NULL;
167}
168
169/*
170 * XXX: move to kstrncpy() once merged.
171 *
172 * Users should use kfree_const() when freeing these.
173 */
174static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp)
175{
176 *dst = kstrndup(name, count, gfp);
177 if (!*dst)
178 return -ENOSPC;
179 return count;
180}
181
182static int __test_firmware_config_init(void)
183{
184 int ret;
185
186 ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME,
187 strlen(TEST_FIRMWARE_NAME), GFP_KERNEL);
188 if (ret < 0)
189 goto out;
190
191 test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
192 test_fw_config->send_uevent = true;
Scott Branden7feebfa2019-08-22 11:40:04 -0700193 test_fw_config->into_buf = false;
Scott Branden5d90e052020-10-02 10:38:28 -0700194 test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
195 test_fw_config->file_offset = 0;
196 test_fw_config->partial = false;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700197 test_fw_config->sync_direct = false;
198 test_fw_config->req_firmware = request_firmware;
199 test_fw_config->test_result = 0;
200 test_fw_config->reqs = NULL;
201
202 return 0;
203
204out:
205 __test_firmware_config_free();
206 return ret;
207}
208
209static ssize_t reset_store(struct device *dev,
210 struct device_attribute *attr,
211 const char *buf, size_t count)
212{
213 int ret;
214
215 mutex_lock(&test_fw_mutex);
216
217 __test_firmware_config_free();
218
219 ret = __test_firmware_config_init();
220 if (ret < 0) {
221 ret = -ENOMEM;
222 pr_err("could not alloc settings for config trigger: %d\n",
223 ret);
224 goto out;
225 }
226
227 pr_info("reset\n");
228 ret = count;
229
230out:
231 mutex_unlock(&test_fw_mutex);
232
233 return ret;
234}
235static DEVICE_ATTR_WO(reset);
236
237static ssize_t config_show(struct device *dev,
238 struct device_attribute *attr,
239 char *buf)
240{
241 int len = 0;
242
243 mutex_lock(&test_fw_mutex);
244
Dan Carpenterbd17cc52019-05-15 12:33:22 +0300245 len += scnprintf(buf, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700246 "Custom trigger configuration for: %s\n",
247 dev_name(dev));
248
249 if (test_fw_config->name)
Scott Branden5d90e052020-10-02 10:38:28 -0700250 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700251 "name:\t%s\n",
252 test_fw_config->name);
253 else
Scott Branden5d90e052020-10-02 10:38:28 -0700254 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700255 "name:\tEMTPY\n");
256
Scott Branden5d90e052020-10-02 10:38:28 -0700257 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700258 "num_requests:\t%u\n", test_fw_config->num_requests);
259
Scott Branden5d90e052020-10-02 10:38:28 -0700260 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700261 "send_uevent:\t\t%s\n",
262 test_fw_config->send_uevent ?
Shawn Guo0733d832021-04-25 10:00:24 +0800263 "FW_ACTION_UEVENT" :
264 "FW_ACTION_NOUEVENT");
Scott Branden5d90e052020-10-02 10:38:28 -0700265 len += scnprintf(buf + len, PAGE_SIZE - len,
Scott Branden7feebfa2019-08-22 11:40:04 -0700266 "into_buf:\t\t%s\n",
267 test_fw_config->into_buf ? "true" : "false");
Scott Branden5d90e052020-10-02 10:38:28 -0700268 len += scnprintf(buf + len, PAGE_SIZE - len,
269 "buf_size:\t%zu\n", test_fw_config->buf_size);
270 len += scnprintf(buf + len, PAGE_SIZE - len,
271 "file_offset:\t%zu\n", test_fw_config->file_offset);
272 len += scnprintf(buf + len, PAGE_SIZE - len,
273 "partial:\t\t%s\n",
274 test_fw_config->partial ? "true" : "false");
275 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700276 "sync_direct:\t\t%s\n",
277 test_fw_config->sync_direct ? "true" : "false");
Scott Branden5d90e052020-10-02 10:38:28 -0700278 len += scnprintf(buf + len, PAGE_SIZE - len,
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700279 "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
280
281 mutex_unlock(&test_fw_mutex);
282
283 return len;
284}
285static DEVICE_ATTR_RO(config);
286
287static ssize_t config_name_store(struct device *dev,
288 struct device_attribute *attr,
289 const char *buf, size_t count)
290{
291 int ret;
292
293 mutex_lock(&test_fw_mutex);
294 kfree_const(test_fw_config->name);
295 ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL);
296 mutex_unlock(&test_fw_mutex);
297
298 return ret;
299}
300
301/*
302 * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE.
303 */
304static ssize_t config_test_show_str(char *dst,
305 char *src)
306{
307 int len;
308
309 mutex_lock(&test_fw_mutex);
310 len = snprintf(dst, PAGE_SIZE, "%s\n", src);
311 mutex_unlock(&test_fw_mutex);
312
313 return len;
314}
315
316static int test_dev_config_update_bool(const char *buf, size_t size,
317 bool *cfg)
318{
319 int ret;
320
321 mutex_lock(&test_fw_mutex);
322 if (strtobool(buf, cfg) < 0)
323 ret = -EINVAL;
324 else
325 ret = size;
326 mutex_unlock(&test_fw_mutex);
327
328 return ret;
329}
330
Scott Branden55623262020-04-14 17:25:17 -0700331static ssize_t test_dev_config_show_bool(char *buf, bool val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700332{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700333 return snprintf(buf, PAGE_SIZE, "%d\n", val);
334}
335
Scott Branden5d90e052020-10-02 10:38:28 -0700336static int test_dev_config_update_size_t(const char *buf,
337 size_t size,
338 size_t *cfg)
339{
340 int ret;
341 long new;
342
343 ret = kstrtol(buf, 10, &new);
344 if (ret)
345 return ret;
346
347 mutex_lock(&test_fw_mutex);
348 *(size_t *)cfg = new;
349 mutex_unlock(&test_fw_mutex);
350
351 /* Always return full write size even if we didn't consume all */
352 return size;
353}
354
355static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
356{
357 return snprintf(buf, PAGE_SIZE, "%zu\n", val);
358}
359
Scott Branden55623262020-04-14 17:25:17 -0700360static ssize_t test_dev_config_show_int(char *buf, int val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700361{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700362 return snprintf(buf, PAGE_SIZE, "%d\n", val);
363}
364
365static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
366{
Alexey Dobriyan506dfc92020-12-15 20:44:00 -0800367 u8 val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700368 int ret;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700369
Alexey Dobriyan506dfc92020-12-15 20:44:00 -0800370 ret = kstrtou8(buf, 10, &val);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700371 if (ret)
372 return ret;
373
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700374 mutex_lock(&test_fw_mutex);
Alexey Dobriyan506dfc92020-12-15 20:44:00 -0800375 *(u8 *)cfg = val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700376 mutex_unlock(&test_fw_mutex);
377
378 /* Always return full write size even if we didn't consume all */
379 return size;
380}
381
Scott Branden55623262020-04-14 17:25:17 -0700382static ssize_t test_dev_config_show_u8(char *buf, u8 val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700383{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700384 return snprintf(buf, PAGE_SIZE, "%u\n", val);
385}
386
387static ssize_t config_name_show(struct device *dev,
388 struct device_attribute *attr,
389 char *buf)
390{
391 return config_test_show_str(buf, test_fw_config->name);
392}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800393static DEVICE_ATTR_RW(config_name);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700394
395static ssize_t config_num_requests_store(struct device *dev,
396 struct device_attribute *attr,
397 const char *buf, size_t count)
398{
399 int rc;
400
401 mutex_lock(&test_fw_mutex);
402 if (test_fw_config->reqs) {
403 pr_err("Must call release_all_firmware prior to changing config\n");
404 rc = -EINVAL;
Wei Yongjuna5e19232018-01-11 11:12:55 +0000405 mutex_unlock(&test_fw_mutex);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700406 goto out;
407 }
408 mutex_unlock(&test_fw_mutex);
409
410 rc = test_dev_config_update_u8(buf, count,
411 &test_fw_config->num_requests);
412
413out:
414 return rc;
415}
416
417static ssize_t config_num_requests_show(struct device *dev,
418 struct device_attribute *attr,
419 char *buf)
420{
421 return test_dev_config_show_u8(buf, test_fw_config->num_requests);
422}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800423static DEVICE_ATTR_RW(config_num_requests);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700424
Scott Branden7feebfa2019-08-22 11:40:04 -0700425static ssize_t config_into_buf_store(struct device *dev,
426 struct device_attribute *attr,
427 const char *buf, size_t count)
428{
429 return test_dev_config_update_bool(buf,
430 count,
431 &test_fw_config->into_buf);
432}
433
434static ssize_t config_into_buf_show(struct device *dev,
435 struct device_attribute *attr,
436 char *buf)
437{
438 return test_dev_config_show_bool(buf, test_fw_config->into_buf);
439}
440static DEVICE_ATTR_RW(config_into_buf);
441
Scott Branden5d90e052020-10-02 10:38:28 -0700442static ssize_t config_buf_size_store(struct device *dev,
443 struct device_attribute *attr,
444 const char *buf, size_t count)
445{
446 int rc;
447
448 mutex_lock(&test_fw_mutex);
449 if (test_fw_config->reqs) {
450 pr_err("Must call release_all_firmware prior to changing config\n");
451 rc = -EINVAL;
452 mutex_unlock(&test_fw_mutex);
453 goto out;
454 }
455 mutex_unlock(&test_fw_mutex);
456
457 rc = test_dev_config_update_size_t(buf, count,
458 &test_fw_config->buf_size);
459
460out:
461 return rc;
462}
463
464static ssize_t config_buf_size_show(struct device *dev,
465 struct device_attribute *attr,
466 char *buf)
467{
468 return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
469}
470static DEVICE_ATTR_RW(config_buf_size);
471
472static ssize_t config_file_offset_store(struct device *dev,
473 struct device_attribute *attr,
474 const char *buf, size_t count)
475{
476 int rc;
477
478 mutex_lock(&test_fw_mutex);
479 if (test_fw_config->reqs) {
480 pr_err("Must call release_all_firmware prior to changing config\n");
481 rc = -EINVAL;
482 mutex_unlock(&test_fw_mutex);
483 goto out;
484 }
485 mutex_unlock(&test_fw_mutex);
486
487 rc = test_dev_config_update_size_t(buf, count,
488 &test_fw_config->file_offset);
489
490out:
491 return rc;
492}
493
494static ssize_t config_file_offset_show(struct device *dev,
495 struct device_attribute *attr,
496 char *buf)
497{
498 return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
499}
500static DEVICE_ATTR_RW(config_file_offset);
501
502static ssize_t config_partial_store(struct device *dev,
503 struct device_attribute *attr,
504 const char *buf, size_t count)
505{
506 return test_dev_config_update_bool(buf,
507 count,
508 &test_fw_config->partial);
509}
510
511static ssize_t config_partial_show(struct device *dev,
512 struct device_attribute *attr,
513 char *buf)
514{
515 return test_dev_config_show_bool(buf, test_fw_config->partial);
516}
517static DEVICE_ATTR_RW(config_partial);
518
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700519static ssize_t config_sync_direct_store(struct device *dev,
520 struct device_attribute *attr,
521 const char *buf, size_t count)
522{
523 int rc = test_dev_config_update_bool(buf, count,
524 &test_fw_config->sync_direct);
525
526 if (rc == count)
527 test_fw_config->req_firmware = test_fw_config->sync_direct ?
528 request_firmware_direct :
529 request_firmware;
530 return rc;
531}
532
533static ssize_t config_sync_direct_show(struct device *dev,
534 struct device_attribute *attr,
535 char *buf)
536{
537 return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
538}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800539static DEVICE_ATTR_RW(config_sync_direct);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700540
541static ssize_t config_send_uevent_store(struct device *dev,
542 struct device_attribute *attr,
543 const char *buf, size_t count)
544{
545 return test_dev_config_update_bool(buf, count,
546 &test_fw_config->send_uevent);
547}
548
549static ssize_t config_send_uevent_show(struct device *dev,
550 struct device_attribute *attr,
551 char *buf)
552{
553 return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
554}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800555static DEVICE_ATTR_RW(config_send_uevent);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700556
557static ssize_t config_read_fw_idx_store(struct device *dev,
558 struct device_attribute *attr,
559 const char *buf, size_t count)
560{
561 return test_dev_config_update_u8(buf, count,
562 &test_fw_config->read_fw_idx);
563}
564
565static ssize_t config_read_fw_idx_show(struct device *dev,
566 struct device_attribute *attr,
567 char *buf)
568{
569 return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
570}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800571static DEVICE_ATTR_RW(config_read_fw_idx);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700572
573
Kees Cook0a8adf52014-07-14 14:38:12 -0700574static ssize_t trigger_request_store(struct device *dev,
575 struct device_attribute *attr,
576 const char *buf, size_t count)
577{
578 int rc;
579 char *name;
580
Brian Norrisbe4a1322015-12-09 14:50:26 -0800581 name = kstrndup(buf, count, GFP_KERNEL);
Kees Cook0a8adf52014-07-14 14:38:12 -0700582 if (!name)
583 return -ENOSPC;
Kees Cook0a8adf52014-07-14 14:38:12 -0700584
585 pr_info("loading '%s'\n", name);
586
587 mutex_lock(&test_fw_mutex);
588 release_firmware(test_firmware);
589 test_firmware = NULL;
590 rc = request_firmware(&test_firmware, name, dev);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800591 if (rc) {
Kees Cook0a8adf52014-07-14 14:38:12 -0700592 pr_info("load of '%s' failed: %d\n", name, rc);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800593 goto out;
594 }
595 pr_info("loaded: %zu\n", test_firmware->size);
596 rc = count;
597
598out:
Kees Cook0a8adf52014-07-14 14:38:12 -0700599 mutex_unlock(&test_fw_mutex);
600
601 kfree(name);
602
Brian Norris47e0bbb2015-12-09 14:50:25 -0800603 return rc;
Kees Cook0a8adf52014-07-14 14:38:12 -0700604}
605static DEVICE_ATTR_WO(trigger_request);
606
Hans de Goede548193c2020-01-15 17:35:49 +0100607#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
Kees Cookbaaabec2020-09-09 15:53:54 -0700608extern struct list_head efi_embedded_fw_list;
609extern bool efi_embedded_fw_checked;
610
Hans de Goede548193c2020-01-15 17:35:49 +0100611static ssize_t trigger_request_platform_store(struct device *dev,
612 struct device_attribute *attr,
613 const char *buf, size_t count)
614{
615 static const u8 test_data[] = {
616 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
617 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
618 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
619 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
620 };
621 struct efi_embedded_fw efi_embedded_fw;
622 const struct firmware *firmware = NULL;
Kees Cookbaaabec2020-09-09 15:53:54 -0700623 bool saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100624 char *name;
625 int rc;
626
627 name = kstrndup(buf, count, GFP_KERNEL);
628 if (!name)
629 return -ENOSPC;
630
631 pr_info("inserting test platform fw '%s'\n", name);
632 efi_embedded_fw.name = name;
633 efi_embedded_fw.data = (void *)test_data;
634 efi_embedded_fw.length = sizeof(test_data);
635 list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
Kees Cookbaaabec2020-09-09 15:53:54 -0700636 saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
637 efi_embedded_fw_checked = true;
Hans de Goede548193c2020-01-15 17:35:49 +0100638
639 pr_info("loading '%s'\n", name);
640 rc = firmware_request_platform(&firmware, name, dev);
641 if (rc) {
642 pr_info("load of '%s' failed: %d\n", name, rc);
643 goto out;
644 }
645 if (firmware->size != sizeof(test_data) ||
646 memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
647 pr_info("firmware contents mismatch for '%s'\n", name);
648 rc = -EINVAL;
649 goto out;
650 }
651 pr_info("loaded: %zu\n", firmware->size);
652 rc = count;
653
654out:
Kees Cookbaaabec2020-09-09 15:53:54 -0700655 efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100656 release_firmware(firmware);
657 list_del(&efi_embedded_fw.list);
658 kfree(name);
659
660 return rc;
661}
662static DEVICE_ATTR_WO(trigger_request_platform);
663#endif
664
Brian Norriseb910942015-12-09 14:50:27 -0800665static DECLARE_COMPLETION(async_fw_done);
666
667static void trigger_async_request_cb(const struct firmware *fw, void *context)
668{
669 test_firmware = fw;
670 complete(&async_fw_done);
671}
672
673static ssize_t trigger_async_request_store(struct device *dev,
674 struct device_attribute *attr,
675 const char *buf, size_t count)
676{
677 int rc;
678 char *name;
679
680 name = kstrndup(buf, count, GFP_KERNEL);
681 if (!name)
682 return -ENOSPC;
683
684 pr_info("loading '%s'\n", name);
685
686 mutex_lock(&test_fw_mutex);
687 release_firmware(test_firmware);
688 test_firmware = NULL;
689 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
690 NULL, trigger_async_request_cb);
691 if (rc) {
692 pr_info("async load of '%s' failed: %d\n", name, rc);
693 kfree(name);
694 goto out;
695 }
696 /* Free 'name' ASAP, to test for race conditions */
697 kfree(name);
698
699 wait_for_completion(&async_fw_done);
700
701 if (test_firmware) {
702 pr_info("loaded: %zu\n", test_firmware->size);
703 rc = count;
704 } else {
705 pr_err("failed to async load firmware\n");
Scott Branden7feebfa2019-08-22 11:40:04 -0700706 rc = -ENOMEM;
Brian Norriseb910942015-12-09 14:50:27 -0800707 }
708
709out:
710 mutex_unlock(&test_fw_mutex);
711
712 return rc;
713}
714static DEVICE_ATTR_WO(trigger_async_request);
715
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800716static ssize_t trigger_custom_fallback_store(struct device *dev,
717 struct device_attribute *attr,
718 const char *buf, size_t count)
719{
720 int rc;
721 char *name;
722
723 name = kstrndup(buf, count, GFP_KERNEL);
724 if (!name)
725 return -ENOSPC;
726
727 pr_info("loading '%s' using custom fallback mechanism\n", name);
728
729 mutex_lock(&test_fw_mutex);
730 release_firmware(test_firmware);
731 test_firmware = NULL;
Shawn Guo0733d832021-04-25 10:00:24 +0800732 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, name,
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800733 dev, GFP_KERNEL, NULL,
734 trigger_async_request_cb);
735 if (rc) {
736 pr_info("async load of '%s' failed: %d\n", name, rc);
737 kfree(name);
738 goto out;
739 }
740 /* Free 'name' ASAP, to test for race conditions */
741 kfree(name);
742
743 wait_for_completion(&async_fw_done);
744
745 if (test_firmware) {
746 pr_info("loaded: %zu\n", test_firmware->size);
747 rc = count;
748 } else {
749 pr_err("failed to async load firmware\n");
750 rc = -ENODEV;
751 }
752
753out:
754 mutex_unlock(&test_fw_mutex);
755
756 return rc;
757}
758static DEVICE_ATTR_WO(trigger_custom_fallback);
759
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700760static int test_fw_run_batch_request(void *data)
761{
762 struct test_batched_req *req = data;
763
764 if (!req) {
765 test_fw_config->test_result = -EINVAL;
766 return -EINVAL;
767 }
768
Scott Branden7feebfa2019-08-22 11:40:04 -0700769 if (test_fw_config->into_buf) {
770 void *test_buf;
771
772 test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
773 if (!test_buf)
774 return -ENOSPC;
775
Scott Branden5d90e052020-10-02 10:38:28 -0700776 if (test_fw_config->partial)
777 req->rc = request_partial_firmware_into_buf
778 (&req->fw,
779 req->name,
780 req->dev,
781 test_buf,
782 test_fw_config->buf_size,
783 test_fw_config->file_offset);
784 else
785 req->rc = request_firmware_into_buf
786 (&req->fw,
787 req->name,
788 req->dev,
789 test_buf,
790 test_fw_config->buf_size);
Scott Branden7feebfa2019-08-22 11:40:04 -0700791 if (!req->fw)
792 kfree(test_buf);
793 } else {
794 req->rc = test_fw_config->req_firmware(&req->fw,
795 req->name,
796 req->dev);
797 }
798
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700799 if (req->rc) {
800 pr_info("#%u: batched sync load failed: %d\n",
801 req->idx, req->rc);
802 if (!test_fw_config->test_result)
803 test_fw_config->test_result = req->rc;
804 } else if (req->fw) {
805 req->sent = true;
806 pr_info("#%u: batched sync loaded %zu\n",
807 req->idx, req->fw->size);
808 }
809 complete(&req->completion);
810
811 req->task = NULL;
812
813 return 0;
814}
815
816/*
817 * We use a kthread as otherwise the kernel serializes all our sync requests
818 * and we would not be able to mimic batched requests on a sync call. Batched
819 * requests on a sync call can for instance happen on a device driver when
820 * multiple cards are used and firmware loading happens outside of probe.
821 */
822static ssize_t trigger_batched_requests_store(struct device *dev,
823 struct device_attribute *attr,
824 const char *buf, size_t count)
825{
826 struct test_batched_req *req;
827 int rc;
828 u8 i;
829
830 mutex_lock(&test_fw_mutex);
831
Kees Cookfad953c2018-06-12 14:27:37 -0700832 test_fw_config->reqs =
833 vzalloc(array3_size(sizeof(struct test_batched_req),
834 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700835 if (!test_fw_config->reqs) {
836 rc = -ENOMEM;
837 goto out_unlock;
838 }
839
840 pr_info("batched sync firmware loading '%s' %u times\n",
841 test_fw_config->name, test_fw_config->num_requests);
842
843 for (i = 0; i < test_fw_config->num_requests; i++) {
844 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700845 req->fw = NULL;
846 req->idx = i;
847 req->name = test_fw_config->name;
848 req->dev = dev;
849 init_completion(&req->completion);
850 req->task = kthread_run(test_fw_run_batch_request, req,
851 "%s-%u", KBUILD_MODNAME, req->idx);
852 if (!req->task || IS_ERR(req->task)) {
853 pr_err("Setting up thread %u failed\n", req->idx);
854 req->task = NULL;
855 rc = -ENOMEM;
856 goto out_bail;
857 }
858 }
859
860 rc = count;
861
862 /*
863 * We require an explicit release to enable more time and delay of
864 * calling release_firmware() to improve our chances of forcing a
865 * batched request. If we instead called release_firmware() right away
866 * then we might miss on an opportunity of having a successful firmware
867 * request pass on the opportunity to be come a batched request.
868 */
869
870out_bail:
871 for (i = 0; i < test_fw_config->num_requests; i++) {
872 req = &test_fw_config->reqs[i];
873 if (req->task || req->sent)
874 wait_for_completion(&req->completion);
875 }
876
877 /* Override any worker error if we had a general setup error */
878 if (rc < 0)
879 test_fw_config->test_result = rc;
880
881out_unlock:
882 mutex_unlock(&test_fw_mutex);
883
884 return rc;
885}
886static DEVICE_ATTR_WO(trigger_batched_requests);
887
888/*
889 * We wait for each callback to return with the lock held, no need to lock here
890 */
891static void trigger_batched_cb(const struct firmware *fw, void *context)
892{
893 struct test_batched_req *req = context;
894
895 if (!req) {
896 test_fw_config->test_result = -EINVAL;
897 return;
898 }
899
900 /* forces *some* batched requests to queue up */
901 if (!req->idx)
902 ssleep(2);
903
904 req->fw = fw;
905
906 /*
907 * Unfortunately the firmware API gives us nothing other than a null FW
908 * if the firmware was not found on async requests. Best we can do is
909 * just assume -ENOENT. A better API would pass the actual return
910 * value to the callback.
911 */
912 if (!fw && !test_fw_config->test_result)
913 test_fw_config->test_result = -ENOENT;
914
915 complete(&req->completion);
916}
917
918static
919ssize_t trigger_batched_requests_async_store(struct device *dev,
920 struct device_attribute *attr,
921 const char *buf, size_t count)
922{
923 struct test_batched_req *req;
924 bool send_uevent;
925 int rc;
926 u8 i;
927
928 mutex_lock(&test_fw_mutex);
929
Kees Cookfad953c2018-06-12 14:27:37 -0700930 test_fw_config->reqs =
931 vzalloc(array3_size(sizeof(struct test_batched_req),
932 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700933 if (!test_fw_config->reqs) {
934 rc = -ENOMEM;
935 goto out;
936 }
937
938 pr_info("batched loading '%s' custom fallback mechanism %u times\n",
939 test_fw_config->name, test_fw_config->num_requests);
940
Shawn Guo0733d832021-04-25 10:00:24 +0800941 send_uevent = test_fw_config->send_uevent ? FW_ACTION_UEVENT :
942 FW_ACTION_NOUEVENT;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700943
944 for (i = 0; i < test_fw_config->num_requests; i++) {
945 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700946 req->name = test_fw_config->name;
947 req->fw = NULL;
948 req->idx = i;
949 init_completion(&req->completion);
950 rc = request_firmware_nowait(THIS_MODULE, send_uevent,
951 req->name,
952 dev, GFP_KERNEL, req,
953 trigger_batched_cb);
954 if (rc) {
955 pr_info("#%u: batched async load failed setup: %d\n",
956 i, rc);
957 req->rc = rc;
958 goto out_bail;
959 } else
960 req->sent = true;
961 }
962
963 rc = count;
964
965out_bail:
966
967 /*
968 * We require an explicit release to enable more time and delay of
969 * calling release_firmware() to improve our chances of forcing a
970 * batched request. If we instead called release_firmware() right away
971 * then we might miss on an opportunity of having a successful firmware
972 * request pass on the opportunity to be come a batched request.
973 */
974
975 for (i = 0; i < test_fw_config->num_requests; i++) {
976 req = &test_fw_config->reqs[i];
977 if (req->sent)
978 wait_for_completion(&req->completion);
979 }
980
981 /* Override any worker error if we had a general setup error */
982 if (rc < 0)
983 test_fw_config->test_result = rc;
984
985out:
986 mutex_unlock(&test_fw_mutex);
987
988 return rc;
989}
990static DEVICE_ATTR_WO(trigger_batched_requests_async);
991
992static ssize_t test_result_show(struct device *dev,
993 struct device_attribute *attr,
994 char *buf)
995{
996 return test_dev_config_show_int(buf, test_fw_config->test_result);
997}
998static DEVICE_ATTR_RO(test_result);
999
1000static ssize_t release_all_firmware_store(struct device *dev,
1001 struct device_attribute *attr,
1002 const char *buf, size_t count)
1003{
1004 test_release_all_firmware();
1005 return count;
1006}
1007static DEVICE_ATTR_WO(release_all_firmware);
1008
1009static ssize_t read_firmware_show(struct device *dev,
1010 struct device_attribute *attr,
1011 char *buf)
1012{
1013 struct test_batched_req *req;
1014 u8 idx;
1015 ssize_t rc = 0;
1016
1017 mutex_lock(&test_fw_mutex);
1018
1019 idx = test_fw_config->read_fw_idx;
1020 if (idx >= test_fw_config->num_requests) {
1021 rc = -ERANGE;
1022 goto out;
1023 }
1024
1025 if (!test_fw_config->reqs) {
1026 rc = -EINVAL;
1027 goto out;
1028 }
1029
1030 req = &test_fw_config->reqs[idx];
1031 if (!req->fw) {
1032 pr_err("#%u: failed to async load firmware\n", idx);
1033 rc = -ENOENT;
1034 goto out;
1035 }
1036
1037 pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1038
1039 if (req->fw->size > PAGE_SIZE) {
1040 pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1041 rc = -EINVAL;
Colin Ian King8bb0a882018-10-19 13:58:01 +01001042 goto out;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001043 }
1044 memcpy(buf, req->fw->data, req->fw->size);
1045
1046 rc = req->fw->size;
1047out:
1048 mutex_unlock(&test_fw_mutex);
1049
1050 return rc;
1051}
1052static DEVICE_ATTR_RO(read_firmware);
1053
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001054#define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr
1055
1056static struct attribute *test_dev_attrs[] = {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001057 TEST_FW_DEV_ATTR(reset),
1058
1059 TEST_FW_DEV_ATTR(config),
1060 TEST_FW_DEV_ATTR(config_name),
1061 TEST_FW_DEV_ATTR(config_num_requests),
Scott Branden7feebfa2019-08-22 11:40:04 -07001062 TEST_FW_DEV_ATTR(config_into_buf),
Scott Branden5d90e052020-10-02 10:38:28 -07001063 TEST_FW_DEV_ATTR(config_buf_size),
1064 TEST_FW_DEV_ATTR(config_file_offset),
1065 TEST_FW_DEV_ATTR(config_partial),
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001066 TEST_FW_DEV_ATTR(config_sync_direct),
1067 TEST_FW_DEV_ATTR(config_send_uevent),
1068 TEST_FW_DEV_ATTR(config_read_fw_idx),
1069
1070 /* These don't use the config at all - they could be ported! */
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001071 TEST_FW_DEV_ATTR(trigger_request),
1072 TEST_FW_DEV_ATTR(trigger_async_request),
Luis R. Rodriguez061132d2017-01-23 08:11:10 -08001073 TEST_FW_DEV_ATTR(trigger_custom_fallback),
Hans de Goede548193c2020-01-15 17:35:49 +01001074#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1075 TEST_FW_DEV_ATTR(trigger_request_platform),
1076#endif
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001077
1078 /* These use the config and can use the test_result */
1079 TEST_FW_DEV_ATTR(trigger_batched_requests),
1080 TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1081
1082 TEST_FW_DEV_ATTR(release_all_firmware),
1083 TEST_FW_DEV_ATTR(test_result),
1084 TEST_FW_DEV_ATTR(read_firmware),
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001085 NULL,
1086};
1087
1088ATTRIBUTE_GROUPS(test_dev);
1089
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001090static struct miscdevice test_fw_misc_device = {
1091 .minor = MISC_DYNAMIC_MINOR,
1092 .name = "test_firmware",
1093 .fops = &test_fw_fops,
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001094 .groups = test_dev_groups,
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001095};
1096
Kees Cook0a8adf52014-07-14 14:38:12 -07001097static int __init test_firmware_init(void)
1098{
1099 int rc;
1100
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001101 test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1102 if (!test_fw_config)
1103 return -ENOMEM;
1104
1105 rc = __test_firmware_config_init();
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001106 if (rc) {
1107 kfree(test_fw_config);
1108 pr_err("could not init firmware test config: %d\n", rc);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001109 return rc;
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001110 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001111
Kees Cook0a8adf52014-07-14 14:38:12 -07001112 rc = misc_register(&test_fw_misc_device);
1113 if (rc) {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001114 kfree(test_fw_config);
Kees Cook0a8adf52014-07-14 14:38:12 -07001115 pr_err("could not register misc device: %d\n", rc);
1116 return rc;
1117 }
Brian Norriseb910942015-12-09 14:50:27 -08001118
Kees Cook0a8adf52014-07-14 14:38:12 -07001119 pr_warn("interface ready\n");
1120
1121 return 0;
Kees Cook0a8adf52014-07-14 14:38:12 -07001122}
1123
1124module_init(test_firmware_init);
1125
1126static void __exit test_firmware_exit(void)
1127{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001128 mutex_lock(&test_fw_mutex);
Kees Cook0a8adf52014-07-14 14:38:12 -07001129 release_firmware(test_firmware);
Kees Cook0a8adf52014-07-14 14:38:12 -07001130 misc_deregister(&test_fw_misc_device);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001131 __test_firmware_config_free();
1132 kfree(test_fw_config);
1133 mutex_unlock(&test_fw_mutex);
1134
Kees Cook0a8adf52014-07-14 14:38:12 -07001135 pr_warn("removed interface\n");
1136}
1137
1138module_exit(test_firmware_exit);
1139
1140MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
1141MODULE_LICENSE("GPL");