Skip to content

Commit

Permalink
util/cbmem: add an option to append timestamp
Browse files Browse the repository at this point in the history
Add an option to the cbmem utility that can be used to append an entry
to the cbmem timestamp table from userspace. This is useful for
bookkeeping of post-coreboot timing information while still being able
to use cbmem-based tooling for processing the generated data.

BUG=b:217638034
BRANCH=none
TEST=Manual: cbmem -a 1234 to append timestamp, verify that cbmem -t
shows the added timestamp.

Change-Id: Ic99e5a11d8cc3f9fffae8eaf2787652105cf4842
Signed-off-by: Mattias Nissler <mnissler@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/62639
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
  • Loading branch information
saittam authored and jwerner-chromium committed Apr 22, 2022
1 parent 0320518 commit ecc165b
Showing 1 changed file with 96 additions and 7 deletions.
103 changes: 96 additions & 7 deletions util/cbmem/cbmem.c
Expand Up @@ -29,6 +29,10 @@
#include <sys/sysctl.h>
#endif

#if defined(__i386__) || defined(__x86_64__)
#include <x86intrin.h>
#endif

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

typedef uint8_t u8;
Expand Down Expand Up @@ -57,6 +61,9 @@ static int verbose = 0;
static int mem_fd;
static struct mapping lbtable_mapping;

/* TSC frequency from the LB_TAG_TSC_INFO record. 0 if not present. */
static uint32_t tsc_freq_khz = 0;

static void die(const char *msg)
{
if (msg)
Expand All @@ -80,9 +87,9 @@ static inline size_t size_to_mib(size_t sz)
}

/* Return mapping of physical address requested. */
static const void *mapping_virt(const struct mapping *mapping)
static void *mapping_virt(const struct mapping *mapping)
{
const char *v = mapping->virt;
char *v = mapping->virt;

if (v == NULL)
return NULL;
Expand All @@ -91,8 +98,8 @@ static const void *mapping_virt(const struct mapping *mapping)
}

/* Returns virtual address on success, NULL on error. mapping is filled in. */
static const void *map_memory(struct mapping *mapping, unsigned long long phys,
size_t sz)
static void *map_memory_with_prot(struct mapping *mapping,
unsigned long long phys, size_t sz, int prot)
{
void *v;
unsigned long long page_size;
Expand All @@ -114,7 +121,7 @@ static const void *map_memory(struct mapping *mapping, unsigned long long phys,
phys);
}

v = mmap(NULL, mapping->virt_size, PROT_READ, MAP_SHARED, mem_fd,
v = mmap(NULL, mapping->virt_size, prot, MAP_SHARED, mem_fd,
phys - mapping->offset);

if (v == MAP_FAILED) {
Expand All @@ -132,6 +139,14 @@ static const void *map_memory(struct mapping *mapping, unsigned long long phys,
return mapping_virt(mapping);
}

/* Convenience helper for the common case of read-only mappings. */
static const void *map_memory(struct mapping *mapping, unsigned long long phys,
size_t sz)
{
return map_memory_with_prot(mapping, phys, sz, PROT_READ);
}


/* Returns 0 on success, < 0 on error. mapping is cleared if successful. */
static int unmap_memory(struct mapping *mapping)
{
Expand Down Expand Up @@ -327,6 +342,10 @@ static int parse_cbtable_entries(const struct mapping *table_mapping)
parse_cbmem_ref((struct lb_cbmem_ref *)lbr_p);
continue;
}
case LB_TAG_TSC_INFO:
debug(" Found TSC info.\n");
tsc_freq_khz = ((struct lb_tsc_info *)lbr_p)->freq_khz;
continue;
case LB_TAG_FORWARD: {
int ret;
/*
Expand Down Expand Up @@ -529,6 +548,22 @@ static void print_norm(u64 v)
}
}

static uint64_t timestamp_get(uint64_t table_tick_freq_mhz)
{
#if defined(__i386__) || defined(__x86_64__)
uint64_t tsc = __rdtsc();

/* No tick frequency specified means raw TSC values. */
if (!table_tick_freq_mhz)
return tsc;

if (tsc_freq_khz)
return tsc * table_tick_freq_mhz * 1000 / tsc_freq_khz;
#endif
die("Don't know how to obtain timestamps on this platform.\n");
return 0;
}

static const char *timestamp_name(uint32_t id)
{
for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
Expand All @@ -538,6 +573,15 @@ static const char *timestamp_name(uint32_t id)
return "<unknown>";
}

static uint32_t timestamp_enum_name_to_id(const char *name)
{
for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
if (!strcmp(timestamp_ids[i].enum_name, name))
return timestamp_ids[i].id;
}
return 0;
}

static uint64_t timestamp_print_parseable_entry(uint32_t id, uint64_t stamp,
uint64_t prev_stamp)
{
Expand Down Expand Up @@ -765,6 +809,38 @@ static void dump_timestamps(enum timestamps_print_type output_type)
free(sorted_tst_p);
}

/* add a timestamp entry */
static void timestamp_add_now(uint32_t timestamp_id)
{
struct timestamp_table *tst_p;
struct mapping timestamp_mapping;

if (timestamps.tag != LB_TAG_TIMESTAMPS) {
die("No timestamps found in coreboot table.\n");
}

tst_p = map_memory_with_prot(&timestamp_mapping, timestamps.cbmem_addr,
timestamps.size, PROT_READ | PROT_WRITE);
if (!tst_p)
die("Unable to map timestamp table\n");

/*
* Note that coreboot sizes the cbmem entry in the table according to
* max_entries, so it's OK to just add more entries if there's room.
*/
if (tst_p->num_entries >= tst_p->max_entries) {
die("Not enough space to add timestamp.\n");
} else {
int64_t time =
timestamp_get(tst_p->tick_freq_mhz) - tst_p->base_time;
tst_p->entries[tst_p->num_entries].entry_id = timestamp_id;
tst_p->entries[tst_p->num_entries].entry_stamp = time;
tst_p->num_entries += 1;
}

unmap_memory(&timestamp_mapping);
}

/* dump the tcpa log table */
static void dump_tcpa_log(void)
{
Expand Down Expand Up @@ -1260,6 +1336,7 @@ static void print_usage(const char *name, int exit_code)
" -t | --timestamps: print timestamp information\n"
" -T | --parseable-timestamps: print parseable timestamps\n"
" -S | --stacked-timestamps: print stacked timestamps (e.g. for flame graph tools)\n"
" -a | --add-timestamp ID: append timestamp with ID\n"
" -L | --tcpa-log print TCPA log\n"
" -V | --verbose: verbose (debugging) output\n"
" -v | --version: print the version\n"
Expand Down Expand Up @@ -1397,6 +1474,7 @@ int main(int argc, char** argv)
unsigned int rawdump_id = 0;
int max_loglevel = BIOS_NEVER;
int print_unknown_logs = 1;
uint32_t timestamp_id = 0;

int opt, option_index = 0;
static struct option long_options[] = {
Expand All @@ -1410,14 +1488,15 @@ int main(int argc, char** argv)
{"timestamps", 0, 0, 't'},
{"parseable-timestamps", 0, 0, 'T'},
{"stacked-timestamps", 0, 0, 'S'},
{"add-timestamp", required_argument, 0, 'a'},
{"hexdump", 0, 0, 'x'},
{"rawdump", required_argument, 0, 'r'},
{"verbose", 0, 0, 'V'},
{"version", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "c12B:CltTSLxVvh?r:",
while ((opt = getopt_long(argc, argv, "c12B:CltTSa:LxVvh?r:",
long_options, &option_index)) != EOF) {
switch (opt) {
case 'c':
Expand Down Expand Up @@ -1470,6 +1549,13 @@ int main(int argc, char** argv)
timestamp_type = TIMESTAMPS_PRINT_STACKED;
print_defaults = 0;
break;
case 'a':
print_defaults = 0;
timestamp_id = timestamp_enum_name_to_id(optarg);
/* Parse numeric value if name is unknown */
if (timestamp_id == 0)
timestamp_id = strtoul(optarg, NULL, 0);
break;
case 'V':
verbose = 1;
break;
Expand All @@ -1492,7 +1578,7 @@ int main(int argc, char** argv)
print_usage(argv[0], 1);
}

mem_fd = open("/dev/mem", O_RDONLY, 0);
mem_fd = open("/dev/mem", timestamp_id ? O_RDWR : O_RDONLY, 0);
if (mem_fd < 0) {
fprintf(stderr, "Failed to gain memory access: %s\n",
strerror(errno));
Expand Down Expand Up @@ -1579,6 +1665,9 @@ int main(int argc, char** argv)
if (print_rawdump)
dump_cbmem_raw(rawdump_id);

if (timestamp_id)
timestamp_add_now(timestamp_id);

if (print_defaults)
timestamp_type = TIMESTAMPS_PRINT_NORMAL;

Expand Down

0 comments on commit ecc165b

Please sign in to comment.