2021-07-17 01:49:04 +02:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause */
|
|
|
|
|
|
|
|
#include <getopt.h>
|
2021-08-16 20:25:52 +02:00
|
|
|
#include <stdbool.h>
|
2021-07-17 01:49:04 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <commonlib/bsd/elog.h>
|
|
|
|
#include <flashrom.h>
|
|
|
|
|
|
|
|
#include "eventlog.h"
|
|
|
|
|
|
|
|
enum elogtool_return {
|
|
|
|
ELOGTOOL_EXIT_SUCCESS = 0,
|
|
|
|
ELOGTOOL_EXIT_BAD_ARGS,
|
2021-08-16 20:25:52 +02:00
|
|
|
ELOGTOOL_EXIT_READ_ERROR,
|
|
|
|
ELOGTOOL_EXIT_WRITE_ERROR,
|
2021-07-17 01:49:04 +02:00
|
|
|
ELOGTOOL_EXIT_NOT_ENOUGH_MEMORY,
|
|
|
|
ELOGTOOL_EXIT_INVALID_ELOG_FORMAT,
|
2021-08-16 20:25:52 +02:00
|
|
|
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},
|
2021-07-17 01:49:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"file", required_argument, 0, 'f'},
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
{NULL, 0, 0, 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
static void usage(char *invoked_as)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "elogtool: list elog events\n\n"
|
|
|
|
"USAGE:\n"
|
|
|
|
"\t%s COMMAND [-f <filename>]\n\n"
|
|
|
|
"where, COMMAND is:\n"
|
2021-08-16 20:25:52 +02:00
|
|
|
" list = lists all the event logs in human readable format\n"
|
|
|
|
" clear = clears all the event logs\n"
|
|
|
|
"\n"
|
2021-07-17 01:49:04 +02:00
|
|
|
"ARGS\n"
|
2021-08-16 20:25:52 +02:00
|
|
|
"-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"
|
2021-07-17 01:49:04 +02:00
|
|
|
"-h, --help Print this help\n",
|
|
|
|
invoked_as);
|
|
|
|
}
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
/*
|
|
|
|
* 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)
|
2021-07-17 01:49:04 +02:00
|
|
|
{
|
|
|
|
if (filename == NULL) {
|
|
|
|
uint8_t *buf;
|
|
|
|
uint32_t buf_size;
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
if (flashrom_read(FLASHROM_PROGRAMMER_INTERNAL_AP, ELOG_RW_REGION_NAME,
|
|
|
|
&buf, &buf_size) != VB2_SUCCESS) {
|
2021-07-17 01:49:04 +02:00
|
|
|
fprintf(stderr, "Could not read RW_ELOG region using flashrom\n");
|
2021-08-16 20:25:52 +02:00
|
|
|
return ELOGTOOL_EXIT_READ_ERROR;
|
2021-07-17 01:49:04 +02:00
|
|
|
}
|
|
|
|
buffer_init(buffer, NULL, buf, buf_size);
|
2021-08-16 20:25:52 +02:00
|
|
|
} 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;
|
2021-07-17 01:49:04 +02:00
|
|
|
}
|
2021-08-16 20:25:52 +02:00
|
|
|
return ELOGTOOL_EXIT_SUCCESS;
|
2021-07-17 01:49:04 +02:00
|
|
|
}
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
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;
|
2021-07-17 01:49:04 +02:00
|
|
|
}
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
static int cmd_list(const struct buffer *buf)
|
2021-07-17 01:49:04 +02:00
|
|
|
{
|
|
|
|
const struct event_header *event;
|
|
|
|
unsigned int count = 0;
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
/* Point to the first event */
|
|
|
|
event = buffer_get(buf) + sizeof(struct elog_header);
|
2021-07-17 01:49:04 +02:00
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
while ((const void *)(event) < buffer_end(buf)) {
|
2021-07-17 01:49:04 +02:00
|
|
|
if (event->type == ELOG_TYPE_EOL || event->length == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
eventlog_print_event(event, count);
|
|
|
|
event = elog_get_next_event(event);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ELOGTOOL_EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
/*
|
|
|
|
* 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)
|
2021-07-17 01:49:04 +02:00
|
|
|
{
|
2021-08-16 20:25:52 +02:00
|
|
|
const struct event_header *event;
|
|
|
|
uint32_t used_data_size = 0;
|
2021-07-17 01:49:04 +02:00
|
|
|
struct buffer buf;
|
2021-08-16 20:25:52 +02:00
|
|
|
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;
|
2021-07-17 01:49:04 +02:00
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
used_data_size += event->length;
|
|
|
|
event = elog_get_next_event(event);
|
|
|
|
}
|
2021-07-17 01:49:04 +02:00
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
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;
|
2021-07-17 01:49:04 +02:00
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
return ELOGTOOL_EXIT_SUCCESS;
|
|
|
|
}
|
2021-07-17 01:49:04 +02:00
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2021-08-16 20:25:52 +02:00
|
|
|
char *filename = NULL;
|
|
|
|
struct buffer buf;
|
|
|
|
unsigned int i;
|
2021-07-17 01:49:04 +02:00
|
|
|
int argflag;
|
2021-08-16 20:25:52 +02:00
|
|
|
int ret;
|
2021-07-17 01:49:04 +02:00
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
usage(argv[0]);
|
|
|
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int option_index;
|
|
|
|
argflag = getopt_long(argc, argv, "hf:", long_options, &option_index);
|
|
|
|
if (argflag == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (argflag) {
|
|
|
|
case 'h':
|
|
|
|
case '?':
|
|
|
|
usage(argv[0]);
|
|
|
|
return ELOGTOOL_EXIT_SUCCESS;
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
if (!optarg) {
|
|
|
|
usage(argv[0]);
|
|
|
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
|
|
|
}
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
filename = optarg;
|
2021-07-17 01:49:04 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* At least one command must be available. */
|
|
|
|
if (optind >= argc) {
|
|
|
|
usage(argv[0]);
|
|
|
|
return ELOGTOOL_EXIT_BAD_ARGS;
|
|
|
|
}
|
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
/* Returned buffer must be freed. */
|
|
|
|
ret = elog_read(&buf, filename);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2021-07-17 01:49:04 +02:00
|
|
|
|
2021-08-16 20:25:52 +02:00
|
|
|
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;
|
2021-07-17 01:49:04 +02:00
|
|
|
}
|