| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | 
|---|
| 2 | #ifndef __X86_MCE_INTERNAL_H__ | 
|---|
| 3 | #define __X86_MCE_INTERNAL_H__ | 
|---|
| 4 |  | 
|---|
| 5 | #undef pr_fmt | 
|---|
| 6 | #define pr_fmt(fmt) "mce: " fmt | 
|---|
| 7 |  | 
|---|
| 8 | #include <linux/device.h> | 
|---|
| 9 | #include <asm/mce.h> | 
|---|
| 10 |  | 
|---|
| 11 | enum severity_level { | 
|---|
| 12 | MCE_NO_SEVERITY, | 
|---|
| 13 | MCE_DEFERRED_SEVERITY, | 
|---|
| 14 | MCE_UCNA_SEVERITY = MCE_DEFERRED_SEVERITY, | 
|---|
| 15 | MCE_KEEP_SEVERITY, | 
|---|
| 16 | MCE_SOME_SEVERITY, | 
|---|
| 17 | MCE_AO_SEVERITY, | 
|---|
| 18 | MCE_UC_SEVERITY, | 
|---|
| 19 | MCE_AR_SEVERITY, | 
|---|
| 20 | MCE_PANIC_SEVERITY, | 
|---|
| 21 | }; | 
|---|
| 22 |  | 
|---|
| 23 | extern struct blocking_notifier_head x86_mce_decoder_chain; | 
|---|
| 24 |  | 
|---|
| 25 | #define INITIAL_CHECK_INTERVAL	5 * 60 /* 5 minutes */ | 
|---|
| 26 |  | 
|---|
| 27 | struct mce_evt_llist { | 
|---|
| 28 | struct llist_node llnode; | 
|---|
| 29 | struct mce_hw_err err; | 
|---|
| 30 | }; | 
|---|
| 31 |  | 
|---|
| 32 | void mce_gen_pool_process(struct work_struct *__unused); | 
|---|
| 33 | bool mce_gen_pool_empty(void); | 
|---|
| 34 | bool mce_gen_pool_add(struct mce_hw_err *err); | 
|---|
| 35 | bool mce_gen_pool_init(void); | 
|---|
| 36 | struct llist_node *mce_gen_pool_prepare_records(void); | 
|---|
| 37 |  | 
|---|
| 38 | int mce_severity(struct mce *a, struct pt_regs *regs, char **msg, bool is_excp); | 
|---|
| 39 | struct dentry *mce_get_debugfs_dir(void); | 
|---|
| 40 |  | 
|---|
| 41 | extern mce_banks_t mce_banks_ce_disabled; | 
|---|
| 42 |  | 
|---|
| 43 | #ifdef CONFIG_X86_MCE_INTEL | 
|---|
| 44 | void mce_intel_handle_storm(int bank, bool on); | 
|---|
| 45 | void cmci_disable_bank(int bank); | 
|---|
| 46 | void intel_init_cmci(void); | 
|---|
| 47 | void intel_init_lmce(void); | 
|---|
| 48 | void intel_clear_lmce(void); | 
|---|
| 49 | bool intel_filter_mce(struct mce *m); | 
|---|
| 50 | bool intel_mce_usable_address(struct mce *m); | 
|---|
| 51 | #else | 
|---|
| 52 | static inline void mce_intel_handle_storm(int bank, bool on) { } | 
|---|
| 53 | static inline void cmci_disable_bank(int bank) { } | 
|---|
| 54 | static inline void intel_init_cmci(void) { } | 
|---|
| 55 | static inline void intel_init_lmce(void) { } | 
|---|
| 56 | static inline void intel_clear_lmce(void) { } | 
|---|
| 57 | static inline bool intel_filter_mce(struct mce *m) { return false; } | 
|---|
| 58 | static inline bool intel_mce_usable_address(struct mce *m) { return false; } | 
|---|
| 59 | #endif | 
|---|
| 60 |  | 
|---|
| 61 | void mce_timer_kick(bool storm); | 
|---|
| 62 |  | 
|---|
| 63 | #ifdef CONFIG_X86_MCE_THRESHOLD | 
|---|
| 64 | void cmci_storm_begin(unsigned int bank); | 
|---|
| 65 | void cmci_storm_end(unsigned int bank); | 
|---|
| 66 | void mce_track_storm(struct mce *mce); | 
|---|
| 67 | void mce_inherit_storm(unsigned int bank); | 
|---|
| 68 | bool mce_get_storm_mode(void); | 
|---|
| 69 | void mce_set_storm_mode(bool storm); | 
|---|
| 70 | #else | 
|---|
| 71 | static inline void cmci_storm_begin(unsigned int bank) {} | 
|---|
| 72 | static inline void cmci_storm_end(unsigned int bank) {} | 
|---|
| 73 | static inline void mce_track_storm(struct mce *mce) {} | 
|---|
| 74 | static inline void mce_inherit_storm(unsigned int bank) {} | 
|---|
| 75 | static inline bool mce_get_storm_mode(void) { return false; } | 
|---|
| 76 | static inline void mce_set_storm_mode(bool storm) {} | 
|---|
| 77 | #endif | 
|---|
| 78 |  | 
|---|
| 79 | /* | 
|---|
| 80 | * history:		Bitmask tracking errors occurrence. Each set bit | 
|---|
| 81 | *			represents an error seen. | 
|---|
| 82 | * | 
|---|
| 83 | * timestamp:		Last time (in jiffies) that the bank was polled. | 
|---|
| 84 | * in_storm_mode:	Is this bank in storm mode? | 
|---|
| 85 | * poll_only:		Bank does not support CMCI, skip storm tracking. | 
|---|
| 86 | */ | 
|---|
| 87 | struct storm_bank { | 
|---|
| 88 | u64 history; | 
|---|
| 89 | u64 timestamp; | 
|---|
| 90 | bool in_storm_mode; | 
|---|
| 91 | bool poll_only; | 
|---|
| 92 | }; | 
|---|
| 93 |  | 
|---|
| 94 | #define NUM_HISTORY_BITS (sizeof(u64) * BITS_PER_BYTE) | 
|---|
| 95 |  | 
|---|
| 96 | /* How many errors within the history buffer mark the start of a storm. */ | 
|---|
| 97 | #define STORM_BEGIN_THRESHOLD	5 | 
|---|
| 98 |  | 
|---|
| 99 | /* | 
|---|
| 100 | * How many polls of machine check bank without an error before declaring | 
|---|
| 101 | * the storm is over. Since it is tracked by the bitmasks in the history | 
|---|
| 102 | * field of struct storm_bank the mask is 30 bits [0 ... 29]. | 
|---|
| 103 | */ | 
|---|
| 104 | #define STORM_END_POLL_THRESHOLD	29 | 
|---|
| 105 |  | 
|---|
| 106 | /* | 
|---|
| 107 | * banks:		per-cpu, per-bank details | 
|---|
| 108 | * stormy_bank_count:	count of MC banks in storm state | 
|---|
| 109 | * poll_mode:		CPU is in poll mode | 
|---|
| 110 | */ | 
|---|
| 111 | struct mca_storm_desc { | 
|---|
| 112 | struct storm_bank	banks[MAX_NR_BANKS]; | 
|---|
| 113 | u8			stormy_bank_count; | 
|---|
| 114 | bool			poll_mode; | 
|---|
| 115 | }; | 
|---|
| 116 |  | 
|---|
| 117 | DECLARE_PER_CPU(struct mca_storm_desc, storm_desc); | 
|---|
| 118 |  | 
|---|
| 119 | #ifdef CONFIG_ACPI_APEI | 
|---|
| 120 | int apei_write_mce(struct mce *m); | 
|---|
| 121 | ssize_t apei_read_mce(struct mce *m, u64 *record_id); | 
|---|
| 122 | int apei_check_mce(void); | 
|---|
| 123 | int apei_clear_mce(u64 record_id); | 
|---|
| 124 | #else | 
|---|
| 125 | static inline int apei_write_mce(struct mce *m) | 
|---|
| 126 | { | 
|---|
| 127 | return -EINVAL; | 
|---|
| 128 | } | 
|---|
| 129 | static inline ssize_t apei_read_mce(struct mce *m, u64 *record_id) | 
|---|
| 130 | { | 
|---|
| 131 | return 0; | 
|---|
| 132 | } | 
|---|
| 133 | static inline int apei_check_mce(void) | 
|---|
| 134 | { | 
|---|
| 135 | return 0; | 
|---|
| 136 | } | 
|---|
| 137 | static inline int apei_clear_mce(u64 record_id) | 
|---|
| 138 | { | 
|---|
| 139 | return -EINVAL; | 
|---|
| 140 | } | 
|---|
| 141 | #endif | 
|---|
| 142 |  | 
|---|
| 143 | /* | 
|---|
| 144 | * We consider records to be equivalent if bank+status+addr+misc all match. | 
|---|
| 145 | * This is only used when the system is going down because of a fatal error | 
|---|
| 146 | * to avoid cluttering the console log with essentially repeated information. | 
|---|
| 147 | * In normal processing all errors seen are logged. | 
|---|
| 148 | */ | 
|---|
| 149 | static inline bool mce_cmp(struct mce *m1, struct mce *m2) | 
|---|
| 150 | { | 
|---|
| 151 | return m1->bank != m2->bank || | 
|---|
| 152 | m1->status != m2->status || | 
|---|
| 153 | m1->addr != m2->addr || | 
|---|
| 154 | m1->misc != m2->misc; | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | extern struct device_attribute dev_attr_trigger; | 
|---|
| 158 |  | 
|---|
| 159 | #ifdef CONFIG_X86_MCELOG_LEGACY | 
|---|
| 160 | void mce_work_trigger(void); | 
|---|
| 161 | void mce_register_injector_chain(struct notifier_block *nb); | 
|---|
| 162 | void mce_unregister_injector_chain(struct notifier_block *nb); | 
|---|
| 163 | #else | 
|---|
| 164 | static inline void mce_work_trigger(void)	{ } | 
|---|
| 165 | static inline void mce_register_injector_chain(struct notifier_block *nb)	{ } | 
|---|
| 166 | static inline void mce_unregister_injector_chain(struct notifier_block *nb)	{ } | 
|---|
| 167 | #endif | 
|---|
| 168 |  | 
|---|
| 169 | struct mca_config { | 
|---|
| 170 | __u64 lmce_disabled		: 1, | 
|---|
| 171 | disabled			: 1, | 
|---|
| 172 | ser			: 1, | 
|---|
| 173 | recovery			: 1, | 
|---|
| 174 | bios_cmci_threshold	: 1, | 
|---|
| 175 | /* Proper #MC exception handler is set */ | 
|---|
| 176 | initialized		: 1, | 
|---|
| 177 | __reserved		: 58; | 
|---|
| 178 |  | 
|---|
| 179 | bool dont_log_ce; | 
|---|
| 180 | bool cmci_disabled; | 
|---|
| 181 | bool ignore_ce; | 
|---|
| 182 | bool print_all; | 
|---|
| 183 |  | 
|---|
| 184 | int monarch_timeout; | 
|---|
| 185 | int panic_timeout; | 
|---|
| 186 | u32 rip_msr; | 
|---|
| 187 | s8 bootlog; | 
|---|
| 188 | }; | 
|---|
| 189 |  | 
|---|
| 190 | extern struct mca_config mca_cfg; | 
|---|
| 191 | DECLARE_PER_CPU_READ_MOSTLY(unsigned int, mce_num_banks); | 
|---|
| 192 |  | 
|---|
| 193 | struct mce_vendor_flags { | 
|---|
| 194 | /* | 
|---|
| 195 | * Indicates that overflow conditions are not fatal, when set. | 
|---|
| 196 | */ | 
|---|
| 197 | __u64 overflow_recov	: 1, | 
|---|
| 198 |  | 
|---|
| 199 | /* | 
|---|
| 200 | * (AMD) SUCCOR stands for S/W UnCorrectable error COntainment and | 
|---|
| 201 | * Recovery. It indicates support for data poisoning in HW and deferred | 
|---|
| 202 | * error interrupts. | 
|---|
| 203 | */ | 
|---|
| 204 | succor			: 1, | 
|---|
| 205 |  | 
|---|
| 206 | /* | 
|---|
| 207 | * (AMD) SMCA: This bit indicates support for Scalable MCA which expands | 
|---|
| 208 | * the register space for each MCA bank and also increases number of | 
|---|
| 209 | * banks. Also, to accommodate the new banks and registers, the MCA | 
|---|
| 210 | * register space is moved to a new MSR range. | 
|---|
| 211 | */ | 
|---|
| 212 | smca			: 1, | 
|---|
| 213 |  | 
|---|
| 214 | /* Zen IFU quirk */ | 
|---|
| 215 | zen_ifu_quirk		: 1, | 
|---|
| 216 |  | 
|---|
| 217 | /* AMD-style error thresholding banks present. */ | 
|---|
| 218 | amd_threshold		: 1, | 
|---|
| 219 |  | 
|---|
| 220 | /* Pentium, family 5-style MCA */ | 
|---|
| 221 | p5			: 1, | 
|---|
| 222 |  | 
|---|
| 223 | /* Centaur Winchip C6-style MCA */ | 
|---|
| 224 | winchip			: 1, | 
|---|
| 225 |  | 
|---|
| 226 | /* SandyBridge IFU quirk */ | 
|---|
| 227 | snb_ifu_quirk		: 1, | 
|---|
| 228 |  | 
|---|
| 229 | /* Skylake, Cascade Lake, Cooper Lake REP;MOVS* quirk */ | 
|---|
| 230 | skx_repmov_quirk	: 1, | 
|---|
| 231 |  | 
|---|
| 232 | __reserved_0		: 55; | 
|---|
| 233 | }; | 
|---|
| 234 |  | 
|---|
| 235 | extern struct mce_vendor_flags mce_flags; | 
|---|
| 236 |  | 
|---|
| 237 | struct mce_bank { | 
|---|
| 238 | /* subevents to enable */ | 
|---|
| 239 | u64			ctl; | 
|---|
| 240 |  | 
|---|
| 241 | /* initialise bank? */ | 
|---|
| 242 | __u64 init		: 1, | 
|---|
| 243 |  | 
|---|
| 244 | /* | 
|---|
| 245 | * (AMD) MCA_CONFIG[McaLsbInStatusSupported]: When set, this bit indicates | 
|---|
| 246 | * the LSB field is found in MCA_STATUS and not in MCA_ADDR. | 
|---|
| 247 | */ | 
|---|
| 248 | lsb_in_status		: 1, | 
|---|
| 249 |  | 
|---|
| 250 | __reserved_1		: 62; | 
|---|
| 251 | }; | 
|---|
| 252 |  | 
|---|
| 253 | DECLARE_PER_CPU_READ_MOSTLY(struct mce_bank[MAX_NR_BANKS], mce_banks_array); | 
|---|
| 254 |  | 
|---|
| 255 | enum mca_msr { | 
|---|
| 256 | MCA_CTL, | 
|---|
| 257 | MCA_STATUS, | 
|---|
| 258 | MCA_ADDR, | 
|---|
| 259 | MCA_MISC, | 
|---|
| 260 | }; | 
|---|
| 261 |  | 
|---|
| 262 | /* Decide whether to add MCE record to MCE event pool or filter it out. */ | 
|---|
| 263 | extern bool filter_mce(struct mce *m); | 
|---|
| 264 | void mce_prep_record_common(struct mce *m); | 
|---|
| 265 | void mce_prep_record_per_cpu(unsigned int cpu, struct mce *m); | 
|---|
| 266 |  | 
|---|
| 267 | #ifdef CONFIG_X86_MCE_AMD | 
|---|
| 268 | void mce_threshold_create_device(unsigned int cpu); | 
|---|
| 269 | void mce_threshold_remove_device(unsigned int cpu); | 
|---|
| 270 | extern bool amd_filter_mce(struct mce *m); | 
|---|
| 271 | bool amd_mce_usable_address(struct mce *m); | 
|---|
| 272 | void amd_clear_bank(struct mce *m); | 
|---|
| 273 |  | 
|---|
| 274 | /* | 
|---|
| 275 | * If MCA_CONFIG[McaLsbInStatusSupported] is set, extract ErrAddr in bits | 
|---|
| 276 | * [56:0] of MCA_STATUS, else in bits [55:0] of MCA_ADDR. | 
|---|
| 277 | */ | 
|---|
| 278 | static __always_inline void (struct mce *m) | 
|---|
| 279 | { | 
|---|
| 280 | u8 lsb; | 
|---|
| 281 |  | 
|---|
| 282 | if (!mce_flags.smca) | 
|---|
| 283 | return; | 
|---|
| 284 |  | 
|---|
| 285 | if (this_cpu_ptr(mce_banks_array)[m->bank].lsb_in_status) { | 
|---|
| 286 | lsb = (m->status >> 24) & 0x3f; | 
|---|
| 287 |  | 
|---|
| 288 | m->addr &= GENMASK_ULL(56, lsb); | 
|---|
| 289 |  | 
|---|
| 290 | return; | 
|---|
| 291 | } | 
|---|
| 292 |  | 
|---|
| 293 | lsb = (m->addr >> 56) & 0x3f; | 
|---|
| 294 |  | 
|---|
| 295 | m->addr &= GENMASK_ULL(55, lsb); | 
|---|
| 296 | } | 
|---|
| 297 |  | 
|---|
| 298 | void smca_bsp_init(void); | 
|---|
| 299 | #else | 
|---|
| 300 | static inline void mce_threshold_create_device(unsigned int cpu)	{ } | 
|---|
| 301 | static inline void mce_threshold_remove_device(unsigned int cpu)	{ } | 
|---|
| 302 | static inline bool amd_filter_mce(struct mce *m) { return false; } | 
|---|
| 303 | static inline bool amd_mce_usable_address(struct mce *m) { return false; } | 
|---|
| 304 | static inline void amd_clear_bank(struct mce *m) { } | 
|---|
| 305 | static inline void smca_extract_err_addr(struct mce *m) { } | 
|---|
| 306 | static inline void smca_bsp_init(void) { } | 
|---|
| 307 | #endif | 
|---|
| 308 |  | 
|---|
| 309 | #ifdef CONFIG_X86_ANCIENT_MCE | 
|---|
| 310 | void intel_p5_mcheck_init(struct cpuinfo_x86 *c); | 
|---|
| 311 | void winchip_mcheck_init(struct cpuinfo_x86 *c); | 
|---|
| 312 | noinstr void pentium_machine_check(struct pt_regs *regs); | 
|---|
| 313 | noinstr void winchip_machine_check(struct pt_regs *regs); | 
|---|
| 314 | static inline void enable_p5_mce(void) { mce_p5_enabled = 1; } | 
|---|
| 315 | #else | 
|---|
| 316 | static __always_inline void intel_p5_mcheck_init(struct cpuinfo_x86 *c) {} | 
|---|
| 317 | static __always_inline void winchip_mcheck_init(struct cpuinfo_x86 *c) {} | 
|---|
| 318 | static __always_inline void enable_p5_mce(void) {} | 
|---|
| 319 | static __always_inline void pentium_machine_check(struct pt_regs *regs) {} | 
|---|
| 320 | static __always_inline void winchip_machine_check(struct pt_regs *regs) {} | 
|---|
| 321 | #endif | 
|---|
| 322 |  | 
|---|
| 323 | noinstr u64 mce_rdmsrq(u32 msr); | 
|---|
| 324 | noinstr void mce_wrmsrq(u32 msr, u64 v); | 
|---|
| 325 |  | 
|---|
| 326 | static __always_inline u32 mca_msr_reg(int bank, enum mca_msr reg) | 
|---|
| 327 | { | 
|---|
| 328 | if (cpu_feature_enabled(X86_FEATURE_SMCA)) { | 
|---|
| 329 | switch (reg) { | 
|---|
| 330 | case MCA_CTL:	 return MSR_AMD64_SMCA_MCx_CTL(bank); | 
|---|
| 331 | case MCA_ADDR:	 return MSR_AMD64_SMCA_MCx_ADDR(bank); | 
|---|
| 332 | case MCA_MISC:	 return MSR_AMD64_SMCA_MCx_MISC(bank); | 
|---|
| 333 | case MCA_STATUS: return MSR_AMD64_SMCA_MCx_STATUS(bank); | 
|---|
| 334 | } | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | switch (reg) { | 
|---|
| 338 | case MCA_CTL:	 return MSR_IA32_MCx_CTL(bank); | 
|---|
| 339 | case MCA_ADDR:	 return MSR_IA32_MCx_ADDR(bank); | 
|---|
| 340 | case MCA_MISC:	 return MSR_IA32_MCx_MISC(bank); | 
|---|
| 341 | case MCA_STATUS: return MSR_IA32_MCx_STATUS(bank); | 
|---|
| 342 | } | 
|---|
| 343 |  | 
|---|
| 344 | return 0; | 
|---|
| 345 | } | 
|---|
| 346 |  | 
|---|
| 347 | extern void (*mc_poll_banks)(void); | 
|---|
| 348 | #endif /* __X86_MCE_INTERNAL_H__ */ | 
|---|
| 349 |  | 
|---|