timestamp: Make timestamp library more flexible and intelligent

Add support for:
1) Using timestamps in bootblock and verstage
2) Allowing the timestamps to be stashed into _timestamp region so that they can
be used across multiple stages
3) Performing operations over the timestamps in _timestamp region

Instead of having two separate APIs for stashing and adding timestamps, let the
timestamp library decide on its own where to save depending on timestamp cache
status. Now the sequence of operations would be something like:
timestamp_init / timestamp_early_init : Set the base time
timestamp_add / timestamp_add_now
cbmem_initialize : It internally calls timestamp_sync
timestamp_add / timestamp_add_now

BUG=chrome-os-partner:32973
BRANCH=None
TEST=Compiles successfully for ryu and samus. cbmem -t on ryu works fine.

Change-Id: Ie5ffda3112d626068bd1904afcc5a09bc4916d16
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://chromium-review.googlesource.com/224024
Reviewed-by: Furquan Shaikh <furquan@chromium.org>
Commit-Queue: Furquan Shaikh <furquan@chromium.org>
Tested-by: Furquan Shaikh <furquan@chromium.org>
This commit is contained in:
Furquan Shaikh 2014-10-15 15:17:49 -07:00 committed by chrome-internal-fetch
commit 2f6b472984
7 changed files with 251 additions and 50 deletions

View file

@ -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.

View file

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

View file

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

View file

@ -26,6 +26,7 @@
#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__)
#include <arch/acpi.h>
#endif
#include <timestamp.h>
// 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

View file

@ -27,6 +27,7 @@
#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__)
#include <arch/acpi.h>
#endif
#include <timestamp.h>
#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;
}

View file

@ -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()! */

View file

@ -17,88 +17,247 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <console/console.h>
#include <cbmem.h>
#include <symbols.h>
#include <timestamp.h>
#include <arch/early_variables.h>
#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 = &timestamp_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