diff options
-rw-r--r-- | src/drivers/elog/Kconfig | 9 | ||||
-rw-r--r-- | src/drivers/elog/Makefile.inc | 2 | ||||
-rw-r--r-- | src/drivers/elog/gsmi.c | 117 | ||||
-rw-r--r-- | src/include/elog.h | 4 |
4 files changed, 132 insertions, 0 deletions
diff --git a/src/drivers/elog/Kconfig b/src/drivers/elog/Kconfig index 867416cc77..7912ab8c79 100644 --- a/src/drivers/elog/Kconfig +++ b/src/drivers/elog/Kconfig @@ -68,6 +68,15 @@ config ELOG_SHRINK_SIZE endif +config ELOG_GSMI + depends on ELOG && SPI_FLASH_SMM && SMM_TSEG + bool "SMI interface to write and clear event log" + default n + help + This interface is compatible with the linux kernel driver + available with CONFIG_GOOGLE_GSMI and can be used to write + kernel reset/shutdown messages to the event log. + config ELOG_BOOT_COUNT depends on ELOG bool "Maintain a monotonic boot number in CMOS" diff --git a/src/drivers/elog/Makefile.inc b/src/drivers/elog/Makefile.inc index 32509c8ce0..79a7cc0f7f 100644 --- a/src/drivers/elog/Makefile.inc +++ b/src/drivers/elog/Makefile.inc @@ -1,4 +1,6 @@ ramstage-$(CONFIG_ELOG) += elog.c +smm-$(CONFIG_ELOG_GSMI) += elog.c gsmi.c + romstage-$(CONFIG_ELOG_BOOT_COUNT) += boot_count.c ramstage-$(CONFIG_ELOG_BOOT_COUNT) += boot_count.c diff --git a/src/drivers/elog/gsmi.c b/src/drivers/elog/gsmi.c new file mode 100644 index 0000000000..dac1af413a --- /dev/null +++ b/src/drivers/elog/gsmi.c @@ -0,0 +1,117 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include <arch/io.h> +#include <console/console.h> +#include <cpu/x86/smm.h> +#include <elog.h> + +#define GSMI_RET_SUCCESS 0x00 +#define GSMI_RET_INVALID_PARAMETER 0x82 +#define GSMI_RET_UNSUPPORTED 0x83 + +#define GSMI_CMD_SET_EVENT_LOG 0x08 +#define GSMI_CMD_CLEAR_EVENT_LOG 0x09 +#define GSMI_CMD_HANDSHAKE_TYPE 0xc1 + +#define GSMI_HANDSHAKE_NONE 0x7f +#define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD + +struct gsmi_set_eventlog_param { + u32 data_ptr; + u32 data_len; + u32 type; +} __attribute__ ((packed)); + +struct gsmi_set_eventlog_type1 { + u16 type; + u32 instance; +} __attribute__ ((packed)); + +struct gsmi_clear_eventlog_param { + u32 percentage; + u32 data_type; +} __attribute__ ((packed)); + +/* Param is usually EBX, ret in EAX */ +u32 gsmi_exec(u8 command, u32 *param) +{ + struct gsmi_set_eventlog_param *sel; + struct gsmi_set_eventlog_type1 *type1; + struct gsmi_clear_eventlog_param *cel; + u32 ret = GSMI_RET_UNSUPPORTED; + + switch (command) { + case GSMI_CMD_HANDSHAKE_TYPE: + /* Used by kernel to verify basic SMI functionality */ + printk(BIOS_DEBUG, "GSMI Handshake\n"); + ret = GSMI_HANDSHAKE_NONE; + break; + + case GSMI_CMD_SET_EVENT_LOG: + /* Look for a type1 event */ + sel = (struct gsmi_set_eventlog_param *)(*param); + if (!sel) + break; + + /* Make sure the input is usable */ + if (sel->type != 1 && sel->data_ptr != 0 && + sel->data_len != sizeof(struct gsmi_set_eventlog_type1)) + break; + + /* Event structure within the data buffer */ + type1 = (struct gsmi_set_eventlog_type1 *)(sel->data_ptr); + if (!type1) + break; + + printk(BIOS_DEBUG, "GSMI Set Event Log " + "(type=0x%x instance=0x%x)\n", + type1->type, type1->instance); + + if (type1->type == GSMI_LOG_ENTRY_TYPE_KERNEL) { + /* Special case for linux kernel shutdown reason */ + elog_add_event_dword(ELOG_TYPE_OS_EVENT, + type1->instance); + } else { + /* Add other events that may be used for testing */ + elog_add_event_dword(type1->type, type1->instance); + } + ret = GSMI_RET_SUCCESS; + break; + + case GSMI_CMD_CLEAR_EVENT_LOG: + /* Get paramter buffer even though we don't use it */ + cel = (struct gsmi_clear_eventlog_param *)(*param); + if (!cel) + break; + + printk(BIOS_DEBUG, "GSMI Clear Event Log (%u%% type=%u)\n", + cel->percentage, cel->data_type); + + if (elog_clear() == 0) + ret = GSMI_RET_SUCCESS; + break; + + default: + printk(BIOS_DEBUG, "GSMI Unknown: 0x%02x\n", command); + break; + } + + return ret; +} diff --git a/src/include/elog.h b/src/include/elog.h index 0542f68fe1..7d89f45d17 100644 --- a/src/include/elog.h +++ b/src/include/elog.h @@ -115,6 +115,10 @@ extern void elog_add_event_dword(u8 event_type, u32 data); extern void elog_add_event_wake(u8 source, u32 instance); extern int elog_smbios_write_type15(unsigned long *current, int handle); +#if CONFIG_ELOG_GSMI +extern u32 gsmi_exec(u8 command, u32 *param); +#endif + #if CONFIG_ELOG_BOOT_COUNT u32 boot_count_read(void); u32 boot_count_increment(void); |