mirror of
https://github.com/fail0verflow/switch-coreboot.git
synced 2025-05-04 01:39:18 -04:00
UPSTREAM: 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. BUG=None BRANCH=None TEST=None 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> Change-Id: I44f1f6d6e4ef5458aca555c8a7d32cc8aae46502 Reviewed-on: https://chromium-review.googlesource.com/362139 Commit-Ready: Furquan Shaikh <furquan@chromium.org> Tested-by: Furquan Shaikh <furquan@chromium.org> Reviewed-by: Furquan Shaikh <furquan@chromium.org>
This commit is contained in:
parent
10eb56b4fb
commit
faa6eff28d
4 changed files with 302 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
×tamps_module,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct coreinfo_cat {
|
||||
|
|
290
payloads/coreinfo/timestamps_module.c
Normal file
290
payloads/coreinfo/timestamps_module.c
Normal file
|
@ -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 = ×tamps->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
|
Loading…
Add table
Reference in a new issue