diff options
| -rw-r--r-- | arch/x86/kernel/cpu/microcode/intel.c | 71 |
1 files changed, 36 insertions, 35 deletions
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 16936a24795c..a44bdbe7c55e 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
| 32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
| 33 | #include <linux/cpu.h> | 33 | #include <linux/cpu.h> |
| 34 | #include <linux/uio.h> | ||
| 34 | #include <linux/mm.h> | 35 | #include <linux/mm.h> |
| 35 | 36 | ||
| 36 | #include <asm/microcode_intel.h> | 37 | #include <asm/microcode_intel.h> |
| @@ -861,32 +862,33 @@ out: | |||
| 861 | return ret; | 862 | return ret; |
| 862 | } | 863 | } |
| 863 | 864 | ||
| 864 | static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | 865 | static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter) |
| 865 | int (*get_ucode_data)(void *, const void *, size_t)) | ||
| 866 | { | 866 | { |
| 867 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 867 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
| 868 | u8 *ucode_ptr = data, *new_mc = NULL, *mc = NULL; | ||
| 869 | int new_rev = uci->cpu_sig.rev; | ||
| 870 | unsigned int leftover = size; | ||
| 871 | unsigned int curr_mc_size = 0, new_mc_size = 0; | 868 | unsigned int curr_mc_size = 0, new_mc_size = 0; |
| 872 | unsigned int csig, cpf; | ||
| 873 | enum ucode_state ret = UCODE_OK; | 869 | enum ucode_state ret = UCODE_OK; |
| 870 | int new_rev = uci->cpu_sig.rev; | ||
| 871 | u8 *new_mc = NULL, *mc = NULL; | ||
| 872 | unsigned int csig, cpf; | ||
| 874 | 873 | ||
| 875 | while (leftover) { | 874 | while (iov_iter_count(iter)) { |
| 876 | struct microcode_header_intel mc_header; | 875 | struct microcode_header_intel mc_header; |
| 877 | unsigned int mc_size; | 876 | unsigned int mc_size, data_size; |
| 877 | u8 *data; | ||
| 878 | 878 | ||
| 879 | if (leftover < sizeof(mc_header)) { | 879 | if (!copy_from_iter_full(&mc_header, sizeof(mc_header), iter)) { |
| 880 | pr_err("error! Truncated header in microcode data file\n"); | 880 | pr_err("error! Truncated or inaccessible header in microcode data file\n"); |
| 881 | break; | 881 | break; |
| 882 | } | 882 | } |
| 883 | 883 | ||
| 884 | if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header))) | ||
| 885 | break; | ||
| 886 | |||
| 887 | mc_size = get_totalsize(&mc_header); | 884 | mc_size = get_totalsize(&mc_header); |
| 888 | if (!mc_size || mc_size > leftover) { | 885 | if (mc_size < sizeof(mc_header)) { |
| 889 | pr_err("error! Bad data in microcode data file\n"); | 886 | pr_err("error! Bad data in microcode data file (totalsize too small)\n"); |
| 887 | break; | ||
| 888 | } | ||
| 889 | data_size = mc_size - sizeof(mc_header); | ||
| 890 | if (data_size > iov_iter_count(iter)) { | ||
| 891 | pr_err("error! Bad data in microcode data file (truncated file?)\n"); | ||
| 890 | break; | 892 | break; |
| 891 | } | 893 | } |
| 892 | 894 | ||
| @@ -899,7 +901,9 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | |||
| 899 | curr_mc_size = mc_size; | 901 | curr_mc_size = mc_size; |
| 900 | } | 902 | } |
| 901 | 903 | ||
| 902 | if (get_ucode_data(mc, ucode_ptr, mc_size) || | 904 | memcpy(mc, &mc_header, sizeof(mc_header)); |
| 905 | data = mc + sizeof(mc_header); | ||
| 906 | if (!copy_from_iter_full(data, data_size, iter) || | ||
| 903 | microcode_sanity_check(mc, 1) < 0) { | 907 | microcode_sanity_check(mc, 1) < 0) { |
| 904 | break; | 908 | break; |
| 905 | } | 909 | } |
| @@ -914,14 +918,11 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | |||
| 914 | mc = NULL; /* trigger new vmalloc */ | 918 | mc = NULL; /* trigger new vmalloc */ |
| 915 | ret = UCODE_NEW; | 919 | ret = UCODE_NEW; |
| 916 | } | 920 | } |
| 917 | |||
| 918 | ucode_ptr += mc_size; | ||
| 919 | leftover -= mc_size; | ||
| 920 | } | 921 | } |
| 921 | 922 | ||
| 922 | vfree(mc); | 923 | vfree(mc); |
| 923 | 924 | ||
| 924 | if (leftover) { | 925 | if (iov_iter_count(iter)) { |
| 925 | vfree(new_mc); | 926 | vfree(new_mc); |
| 926 | return UCODE_ERROR; | 927 | return UCODE_ERROR; |
| 927 | } | 928 | } |
| @@ -945,12 +946,6 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | |||
| 945 | return ret; | 946 | return ret; |
| 946 | } | 947 | } |
| 947 | 948 | ||
| 948 | static int get_ucode_fw(void *to, const void *from, size_t n) | ||
| 949 | { | ||
| 950 | memcpy(to, from, n); | ||
| 951 | return 0; | ||
| 952 | } | ||
| 953 | |||
| 954 | static bool is_blacklisted(unsigned int cpu) | 949 | static bool is_blacklisted(unsigned int cpu) |
| 955 | { | 950 | { |
| 956 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 951 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
| @@ -977,10 +972,12 @@ static bool is_blacklisted(unsigned int cpu) | |||
| 977 | static enum ucode_state request_microcode_fw(int cpu, struct device *device, | 972 | static enum ucode_state request_microcode_fw(int cpu, struct device *device, |
| 978 | bool refresh_fw) | 973 | bool refresh_fw) |
| 979 | { | 974 | { |
| 980 | char name[30]; | ||
| 981 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 975 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
| 982 | const struct firmware *firmware; | 976 | const struct firmware *firmware; |
| 977 | struct iov_iter iter; | ||
| 983 | enum ucode_state ret; | 978 | enum ucode_state ret; |
| 979 | struct kvec kvec; | ||
| 980 | char name[30]; | ||
| 984 | 981 | ||
| 985 | if (is_blacklisted(cpu)) | 982 | if (is_blacklisted(cpu)) |
| 986 | return UCODE_NFOUND; | 983 | return UCODE_NFOUND; |
| @@ -993,26 +990,30 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device, | |||
| 993 | return UCODE_NFOUND; | 990 | return UCODE_NFOUND; |
| 994 | } | 991 | } |
| 995 | 992 | ||
| 996 | ret = generic_load_microcode(cpu, (void *)firmware->data, | 993 | kvec.iov_base = (void *)firmware->data; |
| 997 | firmware->size, &get_ucode_fw); | 994 | kvec.iov_len = firmware->size; |
| 995 | iov_iter_kvec(&iter, WRITE, &kvec, 1, firmware->size); | ||
| 996 | ret = generic_load_microcode(cpu, &iter); | ||
| 998 | 997 | ||
| 999 | release_firmware(firmware); | 998 | release_firmware(firmware); |
| 1000 | 999 | ||
| 1001 | return ret; | 1000 | return ret; |
| 1002 | } | 1001 | } |
| 1003 | 1002 | ||
| 1004 | static int get_ucode_user(void *to, const void *from, size_t n) | ||
| 1005 | { | ||
| 1006 | return copy_from_user(to, from, n); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | static enum ucode_state | 1003 | static enum ucode_state |
| 1010 | request_microcode_user(int cpu, const void __user *buf, size_t size) | 1004 | request_microcode_user(int cpu, const void __user *buf, size_t size) |
| 1011 | { | 1005 | { |
| 1006 | struct iov_iter iter; | ||
| 1007 | struct iovec iov; | ||
| 1008 | |||
| 1012 | if (is_blacklisted(cpu)) | 1009 | if (is_blacklisted(cpu)) |
| 1013 | return UCODE_NFOUND; | 1010 | return UCODE_NFOUND; |
| 1014 | 1011 | ||
| 1015 | return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); | 1012 | iov.iov_base = (void __user *)buf; |
| 1013 | iov.iov_len = size; | ||
| 1014 | iov_iter_init(&iter, WRITE, &iov, 1, size); | ||
| 1015 | |||
| 1016 | return generic_load_microcode(cpu, &iter); | ||
| 1016 | } | 1017 | } |
| 1017 | 1018 | ||
| 1018 | static struct microcode_ops microcode_intel_ops = { | 1019 | static struct microcode_ops microcode_intel_ops = { |
