summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonello Dettori <dev@dettori.io>2016-07-08 11:14:40 +0200
committerMartin Roth <martinroth@google.com>2016-07-20 22:09:32 +0200
commit4b1668fd121c12dcddc28b227e5cd93fe67c9a33 (patch)
tree0c1d38faf5ce9e4cf999f5b30a16b9711ce5ca23
parent613702b5dc61e32b8a7ee2967cb5f8c64e7783ce (diff)
coreinfo: Add support to read timestamps
Read timestamps from the last boot sequence and display the information as if using cbmem -t. Tested on QEMU with a SeaBIOS payload. Change-Id: I44f1f6d6e4ef5458aca555c8a7d32cc8aae46502 Signed-off-by: Antonello Dettori <dev@dettori.io> Reviewed-on: https://review.coreboot.org/15600 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth <martinroth@google.com>
-rw-r--r--payloads/coreinfo/Kconfig6
-rw-r--r--payloads/coreinfo/Makefile4
-rw-r--r--payloads/coreinfo/coreinfo.c4
-rw-r--r--payloads/coreinfo/timestamps_module.c290
4 files changed, 302 insertions, 2 deletions
diff --git a/payloads/coreinfo/Kconfig b/payloads/coreinfo/Kconfig
index e71599ca9a..fd4c1b4a22 100644
--- a/payloads/coreinfo/Kconfig
+++ b/payloads/coreinfo/Kconfig
@@ -104,4 +104,10 @@ config MODULE_CBFS
help
This option will increase the ELF file size by ca. 1440 bytes.
+config MODULE_TIMESTAMPS
+ bool "Enable the coreboot timestamps module"
+ default y
+ help
+ This option will increase the ELF file size by ca. 4200 bytes.
+
endmenu
diff --git a/payloads/coreinfo/Makefile b/payloads/coreinfo/Makefile
index 7c2e636ded..2fe2398f19 100644
--- a/payloads/coreinfo/Makefile
+++ b/payloads/coreinfo/Makefile
@@ -53,10 +53,10 @@ HAVE_LIBPAYLOAD := $(wildcard $(LIBPAYLOAD_OBJ)/lib/libpayload.a)
LIBPAYLOAD_CONFIG ?= configs/defconfig-tinycurses
OBJCOPY ?= objcopy
-INCLUDES = -I$(coreinfo_obj) -include $(LIBPAYLOAD_OBJ)/include/kconfig.h
+INCLUDES = -I$(coreinfo_obj) -include $(LIBPAYLOAD_OBJ)/include/kconfig.h -I$(src)/../../src/commonlib/include
OBJECTS = cpuinfo_module.o cpuid.S.o pci_module.o coreboot_module.o \
nvram_module.o bootlog_module.o ramdump_module.o \
- multiboot_module.o cbfs_module.o coreinfo.o
+ multiboot_module.o cbfs_module.o timestamps_module.o coreinfo.o
OBJS = $(patsubst %,$(coreinfo_obj)/%,$(OBJECTS))
TARGET = $(coreinfo_obj)/coreinfo.elf
diff --git a/payloads/coreinfo/coreinfo.c b/payloads/coreinfo/coreinfo.c
index cb42df443e..3fb2f7296a 100644
--- a/payloads/coreinfo/coreinfo.c
+++ b/payloads/coreinfo/coreinfo.c
@@ -25,6 +25,7 @@ extern struct coreinfo_module nvram_module;
extern struct coreinfo_module bootlog_module;
extern struct coreinfo_module ramdump_module;
extern struct coreinfo_module cbfs_module;
+extern struct coreinfo_module timestamps_module;
struct coreinfo_module *system_modules[] = {
#if IS_ENABLED(CONFIG_MODULE_CPUINFO)
@@ -54,6 +55,9 @@ struct coreinfo_module *firmware_modules[] = {
#if IS_ENABLED(CONFIG_MODULE_CBFS)
&cbfs_module,
#endif
+#if IS_ENABLED(CONFIG_MODULE_TIMESTAMPS)
+ &timestamps_module,
+#endif
};
struct coreinfo_cat {
diff --git a/payloads/coreinfo/timestamps_module.c b/payloads/coreinfo/timestamps_module.c
new file mode 100644
index 0000000000..eedb3c9439
--- /dev/null
+++ b/payloads/coreinfo/timestamps_module.c
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the coreinfo project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "coreinfo.h"
+#include <commonlib/timestamp_serialized.h>
+
+#if IS_ENABLED(CONFIG_MODULE_TIMESTAMPS)
+
+#define LINES_SHOWN 19
+#define TAB_WIDTH 2
+
+/* Globals that are used for tracking screen state */
+static char *g_buf;
+static s32 g_line;
+static s32 g_lines_count;
+static s32 g_max_cursor_line;
+
+static unsigned long tick_freq_mhz;
+
+static const char *timestamp_name(uint32_t id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
+ if (timestamp_ids[i].id == id)
+ return timestamp_ids[i].name;
+ }
+
+ return "<unknown>";
+}
+
+static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)
+{
+ tick_freq_mhz = table_tick_freq_mhz;
+
+ /* Honor table frequency. */
+ if (tick_freq_mhz)
+ return;
+
+ tick_freq_mhz = lib_sysinfo.cpu_khz / 1000;
+
+ if (!tick_freq_mhz) {
+ fprintf(stderr, "Cannot determine timestamp tick frequency.\n");
+ exit(1);
+ }
+}
+
+static u64 arch_convert_raw_ts_entry(u64 ts)
+{
+ return ts / tick_freq_mhz;
+}
+
+static u32 char_width(char c, u32 cursor, u32 screen_width)
+{
+ if (c == '\n')
+ return screen_width - (cursor % screen_width);
+ else if (c == '\t')
+ return TAB_WIDTH;
+ else if (isprint(c))
+ return 1;
+
+ return 0;
+}
+
+static u32 calculate_chars_count(char *str, u32 str_len, u32 screen_width,
+ u32 screen_height)
+{
+ u32 i, count = 0;
+
+ for (i = 0; i < str_len; i++)
+ count += char_width(str[i], count, screen_width);
+
+ /* Ensure that 'count' can occupy at least the whole screen */
+ if (count < screen_width * screen_height)
+ count = screen_width * screen_height;
+
+ /* Pad to line end */
+ if (count % screen_width != 0)
+ count += screen_width - (count % screen_width);
+
+ return count;
+}
+
+/*
+ * This method takes an input buffer and sanitizes it for display, which means:
+ * - '\n' is converted to spaces until end of line
+ * - Tabs are converted to spaces of size TAB_WIDTH
+ * - Only printable characters are preserved
+ */
+static int sanitize_buffer_for_display(char *str, u32 str_len, char *out,
+ u32 out_len, u32 screen_width)
+{
+ u32 cursor = 0;
+ u32 i;
+
+ for (i = 0; i < str_len && cursor < out_len; i++) {
+ u32 width = char_width(str[i], cursor, screen_width);
+
+ if (width == 1)
+ out[cursor++] = str[i];
+ else if (width > 1)
+ while (width-- && cursor < out_len)
+ out[cursor++] = ' ';
+ }
+
+ /* Fill the rest of the out buffer with spaces */
+ while (cursor < out_len)
+ out[cursor++] = ' ';
+
+ return 0;
+}
+
+static uint64_t timestamp_print_entry(char *buffer, size_t size, uint32_t *cur,
+ uint32_t id, uint64_t stamp, uint64_t prev_stamp)
+{
+ const char *name;
+ uint64_t step_time;
+
+ name = timestamp_name(id);
+ step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
+
+ *cur += snprintf(buffer + *cur, size, "%4d: %-45s", id, name);
+ *cur += snprintf(buffer + *cur, size, "%llu",
+ arch_convert_raw_ts_entry(stamp));
+ if (prev_stamp) {
+ *cur += snprintf(buffer + *cur, size, " (");
+ *cur += snprintf(buffer + *cur, size, "%llu", step_time);
+ *cur += snprintf(buffer + *cur, size, ")");
+ }
+ *cur += snprintf(buffer + *cur, size, "\n");
+
+ return step_time;
+}
+
+static int timestamps_module_init(void)
+{
+ /* Make sure that lib_sysinfo is initialized */
+ int ret = lib_get_sysinfo();
+
+ if (ret)
+ return -1;
+
+ struct timestamp_table *timestamps = lib_sysinfo.tstamp_table;
+
+ if (timestamps == NULL)
+ return -1;
+
+ /* Extract timestamps information */
+ u64 base_time = timestamps->base_time;
+ u16 max_entries = timestamps->max_entries;
+ u32 n_entries = timestamps->num_entries;
+
+ timestamp_set_tick_freq(timestamps->tick_freq_mhz);
+
+ char *buffer;
+ u32 buff_cur = 0;
+ uint64_t prev_stamp;
+ uint64_t total_time;
+
+ /* Allocate a buffer big enough to contain all of the possible
+ * entries plus the other information (number entries, total time). */
+ buffer = malloc((max_entries + 4) * SCREEN_X * sizeof(char));
+
+ if (buffer == NULL)
+ return -3;
+
+ /* Write the content */
+ buff_cur += snprintf(buffer, SCREEN_X, "%d entries total:\n\n",
+ n_entries);
+
+ prev_stamp = 0;
+ timestamp_print_entry(buffer, SCREEN_X, &buff_cur, 0, base_time,
+ prev_stamp);
+ prev_stamp = base_time;
+
+ total_time = 0;
+ for (int i = 0; i < n_entries; i++) {
+ uint64_t stamp;
+ const struct timestamp_entry *tse = &timestamps->entries[i];
+
+ stamp = tse->entry_stamp + base_time;
+ total_time += timestamp_print_entry(buffer, SCREEN_X,
+ &buff_cur, tse->entry_id, stamp, prev_stamp);
+ prev_stamp = stamp;
+ }
+
+ buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "\nTotal Time: ");
+ buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "%llu", total_time);
+ buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "\n");
+
+ /* Calculate how many characters will be displayed on screen */
+ u32 chars_count = calculate_chars_count(buffer, buff_cur + 1,
+ SCREEN_X, LINES_SHOWN);
+
+ /* Sanity check, chars_count must be padded to full line */
+ if (chars_count % SCREEN_X != 0)
+ return -2;
+
+ g_lines_count = chars_count / SCREEN_X;
+ g_max_cursor_line = MAX(g_lines_count - 1 - LINES_SHOWN, 0);
+
+ g_buf = malloc(chars_count);
+ if (!g_buf)
+ return -3;
+
+ if (sanitize_buffer_for_display(buffer, buff_cur + 1, g_buf,
+ chars_count, SCREEN_X) < 0) {
+ free(buffer);
+ free(g_buf);
+ g_buf = NULL;
+ return -4;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+static int timestamps_module_redraw(WINDOW *win)
+{
+ print_module_title(win, "Coreboot Timestamps");
+
+ if (!g_buf)
+ return -1;
+
+ int x = 0, y = 0;
+ char *tmp = g_buf + g_line * SCREEN_X;
+
+ for (y = 0; y < LINES_SHOWN; y++) {
+ for (x = 0; x < SCREEN_X; x++) {
+ mvwaddch(win, y + 2, x, *tmp);
+ tmp++;
+ }
+ }
+
+ return 0;
+}
+
+static int timestamps_module_handle(int key)
+{
+ if (!g_buf)
+ return 0;
+
+ switch (key) {
+ case KEY_DOWN:
+ g_line++;
+ break;
+ case KEY_UP:
+ g_line--;
+ break;
+ case KEY_NPAGE: /* Page up */
+ g_line -= LINES_SHOWN;
+ break;
+ case KEY_PPAGE: /* Page down */
+ g_line += LINES_SHOWN;
+ break;
+ }
+
+ if (g_line < 0)
+ g_line = 0;
+
+ if (g_line > g_max_cursor_line)
+ g_line = g_max_cursor_line;
+
+ return 1;
+}
+
+struct coreinfo_module timestamps_module = {
+ .name = "Timestamps",
+ .init = timestamps_module_init,
+ .redraw = timestamps_module_redraw,
+ .handle = timestamps_module_handle,
+};
+
+#else
+
+struct coreinfo_module timestamps_module = {
+};
+
+#endif