Commit 9cf8d636 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86-microcode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 microcode updates from Ingo Molnar:
 "The biggest change in this cycle was the separation of the microcode
  loading mechanism from the initrd code plus the support of built-in
  microcode images.

  There were also lots cleanups and general restructuring (by Borislav
  Petkov)"

* 'x86-microcode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (24 commits)
  x86/microcode/intel: Drop orig_sum from ext signature checksum
  x86/microcode/intel: Improve microcode sanity-checking error messages
  x86/microcode/intel: Merge two consecutive if-statements
  x86/microcode/intel: Get rid of DWSIZE
  x86/microcode/intel: Change checksum variables to u32
  x86/microcode: Use kmemdup() rather than duplicating its implementation
  x86/microcode: Remove unnecessary paravirt_enabled check
  x86/microcode: Document builtin microcode loading method
  x86/microcode/AMD: Issue microcode updated message later
  x86/microcode/intel: Cleanup get_matching_model_microcode()
  x86/microcode/intel: Remove unused arg of get_matching_model_microcode()
  x86/microcode/intel: Rename mc_saved_in_initrd
  x86/microcode/intel: Use *wrmsrl variants
  x86/microcode/intel: Cleanup apply_microcode_intel()
  x86/microcode/intel: Move the BUG_ON up and turn it into WARN_ON
  x86/microcode/intel: Rename mc_intel variable to mc
  x86/microcode/intel: Rename mc_saved_count to num_saved
  x86/microcode/intel: Rename local variables of type struct mc_saved_data
  x86/microcode/AMD: Drop redundant printk prefix
  x86/microcode: Issue update message only once
  ...
parents ecc026bf 4ace2e7a
...@@ -40,3 +40,28 @@ cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin (or AuthenticAMD.bin) ...@@ -40,3 +40,28 @@ cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin (or AuthenticAMD.bin)
find . | cpio -o -H newc >../ucode.cpio find . | cpio -o -H newc >../ucode.cpio
cd .. cd ..
cat ucode.cpio /boot/initrd-3.5.0.img >/boot/initrd-3.5.0.ucode.img cat ucode.cpio /boot/initrd-3.5.0.img >/boot/initrd-3.5.0.ucode.img
Builtin microcode
=================
We can also load builtin microcode supplied through the regular firmware
builtin method CONFIG_FIRMWARE_IN_KERNEL. Here's an example:
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3a-09 amd-ucode/microcode_amd_fam15h.bin"
CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"
This basically means, you have the following tree structure locally:
/lib/firmware/
|-- amd-ucode
...
| |-- microcode_amd_fam15h.bin
...
|-- intel-ucode
...
| |-- 06-3a-09
...
so that the build system can find those files and integrate them into
the final kernel image. The early loader finds them and applies them.
...@@ -1163,22 +1163,23 @@ config MICROCODE ...@@ -1163,22 +1163,23 @@ config MICROCODE
bool "CPU microcode loading support" bool "CPU microcode loading support"
default y default y
depends on CPU_SUP_AMD || CPU_SUP_INTEL depends on CPU_SUP_AMD || CPU_SUP_INTEL
depends on BLK_DEV_INITRD
select FW_LOADER select FW_LOADER
---help--- ---help---
If you say Y here, you will be able to update the microcode on If you say Y here, you will be able to update the microcode on
certain Intel and AMD processors. The Intel support is for the Intel and AMD processors. The Intel support is for the IA32 family,
IA32 family, e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4, Xeon etc. The
Xeon etc. The AMD support is for families 0x10 and later. You will AMD support is for families 0x10 and later. You will obviously need
obviously need the actual microcode binary data itself which is not the actual microcode binary data itself which is not shipped with
shipped with the Linux kernel. the Linux kernel.
This option selects the general module only, you need to select The preferred method to load microcode from a detached initrd is described
at least one vendor specific module as well. in Documentation/x86/early-microcode.txt. For that you need to enable
CONFIG_BLK_DEV_INITRD in order for the loader to be able to scan the
To compile this driver as a module, choose M here: the module initrd for microcode blobs.
will be called microcode.
In addition, you can build-in the microcode into the kernel. For that you
need to enable FIRMWARE_IN_KERNEL and add the vendor-supplied microcode
to the CONFIG_EXTRA_FIRMWARE config option.
config MICROCODE_INTEL config MICROCODE_INTEL
bool "Intel microcode loading support" bool "Intel microcode loading support"
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <asm/cpu.h> #include <asm/cpu.h>
#include <linux/earlycpio.h> #include <linux/earlycpio.h>
#include <linux/initrd.h>
#define native_rdmsr(msr, val1, val2) \ #define native_rdmsr(msr, val1, val2) \
do { \ do { \
...@@ -143,4 +144,29 @@ static inline void reload_early_microcode(void) { } ...@@ -143,4 +144,29 @@ static inline void reload_early_microcode(void) { }
static inline bool static inline bool
get_builtin_firmware(struct cpio_data *cd, const char *name) { return false; } get_builtin_firmware(struct cpio_data *cd, const char *name) { return false; }
#endif #endif
static inline unsigned long get_initrd_start(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
return initrd_start;
#else
return 0;
#endif
}
static inline unsigned long get_initrd_start_addr(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
#ifdef CONFIG_X86_32
unsigned long *initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
return (unsigned long)__pa_nodebug(*initrd_start_p);
#else
return get_initrd_start();
#endif
#else /* CONFIG_BLK_DEV_INITRD */
return 0;
#endif
}
#endif /* _ASM_X86_MICROCODE_H */ #endif /* _ASM_X86_MICROCODE_H */
...@@ -40,7 +40,6 @@ struct extended_sigtable { ...@@ -40,7 +40,6 @@ struct extended_sigtable {
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) #define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) #define EXT_HEADER_SIZE (sizeof(struct extended_sigtable))
#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) #define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature))
#define DWSIZE (sizeof(u32))
#define get_totalsize(mc) \ #define get_totalsize(mc) \
(((struct microcode_intel *)mc)->hdr.datasize ? \ (((struct microcode_intel *)mc)->hdr.datasize ? \
......
...@@ -431,10 +431,6 @@ int __init save_microcode_in_initrd_amd(void) ...@@ -431,10 +431,6 @@ int __init save_microcode_in_initrd_amd(void)
else else
container = cont_va; container = cont_va;
if (ucode_new_rev)
pr_info("microcode: updated early to new patch_level=0x%08x\n",
ucode_new_rev);
eax = cpuid_eax(0x00000001); eax = cpuid_eax(0x00000001);
eax = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); eax = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
...@@ -469,8 +465,7 @@ void reload_ucode_amd(void) ...@@ -469,8 +465,7 @@ void reload_ucode_amd(void)
if (mc && rev < mc->hdr.patch_id) { if (mc && rev < mc->hdr.patch_id) {
if (!__apply_microcode_amd(mc)) { if (!__apply_microcode_amd(mc)) {
ucode_new_rev = mc->hdr.patch_id; ucode_new_rev = mc->hdr.patch_id;
pr_info("microcode: reload patch_level=0x%08x\n", pr_info("reload patch_level=0x%08x\n", ucode_new_rev);
ucode_new_rev);
} }
} }
} }
...@@ -793,15 +788,13 @@ static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover) ...@@ -793,15 +788,13 @@ static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover)
return -EINVAL; return -EINVAL;
} }
patch->data = kzalloc(patch_size, GFP_KERNEL); patch->data = kmemdup(fw + SECTION_HDR_SIZE, patch_size, GFP_KERNEL);
if (!patch->data) { if (!patch->data) {
pr_err("Patch data allocation failure.\n"); pr_err("Patch data allocation failure.\n");
kfree(patch); kfree(patch);
return -EINVAL; return -EINVAL;
} }
/* All looks ok, copy patch... */
memcpy(patch->data, fw + SECTION_HDR_SIZE, patch_size);
INIT_LIST_HEAD(&patch->plist); INIT_LIST_HEAD(&patch->plist);
patch->patch_id = mc_hdr->patch_id; patch->patch_id = mc_hdr->patch_id;
patch->equiv_cpu = proc_id; patch->equiv_cpu = proc_id;
...@@ -957,6 +950,10 @@ struct microcode_ops * __init init_amd_microcode(void) ...@@ -957,6 +950,10 @@ struct microcode_ops * __init init_amd_microcode(void)
return NULL; return NULL;
} }
if (ucode_new_rev)
pr_info_once("microcode updated early to new patch_level=0x%08x\n",
ucode_new_rev);
return &microcode_amd_ops; return &microcode_amd_ops;
} }
......
...@@ -43,16 +43,8 @@ ...@@ -43,16 +43,8 @@
#define MICROCODE_VERSION "2.01" #define MICROCODE_VERSION "2.01"
static struct microcode_ops *microcode_ops; static struct microcode_ops *microcode_ops;
static bool dis_ucode_ldr; static bool dis_ucode_ldr;
static int __init disable_loader(char *str)
{
dis_ucode_ldr = true;
return 1;
}
__setup("dis_ucode_ldr", disable_loader);
/* /*
* Synchronization. * Synchronization.
* *
...@@ -81,15 +73,16 @@ struct cpu_info_ctx { ...@@ -81,15 +73,16 @@ struct cpu_info_ctx {
static bool __init check_loader_disabled_bsp(void) static bool __init check_loader_disabled_bsp(void)
{ {
static const char *__dis_opt_str = "dis_ucode_ldr";
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
const char *cmdline = (const char *)__pa_nodebug(boot_command_line); const char *cmdline = (const char *)__pa_nodebug(boot_command_line);
const char *opt = "dis_ucode_ldr"; const char *option = (const char *)__pa_nodebug(__dis_opt_str);
const char *option = (const char *)__pa_nodebug(opt);
bool *res = (bool *)__pa_nodebug(&dis_ucode_ldr); bool *res = (bool *)__pa_nodebug(&dis_ucode_ldr);
#else /* CONFIG_X86_64 */ #else /* CONFIG_X86_64 */
const char *cmdline = boot_command_line; const char *cmdline = boot_command_line;
const char *option = "dis_ucode_ldr"; const char *option = __dis_opt_str;
bool *res = &dis_ucode_ldr; bool *res = &dis_ucode_ldr;
#endif #endif
...@@ -479,7 +472,7 @@ static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw) ...@@ -479,7 +472,7 @@ static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw)
enum ucode_state ustate; enum ucode_state ustate;
struct ucode_cpu_info *uci = ucode_cpu_info + cpu; struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
if (uci && uci->valid) if (uci->valid)
return UCODE_OK; return UCODE_OK;
if (collect_cpu_info(cpu)) if (collect_cpu_info(cpu))
...@@ -630,7 +623,7 @@ int __init microcode_init(void) ...@@ -630,7 +623,7 @@ int __init microcode_init(void)
struct cpuinfo_x86 *c = &boot_cpu_data; struct cpuinfo_x86 *c = &boot_cpu_data;
int error; int error;
if (paravirt_enabled() || dis_ucode_ldr) if (dis_ucode_ldr)
return -EINVAL; return -EINVAL;
if (c->x86_vendor == X86_VENDOR_INTEL) if (c->x86_vendor == X86_VENDOR_INTEL)
......
This diff is collapsed.
...@@ -49,7 +49,7 @@ int microcode_sanity_check(void *mc, int print_err) ...@@ -49,7 +49,7 @@ int microcode_sanity_check(void *mc, int print_err)
unsigned long total_size, data_size, ext_table_size; unsigned long total_size, data_size, ext_table_size;
struct microcode_header_intel *mc_header = mc; struct microcode_header_intel *mc_header = mc;
struct extended_sigtable *ext_header = NULL; struct extended_sigtable *ext_header = NULL;
int sum, orig_sum, ext_sigcount = 0, i; u32 sum, orig_sum, ext_sigcount = 0, i;
struct extended_signature *ext_sig; struct extended_signature *ext_sig;
total_size = get_totalsize(mc_header); total_size = get_totalsize(mc_header);
...@@ -57,69 +57,85 @@ int microcode_sanity_check(void *mc, int print_err) ...@@ -57,69 +57,85 @@ int microcode_sanity_check(void *mc, int print_err)
if (data_size + MC_HEADER_SIZE > total_size) { if (data_size + MC_HEADER_SIZE > total_size) {
if (print_err) if (print_err)
pr_err("error! Bad data size in microcode data file\n"); pr_err("Error: bad microcode data file size.\n");
return -EINVAL; return -EINVAL;
} }
if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { if (mc_header->ldrver != 1 || mc_header->hdrver != 1) {
if (print_err) if (print_err)
pr_err("error! Unknown microcode update format\n"); pr_err("Error: invalid/unknown microcode update format.\n");
return -EINVAL; return -EINVAL;
} }
ext_table_size = total_size - (MC_HEADER_SIZE + data_size); ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
if (ext_table_size) { if (ext_table_size) {
u32 ext_table_sum = 0;
u32 *ext_tablep;
if ((ext_table_size < EXT_HEADER_SIZE) if ((ext_table_size < EXT_HEADER_SIZE)
|| ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
if (print_err) if (print_err)
pr_err("error! Small exttable size in microcode data file\n"); pr_err("Error: truncated extended signature table.\n");
return -EINVAL; return -EINVAL;
} }
ext_header = mc + MC_HEADER_SIZE + data_size; ext_header = mc + MC_HEADER_SIZE + data_size;
if (ext_table_size != exttable_size(ext_header)) { if (ext_table_size != exttable_size(ext_header)) {
if (print_err) if (print_err)
pr_err("error! Bad exttable size in microcode data file\n"); pr_err("Error: extended signature table size mismatch.\n");
return -EFAULT; return -EFAULT;
} }
ext_sigcount = ext_header->count; ext_sigcount = ext_header->count;
}
/* check extended table checksum */ /*
if (ext_table_size) { * Check extended table checksum: the sum of all dwords that
int ext_table_sum = 0; * comprise a valid table must be 0.
int *ext_tablep = (int *)ext_header; */
ext_tablep = (u32 *)ext_header;
i = ext_table_size / DWSIZE; i = ext_table_size / sizeof(u32);
while (i--) while (i--)
ext_table_sum += ext_tablep[i]; ext_table_sum += ext_tablep[i];
if (ext_table_sum) { if (ext_table_sum) {
if (print_err) if (print_err)
pr_warn("aborting, bad extended signature table checksum\n"); pr_warn("Bad extended signature table checksum, aborting.\n");
return -EINVAL; return -EINVAL;
} }
} }
/* calculate the checksum */ /*
* Calculate the checksum of update data and header. The checksum of
* valid update data and header including the extended signature table
* must be 0.
*/
orig_sum = 0; orig_sum = 0;
i = (MC_HEADER_SIZE + data_size) / DWSIZE; i = (MC_HEADER_SIZE + data_size) / sizeof(u32);
while (i--) while (i--)
orig_sum += ((int *)mc)[i]; orig_sum += ((u32 *)mc)[i];
if (orig_sum) { if (orig_sum) {
if (print_err) if (print_err)
pr_err("aborting, bad checksum\n"); pr_err("Bad microcode data checksum, aborting.\n");
return -EINVAL; return -EINVAL;
} }
if (!ext_table_size) if (!ext_table_size)
return 0; return 0;
/* check extended signature checksum */
/*
* Check extended signature checksum: 0 => valid.
*/
for (i = 0; i < ext_sigcount; i++) { for (i = 0; i < ext_sigcount; i++) {
ext_sig = (void *)ext_header + EXT_HEADER_SIZE + ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
EXT_SIGNATURE_SIZE * i; EXT_SIGNATURE_SIZE * i;
sum = orig_sum
- (mc_header->sig + mc_header->pf + mc_header->cksum) sum = (mc_header->sig + mc_header->pf + mc_header->cksum) -
+ (ext_sig->sig + ext_sig->pf + ext_sig->cksum); (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
if (sum) { if (sum) {
if (print_err) if (print_err)
pr_err("aborting, bad checksum\n"); pr_err("Bad extended signature checksum, aborting.\n");
return -EINVAL; return -EINVAL;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment