.. | .. |
---|
| 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> |
---|
.. | .. |
---|
751 | 748 | { |
---|
752 | 749 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
---|
753 | 750 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
---|
| 751 | + bool bsp = c->cpu_index == boot_cpu_data.cpu_index; |
---|
754 | 752 | struct microcode_intel *mc; |
---|
755 | 753 | enum ucode_state ret; |
---|
756 | 754 | static int prev_rev; |
---|
.. | .. |
---|
796 | 794 | return UCODE_ERROR; |
---|
797 | 795 | } |
---|
798 | 796 | |
---|
799 | | - if (rev != prev_rev) { |
---|
| 797 | + if (bsp && rev != prev_rev) { |
---|
800 | 798 | pr_info("updated to revision 0x%x, date = %04x-%02x-%02x\n", |
---|
801 | 799 | rev, |
---|
802 | 800 | mc->hdr.date & 0xffff, |
---|
.. | .. |
---|
812 | 810 | c->microcode = rev; |
---|
813 | 811 | |
---|
814 | 812 | /* Update boot_cpu_data's revision too, if we're on the BSP: */ |
---|
815 | | - if (c->cpu_index == boot_cpu_data.cpu_index) |
---|
| 813 | + if (bsp) |
---|
816 | 814 | boot_cpu_data.microcode = rev; |
---|
817 | 815 | |
---|
818 | 816 | return ret; |
---|
819 | 817 | } |
---|
820 | 818 | |
---|
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)) |
---|
| 819 | +static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter) |
---|
823 | 820 | { |
---|
824 | 821 | 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 | 822 | unsigned int curr_mc_size = 0, new_mc_size = 0; |
---|
829 | | - unsigned int csig, cpf; |
---|
830 | 823 | enum ucode_state ret = UCODE_OK; |
---|
| 824 | + int new_rev = uci->cpu_sig.rev; |
---|
| 825 | + u8 *new_mc = NULL, *mc = NULL; |
---|
| 826 | + unsigned int csig, cpf; |
---|
831 | 827 | |
---|
832 | | - while (leftover) { |
---|
| 828 | + while (iov_iter_count(iter)) { |
---|
833 | 829 | struct microcode_header_intel mc_header; |
---|
834 | | - unsigned int mc_size; |
---|
| 830 | + unsigned int mc_size, data_size; |
---|
| 831 | + u8 *data; |
---|
835 | 832 | |
---|
836 | | - if (leftover < sizeof(mc_header)) { |
---|
837 | | - pr_err("error! Truncated header in microcode data file\n"); |
---|
| 833 | + if (!copy_from_iter_full(&mc_header, sizeof(mc_header), iter)) { |
---|
| 834 | + pr_err("error! Truncated or inaccessible header in microcode data file\n"); |
---|
838 | 835 | break; |
---|
839 | 836 | } |
---|
840 | 837 | |
---|
841 | | - if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header))) |
---|
842 | | - break; |
---|
843 | | - |
---|
844 | 838 | mc_size = get_totalsize(&mc_header); |
---|
845 | | - if (!mc_size || mc_size > leftover) { |
---|
846 | | - pr_err("error! Bad data in microcode data file\n"); |
---|
| 839 | + if (mc_size < sizeof(mc_header)) { |
---|
| 840 | + pr_err("error! Bad data in microcode data file (totalsize too small)\n"); |
---|
| 841 | + break; |
---|
| 842 | + } |
---|
| 843 | + data_size = mc_size - sizeof(mc_header); |
---|
| 844 | + if (data_size > iov_iter_count(iter)) { |
---|
| 845 | + pr_err("error! Bad data in microcode data file (truncated file?)\n"); |
---|
847 | 846 | break; |
---|
848 | 847 | } |
---|
849 | 848 | |
---|
.. | .. |
---|
856 | 855 | curr_mc_size = mc_size; |
---|
857 | 856 | } |
---|
858 | 857 | |
---|
859 | | - if (get_ucode_data(mc, ucode_ptr, mc_size) || |
---|
| 858 | + memcpy(mc, &mc_header, sizeof(mc_header)); |
---|
| 859 | + data = mc + sizeof(mc_header); |
---|
| 860 | + if (!copy_from_iter_full(data, data_size, iter) || |
---|
860 | 861 | microcode_sanity_check(mc, 1) < 0) { |
---|
861 | 862 | break; |
---|
862 | 863 | } |
---|
.. | .. |
---|
871 | 872 | mc = NULL; /* trigger new vmalloc */ |
---|
872 | 873 | ret = UCODE_NEW; |
---|
873 | 874 | } |
---|
874 | | - |
---|
875 | | - ucode_ptr += mc_size; |
---|
876 | | - leftover -= mc_size; |
---|
877 | 875 | } |
---|
878 | 876 | |
---|
879 | 877 | vfree(mc); |
---|
880 | 878 | |
---|
881 | | - if (leftover) { |
---|
| 879 | + if (iov_iter_count(iter)) { |
---|
882 | 880 | vfree(new_mc); |
---|
883 | 881 | return UCODE_ERROR; |
---|
884 | 882 | } |
---|
.. | .. |
---|
900 | 898 | cpu, new_rev, uci->cpu_sig.rev); |
---|
901 | 899 | |
---|
902 | 900 | 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 | 901 | } |
---|
910 | 902 | |
---|
911 | 903 | static bool is_blacklisted(unsigned int cpu) |
---|
.. | .. |
---|
934 | 926 | static enum ucode_state request_microcode_fw(int cpu, struct device *device, |
---|
935 | 927 | bool refresh_fw) |
---|
936 | 928 | { |
---|
937 | | - char name[30]; |
---|
938 | 929 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
---|
939 | 930 | const struct firmware *firmware; |
---|
| 931 | + struct iov_iter iter; |
---|
940 | 932 | enum ucode_state ret; |
---|
| 933 | + struct kvec kvec; |
---|
| 934 | + char name[30]; |
---|
941 | 935 | |
---|
942 | 936 | if (is_blacklisted(cpu)) |
---|
943 | 937 | return UCODE_NFOUND; |
---|
.. | .. |
---|
950 | 944 | return UCODE_NFOUND; |
---|
951 | 945 | } |
---|
952 | 946 | |
---|
953 | | - ret = generic_load_microcode(cpu, (void *)firmware->data, |
---|
954 | | - firmware->size, &get_ucode_fw); |
---|
| 947 | + kvec.iov_base = (void *)firmware->data; |
---|
| 948 | + kvec.iov_len = firmware->size; |
---|
| 949 | + iov_iter_kvec(&iter, WRITE, &kvec, 1, firmware->size); |
---|
| 950 | + ret = generic_load_microcode(cpu, &iter); |
---|
955 | 951 | |
---|
956 | 952 | release_firmware(firmware); |
---|
957 | 953 | |
---|
958 | 954 | return ret; |
---|
959 | 955 | } |
---|
960 | 956 | |
---|
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 | 957 | static enum ucode_state |
---|
967 | 958 | request_microcode_user(int cpu, const void __user *buf, size_t size) |
---|
968 | 959 | { |
---|
| 960 | + struct iov_iter iter; |
---|
| 961 | + struct iovec iov; |
---|
| 962 | + |
---|
969 | 963 | if (is_blacklisted(cpu)) |
---|
970 | 964 | return UCODE_NFOUND; |
---|
971 | 965 | |
---|
972 | | - return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); |
---|
| 966 | + iov.iov_base = (void __user *)buf; |
---|
| 967 | + iov.iov_len = size; |
---|
| 968 | + iov_iter_init(&iter, WRITE, &iov, 1, size); |
---|
| 969 | + |
---|
| 970 | + return generic_load_microcode(cpu, &iter); |
---|
973 | 971 | } |
---|
974 | 972 | |
---|
975 | 973 | static struct microcode_ops microcode_intel_ops = { |
---|