summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/northbridge/intel/sandybridge/Kconfig17
-rw-r--r--src/northbridge/intel/sandybridge/Makefile.inc2
-rw-r--r--src/northbridge/intel/sandybridge/mrccache.c265
-rw-r--r--src/northbridge/intel/sandybridge/northbridge.c6
-rw-r--r--src/northbridge/intel/sandybridge/raminit.c124
-rw-r--r--src/northbridge/intel/sandybridge/sandybridge.h22
6 files changed, 322 insertions, 114 deletions
diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig
index 8cf0a491d6..67b3defb75 100644
--- a/src/northbridge/intel/sandybridge/Kconfig
+++ b/src/northbridge/intel/sandybridge/Kconfig
@@ -37,6 +37,23 @@ config CACHE_MRC_SIZE_KB
int
default 256
+# FIXME: build from rom size
+config MRC_CACHE_BASE
+ hex
+ default 0xff800000
+
+config MRC_CACHE_LOCATION
+ hex
+ default 0x1ec000
+
+config MRC_CACHE_SIZE
+ hex
+ default 0x10000
+
+config MRC_CACHE_ALIGNMENT
+ hex
+ default 0x1000
+
config DCACHE_RAM_BASE
hex
default 0xff7f0000
diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc
index 87a0b2efef..824700ea29 100644
--- a/src/northbridge/intel/sandybridge/Makefile.inc
+++ b/src/northbridge/intel/sandybridge/Makefile.inc
@@ -21,9 +21,11 @@ driver-y += northbridge.c
driver-y += gma.c
ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c
+ramstage-y += mrccache.c
romstage-y += udelay.c
romstage-y += raminit.c
+romstage-y += mrccache.c
romstage-y += early_init.c
romstage-y += report_platform.c
romstage-y += ../../../arch/x86/lib/walkcbfs.S
diff --git a/src/northbridge/intel/sandybridge/mrccache.c b/src/northbridge/intel/sandybridge/mrccache.c
new file mode 100644
index 0000000000..5c4c3825ce
--- /dev/null
+++ b/src/northbridge/intel/sandybridge/mrccache.c
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 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 <stdint.h>
+#include <string.h>
+#include <console/console.h>
+#include <cbfs.h>
+#include <ip_checksum.h>
+#include <device/device.h>
+#include <cbmem.h>
+#include "pei_data.h"
+#include "sandybridge.h"
+#include <spi.h>
+#include <spi_flash.h>
+/* Using the FDT FMAP for finding the MRC cache area requires including FDT
+ * support in coreboot, which we would like to avoid. There are a number of
+ * options:
+ * - Have each mainboard Kconfig supply a hard-coded offset
+ * - For ChromeOS devices: implement native FMAP
+ * - For non-ChromeOS devices: use CBFS
+ * For now let's leave this code in here until the issue is sorted out in
+ * a way that works for everyone.
+ */
+#undef USE_FDT_FMAP_FOR_MRC_CACHE
+#ifdef USE_FDT_FMAP_FOR_MRC_CACHE
+#include <fdt/libfdt.h>
+#endif
+
+struct mrc_data_container *next_mrc_block(struct mrc_data_container *mrc_cache)
+{
+ /* MRC data blocks are aligned within the region */
+ u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->mrc_data_size;
+ if (mrc_size & (MRC_DATA_ALIGN - 1UL)) {
+ mrc_size &= ~(MRC_DATA_ALIGN - 1UL);
+ mrc_size += MRC_DATA_ALIGN;
+ }
+
+ u8 *region_ptr = (u8*)mrc_cache;
+ region_ptr += mrc_size;
+ return (struct mrc_data_container *)region_ptr;
+}
+
+int is_mrc_cache(struct mrc_data_container *mrc_cache)
+{
+ return (!!mrc_cache) && (mrc_cache->mrc_signature == MRC_DATA_SIGNATURE);
+}
+
+u32 get_mrc_cache_region(struct mrc_data_container **mrc_region_ptr)
+{
+ u8 *mrc_region;
+ u32 region_size;
+ u32 *data;
+#ifdef USE_FDT_FMAP_FOR_MRC_CACHE
+ const struct fdt_header *fdt_header;
+ const struct fdt_property *fdtp;
+ int offset, len;
+ const char *compatible = "chromeos,flashmap";
+ const char *subnode = "rw-mrc-cache";
+ const char *property = "reg";
+ u64 flashrom_base = 0;
+
+ fdt_header = cbfs_find_file(CONFIG_FDT_FILE_NAME, CBFS_TYPE_FDT);
+
+ if (!fdt_header) {
+ printk(BIOS_ERR, "%s: no FDT found!\n", __func__);
+ return 0;
+ }
+
+ offset = fdt_node_offset_by_compatible(fdt_header, 0, compatible);
+ if (offset < 0) {
+ printk(BIOS_ERR, "%s: no %s node found!\n",
+ __func__, compatible);
+ return 0;
+ }
+
+ if (fdt_get_base_addr(fdt_header, offset, &flashrom_base) < 0) {
+ printk(BIOS_ERR, "%s: no base address in node name!\n",
+ __func__);
+ return 0;
+ }
+
+ offset = fdt_subnode_offset(fdt_header, offset, subnode);
+ if (offset < 0) {
+ printk(BIOS_ERR, "%s: no %s found!\n", __func__, subnode);
+ return 0;
+ }
+
+ fdtp = fdt_get_property(fdt_header, offset, property, &len);
+ if (!fdtp || (len != 8)) {
+ printk(BIOS_ERR, "%s: property %s at %p, len %d!\n",
+ __func__, property, fdtp, len);
+ return 0;
+ }
+
+ data = (u32 *)fdtp->data;
+
+ // Calculate actual address of the MRC cache in memory
+ region_size = fdt32_to_cpu(data[1]);
+ mrc_region = (u8*)((unsigned long)flashrom_base + fdt32_to_cpu(data[0]));
+#else
+ data = (u32 *)((void *)(CONFIG_MRC_CACHE_BASE + CONFIG_MRC_CACHE_LOCATION + 12));
+
+ region_size = CONFIG_MRC_CACHE_SIZE;
+ mrc_region = (u8*)(CONFIG_MRC_CACHE_BASE + be32_to_cpu(data[0]));
+#endif
+
+ *mrc_region_ptr = (struct mrc_data_container *)mrc_region;
+
+ return region_size;
+}
+
+/* find the first empty field in the MRC cache area. If there's none, return
+ * the first region. By testing for emptiness caller can detect if flash
+ * needs to be erased.
+ *
+ * FIXME: that interface is crap
+ */
+struct mrc_data_container *find_next_mrc_cache(void)
+{
+ u32 entry_id = 0;
+ struct mrc_data_container *mrc_cache = NULL;
+ u32 region_size = get_mrc_cache_region(&mrc_cache);
+ void *mrc_region = (void*)mrc_cache;
+
+ if (mrc_cache == NULL) {
+ printk(BIOS_ERR, "%s: could not find mrc cache area\n", __func__);
+ return NULL;
+ }
+
+ /* Search for the first empty entry in the region */
+ while (is_mrc_cache(mrc_cache)) {
+ entry_id++;
+ mrc_cache = next_mrc_block(mrc_cache);
+ /* If we exceed the defined area, move to front */
+ if ((void*)mrc_cache >= (void*)(mrc_region + region_size)) {
+ mrc_cache = (struct mrc_data_container *)mrc_region;
+ break;
+ }
+ }
+
+ printk(BIOS_DEBUG, "picked entry %u from cache block when looking for empty block\n", entry_id);
+
+ return mrc_cache;
+}
+
+struct mrc_data_container *find_current_mrc_cache(void)
+{
+ u32 entry_id = 0;
+ struct mrc_data_container *mrc_next, *mrc_cache = NULL;
+ u32 region_size = get_mrc_cache_region(&mrc_cache);
+ void *mrc_region = (void*)mrc_cache;
+ mrc_next = mrc_cache;
+
+ if (mrc_cache == NULL) {
+ printk(BIOS_ERR, "%s: could not find mrc cache area\n", __func__);
+ return NULL;
+ }
+
+ if (mrc_cache->mrc_data_size == -1UL) {
+ printk(BIOS_ERR, "%s: MRC cache not initialized?\n", __func__);
+ /* return non-initialized cache, so we can discern this
+ * from having no cache area at all
+ */
+ return mrc_cache;
+ } else {
+ /* Search for the last filled entry in the region */
+ while (is_mrc_cache(mrc_next)) {
+ entry_id++;
+ mrc_cache = mrc_next;
+ mrc_next = next_mrc_block(mrc_cache);
+ /* Stay in the mrcdata region defined in fdt */
+ if ((void*)mrc_next >= (void*)(mrc_region + region_size))
+ break;
+ }
+ entry_id--;
+ }
+
+ /* Verify checksum */
+ if (mrc_cache->mrc_checksum !=
+ compute_ip_checksum(mrc_cache->mrc_data,
+ mrc_cache->mrc_data_size)) {
+ printk(BIOS_ERR, "%s: MRC cache checksum mismatch\n", __func__);
+ return NULL;
+ }
+
+ printk(BIOS_DEBUG, "picked entry %u from cache block\n", entry_id);
+
+ return mrc_cache;
+}
+
+/* SPI code needs malloc/free.
+ * Also unknown if writing flash from XIP-flash code is a good idea
+ */
+#if !defined(__PRE_RAM__)
+void update_mrc_cache(void)
+{
+ struct mrc_data_container *current = cbmem_find(CBMEM_ID_MRCDATA);
+ if (!current) {
+ printk(BIOS_ERR, "No MRC cache in cbmem. Can't update flash.\n");
+ return;
+ }
+ if (current->mrc_data_size == -1) {
+ printk(BIOS_ERR, "MRC cache data in cbmem invalid.\n");
+ return;
+ }
+
+ /*
+ * we need to:
+ */
+ // 0. compare MRC data to last mrc-cache block (exit if same)
+ struct mrc_data_container *cache;
+ if ((cache = find_current_mrc_cache()) == NULL) {
+ printk(BIOS_DEBUG, "Failure looking for current last block\n");
+ return;
+ }
+
+ if ((cache->mrc_data_size == current->mrc_data_size) && (memcmp(cache, current, cache->mrc_data_size) == 0)) {
+ printk(BIOS_DEBUG, "MRC data in flash is up to date. No update.\n");
+ return;
+ }
+
+ // 1. use spi_flash_probe() to find the flash, then
+ spi_init();
+ struct spi_flash *flash = spi_flash_probe(0, 0, 1000000, SPI_MODE_3);
+ if (!flash) {
+ printk(BIOS_DEBUG, "Could not find SPI device\n");
+ return;
+ }
+
+ // 2. look up the first unused block
+ cache = find_next_mrc_cache();
+ if (!cache) {
+ printk(BIOS_DEBUG, "Could not find MRC cache area\n");
+ return;
+ }
+
+ // 3. if no such place exists, erase entire mrc-cache range & use block 0
+ if (cache->mrc_data_size != -1) {
+ printk(BIOS_DEBUG, "We need to erase the MRC cache region\n");
+ flash->erase(flash, CONFIG_MRC_CACHE_LOCATION, CONFIG_MRC_CACHE_SIZE);
+ /* we know we can start at the beginning again */
+ get_mrc_cache_region(&cache);
+ }
+ // 4. write mrc data with flash->write()
+ printk(BIOS_DEBUG, "Finally: write MRC cache update to flash\n");
+ flash->write(flash, (u32)(((void*)cache)-CONFIG_MRC_CACHE_BASE), current->mrc_data_size + sizeof(*current), current);
+}
+#endif
+
diff --git a/src/northbridge/intel/sandybridge/northbridge.c b/src/northbridge/intel/sandybridge/northbridge.c
index b2baaa3d4e..7d7153e73c 100644
--- a/src/northbridge/intel/sandybridge/northbridge.c
+++ b/src/northbridge/intel/sandybridge/northbridge.c
@@ -77,6 +77,12 @@ int add_northbridge_resources(struct lb_memory *mem)
return 0;
}
+void cbmem_post_handling(void);
+void cbmem_post_handling(void)
+{
+ update_mrc_cache();
+}
+
static int get_pcie_bar(u32 *base, u32 *len)
{
device_t dev;
diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c
index 8209980bb1..0df86d6187 100644
--- a/src/northbridge/intel/sandybridge/raminit.c
+++ b/src/northbridge/intel/sandybridge/raminit.c
@@ -36,9 +36,8 @@
#include "southbridge/intel/bd82x6x/me.h"
#if CONFIG_CHROMEOS
#include <vendorcode/google/chromeos/chromeos.h>
-#endif
-#if 0
-#include <fdt/libfdt.h>
+#else
+#define recovery_mode_enabled(x) 0
#endif
/*
@@ -56,17 +55,6 @@
#define CMOS_OFFSET_MRC_SEED_CHK 120
#endif
-#define MRC_DATA_ALIGN 0x1000
-#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24))
-
-struct mrc_data_container {
- u32 mrc_signature; // "MRCD"
- u32 mrc_data_size; // Actual total size of this structure
- u32 mrc_checksum; // IP style checksum
- u32 reserved; // For header alignment
- u8 mrc_data[0]; // Variable size, platform/run time dependent.
-} __attribute__ ((packed));
-
static void save_mrc_data(struct pei_data *pei_data)
{
u16 c1, c2, checksum;
@@ -119,22 +107,10 @@ static void save_mrc_data(struct pei_data *pei_data)
cmos_write((checksum >> 8) & 0xff, CMOS_OFFSET_MRC_SEED_CHK+1);
}
-#if CONFIG_CHROMEOS
static void prepare_mrc_cache(struct pei_data *pei_data)
{
-#if 0
- const struct fdt_header *fdt_header;
- const struct fdt_property *fdtp;
- int offset, len;
- const char *compatible = "chromeos,flashmap";
- const char *subnode = "rw-mrc-cache";
- const char *property = "reg";
- u32 *data;
- struct mrc_data_container *mrc_cache, *mrc_next;
- u8 *mrc_region, *region_ptr;
+ struct mrc_data_container *mrc_cache;
u16 c1, c2, checksum, seed_checksum;
- u32 region_size, entry_id = 0;
- u64 flashrom_base = 0;
// preset just in case there is an error
pei_data->mrc_input = NULL;
@@ -166,96 +142,18 @@ static void prepare_mrc_cache(struct pei_data *pei_data)
return;
}
- fdt_header = cbfs_find_file(CONFIG_FDT_FILE_NAME, CBFS_TYPE_FDT);
-
- if (!fdt_header) {
- printk(BIOS_ERR, "%s: no FDT found!\n", __func__);
- return;
- }
-
- offset = fdt_node_offset_by_compatible(fdt_header, 0, compatible);
- if (offset < 0) {
- printk(BIOS_ERR, "%s: no %s node found!\n",
- __func__, compatible);
- return;
- }
-
- if (fdt_get_base_addr(fdt_header, offset, &flashrom_base) < 0) {
- printk(BIOS_ERR, "%s: no base address in node name!\n",
- __func__);
- return;
- }
-
- offset = fdt_subnode_offset(fdt_header, offset, subnode);
- if (offset < 0) {
- printk(BIOS_ERR, "%s: no %s found!\n", __func__, subnode);
- return;
- }
-
- fdtp = fdt_get_property(fdt_header, offset, property, &len);
- if (!fdtp || (len != 8)) {
- printk(BIOS_ERR, "%s: property %s at %p, len %d!\n",
- __func__, property, fdtp, len);
- return;
- }
-
- data = (u32 *)fdtp->data;
-
- // Calculate actual address of the MRC cache in memory
- region_size = fdt32_to_cpu(data[1]);
- mrc_region = region_ptr = (u8*)
- ((unsigned long)flashrom_base + fdt32_to_cpu(data[0]));
- mrc_cache = mrc_next = (struct mrc_data_container *)mrc_region;
-
- if (!mrc_cache || mrc_cache->mrc_signature != MRC_DATA_SIGNATURE) {
- printk(BIOS_ERR, "%s: invalid MRC data\n", __func__);
- return;
- }
-
- if (mrc_cache->mrc_data_size == -1UL) {
- printk(BIOS_ERR, "%s: MRC cache not initialized?\n", __func__);
- return;
- } else {
- /* MRC data blocks are aligned within the region */
- u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->mrc_data_size;
- if (mrc_size & (MRC_DATA_ALIGN - 1UL)) {
- mrc_size &= ~(MRC_DATA_ALIGN - 1UL);
- mrc_size += MRC_DATA_ALIGN;
- }
-
- /* Search for the last filled entry in the region */
- while (mrc_next &&
- mrc_next->mrc_signature == MRC_DATA_SIGNATURE) {
- entry_id++;
- mrc_cache = mrc_next;
- /* Stay in the mrcdata region defined in fdt */
- if ((entry_id * mrc_size) > region_size)
- break;
- region_ptr += mrc_size;
- mrc_next = (struct mrc_data_container *)region_ptr;
- }
- entry_id--;
- }
-
- /* Verify checksum */
- if (mrc_cache->mrc_checksum !=
- compute_ip_checksum(mrc_cache->mrc_data,
- mrc_cache->mrc_data_size)) {
- printk(BIOS_ERR, "%s: MRC cache checksum mismatch\n", __func__);
+ if ((mrc_cache = find_current_mrc_cache()) == NULL) {
+ /* error message printed in find_current_mrc_cache */
return;
}
pei_data->mrc_input = mrc_cache->mrc_data;
pei_data->mrc_input_len = mrc_cache->mrc_data_size;
- printk(BIOS_DEBUG, "%s: at %p, entry %u size %x checksum %04x\n",
- __func__, pei_data->mrc_input, entry_id,
+ printk(BIOS_DEBUG, "%s: at %p, size %x checksum %04x\n",
+ __func__, pei_data->mrc_input,
pei_data->mrc_input_len, mrc_cache->mrc_checksum);
-#else
- printk(BIOS_ERR, "MRC cache handling code has to be redone.\n");
-#endif
}
-#endif
static const char* ecc_decoder[] = {
"inactive",
@@ -315,7 +213,6 @@ static void report_memory_config(void)
void sdram_initialize(struct pei_data *pei_data)
{
struct sys_info sysinfo;
- const char *target = "mrc.bin";
unsigned long entry;
report_platform_info();
@@ -330,7 +227,6 @@ void sdram_initialize(struct pei_data *pei_data)
sysinfo.boot_path = pei_data->boot_mode;
-#if CONFIG_CHROMEOS
/*
* Do not pass MRC data in for recovery mode boot,
* Always pass it in for S3 resume.
@@ -340,20 +236,20 @@ void sdram_initialize(struct pei_data *pei_data)
/* If MRC data is not found we cannot continue S3 resume. */
if (pei_data->boot_mode == 2 && !pei_data->mrc_input) {
+ printk(BIOS_DEBUG, "Giving up in sdram_initialize: No MRC data\n");
outb(0x6, 0xcf9);
hlt();
}
-#endif
/* Locate and call UEFI System Agent binary. */
- entry = (unsigned long)cbfs_find_file(target, 0xab);
+ entry = (unsigned long)cbfs_find_file("mrc.bin", 0xab);
if (entry) {
int rv;
asm volatile (
"call *%%ecx\n\t"
:"=a" (rv) : "c" (entry), "a" (pei_data));
if (rv) {
- printk(BIOS_ERR, "MRC returned %d\n", rv);
+ printk(BIOS_ERR, "MRC returned %x\n", rv);
die("Nonzero MRC return value\n");
}
} else {
diff --git a/src/northbridge/intel/sandybridge/sandybridge.h b/src/northbridge/intel/sandybridge/sandybridge.h
index aa62021743..7e12416f46 100644
--- a/src/northbridge/intel/sandybridge/sandybridge.h
+++ b/src/northbridge/intel/sandybridge/sandybridge.h
@@ -221,6 +221,28 @@ void dump_spd_registers(void);
void dump_mem(unsigned start, unsigned end);
void report_platform_info(void);
#endif /* !__SMM__ */
+
+
+#define MRC_DATA_ALIGN 0x1000
+#define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24))
+
+struct mrc_data_container {
+ u32 mrc_signature; // "MRCD"
+ u32 mrc_data_size; // Actual total size of this structure
+ u32 mrc_checksum; // IP style checksum
+ u32 reserved; // For header alignment
+ u8 mrc_data[0]; // Variable size, platform/run time dependent.
+} __attribute__ ((packed));
+
+struct mrc_data_container *next_mrc_block(struct mrc_data_container *mrc_cache);
+int is_mrc_cache(struct mrc_data_container *mrc_cache);
+u32 get_mrc_cache_region(struct mrc_data_container **mrc_region_ptr);
+struct mrc_data_container *find_next_mrc_cache(void);
+struct mrc_data_container *find_current_mrc_cache(void);
+#if !defined(__PRE_RAM__)
+void update_mrc_cache(void);
+#endif
+
#endif
#endif
#endif