Julius Werner has submitted this change. ( https://review.coreboot.org/c/coreboot/+/62639 )
Change subject: util/cbmem: add an option to append timestamp ......................................................................
util/cbmem: add an option to append timestamp
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 --- M util/cbmem/cbmem.c 1 file changed, 96 insertions(+), 7 deletions(-)
Approvals: build bot (Jenkins): Verified Julius Werner: Looks good to me, approved
diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c index 3af8a25..748bc36 100644 --- a/util/cbmem/cbmem.c +++ b/util/cbmem/cbmem.c @@ -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; @@ -57,6 +61,9 @@ 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) @@ -80,9 +87,9 @@ }
/* 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; @@ -91,8 +98,8 @@ }
/* 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; @@ -114,7 +121,7 @@ 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) { @@ -132,6 +139,14 @@ 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) { @@ -327,6 +342,10 @@ 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; /* @@ -529,6 +548,22 @@ } }
+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++) { @@ -538,6 +573,15 @@ 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) { @@ -765,6 +809,38 @@ 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(×tamp_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(×tamp_mapping); +} + /* dump the tcpa log table */ static void dump_tcpa_log(void) { @@ -1260,6 +1336,7 @@ " -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" @@ -1397,6 +1474,7 @@ 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[] = { @@ -1410,6 +1488,7 @@ {"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'}, @@ -1417,7 +1496,7 @@ {"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': @@ -1470,6 +1549,13 @@ 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; @@ -1492,7 +1578,7 @@ 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)); @@ -1579,6 +1665,9 @@ if (print_rawdump) dump_cbmem_raw(rawdump_id);
+ if (timestamp_id) + timestamp_add_now(timestamp_id); + if (print_defaults) timestamp_type = TIMESTAMPS_PRINT_NORMAL;