From 1936f6cf25bee5a0eb313d6734df7af871757e7c Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 3 Jul 2015 17:04:21 -0500 Subject: 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 Original-Reviewed-on: https://chromium-review.googlesource.com/223348 Original-Reviewed-by: Aaron Durbin Original-Reviewed-by: Patrick Georgi Original-Tested-by: Furquan Shaikh Original-Commit-Queue: Furquan Shaikh Original-Change-Id: Ie5ffda3112d626068bd1904afcc5a09bc4916d16 Original-Signed-off-by: Furquan Shaikh Original-Reviewed-on: https://chromium-review.googlesource.com/224024 Original-Reviewed-by: Furquan Shaikh Original-Commit-Queue: Furquan Shaikh Original-Tested-by: Furquan Shaikh Change-Id: I8779526136e89ae61a6f177ce5c74a6530469ae1 Signed-off-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/10790 Reviewed-by: Stefan Reinauer Tested-by: build bot (Jenkins) --- src/lib/timestamp.c | 261 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 182 insertions(+), 79 deletions(-) (limited to 'src/lib/timestamp.c') diff --git a/src/lib/timestamp.c b/src/lib/timestamp.c index 0c41ea2156..51e63b2b85 100644 --- a/src/lib/timestamp.c +++ b/src/lib/timestamp.c @@ -17,10 +17,12 @@ * Foundation, Inc. */ +#include #include #include #include #include +#include #include #include #include @@ -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 = ×tamp_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); - return; + 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; } - if (ts_table->num_entries == ts_table->max_entries) + + /* 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; + } tse = &ts_table->entries[ts_table->num_entries++]; tse->entry_id = id; tse->entry_stamp = ts_time - ts_table->base_time; } -void timestamp_add_now(enum timestamp_id id) +void timestamp_add(enum timestamp_id id, uint64_t ts_time) { - 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(). - */ + struct timestamp_table *ts_table; -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); + ts_table = timestamp_table_get(); - if (ts_entries >= MAX_TIMESTAMP_CACHE) { - printk(BIOS_ERR, "ERROR: failed to add timestamp to cache\n"); + if (!ts_table) { + printk(BIOS_ERR, "ERROR: No timestamp table found\n"); return; } - ts_cache[ts_entries].id = id; - ts_cache[ts_entries].time = ts_time; - car_set_var(timestamp_entries, ++ts_entries); + + timestamp_add_table_entry(ts_table, id, ts_time); } -static void timestamp_do_sync(void) +void timestamp_add_now(enum timestamp_id id) { - 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); + timestamp_add(id, timestamp_get()); } 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; - - /* Locate and use an already existing table. */ - if (!IS_ENABLED(CONFIG_LATE_CBMEM_INIT)) - tst = cbmem_find(CBMEM_ID_TIMESTAMP); + ts_cache = timestamp_cache_get(); - 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) -- cgit v1.2.3