summaryrefslogtreecommitdiff
path: root/src/lib/timestamp.c
diff options
context:
space:
mode:
authorAaron Durbin <adurbin@chromium.org>2015-07-03 17:04:21 -0500
committerPatrick Georgi <pgeorgi@google.com>2015-07-07 20:07:27 +0200
commit1936f6cf25bee5a0eb313d6734df7af871757e7c (patch)
tree41bfb289d9ee27700bbbeaf46dca5ab899708576 /src/lib/timestamp.c
parentd17a8623a54f40c3a8e0aab35859d8e18e73e4a0 (diff)
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> Reviewed-on: http://review.coreboot.org/10790 Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/lib/timestamp.c')
-rw-r--r--src/lib/timestamp.c261
1 files changed, 182 insertions, 79 deletions
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 = &timestamp_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)