2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2020 Alexander Motin <mav@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/systm.h>
37 #include <sys/callout.h>
38 #include <sys/interrupt.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/queue.h>
47 #include <contrib/dev/acpica/include/acpi.h>
48 #include <contrib/dev/acpica/include/accommon.h>
49 #include <contrib/dev/acpica/include/aclocal.h>
50 #include <contrib/dev/acpica/include/actables.h>
52 #include <dev/acpica/acpivar.h>
53 #include <dev/pci/pcireg.h>
54 #include <dev/pci/pcivar.h>
59 ACPI_HEST_GENERIC_V2 v2;
66 struct resource *res2;
67 uint8_t *buf, *copybuf;
68 TAILQ_ENTRY(apei_ge) link;
74 ACPI_TABLE_HEST *hest;
75 TAILQ_HEAD(, apei_ge) ges;
78 struct apei_mem_error {
79 uint64_t ValidationBits;
81 uint64_t PhysicalAddress;
82 uint64_t PhysicalAddressMask;
94 uint8_t MemoryErrorType;
98 uint16_t ModuleHandle;
101 struct apei_pcie_error {
102 uint64_t ValidationBits;
105 uint32_t CommandStatus;
107 uint8_t DeviceID[16];
108 uint8_t DeviceSerialNumber[8];
109 uint8_t BridgeControlStatus[4];
110 uint8_t CapabilityStructure[60];
115 static __inline uint64_t
116 apei_bus_read_8(struct resource *res, bus_size_t offset)
118 return (bus_read_4(res, offset) |
119 ((uint64_t)bus_read_4(res, offset + 4)) << 32);
122 apei_bus_write_8(struct resource *res, bus_size_t offset, uint64_t val)
124 bus_write_4(res, offset, val);
125 bus_write_4(res, offset + 4, val >> 32);
127 #define READ8(r, o) apei_bus_read_8((r), (o))
128 #define WRITE8(r, o, v) apei_bus_write_8((r), (o), (v))
130 #define READ8(r, o) bus_read_8((r), (o))
131 #define WRITE8(r, o, v) bus_write_8((r), (o), (v))
134 int apei_nmi_handler(void);
137 apei_severity(uint32_t s)
140 case ACPI_HEST_GEN_ERROR_RECOVERABLE:
141 return ("Recoverable");
142 case ACPI_HEST_GEN_ERROR_FATAL:
144 case ACPI_HEST_GEN_ERROR_CORRECTED:
145 return ("Corrected");
146 case ACPI_HEST_GEN_ERROR_NONE:
147 return ("Informational");
153 apei_mem_handler(ACPI_HEST_GENERIC_DATA *ged)
155 struct apei_mem_error *p = (struct apei_mem_error *)(ged + 1);
157 printf("APEI %s Memory Error:\n", apei_severity(ged->ErrorSeverity));
158 if (p->ValidationBits & 0x01)
159 printf(" Error Status: 0x%jx\n", p->ErrorStatus);
160 if (p->ValidationBits & 0x02)
161 printf(" Physical Address: 0x%jx\n", p->PhysicalAddress);
162 if (p->ValidationBits & 0x04)
163 printf(" Physical Address Mask: 0x%jx\n", p->PhysicalAddressMask);
164 if (p->ValidationBits & 0x08)
165 printf(" Node: %u\n", p->Node);
166 if (p->ValidationBits & 0x10)
167 printf(" Card: %u\n", p->Card);
168 if (p->ValidationBits & 0x20)
169 printf(" Module: %u\n", p->Module);
170 if (p->ValidationBits & 0x40)
171 printf(" Bank: %u\n", p->Bank);
172 if (p->ValidationBits & 0x80)
173 printf(" Device: %u\n", p->Device);
174 if (p->ValidationBits & 0x100)
175 printf(" Row: %u\n", p->Row);
176 if (p->ValidationBits & 0x200)
177 printf(" Column: %u\n", p->Column);
178 if (p->ValidationBits & 0x400)
179 printf(" Bit Position: %u\n", p->BitPosition);
180 if (p->ValidationBits & 0x800)
181 printf(" Requester ID: 0x%jx\n", p->RequesterID);
182 if (p->ValidationBits & 0x1000)
183 printf(" Responder ID: 0x%jx\n", p->ResponderID);
184 if (p->ValidationBits & 0x2000)
185 printf(" Target ID: 0x%jx\n", p->TargetID);
186 if (p->ValidationBits & 0x4000)
187 printf(" Memory Error Type: %u\n", p->MemoryErrorType);
188 if (p->ValidationBits & 0x8000)
189 printf(" Rank Number: %u\n", p->RankNumber);
190 if (p->ValidationBits & 0x10000)
191 printf(" Card Handle: 0x%x\n", p->CardHandle);
192 if (p->ValidationBits & 0x20000)
193 printf(" Module Handle: 0x%x\n", p->ModuleHandle);
194 if (p->ValidationBits & 0x40000)
195 printf(" Extended Row: %u\n",
196 (uint32_t)(p->Extended & 0x3) << 16 | p->Row);
197 if (p->ValidationBits & 0x80000)
198 printf(" Bank Group: %u\n", p->Bank >> 8);
199 if (p->ValidationBits & 0x100000)
200 printf(" Bank Address: %u\n", p->Bank & 0xff);
201 if (p->ValidationBits & 0x200000)
202 printf(" Chip Identification: %u\n", (p->Extended >> 5) & 0x7);
208 apei_pcie_handler(ACPI_HEST_GENERIC_DATA *ged)
210 struct apei_pcie_error *p = (struct apei_pcie_error *)(ged + 1);
216 if ((p->ValidationBits & 0x8) == 0x8) {
218 dev = pci_find_dbsf((uint32_t)p->DeviceID[10] << 8 |
219 p->DeviceID[9], p->DeviceID[11], p->DeviceID[8],
222 switch (ged->ErrorSeverity) {
223 case ACPI_HEST_GEN_ERROR_FATAL:
224 sev = PCIEM_STA_FATAL_ERROR;
226 case ACPI_HEST_GEN_ERROR_RECOVERABLE:
227 sev = PCIEM_STA_NON_FATAL_ERROR;
230 sev = PCIEM_STA_CORRECTABLE_ERROR;
233 pcie_apei_error(dev, sev,
234 (p->ValidationBits & 0x80) ? p->AERInfo : NULL);
243 printf("APEI %s PCIe Error:\n", apei_severity(ged->ErrorSeverity));
244 if (p->ValidationBits & 0x01)
245 printf(" Port Type: %u\n", p->PortType);
246 if (p->ValidationBits & 0x02)
247 printf(" Version: %x\n", p->Version);
248 if (p->ValidationBits & 0x04)
249 printf(" Command Status: 0x%08x\n", p->CommandStatus);
250 if (p->ValidationBits & 0x08) {
251 printf(" DeviceID:");
252 for (off = 0; off < sizeof(p->DeviceID); off++)
253 printf(" %02x", p->DeviceID[off]);
256 if (p->ValidationBits & 0x10) {
257 printf(" Device Serial Number:");
258 for (off = 0; off < sizeof(p->DeviceSerialNumber); off++)
259 printf(" %02x", p->DeviceSerialNumber[off]);
262 if (p->ValidationBits & 0x20) {
263 printf(" Bridge Control Status:");
264 for (off = 0; off < sizeof(p->BridgeControlStatus); off++)
265 printf(" %02x", p->BridgeControlStatus[off]);
268 if (p->ValidationBits & 0x40) {
269 printf(" Capability Structure:\n");
270 for (off = 0; off < sizeof(p->CapabilityStructure); off++) {
271 printf(" %02x", p->CapabilityStructure[off]);
272 if ((off % 16) == 15 ||
273 off + 1 == sizeof(p->CapabilityStructure))
277 if (p->ValidationBits & 0x80) {
278 printf(" AER Info:\n");
279 for (off = 0; off < sizeof(p->AERInfo); off++) {
280 printf(" %02x", p->AERInfo[off]);
281 if ((off % 16) == 15 || off + 1 == sizeof(p->AERInfo))
289 apei_ged_handler(ACPI_HEST_GENERIC_DATA *ged)
291 ACPI_HEST_GENERIC_DATA_V300 *ged3 = (ACPI_HEST_GENERIC_DATA_V300 *)ged;
292 /* A5BC1114-6F64-4EDE-B863-3E83ED7C83B1 */
293 static uint8_t mem_uuid[ACPI_UUID_LENGTH] = {
294 0x14, 0x11, 0xBC, 0xA5, 0x64, 0x6F, 0xDE, 0x4E,
295 0xB8, 0x63, 0x3E, 0x83, 0xED, 0x7C, 0x83, 0xB1
297 /* D995E954-BBC1-430F-AD91-B44DCB3C6F35 */
298 static uint8_t pcie_uuid[ACPI_UUID_LENGTH] = {
299 0x54, 0xE9, 0x95, 0xD9, 0xC1, 0xBB, 0x0F, 0x43,
300 0xAD, 0x91, 0xB4, 0x4D, 0xCB, 0x3C, 0x6F, 0x35
305 if (memcmp(mem_uuid, ged->SectionType, ACPI_UUID_LENGTH) == 0) {
306 h = apei_mem_handler(ged);
307 } else if (memcmp(pcie_uuid, ged->SectionType, ACPI_UUID_LENGTH) == 0) {
308 h = apei_pcie_handler(ged);
310 t = ged->SectionType;
311 printf("APEI %s Error %02x%02x%02x%02x-%02x%02x-"
312 "%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x:\n",
313 apei_severity(ged->ErrorSeverity),
314 t[3], t[2], t[1], t[0], t[5], t[4], t[7], t[6],
315 t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
316 printf(" Error Data:\n");
317 t = (uint8_t *)(ged + 1);
318 for (off = 0; off < ged->ErrorDataLength; off++) {
319 printf(" %02x", t[off]);
320 if ((off % 16) == 15 || off + 1 == ged->ErrorDataLength)
327 printf(" Flags: 0x%x\n", ged->Flags);
328 if (ged->ValidationBits & ACPI_HEST_GEN_VALID_FRU_ID) {
330 printf(" FRU Id: %02x%02x%02x%02x-%02x%02x-%02x%02x-"
331 "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
332 t[3], t[2], t[1], t[0], t[5], t[4], t[7], t[6],
333 t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
335 if (ged->ValidationBits & ACPI_HEST_GEN_VALID_FRU_STRING)
336 printf(" FRU Text: %.20s", ged->FruText);
337 if (ged->Revision == 0x300 &&
338 ged->ValidationBits & ACPI_HEST_GEN_VALID_TIMESTAMP)
339 printf(" Timestamp: %016jx", ged3->TimeStamp);
343 apei_ge_handler(struct apei_ge *ge, bool copy)
345 uint8_t *buf = copy ? ge->copybuf : ge->buf;
346 ACPI_HEST_GENERIC_STATUS *ges = (ACPI_HEST_GENERIC_STATUS *)buf;
347 ACPI_HEST_GENERIC_DATA *ged;
351 if (ges == NULL || ges->BlockStatus == 0)
354 c = (ges->BlockStatus >> 4) & 0x3ff;
355 sev = ges->ErrorSeverity;
357 /* Process error entries. */
358 for (off = i = 0; i < c && off + sizeof(*ged) <= ges->DataLength; i++) {
359 ged = (ACPI_HEST_GENERIC_DATA *)&buf[sizeof(*ges) + off];
360 apei_ged_handler(ged);
361 off += sizeof(*ged) + ged->ErrorDataLength;
364 /* Acknowledge the error has been processed. */
365 ges->BlockStatus = 0;
366 if (!copy && ge->v1.Header.Type == ACPI_HEST_TYPE_GENERIC_ERROR_V2 &&
368 uint64_t val = READ8(ge->res2, 0);
369 val &= ge->v2.ReadAckPreserve;
370 val |= ge->v2.ReadAckWrite;
371 WRITE8(ge->res2, 0, val);
374 /* If ACPI told the error is fatal -- make it so. */
375 if (sev == ACPI_HEST_GEN_ERROR_FATAL)
376 panic("APEI Fatal Hardware Error!");
382 apei_nmi_swi(void *arg)
384 struct apei_ge *ge = arg;
386 apei_ge_handler(ge, true);
390 apei_nmi_handler(void)
392 struct apei_ge *ge = apei_nmi_ge;
393 ACPI_HEST_GENERIC_STATUS *ges, *gesc;
398 ges = (ACPI_HEST_GENERIC_STATUS *)ge->buf;
399 if (ges == NULL || ges->BlockStatus == 0)
402 /* If ACPI told the error is fatal -- make it so. */
403 if (ges->ErrorSeverity == ACPI_HEST_GEN_ERROR_FATAL)
404 panic("APEI Fatal Hardware Error!");
406 /* Copy the buffer for later processing. */
407 gesc = (ACPI_HEST_GENERIC_STATUS *)ge->copybuf;
408 if (gesc->BlockStatus == 0)
409 memcpy(ge->copybuf, ge->buf, ge->v1.ErrorBlockLength);
411 /* Acknowledge the error has been processed. */
412 ges->BlockStatus = 0;
413 if (ge->v1.Header.Type == ACPI_HEST_TYPE_GENERIC_ERROR_V2 &&
415 uint64_t val = READ8(ge->res2, 0);
416 val &= ge->v2.ReadAckPreserve;
417 val |= ge->v2.ReadAckWrite;
418 WRITE8(ge->res2, 0, val);
421 /* Schedule SWI for real handling. */
422 swi_sched(ge->swi_ih, SWI_FROMNMI);
428 apei_callout_handler(void *context)
430 struct apei_ge *ge = context;
432 apei_ge_handler(ge, false);
433 callout_schedule(&ge->poll, ge->v1.Notify.PollInterval * hz / 1000);
437 apei_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
439 device_t dev = context;
440 struct apei_softc *sc = device_get_softc(dev);
443 TAILQ_FOREACH(ge, &sc->ges, link) {
444 if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_SCI ||
445 ge->v1.Notify.Type == ACPI_HEST_NOTIFY_GPIO ||
446 ge->v1.Notify.Type == ACPI_HEST_NOTIFY_GSIV)
447 apei_ge_handler(ge, false);
452 hest_parse_structure(struct apei_softc *sc, void *addr, int remaining)
454 ACPI_HEST_HEADER *hdr = addr;
457 if (remaining < (int)sizeof(ACPI_HEST_HEADER))
461 case ACPI_HEST_TYPE_IA32_CHECK: {
462 ACPI_HEST_IA_MACHINE_CHECK *s = addr;
463 return (sizeof(*s) + s->NumHardwareBanks *
464 sizeof(ACPI_HEST_IA_ERROR_BANK));
466 case ACPI_HEST_TYPE_IA32_CORRECTED_CHECK: {
467 ACPI_HEST_IA_CORRECTED *s = addr;
468 return (sizeof(*s) + s->NumHardwareBanks *
469 sizeof(ACPI_HEST_IA_ERROR_BANK));
471 case ACPI_HEST_TYPE_IA32_NMI: {
472 ACPI_HEST_IA_NMI *s = addr;
475 case ACPI_HEST_TYPE_AER_ROOT_PORT: {
476 ACPI_HEST_AER_ROOT *s = addr;
479 case ACPI_HEST_TYPE_AER_ENDPOINT: {
480 ACPI_HEST_AER *s = addr;
483 case ACPI_HEST_TYPE_AER_BRIDGE: {
484 ACPI_HEST_AER_BRIDGE *s = addr;
487 case ACPI_HEST_TYPE_GENERIC_ERROR: {
488 ACPI_HEST_GENERIC *s = addr;
489 ge = malloc(sizeof(*ge), M_DEVBUF, M_WAITOK | M_ZERO);
491 TAILQ_INSERT_TAIL(&sc->ges, ge, link);
494 case ACPI_HEST_TYPE_GENERIC_ERROR_V2: {
495 ACPI_HEST_GENERIC_V2 *s = addr;
496 ge = malloc(sizeof(*ge), M_DEVBUF, M_WAITOK | M_ZERO);
498 TAILQ_INSERT_TAIL(&sc->ges, ge, link);
501 case ACPI_HEST_TYPE_IA32_DEFERRED_CHECK: {
502 ACPI_HEST_IA_DEFERRED_CHECK *s = addr;
503 return (sizeof(*s) + s->NumHardwareBanks *
504 sizeof(ACPI_HEST_IA_ERROR_BANK));
512 hest_parse_table(struct apei_softc *sc)
514 ACPI_TABLE_HEST *hest = sc->hest;
516 int remaining, consumed;
518 remaining = hest->Header.Length - sizeof(ACPI_TABLE_HEST);
519 while (remaining > 0) {
520 cp = (char *)hest + hest->Header.Length - remaining;
521 consumed = hest_parse_structure(sc, cp, remaining);
525 remaining -= consumed;
529 static char *apei_ids[] = { "PNP0C33", NULL };
530 static devclass_t apei_devclass;
533 apei_find(ACPI_HANDLE handle, UINT32 level, void *context,
536 int *found = (int *)status;
539 for (ids = apei_ids; *ids != NULL; ids++) {
540 if (acpi_MatchHid(handle, *ids)) {
549 apei_identify(driver_t *driver, device_t parent)
553 ACPI_TABLE_HEADER *hest;
556 if (acpi_disabled("apei"))
559 /* Without HEST table we have nothing to do. */
560 status = AcpiGetTable(ACPI_SIG_HEST, 0, &hest);
561 if (ACPI_FAILURE(status))
565 /* Only one APEI device can exist. */
566 if (devclass_get_device(apei_devclass, 0))
569 /* Search for ACPI error device to be used. */
571 AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
572 100, apei_find, NULL, NULL, (void *)&found);
576 /* If not found - create a fake one. */
577 child = BUS_ADD_CHILD(parent, 2, "apei", 0);
579 printf("%s: can't add child\n", __func__);
583 apei_probe(device_t dev)
585 ACPI_TABLE_HEADER *hest;
589 if (acpi_disabled("apei"))
592 if (acpi_get_handle(dev) != NULL) {
593 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, apei_ids, NULL);
599 /* Without HEST table we have nothing to do. */
600 status = AcpiGetTable(ACPI_SIG_HEST, 0, &hest);
601 if (ACPI_FAILURE(status))
605 device_set_desc(dev, "ACPI Platform Error Interface");
610 apei_attach(device_t dev)
612 struct apei_softc *sc = device_get_softc(dev);
617 TAILQ_INIT(&sc->ges);
619 /* Search and parse HEST table. */
620 status = AcpiGetTable(ACPI_SIG_HEST, 0, (ACPI_TABLE_HEADER **)&sc->hest);
621 if (ACPI_FAILURE(status))
623 hest_parse_table(sc);
624 AcpiPutTable((ACPI_TABLE_HEADER *)sc->hest);
627 TAILQ_FOREACH(ge, &sc->ges, link) {
629 acpi_bus_alloc_gas(dev, &ge->res_type, &ge->res_rid,
630 &ge->v1.ErrorStatusAddress, &ge->res, 0);
632 ge->buf = pmap_mapdev_attr(READ8(ge->res, 0),
633 ge->v1.ErrorBlockLength, VM_MEMATTR_WRITE_COMBINING);
635 device_printf(dev, "Can't allocate status resource.\n");
637 if (ge->v1.Header.Type == ACPI_HEST_TYPE_GENERIC_ERROR_V2) {
638 ge->res2_rid = rid++;
639 acpi_bus_alloc_gas(dev, &ge->res2_type, &ge->res2_rid,
640 &ge->v2.ReadAckRegister, &ge->res2, 0);
641 if (ge->res2 == NULL)
642 device_printf(dev, "Can't allocate ack resource.\n");
644 if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_POLLED) {
645 callout_init(&ge->poll, 1);
646 callout_reset(&ge->poll,
647 ge->v1.Notify.PollInterval * hz / 1000,
648 apei_callout_handler, ge);
649 } else if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_NMI) {
650 ge->copybuf = malloc(ge->v1.ErrorBlockLength,
651 M_DEVBUF, M_WAITOK | M_ZERO);
652 swi_add(&clk_intr_event, "apei", apei_nmi_swi, ge,
653 SWI_CLOCK, INTR_MPSAFE, &ge->swi_ih);
655 apei_nmi = apei_nmi_handler;
659 if (acpi_get_handle(dev) != NULL) {
660 AcpiInstallNotifyHandler(acpi_get_handle(dev),
661 ACPI_DEVICE_NOTIFY, apei_notify_handler, dev);
667 apei_detach(device_t dev)
669 struct apei_softc *sc = device_get_softc(dev);
674 if (acpi_get_handle(dev) != NULL) {
675 AcpiRemoveNotifyHandler(acpi_get_handle(dev),
676 ACPI_DEVICE_NOTIFY, apei_notify_handler);
679 while ((ge = TAILQ_FIRST(&sc->ges)) != NULL) {
680 TAILQ_REMOVE(&sc->ges, ge, link);
682 bus_release_resource(dev, ge->res_type,
683 ge->res_rid, ge->res);
686 bus_release_resource(dev, ge->res2_type,
687 ge->res2_rid, ge->res2);
689 if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_POLLED) {
690 callout_drain(&ge->poll);
691 } else if (ge->v1.Notify.Type == ACPI_HEST_NOTIFY_NMI) {
692 swi_remove(&ge->swi_ih);
693 free(ge->copybuf, M_DEVBUF);
696 pmap_unmapdev((vm_offset_t)ge->buf,
697 ge->v1.ErrorBlockLength);
704 static device_method_t apei_methods[] = {
705 /* Device interface */
706 DEVMETHOD(device_identify, apei_identify),
707 DEVMETHOD(device_probe, apei_probe),
708 DEVMETHOD(device_attach, apei_attach),
709 DEVMETHOD(device_detach, apei_detach),
713 static driver_t apei_driver = {
716 sizeof(struct apei_softc),
719 DRIVER_MODULE(apei, acpi, apei_driver, apei_devclass, 0, 0);
720 MODULE_DEPEND(apei, acpi, 1, 1, 1);