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@siemens.com> Reviewed-on: https://review.coreboot.org/19145 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
parent
00d250e228
commit
97c0979bef
|
@ -23,6 +23,8 @@ config CPU_SPECIFIC_OPTIONS
|
||||||
# Microcode header files are delivered in FSP package
|
# Microcode header files are delivered in FSP package
|
||||||
select USES_MICROCODE_HEADER_FILES if HAVE_FSP_BIN
|
select USES_MICROCODE_HEADER_FILES if HAVE_FSP_BIN
|
||||||
select HAVE_INTEL_FIRMWARE
|
select HAVE_INTEL_FIRMWARE
|
||||||
|
select SMM_TSEG
|
||||||
|
select HAVE_SMI_HANDLER
|
||||||
|
|
||||||
config CBFS_SIZE
|
config CBFS_SIZE
|
||||||
hex
|
hex
|
||||||
|
@ -56,6 +58,14 @@ config VGA_BIOS
|
||||||
bool
|
bool
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
config SMM_TSEG_SIZE
|
||||||
|
hex
|
||||||
|
default 0x800000
|
||||||
|
|
||||||
|
config SMM_RESERVED_SIZE
|
||||||
|
hex
|
||||||
|
default 0x100000
|
||||||
|
|
||||||
config INTEGRATED_UART
|
config INTEGRATED_UART
|
||||||
bool "Integrated UART ports"
|
bool "Integrated UART ports"
|
||||||
default y
|
default y
|
||||||
|
|
|
@ -5,6 +5,7 @@ subdirs-y += ../../../cpu/intel/microcode
|
||||||
subdirs-y += ../../../cpu/intel/turbo
|
subdirs-y += ../../../cpu/intel/turbo
|
||||||
subdirs-y += ../../../cpu/x86/lapic
|
subdirs-y += ../../../cpu/x86/lapic
|
||||||
subdirs-y += ../../../cpu/x86/mtrr
|
subdirs-y += ../../../cpu/x86/mtrr
|
||||||
|
subdirs-y += ../../../cpu/x86/smm
|
||||||
subdirs-y += ../../../cpu/x86/tsc
|
subdirs-y += ../../../cpu/x86/tsc
|
||||||
subdirs-y += ../../../cpu/x86/cache
|
subdirs-y += ../../../cpu/x86/cache
|
||||||
subdirs-y += ../../../lib/fsp
|
subdirs-y += ../../../lib/fsp
|
||||||
|
@ -23,6 +24,11 @@ ramstage-y += reset.c
|
||||||
ramstage-y += acpi.c
|
ramstage-y += acpi.c
|
||||||
ramstage-y += smbus_common.c
|
ramstage-y += smbus_common.c
|
||||||
ramstage-y += smbus.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/include
|
||||||
CPPFLAGS_common += -I$(src)/soc/intel/fsp_broadwell_de/fsp
|
CPPFLAGS_common += -I$(src)/soc/intel/fsp_broadwell_de/fsp
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Google Inc.
|
* Copyright (C) 2013 Google Inc.
|
||||||
* Copyright (C) 2015-2016 Intel Corp.
|
* Copyright (C) 2015-2016 Intel Corp.
|
||||||
|
* Copyright (C) 2017 Siemens AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -26,6 +27,10 @@
|
||||||
#include <soc/msr.h>
|
#include <soc/msr.h>
|
||||||
#include <soc/pattrs.h>
|
#include <soc/pattrs.h>
|
||||||
#include <soc/ramstage.h>
|
#include <soc/ramstage.h>
|
||||||
|
#include <soc/smm.h>
|
||||||
|
|
||||||
|
/* MP initialization support. */
|
||||||
|
static const void *microcode_patch;
|
||||||
|
|
||||||
static void pre_mp_init(void)
|
static void pre_mp_init(void)
|
||||||
{
|
{
|
||||||
|
@ -42,18 +47,43 @@ static int get_cpu_count(void)
|
||||||
return pattrs->num_cpus;
|
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)
|
static void get_microcode_info(const void **microcode, int *parallel)
|
||||||
{
|
{
|
||||||
const struct pattrs *pattrs = pattrs_get();
|
const struct pattrs *pattrs = pattrs_get();
|
||||||
|
|
||||||
|
microcode_patch = pattrs->microcode_patch;
|
||||||
*microcode = pattrs->microcode_patch;
|
*microcode = pattrs->microcode_patch;
|
||||||
*parallel = 1;
|
*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 = {
|
static const struct mp_ops mp_ops = {
|
||||||
.pre_mp_init = pre_mp_init,
|
.pre_mp_init = pre_mp_init,
|
||||||
|
.get_smm_info = smm_info,
|
||||||
.get_cpu_count = get_cpu_count,
|
.get_cpu_count = get_cpu_count,
|
||||||
.get_microcode_info = get_microcode_info,
|
.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)
|
void broadwell_de_init_cpus(device_t dev)
|
||||||
|
@ -76,8 +106,8 @@ static void configure_mca(void)
|
||||||
num_banks = msr.lo & 0xff;
|
num_banks = msr.lo & 0xff;
|
||||||
|
|
||||||
/* TODO(adurbin): This should only be done on a cold boot. Also, some
|
/* 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
|
of these banks are core vs package scope. For now every CPU clears
|
||||||
* every bank. */
|
every bank. */
|
||||||
msr.lo = msr.hi = 0;
|
msr.lo = msr.hi = 0;
|
||||||
for (i = 0; i < num_banks; i++) {
|
for (i = 0; i < num_banks; i++) {
|
||||||
wrmsr(MSR_IA32_MC0_STATUS + (i * 4) + 1, msr);
|
wrmsr(MSR_IA32_MC0_STATUS + (i * 4) + 1, msr);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Google, Inc.
|
* Copyright (C) 2013 Google, Inc.
|
||||||
* Copyright (C) 2015-2016 Intel Corp.
|
* Copyright (C) 2015-2016 Intel Corp.
|
||||||
|
* Copyright (C) 2017 Siemens AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,4 +21,9 @@
|
||||||
#define VTBAR_OFFSET 0x180
|
#define VTBAR_OFFSET 0x180
|
||||||
#define VTBAR_MASK 0xffffe000
|
#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_ */
|
#endif /* _SOC_BROADWELL_DE_H_ */
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Google Inc.
|
* Copyright (C) 2013 Google Inc.
|
||||||
* Copyright (C) 2015-2016 Intel Corp.
|
* Copyright (C) 2015-2016 Intel Corp.
|
||||||
|
* Copyright (C) 2017 Siemens AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -34,6 +35,9 @@
|
||||||
#define LPC_GEN3_DEC 0x8c
|
#define LPC_GEN3_DEC 0x8c
|
||||||
#define LPC_GEN4_DEC 0x90
|
#define LPC_GEN4_DEC 0x90
|
||||||
#define GEN_PMCON_1 0xA0
|
#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_2 0xA2
|
||||||
#define GEN_PMCON_3 0xA4
|
#define GEN_PMCON_3 0xA4
|
||||||
#define RTC_PWR_STS (1 << 2)
|
#define RTC_PWR_STS (1 << 2)
|
||||||
|
@ -72,6 +76,22 @@
|
||||||
#define HOT_PLUG_STS (1 << 1)
|
#define HOT_PLUG_STS (1 << 1)
|
||||||
#define GPE0_EN 0x28
|
#define GPE0_EN 0x28
|
||||||
#define SMI_EN 0x30
|
#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 SMI_STS 0x34
|
||||||
#define ALT_GPIO_SMI 0x38
|
#define ALT_GPIO_SMI 0x38
|
||||||
#define UPRWC 0x3c
|
#define UPRWC 0x3c
|
||||||
|
@ -87,4 +107,17 @@
|
||||||
#define TCO_TMR_HALT (1 << 11)
|
#define TCO_TMR_HALT (1 << 11)
|
||||||
#define TCO_TMR 0x70
|
#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_ */
|
#endif /* _SOC_LPC_H_ */
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Google, Inc.
|
* Copyright (C) 2013 Google, Inc.
|
||||||
* Copyright (C) 2015-2016 Intel Corp.
|
* Copyright (C) 2015-2016 Intel Corp.
|
||||||
|
* Copyright (C) 2017 Siemens AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -25,4 +26,17 @@
|
||||||
#define MSR_PKG_POWER_SKU_UNIT 0x606
|
#define MSR_PKG_POWER_SKU_UNIT 0x606
|
||||||
#define MSR_PKG_POWER_LIMIT 0x610
|
#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_ */
|
#endif /* _SOC_MSR_H_ */
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Google Inc.
|
* Copyright (C) 2013 Google Inc.
|
||||||
* Copyright (C) 2015-2016 Intel Corp.
|
* Copyright (C) 2015-2016 Intel Corp.
|
||||||
|
* Copyright (C) 2017 Siemens AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* 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_PORT7_DEV_FUNC PCI_DEVFN(PCIE_DEV, PCIE_PORT7_FUNC)
|
||||||
#define PCIE_PORT8_DEV_FUNC PCI_DEVFN(PCIE_DEV, PCIE_PORT8_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_ */
|
#endif /* _SOC_PCI_DEVS_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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue