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