Index: FreeBSD/sys/x86/x86/mca.c =================================================================== --- FreeBSD/sys/x86/x86/mca.c +++ FreeBSD/sys/x86/x86/mca.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,11 @@ &workaround_erratum383, 0, "Is the workaround for Erratum 383 on AMD Family 10h processors enabled?"); +static bool mca_uselog = false; +SYSCTL_BOOL(_hw_mca, OID_AUTO, uselog, CTLFLAG_RWTUN, &mca_uselog, 0, + "Should the system send non-fatal machine check errors to the log " + "(instead of the console)?"); + static STAILQ_HEAD(, mca_internal) mca_freelist; static int mca_freecount; static STAILQ_HEAD(, mca_internal) mca_records; @@ -294,59 +300,78 @@ /* Dump details about a single machine check. */ static void -mca_log(const struct mca_record *rec) +mca_log(const struct mca_record *rec, bool fatal) { + char buf[1024], *bufp, *bufend; uint16_t mca_error; + bool uncor; if (mca_mute(rec)) return; - printf("MCA: Bank %d, Status 0x%016llx\n", rec->mr_bank, + uncor = false; + + bufp = buf; + bufend = bufp + 1024; + buf[0] = '\0'; + +#define ADD_OUTPUT(fmt, ...) do { \ + int rv; \ + \ + rv = snprintf(bufp, bufend - bufp, fmt, ##__VA_ARGS__); \ + if (rv > 0) \ + bufp += rv; \ + if (rv < 0 || bufp >= bufend) \ + goto done; \ +} while (0) + + ADD_OUTPUT("MCA: Bank %d, Status 0x%016llx\n", rec->mr_bank, (long long)rec->mr_status); - printf("MCA: Global Cap 0x%016llx, Status 0x%016llx\n", + ADD_OUTPUT("MCA: Global Cap 0x%016llx, Status 0x%016llx\n", (long long)rec->mr_mcg_cap, (long long)rec->mr_mcg_status); - printf("MCA: Vendor \"%s\", ID 0x%x, APIC ID %d\n", cpu_vendor, + ADD_OUTPUT("MCA: Vendor \"%s\", ID 0x%x, APIC ID %d\n", cpu_vendor, rec->mr_cpu_id, rec->mr_apic_id); - printf("MCA: CPU %d ", rec->mr_cpu); - if (rec->mr_status & MC_STATUS_UC) - printf("UNCOR "); - else { - printf("COR "); + ADD_OUTPUT("MCA: CPU %d ", rec->mr_cpu); + if (rec->mr_status & MC_STATUS_UC) { + uncor = true; + ADD_OUTPUT("UNCOR "); + } else { + ADD_OUTPUT("COR "); if (rec->mr_mcg_cap & MCG_CAP_CMCI_P) - printf("(%lld) ", ((long long)rec->mr_status & + ADD_OUTPUT("(%lld) ", ((long long)rec->mr_status & MC_STATUS_COR_COUNT) >> 38); } if (rec->mr_status & MC_STATUS_PCC) - printf("PCC "); + ADD_OUTPUT("PCC "); if (rec->mr_status & MC_STATUS_OVER) - printf("OVER "); + ADD_OUTPUT("OVER "); mca_error = rec->mr_status & MC_STATUS_MCA_ERROR; switch (mca_error) { /* Simple error codes. */ case 0x0000: - printf("no error"); + ADD_OUTPUT("no error"); break; case 0x0001: - printf("unclassified error"); + ADD_OUTPUT("unclassified error"); break; case 0x0002: - printf("ucode ROM parity error"); + ADD_OUTPUT("ucode ROM parity error"); break; case 0x0003: - printf("external error"); + ADD_OUTPUT("external error"); break; case 0x0004: - printf("FRC error"); + ADD_OUTPUT("FRC error"); break; case 0x0005: - printf("internal parity error"); + ADD_OUTPUT("internal parity error"); break; case 0x0400: - printf("internal timer error"); + ADD_OUTPUT("internal timer error"); break; default: if ((mca_error & 0xfc00) == 0x0400) { - printf("internal error %x", mca_error & 0x03ff); + ADD_OUTPUT("internal error %x", mca_error & 0x03ff); break; } @@ -354,31 +379,32 @@ /* Memory hierarchy error. */ if ((mca_error & 0xeffc) == 0x000c) { - printf("%s memory error", mca_error_level(mca_error)); + ADD_OUTPUT("%s memory error", + mca_error_level(mca_error)); break; } /* TLB error. */ if ((mca_error & 0xeff0) == 0x0010) { - printf("%sTLB %s error", mca_error_ttype(mca_error), + ADD_OUTPUT("%sTLB %s error", mca_error_ttype(mca_error), mca_error_level(mca_error)); break; } /* Memory controller error. */ if ((mca_error & 0xef80) == 0x0080) { - printf("%s channel ", mca_error_mmtype(mca_error)); + ADD_OUTPUT("%s channel ", mca_error_mmtype(mca_error)); if ((mca_error & 0x000f) != 0x000f) - printf("%d", mca_error & 0x000f); + ADD_OUTPUT("%d", mca_error & 0x000f); else - printf("??"); - printf(" memory error"); + ADD_OUTPUT("??"); + ADD_OUTPUT(" memory error"); break; } /* Cache error. */ if ((mca_error & 0xef00) == 0x0100) { - printf("%sCACHE %s %s error", + ADD_OUTPUT("%sCACHE %s %s error", mca_error_ttype(mca_error), mca_error_level(mca_error), mca_error_request(mca_error)); @@ -387,49 +413,56 @@ /* Bus and/or Interconnect error. */ if ((mca_error & 0xe800) == 0x0800) { - printf("BUS%s ", mca_error_level(mca_error)); + ADD_OUTPUT("BUS%s ", mca_error_level(mca_error)); switch ((mca_error & 0x0600) >> 9) { case 0: - printf("Source"); + ADD_OUTPUT("Source"); break; case 1: - printf("Responder"); + ADD_OUTPUT("Responder"); break; case 2: - printf("Observer"); + ADD_OUTPUT("Observer"); break; default: - printf("???"); + ADD_OUTPUT("???"); break; } - printf(" %s ", mca_error_request(mca_error)); + ADD_OUTPUT(" %s ", mca_error_request(mca_error)); switch ((mca_error & 0x000c) >> 2) { case 0: - printf("Memory"); + ADD_OUTPUT("Memory"); break; case 2: - printf("I/O"); + ADD_OUTPUT("I/O"); break; case 3: - printf("Other"); + ADD_OUTPUT("Other"); break; default: - printf("???"); + ADD_OUTPUT("???"); break; } if (mca_error & 0x0100) - printf(" timed out"); + ADD_OUTPUT(" timed out"); break; } - printf("unknown error %x", mca_error); + ADD_OUTPUT("unknown error %x", mca_error); break; } - printf("\n"); if (rec->mr_status & MC_STATUS_ADDRV) - printf("MCA: Address 0x%llx\n", (long long)rec->mr_addr); + ADD_OUTPUT("\nMCA: Address 0x%llx", (long long)rec->mr_addr); if (rec->mr_status & MC_STATUS_MISCV) - printf("MCA: Misc 0x%llx\n", (long long)rec->mr_misc); + ADD_OUTPUT("\nMCA: Misc 0x%llx", (long long)rec->mr_misc); +#undef ADD_OUTPUT + +done: + if (fatal || !mca_uselog) + printf("%s\n", buf); + else { + log(uncor ? LOG_CRIT : LOG_ERR, "%s", buf); + } } static int @@ -506,7 +539,7 @@ STAILQ_FOREACH_FROM(mca, &mca_records, link) { if (!mca->logged) { mca->logged = 1; - mca_log(&mca->rec); + mca_log(&mca->rec, false); } mca_lastrec = mca; } @@ -530,7 +563,8 @@ } static void -mca_record_entry(enum scan_mode mode, const struct mca_record *record) +mca_record_entry(enum scan_mode mode, const struct mca_record *record, + bool logged) { struct mca_internal *rec; @@ -540,9 +574,9 @@ } else { mtx_lock_spin(&mca_lock); rec = STAILQ_FIRST(&mca_freelist); - if (rec == NULL) { + if (rec == NULL && !logged) { printf("MCA: Unable to allocate space for an event.\n"); - mca_log(record); + mca_log(record, false); mtx_unlock_spin(&mca_lock); return; } @@ -551,7 +585,7 @@ } rec->rec = *record; - rec->logged = 0; + rec->logged = logged ? 1: 0; STAILQ_INSERT_TAIL(&mca_records, rec, link); mca_count++; mtx_unlock_spin(&mca_lock); @@ -717,10 +751,11 @@ if (rec.mr_status & ucmask) { recoverable = 0; mtx_lock_spin(&mca_lock); - mca_log(&rec); + mca_log(&rec, (mode == MCE)); mtx_unlock_spin(&mca_lock); - } - mca_record_entry(mode, &rec); + mca_record_entry(mode, &rec, true); + } else + mca_record_entry(mode, &rec, false); } #ifdef DEV_APIC