Ricardo Quesada has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/57397 )
Change subject: elogtool: add "add" command ......................................................................
elogtool: add "add" command
Adds "add" command to elogtool. This command allows adding elog events manually. It supports event type and, optionally, event data.
If the free buffer space is < 1/4 of the total space, it shrinks the buffer, making sure that ~1/4 of the free space is available.
BUG=b:172210863 TEST=./elogtool add 0x17 0101 ./elogtool add 0x18 Repeated the same tests on buffers that needed to be shrunk.
Change-Id: Ia6fdf4f951565f842d1bff52173811b52f617f66 Signed-off-by: Ricardo Quesada ricardoq@google.com --- M util/cbfstool/elogtool.c 1 file changed, 148 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/97/57397/1
diff --git a/util/cbfstool/elogtool.c b/util/cbfstool/elogtool.c index 6a4a707..a5861c8 100644 --- a/util/cbfstool/elogtool.c +++ b/util/cbfstool/elogtool.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause */
+#include <assert.h> #include <getopt.h> #include <stdbool.h> #include <stdint.h> @@ -28,6 +29,7 @@
static int cmd_list(const struct buffer *, int, char **); static int cmd_clear(const struct buffer *, int, char **); +static int cmd_add(const struct buffer *, int, char **);
static const struct { const char *name; @@ -37,6 +39,7 @@ } cmds[] = { {"list", cmd_list, false}, {"clear", cmd_clear, true}, + {"add", cmd_add, true}, };
static struct option long_options[] = { @@ -53,6 +56,7 @@ "where, COMMAND is:\n" " list lists all the event logs in human readable format\n" " clear clears all the event logs\n" + " add <event_type> [data] add an entry to the event log\n" "\n" "ARGS\n" "-f, --file <filename> File that holds event log partition.\n" @@ -134,6 +138,52 @@ return offset; }
+/* + * Shrinks buffer by ~bytes_to_shrink, then appends a LOG_CLEAR event, + * and finally fills the remaining area with EOL events. + */ +static int shrink_buffer(const struct buffer *buf, int bytes_to_shrink, int *out_offset) +{ + int hdr_size = sizeof(struct elog_header); + const struct event_header *event; + struct buffer copy; + uint32_t cleared; + int remaining; + void *data; + int offset; + + offset = sizeof(struct elog_header); + data = buffer_get(buf); + + /* Point to the first event */ + event = data + offset; + + while (offset < bytes_to_shrink) { + assert(!(event->type == ELOG_TYPE_EOL || event->length == 0) && + "Unexpected premature end of buffer"); + offset += event->length; + event = elog_get_next_event(event); + } + cleared = offset - sizeof(struct elog_header); + remaining = buffer_size(buf) - offset; + + /* Overlapping copy */ + memmove(data + hdr_size, data + hdr_size + cleared, remaining); + memset(data + hdr_size + remaining, ELOG_TYPE_EOL, cleared); + + offset = next_available_event_offset(buf); + + buffer_clone(©, buf); + buffer_seek(©, offset); + + if (!eventlog_init_event(©, ELOG_TYPE_LOG_CLEAR, &cleared, sizeof(cleared))) + return ELOGTOOL_EXIT_NOT_ENOUGH_BUFFER_SPACE; + + /* Points to the next available event: offset + header + data + checksum */ + *out_offset = offset + sizeof(struct event_header) + sizeof(cleared) + 1; + return ELOGTOOL_EXIT_SUCCESS; +} + static int cmd_list(const struct buffer *buf, int argc, char **argv) { const struct event_header *event; @@ -192,6 +242,104 @@ return ELOGTOOL_EXIT_SUCCESS; }
+static void cmd_add_usage(void) +{ + fprintf(stderr, "USAGE: add <event type> [event data]\n" + "\n" + "event type: is an hexadecimal number. Prefix '0x' is optional\n" + "event data: is a series of hexadecimal numbers. Must be:\n" + " - len(event data) %% 2 == 0\n" + " - len(event data) < 247\n" + "\n" + "Example:\n" + "add 0x16 1289AB # 1289AB is actually three bytes: 0x12, 0x89 and 0xab\n" + "add 19 # 19 is in hexa\n" + ); +} + +static int cmd_add_parse_args(int argc, char **argv, uint8_t *type, + uint8_t *data, int *data_size) +{ + char byte[3] = {0}; + char *endptr; + long value; + int len; + + if (argc != 1 && argc != 2) + return ELOGTOOL_EXIT_BAD_ARGS; + + /* Force type to be an hexa value to be consistent with the data values */ + value = strtol(argv[0], NULL, 16); + if (value > 255) + return ELOGTOOL_EXIT_BAD_ARGS; + + *type = value; + + if (argc == 1) + return ELOGTOOL_EXIT_SUCCESS; + + /* Assuming argc == 2 */ + len = strlen(argv[1]); + + /* Needs 2 bytes per number */ + if (len % 2 != 0) + return ELOGTOOL_EXIT_BAD_ARGS; + + *data_size = len / 2; + + /* Header + data + checksum can be bigger than max size */ + if (sizeof(struct elog_header) + *data_size + 1 > ELOG_MAX_EVENT_SIZE) + return ELOGTOOL_EXIT_BAD_ARGS; + + for (int i = 0; i < *data_size; i++) { + byte[0] = *argv[1]++; + byte[1] = *argv[1]++; + data[i] = strtol(byte, &endptr, 16); + if (endptr != &byte[2]) + return ELOGTOOL_EXIT_BAD_ARGS; + } + + return ELOGTOOL_EXIT_SUCCESS; +} + +/* Appends an elog entry to EventLog buffer. */ +static int cmd_add(const struct buffer *b, int argc, char **argv) +{ + uint8_t data[ELOG_MAX_EVENT_SIZE]; + struct buffer copy; + int data_size = 0; + uint8_t type = 0; + int threshold; + int next = 0; + int offset; + int ret; + + if (cmd_add_parse_args(argc, argv, &type, data, &data_size) != ELOGTOOL_EXIT_SUCCESS) { + cmd_add_usage(); + return ELOGTOOL_EXIT_BAD_ARGS; + } + + buffer_clone(©, b); + + threshold = buffer_size(b) * 3 / 4; + + offset = next_available_event_offset(b); + if (offset > threshold) { + /* Shrink ~ 1/4 of the size */ + ret = shrink_buffer(b, buffer_size(b) - threshold, &next); + if (ret != ELOGTOOL_EXIT_SUCCESS) + return ret; + buffer_seek(©, next); + } else { + buffer_seek(©, offset); + } + + if (!eventlog_init_event(©, type, data, data_size)) + return ELOGTOOL_EXIT_NOT_ENOUGH_BUFFER_SPACE; + + return ELOGTOOL_EXIT_SUCCESS; +} + int main(int argc, char **argv) { char *filename = NULL;