diff options
-rw-r--r-- | util/cbmem/cbmem.c | 103 |
1 files changed, 96 insertions, 7 deletions
diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c index 3af8a25020..748bc36453 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 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) @@ -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; @@ -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; @@ -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) { @@ -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) { @@ -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; /* @@ -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++) { @@ -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) { @@ -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(×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 @@ 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" @@ -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[] = { @@ -1410,6 +1488,7 @@ 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'}, @@ -1417,7 +1496,7 @@ int main(int argc, char** argv) {"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 @@ 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; @@ -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)); @@ -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; |