Michał Żygowski has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/40344 )
Change subject: util/cbmem: implement event log parser ......................................................................
util/cbmem: implement event log parser
Signed-off-by: Michał Żygowski michal.zygowski@3mdeb.com Change-Id: Ia24bc23e99f12ca4bd57505f83351a2907f81a33 --- M util/cbmem/cbmem.c 1 file changed, 213 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/44/40344/1
diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c index f8da7da..d318acb 100644 --- a/util/cbmem/cbmem.c +++ b/util/cbmem/cbmem.c @@ -32,6 +32,7 @@ #include <assert.h> #include <regex.h> #include <commonlib/cbmem_id.h> +#include <commonlib/elog.h> #include <commonlib/timestamp_serialized.h> #include <commonlib/tcpa_log_serialized.h> #include <commonlib/coreboot_tables.h> @@ -1080,6 +1081,207 @@ unmap_memory(&coverage_mapping); }
+ +static int bcd_to_decimal(uint8_t x) +{ + return x - 6 * (x >> 4); +} + +static char *get_name_from_table(const struct elog_event_name *event_table, u8 event) +{ + size_t table_size; + + if (event_table == elog_event_to_name) + table_size = ARRAY_SIZE(elog_event_to_name); + else if (event_table == elog_ec_event_to_name) + table_size = ARRAY_SIZE(elog_ec_event_to_name); + else if (event_table == elog_wake_event_to_name) + table_size = ARRAY_SIZE(elog_wake_event_to_name); + else if (event_table == elog_ec_device_event_to_name) + table_size = ARRAY_SIZE(elog_ec_device_event_to_name); + else if (event_table == mrc_upd_slots) + table_size = ARRAY_SIZE(mrc_upd_slots); + else + return "Unknown"; + + for (size_t i = 0; i < table_size; i++) { + if (event_table[i].event_type == event) + return event_table[i].event_name; + } + return "Unknown event type"; +} + +static void print_raw(uint8_t *start, size_t end) +{ + size_t i, j; + for (i = 0; i < end; i += 16) { + for (j = 0; j < 16; j++) { + if (i + j >= end) + break; + printf("%02x ", start[i+j]); + } + printf("\n"); + } +} + +static void print_elog_date(struct event_header *ev_header) +{ + printf("[20%02d-%02d-%02d %02d:%02d:%02d] ", + bcd_to_decimal(ev_header->year), + bcd_to_decimal(ev_header->month), + bcd_to_decimal(ev_header->day), + bcd_to_decimal(ev_header->hour), + bcd_to_decimal(ev_header->minute), + bcd_to_decimal(ev_header->second)); +} + +struct cbmem_elog { + u32 magic; + u8 version; + u8 header_size; + u8 reserved[2]; +} __attribute__ ((__packed__)); + +static void dump_elog(void) +{ + uint64_t start; + size_t size, offset; + const void *elog; + uint8_t *elog_data; + size_t elog_size; + struct mapping elog_mapping; + struct cbmem_elog *cb_elog; + struct event_header *ev_header; + struct elog_event_data_wake *wake_ev; + struct elog_event_data_me_extended *me_ext_ev; + struct elog_event_mem_cache_update *mrc_event; + struct elog_event_extended_event *elog_ext; + + if (find_cbmem_entry(CBMEM_ID_ELOG, &start, &size)) { + fprintf(stderr, "ELOG area not found\n"); + return; + } + + elog = map_memory(&elog_mapping, start, size); + if (!elog) + die("Unable to map elog area.\n"); + + cb_elog = (struct cbmem_elog *)elog; + + printf("Dumping ELOG data...\n"); + printf("ELOG version %d\n", cb_elog->version); + + offset = cb_elog->header_size; + + while (*(uint8_t *)(elog + offset) != ELOG_TYPE_EOL) { + ev_header = (struct event_header *)(elog + offset); + print_elog_date(ev_header); + printf("%s", get_name_from_table(elog_event_to_name, ev_header->type)); + + elog_data = (uint8_t *) (elog + offset + sizeof(struct event_header)); + elog_size = ev_header->length - sizeof(struct event_header) - 1; + + if (ev_header->type < 0x80) { + offset += ev_header->length; + if (elog_size != 0) { + printf(". Data: "); + print_raw(elog_data, elog_size); + } else { + printf("\n"); + } + continue; + } + + switch (ev_header->type) { + case ELOG_TYPE_EC_EVENT: + printf(": %s\n", + get_name_from_table(elog_ec_event_to_name, *elog_data)); + break; + case ELOG_TYPE_ACPI_ENTER: + case ELOG_TYPE_ACPI_WAKE: + printf(": S%d\n", bcd_to_decimal(*elog_data)); + break; + case ELOG_TYPE_WAKE_SOURCE: + wake_ev = (struct elog_event_data_wake *) + (elog + offset + sizeof(struct event_header)); + printf(": %s instance %d\n", + get_name_from_table(elog_wake_event_to_name, wake_ev->source), + wake_ev->instance); + break; + case ELOG_TYPE_MEM_CACHE_UPDATE: + mrc_event = (struct elog_event_mem_cache_update *) + (elog + offset + sizeof(struct event_header)); + printf(": slot %s, status %s\n", + get_name_from_table(mrc_upd_slots, mrc_event->slot), + mrc_event->status ? "FAIL" : "SUCCESS"); + break; + case ELOG_TYPE_EC_DEVICE_EVENT: + printf(": %s\n", get_name_from_table(elog_ec_device_event_to_name, + *elog_data)); + break; + case ELOG_TYPE_EXTENDED_EVENT: + elog_ext = (struct elog_event_extended_event *) + (elog + offset + sizeof(struct event_header)); + if (elog_ext->event_type == ELOG_SLEEP_PENDING_PM1_WAKE) + printf(": Pending PM1 wake (%08d)\n", + elog_ext->event_complement); + else if (elog_ext->event_type == ELOG_SLEEP_PENDING_GPE0_WAKE) + printf(": Pending GPE0 wake (%08d)\n", + elog_ext->event_complement); + else + printf(": Unknown (%08d)\n", elog_ext->event_complement); + break; + case ELOG_TYPE_CROS_RECOVERY_MODE: + printf(": reason %02x\n", *elog_data); + break; + case ELOG_TYPE_INTRUDER_DETECTION: + if (*elog_data == ELOG_CASE_OPENED) + printf(": Case opened\n"); + else if (*elog_data == ELOG_CASE_CLOSED) + printf(": Case closed\n"); + else + printf(": Unknown (%02x)\n", *elog_data); + break; + case ELOG_TYPE_MANAGEMENT_ENGINE: + if (*elog_data < 6) + printf(": %s\n", me_bios_paths[*elog_data]); + else + printf("Unknown\n"); + break; + case ELOG_TYPE_MANAGEMENT_ENGINE_EXT: + me_ext_ev = (struct elog_event_data_me_extended *) + (elog + offset + sizeof(struct event_header)); + printf(":\n" + "\t\t\tCurrent working state: %02x\n" + "\t\t\tOperation state: %02x\n" + "\t\t\tOperation mode: %02x\n" + "\t\t\tError code: %02x\n" + "\t\t\tProgress code: %02x\n" + "\t\t\tCurrent PM event: %02x\n" + "\t\t\tCurrent state: %02x\n", + me_ext_ev->current_working_state, + me_ext_ev->operation_state, + me_ext_ev->operation_mode, + me_ext_ev->error_code, + me_ext_ev->progress_code, + me_ext_ev->current_pmevent, + me_ext_ev->current_state); + break; + default: + if (elog_size != 0) { + printf(": Unknown. Data: "); + print_raw(elog_data, elog_size); + } else + printf("\n"); + break; + } /* switch */ + + offset += ev_header->length; + } /* while */ + + unmap_memory(&elog_mapping); +} + static void print_version(void) { printf("cbmem v%s -- ", CBMEM_VERSION); @@ -1101,6 +1303,7 @@ " -c | --console: print cbmem console\n" " -1 | --oneboot: print cbmem console for last boot only\n" " -C | --coverage: dump coverage information\n" + " -e | --elog: dump event log\n" " -l | --list: print cbmem table of contents\n" " -x | --hexdump: print hexdump of cbmem area\n" " -r | --rawdump ID: print rawdump of specific ID (in hex) of cbtable\n" @@ -1234,6 +1437,7 @@ int print_defaults = 1; int print_console = 0; int print_coverage = 0; + int print_elog = 0; int print_list = 0; int print_hexdump = 0; int print_rawdump = 0; @@ -1248,6 +1452,7 @@ {"console", 0, 0, 'c'}, {"oneboot", 0, 0, '1'}, {"coverage", 0, 0, 'C'}, + {"elog", 0, 0, 'e'}, {"list", 0, 0, 'l'}, {"tcpa-log", 0, 0, 'L'}, {"timestamps", 0, 0, 't'}, @@ -1259,13 +1464,17 @@ {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "c1CltTLxVvh?r:", + while ((opt = getopt_long(argc, argv, "c1CeltTLxVvh?r:", long_options, &option_index)) != EOF) { switch (opt) { case 'c': print_console = 1; print_defaults = 0; break; + case 'e': + print_elog = 1; + print_defaults = 0; + break; case '1': print_console = 1; one_boot_only = 1; @@ -1401,6 +1610,9 @@ if (print_coverage) dump_coverage();
+ if (print_elog) + dump_elog(); + if (print_list) dump_cbmem_toc();