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