Back to home page

LXR

 
 

    


0001 /*
0002  * This module provides an interface to trigger and test firmware loading.
0003  *
0004  * It is designed to be used for basic evaluation of the firmware loading
0005  * subsystem (for example when validating firmware verification). It lacks
0006  * any extra dependencies, and will not normally be loaded by the system
0007  * unless explicitly requested by name.
0008  */
0009 
0010 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0011 
0012 #include <linux/init.h>
0013 #include <linux/module.h>
0014 #include <linux/printk.h>
0015 #include <linux/completion.h>
0016 #include <linux/firmware.h>
0017 #include <linux/device.h>
0018 #include <linux/fs.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/slab.h>
0021 #include <linux/uaccess.h>
0022 
0023 static DEFINE_MUTEX(test_fw_mutex);
0024 static const struct firmware *test_firmware;
0025 
0026 static ssize_t test_fw_misc_read(struct file *f, char __user *buf,
0027                  size_t size, loff_t *offset)
0028 {
0029     ssize_t rc = 0;
0030 
0031     mutex_lock(&test_fw_mutex);
0032     if (test_firmware)
0033         rc = simple_read_from_buffer(buf, size, offset,
0034                          test_firmware->data,
0035                          test_firmware->size);
0036     mutex_unlock(&test_fw_mutex);
0037     return rc;
0038 }
0039 
0040 static const struct file_operations test_fw_fops = {
0041     .owner          = THIS_MODULE,
0042     .read           = test_fw_misc_read,
0043 };
0044 
0045 static struct miscdevice test_fw_misc_device = {
0046     .minor          = MISC_DYNAMIC_MINOR,
0047     .name           = "test_firmware",
0048     .fops           = &test_fw_fops,
0049 };
0050 
0051 static ssize_t trigger_request_store(struct device *dev,
0052                      struct device_attribute *attr,
0053                      const char *buf, size_t count)
0054 {
0055     int rc;
0056     char *name;
0057 
0058     name = kstrndup(buf, count, GFP_KERNEL);
0059     if (!name)
0060         return -ENOSPC;
0061 
0062     pr_info("loading '%s'\n", name);
0063 
0064     mutex_lock(&test_fw_mutex);
0065     release_firmware(test_firmware);
0066     test_firmware = NULL;
0067     rc = request_firmware(&test_firmware, name, dev);
0068     if (rc) {
0069         pr_info("load of '%s' failed: %d\n", name, rc);
0070         goto out;
0071     }
0072     pr_info("loaded: %zu\n", test_firmware->size);
0073     rc = count;
0074 
0075 out:
0076     mutex_unlock(&test_fw_mutex);
0077 
0078     kfree(name);
0079 
0080     return rc;
0081 }
0082 static DEVICE_ATTR_WO(trigger_request);
0083 
0084 static DECLARE_COMPLETION(async_fw_done);
0085 
0086 static void trigger_async_request_cb(const struct firmware *fw, void *context)
0087 {
0088     test_firmware = fw;
0089     complete(&async_fw_done);
0090 }
0091 
0092 static ssize_t trigger_async_request_store(struct device *dev,
0093                        struct device_attribute *attr,
0094                        const char *buf, size_t count)
0095 {
0096     int rc;
0097     char *name;
0098 
0099     name = kstrndup(buf, count, GFP_KERNEL);
0100     if (!name)
0101         return -ENOSPC;
0102 
0103     pr_info("loading '%s'\n", name);
0104 
0105     mutex_lock(&test_fw_mutex);
0106     release_firmware(test_firmware);
0107     test_firmware = NULL;
0108     rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL,
0109                      NULL, trigger_async_request_cb);
0110     if (rc) {
0111         pr_info("async load of '%s' failed: %d\n", name, rc);
0112         kfree(name);
0113         goto out;
0114     }
0115     /* Free 'name' ASAP, to test for race conditions */
0116     kfree(name);
0117 
0118     wait_for_completion(&async_fw_done);
0119 
0120     if (test_firmware) {
0121         pr_info("loaded: %zu\n", test_firmware->size);
0122         rc = count;
0123     } else {
0124         pr_err("failed to async load firmware\n");
0125         rc = -ENODEV;
0126     }
0127 
0128 out:
0129     mutex_unlock(&test_fw_mutex);
0130 
0131     return rc;
0132 }
0133 static DEVICE_ATTR_WO(trigger_async_request);
0134 
0135 static int __init test_firmware_init(void)
0136 {
0137     int rc;
0138 
0139     rc = misc_register(&test_fw_misc_device);
0140     if (rc) {
0141         pr_err("could not register misc device: %d\n", rc);
0142         return rc;
0143     }
0144     rc = device_create_file(test_fw_misc_device.this_device,
0145                 &dev_attr_trigger_request);
0146     if (rc) {
0147         pr_err("could not create sysfs interface: %d\n", rc);
0148         goto dereg;
0149     }
0150 
0151     rc = device_create_file(test_fw_misc_device.this_device,
0152                 &dev_attr_trigger_async_request);
0153     if (rc) {
0154         pr_err("could not create async sysfs interface: %d\n", rc);
0155         goto remove_file;
0156     }
0157 
0158     pr_warn("interface ready\n");
0159 
0160     return 0;
0161 
0162 remove_file:
0163     device_remove_file(test_fw_misc_device.this_device,
0164                &dev_attr_trigger_async_request);
0165 dereg:
0166     misc_deregister(&test_fw_misc_device);
0167     return rc;
0168 }
0169 
0170 module_init(test_firmware_init);
0171 
0172 static void __exit test_firmware_exit(void)
0173 {
0174     release_firmware(test_firmware);
0175     device_remove_file(test_fw_misc_device.this_device,
0176                &dev_attr_trigger_async_request);
0177     device_remove_file(test_fw_misc_device.this_device,
0178                &dev_attr_trigger_request);
0179     misc_deregister(&test_fw_misc_device);
0180     pr_warn("removed interface\n");
0181 }
0182 
0183 module_exit(test_firmware_exit);
0184 
0185 MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
0186 MODULE_LICENSE("GPL");