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/completion.h> 16 #include <linux/firmware.h> 17 #include <linux/device.h> 18 #include <linux/fs.h> 19 #include <linux/miscdevice.h> 20 #include <linux/slab.h> 21 #include <linux/uaccess.h> 22 23 static DEFINE_MUTEX(test_fw_mutex); 24 static const struct firmware *test_firmware; 25 26 static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 27 size_t size, loff_t *offset) 28 { 29 ssize_t rc = 0; 30 31 mutex_lock(&test_fw_mutex); 32 if (test_firmware) 33 rc = simple_read_from_buffer(buf, size, offset, 34 test_firmware->data, 35 test_firmware->size); 36 mutex_unlock(&test_fw_mutex); 37 return rc; 38 } 39 40 static const struct file_operations test_fw_fops = { 41 .owner = THIS_MODULE, 42 .read = test_fw_misc_read, 43 }; 44 45 static struct miscdevice test_fw_misc_device = { 46 .minor = MISC_DYNAMIC_MINOR, 47 .name = "test_firmware", 48 .fops = &test_fw_fops, 49 }; 50 51 static ssize_t trigger_request_store(struct device *dev, 52 struct device_attribute *attr, 53 const char *buf, size_t count) 54 { 55 int rc; 56 char *name; 57 58 name = kstrndup(buf, count, GFP_KERNEL); 59 if (!name) 60 return -ENOSPC; 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 goto out; 71 } 72 pr_info("loaded: %zu\n", test_firmware->size); 73 rc = count; 74 75 out: 76 mutex_unlock(&test_fw_mutex); 77 78 kfree(name); 79 80 return rc; 81 } 82 static DEVICE_ATTR_WO(trigger_request); 83 84 static DECLARE_COMPLETION(async_fw_done); 85 86 static void trigger_async_request_cb(const struct firmware *fw, void *context) 87 { 88 test_firmware = fw; 89 complete(&async_fw_done); 90 } 91 92 static ssize_t trigger_async_request_store(struct device *dev, 93 struct device_attribute *attr, 94 const char *buf, size_t count) 95 { 96 int rc; 97 char *name; 98 99 name = kstrndup(buf, count, GFP_KERNEL); 100 if (!name) 101 return -ENOSPC; 102 103 pr_info("loading '%s'\n", name); 104 105 mutex_lock(&test_fw_mutex); 106 release_firmware(test_firmware); 107 test_firmware = NULL; 108 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 109 NULL, trigger_async_request_cb); 110 if (rc) { 111 pr_info("async load of '%s' failed: %d\n", name, rc); 112 kfree(name); 113 goto out; 114 } 115 /* Free 'name' ASAP, to test for race conditions */ 116 kfree(name); 117 118 wait_for_completion(&async_fw_done); 119 120 if (test_firmware) { 121 pr_info("loaded: %zu\n", test_firmware->size); 122 rc = count; 123 } else { 124 pr_err("failed to async load firmware\n"); 125 rc = -ENODEV; 126 } 127 128 out: 129 mutex_unlock(&test_fw_mutex); 130 131 return rc; 132 } 133 static DEVICE_ATTR_WO(trigger_async_request); 134 135 static int __init test_firmware_init(void) 136 { 137 int rc; 138 139 rc = misc_register(&test_fw_misc_device); 140 if (rc) { 141 pr_err("could not register misc device: %d\n", rc); 142 return rc; 143 } 144 rc = device_create_file(test_fw_misc_device.this_device, 145 &dev_attr_trigger_request); 146 if (rc) { 147 pr_err("could not create sysfs interface: %d\n", rc); 148 goto dereg; 149 } 150 151 rc = device_create_file(test_fw_misc_device.this_device, 152 &dev_attr_trigger_async_request); 153 if (rc) { 154 pr_err("could not create async sysfs interface: %d\n", rc); 155 goto remove_file; 156 } 157 158 pr_warn("interface ready\n"); 159 160 return 0; 161 162 remove_file: 163 device_remove_file(test_fw_misc_device.this_device, 164 &dev_attr_trigger_async_request); 165 dereg: 166 misc_deregister(&test_fw_misc_device); 167 return rc; 168 } 169 170 module_init(test_firmware_init); 171 172 static void __exit test_firmware_exit(void) 173 { 174 release_firmware(test_firmware); 175 device_remove_file(test_fw_misc_device.this_device, 176 &dev_attr_trigger_async_request); 177 device_remove_file(test_fw_misc_device.this_device, 178 &dev_attr_trigger_request); 179 misc_deregister(&test_fw_misc_device); 180 pr_warn("removed interface\n"); 181 } 182 183 module_exit(test_firmware_exit); 184 185 MODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 186 MODULE_LICENSE("GPL"); 187