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> Reviewed-on: https://review.coreboot.org/c/coreboot/+/57397 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com>
This commit is contained in:
parent
42e61fbb32
commit
683b294d3f
|
@ -1,5 +1,6 @@
|
||||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -14,6 +15,9 @@
|
||||||
|
|
||||||
#include "eventlog.h"
|
#include "eventlog.h"
|
||||||
|
|
||||||
|
/* Only refers to the data max size. The "-1" is the checksum byte */
|
||||||
|
#define ELOG_MAX_EVENT_DATA_SIZE (ELOG_MAX_EVENT_SIZE - sizeof(struct event_header) - 1)
|
||||||
|
|
||||||
enum elogtool_return {
|
enum elogtool_return {
|
||||||
ELOGTOOL_EXIT_SUCCESS = 0,
|
ELOGTOOL_EXIT_SUCCESS = 0,
|
||||||
ELOGTOOL_EXIT_BAD_ARGS,
|
ELOGTOOL_EXIT_BAD_ARGS,
|
||||||
|
@ -26,6 +30,7 @@ enum elogtool_return {
|
||||||
|
|
||||||
static int cmd_list(const struct buffer *);
|
static int cmd_list(const struct buffer *);
|
||||||
static int cmd_clear(const struct buffer *);
|
static int cmd_clear(const struct buffer *);
|
||||||
|
static int cmd_add(const struct buffer *);
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -35,8 +40,12 @@ static const struct {
|
||||||
} cmds[] = {
|
} cmds[] = {
|
||||||
{"list", cmd_list, false},
|
{"list", cmd_list, false},
|
||||||
{"clear", cmd_clear, true},
|
{"clear", cmd_clear, true},
|
||||||
|
{"add", cmd_add, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static char **cmd_argv; /* Command arguments */
|
||||||
|
static char *argv0; /* Used as invoked_as */
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"file", required_argument, 0, 'f'},
|
{"file", required_argument, 0, 'f'},
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
|
@ -45,12 +54,13 @@ static struct option long_options[] = {
|
||||||
|
|
||||||
static void usage(char *invoked_as)
|
static void usage(char *invoked_as)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "elogtool: list elog events\n\n"
|
fprintf(stderr, "elogtool: edit elog events\n\n"
|
||||||
"USAGE:\n"
|
"USAGE:\n"
|
||||||
"\t%s COMMAND [-f <filename>]\n\n"
|
"\t%s COMMAND [-f <filename>]\n\n"
|
||||||
"where, COMMAND is:\n"
|
"where, COMMAND is:\n"
|
||||||
" list lists all the event logs in human readable format\n"
|
" list lists all the event logs in human readable format\n"
|
||||||
" clear clears all the event logs\n"
|
" clear clears all the event logs\n"
|
||||||
|
" add <event_type> [event_data] add an entry to the event log\n"
|
||||||
"\n"
|
"\n"
|
||||||
"ARGS\n"
|
"ARGS\n"
|
||||||
"-f, --file <filename> File that holds event log partition.\n"
|
"-f, --file <filename> File that holds event log partition.\n"
|
||||||
|
@ -115,11 +125,14 @@ static int elog_write(struct buffer *buf, const char *filename)
|
||||||
return ELOGTOOL_EXIT_SUCCESS;
|
return ELOGTOOL_EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Buffer offset must point to a valid event_header struct */
|
||||||
static size_t next_available_event_offset(const struct buffer *buf)
|
static size_t next_available_event_offset(const struct buffer *buf)
|
||||||
{
|
{
|
||||||
const struct event_header *event;
|
const struct event_header *event;
|
||||||
struct buffer copy, *iter = ©
|
struct buffer copy, *iter = ©
|
||||||
|
|
||||||
|
assert(buffer_offset(buf) >= sizeof(struct elog_header));
|
||||||
|
|
||||||
buffer_clone(iter, buf);
|
buffer_clone(iter, buf);
|
||||||
|
|
||||||
while (buffer_size(iter) >= sizeof(struct event_header)) {
|
while (buffer_size(iter) >= sizeof(struct event_header)) {
|
||||||
|
@ -134,6 +147,52 @@ static size_t next_available_event_offset(const struct buffer *buf)
|
||||||
return buffer_offset(iter) - buffer_offset(buf);
|
return buffer_offset(iter) - buffer_offset(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shrinks buffer by ~bytes_to_shrink, then appends a LOG_CLEAR event,
|
||||||
|
* and finally fills the remaining area with EOL events.
|
||||||
|
* Buffer offset must point to a valid event_header struct.
|
||||||
|
*/
|
||||||
|
static int shrink_buffer(const struct buffer *buf, size_t bytes_to_shrink)
|
||||||
|
{
|
||||||
|
struct buffer copy, *iter = ©
|
||||||
|
const struct event_header *event;
|
||||||
|
uint32_t cleared;
|
||||||
|
int remaining;
|
||||||
|
uint8_t *data;
|
||||||
|
|
||||||
|
assert(buffer_offset(buf) >= sizeof(struct elog_header));
|
||||||
|
|
||||||
|
buffer_clone(©, buf);
|
||||||
|
|
||||||
|
/* Save copy of first event for later */
|
||||||
|
data = buffer_get(buf);
|
||||||
|
|
||||||
|
/* Set buffer offset pointing to the event right after bytes_to_shrink */
|
||||||
|
while (buffer_offset(iter) < bytes_to_shrink) {
|
||||||
|
event = buffer_get(iter);
|
||||||
|
assert(!(event->type == ELOG_TYPE_EOL || event->length == 0));
|
||||||
|
|
||||||
|
buffer_seek(iter, event->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Must be relative to the buffer offset */
|
||||||
|
cleared = buffer_offset(iter) - buffer_offset(buf);
|
||||||
|
remaining = buffer_size(iter);
|
||||||
|
|
||||||
|
/* Overlapping copy */
|
||||||
|
memmove(data, data + cleared, remaining);
|
||||||
|
memset(data + remaining, ELOG_TYPE_EOL, cleared);
|
||||||
|
|
||||||
|
/* Re-init copy to have a clean offset. Needed for init_event() */
|
||||||
|
buffer_clone(©, buf);
|
||||||
|
buffer_seek(©, next_available_event_offset(©));
|
||||||
|
|
||||||
|
if (!eventlog_init_event(©, ELOG_TYPE_LOG_CLEAR, &cleared, sizeof(cleared)))
|
||||||
|
return ELOGTOOL_EXIT_NOT_ENOUGH_BUFFER_SPACE;
|
||||||
|
|
||||||
|
return ELOGTOOL_EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_list(const struct buffer *buf)
|
static int cmd_list(const struct buffer *buf)
|
||||||
{
|
{
|
||||||
const struct event_header *event;
|
const struct event_header *event;
|
||||||
|
@ -182,6 +241,122 @@ static int cmd_clear(const struct buffer *buf)
|
||||||
return ELOGTOOL_EXIT_SUCCESS;
|
return ELOGTOOL_EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cmd_add_usage(void)
|
||||||
|
{
|
||||||
|
usage(argv0);
|
||||||
|
|
||||||
|
fprintf(stderr, "\n\nSpecific to ADD command:\n"
|
||||||
|
"\n"
|
||||||
|
"<event_type>: an hexadecimal number (0-255). Prefix '0x' is optional\n"
|
||||||
|
"[event_data]: (optional) a series of hexadecimal numbers. Must be:\n"
|
||||||
|
" - len(event_data) %% 2 == 0\n"
|
||||||
|
" - len(event_data) in bytes <= %ld\n"
|
||||||
|
"\n"
|
||||||
|
"Example:\n"
|
||||||
|
"%s add 0x16 01ABF0 # 01ABF0 is actually three bytes: 0x01, 0xAB and 0xF0\n"
|
||||||
|
"%s add 17 # 17 is in hexa\n",
|
||||||
|
ELOG_MAX_EVENT_DATA_SIZE, argv0, argv0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_add_parse_args(uint8_t *type, uint8_t *data, size_t *data_size)
|
||||||
|
{
|
||||||
|
char byte[3] = {0};
|
||||||
|
int argc = 0;
|
||||||
|
char *endptr;
|
||||||
|
long value;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
while (cmd_argv[argc] != NULL)
|
||||||
|
argc++;
|
||||||
|
|
||||||
|
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(cmd_argv[0], NULL, 16);
|
||||||
|
if (value > 255) {
|
||||||
|
fprintf(stderr, "Error: Event type should be between 0-0xff; "
|
||||||
|
"got: 0x%04lx\n", value);
|
||||||
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*type = value;
|
||||||
|
|
||||||
|
if (argc == 1)
|
||||||
|
return ELOGTOOL_EXIT_SUCCESS;
|
||||||
|
|
||||||
|
/* Assuming argc == 2 */
|
||||||
|
len = strlen(cmd_argv[1]);
|
||||||
|
|
||||||
|
/* Needs 2 bytes per number */
|
||||||
|
if (len % 2 != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: Event data length should be an even number; got: %d\n", len);
|
||||||
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data_size = len / 2;
|
||||||
|
|
||||||
|
if (*data_size > ELOG_MAX_EVENT_DATA_SIZE) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: Event data length (in bytes) should be <= %ld; got: %ld\n",
|
||||||
|
ELOG_MAX_EVENT_DATA_SIZE, *data_size);
|
||||||
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < *data_size; i++) {
|
||||||
|
byte[0] = *cmd_argv[1]++;
|
||||||
|
byte[1] = *cmd_argv[1]++;
|
||||||
|
data[i] = strtol(byte, &endptr, 16);
|
||||||
|
if (endptr != &byte[2]) {
|
||||||
|
fprintf(stderr, "Error: Event data length contains invalid data. "
|
||||||
|
"Only hexa digits are valid\n");
|
||||||
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ELOGTOOL_EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Appends an elog entry to EventLog buffer. */
|
||||||
|
static int cmd_add(const struct buffer *buf)
|
||||||
|
{
|
||||||
|
uint8_t data[ELOG_MAX_EVENT_DATA_SIZE];
|
||||||
|
size_t data_size = 0;
|
||||||
|
struct buffer copy;
|
||||||
|
uint8_t type = 0;
|
||||||
|
size_t next_event;
|
||||||
|
size_t threshold;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (cmd_add_parse_args(&type, data, &data_size) != ELOGTOOL_EXIT_SUCCESS) {
|
||||||
|
cmd_add_usage();
|
||||||
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_clone(©, buf);
|
||||||
|
buffer_seek(©, sizeof(struct elog_header));
|
||||||
|
|
||||||
|
threshold = buffer_size(©) * 3 / 4;
|
||||||
|
next_event = next_available_event_offset(©);
|
||||||
|
|
||||||
|
if (next_event > threshold) {
|
||||||
|
/* Shrink ~ 1/4 of the size */
|
||||||
|
ret = shrink_buffer(©, buffer_size(buf) - threshold);
|
||||||
|
if (ret != ELOGTOOL_EXIT_SUCCESS)
|
||||||
|
return ret;
|
||||||
|
next_event = next_available_event_offset(©);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_seek(©, next_event);
|
||||||
|
|
||||||
|
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)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *filename = NULL;
|
char *filename = NULL;
|
||||||
|
@ -234,6 +409,9 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(cmds); i++) {
|
for (i = 0; i < ARRAY_SIZE(cmds); i++) {
|
||||||
if (!strcmp(cmds[i].name, argv[optind])) {
|
if (!strcmp(cmds[i].name, argv[optind])) {
|
||||||
|
/* For commands that parse their own arguments. */
|
||||||
|
cmd_argv = &argv[optind+1];
|
||||||
|
argv0 = argv[0];
|
||||||
ret = cmds[i].func(&buf);
|
ret = cmds[i].func(&buf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue