apei_interp.c revision 1.4.4.2 1 1.4.4.2 martin /* $NetBSD: apei_interp.c,v 1.4.4.2 2024/10/09 13:00:11 martin Exp $ */
2 1.4.4.2 martin
3 1.4.4.2 martin /*-
4 1.4.4.2 martin * Copyright (c) 2024 The NetBSD Foundation, Inc.
5 1.4.4.2 martin * All rights reserved.
6 1.4.4.2 martin *
7 1.4.4.2 martin * Redistribution and use in source and binary forms, with or without
8 1.4.4.2 martin * modification, are permitted provided that the following conditions
9 1.4.4.2 martin * are met:
10 1.4.4.2 martin * 1. Redistributions of source code must retain the above copyright
11 1.4.4.2 martin * notice, this list of conditions and the following disclaimer.
12 1.4.4.2 martin * 2. Redistributions in binary form must reproduce the above copyright
13 1.4.4.2 martin * notice, this list of conditions and the following disclaimer in the
14 1.4.4.2 martin * documentation and/or other materials provided with the distribution.
15 1.4.4.2 martin *
16 1.4.4.2 martin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.4.4.2 martin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.4.4.2 martin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.4.4.2 martin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.4.4.2 martin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.4.4.2 martin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.4.4.2 martin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.4.4.2 martin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.4.4.2 martin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.4.4.2 martin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.4.4.2 martin * POSSIBILITY OF SUCH DAMAGE.
27 1.4.4.2 martin */
28 1.4.4.2 martin
29 1.4.4.2 martin /*
30 1.4.4.2 martin * APEI action interpreter.
31 1.4.4.2 martin *
32 1.4.4.2 martin * APEI provides a generalized abstraction to implement the actions an
33 1.4.4.2 martin * OS must take to inject an error, or save state in a persistent error
34 1.4.4.2 martin * record for the next boot, since there are many different hardware
35 1.4.4.2 martin * register interfaces for, e.g., injecting errors.
36 1.4.4.2 martin *
37 1.4.4.2 martin * You might think that APEI, being part of ACPI, would use the usual
38 1.4.4.2 martin * ACPI interpreter to run ACPI methods for these actions. You would
39 1.4.4.2 martin * be wrong. Alas.
40 1.4.4.2 martin *
41 1.4.4.2 martin * Instead, there is an entirely different little language of actions
42 1.4.4.2 martin * that an OS must write programs in to inject errors, and an entirely
43 1.4.4.2 martin * different little language of instructions that the interpreter for
44 1.4.4.2 martin * the actions uses to interpret the OS's error injection program. Got
45 1.4.4.2 martin * it?
46 1.4.4.2 martin *
47 1.4.4.2 martin * The EINJ and ERST tables provide a series entries that look like:
48 1.4.4.2 martin *
49 1.4.4.2 martin * +-----------------------------------------------+
50 1.4.4.2 martin * | Action=SET_ERROR_TYPE |
51 1.4.4.2 martin * | Instruction=SKIP_NEXT_INSTRUCTION_IF_TRUE |
52 1.4.4.2 martin * | Register=0x7fabcd10 [memory] |
53 1.4.4.2 martin * | Value=0xdeadbeef |
54 1.4.4.2 martin * +-----------------------------------------------+
55 1.4.4.2 martin * | Action=SET_ERROR_TYPE |
56 1.4.4.2 martin * | Instruction=WRITE_REGISTER_VALUE |
57 1.4.4.2 martin * | Register=0x7fabcd14 [memory] |
58 1.4.4.2 martin * | Value=1 |
59 1.4.4.2 martin * +-----------------------------------------------+
60 1.4.4.2 martin * | Action=SET_ERROR_TYPE |
61 1.4.4.2 martin * | Instruction=READ_REGISTER |
62 1.4.4.2 martin * | Register=0x7fabcd1c [memory] |
63 1.4.4.2 martin * +-----------------------------------------------+
64 1.4.4.2 martin * | Action=SET_ERROR_TYPE |
65 1.4.4.2 martin * | Instruction=WRITE_REGISTER |
66 1.4.4.2 martin * | Register=0x7fabcd20 [memory] |
67 1.4.4.2 martin * +-----------------------------------------------+
68 1.4.4.2 martin * | Action=EXECUTE_OPERATION |
69 1.4.4.2 martin * | Instruction=LOAD_VAR1 |
70 1.4.4.2 martin * | Register=0x7fabcf00 [memory] |
71 1.4.4.2 martin * +-----------------------------------------------+
72 1.4.4.2 martin * | Action=SET_ERROR_TYPE |
73 1.4.4.2 martin * | Instruction=WRITE_REGISTER_VALUE |
74 1.4.4.2 martin * | Register=0x7fabcd24 [memory] |
75 1.4.4.2 martin * | Value=42 |
76 1.4.4.2 martin * +-----------------------------------------------+
77 1.4.4.2 martin * | ... |
78 1.4.4.2 martin * +-----------------------------------------------+
79 1.4.4.2 martin *
80 1.4.4.2 martin * The entries tell the OS, for each action the OS might want to
81 1.4.4.2 martin * perform like BEGIN_INJECTION_OPERATION or SET_ERROR_TYPE or
82 1.4.4.2 martin * EXECUTE_OPERATION, what instructions must be executed and in what
83 1.4.4.2 martin * order.
84 1.4.4.2 martin *
85 1.4.4.2 martin * The instructions run in one of two little state machines -- there's
86 1.4.4.2 martin * a different instruction set for EINJ and ERST -- and vary from noops
87 1.4.4.2 martin * to reading and writing registers to arithmetic on registers to
88 1.4.4.2 martin * conditional and unconditional branches.
89 1.4.4.2 martin *
90 1.4.4.2 martin * Yes, that means this little language -- the ERST language, anyway,
91 1.4.4.2 martin * not the EINJ language -- is Turing-complete.
92 1.4.4.2 martin *
93 1.4.4.2 martin * This APEI interpreter first compiles the table into a contiguous
94 1.4.4.2 martin * sequence of instructions for each action, to make execution easier,
95 1.4.4.2 martin * since there's no requirement that the instructions for an action be
96 1.4.4.2 martin * contiguous in the table, and the GOTO instruction relies on
97 1.4.4.2 martin * contiguous indexing of the instructions for an action.
98 1.4.4.2 martin *
99 1.4.4.2 martin * This interpreter also does a little validation so the firmware
100 1.4.4.2 martin * doesn't, e.g., GOTO somewhere in oblivion. The validation is mainly
101 1.4.4.2 martin * a convenience for catching mistakes in firmware, not a security
102 1.4.4.2 martin * measure, since the OS is absolutely vulnerable to malicious firmware
103 1.4.4.2 martin * anyway.
104 1.4.4.2 martin */
105 1.4.4.2 martin
106 1.4.4.2 martin #include <sys/cdefs.h>
107 1.4.4.2 martin __KERNEL_RCSID(0, "$NetBSD: apei_interp.c,v 1.4.4.2 2024/10/09 13:00:11 martin Exp $");
108 1.4.4.2 martin
109 1.4.4.2 martin #include <sys/types.h>
110 1.4.4.2 martin
111 1.4.4.2 martin #include <sys/kmem.h>
112 1.4.4.2 martin #include <sys/systm.h>
113 1.4.4.2 martin
114 1.4.4.2 martin #include <dev/acpi/acpivar.h>
115 1.4.4.2 martin #include <dev/acpi/apei_interp.h>
116 1.4.4.2 martin #include <dev/acpi/apei_mapreg.h>
117 1.4.4.2 martin
118 1.4.4.2 martin /*
119 1.4.4.2 martin * struct apei_actinst
120 1.4.4.2 martin *
121 1.4.4.2 martin * Sequence of instructions to execute for an action.
122 1.4.4.2 martin */
123 1.4.4.2 martin struct apei_actinst {
124 1.4.4.2 martin uint32_t ninst;
125 1.4.4.2 martin uint32_t ip;
126 1.4.4.2 martin struct {
127 1.4.4.2 martin struct acpi_whea_header *header;
128 1.4.4.2 martin struct apei_mapreg *map;
129 1.4.4.2 martin } *inst;
130 1.4.4.2 martin bool disable;
131 1.4.4.2 martin };
132 1.4.4.2 martin
133 1.4.4.2 martin /*
134 1.4.4.2 martin * struct apei_interp
135 1.4.4.2 martin *
136 1.4.4.2 martin * Table of instructions to interpret APEI actions.
137 1.4.4.2 martin */
138 1.4.4.2 martin struct apei_interp {
139 1.4.4.2 martin const char *name;
140 1.4.4.2 martin const char *const *actname;
141 1.4.4.2 martin unsigned nact;
142 1.4.4.2 martin const char *const *instname;
143 1.4.4.2 martin unsigned ninst;
144 1.4.4.2 martin const bool *instreg;
145 1.4.4.2 martin bool (*instvalid)(ACPI_WHEA_HEADER *, uint32_t,
146 1.4.4.2 martin uint32_t);
147 1.4.4.2 martin void (*instfunc)(ACPI_WHEA_HEADER *,
148 1.4.4.2 martin struct apei_mapreg *, void *, uint32_t *,
149 1.4.4.2 martin uint32_t);
150 1.4.4.2 martin struct apei_actinst actinst[];
151 1.4.4.2 martin };
152 1.4.4.2 martin
153 1.4.4.2 martin struct apei_interp *
154 1.4.4.2 martin apei_interp_create(const char *name,
155 1.4.4.2 martin const char *const *actname, unsigned nact,
156 1.4.4.2 martin const char *const *instname, unsigned ninst,
157 1.4.4.2 martin const bool *instreg,
158 1.4.4.2 martin bool (*instvalid)(ACPI_WHEA_HEADER *, uint32_t, uint32_t),
159 1.4.4.2 martin void (*instfunc)(ACPI_WHEA_HEADER *, struct apei_mapreg *, void *,
160 1.4.4.2 martin uint32_t *, uint32_t))
161 1.4.4.2 martin {
162 1.4.4.2 martin struct apei_interp *I;
163 1.4.4.2 martin
164 1.4.4.2 martin I = kmem_zalloc(offsetof(struct apei_interp, actinst[nact]), KM_SLEEP);
165 1.4.4.2 martin I->name = name;
166 1.4.4.2 martin I->actname = actname;
167 1.4.4.2 martin I->nact = nact;
168 1.4.4.2 martin I->instname = instname;
169 1.4.4.2 martin I->ninst = ninst;
170 1.4.4.2 martin I->instreg = instreg;
171 1.4.4.2 martin I->instvalid = instvalid;
172 1.4.4.2 martin I->instfunc = instfunc;
173 1.4.4.2 martin
174 1.4.4.2 martin return I;
175 1.4.4.2 martin }
176 1.4.4.2 martin
177 1.4.4.2 martin void
178 1.4.4.2 martin apei_interp_destroy(struct apei_interp *I)
179 1.4.4.2 martin {
180 1.4.4.2 martin unsigned action, nact = I->nact;
181 1.4.4.2 martin
182 1.4.4.2 martin for (action = 0; action < nact; action++) {
183 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[action];
184 1.4.4.2 martin unsigned j;
185 1.4.4.2 martin
186 1.4.4.2 martin if (A->ninst == 0 || A->inst == NULL)
187 1.4.4.2 martin continue;
188 1.4.4.2 martin
189 1.4.4.2 martin for (j = 0; j < A->ninst; j++) {
190 1.4.4.2 martin ACPI_WHEA_HEADER *const E = A->inst[j].header;
191 1.4.4.2 martin struct apei_mapreg *const map = A->inst[j].map;
192 1.4.4.2 martin
193 1.4.4.2 martin if (map != NULL)
194 1.4.4.2 martin apei_mapreg_unmap(&E->RegisterRegion, map);
195 1.4.4.2 martin }
196 1.4.4.2 martin
197 1.4.4.2 martin kmem_free(A->inst, A->ninst * sizeof(A->inst[0]));
198 1.4.4.2 martin A->inst = NULL;
199 1.4.4.2 martin }
200 1.4.4.2 martin
201 1.4.4.2 martin kmem_free(I, offsetof(struct apei_interp, actinst[nact]));
202 1.4.4.2 martin }
203 1.4.4.2 martin
204 1.4.4.2 martin /*
205 1.4.4.2 martin * apei_interp_pass1_load(I, i, E)
206 1.4.4.2 martin *
207 1.4.4.2 martin * Load the ith table entry E into the interpreter I. To be
208 1.4.4.2 martin * called for each entry in the table sequentially.
209 1.4.4.2 martin *
210 1.4.4.2 martin * This first pass counts the number of instructions for each
211 1.4.4.2 martin * action, so we can allocate an array of instructions for
212 1.4.4.2 martin * indexing each action.
213 1.4.4.2 martin */
214 1.4.4.2 martin void
215 1.4.4.2 martin apei_interp_pass1_load(struct apei_interp *I, uint32_t i,
216 1.4.4.2 martin ACPI_WHEA_HEADER *E)
217 1.4.4.2 martin {
218 1.4.4.2 martin
219 1.4.4.2 martin /*
220 1.4.4.2 martin * If we don't recognize this action, ignore it and move on.
221 1.4.4.2 martin */
222 1.4.4.2 martin if (E->Action >= I->nact || I->actname[E->Action] == NULL) {
223 1.4.4.2 martin aprint_error("%s[%"PRIu32"]: unknown action: 0x%"PRIx8"\n",
224 1.4.4.2 martin I->name, i, E->Action);
225 1.4.4.2 martin return;
226 1.4.4.2 martin }
227 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[E->Action];
228 1.4.4.2 martin
229 1.4.4.2 martin /*
230 1.4.4.2 martin * If we can't interpret this instruction for this action, or
231 1.4.4.2 martin * if we couldn't interpret a previous instruction for this
232 1.4.4.2 martin * action, disable this action and move on.
233 1.4.4.2 martin */
234 1.4.4.2 martin if (E->Instruction >= I->ninst ||
235 1.4.4.2 martin I->instname[E->Instruction] == NULL) {
236 1.4.4.2 martin aprint_error("%s[%"PRIu32"]: unknown instruction: 0x%02"PRIx8
237 1.4.4.2 martin "\n", I->name, i, E->Instruction);
238 1.4.4.2 martin A->ninst = 0;
239 1.4.4.2 martin A->disable = true;
240 1.4.4.2 martin return;
241 1.4.4.2 martin }
242 1.4.4.2 martin if (A->disable)
243 1.4.4.2 martin return;
244 1.4.4.2 martin
245 1.4.4.2 martin /*
246 1.4.4.2 martin * Count another instruction. We will make a pointer
247 1.4.4.2 martin * to it in a later pass.
248 1.4.4.2 martin */
249 1.4.4.2 martin A->ninst++;
250 1.4.4.2 martin
251 1.4.4.2 martin /*
252 1.4.4.2 martin * If it overflows a reasonable size, disable the action
253 1.4.4.2 martin * altogether.
254 1.4.4.2 martin */
255 1.4.4.2 martin if (A->ninst >= 256) {
256 1.4.4.2 martin aprint_error("%s[%"PRIu32"]:"
257 1.4.4.2 martin " too many instructions for action %"PRIu32" (%s)\n",
258 1.4.4.2 martin I->name, i,
259 1.4.4.2 martin E->Action, I->actname[E->Action]);
260 1.4.4.2 martin A->ninst = 0;
261 1.4.4.2 martin A->disable = true;
262 1.4.4.2 martin return;
263 1.4.4.2 martin }
264 1.4.4.2 martin }
265 1.4.4.2 martin
266 1.4.4.2 martin /*
267 1.4.4.2 martin * apei_interp_pass2_verify(I, i, E)
268 1.4.4.2 martin *
269 1.4.4.2 martin * Verify the ith entry's instruction, using the caller's
270 1.4.4.2 martin * instvalid function, now that all the instructions have been
271 1.4.4.2 martin * counted. To be called for each entry in the table
272 1.4.4.2 martin * sequentially.
273 1.4.4.2 martin *
274 1.4.4.2 martin * This second pass checks that GOTO instructions in particular
275 1.4.4.2 martin * don't jump out of bounds.
276 1.4.4.2 martin */
277 1.4.4.2 martin void
278 1.4.4.2 martin apei_interp_pass2_verify(struct apei_interp *I, uint32_t i,
279 1.4.4.2 martin ACPI_WHEA_HEADER *E)
280 1.4.4.2 martin {
281 1.4.4.2 martin
282 1.4.4.2 martin /*
283 1.4.4.2 martin * If there's no instruction validation function, skip this
284 1.4.4.2 martin * pass.
285 1.4.4.2 martin */
286 1.4.4.2 martin if (I->instvalid == NULL)
287 1.4.4.2 martin return;
288 1.4.4.2 martin
289 1.4.4.2 martin /*
290 1.4.4.2 martin * If we skipped it in earlier passes, skip it now.
291 1.4.4.2 martin */
292 1.4.4.2 martin if (E->Action > I->nact || I->actname[E->Action] == NULL)
293 1.4.4.2 martin return;
294 1.4.4.2 martin
295 1.4.4.2 martin /*
296 1.4.4.2 martin * If the instruction is invalid, disable the whole action.
297 1.4.4.2 martin */
298 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[E->Action];
299 1.4.4.2 martin if (!(*I->instvalid)(E, A->ninst, i)) {
300 1.4.4.2 martin A->ninst = 0;
301 1.4.4.2 martin A->disable = true;
302 1.4.4.2 martin }
303 1.4.4.2 martin }
304 1.4.4.2 martin
305 1.4.4.2 martin /*
306 1.4.4.2 martin * apei_interp_pass3_alloc(I)
307 1.4.4.2 martin *
308 1.4.4.2 martin * Allocate an array of instructions for each action that we
309 1.4.4.2 martin * didn't disable.
310 1.4.4.2 martin */
311 1.4.4.2 martin void
312 1.4.4.2 martin apei_interp_pass3_alloc(struct apei_interp *I)
313 1.4.4.2 martin {
314 1.4.4.2 martin unsigned action;
315 1.4.4.2 martin
316 1.4.4.2 martin for (action = 0; action < I->nact; action++) {
317 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[action];
318 1.4.4.2 martin if (A->ninst == 0 || A->disable)
319 1.4.4.2 martin continue;
320 1.4.4.2 martin A->inst = kmem_zalloc(A->ninst * sizeof(A->inst[0]), KM_SLEEP);
321 1.4.4.2 martin }
322 1.4.4.2 martin }
323 1.4.4.2 martin
324 1.4.4.2 martin /*
325 1.4.4.2 martin * apei_interp_pass4_assemble(I, i, E)
326 1.4.4.2 martin *
327 1.4.4.2 martin * Put the instruction for the ith entry E into the instruction
328 1.4.4.2 martin * array for its action. To be called for each entry in the table
329 1.4.4.2 martin * sequentially.
330 1.4.4.2 martin */
331 1.4.4.2 martin void
332 1.4.4.2 martin apei_interp_pass4_assemble(struct apei_interp *I, uint32_t i,
333 1.4.4.2 martin ACPI_WHEA_HEADER *E)
334 1.4.4.2 martin {
335 1.4.4.2 martin
336 1.4.4.2 martin /*
337 1.4.4.2 martin * If we skipped it in earlier passes, skip it now.
338 1.4.4.2 martin */
339 1.4.4.2 martin if (E->Action >= I->nact || I->actname[E->Action] == NULL)
340 1.4.4.2 martin return;
341 1.4.4.2 martin
342 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[E->Action];
343 1.4.4.2 martin if (A->disable)
344 1.4.4.2 martin return;
345 1.4.4.2 martin
346 1.4.4.2 martin KASSERT(A->ip < A->ninst);
347 1.4.4.2 martin const uint32_t ip = A->ip++;
348 1.4.4.2 martin A->inst[ip].header = E;
349 1.4.4.2 martin A->inst[ip].map = I->instreg[E->Instruction] ?
350 1.4.4.2 martin apei_mapreg_map(&E->RegisterRegion) : NULL;
351 1.4.4.2 martin }
352 1.4.4.2 martin
353 1.4.4.2 martin /*
354 1.4.4.2 martin * apei_interp_pass5_verify(I)
355 1.4.4.2 martin *
356 1.4.4.2 martin * Paranoia: Verify we got all the instructions for each action,
357 1.4.4.2 martin * verify the actions point to their own instructions, and dump
358 1.4.4.2 martin * the instructions for each action, collated, with aprint_debug.
359 1.4.4.2 martin */
360 1.4.4.2 martin void
361 1.4.4.2 martin apei_interp_pass5_verify(struct apei_interp *I)
362 1.4.4.2 martin {
363 1.4.4.2 martin unsigned action;
364 1.4.4.2 martin
365 1.4.4.2 martin for (action = 0; action < I->nact; action++) {
366 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[action];
367 1.4.4.2 martin unsigned j;
368 1.4.4.2 martin
369 1.4.4.2 martin /*
370 1.4.4.2 martin * If the action is disabled, it's all set.
371 1.4.4.2 martin */
372 1.4.4.2 martin if (A->disable)
373 1.4.4.2 martin continue;
374 1.4.4.2 martin KASSERTMSG(A->ip == A->ninst,
375 1.4.4.2 martin "action %s ip=%"PRIu32" ninstruction=%"PRIu32,
376 1.4.4.2 martin I->actname[action], A->ip, A->ninst);
377 1.4.4.2 martin
378 1.4.4.2 martin /*
379 1.4.4.2 martin * XXX Dump the complete instruction table.
380 1.4.4.2 martin */
381 1.4.4.2 martin for (j = 0; j < A->ninst; j++) {
382 1.4.4.2 martin ACPI_WHEA_HEADER *const E = A->inst[j].header;
383 1.4.4.2 martin
384 1.4.4.2 martin KASSERT(E->Action == action);
385 1.4.4.2 martin
386 1.4.4.2 martin /*
387 1.4.4.2 martin * If we need the register and weren't able to
388 1.4.4.2 martin * map it, disable the action.
389 1.4.4.2 martin */
390 1.4.4.2 martin if (I->instreg[E->Instruction] &&
391 1.4.4.2 martin A->inst[j].map == NULL) {
392 1.4.4.2 martin A->disable = true;
393 1.4.4.2 martin continue;
394 1.4.4.2 martin }
395 1.4.4.2 martin
396 1.4.4.2 martin aprint_debug("%s: %s[%"PRIu32"]: %s\n",
397 1.4.4.2 martin I->name, I->actname[action], j,
398 1.4.4.2 martin I->instname[E->Instruction]);
399 1.4.4.2 martin }
400 1.4.4.2 martin }
401 1.4.4.2 martin }
402 1.4.4.2 martin
403 1.4.4.2 martin /*
404 1.4.4.2 martin * apei_interpret(I, action, cookie)
405 1.4.4.2 martin *
406 1.4.4.2 martin * Run the instructions associated with the given action by
407 1.4.4.2 martin * calling the interpreter's instfunc for each one.
408 1.4.4.2 martin *
409 1.4.4.2 martin * Halt when the instruction pointer runs past the end of the
410 1.4.4.2 martin * array, or after 1000 cycles, whichever comes first.
411 1.4.4.2 martin */
412 1.4.4.2 martin void
413 1.4.4.2 martin apei_interpret(struct apei_interp *I, unsigned action, void *cookie)
414 1.4.4.2 martin {
415 1.4.4.2 martin unsigned juice = 1000;
416 1.4.4.2 martin uint32_t ip = 0;
417 1.4.4.2 martin
418 1.4.4.2 martin if (action > I->nact || I->actname[action] == NULL)
419 1.4.4.2 martin return;
420 1.4.4.2 martin struct apei_actinst *const A = &I->actinst[action];
421 1.4.4.2 martin if (A->disable)
422 1.4.4.2 martin return;
423 1.4.4.2 martin
424 1.4.4.2 martin while (ip < A->ninst && juice --> 0) {
425 1.4.4.2 martin ACPI_WHEA_HEADER *const E = A->inst[ip].header;
426 1.4.4.2 martin struct apei_mapreg *const map = A->inst[ip].map;
427 1.4.4.2 martin
428 1.4.4.2 martin ip++;
429 1.4.4.2 martin (*I->instfunc)(E, map, cookie, &ip, A->ninst);
430 1.4.4.2 martin }
431 1.4.4.2 martin }
432