|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen master] x86/ucode: Move microcode into its own directory
commit 9872d99215ba9234b927d2c55625b70b4f555c60
Author: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
AuthorDate: Wed Mar 18 20:02:34 2020 +0000
Commit: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Fri Mar 20 18:42:24 2020 +0000
x86/ucode: Move microcode into its own directory
Split the existing asm/microcode.h in half, keeping the per-cpu cpu_sig
available to external users, and moving everything else into private.h
Take the opportunity to trim and clean up the include lists for all 3 source
files, all of which include rather more than necessary.
No functional change.
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Acked-by: Jan Beulich <jbeulich@xxxxxxxx>
---
xen/arch/x86/Makefile | 3 -
xen/arch/x86/cpu/Makefile | 1 +
xen/arch/x86/cpu/microcode/Makefile | 3 +
xen/arch/x86/cpu/microcode/amd.c | 622 ++++++++++++++++++++++++++
xen/arch/x86/cpu/microcode/core.c | 820 ++++++++++++++++++++++++++++++++++
xen/arch/x86/cpu/microcode/intel.c | 422 ++++++++++++++++++
xen/arch/x86/cpu/microcode/private.h | 37 ++
xen/arch/x86/microcode.c | 827 -----------------------------------
xen/arch/x86/microcode_amd.c | 624 --------------------------
xen/arch/x86/microcode_intel.c | 425 ------------------
xen/include/asm-x86/microcode.h | 30 --
11 files changed, 1905 insertions(+), 1909 deletions(-)
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index ed709e2373..e954edbc2e 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -43,9 +43,6 @@ obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o
obj-y += ioport_emulate.o
obj-y += irq.o
obj-$(CONFIG_KEXEC) += machine_kexec.o
-obj-y += microcode_amd.o
-obj-y += microcode_intel.o
-obj-y += microcode.o
obj-y += mm.o x86_64/mm.o
obj-$(CONFIG_HVM) += monitor.o
obj-y += mpparse.o
diff --git a/xen/arch/x86/cpu/Makefile b/xen/arch/x86/cpu/Makefile
index de983006a1..35561fe51d 100644
--- a/xen/arch/x86/cpu/Makefile
+++ b/xen/arch/x86/cpu/Makefile
@@ -1,4 +1,5 @@
obj-y += mcheck/
+obj-y += microcode/
obj-y += mtrr/
obj-y += amd.o
diff --git a/xen/arch/x86/cpu/microcode/Makefile
b/xen/arch/x86/cpu/microcode/Makefile
new file mode 100644
index 0000000000..aae235245b
--- /dev/null
+++ b/xen/arch/x86/cpu/microcode/Makefile
@@ -0,0 +1,3 @@
+obj-y += amd.o
+obj-y += core.o
+obj-y += intel.o
diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c
new file mode 100644
index 0000000000..9028889813
--- /dev/null
+++ b/xen/arch/x86/cpu/microcode/amd.c
@@ -0,0 +1,622 @@
+/*
+ * AMD CPU Microcode Update Driver for Linux
+ * Copyright (C) 2008 Advanced Micro Devices Inc.
+ *
+ * Author: Peter Oruba <peter.oruba@xxxxxxx>
+ *
+ * Based on work by:
+ * Tigran Aivazian <tigran@xxxxxxxxxxxxxxxxxxxx>
+ *
+ * This driver allows to upgrade microcode on AMD
+ * family 0x10 and later.
+ *
+ * Licensed unter the terms of the GNU General Public
+ * License version 2. See file COPYING for details.
+ */
+
+#include <xen/err.h>
+#include <xen/init.h>
+#include <xen/mm.h> /* TODO: Fix asm/tlbflush.h breakage */
+
+#include <asm/hvm/svm/svm.h>
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+
+#include "private.h"
+
+#define pr_debug(x...) ((void)0)
+
+#define CONT_HDR_SIZE 12
+#define SECTION_HDR_SIZE 8
+#define PATCH_HDR_SIZE 32
+
+struct __packed equiv_cpu_entry {
+ uint32_t installed_cpu;
+ uint32_t fixed_errata_mask;
+ uint32_t fixed_errata_compare;
+ uint16_t equiv_cpu;
+ uint16_t reserved;
+};
+
+struct __packed microcode_header_amd {
+ uint32_t data_code;
+ uint32_t patch_id;
+ uint8_t mc_patch_data_id[2];
+ uint8_t mc_patch_data_len;
+ uint8_t init_flag;
+ uint32_t mc_patch_data_checksum;
+ uint32_t nb_dev_id;
+ uint32_t sb_dev_id;
+ uint16_t processor_rev_id;
+ uint8_t nb_rev_id;
+ uint8_t sb_rev_id;
+ uint8_t bios_api_rev;
+ uint8_t reserved1[3];
+ uint32_t match_reg[8];
+};
+
+#define UCODE_MAGIC 0x00414d44
+#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
+#define UCODE_UCODE_TYPE 0x00000001
+
+struct microcode_amd {
+ void *mpb;
+ size_t mpb_size;
+ struct equiv_cpu_entry *equiv_cpu_table;
+ size_t equiv_cpu_table_size;
+};
+
+struct mpbhdr {
+ uint32_t type;
+ uint32_t len;
+ uint8_t data[];
+};
+
+/* See comment in start_update() for cases when this routine fails */
+static int collect_cpu_info(struct cpu_signature *csig)
+{
+ unsigned int cpu = smp_processor_id();
+ struct cpuinfo_x86 *c = &cpu_data[cpu];
+
+ memset(csig, 0, sizeof(*csig));
+
+ if ( (c->x86_vendor != X86_VENDOR_AMD) || (c->x86 < 0x10) )
+ {
+ printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
+ cpu);
+ return -EINVAL;
+ }
+
+ rdmsrl(MSR_AMD_PATCHLEVEL, csig->rev);
+
+ pr_debug("microcode: CPU%d collect_cpu_info: patch_id=%#x\n",
+ cpu, csig->rev);
+
+ return 0;
+}
+
+static bool_t verify_patch_size(uint32_t patch_size)
+{
+ uint32_t max_size;
+
+#define F1XH_MPB_MAX_SIZE 2048
+#define F14H_MPB_MAX_SIZE 1824
+#define F15H_MPB_MAX_SIZE 4096
+#define F16H_MPB_MAX_SIZE 3458
+#define F17H_MPB_MAX_SIZE 3200
+
+ switch (boot_cpu_data.x86)
+ {
+ case 0x14:
+ max_size = F14H_MPB_MAX_SIZE;
+ break;
+ case 0x15:
+ max_size = F15H_MPB_MAX_SIZE;
+ break;
+ case 0x16:
+ max_size = F16H_MPB_MAX_SIZE;
+ break;
+ case 0x17:
+ max_size = F17H_MPB_MAX_SIZE;
+ break;
+ default:
+ max_size = F1XH_MPB_MAX_SIZE;
+ break;
+ }
+
+ return (patch_size <= max_size);
+}
+
+static bool_t find_equiv_cpu_id(const struct equiv_cpu_entry *equiv_cpu_table,
+ unsigned int current_cpu_id,
+ unsigned int *equiv_cpu_id)
+{
+ unsigned int i;
+
+ if ( !equiv_cpu_table )
+ return 0;
+
+ for ( i = 0; equiv_cpu_table[i].installed_cpu != 0; i++ )
+ {
+ if ( current_cpu_id == equiv_cpu_table[i].installed_cpu )
+ {
+ *equiv_cpu_id = equiv_cpu_table[i].equiv_cpu & 0xffff;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static enum microcode_match_result microcode_fits(
+ const struct microcode_amd *mc_amd)
+{
+ unsigned int cpu = smp_processor_id();
+ const struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
+ const struct microcode_header_amd *mc_header = mc_amd->mpb;
+ const struct equiv_cpu_entry *equiv_cpu_table = mc_amd->equiv_cpu_table;
+ unsigned int current_cpu_id;
+ unsigned int equiv_cpu_id;
+
+ current_cpu_id = cpuid_eax(0x00000001);
+
+ if ( !find_equiv_cpu_id(equiv_cpu_table, current_cpu_id, &equiv_cpu_id) )
+ return MIS_UCODE;
+
+ if ( (mc_header->processor_rev_id) != equiv_cpu_id )
+ return MIS_UCODE;
+
+ if ( !verify_patch_size(mc_amd->mpb_size) )
+ {
+ pr_debug("microcode: patch size mismatch\n");
+ return MIS_UCODE;
+ }
+
+ if ( mc_header->patch_id <= sig->rev )
+ {
+ pr_debug("microcode: patch is already at required level or
greater.\n");
+ return OLD_UCODE;
+ }
+
+ pr_debug("microcode: CPU%d found a matching microcode update with version
%#x (current=%#x)\n",
+ cpu, mc_header->patch_id, sig->rev);
+
+ return NEW_UCODE;
+}
+
+static bool match_cpu(const struct microcode_patch *patch)
+{
+ return patch && (microcode_fits(patch->mc_amd) == NEW_UCODE);
+}
+
+static void free_patch(void *mc)
+{
+ struct microcode_amd *mc_amd = mc;
+
+ if ( mc_amd )
+ {
+ xfree(mc_amd->equiv_cpu_table);
+ xfree(mc_amd->mpb);
+ xfree(mc_amd);
+ }
+}
+
+static enum microcode_match_result compare_header(
+ const struct microcode_header_amd *new_header,
+ const struct microcode_header_amd *old_header)
+{
+ if ( new_header->processor_rev_id == old_header->processor_rev_id )
+ return (new_header->patch_id > old_header->patch_id) ? NEW_UCODE
+ : OLD_UCODE;
+
+ return MIS_UCODE;
+}
+
+static enum microcode_match_result compare_patch(
+ const struct microcode_patch *new, const struct microcode_patch *old)
+{
+ const struct microcode_header_amd *new_header = new->mc_amd->mpb;
+ const struct microcode_header_amd *old_header = old->mc_amd->mpb;
+
+ /* Both patches to compare are supposed to be applicable to local CPU. */
+ ASSERT(microcode_fits(new->mc_amd) != MIS_UCODE);
+ ASSERT(microcode_fits(new->mc_amd) != MIS_UCODE);
+
+ return compare_header(new_header, old_header);
+}
+
+static int apply_microcode(const struct microcode_patch *patch)
+{
+ uint32_t rev;
+ int hw_err;
+ unsigned int cpu = smp_processor_id();
+ struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
+ const struct microcode_header_amd *hdr;
+
+ if ( !patch )
+ return -ENOENT;
+
+ if ( !match_cpu(patch) )
+ return -EINVAL;
+
+ hdr = patch->mc_amd->mpb;
+
+ BUG_ON(local_irq_is_enabled());
+
+ hw_err = wrmsr_safe(MSR_AMD_PATCHLOADER, (unsigned long)hdr);
+
+ /* get patch id after patching */
+ rdmsrl(MSR_AMD_PATCHLEVEL, rev);
+
+ /*
+ * Some processors leave the ucode blob mapping as UC after the update.
+ * Flush the mapping to regain normal cacheability.
+ */
+ flush_area_local(hdr, FLUSH_TLB_GLOBAL | FLUSH_ORDER(0));
+
+ /* check current patch id and patch's id for match */
+ if ( hw_err || (rev != hdr->patch_id) )
+ {
+ printk(KERN_ERR "microcode: CPU%d update from revision "
+ "%#x to %#x failed\n", cpu, rev, hdr->patch_id);
+ return -EIO;
+ }
+
+ printk(KERN_WARNING "microcode: CPU%d updated from revision %#x to %#x\n",
+ cpu, sig->rev, hdr->patch_id);
+
+ sig->rev = rev;
+
+ return 0;
+}
+
+static int get_ucode_from_buffer_amd(
+ struct microcode_amd *mc_amd,
+ const void *buf,
+ size_t bufsize,
+ size_t *offset)
+{
+ const struct mpbhdr *mpbuf = buf + *offset;
+
+ /* No more data */
+ if ( *offset >= bufsize )
+ {
+ printk(KERN_ERR "microcode: Microcode buffer overrun\n");
+ return -EINVAL;
+ }
+
+ if ( mpbuf->type != UCODE_UCODE_TYPE )
+ {
+ printk(KERN_ERR "microcode: Wrong microcode payload type field\n");
+ return -EINVAL;
+ }
+
+ if ( (*offset + mpbuf->len) > bufsize )
+ {
+ printk(KERN_ERR "microcode: Bad data in microcode data file\n");
+ return -EINVAL;
+ }
+
+ mc_amd->mpb = xmalloc_bytes(mpbuf->len);
+ if ( !mc_amd->mpb )
+ return -ENOMEM;
+ mc_amd->mpb_size = mpbuf->len;
+ memcpy(mc_amd->mpb, mpbuf->data, mpbuf->len);
+
+ pr_debug("microcode: CPU%d size %zu, block size %u offset %zu equivID %#x
rev %#x\n",
+ raw_smp_processor_id(), bufsize, mpbuf->len, *offset,
+ ((struct microcode_header_amd *)mc_amd->mpb)->processor_rev_id,
+ ((struct microcode_header_amd *)mc_amd->mpb)->patch_id);
+
+ *offset += mpbuf->len + SECTION_HDR_SIZE;
+
+ return 0;
+}
+
+static int install_equiv_cpu_table(
+ struct microcode_amd *mc_amd,
+ const void *data,
+ size_t *offset)
+{
+ const struct mpbhdr *mpbuf = data + *offset + 4;
+
+ *offset += mpbuf->len + CONT_HDR_SIZE; /* add header length */
+
+ if ( mpbuf->type != UCODE_EQUIV_CPU_TABLE_TYPE )
+ {
+ printk(KERN_ERR "microcode: Wrong microcode equivalent cpu table type
field\n");
+ return -EINVAL;
+ }
+
+ if ( mpbuf->len == 0 )
+ {
+ printk(KERN_ERR "microcode: Wrong microcode equivalent cpu table
length\n");
+ return -EINVAL;
+ }
+
+ mc_amd->equiv_cpu_table = xmalloc_bytes(mpbuf->len);
+ if ( !mc_amd->equiv_cpu_table )
+ {
+ printk(KERN_ERR "microcode: Cannot allocate memory for equivalent cpu
table\n");
+ return -ENOMEM;
+ }
+
+ memcpy(mc_amd->equiv_cpu_table, mpbuf->data, mpbuf->len);
+ mc_amd->equiv_cpu_table_size = mpbuf->len;
+
+ return 0;
+}
+
+static int container_fast_forward(const void *data, size_t size_left, size_t
*offset)
+{
+ for ( ; ; )
+ {
+ size_t size;
+ const uint32_t *header;
+
+ if ( size_left < SECTION_HDR_SIZE )
+ return -EINVAL;
+
+ header = data + *offset;
+
+ if ( header[0] == UCODE_MAGIC &&
+ header[1] == UCODE_EQUIV_CPU_TABLE_TYPE )
+ break;
+
+ if ( header[0] != UCODE_UCODE_TYPE )
+ return -EINVAL;
+ size = header[1] + SECTION_HDR_SIZE;
+ if ( size < PATCH_HDR_SIZE || size_left < size )
+ return -EINVAL;
+
+ size_left -= size;
+ *offset += size;
+
+ if ( !size_left )
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+/*
+ * The 'final_levels' of patch ids have been obtained empirically.
+ * Refer bug https://bugzilla.suse.com/show_bug.cgi?id=913996
+ * for details of the issue. The short version is that people
+ * using certain Fam10h systems noticed system hang issues when
+ * trying to update microcode levels beyond the patch IDs below.
+ * From internal discussions, we gathered that OS/hypervisor
+ * cannot reliably perform microcode updates beyond these levels
+ * due to hardware issues. Therefore, we need to abort microcode
+ * update process if we hit any of these levels.
+ */
+static const unsigned int final_levels[] = {
+ 0x01000098,
+ 0x0100009f,
+ 0x010000af
+};
+
+static bool_t check_final_patch_levels(unsigned int cpu)
+{
+ /*
+ * Check the current patch levels on the cpu. If they are equal to
+ * any of the 'final_levels', then we should not update the microcode
+ * patch on the cpu as system will hang otherwise.
+ */
+ const struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
+ unsigned int i;
+
+ if ( boot_cpu_data.x86 != 0x10 )
+ return 0;
+
+ for ( i = 0; i < ARRAY_SIZE(final_levels); i++ )
+ if ( sig->rev == final_levels[i] )
+ return 1;
+
+ return 0;
+}
+
+static struct microcode_patch *cpu_request_microcode(const void *buf,
+ size_t bufsize)
+{
+ struct microcode_amd *mc_amd;
+ struct microcode_header_amd *saved = NULL;
+ struct microcode_patch *patch = NULL;
+ size_t offset = 0, saved_size = 0;
+ int error = 0;
+ unsigned int current_cpu_id;
+ unsigned int equiv_cpu_id;
+ unsigned int cpu = smp_processor_id();
+ const struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
+
+ current_cpu_id = cpuid_eax(0x00000001);
+
+ if ( *(const uint32_t *)buf != UCODE_MAGIC )
+ {
+ printk(KERN_ERR "microcode: Wrong microcode patch file magic\n");
+ error = -EINVAL;
+ goto out;
+ }
+
+ if ( check_final_patch_levels(cpu) )
+ {
+ printk(XENLOG_INFO
+ "microcode: Cannot update microcode patch on the cpu as we hit
a final level\n");
+ error = -EPERM;
+ goto out;
+ }
+
+ mc_amd = xzalloc(struct microcode_amd);
+ if ( !mc_amd )
+ {
+ printk(KERN_ERR "microcode: Cannot allocate memory for microcode
patch\n");
+ error = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Multiple container file support:
+ * 1. check if this container file has equiv_cpu_id match
+ * 2. If not, fast-fwd to next container file
+ */
+ while ( offset < bufsize )
+ {
+ error = install_equiv_cpu_table(mc_amd, buf, &offset);
+ if ( error )
+ {
+ printk(KERN_ERR "microcode: installing equivalent cpu table
failed\n");
+ break;
+ }
+
+ /*
+ * Could happen as we advance 'offset' early
+ * in install_equiv_cpu_table
+ */
+ if ( offset > bufsize )
+ {
+ printk(KERN_ERR "microcode: Microcode buffer overrun\n");
+ error = -EINVAL;
+ break;
+ }
+
+ if ( find_equiv_cpu_id(mc_amd->equiv_cpu_table, current_cpu_id,
+ &equiv_cpu_id) )
+ break;
+
+ error = container_fast_forward(buf, bufsize - offset, &offset);
+ if ( error == -ENODATA )
+ {
+ ASSERT(offset == bufsize);
+ break;
+ }
+ if ( error )
+ {
+ printk(KERN_ERR "microcode: CPU%d incorrect or corrupt container
file\n"
+ "microcode: Failed to update patch level. "
+ "Current lvl:%#x\n", cpu, sig->rev);
+ break;
+ }
+ }
+
+ if ( error )
+ {
+ /*
+ * -ENODATA here means that the blob was parsed fine but no matching
+ * ucode was found. Don't return it to the caller.
+ */
+ if ( error == -ENODATA )
+ error = 0;
+
+ xfree(mc_amd->equiv_cpu_table);
+ xfree(mc_amd);
+ goto out;
+ }
+
+ /*
+ * It's possible the data file has multiple matching ucode,
+ * lets keep searching till the latest version
+ */
+ while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
+ &offset)) == 0 )
+ {
+ /*
+ * If the new ucode covers current CPU, compare ucodes and store the
+ * one with higher revision.
+ */
+ if ( (microcode_fits(mc_amd) != MIS_UCODE) &&
+ (!saved || (compare_header(mc_amd->mpb, saved) == NEW_UCODE)) )
+ {
+ xfree(saved);
+ saved = mc_amd->mpb;
+ saved_size = mc_amd->mpb_size;
+ }
+ else
+ {
+ xfree(mc_amd->mpb);
+ mc_amd->mpb = NULL;
+ }
+
+ if ( offset >= bufsize )
+ break;
+
+ /*
+ * 1. Given a situation where multiple containers exist and correct
+ * patch lives on a container that is not the last container.
+ * 2. We match equivalent ids using find_equiv_cpu_id() from the
+ * earlier while() (On this case, matches on earlier container
+ * file and we break)
+ * 3. Proceed to while ( (error = get_ucode_from_buffer_amd(mc_amd,
+ * buf, bufsize,&offset)) == 0 )
+ * 4. Find correct patch using microcode_fits() and apply the patch
+ * (Assume: apply_microcode() is successful)
+ * 5. The while() loop from (3) continues to parse the binary as
+ * there is a subsequent container file, but...
+ * 6. ...a correct patch can only be on one container and not on any
+ * subsequent ones. (Refer docs for more info) Therefore, we
+ * don't have to parse a subsequent container. So, we can abort
+ * the process here.
+ * 7. This ensures that we retain a success value (= 0) to 'error'
+ * before if ( mpbuf->type != UCODE_UCODE_TYPE ) evaluates to
+ * false and returns -EINVAL.
+ */
+ if ( offset + SECTION_HDR_SIZE <= bufsize &&
+ *(const uint32_t *)(buf + offset) == UCODE_MAGIC )
+ break;
+ }
+
+ if ( saved )
+ {
+ mc_amd->mpb = saved;
+ mc_amd->mpb_size = saved_size;
+ patch = xmalloc(struct microcode_patch);
+ if ( patch )
+ patch->mc_amd = mc_amd;
+ else
+ {
+ free_patch(mc_amd);
+ error = -ENOMEM;
+ }
+ }
+ else
+ free_patch(mc_amd);
+
+ out:
+ if ( error && !patch )
+ patch = ERR_PTR(error);
+
+ return patch;
+}
+
+#ifdef CONFIG_HVM
+static int start_update(void)
+{
+ /*
+ * svm_host_osvw_init() will be called on each cpu by calling '.end_update'
+ * in common code.
+ */
+ svm_host_osvw_reset();
+
+ return 0;
+}
+#endif
+
+static const struct microcode_ops microcode_amd_ops = {
+ .cpu_request_microcode = cpu_request_microcode,
+ .collect_cpu_info = collect_cpu_info,
+ .apply_microcode = apply_microcode,
+#ifdef CONFIG_HVM
+ .start_update = start_update,
+ .end_update_percpu = svm_host_osvw_init,
+#endif
+ .free_patch = free_patch,
+ .compare_patch = compare_patch,
+ .match_cpu = match_cpu,
+};
+
+int __init microcode_init_amd(void)
+{
+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
+ microcode_ops = µcode_amd_ops;
+ return 0;
+}
diff --git a/xen/arch/x86/cpu/microcode/core.c
b/xen/arch/x86/cpu/microcode/core.c
new file mode 100644
index 0000000000..ac5da6b2fe
--- /dev/null
+++ b/xen/arch/x86/cpu/microcode/core.c
@@ -0,0 +1,820 @@
+/*
+ * Intel CPU Microcode Update Driver for Linux
+ *
+ * Copyright (C) 2000-2006 Tigran Aivazian <tigran@xxxxxxxxxxxxxxxxxxxx>
+ * 2006 Shaohua Li <shaohua.li@xxxxxxxxx> *
+ * This driver allows to upgrade microcode on Intel processors
+ * belonging to IA-32 family - PentiumPro, Pentium II,
+ * Pentium III, Xeon, Pentium 4, etc.
+ *
+ * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
+ * Software Developer's Manual
+ * Order Number 253668 or free download from:
+ *
+ * http://developer.intel.com/design/pentium4/manuals/253668.htm
+ *
+ * For more information, go to http://www.urbanmyth.org/microcode
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <xen/cpu.h>
+#include <xen/earlycpio.h>
+#include <xen/err.h>
+#include <xen/guest_access.h>
+#include <xen/init.h>
+#include <xen/param.h>
+#include <xen/spinlock.h>
+#include <xen/stop_machine.h>
+#include <xen/watchdog.h>
+
+#include <asm/apic.h>
+#include <asm/delay.h>
+#include <asm/nmi.h>
+#include <asm/processor.h>
+#include <asm/setup.h>
+
+#include "private.h"
+
+/*
+ * Before performing a late microcode update on any thread, we
+ * rendezvous all cpus in stop_machine context. The timeout for
+ * waiting for cpu rendezvous is 30ms. It is the timeout used by
+ * live patching
+ */
+#define MICROCODE_CALLIN_TIMEOUT_US 30000
+
+/*
+ * Timeout for each thread to complete update is set to 1s. It is a
+ * conservative choice considering all possible interference.
+ */
+#define MICROCODE_UPDATE_TIMEOUT_US 1000000
+
+static module_t __initdata ucode_mod;
+static signed int __initdata ucode_mod_idx;
+static bool_t __initdata ucode_mod_forced;
+static unsigned int nr_cores;
+
+/*
+ * These states help to coordinate CPUs during loading an update.
+ *
+ * The semantics of each state is as follow:
+ * - LOADING_PREPARE: initial state of 'loading_state'.
+ * - LOADING_CALLIN: CPUs are allowed to callin.
+ * - LOADING_ENTER: all CPUs have called in. Initiate ucode loading.
+ * - LOADING_EXIT: ucode loading is done or aborted.
+ */
+static enum {
+ LOADING_PREPARE,
+ LOADING_CALLIN,
+ LOADING_ENTER,
+ LOADING_EXIT,
+} loading_state;
+
+/*
+ * If we scan the initramfs.cpio for the early microcode code
+ * and find it, then 'ucode_blob' will contain the pointer
+ * and the size of said blob. It is allocated from Xen's heap
+ * memory.
+ */
+struct ucode_mod_blob {
+ const void *data;
+ size_t size;
+};
+
+static struct ucode_mod_blob __initdata ucode_blob;
+/*
+ * By default we will NOT parse the multiboot modules to see if there is
+ * cpio image with the microcode images.
+ */
+static bool_t __initdata ucode_scan;
+
+/* By default, ucode loading is done in NMI handler */
+static bool ucode_in_nmi = true;
+
+/* Protected by microcode_mutex */
+static struct microcode_patch *microcode_cache;
+
+void __init microcode_set_module(unsigned int idx)
+{
+ ucode_mod_idx = idx;
+ ucode_mod_forced = 1;
+}
+
+/*
+ * The format is '[<integer>|scan=<bool>, nmi=<bool>]'. Both options are
+ * optional. If the EFI has forced which of the multiboot payloads is to be
+ * used, only nmi=<bool> is parsed.
+ */
+static int __init parse_ucode(const char *s)
+{
+ const char *ss;
+ int val, rc = 0;
+
+ do {
+ ss = strchr(s, ',');
+ if ( !ss )
+ ss = strchr(s, '\0');
+
+ if ( (val = parse_boolean("nmi", s, ss)) >= 0 )
+ ucode_in_nmi = val;
+ else if ( !ucode_mod_forced ) /* Not forced by EFI */
+ {
+ if ( (val = parse_boolean("scan", s, ss)) >= 0 )
+ ucode_scan = val;
+ else
+ {
+ const char *q;
+
+ ucode_mod_idx = simple_strtol(s, &q, 0);
+ if ( q != ss )
+ rc = -EINVAL;
+ }
+ }
+
+ s = ss + 1;
+ } while ( *ss );
+
+ return rc;
+}
+custom_param("ucode", parse_ucode);
+
+void __init microcode_scan_module(
+ unsigned long *module_map,
+ const multiboot_info_t *mbi)
+{
+ module_t *mod = (module_t *)__va(mbi->mods_addr);
+ uint64_t *_blob_start;
+ unsigned long _blob_size;
+ struct cpio_data cd;
+ long offset;
+ const char *p = NULL;
+ int i;
+
+ ucode_blob.size = 0;
+ if ( !ucode_scan )
+ return;
+
+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
+ p = "kernel/x86/microcode/AuthenticAMD.bin";
+ else if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL )
+ p = "kernel/x86/microcode/GenuineIntel.bin";
+ else
+ return;
+
+ /*
+ * Try all modules and see whichever could be the microcode blob.
+ */
+ for ( i = 1 /* Ignore dom0 kernel */; i < mbi->mods_count; i++ )
+ {
+ if ( !test_bit(i, module_map) )
+ continue;
+
+ _blob_start = bootstrap_map(&mod[i]);
+ _blob_size = mod[i].mod_end;
+ if ( !_blob_start )
+ {
+ printk("Could not map multiboot module #%d (size: %ld)\n",
+ i, _blob_size);
+ continue;
+ }
+ cd.data = NULL;
+ cd.size = 0;
+ cd = find_cpio_data(p, _blob_start, _blob_size, &offset /* ignore */);
+ if ( cd.data )
+ {
+ ucode_blob.size = cd.size;
+ ucode_blob.data = cd.data;
+ break;
+ }
+ bootstrap_map(NULL);
+ }
+}
+void __init microcode_grab_module(
+ unsigned long *module_map,
+ const multiboot_info_t *mbi)
+{
+ module_t *mod = (module_t *)__va(mbi->mods_addr);
+
+ if ( ucode_mod_idx < 0 )
+ ucode_mod_idx += mbi->mods_count;
+ if ( ucode_mod_idx <= 0 || ucode_mod_idx >= mbi->mods_count ||
+ !__test_and_clear_bit(ucode_mod_idx, module_map) )
+ goto scan;
+ ucode_mod = mod[ucode_mod_idx];
+scan:
+ if ( ucode_scan )
+ microcode_scan_module(module_map, mbi);
+}
+
+const struct microcode_ops *microcode_ops;
+
+static DEFINE_SPINLOCK(microcode_mutex);
+
+DEFINE_PER_CPU(struct cpu_signature, cpu_sig);
+/* Store error code of the work done in NMI handler */
+static DEFINE_PER_CPU(int, loading_err);
+
+/*
+ * Count the CPUs that have entered, exited the rendezvous and succeeded in
+ * microcode update during late microcode update respectively.
+ *
+ * Note that a bitmap is used for callin to allow cpu to set a bit multiple
+ * times. It is required to do busy-loop in #NMI handling.
+ */
+static cpumask_t cpu_callin_map;
+static atomic_t cpu_out, cpu_updated;
+static const struct microcode_patch *nmi_patch = ZERO_BLOCK_PTR;
+
+/*
+ * Return a patch that covers current CPU. If there are multiple patches,
+ * return the one with the highest revision number. Return error If no
+ * patch is found and an error occurs during the parsing process. Otherwise
+ * return NULL.
+ */
+static struct microcode_patch *parse_blob(const char *buf, size_t len)
+{
+ if ( likely(!microcode_ops->collect_cpu_info(&this_cpu(cpu_sig))) )
+ return microcode_ops->cpu_request_microcode(buf, len);
+
+ return NULL;
+}
+
+static void microcode_free_patch(struct microcode_patch *microcode_patch)
+{
+ microcode_ops->free_patch(microcode_patch->mc);
+ xfree(microcode_patch);
+}
+
+/* Return true if cache gets updated. Otherwise, return false */
+static bool microcode_update_cache(struct microcode_patch *patch)
+{
+ ASSERT(spin_is_locked(µcode_mutex));
+
+ if ( !microcode_cache )
+ microcode_cache = patch;
+ else if ( microcode_ops->compare_patch(patch,
+ microcode_cache) == NEW_UCODE )
+ {
+ microcode_free_patch(microcode_cache);
+ microcode_cache = patch;
+ }
+ else
+ {
+ microcode_free_patch(patch);
+ return false;
+ }
+
+ return true;
+}
+
+/* Wait for a condition to be met with a timeout (us). */
+static int wait_for_condition(bool (*func)(unsigned int data),
+ unsigned int data, unsigned int timeout)
+{
+ while ( !func(data) )
+ {
+ if ( !timeout-- )
+ {
+ printk("CPU%u: Timeout in %pS\n",
+ smp_processor_id(), __builtin_return_address(0));
+ return -EBUSY;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static bool wait_cpu_callin(unsigned int nr)
+{
+ return cpumask_weight(&cpu_callin_map) >= nr;
+}
+
+static bool wait_cpu_callout(unsigned int nr)
+{
+ return atomic_read(&cpu_out) >= nr;
+}
+
+/*
+ * Load a microcode update to current CPU.
+ *
+ * If no patch is provided, the cached patch will be loaded. Microcode update
+ * during APs bringup and CPU resuming falls into this case.
+ */
+static int microcode_update_cpu(const struct microcode_patch *patch)
+{
+ int err = microcode_ops->collect_cpu_info(&this_cpu(cpu_sig));
+
+ if ( unlikely(err) )
+ return err;
+
+ spin_lock(µcode_mutex);
+ if ( patch )
+ err = microcode_ops->apply_microcode(patch);
+ else if ( microcode_cache )
+ {
+ err = microcode_ops->apply_microcode(microcode_cache);
+ if ( err == -EIO )
+ {
+ microcode_free_patch(microcode_cache);
+ microcode_cache = NULL;
+ }
+ }
+ else
+ /* No patch to update */
+ err = -ENOENT;
+ spin_unlock(µcode_mutex);
+
+ return err;
+}
+
+static bool wait_for_state(typeof(loading_state) state)
+{
+ typeof(loading_state) cur_state;
+
+ while ( (cur_state = ACCESS_ONCE(loading_state)) != state )
+ {
+ if ( cur_state == LOADING_EXIT )
+ return false;
+ cpu_relax();
+ }
+
+ return true;
+}
+
+static void set_state(typeof(loading_state) state)
+{
+ ACCESS_ONCE(loading_state) = state;
+}
+
+static int secondary_nmi_work(void)
+{
+ cpumask_set_cpu(smp_processor_id(), &cpu_callin_map);
+
+ return wait_for_state(LOADING_EXIT) ? 0 : -EBUSY;
+}
+
+static int primary_thread_work(const struct microcode_patch *patch)
+{
+ int ret;
+
+ cpumask_set_cpu(smp_processor_id(), &cpu_callin_map);
+
+ if ( !wait_for_state(LOADING_ENTER) )
+ return -EBUSY;
+
+ ret = microcode_ops->apply_microcode(patch);
+ if ( !ret )
+ atomic_inc(&cpu_updated);
+ atomic_inc(&cpu_out);
+
+ return ret;
+}
+
+static int microcode_nmi_callback(const struct cpu_user_regs *regs, int cpu)
+{
+ unsigned int primary = cpumask_first(this_cpu(cpu_sibling_mask));
+ int ret;
+
+ /* System-generated NMI, leave to main handler */
+ if ( ACCESS_ONCE(loading_state) != LOADING_CALLIN )
+ return 0;
+
+ /*
+ * Primary threads load ucode in NMI handler on if ucode_in_nmi is true.
+ * Secondary threads are expected to stay in NMI handler regardless of
+ * ucode_in_nmi.
+ */
+ if ( cpu == cpumask_first(&cpu_online_map) ||
+ (!ucode_in_nmi && cpu == primary) )
+ return 0;
+
+ if ( cpu == primary )
+ ret = primary_thread_work(nmi_patch);
+ else
+ ret = secondary_nmi_work();
+ this_cpu(loading_err) = ret;
+
+ return 0;
+}
+
+static int secondary_thread_fn(void)
+{
+ if ( !wait_for_state(LOADING_CALLIN) )
+ return -EBUSY;
+
+ self_nmi();
+
+ /*
+ * Wait for ucode loading is done in case that the NMI does not arrive
+ * synchronously, which may lead to a not-yet-updated CPU signature is
+ * copied below.
+ */
+ if ( unlikely(!wait_for_state(LOADING_EXIT)) )
+ ASSERT_UNREACHABLE();
+
+ /* Copy update revision from the primary thread. */
+ this_cpu(cpu_sig).rev =
+ per_cpu(cpu_sig, cpumask_first(this_cpu(cpu_sibling_mask))).rev;
+
+ return this_cpu(loading_err);
+}
+
+static int primary_thread_fn(const struct microcode_patch *patch)
+{
+ if ( !wait_for_state(LOADING_CALLIN) )
+ return -EBUSY;
+
+ if ( ucode_in_nmi )
+ {
+ self_nmi();
+
+ /*
+ * Wait for ucode loading is done in case that the NMI does not arrive
+ * synchronously, which may lead to a not-yet-updated error is returned
+ * below.
+ */
+ if ( unlikely(!wait_for_state(LOADING_EXIT)) )
+ ASSERT_UNREACHABLE();
+
+ return this_cpu(loading_err);
+ }
+
+ return primary_thread_work(patch);
+}
+
+static int control_thread_fn(const struct microcode_patch *patch)
+{
+ unsigned int cpu = smp_processor_id(), done;
+ unsigned long tick;
+ int ret;
+ nmi_callback_t *saved_nmi_callback;
+
+ /*
+ * We intend to keep interrupt disabled for a long time, which may lead to
+ * watchdog timeout.
+ */
+ watchdog_disable();
+
+ nmi_patch = patch;
+ smp_wmb();
+ saved_nmi_callback = set_nmi_callback(microcode_nmi_callback);
+
+ /* Allow threads to call in */
+ set_state(LOADING_CALLIN);
+
+ cpumask_set_cpu(cpu, &cpu_callin_map);
+
+ /* Waiting for all threads calling in */
+ ret = wait_for_condition(wait_cpu_callin, num_online_cpus(),
+ MICROCODE_CALLIN_TIMEOUT_US);
+ if ( ret )
+ {
+ set_state(LOADING_EXIT);
+ return ret;
+ }
+
+ /* Control thread loads ucode first while others are in NMI handler. */
+ ret = microcode_ops->apply_microcode(patch);
+ if ( !ret )
+ atomic_inc(&cpu_updated);
+ atomic_inc(&cpu_out);
+
+ if ( ret == -EIO )
+ {
+ printk(XENLOG_ERR
+ "Late loading aborted: CPU%u failed to update ucode\n", cpu);
+ set_state(LOADING_EXIT);
+ return ret;
+ }
+
+ /* Let primary threads load the given ucode update */
+ set_state(LOADING_ENTER);
+
+ tick = rdtsc_ordered();
+ /* Wait for primary threads finishing update */
+ while ( (done = atomic_read(&cpu_out)) != nr_cores )
+ {
+ /*
+ * During each timeout interval, at least a CPU is expected to
+ * finish its update. Otherwise, something goes wrong.
+ *
+ * Note that RDTSC (in wait_for_condition()) is safe for threads to
+ * execute while waiting for completion of loading an update.
+ */
+ if ( wait_for_condition(wait_cpu_callout, (done + 1),
+ MICROCODE_UPDATE_TIMEOUT_US) )
+ panic("Timeout when finished updating microcode (finished %u/%u)",
+ done, nr_cores);
+
+ /* Print warning message once if long time is spent here */
+ if ( tick && rdtsc_ordered() - tick >= cpu_khz * 1000 )
+ {
+ printk(XENLOG_WARNING
+ "WARNING: UPDATING MICROCODE HAS CONSUMED MORE THAN 1
SECOND!\n");
+ tick = 0;
+ }
+ }
+
+ /* Mark loading is done to unblock other threads */
+ set_state(LOADING_EXIT);
+
+ set_nmi_callback(saved_nmi_callback);
+ smp_wmb();
+ nmi_patch = ZERO_BLOCK_PTR;
+
+ watchdog_enable();
+
+ return ret;
+}
+
+static int do_microcode_update(void *patch)
+{
+ unsigned int cpu = smp_processor_id();
+ int ret;
+
+ /*
+ * The control thread set state to coordinate ucode loading. Primary
+ * threads load the given ucode patch. Secondary threads just wait for
+ * the completion of the ucode loading process.
+ */
+ if ( cpu == cpumask_first(&cpu_online_map) )
+ ret = control_thread_fn(patch);
+ else if ( cpu == cpumask_first(this_cpu(cpu_sibling_mask)) )
+ ret = primary_thread_fn(patch);
+ else
+ ret = secondary_thread_fn();
+
+ if ( microcode_ops->end_update_percpu )
+ microcode_ops->end_update_percpu();
+
+ return ret;
+}
+
+struct ucode_buf {
+ unsigned int len;
+ char buffer[];
+};
+
+static long microcode_update_helper(void *data)
+{
+ int ret;
+ struct ucode_buf *buffer = data;
+ unsigned int cpu, updated;
+ struct microcode_patch *patch;
+
+ /* cpu_online_map must not change during update */
+ if ( !get_cpu_maps() )
+ {
+ xfree(buffer);
+ return -EBUSY;
+ }
+
+ /*
+ * CPUs except the first online CPU would send a fake (self) NMI to
+ * rendezvous in NMI handler. But a fake NMI to nmi_cpu may trigger
+ * unknown_nmi_error(). It ensures nmi_cpu won't receive a fake NMI.
+ */
+ if ( unlikely(cpumask_first(&cpu_online_map) != nmi_cpu) )
+ {
+ xfree(buffer);
+ printk(XENLOG_WARNING
+ "CPU%u is expected to lead ucode loading (but got CPU%u)\n",
+ nmi_cpu, cpumask_first(&cpu_online_map));
+ return -EPERM;
+ }
+
+ patch = parse_blob(buffer->buffer, buffer->len);
+ xfree(buffer);
+ if ( IS_ERR(patch) )
+ {
+ ret = PTR_ERR(patch);
+ printk(XENLOG_WARNING "Parsing microcode blob error %d\n", ret);
+ goto put;
+ }
+
+ if ( !patch )
+ {
+ printk(XENLOG_WARNING "microcode: couldn't find any matching ucode in "
+ "the provided blob!\n");
+ ret = -ENOENT;
+ goto put;
+ }
+
+ /*
+ * If microcode_cache exists, all CPUs in the system should have at least
+ * that ucode revision.
+ */
+ spin_lock(µcode_mutex);
+ if ( microcode_cache &&
+ microcode_ops->compare_patch(patch, microcode_cache) != NEW_UCODE )
+ {
+ spin_unlock(µcode_mutex);
+ printk(XENLOG_WARNING "microcode: couldn't find any newer revision "
+ "in the provided blob!\n");
+ microcode_free_patch(patch);
+ ret = -ENOENT;
+
+ goto put;
+ }
+ spin_unlock(µcode_mutex);
+
+ if ( microcode_ops->start_update )
+ {
+ ret = microcode_ops->start_update();
+ if ( ret )
+ {
+ microcode_free_patch(patch);
+ goto put;
+ }
+ }
+
+ cpumask_clear(&cpu_callin_map);
+ atomic_set(&cpu_out, 0);
+ atomic_set(&cpu_updated, 0);
+ loading_state = LOADING_PREPARE;
+
+ /* Calculate the number of online CPU core */
+ nr_cores = 0;
+ for_each_online_cpu(cpu)
+ if ( cpu == cpumask_first(per_cpu(cpu_sibling_mask, cpu)) )
+ nr_cores++;
+
+ printk(XENLOG_INFO "%u cores are to update their microcode\n", nr_cores);
+
+ /*
+ * Late loading dance. Why the heavy-handed stop_machine effort?
+ *
+ * - HT siblings must be idle and not execute other code while the other
+ * sibling is loading microcode in order to avoid any negative
+ * interactions cause by the loading.
+ *
+ * - In addition, microcode update on the cores must be serialized until
+ * this requirement can be relaxed in the future. Right now, this is
+ * conservative and good.
+ */
+ ret = stop_machine_run(do_microcode_update, patch, NR_CPUS);
+
+ updated = atomic_read(&cpu_updated);
+ if ( updated > 0 )
+ {
+ spin_lock(µcode_mutex);
+ microcode_update_cache(patch);
+ spin_unlock(µcode_mutex);
+ }
+ else
+ microcode_free_patch(patch);
+
+ if ( updated && updated != nr_cores )
+ printk(XENLOG_ERR "ERROR: Updating microcode succeeded on %u cores and
failed\n"
+ XENLOG_ERR "on other %u cores. A system with differing
microcode\n"
+ XENLOG_ERR "revisions is considered unstable. Please reboot and
do not\n"
+ XENLOG_ERR "load the microcode that triggers this warning!\n",
+ updated, nr_cores - updated);
+
+ put:
+ put_cpu_maps();
+ return ret;
+}
+
+int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len)
+{
+ int ret;
+ struct ucode_buf *buffer;
+
+ if ( len != (uint32_t)len )
+ return -E2BIG;
+
+ if ( microcode_ops == NULL )
+ return -EINVAL;
+
+ buffer = xmalloc_flex_struct(struct ucode_buf, buffer, len);
+ if ( !buffer )
+ return -ENOMEM;
+
+ ret = copy_from_guest(buffer->buffer, buf, len);
+ if ( ret )
+ {
+ xfree(buffer);
+ return -EFAULT;
+ }
+ buffer->len = len;
+
+ return continue_hypercall_on_cpu(smp_processor_id(),
+ microcode_update_helper, buffer);
+}
+
+static int __init microcode_init(void)
+{
+ /*
+ * At this point, all CPUs should have updated their microcode
+ * via the early_microcode_* paths so free the microcode blob.
+ */
+ if ( ucode_blob.size )
+ {
+ bootstrap_map(NULL);
+ ucode_blob.size = 0;
+ ucode_blob.data = NULL;
+ }
+ else if ( ucode_mod.mod_end )
+ {
+ bootstrap_map(NULL);
+ ucode_mod.mod_end = 0;
+ }
+
+ return 0;
+}
+__initcall(microcode_init);
+
+/* Load a cached update to current cpu */
+int microcode_update_one(bool start_update)
+{
+ int err;
+
+ if ( !microcode_ops )
+ return -EOPNOTSUPP;
+
+ microcode_ops->collect_cpu_info(&this_cpu(cpu_sig));
+
+ if ( start_update && microcode_ops->start_update )
+ {
+ err = microcode_ops->start_update();
+ if ( err )
+ return err;
+ }
+
+ err = microcode_update_cpu(NULL);
+
+ if ( microcode_ops->end_update_percpu )
+ microcode_ops->end_update_percpu();
+
+ return err;
+}
+
+/* BSP calls this function to parse ucode blob and then apply an update. */
+static int __init early_microcode_update_cpu(void)
+{
+ int rc = 0;
+ const void *data = NULL;
+ size_t len;
+ struct microcode_patch *patch;
+
+ if ( ucode_blob.size )
+ {
+ len = ucode_blob.size;
+ data = ucode_blob.data;
+ }
+ else if ( ucode_mod.mod_end )
+ {
+ len = ucode_mod.mod_end;
+ data = bootstrap_map(&ucode_mod);
+ }
+
+ if ( !data )
+ return -ENOMEM;
+
+ patch = parse_blob(data, len);
+ if ( IS_ERR(patch) )
+ {
+ printk(XENLOG_WARNING "Parsing microcode blob error %ld\n",
+ PTR_ERR(patch));
+ return PTR_ERR(patch);
+ }
+
+ if ( !patch )
+ return -ENOENT;
+
+ spin_lock(µcode_mutex);
+ rc = microcode_update_cache(patch);
+ spin_unlock(µcode_mutex);
+ ASSERT(rc);
+
+ return microcode_update_one(true);
+}
+
+int __init early_microcode_init(void)
+{
+ int rc;
+
+ rc = microcode_init_intel();
+ if ( rc )
+ return rc;
+
+ rc = microcode_init_amd();
+ if ( rc )
+ return rc;
+
+ if ( microcode_ops )
+ {
+ microcode_ops->collect_cpu_info(&this_cpu(cpu_sig));
+
+ if ( ucode_mod.mod_end || ucode_blob.size )
+ rc = early_microcode_update_cpu();
+ }
+
+ return rc;
+}
diff --git a/xen/arch/x86/cpu/microcode/intel.c
b/xen/arch/x86/cpu/microcode/intel.c
new file mode 100644
index 0000000000..90fb006c94
--- /dev/null
+++ b/xen/arch/x86/cpu/microcode/intel.c
@@ -0,0 +1,422 @@
+/*
+ * Intel CPU Microcode Update Driver for Linux
+ *
+ * Copyright (C) 2000-2006 Tigran Aivazian <tigran@xxxxxxxxxxxxxxxxxxxx>
+ * 2006 Shaohua Li <shaohua.li@xxxxxxxxx> *
+ * This driver allows to upgrade microcode on Intel processors
+ * belonging to IA-32 family - PentiumPro, Pentium II,
+ * Pentium III, Xeon, Pentium 4, etc.
+ *
+ * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
+ * Software Developer's Manual
+ * Order Number 253668 or free download from:
+ *
+ * http://developer.intel.com/design/pentium4/manuals/253668.htm
+ *
+ * For more information, go to http://www.urbanmyth.org/microcode
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <xen/err.h>
+#include <xen/init.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+
+#include "private.h"
+
+#define pr_debug(x...) ((void)0)
+
+struct microcode_header_intel {
+ unsigned int hdrver;
+ unsigned int rev;
+ union {
+ struct {
+ uint16_t year;
+ uint8_t day;
+ uint8_t month;
+ };
+ unsigned int date;
+ };
+ unsigned int sig;
+ unsigned int cksum;
+ unsigned int ldrver;
+ unsigned int pf;
+ unsigned int datasize;
+ unsigned int totalsize;
+ unsigned int reserved[3];
+};
+
+struct microcode_intel {
+ struct microcode_header_intel hdr;
+ unsigned int bits[0];
+};
+
+/* microcode format is extended from prescott processors */
+struct extended_signature {
+ unsigned int sig;
+ unsigned int pf;
+ unsigned int cksum;
+};
+
+struct extended_sigtable {
+ unsigned int count;
+ unsigned int cksum;
+ unsigned int reserved[3];
+ struct extended_signature sigs[0];
+};
+
+#define DEFAULT_UCODE_DATASIZE (2000)
+#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel))
+#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
+#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable))
+#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature))
+#define DWSIZE (sizeof(u32))
+#define get_totalsize(mc) \
+ (((struct microcode_intel *)mc)->hdr.totalsize ? \
+ ((struct microcode_intel *)mc)->hdr.totalsize : \
+ DEFAULT_UCODE_TOTALSIZE)
+
+#define get_datasize(mc) \
+ (((struct microcode_intel *)mc)->hdr.datasize ? \
+ ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
+
+#define sigmatch(s1, s2, p1, p2) \
+ (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0))))
+
+#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
+
+static int collect_cpu_info(struct cpu_signature *csig)
+{
+ unsigned int cpu_num = smp_processor_id();
+ struct cpuinfo_x86 *c = &cpu_data[cpu_num];
+ uint64_t msr_content;
+
+ memset(csig, 0, sizeof(*csig));
+
+ if ( (c->x86_vendor != X86_VENDOR_INTEL) || (c->x86 < 6) )
+ {
+ printk(KERN_ERR "microcode: CPU%d not a capable Intel "
+ "processor\n", cpu_num);
+ return -1;
+ }
+
+ csig->sig = cpuid_eax(0x00000001);
+
+ if ( (c->x86_model >= 5) || (c->x86 > 6) )
+ {
+ /* get processor flags from MSR 0x17 */
+ rdmsrl(MSR_IA32_PLATFORM_ID, msr_content);
+ csig->pf = 1 << ((msr_content >> 50) & 7);
+ }
+
+ wrmsrl(MSR_IA32_UCODE_REV, 0x0ULL);
+ /* As documented in the SDM: Do a CPUID 1 here */
+ cpuid_eax(1);
+
+ /* get the current revision from MSR 0x8B */
+ rdmsrl(MSR_IA32_UCODE_REV, msr_content);
+ csig->rev = (uint32_t)(msr_content >> 32);
+ pr_debug("microcode: collect_cpu_info : sig=%#x, pf=%#x, rev=%#x\n",
+ csig->sig, csig->pf, csig->rev);
+
+ return 0;
+}
+
+static int microcode_sanity_check(const void *mc)
+{
+ const struct microcode_header_intel *mc_header = mc;
+ const struct extended_sigtable *ext_header = NULL;
+ const struct extended_signature *ext_sig;
+ unsigned long total_size, data_size, ext_table_size;
+ unsigned int ext_sigcount = 0, i;
+ uint32_t sum, orig_sum;
+
+ total_size = get_totalsize(mc_header);
+ data_size = get_datasize(mc_header);
+ if ( (data_size + MC_HEADER_SIZE) > total_size )
+ {
+ printk(KERN_ERR "microcode: error! "
+ "Bad data size in microcode data file\n");
+ return -EINVAL;
+ }
+
+ if ( (mc_header->ldrver != 1) || (mc_header->hdrver != 1) )
+ {
+ printk(KERN_ERR "microcode: error! "
+ "Unknown microcode update format\n");
+ return -EINVAL;
+ }
+ ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
+ if ( ext_table_size )
+ {
+ if ( (ext_table_size < EXT_HEADER_SIZE) ||
+ ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE) )
+ {
+ printk(KERN_ERR "microcode: error! "
+ "Small exttable size in microcode data file\n");
+ return -EINVAL;
+ }
+ ext_header = mc + MC_HEADER_SIZE + data_size;
+ if ( ext_table_size != exttable_size(ext_header) )
+ {
+ printk(KERN_ERR "microcode: error! "
+ "Bad exttable size in microcode data file\n");
+ return -EFAULT;
+ }
+ ext_sigcount = ext_header->count;
+ }
+
+ /* check extended table checksum */
+ if ( ext_table_size )
+ {
+ uint32_t ext_table_sum = 0;
+ uint32_t *ext_tablep = (uint32_t *)ext_header;
+
+ i = ext_table_size / DWSIZE;
+ while ( i-- )
+ ext_table_sum += ext_tablep[i];
+ if ( ext_table_sum )
+ {
+ printk(KERN_WARNING "microcode: aborting, "
+ "bad extended signature table checksum\n");
+ return -EINVAL;
+ }
+ }
+
+ /* calculate the checksum */
+ orig_sum = 0;
+ i = (MC_HEADER_SIZE + data_size) / DWSIZE;
+ while ( i-- )
+ orig_sum += ((uint32_t *)mc)[i];
+ if ( orig_sum )
+ {
+ printk(KERN_ERR "microcode: aborting, bad checksum\n");
+ return -EINVAL;
+ }
+ if ( !ext_table_size )
+ return 0;
+ /* check extended signature checksum */
+ for ( i = 0; i < ext_sigcount; i++ )
+ {
+ ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
+ EXT_SIGNATURE_SIZE * i;
+ sum = orig_sum
+ - (mc_header->sig + mc_header->pf + mc_header->cksum)
+ + (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
+ if ( sum )
+ {
+ printk(KERN_ERR "microcode: aborting, bad checksum\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/* Check an update against the CPU signature and current update revision */
+static enum microcode_match_result microcode_update_match(
+ const struct microcode_header_intel *mc_header)
+{
+ const struct extended_sigtable *ext_header;
+ const struct extended_signature *ext_sig;
+ unsigned int i;
+ struct cpu_signature *cpu_sig = &this_cpu(cpu_sig);
+ unsigned int sig = cpu_sig->sig;
+ unsigned int pf = cpu_sig->pf;
+ unsigned int rev = cpu_sig->rev;
+ unsigned long data_size = get_datasize(mc_header);
+ const void *end = (const void *)mc_header + get_totalsize(mc_header);
+
+ ASSERT(!microcode_sanity_check(mc_header));
+ if ( sigmatch(sig, mc_header->sig, pf, mc_header->pf) )
+ return (mc_header->rev > rev) ? NEW_UCODE : OLD_UCODE;
+
+ ext_header = (const void *)(mc_header + 1) + data_size;
+ ext_sig = (const void *)(ext_header + 1);
+
+ /*
+ * Make sure there is enough space to hold an extended header and enough
+ * array elements.
+ */
+ if ( end <= (const void *)ext_sig )
+ return MIS_UCODE;
+
+ for ( i = 0; i < ext_header->count; i++ )
+ if ( sigmatch(sig, ext_sig[i].sig, pf, ext_sig[i].pf) )
+ return (mc_header->rev > rev) ? NEW_UCODE : OLD_UCODE;
+
+ return MIS_UCODE;
+}
+
+static bool match_cpu(const struct microcode_patch *patch)
+{
+ if ( !patch )
+ return false;
+
+ return microcode_update_match(&patch->mc_intel->hdr) == NEW_UCODE;
+}
+
+static void free_patch(void *mc)
+{
+ xfree(mc);
+}
+
+static enum microcode_match_result compare_patch(
+ const struct microcode_patch *new, const struct microcode_patch *old)
+{
+ /*
+ * Both patches to compare are supposed to be applicable to local CPU.
+ * Just compare the revision number.
+ */
+ ASSERT(microcode_update_match(&old->mc_intel->hdr) != MIS_UCODE);
+ ASSERT(microcode_update_match(&new->mc_intel->hdr) != MIS_UCODE);
+
+ return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ? NEW_UCODE
+ : OLD_UCODE;
+}
+
+static int apply_microcode(const struct microcode_patch *patch)
+{
+ uint64_t msr_content;
+ unsigned int val[2];
+ unsigned int cpu_num = raw_smp_processor_id();
+ struct cpu_signature *sig = &this_cpu(cpu_sig);
+ const struct microcode_intel *mc_intel;
+
+ if ( !patch )
+ return -ENOENT;
+
+ if ( !match_cpu(patch) )
+ return -EINVAL;
+
+ mc_intel = patch->mc_intel;
+
+ BUG_ON(local_irq_is_enabled());
+
+ /* write microcode via MSR 0x79 */
+ wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)mc_intel->bits);
+ wrmsrl(MSR_IA32_UCODE_REV, 0x0ULL);
+
+ /* As documented in the SDM: Do a CPUID 1 here */
+ cpuid_eax(1);
+
+ /* get the current revision from MSR 0x8B */
+ rdmsrl(MSR_IA32_UCODE_REV, msr_content);
+ val[1] = (uint32_t)(msr_content >> 32);
+
+ if ( val[1] != mc_intel->hdr.rev )
+ {
+ printk(KERN_ERR "microcode: CPU%d update from revision "
+ "%#x to %#x failed. Resulting revision is %#x.\n", cpu_num,
+ sig->rev, mc_intel->hdr.rev, val[1]);
+ return -EIO;
+ }
+ printk(KERN_INFO "microcode: CPU%d updated from revision "
+ "%#x to %#x, date = %04x-%02x-%02x\n",
+ cpu_num, sig->rev, val[1], mc_intel->hdr.year,
+ mc_intel->hdr.month, mc_intel->hdr.day);
+ sig->rev = val[1];
+
+ return 0;
+}
+
+static long get_next_ucode_from_buffer(struct microcode_intel **mc,
+ const uint8_t *buf, unsigned long size,
+ unsigned long offset)
+{
+ struct microcode_header_intel *mc_header;
+ unsigned long total_size;
+
+ /* No more data */
+ if ( offset >= size )
+ return 0;
+ mc_header = (struct microcode_header_intel *)(buf + offset);
+ total_size = get_totalsize(mc_header);
+
+ if ( (offset + total_size) > size )
+ {
+ printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
+ return -EINVAL;
+ }
+
+ *mc = xmalloc_bytes(total_size);
+ if ( *mc == NULL )
+ {
+ printk(KERN_ERR "microcode: error! Can not allocate memory\n");
+ return -ENOMEM;
+ }
+ memcpy(*mc, (const void *)(buf + offset), total_size);
+ return offset + total_size;
+}
+
+static struct microcode_patch *cpu_request_microcode(const void *buf,
+ size_t size)
+{
+ long offset = 0;
+ int error = 0;
+ struct microcode_intel *mc, *saved = NULL;
+ struct microcode_patch *patch = NULL;
+
+ while ( (offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) > 0 )
+ {
+ error = microcode_sanity_check(mc);
+ if ( error )
+ {
+ xfree(mc);
+ break;
+ }
+
+ /*
+ * If the new update covers current CPU, compare updates and store the
+ * one with higher revision.
+ */
+ if ( (microcode_update_match(&mc->hdr) != MIS_UCODE) &&
+ (!saved || (mc->hdr.rev > saved->hdr.rev)) )
+ {
+ xfree(saved);
+ saved = mc;
+ }
+ else
+ xfree(mc);
+ }
+ if ( offset < 0 )
+ error = offset;
+
+ if ( saved )
+ {
+ patch = xmalloc(struct microcode_patch);
+ if ( patch )
+ patch->mc_intel = saved;
+ else
+ {
+ xfree(saved);
+ error = -ENOMEM;
+ }
+ }
+
+ if ( error && !patch )
+ patch = ERR_PTR(error);
+
+ return patch;
+}
+
+static const struct microcode_ops microcode_intel_ops = {
+ .cpu_request_microcode = cpu_request_microcode,
+ .collect_cpu_info = collect_cpu_info,
+ .apply_microcode = apply_microcode,
+ .free_patch = free_patch,
+ .compare_patch = compare_patch,
+ .match_cpu = match_cpu,
+};
+
+int __init microcode_init_intel(void)
+{
+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL )
+ microcode_ops = µcode_intel_ops;
+ return 0;
+}
diff --git a/xen/arch/x86/cpu/microcode/private.h
b/xen/arch/x86/cpu/microcode/private.h
new file mode 100644
index 0000000000..2e3be79eaf
--- /dev/null
+++ b/xen/arch/x86/cpu/microcode/private.h
@@ -0,0 +1,37 @@
+#ifndef ASM_X86_MICROCODE_PRIVATE_H
+#define ASM_X86_MICROCODE_PRIVATE_H
+
+#include <xen/types.h>
+
+#include <asm/microcode.h>
+
+enum microcode_match_result {
+ OLD_UCODE, /* signature matched, but revision id is older or equal */
+ NEW_UCODE, /* signature matched, but revision id is newer */
+ MIS_UCODE, /* signature mismatched */
+};
+
+struct microcode_patch {
+ union {
+ struct microcode_intel *mc_intel;
+ struct microcode_amd *mc_amd;
+ void *mc;
+ };
+};
+
+struct microcode_ops {
+ struct microcode_patch *(*cpu_request_microcode)(const void *buf,
+ size_t size);
+ int (*collect_cpu_info)(struct cpu_signature *csig);
+ int (*apply_microcode)(const struct microcode_patch *patch);
+ int (*start_update)(void);
+ void (*end_update_percpu)(void);
+ void (*free_patch)(void *mc);
+ bool (*match_cpu)(const struct microcode_patch *patch);
+ enum microcode_match_result (*compare_patch)(
+ const struct microcode_patch *new, const struct microcode_patch *old);
+};
+
+extern const struct microcode_ops *microcode_ops;
+
+#endif /* ASM_X86_MICROCODE_PRIVATE_H */
diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c
deleted file mode 100644
index 27a88c6826..0000000000
--- a/xen/arch/x86/microcode.c
+++ /dev/null
@@ -1,827 +0,0 @@
-/*
- * Intel CPU Microcode Update Driver for Linux
- *
- * Copyright (C) 2000-2006 Tigran Aivazian <tigran@xxxxxxxxxxxxxxxxxxxx>
- * 2006 Shaohua Li <shaohua.li@xxxxxxxxx> *
- * This driver allows to upgrade microcode on Intel processors
- * belonging to IA-32 family - PentiumPro, Pentium II,
- * Pentium III, Xeon, Pentium 4, etc.
- *
- * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
- * Software Developer's Manual
- * Order Number 253668 or free download from:
- *
- * http://developer.intel.com/design/pentium4/manuals/253668.htm
- *
- * For more information, go to http://www.urbanmyth.org/microcode
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <xen/cpu.h>
-#include <xen/err.h>
-#include <xen/init.h>
-#include <xen/kernel.h>
-#include <xen/lib.h>
-#include <xen/notifier.h>
-#include <xen/param.h>
-#include <xen/sched.h>
-#include <xen/smp.h>
-#include <xen/softirq.h>
-#include <xen/spinlock.h>
-#include <xen/stop_machine.h>
-#include <xen/tasklet.h>
-#include <xen/guest_access.h>
-#include <xen/earlycpio.h>
-#include <xen/watchdog.h>
-
-#include <asm/apic.h>
-#include <asm/delay.h>
-#include <asm/msr.h>
-#include <asm/nmi.h>
-#include <asm/processor.h>
-#include <asm/setup.h>
-#include <asm/microcode.h>
-
-/*
- * Before performing a late microcode update on any thread, we
- * rendezvous all cpus in stop_machine context. The timeout for
- * waiting for cpu rendezvous is 30ms. It is the timeout used by
- * live patching
- */
-#define MICROCODE_CALLIN_TIMEOUT_US 30000
-
-/*
- * Timeout for each thread to complete update is set to 1s. It is a
- * conservative choice considering all possible interference.
- */
-#define MICROCODE_UPDATE_TIMEOUT_US 1000000
-
-static module_t __initdata ucode_mod;
-static signed int __initdata ucode_mod_idx;
-static bool_t __initdata ucode_mod_forced;
-static unsigned int nr_cores;
-
-/*
- * These states help to coordinate CPUs during loading an update.
- *
- * The semantics of each state is as follow:
- * - LOADING_PREPARE: initial state of 'loading_state'.
- * - LOADING_CALLIN: CPUs are allowed to callin.
- * - LOADING_ENTER: all CPUs have called in. Initiate ucode loading.
- * - LOADING_EXIT: ucode loading is done or aborted.
- */
-static enum {
- LOADING_PREPARE,
- LOADING_CALLIN,
- LOADING_ENTER,
- LOADING_EXIT,
-} loading_state;
-
-/*
- * If we scan the initramfs.cpio for the early microcode code
- * and find it, then 'ucode_blob' will contain the pointer
- * and the size of said blob. It is allocated from Xen's heap
- * memory.
- */
-struct ucode_mod_blob {
- const void *data;
- size_t size;
-};
-
-static struct ucode_mod_blob __initdata ucode_blob;
-/*
- * By default we will NOT parse the multiboot modules to see if there is
- * cpio image with the microcode images.
- */
-static bool_t __initdata ucode_scan;
-
-/* By default, ucode loading is done in NMI handler */
-static bool ucode_in_nmi = true;
-
-/* Protected by microcode_mutex */
-static struct microcode_patch *microcode_cache;
-
-void __init microcode_set_module(unsigned int idx)
-{
- ucode_mod_idx = idx;
- ucode_mod_forced = 1;
-}
-
-/*
- * The format is '[<integer>|scan=<bool>, nmi=<bool>]'. Both options are
- * optional. If the EFI has forced which of the multiboot payloads is to be
- * used, only nmi=<bool> is parsed.
- */
-static int __init parse_ucode(const char *s)
-{
- const char *ss;
- int val, rc = 0;
-
- do {
- ss = strchr(s, ',');
- if ( !ss )
- ss = strchr(s, '\0');
-
- if ( (val = parse_boolean("nmi", s, ss)) >= 0 )
- ucode_in_nmi = val;
- else if ( !ucode_mod_forced ) /* Not forced by EFI */
- {
- if ( (val = parse_boolean("scan", s, ss)) >= 0 )
- ucode_scan = val;
- else
- {
- const char *q;
-
- ucode_mod_idx = simple_strtol(s, &q, 0);
- if ( q != ss )
- rc = -EINVAL;
- }
- }
-
- s = ss + 1;
- } while ( *ss );
-
- return rc;
-}
-custom_param("ucode", parse_ucode);
-
-void __init microcode_scan_module(
- unsigned long *module_map,
- const multiboot_info_t *mbi)
-{
- module_t *mod = (module_t *)__va(mbi->mods_addr);
- uint64_t *_blob_start;
- unsigned long _blob_size;
- struct cpio_data cd;
- long offset;
- const char *p = NULL;
- int i;
-
- ucode_blob.size = 0;
- if ( !ucode_scan )
- return;
-
- if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
- p = "kernel/x86/microcode/AuthenticAMD.bin";
- else if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL )
- p = "kernel/x86/microcode/GenuineIntel.bin";
- else
- return;
-
- /*
- * Try all modules and see whichever could be the microcode blob.
- */
- for ( i = 1 /* Ignore dom0 kernel */; i < mbi->mods_count; i++ )
- {
- if ( !test_bit(i, module_map) )
- continue;
-
- _blob_start = bootstrap_map(&mod[i]);
- _blob_size = mod[i].mod_end;
- if ( !_blob_start )
- {
- printk("Could not map multiboot module #%d (size: %ld)\n",
- i, _blob_size);
- continue;
- }
- cd.data = NULL;
- cd.size = 0;
- cd = find_cpio_data(p, _blob_start, _blob_size, &offset /* ignore */);
- if ( cd.data )
- {
- ucode_blob.size = cd.size;
- ucode_blob.data = cd.data;
- break;
- }
- bootstrap_map(NULL);
- }
-}
-void __init microcode_grab_module(
- unsigned long *module_map,
- const multiboot_info_t *mbi)
-{
- module_t *mod = (module_t *)__va(mbi->mods_addr);
-
- if ( ucode_mod_idx < 0 )
- ucode_mod_idx += mbi->mods_count;
- if ( ucode_mod_idx <= 0 || ucode_mod_idx >= mbi->mods_count ||
- !__test_and_clear_bit(ucode_mod_idx, module_map) )
- goto scan;
- ucode_mod = mod[ucode_mod_idx];
-scan:
- if ( ucode_scan )
- microcode_scan_module(module_map, mbi);
-}
-
-const struct microcode_ops *microcode_ops;
-
-static DEFINE_SPINLOCK(microcode_mutex);
-
-DEFINE_PER_CPU(struct cpu_signature, cpu_sig);
-/* Store error code of the work done in NMI handler */
-static DEFINE_PER_CPU(int, loading_err);
-
-/*
- * Count the CPUs that have entered, exited the rendezvous and succeeded in
- * microcode update during late microcode update respectively.
- *
- * Note that a bitmap is used for callin to allow cpu to set a bit multiple
- * times. It is required to do busy-loop in #NMI handling.
- */
-static cpumask_t cpu_callin_map;
-static atomic_t cpu_out, cpu_updated;
-static const struct microcode_patch *nmi_patch = ZERO_BLOCK_PTR;
-
-/*
- * Return a patch that covers current CPU. If there are multiple patches,
- * return the one with the highest revision number. Return error If no
- * patch is found and an error occurs during the parsing process. Otherwise
- * return NULL.
- */
-static struct microcode_patch *parse_blob(const char *buf, size_t len)
-{
- if ( likely(!microcode_ops->collect_cpu_info(&this_cpu(cpu_sig))) )
- return microcode_ops->cpu_request_microcode(buf, len);
-
- return NULL;
-}
-
-static void microcode_free_patch(struct microcode_patch *microcode_patch)
-{
- microcode_ops->free_patch(microcode_patch->mc);
- xfree(microcode_patch);
-}
-
-/* Return true if cache gets updated. Otherwise, return false */
-static bool microcode_update_cache(struct microcode_patch *patch)
-{
- ASSERT(spin_is_locked(µcode_mutex));
-
- if ( !microcode_cache )
- microcode_cache = patch;
- else if ( microcode_ops->compare_patch(patch,
- microcode_cache) == NEW_UCODE )
- {
- microcode_free_patch(microcode_cache);
- microcode_cache = patch;
- }
- else
- {
- microcode_free_patch(patch);
- return false;
- }
-
- return true;
-}
-
-/* Wait for a condition to be met with a timeout (us). */
-static int wait_for_condition(bool (*func)(unsigned int data),
- unsigned int data, unsigned int timeout)
-{
- while ( !func(data) )
- {
- if ( !timeout-- )
- {
- printk("CPU%u: Timeout in %pS\n",
- smp_processor_id(), __builtin_return_address(0));
- return -EBUSY;
- }
- udelay(1);
- }
-
- return 0;
-}
-
-static bool wait_cpu_callin(unsigned int nr)
-{
- return cpumask_weight(&cpu_callin_map) >= nr;
-}
-
-static bool wait_cpu_callout(unsigned int nr)
-{
- return atomic_read(&cpu_out) >= nr;
-}
-
-/*
- * Load a microcode update to current CPU.
- *
- * If no patch is provided, the cached patch will be loaded. Microcode update
- * during APs bringup and CPU resuming falls into this case.
- */
-static int microcode_update_cpu(const struct microcode_patch *patch)
-{
- int err = microcode_ops->collect_cpu_info(&this_cpu(cpu_sig));
-
- if ( unlikely(err) )
- return err;
-
- spin_lock(µcode_mutex);
- if ( patch )
- err = microcode_ops->apply_microcode(patch);
- else if ( microcode_cache )
- {
- err = microcode_ops->apply_microcode(microcode_cache);
- if ( err == -EIO )
- {
- microcode_free_patch(microcode_cache);
- microcode_cache = NULL;
- }
- }
- else
- /* No patch to update */
- err = -ENOENT;
- spin_unlock(µcode_mutex);
-
- return err;
-}
-
-static bool wait_for_state(typeof(loading_state) state)
-{
- typeof(loading_state) cur_state;
-
- while ( (cur_state = ACCESS_ONCE(loading_state)) != state )
- {
- if ( cur_state == LOADING_EXIT )
- return false;
- cpu_relax();
- }
-
- return true;
-}
-
-static void set_state(typeof(loading_state) state)
-{
- ACCESS_ONCE(loading_state) = state;
-}
-
-static int secondary_nmi_work(void)
-{
- cpumask_set_cpu(smp_processor_id(), &cpu_callin_map);
-
- return wait_for_state(LOADING_EXIT) ? 0 : -EBUSY;
-}
-
-static int primary_thread_work(const struct microcode_patch *patch)
-{
- int ret;
-
- cpumask_set_cpu(smp_processor_id(), &cpu_callin_map);
-
- if ( !wait_for_state(LOADING_ENTER) )
- return -EBUSY;
-
- ret = microcode_ops->apply_microcode(patch);
- if ( !ret )
- atomic_inc(&cpu_updated);
- atomic_inc(&cpu_out);
-
- return ret;
-}
-
-static int microcode_nmi_callback(const struct cpu_user_regs *regs, int cpu)
-{
- unsigned int primary = cpumask_first(this_cpu(cpu_sibling_mask));
- int ret;
-
- /* System-generated NMI, leave to main handler */
- if ( ACCESS_ONCE(loading_state) != LOADING_CALLIN )
- return 0;
-
- /*
- * Primary threads load ucode in NMI handler on if ucode_in_nmi is true.
- * Secondary threads are expected to stay in NMI handler regardless of
- * ucode_in_nmi.
- */
- if ( cpu == cpumask_first(&cpu_online_map) ||
- (!ucode_in_nmi && cpu == primary) )
- return 0;
-
- if ( cpu == primary )
- ret = primary_thread_work(nmi_patch);
- else
- ret = secondary_nmi_work();
- this_cpu(loading_err) = ret;
-
- return 0;
-}
-
-static int secondary_thread_fn(void)
-{
- if ( !wait_for_state(LOADING_CALLIN) )
- return -EBUSY;
-
- self_nmi();
-
- /*
- * Wait for ucode loading is done in case that the NMI does not arrive
- * synchronously, which may lead to a not-yet-updated CPU signature is
- * copied below.
- */
- if ( unlikely(!wait_for_state(LOADING_EXIT)) )
- ASSERT_UNREACHABLE();
-
- /* Copy update revision from the primary thread. */
- this_cpu(cpu_sig).rev =
- per_cpu(cpu_sig, cpumask_first(this_cpu(cpu_sibling_mask))).rev;
-
- return this_cpu(loading_err);
-}
-
-static int primary_thread_fn(const struct microcode_patch *patch)
-{
- if ( !wait_for_state(LOADING_CALLIN) )
- return -EBUSY;
-
- if ( ucode_in_nmi )
- {
- self_nmi();
-
- /*
- * Wait for ucode loading is done in case that the NMI does not arrive
- * synchronously, which may lead to a not-yet-updated error is returned
- * below.
- */
- if ( unlikely(!wait_for_state(LOADING_EXIT)) )
- ASSERT_UNREACHABLE();
-
- return this_cpu(loading_err);
- }
-
- return primary_thread_work(patch);
-}
-
-static int control_thread_fn(const struct microcode_patch *patch)
-{
- unsigned int cpu = smp_processor_id(), done;
- unsigned long tick;
- int ret;
- nmi_callback_t *saved_nmi_callback;
-
- /*
- * We intend to keep interrupt disabled for a long time, which may lead to
- * watchdog timeout.
- */
- watchdog_disable();
-
- nmi_patch = patch;
- smp_wmb();
- saved_nmi_callback = set_nmi_callback(microcode_nmi_callback);
-
- /* Allow threads to call in */
- set_state(LOADING_CALLIN);
-
- cpumask_set_cpu(cpu, &cpu_callin_map);
-
- /* Waiting for all threads calling in */
- ret = wait_for_condition(wait_cpu_callin, num_online_cpus(),
- MICROCODE_CALLIN_TIMEOUT_US);
- if ( ret )
- {
- set_state(LOADING_EXIT);
- return ret;
- }
-
- /* Control thread loads ucode first while others are in NMI handler. */
- ret = microcode_ops->apply_microcode(patch);
- if ( !ret )
- atomic_inc(&cpu_updated);
- atomic_inc(&cpu_out);
-
- if ( ret == -EIO )
- {
- printk(XENLOG_ERR
- "Late loading aborted: CPU%u failed to update ucode\n", cpu);
- set_state(LOADING_EXIT);
- return ret;
- }
-
- /* Let primary threads load the given ucode update */
- set_state(LOADING_ENTER);
-
- tick = rdtsc_ordered();
- /* Wait for primary threads finishing update */
- while ( (done = atomic_read(&cpu_out)) != nr_cores )
- {
- /*
- * During each timeout interval, at least a CPU is expected to
- * finish its update. Otherwise, something goes wrong.
- *
- * Note that RDTSC (in wait_for_condition()) is safe for threads to
- * execute while waiting for completion of loading an update.
- */
- if ( wait_for_condition(wait_cpu_callout, (done + 1),
- MICROCODE_UPDATE_TIMEOUT_US) )
- panic("Timeout when finished updating microcode (finished %u/%u)",
- done, nr_cores);
-
- /* Print warning message once if long time is spent here */
- if ( tick && rdtsc_ordered() - tick >= cpu_khz * 1000 )
- {
- printk(XENLOG_WARNING
- "WARNING: UPDATING MICROCODE HAS CONSUMED MORE THAN 1
SECOND!\n");
- tick = 0;
- }
- }
-
- /* Mark loading is done to unblock other threads */
- set_state(LOADING_EXIT);
-
- set_nmi_callback(saved_nmi_callback);
- smp_wmb();
- nmi_patch = ZERO_BLOCK_PTR;
-
- watchdog_enable();
-
- return ret;
-}
-
-static int do_microcode_update(void *patch)
-{
- unsigned int cpu = smp_processor_id();
- int ret;
-
- /*
- * The control thread set state to coordinate ucode loading. Primary
- * threads load the given ucode patch. Secondary threads just wait for
- * the completion of the ucode loading process.
- */
- if ( cpu == cpumask_first(&cpu_online_map) )
- ret = control_thread_fn(patch);
- else if ( cpu == cpumask_first(this_cpu(cpu_sibling_mask)) )
- ret = primary_thread_fn(patch);
- else
- ret = secondary_thread_fn();
-
- if ( microcode_ops->end_update_percpu )
- microcode_ops->end_update_percpu();
-
- return ret;
-}
-
-struct ucode_buf {
- unsigned int len;
- char buffer[];
-};
-
-static long microcode_update_helper(void *data)
-{
- int ret;
- struct ucode_buf *buffer = data;
- unsigned int cpu, updated;
- struct microcode_patch *patch;
-
- /* cpu_online_map must not change during update */
- if ( !get_cpu_maps() )
- {
- xfree(buffer);
- return -EBUSY;
- }
-
- /*
- * CPUs except the first online CPU would send a fake (self) NMI to
- * rendezvous in NMI handler. But a fake NMI to nmi_cpu may trigger
- * unknown_nmi_error(). It ensures nmi_cpu won't receive a fake NMI.
- */
- if ( unlikely(cpumask_first(&cpu_online_map) != nmi_cpu) )
- {
- xfree(buffer);
- printk(XENLOG_WARNING
- "CPU%u is expected to lead ucode loading (but got CPU%u)\n",
- nmi_cpu, cpumask_first(&cpu_online_map));
- return -EPERM;
- }
-
- patch = parse_blob(buffer->buffer, buffer->len);
- xfree(buffer);
- if ( IS_ERR(patch) )
- {
- ret = PTR_ERR(patch);
- printk(XENLOG_WARNING "Parsing microcode blob error %d\n", ret);
- goto put;
- }
-
- if ( !patch )
- {
- printk(XENLOG_WARNING "microcode: couldn't find any matching ucode in "
- "the provided blob!\n");
- ret = -ENOENT;
- goto put;
- }
-
- /*
- * If microcode_cache exists, all CPUs in the system should have at least
- * that ucode revision.
- */
- spin_lock(µcode_mutex);
- if ( microcode_cache &&
- microcode_ops->compare_patch(patch, microcode_cache) != NEW_UCODE )
- {
- spin_unlock(µcode_mutex);
- printk(XENLOG_WARNING "microcode: couldn't find any newer revision "
- "in the provided blob!\n");
- microcode_free_patch(patch);
- ret = -ENOENT;
-
- goto put;
- }
- spin_unlock(µcode_mutex);
-
- if ( microcode_ops->start_update )
- {
- ret = microcode_ops->start_update();
- if ( ret )
- {
- microcode_free_patch(patch);
- goto put;
- }
- }
-
- cpumask_clear(&cpu_callin_map);
- atomic_set(&cpu_out, 0);
- atomic_set(&cpu_updated, 0);
- loading_state = LOADING_PREPARE;
-
- /* Calculate the number of online CPU core */
- nr_cores = 0;
- for_each_online_cpu(cpu)
- if ( cpu == cpumask_first(per_cpu(cpu_sibling_mask, cpu)) )
- nr_cores++;
-
- printk(XENLOG_INFO "%u cores are to update their microcode\n", nr_cores);
-
- /*
- * Late loading dance. Why the heavy-handed stop_machine effort?
- *
- * - HT siblings must be idle and not execute other code while the other
- * sibling is loading microcode in order to avoid any negative
- * interactions cause by the loading.
- *
- * - In addition, microcode update on the cores must be serialized until
- * this requirement can be relaxed in the future. Right now, this is
- * conservative and good.
- */
- ret = stop_machine_run(do_microcode_update, patch, NR_CPUS);
-
- updated = atomic_read(&cpu_updated);
- if ( updated > 0 )
- {
- spin_lock(µcode_mutex);
- microcode_update_cache(patch);
- spin_unlock(µcode_mutex);
- }
- else
- microcode_free_patch(patch);
-
- if ( updated && updated != nr_cores )
- printk(XENLOG_ERR "ERROR: Updating microcode succeeded on %u cores and
failed\n"
- XENLOG_ERR "on other %u cores. A system with differing
microcode\n"
- XENLOG_ERR "revisions is considered unstable. Please reboot and
do not\n"
- XENLOG_ERR "load the microcode that triggers this warning!\n",
- updated, nr_cores - updated);
-
- put:
- put_cpu_maps();
- return ret;
-}
-
-int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len)
-{
- int ret;
- struct ucode_buf *buffer;
-
- if ( len != (uint32_t)len )
- return -E2BIG;
-
- if ( microcode_ops == NULL )
- return -EINVAL;
-
- buffer = xmalloc_flex_struct(struct ucode_buf, buffer, len);
- if ( !buffer )
- return -ENOMEM;
-
- ret = copy_from_guest(buffer->buffer, buf, len);
- if ( ret )
- {
- xfree(buffer);
- return -EFAULT;
- }
- buffer->len = len;
-
- return continue_hypercall_on_cpu(smp_processor_id(),
- microcode_update_helper, buffer);
-}
-
-static int __init microcode_init(void)
-{
- /*
- * At this point, all CPUs should have updated their microcode
- * via the early_microcode_* paths so free the microcode blob.
- */
- if ( ucode_blob.size )
- {
- bootstrap_map(NULL);
- ucode_blob.size = 0;
- ucode_blob.data = NULL;
- }
- else if ( ucode_mod.mod_end )
- {
- bootstrap_map(NULL);
- ucode_mod.mod_end = 0;
- }
-
- return 0;
-}
-__initcall(microcode_init);
-
-/* Load a cached update to current cpu */
-int microcode_update_one(bool start_update)
-{
- int err;
-
- if ( !microcode_ops )
- return -EOPNOTSUPP;
-
- microcode_ops->collect_cpu_info(&this_cpu(cpu_sig));
-
- if ( start_update && microcode_ops->start_update )
- {
- err = microcode_ops->start_update();
- if ( err )
- return err;
- }
-
- err = microcode_update_cpu(NULL);
-
- if ( microcode_ops->end_update_percpu )
- microcode_ops->end_update_percpu();
-
- return err;
-}
-
-/* BSP calls this function to parse ucode blob and then apply an update. */
-static int __init early_microcode_update_cpu(void)
-{
- int rc = 0;
- const void *data = NULL;
- size_t len;
- struct microcode_patch *patch;
-
- if ( ucode_blob.size )
- {
- len = ucode_blob.size;
- data = ucode_blob.data;
- }
- else if ( ucode_mod.mod_end )
- {
- len = ucode_mod.mod_end;
- data = bootstrap_map(&ucode_mod);
- }
-
- if ( !data )
- return -ENOMEM;
-
- patch = parse_blob(data, len);
- if ( IS_ERR(patch) )
- {
- printk(XENLOG_WARNING "Parsing microcode blob error %ld\n",
- PTR_ERR(patch));
- return PTR_ERR(patch);
- }
-
- if ( !patch )
- return -ENOENT;
-
- spin_lock(µcode_mutex);
- rc = microcode_update_cache(patch);
- spin_unlock(µcode_mutex);
- ASSERT(rc);
-
- return microcode_update_one(true);
-}
-
-int __init early_microcode_init(void)
-{
- int rc;
-
- rc = microcode_init_intel();
- if ( rc )
- return rc;
-
- rc = microcode_init_amd();
- if ( rc )
- return rc;
-
- if ( microcode_ops )
- {
- microcode_ops->collect_cpu_info(&this_cpu(cpu_sig));
-
- if ( ucode_mod.mod_end || ucode_blob.size )
- rc = early_microcode_update_cpu();
- }
-
- return rc;
-}
diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c
deleted file mode 100644
index bc7459416c..0000000000
--- a/xen/arch/x86/microcode_amd.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * AMD CPU Microcode Update Driver for Linux
- * Copyright (C) 2008 Advanced Micro Devices Inc.
- *
- * Author: Peter Oruba <peter.oruba@xxxxxxx>
- *
- * Based on work by:
- * Tigran Aivazian <tigran@xxxxxxxxxxxxxxxxxxxx>
- *
- * This driver allows to upgrade microcode on AMD
- * family 0x10 and later.
- *
- * Licensed unter the terms of the GNU General Public
- * License version 2. See file COPYING for details.
- */
-
-#include <xen/err.h>
-#include <xen/init.h>
-#include <xen/kernel.h>
-#include <xen/lib.h>
-#include <xen/sched.h>
-#include <xen/smp.h>
-#include <xen/spinlock.h>
-
-#include <asm/msr.h>
-#include <asm/processor.h>
-#include <asm/microcode.h>
-#include <asm/hvm/svm/svm.h>
-
-#define pr_debug(x...) ((void)0)
-
-#define CONT_HDR_SIZE 12
-#define SECTION_HDR_SIZE 8
-#define PATCH_HDR_SIZE 32
-
-struct __packed equiv_cpu_entry {
- uint32_t installed_cpu;
- uint32_t fixed_errata_mask;
- uint32_t fixed_errata_compare;
- uint16_t equiv_cpu;
- uint16_t reserved;
-};
-
-struct __packed microcode_header_amd {
- uint32_t data_code;
- uint32_t patch_id;
- uint8_t mc_patch_data_id[2];
- uint8_t mc_patch_data_len;
- uint8_t init_flag;
- uint32_t mc_patch_data_checksum;
- uint32_t nb_dev_id;
- uint32_t sb_dev_id;
- uint16_t processor_rev_id;
- uint8_t nb_rev_id;
- uint8_t sb_rev_id;
- uint8_t bios_api_rev;
- uint8_t reserved1[3];
- uint32_t match_reg[8];
-};
-
-#define UCODE_MAGIC 0x00414d44
-#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
-#define UCODE_UCODE_TYPE 0x00000001
-
-struct microcode_amd {
- void *mpb;
- size_t mpb_size;
- struct equiv_cpu_entry *equiv_cpu_table;
- size_t equiv_cpu_table_size;
-};
-
-struct mpbhdr {
- uint32_t type;
- uint32_t len;
- uint8_t data[];
-};
-
-/* See comment in start_update() for cases when this routine fails */
-static int collect_cpu_info(struct cpu_signature *csig)
-{
- unsigned int cpu = smp_processor_id();
- struct cpuinfo_x86 *c = &cpu_data[cpu];
-
- memset(csig, 0, sizeof(*csig));
-
- if ( (c->x86_vendor != X86_VENDOR_AMD) || (c->x86 < 0x10) )
- {
- printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
- cpu);
- return -EINVAL;
- }
-
- rdmsrl(MSR_AMD_PATCHLEVEL, csig->rev);
-
- pr_debug("microcode: CPU%d collect_cpu_info: patch_id=%#x\n",
- cpu, csig->rev);
-
- return 0;
-}
-
-static bool_t verify_patch_size(uint32_t patch_size)
-{
- uint32_t max_size;
-
-#define F1XH_MPB_MAX_SIZE 2048
-#define F14H_MPB_MAX_SIZE 1824
-#define F15H_MPB_MAX_SIZE 4096
-#define F16H_MPB_MAX_SIZE 3458
-#define F17H_MPB_MAX_SIZE 3200
-
- switch (boot_cpu_data.x86)
- {
- case 0x14:
- max_size = F14H_MPB_MAX_SIZE;
- break;
- case 0x15:
- max_size = F15H_MPB_MAX_SIZE;
- break;
- case 0x16:
- max_size = F16H_MPB_MAX_SIZE;
- break;
- case 0x17:
- max_size = F17H_MPB_MAX_SIZE;
- break;
- default:
- max_size = F1XH_MPB_MAX_SIZE;
- break;
- }
-
- return (patch_size <= max_size);
-}
-
-static bool_t find_equiv_cpu_id(const struct equiv_cpu_entry *equiv_cpu_table,
- unsigned int current_cpu_id,
- unsigned int *equiv_cpu_id)
-{
- unsigned int i;
-
- if ( !equiv_cpu_table )
- return 0;
-
- for ( i = 0; equiv_cpu_table[i].installed_cpu != 0; i++ )
- {
- if ( current_cpu_id == equiv_cpu_table[i].installed_cpu )
- {
- *equiv_cpu_id = equiv_cpu_table[i].equiv_cpu & 0xffff;
- return 1;
- }
- }
-
- return 0;
-}
-
-static enum microcode_match_result microcode_fits(
- const struct microcode_amd *mc_amd)
-{
- unsigned int cpu = smp_processor_id();
- const struct cpu_signature *sig = &per_cpu(cpu_sig, cpu);
- const struct microcode_header_amd *mc_header = mc_amd->mpb;
- const struct equiv_cpu_entry *equiv_cpu_table = mc_amd->equiv_cpu_table;
- unsigned int current_cpu_id;
- unsigned int equiv_cpu_id;
-
- current_cpu_id = cpuid_eax(0x00000001);
-
- if ( !find_equiv_cpu_id(equiv_cpu_table, current_cpu_id, &equiv_cpu_id) )
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/xen-changelog
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |