[coreboot-gerrit] Patch set updated for coreboot: soc/apollolake: Add common smihandler code

Hannah Williams (hannah.williams@intel.com) gerrit at coreboot.org
Tue May 17 02:55:16 CEST 2016


Hannah Williams (hannah.williams at intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/14615

-gerrit

commit c8b42c6bbe4b300c70e26ceba4a2581212206538
Author: Hannah Williams <hannah.williams at intel.com>
Date:   Wed May 4 18:15:49 2016 -0700

    soc/apollolake: Add common smihandler code
    
    Provide default handler for some SMI events. Provide the framework for
    extracting data from SMM Save State area for processors with SMM revision
    30100 and 30101.
    The SOC specific code should initialize southbridge_smi with event
    handlers. For SMM Save state handling, SOC code should implement
    get_smm_save_state_ops which initializes the SOC specific ops for SMM Save
    State handling.
    
    Change-Id: I0aefb6dbb2b1cac5961f9e43f4752b5929235df3
    Signed-off-by: Hannah Williams <hannah.williams at intel.com>
---
 src/include/elog.h                |   2 -
 src/soc/intel/common/Kconfig      |   4 +
 src/soc/intel/common/Makefile.inc |   2 +
 src/soc/intel/common/smi.h        | 138 ++++++++++++
 src/soc/intel/common/smihandler.c | 441 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 585 insertions(+), 2 deletions(-)

diff --git a/src/include/elog.h b/src/include/elog.h
index fa70155..b94a281 100644
--- a/src/include/elog.h
+++ b/src/include/elog.h
@@ -169,9 +169,7 @@ static inline int elog_smbios_write_type15(unsigned long *current,
 }
 #endif
 
-#if CONFIG_ELOG_GSMI
 extern u32 gsmi_exec(u8 command, u32 *param);
-#endif
 
 #if CONFIG_ELOG_BOOT_COUNT
 u32 boot_count_read(void);
diff --git a/src/soc/intel/common/Kconfig b/src/soc/intel/common/Kconfig
index 8b68aad..a4081ce 100644
--- a/src/soc/intel/common/Kconfig
+++ b/src/soc/intel/common/Kconfig
@@ -59,4 +59,8 @@ config MMA_BLOBS_PATH
 	depends on MMA
 	default "3rdparty/blobs/mainboard/$(MAINBOARDDIR)/mma"
 
+config SOC_INTEL_COMMON_SMI
+	bool
+	default n
+
 endif # SOC_INTEL_COMMON
diff --git a/src/soc/intel/common/Makefile.inc b/src/soc/intel/common/Makefile.inc
index a7218b7..588a0a8 100644
--- a/src/soc/intel/common/Makefile.inc
+++ b/src/soc/intel/common/Makefile.inc
@@ -15,6 +15,8 @@ ramstage-y += util.c
 ramstage-$(CONFIG_MMA) += mma.c
 ramstage-$(CONFIG_SOC_INTEL_COMMON_ACPI_WAKE_SOURCE) += acpi_wake_source.c
 
+smm-$(CONFIG_SOC_INTEL_COMMON_SMI) += smihandler.c
+
 # Create and add the MRC cache to the cbfs image
 ifneq ($(CONFIG_CHROMEOS),y)
 $(obj)/mrc.cache: $(obj)/config.h
diff --git a/src/soc/intel/common/smi.h b/src/soc/intel/common/smi.h
new file mode 100644
index 0000000..c6c192f
--- /dev/null
+++ b/src/soc/intel/common/smi.h
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2016 Intel Corporation.
+ *
+ * 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.
+ */
+
+#ifndef _INTEL_COMMON_SMI_H_
+#define _INTEL_COMMON_SMI_H_
+
+/*
+ * The register value is used with get_reg and set_reg
+ */
+enum smm_reg {
+	RAX,
+	RBX,
+	RCX,
+	RDX,
+	RSI,
+	RDI,
+	R8,
+	R9,
+	R10,
+	R11,
+	R12,
+	R13,
+	R14,
+	R15,
+	RSP,
+};
+
+
+struct	smm_save_state_ops {
+	/* return io_misc_info from SMM Save State Area */
+	uintptr_t (*get_io_misc_info)(void *state);
+
+	/* return value of the requested register from
+	 * SMM Save State Area
+	 */
+	uint64_t (*get_reg)(void *state, enum smm_reg reg);
+
+	void (*set_reg)(void *state, enum smm_reg reg, uint64_t val);
+};
+
+typedef void (*smi_handler_t)(void);
+
+/*
+ * SOC SMI Handler has to provide this structure which has methods to access
+ * the SOC specific SMM Save State Area
+ */
+const struct smm_save_state_ops *get_smm_save_state_ops(void);
+
+/*
+ * southbridge_smi should be defined inside SOC specific code and should have
+ * handlers for any SMI events that need to be handled. Default handlers
+ * for some SMI events are provided in soc/intel/common/smihandler.c
+ */
+extern const smi_handler_t southbridge_smi[32];
+
+/*
+ * This function should be implemented in SOC specific code to handle
+ * the SMI event on SLP_EN. The default functionality is provided in
+ * soc/intel/common/smihandler.c
+ */
+void southbridge_smi_sleep(void);
+
+/*
+ * This function should be implemented in SOC specific code to handle
+ * SMI_APM event. The default functionality is provided in
+ * soc/intel/common/smihandler.c
+ */
+void southbridge_smi_apmc(void);
+
+/*
+ * This function should be implemented in SOC specific code to handle
+ * SMI_PM1 event. The default functionality is provided in
+ * soc/intel/common/smihandler.c
+ */
+void southbridge_smi_pm1(void);
+
+/*
+ * This function should be implemented in SOC specific code to handle
+ * SMI_GPE0 event. The default functionality is provided in
+ * soc/intel/common/smihandler.c
+ */
+void southbridge_smi_gpe0(void);
+
+/*
+ * This function should be implemented in SOC specific code to handle
+ * SMI_TCO event. The default functionality is provided in
+ * soc/intel/common/smihandler.c
+ */
+void southbridge_smi_tco(void);
+
+/*
+ * This function should be implemented in SOC specific code to handle
+ * SMI PERIODIC_STS event. The default functionality is provided in
+ * soc/intel/common/smihandler.c
+ */
+void southbridge_smi_periodic(void);
+
+/*
+ * This function returns a 1 or 0 depending on whether disable_busmaster
+ * needs to be done for the specified device on S5 entry
+ */
+int smm_disable_busmaster(device_t dev);
+
+/*
+ * Returns io_misc_info from the saved SMM state area for Intel processors with
+ * SMM Revision 30100
+ */
+uintptr_t em64t100_smm_save_state_get_io_misc_info(void *state);
+/*
+ * Returns value of the specified register from the saved SMM state area
+ * for Intel processors with SMM Revision 30100
+ */
+uint64_t em64t100_smm_save_state_get_reg(void *state, enum smm_reg reg);
+void em64t100_smm_save_state_set_reg(void *state, enum smm_reg reg, uint64_t val);
+/*
+ * Returns io_misc_info from the saved SMM state area for Intel processors with
+ * SMM Revision 30101
+ */
+uintptr_t em64t101_smm_save_state_get_io_misc_info(void *state);
+/*
+ * Returns value of the specified register from the saved SMM state area
+ * for Intel processors with SMM Revision 30101
+ */
+uint64_t em64t101_smm_save_state_get_reg(void *state, enum smm_reg reg);
+void em64t101_smm_save_state_set_reg(void *state, enum smm_reg reg, uint64_t val);
+#endif
diff --git a/src/soc/intel/common/smihandler.c b/src/soc/intel/common/smihandler.c
new file mode 100644
index 0000000..4ad3b72
--- /dev/null
+++ b/src/soc/intel/common/smihandler.c
@@ -0,0 +1,441 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google Inc.
+ * Copyright (C) 2015-2016 Intel Corp.
+ *
+ * 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.
+ */
+
+#include <arch/hlt.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/smm.h>
+#include <device/pci_def.h>
+#include <elog.h>
+#include <soc/nvs.h>
+#include <soc/pm.h>
+#include <soc/gpio.h>
+#include <soc/iomap.h>
+#include <spi-generic.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "smi.h"
+
+/* GNVS needs to be set by coreboot initiating a software SMI. */
+static struct global_nvs_t *gnvs;
+
+__attribute__((weak)) int smm_disable_busmaster(device_t dev)
+{
+	return 1;
+}
+
+static void *find_save_state(int cmd)
+{
+	int node;
+	void *state = NULL;
+	uintptr_t io_misc_info;
+	uintptr_t reg_rax;
+
+	/* Check all nodes looking for the one that issued the IO */
+	for (node = 0; node < CONFIG_MAX_CPUS; node++) {
+		state = smm_get_save_state(node);
+
+		io_misc_info =
+		get_smm_save_state_ops()->get_io_misc_info(state);
+
+		/* Check for Synchronous IO (bit0==1) */
+		if (!(io_misc_info & (1 << 0)))
+			continue;
+		/* Make sure it was a write (bit4==0) */
+		if (io_misc_info & (1 << 4))
+			continue;
+		/* Check for APMC IO port */
+		if (((io_misc_info >> 16) & 0xff) != APM_CNT)
+			continue;
+		/* Check AX against the requested command */
+		reg_rax = get_smm_save_state_ops()->get_reg(state,RAX);
+		if ((reg_rax & 0xff) != cmd)
+			continue;
+		break;
+	}
+	return state;
+}
+
+void southbridge_smi_set_eos(void)
+{
+	enable_smi(EOS);
+}
+
+struct global_nvs_t *smm_get_gnvs(void)
+{
+	return gnvs;
+}
+
+static void busmaster_disable_on_bus(int bus)
+{
+	int slot, func;
+	unsigned int val;
+	unsigned char hdr;
+
+	for (slot = 0; slot < 0x20; slot++) {
+		for (func = 0; func < 8; func++) {
+			u32 reg32;
+			device_t dev = PCI_DEV(bus, slot, func);
+
+			if (!smm_disable_busmaster(dev))
+				continue;
+			val = pci_read_config32(dev, PCI_VENDOR_ID);
+
+			if (val == 0xffffffff || val == 0x00000000 ||
+			    val == 0x0000ffff || val == 0xffff0000)
+				continue;
+
+			/* Disable Bus Mastering for this one device */
+			reg32 = pci_read_config32(dev, PCI_COMMAND);
+			reg32 &= ~PCI_COMMAND_MASTER;
+			pci_write_config32(dev, PCI_COMMAND, reg32);
+
+			/* If this is a bridge, then follow it. */
+			hdr = pci_read_config8(dev, PCI_HEADER_TYPE);
+			hdr &= 0x7f;
+			if (hdr == PCI_HEADER_TYPE_BRIDGE ||
+			    hdr == PCI_HEADER_TYPE_CARDBUS) {
+				unsigned int buses;
+				buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
+				busmaster_disable_on_bus((buses >> 8) & 0xff);
+			}
+		}
+	}
+}
+
+
+void southbridge_smi_sleep(void)
+{
+	uint32_t reg32;
+	uint8_t slp_typ;
+
+	/* First, disable further SMIs */
+	disable_smi(SLP_SMI_EN);
+
+	/* Figure out SLP_TYP */
+	reg32 = inl(ACPI_PMIO_BASE + PM1_CNT);
+	printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
+	slp_typ = (reg32 >> 10) & 7;
+
+	/* Do any mainboard sleep handling */
+	mainboard_smi_sleep(slp_typ-2);
+
+	if (IS_ENABLED(CONFIG_ELOG_GSMI)) {
+	/* Log S3, S4, and S5 entry */
+		if (slp_typ >= 5)
+			elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ-2);
+	}
+      /* Clear pending GPE events */
+	clear_gpe_status();
+
+	/* Next, do the deed. */
+
+	switch (slp_typ) {
+	case SLP_TYP_S0:
+		printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
+		break;
+	case SLP_TYP_S3:
+		printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
+
+		/* Invalidate the cache before going to S3 */
+		wbinvd();
+		break;
+	case SLP_TYP_S4:
+		printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
+		break;
+	case SLP_TYP_S5:
+		printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
+
+		/* Disable all GPE */
+		disable_all_gpe();
+		/* also iterates over all bridges on bus 0 */
+		busmaster_disable_on_bus(0);
+		break;
+	default:
+		printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
+		break;
+	}
+	/* Clear pending wake status bit to avoid immediate wake */
+
+	/* Tri-state specific GPIOS to avoid leakage during S3/S5 */
+
+	/*
+	 * Write back to the SLP register to cause the originally intended
+	 * event again. We need to set BIT13 (SLP_EN) though to make the
+	 * sleep happen.
+	 */
+	enable_pm1_control(SLP_EN);
+
+	/* Make sure to stop executing code here for S3/S4/S5 */
+	if (slp_typ > 1)
+		hlt();
+
+	/*
+	 * In most sleep states, the code flow of this function ends at
+	 * the line above. However, if we entered sleep state S1 and wake
+	 * up again, we will continue to execute code in this function.
+	 */
+	reg32 = inl(ACPI_PMIO_BASE + PM1_CNT);
+	if (reg32 & SCI_EN) {
+		/* The OS is not an ACPI OS, so we set the state to S0 */
+		disable_pm1_control(SLP_EN | SLP_TYP);
+	}
+}
+
+static void southbridge_smi_gsmi(void)
+{
+	uintptr_t ret, param;
+	u8 sub_command;
+	void *io_smi = NULL;
+
+	if (IS_ENABLED(CONFIG_ELOG_GSMI)) {
+		io_smi = find_save_state(ELOG_GSMI_APM_CNT);
+		if (!io_smi)
+			return;
+
+		/* Command and return value in EAX */
+		ret = (uintptr_t)get_smm_save_state_ops()->get_reg(io_smi,RAX);
+		sub_command = *(u8 *)(ret) >> 8;
+
+		/* Parameter buffer in EBX */
+		param = (uintptr_t)get_smm_save_state_ops()->get_reg(io_smi,RBX);
+
+		/* drivers/elog/gsmi.c */
+		*(u32 *)(ret) = gsmi_exec(sub_command, (u32 *)param);
+	}
+}
+
+static void finalize(void)
+{
+	static int finalize_done;
+
+	if (finalize_done) {
+		printk(BIOS_DEBUG, "SMM already finalized.\n");
+		return;
+	}
+	finalize_done = 1;
+
+}
+
+void southbridge_smi_apmc(void)
+{
+	uint8_t reg8;
+	void	*state = NULL;
+	static int smm_initialized = 0;
+
+	/* Emulate B2 register as the FADT / Linux expects it */
+
+	reg8 = inb(APM_CNT);
+	switch (reg8) {
+	case APM_CNT_CST_CONTROL:
+		/*
+		 * Calling this function seems to cause
+		 * some kind of race condition in Linux
+		 * and causes a kernel oops
+		 */
+		printk(BIOS_DEBUG, "C-state control\n");
+		break;
+	case APM_CNT_PST_CONTROL:
+		/*
+		 * Calling this function seems to cause
+		 * some kind of race condition in Linux
+		 * and causes a kernel oops
+		 */
+		printk(BIOS_DEBUG, "P-state control\n");
+		break;
+	case APM_CNT_ACPI_DISABLE:
+		disable_pm1_control(SCI_EN);
+		printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
+		break;
+	case APM_CNT_ACPI_ENABLE:
+		enable_pm1_control(SCI_EN);
+		printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
+		break;
+	case APM_CNT_GNVS_UPDATE:
+		if (smm_initialized) {
+			printk(BIOS_DEBUG,
+			       "SMI#: SMM structures already initialized!\n");
+			return;
+		}
+		state = find_save_state(reg8);
+		if (state) {
+			/* EBX in the state save contains the GNVS pointer */
+			gnvs = ( struct global_nvs_t *)(uintptr_t)
+				get_smm_save_state_ops()->get_reg(state,RBX);
+			smm_initialized = 1;
+			printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs);
+		}
+		break;
+	case ELOG_GSMI_APM_CNT:
+		if (IS_ENABLED(CONFIG_ELOG_GSMI))
+			southbridge_smi_gsmi();
+		break;
+	case APM_CNT_FINALIZE:
+		finalize();
+		break;
+	}
+
+	mainboard_smi_apmc(reg8);
+}
+
+void southbridge_smi_pm1(void)
+{
+	uint16_t pm1_sts = clear_pm1_status();
+
+	/*
+	 * While OSPM is not active, poweroff immediately
+	 * on a power button event.
+	 */
+	if (pm1_sts & PWRBTN_STS) {
+		/* power button pressed */
+		if (IS_ENABLED(CONFIG_ELOG_GSMI))
+			elog_add_event(ELOG_TYPE_POWER_BUTTON);
+		disable_pm1_control(-1UL);
+		enable_pm1_control(SLP_EN | (SLP_TYP_S5 << SLP_TYP_SHIFT));
+	}
+}
+
+void southbridge_smi_gpe0(void)
+{
+	clear_gpe_status();
+}
+
+void southbridge_smi_tco(void)
+{
+	uint32_t tco_sts = clear_tco_status();
+
+	/* Any TCO event? */
+	if (!tco_sts)
+		return;
+
+	if (tco_sts & TCO_TIMEOUT) { /* TIMEOUT */
+		/* Handle TCO timeout */
+		printk(BIOS_DEBUG, "TCO Timeout.\n");
+	}
+}
+
+void southbridge_smi_periodic(void)
+{
+	uint32_t reg32;
+
+	reg32 = get_smi_en();
+
+	/* Are periodic SMIs enabled? */
+	if ((reg32 & PERIODIC_EN) == 0)
+		return;
+	printk(BIOS_DEBUG, "Periodic SMI.\n");
+}
+
+void southbridge_smi_handler(void)
+{
+	int i;
+	uint32_t smi_sts;
+
+	/*
+	 * We need to clear the SMI status registers, or we won't see what's
+	 * happening in the following calls.
+	 */
+	smi_sts = clear_smi_status();
+
+	/* Call SMI sub handler for each of the status bits */
+	for (i = 0; i < ARRAY_SIZE(southbridge_smi); i++) {
+		if (!(smi_sts & (1 << i)))
+			continue;
+
+		if (southbridge_smi[i] != NULL) {
+			southbridge_smi[i]();
+		} else {
+			printk(BIOS_DEBUG,
+			       "SMI_STS[%d] occurred, but no "
+			       "handler available.\n", i);
+		}
+	}
+}
+
+uintptr_t em64t100_smm_save_state_get_io_misc_info(void *state)
+{
+	em64t100_smm_state_save_area_t *smm_state = state;
+	return (smm_state ? smm_state->io_misc_info : 0);
+}
+
+uint64_t em64t100_smm_save_state_get_reg(void *state, enum smm_reg reg)
+{
+	uintptr_t value = 0;
+	em64t100_smm_state_save_area_t *smm_state = state;
+
+	switch(reg) {
+		case RAX:
+			value = smm_state->rax;
+			break;
+		case RBX:
+			value = smm_state->rbx;
+			break;
+		default:
+			break;
+	}
+	return value;
+}
+
+void em64t100_smm_save_state_set_reg(void *state, enum smm_reg reg, uint64_t val)
+{
+	em64t100_smm_state_save_area_t *smm_state = state;
+	switch(reg) {
+	case RAX:
+		smm_state->rax = val;
+		break;
+	default:
+		break;
+	}
+}
+
+
+uintptr_t em64t101_smm_save_state_get_io_misc_info(void *state)
+{
+	em64t101_smm_state_save_area_t *smm_state = state;
+	return (smm_state ? smm_state->io_misc_info : 0);
+}
+
+uint64_t em64t101_smm_save_state_get_reg(void *state, enum smm_reg reg)
+{
+	uintptr_t value = 0;
+	em64t101_smm_state_save_area_t *smm_state = state;
+
+	switch(reg) {
+		case RAX:
+			value = smm_state->rax;
+			break;
+		case RBX:
+			value = smm_state->rbx;
+			break;
+		default:
+			break;
+	}
+	return value;
+}
+
+void em64t101_smm_save_state_set_reg(void *state, enum smm_reg reg, uint64_t val)
+{
+	em64t101_smm_state_save_area_t *smm_state = state;
+	switch(reg) {
+	case RAX:
+		smm_state->rax = val;
+		break;
+	default:
+		break;
+	}
+}
+



More information about the coreboot-gerrit mailing list