apei_erst.c revision 1.2 1 1.2 riastrad /* $NetBSD: apei_erst.c,v 1.2 2024/03/22 20:48:05 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 ERST -- Error Record Serialization Table
31 1.1 riastrad *
32 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#error-serialization
33 1.1 riastrad *
34 1.1 riastrad * XXX Expose this through a /dev node with ioctls and/or through a
35 1.1 riastrad * file system.
36 1.1 riastrad */
37 1.1 riastrad
38 1.1 riastrad #include <sys/cdefs.h>
39 1.2 riastrad __KERNEL_RCSID(0, "$NetBSD: apei_erst.c,v 1.2 2024/03/22 20:48:05 riastradh Exp $");
40 1.1 riastrad
41 1.1 riastrad #include <sys/param.h>
42 1.1 riastrad #include <sys/types.h>
43 1.1 riastrad
44 1.1 riastrad #include <sys/systm.h>
45 1.1 riastrad
46 1.1 riastrad #include <dev/acpi/acpivar.h>
47 1.1 riastrad #include <dev/acpi/apei_erstvar.h>
48 1.1 riastrad #include <dev/acpi/apei_interp.h>
49 1.1 riastrad #include <dev/acpi/apei_reg.h>
50 1.1 riastrad #include <dev/acpi/apeivar.h>
51 1.1 riastrad
52 1.1 riastrad #define _COMPONENT ACPI_RESOURCE_COMPONENT
53 1.1 riastrad ACPI_MODULE_NAME ("apei")
54 1.1 riastrad
55 1.1 riastrad static bool apei_erst_instvalid(ACPI_WHEA_HEADER *, uint32_t, uint32_t);
56 1.2 riastrad static void apei_erst_instfunc(ACPI_WHEA_HEADER *, struct apei_mapreg *,
57 1.2 riastrad void *, uint32_t *, uint32_t);
58 1.1 riastrad static uint64_t apei_erst_act(struct apei_softc *, enum AcpiErstActions,
59 1.1 riastrad uint64_t);
60 1.1 riastrad
61 1.1 riastrad /*
62 1.1 riastrad * apei_erst_action
63 1.1 riastrad *
64 1.1 riastrad * Symbolic names of the APEI ERST (Error Record Serialization
65 1.1 riastrad * Table) logical actions are taken (and downcased) from:
66 1.1 riastrad *
67 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#error-record-serialization-actions-table
68 1.1 riastrad */
69 1.1 riastrad static const char *const apei_erst_action[] = {
70 1.1 riastrad [ACPI_ERST_BEGIN_WRITE] = "begin_write_operation",
71 1.1 riastrad [ACPI_ERST_BEGIN_READ] = "begin_read_operation",
72 1.1 riastrad [ACPI_ERST_BEGIN_CLEAR] = "begin_clear_operation",
73 1.1 riastrad [ACPI_ERST_END] = "end_operation",
74 1.1 riastrad [ACPI_ERST_SET_RECORD_OFFSET] = "set_record_offset",
75 1.1 riastrad [ACPI_ERST_EXECUTE_OPERATION] = "execute_operation",
76 1.1 riastrad [ACPI_ERST_CHECK_BUSY_STATUS] = "check_busy_status",
77 1.1 riastrad [ACPI_ERST_GET_COMMAND_STATUS] = "get_command_status",
78 1.1 riastrad [ACPI_ERST_GET_RECORD_ID] = "get_record_identifier",
79 1.1 riastrad [ACPI_ERST_SET_RECORD_ID] = "set_record_identifier",
80 1.1 riastrad [ACPI_ERST_GET_RECORD_COUNT] = "get_record_count",
81 1.1 riastrad [ACPI_ERST_BEGIN_DUMMY_WRIITE] = "begin_dummy_write_operation",
82 1.1 riastrad [ACPI_ERST_NOT_USED] = "reserved",
83 1.1 riastrad [ACPI_ERST_GET_ERROR_RANGE] = "get_error_log_address_range",
84 1.1 riastrad [ACPI_ERST_GET_ERROR_LENGTH] = "get_error_log_address_range_length",
85 1.1 riastrad [ACPI_ERST_GET_ERROR_ATTRIBUTES] =
86 1.1 riastrad "get_error_log_address_range_attributes",
87 1.1 riastrad [ACPI_ERST_EXECUTE_TIMINGS] = "get_execute_operations_timings",
88 1.1 riastrad };
89 1.1 riastrad
90 1.1 riastrad /*
91 1.1 riastrad * apei_erst_instruction
92 1.1 riastrad *
93 1.1 riastrad * Symbolic names of the APEI ERST (Error Record Serialization
94 1.1 riastrad * Table) instructions to implement logical actions are taken (and
95 1.1 riastrad * downcased) from:
96 1.1 riastrad *
97 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#serialization-instructions
98 1.1 riastrad */
99 1.1 riastrad static const char *apei_erst_instruction[] = {
100 1.1 riastrad [ACPI_ERST_READ_REGISTER] = "read_register",
101 1.1 riastrad [ACPI_ERST_READ_REGISTER_VALUE] = "read_register_value",
102 1.1 riastrad [ACPI_ERST_WRITE_REGISTER] = "write_register",
103 1.1 riastrad [ACPI_ERST_WRITE_REGISTER_VALUE] = "write_register_value",
104 1.1 riastrad [ACPI_ERST_NOOP] = "noop",
105 1.1 riastrad [ACPI_ERST_LOAD_VAR1] = "load_var1",
106 1.1 riastrad [ACPI_ERST_LOAD_VAR2] = "load_var2",
107 1.1 riastrad [ACPI_ERST_STORE_VAR1] = "store_var1",
108 1.1 riastrad [ACPI_ERST_ADD] = "add",
109 1.1 riastrad [ACPI_ERST_SUBTRACT] = "subtract",
110 1.1 riastrad [ACPI_ERST_ADD_VALUE] = "add_value",
111 1.1 riastrad [ACPI_ERST_SUBTRACT_VALUE] = "subtract_value",
112 1.1 riastrad [ACPI_ERST_STALL] = "stall",
113 1.1 riastrad [ACPI_ERST_STALL_WHILE_TRUE] = "stall_while_true",
114 1.1 riastrad [ACPI_ERST_SKIP_NEXT_IF_TRUE] = "skip_next_instruction_if_true",
115 1.1 riastrad [ACPI_ERST_GOTO] = "goto",
116 1.1 riastrad [ACPI_ERST_SET_SRC_ADDRESS_BASE] = "set_src_address_base",
117 1.1 riastrad [ACPI_ERST_SET_DST_ADDRESS_BASE] = "set_dst_address_base",
118 1.1 riastrad [ACPI_ERST_MOVE_DATA] = "move_data",
119 1.1 riastrad };
120 1.1 riastrad
121 1.1 riastrad /*
122 1.2 riastrad * apei_erst_instreg
123 1.2 riastrad *
124 1.2 riastrad * Table of which isntructions use a register operand.
125 1.2 riastrad *
126 1.2 riastrad * Must match apei_erst_instfunc.
127 1.2 riastrad */
128 1.2 riastrad static const bool apei_erst_instreg[] = {
129 1.2 riastrad [ACPI_ERST_READ_REGISTER] = true,
130 1.2 riastrad [ACPI_ERST_READ_REGISTER_VALUE] = true,
131 1.2 riastrad [ACPI_ERST_WRITE_REGISTER] = true,
132 1.2 riastrad [ACPI_ERST_WRITE_REGISTER_VALUE] = true,
133 1.2 riastrad [ACPI_ERST_NOOP] = false,
134 1.2 riastrad [ACPI_ERST_LOAD_VAR1] = true,
135 1.2 riastrad [ACPI_ERST_LOAD_VAR2] = true,
136 1.2 riastrad [ACPI_ERST_STORE_VAR1] = true,
137 1.2 riastrad [ACPI_ERST_ADD] = false,
138 1.2 riastrad [ACPI_ERST_SUBTRACT] = false,
139 1.2 riastrad [ACPI_ERST_ADD_VALUE] = true,
140 1.2 riastrad [ACPI_ERST_SUBTRACT_VALUE] = true,
141 1.2 riastrad [ACPI_ERST_STALL] = false,
142 1.2 riastrad [ACPI_ERST_STALL_WHILE_TRUE] = true,
143 1.2 riastrad [ACPI_ERST_SKIP_NEXT_IF_TRUE] = true,
144 1.2 riastrad [ACPI_ERST_GOTO] = false,
145 1.2 riastrad [ACPI_ERST_SET_SRC_ADDRESS_BASE] = true,
146 1.2 riastrad [ACPI_ERST_SET_DST_ADDRESS_BASE] = true,
147 1.2 riastrad [ACPI_ERST_MOVE_DATA] = true,
148 1.2 riastrad };
149 1.2 riastrad
150 1.2 riastrad /*
151 1.1 riastrad * XXX dtrace and kernhist
152 1.1 riastrad */
153 1.1 riastrad static void
154 1.1 riastrad apei_pmemmove(uint64_t pdst, uint64_t psrc, uint64_t nbytes)
155 1.1 riastrad {
156 1.1 riastrad char *vdst, *vsrc;
157 1.1 riastrad
158 1.1 riastrad aprint_debug("ERST: move"
159 1.1 riastrad " %"PRIu64" bytes from 0x%"PRIx64" to 0x%"PRIx64"\n",
160 1.1 riastrad nbytes, psrc, pdst);
161 1.1 riastrad
162 1.1 riastrad /*
163 1.1 riastrad * Carefully check for overlap.
164 1.1 riastrad */
165 1.1 riastrad if (pdst == psrc) {
166 1.1 riastrad /*
167 1.1 riastrad * Technically this could happen, I guess!
168 1.1 riastrad */
169 1.1 riastrad return;
170 1.1 riastrad } else if (pdst < psrc && psrc < pdst + nbytes) {
171 1.1 riastrad /*
172 1.1 riastrad * psrc ______ psrc + nbytes
173 1.1 riastrad * / \
174 1.1 riastrad * <--------------------->
175 1.1 riastrad * \______/
176 1.1 riastrad * pdst pdst + nbytes
177 1.1 riastrad */
178 1.1 riastrad vdst = AcpiOsMapMemory(pdst, nbytes + (psrc - pdst));
179 1.1 riastrad vsrc = vdst + (psrc - pdst);
180 1.1 riastrad memmove(vdst, vsrc, nbytes);
181 1.1 riastrad AcpiOsUnmapMemory(vdst, nbytes + (psrc - pdst));
182 1.1 riastrad } else if (psrc < pdst && pdst < psrc + nbytes) {
183 1.1 riastrad /*
184 1.1 riastrad * psrc ______ psrc + nbytes
185 1.1 riastrad * / \
186 1.1 riastrad * <--------------------->
187 1.1 riastrad * \______/
188 1.1 riastrad * pdst pdst + nbytes
189 1.1 riastrad */
190 1.1 riastrad vsrc = AcpiOsMapMemory(psrc, nbytes + (pdst - psrc));
191 1.1 riastrad vdst = vsrc + (pdst - psrc);
192 1.1 riastrad memmove(vdst, vsrc, nbytes);
193 1.1 riastrad AcpiOsUnmapMemory(vsrc, nbytes + (pdst - psrc));
194 1.1 riastrad } else {
195 1.1 riastrad /*
196 1.1 riastrad * No overlap.
197 1.1 riastrad */
198 1.1 riastrad vdst = AcpiOsMapMemory(pdst, nbytes);
199 1.1 riastrad vsrc = AcpiOsMapMemory(psrc, nbytes);
200 1.1 riastrad memcpy(vdst, vsrc, nbytes);
201 1.1 riastrad AcpiOsUnmapMemory(vsrc, nbytes);
202 1.1 riastrad AcpiOsUnmapMemory(vdst, nbytes);
203 1.1 riastrad }
204 1.1 riastrad }
205 1.1 riastrad
206 1.1 riastrad /*
207 1.1 riastrad * apei_erst_attach(sc)
208 1.1 riastrad *
209 1.1 riastrad * Scan the Error Record Serialization Table to collate the
210 1.1 riastrad * instructions for each ERST action.
211 1.1 riastrad */
212 1.1 riastrad void
213 1.1 riastrad apei_erst_attach(struct apei_softc *sc)
214 1.1 riastrad {
215 1.1 riastrad ACPI_TABLE_ERST *erst = sc->sc_tab.erst;
216 1.1 riastrad struct apei_erst_softc *ssc = &sc->sc_erst;
217 1.1 riastrad ACPI_ERST_ENTRY *entry;
218 1.1 riastrad uint32_t i, nentries, maxnentries;
219 1.1 riastrad
220 1.1 riastrad /*
221 1.1 riastrad * Verify the table length, table header length, and
222 1.1 riastrad * instruction entry count are all sensible. If the header is
223 1.1 riastrad * truncated, stop here; if the entries are truncated, stop at
224 1.1 riastrad * the largest integral number of full entries that fits.
225 1.1 riastrad */
226 1.1 riastrad if (erst->Header.Length < sizeof(*erst)) {
227 1.1 riastrad aprint_error_dev(sc->sc_dev, "ERST: truncated table:"
228 1.1 riastrad " %"PRIu32" < %zu minimum bytes\n",
229 1.1 riastrad erst->Header.Length, sizeof(*erst));
230 1.1 riastrad return;
231 1.1 riastrad }
232 1.1 riastrad if (erst->HeaderLength <
233 1.1 riastrad sizeof(*erst) - offsetof(ACPI_TABLE_ERST, HeaderLength)) {
234 1.1 riastrad aprint_error_dev(sc->sc_dev, "ERST: truncated header:"
235 1.1 riastrad " %"PRIu32" < %zu bytes\n",
236 1.1 riastrad erst->HeaderLength,
237 1.1 riastrad sizeof(*erst) - offsetof(ACPI_TABLE_ERST, HeaderLength));
238 1.1 riastrad return;
239 1.1 riastrad }
240 1.1 riastrad nentries = erst->Entries;
241 1.1 riastrad maxnentries = (erst->Header.Length - sizeof(*erst))/sizeof(*entry);
242 1.1 riastrad if (nentries > maxnentries) {
243 1.1 riastrad aprint_error_dev(sc->sc_dev, "ERST: excessive entries:"
244 1.1 riastrad " %"PRIu32", truncating to %"PRIu32"\n",
245 1.1 riastrad nentries, maxnentries);
246 1.1 riastrad nentries = maxnentries;
247 1.1 riastrad }
248 1.1 riastrad if (nentries*sizeof(*entry) < erst->Header.Length - sizeof(*erst)) {
249 1.1 riastrad aprint_error_dev(sc->sc_dev, "ERST:"
250 1.1 riastrad " %zu bytes of trailing garbage after last entry\n",
251 1.1 riastrad erst->Header.Length - nentries*sizeof(*entry));
252 1.1 riastrad }
253 1.1 riastrad
254 1.1 riastrad /*
255 1.1 riastrad * Create an interpreter for ERST actions.
256 1.1 riastrad */
257 1.1 riastrad ssc->ssc_interp = apei_interp_create("ERST",
258 1.1 riastrad apei_erst_action, __arraycount(apei_erst_action),
259 1.1 riastrad apei_erst_instruction, __arraycount(apei_erst_instruction),
260 1.2 riastrad apei_erst_instreg, apei_erst_instvalid, apei_erst_instfunc);
261 1.1 riastrad
262 1.1 riastrad /*
263 1.1 riastrad * Compile the interpreter from the ERST action instruction
264 1.1 riastrad * table.
265 1.1 riastrad */
266 1.1 riastrad entry = (ACPI_ERST_ENTRY *)(erst + 1);
267 1.1 riastrad for (i = 0; i < nentries; i++, entry++)
268 1.1 riastrad apei_interp_pass1_load(ssc->ssc_interp, i, &entry->WheaHeader);
269 1.1 riastrad entry = (ACPI_ERST_ENTRY *)(erst + 1);
270 1.1 riastrad for (i = 0; i < nentries; i++, entry++) {
271 1.1 riastrad apei_interp_pass2_verify(ssc->ssc_interp, i,
272 1.1 riastrad &entry->WheaHeader);
273 1.1 riastrad }
274 1.1 riastrad apei_interp_pass3_alloc(ssc->ssc_interp);
275 1.1 riastrad entry = (ACPI_ERST_ENTRY *)(erst + 1);
276 1.1 riastrad for (i = 0; i < nentries; i++, entry++) {
277 1.1 riastrad apei_interp_pass4_assemble(ssc->ssc_interp, i,
278 1.1 riastrad &entry->WheaHeader);
279 1.1 riastrad }
280 1.1 riastrad apei_interp_pass5_verify(ssc->ssc_interp);
281 1.1 riastrad
282 1.1 riastrad /*
283 1.1 riastrad * Print some basic information about the stored records.
284 1.1 riastrad */
285 1.1 riastrad uint64_t logaddr = apei_erst_act(sc, ACPI_ERST_GET_ERROR_RANGE, 0);
286 1.1 riastrad uint64_t logbytes = apei_erst_act(sc, ACPI_ERST_GET_ERROR_LENGTH, 0);
287 1.1 riastrad uint64_t attr = apei_erst_act(sc, ACPI_ERST_GET_ERROR_ATTRIBUTES, 0);
288 1.1 riastrad uint64_t nrecords = apei_erst_act(sc, ACPI_ERST_GET_RECORD_COUNT, 0);
289 1.1 riastrad char attrbuf[128];
290 1.1 riastrad
291 1.1 riastrad /* XXX define this format somewhere */
292 1.1 riastrad snprintb(attrbuf, sizeof(attrbuf), "\177\020"
293 1.1 riastrad "\001" "NVRAM\0"
294 1.1 riastrad "\002" "SLOW\0"
295 1.1 riastrad "\0", attr);
296 1.1 riastrad
297 1.1 riastrad aprint_normal_dev(sc->sc_dev, "ERST: %"PRIu64" records in error log"
298 1.1 riastrad " %"PRIu64" bytes @ 0x%"PRIx64" attr=%s\n",
299 1.1 riastrad nrecords, logbytes, logaddr, attrbuf);
300 1.1 riastrad
301 1.1 riastrad /*
302 1.1 riastrad * XXX wire up to sysctl or a file system or something, and/or
303 1.1 riastrad * dmesg or crash dumps
304 1.1 riastrad */
305 1.1 riastrad }
306 1.1 riastrad
307 1.1 riastrad /*
308 1.1 riastrad * apei_erst_detach(sc)
309 1.1 riastrad *
310 1.1 riastrad * Free software resource allocated for ERST handling.
311 1.1 riastrad */
312 1.1 riastrad void
313 1.1 riastrad apei_erst_detach(struct apei_softc *sc)
314 1.1 riastrad {
315 1.1 riastrad struct apei_erst_softc *ssc = &sc->sc_erst;
316 1.1 riastrad
317 1.1 riastrad if (ssc->ssc_interp) {
318 1.1 riastrad apei_interp_destroy(ssc->ssc_interp);
319 1.1 riastrad ssc->ssc_interp = NULL;
320 1.1 riastrad }
321 1.1 riastrad }
322 1.1 riastrad
323 1.1 riastrad /*
324 1.1 riastrad * apei_erst_instvalid(header, ninst, i)
325 1.1 riastrad *
326 1.1 riastrad * Routine to validate the ith entry, for an action with ninst
327 1.1 riastrad * instructions.
328 1.1 riastrad */
329 1.1 riastrad static bool
330 1.1 riastrad apei_erst_instvalid(ACPI_WHEA_HEADER *header, uint32_t ninst, uint32_t i)
331 1.1 riastrad {
332 1.1 riastrad
333 1.1 riastrad switch (header->Instruction) {
334 1.1 riastrad case ACPI_ERST_GOTO:
335 1.1 riastrad if (header->Value > ninst) {
336 1.1 riastrad aprint_error("ERST[%"PRIu32"]:"
337 1.1 riastrad " GOTO(%"PRIu64") out of bounds,"
338 1.1 riastrad " disabling action %"PRIu32" (%s)\n", i,
339 1.1 riastrad header->Value,
340 1.1 riastrad header->Action,
341 1.1 riastrad apei_erst_action[header->Action]);
342 1.1 riastrad return false;
343 1.1 riastrad }
344 1.1 riastrad }
345 1.1 riastrad return true;
346 1.1 riastrad }
347 1.1 riastrad
348 1.1 riastrad /*
349 1.1 riastrad * struct apei_erst_machine
350 1.1 riastrad *
351 1.1 riastrad * Machine state for executing ERST instructions.
352 1.1 riastrad */
353 1.1 riastrad struct apei_erst_machine {
354 1.1 riastrad struct apei_softc *sc;
355 1.1 riastrad uint64_t x; /* in */
356 1.1 riastrad uint64_t y; /* out */
357 1.1 riastrad uint64_t var1;
358 1.1 riastrad uint64_t var2;
359 1.1 riastrad uint64_t src_base;
360 1.1 riastrad uint64_t dst_base;
361 1.1 riastrad };
362 1.1 riastrad
363 1.1 riastrad /*
364 1.2 riastrad * apei_erst_instfunc(header, map, cookie, &ip, maxip)
365 1.1 riastrad *
366 1.1 riastrad * Run a single instruction in the service of performing an ERST
367 1.1 riastrad * action. Updates the ERST machine at cookie, and the ip if
368 1.1 riastrad * necessary, in place.
369 1.1 riastrad *
370 1.1 riastrad * On entry, ip points to the next instruction after this one
371 1.1 riastrad * sequentially; on exit, ip points to the next instruction to
372 1.1 riastrad * execute.
373 1.1 riastrad */
374 1.1 riastrad static void
375 1.2 riastrad apei_erst_instfunc(ACPI_WHEA_HEADER *header, struct apei_mapreg *map,
376 1.2 riastrad void *cookie, uint32_t *ipp, uint32_t maxip)
377 1.1 riastrad {
378 1.1 riastrad struct apei_erst_machine *const M = cookie;
379 1.1 riastrad ACPI_STATUS rv = AE_OK;
380 1.1 riastrad
381 1.1 riastrad /*
382 1.1 riastrad * Abbreviate some of the intermediate quantities to make the
383 1.1 riastrad * instruction logic conciser and more legible.
384 1.1 riastrad */
385 1.1 riastrad const uint8_t BitOffset = header->RegisterRegion.BitOffset;
386 1.1 riastrad const uint64_t Mask = header->Mask;
387 1.1 riastrad const uint64_t Value = header->Value;
388 1.1 riastrad ACPI_GENERIC_ADDRESS *const reg = &header->RegisterRegion;
389 1.1 riastrad const bool preserve_register = header->Flags & ACPI_ERST_PRESERVE;
390 1.1 riastrad
391 1.1 riastrad aprint_debug_dev(M->sc->sc_dev, "%s: instr=0x%02"PRIx8
392 1.1 riastrad " (%s)"
393 1.1 riastrad " Address=0x%"PRIx64
394 1.1 riastrad " BitOffset=%"PRIu8" Mask=0x%"PRIx64" Value=0x%"PRIx64
395 1.1 riastrad " Flags=0x%"PRIx8"\n",
396 1.1 riastrad __func__, header->Instruction,
397 1.1 riastrad (header->Instruction < __arraycount(apei_erst_instruction)
398 1.1 riastrad ? apei_erst_instruction[header->Instruction]
399 1.1 riastrad : "unknown"),
400 1.1 riastrad reg->Address,
401 1.1 riastrad BitOffset, Mask, Value,
402 1.1 riastrad header->Flags);
403 1.1 riastrad
404 1.1 riastrad /*
405 1.1 riastrad * Zero-initialize the output by default.
406 1.1 riastrad */
407 1.1 riastrad M->y = 0;
408 1.1 riastrad
409 1.1 riastrad /*
410 1.1 riastrad * Dispatch the instruction.
411 1.1 riastrad */
412 1.1 riastrad switch (header->Instruction) {
413 1.1 riastrad case ACPI_ERST_READ_REGISTER:
414 1.2 riastrad rv = apei_read_register(reg, map, Mask, &M->y);
415 1.1 riastrad break;
416 1.1 riastrad case ACPI_ERST_READ_REGISTER_VALUE: {
417 1.1 riastrad uint64_t v;
418 1.1 riastrad
419 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
420 1.1 riastrad if (ACPI_FAILURE(rv))
421 1.1 riastrad break;
422 1.1 riastrad M->y = (v == Value ? 1 : 0);
423 1.1 riastrad break;
424 1.1 riastrad }
425 1.1 riastrad case ACPI_ERST_WRITE_REGISTER:
426 1.2 riastrad rv = apei_write_register(reg, map, Mask, preserve_register,
427 1.2 riastrad M->x);
428 1.1 riastrad break;
429 1.1 riastrad case ACPI_ERST_WRITE_REGISTER_VALUE:
430 1.2 riastrad rv = apei_write_register(reg, map, Mask, preserve_register,
431 1.2 riastrad Value);
432 1.1 riastrad break;
433 1.1 riastrad case ACPI_ERST_NOOP:
434 1.1 riastrad break;
435 1.1 riastrad case ACPI_ERST_LOAD_VAR1:
436 1.2 riastrad rv = apei_read_register(reg, map, Mask, &M->var1);
437 1.1 riastrad break;
438 1.1 riastrad case ACPI_ERST_LOAD_VAR2:
439 1.2 riastrad rv = apei_read_register(reg, map, Mask, &M->var2);
440 1.1 riastrad break;
441 1.1 riastrad case ACPI_ERST_STORE_VAR1:
442 1.2 riastrad rv = apei_write_register(reg, map, Mask, preserve_register,
443 1.1 riastrad M->var1);
444 1.1 riastrad break;
445 1.1 riastrad case ACPI_ERST_ADD:
446 1.1 riastrad M->var1 += M->var2;
447 1.1 riastrad break;
448 1.1 riastrad case ACPI_ERST_SUBTRACT:
449 1.1 riastrad /*
450 1.1 riastrad * The specification at
451 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#serialization-instructions
452 1.1 riastrad * says:
453 1.1 riastrad *
454 1.1 riastrad * 0x09 SUBTRACT Subtracts VAR1 from VAR2
455 1.1 riastrad * and stores the result in
456 1.1 riastrad * VAR1.
457 1.1 riastrad *
458 1.1 riastrad * So, according to the spec, this is _not_ simply
459 1.1 riastrad *
460 1.1 riastrad * M->var1 -= M->var2;
461 1.1 riastrad */
462 1.1 riastrad M->var1 = M->var2 - M->var1;
463 1.1 riastrad break;
464 1.1 riastrad case ACPI_ERST_ADD_VALUE: {
465 1.1 riastrad uint64_t v;
466 1.1 riastrad
467 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
468 1.1 riastrad if (ACPI_FAILURE(rv))
469 1.1 riastrad break;
470 1.1 riastrad
471 1.1 riastrad v += Value;
472 1.1 riastrad
473 1.2 riastrad rv = apei_write_register(reg, map, Mask, preserve_register, v);
474 1.1 riastrad break;
475 1.1 riastrad }
476 1.1 riastrad case ACPI_ERST_SUBTRACT_VALUE: {
477 1.1 riastrad uint64_t v;
478 1.1 riastrad
479 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
480 1.1 riastrad if (ACPI_FAILURE(rv))
481 1.1 riastrad break;
482 1.1 riastrad
483 1.1 riastrad v -= Value;
484 1.1 riastrad
485 1.2 riastrad rv = apei_write_register(reg, map, Mask, preserve_register, v);
486 1.1 riastrad break;
487 1.1 riastrad }
488 1.1 riastrad case ACPI_ERST_STALL:
489 1.1 riastrad DELAY(Value); /* XXX avoid excessive delays */
490 1.1 riastrad break;
491 1.1 riastrad case ACPI_ERST_STALL_WHILE_TRUE:
492 1.1 riastrad for (;;) {
493 1.1 riastrad uint64_t v;
494 1.1 riastrad
495 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
496 1.1 riastrad if (ACPI_FAILURE(rv))
497 1.1 riastrad break;
498 1.1 riastrad if (v != Value)
499 1.1 riastrad break;
500 1.1 riastrad DELAY(M->var1);
501 1.1 riastrad }
502 1.1 riastrad break;
503 1.1 riastrad case ACPI_ERST_SKIP_NEXT_IF_TRUE: {
504 1.1 riastrad uint64_t v;
505 1.1 riastrad
506 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
507 1.1 riastrad if (ACPI_FAILURE(rv))
508 1.1 riastrad break;
509 1.1 riastrad
510 1.1 riastrad /*
511 1.1 riastrad * If reading the register yields Value, skip the next
512 1.1 riastrad * instruction -- unless that would run past the end of
513 1.1 riastrad * the instruction buffer.
514 1.1 riastrad */
515 1.1 riastrad if (v == Value) {
516 1.1 riastrad if (*ipp < maxip)
517 1.1 riastrad (*ipp)++;
518 1.1 riastrad }
519 1.1 riastrad break;
520 1.1 riastrad }
521 1.1 riastrad case ACPI_ERST_GOTO:
522 1.1 riastrad if (Value >= maxip) /* paranoia */
523 1.1 riastrad *ipp = maxip;
524 1.1 riastrad else
525 1.1 riastrad *ipp = Value;
526 1.1 riastrad break;
527 1.1 riastrad case ACPI_ERST_SET_SRC_ADDRESS_BASE: {
528 1.1 riastrad uint64_t v;
529 1.1 riastrad
530 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
531 1.1 riastrad if (ACPI_FAILURE(rv))
532 1.1 riastrad break;
533 1.1 riastrad M->src_base = v;
534 1.1 riastrad break;
535 1.1 riastrad }
536 1.1 riastrad case ACPI_ERST_SET_DST_ADDRESS_BASE: {
537 1.1 riastrad uint64_t v;
538 1.1 riastrad
539 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
540 1.1 riastrad if (ACPI_FAILURE(rv))
541 1.1 riastrad break;
542 1.1 riastrad M->src_base = v;
543 1.1 riastrad break;
544 1.1 riastrad }
545 1.1 riastrad case ACPI_ERST_MOVE_DATA: {
546 1.1 riastrad uint64_t v;
547 1.1 riastrad
548 1.2 riastrad rv = apei_read_register(reg, map, Mask, &v);
549 1.1 riastrad if (ACPI_FAILURE(rv))
550 1.1 riastrad break;
551 1.2 riastrad /*
552 1.2 riastrad * XXX This might not work in nasty contexts unless we
553 1.2 riastrad * pre-allocate a virtual page for the mapping.
554 1.2 riastrad */
555 1.1 riastrad apei_pmemmove(M->dst_base + v, M->src_base + v, M->var2);
556 1.1 riastrad break;
557 1.1 riastrad }
558 1.1 riastrad default:
559 1.1 riastrad break;
560 1.1 riastrad }
561 1.1 riastrad
562 1.1 riastrad /*
563 1.1 riastrad * If any register I/O failed, print the failure message. This
564 1.1 riastrad * could be more specific about exactly what failed, but that
565 1.1 riastrad * takes a little more effort to write.
566 1.1 riastrad */
567 1.1 riastrad if (ACPI_FAILURE(rv)) {
568 1.1 riastrad aprint_debug_dev(M->sc->sc_dev, "%s: failed: %s\n", __func__,
569 1.1 riastrad AcpiFormatException(rv));
570 1.1 riastrad }
571 1.1 riastrad }
572 1.1 riastrad
573 1.1 riastrad /*
574 1.1 riastrad * apei_erst_act(sc, action, x)
575 1.1 riastrad *
576 1.1 riastrad * Perform the named ERST action with input x, by stepping through
577 1.1 riastrad * all the instructions defined for the action by the ERST, and
578 1.1 riastrad * return the output.
579 1.1 riastrad */
580 1.1 riastrad static uint64_t
581 1.1 riastrad apei_erst_act(struct apei_softc *sc, enum AcpiErstActions action, uint64_t x)
582 1.1 riastrad {
583 1.1 riastrad struct apei_erst_softc *const ssc = &sc->sc_erst;
584 1.1 riastrad struct apei_erst_machine erst_machine, *const M = &erst_machine;
585 1.1 riastrad
586 1.1 riastrad aprint_debug_dev(sc->sc_dev, "%s: action=%d (%s) input=0x%"PRIx64"\n",
587 1.1 riastrad __func__,
588 1.1 riastrad action,
589 1.1 riastrad (action < __arraycount(apei_erst_action)
590 1.1 riastrad ? apei_erst_action[action]
591 1.1 riastrad : "unknown"),
592 1.1 riastrad x);
593 1.1 riastrad
594 1.1 riastrad /*
595 1.1 riastrad * Initialize the machine to execute the action's instructions.
596 1.1 riastrad */
597 1.1 riastrad memset(M, 0, sizeof(*M));
598 1.1 riastrad M->sc = sc;
599 1.1 riastrad M->x = x; /* input */
600 1.1 riastrad M->y = 0; /* output */
601 1.1 riastrad M->var1 = 0;
602 1.1 riastrad M->var2 = 0;
603 1.1 riastrad M->src_base = 0;
604 1.1 riastrad M->dst_base = 0;
605 1.1 riastrad
606 1.1 riastrad /*
607 1.1 riastrad * Run the interpreter.
608 1.1 riastrad */
609 1.1 riastrad apei_interpret(ssc->ssc_interp, action, M);
610 1.1 riastrad
611 1.1 riastrad /*
612 1.1 riastrad * Return the result.
613 1.1 riastrad */
614 1.1 riastrad aprint_debug_dev(sc->sc_dev, "%s: output=0x%"PRIx64"\n", __func__,
615 1.1 riastrad M->y);
616 1.1 riastrad return M->y;
617 1.1 riastrad }
618