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 22static DEFINE_MUTEX(test_fw_mutex); 23static const struct firmware *test_firmware; 24 25static 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 39static const struct file_operations test_fw_fops = { 40 .owner = THIS_MODULE, 41 .read = test_fw_misc_read, 42}; 43 44static struct miscdevice test_fw_misc_device = { 45 .minor = MISC_DYNAMIC_MINOR, 46 .name = "test_firmware", 47 .fops = &test_fw_fops, 48}; 49 50static 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} 77static DEVICE_ATTR_WO(trigger_request); 78 79static 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; 98dereg: 99 misc_deregister(&test_fw_misc_device); 100 return rc; 101} 102 103module_init(test_firmware_init); 104 105static 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 114module_exit(test_firmware_exit); 115 116MODULE_AUTHOR("Kees Cook <keescook@chromium.org>"); 117MODULE_LICENSE("GPL"); 118