|
|
|
@ -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);
|
|
|
|
|
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)
|
|
|
|
|