diff --git a/src/cpu/intel/haswell/haswell.h b/src/cpu/intel/haswell/haswell.h index 3a5ebe65ae..a1c6f39ec2 100644 --- a/src/cpu/intel/haswell/haswell.h +++ b/src/cpu/intel/haswell/haswell.h @@ -174,6 +174,23 @@ int setup_ap_init(struct bus *cpu_bus, int *max_cpus, int start_aps(struct bus *cpu_bus, int max_cpus); void release_aps_for_smm_relocation(int do_parallel_relocation); #endif + +/* This structure is saved along with the relocated ramstage program in SMM + * space. It is used to protect the integrity of the ramstage program on S3 + * resume by saving a copy of the relocated ramstage in SMM space with the + * assumption that the SMM region cannot be altered from the OS. The magic + * value just serves as a quick sanity check. */ + +#define RAMSTAGE_CACHE_MAGIC 0xf3c3a02a + +struct ramstage_cache { + uint32_t magic; + uint32_t entry_point; + uint32_t load_address; + uint32_t size; + char program[0]; +} __attribute__((packed)); + #endif #endif diff --git a/src/cpu/intel/haswell/romstage.c b/src/cpu/intel/haswell/romstage.c index b26cbde605..d491c7e4ad 100644 --- a/src/cpu/intel/haswell/romstage.c +++ b/src/cpu/intel/haswell/romstage.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #if CONFIG_CHROMEOS #include @@ -320,3 +321,67 @@ void romstage_after_car(void) /* Load the ramstage. */ copy_and_run(0); } + + +#if CONFIG_RELOCATABLE_RAMSTAGE +void cache_loaded_ramstage(struct romstage_handoff *handoff, + void *ramstage_base, uint32_t ramstage_size, + void *entry_point) +{ + struct ramstage_cache *cache; + uint32_t total_size; + + /* The ramstage cache lives in the TSEG region at RESERVED_SMM_OFFSET. + * The top of ram is defined to be the TSEG base address. */ + cache = (void *)(get_top_of_ram() + RESERVED_SMM_OFFSET); + total_size = sizeof(*cache) + ramstage_size; + if (total_size > RESERVED_SMM_SIZE) { + printk(BIOS_DEBUG, "0x%08x > RESERVED_SMM_SIZE (0x%08x)\n", + total_size, RESERVED_SMM_SIZE); + /* Nuke whatever may be there now just in case. */ + cache->magic = ~RAMSTAGE_CACHE_MAGIC; + return; + } + + cache->magic = RAMSTAGE_CACHE_MAGIC; + cache->entry_point = (uint32_t)entry_point; + cache->load_address = (uint32_t)ramstage_base; + cache->size = ramstage_size; + + printk(BIOS_DEBUG, "Saving ramstage to SMM space cache.\n"); + + /* Copy over the program. */ + memcpy(&cache->program[0], ramstage_base, ramstage_size); + + /* Do not update reserve region if the handoff structure is not + * available. Perhaps the ramstage will fix things up for the resume + * path. */ + if (handoff == NULL) + return; + + /* Update entry and reserve region. */ + handoff->reserve_base = (uint32_t)ramstage_base; + handoff->reserve_size = ramstage_size; + handoff->ramstage_entry_point = (uint32_t)entry_point; +} + +void *load_cached_ramstage(struct romstage_handoff *handoff) +{ + struct ramstage_cache *cache; + + /* The ramstage cache lives in the TSEG region at RESERVED_SMM_OFFSET. + * The top of ram is defined to be the TSEG base address. */ + cache = (void *)(get_top_of_ram() + RESERVED_SMM_OFFSET); + + if (cache->magic != RAMSTAGE_CACHE_MAGIC) { + printk(BIOS_DEBUG, "Invalid ramstage cache found.\n"); + return NULL; + } + + printk(BIOS_DEBUG, "Loading ramstage from SMM space cache.\n"); + + memcpy((void *)cache->load_address, &cache->program[0], cache->size); + + return (void *)cache->entry_point; +} +#endif