coreboot-kgpe-d16/src/drivers/mrc_cache/mrc_cache.c

740 lines
19 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: GPL-2.0-only */
#include <string.h>
#include <boot_device.h>
#include <bootstate.h>
#include <bootmode.h>
#include <console/console.h>
#include <cbmem.h>
#include <elog.h>
#include <fmap.h>
#include <ip_checksum.h>
#include <region_file.h>
#include <security/vboot/antirollback.h>
#include <security/vboot/mrc_cache_hash_tpm.h>
#include <security/vboot/vboot_common.h>
#include <spi_flash.h>
#include "mrc_cache.h"
#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"
#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24))
struct mrc_metadata {
uint32_t signature;
uint32_t data_size;
uint16_t data_checksum;
uint16_t header_checksum;
uint32_t version;
} __packed;
enum result {
UPDATE_FAILURE = -1,
UPDATE_SUCCESS = 0,
ALREADY_UPTODATE = 1
};
#define NORMAL_FLAG (1 << 0)
#define RECOVERY_FLAG (1 << 1)
struct cache_region {
const char *name;
uint32_t cbmem_id;
int type;
int elog_slot;
uint32_t tpm_hash_index;
int flags;
};
static const struct cache_region recovery_training = {
.name = RECOVERY_MRC_CACHE,
.cbmem_id = CBMEM_ID_MRCDATA,
.type = MRC_TRAINING_DATA,
.elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_RECOVERY,
.tpm_hash_index = MRC_REC_HASH_NV_INDEX,
#if CONFIG(HAS_RECOVERY_MRC_CACHE)
.flags = RECOVERY_FLAG,
#else
.flags = 0,
#endif
};
static const struct cache_region normal_training = {
.name = DEFAULT_MRC_CACHE,
.cbmem_id = CBMEM_ID_MRCDATA,
.type = MRC_TRAINING_DATA,
.elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_NORMAL,
.tpm_hash_index = MRC_RW_HASH_NV_INDEX,
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
#if CONFIG(VBOOT_STARTS_IN_ROMSTAGE)
/*
* If VBOOT_STARTS_IN_ROMSTAGE is selected, this means that
* memory training happens before vboot (in RO) and the
* mrc_cache data is always safe to use.
*/
.flags = NORMAL_FLAG | RECOVERY_FLAG,
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
#else
/*
* If !VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens after
* vboot (in RW code) and is never safe to use in recovery.
*/
.flags = NORMAL_FLAG,
#endif
};
static const struct cache_region variable_data = {
.name = VARIABLE_MRC_CACHE,
.cbmem_id = CBMEM_ID_VAR_MRCDATA,
.type = MRC_VARIABLE_DATA,
.elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_VARIABLE,
.tpm_hash_index = 0,
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
#if CONFIG(VBOOT_STARTS_IN_ROMSTAGE)
/*
* If VBOOT_STARTS_IN_ROMSTAGE is selected, this means that
* memory training happens before vboot (in RO) and the
* mrc_cache data is always safe to use.
*/
.flags = NORMAL_FLAG | RECOVERY_FLAG,
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
#else
/*
* If !VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens after
* vboot (in RW code) and is never safe to use in recovery.
*/
.flags = NORMAL_FLAG,
#endif
};
/* Order matters here for priority in matching. */
static const struct cache_region *cache_regions[] = {
&recovery_training,
&normal_training,
&variable_data,
};
/* TPM MRC hash functionality depends on vboot starting before memory init. */
_Static_assert(!CONFIG(MRC_SAVE_HASH_IN_TPM) ||
CONFIG(VBOOT_STARTS_IN_BOOTBLOCK),
"for TPM MRC hash functionality, vboot must start in bootblock");
static int lookup_region_by_name(const char *name, struct region *r)
{
if (fmap_locate_area(name, r) == 0)
return 0;
return -1;
}
static const struct cache_region *lookup_region_type(int type)
{
int i;
int flags;
vboot: Clean up pre-RAM use of vboot_recovery_mode_enabled() vboot_recovery_mode_enabled() was recently changed to assert() when it is called before vboot logic has run, because we cannot determine whether we're going to be in recovery mode at that point and we wanted to flush out existing uses that pretended that we could. Turns out there are a bunch of uses like that, and there is some code that is shared across configurations that can and those that can't. This patch cleans them up to either remove checks that cannot return true, or add explicit Kconfig guards to clarify that the code is shared. This means that using a separate recovery MRC cache is no longer supported on boards that use VBOOT_STARTS_IN_ROMSTAGE (this has already been broken with CB:38780, but with this patch those boards will boot again using their normal MRC caches rather than just die). Skipping the MRC cache and always regenerating from scratch in recovery mode is likewise no longer supported for VBOOT_STARTS_IN_ROMSTAGE. For FSP1.1 boards, none of them support VBOOT_STARTS_IN_BOOTBLOCK and that is unlikely to change in the future so we will just hardcode that fact in Kconfig (otherwise, fsp1.1 raminit would also have to be fixed to work around this issue). Signed-off-by: Julius Werner <jwerner@chromium.org> Change-Id: I31bfc7663724fdacab9955224dcaf650d1ec1c3c Reviewed-on: https://review.coreboot.org/c/coreboot/+/39221 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Aaron Durbin <adurbin@chromium.org>
2020-03-03 00:54:43 +01:00
if (CONFIG(VBOOT_STARTS_IN_BOOTBLOCK) && vboot_recovery_mode_enabled())
flags = RECOVERY_FLAG;
else
flags = NORMAL_FLAG;
for (i = 0; i < ARRAY_SIZE(cache_regions); i++) {
if (cache_regions[i]->type != type)
continue;
if ((cache_regions[i]->flags & flags) == flags)
return cache_regions[i];
}
return NULL;
}
static const struct cache_region *lookup_region(struct region *r, int type)
{
const struct cache_region *cr;
cr = lookup_region_type(type);
if (cr == NULL) {
printk(BIOS_ERR, "MRC: failed to locate region type %d.\n",
2020-05-02 02:00:31 +02:00
type);
return NULL;
}
if (lookup_region_by_name(cr->name, r) < 0)
return NULL;
return cr;
}
static int mrc_header_valid(struct region_device *rdev, struct mrc_metadata *md)
{
uint16_t checksum;
uint16_t checksum_result;
size_t size;
if (rdev_readat(rdev, md, 0, sizeof(*md)) < 0) {
printk(BIOS_ERR, "MRC: couldn't read metadata\n");
return -1;
}
if (md->signature != MRC_DATA_SIGNATURE) {
printk(BIOS_ERR, "MRC: invalid header signature\n");
return -1;
}
/* Compute checksum over header with 0 as the value. */
checksum = md->header_checksum;
md->header_checksum = 0;
checksum_result = compute_ip_checksum(md, sizeof(*md));
if (checksum != checksum_result) {
printk(BIOS_ERR, "MRC: header checksum mismatch: %x vs %x\n",
checksum, checksum_result);
return -1;
}
/* Put back original. */
md->header_checksum = checksum;
/* Re-size the region device according to the metadata as a region_file
* does block allocation. */
size = sizeof(*md) + md->data_size;
if (rdev_chain(rdev, rdev, 0, size) < 0) {
printk(BIOS_ERR, "MRC: size exceeds rdev size: %zx vs %zx\n",
size, region_device_sz(rdev));
return -1;
}
return 0;
}
static int mrc_data_valid(int type, const struct mrc_metadata *md,
void *data, size_t data_size)
{
uint16_t checksum;
const struct cache_region *cr = lookup_region_type(type);
uint32_t hash_idx;
if (cr == NULL)
return -1;
if (md->data_size != data_size)
return -1;
hash_idx = cr->tpm_hash_index;
if (hash_idx && CONFIG(MRC_SAVE_HASH_IN_TPM)) {
if (!mrc_cache_verify_hash(hash_idx, data, data_size))
return -1;
} else {
checksum = compute_ip_checksum(data, data_size);
if (md->data_checksum != checksum) {
printk(BIOS_ERR, "MRC: data checksum mismatch: %x vs %x\n",
md->data_checksum, checksum);
return -1;
}
}
return 0;
}
static int mrc_cache_get_latest_slot_info(const char *name,
const struct region_device *backing_rdev,
struct mrc_metadata *md,
struct region_file *cache_file,
struct region_device *rdev,
bool fail_bad_data)
{
/* Init and obtain a handle to the file data. */
if (region_file_init(cache_file, backing_rdev) < 0) {
printk(BIOS_ERR, "MRC: region file invalid in '%s'\n", name);
return -1;
}
/* Provide a 0 sized region_device from here on out so the caller
* has a valid yet unusable region_device. */
rdev_chain(rdev, backing_rdev, 0, 0);
/* No data to return. */
if (region_file_data(cache_file, rdev) < 0) {
printk(BIOS_ERR, "MRC: no data in '%s'\n", name);
return fail_bad_data ? -1 : 0;
}
/* Validate header and resize region to reflect actual usage on the
* saved medium (including metadata and data). */
if (mrc_header_valid(rdev, md) < 0) {
printk(BIOS_ERR, "MRC: invalid header in '%s'\n", name);
return fail_bad_data ? -1 : 0;
}
return 0;
}
static int mrc_cache_find_current(int type, uint32_t version,
struct region_device *rdev,
struct mrc_metadata *md)
{
const struct cache_region *cr;
struct region region;
struct region_device read_rdev;
struct region_file cache_file;
size_t data_size;
const size_t md_size = sizeof(*md);
const bool fail_bad_data = true;
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
/*
* In recovery mode, force retraining if the memory retrain
* switch is set.
*/
if (vboot_recovery_mode_enabled() && get_recovery_mode_retrain_switch())
return -1;
cr = lookup_region(&region, type);
if (cr == NULL)
return -1;
if (boot_device_ro_subregion(&region, &read_rdev) < 0)
return -1;
if (mrc_cache_get_latest_slot_info(cr->name,
&read_rdev,
md,
&cache_file,
rdev,
fail_bad_data) < 0)
return -1;
if (version != md->version) {
printk(BIOS_INFO, "MRC: version mismatch: %x vs %x\n",
md->version, version);
return -1;
}
/* Re-size rdev to only contain the data. i.e. remove metadata. */
data_size = md->data_size;
return rdev_chain(rdev, rdev, md_size, data_size);
}
ssize_t mrc_cache_load_current(int type, uint32_t version, void *buffer,
size_t buffer_size)
{
struct region_device rdev;
struct mrc_metadata md;
ssize_t data_size;
if (mrc_cache_find_current(type, version, &rdev, &md) < 0)
return -1;
data_size = region_device_sz(&rdev);
if (buffer_size < data_size)
return -1;
if (rdev_readat(&rdev, buffer, 0, data_size) != data_size)
return -1;
if (mrc_data_valid(type, &md, buffer, data_size) < 0)
return -1;
return data_size;
}
void *mrc_cache_current_mmap_leak(int type, uint32_t version,
size_t *data_size)
{
struct region_device rdev;
void *data;
size_t region_device_size;
struct mrc_metadata md;
if (mrc_cache_find_current(type, version, &rdev, &md) < 0)
return NULL;
region_device_size = region_device_sz(&rdev);
if (data_size)
*data_size = region_device_size;
data = rdev_mmap_full(&rdev);
if (data == NULL) {
printk(BIOS_INFO, "MRC: mmap failure.\n");
return NULL;
}
if (mrc_data_valid(type, &md, data, region_device_size) < 0)
return NULL;
return data;
}
static bool mrc_cache_needs_update(const struct region_device *rdev,
2020-05-02 02:00:31 +02:00
const struct mrc_metadata *new_md,
const void *new_data, size_t new_data_size)
{
2020-05-02 02:00:31 +02:00
void *mapping, *data_mapping;
size_t old_data_size = region_device_sz(rdev) - sizeof(struct mrc_metadata);
bool need_update = false;
if (new_data_size != old_data_size)
return true;
mapping = rdev_mmap_full(rdev);
2020-05-02 02:00:31 +02:00
if (mapping == NULL) {
printk(BIOS_ERR, "MRC: cannot mmap existing cache.\n");
return true;
}
data_mapping = mapping + sizeof(struct mrc_metadata);
/* we need to compare the md and the data separately */
/* check the mrc_metadata */
if (memcmp(new_md, mapping, sizeof(struct mrc_metadata)))
need_update = true;
2020-05-02 02:00:31 +02:00
/* check the data */
if (!need_update && memcmp(new_data, data_mapping, new_data_size))
need_update = true;
rdev_munmap(rdev, mapping);
return need_update;
}
static void log_event_cache_update(uint8_t slot, enum result res)
{
const int type = ELOG_TYPE_MEM_CACHE_UPDATE;
struct elog_event_mem_cache_update event = {
.slot = slot
};
/* Filter through interesting events only */
switch (res) {
case UPDATE_FAILURE:
event.status = ELOG_MEM_CACHE_UPDATE_STATUS_FAIL;
break;
case UPDATE_SUCCESS:
event.status = ELOG_MEM_CACHE_UPDATE_STATUS_SUCCESS;
break;
default:
return;
}
if (elog_add_event_raw(type, &event, sizeof(event)) < 0)
printk(BIOS_ERR, "Failed to log mem cache update event.\n");
}
/* During ramstage this code purposefully uses incoherent transactions between
* read and write. The read assumes a memory-mapped boot device that can be used
* to quickly locate and compare the up-to-date data. However, when an update
* is required it uses the writeable region access to perform the update. */
2020-05-02 02:00:31 +02:00
static void update_mrc_cache_by_type(int type,
struct mrc_metadata *new_md,
const void *new_data,
size_t new_data_size)
{
const struct cache_region *cr;
struct region region;
struct region_device read_rdev;
struct region_device write_rdev;
struct region_file cache_file;
struct mrc_metadata md;
struct incoherent_rdev backing_irdev;
const struct region_device *backing_rdev;
struct region_device latest_rdev;
const bool fail_bad_data = false;
uint32_t hash_idx;
cr = lookup_region(&region, type);
if (cr == NULL)
return;
printk(BIOS_DEBUG, "MRC: Checking cached data update for '%s'.\n",
cr->name);
if (boot_device_ro_subregion(&region, &read_rdev) < 0)
return;
if (boot_device_rw_subregion(&region, &write_rdev) < 0)
return;
backing_rdev = incoherent_rdev_init(&backing_irdev, &region, &read_rdev,
&write_rdev);
if (backing_rdev == NULL)
return;
/* Note that mrc_cache_get_latest_slot_info doesn't check the
* validity of the current slot. If the slot is invalid,
* we'll overwrite it anyway when we update the mrc_cache.
*/
if (mrc_cache_get_latest_slot_info(cr->name,
backing_rdev,
&md,
&cache_file,
&latest_rdev,
fail_bad_data) < 0)
return;
2020-05-02 02:00:31 +02:00
if (!mrc_cache_needs_update(&latest_rdev,
new_md, new_data, new_data_size)) {
printk(BIOS_DEBUG, "MRC: '%s' does not need update.\n", cr->name);
log_event_cache_update(cr->elog_slot, ALREADY_UPTODATE);
return;
}
printk(BIOS_DEBUG, "MRC: cache data '%s' needs update.\n", cr->name);
2020-05-02 02:00:31 +02:00
struct update_region_file_entry entries[] = {
[0] = {
.size = sizeof(struct mrc_metadata),
.data = new_md,
},
[1] = {
.size = new_data_size,
.data = new_data,
},
};
if (region_file_update_data_arr(&cache_file, entries, ARRAY_SIZE(entries)) < 0) {
printk(BIOS_ERR, "MRC: failed to update '%s'.\n", cr->name);
log_event_cache_update(cr->elog_slot, UPDATE_FAILURE);
} else {
printk(BIOS_DEBUG, "MRC: updated '%s'.\n", cr->name);
log_event_cache_update(cr->elog_slot, UPDATE_SUCCESS);
hash_idx = cr->tpm_hash_index;
if (hash_idx && CONFIG(MRC_SAVE_HASH_IN_TPM))
mrc_cache_update_hash(hash_idx, new_data, new_data_size);
}
}
/* Read flash status register to determine if write protect is active */
static int nvm_is_write_protected(void)
{
u8 sr1;
u8 wp_gpio;
u8 wp_spi;
if (!CONFIG(CHROMEOS))
return 0;
if (!CONFIG(BOOT_DEVICE_SPI_FLASH))
return 0;
/* Read Write Protect GPIO if available */
wp_gpio = get_write_protect_state();
/* Read Status Register 1 */
if (spi_flash_status(boot_device_spi_flash(), &sr1) < 0) {
printk(BIOS_ERR, "Failed to read SPI status register 1\n");
return -1;
}
wp_spi = !!(sr1 & 0x80);
printk(BIOS_DEBUG, "SPI flash protection: WPSW=%d SRP0=%d\n",
wp_gpio, wp_spi);
return wp_gpio && wp_spi;
}
/* Apply protection to a range of flash */
static int nvm_protect(const struct region *r)
{
if (!CONFIG(MRC_SETTINGS_PROTECT))
return 0;
if (!CONFIG(BOOT_DEVICE_SPI_FLASH))
return 0;
return spi_flash_ctrlr_protect_region(boot_device_spi_flash(), r, WRITE_PROTECT);
}
/* Protect mrc region with a Protected Range Register */
static int protect_mrc_cache(const char *name)
{
struct region region;
if (!CONFIG(MRC_SETTINGS_PROTECT))
return 0;
if (lookup_region_by_name(name, &region) < 0) {
printk(BIOS_INFO, "MRC: Could not find region '%s'\n", name);
return -1;
}
if (nvm_is_write_protected() <= 0) {
printk(BIOS_INFO, "MRC: NOT enabling PRR for '%s'.\n", name);
return 0;
}
if (nvm_protect(&region) < 0) {
printk(BIOS_ERR, "MRC: ERROR setting PRR for '%s'.\n", name);
return -1;
}
printk(BIOS_INFO, "MRC: Enabled Protected Range on '%s'.\n", name);
return 0;
}
static void protect_mrc_region(void)
{
/*
* Check if there is a single unified region that encompasses both
* RECOVERY_MRC_CACHE and DEFAULT_MRC_CACHE. In that case protect the
* entire region using a single PRR.
*
* If we are not able to protect the entire region, try protecting
* individual regions next.
*/
if (protect_mrc_cache(UNIFIED_MRC_CACHE) == 0)
return;
if (CONFIG(HAS_RECOVERY_MRC_CACHE))
protect_mrc_cache(RECOVERY_MRC_CACHE);
protect_mrc_cache(DEFAULT_MRC_CACHE);
}
static void invalidate_normal_cache(void)
{
struct region_file cache_file;
struct region_device rdev;
const char *name = DEFAULT_MRC_CACHE;
const uint32_t invalid = ~MRC_DATA_SIGNATURE;
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
/*
* If !HAS_RECOVERY_MRC_CACHE and VBOOT_STARTS_IN_ROMSTAGE is
* selected, this means that memory training occurs before
* verified boot (in RO), so normal mode cache does not need
* to be invalidated.
*/
if (!CONFIG(HAS_RECOVERY_MRC_CACHE) && CONFIG(VBOOT_STARTS_IN_ROMSTAGE))
return;
/* We only invalidate the normal cache in recovery mode. */
if (!vboot_recovery_mode_enabled())
return;
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
/*
* For platforms with a recovery mrc_cache, no need to
* invalidate when retrain switch is not set.
*/
if (CONFIG(HAS_RECOVERY_MRC_CACHE) && !get_recovery_mode_retrain_switch())
return;
if (fmap_locate_area_as_rdev_rw(name, &rdev) < 0) {
printk(BIOS_ERR, "MRC: Couldn't find '%s' region. Invalidation failed\n",
name);
return;
}
if (region_file_init(&cache_file, &rdev) < 0) {
printk(BIOS_ERR, "MRC: region file invalid for '%s'. Invalidation failed\n",
name);
return;
}
/* Push an update that consists of 4 bytes that is smaller than the
* MRC metadata as well as an invalid signature. */
if (region_file_update_data(&cache_file, &invalid, sizeof(invalid)) < 0)
printk(BIOS_ERR, "MRC: invalidation failed for '%s'.\n", name);
}
2020-05-02 02:00:31 +02:00
static void update_mrc_cache_from_cbmem(int type)
{
const struct cache_region *cr;
struct region region;
const struct cbmem_entry *to_be_updated;
cr = lookup_region(&region, type);
if (cr == NULL) {
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
printk(BIOS_INFO, "MRC: could not find cache_region type %d\n", type);
2020-05-02 02:00:31 +02:00
return;
}
to_be_updated = cbmem_entry_find(cr->cbmem_id);
if (to_be_updated == NULL) {
printk(BIOS_INFO, "MRC: No data in cbmem for '%s'.\n",
cr->name);
return;
}
update_mrc_cache_by_type(type,
/* pointer to mrc_cache entry metadata header */
cbmem_entry_start(to_be_updated),
/* pointer to start of mrc_cache entry data */
cbmem_entry_start(to_be_updated) +
sizeof(struct mrc_metadata),
/* size of just data portion of the entry */
cbmem_entry_size(to_be_updated) -
sizeof(struct mrc_metadata));
}
static void finalize_mrc_cache(void *unused)
{
2020-05-02 02:00:31 +02:00
if (CONFIG(MRC_STASH_TO_CBMEM)) {
update_mrc_cache_from_cbmem(MRC_TRAINING_DATA);
2020-05-02 02:00:31 +02:00
if (CONFIG(MRC_SETTINGS_VARIABLE_DATA))
update_mrc_cache_from_cbmem(MRC_VARIABLE_DATA);
}
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
invalidate_normal_cache();
protect_mrc_region();
}
int mrc_cache_stash_data(int type, uint32_t version, const void *data,
2020-05-02 02:00:31 +02:00
size_t size)
{
const struct cache_region *cr;
2020-05-02 02:00:31 +02:00
struct mrc_metadata md = {
.signature = MRC_DATA_SIGNATURE,
.data_size = size,
.version = version,
.data_checksum = compute_ip_checksum(data, size),
};
md.header_checksum =
compute_ip_checksum(&md, sizeof(struct mrc_metadata));
if (CONFIG(MRC_STASH_TO_CBMEM)) {
/* Store data in cbmem for use in ramstage */
struct mrc_metadata *cbmem_md;
size_t cbmem_size;
cbmem_size = sizeof(*cbmem_md) + size;
mrc_cache: Move code for triggering memory training into mrc_cache Currently the decision of whether or not to use mrc_cache in recovery mode is made within the individual platforms' drivers (ie: fsp2.0, fsp1.1, etc.). As this is not platform specific, but uses common vboot infrastructure, the code can be unified and moved into mrc_cache. The conditions are as follows: 1. If HAS_RECOVERY_MRC_CACHE, use mrc_cache data (unless retrain switch is true) 2. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_BOOTBLOCK, this means that memory training will occur after verified boot, meaning that mrc_cache will be filled with data from executing RW code. So in this case, we never want to use the training data in the mrc_cache for recovery mode. 3. If !HAS_RECOVERY_MRC_CACHE && VBOOT_STARTS_IN_ROMSTAGE, this means that memory training happens before verfied boot, meaning that the mrc_cache data is generated by RO code, so it is safe to use for a recovery boot. 4. Any platform that does not use vboot should be unaffected. Additionally, we have removed the MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN config because the mrc_cache driver takes care of invalidating the mrc_cache data for normal mode. If the platform: 1. !HAS_RECOVERY_MRC_CACHE, always invalidate mrc_cache data 2. HAS_RECOVERY_MRC_CACHE, only invalidate if retrain switch is set BUG=b:150502246 BRANCH=None TEST=1. run dut-control power_state:rec_force_mrc twice on lazor ensure that memory retraining happens both times run dut-control power_state:rec twice on lazor ensure that memory retraining happens only first time 2. remove HAS_RECOVERY_MRC_CACHE from lazor Kconfig boot twice to ensure caching of memory training occurred on each boot. Change-Id: I3875a7b4a4ba3c1aa8a3c1507b3993036a7155fc Signed-off-by: Shelley Chen <shchen@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/46855 Reviewed-by: Furquan Shaikh <furquan@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
2020-10-27 23:58:31 +01:00
cr = lookup_region_type(type);
if (cr == NULL) {
printk(BIOS_INFO, "MRC: No region type found. Skip adding to cbmem for type %d.\n",
type);
return 0;
}
2020-05-02 02:00:31 +02:00
cbmem_md = cbmem_add(cr->cbmem_id, cbmem_size);
if (cbmem_md == NULL) {
printk(BIOS_ERR, "MRC: failed to add '%s' to cbmem.\n",
cr->name);
return -1;
}
memcpy(cbmem_md, &md, sizeof(*cbmem_md));
/* cbmem_md + 1 is the pointer to the mrc_cache data */
memcpy(cbmem_md + 1, data, size);
} else {
/* Otherwise store to mrc_cache right away */
update_mrc_cache_by_type(type, &md, data, size);
}
return 0;
}
/*
* Ensures MRC training data is stored into SPI after PCI enumeration is done.
* Some implementations may require this to be later than others.
*/
#if CONFIG(MRC_WRITE_NV_LATE)
2020-05-02 02:00:31 +02:00
BOOT_STATE_INIT_ENTRY(BS_OS_RESUME_CHECK, BS_ON_ENTRY, finalize_mrc_cache, NULL);
#else
2020-05-02 02:00:31 +02:00
BOOT_STATE_INIT_ENTRY(BS_DEV_ENUMERATE, BS_ON_EXIT, finalize_mrc_cache, NULL);
#endif