static struct equiv_cpu_entry *equiv_cpu_table;
 
-/* page-sized ucode patch buffer */
-void *patch;
-
 struct ucode_patch {
        struct list_head plist;
        void *data;
        return 0;
 }
 
-static unsigned int verify_ucode_size(int cpu, u32 patch_size,
+static unsigned int verify_patch_size(int cpu, u32 patch_size,
                                      unsigned int size)
 {
        struct cpuinfo_x86 *c = &cpu_data(cpu);
        return patch_size;
 }
 
-/*
- * we signal a good patch is found by returning its size > 0
- */
-static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
-                                 unsigned int leftover_size, int rev,
-                                 unsigned int *current_size)
-{
-       struct microcode_header_amd *mc_hdr;
-       unsigned int actual_size, patch_size;
-       u16 equiv_cpu_id;
-
-       /* size of the current patch we're staring at */
-       patch_size = *(u32 *)(ucode_ptr + 4);
-       *current_size = patch_size + SECTION_HDR_SIZE;
-
-       equiv_cpu_id = find_equiv_id(cpu);
-       if (!equiv_cpu_id)
-               return 0;
-
-       /*
-        * let's look at the patch header itself now
-        */
-       mc_hdr = (struct microcode_header_amd *)(ucode_ptr + SECTION_HDR_SIZE);
-
-       if (mc_hdr->processor_rev_id != equiv_cpu_id)
-               return 0;
-
-       /* ucode might be chipset specific -- currently we don't support this */
-       if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
-               pr_err("CPU%d: chipset specific code not yet supported\n",
-                      cpu);
-               return 0;
-       }
-
-       if (mc_hdr->patch_id <= rev)
-               return 0;
-
-       /*
-        * now that the header looks sane, verify its size
-        */
-       actual_size = verify_ucode_size(cpu, patch_size, leftover_size);
-       if (!actual_size)
-               return 0;
-
-       /* clear the patch buffer */
-       memset(patch, 0, PAGE_SIZE);
-
-       /* all looks ok, get the binary patch */
-       memcpy(patch, ucode_ptr + SECTION_HDR_SIZE, actual_size);
-
-       return actual_size;
-}
-
 static int apply_microcode_amd(int cpu)
 {
-       u32 rev, dummy;
-       int cpu_num = raw_smp_processor_id();
-       struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
-       struct microcode_amd *mc_amd = uci->mc;
        struct cpuinfo_x86 *c = &cpu_data(cpu);
+       struct microcode_amd *mc_amd;
+       struct ucode_cpu_info *uci;
+       struct ucode_patch *p;
+       u32 rev, dummy;
+
+       BUG_ON(raw_smp_processor_id() != cpu);
 
-       /* We should bind the task to the CPU */
-       BUG_ON(cpu_num != cpu);
+       uci = ucode_cpu_info + cpu;
 
-       if (mc_amd == NULL)
+       p = find_patch(cpu);
+       if (!p)
                return 0;
 
+       mc_amd  = p->data;
+       uci->mc = p->data;
+
        rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
 
        /* need to apply patch? */
        equiv_cpu_table = NULL;
 }
 
-static enum ucode_state
-generic_load_microcode(int cpu, const u8 *data, size_t size)
+static void cleanup(void)
 {
-       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-       struct microcode_header_amd *mc_hdr = NULL;
-       unsigned int mc_size, leftover, current_size = 0;
+       free_equiv_cpu_table();
+       free_cache();
+}
+
+/*
+ * We return the current size even if some of the checks failed so that
+ * we can skip over the next patch. If we return a negative value, we
+ * signal a grave error like a memory allocation has failed and the
+ * driver cannot continue functioning normally. In such cases, we tear
+ * down everything we've used up so far and exit.
+ */
+static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
+{
+       struct cpuinfo_x86 *c = &cpu_data(cpu);
+       struct microcode_header_amd *mc_hdr;
+       struct ucode_patch *patch;
+       unsigned int patch_size, crnt_size, ret;
+       u32 proc_fam;
+       u16 proc_id;
+
+       patch_size  = *(u32 *)(fw + 4);
+       crnt_size   = patch_size + SECTION_HDR_SIZE;
+       mc_hdr      = (struct microcode_header_amd *)(fw + SECTION_HDR_SIZE);
+       proc_id     = mc_hdr->processor_rev_id;
+
+       proc_fam = find_cpu_family_by_equiv_cpu(proc_id);
+       if (!proc_fam) {
+               pr_err("No patch family for equiv ID: 0x%04x\n", proc_id);
+               return crnt_size;
+       }
+
+       /* check if patch is for the current family */
+       proc_fam = ((proc_fam >> 8) & 0xf) + ((proc_fam >> 20) & 0xff);
+       if (proc_fam != c->x86)
+               return crnt_size;
+
+       if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
+               pr_err("Patch-ID 0x%08x: chipset-specific code unsupported.\n",
+                       mc_hdr->patch_id);
+               return crnt_size;
+       }
+
+       ret = verify_patch_size(cpu, patch_size, leftover);
+       if (!ret) {
+               pr_err("Patch-ID 0x%08x: size mismatch.\n", mc_hdr->patch_id);
+               return crnt_size;
+       }
+
+       patch = kzalloc(sizeof(*patch), GFP_KERNEL);
+       if (!patch) {
+               pr_err("Patch allocation failure.\n");
+               return -EINVAL;
+       }
+
+       patch->data = kzalloc(patch_size, GFP_KERNEL);
+       if (!patch->data) {
+               pr_err("Patch data allocation failure.\n");
+               kfree(patch);
+               return -EINVAL;
+       }
+
+       /* All looks ok, copy patch... */
+       memcpy(patch->data, fw + SECTION_HDR_SIZE, patch_size);
+       INIT_LIST_HEAD(&patch->plist);
+       patch->patch_id  = mc_hdr->patch_id;
+       patch->equiv_cpu = proc_id;
+
+       /* ... and add to cache. */
+       update_cache(patch);
+
+       return crnt_size;
+}
+
+static enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size)
+{
+       enum ucode_state ret = UCODE_ERROR;
+       unsigned int leftover;
+       u8 *fw = (u8 *)data;
+       int crnt_size = 0;
        int offset;
-       const u8 *ucode_ptr = data;
-       void *new_mc = NULL;
-       unsigned int new_rev = uci->cpu_sig.rev;
-       enum ucode_state state = UCODE_ERROR;
 
-       offset = install_equiv_cpu_table(ucode_ptr);
+       offset = install_equiv_cpu_table(data);
        if (offset < 0) {
                pr_err("failed to create equivalent cpu table\n");
-               goto out;
+               return ret;
        }
-       ucode_ptr += offset;
+       fw += offset;
        leftover = size - offset;
 
-       if (*(u32 *)ucode_ptr != UCODE_UCODE_TYPE) {
+       if (*(u32 *)fw != UCODE_UCODE_TYPE) {
                pr_err("invalid type field in container file section header\n");
-               goto free_table;
+               free_equiv_cpu_table();
+               return ret;
        }
 
        while (leftover) {
-               mc_size = get_matching_microcode(cpu, ucode_ptr, leftover,
-                                                new_rev, ¤t_size);
-               if (mc_size) {
-                       mc_hdr  = patch;
-                       new_mc  = patch;
-                       new_rev = mc_hdr->patch_id;
-                       goto out_ok;
-               }
+               crnt_size = verify_and_add_patch(cpu, fw, leftover);
+               if (crnt_size < 0)
+                       return ret;
 
-               ucode_ptr += current_size;
-               leftover  -= current_size;
+               fw       += crnt_size;
+               leftover -= crnt_size;
        }
 
-       if (!new_mc) {
-               state = UCODE_NFOUND;
-               goto free_table;
-       }
-
-out_ok:
-       uci->mc = new_mc;
-       state = UCODE_OK;
-       pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
-                cpu, uci->cpu_sig.rev, new_rev);
-
-free_table:
-       free_equiv_cpu_table();
-
-out:
-       return state;
+       return UCODE_OK;
 }
 
 /*
  *
  * This legacy file is always smaller than 2K in size.
  *
- * Starting at family 15h they are in family specific firmware files:
+ * Beginning with family 15h, they are in family-specific firmware files:
  *
  *    amd-ucode/microcode_amd_fam15h.bin
  *    amd-ucode/microcode_amd_fam16h.bin
                                              bool refresh_fw)
 {
        char fw_name[36] = "amd-ucode/microcode_amd.bin";
-       const struct firmware *fw;
-       enum ucode_state ret = UCODE_NFOUND;
        struct cpuinfo_x86 *c = &cpu_data(cpu);
+       enum ucode_state ret = UCODE_NFOUND;
+       const struct firmware *fw;
+
+       /* reload ucode container only on the boot cpu */
+       if (!refresh_fw || c->cpu_index != boot_cpu_data.cpu_index)
+               return UCODE_OK;
 
        if (c->x86 >= 0x15)
                snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
                goto fw_release;
        }
 
-       ret = generic_load_microcode(cpu, fw->data, fw->size);
+       /* free old equiv table */
+       free_equiv_cpu_table();
+
+       ret = load_microcode_amd(cpu, fw->data, fw->size);
+       if (ret != UCODE_OK)
+               cleanup();
 
-fw_release:
+ fw_release:
        release_firmware(fw);
 
-out:
+ out:
        return ret;
 }
 
                return NULL;
        }
 
-       patch = (void *)get_zeroed_page(GFP_KERNEL);
-       if (!patch)
-               return NULL;
-
        return µcode_amd_ops;
 }
 
 void __exit exit_amd_microcode(void)
 {
-       free_page((unsigned long)patch);
+       cleanup();
 }