Arthur Heymans has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/45472 )
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.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;