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
65ACPI_MODULE_NAME	("apei")
66
67static void apei_einj_instfunc(ACPI_WHEA_HEADER *, struct apei_mapreg *,
68    void *, uint32_t *, uint32_t);
69static uint64_t apei_einj_act(struct apei_softc *, enum AcpiEinjActions,
70    uint64_t);
71static uint64_t apei_einj_trigger(struct apei_softc *, uint64_t);
72static int apei_einj_action_sysctl(SYSCTLFN_ARGS);
73static int apei_einj_trigger_sysctl(SYSCTLFN_ARGS);
74static 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 */
84static 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
106static 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 */
121static 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 */
136void
137apei_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 */
365void
366apei_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 */
381struct 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 */
397static void
398apei_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 */
465static uint64_t
466apei_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 */
508static uint64_t
509apei_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
664out:	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 */
677static int
678apei_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
759out:	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 */
771static int
772apei_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
829out:	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 */
841static int
842apei_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
883out:	if (apei0) {
884		device_release(apei0);
885		apei0 = NULL;
886	}
887	return error;
888}
889