diff --git a/src/arch/x86/init/romstage.ld b/src/arch/x86/init/romstage.ld index fb7e8771df..3fbadbb7aa 100644 --- a/src/arch/x86/init/romstage.ld +++ b/src/arch/x86/init/romstage.ld @@ -69,6 +69,11 @@ SECTIONS .car.data . (NOLOAD) : { _car_data_start = .; *(.car.global_data); +#if IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) + _timestamp = .; + . = . + 0x100; + _etimestamp = .; +#endif _car_data_end = .; /* The preram cbmem console area comes last to take advantage * of a zero-sized array to hold the memconsole contents. diff --git a/src/include/timestamp.h b/src/include/timestamp.h index ff7fa464be..e3e9450784 100644 --- a/src/include/timestamp.h +++ b/src/include/timestamp.h @@ -59,18 +59,45 @@ enum timestamp_id { }; #if CONFIG_COLLECT_TIMESTAMPS +/* + * Order of usage of timestamp library is: + * Call timestamp_early_init / timestamp_init to set base time before any + * timestamp_add function is called. timestamp_early_init also ensures that the + * cache is valid in _timestamp region. + * After this, timestamp_add / timestamp_add_now can be used to record + * timestamps. Sync will be automatically taken care of by cbmem_initialize + */ +/* + * Initialize the cache to a valid state and set the base time. + * This function is used before DRAM is setup so that the timestamp cache can + * be initialized in _timestamp region. + * Both, timestamp_init and timestamp_early_init reset the cbmem state to + * timestamp table reset required. Thus, whenever a timestamp_add or + * timestamp_sync is done to add new entries into the cbmem timestamp table, it + * first resets the table to 0 entries. + */ +void timestamp_early_init(uint64_t base); +/* Initialize the base time for timestamps and mark cache as valid */ 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); -void timestamp_stash(enum timestamp_id id); +/* + * Sync all timestamps from timestamp_cache to cbmem area. Called by + * cbmem_initialize. + */ void timestamp_sync(void); /* Implemented by the architecture code */ uint64_t timestamp_get(void); #else +#define timestamp_early_init(base) #define timestamp_init(base) #define timestamp_add(id, time) #define timestamp_add_now(id) -#define timestamp_stash(id) #define timestamp_sync() #endif diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index a76e390e43..5dae1437da 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -24,7 +24,7 @@ bootblock-y += memcmp.c ifeq ($(CONFIG_ARCH_BOOTBLOCK_ARM_V7_M),y) bootblock-y += memset.c memcpy.c memmove.c endif - +bootblock-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c bootblock-$(CONFIG_GENERIC_UDELAY) += timer.c ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y) @@ -51,6 +51,7 @@ verstage-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c verstage-$(CONFIG_COMMON_CBFS_SPI_WRAPPER) += cbfs_spi.c verstage-y += tlcl.c verstage-$(CONFIG_GENERIC_UDELAY) += timer.c +verstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c romstage-y += delay.c romstage-y += cbfs.c diff --git a/src/lib/cbmem.c b/src/lib/cbmem.c index d0bf54d39d..a2cf8e3839 100644 --- a/src/lib/cbmem.c +++ b/src/lib/cbmem.c @@ -26,6 +26,7 @@ #if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__) #include #endif +#include // The CBMEM TOC reserves 512 bytes to keep // the other entries somewhat aligned. @@ -232,6 +233,9 @@ int cbmem_initialize(void) /* Migrate cache-as-ram variables. */ car_migrate_variables(); + /* Pull in all timestamps from timestamp cache into cbmem area */ + timestamp_sync(); + return rv; } #endif diff --git a/src/lib/dynamic_cbmem.c b/src/lib/dynamic_cbmem.c index ecfe5240c3..a613596242 100644 --- a/src/lib/dynamic_cbmem.c +++ b/src/lib/dynamic_cbmem.c @@ -27,6 +27,7 @@ #if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__) #include #endif +#include #ifndef UINT_MAX #define UINT_MAX 4294967295U @@ -186,6 +187,9 @@ void cbmem_initialize_empty(void) /* Migrate cache-as-ram variables. */ car_migrate_variables(); + + /* Pull in all timestamps from timestamp cache into cbmem area */ + timestamp_sync(); } static inline int cbmem_fail_recovery(void) @@ -265,6 +269,9 @@ int cbmem_initialize(void) /* Migrate cache-as-ram variables. */ car_migrate_variables(); + /* Pull in all timestamps from timestamp cache into cbmem area */ + timestamp_sync(); + /* Recovery successful. */ return 0; } diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c index 80ee68befe..85934277c8 100644 --- a/src/lib/hardwaremain.c +++ b/src/lib/hardwaremain.c @@ -130,7 +130,7 @@ static boot_state_t bs_pre_device(void *arg) static boot_state_t bs_dev_init_chips(void *arg) { - timestamp_stash(TS_DEVICE_ENUMERATE); + timestamp_add_now(TS_DEVICE_ENUMERATE); /* Initialize chips early, they might disable unused devices. */ dev_initialize_chips(); @@ -148,7 +148,7 @@ static boot_state_t bs_dev_enumerate(void *arg) static boot_state_t bs_dev_resources(void *arg) { - timestamp_stash(TS_DEVICE_CONFIGURE); + timestamp_add_now(TS_DEVICE_CONFIGURE); /* Now compute and assign the bus resources. */ dev_configure(); @@ -158,7 +158,7 @@ static boot_state_t bs_dev_resources(void *arg) static boot_state_t bs_dev_eanble(void *arg) { - timestamp_stash(TS_DEVICE_ENABLE); + timestamp_add_now(TS_DEVICE_ENABLE); /* Now actually enable devices on the bus */ dev_enable(); @@ -168,7 +168,7 @@ static boot_state_t bs_dev_eanble(void *arg) static boot_state_t bs_dev_init(void *arg) { - timestamp_stash(TS_DEVICE_INITIALIZE); + timestamp_add_now(TS_DEVICE_INITIALIZE); /* And of course initialize devices on the bus */ dev_initialize(); @@ -178,9 +178,7 @@ static boot_state_t bs_dev_init(void *arg) static boot_state_t bs_post_device(void *arg) { - timestamp_stash(TS_DEVICE_DONE); - - timestamp_sync(); + timestamp_add_now(TS_DEVICE_DONE); return BS_OS_RESUME_CHECK; } @@ -484,7 +482,7 @@ static void boot_state_schedule_static_entries(void) void main(void) { - timestamp_stash(TS_START_RAMSTAGE); + timestamp_add_now(TS_START_RAMSTAGE); post_code(POST_ENTRY_RAMSTAGE); /* console_init() MUST PRECEDE ALL printk()! */ diff --git a/src/lib/timestamp.c b/src/lib/timestamp.c index 58d25e3971..1af491dc48 100644 --- a/src/lib/timestamp.c +++ b/src/lib/timestamp.c @@ -17,88 +17,247 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */ +#include #include #include #include #include +#include #include #include #define MAX_TIMESTAMPS 30 -void timestamp_init(uint64_t base) +#define MAX_TIMESTAMP_CACHE 16 + +struct __attribute__((__packed__)) timestamp_cache { + uint16_t cache_state; + uint16_t cbmem_state; + struct timestamp_table table; + struct timestamp_entry entries[MAX_TIMESTAMP_CACHE]; +}; + +#define USE_TIMESTAMP_REGION \ + (IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) && \ + defined(__PRE_RAM__)) + +#define USE_LOCAL_TIMESTAMP_CACHE (!defined(__PRE_RAM__)) + +#define HAS_CBMEM \ + (defined(__ROMSTAGE__) || defined(__RAMSTAGE__)) + +#if USE_LOCAL_TIMESTAMP_CACHE +static struct timestamp_cache timestamp_cache; +#endif + +enum { + TIMESTAMP_CACHE_UNINITIALIZED = 0, + TIMESTAMP_CACHE_INITIALIZED, + TIMESTAMP_CACHE_NOT_NEEDED, +}; + +enum { + TIMESTAMP_CBMEM_RESET_NOT_REQD = 0, + TIMESTAMP_CBMEM_RESET_REQD, +}; + +static void timestamp_cache_init(struct timestamp_cache *ts_cache, + uint64_t base, uint16_t cbmem_state) { - struct timestamp_table* tst; + 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; + ts_cache->cbmem_state = cbmem_state; +} + +static struct timestamp_cache *timestamp_cache_get(void) +{ + struct timestamp_cache *ts_cache = NULL; + +#if USE_LOCAL_TIMESTAMP_CACHE + ts_cache = ×tamp_cache; +#elif IS_ENABLED(CONFIG_HAS_PRECBMEM_TIMESTAMP_REGION) + if (_timestamp_size < sizeof(*ts_cache)) + BUG(); + ts_cache = car_get_var_ptr((void *)_timestamp); +#endif + + if (ts_cache && ts_cache->cache_state == TIMESTAMP_CACHE_UNINITIALIZED) + timestamp_cache_init(ts_cache, 0, + TIMESTAMP_CBMEM_RESET_NOT_REQD); + + return ts_cache; +} + +#if HAS_CBMEM +static struct timestamp_table *timestamp_alloc_cbmem_table(void) +{ + struct timestamp_table *tst; tst = cbmem_add(CBMEM_ID_TIMESTAMP, sizeof(struct timestamp_table) + MAX_TIMESTAMPS * sizeof(struct timestamp_entry)); - if (!tst) { - printk(BIOS_ERR, "ERROR: failed to allocate timestamp table\n"); - return; - } + if (!tst) + return NULL; - tst->base_time = base; + tst->base_time = 0; tst->max_entries = MAX_TIMESTAMPS; tst->num_entries = 0; -} -void timestamp_add(enum timestamp_id id, uint64_t ts_time) + return tst; +} +#endif + +static struct timestamp_table *timestamp_table_get(void) { - struct timestamp_entry *tse; MAYBE_STATIC struct timestamp_table *ts_table = NULL; + struct timestamp_cache *ts_cache; + + ts_cache = timestamp_cache_get(); + + if (ts_cache == NULL) { +#if HAS_CBMEM + ts_table = cbmem_find(CBMEM_ID_TIMESTAMP); +#endif + return ts_table; + } + + if (ts_cache->cache_state != TIMESTAMP_CACHE_NOT_NEEDED) + return &ts_cache->table; + +#if HAS_CBMEM + if (ts_cache->cbmem_state == TIMESTAMP_CBMEM_RESET_REQD) { + ts_table = timestamp_alloc_cbmem_table(); + ts_cache->cbmem_state = TIMESTAMP_CBMEM_RESET_NOT_REQD; + } if (!ts_table) ts_table = cbmem_find(CBMEM_ID_TIMESTAMP); - if (!ts_table || (ts_table->num_entries == ts_table->max_entries)) +#endif + + 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: dropped a timestamp entry\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_sync(void) +{ + uint32_t i; + + struct timestamp_cache *ts_cache; + struct timestamp_table *ts_cache_table; + struct timestamp_table *ts_cbmem_table = NULL; + + ts_cache = timestamp_cache_get(); + + /* No timestamp cache found */ + if (ts_cache == NULL) { + printk(BIOS_ERR, "ERROR: No timestamp table allocated\n"); + return; + } + + ts_cache_table = &ts_cache->table; + +#if HAS_CBMEM + ts_cbmem_table = cbmem_find(CBMEM_ID_TIMESTAMP); + + if ((ts_cache->cbmem_state == TIMESTAMP_CBMEM_RESET_REQD) || + (ts_cbmem_table == NULL)) + ts_cbmem_table = timestamp_alloc_cbmem_table(); +#endif + + if (ts_cbmem_table == NULL) { + printk(BIOS_ERR, "ERROR: No timestamp table allocated\n"); + return; + } + + if (ts_cbmem_table == ts_cache_table) { + printk(BIOS_ERR, "ERROR: table error to sync timestamps\n"); + return; + } + + ts_cache->cache_state = TIMESTAMP_CACHE_NOT_NEEDED; + ts_cache->cbmem_state = TIMESTAMP_CBMEM_RESET_NOT_REQD; + + /* + * There can be two cases for timestamp sync: + * 1. Newly added cbmem_table will have base_time of 0. Thus, no + * adjusments are needed for the timestamps being added from cache to + * cbmem table. + * 2. Timestamps added to cache before ramstage: In this case, the + * base_time in cache table would be 0 and add_table_entry will take + * care of subtracting the appropriate base_time. + */ + 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); + } + + ts_cache_table->num_entries = 0; + /* 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; +} + +void timestamp_early_init(uint64_t base) +{ + struct timestamp_cache *ts_cache; + ts_cache = timestamp_cache_get(); + if (ts_cache == NULL) + BUG(); + timestamp_cache_init(ts_cache, base, TIMESTAMP_CBMEM_RESET_REQD); +} + +void timestamp_init(uint64_t base) +{ + struct timestamp_cache *ts_cache; + struct timestamp_table *tst; + + ts_cache = timestamp_cache_get(); + + ts_cache->cbmem_state = TIMESTAMP_CBMEM_RESET_REQD; + + tst = timestamp_table_get(); + + if (!tst) { + printk(BIOS_ERR, "ERROR: No timestamp table to init\n"); + return; + } + + tst->base_time = base; +} + +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: dropped a timestamp entry\n"); + return; + } + + timestamp_add_table_entry(ts_table, id, ts_time); +} + void timestamp_add_now(enum timestamp_id id) { timestamp_add(id, timestamp_get()); } - -#ifndef __PRE_RAM__ - -#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 time stamps. - * This is needed when time stamping before the CBMEM area - * is initialized. The function timestamp_sync() is used to - * write the time stamps to the CBMEM area. This is done in - * ram stage main() - */ - -void timestamp_stash(enum timestamp_id id) -{ - if (timestamp_entries >= MAX_TIMESTAMP_CACHE) { - printk(BIOS_ERR, "ERROR: failed to add timestamp to cache\n"); - return; - } - timestamp_cache[timestamp_entries].id = id; - timestamp_cache[timestamp_entries].time = timestamp_get(); - timestamp_entries++; -} - -void timestamp_sync(void) -{ - int i; - for (i = 0; i < timestamp_entries; i++) - timestamp_add(timestamp_cache[i].id, timestamp_cache[i].time); - timestamp_entries = 0; -} - -#endif