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; 4848e15602SMirsad Goran Todorovac const char *fw_buf; 49c92316bfSLuis R. Rodriguez struct completion completion; 50c92316bfSLuis R. Rodriguez struct task_struct *task; 51c92316bfSLuis R. Rodriguez struct device *dev; 52c92316bfSLuis R. Rodriguez }; 53c92316bfSLuis R. Rodriguez 54c92316bfSLuis R. Rodriguez /** 551ad5288fSRandy Dunlap * struct test_config - represents configuration for the test for different triggers 56c92316bfSLuis R. Rodriguez * 57c92316bfSLuis R. Rodriguez * @name: the name of the firmware file to look for 587feebfa4SScott Branden * @into_buf: when the into_buf is used if this is true 597feebfa4SScott Branden * request_firmware_into_buf() will be used instead. 605d90e05cSScott Branden * @buf_size: size of buf to allocate when into_buf is true 615d90e05cSScott Branden * @file_offset: file offset to request when calling request_firmware_into_buf 625d90e05cSScott Branden * @partial: partial read opt when calling request_firmware_into_buf 63c92316bfSLuis R. Rodriguez * @sync_direct: when the sync trigger is used if this is true 64c92316bfSLuis R. Rodriguez * request_firmware_direct() will be used instead. 65c92316bfSLuis R. Rodriguez * @send_uevent: whether or not to send a uevent for async requests 66c92316bfSLuis R. Rodriguez * @num_requests: number of requests to try per test case. This is trigger 67c92316bfSLuis R. Rodriguez * specific. 68c92316bfSLuis R. Rodriguez * @reqs: stores all requests information 69c92316bfSLuis R. Rodriguez * @read_fw_idx: index of thread from which we want to read firmware results 70c92316bfSLuis R. Rodriguez * from through the read_fw trigger. 71a31ad463SRuss Weight * @upload_name: firmware name to be used with upload_read sysfs node 72c92316bfSLuis R. Rodriguez * @test_result: a test may use this to collect the result from the call 73c92316bfSLuis R. Rodriguez * of the request_firmware*() calls used in their tests. In order of 74c92316bfSLuis R. Rodriguez * priority we always keep first any setup error. If no setup errors were 75c92316bfSLuis R. Rodriguez * found then we move on to the first error encountered while running the 76c92316bfSLuis R. Rodriguez * API. Note that for async calls this typically will be a successful 77c92316bfSLuis R. Rodriguez * result (0) unless of course you've used bogus parameters, or the system 78c92316bfSLuis R. Rodriguez * is out of memory. In the async case the callback is expected to do a 79c92316bfSLuis R. Rodriguez * bit more homework to figure out what happened, unfortunately the only 80c92316bfSLuis R. Rodriguez * information passed today on error is the fact that no firmware was 81c92316bfSLuis R. Rodriguez * found so we can only assume -ENOENT on async calls if the firmware is 82c92316bfSLuis R. Rodriguez * NULL. 83c92316bfSLuis R. Rodriguez * 84c92316bfSLuis R. Rodriguez * Errors you can expect: 85c92316bfSLuis R. Rodriguez * 86c92316bfSLuis R. Rodriguez * API specific: 87c92316bfSLuis R. Rodriguez * 88c92316bfSLuis R. Rodriguez * 0: success for sync, for async it means request was sent 89c92316bfSLuis R. Rodriguez * -EINVAL: invalid parameters or request 90c92316bfSLuis R. Rodriguez * -ENOENT: files not found 91c92316bfSLuis R. Rodriguez * 92c92316bfSLuis R. Rodriguez * System environment: 93c92316bfSLuis R. Rodriguez * 94c92316bfSLuis R. Rodriguez * -ENOMEM: memory pressure on system 95c92316bfSLuis R. Rodriguez * -ENODEV: out of number of devices to test 96c92316bfSLuis R. Rodriguez * -EINVAL: an unexpected error has occurred 97c92316bfSLuis R. Rodriguez * @req_firmware: if @sync_direct is true this is set to 98c92316bfSLuis R. Rodriguez * request_firmware_direct(), otherwise request_firmware() 99c92316bfSLuis R. Rodriguez */ 100c92316bfSLuis R. Rodriguez struct test_config { 101c92316bfSLuis R. Rodriguez char *name; 1027feebfa4SScott Branden bool into_buf; 1035d90e05cSScott Branden size_t buf_size; 1045d90e05cSScott Branden size_t file_offset; 1055d90e05cSScott Branden bool partial; 106c92316bfSLuis R. Rodriguez bool sync_direct; 107c92316bfSLuis R. Rodriguez bool send_uevent; 108c92316bfSLuis R. Rodriguez u8 num_requests; 109c92316bfSLuis R. Rodriguez u8 read_fw_idx; 110a31ad463SRuss Weight char *upload_name; 111c92316bfSLuis R. Rodriguez 112c92316bfSLuis R. Rodriguez /* 113c92316bfSLuis R. Rodriguez * These below don't belong her but we'll move them once we create 114c92316bfSLuis R. Rodriguez * a struct fw_test_device and stuff the misc_dev under there later. 115c92316bfSLuis R. Rodriguez */ 116c92316bfSLuis R. Rodriguez struct test_batched_req *reqs; 117c92316bfSLuis R. Rodriguez int test_result; 118c92316bfSLuis R. Rodriguez int (*req_firmware)(const struct firmware **fw, const char *name, 119c92316bfSLuis R. Rodriguez struct device *device); 120c92316bfSLuis R. Rodriguez }; 121c92316bfSLuis R. Rodriguez 1224a4e975bSRuss Weight struct upload_inject_err { 1234a4e975bSRuss Weight const char *prog; 1244a4e975bSRuss Weight enum fw_upload_err err_code; 1254a4e975bSRuss Weight }; 1264a4e975bSRuss Weight 127a31ad463SRuss Weight struct test_firmware_upload { 128a31ad463SRuss Weight char *name; 129a31ad463SRuss Weight struct list_head node; 130a31ad463SRuss Weight char *buf; 131a31ad463SRuss Weight size_t size; 132a31ad463SRuss Weight bool cancel_request; 1334a4e975bSRuss Weight struct upload_inject_err inject; 134a31ad463SRuss Weight struct fw_upload *fwl; 135a31ad463SRuss Weight }; 136a31ad463SRuss Weight 13776f8ab1bSWei Yongjun static struct test_config *test_fw_config; 138c92316bfSLuis R. Rodriguez 139a31ad463SRuss Weight static struct test_firmware_upload *upload_lookup_name(const char *name) 140a31ad463SRuss Weight { 141a31ad463SRuss Weight struct test_firmware_upload *tst; 142a31ad463SRuss Weight 143a31ad463SRuss Weight list_for_each_entry(tst, &test_upload_list, node) 144a31ad463SRuss Weight if (strncmp(name, tst->name, strlen(tst->name)) == 0) 145a31ad463SRuss Weight return tst; 146a31ad463SRuss Weight 147a31ad463SRuss Weight return NULL; 148a31ad463SRuss Weight } 149a31ad463SRuss Weight 1500a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 1510a8adf58SKees Cook size_t size, loff_t *offset) 1520a8adf58SKees Cook { 1530a8adf58SKees Cook ssize_t rc = 0; 1540a8adf58SKees Cook 1550a8adf58SKees Cook mutex_lock(&test_fw_mutex); 1560a8adf58SKees Cook if (test_firmware) 1570a8adf58SKees Cook rc = simple_read_from_buffer(buf, size, offset, 1580a8adf58SKees Cook test_firmware->data, 1590a8adf58SKees Cook test_firmware->size); 1600a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 1610a8adf58SKees Cook return rc; 1620a8adf58SKees Cook } 1630a8adf58SKees Cook 1640a8adf58SKees Cook static const struct file_operations test_fw_fops = { 1650a8adf58SKees Cook .owner = THIS_MODULE, 1660a8adf58SKees Cook .read = test_fw_misc_read, 1670a8adf58SKees Cook }; 1680a8adf58SKees Cook 169c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void) 170c92316bfSLuis R. Rodriguez { 171c92316bfSLuis R. Rodriguez struct test_batched_req *req; 172c92316bfSLuis R. Rodriguez u8 i; 173c92316bfSLuis R. Rodriguez 174c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) 175c92316bfSLuis R. Rodriguez return; 176c92316bfSLuis R. Rodriguez 177c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 178c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 17948e15602SMirsad Goran Todorovac if (req->fw) { 18048e15602SMirsad Goran Todorovac if (req->fw_buf) { 18148e15602SMirsad Goran Todorovac kfree_const(req->fw_buf); 18248e15602SMirsad Goran Todorovac req->fw_buf = NULL; 18348e15602SMirsad Goran Todorovac } 184c92316bfSLuis R. Rodriguez release_firmware(req->fw); 18548e15602SMirsad Goran Todorovac req->fw = NULL; 18648e15602SMirsad Goran Todorovac } 187c92316bfSLuis R. Rodriguez } 188c92316bfSLuis R. Rodriguez 189c92316bfSLuis R. Rodriguez vfree(test_fw_config->reqs); 190c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 191c92316bfSLuis R. Rodriguez } 192c92316bfSLuis R. Rodriguez 193c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void) 194c92316bfSLuis R. Rodriguez { 195c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 196c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 197c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 198c92316bfSLuis R. Rodriguez } 199c92316bfSLuis R. Rodriguez 200c92316bfSLuis R. Rodriguez 201c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void) 202c92316bfSLuis R. Rodriguez { 203c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 204c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 205c92316bfSLuis R. Rodriguez test_fw_config->name = NULL; 206c92316bfSLuis R. Rodriguez } 207c92316bfSLuis R. Rodriguez 208c92316bfSLuis R. Rodriguez /* 209c92316bfSLuis R. Rodriguez * XXX: move to kstrncpy() once merged. 210c92316bfSLuis R. Rodriguez * 211c92316bfSLuis R. Rodriguez * Users should use kfree_const() when freeing these. 212c92316bfSLuis R. Rodriguez */ 213c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp) 214c92316bfSLuis R. Rodriguez { 215c92316bfSLuis R. Rodriguez *dst = kstrndup(name, count, gfp); 216c92316bfSLuis R. Rodriguez if (!*dst) 2177dae593cSMirsad Goran Todorovac return -ENOMEM; 218c92316bfSLuis R. Rodriguez return count; 219c92316bfSLuis R. Rodriguez } 220c92316bfSLuis R. Rodriguez 221c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void) 222c92316bfSLuis R. Rodriguez { 223c92316bfSLuis R. Rodriguez int ret; 224c92316bfSLuis R. Rodriguez 225c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME, 226c92316bfSLuis R. Rodriguez strlen(TEST_FIRMWARE_NAME), GFP_KERNEL); 227c92316bfSLuis R. Rodriguez if (ret < 0) 228c92316bfSLuis R. Rodriguez goto out; 229c92316bfSLuis R. Rodriguez 230c92316bfSLuis R. Rodriguez test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS; 231c92316bfSLuis R. Rodriguez test_fw_config->send_uevent = true; 2327feebfa4SScott Branden test_fw_config->into_buf = false; 2335d90e05cSScott Branden test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE; 2345d90e05cSScott Branden test_fw_config->file_offset = 0; 2355d90e05cSScott Branden test_fw_config->partial = false; 236c92316bfSLuis R. Rodriguez test_fw_config->sync_direct = false; 237c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = request_firmware; 238c92316bfSLuis R. Rodriguez test_fw_config->test_result = 0; 239c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 240a31ad463SRuss Weight test_fw_config->upload_name = NULL; 241c92316bfSLuis R. Rodriguez 242c92316bfSLuis R. Rodriguez return 0; 243c92316bfSLuis R. Rodriguez 244c92316bfSLuis R. Rodriguez out: 245c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 246c92316bfSLuis R. Rodriguez return ret; 247c92316bfSLuis R. Rodriguez } 248c92316bfSLuis R. Rodriguez 249c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev, 250c92316bfSLuis R. Rodriguez struct device_attribute *attr, 251c92316bfSLuis R. Rodriguez const char *buf, size_t count) 252c92316bfSLuis R. Rodriguez { 253c92316bfSLuis R. Rodriguez int ret; 254c92316bfSLuis R. Rodriguez 255c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 256c92316bfSLuis R. Rodriguez 257c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 258c92316bfSLuis R. Rodriguez 259c92316bfSLuis R. Rodriguez ret = __test_firmware_config_init(); 260c92316bfSLuis R. Rodriguez if (ret < 0) { 261c92316bfSLuis R. Rodriguez ret = -ENOMEM; 262c92316bfSLuis R. Rodriguez pr_err("could not alloc settings for config trigger: %d\n", 263c92316bfSLuis R. Rodriguez ret); 264c92316bfSLuis R. Rodriguez goto out; 265c92316bfSLuis R. Rodriguez } 266c92316bfSLuis R. Rodriguez 267c92316bfSLuis R. Rodriguez pr_info("reset\n"); 268c92316bfSLuis R. Rodriguez ret = count; 269c92316bfSLuis R. Rodriguez 270c92316bfSLuis R. Rodriguez out: 271c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 272c92316bfSLuis R. Rodriguez 273c92316bfSLuis R. Rodriguez return ret; 274c92316bfSLuis R. Rodriguez } 275c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset); 276c92316bfSLuis R. Rodriguez 277c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev, 278c92316bfSLuis R. Rodriguez struct device_attribute *attr, 279c92316bfSLuis R. Rodriguez char *buf) 280c92316bfSLuis R. Rodriguez { 281c92316bfSLuis R. Rodriguez int len = 0; 282c92316bfSLuis R. Rodriguez 283c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 284c92316bfSLuis R. Rodriguez 285bd17cc5aSDan Carpenter len += scnprintf(buf, PAGE_SIZE - len, 286c92316bfSLuis R. Rodriguez "Custom trigger configuration for: %s\n", 287c92316bfSLuis R. Rodriguez dev_name(dev)); 288c92316bfSLuis R. Rodriguez 289c92316bfSLuis R. Rodriguez if (test_fw_config->name) 290bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 291c92316bfSLuis R. Rodriguez "name:\t%s\n", 292c92316bfSLuis R. Rodriguez test_fw_config->name); 293c92316bfSLuis R. Rodriguez else 294bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 295d88bd098SColin Ian King "name:\tEMPTY\n"); 296c92316bfSLuis R. Rodriguez 297bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 298c92316bfSLuis R. Rodriguez "num_requests:\t%u\n", test_fw_config->num_requests); 299c92316bfSLuis R. Rodriguez 300bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 301c92316bfSLuis R. Rodriguez "send_uevent:\t\t%s\n", 302c92316bfSLuis R. Rodriguez test_fw_config->send_uevent ? 3030733d839SShawn Guo "FW_ACTION_UEVENT" : 3040733d839SShawn Guo "FW_ACTION_NOUEVENT"); 305bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 3067feebfa4SScott Branden "into_buf:\t\t%s\n", 3077feebfa4SScott Branden test_fw_config->into_buf ? "true" : "false"); 3087feebfa4SScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 3095d90e05cSScott Branden "buf_size:\t%zu\n", test_fw_config->buf_size); 3105d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 3115d90e05cSScott Branden "file_offset:\t%zu\n", test_fw_config->file_offset); 3125d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 3135d90e05cSScott Branden "partial:\t\t%s\n", 3145d90e05cSScott Branden test_fw_config->partial ? "true" : "false"); 3155d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 316c92316bfSLuis R. Rodriguez "sync_direct:\t\t%s\n", 317c92316bfSLuis R. Rodriguez test_fw_config->sync_direct ? "true" : "false"); 318bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 319c92316bfSLuis R. Rodriguez "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); 320a31ad463SRuss Weight if (test_fw_config->upload_name) 321a31ad463SRuss Weight len += scnprintf(buf + len, PAGE_SIZE - len, 322a31ad463SRuss Weight "upload_name:\t%s\n", 323a31ad463SRuss Weight test_fw_config->upload_name); 324a31ad463SRuss Weight else 325a31ad463SRuss Weight len += scnprintf(buf + len, PAGE_SIZE - len, 326d88bd098SColin Ian King "upload_name:\tEMPTY\n"); 327c92316bfSLuis R. Rodriguez 328c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 329c92316bfSLuis R. Rodriguez 330c92316bfSLuis R. Rodriguez return len; 331c92316bfSLuis R. Rodriguez } 332c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config); 333c92316bfSLuis R. Rodriguez 334c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev, 335c92316bfSLuis R. Rodriguez struct device_attribute *attr, 336c92316bfSLuis R. Rodriguez const char *buf, size_t count) 337c92316bfSLuis R. Rodriguez { 338c92316bfSLuis R. Rodriguez int ret; 339c92316bfSLuis R. Rodriguez 340c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 341c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 342c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL); 343c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 344c92316bfSLuis R. Rodriguez 345c92316bfSLuis R. Rodriguez return ret; 346c92316bfSLuis R. Rodriguez } 347c92316bfSLuis R. Rodriguez 348c92316bfSLuis R. Rodriguez /* 349c92316bfSLuis R. Rodriguez * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE. 350c92316bfSLuis R. Rodriguez */ 351c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst, 352c92316bfSLuis R. Rodriguez char *src) 353c92316bfSLuis R. Rodriguez { 354c92316bfSLuis R. Rodriguez int len; 355c92316bfSLuis R. Rodriguez 356c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 357c92316bfSLuis R. Rodriguez len = snprintf(dst, PAGE_SIZE, "%s\n", src); 358c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 359c92316bfSLuis R. Rodriguez 360c92316bfSLuis R. Rodriguez return len; 361c92316bfSLuis R. Rodriguez } 362c92316bfSLuis R. Rodriguez 3634acfe3dfSMirsad Goran Todorovac static inline int __test_dev_config_update_bool(const char *buf, size_t size, 3644acfe3dfSMirsad Goran Todorovac bool *cfg) 3654acfe3dfSMirsad Goran Todorovac { 3664acfe3dfSMirsad Goran Todorovac int ret; 3674acfe3dfSMirsad Goran Todorovac 3684acfe3dfSMirsad Goran Todorovac if (kstrtobool(buf, cfg) < 0) 3694acfe3dfSMirsad Goran Todorovac ret = -EINVAL; 3704acfe3dfSMirsad Goran Todorovac else 3714acfe3dfSMirsad Goran Todorovac ret = size; 3724acfe3dfSMirsad Goran Todorovac 3734acfe3dfSMirsad Goran Todorovac return ret; 3744acfe3dfSMirsad Goran Todorovac } 3754acfe3dfSMirsad Goran Todorovac 376c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size, 377c92316bfSLuis R. Rodriguez bool *cfg) 378c92316bfSLuis R. Rodriguez { 379c92316bfSLuis R. Rodriguez int ret; 380c92316bfSLuis R. Rodriguez 381c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 3824acfe3dfSMirsad Goran Todorovac ret = __test_dev_config_update_bool(buf, size, cfg); 383c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 384c92316bfSLuis R. Rodriguez 385c92316bfSLuis R. Rodriguez return ret; 386c92316bfSLuis R. Rodriguez } 387c92316bfSLuis R. Rodriguez 38855623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val) 389c92316bfSLuis R. Rodriguez { 390c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 391c92316bfSLuis R. Rodriguez } 392c92316bfSLuis R. Rodriguez 3934acfe3dfSMirsad Goran Todorovac static int __test_dev_config_update_size_t( 3944acfe3dfSMirsad Goran Todorovac const char *buf, 3955d90e05cSScott Branden size_t size, 3965d90e05cSScott Branden size_t *cfg) 3975d90e05cSScott Branden { 3985d90e05cSScott Branden int ret; 3995d90e05cSScott Branden long new; 4005d90e05cSScott Branden 4015d90e05cSScott Branden ret = kstrtol(buf, 10, &new); 4025d90e05cSScott Branden if (ret) 4035d90e05cSScott Branden return ret; 4045d90e05cSScott Branden 4055d90e05cSScott Branden *(size_t *)cfg = new; 4065d90e05cSScott Branden 4075d90e05cSScott Branden /* Always return full write size even if we didn't consume all */ 4085d90e05cSScott Branden return size; 4095d90e05cSScott Branden } 4105d90e05cSScott Branden 4115d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val) 4125d90e05cSScott Branden { 4135d90e05cSScott Branden return snprintf(buf, PAGE_SIZE, "%zu\n", val); 4145d90e05cSScott Branden } 4155d90e05cSScott Branden 41655623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val) 417c92316bfSLuis R. Rodriguez { 418c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 419c92316bfSLuis R. Rodriguez } 420c92316bfSLuis R. Rodriguez 4214acfe3dfSMirsad Goran Todorovac static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 422c92316bfSLuis R. Rodriguez { 423506dfc99SAlexey Dobriyan u8 val; 424c92316bfSLuis R. Rodriguez int ret; 425c92316bfSLuis R. Rodriguez 426506dfc99SAlexey Dobriyan ret = kstrtou8(buf, 10, &val); 427c92316bfSLuis R. Rodriguez if (ret) 428c92316bfSLuis R. Rodriguez return ret; 429c92316bfSLuis R. Rodriguez 430506dfc99SAlexey Dobriyan *(u8 *)cfg = val; 431c92316bfSLuis R. Rodriguez 432c92316bfSLuis R. Rodriguez /* Always return full write size even if we didn't consume all */ 433c92316bfSLuis R. Rodriguez return size; 434c92316bfSLuis R. Rodriguez } 435c92316bfSLuis R. Rodriguez 4364acfe3dfSMirsad Goran Todorovac static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 4374acfe3dfSMirsad Goran Todorovac { 4384acfe3dfSMirsad Goran Todorovac int ret; 4394acfe3dfSMirsad Goran Todorovac 4404acfe3dfSMirsad Goran Todorovac mutex_lock(&test_fw_mutex); 4414acfe3dfSMirsad Goran Todorovac ret = __test_dev_config_update_u8(buf, size, cfg); 4424acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 4434acfe3dfSMirsad Goran Todorovac 4444acfe3dfSMirsad Goran Todorovac return ret; 4454acfe3dfSMirsad Goran Todorovac } 4464acfe3dfSMirsad Goran Todorovac 44755623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val) 448c92316bfSLuis R. Rodriguez { 449c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%u\n", val); 450c92316bfSLuis R. Rodriguez } 451c92316bfSLuis R. Rodriguez 452c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev, 453c92316bfSLuis R. Rodriguez struct device_attribute *attr, 454c92316bfSLuis R. Rodriguez char *buf) 455c92316bfSLuis R. Rodriguez { 456c92316bfSLuis R. Rodriguez return config_test_show_str(buf, test_fw_config->name); 457c92316bfSLuis R. Rodriguez } 458b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name); 459c92316bfSLuis R. Rodriguez 460a31ad463SRuss Weight static ssize_t config_upload_name_store(struct device *dev, 461a31ad463SRuss Weight struct device_attribute *attr, 462a31ad463SRuss Weight const char *buf, size_t count) 463a31ad463SRuss Weight { 464a31ad463SRuss Weight struct test_firmware_upload *tst; 465a31ad463SRuss Weight int ret = count; 466a31ad463SRuss Weight 467a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 468a31ad463SRuss Weight tst = upload_lookup_name(buf); 469a31ad463SRuss Weight if (tst) 470a31ad463SRuss Weight test_fw_config->upload_name = tst->name; 471a31ad463SRuss Weight else 472a31ad463SRuss Weight ret = -EINVAL; 473a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 474a31ad463SRuss Weight 475a31ad463SRuss Weight return ret; 476a31ad463SRuss Weight } 477a31ad463SRuss Weight 478a31ad463SRuss Weight static ssize_t config_upload_name_show(struct device *dev, 479a31ad463SRuss Weight struct device_attribute *attr, 480a31ad463SRuss Weight char *buf) 481a31ad463SRuss Weight { 482a31ad463SRuss Weight return config_test_show_str(buf, test_fw_config->upload_name); 483a31ad463SRuss Weight } 484a31ad463SRuss Weight static DEVICE_ATTR_RW(config_upload_name); 485a31ad463SRuss Weight 486c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev, 487c92316bfSLuis R. Rodriguez struct device_attribute *attr, 488c92316bfSLuis R. Rodriguez const char *buf, size_t count) 489c92316bfSLuis R. Rodriguez { 490c92316bfSLuis R. Rodriguez int rc; 491c92316bfSLuis R. Rodriguez 492c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 493c92316bfSLuis R. Rodriguez if (test_fw_config->reqs) { 494c92316bfSLuis R. Rodriguez pr_err("Must call release_all_firmware prior to changing config\n"); 495c92316bfSLuis R. Rodriguez rc = -EINVAL; 496a5e19233SWei Yongjun mutex_unlock(&test_fw_mutex); 497c92316bfSLuis R. Rodriguez goto out; 498c92316bfSLuis R. Rodriguez } 499c92316bfSLuis R. Rodriguez 5004acfe3dfSMirsad Goran Todorovac rc = __test_dev_config_update_u8(buf, count, 501c92316bfSLuis R. Rodriguez &test_fw_config->num_requests); 5024acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 503c92316bfSLuis R. Rodriguez 504c92316bfSLuis R. Rodriguez out: 505c92316bfSLuis R. Rodriguez return rc; 506c92316bfSLuis R. Rodriguez } 507c92316bfSLuis R. Rodriguez 508c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev, 509c92316bfSLuis R. Rodriguez struct device_attribute *attr, 510c92316bfSLuis R. Rodriguez char *buf) 511c92316bfSLuis R. Rodriguez { 512c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->num_requests); 513c92316bfSLuis R. Rodriguez } 514b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests); 515c92316bfSLuis R. Rodriguez 5167feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev, 5177feebfa4SScott Branden struct device_attribute *attr, 5187feebfa4SScott Branden const char *buf, size_t count) 5197feebfa4SScott Branden { 5207feebfa4SScott Branden return test_dev_config_update_bool(buf, 5217feebfa4SScott Branden count, 5227feebfa4SScott Branden &test_fw_config->into_buf); 5237feebfa4SScott Branden } 5247feebfa4SScott Branden 5257feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev, 5267feebfa4SScott Branden struct device_attribute *attr, 5277feebfa4SScott Branden char *buf) 5287feebfa4SScott Branden { 5297feebfa4SScott Branden return test_dev_config_show_bool(buf, test_fw_config->into_buf); 5307feebfa4SScott Branden } 5317feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf); 5327feebfa4SScott Branden 5335d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev, 5345d90e05cSScott Branden struct device_attribute *attr, 5355d90e05cSScott Branden const char *buf, size_t count) 5365d90e05cSScott Branden { 5375d90e05cSScott Branden int rc; 5385d90e05cSScott Branden 5395d90e05cSScott Branden mutex_lock(&test_fw_mutex); 5405d90e05cSScott Branden if (test_fw_config->reqs) { 5415d90e05cSScott Branden pr_err("Must call release_all_firmware prior to changing config\n"); 5425d90e05cSScott Branden rc = -EINVAL; 5435d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 5445d90e05cSScott Branden goto out; 5455d90e05cSScott Branden } 5465d90e05cSScott Branden 5474acfe3dfSMirsad Goran Todorovac rc = __test_dev_config_update_size_t(buf, count, 5485d90e05cSScott Branden &test_fw_config->buf_size); 5494acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 5505d90e05cSScott Branden 5515d90e05cSScott Branden out: 5525d90e05cSScott Branden return rc; 5535d90e05cSScott Branden } 5545d90e05cSScott Branden 5555d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev, 5565d90e05cSScott Branden struct device_attribute *attr, 5575d90e05cSScott Branden char *buf) 5585d90e05cSScott Branden { 5595d90e05cSScott Branden return test_dev_config_show_size_t(buf, test_fw_config->buf_size); 5605d90e05cSScott Branden } 5615d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size); 5625d90e05cSScott Branden 5635d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev, 5645d90e05cSScott Branden struct device_attribute *attr, 5655d90e05cSScott Branden const char *buf, size_t count) 5665d90e05cSScott Branden { 5675d90e05cSScott Branden int rc; 5685d90e05cSScott Branden 5695d90e05cSScott Branden mutex_lock(&test_fw_mutex); 5705d90e05cSScott Branden if (test_fw_config->reqs) { 5715d90e05cSScott Branden pr_err("Must call release_all_firmware prior to changing config\n"); 5725d90e05cSScott Branden rc = -EINVAL; 5735d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 5745d90e05cSScott Branden goto out; 5755d90e05cSScott Branden } 5765d90e05cSScott Branden 5774acfe3dfSMirsad Goran Todorovac rc = __test_dev_config_update_size_t(buf, count, 5785d90e05cSScott Branden &test_fw_config->file_offset); 5794acfe3dfSMirsad Goran Todorovac mutex_unlock(&test_fw_mutex); 5805d90e05cSScott Branden 5815d90e05cSScott Branden out: 5825d90e05cSScott Branden return rc; 5835d90e05cSScott Branden } 5845d90e05cSScott Branden 5855d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev, 5865d90e05cSScott Branden struct device_attribute *attr, 5875d90e05cSScott Branden char *buf) 5885d90e05cSScott Branden { 5895d90e05cSScott Branden return test_dev_config_show_size_t(buf, test_fw_config->file_offset); 5905d90e05cSScott Branden } 5915d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset); 5925d90e05cSScott Branden 5935d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev, 5945d90e05cSScott Branden struct device_attribute *attr, 5955d90e05cSScott Branden const char *buf, size_t count) 5965d90e05cSScott Branden { 5975d90e05cSScott Branden return test_dev_config_update_bool(buf, 5985d90e05cSScott Branden count, 5995d90e05cSScott Branden &test_fw_config->partial); 6005d90e05cSScott Branden } 6015d90e05cSScott Branden 6025d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev, 6035d90e05cSScott Branden struct device_attribute *attr, 6045d90e05cSScott Branden char *buf) 6055d90e05cSScott Branden { 6065d90e05cSScott Branden return test_dev_config_show_bool(buf, test_fw_config->partial); 6075d90e05cSScott Branden } 6085d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial); 6095d90e05cSScott Branden 610c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev, 611c92316bfSLuis R. Rodriguez struct device_attribute *attr, 612c92316bfSLuis R. Rodriguez const char *buf, size_t count) 613c92316bfSLuis R. Rodriguez { 614c92316bfSLuis R. Rodriguez int rc = test_dev_config_update_bool(buf, count, 615c92316bfSLuis R. Rodriguez &test_fw_config->sync_direct); 616c92316bfSLuis R. Rodriguez 617c92316bfSLuis R. Rodriguez if (rc == count) 618c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = test_fw_config->sync_direct ? 619c92316bfSLuis R. Rodriguez request_firmware_direct : 620c92316bfSLuis R. Rodriguez request_firmware; 621c92316bfSLuis R. Rodriguez return rc; 622c92316bfSLuis R. Rodriguez } 623c92316bfSLuis R. Rodriguez 624c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev, 625c92316bfSLuis R. Rodriguez struct device_attribute *attr, 626c92316bfSLuis R. Rodriguez char *buf) 627c92316bfSLuis R. Rodriguez { 628c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->sync_direct); 629c92316bfSLuis R. Rodriguez } 630b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct); 631c92316bfSLuis R. Rodriguez 632c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev, 633c92316bfSLuis R. Rodriguez struct device_attribute *attr, 634c92316bfSLuis R. Rodriguez const char *buf, size_t count) 635c92316bfSLuis R. Rodriguez { 636c92316bfSLuis R. Rodriguez return test_dev_config_update_bool(buf, count, 637c92316bfSLuis R. Rodriguez &test_fw_config->send_uevent); 638c92316bfSLuis R. Rodriguez } 639c92316bfSLuis R. Rodriguez 640c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev, 641c92316bfSLuis R. Rodriguez struct device_attribute *attr, 642c92316bfSLuis R. Rodriguez char *buf) 643c92316bfSLuis R. Rodriguez { 644c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->send_uevent); 645c92316bfSLuis R. Rodriguez } 646b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent); 647c92316bfSLuis R. Rodriguez 648c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev, 649c92316bfSLuis R. Rodriguez struct device_attribute *attr, 650c92316bfSLuis R. Rodriguez const char *buf, size_t count) 651c92316bfSLuis R. Rodriguez { 652c92316bfSLuis R. Rodriguez return test_dev_config_update_u8(buf, count, 653c92316bfSLuis R. Rodriguez &test_fw_config->read_fw_idx); 654c92316bfSLuis R. Rodriguez } 655c92316bfSLuis R. Rodriguez 656c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev, 657c92316bfSLuis R. Rodriguez struct device_attribute *attr, 658c92316bfSLuis R. Rodriguez char *buf) 659c92316bfSLuis R. Rodriguez { 660c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx); 661c92316bfSLuis R. Rodriguez } 662b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx); 663c92316bfSLuis R. Rodriguez 664c92316bfSLuis R. Rodriguez 6650a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev, 6660a8adf58SKees Cook struct device_attribute *attr, 6670a8adf58SKees Cook const char *buf, size_t count) 6680a8adf58SKees Cook { 6690a8adf58SKees Cook int rc; 6700a8adf58SKees Cook char *name; 6710a8adf58SKees Cook 672be4a1326SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 6730a8adf58SKees Cook if (!name) 6747dae593cSMirsad Goran Todorovac return -ENOMEM; 6750a8adf58SKees Cook 6760a8adf58SKees Cook pr_info("loading '%s'\n", name); 6770a8adf58SKees Cook 6780a8adf58SKees Cook mutex_lock(&test_fw_mutex); 6790a8adf58SKees Cook release_firmware(test_firmware); 68048e15602SMirsad Goran Todorovac if (test_fw_config->reqs) 68148e15602SMirsad Goran Todorovac __test_release_all_firmware(); 6820a8adf58SKees Cook test_firmware = NULL; 6830a8adf58SKees Cook rc = request_firmware(&test_firmware, name, dev); 68447e0bbb7SBrian Norris if (rc) { 6850a8adf58SKees Cook pr_info("load of '%s' failed: %d\n", name, rc); 68647e0bbb7SBrian Norris goto out; 68747e0bbb7SBrian Norris } 68847e0bbb7SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 68947e0bbb7SBrian Norris rc = count; 69047e0bbb7SBrian Norris 69147e0bbb7SBrian Norris out: 6920a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 6930a8adf58SKees Cook 6940a8adf58SKees Cook kfree(name); 6950a8adf58SKees Cook 69647e0bbb7SBrian Norris return rc; 6970a8adf58SKees Cook } 6980a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request); 6990a8adf58SKees Cook 700548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 701baaabecfSKees Cook extern struct list_head efi_embedded_fw_list; 702baaabecfSKees Cook extern bool efi_embedded_fw_checked; 703baaabecfSKees Cook 704548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev, 705548193cbSHans de Goede struct device_attribute *attr, 706548193cbSHans de Goede const char *buf, size_t count) 707548193cbSHans de Goede { 708548193cbSHans de Goede static const u8 test_data[] = { 709548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04, 710548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08, 711548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40, 712548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80 713548193cbSHans de Goede }; 714548193cbSHans de Goede struct efi_embedded_fw efi_embedded_fw; 715548193cbSHans de Goede const struct firmware *firmware = NULL; 716baaabecfSKees Cook bool saved_efi_embedded_fw_checked; 717548193cbSHans de Goede char *name; 718548193cbSHans de Goede int rc; 719548193cbSHans de Goede 720548193cbSHans de Goede name = kstrndup(buf, count, GFP_KERNEL); 721548193cbSHans de Goede if (!name) 7227dae593cSMirsad Goran Todorovac return -ENOMEM; 723548193cbSHans de Goede 724548193cbSHans de Goede pr_info("inserting test platform fw '%s'\n", name); 725548193cbSHans de Goede efi_embedded_fw.name = name; 726548193cbSHans de Goede efi_embedded_fw.data = (void *)test_data; 727548193cbSHans de Goede efi_embedded_fw.length = sizeof(test_data); 728548193cbSHans de Goede list_add(&efi_embedded_fw.list, &efi_embedded_fw_list); 729baaabecfSKees Cook saved_efi_embedded_fw_checked = efi_embedded_fw_checked; 730baaabecfSKees Cook efi_embedded_fw_checked = true; 731548193cbSHans de Goede 732548193cbSHans de Goede pr_info("loading '%s'\n", name); 733548193cbSHans de Goede rc = firmware_request_platform(&firmware, name, dev); 734548193cbSHans de Goede if (rc) { 735548193cbSHans de Goede pr_info("load of '%s' failed: %d\n", name, rc); 736548193cbSHans de Goede goto out; 737548193cbSHans de Goede } 738548193cbSHans de Goede if (firmware->size != sizeof(test_data) || 739548193cbSHans de Goede memcmp(firmware->data, test_data, sizeof(test_data)) != 0) { 740548193cbSHans de Goede pr_info("firmware contents mismatch for '%s'\n", name); 741548193cbSHans de Goede rc = -EINVAL; 742548193cbSHans de Goede goto out; 743548193cbSHans de Goede } 744548193cbSHans de Goede pr_info("loaded: %zu\n", firmware->size); 745548193cbSHans de Goede rc = count; 746548193cbSHans de Goede 747548193cbSHans de Goede out: 748baaabecfSKees Cook efi_embedded_fw_checked = saved_efi_embedded_fw_checked; 749548193cbSHans de Goede release_firmware(firmware); 750548193cbSHans de Goede list_del(&efi_embedded_fw.list); 751548193cbSHans de Goede kfree(name); 752548193cbSHans de Goede 753548193cbSHans de Goede return rc; 754548193cbSHans de Goede } 755548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform); 756548193cbSHans de Goede #endif 757548193cbSHans de Goede 758eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done); 759eb910947SBrian Norris 760eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context) 761eb910947SBrian Norris { 762eb910947SBrian Norris test_firmware = fw; 763eb910947SBrian Norris complete(&async_fw_done); 764eb910947SBrian Norris } 765eb910947SBrian Norris 766eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev, 767eb910947SBrian Norris struct device_attribute *attr, 768eb910947SBrian Norris const char *buf, size_t count) 769eb910947SBrian Norris { 770eb910947SBrian Norris int rc; 771eb910947SBrian Norris char *name; 772eb910947SBrian Norris 773eb910947SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 774eb910947SBrian Norris if (!name) 7757dae593cSMirsad Goran Todorovac return -ENOMEM; 776eb910947SBrian Norris 777eb910947SBrian Norris pr_info("loading '%s'\n", name); 778eb910947SBrian Norris 779eb910947SBrian Norris mutex_lock(&test_fw_mutex); 780eb910947SBrian Norris release_firmware(test_firmware); 781eb910947SBrian Norris test_firmware = NULL; 78248e15602SMirsad Goran Todorovac if (test_fw_config->reqs) 78348e15602SMirsad Goran Todorovac __test_release_all_firmware(); 784eb910947SBrian Norris rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 785eb910947SBrian Norris NULL, trigger_async_request_cb); 786eb910947SBrian Norris if (rc) { 787eb910947SBrian Norris pr_info("async load of '%s' failed: %d\n", name, rc); 788eb910947SBrian Norris kfree(name); 789eb910947SBrian Norris goto out; 790eb910947SBrian Norris } 791eb910947SBrian Norris /* Free 'name' ASAP, to test for race conditions */ 792eb910947SBrian Norris kfree(name); 793eb910947SBrian Norris 794eb910947SBrian Norris wait_for_completion(&async_fw_done); 795eb910947SBrian Norris 796eb910947SBrian Norris if (test_firmware) { 797eb910947SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 798eb910947SBrian Norris rc = count; 799eb910947SBrian Norris } else { 800eb910947SBrian Norris pr_err("failed to async load firmware\n"); 8017feebfa4SScott Branden rc = -ENOMEM; 802eb910947SBrian Norris } 803eb910947SBrian Norris 804eb910947SBrian Norris out: 805eb910947SBrian Norris mutex_unlock(&test_fw_mutex); 806eb910947SBrian Norris 807eb910947SBrian Norris return rc; 808eb910947SBrian Norris } 809eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request); 810eb910947SBrian Norris 811061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev, 812061132d2SLuis R. Rodriguez struct device_attribute *attr, 813061132d2SLuis R. Rodriguez const char *buf, size_t count) 814061132d2SLuis R. Rodriguez { 815061132d2SLuis R. Rodriguez int rc; 816061132d2SLuis R. Rodriguez char *name; 817061132d2SLuis R. Rodriguez 818061132d2SLuis R. Rodriguez name = kstrndup(buf, count, GFP_KERNEL); 819061132d2SLuis R. Rodriguez if (!name) 8207dae593cSMirsad Goran Todorovac return -ENOMEM; 821061132d2SLuis R. Rodriguez 822061132d2SLuis R. Rodriguez pr_info("loading '%s' using custom fallback mechanism\n", name); 823061132d2SLuis R. Rodriguez 824061132d2SLuis R. Rodriguez mutex_lock(&test_fw_mutex); 825061132d2SLuis R. Rodriguez release_firmware(test_firmware); 82648e15602SMirsad Goran Todorovac if (test_fw_config->reqs) 82748e15602SMirsad Goran Todorovac __test_release_all_firmware(); 828061132d2SLuis R. Rodriguez test_firmware = NULL; 8290733d839SShawn Guo rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT, name, 830061132d2SLuis R. Rodriguez dev, GFP_KERNEL, NULL, 831061132d2SLuis R. Rodriguez trigger_async_request_cb); 832061132d2SLuis R. Rodriguez if (rc) { 833061132d2SLuis R. Rodriguez pr_info("async load of '%s' failed: %d\n", name, rc); 834061132d2SLuis R. Rodriguez kfree(name); 835061132d2SLuis R. Rodriguez goto out; 836061132d2SLuis R. Rodriguez } 837061132d2SLuis R. Rodriguez /* Free 'name' ASAP, to test for race conditions */ 838061132d2SLuis R. Rodriguez kfree(name); 839061132d2SLuis R. Rodriguez 840061132d2SLuis R. Rodriguez wait_for_completion(&async_fw_done); 841061132d2SLuis R. Rodriguez 842061132d2SLuis R. Rodriguez if (test_firmware) { 843061132d2SLuis R. Rodriguez pr_info("loaded: %zu\n", test_firmware->size); 844061132d2SLuis R. Rodriguez rc = count; 845061132d2SLuis R. Rodriguez } else { 846061132d2SLuis R. Rodriguez pr_err("failed to async load firmware\n"); 847061132d2SLuis R. Rodriguez rc = -ENODEV; 848061132d2SLuis R. Rodriguez } 849061132d2SLuis R. Rodriguez 850061132d2SLuis R. Rodriguez out: 851061132d2SLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 852061132d2SLuis R. Rodriguez 853061132d2SLuis R. Rodriguez return rc; 854061132d2SLuis R. Rodriguez } 855061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback); 856061132d2SLuis R. Rodriguez 857c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data) 858c92316bfSLuis R. Rodriguez { 859c92316bfSLuis R. Rodriguez struct test_batched_req *req = data; 860c92316bfSLuis R. Rodriguez 861c92316bfSLuis R. Rodriguez if (!req) { 862c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 863c92316bfSLuis R. Rodriguez return -EINVAL; 864c92316bfSLuis R. Rodriguez } 865c92316bfSLuis R. Rodriguez 8667feebfa4SScott Branden if (test_fw_config->into_buf) { 8677feebfa4SScott Branden void *test_buf; 8687feebfa4SScott Branden 8697feebfa4SScott Branden test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); 8707feebfa4SScott Branden if (!test_buf) 8717dae593cSMirsad Goran Todorovac return -ENOMEM; 8727feebfa4SScott Branden 8735d90e05cSScott Branden if (test_fw_config->partial) 8745d90e05cSScott Branden req->rc = request_partial_firmware_into_buf 8755d90e05cSScott Branden (&req->fw, 8767feebfa4SScott Branden req->name, 8777feebfa4SScott Branden req->dev, 8787feebfa4SScott Branden test_buf, 8795d90e05cSScott Branden test_fw_config->buf_size, 8805d90e05cSScott Branden test_fw_config->file_offset); 8815d90e05cSScott Branden else 8825d90e05cSScott Branden req->rc = request_firmware_into_buf 8835d90e05cSScott Branden (&req->fw, 8845d90e05cSScott Branden req->name, 8855d90e05cSScott Branden req->dev, 8865d90e05cSScott Branden test_buf, 8875d90e05cSScott Branden test_fw_config->buf_size); 8887feebfa4SScott Branden if (!req->fw) 8897feebfa4SScott Branden kfree(test_buf); 89048e15602SMirsad Goran Todorovac else 89148e15602SMirsad Goran Todorovac req->fw_buf = test_buf; 8927feebfa4SScott Branden } else { 8937feebfa4SScott Branden req->rc = test_fw_config->req_firmware(&req->fw, 8947feebfa4SScott Branden req->name, 8957feebfa4SScott Branden req->dev); 8967feebfa4SScott Branden } 8977feebfa4SScott Branden 898c92316bfSLuis R. Rodriguez if (req->rc) { 899c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync load failed: %d\n", 900c92316bfSLuis R. Rodriguez req->idx, req->rc); 901c92316bfSLuis R. Rodriguez if (!test_fw_config->test_result) 902c92316bfSLuis R. Rodriguez test_fw_config->test_result = req->rc; 903c92316bfSLuis R. Rodriguez } else if (req->fw) { 904c92316bfSLuis R. Rodriguez req->sent = true; 905c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync loaded %zu\n", 906c92316bfSLuis R. Rodriguez req->idx, req->fw->size); 907c92316bfSLuis R. Rodriguez } 908c92316bfSLuis R. Rodriguez complete(&req->completion); 909c92316bfSLuis R. Rodriguez 910c92316bfSLuis R. Rodriguez req->task = NULL; 911c92316bfSLuis R. Rodriguez 912c92316bfSLuis R. Rodriguez return 0; 913c92316bfSLuis R. Rodriguez } 914c92316bfSLuis R. Rodriguez 915c92316bfSLuis R. Rodriguez /* 916c92316bfSLuis R. Rodriguez * We use a kthread as otherwise the kernel serializes all our sync requests 917c92316bfSLuis R. Rodriguez * and we would not be able to mimic batched requests on a sync call. Batched 918c92316bfSLuis R. Rodriguez * requests on a sync call can for instance happen on a device driver when 919c92316bfSLuis R. Rodriguez * multiple cards are used and firmware loading happens outside of probe. 920c92316bfSLuis R. Rodriguez */ 921c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev, 922c92316bfSLuis R. Rodriguez struct device_attribute *attr, 923c92316bfSLuis R. Rodriguez const char *buf, size_t count) 924c92316bfSLuis R. Rodriguez { 925c92316bfSLuis R. Rodriguez struct test_batched_req *req; 926c92316bfSLuis R. Rodriguez int rc; 927c92316bfSLuis R. Rodriguez u8 i; 928c92316bfSLuis R. Rodriguez 929c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 930c92316bfSLuis R. Rodriguez 931be37bed7SMirsad Goran Todorovac if (test_fw_config->reqs) { 932be37bed7SMirsad Goran Todorovac rc = -EBUSY; 933be37bed7SMirsad Goran Todorovac goto out_bail; 934be37bed7SMirsad Goran Todorovac } 935be37bed7SMirsad Goran Todorovac 936fad953ceSKees Cook test_fw_config->reqs = 937fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 938fad953ceSKees Cook test_fw_config->num_requests, 2)); 939c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 940c92316bfSLuis R. Rodriguez rc = -ENOMEM; 941c92316bfSLuis R. Rodriguez goto out_unlock; 942c92316bfSLuis R. Rodriguez } 943c92316bfSLuis R. Rodriguez 944c92316bfSLuis R. Rodriguez pr_info("batched sync firmware loading '%s' %u times\n", 945c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 946c92316bfSLuis R. Rodriguez 947c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 948c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 949c92316bfSLuis R. Rodriguez req->fw = NULL; 950c92316bfSLuis R. Rodriguez req->idx = i; 951c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 95248e15602SMirsad Goran Todorovac req->fw_buf = NULL; 953c92316bfSLuis R. Rodriguez req->dev = dev; 954c92316bfSLuis R. Rodriguez init_completion(&req->completion); 955c92316bfSLuis R. Rodriguez req->task = kthread_run(test_fw_run_batch_request, req, 956c92316bfSLuis R. Rodriguez "%s-%u", KBUILD_MODNAME, req->idx); 957c92316bfSLuis R. Rodriguez if (!req->task || IS_ERR(req->task)) { 958c92316bfSLuis R. Rodriguez pr_err("Setting up thread %u failed\n", req->idx); 959c92316bfSLuis R. Rodriguez req->task = NULL; 960c92316bfSLuis R. Rodriguez rc = -ENOMEM; 961c92316bfSLuis R. Rodriguez goto out_bail; 962c92316bfSLuis R. Rodriguez } 963c92316bfSLuis R. Rodriguez } 964c92316bfSLuis R. Rodriguez 965c92316bfSLuis R. Rodriguez rc = count; 966c92316bfSLuis R. Rodriguez 967c92316bfSLuis R. Rodriguez /* 968c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 969c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 970c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 971c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 972c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 973c92316bfSLuis R. Rodriguez */ 974c92316bfSLuis R. Rodriguez 975c92316bfSLuis R. Rodriguez out_bail: 976c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 977c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 978c92316bfSLuis R. Rodriguez if (req->task || req->sent) 979c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 980c92316bfSLuis R. Rodriguez } 981c92316bfSLuis R. Rodriguez 982c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 983c92316bfSLuis R. Rodriguez if (rc < 0) 984c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 985c92316bfSLuis R. Rodriguez 986c92316bfSLuis R. Rodriguez out_unlock: 987c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 988c92316bfSLuis R. Rodriguez 989c92316bfSLuis R. Rodriguez return rc; 990c92316bfSLuis R. Rodriguez } 991c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests); 992c92316bfSLuis R. Rodriguez 993c92316bfSLuis R. Rodriguez /* 994c92316bfSLuis R. Rodriguez * We wait for each callback to return with the lock held, no need to lock here 995c92316bfSLuis R. Rodriguez */ 996c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context) 997c92316bfSLuis R. Rodriguez { 998c92316bfSLuis R. Rodriguez struct test_batched_req *req = context; 999c92316bfSLuis R. Rodriguez 1000c92316bfSLuis R. Rodriguez if (!req) { 1001c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 1002c92316bfSLuis R. Rodriguez return; 1003c92316bfSLuis R. Rodriguez } 1004c92316bfSLuis R. Rodriguez 1005c92316bfSLuis R. Rodriguez /* forces *some* batched requests to queue up */ 1006c92316bfSLuis R. Rodriguez if (!req->idx) 1007c92316bfSLuis R. Rodriguez ssleep(2); 1008c92316bfSLuis R. Rodriguez 1009c92316bfSLuis R. Rodriguez req->fw = fw; 1010c92316bfSLuis R. Rodriguez 1011c92316bfSLuis R. Rodriguez /* 1012c92316bfSLuis R. Rodriguez * Unfortunately the firmware API gives us nothing other than a null FW 1013c92316bfSLuis R. Rodriguez * if the firmware was not found on async requests. Best we can do is 1014c92316bfSLuis R. Rodriguez * just assume -ENOENT. A better API would pass the actual return 1015c92316bfSLuis R. Rodriguez * value to the callback. 1016c92316bfSLuis R. Rodriguez */ 1017c92316bfSLuis R. Rodriguez if (!fw && !test_fw_config->test_result) 1018c92316bfSLuis R. Rodriguez test_fw_config->test_result = -ENOENT; 1019c92316bfSLuis R. Rodriguez 1020c92316bfSLuis R. Rodriguez complete(&req->completion); 1021c92316bfSLuis R. Rodriguez } 1022c92316bfSLuis R. Rodriguez 1023c92316bfSLuis R. Rodriguez static 1024c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev, 1025c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1026c92316bfSLuis R. Rodriguez const char *buf, size_t count) 1027c92316bfSLuis R. Rodriguez { 1028c92316bfSLuis R. Rodriguez struct test_batched_req *req; 1029c92316bfSLuis R. Rodriguez bool send_uevent; 1030c92316bfSLuis R. Rodriguez int rc; 1031c92316bfSLuis R. Rodriguez u8 i; 1032c92316bfSLuis R. Rodriguez 1033c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 1034c92316bfSLuis R. Rodriguez 1035be37bed7SMirsad Goran Todorovac if (test_fw_config->reqs) { 1036be37bed7SMirsad Goran Todorovac rc = -EBUSY; 1037be37bed7SMirsad Goran Todorovac goto out_bail; 1038be37bed7SMirsad Goran Todorovac } 1039be37bed7SMirsad Goran Todorovac 1040fad953ceSKees Cook test_fw_config->reqs = 1041fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 1042fad953ceSKees Cook test_fw_config->num_requests, 2)); 1043c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 1044c92316bfSLuis R. Rodriguez rc = -ENOMEM; 1045c92316bfSLuis R. Rodriguez goto out; 1046c92316bfSLuis R. Rodriguez } 1047c92316bfSLuis R. Rodriguez 1048c92316bfSLuis R. Rodriguez pr_info("batched loading '%s' custom fallback mechanism %u times\n", 1049c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 1050c92316bfSLuis R. Rodriguez 10510733d839SShawn Guo send_uevent = test_fw_config->send_uevent ? FW_ACTION_UEVENT : 10520733d839SShawn Guo FW_ACTION_NOUEVENT; 1053c92316bfSLuis R. Rodriguez 1054c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 1055c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 1056c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 105748e15602SMirsad Goran Todorovac req->fw_buf = NULL; 1058c92316bfSLuis R. Rodriguez req->fw = NULL; 1059c92316bfSLuis R. Rodriguez req->idx = i; 1060c92316bfSLuis R. Rodriguez init_completion(&req->completion); 1061c92316bfSLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, send_uevent, 1062c92316bfSLuis R. Rodriguez req->name, 1063c92316bfSLuis R. Rodriguez dev, GFP_KERNEL, req, 1064c92316bfSLuis R. Rodriguez trigger_batched_cb); 1065c92316bfSLuis R. Rodriguez if (rc) { 1066c92316bfSLuis R. Rodriguez pr_info("#%u: batched async load failed setup: %d\n", 1067c92316bfSLuis R. Rodriguez i, rc); 1068c92316bfSLuis R. Rodriguez req->rc = rc; 1069c92316bfSLuis R. Rodriguez goto out_bail; 1070c92316bfSLuis R. Rodriguez } else 1071c92316bfSLuis R. Rodriguez req->sent = true; 1072c92316bfSLuis R. Rodriguez } 1073c92316bfSLuis R. Rodriguez 1074c92316bfSLuis R. Rodriguez rc = count; 1075c92316bfSLuis R. Rodriguez 1076c92316bfSLuis R. Rodriguez out_bail: 1077c92316bfSLuis R. Rodriguez 1078c92316bfSLuis R. Rodriguez /* 1079c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 1080c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 1081c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 1082c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 1083c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 1084c92316bfSLuis R. Rodriguez */ 1085c92316bfSLuis R. Rodriguez 1086c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 1087c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 1088c92316bfSLuis R. Rodriguez if (req->sent) 1089c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 1090c92316bfSLuis R. Rodriguez } 1091c92316bfSLuis R. Rodriguez 1092c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 1093c92316bfSLuis R. Rodriguez if (rc < 0) 1094c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 1095c92316bfSLuis R. Rodriguez 1096c92316bfSLuis R. Rodriguez out: 1097c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1098c92316bfSLuis R. Rodriguez 1099c92316bfSLuis R. Rodriguez return rc; 1100c92316bfSLuis R. Rodriguez } 1101c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async); 1102c92316bfSLuis R. Rodriguez 1103a31ad463SRuss Weight static void upload_release(struct test_firmware_upload *tst) 1104a31ad463SRuss Weight { 1105a31ad463SRuss Weight firmware_upload_unregister(tst->fwl); 1106a31ad463SRuss Weight kfree(tst->buf); 1107a31ad463SRuss Weight kfree(tst->name); 1108a31ad463SRuss Weight kfree(tst); 1109a31ad463SRuss Weight } 1110a31ad463SRuss Weight 1111a31ad463SRuss Weight static void upload_release_all(void) 1112a31ad463SRuss Weight { 1113a31ad463SRuss Weight struct test_firmware_upload *tst, *tmp; 1114a31ad463SRuss Weight 1115a31ad463SRuss Weight list_for_each_entry_safe(tst, tmp, &test_upload_list, node) { 1116a31ad463SRuss Weight list_del(&tst->node); 1117a31ad463SRuss Weight upload_release(tst); 1118a31ad463SRuss Weight } 1119a31ad463SRuss Weight test_fw_config->upload_name = NULL; 1120a31ad463SRuss Weight } 1121a31ad463SRuss Weight 11224a4e975bSRuss Weight /* 11234a4e975bSRuss Weight * This table is replicated from .../firmware_loader/sysfs_upload.c 11244a4e975bSRuss Weight * and needs to be kept in sync. 11254a4e975bSRuss Weight */ 11264a4e975bSRuss Weight static const char * const fw_upload_err_str[] = { 11274a4e975bSRuss Weight [FW_UPLOAD_ERR_NONE] = "none", 11284a4e975bSRuss Weight [FW_UPLOAD_ERR_HW_ERROR] = "hw-error", 11294a4e975bSRuss Weight [FW_UPLOAD_ERR_TIMEOUT] = "timeout", 11304a4e975bSRuss Weight [FW_UPLOAD_ERR_CANCELED] = "user-abort", 11314a4e975bSRuss Weight [FW_UPLOAD_ERR_BUSY] = "device-busy", 11324a4e975bSRuss Weight [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size", 11334a4e975bSRuss Weight [FW_UPLOAD_ERR_RW_ERROR] = "read-write-error", 11344a4e975bSRuss Weight [FW_UPLOAD_ERR_WEAROUT] = "flash-wearout", 1135*a066f906SKory Maincent [FW_UPLOAD_ERR_FW_INVALID] = "firmware-invalid", 11364a4e975bSRuss Weight }; 11374a4e975bSRuss Weight 11384a4e975bSRuss Weight static void upload_err_inject_error(struct test_firmware_upload *tst, 11394a4e975bSRuss Weight const u8 *p, const char *prog) 11404a4e975bSRuss Weight { 11414a4e975bSRuss Weight enum fw_upload_err err; 11424a4e975bSRuss Weight 11434a4e975bSRuss Weight for (err = FW_UPLOAD_ERR_NONE + 1; err < FW_UPLOAD_ERR_MAX; err++) { 11444a4e975bSRuss Weight if (strncmp(p, fw_upload_err_str[err], 11454a4e975bSRuss Weight strlen(fw_upload_err_str[err])) == 0) { 11464a4e975bSRuss Weight tst->inject.prog = prog; 11474a4e975bSRuss Weight tst->inject.err_code = err; 11484a4e975bSRuss Weight return; 11494a4e975bSRuss Weight } 11504a4e975bSRuss Weight } 11514a4e975bSRuss Weight } 11524a4e975bSRuss Weight 11534a4e975bSRuss Weight static void upload_err_inject_prog(struct test_firmware_upload *tst, 11544a4e975bSRuss Weight const u8 *p) 11554a4e975bSRuss Weight { 11564a4e975bSRuss Weight static const char * const progs[] = { 11574a4e975bSRuss Weight "preparing:", "transferring:", "programming:" 11584a4e975bSRuss Weight }; 11594a4e975bSRuss Weight int i; 11604a4e975bSRuss Weight 11614a4e975bSRuss Weight for (i = 0; i < ARRAY_SIZE(progs); i++) { 11624a4e975bSRuss Weight if (strncmp(p, progs[i], strlen(progs[i])) == 0) { 11634a4e975bSRuss Weight upload_err_inject_error(tst, p + strlen(progs[i]), 11644a4e975bSRuss Weight progs[i]); 11654a4e975bSRuss Weight return; 11664a4e975bSRuss Weight } 11674a4e975bSRuss Weight } 11684a4e975bSRuss Weight } 11694a4e975bSRuss Weight 11704a4e975bSRuss Weight #define FIVE_MINUTES_MS (5 * 60 * 1000) 11714a4e975bSRuss Weight static enum fw_upload_err 11724a4e975bSRuss Weight fw_upload_wait_on_cancel(struct test_firmware_upload *tst) 11734a4e975bSRuss Weight { 11744a4e975bSRuss Weight int ms_delay; 11754a4e975bSRuss Weight 11764a4e975bSRuss Weight for (ms_delay = 0; ms_delay < FIVE_MINUTES_MS; ms_delay += 100) { 11774a4e975bSRuss Weight msleep(100); 11784a4e975bSRuss Weight if (tst->cancel_request) 11794a4e975bSRuss Weight return FW_UPLOAD_ERR_CANCELED; 11804a4e975bSRuss Weight } 11814a4e975bSRuss Weight return FW_UPLOAD_ERR_NONE; 11824a4e975bSRuss Weight } 11834a4e975bSRuss Weight 1184a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_prepare(struct fw_upload *fwl, 1185a31ad463SRuss Weight const u8 *data, u32 size) 1186a31ad463SRuss Weight { 1187a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 11884a4e975bSRuss Weight enum fw_upload_err ret = FW_UPLOAD_ERR_NONE; 11894a4e975bSRuss Weight const char *progress = "preparing:"; 1190a31ad463SRuss Weight 1191a31ad463SRuss Weight tst->cancel_request = false; 1192a31ad463SRuss Weight 11934a4e975bSRuss Weight if (!size || size > TEST_UPLOAD_MAX_SIZE) { 11944a4e975bSRuss Weight ret = FW_UPLOAD_ERR_INVALID_SIZE; 11954a4e975bSRuss Weight goto err_out; 11964a4e975bSRuss Weight } 11974a4e975bSRuss Weight 11984a4e975bSRuss Weight if (strncmp(data, "inject:", strlen("inject:")) == 0) 11994a4e975bSRuss Weight upload_err_inject_prog(tst, data + strlen("inject:")); 1200a31ad463SRuss Weight 1201a31ad463SRuss Weight memset(tst->buf, 0, TEST_UPLOAD_MAX_SIZE); 1202a31ad463SRuss Weight tst->size = size; 1203a31ad463SRuss Weight 12044a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_NONE || 12054a4e975bSRuss Weight strncmp(tst->inject.prog, progress, strlen(progress)) != 0) 1206a31ad463SRuss Weight return FW_UPLOAD_ERR_NONE; 12074a4e975bSRuss Weight 12084a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED) 12094a4e975bSRuss Weight ret = fw_upload_wait_on_cancel(tst); 12104a4e975bSRuss Weight else 12114a4e975bSRuss Weight ret = tst->inject.err_code; 12124a4e975bSRuss Weight 12134a4e975bSRuss Weight err_out: 12144a4e975bSRuss Weight /* 12154a4e975bSRuss Weight * The cleanup op only executes if the prepare op succeeds. 12164a4e975bSRuss Weight * If the prepare op fails, it must do it's own clean-up. 12174a4e975bSRuss Weight */ 12184a4e975bSRuss Weight tst->inject.err_code = FW_UPLOAD_ERR_NONE; 12194a4e975bSRuss Weight tst->inject.prog = NULL; 12204a4e975bSRuss Weight 12214a4e975bSRuss Weight return ret; 1222a31ad463SRuss Weight } 1223a31ad463SRuss Weight 1224a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_write(struct fw_upload *fwl, 1225a31ad463SRuss Weight const u8 *data, u32 offset, 1226a31ad463SRuss Weight u32 size, u32 *written) 1227a31ad463SRuss Weight { 1228a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 12294a4e975bSRuss Weight const char *progress = "transferring:"; 1230a31ad463SRuss Weight u32 blk_size; 1231a31ad463SRuss Weight 1232a31ad463SRuss Weight if (tst->cancel_request) 1233a31ad463SRuss Weight return FW_UPLOAD_ERR_CANCELED; 1234a31ad463SRuss Weight 1235a31ad463SRuss Weight blk_size = min_t(u32, TEST_UPLOAD_BLK_SIZE, size); 1236a31ad463SRuss Weight memcpy(tst->buf + offset, data + offset, blk_size); 1237a31ad463SRuss Weight 1238a31ad463SRuss Weight *written = blk_size; 12394a4e975bSRuss Weight 12404a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_NONE || 12414a4e975bSRuss Weight strncmp(tst->inject.prog, progress, strlen(progress)) != 0) 1242a31ad463SRuss Weight return FW_UPLOAD_ERR_NONE; 12434a4e975bSRuss Weight 12444a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED) 12454a4e975bSRuss Weight return fw_upload_wait_on_cancel(tst); 12464a4e975bSRuss Weight 12474a4e975bSRuss Weight return tst->inject.err_code; 1248a31ad463SRuss Weight } 1249a31ad463SRuss Weight 1250a31ad463SRuss Weight static enum fw_upload_err test_fw_upload_complete(struct fw_upload *fwl) 1251a31ad463SRuss Weight { 1252a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 12534a4e975bSRuss Weight const char *progress = "programming:"; 1254a31ad463SRuss Weight 1255a31ad463SRuss Weight if (tst->cancel_request) 1256a31ad463SRuss Weight return FW_UPLOAD_ERR_CANCELED; 1257a31ad463SRuss Weight 12584a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_NONE || 12594a4e975bSRuss Weight strncmp(tst->inject.prog, progress, strlen(progress)) != 0) 1260a31ad463SRuss Weight return FW_UPLOAD_ERR_NONE; 12614a4e975bSRuss Weight 12624a4e975bSRuss Weight if (tst->inject.err_code == FW_UPLOAD_ERR_CANCELED) 12634a4e975bSRuss Weight return fw_upload_wait_on_cancel(tst); 12644a4e975bSRuss Weight 12654a4e975bSRuss Weight return tst->inject.err_code; 1266a31ad463SRuss Weight } 1267a31ad463SRuss Weight 1268a31ad463SRuss Weight static void test_fw_upload_cancel(struct fw_upload *fwl) 1269a31ad463SRuss Weight { 1270a31ad463SRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 1271a31ad463SRuss Weight 1272a31ad463SRuss Weight tst->cancel_request = true; 1273a31ad463SRuss Weight } 1274a31ad463SRuss Weight 12754a4e975bSRuss Weight static void test_fw_cleanup(struct fw_upload *fwl) 12764a4e975bSRuss Weight { 12774a4e975bSRuss Weight struct test_firmware_upload *tst = fwl->dd_handle; 12784a4e975bSRuss Weight 12794a4e975bSRuss Weight tst->inject.err_code = FW_UPLOAD_ERR_NONE; 12804a4e975bSRuss Weight tst->inject.prog = NULL; 12814a4e975bSRuss Weight } 12824a4e975bSRuss Weight 1283a31ad463SRuss Weight static const struct fw_upload_ops upload_test_ops = { 1284a31ad463SRuss Weight .prepare = test_fw_upload_prepare, 1285a31ad463SRuss Weight .write = test_fw_upload_write, 1286a31ad463SRuss Weight .poll_complete = test_fw_upload_complete, 1287a31ad463SRuss Weight .cancel = test_fw_upload_cancel, 12884a4e975bSRuss Weight .cleanup = test_fw_cleanup 1289a31ad463SRuss Weight }; 1290a31ad463SRuss Weight 1291a31ad463SRuss Weight static ssize_t upload_register_store(struct device *dev, 1292a31ad463SRuss Weight struct device_attribute *attr, 1293a31ad463SRuss Weight const char *buf, size_t count) 1294a31ad463SRuss Weight { 1295a31ad463SRuss Weight struct test_firmware_upload *tst; 1296a31ad463SRuss Weight struct fw_upload *fwl; 1297a31ad463SRuss Weight char *name; 1298a31ad463SRuss Weight int ret; 1299a31ad463SRuss Weight 1300a31ad463SRuss Weight name = kstrndup(buf, count, GFP_KERNEL); 1301a31ad463SRuss Weight if (!name) 1302a31ad463SRuss Weight return -ENOMEM; 1303a31ad463SRuss Weight 1304a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 1305a31ad463SRuss Weight tst = upload_lookup_name(name); 1306a31ad463SRuss Weight if (tst) { 1307a31ad463SRuss Weight ret = -EEXIST; 1308a31ad463SRuss Weight goto free_name; 1309a31ad463SRuss Weight } 1310a31ad463SRuss Weight 1311a31ad463SRuss Weight tst = kzalloc(sizeof(*tst), GFP_KERNEL); 1312a31ad463SRuss Weight if (!tst) { 1313a31ad463SRuss Weight ret = -ENOMEM; 1314a31ad463SRuss Weight goto free_name; 1315a31ad463SRuss Weight } 1316a31ad463SRuss Weight 1317a31ad463SRuss Weight tst->name = name; 1318a31ad463SRuss Weight tst->buf = kzalloc(TEST_UPLOAD_MAX_SIZE, GFP_KERNEL); 1319a31ad463SRuss Weight if (!tst->buf) { 1320a31ad463SRuss Weight ret = -ENOMEM; 1321a31ad463SRuss Weight goto free_tst; 1322a31ad463SRuss Weight } 1323a31ad463SRuss Weight 1324a31ad463SRuss Weight fwl = firmware_upload_register(THIS_MODULE, dev, tst->name, 1325a31ad463SRuss Weight &upload_test_ops, tst); 1326a31ad463SRuss Weight if (IS_ERR(fwl)) { 1327a31ad463SRuss Weight ret = PTR_ERR(fwl); 1328a31ad463SRuss Weight goto free_buf; 1329a31ad463SRuss Weight } 1330a31ad463SRuss Weight 1331a31ad463SRuss Weight tst->fwl = fwl; 1332a31ad463SRuss Weight list_add_tail(&tst->node, &test_upload_list); 1333a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1334a31ad463SRuss Weight return count; 1335a31ad463SRuss Weight 1336a31ad463SRuss Weight free_buf: 1337a31ad463SRuss Weight kfree(tst->buf); 1338a31ad463SRuss Weight 1339a31ad463SRuss Weight free_tst: 1340a31ad463SRuss Weight kfree(tst); 1341a31ad463SRuss Weight 1342a31ad463SRuss Weight free_name: 1343a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1344a31ad463SRuss Weight kfree(name); 1345a31ad463SRuss Weight 1346a31ad463SRuss Weight return ret; 1347a31ad463SRuss Weight } 1348a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_register); 1349a31ad463SRuss Weight 1350a31ad463SRuss Weight static ssize_t upload_unregister_store(struct device *dev, 1351a31ad463SRuss Weight struct device_attribute *attr, 1352a31ad463SRuss Weight const char *buf, size_t count) 1353a31ad463SRuss Weight { 1354a31ad463SRuss Weight struct test_firmware_upload *tst; 1355a31ad463SRuss Weight int ret = count; 1356a31ad463SRuss Weight 1357a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 1358a31ad463SRuss Weight tst = upload_lookup_name(buf); 1359a31ad463SRuss Weight if (!tst) { 1360a31ad463SRuss Weight ret = -EINVAL; 1361a31ad463SRuss Weight goto out; 1362a31ad463SRuss Weight } 1363a31ad463SRuss Weight 1364a31ad463SRuss Weight if (test_fw_config->upload_name == tst->name) 1365a31ad463SRuss Weight test_fw_config->upload_name = NULL; 1366a31ad463SRuss Weight 1367a31ad463SRuss Weight list_del(&tst->node); 1368a31ad463SRuss Weight upload_release(tst); 1369a31ad463SRuss Weight 1370a31ad463SRuss Weight out: 1371a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1372a31ad463SRuss Weight return ret; 1373a31ad463SRuss Weight } 1374a31ad463SRuss Weight static DEVICE_ATTR_WO(upload_unregister); 1375a31ad463SRuss Weight 1376c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev, 1377c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1378c92316bfSLuis R. Rodriguez char *buf) 1379c92316bfSLuis R. Rodriguez { 1380c92316bfSLuis R. Rodriguez return test_dev_config_show_int(buf, test_fw_config->test_result); 1381c92316bfSLuis R. Rodriguez } 1382c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result); 1383c92316bfSLuis R. Rodriguez 1384c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev, 1385c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1386c92316bfSLuis R. Rodriguez const char *buf, size_t count) 1387c92316bfSLuis R. Rodriguez { 1388c92316bfSLuis R. Rodriguez test_release_all_firmware(); 1389c92316bfSLuis R. Rodriguez return count; 1390c92316bfSLuis R. Rodriguez } 1391c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware); 1392c92316bfSLuis R. Rodriguez 1393c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev, 1394c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1395c92316bfSLuis R. Rodriguez char *buf) 1396c92316bfSLuis R. Rodriguez { 1397c92316bfSLuis R. Rodriguez struct test_batched_req *req; 1398c92316bfSLuis R. Rodriguez u8 idx; 1399c92316bfSLuis R. Rodriguez ssize_t rc = 0; 1400c92316bfSLuis R. Rodriguez 1401c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 1402c92316bfSLuis R. Rodriguez 1403c92316bfSLuis R. Rodriguez idx = test_fw_config->read_fw_idx; 1404c92316bfSLuis R. Rodriguez if (idx >= test_fw_config->num_requests) { 1405c92316bfSLuis R. Rodriguez rc = -ERANGE; 1406c92316bfSLuis R. Rodriguez goto out; 1407c92316bfSLuis R. Rodriguez } 1408c92316bfSLuis R. Rodriguez 1409c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 1410c92316bfSLuis R. Rodriguez rc = -EINVAL; 1411c92316bfSLuis R. Rodriguez goto out; 1412c92316bfSLuis R. Rodriguez } 1413c92316bfSLuis R. Rodriguez 1414c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[idx]; 1415c92316bfSLuis R. Rodriguez if (!req->fw) { 1416c92316bfSLuis R. Rodriguez pr_err("#%u: failed to async load firmware\n", idx); 1417c92316bfSLuis R. Rodriguez rc = -ENOENT; 1418c92316bfSLuis R. Rodriguez goto out; 1419c92316bfSLuis R. Rodriguez } 1420c92316bfSLuis R. Rodriguez 1421c92316bfSLuis R. Rodriguez pr_info("#%u: loaded %zu\n", idx, req->fw->size); 1422c92316bfSLuis R. Rodriguez 1423c92316bfSLuis R. Rodriguez if (req->fw->size > PAGE_SIZE) { 1424c92316bfSLuis R. Rodriguez pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 1425c92316bfSLuis R. Rodriguez rc = -EINVAL; 14268bb0a886SColin Ian King goto out; 1427c92316bfSLuis R. Rodriguez } 1428c92316bfSLuis R. Rodriguez memcpy(buf, req->fw->data, req->fw->size); 1429c92316bfSLuis R. Rodriguez 1430c92316bfSLuis R. Rodriguez rc = req->fw->size; 1431c92316bfSLuis R. Rodriguez out: 1432c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1433c92316bfSLuis R. Rodriguez 1434c92316bfSLuis R. Rodriguez return rc; 1435c92316bfSLuis R. Rodriguez } 1436c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware); 1437c92316bfSLuis R. Rodriguez 1438a31ad463SRuss Weight static ssize_t upload_read_show(struct device *dev, 1439a31ad463SRuss Weight struct device_attribute *attr, 1440a31ad463SRuss Weight char *buf) 1441a31ad463SRuss Weight { 1442185b29c6SDan Carpenter struct test_firmware_upload *tst = NULL; 1443185b29c6SDan Carpenter struct test_firmware_upload *tst_iter; 1444a31ad463SRuss Weight int ret = -EINVAL; 1445a31ad463SRuss Weight 1446a31ad463SRuss Weight if (!test_fw_config->upload_name) { 1447a31ad463SRuss Weight pr_err("Set config_upload_name before using upload_read\n"); 1448a31ad463SRuss Weight return -EINVAL; 1449a31ad463SRuss Weight } 1450a31ad463SRuss Weight 1451a31ad463SRuss Weight mutex_lock(&test_fw_mutex); 1452185b29c6SDan Carpenter list_for_each_entry(tst_iter, &test_upload_list, node) 1453185b29c6SDan Carpenter if (tst_iter->name == test_fw_config->upload_name) { 1454185b29c6SDan Carpenter tst = tst_iter; 1455a31ad463SRuss Weight break; 1456185b29c6SDan Carpenter } 1457a31ad463SRuss Weight 1458185b29c6SDan Carpenter if (!tst) { 1459a31ad463SRuss Weight pr_err("Firmware name not found: %s\n", 1460a31ad463SRuss Weight test_fw_config->upload_name); 1461a31ad463SRuss Weight goto out; 1462a31ad463SRuss Weight } 1463a31ad463SRuss Weight 1464a31ad463SRuss Weight if (tst->size > PAGE_SIZE) { 1465a31ad463SRuss Weight pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 1466a31ad463SRuss Weight goto out; 1467a31ad463SRuss Weight } 1468a31ad463SRuss Weight 1469a31ad463SRuss Weight memcpy(buf, tst->buf, tst->size); 1470a31ad463SRuss Weight ret = tst->size; 1471a31ad463SRuss Weight out: 1472a31ad463SRuss Weight mutex_unlock(&test_fw_mutex); 1473a31ad463SRuss Weight return ret; 1474a31ad463SRuss Weight } 1475a31ad463SRuss Weight static DEVICE_ATTR_RO(upload_read); 1476a31ad463SRuss Weight 1477083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr 1478083a93b0SLuis R. Rodriguez 1479083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = { 1480c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(reset), 1481c92316bfSLuis R. Rodriguez 1482c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config), 1483c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_name), 1484c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_num_requests), 14857feebfa4SScott Branden TEST_FW_DEV_ATTR(config_into_buf), 14865d90e05cSScott Branden TEST_FW_DEV_ATTR(config_buf_size), 14875d90e05cSScott Branden TEST_FW_DEV_ATTR(config_file_offset), 14885d90e05cSScott Branden TEST_FW_DEV_ATTR(config_partial), 1489c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_sync_direct), 1490c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_send_uevent), 1491c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_read_fw_idx), 1492a31ad463SRuss Weight TEST_FW_DEV_ATTR(config_upload_name), 1493c92316bfSLuis R. Rodriguez 1494c92316bfSLuis R. Rodriguez /* These don't use the config at all - they could be ported! */ 1495083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_request), 1496083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_async_request), 1497061132d2SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_custom_fallback), 1498548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 1499548193cbSHans de Goede TEST_FW_DEV_ATTR(trigger_request_platform), 1500548193cbSHans de Goede #endif 1501c92316bfSLuis R. Rodriguez 1502c92316bfSLuis R. Rodriguez /* These use the config and can use the test_result */ 1503c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests), 1504c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests_async), 1505c92316bfSLuis R. Rodriguez 1506c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(release_all_firmware), 1507c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(test_result), 1508c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(read_firmware), 1509a31ad463SRuss Weight TEST_FW_DEV_ATTR(upload_read), 1510a31ad463SRuss Weight TEST_FW_DEV_ATTR(upload_register), 1511a31ad463SRuss Weight TEST_FW_DEV_ATTR(upload_unregister), 1512083a93b0SLuis R. Rodriguez NULL, 1513083a93b0SLuis R. Rodriguez }; 1514083a93b0SLuis R. Rodriguez 1515083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev); 1516083a93b0SLuis R. Rodriguez 151767fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = { 151867fd553cSLuis R. Rodriguez .minor = MISC_DYNAMIC_MINOR, 151967fd553cSLuis R. Rodriguez .name = "test_firmware", 152067fd553cSLuis R. Rodriguez .fops = &test_fw_fops, 1521083a93b0SLuis R. Rodriguez .groups = test_dev_groups, 152267fd553cSLuis R. Rodriguez }; 152367fd553cSLuis R. Rodriguez 15240a8adf58SKees Cook static int __init test_firmware_init(void) 15250a8adf58SKees Cook { 15260a8adf58SKees Cook int rc; 15270a8adf58SKees Cook 1528c92316bfSLuis R. Rodriguez test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL); 1529c92316bfSLuis R. Rodriguez if (!test_fw_config) 1530c92316bfSLuis R. Rodriguez return -ENOMEM; 1531c92316bfSLuis R. Rodriguez 1532c92316bfSLuis R. Rodriguez rc = __test_firmware_config_init(); 1533d4fddac5SWenwen Wang if (rc) { 1534d4fddac5SWenwen Wang kfree(test_fw_config); 1535d4fddac5SWenwen Wang pr_err("could not init firmware test config: %d\n", rc); 1536c92316bfSLuis R. Rodriguez return rc; 1537d4fddac5SWenwen Wang } 1538c92316bfSLuis R. Rodriguez 15390a8adf58SKees Cook rc = misc_register(&test_fw_misc_device); 15400a8adf58SKees Cook if (rc) { 15417610615eSZhengchao Shao __test_firmware_config_free(); 1542c92316bfSLuis R. Rodriguez kfree(test_fw_config); 15430a8adf58SKees Cook pr_err("could not register misc device: %d\n", rc); 15440a8adf58SKees Cook return rc; 15450a8adf58SKees Cook } 1546eb910947SBrian Norris 15470a8adf58SKees Cook pr_warn("interface ready\n"); 15480a8adf58SKees Cook 15490a8adf58SKees Cook return 0; 15500a8adf58SKees Cook } 15510a8adf58SKees Cook 15520a8adf58SKees Cook module_init(test_firmware_init); 15530a8adf58SKees Cook 15540a8adf58SKees Cook static void __exit test_firmware_exit(void) 15550a8adf58SKees Cook { 1556c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 15570a8adf58SKees Cook release_firmware(test_firmware); 15580a8adf58SKees Cook misc_deregister(&test_fw_misc_device); 1559a31ad463SRuss Weight upload_release_all(); 1560c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 1561c92316bfSLuis R. Rodriguez kfree(test_fw_config); 1562c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1563c92316bfSLuis R. Rodriguez 15640a8adf58SKees Cook pr_warn("removed interface\n"); 15650a8adf58SKees Cook } 15660a8adf58SKees Cook 15670a8adf58SKees Cook module_exit(test_firmware_exit); 15680a8adf58SKees Cook 15690a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>"); 15700a8adf58SKees Cook MODULE_LICENSE("GPL"); 1571