|
@@ -14,12 +14,17 @@
|
|
|
#include <errno.h>
|
|
|
#include <fdtdec.h>
|
|
|
#include <malloc.h>
|
|
|
+#include <net.h>
|
|
|
+#include <rtc.h>
|
|
|
+#include <spi.h>
|
|
|
+#include <spi_flash.h>
|
|
|
#include <asm/processor.h>
|
|
|
#include <asm/gpio.h>
|
|
|
#include <asm/global_data.h>
|
|
|
#include <asm/mtrr.h>
|
|
|
#include <asm/pci.h>
|
|
|
#include <asm/arch/me.h>
|
|
|
+#include <asm/arch/mrccache.h>
|
|
|
#include <asm/arch/pei_data.h>
|
|
|
#include <asm/arch/pch.h>
|
|
|
#include <asm/post.h>
|
|
@@ -27,6 +32,10 @@
|
|
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
|
|
+#define CMOS_OFFSET_MRC_SEED 152
|
|
|
+#define CMOS_OFFSET_MRC_SEED_S3 156
|
|
|
+#define CMOS_OFFSET_MRC_SEED_CHK 160
|
|
|
+
|
|
|
/*
|
|
|
* This function looks for the highest region of memory lower than 4GB which
|
|
|
* has enough space for U-Boot where U-Boot is aligned on a page boundary.
|
|
@@ -80,6 +89,202 @@ void dram_init_banksize(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int get_mrc_entry(struct spi_flash **sfp, struct fmap_entry *entry)
|
|
|
+{
|
|
|
+ const void *blob = gd->fdt_blob;
|
|
|
+ int node, spi_node, mrc_node;
|
|
|
+ int upto;
|
|
|
+
|
|
|
+ /* Find the flash chip within the SPI controller node */
|
|
|
+ upto = 0;
|
|
|
+ spi_node = fdtdec_next_alias(blob, "spi", COMPAT_INTEL_ICH_SPI, &upto);
|
|
|
+ if (spi_node < 0)
|
|
|
+ return -ENOENT;
|
|
|
+ node = fdt_first_subnode(blob, spi_node);
|
|
|
+ if (node < 0)
|
|
|
+ return -ECHILD;
|
|
|
+
|
|
|
+ /* Find the place where we put the MRC cache */
|
|
|
+ mrc_node = fdt_subnode_offset(blob, node, "rw-mrc-cache");
|
|
|
+ if (mrc_node < 0)
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ if (fdtdec_read_fmap_entry(blob, mrc_node, "rm-mrc-cache", entry))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (sfp) {
|
|
|
+ *sfp = spi_flash_probe_fdt(blob, node, spi_node);
|
|
|
+ if (!*sfp)
|
|
|
+ return -EBADF;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int read_seed_from_cmos(struct pei_data *pei_data)
|
|
|
+{
|
|
|
+ u16 c1, c2, checksum, seed_checksum;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Read scrambler seeds from CMOS RAM. We don't want to store them in
|
|
|
+ * SPI flash since they change on every boot and that would wear down
|
|
|
+ * the flash too much. So we store these in CMOS and the large MRC
|
|
|
+ * data in SPI flash.
|
|
|
+ */
|
|
|
+ pei_data->scrambler_seed = rtc_read32(CMOS_OFFSET_MRC_SEED);
|
|
|
+ debug("Read scrambler seed 0x%08x from CMOS 0x%02x\n",
|
|
|
+ pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
|
|
|
+
|
|
|
+ pei_data->scrambler_seed_s3 = rtc_read32(CMOS_OFFSET_MRC_SEED_S3);
|
|
|
+ debug("Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n",
|
|
|
+ pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
|
|
|
+
|
|
|
+ /* Compute seed checksum and compare */
|
|
|
+ c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed,
|
|
|
+ sizeof(u32));
|
|
|
+ c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3,
|
|
|
+ sizeof(u32));
|
|
|
+ checksum = add_ip_checksums(sizeof(u32), c1, c2);
|
|
|
+
|
|
|
+ seed_checksum = rtc_read8(CMOS_OFFSET_MRC_SEED_CHK);
|
|
|
+ seed_checksum |= rtc_read8(CMOS_OFFSET_MRC_SEED_CHK + 1) << 8;
|
|
|
+
|
|
|
+ if (checksum != seed_checksum) {
|
|
|
+ debug("%s: invalid seed checksum\n", __func__);
|
|
|
+ pei_data->scrambler_seed = 0;
|
|
|
+ pei_data->scrambler_seed_s3 = 0;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int prepare_mrc_cache(struct pei_data *pei_data)
|
|
|
+{
|
|
|
+ struct mrc_data_container *mrc_cache;
|
|
|
+ struct fmap_entry entry;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = read_seed_from_cmos(pei_data);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ ret = get_mrc_entry(NULL, &entry);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ mrc_cache = mrccache_find_current(&entry);
|
|
|
+ if (!mrc_cache)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * TODO(sjg@chromium.org): Skip this for now as it causes boot
|
|
|
+ * problems
|
|
|
+ */
|
|
|
+ if (0) {
|
|
|
+ pei_data->mrc_input = mrc_cache->data;
|
|
|
+ pei_data->mrc_input_len = mrc_cache->data_size;
|
|
|
+ }
|
|
|
+ debug("%s: at %p, size %x checksum %04x\n", __func__,
|
|
|
+ pei_data->mrc_input, pei_data->mrc_input_len,
|
|
|
+ mrc_cache->checksum);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int build_mrc_data(struct mrc_data_container **datap)
|
|
|
+{
|
|
|
+ struct mrc_data_container *data;
|
|
|
+ int orig_len;
|
|
|
+ int output_len;
|
|
|
+
|
|
|
+ orig_len = gd->arch.mrc_output_len;
|
|
|
+ output_len = ALIGN(orig_len, 16);
|
|
|
+ data = malloc(output_len + sizeof(*data));
|
|
|
+ if (!data)
|
|
|
+ return -ENOMEM;
|
|
|
+ data->signature = MRC_DATA_SIGNATURE;
|
|
|
+ data->data_size = output_len;
|
|
|
+ data->reserved = 0;
|
|
|
+ memcpy(data->data, gd->arch.mrc_output, orig_len);
|
|
|
+
|
|
|
+ /* Zero the unused space in aligned buffer. */
|
|
|
+ if (output_len > orig_len)
|
|
|
+ memset(data->data + orig_len, 0, output_len - orig_len);
|
|
|
+
|
|
|
+ data->checksum = compute_ip_checksum(data->data, output_len);
|
|
|
+ *datap = data;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int write_seeds_to_cmos(struct pei_data *pei_data)
|
|
|
+{
|
|
|
+ u16 c1, c2, checksum;
|
|
|
+
|
|
|
+ /* Save the MRC seed values to CMOS */
|
|
|
+ rtc_write32(CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed);
|
|
|
+ debug("Save scrambler seed 0x%08x to CMOS 0x%02x\n",
|
|
|
+ pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
|
|
|
+
|
|
|
+ rtc_write32(CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3);
|
|
|
+ debug("Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n",
|
|
|
+ pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
|
|
|
+
|
|
|
+ /* Save a simple checksum of the seed values */
|
|
|
+ c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed,
|
|
|
+ sizeof(u32));
|
|
|
+ c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3,
|
|
|
+ sizeof(u32));
|
|
|
+ checksum = add_ip_checksums(sizeof(u32), c1, c2);
|
|
|
+
|
|
|
+ rtc_write8(CMOS_OFFSET_MRC_SEED_CHK, checksum & 0xff);
|
|
|
+ rtc_write8(CMOS_OFFSET_MRC_SEED_CHK + 1, (checksum >> 8) & 0xff);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdram_save_mrc_data(void)
|
|
|
+{
|
|
|
+ struct mrc_data_container *data;
|
|
|
+ struct fmap_entry entry;
|
|
|
+ struct spi_flash *sf;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!gd->arch.mrc_output_len)
|
|
|
+ return 0;
|
|
|
+ debug("Saving %d bytes of MRC output data to SPI flash\n",
|
|
|
+ gd->arch.mrc_output_len);
|
|
|
+
|
|
|
+ ret = get_mrc_entry(&sf, &entry);
|
|
|
+ if (ret)
|
|
|
+ goto err_entry;
|
|
|
+ ret = build_mrc_data(&data);
|
|
|
+ if (ret)
|
|
|
+ goto err_data;
|
|
|
+ ret = mrccache_update(sf, &entry, data);
|
|
|
+ if (!ret)
|
|
|
+ debug("Saved MRC data with checksum %04x\n", data->checksum);
|
|
|
+
|
|
|
+ free(data);
|
|
|
+err_data:
|
|
|
+ spi_flash_free(sf);
|
|
|
+err_entry:
|
|
|
+ if (ret)
|
|
|
+ debug("%s: Failed: %d\n", __func__, ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* Use this hook to save our SDRAM parameters */
|
|
|
+int misc_init_r(void)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = sdram_save_mrc_data();
|
|
|
+ if (ret)
|
|
|
+ printf("Unable to save MRC data: %d\n", ret);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static const char *const ecc_decoder[] = {
|
|
|
"inactive",
|
|
|
"active on IO",
|
|
@@ -142,6 +347,11 @@ static asmlinkage void console_tx_byte(unsigned char byte)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+static int recovery_mode_enabled(void)
|
|
|
+{
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Find the PEI executable in the ROM and execute it.
|
|
|
*
|
|
@@ -166,6 +376,17 @@ int sdram_initialise(struct pei_data *pei_data)
|
|
|
|
|
|
debug("Starting UEFI PEI System Agent\n");
|
|
|
|
|
|
+ /*
|
|
|
+ * Do not pass MRC data in for recovery mode boot,
|
|
|
+ * Always pass it in for S3 resume.
|
|
|
+ */
|
|
|
+ if (!recovery_mode_enabled() ||
|
|
|
+ pei_data->boot_mode == PEI_BOOT_RESUME) {
|
|
|
+ ret = prepare_mrc_cache(pei_data);
|
|
|
+ if (ret)
|
|
|
+ debug("prepare_mrc_cache failed: %d\n", ret);
|
|
|
+ }
|
|
|
+
|
|
|
/* If MRC data is not found we cannot continue S3 resume. */
|
|
|
if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) {
|
|
|
debug("Giving up in sdram_initialize: No MRC data\n");
|
|
@@ -216,6 +437,8 @@ int sdram_initialise(struct pei_data *pei_data)
|
|
|
debug("System Agent Version %d.%d.%d Build %d\n",
|
|
|
version >> 24 , (version >> 16) & 0xff,
|
|
|
(version >> 8) & 0xff, version & 0xff);
|
|
|
+ debug("MCR output data length %#x at %p\n", pei_data->mrc_output_len,
|
|
|
+ pei_data->mrc_output);
|
|
|
|
|
|
/*
|
|
|
* Send ME init done for SandyBridge here. This is done inside the
|
|
@@ -231,6 +454,36 @@ int sdram_initialise(struct pei_data *pei_data)
|
|
|
post_system_agent_init(pei_data);
|
|
|
report_memory_config();
|
|
|
|
|
|
+ /* S3 resume: don't save scrambler seed or MRC data */
|
|
|
+ if (pei_data->boot_mode != PEI_BOOT_RESUME) {
|
|
|
+ /*
|
|
|
+ * This will be copied to SDRAM in reserve_arch(), then written
|
|
|
+ * to SPI flash in sdram_save_mrc_data()
|
|
|
+ */
|
|
|
+ gd->arch.mrc_output = (char *)pei_data->mrc_output;
|
|
|
+ gd->arch.mrc_output_len = pei_data->mrc_output_len;
|
|
|
+ ret = write_seeds_to_cmos(pei_data);
|
|
|
+ if (ret)
|
|
|
+ debug("Failed to write seeds to CMOS: %d\n", ret);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int reserve_arch(void)
|
|
|
+{
|
|
|
+ u16 checksum;
|
|
|
+
|
|
|
+ checksum = compute_ip_checksum(gd->arch.mrc_output,
|
|
|
+ gd->arch.mrc_output_len);
|
|
|
+ debug("Saving %d bytes for MRC output data, checksum %04x\n",
|
|
|
+ gd->arch.mrc_output_len, checksum);
|
|
|
+ gd->start_addr_sp -= gd->arch.mrc_output_len;
|
|
|
+ memcpy((void *)gd->start_addr_sp, gd->arch.mrc_output,
|
|
|
+ gd->arch.mrc_output_len);
|
|
|
+ gd->arch.mrc_output = (char *)gd->start_addr_sp;
|
|
|
+ gd->start_addr_sp &= ~0xf;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|