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 <ricardoq@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/56883 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
This commit is contained in:
parent
6db0f04fa2
commit
49a96a9463
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -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 <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 all the event logs\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"
|
||||
"-f, --file <filename> 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elog_list_events(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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Point to first event */
|
||||
event = (const struct event_header *)(data + sizeof(struct elog_header));
|
||||
return ELOGTOOL_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
while ((const void *)(event) < data + data_len) {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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 cmd_list(const struct buffer *buf)
|
||||
{
|
||||
const struct event_header *event;
|
||||
unsigned int count = 0;
|
||||
|
||||
/* Point to the first event */
|
||||
event = buffer_get(buf) + sizeof(struct elog_header);
|
||||
|
||||
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;
|
||||
|
||||
ret = elog_list_events(&buf);
|
||||
/* 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));
|
||||
|
||||
buffer_delete(&buf);
|
||||
return ret;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
used_data_size += event->length;
|
||||
event = elog_get_next_event(event);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <commonlib/bsd/elog.h>
|
||||
#include <vb2_api.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
#ifndef EVENTLOG_H_
|
||||
#define EVENTLOG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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_
|
||||
|
|
Loading…
Reference in New Issue