[U-Boot] [PATCH 07/10] x86: Implement a cache for Memory Reference Code parameters

Bin Meng bmeng.cn at gmail.com
Sun Jan 4 08:49:40 CET 2015


Hi Simon,

On Tue, Dec 30, 2014 at 9:12 AM, Simon Glass <sjg at chromium.org> wrote:
> 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.

Is storing mrc data to cmos ram not enough, so that we must store it
to spi flash?

> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
>
>  arch/x86/cpu/ivybridge/Makefile                |   1 +
>  arch/x86/cpu/ivybridge/mrccache.c              | 156 ++++++++++++++
>  arch/x86/cpu/ivybridge/sdram.c                 | 277 +++++++++++++++++++++++++
>  arch/x86/include/asm/arch-ivybridge/mrccache.h |  51 +++++
>  arch/x86/include/asm/global_data.h             |   3 +
>  5 files changed, 488 insertions(+)
>  create mode 100644 arch/x86/cpu/ivybridge/mrccache.c
>  create mode 100644 arch/x86/include/asm/arch-ivybridge/mrccache.h
>
> diff --git a/arch/x86/cpu/ivybridge/Makefile b/arch/x86/cpu/ivybridge/Makefile
> index 0c7efae..3576b83 100644
> --- a/arch/x86/cpu/ivybridge/Makefile
> +++ b/arch/x86/cpu/ivybridge/Makefile
> @@ -14,6 +14,7 @@ obj-y += lpc.o
>  obj-y += me_status.o
>  obj-y += model_206ax.o
>  obj-y += microcode_intel.o
> +obj-y += mrccache.o
>  obj-y += northbridge.o
>  obj-y += pch.o
>  obj-y += pci.o
> diff --git a/arch/x86/cpu/ivybridge/mrccache.c b/arch/x86/cpu/ivybridge/mrccache.c
> new file mode 100644
> index 0000000..182c995
> --- /dev/null
> +++ b/arch/x86/cpu/ivybridge/mrccache.c
> @@ -0,0 +1,156 @@
> +/*
> + * From Coreboot src/southbridge/intel/bd82x6x/mrccache.c
> + *
> + * Copyright (C) 2014 Google Inc.
> + *
> + * SPDX-License-Identifier:    GPL-2.0
> + */
> +
> +#include <common.h>
> +#include <errno.h>
> +#include <fdtdec.h>
> +#include <spi.h>
> +#include <spi_flash.h>
> +#include <asm/ip_checksum.h>
> +#include <asm/arch/mrccache.h>
> +#include <asm/arch/sandybridge.h>
> +
> +static 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->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;
> +}
> +
> +static int is_mrc_cache(struct mrc_data_container *cache)
> +{
> +       return cache && (cache->signature == MRC_DATA_SIGNATURE);
> +}
> +
> +/*
> + * Find the largest index block in the MRC cache. Return NULL if none is
> + * found.
> + */
> +struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry)
> +{
> +       struct mrc_data_container *cache, *next;
> +       ulong base_addr, end_addr;
> +       uint id;
> +
> +       base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
> +       end_addr = base_addr + entry->length;
> +       cache = NULL;
> +
> +       /* Search for the last filled entry in the region */
> +       for (id = 0, next = (struct mrc_data_container *)base_addr;
> +            is_mrc_cache(next);
> +            id++) {
> +               cache = next;
> +               next = next_mrc_block(next);
> +               if ((ulong)next >= end_addr)
> +                       break;
> +       }
> +
> +       if (id-- == 0) {
> +               debug("%s: No valid MRC cache found.\n", __func__);
> +               return NULL;
> +       }
> +
> +       /* Verify checksum */
> +       if (cache->checksum != compute_ip_checksum(cache->data,
> +                                                  cache->data_size)) {
> +               printf("%s: MRC cache checksum mismatch\n", __func__);
> +               return NULL;
> +       }
> +
> +       debug("%s: picked entry %u from cache block\n", __func__, id);
> +
> +       return cache;
> +}
> +
> +/**
> + * find_next_mrc_cache() - get next cache entry
> + *
> + * @entry:     MRC cache flash area
> + * @cache:     Entry to start from
> + *
> + * @return next cache entry if found, NULL if we got to the end
> + */
> +static struct mrc_data_container *find_next_mrc_cache(struct fmap_entry *entry,
> +               struct mrc_data_container *cache)
> +{
> +       ulong base_addr, end_addr;
> +
> +       base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
> +       end_addr = base_addr + entry->length;
> +
> +       cache = next_mrc_block(cache);
> +       if ((ulong)cache >= end_addr) {
> +               /* Crossed the boundary */
> +               cache = NULL;
> +               debug("%s: no available entries found\n", __func__);
> +       } else {
> +               debug("%s: picked next entry from cache block at %p\n",
> +                     __func__, cache);
> +       }
> +
> +       return cache;
> +}
> +
> +int mrccache_update(struct spi_flash *sf, struct fmap_entry *entry,
> +                   struct mrc_data_container *cur)
> +{
> +       struct mrc_data_container *cache;
> +       ulong offset;
> +       ulong base_addr;
> +       int ret;
> +
> +       /* Find the last used block */
> +       base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
> +       debug("Updating MRC cache data\n");
> +       cache = mrccache_find_current(entry);
> +       if (cache && (cache->data_size == cur->data_size) &&
> +           (!memcmp(cache, cur, cache->data_size + sizeof(*cur)))) {
> +               debug("MRC data in flash is up to date. No update\n");
> +               return -EEXIST;
> +       }
> +
> +       /* Move to the next block, which will be the first unused block */
> +       if (cache)
> +               cache = find_next_mrc_cache(entry, cache);
> +
> +       /*
> +        * If we have got to the end, erase the entire mrc-cache area and start
> +        * again at block 0.
> +        */
> +       if (!cache) {
> +               debug("Erasing the MRC cache region of %x bytes at %x\n",
> +                     entry->length, entry->offset);
> +
> +               ret = spi_flash_erase(sf, entry->offset, entry->length);
> +               if (ret) {
> +                       debug("Failed to erase flash region\n");
> +                       return ret;
> +               }
> +               cache = (struct mrc_data_container *)base_addr;
> +       }
> +
> +       /* Write the data out */
> +       offset = (ulong)cache - base_addr + entry->offset;
> +       debug("Write MRC cache update to flash at %lx\n", offset);
> +       ret = spi_flash_write(sf, offset, cur->data_size + sizeof(*cur), cur);
> +       if (ret) {
> +               debug("Failed to write to SPI flash\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c
> index 9504735..800c75b 100644
> --- a/arch/x86/cpu/ivybridge/sdram.c
> +++ b/arch/x86/cpu/ivybridge/sdram.c
> @@ -14,12 +14,17 @@
>  #include <errno.h>
>  #include <fdtdec.h>
>  #include <malloc.h>
> +#include <spi.h>
> +#include <spi_flash.h>
> +#include <asm/cmos.h>
> +#include <asm/ip_checksum.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,195 @@ 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_ICH9_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 */
> +       pei_data->scrambler_seed = cmos_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 = cmos_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 = cmos_read(CMOS_OFFSET_MRC_SEED_CHK);
> +       seed_checksum |= cmos_read(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;
> +
> +       /* Skip this for now as it causes boot problems */

Add a TODO:?

> +       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 */
> +       cmos_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);
> +
> +       cmos_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);
> +
> +       cmos_write(checksum & 0xff, CMOS_OFFSET_MRC_SEED_CHK);
> +       cmos_write((checksum >> 8) & 0xff, CMOS_OFFSET_MRC_SEED_CHK+1);
> +
> +       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 +340,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 +369,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 +430,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 +447,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;
>  }
>
> @@ -569,6 +815,37 @@ int dram_init(void)
>
>         writew(0xCAFE, MCHBAR_REG(SSKPD));
>
> +       /* TODO: Work out ACPI resume */

Or maybe we can simply remove the following as there are lots of coreboot sutff.

> +#if CONFIG_HAVE_ACPI_RESUME && 0
> +       /*
> +        * If there is no high memory area, we didn't boot before, so
> +        * this is not a resume. In that case we just create the cbmem toc.
> +        */
> +
> +       *(u32 *)CBMEM_BOOT_MODE = 0;
> +       *(u32 *)CBMEM_RESUME_BACKUP = 0;
> +
> +       if ((boot_mode == 2) && cbmem_was_initted) {
> +               void *resume_backup_memory = cbmem_find(CBMEM_ID_RESUME);
> +               if (resume_backup_memory) {
> +                       *(u32 *)CBMEM_BOOT_MODE = boot_mode;
> +                       *(u32 *)CBMEM_RESUME_BACKUP = (u32)resume_backup_memory;
> +               }
> +               /* Magic for S3 resume */
> +               pci_write_config32(PCI_BDF(0, 0x00, 0), SKPAD, 0xcafed00d);
> +       } else if (boot_mode == 2) {
> +               /* Failed S3 resume, reset to come up cleanly */
> +               outb(0x6, 0xcf9);
> +               cpu_hlt();
> +       } else {
> +               pci_write_config32(PCI_BDF(0, 0x00, 0), SKPAD, 0xcafebabe);
> +       }
> +#endif
> +       post_code(0x3f);
> +#if CONFIG_CHROMEOS
> +       init_chromeos(boot_mode);
> +#endif
> +
>         post_code(POST_DRAM);
>
>         ret = sdram_find(dev);
> diff --git a/arch/x86/include/asm/arch-ivybridge/mrccache.h b/arch/x86/include/asm/arch-ivybridge/mrccache.h
> new file mode 100644
> index 0000000..968b2ef
> --- /dev/null
> +++ b/arch/x86/include/asm/arch-ivybridge/mrccache.h
> @@ -0,0 +1,51 @@
> +/*
> + * Copyright (c) 2014 Google, Inc
> + *
> + * SPDX-License-Identifier:    GPL-2.0+
> + */
> +
> +#ifndef _ASM_ARCH_MRCCACHE_H
> +#define _ASM_ARCH_MRCCACHE_H
> +
> +#define MRC_DATA_ALIGN         0x1000
> +#define MRC_DATA_SIGNATURE     (('M' << 0) | ('R' << 8) | ('C' << 16) | \
> +                                       ('D'<<24))
> +
> +__packed struct mrc_data_container {
> +       u32     signature;      /* "MRCD" */
> +       u32     data_size;      /* Size of the 'data' field */
> +       u32     checksum;       /* IP style checksum */
> +       u32     reserved;       /* For header alignment */
> +       u8      data[0];        /* Variable size, platform/run time dependent */
> +};
> +
> +struct fmap_entry;
> +struct spi_flash;
> +
> +/**
> + * mrccache_find_current() - find the latest MRC cache record
> + *
> + * This searches the MRC cache region looking for the latest record to use
> + * for setting up SDRAM
> + *
> + * @entry:     Information about the position and size of the MRC cache
> + * @return pointer to latest record, or NULL if none
> + */
> +struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry);
> +
> +/**
> + * mrccache_update() - update the MRC cache with a new record
> + *
> + * This writes a new record to the end of the MRC cache. If the new record is
> + * the same as the latest record then the write is skipped
> + *
> + * @sf:                SPI flash to write to
> + * @entry:     Position and size of MRC cache in SPI flash
> + * @cur:       Record to write
> + * @return 0 if updated, -EEXIST if the record is the same as the latest
> + * record, other error if SPI write failed
> + */
> +int mrccache_update(struct spi_flash *sf, struct fmap_entry *entry,
> +                   struct mrc_data_container *cur);
> +
> +#endif
> diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
> index 15e76f6..b7e7faf 100644
> --- a/arch/x86/include/asm/global_data.h
> +++ b/arch/x86/include/asm/global_data.h
> @@ -65,6 +65,9 @@ struct arch_global_data {
>  #endif
>         struct mtrr_request mtrr_req[MAX_MTRR_REQUESTS];
>         int mtrr_req_count;
> +       /* MRC training data to save for the next boot */
> +       char *mrc_output;
> +       unsigned int mrc_output_len;
>  };
>
>  #endif
> --

Regards,
Bin


More information about the U-Boot mailing list