xref: /freebsd/sys/dev/acpica/acpi_apei.c (revision 5cd08d9e)
1855e49f3SAlexander Motin /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3855e49f3SAlexander Motin  *
4855e49f3SAlexander Motin  * Copyright (c) 2020 Alexander Motin <mav@FreeBSD.org>
5855e49f3SAlexander Motin  *
6855e49f3SAlexander Motin  * Redistribution and use in source and binary forms, with or without
7855e49f3SAlexander Motin  * modification, are permitted provided that the following conditions
8855e49f3SAlexander Motin  * are met:
9855e49f3SAlexander Motin  * 1. Redistributions of source code must retain the above copyright
10855e49f3SAlexander Motin  *    notice, this list of conditions and the following disclaimer.
11855e49f3SAlexander Motin  * 2. Redistributions in binary form must reproduce the above copyright
12855e49f3SAlexander Motin  *    notice, this list of conditions and the following disclaimer in the
13855e49f3SAlexander Motin  *    documentation and/or other materials provided with the distribution.
14855e49f3SAlexander Motin  *
15855e49f3SAlexander Motin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16855e49f3SAlexander Motin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17855e49f3SAlexander Motin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18855e49f3SAlexander Motin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19855e49f3SAlexander Motin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20855e49f3SAlexander Motin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21855e49f3SAlexander Motin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22855e49f3SAlexander Motin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23855e49f3SAlexander Motin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24855e49f3SAlexander Motin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25855e49f3SAlexander Motin  * SUCH DAMAGE.
26855e49f3SAlexander Motin  */
27855e49f3SAlexander Motin 
28855e49f3SAlexander Motin #include <sys/cdefs.h>
29855e49f3SAlexander Motin #include "opt_acpi.h"
309f91d464SAlexander Motin #include "opt_pci.h"
31855e49f3SAlexander Motin 
32855e49f3SAlexander Motin #include <sys/param.h>
33855e49f3SAlexander Motin #include <sys/systm.h>
34855e49f3SAlexander Motin #include <sys/bus.h>
35855e49f3SAlexander Motin #include <sys/callout.h>
36855e49f3SAlexander Motin #include <sys/interrupt.h>
37855e49f3SAlexander Motin #include <sys/kernel.h>
38855e49f3SAlexander Motin #include <sys/malloc.h>
39855e49f3SAlexander Motin #include <sys/module.h>
40855e49f3SAlexander Motin #include <sys/queue.h>
41855e49f3SAlexander Motin #include <sys/rman.h>
42855e49f3SAlexander Motin #include <vm/vm.h>
43855e49f3SAlexander Motin #include <vm/pmap.h>
44855e49f3SAlexander Motin 
45855e49f3SAlexander Motin #include <contrib/dev/acpica/include/acpi.h>
46855e49f3SAlexander Motin #include <contrib/dev/acpica/include/accommon.h>
47855e49f3SAlexander Motin #include <contrib/dev/acpica/include/aclocal.h>
48855e49f3SAlexander Motin #include <contrib/dev/acpica/include/actables.h>
49855e49f3SAlexander Motin 
50855e49f3SAlexander Motin #include <dev/acpica/acpivar.h>
51855e49f3SAlexander Motin #include <dev/pci/pcireg.h>
52855e49f3SAlexander Motin #include <dev/pci/pcivar.h>
53855e49f3SAlexander Motin 
54855e49f3SAlexander Motin struct apei_ge {
55855e49f3SAlexander Motin 	union {
56855e49f3SAlexander Motin 		ACPI_HEST_GENERIC v1;
57855e49f3SAlexander Motin 		ACPI_HEST_GENERIC_V2 v2;
58855e49f3SAlexander Motin 	};
59855e49f3SAlexander Motin 	int		 res_type;
60855e49f3SAlexander Motin 	int		 res_rid;
61855e49f3SAlexander Motin 	struct resource	*res;
62855e49f3SAlexander Motin 	int		 res2_type;
63855e49f3SAlexander Motin 	int		 res2_rid;
64855e49f3SAlexander Motin 	struct resource	*res2;
65855e49f3SAlexander Motin 	uint8_t		*buf, *copybuf;
66855e49f3SAlexander Motin 	TAILQ_ENTRY(apei_ge) link;
672dfc1f73SAlexander Motin 	TAILQ_ENTRY(apei_ge) nlink;
682dfc1f73SAlexander Motin };
692dfc1f73SAlexander Motin 
702dfc1f73SAlexander Motin /* NMI */
712dfc1f73SAlexander Motin struct apei_nges {
72855e49f3SAlexander Motin 	void		*swi_ih;
732dfc1f73SAlexander Motin 	TAILQ_HEAD(, apei_ge) ges;
742dfc1f73SAlexander Motin } *apei_nmi_nges;
752dfc1f73SAlexander Motin 
762dfc1f73SAlexander Motin /* Interrupt */
772dfc1f73SAlexander Motin struct apei_iges {
782dfc1f73SAlexander Motin 	TAILQ_HEAD(, apei_ge) ges;
792dfc1f73SAlexander Motin };
802dfc1f73SAlexander Motin 
812dfc1f73SAlexander Motin /* Polling */
822dfc1f73SAlexander Motin struct apei_pges {
832dfc1f73SAlexander Motin 	sbintime_t	 interval;
842dfc1f73SAlexander Motin 	struct callout	 poll;
852dfc1f73SAlexander Motin 	TAILQ_HEAD(, apei_ge) ges;
862dfc1f73SAlexander Motin };
87855e49f3SAlexander Motin 
88855e49f3SAlexander Motin struct apei_softc {
89855e49f3SAlexander Motin 	ACPI_TABLE_HEST *hest;
90855e49f3SAlexander Motin 	TAILQ_HEAD(, apei_ge) ges;
912dfc1f73SAlexander Motin 	struct apei_nges nges;
922dfc1f73SAlexander Motin 	struct apei_iges iges;
932dfc1f73SAlexander Motin 	struct apei_pges pges[32];
94855e49f3SAlexander Motin };
95855e49f3SAlexander Motin 
96855e49f3SAlexander Motin struct apei_mem_error {
97855e49f3SAlexander Motin 	uint64_t	ValidationBits;
98855e49f3SAlexander Motin 	uint64_t	ErrorStatus;
99855e49f3SAlexander Motin 	uint64_t	PhysicalAddress;
100855e49f3SAlexander Motin 	uint64_t	PhysicalAddressMask;
101855e49f3SAlexander Motin 	uint16_t	Node;
102855e49f3SAlexander Motin 	uint16_t	Card;
103855e49f3SAlexander Motin 	uint16_t	Module;
104855e49f3SAlexander Motin 	uint16_t	Bank;
105855e49f3SAlexander Motin 	uint16_t	Device;
106855e49f3SAlexander Motin 	uint16_t	Row;
107855e49f3SAlexander Motin 	uint16_t	Column;
108855e49f3SAlexander Motin 	uint16_t	BitPosition;
109855e49f3SAlexander Motin 	uint64_t	RequesterID;
110855e49f3SAlexander Motin 	uint64_t	ResponderID;
111855e49f3SAlexander Motin 	uint64_t	TargetID;
112855e49f3SAlexander Motin 	uint8_t		MemoryErrorType;
113855e49f3SAlexander Motin 	uint8_t		Extended;
114855e49f3SAlexander Motin 	uint16_t	RankNumber;
115855e49f3SAlexander Motin 	uint16_t	CardHandle;
116855e49f3SAlexander Motin 	uint16_t	ModuleHandle;
117855e49f3SAlexander Motin };
118855e49f3SAlexander Motin 
119855e49f3SAlexander Motin struct apei_pcie_error {
120855e49f3SAlexander Motin 	uint64_t	ValidationBits;
121855e49f3SAlexander Motin 	uint32_t	PortType;
122855e49f3SAlexander Motin 	uint32_t	Version;
123855e49f3SAlexander Motin 	uint32_t	CommandStatus;
124855e49f3SAlexander Motin 	uint32_t	Reserved;
125855e49f3SAlexander Motin 	uint8_t		DeviceID[16];
126855e49f3SAlexander Motin 	uint8_t		DeviceSerialNumber[8];
127855e49f3SAlexander Motin 	uint8_t		BridgeControlStatus[4];
128855e49f3SAlexander Motin 	uint8_t		CapabilityStructure[60];
129855e49f3SAlexander Motin 	uint8_t		AERInfo[96];
130855e49f3SAlexander Motin };
131855e49f3SAlexander Motin 
132855e49f3SAlexander Motin #ifdef __i386__
133855e49f3SAlexander Motin static __inline uint64_t
apei_bus_read_8(struct resource * res,bus_size_t offset)134855e49f3SAlexander Motin apei_bus_read_8(struct resource *res, bus_size_t offset)
135855e49f3SAlexander Motin {
136855e49f3SAlexander Motin 	return (bus_read_4(res, offset) |
137855e49f3SAlexander Motin 	    ((uint64_t)bus_read_4(res, offset + 4)) << 32);
138855e49f3SAlexander Motin }
139855e49f3SAlexander Motin static __inline void
apei_bus_write_8(struct resource * res,bus_size_t offset,uint64_t val)140855e49f3SAlexander Motin apei_bus_write_8(struct resource *res, bus_size_t offset, uint64_t val)
141855e49f3SAlexander Motin {
142855e49f3SAlexander Motin 	bus_write_4(res, offset, val);
143855e49f3SAlexander Motin 	bus_write_4(res, offset + 4, val >> 32);
144855e49f3SAlexander Motin }
145855e49f3SAlexander Motin #define	READ8(r, o)	apei_bus_read_8((r), (o))
146855e49f3SAlexander Motin #define	WRITE8(r, o, v)	apei_bus_write_8((r), (o), (v))
147855e49f3SAlexander Motin #else
148855e49f3SAlexander Motin #define	READ8(r, o)	bus_read_8((r), (o))
149855e49f3SAlexander Motin #define	WRITE8(r, o, v)	bus_write_8((r), (o), (v))
150855e49f3SAlexander Motin #endif
151855e49f3SAlexander Motin 
152ba83762aSAlexander Motin #define GED_SIZE(ged)	((ged)->Revision >= 0x300 ? \
153ba83762aSAlexander Motin     sizeof(ACPI_HEST_GENERIC_DATA_V300) : sizeof(ACPI_HEST_GENERIC_DATA))
154ba83762aSAlexander Motin #define GED_DATA(ged)	((uint8_t *)(ged) + GED_SIZE(ged))
155ba83762aSAlexander Motin 
1562dfc1f73SAlexander Motin #define PGE_ID(ge)	(fls(MAX(1, (ge)->v1.Notify.PollInterval)) - 1)
1572dfc1f73SAlexander Motin 
15852a54b96SAlexander Motin static struct sysctl_ctx_list apei_sysctl_ctx;
15952a54b96SAlexander Motin static struct sysctl_oid *apei_sysctl_tree;
16052a54b96SAlexander Motin static int log_corrected = 1;
16152a54b96SAlexander Motin 
162855e49f3SAlexander Motin int apei_nmi_handler(void);
163855e49f3SAlexander Motin 
164855e49f3SAlexander Motin static const char *
apei_severity(uint32_t s)165855e49f3SAlexander Motin apei_severity(uint32_t s)
166855e49f3SAlexander Motin {
167855e49f3SAlexander Motin 	switch (s) {
168855e49f3SAlexander Motin 	case ACPI_HEST_GEN_ERROR_RECOVERABLE:
169855e49f3SAlexander Motin 	    return ("Recoverable");
170855e49f3SAlexander Motin 	case ACPI_HEST_GEN_ERROR_FATAL:
171855e49f3SAlexander Motin 	    return ("Fatal");
172855e49f3SAlexander Motin 	case ACPI_HEST_GEN_ERROR_CORRECTED:
173855e49f3SAlexander Motin 	    return ("Corrected");
174855e49f3SAlexander Motin 	case ACPI_HEST_GEN_ERROR_NONE:
175855e49f3SAlexander Motin 	    return ("Informational");
176855e49f3SAlexander Motin 	}
177855e49f3SAlexander Motin 	return ("???");
178855e49f3SAlexander Motin }
179855e49f3SAlexander Motin 
180855e49f3SAlexander Motin static int
apei_mem_handler(ACPI_HEST_GENERIC_DATA * ged)181855e49f3SAlexander Motin apei_mem_handler(ACPI_HEST_GENERIC_DATA *ged)
182855e49f3SAlexander Motin {
183ba83762aSAlexander Motin 	struct apei_mem_error *p = (struct apei_mem_error *)GED_DATA(ged);
184855e49f3SAlexander Motin 
18552a54b96SAlexander Motin 	if (!log_corrected &&
18652a54b96SAlexander Motin 	    (ged->ErrorSeverity == ACPI_HEST_GEN_ERROR_CORRECTED ||
18752a54b96SAlexander Motin 	    ged->ErrorSeverity == ACPI_HEST_GEN_ERROR_NONE))
18852a54b96SAlexander Motin 		return (1);
18952a54b96SAlexander Motin 
190855e49f3SAlexander Motin 	printf("APEI %s Memory Error:\n", apei_severity(ged->ErrorSeverity));
191855e49f3SAlexander Motin 	if (p->ValidationBits & 0x01)
192855e49f3SAlexander Motin 		printf(" Error Status: 0x%jx\n", p->ErrorStatus);
193855e49f3SAlexander Motin 	if (p->ValidationBits & 0x02)
194855e49f3SAlexander Motin 		printf(" Physical Address: 0x%jx\n", p->PhysicalAddress);
195855e49f3SAlexander Motin 	if (p->ValidationBits & 0x04)
196855e49f3SAlexander Motin 		printf(" Physical Address Mask: 0x%jx\n", p->PhysicalAddressMask);
197855e49f3SAlexander Motin 	if (p->ValidationBits & 0x08)
198855e49f3SAlexander Motin 		printf(" Node: %u\n", p->Node);
199855e49f3SAlexander Motin 	if (p->ValidationBits & 0x10)
200855e49f3SAlexander Motin 		printf(" Card: %u\n", p->Card);
201855e49f3SAlexander Motin 	if (p->ValidationBits & 0x20)
202855e49f3SAlexander Motin 		printf(" Module: %u\n", p->Module);
203855e49f3SAlexander Motin 	if (p->ValidationBits & 0x40)
204855e49f3SAlexander Motin 		printf(" Bank: %u\n", p->Bank);
205855e49f3SAlexander Motin 	if (p->ValidationBits & 0x80)
206855e49f3SAlexander Motin 		printf(" Device: %u\n", p->Device);
207855e49f3SAlexander Motin 	if (p->ValidationBits & 0x100)
208855e49f3SAlexander Motin 		printf(" Row: %u\n", p->Row);
209855e49f3SAlexander Motin 	if (p->ValidationBits & 0x200)
210855e49f3SAlexander Motin 		printf(" Column: %u\n", p->Column);
211855e49f3SAlexander Motin 	if (p->ValidationBits & 0x400)
212855e49f3SAlexander Motin 		printf(" Bit Position: %u\n", p->BitPosition);
213855e49f3SAlexander Motin 	if (p->ValidationBits & 0x800)
214855e49f3SAlexander Motin 		printf(" Requester ID: 0x%jx\n", p->RequesterID);
215855e49f3SAlexander Motin 	if (p->ValidationBits & 0x1000)
216855e49f3SAlexander Motin 		printf(" Responder ID: 0x%jx\n", p->ResponderID);
217855e49f3SAlexander Motin 	if (p->ValidationBits & 0x2000)
218855e49f3SAlexander Motin 		printf(" Target ID: 0x%jx\n", p->TargetID);
219855e49f3SAlexander Motin 	if (p->ValidationBits & 0x4000)
220855e49f3SAlexander Motin 		printf(" Memory Error Type: %u\n", p->MemoryErrorType);
221855e49f3SAlexander Motin 	if (p->ValidationBits & 0x8000)
222855e49f3SAlexander Motin 		printf(" Rank Number: %u\n", p->RankNumber);
223855e49f3SAlexander Motin 	if (p->ValidationBits & 0x10000)
224855e49f3SAlexander Motin 		printf(" Card Handle: 0x%x\n", p->CardHandle);
225855e49f3SAlexander Motin 	if (p->ValidationBits & 0x20000)
226855e49f3SAlexander Motin 		printf(" Module Handle: 0x%x\n", p->ModuleHandle);
227855e49f3SAlexander Motin 	if (p->ValidationBits & 0x40000)
228855e49f3SAlexander Motin 		printf(" Extended Row: %u\n",
229855e49f3SAlexander Motin 		    (uint32_t)(p->Extended & 0x3) << 16 | p->Row);
230855e49f3SAlexander Motin 	if (p->ValidationBits & 0x80000)
231855e49f3SAlexander Motin 		printf(" Bank Group: %u\n", p->Bank >> 8);
232855e49f3SAlexander Motin 	if (p->ValidationBits & 0x100000)
233855e49f3SAlexander Motin 		printf(" Bank Address: %u\n", p->Bank & 0xff);
234855e49f3SAlexander Motin 	if (p->ValidationBits & 0x200000)
235855e49f3SAlexander Motin 		printf(" Chip Identification: %u\n", (p->Extended >> 5) & 0x7);
236855e49f3SAlexander Motin 
237855e49f3SAlexander Motin 	return (0);
238855e49f3SAlexander Motin }
239855e49f3SAlexander Motin 
240855e49f3SAlexander Motin static int
apei_pcie_handler(ACPI_HEST_GENERIC_DATA * ged)241855e49f3SAlexander Motin apei_pcie_handler(ACPI_HEST_GENERIC_DATA *ged)
242855e49f3SAlexander Motin {
243ba83762aSAlexander Motin 	struct apei_pcie_error *p = (struct apei_pcie_error *)GED_DATA(ged);
24452a54b96SAlexander Motin 	int off;
2459f91d464SAlexander Motin #ifdef DEV_PCI
246855e49f3SAlexander Motin 	device_t dev;
24752a54b96SAlexander Motin 	int h = 0, sev;
248855e49f3SAlexander Motin 
249855e49f3SAlexander Motin 	if ((p->ValidationBits & 0x8) == 0x8) {
250855e49f3SAlexander Motin 		mtx_lock(&Giant);
251855e49f3SAlexander Motin 		dev = pci_find_dbsf((uint32_t)p->DeviceID[10] << 8 |
252855e49f3SAlexander Motin 		    p->DeviceID[9], p->DeviceID[11], p->DeviceID[8],
253855e49f3SAlexander Motin 		    p->DeviceID[7]);
254855e49f3SAlexander Motin 		if (dev != NULL) {
255855e49f3SAlexander Motin 			switch (ged->ErrorSeverity) {
256855e49f3SAlexander Motin 			case ACPI_HEST_GEN_ERROR_FATAL:
257855e49f3SAlexander Motin 				sev = PCIEM_STA_FATAL_ERROR;
258855e49f3SAlexander Motin 				break;
259855e49f3SAlexander Motin 			case ACPI_HEST_GEN_ERROR_RECOVERABLE:
260855e49f3SAlexander Motin 				sev = PCIEM_STA_NON_FATAL_ERROR;
261855e49f3SAlexander Motin 				break;
262855e49f3SAlexander Motin 			default:
263855e49f3SAlexander Motin 				sev = PCIEM_STA_CORRECTABLE_ERROR;
264855e49f3SAlexander Motin 				break;
265855e49f3SAlexander Motin 			}
266855e49f3SAlexander Motin 			pcie_apei_error(dev, sev,
267855e49f3SAlexander Motin 			    (p->ValidationBits & 0x80) ? p->AERInfo : NULL);
268855e49f3SAlexander Motin 			h = 1;
269855e49f3SAlexander Motin 		}
270855e49f3SAlexander Motin 		mtx_unlock(&Giant);
271855e49f3SAlexander Motin 	}
272855e49f3SAlexander Motin 	if (h)
273855e49f3SAlexander Motin 		return (h);
2749f91d464SAlexander Motin #endif
275855e49f3SAlexander Motin 
27652a54b96SAlexander Motin 	if (!log_corrected &&
27752a54b96SAlexander Motin 	    (ged->ErrorSeverity == ACPI_HEST_GEN_ERROR_CORRECTED ||
27852a54b96SAlexander Motin 	    ged->ErrorSeverity == ACPI_HEST_GEN_ERROR_NONE))
27952a54b96SAlexander Motin 		return (1);
28052a54b96SAlexander Motin 
281855e49f3SAlexander Motin 	printf("APEI %s PCIe Error:\n", apei_severity(ged->ErrorSeverity));
282855e49f3SAlexander Motin 	if (p->ValidationBits & 0x01)
283855e49f3SAlexander Motin 		printf(" Port Type: %u\n", p->PortType);
284855e49f3SAlexander Motin 	if (p->ValidationBits & 0x02)
285855e49f3SAlexander Motin 		printf(" Version: %x\n", p->Version);
286855e49f3SAlexander Motin 	if (p->ValidationBits & 0x04)
287855e49f3SAlexander Motin 		printf(" Command Status: 0x%08x\n", p->CommandStatus);
288855e49f3SAlexander Motin 	if (p->ValidationBits & 0x08) {
289855e49f3SAlexander Motin 		printf(" DeviceID:");
290855e49f3SAlexander Motin 		for (off = 0; off < sizeof(p->DeviceID); off++)
291855e49f3SAlexander Motin 			printf(" %02x", p->DeviceID[off]);
292855e49f3SAlexander Motin 		printf("\n");
293855e49f3SAlexander Motin 	}
294855e49f3SAlexander Motin 	if (p->ValidationBits & 0x10) {
295855e49f3SAlexander Motin 		printf(" Device Serial Number:");
296855e49f3SAlexander Motin 		for (off = 0; off < sizeof(p->DeviceSerialNumber); off++)
297855e49f3SAlexander Motin 			printf(" %02x", p->DeviceSerialNumber[off]);
298855e49f3SAlexander Motin 		printf("\n");
299855e49f3SAlexander Motin 	}
300855e49f3SAlexander Motin 	if (p->ValidationBits & 0x20) {
301855e49f3SAlexander Motin 		printf(" Bridge Control Status:");
302855e49f3SAlexander Motin 		for (off = 0; off < sizeof(p->BridgeControlStatus); off++)
303855e49f3SAlexander Motin 			printf(" %02x", p->BridgeControlStatus[off]);
304855e49f3SAlexander Motin 		printf("\n");
305855e49f3SAlexander Motin 	}
306855e49f3SAlexander Motin 	if (p->ValidationBits & 0x40) {
307855e49f3SAlexander Motin 		printf(" Capability Structure:\n");
308855e49f3SAlexander Motin 		for (off = 0; off < sizeof(p->CapabilityStructure); off++) {
309855e49f3SAlexander Motin 			printf(" %02x", p->CapabilityStructure[off]);
310855e49f3SAlexander Motin 			if ((off % 16) == 15 ||
311855e49f3SAlexander Motin 			    off + 1 == sizeof(p->CapabilityStructure))
312855e49f3SAlexander Motin 				printf("\n");
313855e49f3SAlexander Motin 		}
314855e49f3SAlexander Motin 	}
315855e49f3SAlexander Motin 	if (p->ValidationBits & 0x80) {
316855e49f3SAlexander Motin 		printf(" AER Info:\n");
317855e49f3SAlexander Motin 		for (off = 0; off < sizeof(p->AERInfo); off++) {
318855e49f3SAlexander Motin 			printf(" %02x", p->AERInfo[off]);
319855e49f3SAlexander Motin 			if ((off % 16) == 15 || off + 1 == sizeof(p->AERInfo))
320855e49f3SAlexander Motin 				printf("\n");
321855e49f3SAlexander Motin 		}
322855e49f3SAlexander Motin 	}
32352a54b96SAlexander Motin 	return (0);
324855e49f3SAlexander Motin }
325855e49f3SAlexander Motin 
326855e49f3SAlexander Motin static void
apei_ged_handler(ACPI_HEST_GENERIC_DATA * ged)327855e49f3SAlexander Motin apei_ged_handler(ACPI_HEST_GENERIC_DATA *ged)
328855e49f3SAlexander Motin {
329855e49f3SAlexander Motin 	ACPI_HEST_GENERIC_DATA_V300 *ged3 = (ACPI_HEST_GENERIC_DATA_V300 *)ged;
330855e49f3SAlexander Motin 	/* A5BC1114-6F64-4EDE-B863-3E83ED7C83B1 */
331855e49f3SAlexander Motin 	static uint8_t mem_uuid[ACPI_UUID_LENGTH] = {
332855e49f3SAlexander Motin 		0x14, 0x11, 0xBC, 0xA5, 0x64, 0x6F, 0xDE, 0x4E,
333855e49f3SAlexander Motin 		0xB8, 0x63, 0x3E, 0x83, 0xED, 0x7C, 0x83, 0xB1
334855e49f3SAlexander Motin 	};
335855e49f3SAlexander Motin 	/* D995E954-BBC1-430F-AD91-B44DCB3C6F35 */
336855e49f3SAlexander Motin 	static uint8_t pcie_uuid[ACPI_UUID_LENGTH] = {
337855e49f3SAlexander Motin 		0x54, 0xE9, 0x95, 0xD9, 0xC1, 0xBB, 0x0F, 0x43,
338855e49f3SAlexander Motin 		0xAD, 0x91, 0xB4, 0x4D, 0xCB, 0x3C, 0x6F, 0x35
339855e49f3SAlexander Motin 	};
340855e49f3SAlexander Motin 	uint8_t *t;
341855e49f3SAlexander Motin 	int h = 0, off;
342855e49f3SAlexander Motin 
343855e49f3SAlexander Motin 	if (memcmp(mem_uuid, ged->SectionType, ACPI_UUID_LENGTH) == 0) {
344855e49f3SAlexander Motin 		h = apei_mem_handler(ged);
345855e49f3SAlexander Motin 	} else if (memcmp(pcie_uuid, ged->SectionType, ACPI_UUID_LENGTH) == 0) {
346855e49f3SAlexander Motin 		h = apei_pcie_handler(ged);
347855e49f3SAlexander Motin 	} else {
34852a54b96SAlexander Motin 		if (!log_corrected &&
34952a54b96SAlexander Motin 		    (ged->ErrorSeverity == ACPI_HEST_GEN_ERROR_CORRECTED ||
35052a54b96SAlexander Motin 		    ged->ErrorSeverity == ACPI_HEST_GEN_ERROR_NONE))
35152a54b96SAlexander Motin 			return;
35252a54b96SAlexander Motin 
353855e49f3SAlexander Motin 		t = ged->SectionType;
354855e49f3SAlexander Motin 		printf("APEI %s Error %02x%02x%02x%02x-%02x%02x-"
355855e49f3SAlexander Motin 		    "%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x:\n",
356855e49f3SAlexander Motin 		    apei_severity(ged->ErrorSeverity),
357855e49f3SAlexander Motin 		    t[3], t[2], t[1], t[0], t[5], t[4], t[7], t[6],
358855e49f3SAlexander Motin 		    t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
359855e49f3SAlexander Motin 		printf(" Error Data:\n");
360ba83762aSAlexander Motin 		t = (uint8_t *)GED_DATA(ged);
361855e49f3SAlexander Motin 		for (off = 0; off < ged->ErrorDataLength; off++) {
362855e49f3SAlexander Motin 			printf(" %02x", t[off]);
363855e49f3SAlexander Motin 			if ((off % 16) == 15 || off + 1 == ged->ErrorDataLength)
364855e49f3SAlexander Motin 				printf("\n");
365855e49f3SAlexander Motin 		}
366855e49f3SAlexander Motin 	}
367855e49f3SAlexander Motin 	if (h)
368855e49f3SAlexander Motin 		return;
369855e49f3SAlexander Motin 
370855e49f3SAlexander Motin 	printf(" Flags: 0x%x\n", ged->Flags);
371855e49f3SAlexander Motin 	if (ged->ValidationBits & ACPI_HEST_GEN_VALID_FRU_ID) {
372855e49f3SAlexander Motin 		t = ged->FruId;
373855e49f3SAlexander Motin 		printf(" FRU Id: %02x%02x%02x%02x-%02x%02x-%02x%02x-"
374855e49f3SAlexander Motin 		    "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
375855e49f3SAlexander Motin 		    t[3], t[2], t[1], t[0], t[5], t[4], t[7], t[6],
376855e49f3SAlexander Motin 		    t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
377855e49f3SAlexander Motin 	}
378855e49f3SAlexander Motin 	if (ged->ValidationBits & ACPI_HEST_GEN_VALID_FRU_STRING)
3798fee65d0SAlexander Motin 		printf(" FRU Text: %.20s\n", ged->FruText);
380ba83762aSAlexander Motin 	if (ged->Revision >= 0x300 &&
381855e49f3SAlexander Motin 	    ged->ValidationBits & ACPI_HEST_GEN_VALID_TIMESTAMP)
3828fee65d0SAlexander Motin 		printf(" Timestamp: %016jx\n", ged3->TimeStamp);
383855e49f3SAlexander Motin }
384855e49f3SAlexander Motin 
385855e49f3SAlexander Motin static int
apei_ge_handler(struct apei_ge * ge,bool copy)386855e49f3SAlexander Motin apei_ge_handler(struct apei_ge *ge, bool copy)
387855e49f3SAlexander Motin {
388855e49f3SAlexander Motin 	uint8_t *buf = copy ? ge->copybuf : ge->buf;
389855e49f3SAlexander Motin 	ACPI_HEST_GENERIC_STATUS *ges = (ACPI_HEST_GENERIC_STATUS *)buf;
390855e49f3SAlexander Motin 	ACPI_HEST_GENERIC_DATA *ged;
3913b248a21SAlexander Motin 	size_t off, len;
392855e49f3SAlexander Motin 	uint32_t sev;
3933b248a21SAlexander Motin 	int i, c;
394855e49f3SAlexander Motin 
395a4566383SAlexander Motin 	if (ges == NULL || ges->BlockStatus == 0)
396855e49f3SAlexander Motin 		return (0);
397855e49f3SAlexander Motin 
398855e49f3SAlexander Motin 	c = (ges->BlockStatus >> 4) & 0x3ff;
399855e49f3SAlexander Motin 	sev = ges->ErrorSeverity;
400855e49f3SAlexander Motin 
401855e49f3SAlexander Motin 	/* Process error entries. */
4023b248a21SAlexander Motin 	len = MIN(ge->v1.ErrorBlockLength - sizeof(*ges), ges->DataLength);
4033b248a21SAlexander Motin 	for (off = i = 0; i < c && off + sizeof(*ged) <= len; i++) {
404855e49f3SAlexander Motin 		ged = (ACPI_HEST_GENERIC_DATA *)&buf[sizeof(*ges) + off];
4053b248a21SAlexander Motin 		if ((uint64_t)GED_SIZE(ged) + ged->ErrorDataLength > len - off)
4063b248a21SAlexander Motin 			break;
407855e49f3SAlexander Motin 		apei_ged_handler(ged);
408ba83762aSAlexander Motin 		off += GED_SIZE(ged) + ged->ErrorDataLength;
409855e49f3SAlexander Motin 	}
410855e49f3SAlexander Motin 
411855e49f3SAlexander Motin 	/* Acknowledge the error has been processed. */
412855e49f3SAlexander Motin 	ges->BlockStatus = 0;
413a4566383SAlexander Motin 	if (!copy && ge->v1.Header.Type == ACPI_HEST_TYPE_GENERIC_ERROR_V2 &&
414a4566383SAlexander Motin 	    ge->res2) {
415855e49f3SAlexander Motin 		uint64_t val = READ8(ge->res2, 0);
416855e49f3SAlexander Motin 		val &= ge->v2.ReadAckPreserve;
417855e49f3SAlexander Motin 		val |= ge->v2.ReadAckWrite;
418855e49f3SAlexander Motin 		WRITE8(ge->res2, 0, val);
419855e49f3SAlexander Motin 	}
420855e49f3SAlexander Motin 
421855e49f3SAlexander Motin 	/* If ACPI told the error is fatal -- make it so. */
422855e49f3SAlexander Motin 	if (sev == ACPI_HEST_GEN_ERROR_FATAL)
423855e49f3SAlexander Motin 		panic("APEI Fatal Hardware Error!");
424855e49f3SAlexander Motin 
425855e49f3SAlexander Motin 	return (1);
426855e49f3SAlexander Motin }
427855e49f3SAlexander Motin 
428855e49f3SAlexander Motin static void
apei_nmi_swi(void * arg)429855e49f3SAlexander Motin apei_nmi_swi(void *arg)
430855e49f3SAlexander Motin {
4312dfc1f73SAlexander Motin 	struct apei_nges *nges = arg;
4322dfc1f73SAlexander Motin 	struct apei_ge *ge;
433855e49f3SAlexander Motin 
4342dfc1f73SAlexander Motin 	TAILQ_FOREACH(ge, &nges->ges, nlink)
435855e49f3SAlexander Motin 		apei_ge_handler(ge, true);
436855e49f3SAlexander Motin }
437855e49f3SAlexander Motin 
438855e49f3SAlexander Motin int
apei_nmi_handler(void)439855e49f3SAlexander Motin apei_nmi_handler(void)
440855e49f3SAlexander Motin {
4412dfc1f73SAlexander Motin 	struct apei_nges *nges = apei_nmi_nges;
4422dfc1f73SAlexander Motin 	struct apei_ge *ge;
443855e49f3SAlexander Motin 	ACPI_HEST_GENERIC_STATUS *ges, *gesc;
4442dfc1f73SAlexander Motin 	int handled = 0;
445855e49f3SAlexander Motin 
4462dfc1f73SAlexander Motin 	if (nges == NULL)
447855e49f3SAlexander Motin 		return (0);
448855e49f3SAlexander Motin 
4492dfc1f73SAlexander Motin 	TAILQ_FOREACH(ge, &nges->ges, nlink) {
450855e49f3SAlexander Motin 		ges = (ACPI_HEST_GENERIC_STATUS *)ge->buf;
451a4566383SAlexander Motin 		if (ges == NULL || ges->BlockStatus == 0)
4522dfc1f73SAlexander Motin 			continue;
453855e49f3SAlexander Motin 
454855e49f3SAlexander Motin 		/* If ACPI told the error is fatal -- make it so. */
455855e49f3SAlexander Motin 		if (ges->ErrorSeverity == ACPI_HEST_GEN_ERROR_FATAL)
456855e49f3SAlexander Motin 			panic("APEI Fatal Hardware Error!");
457855e49f3SAlexander Motin 
458855e49f3SAlexander Motin 		/* Copy the buffer for later processing. */
459855e49f3SAlexander Motin 		gesc = (ACPI_HEST_GENERIC_STATUS *)ge->copybuf;
460855e49f3SAlexander Motin 		if (gesc->BlockStatus == 0)
461855e49f3SAlexander Motin 			memcpy(ge->copybuf, ge->buf, ge->v1.ErrorBlockLength);
462855e49f3SAlexander Motin 
463855e49f3SAlexander Motin 		/* Acknowledge the error has been processed. */
464855e49f3SAlexander Motin 		ges->BlockStatus = 0;
465a4566383SAlexander Motin 		if (ge->v1.Header.Type == ACPI_HEST_TYPE_GENERIC_ERROR_V2 &&
466a4566383SAlexander Motin 		    ge->res2) {
467855e49f3SAlexander Motin 			uint64_t val = READ8(ge->res2, 0);
468855e49f3SAlexander Motin 			val &= ge->v2.ReadAckPreserve;
469855e49f3SAlexander Motin 			val |= ge->v2.ReadAckWrite;
470855e49f3SAlexander Motin 			WRITE8(ge->res2, 0, val);
471855e49f3SAlexander Motin 		}
4722dfc1f73SAlexander Motin 		handled = 1;
4732dfc1f73SAlexander Motin 	}
474855e49f3SAlexander Motin 
475855e49f3SAlexander Motin 	/* Schedule SWI for real handling. */
4762dfc1f73SAlexander Motin 	if (handled)
4772dfc1f73SAlexander Motin 		swi_sched(nges->swi_ih, SWI_FROMNMI);
478855e49f3SAlexander Motin 
4792dfc1f73SAlexander Motin 	return (handled);
480855e49f3SAlexander Motin }
481855e49f3SAlexander Motin 
482855e49f3SAlexander Motin static void
apei_callout_handler(void * context)483855e49f3SAlexander Motin apei_callout_handler(void *context)
484855e49f3SAlexander Motin {
4852dfc1f73SAlexander Motin 	struct apei_pges *pges = context;
4862dfc1f73SAlexander Motin 	struct apei_ge *ge;
487855e49f3SAlexander Motin 
4882dfc1f73SAlexander Motin 	TAILQ_FOREACH(ge, &pges->ges, nlink)
489855e49f3SAlexander Motin 		apei_ge_handler(ge, false);
4902dfc1f73SAlexander Motin 	callout_schedule_sbt(&pges->poll, pges->interval, pges->interval, 0);
491855e49f3SAlexander Motin }
492855e49f3SAlexander Motin 
493855e49f3SAlexander Motin static void
apei_notify_handler(ACPI_HANDLE h,UINT32 notify,void * context)494855e49f3SAlexander Motin apei_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
495855e49f3SAlexander Motin {
496855e49f3SAlexander Motin 	device_t dev = context;
497855e49f3SAlexander Motin 	struct apei_softc *sc = device_get_softc(dev);
498855e49f3SAlexander Motin 	struct apei_ge *ge;
499855e49f3SAlexander Motin 
5002dfc1f73SAlexander Motin 	TAILQ_FOREACH(ge, &sc->iges.ges, nlink)
501855e49f3SAlexander Motin 		apei_ge_handler(ge, false);
502855e49f3SAlexander Motin }
503855e49f3SAlexander Motin 
504855e49f3SAlexander Motin static int
hest_parse_structure(struct apei_softc * sc,void * addr,int remaining)505855e49f3SAlexander Motin hest_parse_structure(struct apei_softc *sc, void *addr, int remaining)
506855e49f3SAlexander Motin {
507855e49f3SAlexander Motin 	ACPI_HEST_HEADER *hdr = addr;
508855e49f3SAlexander Motin 	struct apei_ge *ge;
509855e49f3SAlexander Motin 
510855e49f3SAlexander Motin 	if (remaining < (int)sizeof(ACPI_HEST_HEADER))
511855e49f3SAlexander Motin 		return (-1);
512855e49f3SAlexander Motin 
513855e49f3SAlexander Motin 	switch (hdr->Type) {
514855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_IA32_CHECK: {
515855e49f3SAlexander Motin 		ACPI_HEST_IA_MACHINE_CHECK *s = addr;
516855e49f3SAlexander Motin 		return (sizeof(*s) + s->NumHardwareBanks *
517855e49f3SAlexander Motin 		    sizeof(ACPI_HEST_IA_ERROR_BANK));
518855e49f3SAlexander Motin 	}
519855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_IA32_CORRECTED_CHECK: {
520855e49f3SAlexander Motin 		ACPI_HEST_IA_CORRECTED *s = addr;
521855e49f3SAlexander Motin 		return (sizeof(*s) + s->NumHardwareBanks *
522855e49f3SAlexander Motin 		    sizeof(ACPI_HEST_IA_ERROR_BANK));
523855e49f3SAlexander Motin 	}
524855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_IA32_NMI: {
525855e49f3SAlexander Motin 		ACPI_HEST_IA_NMI *s = addr;
526855e49f3SAlexander Motin 		return (sizeof(*s));
527855e49f3SAlexander Motin 	}
528855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_AER_ROOT_PORT: {
529855e49f3SAlexander Motin 		ACPI_HEST_AER_ROOT *s = addr;
530855e49f3SAlexander Motin 		return (sizeof(*s));
531855e49f3SAlexander Motin 	}
532855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_AER_ENDPOINT: {
533855e49f3SAlexander Motin 		ACPI_HEST_AER *s = addr;
534855e49f3SAlexander Motin 		return (sizeof(*s));
535855e49f3SAlexander Motin 	}
536855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_AER_BRIDGE: {
537855e49f3SAlexander Motin 		ACPI_HEST_AER_BRIDGE *s = addr;
538855e49f3SAlexander Motin 		return (sizeof(*s));
539855e49f3SAlexander Motin 	}
540855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_GENERIC_ERROR: {
541855e49f3SAlexander Motin 		ACPI_HEST_GENERIC *s = addr;
542855e49f3SAlexander Motin 		ge = malloc(sizeof(*ge), M_DEVBUF, M_WAITOK | M_ZERO);
543855e49f3SAlexander Motin 		ge->v1 = *s;
544855e49f3SAlexander Motin 		TAILQ_INSERT_TAIL(&sc->ges, ge, link);
545855e49f3SAlexander Motin 		return (sizeof(*s));
546855e49f3SAlexander Motin 	}
547855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_GENERIC_ERROR_V2: {
548855e49f3SAlexander Motin 		ACPI_HEST_GENERIC_V2 *s = addr;
549855e49f3SAlexander Motin 		ge = malloc(sizeof(*ge), M_DEVBUF, M_WAITOK | M_ZERO);
550855e49f3SAlexander Motin 		ge->v2 = *s;
551855e49f3SAlexander Motin 		TAILQ_INSERT_TAIL(&sc->ges, ge, link);
552855e49f3SAlexander Motin 		return (sizeof(*s));
553855e49f3SAlexander Motin 	}
554855e49f3SAlexander Motin 	case ACPI_HEST_TYPE_IA32_DEFERRED_CHECK: {
555855e49f3SAlexander Motin 		ACPI_HEST_IA_DEFERRED_CHECK *s = addr;
556855e49f3SAlexander Motin 		return (sizeof(*s) + s->NumHardwareBanks *
557855e49f3SAlexander Motin 		    sizeof(ACPI_HEST_IA_ERROR_BANK));
558855e49f3SAlexander Motin 	}
559855e49f3SAlexander Motin 	default:
560855e49f3SAlexander Motin 		return (-1);
561855e49f3SAlexander Motin 	}
562855e49f3SAlexander Motin }
563855e49f3SAlexander Motin 
564855e49f3SAlexander Motin static void
hest_parse_table(struct apei_softc * sc)565855e49f3SAlexander Motin hest_parse_table(struct apei_softc *sc)
566855e49f3SAlexander Motin {
567855e49f3SAlexander Motin 	ACPI_TABLE_HEST *hest = sc->hest;
568855e49f3SAlexander Motin 	char *cp;
569855e49f3SAlexander Motin 	int remaining, consumed;
570855e49f3SAlexander Motin 
571855e49f3SAlexander Motin 	remaining = hest->Header.Length - sizeof(ACPI_TABLE_HEST);
572855e49f3SAlexander Motin 	while (remaining > 0) {
573855e49f3SAlexander Motin 		cp = (char *)hest + hest->Header.Length - remaining;
574855e49f3SAlexander Motin 		consumed = hest_parse_structure(sc, cp, remaining);
575855e49f3SAlexander Motin 		if (consumed <= 0)
576855e49f3SAlexander Motin 			break;
577855e49f3SAlexander Motin 		else
578855e49f3SAlexander Motin 			remaining -= consumed;
579855e49f3SAlexander Motin 	}
580855e49f3SAlexander Motin }
581855e49f3SAlexander Motin 
582855e49f3SAlexander Motin static char *apei_ids[] = { "PNP0C33", NULL };
583855e49f3SAlexander Motin 
584855e49f3SAlexander Motin static ACPI_STATUS
apei_find(ACPI_HANDLE handle,UINT32 level,void * context,void ** status)585855e49f3SAlexander Motin apei_find(ACPI_HANDLE handle, UINT32 level, void *context,
586855e49f3SAlexander Motin     void **status)
587855e49f3SAlexander Motin {
588855e49f3SAlexander Motin 	int *found = (int *)status;
589855e49f3SAlexander Motin 	char **ids;
590855e49f3SAlexander Motin 
591855e49f3SAlexander Motin 	for (ids = apei_ids; *ids != NULL; ids++) {
592855e49f3SAlexander Motin 		if (acpi_MatchHid(handle, *ids)) {
593855e49f3SAlexander Motin 			*found = 1;
594855e49f3SAlexander Motin 			break;
595855e49f3SAlexander Motin 		}
596855e49f3SAlexander Motin 	}
597855e49f3SAlexander Motin 	return (AE_OK);
598855e49f3SAlexander Motin }
599855e49f3SAlexander Motin 
600855e49f3SAlexander Motin static void
apei_identify(driver_t * driver,device_t parent)601855e49f3SAlexander Motin apei_identify(driver_t *driver, device_t parent)
602855e49f3SAlexander Motin {
603855e49f3SAlexander Motin 	device_t	child;
604855e49f3SAlexander Motin 	int		found;
6053b580150SAlexander Motin 	ACPI_TABLE_HEADER *hest;
6063b580150SAlexander Motin 	ACPI_STATUS	status;
607855e49f3SAlexander Motin 
608855e49f3SAlexander Motin 	if (acpi_disabled("apei"))
609855e49f3SAlexander Motin 		return;
6103b580150SAlexander Motin 
6113b580150SAlexander Motin 	/* Without HEST table we have nothing to do. */
6123b580150SAlexander Motin 	status = AcpiGetTable(ACPI_SIG_HEST, 0, &hest);
6133b580150SAlexander Motin 	if (ACPI_FAILURE(status))
614855e49f3SAlexander Motin 		return;
6153b580150SAlexander Motin 	AcpiPutTable(hest);
6163b580150SAlexander Motin 
617855e49f3SAlexander Motin 	/* Only one APEI device can exist. */
6183730d6aaSJohn Baldwin 	if (devclass_get_device(devclass_find("apei"), 0))
619855e49f3SAlexander Motin 		return;
6203b580150SAlexander Motin 
621855e49f3SAlexander Motin 	/* Search for ACPI error device to be used. */
622855e49f3SAlexander Motin 	found = 0;
623855e49f3SAlexander Motin 	AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
624855e49f3SAlexander Motin 	    100, apei_find, NULL, NULL, (void *)&found);
625855e49f3SAlexander Motin 	if (found)
626855e49f3SAlexander Motin 		return;
6273b580150SAlexander Motin 
628855e49f3SAlexander Motin 	/* If not found - create a fake one. */
629855e49f3SAlexander Motin 	child = BUS_ADD_CHILD(parent, 2, "apei", 0);
630855e49f3SAlexander Motin 	if (child == NULL)
631855e49f3SAlexander Motin 		printf("%s: can't add child\n", __func__);
632855e49f3SAlexander Motin }
633855e49f3SAlexander Motin 
634855e49f3SAlexander Motin static int
apei_probe(device_t dev)635855e49f3SAlexander Motin apei_probe(device_t dev)
636855e49f3SAlexander Motin {
6373b580150SAlexander Motin 	ACPI_TABLE_HEADER *hest;
6383b580150SAlexander Motin 	ACPI_STATUS	status;
639855e49f3SAlexander Motin 	int rv;
640855e49f3SAlexander Motin 
641855e49f3SAlexander Motin 	if (acpi_disabled("apei"))
642855e49f3SAlexander Motin 		return (ENXIO);
6433b580150SAlexander Motin 
6443b580150SAlexander Motin 	if (acpi_get_handle(dev) != NULL) {
645855e49f3SAlexander Motin 		rv = ACPI_ID_PROBE(device_get_parent(dev), dev, apei_ids, NULL);
6463b580150SAlexander Motin 		if (rv > 0)
6473b580150SAlexander Motin 			return (rv);
6483b580150SAlexander Motin 	} else
649855e49f3SAlexander Motin 		rv = 0;
6503b580150SAlexander Motin 
6513b580150SAlexander Motin 	/* Without HEST table we have nothing to do. */
6523b580150SAlexander Motin 	status = AcpiGetTable(ACPI_SIG_HEST, 0, &hest);
6533b580150SAlexander Motin 	if (ACPI_FAILURE(status))
6543b580150SAlexander Motin 		return (ENXIO);
6553b580150SAlexander Motin 	AcpiPutTable(hest);
6563b580150SAlexander Motin 
6573b580150SAlexander Motin 	device_set_desc(dev, "ACPI Platform Error Interface");
658855e49f3SAlexander Motin 	return (rv);
659855e49f3SAlexander Motin }
660855e49f3SAlexander Motin 
661855e49f3SAlexander Motin static int
apei_attach(device_t dev)662855e49f3SAlexander Motin apei_attach(device_t dev)
663855e49f3SAlexander Motin {
664855e49f3SAlexander Motin 	struct apei_softc *sc = device_get_softc(dev);
66552a54b96SAlexander Motin 	struct acpi_softc *acpi_sc;
6662dfc1f73SAlexander Motin 	struct apei_pges *pges;
667855e49f3SAlexander Motin 	struct apei_ge *ge;
668855e49f3SAlexander Motin 	ACPI_STATUS status;
669855e49f3SAlexander Motin 	int rid;
670855e49f3SAlexander Motin 
67152a54b96SAlexander Motin 	if (!apei_sysctl_tree) {
67252a54b96SAlexander Motin 		/* Install hw.acpi.apei sysctl tree */
67352a54b96SAlexander Motin 		acpi_sc = acpi_device_get_parent_softc(dev);
67452a54b96SAlexander Motin 		apei_sysctl_tree = SYSCTL_ADD_NODE(&apei_sysctl_ctx,
67552a54b96SAlexander Motin 		    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
67652a54b96SAlexander Motin 		    "apei", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
67752a54b96SAlexander Motin 		    "ACPI Platform Error Interface");
67852a54b96SAlexander Motin 		SYSCTL_ADD_INT(&apei_sysctl_ctx, SYSCTL_CHILDREN(apei_sysctl_tree),
67952a54b96SAlexander Motin 		    OID_AUTO, "log_corrected", CTLFLAG_RWTUN, &log_corrected, 0,
68052a54b96SAlexander Motin 		    "Log corrected errors to the console");
68152a54b96SAlexander Motin 	}
68252a54b96SAlexander Motin 
683855e49f3SAlexander Motin 	TAILQ_INIT(&sc->ges);
6842dfc1f73SAlexander Motin 	TAILQ_INIT(&sc->nges.ges);
6852dfc1f73SAlexander Motin 	TAILQ_INIT(&sc->iges.ges);
6862dfc1f73SAlexander Motin 	for (int i = 0; i < nitems(sc->pges); i++) {
6872dfc1f73SAlexander Motin 		pges = &sc->pges[i];
6882dfc1f73SAlexander Motin 		pges->interval = SBT_1MS << i;
6892dfc1f73SAlexander Motin 		callout_init(&pges->poll, 1);
6902dfc1f73SAlexander Motin 		TAILQ_INIT(&pges->ges);
6912dfc1f73SAlexander Motin 	}
692855e49f3SAlexander Motin 
693855e49f3SAlexander Motin 	/* Search and parse HEST table. */
694855e49f3SAlexander Motin 	status = AcpiGetTable(ACPI_SIG_HEST, 0, (ACPI_TABLE_HEADER **)&sc->hest);
695855e49f3SAlexander Motin 	if (ACPI_FAILURE(status))
696855e49f3SAlexander Motin 		return (ENXIO);
697855e49f3SAlexander Motin 	hest_parse_table(sc);
698855e49f3SAlexander Motin 	AcpiPutTable((ACPI_TABLE_HEADER *)sc->hest);
699855e49f3SAlexander Motin 
700855e49f3SAlexander Motin 	rid = 0;
701855e49f3SAlexander Motin 	TAILQ_FOREACH(ge, &sc->ges, link) {
702855e49f3SAlexander Motin 		ge->res_rid = rid++;
703855e49f3SAlexander Motin 		acpi_bus_alloc_gas(dev, &ge->res_type, &ge->res_rid,
704855e49f3SAlexander Motin 		    &ge->v1.ErrorStatusAddress, &ge->res, 0);
705a4566383SAlexander Motin 		if (ge->res) {
706a4566383SAlexander Motin 			ge->buf = pmap_mapdev_attr(READ8(ge->res, 0),
707a4566383SAlexander Motin 			    ge->v1.ErrorBlockLength, VM_MEMATTR_WRITE_COMBINING);
708a4566383SAlexander Motin 		} else {
709a4566383SAlexander Motin 			device_printf(dev, "Can't allocate status resource.\n");
710a4566383SAlexander Motin 		}
711855e49f3SAlexander Motin 		if (ge->v1.Header.Type == ACPI_HEST_TYPE_GENERIC_ERROR_V2) {
712855e49f3SAlexander Motin 			ge->res2_rid = rid++;
713855e49f3SAlexander Motin 			acpi_bus_alloc_gas(dev, &ge->res2_type, &ge->res2_rid,
7145cd08d9eSAndrew Gallatin 			    &ge->v2.ReadAckRegister, &ge->res2, RF_SHAREABLE);
715a4566383SAlexander Motin 			if (ge->res2 == NULL)
716a4566383SAlexander Motin 				device_printf(dev, "Can't allocate ack resource.\n");
717855e49f3SAlexander Motin 		}
718855e49f3SAlexander Motin 		if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_POLLED) {
7192dfc1f73SAlexander Motin 			pges = &sc->pges[PGE_ID(ge)];
7202dfc1f73SAlexander Motin 			TAILQ_INSERT_TAIL(&sc->pges[PGE_ID(ge)].ges, ge, nlink);
7212dfc1f73SAlexander Motin 			callout_reset_sbt(&pges->poll, pges->interval, pges->interval,
7222dfc1f73SAlexander Motin 			    apei_callout_handler, pges, 0);
7232dfc1f73SAlexander Motin 		} else if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_SCI ||
7242dfc1f73SAlexander Motin 		    ge->v1.Notify.Type == ACPI_HEST_NOTIFY_GPIO ||
7252dfc1f73SAlexander Motin 		    ge->v1.Notify.Type == ACPI_HEST_NOTIFY_GSIV) {
7262dfc1f73SAlexander Motin 			TAILQ_INSERT_TAIL(&sc->iges.ges, ge, nlink);
727855e49f3SAlexander Motin 		} else if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_NMI) {
728855e49f3SAlexander Motin 			ge->copybuf = malloc(ge->v1.ErrorBlockLength,
729855e49f3SAlexander Motin 			    M_DEVBUF, M_WAITOK | M_ZERO);
7302dfc1f73SAlexander Motin 			TAILQ_INSERT_TAIL(&sc->nges.ges, ge, nlink);
7312dfc1f73SAlexander Motin 			if (sc->nges.swi_ih == NULL) {
7322dfc1f73SAlexander Motin 				swi_add(&clk_intr_event, "apei", apei_nmi_swi,
7332dfc1f73SAlexander Motin 				    &sc->nges, SWI_CLOCK, INTR_MPSAFE,
7342dfc1f73SAlexander Motin 				    &sc->nges.swi_ih);
7352dfc1f73SAlexander Motin 				apei_nmi_nges = &sc->nges;
736855e49f3SAlexander Motin 				apei_nmi = apei_nmi_handler;
737855e49f3SAlexander Motin 			}
738855e49f3SAlexander Motin 		}
7392dfc1f73SAlexander Motin 	}
740855e49f3SAlexander Motin 
741855e49f3SAlexander Motin 	if (acpi_get_handle(dev) != NULL) {
742855e49f3SAlexander Motin 		AcpiInstallNotifyHandler(acpi_get_handle(dev),
743855e49f3SAlexander Motin 		    ACPI_DEVICE_NOTIFY, apei_notify_handler, dev);
744855e49f3SAlexander Motin 	}
745855e49f3SAlexander Motin 	return (0);
746855e49f3SAlexander Motin }
747855e49f3SAlexander Motin 
748855e49f3SAlexander Motin static int
apei_detach(device_t dev)749855e49f3SAlexander Motin apei_detach(device_t dev)
750855e49f3SAlexander Motin {
751855e49f3SAlexander Motin 	struct apei_softc *sc = device_get_softc(dev);
752855e49f3SAlexander Motin 	struct apei_ge *ge;
753855e49f3SAlexander Motin 
754855e49f3SAlexander Motin 	apei_nmi = NULL;
7552dfc1f73SAlexander Motin 	apei_nmi_nges = NULL;
7562dfc1f73SAlexander Motin 	if (sc->nges.swi_ih != NULL) {
7572dfc1f73SAlexander Motin 		swi_remove(&sc->nges.swi_ih);
7582dfc1f73SAlexander Motin 		sc->nges.swi_ih = NULL;
7592dfc1f73SAlexander Motin 	}
760855e49f3SAlexander Motin 	if (acpi_get_handle(dev) != NULL) {
761855e49f3SAlexander Motin 		AcpiRemoveNotifyHandler(acpi_get_handle(dev),
762855e49f3SAlexander Motin 		    ACPI_DEVICE_NOTIFY, apei_notify_handler);
763855e49f3SAlexander Motin 	}
7642dfc1f73SAlexander Motin 	for (int i = 0; i < nitems(sc->pges); i++)
7652dfc1f73SAlexander Motin 		callout_drain(&sc->pges[i].poll);
766855e49f3SAlexander Motin 
767855e49f3SAlexander Motin 	while ((ge = TAILQ_FIRST(&sc->ges)) != NULL) {
768855e49f3SAlexander Motin 		TAILQ_REMOVE(&sc->ges, ge, link);
769a4566383SAlexander Motin 		if (ge->res) {
770a4566383SAlexander Motin 			bus_release_resource(dev, ge->res_type,
771a4566383SAlexander Motin 			    ge->res_rid, ge->res);
772a4566383SAlexander Motin 		}
773855e49f3SAlexander Motin 		if (ge->res2) {
774855e49f3SAlexander Motin 			bus_release_resource(dev, ge->res2_type,
775855e49f3SAlexander Motin 			    ge->res2_rid, ge->res2);
776855e49f3SAlexander Motin 		}
777855e49f3SAlexander Motin 		if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_POLLED) {
7782dfc1f73SAlexander Motin 			TAILQ_REMOVE(&sc->pges[PGE_ID(ge)].ges, ge, nlink);
7792dfc1f73SAlexander Motin 		} else if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_SCI ||
7802dfc1f73SAlexander Motin 		    ge->v1.Notify.Type == ACPI_HEST_NOTIFY_GPIO ||
7812dfc1f73SAlexander Motin 		    ge->v1.Notify.Type == ACPI_HEST_NOTIFY_GSIV) {
7822dfc1f73SAlexander Motin 			TAILQ_REMOVE(&sc->iges.ges, ge, nlink);
783855e49f3SAlexander Motin 		} else if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_NMI) {
7842dfc1f73SAlexander Motin 			TAILQ_REMOVE(&sc->nges.ges, ge, nlink);
785855e49f3SAlexander Motin 			free(ge->copybuf, M_DEVBUF);
786855e49f3SAlexander Motin 		}
787a4566383SAlexander Motin 		if (ge->buf) {
7887ae99f80SJohn Baldwin 			pmap_unmapdev(ge->buf, ge->v1.ErrorBlockLength);
789a4566383SAlexander Motin 		}
790855e49f3SAlexander Motin 		free(ge, M_DEVBUF);
791855e49f3SAlexander Motin 	}
792855e49f3SAlexander Motin 	return (0);
793855e49f3SAlexander Motin }
794855e49f3SAlexander Motin 
795855e49f3SAlexander Motin static device_method_t apei_methods[] = {
796855e49f3SAlexander Motin 	/* Device interface */
797855e49f3SAlexander Motin 	DEVMETHOD(device_identify, apei_identify),
798855e49f3SAlexander Motin 	DEVMETHOD(device_probe, apei_probe),
799855e49f3SAlexander Motin 	DEVMETHOD(device_attach, apei_attach),
800855e49f3SAlexander Motin 	DEVMETHOD(device_detach, apei_detach),
801855e49f3SAlexander Motin 	DEVMETHOD_END
802855e49f3SAlexander Motin };
803855e49f3SAlexander Motin 
804855e49f3SAlexander Motin static driver_t	apei_driver = {
805855e49f3SAlexander Motin 	"apei",
806855e49f3SAlexander Motin 	apei_methods,
807855e49f3SAlexander Motin 	sizeof(struct apei_softc),
808855e49f3SAlexander Motin };
809855e49f3SAlexander Motin 
81052a54b96SAlexander Motin static int
apei_modevent(struct module * mod __unused,int evt,void * cookie __unused)81152a54b96SAlexander Motin apei_modevent(struct module *mod __unused, int evt, void *cookie __unused)
81252a54b96SAlexander Motin {
81352a54b96SAlexander Motin 	int err = 0;
81452a54b96SAlexander Motin 
81552a54b96SAlexander Motin 	switch (evt) {
81652a54b96SAlexander Motin 	case MOD_LOAD:
81752a54b96SAlexander Motin 		sysctl_ctx_init(&apei_sysctl_ctx);
81852a54b96SAlexander Motin 		break;
81952a54b96SAlexander Motin 	case MOD_UNLOAD:
82052a54b96SAlexander Motin 		sysctl_ctx_free(&apei_sysctl_ctx);
82152a54b96SAlexander Motin 		break;
82252a54b96SAlexander Motin 	default:
82352a54b96SAlexander Motin 		err = EINVAL;
82452a54b96SAlexander Motin 	}
82552a54b96SAlexander Motin 	return (err);
82652a54b96SAlexander Motin }
82752a54b96SAlexander Motin 
82852a54b96SAlexander Motin DRIVER_MODULE(apei, acpi, apei_driver, apei_modevent, 0);
829855e49f3SAlexander Motin MODULE_DEPEND(apei, acpi, 1, 1, 1);
830