cpu/x86/smm: reserve SMRAM for OPAL S3 state

Reserve a small persistent SMRAM subregion for OPAL S3 unlock state, so
the payload-provided OPAL secret can survive SMM handler reload on S3
resume.

Expose the region base/size to SMM via smm_runtime and provide an
accessor for SMM code. Clear the region on cold boot/reboot, but
preserve it when waking from S3.

TEST=tested with rest of patch train

Change-Id: Ib1e92edb31c845367afe6185e5fa18ab1bc71108
Signed-off-by: Sean Rhodes <sean@starlabs.systems>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/91414
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
This commit is contained in:
Sean Rhodes 2026-02-13 20:58:00 +00:00 committed by Matt DeVillier
commit ce1db1f54a
5 changed files with 62 additions and 1 deletions

View file

@ -196,6 +196,21 @@ config SMM_MODULE_STACK_SIZE
This option determines the size of the stack within the SMM handler
modules.
config SMM_OPAL_S3_STATE_SMRAM
bool
help
Reserve a small persistent region in SMRAM for OPAL S3 state so the
OPAL secret survives SMM handler reload on S3 resume.
config SMM_OPAL_S3_STATE_SMRAM_SIZE
hex
default 0x1000 if SMM_OPAL_S3_STATE_SMRAM
default 0x0
help
Size in bytes of the persistent SMRAM region reserved for OPAL S3
secret/state. Must be large enough to store the OPAL S3 state
structure and should be kept minimal to reduce SMRAM pressure.
config SMM_OPAL_S3_SCRATCH_CBMEM
bool
depends on HAVE_ACPI_RESUME

View file

@ -74,6 +74,14 @@ void smm_get_opal_s3_scratch_buffer(uintptr_t *base, size_t *size)
}
#endif
#if CONFIG(SMM_OPAL_S3_STATE_SMRAM)
void smm_get_opal_s3_state_buffer(uintptr_t *base, size_t *size)
{
*base = smm_runtime.opal_s3_state_base;
*size = smm_runtime.opal_s3_state_size;
}
#endif
void smm_get_cbmemc_buffer(void **buffer_out, size_t *size_out)
{
*buffer_out = smm_runtime.cbmemc;

View file

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpi.h>
#include <acpi/acpi_gnvs.h>
#include <cbmem.h>
#include <commonlib/helpers.h>
@ -386,6 +387,23 @@ static void setup_smihandler_params(struct smm_runtime *mod_params,
mod_params->opal_s3_scratch_size = opal_s3_scratch_size;
}
#endif
#if CONFIG(SMM_OPAL_S3_STATE_SMRAM)
uintptr_t state_base = 0;
size_t state_size = 0;
if (smm_subregion(SMM_SUBREGION_OPAL_S3_STATE, &state_base, &state_size)) {
printk(BIOS_ERR, "SMM: Failed to locate OPAL S3 state SMRAM region\n");
mod_params->opal_s3_state_base = 0;
mod_params->opal_s3_state_size = 0;
} else {
mod_params->opal_s3_state_base = state_base;
mod_params->opal_s3_state_size = state_size;
/* Clear any stale state on cold boot/reboot, but preserve it on S3 resume. */
if (!acpi_is_wakeup_s3())
memset((void *)state_base, 0, state_size);
}
#endif
}
static void print_region(const char *name, const struct region region)

View file

@ -15,6 +15,8 @@
* +-------------------------+
* | External Stage Cache | SMM_RESERVED_SIZE
* +-------------------------+
* | OPAL S3 State (opt) | SMM_OPAL_S3_STATE_SMRAM_SIZE
* +-------------------------+
* | code and data |
* | (TSEG) |
* +-------------------------+ TSEG
@ -25,6 +27,7 @@ int smm_subregion(int sub, uintptr_t *start, size_t *size)
size_t sub_size;
const size_t ied_size = CONFIG_IED_REGION_SIZE;
const size_t cache_size = CONFIG_SMM_RESERVED_SIZE;
const size_t opal_state_size = CONFIG_SMM_OPAL_S3_STATE_SMRAM_SIZE;
if (CONFIG(SMM_TSEG))
smm_region(&sub_base, &sub_size);
@ -34,13 +37,21 @@ int smm_subregion(int sub, uintptr_t *start, size_t *size)
return -1;
ASSERT(IS_ALIGNED(sub_base, sub_size));
ASSERT(sub_size > (cache_size + ied_size));
ASSERT(sub_size > (cache_size + ied_size + opal_state_size));
switch (sub) {
case SMM_SUBREGION_HANDLER:
/* Handler starts at the base of TSEG. */
sub_size -= ied_size;
sub_size -= cache_size;
sub_size -= opal_state_size;
break;
case SMM_SUBREGION_OPAL_S3_STATE:
if (!opal_state_size)
return -1;
/* Persistent OPAL S3 state lives below the external stage cache. */
sub_base += sub_size - (ied_size + cache_size + opal_state_size);
sub_size = opal_state_size;
break;
case SMM_SUBREGION_CACHE:
/* External cache is in the middle of TSEG. */

View file

@ -100,6 +100,10 @@ struct smm_runtime {
uintptr_t opal_s3_scratch_base;
size_t opal_s3_scratch_size;
#endif
#if CONFIG(SMM_OPAL_S3_STATE_SMRAM)
uintptr_t opal_s3_state_base;
size_t opal_s3_state_size;
#endif
} __packed;
struct smm_module_params {
@ -206,6 +210,8 @@ static inline void aseg_region(uintptr_t *start, size_t *size)
enum {
/* SMM handler area. */
SMM_SUBREGION_HANDLER,
/* Persistent OPAL S3 state area. */
SMM_SUBREGION_OPAL_S3_STATE,
/* SMM cache region. */
SMM_SUBREGION_CACHE,
/* Chipset specific area. */
@ -245,5 +251,8 @@ void smm_get_smmstore_com_buffer(uintptr_t *base, size_t *size);
#if CONFIG(SMM_OPAL_S3_SCRATCH_CBMEM)
void smm_get_opal_s3_scratch_buffer(uintptr_t *base, size_t *size);
#endif
#if CONFIG(SMM_OPAL_S3_STATE_SMRAM)
void smm_get_opal_s3_state_buffer(uintptr_t *base, size_t *size);
#endif
#endif /* CPU_X86_SMM_H */