summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/drivers/elog/Kconfig9
-rw-r--r--src/drivers/elog/Makefile.inc2
-rw-r--r--src/drivers/elog/gsmi.c117
-rw-r--r--src/include/elog.h4
4 files changed, 132 insertions, 0 deletions
diff --git a/src/drivers/elog/Kconfig b/src/drivers/elog/Kconfig
index 867416cc7729..7912ab8c79f7 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 32509c8ce0c8..79a7cc0f7f18 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 000000000000..dac1af413a25
--- /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 0542f68fe1c3..7d89f45d174b 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);