hc
2024-10-22 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5
kernel/lib/test_firmware.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * This module provides an interface to trigger and test firmware loading.
34 *
....@@ -17,14 +18,20 @@
1718 #include <linux/device.h>
1819 #include <linux/fs.h>
1920 #include <linux/miscdevice.h>
21
+#include <linux/sizes.h>
2022 #include <linux/slab.h>
2123 #include <linux/uaccess.h>
2224 #include <linux/delay.h>
25
+#include <linux/kstrtox.h>
2326 #include <linux/kthread.h>
2427 #include <linux/vmalloc.h>
28
+#include <linux/efi_embedded_fw.h>
29
+
30
+MODULE_IMPORT_NS(TEST_FIRMWARE);
2531
2632 #define TEST_FIRMWARE_NAME "test-firmware.bin"
2733 #define TEST_FIRMWARE_NUM_REQS 4
34
+#define TEST_FIRMWARE_BUF_SIZE SZ_1K
2835
2936 static DEFINE_MUTEX(test_fw_mutex);
3037 static const struct firmware *test_firmware;
....@@ -35,6 +42,7 @@
3542 bool sent;
3643 const struct firmware *fw;
3744 const char *name;
45
+ const char *fw_buf;
3846 struct completion completion;
3947 struct task_struct *task;
4048 struct device *dev;
....@@ -44,6 +52,11 @@
4452 * test_config - represents configuration for the test for different triggers
4553 *
4654 * @name: the name of the firmware file to look for
55
+ * @into_buf: when the into_buf is used if this is true
56
+ * request_firmware_into_buf() will be used instead.
57
+ * @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
4760 * @sync_direct: when the sync trigger is used if this is true
4861 * request_firmware_direct() will be used instead.
4962 * @send_uevent: whether or not to send a uevent for async requests
....@@ -82,6 +95,10 @@
8295 */
8396 struct test_config {
8497 char *name;
98
+ bool into_buf;
99
+ size_t buf_size;
100
+ size_t file_offset;
101
+ bool partial;
85102 bool sync_direct;
86103 bool send_uevent;
87104 u8 num_requests;
....@@ -128,8 +145,14 @@
128145
129146 for (i = 0; i < test_fw_config->num_requests; i++) {
130147 req = &test_fw_config->reqs[i];
131
- if (req->fw)
148
+ if (req->fw) {
149
+ if (req->fw_buf) {
150
+ kfree_const(req->fw_buf);
151
+ req->fw_buf = NULL;
152
+ }
132153 release_firmware(req->fw);
154
+ req->fw = NULL;
155
+ }
133156 }
134157
135158 vfree(test_fw_config->reqs);
....@@ -160,7 +183,7 @@
160183 {
161184 *dst = kstrndup(name, count, gfp);
162185 if (!*dst)
163
- return -ENOSPC;
186
+ return -ENOMEM;
164187 return count;
165188 }
166189
....@@ -175,6 +198,10 @@
175198
176199 test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS;
177200 test_fw_config->send_uevent = true;
201
+ test_fw_config->into_buf = false;
202
+ test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE;
203
+ test_fw_config->file_offset = 0;
204
+ test_fw_config->partial = false;
178205 test_fw_config->sync_direct = false;
179206 test_fw_config->req_firmware = request_firmware;
180207 test_fw_config->test_result = 0;
....@@ -228,25 +255,35 @@
228255 dev_name(dev));
229256
230257 if (test_fw_config->name)
231
- len += scnprintf(buf+len, PAGE_SIZE - len,
258
+ len += scnprintf(buf + len, PAGE_SIZE - len,
232259 "name:\t%s\n",
233260 test_fw_config->name);
234261 else
235
- len += scnprintf(buf+len, PAGE_SIZE - len,
262
+ len += scnprintf(buf + len, PAGE_SIZE - len,
236263 "name:\tEMTPY\n");
237264
238
- len += scnprintf(buf+len, PAGE_SIZE - len,
265
+ len += scnprintf(buf + len, PAGE_SIZE - len,
239266 "num_requests:\t%u\n", test_fw_config->num_requests);
240267
241
- len += scnprintf(buf+len, PAGE_SIZE - len,
268
+ len += scnprintf(buf + len, PAGE_SIZE - len,
242269 "send_uevent:\t\t%s\n",
243270 test_fw_config->send_uevent ?
244271 "FW_ACTION_HOTPLUG" :
245272 "FW_ACTION_NOHOTPLUG");
246
- len += scnprintf(buf+len, PAGE_SIZE - len,
273
+ len += scnprintf(buf + len, PAGE_SIZE - len,
274
+ "into_buf:\t\t%s\n",
275
+ test_fw_config->into_buf ? "true" : "false");
276
+ 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,
247284 "sync_direct:\t\t%s\n",
248285 test_fw_config->sync_direct ? "true" : "false");
249
- len += scnprintf(buf+len, PAGE_SIZE - len,
286
+ len += scnprintf(buf + len, PAGE_SIZE - len,
250287 "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx);
251288
252289 mutex_unlock(&test_fw_mutex);
....@@ -284,46 +321,40 @@
284321 return len;
285322 }
286323
324
+static 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
+
287337 static int test_dev_config_update_bool(const char *buf, size_t size,
288338 bool *cfg)
289339 {
290340 int ret;
291341
292342 mutex_lock(&test_fw_mutex);
293
- if (strtobool(buf, cfg) < 0)
294
- ret = -EINVAL;
295
- else
296
- ret = size;
343
+ ret = __test_dev_config_update_bool(buf, size, cfg);
297344 mutex_unlock(&test_fw_mutex);
298345
299346 return ret;
300347 }
301348
302
-static ssize_t
303
-test_dev_config_show_bool(char *buf,
304
- bool config)
349
+static ssize_t test_dev_config_show_bool(char *buf, bool val)
305350 {
306
- bool val;
307
-
308
- mutex_lock(&test_fw_mutex);
309
- val = config;
310
- mutex_unlock(&test_fw_mutex);
311
-
312351 return snprintf(buf, PAGE_SIZE, "%d\n", val);
313352 }
314353
315
-static ssize_t test_dev_config_show_int(char *buf, int cfg)
316
-{
317
- int val;
318
-
319
- mutex_lock(&test_fw_mutex);
320
- val = cfg;
321
- mutex_unlock(&test_fw_mutex);
322
-
323
- return snprintf(buf, PAGE_SIZE, "%d\n", val);
324
-}
325
-
326
-static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
354
+static int __test_dev_config_update_size_t(
355
+ const char *buf,
356
+ size_t size,
357
+ size_t *cfg)
327358 {
328359 int ret;
329360 long new;
....@@ -332,25 +363,50 @@
332363 if (ret)
333364 return ret;
334365
335
- if (new > U8_MAX)
336
- return -EINVAL;
337
-
338
- mutex_lock(&test_fw_mutex);
339
- *(u8 *)cfg = new;
340
- mutex_unlock(&test_fw_mutex);
366
+ *(size_t *)cfg = new;
341367
342368 /* Always return full write size even if we didn't consume all */
343369 return size;
344370 }
345371
346
-static ssize_t test_dev_config_show_u8(char *buf, u8 cfg)
372
+static 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
+
377
+static ssize_t test_dev_config_show_int(char *buf, int val)
378
+{
379
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
380
+}
381
+
382
+static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
347383 {
348384 u8 val;
385
+ int ret;
386
+
387
+ ret = kstrtou8(buf, 10, &val);
388
+ if (ret)
389
+ return ret;
390
+
391
+ *(u8 *)cfg = val;
392
+
393
+ /* Always return full write size even if we didn't consume all */
394
+ return size;
395
+}
396
+
397
+static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg)
398
+{
399
+ int ret;
349400
350401 mutex_lock(&test_fw_mutex);
351
- val = cfg;
402
+ ret = __test_dev_config_update_u8(buf, size, cfg);
352403 mutex_unlock(&test_fw_mutex);
353404
405
+ return ret;
406
+}
407
+
408
+static ssize_t test_dev_config_show_u8(char *buf, u8 val)
409
+{
354410 return snprintf(buf, PAGE_SIZE, "%u\n", val);
355411 }
356412
....@@ -375,10 +431,10 @@
375431 mutex_unlock(&test_fw_mutex);
376432 goto out;
377433 }
378
- mutex_unlock(&test_fw_mutex);
379434
380
- rc = test_dev_config_update_u8(buf, count,
381
- &test_fw_config->num_requests);
435
+ rc = __test_dev_config_update_u8(buf, count,
436
+ &test_fw_config->num_requests);
437
+ mutex_unlock(&test_fw_mutex);
382438
383439 out:
384440 return rc;
....@@ -391,6 +447,100 @@
391447 return test_dev_config_show_u8(buf, test_fw_config->num_requests);
392448 }
393449 static DEVICE_ATTR_RW(config_num_requests);
450
+
451
+static 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
+
460
+static 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
+}
466
+static DEVICE_ATTR_RW(config_into_buf);
467
+
468
+static 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
+ }
481
+
482
+ rc = __test_dev_config_update_size_t(buf, count,
483
+ &test_fw_config->buf_size);
484
+ mutex_unlock(&test_fw_mutex);
485
+
486
+out:
487
+ return rc;
488
+}
489
+
490
+static 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
+}
496
+static DEVICE_ATTR_RW(config_buf_size);
497
+
498
+static 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
+ }
511
+
512
+ rc = __test_dev_config_update_size_t(buf, count,
513
+ &test_fw_config->file_offset);
514
+ mutex_unlock(&test_fw_mutex);
515
+
516
+out:
517
+ return rc;
518
+}
519
+
520
+static 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
+}
526
+static DEVICE_ATTR_RW(config_file_offset);
527
+
528
+static 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
+
537
+static 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
+}
543
+static DEVICE_ATTR_RW(config_partial);
394544
395545 static ssize_t config_sync_direct_store(struct device *dev,
396546 struct device_attribute *attr,
....@@ -456,12 +606,14 @@
456606
457607 name = kstrndup(buf, count, GFP_KERNEL);
458608 if (!name)
459
- return -ENOSPC;
609
+ return -ENOMEM;
460610
461611 pr_info("loading '%s'\n", name);
462612
463613 mutex_lock(&test_fw_mutex);
464614 release_firmware(test_firmware);
615
+ if (test_fw_config->reqs)
616
+ __test_release_all_firmware();
465617 test_firmware = NULL;
466618 rc = request_firmware(&test_firmware, name, dev);
467619 if (rc) {
....@@ -480,6 +632,64 @@
480632 }
481633 static DEVICE_ATTR_WO(trigger_request);
482634
635
+#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
636
+extern struct list_head efi_embedded_fw_list;
637
+extern bool efi_embedded_fw_checked;
638
+
639
+static 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;
651
+ bool saved_efi_embedded_fw_checked;
652
+ char *name;
653
+ int rc;
654
+
655
+ name = kstrndup(buf, count, GFP_KERNEL);
656
+ if (!name)
657
+ return -ENOMEM;
658
+
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);
664
+ saved_efi_embedded_fw_checked = efi_embedded_fw_checked;
665
+ efi_embedded_fw_checked = true;
666
+
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
+
682
+out:
683
+ efi_embedded_fw_checked = saved_efi_embedded_fw_checked;
684
+ release_firmware(firmware);
685
+ list_del(&efi_embedded_fw.list);
686
+ kfree(name);
687
+
688
+ return rc;
689
+}
690
+static DEVICE_ATTR_WO(trigger_request_platform);
691
+#endif
692
+
483693 static DECLARE_COMPLETION(async_fw_done);
484694
485695 static void trigger_async_request_cb(const struct firmware *fw, void *context)
....@@ -497,13 +707,15 @@
497707
498708 name = kstrndup(buf, count, GFP_KERNEL);
499709 if (!name)
500
- return -ENOSPC;
710
+ return -ENOMEM;
501711
502712 pr_info("loading '%s'\n", name);
503713
504714 mutex_lock(&test_fw_mutex);
505715 release_firmware(test_firmware);
506716 test_firmware = NULL;
717
+ if (test_fw_config->reqs)
718
+ __test_release_all_firmware();
507719 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
508720 NULL, trigger_async_request_cb);
509721 if (rc) {
....@@ -521,7 +733,7 @@
521733 rc = count;
522734 } else {
523735 pr_err("failed to async load firmware\n");
524
- rc = -ENODEV;
736
+ rc = -ENOMEM;
525737 }
526738
527739 out:
....@@ -540,12 +752,14 @@
540752
541753 name = kstrndup(buf, count, GFP_KERNEL);
542754 if (!name)
543
- return -ENOSPC;
755
+ return -ENOMEM;
544756
545757 pr_info("loading '%s' using custom fallback mechanism\n", name);
546758
547759 mutex_lock(&test_fw_mutex);
548760 release_firmware(test_firmware);
761
+ if (test_fw_config->reqs)
762
+ __test_release_all_firmware();
549763 test_firmware = NULL;
550764 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name,
551765 dev, GFP_KERNEL, NULL,
....@@ -584,7 +798,38 @@
584798 return -EINVAL;
585799 }
586800
587
- req->rc = test_fw_config->req_firmware(&req->fw, req->name, req->dev);
801
+ 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)
806
+ return -ENOMEM;
807
+
808
+ 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);
823
+ if (!req->fw)
824
+ kfree(test_buf);
825
+ else
826
+ req->fw_buf = test_buf;
827
+ } else {
828
+ req->rc = test_fw_config->req_firmware(&req->fw,
829
+ req->name,
830
+ req->dev);
831
+ }
832
+
588833 if (req->rc) {
589834 pr_info("#%u: batched sync load failed: %d\n",
590835 req->idx, req->rc);
....@@ -618,6 +863,11 @@
618863
619864 mutex_lock(&test_fw_mutex);
620865
866
+ if (test_fw_config->reqs) {
867
+ rc = -EBUSY;
868
+ goto out_bail;
869
+ }
870
+
621871 test_fw_config->reqs =
622872 vzalloc(array3_size(sizeof(struct test_batched_req),
623873 test_fw_config->num_requests, 2));
....@@ -631,14 +881,10 @@
631881
632882 for (i = 0; i < test_fw_config->num_requests; i++) {
633883 req = &test_fw_config->reqs[i];
634
- if (!req) {
635
- WARN_ON(1);
636
- rc = -ENOMEM;
637
- goto out_bail;
638
- }
639884 req->fw = NULL;
640885 req->idx = i;
641886 req->name = test_fw_config->name;
887
+ req->fw_buf = NULL;
642888 req->dev = dev;
643889 init_completion(&req->completion);
644890 req->task = kthread_run(test_fw_run_batch_request, req,
....@@ -721,6 +967,11 @@
721967
722968 mutex_lock(&test_fw_mutex);
723969
970
+ if (test_fw_config->reqs) {
971
+ rc = -EBUSY;
972
+ goto out_bail;
973
+ }
974
+
724975 test_fw_config->reqs =
725976 vzalloc(array3_size(sizeof(struct test_batched_req),
726977 test_fw_config->num_requests, 2));
....@@ -737,11 +988,8 @@
737988
738989 for (i = 0; i < test_fw_config->num_requests; i++) {
739990 req = &test_fw_config->reqs[i];
740
- if (!req) {
741
- WARN_ON(1);
742
- goto out_bail;
743
- }
744991 req->name = test_fw_config->name;
992
+ req->fw_buf = NULL;
745993 req->fw = NULL;
746994 req->idx = i;
747995 init_completion(&req->completion);
....@@ -857,6 +1105,10 @@
8571105 TEST_FW_DEV_ATTR(config),
8581106 TEST_FW_DEV_ATTR(config_name),
8591107 TEST_FW_DEV_ATTR(config_num_requests),
1108
+ TEST_FW_DEV_ATTR(config_into_buf),
1109
+ TEST_FW_DEV_ATTR(config_buf_size),
1110
+ TEST_FW_DEV_ATTR(config_file_offset),
1111
+ TEST_FW_DEV_ATTR(config_partial),
8601112 TEST_FW_DEV_ATTR(config_sync_direct),
8611113 TEST_FW_DEV_ATTR(config_send_uevent),
8621114 TEST_FW_DEV_ATTR(config_read_fw_idx),
....@@ -865,6 +1117,9 @@
8651117 TEST_FW_DEV_ATTR(trigger_request),
8661118 TEST_FW_DEV_ATTR(trigger_async_request),
8671119 TEST_FW_DEV_ATTR(trigger_custom_fallback),
1120
+#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
1121
+ TEST_FW_DEV_ATTR(trigger_request_platform),
1122
+#endif
8681123
8691124 /* These use the config and can use the test_result */
8701125 TEST_FW_DEV_ATTR(trigger_batched_requests),
....@@ -902,6 +1157,7 @@
9021157
9031158 rc = misc_register(&test_fw_misc_device);
9041159 if (rc) {
1160
+ __test_firmware_config_free();
9051161 kfree(test_fw_config);
9061162 pr_err("could not register misc device: %d\n", rc);
9071163 return rc;