1*4b159fe5Sriastradh /* $NetBSD: apei_bert.c,v 1.1 2024/03/20 17:11:43 riastradh Exp $ */
2*4b159fe5Sriastradh
3*4b159fe5Sriastradh /*-
4*4b159fe5Sriastradh * Copyright (c) 2024 The NetBSD Foundation, Inc.
5*4b159fe5Sriastradh * All rights reserved.
6*4b159fe5Sriastradh *
7*4b159fe5Sriastradh * Redistribution and use in source and binary forms, with or without
8*4b159fe5Sriastradh * modification, are permitted provided that the following conditions
9*4b159fe5Sriastradh * are met:
10*4b159fe5Sriastradh * 1. Redistributions of source code must retain the above copyright
11*4b159fe5Sriastradh * notice, this list of conditions and the following disclaimer.
12*4b159fe5Sriastradh * 2. Redistributions in binary form must reproduce the above copyright
13*4b159fe5Sriastradh * notice, this list of conditions and the following disclaimer in the
14*4b159fe5Sriastradh * documentation and/or other materials provided with the distribution.
15*4b159fe5Sriastradh *
16*4b159fe5Sriastradh * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17*4b159fe5Sriastradh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18*4b159fe5Sriastradh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19*4b159fe5Sriastradh * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20*4b159fe5Sriastradh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21*4b159fe5Sriastradh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22*4b159fe5Sriastradh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23*4b159fe5Sriastradh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24*4b159fe5Sriastradh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25*4b159fe5Sriastradh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26*4b159fe5Sriastradh * POSSIBILITY OF SUCH DAMAGE.
27*4b159fe5Sriastradh */
28*4b159fe5Sriastradh
29*4b159fe5Sriastradh /*
30*4b159fe5Sriastradh * APEI BERT -- Boot Error Record Table
31*4b159fe5Sriastradh *
32*4b159fe5Sriastradh * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#boot-error-source
33*4b159fe5Sriastradh */
34*4b159fe5Sriastradh
35*4b159fe5Sriastradh #include <sys/cdefs.h>
36*4b159fe5Sriastradh __KERNEL_RCSID(0, "$NetBSD: apei_bert.c,v 1.1 2024/03/20 17:11:43 riastradh Exp $");
37*4b159fe5Sriastradh
38*4b159fe5Sriastradh #include <sys/types.h>
39*4b159fe5Sriastradh
40*4b159fe5Sriastradh #include <sys/systm.h>
41*4b159fe5Sriastradh
42*4b159fe5Sriastradh #include <dev/acpi/acpivar.h>
43*4b159fe5Sriastradh #include <dev/acpi/apeivar.h>
44*4b159fe5Sriastradh #include <dev/acpi/apei_bertvar.h>
45*4b159fe5Sriastradh
46*4b159fe5Sriastradh #define _COMPONENT ACPI_RESOURCE_COMPONENT
47*4b159fe5Sriastradh ACPI_MODULE_NAME ("apei")
48*4b159fe5Sriastradh
49*4b159fe5Sriastradh /*
50*4b159fe5Sriastradh * apei_bert_attach(sc)
51*4b159fe5Sriastradh *
52*4b159fe5Sriastradh * Scan the Boot Error Record Table for hardware errors that
53*4b159fe5Sriastradh * happened early at boot or on the previous boot.
54*4b159fe5Sriastradh */
55*4b159fe5Sriastradh void
apei_bert_attach(struct apei_softc * sc)56*4b159fe5Sriastradh apei_bert_attach(struct apei_softc *sc)
57*4b159fe5Sriastradh {
58*4b159fe5Sriastradh const ACPI_TABLE_BERT *bert = sc->sc_tab.bert;
59*4b159fe5Sriastradh struct apei_bert_softc *bsc = &sc->sc_bert;
60*4b159fe5Sriastradh bool fatal = false;
61*4b159fe5Sriastradh
62*4b159fe5Sriastradh /*
63*4b159fe5Sriastradh * Verify the table is large enough.
64*4b159fe5Sriastradh */
65*4b159fe5Sriastradh if (bert->Header.Length < sizeof(*bert)) {
66*4b159fe5Sriastradh aprint_error_dev(sc->sc_dev, "BERT: truncated table:"
67*4b159fe5Sriastradh " %"PRIu32" < %zu bytes\n",
68*4b159fe5Sriastradh bert->Header.Length, sizeof(*bert));
69*4b159fe5Sriastradh return;
70*4b159fe5Sriastradh }
71*4b159fe5Sriastradh
72*4b159fe5Sriastradh /*
73*4b159fe5Sriastradh * In verbose boots, print the BERT physical address and
74*4b159fe5Sriastradh * length. The operator might find this handy for dd'ing it
75*4b159fe5Sriastradh * from /dev/mem, if allowed.
76*4b159fe5Sriastradh */
77*4b159fe5Sriastradh aprint_verbose_dev(sc->sc_dev, "BERT: 0x%x bytes at 0x%"PRIx64"\n",
78*4b159fe5Sriastradh bert->RegionLength, bert->Address);
79*4b159fe5Sriastradh
80*4b159fe5Sriastradh /*
81*4b159fe5Sriastradh * Verify the length is enough for a Generic Error Status Block
82*4b159fe5Sriastradh * header, at least.
83*4b159fe5Sriastradh */
84*4b159fe5Sriastradh if (bert->RegionLength < sizeof(*bsc->bsc_gesb)) {
85*4b159fe5Sriastradh aprint_error_dev(sc->sc_dev,
86*4b159fe5Sriastradh "BERT: truncated boot error region, %"PRIu32" < %zu bytes",
87*4b159fe5Sriastradh bert->RegionLength, sizeof(*bsc->bsc_gesb));
88*4b159fe5Sriastradh return;
89*4b159fe5Sriastradh }
90*4b159fe5Sriastradh
91*4b159fe5Sriastradh /*
92*4b159fe5Sriastradh * Map the GESB and process it, but don't acknowledge it --
93*4b159fe5Sriastradh * this is a one-time polled source; it won't (or at least,
94*4b159fe5Sriastradh * shouldn't) change after boot.
95*4b159fe5Sriastradh */
96*4b159fe5Sriastradh bsc->bsc_gesb = AcpiOsMapMemory(bert->Address, bert->RegionLength);
97*4b159fe5Sriastradh const uint32_t status = apei_gesb_report(sc, bsc->bsc_gesb,
98*4b159fe5Sriastradh bert->RegionLength, "boot error record", &fatal);
99*4b159fe5Sriastradh if (status == 0) {
100*4b159fe5Sriastradh /*
101*4b159fe5Sriastradh * If there were no boot errors, leave a note in dmesg
102*4b159fe5Sriastradh * to this effect without cluttering up the console
103*4b159fe5Sriastradh * unless you asked for it by `boot -v'.
104*4b159fe5Sriastradh */
105*4b159fe5Sriastradh aprint_verbose_dev(sc->sc_dev,
106*4b159fe5Sriastradh "BERT: no boot errors recorded\n");
107*4b159fe5Sriastradh }
108*4b159fe5Sriastradh
109*4b159fe5Sriastradh /*
110*4b159fe5Sriastradh * If the error was fatal, print a warning to the console.
111*4b159fe5Sriastradh * Probably not actually fatal now since it is usually related
112*4b159fe5Sriastradh * to early or previous boot.
113*4b159fe5Sriastradh */
114*4b159fe5Sriastradh if (fatal) {
115*4b159fe5Sriastradh aprint_error_dev(sc->sc_dev, "BERT:"
116*4b159fe5Sriastradh " fatal pre-boot error recorded\n");
117*4b159fe5Sriastradh }
118*4b159fe5Sriastradh
119*4b159fe5Sriastradh /* XXX expose content via sysctl? */
120*4b159fe5Sriastradh }
121*4b159fe5Sriastradh
122*4b159fe5Sriastradh /*
123*4b159fe5Sriastradh * apei_bert_detach(sc)
124*4b159fe5Sriastradh *
125*4b159fe5Sriastradh * Free any software resources associated with the Boot Error
126*4b159fe5Sriastradh * Record Table.
127*4b159fe5Sriastradh */
128*4b159fe5Sriastradh void
apei_bert_detach(struct apei_softc * sc)129*4b159fe5Sriastradh apei_bert_detach(struct apei_softc *sc)
130*4b159fe5Sriastradh {
131*4b159fe5Sriastradh const ACPI_TABLE_BERT *bert = sc->sc_tab.bert;
132*4b159fe5Sriastradh struct apei_bert_softc *bsc = &sc->sc_bert;
133*4b159fe5Sriastradh
134*4b159fe5Sriastradh if (bsc->bsc_gesb) {
135*4b159fe5Sriastradh AcpiOsUnmapMemory(bsc->bsc_gesb, bert->RegionLength);
136*4b159fe5Sriastradh bsc->bsc_gesb = NULL;
137*4b159fe5Sriastradh }
138*4b159fe5Sriastradh }
139