Skip to content

Commit 43181a4

Browse files
committed
x86/microcode: Add microcode loader debugging functionality
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
1 parent 632ff61 commit 43181a4

5 files changed

Lines changed: 96 additions & 23 deletions

File tree

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,6 +3770,10 @@
37703770
microcode= [X86] Control the behavior of the microcode loader.
37713771
Available options, comma separated:
37723772

3773+
base_rev=X - with <X> with format: <u32>
3774+
Set the base microcode revision of each thread when in
3775+
debug mode.
3776+
37733777
dis_ucode_ldr: disable the microcode loader
37743778

37753779
force_minrev:

arch/x86/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,18 @@ config MICROCODE_LATE_FORCE_MINREV
13601360

13611361
If unsure say Y.
13621362

1363+
config MICROCODE_DBG
1364+
bool "Enable microcode loader debugging"
1365+
default n
1366+
depends on MICROCODE
1367+
help
1368+
Enable code which allows for debugging the microcode loader in
1369+
a guest. Meaning the patch loading is simulated but everything else
1370+
related to patch parsing and handling is done as on baremetal with
1371+
the purpose of debugging solely the software side of things.
1372+
1373+
You almost certainly want to say n here.
1374+
13631375
config X86_MSR
13641376
tristate "/dev/cpu/*/msr - Model-specific register support"
13651377
help

arch/x86/kernel/cpu/microcode/amd.c

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,6 @@ static bool verify_sha256_digest(u32 patch_id, u32 cur_rev, const u8 *data, unsi
269269
return true;
270270
}
271271

272-
static u32 get_patch_level(void)
273-
{
274-
u32 rev, dummy __always_unused;
275-
276-
native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
277-
278-
return rev;
279-
}
280-
281272
static union cpuid_1_eax ucode_rev_to_cpuid(unsigned int val)
282273
{
283274
union zen_patch_rev p;
@@ -295,6 +286,30 @@ static union cpuid_1_eax ucode_rev_to_cpuid(unsigned int val)
295286
return c;
296287
}
297288

289+
static u32 get_patch_level(void)
290+
{
291+
u32 rev, dummy __always_unused;
292+
293+
if (IS_ENABLED(CONFIG_MICROCODE_DBG)) {
294+
int cpu = smp_processor_id();
295+
296+
if (!microcode_rev[cpu]) {
297+
if (!base_rev)
298+
base_rev = cpuid_to_ucode_rev(bsp_cpuid_1_eax);
299+
300+
microcode_rev[cpu] = base_rev;
301+
302+
ucode_dbg("CPU%d, base_rev: 0x%x\n", cpu, base_rev);
303+
}
304+
305+
return microcode_rev[cpu];
306+
}
307+
308+
native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
309+
310+
return rev;
311+
}
312+
298313
static u16 find_equiv_id(struct equiv_cpu_table *et, u32 sig)
299314
{
300315
unsigned int i;
@@ -324,13 +339,13 @@ static bool verify_container(const u8 *buf, size_t buf_size)
324339
u32 cont_magic;
325340

326341
if (buf_size <= CONTAINER_HDR_SZ) {
327-
pr_debug("Truncated microcode container header.\n");
342+
ucode_dbg("Truncated microcode container header.\n");
328343
return false;
329344
}
330345

331346
cont_magic = *(const u32 *)buf;
332347
if (cont_magic != UCODE_MAGIC) {
333-
pr_debug("Invalid magic value (0x%08x).\n", cont_magic);
348+
ucode_dbg("Invalid magic value (0x%08x).\n", cont_magic);
334349
return false;
335350
}
336351

@@ -355,8 +370,8 @@ static bool verify_equivalence_table(const u8 *buf, size_t buf_size)
355370

356371
cont_type = hdr[1];
357372
if (cont_type != UCODE_EQUIV_CPU_TABLE_TYPE) {
358-
pr_debug("Wrong microcode container equivalence table type: %u.\n",
359-
cont_type);
373+
ucode_dbg("Wrong microcode container equivalence table type: %u.\n",
374+
cont_type);
360375
return false;
361376
}
362377

@@ -365,7 +380,7 @@ static bool verify_equivalence_table(const u8 *buf, size_t buf_size)
365380
equiv_tbl_len = hdr[2];
366381
if (equiv_tbl_len < sizeof(struct equiv_cpu_entry) ||
367382
buf_size < equiv_tbl_len) {
368-
pr_debug("Truncated equivalence table.\n");
383+
ucode_dbg("Truncated equivalence table.\n");
369384
return false;
370385
}
371386

@@ -385,7 +400,7 @@ static bool __verify_patch_section(const u8 *buf, size_t buf_size, u32 *sh_psize
385400
const u32 *hdr;
386401

387402
if (buf_size < SECTION_HDR_SIZE) {
388-
pr_debug("Truncated patch section.\n");
403+
ucode_dbg("Truncated patch section.\n");
389404
return false;
390405
}
391406

@@ -394,13 +409,13 @@ static bool __verify_patch_section(const u8 *buf, size_t buf_size, u32 *sh_psize
394409
p_size = hdr[1];
395410

396411
if (p_type != UCODE_UCODE_TYPE) {
397-
pr_debug("Invalid type field (0x%x) in container file section header.\n",
398-
p_type);
412+
ucode_dbg("Invalid type field (0x%x) in container file section header.\n",
413+
p_type);
399414
return false;
400415
}
401416

402417
if (p_size < sizeof(struct microcode_header_amd)) {
403-
pr_debug("Patch of size %u too short.\n", p_size);
418+
ucode_dbg("Patch of size %u too short.\n", p_size);
404419
return false;
405420
}
406421

@@ -477,12 +492,12 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
477492
* size sh_psize, as the section claims.
478493
*/
479494
if (buf_size < sh_psize) {
480-
pr_debug("Patch of size %u truncated.\n", sh_psize);
495+
ucode_dbg("Patch of size %u truncated.\n", sh_psize);
481496
return -1;
482497
}
483498

484499
if (!__verify_patch_size(sh_psize, buf_size)) {
485-
pr_debug("Per-family patch size mismatch.\n");
500+
ucode_dbg("Per-family patch size mismatch.\n");
486501
return -1;
487502
}
488503

@@ -496,6 +511,9 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
496511

497512
proc_id = mc_hdr->processor_rev_id;
498513
patch_fam = 0xf + (proc_id >> 12);
514+
515+
ucode_dbg("Patch-ID 0x%08x: family: 0x%x\n", mc_hdr->patch_id, patch_fam);
516+
499517
if (patch_fam != family)
500518
return 1;
501519

@@ -566,9 +584,14 @@ static size_t parse_container(u8 *ucode, size_t size, struct cont_desc *desc)
566584
}
567585

568586
mc = (struct microcode_amd *)(buf + SECTION_HDR_SIZE);
587+
588+
ucode_dbg("patch_id: 0x%x\n", mc->hdr.patch_id);
589+
569590
if (mc_patch_matches(mc, eq_id)) {
570591
desc->psize = patch_size;
571592
desc->mc = mc;
593+
594+
ucode_dbg(" match: size: %d\n", patch_size);
572595
}
573596

574597
skip:
@@ -639,8 +662,14 @@ static bool __apply_microcode_amd(struct microcode_amd *mc, u32 *cur_rev,
639662
invlpg(p_addr_end);
640663
}
641664

665+
if (IS_ENABLED(CONFIG_MICROCODE_DBG))
666+
microcode_rev[smp_processor_id()] = mc->hdr.patch_id;
667+
642668
/* verify patch application was successful */
643669
*cur_rev = get_patch_level();
670+
671+
ucode_dbg("updated rev: 0x%x\n", *cur_rev);
672+
644673
if (*cur_rev != mc->hdr.patch_id)
645674
return false;
646675

@@ -1026,7 +1055,7 @@ static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover,
10261055
patch->patch_id = mc_hdr->patch_id;
10271056
patch->equiv_cpu = proc_id;
10281057

1029-
pr_debug("%s: Adding patch_id: 0x%08x, proc_id: 0x%04x\n",
1058+
ucode_dbg("%s: Adding patch_id: 0x%08x, proc_id: 0x%04x\n",
10301059
__func__, patch->patch_id, proc_id);
10311060

10321061
/* ... and add to cache. */
@@ -1169,7 +1198,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device)
11691198
snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
11701199

11711200
if (request_firmware_direct(&fw, (const char *)fw_name, device)) {
1172-
pr_debug("failed to load file %s\n", fw_name);
1201+
ucode_dbg("failed to load file %s\n", fw_name);
11731202
goto out;
11741203
}
11751204

arch/x86/kernel/cpu/microcode/core.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ static bool dis_ucode_ldr;
4747

4848
bool force_minrev = IS_ENABLED(CONFIG_MICROCODE_LATE_FORCE_MINREV);
4949

50+
/*
51+
* Those below should be behind CONFIG_MICROCODE_DBG ifdeffery but in
52+
* order to not uglify the code with ifdeffery and use IS_ENABLED()
53+
* instead, leave them in. When microcode debugging is not enabled,
54+
* those are meaningless anyway.
55+
*/
56+
/* base microcode revision for debugging */
57+
u32 base_rev;
58+
u32 microcode_rev[NR_CPUS] = {};
59+
5060
/*
5161
* Synchronization.
5262
*
@@ -118,7 +128,8 @@ bool __init microcode_loader_disabled(void)
118128
* overwritten.
119129
*/
120130
if (!cpuid_feature() ||
121-
native_cpuid_ecx(1) & BIT(31) ||
131+
((native_cpuid_ecx(1) & BIT(31)) &&
132+
!IS_ENABLED(CONFIG_MICROCODE_DBG)) ||
122133
amd_check_current_patch_level())
123134
dis_ucode_ldr = true;
124135

@@ -132,6 +143,14 @@ static void early_parse_cmdline(void)
132143

133144
if (cmdline_find_option(boot_command_line, "microcode", cmd_buf, sizeof(cmd_buf)) > 0) {
134145
while ((s = strsep(&p, ","))) {
146+
if (IS_ENABLED(CONFIG_MICROCODE_DBG)) {
147+
if (strstr(s, "base_rev=")) {
148+
/* advance to the option arg */
149+
strsep(&s, "=");
150+
if (kstrtouint(s, 16, &base_rev)) { ; }
151+
}
152+
}
153+
135154
if (!strcmp("force_minrev", s))
136155
force_minrev = true;
137156

arch/x86/kernel/cpu/microcode/internal.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ struct early_load_data {
4444

4545
extern struct early_load_data early_data;
4646
extern struct ucode_cpu_info ucode_cpu_info[];
47+
extern u32 microcode_rev[NR_CPUS];
48+
extern u32 base_rev;
49+
4750
struct cpio_data find_microcode_in_initrd(const char *path);
4851

4952
#define MAX_UCODE_COUNT 128
@@ -122,4 +125,10 @@ static inline void reload_ucode_intel(void) { }
122125
static inline struct microcode_ops *init_intel_microcode(void) { return NULL; }
123126
#endif /* !CONFIG_CPU_SUP_INTEL */
124127

128+
#define ucode_dbg(fmt, ...) \
129+
({ \
130+
if (IS_ENABLED(CONFIG_MICROCODE_DBG)) \
131+
pr_info(fmt, ##__VA_ARGS__); \
132+
})
133+
125134
#endif /* _X86_MICROCODE_INTERNAL_H */

0 commit comments

Comments
 (0)