apei(4): Pre-map registers when compiling interpreter.

This way we don't have to worry about mapping them in nasty contexts
where access to uvm_km_alloc may not be allowed.  Paves the way to
use ERST for saving dmesg on crash.

Exception: ACPI_ERST_MOVE_DATA still needs to do AcpiOsMapMemory.
We'll need to reserve a couple pages to avoid that.

PR kern/58046
This commit is contained in:
riastradh 2024-03-22 20:48:05 +00:00
parent 5ca1c7a6c2
commit 4f770de5f6
6 changed files with 212 additions and 90 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: apei_einj.c,v 1.3 2024/03/21 02:35:09 riastradh Exp $ */
/* $NetBSD: apei_einj.c,v 1.4 2024/03/22 20:48:05 riastradh Exp $ */
/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
@ -44,7 +44,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: apei_einj.c,v 1.3 2024/03/21 02:35:09 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: apei_einj.c,v 1.4 2024/03/22 20:48:05 riastradh Exp $");
#include <sys/types.h>
@ -55,6 +55,7 @@ __KERNEL_RCSID(0, "$NetBSD: apei_einj.c,v 1.3 2024/03/21 02:35:09 riastradh Exp
#include <dev/acpi/acpivar.h>
#include <dev/acpi/apei_einjvar.h>
#include <dev/acpi/apei_interp.h>
#include <dev/acpi/apei_mapreg.h>
#include <dev/acpi/apei_reg.h>
#include <dev/acpi/apeivar.h>
@ -63,8 +64,8 @@ __KERNEL_RCSID(0, "$NetBSD: apei_einj.c,v 1.3 2024/03/21 02:35:09 riastradh Exp
#define _COMPONENT ACPI_RESOURCE_COMPONENT
ACPI_MODULE_NAME ("apei")
static void apei_einj_instfunc(ACPI_WHEA_HEADER *, void *, uint32_t *,
uint32_t);
static void apei_einj_instfunc(ACPI_WHEA_HEADER *, struct apei_mapreg *,
void *, uint32_t *, uint32_t);
static uint64_t apei_einj_act(struct apei_softc *, enum AcpiEinjActions,
uint64_t);
static uint64_t apei_einj_trigger(struct apei_softc *, uint64_t);
@ -110,6 +111,21 @@ static const char *const apei_einj_instruction[] = {
[ACPI_EINJ_NOOP] = "noop",
};
/*
* apei_einj_instreg
*
* Table of which isntructions use a register operand.
*
* Must match apei_einj_instfunc.
*/
static const bool apei_einj_instreg[] = {
[ACPI_EINJ_READ_REGISTER] = true,
[ACPI_EINJ_READ_REGISTER_VALUE] = true,
[ACPI_EINJ_WRITE_REGISTER] = true,
[ACPI_EINJ_WRITE_REGISTER_VALUE] = true,
[ACPI_EINJ_NOOP] = false,
};
/*
* apei_einj_attach(sc)
*
@ -184,7 +200,7 @@ apei_einj_attach(struct apei_softc *sc)
jsc->jsc_interp = apei_interp_create("EINJ",
apei_einj_action, __arraycount(apei_einj_action),
apei_einj_instruction, __arraycount(apei_einj_instruction),
/*instvalid*/NULL, apei_einj_instfunc);
apei_einj_instreg, /*instvalid*/NULL, apei_einj_instfunc);
/*
* Compile the interpreter from the EINJ action instruction
@ -379,8 +395,8 @@ struct apei_einj_machine {
* too.
*/
static void
apei_einj_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
uint32_t maxip)
apei_einj_instfunc(ACPI_WHEA_HEADER *header, struct apei_mapreg *map,
void *cookie, uint32_t *ipp, uint32_t maxip)
{
struct apei_einj_machine *M = cookie;
ACPI_STATUS rv = AE_OK;
@ -418,24 +434,26 @@ apei_einj_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
*/
switch (header->Instruction) {
case ACPI_EINJ_READ_REGISTER:
rv = apei_read_register(reg, Mask, &M->y);
rv = apei_read_register(reg, map, Mask, &M->y);
if (ACPI_FAILURE(rv))
break;
break;
case ACPI_EINJ_READ_REGISTER_VALUE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
M->y = (v == Value ? 1 : 0);
break;
}
case ACPI_EINJ_WRITE_REGISTER:
rv = apei_write_register(reg, Mask, preserve_register, M->x);
rv = apei_write_register(reg, map, Mask, preserve_register,
M->x);
break;
case ACPI_EINJ_WRITE_REGISTER_VALUE:
rv = apei_write_register(reg, Mask, preserve_register, Value);
rv = apei_write_register(reg, map, Mask, preserve_register,
Value);
break;
case ACPI_EINJ_NOOP:
break;
@ -620,6 +638,29 @@ apei_einj_trigger(struct apei_softc *sc, uint64_t x)
continue;
}
/*
* Verify the instruction.
*/
if (header->Instruction >= __arraycount(apei_einj_instreg)) {
device_printf(sc->sc_dev, "TRIGGER_ERROR action table:"
" unknown instruction: %"PRIu32"\n",
header->Instruction);
continue;
}
/*
* Map the register if needed.
*/
struct apei_mapreg *map = NULL;
if (apei_einj_instreg[header->Instruction]) {
map = apei_mapreg_map(&header->RegisterRegion);
if (map == NULL) {
device_printf(sc->sc_dev, "TRIGGER_ERROR"
" action table: failed to map register\n");
continue;
}
}
/*
* Execute the instruction. Since there's only one
* action, we don't bother with the apei_interp
@ -627,8 +668,14 @@ apei_einj_trigger(struct apei_softc *sc, uint64_t x)
* action. EINJ instructions don't change ip.
*/
uint32_t ip = i + 1;
apei_einj_instfunc(header, M, &ip, nentries);
apei_einj_instfunc(header, map, M, &ip, nentries);
KASSERT(ip == i + 1);
/*
* Unmap the register if mapped.
*/
if (map != NULL)
apei_mapreg_unmap(&header->RegisterRegion, map);
}
out: if (teatab) {

View File

@ -1,4 +1,4 @@
/* $NetBSD: apei_erst.c,v 1.1 2024/03/20 17:11:43 riastradh Exp $ */
/* $NetBSD: apei_erst.c,v 1.2 2024/03/22 20:48:05 riastradh Exp $ */
/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
@ -36,7 +36,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: apei_erst.c,v 1.1 2024/03/20 17:11:43 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: apei_erst.c,v 1.2 2024/03/22 20:48:05 riastradh Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -53,8 +53,8 @@ __KERNEL_RCSID(0, "$NetBSD: apei_erst.c,v 1.1 2024/03/20 17:11:43 riastradh Exp
ACPI_MODULE_NAME ("apei")
static bool apei_erst_instvalid(ACPI_WHEA_HEADER *, uint32_t, uint32_t);
static void apei_erst_instfunc(ACPI_WHEA_HEADER *, void *, uint32_t *,
uint32_t);
static void apei_erst_instfunc(ACPI_WHEA_HEADER *, struct apei_mapreg *,
void *, uint32_t *, uint32_t);
static uint64_t apei_erst_act(struct apei_softc *, enum AcpiErstActions,
uint64_t);
@ -118,6 +118,35 @@ static const char *apei_erst_instruction[] = {
[ACPI_ERST_MOVE_DATA] = "move_data",
};
/*
* apei_erst_instreg
*
* Table of which isntructions use a register operand.
*
* Must match apei_erst_instfunc.
*/
static const bool apei_erst_instreg[] = {
[ACPI_ERST_READ_REGISTER] = true,
[ACPI_ERST_READ_REGISTER_VALUE] = true,
[ACPI_ERST_WRITE_REGISTER] = true,
[ACPI_ERST_WRITE_REGISTER_VALUE] = true,
[ACPI_ERST_NOOP] = false,
[ACPI_ERST_LOAD_VAR1] = true,
[ACPI_ERST_LOAD_VAR2] = true,
[ACPI_ERST_STORE_VAR1] = true,
[ACPI_ERST_ADD] = false,
[ACPI_ERST_SUBTRACT] = false,
[ACPI_ERST_ADD_VALUE] = true,
[ACPI_ERST_SUBTRACT_VALUE] = true,
[ACPI_ERST_STALL] = false,
[ACPI_ERST_STALL_WHILE_TRUE] = true,
[ACPI_ERST_SKIP_NEXT_IF_TRUE] = true,
[ACPI_ERST_GOTO] = false,
[ACPI_ERST_SET_SRC_ADDRESS_BASE] = true,
[ACPI_ERST_SET_DST_ADDRESS_BASE] = true,
[ACPI_ERST_MOVE_DATA] = true,
};
/*
* XXX dtrace and kernhist
*/
@ -228,7 +257,7 @@ apei_erst_attach(struct apei_softc *sc)
ssc->ssc_interp = apei_interp_create("ERST",
apei_erst_action, __arraycount(apei_erst_action),
apei_erst_instruction, __arraycount(apei_erst_instruction),
apei_erst_instvalid, apei_erst_instfunc);
apei_erst_instreg, apei_erst_instvalid, apei_erst_instfunc);
/*
* Compile the interpreter from the ERST action instruction
@ -332,7 +361,7 @@ struct apei_erst_machine {
};
/*
* apei_erst_instfunc(header, cookie, &ip, maxip)
* apei_erst_instfunc(header, map, cookie, &ip, maxip)
*
* Run a single instruction in the service of performing an ERST
* action. Updates the ERST machine at cookie, and the ip if
@ -343,8 +372,8 @@ struct apei_erst_machine {
* execute.
*/
static void
apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
uint32_t maxip)
apei_erst_instfunc(ACPI_WHEA_HEADER *header, struct apei_mapreg *map,
void *cookie, uint32_t *ipp, uint32_t maxip)
{
struct apei_erst_machine *const M = cookie;
ACPI_STATUS rv = AE_OK;
@ -382,33 +411,35 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
*/
switch (header->Instruction) {
case ACPI_ERST_READ_REGISTER:
rv = apei_read_register(reg, Mask, &M->y);
rv = apei_read_register(reg, map, Mask, &M->y);
break;
case ACPI_ERST_READ_REGISTER_VALUE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
M->y = (v == Value ? 1 : 0);
break;
}
case ACPI_ERST_WRITE_REGISTER:
rv = apei_write_register(reg, Mask, preserve_register, M->x);
rv = apei_write_register(reg, map, Mask, preserve_register,
M->x);
break;
case ACPI_ERST_WRITE_REGISTER_VALUE:
rv = apei_write_register(reg, Mask, preserve_register, Value);
rv = apei_write_register(reg, map, Mask, preserve_register,
Value);
break;
case ACPI_ERST_NOOP:
break;
case ACPI_ERST_LOAD_VAR1:
rv = apei_read_register(reg, Mask, &M->var1);
rv = apei_read_register(reg, map, Mask, &M->var1);
break;
case ACPI_ERST_LOAD_VAR2:
rv = apei_read_register(reg, Mask, &M->var2);
rv = apei_read_register(reg, map, Mask, &M->var2);
break;
case ACPI_ERST_STORE_VAR1:
rv = apei_write_register(reg, Mask, preserve_register,
rv = apei_write_register(reg, map, Mask, preserve_register,
M->var1);
break;
case ACPI_ERST_ADD:
@ -433,25 +464,25 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
case ACPI_ERST_ADD_VALUE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
v += Value;
rv = apei_write_register(reg, Mask, preserve_register, v);
rv = apei_write_register(reg, map, Mask, preserve_register, v);
break;
}
case ACPI_ERST_SUBTRACT_VALUE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
v -= Value;
rv = apei_write_register(reg, Mask, preserve_register, v);
rv = apei_write_register(reg, map, Mask, preserve_register, v);
break;
}
case ACPI_ERST_STALL:
@ -461,7 +492,7 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
for (;;) {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
if (v != Value)
@ -472,7 +503,7 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
case ACPI_ERST_SKIP_NEXT_IF_TRUE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
@ -496,7 +527,7 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
case ACPI_ERST_SET_SRC_ADDRESS_BASE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
M->src_base = v;
@ -505,7 +536,7 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
case ACPI_ERST_SET_DST_ADDRESS_BASE: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
M->src_base = v;
@ -514,9 +545,13 @@ apei_erst_instfunc(ACPI_WHEA_HEADER *header, void *cookie, uint32_t *ipp,
case ACPI_ERST_MOVE_DATA: {
uint64_t v;
rv = apei_read_register(reg, Mask, &v);
rv = apei_read_register(reg, map, Mask, &v);
if (ACPI_FAILURE(rv))
break;
/*
* XXX This might not work in nasty contexts unless we
* pre-allocate a virtual page for the mapping.
*/
apei_pmemmove(M->dst_base + v, M->src_base + v, M->var2);
break;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: apei_interp.c,v 1.3 2024/03/22 18:19:03 riastradh Exp $ */
/* $NetBSD: apei_interp.c,v 1.4 2024/03/22 20:48:05 riastradh Exp $ */
/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
@ -101,13 +101,10 @@
* a convenience for catching mistakes in firmware, not a security
* measure, since the OS is absolutely vulnerable to malicious firmware
* anyway.
*
* XXX Map instruction registers in advance so ERST is safe in nasty
* contexts, e.g. to save dmesg?
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: apei_interp.c,v 1.3 2024/03/22 18:19:03 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: apei_interp.c,v 1.4 2024/03/22 20:48:05 riastradh Exp $");
#include <sys/types.h>
@ -116,6 +113,7 @@ __KERNEL_RCSID(0, "$NetBSD: apei_interp.c,v 1.3 2024/03/22 18:19:03 riastradh Ex
#include <dev/acpi/acpivar.h>
#include <dev/acpi/apei_interp.h>
#include <dev/acpi/apei_mapreg.h>
/*
* struct apei_actinst
@ -125,7 +123,11 @@ __KERNEL_RCSID(0, "$NetBSD: apei_interp.c,v 1.3 2024/03/22 18:19:03 riastradh Ex
struct apei_actinst {
uint32_t ninst;
uint32_t ip;
struct acpi_whea_header **inst;
struct {
struct acpi_whea_header *header;
struct apei_mapreg *map;
} *inst;
bool disable;
};
/*
@ -139,10 +141,12 @@ struct apei_interp {
unsigned nact;
const char *const *instname;
unsigned ninst;
const bool *instreg;
bool (*instvalid)(ACPI_WHEA_HEADER *, uint32_t,
uint32_t);
void (*instfunc)(ACPI_WHEA_HEADER *, void *,
uint32_t *, uint32_t);
void (*instfunc)(ACPI_WHEA_HEADER *,
struct apei_mapreg *, void *, uint32_t *,
uint32_t);
struct apei_actinst actinst[];
};
@ -150,8 +154,10 @@ struct apei_interp *
apei_interp_create(const char *name,
const char *const *actname, unsigned nact,
const char *const *instname, unsigned ninst,
const bool *instreg,
bool (*instvalid)(ACPI_WHEA_HEADER *, uint32_t, uint32_t),
void (*instfunc)(ACPI_WHEA_HEADER *, void *, uint32_t *, uint32_t))
void (*instfunc)(ACPI_WHEA_HEADER *, struct apei_mapreg *, void *,
uint32_t *, uint32_t))
{
struct apei_interp *I;
@ -161,6 +167,7 @@ apei_interp_create(const char *name,
I->nact = nact;
I->instname = instname;
I->ninst = ninst;
I->instreg = instreg;
I->instvalid = instvalid;
I->instfunc = instfunc;
@ -174,9 +181,19 @@ apei_interp_destroy(struct apei_interp *I)
for (action = 0; action < nact; action++) {
struct apei_actinst *const A = &I->actinst[action];
unsigned j;
if (A->ninst == 0 || A->ninst == UINT32_MAX || A->inst == NULL)
if (A->ninst == 0 || A->inst == NULL)
continue;
for (j = 0; j < A->ninst; j++) {
ACPI_WHEA_HEADER *const E = A->inst[j].header;
struct apei_mapreg *const map = A->inst[j].map;
if (map != NULL)
apei_mapreg_unmap(&E->RegisterRegion, map);
}
kmem_free(A->inst, A->ninst * sizeof(A->inst[0]));
A->inst = NULL;
}
@ -212,18 +229,17 @@ apei_interp_pass1_load(struct apei_interp *I, uint32_t i,
/*
* If we can't interpret this instruction for this action, or
* if we couldn't interpret a previous instruction for this
* action, ignore _all_ instructions for this action -- by
* marking the action as having UINT32_MAX instructions -- and
* move on.
* 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 = UINT32_MAX;
A->ninst = 0;
A->disable = true;
return;
}
if (A->ninst == UINT32_MAX)
if (A->disable)
return;
/*
@ -233,14 +249,16 @@ apei_interp_pass1_load(struct apei_interp *I, uint32_t i,
A->ninst++;
/*
* If it overflows a reasonable size, bail on this instruction.
* 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 = UINT32_MAX;
A->ninst = 0;
A->disable = true;
return;
}
}
@ -278,15 +296,17 @@ apei_interp_pass2_verify(struct apei_interp *I, uint32_t i,
* 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 = UINT32_MAX;
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 decide to bail on, marked with UINT32_MAX.
* didn't disable.
*/
void
apei_interp_pass3_alloc(struct apei_interp *I)
@ -295,7 +315,7 @@ apei_interp_pass3_alloc(struct apei_interp *I)
for (action = 0; action < I->nact; action++) {
struct apei_actinst *const A = &I->actinst[action];
if (A->ninst == 0 || A->ninst == UINT32_MAX)
if (A->ninst == 0 || A->disable)
continue;
A->inst = kmem_zalloc(A->ninst * sizeof(A->inst[0]), KM_SLEEP);
}
@ -320,11 +340,14 @@ apei_interp_pass4_assemble(struct apei_interp *I, uint32_t i,
return;
struct apei_actinst *const A = &I->actinst[E->Action];
if (A->ninst == UINT32_MAX)
if (A->disable)
return;
KASSERT(A->ip < A->ninst);
A->inst[A->ip++] = E;
const uint32_t ip = A->ip++;
A->inst[ip].header = E;
A->inst[ip].map = I->instreg[E->Instruction] ?
apei_mapreg_map(&E->RegisterRegion) : NULL;
}
/*
@ -346,7 +369,7 @@ apei_interp_pass5_verify(struct apei_interp *I)
/*
* If the action is disabled, it's all set.
*/
if (A->ninst == UINT32_MAX)
if (A->disable)
continue;
KASSERTMSG(A->ip == A->ninst,
"action %s ip=%"PRIu32" ninstruction=%"PRIu32,
@ -356,9 +379,20 @@ apei_interp_pass5_verify(struct apei_interp *I)
* XXX Dump the complete instruction table.
*/
for (j = 0; j < A->ninst; j++) {
ACPI_WHEA_HEADER *const E = A->inst[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;
}
aprint_debug("%s: %s[%"PRIu32"]: %s\n",
I->name, I->actname[action], j,
I->instname[E->Instruction]);
@ -384,10 +418,14 @@ apei_interpret(struct apei_interp *I, unsigned action, void *cookie)
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++];
ACPI_WHEA_HEADER *const E = A->inst[ip].header;
struct apei_mapreg *const map = A->inst[ip].map;
(*I->instfunc)(E, cookie, &ip, A->ninst);
ip++;
(*I->instfunc)(E, map, cookie, &ip, A->ninst);
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: apei_interp.h,v 1.1 2024/03/20 17:11:43 riastradh Exp $ */
/* $NetBSD: apei_interp.h,v 1.2 2024/03/22 20:48:05 riastradh Exp $ */
/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
@ -33,12 +33,15 @@
struct acpi_whea_header;
struct apei_interp;
struct apei_mapreg;
struct apei_interp *apei_interp_create(const char *,
const char *const *, unsigned,
const char *const *, unsigned,
const bool *,
bool (*)(struct acpi_whea_header *, uint32_t, uint32_t),
void (*)(struct acpi_whea_header *, void *, uint32_t *, uint32_t));
void (*)(struct acpi_whea_header *, struct apei_mapreg *, void *,
uint32_t *, uint32_t));
void apei_interp_destroy(struct apei_interp *);
void apei_interp_pass1_load(struct apei_interp *, uint32_t,

View File

@ -1,4 +1,4 @@
/* $NetBSD: apei_reg.c,v 1.1 2024/03/20 17:11:44 riastradh Exp $ */
/* $NetBSD: apei_reg.c,v 1.2 2024/03/22 20:48:05 riastradh Exp $ */
/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
@ -31,18 +31,19 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: apei_reg.c,v 1.1 2024/03/20 17:11:44 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: apei_reg.c,v 1.2 2024/03/22 20:48:05 riastradh Exp $");
#include <sys/types.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/apei_mapreg.h>
#include <dev/acpi/apei_reg.h>
/*
* apei_read_register(Register, Mask, &X)
* apei_read_register(Register, map, Mask, &X)
*
* Read from Register, shifted out of position and then masked
* with Mask, and store the result in X.
* Read from Register mapped at map, shifted out of position and
* then masked with Mask, and store the result in X.
*
* https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#read-register
*
@ -50,17 +51,13 @@ __KERNEL_RCSID(0, "$NetBSD: apei_reg.c,v 1.1 2024/03/20 17:11:44 riastradh Exp $
* that section is under the ERST part.)
*/
ACPI_STATUS
apei_read_register(ACPI_GENERIC_ADDRESS *Register, uint64_t Mask, uint64_t *p)
apei_read_register(ACPI_GENERIC_ADDRESS *Register, struct apei_mapreg *map,
uint64_t Mask, uint64_t *p)
{
const uint8_t BitOffset = Register->BitOffset;
uint64_t X;
ACPI_STATUS rv;
rv = AcpiRead(&X, Register);
if (ACPI_FAILURE(rv)) {
*p = 0; /* XXX */
return rv;
}
X = apei_mapreg_read(Register, map);
X >>= BitOffset;
X &= Mask;
@ -69,10 +66,11 @@ apei_read_register(ACPI_GENERIC_ADDRESS *Register, uint64_t Mask, uint64_t *p)
}
/*
* apei_write_register(Register, Mask, preserve_register, X)
* apei_write_register(Register, map, Mask, preserve_register, X)
*
* Write X, masked with Mask and shifted into position, to
* Register, preserving other bits if preserve_register is true.
* Register, mapped at map, preserving other bits if
* preserve_register is true.
*
* https://uefi.org/specs/ACPI/6.5/18_Platform_Error_Interfaces.html#write-register
*
@ -82,22 +80,20 @@ apei_read_register(ACPI_GENERIC_ADDRESS *Register, uint64_t Mask, uint64_t *p)
* which has been lost in more recent versions of the spec.
*/
ACPI_STATUS
apei_write_register(ACPI_GENERIC_ADDRESS *Register, uint64_t Mask,
bool preserve_register, uint64_t X)
apei_write_register(ACPI_GENERIC_ADDRESS *Register, struct apei_mapreg *map,
uint64_t Mask, bool preserve_register, uint64_t X)
{
const uint8_t BitOffset = Register->BitOffset;
ACPI_STATUS rv;
X &= Mask;
X <<= BitOffset;
if (preserve_register) {
uint64_t Y;
rv = AcpiRead(&Y, Register);
if (ACPI_FAILURE(rv))
return rv;
Y = apei_mapreg_read(Register, map);
Y &= ~(Mask << BitOffset);
X |= Y;
}
return AcpiWrite(X, Register);
apei_mapreg_write(Register, map, X);
return AE_OK;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: apei_reg.h,v 1.1 2024/03/20 17:11:44 riastradh Exp $ */
/* $NetBSD: apei_reg.h,v 1.2 2024/03/22 20:48:05 riastradh Exp $ */
/*-
* Copyright (c) 2024 The NetBSD Foundation, Inc.
@ -33,8 +33,11 @@
#include <dev/acpi/acpivar.h>
ACPI_STATUS apei_read_register(ACPI_GENERIC_ADDRESS *, uint64_t, uint64_t *);
ACPI_STATUS apei_write_register(ACPI_GENERIC_ADDRESS *, uint64_t, bool,
uint64_t);
struct apei_mapreg;
ACPI_STATUS apei_read_register(ACPI_GENERIC_ADDRESS *, struct apei_mapreg *,
uint64_t, uint64_t *);
ACPI_STATUS apei_write_register(ACPI_GENERIC_ADDRESS *, struct apei_mapreg *,
uint64_t, bool, uint64_t);
#endif /* _SYS_DEV_ACPI_APEI_REG_H_ */