diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Kconfig | 9 | ||||
-rw-r--r-- | src/arch/x86/init/romstage.ld | 5 | ||||
-rw-r--r-- | src/include/memlayout.h | 3 | ||||
-rw-r--r-- | src/include/symbols.h | 4 | ||||
-rw-r--r-- | src/include/timestamp.h | 13 | ||||
-rw-r--r-- | src/lib/timestamp.c | 261 |
6 files changed, 216 insertions, 79 deletions
diff --git a/src/Kconfig b/src/Kconfig index 269f7d232b..84523a2c0d 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 7b9cb6aa45..951ca656a3 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 2771f2fe14..a5296286b2 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 9102e82952..3fbf819902 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 a248ea45cd..54d69ce132 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 0c41ea2156..51e63b2b85 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) |