[patch 3/3] Add mce test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Test mce injection, triggered via monitor interface.

Signed-off-by: Marcelo Tosatti <mtosatti@xxxxxxxxxx>

Index: kvm-unit-tests/config-x86-common.mak
===================================================================
--- kvm-unit-tests.orig/config-x86-common.mak
+++ kvm-unit-tests/config-x86-common.mak
@@ -27,7 +27,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $
                $(TEST_DIR)/smptest.flat  $(TEST_DIR)/port80.flat \
                $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \
                $(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \
-               $(TEST_DIR)/kvmclock_test.flat
+               $(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/mce.flat
 
 tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
 
@@ -74,6 +74,8 @@ $(TEST_DIR)/svm.flat: $(cstart.o) $(TEST
 $(TEST_DIR)/kvmclock_test.flat: $(cstart.o) $(TEST_DIR)/kvmclock.o \
                                 $(TEST_DIR)/kvmclock_test.o
 
+$(TEST_DIR)/mce.flat: $(cstart.o) $(TEST_DIR)/mce.o $(TEST_DIR)/vm.o
+
 arch_clean:
 	$(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \
 	$(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o
Index: kvm-unit-tests/x86/mce.c
===================================================================
--- /dev/null
+++ kvm-unit-tests/x86/mce.c
@@ -0,0 +1,267 @@
+#include "libcflat.h"
+#include "processor.h"
+#include "smp.h"
+#include "apic.h"
+
+typedef struct {
+    unsigned short offset0;
+    unsigned short selector;
+    unsigned short ist : 3;
+    unsigned short : 5;
+    unsigned short type : 4;
+    unsigned short : 1;
+    unsigned short dpl : 2;
+    unsigned short p : 1;
+    unsigned short offset1;
+#ifdef __x86_64__
+    unsigned offset2;
+    unsigned reserved;
+#endif
+} idt_entry_t;
+
+typedef struct {
+    ulong regs[sizeof(ulong)*2];
+    ulong func;
+    ulong rip;
+    ulong cs;
+    ulong rflags;
+} isr_regs_t;
+
+#ifdef __x86_64__
+#  define R "r"
+#else
+#  define R "e"
+#endif
+
+extern char isr_entry_point[];
+
+asm (
+    "isr_entry_point: \n"
+#ifdef __x86_64__
+    "push %r15 \n\t"
+    "push %r14 \n\t"
+    "push %r13 \n\t"
+    "push %r12 \n\t"
+    "push %r11 \n\t"
+    "push %r10 \n\t"
+    "push %r9  \n\t"
+    "push %r8  \n\t"
+#endif
+    "push %"R "di \n\t"
+    "push %"R "si \n\t"
+    "push %"R "bp \n\t"
+    "push %"R "sp \n\t"
+    "push %"R "bx \n\t"
+    "push %"R "dx \n\t"
+    "push %"R "cx \n\t"
+    "push %"R "ax \n\t"
+#ifdef __x86_64__
+    "mov %rsp, %rdi \n\t"
+    "callq *8*16(%rsp) \n\t"
+#else
+    "push %esp \n\t"
+    "calll *4+4*8(%esp) \n\t"
+    "add $4, %esp \n\t"
+#endif
+    "pop %"R "ax \n\t"
+    "pop %"R "cx \n\t"
+    "pop %"R "dx \n\t"
+    "pop %"R "bx \n\t"
+    "pop %"R "bp \n\t"
+    "pop %"R "bp \n\t"
+    "pop %"R "si \n\t"
+    "pop %"R "di \n\t"
+#ifdef __x86_64__
+    "pop %r8  \n\t"
+    "pop %r9  \n\t"
+    "pop %r10 \n\t"
+    "pop %r11 \n\t"
+    "pop %r12 \n\t"
+    "pop %r13 \n\t"
+    "pop %r14 \n\t"
+    "pop %r15 \n\t"
+#endif
+#ifdef __x86_64__
+    "add $8, %rsp \n\t"
+    "iretq \n\t"
+#else
+    "add $4, %esp \n\t"
+    "iretl \n\t"
+#endif
+    );
+
+static idt_entry_t idt[256];
+
+static void init_idt(void)
+{
+    struct {
+        u16 limit;
+        ulong idt;
+    } __attribute__((packed)) idt_ptr = {
+        sizeof(idt_entry_t) * 256 - 1,
+        (ulong)&idt,
+    };
+
+    asm volatile("lidt %0" : : "m"(idt_ptr));
+}
+
+u8 code[50];
+
+static void set_idt_entry(unsigned vec, void (*func)(isr_regs_t *regs))
+{
+    u8 *thunk = code;
+    ulong ptr = (ulong)thunk;
+    idt_entry_t ent = {
+        .offset0 = ptr,
+        .selector = read_cs(),
+        .ist = 0,
+        .type = 14,
+        .dpl = 0,
+        .p = 1,
+        .offset1 = ptr >> 16,
+#ifdef __x86_64__
+        .offset2 = ptr >> 32,
+#endif
+    };
+#ifdef __x86_64__
+    /* sub $8, %rsp */
+    *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08;
+    /* mov $func_low, %(rsp) */
+    *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24;
+    *(u32 *)thunk = (ulong)func; thunk += 4;
+    /* mov $func_high, %(rsp+4) */
+    *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04;
+    *(u32 *)thunk = (ulong)func >> 32; thunk += 4;
+    /* jmp isr_entry_point */
+    *thunk ++ = 0xe9;
+    *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#else
+    /* push $func */
+    *thunk++ = 0x68;
+    *(u32 *)thunk = (ulong)func;
+    /* jmp isr_entry_point */
+    *thunk ++ = 0xe9;
+    *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#endif
+    idt[vec] = ent;
+}
+
+#define MCI_STATUS_VAL   (1ULL<<63)  /* valid error */
+#define MCI_STATUS_OVER  (1ULL<<62)  /* previous errors lost */
+#define MCI_STATUS_UC    (1ULL<<61)  /* uncorrected error */
+#define MCI_STATUS_EN    (1ULL<<60)  /* error enabled */
+#define MCI_STATUS_MISCV (1ULL<<59)  /* misc error reg. valid */
+#define MCI_STATUS_ADDRV (1ULL<<58)  /* addr reg. valid */
+#define MCI_STATUS_PCC   (1ULL<<57)  /* processor context corrupt */
+#define MCI_STATUS_S     (1ULL<<56)  /* Signaled machine check */
+#define MCI_STATUS_AR    (1ULL<<55)  /* Action required */
+
+#define MSR_IA32_MCG_CAP                0x00000179
+#define MSR_IA32_MCG_STATUS             0x0000017a
+#define MSR_IA32_MCG_CTL                0x0000017b
+
+#define MSR_IA32_MC0_CTL                0x00000400
+#define MSR_IA32_MC0_STATUS             0x00000401
+#define MSR_IA32_MC0_ADDR               0x00000402
+#define MSR_IA32_MC0_MISC               0x00000403
+
+#define MSR_IA32_MCx_CTL(x)             (MSR_IA32_MC0_CTL + 4*(x))
+#define MSR_IA32_MCx_STATUS(x)          (MSR_IA32_MC0_STATUS + 4*(x))
+#define MSR_IA32_MCx_ADDR(x)            (MSR_IA32_MC0_ADDR + 4*(x))
+#define MSR_IA32_MCx_MISC(x)            (MSR_IA32_MC0_MISC + 4*(x))
+
+#define X86_CR4_MCE     0x00000040 /* Machine check enable */
+
+
+volatile int ex_done;
+unsigned long long ex_bank, ex_status, ex_cpu, ex_addr;
+
+static void do_handle_mce(isr_regs_t *regs)
+{
+    int i;
+    int nr_banks = rdmsr(MSR_IA32_MCG_CAP) & 0xff;
+    unsigned long status = 0;
+
+    for (i = 0; i < nr_banks; i++) {
+        status = rdmsr(MSR_IA32_MCx_STATUS(i));
+
+        if (status & MCI_STATUS_VAL)
+            break;
+    }
+    ex_bank = i;
+    ex_status = status;
+    ex_cpu = smp_id();
+    ex_addr = rdmsr(MSR_IA32_MCx_ADDR(i));
+    ex_done = 1;
+#ifdef _DEBUG_
+    printf("cpu%d bank=%lx status=%lx addr=%lx\n",
+           ex_cpu, ex_bank, ex_status, ex_addr);
+#endif
+}
+
+int fail = 0;
+
+static void report(const char *name, int cpu, int bank,
+                   unsigned long long status, unsigned long long addr)
+{
+    if (ex_cpu != cpu || bank != ex_bank || status != ex_status ||
+        addr != ex_addr) {
+        printf("MCE %s: FAIL\n", name);
+        fail = 1;
+    } else
+        printf("MCE %s: PASS\n", name);
+}
+
+static void enable_mce(void *data)
+{
+    init_idt();
+    write_cr4(read_cr4() | X86_CR4_MCE);
+}
+
+int main(void)
+{
+    unsigned long long status, addr;
+    int bank;
+
+    smp_init();
+    init_idt();
+    set_idt_entry(18, do_handle_mce);
+
+    write_cr4(read_cr4() | X86_CR4_MCE);
+
+    wrmsr(MSR_IA32_MCG_CTL, ~0ULL);
+    wrmsr(MSR_IA32_MC0_CTL, ~0ULL);
+
+    status = MCI_STATUS_VAL|MCI_STATUS_UC;
+    addr = 0x7ffffff;
+    bank = 0;
+    ex_done = 0;
+    /* mce cpu bank status mcgstatus addr misc */
+    monitor_printf("mce %d %d 0x%llx 1 0x%llx 1\n", 0, bank, status, addr);
+    while (!ex_done);
+    report("test 1", 0, bank, status, addr);
+
+    wrmsr(MSR_IA32_MCG_STATUS, 0);
+    wrmsr(MSR_IA32_MCx_STATUS(bank), 0);
+    addr = 0x8fffff;
+    bank = 9;
+    ex_done = 0;
+    monitor_printf("mce %d %d 0x%llx 1 0x%llx 1\n", 0, bank, status, addr);
+    while (!ex_done);
+    report("test 2", 0, bank, status, addr);
+
+    if (cpu_count() == 1)
+        return fail;
+
+    on_cpu(1, enable_mce, 0);
+    wrmsr(MSR_IA32_MCG_STATUS, 0);
+    wrmsr(MSR_IA32_MCx_STATUS(bank), 0);
+    addr = 0x9fffff;
+    bank = 3;
+    ex_done = 0;
+    monitor_printf("mce %d %d 0x%llx 1 0x%llx 1\n", 1, bank, status, addr);
+    while (!ex_done);
+    report("test 3", 1, bank, status, addr);
+
+    return fail;
+}


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux