Commit 191c008a2155f99fc6476539878640b4032a457b

Authored by Simon Glass
1 parent a9aff2f46a

x86: Implement a cache for Memory Reference Code parameters

The memory reference code takes a very long time to 'train' its SDRAM
interface, around half a second. To avoid this delay on every boot we can
store the parameters from the last training sessions to speed up the next.

Add an implementation of this, storing the training data in CMOS RAM and
SPI flash.

Signed-off-by: Simon Glass <sjg@chromium.org>

Showing 5 changed files with 464 additions and 0 deletions Side-by-side Diff

arch/x86/cpu/ivybridge/Makefile
... ... @@ -14,6 +14,7 @@
14 14 obj-y += me_status.o
15 15 obj-y += model_206ax.o
16 16 obj-y += microcode_intel.o
  17 +obj-y += mrccache.o
17 18 obj-y += northbridge.o
18 19 obj-y += pch.o
19 20 obj-y += pci.o
arch/x86/cpu/ivybridge/mrccache.c
  1 +/*
  2 + * From Coreboot src/southbridge/intel/bd82x6x/mrccache.c
  3 + *
  4 + * Copyright (C) 2014 Google Inc.
  5 + *
  6 + * SPDX-License-Identifier: GPL-2.0
  7 + */
  8 +
  9 +#include <common.h>
  10 +#include <errno.h>
  11 +#include <fdtdec.h>
  12 +#include <net.h>
  13 +#include <spi.h>
  14 +#include <spi_flash.h>
  15 +#include <asm/arch/mrccache.h>
  16 +#include <asm/arch/sandybridge.h>
  17 +
  18 +static struct mrc_data_container *next_mrc_block(
  19 + struct mrc_data_container *mrc_cache)
  20 +{
  21 + /* MRC data blocks are aligned within the region */
  22 + u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->data_size;
  23 + if (mrc_size & (MRC_DATA_ALIGN - 1UL)) {
  24 + mrc_size &= ~(MRC_DATA_ALIGN - 1UL);
  25 + mrc_size += MRC_DATA_ALIGN;
  26 + }
  27 +
  28 + u8 *region_ptr = (u8 *)mrc_cache;
  29 + region_ptr += mrc_size;
  30 + return (struct mrc_data_container *)region_ptr;
  31 +}
  32 +
  33 +static int is_mrc_cache(struct mrc_data_container *cache)
  34 +{
  35 + return cache && (cache->signature == MRC_DATA_SIGNATURE);
  36 +}
  37 +
  38 +/*
  39 + * Find the largest index block in the MRC cache. Return NULL if none is
  40 + * found.
  41 + */
  42 +struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry)
  43 +{
  44 + struct mrc_data_container *cache, *next;
  45 + ulong base_addr, end_addr;
  46 + uint id;
  47 +
  48 + base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
  49 + end_addr = base_addr + entry->length;
  50 + cache = NULL;
  51 +
  52 + /* Search for the last filled entry in the region */
  53 + for (id = 0, next = (struct mrc_data_container *)base_addr;
  54 + is_mrc_cache(next);
  55 + id++) {
  56 + cache = next;
  57 + next = next_mrc_block(next);
  58 + if ((ulong)next >= end_addr)
  59 + break;
  60 + }
  61 +
  62 + if (id-- == 0) {
  63 + debug("%s: No valid MRC cache found.\n", __func__);
  64 + return NULL;
  65 + }
  66 +
  67 + /* Verify checksum */
  68 + if (cache->checksum != compute_ip_checksum(cache->data,
  69 + cache->data_size)) {
  70 + printf("%s: MRC cache checksum mismatch\n", __func__);
  71 + return NULL;
  72 + }
  73 +
  74 + debug("%s: picked entry %u from cache block\n", __func__, id);
  75 +
  76 + return cache;
  77 +}
  78 +
  79 +/**
  80 + * find_next_mrc_cache() - get next cache entry
  81 + *
  82 + * @entry: MRC cache flash area
  83 + * @cache: Entry to start from
  84 + *
  85 + * @return next cache entry if found, NULL if we got to the end
  86 + */
  87 +static struct mrc_data_container *find_next_mrc_cache(struct fmap_entry *entry,
  88 + struct mrc_data_container *cache)
  89 +{
  90 + ulong base_addr, end_addr;
  91 +
  92 + base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
  93 + end_addr = base_addr + entry->length;
  94 +
  95 + cache = next_mrc_block(cache);
  96 + if ((ulong)cache >= end_addr) {
  97 + /* Crossed the boundary */
  98 + cache = NULL;
  99 + debug("%s: no available entries found\n", __func__);
  100 + } else {
  101 + debug("%s: picked next entry from cache block at %p\n",
  102 + __func__, cache);
  103 + }
  104 +
  105 + return cache;
  106 +}
  107 +
  108 +int mrccache_update(struct spi_flash *sf, struct fmap_entry *entry,
  109 + struct mrc_data_container *cur)
  110 +{
  111 + struct mrc_data_container *cache;
  112 + ulong offset;
  113 + ulong base_addr;
  114 + int ret;
  115 +
  116 + /* Find the last used block */
  117 + base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
  118 + debug("Updating MRC cache data\n");
  119 + cache = mrccache_find_current(entry);
  120 + if (cache && (cache->data_size == cur->data_size) &&
  121 + (!memcmp(cache, cur, cache->data_size + sizeof(*cur)))) {
  122 + debug("MRC data in flash is up to date. No update\n");
  123 + return -EEXIST;
  124 + }
  125 +
  126 + /* Move to the next block, which will be the first unused block */
  127 + if (cache)
  128 + cache = find_next_mrc_cache(entry, cache);
  129 +
  130 + /*
  131 + * If we have got to the end, erase the entire mrc-cache area and start
  132 + * again at block 0.
  133 + */
  134 + if (!cache) {
  135 + debug("Erasing the MRC cache region of %x bytes at %x\n",
  136 + entry->length, entry->offset);
  137 +
  138 + ret = spi_flash_erase(sf, entry->offset, entry->length);
  139 + if (ret) {
  140 + debug("Failed to erase flash region\n");
  141 + return ret;
  142 + }
  143 + cache = (struct mrc_data_container *)base_addr;
  144 + }
  145 +
  146 + /* Write the data out */
  147 + offset = (ulong)cache - base_addr + entry->offset;
  148 + debug("Write MRC cache update to flash at %lx\n", offset);
  149 + ret = spi_flash_write(sf, offset, cur->data_size + sizeof(*cur), cur);
  150 + if (ret) {
  151 + debug("Failed to write to SPI flash\n");
  152 + return ret;
  153 + }
  154 +
  155 + return 0;
  156 +}
arch/x86/cpu/ivybridge/sdram.c
... ... @@ -14,12 +14,17 @@
14 14 #include <errno.h>
15 15 #include <fdtdec.h>
16 16 #include <malloc.h>
  17 +#include <net.h>
  18 +#include <rtc.h>
  19 +#include <spi.h>
  20 +#include <spi_flash.h>
17 21 #include <asm/processor.h>
18 22 #include <asm/gpio.h>
19 23 #include <asm/global_data.h>
20 24 #include <asm/mtrr.h>
21 25 #include <asm/pci.h>
22 26 #include <asm/arch/me.h>
  27 +#include <asm/arch/mrccache.h>
23 28 #include <asm/arch/pei_data.h>
24 29 #include <asm/arch/pch.h>
25 30 #include <asm/post.h>
... ... @@ -27,6 +32,10 @@
27 32  
28 33 DECLARE_GLOBAL_DATA_PTR;
29 34  
  35 +#define CMOS_OFFSET_MRC_SEED 152
  36 +#define CMOS_OFFSET_MRC_SEED_S3 156
  37 +#define CMOS_OFFSET_MRC_SEED_CHK 160
  38 +
30 39 /*
31 40 * This function looks for the highest region of memory lower than 4GB which
32 41 * has enough space for U-Boot where U-Boot is aligned on a page boundary.
... ... @@ -80,6 +89,202 @@
80 89 }
81 90 }
82 91  
  92 +static int get_mrc_entry(struct spi_flash **sfp, struct fmap_entry *entry)
  93 +{
  94 + const void *blob = gd->fdt_blob;
  95 + int node, spi_node, mrc_node;
  96 + int upto;
  97 +
  98 + /* Find the flash chip within the SPI controller node */
  99 + upto = 0;
  100 + spi_node = fdtdec_next_alias(blob, "spi", COMPAT_INTEL_ICH_SPI, &upto);
  101 + if (spi_node < 0)
  102 + return -ENOENT;
  103 + node = fdt_first_subnode(blob, spi_node);
  104 + if (node < 0)
  105 + return -ECHILD;
  106 +
  107 + /* Find the place where we put the MRC cache */
  108 + mrc_node = fdt_subnode_offset(blob, node, "rw-mrc-cache");
  109 + if (mrc_node < 0)
  110 + return -EPERM;
  111 +
  112 + if (fdtdec_read_fmap_entry(blob, mrc_node, "rm-mrc-cache", entry))
  113 + return -EINVAL;
  114 +
  115 + if (sfp) {
  116 + *sfp = spi_flash_probe_fdt(blob, node, spi_node);
  117 + if (!*sfp)
  118 + return -EBADF;
  119 + }
  120 +
  121 + return 0;
  122 +}
  123 +
  124 +static int read_seed_from_cmos(struct pei_data *pei_data)
  125 +{
  126 + u16 c1, c2, checksum, seed_checksum;
  127 +
  128 + /*
  129 + * Read scrambler seeds from CMOS RAM. We don't want to store them in
  130 + * SPI flash since they change on every boot and that would wear down
  131 + * the flash too much. So we store these in CMOS and the large MRC
  132 + * data in SPI flash.
  133 + */
  134 + pei_data->scrambler_seed = rtc_read32(CMOS_OFFSET_MRC_SEED);
  135 + debug("Read scrambler seed 0x%08x from CMOS 0x%02x\n",
  136 + pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
  137 +
  138 + pei_data->scrambler_seed_s3 = rtc_read32(CMOS_OFFSET_MRC_SEED_S3);
  139 + debug("Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n",
  140 + pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
  141 +
  142 + /* Compute seed checksum and compare */
  143 + c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed,
  144 + sizeof(u32));
  145 + c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3,
  146 + sizeof(u32));
  147 + checksum = add_ip_checksums(sizeof(u32), c1, c2);
  148 +
  149 + seed_checksum = rtc_read8(CMOS_OFFSET_MRC_SEED_CHK);
  150 + seed_checksum |= rtc_read8(CMOS_OFFSET_MRC_SEED_CHK + 1) << 8;
  151 +
  152 + if (checksum != seed_checksum) {
  153 + debug("%s: invalid seed checksum\n", __func__);
  154 + pei_data->scrambler_seed = 0;
  155 + pei_data->scrambler_seed_s3 = 0;
  156 + return -EINVAL;
  157 + }
  158 +
  159 + return 0;
  160 +}
  161 +
  162 +static int prepare_mrc_cache(struct pei_data *pei_data)
  163 +{
  164 + struct mrc_data_container *mrc_cache;
  165 + struct fmap_entry entry;
  166 + int ret;
  167 +
  168 + ret = read_seed_from_cmos(pei_data);
  169 + if (ret)
  170 + return ret;
  171 + ret = get_mrc_entry(NULL, &entry);
  172 + if (ret)
  173 + return ret;
  174 + mrc_cache = mrccache_find_current(&entry);
  175 + if (!mrc_cache)
  176 + return -ENOENT;
  177 +
  178 + /*
  179 + * TODO(sjg@chromium.org): Skip this for now as it causes boot
  180 + * problems
  181 + */
  182 + if (0) {
  183 + pei_data->mrc_input = mrc_cache->data;
  184 + pei_data->mrc_input_len = mrc_cache->data_size;
  185 + }
  186 + debug("%s: at %p, size %x checksum %04x\n", __func__,
  187 + pei_data->mrc_input, pei_data->mrc_input_len,
  188 + mrc_cache->checksum);
  189 +
  190 + return 0;
  191 +}
  192 +
  193 +static int build_mrc_data(struct mrc_data_container **datap)
  194 +{
  195 + struct mrc_data_container *data;
  196 + int orig_len;
  197 + int output_len;
  198 +
  199 + orig_len = gd->arch.mrc_output_len;
  200 + output_len = ALIGN(orig_len, 16);
  201 + data = malloc(output_len + sizeof(*data));
  202 + if (!data)
  203 + return -ENOMEM;
  204 + data->signature = MRC_DATA_SIGNATURE;
  205 + data->data_size = output_len;
  206 + data->reserved = 0;
  207 + memcpy(data->data, gd->arch.mrc_output, orig_len);
  208 +
  209 + /* Zero the unused space in aligned buffer. */
  210 + if (output_len > orig_len)
  211 + memset(data->data + orig_len, 0, output_len - orig_len);
  212 +
  213 + data->checksum = compute_ip_checksum(data->data, output_len);
  214 + *datap = data;
  215 +
  216 + return 0;
  217 +}
  218 +
  219 +static int write_seeds_to_cmos(struct pei_data *pei_data)
  220 +{
  221 + u16 c1, c2, checksum;
  222 +
  223 + /* Save the MRC seed values to CMOS */
  224 + rtc_write32(CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed);
  225 + debug("Save scrambler seed 0x%08x to CMOS 0x%02x\n",
  226 + pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
  227 +
  228 + rtc_write32(CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3);
  229 + debug("Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n",
  230 + pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
  231 +
  232 + /* Save a simple checksum of the seed values */
  233 + c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed,
  234 + sizeof(u32));
  235 + c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3,
  236 + sizeof(u32));
  237 + checksum = add_ip_checksums(sizeof(u32), c1, c2);
  238 +
  239 + rtc_write8(CMOS_OFFSET_MRC_SEED_CHK, checksum & 0xff);
  240 + rtc_write8(CMOS_OFFSET_MRC_SEED_CHK + 1, (checksum >> 8) & 0xff);
  241 +
  242 + return 0;
  243 +}
  244 +
  245 +static int sdram_save_mrc_data(void)
  246 +{
  247 + struct mrc_data_container *data;
  248 + struct fmap_entry entry;
  249 + struct spi_flash *sf;
  250 + int ret;
  251 +
  252 + if (!gd->arch.mrc_output_len)
  253 + return 0;
  254 + debug("Saving %d bytes of MRC output data to SPI flash\n",
  255 + gd->arch.mrc_output_len);
  256 +
  257 + ret = get_mrc_entry(&sf, &entry);
  258 + if (ret)
  259 + goto err_entry;
  260 + ret = build_mrc_data(&data);
  261 + if (ret)
  262 + goto err_data;
  263 + ret = mrccache_update(sf, &entry, data);
  264 + if (!ret)
  265 + debug("Saved MRC data with checksum %04x\n", data->checksum);
  266 +
  267 + free(data);
  268 +err_data:
  269 + spi_flash_free(sf);
  270 +err_entry:
  271 + if (ret)
  272 + debug("%s: Failed: %d\n", __func__, ret);
  273 + return ret;
  274 +}
  275 +
  276 +/* Use this hook to save our SDRAM parameters */
  277 +int misc_init_r(void)
  278 +{
  279 + int ret;
  280 +
  281 + ret = sdram_save_mrc_data();
  282 + if (ret)
  283 + printf("Unable to save MRC data: %d\n", ret);
  284 +
  285 + return 0;
  286 +}
  287 +
83 288 static const char *const ecc_decoder[] = {
84 289 "inactive",
85 290 "active on IO",
... ... @@ -142,6 +347,11 @@
142 347 #endif
143 348 }
144 349  
  350 +static int recovery_mode_enabled(void)
  351 +{
  352 + return false;
  353 +}
  354 +
145 355 /**
146 356 * Find the PEI executable in the ROM and execute it.
147 357 *
... ... @@ -166,6 +376,17 @@
166 376  
167 377 debug("Starting UEFI PEI System Agent\n");
168 378  
  379 + /*
  380 + * Do not pass MRC data in for recovery mode boot,
  381 + * Always pass it in for S3 resume.
  382 + */
  383 + if (!recovery_mode_enabled() ||
  384 + pei_data->boot_mode == PEI_BOOT_RESUME) {
  385 + ret = prepare_mrc_cache(pei_data);
  386 + if (ret)
  387 + debug("prepare_mrc_cache failed: %d\n", ret);
  388 + }
  389 +
169 390 /* If MRC data is not found we cannot continue S3 resume. */
170 391 if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) {
171 392 debug("Giving up in sdram_initialize: No MRC data\n");
... ... @@ -216,6 +437,8 @@
216 437 debug("System Agent Version %d.%d.%d Build %d\n",
217 438 version >> 24 , (version >> 16) & 0xff,
218 439 (version >> 8) & 0xff, version & 0xff);
  440 + debug("MCR output data length %#x at %p\n", pei_data->mrc_output_len,
  441 + pei_data->mrc_output);
219 442  
220 443 /*
221 444 * Send ME init done for SandyBridge here. This is done inside the
... ... @@ -230,6 +453,36 @@
230 453  
231 454 post_system_agent_init(pei_data);
232 455 report_memory_config();
  456 +
  457 + /* S3 resume: don't save scrambler seed or MRC data */
  458 + if (pei_data->boot_mode != PEI_BOOT_RESUME) {
  459 + /*
  460 + * This will be copied to SDRAM in reserve_arch(), then written
  461 + * to SPI flash in sdram_save_mrc_data()
  462 + */
  463 + gd->arch.mrc_output = (char *)pei_data->mrc_output;
  464 + gd->arch.mrc_output_len = pei_data->mrc_output_len;
  465 + ret = write_seeds_to_cmos(pei_data);
  466 + if (ret)
  467 + debug("Failed to write seeds to CMOS: %d\n", ret);
  468 + }
  469 +
  470 + return 0;
  471 +}
  472 +
  473 +int reserve_arch(void)
  474 +{
  475 + u16 checksum;
  476 +
  477 + checksum = compute_ip_checksum(gd->arch.mrc_output,
  478 + gd->arch.mrc_output_len);
  479 + debug("Saving %d bytes for MRC output data, checksum %04x\n",
  480 + gd->arch.mrc_output_len, checksum);
  481 + gd->start_addr_sp -= gd->arch.mrc_output_len;
  482 + memcpy((void *)gd->start_addr_sp, gd->arch.mrc_output,
  483 + gd->arch.mrc_output_len);
  484 + gd->arch.mrc_output = (char *)gd->start_addr_sp;
  485 + gd->start_addr_sp &= ~0xf;
233 486  
234 487 return 0;
235 488 }
arch/x86/include/asm/arch-ivybridge/mrccache.h
  1 +/*
  2 + * Copyright (c) 2014 Google, Inc
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + */
  6 +
  7 +#ifndef _ASM_ARCH_MRCCACHE_H
  8 +#define _ASM_ARCH_MRCCACHE_H
  9 +
  10 +#define MRC_DATA_ALIGN 0x1000
  11 +#define MRC_DATA_SIGNATURE (('M' << 0) | ('R' << 8) | ('C' << 16) | \
  12 + ('D'<<24))
  13 +
  14 +__packed struct mrc_data_container {
  15 + u32 signature; /* "MRCD" */
  16 + u32 data_size; /* Size of the 'data' field */
  17 + u32 checksum; /* IP style checksum */
  18 + u32 reserved; /* For header alignment */
  19 + u8 data[0]; /* Variable size, platform/run time dependent */
  20 +};
  21 +
  22 +struct fmap_entry;
  23 +struct spi_flash;
  24 +
  25 +/**
  26 + * mrccache_find_current() - find the latest MRC cache record
  27 + *
  28 + * This searches the MRC cache region looking for the latest record to use
  29 + * for setting up SDRAM
  30 + *
  31 + * @entry: Information about the position and size of the MRC cache
  32 + * @return pointer to latest record, or NULL if none
  33 + */
  34 +struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry);
  35 +
  36 +/**
  37 + * mrccache_update() - update the MRC cache with a new record
  38 + *
  39 + * This writes a new record to the end of the MRC cache. If the new record is
  40 + * the same as the latest record then the write is skipped
  41 + *
  42 + * @sf: SPI flash to write to
  43 + * @entry: Position and size of MRC cache in SPI flash
  44 + * @cur: Record to write
  45 + * @return 0 if updated, -EEXIST if the record is the same as the latest
  46 + * record, other error if SPI write failed
  47 + */
  48 +int mrccache_update(struct spi_flash *sf, struct fmap_entry *entry,
  49 + struct mrc_data_container *cur);
  50 +
  51 +#endif
arch/x86/include/asm/global_data.h
... ... @@ -65,6 +65,9 @@
65 65 struct mtrr_request mtrr_req[MAX_MTRR_REQUESTS];
66 66 int mtrr_req_count;
67 67 int has_mtrr;
  68 + /* MRC training data to save for the next boot */
  69 + char *mrc_output;
  70 + unsigned int mrc_output_len;
68 71 };
69 72  
70 73 #endif