mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
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
This commit is contained in:
parent
632ff61706
commit
43181a4726
|
|
@ -3770,6 +3770,10 @@
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user