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