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:
parent
5ca1c7a6c2
commit
4f770de5f6
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Reference in New Issue