#ifndef _MCE_H

#define _MCE_H

#include <xen/init.h>
#include <xen/percpu.h>
#include <xen/sched.h>
#include <xen/smp.h>

#include <asm/atomic.h>
#include <asm/msr.h>
#include <asm/traps.h>

#include "mctelem.h"
#include "x86_mca.h"

#define MCE_QUIET       0
#define MCE_VERBOSE     1
/* !only for developer debug as printk is unsafe in MCE context */
#define MCE_CRITICAL    2

extern int mce_verbosity;
/*
 * Define the default level of machine check related print.
 * When set mce_verbosity=verbose, all mce debug information
 * will be printed, otherwise, those information will not be
 * printed.
 */
#define mce_printk(v, s, a...) do {       \
        if ((v) <= mce_verbosity) \
            printk(s, ##a);       \
        } while (0)

enum mcheck_type {
    mcheck_unset = -1,
    mcheck_none,
    mcheck_amd_famXX,
    mcheck_amd_k8,
    mcheck_intel,
    mcheck_hygon
};

extern uint8_t cmci_apic_vector;
extern bool lmce_support;

/* Init functions */
enum mcheck_type amd_mcheck_init(const struct cpuinfo_x86 *c, bool bsp);
enum mcheck_type intel_mcheck_init(struct cpuinfo_x86 *c, bool bsp);

void amd_nonfatal_mcheck_init(struct cpuinfo_x86 *c);
void intel_nonfatal_mcheck_init(struct cpuinfo_x86 *c);

extern unsigned int firstbank;
extern unsigned int ppin_msr;

struct mcinfo_extended *intel_get_extended_msrs(
    struct mcinfo_global *mig, struct mc_info *mi);

bool mce_available(const struct cpuinfo_x86 *c);
unsigned int mce_firstbank(struct cpuinfo_x86 *c);
/* Helper functions used for collecting error telemetry */
void noreturn mc_panic(const char *s);
void x86_mc_get_cpu_info(unsigned cpu, uint32_t *chipid, uint16_t *coreid,
                         uint16_t *threadid, uint32_t *apicid,
                         unsigned *ncores, unsigned *ncores_active,
                         unsigned *nthreads);

/*
 * 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);

/* Read an MSR, checking for an interposed value first */
extern struct intpose_ent *intpose_lookup(unsigned int cpu_nr, uint64_t msr,
    uint64_t *valp);
extern bool intpose_inval(unsigned int cpu_nr, uint64_t msr);

static inline uint64_t mca_rdmsr(unsigned int msr)
{
    uint64_t val;
    if (intpose_lookup(smp_processor_id(), msr, &val) == NULL)
        rdmsrl(msr, val);
    return val;
}

/* Write an MSR, invalidating any interposed value */
#define mca_wrmsr(msr, val) do { \
    if ( !intpose_inval(smp_processor_id(), msr) ) \
        wrmsrl(msr, val); \
} while ( 0 )


/*
 * Utility function to "logout" all architectural MCA telemetry from the MCA
 * banks of the current processor.  A cookie is returned which may be
 * uses to reference the data so logged (the cookie can be NULL if
 * no logout structures were available).  The caller can also pass a pointer
 * to a structure which will be completed with some summary information
 * of the MCA data observed in the logout operation.
 */

enum mca_source {
    MCA_POLLER,
    MCA_CMCI_HANDLER,
    MCA_RESET,
    MCA_MCE_SCAN
};

struct mca_summary {
    uint32_t    errcnt; /* number of banks with valid errors */
    int         ripv;   /* meaningful on #MC */
    int         eipv;   /* meaningful on #MC */
    bool        uc;     /* UC flag */
    bool        pcc;    /* PCC flag */
    bool        lmce;   /* LMCE flag (Intel only) */
    bool        recoverable; /* software error recoverable flag */
};

DECLARE_PER_CPU(struct mca_banks *, poll_bankmask);
DECLARE_PER_CPU(struct mca_banks *, no_cmci_banks);
DECLARE_PER_CPU(struct mca_banks *, mce_clear_banks);

extern bool cmci_support;
extern bool is_mc_panic;
extern bool mce_broadcast;
extern void mcheck_mca_clearbanks(struct mca_banks *bankmask);

extern mctelem_cookie_t mcheck_mca_logout(enum mca_source who,
                                          struct mca_banks *bankmask,
                                          struct mca_summary *sp,
                                          struct mca_banks *clear_bank);

void *x86_mcinfo_reserve(struct mc_info *mi,
                         unsigned int size, unsigned int type);
void x86_mcinfo_dump(struct mc_info *mi);

static inline int mce_vendor_bank_msr(const struct vcpu *v, uint32_t msr)
{
    switch (boot_cpu_data.x86_vendor) {
    case X86_VENDOR_INTEL:
        if (msr >= MSR_IA32_MC0_CTL2 &&
            msr < MSR_IA32_MCx_CTL2(v->arch.vmce.mcg_cap & MCG_CAP_COUNT) )
            return 1;
        fallthrough;

    case X86_VENDOR_CENTAUR:
    case X86_VENDOR_SHANGHAI:
        if (msr == MSR_P5_MC_ADDR || msr == MSR_P5_MC_TYPE)
            return 1;
        break;

    case X86_VENDOR_AMD:
        switch (msr) {
        case MSR_F10_MC4_MISC1:
        case MSR_F10_MC4_MISC2:
        case MSR_F10_MC4_MISC3:
            return 1;
        }
        break;
    }
    return 0;
}

static inline int mce_bank_msr(const struct vcpu *v, uint32_t msr)
{
    if ( (msr >= MSR_IA32_MC0_CTL &&
         msr < MSR_IA32_MCx_CTL(v->arch.vmce.mcg_cap & MCG_CAP_COUNT)) ||
         mce_vendor_bank_msr(v, msr) )
        return 1;
    return 0;
}

static inline bool vmce_has_lmce(const struct vcpu *v)
{
    return v->arch.vmce.mcg_cap & MCG_LMCE_P;
}

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;
extern unsigned int mce_dhandler_num;
extern unsigned int mce_uhandler_num;

/* Fields are zero when not available */
struct mce {
    uint64_t status;
    uint64_t misc;
    uint64_t addr;
    uint64_t mcgstatus;
    uint64_t ip;
    uint64_t tsc;      /* cpu time stamp counter */
    uint64_t time;     /* wall time_t when error was detected */
    uint8_t  cpuvendor;        /* cpu vendor as encoded in system.h */
    uint8_t  inject_flags;     /* software inject flags */
    uint16_t pad;
    uint32_t cpuid;    /* CPUID 1 EAX */
    uint8_t  cs;       /* code segment */
    uint8_t  bank;     /* machine check bank */
    uint8_t  cpu;      /* cpu number; obsolete; use extcpu now */
    uint8_t  finished; /* entry is valid */
    uint32_t extcpu;   /* linux cpu number that detected the error */
    uint32_t socketid; /* CPU socket ID */
    uint32_t apicid;   /* CPU initial apic ID */
    uint64_t mcgcap;   /* MCGCAP MSR: machine check capabilities of CPU */
};

extern int apei_write_mce(struct mce *m);

#endif /* _MCE_H */
