.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Intel CPU Microcode Update Driver for Linux |
---|
3 | 4 | * |
---|
.. | .. |
---|
8 | 9 | * |
---|
9 | 10 | * Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com> |
---|
10 | 11 | * H Peter Anvin" <hpa@zytor.com> |
---|
11 | | - * |
---|
12 | | - * This program is free software; you can redistribute it and/or |
---|
13 | | - * modify it under the terms of the GNU General Public License |
---|
14 | | - * as published by the Free Software Foundation; either version |
---|
15 | | - * 2 of the License, or (at your option) any later version. |
---|
16 | 12 | */ |
---|
17 | 13 | |
---|
18 | 14 | /* |
---|
.. | .. |
---|
31 | 27 | #include <linux/kernel.h> |
---|
32 | 28 | #include <linux/slab.h> |
---|
33 | 29 | #include <linux/cpu.h> |
---|
| 30 | +#include <linux/uio.h> |
---|
34 | 31 | #include <linux/mm.h> |
---|
35 | 32 | |
---|
36 | 33 | #include <asm/microcode_intel.h> |
---|
.. | .. |
---|
662 | 659 | else |
---|
663 | 660 | iup = &intel_ucode_patch; |
---|
664 | 661 | |
---|
665 | | -reget: |
---|
666 | 662 | if (!*iup) { |
---|
667 | 663 | patch = __load_ucode_intel(&uci); |
---|
668 | 664 | if (!patch) |
---|
.. | .. |
---|
673 | 669 | |
---|
674 | 670 | uci.mc = *iup; |
---|
675 | 671 | |
---|
676 | | - if (apply_microcode_early(&uci, true)) { |
---|
677 | | - /* Mixed-silicon system? Try to refetch the proper patch: */ |
---|
678 | | - *iup = NULL; |
---|
679 | | - |
---|
680 | | - goto reget; |
---|
681 | | - } |
---|
| 672 | + apply_microcode_early(&uci, true); |
---|
682 | 673 | } |
---|
683 | 674 | |
---|
684 | 675 | static struct microcode_intel *find_patch(struct ucode_cpu_info *uci) |
---|
.. | .. |
---|
751 | 742 | { |
---|
752 | 743 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
---|
753 | 744 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
---|
| 745 | + bool bsp = c->cpu_index == boot_cpu_data.cpu_index; |
---|
754 | 746 | struct microcode_intel *mc; |
---|
755 | 747 | enum ucode_state ret; |
---|
756 | 748 | static int prev_rev; |
---|
.. | .. |
---|
796 | 788 | return UCODE_ERROR; |
---|
797 | 789 | } |
---|
798 | 790 | |
---|
799 | | - if (rev != prev_rev) { |
---|
| 791 | + if (bsp && rev != prev_rev) { |
---|
800 | 792 | pr_info("updated to revision 0x%x, date = %04x-%02x-%02x\n", |
---|
801 | 793 | rev, |
---|
802 | 794 | mc->hdr.date & 0xffff, |
---|
.. | .. |
---|
812 | 804 | c->microcode = rev; |
---|
813 | 805 | |
---|
814 | 806 | /* Update boot_cpu_data's revision too, if we're on the BSP: */ |
---|
815 | | - if (c->cpu_index == boot_cpu_data.cpu_index) |
---|
| 807 | + if (bsp) |
---|
816 | 808 | boot_cpu_data.microcode = rev; |
---|
817 | 809 | |
---|
818 | 810 | return ret; |
---|
819 | 811 | } |
---|
820 | 812 | |
---|
821 | | -static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, |
---|
822 | | - int (*get_ucode_data)(void *, const void *, size_t)) |
---|
| 813 | +static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter) |
---|
823 | 814 | { |
---|
824 | 815 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
---|
825 | | - u8 *ucode_ptr = data, *new_mc = NULL, *mc = NULL; |
---|
826 | | - int new_rev = uci->cpu_sig.rev; |
---|
827 | | - unsigned int leftover = size; |
---|
828 | 816 | unsigned int curr_mc_size = 0, new_mc_size = 0; |
---|
829 | | - unsigned int csig, cpf; |
---|
830 | 817 | enum ucode_state ret = UCODE_OK; |
---|
| 818 | + int new_rev = uci->cpu_sig.rev; |
---|
| 819 | + u8 *new_mc = NULL, *mc = NULL; |
---|
| 820 | + unsigned int csig, cpf; |
---|
831 | 821 | |
---|
832 | | - while (leftover) { |
---|
| 822 | + while (iov_iter_count(iter)) { |
---|
833 | 823 | struct microcode_header_intel mc_header; |
---|
834 | | - unsigned int mc_size; |
---|
| 824 | + unsigned int mc_size, data_size; |
---|
| 825 | + u8 *data; |
---|
835 | 826 | |
---|
836 | | - if (leftover < sizeof(mc_header)) { |
---|
837 | | - pr_err("error! Truncated header in microcode data file\n"); |
---|
| 827 | + if (!copy_from_iter_full(&mc_header, sizeof(mc_header), iter)) { |
---|
| 828 | + pr_err("error! Truncated or inaccessible header in microcode data file\n"); |
---|
838 | 829 | break; |
---|
839 | 830 | } |
---|
840 | 831 | |
---|
841 | | - if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header))) |
---|
842 | | - break; |
---|
843 | | - |
---|
844 | 832 | mc_size = get_totalsize(&mc_header); |
---|
845 | | - if (!mc_size || mc_size > leftover) { |
---|
846 | | - pr_err("error! Bad data in microcode data file\n"); |
---|
| 833 | + if (mc_size < sizeof(mc_header)) { |
---|
| 834 | + pr_err("error! Bad data in microcode data file (totalsize too small)\n"); |
---|
| 835 | + break; |
---|
| 836 | + } |
---|
| 837 | + data_size = mc_size - sizeof(mc_header); |
---|
| 838 | + if (data_size > iov_iter_count(iter)) { |
---|
| 839 | + pr_err("error! Bad data in microcode data file (truncated file?)\n"); |
---|
847 | 840 | break; |
---|
848 | 841 | } |
---|
849 | 842 | |
---|
.. | .. |
---|
856 | 849 | curr_mc_size = mc_size; |
---|
857 | 850 | } |
---|
858 | 851 | |
---|
859 | | - if (get_ucode_data(mc, ucode_ptr, mc_size) || |
---|
| 852 | + memcpy(mc, &mc_header, sizeof(mc_header)); |
---|
| 853 | + data = mc + sizeof(mc_header); |
---|
| 854 | + if (!copy_from_iter_full(data, data_size, iter) || |
---|
860 | 855 | microcode_sanity_check(mc, 1) < 0) { |
---|
861 | 856 | break; |
---|
862 | 857 | } |
---|
.. | .. |
---|
871 | 866 | mc = NULL; /* trigger new vmalloc */ |
---|
872 | 867 | ret = UCODE_NEW; |
---|
873 | 868 | } |
---|
874 | | - |
---|
875 | | - ucode_ptr += mc_size; |
---|
876 | | - leftover -= mc_size; |
---|
877 | 869 | } |
---|
878 | 870 | |
---|
879 | 871 | vfree(mc); |
---|
880 | 872 | |
---|
881 | | - if (leftover) { |
---|
| 873 | + if (iov_iter_count(iter)) { |
---|
882 | 874 | vfree(new_mc); |
---|
883 | 875 | return UCODE_ERROR; |
---|
884 | 876 | } |
---|
.. | .. |
---|
900 | 892 | cpu, new_rev, uci->cpu_sig.rev); |
---|
901 | 893 | |
---|
902 | 894 | return ret; |
---|
903 | | -} |
---|
904 | | - |
---|
905 | | -static int get_ucode_fw(void *to, const void *from, size_t n) |
---|
906 | | -{ |
---|
907 | | - memcpy(to, from, n); |
---|
908 | | - return 0; |
---|
909 | 895 | } |
---|
910 | 896 | |
---|
911 | 897 | static bool is_blacklisted(unsigned int cpu) |
---|
.. | .. |
---|
934 | 920 | static enum ucode_state request_microcode_fw(int cpu, struct device *device, |
---|
935 | 921 | bool refresh_fw) |
---|
936 | 922 | { |
---|
937 | | - char name[30]; |
---|
938 | 923 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
---|
939 | 924 | const struct firmware *firmware; |
---|
| 925 | + struct iov_iter iter; |
---|
940 | 926 | enum ucode_state ret; |
---|
| 927 | + struct kvec kvec; |
---|
| 928 | + char name[30]; |
---|
941 | 929 | |
---|
942 | 930 | if (is_blacklisted(cpu)) |
---|
943 | 931 | return UCODE_NFOUND; |
---|
.. | .. |
---|
950 | 938 | return UCODE_NFOUND; |
---|
951 | 939 | } |
---|
952 | 940 | |
---|
953 | | - ret = generic_load_microcode(cpu, (void *)firmware->data, |
---|
954 | | - firmware->size, &get_ucode_fw); |
---|
| 941 | + kvec.iov_base = (void *)firmware->data; |
---|
| 942 | + kvec.iov_len = firmware->size; |
---|
| 943 | + iov_iter_kvec(&iter, WRITE, &kvec, 1, firmware->size); |
---|
| 944 | + ret = generic_load_microcode(cpu, &iter); |
---|
955 | 945 | |
---|
956 | 946 | release_firmware(firmware); |
---|
957 | 947 | |
---|
958 | 948 | return ret; |
---|
959 | 949 | } |
---|
960 | 950 | |
---|
961 | | -static int get_ucode_user(void *to, const void *from, size_t n) |
---|
962 | | -{ |
---|
963 | | - return copy_from_user(to, from, n); |
---|
964 | | -} |
---|
965 | | - |
---|
966 | 951 | static enum ucode_state |
---|
967 | 952 | request_microcode_user(int cpu, const void __user *buf, size_t size) |
---|
968 | 953 | { |
---|
| 954 | + struct iov_iter iter; |
---|
| 955 | + struct iovec iov; |
---|
| 956 | + |
---|
969 | 957 | if (is_blacklisted(cpu)) |
---|
970 | 958 | return UCODE_NFOUND; |
---|
971 | 959 | |
---|
972 | | - return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); |
---|
| 960 | + iov.iov_base = (void __user *)buf; |
---|
| 961 | + iov.iov_len = size; |
---|
| 962 | + iov_iter_init(&iter, WRITE, &iov, 1, size); |
---|
| 963 | + |
---|
| 964 | + return generic_load_microcode(cpu, &iter); |
---|
973 | 965 | } |
---|
974 | 966 | |
---|
975 | 967 | static struct microcode_ops microcode_intel_ops = { |
---|