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; | ... | ... |