Ricardo Quesada has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/56883 )
Change subject: elogtool: add "clear" command ......................................................................
elogtool: add "clear" command
Adds "clear" command to cbfsutil/elogtool tool. "clear" clears the RW_ELOG using flashrom with ELOG_TYPE_EOL. And inserts a ELOG_TYPE_LOG_CLEAR event.
Usage: $ elogtool clear
BUG=b:172210863
Change-Id: Ia28a6eb34c82103ab078a0841b022e2e5e430585 Signed-off-by: Ricardo Quesada ricardoq@google.com --- M src/commonlib/bsd/elog.c M src/commonlib/bsd/include/commonlib/bsd/elog.h M util/cbfstool/elogtool.c M util/cbfstool/eventlog.c M util/cbfstool/eventlog.h 5 files changed, 166 insertions(+), 6 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/83/56883/1
diff --git a/src/commonlib/bsd/elog.c b/src/commonlib/bsd/elog.c index 6c927aa..27fe808 100644 --- a/src/commonlib/bsd/elog.c +++ b/src/commonlib/bsd/elog.c @@ -40,7 +40,55 @@ /* return the data associated to the event_header. */ const void *event_get_data(const struct event_header *event) { - // Pointing to the next event returns the data, since data is the first field - // right after the header. + /* + * Pointing to the next event returns the data, since data is the first + * field right after the header. + */ return (const void *)(&event[1]); } + +static inline uint8_t mybin2bcd(uint8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} + +/* Populate timestamp in event header with given time. */ +void elog_fill_timestamp(struct event_header *event, uint8_t sec, uint8_t min, + uint8_t hour, uint8_t mday, uint8_t mon, uint8_t year) +{ + event->second = mybin2bcd(sec); + event->minute = mybin2bcd(min); + event->hour = mybin2bcd(hour); + event->day = mybin2bcd(mday); + event->month = mybin2bcd(mon); + event->year = mybin2bcd(year % 100); + + /* Basic sanity check of expected ranges. */ + if (event->month > 0x12 || event->day > 0x31 || event->hour > 0x23 || + event->minute > 0x59 || event->second > 0x59) { + event->year = 0; + event->month = 0; + event->day = 0; + event->hour = 0; + event->minute = 0; + event->second = 0; + } +} + +/* Update the checksum at the last byte. */ +void elog_update_checksum(struct event_header *event, uint8_t checksum) +{ + uint8_t *event_data = (uint8_t *)event; + event_data[event->length - 1] = checksum; +} + +/* Simple byte checksum for events. */ +uint8_t elog_checksum_event(struct event_header *event) +{ + uint8_t index, checksum = 0; + uint8_t *data = (uint8_t *)event; + + for (index = 0; index < event->length; index++) + checksum += data[index]; + return checksum; +} diff --git a/src/commonlib/bsd/include/commonlib/bsd/elog.h b/src/commonlib/bsd/include/commonlib/bsd/elog.h index 0d41157..54e7ec4 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/elog.h +++ b/src/commonlib/bsd/include/commonlib/bsd/elog.h @@ -313,5 +313,9 @@ enum cb_err elog_verify_header(const struct elog_header *header); const struct event_header *elog_get_next_event(const struct event_header *event); const void *event_get_data(const struct event_header *event); +void elog_fill_timestamp(struct event_header *event, uint8_t sec, uint8_t min, + uint8_t hour, uint8_t mday, uint8_t mon, uint8_t year); +void elog_update_checksum(struct event_header *event, uint8_t checksum); +uint8_t elog_checksum_event(struct event_header *event);
#endif /* _COMMONLIB_BSD_ELOG_H_ */ diff --git a/util/cbfstool/elogtool.c b/util/cbfstool/elogtool.c index ba59eac..7a05aff 100644 --- a/util/cbfstool/elogtool.c +++ b/util/cbfstool/elogtool.c @@ -33,7 +33,9 @@ "USAGE:\n" "\t%s COMMAND [-f <filename>]\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 the contents RW_ELOG region from flash\n" + "\n" "ARGS\n" "-f, --file <filename> Input file that holds event log partition.\n" " If empty it will try to read from the RW_ELOG ROM region\n" @@ -41,8 +43,11 @@ 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. +/* + * 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 == NULL) { @@ -99,7 +104,7 @@ { int ret;
- // Returned buffer must be freed. + /* Returned buffer must be freed. */ struct buffer buf; ret = elog_read(filename, &buf); if (ret != 0) @@ -111,6 +116,74 @@ return ret; }
+/* + * Clears the elog events from buf, which is a valid RW_ELOG region. + * A LOG_CLEAR event is appended. + */ +static int elog_clear_events(struct buffer *buf) +{ + const struct event_header *event; + uint32_t used_data_size = 0; + void* data_offset; + size_t data_size; + + data_size = buf->size - sizeof(struct elog_header); + data_offset = buf->data + sizeof(struct elog_header); + + /* Must have at least size for event + data + checksum */ + if (data_size < sizeof(struct event_header) + sizeof(used_data_size) + 1) + return ELOGTOOL_EXIT_INVALID_ELOG_FORMAT; + + /* + * Calculate the size of the "used" buffer, needed for ELOG_TYPE_LOG_CLEAR. + * Then overwritte the entire buffer with ELOG_TYPE_EOL. + * Finally insert a LOG_CLEAR event into the buffer. + */ + event = (const struct event_header *)(data_offset); + + while ((const char *)(event) < buf->data + buf->size) { + if (event->type == ELOG_TYPE_EOL || event->length == 0) + break; + + used_data_size += event->length; + event = elog_get_next_event(event); + } + + memset(data_offset, ELOG_TYPE_EOL, data_size); + + eventlog_init_event(data_offset, ELOG_TYPE_LOG_CLEAR, + &used_data_size, sizeof(used_data_size)); + + return ELOGTOOL_EXIT_SUCCESS; +} + +static int cmd_clear(void) +{ + int ret; + + /* Returned buffer must be freed. */ + struct buffer buf; + ret = elog_read(NULL, &buf); + if (ret != 0) + return ret; + + ret = elog_clear_events(&buf); + if (ret != 0) { + buffer_delete(&buf); + return ret; + } + + if (flashrom_write(FLASHROM_PROGRAMMER_INTERNAL_AP, "RW_ELOG", + (void *)buf.data, buf.size) != VB2_SUCCESS) { + + fprintf(stderr, + "Failed to write to RW_ELOG region using flashrom\n"); + ret = ELOGTOOL_EXIT_BAD_INPUT_PATH; + } + + buffer_delete(&buf); + return ret; +}
int main(int argc, char **argv) { @@ -157,5 +230,8 @@ if (!strcmp(argv[optind], "list")) return cmd_list(input_file);
+ if (!strcmp(argv[optind], "clear")) + return cmd_clear(); + return ELOGTOOL_EXIT_SUCCESS; } diff --git a/util/cbfstool/eventlog.c b/util/cbfstool/eventlog.c index 2d99f92..67168c3 100644 --- a/util/cbfstool/eventlog.c +++ b/util/cbfstool/eventlog.c @@ -634,3 +634,31 @@ eventlog_printf_ignore_separator_once = 1; eventlog_printf("\n"); } + +/* + * Initializes the eventlog header with the given type and data, + * and calculates the checksum. + */ +void eventlog_init_event(struct event_header *event, uint8_t type, + const void *data, int data_size) +{ + time_t secs = time(NULL); + struct tm tm; + + 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) { + void *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))); +} diff --git a/util/cbfstool/eventlog.h b/util/cbfstool/eventlog.h index baecb21..670a64e 100644 --- a/util/cbfstool/eventlog.h +++ b/util/cbfstool/eventlog.h @@ -3,8 +3,12 @@ #ifndef EVENTLOG_H_ #define EVENTLOG_H_
+#include <stdint.h> + struct event_header;
void eventlog_print_event(const struct event_header *event, int count); +void eventlog_init_event(struct event_header* event, uint8_t type, + const void *data, int data_size);
#endif // EVENTLOG_H_