xref: /openbmc/linux/drivers/edac/ghes_edac.c (revision 802e7f1d)
112237550SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
277c5f5d2SMauro Carvalho Chehab /*
377c5f5d2SMauro Carvalho Chehab  * GHES/EDAC Linux driver
477c5f5d2SMauro Carvalho Chehab  *
537e59f87SMauro Carvalho Chehab  * Copyright (c) 2013 by Mauro Carvalho Chehab
677c5f5d2SMauro Carvalho Chehab  *
77d4c1ea2SAlexander A. Klimov  * Red Hat Inc. https://www.redhat.com
877c5f5d2SMauro Carvalho Chehab  */
977c5f5d2SMauro Carvalho Chehab 
10d2a68566SMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11d2a68566SMauro Carvalho Chehab 
1277c5f5d2SMauro Carvalho Chehab #include <acpi/ghes.h>
1377c5f5d2SMauro Carvalho Chehab #include <linux/edac.h>
1432fa1f53SMauro Carvalho Chehab #include <linux/dmi.h>
1578d88e8aSMauro Carvalho Chehab #include "edac_module.h"
168ae8f50aSMauro Carvalho Chehab #include <ras/ras_event.h>
178e40612fSJia He #include <linux/notifier.h>
1877c5f5d2SMauro Carvalho Chehab 
19ed27b5dfSShuai Xue #define OTHER_DETAIL_LEN	400
20ed27b5dfSShuai Xue 
21b001694dSRobert Richter struct ghes_pvt {
2277c5f5d2SMauro Carvalho Chehab 	struct mem_ctl_info *mci;
23689c9cd8SMauro Carvalho Chehab 
24689c9cd8SMauro Carvalho Chehab 	/* Buffers for the error handling routine */
25ed27b5dfSShuai Xue 	char other_detail[OTHER_DETAIL_LEN];
26689c9cd8SMauro Carvalho Chehab 	char msg[80];
2777c5f5d2SMauro Carvalho Chehab };
2877c5f5d2SMauro Carvalho Chehab 
2923f61b9fSRobert Richter static refcount_t ghes_refcount = REFCOUNT_INIT(0);
3023f61b9fSRobert Richter 
3123f61b9fSRobert Richter /*
3223f61b9fSRobert Richter  * Access to ghes_pvt must be protected by ghes_lock. The spinlock
3323f61b9fSRobert Richter  * also provides the necessary (implicit) memory barrier for the SMP
3423f61b9fSRobert Richter  * case to make the pointer visible on another CPU.
3523f61b9fSRobert Richter  */
36b001694dSRobert Richter static struct ghes_pvt *ghes_pvt;
3777c5f5d2SMauro Carvalho Chehab 
38b9cae277SBorislav Petkov /*
39b9cae277SBorislav Petkov  * This driver's representation of the system hardware, as collected
40b9cae277SBorislav Petkov  * from DMI.
41b9cae277SBorislav Petkov  */
42815fad6eSTom Rix static struct ghes_hw_desc {
43b9cae277SBorislav Petkov 	int num_dimms;
44b9cae277SBorislav Petkov 	struct dimm_info *dimms;
45b9cae277SBorislav Petkov } ghes_hw;
46b9cae277SBorislav Petkov 
4723f61b9fSRobert Richter /* GHES registration mutex */
4823f61b9fSRobert Richter static DEFINE_MUTEX(ghes_reg_mutex);
4923f61b9fSRobert Richter 
500fe5f281SBorislav Petkov /*
510fe5f281SBorislav Petkov  * Sync with other, potentially concurrent callers of
520fe5f281SBorislav Petkov  * ghes_edac_report_mem_error(). We don't know what the
530fe5f281SBorislav Petkov  * "inventive" firmware would do.
540fe5f281SBorislav Petkov  */
550fe5f281SBorislav Petkov static DEFINE_SPINLOCK(ghes_lock);
56d2a68566SMauro Carvalho Chehab 
57b972fdbaSShiju Jose static bool system_scanned;
58b972fdbaSShiju Jose 
59*802e7f1dSJia He static struct list_head *ghes_devs;
60*802e7f1dSJia He 
6132fa1f53SMauro Carvalho Chehab /* Memory Device - Type 17 of SMBIOS spec */
6232fa1f53SMauro Carvalho Chehab struct memdev_dmi_entry {
6332fa1f53SMauro Carvalho Chehab 	u8 type;
6432fa1f53SMauro Carvalho Chehab 	u8 length;
6532fa1f53SMauro Carvalho Chehab 	u16 handle;
6632fa1f53SMauro Carvalho Chehab 	u16 phys_mem_array_handle;
6732fa1f53SMauro Carvalho Chehab 	u16 mem_err_info_handle;
6832fa1f53SMauro Carvalho Chehab 	u16 total_width;
6932fa1f53SMauro Carvalho Chehab 	u16 data_width;
7032fa1f53SMauro Carvalho Chehab 	u16 size;
7132fa1f53SMauro Carvalho Chehab 	u8 form_factor;
7232fa1f53SMauro Carvalho Chehab 	u8 device_set;
7332fa1f53SMauro Carvalho Chehab 	u8 device_locator;
7432fa1f53SMauro Carvalho Chehab 	u8 bank_locator;
7532fa1f53SMauro Carvalho Chehab 	u8 memory_type;
7632fa1f53SMauro Carvalho Chehab 	u16 type_detail;
7732fa1f53SMauro Carvalho Chehab 	u16 speed;
7832fa1f53SMauro Carvalho Chehab 	u8 manufacturer;
7932fa1f53SMauro Carvalho Chehab 	u8 serial_number;
8032fa1f53SMauro Carvalho Chehab 	u8 asset_tag;
8132fa1f53SMauro Carvalho Chehab 	u8 part_number;
8232fa1f53SMauro Carvalho Chehab 	u8 attributes;
8332fa1f53SMauro Carvalho Chehab 	u32 extended_size;
8432fa1f53SMauro Carvalho Chehab 	u16 conf_mem_clk_speed;
8532fa1f53SMauro Carvalho Chehab } __attribute__((__packed__));
8632fa1f53SMauro Carvalho Chehab 
find_dimm_by_handle(struct mem_ctl_info * mci,u16 handle)87cb51a371SRobert Richter static struct dimm_info *find_dimm_by_handle(struct mem_ctl_info *mci, u16 handle)
88c798c88fSFan Wu {
89c498afafSRobert Richter 	struct dimm_info *dimm;
90c798c88fSFan Wu 
91c498afafSRobert Richter 	mci_for_each_dimm(mci, dimm) {
92c498afafSRobert Richter 		if (dimm->smbios_handle == handle)
93cb51a371SRobert Richter 			return dimm;
94c798c88fSFan Wu 	}
95c498afafSRobert Richter 
96cb51a371SRobert Richter 	return NULL;
97cb51a371SRobert Richter }
98cb51a371SRobert Richter 
dimm_setup_label(struct dimm_info * dimm,u16 handle)99cb51a371SRobert Richter static void dimm_setup_label(struct dimm_info *dimm, u16 handle)
100cb51a371SRobert Richter {
101cb51a371SRobert Richter 	const char *bank = NULL, *device = NULL;
102cb51a371SRobert Richter 
103cb51a371SRobert Richter 	dmi_memdev_name(handle, &bank, &device);
104cb51a371SRobert Richter 
1055e2805d5SToshi Kani 	/*
1065e2805d5SToshi Kani 	 * Set to a NULL string when both bank and device are zero. In this case,
1075e2805d5SToshi Kani 	 * the label assigned by default will be preserved.
1085e2805d5SToshi Kani 	 */
1095e2805d5SToshi Kani 	snprintf(dimm->label, sizeof(dimm->label), "%s%s%s",
1105e2805d5SToshi Kani 		 (bank && *bank) ? bank : "",
1115e2805d5SToshi Kani 		 (bank && *bank && device && *device) ? " " : "",
1125e2805d5SToshi Kani 		 (device && *device) ? device : "");
113c798c88fSFan Wu }
114c798c88fSFan Wu 
assign_dmi_dimm_info(struct dimm_info * dimm,struct memdev_dmi_entry * entry)115b9cae277SBorislav Petkov static void assign_dmi_dimm_info(struct dimm_info *dimm, struct memdev_dmi_entry *entry)
11632fa1f53SMauro Carvalho Chehab {
117a0671c39SBorislav Petkov 	u16 rdr_mask = BIT(7) | BIT(13);
11832fa1f53SMauro Carvalho Chehab 
11932fa1f53SMauro Carvalho Chehab 	if (entry->size == 0xffff) {
120b9cae277SBorislav Petkov 		pr_info("Can't get DIMM%i size\n", dimm->idx);
12132fa1f53SMauro Carvalho Chehab 		dimm->nr_pages = MiB_TO_PAGES(32);/* Unknown */
12232fa1f53SMauro Carvalho Chehab 	} else if (entry->size == 0x7fff) {
12332fa1f53SMauro Carvalho Chehab 		dimm->nr_pages = MiB_TO_PAGES(entry->extended_size);
12432fa1f53SMauro Carvalho Chehab 	} else {
125a0671c39SBorislav Petkov 		if (entry->size & BIT(15))
126a0671c39SBorislav Petkov 			dimm->nr_pages = MiB_TO_PAGES((entry->size & 0x7fff) << 10);
12732fa1f53SMauro Carvalho Chehab 		else
12832fa1f53SMauro Carvalho Chehab 			dimm->nr_pages = MiB_TO_PAGES(entry->size);
12932fa1f53SMauro Carvalho Chehab 	}
13032fa1f53SMauro Carvalho Chehab 
13132fa1f53SMauro Carvalho Chehab 	switch (entry->memory_type) {
13232fa1f53SMauro Carvalho Chehab 	case 0x12:
133a0671c39SBorislav Petkov 		if (entry->type_detail & BIT(13))
13432fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_RDDR;
13532fa1f53SMauro Carvalho Chehab 		else
13632fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_DDR;
13732fa1f53SMauro Carvalho Chehab 		break;
13832fa1f53SMauro Carvalho Chehab 	case 0x13:
139a0671c39SBorislav Petkov 		if (entry->type_detail & BIT(13))
14032fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_RDDR2;
14132fa1f53SMauro Carvalho Chehab 		else
14232fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_DDR2;
14332fa1f53SMauro Carvalho Chehab 		break;
14432fa1f53SMauro Carvalho Chehab 	case 0x14:
14532fa1f53SMauro Carvalho Chehab 		dimm->mtype = MEM_FB_DDR2;
14632fa1f53SMauro Carvalho Chehab 		break;
14732fa1f53SMauro Carvalho Chehab 	case 0x18:
148a0671c39SBorislav Petkov 		if (entry->type_detail & BIT(12))
149ad0d73b3SToshi Kani 			dimm->mtype = MEM_NVDIMM;
150a0671c39SBorislav Petkov 		else if (entry->type_detail & BIT(13))
15132fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_RDDR3;
15232fa1f53SMauro Carvalho Chehab 		else
15332fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_DDR3;
15432fa1f53SMauro Carvalho Chehab 		break;
155ad0d73b3SToshi Kani 	case 0x1a:
156a0671c39SBorislav Petkov 		if (entry->type_detail & BIT(12))
157ad0d73b3SToshi Kani 			dimm->mtype = MEM_NVDIMM;
158a0671c39SBorislav Petkov 		else if (entry->type_detail & BIT(13))
159ad0d73b3SToshi Kani 			dimm->mtype = MEM_RDDR4;
160ad0d73b3SToshi Kani 		else
161ad0d73b3SToshi Kani 			dimm->mtype = MEM_DDR4;
162ad0d73b3SToshi Kani 		break;
16332fa1f53SMauro Carvalho Chehab 	default:
164a0671c39SBorislav Petkov 		if (entry->type_detail & BIT(6))
16532fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_RMBS;
166a0671c39SBorislav Petkov 		else if ((entry->type_detail & rdr_mask) == rdr_mask)
16732fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_RDR;
168a0671c39SBorislav Petkov 		else if (entry->type_detail & BIT(7))
16932fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_SDR;
170a0671c39SBorislav Petkov 		else if (entry->type_detail & BIT(9))
17132fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_EDO;
17232fa1f53SMauro Carvalho Chehab 		else
17332fa1f53SMauro Carvalho Chehab 			dimm->mtype = MEM_UNKNOWN;
17432fa1f53SMauro Carvalho Chehab 	}
17532fa1f53SMauro Carvalho Chehab 
17632fa1f53SMauro Carvalho Chehab 	/*
17732fa1f53SMauro Carvalho Chehab 	 * Actually, we can only detect if the memory has bits for
17832fa1f53SMauro Carvalho Chehab 	 * checksum or not
17932fa1f53SMauro Carvalho Chehab 	 */
18032fa1f53SMauro Carvalho Chehab 	if (entry->total_width == entry->data_width)
18132fa1f53SMauro Carvalho Chehab 		dimm->edac_mode = EDAC_NONE;
18232fa1f53SMauro Carvalho Chehab 	else
18332fa1f53SMauro Carvalho Chehab 		dimm->edac_mode = EDAC_SECDED;
18432fa1f53SMauro Carvalho Chehab 
18532fa1f53SMauro Carvalho Chehab 	dimm->dtype = DEV_UNKNOWN;
18632fa1f53SMauro Carvalho Chehab 	dimm->grain = 128;		/* Likely, worse case */
18732fa1f53SMauro Carvalho Chehab 
188cb51a371SRobert Richter 	dimm_setup_label(dimm, entry->handle);
18932fa1f53SMauro Carvalho Chehab 
19032fa1f53SMauro Carvalho Chehab 	if (dimm->nr_pages) {
191d2a68566SMauro Carvalho Chehab 		edac_dbg(1, "DIMM%i: %s size = %d MB%s\n",
192b9cae277SBorislav Petkov 			dimm->idx, edac_mem_types[dimm->mtype],
19332fa1f53SMauro Carvalho Chehab 			PAGES_TO_MiB(dimm->nr_pages),
19432fa1f53SMauro Carvalho Chehab 			(dimm->edac_mode != EDAC_NONE) ? "(ECC)" : "");
195d2a68566SMauro Carvalho Chehab 		edac_dbg(2, "\ttype %d, detail 0x%02x, width %d(total %d)\n",
19632fa1f53SMauro Carvalho Chehab 			entry->memory_type, entry->type_detail,
19732fa1f53SMauro Carvalho Chehab 			entry->total_width, entry->data_width);
19832fa1f53SMauro Carvalho Chehab 	}
19932fa1f53SMauro Carvalho Chehab 
200c798c88fSFan Wu 	dimm->smbios_handle = entry->handle;
20132fa1f53SMauro Carvalho Chehab }
202b9cae277SBorislav Petkov 
enumerate_dimms(const struct dmi_header * dh,void * arg)203b9cae277SBorislav Petkov static void enumerate_dimms(const struct dmi_header *dh, void *arg)
204b9cae277SBorislav Petkov {
205b9cae277SBorislav Petkov 	struct memdev_dmi_entry *entry = (struct memdev_dmi_entry *)dh;
206b9cae277SBorislav Petkov 	struct ghes_hw_desc *hw = (struct ghes_hw_desc *)arg;
207b9cae277SBorislav Petkov 	struct dimm_info *d;
208b9cae277SBorislav Petkov 
209b9cae277SBorislav Petkov 	if (dh->type != DMI_ENTRY_MEM_DEVICE)
210b9cae277SBorislav Petkov 		return;
211b9cae277SBorislav Petkov 
212b9cae277SBorislav Petkov 	/* Enlarge the array with additional 16 */
213b9cae277SBorislav Petkov 	if (!hw->num_dimms || !(hw->num_dimms % 16)) {
214b9cae277SBorislav Petkov 		struct dimm_info *new;
215b9cae277SBorislav Petkov 
216af11be05SBartosz Golaszewski 		new = krealloc_array(hw->dimms, hw->num_dimms + 16,
217af11be05SBartosz Golaszewski 				     sizeof(struct dimm_info), GFP_KERNEL);
218b9cae277SBorislav Petkov 		if (!new) {
219b9cae277SBorislav Petkov 			WARN_ON_ONCE(1);
220b9cae277SBorislav Petkov 			return;
221b9cae277SBorislav Petkov 		}
222b9cae277SBorislav Petkov 
223b9cae277SBorislav Petkov 		hw->dimms = new;
224b9cae277SBorislav Petkov 	}
225b9cae277SBorislav Petkov 
226b9cae277SBorislav Petkov 	d = &hw->dimms[hw->num_dimms];
227b9cae277SBorislav Petkov 	d->idx = hw->num_dimms;
228b9cae277SBorislav Petkov 
229b9cae277SBorislav Petkov 	assign_dmi_dimm_info(d, entry);
230b9cae277SBorislav Petkov 
231b9cae277SBorislav Petkov 	hw->num_dimms++;
232b9cae277SBorislav Petkov }
233b9cae277SBorislav Petkov 
ghes_scan_system(void)234b9cae277SBorislav Petkov static void ghes_scan_system(void)
235b9cae277SBorislav Petkov {
236b972fdbaSShiju Jose 	if (system_scanned)
237b9cae277SBorislav Petkov 		return;
238b9cae277SBorislav Petkov 
239b9cae277SBorislav Petkov 	dmi_walk(enumerate_dimms, &ghes_hw);
240b9cae277SBorislav Petkov 
241b972fdbaSShiju Jose 	system_scanned = true;
24232fa1f53SMauro Carvalho Chehab }
24332fa1f53SMauro Carvalho Chehab 
print_mem_error_other_detail(const struct cper_sec_mem_err * mem,char * msg,const char * location,unsigned int len)244ed27b5dfSShuai Xue static int print_mem_error_other_detail(const struct cper_sec_mem_err *mem, char *msg,
245ed27b5dfSShuai Xue 					const char *location, unsigned int len)
246ed27b5dfSShuai Xue {
247ed27b5dfSShuai Xue 	u32 n;
248ed27b5dfSShuai Xue 
249ed27b5dfSShuai Xue 	if (!msg)
250ed27b5dfSShuai Xue 		return 0;
251ed27b5dfSShuai Xue 
252ed27b5dfSShuai Xue 	n = 0;
253ed27b5dfSShuai Xue 	len -= 1;
254ed27b5dfSShuai Xue 
255ed27b5dfSShuai Xue 	n += scnprintf(msg + n, len - n, "APEI location: %s ", location);
256ed27b5dfSShuai Xue 
257ed27b5dfSShuai Xue 	if (!(mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS))
258ed27b5dfSShuai Xue 		goto out;
259ed27b5dfSShuai Xue 
260ed27b5dfSShuai Xue 	n += scnprintf(msg + n, len - n, "status(0x%016llx): ", mem->error_status);
261ed27b5dfSShuai Xue 	n += scnprintf(msg + n, len - n, "%s ", cper_mem_err_status_str(mem->error_status));
262ed27b5dfSShuai Xue 
263ed27b5dfSShuai Xue out:
264ed27b5dfSShuai Xue 	msg[n] = '\0';
265ed27b5dfSShuai Xue 
266ed27b5dfSShuai Xue 	return n;
267ed27b5dfSShuai Xue }
268ed27b5dfSShuai Xue 
ghes_edac_report_mem_error(struct notifier_block * nb,unsigned long val,void * data)2698e40612fSJia He static int ghes_edac_report_mem_error(struct notifier_block *nb,
2708e40612fSJia He 				      unsigned long val, void *data)
27177c5f5d2SMauro Carvalho Chehab {
2728e40612fSJia He 	struct cper_sec_mem_err *mem_err = (struct cper_sec_mem_err *)data;
273ed27b5dfSShuai Xue 	struct cper_mem_err_compact cmem;
274f04c62a7SMauro Carvalho Chehab 	struct edac_raw_error_desc *e;
275f04c62a7SMauro Carvalho Chehab 	struct mem_ctl_info *mci;
2768e40612fSJia He 	unsigned long sev = val;
277b001694dSRobert Richter 	struct ghes_pvt *pvt;
2780fe5f281SBorislav Petkov 	unsigned long flags;
279689c9cd8SMauro Carvalho Chehab 	char *p;
280f04c62a7SMauro Carvalho Chehab 
2810fe5f281SBorislav Petkov 	/*
2820fe5f281SBorislav Petkov 	 * We can do the locking below because GHES defers error processing
2830fe5f281SBorislav Petkov 	 * from NMI to IRQ context. Whenever that changes, we'd at least
2840fe5f281SBorislav Petkov 	 * know.
2850fe5f281SBorislav Petkov 	 */
2860fe5f281SBorislav Petkov 	if (WARN_ON_ONCE(in_nmi()))
2878e40612fSJia He 		return NOTIFY_OK;
2880fe5f281SBorislav Petkov 
2890fe5f281SBorislav Petkov 	spin_lock_irqsave(&ghes_lock, flags);
2900fe5f281SBorislav Petkov 
29123f61b9fSRobert Richter 	pvt = ghes_pvt;
29223f61b9fSRobert Richter 	if (!pvt)
29323f61b9fSRobert Richter 		goto unlock;
29423f61b9fSRobert Richter 
295f04c62a7SMauro Carvalho Chehab 	mci = pvt->mci;
296f04c62a7SMauro Carvalho Chehab 	e = &mci->error_desc;
297f04c62a7SMauro Carvalho Chehab 
298f04c62a7SMauro Carvalho Chehab 	/* Cleans the error report buffer */
299f04c62a7SMauro Carvalho Chehab 	memset(e, 0, sizeof (*e));
300f04c62a7SMauro Carvalho Chehab 	e->error_count = 1;
3017088e29eSRobert Richter 	e->grain = 1;
302689c9cd8SMauro Carvalho Chehab 	e->msg = pvt->msg;
303689c9cd8SMauro Carvalho Chehab 	e->other_detail = pvt->other_detail;
304689c9cd8SMauro Carvalho Chehab 	e->top_layer = -1;
305689c9cd8SMauro Carvalho Chehab 	e->mid_layer = -1;
306689c9cd8SMauro Carvalho Chehab 	e->low_layer = -1;
307689c9cd8SMauro Carvalho Chehab 	*pvt->other_detail = '\0';
308689c9cd8SMauro Carvalho Chehab 	*pvt->msg = '\0';
309f04c62a7SMauro Carvalho Chehab 
310f04c62a7SMauro Carvalho Chehab 	switch (sev) {
311f04c62a7SMauro Carvalho Chehab 	case GHES_SEV_CORRECTED:
312672ef0e5SRobert Richter 		e->type = HW_EVENT_ERR_CORRECTED;
313f04c62a7SMauro Carvalho Chehab 		break;
314f04c62a7SMauro Carvalho Chehab 	case GHES_SEV_RECOVERABLE:
315672ef0e5SRobert Richter 		e->type = HW_EVENT_ERR_UNCORRECTED;
316f04c62a7SMauro Carvalho Chehab 		break;
317f04c62a7SMauro Carvalho Chehab 	case GHES_SEV_PANIC:
318672ef0e5SRobert Richter 		e->type = HW_EVENT_ERR_FATAL;
319f04c62a7SMauro Carvalho Chehab 		break;
320f04c62a7SMauro Carvalho Chehab 	default:
321f04c62a7SMauro Carvalho Chehab 	case GHES_SEV_NO:
322672ef0e5SRobert Richter 		e->type = HW_EVENT_ERR_INFO;
323f04c62a7SMauro Carvalho Chehab 	}
324f04c62a7SMauro Carvalho Chehab 
325689c9cd8SMauro Carvalho Chehab 	edac_dbg(1, "error validation_bits: 0x%08llx\n",
326689c9cd8SMauro Carvalho Chehab 		 (long long)mem_err->validation_bits);
327689c9cd8SMauro Carvalho Chehab 
328689c9cd8SMauro Carvalho Chehab 	/* Error type, mapped on e->msg */
329689c9cd8SMauro Carvalho Chehab 	if (mem_err->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
330ed27b5dfSShuai Xue 		u8 etype = mem_err->error_type;
331ed27b5dfSShuai Xue 
332689c9cd8SMauro Carvalho Chehab 		p = pvt->msg;
333ed27b5dfSShuai Xue 		p += snprintf(p, sizeof(pvt->msg), "%s", cper_mem_err_type_str(etype));
334689c9cd8SMauro Carvalho Chehab 	} else {
335689c9cd8SMauro Carvalho Chehab 		strcpy(pvt->msg, "unknown error");
336689c9cd8SMauro Carvalho Chehab 	}
337689c9cd8SMauro Carvalho Chehab 
338689c9cd8SMauro Carvalho Chehab 	/* Error address */
339147de147SChen, Gong 	if (mem_err->validation_bits & CPER_MEM_VALID_PA) {
3407c104931SRobert Richter 		e->page_frame_number = PHYS_PFN(mem_err->physical_addr);
3417c104931SRobert Richter 		e->offset_in_page = offset_in_page(mem_err->physical_addr);
342689c9cd8SMauro Carvalho Chehab 	}
343689c9cd8SMauro Carvalho Chehab 
344689c9cd8SMauro Carvalho Chehab 	/* Error grain */
345147de147SChen, Gong 	if (mem_err->validation_bits & CPER_MEM_VALID_PA_MASK)
3467088e29eSRobert Richter 		e->grain = ~mem_err->physical_addr_mask + 1;
347689c9cd8SMauro Carvalho Chehab 
348689c9cd8SMauro Carvalho Chehab 	/* Memory error location, mapped on e->location */
349689c9cd8SMauro Carvalho Chehab 	p = e->location;
350ed27b5dfSShuai Xue 	cper_mem_err_pack(mem_err, &cmem);
351ed27b5dfSShuai Xue 	p += cper_mem_err_location(&cmem, p);
3529baf68ccSAlex Kluver 
35356507694SChen, Gong 	if (mem_err->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) {
354cb51a371SRobert Richter 		struct dimm_info *dimm;
355c798c88fSFan Wu 
356ed27b5dfSShuai Xue 		p += cper_dimm_err_location(&cmem, p);
357cb51a371SRobert Richter 		dimm = find_dimm_by_handle(mci, mem_err->mem_dev_handle);
358cb51a371SRobert Richter 		if (dimm) {
359cb51a371SRobert Richter 			e->top_layer = dimm->idx;
360cb51a371SRobert Richter 			strcpy(e->label, dimm->label);
361cb51a371SRobert Richter 		}
36256507694SChen, Gong 	}
363689c9cd8SMauro Carvalho Chehab 	if (p > e->location)
364689c9cd8SMauro Carvalho Chehab 		*(p - 1) = '\0';
365689c9cd8SMauro Carvalho Chehab 
366cb51a371SRobert Richter 	if (!*e->label)
367cb51a371SRobert Richter 		strcpy(e->label, "unknown memory");
368cb51a371SRobert Richter 
369689c9cd8SMauro Carvalho Chehab 	/* All other fields are mapped on e->other_detail */
370689c9cd8SMauro Carvalho Chehab 	p = pvt->other_detail;
371ed27b5dfSShuai Xue 	p += print_mem_error_other_detail(mem_err, p, e->location, OTHER_DETAIL_LEN);
372689c9cd8SMauro Carvalho Chehab 	if (p > pvt->other_detail)
373689c9cd8SMauro Carvalho Chehab 		*(p - 1) = '\0';
374f04c62a7SMauro Carvalho Chehab 
37591b327f6SRobert Richter 	edac_raw_mc_handle_error(e);
37623f61b9fSRobert Richter 
37723f61b9fSRobert Richter unlock:
3780fe5f281SBorislav Petkov 	spin_unlock_irqrestore(&ghes_lock, flags);
3798e40612fSJia He 
3808e40612fSJia He 	return NOTIFY_OK;
38177c5f5d2SMauro Carvalho Chehab }
38277c5f5d2SMauro Carvalho Chehab 
3838e40612fSJia He static struct notifier_block ghes_edac_mem_err_nb = {
3848e40612fSJia He 	.notifier_call	= ghes_edac_report_mem_error,
3858e40612fSJia He 	.priority	= 0,
3868e40612fSJia He };
3878e40612fSJia He 
ghes_edac_register(struct device * dev)388*802e7f1dSJia He static int ghes_edac_register(struct device *dev)
38977c5f5d2SMauro Carvalho Chehab {
39032fa1f53SMauro Carvalho Chehab 	bool fake = false;
39177c5f5d2SMauro Carvalho Chehab 	struct mem_ctl_info *mci;
392b001694dSRobert Richter 	struct ghes_pvt *pvt;
39377c5f5d2SMauro Carvalho Chehab 	struct edac_mc_layer layers[1];
39423f61b9fSRobert Richter 	unsigned long flags;
395b9cae277SBorislav Petkov 	int rc = 0;
3965deed6b6SToshi Kani 
39723f61b9fSRobert Richter 	/* finish another registration/unregistration instance first */
39823f61b9fSRobert Richter 	mutex_lock(&ghes_reg_mutex);
39923f61b9fSRobert Richter 
4000fe5f281SBorislav Petkov 	/*
4010fe5f281SBorislav Petkov 	 * We have only one logical memory controller to which all DIMMs belong.
4020fe5f281SBorislav Petkov 	 */
40323f61b9fSRobert Richter 	if (refcount_inc_not_zero(&ghes_refcount))
40423f61b9fSRobert Richter 		goto unlock;
4050fe5f281SBorislav Petkov 
406b9cae277SBorislav Petkov 	ghes_scan_system();
40732fa1f53SMauro Carvalho Chehab 
40832fa1f53SMauro Carvalho Chehab 	/* Check if we've got a bogus BIOS */
409b9cae277SBorislav Petkov 	if (!ghes_hw.num_dimms) {
41032fa1f53SMauro Carvalho Chehab 		fake = true;
411b9cae277SBorislav Petkov 		ghes_hw.num_dimms = 1;
41232fa1f53SMauro Carvalho Chehab 	}
41377c5f5d2SMauro Carvalho Chehab 
41477c5f5d2SMauro Carvalho Chehab 	layers[0].type = EDAC_MC_LAYER_ALL_MEM;
415b9cae277SBorislav Petkov 	layers[0].size = ghes_hw.num_dimms;
41677c5f5d2SMauro Carvalho Chehab 	layers[0].is_virt_csrow = true;
41777c5f5d2SMauro Carvalho Chehab 
418b001694dSRobert Richter 	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(struct ghes_pvt));
41977c5f5d2SMauro Carvalho Chehab 	if (!mci) {
420d2a68566SMauro Carvalho Chehab 		pr_info("Can't allocate memory for EDAC data\n");
42123f61b9fSRobert Richter 		rc = -ENOMEM;
42223f61b9fSRobert Richter 		goto unlock;
42377c5f5d2SMauro Carvalho Chehab 	}
42477c5f5d2SMauro Carvalho Chehab 
42523f61b9fSRobert Richter 	pvt		= mci->pvt_info;
42623f61b9fSRobert Richter 	pvt->mci	= mci;
42777c5f5d2SMauro Carvalho Chehab 
4280fe5f281SBorislav Petkov 	mci->pdev = dev;
42977c5f5d2SMauro Carvalho Chehab 	mci->mtype_cap = MEM_FLAG_EMPTY;
43077c5f5d2SMauro Carvalho Chehab 	mci->edac_ctl_cap = EDAC_FLAG_NONE;
43177c5f5d2SMauro Carvalho Chehab 	mci->edac_cap = EDAC_FLAG_NONE;
43277c5f5d2SMauro Carvalho Chehab 	mci->mod_name = "ghes_edac.c";
43377c5f5d2SMauro Carvalho Chehab 	mci->ctl_name = "ghes_edac";
43477c5f5d2SMauro Carvalho Chehab 	mci->dev_name = "ghes";
43577c5f5d2SMauro Carvalho Chehab 
4365deed6b6SToshi Kani 	if (fake) {
4375deed6b6SToshi Kani 		pr_info("This system has a very crappy BIOS: It doesn't even list the DIMMS.\n");
4385deed6b6SToshi Kani 		pr_info("Its SMBIOS info is wrong. It is doubtful that the error report would\n");
4395deed6b6SToshi Kani 		pr_info("work on such system. Use this driver with caution\n");
440d2a68566SMauro Carvalho Chehab 	}
441d2a68566SMauro Carvalho Chehab 
4429057a3f7SJia He 	pr_info("This system has %d DIMM sockets.\n", ghes_hw.num_dimms);
4439057a3f7SJia He 
44432fa1f53SMauro Carvalho Chehab 	if (!fake) {
445b9cae277SBorislav Petkov 		struct dimm_info *src, *dst;
446b9cae277SBorislav Petkov 		int i = 0;
447b9cae277SBorislav Petkov 
448b9cae277SBorislav Petkov 		mci_for_each_dimm(mci, dst) {
449b9cae277SBorislav Petkov 			src = &ghes_hw.dimms[i];
450b9cae277SBorislav Petkov 
451b9cae277SBorislav Petkov 			dst->idx	   = src->idx;
452b9cae277SBorislav Petkov 			dst->smbios_handle = src->smbios_handle;
453b9cae277SBorislav Petkov 			dst->nr_pages	   = src->nr_pages;
454b9cae277SBorislav Petkov 			dst->mtype	   = src->mtype;
455b9cae277SBorislav Petkov 			dst->edac_mode	   = src->edac_mode;
456b9cae277SBorislav Petkov 			dst->dtype	   = src->dtype;
457b9cae277SBorislav Petkov 			dst->grain	   = src->grain;
458b9cae277SBorislav Petkov 
459b9cae277SBorislav Petkov 			/*
460b9cae277SBorislav Petkov 			 * If no src->label, preserve default label assigned
461b9cae277SBorislav Petkov 			 * from EDAC core.
462b9cae277SBorislav Petkov 			 */
463b9cae277SBorislav Petkov 			if (strlen(src->label))
464b9cae277SBorislav Petkov 				memcpy(dst->label, src->label, sizeof(src->label));
465b9cae277SBorislav Petkov 
466b9cae277SBorislav Petkov 			i++;
467b9cae277SBorislav Petkov 		}
468b9cae277SBorislav Petkov 
46932fa1f53SMauro Carvalho Chehab 	} else {
470bc9ad9e4SRobert Richter 		struct dimm_info *dimm = edac_get_dimm(mci, 0, 0, 0);
47177c5f5d2SMauro Carvalho Chehab 
472d2a68566SMauro Carvalho Chehab 		dimm->nr_pages = 1;
47377c5f5d2SMauro Carvalho Chehab 		dimm->grain = 128;
47477c5f5d2SMauro Carvalho Chehab 		dimm->mtype = MEM_UNKNOWN;
47577c5f5d2SMauro Carvalho Chehab 		dimm->dtype = DEV_UNKNOWN;
47677c5f5d2SMauro Carvalho Chehab 		dimm->edac_mode = EDAC_SECDED;
47732fa1f53SMauro Carvalho Chehab 	}
47877c5f5d2SMauro Carvalho Chehab 
47977c5f5d2SMauro Carvalho Chehab 	rc = edac_mc_add_mc(mci);
48077c5f5d2SMauro Carvalho Chehab 	if (rc < 0) {
481b9cae277SBorislav Petkov 		pr_info("Can't register with the EDAC core\n");
48277c5f5d2SMauro Carvalho Chehab 		edac_mc_free(mci);
48323f61b9fSRobert Richter 		rc = -ENODEV;
48423f61b9fSRobert Richter 		goto unlock;
48577c5f5d2SMauro Carvalho Chehab 	}
48623f61b9fSRobert Richter 
48723f61b9fSRobert Richter 	spin_lock_irqsave(&ghes_lock, flags);
48823f61b9fSRobert Richter 	ghes_pvt = pvt;
48923f61b9fSRobert Richter 	spin_unlock_irqrestore(&ghes_lock, flags);
49023f61b9fSRobert Richter 
4918e40612fSJia He 	ghes_register_report_chain(&ghes_edac_mem_err_nb);
4928e40612fSJia He 
49316214bd9SRobert Richter 	/* only set on success */
49416214bd9SRobert Richter 	refcount_set(&ghes_refcount, 1);
49523f61b9fSRobert Richter 
49623f61b9fSRobert Richter unlock:
497b9cae277SBorislav Petkov 
498b9cae277SBorislav Petkov 	/* Not needed anymore */
499b9cae277SBorislav Petkov 	kfree(ghes_hw.dimms);
500b9cae277SBorislav Petkov 	ghes_hw.dimms = NULL;
501b9cae277SBorislav Petkov 
50223f61b9fSRobert Richter 	mutex_unlock(&ghes_reg_mutex);
50323f61b9fSRobert Richter 
50423f61b9fSRobert Richter 	return rc;
50577c5f5d2SMauro Carvalho Chehab }
50677c5f5d2SMauro Carvalho Chehab 
ghes_edac_unregister(struct ghes * ghes)507*802e7f1dSJia He static void ghes_edac_unregister(struct ghes *ghes)
50877c5f5d2SMauro Carvalho Chehab {
50977c5f5d2SMauro Carvalho Chehab 	struct mem_ctl_info *mci;
51023f61b9fSRobert Richter 	unsigned long flags;
51177c5f5d2SMauro Carvalho Chehab 
51223f61b9fSRobert Richter 	mutex_lock(&ghes_reg_mutex);
513a66bdf5dSSughosh Ganu 
514b972fdbaSShiju Jose 	system_scanned = false;
515cd8100f1SBorislav Petkov 	memset(&ghes_hw, 0, sizeof(struct ghes_hw_desc));
516b972fdbaSShiju Jose 
51723f61b9fSRobert Richter 	if (!refcount_dec_and_test(&ghes_refcount))
51823f61b9fSRobert Richter 		goto unlock;
5191e72e673SJames Morse 
52023f61b9fSRobert Richter 	/*
52123f61b9fSRobert Richter 	 * Wait for the irq handler being finished.
52223f61b9fSRobert Richter 	 */
52323f61b9fSRobert Richter 	spin_lock_irqsave(&ghes_lock, flags);
52423f61b9fSRobert Richter 	mci = ghes_pvt ? ghes_pvt->mci : NULL;
5251e72e673SJames Morse 	ghes_pvt = NULL;
52623f61b9fSRobert Richter 	spin_unlock_irqrestore(&ghes_lock, flags);
52723f61b9fSRobert Richter 
52823f61b9fSRobert Richter 	if (!mci)
52923f61b9fSRobert Richter 		goto unlock;
53023f61b9fSRobert Richter 
53123f61b9fSRobert Richter 	mci = edac_mc_del_mc(mci->pdev);
53223f61b9fSRobert Richter 	if (mci)
53377c5f5d2SMauro Carvalho Chehab 		edac_mc_free(mci);
53423f61b9fSRobert Richter 
5358e40612fSJia He 	ghes_unregister_report_chain(&ghes_edac_mem_err_nb);
5368e40612fSJia He 
53723f61b9fSRobert Richter unlock:
53823f61b9fSRobert Richter 	mutex_unlock(&ghes_reg_mutex);
53977c5f5d2SMauro Carvalho Chehab }
540*802e7f1dSJia He 
ghes_edac_init(void)541*802e7f1dSJia He static int __init ghes_edac_init(void)
542*802e7f1dSJia He {
543*802e7f1dSJia He 	struct ghes *g, *g_tmp;
544*802e7f1dSJia He 
545*802e7f1dSJia He 	ghes_devs = ghes_get_devices();
546*802e7f1dSJia He 	if (!ghes_devs)
547*802e7f1dSJia He 		return -ENODEV;
548*802e7f1dSJia He 
549*802e7f1dSJia He 	if (list_empty(ghes_devs)) {
550*802e7f1dSJia He 		pr_info("GHES probing device list is empty");
551*802e7f1dSJia He 		return -ENODEV;
552*802e7f1dSJia He 	}
553*802e7f1dSJia He 
554*802e7f1dSJia He 	list_for_each_entry_safe(g, g_tmp, ghes_devs, elist) {
555*802e7f1dSJia He 		ghes_edac_register(g->dev);
556*802e7f1dSJia He 	}
557*802e7f1dSJia He 
558*802e7f1dSJia He 	return 0;
559*802e7f1dSJia He }
560*802e7f1dSJia He module_init(ghes_edac_init);
561*802e7f1dSJia He 
ghes_edac_exit(void)562*802e7f1dSJia He static void __exit ghes_edac_exit(void)
563*802e7f1dSJia He {
564*802e7f1dSJia He 	struct ghes *g, *g_tmp;
565*802e7f1dSJia He 
566*802e7f1dSJia He 	list_for_each_entry_safe(g, g_tmp, ghes_devs, elist) {
567*802e7f1dSJia He 		ghes_edac_unregister(g);
568*802e7f1dSJia He 	}
569*802e7f1dSJia He }
570*802e7f1dSJia He module_exit(ghes_edac_exit);
571*802e7f1dSJia He 
572*802e7f1dSJia He MODULE_LICENSE("GPL");
573*802e7f1dSJia He MODULE_DESCRIPTION("Output ACPI APEI/GHES BIOS detected errors via EDAC");
574