Arthur Heymans has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/44323 )
Change subject: [WIP] SMM save states ......................................................................
[WIP] SMM save states
Change-Id: I4a31d05c09065543424a9010ac434dde0dfb5836 Signed-off-by: Arthur Heymans arthur@aheymans.xyz --- M src/cpu/intel/smm/Makefile.inc A src/cpu/intel/smm/em64t101_save_state.c M src/cpu/x86/smm/Makefile.inc A src/cpu/x86/smm/save_state.c M src/cpu/x86/smm/smm.ld A src/include/cpu/x86/save_state.h M src/lib/program.ld M src/southbridge/intel/i82801ix/smihandler.c 8 files changed, 210 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/23/44323/1
diff --git a/src/cpu/intel/smm/Makefile.inc b/src/cpu/intel/smm/Makefile.inc index a49b796..df73ae7 100644 --- a/src/cpu/intel/smm/Makefile.inc +++ b/src/cpu/intel/smm/Makefile.inc @@ -1 +1,2 @@ ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smm_reloc.c +smm-y += em64t101_save_state.c diff --git a/src/cpu/intel/smm/em64t101_save_state.c b/src/cpu/intel/smm/em64t101_save_state.c new file mode 100644 index 0000000..404906b --- /dev/null +++ b/src/cpu/intel/smm/em64t101_save_state.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <string.h> +#include <cpu/x86/save_state.h> +#include <cpu/intel/em64t101_save_state.h> + +static void *em64t101_get_reg_base(const enum cpu_reg reg, const int node) +{ + const em64t101_smm_state_save_area_t *save_state = (const em64t101_smm_state_save_area_t *)smm_get_save_state(node); + + switch (reg) { + case RAX: + return (void *)&save_state->rax; + case RBX: + return (void *)&save_state->rbx; + case RCX: + return (void *)&save_state->rcx; + case RDX: + return (void *)&save_state->rdx; + } + + return NULL; +} + +enum get_set { + GET, + SET +}; + +static int em64t101_get_set(const enum get_set op_type, const enum cpu_reg reg, + const int node, void *in_out, const uint8_t length) +{ + + void *reg_base = em64t101_get_reg_base(reg, node); + + if (!reg_base) + return -1; + + switch (length) { + case 1: + case 2: + case 4: + case 8: + switch (op_type) { + case GET: + memcpy(in_out, reg_base, length); + return 0; + case SET: + memcpy(reg_base, in_out, length); + } + } + + return -1; +} + +static int em64t101_get_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length) +{ + return em64t101_get_set(GET, reg, node, out, length); +} + +static int em64t101_set_reg(const enum cpu_reg reg, const int node, void *in, const uint8_t length) +{ + return em64t101_get_set(SET, reg, node, in, length); +} + +static int em64t101_apmc_node(u8 cmd) +{ + em64t101_smm_state_save_area_t *state; + int node; + + for (node = 0; node < CONFIG_MAX_CPUS; node++) { + state = smm_get_save_state(node); + + /* Check for Synchronous IO (bit0 == 1) */ + if (!(state->io_misc_info & (1 << 0))) + continue; + + /* Make sure it was a write (bit4 == 0) */ + if (state->io_misc_info & (1 << 4)) + continue; + + /* Check for APMC IO port */ + if (((state->io_misc_info >> 16) & 0xff) != APM_CNT) + continue; + + /* Check AX against the requested command */ + if ((state->rax & 0xff) != cmd) + continue; + + return node; + } + + return -1; +} + +static const uint32_t revisions[] = { + 0x00030101, + SMM_REV_INVALID, +}; + +static const struct smm_save_state_ops ops __smm_save_state_ops = { + .revision_table = revisions, + .get_reg = em64t101_get_reg, + .set_reg = em64t101_set_reg, + .apmc_node = em64t101_apmc_node, +}; diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc index bbd96c6..2b3e073 100644 --- a/src/cpu/x86/smm/Makefile.inc +++ b/src/cpu/x86/smm/Makefile.inc @@ -28,6 +28,8 @@ ramstage-srcs += $(obj)/cpu/x86/smm/smm.manual endif
+smm-y += save_state.c + ifeq ($(CONFIG_SMM_TSEG),y)
ramstage-y += tseg_region.c diff --git a/src/cpu/x86/smm/save_state.c b/src/cpu/x86/smm/save_state.c new file mode 100644 index 0000000..7c757e3 --- /dev/null +++ b/src/cpu/x86/smm/save_state.c @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <cpu/x86/smm.h> +#include <cpu/x86/save_state.h> + +static struct smm_save_state_ops *save_state; + +/* Returns -1 on failure, 0 on success */ +static int init_save_state(void) +{ + const uint32_t revision = smm_revision(); + struct smm_save_state_ops *ops; + static bool initialized = false; + + if (initialized) + return 0; + + for (ops = _smm_save_state_ops; ops < _esmm_save_state_ops; ops++) { + const uint32_t *rev; + for (rev = ops->revision_table; *rev != SMM_REV_INVALID; rev++) + if (*rev == revision) { + save_state = ops; + initialized = true; + return 0; + } + } + + return -1; +} + +int get_apmc_node(u8 cmd) +{ + if (init_save_state()) + return -1; + + return save_state->apmc_node(cmd); +} + +int get_save_state_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length) +{ + if (init_save_state()) + return -1; + + return save_state->get_reg(reg, node, out, length); +} diff --git a/src/cpu/x86/smm/smm.ld b/src/cpu/x86/smm/smm.ld index 2f46975..39afee4 100644 --- a/src/cpu/x86/smm/smm.ld +++ b/src/cpu/x86/smm/smm.ld @@ -25,6 +25,11 @@
/* C read-only data of the SMM handler */ . = ALIGN(16); + + _smm_save_state_ops = .; + KEEP(*(.rodata.smm_ops)); + _esmm_save_state_ops = .; + *(.rodata) *(.rodata.*)
diff --git a/src/include/cpu/x86/save_state.h b/src/include/cpu/x86/save_state.h new file mode 100644 index 0000000..b97890b --- /dev/null +++ b/src/include/cpu/x86/save_state.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __CPU_X86_SAVE_STATE_H__ +#define __CPU_X86_SAVE_STATE_H__ + +#include <stdint.h> + +#if ENV_SMM +#define __smm_save_state_ops __attribute__((used, __section__(".rodata.smm_ops"))) +#else +#define __smm_save_state_ops __attribute__((unused)) +#endif + +enum cpu_reg { + RAX, + RBX, + RCX, + RDX +}; + +#define SMM_REV_INVALID 0xffffffff + +struct smm_save_state_ops { + const uint32_t *revision_table; + /* Accessors for CPU registers in the SMM save state + Returns -1 on failure, 0 on success */ + int (*get_reg)(const enum cpu_reg reg, const int node, void *out, const uint8_t length); + int(*set_reg)(const enum cpu_reg reg, const int node, void *in, const uint8_t length); + /* Returns -1 on failure, the node on which the 'cmd' was send on success */ + int(*apmc_node)(u8 cmd); +}; + +/** start of compile time generated pci driver array */ +extern struct smm_save_state_ops _smm_save_state_ops[]; +/** end of compile time generated pci driver array */ +extern struct smm_save_state_ops _esmm_save_state_ops[]; + +int get_apmc_node(u8 cmd); +int get_save_state_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length); + +#endif /* __CPU_X86_SAVE_STATE_H__ */ diff --git a/src/lib/program.ld b/src/lib/program.ld index 88a3126..1894ae0 100644 --- a/src/lib/program.ld +++ b/src/lib/program.ld @@ -44,6 +44,13 @@ _ecpu_drivers = .; #endif
+#if ENV_SMM || ENV_RMODULE + . = ALIGN(ARCH_POINTER_ALIGN_SIZE); + _smm_save_state_ops = .; + KEEP(*(.rodata.smm_ops)); + _esmm_save_state_ops = .; +#endif + . = ALIGN(ARCH_POINTER_ALIGN_SIZE); *(.rodata); *(.rodata.*); diff --git a/src/southbridge/intel/i82801ix/smihandler.c b/src/southbridge/intel/i82801ix/smihandler.c index 699f51f..9b37f05 100644 --- a/src/southbridge/intel/i82801ix/smihandler.c +++ b/src/southbridge/intel/i82801ix/smihandler.c @@ -3,6 +3,7 @@ #include <types.h> #include <console/console.h> #include <cpu/x86/smm.h> +#include <cpu/x86/save_state.h> #include <device/pci_def.h> #include <southbridge/intel/common/pmutil.h> #include "i82801ix.h" @@ -31,6 +32,8 @@ return 1; /* IO trap handled */ }
+ get_apmc_node(5); + /* Not handled */ return 0; }