apei_einj.c revision 1.2 1 1.2 riastrad /* $NetBSD: apei_einj.c,v 1.2 2024/03/21 02:34:59 riastradh Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*-
4 1.1 riastrad * Copyright (c) 2024 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * Redistribution and use in source and binary forms, with or without
8 1.1 riastrad * modification, are permitted provided that the following conditions
9 1.1 riastrad * are met:
10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
11 1.1 riastrad * notice, this list of conditions and the following disclaimer.
12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
14 1.1 riastrad * documentation and/or other materials provided with the distribution.
15 1.1 riastrad *
16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad /*
30 1.1 riastrad * APEI EINJ -- Error Injection Table
31 1.1 riastrad *
32 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#error-injection
33 1.1 riastrad *
34 1.1 riastrad * XXX Consider a /dev node with ioctls for error injection rather than
35 1.1 riastrad * the somewhat kooky sysctl interface. By representing an error
36 1.1 riastrad * injection request in a structure, we can serialize access to the
37 1.1 riastrad * platform's EINJ operational context. However, this also requires
38 1.1 riastrad * some nontrivial userland support; maybe relying on the user to tread
39 1.1 riastrad * carefully with error injection is fine -- after all, many types of
40 1.1 riastrad * error injection will cause a system halt/panic.
41 1.1 riastrad */
42 1.1 riastrad
43 1.1 riastrad #include <sys/cdefs.h>
44 1.2 riastrad __KERNEL_RCSID(0, "$NetBSD: apei_einj.c,v 1.2 2024/03/21 02:34:59 riastradh Exp $");
45 1.1 riastrad
46 1.1 riastrad #include <sys/types.h>
47 1.1 riastrad
48 1.1 riastrad #include <sys/device.h>
49 1.1 riastrad #include <sys/sysctl.h>
50 1.1 riastrad #include <sys/systm.h>
51 1.1 riastrad
52 1.1 riastrad #include <dev/acpi/acpivar.h>
53 1.1 riastrad #include <dev/acpi/apei_einjvar.h>
54 1.1 riastrad #include <dev/acpi/apei_interp.h>
55 1.1 riastrad #include <dev/acpi/apei_reg.h>
56 1.1 riastrad #include <dev/acpi/apeivar.h>
57 1.1 riastrad
58 1.1 riastrad #include "ioconf.h"
59 1.1 riastrad
60 1.1 riastrad #define _COMPONENT ACPI_RESOURCE_COMPONENT
61 1.1 riastrad ACPI_MODULE_NAME ("apei")
62 1.1 riastrad
63 1.1 riastrad static void apei_einj_instfunc(ACPI_WHEA_HEADER *, void *, uint32_t *,
64 1.1 riastrad uint32_t);
65 1.1 riastrad static uint64_t apei_einj_act(struct apei_softc *, enum AcpiEinjActions,
66 1.1 riastrad uint64_t);
67 1.1 riastrad static uint64_t apei_einj_trigger(struct apei_softc *, uint64_t);
68 1.1 riastrad static int apei_einj_action_sysctl(SYSCTLFN_ARGS);
69 1.1 riastrad static int apei_einj_trigger_sysctl(SYSCTLFN_ARGS);
70 1.1 riastrad static int apei_einj_types_sysctl(SYSCTLFN_ARGS);
71 1.1 riastrad
72 1.1 riastrad /*
73 1.1 riastrad * apei_einj_action
74 1.1 riastrad *
75 1.1 riastrad * Symbolic names of the APEI EINJ (Error Injection) logical actions
76 1.1 riastrad * are taken (and downcased) from:
77 1.1 riastrad *
78 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#error-injection-actions
79 1.1 riastrad */
80 1.1 riastrad static const char *const apei_einj_action[] = {
81 1.1 riastrad [ACPI_EINJ_BEGIN_OPERATION] = "begin_injection_operation",
82 1.1 riastrad [ACPI_EINJ_GET_TRIGGER_TABLE] = "get_trigger_error_action_table",
83 1.1 riastrad [ACPI_EINJ_SET_ERROR_TYPE] = "set_error_type",
84 1.1 riastrad [ACPI_EINJ_GET_ERROR_TYPE] = "get_error_type",
85 1.1 riastrad [ACPI_EINJ_END_OPERATION] = "end_operation",
86 1.1 riastrad [ACPI_EINJ_EXECUTE_OPERATION] = "execute_operation",
87 1.1 riastrad [ACPI_EINJ_CHECK_BUSY_STATUS] = "check_busy_status",
88 1.1 riastrad [ACPI_EINJ_GET_COMMAND_STATUS] = "get_command_status",
89 1.1 riastrad [ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS] = "set_error_type_with_address",
90 1.1 riastrad [ACPI_EINJ_GET_EXECUTE_TIMINGS] = "get_execute_operation_timings",
91 1.1 riastrad };
92 1.1 riastrad
93 1.1 riastrad /*
94 1.1 riastrad * apei_einj_instruction
95 1.1 riastrad *
96 1.1 riastrad * Symbolic names of the APEI EINJ (Error Injection) instructions to
97 1.1 riastrad * implement logical actions are taken (and downcased) from:
98 1.1 riastrad *
99 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#injection-instructions-table
100 1.1 riastrad */
101 1.1 riastrad
102 1.1 riastrad static const char *const apei_einj_instruction[] = {
103 1.1 riastrad [ACPI_EINJ_READ_REGISTER] = "read_register",
104 1.1 riastrad [ACPI_EINJ_READ_REGISTER_VALUE] = "read_register",
105 1.1 riastrad [ACPI_EINJ_WRITE_REGISTER] = "write_register",
106 1.1 riastrad [ACPI_EINJ_WRITE_REGISTER_VALUE] = "write_register_value",
107 1.1 riastrad [ACPI_EINJ_NOOP] = "noop",
108 1.1 riastrad };
109 1.1 riastrad
110 1.1 riastrad /*
111 1.1 riastrad * apei_einj_attach(sc)
112 1.1 riastrad *
113 1.1 riastrad * Scan the Error Injection table to ascertain what error
114 1.1 riastrad * injection actions the firmware supports and how to perform
115 1.1 riastrad * them. Create sysctl nodes for triggering error injection.
116 1.1 riastrad */
117 1.1 riastrad void
118 1.1 riastrad apei_einj_attach(struct apei_softc *sc)
119 1.1 riastrad {
120 1.1 riastrad ACPI_TABLE_EINJ *einj = sc->sc_tab.einj;
121 1.1 riastrad struct apei_einj_softc *jsc = &sc->sc_einj;
122 1.1 riastrad ACPI_EINJ_ENTRY *entry;
123 1.1 riastrad const struct sysctlnode *sysctl_einj;
124 1.1 riastrad const struct sysctlnode *sysctl_einj_action;
125 1.1 riastrad uint32_t i, nentries, maxnentries;
126 1.1 riastrad unsigned action;
127 1.1 riastrad int error;
128 1.1 riastrad
129 1.1 riastrad /*
130 1.1 riastrad * Verify the table length, table header length, and
131 1.1 riastrad * instruction entry count are all sensible. If the header is
132 1.1 riastrad * truncated, stop here; if the entries are truncated, stop at
133 1.1 riastrad * the largest integral number of full entries that fits.
134 1.1 riastrad */
135 1.1 riastrad if (einj->Header.Length < sizeof(*einj)) {
136 1.1 riastrad aprint_error_dev(sc->sc_dev, "EINJ: truncated table:"
137 1.1 riastrad " %"PRIu32" < %zu minimum bytes\n",
138 1.1 riastrad einj->Header.Length, sizeof(*einj));
139 1.1 riastrad return;
140 1.1 riastrad }
141 1.1 riastrad if (einj->HeaderLength <
142 1.1 riastrad sizeof(*einj) - offsetof(ACPI_TABLE_EINJ, HeaderLength)) {
143 1.1 riastrad aprint_error_dev(sc->sc_dev, "EINJ: truncated header:"
144 1.1 riastrad " %"PRIu32" < %zu bytes\n",
145 1.1 riastrad einj->HeaderLength,
146 1.1 riastrad sizeof(*einj) - offsetof(ACPI_TABLE_EINJ, HeaderLength));
147 1.1 riastrad return;
148 1.1 riastrad }
149 1.1 riastrad nentries = einj->Entries;
150 1.1 riastrad maxnentries = (einj->Header.Length - sizeof(*einj))/sizeof(*entry);
151 1.1 riastrad if (nentries > maxnentries) {
152 1.1 riastrad aprint_error_dev(sc->sc_dev, "EINJ: excessive entries:"
153 1.1 riastrad " %"PRIu32", truncating to %"PRIu32"\n",
154 1.1 riastrad nentries, maxnentries);
155 1.1 riastrad nentries = maxnentries;
156 1.1 riastrad }
157 1.1 riastrad if (nentries*sizeof(*entry) < einj->Header.Length - sizeof(*einj)) {
158 1.1 riastrad aprint_error_dev(sc->sc_dev, "EINJ:"
159 1.1 riastrad " %zu bytes of trailing garbage after last entry\n",
160 1.1 riastrad einj->Header.Length - nentries*sizeof(*entry));
161 1.1 riastrad }
162 1.1 riastrad
163 1.1 riastrad /*
164 1.1 riastrad * Create sysctl hw.acpi.apei.einj for all EINJ-related knobs.
165 1.1 riastrad */
166 1.1 riastrad error = sysctl_createv(&sc->sc_sysctllog, 0,
167 1.1 riastrad &sc->sc_sysctlroot, &sysctl_einj, 0,
168 1.1 riastrad CTLTYPE_NODE, "einj",
169 1.1 riastrad SYSCTL_DESCR("Error injection"),
170 1.1 riastrad NULL, 0, NULL, 0,
171 1.1 riastrad CTL_CREATE, CTL_EOL);
172 1.1 riastrad if (error) {
173 1.1 riastrad aprint_error_dev(sc->sc_dev, "failed to create"
174 1.1 riastrad " hw.acpi.apei.einj: %d\n", error);
175 1.1 riastrad sysctl_einj = NULL;
176 1.1 riastrad }
177 1.1 riastrad
178 1.1 riastrad /*
179 1.1 riastrad * Create an interpreter for EINJ actions.
180 1.1 riastrad */
181 1.1 riastrad jsc->jsc_interp = apei_interp_create("EINJ",
182 1.1 riastrad apei_einj_action, __arraycount(apei_einj_action),
183 1.1 riastrad apei_einj_instruction, __arraycount(apei_einj_instruction),
184 1.1 riastrad /*instvalid*/NULL, apei_einj_instfunc);
185 1.1 riastrad
186 1.1 riastrad /*
187 1.1 riastrad * Compile the interpreter from the EINJ action instruction
188 1.1 riastrad * table.
189 1.1 riastrad */
190 1.1 riastrad entry = (ACPI_EINJ_ENTRY *)(einj + 1);
191 1.1 riastrad for (i = 0; i < nentries; i++, entry++)
192 1.1 riastrad apei_interp_pass1_load(jsc->jsc_interp, i, &entry->WheaHeader);
193 1.1 riastrad entry = (ACPI_EINJ_ENTRY *)(einj + 1);
194 1.1 riastrad for (i = 0; i < nentries; i++, entry++) {
195 1.1 riastrad apei_interp_pass2_verify(jsc->jsc_interp, i,
196 1.1 riastrad &entry->WheaHeader);
197 1.1 riastrad }
198 1.1 riastrad apei_interp_pass3_alloc(jsc->jsc_interp);
199 1.1 riastrad entry = (ACPI_EINJ_ENTRY *)(einj + 1);
200 1.1 riastrad for (i = 0; i < nentries; i++, entry++) {
201 1.1 riastrad apei_interp_pass4_assemble(jsc->jsc_interp, i,
202 1.1 riastrad &entry->WheaHeader);
203 1.1 riastrad }
204 1.1 riastrad apei_interp_pass5_verify(jsc->jsc_interp);
205 1.1 riastrad
206 1.1 riastrad /*
207 1.1 riastrad * Create sysctl hw.acpi.apei.einj.action for individual actions.
208 1.1 riastrad */
209 1.1 riastrad error = sysctl_einj == NULL ? ENOENT :
210 1.1 riastrad sysctl_createv(&sc->sc_sysctllog, 0,
211 1.1 riastrad &sysctl_einj, &sysctl_einj_action, 0,
212 1.1 riastrad CTLTYPE_NODE, "action",
213 1.1 riastrad SYSCTL_DESCR("EINJ actions"),
214 1.1 riastrad NULL, 0, NULL, 0,
215 1.1 riastrad CTL_CREATE, CTL_EOL);
216 1.1 riastrad if (error) {
217 1.1 riastrad aprint_error_dev(sc->sc_dev, "failed to create"
218 1.1 riastrad " hw.acpi.apei.einj.action: %d\n", error);
219 1.1 riastrad sysctl_einj_action = NULL;
220 1.1 riastrad }
221 1.1 riastrad
222 1.1 riastrad /*
223 1.1 riastrad * Create sysctl nodes for each action we know about.
224 1.1 riastrad */
225 1.1 riastrad for (action = 0; action < __arraycount(apei_einj_action); action++) {
226 1.1 riastrad if (apei_einj_action[action] == NULL)
227 1.1 riastrad continue;
228 1.1 riastrad
229 1.1 riastrad /*
230 1.1 riastrad * Check to see if there are any instructions for this
231 1.1 riastrad * action.
232 1.1 riastrad *
233 1.1 riastrad * XXX Maybe add this to the apei_interp.h abstraction.
234 1.1 riastrad */
235 1.1 riastrad entry = (ACPI_EINJ_ENTRY *)(einj + 1);
236 1.1 riastrad for (i = 0; i < nentries; i++, entry++) {
237 1.1 riastrad ACPI_WHEA_HEADER *const header = &entry->WheaHeader;
238 1.1 riastrad
239 1.1 riastrad if (action == header->Action)
240 1.1 riastrad break;
241 1.1 riastrad }
242 1.1 riastrad if (i == nentries) {
243 1.1 riastrad /*
244 1.1 riastrad * No instructions for this action, so assume
245 1.1 riastrad * it's not supported.
246 1.1 riastrad */
247 1.1 riastrad continue;
248 1.1 riastrad }
249 1.1 riastrad
250 1.1 riastrad /*
251 1.1 riastrad * Create a sysctl knob to perform the action.
252 1.1 riastrad */
253 1.1 riastrad error = sysctl_einj_action == NULL ? ENOENT :
254 1.1 riastrad sysctl_createv(&sc->sc_sysctllog, 0,
255 1.1 riastrad &sysctl_einj_action, NULL, CTLFLAG_READWRITE,
256 1.1 riastrad CTLTYPE_QUAD, apei_einj_action[action],
257 1.1 riastrad NULL, /* description */
258 1.1 riastrad &apei_einj_action_sysctl, 0, NULL, 0,
259 1.1 riastrad action, CTL_EOL);
260 1.1 riastrad if (error) {
261 1.1 riastrad aprint_error_dev(sc->sc_dev, "failed to create"
262 1.1 riastrad " sysctl hw.acpi.apei.einj.action.%s: %d\n",
263 1.1 riastrad apei_einj_action[action], error);
264 1.1 riastrad continue;
265 1.1 riastrad }
266 1.1 riastrad }
267 1.1 riastrad
268 1.1 riastrad /*
269 1.1 riastrad * Create a sysctl knob to trigger error.
270 1.1 riastrad */
271 1.1 riastrad error = sysctl_einj == NULL ? ENOENT :
272 1.1 riastrad sysctl_createv(&sc->sc_sysctllog, 0,
273 1.1 riastrad &sysctl_einj, NULL, CTLFLAG_READWRITE,
274 1.1 riastrad CTLTYPE_QUAD, "trigger",
275 1.1 riastrad NULL, /* description */
276 1.1 riastrad &apei_einj_trigger_sysctl, 0, NULL, 0,
277 1.1 riastrad CTL_CREATE, CTL_EOL);
278 1.1 riastrad if (error) {
279 1.1 riastrad aprint_error_dev(sc->sc_dev, "failed to create"
280 1.1 riastrad " sysctl hw.acpi.apei.einj.trigger: %d\n",
281 1.1 riastrad error);
282 1.1 riastrad }
283 1.1 riastrad
284 1.1 riastrad /*
285 1.1 riastrad * Query the available types of error to inject and print it to
286 1.1 riastrad * dmesg.
287 1.1 riastrad *
288 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#error-types
289 1.1 riastrad */
290 1.1 riastrad uint64_t types = apei_einj_act(sc, ACPI_EINJ_GET_ERROR_TYPE, 0);
291 1.1 riastrad char typesbuf[1024], *typesp;
292 1.1 riastrad /* XXX define this format somewhere */
293 1.1 riastrad snprintb_m(typesbuf, sizeof(typesbuf), "\177\020"
294 1.1 riastrad "b\000" "PROC_CORRECTABLE\0"
295 1.1 riastrad "b\001" "PROC_UNCORRECTABLE\0"
296 1.1 riastrad "b\002" "PROC_FATAL\0"
297 1.1 riastrad "b\003" "MEM_CORRECTABLE\0"
298 1.1 riastrad "b\004" "MEM_UNCORRECTABLE\0"
299 1.1 riastrad "b\005" "MEM_FATAL\0"
300 1.1 riastrad "b\006" "PCIE_CORRECTABLE\0"
301 1.1 riastrad "b\007" "PCIE_UNCORRECTABLE\0"
302 1.1 riastrad "b\010" "PCIE_FATAL\0"
303 1.1 riastrad "b\011" "PLAT_CORRECTABLE\0"
304 1.1 riastrad "b\012" "PLAT_UNCORRECTABLE\0"
305 1.1 riastrad "b\013" "PLAT_FATAL\0"
306 1.1 riastrad "b\014" "CXLCACHE_CORRECTABLE\0"
307 1.1 riastrad "b\015" "CXLCACHE_UNCORRECTABLE\0"
308 1.1 riastrad "b\016" "CXLCACHE_FATAL\0"
309 1.1 riastrad "b\017" "CXLMEM_CORRECTABLE\0"
310 1.1 riastrad "b\020" "CXLMEM_UNCORRECTABLE\0"
311 1.1 riastrad "b\021" "CXLMEM_FATAL\0"
312 1.1 riastrad // "f\022\014" "reserved\0"
313 1.1 riastrad "b\036" "EINJv2\0"
314 1.1 riastrad "b\037" "VENDOR\0"
315 1.1 riastrad "\0", types, 36);
316 1.1 riastrad for (typesp = typesbuf; strlen(typesp); typesp += strlen(typesp) + 1) {
317 1.1 riastrad aprint_normal_dev(sc->sc_dev, "EINJ: can inject:"
318 1.1 riastrad " %s\n", typesp);
319 1.1 riastrad }
320 1.1 riastrad
321 1.1 riastrad /*
322 1.1 riastrad * Create a sysctl knob to query the available types of error
323 1.1 riastrad * to inject. In principle this could change dynamically, so
324 1.1 riastrad * we'll make it dynamic.
325 1.1 riastrad */
326 1.1 riastrad error = sysctl_einj == NULL ? ENOENT :
327 1.1 riastrad sysctl_createv(&sc->sc_sysctllog, 0,
328 1.1 riastrad &sysctl_einj, NULL, 0,
329 1.1 riastrad CTLTYPE_QUAD, "types",
330 1.1 riastrad SYSCTL_DESCR("Types of errors that can be injected"),
331 1.1 riastrad &apei_einj_types_sysctl, 0, NULL, 0,
332 1.1 riastrad CTL_CREATE, CTL_EOL);
333 1.1 riastrad if (error) {
334 1.1 riastrad aprint_error_dev(sc->sc_dev, "failed to create"
335 1.1 riastrad " sysctl hw.acpi.apei.einj.types: %d\n",
336 1.1 riastrad error);
337 1.1 riastrad }
338 1.1 riastrad }
339 1.1 riastrad
340 1.1 riastrad /*
341 1.1 riastrad * apei_einj_detach(sc)
342 1.1 riastrad *
343 1.1 riastrad * Free any software resources associated with the Error Injection
344 1.1 riastrad * table.
345 1.1 riastrad */
346 1.1 riastrad void
347 1.1 riastrad apei_einj_detach(struct apei_softc *sc)
348 1.1 riastrad {
349 1.1 riastrad struct apei_einj_softc *jsc = &sc->sc_einj;
350 1.1 riastrad
351 1.1 riastrad if (jsc->jsc_interp) {
352 1.1 riastrad apei_interp_destroy(jsc->jsc_interp);
353 1.1 riastrad jsc->jsc_interp = NULL;
354 1.1 riastrad }
355 1.1 riastrad }
356 1.1 riastrad
357 1.1 riastrad /*
358 1.1 riastrad * struct apei_einj_machine
359 1.1 riastrad *
360 1.1 riastrad * Machine state for executing EINJ instructions.
361 1.1 riastrad */
362 1.1 riastrad struct apei_einj_machine {
363 1.1 riastrad struct apei_softc *sc;
364 1.1 riastrad uint64_t x; /* in */
365 1.1 riastrad uint64_t y; /* out */
366 1.1 riastrad };
367 1.1 riastrad
368 1.1 riastrad /*
369 1.1 riastrad * apei_einj_instfunc(header, cookie, &ip, maxip)
370 1.1 riastrad *
371 1.1 riastrad * Run a single instruction in the service of performing an EINJ
372 1.1 riastrad * action. Updates the EINJ machine at cookie in place.
373 1.1 riastrad *
374 1.1 riastrad * This doesn't read or write ip. The TRIGGER_ERROR logic relies
375 1.1 riastrad * on this; if you change the fact, you must update that logic
376 1.1 riastrad * too.
377 1.1 riastrad */
378 1.1 riastrad static void
379 1.1 riastrad apei_einj_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
380 1.1 riastrad uint32_t maxip)
381 1.1 riastrad {
382 1.1 riastrad struct apei_einj_machine *M = cookie;
383 1.1 riastrad ACPI_STATUS rv = AE_OK;
384 1.1 riastrad
385 1.1 riastrad /*
386 1.1 riastrad * Abbreviate some of the intermediate quantities to make the
387 1.1 riastrad * instruction logic conciser and more legible.
388 1.1 riastrad */
389 1.1 riastrad const uint8_t BitOffset = header->RegisterRegion.BitOffset;
390 1.1 riastrad const uint64_t Mask = header->Mask;
391 1.1 riastrad const uint64_t Value = header->Value;
392 1.1 riastrad ACPI_GENERIC_ADDRESS *const reg = &header->RegisterRegion;
393 1.1 riastrad const bool preserve_register = header->Flags & ACPI_EINJ_PRESERVE;
394 1.1 riastrad
395 1.1 riastrad aprint_debug_dev(M->sc->sc_dev, "%s: instr=0x%02"PRIx8
396 1.1 riastrad " (%s)"
397 1.1 riastrad " Address=0x%"PRIx64
398 1.1 riastrad " BitOffset=%"PRIu8" Mask=0x%"PRIx64" Value=0x%"PRIx64
399 1.1 riastrad " Flags=0x%"PRIx8"\n",
400 1.1 riastrad __func__, header->Instruction,
401 1.1 riastrad (header->Instruction < __arraycount(apei_einj_instruction)
402 1.1 riastrad ? apei_einj_instruction[header->Instruction]
403 1.1 riastrad : "unknown"),
404 1.1 riastrad reg->Address,
405 1.1 riastrad BitOffset, Mask, Value,
406 1.1 riastrad header->Flags);
407 1.1 riastrad
408 1.1 riastrad /*
409 1.1 riastrad * Zero-initialize the output by default.
410 1.1 riastrad */
411 1.1 riastrad M->y = 0;
412 1.1 riastrad
413 1.1 riastrad /*
414 1.1 riastrad * Dispatch the instruction.
415 1.1 riastrad */
416 1.1 riastrad switch (header->Instruction) {
417 1.1 riastrad case ACPI_EINJ_READ_REGISTER:
418 1.1 riastrad rv = apei_read_register(reg, Mask, &M->y);
419 1.1 riastrad if (ACPI_FAILURE(rv))
420 1.1 riastrad break;
421 1.1 riastrad break;
422 1.1 riastrad case ACPI_EINJ_READ_REGISTER_VALUE: {
423 1.1 riastrad uint64_t v;
424 1.1 riastrad
425 1.1 riastrad rv = apei_read_register(reg, Mask, &v);
426 1.1 riastrad if (ACPI_FAILURE(rv))
427 1.1 riastrad break;
428 1.1 riastrad M->y = (v == Value ? 1 : 0);
429 1.1 riastrad break;
430 1.1 riastrad }
431 1.1 riastrad case ACPI_EINJ_WRITE_REGISTER:
432 1.1 riastrad rv = apei_write_register(reg, Mask, preserve_register, M->x);
433 1.1 riastrad break;
434 1.1 riastrad case ACPI_EINJ_WRITE_REGISTER_VALUE:
435 1.1 riastrad rv = apei_write_register(reg, Mask, preserve_register, Value);
436 1.1 riastrad break;
437 1.1 riastrad case ACPI_EINJ_NOOP:
438 1.1 riastrad break;
439 1.1 riastrad default:
440 1.1 riastrad rv = AE_ERROR;
441 1.1 riastrad break;
442 1.1 riastrad }
443 1.1 riastrad
444 1.1 riastrad /*
445 1.1 riastrad * If any register I/O failed, print the failure message. This
446 1.1 riastrad * could be more specific about exactly what failed, but that
447 1.1 riastrad * takes a little more effort to write.
448 1.1 riastrad */
449 1.1 riastrad if (ACPI_FAILURE(rv)) {
450 1.1 riastrad aprint_debug_dev(M->sc->sc_dev, "%s: failed: %s\n", __func__,
451 1.1 riastrad AcpiFormatException(rv));
452 1.1 riastrad }
453 1.1 riastrad }
454 1.1 riastrad
455 1.1 riastrad /*
456 1.1 riastrad * apei_einj_act(sc, action, x)
457 1.1 riastrad *
458 1.1 riastrad * Perform the named EINJ action with input x, by executing the
459 1.1 riastrad * instruction defined for the action by the EINJ, and return the
460 1.1 riastrad * output.
461 1.1 riastrad */
462 1.1 riastrad static uint64_t
463 1.1 riastrad apei_einj_act(struct apei_softc *sc, enum AcpiEinjActions action,
464 1.1 riastrad uint64_t x)
465 1.1 riastrad {
466 1.1 riastrad struct apei_einj_softc *const jsc = &sc->sc_einj;
467 1.1 riastrad struct apei_einj_machine einj_machine, *const M = &einj_machine;
468 1.1 riastrad
469 1.1 riastrad aprint_debug_dev(sc->sc_dev, "%s: action=%d (%s) input=0x%"PRIx64"\n",
470 1.1 riastrad __func__,
471 1.1 riastrad action,
472 1.1 riastrad (action < __arraycount(apei_einj_action)
473 1.1 riastrad ? apei_einj_action[action]
474 1.1 riastrad : "unknown"),
475 1.1 riastrad x);
476 1.1 riastrad
477 1.1 riastrad /*
478 1.1 riastrad * Initialize the machine to execute the action's instructions.
479 1.1 riastrad */
480 1.1 riastrad memset(M, 0, sizeof(*M));
481 1.1 riastrad M->sc = sc;
482 1.1 riastrad M->x = x; /* input */
483 1.1 riastrad M->y = 0; /* output */
484 1.1 riastrad
485 1.1 riastrad /*
486 1.1 riastrad * Run the interpreter.
487 1.1 riastrad */
488 1.1 riastrad apei_interpret(jsc->jsc_interp, action, M);
489 1.1 riastrad
490 1.1 riastrad /*
491 1.1 riastrad * Return the result.
492 1.1 riastrad */
493 1.1 riastrad aprint_debug_dev(sc->sc_dev, "%s: output=0x%"PRIx64"\n", __func__,
494 1.1 riastrad M->y);
495 1.1 riastrad return M->y;
496 1.1 riastrad }
497 1.1 riastrad
498 1.1 riastrad /*
499 1.1 riastrad * apei_einj_trigger(sc, x)
500 1.1 riastrad *
501 1.1 riastrad * Obtain the TRIGGER_ERROR action table and, if there is anything
502 1.1 riastrad * to be done with it, execute it with input x and return the
503 1.1 riastrad * output. If nothing is to be done, return 0.
504 1.1 riastrad */
505 1.1 riastrad static uint64_t
506 1.1 riastrad apei_einj_trigger(struct apei_softc *sc, uint64_t x)
507 1.1 riastrad {
508 1.1 riastrad uint64_t teatab_pa;
509 1.1 riastrad ACPI_EINJ_TRIGGER *teatab = NULL;
510 1.2 riastrad size_t mapsize = 0, tabsize, bodysize;
511 1.1 riastrad ACPI_EINJ_ENTRY *entry;
512 1.1 riastrad struct apei_einj_machine einj_machine, *const M = &einj_machine;
513 1.1 riastrad uint32_t i, nentries;
514 1.1 riastrad
515 1.1 riastrad /*
516 1.1 riastrad * Get the TRIGGER_ERROR action table's physical address.
517 1.1 riastrad */
518 1.1 riastrad teatab_pa = apei_einj_act(sc, ACPI_EINJ_GET_TRIGGER_TABLE, 0);
519 1.1 riastrad
520 1.1 riastrad /*
521 1.1 riastrad * Map just the header. We don't know how large the table is
522 1.1 riastrad * because we get that from the header.
523 1.1 riastrad */
524 1.1 riastrad mapsize = sizeof(*teatab);
525 1.1 riastrad teatab = AcpiOsMapMemory(teatab_pa, mapsize);
526 1.1 riastrad
527 1.1 riastrad /*
528 1.1 riastrad * If there's no entries, stop here -- nothing to do separately
529 1.1 riastrad * to trigger an error report.
530 1.1 riastrad */
531 1.1 riastrad nentries = teatab->EntryCount;
532 1.1 riastrad if (nentries == 0)
533 1.1 riastrad goto out;
534 1.1 riastrad
535 1.1 riastrad /*
536 1.1 riastrad * If the header size or the table size is nonsense, bail.
537 1.1 riastrad */
538 1.1 riastrad if (teatab->HeaderSize < sizeof(*teatab) ||
539 1.1 riastrad teatab->TableSize < teatab->HeaderSize) {
540 1.1 riastrad device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
541 1.1 riastrad " invalid sizes:"
542 1.1 riastrad " HeaderSize=%"PRIu32" TableSize=%"PRIu32"\n",
543 1.1 riastrad teatab->HeaderSize, teatab->TableSize);
544 1.1 riastrad }
545 1.1 riastrad
546 1.1 riastrad /*
547 1.1 riastrad * If the revision is nonzero, we don't know what to do. I've
548 1.1 riastrad * only seen revision zero so far, and the spec doesn't say
549 1.1 riastrad * anything about revisions that I've found.
550 1.1 riastrad */
551 1.1 riastrad if (teatab->Revision != 0) {
552 1.1 riastrad device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
553 1.1 riastrad " unknown revision: %"PRIx32"\n", teatab->Revision);
554 1.1 riastrad goto out;
555 1.1 riastrad }
556 1.1 riastrad
557 1.1 riastrad /*
558 1.1 riastrad * Truncate the table to the number of entries requested and
559 1.1 riastrad * ignore trailing garbage if the table is long, or round the
560 1.1 riastrad * number of entries down to what fits in the table if the
561 1.1 riastrad * table is short.
562 1.1 riastrad */
563 1.1 riastrad tabsize = teatab->TableSize;
564 1.2 riastrad bodysize = tabsize - teatab->HeaderSize;
565 1.2 riastrad if (nentries < howmany(bodysize, sizeof(ACPI_EINJ_ENTRY))) {
566 1.1 riastrad device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
567 1.1 riastrad " %zu bytes of trailing garbage\n",
568 1.1 riastrad tabsize - nentries*sizeof(ACPI_EINJ_ENTRY));
569 1.2 riastrad bodysize = nentries*sizeof(ACPI_EINJ_ENTRY);
570 1.2 riastrad tabsize = teatab->HeaderSize + bodysize;
571 1.2 riastrad } else if (nentries > howmany(bodysize, sizeof(ACPI_EINJ_ENTRY))) {
572 1.1 riastrad device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
573 1.1 riastrad " truncated to %zu entries\n",
574 1.1 riastrad nentries*sizeof(ACPI_EINJ_ENTRY));
575 1.2 riastrad nentries = howmany(bodysize, sizeof(ACPI_EINJ_ENTRY));
576 1.2 riastrad bodysize = nentries*sizeof(ACPI_EINJ_ENTRY);
577 1.2 riastrad tabsize = teatab->HeaderSize + bodysize;
578 1.1 riastrad }
579 1.1 riastrad
580 1.1 riastrad /*
581 1.1 riastrad * Unmap the header and map the whole table instead.
582 1.1 riastrad */
583 1.1 riastrad AcpiOsUnmapMemory(teatab, mapsize);
584 1.1 riastrad mapsize = tabsize;
585 1.1 riastrad teatab = AcpiOsMapMemory(teatab_pa, mapsize);
586 1.1 riastrad
587 1.1 riastrad /*
588 1.1 riastrad * Initialize the machine to execute the TRIGGER_ERROR action's
589 1.1 riastrad * instructions.
590 1.1 riastrad */
591 1.1 riastrad memset(M, 0, sizeof(*M));
592 1.1 riastrad M->sc = sc;
593 1.1 riastrad M->x = x; /* input */
594 1.1 riastrad M->y = 0; /* output */
595 1.1 riastrad
596 1.1 riastrad /*
597 1.1 riastrad * Now iterate over the EINJ-type entries and execute the
598 1.1 riastrad * trigger error action instructions -- but skip if they're not
599 1.1 riastrad * for the TRIGGER_ERROR action, and stop if they're truncated.
600 1.1 riastrad *
601 1.1 riastrad * Entries are fixed-size, so we can just index them.
602 1.1 riastrad */
603 1.2 riastrad entry = (ACPI_EINJ_ENTRY *)((char *)teatab + teatab->HeaderSize);
604 1.1 riastrad for (i = 0; i < nentries; i++) {
605 1.1 riastrad ACPI_WHEA_HEADER *const header = &entry[i].WheaHeader;
606 1.1 riastrad
607 1.1 riastrad /*
608 1.1 riastrad * Verify the action is TRIGGER_ERROR. If not, skip.
609 1.1 riastrad */
610 1.1 riastrad if (header->Action != ACPI_EINJ_TRIGGER_ERROR) {
611 1.1 riastrad device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
612 1.1 riastrad " other action: %"PRIu32" (%s)\n",
613 1.1 riastrad header->Action,
614 1.1 riastrad (header->Action < __arraycount(apei_einj_action)
615 1.1 riastrad ? apei_einj_action[header->Action]
616 1.1 riastrad : "unknown"));
617 1.1 riastrad continue;
618 1.1 riastrad }
619 1.1 riastrad
620 1.1 riastrad /*
621 1.1 riastrad * Execute the instruction. Since there's only one
622 1.1 riastrad * action, we don't bother with the apei_interp
623 1.1 riastrad * machinery to collate instruction tables for each
624 1.1 riastrad * action. EINJ instructions don't change ip.
625 1.1 riastrad */
626 1.1 riastrad uint32_t ip = i + 1;
627 1.1 riastrad apei_einj_instfunc(header, M, &ip, nentries);
628 1.1 riastrad KASSERT(ip == i + 1);
629 1.1 riastrad }
630 1.1 riastrad
631 1.1 riastrad out: if (teatab) {
632 1.1 riastrad AcpiOsUnmapMemory(teatab, mapsize);
633 1.1 riastrad teatab = NULL;
634 1.1 riastrad mapsize = 0;
635 1.1 riastrad }
636 1.1 riastrad return M->y;
637 1.1 riastrad }
638 1.1 riastrad
639 1.1 riastrad /*
640 1.1 riastrad * apei_einj_action_sysctl:
641 1.1 riastrad *
642 1.1 riastrad * Handle sysctl queries under hw.acpi.apei.einj.action.*.
643 1.1 riastrad */
644 1.1 riastrad static int
645 1.1 riastrad apei_einj_action_sysctl(SYSCTLFN_ARGS)
646 1.1 riastrad {
647 1.1 riastrad device_t apei0 = NULL;
648 1.1 riastrad struct apei_softc *sc;
649 1.1 riastrad enum AcpiEinjActions action;
650 1.1 riastrad struct sysctlnode node = *rnode;
651 1.1 riastrad uint64_t v;
652 1.1 riastrad int error;
653 1.1 riastrad
654 1.1 riastrad /*
655 1.1 riastrad * As a defence against mistakes, require the user to specify a
656 1.1 riastrad * write.
657 1.1 riastrad */
658 1.1 riastrad if (newp == NULL) {
659 1.1 riastrad error = ENOENT;
660 1.1 riastrad goto out;
661 1.1 riastrad }
662 1.1 riastrad
663 1.1 riastrad /*
664 1.1 riastrad * Take a reference to the apei0 device so it doesn't go away
665 1.1 riastrad * while we're working, and get the softc.
666 1.1 riastrad */
667 1.1 riastrad if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
668 1.1 riastrad error = ENXIO;
669 1.1 riastrad goto out;
670 1.1 riastrad }
671 1.1 riastrad sc = device_private(apei0);
672 1.1 riastrad
673 1.1 riastrad /*
674 1.1 riastrad * Fail if there's no EINJ.
675 1.1 riastrad */
676 1.1 riastrad if (sc->sc_tab.einj == NULL) {
677 1.1 riastrad error = ENODEV;
678 1.1 riastrad goto out;
679 1.1 riastrad }
680 1.1 riastrad
681 1.1 riastrad /*
682 1.1 riastrad * Identify the requested action. If we don't recognize it,
683 1.1 riastrad * fail with EINVAL.
684 1.1 riastrad */
685 1.1 riastrad switch (node.sysctl_num) {
686 1.1 riastrad case ACPI_EINJ_BEGIN_OPERATION:
687 1.1 riastrad case ACPI_EINJ_GET_TRIGGER_TABLE:
688 1.1 riastrad case ACPI_EINJ_SET_ERROR_TYPE:
689 1.1 riastrad case ACPI_EINJ_GET_ERROR_TYPE:
690 1.1 riastrad case ACPI_EINJ_END_OPERATION:
691 1.1 riastrad case ACPI_EINJ_EXECUTE_OPERATION:
692 1.1 riastrad case ACPI_EINJ_CHECK_BUSY_STATUS:
693 1.1 riastrad case ACPI_EINJ_GET_COMMAND_STATUS:
694 1.1 riastrad case ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS:
695 1.1 riastrad case ACPI_EINJ_GET_EXECUTE_TIMINGS:
696 1.1 riastrad action = node.sysctl_num;
697 1.1 riastrad break;
698 1.1 riastrad default:
699 1.1 riastrad error = ENOENT;
700 1.1 riastrad goto out;
701 1.1 riastrad }
702 1.1 riastrad
703 1.1 riastrad /*
704 1.1 riastrad * Kludge: Copy the `new value' for the sysctl in as an input
705 1.1 riastrad * to the injection action.
706 1.1 riastrad */
707 1.1 riastrad error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
708 1.1 riastrad if (error)
709 1.1 riastrad goto out;
710 1.1 riastrad
711 1.1 riastrad /*
712 1.1 riastrad * Perform the EINJ action by following the table's
713 1.1 riastrad * instructions.
714 1.1 riastrad */
715 1.1 riastrad v = apei_einj_act(sc, action, v);
716 1.1 riastrad
717 1.1 riastrad /*
718 1.1 riastrad * Return the output of the operation as the `old value' of the
719 1.1 riastrad * sysctl. This also updates v with what was written to the
720 1.1 riastrad * sysctl was written, but we don't care because we already
721 1.1 riastrad * read that in and acted on it.
722 1.1 riastrad */
723 1.1 riastrad node.sysctl_data = &v;
724 1.1 riastrad error = sysctl_lookup(SYSCTLFN_CALL(&node));
725 1.1 riastrad
726 1.1 riastrad out: if (apei0) {
727 1.1 riastrad device_release(apei0);
728 1.1 riastrad apei0 = NULL;
729 1.1 riastrad }
730 1.1 riastrad return error;
731 1.1 riastrad }
732 1.1 riastrad
733 1.1 riastrad /*
734 1.1 riastrad * apei_einj_trigger_sysctl
735 1.1 riastrad *
736 1.1 riastrad * Handle sysctl hw.acpi.apei.einj.trigger.
737 1.1 riastrad */
738 1.1 riastrad static int
739 1.1 riastrad apei_einj_trigger_sysctl(SYSCTLFN_ARGS)
740 1.1 riastrad {
741 1.1 riastrad device_t apei0 = NULL;
742 1.1 riastrad struct apei_softc *sc;
743 1.1 riastrad struct sysctlnode node = *rnode;
744 1.1 riastrad uint64_t v;
745 1.1 riastrad int error;
746 1.1 riastrad
747 1.1 riastrad /*
748 1.1 riastrad * As a defence against mistakes, require the user to specify a
749 1.1 riastrad * write.
750 1.1 riastrad */
751 1.1 riastrad if (newp == NULL) {
752 1.1 riastrad error = ENOENT;
753 1.1 riastrad goto out;
754 1.1 riastrad }
755 1.1 riastrad
756 1.1 riastrad /*
757 1.1 riastrad * Take a reference to the apei0 device so it doesn't go away
758 1.1 riastrad * while we're working, and get the softc.
759 1.1 riastrad */
760 1.1 riastrad if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
761 1.1 riastrad error = ENXIO;
762 1.1 riastrad goto out;
763 1.1 riastrad }
764 1.1 riastrad sc = device_private(apei0);
765 1.1 riastrad
766 1.1 riastrad /*
767 1.1 riastrad * Fail if there's no EINJ.
768 1.1 riastrad */
769 1.1 riastrad if (sc->sc_tab.einj == NULL) {
770 1.1 riastrad error = ENODEV;
771 1.1 riastrad goto out;
772 1.1 riastrad }
773 1.1 riastrad
774 1.1 riastrad /*
775 1.1 riastrad * Kludge: Copy the `new value' for the sysctl in as an input
776 1.1 riastrad * to the trigger action.
777 1.1 riastrad */
778 1.1 riastrad error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
779 1.1 riastrad if (error)
780 1.1 riastrad goto out;
781 1.1 riastrad
782 1.1 riastrad /*
783 1.1 riastrad * Perform the TRIGGER_ERROR action.
784 1.1 riastrad */
785 1.1 riastrad v = apei_einj_trigger(sc, v);
786 1.1 riastrad
787 1.1 riastrad /*
788 1.1 riastrad * Return the output of the operation as the `old value' of the
789 1.1 riastrad * sysctl. This also updates v with what was written to the
790 1.1 riastrad * sysctl was written, but we don't care because we already
791 1.1 riastrad * read that in and acted on it.
792 1.1 riastrad */
793 1.1 riastrad node.sysctl_data = &v;
794 1.1 riastrad error = sysctl_lookup(SYSCTLFN_CALL(&node));
795 1.1 riastrad
796 1.1 riastrad out: if (apei0) {
797 1.1 riastrad device_release(apei0);
798 1.1 riastrad apei0 = NULL;
799 1.1 riastrad }
800 1.1 riastrad return error;
801 1.1 riastrad }
802 1.1 riastrad
803 1.1 riastrad /*
804 1.1 riastrad * apei_einj_types_sysctl
805 1.1 riastrad *
806 1.1 riastrad * Handle sysctl hw.acpi.apei.einj.types.
807 1.1 riastrad */
808 1.1 riastrad static int
809 1.1 riastrad apei_einj_types_sysctl(SYSCTLFN_ARGS)
810 1.1 riastrad {
811 1.1 riastrad device_t apei0 = NULL;
812 1.1 riastrad struct apei_softc *sc;
813 1.1 riastrad struct sysctlnode node = *rnode;
814 1.1 riastrad uint64_t types;
815 1.1 riastrad int error;
816 1.1 riastrad
817 1.1 riastrad /*
818 1.1 riastrad * Take a reference to the apei0 device so it doesn't go away
819 1.1 riastrad * while we're working, and get the softc.
820 1.1 riastrad *
821 1.1 riastrad * XXX Is this necessary? Shouldn't sysctl_teardown take care
822 1.1 riastrad * of preventing new sysctl calls and waiting until all pending
823 1.1 riastrad * sysctl calls are done?
824 1.1 riastrad */
825 1.1 riastrad if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
826 1.1 riastrad error = ENXIO;
827 1.1 riastrad goto out;
828 1.1 riastrad }
829 1.1 riastrad sc = device_private(apei0);
830 1.1 riastrad
831 1.1 riastrad /*
832 1.1 riastrad * Fail if there's no EINJ.
833 1.1 riastrad */
834 1.1 riastrad if (sc->sc_tab.einj == NULL) {
835 1.1 riastrad error = ENODEV;
836 1.1 riastrad goto out;
837 1.1 riastrad }
838 1.1 riastrad
839 1.1 riastrad /*
840 1.1 riastrad * Perform the GET_ERROR_TYPE action and return the value to
841 1.1 riastrad * sysctl.
842 1.1 riastrad *
843 1.1 riastrad * XXX Should this do it between BEGIN_INJECTION_OPERATION and
844 1.1 riastrad * END_OPERATION?
845 1.1 riastrad */
846 1.1 riastrad types = apei_einj_act(sc, ACPI_EINJ_GET_ERROR_TYPE, 0);
847 1.1 riastrad node.sysctl_data = &types;
848 1.1 riastrad error = sysctl_lookup(SYSCTLFN_CALL(&node));
849 1.1 riastrad
850 1.1 riastrad out: if (apei0) {
851 1.1 riastrad device_release(apei0);
852 1.1 riastrad apei0 = NULL;
853 1.1 riastrad }
854 1.1 riastrad return error;
855 1.1 riastrad }
856