From d8c4f2b72462f60ae92a59a976437c2407ec6654 Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Tue, 22 Apr 2014 10:46:06 -0700 Subject: baytrail: Move MRC cache code to a common directory This common code can be shared across Intel SOCs. Change-Id: Id9ec4ccd3fc81cbab19a7d7e13bfa3975d9802d0 Signed-off-by: Duncan Laurie Reviewed-on: https://chromium-review.googlesource.com/196263 Reviewed-by: Aaron Durbin (cherry picked from commit f9919e2551b02056b83918d2e7b515b25541c583) Signed-off-by: Isaac Christensen Reviewed-on: http://review.coreboot.org/6967 Tested-by: build bot (Jenkins) Reviewed-by: Marc Jones --- src/soc/intel/Kconfig | 1 + src/soc/intel/baytrail/Kconfig | 16 -- src/soc/intel/baytrail/Makefile.inc | 4 +- src/soc/intel/baytrail/baytrail/mrc_cache.h | 40 ---- src/soc/intel/baytrail/baytrail/nvm.h | 34 --- src/soc/intel/baytrail/mrc_cache.c | 300 --------------------------- src/soc/intel/baytrail/nvm.c | 82 -------- src/soc/intel/baytrail/romstage/raminit.c | 2 +- src/soc/intel/common/Kconfig | 19 ++ src/soc/intel/common/Makefile.inc | 3 + src/soc/intel/common/mrc_cache.c | 311 ++++++++++++++++++++++++++++ src/soc/intel/common/mrc_cache.h | 41 ++++ src/soc/intel/common/nvm.c | 82 ++++++++ src/soc/intel/common/nvm.h | 34 +++ 14 files changed, 493 insertions(+), 476 deletions(-) delete mode 100644 src/soc/intel/baytrail/baytrail/mrc_cache.h delete mode 100644 src/soc/intel/baytrail/baytrail/nvm.h delete mode 100644 src/soc/intel/baytrail/mrc_cache.c delete mode 100644 src/soc/intel/baytrail/nvm.c create mode 100644 src/soc/intel/common/Kconfig create mode 100644 src/soc/intel/common/Makefile.inc create mode 100644 src/soc/intel/common/mrc_cache.c create mode 100644 src/soc/intel/common/mrc_cache.h create mode 100644 src/soc/intel/common/nvm.c create mode 100644 src/soc/intel/common/nvm.h (limited to 'src/soc/intel') diff --git a/src/soc/intel/Kconfig b/src/soc/intel/Kconfig index 3ddbf2841e..d8a5011060 100644 --- a/src/soc/intel/Kconfig +++ b/src/soc/intel/Kconfig @@ -1,2 +1,3 @@ source src/soc/intel/baytrail/Kconfig source src/soc/intel/fsp_baytrail/Kconfig +source src/soc/intel/common/Kconfig diff --git a/src/soc/intel/baytrail/Kconfig b/src/soc/intel/baytrail/Kconfig index aa55444e02..3915f04d26 100644 --- a/src/soc/intel/baytrail/Kconfig +++ b/src/soc/intel/baytrail/Kconfig @@ -88,22 +88,6 @@ config MRC_RMT bool "Enable MRC RMT training + debug prints" default n -config CACHE_MRC_SETTINGS - bool "Save cached MRC settings" - default n - -if CACHE_MRC_SETTINGS - -config MRC_SETTINGS_CACHE_BASE - hex - default 0xffb00000 - -config MRC_SETTINGS_CACHE_SIZE - hex - default 0x10000 - -endif # CACHE_MRC_SETTINGS - endif # HAVE_MRC # Cache As RAM region layout: diff --git a/src/soc/intel/baytrail/Makefile.inc b/src/soc/intel/baytrail/Makefile.inc index ce1f2433bb..f5c4c9f6e7 100644 --- a/src/soc/intel/baytrail/Makefile.inc +++ b/src/soc/intel/baytrail/Makefile.inc @@ -1,6 +1,7 @@ subdirs-y += bootblock subdirs-y += microcode subdirs-y += romstage +subdirs-y += ../common subdirs-y += ../../../cpu/x86/lapic subdirs-y += ../../../cpu/x86/mtrr subdirs-y += ../../../cpu/x86/smm @@ -13,9 +14,6 @@ romstage-y += memmap.c ramstage-y += tsc_freq.c romstage-y += tsc_freq.c smm-y += tsc_freq.c -ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += nvm.c -ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c -romstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c ramstage-y += spi.c smm-y += spi.c ramstage-y += chip.c diff --git a/src/soc/intel/baytrail/baytrail/mrc_cache.h b/src/soc/intel/baytrail/baytrail/mrc_cache.h deleted file mode 100644 index 6791ec4a82..0000000000 --- a/src/soc/intel/baytrail/baytrail/mrc_cache.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2013 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _MRC_CACHE_H_ -#define _MRC_CACHE_H_ - -#include -#include - -/* Wrapper object to save MRC information. */ -struct mrc_saved_data { - uint32_t signature; - uint32_t size; - uint32_t checksum; - uint32_t reserved; - uint8_t data[0]; -} __attribute__((packed)); - -/* Locate the most recently saved MRC data. */ -int mrc_cache_get_current(const struct mrc_saved_data **cache); - -/* Stash the resulting MRC data to be saved in non-volatile storage later. */ -int mrc_cache_stash_data(void *data, size_t size); -#endif /* _MRC_CACHE_H_ */ diff --git a/src/soc/intel/baytrail/baytrail/nvm.h b/src/soc/intel/baytrail/baytrail/nvm.h deleted file mode 100644 index d0cbf7b0e6..0000000000 --- a/src/soc/intel/baytrail/baytrail/nvm.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2013 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _NVM_H_ -#define _NVM_H_ - -#include - -/* Determine if area is erased. returns 1 if erased. 0 otherwise. */ -int nvm_is_erased(const void *start, size_t size); - -/* Erase region according to start and size. Returns < 0 on error else 0. */ -int nvm_erase(void *start, size_t size); - -/* Write data to NVM. Returns 0 on success < 0 on error. */ -int nvm_write(void *start, const void *data, size_t size); - -#endif /* _NVM_H_ */ diff --git a/src/soc/intel/baytrail/mrc_cache.c b/src/soc/intel/baytrail/mrc_cache.c deleted file mode 100644 index f12f3ef0ea..0000000000 --- a/src/soc/intel/baytrail/mrc_cache.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2013 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#if CONFIG_CHROMEOS -#include -#endif -#include - -#define MRC_DATA_ALIGN 0x1000 -#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24)) - -/* The mrc_data_region describes the larger non-volatile area to store - * mrc_saved_data objects.*/ -struct mrc_data_region { - void *base; - uint32_t size; -}; - -/* common code */ -static int mrc_cache_get_region(struct mrc_data_region *region) -{ -#if CONFIG_CHROMEOS - int ret; - ret = find_fmap_entry("RW_MRC_CACHE", ®ion->base); - if (ret >= 0) { - region->size = ret; - return 0; - } -#endif - region->base = (void *)CONFIG_MRC_SETTINGS_CACHE_BASE; - region->size = CONFIG_MRC_SETTINGS_CACHE_SIZE; - return 0; -} - -static int mrc_cache_in_region(const struct mrc_data_region *region, - const struct mrc_saved_data *cache) -{ - uintptr_t region_end; - uintptr_t cache_end; - - if ((uintptr_t)cache < (uintptr_t)region->base) - return 0; - - region_end = (uintptr_t)region->base; - region_end += region->size; - - if ((uintptr_t)cache >= region_end) - return 0; - - if ((sizeof(*cache) + (uintptr_t)cache) >= region_end) - return 0; - - cache_end = (uintptr_t)cache; - cache_end += cache->size + sizeof(*cache); - - if (cache_end > region_end) - return 0; - - return 1; -} - -static int mrc_cache_valid(const struct mrc_data_region *region, - const struct mrc_saved_data *cache) -{ - uint32_t checksum; - - if (cache->signature != MRC_DATA_SIGNATURE) - return 0; - - if (cache->size > region->size) - return 0; - - if (cache->reserved != 0) - return 0; - - checksum = compute_ip_checksum((void *)&cache->data[0], cache->size); - - if (cache->checksum != checksum) - return 0; - - return 1; -} - -static const struct mrc_saved_data * -next_cache_block(const struct mrc_saved_data *cache) -{ - uintptr_t next = (uintptr_t)cache; - - next += ALIGN(cache->size + sizeof(*cache), MRC_DATA_ALIGN); - - return (const struct mrc_saved_data *)next; -} - -/* Locate the most recently saved MRC data. */ -static int __mrc_cache_get_current(const struct mrc_data_region *region, - const struct mrc_saved_data **cache) -{ - const struct mrc_saved_data *msd; - const struct mrc_saved_data *verified_cache; - - msd = region->base; - - verified_cache = NULL; - - while (mrc_cache_in_region(region, msd) && - mrc_cache_valid(region, msd)) { - verified_cache = msd; - msd = next_cache_block(msd); - } - - if (verified_cache == NULL) - return -1; - - *cache = verified_cache; - return 0; -} - -int mrc_cache_get_current(const struct mrc_saved_data **cache) -{ - struct mrc_data_region region; - - if (mrc_cache_get_region(®ion) < 0) - return -1; - - return __mrc_cache_get_current(®ion, cache); -} - -#if defined(__PRE_RAM__) -/* romstage code */ - -/* Fill in mrc_saved_data structure with payload. */ -static void mrc_cache_fill(struct mrc_saved_data *cache, void *data, - size_t size) -{ - cache->signature = MRC_DATA_SIGNATURE; - cache->size = size; - cache->reserved = 0; - memcpy(&cache->data[0], data, size); - cache->checksum = compute_ip_checksum((void *)&cache->data[0], - cache->size); -} - -int mrc_cache_stash_data(void *data, size_t size) -{ - int cbmem_size; - struct mrc_saved_data *cache; - - cbmem_size = sizeof(*cache) + ALIGN(size, 16); - - cache = cbmem_add(CBMEM_ID_MRCDATA, cbmem_size); - - if (cache == NULL) { - printk(BIOS_ERR, "No space in cbmem for MRC data.\n"); - return -1; - } - - /* Clear alignment padding bytes at end of data. */ - memset(&cache->data[size], 0, cbmem_size - size - sizeof(*cache)); - - printk(BIOS_DEBUG, "Relocate MRC DATA from %p to %p (%zu bytes)\n", - data, cache, size); - - mrc_cache_fill(cache, data, size); - - return 0; -} - -#else -/* ramstage code */ -#include -#include - -static int mrc_slot_valid(const struct mrc_data_region *region, - const struct mrc_saved_data *slot, - const struct mrc_saved_data *to_save) -{ - uintptr_t region_begin; - uintptr_t region_end; - uintptr_t slot_end; - uintptr_t slot_begin; - uint32_t size; - - region_begin = (uintptr_t)region->base; - region_end = region_begin + region->size; - slot_begin = (uintptr_t)slot; - size = to_save->size + sizeof(*to_save); - slot_end = slot_begin + size; - - if (slot_begin < region_begin || slot_begin >= region_end) - return 0; - - if (size > region->size) - return 0; - - if (slot_end > region_end || slot_end < region_begin) - return 0; - - if (!nvm_is_erased(slot, size)) - return 0; - - return 1; -} - -static const struct mrc_saved_data * -mrc_cache_next_slot(const struct mrc_data_region *region, - const struct mrc_saved_data *current_slot) -{ - const struct mrc_saved_data *next_slot; - - if (current_slot == NULL) { - next_slot = region->base; - } else { - next_slot = next_cache_block(current_slot); - } - - return next_slot; -} - -static void update_mrc_cache(void *unused) -{ - const struct mrc_saved_data *current_boot; - const struct mrc_saved_data *current_saved; - const struct mrc_saved_data *next_slot; - struct mrc_data_region region; - - printk(BIOS_DEBUG, "Updating MRC cache data.\n"); - - current_boot = cbmem_find(CBMEM_ID_MRCDATA); - if (!current_boot) { - printk(BIOS_ERR, "No MRC cache in cbmem.\n"); - return; - } - - if (mrc_cache_get_region(®ion)) { - printk(BIOS_ERR, "Could not obtain MRC cache region.\n"); - return; - } - - if (!mrc_cache_valid(®ion, current_boot)) { - printk(BIOS_ERR, "MRC cache data in cbmem invalid.\n"); - return; - } - - current_saved = NULL; - - if (!__mrc_cache_get_current(®ion, ¤t_saved)) { - if (current_saved->size == current_boot->size && - !memcmp(¤t_saved->data[0], ¤t_boot->data[0], - current_saved->size)) { - printk(BIOS_DEBUG, "MRC cache up to date.\n"); - return; - } - } - - next_slot = mrc_cache_next_slot(®ion, current_saved); - - if (!mrc_slot_valid(®ion, next_slot, current_boot)) { - printk(BIOS_DEBUG, "Slot @ %p is invalid.\n", next_slot); - if (!nvm_is_erased(region.base, region.size)) { - if (nvm_erase(region.base, region.size) < 0) { - printk(BIOS_DEBUG, "Failure erasing region.\n"); - return; - } - } - next_slot = region.base; - } - - if (nvm_write((void *)next_slot, current_boot, - current_boot->size + sizeof(*current_boot))) { - printk(BIOS_DEBUG, "Failure writing MRC cache to %p.\n", - next_slot); - } -} - -BOOT_STATE_INIT_ENTRIES(mrc_cache_update) = { - BOOT_STATE_INIT_ENTRY(BS_WRITE_TABLES, BS_ON_ENTRY, - update_mrc_cache, NULL), -}; - -#endif /* defined(__PRE_RAM__) */ diff --git a/src/soc/intel/baytrail/nvm.c b/src/soc/intel/baytrail/nvm.c deleted file mode 100644 index 0cd42ccd52..0000000000 --- a/src/soc/intel/baytrail/nvm.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2013 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -/* This module assumes the flash is memory mapped just below 4GiB in the - * address space for reading. Also this module assumes an area it erased - * when all bytes read as all 0xff's. */ - -static struct spi_flash *flash; - -static int nvm_init(void) -{ - if (flash != NULL) - return 0; - - spi_init(); - flash = spi_flash_probe(0, 0); - if (!flash) { - printk(BIOS_DEBUG, "Could not find SPI device\n"); - return -1; - } - - return 0; -} - -/* Convert memory mapped pointer to flash offset. */ -static inline uint32_t to_flash_offset(void *p) -{ - return CONFIG_ROM_SIZE + (uintptr_t)p; -} - -int nvm_is_erased(const void *start, size_t size) -{ - const uint8_t *cur = start; - const uint8_t erased_value = 0xff; - - while (size > 0) { - if (*cur != erased_value) - return 0; - cur++; - size--; - } - return 1; -} - -int nvm_erase(void *start, size_t size) -{ - if (nvm_init() < 0) - return -1; - return flash->erase(flash, to_flash_offset(start), size); -} - -/* Write data to NVM. Returns 0 on success < 0 on error. */ -int nvm_write(void *start, const void *data, size_t size) -{ - if (nvm_init() < 0) - return -1; - return flash->write(flash, to_flash_offset(start), size, data); -} diff --git a/src/soc/intel/baytrail/romstage/raminit.c b/src/soc/intel/baytrail/romstage/raminit.c index a18f312386..d5ee0317f0 100644 --- a/src/soc/intel/baytrail/romstage/raminit.c +++ b/src/soc/intel/baytrail/romstage/raminit.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/soc/intel/common/Kconfig b/src/soc/intel/common/Kconfig new file mode 100644 index 0000000000..8b02a4a905 --- /dev/null +++ b/src/soc/intel/common/Kconfig @@ -0,0 +1,19 @@ +if HAVE_MRC + +config CACHE_MRC_SETTINGS + bool "Save cached MRC settings" + default n + +if CACHE_MRC_SETTINGS + +config MRC_SETTINGS_CACHE_BASE + hex + default 0xffb00000 + +config MRC_SETTINGS_CACHE_SIZE + hex + default 0x10000 + +endif # CACHE_MRC_SETTINGS + +endif # HAVE_MRC diff --git a/src/soc/intel/common/Makefile.inc b/src/soc/intel/common/Makefile.inc new file mode 100644 index 0000000000..b32255ae42 --- /dev/null +++ b/src/soc/intel/common/Makefile.inc @@ -0,0 +1,3 @@ +ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += nvm.c +ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c +romstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c diff --git a/src/soc/intel/common/mrc_cache.c b/src/soc/intel/common/mrc_cache.c new file mode 100644 index 0000000000..8f0d18f8b3 --- /dev/null +++ b/src/soc/intel/common/mrc_cache.c @@ -0,0 +1,311 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#if CONFIG_CHROMEOS +#include +#endif +#include "mrc_cache.h" + +#define MRC_DATA_ALIGN 0x1000 +#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24)) + +/* The mrc_data_region describes the larger non-volatile area to store + * mrc_saved_data objects.*/ +struct mrc_data_region { + void *base; + uint32_t size; +}; + +/* common code */ +static int mrc_cache_get_region(struct mrc_data_region *region) +{ +#if CONFIG_CHROMEOS + int ret; + ret = find_fmap_entry("RW_MRC_CACHE", ®ion->base); + if (ret >= 0) { + region->size = ret; + return 0; + } +#endif + region->base = (void *)CONFIG_MRC_SETTINGS_CACHE_BASE; + region->size = CONFIG_MRC_SETTINGS_CACHE_SIZE; + return 0; +} + +static int mrc_cache_in_region(const struct mrc_data_region *region, + const struct mrc_saved_data *cache) +{ + uintptr_t region_end; + uintptr_t cache_end; + + if ((uintptr_t)cache < (uintptr_t)region->base) + return 0; + + region_end = (uintptr_t)region->base; + region_end += region->size; + + if ((uintptr_t)cache >= region_end) + return 0; + + if ((sizeof(*cache) + (uintptr_t)cache) >= region_end) + return 0; + + cache_end = (uintptr_t)cache; + cache_end += cache->size + sizeof(*cache); + + if (cache_end > region_end) + return 0; + + return 1; +} + +static int mrc_cache_valid(const struct mrc_data_region *region, + const struct mrc_saved_data *cache) +{ + uint32_t checksum; + + if (cache->signature != MRC_DATA_SIGNATURE) + return 0; + + if (cache->size > region->size) + return 0; + + if (cache->reserved != 0) + return 0; + + checksum = compute_ip_checksum((void *)&cache->data[0], cache->size); + + if (cache->checksum != checksum) + return 0; + + return 1; +} + +static const struct mrc_saved_data * +next_cache_block(const struct mrc_saved_data *cache) +{ + uintptr_t next = (uintptr_t)cache; + + next += ALIGN(cache->size + sizeof(*cache), MRC_DATA_ALIGN); + + return (const struct mrc_saved_data *)next; +} + +/* Locate the most recently saved MRC data. */ +static int __mrc_cache_get_current(const struct mrc_data_region *region, + const struct mrc_saved_data **cache) +{ + const struct mrc_saved_data *msd; + const struct mrc_saved_data *verified_cache; + int slot = 0; + + msd = region->base; + + verified_cache = NULL; + + while (mrc_cache_in_region(region, msd) && + mrc_cache_valid(region, msd)) { + verified_cache = msd; + msd = next_cache_block(msd); + slot++; + } + + if (verified_cache == NULL) + return -1; + + *cache = verified_cache; + printk(BIOS_DEBUG, "MRC cache slot %d @ %p\n", slot-1, verified_cache); + + return 0; +} + +int mrc_cache_get_current(const struct mrc_saved_data **cache) +{ + struct mrc_data_region region; + + if (mrc_cache_get_region(®ion) < 0) + return -1; + + return __mrc_cache_get_current(®ion, cache); +} + +#if defined(__PRE_RAM__) + +/* + * romstage code + */ + +/* Fill in mrc_saved_data structure with payload. */ +static void mrc_cache_fill(struct mrc_saved_data *cache, void *data, + size_t size) +{ + cache->signature = MRC_DATA_SIGNATURE; + cache->size = size; + cache->reserved = 0; + memcpy(&cache->data[0], data, size); + cache->checksum = compute_ip_checksum((void *)&cache->data[0], + cache->size); +} + +int mrc_cache_stash_data(void *data, size_t size) +{ + int cbmem_size; + struct mrc_saved_data *cache; + + cbmem_size = sizeof(*cache) + ALIGN(size, 16); + + cache = cbmem_add(CBMEM_ID_MRCDATA, cbmem_size); + + if (cache == NULL) { + printk(BIOS_ERR, "No space in cbmem for MRC data.\n"); + return -1; + } + + /* Clear alignment padding bytes at end of data. */ + memset(&cache->data[size], 0, cbmem_size - size - sizeof(*cache)); + + printk(BIOS_DEBUG, "Relocate MRC DATA from %p to %p (%zu bytes)\n", + data, cache, size); + + mrc_cache_fill(cache, data, size); + + return 0; +} + +#else + +/* + * ramstage code + */ + +#include +#include "nvm.h" + +static int mrc_slot_valid(const struct mrc_data_region *region, + const struct mrc_saved_data *slot, + const struct mrc_saved_data *to_save) +{ + uintptr_t region_begin; + uintptr_t region_end; + uintptr_t slot_end; + uintptr_t slot_begin; + uint32_t size; + + region_begin = (uintptr_t)region->base; + region_end = region_begin + region->size; + slot_begin = (uintptr_t)slot; + size = to_save->size + sizeof(*to_save); + slot_end = slot_begin + size; + + if (slot_begin < region_begin || slot_begin >= region_end) + return 0; + + if (size > region->size) + return 0; + + if (slot_end > region_end || slot_end < region_begin) + return 0; + + if (!nvm_is_erased(slot, size)) + return 0; + + return 1; +} + +static const struct mrc_saved_data * +mrc_cache_next_slot(const struct mrc_data_region *region, + const struct mrc_saved_data *current_slot) +{ + const struct mrc_saved_data *next_slot; + + if (current_slot == NULL) { + next_slot = region->base; + } else { + next_slot = next_cache_block(current_slot); + } + + return next_slot; +} + +static void update_mrc_cache(void *unused) +{ + const struct mrc_saved_data *current_boot; + const struct mrc_saved_data *current_saved; + const struct mrc_saved_data *next_slot; + struct mrc_data_region region; + + printk(BIOS_DEBUG, "Updating MRC cache data.\n"); + + current_boot = cbmem_find(CBMEM_ID_MRCDATA); + if (!current_boot) { + printk(BIOS_ERR, "No MRC cache in cbmem.\n"); + return; + } + + if (mrc_cache_get_region(®ion)) { + printk(BIOS_ERR, "Could not obtain MRC cache region.\n"); + return; + } + + if (!mrc_cache_valid(®ion, current_boot)) { + printk(BIOS_ERR, "MRC cache data in cbmem invalid.\n"); + return; + } + + current_saved = NULL; + + if (!__mrc_cache_get_current(®ion, ¤t_saved)) { + if (current_saved->size == current_boot->size && + !memcmp(¤t_saved->data[0], ¤t_boot->data[0], + current_saved->size)) { + printk(BIOS_DEBUG, "MRC cache up to date.\n"); + return; + } + } + + next_slot = mrc_cache_next_slot(®ion, current_saved); + + if (!mrc_slot_valid(®ion, next_slot, current_boot)) { + printk(BIOS_DEBUG, "Slot @ %p is invalid.\n", next_slot); + if (!nvm_is_erased(region.base, region.size)) { + if (nvm_erase(region.base, region.size) < 0) { + printk(BIOS_DEBUG, "Failure erasing region.\n"); + return; + } + } + next_slot = region.base; + } + + if (nvm_write((void *)next_slot, current_boot, + current_boot->size + sizeof(*current_boot))) { + printk(BIOS_DEBUG, "Failure writing MRC cache to %p.\n", + next_slot); + } +} + +BOOT_STATE_INIT_ENTRIES(mrc_cache_update) = { + BOOT_STATE_INIT_ENTRY(BS_WRITE_TABLES, BS_ON_ENTRY, + update_mrc_cache, NULL), +}; + +#endif /* defined(__PRE_RAM__) */ diff --git a/src/soc/intel/common/mrc_cache.h b/src/soc/intel/common/mrc_cache.h new file mode 100644 index 0000000000..14767e52b3 --- /dev/null +++ b/src/soc/intel/common/mrc_cache.h @@ -0,0 +1,41 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _COMMON_MRC_CACHE_H_ +#define _COMMON_MRC_CACHE_H_ + +#include +#include + +/* Wrapper object to save MRC information. */ +struct mrc_saved_data { + uint32_t signature; + uint32_t size; + uint32_t checksum; + uint32_t reserved; + uint8_t data[0]; +} __attribute__((packed)); + +/* Locate the most recently saved MRC data. */ +int mrc_cache_get_current(const struct mrc_saved_data **cache); + +/* Stash the resulting MRC data to be saved in non-volatile storage later. */ +int mrc_cache_stash_data(void *data, size_t size); + +#endif /* _COMMON_MRC_CACHE_H_ */ diff --git a/src/soc/intel/common/nvm.c b/src/soc/intel/common/nvm.c new file mode 100644 index 0000000000..791422fe30 --- /dev/null +++ b/src/soc/intel/common/nvm.c @@ -0,0 +1,82 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "nvm.h" + +/* This module assumes the flash is memory mapped just below 4GiB in the + * address space for reading. Also this module assumes an area it erased + * when all bytes read as all 0xff's. */ + +static struct spi_flash *flash; + +static int nvm_init(void) +{ + if (flash != NULL) + return 0; + + spi_init(); + flash = spi_flash_probe(0, 0); + if (!flash) { + printk(BIOS_DEBUG, "Could not find SPI device\n"); + return -1; + } + + return 0; +} + +/* Convert memory mapped pointer to flash offset. */ +static inline uint32_t to_flash_offset(void *p) +{ + return CONFIG_ROM_SIZE + (uintptr_t)p; +} + +int nvm_is_erased(const void *start, size_t size) +{ + const uint8_t *cur = start; + const uint8_t erased_value = 0xff; + + while (size > 0) { + if (*cur != erased_value) + return 0; + cur++; + size--; + } + return 1; +} + +int nvm_erase(void *start, size_t size) +{ + if (nvm_init() < 0) + return -1; + return flash->erase(flash, to_flash_offset(start), size); +} + +/* Write data to NVM. Returns 0 on success < 0 on error. */ +int nvm_write(void *start, const void *data, size_t size) +{ + if (nvm_init() < 0) + return -1; + return flash->write(flash, to_flash_offset(start), size, data); +} diff --git a/src/soc/intel/common/nvm.h b/src/soc/intel/common/nvm.h new file mode 100644 index 0000000000..d332d831f7 --- /dev/null +++ b/src/soc/intel/common/nvm.h @@ -0,0 +1,34 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _COMMON_NVM_H_ +#define _COMMON_NVM_H_ + +#include + +/* Determine if area is erased. returns 1 if erased. 0 otherwise. */ +int nvm_is_erased(const void *start, size_t size); + +/* Erase region according to start and size. Returns < 0 on error else 0. */ +int nvm_erase(void *start, size_t size); + +/* Write data to NVM. Returns 0 on success < 0 on error. */ +int nvm_write(void *start, const void *data, size_t size); + +#endif /* _COMMON_NVM_H_ */ -- cgit v1.2.3