/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* APEI action interpreter.
*
* APEI provides a generalized abstraction to implement the actions an
* OS must take to inject an error, or save state in a persistent error
* record for the next boot, since there are many different hardware
* register interfaces for, e.g., injecting errors.
*
* You might think that APEI, being part of ACPI, would use the usual
* ACPI interpreter to run ACPI methods for these actions. You would
* be wrong. Alas.
*
* Instead, there is an entirely different little language of actions
* that an OS must write programs in to inject errors, and an entirely
* different little language of instructions that the interpreter for
* the actions uses to interpret the OS's error injection program. Got
* it?
*
* The EINJ and ERST tables provide a series entries that look like:
*
* +-----------------------------------------------+
* | Action=SET_ERROR_TYPE |
* | Instruction=SKIP_NEXT_INSTRUCTION_IF_TRUE |
* | Register=0x7fabcd10 [memory] |
* | Value=0xdeadbeef |
* +-----------------------------------------------+
* | Action=SET_ERROR_TYPE |
* | Instruction=WRITE_REGISTER_VALUE |
* | Register=0x7fabcd14 [memory] |
* | Value=1 |
* +-----------------------------------------------+
* | Action=SET_ERROR_TYPE |
* | Instruction=READ_REGISTER |
* | Register=0x7fabcd1c [memory] |
* +-----------------------------------------------+
* | Action=SET_ERROR_TYPE |
* | Instruction=WRITE_REGISTER |
* | Register=0x7fabcd20 [memory] |
* +-----------------------------------------------+
* | Action=EXECUTE_OPERATION |
* | Instruction=LOAD_VAR1 |
* | Register=0x7fabcf00 [memory] |
* +-----------------------------------------------+
* | Action=SET_ERROR_TYPE |
* | Instruction=WRITE_REGISTER_VALUE |
* | Register=0x7fabcd24 [memory] |
* | Value=42 |
* +-----------------------------------------------+
* | ... |
* +-----------------------------------------------+
*
* The entries tell the OS, for each action the OS might want to
* perform like BEGIN_INJECTION_OPERATION or SET_ERROR_TYPE or
* EXECUTE_OPERATION, what instructions must be executed and in what
* order.
*
* The instructions run in one of two little state machines -- there's
* a different instruction set for EINJ and ERST -- and vary from noops
* to reading and writing registers to arithmetic on registers to
* conditional and unconditional branches.
*
* Yes, that means this little language -- the ERST language, anyway,
* not the EINJ language -- is Turing-complete.
*
* This APEI interpreter first compiles the table into a contiguous
* sequence of instructions for each action, to make execution easier,
* since there's no requirement that the instructions for an action be
* contiguous in the table, and the GOTO instruction relies on
* contiguous indexing of the instructions for an action.
*
* This interpreter also does a little validation so the firmware
* doesn't, e.g., GOTO somewhere in oblivion. The validation is mainly
* a convenience for catching mistakes in firmware, not a security
* measure, since the OS is absolutely vulnerable to malicious firmware
* anyway.
*/
/*
* apei_interp_pass1_load(I, i, E)
*
* Load the ith table entry E into the interpreter I. To be
* called for each entry in the table sequentially.
*
* This first pass counts the number of instructions for each
* action, so we can allocate an array of instructions for
* indexing each action.
*/
void
apei_interp_pass1_load(struct apei_interp *I, uint32_t i,
ACPI_WHEA_HEADER *E)
{
/*
* If we don't recognize this action, ignore it and move on.
*/
if (E->Action >= I->nact || I->actname[E->Action] == NULL) {
aprint_error("%s[%"PRIu32"]: unknown action: 0x%"PRIx8"\n",
I->name, i, E->Action);
return;
}
struct apei_actinst *const A = &I->actinst[E->Action];
/*
* If we can't interpret this instruction for this action, or
* if we couldn't interpret a previous instruction for this
* action, disable this action and move on.
*/
if (E->Instruction >= I->ninst ||
I->instname[E->Instruction] == NULL) {
aprint_error("%s[%"PRIu32"]: unknown instruction: 0x%02"PRIx8
"\n", I->name, i, E->Instruction);
A->ninst = 0;
A->disable = true;
return;
}
if (A->disable)
return;
/*
* Count another instruction. We will make a pointer
* to it in a later pass.
*/
A->ninst++;
/*
* If it overflows a reasonable size, disable the action
* altogether.
*/
if (A->ninst >= 256) {
aprint_error("%s[%"PRIu32"]:"
" too many instructions for action %"PRIu32" (%s)\n",
I->name, i,
E->Action, I->actname[E->Action]);
A->ninst = 0;
A->disable = true;
return;
}
}
/*
* apei_interp_pass2_verify(I, i, E)
*
* Verify the ith entry's instruction, using the caller's
* instvalid function, now that all the instructions have been
* counted. To be called for each entry in the table
* sequentially.
*
* This second pass checks that GOTO instructions in particular
* don't jump out of bounds.
*/
void
apei_interp_pass2_verify(struct apei_interp *I, uint32_t i,
ACPI_WHEA_HEADER *E)
{
/*
* If there's no instruction validation function, skip this
* pass.
*/
if (I->instvalid == NULL)
return;
/*
* If we skipped it in earlier passes, skip it now.
*/
if (E->Action > I->nact || I->actname[E->Action] == NULL)
return;
/*
* If the instruction is invalid, disable the whole action.
*/
struct apei_actinst *const A = &I->actinst[E->Action];
if (!(*I->instvalid)(E, A->ninst, i)) {
A->ninst = 0;
A->disable = true;
}
}
/*
* apei_interp_pass3_alloc(I)
*
* Allocate an array of instructions for each action that we
* didn't disable.
*/
void
apei_interp_pass3_alloc(struct apei_interp *I)
{
unsigned action;
for (action = 0; action < I->nact; action++) {
struct apei_actinst *const A = &I->actinst[action];
if (A->ninst == 0 || A->disable)
continue;
A->inst = kmem_zalloc(A->ninst * sizeof(A->inst[0]), KM_SLEEP);
}
}
/*
* apei_interp_pass4_assemble(I, i, E)
*
* Put the instruction for the ith entry E into the instruction
* array for its action. To be called for each entry in the table
* sequentially.
*/
void
apei_interp_pass4_assemble(struct apei_interp *I, uint32_t i,
ACPI_WHEA_HEADER *E)
{
/*
* If we skipped it in earlier passes, skip it now.
*/
if (E->Action >= I->nact || I->actname[E->Action] == NULL)
return;
struct apei_actinst *const A = &I->actinst[E->Action];
if (A->disable)
return;
/*
* apei_interp_pass5_verify(I)
*
* Paranoia: Verify we got all the instructions for each action,
* verify the actions point to their own instructions, and dump
* the instructions for each action, collated, with aprint_debug.
*/
void
apei_interp_pass5_verify(struct apei_interp *I)
{
unsigned action;
for (action = 0; action < I->nact; action++) {
struct apei_actinst *const A = &I->actinst[action];
unsigned j;
/*
* If the action is disabled, it's all set.
*/
if (A->disable)
continue;
KASSERTMSG(A->ip == A->ninst,
"action %s ip=%"PRIu32" ninstruction=%"PRIu32,
I->actname[action], A->ip, A->ninst);
/*
* XXX Dump the complete instruction table.
*/
for (j = 0; j < A->ninst; j++) {
ACPI_WHEA_HEADER *const E = A->inst[j].header;
KASSERT(E->Action == action);
/*
* If we need the register and weren't able to
* map it, disable the action.
*/
if (I->instreg[E->Instruction] &&
A->inst[j].map == NULL) {
A->disable = true;
continue;
}
/*
* apei_interpret(I, action, cookie)
*
* Run the instructions associated with the given action by
* calling the interpreter's instfunc for each one.
*
* Halt when the instruction pointer runs past the end of the
* array, or after 1000 cycles, whichever comes first.
*/
void
apei_interpret(struct apei_interp *I, unsigned action, void *cookie)
{
unsigned juice = 1000;
uint32_t ip = 0;
if (action > I->nact || I->actname[action] == NULL)
return;
struct apei_actinst *const A = &I->actinst[action];
if (A->disable)
return;
while (ip < A->ninst && juice --> 0) {
ACPI_WHEA_HEADER *const E = A->inst[ip].header;
struct apei_mapreg *const map = A->inst[ip].map;