From decd062875c1e33d4c9203c2edc0652792a46e73 Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 15 Dec 2017 12:26:40 -0700 Subject: drivers/mrc_cache: move mrc_cache support to drivers There's nothing intel-specific about the current mrc_cache support. It's logic manages saving non-volatile areas into the boot media. Therefore, expose it to the rest of the system for any and all to use. BUG=b:69614064 Change-Id: I3b331c82a102f88912a3e10507a70207fb20aecc Signed-off-by: Aaron Durbin Reviewed-on: https://review.coreboot.org/22901 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh --- src/drivers/intel/fsp1_1/romstage.c | 2 +- src/drivers/intel/fsp2_0/memory_init.c | 2 +- src/drivers/mrc_cache/Kconfig | 31 ++ src/drivers/mrc_cache/Makefile.inc | 16 + src/drivers/mrc_cache/mrc_cache.c | 581 +++++++++++++++++++++++++++++ src/include/mrc_cache.h | 43 +++ src/soc/intel/apollolake/romstage.c | 2 +- src/soc/intel/baytrail/romstage/raminit.c | 2 +- src/soc/intel/braswell/romstage/romstage.c | 2 +- src/soc/intel/broadwell/romstage/raminit.c | 2 +- src/soc/intel/common/Kconfig | 32 -- src/soc/intel/common/Makefile.inc | 15 - src/soc/intel/common/mrc_cache.c | 581 ----------------------------- src/soc/intel/common/mrc_cache.h | 43 --- 14 files changed, 677 insertions(+), 677 deletions(-) create mode 100644 src/drivers/mrc_cache/Kconfig create mode 100644 src/drivers/mrc_cache/Makefile.inc create mode 100644 src/drivers/mrc_cache/mrc_cache.c create mode 100644 src/include/mrc_cache.h delete mode 100644 src/soc/intel/common/mrc_cache.c delete mode 100644 src/soc/intel/common/mrc_cache.h diff --git a/src/drivers/intel/fsp1_1/romstage.c b/src/drivers/intel/fsp1_1/romstage.c index e6dec251be..81939c4c33 100644 --- a/src/drivers/intel/fsp1_1/romstage.c +++ b/src/drivers/intel/fsp1_1/romstage.c @@ -28,11 +28,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/src/drivers/intel/fsp2_0/memory_init.c b/src/drivers/intel/fsp2_0/memory_init.c index 0aea1ad76d..039dafec8e 100644 --- a/src/drivers/intel/fsp2_0/memory_init.c +++ b/src/drivers/intel/fsp2_0/memory_init.c @@ -24,10 +24,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/src/drivers/mrc_cache/Kconfig b/src/drivers/mrc_cache/Kconfig new file mode 100644 index 0000000000..3e0bdda2be --- /dev/null +++ b/src/drivers/mrc_cache/Kconfig @@ -0,0 +1,31 @@ +config CACHE_MRC_SETTINGS + bool "Save cached MRC settings" + default n + +if CACHE_MRC_SETTINGS + +config MRC_SETTINGS_CACHE_BASE + hex + default 0xfffe0000 + +config MRC_SETTINGS_CACHE_SIZE + hex + default 0x10000 + +config MRC_SETTINGS_PROTECT + bool "Enable protection on MRC settings" + default n + +config HAS_RECOVERY_MRC_CACHE + bool + default n + +config MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN + bool + default n + +config MRC_SETTINGS_VARIABLE_DATA + bool + default n + +endif # CACHE_MRC_SETTINGS diff --git a/src/drivers/mrc_cache/Makefile.inc b/src/drivers/mrc_cache/Makefile.inc new file mode 100644 index 0000000000..819d637e4f --- /dev/null +++ b/src/drivers/mrc_cache/Makefile.inc @@ -0,0 +1,16 @@ + +romstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c +ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c + +# Create and add the MRC cache to the cbfs image +ifneq ($(CONFIG_CHROMEOS),y) +$(obj)/mrc.cache: $(obj)/config.h + dd if=/dev/zero count=1 \ + bs=$(shell printf "%d" $(CONFIG_MRC_SETTINGS_CACHE_SIZE) ) | \ + tr '\000' '\377' > $@ + +cbfs-files-$(CONFIG_CACHE_MRC_SETTINGS) += mrc.cache +mrc.cache-file := $(obj)/mrc.cache +mrc.cache-position := $(CONFIG_MRC_SETTINGS_CACHE_BASE) +mrc.cache-type := mrc_cache +endif diff --git a/src/drivers/mrc_cache/mrc_cache.c b/src/drivers/mrc_cache/mrc_cache.c new file mode 100644 index 0000000000..3a9689645f --- /dev/null +++ b/src/drivers/mrc_cache/mrc_cache.c @@ -0,0 +1,581 @@ +/* + * This file is part of the coreboot project. + * + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + 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, +#if IS_ENABLED(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, + .flags = NORMAL_FLAG | RECOVERY_FLAG, +}; + +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, + .flags = NORMAL_FLAG | RECOVERY_FLAG, +}; + +/* Order matters here for priority in matching. */ +static const struct cache_region *cache_regions[] = { + &recovery_training, + &normal_training, + &variable_data, +}; + +static int lookup_region_by_name(const char *name, struct region *r) +{ + /* This assumes memory mapped boot media just under 4GiB. */ + const uint32_t pointer_base_32bit = -CONFIG_ROM_SIZE; + + if (fmap_locate_area(name, r) == 0) + return 0; + + /* CHROMEOS builds must get their MRC cache from FMAP. */ + if (IS_ENABLED(CONFIG_CHROMEOS)) { + printk(BIOS_ERR, "MRC: Chrome OS lookup failure.\n"); + return -1; + } + + if (!IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)) + return -1; + + /* Base is in the form of a pointer. Make it an offset. */ + r->offset = CONFIG_MRC_SETTINGS_CACHE_BASE - pointer_base_32bit; + r->size = CONFIG_MRC_SETTINGS_CACHE_SIZE; + + return 0; +} + +static const struct cache_region *lookup_region_type(int type) +{ + int i; + int flags; + + if (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; +} + +int mrc_cache_stash_data(int type, uint32_t version, const void *data, + size_t size) +{ + const struct cache_region *cr; + size_t cbmem_size; + struct mrc_metadata *md; + + cr = lookup_region_type(type); + if (cr == NULL) { + printk(BIOS_ERR, "MRC: failed to add to cbmem for type %d.\n", + type); + return -1; + } + + cbmem_size = sizeof(*md) + size; + + md = cbmem_add(cr->cbmem_id, cbmem_size); + + if (md == NULL) { + printk(BIOS_ERR, "MRC: failed to add '%s' to cbmem.\n", + cr->name); + return -1; + } + + memset(md, 0, sizeof(*md)); + md->signature = MRC_DATA_SIGNATURE; + md->data_size = size; + md->version = version; + md->data_checksum = compute_ip_checksum(data, size); + md->header_checksum = compute_ip_checksum(md, sizeof(*md)); + memcpy(&md[1], data, size); + + return 0; +} + +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", + 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(const struct region_device *rdev, + const struct mrc_metadata *md) +{ + void *data; + uint16_t checksum; + const size_t md_size = sizeof(*md); + const size_t data_size = md->data_size; + + data = rdev_mmap(rdev, md_size, data_size); + if (data == NULL) { + printk(BIOS_ERR, "MRC: mmap failure on data verification.\n"); + return -1; + } + + checksum = compute_ip_checksum(data, data_size); + + rdev_munmap(rdev, data); + 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_latest(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; + } + + /* Validate Data */ + if (mrc_data_valid(rdev, md) < 0) { + printk(BIOS_ERR, "MRC: invalid data in '%s'\n", name); + return fail_bad_data ? -1 : 0; + } + + return 0; +} + +int mrc_cache_get_current(int type, uint32_t version, + struct region_device *rdev) +{ + const struct cache_region *cr; + struct region region; + struct region_device read_rdev; + struct region_file cache_file; + struct mrc_metadata md; + size_t data_size; + const size_t md_size = sizeof(md); + const bool fail_bad_data = true; + + cr = lookup_region(®ion, type); + + if (cr == NULL) + return -1; + + if (boot_device_ro_subregion(®ion, &read_rdev) < 0) + return -1; + + if (mrc_cache_latest(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); +} + +static bool mrc_cache_needs_update(const struct region_device *rdev, + const struct cbmem_entry *to_be_updated) +{ + void *mapping; + size_t size = region_device_sz(rdev); + bool need_update = false; + + if (cbmem_entry_size(to_be_updated) != size) + return true; + + mapping = rdev_mmap_full(rdev); + + if (memcmp(cbmem_entry_start(to_be_updated), mapping, 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. */ +static void update_mrc_cache_by_type(int type) +{ + 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; + const struct cbmem_entry *to_be_updated; + struct incoherent_rdev backing_irdev; + const struct region_device *backing_rdev; + struct region_device latest_rdev; + const bool fail_bad_data = false; + + cr = lookup_region(®ion, type); + + if (cr == NULL) + return; + + to_be_updated = cbmem_entry_find(cr->cbmem_id); + if (to_be_updated == NULL) { + printk(BIOS_ERR, "MRC: No data in cbmem for '%s'.\n", + cr->name); + return; + } + + printk(BIOS_DEBUG, "MRC: Checking cached data update for '%s'.\n", + cr->name); + + if (boot_device_ro_subregion(®ion, &read_rdev) < 0) + return; + + if (boot_device_rw_subregion(®ion, &write_rdev) < 0) + return; + + backing_rdev = incoherent_rdev_init(&backing_irdev, ®ion, &read_rdev, + &write_rdev); + + if (backing_rdev == NULL) + return; + + if (mrc_cache_latest(cr->name, backing_rdev, &md, &cache_file, + &latest_rdev, fail_bad_data) < 0) + return; + + if (!mrc_cache_needs_update(&latest_rdev, to_be_updated)) { + log_event_cache_update(cr->elog_slot, ALREADY_UPTODATE); + return; + } + + printk(BIOS_DEBUG, "MRC: cache data '%s' needs update.\n", cr->name); + + if (region_file_update_data(&cache_file, + cbmem_entry_start(to_be_updated), + cbmem_entry_size(to_be_updated)) < 0) + log_event_cache_update(cr->elog_slot, UPDATE_FAILURE); + else + log_event_cache_update(cr->elog_slot, UPDATE_SUCCESS); +} + +/* 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 (!IS_ENABLED(CONFIG_CHROMEOS)) + return 0; + + if (!IS_ENABLED(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 (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) + return 0; + + if (!IS_ENABLED(CONFIG_BOOT_DEVICE_SPI_FLASH)) + return 0; + + return spi_flash_ctrlr_protect_region(boot_device_spi_flash(), r); +} + +/* Protect mrc region with a Protected Range Register */ +static int protect_mrc_cache(const char *name) +{ + struct region region; + + if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) + return 0; + + if (lookup_region_by_name(name, ®ion) < 0) { + printk(BIOS_ERR, "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(®ion) < 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 (IS_ENABLED(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; + + /* Invalidate only on recovery mode with retraining enabled. */ + if (!vboot_recovery_mode_enabled()) + return; + if (!vboot_recovery_mode_memory_retrain()) + 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); +} + +static void update_mrc_cache(void *unused) +{ + update_mrc_cache_by_type(MRC_TRAINING_DATA); + + if (IS_ENABLED(CONFIG_MRC_SETTINGS_VARIABLE_DATA)) + update_mrc_cache_by_type(MRC_VARIABLE_DATA); + + if (IS_ENABLED(CONFIG_MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN)) + invalidate_normal_cache(); + + protect_mrc_region(); +} + +/* + * Ensures MRC training data is stored into SPI after PCI enumeration is done + * during BS_DEV_ENUMERATE-BS_ON_EXIT and lock down SPI protected ranges + * during BS_DEV_RESOURCES-BS_ON_EXIT. + */ +BOOT_STATE_INIT_ENTRY(BS_DEV_ENUMERATE, BS_ON_EXIT, update_mrc_cache, NULL); diff --git a/src/include/mrc_cache.h b/src/include/mrc_cache.h new file mode 100644 index 0000000000..4511fc3016 --- /dev/null +++ b/src/include/mrc_cache.h @@ -0,0 +1,43 @@ +/* + * This file is part of the coreboot project. + * + * 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 + * 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 _COMMON_MRC_CACHE_H_ +#define _COMMON_MRC_CACHE_H_ + +#include +#include + +enum { + MRC_TRAINING_DATA, + MRC_VARIABLE_DATA, +}; + +/* + * It's up to the caller to decide when to retrieve and stash data. There is + * differentiation on recovery mode CONFIG_HAS_RECOVERY_MRC_CACHE, but that's + * only for locating where to retrieve and save the data. If a platform doesn't + * want to update the data then it shouldn't stash the data for saving. + * Similarly, if the platform doesn't need the data for booting because of a + * policy don't request the data. + */ + +/* Get and stash data for saving provided the type passed in. The functions + * return < 0 on error, 0 on success. */ +int mrc_cache_get_current(int type, uint32_t version, + struct region_device *rdev); +int mrc_cache_stash_data(int type, uint32_t version, const void *data, + size_t size); + +#endif /* _COMMON_MRC_CACHE_H_ */ diff --git a/src/soc/intel/apollolake/romstage.c b/src/soc/intel/apollolake/romstage.c index a003ea0e0e..20b67fd471 100644 --- a/src/soc/intel/apollolake/romstage.c +++ b/src/soc/intel/apollolake/romstage.c @@ -36,9 +36,9 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/src/soc/intel/baytrail/romstage/raminit.c b/src/soc/intel/baytrail/romstage/raminit.c index b577a35ece..45bc75b9b3 100644 --- a/src/soc/intel/baytrail/romstage/raminit.c +++ b/src/soc/intel/baytrail/romstage/raminit.c @@ -21,8 +21,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/src/soc/intel/braswell/romstage/romstage.c b/src/soc/intel/braswell/romstage/romstage.c index 01258476d4..0f82c49999 100644 --- a/src/soc/intel/braswell/romstage/romstage.c +++ b/src/soc/intel/braswell/romstage/romstage.c @@ -28,13 +28,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/src/soc/intel/broadwell/romstage/raminit.c b/src/soc/intel/broadwell/romstage/raminit.c index 0470beec7e..665dad277e 100644 --- a/src/soc/intel/broadwell/romstage/raminit.c +++ b/src/soc/intel/broadwell/romstage/raminit.c @@ -21,13 +21,13 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_EC_GOOGLE_CHROMEEC) #include #include #endif #include -#include #include #include #include diff --git a/src/soc/intel/common/Kconfig b/src/soc/intel/common/Kconfig index 18a34b335c..95c09c78c4 100644 --- a/src/soc/intel/common/Kconfig +++ b/src/soc/intel/common/Kconfig @@ -8,38 +8,6 @@ if SOC_INTEL_COMMON comment "Intel SoC Common Code" source "src/soc/intel/common/block/Kconfig" -config CACHE_MRC_SETTINGS - bool "Save cached MRC settings" - default n - -if CACHE_MRC_SETTINGS - -config MRC_SETTINGS_CACHE_BASE - hex - default 0xfffe0000 - -config MRC_SETTINGS_CACHE_SIZE - hex - default 0x10000 - -config MRC_SETTINGS_PROTECT - bool "Enable protection on MRC settings" - default n - -config HAS_RECOVERY_MRC_CACHE - bool - default n - -config MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN - bool - default n - -config MRC_SETTINGS_VARIABLE_DATA - bool - default n - -endif # CACHE_MRC_SETTINGS - config DISPLAY_MTRRS bool "MTRRs: Display the MTRR settings" default n diff --git a/src/soc/intel/common/Makefile.inc b/src/soc/intel/common/Makefile.inc index c913cfd195..e56ac7daaf 100644 --- a/src/soc/intel/common/Makefile.inc +++ b/src/soc/intel/common/Makefile.inc @@ -9,7 +9,6 @@ verstage-$(CONFIG_SOC_INTEL_COMMON_RESET) += reset.c bootblock-$(CONFIG_SOC_INTEL_COMMON_RESET) += reset.c -romstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c romstage-$(CONFIG_SOC_INTEL_COMMON_RESET) += reset.c romstage-y += util.c romstage-$(CONFIG_MMA) += mma.c @@ -19,7 +18,6 @@ postcar-y += util.c postcar-$(CONFIG_SOC_INTEL_COMMON_RESET) += reset.c ramstage-y += hda_verb.c -ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c ramstage-$(CONFIG_SOC_INTEL_COMMON_RESET) += reset.c ramstage-y += util.c ramstage-$(CONFIG_MMA) += mma.c @@ -33,19 +31,6 @@ verstage-$(CONFIG_MAINBOARD_HAS_TPM_CR50) += tpm_tis.c romstage-$(CONFIG_MAINBOARD_HAS_TPM_CR50) += tpm_tis.c ramstage-$(CONFIG_MAINBOARD_HAS_TPM_CR50) += tpm_tis.c -# Create and add the MRC cache to the cbfs image -ifneq ($(CONFIG_CHROMEOS),y) -$(obj)/mrc.cache: $(obj)/config.h - dd if=/dev/zero count=1 \ - bs=$(shell printf "%d" $(CONFIG_MRC_SETTINGS_CACHE_SIZE) ) | \ - tr '\000' '\377' > $@ - -cbfs-files-$(CONFIG_CACHE_MRC_SETTINGS) += mrc.cache -mrc.cache-file := $(obj)/mrc.cache -mrc.cache-position := $(CONFIG_MRC_SETTINGS_CACHE_BASE) -mrc.cache-type := mrc_cache -endif - ifeq ($(CONFIG_MMA),y) MMA_BLOBS_PATH = $(call strip_quotes,$(CONFIG_MMA_BLOBS_PATH)) MMA_TEST_NAMES = $(notdir $(wildcard $(MMA_BLOBS_PATH)/tests/*)) diff --git a/src/soc/intel/common/mrc_cache.c b/src/soc/intel/common/mrc_cache.c deleted file mode 100644 index 3a9689645f..0000000000 --- a/src/soc/intel/common/mrc_cache.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * 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 - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - 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, -#if IS_ENABLED(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, - .flags = NORMAL_FLAG | RECOVERY_FLAG, -}; - -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, - .flags = NORMAL_FLAG | RECOVERY_FLAG, -}; - -/* Order matters here for priority in matching. */ -static const struct cache_region *cache_regions[] = { - &recovery_training, - &normal_training, - &variable_data, -}; - -static int lookup_region_by_name(const char *name, struct region *r) -{ - /* This assumes memory mapped boot media just under 4GiB. */ - const uint32_t pointer_base_32bit = -CONFIG_ROM_SIZE; - - if (fmap_locate_area(name, r) == 0) - return 0; - - /* CHROMEOS builds must get their MRC cache from FMAP. */ - if (IS_ENABLED(CONFIG_CHROMEOS)) { - printk(BIOS_ERR, "MRC: Chrome OS lookup failure.\n"); - return -1; - } - - if (!IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)) - return -1; - - /* Base is in the form of a pointer. Make it an offset. */ - r->offset = CONFIG_MRC_SETTINGS_CACHE_BASE - pointer_base_32bit; - r->size = CONFIG_MRC_SETTINGS_CACHE_SIZE; - - return 0; -} - -static const struct cache_region *lookup_region_type(int type) -{ - int i; - int flags; - - if (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; -} - -int mrc_cache_stash_data(int type, uint32_t version, const void *data, - size_t size) -{ - const struct cache_region *cr; - size_t cbmem_size; - struct mrc_metadata *md; - - cr = lookup_region_type(type); - if (cr == NULL) { - printk(BIOS_ERR, "MRC: failed to add to cbmem for type %d.\n", - type); - return -1; - } - - cbmem_size = sizeof(*md) + size; - - md = cbmem_add(cr->cbmem_id, cbmem_size); - - if (md == NULL) { - printk(BIOS_ERR, "MRC: failed to add '%s' to cbmem.\n", - cr->name); - return -1; - } - - memset(md, 0, sizeof(*md)); - md->signature = MRC_DATA_SIGNATURE; - md->data_size = size; - md->version = version; - md->data_checksum = compute_ip_checksum(data, size); - md->header_checksum = compute_ip_checksum(md, sizeof(*md)); - memcpy(&md[1], data, size); - - return 0; -} - -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", - 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(const struct region_device *rdev, - const struct mrc_metadata *md) -{ - void *data; - uint16_t checksum; - const size_t md_size = sizeof(*md); - const size_t data_size = md->data_size; - - data = rdev_mmap(rdev, md_size, data_size); - if (data == NULL) { - printk(BIOS_ERR, "MRC: mmap failure on data verification.\n"); - return -1; - } - - checksum = compute_ip_checksum(data, data_size); - - rdev_munmap(rdev, data); - 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_latest(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; - } - - /* Validate Data */ - if (mrc_data_valid(rdev, md) < 0) { - printk(BIOS_ERR, "MRC: invalid data in '%s'\n", name); - return fail_bad_data ? -1 : 0; - } - - return 0; -} - -int mrc_cache_get_current(int type, uint32_t version, - struct region_device *rdev) -{ - const struct cache_region *cr; - struct region region; - struct region_device read_rdev; - struct region_file cache_file; - struct mrc_metadata md; - size_t data_size; - const size_t md_size = sizeof(md); - const bool fail_bad_data = true; - - cr = lookup_region(®ion, type); - - if (cr == NULL) - return -1; - - if (boot_device_ro_subregion(®ion, &read_rdev) < 0) - return -1; - - if (mrc_cache_latest(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); -} - -static bool mrc_cache_needs_update(const struct region_device *rdev, - const struct cbmem_entry *to_be_updated) -{ - void *mapping; - size_t size = region_device_sz(rdev); - bool need_update = false; - - if (cbmem_entry_size(to_be_updated) != size) - return true; - - mapping = rdev_mmap_full(rdev); - - if (memcmp(cbmem_entry_start(to_be_updated), mapping, 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. */ -static void update_mrc_cache_by_type(int type) -{ - 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; - const struct cbmem_entry *to_be_updated; - struct incoherent_rdev backing_irdev; - const struct region_device *backing_rdev; - struct region_device latest_rdev; - const bool fail_bad_data = false; - - cr = lookup_region(®ion, type); - - if (cr == NULL) - return; - - to_be_updated = cbmem_entry_find(cr->cbmem_id); - if (to_be_updated == NULL) { - printk(BIOS_ERR, "MRC: No data in cbmem for '%s'.\n", - cr->name); - return; - } - - printk(BIOS_DEBUG, "MRC: Checking cached data update for '%s'.\n", - cr->name); - - if (boot_device_ro_subregion(®ion, &read_rdev) < 0) - return; - - if (boot_device_rw_subregion(®ion, &write_rdev) < 0) - return; - - backing_rdev = incoherent_rdev_init(&backing_irdev, ®ion, &read_rdev, - &write_rdev); - - if (backing_rdev == NULL) - return; - - if (mrc_cache_latest(cr->name, backing_rdev, &md, &cache_file, - &latest_rdev, fail_bad_data) < 0) - return; - - if (!mrc_cache_needs_update(&latest_rdev, to_be_updated)) { - log_event_cache_update(cr->elog_slot, ALREADY_UPTODATE); - return; - } - - printk(BIOS_DEBUG, "MRC: cache data '%s' needs update.\n", cr->name); - - if (region_file_update_data(&cache_file, - cbmem_entry_start(to_be_updated), - cbmem_entry_size(to_be_updated)) < 0) - log_event_cache_update(cr->elog_slot, UPDATE_FAILURE); - else - log_event_cache_update(cr->elog_slot, UPDATE_SUCCESS); -} - -/* 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 (!IS_ENABLED(CONFIG_CHROMEOS)) - return 0; - - if (!IS_ENABLED(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 (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) - return 0; - - if (!IS_ENABLED(CONFIG_BOOT_DEVICE_SPI_FLASH)) - return 0; - - return spi_flash_ctrlr_protect_region(boot_device_spi_flash(), r); -} - -/* Protect mrc region with a Protected Range Register */ -static int protect_mrc_cache(const char *name) -{ - struct region region; - - if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) - return 0; - - if (lookup_region_by_name(name, ®ion) < 0) { - printk(BIOS_ERR, "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(®ion) < 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 (IS_ENABLED(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; - - /* Invalidate only on recovery mode with retraining enabled. */ - if (!vboot_recovery_mode_enabled()) - return; - if (!vboot_recovery_mode_memory_retrain()) - 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); -} - -static void update_mrc_cache(void *unused) -{ - update_mrc_cache_by_type(MRC_TRAINING_DATA); - - if (IS_ENABLED(CONFIG_MRC_SETTINGS_VARIABLE_DATA)) - update_mrc_cache_by_type(MRC_VARIABLE_DATA); - - if (IS_ENABLED(CONFIG_MRC_CLEAR_NORMAL_CACHE_ON_RECOVERY_RETRAIN)) - invalidate_normal_cache(); - - protect_mrc_region(); -} - -/* - * Ensures MRC training data is stored into SPI after PCI enumeration is done - * during BS_DEV_ENUMERATE-BS_ON_EXIT and lock down SPI protected ranges - * during BS_DEV_RESOURCES-BS_ON_EXIT. - */ -BOOT_STATE_INIT_ENTRY(BS_DEV_ENUMERATE, BS_ON_EXIT, update_mrc_cache, NULL); diff --git a/src/soc/intel/common/mrc_cache.h b/src/soc/intel/common/mrc_cache.h deleted file mode 100644 index 4511fc3016..0000000000 --- a/src/soc/intel/common/mrc_cache.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * 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 - * 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 _COMMON_MRC_CACHE_H_ -#define _COMMON_MRC_CACHE_H_ - -#include -#include - -enum { - MRC_TRAINING_DATA, - MRC_VARIABLE_DATA, -}; - -/* - * It's up to the caller to decide when to retrieve and stash data. There is - * differentiation on recovery mode CONFIG_HAS_RECOVERY_MRC_CACHE, but that's - * only for locating where to retrieve and save the data. If a platform doesn't - * want to update the data then it shouldn't stash the data for saving. - * Similarly, if the platform doesn't need the data for booting because of a - * policy don't request the data. - */ - -/* Get and stash data for saving provided the type passed in. The functions - * return < 0 on error, 0 on success. */ -int mrc_cache_get_current(int type, uint32_t version, - struct region_device *rdev); -int mrc_cache_stash_data(int type, uint32_t version, const void *data, - size_t size); - -#endif /* _COMMON_MRC_CACHE_H_ */ -- cgit v1.2.3