1 /* 2 * This module provides an interface to trigger and test firmware loading. 3 * 4 * It is designed to be used for basic evaluation of the firmware loading 5 * subsystem (for example when validating firmware verification). It lacks 6 * any extra dependencies, and will not normally be loaded by the system 7 * unless explicitly requested by name. 8 */ 9 10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/printk.h> 15 #include <linux/firmware.h> 16 #include <linux/device.h> 17 #include <linux/fs.h> 18 #include <linux/miscdevice.h> 19 #include <linux/slab.h> 20 #include <linux/uaccess.h> 21 22 static DEFINE_MUTEX(test_fw_mutex); 23 static const struct firmware *test_firmware; 24 25 static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 26 size_t size, loff_t *offset) 27 { 28 ssize_t rc = 0; 29 30 mutex_lock(&test_fw_mutex); 31 if (test_firmware) 32 rc = simple_read_from_buffer(buf, size, offset, 33 test_firmware->data, 34 test_firmware->size); 35 mutex_unlock(&test_fw_mutex); 36 return rc; 37 } 38 39 static const struct file_operations test_fw_fops = { 40 .owner = THIS_MODULE, 41 .read = test_fw_misc_read, 42 }; 43 44 static struct miscdevice test_fw_misc_device = { 45 .minor = MISC_DYNAMIC_MINOR, 46 .name = "test_firmware", 47 .fops = &test_fw_fops, 48 }; 49 50 static ssize_t trigger_request_store(struct device *dev, 51 struct device_attribute *attr, 52 const char *buf, size_t count) 53 { 54 int rc; 55 char *name; 56 57 name = kzalloc(count + 1, GFP_KERNEL); 58 if (!name) 59 return -ENOSPC; 60 memcpy(name, buf, count); 61 62 pr_info("loading '%s'\n", name); 63 64 mutex_lock(&test_fw_mutex); 65 release_firmware(test_firmware); 66 test_firmware = NULL; 67 rc = request_firmware(&test_firmware, name, dev); 68 if (rc) 69 pr_info("load of '%s' failed: %d\n", name, rc); 70 pr_info("loaded: %zu\n", test_firmware ? test_firmware->size : 0); 71 mutex_unlock(&test_fw_mutex); 72 73 kfree(name); 74 75 return count; 76 } 77 static DEVICE_ATTR_WO(trigger_request); 78 79 static int __init test_firmware_init(void) 80 { 81 int rc; 82 83 rc = misc_register(&test_fw_misc_device); 84 if (rc) { 85 pr_err("could not register misc device: %d\n", rc); 86 return rc; 87 } 88 rc = device_create_file(test_fw_misc_device.this_device, 89 &dev_attr_trigger_request); 90 if (rc) { 91 pr_err("could not create sysfs interface: %d\n", rc); 92 goto dereg; 93 } 94 95 pr_warn("interface ready\n"); 96 97 return 0; 98 dereg: 99 misc_deregister(&test_fw_misc_device); 100 return rc; 101 } 102 103 module_init(test_firmware_init); 104 105 static void __exit test_firmware_exit(void) 106 { 107 release_firmware(test_firmware); 108 device_remove_file(test_fw_misc_device.this_device, 109 &dev_attr_trigger_request); 110 misc_deregister(&test_fw_misc_device); 111 pr_warn("removed interface\n"); 112 } 113 114 module_exit(test_firmware_exit); 115 116 MODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 117 MODULE_LICENSE("GPL"); 118