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 ssize_t trigger_request_store(struct device *dev, 46 struct device_attribute *attr, 47 const char *buf, size_t count) 48 { 49 int rc; 50 char *name; 51 52 name = kstrndup(buf, count, GFP_KERNEL); 53 if (!name) 54 return -ENOSPC; 55 56 pr_info("loading '%s'\n", name); 57 58 mutex_lock(&test_fw_mutex); 59 release_firmware(test_firmware); 60 test_firmware = NULL; 61 rc = request_firmware(&test_firmware, name, dev); 62 if (rc) { 63 pr_info("load of '%s' failed: %d\n", name, rc); 64 goto out; 65 } 66 pr_info("loaded: %zu\n", test_firmware->size); 67 rc = count; 68 69 out: 70 mutex_unlock(&test_fw_mutex); 71 72 kfree(name); 73 74 return rc; 75 } 76 static DEVICE_ATTR_WO(trigger_request); 77 78 static DECLARE_COMPLETION(async_fw_done); 79 80 static void trigger_async_request_cb(const struct firmware *fw, void *context) 81 { 82 test_firmware = fw; 83 complete(&async_fw_done); 84 } 85 86 static ssize_t trigger_async_request_store(struct device *dev, 87 struct device_attribute *attr, 88 const char *buf, size_t count) 89 { 90 int rc; 91 char *name; 92 93 name = kstrndup(buf, count, GFP_KERNEL); 94 if (!name) 95 return -ENOSPC; 96 97 pr_info("loading '%s'\n", name); 98 99 mutex_lock(&test_fw_mutex); 100 release_firmware(test_firmware); 101 test_firmware = NULL; 102 rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 103 NULL, trigger_async_request_cb); 104 if (rc) { 105 pr_info("async load of '%s' failed: %d\n", name, rc); 106 kfree(name); 107 goto out; 108 } 109 /* Free 'name' ASAP, to test for race conditions */ 110 kfree(name); 111 112 wait_for_completion(&async_fw_done); 113 114 if (test_firmware) { 115 pr_info("loaded: %zu\n", test_firmware->size); 116 rc = count; 117 } else { 118 pr_err("failed to async load firmware\n"); 119 rc = -ENODEV; 120 } 121 122 out: 123 mutex_unlock(&test_fw_mutex); 124 125 return rc; 126 } 127 static DEVICE_ATTR_WO(trigger_async_request); 128 129 static ssize_t trigger_custom_fallback_store(struct device *dev, 130 struct device_attribute *attr, 131 const char *buf, size_t count) 132 { 133 int rc; 134 char *name; 135 136 name = kstrndup(buf, count, GFP_KERNEL); 137 if (!name) 138 return -ENOSPC; 139 140 pr_info("loading '%s' using custom fallback mechanism\n", name); 141 142 mutex_lock(&test_fw_mutex); 143 release_firmware(test_firmware); 144 test_firmware = NULL; 145 rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name, 146 dev, GFP_KERNEL, NULL, 147 trigger_async_request_cb); 148 if (rc) { 149 pr_info("async load of '%s' failed: %d\n", name, rc); 150 kfree(name); 151 goto out; 152 } 153 /* Free 'name' ASAP, to test for race conditions */ 154 kfree(name); 155 156 wait_for_completion(&async_fw_done); 157 158 if (test_firmware) { 159 pr_info("loaded: %zu\n", test_firmware->size); 160 rc = count; 161 } else { 162 pr_err("failed to async load firmware\n"); 163 rc = -ENODEV; 164 } 165 166 out: 167 mutex_unlock(&test_fw_mutex); 168 169 return rc; 170 } 171 static DEVICE_ATTR_WO(trigger_custom_fallback); 172 173 #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr 174 175 static struct attribute *test_dev_attrs[] = { 176 TEST_FW_DEV_ATTR(trigger_request), 177 TEST_FW_DEV_ATTR(trigger_async_request), 178 TEST_FW_DEV_ATTR(trigger_custom_fallback), 179 NULL, 180 }; 181 182 ATTRIBUTE_GROUPS(test_dev); 183 184 static struct miscdevice test_fw_misc_device = { 185 .minor = MISC_DYNAMIC_MINOR, 186 .name = "test_firmware", 187 .fops = &test_fw_fops, 188 .groups = test_dev_groups, 189 }; 190 191 static int __init test_firmware_init(void) 192 { 193 int rc; 194 195 rc = misc_register(&test_fw_misc_device); 196 if (rc) { 197 pr_err("could not register misc device: %d\n", rc); 198 return rc; 199 } 200 201 pr_warn("interface ready\n"); 202 203 return 0; 204 } 205 206 module_init(test_firmware_init); 207 208 static void __exit test_firmware_exit(void) 209 { 210 release_firmware(test_firmware); 211 misc_deregister(&test_fw_misc_device); 212 pr_warn("removed interface\n"); 213 } 214 215 module_exit(test_firmware_exit); 216 217 MODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 218 MODULE_LICENSE("GPL"); 219