apei_einj.c revision 1.6 1 /* $NetBSD: apei_einj.c,v 1.6 2024/03/26 22:01:03 rillig 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.6 2024/03/26 22:01:03 rillig 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 instructions 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
403 /*
404 * Abbreviate some of the intermediate quantities to make the
405 * instruction logic conciser and more legible.
406 */
407 const uint8_t BitOffset = header->RegisterRegion.BitOffset;
408 const uint64_t Mask = header->Mask;
409 const uint64_t Value = header->Value;
410 ACPI_GENERIC_ADDRESS *const reg = &header->RegisterRegion;
411 const bool preserve_register = header->Flags & ACPI_EINJ_PRESERVE;
412
413 aprint_debug_dev(M->sc->sc_dev, "%s: instr=0x%02"PRIx8
414 " (%s)"
415 " Address=0x%"PRIx64
416 " BitOffset=%"PRIu8" Mask=0x%"PRIx64" Value=0x%"PRIx64
417 " Flags=0x%"PRIx8"\n",
418 __func__, header->Instruction,
419 (header->Instruction < __arraycount(apei_einj_instruction)
420 ? apei_einj_instruction[header->Instruction]
421 : "unknown"),
422 reg->Address,
423 BitOffset, Mask, Value,
424 header->Flags);
425
426 /*
427 * Zero-initialize the output by default.
428 */
429 M->y = 0;
430
431 /*
432 * Dispatch the instruction.
433 */
434 switch (header->Instruction) {
435 case ACPI_EINJ_READ_REGISTER:
436 M->y = apei_read_register(reg, map, Mask);
437 break;
438 case ACPI_EINJ_READ_REGISTER_VALUE: {
439 uint64_t v;
440
441 v = apei_read_register(reg, map, Mask);
442 M->y = (v == Value ? 1 : 0);
443 break;
444 }
445 case ACPI_EINJ_WRITE_REGISTER:
446 apei_write_register(reg, map, Mask, preserve_register, M->x);
447 break;
448 case ACPI_EINJ_WRITE_REGISTER_VALUE:
449 apei_write_register(reg, map, Mask, preserve_register, Value);
450 break;
451 case ACPI_EINJ_NOOP:
452 break;
453 default: /* XXX unreachable */
454 break;
455 }
456 }
457
458 /*
459 * apei_einj_act(sc, action, x)
460 *
461 * Perform the named EINJ action with input x, by executing the
462 * instruction defined for the action by the EINJ, and return the
463 * output.
464 */
465 static uint64_t
466 apei_einj_act(struct apei_softc *sc, enum AcpiEinjActions action,
467 uint64_t x)
468 {
469 struct apei_einj_softc *const jsc = &sc->sc_einj;
470 struct apei_einj_machine einj_machine, *const M = &einj_machine;
471
472 aprint_debug_dev(sc->sc_dev, "%s: action=%d (%s) input=0x%"PRIx64"\n",
473 __func__,
474 action,
475 (action < __arraycount(apei_einj_action)
476 ? apei_einj_action[action]
477 : "unknown"),
478 x);
479
480 /*
481 * Initialize the machine to execute the action's instructions.
482 */
483 memset(M, 0, sizeof(*M));
484 M->sc = sc;
485 M->x = x; /* input */
486 M->y = 0; /* output */
487
488 /*
489 * Run the interpreter.
490 */
491 apei_interpret(jsc->jsc_interp, action, M);
492
493 /*
494 * Return the result.
495 */
496 aprint_debug_dev(sc->sc_dev, "%s: output=0x%"PRIx64"\n", __func__,
497 M->y);
498 return M->y;
499 }
500
501 /*
502 * apei_einj_trigger(sc, x)
503 *
504 * Obtain the TRIGGER_ERROR action table and, if there is anything
505 * to be done with it, execute it with input x and return the
506 * output. If nothing is to be done, return 0.
507 */
508 static uint64_t
509 apei_einj_trigger(struct apei_softc *sc, uint64_t x)
510 {
511 uint64_t teatab_pa;
512 ACPI_EINJ_TRIGGER *teatab = NULL;
513 size_t mapsize = 0, tabsize, bodysize;
514 ACPI_EINJ_ENTRY *entry;
515 struct apei_einj_machine einj_machine, *const M = &einj_machine;
516 uint32_t i, nentries;
517
518 /*
519 * Get the TRIGGER_ERROR action table's physical address.
520 */
521 teatab_pa = apei_einj_act(sc, ACPI_EINJ_GET_TRIGGER_TABLE, 0);
522
523 /*
524 * Map just the header. We don't know how large the table is
525 * because we get that from the header.
526 */
527 mapsize = sizeof(*teatab);
528 teatab = AcpiOsMapMemory(teatab_pa, mapsize);
529
530 /*
531 * If there's no entries, stop here -- nothing to do separately
532 * to trigger an error report.
533 */
534 nentries = teatab->EntryCount;
535 if (nentries == 0)
536 goto out;
537
538 /*
539 * If the header size or the table size is nonsense, bail.
540 */
541 if (teatab->HeaderSize < sizeof(*teatab) ||
542 teatab->TableSize < teatab->HeaderSize) {
543 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
544 " invalid sizes:"
545 " HeaderSize=%"PRIu32" TableSize=%"PRIu32"\n",
546 teatab->HeaderSize, teatab->TableSize);
547 }
548
549 /*
550 * If the revision is nonzero, we don't know what to do. I've
551 * only seen revision zero so far, and the spec doesn't say
552 * anything about revisions that I've found.
553 */
554 if (teatab->Revision != 0) {
555 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
556 " unknown revision: %"PRIx32"\n", teatab->Revision);
557 goto out;
558 }
559
560 /*
561 * Truncate the table to the number of entries requested and
562 * ignore trailing garbage if the table is long, or round the
563 * number of entries down to what fits in the table if the
564 * table is short.
565 */
566 tabsize = teatab->TableSize;
567 bodysize = tabsize - teatab->HeaderSize;
568 if (nentries < howmany(bodysize, sizeof(ACPI_EINJ_ENTRY))) {
569 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
570 " %zu bytes of trailing garbage\n",
571 tabsize - nentries*sizeof(ACPI_EINJ_ENTRY));
572 bodysize = nentries*sizeof(ACPI_EINJ_ENTRY);
573 tabsize = teatab->HeaderSize + bodysize;
574 } else if (nentries > howmany(bodysize, sizeof(ACPI_EINJ_ENTRY))) {
575 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
576 " truncated to %zu entries\n",
577 nentries*sizeof(ACPI_EINJ_ENTRY));
578 nentries = howmany(bodysize, sizeof(ACPI_EINJ_ENTRY));
579 bodysize = nentries*sizeof(ACPI_EINJ_ENTRY);
580 tabsize = teatab->HeaderSize + bodysize;
581 }
582
583 /*
584 * Unmap the header and map the whole table instead.
585 */
586 AcpiOsUnmapMemory(teatab, mapsize);
587 mapsize = tabsize;
588 teatab = AcpiOsMapMemory(teatab_pa, mapsize);
589
590 /*
591 * Initialize the machine to execute the TRIGGER_ERROR action's
592 * instructions.
593 */
594 memset(M, 0, sizeof(*M));
595 M->sc = sc;
596 M->x = x; /* input */
597 M->y = 0; /* output */
598
599 /*
600 * Now iterate over the EINJ-type entries and execute the
601 * trigger error action instructions -- but skip if they're not
602 * for the TRIGGER_ERROR action, and stop if they're truncated.
603 *
604 * Entries are fixed-size, so we can just index them.
605 */
606 entry = (ACPI_EINJ_ENTRY *)((char *)teatab + teatab->HeaderSize);
607 for (i = 0; i < nentries; i++) {
608 ACPI_WHEA_HEADER *const header = &entry[i].WheaHeader;
609
610 /*
611 * Verify the action is TRIGGER_ERROR. If not, skip.
612 */
613 if (header->Action != ACPI_EINJ_TRIGGER_ERROR) {
614 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
615 " other action: %"PRIu32" (%s)\n",
616 header->Action,
617 (header->Action < __arraycount(apei_einj_action)
618 ? apei_einj_action[header->Action]
619 : "unknown"));
620 continue;
621 }
622
623 /*
624 * Verify the instruction.
625 */
626 if (header->Instruction >= __arraycount(apei_einj_instreg)) {
627 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
628 " unknown instruction: %"PRIu32"\n",
629 header->Instruction);
630 continue;
631 }
632
633 /*
634 * Map the register if needed.
635 */
636 struct apei_mapreg *map = NULL;
637 if (apei_einj_instreg[header->Instruction]) {
638 map = apei_mapreg_map(&header->RegisterRegion);
639 if (map == NULL) {
640 device_printf(sc->sc_dev, "TRIGGER_ERROR"
641 " action table: failed to map register\n");
642 continue;
643 }
644 }
645
646 /*
647 * Execute the instruction. Since there's only one
648 * action, we don't bother with the apei_interp
649 * machinery to collate instruction tables for each
650 * action. EINJ instructions don't change ip.
651 */
652 uint32_t ip = i + 1;
653 apei_einj_instfunc(header, map, M, &ip, nentries);
654 KASSERT(ip == i + 1);
655
656 /*
657 * Unmap the register if mapped.
658 */
659 if (map != NULL)
660 apei_mapreg_unmap(&header->RegisterRegion, map);
661 }
662
663 out: if (teatab) {
664 AcpiOsUnmapMemory(teatab, mapsize);
665 teatab = NULL;
666 mapsize = 0;
667 }
668 return M->y;
669 }
670
671 /*
672 * apei_einj_action_sysctl:
673 *
674 * Handle sysctl queries under hw.acpi.apei.einj.action.*.
675 */
676 static int
677 apei_einj_action_sysctl(SYSCTLFN_ARGS)
678 {
679 device_t apei0 = NULL;
680 struct apei_softc *sc;
681 enum AcpiEinjActions action;
682 struct sysctlnode node = *rnode;
683 uint64_t v;
684 int error;
685
686 /*
687 * As a defence against mistakes, require the user to specify a
688 * write.
689 */
690 if (newp == NULL) {
691 error = ENOENT;
692 goto out;
693 }
694
695 /*
696 * Take a reference to the apei0 device so it doesn't go away
697 * while we're working, and get the softc.
698 */
699 if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
700 error = ENXIO;
701 goto out;
702 }
703 sc = device_private(apei0);
704
705 /*
706 * Fail if there's no EINJ.
707 */
708 if (sc->sc_tab.einj == NULL) {
709 error = ENODEV;
710 goto out;
711 }
712
713 /*
714 * Identify the requested action. If we don't recognize it,
715 * fail with EINVAL.
716 */
717 switch (node.sysctl_num) {
718 case ACPI_EINJ_BEGIN_OPERATION:
719 case ACPI_EINJ_GET_TRIGGER_TABLE:
720 case ACPI_EINJ_SET_ERROR_TYPE:
721 case ACPI_EINJ_GET_ERROR_TYPE:
722 case ACPI_EINJ_END_OPERATION:
723 case ACPI_EINJ_EXECUTE_OPERATION:
724 case ACPI_EINJ_CHECK_BUSY_STATUS:
725 case ACPI_EINJ_GET_COMMAND_STATUS:
726 case ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS:
727 case ACPI_EINJ_GET_EXECUTE_TIMINGS:
728 action = node.sysctl_num;
729 break;
730 default:
731 error = ENOENT;
732 goto out;
733 }
734
735 /*
736 * Kludge: Copy the `new value' for the sysctl in as an input
737 * to the injection action.
738 */
739 error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
740 if (error)
741 goto out;
742
743 /*
744 * Perform the EINJ action by following the table's
745 * instructions.
746 */
747 v = apei_einj_act(sc, action, v);
748
749 /*
750 * Return the output of the operation as the `old value' of the
751 * sysctl. This also updates v with what was written to the
752 * sysctl was written, but we don't care because we already
753 * read that in and acted on it.
754 */
755 node.sysctl_data = &v;
756 error = sysctl_lookup(SYSCTLFN_CALL(&node));
757
758 out: if (apei0) {
759 device_release(apei0);
760 apei0 = NULL;
761 }
762 return error;
763 }
764
765 /*
766 * apei_einj_trigger_sysctl
767 *
768 * Handle sysctl hw.acpi.apei.einj.trigger.
769 */
770 static int
771 apei_einj_trigger_sysctl(SYSCTLFN_ARGS)
772 {
773 device_t apei0 = NULL;
774 struct apei_softc *sc;
775 struct sysctlnode node = *rnode;
776 uint64_t v;
777 int error;
778
779 /*
780 * As a defence against mistakes, require the user to specify a
781 * write.
782 */
783 if (newp == NULL) {
784 error = ENOENT;
785 goto out;
786 }
787
788 /*
789 * Take a reference to the apei0 device so it doesn't go away
790 * while we're working, and get the softc.
791 */
792 if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
793 error = ENXIO;
794 goto out;
795 }
796 sc = device_private(apei0);
797
798 /*
799 * Fail if there's no EINJ.
800 */
801 if (sc->sc_tab.einj == NULL) {
802 error = ENODEV;
803 goto out;
804 }
805
806 /*
807 * Kludge: Copy the `new value' for the sysctl in as an input
808 * to the trigger action.
809 */
810 error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
811 if (error)
812 goto out;
813
814 /*
815 * Perform the TRIGGER_ERROR action.
816 */
817 v = apei_einj_trigger(sc, v);
818
819 /*
820 * Return the output of the operation as the `old value' of the
821 * sysctl. This also updates v with what was written to the
822 * sysctl was written, but we don't care because we already
823 * read that in and acted on it.
824 */
825 node.sysctl_data = &v;
826 error = sysctl_lookup(SYSCTLFN_CALL(&node));
827
828 out: if (apei0) {
829 device_release(apei0);
830 apei0 = NULL;
831 }
832 return error;
833 }
834
835 /*
836 * apei_einj_types_sysctl
837 *
838 * Handle sysctl hw.acpi.apei.einj.types.
839 */
840 static int
841 apei_einj_types_sysctl(SYSCTLFN_ARGS)
842 {
843 device_t apei0 = NULL;
844 struct apei_softc *sc;
845 struct sysctlnode node = *rnode;
846 uint64_t types;
847 int error;
848
849 /*
850 * Take a reference to the apei0 device so it doesn't go away
851 * while we're working, and get the softc.
852 *
853 * XXX Is this necessary? Shouldn't sysctl_teardown take care
854 * of preventing new sysctl calls and waiting until all pending
855 * sysctl calls are done?
856 */
857 if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
858 error = ENXIO;
859 goto out;
860 }
861 sc = device_private(apei0);
862
863 /*
864 * Fail if there's no EINJ.
865 */
866 if (sc->sc_tab.einj == NULL) {
867 error = ENODEV;
868 goto out;
869 }
870
871 /*
872 * Perform the GET_ERROR_TYPE action and return the value to
873 * sysctl.
874 *
875 * XXX Should this do it between BEGIN_INJECTION_OPERATION and
876 * END_OPERATION?
877 */
878 types = apei_einj_act(sc, ACPI_EINJ_GET_ERROR_TYPE, 0);
879 node.sysctl_data = &types;
880 error = sysctl_lookup(SYSCTLFN_CALL(&node));
881
882 out: if (apei0) {
883 device_release(apei0);
884 apei0 = NULL;
885 }
886 return error;
887 }
888