diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 644c45fb0a..52b7d1ddf2 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3450,6 +3450,110 @@ static int xen_init(MachineState *ms, KVMState *s) #endif } +#ifndef MSR_IA32_PLATFORM_ID +#define MSR_IA32_PLATFORM_ID 0x17 +#endif + +/* Platform ID lives in bits 52:50. Loaders derive pf = 1 << platform_id + * and AND it against the image header PF (offset 0x18). Read-only on HW. */ +static bool ucode_rdmsr_platform_id(X86CPU *cpu, uint32_t msr, uint64_t *val) +{ + const char *e = getenv("UCODE_FAKE_PLATFORM_ID"); + unsigned pid = (e ? (unsigned)strtoul(e, NULL, 0) : 0) & 0x7; + + *val = (uint64_t)pid << 50; + return true; +} + +#ifndef MSR_IA32_UCODE_REV +#define MSR_IA32_UCODE_REV 0x8b +#endif + +/* Revision handed to the guest via RDMSR 0x8b (bits 63:32). + * Bumped by the 0x79 handler so a faked load appears to apply. */ +static uint32_t ucode_reported_rev; + +static uint32_t ucode_initial_rev(X86CPU *cpu) +{ + const char *e = getenv("UCODE_FAKE_REV"); + if (e) { + return (uint32_t)strtoul(e, NULL, 0); /* accepts 0x… */ + } + if (cpu->ucode_rev) { /* respect -cpu ...,ucode_rev= */ + return (uint32_t)(cpu->ucode_rev >> 32); + } + return 0x1; +} + +static bool ucode_rdmsr_rev(X86CPU *cpu, uint32_t msr, uint64_t *val) +{ + if (ucode_reported_rev == 0) { + ucode_reported_rev = ucode_initial_rev(cpu); + } + *val = (uint64_t)ucode_reported_rev << 32; /* rev in high dword */ + return true; +} + +#ifndef MSR_IA32_UCODE_WRITE +#define MSR_IA32_UCODE_WRITE 0x79 +#endif + +static bool ucode_capture_wrmsr(X86CPU *cpu, uint32_t msr, uint64_t data) +{ + CPUState *cs = CPU(cpu); + static unsigned seq; + uint8_t hdr[48]; + uint8_t *buf; + uint32_t total; + uint64_t base = data - sizeof(hdr); /* data ptr is header+48 */ + const char *dir = getenv("UCODE_DUMP_DIR") ?: "/tmp"; + char path[256]; + FILE *f; + + /* Read the 48-byte Intel header sitting just before the data block. */ + if (cpu_memory_rw_debug(cs, base, hdr, sizeof(hdr), false) != 0) { + warn_report("ucode-capture: cpu%d unreadable header @ 0x%" PRIx64, + cs->cpu_index, base); + return true; /* complete as a silent no-op */ + } + + total = ldl_le_p(hdr + 0x20); /* TotalSize; 0 => 2048 */ + if (total == 0) { + total = 2048; + } + if (total < sizeof(hdr) || total > (1u << 20)) { + warn_report("ucode-capture: cpu%d implausible TotalSize 0x%x @ 0x%" + PRIx64, cs->cpu_index, total, base); + return true; + } + + buf = g_malloc(total); + if (cpu_memory_rw_debug(cs, base, buf, total, false) != 0) { + warn_report("ucode-capture: cpu%d short read of %u bytes @ 0x%" PRIx64, + cs->cpu_index, total, base); + g_free(buf); + return true; + } + ucode_reported_rev = ldl_le_p(hdr + 0x04); /* header's update revision */ + + snprintf(path, sizeof(path), "%s/ucode-cpu%d-%04u.bin", + dir, cs->cpu_index, seq++); + f = fopen(path, "wb"); + if (f) { + fwrite(buf, 1, total, f); + fclose(f); + info_report("ucode-capture: cpu%d %u bytes -> %s " + "(hdrrev 0x%x sig 0x%x pf 0x%x datasize 0x%x) gva 0x%" + PRIx64, cs->cpu_index, total, path, + ldl_le_p(hdr + 0x04), ldl_le_p(hdr + 0x0c), + ldl_le_p(hdr + 0x18), ldl_le_p(hdr + 0x1c), data); + } else { + warn_report("ucode-capture: cpu%d cannot open %s", cs->cpu_index, path); + } + g_free(buf); + return true; /* handled: KVM finishes the WRMSR as a no-op, no #GP */ +} + int kvm_arch_init(MachineState *ms, KVMState *s) { int ret; @@ -3611,6 +3715,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } first = false; + + { + kvm_filter_msr(s, MSR_IA32_UCODE_WRITE, NULL, ucode_capture_wrmsr); + kvm_filter_msr(s, MSR_IA32_UCODE_REV, ucode_rdmsr_rev, NULL); + kvm_filter_msr(s, MSR_IA32_PLATFORM_ID, ucode_rdmsr_platform_id, NULL); + } + return 0; } @@ -4107,7 +4218,7 @@ static int kvm_buf_set_msrs(X86CPU *cpu) if (ret < cpu->kvm_msr_buf->nmsrs) { struct kvm_msr_entry *e = &cpu->kvm_msr_buf->entries[ret]; - error_report("error: failed to set MSR 0x%" PRIx32 " to 0x%" PRIx64, + error_report("error: 22 failed to set MSR 0x%" PRIx32 " to 0x%" PRIx64, (uint32_t)e->index, (uint64_t)e->data); }