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