Arthur Heymans has uploaded this change for review.

View Change

cpu/x86/smm: Add save state ops for different save states.

Change-Id: I67ab44fbdca5fac5837d32ffda5caad61e534473
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
---
A src/cpu/amd/smm/amd64_save_state.c
A src/cpu/intel/smm/em64t100_save_state.c
A src/cpu/intel/smm/em64t101_save_state.c
M src/cpu/x86/Kconfig
M src/cpu/x86/smm/Makefile.inc
A src/cpu/x86/smm/legacy_save_state.c
6 files changed, 476 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/72/45472/1
diff --git a/src/cpu/amd/smm/amd64_save_state.c b/src/cpu/amd/smm/amd64_save_state.c
new file mode 100644
index 0000000..6aabb56
--- /dev/null
+++ b/src/cpu/amd/smm/amd64_save_state.c
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <string.h>
+#include <cpu/x86/save_state.h>
+#include <cpu/amd/amd64_save_state.h>
+
+static void *amd64_get_reg_base(const enum cpu_reg reg, const int node)
+{
+ const amd64_smm_state_save_area_t *save_state =
+ (const amd64_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 amd64_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 = amd64_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 amd64_get_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length)
+{
+ return amd64_get_set(GET, reg, node, out, length);
+}
+
+static int amd64_set_reg(const enum cpu_reg reg, const int node, void *in, const uint8_t length)
+{
+ return amd64_get_set(SET, reg, node, in, length);
+}
+
+/* bits in smm_io_trap */
+#define SMM_IO_TRAP_PORT_OFFSET 16
+#define SMM_IO_TRAP_PORT_ADDRESS_MASK 0xffff
+#define SMM_IO_TRAP_RW (1 << 0)
+#define SMM_IO_TRAP_VALID (1 << 1)
+
+static inline u16 get_io_address(u32 info)
+{
+ return ((info >> SMM_IO_TRAP_PORT_OFFSET) &
+ SMM_IO_TRAP_PORT_ADDRESS_MASK);
+}
+
+static int amd64_apmc_node(u8 cmd)
+{
+ amd64_smm_state_save_area_t *state;
+ u32 smm_io_trap;
+ int node;
+
+ for (node = 0; node < CONFIG_MAX_CPUS; node++) {
+ state = smm_get_save_state(node);
+ smm_io_trap = state->smm_io_trap_offset;
+
+ /* Check for Valid IO Trap Word (bit1==1) */
+ if (!(smm_io_trap & SMM_IO_TRAP_VALID))
+ continue;
+ /* Make sure it was a write (bit0==0) */
+ if (smm_io_trap & SMM_IO_TRAP_RW)
+ continue;
+ /* Check for APMC IO port */
+ if (pm_acpi_smi_cmd_port() != get_io_address(smm_io_trap))
+ continue;
+ /* Check AL against the requested command */
+ reg_al = state->rax;
+ if (reg_al == cmd)
+ return node;
+ }
+
+ return -1;
+}
+
+static const uint32_t revisions[] = {
+ 0x00020064,
+ 0x00030064,
+ SMM_REV_INVALID,
+};
+
+static const struct smm_save_state_ops ops = {
+ .revision_table = revisions,
+ .get_reg = amd64_get_reg,
+ .set_reg = amd64_set_reg,
+ .apmc_node = amd64_apmc_node,
+};
+
+const struct smm_save_state_ops *amd64_ops = &ops;
diff --git a/src/cpu/intel/smm/em64t100_save_state.c b/src/cpu/intel/smm/em64t100_save_state.c
new file mode 100644
index 0000000..c654fd9
--- /dev/null
+++ b/src/cpu/intel/smm/em64t100_save_state.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <string.h>
+#include <cpu/x86/save_state.h>
+#include <cpu/intel/em64t100_save_state.h>
+
+static void *em64t100_get_reg_base(const enum cpu_reg reg, const int node)
+{
+ const em64t100_smm_state_save_area_t *save_state =
+ (const em64t100_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 em64t100_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 = em64t100_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 em64t100_get_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length)
+{
+ return em64t100_get_set(GET, reg, node, out, length);
+}
+
+static int em64t100_set_reg(const enum cpu_reg reg, const int node, void *in, const uint8_t length)
+{
+ return em64t100_get_set(SET, reg, node, in, length);
+}
+
+static int em64t100_apmc_node(u8 cmd)
+{
+ em64t100_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[] = {
+ 0x00030100,
+ SMM_REV_INVALID,
+};
+
+static const struct smm_save_state_ops ops = {
+ .revision_table = revisions,
+ .get_reg = em64t100_get_reg,
+ .set_reg = em64t100_set_reg,
+ .apmc_node = em64t100_apmc_node,
+};
+
+const struct smm_save_state_ops *em64t100_ops = &ops;
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..2d1f9bc
--- /dev/null
+++ b/src/cpu/intel/smm/em64t101_save_state.c
@@ -0,0 +1,108 @@
+/* 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 = {
+ .revision_table = revisions,
+ .get_reg = em64t101_get_reg,
+ .set_reg = em64t101_set_reg,
+ .apmc_node = em64t101_apmc_node,
+};
+
+const struct smm_save_state_ops *em64t101_ops = &ops;
diff --git a/src/cpu/x86/Kconfig b/src/cpu/x86/Kconfig
index 5394cd0..ad6f7b7 100644
--- a/src/cpu/x86/Kconfig
+++ b/src/cpu/x86/Kconfig
@@ -96,6 +96,32 @@
default y
depends on !(NO_SMM || SMM_ASEG)

+config X86_LEGACY_SAVE_STATE
+ bool
+ help
+ Select this on platforms that have CPUs with a legacy SMM save
+ state area.
+
+config X86_EM64T100_SAVE_STATE
+ bool
+ help
+ Select this on platforms that have CPUs with a SMM save
+ state area revision 0x00030100. These are mostly small
+ core (atom) Intel targets.
+
+config X86_EM64T101_SAVE_STATE
+ bool
+ help
+ Select this on platforms that have CPUs with a SMM save
+ state area revision 0x00030101. These are mostly big
+ core (core i) Intel targets.
+
+config X86_AMD64_SAVE_STATE
+ bool
+ help
+ Select this on platforms that have CPUs with a AMD64 save
+ state area.
+
if SMM_TSEG

config SMM_MODULE_HEAP_SIZE
diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc
index e0ee5c0..cb62023 100644
--- a/src/cpu/x86/smm/Makefile.inc
+++ b/src/cpu/x86/smm/Makefile.inc
@@ -29,6 +29,10 @@
endif

smm-y += save_state.c
+smm-$(CONFIG_X86_LEGACY_SAVE_STATE) += legacy_save_state.c
+smm-$(CONFIG_X86_EM64T100_SAVE_STATE) += ../../intel/smm/em64t100_save_state.c
+smm-$(CONFIG_X86_EM64T101_SAVE_STATE) += ../../intel/smm/em64t101_save_state.c
+smm-$(CONFIG_X86_AMD64_SAVE_STATE) += ../../amd/smm/amd64_save_state.c

ifeq ($(CONFIG_SMM_TSEG),y)

diff --git a/src/cpu/x86/smm/legacy_save_state.c b/src/cpu/x86/smm/legacy_save_state.c
new file mode 100644
index 0000000..59fa99f
--- /dev/null
+++ b/src/cpu/x86/smm/legacy_save_state.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <string.h>
+#include <cpu/x86/save_state.h>
+#include <cpu/x86/legacy_save_state.h>
+
+static void *legacy_get_reg_base(const enum cpu_reg reg, const int node)
+{
+ const legacy_smm_state_save_area_t *save_state =
+ (const legacy_smm_state_save_area_t *)smm_get_save_state(node);
+
+ switch (reg) {
+ case RAX:
+ return (void *)&save_state->eax;
+ case RBX:
+ return (void *)&save_state->ebx;
+ case RCX:
+ return (void *)&save_state->ecx;
+ case RDX:
+ return (void *)&save_state->edx;
+ }
+
+ return NULL;
+}
+
+enum get_set {
+ GET,
+ SET
+};
+
+static int legacy_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 = legacy_get_reg_base(reg, node);
+
+ if (!reg_base)
+ return -1;
+
+ switch (length) {
+ case 1:
+ case 2:
+ case 4:
+ 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 legacy_get_reg(const enum cpu_reg reg, const int node, void *out, const uint8_t length)
+{
+ return legacy_get_set(GET, reg, node, out, length);
+}
+
+static int legacy_set_reg(const enum cpu_reg reg, const int node, void *in, const uint8_t length)
+{
+ return legacy_get_set(SET, reg, node, in, length);
+}
+
+static int legacy_apmc_node(u8 cmd)
+{
+ legacy_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[] = {
+ 0x00030002,
+ 0x00030007,
+ SMM_REV_INVALID,
+};
+
+static const struct smm_save_state_ops ops = {
+ .revision_table = revisions,
+ .get_reg = legacy_get_reg,
+ .set_reg = legacy_set_reg,
+ .apmc_node = legacy_apmc_node,
+};
+
+const struct smm_save_state_ops *legacy_ops = &ops;

To view, visit change 45472. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I67ab44fbdca5fac5837d32ffda5caad61e534473
Gerrit-Change-Number: 45472
Gerrit-PatchSet: 1
Gerrit-Owner: Arthur Heymans <arthur@aheymans.xyz>
Gerrit-Reviewer: Martin Roth <martinroth@google.com>
Gerrit-Reviewer: Patrick Georgi <pgeorgi@google.com>
Gerrit-Reviewer: Patrick Rudolph <siro@das-labor.org>
Gerrit-MessageType: newchange