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