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 29*baaabecfSKees Cook MODULE_IMPORT_NS(TEST_FIRMWARE); 30*baaabecfSKees 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. 55c92316bfSLuis R. Rodriguez * @sync_direct: when the sync trigger is used if this is true 56c92316bfSLuis R. Rodriguez * request_firmware_direct() will be used instead. 57c92316bfSLuis R. Rodriguez * @send_uevent: whether or not to send a uevent for async requests 58c92316bfSLuis R. Rodriguez * @num_requests: number of requests to try per test case. This is trigger 59c92316bfSLuis R. Rodriguez * specific. 60c92316bfSLuis R. Rodriguez * @reqs: stores all requests information 61c92316bfSLuis R. Rodriguez * @read_fw_idx: index of thread from which we want to read firmware results 62c92316bfSLuis R. Rodriguez * from through the read_fw trigger. 63c92316bfSLuis R. Rodriguez * @test_result: a test may use this to collect the result from the call 64c92316bfSLuis R. Rodriguez * of the request_firmware*() calls used in their tests. In order of 65c92316bfSLuis R. Rodriguez * priority we always keep first any setup error. If no setup errors were 66c92316bfSLuis R. Rodriguez * found then we move on to the first error encountered while running the 67c92316bfSLuis R. Rodriguez * API. Note that for async calls this typically will be a successful 68c92316bfSLuis R. Rodriguez * result (0) unless of course you've used bogus parameters, or the system 69c92316bfSLuis R. Rodriguez * is out of memory. In the async case the callback is expected to do a 70c92316bfSLuis R. Rodriguez * bit more homework to figure out what happened, unfortunately the only 71c92316bfSLuis R. Rodriguez * information passed today on error is the fact that no firmware was 72c92316bfSLuis R. Rodriguez * found so we can only assume -ENOENT on async calls if the firmware is 73c92316bfSLuis R. Rodriguez * NULL. 74c92316bfSLuis R. Rodriguez * 75c92316bfSLuis R. Rodriguez * Errors you can expect: 76c92316bfSLuis R. Rodriguez * 77c92316bfSLuis R. Rodriguez * API specific: 78c92316bfSLuis R. Rodriguez * 79c92316bfSLuis R. Rodriguez * 0: success for sync, for async it means request was sent 80c92316bfSLuis R. Rodriguez * -EINVAL: invalid parameters or request 81c92316bfSLuis R. Rodriguez * -ENOENT: files not found 82c92316bfSLuis R. Rodriguez * 83c92316bfSLuis R. Rodriguez * System environment: 84c92316bfSLuis R. Rodriguez * 85c92316bfSLuis R. Rodriguez * -ENOMEM: memory pressure on system 86c92316bfSLuis R. Rodriguez * -ENODEV: out of number of devices to test 87c92316bfSLuis R. Rodriguez * -EINVAL: an unexpected error has occurred 88c92316bfSLuis R. Rodriguez * @req_firmware: if @sync_direct is true this is set to 89c92316bfSLuis R. Rodriguez * request_firmware_direct(), otherwise request_firmware() 90c92316bfSLuis R. Rodriguez */ 91c92316bfSLuis R. Rodriguez struct test_config { 92c92316bfSLuis R. Rodriguez char *name; 937feebfa4SScott Branden bool into_buf; 94c92316bfSLuis R. Rodriguez bool sync_direct; 95c92316bfSLuis R. Rodriguez bool send_uevent; 96c92316bfSLuis R. Rodriguez u8 num_requests; 97c92316bfSLuis R. Rodriguez u8 read_fw_idx; 98c92316bfSLuis R. Rodriguez 99c92316bfSLuis R. Rodriguez /* 100c92316bfSLuis R. Rodriguez * These below don't belong her but we'll move them once we create 101c92316bfSLuis R. Rodriguez * a struct fw_test_device and stuff the misc_dev under there later. 102c92316bfSLuis R. Rodriguez */ 103c92316bfSLuis R. Rodriguez struct test_batched_req *reqs; 104c92316bfSLuis R. Rodriguez int test_result; 105c92316bfSLuis R. Rodriguez int (*req_firmware)(const struct firmware **fw, const char *name, 106c92316bfSLuis R. Rodriguez struct device *device); 107c92316bfSLuis R. Rodriguez }; 108c92316bfSLuis R. Rodriguez 10976f8ab1bSWei Yongjun static struct test_config *test_fw_config; 110c92316bfSLuis R. Rodriguez 1110a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 1120a8adf58SKees Cook size_t size, loff_t *offset) 1130a8adf58SKees Cook { 1140a8adf58SKees Cook ssize_t rc = 0; 1150a8adf58SKees Cook 1160a8adf58SKees Cook mutex_lock(&test_fw_mutex); 1170a8adf58SKees Cook if (test_firmware) 1180a8adf58SKees Cook rc = simple_read_from_buffer(buf, size, offset, 1190a8adf58SKees Cook test_firmware->data, 1200a8adf58SKees Cook test_firmware->size); 1210a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 1220a8adf58SKees Cook return rc; 1230a8adf58SKees Cook } 1240a8adf58SKees Cook 1250a8adf58SKees Cook static const struct file_operations test_fw_fops = { 1260a8adf58SKees Cook .owner = THIS_MODULE, 1270a8adf58SKees Cook .read = test_fw_misc_read, 1280a8adf58SKees Cook }; 1290a8adf58SKees Cook 130c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void) 131c92316bfSLuis R. Rodriguez { 132c92316bfSLuis R. Rodriguez struct test_batched_req *req; 133c92316bfSLuis R. Rodriguez u8 i; 134c92316bfSLuis R. Rodriguez 135c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) 136c92316bfSLuis R. Rodriguez return; 137c92316bfSLuis R. Rodriguez 138c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 139c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 140c92316bfSLuis R. Rodriguez if (req->fw) 141c92316bfSLuis R. Rodriguez release_firmware(req->fw); 142c92316bfSLuis R. Rodriguez } 143c92316bfSLuis R. Rodriguez 144c92316bfSLuis R. Rodriguez vfree(test_fw_config->reqs); 145c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 146c92316bfSLuis R. Rodriguez } 147c92316bfSLuis R. Rodriguez 148c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void) 149c92316bfSLuis R. Rodriguez { 150c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 151c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 152c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 153c92316bfSLuis R. Rodriguez } 154c92316bfSLuis R. Rodriguez 155c92316bfSLuis R. Rodriguez 156c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void) 157c92316bfSLuis R. Rodriguez { 158c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 159c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 160c92316bfSLuis R. Rodriguez test_fw_config->name = NULL; 161c92316bfSLuis R. Rodriguez } 162c92316bfSLuis R. Rodriguez 163c92316bfSLuis R. Rodriguez /* 164c92316bfSLuis R. Rodriguez * XXX: move to kstrncpy() once merged. 165c92316bfSLuis R. Rodriguez * 166c92316bfSLuis R. Rodriguez * Users should use kfree_const() when freeing these. 167c92316bfSLuis R. Rodriguez */ 168c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp) 169c92316bfSLuis R. Rodriguez { 170c92316bfSLuis R. Rodriguez *dst = kstrndup(name, count, gfp); 171c92316bfSLuis R. Rodriguez if (!*dst) 172c92316bfSLuis R. Rodriguez return -ENOSPC; 173c92316bfSLuis R. Rodriguez return count; 174c92316bfSLuis R. Rodriguez } 175c92316bfSLuis R. Rodriguez 176c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void) 177c92316bfSLuis R. Rodriguez { 178c92316bfSLuis R. Rodriguez int ret; 179c92316bfSLuis R. Rodriguez 180c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME, 181c92316bfSLuis R. Rodriguez strlen(TEST_FIRMWARE_NAME), GFP_KERNEL); 182c92316bfSLuis R. Rodriguez if (ret < 0) 183c92316bfSLuis R. Rodriguez goto out; 184c92316bfSLuis R. Rodriguez 185c92316bfSLuis R. Rodriguez test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS; 186c92316bfSLuis R. Rodriguez test_fw_config->send_uevent = true; 1877feebfa4SScott Branden test_fw_config->into_buf = false; 188c92316bfSLuis R. Rodriguez test_fw_config->sync_direct = false; 189c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = request_firmware; 190c92316bfSLuis R. Rodriguez test_fw_config->test_result = 0; 191c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 192c92316bfSLuis R. Rodriguez 193c92316bfSLuis R. Rodriguez return 0; 194c92316bfSLuis R. Rodriguez 195c92316bfSLuis R. Rodriguez out: 196c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 197c92316bfSLuis R. Rodriguez return ret; 198c92316bfSLuis R. Rodriguez } 199c92316bfSLuis R. Rodriguez 200c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev, 201c92316bfSLuis R. Rodriguez struct device_attribute *attr, 202c92316bfSLuis R. Rodriguez const char *buf, size_t count) 203c92316bfSLuis R. Rodriguez { 204c92316bfSLuis R. Rodriguez int ret; 205c92316bfSLuis R. Rodriguez 206c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 207c92316bfSLuis R. Rodriguez 208c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 209c92316bfSLuis R. Rodriguez 210c92316bfSLuis R. Rodriguez ret = __test_firmware_config_init(); 211c92316bfSLuis R. Rodriguez if (ret < 0) { 212c92316bfSLuis R. Rodriguez ret = -ENOMEM; 213c92316bfSLuis R. Rodriguez pr_err("could not alloc settings for config trigger: %d\n", 214c92316bfSLuis R. Rodriguez ret); 215c92316bfSLuis R. Rodriguez goto out; 216c92316bfSLuis R. Rodriguez } 217c92316bfSLuis R. Rodriguez 218c92316bfSLuis R. Rodriguez pr_info("reset\n"); 219c92316bfSLuis R. Rodriguez ret = count; 220c92316bfSLuis R. Rodriguez 221c92316bfSLuis R. Rodriguez out: 222c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 223c92316bfSLuis R. Rodriguez 224c92316bfSLuis R. Rodriguez return ret; 225c92316bfSLuis R. Rodriguez } 226c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset); 227c92316bfSLuis R. Rodriguez 228c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev, 229c92316bfSLuis R. Rodriguez struct device_attribute *attr, 230c92316bfSLuis R. Rodriguez char *buf) 231c92316bfSLuis R. Rodriguez { 232c92316bfSLuis R. Rodriguez int len = 0; 233c92316bfSLuis R. Rodriguez 234c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 235c92316bfSLuis R. Rodriguez 236bd17cc5aSDan Carpenter len += scnprintf(buf, PAGE_SIZE - len, 237c92316bfSLuis R. Rodriguez "Custom trigger configuration for: %s\n", 238c92316bfSLuis R. Rodriguez dev_name(dev)); 239c92316bfSLuis R. Rodriguez 240c92316bfSLuis R. Rodriguez if (test_fw_config->name) 241bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 242c92316bfSLuis R. Rodriguez "name:\t%s\n", 243c92316bfSLuis R. Rodriguez test_fw_config->name); 244c92316bfSLuis R. Rodriguez else 245bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 246c92316bfSLuis R. Rodriguez "name:\tEMTPY\n"); 247c92316bfSLuis R. Rodriguez 248bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 249c92316bfSLuis R. Rodriguez "num_requests:\t%u\n", test_fw_config->num_requests); 250c92316bfSLuis R. Rodriguez 251bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 252c92316bfSLuis R. Rodriguez "send_uevent:\t\t%s\n", 253c92316bfSLuis R. Rodriguez test_fw_config->send_uevent ? 254c92316bfSLuis R. Rodriguez "FW_ACTION_HOTPLUG" : 255c92316bfSLuis R. Rodriguez "FW_ACTION_NOHOTPLUG"); 256bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 2577feebfa4SScott Branden "into_buf:\t\t%s\n", 2587feebfa4SScott Branden test_fw_config->into_buf ? "true" : "false"); 2597feebfa4SScott Branden len += scnprintf(buf+len, PAGE_SIZE - len, 260c92316bfSLuis R. Rodriguez "sync_direct:\t\t%s\n", 261c92316bfSLuis R. Rodriguez test_fw_config->sync_direct ? "true" : "false"); 262bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 263c92316bfSLuis R. Rodriguez "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); 264c92316bfSLuis R. Rodriguez 265c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 266c92316bfSLuis R. Rodriguez 267c92316bfSLuis R. Rodriguez return len; 268c92316bfSLuis R. Rodriguez } 269c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config); 270c92316bfSLuis R. Rodriguez 271c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev, 272c92316bfSLuis R. Rodriguez struct device_attribute *attr, 273c92316bfSLuis R. Rodriguez const char *buf, size_t count) 274c92316bfSLuis R. Rodriguez { 275c92316bfSLuis R. Rodriguez int ret; 276c92316bfSLuis R. Rodriguez 277c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 278c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 279c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL); 280c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 281c92316bfSLuis R. Rodriguez 282c92316bfSLuis R. Rodriguez return ret; 283c92316bfSLuis R. Rodriguez } 284c92316bfSLuis R. Rodriguez 285c92316bfSLuis R. Rodriguez /* 286c92316bfSLuis R. Rodriguez * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE. 287c92316bfSLuis R. Rodriguez */ 288c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst, 289c92316bfSLuis R. Rodriguez char *src) 290c92316bfSLuis R. Rodriguez { 291c92316bfSLuis R. Rodriguez int len; 292c92316bfSLuis R. Rodriguez 293c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 294c92316bfSLuis R. Rodriguez len = snprintf(dst, PAGE_SIZE, "%s\n", src); 295c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 296c92316bfSLuis R. Rodriguez 297c92316bfSLuis R. Rodriguez return len; 298c92316bfSLuis R. Rodriguez } 299c92316bfSLuis R. Rodriguez 300c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size, 301c92316bfSLuis R. Rodriguez bool *cfg) 302c92316bfSLuis R. Rodriguez { 303c92316bfSLuis R. Rodriguez int ret; 304c92316bfSLuis R. Rodriguez 305c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 306c92316bfSLuis R. Rodriguez if (strtobool(buf, cfg) < 0) 307c92316bfSLuis R. Rodriguez ret = -EINVAL; 308c92316bfSLuis R. Rodriguez else 309c92316bfSLuis R. Rodriguez ret = size; 310c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 311c92316bfSLuis R. Rodriguez 312c92316bfSLuis R. Rodriguez return ret; 313c92316bfSLuis R. Rodriguez } 314c92316bfSLuis R. Rodriguez 31555623260SScott Branden static ssize_t test_dev_config_show_bool(char *buf, bool val) 316c92316bfSLuis R. Rodriguez { 317c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 318c92316bfSLuis R. Rodriguez } 319c92316bfSLuis R. Rodriguez 32055623260SScott Branden static ssize_t test_dev_config_show_int(char *buf, int val) 321c92316bfSLuis R. Rodriguez { 322c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 323c92316bfSLuis R. Rodriguez } 324c92316bfSLuis R. Rodriguez 325c92316bfSLuis R. Rodriguez static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 326c92316bfSLuis R. Rodriguez { 327c92316bfSLuis R. Rodriguez int ret; 328c92316bfSLuis R. Rodriguez long new; 329c92316bfSLuis R. Rodriguez 330c92316bfSLuis R. Rodriguez ret = kstrtol(buf, 10, &new); 331c92316bfSLuis R. Rodriguez if (ret) 332c92316bfSLuis R. Rodriguez return ret; 333c92316bfSLuis R. Rodriguez 334c92316bfSLuis R. Rodriguez if (new > U8_MAX) 335c92316bfSLuis R. Rodriguez return -EINVAL; 336c92316bfSLuis R. Rodriguez 337c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 338c92316bfSLuis R. Rodriguez *(u8 *)cfg = new; 339c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 340c92316bfSLuis R. Rodriguez 341c92316bfSLuis R. Rodriguez /* Always return full write size even if we didn't consume all */ 342c92316bfSLuis R. Rodriguez return size; 343c92316bfSLuis R. Rodriguez } 344c92316bfSLuis R. Rodriguez 34555623260SScott Branden static ssize_t test_dev_config_show_u8(char *buf, u8 val) 346c92316bfSLuis R. Rodriguez { 347c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%u\n", val); 348c92316bfSLuis R. Rodriguez } 349c92316bfSLuis R. Rodriguez 350c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev, 351c92316bfSLuis R. Rodriguez struct device_attribute *attr, 352c92316bfSLuis R. Rodriguez char *buf) 353c92316bfSLuis R. Rodriguez { 354c92316bfSLuis R. Rodriguez return config_test_show_str(buf, test_fw_config->name); 355c92316bfSLuis R. Rodriguez } 356b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name); 357c92316bfSLuis R. Rodriguez 358c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev, 359c92316bfSLuis R. Rodriguez struct device_attribute *attr, 360c92316bfSLuis R. Rodriguez const char *buf, size_t count) 361c92316bfSLuis R. Rodriguez { 362c92316bfSLuis R. Rodriguez int rc; 363c92316bfSLuis R. Rodriguez 364c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 365c92316bfSLuis R. Rodriguez if (test_fw_config->reqs) { 366c92316bfSLuis R. Rodriguez pr_err("Must call release_all_firmware prior to changing config\n"); 367c92316bfSLuis R. Rodriguez rc = -EINVAL; 368a5e19233SWei Yongjun mutex_unlock(&test_fw_mutex); 369c92316bfSLuis R. Rodriguez goto out; 370c92316bfSLuis R. Rodriguez } 371c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 372c92316bfSLuis R. Rodriguez 373c92316bfSLuis R. Rodriguez rc = test_dev_config_update_u8(buf, count, 374c92316bfSLuis R. Rodriguez &test_fw_config->num_requests); 375c92316bfSLuis R. Rodriguez 376c92316bfSLuis R. Rodriguez out: 377c92316bfSLuis R. Rodriguez return rc; 378c92316bfSLuis R. Rodriguez } 379c92316bfSLuis R. Rodriguez 380c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev, 381c92316bfSLuis R. Rodriguez struct device_attribute *attr, 382c92316bfSLuis R. Rodriguez char *buf) 383c92316bfSLuis R. Rodriguez { 384c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->num_requests); 385c92316bfSLuis R. Rodriguez } 386b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests); 387c92316bfSLuis R. Rodriguez 3887feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev, 3897feebfa4SScott Branden struct device_attribute *attr, 3907feebfa4SScott Branden const char *buf, size_t count) 3917feebfa4SScott Branden { 3927feebfa4SScott Branden return test_dev_config_update_bool(buf, 3937feebfa4SScott Branden count, 3947feebfa4SScott Branden &test_fw_config->into_buf); 3957feebfa4SScott Branden } 3967feebfa4SScott Branden 3977feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev, 3987feebfa4SScott Branden struct device_attribute *attr, 3997feebfa4SScott Branden char *buf) 4007feebfa4SScott Branden { 4017feebfa4SScott Branden return test_dev_config_show_bool(buf, test_fw_config->into_buf); 4027feebfa4SScott Branden } 4037feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf); 4047feebfa4SScott Branden 405c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev, 406c92316bfSLuis R. Rodriguez struct device_attribute *attr, 407c92316bfSLuis R. Rodriguez const char *buf, size_t count) 408c92316bfSLuis R. Rodriguez { 409c92316bfSLuis R. Rodriguez int rc = test_dev_config_update_bool(buf, count, 410c92316bfSLuis R. Rodriguez &test_fw_config->sync_direct); 411c92316bfSLuis R. Rodriguez 412c92316bfSLuis R. Rodriguez if (rc == count) 413c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = test_fw_config->sync_direct ? 414c92316bfSLuis R. Rodriguez request_firmware_direct : 415c92316bfSLuis R. Rodriguez request_firmware; 416c92316bfSLuis R. Rodriguez return rc; 417c92316bfSLuis R. Rodriguez } 418c92316bfSLuis R. Rodriguez 419c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev, 420c92316bfSLuis R. Rodriguez struct device_attribute *attr, 421c92316bfSLuis R. Rodriguez char *buf) 422c92316bfSLuis R. Rodriguez { 423c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->sync_direct); 424c92316bfSLuis R. Rodriguez } 425b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct); 426c92316bfSLuis R. Rodriguez 427c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev, 428c92316bfSLuis R. Rodriguez struct device_attribute *attr, 429c92316bfSLuis R. Rodriguez const char *buf, size_t count) 430c92316bfSLuis R. Rodriguez { 431c92316bfSLuis R. Rodriguez return test_dev_config_update_bool(buf, count, 432c92316bfSLuis R. Rodriguez &test_fw_config->send_uevent); 433c92316bfSLuis R. Rodriguez } 434c92316bfSLuis R. Rodriguez 435c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev, 436c92316bfSLuis R. Rodriguez struct device_attribute *attr, 437c92316bfSLuis R. Rodriguez char *buf) 438c92316bfSLuis R. Rodriguez { 439c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->send_uevent); 440c92316bfSLuis R. Rodriguez } 441b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent); 442c92316bfSLuis R. Rodriguez 443c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev, 444c92316bfSLuis R. Rodriguez struct device_attribute *attr, 445c92316bfSLuis R. Rodriguez const char *buf, size_t count) 446c92316bfSLuis R. Rodriguez { 447c92316bfSLuis R. Rodriguez return test_dev_config_update_u8(buf, count, 448c92316bfSLuis R. Rodriguez &test_fw_config->read_fw_idx); 449c92316bfSLuis R. Rodriguez } 450c92316bfSLuis R. Rodriguez 451c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev, 452c92316bfSLuis R. Rodriguez struct device_attribute *attr, 453c92316bfSLuis R. Rodriguez char *buf) 454c92316bfSLuis R. Rodriguez { 455c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx); 456c92316bfSLuis R. Rodriguez } 457b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx); 458c92316bfSLuis R. Rodriguez 459c92316bfSLuis R. Rodriguez 4600a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev, 4610a8adf58SKees Cook struct device_attribute *attr, 4620a8adf58SKees Cook const char *buf, size_t count) 4630a8adf58SKees Cook { 4640a8adf58SKees Cook int rc; 4650a8adf58SKees Cook char *name; 4660a8adf58SKees Cook 467be4a1326SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 4680a8adf58SKees Cook if (!name) 4690a8adf58SKees Cook return -ENOSPC; 4700a8adf58SKees Cook 4710a8adf58SKees Cook pr_info("loading '%s'\n", name); 4720a8adf58SKees Cook 4730a8adf58SKees Cook mutex_lock(&test_fw_mutex); 4740a8adf58SKees Cook release_firmware(test_firmware); 4750a8adf58SKees Cook test_firmware = NULL; 4760a8adf58SKees Cook rc = request_firmware(&test_firmware, name, dev); 47747e0bbb7SBrian Norris if (rc) { 4780a8adf58SKees Cook pr_info("load of '%s' failed: %d\n", name, rc); 47947e0bbb7SBrian Norris goto out; 48047e0bbb7SBrian Norris } 48147e0bbb7SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 48247e0bbb7SBrian Norris rc = count; 48347e0bbb7SBrian Norris 48447e0bbb7SBrian Norris out: 4850a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 4860a8adf58SKees Cook 4870a8adf58SKees Cook kfree(name); 4880a8adf58SKees Cook 48947e0bbb7SBrian Norris return rc; 4900a8adf58SKees Cook } 4910a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request); 4920a8adf58SKees Cook 493548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 494*baaabecfSKees Cook extern struct list_head efi_embedded_fw_list; 495*baaabecfSKees Cook extern bool efi_embedded_fw_checked; 496*baaabecfSKees Cook 497548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev, 498548193cbSHans de Goede struct device_attribute *attr, 499548193cbSHans de Goede const char *buf, size_t count) 500548193cbSHans de Goede { 501548193cbSHans de Goede static const u8 test_data[] = { 502548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04, 503548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08, 504548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40, 505548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80 506548193cbSHans de Goede }; 507548193cbSHans de Goede struct efi_embedded_fw efi_embedded_fw; 508548193cbSHans de Goede const struct firmware *firmware = NULL; 509*baaabecfSKees Cook bool saved_efi_embedded_fw_checked; 510548193cbSHans de Goede char *name; 511548193cbSHans de Goede int rc; 512548193cbSHans de Goede 513548193cbSHans de Goede name = kstrndup(buf, count, GFP_KERNEL); 514548193cbSHans de Goede if (!name) 515548193cbSHans de Goede return -ENOSPC; 516548193cbSHans de Goede 517548193cbSHans de Goede pr_info("inserting test platform fw '%s'\n", name); 518548193cbSHans de Goede efi_embedded_fw.name = name; 519548193cbSHans de Goede efi_embedded_fw.data = (void *)test_data; 520548193cbSHans de Goede efi_embedded_fw.length = sizeof(test_data); 521548193cbSHans de Goede list_add(&efi_embedded_fw.list, &efi_embedded_fw_list); 522*baaabecfSKees Cook saved_efi_embedded_fw_checked = efi_embedded_fw_checked; 523*baaabecfSKees Cook efi_embedded_fw_checked = true; 524548193cbSHans de Goede 525548193cbSHans de Goede pr_info("loading '%s'\n", name); 526548193cbSHans de Goede rc = firmware_request_platform(&firmware, name, dev); 527548193cbSHans de Goede if (rc) { 528548193cbSHans de Goede pr_info("load of '%s' failed: %d\n", name, rc); 529548193cbSHans de Goede goto out; 530548193cbSHans de Goede } 531548193cbSHans de Goede if (firmware->size != sizeof(test_data) || 532548193cbSHans de Goede memcmp(firmware->data, test_data, sizeof(test_data)) != 0) { 533548193cbSHans de Goede pr_info("firmware contents mismatch for '%s'\n", name); 534548193cbSHans de Goede rc = -EINVAL; 535548193cbSHans de Goede goto out; 536548193cbSHans de Goede } 537548193cbSHans de Goede pr_info("loaded: %zu\n", firmware->size); 538548193cbSHans de Goede rc = count; 539548193cbSHans de Goede 540548193cbSHans de Goede out: 541*baaabecfSKees Cook efi_embedded_fw_checked = saved_efi_embedded_fw_checked; 542548193cbSHans de Goede release_firmware(firmware); 543548193cbSHans de Goede list_del(&efi_embedded_fw.list); 544548193cbSHans de Goede kfree(name); 545548193cbSHans de Goede 546548193cbSHans de Goede return rc; 547548193cbSHans de Goede } 548548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform); 549548193cbSHans de Goede #endif 550548193cbSHans de Goede 551eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done); 552eb910947SBrian Norris 553eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context) 554eb910947SBrian Norris { 555eb910947SBrian Norris test_firmware = fw; 556eb910947SBrian Norris complete(&async_fw_done); 557eb910947SBrian Norris } 558eb910947SBrian Norris 559eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev, 560eb910947SBrian Norris struct device_attribute *attr, 561eb910947SBrian Norris const char *buf, size_t count) 562eb910947SBrian Norris { 563eb910947SBrian Norris int rc; 564eb910947SBrian Norris char *name; 565eb910947SBrian Norris 566eb910947SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 567eb910947SBrian Norris if (!name) 568eb910947SBrian Norris return -ENOSPC; 569eb910947SBrian Norris 570eb910947SBrian Norris pr_info("loading '%s'\n", name); 571eb910947SBrian Norris 572eb910947SBrian Norris mutex_lock(&test_fw_mutex); 573eb910947SBrian Norris release_firmware(test_firmware); 574eb910947SBrian Norris test_firmware = NULL; 575eb910947SBrian Norris rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 576eb910947SBrian Norris NULL, trigger_async_request_cb); 577eb910947SBrian Norris if (rc) { 578eb910947SBrian Norris pr_info("async load of '%s' failed: %d\n", name, rc); 579eb910947SBrian Norris kfree(name); 580eb910947SBrian Norris goto out; 581eb910947SBrian Norris } 582eb910947SBrian Norris /* Free 'name' ASAP, to test for race conditions */ 583eb910947SBrian Norris kfree(name); 584eb910947SBrian Norris 585eb910947SBrian Norris wait_for_completion(&async_fw_done); 586eb910947SBrian Norris 587eb910947SBrian Norris if (test_firmware) { 588eb910947SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 589eb910947SBrian Norris rc = count; 590eb910947SBrian Norris } else { 591eb910947SBrian Norris pr_err("failed to async load firmware\n"); 5927feebfa4SScott Branden rc = -ENOMEM; 593eb910947SBrian Norris } 594eb910947SBrian Norris 595eb910947SBrian Norris out: 596eb910947SBrian Norris mutex_unlock(&test_fw_mutex); 597eb910947SBrian Norris 598eb910947SBrian Norris return rc; 599eb910947SBrian Norris } 600eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request); 601eb910947SBrian Norris 602061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev, 603061132d2SLuis R. Rodriguez struct device_attribute *attr, 604061132d2SLuis R. Rodriguez const char *buf, size_t count) 605061132d2SLuis R. Rodriguez { 606061132d2SLuis R. Rodriguez int rc; 607061132d2SLuis R. Rodriguez char *name; 608061132d2SLuis R. Rodriguez 609061132d2SLuis R. Rodriguez name = kstrndup(buf, count, GFP_KERNEL); 610061132d2SLuis R. Rodriguez if (!name) 611061132d2SLuis R. Rodriguez return -ENOSPC; 612061132d2SLuis R. Rodriguez 613061132d2SLuis R. Rodriguez pr_info("loading '%s' using custom fallback mechanism\n", name); 614061132d2SLuis R. Rodriguez 615061132d2SLuis R. Rodriguez mutex_lock(&test_fw_mutex); 616061132d2SLuis R. Rodriguez release_firmware(test_firmware); 617061132d2SLuis R. Rodriguez test_firmware = NULL; 618061132d2SLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name, 619061132d2SLuis R. Rodriguez dev, GFP_KERNEL, NULL, 620061132d2SLuis R. Rodriguez trigger_async_request_cb); 621061132d2SLuis R. Rodriguez if (rc) { 622061132d2SLuis R. Rodriguez pr_info("async load of '%s' failed: %d\n", name, rc); 623061132d2SLuis R. Rodriguez kfree(name); 624061132d2SLuis R. Rodriguez goto out; 625061132d2SLuis R. Rodriguez } 626061132d2SLuis R. Rodriguez /* Free 'name' ASAP, to test for race conditions */ 627061132d2SLuis R. Rodriguez kfree(name); 628061132d2SLuis R. Rodriguez 629061132d2SLuis R. Rodriguez wait_for_completion(&async_fw_done); 630061132d2SLuis R. Rodriguez 631061132d2SLuis R. Rodriguez if (test_firmware) { 632061132d2SLuis R. Rodriguez pr_info("loaded: %zu\n", test_firmware->size); 633061132d2SLuis R. Rodriguez rc = count; 634061132d2SLuis R. Rodriguez } else { 635061132d2SLuis R. Rodriguez pr_err("failed to async load firmware\n"); 636061132d2SLuis R. Rodriguez rc = -ENODEV; 637061132d2SLuis R. Rodriguez } 638061132d2SLuis R. Rodriguez 639061132d2SLuis R. Rodriguez out: 640061132d2SLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 641061132d2SLuis R. Rodriguez 642061132d2SLuis R. Rodriguez return rc; 643061132d2SLuis R. Rodriguez } 644061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback); 645061132d2SLuis R. Rodriguez 646c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data) 647c92316bfSLuis R. Rodriguez { 648c92316bfSLuis R. Rodriguez struct test_batched_req *req = data; 649c92316bfSLuis R. Rodriguez 650c92316bfSLuis R. Rodriguez if (!req) { 651c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 652c92316bfSLuis R. Rodriguez return -EINVAL; 653c92316bfSLuis R. Rodriguez } 654c92316bfSLuis R. Rodriguez 6557feebfa4SScott Branden if (test_fw_config->into_buf) { 6567feebfa4SScott Branden void *test_buf; 6577feebfa4SScott Branden 6587feebfa4SScott Branden test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); 6597feebfa4SScott Branden if (!test_buf) 6607feebfa4SScott Branden return -ENOSPC; 6617feebfa4SScott Branden 6627feebfa4SScott Branden req->rc = request_firmware_into_buf(&req->fw, 6637feebfa4SScott Branden req->name, 6647feebfa4SScott Branden req->dev, 6657feebfa4SScott Branden test_buf, 6667feebfa4SScott Branden TEST_FIRMWARE_BUF_SIZE); 6677feebfa4SScott Branden if (!req->fw) 6687feebfa4SScott Branden kfree(test_buf); 6697feebfa4SScott Branden } else { 6707feebfa4SScott Branden req->rc = test_fw_config->req_firmware(&req->fw, 6717feebfa4SScott Branden req->name, 6727feebfa4SScott Branden req->dev); 6737feebfa4SScott Branden } 6747feebfa4SScott Branden 675c92316bfSLuis R. Rodriguez if (req->rc) { 676c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync load failed: %d\n", 677c92316bfSLuis R. Rodriguez req->idx, req->rc); 678c92316bfSLuis R. Rodriguez if (!test_fw_config->test_result) 679c92316bfSLuis R. Rodriguez test_fw_config->test_result = req->rc; 680c92316bfSLuis R. Rodriguez } else if (req->fw) { 681c92316bfSLuis R. Rodriguez req->sent = true; 682c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync loaded %zu\n", 683c92316bfSLuis R. Rodriguez req->idx, req->fw->size); 684c92316bfSLuis R. Rodriguez } 685c92316bfSLuis R. Rodriguez complete(&req->completion); 686c92316bfSLuis R. Rodriguez 687c92316bfSLuis R. Rodriguez req->task = NULL; 688c92316bfSLuis R. Rodriguez 689c92316bfSLuis R. Rodriguez return 0; 690c92316bfSLuis R. Rodriguez } 691c92316bfSLuis R. Rodriguez 692c92316bfSLuis R. Rodriguez /* 693c92316bfSLuis R. Rodriguez * We use a kthread as otherwise the kernel serializes all our sync requests 694c92316bfSLuis R. Rodriguez * and we would not be able to mimic batched requests on a sync call. Batched 695c92316bfSLuis R. Rodriguez * requests on a sync call can for instance happen on a device driver when 696c92316bfSLuis R. Rodriguez * multiple cards are used and firmware loading happens outside of probe. 697c92316bfSLuis R. Rodriguez */ 698c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev, 699c92316bfSLuis R. Rodriguez struct device_attribute *attr, 700c92316bfSLuis R. Rodriguez const char *buf, size_t count) 701c92316bfSLuis R. Rodriguez { 702c92316bfSLuis R. Rodriguez struct test_batched_req *req; 703c92316bfSLuis R. Rodriguez int rc; 704c92316bfSLuis R. Rodriguez u8 i; 705c92316bfSLuis R. Rodriguez 706c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 707c92316bfSLuis R. Rodriguez 708fad953ceSKees Cook test_fw_config->reqs = 709fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 710fad953ceSKees Cook test_fw_config->num_requests, 2)); 711c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 712c92316bfSLuis R. Rodriguez rc = -ENOMEM; 713c92316bfSLuis R. Rodriguez goto out_unlock; 714c92316bfSLuis R. Rodriguez } 715c92316bfSLuis R. Rodriguez 716c92316bfSLuis R. Rodriguez pr_info("batched sync firmware loading '%s' %u times\n", 717c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 718c92316bfSLuis R. Rodriguez 719c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 720c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 721c92316bfSLuis R. Rodriguez req->fw = NULL; 722c92316bfSLuis R. Rodriguez req->idx = i; 723c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 724c92316bfSLuis R. Rodriguez req->dev = dev; 725c92316bfSLuis R. Rodriguez init_completion(&req->completion); 726c92316bfSLuis R. Rodriguez req->task = kthread_run(test_fw_run_batch_request, req, 727c92316bfSLuis R. Rodriguez "%s-%u", KBUILD_MODNAME, req->idx); 728c92316bfSLuis R. Rodriguez if (!req->task || IS_ERR(req->task)) { 729c92316bfSLuis R. Rodriguez pr_err("Setting up thread %u failed\n", req->idx); 730c92316bfSLuis R. Rodriguez req->task = NULL; 731c92316bfSLuis R. Rodriguez rc = -ENOMEM; 732c92316bfSLuis R. Rodriguez goto out_bail; 733c92316bfSLuis R. Rodriguez } 734c92316bfSLuis R. Rodriguez } 735c92316bfSLuis R. Rodriguez 736c92316bfSLuis R. Rodriguez rc = count; 737c92316bfSLuis R. Rodriguez 738c92316bfSLuis R. Rodriguez /* 739c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 740c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 741c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 742c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 743c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 744c92316bfSLuis R. Rodriguez */ 745c92316bfSLuis R. Rodriguez 746c92316bfSLuis R. Rodriguez out_bail: 747c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 748c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 749c92316bfSLuis R. Rodriguez if (req->task || req->sent) 750c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 751c92316bfSLuis R. Rodriguez } 752c92316bfSLuis R. Rodriguez 753c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 754c92316bfSLuis R. Rodriguez if (rc < 0) 755c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 756c92316bfSLuis R. Rodriguez 757c92316bfSLuis R. Rodriguez out_unlock: 758c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 759c92316bfSLuis R. Rodriguez 760c92316bfSLuis R. Rodriguez return rc; 761c92316bfSLuis R. Rodriguez } 762c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests); 763c92316bfSLuis R. Rodriguez 764c92316bfSLuis R. Rodriguez /* 765c92316bfSLuis R. Rodriguez * We wait for each callback to return with the lock held, no need to lock here 766c92316bfSLuis R. Rodriguez */ 767c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context) 768c92316bfSLuis R. Rodriguez { 769c92316bfSLuis R. Rodriguez struct test_batched_req *req = context; 770c92316bfSLuis R. Rodriguez 771c92316bfSLuis R. Rodriguez if (!req) { 772c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 773c92316bfSLuis R. Rodriguez return; 774c92316bfSLuis R. Rodriguez } 775c92316bfSLuis R. Rodriguez 776c92316bfSLuis R. Rodriguez /* forces *some* batched requests to queue up */ 777c92316bfSLuis R. Rodriguez if (!req->idx) 778c92316bfSLuis R. Rodriguez ssleep(2); 779c92316bfSLuis R. Rodriguez 780c92316bfSLuis R. Rodriguez req->fw = fw; 781c92316bfSLuis R. Rodriguez 782c92316bfSLuis R. Rodriguez /* 783c92316bfSLuis R. Rodriguez * Unfortunately the firmware API gives us nothing other than a null FW 784c92316bfSLuis R. Rodriguez * if the firmware was not found on async requests. Best we can do is 785c92316bfSLuis R. Rodriguez * just assume -ENOENT. A better API would pass the actual return 786c92316bfSLuis R. Rodriguez * value to the callback. 787c92316bfSLuis R. Rodriguez */ 788c92316bfSLuis R. Rodriguez if (!fw && !test_fw_config->test_result) 789c92316bfSLuis R. Rodriguez test_fw_config->test_result = -ENOENT; 790c92316bfSLuis R. Rodriguez 791c92316bfSLuis R. Rodriguez complete(&req->completion); 792c92316bfSLuis R. Rodriguez } 793c92316bfSLuis R. Rodriguez 794c92316bfSLuis R. Rodriguez static 795c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev, 796c92316bfSLuis R. Rodriguez struct device_attribute *attr, 797c92316bfSLuis R. Rodriguez const char *buf, size_t count) 798c92316bfSLuis R. Rodriguez { 799c92316bfSLuis R. Rodriguez struct test_batched_req *req; 800c92316bfSLuis R. Rodriguez bool send_uevent; 801c92316bfSLuis R. Rodriguez int rc; 802c92316bfSLuis R. Rodriguez u8 i; 803c92316bfSLuis R. Rodriguez 804c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 805c92316bfSLuis R. Rodriguez 806fad953ceSKees Cook test_fw_config->reqs = 807fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 808fad953ceSKees Cook test_fw_config->num_requests, 2)); 809c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 810c92316bfSLuis R. Rodriguez rc = -ENOMEM; 811c92316bfSLuis R. Rodriguez goto out; 812c92316bfSLuis R. Rodriguez } 813c92316bfSLuis R. Rodriguez 814c92316bfSLuis R. Rodriguez pr_info("batched loading '%s' custom fallback mechanism %u times\n", 815c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 816c92316bfSLuis R. Rodriguez 817c92316bfSLuis R. Rodriguez send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG : 818c92316bfSLuis R. Rodriguez FW_ACTION_NOHOTPLUG; 819c92316bfSLuis R. Rodriguez 820c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 821c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 822c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 823c92316bfSLuis R. Rodriguez req->fw = NULL; 824c92316bfSLuis R. Rodriguez req->idx = i; 825c92316bfSLuis R. Rodriguez init_completion(&req->completion); 826c92316bfSLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, send_uevent, 827c92316bfSLuis R. Rodriguez req->name, 828c92316bfSLuis R. Rodriguez dev, GFP_KERNEL, req, 829c92316bfSLuis R. Rodriguez trigger_batched_cb); 830c92316bfSLuis R. Rodriguez if (rc) { 831c92316bfSLuis R. Rodriguez pr_info("#%u: batched async load failed setup: %d\n", 832c92316bfSLuis R. Rodriguez i, rc); 833c92316bfSLuis R. Rodriguez req->rc = rc; 834c92316bfSLuis R. Rodriguez goto out_bail; 835c92316bfSLuis R. Rodriguez } else 836c92316bfSLuis R. Rodriguez req->sent = true; 837c92316bfSLuis R. Rodriguez } 838c92316bfSLuis R. Rodriguez 839c92316bfSLuis R. Rodriguez rc = count; 840c92316bfSLuis R. Rodriguez 841c92316bfSLuis R. Rodriguez out_bail: 842c92316bfSLuis R. Rodriguez 843c92316bfSLuis R. Rodriguez /* 844c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 845c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 846c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 847c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 848c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 849c92316bfSLuis R. Rodriguez */ 850c92316bfSLuis R. Rodriguez 851c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 852c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 853c92316bfSLuis R. Rodriguez if (req->sent) 854c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 855c92316bfSLuis R. Rodriguez } 856c92316bfSLuis R. Rodriguez 857c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 858c92316bfSLuis R. Rodriguez if (rc < 0) 859c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 860c92316bfSLuis R. Rodriguez 861c92316bfSLuis R. Rodriguez out: 862c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 863c92316bfSLuis R. Rodriguez 864c92316bfSLuis R. Rodriguez return rc; 865c92316bfSLuis R. Rodriguez } 866c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async); 867c92316bfSLuis R. Rodriguez 868c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev, 869c92316bfSLuis R. Rodriguez struct device_attribute *attr, 870c92316bfSLuis R. Rodriguez char *buf) 871c92316bfSLuis R. Rodriguez { 872c92316bfSLuis R. Rodriguez return test_dev_config_show_int(buf, test_fw_config->test_result); 873c92316bfSLuis R. Rodriguez } 874c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result); 875c92316bfSLuis R. Rodriguez 876c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev, 877c92316bfSLuis R. Rodriguez struct device_attribute *attr, 878c92316bfSLuis R. Rodriguez const char *buf, size_t count) 879c92316bfSLuis R. Rodriguez { 880c92316bfSLuis R. Rodriguez test_release_all_firmware(); 881c92316bfSLuis R. Rodriguez return count; 882c92316bfSLuis R. Rodriguez } 883c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware); 884c92316bfSLuis R. Rodriguez 885c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev, 886c92316bfSLuis R. Rodriguez struct device_attribute *attr, 887c92316bfSLuis R. Rodriguez char *buf) 888c92316bfSLuis R. Rodriguez { 889c92316bfSLuis R. Rodriguez struct test_batched_req *req; 890c92316bfSLuis R. Rodriguez u8 idx; 891c92316bfSLuis R. Rodriguez ssize_t rc = 0; 892c92316bfSLuis R. Rodriguez 893c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 894c92316bfSLuis R. Rodriguez 895c92316bfSLuis R. Rodriguez idx = test_fw_config->read_fw_idx; 896c92316bfSLuis R. Rodriguez if (idx >= test_fw_config->num_requests) { 897c92316bfSLuis R. Rodriguez rc = -ERANGE; 898c92316bfSLuis R. Rodriguez goto out; 899c92316bfSLuis R. Rodriguez } 900c92316bfSLuis R. Rodriguez 901c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 902c92316bfSLuis R. Rodriguez rc = -EINVAL; 903c92316bfSLuis R. Rodriguez goto out; 904c92316bfSLuis R. Rodriguez } 905c92316bfSLuis R. Rodriguez 906c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[idx]; 907c92316bfSLuis R. Rodriguez if (!req->fw) { 908c92316bfSLuis R. Rodriguez pr_err("#%u: failed to async load firmware\n", idx); 909c92316bfSLuis R. Rodriguez rc = -ENOENT; 910c92316bfSLuis R. Rodriguez goto out; 911c92316bfSLuis R. Rodriguez } 912c92316bfSLuis R. Rodriguez 913c92316bfSLuis R. Rodriguez pr_info("#%u: loaded %zu\n", idx, req->fw->size); 914c92316bfSLuis R. Rodriguez 915c92316bfSLuis R. Rodriguez if (req->fw->size > PAGE_SIZE) { 916c92316bfSLuis R. Rodriguez pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 917c92316bfSLuis R. Rodriguez rc = -EINVAL; 9188bb0a886SColin Ian King goto out; 919c92316bfSLuis R. Rodriguez } 920c92316bfSLuis R. Rodriguez memcpy(buf, req->fw->data, req->fw->size); 921c92316bfSLuis R. Rodriguez 922c92316bfSLuis R. Rodriguez rc = req->fw->size; 923c92316bfSLuis R. Rodriguez out: 924c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 925c92316bfSLuis R. Rodriguez 926c92316bfSLuis R. Rodriguez return rc; 927c92316bfSLuis R. Rodriguez } 928c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware); 929c92316bfSLuis R. Rodriguez 930083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr 931083a93b0SLuis R. Rodriguez 932083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = { 933c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(reset), 934c92316bfSLuis R. Rodriguez 935c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config), 936c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_name), 937c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_num_requests), 9387feebfa4SScott Branden TEST_FW_DEV_ATTR(config_into_buf), 939c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_sync_direct), 940c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_send_uevent), 941c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_read_fw_idx), 942c92316bfSLuis R. Rodriguez 943c92316bfSLuis R. Rodriguez /* These don't use the config at all - they could be ported! */ 944083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_request), 945083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_async_request), 946061132d2SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_custom_fallback), 947548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 948548193cbSHans de Goede TEST_FW_DEV_ATTR(trigger_request_platform), 949548193cbSHans de Goede #endif 950c92316bfSLuis R. Rodriguez 951c92316bfSLuis R. Rodriguez /* These use the config and can use the test_result */ 952c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests), 953c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests_async), 954c92316bfSLuis R. Rodriguez 955c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(release_all_firmware), 956c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(test_result), 957c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(read_firmware), 958083a93b0SLuis R. Rodriguez NULL, 959083a93b0SLuis R. Rodriguez }; 960083a93b0SLuis R. Rodriguez 961083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev); 962083a93b0SLuis R. Rodriguez 96367fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = { 96467fd553cSLuis R. Rodriguez .minor = MISC_DYNAMIC_MINOR, 96567fd553cSLuis R. Rodriguez .name = "test_firmware", 96667fd553cSLuis R. Rodriguez .fops = &test_fw_fops, 967083a93b0SLuis R. Rodriguez .groups = test_dev_groups, 96867fd553cSLuis R. Rodriguez }; 96967fd553cSLuis R. Rodriguez 9700a8adf58SKees Cook static int __init test_firmware_init(void) 9710a8adf58SKees Cook { 9720a8adf58SKees Cook int rc; 9730a8adf58SKees Cook 974c92316bfSLuis R. Rodriguez test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL); 975c92316bfSLuis R. Rodriguez if (!test_fw_config) 976c92316bfSLuis R. Rodriguez return -ENOMEM; 977c92316bfSLuis R. Rodriguez 978c92316bfSLuis R. Rodriguez rc = __test_firmware_config_init(); 979d4fddac5SWenwen Wang if (rc) { 980d4fddac5SWenwen Wang kfree(test_fw_config); 981d4fddac5SWenwen Wang pr_err("could not init firmware test config: %d\n", rc); 982c92316bfSLuis R. Rodriguez return rc; 983d4fddac5SWenwen Wang } 984c92316bfSLuis R. Rodriguez 9850a8adf58SKees Cook rc = misc_register(&test_fw_misc_device); 9860a8adf58SKees Cook if (rc) { 987c92316bfSLuis R. Rodriguez kfree(test_fw_config); 9880a8adf58SKees Cook pr_err("could not register misc device: %d\n", rc); 9890a8adf58SKees Cook return rc; 9900a8adf58SKees Cook } 991eb910947SBrian Norris 9920a8adf58SKees Cook pr_warn("interface ready\n"); 9930a8adf58SKees Cook 9940a8adf58SKees Cook return 0; 9950a8adf58SKees Cook } 9960a8adf58SKees Cook 9970a8adf58SKees Cook module_init(test_firmware_init); 9980a8adf58SKees Cook 9990a8adf58SKees Cook static void __exit test_firmware_exit(void) 10000a8adf58SKees Cook { 1001c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 10020a8adf58SKees Cook release_firmware(test_firmware); 10030a8adf58SKees Cook misc_deregister(&test_fw_misc_device); 1004c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 1005c92316bfSLuis R. Rodriguez kfree(test_fw_config); 1006c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1007c92316bfSLuis R. Rodriguez 10080a8adf58SKees Cook pr_warn("removed interface\n"); 10090a8adf58SKees Cook } 10100a8adf58SKees Cook 10110a8adf58SKees Cook module_exit(test_firmware_exit); 10120a8adf58SKees Cook 10130a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>"); 10140a8adf58SKees Cook MODULE_LICENSE("GPL"); 1015