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> 25c92316bfSLuis R. Rodriguez #include <linux/kthread.h> 26514c6032SRandy Dunlap #include <linux/vmalloc.h> 27548193cbSHans de Goede #include <linux/efi_embedded_fw.h> 28c92316bfSLuis R. Rodriguez 29baaabecfSKees Cook MODULE_IMPORT_NS(TEST_FIRMWARE); 30baaabecfSKees Cook 31c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NAME "test-firmware.bin" 32c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NUM_REQS 4 337feebfa4SScott Branden #define TEST_FIRMWARE_BUF_SIZE SZ_1K 340a8adf58SKees Cook 350a8adf58SKees Cook static DEFINE_MUTEX(test_fw_mutex); 360a8adf58SKees Cook static const struct firmware *test_firmware; 370a8adf58SKees Cook 38c92316bfSLuis R. Rodriguez struct test_batched_req { 39c92316bfSLuis R. Rodriguez u8 idx; 40c92316bfSLuis R. Rodriguez int rc; 41c92316bfSLuis R. Rodriguez bool sent; 42c92316bfSLuis R. Rodriguez const struct firmware *fw; 43c92316bfSLuis R. Rodriguez const char *name; 44c92316bfSLuis R. Rodriguez struct completion completion; 45c92316bfSLuis R. Rodriguez struct task_struct *task; 46c92316bfSLuis R. Rodriguez struct device *dev; 47c92316bfSLuis R. Rodriguez }; 48c92316bfSLuis R. Rodriguez 49c92316bfSLuis R. Rodriguez /** 50c92316bfSLuis R. Rodriguez * test_config - represents configuration for the test for different triggers 51c92316bfSLuis R. Rodriguez * 52c92316bfSLuis R. Rodriguez * @name: the name of the firmware file to look for 537feebfa4SScott Branden * @into_buf: when the into_buf is used if this is true 547feebfa4SScott Branden * request_firmware_into_buf() will be used instead. 55*5d90e05cSScott Branden * @buf_size: size of buf to allocate when into_buf is true 56*5d90e05cSScott Branden * @file_offset: file offset to request when calling request_firmware_into_buf 57*5d90e05cSScott Branden * @partial: partial read opt when calling request_firmware_into_buf 58c92316bfSLuis R. Rodriguez * @sync_direct: when the sync trigger is used if this is true 59c92316bfSLuis R. Rodriguez * request_firmware_direct() will be used instead. 60c92316bfSLuis R. Rodriguez * @send_uevent: whether or not to send a uevent for async requests 61c92316bfSLuis R. Rodriguez * @num_requests: number of requests to try per test case. This is trigger 62c92316bfSLuis R. Rodriguez * specific. 63c92316bfSLuis R. Rodriguez * @reqs: stores all requests information 64c92316bfSLuis R. Rodriguez * @read_fw_idx: index of thread from which we want to read firmware results 65c92316bfSLuis R. Rodriguez * from through the read_fw trigger. 66c92316bfSLuis R. Rodriguez * @test_result: a test may use this to collect the result from the call 67c92316bfSLuis R. Rodriguez * of the request_firmware*() calls used in their tests. In order of 68c92316bfSLuis R. Rodriguez * priority we always keep first any setup error. If no setup errors were 69c92316bfSLuis R. Rodriguez * found then we move on to the first error encountered while running the 70c92316bfSLuis R. Rodriguez * API. Note that for async calls this typically will be a successful 71c92316bfSLuis R. Rodriguez * result (0) unless of course you've used bogus parameters, or the system 72c92316bfSLuis R. Rodriguez * is out of memory. In the async case the callback is expected to do a 73c92316bfSLuis R. Rodriguez * bit more homework to figure out what happened, unfortunately the only 74c92316bfSLuis R. Rodriguez * information passed today on error is the fact that no firmware was 75c92316bfSLuis R. Rodriguez * found so we can only assume -ENOENT on async calls if the firmware is 76c92316bfSLuis R. Rodriguez * NULL. 77c92316bfSLuis R. Rodriguez * 78c92316bfSLuis R. Rodriguez * Errors you can expect: 79c92316bfSLuis R. Rodriguez * 80c92316bfSLuis R. Rodriguez * API specific: 81c92316bfSLuis R. Rodriguez * 82c92316bfSLuis R. Rodriguez * 0: success for sync, for async it means request was sent 83c92316bfSLuis R. Rodriguez * -EINVAL: invalid parameters or request 84c92316bfSLuis R. Rodriguez * -ENOENT: files not found 85c92316bfSLuis R. Rodriguez * 86c92316bfSLuis R. Rodriguez * System environment: 87c92316bfSLuis R. Rodriguez * 88c92316bfSLuis R. Rodriguez * -ENOMEM: memory pressure on system 89c92316bfSLuis R. Rodriguez * -ENODEV: out of number of devices to test 90c92316bfSLuis R. Rodriguez * -EINVAL: an unexpected error has occurred 91c92316bfSLuis R. Rodriguez * @req_firmware: if @sync_direct is true this is set to 92c92316bfSLuis R. Rodriguez * request_firmware_direct(), otherwise request_firmware() 93c92316bfSLuis R. Rodriguez */ 94c92316bfSLuis R. Rodriguez struct test_config { 95c92316bfSLuis R. Rodriguez char *name; 967feebfa4SScott Branden bool into_buf; 97*5d90e05cSScott Branden size_t buf_size; 98*5d90e05cSScott Branden size_t file_offset; 99*5d90e05cSScott Branden bool partial; 100c92316bfSLuis R. Rodriguez bool sync_direct; 101c92316bfSLuis R. Rodriguez bool send_uevent; 102c92316bfSLuis R. Rodriguez u8 num_requests; 103c92316bfSLuis R. Rodriguez u8 read_fw_idx; 104c92316bfSLuis R. Rodriguez 105c92316bfSLuis R. Rodriguez /* 106c92316bfSLuis R. Rodriguez * These below don't belong her but we'll move them once we create 107c92316bfSLuis R. Rodriguez * a struct fw_test_device and stuff the misc_dev under there later. 108c92316bfSLuis R. Rodriguez */ 109c92316bfSLuis R. Rodriguez struct test_batched_req *reqs; 110c92316bfSLuis R. Rodriguez int test_result; 111c92316bfSLuis R. Rodriguez int (*req_firmware)(const struct firmware **fw, const char *name, 112c92316bfSLuis R. Rodriguez struct device *device); 113c92316bfSLuis R. Rodriguez }; 114c92316bfSLuis R. Rodriguez 11576f8ab1bSWei Yongjun static struct test_config *test_fw_config; 116c92316bfSLuis R. Rodriguez 1170a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 1180a8adf58SKees Cook size_t size, loff_t *offset) 1190a8adf58SKees Cook { 1200a8adf58SKees Cook ssize_t rc = 0; 1210a8adf58SKees Cook 1220a8adf58SKees Cook mutex_lock(&test_fw_mutex); 1230a8adf58SKees Cook if (test_firmware) 1240a8adf58SKees Cook rc = simple_read_from_buffer(buf, size, offset, 1250a8adf58SKees Cook test_firmware->data, 1260a8adf58SKees Cook test_firmware->size); 1270a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 1280a8adf58SKees Cook return rc; 1290a8adf58SKees Cook } 1300a8adf58SKees Cook 1310a8adf58SKees Cook static const struct file_operations test_fw_fops = { 1320a8adf58SKees Cook .owner = THIS_MODULE, 1330a8adf58SKees Cook .read = test_fw_misc_read, 1340a8adf58SKees Cook }; 1350a8adf58SKees Cook 136c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void) 137c92316bfSLuis R. Rodriguez { 138c92316bfSLuis R. Rodriguez struct test_batched_req *req; 139c92316bfSLuis R. Rodriguez u8 i; 140c92316bfSLuis R. Rodriguez 141c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) 142c92316bfSLuis R. Rodriguez return; 143c92316bfSLuis R. Rodriguez 144c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 145c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 146c92316bfSLuis R. Rodriguez if (req->fw) 147c92316bfSLuis R. Rodriguez release_firmware(req->fw); 148c92316bfSLuis R. Rodriguez } 149c92316bfSLuis R. Rodriguez 150c92316bfSLuis R. Rodriguez vfree(test_fw_config->reqs); 151c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 152c92316bfSLuis R. Rodriguez } 153c92316bfSLuis R. Rodriguez 154c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void) 155c92316bfSLuis R. Rodriguez { 156c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 157c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 158c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 159c92316bfSLuis R. Rodriguez } 160c92316bfSLuis R. Rodriguez 161c92316bfSLuis R. Rodriguez 162c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void) 163c92316bfSLuis R. Rodriguez { 164c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 165c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 166c92316bfSLuis R. Rodriguez test_fw_config->name = NULL; 167c92316bfSLuis R. Rodriguez } 168c92316bfSLuis R. Rodriguez 169c92316bfSLuis R. Rodriguez /* 170c92316bfSLuis R. Rodriguez * XXX: move to kstrncpy() once merged. 171c92316bfSLuis R. Rodriguez * 172c92316bfSLuis R. Rodriguez * Users should use kfree_const() when freeing these. 173c92316bfSLuis R. Rodriguez */ 174c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp) 175c92316bfSLuis R. Rodriguez { 176c92316bfSLuis R. Rodriguez *dst = kstrndup(name, count, gfp); 177c92316bfSLuis R. Rodriguez if (!*dst) 178c92316bfSLuis R. Rodriguez return -ENOSPC; 179c92316bfSLuis R. Rodriguez return count; 180c92316bfSLuis R. Rodriguez } 181c92316bfSLuis R. Rodriguez 182c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void) 183c92316bfSLuis R. Rodriguez { 184c92316bfSLuis R. Rodriguez int ret; 185c92316bfSLuis R. Rodriguez 186c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME, 187c92316bfSLuis R. Rodriguez strlen(TEST_FIRMWARE_NAME), GFP_KERNEL); 188c92316bfSLuis R. Rodriguez if (ret < 0) 189c92316bfSLuis R. Rodriguez goto out; 190c92316bfSLuis R. Rodriguez 191c92316bfSLuis R. Rodriguez test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS; 192c92316bfSLuis R. Rodriguez test_fw_config->send_uevent = true; 1937feebfa4SScott Branden test_fw_config->into_buf = false; 194*5d90e05cSScott Branden test_fw_config->buf_size = TEST_FIRMWARE_BUF_SIZE; 195*5d90e05cSScott Branden test_fw_config->file_offset = 0; 196*5d90e05cSScott Branden test_fw_config->partial = false; 197c92316bfSLuis R. Rodriguez test_fw_config->sync_direct = false; 198c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = request_firmware; 199c92316bfSLuis R. Rodriguez test_fw_config->test_result = 0; 200c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 201c92316bfSLuis R. Rodriguez 202c92316bfSLuis R. Rodriguez return 0; 203c92316bfSLuis R. Rodriguez 204c92316bfSLuis R. Rodriguez out: 205c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 206c92316bfSLuis R. Rodriguez return ret; 207c92316bfSLuis R. Rodriguez } 208c92316bfSLuis R. Rodriguez 209c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev, 210c92316bfSLuis R. Rodriguez struct device_attribute *attr, 211c92316bfSLuis R. Rodriguez const char *buf, size_t count) 212c92316bfSLuis R. Rodriguez { 213c92316bfSLuis R. Rodriguez int ret; 214c92316bfSLuis R. Rodriguez 215c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 216c92316bfSLuis R. Rodriguez 217c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 218c92316bfSLuis R. Rodriguez 219c92316bfSLuis R. Rodriguez ret = __test_firmware_config_init(); 220c92316bfSLuis R. Rodriguez if (ret < 0) { 221c92316bfSLuis R. Rodriguez ret = -ENOMEM; 222c92316bfSLuis R. Rodriguez pr_err("could not alloc settings for config trigger: %d\n", 223c92316bfSLuis R. Rodriguez ret); 224c92316bfSLuis R. Rodriguez goto out; 225c92316bfSLuis R. Rodriguez } 226c92316bfSLuis R. Rodriguez 227c92316bfSLuis R. Rodriguez pr_info("reset\n"); 228c92316bfSLuis R. Rodriguez ret = count; 229c92316bfSLuis R. Rodriguez 230c92316bfSLuis R. Rodriguez out: 231c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 232c92316bfSLuis R. Rodriguez 233c92316bfSLuis R. Rodriguez return ret; 234c92316bfSLuis R. Rodriguez } 235c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset); 236c92316bfSLuis R. Rodriguez 237c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev, 238c92316bfSLuis R. Rodriguez struct device_attribute *attr, 239c92316bfSLuis R. Rodriguez char *buf) 240c92316bfSLuis R. Rodriguez { 241c92316bfSLuis R. Rodriguez int len = 0; 242c92316bfSLuis R. Rodriguez 243c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 244c92316bfSLuis R. Rodriguez 245bd17cc5aSDan Carpenter len += scnprintf(buf, PAGE_SIZE - len, 246c92316bfSLuis R. Rodriguez "Custom trigger configuration for: %s\n", 247c92316bfSLuis R. Rodriguez dev_name(dev)); 248c92316bfSLuis R. Rodriguez 249c92316bfSLuis R. Rodriguez if (test_fw_config->name) 250bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 251c92316bfSLuis R. Rodriguez "name:\t%s\n", 252c92316bfSLuis R. Rodriguez test_fw_config->name); 253c92316bfSLuis R. Rodriguez else 254bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 255c92316bfSLuis R. Rodriguez "name:\tEMTPY\n"); 256c92316bfSLuis R. Rodriguez 257bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 258c92316bfSLuis R. Rodriguez "num_requests:\t%u\n", test_fw_config->num_requests); 259c92316bfSLuis R. Rodriguez 260bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 261c92316bfSLuis R. Rodriguez "send_uevent:\t\t%s\n", 262c92316bfSLuis R. Rodriguez test_fw_config->send_uevent ? 263c92316bfSLuis R. Rodriguez "FW_ACTION_HOTPLUG" : 264c92316bfSLuis R. Rodriguez "FW_ACTION_NOHOTPLUG"); 265bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 2667feebfa4SScott Branden "into_buf:\t\t%s\n", 2677feebfa4SScott Branden test_fw_config->into_buf ? "true" : "false"); 2687feebfa4SScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 269*5d90e05cSScott Branden "buf_size:\t%zu\n", test_fw_config->buf_size); 270*5d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 271*5d90e05cSScott Branden "file_offset:\t%zu\n", test_fw_config->file_offset); 272*5d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 273*5d90e05cSScott Branden "partial:\t\t%s\n", 274*5d90e05cSScott Branden test_fw_config->partial ? "true" : "false"); 275*5d90e05cSScott Branden len += scnprintf(buf + len, PAGE_SIZE - len, 276c92316bfSLuis R. Rodriguez "sync_direct:\t\t%s\n", 277c92316bfSLuis R. Rodriguez test_fw_config->sync_direct ? "true" : "false"); 278bd17cc5aSDan Carpenter len += scnprintf(buf + len, PAGE_SIZE - len, 279c92316bfSLuis R. Rodriguez "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); 280c92316bfSLuis R. Rodriguez 281c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 282c92316bfSLuis R. Rodriguez 283c92316bfSLuis R. Rodriguez return len; 284c92316bfSLuis R. Rodriguez } 285c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config); 286c92316bfSLuis R. Rodriguez 287c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev, 288c92316bfSLuis R. Rodriguez struct device_attribute *attr, 289c92316bfSLuis R. Rodriguez const char *buf, size_t count) 290c92316bfSLuis R. Rodriguez { 291c92316bfSLuis R. Rodriguez int ret; 292c92316bfSLuis R. Rodriguez 293c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 294c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 295c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL); 296c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 297c92316bfSLuis R. Rodriguez 298c92316bfSLuis R. Rodriguez return ret; 299c92316bfSLuis R. Rodriguez } 300c92316bfSLuis R. Rodriguez 301c92316bfSLuis R. Rodriguez /* 302c92316bfSLuis R. Rodriguez * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE. 303c92316bfSLuis R. Rodriguez */ 304c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst, 305c92316bfSLuis R. Rodriguez char *src) 306c92316bfSLuis R. Rodriguez { 307c92316bfSLuis R. Rodriguez int len; 308c92316bfSLuis R. Rodriguez 309c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 310c92316bfSLuis R. Rodriguez len = snprintf(dst, PAGE_SIZE, "%s\n", src); 311c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 312c92316bfSLuis R. Rodriguez 313c92316bfSLuis R. Rodriguez return len; 314c92316bfSLuis R. Rodriguez } 315c92316bfSLuis R. Rodriguez 316c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size, 317c92316bfSLuis R. Rodriguez bool *cfg) 318c92316bfSLuis R. Rodriguez { 319c92316bfSLuis R. Rodriguez int ret; 320c92316bfSLuis R. Rodriguez 321c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 322c92316bfSLuis R. Rodriguez if (strtobool(buf, cfg) < 0) 323c92316bfSLuis R. Rodriguez ret = -EINVAL; 324c92316bfSLuis R. Rodriguez else 325c92316bfSLuis R. Rodriguez ret = size; 326c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 327c92316bfSLuis R. Rodriguez 328c92316bfSLuis R. Rodriguez return ret; 329c92316bfSLuis R. Rodriguez } 330c92316bfSLuis R. Rodriguez 33155623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val) 332c92316bfSLuis R. Rodriguez { 333c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 334c92316bfSLuis R. Rodriguez } 335c92316bfSLuis R. Rodriguez 336*5d90e05cSScott Branden static int test_dev_config_update_size_t(const char *buf, 337*5d90e05cSScott Branden size_t size, 338*5d90e05cSScott Branden size_t *cfg) 339*5d90e05cSScott Branden { 340*5d90e05cSScott Branden int ret; 341*5d90e05cSScott Branden long new; 342*5d90e05cSScott Branden 343*5d90e05cSScott Branden ret = kstrtol(buf, 10, &new); 344*5d90e05cSScott Branden if (ret) 345*5d90e05cSScott Branden return ret; 346*5d90e05cSScott Branden 347*5d90e05cSScott Branden mutex_lock(&test_fw_mutex); 348*5d90e05cSScott Branden *(size_t *)cfg = new; 349*5d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 350*5d90e05cSScott Branden 351*5d90e05cSScott Branden /* Always return full write size even if we didn't consume all */ 352*5d90e05cSScott Branden return size; 353*5d90e05cSScott Branden } 354*5d90e05cSScott Branden 355*5d90e05cSScott Branden static ssize_t test_dev_config_show_size_t(char *buf, size_t val) 356*5d90e05cSScott Branden { 357*5d90e05cSScott Branden return snprintf(buf, PAGE_SIZE, "%zu\n", val); 358*5d90e05cSScott Branden } 359*5d90e05cSScott Branden 36055623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val) 361c92316bfSLuis R. Rodriguez { 362c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 363c92316bfSLuis R. Rodriguez } 364c92316bfSLuis R. Rodriguez 365c92316bfSLuis R. Rodriguez static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 366c92316bfSLuis R. Rodriguez { 367c92316bfSLuis R. Rodriguez int ret; 368c92316bfSLuis R. Rodriguez long new; 369c92316bfSLuis R. Rodriguez 370c92316bfSLuis R. Rodriguez ret = kstrtol(buf, 10, &new); 371c92316bfSLuis R. Rodriguez if (ret) 372c92316bfSLuis R. Rodriguez return ret; 373c92316bfSLuis R. Rodriguez 374c92316bfSLuis R. Rodriguez if (new > U8_MAX) 375c92316bfSLuis R. Rodriguez return -EINVAL; 376c92316bfSLuis R. Rodriguez 377c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 378c92316bfSLuis R. Rodriguez *(u8 *)cfg = new; 379c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 380c92316bfSLuis R. Rodriguez 381c92316bfSLuis R. Rodriguez /* Always return full write size even if we didn't consume all */ 382c92316bfSLuis R. Rodriguez return size; 383c92316bfSLuis R. Rodriguez } 384c92316bfSLuis R. Rodriguez 38555623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val) 386c92316bfSLuis R. Rodriguez { 387c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%u\n", val); 388c92316bfSLuis R. Rodriguez } 389c92316bfSLuis R. Rodriguez 390c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev, 391c92316bfSLuis R. Rodriguez struct device_attribute *attr, 392c92316bfSLuis R. Rodriguez char *buf) 393c92316bfSLuis R. Rodriguez { 394c92316bfSLuis R. Rodriguez return config_test_show_str(buf, test_fw_config->name); 395c92316bfSLuis R. Rodriguez } 396b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name); 397c92316bfSLuis R. Rodriguez 398c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev, 399c92316bfSLuis R. Rodriguez struct device_attribute *attr, 400c92316bfSLuis R. Rodriguez const char *buf, size_t count) 401c92316bfSLuis R. Rodriguez { 402c92316bfSLuis R. Rodriguez int rc; 403c92316bfSLuis R. Rodriguez 404c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 405c92316bfSLuis R. Rodriguez if (test_fw_config->reqs) { 406c92316bfSLuis R. Rodriguez pr_err("Must call release_all_firmware prior to changing config\n"); 407c92316bfSLuis R. Rodriguez rc = -EINVAL; 408a5e19233SWei Yongjun mutex_unlock(&test_fw_mutex); 409c92316bfSLuis R. Rodriguez goto out; 410c92316bfSLuis R. Rodriguez } 411c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 412c92316bfSLuis R. Rodriguez 413c92316bfSLuis R. Rodriguez rc = test_dev_config_update_u8(buf, count, 414c92316bfSLuis R. Rodriguez &test_fw_config->num_requests); 415c92316bfSLuis R. Rodriguez 416c92316bfSLuis R. Rodriguez out: 417c92316bfSLuis R. Rodriguez return rc; 418c92316bfSLuis R. Rodriguez } 419c92316bfSLuis R. Rodriguez 420c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev, 421c92316bfSLuis R. Rodriguez struct device_attribute *attr, 422c92316bfSLuis R. Rodriguez char *buf) 423c92316bfSLuis R. Rodriguez { 424c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->num_requests); 425c92316bfSLuis R. Rodriguez } 426b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests); 427c92316bfSLuis R. Rodriguez 4287feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev, 4297feebfa4SScott Branden struct device_attribute *attr, 4307feebfa4SScott Branden const char *buf, size_t count) 4317feebfa4SScott Branden { 4327feebfa4SScott Branden return test_dev_config_update_bool(buf, 4337feebfa4SScott Branden count, 4347feebfa4SScott Branden &test_fw_config->into_buf); 4357feebfa4SScott Branden } 4367feebfa4SScott Branden 4377feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev, 4387feebfa4SScott Branden struct device_attribute *attr, 4397feebfa4SScott Branden char *buf) 4407feebfa4SScott Branden { 4417feebfa4SScott Branden return test_dev_config_show_bool(buf, test_fw_config->into_buf); 4427feebfa4SScott Branden } 4437feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf); 4447feebfa4SScott Branden 445*5d90e05cSScott Branden static ssize_t config_buf_size_store(struct device *dev, 446*5d90e05cSScott Branden struct device_attribute *attr, 447*5d90e05cSScott Branden const char *buf, size_t count) 448*5d90e05cSScott Branden { 449*5d90e05cSScott Branden int rc; 450*5d90e05cSScott Branden 451*5d90e05cSScott Branden mutex_lock(&test_fw_mutex); 452*5d90e05cSScott Branden if (test_fw_config->reqs) { 453*5d90e05cSScott Branden pr_err("Must call release_all_firmware prior to changing config\n"); 454*5d90e05cSScott Branden rc = -EINVAL; 455*5d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 456*5d90e05cSScott Branden goto out; 457*5d90e05cSScott Branden } 458*5d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 459*5d90e05cSScott Branden 460*5d90e05cSScott Branden rc = test_dev_config_update_size_t(buf, count, 461*5d90e05cSScott Branden &test_fw_config->buf_size); 462*5d90e05cSScott Branden 463*5d90e05cSScott Branden out: 464*5d90e05cSScott Branden return rc; 465*5d90e05cSScott Branden } 466*5d90e05cSScott Branden 467*5d90e05cSScott Branden static ssize_t config_buf_size_show(struct device *dev, 468*5d90e05cSScott Branden struct device_attribute *attr, 469*5d90e05cSScott Branden char *buf) 470*5d90e05cSScott Branden { 471*5d90e05cSScott Branden return test_dev_config_show_size_t(buf, test_fw_config->buf_size); 472*5d90e05cSScott Branden } 473*5d90e05cSScott Branden static DEVICE_ATTR_RW(config_buf_size); 474*5d90e05cSScott Branden 475*5d90e05cSScott Branden static ssize_t config_file_offset_store(struct device *dev, 476*5d90e05cSScott Branden struct device_attribute *attr, 477*5d90e05cSScott Branden const char *buf, size_t count) 478*5d90e05cSScott Branden { 479*5d90e05cSScott Branden int rc; 480*5d90e05cSScott Branden 481*5d90e05cSScott Branden mutex_lock(&test_fw_mutex); 482*5d90e05cSScott Branden if (test_fw_config->reqs) { 483*5d90e05cSScott Branden pr_err("Must call release_all_firmware prior to changing config\n"); 484*5d90e05cSScott Branden rc = -EINVAL; 485*5d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 486*5d90e05cSScott Branden goto out; 487*5d90e05cSScott Branden } 488*5d90e05cSScott Branden mutex_unlock(&test_fw_mutex); 489*5d90e05cSScott Branden 490*5d90e05cSScott Branden rc = test_dev_config_update_size_t(buf, count, 491*5d90e05cSScott Branden &test_fw_config->file_offset); 492*5d90e05cSScott Branden 493*5d90e05cSScott Branden out: 494*5d90e05cSScott Branden return rc; 495*5d90e05cSScott Branden } 496*5d90e05cSScott Branden 497*5d90e05cSScott Branden static ssize_t config_file_offset_show(struct device *dev, 498*5d90e05cSScott Branden struct device_attribute *attr, 499*5d90e05cSScott Branden char *buf) 500*5d90e05cSScott Branden { 501*5d90e05cSScott Branden return test_dev_config_show_size_t(buf, test_fw_config->file_offset); 502*5d90e05cSScott Branden } 503*5d90e05cSScott Branden static DEVICE_ATTR_RW(config_file_offset); 504*5d90e05cSScott Branden 505*5d90e05cSScott Branden static ssize_t config_partial_store(struct device *dev, 506*5d90e05cSScott Branden struct device_attribute *attr, 507*5d90e05cSScott Branden const char *buf, size_t count) 508*5d90e05cSScott Branden { 509*5d90e05cSScott Branden return test_dev_config_update_bool(buf, 510*5d90e05cSScott Branden count, 511*5d90e05cSScott Branden &test_fw_config->partial); 512*5d90e05cSScott Branden } 513*5d90e05cSScott Branden 514*5d90e05cSScott Branden static ssize_t config_partial_show(struct device *dev, 515*5d90e05cSScott Branden struct device_attribute *attr, 516*5d90e05cSScott Branden char *buf) 517*5d90e05cSScott Branden { 518*5d90e05cSScott Branden return test_dev_config_show_bool(buf, test_fw_config->partial); 519*5d90e05cSScott Branden } 520*5d90e05cSScott Branden static DEVICE_ATTR_RW(config_partial); 521*5d90e05cSScott Branden 522c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev, 523c92316bfSLuis R. Rodriguez struct device_attribute *attr, 524c92316bfSLuis R. Rodriguez const char *buf, size_t count) 525c92316bfSLuis R. Rodriguez { 526c92316bfSLuis R. Rodriguez int rc = test_dev_config_update_bool(buf, count, 527c92316bfSLuis R. Rodriguez &test_fw_config->sync_direct); 528c92316bfSLuis R. Rodriguez 529c92316bfSLuis R. Rodriguez if (rc == count) 530c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = test_fw_config->sync_direct ? 531c92316bfSLuis R. Rodriguez request_firmware_direct : 532c92316bfSLuis R. Rodriguez request_firmware; 533c92316bfSLuis R. Rodriguez return rc; 534c92316bfSLuis R. Rodriguez } 535c92316bfSLuis R. Rodriguez 536c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev, 537c92316bfSLuis R. Rodriguez struct device_attribute *attr, 538c92316bfSLuis R. Rodriguez char *buf) 539c92316bfSLuis R. Rodriguez { 540c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->sync_direct); 541c92316bfSLuis R. Rodriguez } 542b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct); 543c92316bfSLuis R. Rodriguez 544c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev, 545c92316bfSLuis R. Rodriguez struct device_attribute *attr, 546c92316bfSLuis R. Rodriguez const char *buf, size_t count) 547c92316bfSLuis R. Rodriguez { 548c92316bfSLuis R. Rodriguez return test_dev_config_update_bool(buf, count, 549c92316bfSLuis R. Rodriguez &test_fw_config->send_uevent); 550c92316bfSLuis R. Rodriguez } 551c92316bfSLuis R. Rodriguez 552c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev, 553c92316bfSLuis R. Rodriguez struct device_attribute *attr, 554c92316bfSLuis R. Rodriguez char *buf) 555c92316bfSLuis R. Rodriguez { 556c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->send_uevent); 557c92316bfSLuis R. Rodriguez } 558b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent); 559c92316bfSLuis R. Rodriguez 560c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev, 561c92316bfSLuis R. Rodriguez struct device_attribute *attr, 562c92316bfSLuis R. Rodriguez const char *buf, size_t count) 563c92316bfSLuis R. Rodriguez { 564c92316bfSLuis R. Rodriguez return test_dev_config_update_u8(buf, count, 565c92316bfSLuis R. Rodriguez &test_fw_config->read_fw_idx); 566c92316bfSLuis R. Rodriguez } 567c92316bfSLuis R. Rodriguez 568c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev, 569c92316bfSLuis R. Rodriguez struct device_attribute *attr, 570c92316bfSLuis R. Rodriguez char *buf) 571c92316bfSLuis R. Rodriguez { 572c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx); 573c92316bfSLuis R. Rodriguez } 574b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx); 575c92316bfSLuis R. Rodriguez 576c92316bfSLuis R. Rodriguez 5770a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev, 5780a8adf58SKees Cook struct device_attribute *attr, 5790a8adf58SKees Cook const char *buf, size_t count) 5800a8adf58SKees Cook { 5810a8adf58SKees Cook int rc; 5820a8adf58SKees Cook char *name; 5830a8adf58SKees Cook 584be4a1326SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 5850a8adf58SKees Cook if (!name) 5860a8adf58SKees Cook return -ENOSPC; 5870a8adf58SKees Cook 5880a8adf58SKees Cook pr_info("loading '%s'\n", name); 5890a8adf58SKees Cook 5900a8adf58SKees Cook mutex_lock(&test_fw_mutex); 5910a8adf58SKees Cook release_firmware(test_firmware); 5920a8adf58SKees Cook test_firmware = NULL; 5930a8adf58SKees Cook rc = request_firmware(&test_firmware, name, dev); 59447e0bbb7SBrian Norris if (rc) { 5950a8adf58SKees Cook pr_info("load of '%s' failed: %d\n", name, rc); 59647e0bbb7SBrian Norris goto out; 59747e0bbb7SBrian Norris } 59847e0bbb7SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 59947e0bbb7SBrian Norris rc = count; 60047e0bbb7SBrian Norris 60147e0bbb7SBrian Norris out: 6020a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 6030a8adf58SKees Cook 6040a8adf58SKees Cook kfree(name); 6050a8adf58SKees Cook 60647e0bbb7SBrian Norris return rc; 6070a8adf58SKees Cook } 6080a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request); 6090a8adf58SKees Cook 610548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 611baaabecfSKees Cook extern struct list_head efi_embedded_fw_list; 612baaabecfSKees Cook extern bool efi_embedded_fw_checked; 613baaabecfSKees Cook 614548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev, 615548193cbSHans de Goede struct device_attribute *attr, 616548193cbSHans de Goede const char *buf, size_t count) 617548193cbSHans de Goede { 618548193cbSHans de Goede static const u8 test_data[] = { 619548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04, 620548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08, 621548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40, 622548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80 623548193cbSHans de Goede }; 624548193cbSHans de Goede struct efi_embedded_fw efi_embedded_fw; 625548193cbSHans de Goede const struct firmware *firmware = NULL; 626baaabecfSKees Cook bool saved_efi_embedded_fw_checked; 627548193cbSHans de Goede char *name; 628548193cbSHans de Goede int rc; 629548193cbSHans de Goede 630548193cbSHans de Goede name = kstrndup(buf, count, GFP_KERNEL); 631548193cbSHans de Goede if (!name) 632548193cbSHans de Goede return -ENOSPC; 633548193cbSHans de Goede 634548193cbSHans de Goede pr_info("inserting test platform fw '%s'\n", name); 635548193cbSHans de Goede efi_embedded_fw.name = name; 636548193cbSHans de Goede efi_embedded_fw.data = (void *)test_data; 637548193cbSHans de Goede efi_embedded_fw.length = sizeof(test_data); 638548193cbSHans de Goede list_add(&efi_embedded_fw.list, &efi_embedded_fw_list); 639baaabecfSKees Cook saved_efi_embedded_fw_checked = efi_embedded_fw_checked; 640baaabecfSKees Cook efi_embedded_fw_checked = true; 641548193cbSHans de Goede 642548193cbSHans de Goede pr_info("loading '%s'\n", name); 643548193cbSHans de Goede rc = firmware_request_platform(&firmware, name, dev); 644548193cbSHans de Goede if (rc) { 645548193cbSHans de Goede pr_info("load of '%s' failed: %d\n", name, rc); 646548193cbSHans de Goede goto out; 647548193cbSHans de Goede } 648548193cbSHans de Goede if (firmware->size != sizeof(test_data) || 649548193cbSHans de Goede memcmp(firmware->data, test_data, sizeof(test_data)) != 0) { 650548193cbSHans de Goede pr_info("firmware contents mismatch for '%s'\n", name); 651548193cbSHans de Goede rc = -EINVAL; 652548193cbSHans de Goede goto out; 653548193cbSHans de Goede } 654548193cbSHans de Goede pr_info("loaded: %zu\n", firmware->size); 655548193cbSHans de Goede rc = count; 656548193cbSHans de Goede 657548193cbSHans de Goede out: 658baaabecfSKees Cook efi_embedded_fw_checked = saved_efi_embedded_fw_checked; 659548193cbSHans de Goede release_firmware(firmware); 660548193cbSHans de Goede list_del(&efi_embedded_fw.list); 661548193cbSHans de Goede kfree(name); 662548193cbSHans de Goede 663548193cbSHans de Goede return rc; 664548193cbSHans de Goede } 665548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform); 666548193cbSHans de Goede #endif 667548193cbSHans de Goede 668eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done); 669eb910947SBrian Norris 670eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context) 671eb910947SBrian Norris { 672eb910947SBrian Norris test_firmware = fw; 673eb910947SBrian Norris complete(&async_fw_done); 674eb910947SBrian Norris } 675eb910947SBrian Norris 676eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev, 677eb910947SBrian Norris struct device_attribute *attr, 678eb910947SBrian Norris const char *buf, size_t count) 679eb910947SBrian Norris { 680eb910947SBrian Norris int rc; 681eb910947SBrian Norris char *name; 682eb910947SBrian Norris 683eb910947SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 684eb910947SBrian Norris if (!name) 685eb910947SBrian Norris return -ENOSPC; 686eb910947SBrian Norris 687eb910947SBrian Norris pr_info("loading '%s'\n", name); 688eb910947SBrian Norris 689eb910947SBrian Norris mutex_lock(&test_fw_mutex); 690eb910947SBrian Norris release_firmware(test_firmware); 691eb910947SBrian Norris test_firmware = NULL; 692eb910947SBrian Norris rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 693eb910947SBrian Norris NULL, trigger_async_request_cb); 694eb910947SBrian Norris if (rc) { 695eb910947SBrian Norris pr_info("async load of '%s' failed: %d\n", name, rc); 696eb910947SBrian Norris kfree(name); 697eb910947SBrian Norris goto out; 698eb910947SBrian Norris } 699eb910947SBrian Norris /* Free 'name' ASAP, to test for race conditions */ 700eb910947SBrian Norris kfree(name); 701eb910947SBrian Norris 702eb910947SBrian Norris wait_for_completion(&async_fw_done); 703eb910947SBrian Norris 704eb910947SBrian Norris if (test_firmware) { 705eb910947SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 706eb910947SBrian Norris rc = count; 707eb910947SBrian Norris } else { 708eb910947SBrian Norris pr_err("failed to async load firmware\n"); 7097feebfa4SScott Branden rc = -ENOMEM; 710eb910947SBrian Norris } 711eb910947SBrian Norris 712eb910947SBrian Norris out: 713eb910947SBrian Norris mutex_unlock(&test_fw_mutex); 714eb910947SBrian Norris 715eb910947SBrian Norris return rc; 716eb910947SBrian Norris } 717eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request); 718eb910947SBrian Norris 719061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev, 720061132d2SLuis R. Rodriguez struct device_attribute *attr, 721061132d2SLuis R. Rodriguez const char *buf, size_t count) 722061132d2SLuis R. Rodriguez { 723061132d2SLuis R. Rodriguez int rc; 724061132d2SLuis R. Rodriguez char *name; 725061132d2SLuis R. Rodriguez 726061132d2SLuis R. Rodriguez name = kstrndup(buf, count, GFP_KERNEL); 727061132d2SLuis R. Rodriguez if (!name) 728061132d2SLuis R. Rodriguez return -ENOSPC; 729061132d2SLuis R. Rodriguez 730061132d2SLuis R. Rodriguez pr_info("loading '%s' using custom fallback mechanism\n", name); 731061132d2SLuis R. Rodriguez 732061132d2SLuis R. Rodriguez mutex_lock(&test_fw_mutex); 733061132d2SLuis R. Rodriguez release_firmware(test_firmware); 734061132d2SLuis R. Rodriguez test_firmware = NULL; 735061132d2SLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name, 736061132d2SLuis R. Rodriguez dev, GFP_KERNEL, NULL, 737061132d2SLuis R. Rodriguez trigger_async_request_cb); 738061132d2SLuis R. Rodriguez if (rc) { 739061132d2SLuis R. Rodriguez pr_info("async load of '%s' failed: %d\n", name, rc); 740061132d2SLuis R. Rodriguez kfree(name); 741061132d2SLuis R. Rodriguez goto out; 742061132d2SLuis R. Rodriguez } 743061132d2SLuis R. Rodriguez /* Free 'name' ASAP, to test for race conditions */ 744061132d2SLuis R. Rodriguez kfree(name); 745061132d2SLuis R. Rodriguez 746061132d2SLuis R. Rodriguez wait_for_completion(&async_fw_done); 747061132d2SLuis R. Rodriguez 748061132d2SLuis R. Rodriguez if (test_firmware) { 749061132d2SLuis R. Rodriguez pr_info("loaded: %zu\n", test_firmware->size); 750061132d2SLuis R. Rodriguez rc = count; 751061132d2SLuis R. Rodriguez } else { 752061132d2SLuis R. Rodriguez pr_err("failed to async load firmware\n"); 753061132d2SLuis R. Rodriguez rc = -ENODEV; 754061132d2SLuis R. Rodriguez } 755061132d2SLuis R. Rodriguez 756061132d2SLuis R. Rodriguez out: 757061132d2SLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 758061132d2SLuis R. Rodriguez 759061132d2SLuis R. Rodriguez return rc; 760061132d2SLuis R. Rodriguez } 761061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback); 762061132d2SLuis R. Rodriguez 763c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data) 764c92316bfSLuis R. Rodriguez { 765c92316bfSLuis R. Rodriguez struct test_batched_req *req = data; 766c92316bfSLuis R. Rodriguez 767c92316bfSLuis R. Rodriguez if (!req) { 768c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 769c92316bfSLuis R. Rodriguez return -EINVAL; 770c92316bfSLuis R. Rodriguez } 771c92316bfSLuis R. Rodriguez 7727feebfa4SScott Branden if (test_fw_config->into_buf) { 7737feebfa4SScott Branden void *test_buf; 7747feebfa4SScott Branden 7757feebfa4SScott Branden test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); 7767feebfa4SScott Branden if (!test_buf) 7777feebfa4SScott Branden return -ENOSPC; 7787feebfa4SScott Branden 779*5d90e05cSScott Branden if (test_fw_config->partial) 780*5d90e05cSScott Branden req->rc = request_partial_firmware_into_buf 781*5d90e05cSScott Branden (&req->fw, 7827feebfa4SScott Branden req->name, 7837feebfa4SScott Branden req->dev, 7847feebfa4SScott Branden test_buf, 785*5d90e05cSScott Branden test_fw_config->buf_size, 786*5d90e05cSScott Branden test_fw_config->file_offset); 787*5d90e05cSScott Branden else 788*5d90e05cSScott Branden req->rc = request_firmware_into_buf 789*5d90e05cSScott Branden (&req->fw, 790*5d90e05cSScott Branden req->name, 791*5d90e05cSScott Branden req->dev, 792*5d90e05cSScott Branden test_buf, 793*5d90e05cSScott Branden test_fw_config->buf_size); 7947feebfa4SScott Branden if (!req->fw) 7957feebfa4SScott Branden kfree(test_buf); 7967feebfa4SScott Branden } else { 7977feebfa4SScott Branden req->rc = test_fw_config->req_firmware(&req->fw, 7987feebfa4SScott Branden req->name, 7997feebfa4SScott Branden req->dev); 8007feebfa4SScott Branden } 8017feebfa4SScott Branden 802c92316bfSLuis R. Rodriguez if (req->rc) { 803c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync load failed: %d\n", 804c92316bfSLuis R. Rodriguez req->idx, req->rc); 805c92316bfSLuis R. Rodriguez if (!test_fw_config->test_result) 806c92316bfSLuis R. Rodriguez test_fw_config->test_result = req->rc; 807c92316bfSLuis R. Rodriguez } else if (req->fw) { 808c92316bfSLuis R. Rodriguez req->sent = true; 809c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync loaded %zu\n", 810c92316bfSLuis R. Rodriguez req->idx, req->fw->size); 811c92316bfSLuis R. Rodriguez } 812c92316bfSLuis R. Rodriguez complete(&req->completion); 813c92316bfSLuis R. Rodriguez 814c92316bfSLuis R. Rodriguez req->task = NULL; 815c92316bfSLuis R. Rodriguez 816c92316bfSLuis R. Rodriguez return 0; 817c92316bfSLuis R. Rodriguez } 818c92316bfSLuis R. Rodriguez 819c92316bfSLuis R. Rodriguez /* 820c92316bfSLuis R. Rodriguez * We use a kthread as otherwise the kernel serializes all our sync requests 821c92316bfSLuis R. Rodriguez * and we would not be able to mimic batched requests on a sync call. Batched 822c92316bfSLuis R. Rodriguez * requests on a sync call can for instance happen on a device driver when 823c92316bfSLuis R. Rodriguez * multiple cards are used and firmware loading happens outside of probe. 824c92316bfSLuis R. Rodriguez */ 825c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev, 826c92316bfSLuis R. Rodriguez struct device_attribute *attr, 827c92316bfSLuis R. Rodriguez const char *buf, size_t count) 828c92316bfSLuis R. Rodriguez { 829c92316bfSLuis R. Rodriguez struct test_batched_req *req; 830c92316bfSLuis R. Rodriguez int rc; 831c92316bfSLuis R. Rodriguez u8 i; 832c92316bfSLuis R. Rodriguez 833c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 834c92316bfSLuis R. Rodriguez 835fad953ceSKees Cook test_fw_config->reqs = 836fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 837fad953ceSKees Cook test_fw_config->num_requests, 2)); 838c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 839c92316bfSLuis R. Rodriguez rc = -ENOMEM; 840c92316bfSLuis R. Rodriguez goto out_unlock; 841c92316bfSLuis R. Rodriguez } 842c92316bfSLuis R. Rodriguez 843c92316bfSLuis R. Rodriguez pr_info("batched sync firmware loading '%s' %u times\n", 844c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 845c92316bfSLuis R. Rodriguez 846c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 847c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 848c92316bfSLuis R. Rodriguez req->fw = NULL; 849c92316bfSLuis R. Rodriguez req->idx = i; 850c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 851c92316bfSLuis R. Rodriguez req->dev = dev; 852c92316bfSLuis R. Rodriguez init_completion(&req->completion); 853c92316bfSLuis R. Rodriguez req->task = kthread_run(test_fw_run_batch_request, req, 854c92316bfSLuis R. Rodriguez "%s-%u", KBUILD_MODNAME, req->idx); 855c92316bfSLuis R. Rodriguez if (!req->task || IS_ERR(req->task)) { 856c92316bfSLuis R. Rodriguez pr_err("Setting up thread %u failed\n", req->idx); 857c92316bfSLuis R. Rodriguez req->task = NULL; 858c92316bfSLuis R. Rodriguez rc = -ENOMEM; 859c92316bfSLuis R. Rodriguez goto out_bail; 860c92316bfSLuis R. Rodriguez } 861c92316bfSLuis R. Rodriguez } 862c92316bfSLuis R. Rodriguez 863c92316bfSLuis R. Rodriguez rc = count; 864c92316bfSLuis R. Rodriguez 865c92316bfSLuis R. Rodriguez /* 866c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 867c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 868c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 869c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 870c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 871c92316bfSLuis R. Rodriguez */ 872c92316bfSLuis R. Rodriguez 873c92316bfSLuis R. Rodriguez out_bail: 874c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 875c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 876c92316bfSLuis R. Rodriguez if (req->task || req->sent) 877c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 878c92316bfSLuis R. Rodriguez } 879c92316bfSLuis R. Rodriguez 880c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 881c92316bfSLuis R. Rodriguez if (rc < 0) 882c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 883c92316bfSLuis R. Rodriguez 884c92316bfSLuis R. Rodriguez out_unlock: 885c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 886c92316bfSLuis R. Rodriguez 887c92316bfSLuis R. Rodriguez return rc; 888c92316bfSLuis R. Rodriguez } 889c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests); 890c92316bfSLuis R. Rodriguez 891c92316bfSLuis R. Rodriguez /* 892c92316bfSLuis R. Rodriguez * We wait for each callback to return with the lock held, no need to lock here 893c92316bfSLuis R. Rodriguez */ 894c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context) 895c92316bfSLuis R. Rodriguez { 896c92316bfSLuis R. Rodriguez struct test_batched_req *req = context; 897c92316bfSLuis R. Rodriguez 898c92316bfSLuis R. Rodriguez if (!req) { 899c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 900c92316bfSLuis R. Rodriguez return; 901c92316bfSLuis R. Rodriguez } 902c92316bfSLuis R. Rodriguez 903c92316bfSLuis R. Rodriguez /* forces *some* batched requests to queue up */ 904c92316bfSLuis R. Rodriguez if (!req->idx) 905c92316bfSLuis R. Rodriguez ssleep(2); 906c92316bfSLuis R. Rodriguez 907c92316bfSLuis R. Rodriguez req->fw = fw; 908c92316bfSLuis R. Rodriguez 909c92316bfSLuis R. Rodriguez /* 910c92316bfSLuis R. Rodriguez * Unfortunately the firmware API gives us nothing other than a null FW 911c92316bfSLuis R. Rodriguez * if the firmware was not found on async requests. Best we can do is 912c92316bfSLuis R. Rodriguez * just assume -ENOENT. A better API would pass the actual return 913c92316bfSLuis R. Rodriguez * value to the callback. 914c92316bfSLuis R. Rodriguez */ 915c92316bfSLuis R. Rodriguez if (!fw && !test_fw_config->test_result) 916c92316bfSLuis R. Rodriguez test_fw_config->test_result = -ENOENT; 917c92316bfSLuis R. Rodriguez 918c92316bfSLuis R. Rodriguez complete(&req->completion); 919c92316bfSLuis R. Rodriguez } 920c92316bfSLuis R. Rodriguez 921c92316bfSLuis R. Rodriguez static 922c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev, 923c92316bfSLuis R. Rodriguez struct device_attribute *attr, 924c92316bfSLuis R. Rodriguez const char *buf, size_t count) 925c92316bfSLuis R. Rodriguez { 926c92316bfSLuis R. Rodriguez struct test_batched_req *req; 927c92316bfSLuis R. Rodriguez bool send_uevent; 928c92316bfSLuis R. Rodriguez int rc; 929c92316bfSLuis R. Rodriguez u8 i; 930c92316bfSLuis R. Rodriguez 931c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 932c92316bfSLuis R. Rodriguez 933fad953ceSKees Cook test_fw_config->reqs = 934fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 935fad953ceSKees Cook test_fw_config->num_requests, 2)); 936c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 937c92316bfSLuis R. Rodriguez rc = -ENOMEM; 938c92316bfSLuis R. Rodriguez goto out; 939c92316bfSLuis R. Rodriguez } 940c92316bfSLuis R. Rodriguez 941c92316bfSLuis R. Rodriguez pr_info("batched loading '%s' custom fallback mechanism %u times\n", 942c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 943c92316bfSLuis R. Rodriguez 944c92316bfSLuis R. Rodriguez send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG : 945c92316bfSLuis R. Rodriguez FW_ACTION_NOHOTPLUG; 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->name = test_fw_config->name; 950c92316bfSLuis R. Rodriguez req->fw = NULL; 951c92316bfSLuis R. Rodriguez req->idx = i; 952c92316bfSLuis R. Rodriguez init_completion(&req->completion); 953c92316bfSLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, send_uevent, 954c92316bfSLuis R. Rodriguez req->name, 955c92316bfSLuis R. Rodriguez dev, GFP_KERNEL, req, 956c92316bfSLuis R. Rodriguez trigger_batched_cb); 957c92316bfSLuis R. Rodriguez if (rc) { 958c92316bfSLuis R. Rodriguez pr_info("#%u: batched async load failed setup: %d\n", 959c92316bfSLuis R. Rodriguez i, rc); 960c92316bfSLuis R. Rodriguez req->rc = rc; 961c92316bfSLuis R. Rodriguez goto out_bail; 962c92316bfSLuis R. Rodriguez } else 963c92316bfSLuis R. Rodriguez req->sent = true; 964c92316bfSLuis R. Rodriguez } 965c92316bfSLuis R. Rodriguez 966c92316bfSLuis R. Rodriguez rc = count; 967c92316bfSLuis R. Rodriguez 968c92316bfSLuis R. Rodriguez out_bail: 969c92316bfSLuis R. Rodriguez 970c92316bfSLuis R. Rodriguez /* 971c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 972c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 973c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 974c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 975c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 976c92316bfSLuis R. Rodriguez */ 977c92316bfSLuis R. Rodriguez 978c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 979c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 980c92316bfSLuis R. Rodriguez if (req->sent) 981c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 982c92316bfSLuis R. Rodriguez } 983c92316bfSLuis R. Rodriguez 984c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 985c92316bfSLuis R. Rodriguez if (rc < 0) 986c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 987c92316bfSLuis R. Rodriguez 988c92316bfSLuis R. Rodriguez out: 989c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 990c92316bfSLuis R. Rodriguez 991c92316bfSLuis R. Rodriguez return rc; 992c92316bfSLuis R. Rodriguez } 993c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async); 994c92316bfSLuis R. Rodriguez 995c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev, 996c92316bfSLuis R. Rodriguez struct device_attribute *attr, 997c92316bfSLuis R. Rodriguez char *buf) 998c92316bfSLuis R. Rodriguez { 999c92316bfSLuis R. Rodriguez return test_dev_config_show_int(buf, test_fw_config->test_result); 1000c92316bfSLuis R. Rodriguez } 1001c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result); 1002c92316bfSLuis R. Rodriguez 1003c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_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 test_release_all_firmware(); 1008c92316bfSLuis R. Rodriguez return count; 1009c92316bfSLuis R. Rodriguez } 1010c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware); 1011c92316bfSLuis R. Rodriguez 1012c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev, 1013c92316bfSLuis R. Rodriguez struct device_attribute *attr, 1014c92316bfSLuis R. Rodriguez char *buf) 1015c92316bfSLuis R. Rodriguez { 1016c92316bfSLuis R. Rodriguez struct test_batched_req *req; 1017c92316bfSLuis R. Rodriguez u8 idx; 1018c92316bfSLuis R. Rodriguez ssize_t rc = 0; 1019c92316bfSLuis R. Rodriguez 1020c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 1021c92316bfSLuis R. Rodriguez 1022c92316bfSLuis R. Rodriguez idx = test_fw_config->read_fw_idx; 1023c92316bfSLuis R. Rodriguez if (idx >= test_fw_config->num_requests) { 1024c92316bfSLuis R. Rodriguez rc = -ERANGE; 1025c92316bfSLuis R. Rodriguez goto out; 1026c92316bfSLuis R. Rodriguez } 1027c92316bfSLuis R. Rodriguez 1028c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 1029c92316bfSLuis R. Rodriguez rc = -EINVAL; 1030c92316bfSLuis R. Rodriguez goto out; 1031c92316bfSLuis R. Rodriguez } 1032c92316bfSLuis R. Rodriguez 1033c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[idx]; 1034c92316bfSLuis R. Rodriguez if (!req->fw) { 1035c92316bfSLuis R. Rodriguez pr_err("#%u: failed to async load firmware\n", idx); 1036c92316bfSLuis R. Rodriguez rc = -ENOENT; 1037c92316bfSLuis R. Rodriguez goto out; 1038c92316bfSLuis R. Rodriguez } 1039c92316bfSLuis R. Rodriguez 1040c92316bfSLuis R. Rodriguez pr_info("#%u: loaded %zu\n", idx, req->fw->size); 1041c92316bfSLuis R. Rodriguez 1042c92316bfSLuis R. Rodriguez if (req->fw->size > PAGE_SIZE) { 1043c92316bfSLuis R. Rodriguez pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 1044c92316bfSLuis R. Rodriguez rc = -EINVAL; 10458bb0a886SColin Ian King goto out; 1046c92316bfSLuis R. Rodriguez } 1047c92316bfSLuis R. Rodriguez memcpy(buf, req->fw->data, req->fw->size); 1048c92316bfSLuis R. Rodriguez 1049c92316bfSLuis R. Rodriguez rc = req->fw->size; 1050c92316bfSLuis R. Rodriguez out: 1051c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1052c92316bfSLuis R. Rodriguez 1053c92316bfSLuis R. Rodriguez return rc; 1054c92316bfSLuis R. Rodriguez } 1055c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware); 1056c92316bfSLuis R. Rodriguez 1057083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr 1058083a93b0SLuis R. Rodriguez 1059083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = { 1060c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(reset), 1061c92316bfSLuis R. Rodriguez 1062c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config), 1063c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_name), 1064c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_num_requests), 10657feebfa4SScott Branden TEST_FW_DEV_ATTR(config_into_buf), 1066*5d90e05cSScott Branden TEST_FW_DEV_ATTR(config_buf_size), 1067*5d90e05cSScott Branden TEST_FW_DEV_ATTR(config_file_offset), 1068*5d90e05cSScott Branden TEST_FW_DEV_ATTR(config_partial), 1069c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_sync_direct), 1070c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_send_uevent), 1071c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_read_fw_idx), 1072c92316bfSLuis R. Rodriguez 1073c92316bfSLuis R. Rodriguez /* These don't use the config at all - they could be ported! */ 1074083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_request), 1075083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_async_request), 1076061132d2SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_custom_fallback), 1077548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 1078548193cbSHans de Goede TEST_FW_DEV_ATTR(trigger_request_platform), 1079548193cbSHans de Goede #endif 1080c92316bfSLuis R. Rodriguez 1081c92316bfSLuis R. Rodriguez /* These use the config and can use the test_result */ 1082c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests), 1083c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests_async), 1084c92316bfSLuis R. Rodriguez 1085c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(release_all_firmware), 1086c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(test_result), 1087c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(read_firmware), 1088083a93b0SLuis R. Rodriguez NULL, 1089083a93b0SLuis R. Rodriguez }; 1090083a93b0SLuis R. Rodriguez 1091083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev); 1092083a93b0SLuis R. Rodriguez 109367fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = { 109467fd553cSLuis R. Rodriguez .minor = MISC_DYNAMIC_MINOR, 109567fd553cSLuis R. Rodriguez .name = "test_firmware", 109667fd553cSLuis R. Rodriguez .fops = &test_fw_fops, 1097083a93b0SLuis R. Rodriguez .groups = test_dev_groups, 109867fd553cSLuis R. Rodriguez }; 109967fd553cSLuis R. Rodriguez 11000a8adf58SKees Cook static int __init test_firmware_init(void) 11010a8adf58SKees Cook { 11020a8adf58SKees Cook int rc; 11030a8adf58SKees Cook 1104c92316bfSLuis R. Rodriguez test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL); 1105c92316bfSLuis R. Rodriguez if (!test_fw_config) 1106c92316bfSLuis R. Rodriguez return -ENOMEM; 1107c92316bfSLuis R. Rodriguez 1108c92316bfSLuis R. Rodriguez rc = __test_firmware_config_init(); 1109d4fddac5SWenwen Wang if (rc) { 1110d4fddac5SWenwen Wang kfree(test_fw_config); 1111d4fddac5SWenwen Wang pr_err("could not init firmware test config: %d\n", rc); 1112c92316bfSLuis R. Rodriguez return rc; 1113d4fddac5SWenwen Wang } 1114c92316bfSLuis R. Rodriguez 11150a8adf58SKees Cook rc = misc_register(&test_fw_misc_device); 11160a8adf58SKees Cook if (rc) { 1117c92316bfSLuis R. Rodriguez kfree(test_fw_config); 11180a8adf58SKees Cook pr_err("could not register misc device: %d\n", rc); 11190a8adf58SKees Cook return rc; 11200a8adf58SKees Cook } 1121eb910947SBrian Norris 11220a8adf58SKees Cook pr_warn("interface ready\n"); 11230a8adf58SKees Cook 11240a8adf58SKees Cook return 0; 11250a8adf58SKees Cook } 11260a8adf58SKees Cook 11270a8adf58SKees Cook module_init(test_firmware_init); 11280a8adf58SKees Cook 11290a8adf58SKees Cook static void __exit test_firmware_exit(void) 11300a8adf58SKees Cook { 1131c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 11320a8adf58SKees Cook release_firmware(test_firmware); 11330a8adf58SKees Cook misc_deregister(&test_fw_misc_device); 1134c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 1135c92316bfSLuis R. Rodriguez kfree(test_fw_config); 1136c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1137c92316bfSLuis R. Rodriguez 11380a8adf58SKees Cook pr_warn("removed interface\n"); 11390a8adf58SKees Cook } 11400a8adf58SKees Cook 11410a8adf58SKees Cook module_exit(test_firmware_exit); 11420a8adf58SKees Cook 11430a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>"); 11440a8adf58SKees Cook MODULE_LICENSE("GPL"); 1145