From 97c0979befedb822e9d77bc0e11374e291332c49 Mon Sep 17 00:00:00 2001 From: Werner Zeh Date: Thu, 6 Apr 2017 10:01:24 +0200 Subject: 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 Reviewed-on: https://review.coreboot.org/19145 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin --- src/soc/intel/fsp_broadwell_de/Kconfig | 10 + src/soc/intel/fsp_broadwell_de/Makefile.inc | 6 + src/soc/intel/fsp_broadwell_de/cpu.c | 34 ++- .../fsp_broadwell_de/include/soc/broadwell_de.h | 6 + src/soc/intel/fsp_broadwell_de/include/soc/lpc.h | 33 ++ src/soc/intel/fsp_broadwell_de/include/soc/msr.h | 28 +- .../intel/fsp_broadwell_de/include/soc/pci_devs.h | 7 + src/soc/intel/fsp_broadwell_de/include/soc/smm.h | 76 +++++ src/soc/intel/fsp_broadwell_de/pmutil.c | 173 +++++++++++ src/soc/intel/fsp_broadwell_de/smi.c | 87 ++++++ src/soc/intel/fsp_broadwell_de/smihandler.c | 112 +++++++ src/soc/intel/fsp_broadwell_de/smmrelocate.c | 337 +++++++++++++++++++++ 12 files changed, 900 insertions(+), 9 deletions(-) create mode 100644 src/soc/intel/fsp_broadwell_de/include/soc/smm.h create mode 100644 src/soc/intel/fsp_broadwell_de/pmutil.c create mode 100644 src/soc/intel/fsp_broadwell_de/smi.c create mode 100644 src/soc/intel/fsp_broadwell_de/smihandler.c create mode 100644 src/soc/intel/fsp_broadwell_de/smmrelocate.c diff --git a/src/soc/intel/fsp_broadwell_de/Kconfig b/src/soc/intel/fsp_broadwell_de/Kconfig index 8442963977..cfe3fb05b8 100644 --- a/src/soc/intel/fsp_broadwell_de/Kconfig +++ b/src/soc/intel/fsp_broadwell_de/Kconfig @@ -23,6 +23,8 @@ config CPU_SPECIFIC_OPTIONS # 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 @@ config VGA_BIOS 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 657f70c936..028c45d6dc 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/microcode 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 += reset.c 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 1e4ec34858..9d7fe98dde 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 #include #include +#include + +/* MP initialization support. */ +static const void *microcode_patch; static void pre_mp_init(void) { @@ -42,18 +47,43 @@ static int get_cpu_count(void) 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 @@ static void configure_mca(void) 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 85298a5ccd..4c11f386dd 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 8cf8889c3f..6a91f8fd2d 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 da9ebb9921..f5ea34ccd3 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 c5bb77ab75..5fb8113980 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 0000000000..ab8ca8eb71 --- /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 +#include + +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 0000000000..bd19104f4d --- /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 +#include +#include +#include +#include +#include +#include +#include + +/* 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 0000000000..5411bcaed0 --- /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 +#include +#include +#include +#include + +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 0000000000..d34a070bec --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * @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 0000000000..a94693b63a --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} -- cgit v1.2.3