]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
x86/microcode: Add microcode loader debugging functionality
authorBorislav Petkov (AMD) <bp@alien8.de>
Wed, 20 Aug 2025 13:50:43 +0000 (15:50 +0200)
committerBorislav Petkov (AMD) <bp@alien8.de>
Thu, 4 Sep 2025 14:15:19 +0000 (16:15 +0200)
Instead of adding ad-hoc debugging glue to the microcode loader each
time I need it, add debugging functionality which is not built by
default.

Simulate all patch handling the loader does except the actual loading of
the microcode patch into the hardware.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/20250820135043.19048-3-bp@kernel.org
Documentation/admin-guide/kernel-parameters.txt
arch/x86/Kconfig
arch/x86/kernel/cpu/microcode/amd.c
arch/x86/kernel/cpu/microcode/core.c
arch/x86/kernel/cpu/microcode/internal.h

index 9e3bbce6583f20f3414215bc164989adc329e791..2c142e5f9f0617fdd1f1def9e259827cde2368cd 100644 (file)
        microcode=      [X86] Control the behavior of the microcode loader.
                        Available options, comma separated:
 
+                       base_rev=X - with <X> with format: <u32>
+                       Set the base microcode revision of each thread when in
+                       debug mode.
+
                        dis_ucode_ldr: disable the microcode loader
 
                        force_minrev:
index aa250d90f927a23f7aac600ff019e97c6a12d476..77f72f075d896f7b63731258e0cc5e0b39331f72 100644 (file)
@@ -1360,6 +1360,18 @@ config MICROCODE_LATE_FORCE_MINREV
 
          If unsure say Y.
 
+config MICROCODE_DBG
+       bool "Enable microcode loader debugging"
+       default n
+       depends on MICROCODE
+       help
+         Enable code which allows for debugging the microcode loader in
+         a guest. Meaning the patch loading is simulated but everything else
+         related to patch parsing and handling is done as on baremetal with
+         the purpose of debugging solely the software side of things.
+
+         You almost certainly want to say n here.
+
 config X86_MSR
        tristate "/dev/cpu/*/msr - Model-specific register support"
        help
index 514f63340880fde443449e8aa122d1465c7a3351..cdce885e2fd50a0735c9908803035c8367899ce2 100644 (file)
@@ -269,15 +269,6 @@ static bool verify_sha256_digest(u32 patch_id, u32 cur_rev, const u8 *data, unsi
        return true;
 }
 
-static u32 get_patch_level(void)
-{
-       u32 rev, dummy __always_unused;
-
-       native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
-
-       return rev;
-}
-
 static union cpuid_1_eax ucode_rev_to_cpuid(unsigned int val)
 {
        union zen_patch_rev p;
@@ -295,6 +286,30 @@ static union cpuid_1_eax ucode_rev_to_cpuid(unsigned int val)
        return c;
 }
 
+static u32 get_patch_level(void)
+{
+       u32 rev, dummy __always_unused;
+
+       if (IS_ENABLED(CONFIG_MICROCODE_DBG)) {
+               int cpu = smp_processor_id();
+
+               if (!microcode_rev[cpu]) {
+                       if (!base_rev)
+                               base_rev = cpuid_to_ucode_rev(bsp_cpuid_1_eax);
+
+                       microcode_rev[cpu] = base_rev;
+
+                       ucode_dbg("CPU%d, base_rev: 0x%x\n", cpu, base_rev);
+               }
+
+               return microcode_rev[cpu];
+       }
+
+       native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+
+       return rev;
+}
+
 static u16 find_equiv_id(struct equiv_cpu_table *et, u32 sig)
 {
        unsigned int i;
@@ -324,13 +339,13 @@ static bool verify_container(const u8 *buf, size_t buf_size)
        u32 cont_magic;
 
        if (buf_size <= CONTAINER_HDR_SZ) {
-               pr_debug("Truncated microcode container header.\n");
+               ucode_dbg("Truncated microcode container header.\n");
                return false;
        }
 
        cont_magic = *(const u32 *)buf;
        if (cont_magic != UCODE_MAGIC) {
-               pr_debug("Invalid magic value (0x%08x).\n", cont_magic);
+               ucode_dbg("Invalid magic value (0x%08x).\n", cont_magic);
                return false;
        }
 
@@ -355,8 +370,8 @@ static bool verify_equivalence_table(const u8 *buf, size_t buf_size)
 
        cont_type = hdr[1];
        if (cont_type != UCODE_EQUIV_CPU_TABLE_TYPE) {
-               pr_debug("Wrong microcode container equivalence table type: %u.\n",
-                        cont_type);
+               ucode_dbg("Wrong microcode container equivalence table type: %u.\n",
+                         cont_type);
                return false;
        }
 
@@ -365,7 +380,7 @@ static bool verify_equivalence_table(const u8 *buf, size_t buf_size)
        equiv_tbl_len = hdr[2];
        if (equiv_tbl_len < sizeof(struct equiv_cpu_entry) ||
            buf_size < equiv_tbl_len) {
-               pr_debug("Truncated equivalence table.\n");
+               ucode_dbg("Truncated equivalence table.\n");
                return false;
        }
 
@@ -385,7 +400,7 @@ static bool __verify_patch_section(const u8 *buf, size_t buf_size, u32 *sh_psize
        const u32 *hdr;
 
        if (buf_size < SECTION_HDR_SIZE) {
-               pr_debug("Truncated patch section.\n");
+               ucode_dbg("Truncated patch section.\n");
                return false;
        }
 
@@ -394,13 +409,13 @@ static bool __verify_patch_section(const u8 *buf, size_t buf_size, u32 *sh_psize
        p_size = hdr[1];
 
        if (p_type != UCODE_UCODE_TYPE) {
-               pr_debug("Invalid type field (0x%x) in container file section header.\n",
-                        p_type);
+               ucode_dbg("Invalid type field (0x%x) in container file section header.\n",
+                         p_type);
                return false;
        }
 
        if (p_size < sizeof(struct microcode_header_amd)) {
-               pr_debug("Patch of size %u too short.\n", p_size);
+               ucode_dbg("Patch of size %u too short.\n", p_size);
                return false;
        }
 
@@ -477,12 +492,12 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
         * size sh_psize, as the section claims.
         */
        if (buf_size < sh_psize) {
-               pr_debug("Patch of size %u truncated.\n", sh_psize);
+               ucode_dbg("Patch of size %u truncated.\n", sh_psize);
                return -1;
        }
 
        if (!__verify_patch_size(sh_psize, buf_size)) {
-               pr_debug("Per-family patch size mismatch.\n");
+               ucode_dbg("Per-family patch size mismatch.\n");
                return -1;
        }
 
@@ -496,6 +511,9 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
 
        proc_id = mc_hdr->processor_rev_id;
        patch_fam = 0xf + (proc_id >> 12);
+
+       ucode_dbg("Patch-ID 0x%08x: family: 0x%x\n", mc_hdr->patch_id, patch_fam);
+
        if (patch_fam != family)
                return 1;
 
@@ -566,9 +584,14 @@ static size_t parse_container(u8 *ucode, size_t size, struct cont_desc *desc)
                }
 
                mc = (struct microcode_amd *)(buf + SECTION_HDR_SIZE);
+
+               ucode_dbg("patch_id: 0x%x\n", mc->hdr.patch_id);
+
                if (mc_patch_matches(mc, eq_id)) {
                        desc->psize = patch_size;
                        desc->mc = mc;
+
+                       ucode_dbg(" match: size: %d\n", patch_size);
                }
 
 skip:
@@ -639,8 +662,14 @@ static bool __apply_microcode_amd(struct microcode_amd *mc, u32 *cur_rev,
                        invlpg(p_addr_end);
        }
 
+       if (IS_ENABLED(CONFIG_MICROCODE_DBG))
+               microcode_rev[smp_processor_id()] = mc->hdr.patch_id;
+
        /* verify patch application was successful */
        *cur_rev = get_patch_level();
+
+       ucode_dbg("updated rev: 0x%x\n", *cur_rev);
+
        if (*cur_rev != mc->hdr.patch_id)
                return false;
 
@@ -1026,7 +1055,7 @@ static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover,
        patch->patch_id  = mc_hdr->patch_id;
        patch->equiv_cpu = proc_id;
 
-       pr_debug("%s: Adding patch_id: 0x%08x, proc_id: 0x%04x\n",
+       ucode_dbg("%s: Adding patch_id: 0x%08x, proc_id: 0x%04x\n",
                 __func__, patch->patch_id, proc_id);
 
        /* ... and add to cache. */
@@ -1169,7 +1198,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device)
                snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
 
        if (request_firmware_direct(&fw, (const char *)fw_name, device)) {
-               pr_debug("failed to load file %s\n", fw_name);
+               ucode_dbg("failed to load file %s\n", fw_name);
                goto out;
        }
 
index 7d590630673b42de6f4b66e55da44d0a47cbaffa..f75c140906d002274cabf478c5d6a59838bd070b 100644 (file)
@@ -47,6 +47,16 @@ static bool dis_ucode_ldr;
 
 bool force_minrev = IS_ENABLED(CONFIG_MICROCODE_LATE_FORCE_MINREV);
 
+/*
+ * Those below should be behind CONFIG_MICROCODE_DBG ifdeffery but in
+ * order to not uglify the code with ifdeffery and use IS_ENABLED()
+ * instead, leave them in. When microcode debugging is not enabled,
+ * those are meaningless anyway.
+ */
+/* base microcode revision for debugging */
+u32 base_rev;
+u32 microcode_rev[NR_CPUS] = {};
+
 /*
  * Synchronization.
  *
@@ -118,7 +128,8 @@ bool __init microcode_loader_disabled(void)
         *    overwritten.
         */
        if (!cpuid_feature() ||
-           native_cpuid_ecx(1) & BIT(31) ||
+           ((native_cpuid_ecx(1) & BIT(31)) &&
+             !IS_ENABLED(CONFIG_MICROCODE_DBG)) ||
            amd_check_current_patch_level())
                dis_ucode_ldr = true;
 
@@ -132,6 +143,14 @@ static void early_parse_cmdline(void)
 
        if (cmdline_find_option(boot_command_line, "microcode", cmd_buf, sizeof(cmd_buf)) > 0) {
                while ((s = strsep(&p, ","))) {
+                       if (IS_ENABLED(CONFIG_MICROCODE_DBG)) {
+                               if (strstr(s, "base_rev=")) {
+                                       /* advance to the option arg */
+                                       strsep(&s, "=");
+                                       if (kstrtouint(s, 16, &base_rev)) { ; }
+                               }
+                       }
+
                        if (!strcmp("force_minrev", s))
                                force_minrev = true;
 
index 50a9702ae4e2b564dd41886676547d267809f71c..ae8dbc2b908d72c4b4fef05bd79f56befcb41329 100644 (file)
@@ -44,6 +44,9 @@ struct early_load_data {
 
 extern struct early_load_data early_data;
 extern struct ucode_cpu_info ucode_cpu_info[];
+extern u32 microcode_rev[NR_CPUS];
+extern u32 base_rev;
+
 struct cpio_data find_microcode_in_initrd(const char *path);
 
 #define MAX_UCODE_COUNT 128
@@ -122,4 +125,10 @@ static inline void reload_ucode_intel(void) { }
 static inline struct microcode_ops *init_intel_microcode(void) { return NULL; }
 #endif  /* !CONFIG_CPU_SUP_INTEL */
 
+#define ucode_dbg(fmt, ...)                                    \
+({                                                             \
+       if (IS_ENABLED(CONFIG_MICROCODE_DBG))                   \
+               pr_info(fmt, ##__VA_ARGS__);                    \
+})
+
 #endif /* _X86_MICROCODE_INTERNAL_H */