Marshall Dawson has uploaded this change for review. ( https://review.coreboot.org/22727
Change subject: soc/amd/common: Add S3 supporting functions ......................................................................
soc/amd/common: Add S3 supporting functions
Add functions that the wrapper will call to get and save the S3 data. The wrapper requires two types of data saved: * Non-volatile: information that is the minimum required for bringing the memory controller back online * Volatile: may be stored in DRAM; information required to complete the system restoration process.
For each type, the code stores AGESA's reported size followed by the data itself.
AGESA does not save and restore MTRR settings, so this patch saves the current register settings and adds a function to restore them on a per-core basis. (It is assumed each core is properly in sync during a suspend.) AGESA restores the Top Of Memory and TOM2 registers.
Change-Id: Ie60162ea10f053393bc84e927dbd80c9279e6b63 Signed-off-by: Marshall Dawson marshalldawson3rd@gmail.com --- M src/soc/amd/common/Makefile.inc M src/soc/amd/common/s3_resume.c M src/soc/amd/common/s3_resume.h 3 files changed, 287 insertions(+), 10 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/27/22727/1
diff --git a/src/soc/amd/common/Makefile.inc b/src/soc/amd/common/Makefile.inc index 926262c..fe46233 100644 --- a/src/soc/amd/common/Makefile.inc +++ b/src/soc/amd/common/Makefile.inc @@ -9,6 +9,7 @@ romstage-y += agesawrapper.c romstage-y += def_callouts.c romstage-y += heapmanager.c +romstage-$(CONFIG_HAVE_ACPI_RESUME) += s3_resume.c
ramstage-y += agesawrapper.c ramstage-y += amd_late_init.c diff --git a/src/soc/amd/common/s3_resume.c b/src/soc/amd/common/s3_resume.c index 44c86e6..678f65b 100644 --- a/src/soc/amd/common/s3_resume.c +++ b/src/soc/amd/common/s3_resume.c @@ -1,7 +1,8 @@ /* * This file is part of the coreboot project. * - * Copyright (C) 2012 Advanced Micro Devices, Inc. + * Copyright (C) 2012-2017 Advanced Micro Devices, Inc. + * Copyright (C) 2014 Google Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -13,27 +14,283 @@ * GNU General Public License for more details. */
+#include <cpu/x86/msr.h> +#include <cpu/x86/mtrr.h> +#include <cpu/amd/mtrr.h> +#include <cpu/x86/cache.h> +#include <cbmem.h> +#include <fmap.h> #include <console/console.h> #include <spi-generic.h> #include <spi_flash.h> +#include <region_file.h> +#include <security/vboot/vboot_common.h> +#include <string.h> +#include <soc/iomap.h> #include "s3_resume.h" +#include <BiosCallOuts.h> +#include <agesa_headers.h>
-void spi_SaveS3info(u32 pos, u32 size, u8 *buf, u32 len) +#if !IS_ENABLED(CONFIG_SPI_FLASH) +#error "CONFIG_SPI_FLASH must be enabled" +#endif + +int spi_save_s3_info(struct region dst, struct region src) { struct spi_flash flash; + int status; + + if (src.size == 0) + return 0;
spi_init(); if (spi_flash_probe(0, 0, &flash)) { - printk(BIOS_DEBUG, "Could not find SPI device\n"); - /* Dont make flow stop. */ + printk(BIOS_DEBUG, "S3 SPI: couldn't find SPI device\n"); + return -1; + } + + /* need to write the data plus the size below */ + if (src.size + sizeof(src.size) > dst.size) { + printk(BIOS_DEBUG, "S3 SPI: src is larger than dest.\n"); + return -1; + } + + status = spi_flash_volatile_group_begin(&flash); + if (status) + goto error; + + status = spi_flash_erase(&flash, dst.offset, dst.size); + if (status) + goto error; + + /* write size first */ + status = spi_flash_write(&flash, dst.offset, sizeof(src.size), + (void *)&src.size); + if (status) + goto error; + + /* write buffer */ + status = spi_flash_write(&flash, dst.offset + sizeof(dst.size), + src.size, (void *)src.offset); + +error: + spi_flash_volatile_group_end(&flash); + return status; +} + +static int lookup_region_by_name(const char *name, struct region *r) +{ + /* todo: make Intel MRC cache location common, convert AMD to use it. */ + if (fmap_locate_area(name, r) == 0) + return 0; + + if (IS_ENABLED(CONFIG_VBOOT)) { + printk(BIOS_ERR, "Error: vboot requires flash map for S3\n"); + return -1; + } + + /* Fall back to Kconfig values */ + if (!CONFIG_S3_DATA_POS || !CONFIG_S3_DATA_SIZE) + return -1; + + r->offset = CONFIG_S3_DATA_POS; + r->size = CONFIG_S3_DATA_SIZE; + return 0; +} + +static void get_s3nv_region(struct region *nv) +{ + int status = lookup_region_by_name("RW_MRC_CACHE", nv); + if (status) { + nv->offset = 0; + nv->size = 0; + return; + } +} + +static void get_s3vol_region(struct region *vol) +{ + struct cbmem_usage *heap = (struct cbmem_usage *)GetHeapBase(); + if (!heap) { + printk(BIOS_ERR, "Error locating CBMEM_ID_RESUME_SCRATCH\n"); + return; + } + vol->offset = (size_t)heap->s3_vol_data; + vol->size = S3_VOLATILE_SIZE; +} + +static void get_s3mtrr_region(struct region *mtrr) +{ + struct cbmem_usage *heap = (struct cbmem_usage *)GetHeapBase(); + if (!heap) { + printk(BIOS_ERR, "Error locating CBMEM_ID_RESUME_SCRATCH\n"); + return; + } + mtrr->offset = (size_t)&heap->s3_mtrrs; + mtrr->size = sizeof(struct all_mtrrs); +} + +void restore_mtrr(void) +{ + u32 i; + msr_t msr; + struct all_mtrrs *mtrrs; + int num_vmtrrs; + + struct cbmem_usage *heap = (struct cbmem_usage *)GetHeapBase(); + if (!heap) { + printk(BIOS_ERR, "Error locating CBMEM_ID_RESUME_SCRATCH\n"); + return; + } + mtrrs = (struct all_mtrrs *)&heap->s3_mtrrs; + + msr = rdmsr(MTRR_CAP_MSR); + num_vmtrrs = msr.lo & MTRR_CAP_VCNT; + + disable_cache(); + + /* Enable access to AMD RdDram and WrDram extension bits */ + msr = rdmsr(SYSCFG_MSR); + msr.lo |= SYSCFG_MSR_MtrrFixDramModEn; + wrmsr(SYSCFG_MSR, msr); + + /* Now restore the Fixed MTRRs */ + wrmsr(MTRR_FIX_64K_00000, mtrrs->_64k_00); + wrmsr(MTRR_FIX_16K_80000, mtrrs->_16k_80); + wrmsr(MTRR_FIX_16K_A0000, mtrrs->_16k_a0); + wrmsr(MTRR_FIX_4K_C0000, mtrrs->_4k_c0); + wrmsr(MTRR_FIX_4K_C8000, mtrrs->_4k_c8); + wrmsr(MTRR_FIX_4K_D0000, mtrrs->_4k_d0); + wrmsr(MTRR_FIX_4K_D8000, mtrrs->_4k_d8); + wrmsr(MTRR_FIX_4K_E0000, mtrrs->_4k_e0); + wrmsr(MTRR_FIX_4K_E8000, mtrrs->_4k_e8); + wrmsr(MTRR_FIX_4K_F0000, mtrrs->_4k_f0); + wrmsr(MTRR_FIX_4K_F8000, mtrrs->_4k_f8); + + for (i = 0 ; i < num_vmtrrs ; i++) { + wrmsr(MTRR_PHYS_BASE(i), mtrrs->vmtrr[i].base); + wrmsr(MTRR_PHYS_MASK(i), mtrrs->vmtrr[i].mask); + } + + wrmsr(SYSCFG_MSR, mtrrs->sys_cfg); +} + +static int save_mtrr(struct all_mtrrs *mtrrs) +{ + u32 i; + msr_t msr; + int num_vmtrrs; + + msr = rdmsr(MTRR_CAP_MSR); + num_vmtrrs = msr.lo & MTRR_CAP_VCNT; /* no. of supported var. MTRRs */ + + if (num_vmtrrs > MAX_VAR_MTRRS) { + printk(BIOS_ERR, "Error: Increase MAX_VAR_MTRRS for S3\n"); + return -1; + } + + /* Start saving */ + mtrrs->sys_cfg = rdmsr(SYSCFG_MSR); + + /* Enable access to AMD RdDram and WrDram extension bits */ + msr = rdmsr(SYSCFG_MSR); + msr.lo |= SYSCFG_MSR_MtrrFixDramModEn; + wrmsr(SYSCFG_MSR, msr); + + /* Save the Fixed MTRRs */ + mtrrs->_64k_00 = rdmsr(MTRR_FIX_64K_00000); + mtrrs->_16k_80 = rdmsr(MTRR_FIX_16K_80000); + mtrrs->_16k_a0 = rdmsr(MTRR_FIX_16K_A0000); + mtrrs->_4k_c0 = rdmsr(MTRR_FIX_4K_C0000); + mtrrs->_4k_c8 = rdmsr(MTRR_FIX_4K_C8000); + mtrrs->_4k_d0 = rdmsr(MTRR_FIX_4K_D0000); + mtrrs->_4k_d8 = rdmsr(MTRR_FIX_4K_D8000); + mtrrs->_4k_e0 = rdmsr(MTRR_FIX_4K_E0000); + mtrrs->_4k_e8 = rdmsr(MTRR_FIX_4K_E8000); + mtrrs->_4k_f0 = rdmsr(MTRR_FIX_4K_F0000); + mtrrs->_4k_f8 = rdmsr(MTRR_FIX_4K_F8000); + + /* Disable access to AMD RdDram and WrDram extension bits */ + msr = rdmsr(SYSCFG_MSR); + msr.lo &= ~SYSCFG_MSR_MtrrFixDramModEn; + wrmsr(SYSCFG_MSR, msr); + + for (i = 0 ; i < num_vmtrrs ; i++) { + mtrrs->vmtrr[i].base = rdmsr(MTRR_PHYS_BASE(i)); + mtrrs->vmtrr[i].mask = rdmsr(MTRR_PHYS_MASK(i)); + } + + /* AGESA restores TOM and TOM2 */ + + return 0; +} + +void get_s3_info(S3_DATA_TYPE type, struct region *info) +{ + struct region rgn = { .offset = 0, .size = 0 }; + + switch (type) { + case S3_DATA_TYPE_NV: + get_s3nv_region(&rgn); + rgn.offset += FLASH_BASE_ADDR; + printk(BIOS_SPEW, "S3 NV data @0x%zx\n", rgn.offset); + break; + case S3_DATA_TYPE_VOLATILE: + get_s3vol_region(&rgn); + printk(BIOS_SPEW, "S3 volatile data @0x%zx\n", rgn.offset); + break; + default: + printk(BIOS_ERR, "Error: %s called with bad type %d\n", + __func__, type); return; }
- spi_flash_volatile_group_begin(&flash); + info->size = *(size_t *)rgn.offset; + info->offset = rgn.offset + sizeof(info->size); +}
- spi_flash_erase(&flash, pos, size); - spi_flash_write(&flash, pos, sizeof(len), &len); - spi_flash_write(&flash, pos + sizeof(len), len, buf); +int save_s3_info(struct region nv, struct region vol) +{ + struct region dst; + void *data;
- spi_flash_volatile_group_end(&flash); + printk(BIOS_SPEW, "%s(): S3 NV data @0x%zx (0x%zx bytes)" + " volatile data @0x%zx (0x%zx bytes)\n", + __func__, nv.offset, nv.size, + vol.offset, vol.size); + + get_s3vol_region(&dst); + if (vol.size && (dst.offset == 0 || dst.size == 0)) { + printk(BIOS_ERR, "Error: can't access S3 volatile save area\n"); + return -1; + } + + /* write size followed by AGESA's volatile data */ + data = (void *)(dst.offset + sizeof(size_t)); + memset((void *)dst.offset, 0, S3_VOLATILE_SIZE); + + *(size_t *)dst.offset = vol.size; + memcpy(data, (void *)vol.offset, vol.size); + + get_s3mtrr_region(&dst); + if (!dst.offset || !dst.size) { + printk(BIOS_ERR, "Error: can't access S3 MTRR save area\n"); + return -1; + } + + if (save_mtrr((void *)dst.offset)) + return -1; + + get_s3nv_region(&dst); + if (!dst.offset || !dst.size) { + printk(BIOS_ERR, "Error: can't find S3 NV save area\n"); + return -1; + } + + if (spi_save_s3_info(dst, nv)) { + printk(BIOS_ERR, "Error: can't save S3 NV data\n"); + return -1; + } + + return 0; } diff --git a/src/soc/amd/common/s3_resume.h b/src/soc/amd/common/s3_resume.h index 8dc60da..1abb79d 100644 --- a/src/soc/amd/common/s3_resume.h +++ b/src/soc/amd/common/s3_resume.h @@ -17,6 +17,20 @@ #define __S3_RESUME_H__
#include <cpu/x86/msr.h> +#include <commonlib/region.h> + +/* todo: Make the Intel MRC flash region management common and then use that + * instead of redefining it here. + */ +#define DEFAULT_MRC_CACHE "RW_MRC_CACHE" +#define VARIABLE_MRC_CACHE "RW_VAR_MRC_CACHE" +#define RECOVERY_MRC_CACHE "RECOVERY_MRC_CACHE" +#define UNIFIED_MRC_CACHE "UNIFIED_MRC_CACHE" + +typedef enum { + S3_DATA_TYPE_NV = 0, + S3_DATA_TYPE_VOLATILE, +} S3_DATA_TYPE;
#define S3_VOLATILE_SIZE 0x1000
@@ -42,6 +56,11 @@ struct vmtrr_pair vmtrr[MAX_VAR_MTRRS]; };
-void spi_SaveS3info(u32 pos, u32 size, u8 *buf, u32 len); +void restore_mtrr(void); + +int save_s3_info(struct region nv, struct region vol); +void get_s3_info(S3_DATA_TYPE type, struct region *info); + +int spi_save_s3_info(struct region dst, struct region src);
#endif /* __S3_RESUME_H__ */