blob: 5633a88df10c0a3b2ef8a11b54554c78f8e3d95d [file] [log] [blame]
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fmap.h>
#include <stdlib.h>
#include <inttypes.h>
#include <valstr.h>
#include "intf/mmio.h"
#include "lib/eeprom.h"
#include "lib/elog.h"
#include "lib/eventlog.h"
#include "lib/smbios.h"
#include "lib/string.h"
#include "mosys/alloc.h"
#include "mosys/kv_pair.h"
#include "mosys/log.h"
#include "mosys/output.h"
#include "mosys/platform.h"
/*
* elog_verify_header - verify and validate if header is a valid Google Event
* Log header.
*
* @header: pointer to elog header to verify
*
* return < 0 if invalid, 0 if valid
*/
int elog_verify_header(struct elog_header *header)
{
if (header == NULL)
return -1;
if (header->elog_magic != ELOG_MAGIC)
return -1;
if (header->elog_size != sizeof(*header))
return -1;
if (header->elog_version != ELOG_VERSION)
return -1;
return 0;
}
/*
* elog_print_type - add the type of the entry to the kv_pair
*
* @intf: platform interface used for low level hardware access
* @entry: the smbios log entry to get type information
* @kv: kv_pair structure to add type information to
*
* Returns 0 on failure, 1 on success.
*/
int elog_print_type(struct platform_intf *intf, struct smbios_log_entry *entry,
struct kv_pair *kv)
{
const char *type;
const struct valstr elog_event_types[] = {
{ ELOG_TYPE_OS_EVENT, "Kernel Event" },
{ ELOG_TYPE_OS_BOOT, "OS Boot" },
{ ELOG_TYPE_EC_EVENT, "EC Event" },
{ ELOG_TYPE_POWER_FAIL, "Power Fail" },
{ ELOG_TYPE_SUS_POWER_FAIL, "SUS Power Fail" },
{ ELOG_TYPE_PWROK_FAIL, "PWROK Fail" },
{ ELOG_TYPE_SYS_PWROK_FAIL, "SYS PWROK Fail" },
{ ELOG_TYPE_POWER_ON, "Power On" },
{ ELOG_TYPE_POWER_BUTTON, "Power Button" },
{ ELOG_TYPE_POWER_BUTTON_OVERRIDE, "Power Button Override" },
{ ELOG_TYPE_RESET_BUTTON, "Reset Button" },
{ ELOG_TYPE_SYSTEM_RESET, "System Reset" },
{ ELOG_TYPE_RTC_RESET, "RTC Reset" },
{ ELOG_TYPE_TCO_RESET, "TCO Reset" },
{ ELOG_TYPE_ACPI_ENTER, "ACPI Enter" },
{ ELOG_TYPE_ACPI_WAKE, "ACPI Wake" },
{ ELOG_TYPE_WAKE_SOURCE, "Wake Source" },
{ ELOG_TYPE_CROS_DEVELOPER_MODE, "Chrome OS Developer Mode" },
{ ELOG_TYPE_CROS_RECOVERY_MODE, "Chrome OS Recovery Mode" },
{ ELOG_TYPE_MANAGEMENT_ENGINE, "Management Engine" },
{ ELOG_TYPE_MANAGEMENT_ENGINE_EXT, "Management Engine Extra" },
{ ELOG_TYPE_LAST_POST_CODE, "Last post code in previous boot" },
{ 0x0, NULL },
};
type = smbios_get_event_type_string(entry);
if (type == NULL) {
type = val2str_default(entry->type, elog_event_types, NULL);
}
if (type != NULL) {
kv_pair_add(kv, "type", type);
return 1;
}
/* Indicate unknown type in value pair */
kv_pair_add(kv, "type", "Unknown");
kv_pair_fmt(kv, "value", "0x%02x", entry->type);
return 1;
}
/*
* elog_print_data - add the data associated with the entry to the kv_pair
*
* @intf: platform interface used for low level hardware access
* @entry: the smbios log entry to get the data information
* @kv: kv_pair structure to add data to
*
* Returns 0 on failure, 1 on success.
*/
int elog_print_data(struct platform_intf *intf, struct smbios_log_entry *entry,
struct kv_pair *kv)
{
static struct valstr os_events[] = {
{ ELOG_OS_EVENT_CLEAN, "Clean Shutdown" },
{ ELOG_OS_EVENT_NMIWDT, "NMI Watchdog" },
{ ELOG_OS_EVENT_PANIC, "Panic" },
{ ELOG_OS_EVENT_OOPS, "Oops" },
{ ELOG_OS_EVENT_DIE, "Die" },
{ ELOG_OS_EVENT_MCE, "MCE" },
{ ELOG_OS_EVENT_SOFTWDT, "Software Watchdog" },
{ ELOG_OS_EVENT_MBE, "Multi-bit Error" },
{ ELOG_OS_EVENT_TRIPLE, "Triple Fault" },
{ ELOG_OS_EVENT_THERMAL, "Critical Thermal Threshold" },
{ 0, NULL },
};
static struct valstr wake_source_types[] = {
{ ELOG_WAKE_SOURCE_PCIE, "PCI Express" },
{ ELOG_WAKE_SOURCE_PME, "PCI PME" },
{ ELOG_WAKE_SOURCE_PME_INTERNAL, "Internal PME" },
{ ELOG_WAKE_SOURCE_RTC, "RTC Alarm" },
{ ELOG_WAKE_SOURCE_GPIO, "GPIO" },
{ ELOG_WAKE_SOURCE_SMBUS, "SMBALERT" },
{ 0, NULL },
};
static struct valstr ec_event_types[] = {
{ EC_EVENT_LID_CLOSED, "Lid Closed" },
{ EC_EVENT_LID_OPEN, "Lid Open" },
{ EC_EVENT_POWER_BUTTON, "Power Button" },
{ EC_EVENT_AC_CONNECTED, "AC Connected" },
{ EC_EVENT_AC_DISCONNECTED, "AC Disconnected" },
{ EC_EVENT_BATTERY_LOW, "Battery Low" },
{ EC_EVENT_BATTERY_CRITICAL, "Battery Critical" },
{ EC_EVENT_BATTERY, "Battery" },
{ EC_EVENT_THERMAL_THRESHOLD, "Thermal Threshold" },
{ EC_EVENT_THERMAL_OVERLOAD, "Thermal Overload" },
{ EC_EVENT_THERMAL, "Thermal" },
{ EC_EVENT_USB_CHARGER, "USB Charger" },
{ EC_EVENT_KEY_PRESSED, "Key Pressed" },
{ EC_EVENT_INTERFACE_READY, "Host Interface Ready" },
{ EC_EVENT_KEYBOARD_RECOVERY, "Keyboard Recovery" },
{ EC_EVENT_THERMAL_SHUTDOWN,
"Thermal Shutdown in previous boot" },
{ EC_EVENT_BATTERY_SHUTDOWN,
"Battery Shutdown in previous boot" },
{ 0, NULL },
};
static struct valstr cros_recovery_reasons[] = {
{ VBNV_RECOVERY_LEGACY, "Legacy Utility" },
{ VBNV_RECOVERY_RO_MANUAL, "Recovery Button Pressed" },
{ VBNV_RECOVERY_RO_INVALID_RW, "RW Failed Signature Check" },
{ VBNV_RECOVERY_RO_S3_RESUME, "S3 Resume Failed" },
{ VBNV_RECOVERY_RO_TPM_ERROR, "TPM Error in RO Firmware" },
{ VBNV_RECOVERY_RO_SHARED_DATA,
"Shared Data Error in RO Firmware" },
{ VBNV_RECOVERY_RO_TEST_S3, "Test Error from S3 Resume()" },
{ VBNV_RECOVERY_RO_TEST_LFS,
"Test Error from LoadFirmwareSetup()" },
{ VBNV_RECOVERY_RO_TEST_LF,
"Test Errofr from LoadFirmware()" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_0, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_1, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_2, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_3, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_4, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_5, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_6, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_7, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_8, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_9, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_A, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_B, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_C, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_D, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_E, "" },
{ VBNV_RECOVERY_RO_INVALID_RW_CHECK_F, "" },
{ VBNV_RECOVERY_RO_FIRMWARE, "Firmware Boot Failure" },
{ VBNV_RECOVERY_RO_TPM_REBOOT, "Recovery Mode TPM Reboot" },
{ VBNV_RECOVERY_EC_SOFTWARE_SYNC,
"EC Software Sync Error" },
{ VBNV_RECOVERY_EC_UNKNOWN_IMAGE,
"Unable to determine active EC image" },
{ VBNV_RECOVERY_RO_UNSPECIFIED,
"Unknown Error in RO Firmware" },
{ VBNV_RECOVERY_RW_DEV_SCREEN,
"User Requested from Developer Screen" },
{ VBNV_RECOVERY_RW_NO_OS, "No OS Kernel Detected" },
{ VBNV_RECOVERY_RW_INVALID_OS,
"OS Kernel Failed Signature Check" },
{ VBNV_RECOVERY_RW_TPM_ERROR, "TPM Error in RW Firmware" },
{ VBNV_RECOVERY_RW_DEV_MISMATCH,
"RW Dev Firmware but not Dev Mode" },
{ VBNV_RECOVERY_RW_SHARED_DATA,
"Shared Data Error in RW Firmware" },
{ VBNV_RECOVERY_RW_TEST_LK, "Test Error from LoadKernel()" },
{ VBNV_RECOVERY_RW_NO_DISK, "No Bootable Disk Found" },
{ VBNV_RECOVERY_RW_UNSPECIFIED,
"Unknown Error in RW Firmware" },
{ VBNV_RECOVERY_KE_DM_VERITY, "DM-Verity Error" },
{ VBNV_RECOVERY_KE_UNSPECIFIED, "Unknown Error in Kernel" },
{ VBNV_RECOVERY_US_TEST, "Test from User Mode" },
{ VBNV_RECOVERY_US_UNSPECIFIED, "Unknown Error in User Mode" },
{ 0, NULL },
};
static struct valstr me_path_types[] = {
{ ELOG_ME_PATH_NORMAL, "Normal" },
{ ELOG_ME_PATH_NORMAL, "S3 Wake" },
{ ELOG_ME_PATH_ERROR, "Error" },
{ ELOG_ME_PATH_RECOVERY, "Recovery" },
{ ELOG_ME_PATH_DISABLED, "Disabled" },
{ ELOG_ME_PATH_FW_UPDATE, "Firmware Update" },
{ 0, NULL },
};
switch (entry->type) {
case SMBIOS_EVENT_TYPE_LOGCLEAR:
{
uint16_t *bytes = (void *)&entry->data[0];
kv_pair_fmt(kv, "bytes", "%u", *bytes);
break;
}
case SMBIOS_EVENT_TYPE_BOOT:
{
uint32_t *count = (void *)&entry->data[0];
kv_pair_fmt(kv, "count", "%u", *count);
break;
}
case ELOG_TYPE_LAST_POST_CODE:
{
uint16_t *code = (void *)&entry->data[0];
kv_pair_fmt(kv, "code", "0x%02x", *code);
break;
}
case ELOG_TYPE_OS_EVENT:
{
uint32_t *event = (void *)&entry->data[0];
kv_pair_add(kv, "event", val2str(*event, os_events));
break;
}
case ELOG_TYPE_ACPI_ENTER:
case ELOG_TYPE_ACPI_WAKE:
{
uint8_t *state = (void *)&entry->data[0];
kv_pair_fmt(kv, "state", "S%u", *state);
break;
}
case ELOG_TYPE_WAKE_SOURCE:
{
struct elog_wake_source *event;
event = (void *)&entry->data[0];
kv_pair_add(kv, "source",
val2str(event->source, wake_source_types));
kv_pair_fmt(kv, "instance", "%u", event->instance);
break;
}
case ELOG_TYPE_EC_EVENT:
{
uint8_t *event = (void *)&entry->data[0];
kv_pair_add(kv, "event", val2str(*event, ec_event_types));
break;
}
case ELOG_TYPE_CROS_RECOVERY_MODE:
{
uint8_t *reason = (void *)&entry->data[0];
kv_pair_add(kv, "reason",
val2str(*reason, cros_recovery_reasons));
break;
}
case ELOG_TYPE_MANAGEMENT_ENGINE:
{
uint8_t *path = (void *)&entry->data[0];
kv_pair_add(kv, "path", val2str(*path, me_path_types));
}
default:
return 0;
}
return 0;
}
static int elog_print_entry_me_ext(struct platform_intf *intf,
struct smbios_log_entry *entry, int id,
const char *desc, const char *value)
{
struct kv_pair *kv;
if (!desc || !value)
return 0;
kv = kv_pair_new();
kv_pair_fmt(kv, "entry", "%d", id);
smbios_eventlog_print_timestamp(intf, entry, kv);
kv_pair_add(kv, "type", desc);
kv_pair_add(kv, "value", value);
kv_pair_print(kv);
kv_pair_free(kv);
return 1;
}
/*
* elog_print_multi_me_ext - print management engine extended events
*
* @intf: platform interface
* @entry: log entry
* @start_id: starting entry id
*
* returns 0 to indicate nothing was printed
* returns >0 to indicate how many events were printed
* returns <0 to indicate error
*/
static int elog_print_multi_me_ext(struct platform_intf *intf,
struct smbios_log_entry *entry,
int start_id)
{
int num_msg = 0;
const struct valstr *me_state_values;
const struct elog_event_data_me_extended *me = (void *)&entry->data[0];
const struct valstr me_cws_values[] = {
{ 0x00, "Reset" },
{ 0x01, "Initializing" },
{ 0x02, "Recovery" },
{ 0x05, "Normal" },
{ 0x06, "Platform Disable Wait" },
{ 0x07, "OP State Transition" },
{ 0x08, "Invalid CPU Plugged In" },
{ 0xFF, NULL }
};
const struct valstr me_opstate_values[] = {
{ 0x00, "Preboot" },
{ 0x01, "M0 with UMA" },
{ 0x04, "M3 without UMA" },
{ 0x05, "M0 without UMA" },
{ 0x06, "Bring up" },
{ 0x07, "M0 without UMA but with error" },
{ 0xFF, NULL }
};
const struct valstr me_opmode_values[] = {
{ 0x02, "Debug" },
{ 0x03, "Soft Temporary Disable" },
{ 0x04, "Security Override via Jumper" },
{ 0x05, "Security Override via MEI Message" },
{ 0xFF, NULL }
};
const struct valstr me_error_values[] = {
{ 0x01, "Uncategorized Failure" },
{ 0x03, "Image Failure" },
{ 0x04, "Debug Failure" },
{ 0xFF, NULL }
};
const struct valstr me_progress_values[] = {
{ 0x00, "ROM Phase" },
{ 0x01, "BUP Phase" },
{ 0x02, "uKernel Phase" },
{ 0x03, "Policy Module" },
{ 0x04, "Module Loading" },
{ 0x05, "Unknown" },
{ 0x06, "Host Communication" },
{ 0xFF, NULL }
};
const struct valstr me_pmevent_values[] = {
{ 0x00, "Clean Moff->Mx wake" },
{ 0x01, "Moff->Mx wake after an error" },
{ 0x02, "Clean global reset" },
{ 0x03, "Global reset after an error" },
{ 0x04, "Clean Intel ME reset" },
{ 0x05, "Intel ME reset due to exception" },
{ 0x06, "Pseudo-global reset" },
{ 0x07, "S0/M0->Sx/M3" },
{ 0x08, "Sx/M3->S0/M0" },
{ 0x09, "Non-power cycle reset" },
{ 0x0a, "Power cycle reset through M3" },
{ 0x0b, "Power cycle reset through Moff" },
{ 0x0c, "Sx/Mx->Sx/Moff" },
{ 0xFF, NULL }
};
const struct valstr me_progress_rom_values[] = {
{ 0x00, "BEGIN" },
{ 0x06, "DISABLE" },
{ 0xFF, NULL }
};
const struct valstr me_progress_bup_values[] = {
{ 0x00, "Initialization starts" },
{ 0x01, "Disable the host wake event" },
{ 0x04, "Flow determination start process" },
{ 0x08, "Error reading/matching VSCC table in the descriptor" },
{ 0x0a, "Check to see if straps say ME DISABLED" },
{ 0x0b, "Timeout waiting for PWROK" },
{ 0x0d, "Possibly handle BUP manufacturing override strap" },
{ 0x11, "Bringup in M3" },
{ 0x12, "Bringup in M0" },
{ 0x13, "Flow detection error" },
{ 0x15, "M3 clock switching error" },
{ 0x18, "M3 kernel load" },
{ 0x1c, "T34 missing - cannot program ICC" },
{ 0x1f, "Waiting for DID BIOS message" },
{ 0x20, "Waiting for DID BIOS message failure" },
{ 0x21, "DID reported an error" },
{ 0x22, "Enabling UMA" },
{ 0x23, "Enabling UMA error" },
{ 0x24, "Sending DID Ack to BIOS" },
{ 0x25, "Sending DID Ack to BIOS error" },
{ 0x26, "Switching clocks in M0" },
{ 0x27, "Switching clocks in M0 error" },
{ 0x28, "ME in temp disable" },
{ 0x32, "M0 kernel load" },
{ 0xFF, NULL }
};
const struct valstr me_progress_policy_values[] = {
{ 0x00, "Entery into Policy Module" },
{ 0x03, "Received S3 entry" },
{ 0x04, "Received S4 entry" },
{ 0x05, "Received S5 entry" },
{ 0x06, "Received UPD entry" },
{ 0x07, "Received PCR entry" },
{ 0x08, "Received NPCR entry" },
{ 0x09, "Received host wake" },
{ 0x0a, "Received AC<>DC switch" },
{ 0x0b, "Received DRAM Init Done" },
{ 0x0c, "VSCC Data not found for flash device" },
{ 0x0d, "VSCC Table is not valid" },
{ 0x0e, "Flash Partition Boundary is outside address space" },
{ 0x0f, "ME cannot access the chipset descriptor region" },
{ 0x10, "Required VSCC values for flash parts do not match" },
{ 0xFF, NULL }
};
const struct valstr me_progress_hostcomm_values[] = {
{ 0x00, "Host Communication Established" },
{ 0xFF, NULL }
};
/* Current Working State */
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME Working State",
val2str_default(me->current_working_state,
me_cws_values, NULL));
/* Current Operation State */
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME Operation State",
val2str_default(me->operation_state,
me_opstate_values, NULL));
/* Current Operation Mode */
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME Operation Mode",
val2str_default(me->operation_mode,
me_opmode_values, NULL));
/* Progress Phase */
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME Progress Phase",
val2str_default(me->progress_code,
me_progress_values, NULL));
/* Power Management Event */
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME PM Event",
val2str_default(me->current_pmevent,
me_pmevent_values, NULL));
/* Error Code (if non-zero) */
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME Error Code",
val2str_default(me->error_code,
me_error_values, NULL));
switch (me->progress_code) {
case ELOG_ME_PHASE_ROM:
me_state_values = me_progress_rom_values;
break;
case ELOG_ME_PHASE_BRINGUP:
me_state_values = me_progress_bup_values;
break;
case ELOG_ME_PHASE_POLICY:
me_state_values = me_progress_policy_values;
break;
case ELOG_ME_PHASE_HOST:
me_state_values = me_progress_hostcomm_values;
break;
}
num_msg += elog_print_entry_me_ext(
intf, entry, start_id + num_msg, "ME Phase State",
val2str_default(me->current_state,
me_state_values, NULL));
return num_msg;
}
/*
* elog_print_multi - print multiple entries for an event
*
* @intf: platform interface
* @entry: log entry
* @start_id: starting entry id
*
* returns 0 to indicate nothing was printed
* returns >0 to indicate how many events were printed
* returns <0 to indicate error
*/
int elog_print_multi(struct platform_intf *intf,
struct smbios_log_entry *entry, int start_id)
{
switch (entry->type) {
case ELOG_TYPE_MANAGEMENT_ENGINE_EXT:
/* Management Engine Extended Event */
return elog_print_multi_me_ext(intf, entry, start_id);
}
return 0;
}
/*
* elog_verify - verify the contents of the SMBIOS log entry.
*
* @intf: platform interface used for low level hardware access
* @entry: the smbios log entry to verify
*
* returns 0 failure of verification, 1 if verification is sucessful
*/
int elog_verify(struct platform_intf *intf, struct smbios_log_entry *entry)
{
char *data;
char checksum;
int i;
checksum = 0;
data = (void *)entry;
for (i = 0; i < entry->length; i++) {
checksum += data[i];
}
return checksum == 0;
}
/*
* elog_fetch_from_smbios - fetch the eventlog from the SMBIOS tables.
*
* @intf: platform interface used for low level hardware access
* @data: pointer to the fetched contents of the event log
* @length: pointer to the length of the event log
* @header_offset: offset of the header in the event log
* @data_offset: offset of the first event in the event log
*
* returns -1 on failure, 0 on success
*/
int elog_fetch_from_smbios(struct platform_intf *intf, uint8_t **data,
size_t *length, off_t *header_offset,
off_t *data_offset)
{
struct smbios_table table;
if (smbios_find_table(intf, SMBIOS_TYPE_LOG, 0, &table,
SMBIOS_LEGACY_ENTRY_BASE,
SMBIOS_LEGACY_ENTRY_LEN) < 0) {
lprintf(LOG_WARNING, "Unable to find SMBIOS eventlog table.\n");
return -1;
}
if (table.data.log.header_format != ELOG_HEADER_FORMAT)
return -1;
/* Only support memory mapped I/O access */
if (table.data.log.method != SMBIOS_LOG_METHOD_TYPE_MEM)
return -1;
*length = table.data.log.length;
*data = mosys_malloc(*length);
*header_offset = table.data.log.header_start;
*data_offset = table.data.log.data_start;
if (mmio_read(intf, table.data.log.address.mem, *length, *data) < 0)
return -1;
return 0;
}
/*
* elog_fetch_from_flash - fetch the eventlog from the RW_ELOG area of flash.
*
* @intf: platform interface used for low level hardware access
* @data: pointer to the fetched contents of the event log
* @length: pointer to the length of the event log
* @header_offset: offset of the header in the event log
* @data_offset: offset of the first event in the event log
*
* returns -1 on failure, 0 on success
*/
int elog_fetch_from_flash(struct platform_intf *intf, uint8_t **data,
size_t *length, off_t *header_offset,
off_t *data_offset)
{
struct fmap *fmap;
struct fmap_area *fmap_area;
struct eeprom *host;
host = &intf->cb->eeprom->eeprom_list[0];
while (host->name && strcmp(host->name, "host_firmware"))
host++;
if (!host->name) {
lprintf(LOG_WARNING, "No host firmware found.\n");
return -1;
}
fmap = host->device->get_map(intf, host);
if (!fmap)
return -1;
fmap_area = fmap_find_area(fmap, "RW_ELOG");
if (!fmap_area) {
lprintf(LOG_WARNING, "No event log area in flash.\n");
return -1;
}
*data = malloc(fmap_area->size);
if (host->device->read(intf, host, fmap_area->offset,
fmap_area->size, *data)) {
free(*data);
lprintf(LOG_WARNING, "Failed to read event log from flash.\n");
return -1;
}
*length = fmap_area->size;
*header_offset = 0;
*data_offset = sizeof(struct elog_header);
return 0;
}