Module Name:    src
Committed By:   riastradh
Date:           Fri Mar 22 20:48:05 UTC 2024

Modified Files:
        src/sys/dev/acpi: apei_einj.c apei_erst.c apei_interp.c apei_interp.h
            apei_reg.c apei_reg.h

Log Message:
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


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/acpi/apei_einj.c \
    src/sys/dev/acpi/apei_interp.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/acpi/apei_erst.c \
    src/sys/dev/acpi/apei_interp.h src/sys/dev/acpi/apei_reg.c \
    src/sys/dev/acpi/apei_reg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/acpi/apei_einj.c
diff -u src/sys/dev/acpi/apei_einj.c:1.3 src/sys/dev/acpi/apei_einj.c:1.4
--- src/sys/dev/acpi/apei_einj.c:1.3	Thu Mar 21 02:35:09 2024
+++ src/sys/dev/acpi/apei_einj.c	Fri Mar 22 20:48:05 2024
@@ -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,
 #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,
 #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);
@@ -111,6 +112,21 @@ static const char *const apei_einj_instr
 };
 
 /*
+ * 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)
  *
  *	Scan the Error Injection table to ascertain what error
@@ -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 *hea
 	 */
 	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;
@@ -621,14 +639,43 @@ apei_einj_trigger(struct apei_softc *sc,
 		}
 
 		/*
+		 * 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
 		 * machinery to collate instruction tables for each
 		 * 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) {
Index: src/sys/dev/acpi/apei_interp.c
diff -u src/sys/dev/acpi/apei_interp.c:1.3 src/sys/dev/acpi/apei_interp.c:1.4
--- src/sys/dev/acpi/apei_interp.c:1.3	Fri Mar 22 18:19:03 2024
+++ src/sys/dev/acpi/apei_interp.c	Fri Mar 22 20:48:05 2024
@@ -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.
 
 #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.
 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 *
 
 	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_inter
 	/*
 	 * 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_inter
 	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_int
 	 * 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_inte
 
 	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_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_int
 		/*
 		 * 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_int
 		 * 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, un
 	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);
 	}
 }

Index: src/sys/dev/acpi/apei_erst.c
diff -u src/sys/dev/acpi/apei_erst.c:1.1 src/sys/dev/acpi/apei_erst.c:1.2
--- src/sys/dev/acpi/apei_erst.c:1.1	Wed Mar 20 17:11:43 2024
+++ src/sys/dev/acpi/apei_erst.c	Fri Mar 22 20:48:05 2024
@@ -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,
 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);
 
@@ -119,6 +119,35 @@ static const char *apei_erst_instruction
 };
 
 /*
+ * 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
  */
 static void
@@ -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 *hea
 	 */
 	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 *hea
 	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 *hea
 		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 *hea
 	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 *hea
 	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 *hea
 	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 *hea
 	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;
 	}
Index: src/sys/dev/acpi/apei_interp.h
diff -u src/sys/dev/acpi/apei_interp.h:1.1 src/sys/dev/acpi/apei_interp.h:1.2
--- src/sys/dev/acpi/apei_interp.h:1.1	Wed Mar 20 17:11:43 2024
+++ src/sys/dev/acpi/apei_interp.h	Fri Mar 22 20:48:05 2024
@@ -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,
Index: src/sys/dev/acpi/apei_reg.c
diff -u src/sys/dev/acpi/apei_reg.c:1.1 src/sys/dev/acpi/apei_reg.c:1.2
--- src/sys/dev/acpi/apei_reg.c:1.1	Wed Mar 20 17:11:44 2024
+++ src/sys/dev/acpi/apei_reg.c	Fri Mar 22 20:48:05 2024
@@ -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
  *	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 
 }
 
 /*
- * 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 
  *	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;
 }
Index: src/sys/dev/acpi/apei_reg.h
diff -u src/sys/dev/acpi/apei_reg.h:1.1 src/sys/dev/acpi/apei_reg.h:1.2
--- src/sys/dev/acpi/apei_reg.h:1.1	Wed Mar 20 17:11:44 2024
+++ src/sys/dev/acpi/apei_reg.h	Fri Mar 22 20:48:05 2024
@@ -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_ */

Reply via email to