Andrey Petrov (andrey.petrov@intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17237
-gerrit
commit 14240a4dc605276990f4663f5748c34817f24adc Author: Andrey Petrov andrey.petrov@intel.com Date: Fri Nov 4 16:18:30 2016 -0700
WIP: soc/intel/apollolake: Add save/restore variable MRC cache
Apollolake MRC cache is divided into two regions: constant and variable. Currently they are clubbed together. Since variable data changes across cold reboot it triggers invalidation of the whole cache region. This change declubs the data, adds routines to load/store variable data on flash.
BUG=chrome-os-partner:57515 TEST=with patch series applied: cold reboot, make sure MRC is not updated. Do S3 suspend/resume cycle.
Change-Id: I374519777abe9b9a1e6cceae5318decd405bb527 Signed-off-by: Andrey Petrov andrey.petrov@intel.com --- src/soc/intel/apollolake/Makefile.inc | 1 + src/soc/intel/apollolake/mrc.c | 90 +++++++++++++++++++++++++++++++++++ src/soc/intel/apollolake/mrc.h | 37 ++++++++++++++ src/soc/intel/apollolake/romstage.c | 40 +++++++++++++++- 4 files changed, 166 insertions(+), 2 deletions(-)
diff --git a/src/soc/intel/apollolake/Makefile.inc b/src/soc/intel/apollolake/Makefile.inc index 4f867e1..2c1bc23 100644 --- a/src/soc/intel/apollolake/Makefile.inc +++ b/src/soc/intel/apollolake/Makefile.inc @@ -33,6 +33,7 @@ romstage-y += lpc_lib.c romstage-y += memmap.c romstage-y += meminit.c romstage-y += mmap_boot.c +romstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc.c romstage-y += tsc_freq.c romstage-y += pmutil.c romstage-y += reset.c diff --git a/src/soc/intel/apollolake/mrc.c b/src/soc/intel/apollolake/mrc.c new file mode 100644 index 0000000..11eeb93 --- /dev/null +++ b/src/soc/intel/apollolake/mrc.c @@ -0,0 +1,90 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2016 Intel Corp. + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <assert.h> +#include <bootstate.h> +#include <fmap.h> +#include <ip_checksum.h> +#include <string.h> + +#include "mrc.h" +#include "soc/intel/common/nvm.h" + +#define MRC_VAR_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('V'<<24)) + +int mrc_save_variable_data(const void *payload, size_t payload_size) +{ + struct region_device rdev; + void *regbase; + struct mrc_saved_variable_data data; + + if (!payload) + return -1; + + if (payload_size > ARRAY_SIZE(data.payload)) + return -1; + + if (fmap_locate_area_as_rdev("RW_VAR_MRC_CACHE", &rdev) < 0) + return -1; + + regbase = rdev_mmap_full(&rdev); + memset(&data, 0, sizeof(data)); + + data.signature = MRC_VAR_DATA_SIGNATURE; + data.size = payload_size; + data.checksum = compute_ip_checksum((void*)payload, payload_size); + + memcpy(&data.payload, payload, MIN(payload_size, + ARRAY_SIZE(data.payload))); + rdev_munmap(&rdev, regbase); + nvm_erase(regbase, region_device_sz(&rdev)); + + return nvm_write(regbase, &data, sizeof(data)); +} + +int mrc_restore_variable_data(void *data) +{ + struct region_device rdev; + struct mrc_saved_variable_data *svd; + int ret = -1; + + if (fmap_locate_area_as_rdev("RW_VAR_MRC_CACHE", &rdev) < 0) + return ret; + + svd = (struct mrc_saved_variable_data*) rdev_mmap_full(&rdev); + + if (!svd) + return ret; + + if (svd->signature != MRC_VAR_DATA_SIGNATURE || + svd->size > region_device_sz(&rdev) || + svd->checksum != compute_ip_checksum(&svd->payload, svd->size)) { + printk(BIOS_WARNING, "Variable MRC data unusable\n"); + } else { + memcpy(data, svd->payload, + MIN(ARRAY_SIZE(svd->payload), svd->size)); + ret = 0; + } + + rdev_munmap(&rdev, svd); + return ret; +} + +int mrc_protect_variable_data(void) +{ + /* Not implemented yet */ + return 0; +} diff --git a/src/soc/intel/apollolake/mrc.h b/src/soc/intel/apollolake/mrc.h new file mode 100644 index 0000000..ddcb745 --- /dev/null +++ b/src/soc/intel/apollolake/mrc.h @@ -0,0 +1,37 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2016 Intel Corp. + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SOC_APOLLOLAKE_MRC_H_ +#define _SOC_APOLLOLAKE_MRC_H_ + +#include <stddef.h> +#include <stdint.h> + +#define MRC_MAX_VARDATA_SIZE 32 + +struct mrc_saved_variable_data { + uint32_t signature; + unsigned long checksum; + size_t size; + uint8_t payload[MRC_MAX_VARDATA_SIZE]; +} __attribute__((packed)); + +/* store variable data */ +int mrc_save_variable_data(const void *payload, size_t payload_size); +/* restore MRC into provided storage */ +int mrc_restore_variable_data(void *data); +int mrc_protect_variable_data(void); + +#endif \ No newline at end of file diff --git a/src/soc/intel/apollolake/romstage.c b/src/soc/intel/apollolake/romstage.c index 1f6a38f..cc5d25a 100644 --- a/src/soc/intel/apollolake/romstage.c +++ b/src/soc/intel/apollolake/romstage.c @@ -40,8 +40,18 @@ #include <soc/uart.h> #include <string.h> #include <timestamp.h> +#include "mrc.h" + +#include <lib.h>
static struct chipset_power_state power_state CAR_GLOBAL; +static uint8_t mrc_var_buff[MRC_MAX_VARDATA_SIZE] CAR_GLOBAL; + +static const uint8_t variable_guid[16] = { + 0x7d, 0x14, 0x34, 0xa0, 0x0c, 0x69, 0x54, 0x41, + 0x8d, 0xe6, 0xc0, 0x44, 0x64, 0x1d, 0xe9, 0x42, +}; +
/* High Performance Event Timer Configuration */ #define P2SB_HPTC 0x60 @@ -106,8 +116,9 @@ asmlinkage void car_stage_entry(void) uintptr_t top_of_ram; bool s3wake; struct chipset_power_state *ps = car_get_var_ptr(&power_state); - void *smm_base; - size_t smm_size; + void *smm_base, *mrc_buff; + const void *new_var_data; + size_t smm_size, new_sz; uintptr_t tseg_base;
timestamp_add_now(TS_START_ROMSTAGE); @@ -119,6 +130,15 @@ asmlinkage void car_stage_entry(void)
s3wake = fill_power_state(ps) == ACPI_S3; fsp_memory_init(s3wake); + + /* Update saved MRC variable data if it doesn't match current */ + new_var_data = fsp_find_extension_hob_by_guid(variable_guid, &new_sz); + mrc_buff = car_get_var_ptr(mrc_var_buff); + if (new_var_data && memcmp(mrc_buff, new_var_data, new_sz)) { + printk(BIOS_SPEW, "MRC variable data changed, updating..\n"); + mrc_save_variable_data(new_var_data, new_sz); + } + if (postcar_frame_init(&pcf, 1*KiB)) die("Unable to initialize postcar frame.\n");
@@ -170,6 +190,8 @@ static void fill_console_params(FSPM_UPD *mupd)
void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd) { + void *saved_data = car_get_var_ptr(mrc_var_buff); + fill_console_params(mupd); mainboard_memory_init_params(mupd);
@@ -193,6 +215,20 @@ void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd) * skip HECI2 reset. */ mupd->FspmConfig.EnableS3Heci2 = 0; + + /* + * Apollolake splits MRC cache into two parts: constant and variable. + * The constant part is not expected to change often and variable is. + * Currently variable part consists of parameters that change on cold + * boot, such as scrambler seed, and some memory controller registers. + * Scrambler seed is vital for S3 resume case because attempt to use + * wrong/missing key renders DRAM contents into junk. + */ + + if (saved_data && mrc_restore_variable_data(saved_data) >= 0) { + mupd->FspmConfig.VariableNvsBufferPtr = saved_data; + printk(BIOS_DEBUG,"MRC variable data loaded\n"); + } }
__attribute__ ((weak))