[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen staging] 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 = &microcode_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(&microcode_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(&microcode_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(&microcode_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(&microcode_mutex);
+    if ( microcode_cache &&
+         microcode_ops->compare_patch(patch, microcode_cache) != NEW_UCODE )
+    {
+        spin_unlock(&microcode_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(&microcode_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(&microcode_mutex);
+        microcode_update_cache(patch);
+        spin_unlock(&microcode_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(&microcode_mutex);
+    rc = microcode_update_cache(patch);
+    spin_unlock(&microcode_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 = &microcode_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(&microcode_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(&microcode_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(&microcode_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(&microcode_mutex);
-    if ( microcode_cache &&
-         microcode_ops->compare_patch(patch, microcode_cache) != NEW_UCODE )
-    {
-        spin_unlock(&microcode_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(&microcode_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(&microcode_mutex);
-        microcode_update_cache(patch);
-        spin_unlock(&microcode_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(&microcode_mutex);
-    rc = microcode_update_cache(patch);
-    spin_unlock(&microcode_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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.