xref: /linux/drivers/edac/intel-bff.c (revision 4835abed9f652de1bea8957b867b8479505caa74)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2026 Intel Corporation. */
3 
4 /*
5  * Intel driver to reset bitfix filters when they overflow.
6  *
7  * Each bitfix filter has limited slots to track corrected errors.
8  * These slots can be filled by transient corrected errors (e.g.,
9  * bit flips from particle strikes) that don't represent permanent
10  * hardware defects.
11  *
12  * When the filter overflows (yellow status), reset it to reclaim
13  * slots occupied by transient corrected errors. If the overflow
14  * repeats frequently after reset, it indicates persistent hardware
15  * defects that need attention: the system should be scheduled for
16  * servicing.
17  */
18 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 
20 #include <linux/bitfield.h>
21 #include <linux/bits.h>
22 #include <linux/cacheinfo.h>
23 #include <linux/cleanup.h>
24 #include <linux/cpufeature.h>
25 #include <linux/cpuhplock.h>
26 #include <linux/device-id/x86_cpu.h>
27 #include <linux/errno.h>
28 #include <linux/init.h>
29 #include <linux/limits.h>
30 #include <linux/module.h>
31 #include <linux/notifier.h>
32 #include <linux/printk.h>
33 #include <linux/topology.h>
34 #include <linux/types.h>
35 
36 #include <asm/cpu_device_id.h>
37 #include <asm/cpufeatures.h>
38 #include <asm/intel-family.h>
39 #include <asm/mce.h>
40 #include <asm/msr.h>
41 #include <asm/msr-index.h>
42 
43 /* Intel bitfix filter control register defines */
44 #define MSR_MC0_BFF_CTL		0x000006c0
45 #define MSR_MCx_BFF_CTL(x)	(MSR_MC0_BFF_CTL + (x))
46 #define  MCI_BFF_RESET		BIT_ULL(0)
47 
48 /* Machine check bank hardware unit types */
49 enum bff_bank_type {
50 	BFF_BANK_NONE = 0,
51 	BFF_BANK_DCU,
52 	BFF_BANK_DTLB,
53 	BFF_BANK_MLC,
54 	BFF_BANK_CCF,
55 	BFF_BANK_HSF,
56 	BFF_BANK_IOCACHE,
57 };
58 
59 /*
60  * Mapping from machine check bank numbers on Diamond Rapids CPU, that are
61  * supported by a bitfix filter, to hardware unit type.
62  */
63 static const enum bff_bank_type dmr_mcbanks[MAX_NR_BANKS] = {
64 	[1]	= BFF_BANK_DCU,
65 	[2]	= BFF_BANK_DTLB,
66 	[3]	= BFF_BANK_MLC,
67 	[6]	= BFF_BANK_CCF,
68 	[13]	= BFF_BANK_HSF,
69 	[17]	= BFF_BANK_IOCACHE
70 };
71 
72 static const struct x86_cpu_id bff_cpu_ids[] __initconst = {
73 	X86_MATCH_VFM(INTEL_DIAMONDRAPIDS_X, dmr_mcbanks),
74 	{}
75 };
76 MODULE_DEVICE_TABLE(x86cpu, bff_cpu_ids);
77 
78 static const enum bff_bank_type *bff_bank_types;
79 
80 /* Diamond Rapids maps APICID[2] to the IMH instance within a socket. */
81 #define APICID_IMH_NUM		GENMASK(2, 2)
82 #define IMH_NUM(apicid)		FIELD_GET(APICID_IMH_NUM, apicid)
83 #define NUM_IMH_PER_SOCKET	2
84 
85 static void bff_set_imh_id(struct mce *mce, unsigned long *id)
86 {
87 	int imh_num;
88 
89 	imh_num = NUM_IMH_PER_SOCKET * topology_physical_package_id(mce->extcpu) +
90 			IMH_NUM(mce->apicid);
91 
92 	*id |= imh_num;
93 }
94 
95 static bool bff_set_cache_id(int cpu, int level, unsigned long *id)
96 {
97 	int cacheid;
98 
99 	guard(cpus_read_lock)();
100 
101 	cacheid = get_cpu_cacheinfo_id(cpu, level);
102 	if (cacheid == -1) {
103 		pr_warn("Could not get L%d cache id for CPU %d\n", level, cpu);
104 		return false;
105 	}
106 
107 	*id |= cacheid;
108 
109 	return true;
110 }
111 
112 /*
113  * Cache IDs are only unique within a cache level.
114  * Include the MCA bank number so each BFF-capable hardware
115  * resource has a unique tracking ID.
116  */
117 #define BFF_ID_BANK_FIELD	GENMASK(63, 32)
118 
119 static unsigned long bff_get_id(struct mce *mce)
120 {
121 	unsigned long id = FIELD_PREP(BFF_ID_BANK_FIELD, mce->bank);
122 
123 	switch (bff_bank_types[mce->bank]) {
124 	case BFF_BANK_DCU:
125 	case BFF_BANK_DTLB:
126 		if (!bff_set_cache_id(mce->extcpu, 1, &id))
127 			return ULONG_MAX;
128 		break;
129 
130 	case BFF_BANK_MLC:
131 		if (!bff_set_cache_id(mce->extcpu, 2, &id))
132 			return ULONG_MAX;
133 		break;
134 
135 	case BFF_BANK_CCF:
136 		if (!bff_set_cache_id(mce->extcpu, 3, &id))
137 			return ULONG_MAX;
138 		break;
139 
140 	case BFF_BANK_HSF:
141 	case BFF_BANK_IOCACHE:
142 		bff_set_imh_id(mce, &id);
143 		break;
144 
145 	default:
146 		return ULONG_MAX;
147 	}
148 
149 	return id;
150 }
151 
152 static void bff_reset_and_report(struct mce *mce)
153 {
154 	/* Reset bitfix filter using the CPU that logged the yellow status */
155 	if (wrmsrq_on_cpu(mce->extcpu, MSR_MCx_BFF_CTL(mce->bank), MCI_BFF_RESET))
156 		pr_warn("Failed to reset bitfix filter for CPU %d Bank %d\n",
157 			mce->extcpu, mce->bank);
158 
159 	/* Placeholder use of bff_get_id() */
160 	pr_debug("unique_id = 0x%lx\n", bff_get_id(mce));
161 }
162 
163 static int bff_mce_notify(struct notifier_block *nb, unsigned long val, void *data)
164 {
165 	struct mce *mce = data;
166 
167 	/* TES is undefined for uncorrected errors. */
168 	if (mce->status & MCI_STATUS_UC)
169 		return NOTIFY_DONE;
170 
171 	if (MCI_STATUS_TES(mce->status) != MCI_STATUS_TES_YELLOW)
172 		return NOTIFY_DONE;
173 
174 	if (mce->bank >= MAX_NR_BANKS || bff_bank_types[mce->bank] == BFF_BANK_NONE)
175 		return NOTIFY_DONE;
176 
177 	bff_reset_and_report(mce);
178 
179 	return NOTIFY_DONE;
180 }
181 
182 static struct notifier_block bff_notifier = {
183 	.notifier_call	= bff_mce_notify,
184 	.priority	= MCE_PRIO_EDAC,
185 };
186 
187 static int __init bff_init(void)
188 {
189 	const struct x86_cpu_id *m;
190 	u64 core_caps, mcg_cap;
191 
192 	if (!cpu_feature_enabled(X86_FEATURE_MCA))
193 		return -ENODEV;
194 	rdmsrq(MSR_IA32_MCG_CAP, mcg_cap);
195 	if (!(mcg_cap & MCG_TES_P))
196 		return -ENODEV;
197 
198 	if (!cpu_feature_enabled(X86_FEATURE_CORE_CAPABILITIES))
199 		return -ENODEV;
200 	rdmsrq(MSR_IA32_CORE_CAPS, core_caps);
201 	if (!(core_caps & MSR_IA32_CORE_CAPS_BFF_RESET))
202 		return -ENODEV;
203 
204 	m = x86_match_cpu(bff_cpu_ids);
205 	if (!m) {
206 		pr_info("CPU model not supported by the bitfix filter reset driver\n");
207 		return -ENODEV;
208 	}
209 
210 	bff_bank_types = (const enum bff_bank_type *)m->driver_data;
211 
212 	mce_register_decode_chain(&bff_notifier);
213 
214 	return 0;
215 }
216 
217 static void __exit bff_exit(void)
218 {
219 	mce_unregister_decode_chain(&bff_notifier);
220 }
221 
222 module_init(bff_init);
223 module_exit(bff_exit);
224 
225 MODULE_LICENSE("GPL");
226 MODULE_AUTHOR("Tony Luck");
227 MODULE_DESCRIPTION("Intel bitfix filter reset driver");
228