| /* SPDX-License-Identifier: BSD-3-Clause */ |
| |
| #include <getopt.h> |
| #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, |
| ELOGTOOL_EXIT_BAD_INPUT_PATH, |
| ELOGTOOL_EXIT_NOT_ENOUGH_MEMORY, |
| ELOGTOOL_EXIT_INVALID_ELOG_FORMAT, |
| }; |
| |
| 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" |
| " list = lists all the event logs in human readable format\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" |
| "-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 == NULL) { |
| uint8_t *buf; |
| uint32_t buf_size; |
| |
| if (flashrom_read(FLASHROM_PROGRAMMER_INTERNAL_AP, "RW_ELOG", &buf, &buf_size) |
| != VB2_SUCCESS) { |
| fprintf(stderr, "Could not read RW_ELOG region using flashrom\n"); |
| return ELOGTOOL_EXIT_BAD_INPUT_PATH; |
| } |
| 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; |
| } |
| } |
| |
| 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) { |
| fprintf(stderr, "FATAL: Invalid elog header\n"); |
| return ELOGTOOL_EXIT_INVALID_ELOG_FORMAT; |
| } |
| |
| /* Point to first event */ |
| event = (const struct event_header *)(data + sizeof(struct elog_header)); |
| |
| while ((const void *)(event) < data + data_len) { |
| 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; |
| } |
| |
| static int cmd_list(const char *filename) |
| { |
| int ret; |
| |
| // Returned buffer must be freed. |
| struct buffer buf; |
| ret = elog_read(filename, &buf); |
| if (ret != 0) |
| return ret; |
| |
| ret = elog_list_events(&buf); |
| |
| buffer_delete(&buf); |
| return ret; |
| } |
| |
| |
| int main(int argc, char **argv) |
| { |
| int argflag; |
| char *input_file = NULL; |
| |
| 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; |
| } |
| |
| input_file = optarg; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /* At least one command must be available. */ |
| if (optind >= argc) { |
| usage(argv[0]); |
| return ELOGTOOL_EXIT_BAD_ARGS; |
| } |
| |
| if (!strcmp(argv[optind], "list")) |
| return cmd_list(input_file); |
| |
| return ELOGTOOL_EXIT_SUCCESS; |
| } |