diff mbox series

[v9,04/15] microcode: introduce a global cache of ucode patch

Message ID 1566177928-19114-5-git-send-email-chao.gao@intel.com (mailing list archive)
State Superseded
Headers show
Series improve late microcode loading | expand

Commit Message

Chao Gao Aug. 19, 2019, 1:25 a.m. UTC
to replace the current per-cpu cache 'uci->mc'.

With the assumption that all CPUs in the system have the same signature
(family, model, stepping and 'pf'), one microcode update matches with
one cpu should match with others. Having multiple microcode revisions
on different cpus would cause system unstable and should be avoided.
Hence, caching only one microcode update is good enough for all cases.

Introduce a global variable, microcode_cache, to store the newest
matching microcode update. Whenever we get a new valid microcode update,
its revision id is compared against that of the microcode update to
determine whether the "microcode_cache" needs to be replaced. And
this global cache is loaded to cpu in apply_microcode().

All operations on the cache is protected by 'microcode_mutex'.

Note that I deliberately avoid touching the old per-cpu cache ('uci->mc')
as I am going to remove it completely in the following patches. We copy
everything to create the new cache blob to avoid reusing some buffers
previously allocated for the old per-cpu cache. It is not so efficient,
but it is already corrected by a patch later in this series.

Signed-off-by: Chao Gao <chao.gao@intel.com>
---
Changes in v9:
 - on Intel side, ->compare_patch just checks the patch revision number.
 - explain why all buffers are copied in alloc_microcode_patch() in
 patch description.

Changes in v8:
 - Free generic wrapper struct in general code
 - Try to update cache as long as a patch covers current cpu. Previsouly,
 cache is updated only if the patch is newer than current update revision in
 the CPU. The small difference can work around a broken bios which only
 applies microcode update to BSP and software has to apply the same
 update to other CPUs.

Changes in v7:
 - reworked to cache only one microcode patch rather than a list of
 microcode patches.
---
 xen/arch/x86/microcode.c        | 39 ++++++++++++++++++
 xen/arch/x86/microcode_amd.c    | 90 +++++++++++++++++++++++++++++++++++++----
 xen/arch/x86/microcode_intel.c  | 73 ++++++++++++++++++++++++++-------
 xen/include/asm-x86/microcode.h | 17 ++++++++
 4 files changed, 197 insertions(+), 22 deletions(-)

Comments

Roger Pau Monné Aug. 22, 2019, 11:11 a.m. UTC | #1
On Mon, Aug 19, 2019 at 09:25:17AM +0800, Chao Gao wrote:
> to replace the current per-cpu cache 'uci->mc'.
> 
> With the assumption that all CPUs in the system have the same signature
> (family, model, stepping and 'pf'), one microcode update matches with
> one cpu should match with others. Having multiple microcode revisions
> on different cpus would cause system unstable and should be avoided.
> Hence, caching only one microcode update is good enough for all cases.
> 
> Introduce a global variable, microcode_cache, to store the newest
> matching microcode update. Whenever we get a new valid microcode update,
> its revision id is compared against that of the microcode update to
> determine whether the "microcode_cache" needs to be replaced. And
> this global cache is loaded to cpu in apply_microcode().
> 
> All operations on the cache is protected by 'microcode_mutex'.
> 
> Note that I deliberately avoid touching the old per-cpu cache ('uci->mc')
> as I am going to remove it completely in the following patches. We copy
> everything to create the new cache blob to avoid reusing some buffers
> previously allocated for the old per-cpu cache. It is not so efficient,
> but it is already corrected by a patch later in this series.
> 
> Signed-off-by: Chao Gao <chao.gao@intel.com>

Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>

I have some nits below, but I don't think they affect functionality in
anyway, hence my RB. It would be nice to get those fixed as follow-ups
if others think the current version is suitable for committing.

> ---
> Changes in v9:
>  - on Intel side, ->compare_patch just checks the patch revision number.
>  - explain why all buffers are copied in alloc_microcode_patch() in
>  patch description.
> 
> Changes in v8:
>  - Free generic wrapper struct in general code
>  - Try to update cache as long as a patch covers current cpu. Previsouly,
>  cache is updated only if the patch is newer than current update revision in
>  the CPU. The small difference can work around a broken bios which only
>  applies microcode update to BSP and software has to apply the same
>  update to other CPUs.
> 
> Changes in v7:
>  - reworked to cache only one microcode patch rather than a list of
>  microcode patches.
> ---
>  xen/arch/x86/microcode.c        | 39 ++++++++++++++++++
>  xen/arch/x86/microcode_amd.c    | 90 +++++++++++++++++++++++++++++++++++++----
>  xen/arch/x86/microcode_intel.c  | 73 ++++++++++++++++++++++++++-------
>  xen/include/asm-x86/microcode.h | 17 ++++++++
>  4 files changed, 197 insertions(+), 22 deletions(-)
> 
> diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c
> index 421d57e..0ecd2fd 100644
> --- a/xen/arch/x86/microcode.c
> +++ b/xen/arch/x86/microcode.c
> @@ -61,6 +61,9 @@ static struct ucode_mod_blob __initdata ucode_blob;
>   */
>  static bool_t __initdata ucode_scan;
>  
> +/* Protected by microcode_mutex */
> +static struct microcode_patch *microcode_cache;
> +
>  void __init microcode_set_module(unsigned int idx)
>  {
>      ucode_mod_idx = idx;
> @@ -262,6 +265,42 @@ int microcode_resume_cpu(unsigned int cpu)
>      return err;
>  }
>  
> +void microcode_free_patch(struct microcode_patch *microcode_patch)
> +{
> +    microcode_ops->free_patch(microcode_patch->mc);
> +    xfree(microcode_patch);
> +}
> +
> +const struct microcode_patch *microcode_get_cache(void)
> +{
> +    ASSERT(spin_is_locked(&microcode_mutex));
> +
> +    return microcode_cache;
> +}
> +
> +/* Return true if cache gets updated. Otherwise, return false */
> +bool microcode_update_cache(struct microcode_patch *patch)
> +{
> +

Nit: extra newline above.

> +    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;
> +}
> +
>  static int microcode_update_cpu(const void *buf, size_t size)
>  {
>      int err;
> diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c
> index 3db3555..30129ca 100644
> --- a/xen/arch/x86/microcode_amd.c
> +++ b/xen/arch/x86/microcode_amd.c
> @@ -190,24 +190,83 @@ static enum microcode_match_result microcode_fits(
>      return NEW_UCODE;
>  }
>  
> +static bool match_cpu(const struct microcode_patch *patch)
> +{
> +    if ( !patch )
> +        return false;
> +    return microcode_fits(patch->mc_amd, smp_processor_id()) == NEW_UCODE;
> +}
> +
> +static struct microcode_patch *alloc_microcode_patch(
> +    const struct microcode_amd *mc_amd)
> +{
> +    struct microcode_patch *microcode_patch = xmalloc(struct microcode_patch);
> +    struct microcode_amd *cache = xmalloc(struct microcode_amd);
> +    void *mpb = xmalloc_bytes(mc_amd->mpb_size);
> +    struct equiv_cpu_entry *equiv_cpu_table =
> +                                xmalloc_bytes(mc_amd->equiv_cpu_table_size);
> +
> +    if ( !microcode_patch || !cache || !mpb || !equiv_cpu_table )
> +    {
> +        xfree(microcode_patch);
> +        xfree(cache);
> +        xfree(mpb);
> +        xfree(equiv_cpu_table);
> +        return ERR_PTR(-ENOMEM);
> +    }
> +
> +    memcpy(mpb, mc_amd->mpb, mc_amd->mpb_size);
> +    cache->mpb = mpb;
> +    cache->mpb_size = mc_amd->mpb_size;
> +    memcpy(equiv_cpu_table, mc_amd->equiv_cpu_table,
> +           mc_amd->equiv_cpu_table_size);
> +    cache->equiv_cpu_table = equiv_cpu_table;
> +    cache->equiv_cpu_table_size = mc_amd->equiv_cpu_table_size;
> +    microcode_patch->mc_amd = cache;
> +
> +    return microcode_patch;
> +}
> +
> +static void free_patch(void *mc)
> +{
> +    struct microcode_amd *mc_amd = mc;
> +
> +    xfree(mc_amd->equiv_cpu_table);
> +    xfree(mc_amd->mpb);
> +    xfree(mc_amd);
> +}

It's asymmetric that alloc_microcode_patch allocates microcode_patch,
but free_patch doesn't free it. Not a big deal, but it would be good
to make this symmetric IMO.

> +
> +static enum microcode_match_result compare_patch(
> +    const struct microcode_patch *new, const struct microcode_patch *old)
> +{
> +    const struct microcode_amd *new_mc = new->mc_amd;
> +    const struct microcode_header_amd *new_header = new_mc->mpb;
> +    const struct microcode_amd *old_mc = old->mc_amd;
> +    const struct microcode_header_amd *old_header = old_mc->mpb;

The local variables new_mc/old_mc are used just one, and hence are
not really helpful IMO, you could just do:

const struct microcode_header_amd *new_header = new->mc_amd->mpb;
const struct microcode_header_amd *old_header = old->mc_amd->mpb;

Again, just a nit.

> +
> +    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 int apply_microcode(unsigned int cpu)
>  {
>      unsigned long flags;
>      struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
>      uint32_t rev;
> -    struct microcode_amd *mc_amd = uci->mc.mc_amd;
> -    struct microcode_header_amd *hdr;
>      int hw_err;
> +    const struct microcode_header_amd *hdr;
> +    const struct microcode_patch *patch = microcode_get_cache();
>  
>      /* We should bind the task to the CPU */
>      BUG_ON(raw_smp_processor_id() != cpu);
>  
> -    if ( mc_amd == NULL )
> +    if ( !match_cpu(patch) )
>          return -EINVAL;

Another nit, but !patch should get ENOENT rather than EINVAL IMO.

>  
> -    hdr = mc_amd->mpb;
> -    if ( hdr == NULL )
> -        return -EINVAL;
> +    hdr = patch->mc_amd->mpb;
>  
>      spin_lock_irqsave(&microcode_update_lock, flags);
>  
> @@ -496,7 +555,21 @@ static int cpu_request_microcode(unsigned int cpu, const void *buf,
>      while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
>                                                 &offset)) == 0 )
>      {
> -        if ( microcode_fits(mc_amd, cpu) == NEW_UCODE )
> +        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
> +
> +        if ( IS_ERR(new_patch) )
> +        {
> +            error = PTR_ERR(new_patch);
> +            break;
> +        }
> +
> +        /* Update cache if this patch covers current CPU */
> +        if ( microcode_fits(new_patch->mc_amd, cpu) != MIS_UCODE )
> +            microcode_update_cache(new_patch);
> +        else
> +            microcode_free_patch(new_patch);
> +
> +        if ( match_cpu(microcode_get_cache()) )
>          {
>              error = apply_microcode(cpu);
>              if ( error )
> @@ -640,6 +713,9 @@ static const struct microcode_ops microcode_amd_ops = {
>      .collect_cpu_info                 = collect_cpu_info,
>      .apply_microcode                  = apply_microcode,
>      .start_update                     = start_update,
> +    .free_patch                       = free_patch,
> +    .compare_patch                    = compare_patch,
> +    .match_cpu                        = match_cpu,
>  };
>  
>  int __init microcode_init_amd(void)
> diff --git a/xen/arch/x86/microcode_intel.c b/xen/arch/x86/microcode_intel.c
> index c185b5c..14485dc 100644
> --- a/xen/arch/x86/microcode_intel.c
> +++ b/xen/arch/x86/microcode_intel.c
> @@ -259,6 +259,31 @@ static int microcode_sanity_check(void *mc)
>      return 0;
>  }
>  
> +static bool match_cpu(const struct microcode_patch *patch)
> +{
> +    if ( !patch )
> +        return false;
> +
> +    return microcode_update_match(&patch->mc_intel->hdr,
> +                                  smp_processor_id()) == NEW_UCODE;
> +}
> +
> +static void free_patch(void *mc)
> +{
> +    xfree(mc);
> +}
> +
> +/*
> + * Both patches to compare are supposed to be applicable to local CPU.
> + * Just compare the revision number.
> + */
> +static enum microcode_match_result compare_patch(
> +    const struct microcode_patch *new, const struct microcode_patch *old)
> +{
> +    return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ?  NEW_UCODE :
> +                                                                OLD_UCODE;

Nit, the usual format in Xen would be:

return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ? NEW_UCODE
                                                         : OLD_UCODE;

> +}
> +
>  /*
>   * return 0 - no update found
>   * return 1 - found update
> @@ -269,10 +294,26 @@ static int get_matching_microcode(const void *mc, unsigned int cpu)
>      struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
>      const struct microcode_header_intel *mc_header = mc;
>      unsigned long total_size = get_totalsize(mc_header);
> -    void *new_mc;
> +    void *new_mc = xmalloc_bytes(total_size);
> +    struct microcode_patch *new_patch = xmalloc(struct microcode_patch);
>  
> -    if ( microcode_update_match(mc, cpu) != NEW_UCODE )
> +    if ( !new_patch || !new_mc )
> +    {
> +        xfree(new_patch);
> +        xfree(new_mc);
> +        return -ENOMEM;
> +    }
> +    memcpy(new_mc, mc, total_size);
> +    new_patch->mc_intel = new_mc;
> +
> +    /* Make sure that this patch covers current CPU */
> +    if ( microcode_update_match(mc, cpu) == MIS_UCODE )
> +    {
> +        microcode_free_patch(new_patch);
>          return 0;
> +    }

Nit: won't it be easier to do this check first and then allocate if
needed?

AFAICT new_mc or new_patch are not required by the
microcode_update_match call, and hence could be allocated after such
call. Maybe this ugliness will go away in following patches?

Thanks, Roger.
Jan Beulich Aug. 28, 2019, 3:21 p.m. UTC | #2
On 19.08.2019 03:25, Chao Gao wrote:
> +/* Return true if cache gets updated. Otherwise, return false */
> +bool microcode_update_cache(struct microcode_patch *patch)
> +{
> +
> +    ASSERT(spin_is_locked(&microcode_mutex));

Stray blank line ahead of this one.

> --- a/xen/arch/x86/microcode_intel.c
> +++ b/xen/arch/x86/microcode_intel.c
> @@ -259,6 +259,31 @@ static int microcode_sanity_check(void *mc)
>      return 0;
>  }
>  
> +static bool match_cpu(const struct microcode_patch *patch)
> +{
> +    if ( !patch )
> +        return false;
> +
> +    return microcode_update_match(&patch->mc_intel->hdr,
> +                                  smp_processor_id()) == NEW_UCODE;
> +}
> +
> +static void free_patch(void *mc)
> +{
> +    xfree(mc);
> +}
> +
> +/*
> + * Both patches to compare are supposed to be applicable to local CPU.
> + * Just compare the revision number.
> + */
> +static enum microcode_match_result compare_patch(
> +    const struct microcode_patch *new, const struct microcode_patch *old)
> +{
> +    return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ?  NEW_UCODE :
> +                                                                OLD_UCODE;
> +}

The comment ahead of the function is nice, but please move it
inside the function and accompany it by ASSERT()s checking what
the comment says.

> @@ -19,6 +27,11 @@ struct microcode_ops {
>      int (*collect_cpu_info)(unsigned int cpu, struct cpu_signature *csig);
>      int (*apply_microcode)(unsigned int cpu);
>      int (*start_update)(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);

I'm afraid indentation here doesn't really match our (sadly still
unwritten) conventions - it should be four more spaces than what
the previous (incomplete) line had.

Jan
Jan Beulich Aug. 29, 2019, 10:18 a.m. UTC | #3
On 19.08.2019 03:25, Chao Gao wrote:
> +static enum microcode_match_result compare_patch(
> +    const struct microcode_patch *new, const struct microcode_patch *old)
> +{
> +    return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ?  NEW_UCODE :
> +                                                                OLD_UCODE;

There's one too many blank after the ? here. Also we commonly align
the : under the ? in cases like this one.

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c
index 421d57e..0ecd2fd 100644
--- a/xen/arch/x86/microcode.c
+++ b/xen/arch/x86/microcode.c
@@ -61,6 +61,9 @@  static struct ucode_mod_blob __initdata ucode_blob;
  */
 static bool_t __initdata ucode_scan;
 
+/* Protected by microcode_mutex */
+static struct microcode_patch *microcode_cache;
+
 void __init microcode_set_module(unsigned int idx)
 {
     ucode_mod_idx = idx;
@@ -262,6 +265,42 @@  int microcode_resume_cpu(unsigned int cpu)
     return err;
 }
 
+void microcode_free_patch(struct microcode_patch *microcode_patch)
+{
+    microcode_ops->free_patch(microcode_patch->mc);
+    xfree(microcode_patch);
+}
+
+const struct microcode_patch *microcode_get_cache(void)
+{
+    ASSERT(spin_is_locked(&microcode_mutex));
+
+    return microcode_cache;
+}
+
+/* Return true if cache gets updated. Otherwise, return false */
+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;
+}
+
 static int microcode_update_cpu(const void *buf, size_t size)
 {
     int err;
diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c
index 3db3555..30129ca 100644
--- a/xen/arch/x86/microcode_amd.c
+++ b/xen/arch/x86/microcode_amd.c
@@ -190,24 +190,83 @@  static enum microcode_match_result microcode_fits(
     return NEW_UCODE;
 }
 
+static bool match_cpu(const struct microcode_patch *patch)
+{
+    if ( !patch )
+        return false;
+    return microcode_fits(patch->mc_amd, smp_processor_id()) == NEW_UCODE;
+}
+
+static struct microcode_patch *alloc_microcode_patch(
+    const struct microcode_amd *mc_amd)
+{
+    struct microcode_patch *microcode_patch = xmalloc(struct microcode_patch);
+    struct microcode_amd *cache = xmalloc(struct microcode_amd);
+    void *mpb = xmalloc_bytes(mc_amd->mpb_size);
+    struct equiv_cpu_entry *equiv_cpu_table =
+                                xmalloc_bytes(mc_amd->equiv_cpu_table_size);
+
+    if ( !microcode_patch || !cache || !mpb || !equiv_cpu_table )
+    {
+        xfree(microcode_patch);
+        xfree(cache);
+        xfree(mpb);
+        xfree(equiv_cpu_table);
+        return ERR_PTR(-ENOMEM);
+    }
+
+    memcpy(mpb, mc_amd->mpb, mc_amd->mpb_size);
+    cache->mpb = mpb;
+    cache->mpb_size = mc_amd->mpb_size;
+    memcpy(equiv_cpu_table, mc_amd->equiv_cpu_table,
+           mc_amd->equiv_cpu_table_size);
+    cache->equiv_cpu_table = equiv_cpu_table;
+    cache->equiv_cpu_table_size = mc_amd->equiv_cpu_table_size;
+    microcode_patch->mc_amd = cache;
+
+    return microcode_patch;
+}
+
+static void free_patch(void *mc)
+{
+    struct microcode_amd *mc_amd = mc;
+
+    xfree(mc_amd->equiv_cpu_table);
+    xfree(mc_amd->mpb);
+    xfree(mc_amd);
+}
+
+static enum microcode_match_result compare_patch(
+    const struct microcode_patch *new, const struct microcode_patch *old)
+{
+    const struct microcode_amd *new_mc = new->mc_amd;
+    const struct microcode_header_amd *new_header = new_mc->mpb;
+    const struct microcode_amd *old_mc = old->mc_amd;
+    const struct microcode_header_amd *old_header = old_mc->mpb;
+
+    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 int apply_microcode(unsigned int cpu)
 {
     unsigned long flags;
     struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
     uint32_t rev;
-    struct microcode_amd *mc_amd = uci->mc.mc_amd;
-    struct microcode_header_amd *hdr;
     int hw_err;
+    const struct microcode_header_amd *hdr;
+    const struct microcode_patch *patch = microcode_get_cache();
 
     /* We should bind the task to the CPU */
     BUG_ON(raw_smp_processor_id() != cpu);
 
-    if ( mc_amd == NULL )
+    if ( !match_cpu(patch) )
         return -EINVAL;
 
-    hdr = mc_amd->mpb;
-    if ( hdr == NULL )
-        return -EINVAL;
+    hdr = patch->mc_amd->mpb;
 
     spin_lock_irqsave(&microcode_update_lock, flags);
 
@@ -496,7 +555,21 @@  static int cpu_request_microcode(unsigned int cpu, const void *buf,
     while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
                                                &offset)) == 0 )
     {
-        if ( microcode_fits(mc_amd, cpu) == NEW_UCODE )
+        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
+
+        if ( IS_ERR(new_patch) )
+        {
+            error = PTR_ERR(new_patch);
+            break;
+        }
+
+        /* Update cache if this patch covers current CPU */
+        if ( microcode_fits(new_patch->mc_amd, cpu) != MIS_UCODE )
+            microcode_update_cache(new_patch);
+        else
+            microcode_free_patch(new_patch);
+
+        if ( match_cpu(microcode_get_cache()) )
         {
             error = apply_microcode(cpu);
             if ( error )
@@ -640,6 +713,9 @@  static const struct microcode_ops microcode_amd_ops = {
     .collect_cpu_info                 = collect_cpu_info,
     .apply_microcode                  = apply_microcode,
     .start_update                     = start_update,
+    .free_patch                       = free_patch,
+    .compare_patch                    = compare_patch,
+    .match_cpu                        = match_cpu,
 };
 
 int __init microcode_init_amd(void)
diff --git a/xen/arch/x86/microcode_intel.c b/xen/arch/x86/microcode_intel.c
index c185b5c..14485dc 100644
--- a/xen/arch/x86/microcode_intel.c
+++ b/xen/arch/x86/microcode_intel.c
@@ -259,6 +259,31 @@  static int microcode_sanity_check(void *mc)
     return 0;
 }
 
+static bool match_cpu(const struct microcode_patch *patch)
+{
+    if ( !patch )
+        return false;
+
+    return microcode_update_match(&patch->mc_intel->hdr,
+                                  smp_processor_id()) == NEW_UCODE;
+}
+
+static void free_patch(void *mc)
+{
+    xfree(mc);
+}
+
+/*
+ * Both patches to compare are supposed to be applicable to local CPU.
+ * Just compare the revision number.
+ */
+static enum microcode_match_result compare_patch(
+    const struct microcode_patch *new, const struct microcode_patch *old)
+{
+    return (new->mc_intel->hdr.rev > old->mc_intel->hdr.rev) ?  NEW_UCODE :
+                                                                OLD_UCODE;
+}
+
 /*
  * return 0 - no update found
  * return 1 - found update
@@ -269,10 +294,26 @@  static int get_matching_microcode(const void *mc, unsigned int cpu)
     struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
     const struct microcode_header_intel *mc_header = mc;
     unsigned long total_size = get_totalsize(mc_header);
-    void *new_mc;
+    void *new_mc = xmalloc_bytes(total_size);
+    struct microcode_patch *new_patch = xmalloc(struct microcode_patch);
 
-    if ( microcode_update_match(mc, cpu) != NEW_UCODE )
+    if ( !new_patch || !new_mc )
+    {
+        xfree(new_patch);
+        xfree(new_mc);
+        return -ENOMEM;
+    }
+    memcpy(new_mc, mc, total_size);
+    new_patch->mc_intel = new_mc;
+
+    /* Make sure that this patch covers current CPU */
+    if ( microcode_update_match(mc, cpu) == MIS_UCODE )
+    {
+        microcode_free_patch(new_patch);
         return 0;
+    }
+
+    microcode_update_cache(new_patch);
 
     pr_debug("microcode: CPU%d found a matching microcode update with"
              " version %#x (current=%#x)\n",
@@ -297,18 +338,22 @@  static int apply_microcode(unsigned int cpu)
     unsigned int val[2];
     unsigned int cpu_num = raw_smp_processor_id();
     struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu_num);
+    const struct microcode_intel *mc_intel;
+    const struct microcode_patch *patch = microcode_get_cache();
 
     /* We should bind the task to the CPU */
     BUG_ON(cpu_num != cpu);
 
-    if ( uci->mc.mc_intel == NULL )
+    if ( !match_cpu(patch) )
         return -EINVAL;
 
+    mc_intel = patch->mc_intel;
+
     /* serialize access to the physical write to MSR 0x79 */
     spin_lock_irqsave(&microcode_update_lock, flags);
 
     /* write microcode via MSR 0x79 */
-    wrmsrl(MSR_IA32_UCODE_WRITE, (unsigned long)uci->mc.mc_intel->bits);
+    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 */
@@ -319,19 +364,17 @@  static int apply_microcode(unsigned int cpu)
     val[1] = (uint32_t)(msr_content >> 32);
 
     spin_unlock_irqrestore(&microcode_update_lock, flags);
-    if ( val[1] != uci->mc.mc_intel->hdr.rev )
+    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,
-               uci->cpu_sig.rev, uci->mc.mc_intel->hdr.rev, val[1]);
+               uci->cpu_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, uci->cpu_sig.rev, val[1],
-           uci->mc.mc_intel->hdr.year,
-           uci->mc.mc_intel->hdr.month,
-           uci->mc.mc_intel->hdr.day);
+           cpu_num, uci->cpu_sig.rev, val[1], mc_intel->hdr.year,
+           mc_intel->hdr.month, mc_intel->hdr.day);
     uci->cpu_sig.rev = val[1];
 
     return 0;
@@ -371,7 +414,6 @@  static int cpu_request_microcode(unsigned int cpu, const void *buf,
     long offset = 0;
     int error = 0;
     void *mc;
-    unsigned int matching_count = 0;
 
     /* We should bind the task to the CPU */
     BUG_ON(cpu != raw_smp_processor_id());
@@ -389,10 +431,8 @@  static int cpu_request_microcode(unsigned int cpu, const void *buf,
          * lets keep searching till the latest version
          */
         if ( error == 1 )
-        {
-            matching_count++;
             error = 0;
-        }
+
         xfree(mc);
     }
     if ( offset > 0 )
@@ -400,7 +440,7 @@  static int cpu_request_microcode(unsigned int cpu, const void *buf,
     if ( offset < 0 )
         error = offset;
 
-    if ( !error && matching_count )
+    if ( !error && match_cpu(microcode_get_cache()) )
         error = apply_microcode(cpu);
 
     return error;
@@ -416,6 +456,9 @@  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)
diff --git a/xen/include/asm-x86/microcode.h b/xen/include/asm-x86/microcode.h
index 882f560..42949b1 100644
--- a/xen/include/asm-x86/microcode.h
+++ b/xen/include/asm-x86/microcode.h
@@ -12,6 +12,14 @@  enum microcode_match_result {
 struct cpu_signature;
 struct ucode_cpu_info;
 
+struct microcode_patch {
+    union {
+        struct microcode_intel *mc_intel;
+        struct microcode_amd *mc_amd;
+        void *mc;
+    };
+};
+
 struct microcode_ops {
     int (*microcode_resume_match)(unsigned int cpu, const void *mc);
     int (*cpu_request_microcode)(unsigned int cpu, const void *buf,
@@ -19,6 +27,11 @@  struct microcode_ops {
     int (*collect_cpu_info)(unsigned int cpu, struct cpu_signature *csig);
     int (*apply_microcode)(unsigned int cpu);
     int (*start_update)(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);
 };
 
 struct cpu_signature {
@@ -39,4 +52,8 @@  struct ucode_cpu_info {
 DECLARE_PER_CPU(struct ucode_cpu_info, ucode_cpu_info);
 extern const struct microcode_ops *microcode_ops;
 
+const struct microcode_patch *microcode_get_cache(void);
+bool microcode_update_cache(struct microcode_patch *patch);
+void microcode_free_patch(struct microcode_patch *patch);
+
 #endif /* ASM_X86__MICROCODE_H */