References: bnc#736824

# HG changeset patch
# User Christoph Egger <Christoph.Egger@amd.com>
# Date 1323943209 -3600
# Node ID ca5f588bd203c9207e0988fcc80f43d83eed5420
# Parent  25f8952313ae683f41b634163f62651185d7be38
x86/ucode: fix for AMD Fam15 CPUs

Remove hardcoded maximum size a microcode patch can have. This is
dynamic now.

The microcode patch for family15h can be larger than 2048 bytes and
gets silently truncated.

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>

--- a/xen/arch/x86/microcode_amd.c
+++ b/xen/arch/x86/microcode_amd.c
@@ -27,18 +27,10 @@
 #include <asm/processor.h>
 #include <asm/microcode.h>
 
-#define pr_debug(x...) ((void)0)
-
 #define UCODE_MAGIC                0x00414d44
 #define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
 #define UCODE_UCODE_TYPE           0x00000001
 
-#define UCODE_MAX_SIZE          (2048)
-#define DEFAULT_UCODE_DATASIZE  (896)
-#define MC_HEADER_SIZE          (sizeof(struct microcode_header_amd))
-#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
-#define DWSIZE                  (sizeof(uint32_t))
-
 /* serialize access to the physical write */
 static DEFINE_SPINLOCK(microcode_update_lock);
 
@@ -100,7 +92,7 @@ static int microcode_fits(void *mc, int 
     }
 
     if ( mc_header->patch_id <= uci->cpu_sig.rev )
-        return -EINVAL;
+        return 0;
 
     printk(KERN_DEBUG "microcode: CPU%d found a matching microcode "
            "update with version 0x%x (current=0x%x)\n",
@@ -148,8 +140,12 @@ static int apply_microcode(int cpu)
     return 0;
 }
 
-static int get_next_ucode_from_buffer_amd(void *mc, const void *buf,
-                                         size_t size, unsigned long *offset)
+static int get_next_ucode_from_buffer_amd(
+    void **mc,
+    size_t *mc_size,
+    const void *buf,
+    size_t size,
+    unsigned long *offset)
 {
     struct microcode_header_amd *mc_header;
     size_t total_size;
@@ -182,8 +178,17 @@ static int get_next_ucode_from_buffer_am
         return -EINVAL;
     }
 
-    memset(mc, 0, UCODE_MAX_SIZE);
-    memcpy(mc, (const void *)(&bufp[off + 8]), total_size);
+    if ( *mc_size < total_size )
+    {
+        xfree(*mc);
+        *mc = xmalloc_bytes(total_size);
+        if ( !*mc )
+            return -ENOMEM;
+        *mc_size = total_size;
+    }
+    else if ( *mc_size > total_size )
+        memset(*mc + total_size, 0, *mc_size - total_size);
+    memcpy(*mc, mc_header, total_size);
 
     *offset = off + total_size + 8;
 
@@ -237,10 +242,10 @@ static int cpu_request_microcode(int cpu
 {
     const uint32_t *buf_pos;
     unsigned long offset = 0;
-    int error = 0;
-    int ret;
+    int error;
     struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
     void *mc;
+    size_t mc_size;
 
     /* We should bind the task to the CPU */
     BUG_ON(cpu != raw_smp_processor_id());
@@ -261,7 +266,9 @@ static int cpu_request_microcode(int cpu
         return -EINVAL;
     }
 
-    mc = xmalloc_bytes(UCODE_MAX_SIZE);
+    /* Size of 1st microcode patch in bytes */
+    mc_size = buf_pos[offset / sizeof(*buf_pos) + 1];
+    mc = xmalloc_bytes(mc_size);
     if ( mc == NULL )
     {
         printk(KERN_ERR "microcode: error! "
@@ -277,24 +284,33 @@ static int cpu_request_microcode(int cpu
      * It's possible the data file has multiple matching ucode,
      * lets keep searching till the latest version
      */
-    while ( (ret = get_next_ucode_from_buffer_amd(mc, buf, size, &offset)) == 0)
+    while ( (error = get_next_ucode_from_buffer_amd(&mc, &mc_size, buf, size,
+                                                    &offset)) == 0 )
     {
+        uci->mc.mc_amd = mc;
+
         error = microcode_fits(mc, cpu);
         if (error <= 0)
             continue;
 
         error = apply_microcode(cpu);
         if (error == 0)
+        {
+            error = 1;
             break;
+        }
     }
 
     /* On success keep the microcode patch for
      * re-apply on resume.
      */
-    if (error) {
+    if ( error <= 0 )
+    {
         xfree(mc);
         mc = NULL;
     }
+    else
+        error = 0;
     uci->mc.mc_amd = mc;
 
 out:
