summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpu/x86/smm/Makefile.inc2
-rw-r--r--src/cpu/x86/smm/save_state.c77
-rw-r--r--src/include/cpu/x86/save_state.h34
3 files changed, 113 insertions, 0 deletions
diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc
index c2f49cf94d..eb386a69e5 100644
--- a/src/cpu/x86/smm/Makefile.inc
+++ b/src/cpu/x86/smm/Makefile.inc
@@ -32,6 +32,8 @@ ifeq ($(CONFIG_HAVE_SMI_HANDLER),y)
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 0000000000..bb08f86414
--- /dev/null
+++ b/src/cpu/x86/smm/save_state.c
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <cpu/x86/smm.h>
+#include <cpu/x86/save_state.h>
+
+/* These are weakly linked such that platforms can link only the save state
+ ops they actually require. */
+const struct smm_save_state_ops *legacy_ops __weak = NULL;
+const struct smm_save_state_ops *em64t100_ops __weak = NULL;
+const struct smm_save_state_ops *em64t101_ops __weak = NULL;
+const struct smm_save_state_ops *amd64_ops __weak = NULL;
+
+static const 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();
+ int i;
+ static bool initialized = false;
+ const struct smm_save_state_ops *save_state_ops[] = {
+ legacy_ops,
+ em64t100_ops,
+ em64t101_ops,
+ amd64_ops,
+ };
+
+ if (initialized)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(save_state_ops); i++) {
+ const struct smm_save_state_ops *ops = save_state_ops[i];
+ const uint32_t *rev;
+
+ if (ops == NULL)
+ continue;
+
+ 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;
+
+ if (node > CONFIG_MAX_CPUS)
+ return -1;
+
+ return save_state->get_reg(reg, node, out, length);
+}
+
+int set_save_state_reg(const enum cpu_reg reg, const int node, void *in, const uint8_t length)
+{
+ if (init_save_state())
+ return -1;
+
+ if (node > CONFIG_MAX_CPUS)
+ return -1;
+
+ return save_state->set_reg(reg, node, in, length);
+}
diff --git a/src/include/cpu/x86/save_state.h b/src/include/cpu/x86/save_state.h
new file mode 100644
index 0000000000..d6fcf63d79
--- /dev/null
+++ b/src/include/cpu/x86/save_state.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __CPU_X86_SAVE_STATE_H__
+#define __CPU_X86_SAVE_STATE_H__
+
+#include <stdint.h>
+
+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);
+};
+
+/* Return -1 on failure, otherwise returns which CPU node issued an APMC IO write */
+int get_apmc_node(u8 cmd);
+/* Return -1 on failure, 0 on succes.
+ Accessors for the SMM save state CPU registers RAX, RBX, RCX and RDX */
+int get_save_state_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length);
+int set_save_state_reg(const enum cpu_reg reg, const int node, void *in, const uint8_t length);
+
+#endif /* __CPU_X86_SAVE_STATE_H__ */