diff mbox series

[v9,12/15] microcode: reduce memory allocation and copy when creating a patch

Message ID 1566177928-19114-13-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 create a microcode patch from a vendor-specific update,
allocate_microcode_patch() copied everything from the update.
It is not efficient. Essentially, we just need to go through
ucodes in the blob, find the one with the newest revision and
install it into the microcode_patch. In the process, buffers
like mc_amd, equiv_cpu_table (on AMD side), and mc (on Intel
side) can be reused. microcode_patch now is allocated after
it is sure that there is a matching ucode.

Signed-off-by: Chao Gao <chao.gao@intel.com>
---
Changes in v9:
 - new
---
 xen/arch/x86/microcode_amd.c   | 99 +++++++++++++++---------------------------
 xen/arch/x86/microcode_intel.c | 65 ++++++++++-----------------
 2 files changed, 58 insertions(+), 106 deletions(-)

Comments

Roger Pau Monné Aug. 23, 2019, 8:11 a.m. UTC | #1
On Mon, Aug 19, 2019 at 09:25:25AM +0800, Chao Gao wrote:
> To create a microcode patch from a vendor-specific update,
> allocate_microcode_patch() copied everything from the update.
> It is not efficient. Essentially, we just need to go through
> ucodes in the blob, find the one with the newest revision and
> install it into the microcode_patch. In the process, buffers
> like mc_amd, equiv_cpu_table (on AMD side), and mc (on Intel
> side) can be reused. microcode_patch now is allocated after
> it is sure that there is a matching ucode.

Oh, I think this answers my question on a previous patch.

For future series it would be nice to avoid so many rewrites in the
same series, alloc_microcode_patch is already modified in a previous
patch, just to be removed here. It also makes it harder to follow
what's going on.

> 
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> ---
> Changes in v9:
>  - new
> ---
>  xen/arch/x86/microcode_amd.c   | 99 +++++++++++++++---------------------------
>  xen/arch/x86/microcode_intel.c | 65 ++++++++++-----------------
>  2 files changed, 58 insertions(+), 106 deletions(-)
> 
> diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c
> index 6353323..ec1c2eb 100644
> --- a/xen/arch/x86/microcode_amd.c
> +++ b/xen/arch/x86/microcode_amd.c
> @@ -194,36 +194,6 @@ static bool match_cpu(const struct microcode_patch *patch)
>      return patch && (microcode_fits(patch->mc_amd) == 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;
> @@ -320,18 +290,10 @@ static int get_ucode_from_buffer_amd(
>          return -EINVAL;
>      }
>  
> -    if ( mc_amd->mpb_size < mpbuf->len )
> -    {
> -        if ( mc_amd->mpb )
> -        {
> -            xfree(mc_amd->mpb);
> -            mc_amd->mpb_size = 0;
> -        }
> -        mc_amd->mpb = xmalloc_bytes(mpbuf->len);
> -        if ( mc_amd->mpb == NULL )
> -            return -ENOMEM;
> -        mc_amd->mpb_size = mpbuf->len;
> -    }
> +    mc_amd->mpb = xmalloc_bytes(mpbuf->len);
> +    if ( mc_amd->mpb == NULL )

Nit:

if ( !mc_amd->mpb )

is the usual idiom used in Xen.

> +        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",
> @@ -451,8 +413,9 @@ 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;
> +    size_t offset = 0, saved_size = 0;
>      int error = 0;
>      unsigned int current_cpu_id;
>      unsigned int equiv_cpu_id;
> @@ -542,29 +505,21 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
>      while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
>                                                 &offset)) == 0 )
>      {
> -        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
> -
> -        if ( IS_ERR(new_patch) )
> -        {
> -            error = PTR_ERR(new_patch);
> -            break;
> -        }
> -
>          /*
> -         * If the new patch covers current CPU, compare patches and store the
> +         * If the new ucode covers current CPU, compare ucodes and store the
>           * one with higher revision.
>           */
> -        if ( (microcode_fits(new_patch->mc_amd) != MIS_UCODE) &&
> -             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
> +#define REV_ID(mpb) (((struct microcode_header_amd *)(mpb))->processor_rev_id)
> +        if ( (microcode_fits(mc_amd) != MIS_UCODE) &&
> +             (!saved || (REV_ID(mc_amd->mpb) > REV_ID(saved))) )
> +#undef REV_ID
>          {
> -            struct microcode_patch *tmp = patch;
> -
> -            patch = new_patch;
> -            new_patch = tmp;
> +            xfree(saved);
> +            saved = mc_amd->mpb;
> +            saved_size = mc_amd->mpb_size;
>          }
> -
> -        if ( new_patch )
> -            microcode_free_patch(new_patch);
> +        else
> +            xfree(mc_amd->mpb);
>  
>          if ( offset >= bufsize )
>              break;
> @@ -593,9 +548,25 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
>               *(const uint32_t *)(buf + offset) == UCODE_MAGIC )
>              break;
>      }
> -    xfree(mc_amd->mpb);
> -    xfree(mc_amd->equiv_cpu_table);
> -    xfree(mc_amd);
> +
> +    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
> +    {
> +        mc_amd->mpb = NULL;

What's the point in setting mpb to NULL if you are just going to free
mc_amd below?

Also, I'm not sure I understand why you need to free mc_amd, isn't
this buff memory that should be freed by the caller?

ie: in the Intel counterpart below you don't seem to free the mc
cursor used for the get_next_ucode_from_buffer loop.

> +        free_patch(mc_amd);
> +    }
>  
>    out:
>      if ( error && !patch )
> diff --git a/xen/arch/x86/microcode_intel.c b/xen/arch/x86/microcode_intel.c
> index 96b38f8..ae5759f 100644
> --- a/xen/arch/x86/microcode_intel.c
> +++ b/xen/arch/x86/microcode_intel.c
> @@ -282,25 +282,6 @@ static enum microcode_match_result compare_patch(
>                                                                  OLD_UCODE;
>  }
>  
> -static struct microcode_patch *alloc_microcode_patch(
> -    const struct microcode_header_intel *mc_header)
> -{
> -    unsigned long total_size = get_totalsize(mc_header);
> -    void *new_mc = xmalloc_bytes(total_size);
> -    struct microcode_patch *new_patch = xmalloc(struct microcode_patch);
> -
> -    if ( !new_patch || !new_mc )
> -    {
> -        xfree(new_patch);
> -        xfree(new_mc);
> -        return ERR_PTR(-ENOMEM);
> -    }
> -    memcpy(new_mc, mc_header, total_size);
> -    new_patch->mc_intel = new_mc;
> -
> -    return new_patch;
> -}
> -
>  static int apply_microcode(const struct microcode_patch *patch)
>  {
>      unsigned long flags;
> @@ -379,47 +360,47 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
>  {
>      long offset = 0;
>      int error = 0;
> -    void *mc;
> +    struct microcode_intel *mc, *saved = NULL;
>      struct microcode_patch *patch = NULL;
>  
> -    while ( (offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) > 0 )
> +    while ( (offset = get_next_ucode_from_buffer((void **)&mc, buf,
> +                                                 size, offset)) > 0 )
>      {
> -        struct microcode_patch *new_patch;
> -
>          error = microcode_sanity_check(mc);
>          if ( error )
> -            break;
> -
> -        new_patch = alloc_microcode_patch(mc);
> -        if ( IS_ERR(new_patch) )
>          {
> -            error = PTR_ERR(new_patch);
> +            xfree(mc);
>              break;
>          }
>  
>          /*
> -         * If the new patch covers current CPU, compare patches and store the
> +         * If the new update covers current CPU, compare updates and store the
>           * one with higher revision.
>           */
> -        if ( (microcode_update_match(&new_patch->mc_intel->hdr) != MIS_UCODE) &&
> -             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
> +        if ( (microcode_update_match(&mc->hdr) != MIS_UCODE) &&
> +             (!saved || (mc->hdr.rev > saved->hdr.rev)) )
>          {
> -            struct microcode_patch *tmp = patch;
> -
> -            patch = new_patch;
> -            new_patch = tmp;
> +            xfree(saved);
> +            saved = mc;
>          }
> -
> -        if ( new_patch )
> -            microcode_free_patch(new_patch);
> -
> -        xfree(mc);
> +        else
> +            xfree(mc);
>      }
> -    if ( offset > 0 )
> -        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;
> +        }
> +    }
> +

The above code looks suspiciously similar to the AMD
cpu_request_microcode counterpart, which makes me think it could be
further abstracted in order to reduce this duplication. In any case,
this can be done in a follow up patch.

Thanks, Roger.
Chao Gao Aug. 26, 2019, 7:03 a.m. UTC | #2
On Fri, Aug 23, 2019 at 10:11:21AM +0200, Roger Pau Monné wrote:
>On Mon, Aug 19, 2019 at 09:25:25AM +0800, Chao Gao wrote:
>> To create a microcode patch from a vendor-specific update,
>> allocate_microcode_patch() copied everything from the update.
>> It is not efficient. Essentially, we just need to go through
>> ucodes in the blob, find the one with the newest revision and
>> install it into the microcode_patch. In the process, buffers
>> like mc_amd, equiv_cpu_table (on AMD side), and mc (on Intel
>> side) can be reused. microcode_patch now is allocated after
>> it is sure that there is a matching ucode.
>
>Oh, I think this answers my question on a previous patch.
>
>For future series it would be nice to avoid so many rewrites in the
>same series, alloc_microcode_patch is already modified in a previous
>patch, just to be removed here. It also makes it harder to follow
>what's going on.

Got it. This patch is added in this new version. And some trivial
patches already got reviewed-by. So I don't merge it with them.

>>      while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
>>                                                 &offset)) == 0 )
>>      {
>> -        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
>> -
>> -        if ( IS_ERR(new_patch) )
>> -        {
>> -            error = PTR_ERR(new_patch);
>> -            break;
>> -        }
>> -
>>          /*
>> -         * If the new patch covers current CPU, compare patches and store the
>> +         * If the new ucode covers current CPU, compare ucodes and store the
>>           * one with higher revision.
>>           */
>> -        if ( (microcode_fits(new_patch->mc_amd) != MIS_UCODE) &&
>> -             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
>> +#define REV_ID(mpb) (((struct microcode_header_amd *)(mpb))->processor_rev_id)
>> +        if ( (microcode_fits(mc_amd) != MIS_UCODE) &&
>> +             (!saved || (REV_ID(mc_amd->mpb) > REV_ID(saved))) )
>> +#undef REV_ID
>>          {
>> -            struct microcode_patch *tmp = patch;
>> -
>> -            patch = new_patch;
>> -            new_patch = tmp;
>> +            xfree(saved);
>> +            saved = mc_amd->mpb;
>> +            saved_size = mc_amd->mpb_size;
>>          }
>> -
>> -        if ( new_patch )
>> -            microcode_free_patch(new_patch);
>> +        else
>> +            xfree(mc_amd->mpb);

It might be better to move 'mc_amd->mpb = NULL' here.

>>  
>>          if ( offset >= bufsize )
>>              break;
>> @@ -593,9 +548,25 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
>>               *(const uint32_t *)(buf + offset) == UCODE_MAGIC )
>>              break;
>>      }
>> -    xfree(mc_amd->mpb);
>> -    xfree(mc_amd->equiv_cpu_table);
>> -    xfree(mc_amd);
>> +
>> +    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
>> +    {
>> +        mc_amd->mpb = NULL;
>
>What's the point in setting mpb to NULL if you are just going to free
>mc_amd below?

To avoid double free here. mc_amd->mpb is always freed or saved.
And here we want to free mc_amd itself and mc_amd->equiv_cpu_table.

>
>Also, I'm not sure I understand why you need to free mc_amd, isn't
>this buff memory that should be freed by the caller?

But mc_amd is allocated in this function.

>
>ie: in the Intel counterpart below you don't seem to free the mc
>cursor used for the get_next_ucode_from_buffer loop.

'mc' is saved if it is newer than current patch stored in 'saved'.
Otherwise 'mc' is freed immediately. So we don't need to free it
again after the while loop.

Thanks
Chao
Roger Pau Monné Aug. 26, 2019, 8:11 a.m. UTC | #3
On Mon, Aug 26, 2019 at 03:03:22PM +0800, Chao Gao wrote:
> On Fri, Aug 23, 2019 at 10:11:21AM +0200, Roger Pau Monné wrote:
> >On Mon, Aug 19, 2019 at 09:25:25AM +0800, Chao Gao wrote:
> >> To create a microcode patch from a vendor-specific update,
> >> allocate_microcode_patch() copied everything from the update.
> >> It is not efficient. Essentially, we just need to go through
> >> ucodes in the blob, find the one with the newest revision and
> >> install it into the microcode_patch. In the process, buffers
> >> like mc_amd, equiv_cpu_table (on AMD side), and mc (on Intel
> >> side) can be reused. microcode_patch now is allocated after
> >> it is sure that there is a matching ucode.
> >
> >Oh, I think this answers my question on a previous patch.
> >
> >For future series it would be nice to avoid so many rewrites in the
> >same series, alloc_microcode_patch is already modified in a previous
> >patch, just to be removed here. It also makes it harder to follow
> >what's going on.
> 
> Got it. This patch is added in this new version. And some trivial
> patches already got reviewed-by. So I don't merge it with them.
> 
> >>      while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
> >>                                                 &offset)) == 0 )
> >>      {
> >> -        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
> >> -
> >> -        if ( IS_ERR(new_patch) )
> >> -        {
> >> -            error = PTR_ERR(new_patch);
> >> -            break;
> >> -        }
> >> -
> >>          /*
> >> -         * If the new patch covers current CPU, compare patches and store the
> >> +         * If the new ucode covers current CPU, compare ucodes and store the
> >>           * one with higher revision.
> >>           */
> >> -        if ( (microcode_fits(new_patch->mc_amd) != MIS_UCODE) &&
> >> -             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
> >> +#define REV_ID(mpb) (((struct microcode_header_amd *)(mpb))->processor_rev_id)
> >> +        if ( (microcode_fits(mc_amd) != MIS_UCODE) &&
> >> +             (!saved || (REV_ID(mc_amd->mpb) > REV_ID(saved))) )
> >> +#undef REV_ID
> >>          {
> >> -            struct microcode_patch *tmp = patch;
> >> -
> >> -            patch = new_patch;
> >> -            new_patch = tmp;
> >> +            xfree(saved);
> >> +            saved = mc_amd->mpb;
> >> +            saved_size = mc_amd->mpb_size;
> >>          }
> >> -
> >> -        if ( new_patch )
> >> -            microcode_free_patch(new_patch);
> >> +        else
> >> +            xfree(mc_amd->mpb);
> 
> It might be better to move 'mc_amd->mpb = NULL' here.
> 
> >>  
> >>          if ( offset >= bufsize )
> >>              break;
> >> @@ -593,9 +548,25 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
> >>               *(const uint32_t *)(buf + offset) == UCODE_MAGIC )
> >>              break;
> >>      }
> >> -    xfree(mc_amd->mpb);
> >> -    xfree(mc_amd->equiv_cpu_table);
> >> -    xfree(mc_amd);
> >> +
> >> +    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
> >> +    {
> >> +        mc_amd->mpb = NULL;
> >
> >What's the point in setting mpb to NULL if you are just going to free
> >mc_amd below?
> 
> To avoid double free here. mc_amd->mpb is always freed or saved.
> And here we want to free mc_amd itself and mc_amd->equiv_cpu_table.

But there's no chance of a double free here, since you are freeing
mc_amd in the line below after setting mpb = NULL.

I think it would make sense to set mpb = NULL after freeing it inside
the loop.

With that you can add my:

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

> >
> >Also, I'm not sure I understand why you need to free mc_amd, isn't
> >this buff memory that should be freed by the caller?
> 
> But mc_amd is allocated in this function.
> 
> >
> >ie: in the Intel counterpart below you don't seem to free the mc
> >cursor used for the get_next_ucode_from_buffer loop.
> 
> 'mc' is saved if it is newer than current patch stored in 'saved'.
> Otherwise 'mc' is freed immediately. So we don't need to free it
> again after the while loop.

Ack, thanks!
Jan Beulich Aug. 29, 2019, 10:47 a.m. UTC | #4
On 19.08.2019 03:25, Chao Gao wrote:
> @@ -542,29 +505,21 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
>      while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
>                                                 &offset)) == 0 )
>      {
> -        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
> -
> -        if ( IS_ERR(new_patch) )
> -        {
> -            error = PTR_ERR(new_patch);
> -            break;
> -        }
> -
>          /*
> -         * If the new patch covers current CPU, compare patches and store the
> +         * If the new ucode covers current CPU, compare ucodes and store the
>           * one with higher revision.
>           */
> -        if ( (microcode_fits(new_patch->mc_amd) != MIS_UCODE) &&
> -             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
> +#define REV_ID(mpb) (((struct microcode_header_amd *)(mpb))->processor_rev_id)
> +        if ( (microcode_fits(mc_amd) != MIS_UCODE) &&
> +             (!saved || (REV_ID(mc_amd->mpb) > REV_ID(saved))) )
> +#undef REV_ID

I'm not happy with this helper #define, the more that "saved" already is
of the correct type. compare_patch() in reality only acts on the header,
so I'd suggest having that function forward to a new compare_header()
(or some other suitable name) and use that new function here as well.

> @@ -379,47 +360,47 @@ static struct microcode_patch *cpu_request_microcode(const void *buf,
>  {
>      long offset = 0;
>      int error = 0;
> -    void *mc;
> +    struct microcode_intel *mc, *saved = NULL;
>      struct microcode_patch *patch = NULL;
>  
> -    while ( (offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) > 0 )
> +    while ( (offset = get_next_ucode_from_buffer((void **)&mc, buf,

Casts like this make me rather nervous. Please see about getting rid of
it (by using a union or a 2nd local variable).

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c
index 6353323..ec1c2eb 100644
--- a/xen/arch/x86/microcode_amd.c
+++ b/xen/arch/x86/microcode_amd.c
@@ -194,36 +194,6 @@  static bool match_cpu(const struct microcode_patch *patch)
     return patch && (microcode_fits(patch->mc_amd) == 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;
@@ -320,18 +290,10 @@  static int get_ucode_from_buffer_amd(
         return -EINVAL;
     }
 
-    if ( mc_amd->mpb_size < mpbuf->len )
-    {
-        if ( mc_amd->mpb )
-        {
-            xfree(mc_amd->mpb);
-            mc_amd->mpb_size = 0;
-        }
-        mc_amd->mpb = xmalloc_bytes(mpbuf->len);
-        if ( mc_amd->mpb == NULL )
-            return -ENOMEM;
-        mc_amd->mpb_size = mpbuf->len;
-    }
+    mc_amd->mpb = xmalloc_bytes(mpbuf->len);
+    if ( mc_amd->mpb == NULL )
+        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",
@@ -451,8 +413,9 @@  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;
+    size_t offset = 0, saved_size = 0;
     int error = 0;
     unsigned int current_cpu_id;
     unsigned int equiv_cpu_id;
@@ -542,29 +505,21 @@  static struct microcode_patch *cpu_request_microcode(const void *buf,
     while ( (error = get_ucode_from_buffer_amd(mc_amd, buf, bufsize,
                                                &offset)) == 0 )
     {
-        struct microcode_patch *new_patch = alloc_microcode_patch(mc_amd);
-
-        if ( IS_ERR(new_patch) )
-        {
-            error = PTR_ERR(new_patch);
-            break;
-        }
-
         /*
-         * If the new patch covers current CPU, compare patches and store the
+         * If the new ucode covers current CPU, compare ucodes and store the
          * one with higher revision.
          */
-        if ( (microcode_fits(new_patch->mc_amd) != MIS_UCODE) &&
-             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
+#define REV_ID(mpb) (((struct microcode_header_amd *)(mpb))->processor_rev_id)
+        if ( (microcode_fits(mc_amd) != MIS_UCODE) &&
+             (!saved || (REV_ID(mc_amd->mpb) > REV_ID(saved))) )
+#undef REV_ID
         {
-            struct microcode_patch *tmp = patch;
-
-            patch = new_patch;
-            new_patch = tmp;
+            xfree(saved);
+            saved = mc_amd->mpb;
+            saved_size = mc_amd->mpb_size;
         }
-
-        if ( new_patch )
-            microcode_free_patch(new_patch);
+        else
+            xfree(mc_amd->mpb);
 
         if ( offset >= bufsize )
             break;
@@ -593,9 +548,25 @@  static struct microcode_patch *cpu_request_microcode(const void *buf,
              *(const uint32_t *)(buf + offset) == UCODE_MAGIC )
             break;
     }
-    xfree(mc_amd->mpb);
-    xfree(mc_amd->equiv_cpu_table);
-    xfree(mc_amd);
+
+    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
+    {
+        mc_amd->mpb = NULL;
+        free_patch(mc_amd);
+    }
 
   out:
     if ( error && !patch )
diff --git a/xen/arch/x86/microcode_intel.c b/xen/arch/x86/microcode_intel.c
index 96b38f8..ae5759f 100644
--- a/xen/arch/x86/microcode_intel.c
+++ b/xen/arch/x86/microcode_intel.c
@@ -282,25 +282,6 @@  static enum microcode_match_result compare_patch(
                                                                 OLD_UCODE;
 }
 
-static struct microcode_patch *alloc_microcode_patch(
-    const struct microcode_header_intel *mc_header)
-{
-    unsigned long total_size = get_totalsize(mc_header);
-    void *new_mc = xmalloc_bytes(total_size);
-    struct microcode_patch *new_patch = xmalloc(struct microcode_patch);
-
-    if ( !new_patch || !new_mc )
-    {
-        xfree(new_patch);
-        xfree(new_mc);
-        return ERR_PTR(-ENOMEM);
-    }
-    memcpy(new_mc, mc_header, total_size);
-    new_patch->mc_intel = new_mc;
-
-    return new_patch;
-}
-
 static int apply_microcode(const struct microcode_patch *patch)
 {
     unsigned long flags;
@@ -379,47 +360,47 @@  static struct microcode_patch *cpu_request_microcode(const void *buf,
 {
     long offset = 0;
     int error = 0;
-    void *mc;
+    struct microcode_intel *mc, *saved = NULL;
     struct microcode_patch *patch = NULL;
 
-    while ( (offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) > 0 )
+    while ( (offset = get_next_ucode_from_buffer((void **)&mc, buf,
+                                                 size, offset)) > 0 )
     {
-        struct microcode_patch *new_patch;
-
         error = microcode_sanity_check(mc);
         if ( error )
-            break;
-
-        new_patch = alloc_microcode_patch(mc);
-        if ( IS_ERR(new_patch) )
         {
-            error = PTR_ERR(new_patch);
+            xfree(mc);
             break;
         }
 
         /*
-         * If the new patch covers current CPU, compare patches and store the
+         * If the new update covers current CPU, compare updates and store the
          * one with higher revision.
          */
-        if ( (microcode_update_match(&new_patch->mc_intel->hdr) != MIS_UCODE) &&
-             (!patch || (compare_patch(new_patch, patch) == NEW_UCODE)) )
+        if ( (microcode_update_match(&mc->hdr) != MIS_UCODE) &&
+             (!saved || (mc->hdr.rev > saved->hdr.rev)) )
         {
-            struct microcode_patch *tmp = patch;
-
-            patch = new_patch;
-            new_patch = tmp;
+            xfree(saved);
+            saved = mc;
         }
-
-        if ( new_patch )
-            microcode_free_patch(new_patch);
-
-        xfree(mc);
+        else
+            xfree(mc);
     }
-    if ( offset > 0 )
-        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);