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> 27*548193cbSHans de Goede #include <linux/efi_embedded_fw.h> 28c92316bfSLuis R. Rodriguez 29c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NAME "test-firmware.bin" 30c92316bfSLuis R. Rodriguez #define TEST_FIRMWARE_NUM_REQS 4 317feebfa4SScott Branden #define TEST_FIRMWARE_BUF_SIZE SZ_1K 320a8adf58SKees Cook 330a8adf58SKees Cook static DEFINE_MUTEX(test_fw_mutex); 340a8adf58SKees Cook static const struct firmware *test_firmware; 350a8adf58SKees Cook 36c92316bfSLuis R. Rodriguez struct test_batched_req { 37c92316bfSLuis R. Rodriguez u8 idx; 38c92316bfSLuis R. Rodriguez int rc; 39c92316bfSLuis R. Rodriguez bool sent; 40c92316bfSLuis R. Rodriguez const struct firmware *fw; 41c92316bfSLuis R. Rodriguez const char *name; 42c92316bfSLuis R. Rodriguez struct completion completion; 43c92316bfSLuis R. Rodriguez struct task_struct *task; 44c92316bfSLuis R. Rodriguez struct device *dev; 45c92316bfSLuis R. Rodriguez }; 46c92316bfSLuis R. Rodriguez 47c92316bfSLuis R. Rodriguez /** 48c92316bfSLuis R. Rodriguez * test_config - represents configuration for the test for different triggers 49c92316bfSLuis R. Rodriguez * 50c92316bfSLuis R. Rodriguez * @name: the name of the firmware file to look for 517feebfa4SScott Branden * @into_buf: when the into_buf is used if this is true 527feebfa4SScott Branden * request_firmware_into_buf() will be used instead. 53c92316bfSLuis R. Rodriguez * @sync_direct: when the sync trigger is used if this is true 54c92316bfSLuis R. Rodriguez * request_firmware_direct() will be used instead. 55c92316bfSLuis R. Rodriguez * @send_uevent: whether or not to send a uevent for async requests 56c92316bfSLuis R. Rodriguez * @num_requests: number of requests to try per test case. This is trigger 57c92316bfSLuis R. Rodriguez * specific. 58c92316bfSLuis R. Rodriguez * @reqs: stores all requests information 59c92316bfSLuis R. Rodriguez * @read_fw_idx: index of thread from which we want to read firmware results 60c92316bfSLuis R. Rodriguez * from through the read_fw trigger. 61c92316bfSLuis R. Rodriguez * @test_result: a test may use this to collect the result from the call 62c92316bfSLuis R. Rodriguez * of the request_firmware*() calls used in their tests. In order of 63c92316bfSLuis R. Rodriguez * priority we always keep first any setup error. If no setup errors were 64c92316bfSLuis R. Rodriguez * found then we move on to the first error encountered while running the 65c92316bfSLuis R. Rodriguez * API. Note that for async calls this typically will be a successful 66c92316bfSLuis R. Rodriguez * result (0) unless of course you've used bogus parameters, or the system 67c92316bfSLuis R. Rodriguez * is out of memory. In the async case the callback is expected to do a 68c92316bfSLuis R. Rodriguez * bit more homework to figure out what happened, unfortunately the only 69c92316bfSLuis R. Rodriguez * information passed today on error is the fact that no firmware was 70c92316bfSLuis R. Rodriguez * found so we can only assume -ENOENT on async calls if the firmware is 71c92316bfSLuis R. Rodriguez * NULL. 72c92316bfSLuis R. Rodriguez * 73c92316bfSLuis R. Rodriguez * Errors you can expect: 74c92316bfSLuis R. Rodriguez * 75c92316bfSLuis R. Rodriguez * API specific: 76c92316bfSLuis R. Rodriguez * 77c92316bfSLuis R. Rodriguez * 0: success for sync, for async it means request was sent 78c92316bfSLuis R. Rodriguez * -EINVAL: invalid parameters or request 79c92316bfSLuis R. Rodriguez * -ENOENT: files not found 80c92316bfSLuis R. Rodriguez * 81c92316bfSLuis R. Rodriguez * System environment: 82c92316bfSLuis R. Rodriguez * 83c92316bfSLuis R. Rodriguez * -ENOMEM: memory pressure on system 84c92316bfSLuis R. Rodriguez * -ENODEV: out of number of devices to test 85c92316bfSLuis R. Rodriguez * -EINVAL: an unexpected error has occurred 86c92316bfSLuis R. Rodriguez * @req_firmware: if @sync_direct is true this is set to 87c92316bfSLuis R. Rodriguez * request_firmware_direct(), otherwise request_firmware() 88c92316bfSLuis R. Rodriguez */ 89c92316bfSLuis R. Rodriguez struct test_config { 90c92316bfSLuis R. Rodriguez char *name; 917feebfa4SScott Branden bool into_buf; 92c92316bfSLuis R. Rodriguez bool sync_direct; 93c92316bfSLuis R. Rodriguez bool send_uevent; 94c92316bfSLuis R. Rodriguez u8 num_requests; 95c92316bfSLuis R. Rodriguez u8 read_fw_idx; 96c92316bfSLuis R. Rodriguez 97c92316bfSLuis R. Rodriguez /* 98c92316bfSLuis R. Rodriguez * These below don't belong her but we'll move them once we create 99c92316bfSLuis R. Rodriguez * a struct fw_test_device and stuff the misc_dev under there later. 100c92316bfSLuis R. Rodriguez */ 101c92316bfSLuis R. Rodriguez struct test_batched_req *reqs; 102c92316bfSLuis R. Rodriguez int test_result; 103c92316bfSLuis R. Rodriguez int (*req_firmware)(const struct firmware **fw, const char *name, 104c92316bfSLuis R. Rodriguez struct device *device); 105c92316bfSLuis R. Rodriguez }; 106c92316bfSLuis R. Rodriguez 10776f8ab1bSWei Yongjun static struct test_config *test_fw_config; 108c92316bfSLuis R. Rodriguez 1090a8adf58SKees Cook static ssize_t test_fw_misc_read(struct file *f, char __user *buf, 1100a8adf58SKees Cook size_t size, loff_t *offset) 1110a8adf58SKees Cook { 1120a8adf58SKees Cook ssize_t rc = 0; 1130a8adf58SKees Cook 1140a8adf58SKees Cook mutex_lock(&test_fw_mutex); 1150a8adf58SKees Cook if (test_firmware) 1160a8adf58SKees Cook rc = simple_read_from_buffer(buf, size, offset, 1170a8adf58SKees Cook test_firmware->data, 1180a8adf58SKees Cook test_firmware->size); 1190a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 1200a8adf58SKees Cook return rc; 1210a8adf58SKees Cook } 1220a8adf58SKees Cook 1230a8adf58SKees Cook static const struct file_operations test_fw_fops = { 1240a8adf58SKees Cook .owner = THIS_MODULE, 1250a8adf58SKees Cook .read = test_fw_misc_read, 1260a8adf58SKees Cook }; 1270a8adf58SKees Cook 128c92316bfSLuis R. Rodriguez static void __test_release_all_firmware(void) 129c92316bfSLuis R. Rodriguez { 130c92316bfSLuis R. Rodriguez struct test_batched_req *req; 131c92316bfSLuis R. Rodriguez u8 i; 132c92316bfSLuis R. Rodriguez 133c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) 134c92316bfSLuis R. Rodriguez return; 135c92316bfSLuis R. Rodriguez 136c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 137c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 138c92316bfSLuis R. Rodriguez if (req->fw) 139c92316bfSLuis R. Rodriguez release_firmware(req->fw); 140c92316bfSLuis R. Rodriguez } 141c92316bfSLuis R. Rodriguez 142c92316bfSLuis R. Rodriguez vfree(test_fw_config->reqs); 143c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 144c92316bfSLuis R. Rodriguez } 145c92316bfSLuis R. Rodriguez 146c92316bfSLuis R. Rodriguez static void test_release_all_firmware(void) 147c92316bfSLuis R. Rodriguez { 148c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 149c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 150c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 151c92316bfSLuis R. Rodriguez } 152c92316bfSLuis R. Rodriguez 153c92316bfSLuis R. Rodriguez 154c92316bfSLuis R. Rodriguez static void __test_firmware_config_free(void) 155c92316bfSLuis R. Rodriguez { 156c92316bfSLuis R. Rodriguez __test_release_all_firmware(); 157c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 158c92316bfSLuis R. Rodriguez test_fw_config->name = NULL; 159c92316bfSLuis R. Rodriguez } 160c92316bfSLuis R. Rodriguez 161c92316bfSLuis R. Rodriguez /* 162c92316bfSLuis R. Rodriguez * XXX: move to kstrncpy() once merged. 163c92316bfSLuis R. Rodriguez * 164c92316bfSLuis R. Rodriguez * Users should use kfree_const() when freeing these. 165c92316bfSLuis R. Rodriguez */ 166c92316bfSLuis R. Rodriguez static int __kstrncpy(char **dst, const char *name, size_t count, gfp_t gfp) 167c92316bfSLuis R. Rodriguez { 168c92316bfSLuis R. Rodriguez *dst = kstrndup(name, count, gfp); 169c92316bfSLuis R. Rodriguez if (!*dst) 170c92316bfSLuis R. Rodriguez return -ENOSPC; 171c92316bfSLuis R. Rodriguez return count; 172c92316bfSLuis R. Rodriguez } 173c92316bfSLuis R. Rodriguez 174c92316bfSLuis R. Rodriguez static int __test_firmware_config_init(void) 175c92316bfSLuis R. Rodriguez { 176c92316bfSLuis R. Rodriguez int ret; 177c92316bfSLuis R. Rodriguez 178c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, TEST_FIRMWARE_NAME, 179c92316bfSLuis R. Rodriguez strlen(TEST_FIRMWARE_NAME), GFP_KERNEL); 180c92316bfSLuis R. Rodriguez if (ret < 0) 181c92316bfSLuis R. Rodriguez goto out; 182c92316bfSLuis R. Rodriguez 183c92316bfSLuis R. Rodriguez test_fw_config->num_requests = TEST_FIRMWARE_NUM_REQS; 184c92316bfSLuis R. Rodriguez test_fw_config->send_uevent = true; 1857feebfa4SScott Branden test_fw_config->into_buf = false; 186c92316bfSLuis R. Rodriguez test_fw_config->sync_direct = false; 187c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = request_firmware; 188c92316bfSLuis R. Rodriguez test_fw_config->test_result = 0; 189c92316bfSLuis R. Rodriguez test_fw_config->reqs = NULL; 190c92316bfSLuis R. Rodriguez 191c92316bfSLuis R. Rodriguez return 0; 192c92316bfSLuis R. Rodriguez 193c92316bfSLuis R. Rodriguez out: 194c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 195c92316bfSLuis R. Rodriguez return ret; 196c92316bfSLuis R. Rodriguez } 197c92316bfSLuis R. Rodriguez 198c92316bfSLuis R. Rodriguez static ssize_t reset_store(struct device *dev, 199c92316bfSLuis R. Rodriguez struct device_attribute *attr, 200c92316bfSLuis R. Rodriguez const char *buf, size_t count) 201c92316bfSLuis R. Rodriguez { 202c92316bfSLuis R. Rodriguez int ret; 203c92316bfSLuis R. Rodriguez 204c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 205c92316bfSLuis R. Rodriguez 206c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 207c92316bfSLuis R. Rodriguez 208c92316bfSLuis R. Rodriguez ret = __test_firmware_config_init(); 209c92316bfSLuis R. Rodriguez if (ret < 0) { 210c92316bfSLuis R. Rodriguez ret = -ENOMEM; 211c92316bfSLuis R. Rodriguez pr_err("could not alloc settings for config trigger: %d\n", 212c92316bfSLuis R. Rodriguez ret); 213c92316bfSLuis R. Rodriguez goto out; 214c92316bfSLuis R. Rodriguez } 215c92316bfSLuis R. Rodriguez 216c92316bfSLuis R. Rodriguez pr_info("reset\n"); 217c92316bfSLuis R. Rodriguez ret = count; 218c92316bfSLuis R. Rodriguez 219c92316bfSLuis R. Rodriguez out: 220c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 221c92316bfSLuis R. Rodriguez 222c92316bfSLuis R. Rodriguez return ret; 223c92316bfSLuis R. Rodriguez } 224c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(reset); 225c92316bfSLuis R. Rodriguez 226c92316bfSLuis R. Rodriguez static ssize_t config_show(struct device *dev, 227c92316bfSLuis R. Rodriguez struct device_attribute *attr, 228c92316bfSLuis R. Rodriguez char *buf) 229c92316bfSLuis R. Rodriguez { 230c92316bfSLuis R. Rodriguez int len = 0; 231c92316bfSLuis R. Rodriguez 232c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 233c92316bfSLuis R. Rodriguez 234bd17cc5aSDan Carpenter len += scnprintf(buf, PAGE_SIZE - len, 235c92316bfSLuis R. Rodriguez "Custom trigger configuration for: %s\n", 236c92316bfSLuis R. Rodriguez dev_name(dev)); 237c92316bfSLuis R. Rodriguez 238c92316bfSLuis R. Rodriguez if (test_fw_config->name) 239bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 240c92316bfSLuis R. Rodriguez "name:\t%s\n", 241c92316bfSLuis R. Rodriguez test_fw_config->name); 242c92316bfSLuis R. Rodriguez else 243bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 244c92316bfSLuis R. Rodriguez "name:\tEMTPY\n"); 245c92316bfSLuis R. Rodriguez 246bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 247c92316bfSLuis R. Rodriguez "num_requests:\t%u\n", test_fw_config->num_requests); 248c92316bfSLuis R. Rodriguez 249bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 250c92316bfSLuis R. Rodriguez "send_uevent:\t\t%s\n", 251c92316bfSLuis R. Rodriguez test_fw_config->send_uevent ? 252c92316bfSLuis R. Rodriguez "FW_ACTION_HOTPLUG" : 253c92316bfSLuis R. Rodriguez "FW_ACTION_NOHOTPLUG"); 254bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 2557feebfa4SScott Branden "into_buf:\t\t%s\n", 2567feebfa4SScott Branden test_fw_config->into_buf ? "true" : "false"); 2577feebfa4SScott Branden len += scnprintf(buf+len, PAGE_SIZE - len, 258c92316bfSLuis R. Rodriguez "sync_direct:\t\t%s\n", 259c92316bfSLuis R. Rodriguez test_fw_config->sync_direct ? "true" : "false"); 260bd17cc5aSDan Carpenter len += scnprintf(buf+len, PAGE_SIZE - len, 261c92316bfSLuis R. Rodriguez "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); 262c92316bfSLuis R. Rodriguez 263c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 264c92316bfSLuis R. Rodriguez 265c92316bfSLuis R. Rodriguez return len; 266c92316bfSLuis R. Rodriguez } 267c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(config); 268c92316bfSLuis R. Rodriguez 269c92316bfSLuis R. Rodriguez static ssize_t config_name_store(struct device *dev, 270c92316bfSLuis R. Rodriguez struct device_attribute *attr, 271c92316bfSLuis R. Rodriguez const char *buf, size_t count) 272c92316bfSLuis R. Rodriguez { 273c92316bfSLuis R. Rodriguez int ret; 274c92316bfSLuis R. Rodriguez 275c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 276c92316bfSLuis R. Rodriguez kfree_const(test_fw_config->name); 277c92316bfSLuis R. Rodriguez ret = __kstrncpy(&test_fw_config->name, buf, count, GFP_KERNEL); 278c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 279c92316bfSLuis R. Rodriguez 280c92316bfSLuis R. Rodriguez return ret; 281c92316bfSLuis R. Rodriguez } 282c92316bfSLuis R. Rodriguez 283c92316bfSLuis R. Rodriguez /* 284c92316bfSLuis R. Rodriguez * As per sysfs_kf_seq_show() the buf is max PAGE_SIZE. 285c92316bfSLuis R. Rodriguez */ 286c92316bfSLuis R. Rodriguez static ssize_t config_test_show_str(char *dst, 287c92316bfSLuis R. Rodriguez char *src) 288c92316bfSLuis R. Rodriguez { 289c92316bfSLuis R. Rodriguez int len; 290c92316bfSLuis R. Rodriguez 291c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 292c92316bfSLuis R. Rodriguez len = snprintf(dst, PAGE_SIZE, "%s\n", src); 293c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 294c92316bfSLuis R. Rodriguez 295c92316bfSLuis R. Rodriguez return len; 296c92316bfSLuis R. Rodriguez } 297c92316bfSLuis R. Rodriguez 298c92316bfSLuis R. Rodriguez static int test_dev_config_update_bool(const char *buf, size_t size, 299c92316bfSLuis R. Rodriguez bool *cfg) 300c92316bfSLuis R. Rodriguez { 301c92316bfSLuis R. Rodriguez int ret; 302c92316bfSLuis R. Rodriguez 303c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 304c92316bfSLuis R. Rodriguez if (strtobool(buf, cfg) < 0) 305c92316bfSLuis R. Rodriguez ret = -EINVAL; 306c92316bfSLuis R. Rodriguez else 307c92316bfSLuis R. Rodriguez ret = size; 308c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 309c92316bfSLuis R. Rodriguez 310c92316bfSLuis R. Rodriguez return ret; 311c92316bfSLuis R. Rodriguez } 312c92316bfSLuis R. Rodriguez 313c92316bfSLuis R. Rodriguez static ssize_t 314c92316bfSLuis R. Rodriguez test_dev_config_show_bool(char *buf, 315c92316bfSLuis R. Rodriguez bool config) 316c92316bfSLuis R. Rodriguez { 317c92316bfSLuis R. Rodriguez bool val; 318c92316bfSLuis R. Rodriguez 319c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 320c92316bfSLuis R. Rodriguez val = config; 321c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 322c92316bfSLuis R. Rodriguez 323c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 324c92316bfSLuis R. Rodriguez } 325c92316bfSLuis R. Rodriguez 326c92316bfSLuis R. Rodriguez static ssize_t test_dev_config_show_int(char *buf, int cfg) 327c92316bfSLuis R. Rodriguez { 328c92316bfSLuis R. Rodriguez int val; 329c92316bfSLuis R. Rodriguez 330c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 331c92316bfSLuis R. Rodriguez val = cfg; 332c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 333c92316bfSLuis R. Rodriguez 334c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%d\n", val); 335c92316bfSLuis R. Rodriguez } 336c92316bfSLuis R. Rodriguez 337c92316bfSLuis R. Rodriguez static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) 338c92316bfSLuis R. Rodriguez { 339c92316bfSLuis R. Rodriguez int ret; 340c92316bfSLuis R. Rodriguez long new; 341c92316bfSLuis R. Rodriguez 342c92316bfSLuis R. Rodriguez ret = kstrtol(buf, 10, &new); 343c92316bfSLuis R. Rodriguez if (ret) 344c92316bfSLuis R. Rodriguez return ret; 345c92316bfSLuis R. Rodriguez 346c92316bfSLuis R. Rodriguez if (new > U8_MAX) 347c92316bfSLuis R. Rodriguez return -EINVAL; 348c92316bfSLuis R. Rodriguez 349c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 350c92316bfSLuis R. Rodriguez *(u8 *)cfg = new; 351c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 352c92316bfSLuis R. Rodriguez 353c92316bfSLuis R. Rodriguez /* Always return full write size even if we didn't consume all */ 354c92316bfSLuis R. Rodriguez return size; 355c92316bfSLuis R. Rodriguez } 356c92316bfSLuis R. Rodriguez 357c92316bfSLuis R. Rodriguez static ssize_t test_dev_config_show_u8(char *buf, u8 cfg) 358c92316bfSLuis R. Rodriguez { 359c92316bfSLuis R. Rodriguez u8 val; 360c92316bfSLuis R. Rodriguez 361c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 362c92316bfSLuis R. Rodriguez val = cfg; 363c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 364c92316bfSLuis R. Rodriguez 365c92316bfSLuis R. Rodriguez return snprintf(buf, PAGE_SIZE, "%u\n", val); 366c92316bfSLuis R. Rodriguez } 367c92316bfSLuis R. Rodriguez 368c92316bfSLuis R. Rodriguez static ssize_t config_name_show(struct device *dev, 369c92316bfSLuis R. Rodriguez struct device_attribute *attr, 370c92316bfSLuis R. Rodriguez char *buf) 371c92316bfSLuis R. Rodriguez { 372c92316bfSLuis R. Rodriguez return config_test_show_str(buf, test_fw_config->name); 373c92316bfSLuis R. Rodriguez } 374b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_name); 375c92316bfSLuis R. Rodriguez 376c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_store(struct device *dev, 377c92316bfSLuis R. Rodriguez struct device_attribute *attr, 378c92316bfSLuis R. Rodriguez const char *buf, size_t count) 379c92316bfSLuis R. Rodriguez { 380c92316bfSLuis R. Rodriguez int rc; 381c92316bfSLuis R. Rodriguez 382c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 383c92316bfSLuis R. Rodriguez if (test_fw_config->reqs) { 384c92316bfSLuis R. Rodriguez pr_err("Must call release_all_firmware prior to changing config\n"); 385c92316bfSLuis R. Rodriguez rc = -EINVAL; 386a5e19233SWei Yongjun mutex_unlock(&test_fw_mutex); 387c92316bfSLuis R. Rodriguez goto out; 388c92316bfSLuis R. Rodriguez } 389c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 390c92316bfSLuis R. Rodriguez 391c92316bfSLuis R. Rodriguez rc = test_dev_config_update_u8(buf, count, 392c92316bfSLuis R. Rodriguez &test_fw_config->num_requests); 393c92316bfSLuis R. Rodriguez 394c92316bfSLuis R. Rodriguez out: 395c92316bfSLuis R. Rodriguez return rc; 396c92316bfSLuis R. Rodriguez } 397c92316bfSLuis R. Rodriguez 398c92316bfSLuis R. Rodriguez static ssize_t config_num_requests_show(struct device *dev, 399c92316bfSLuis R. Rodriguez struct device_attribute *attr, 400c92316bfSLuis R. Rodriguez char *buf) 401c92316bfSLuis R. Rodriguez { 402c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->num_requests); 403c92316bfSLuis R. Rodriguez } 404b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_num_requests); 405c92316bfSLuis R. Rodriguez 4067feebfa4SScott Branden static ssize_t config_into_buf_store(struct device *dev, 4077feebfa4SScott Branden struct device_attribute *attr, 4087feebfa4SScott Branden const char *buf, size_t count) 4097feebfa4SScott Branden { 4107feebfa4SScott Branden return test_dev_config_update_bool(buf, 4117feebfa4SScott Branden count, 4127feebfa4SScott Branden &test_fw_config->into_buf); 4137feebfa4SScott Branden } 4147feebfa4SScott Branden 4157feebfa4SScott Branden static ssize_t config_into_buf_show(struct device *dev, 4167feebfa4SScott Branden struct device_attribute *attr, 4177feebfa4SScott Branden char *buf) 4187feebfa4SScott Branden { 4197feebfa4SScott Branden return test_dev_config_show_bool(buf, test_fw_config->into_buf); 4207feebfa4SScott Branden } 4217feebfa4SScott Branden static DEVICE_ATTR_RW(config_into_buf); 4227feebfa4SScott Branden 423c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_store(struct device *dev, 424c92316bfSLuis R. Rodriguez struct device_attribute *attr, 425c92316bfSLuis R. Rodriguez const char *buf, size_t count) 426c92316bfSLuis R. Rodriguez { 427c92316bfSLuis R. Rodriguez int rc = test_dev_config_update_bool(buf, count, 428c92316bfSLuis R. Rodriguez &test_fw_config->sync_direct); 429c92316bfSLuis R. Rodriguez 430c92316bfSLuis R. Rodriguez if (rc == count) 431c92316bfSLuis R. Rodriguez test_fw_config->req_firmware = test_fw_config->sync_direct ? 432c92316bfSLuis R. Rodriguez request_firmware_direct : 433c92316bfSLuis R. Rodriguez request_firmware; 434c92316bfSLuis R. Rodriguez return rc; 435c92316bfSLuis R. Rodriguez } 436c92316bfSLuis R. Rodriguez 437c92316bfSLuis R. Rodriguez static ssize_t config_sync_direct_show(struct device *dev, 438c92316bfSLuis R. Rodriguez struct device_attribute *attr, 439c92316bfSLuis R. Rodriguez char *buf) 440c92316bfSLuis R. Rodriguez { 441c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->sync_direct); 442c92316bfSLuis R. Rodriguez } 443b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_sync_direct); 444c92316bfSLuis R. Rodriguez 445c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_store(struct device *dev, 446c92316bfSLuis R. Rodriguez struct device_attribute *attr, 447c92316bfSLuis R. Rodriguez const char *buf, size_t count) 448c92316bfSLuis R. Rodriguez { 449c92316bfSLuis R. Rodriguez return test_dev_config_update_bool(buf, count, 450c92316bfSLuis R. Rodriguez &test_fw_config->send_uevent); 451c92316bfSLuis R. Rodriguez } 452c92316bfSLuis R. Rodriguez 453c92316bfSLuis R. Rodriguez static ssize_t config_send_uevent_show(struct device *dev, 454c92316bfSLuis R. Rodriguez struct device_attribute *attr, 455c92316bfSLuis R. Rodriguez char *buf) 456c92316bfSLuis R. Rodriguez { 457c92316bfSLuis R. Rodriguez return test_dev_config_show_bool(buf, test_fw_config->send_uevent); 458c92316bfSLuis R. Rodriguez } 459b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_send_uevent); 460c92316bfSLuis R. Rodriguez 461c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_store(struct device *dev, 462c92316bfSLuis R. Rodriguez struct device_attribute *attr, 463c92316bfSLuis R. Rodriguez const char *buf, size_t count) 464c92316bfSLuis R. Rodriguez { 465c92316bfSLuis R. Rodriguez return test_dev_config_update_u8(buf, count, 466c92316bfSLuis R. Rodriguez &test_fw_config->read_fw_idx); 467c92316bfSLuis R. Rodriguez } 468c92316bfSLuis R. Rodriguez 469c92316bfSLuis R. Rodriguez static ssize_t config_read_fw_idx_show(struct device *dev, 470c92316bfSLuis R. Rodriguez struct device_attribute *attr, 471c92316bfSLuis R. Rodriguez char *buf) 472c92316bfSLuis R. Rodriguez { 473c92316bfSLuis R. Rodriguez return test_dev_config_show_u8(buf, test_fw_config->read_fw_idx); 474c92316bfSLuis R. Rodriguez } 475b6b996b6SJoe Perches static DEVICE_ATTR_RW(config_read_fw_idx); 476c92316bfSLuis R. Rodriguez 477c92316bfSLuis R. Rodriguez 4780a8adf58SKees Cook static ssize_t trigger_request_store(struct device *dev, 4790a8adf58SKees Cook struct device_attribute *attr, 4800a8adf58SKees Cook const char *buf, size_t count) 4810a8adf58SKees Cook { 4820a8adf58SKees Cook int rc; 4830a8adf58SKees Cook char *name; 4840a8adf58SKees Cook 485be4a1326SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 4860a8adf58SKees Cook if (!name) 4870a8adf58SKees Cook return -ENOSPC; 4880a8adf58SKees Cook 4890a8adf58SKees Cook pr_info("loading '%s'\n", name); 4900a8adf58SKees Cook 4910a8adf58SKees Cook mutex_lock(&test_fw_mutex); 4920a8adf58SKees Cook release_firmware(test_firmware); 4930a8adf58SKees Cook test_firmware = NULL; 4940a8adf58SKees Cook rc = request_firmware(&test_firmware, name, dev); 49547e0bbb7SBrian Norris if (rc) { 4960a8adf58SKees Cook pr_info("load of '%s' failed: %d\n", name, rc); 49747e0bbb7SBrian Norris goto out; 49847e0bbb7SBrian Norris } 49947e0bbb7SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 50047e0bbb7SBrian Norris rc = count; 50147e0bbb7SBrian Norris 50247e0bbb7SBrian Norris out: 5030a8adf58SKees Cook mutex_unlock(&test_fw_mutex); 5040a8adf58SKees Cook 5050a8adf58SKees Cook kfree(name); 5060a8adf58SKees Cook 50747e0bbb7SBrian Norris return rc; 5080a8adf58SKees Cook } 5090a8adf58SKees Cook static DEVICE_ATTR_WO(trigger_request); 5100a8adf58SKees Cook 511*548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 512*548193cbSHans de Goede static ssize_t trigger_request_platform_store(struct device *dev, 513*548193cbSHans de Goede struct device_attribute *attr, 514*548193cbSHans de Goede const char *buf, size_t count) 515*548193cbSHans de Goede { 516*548193cbSHans de Goede static const u8 test_data[] = { 517*548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x01, 0x02, 0x03, 0x04, 518*548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x05, 0x06, 0x07, 0x08, 519*548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x10, 0x20, 0x30, 0x40, 520*548193cbSHans de Goede 0x55, 0xaa, 0x55, 0xaa, 0x50, 0x60, 0x70, 0x80 521*548193cbSHans de Goede }; 522*548193cbSHans de Goede struct efi_embedded_fw efi_embedded_fw; 523*548193cbSHans de Goede const struct firmware *firmware = NULL; 524*548193cbSHans de Goede char *name; 525*548193cbSHans de Goede int rc; 526*548193cbSHans de Goede 527*548193cbSHans de Goede name = kstrndup(buf, count, GFP_KERNEL); 528*548193cbSHans de Goede if (!name) 529*548193cbSHans de Goede return -ENOSPC; 530*548193cbSHans de Goede 531*548193cbSHans de Goede pr_info("inserting test platform fw '%s'\n", name); 532*548193cbSHans de Goede efi_embedded_fw.name = name; 533*548193cbSHans de Goede efi_embedded_fw.data = (void *)test_data; 534*548193cbSHans de Goede efi_embedded_fw.length = sizeof(test_data); 535*548193cbSHans de Goede list_add(&efi_embedded_fw.list, &efi_embedded_fw_list); 536*548193cbSHans de Goede 537*548193cbSHans de Goede pr_info("loading '%s'\n", name); 538*548193cbSHans de Goede rc = firmware_request_platform(&firmware, name, dev); 539*548193cbSHans de Goede if (rc) { 540*548193cbSHans de Goede pr_info("load of '%s' failed: %d\n", name, rc); 541*548193cbSHans de Goede goto out; 542*548193cbSHans de Goede } 543*548193cbSHans de Goede if (firmware->size != sizeof(test_data) || 544*548193cbSHans de Goede memcmp(firmware->data, test_data, sizeof(test_data)) != 0) { 545*548193cbSHans de Goede pr_info("firmware contents mismatch for '%s'\n", name); 546*548193cbSHans de Goede rc = -EINVAL; 547*548193cbSHans de Goede goto out; 548*548193cbSHans de Goede } 549*548193cbSHans de Goede pr_info("loaded: %zu\n", firmware->size); 550*548193cbSHans de Goede rc = count; 551*548193cbSHans de Goede 552*548193cbSHans de Goede out: 553*548193cbSHans de Goede release_firmware(firmware); 554*548193cbSHans de Goede list_del(&efi_embedded_fw.list); 555*548193cbSHans de Goede kfree(name); 556*548193cbSHans de Goede 557*548193cbSHans de Goede return rc; 558*548193cbSHans de Goede } 559*548193cbSHans de Goede static DEVICE_ATTR_WO(trigger_request_platform); 560*548193cbSHans de Goede #endif 561*548193cbSHans de Goede 562eb910947SBrian Norris static DECLARE_COMPLETION(async_fw_done); 563eb910947SBrian Norris 564eb910947SBrian Norris static void trigger_async_request_cb(const struct firmware *fw, void *context) 565eb910947SBrian Norris { 566eb910947SBrian Norris test_firmware = fw; 567eb910947SBrian Norris complete(&async_fw_done); 568eb910947SBrian Norris } 569eb910947SBrian Norris 570eb910947SBrian Norris static ssize_t trigger_async_request_store(struct device *dev, 571eb910947SBrian Norris struct device_attribute *attr, 572eb910947SBrian Norris const char *buf, size_t count) 573eb910947SBrian Norris { 574eb910947SBrian Norris int rc; 575eb910947SBrian Norris char *name; 576eb910947SBrian Norris 577eb910947SBrian Norris name = kstrndup(buf, count, GFP_KERNEL); 578eb910947SBrian Norris if (!name) 579eb910947SBrian Norris return -ENOSPC; 580eb910947SBrian Norris 581eb910947SBrian Norris pr_info("loading '%s'\n", name); 582eb910947SBrian Norris 583eb910947SBrian Norris mutex_lock(&test_fw_mutex); 584eb910947SBrian Norris release_firmware(test_firmware); 585eb910947SBrian Norris test_firmware = NULL; 586eb910947SBrian Norris rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, 587eb910947SBrian Norris NULL, trigger_async_request_cb); 588eb910947SBrian Norris if (rc) { 589eb910947SBrian Norris pr_info("async load of '%s' failed: %d\n", name, rc); 590eb910947SBrian Norris kfree(name); 591eb910947SBrian Norris goto out; 592eb910947SBrian Norris } 593eb910947SBrian Norris /* Free 'name' ASAP, to test for race conditions */ 594eb910947SBrian Norris kfree(name); 595eb910947SBrian Norris 596eb910947SBrian Norris wait_for_completion(&async_fw_done); 597eb910947SBrian Norris 598eb910947SBrian Norris if (test_firmware) { 599eb910947SBrian Norris pr_info("loaded: %zu\n", test_firmware->size); 600eb910947SBrian Norris rc = count; 601eb910947SBrian Norris } else { 602eb910947SBrian Norris pr_err("failed to async load firmware\n"); 6037feebfa4SScott Branden rc = -ENOMEM; 604eb910947SBrian Norris } 605eb910947SBrian Norris 606eb910947SBrian Norris out: 607eb910947SBrian Norris mutex_unlock(&test_fw_mutex); 608eb910947SBrian Norris 609eb910947SBrian Norris return rc; 610eb910947SBrian Norris } 611eb910947SBrian Norris static DEVICE_ATTR_WO(trigger_async_request); 612eb910947SBrian Norris 613061132d2SLuis R. Rodriguez static ssize_t trigger_custom_fallback_store(struct device *dev, 614061132d2SLuis R. Rodriguez struct device_attribute *attr, 615061132d2SLuis R. Rodriguez const char *buf, size_t count) 616061132d2SLuis R. Rodriguez { 617061132d2SLuis R. Rodriguez int rc; 618061132d2SLuis R. Rodriguez char *name; 619061132d2SLuis R. Rodriguez 620061132d2SLuis R. Rodriguez name = kstrndup(buf, count, GFP_KERNEL); 621061132d2SLuis R. Rodriguez if (!name) 622061132d2SLuis R. Rodriguez return -ENOSPC; 623061132d2SLuis R. Rodriguez 624061132d2SLuis R. Rodriguez pr_info("loading '%s' using custom fallback mechanism\n", name); 625061132d2SLuis R. Rodriguez 626061132d2SLuis R. Rodriguez mutex_lock(&test_fw_mutex); 627061132d2SLuis R. Rodriguez release_firmware(test_firmware); 628061132d2SLuis R. Rodriguez test_firmware = NULL; 629061132d2SLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name, 630061132d2SLuis R. Rodriguez dev, GFP_KERNEL, NULL, 631061132d2SLuis R. Rodriguez trigger_async_request_cb); 632061132d2SLuis R. Rodriguez if (rc) { 633061132d2SLuis R. Rodriguez pr_info("async load of '%s' failed: %d\n", name, rc); 634061132d2SLuis R. Rodriguez kfree(name); 635061132d2SLuis R. Rodriguez goto out; 636061132d2SLuis R. Rodriguez } 637061132d2SLuis R. Rodriguez /* Free 'name' ASAP, to test for race conditions */ 638061132d2SLuis R. Rodriguez kfree(name); 639061132d2SLuis R. Rodriguez 640061132d2SLuis R. Rodriguez wait_for_completion(&async_fw_done); 641061132d2SLuis R. Rodriguez 642061132d2SLuis R. Rodriguez if (test_firmware) { 643061132d2SLuis R. Rodriguez pr_info("loaded: %zu\n", test_firmware->size); 644061132d2SLuis R. Rodriguez rc = count; 645061132d2SLuis R. Rodriguez } else { 646061132d2SLuis R. Rodriguez pr_err("failed to async load firmware\n"); 647061132d2SLuis R. Rodriguez rc = -ENODEV; 648061132d2SLuis R. Rodriguez } 649061132d2SLuis R. Rodriguez 650061132d2SLuis R. Rodriguez out: 651061132d2SLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 652061132d2SLuis R. Rodriguez 653061132d2SLuis R. Rodriguez return rc; 654061132d2SLuis R. Rodriguez } 655061132d2SLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_custom_fallback); 656061132d2SLuis R. Rodriguez 657c92316bfSLuis R. Rodriguez static int test_fw_run_batch_request(void *data) 658c92316bfSLuis R. Rodriguez { 659c92316bfSLuis R. Rodriguez struct test_batched_req *req = data; 660c92316bfSLuis R. Rodriguez 661c92316bfSLuis R. Rodriguez if (!req) { 662c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 663c92316bfSLuis R. Rodriguez return -EINVAL; 664c92316bfSLuis R. Rodriguez } 665c92316bfSLuis R. Rodriguez 6667feebfa4SScott Branden if (test_fw_config->into_buf) { 6677feebfa4SScott Branden void *test_buf; 6687feebfa4SScott Branden 6697feebfa4SScott Branden test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); 6707feebfa4SScott Branden if (!test_buf) 6717feebfa4SScott Branden return -ENOSPC; 6727feebfa4SScott Branden 6737feebfa4SScott Branden req->rc = request_firmware_into_buf(&req->fw, 6747feebfa4SScott Branden req->name, 6757feebfa4SScott Branden req->dev, 6767feebfa4SScott Branden test_buf, 6777feebfa4SScott Branden TEST_FIRMWARE_BUF_SIZE); 6787feebfa4SScott Branden if (!req->fw) 6797feebfa4SScott Branden kfree(test_buf); 6807feebfa4SScott Branden } else { 6817feebfa4SScott Branden req->rc = test_fw_config->req_firmware(&req->fw, 6827feebfa4SScott Branden req->name, 6837feebfa4SScott Branden req->dev); 6847feebfa4SScott Branden } 6857feebfa4SScott Branden 686c92316bfSLuis R. Rodriguez if (req->rc) { 687c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync load failed: %d\n", 688c92316bfSLuis R. Rodriguez req->idx, req->rc); 689c92316bfSLuis R. Rodriguez if (!test_fw_config->test_result) 690c92316bfSLuis R. Rodriguez test_fw_config->test_result = req->rc; 691c92316bfSLuis R. Rodriguez } else if (req->fw) { 692c92316bfSLuis R. Rodriguez req->sent = true; 693c92316bfSLuis R. Rodriguez pr_info("#%u: batched sync loaded %zu\n", 694c92316bfSLuis R. Rodriguez req->idx, req->fw->size); 695c92316bfSLuis R. Rodriguez } 696c92316bfSLuis R. Rodriguez complete(&req->completion); 697c92316bfSLuis R. Rodriguez 698c92316bfSLuis R. Rodriguez req->task = NULL; 699c92316bfSLuis R. Rodriguez 700c92316bfSLuis R. Rodriguez return 0; 701c92316bfSLuis R. Rodriguez } 702c92316bfSLuis R. Rodriguez 703c92316bfSLuis R. Rodriguez /* 704c92316bfSLuis R. Rodriguez * We use a kthread as otherwise the kernel serializes all our sync requests 705c92316bfSLuis R. Rodriguez * and we would not be able to mimic batched requests on a sync call. Batched 706c92316bfSLuis R. Rodriguez * requests on a sync call can for instance happen on a device driver when 707c92316bfSLuis R. Rodriguez * multiple cards are used and firmware loading happens outside of probe. 708c92316bfSLuis R. Rodriguez */ 709c92316bfSLuis R. Rodriguez static ssize_t trigger_batched_requests_store(struct device *dev, 710c92316bfSLuis R. Rodriguez struct device_attribute *attr, 711c92316bfSLuis R. Rodriguez const char *buf, size_t count) 712c92316bfSLuis R. Rodriguez { 713c92316bfSLuis R. Rodriguez struct test_batched_req *req; 714c92316bfSLuis R. Rodriguez int rc; 715c92316bfSLuis R. Rodriguez u8 i; 716c92316bfSLuis R. Rodriguez 717c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 718c92316bfSLuis R. Rodriguez 719fad953ceSKees Cook test_fw_config->reqs = 720fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 721fad953ceSKees Cook test_fw_config->num_requests, 2)); 722c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 723c92316bfSLuis R. Rodriguez rc = -ENOMEM; 724c92316bfSLuis R. Rodriguez goto out_unlock; 725c92316bfSLuis R. Rodriguez } 726c92316bfSLuis R. Rodriguez 727c92316bfSLuis R. Rodriguez pr_info("batched sync firmware loading '%s' %u times\n", 728c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 729c92316bfSLuis R. Rodriguez 730c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 731c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 732c92316bfSLuis R. Rodriguez req->fw = NULL; 733c92316bfSLuis R. Rodriguez req->idx = i; 734c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 735c92316bfSLuis R. Rodriguez req->dev = dev; 736c92316bfSLuis R. Rodriguez init_completion(&req->completion); 737c92316bfSLuis R. Rodriguez req->task = kthread_run(test_fw_run_batch_request, req, 738c92316bfSLuis R. Rodriguez "%s-%u", KBUILD_MODNAME, req->idx); 739c92316bfSLuis R. Rodriguez if (!req->task || IS_ERR(req->task)) { 740c92316bfSLuis R. Rodriguez pr_err("Setting up thread %u failed\n", req->idx); 741c92316bfSLuis R. Rodriguez req->task = NULL; 742c92316bfSLuis R. Rodriguez rc = -ENOMEM; 743c92316bfSLuis R. Rodriguez goto out_bail; 744c92316bfSLuis R. Rodriguez } 745c92316bfSLuis R. Rodriguez } 746c92316bfSLuis R. Rodriguez 747c92316bfSLuis R. Rodriguez rc = count; 748c92316bfSLuis R. Rodriguez 749c92316bfSLuis R. Rodriguez /* 750c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 751c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 752c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 753c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 754c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 755c92316bfSLuis R. Rodriguez */ 756c92316bfSLuis R. Rodriguez 757c92316bfSLuis R. Rodriguez out_bail: 758c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 759c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 760c92316bfSLuis R. Rodriguez if (req->task || req->sent) 761c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 762c92316bfSLuis R. Rodriguez } 763c92316bfSLuis R. Rodriguez 764c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 765c92316bfSLuis R. Rodriguez if (rc < 0) 766c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 767c92316bfSLuis R. Rodriguez 768c92316bfSLuis R. Rodriguez out_unlock: 769c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 770c92316bfSLuis R. Rodriguez 771c92316bfSLuis R. Rodriguez return rc; 772c92316bfSLuis R. Rodriguez } 773c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests); 774c92316bfSLuis R. Rodriguez 775c92316bfSLuis R. Rodriguez /* 776c92316bfSLuis R. Rodriguez * We wait for each callback to return with the lock held, no need to lock here 777c92316bfSLuis R. Rodriguez */ 778c92316bfSLuis R. Rodriguez static void trigger_batched_cb(const struct firmware *fw, void *context) 779c92316bfSLuis R. Rodriguez { 780c92316bfSLuis R. Rodriguez struct test_batched_req *req = context; 781c92316bfSLuis R. Rodriguez 782c92316bfSLuis R. Rodriguez if (!req) { 783c92316bfSLuis R. Rodriguez test_fw_config->test_result = -EINVAL; 784c92316bfSLuis R. Rodriguez return; 785c92316bfSLuis R. Rodriguez } 786c92316bfSLuis R. Rodriguez 787c92316bfSLuis R. Rodriguez /* forces *some* batched requests to queue up */ 788c92316bfSLuis R. Rodriguez if (!req->idx) 789c92316bfSLuis R. Rodriguez ssleep(2); 790c92316bfSLuis R. Rodriguez 791c92316bfSLuis R. Rodriguez req->fw = fw; 792c92316bfSLuis R. Rodriguez 793c92316bfSLuis R. Rodriguez /* 794c92316bfSLuis R. Rodriguez * Unfortunately the firmware API gives us nothing other than a null FW 795c92316bfSLuis R. Rodriguez * if the firmware was not found on async requests. Best we can do is 796c92316bfSLuis R. Rodriguez * just assume -ENOENT. A better API would pass the actual return 797c92316bfSLuis R. Rodriguez * value to the callback. 798c92316bfSLuis R. Rodriguez */ 799c92316bfSLuis R. Rodriguez if (!fw && !test_fw_config->test_result) 800c92316bfSLuis R. Rodriguez test_fw_config->test_result = -ENOENT; 801c92316bfSLuis R. Rodriguez 802c92316bfSLuis R. Rodriguez complete(&req->completion); 803c92316bfSLuis R. Rodriguez } 804c92316bfSLuis R. Rodriguez 805c92316bfSLuis R. Rodriguez static 806c92316bfSLuis R. Rodriguez ssize_t trigger_batched_requests_async_store(struct device *dev, 807c92316bfSLuis R. Rodriguez struct device_attribute *attr, 808c92316bfSLuis R. Rodriguez const char *buf, size_t count) 809c92316bfSLuis R. Rodriguez { 810c92316bfSLuis R. Rodriguez struct test_batched_req *req; 811c92316bfSLuis R. Rodriguez bool send_uevent; 812c92316bfSLuis R. Rodriguez int rc; 813c92316bfSLuis R. Rodriguez u8 i; 814c92316bfSLuis R. Rodriguez 815c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 816c92316bfSLuis R. Rodriguez 817fad953ceSKees Cook test_fw_config->reqs = 818fad953ceSKees Cook vzalloc(array3_size(sizeof(struct test_batched_req), 819fad953ceSKees Cook test_fw_config->num_requests, 2)); 820c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 821c92316bfSLuis R. Rodriguez rc = -ENOMEM; 822c92316bfSLuis R. Rodriguez goto out; 823c92316bfSLuis R. Rodriguez } 824c92316bfSLuis R. Rodriguez 825c92316bfSLuis R. Rodriguez pr_info("batched loading '%s' custom fallback mechanism %u times\n", 826c92316bfSLuis R. Rodriguez test_fw_config->name, test_fw_config->num_requests); 827c92316bfSLuis R. Rodriguez 828c92316bfSLuis R. Rodriguez send_uevent = test_fw_config->send_uevent ? FW_ACTION_HOTPLUG : 829c92316bfSLuis R. Rodriguez FW_ACTION_NOHOTPLUG; 830c92316bfSLuis R. Rodriguez 831c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 832c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 833c92316bfSLuis R. Rodriguez req->name = test_fw_config->name; 834c92316bfSLuis R. Rodriguez req->fw = NULL; 835c92316bfSLuis R. Rodriguez req->idx = i; 836c92316bfSLuis R. Rodriguez init_completion(&req->completion); 837c92316bfSLuis R. Rodriguez rc = request_firmware_nowait(THIS_MODULE, send_uevent, 838c92316bfSLuis R. Rodriguez req->name, 839c92316bfSLuis R. Rodriguez dev, GFP_KERNEL, req, 840c92316bfSLuis R. Rodriguez trigger_batched_cb); 841c92316bfSLuis R. Rodriguez if (rc) { 842c92316bfSLuis R. Rodriguez pr_info("#%u: batched async load failed setup: %d\n", 843c92316bfSLuis R. Rodriguez i, rc); 844c92316bfSLuis R. Rodriguez req->rc = rc; 845c92316bfSLuis R. Rodriguez goto out_bail; 846c92316bfSLuis R. Rodriguez } else 847c92316bfSLuis R. Rodriguez req->sent = true; 848c92316bfSLuis R. Rodriguez } 849c92316bfSLuis R. Rodriguez 850c92316bfSLuis R. Rodriguez rc = count; 851c92316bfSLuis R. Rodriguez 852c92316bfSLuis R. Rodriguez out_bail: 853c92316bfSLuis R. Rodriguez 854c92316bfSLuis R. Rodriguez /* 855c92316bfSLuis R. Rodriguez * We require an explicit release to enable more time and delay of 856c92316bfSLuis R. Rodriguez * calling release_firmware() to improve our chances of forcing a 857c92316bfSLuis R. Rodriguez * batched request. If we instead called release_firmware() right away 858c92316bfSLuis R. Rodriguez * then we might miss on an opportunity of having a successful firmware 859c92316bfSLuis R. Rodriguez * request pass on the opportunity to be come a batched request. 860c92316bfSLuis R. Rodriguez */ 861c92316bfSLuis R. Rodriguez 862c92316bfSLuis R. Rodriguez for (i = 0; i < test_fw_config->num_requests; i++) { 863c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[i]; 864c92316bfSLuis R. Rodriguez if (req->sent) 865c92316bfSLuis R. Rodriguez wait_for_completion(&req->completion); 866c92316bfSLuis R. Rodriguez } 867c92316bfSLuis R. Rodriguez 868c92316bfSLuis R. Rodriguez /* Override any worker error if we had a general setup error */ 869c92316bfSLuis R. Rodriguez if (rc < 0) 870c92316bfSLuis R. Rodriguez test_fw_config->test_result = rc; 871c92316bfSLuis R. Rodriguez 872c92316bfSLuis R. Rodriguez out: 873c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 874c92316bfSLuis R. Rodriguez 875c92316bfSLuis R. Rodriguez return rc; 876c92316bfSLuis R. Rodriguez } 877c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(trigger_batched_requests_async); 878c92316bfSLuis R. Rodriguez 879c92316bfSLuis R. Rodriguez static ssize_t test_result_show(struct device *dev, 880c92316bfSLuis R. Rodriguez struct device_attribute *attr, 881c92316bfSLuis R. Rodriguez char *buf) 882c92316bfSLuis R. Rodriguez { 883c92316bfSLuis R. Rodriguez return test_dev_config_show_int(buf, test_fw_config->test_result); 884c92316bfSLuis R. Rodriguez } 885c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(test_result); 886c92316bfSLuis R. Rodriguez 887c92316bfSLuis R. Rodriguez static ssize_t release_all_firmware_store(struct device *dev, 888c92316bfSLuis R. Rodriguez struct device_attribute *attr, 889c92316bfSLuis R. Rodriguez const char *buf, size_t count) 890c92316bfSLuis R. Rodriguez { 891c92316bfSLuis R. Rodriguez test_release_all_firmware(); 892c92316bfSLuis R. Rodriguez return count; 893c92316bfSLuis R. Rodriguez } 894c92316bfSLuis R. Rodriguez static DEVICE_ATTR_WO(release_all_firmware); 895c92316bfSLuis R. Rodriguez 896c92316bfSLuis R. Rodriguez static ssize_t read_firmware_show(struct device *dev, 897c92316bfSLuis R. Rodriguez struct device_attribute *attr, 898c92316bfSLuis R. Rodriguez char *buf) 899c92316bfSLuis R. Rodriguez { 900c92316bfSLuis R. Rodriguez struct test_batched_req *req; 901c92316bfSLuis R. Rodriguez u8 idx; 902c92316bfSLuis R. Rodriguez ssize_t rc = 0; 903c92316bfSLuis R. Rodriguez 904c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 905c92316bfSLuis R. Rodriguez 906c92316bfSLuis R. Rodriguez idx = test_fw_config->read_fw_idx; 907c92316bfSLuis R. Rodriguez if (idx >= test_fw_config->num_requests) { 908c92316bfSLuis R. Rodriguez rc = -ERANGE; 909c92316bfSLuis R. Rodriguez goto out; 910c92316bfSLuis R. Rodriguez } 911c92316bfSLuis R. Rodriguez 912c92316bfSLuis R. Rodriguez if (!test_fw_config->reqs) { 913c92316bfSLuis R. Rodriguez rc = -EINVAL; 914c92316bfSLuis R. Rodriguez goto out; 915c92316bfSLuis R. Rodriguez } 916c92316bfSLuis R. Rodriguez 917c92316bfSLuis R. Rodriguez req = &test_fw_config->reqs[idx]; 918c92316bfSLuis R. Rodriguez if (!req->fw) { 919c92316bfSLuis R. Rodriguez pr_err("#%u: failed to async load firmware\n", idx); 920c92316bfSLuis R. Rodriguez rc = -ENOENT; 921c92316bfSLuis R. Rodriguez goto out; 922c92316bfSLuis R. Rodriguez } 923c92316bfSLuis R. Rodriguez 924c92316bfSLuis R. Rodriguez pr_info("#%u: loaded %zu\n", idx, req->fw->size); 925c92316bfSLuis R. Rodriguez 926c92316bfSLuis R. Rodriguez if (req->fw->size > PAGE_SIZE) { 927c92316bfSLuis R. Rodriguez pr_err("Testing interface must use PAGE_SIZE firmware for now\n"); 928c92316bfSLuis R. Rodriguez rc = -EINVAL; 9298bb0a886SColin Ian King goto out; 930c92316bfSLuis R. Rodriguez } 931c92316bfSLuis R. Rodriguez memcpy(buf, req->fw->data, req->fw->size); 932c92316bfSLuis R. Rodriguez 933c92316bfSLuis R. Rodriguez rc = req->fw->size; 934c92316bfSLuis R. Rodriguez out: 935c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 936c92316bfSLuis R. Rodriguez 937c92316bfSLuis R. Rodriguez return rc; 938c92316bfSLuis R. Rodriguez } 939c92316bfSLuis R. Rodriguez static DEVICE_ATTR_RO(read_firmware); 940c92316bfSLuis R. Rodriguez 941083a93b0SLuis R. Rodriguez #define TEST_FW_DEV_ATTR(name) &dev_attr_##name.attr 942083a93b0SLuis R. Rodriguez 943083a93b0SLuis R. Rodriguez static struct attribute *test_dev_attrs[] = { 944c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(reset), 945c92316bfSLuis R. Rodriguez 946c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config), 947c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_name), 948c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_num_requests), 9497feebfa4SScott Branden TEST_FW_DEV_ATTR(config_into_buf), 950c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_sync_direct), 951c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_send_uevent), 952c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(config_read_fw_idx), 953c92316bfSLuis R. Rodriguez 954c92316bfSLuis R. Rodriguez /* These don't use the config at all - they could be ported! */ 955083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_request), 956083a93b0SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_async_request), 957061132d2SLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_custom_fallback), 958*548193cbSHans de Goede #ifdef CONFIG_EFI_EMBEDDED_FIRMWARE 959*548193cbSHans de Goede TEST_FW_DEV_ATTR(trigger_request_platform), 960*548193cbSHans de Goede #endif 961c92316bfSLuis R. Rodriguez 962c92316bfSLuis R. Rodriguez /* These use the config and can use the test_result */ 963c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests), 964c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(trigger_batched_requests_async), 965c92316bfSLuis R. Rodriguez 966c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(release_all_firmware), 967c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(test_result), 968c92316bfSLuis R. Rodriguez TEST_FW_DEV_ATTR(read_firmware), 969083a93b0SLuis R. Rodriguez NULL, 970083a93b0SLuis R. Rodriguez }; 971083a93b0SLuis R. Rodriguez 972083a93b0SLuis R. Rodriguez ATTRIBUTE_GROUPS(test_dev); 973083a93b0SLuis R. Rodriguez 97467fd553cSLuis R. Rodriguez static struct miscdevice test_fw_misc_device = { 97567fd553cSLuis R. Rodriguez .minor = MISC_DYNAMIC_MINOR, 97667fd553cSLuis R. Rodriguez .name = "test_firmware", 97767fd553cSLuis R. Rodriguez .fops = &test_fw_fops, 978083a93b0SLuis R. Rodriguez .groups = test_dev_groups, 97967fd553cSLuis R. Rodriguez }; 98067fd553cSLuis R. Rodriguez 9810a8adf58SKees Cook static int __init test_firmware_init(void) 9820a8adf58SKees Cook { 9830a8adf58SKees Cook int rc; 9840a8adf58SKees Cook 985c92316bfSLuis R. Rodriguez test_fw_config = kzalloc(sizeof(struct test_config), GFP_KERNEL); 986c92316bfSLuis R. Rodriguez if (!test_fw_config) 987c92316bfSLuis R. Rodriguez return -ENOMEM; 988c92316bfSLuis R. Rodriguez 989c92316bfSLuis R. Rodriguez rc = __test_firmware_config_init(); 990d4fddac5SWenwen Wang if (rc) { 991d4fddac5SWenwen Wang kfree(test_fw_config); 992d4fddac5SWenwen Wang pr_err("could not init firmware test config: %d\n", rc); 993c92316bfSLuis R. Rodriguez return rc; 994d4fddac5SWenwen Wang } 995c92316bfSLuis R. Rodriguez 9960a8adf58SKees Cook rc = misc_register(&test_fw_misc_device); 9970a8adf58SKees Cook if (rc) { 998c92316bfSLuis R. Rodriguez kfree(test_fw_config); 9990a8adf58SKees Cook pr_err("could not register misc device: %d\n", rc); 10000a8adf58SKees Cook return rc; 10010a8adf58SKees Cook } 1002eb910947SBrian Norris 10030a8adf58SKees Cook pr_warn("interface ready\n"); 10040a8adf58SKees Cook 10050a8adf58SKees Cook return 0; 10060a8adf58SKees Cook } 10070a8adf58SKees Cook 10080a8adf58SKees Cook module_init(test_firmware_init); 10090a8adf58SKees Cook 10100a8adf58SKees Cook static void __exit test_firmware_exit(void) 10110a8adf58SKees Cook { 1012c92316bfSLuis R. Rodriguez mutex_lock(&test_fw_mutex); 10130a8adf58SKees Cook release_firmware(test_firmware); 10140a8adf58SKees Cook misc_deregister(&test_fw_misc_device); 1015c92316bfSLuis R. Rodriguez __test_firmware_config_free(); 1016c92316bfSLuis R. Rodriguez kfree(test_fw_config); 1017c92316bfSLuis R. Rodriguez mutex_unlock(&test_fw_mutex); 1018c92316bfSLuis R. Rodriguez 10190a8adf58SKees Cook pr_warn("removed interface\n"); 10200a8adf58SKees Cook } 10210a8adf58SKees Cook 10220a8adf58SKees Cook module_exit(test_firmware_exit); 10230a8adf58SKees Cook 10240a8adf58SKees Cook MODULE_AUTHOR("Kees Cook <[email protected]>"); 10250a8adf58SKees Cook MODULE_LICENSE("GPL"); 1026