| .. | .. |
|---|
| 22 | 22 | #include <linux/slab.h> |
|---|
| 23 | 23 | #include <linux/uaccess.h> |
|---|
| 24 | 24 | #include <linux/delay.h> |
|---|
| 25 | +#include <linux/kstrtox.h> |
|---|
| 25 | 26 | #include <linux/kthread.h> |
|---|
| 26 | 27 | #include <linux/vmalloc.h> |
|---|
| 27 | 28 | #include <linux/efi_embedded_fw.h> |
|---|
| .. | .. |
|---|
| 41 | 42 | bool sent; |
|---|
| 42 | 43 | const struct firmware *fw; |
|---|
| 43 | 44 | const char *name; |
|---|
| 45 | + const char *fw_buf; |
|---|
| 44 | 46 | struct completion completion; |
|---|
| 45 | 47 | struct task_struct *task; |
|---|
| 46 | 48 | struct device *dev; |
|---|
| .. | .. |
|---|
| 143 | 145 | |
|---|
| 144 | 146 | for (i = 0; i < test_fw_config->num_requests; i++) { |
|---|
| 145 | 147 | req = &test_fw_config->reqs[i]; |
|---|
| 146 | | - if (req->fw) |
|---|
| 148 | + if (req->fw) { |
|---|
| 149 | + if (req->fw_buf) { |
|---|
| 150 | + kfree_const(req->fw_buf); |
|---|
| 151 | + req->fw_buf = NULL; |
|---|
| 152 | + } |
|---|
| 147 | 153 | release_firmware(req->fw); |
|---|
| 154 | + req->fw = NULL; |
|---|
| 155 | + } |
|---|
| 148 | 156 | } |
|---|
| 149 | 157 | |
|---|
| 150 | 158 | vfree(test_fw_config->reqs); |
|---|
| .. | .. |
|---|
| 175 | 183 | { |
|---|
| 176 | 184 | *dst = kstrndup(name, count, gfp); |
|---|
| 177 | 185 | if (!*dst) |
|---|
| 178 | | - return -ENOSPC; |
|---|
| 186 | + return -ENOMEM; |
|---|
| 179 | 187 | return count; |
|---|
| 180 | 188 | } |
|---|
| 181 | 189 | |
|---|
| .. | .. |
|---|
| 313 | 321 | return len; |
|---|
| 314 | 322 | } |
|---|
| 315 | 323 | |
|---|
| 324 | +static inline int __test_dev_config_update_bool(const char *buf, size_t size, |
|---|
| 325 | + bool *cfg) |
|---|
| 326 | +{ |
|---|
| 327 | + int ret; |
|---|
| 328 | + |
|---|
| 329 | + if (kstrtobool(buf, cfg) < 0) |
|---|
| 330 | + ret = -EINVAL; |
|---|
| 331 | + else |
|---|
| 332 | + ret = size; |
|---|
| 333 | + |
|---|
| 334 | + return ret; |
|---|
| 335 | +} |
|---|
| 336 | + |
|---|
| 316 | 337 | static int test_dev_config_update_bool(const char *buf, size_t size, |
|---|
| 317 | 338 | bool *cfg) |
|---|
| 318 | 339 | { |
|---|
| 319 | 340 | int ret; |
|---|
| 320 | 341 | |
|---|
| 321 | 342 | mutex_lock(&test_fw_mutex); |
|---|
| 322 | | - if (strtobool(buf, cfg) < 0) |
|---|
| 323 | | - ret = -EINVAL; |
|---|
| 324 | | - else |
|---|
| 325 | | - ret = size; |
|---|
| 343 | + ret = __test_dev_config_update_bool(buf, size, cfg); |
|---|
| 326 | 344 | mutex_unlock(&test_fw_mutex); |
|---|
| 327 | 345 | |
|---|
| 328 | 346 | return ret; |
|---|
| .. | .. |
|---|
| 333 | 351 | return snprintf(buf, PAGE_SIZE, "%d\n", val); |
|---|
| 334 | 352 | } |
|---|
| 335 | 353 | |
|---|
| 336 | | -static int test_dev_config_update_size_t(const char *buf, |
|---|
| 354 | +static int __test_dev_config_update_size_t( |
|---|
| 355 | + const char *buf, |
|---|
| 337 | 356 | size_t size, |
|---|
| 338 | 357 | size_t *cfg) |
|---|
| 339 | 358 | { |
|---|
| .. | .. |
|---|
| 344 | 363 | if (ret) |
|---|
| 345 | 364 | return ret; |
|---|
| 346 | 365 | |
|---|
| 347 | | - mutex_lock(&test_fw_mutex); |
|---|
| 348 | 366 | *(size_t *)cfg = new; |
|---|
| 349 | | - mutex_unlock(&test_fw_mutex); |
|---|
| 350 | 367 | |
|---|
| 351 | 368 | /* Always return full write size even if we didn't consume all */ |
|---|
| 352 | 369 | return size; |
|---|
| .. | .. |
|---|
| 362 | 379 | return snprintf(buf, PAGE_SIZE, "%d\n", val); |
|---|
| 363 | 380 | } |
|---|
| 364 | 381 | |
|---|
| 365 | | -static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) |
|---|
| 382 | +static int __test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) |
|---|
| 366 | 383 | { |
|---|
| 384 | + u8 val; |
|---|
| 367 | 385 | int ret; |
|---|
| 368 | | - long new; |
|---|
| 369 | 386 | |
|---|
| 370 | | - ret = kstrtol(buf, 10, &new); |
|---|
| 387 | + ret = kstrtou8(buf, 10, &val); |
|---|
| 371 | 388 | if (ret) |
|---|
| 372 | 389 | return ret; |
|---|
| 373 | 390 | |
|---|
| 374 | | - if (new > U8_MAX) |
|---|
| 375 | | - return -EINVAL; |
|---|
| 376 | | - |
|---|
| 377 | | - mutex_lock(&test_fw_mutex); |
|---|
| 378 | | - *(u8 *)cfg = new; |
|---|
| 379 | | - mutex_unlock(&test_fw_mutex); |
|---|
| 391 | + *(u8 *)cfg = val; |
|---|
| 380 | 392 | |
|---|
| 381 | 393 | /* Always return full write size even if we didn't consume all */ |
|---|
| 382 | 394 | return size; |
|---|
| 395 | +} |
|---|
| 396 | + |
|---|
| 397 | +static int test_dev_config_update_u8(const char *buf, size_t size, u8 *cfg) |
|---|
| 398 | +{ |
|---|
| 399 | + int ret; |
|---|
| 400 | + |
|---|
| 401 | + mutex_lock(&test_fw_mutex); |
|---|
| 402 | + ret = __test_dev_config_update_u8(buf, size, cfg); |
|---|
| 403 | + mutex_unlock(&test_fw_mutex); |
|---|
| 404 | + |
|---|
| 405 | + return ret; |
|---|
| 383 | 406 | } |
|---|
| 384 | 407 | |
|---|
| 385 | 408 | static ssize_t test_dev_config_show_u8(char *buf, u8 val) |
|---|
| .. | .. |
|---|
| 408 | 431 | mutex_unlock(&test_fw_mutex); |
|---|
| 409 | 432 | goto out; |
|---|
| 410 | 433 | } |
|---|
| 411 | | - mutex_unlock(&test_fw_mutex); |
|---|
| 412 | 434 | |
|---|
| 413 | | - rc = test_dev_config_update_u8(buf, count, |
|---|
| 414 | | - &test_fw_config->num_requests); |
|---|
| 435 | + rc = __test_dev_config_update_u8(buf, count, |
|---|
| 436 | + &test_fw_config->num_requests); |
|---|
| 437 | + mutex_unlock(&test_fw_mutex); |
|---|
| 415 | 438 | |
|---|
| 416 | 439 | out: |
|---|
| 417 | 440 | return rc; |
|---|
| .. | .. |
|---|
| 455 | 478 | mutex_unlock(&test_fw_mutex); |
|---|
| 456 | 479 | goto out; |
|---|
| 457 | 480 | } |
|---|
| 458 | | - mutex_unlock(&test_fw_mutex); |
|---|
| 459 | 481 | |
|---|
| 460 | | - rc = test_dev_config_update_size_t(buf, count, |
|---|
| 461 | | - &test_fw_config->buf_size); |
|---|
| 482 | + rc = __test_dev_config_update_size_t(buf, count, |
|---|
| 483 | + &test_fw_config->buf_size); |
|---|
| 484 | + mutex_unlock(&test_fw_mutex); |
|---|
| 462 | 485 | |
|---|
| 463 | 486 | out: |
|---|
| 464 | 487 | return rc; |
|---|
| .. | .. |
|---|
| 485 | 508 | mutex_unlock(&test_fw_mutex); |
|---|
| 486 | 509 | goto out; |
|---|
| 487 | 510 | } |
|---|
| 488 | | - mutex_unlock(&test_fw_mutex); |
|---|
| 489 | 511 | |
|---|
| 490 | | - rc = test_dev_config_update_size_t(buf, count, |
|---|
| 491 | | - &test_fw_config->file_offset); |
|---|
| 512 | + rc = __test_dev_config_update_size_t(buf, count, |
|---|
| 513 | + &test_fw_config->file_offset); |
|---|
| 514 | + mutex_unlock(&test_fw_mutex); |
|---|
| 492 | 515 | |
|---|
| 493 | 516 | out: |
|---|
| 494 | 517 | return rc; |
|---|
| .. | .. |
|---|
| 583 | 606 | |
|---|
| 584 | 607 | name = kstrndup(buf, count, GFP_KERNEL); |
|---|
| 585 | 608 | if (!name) |
|---|
| 586 | | - return -ENOSPC; |
|---|
| 609 | + return -ENOMEM; |
|---|
| 587 | 610 | |
|---|
| 588 | 611 | pr_info("loading '%s'\n", name); |
|---|
| 589 | 612 | |
|---|
| 590 | 613 | mutex_lock(&test_fw_mutex); |
|---|
| 591 | 614 | release_firmware(test_firmware); |
|---|
| 615 | + if (test_fw_config->reqs) |
|---|
| 616 | + __test_release_all_firmware(); |
|---|
| 592 | 617 | test_firmware = NULL; |
|---|
| 593 | 618 | rc = request_firmware(&test_firmware, name, dev); |
|---|
| 594 | 619 | if (rc) { |
|---|
| .. | .. |
|---|
| 629 | 654 | |
|---|
| 630 | 655 | name = kstrndup(buf, count, GFP_KERNEL); |
|---|
| 631 | 656 | if (!name) |
|---|
| 632 | | - return -ENOSPC; |
|---|
| 657 | + return -ENOMEM; |
|---|
| 633 | 658 | |
|---|
| 634 | 659 | pr_info("inserting test platform fw '%s'\n", name); |
|---|
| 635 | 660 | efi_embedded_fw.name = name; |
|---|
| .. | .. |
|---|
| 682 | 707 | |
|---|
| 683 | 708 | name = kstrndup(buf, count, GFP_KERNEL); |
|---|
| 684 | 709 | if (!name) |
|---|
| 685 | | - return -ENOSPC; |
|---|
| 710 | + return -ENOMEM; |
|---|
| 686 | 711 | |
|---|
| 687 | 712 | pr_info("loading '%s'\n", name); |
|---|
| 688 | 713 | |
|---|
| 689 | 714 | mutex_lock(&test_fw_mutex); |
|---|
| 690 | 715 | release_firmware(test_firmware); |
|---|
| 691 | 716 | test_firmware = NULL; |
|---|
| 717 | + if (test_fw_config->reqs) |
|---|
| 718 | + __test_release_all_firmware(); |
|---|
| 692 | 719 | rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, |
|---|
| 693 | 720 | NULL, trigger_async_request_cb); |
|---|
| 694 | 721 | if (rc) { |
|---|
| .. | .. |
|---|
| 725 | 752 | |
|---|
| 726 | 753 | name = kstrndup(buf, count, GFP_KERNEL); |
|---|
| 727 | 754 | if (!name) |
|---|
| 728 | | - return -ENOSPC; |
|---|
| 755 | + return -ENOMEM; |
|---|
| 729 | 756 | |
|---|
| 730 | 757 | pr_info("loading '%s' using custom fallback mechanism\n", name); |
|---|
| 731 | 758 | |
|---|
| 732 | 759 | mutex_lock(&test_fw_mutex); |
|---|
| 733 | 760 | release_firmware(test_firmware); |
|---|
| 761 | + if (test_fw_config->reqs) |
|---|
| 762 | + __test_release_all_firmware(); |
|---|
| 734 | 763 | test_firmware = NULL; |
|---|
| 735 | 764 | rc = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, name, |
|---|
| 736 | 765 | dev, GFP_KERNEL, NULL, |
|---|
| .. | .. |
|---|
| 774 | 803 | |
|---|
| 775 | 804 | test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); |
|---|
| 776 | 805 | if (!test_buf) |
|---|
| 777 | | - return -ENOSPC; |
|---|
| 806 | + return -ENOMEM; |
|---|
| 778 | 807 | |
|---|
| 779 | 808 | if (test_fw_config->partial) |
|---|
| 780 | 809 | req->rc = request_partial_firmware_into_buf |
|---|
| .. | .. |
|---|
| 793 | 822 | test_fw_config->buf_size); |
|---|
| 794 | 823 | if (!req->fw) |
|---|
| 795 | 824 | kfree(test_buf); |
|---|
| 825 | + else |
|---|
| 826 | + req->fw_buf = test_buf; |
|---|
| 796 | 827 | } else { |
|---|
| 797 | 828 | req->rc = test_fw_config->req_firmware(&req->fw, |
|---|
| 798 | 829 | req->name, |
|---|
| .. | .. |
|---|
| 832 | 863 | |
|---|
| 833 | 864 | mutex_lock(&test_fw_mutex); |
|---|
| 834 | 865 | |
|---|
| 866 | + if (test_fw_config->reqs) { |
|---|
| 867 | + rc = -EBUSY; |
|---|
| 868 | + goto out_bail; |
|---|
| 869 | + } |
|---|
| 870 | + |
|---|
| 835 | 871 | test_fw_config->reqs = |
|---|
| 836 | 872 | vzalloc(array3_size(sizeof(struct test_batched_req), |
|---|
| 837 | 873 | test_fw_config->num_requests, 2)); |
|---|
| .. | .. |
|---|
| 848 | 884 | req->fw = NULL; |
|---|
| 849 | 885 | req->idx = i; |
|---|
| 850 | 886 | req->name = test_fw_config->name; |
|---|
| 887 | + req->fw_buf = NULL; |
|---|
| 851 | 888 | req->dev = dev; |
|---|
| 852 | 889 | init_completion(&req->completion); |
|---|
| 853 | 890 | req->task = kthread_run(test_fw_run_batch_request, req, |
|---|
| .. | .. |
|---|
| 930 | 967 | |
|---|
| 931 | 968 | mutex_lock(&test_fw_mutex); |
|---|
| 932 | 969 | |
|---|
| 970 | + if (test_fw_config->reqs) { |
|---|
| 971 | + rc = -EBUSY; |
|---|
| 972 | + goto out_bail; |
|---|
| 973 | + } |
|---|
| 974 | + |
|---|
| 933 | 975 | test_fw_config->reqs = |
|---|
| 934 | 976 | vzalloc(array3_size(sizeof(struct test_batched_req), |
|---|
| 935 | 977 | test_fw_config->num_requests, 2)); |
|---|
| .. | .. |
|---|
| 947 | 989 | for (i = 0; i < test_fw_config->num_requests; i++) { |
|---|
| 948 | 990 | req = &test_fw_config->reqs[i]; |
|---|
| 949 | 991 | req->name = test_fw_config->name; |
|---|
| 992 | + req->fw_buf = NULL; |
|---|
| 950 | 993 | req->fw = NULL; |
|---|
| 951 | 994 | req->idx = i; |
|---|
| 952 | 995 | init_completion(&req->completion); |
|---|
| .. | .. |
|---|
| 1114 | 1157 | |
|---|
| 1115 | 1158 | rc = misc_register(&test_fw_misc_device); |
|---|
| 1116 | 1159 | if (rc) { |
|---|
| 1160 | + __test_firmware_config_free(); |
|---|
| 1117 | 1161 | kfree(test_fw_config); |
|---|
| 1118 | 1162 | pr_err("could not register misc device: %d\n", rc); |
|---|
| 1119 | 1163 | return rc; |
|---|