| .. | .. |
|---|
| 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 = { |
|---|