109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20a8adf58SKees Cook /* 30a8adf58SKees Cook * This module provides an interface to trigger and test firmware loading. 40a8adf58SKees Cook * 50a8adf58SKees Cook * It is designed to be used for basic evaluation of the firmware loading 60a8adf58SKees Cook * subsystem (for example when validating firmware verification). It lacks 70a8adf58SKees Cook * any extra dependencies, and will not normally be loaded by the system 80a8adf58SKees Cook * unless explicitly requested by name. 90a8adf58SKees Cook */ 100a8adf58SKees Cook 110a8adf58SKees Cook #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 120a8adf58SKees Cook 130a8adf58SKees Cook #include <linux/init.h> 140a8adf58SKees Cook #include <linux/module.h> 150a8adf58SKees Cook #include <linux/printk.h> 16eb910947SBrian Norris #include <linux/completion.h> 170a8adf58SKees Cook #include <linux/firmware.h> 180a8adf58SKees Cook #include <linux/device.h> 190a8adf58SKees Cook #include <linux/fs.h> 200a8adf58SKees Cook #include <linux/miscdevice.h> 217feebfa4SScott Branden #include <linux/sizes.h> 220a8adf58SKees Cook #include <linux/slab.h> 230a8adf58SKees Cook #include <linux/uaccess.h> 24c92316bfSLuis R. Rodriguez #include <linux/delay.h> 25f7d85515SChristophe JAILLET #include <linux/kstrtox.h> 26c92316bfSLuis R. Rodriguez #include <linux/kthread.h> 27514c6032SRandy Dunlap #include <linux/vmalloc.h> 28548193cbSHans de Goede #include <linux/efi_embedded_fw.h> 29c92316bfSLuis R. Rodriguez 30baaabecfSKees Cook MODULE_IMPORT_NS(TEST_FIRMWARE); 31baaabecfSKees Cook 32c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NAME "test-firmware.bin" 33c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NUM_REQS 4 347feebfa4SScott Branden #define TEST_FIRMWARE_BUF_SIZE SZ_1K 35a31ad463SRuss Weight #define TEST_UPLOAD_MAX_SIZE SZ_2K 36a31ad463SRuss Weight #define TEST_UPLOAD_BLK_SIZE 37 /* Avoid powers of two in testing */ 370a8adf58SKees Cook 380a8adf58SKees Cook static DEFINE_MUTEX(test_fw_mutex); 390a8adf58SKees Cook static const struct firmware *test_firmware; 40a31ad463SRuss Weight static LIST_HEAD(test_upload_list); 410a8adf58SKees Cook 42c92316bfSLuis R. Rodriguez struct test_batched_req { 43c92316bfSLuis R. Rodriguez u8 idx; 44c92316bfSLuis R. Rodriguez int rc; 45c92316bfSLuis R. Rodriguez bool sent; 46c92316bfSLuis R. Rodriguez const struct firmware *fw; 47c92316bfSLuis R. Rodriguez const char *name; 48c92316bfSLuis R. Rodriguez struct completion completion; 49c92316bfSLuis R. Rodriguez struct task_struct *task; 50c92316bfSLuis R. Rodriguez struct device *dev; 51c92316bfSLuis R. Rodriguez }; 52c92316bfSLuis R. Rodriguez 53c92316bfSLuis R. Rodriguez /** 541ad5288fSRandy Dunlap * struct test_config - represents configuration for the test for different triggers 55c92316bfSLuis R. Rodriguez * 56c92316bfSLuis R. Rodriguez * @name: the name of the firmware file to look for 577feebfa4SScott Branden * @into_buf: when the into_buf is used if this is true 587feebfa4SScott Branden * request_firmware_into_buf() will be used instead. 595d90e05cSScott Branden * @buf_size: size of buf to allocate when into_buf is true 605d90e05cSScott Branden * @file_offset: file offset to request when calling request_firmware_into_buf 615d90e05cSScott Branden * @partial: partial read opt when calling request_firmware_into_buf 62c92316bfSLuis R. Rodriguez * @sync_direct: when the sync trigger is used if this is true 63c92316bfSLuis R. Rodriguez * request_firmware_direct() will be used instead. 64c92316bfSLuis R. Rodriguez * @send_uevent: whether or not to send a uevent for async requests 65c92316bfSLuis R. Rodriguez * @num_requests: number of requests to try per test case. This is trigger 66c92316bfSLuis R. Rodriguez * specific. 67c92316bfSLuis R. Rodriguez * @reqs: stores all requests information 68c92316bfSLuis R. Rodriguez * @read_fw_idx: index of thread from which we want to read firmware results 69c92316bfSLuis R. Rodriguez * from through the read_fw trigger. 70a31ad463SRuss Weight * @upload_name: firmware name to be used with upload_read sysfs node 71c92316bfSLuis R. Rodriguez * @test_result: a test may use this to collect the result from the call 72c92316bfSLuis R. Rodriguez * of the request_firmware*() calls used in their tests. In order of 73c92316bfSLuis R. Rodriguez * priority we always keep first any setup error. If no setup errors were 74c92316bfSLuis R. Rodriguez * found then we move on to the first error encountered while running the 75c92316bfSLuis R. Rodriguez * API. Note that for async calls this typically will be a successful 76c92316bfSLuis R. Rodriguez * result (0) unless of course you've used bogus parameters, or the system 77c92316bfSLuis R. Rodriguez * is out of memory. In the async case the callback is expected to do a 78c92316bfSLuis R. Rodriguez * bit more homework to figure out what happened, unfortunately the only 79c92316bfSLuis R. Rodriguez * information passed today on error is the fact that no firmware was 80c92316bfSLuis R. Rodriguez * found so we can only assume -ENOENT on async calls if the firmware is 81c92316bfSLuis R. Rodriguez * NULL. 82c92316bfSLuis R. Rodriguez * 83c92316bfSLuis R. Rodriguez * Errors you can expect: 84c92316bfSLuis R. Rodriguez * 85c92316bfSLuis R. Rodriguez * API specific: 86c92316bfSLuis R. Rodriguez * 87c92316bfSLuis R. Rodriguez * 0: success for sync, for async it means request was sent 88c92316bfSLuis R. Rodriguez * -EINVAL: invalid parameters or request 89c92316bfSLuis R. Rodriguez * -ENOENT: files not found 90c92316bfSLuis R. Rodriguez * 91c92316bfSLuis R. Rodriguez * System environment: 92c92316bfSLuis R. Rodriguez * 93c92316bfSLuis R. Rodriguez * -ENOMEM: memory pressure on system 94c92316bfSLuis R. Rodriguez * -ENODEV: out of number of devices to test 95c92316bfSLuis R. Rodriguez * -EINVAL: an unexpected error has occurred 96c92316bfSLuis R. Rodriguez * @req_firmware: if @sync_direct is true this is set to 97c92316bfSLuis R. Rodriguez * request_firmware_direct(), otherwise request_firmware() 98c92316bfSLuis R. Rodriguez */ 99c92316bfSLuis R. Rodriguez struct test_config { 100c92316bfSLuis R. Rodriguez char *name; 1017feebfa4SScott Branden bool into_buf; 1025d90e05cSScott Branden size_t buf_size; 1035d90e05cSScott Branden size_t file_offset; 1045d90e05cSScott Branden bool partial; 105c92316bfSLuis R. Rodriguez bool sync_direct; 106c92316bfSLuis R. Rodriguez bool send_uevent; 107c92316bfSLuis R. Rodriguez u8 num_requests; 108c92316bfSLuis R. Rodriguez u8 read_fw_idx; 109a31ad463SRuss Weight char *upload_name; 110c92316bfSLuis R. Rodriguez 111c92316bfSLuis R. Rodriguez /* 112c92316bfSLuis R. Rodriguez * These below don't belong her but we'll move them once we create 113c92316bfSLuis R. Rodriguez * a struct fw_test_device and stuff the misc_dev under there later. 114c92316bfSLuis R. Rodriguez */ 115c92316bfSLuis R. Rodriguez struct test_batched_req *reqs; 116c92316bfSLuis R. Rodriguez int test_result; 117c92316bfSLuis R. Rodriguez int (*req_firmware)(const struct firmware **fw, const char *name, 118c92316bfSLuis R. Rodriguez struct device *device); 119c92316bfSLuis R. Rodriguez }; 120c92316bfSLuis R. Rodriguez 1214a4e975bSRuss Weight struct upload_inject_err { 1224a4e975bSRuss Weight const char *prog; 1234a4e975bSRuss Weight enum fw_upload_err err_code; 1244a4e975bSRuss Weight }; 1254a4e975bSRuss Weight 126a31ad463SRuss Weight struct test_firmware_upload { 127a31ad463SRuss Weight char *name; 128a31ad463SRuss Weight struct list_head node; 129a31ad463SRuss Weight char *buf; 130a31ad463SRuss Weight size_t size; 131a31ad463SRuss Weight bool cancel_request; 1324a4e975bSRuss Weight struct upload_inject_err inject; 133a31ad463SRuss Weight struct fw_upload *fwl; 134a31ad463SRuss Weight }; 135a31ad463SRuss Weight 13676f8ab1bSWei Yongjun static struct test_config *test_fw_config; 137c92316bfSLuis R. Rodriguez 138a31ad463SRuss Weight static struct test_firmware_upload *upload_lookup_name(const char *name) 139a31ad463SRuss Weight { 140a31ad463SRuss Weight struct test_firmware_upload *tst; 141a31ad463SRuss Weight 142a31ad463SRuss Weight list_for_each_entry(tst, &test_upload_list, node) 143a31ad463SRuss Weight if (strncmp(name, tst->name, strlen(tst->name)) == 0) 144a31ad463SRuss Weight return tst; 145a31ad463SRuss Weight 146a31ad463SRuss Weight return NULL; 147a31ad463SRuss Weight } 148a31ad463SRuss Weight 1490a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 1500a8adf58SKees Cook size_t size, loff_t *offset) 1510a8adf58SKees Cook { 1520a8adf58SKees Cook ssize_t rc = 0; 1530a8adf58SKees Cook 1540a8adf58SKees Cook mutex_lock(&test_fw_mutex); 1550a8adf58SKees Cook if (test_firmware) 1560a8adf58SKees Cook rc = simple_read_from_buffer(buf, size, offset, 1570a8adf58SKees Cook test_firmware->data, 1580a8adf58SKees Cook test_firmware->size); 1590a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 1600a8adf58SKees Cook return rc; 1610a8adf58SKees Cook } 1620a8adf58SKees Cook 1630a8adf58SKees Cook static const struct file_operations test_fw_fops = { 1640a8adf58SKees Cook .owner = THIS_MODULE, 1650a8adf58SKees Cook .read = test_fw_misc_read, 1660a8adf58SKees Cook }; 1670a8adf58SKees Cook 168c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void) 169c92316bfSLuis R. Rodriguez { 170c92316bfSLuis R. Rodriguez struct test_batched_req *req; 171c92316bfSLuis R. Rodriguez u8 i; 172c92316bfSLuis R. Rodriguez 173c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) 174c92316bfSLuis R. Rodriguez return; 175c92316bfSLuis R. Rodriguez 176c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 177c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 178c92316bfSLuis R. Rodriguez if (req->fw) 179c92316bfSLuis R. Rodriguez release_firmware(req->fw); 180c92316bfSLuis R. Rodriguez } 181c92316bfSLuis R. Rodriguez 182c92316bfSLuis R. Rodriguez vfree(test_fw_config->reqs); 183c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 184c92316bfSLuis R. Rodriguez } 185c92316bfSLuis R. Rodriguez 186c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void) 187c92316bfSLuis R. Rodriguez { 188c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 189c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 190c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 191c92316bfSLuis R. Rodriguez } 192c92316bfSLuis R. Rodriguez 193c92316bfSLuis R. Rodriguez 194c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void) 195c92316bfSLuis R. Rodriguez { 196c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 197c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 198c92316bfSLuis R. Rodriguez test_fw_config->name = NULL; 199c92316bfSLuis R. Rodriguez } 200c92316bfSLuis R. Rodriguez 201c92316bfSLuis R. Rodriguez /* 202c92316bfSLuis R. Rodriguez * XXX: move to kstrncpy() once merged. 203c92316bfSLuis R. Rodriguez * 204c92316bfSLuis R. Rodriguez * Users should use kfree_const() when freeing these. 205c92316bfSLuis R. Rodriguez */ 206c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp) 207c92316bfSLuis R. Rodriguez { 208c92316bfSLuis R. Rodriguez *dst = kstrndup(name, count, gfp); 209c92316bfSLuis R. Rodriguez if (!*dst) 210c92316bfSLuis R. Rodriguez return -ENOSPC; 211c92316bfSLuis R. Rodriguez return count; 212c92316bfSLuis R. Rodriguez } 213c92316bfSLuis R. Rodriguez 214c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void) 215c92316bfSLuis R. Rodriguez { 216c92316bfSLuis R. Rodriguez int ret; 217c92316bfSLuis R. Rodriguez 218c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME, 219c92316bfSLuis R. Rodriguez strlen(TEST_FIRMWARE_NAME), GFP_KERNEL); 220c92316bfSLuis R. Rodriguez if (ret < 0) 221c92316bfSLuis R. Rodriguez goto out; 222c92316bfSLuis R. Rodriguez 223c92316bfSLuis R. Rodriguez test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS; 224c92316bfSLuis R. Rodriguez test_fw_config->send_uevent = true; 2257feebfa4SScott Branden test_fw_config->into_buf = false; 2265d90e05cSScott Branden test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE; 2275d90e05cSScott Branden test_fw_config->file_offset = 0; 2285d90e05cSScott Branden test_fw_config->partial = false; 229c92316bfSLuis R. Rodriguez test_fw_config->sync_direct = false; 230c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = request_firmware; 231c92316bfSLuis R. Rodriguez test_fw_config->test_result = 0; 232c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 233a31ad463SRuss Weight test_fw_config->upload_name = NULL; 234c92316bfSLuis R. Rodriguez 235c92316bfSLuis R. Rodriguez return 0; 236c92316bfSLuis R. Rodriguez 237c92316bfSLuis R. Rodriguez out: 238c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 239c92316bfSLuis R. Rodriguez return ret; 240c92316bfSLuis R. Rodriguez } 241c92316bfSLuis R. Rodriguez 242c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev, 243c92316bfSLuis R. Rodriguez struct device_attribute *attr, 244c92316bfSLuis R. Rodriguez const char *buf, size_t count) 245c92316bfSLuis R. Rodriguez { 246c92316bfSLuis R. Rodriguez int ret; 247c92316bfSLuis R. Rodriguez 248c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 249c92316bfSLuis R. Rodriguez 250c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 251c92316bfSLuis R. Rodriguez 252c92316bfSLuis R. Rodriguez ret = __test_firmware_config_init(); 253c92316bfSLuis R. Rodriguez if (ret < 0) { 254c92316bfSLuis R. Rodriguez ret = -ENOMEM; 255c92316bfSLuis R. Rodriguez pr_err("could not alloc settings for config trigger: %d\n", 256c92316bfSLuis R. Rodriguez ret); 257c92316bfSLuis R. Rodriguez goto out; 258c92316bfSLuis R. Rodriguez } 259c92316bfSLuis R. Rodriguez 260c92316bfSLuis R. Rodriguez pr_info("reset\n"); 261c92316bfSLuis R. Rodriguez ret = count; 262c92316bfSLuis R. Rodriguez 263c92316bfSLuis R. Rodriguez out: 264c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 265c92316bfSLuis R. Rodriguez 266c92316bfSLuis R. Rodriguez return ret; 267c92316bfSLuis R. Rodriguez } 268c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset); 269c92316bfSLuis R. Rodriguez 270c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev, 271c92316bfSLuis R. Rodriguez struct device_attribute *attr, 272c92316bfSLuis R. Rodriguez char *buf) 273c92316bfSLuis R. Rodriguez { 274c92316bfSLuis R. Rodriguez int len = 0; 275c92316bfSLuis R. Rodriguez 276c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 277c92316bfSLuis R. Rodriguez 278bd17cc5aSDan Carpenter len += scnprintf(buf, PAGE_SIZE - len, 279c92316bfSLuis R. Rodriguez "Custom trigger configuration for: %s\n", 280c92316bfSLuis R. Rodriguez dev_name(dev)); 281c92316bfSLuis R. Rodriguez 282c92316bfSLuis R. Rodriguez if (test_fw_config->name) 283bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 284c92316bfSLuis R. Rodriguez "name:\t%s\n", 285c92316bfSLuis R. Rodriguez test_fw_config->name); 286c92316bfSLuis R. Rodriguez else 287bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 288d88bd098SColin Ian King "name:\tEMPTY\n"); 289c92316bfSLuis R. Rodriguez 290bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 291c92316bfSLuis R. Rodriguez "num_requests:\t%u\n", test_fw_config->num_requests); 292c92316bfSLuis R. Rodriguez 293bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 294c92316bfSLuis R. Rodriguez "send_uevent:\t\t%s\n", 295c92316bfSLuis R. Rodriguez test_fw_config->send_uevent ? 2960733d839SShawn Guo "FW_ACTION_UEVENT" : 2970733d839SShawn Guo "FW_ACTION_NOUEVENT"); 298bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 2997feebfa4SScott Branden "into_buf:\t\t%s\n", 3007feebfa4SScott Branden test_fw_config->into_buf ? "true" : "false"); 3017feebfa4SScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 3025d90e05cSScott Branden "buf_size:\t%zu\n", test_fw_config->buf_size); 3035d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 3045d90e05cSScott Branden "file_offset:\t%zu\n", test_fw_config->file_offset); 3055d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 3065d90e05cSScott Branden "partial:\t\t%s\n", 3075d90e05cSScott Branden test_fw_config->partial ? "true" : "false"); 3085d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 309c92316bfSLuis R. Rodriguez "sync_direct:\t\t%s\n", 310c92316bfSLuis R. Rodriguez test_fw_config->sync_direct ? "true" : "false"); 311bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 312c92316bfSLuis R. Rodriguez "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); 313a31ad463SRuss Weight if (test_fw_config->upload_name) 314a31ad463SRuss Weight len += scnprintf(buf + len, PAGE_SIZE - len, 315a31ad463SRuss Weight "upload_name:\t%s\n", 316a31ad463SRuss Weight test_fw_config->upload_name); 317a31ad463SRuss Weight else 318a31ad463SRuss Weight len += scnprintf(buf + len, PAGE_SIZE - len, 319d88bd098SColin Ian King "upload_name:\tEMPTY\n"); 320c92316bfSLuis R. Rodriguez 321c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 322c92316bfSLuis R. Rodriguez 323c92316bfSLuis R. Rodriguez return len; 324c92316bfSLuis R. Rodriguez } 325c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config); 326c92316bfSLuis R. Rodriguez 327c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev, 328c92316bfSLuis R. Rodriguez struct device_attribute *attr, 329c92316bfSLuis R. Rodriguez const char *buf, size_t count) 330c92316bfSLuis R. Rodriguez { 331c92316bfSLuis R. Rodriguez int ret; 332c92316bfSLuis R. Rodriguez 333c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 334c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 335c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL); 336c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 337c92316bfSLuis R. Rodriguez 338c92316bfSLuis R. Rodriguez return ret; 339c92316bfSLuis R. Rodriguez } 340c92316bfSLuis R. Rodriguez 341c92316bfSLuis R. Rodriguez /* 342c92316bfSLuis R. Rodriguez * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE. 343c92316bfSLuis R. Rodriguez */ 344c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst, 345c92316bfSLuis R. Rodriguez char *src) 346c92316bfSLuis R. Rodriguez { 347c92316bfSLuis R. Rodriguez int len; 348c92316bfSLuis R. Rodriguez 349c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 350c92316bfSLuis R. Rodriguez len = snprintf(dst, PAGE_SIZE, "%s\n", src); 351c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 352c92316bfSLuis R. Rodriguez 353c92316bfSLuis R. Rodriguez return len; 354c92316bfSLuis R. Rodriguez } 355c92316bfSLuis R. Rodriguez 356*4acfe3dfSMirsad Goran Todorovac static inline int __test_dev_config_update_bool(const char *buf, size_t size, 357*4acfe3dfSMirsad Goran Todorovac bool *cfg) 358*4acfe3dfSMirsad Goran Todorovac { 359*4acfe3dfSMirsad Goran Todorovac int ret; 360*4acfe3dfSMirsad Goran Todorovac 361*4acfe3dfSMirsad Goran Todorovac if (kstrtobool(buf, cfg) < 0) 362*4acfe3dfSMirsad Goran Todorovac ret = -EINVAL; 363*4acfe3dfSMirsad Goran Todorovac else 364*4acfe3dfSMirsad Goran Todorovac ret = size; 365*4acfe3dfSMirsad Goran Todorovac 366*4acfe3dfSMirsad Goran Todorovac return ret; 367*4acfe3dfSMirsad Goran Todorovac } 368*4acfe3dfSMirsad Goran Todorovac 369c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size, 370c92316bfSLuis R. Rodriguez bool *cfg) 371c92316bfSLuis R. Rodriguez { 372c92316bfSLuis R. Rodriguez int ret; 373c92316bfSLuis R. Rodriguez 374c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 375*4acfe3dfSMirsad Goran Todorovac ret = __test_dev_config_update_bool(buf, size, cfg); 376c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 377c92316bfSLuis R. Rodriguez 378c92316bfSLuis R. Rodriguez return ret; 379c92316bfSLuis R. Rodriguez } 380c92316bfSLuis R. Rodriguez 38155623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val) 382c92316bfSLuis R. Rodriguez { 383c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 384c92316bfSLuis R. Rodriguez } 385c92316bfSLuis R. Rodriguez 386*4acfe3dfSMirsad Goran Todorovac static int __test_dev_config_update_size_t( 387*4acfe3dfSMirsad Goran Todorovac const char *buf, 3885d90e05cSScott Branden size_t size, 3895d90e05cSScott Branden size_t *cfg) 3905d90e05cSScott Branden { 3915d90e05cSScott Branden int ret; 3925d90e05cSScott Branden long new; 3935d90e05cSScott Branden 3945d90e05cSScott Branden ret = kstrtol(buf, 10, &new); 3955d90e05cSScott Branden if (ret) 3965d90e05cSScott Branden return ret; 3975d90e05cSScott Branden 3985d90e05cSScott Branden *(size_t *)cfg = new; 3995d90e05cSScott Branden 4005d90e05cSScott Branden /* Always return full write size even if we didn't consume all */ 4015d90e05cSScott Branden return size; 4025d90e05cSScott Branden } 4035d90e05cSScott Branden 4045d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val) 4055d90e05cSScott Branden { 4065d90e05cSScott Branden return snprintf(buf, PAGE_SIZE, "%zu\n", val); 4075d90e05cSScott Branden } 4085d90e05cSScott Branden 40955623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val) 410c92316bfSLuis R. Rodriguez { 411c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 412c92316bfSLuis R. Rodriguez } 413c92316bfSLuis R. Rodriguez 414*4acfe3dfSMirsad Goran Todorovac static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 415c92316bfSLuis R. Rodriguez { 416506dfc99SAlexey Dobriyan u8 val; 417c92316bfSLuis R. Rodriguez int ret; 418c92316bfSLuis R. Rodriguez 419506dfc99SAlexey Dobriyan ret = kstrtou8(buf, 10, &val); 420c92316bfSLuis R. Rodriguez if (ret) 421c92316bfSLuis R. Rodriguez return ret; 422c92316bfSLuis R. Rodriguez 423506dfc99SAlexey Dobriyan *(u8 *)cfg = val; 424c92316bfSLuis R. Rodriguez 425c92316bfSLuis R. Rodriguez /* Always return full write size even if we didn't consume all */ 426c92316bfSLuis R. Rodriguez return size; 427c92316bfSLuis R. Rodriguez } 428c92316bfSLuis R. Rodriguez 429*4acfe3dfSMirsad Goran Todorovac static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 430*4acfe3dfSMirsad Goran Todorovac { 431*4acfe3dfSMirsad Goran Todorovac int ret; 432*4acfe3dfSMirsad Goran Todorovac 433*4acfe3dfSMirsad Goran Todorovac mutex_lock(&test_fw_mutex); 434*4acfe3dfSMirsad Goran Todorovac ret = __test_dev_config_update_u8(buf, size, cfg); 435*4acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 436*4acfe3dfSMirsad Goran Todorovac 437*4acfe3dfSMirsad Goran Todorovac return ret; 438*4acfe3dfSMirsad Goran Todorovac } 439*4acfe3dfSMirsad Goran Todorovac 44055623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val) 441c92316bfSLuis R. Rodriguez { 442c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%u\n", val); 443c92316bfSLuis R. Rodriguez } 444c92316bfSLuis R. Rodriguez 445c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev, 446c92316bfSLuis R. Rodriguez struct device_attribute *attr, 447c92316bfSLuis R. Rodriguez char *buf) 448c92316bfSLuis R. Rodriguez { 449c92316bfSLuis R. Rodriguez return config_test_show_str(buf, test_fw_config->name); 450c92316bfSLuis R. Rodriguez } 451b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name); 452c92316bfSLuis R. Rodriguez 453a31ad463SRuss Weight static ssize_t config_upload_name_store(struct device *dev, 454a31ad463SRuss Weight struct device_attribute *attr, 455a31ad463SRuss Weight const char *buf, size_t count) 456a31ad463SRuss Weight { 457a31ad463SRuss Weight struct test_firmware_upload *tst; 458a31ad463SRuss Weight int ret = count; 459a31ad463SRuss Weight 460a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 461a31ad463SRuss Weight tst = upload_lookup_name(buf); 462a31ad463SRuss Weight if (tst) 463a31ad463SRuss Weight test_fw_config->upload_name = tst->name; 464a31ad463SRuss Weight else 465a31ad463SRuss Weight ret = -EINVAL; 466a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 467a31ad463SRuss Weight 468a31ad463SRuss Weight return ret; 469a31ad463SRuss Weight } 470a31ad463SRuss Weight 471a31ad463SRuss Weight static ssize_t config_upload_name_show(struct device *dev, 472a31ad463SRuss Weight struct device_attribute *attr, 473a31ad463SRuss Weight char *buf) 474a31ad463SRuss Weight { 475a31ad463SRuss Weight return config_test_show_str(buf, test_fw_config->upload_name); 476a31ad463SRuss Weight } 477a31ad463SRuss Weight static DEVICE_ATTR_RW(config_upload_name); 478a31ad463SRuss Weight 479c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev, 480c92316bfSLuis R. Rodriguez struct device_attribute *attr, 481c92316bfSLuis R. Rodriguez const char *buf, size_t count) 482c92316bfSLuis R. Rodriguez { 483c92316bfSLuis R. Rodriguez int rc; 484c92316bfSLuis R. Rodriguez 485c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 486c92316bfSLuis R. Rodriguez if (test_fw_config->reqs) { 487c92316bfSLuis R. Rodriguez pr_err("Must call release_all_firmware prior to changing config\n"); 488c92316bfSLuis R. Rodriguez rc = -EINVAL; 489a5e19233SWei Yongjun mutex_unlock(&test_fw_mutex); 490c92316bfSLuis R. Rodriguez goto out; 491c92316bfSLuis R. Rodriguez } 492c92316bfSLuis R. Rodriguez 493*4acfe3dfSMirsad Goran Todorovac rc = __test_dev_config_update_u8(buf, count, 494c92316bfSLuis R. Rodriguez &test_fw_config->num_requests); 495*4acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 496c92316bfSLuis R. Rodriguez 497c92316bfSLuis R. Rodriguez out: 498c92316bfSLuis R. Rodriguez return rc; 499c92316bfSLuis R. Rodriguez } 500c92316bfSLuis R. Rodriguez 501c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev, 502c92316bfSLuis R. Rodriguez struct device_attribute *attr, 503c92316bfSLuis R. Rodriguez char *buf) 504c92316bfSLuis R. Rodriguez { 505c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->num_requests); 506c92316bfSLuis R. Rodriguez } 507b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests); 508c92316bfSLuis R. Rodriguez 5097feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev, 5107feebfa4SScott Branden struct device_attribute *attr, 5117feebfa4SScott Branden const char *buf, size_t count) 5127feebfa4SScott Branden { 5137feebfa4SScott Branden return test_dev_config_update_bool(buf, 5147feebfa4SScott Branden count, 5157feebfa4SScott Branden &test_fw_config->into_buf); 5167feebfa4SScott Branden } 5177feebfa4SScott Branden 5187feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev, 5197feebfa4SScott Branden struct device_attribute *attr, 5207feebfa4SScott Branden char *buf) 5217feebfa4SScott Branden { 5227feebfa4SScott Branden return test_dev_config_show_bool(buf, test_fw_config->into_buf); 5237feebfa4SScott Branden } 5247feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf); 5257feebfa4SScott Branden 5265d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev, 5275d90e05cSScott Branden struct device_attribute *attr, 5285d90e05cSScott Branden const char *buf, size_t count) 5295d90e05cSScott Branden { 5305d90e05cSScott Branden int rc; 5315d90e05cSScott Branden 5325d90e05cSScott Branden mutex_lock(&test_fw_mutex); 5335d90e05cSScott Branden if (test_fw_config->reqs) { 5345d90e05cSScott Branden pr_err("Must call release_all_firmware prior to changing config\n"); 5355d90e05cSScott Branden rc = -EINVAL; 5365d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 5375d90e05cSScott Branden goto out; 5385d90e05cSScott Branden } 5395d90e05cSScott Branden 540*4acfe3dfSMirsad Goran Todorovac rc = __test_dev_config_update_size_t(buf, count, 5415d90e05cSScott Branden &test_fw_config->buf_size); 542*4acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 5435d90e05cSScott Branden 5445d90e05cSScott Branden out: 5455d90e05cSScott Branden return rc; 5465d90e05cSScott Branden } 5475d90e05cSScott Branden 5485d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev, 5495d90e05cSScott Branden struct device_attribute *attr, 5505d90e05cSScott Branden char *buf) 5515d90e05cSScott Branden { 5525d90e05cSScott Branden return test_dev_config_show_size_t(buf, test_fw_config->buf_size); 5535d90e05cSScott Branden } 5545d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size); 5555d90e05cSScott Branden 5565d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev, 5575d90e05cSScott Branden struct device_attribute *attr, 5585d90e05cSScott Branden const char *buf, size_t count) 5595d90e05cSScott Branden { 5605d90e05cSScott Branden int rc; 5615d90e05cSScott Branden 5625d90e05cSScott Branden mutex_lock(&test_fw_mutex); 5635d90e05cSScott Branden if (test_fw_config->reqs) { 5645d90e05cSScott Branden pr_err("Must call release_all_firmware prior to changing config\n"); 5655d90e05cSScott Branden rc = -EINVAL; 5665d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 5675d90e05cSScott Branden goto out; 5685d90e05cSScott Branden } 5695d90e05cSScott Branden 570*4acfe3dfSMirsad Goran Todorovac rc = __test_dev_config_update_size_t(buf, count, 5715d90e05cSScott Branden &test_fw_config->file_offset); 572*4acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 5735d90e05cSScott Branden 5745d90e05cSScott Branden out: 5755d90e05cSScott Branden return rc; 5765d90e05cSScott Branden } 5775d90e05cSScott Branden 5785d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev, 5795d90e05cSScott Branden struct device_attribute *attr, 5805d90e05cSScott Branden char *buf) 5815d90e05cSScott Branden { 5825d90e05cSScott Branden return test_dev_config_show_size_t(buf, test_fw_config->file_offset); 5835d90e05cSScott Branden } 5845d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset); 5855d90e05cSScott Branden 5865d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev, 5875d90e05cSScott Branden struct device_attribute *attr, 5885d90e05cSScott Branden const char *buf, size_t count) 5895d90e05cSScott Branden { 5905d90e05cSScott Branden return test_dev_config_update_bool(buf, 5915d90e05cSScott Branden count, 5925d90e05cSScott Branden &test_fw_config->partial); 5935d90e05cSScott Branden } 5945d90e05cSScott Branden 5955d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev, 5965d90e05cSScott Branden struct device_attribute *attr, 5975d90e05cSScott Branden char *buf) 5985d90e05cSScott Branden { 5995d90e05cSScott Branden return test_dev_config_show_bool(buf, test_fw_config->partial); 6005d90e05cSScott Branden } 6015d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial); 6025d90e05cSScott Branden 603c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev, 604c92316bfSLuis R. Rodriguez struct device_attribute *attr, 605c92316bfSLuis R. Rodriguez const char *buf, size_t count) 606c92316bfSLuis R. Rodriguez { 607c92316bfSLuis R. Rodriguez int rc = test_dev_config_update_bool(buf, count, 608c92316bfSLuis R. Rodriguez &test_fw_config->sync_direct); 609c92316bfSLuis R. Rodriguez 610c92316bfSLuis R. Rodriguez if (rc == count) 611c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = test_fw_config->sync_direct ? 612c92316bfSLuis R. Rodriguez request_firmware_direct : 613c92316bfSLuis R. Rodriguez request_firmware; 614c92316bfSLuis R. Rodriguez return rc; 615c92316bfSLuis R. Rodriguez } 616c92316bfSLuis R. Rodriguez 617c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev, 618c92316bfSLuis R. Rodriguez struct device_attribute *attr, 619c92316bfSLuis R. Rodriguez char *buf) 620c92316bfSLuis R. Rodriguez { 621c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->sync_direct); 622c92316bfSLuis R. Rodriguez } 623b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct); 624c92316bfSLuis R. Rodriguez 625c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev, 626c92316bfSLuis R. Rodriguez struct device_attribute *attr, 627c92316bfSLuis R. Rodriguez const char *buf, size_t count) 628c92316bfSLuis R. Rodriguez { 629c92316bfSLuis R. Rodriguez return test_dev_config_update_bool(buf, count, 630c92316bfSLuis R. Rodriguez &test_fw_config->send_uevent); 631c92316bfSLuis R. Rodriguez } 632c92316bfSLuis R. Rodriguez 633c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev, 634c92316bfSLuis R. Rodriguez struct device_attribute *attr, 635c92316bfSLuis R. Rodriguez char *buf) 636c92316bfSLuis R. Rodriguez { 637c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->send_uevent); 638c92316bfSLuis R. Rodriguez } 639b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent); 640c92316bfSLuis R. Rodriguez 641c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev, 642c92316bfSLuis R. Rodriguez struct device_attribute *attr, 643c92316bfSLuis R. Rodriguez const char *buf, size_t count) 644c92316bfSLuis R. Rodriguez { 645c92316bfSLuis R. Rodriguez return test_dev_config_update_u8(buf, count, 646c92316bfSLuis R. Rodriguez &test_fw_config->read_fw_idx); 647c92316bfSLuis R. Rodriguez } 648c92316bfSLuis R. Rodriguez 649c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev, 650c92316bfSLuis R. Rodriguez struct device_attribute *attr, 651c92316bfSLuis R. Rodriguez char *buf) 652c92316bfSLuis R. Rodriguez { 653c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx); 654c92316bfSLuis R. Rodriguez } 655b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx); 656c92316bfSLuis R. Rodriguez 657c92316bfSLuis R. Rodriguez 6580a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev, 6590a8adf58SKees Cook struct device_attribute *attr, 6600a8adf58SKees Cook const char *buf, size_t count) 6610a8adf58SKees Cook { 6620a8adf58SKees Cook int rc; 6630a8adf58SKees Cook char *name; 6640a8adf58SKees Cook 665be4a1326SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 6660a8adf58SKees Cook if (!name) 6670a8adf58SKees Cook return -ENOSPC; 6680a8adf58SKees Cook 6690a8adf58SKees Cook pr_info("loading '%s'\n", name); 6700a8adf58SKees Cook 6710a8adf58SKees Cook mutex_lock(&test_fw_mutex); 6720a8adf58SKees Cook release_firmware(test_firmware); 6730a8adf58SKees Cook test_firmware = NULL; 6740a8adf58SKees Cook rc = request_firmware(&test_firmware, name, dev); 67547e0bbb7SBrian Norris if (rc) { 6760a8adf58SKees Cook pr_info("load of '%s' failed: %d\n", name, rc); 67747e0bbb7SBrian Norris goto out; 67847e0bbb7SBrian Norris } 67947e0bbb7SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 68047e0bbb7SBrian Norris rc = count; 68147e0bbb7SBrian Norris 68247e0bbb7SBrian Norris out: 6830a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 6840a8adf58SKees Cook 6850a8adf58SKees Cook kfree(name); 6860a8adf58SKees Cook 68747e0bbb7SBrian Norris return rc; 6880a8adf58SKees Cook } 6890a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request); 6900a8adf58SKees Cook 691548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 692baaabecfSKees Cook extern struct list_head efi_embedded_fw_list; 693baaabecfSKees Cook extern bool efi_embedded_fw_checked; 694baaabecfSKees Cook 695548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev, 696548193cbSHans de Goede struct device_attribute *attr, 697548193cbSHans de Goede const char *buf, size_t count) 698548193cbSHans de Goede { 699548193cbSHans de Goede static const u8 test_data[] = { 700548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04, 701548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08, 702548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40, 703548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80 704548193cbSHans de Goede }; 705548193cbSHans de Goede struct efi_embedded_fw efi_embedded_fw; 706548193cbSHans de Goede const struct firmware *firmware = NULL; 707baaabecfSKees Cook bool saved_efi_embedded_fw_checked; 708548193cbSHans de Goede char *name; 709548193cbSHans de Goede int rc; 710548193cbSHans de Goede 711548193cbSHans de Goede name = kstrndup(buf, count, GFP_KERNEL); 712548193cbSHans de Goede if (!name) 713548193cbSHans de Goede return -ENOSPC; 714548193cbSHans de Goede 715548193cbSHans de Goede pr_info("inserting test platform fw '%s'\n", name); 716548193cbSHans de Goede efi_embedded_fw.name = name; 717548193cbSHans de Goede efi_embedded_fw.data = (void *)test_data; 718548193cbSHans de Goede efi_embedded_fw.length = sizeof(test_data); 719548193cbSHans de Goede list_add(&efi_embedded_fw.list, &efi_embedded_fw_list); 720baaabecfSKees Cook saved_efi_embedded_fw_checked = efi_embedded_fw_checked; 721baaabecfSKees Cook efi_embedded_fw_checked = true; 722548193cbSHans de Goede 723548193cbSHans de Goede pr_info("loading '%s'\n", name); 724548193cbSHans de Goede rc = firmware_request_platform(&firmware, name, dev); 725548193cbSHans de Goede if (rc) { 726548193cbSHans de Goede pr_info("load of '%s' failed: %d\n", name, rc); 727548193cbSHans de Goede goto out; 728548193cbSHans de Goede } 729548193cbSHans de Goede if (firmware->size != sizeof(test_data) || 730548193cbSHans de Goede memcmp(firmware->data, test_data, sizeof(test_data)) != 0) { 731548193cbSHans de Goede pr_info("firmware contents mismatch for '%s'\n", name); 732548193cbSHans de Goede rc = -EINVAL; 733548193cbSHans de Goede goto out; 734548193cbSHans de Goede } 735548193cbSHans de Goede pr_info("loaded: %zu\n", firmware->size); 736548193cbSHans de Goede rc = count; 737548193cbSHans de Goede 738548193cbSHans de Goede out: 739baaabecfSKees Cook efi_embedded_fw_checked = saved_efi_embedded_fw_checked; 740548193cbSHans de Goede release_firmware(firmware); 741548193cbSHans de Goede list_del(&efi_embedded_fw.list); 742548193cbSHans de Goede kfree(name); 743548193cbSHans de Goede 744548193cbSHans de Goede return rc; 745548193cbSHans de Goede } 746548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform); 747548193cbSHans de Goede #endif 748548193cbSHans de Goede 749eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done); 750eb910947SBrian Norris 751eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context) 752eb910947SBrian Norris { 753eb910947SBrian Norris test_firmware = fw; 754eb910947SBrian Norris complete(&async_fw_done); 755eb910947SBrian Norris } 756eb910947SBrian Norris 757eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev, 758eb910947SBrian Norris struct device_attribute *attr, 759eb910947SBrian Norris const char *buf, size_t count) 760eb910947SBrian Norris { 761eb910947SBrian Norris int rc; 762eb910947SBrian Norris char *name; 763eb910947SBrian Norris 764eb910947SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 765eb910947SBrian Norris if (!name) 766eb910947SBrian Norris return -ENOSPC; 767eb910947SBrian Norris 768eb910947SBrian Norris pr_info("loading '%s'\n", name); 769eb910947SBrian Norris 770eb910947SBrian Norris mutex_lock(&test_fw_mutex); 771eb910947SBrian Norris release_firmware(test_firmware); 772eb910947SBrian Norris test_firmware = NULL; 773eb910947SBrian Norris rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 774eb910947SBrian Norris NULL, trigger_async_request_cb); 775eb910947SBrian Norris if (rc) { 776eb910947SBrian Norris pr_info("async load of '%s' failed: %d\n", name, rc); 777eb910947SBrian Norris kfree(name); 778eb910947SBrian Norris goto out; 779eb910947SBrian Norris } 780eb910947SBrian Norris /* Free 'name' ASAP, to test for race conditions */ 781eb910947SBrian Norris kfree(name); 782eb910947SBrian Norris 783eb910947SBrian Norris wait_for_completion(&async_fw_done); 784eb910947SBrian Norris 785eb910947SBrian Norris if (test_firmware) { 786eb910947SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 787eb910947SBrian Norris rc = count; 788eb910947SBrian Norris } else { 789eb910947SBrian Norris pr_err("failed to async load firmware\n"); 7907feebfa4SScott Branden rc = -ENOMEM; 791eb910947SBrian Norris } 792eb910947SBrian Norris 793eb910947SBrian Norris out: 794eb910947SBrian Norris mutex_unlock(&test_fw_mutex); 795eb910947SBrian Norris 796eb910947SBrian Norris return rc; 797eb910947SBrian Norris } 798eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request); 799eb910947SBrian Norris 800061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev, 801061132d2SLuis R. Rodriguez struct device_attribute *attr, 802061132d2SLuis R. Rodriguez const char *buf, size_t count) 803061132d2SLuis R. Rodriguez { 804061132d2SLuis R. Rodriguez int rc; 805061132d2SLuis R. Rodriguez char *name; 806061132d2SLuis R. Rodriguez 807061132d2SLuis R. Rodriguez name = kstrndup(buf, count, GFP_KERNEL); 808061132d2SLuis R. Rodriguez if (!name) 809061132d2SLuis R. Rodriguez return -ENOSPC; 810061132d2SLuis R. Rodriguez 811061132d2SLuis R. Rodriguez pr_info("loading '%s' using custom fallback mechanism\n", name); 812061132d2SLuis R. Rodriguez 813061132d2SLuis R. Rodriguez mutex_lock(&test_fw_mutex); 814061132d2SLuis R. Rodriguez release_firmware(test_firmware); 815061132d2SLuis R. Rodriguez test_firmware = NULL; 8160733d839SShawn Guo rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, name, 817061132d2SLuis R. Rodriguez dev, GFP_KERNEL, NULL, 818061132d2SLuis R. Rodriguez trigger_async_request_cb); 819061132d2SLuis R. Rodriguez if (rc) { 820061132d2SLuis R. Rodriguez pr_info("async load of '%s' failed: %d\n", name, rc); 821061132d2SLuis R. Rodriguez kfree(name); 822061132d2SLuis R. Rodriguez goto out; 823061132d2SLuis R. Rodriguez } 824061132d2SLuis R. Rodriguez /* Free 'name' ASAP, to test for race conditions */ 825061132d2SLuis R. Rodriguez kfree(name); 826061132d2SLuis R. Rodriguez 827061132d2SLuis R. Rodriguez wait_for_completion(&async_fw_done); 828061132d2SLuis R. Rodriguez 829061132d2SLuis R. Rodriguez if (test_firmware) { 830061132d2SLuis R. Rodriguez pr_info("loaded: %zu\n", test_firmware->size); 831061132d2SLuis R. Rodriguez rc = count; 832061132d2SLuis R. Rodriguez } else { 833061132d2SLuis R. Rodriguez pr_err("failed to async load firmware\n"); 834061132d2SLuis R. Rodriguez rc = -ENODEV; 835061132d2SLuis R. Rodriguez } 836061132d2SLuis R. Rodriguez 837061132d2SLuis R. Rodriguez out: 838061132d2SLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 839061132d2SLuis R. Rodriguez 840061132d2SLuis R. Rodriguez return rc; 841061132d2SLuis R. Rodriguez } 842061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback); 843061132d2SLuis R. Rodriguez 844c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data) 845c92316bfSLuis R. Rodriguez { 846c92316bfSLuis R. Rodriguez struct test_batched_req *req = data; 847c92316bfSLuis R. Rodriguez 848c92316bfSLuis R. Rodriguez if (!req) { 849c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 850c92316bfSLuis R. Rodriguez return -EINVAL; 851c92316bfSLuis R. Rodriguez } 852c92316bfSLuis R. Rodriguez 8537feebfa4SScott Branden if (test_fw_config->into_buf) { 8547feebfa4SScott Branden void *test_buf; 8557feebfa4SScott Branden 8567feebfa4SScott Branden test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); 8577feebfa4SScott Branden if (!test_buf) 8587feebfa4SScott Branden return -ENOSPC; 8597feebfa4SScott Branden 8605d90e05cSScott Branden if (test_fw_config->partial) 8615d90e05cSScott Branden req->rc = request_partial_firmware_into_buf 8625d90e05cSScott Branden (&req->fw, 8637feebfa4SScott Branden req->name, 8647feebfa4SScott Branden req->dev, 8657feebfa4SScott Branden test_buf, 8665d90e05cSScott Branden test_fw_config->buf_size, 8675d90e05cSScott Branden test_fw_config->file_offset); 8685d90e05cSScott Branden else 8695d90e05cSScott Branden req->rc = request_firmware_into_buf 8705d90e05cSScott Branden (&req->fw, 8715d90e05cSScott Branden req->name, 8725d90e05cSScott Branden req->dev, 8735d90e05cSScott Branden test_buf, 8745d90e05cSScott Branden test_fw_config->buf_size); 8757feebfa4SScott Branden if (!req->fw) 8767feebfa4SScott Branden kfree(test_buf); 8777feebfa4SScott Branden } else { 8787feebfa4SScott Branden req->rc = test_fw_config->req_firmware(&req->fw, 8797feebfa4SScott Branden req->name, 8807feebfa4SScott Branden req->dev); 8817feebfa4SScott Branden } 8827feebfa4SScott Branden 883c92316bfSLuis R. Rodriguez if (req->rc) { 884c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync load failed: %d\n", 885c92316bfSLuis R. Rodriguez req->idx, req->rc); 886c92316bfSLuis R. Rodriguez if (!test_fw_config->test_result) 887c92316bfSLuis R. Rodriguez test_fw_config->test_result = req->rc; 888c92316bfSLuis R. Rodriguez } else if (req->fw) { 889c92316bfSLuis R. Rodriguez req->sent = true; 890c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync loaded %zu\n", 891c92316bfSLuis R. Rodriguez req->idx, req->fw->size); 892c92316bfSLuis R. Rodriguez } 893c92316bfSLuis R. Rodriguez complete(&req->completion); 894c92316bfSLuis R. Rodriguez 895c92316bfSLuis R. Rodriguez req->task = NULL; 896c92316bfSLuis R. Rodriguez 897c92316bfSLuis R. Rodriguez return 0; 898c92316bfSLuis R. Rodriguez } 899c92316bfSLuis R. Rodriguez 900c92316bfSLuis R. Rodriguez /* 901c92316bfSLuis R. Rodriguez * We use a kthread as otherwise the kernel serializes all our sync requests 902c92316bfSLuis R. Rodriguez * and we would not be able to mimic batched requests on a sync call. Batched 903c92316bfSLuis R. Rodriguez * requests on a sync call can for instance happen on a device driver when 904c92316bfSLuis R. Rodriguez * multiple cards are used and firmware loading happens outside of probe. 905c92316bfSLuis R. Rodriguez */ 906c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev, 907c92316bfSLuis R. Rodriguez struct device_attribute *attr, 908c92316bfSLuis R. Rodriguez const char *buf, size_t count) 909c92316bfSLuis R. Rodriguez { 910c92316bfSLuis R. Rodriguez struct test_batched_req *req; 911c92316bfSLuis R. Rodriguez int rc; 912c92316bfSLuis R. Rodriguez u8 i; 913c92316bfSLuis R. Rodriguez 914c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 915c92316bfSLuis R. Rodriguez 916fad953ceSKees Cook test_fw_config->reqs = 917fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 918fad953ceSKees Cook test_fw_config->num_requests, 2)); 919c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 920c92316bfSLuis R. Rodriguez rc = -ENOMEM; 921c92316bfSLuis R. Rodriguez goto out_unlock; 922c92316bfSLuis R. Rodriguez } 923c92316bfSLuis R. Rodriguez 924c92316bfSLuis R. Rodriguez pr_info("batched sync firmware loading '%s' %u times\n", 925c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 926c92316bfSLuis R. Rodriguez 927c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 928c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 929c92316bfSLuis R. Rodriguez req->fw = NULL; 930c92316bfSLuis R. Rodriguez req->idx = i; 931c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 932c92316bfSLuis R. Rodriguez req->dev = dev; 933c92316bfSLuis R. Rodriguez init_completion(&req->completion); 934c92316bfSLuis R. Rodriguez req->task = kthread_run(test_fw_run_batch_request, req, 935c92316bfSLuis R. Rodriguez "%s-%u", KBUILD_MODNAME, req->idx); 936c92316bfSLuis R. Rodriguez if (!req->task || IS_ERR(req->task)) { 937c92316bfSLuis R. Rodriguez pr_err("Setting up thread %u failed\n", req->idx); 938c92316bfSLuis R. Rodriguez req->task = NULL; 939c92316bfSLuis R. Rodriguez rc = -ENOMEM; 940c92316bfSLuis R. Rodriguez goto out_bail; 941c92316bfSLuis R. Rodriguez } 942c92316bfSLuis R. Rodriguez } 943c92316bfSLuis R. Rodriguez 944c92316bfSLuis R. Rodriguez rc = count; 945c92316bfSLuis R. Rodriguez 946c92316bfSLuis R. Rodriguez /* 947c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 948c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 949c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 950c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 951c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 952c92316bfSLuis R. Rodriguez */ 953c92316bfSLuis R. Rodriguez 954c92316bfSLuis R. Rodriguez out_bail: 955c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 956c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 957c92316bfSLuis R. Rodriguez if (req->task || req->sent) 958c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 959c92316bfSLuis R. Rodriguez } 960c92316bfSLuis R. Rodriguez 961c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 962c92316bfSLuis R. Rodriguez if (rc < 0) 963c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 964c92316bfSLuis R. Rodriguez 965c92316bfSLuis R. Rodriguez out_unlock: 966c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 967c92316bfSLuis R. Rodriguez 968c92316bfSLuis R. Rodriguez return rc; 969c92316bfSLuis R. Rodriguez } 970c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests); 971c92316bfSLuis R. Rodriguez 972c92316bfSLuis R. Rodriguez /* 973c92316bfSLuis R. Rodriguez * We wait for each callback to return with the lock held, no need to lock here 974c92316bfSLuis R. Rodriguez */ 975c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context) 976c92316bfSLuis R. Rodriguez { 977c92316bfSLuis R. Rodriguez struct test_batched_req *req = context; 978c92316bfSLuis R. Rodriguez 979c92316bfSLuis R. Rodriguez if (!req) { 980c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 981c92316bfSLuis R. Rodriguez return; 982c92316bfSLuis R. Rodriguez } 983c92316bfSLuis R. Rodriguez 984c92316bfSLuis R. Rodriguez /* forces *some* batched requests to queue up */ 985c92316bfSLuis R. Rodriguez if (!req->idx) 986c92316bfSLuis R. Rodriguez ssleep(2); 987c92316bfSLuis R. Rodriguez 988c92316bfSLuis R. Rodriguez req->fw = fw; 989c92316bfSLuis R. Rodriguez 990c92316bfSLuis R. Rodriguez /* 991c92316bfSLuis R. Rodriguez * Unfortunately the firmware API gives us nothing other than a null FW 992c92316bfSLuis R. Rodriguez * if the firmware was not found on async requests. Best we can do is 993c92316bfSLuis R. Rodriguez * just assume -ENOENT. A better API would pass the actual return 994c92316bfSLuis R. Rodriguez * value to the callback. 995c92316bfSLuis R. Rodriguez */ 996c92316bfSLuis R. Rodriguez if (!fw && !test_fw_config->test_result) 997c92316bfSLuis R. Rodriguez test_fw_config->test_result = -ENOENT; 998c92316bfSLuis R. Rodriguez 999c92316bfSLuis R. Rodriguez complete(&req->completion); 1000c92316bfSLuis R. Rodriguez } 1001c92316bfSLuis R. Rodriguez 1002c92316bfSLuis R. Rodriguez static 1003c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev, 1004c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1005c92316bfSLuis R. Rodriguez const char *buf, size_t count) 1006c92316bfSLuis R. Rodriguez { 1007c92316bfSLuis R. Rodriguez struct test_batched_req *req; 1008c92316bfSLuis R. Rodriguez bool send_uevent; 1009c92316bfSLuis R. Rodriguez int rc; 1010c92316bfSLuis R. Rodriguez u8 i; 1011c92316bfSLuis R. Rodriguez 1012c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 1013c92316bfSLuis R. Rodriguez 1014fad953ceSKees Cook test_fw_config->reqs = 1015fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 1016fad953ceSKees Cook test_fw_config->num_requests, 2)); 1017c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 1018c92316bfSLuis R. Rodriguez rc = -ENOMEM; 1019c92316bfSLuis R. Rodriguez goto out; 1020c92316bfSLuis R. Rodriguez } 1021c92316bfSLuis R. Rodriguez 1022c92316bfSLuis R. Rodriguez pr_info("batched loading '%s' custom fallback mechanism %u times\n", 1023c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 1024c92316bfSLuis R. Rodriguez 10250733d839SShawn Guo send_uevent = test_fw_config->send_uevent ? FW_ACTION_UEVENT : 10260733d839SShawn Guo FW_ACTION_NOUEVENT; 1027c92316bfSLuis R. Rodriguez 1028c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 1029c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 1030c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 1031c92316bfSLuis R. Rodriguez req->fw = NULL; 1032c92316bfSLuis R. Rodriguez req->idx = i; 1033c92316bfSLuis R. Rodriguez init_completion(&req->completion); 1034c92316bfSLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, send_uevent, 1035c92316bfSLuis R. Rodriguez req->name, 1036c92316bfSLuis R. Rodriguez dev, GFP_KERNEL, req, 1037c92316bfSLuis R. Rodriguez trigger_batched_cb); 1038c92316bfSLuis R. Rodriguez if (rc) { 1039c92316bfSLuis R. Rodriguez pr_info("#%u: batched async load failed setup: %d\n", 1040c92316bfSLuis R. Rodriguez i, rc); 1041c92316bfSLuis R. Rodriguez req->rc = rc; 1042c92316bfSLuis R. Rodriguez goto out_bail; 1043c92316bfSLuis R. Rodriguez } else 1044c92316bfSLuis R. Rodriguez req->sent = true; 1045c92316bfSLuis R. Rodriguez } 1046c92316bfSLuis R. Rodriguez 1047c92316bfSLuis R. Rodriguez rc = count; 1048c92316bfSLuis R. Rodriguez 1049c92316bfSLuis R. Rodriguez out_bail: 1050c92316bfSLuis R. Rodriguez 1051c92316bfSLuis R. Rodriguez /* 1052c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 1053c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 1054c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 1055c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 1056c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 1057c92316bfSLuis R. Rodriguez */ 1058c92316bfSLuis R. Rodriguez 1059c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 1060c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 1061c92316bfSLuis R. Rodriguez if (req->sent) 1062c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 1063c92316bfSLuis R. Rodriguez } 1064c92316bfSLuis R. Rodriguez 1065c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 1066c92316bfSLuis R. Rodriguez if (rc < 0) 1067c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 1068c92316bfSLuis R. Rodriguez 1069c92316bfSLuis R. Rodriguez out: 1070c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1071c92316bfSLuis R. Rodriguez 1072c92316bfSLuis R. Rodriguez return rc; 1073c92316bfSLuis R. Rodriguez } 1074c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async); 1075c92316bfSLuis R. Rodriguez 1076a31ad463SRuss Weight static void upload_release(struct test_firmware_upload *tst) 1077a31ad463SRuss Weight { 1078a31ad463SRuss Weight firmware_upload_unregister(tst->fwl); 1079a31ad463SRuss Weight kfree(tst->buf); 1080a31ad463SRuss Weight kfree(tst->name); 1081a31ad463SRuss Weight kfree(tst); 1082a31ad463SRuss Weight } 1083a31ad463SRuss Weight 1084a31ad463SRuss Weight static void upload_release_all(void) 1085a31ad463SRuss Weight { 1086a31ad463SRuss Weight struct test_firmware_upload *tst, *tmp; 1087a31ad463SRuss Weight 1088a31ad463SRuss Weight list_for_each_entry_safe(tst, tmp, &test_upload_list, node) { 1089a31ad463SRuss Weight list_del(&tst->node); 1090a31ad463SRuss Weight upload_release(tst); 1091a31ad463SRuss Weight } 1092a31ad463SRuss Weight test_fw_config->upload_name = NULL; 1093a31ad463SRuss Weight } 1094a31ad463SRuss Weight 10954a4e975bSRuss Weight /* 10964a4e975bSRuss Weight * This table is replicated from .../firmware_loader/sysfs_upload.c 10974a4e975bSRuss Weight * and needs to be kept in sync. 10984a4e975bSRuss Weight */ 10994a4e975bSRuss Weight static const char * const fw_upload_err_str[] = { 11004a4e975bSRuss Weight [FW_UPLOAD_ERR_NONE] = "none", 11014a4e975bSRuss Weight [FW_UPLOAD_ERR_HW_ERROR] = "hw-error", 11024a4e975bSRuss Weight [FW_UPLOAD_ERR_TIMEOUT] = "timeout", 11034a4e975bSRuss Weight [FW_UPLOAD_ERR_CANCELED] = "user-abort", 11044a4e975bSRuss Weight [FW_UPLOAD_ERR_BUSY] = "device-busy", 11054a4e975bSRuss Weight [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size", 11064a4e975bSRuss Weight [FW_UPLOAD_ERR_RW_ERROR] = "read-write-error", 11074a4e975bSRuss Weight [FW_UPLOAD_ERR_WEAROUT] = "flash-wearout", 11084a4e975bSRuss Weight }; 11094a4e975bSRuss Weight 11104a4e975bSRuss Weight static void upload_err_inject_error(struct test_firmware_upload *tst, 11114a4e975bSRuss Weight const u8 *p, const char *prog) 11124a4e975bSRuss Weight { 11134a4e975bSRuss Weight enum fw_upload_err err; 11144a4e975bSRuss Weight 11154a4e975bSRuss Weight for (err = FW_UPLOAD_ERR_NONE + 1; err < FW_UPLOAD_ERR_MAX; err++) { 11164a4e975bSRuss Weight if (strncmp(p, fw_upload_err_str[err], 11174a4e975bSRuss Weight strlen(fw_upload_err_str[err])) == 0) { 11184a4e975bSRuss Weight tst->inject.prog = prog; 11194a4e975bSRuss Weight tst->inject.err_code = err; 11204a4e975bSRuss Weight return; 11214a4e975bSRuss Weight } 11224a4e975bSRuss Weight } 11234a4e975bSRuss Weight } 11244a4e975bSRuss Weight 11254a4e975bSRuss Weight static void upload_err_inject_prog(struct test_firmware_upload *tst, 11264a4e975bSRuss Weight const u8 *p) 11274a4e975bSRuss Weight { 11284a4e975bSRuss Weight static const char * const progs[] = { 11294a4e975bSRuss Weight "preparing:", "transferring:", "programming:" 11304a4e975bSRuss Weight }; 11314a4e975bSRuss Weight int i; 11324a4e975bSRuss Weight 11334a4e975bSRuss Weight for (i = 0; i < ARRAY_SIZE(progs); i++) { 11344a4e975bSRuss Weight if (strncmp(p, progs[i], strlen(progs[i])) == 0) { 11354a4e975bSRuss Weight upload_err_inject_error(tst, p + strlen(progs[i]), 11364a4e975bSRuss Weight progs[i]); 11374a4e975bSRuss Weight return; 11384a4e975bSRuss Weight } 11394a4e975bSRuss Weight } 11404a4e975bSRuss Weight } 11414a4e975bSRuss Weight 11424a4e975bSRuss Weight #define FIVE_MINUTES_MS (5 * 60 * 1000) 11434a4e975bSRuss Weight static enum fw_upload_err 11444a4e975bSRuss Weight fw_upload_wait_on_cancel(struct test_firmware_upload *tst) 11454a4e975bSRuss Weight { 11464a4e975bSRuss Weight int ms_delay; 11474a4e975bSRuss Weight 11484a4e975bSRuss Weight for (ms_delay = 0; ms_delay < FIVE_MINUTES_MS; ms_delay += 100) { 11494a4e975bSRuss Weight msleep(100); 11504a4e975bSRuss Weight if (tst->cancel_request) 11514a4e975bSRuss Weight return FW_UPLOAD_ERR_CANCELED; 11524a4e975bSRuss Weight } 11534a4e975bSRuss Weight return FW_UPLOAD_ERR_NONE; 11544a4e975bSRuss Weight } 11554a4e975bSRuss Weight 1156a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_prepare(struct fw_upload *fwl, 1157a31ad463SRuss Weight const u8 *data, u32 size) 1158a31ad463SRuss Weight { 1159a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 11604a4e975bSRuss Weight enum fw_upload_err ret = FW_UPLOAD_ERR_NONE; 11614a4e975bSRuss Weight const char *progress = "preparing:"; 1162a31ad463SRuss Weight 1163a31ad463SRuss Weight tst->cancel_request = false; 1164a31ad463SRuss Weight 11654a4e975bSRuss Weight if (!size || size > TEST_UPLOAD_MAX_SIZE) { 11664a4e975bSRuss Weight ret = FW_UPLOAD_ERR_INVALID_SIZE; 11674a4e975bSRuss Weight goto err_out; 11684a4e975bSRuss Weight } 11694a4e975bSRuss Weight 11704a4e975bSRuss Weight if (strncmp(data, "inject:", strlen("inject:")) == 0) 11714a4e975bSRuss Weight upload_err_inject_prog(tst, data + strlen("inject:")); 1172a31ad463SRuss Weight 1173a31ad463SRuss Weight memset(tst->buf, 0, TEST_UPLOAD_MAX_SIZE); 1174a31ad463SRuss Weight tst->size = size; 1175a31ad463SRuss Weight 11764a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_NONE || 11774a4e975bSRuss Weight strncmp(tst->inject.prog, progress, strlen(progress)) != 0) 1178a31ad463SRuss Weight return FW_UPLOAD_ERR_NONE; 11794a4e975bSRuss Weight 11804a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED) 11814a4e975bSRuss Weight ret = fw_upload_wait_on_cancel(tst); 11824a4e975bSRuss Weight else 11834a4e975bSRuss Weight ret = tst->inject.err_code; 11844a4e975bSRuss Weight 11854a4e975bSRuss Weight err_out: 11864a4e975bSRuss Weight /* 11874a4e975bSRuss Weight * The cleanup op only executes if the prepare op succeeds. 11884a4e975bSRuss Weight * If the prepare op fails, it must do it's own clean-up. 11894a4e975bSRuss Weight */ 11904a4e975bSRuss Weight tst->inject.err_code = FW_UPLOAD_ERR_NONE; 11914a4e975bSRuss Weight tst->inject.prog = NULL; 11924a4e975bSRuss Weight 11934a4e975bSRuss Weight return ret; 1194a31ad463SRuss Weight } 1195a31ad463SRuss Weight 1196a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl, 1197a31ad463SRuss Weight const u8 *data, u32 offset, 1198a31ad463SRuss Weight u32 size, u32 *written) 1199a31ad463SRuss Weight { 1200a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 12014a4e975bSRuss Weight const char *progress = "transferring:"; 1202a31ad463SRuss Weight u32 blk_size; 1203a31ad463SRuss Weight 1204a31ad463SRuss Weight if (tst->cancel_request) 1205a31ad463SRuss Weight return FW_UPLOAD_ERR_CANCELED; 1206a31ad463SRuss Weight 1207a31ad463SRuss Weight blk_size = min_t(u32, TEST_UPLOAD_BLK_SIZE, size); 1208a31ad463SRuss Weight memcpy(tst->buf + offset, data + offset, blk_size); 1209a31ad463SRuss Weight 1210a31ad463SRuss Weight *written = blk_size; 12114a4e975bSRuss Weight 12124a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_NONE || 12134a4e975bSRuss Weight strncmp(tst->inject.prog, progress, strlen(progress)) != 0) 1214a31ad463SRuss Weight return FW_UPLOAD_ERR_NONE; 12154a4e975bSRuss Weight 12164a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED) 12174a4e975bSRuss Weight return fw_upload_wait_on_cancel(tst); 12184a4e975bSRuss Weight 12194a4e975bSRuss Weight return tst->inject.err_code; 1220a31ad463SRuss Weight } 1221a31ad463SRuss Weight 1222a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_complete(struct fw_upload *fwl) 1223a31ad463SRuss Weight { 1224a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 12254a4e975bSRuss Weight const char *progress = "programming:"; 1226a31ad463SRuss Weight 1227a31ad463SRuss Weight if (tst->cancel_request) 1228a31ad463SRuss Weight return FW_UPLOAD_ERR_CANCELED; 1229a31ad463SRuss Weight 12304a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_NONE || 12314a4e975bSRuss Weight strncmp(tst->inject.prog, progress, strlen(progress)) != 0) 1232a31ad463SRuss Weight return FW_UPLOAD_ERR_NONE; 12334a4e975bSRuss Weight 12344a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED) 12354a4e975bSRuss Weight return fw_upload_wait_on_cancel(tst); 12364a4e975bSRuss Weight 12374a4e975bSRuss Weight return tst->inject.err_code; 1238a31ad463SRuss Weight } 1239a31ad463SRuss Weight 1240a31ad463SRuss Weight static void test_fw_upload_cancel(struct fw_upload *fwl) 1241a31ad463SRuss Weight { 1242a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 1243a31ad463SRuss Weight 1244a31ad463SRuss Weight tst->cancel_request = true; 1245a31ad463SRuss Weight } 1246a31ad463SRuss Weight 12474a4e975bSRuss Weight static void test_fw_cleanup(struct fw_upload *fwl) 12484a4e975bSRuss Weight { 12494a4e975bSRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 12504a4e975bSRuss Weight 12514a4e975bSRuss Weight tst->inject.err_code = FW_UPLOAD_ERR_NONE; 12524a4e975bSRuss Weight tst->inject.prog = NULL; 12534a4e975bSRuss Weight } 12544a4e975bSRuss Weight 1255a31ad463SRuss Weight static const struct fw_upload_ops upload_test_ops = { 1256a31ad463SRuss Weight .prepare = test_fw_upload_prepare, 1257a31ad463SRuss Weight .write = test_fw_upload_write, 1258a31ad463SRuss Weight .poll_complete = test_fw_upload_complete, 1259a31ad463SRuss Weight .cancel = test_fw_upload_cancel, 12604a4e975bSRuss Weight .cleanup = test_fw_cleanup 1261a31ad463SRuss Weight }; 1262a31ad463SRuss Weight 1263a31ad463SRuss Weight static ssize_t upload_register_store(struct device *dev, 1264a31ad463SRuss Weight struct device_attribute *attr, 1265a31ad463SRuss Weight const char *buf, size_t count) 1266a31ad463SRuss Weight { 1267a31ad463SRuss Weight struct test_firmware_upload *tst; 1268a31ad463SRuss Weight struct fw_upload *fwl; 1269a31ad463SRuss Weight char *name; 1270a31ad463SRuss Weight int ret; 1271a31ad463SRuss Weight 1272a31ad463SRuss Weight name = kstrndup(buf, count, GFP_KERNEL); 1273a31ad463SRuss Weight if (!name) 1274a31ad463SRuss Weight return -ENOMEM; 1275a31ad463SRuss Weight 1276a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 1277a31ad463SRuss Weight tst = upload_lookup_name(name); 1278a31ad463SRuss Weight if (tst) { 1279a31ad463SRuss Weight ret = -EEXIST; 1280a31ad463SRuss Weight goto free_name; 1281a31ad463SRuss Weight } 1282a31ad463SRuss Weight 1283a31ad463SRuss Weight tst = kzalloc(sizeof(*tst), GFP_KERNEL); 1284a31ad463SRuss Weight if (!tst) { 1285a31ad463SRuss Weight ret = -ENOMEM; 1286a31ad463SRuss Weight goto free_name; 1287a31ad463SRuss Weight } 1288a31ad463SRuss Weight 1289a31ad463SRuss Weight tst->name = name; 1290a31ad463SRuss Weight tst->buf = kzalloc(TEST_UPLOAD_MAX_SIZE, GFP_KERNEL); 1291a31ad463SRuss Weight if (!tst->buf) { 1292a31ad463SRuss Weight ret = -ENOMEM; 1293a31ad463SRuss Weight goto free_tst; 1294a31ad463SRuss Weight } 1295a31ad463SRuss Weight 1296a31ad463SRuss Weight fwl = firmware_upload_register(THIS_MODULE, dev, tst->name, 1297a31ad463SRuss Weight &upload_test_ops, tst); 1298a31ad463SRuss Weight if (IS_ERR(fwl)) { 1299a31ad463SRuss Weight ret = PTR_ERR(fwl); 1300a31ad463SRuss Weight goto free_buf; 1301a31ad463SRuss Weight } 1302a31ad463SRuss Weight 1303a31ad463SRuss Weight tst->fwl = fwl; 1304a31ad463SRuss Weight list_add_tail(&tst->node, &test_upload_list); 1305a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1306a31ad463SRuss Weight return count; 1307a31ad463SRuss Weight 1308a31ad463SRuss Weight free_buf: 1309a31ad463SRuss Weight kfree(tst->buf); 1310a31ad463SRuss Weight 1311a31ad463SRuss Weight free_tst: 1312a31ad463SRuss Weight kfree(tst); 1313a31ad463SRuss Weight 1314a31ad463SRuss Weight free_name: 1315a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1316a31ad463SRuss Weight kfree(name); 1317a31ad463SRuss Weight 1318a31ad463SRuss Weight return ret; 1319a31ad463SRuss Weight } 1320a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_register); 1321a31ad463SRuss Weight 1322a31ad463SRuss Weight static ssize_t upload_unregister_store(struct device *dev, 1323a31ad463SRuss Weight struct device_attribute *attr, 1324a31ad463SRuss Weight const char *buf, size_t count) 1325a31ad463SRuss Weight { 1326a31ad463SRuss Weight struct test_firmware_upload *tst; 1327a31ad463SRuss Weight int ret = count; 1328a31ad463SRuss Weight 1329a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 1330a31ad463SRuss Weight tst = upload_lookup_name(buf); 1331a31ad463SRuss Weight if (!tst) { 1332a31ad463SRuss Weight ret = -EINVAL; 1333a31ad463SRuss Weight goto out; 1334a31ad463SRuss Weight } 1335a31ad463SRuss Weight 1336a31ad463SRuss Weight if (test_fw_config->upload_name == tst->name) 1337a31ad463SRuss Weight test_fw_config->upload_name = NULL; 1338a31ad463SRuss Weight 1339a31ad463SRuss Weight list_del(&tst->node); 1340a31ad463SRuss Weight upload_release(tst); 1341a31ad463SRuss Weight 1342a31ad463SRuss Weight out: 1343a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1344a31ad463SRuss Weight return ret; 1345a31ad463SRuss Weight } 1346a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_unregister); 1347a31ad463SRuss Weight 1348c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev, 1349c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1350c92316bfSLuis R. Rodriguez char *buf) 1351c92316bfSLuis R. Rodriguez { 1352c92316bfSLuis R. Rodriguez return test_dev_config_show_int(buf, test_fw_config->test_result); 1353c92316bfSLuis R. Rodriguez } 1354c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result); 1355c92316bfSLuis R. Rodriguez 1356c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev, 1357c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1358c92316bfSLuis R. Rodriguez const char *buf, size_t count) 1359c92316bfSLuis R. Rodriguez { 1360c92316bfSLuis R. Rodriguez test_release_all_firmware(); 1361c92316bfSLuis R. Rodriguez return count; 1362c92316bfSLuis R. Rodriguez } 1363c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware); 1364c92316bfSLuis R. Rodriguez 1365c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev, 1366c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1367c92316bfSLuis R. Rodriguez char *buf) 1368c92316bfSLuis R. Rodriguez { 1369c92316bfSLuis R. Rodriguez struct test_batched_req *req; 1370c92316bfSLuis R. Rodriguez u8 idx; 1371c92316bfSLuis R. Rodriguez ssize_t rc = 0; 1372c92316bfSLuis R. Rodriguez 1373c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 1374c92316bfSLuis R. Rodriguez 1375c92316bfSLuis R. Rodriguez idx = test_fw_config->read_fw_idx; 1376c92316bfSLuis R. Rodriguez if (idx >= test_fw_config->num_requests) { 1377c92316bfSLuis R. Rodriguez rc = -ERANGE; 1378c92316bfSLuis R. Rodriguez goto out; 1379c92316bfSLuis R. Rodriguez } 1380c92316bfSLuis R. Rodriguez 1381c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 1382c92316bfSLuis R. Rodriguez rc = -EINVAL; 1383c92316bfSLuis R. Rodriguez goto out; 1384c92316bfSLuis R. Rodriguez } 1385c92316bfSLuis R. Rodriguez 1386c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[idx]; 1387c92316bfSLuis R. Rodriguez if (!req->fw) { 1388c92316bfSLuis R. Rodriguez pr_err("#%u: failed to async load firmware\n", idx); 1389c92316bfSLuis R. Rodriguez rc = -ENOENT; 1390c92316bfSLuis R. Rodriguez goto out; 1391c92316bfSLuis R. Rodriguez } 1392c92316bfSLuis R. Rodriguez 1393c92316bfSLuis R. Rodriguez pr_info("#%u: loaded %zu\n", idx, req->fw->size); 1394c92316bfSLuis R. Rodriguez 1395c92316bfSLuis R. Rodriguez if (req->fw->size > PAGE_SIZE) { 1396c92316bfSLuis R. Rodriguez pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 1397c92316bfSLuis R. Rodriguez rc = -EINVAL; 13988bb0a886SColin Ian King goto out; 1399c92316bfSLuis R. Rodriguez } 1400c92316bfSLuis R. Rodriguez memcpy(buf, req->fw->data, req->fw->size); 1401c92316bfSLuis R. Rodriguez 1402c92316bfSLuis R. Rodriguez rc = req->fw->size; 1403c92316bfSLuis R. Rodriguez out: 1404c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1405c92316bfSLuis R. Rodriguez 1406c92316bfSLuis R. Rodriguez return rc; 1407c92316bfSLuis R. Rodriguez } 1408c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware); 1409c92316bfSLuis R. Rodriguez 1410a31ad463SRuss Weight static ssize_t upload_read_show(struct device *dev, 1411a31ad463SRuss Weight struct device_attribute *attr, 1412a31ad463SRuss Weight char *buf) 1413a31ad463SRuss Weight { 1414185b29c6SDan Carpenter struct test_firmware_upload *tst = NULL; 1415185b29c6SDan Carpenter struct test_firmware_upload *tst_iter; 1416a31ad463SRuss Weight int ret = -EINVAL; 1417a31ad463SRuss Weight 1418a31ad463SRuss Weight if (!test_fw_config->upload_name) { 1419a31ad463SRuss Weight pr_err("Set config_upload_name before using upload_read\n"); 1420a31ad463SRuss Weight return -EINVAL; 1421a31ad463SRuss Weight } 1422a31ad463SRuss Weight 1423a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 1424185b29c6SDan Carpenter list_for_each_entry(tst_iter, &test_upload_list, node) 1425185b29c6SDan Carpenter if (tst_iter->name == test_fw_config->upload_name) { 1426185b29c6SDan Carpenter tst = tst_iter; 1427a31ad463SRuss Weight break; 1428185b29c6SDan Carpenter } 1429a31ad463SRuss Weight 1430185b29c6SDan Carpenter if (!tst) { 1431a31ad463SRuss Weight pr_err("Firmware name not found: %s\n", 1432a31ad463SRuss Weight test_fw_config->upload_name); 1433a31ad463SRuss Weight goto out; 1434a31ad463SRuss Weight } 1435a31ad463SRuss Weight 1436a31ad463SRuss Weight if (tst->size > PAGE_SIZE) { 1437a31ad463SRuss Weight pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 1438a31ad463SRuss Weight goto out; 1439a31ad463SRuss Weight } 1440a31ad463SRuss Weight 1441a31ad463SRuss Weight memcpy(buf, tst->buf, tst->size); 1442a31ad463SRuss Weight ret = tst->size; 1443a31ad463SRuss Weight out: 1444a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1445a31ad463SRuss Weight return ret; 1446a31ad463SRuss Weight } 1447a31ad463SRuss Weight static DEVICE_ATTR_RO(upload_read); 1448a31ad463SRuss Weight 1449083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr 1450083a93b0SLuis R. Rodriguez 1451083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = { 1452c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(reset), 1453c92316bfSLuis R. Rodriguez 1454c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config), 1455c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_name), 1456c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_num_requests), 14577feebfa4SScott Branden TEST_FW_DEV_ATTR(config_into_buf), 14585d90e05cSScott Branden TEST_FW_DEV_ATTR(config_buf_size), 14595d90e05cSScott Branden TEST_FW_DEV_ATTR(config_file_offset), 14605d90e05cSScott Branden TEST_FW_DEV_ATTR(config_partial), 1461c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_sync_direct), 1462c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_send_uevent), 1463c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_read_fw_idx), 1464a31ad463SRuss Weight TEST_FW_DEV_ATTR(config_upload_name), 1465c92316bfSLuis R. Rodriguez 1466c92316bfSLuis R. Rodriguez /* These don't use the config at all - they could be ported! */ 1467083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_request), 1468083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_async_request), 1469061132d2SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_custom_fallback), 1470548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 1471548193cbSHans de Goede TEST_FW_DEV_ATTR(trigger_request_platform), 1472548193cbSHans de Goede #endif 1473c92316bfSLuis R. Rodriguez 1474c92316bfSLuis R. Rodriguez /* These use the config and can use the test_result */ 1475c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests), 1476c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests_async), 1477c92316bfSLuis R. Rodriguez 1478c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(release_all_firmware), 1479c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(test_result), 1480c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(read_firmware), 1481a31ad463SRuss Weight TEST_FW_DEV_ATTR(upload_read), 1482a31ad463SRuss Weight TEST_FW_DEV_ATTR(upload_register), 1483a31ad463SRuss Weight TEST_FW_DEV_ATTR(upload_unregister), 1484083a93b0SLuis R. Rodriguez NULL, 1485083a93b0SLuis R. Rodriguez }; 1486083a93b0SLuis R. Rodriguez 1487083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev); 1488083a93b0SLuis R. Rodriguez 148967fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = { 149067fd553cSLuis R. Rodriguez .minor = MISC_DYNAMIC_MINOR, 149167fd553cSLuis R. Rodriguez .name = "test_firmware", 149267fd553cSLuis R. Rodriguez .fops = &test_fw_fops, 1493083a93b0SLuis R. Rodriguez .groups = test_dev_groups, 149467fd553cSLuis R. Rodriguez }; 149567fd553cSLuis R. Rodriguez 14960a8adf58SKees Cook static int __init test_firmware_init(void) 14970a8adf58SKees Cook { 14980a8adf58SKees Cook int rc; 14990a8adf58SKees Cook 1500c92316bfSLuis R. Rodriguez test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL); 1501c92316bfSLuis R. Rodriguez if (!test_fw_config) 1502c92316bfSLuis R. Rodriguez return -ENOMEM; 1503c92316bfSLuis R. Rodriguez 1504c92316bfSLuis R. Rodriguez rc = __test_firmware_config_init(); 1505d4fddac5SWenwen Wang if (rc) { 1506d4fddac5SWenwen Wang kfree(test_fw_config); 1507d4fddac5SWenwen Wang pr_err("could not init firmware test config: %d\n", rc); 1508c92316bfSLuis R. Rodriguez return rc; 1509d4fddac5SWenwen Wang } 1510c92316bfSLuis R. Rodriguez 15110a8adf58SKees Cook rc = misc_register(&test_fw_misc_device); 15120a8adf58SKees Cook if (rc) { 15137610615eSZhengchao Shao __test_firmware_config_free(); 1514c92316bfSLuis R. Rodriguez kfree(test_fw_config); 15150a8adf58SKees Cook pr_err("could not register misc device: %d\n", rc); 15160a8adf58SKees Cook return rc; 15170a8adf58SKees Cook } 1518eb910947SBrian Norris 15190a8adf58SKees Cook pr_warn("interface ready\n"); 15200a8adf58SKees Cook 15210a8adf58SKees Cook return 0; 15220a8adf58SKees Cook } 15230a8adf58SKees Cook 15240a8adf58SKees Cook module_init(test_firmware_init); 15250a8adf58SKees Cook 15260a8adf58SKees Cook static void __exit test_firmware_exit(void) 15270a8adf58SKees Cook { 1528c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 15290a8adf58SKees Cook release_firmware(test_firmware); 15300a8adf58SKees Cook misc_deregister(&test_fw_misc_device); 1531a31ad463SRuss Weight upload_release_all(); 1532c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 1533c92316bfSLuis R. Rodriguez kfree(test_fw_config); 1534c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1535c92316bfSLuis R. Rodriguez 15360a8adf58SKees Cook pr_warn("removed interface\n"); 15370a8adf58SKees Cook } 15380a8adf58SKees Cook 15390a8adf58SKees Cook module_exit(test_firmware_exit); 15400a8adf58SKees Cook 15410a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>"); 15420a8adf58SKees Cook MODULE_LICENSE("GPL"); 1543