return rec;
}
-mce_check_addr_t mc_check_addr = NULL;
-
-void __init mce_register_addrcheck(mce_check_addr_t cbfunc)
-{
- mc_check_addr = cbfunc;
-}
-
void
mc_memerr_dhandler(struct mca_binfo *binfo,
enum mce_result *result,
int vmce_vcpuid;
unsigned int mc_vcpuid;
- if ( !mc_check_addr(bank->mc_status, bank->mc_misc, MC_ADDR_PHYSICAL) )
+ if ( !alternative_call(mce_callbacks.check_addr, bank->mc_status,
+ bank->mc_misc, MC_ADDR_PHYSICAL) )
{
dprintk(XENLOG_WARNING,
"No physical address provided for memory error\n");
#define MC_ADDR_PHYSICAL 0
#define MC_ADDR_VIRTUAL 1
-typedef bool (*mce_check_addr_t)(uint64_t status, uint64_t misc, int addr_type);
-extern void mce_register_addrcheck(mce_check_addr_t);
-
-extern mce_check_addr_t mc_check_addr;
-
#endif
fatal_trap(regs, 1);
}
-static x86_mce_vector_t _machine_check_vector = unexpected_machine_check;
-
-void __init x86_mce_vector_register(x86_mce_vector_t hdlr)
-{
- _machine_check_vector = hdlr;
-}
+struct mce_callbacks __ro_after_init mce_callbacks = {
+ .handler = unexpected_machine_check,
+};
+static const typeof(mce_callbacks.handler) __initconst_cf_clobber __used
+ default_handler = unexpected_machine_check;
/* Call the installed machine check handler for this CPU setup. */
void do_machine_check(const struct cpu_user_regs *regs)
{
mce_enter();
- _machine_check_vector(regs);
+ alternative_vcall(mce_callbacks.handler, regs);
mce_exit();
}
-/*
- * Init machine check callback handler
- * It is used to collect additional information provided by newer
- * CPU families/models without the need to duplicate the whole handler.
- * This avoids having many handlers doing almost nearly the same and each
- * with its own tweaks ands bugs.
- */
-static x86_mce_callback_t mc_callback_bank_extended = NULL;
-
-void __init x86_mce_callback_register(x86_mce_callback_t cbfunc)
-{
- mc_callback_bank_extended = cbfunc;
-}
-
-/*
- * Machine check recoverable judgement callback handler
- * It is used to judge whether an UC error is recoverable by software
- */
-static mce_recoverable_t mc_recoverable_scan = NULL;
-
-void __init mce_recoverable_register(mce_recoverable_t cbfunc)
-{
- mc_recoverable_scan = cbfunc;
-}
-
struct mca_banks *mcabanks_alloc(unsigned int nr_mce_banks)
{
struct mca_banks *mb;
mca_wrmsr(MSR_IA32_MCx_STATUS(banknum), 0x0ULL);
}
-/*
- * Judging whether to Clear Machine Check error bank callback handler
- * According to Intel latest MCA OS Recovery Writer's Guide,
- * whether the error MCA bank needs to be cleared is decided by the mca_source
- * and MCi_status bit value.
- */
-static mce_need_clearbank_t mc_need_clearbank_scan = NULL;
-
-void __init mce_need_clearbank_register(mce_need_clearbank_t cbfunc)
-{
- mc_need_clearbank_scan = cbfunc;
-}
-
/*
* mce_logout_lock should only be used in the trap handler,
* while MCIP has not been cleared yet in the global status
if ( (mib->mc_status & MCi_STATUS_MISCV) &&
(mib->mc_status & MCi_STATUS_ADDRV) &&
- (mc_check_addr(mib->mc_status, mib->mc_misc, MC_ADDR_PHYSICAL)) &&
+ alternative_call(mce_callbacks.check_addr, mib->mc_status,
+ mib->mc_misc, MC_ADDR_PHYSICAL) &&
(who == MCA_POLLER || who == MCA_CMCI_HANDLER) &&
(mfn_valid(_mfn(paddr_to_pfn(mib->mc_addr)))) )
{
* If no mc_recovery_scan callback handler registered,
* this error is not recoverable
*/
- recover = mc_recoverable_scan ? 1 : 0;
+ recover = mce_callbacks.recoverable_scan;
for ( i = 0; i < this_cpu(nr_mce_banks); i++ )
{
* decide whether to clear bank by MCi_STATUS bit value such as
* OVER/UC/EN/PCC/S/AR
*/
- if ( mc_need_clearbank_scan )
- need_clear = mc_need_clearbank_scan(who, status);
+ if ( mce_callbacks.need_clearbank_scan )
+ need_clear = alternative_call(mce_callbacks.need_clearbank_scan,
+ who, status);
/*
* If this is the first bank with valid MCA DATA, then
if ( recover && uc )
/* uc = true, recover = true, we need not panic. */
- recover = mc_recoverable_scan(status);
+ recover = alternative_call(mce_callbacks.recoverable_scan, status);
mca_init_bank(who, mci, i);
- if ( mc_callback_bank_extended )
- mc_callback_bank_extended(mci, i, status);
+ if ( mce_callbacks.info_collect )
+ alternative_vcall(mce_callbacks.info_collect, mci, i, status);
/* By default, need_clear = true */
if ( who != MCA_MCE_SCAN && need_clear )
* will help to collect and log those MCE errors.
* Round2: Do all MCE processing logic as normal.
*/
-void __init mce_handler_init(void)
+void __init mce_handler_init(const struct mce_callbacks *cb)
{
/* callback register, do we really need so many callback? */
+ mce_callbacks = *cb;
+
/* mce handler data initialization */
spin_lock_init(&mce_logout_lock);
open_softirq(MACHINE_CHECK_SOFTIRQ, mce_softirq);
void x86_mc_get_cpu_info(unsigned, uint32_t *, uint16_t *, uint16_t *,
uint32_t *, uint32_t *, uint32_t *, uint32_t *);
-/* Register a handler for machine check exceptions. */
-typedef void (*x86_mce_vector_t)(const struct cpu_user_regs *regs);
-extern void x86_mce_vector_register(x86_mce_vector_t);
-
/*
* Common generic MCE handler that implementations may nominate
* via x86_mce_vector_register.
*/
void cf_check mcheck_cmn_handler(const struct cpu_user_regs *regs);
-/* Register a handler for judging whether mce is recoverable. */
-typedef bool (*mce_recoverable_t)(uint64_t status);
-extern void mce_recoverable_register(mce_recoverable_t);
-
/* Read an MSR, checking for an interposed value first */
extern struct intpose_ent *intpose_lookup(unsigned int, uint64_t,
uint64_t *);
extern mctelem_cookie_t mcheck_mca_logout(enum mca_source, struct mca_banks *,
struct mca_summary *, struct mca_banks *);
-/*
- * Register callbacks to be made during bank telemetry logout.
- * Those callbacks are only available to those machine check handlers
- * that call to the common mcheck_cmn_handler or who use the common
- * telemetry logout function mcheck_mca_logout in error polling.
- */
-
-/* Register a handler for judging whether the bank need to be cleared */
-typedef bool (*mce_need_clearbank_t)(enum mca_source who, u64 status);
-extern void mce_need_clearbank_register(mce_need_clearbank_t);
-
-/*
- * Register a callback to collect additional information (typically non-
- * architectural) provided by newer CPU families/models without the need
- * to duplicate the whole handler resulting in various handlers each with
- * its own tweaks and bugs. The callback receives an struct mc_info pointer
- * which it can use with x86_mcinfo_reserve to add additional telemetry,
- * the current MCA bank number we are reading telemetry from, and the
- * MCi_STATUS value for that bank.
- */
-typedef struct mcinfo_extended *(*x86_mce_callback_t)
- (struct mc_info *, uint16_t, uint64_t);
-extern void x86_mce_callback_register(x86_mce_callback_t);
-
void *x86_mcinfo_reserve(struct mc_info *mi,
unsigned int size, unsigned int type);
void x86_mcinfo_dump(struct mc_info *mi);
return 0;
}
-/* MC softirq */
-void mce_handler_init(void);
+struct mce_callbacks {
+ void (*handler)(const struct cpu_user_regs *regs);
+ bool (*check_addr)(uint64_t status, uint64_t misc, int addr_type);
+
+ /* Handler for judging whether mce is recoverable. */
+ bool (*recoverable_scan)(uint64_t status);
+
+ /*
+ * Callbacks to be made during bank telemetry logout.
+ * They are only available to those machine check handlers
+ * that call to the common mcheck_cmn_handler or who use the common
+ * telemetry logout function mcheck_mca_logout in error polling.
+ */
+
+ /*
+ * Judging whether to Clear Machine Check error bank callback handler.
+ * According to Intel latest MCA OS Recovery Writer's Guide, whether
+ * the error MCA bank needs to be cleared is decided by the mca_source
+ * and MCi_status bit value.
+ */
+ bool (*need_clearbank_scan)(enum mca_source who, u64 status);
+
+ /*
+ * Callback to collect additional information (typically non-
+ * architectural) provided by newer CPU families/models without the need
+ * to duplicate the whole handler resulting in various handlers each with
+ * its own tweaks and bugs. The callback receives an struct mc_info pointer
+ * which it can use with x86_mcinfo_reserve to add additional telemetry,
+ * the current MCA bank number we are reading telemetry from, and the
+ * MCi_STATUS value for that bank.
+ */
+ struct mcinfo_extended *(*info_collect)
+ (struct mc_info *mi, uint16_t bank, uint64_t status);
+};
+
+extern struct mce_callbacks mce_callbacks;
+
+void mce_handler_init(const struct mce_callbacks *cb);
extern const struct mca_error_handler *mce_dhandlers;
extern const struct mca_error_handler *mce_uhandlers;
return 1;
}
+static const struct mce_callbacks __initconst_cf_clobber k8_callbacks = {
+ .handler = mcheck_cmn_handler,
+ .need_clearbank_scan = amd_need_clearbank_scan,
+};
+
+static const struct mce_callbacks __initconst_cf_clobber k10_callbacks = {
+ .handler = mcheck_cmn_handler,
+ .check_addr = mc_amd_addrcheck,
+ .recoverable_scan = mc_amd_recoverable_scan,
+ .need_clearbank_scan = amd_need_clearbank_scan,
+ .info_collect = amd_f10_handler,
+};
+
enum mcheck_type
amd_mcheck_init(const struct cpuinfo_x86 *ci, bool bsp)
{
/* Assume that machine check support is available.
* The minimum provided support is at least the K8. */
if ( bsp )
- {
- mce_handler_init();
- x86_mce_vector_register(mcheck_cmn_handler);
- mce_need_clearbank_register(amd_need_clearbank_scan);
- }
+ mce_handler_init(ci->x86 == 0xf ? &k8_callbacks : &k10_callbacks);
for ( i = 0; i < this_cpu(nr_mce_banks); i++ )
{
ppin_msr = MSR_AMD_PPIN;
}
- if ( bsp )
- {
- x86_mce_callback_register(amd_f10_handler);
- mce_recoverable_register(mc_amd_recoverable_scan);
- mce_register_addrcheck(mc_amd_addrcheck);
- }
-
return ci->x86_vendor == X86_VENDOR_HYGON ?
mcheck_hygon : mcheck_amd_famXX;
}
if ( !bsp )
return;
- x86_mce_vector_register(mcheck_cmn_handler);
- mce_recoverable_register(intel_recoverable_scan);
- mce_need_clearbank_register(intel_need_clearbank_scan);
- mce_register_addrcheck(intel_checkaddr);
-
mce_dhandlers = intel_mce_dhandlers;
mce_dhandler_num = ARRAY_SIZE(intel_mce_dhandlers);
mce_uhandlers = intel_mce_uhandlers;
return !rc ? NOTIFY_DONE : notifier_from_errno(rc);
}
+static const struct mce_callbacks __initconst_cf_clobber intel_callbacks = {
+ .handler = mcheck_cmn_handler,
+ .check_addr = intel_checkaddr,
+ .recoverable_scan = intel_recoverable_scan,
+ .need_clearbank_scan = intel_need_clearbank_scan,
+};
+
static struct notifier_block cpu_nfb = {
.notifier_call = cpu_callback
};
intel_init_mca(c);
if ( bsp )
- mce_handler_init();
+ mce_handler_init(&intel_callbacks);
intel_init_mce(bsp);