1 1.3 riastrad /* $NetBSD: apei_erst.c,v 1.3 2024/03/22 20:48:14 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.3 riastrad __KERNEL_RCSID(0, "$NetBSD: apei_erst.c,v 1.3 2024/03/22 20:48:14 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 380 1.1 riastrad /* 381 1.1 riastrad * Abbreviate some of the intermediate quantities to make the 382 1.1 riastrad * instruction logic conciser and more legible. 383 1.1 riastrad */ 384 1.1 riastrad const uint8_t BitOffset = header->RegisterRegion.BitOffset; 385 1.1 riastrad const uint64_t Mask = header->Mask; 386 1.1 riastrad const uint64_t Value = header->Value; 387 1.1 riastrad ACPI_GENERIC_ADDRESS *const reg = &header->RegisterRegion; 388 1.1 riastrad const bool preserve_register = header->Flags & ACPI_ERST_PRESERVE; 389 1.1 riastrad 390 1.1 riastrad aprint_debug_dev(M->sc->sc_dev, "%s: instr=0x%02"PRIx8 391 1.1 riastrad " (%s)" 392 1.1 riastrad " Address=0x%"PRIx64 393 1.1 riastrad " BitOffset=%"PRIu8" Mask=0x%"PRIx64" Value=0x%"PRIx64 394 1.1 riastrad " Flags=0x%"PRIx8"\n", 395 1.1 riastrad __func__, header->Instruction, 396 1.1 riastrad (header->Instruction < __arraycount(apei_erst_instruction) 397 1.1 riastrad ? apei_erst_instruction[header->Instruction] 398 1.1 riastrad : "unknown"), 399 1.1 riastrad reg->Address, 400 1.1 riastrad BitOffset, Mask, Value, 401 1.1 riastrad header->Flags); 402 1.1 riastrad 403 1.1 riastrad /* 404 1.1 riastrad * Zero-initialize the output by default. 405 1.1 riastrad */ 406 1.1 riastrad M->y = 0; 407 1.1 riastrad 408 1.1 riastrad /* 409 1.1 riastrad * Dispatch the instruction. 410 1.1 riastrad */ 411 1.1 riastrad switch (header->Instruction) { 412 1.1 riastrad case ACPI_ERST_READ_REGISTER: 413 1.3 riastrad M->y = apei_read_register(reg, map, Mask); 414 1.1 riastrad break; 415 1.1 riastrad case ACPI_ERST_READ_REGISTER_VALUE: { 416 1.1 riastrad uint64_t v; 417 1.1 riastrad 418 1.3 riastrad v = apei_read_register(reg, map, Mask); 419 1.1 riastrad M->y = (v == Value ? 1 : 0); 420 1.1 riastrad break; 421 1.1 riastrad } 422 1.1 riastrad case ACPI_ERST_WRITE_REGISTER: 423 1.3 riastrad apei_write_register(reg, map, Mask, preserve_register, M->x); 424 1.1 riastrad break; 425 1.1 riastrad case ACPI_ERST_WRITE_REGISTER_VALUE: 426 1.3 riastrad apei_write_register(reg, map, Mask, preserve_register, Value); 427 1.1 riastrad break; 428 1.1 riastrad case ACPI_ERST_NOOP: 429 1.1 riastrad break; 430 1.1 riastrad case ACPI_ERST_LOAD_VAR1: 431 1.3 riastrad M->var1 = apei_read_register(reg, map, Mask); 432 1.1 riastrad break; 433 1.1 riastrad case ACPI_ERST_LOAD_VAR2: 434 1.3 riastrad M->var2 = apei_read_register(reg, map, Mask); 435 1.1 riastrad break; 436 1.1 riastrad case ACPI_ERST_STORE_VAR1: 437 1.3 riastrad apei_write_register(reg, map, Mask, preserve_register, 438 1.1 riastrad M->var1); 439 1.1 riastrad break; 440 1.1 riastrad case ACPI_ERST_ADD: 441 1.1 riastrad M->var1 += M->var2; 442 1.1 riastrad break; 443 1.1 riastrad case ACPI_ERST_SUBTRACT: 444 1.1 riastrad /* 445 1.1 riastrad * The specification at 446 1.1 riastrad * https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#serialization-instructions 447 1.1 riastrad * says: 448 1.1 riastrad * 449 1.1 riastrad * 0x09 SUBTRACT Subtracts VAR1 from VAR2 450 1.1 riastrad * and stores the result in 451 1.1 riastrad * VAR1. 452 1.1 riastrad * 453 1.1 riastrad * So, according to the spec, this is _not_ simply 454 1.1 riastrad * 455 1.1 riastrad * M->var1 -= M->var2; 456 1.1 riastrad */ 457 1.1 riastrad M->var1 = M->var2 - M->var1; 458 1.1 riastrad break; 459 1.1 riastrad case ACPI_ERST_ADD_VALUE: { 460 1.1 riastrad uint64_t v; 461 1.1 riastrad 462 1.3 riastrad v = apei_read_register(reg, map, Mask); 463 1.1 riastrad v += Value; 464 1.3 riastrad apei_write_register(reg, map, Mask, preserve_register, v); 465 1.1 riastrad break; 466 1.1 riastrad } 467 1.1 riastrad case ACPI_ERST_SUBTRACT_VALUE: { 468 1.1 riastrad uint64_t v; 469 1.1 riastrad 470 1.3 riastrad v = apei_read_register(reg, map, Mask); 471 1.1 riastrad v -= Value; 472 1.3 riastrad apei_write_register(reg, map, Mask, preserve_register, v); 473 1.1 riastrad break; 474 1.1 riastrad } 475 1.1 riastrad case ACPI_ERST_STALL: 476 1.1 riastrad DELAY(Value); /* XXX avoid excessive delays */ 477 1.1 riastrad break; 478 1.1 riastrad case ACPI_ERST_STALL_WHILE_TRUE: 479 1.1 riastrad for (;;) { 480 1.3 riastrad if (apei_read_register(reg, map, Mask) != Value) 481 1.1 riastrad break; 482 1.1 riastrad DELAY(M->var1); 483 1.1 riastrad } 484 1.1 riastrad break; 485 1.3 riastrad case ACPI_ERST_SKIP_NEXT_IF_TRUE: 486 1.1 riastrad /* 487 1.1 riastrad * If reading the register yields Value, skip the next 488 1.1 riastrad * instruction -- unless that would run past the end of 489 1.1 riastrad * the instruction buffer. 490 1.1 riastrad */ 491 1.3 riastrad if (apei_read_register(reg, map, Mask) == Value) { 492 1.1 riastrad if (*ipp < maxip) 493 1.1 riastrad (*ipp)++; 494 1.1 riastrad } 495 1.1 riastrad break; 496 1.1 riastrad case ACPI_ERST_GOTO: 497 1.1 riastrad if (Value >= maxip) /* paranoia */ 498 1.1 riastrad *ipp = maxip; 499 1.1 riastrad else 500 1.1 riastrad *ipp = Value; 501 1.1 riastrad break; 502 1.3 riastrad case ACPI_ERST_SET_SRC_ADDRESS_BASE: 503 1.3 riastrad M->src_base = apei_read_register(reg, map, Mask); 504 1.1 riastrad break; 505 1.3 riastrad case ACPI_ERST_SET_DST_ADDRESS_BASE: 506 1.3 riastrad M->src_base = apei_read_register(reg, map, Mask); 507 1.1 riastrad break; 508 1.1 riastrad case ACPI_ERST_MOVE_DATA: { 509 1.3 riastrad const uint64_t v = apei_read_register(reg, map, Mask); 510 1.1 riastrad 511 1.2 riastrad /* 512 1.2 riastrad * XXX This might not work in nasty contexts unless we 513 1.2 riastrad * pre-allocate a virtual page for the mapping. 514 1.2 riastrad */ 515 1.1 riastrad apei_pmemmove(M->dst_base + v, M->src_base + v, M->var2); 516 1.1 riastrad break; 517 1.1 riastrad } 518 1.3 riastrad default: /* XXX unreachable */ 519 1.1 riastrad break; 520 1.1 riastrad } 521 1.1 riastrad } 522 1.1 riastrad 523 1.1 riastrad /* 524 1.1 riastrad * apei_erst_act(sc, action, x) 525 1.1 riastrad * 526 1.1 riastrad * Perform the named ERST action with input x, by stepping through 527 1.1 riastrad * all the instructions defined for the action by the ERST, and 528 1.1 riastrad * return the output. 529 1.1 riastrad */ 530 1.1 riastrad static uint64_t 531 1.1 riastrad apei_erst_act(struct apei_softc *sc, enum AcpiErstActions action, uint64_t x) 532 1.1 riastrad { 533 1.1 riastrad struct apei_erst_softc *const ssc = &sc->sc_erst; 534 1.1 riastrad struct apei_erst_machine erst_machine, *const M = &erst_machine; 535 1.1 riastrad 536 1.1 riastrad aprint_debug_dev(sc->sc_dev, "%s: action=%d (%s) input=0x%"PRIx64"\n", 537 1.1 riastrad __func__, 538 1.1 riastrad action, 539 1.1 riastrad (action < __arraycount(apei_erst_action) 540 1.1 riastrad ? apei_erst_action[action] 541 1.1 riastrad : "unknown"), 542 1.1 riastrad x); 543 1.1 riastrad 544 1.1 riastrad /* 545 1.1 riastrad * Initialize the machine to execute the action's instructions. 546 1.1 riastrad */ 547 1.1 riastrad memset(M, 0, sizeof(*M)); 548 1.1 riastrad M->sc = sc; 549 1.1 riastrad M->x = x; /* input */ 550 1.1 riastrad M->y = 0; /* output */ 551 1.1 riastrad M->var1 = 0; 552 1.1 riastrad M->var2 = 0; 553 1.1 riastrad M->src_base = 0; 554 1.1 riastrad M->dst_base = 0; 555 1.1 riastrad 556 1.1 riastrad /* 557 1.1 riastrad * Run the interpreter. 558 1.1 riastrad */ 559 1.1 riastrad apei_interpret(ssc->ssc_interp, action, M); 560 1.1 riastrad 561 1.1 riastrad /* 562 1.1 riastrad * Return the result. 563 1.1 riastrad */ 564 1.1 riastrad aprint_debug_dev(sc->sc_dev, "%s: output=0x%"PRIx64"\n", __func__, 565 1.1 riastrad M->y); 566 1.1 riastrad return M->y; 567 1.1 riastrad } 568