From 49a96a94634ca146969293c8f1ac12dba6bbd231 Mon Sep 17 00:00:00 2001 From: Ricardo Quesada Date: Mon, 16 Aug 2021 11:25:52 -0700 Subject: elogtool: add "clear" command Adds "clear" command to cbfsutil/elogtool tool. "clear" clears the RW_ELOG region by using either: * flashrom if no file is provided * or using file write if an input file is provided. The region is filled with ELOG_TYPE_EOL. And a ELOG_TYPE_LOG_CLEAR event is inserted. Additionally, it does a minor cleanup to command "list", like: * use buffer_end() * add "list" to the cmds struct * and make elog_read() very similar to elog_write() Usage: $ elogtool clear BUG=b:172210863 TEST=elogtool clear && elogtool list elogtool clear -f invalid.raw elogtool clear -f valid.raw Change-Id: Ia28a6eb34c82103ab078a0841b022e2e5e430585 Signed-off-by: Ricardo Quesada Reviewed-on: https://review.coreboot.org/c/coreboot/+/56883 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh Reviewed-by: Jack Rosenthal --- util/cbfstool/elogtool.c | 172 ++++++++++++++++++++++++++++++++++------------- util/cbfstool/eventlog.c | 40 +++++++++++ util/cbfstool/eventlog.h | 5 ++ 3 files changed, 172 insertions(+), 45 deletions(-) (limited to 'util/cbfstool') diff --git a/util/cbfstool/elogtool.c b/util/cbfstool/elogtool.c index ba59eac514..4ab8494209 100644 --- a/util/cbfstool/elogtool.c +++ b/util/cbfstool/elogtool.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause */ #include +#include #include #include #include @@ -16,9 +17,24 @@ enum elogtool_return { ELOGTOOL_EXIT_SUCCESS = 0, ELOGTOOL_EXIT_BAD_ARGS, - ELOGTOOL_EXIT_BAD_INPUT_PATH, + ELOGTOOL_EXIT_READ_ERROR, + ELOGTOOL_EXIT_WRITE_ERROR, ELOGTOOL_EXIT_NOT_ENOUGH_MEMORY, ELOGTOOL_EXIT_INVALID_ELOG_FORMAT, + ELOGTOOL_EXIT_NOT_ENOUGH_BUFFER_SPACE, +}; + +static int cmd_list(const struct buffer *); +static int cmd_clear(const struct buffer *); + +static const struct { + const char *name; + int (*func)(const struct buffer *buf); + /* Whether it requires to write the buffer back */ + bool write_back; +} cmds[] = { + {"list", cmd_list, false}, + {"clear", cmd_clear, true}, }; static struct option long_options[] = { @@ -33,57 +49,81 @@ static void usage(char *invoked_as) "USAGE:\n" "\t%s COMMAND [-f ]\n\n" "where, COMMAND is:\n" - " list = lists all the event logs in human readable format\n\n" + " list = lists all the event logs in human readable format\n" + " clear = clears all the event logs\n" + "\n" "ARGS\n" - "-f, --file Input file that holds event log partition.\n" - " If empty it will try to read from the RW_ELOG ROM region\n" + "-f, --file File that holds event log partition.\n" + " If empty it will try to read/write from/to\n" + " the " ELOG_RW_REGION_NAME " using flashrom.\n" "-h, --help Print this help\n", invoked_as); } -// If filename is empty, read RW_ELOG from flashrom. Otherwise read the RW_ELOG from a file. -// Buffer must be freed by caller. -static int elog_read(const char *filename, struct buffer *buffer) +/* + * If filename is empty, read RW_ELOG from flashrom. + * Otherwise read the RW_ELOG from a file. + * It fails if the ELOG header is invalid. + * On success, buffer must be freed by caller. + */ +static int elog_read(struct buffer *buffer, const char *filename) { if (filename == NULL) { uint8_t *buf; uint32_t buf_size; - if (flashrom_read(FLASHROM_PROGRAMMER_INTERNAL_AP, "RW_ELOG", &buf, &buf_size) - != VB2_SUCCESS) { + if (flashrom_read(FLASHROM_PROGRAMMER_INTERNAL_AP, ELOG_RW_REGION_NAME, + &buf, &buf_size) != VB2_SUCCESS) { fprintf(stderr, "Could not read RW_ELOG region using flashrom\n"); - return ELOGTOOL_EXIT_BAD_INPUT_PATH; + return ELOGTOOL_EXIT_READ_ERROR; } buffer_init(buffer, NULL, buf, buf_size); - } else { - if (buffer_from_file(buffer, filename) != 0) { - fprintf(stderr, "Could not read input file: %s\n", filename); - return ELOGTOOL_EXIT_BAD_INPUT_PATH; + } else if (buffer_from_file(buffer, filename) != 0) { + fprintf(stderr, "Could not read input file: %s\n", filename); + return ELOGTOOL_EXIT_READ_ERROR; + } + + if (elog_verify_header(buffer_get(buffer)) != CB_SUCCESS) { + fprintf(stderr, "FATAL: Invalid elog header\n"); + buffer_delete(buffer); + return ELOGTOOL_EXIT_INVALID_ELOG_FORMAT; + } + + return ELOGTOOL_EXIT_SUCCESS; +} + +/* + * If filename is NULL, it saves the buffer using flashrom. + * Otherwise, it saves the buffer in the given filename. + */ +static int elog_write(struct buffer *buf, const char *filename) +{ + if (filename == NULL) { + if (flashrom_write(FLASHROM_PROGRAMMER_INTERNAL_AP, ELOG_RW_REGION_NAME, + buffer_get(buf), buffer_size(buf)) != VB2_SUCCESS) { + fprintf(stderr, + "Failed to write to RW_ELOG region using flashrom\n"); + return ELOGTOOL_EXIT_WRITE_ERROR; } + return ELOGTOOL_EXIT_SUCCESS; } - return 0; + if (buffer_write_file(buf, filename) != 0) { + fprintf(stderr, "Failed to write to file %s\n", filename); + return ELOGTOOL_EXIT_WRITE_ERROR; + } + return ELOGTOOL_EXIT_SUCCESS; } -static int elog_list_events(const struct buffer *buf) +static int cmd_list(const struct buffer *buf) { const struct event_header *event; - const void *data; - uint32_t data_len; unsigned int count = 0; - data = buffer_get(buf); - data_len = buffer_size(buf); - - if (elog_verify_header(data) != CB_SUCCESS) { - fprintf(stderr, "FATAL: Invalid elog header\n"); - return ELOGTOOL_EXIT_INVALID_ELOG_FORMAT; - } - - /* Point to first event */ - event = (const struct event_header *)(data + sizeof(struct elog_header)); + /* Point to the first event */ + event = buffer_get(buf) + sizeof(struct elog_header); - while ((const void *)(event) < data + data_len) { + while ((const void *)(event) < buffer_end(buf)) { if (event->type == ELOG_TYPE_EOL || event->length == 0) break; @@ -95,27 +135,52 @@ static int elog_list_events(const struct buffer *buf) return ELOGTOOL_EXIT_SUCCESS; } -static int cmd_list(const char *filename) +/* + * Clears the elog events from the given buffer, which is a valid RW_ELOG region. + * A LOG_CLEAR event is appended. + */ +static int cmd_clear(const struct buffer *b) { - int ret; - - // Returned buffer must be freed. + const struct event_header *event; + uint32_t used_data_size = 0; struct buffer buf; - ret = elog_read(filename, &buf); - if (ret != 0) - return ret; + void *data_offset; + + /* Clone the buffer to avoid changing the offset of the original buffer */ + buffer_clone(&buf, b); + /* eventlog_init_event() expects buffer to point to the event */ + buffer_seek(&buf, sizeof(struct elog_header)); + + /* + * Calculate the size of the "used" buffer, needed for ELOG_TYPE_LOG_CLEAR. + * Then overwrite the entire buffer with ELOG_TYPE_EOL. + * Finally insert a LOG_CLEAR event into the buffer. + */ + event = data_offset = buffer_get(&buf); + while ((const void *)(event) < buffer_end(&buf)) { + if (event->type == ELOG_TYPE_EOL || event->length == 0) + break; - ret = elog_list_events(&buf); + used_data_size += event->length; + event = elog_get_next_event(event); + } - buffer_delete(&buf); - return ret; -} + memset(data_offset, ELOG_TYPE_EOL, buffer_size(&buf)); + + if (!eventlog_init_event(&buf, ELOG_TYPE_LOG_CLEAR, + &used_data_size, sizeof(used_data_size))) + return ELOGTOOL_EXIT_NOT_ENOUGH_BUFFER_SPACE; + return ELOGTOOL_EXIT_SUCCESS; +} int main(int argc, char **argv) { + char *filename = NULL; + struct buffer buf; + unsigned int i; int argflag; - char *input_file = NULL; + int ret; if (argc < 2) { usage(argv[0]); @@ -140,7 +205,7 @@ int main(int argc, char **argv) return ELOGTOOL_EXIT_BAD_ARGS; } - input_file = optarg; + filename = optarg; break; default: @@ -154,8 +219,25 @@ int main(int argc, char **argv) return ELOGTOOL_EXIT_BAD_ARGS; } - if (!strcmp(argv[optind], "list")) - return cmd_list(input_file); + /* Returned buffer must be freed. */ + ret = elog_read(&buf, filename); + if (ret) + return ret; - return ELOGTOOL_EXIT_SUCCESS; + for (i = 0; i < ARRAY_SIZE(cmds); i++) { + if (!strcmp(cmds[i].name, argv[optind])) { + ret = cmds[i].func(&buf); + break; + } + } + + if (i == ARRAY_SIZE(cmds)) + ret = ELOGTOOL_EXIT_BAD_ARGS; + + if (!ret && cmds[i].write_back) + ret = elog_write(&buf, filename); + + buffer_delete(&buf); + + return ret; } diff --git a/util/cbfstool/eventlog.c b/util/cbfstool/eventlog.c index 2d99f9237a..8b03686ecd 100644 --- a/util/cbfstool/eventlog.c +++ b/util/cbfstool/eventlog.c @@ -11,6 +11,7 @@ #include #include +#include "common.h" #include "valstr.h" #define PATH_PCI_BUS_SHIFT 8 @@ -634,3 +635,42 @@ void eventlog_print_event(const struct event_header *event, int count) eventlog_printf_ignore_separator_once = 1; eventlog_printf("\n"); } + +/* + * Initializes the eventlog header with the given type and data, + * and calculates the checksum. + * buffer_get() points to the event to be initialized. + * On success it returns 1, otherwise 0. + */ +int eventlog_init_event(const struct buffer *buf, uint8_t type, + const void *data, int data_size) +{ + struct event_header *event; + time_t secs = time(NULL); + struct tm tm; + + /* Must have at least size for data + checksum byte */ + if (buffer_size(buf) < (size_t)data_size + 1) + return 0; + + event = buffer_get(buf); + + event->type = type; + gmtime_r(&secs, &tm); + elog_fill_timestamp(event, tm.tm_sec, tm.tm_min, tm.tm_hour, + tm.tm_mday, tm.tm_mon, tm.tm_year); + + if (data && data_size) { + uint32_t *ptr = (uint32_t *)&event[1]; + memcpy(ptr, data, data_size); + } + + /* Header + data + checksum */ + event->length = sizeof(*event) + data_size + 1; + + /* Zero the checksum byte and then compute checksum */ + elog_update_checksum(event, 0); + elog_update_checksum(event, -(elog_checksum_event(event))); + + return 1; +} diff --git a/util/cbfstool/eventlog.h b/util/cbfstool/eventlog.h index baecb21db6..1911b77e82 100644 --- a/util/cbfstool/eventlog.h +++ b/util/cbfstool/eventlog.h @@ -3,8 +3,13 @@ #ifndef EVENTLOG_H_ #define EVENTLOG_H_ +#include + struct event_header; +struct buffer; void eventlog_print_event(const struct event_header *event, int count); +int eventlog_init_event(const struct buffer *buf, uint8_t type, + const void *data, int data_size); #endif // EVENTLOG_H_ -- cgit v1.2.3