1 /* $NetBSD: apei_einj.c,v 1.7 2024/03/28 13:40:08 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.7 2024/03/28 13:40:08 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 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
apei_einj_attach(struct apei_softc * sc)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
apei_einj_detach(struct apei_softc * sc)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
apei_einj_instfunc(ACPI_WHEA_HEADER * header,struct apei_mapreg * map,void * cookie,uint32_t * ipp,uint32_t maxip)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
apei_einj_act(struct apei_softc * sc,enum AcpiEinjActions action,uint64_t x)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
apei_einj_trigger(struct apei_softc * sc,uint64_t x)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 * Initialize the machine to execute the TRIGGER_ERROR action's
520 * instructions. Do this early to keep the error branches
521 * simpler.
522 */
523 memset(M, 0, sizeof(*M));
524 M->sc = sc;
525 M->x = x; /* input */
526 M->y = 0; /* output */
527
528 /*
529 * Get the TRIGGER_ERROR action table's physical address.
530 */
531 teatab_pa = apei_einj_act(sc, ACPI_EINJ_GET_TRIGGER_TABLE, 0);
532
533 /*
534 * Map just the header. We don't know how large the table is
535 * because we get that from the header.
536 */
537 mapsize = sizeof(*teatab);
538 teatab = AcpiOsMapMemory(teatab_pa, mapsize);
539
540 /*
541 * If there's no entries, stop here -- nothing to do separately
542 * to trigger an error report.
543 */
544 nentries = teatab->EntryCount;
545 if (nentries == 0)
546 goto out;
547
548 /*
549 * If the header size or the table size is nonsense, bail.
550 */
551 if (teatab->HeaderSize < sizeof(*teatab) ||
552 teatab->TableSize < teatab->HeaderSize) {
553 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
554 " invalid sizes:"
555 " HeaderSize=%"PRIu32" TableSize=%"PRIu32"\n",
556 teatab->HeaderSize, teatab->TableSize);
557 }
558
559 /*
560 * If the revision is nonzero, we don't know what to do. I've
561 * only seen revision zero so far, and the spec doesn't say
562 * anything about revisions that I've found.
563 */
564 if (teatab->Revision != 0) {
565 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
566 " unknown revision: %"PRIx32"\n", teatab->Revision);
567 goto out;
568 }
569
570 /*
571 * Truncate the table to the number of entries requested and
572 * ignore trailing garbage if the table is long, or round the
573 * number of entries down to what fits in the table if the
574 * table is short.
575 */
576 tabsize = teatab->TableSize;
577 bodysize = tabsize - teatab->HeaderSize;
578 if (nentries < howmany(bodysize, sizeof(ACPI_EINJ_ENTRY))) {
579 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
580 " %zu bytes of trailing garbage\n",
581 tabsize - nentries*sizeof(ACPI_EINJ_ENTRY));
582 bodysize = nentries*sizeof(ACPI_EINJ_ENTRY);
583 tabsize = teatab->HeaderSize + bodysize;
584 } else if (nentries > howmany(bodysize, sizeof(ACPI_EINJ_ENTRY))) {
585 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
586 " truncated to %zu entries\n",
587 nentries*sizeof(ACPI_EINJ_ENTRY));
588 nentries = howmany(bodysize, sizeof(ACPI_EINJ_ENTRY));
589 bodysize = nentries*sizeof(ACPI_EINJ_ENTRY);
590 tabsize = teatab->HeaderSize + bodysize;
591 }
592
593 /*
594 * Unmap the header and map the whole table instead.
595 */
596 AcpiOsUnmapMemory(teatab, mapsize);
597 mapsize = tabsize;
598 teatab = AcpiOsMapMemory(teatab_pa, mapsize);
599
600 /*
601 * Now iterate over the EINJ-type entries and execute the
602 * trigger error action instructions -- but skip if they're not
603 * for the TRIGGER_ERROR action, and stop if they're truncated.
604 *
605 * Entries are fixed-size, so we can just index them.
606 */
607 entry = (ACPI_EINJ_ENTRY *)((char *)teatab + teatab->HeaderSize);
608 for (i = 0; i < nentries; i++) {
609 ACPI_WHEA_HEADER *const header = &entry[i].WheaHeader;
610
611 /*
612 * Verify the action is TRIGGER_ERROR. If not, skip.
613 */
614 if (header->Action != ACPI_EINJ_TRIGGER_ERROR) {
615 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
616 " other action: %"PRIu32" (%s)\n",
617 header->Action,
618 (header->Action < __arraycount(apei_einj_action)
619 ? apei_einj_action[header->Action]
620 : "unknown"));
621 continue;
622 }
623
624 /*
625 * Verify the instruction.
626 */
627 if (header->Instruction >= __arraycount(apei_einj_instreg)) {
628 device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
629 " unknown instruction: %"PRIu32"\n",
630 header->Instruction);
631 continue;
632 }
633
634 /*
635 * Map the register if needed.
636 */
637 struct apei_mapreg *map = NULL;
638 if (apei_einj_instreg[header->Instruction]) {
639 map = apei_mapreg_map(&header->RegisterRegion);
640 if (map == NULL) {
641 device_printf(sc->sc_dev, "TRIGGER_ERROR"
642 " action table: failed to map register\n");
643 continue;
644 }
645 }
646
647 /*
648 * Execute the instruction. Since there's only one
649 * action, we don't bother with the apei_interp
650 * machinery to collate instruction tables for each
651 * action. EINJ instructions don't change ip.
652 */
653 uint32_t ip = i + 1;
654 apei_einj_instfunc(header, map, M, &ip, nentries);
655 KASSERT(ip == i + 1);
656
657 /*
658 * Unmap the register if mapped.
659 */
660 if (map != NULL)
661 apei_mapreg_unmap(&header->RegisterRegion, map);
662 }
663
664 out: if (teatab) {
665 AcpiOsUnmapMemory(teatab, mapsize);
666 teatab = NULL;
667 mapsize = 0;
668 }
669 return M->y;
670 }
671
672 /*
673 * apei_einj_action_sysctl:
674 *
675 * Handle sysctl queries under hw.acpi.apei.einj.action.*.
676 */
677 static int
apei_einj_action_sysctl(SYSCTLFN_ARGS)678 apei_einj_action_sysctl(SYSCTLFN_ARGS)
679 {
680 device_t apei0 = NULL;
681 struct apei_softc *sc;
682 enum AcpiEinjActions action;
683 struct sysctlnode node = *rnode;
684 uint64_t v;
685 int error;
686
687 /*
688 * As a defence against mistakes, require the user to specify a
689 * write.
690 */
691 if (newp == NULL) {
692 error = ENOENT;
693 goto out;
694 }
695
696 /*
697 * Take a reference to the apei0 device so it doesn't go away
698 * while we're working, and get the softc.
699 */
700 if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
701 error = ENXIO;
702 goto out;
703 }
704 sc = device_private(apei0);
705
706 /*
707 * Fail if there's no EINJ.
708 */
709 if (sc->sc_tab.einj == NULL) {
710 error = ENODEV;
711 goto out;
712 }
713
714 /*
715 * Identify the requested action. If we don't recognize it,
716 * fail with EINVAL.
717 */
718 switch (node.sysctl_num) {
719 case ACPI_EINJ_BEGIN_OPERATION:
720 case ACPI_EINJ_GET_TRIGGER_TABLE:
721 case ACPI_EINJ_SET_ERROR_TYPE:
722 case ACPI_EINJ_GET_ERROR_TYPE:
723 case ACPI_EINJ_END_OPERATION:
724 case ACPI_EINJ_EXECUTE_OPERATION:
725 case ACPI_EINJ_CHECK_BUSY_STATUS:
726 case ACPI_EINJ_GET_COMMAND_STATUS:
727 case ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS:
728 case ACPI_EINJ_GET_EXECUTE_TIMINGS:
729 action = node.sysctl_num;
730 break;
731 default:
732 error = ENOENT;
733 goto out;
734 }
735
736 /*
737 * Kludge: Copy the `new value' for the sysctl in as an input
738 * to the injection action.
739 */
740 error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
741 if (error)
742 goto out;
743
744 /*
745 * Perform the EINJ action by following the table's
746 * instructions.
747 */
748 v = apei_einj_act(sc, action, v);
749
750 /*
751 * Return the output of the operation as the `old value' of the
752 * sysctl. This also updates v with what was written to the
753 * sysctl was written, but we don't care because we already
754 * read that in and acted on it.
755 */
756 node.sysctl_data = &v;
757 error = sysctl_lookup(SYSCTLFN_CALL(&node));
758
759 out: if (apei0) {
760 device_release(apei0);
761 apei0 = NULL;
762 }
763 return error;
764 }
765
766 /*
767 * apei_einj_trigger_sysctl
768 *
769 * Handle sysctl hw.acpi.apei.einj.trigger.
770 */
771 static int
apei_einj_trigger_sysctl(SYSCTLFN_ARGS)772 apei_einj_trigger_sysctl(SYSCTLFN_ARGS)
773 {
774 device_t apei0 = NULL;
775 struct apei_softc *sc;
776 struct sysctlnode node = *rnode;
777 uint64_t v;
778 int error;
779
780 /*
781 * As a defence against mistakes, require the user to specify a
782 * write.
783 */
784 if (newp == NULL) {
785 error = ENOENT;
786 goto out;
787 }
788
789 /*
790 * Take a reference to the apei0 device so it doesn't go away
791 * while we're working, and get the softc.
792 */
793 if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
794 error = ENXIO;
795 goto out;
796 }
797 sc = device_private(apei0);
798
799 /*
800 * Fail if there's no EINJ.
801 */
802 if (sc->sc_tab.einj == NULL) {
803 error = ENODEV;
804 goto out;
805 }
806
807 /*
808 * Kludge: Copy the `new value' for the sysctl in as an input
809 * to the trigger action.
810 */
811 error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
812 if (error)
813 goto out;
814
815 /*
816 * Perform the TRIGGER_ERROR action.
817 */
818 v = apei_einj_trigger(sc, v);
819
820 /*
821 * Return the output of the operation as the `old value' of the
822 * sysctl. This also updates v with what was written to the
823 * sysctl was written, but we don't care because we already
824 * read that in and acted on it.
825 */
826 node.sysctl_data = &v;
827 error = sysctl_lookup(SYSCTLFN_CALL(&node));
828
829 out: if (apei0) {
830 device_release(apei0);
831 apei0 = NULL;
832 }
833 return error;
834 }
835
836 /*
837 * apei_einj_types_sysctl
838 *
839 * Handle sysctl hw.acpi.apei.einj.types.
840 */
841 static int
apei_einj_types_sysctl(SYSCTLFN_ARGS)842 apei_einj_types_sysctl(SYSCTLFN_ARGS)
843 {
844 device_t apei0 = NULL;
845 struct apei_softc *sc;
846 struct sysctlnode node = *rnode;
847 uint64_t types;
848 int error;
849
850 /*
851 * Take a reference to the apei0 device so it doesn't go away
852 * while we're working, and get the softc.
853 *
854 * XXX Is this necessary? Shouldn't sysctl_teardown take care
855 * of preventing new sysctl calls and waiting until all pending
856 * sysctl calls are done?
857 */
858 if ((apei0 = device_lookup_acquire(&apei_cd, 0)) == NULL) {
859 error = ENXIO;
860 goto out;
861 }
862 sc = device_private(apei0);
863
864 /*
865 * Fail if there's no EINJ.
866 */
867 if (sc->sc_tab.einj == NULL) {
868 error = ENODEV;
869 goto out;
870 }
871
872 /*
873 * Perform the GET_ERROR_TYPE action and return the value to
874 * sysctl.
875 *
876 * XXX Should this do it between BEGIN_INJECTION_OPERATION and
877 * END_OPERATION?
878 */
879 types = apei_einj_act(sc, ACPI_EINJ_GET_ERROR_TYPE, 0);
880 node.sysctl_data = &types;
881 error = sysctl_lookup(SYSCTLFN_CALL(&node));
882
883 out: if (apei0) {
884 device_release(apei0);
885 apei0 = NULL;
886 }
887 return error;
888 }
889