[coreboot-gerrit] Change in coreboot[master]: fsp_broadwell_de: Add SMM code

Werner Zeh (Code Review) gerrit at coreboot.org
Fri Apr 28 06:19:23 CEST 2017


Werner Zeh has submitted this change and it was merged. ( https://review.coreboot.org/19145 )

Change subject: fsp_broadwell_de: Add SMM code
......................................................................


fsp_broadwell_de: Add SMM code

Add basic SMM support for Broadwell-DE SoC.

The code is mainly based on the SMM implementation of Broadwell with a
few differences:
- EMRR is now called PRMRR and the UNCORE part of it is not available
- SMM_FEATURE_CONTROL is no longer a MSR but is now located in PCI space
- currently only SERIRQ-SMI has a handler

Change-Id: I461a14d411aedefdb0cb54ae43b91103a80a4f6a
Signed-off-by: Werner Zeh <werner.zeh at siemens.com>
Reviewed-on: https://review.coreboot.org/19145
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin at chromium.org>
---
M src/soc/intel/fsp_broadwell_de/Kconfig
M src/soc/intel/fsp_broadwell_de/Makefile.inc
M src/soc/intel/fsp_broadwell_de/cpu.c
M src/soc/intel/fsp_broadwell_de/include/soc/broadwell_de.h
M src/soc/intel/fsp_broadwell_de/include/soc/lpc.h
M src/soc/intel/fsp_broadwell_de/include/soc/msr.h
M src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h
A src/soc/intel/fsp_broadwell_de/include/soc/smm.h
A src/soc/intel/fsp_broadwell_de/pmutil.c
A src/soc/intel/fsp_broadwell_de/smi.c
A src/soc/intel/fsp_broadwell_de/smihandler.c
A src/soc/intel/fsp_broadwell_de/smmrelocate.c
12 files changed, 900 insertions(+), 9 deletions(-)

Approvals:
  Aaron Durbin: Looks good to me, approved
  build bot (Jenkins): Verified



diff --git a/src/soc/intel/fsp_broadwell_de/Kconfig b/src/soc/intel/fsp_broadwell_de/Kconfig
index 8442963..cfe3fb0 100644
--- a/src/soc/intel/fsp_broadwell_de/Kconfig
+++ b/src/soc/intel/fsp_broadwell_de/Kconfig
@@ -23,6 +23,8 @@
 	# Microcode header files are delivered in FSP package
 	select USES_MICROCODE_HEADER_FILES if HAVE_FSP_BIN
 	select HAVE_INTEL_FIRMWARE
+	select SMM_TSEG
+	select HAVE_SMI_HANDLER
 
 config CBFS_SIZE
 	hex
@@ -56,6 +58,14 @@
 	bool
 	default n
 
+config SMM_TSEG_SIZE
+	hex
+	default 0x800000
+
+config SMM_RESERVED_SIZE
+	hex
+	default 0x100000
+
 config INTEGRATED_UART
 	bool "Integrated UART ports"
 	default y
diff --git a/src/soc/intel/fsp_broadwell_de/Makefile.inc b/src/soc/intel/fsp_broadwell_de/Makefile.inc
index 657f70c..028c45d 100644
--- a/src/soc/intel/fsp_broadwell_de/Makefile.inc
+++ b/src/soc/intel/fsp_broadwell_de/Makefile.inc
@@ -5,6 +5,7 @@
 subdirs-y += ../../../cpu/intel/turbo
 subdirs-y += ../../../cpu/x86/lapic
 subdirs-y += ../../../cpu/x86/mtrr
+subdirs-y += ../../../cpu/x86/smm
 subdirs-y += ../../../cpu/x86/tsc
 subdirs-y += ../../../cpu/x86/cache
 subdirs-y += ../../../lib/fsp
@@ -23,6 +24,11 @@
 ramstage-y += acpi.c
 ramstage-y += smbus_common.c
 ramstage-y += smbus.c
+ramstage-y += smi.c
+ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smmrelocate.c
+ramstage-$(CONFIG_HAVE_SMI_HANDLER) += pmutil.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += pmutil.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c
 
 CPPFLAGS_common += -I$(src)/soc/intel/fsp_broadwell_de/include
 CPPFLAGS_common += -I$(src)/soc/intel/fsp_broadwell_de/fsp
diff --git a/src/soc/intel/fsp_broadwell_de/cpu.c b/src/soc/intel/fsp_broadwell_de/cpu.c
index 1e4ec34..9d7fe98 100644
--- a/src/soc/intel/fsp_broadwell_de/cpu.c
+++ b/src/soc/intel/fsp_broadwell_de/cpu.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2013 Google Inc.
  * Copyright (C) 2015-2016 Intel Corp.
+ * Copyright (C) 2017 Siemens AG
  *
  * 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
@@ -26,6 +27,10 @@
 #include <soc/msr.h>
 #include <soc/pattrs.h>
 #include <soc/ramstage.h>
+#include <soc/smm.h>
+
+/* MP initialization support. */
+static const void *microcode_patch;
 
 static void pre_mp_init(void)
 {
@@ -42,18 +47,43 @@
 	return pattrs->num_cpus;
 }
 
+static void per_cpu_smm_trigger(void)
+{
+	/* Relocate the SMM handler. */
+	smm_relocate();
+
+	/* After SMM relocation a 2nd microcode load is required. */
+	intel_microcode_load_unlocked(microcode_patch);
+}
+
 static void get_microcode_info(const void **microcode, int *parallel)
 {
 	const struct pattrs *pattrs = pattrs_get();
 
+	microcode_patch = pattrs->microcode_patch;
 	*microcode = pattrs->microcode_patch;
 	*parallel = 1;
 }
 
+static void post_mp_init(void)
+{
+	/* Now that all APs have been relocated as well as the BSP let SMIs
+	   start flowing. */
+	southbridge_smm_enable_smi();
+
+	/* Set SMI lock bits. */
+	smm_lock();
+}
+
 static const struct mp_ops mp_ops = {
 	.pre_mp_init = pre_mp_init,
+	.get_smm_info = smm_info,
 	.get_cpu_count = get_cpu_count,
 	.get_microcode_info = get_microcode_info,
+	.pre_mp_smm_init = smm_initialize,
+	.per_cpu_smm_trigger = per_cpu_smm_trigger,
+	.relocation_handler = smm_relocation_handler,
+	.post_mp_init = post_mp_init
 };
 
 void broadwell_de_init_cpus(device_t dev)
@@ -76,8 +106,8 @@
 	num_banks = msr.lo & 0xff;
 
 	/* TODO(adurbin): This should only be done on a cold boot. Also, some
-	 * of these banks are core vs package scope. For now every CPU clears
-	 * every bank. */
+	   of these banks are core vs package scope. For now every CPU clears
+	   every bank. */
 	msr.lo = msr.hi = 0;
 	for (i = 0; i < num_banks; i++) {
 		wrmsr(MSR_IA32_MC0_STATUS + (i * 4) + 1, msr);
diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/broadwell_de.h b/src/soc/intel/fsp_broadwell_de/include/soc/broadwell_de.h
index 85298a5..4c11f38 100644
--- a/src/soc/intel/fsp_broadwell_de/include/soc/broadwell_de.h
+++ b/src/soc/intel/fsp_broadwell_de/include/soc/broadwell_de.h
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2013 Google, Inc.
  * Copyright (C) 2015-2016 Intel Corp.
+ * Copyright (C) 2017 Siemens AG
  *
  * 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
@@ -20,4 +21,9 @@
 #define VTBAR_OFFSET		0x180
 #define VTBAR_MASK		0xffffe000
 
+#define SMM_FEATURE_CONTROL	0x58
+#define  SMM_CPU_SAVE_EN	(1 << 1)
+#define TSEG_BASE		0xa8	/* TSEG base */
+#define TSEG_LIMIT		0xac	/* TSEG limit */
+
 #endif /* _SOC_BROADWELL_DE_H_ */
diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h b/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h
index 8cf8889..6a91f8f 100644
--- a/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h
+++ b/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2013 Google Inc.
  * Copyright (C) 2015-2016 Intel Corp.
+ * Copyright (C) 2017 Siemens AG
  *
  * 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
@@ -34,6 +35,9 @@
 #define LPC_GEN3_DEC		0x8c
 #define LPC_GEN4_DEC		0x90
 #define GEN_PMCON_1		0xA0
+#define   SMI_LOCK		(1 << 4)
+#define   SMI_LOCK_GP6		(1 << 5)
+#define   SMI_LOCK_GP22		(1 << 6)
 #define GEN_PMCON_2		0xA2
 #define GEN_PMCON_3		0xA4
 #define   RTC_PWR_STS		(1 << 2)
@@ -72,6 +76,22 @@
 #define   HOT_PLUG_STS		(1 << 1)
 #define GPE0_EN			0x28
 #define SMI_EN			0x30
+#define  XHCI_SMI_EN		(1 << 31)
+#define  ME_SMI_EN		(1 << 30)
+#define  GPIO_UNLOCK_SMI_EN	(1 << 27)
+#define  INTEL_USB2_EN		(1 << 18)
+#define  LEGACY_USB2_EN		(1 << 17)
+#define  PERIODIC_EN		(1 << 14)
+#define  TCO_EN			(1 << 13)
+#define  MCSMI_EN		(1 << 11)
+#define  BIOS_RLS		(1 <<  7)
+#define  SWSMI_TMR_EN		(1 <<  6)
+#define  APMC_EN		(1 <<  5)
+#define  SLP_SMI_EN		(1 <<  4)
+#define  LEGACY_USB_EN		(1 <<  3)
+#define  BIOS_EN		(1 <<  2)
+#define  EOS			(1 <<  1)
+#define  GBL_SMI_EN		(1 <<  0)
 #define SMI_STS			0x34
 #define ALT_GPIO_SMI		0x38
 #define UPRWC			0x3c
@@ -87,4 +107,17 @@
 #define   TCO_TMR_HALT		(1 << 11)
 #define TCO_TMR			0x70
 
+/* PM1_CNT */
+void enable_pm1_control(uint32_t mask);
+void disable_pm1_control(uint32_t mask);
+
+/* PM1 */
+uint16_t clear_pm1_status(void);
+void enable_pm1(uint16_t events);
+uint32_t clear_smi_status(void);
+
+/* SMI */
+void enable_smi(uint32_t mask);
+void disable_smi(uint32_t mask);
+
 #endif /* _SOC_LPC_H_ */
diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/msr.h b/src/soc/intel/fsp_broadwell_de/include/soc/msr.h
index da9ebb9..f5ea34c 100644
--- a/src/soc/intel/fsp_broadwell_de/include/soc/msr.h
+++ b/src/soc/intel/fsp_broadwell_de/include/soc/msr.h
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2013 Google, Inc.
  * Copyright (C) 2015-2016 Intel Corp.
+ * Copyright (C) 2017 Siemens AG
  *
  * 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
@@ -17,12 +18,25 @@
 #ifndef _SOC_MSR_H_
 #define _SOC_MSR_H_
 
-#define MSR_IA32_PLATFORM_ID    0x17
-#define MSR_CORE_THREAD_COUNT   0x35
-#define MSR_PLATFORM_INFO       0xce
-#define MSR_TURBO_RATIO_LIMIT   0x1ad
-#define MSR_IA32_MC0_STATUS     0x400
-#define MSR_PKG_POWER_SKU_UNIT  0x606
-#define MSR_PKG_POWER_LIMIT     0x610
+#define MSR_IA32_PLATFORM_ID	0x17
+#define MSR_CORE_THREAD_COUNT	0x35
+#define MSR_PLATFORM_INFO	0xce
+#define MSR_TURBO_RATIO_LIMIT	0x1ad
+#define MSR_IA32_MC0_STATUS	0x400
+#define MSR_PKG_POWER_SKU_UNIT	0x606
+#define MSR_PKG_POWER_LIMIT	0x610
+
+#define SMM_MCA_CAP_MSR		0x17d
+#define  SMM_CPU_SVRSTR_BIT	57
+#define  SMM_CPU_SVRSTR_MASK	(1 << (SMM_CPU_SVRSTR_BIT - 32))
+
+/* SMM save state MSRs */
+#define SMBASE_MSR		0xc20
+#define IEDBASE_MSR		0xc22
+/* MTRR_CAP_MSR bits */
+#define SMRR_SUPPORTED		(1 << 11)
+#define PRMRR_SUPPORTED		(1 << 12)
+#define PRMRRphysBase_MSR	0x1f4
+#define PRMRRphysMask_MSR	0x1f5
 
 #endif /* _SOC_MSR_H_ */
diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h b/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h
index c5bb77a..5fb8113 100644
--- a/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h
+++ b/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2013 Google Inc.
  * Copyright (C) 2015-2016 Intel Corp.
+ * Copyright (C) 2017 Siemens AG
  *
  * 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
@@ -119,4 +120,10 @@
 #define PCIE_PORT7_DEV_FUNC PCI_DEVFN(PCIE_DEV, PCIE_PORT7_FUNC)
 #define PCIE_PORT8_DEV_FUNC PCI_DEVFN(PCIE_DEV, PCIE_PORT8_FUNC)
 
+/* The SMM device is located on bus 0xff (QPI) */
+#define QPI_BUS			0xff
+#define SMM_DEV			0x10
+#define SMM_FUNC		0x06
+#define SMM_DEV_FUNC		PCI_DEVFN(SMM_DEV, SMM_FUNC)
+
 #endif /* _SOC_PCI_DEVS_H_ */
diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/smm.h b/src/soc/intel/fsp_broadwell_de/include/soc/smm.h
new file mode 100644
index 0000000..ab8ca8e
--- /dev/null
+++ b/src/soc/intel/fsp_broadwell_de/include/soc/smm.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google Inc.
+ * Copyright (C) 2017 Siemens AG
+ *
+ * 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 _BROADWELL_SMM_H_
+#define _BROADWELL_SMM_H_
+
+#include <stdint.h>
+#include <cpu/x86/msr.h>
+
+struct ied_header {
+	char signature[10];
+	u32 size;
+	u8 reserved[34];
+} __attribute__ ((packed));
+
+struct smm_relocation_params {
+	u32 smram_base;
+	u32 smram_size;
+	u32 ied_base;
+	u32 ied_size;
+	msr_t smrr_base;
+	msr_t smrr_mask;
+	msr_t prmrr_base;
+	msr_t prmrr_mask;
+	/* The smm_save_state_in_msrs field indicates if SMM save state
+	   locations live in MSRs. This indicates to the CPUs how to adjust
+	   the SMMBASE and IEDBASE. */
+	int smm_save_state_in_msrs;
+};
+
+/*
+ * There is a bug in the order of Kconfig includes in that arch/x86/Kconfig
+ * is included after chipset code. This causes the chipset's Kconfig to be
+ * clobbered by the arch/x86/Kconfig if they have the same name.
+ */
+static inline int smm_region_size(void)
+{
+	/* Make it 8MiB by default. */
+	if (CONFIG_SMM_TSEG_SIZE == 0)
+		return (8 << 20);
+	return CONFIG_SMM_TSEG_SIZE;
+}
+
+void smm_relocation_handler(int cpu, uintptr_t curr_smbase,
+				uintptr_t staggered_smbase);
+void smm_info(uintptr_t *perm_smbase, size_t *perm_smsize,
+		size_t *smm_save_state_size);
+void smm_initialize(void);
+void smm_relocate(void);
+
+/* These helpers are for performing SMM relocation. */
+void southbridge_trigger_smi(void);
+void southbridge_clear_smi_status(void);
+
+/*
+ * The initialization of the southbridge is split into 2 components. One is
+ * for clearing the state in the SMM registers. The other is for enabling
+ * SMIs. They are split so that other work between the 2 actions.
+ */
+void southbridge_smm_clear_state(void);
+void southbridge_smm_enable_smi(void);
+
+#endif
diff --git a/src/soc/intel/fsp_broadwell_de/pmutil.c b/src/soc/intel/fsp_broadwell_de/pmutil.c
new file mode 100644
index 0000000..bd19104
--- /dev/null
+++ b/src/soc/intel/fsp_broadwell_de/pmutil.c
@@ -0,0 +1,173 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google Inc.
+ * Copyright (C) 2017 Siemens AG
+ *
+ * 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.
+ */
+
+/*
+ * Helper functions for dealing with power management registers
+ * and the differences between PCH variants.
+ */
+
+#include <arch/io.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_def.h>
+#include <console/console.h>
+#include <soc/iomap.h>
+#include <soc/lpc.h>
+#include <soc/pci_devs.h>
+
+/* Print status bits with descriptive names */
+static void print_status_bits(u32 status, const char * const bit_names[])
+{
+	int i;
+
+	if (!status)
+		return;
+
+	for (i = 31; i >= 0; i--) {
+		if (status & (1 << i)) {
+			if (bit_names[i])
+				printk(BIOS_DEBUG, "%s ", bit_names[i]);
+			else
+				printk(BIOS_DEBUG, "BIT%d ", i);
+		}
+	}
+}
+
+/* Enable events in PM1 control register */
+void enable_pm1_control(u32 mask)
+{
+	u32 pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
+	pm1_cnt |= mask;
+	outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
+}
+
+/* Disable events in PM1 control register */
+void disable_pm1_control(u32 mask)
+{
+	u32 pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
+	pm1_cnt &= ~mask;
+	outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
+}
+
+/* Clear and return PM1 status register */
+static u16 reset_pm1_status(void)
+{
+	u16 pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
+	outw(pm1_sts, ACPI_BASE_ADDRESS + PM1_STS);
+	return pm1_sts;
+}
+
+/* Print PM1 status bits */
+static u16 print_pm1_status(u16 pm1_sts)
+{
+	static const char * const pm1_sts_bits[] = {
+		[0] = "TMROF",
+		[4] = "BM",
+		[5] = "GBL",
+		[8] = "PWRBTN",
+		[10] = "RTC",
+		[11] = "PRBTNOR",
+		[14] = "PCIEXPWAK",
+		[15] = "WAK",
+	};
+
+	if (!pm1_sts)
+		return 0;
+
+	printk(BIOS_SPEW, "PM1_STS: ");
+	print_status_bits(pm1_sts, pm1_sts_bits);
+	printk(BIOS_SPEW, "\n");
+
+	return pm1_sts;
+}
+
+/* Print, clear, and return PM1 status */
+u16 clear_pm1_status(void)
+{
+	return print_pm1_status(reset_pm1_status());
+}
+
+/* Set the PM1 register to events */
+void enable_pm1(u16 events)
+{
+	outw(events, ACPI_BASE_ADDRESS + PM1_EN);
+}
+
+/* Clear and return SMI status register */
+static u32 reset_smi_status(void)
+{
+	u32 smi_sts = inl(ACPI_BASE_ADDRESS + SMI_STS);
+	outl(smi_sts, ACPI_BASE_ADDRESS + SMI_STS);
+	return smi_sts;
+}
+
+/* Print SMI status bits */
+static u32 print_smi_status(u32 smi_sts)
+{
+	static const char * const smi_sts_bits[] = {
+		[2] = "BIOS",
+		[3] = "LEGACY_USB",
+		[4] = "SLP_SMI",
+		[5] = "APM",
+		[6] = "SWSMI_TMR",
+		[8] = "PM1",
+		[9] = "GPE0",
+		[10] = "GPI",
+		[11] = "MCSMI",
+		[12] = "DEVMON",
+		[13] = "TCO",
+		[14] = "PERIODIC",
+		[15] = "SERIRQ_SMI",
+		[16] = "SMBUS_SMI",
+		[17] = "LEGACY_USB2",
+		[18] = "INTEL_USB2",
+		[20] = "PCI_EXP_SMI",
+		[21] = "MONITOR",
+		[26] = "SPI",
+		[27] = "GPIO_UNLOCK"
+	};
+
+	if (!smi_sts)
+		return 0;
+
+	printk(BIOS_DEBUG, "SMI_STS: ");
+	print_status_bits(smi_sts, smi_sts_bits);
+	printk(BIOS_DEBUG, "\n");
+
+	return smi_sts;
+}
+
+/* Print, clear, and return SMI status */
+u32 clear_smi_status(void)
+{
+	return print_smi_status(reset_smi_status());
+}
+
+/* Enable SMI event */
+void enable_smi(u32 mask)
+{
+	u32 smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
+	smi_en |= mask;
+	outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
+}
+
+/* Disable SMI event */
+void disable_smi(u32 mask)
+{
+	u32 smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
+	smi_en &= ~mask;
+	outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
+}
diff --git a/src/soc/intel/fsp_broadwell_de/smi.c b/src/soc/intel/fsp_broadwell_de/smi.c
new file mode 100644
index 0000000..5411bca
--- /dev/null
+++ b/src/soc/intel/fsp_broadwell_de/smi.c
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2014 Google Inc.
+ * Copyright (C) 2017 Siemens AG
+ *
+ * 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 <console/console.h>
+#include <arch/io.h>
+#include <soc/iomap.h>
+#include <soc/lpc.h>
+#include <soc/smm.h>
+
+void southbridge_smm_clear_state(void)
+{
+	u32 smi_en;
+
+	printk(BIOS_DEBUG, "Initializing Southbridge SMI...");
+	printk(BIOS_SPEW, " ... pmbase = 0x%04x\n", ACPI_BASE_ADDRESS);
+
+	smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
+	if (smi_en & APMC_EN) {
+		printk(BIOS_INFO, "SMI# handler already enabled?\n");
+		return;
+	}
+
+	printk(BIOS_DEBUG, "\n");
+
+	/* Dump and clear status registers */
+	clear_smi_status();
+	clear_pm1_status();
+}
+
+void southbridge_smm_enable_smi(void)
+{
+	printk(BIOS_DEBUG, "Enabling SMIs.\n");
+
+	/* Clear all possible set SMI status bits
+	   before enabling SMIs. */
+	southbridge_clear_smi_status();
+
+	/* Enable SMI generation:
+	   - on SERIRQ-SMI (is always enabled) */
+	enable_smi(EOS | GBL_SMI_EN);
+}
+
+void southbridge_trigger_smi(void)
+{
+	/*
+	 * There are several methods of raising a controlled SMI# via
+	 * software, among them:
+	 *  - Writes to io 0xb2 (APMC)
+	 *  - Writes to the Local Apic ICR with Delivery mode SMI.
+	 *
+	 * Using the local apic is a bit more tricky. According to
+	 * AMD Family 11 Processor BKDG no destination shorthand must be
+	 * used.
+	 * The whole SMM initialization is quite a bit hardware specific, so
+	 * I'm not too worried about the better of the methods at the moment
+	 */
+
+	/* Raise an SMI interrupt */
+	printk(BIOS_SPEW, "  ... raise SMI#\n");
+	outb(0x00, 0xb2);
+}
+
+void southbridge_clear_smi_status(void)
+{
+	/* Clear SMI status */
+	clear_smi_status();
+
+	/* Clear PM1 status */
+	clear_pm1_status();
+
+	/* Set EOS bit so other SMIs can occur. */
+	enable_smi(EOS);
+}
diff --git a/src/soc/intel/fsp_broadwell_de/smihandler.c b/src/soc/intel/fsp_broadwell_de/smihandler.c
new file mode 100644
index 0000000..d34a070
--- /dev/null
+++ b/src/soc/intel/fsp_broadwell_de/smihandler.c
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2014 Google Inc.
+ * Copyright (C) 2017 Siemens AG
+ *
+ * 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 <delay.h>
+#include <types.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/x86/cache.h>
+#include <device/pci_def.h>
+#include <cpu/x86/smm.h>
+#include <spi-generic.h>
+#include <elog.h>
+#include <halt.h>
+#include <pc80/mc146818rtc.h>
+#include <soc/lpc.h>
+#include <soc/iomap.h>
+#include <soc/pci_devs.h>
+#include <soc/smm.h>
+
+
+/**
+ * @brief Set the EOS bit
+ */
+void southbridge_smi_set_eos(void)
+{
+	enable_smi(EOS);
+}
+
+static void southbridge_smi_serirq(void)
+{
+
+}
+
+typedef void (*smi_handler_t)(void);
+
+static smi_handler_t southbridge_smi[32] = {
+	NULL,			  //  [0] reserved
+	NULL,			  //  [1] reserved
+	NULL,			  //  [2] BIOS_STS
+	NULL,			  //  [3] LEGACY_USB_STS
+	NULL,			  //  [4] SLP_SMI_STS
+	NULL,			  //  [5] APM_STS
+	NULL,			  //  [6] SWSMI_TMR_STS
+	NULL,			  //  [7] reserved
+	NULL,			  //  [8] PM1_STS
+	NULL,			  //  [9] GPE0_STS
+	NULL,			  // [10] GPI_STS
+	NULL,			  // [11] MCSMI_STS
+	NULL,			  // [12] DEVMON_STS
+	NULL,			  // [13] TCO_STS
+	NULL,			  // [14] PERIODIC_STS
+	southbridge_smi_serirq,	  // [15] SERIRQ_SMI_STS
+	NULL,			  // [16] SMBUS_SMI_STS
+	NULL,			  // [17] LEGACY_USB2_STS
+	NULL,			  // [18] INTEL_USB2_STS
+	NULL,			  // [19] reserved
+	NULL,			  // [20] PCI_EXP_SMI_STS
+	NULL,			  // [21] MONITOR_STS
+	NULL,			  // [22] reserved
+	NULL,			  // [23] reserved
+	NULL,			  // [24] reserved
+	NULL,			  // [25] EL_SMI_STS
+	NULL,			  // [26] SPI_STS
+	NULL,			  // [27] reserved
+	NULL,			  // [28] reserved
+	NULL,			  // [29] reserved
+	NULL,			  // [30] reserved
+	NULL			  // [31] reserved
+};
+
+/**
+ * @brief Interrupt handler for SMI#
+ *
+ * @param smm_revision revision of the smm state save map
+ */
+
+void southbridge_smi_handler(void)
+{
+	int i;
+	u32 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 < 31; i++) {
+		if (smi_sts & (1 << i)) {
+			if (southbridge_smi[i]) {
+				southbridge_smi[i]();
+			} else {
+				printk(BIOS_DEBUG,
+				       "SMI_STS[%d] occurred, but no "
+				       "handler available.\n", i);
+			}
+		}
+	}
+}
diff --git a/src/soc/intel/fsp_broadwell_de/smmrelocate.c b/src/soc/intel/fsp_broadwell_de/smmrelocate.c
new file mode 100644
index 0000000..a94693b
--- /dev/null
+++ b/src/soc/intel/fsp_broadwell_de/smmrelocate.c
@@ -0,0 +1,337 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google Inc.
+ * Copyright (C) 2017 Siemens AG
+ *
+ * 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.
+ */
+
+#define __SIMPLE_DEVICE__
+
+#include <types.h>
+#include <string.h>
+#include <device/pci.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/lapic.h>
+#include <cpu/x86/mp.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/smm.h>
+#include <console/console.h>
+#include <arch/io.h>
+#include <soc/lpc.h>
+#include <soc/msr.h>
+#include <soc/pci_devs.h>
+#include <soc/smm.h>
+#include <soc/broadwell_de.h>
+
+/* This gets filled in and used during relocation. */
+static struct smm_relocation_params smm_reloc_params;
+
+static inline void write_smrr(struct smm_relocation_params *relo_params)
+{
+	printk(BIOS_DEBUG, "Writing SMRR. base = 0x%08x, mask=0x%08x\n",
+			relo_params->smrr_base.lo, relo_params->smrr_mask.lo);
+	wrmsr(SMRR_PHYS_BASE, relo_params->smrr_base);
+	wrmsr(SMRR_PHYS_MASK, relo_params->smrr_mask);
+}
+
+static inline void write_prmrr(struct smm_relocation_params *relo_params)
+{
+	printk(BIOS_DEBUG, "Writing PRMRR. base = 0x%08x, mask=0x%08x\n",
+			relo_params->prmrr_base.lo, relo_params->prmrr_mask.lo);
+	wrmsr(PRMRRphysBase_MSR, relo_params->prmrr_base);
+	wrmsr(PRMRRphysMask_MSR, relo_params->prmrr_mask);
+}
+
+static void update_save_state(int cpu, uintptr_t curr_smbase,
+				uintptr_t staggered_smbase,
+				struct smm_relocation_params *relo_params)
+{
+	u32 smbase;
+	u32 iedbase;
+
+	/* The relocated handler runs with all CPUs concurrently. Therefore
+	   stagger the entry points adjusting SMBASE downwards by save state
+	   size * CPU num. */
+	smbase = staggered_smbase;
+	iedbase = relo_params->ied_base;
+
+	printk(BIOS_DEBUG, "New SMBASE=0x%08x IEDBASE=0x%08x\n",
+		smbase, iedbase);
+
+	/*
+	 *  All threads need to set IEDBASE and SMBASE to the relocated
+	 * handler region. However, the save state location depends on the
+	 * smm_save_state_in_msrs field in the relocation parameters. If
+	 * smm_save_state_in_msrs is non-zero then the CPUs are relocating
+	 * the SMM handler in parallel, and each CPUs save state area is
+	 * located in their respective MSR space. If smm_save_state_in_msrs
+	 * is zero then the SMM relocation is happening serially so the
+	 * save state is at the same default location for all CPUs.
+	 */
+	if (relo_params->smm_save_state_in_msrs) {
+		msr_t smbase_msr;
+		msr_t iedbase_msr;
+
+		smbase_msr.lo = smbase;
+		smbase_msr.hi = 0;
+
+		/* According the BWG the IEDBASE MSR is in bits 63:32. It's
+		   not clear why it differs from the SMBASE MSR. */
+		iedbase_msr.lo = 0;
+		iedbase_msr.hi = iedbase;
+
+		wrmsr(SMBASE_MSR, smbase_msr);
+		wrmsr(IEDBASE_MSR, iedbase_msr);
+	} else {
+		em64t101_smm_state_save_area_t *save_state;
+
+		save_state = (void *)(curr_smbase + SMM_DEFAULT_SIZE -
+					sizeof(*save_state));
+		save_state->smbase = smbase;
+		save_state->iedbase = iedbase;
+	}
+}
+
+/* Returns 1 if SMM MSR save state was set. */
+static int bsp_setup_msr_save_state(struct smm_relocation_params *relo_params)
+{
+	msr_t smm_mca_cap;
+
+	smm_mca_cap = rdmsr(SMM_MCA_CAP_MSR);
+	if (smm_mca_cap.hi & SMM_CPU_SVRSTR_MASK) {
+		uint32_t smm_feature_control;
+		device_t dev = PCI_DEV(QPI_BUS, SMM_DEV, SMM_FUNC);
+
+		/*
+		 * SMM_FEATURE_CONTROL on Broadwell-DE is not located in
+		 * MSR range but in PCI config space. The used PCI device is
+		 * located on bus 0xff, which has no root bridge and hence is
+		 * not scanned by PCI scan. Use MMIO config access to read the
+		 * needed 32 bit register.
+		 */
+		smm_feature_control = pci_read_config32(dev,
+							SMM_FEATURE_CONTROL);
+		smm_feature_control |= SMM_CPU_SAVE_EN;
+		pci_write_config32(dev,
+				   SMM_FEATURE_CONTROL, smm_feature_control);
+		relo_params->smm_save_state_in_msrs = 1;
+	}
+	return relo_params->smm_save_state_in_msrs;
+}
+
+/*
+ * The relocation work is actually performed in SMM context, but the code
+ * resides in the ramstage module. This occurs by trampolining from the default
+ * SMRAM entry point to here.
+ */
+void smm_relocation_handler(int cpu, uintptr_t curr_smbase,
+				uintptr_t staggered_smbase)
+{
+	msr_t mtrr_cap;
+	struct smm_relocation_params *relo_params = &smm_reloc_params;
+
+	printk(BIOS_DEBUG, "In relocation handler: CPU %d\n", cpu);
+
+	/* Determine if the processor supports saving state in MSRs. If so,
+	   enable it before the non-BSPs run so that SMM relocation can occur
+	   in parallel in the non-BSP CPUs. */
+	if (cpu == 0) {
+		/*
+		 * If smm_save_state_in_msrs is 1 then that means this is the
+		 * 2nd time through the relocation handler for the BSP.
+		 * Parallel SMM handler relocation is taking place. However,
+		 * it is desired to access other CPUs save state in the real
+		 * SMM handler. Therefore, disable the SMM save state in MSRs
+		 * feature.
+		 */
+		if (relo_params->smm_save_state_in_msrs) {
+			uint32_t smm_feature_control;
+			device_t dev = PCI_DEV(QPI_BUS, SMM_DEV, SMM_FUNC);
+
+			/*
+			 * SMM_FEATURE_CONTROL on Broadwell-DE is not located in
+			 * MSR range but in PCI config space. The used PCI
+			 * device is located on bus 0xff, which has no root
+			 * bridge and hence is not scanned by PCI scan.
+			 * Use MMIO config access to read the needed 32 bit
+			 * register.
+			 */
+			smm_feature_control = pci_read_config32(dev,
+							SMM_FEATURE_CONTROL);
+			smm_feature_control &= ~SMM_CPU_SAVE_EN;
+			pci_write_config32(dev, SMM_FEATURE_CONTROL,
+						smm_feature_control);
+		} else if (bsp_setup_msr_save_state(relo_params))
+			/*
+			 * Just return from relocation handler if MSR save
+			 * state is enabled. In that case the BSP will come
+			 * back into the relocation handler to setup the new
+			 * SMBASE as well disabling SMM save state in MSRs.
+			 */
+			return;
+	}
+
+	/* Make appropriate changes to the save state map. */
+	update_save_state(cpu, curr_smbase, staggered_smbase, relo_params);
+	/* Write PRMRR and SMRR MSRs based on indicated support. */
+	mtrr_cap = rdmsr(MTRR_CAP_MSR);
+	if (mtrr_cap.lo & SMRR_SUPPORTED)
+		write_smrr(relo_params);
+
+	if (mtrr_cap.lo & PRMRR_SUPPORTED)
+		write_prmrr(relo_params);
+}
+
+static u32 northbridge_get_base_reg(device_t dev, int reg)
+{
+	u32 value;
+
+	value = pci_read_config32(dev, reg);
+	/* Base registers are at 1MiB granularity. */
+	value &= ~((1 << 20) - 1);
+	return value;
+}
+
+static void fill_in_relocation_params(device_t dev,
+					struct smm_relocation_params *params)
+{
+	u32 tseg_size;
+	u32 tseg_base;
+	u32 tseg_limit;
+	u32 prmrr_base;
+	u32 prmrr_size;
+	int phys_bits;
+	/* All range registers are aligned to 4KiB */
+	const u32 rmask = ~((1 << 12) - 1);
+
+	/* Some of the range registers are dependent on the number of physical
+	   address bits supported. */
+	phys_bits = cpuid_eax(0x80000008) & 0xff;
+	/*
+	 * The range bounded by the TSEG_BASE and TSEG_LIMIT registers
+	 * encompasses the SMRAM range as well as the IED range.
+	 * However, the SMRAM available to the handler is 4MiB since the IEDRAM
+	 * lives TSEG_BASE + 4MiB.
+	 */
+	tseg_base = northbridge_get_base_reg(dev, TSEG_BASE);
+	tseg_limit = northbridge_get_base_reg(dev, TSEG_LIMIT);
+	tseg_size = tseg_limit - tseg_base;
+
+	params->smram_base = tseg_base;
+	params->smram_size = 4 << 20;
+	params->ied_base = tseg_base + params->smram_size;
+	params->ied_size = tseg_size - params->smram_size;
+
+	/* Adjust available SMM handler memory size. */
+	params->smram_size -= CONFIG_SMM_RESERVED_SIZE;
+
+	/* SMRR has 32-bits of valid address aligned to 4KiB. */
+	params->smrr_base.lo = (params->smram_base & rmask) | MTRR_TYPE_WRBACK;
+	params->smrr_base.hi = 0;
+	params->smrr_mask.lo = (~(tseg_size - 1) & rmask) |
+					MTRR_PHYS_MASK_VALID;
+	params->smrr_mask.hi = 0;
+
+	/* The PRMRR is at IEDBASE + 2MiB */
+	prmrr_base = (params->ied_base + (2 << 20)) & rmask;
+	prmrr_size = params->ied_size - (2 << 20);
+
+	/* PRMRR has 46 bits of valid address aligned to 4KiB. It's dependent
+	   on the number of physical address bits supported. */
+	params->prmrr_base.lo = prmrr_base | MTRR_TYPE_WRBACK;
+	params->prmrr_base.hi = 0;
+	params->prmrr_mask.lo = (~(prmrr_size - 1) & rmask)
+		| MTRR_PHYS_MASK_VALID;
+	params->prmrr_mask.hi = (1 << (phys_bits - 32)) - 1;
+}
+
+static void setup_ied_area(struct smm_relocation_params *params)
+{
+	char *ied_base;
+
+	struct ied_header ied = {
+		.signature = "INTEL RSVD",
+		.size = params->ied_size,
+		.reserved = {0},
+	};
+
+	ied_base = (void *)params->ied_base;
+
+	/* Place IED header at IEDBASE. */
+	memcpy(ied_base, &ied, sizeof(ied));
+
+	/* Zero out 32KiB at IEDBASE + 1MiB */
+	memset(ied_base + (1 << 20), 0, (32 << 10));
+}
+
+void smm_info(uintptr_t *perm_smbase, size_t *perm_smsize,
+				size_t *smm_save_state_size)
+{
+	device_t dev = PCI_DEV(BUS0, VTD_DEV, VTD_FUNC);
+
+	printk(BIOS_DEBUG, "Setting up SMI for CPU\n");
+
+	fill_in_relocation_params(dev, &smm_reloc_params);
+
+	setup_ied_area(&smm_reloc_params);
+
+	*perm_smbase = smm_reloc_params.smram_base;
+	*perm_smsize = smm_reloc_params.smram_size;
+	*smm_save_state_size = sizeof(em64t101_smm_state_save_area_t);
+}
+
+void smm_initialize(void)
+{
+	/* Clear the SMM state in the southbridge. */
+	southbridge_smm_clear_state();
+
+	/* Run the relocation handler for on the BSP to check and set up
+	   parallel SMM relocation. */
+	smm_initiate_relocation();
+
+	if (smm_reloc_params.smm_save_state_in_msrs)
+		printk(BIOS_DEBUG, "Doing parallel SMM relocation.\n");
+}
+
+/*
+ * The default SMM entry can happen in parallel or serially. If the
+ * default SMM entry is done in parallel the BSP has already setup
+ * the saving state to each CPU's MSRs. At least one save state size
+ * is required for the initial SMM entry for the BSP to determine if
+ * parallel SMM relocation is even feasible.
+ */
+void smm_relocate(void)
+{
+	/*
+	 * If smm_save_state_in_msrs is non-zero then parallel SMM relocation
+	 * shall take place. Run the relocation handler a second time on the
+	 * BSP to do the final move. For APs, a relocation handler always
+	 * needs to be run.
+	 */
+	if (smm_reloc_params.smm_save_state_in_msrs)
+		smm_initiate_relocation_parallel();
+	else if (!boot_cpu())
+		smm_initiate_relocation();
+}
+
+void smm_lock(void)
+{
+	device_t dev = PCI_DEV(BUS0, LPC_DEV, LPC_FUNC);
+	uint16_t smi_lock;
+
+	/* There is no register to lock SMRAM region on Broadwell-DE.
+	   Use this function to lock the SMI control bits. */
+	printk(BIOS_DEBUG, "Locking SMM.\n");
+	smi_lock = pci_read_config16(dev, GEN_PMCON_1);
+	smi_lock |= (SMI_LOCK | SMI_LOCK_GP6 | SMI_LOCK_GP22);
+	pci_write_config16(dev, GEN_PMCON_1, smi_lock);
+}

-- 
To view, visit https://review.coreboot.org/19145
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I461a14d411aedefdb0cb54ae43b91103a80a4f6a
Gerrit-PatchSet: 5
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Owner: Werner Zeh <werner.zeh at siemens.com>
Gerrit-Reviewer: Aaron Durbin <adurbin at chromium.org>
Gerrit-Reviewer: Mario Scheithauer <mario.scheithauer at siemens.com>
Gerrit-Reviewer: Paul Menzel <paulepanter at users.sourceforge.net>
Gerrit-Reviewer: Werner Zeh <werner.zeh at siemens.com>
Gerrit-Reviewer: York Yang <york.yang at intel.com>
Gerrit-Reviewer: build bot (Jenkins)



More information about the coreboot-gerrit mailing list