Commit 79c4f6b08009a1d23177c2be8bd003253cf3686a
Committed by
Anthony Liguori
1 parent
2152390d
QEMU: MCE: Add MCE simulation to qemu/tcg
- MCE features are initialized when VCPU is intialized according to CPUID. - A monitor command "mce" is added to inject a MCE. - A new interrupt mask: CPU_INTERRUPT_MCE is added to inject the MCE. aliguori: fix build for linux-user Signed-off-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing
8 changed files
with
217 additions
and
2 deletions
cpu-all.h
| ... | ... | @@ -770,6 +770,7 @@ extern int use_icount; |
| 770 | 770 | #define CPU_INTERRUPT_NMI 0x200 /* NMI pending. */ |
| 771 | 771 | #define CPU_INTERRUPT_INIT 0x400 /* INIT pending. */ |
| 772 | 772 | #define CPU_INTERRUPT_SIPI 0x800 /* SIPI pending. */ |
| 773 | +#define CPU_INTERRUPT_MCE 0x1000 /* (x86 only) MCE pending. */ | |
| 773 | 774 | |
| 774 | 775 | void cpu_interrupt(CPUState *s, int mask); |
| 775 | 776 | void cpu_reset_interrupt(CPUState *env, int mask); |
| ... | ... | @@ -1071,4 +1072,7 @@ extern int64_t kqemu_ret_excp_count; |
| 1071 | 1072 | extern int64_t kqemu_ret_intr_count; |
| 1072 | 1073 | #endif |
| 1073 | 1074 | |
| 1075 | +void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, | |
| 1076 | + uint64_t mcg_status, uint64_t addr, uint64_t misc); | |
| 1077 | + | |
| 1074 | 1078 | #endif /* CPU_ALL_H */ | ... | ... |
cpu-exec.c
| ... | ... | @@ -400,6 +400,10 @@ int cpu_exec(CPUState *env1) |
| 400 | 400 | env->hflags2 |= HF2_NMI_MASK; |
| 401 | 401 | do_interrupt(EXCP02_NMI, 0, 0, 0, 1); |
| 402 | 402 | next_tb = 0; |
| 403 | + } else if (interrupt_request & CPU_INTERRUPT_MCE) { | |
| 404 | + env->interrupt_request &= ~CPU_INTERRUPT_MCE; | |
| 405 | + do_interrupt(EXCP12_MCHK, 0, 0, 0, 0); | |
| 406 | + next_tb = 0; | |
| 403 | 407 | } else if ((interrupt_request & CPU_INTERRUPT_HARD) && |
| 404 | 408 | (((env->hflags2 & HF2_VINTR_MASK) && |
| 405 | 409 | (env->hflags2 & HF2_HIF_MASK)) || | ... | ... |
monitor.c
| ... | ... | @@ -1677,6 +1677,28 @@ static void do_acl_remove(Monitor *mon, const char *aclname, const char *match) |
| 1677 | 1677 | } |
| 1678 | 1678 | } |
| 1679 | 1679 | |
| 1680 | +#if defined(TARGET_I386) | |
| 1681 | +static void do_inject_mce(Monitor *mon, | |
| 1682 | + int cpu_index, int bank, | |
| 1683 | + unsigned status_hi, unsigned status_lo, | |
| 1684 | + unsigned mcg_status_hi, unsigned mcg_status_lo, | |
| 1685 | + unsigned addr_hi, unsigned addr_lo, | |
| 1686 | + unsigned misc_hi, unsigned misc_lo) | |
| 1687 | +{ | |
| 1688 | + CPUState *cenv; | |
| 1689 | + uint64_t status = ((uint64_t)status_hi << 32) | status_lo; | |
| 1690 | + uint64_t mcg_status = ((uint64_t)mcg_status_hi << 32) | mcg_status_lo; | |
| 1691 | + uint64_t addr = ((uint64_t)addr_hi << 32) | addr_lo; | |
| 1692 | + uint64_t misc = ((uint64_t)misc_hi << 32) | misc_lo; | |
| 1693 | + | |
| 1694 | + for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) | |
| 1695 | + if (cenv->cpu_index == cpu_index && cenv->mcg_cap) { | |
| 1696 | + cpu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc); | |
| 1697 | + break; | |
| 1698 | + } | |
| 1699 | +} | |
| 1700 | +#endif | |
| 1701 | + | |
| 1680 | 1702 | static const mon_cmd_t mon_cmds[] = { |
| 1681 | 1703 | #include "qemu-monitor.h" |
| 1682 | 1704 | { NULL, NULL, }, |
| ... | ... | @@ -2451,6 +2473,15 @@ static void monitor_handle_command(Monitor *mon, const char *cmdline) |
| 2451 | 2473 | void *arg3, void *arg4, void *arg5); |
| 2452 | 2474 | void (*handler_7)(Monitor *mon, void *arg0, void *arg1, void *arg2, |
| 2453 | 2475 | void *arg3, void *arg4, void *arg5, void *arg6); |
| 2476 | + void (*handler_8)(Monitor *mon, void *arg0, void *arg1, void *arg2, | |
| 2477 | + void *arg3, void *arg4, void *arg5, void *arg6, | |
| 2478 | + void *arg7); | |
| 2479 | + void (*handler_9)(Monitor *mon, void *arg0, void *arg1, void *arg2, | |
| 2480 | + void *arg3, void *arg4, void *arg5, void *arg6, | |
| 2481 | + void *arg7, void *arg8); | |
| 2482 | + void (*handler_10)(Monitor *mon, void *arg0, void *arg1, void *arg2, | |
| 2483 | + void *arg3, void *arg4, void *arg5, void *arg6, | |
| 2484 | + void *arg7, void *arg8, void *arg9); | |
| 2454 | 2485 | |
| 2455 | 2486 | #ifdef DEBUG |
| 2456 | 2487 | monitor_printf(mon, "command='%s'\n", cmdline); |
| ... | ... | @@ -2739,6 +2770,21 @@ static void monitor_handle_command(Monitor *mon, const char *cmdline) |
| 2739 | 2770 | handler_7(mon, args[0], args[1], args[2], args[3], args[4], args[5], |
| 2740 | 2771 | args[6]); |
| 2741 | 2772 | break; |
| 2773 | + case 8: | |
| 2774 | + handler_8 = cmd->handler; | |
| 2775 | + handler_8(mon, args[0], args[1], args[2], args[3], args[4], args[5], | |
| 2776 | + args[6], args[7]); | |
| 2777 | + break; | |
| 2778 | + case 9: | |
| 2779 | + handler_9 = cmd->handler; | |
| 2780 | + handler_9(mon, args[0], args[1], args[2], args[3], args[4], args[5], | |
| 2781 | + args[6], args[7], args[8]); | |
| 2782 | + break; | |
| 2783 | + case 10: | |
| 2784 | + handler_10 = cmd->handler; | |
| 2785 | + handler_10(mon, args[0], args[1], args[2], args[3], args[4], args[5], | |
| 2786 | + args[6], args[7], args[8], args[9]); | |
| 2787 | + break; | |
| 2742 | 2788 | default: |
| 2743 | 2789 | monitor_printf(mon, "unsupported number of arguments: %d\n", nb_args); |
| 2744 | 2790 | goto fail; | ... | ... |
qemu-monitor.hx
| ... | ... | @@ -615,6 +615,14 @@ Remove all matches from the access control list, and set the default |
| 615 | 615 | policy back to @code{deny}. |
| 616 | 616 | ETEXI |
| 617 | 617 | |
| 618 | +#if defined(TARGET_I386) | |
| 619 | + { "mce", "iillll", do_inject_mce, "cpu bank status mcgstatus addr misc", "inject a MCE on the given CPU"}, | |
| 620 | +#endif | |
| 621 | +STEXI | |
| 622 | +@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc} | |
| 623 | +Inject an MCE on the given CPU (x86 only). | |
| 624 | +ETEXI | |
| 625 | + | |
| 618 | 626 | STEXI |
| 619 | 627 | @end table |
| 620 | 628 | ETEXI | ... | ... |
target-i386/cpu.h
| ... | ... | @@ -204,6 +204,7 @@ |
| 204 | 204 | #define CR4_DE_MASK (1 << 3) |
| 205 | 205 | #define CR4_PSE_MASK (1 << 4) |
| 206 | 206 | #define CR4_PAE_MASK (1 << 5) |
| 207 | +#define CR4_MCE_MASK (1 << 6) | |
| 207 | 208 | #define CR4_PGE_MASK (1 << 7) |
| 208 | 209 | #define CR4_PCE_MASK (1 << 8) |
| 209 | 210 | #define CR4_OSFXSR_SHIFT 9 |
| ... | ... | @@ -250,6 +251,17 @@ |
| 250 | 251 | #define PG_ERROR_RSVD_MASK 0x08 |
| 251 | 252 | #define PG_ERROR_I_D_MASK 0x10 |
| 252 | 253 | |
| 254 | +#define MCG_CTL_P (1UL<<8) /* MCG_CAP register available */ | |
| 255 | + | |
| 256 | +#define MCE_CAP_DEF MCG_CTL_P | |
| 257 | +#define MCE_BANKS_DEF 10 | |
| 258 | + | |
| 259 | +#define MCG_STATUS_MCIP (1UL<<2) /* machine check in progress */ | |
| 260 | + | |
| 261 | +#define MCI_STATUS_VAL (1UL<<63) /* valid error */ | |
| 262 | +#define MCI_STATUS_OVER (1UL<<62) /* previous errors lost */ | |
| 263 | +#define MCI_STATUS_UC (1UL<<61) /* uncorrected error */ | |
| 264 | + | |
| 253 | 265 | #define MSR_IA32_TSC 0x10 |
| 254 | 266 | #define MSR_IA32_APICBASE 0x1b |
| 255 | 267 | #define MSR_IA32_APICBASE_BSP (1<<8) |
| ... | ... | @@ -290,6 +302,11 @@ |
| 290 | 302 | |
| 291 | 303 | #define MSR_MTRRdefType 0x2ff |
| 292 | 304 | |
| 305 | +#define MSR_MC0_CTL 0x400 | |
| 306 | +#define MSR_MC0_STATUS 0x401 | |
| 307 | +#define MSR_MC0_ADDR 0x402 | |
| 308 | +#define MSR_MC0_MISC 0x403 | |
| 309 | + | |
| 293 | 310 | #define MSR_EFER 0xc0000080 |
| 294 | 311 | |
| 295 | 312 | #define MSR_EFER_SCE (1 << 0) |
| ... | ... | @@ -678,6 +695,11 @@ typedef struct CPUX86State { |
| 678 | 695 | /* in order to simplify APIC support, we leave this pointer to the |
| 679 | 696 | user */ |
| 680 | 697 | struct APICState *apic_state; |
| 698 | + | |
| 699 | + uint64 mcg_cap; | |
| 700 | + uint64 mcg_status; | |
| 701 | + uint64 mcg_ctl; | |
| 702 | + uint64 *mce_banks; | |
| 681 | 703 | } CPUX86State; |
| 682 | 704 | |
| 683 | 705 | CPUX86State *cpu_x86_init(const char *cpu_model); |
| ... | ... | @@ -842,7 +864,7 @@ static inline int cpu_get_time_fast(void) |
| 842 | 864 | #define cpu_signal_handler cpu_x86_signal_handler |
| 843 | 865 | #define cpu_list x86_cpu_list |
| 844 | 866 | |
| 845 | -#define CPU_SAVE_VERSION 9 | |
| 867 | +#define CPU_SAVE_VERSION 10 | |
| 846 | 868 | |
| 847 | 869 | /* MMU modes definitions */ |
| 848 | 870 | #define MMU_MODE0_SUFFIX _kernel | ... | ... |
target-i386/helper.c
| ... | ... | @@ -1496,8 +1496,77 @@ static void breakpoint_handler(CPUState *env) |
| 1496 | 1496 | if (prev_debug_excp_handler) |
| 1497 | 1497 | prev_debug_excp_handler(env); |
| 1498 | 1498 | } |
| 1499 | + | |
| 1500 | +/* This should come from sysemu.h - if we could include it here... */ | |
| 1501 | +void qemu_system_reset_request(void); | |
| 1502 | + | |
| 1503 | +void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, | |
| 1504 | + uint64_t mcg_status, uint64_t addr, uint64_t misc) | |
| 1505 | +{ | |
| 1506 | + uint64_t mcg_cap = cenv->mcg_cap; | |
| 1507 | + unsigned bank_num = mcg_cap & 0xff; | |
| 1508 | + uint64_t *banks = cenv->mce_banks; | |
| 1509 | + | |
| 1510 | + if (bank >= bank_num || !(status & MCI_STATUS_VAL)) | |
| 1511 | + return; | |
| 1512 | + | |
| 1513 | + /* | |
| 1514 | + * if MSR_MCG_CTL is not all 1s, the uncorrected error | |
| 1515 | + * reporting is disabled | |
| 1516 | + */ | |
| 1517 | + if ((status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) && | |
| 1518 | + cenv->mcg_ctl != ~(uint64_t)0) | |
| 1519 | + return; | |
| 1520 | + banks += 4 * bank; | |
| 1521 | + /* | |
| 1522 | + * if MSR_MCi_CTL is not all 1s, the uncorrected error | |
| 1523 | + * reporting is disabled for the bank | |
| 1524 | + */ | |
| 1525 | + if ((status & MCI_STATUS_UC) && banks[0] != ~(uint64_t)0) | |
| 1526 | + return; | |
| 1527 | + if (status & MCI_STATUS_UC) { | |
| 1528 | + if ((cenv->mcg_status & MCG_STATUS_MCIP) || | |
| 1529 | + !(cenv->cr[4] & CR4_MCE_MASK)) { | |
| 1530 | + fprintf(stderr, "injects mce exception while previous " | |
| 1531 | + "one is in progress!\n"); | |
| 1532 | + qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); | |
| 1533 | + qemu_system_reset_request(); | |
| 1534 | + return; | |
| 1535 | + } | |
| 1536 | + if (banks[1] & MCI_STATUS_VAL) | |
| 1537 | + status |= MCI_STATUS_OVER; | |
| 1538 | + banks[2] = addr; | |
| 1539 | + banks[3] = misc; | |
| 1540 | + cenv->mcg_status = mcg_status; | |
| 1541 | + banks[1] = status; | |
| 1542 | + cpu_interrupt(cenv, CPU_INTERRUPT_MCE); | |
| 1543 | + } else if (!(banks[1] & MCI_STATUS_VAL) | |
| 1544 | + || !(banks[1] & MCI_STATUS_UC)) { | |
| 1545 | + if (banks[1] & MCI_STATUS_VAL) | |
| 1546 | + status |= MCI_STATUS_OVER; | |
| 1547 | + banks[2] = addr; | |
| 1548 | + banks[3] = misc; | |
| 1549 | + banks[1] = status; | |
| 1550 | + } else | |
| 1551 | + banks[1] |= MCI_STATUS_OVER; | |
| 1552 | +} | |
| 1499 | 1553 | #endif /* !CONFIG_USER_ONLY */ |
| 1500 | 1554 | |
| 1555 | +static void mce_init(CPUX86State *cenv) | |
| 1556 | +{ | |
| 1557 | + unsigned int bank, bank_num; | |
| 1558 | + | |
| 1559 | + if (((cenv->cpuid_version >> 8)&0xf) >= 6 | |
| 1560 | + && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)) { | |
| 1561 | + cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF; | |
| 1562 | + cenv->mcg_ctl = ~(uint64_t)0; | |
| 1563 | + bank_num = cenv->mcg_cap & 0xff; | |
| 1564 | + cenv->mce_banks = qemu_mallocz(bank_num * sizeof(uint64_t) * 4); | |
| 1565 | + for (bank = 0; bank < bank_num; bank++) | |
| 1566 | + cenv->mce_banks[bank*4] = ~(uint64_t)0; | |
| 1567 | + } | |
| 1568 | +} | |
| 1569 | + | |
| 1501 | 1570 | static void host_cpuid(uint32_t function, uint32_t count, |
| 1502 | 1571 | uint32_t *eax, uint32_t *ebx, |
| 1503 | 1572 | uint32_t *ecx, uint32_t *edx) |
| ... | ... | @@ -1735,6 +1804,7 @@ CPUX86State *cpu_x86_init(const char *cpu_model) |
| 1735 | 1804 | cpu_x86_close(env); |
| 1736 | 1805 | return NULL; |
| 1737 | 1806 | } |
| 1807 | + mce_init(env); | |
| 1738 | 1808 | cpu_reset(env); |
| 1739 | 1809 | #ifdef CONFIG_KQEMU |
| 1740 | 1810 | kqemu_init(env); | ... | ... |
target-i386/machine.c
| ... | ... | @@ -158,7 +158,20 @@ void cpu_save(QEMUFile *f, void *opaque) |
| 158 | 158 | qemu_put_sbe32s(f, &pending_irq); |
| 159 | 159 | qemu_put_be32s(f, &env->mp_state); |
| 160 | 160 | qemu_put_be64s(f, &env->tsc); |
| 161 | -} | |
| 161 | + | |
| 162 | + /* MCE */ | |
| 163 | + qemu_put_be64s(f, &env->mcg_cap); | |
| 164 | + if (env->mcg_cap) { | |
| 165 | + qemu_put_be64s(f, &env->mcg_status); | |
| 166 | + qemu_put_be64s(f, &env->mcg_ctl); | |
| 167 | + for (i = 0; i < (env->mcg_cap & 0xff); i++) { | |
| 168 | + qemu_put_be64s(f, &env->mce_banks[4*i]); | |
| 169 | + qemu_put_be64s(f, &env->mce_banks[4*i + 1]); | |
| 170 | + qemu_put_be64s(f, &env->mce_banks[4*i + 2]); | |
| 171 | + qemu_put_be64s(f, &env->mce_banks[4*i + 3]); | |
| 172 | + } | |
| 173 | + } | |
| 174 | + } | |
| 162 | 175 | |
| 163 | 176 | #ifdef USE_X86LDOUBLE |
| 164 | 177 | /* XXX: add that in a FPU generic layer */ |
| ... | ... | @@ -349,6 +362,20 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) |
| 349 | 362 | qemu_get_be64s(f, &env->tsc); |
| 350 | 363 | } |
| 351 | 364 | |
| 365 | + if (version_id >= 10) { | |
| 366 | + qemu_get_be64s(f, &env->mcg_cap); | |
| 367 | + if (env->mcg_cap) { | |
| 368 | + qemu_get_be64s(f, &env->mcg_status); | |
| 369 | + qemu_get_be64s(f, &env->mcg_ctl); | |
| 370 | + for (i = 0; i < (env->mcg_cap & 0xff); i++) { | |
| 371 | + qemu_get_be64s(f, &env->mce_banks[4*i]); | |
| 372 | + qemu_get_be64s(f, &env->mce_banks[4*i + 1]); | |
| 373 | + qemu_get_be64s(f, &env->mce_banks[4*i + 2]); | |
| 374 | + qemu_get_be64s(f, &env->mce_banks[4*i + 3]); | |
| 375 | + } | |
| 376 | + } | |
| 377 | + } | |
| 378 | + | |
| 352 | 379 | /* XXX: ensure compatiblity for halted bit ? */ |
| 353 | 380 | /* XXX: compute redundant hflags bits */ |
| 354 | 381 | env->hflags = hflags; | ... | ... |
target-i386/op_helper.c
| ... | ... | @@ -3133,7 +3133,23 @@ void helper_wrmsr(void) |
| 3133 | 3133 | case MSR_MTRRdefType: |
| 3134 | 3134 | env->mtrr_deftype = val; |
| 3135 | 3135 | break; |
| 3136 | + case MSR_MCG_STATUS: | |
| 3137 | + env->mcg_status = val; | |
| 3138 | + break; | |
| 3139 | + case MSR_MCG_CTL: | |
| 3140 | + if ((env->mcg_cap & MCG_CTL_P) | |
| 3141 | + && (val == 0 || val == ~(uint64_t)0)) | |
| 3142 | + env->mcg_ctl = val; | |
| 3143 | + break; | |
| 3136 | 3144 | default: |
| 3145 | + if ((uint32_t)ECX >= MSR_MC0_CTL | |
| 3146 | + && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) { | |
| 3147 | + uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL; | |
| 3148 | + if ((offset & 0x3) != 0 | |
| 3149 | + || (val == 0 || val == ~(uint64_t)0)) | |
| 3150 | + env->mce_banks[offset] = val; | |
| 3151 | + break; | |
| 3152 | + } | |
| 3137 | 3153 | /* XXX: exception ? */ |
| 3138 | 3154 | break; |
| 3139 | 3155 | } |
| ... | ... | @@ -3252,7 +3268,25 @@ void helper_rdmsr(void) |
| 3252 | 3268 | /* XXX: exception ? */ |
| 3253 | 3269 | val = 0; |
| 3254 | 3270 | break; |
| 3271 | + case MSR_MCG_CAP: | |
| 3272 | + val = env->mcg_cap; | |
| 3273 | + break; | |
| 3274 | + case MSR_MCG_CTL: | |
| 3275 | + if (env->mcg_cap & MCG_CTL_P) | |
| 3276 | + val = env->mcg_ctl; | |
| 3277 | + else | |
| 3278 | + val = 0; | |
| 3279 | + break; | |
| 3280 | + case MSR_MCG_STATUS: | |
| 3281 | + val = env->mcg_status; | |
| 3282 | + break; | |
| 3255 | 3283 | default: |
| 3284 | + if ((uint32_t)ECX >= MSR_MC0_CTL | |
| 3285 | + && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) { | |
| 3286 | + uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL; | |
| 3287 | + val = env->mce_banks[offset]; | |
| 3288 | + break; | |
| 3289 | + } | |
| 3256 | 3290 | /* XXX: exception ? */ |
| 3257 | 3291 | val = 0; |
| 3258 | 3292 | break; | ... | ... |