timestamp: add generic cache region

In order to accommodate tracking timestamps in all the
__PRE_RAM__ stages (bootblock, verstage, romstage, etc)
of a platform one needs to provide a way to specify
a persistent region of SRAM or cache-as-ram to store
the timestamps until cbmem comes online. Provide that
infrastructure.

Based on original patches from chromium.org:
Original-Change-Id: I4d78653c0595523eeeb02115423e7fecceea5e1e
Original-Signed-off-by: Furquan Shaikh <furquan@google.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/223348
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Original-Reviewed-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Tested-by: Furquan Shaikh <furquan@chromium.org>
Original-Commit-Queue: Furquan Shaikh <furquan@chromium.org>

Original-Change-Id: Ie5ffda3112d626068bd1904afcc5a09bc4916d16
Original-Signed-off-by: Furquan Shaikh <furquan@google.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/224024
Original-Reviewed-by: Furquan Shaikh <furquan@chromium.org>
Original-Commit-Queue: Furquan Shaikh <furquan@chromium.org>
Original-Tested-by: Furquan Shaikh <furquan@chromium.org>

Change-Id: I8779526136e89ae61a6f177ce5c74a6530469ae1
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/10790
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Tested-by: build bot (Jenkins)
This commit is contained in:
Aaron Durbin 2015-07-03 17:04:21 -05:00 committed by Patrick Georgi
parent d17a8623a5
commit 1936f6cf25
6 changed files with 221 additions and 84 deletions

View File

@ -200,6 +200,15 @@ config COLLECT_TIMESTAMPS
Make coreboot create a table of timer-ID/timer-value pairs to
allow measuring time spent at different phases of the boot process.
config HAS_PRECBMEM_TIMESTAMP_REGION
bool "Timestamp region exists for pre-cbmem timestamps"
default y if ARCH_ROMSTAGE_X86_32 && CACHE_AS_RAM
depends on COLLECT_TIMESTAMPS
help
A separate region is maintained to allow storing of timestamps before
cbmem comes up. This is useful for storing timestamps across different
stage boundaries.
config USE_BLOBS
bool "Allow use of binary-only repository"
default n

View File

@ -52,6 +52,11 @@ SECTIONS
. = CONFIG_DCACHE_RAM_BASE;
.car.data . (NOLOAD) : {
_car_data_start = .;
#if IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION)
_timestamp = .;
. = . + 0x100;
_etimestamp = .;
#endif
*(.car.global_data);
_car_data_end = .;
/* The preram cbmem console area comes last to take advantage

View File

@ -47,6 +47,9 @@
#define DRAM_START(addr) SYMBOL(dram, addr)
#define TIMESTAMP(addr, size) \
REGION(timestamp, addr, size, 8)
#define PRERAM_CBMEM_CONSOLE(addr, size) \
REGION(preram_cbmem_console, addr, size, 4)

View File

@ -28,6 +28,10 @@ extern u8 _esram[];
extern u8 _dram[];
extern u8 _timestamp[];
extern u8 _etimestamp[];
#define _timestamp_size (_etimestamp - _timestamp)
extern u8 _preram_cbmem_console[];
extern u8 _epreram_cbmem_console[];
#define _preram_cbmem_console_size \

View File

@ -89,8 +89,21 @@ enum timestamp_id {
};
#if CONFIG_COLLECT_TIMESTAMPS && (CONFIG_EARLY_CBMEM_INIT || !defined(__PRE_RAM__))
/*
* timestamp_init() needs to be called once for each of these cases:
* 1. __PRE_RAM__ (bootblock, romstage, verstage, etc) and
* 2. !__PRE_RAM__ (ramstage)
* The latter is taken care of by the generic coreboot infrastructure so
* it's up to the chipset/arch to call timestamp_init() in *one* of
* the __PRE_RAM__ stages. If multiple calls are made timestamps will be lost.
*/
void timestamp_init(uint64_t base);
/*
* Add a new timestamp. Depending on cbmem is available or not, this timestamp
* will be stored to cbmem / timestamp cache.
*/
void timestamp_add(enum timestamp_id id, uint64_t ts_time);
/* Calls timestamp_add with current timestamp. */
void timestamp_add_now(enum timestamp_id id);
#else
#define timestamp_init(base)

View File

@ -17,10 +17,12 @@
* Foundation, Inc.
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <console/console.h>
#include <cbmem.h>
#include <symbols.h>
#include <timer.h>
#include <timestamp.h>
#include <arch/early_variables.h>
@ -29,12 +31,65 @@
#define MAX_TIMESTAMPS 60
static struct timestamp_table* ts_table_p CAR_GLOBAL = NULL;
static uint64_t ts_basetime CAR_GLOBAL = 0;
#define MAX_TIMESTAMP_CACHE 16
static void timestamp_stash(enum timestamp_id id, uint64_t ts_time);
struct __attribute__((__packed__)) timestamp_cache {
int cache_state;
struct timestamp_table table;
/* The struct timestamp_table has a 0 length array as its last field.
* The following 'entries' array serves as the storage space for the
* cache. */
struct timestamp_entry entries[MAX_TIMESTAMP_CACHE];
};
static void timestamp_real_init(uint64_t base)
#if (IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) && defined(__PRE_RAM__))
#define USE_TIMESTAMP_REGION 1
#else
#define USE_TIMESTAMP_REGION 0
#endif
/* The cache location will sit in BSS when in ramstage. */
#define TIMESTAMP_CACHE_IN_BSS ENV_RAMSTAGE
#define HAS_CBMEM (ENV_ROMSTAGE || ENV_RAMSTAGE)
/* Storage of cache entries during ramstage prior to cbmem coming online. */
static struct timestamp_cache timestamp_cache;
enum {
TIMESTAMP_CACHE_UNINITIALIZED = 0,
TIMESTAMP_CACHE_INITIALIZED,
TIMESTAMP_CACHE_NOT_NEEDED,
};
static void timestamp_cache_init(struct timestamp_cache *ts_cache,
uint64_t base)
{
ts_cache->table.num_entries = 0;
ts_cache->table.max_entries = MAX_TIMESTAMP_CACHE;
ts_cache->table.base_time = base;
ts_cache->cache_state = TIMESTAMP_CACHE_INITIALIZED;
}
static struct timestamp_cache *timestamp_cache_get(void)
{
struct timestamp_cache *ts_cache = NULL;
if (TIMESTAMP_CACHE_IN_BSS) {
ts_cache = &timestamp_cache;
} else if (USE_TIMESTAMP_REGION) {
if (_timestamp_size < sizeof(*ts_cache))
BUG();
ts_cache = car_get_var_ptr((void *)_timestamp);
}
if (ts_cache && ts_cache->cache_state == TIMESTAMP_CACHE_UNINITIALIZED)
timestamp_cache_init(ts_cache, 0);
return ts_cache;
}
static struct timestamp_table *timestamp_alloc_cbmem_table(void)
{
struct timestamp_table* tst;
@ -43,13 +98,13 @@ static void timestamp_real_init(uint64_t base)
MAX_TIMESTAMPS * sizeof(struct timestamp_entry));
if (!tst)
return;
return NULL;
tst->base_time = base;
tst->base_time = 0;
tst->max_entries = MAX_TIMESTAMPS;
tst->num_entries = 0;
car_set_var(ts_table_p, tst);
return tst;
}
/* Determine if one should proceed into timestamp code. This is for protecting
@ -64,117 +119,165 @@ static int timestamp_should_run(void)
return 1;
}
void timestamp_add(enum timestamp_id id, uint64_t ts_time)
static struct timestamp_table *timestamp_table_get(void)
{
struct timestamp_entry *tse;
struct timestamp_table *ts_table = NULL;
MAYBE_STATIC struct timestamp_table *ts_table = NULL;
struct timestamp_cache *ts_cache;
if (!timestamp_should_run())
return;
return NULL;
ts_table = car_get_var(ts_table_p);
if (!ts_table) {
timestamp_stash(id, ts_time);
if (ts_table != NULL)
return ts_table;
ts_cache = timestamp_cache_get();
if (ts_cache == NULL) {
if (HAS_CBMEM)
ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
return ts_table;
}
/* Cache is required. */
if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED)
return &ts_cache->table;
/* Cache shouldn't be used but there's no backing store. */
if (!HAS_CBMEM)
return NULL;
ts_table = cbmem_find(CBMEM_ID_TIMESTAMP);
return ts_table;
}
static void timestamp_add_table_entry(struct timestamp_table *ts_table,
enum timestamp_id id, uint64_t ts_time)
{
struct timestamp_entry *tse;
if (ts_table->num_entries == ts_table->max_entries) {
printk(BIOS_ERR, "ERROR: Timestamp table full\n");
return;
}
if (ts_table->num_entries == ts_table->max_entries)
return;
tse = &ts_table->entries[ts_table->num_entries++];
tse->entry_id = id;
tse->entry_stamp = ts_time - ts_table->base_time;
}
void timestamp_add(enum timestamp_id id, uint64_t ts_time)
{
struct timestamp_table *ts_table;
ts_table = timestamp_table_get();
if (!ts_table) {
printk(BIOS_ERR, "ERROR: No timestamp table found\n");
return;
}
timestamp_add_table_entry(ts_table, id, ts_time);
}
void timestamp_add_now(enum timestamp_id id)
{
timestamp_add(id, timestamp_get());
}
#define MAX_TIMESTAMP_CACHE 8
struct timestamp_cache {
enum timestamp_id id;
uint64_t time;
} timestamp_cache[MAX_TIMESTAMP_CACHE] CAR_GLOBAL;
static int timestamp_entries CAR_GLOBAL = 0;
/**
* timestamp_stash() allows to temporarily cache timestamps.
* This is needed when timestamping before the CBMEM area
* is initialized. The function timestamp_do_sync() is used to
* write the timestamps to the CBMEM area and this is done as
* part of CAR migration for romstage, and in ramstage main().
*/
static void timestamp_stash(enum timestamp_id id, uint64_t ts_time)
{
struct timestamp_cache *ts_cache = car_get_var(timestamp_cache);
int ts_entries = car_get_var(timestamp_entries);
if (ts_entries >= MAX_TIMESTAMP_CACHE) {
printk(BIOS_ERR, "ERROR: failed to add timestamp to cache\n");
return;
}
ts_cache[ts_entries].id = id;
ts_cache[ts_entries].time = ts_time;
car_set_var(timestamp_entries, ++ts_entries);
}
static void timestamp_do_sync(void)
{
struct timestamp_cache *ts_cache = car_get_var(timestamp_cache);
int ts_entries = car_get_var(timestamp_entries);
int i;
for (i = 0; i < ts_entries; i++)
timestamp_add(ts_cache[i].id, ts_cache[i].time);
car_set_var(timestamp_entries, 0);
}
void timestamp_init(uint64_t base)
{
struct timestamp_cache *ts_cache;
if (!timestamp_should_run())
return;
#ifdef __PRE_RAM__
/* Copy of basetime, it is too early for CBMEM. */
car_set_var(ts_basetime, base);
#else
struct timestamp_table *tst = NULL;
ts_cache = timestamp_cache_get();
/* Locate and use an already existing table. */
if (!IS_ENABLED(CONFIG_LATE_CBMEM_INIT))
tst = cbmem_find(CBMEM_ID_TIMESTAMP);
if (tst) {
car_set_var(ts_table_p, tst);
if (!ts_cache) {
printk(BIOS_ERR, "ERROR: No timestamp cache to init\n");
return;
}
/* Copy of basetime, may be too early for CBMEM. */
car_set_var(ts_basetime, base);
timestamp_real_init(base);
#endif
timestamp_cache_init(ts_cache, base);
}
static void timestamp_reinit(int is_recovery)
static void timestamp_sync_cache_to_cbmem(int is_recovery)
{
uint32_t i;
struct timestamp_cache *ts_cache;
struct timestamp_table *ts_cache_table;
struct timestamp_table *ts_cbmem_table = NULL;
if (!timestamp_should_run())
return;
#ifdef __PRE_RAM__
timestamp_real_init(car_get_var(ts_basetime));
#else
if (!car_get_var(ts_table_p))
timestamp_init(car_get_var(ts_basetime));
#endif
if (car_get_var(ts_table_p))
timestamp_do_sync();
ts_cache = timestamp_cache_get();
/* No timestamp cache found */
if (ts_cache == NULL) {
printk(BIOS_ERR, "ERROR: No timestamp cache found\n");
return;
}
ts_cache_table = &ts_cache->table;
/* cbmem is being recovered. */
if (is_recovery) {
/* x86 resume path expects timestamps to be reset. */
if (IS_ENABLED(CONFIG_ARCH_ROMSTAGE_X86_32) && ENV_ROMSTAGE)
ts_cbmem_table = timestamp_alloc_cbmem_table();
else {
/* Find existing table in cbmem. */
ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP);
/* No existing timestamp table. */
if (ts_cbmem_table == NULL)
ts_cbmem_table = timestamp_alloc_cbmem_table();
}
} else
/* First time sync. Add new table. */
ts_cbmem_table = timestamp_alloc_cbmem_table();
if (ts_cbmem_table == NULL) {
printk(BIOS_ERR, "ERROR: No timestamp table allocated\n");
return;
}
/*
* There's no need to worry about the base_time fields being out of
* sync because the following configurations are used/supported:
*
* 1. CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION is enabled. This
* implies CONFIG_EARLY_CBMEM_INIT so once cbmem comes
* online we sync the timestamps to the cbmem storage while
* running in romstage. In ramstage the cbmem area is
* recovered and utilized.
*
* 2. CONFIG_LATE_CBMEM_INIT (!CONFIG_EARLY_CBMEM_INIT) is
* being used. That means the only cache that exists is
* in ramstage. Once cbmem comes online in ramstage those
* values are sync'd over.
*
* Any other combinations will result in inconsistent base_time
* values including bizarre timestamp values.
*/
for (i = 0; i < ts_cache_table->num_entries; i++) {
struct timestamp_entry *tse = &ts_cache_table->entries[i];
timestamp_add_table_entry(ts_cbmem_table, tse->entry_id,
tse->entry_stamp);
}
/* Freshly added cbmem table has base_time 0. Inherit cache base_time */
if (ts_cbmem_table->base_time == 0)
ts_cbmem_table->base_time = ts_cache_table->base_time;
/* Cache no longer required. */
ts_cache_table->num_entries = 0;
ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED;
}
/* Call timestamp_reinit CBMEM init hooks. */
ROMSTAGE_CBMEM_INIT_HOOK(timestamp_reinit)
RAMSTAGE_CBMEM_INIT_HOOK(timestamp_reinit)
ROMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
RAMSTAGE_CBMEM_INIT_HOOK(timestamp_sync_cache_to_cbmem)
/* Provide default timestamp implementation using monotonic timer. */
uint64_t __attribute__((weak)) timestamp_get(void)