x86/microcode/intel: Switch to kvmalloc()
authorThomas Gleixner <tglx@linutronix.de>
Mon, 2 Oct 2023 11:59:45 +0000 (13:59 +0200)
committerBorislav Petkov (AMD) <bp@alien8.de>
Tue, 24 Oct 2023 13:05:53 +0000 (15:05 +0200)
Microcode blobs are getting larger and might soon reach the kmalloc()
limit. Switch over kvmalloc().

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20231002115902.564323243@linutronix.de
arch/x86/kernel/cpu/microcode/intel.c

index 076133b09cc78ec233532d662d77f6b984f9a789..dd2d3fde8d062d80ebe4b3f6d8fec5587af424a7 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/earlycpio.h>
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
-#include <linux/vmalloc.h>
 #include <linux/initrd.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -245,7 +244,7 @@ EXPORT_SYMBOL_GPL(intel_microcode_sanity_check);
 
 static void update_ucode_pointer(struct microcode_intel *mc)
 {
-       kfree(ucode_patch_va);
+       kvfree(ucode_patch_va);
 
        /*
         * Save the virtual address for early loading and for eventual free
@@ -256,11 +255,14 @@ static void update_ucode_pointer(struct microcode_intel *mc)
 
 static void save_microcode_patch(struct microcode_intel *patch)
 {
+       unsigned int size = get_totalsize(&patch->hdr);
        struct microcode_intel *mc;
 
-       mc = kmemdup(patch, get_totalsize(&patch->hdr), GFP_KERNEL);
+       mc = kvmemdup(patch, size, GFP_KERNEL);
        if (mc)
                update_ucode_pointer(mc);
+       else
+               pr_err("Unable to allocate microcode memory size: %u\n", size);
 }
 
 /* Scan blob for microcode matching the boot CPUs family, model, stepping */
@@ -539,36 +541,34 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
 
                if (!copy_from_iter_full(&mc_header, sizeof(mc_header), iter)) {
                        pr_err("error! Truncated or inaccessible header in microcode data file\n");
-                       break;
+                       goto fail;
                }
 
                mc_size = get_totalsize(&mc_header);
                if (mc_size < sizeof(mc_header)) {
                        pr_err("error! Bad data in microcode data file (totalsize too small)\n");
-                       break;
+                       goto fail;
                }
-
                data_size = mc_size - sizeof(mc_header);
                if (data_size > iov_iter_count(iter)) {
                        pr_err("error! Bad data in microcode data file (truncated file?)\n");
-                       break;
+                       goto fail;
                }
 
                /* For performance reasons, reuse mc area when possible */
                if (!mc || mc_size > curr_mc_size) {
-                       vfree(mc);
-                       mc = vmalloc(mc_size);
+                       kvfree(mc);
+                       mc = kvmalloc(mc_size, GFP_KERNEL);
                        if (!mc)
-                               break;
+                               goto fail;
                        curr_mc_size = mc_size;
                }
 
                memcpy(mc, &mc_header, sizeof(mc_header));
                data = mc + sizeof(mc_header);
                if (!copy_from_iter_full(data, data_size, iter) ||
-                   intel_microcode_sanity_check(mc, true, MC_HEADER_TYPE_MICROCODE) < 0) {
-                       break;
-               }
+                   intel_microcode_sanity_check(mc, true, MC_HEADER_TYPE_MICROCODE) < 0)
+                       goto fail;
 
                if (cur_rev >= mc_header.rev)
                        continue;
@@ -576,24 +576,26 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
                if (!intel_find_matching_signature(mc, uci->cpu_sig.sig, uci->cpu_sig.pf))
                        continue;
 
-               vfree(new_mc);
+               kvfree(new_mc);
                cur_rev = mc_header.rev;
                new_mc  = mc;
                mc = NULL;
        }
 
-       vfree(mc);
-
-       if (iov_iter_count(iter)) {
-               vfree(new_mc);
-               return UCODE_ERROR;
-       }
+       if (iov_iter_count(iter))
+               goto fail;
 
+       kvfree(mc);
        if (!new_mc)
                return UCODE_NFOUND;
 
        ucode_patch_late = (struct microcode_intel *)new_mc;
        return UCODE_NEW;
+
+fail:
+       kvfree(mc);
+       kvfree(new_mc);
+       return UCODE_ERROR;
 }
 
 static bool is_blacklisted(unsigned int cpu)
@@ -652,9 +654,9 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)
 static void finalize_late_load(int result)
 {
        if (!result)
-               save_microcode_patch(ucode_patch_late);
-
-       vfree(ucode_patch_late);
+               update_ucode_pointer(ucode_patch_late);
+       else
+               kvfree(ucode_patch_late);
        ucode_patch_late = NULL;
 }