Arthur Heymans has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/83064?usp=email )
Change subject: cpu/x86/smm: Add save state ops for different save states ......................................................................
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.mk A src/cpu/x86/smm/legacy_save_state.c 6 files changed, 480 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/64/83064/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..d0231f7 --- /dev/null +++ b/src/cpu/amd/smm/amd64_save_state.c @@ -0,0 +1,123 @@ +/* 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 0; + } + } + + 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; + u8 reg_al; + + 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. This tends to be configurable on AMD CPUs */ + 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..8e98630 --- /dev/null +++ b/src/cpu/intel/smm/em64t100_save_state.c @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <string.h> +#include <cpu/x86/smm.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 0; + } + } + + 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 AL 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..fa1ea73 --- /dev/null +++ b/src/cpu/intel/smm/em64t101_save_state.c @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <string.h> +#include <cpu/x86/smm.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 0; + } + } + + 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 AL 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 828c0f9..fbcb0e2 100644 --- a/src/cpu/x86/Kconfig +++ b/src/cpu/x86/Kconfig @@ -177,6 +177,32 @@ IEDRAM is a memory region used for enhanced debug features. SMM-mode processor access to TSEG always targets the physical DRAM.
+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 HAVE_SMI_HANDLER
config SMM_MODULE_STACK_SIZE diff --git a/src/cpu/x86/smm/Makefile.mk b/src/cpu/x86/smm/Makefile.mk index 33b24a8..2d5383b 100644 --- a/src/cpu/x86/smm/Makefile.mk +++ b/src/cpu/x86/smm/Makefile.mk @@ -31,6 +31,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
ramstage-y += tseg_region.c romstage-y += tseg_region.c 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..e0b4517 --- /dev/null +++ b/src/cpu/x86/smm/legacy_save_state.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <string.h> +#include <cpu/x86/smm.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 0; + } + } + + 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 AL against the requested command */ + if ((state->eax & 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;