Patrick Georgi (pgeorgi@google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/10790
-gerrit
commit f7a9958c65f5556dfc26a6027ff28760f9a572aa Author: Aaron Durbin adurbin@chromium.org Date: Fri Jul 3 17:04:21 2015 -0500
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 --- src/Kconfig | 9 ++ src/arch/x86/init/romstage.ld | 5 + src/include/memlayout.h | 3 + src/include/symbols.h | 4 + src/include/timestamp.h | 13 +++ src/lib/timestamp.c | 261 +++++++++++++++++++++++++++++------------- 6 files changed, 216 insertions(+), 79 deletions(-)
diff --git a/src/Kconfig b/src/Kconfig index 269f7d2..84523a2 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -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 diff --git a/src/arch/x86/init/romstage.ld b/src/arch/x86/init/romstage.ld index 7b9cb6a..951ca65 100644 --- a/src/arch/x86/init/romstage.ld +++ b/src/arch/x86/init/romstage.ld @@ -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 diff --git a/src/include/memlayout.h b/src/include/memlayout.h index 2771f2f..a529628 100644 --- a/src/include/memlayout.h +++ b/src/include/memlayout.h @@ -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)
diff --git a/src/include/symbols.h b/src/include/symbols.h index 9102e82..3fbf819 100644 --- a/src/include/symbols.h +++ b/src/include/symbols.h @@ -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 \ diff --git a/src/include/timestamp.h b/src/include/timestamp.h index a248ea4..54d69ce 100644 --- a/src/include/timestamp.h +++ b/src/include/timestamp.h @@ -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) diff --git a/src/lib/timestamp.c b/src/lib/timestamp.c index 0c41ea2..51e63b2 100644 --- a/src/lib/timestamp.c +++ b/src/lib/timestamp.c @@ -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 = ×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)