blob: 25dc9eb6c902b74e4991195ffcd372cdd527af08 [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)
Mirsad Goran Todorovac4d240512023-06-06 09:08:10 +0200186 return -ENOMEM;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700187 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
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200324static inline int __test_dev_config_update_bool(const char *buf, size_t size,
325 bool *cfg)
326{
327 int ret;
328
329 if (kstrtobool(buf, cfg) < 0)
330 ret = -EINVAL;
331 else
332 ret = size;
333
334 return ret;
335}
336
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700337static int test_dev_config_update_bool(const char *buf, size_t size,
338 bool *cfg)
339{
340 int ret;
341
342 mutex_lock(&test_fw_mutex);
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200343 ret = __test_dev_config_update_bool(buf, size, cfg);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700344 mutex_unlock(&test_fw_mutex);
345
346 return ret;
347}
348
Scott Branden55623262020-04-14 17:25:17 -0700349static ssize_t test_dev_config_show_bool(char *buf, bool val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700350{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700351 return snprintf(buf, PAGE_SIZE, "%d\n", val);
352}
353
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200354static int __test_dev_config_update_size_t(
355 const char *buf,
Scott Branden5d90e052020-10-02 10:38:28 -0700356 size_t size,
357 size_t *cfg)
358{
359 int ret;
360 long new;
361
362 ret = kstrtol(buf, 10, &new);
363 if (ret)
364 return ret;
365
Scott Branden5d90e052020-10-02 10:38:28 -0700366 *(size_t *)cfg = new;
Scott Branden5d90e052020-10-02 10:38:28 -0700367
368 /* Always return full write size even if we didn't consume all */
369 return size;
370}
371
372static ssize_t test_dev_config_show_size_t(char *buf, size_t val)
373{
374 return snprintf(buf, PAGE_SIZE, "%zu\n", val);
375}
376
Scott Branden55623262020-04-14 17:25:17 -0700377static ssize_t test_dev_config_show_int(char *buf, int val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700378{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700379 return snprintf(buf, PAGE_SIZE, "%d\n", val);
380}
381
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200382static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700383{
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800384 u8 val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700385 int ret;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700386
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800387 ret = kstrtou8(buf, 10, &val);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700388 if (ret)
389 return ret;
390
Alexey Dobriyanc2def5572020-12-15 20:44:00 -0800391 *(u8 *)cfg = val;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700392
393 /* Always return full write size even if we didn't consume all */
394 return size;
395}
396
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200397static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
398{
399 int ret;
400
401 mutex_lock(&test_fw_mutex);
402 ret = __test_dev_config_update_u8(buf, size, cfg);
403 mutex_unlock(&test_fw_mutex);
404
405 return ret;
406}
407
Scott Branden55623262020-04-14 17:25:17 -0700408static ssize_t test_dev_config_show_u8(char *buf, u8 val)
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700409{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700410 return snprintf(buf, PAGE_SIZE, "%u\n", val);
411}
412
413static ssize_t config_name_show(struct device *dev,
414 struct device_attribute *attr,
415 char *buf)
416{
417 return config_test_show_str(buf, test_fw_config->name);
418}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800419static DEVICE_ATTR_RW(config_name);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700420
421static ssize_t config_num_requests_store(struct device *dev,
422 struct device_attribute *attr,
423 const char *buf, size_t count)
424{
425 int rc;
426
427 mutex_lock(&test_fw_mutex);
428 if (test_fw_config->reqs) {
429 pr_err("Must call release_all_firmware prior to changing config\n");
430 rc = -EINVAL;
Wei Yongjuna5e19232018-01-11 11:12:55 +0000431 mutex_unlock(&test_fw_mutex);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700432 goto out;
433 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700434
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200435 rc = __test_dev_config_update_u8(buf, count,
436 &test_fw_config->num_requests);
437 mutex_unlock(&test_fw_mutex);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700438
439out:
440 return rc;
441}
442
443static ssize_t config_num_requests_show(struct device *dev,
444 struct device_attribute *attr,
445 char *buf)
446{
447 return test_dev_config_show_u8(buf, test_fw_config->num_requests);
448}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800449static DEVICE_ATTR_RW(config_num_requests);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700450
Scott Branden7feebfa2019-08-22 11:40:04 -0700451static ssize_t config_into_buf_store(struct device *dev,
452 struct device_attribute *attr,
453 const char *buf, size_t count)
454{
455 return test_dev_config_update_bool(buf,
456 count,
457 &test_fw_config->into_buf);
458}
459
460static ssize_t config_into_buf_show(struct device *dev,
461 struct device_attribute *attr,
462 char *buf)
463{
464 return test_dev_config_show_bool(buf, test_fw_config->into_buf);
465}
466static DEVICE_ATTR_RW(config_into_buf);
467
Scott Branden5d90e052020-10-02 10:38:28 -0700468static ssize_t config_buf_size_store(struct device *dev,
469 struct device_attribute *attr,
470 const char *buf, size_t count)
471{
472 int rc;
473
474 mutex_lock(&test_fw_mutex);
475 if (test_fw_config->reqs) {
476 pr_err("Must call release_all_firmware prior to changing config\n");
477 rc = -EINVAL;
478 mutex_unlock(&test_fw_mutex);
479 goto out;
480 }
Scott Branden5d90e052020-10-02 10:38:28 -0700481
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200482 rc = __test_dev_config_update_size_t(buf, count,
483 &test_fw_config->buf_size);
484 mutex_unlock(&test_fw_mutex);
Scott Branden5d90e052020-10-02 10:38:28 -0700485
486out:
487 return rc;
488}
489
490static ssize_t config_buf_size_show(struct device *dev,
491 struct device_attribute *attr,
492 char *buf)
493{
494 return test_dev_config_show_size_t(buf, test_fw_config->buf_size);
495}
496static DEVICE_ATTR_RW(config_buf_size);
497
498static ssize_t config_file_offset_store(struct device *dev,
499 struct device_attribute *attr,
500 const char *buf, size_t count)
501{
502 int rc;
503
504 mutex_lock(&test_fw_mutex);
505 if (test_fw_config->reqs) {
506 pr_err("Must call release_all_firmware prior to changing config\n");
507 rc = -EINVAL;
508 mutex_unlock(&test_fw_mutex);
509 goto out;
510 }
Scott Branden5d90e052020-10-02 10:38:28 -0700511
Mirsad Goran Todorovacaf36f352023-05-09 10:47:45 +0200512 rc = __test_dev_config_update_size_t(buf, count,
513 &test_fw_config->file_offset);
514 mutex_unlock(&test_fw_mutex);
Scott Branden5d90e052020-10-02 10:38:28 -0700515
516out:
517 return rc;
518}
519
520static ssize_t config_file_offset_show(struct device *dev,
521 struct device_attribute *attr,
522 char *buf)
523{
524 return test_dev_config_show_size_t(buf, test_fw_config->file_offset);
525}
526static DEVICE_ATTR_RW(config_file_offset);
527
528static ssize_t config_partial_store(struct device *dev,
529 struct device_attribute *attr,
530 const char *buf, size_t count)
531{
532 return test_dev_config_update_bool(buf,
533 count,
534 &test_fw_config->partial);
535}
536
537static ssize_t config_partial_show(struct device *dev,
538 struct device_attribute *attr,
539 char *buf)
540{
541 return test_dev_config_show_bool(buf, test_fw_config->partial);
542}
543static DEVICE_ATTR_RW(config_partial);
544
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700545static ssize_t config_sync_direct_store(struct device *dev,
546 struct device_attribute *attr,
547 const char *buf, size_t count)
548{
549 int rc = test_dev_config_update_bool(buf, count,
550 &test_fw_config->sync_direct);
551
552 if (rc == count)
553 test_fw_config->req_firmware = test_fw_config->sync_direct ?
554 request_firmware_direct :
555 request_firmware;
556 return rc;
557}
558
559static ssize_t config_sync_direct_show(struct device *dev,
560 struct device_attribute *attr,
561 char *buf)
562{
563 return test_dev_config_show_bool(buf, test_fw_config->sync_direct);
564}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800565static DEVICE_ATTR_RW(config_sync_direct);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700566
567static ssize_t config_send_uevent_store(struct device *dev,
568 struct device_attribute *attr,
569 const char *buf, size_t count)
570{
571 return test_dev_config_update_bool(buf, count,
572 &test_fw_config->send_uevent);
573}
574
575static ssize_t config_send_uevent_show(struct device *dev,
576 struct device_attribute *attr,
577 char *buf)
578{
579 return test_dev_config_show_bool(buf, test_fw_config->send_uevent);
580}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800581static DEVICE_ATTR_RW(config_send_uevent);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700582
583static ssize_t config_read_fw_idx_store(struct device *dev,
584 struct device_attribute *attr,
585 const char *buf, size_t count)
586{
587 return test_dev_config_update_u8(buf, count,
588 &test_fw_config->read_fw_idx);
589}
590
591static ssize_t config_read_fw_idx_show(struct device *dev,
592 struct device_attribute *attr,
593 char *buf)
594{
595 return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx);
596}
Joe Perchesb6b996b2017-12-19 10:15:07 -0800597static DEVICE_ATTR_RW(config_read_fw_idx);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700598
599
Kees Cook0a8adf52014-07-14 14:38:12 -0700600static ssize_t trigger_request_store(struct device *dev,
601 struct device_attribute *attr,
602 const char *buf, size_t count)
603{
604 int rc;
605 char *name;
606
Brian Norrisbe4a1322015-12-09 14:50:26 -0800607 name = kstrndup(buf, count, GFP_KERNEL);
Kees Cook0a8adf52014-07-14 14:38:12 -0700608 if (!name)
Mirsad Goran Todorovac4d240512023-06-06 09:08:10 +0200609 return -ENOMEM;
Kees Cook0a8adf52014-07-14 14:38:12 -0700610
611 pr_info("loading '%s'\n", name);
612
613 mutex_lock(&test_fw_mutex);
614 release_firmware(test_firmware);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200615 if (test_fw_config->reqs)
616 __test_release_all_firmware();
Kees Cook0a8adf52014-07-14 14:38:12 -0700617 test_firmware = NULL;
618 rc = request_firmware(&test_firmware, name, dev);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800619 if (rc) {
Kees Cook0a8adf52014-07-14 14:38:12 -0700620 pr_info("load of '%s' failed: %d\n", name, rc);
Brian Norris47e0bbb2015-12-09 14:50:25 -0800621 goto out;
622 }
623 pr_info("loaded: %zu\n", test_firmware->size);
624 rc = count;
625
626out:
Kees Cook0a8adf52014-07-14 14:38:12 -0700627 mutex_unlock(&test_fw_mutex);
628
629 kfree(name);
630
Brian Norris47e0bbb2015-12-09 14:50:25 -0800631 return rc;
Kees Cook0a8adf52014-07-14 14:38:12 -0700632}
633static DEVICE_ATTR_WO(trigger_request);
634
Hans de Goede548193c2020-01-15 17:35:49 +0100635#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
Kees Cookbaaabec2020-09-09 15:53:54 -0700636extern struct list_head efi_embedded_fw_list;
637extern bool efi_embedded_fw_checked;
638
Hans de Goede548193c2020-01-15 17:35:49 +0100639static ssize_t trigger_request_platform_store(struct device *dev,
640 struct device_attribute *attr,
641 const char *buf, size_t count)
642{
643 static const u8 test_data[] = {
644 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04,
645 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08,
646 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40,
647 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80
648 };
649 struct efi_embedded_fw efi_embedded_fw;
650 const struct firmware *firmware = NULL;
Kees Cookbaaabec2020-09-09 15:53:54 -0700651 bool saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100652 char *name;
653 int rc;
654
655 name = kstrndup(buf, count, GFP_KERNEL);
656 if (!name)
Mirsad Goran Todorovac4d240512023-06-06 09:08:10 +0200657 return -ENOMEM;
Hans de Goede548193c2020-01-15 17:35:49 +0100658
659 pr_info("inserting test platform fw '%s'\n", name);
660 efi_embedded_fw.name = name;
661 efi_embedded_fw.data = (void *)test_data;
662 efi_embedded_fw.length = sizeof(test_data);
663 list_add(&efi_embedded_fw.list, &efi_embedded_fw_list);
Kees Cookbaaabec2020-09-09 15:53:54 -0700664 saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
665 efi_embedded_fw_checked = true;
Hans de Goede548193c2020-01-15 17:35:49 +0100666
667 pr_info("loading '%s'\n", name);
668 rc = firmware_request_platform(&firmware, name, dev);
669 if (rc) {
670 pr_info("load of '%s' failed: %d\n", name, rc);
671 goto out;
672 }
673 if (firmware->size != sizeof(test_data) ||
674 memcmp(firmware->data, test_data, sizeof(test_data)) != 0) {
675 pr_info("firmware contents mismatch for '%s'\n", name);
676 rc = -EINVAL;
677 goto out;
678 }
679 pr_info("loaded: %zu\n", firmware->size);
680 rc = count;
681
682out:
Kees Cookbaaabec2020-09-09 15:53:54 -0700683 efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
Hans de Goede548193c2020-01-15 17:35:49 +0100684 release_firmware(firmware);
685 list_del(&efi_embedded_fw.list);
686 kfree(name);
687
688 return rc;
689}
690static DEVICE_ATTR_WO(trigger_request_platform);
691#endif
692
Brian Norriseb910942015-12-09 14:50:27 -0800693static DECLARE_COMPLETION(async_fw_done);
694
695static void trigger_async_request_cb(const struct firmware *fw, void *context)
696{
697 test_firmware = fw;
698 complete(&async_fw_done);
699}
700
701static ssize_t trigger_async_request_store(struct device *dev,
702 struct device_attribute *attr,
703 const char *buf, size_t count)
704{
705 int rc;
706 char *name;
707
708 name = kstrndup(buf, count, GFP_KERNEL);
709 if (!name)
Mirsad Goran Todorovac4d240512023-06-06 09:08:10 +0200710 return -ENOMEM;
Brian Norriseb910942015-12-09 14:50:27 -0800711
712 pr_info("loading '%s'\n", name);
713
714 mutex_lock(&test_fw_mutex);
715 release_firmware(test_firmware);
716 test_firmware = NULL;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200717 if (test_fw_config->reqs)
718 __test_release_all_firmware();
Brian Norriseb910942015-12-09 14:50:27 -0800719 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
720 NULL, trigger_async_request_cb);
721 if (rc) {
722 pr_info("async load of '%s' failed: %d\n", name, rc);
723 kfree(name);
724 goto out;
725 }
726 /* Free 'name' ASAP, to test for race conditions */
727 kfree(name);
728
729 wait_for_completion(&async_fw_done);
730
731 if (test_firmware) {
732 pr_info("loaded: %zu\n", test_firmware->size);
733 rc = count;
734 } else {
735 pr_err("failed to async load firmware\n");
Scott Branden7feebfa2019-08-22 11:40:04 -0700736 rc = -ENOMEM;
Brian Norriseb910942015-12-09 14:50:27 -0800737 }
738
739out:
740 mutex_unlock(&test_fw_mutex);
741
742 return rc;
743}
744static DEVICE_ATTR_WO(trigger_async_request);
745
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800746static ssize_t trigger_custom_fallback_store(struct device *dev,
747 struct device_attribute *attr,
748 const char *buf, size_t count)
749{
750 int rc;
751 char *name;
752
753 name = kstrndup(buf, count, GFP_KERNEL);
754 if (!name)
Mirsad Goran Todorovac4d240512023-06-06 09:08:10 +0200755 return -ENOMEM;
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800756
757 pr_info("loading '%s' using custom fallback mechanism\n", name);
758
759 mutex_lock(&test_fw_mutex);
760 release_firmware(test_firmware);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200761 if (test_fw_config->reqs)
762 __test_release_all_firmware();
Luis R. Rodriguez061132d2017-01-23 08:11:10 -0800763 test_firmware = NULL;
764 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
765 dev, GFP_KERNEL, NULL,
766 trigger_async_request_cb);
767 if (rc) {
768 pr_info("async load of '%s' failed: %d\n", name, rc);
769 kfree(name);
770 goto out;
771 }
772 /* Free 'name' ASAP, to test for race conditions */
773 kfree(name);
774
775 wait_for_completion(&async_fw_done);
776
777 if (test_firmware) {
778 pr_info("loaded: %zu\n", test_firmware->size);
779 rc = count;
780 } else {
781 pr_err("failed to async load firmware\n");
782 rc = -ENODEV;
783 }
784
785out:
786 mutex_unlock(&test_fw_mutex);
787
788 return rc;
789}
790static DEVICE_ATTR_WO(trigger_custom_fallback);
791
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700792static int test_fw_run_batch_request(void *data)
793{
794 struct test_batched_req *req = data;
795
796 if (!req) {
797 test_fw_config->test_result = -EINVAL;
798 return -EINVAL;
799 }
800
Scott Branden7feebfa2019-08-22 11:40:04 -0700801 if (test_fw_config->into_buf) {
802 void *test_buf;
803
804 test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL);
805 if (!test_buf)
Mirsad Goran Todorovac4d240512023-06-06 09:08:10 +0200806 return -ENOMEM;
Scott Branden7feebfa2019-08-22 11:40:04 -0700807
Scott Branden5d90e052020-10-02 10:38:28 -0700808 if (test_fw_config->partial)
809 req->rc = request_partial_firmware_into_buf
810 (&req->fw,
811 req->name,
812 req->dev,
813 test_buf,
814 test_fw_config->buf_size,
815 test_fw_config->file_offset);
816 else
817 req->rc = request_firmware_into_buf
818 (&req->fw,
819 req->name,
820 req->dev,
821 test_buf,
822 test_fw_config->buf_size);
Scott Branden7feebfa2019-08-22 11:40:04 -0700823 if (!req->fw)
824 kfree(test_buf);
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200825 else
826 req->fw_buf = test_buf;
Scott Branden7feebfa2019-08-22 11:40:04 -0700827 } else {
828 req->rc = test_fw_config->req_firmware(&req->fw,
829 req->name,
830 req->dev);
831 }
832
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700833 if (req->rc) {
834 pr_info("#%u: batched sync load failed: %d\n",
835 req->idx, req->rc);
836 if (!test_fw_config->test_result)
837 test_fw_config->test_result = req->rc;
838 } else if (req->fw) {
839 req->sent = true;
840 pr_info("#%u: batched sync loaded %zu\n",
841 req->idx, req->fw->size);
842 }
843 complete(&req->completion);
844
845 req->task = NULL;
846
847 return 0;
848}
849
850/*
851 * We use a kthread as otherwise the kernel serializes all our sync requests
852 * and we would not be able to mimic batched requests on a sync call. Batched
853 * requests on a sync call can for instance happen on a device driver when
854 * multiple cards are used and firmware loading happens outside of probe.
855 */
856static ssize_t trigger_batched_requests_store(struct device *dev,
857 struct device_attribute *attr,
858 const char *buf, size_t count)
859{
860 struct test_batched_req *req;
861 int rc;
862 u8 i;
863
864 mutex_lock(&test_fw_mutex);
865
Mirsad Goran Todorovac539c3872023-05-09 10:47:47 +0200866 if (test_fw_config->reqs) {
867 rc = -EBUSY;
868 goto out_bail;
869 }
870
Kees Cookfad953c2018-06-12 14:27:37 -0700871 test_fw_config->reqs =
872 vzalloc(array3_size(sizeof(struct test_batched_req),
873 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700874 if (!test_fw_config->reqs) {
875 rc = -ENOMEM;
876 goto out_unlock;
877 }
878
879 pr_info("batched sync firmware loading '%s' %u times\n",
880 test_fw_config->name, test_fw_config->num_requests);
881
882 for (i = 0; i < test_fw_config->num_requests; i++) {
883 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700884 req->fw = NULL;
885 req->idx = i;
886 req->name = test_fw_config->name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200887 req->fw_buf = NULL;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700888 req->dev = dev;
889 init_completion(&req->completion);
890 req->task = kthread_run(test_fw_run_batch_request, req,
891 "%s-%u", KBUILD_MODNAME, req->idx);
892 if (!req->task || IS_ERR(req->task)) {
893 pr_err("Setting up thread %u failed\n", req->idx);
894 req->task = NULL;
895 rc = -ENOMEM;
896 goto out_bail;
897 }
898 }
899
900 rc = count;
901
902 /*
903 * We require an explicit release to enable more time and delay of
904 * calling release_firmware() to improve our chances of forcing a
905 * batched request. If we instead called release_firmware() right away
906 * then we might miss on an opportunity of having a successful firmware
907 * request pass on the opportunity to be come a batched request.
908 */
909
910out_bail:
911 for (i = 0; i < test_fw_config->num_requests; i++) {
912 req = &test_fw_config->reqs[i];
913 if (req->task || req->sent)
914 wait_for_completion(&req->completion);
915 }
916
917 /* Override any worker error if we had a general setup error */
918 if (rc < 0)
919 test_fw_config->test_result = rc;
920
921out_unlock:
922 mutex_unlock(&test_fw_mutex);
923
924 return rc;
925}
926static DEVICE_ATTR_WO(trigger_batched_requests);
927
928/*
929 * We wait for each callback to return with the lock held, no need to lock here
930 */
931static void trigger_batched_cb(const struct firmware *fw, void *context)
932{
933 struct test_batched_req *req = context;
934
935 if (!req) {
936 test_fw_config->test_result = -EINVAL;
937 return;
938 }
939
940 /* forces *some* batched requests to queue up */
941 if (!req->idx)
942 ssleep(2);
943
944 req->fw = fw;
945
946 /*
947 * Unfortunately the firmware API gives us nothing other than a null FW
948 * if the firmware was not found on async requests. Best we can do is
949 * just assume -ENOENT. A better API would pass the actual return
950 * value to the callback.
951 */
952 if (!fw && !test_fw_config->test_result)
953 test_fw_config->test_result = -ENOENT;
954
955 complete(&req->completion);
956}
957
958static
959ssize_t trigger_batched_requests_async_store(struct device *dev,
960 struct device_attribute *attr,
961 const char *buf, size_t count)
962{
963 struct test_batched_req *req;
964 bool send_uevent;
965 int rc;
966 u8 i;
967
968 mutex_lock(&test_fw_mutex);
969
Mirsad Goran Todorovac539c3872023-05-09 10:47:47 +0200970 if (test_fw_config->reqs) {
971 rc = -EBUSY;
972 goto out_bail;
973 }
974
Kees Cookfad953c2018-06-12 14:27:37 -0700975 test_fw_config->reqs =
976 vzalloc(array3_size(sizeof(struct test_batched_req),
977 test_fw_config->num_requests, 2));
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700978 if (!test_fw_config->reqs) {
979 rc = -ENOMEM;
980 goto out;
981 }
982
983 pr_info("batched loading '%s' custom fallback mechanism %u times\n",
984 test_fw_config->name, test_fw_config->num_requests);
985
986 send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG :
987 FW_ACTION_NOHOTPLUG;
988
989 for (i = 0; i < test_fw_config->num_requests; i++) {
990 req = &test_fw_config->reqs[i];
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700991 req->name = test_fw_config->name;
Mirsad Goran Todorovac21bb3cd2023-05-09 10:47:49 +0200992 req->fw_buf = NULL;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -0700993 req->fw = NULL;
994 req->idx = i;
995 init_completion(&req->completion);
996 rc = request_firmware_nowait(THIS_MODULE, send_uevent,
997 req->name,
998 dev, GFP_KERNEL, req,
999 trigger_batched_cb);
1000 if (rc) {
1001 pr_info("#%u: batched async load failed setup: %d\n",
1002 i, rc);
1003 req->rc = rc;
1004 goto out_bail;
1005 } else
1006 req->sent = true;
1007 }
1008
1009 rc = count;
1010
1011out_bail:
1012
1013 /*
1014 * We require an explicit release to enable more time and delay of
1015 * calling release_firmware() to improve our chances of forcing a
1016 * batched request. If we instead called release_firmware() right away
1017 * then we might miss on an opportunity of having a successful firmware
1018 * request pass on the opportunity to be come a batched request.
1019 */
1020
1021 for (i = 0; i < test_fw_config->num_requests; i++) {
1022 req = &test_fw_config->reqs[i];
1023 if (req->sent)
1024 wait_for_completion(&req->completion);
1025 }
1026
1027 /* Override any worker error if we had a general setup error */
1028 if (rc < 0)
1029 test_fw_config->test_result = rc;
1030
1031out:
1032 mutex_unlock(&test_fw_mutex);
1033
1034 return rc;
1035}
1036static DEVICE_ATTR_WO(trigger_batched_requests_async);
1037
1038static ssize_t test_result_show(struct device *dev,
1039 struct device_attribute *attr,
1040 char *buf)
1041{
1042 return test_dev_config_show_int(buf, test_fw_config->test_result);
1043}
1044static DEVICE_ATTR_RO(test_result);
1045
1046static ssize_t release_all_firmware_store(struct device *dev,
1047 struct device_attribute *attr,
1048 const char *buf, size_t count)
1049{
1050 test_release_all_firmware();
1051 return count;
1052}
1053static DEVICE_ATTR_WO(release_all_firmware);
1054
1055static ssize_t read_firmware_show(struct device *dev,
1056 struct device_attribute *attr,
1057 char *buf)
1058{
1059 struct test_batched_req *req;
1060 u8 idx;
1061 ssize_t rc = 0;
1062
1063 mutex_lock(&test_fw_mutex);
1064
1065 idx = test_fw_config->read_fw_idx;
1066 if (idx >= test_fw_config->num_requests) {
1067 rc = -ERANGE;
1068 goto out;
1069 }
1070
1071 if (!test_fw_config->reqs) {
1072 rc = -EINVAL;
1073 goto out;
1074 }
1075
1076 req = &test_fw_config->reqs[idx];
1077 if (!req->fw) {
1078 pr_err("#%u: failed to async load firmware\n", idx);
1079 rc = -ENOENT;
1080 goto out;
1081 }
1082
1083 pr_info("#%u: loaded %zu\n", idx, req->fw->size);
1084
1085 if (req->fw->size > PAGE_SIZE) {
1086 pr_err("Testing interface must use PAGE_SIZE firmware for now\n");
1087 rc = -EINVAL;
Colin Ian King8bb0a882018-10-19 13:58:01 +01001088 goto out;
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001089 }
1090 memcpy(buf, req->fw->data, req->fw->size);
1091
1092 rc = req->fw->size;
1093out:
1094 mutex_unlock(&test_fw_mutex);
1095
1096 return rc;
1097}
1098static DEVICE_ATTR_RO(read_firmware);
1099
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001100#define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr
1101
1102static struct attribute *test_dev_attrs[] = {
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001103 TEST_FW_DEV_ATTR(reset),
1104
1105 TEST_FW_DEV_ATTR(config),
1106 TEST_FW_DEV_ATTR(config_name),
1107 TEST_FW_DEV_ATTR(config_num_requests),
Scott Branden7feebfa2019-08-22 11:40:04 -07001108 TEST_FW_DEV_ATTR(config_into_buf),
Scott Branden5d90e052020-10-02 10:38:28 -07001109 TEST_FW_DEV_ATTR(config_buf_size),
1110 TEST_FW_DEV_ATTR(config_file_offset),
1111 TEST_FW_DEV_ATTR(config_partial),
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001112 TEST_FW_DEV_ATTR(config_sync_direct),
1113 TEST_FW_DEV_ATTR(config_send_uevent),
1114 TEST_FW_DEV_ATTR(config_read_fw_idx),
1115
1116 /* These don't use the config at all - they could be ported! */
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001117 TEST_FW_DEV_ATTR(trigger_request),
1118 TEST_FW_DEV_ATTR(trigger_async_request),
Luis R. Rodriguez061132d2017-01-23 08:11:10 -08001119 TEST_FW_DEV_ATTR(trigger_custom_fallback),
Hans de Goede548193c2020-01-15 17:35:49 +01001120#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1121 TEST_FW_DEV_ATTR(trigger_request_platform),
1122#endif
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001123
1124 /* These use the config and can use the test_result */
1125 TEST_FW_DEV_ATTR(trigger_batched_requests),
1126 TEST_FW_DEV_ATTR(trigger_batched_requests_async),
1127
1128 TEST_FW_DEV_ATTR(release_all_firmware),
1129 TEST_FW_DEV_ATTR(test_result),
1130 TEST_FW_DEV_ATTR(read_firmware),
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001131 NULL,
1132};
1133
1134ATTRIBUTE_GROUPS(test_dev);
1135
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001136static struct miscdevice test_fw_misc_device = {
1137 .minor = MISC_DYNAMIC_MINOR,
1138 .name = "test_firmware",
1139 .fops = &test_fw_fops,
Luis R. Rodriguez083a93b2017-01-23 08:11:06 -08001140 .groups = test_dev_groups,
Luis R. Rodriguez67fd5532017-01-23 08:11:05 -08001141};
1142
Kees Cook0a8adf52014-07-14 14:38:12 -07001143static int __init test_firmware_init(void)
1144{
1145 int rc;
1146
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001147 test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL);
1148 if (!test_fw_config)
1149 return -ENOMEM;
1150
1151 rc = __test_firmware_config_init();
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001152 if (rc) {
1153 kfree(test_fw_config);
1154 pr_err("could not init firmware test config: %d\n", rc);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001155 return rc;
Wenwen Wangd4fddac2019-07-14 01:11:35 -05001156 }
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001157
Kees Cook0a8adf52014-07-14 14:38:12 -07001158 rc = misc_register(&test_fw_misc_device);
1159 if (rc) {
Zhengchao Shao0b5a89e2022-11-19 11:57:21 +08001160 __test_firmware_config_free();
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001161 kfree(test_fw_config);
Kees Cook0a8adf52014-07-14 14:38:12 -07001162 pr_err("could not register misc device: %d\n", rc);
1163 return rc;
1164 }
Brian Norriseb910942015-12-09 14:50:27 -08001165
Kees Cook0a8adf52014-07-14 14:38:12 -07001166 pr_warn("interface ready\n");
1167
1168 return 0;
Kees Cook0a8adf52014-07-14 14:38:12 -07001169}
1170
1171module_init(test_firmware_init);
1172
1173static void __exit test_firmware_exit(void)
1174{
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001175 mutex_lock(&test_fw_mutex);
Kees Cook0a8adf52014-07-14 14:38:12 -07001176 release_firmware(test_firmware);
Kees Cook0a8adf52014-07-14 14:38:12 -07001177 misc_deregister(&test_fw_misc_device);
Luis R. Rodriguezc92316b2017-07-20 13:13:42 -07001178 __test_firmware_config_free();
1179 kfree(test_fw_config);
1180 mutex_unlock(&test_fw_mutex);
1181
Kees Cook0a8adf52014-07-14 14:38:12 -07001182 pr_warn("removed interface\n");
1183}
1184
1185module_exit(test_firmware_exit);
1186
1187MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
1188MODULE_LICENSE("GPL");