haswell: Use SMM Modules

This commit adds support for using the SMM modules for haswell-based
boards. The SMI handling was also refactored to put the relocation
handler and permanent SMM handler loading in the cpu directory. All
tseg adjustment support is dropped by relying on the SMM module support
to perform the necessary relocations.

Change-Id: I8dd23610772fc4408567d9f4adf339596eac7b1f
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/2728
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
Aaron Durbin 2012-12-21 21:21:48 -06:00 committed by Ronald G. Minnich
parent b7ecf6d830
commit 29ffa54969
8 changed files with 378 additions and 196 deletions

View File

@ -10,6 +10,7 @@ config CPU_SPECIFIC_OPTIONS
select SSE2 select SSE2
select UDELAY_LAPIC select UDELAY_LAPIC
select SMM_TSEG select SMM_TSEG
select SMM_MODULES
select CPU_MICROCODE_IN_CBFS select CPU_MICROCODE_IN_CBFS
#select AP_IN_SIPI_WAIT #select AP_IN_SIPI_WAIT
select TSC_SYNC_MFENCE select TSC_SYNC_MFENCE

View File

@ -2,6 +2,7 @@ ramstage-y += haswell_init.c
subdirs-y += ../../x86/name subdirs-y += ../../x86/name
ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c
ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smmrelocate.c
cpu_microcode-$(CONFIG_CPU_MICROCODE_CBFS_GENERATE) += microcode_blob.c cpu_microcode-$(CONFIG_CPU_MICROCODE_CBFS_GENERATE) += microcode_blob.c

View File

@ -0,0 +1,332 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 ChromeOS Authors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <types.h>
#include <string.h>
#include <device/device.h>
#include <device/pci.h>
#include <cpu/cpu.h>
#include <cpu/x86/cache.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <cpu/x86/smm.h>
#include <console/console.h>
#include <northbridge/intel/haswell/haswell.h>
#include <southbridge/intel/lynxpoint/pch.h>
#include "haswell.h"
#define EMRRphysBase_MSR 0x1f4
#define EMRRphysMask_MSR 0x1f5
#define UNCORE_EMRRphysBase_MSR 0x2f4
#define UNCORE_EMRRphysMask_MSR 0x2f5
#define CORE_THREAD_COUNT_MSR 0x35
#define SMRR_SUPPORTED (1<<11)
#define EMRR_SUPPORTED (1<<12)
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 emrr_base;
msr_t emrr_mask;
msr_t uncore_emrr_base;
msr_t uncore_emrr_mask;
};
/* 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(SMRRphysBase_MSR, relo_params->smrr_base);
wrmsr(SMRRphysMask_MSR, relo_params->smrr_mask);
}
static inline void write_emrr(struct smm_relocation_params *relo_params)
{
printk(BIOS_DEBUG, "Writing EMRR. base = 0x%08x, mask=0x%08x\n",
relo_params->emrr_base.lo, relo_params->emrr_mask.lo);
wrmsr(EMRRphysBase_MSR, relo_params->emrr_base);
wrmsr(EMRRphysMask_MSR, relo_params->emrr_mask);
}
static inline void write_uncore_emrr(struct smm_relocation_params *relo_params)
{
printk(BIOS_DEBUG,
"Writing UNCORE_EMRR. base = 0x%08x, mask=0x%08x\n",
relo_params->uncore_emrr_base.lo,
relo_params->uncore_emrr_mask.lo);
wrmsr(UNCORE_EMRRphysBase_MSR, relo_params->uncore_emrr_base);
wrmsr(UNCORE_EMRRphysMask_MSR, relo_params->uncore_emrr_mask);
}
/* 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. */
static void cpu_smm_do_relocation(void *arg, int cpu,
const struct smm_runtime *runtime)
{
em64t101_smm_state_save_area_t *save_state;
msr_t mtrr_cap;
struct smm_relocation_params *relo_params = arg;
if (cpu >= CONFIG_MAX_CPUS) {
printk(BIOS_CRIT,
"Invalid CPU number assigned in SMM stub: %d\n", cpu);
return;
}
printk(BIOS_DEBUG, "In relocation handler: cpu %d\n", cpu);
/* All threads need to set IEDBASE and SMBASE in the save state area.
* Since one thread runs at a time during the relocation the save state
* is the same for all cpus. */
save_state = (void *)(runtime->smbase + SMM_DEFAULT_SIZE -
runtime->save_state_size);
/* The relocated handler runs with all CPUs concurrently. Therefore
* stagger the entry points adjusting SMBASE downwards by save state
* size * CPU num. */
save_state->smbase = relo_params->smram_base -
cpu * runtime->save_state_size;
save_state->iedbase = relo_params->ied_base;
printk(BIOS_DEBUG, "New SMBASE=0x%08x IEDBASE=0x%08x @ %p\n",
save_state->smbase, save_state->iedbase, save_state);
/* Write EMRR and SMRR MSRs based on indicated support. */
mtrr_cap = rdmsr(MTRRcap_MSR);
if (mtrr_cap.lo & SMRR_SUPPORTED)
write_smrr(relo_params);
if (mtrr_cap.lo & EMRR_SUPPORTED) {
write_emrr(relo_params);
/* UNCORE_EMRR msrs are package level. Therefore, only
* configure these MSRs on the BSP. */
if (cpu == 0)
write_uncore_emrr(relo_params);
}
southbridge_clear_smi_status();
}
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 tsegmb;
u32 bgsm;
u32 emrr_base;
u32 emrr_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 TSEGMB and BGSM registers encompasses the
* SMRAM range as well as the IED range. However, the SMRAM available
* to the handler is 4MiB since the IEDRAM lives TSEGMB + 4MiB.
*/
tsegmb = northbridge_get_base_reg(dev, TSEG);
bgsm = northbridge_get_base_reg(dev, BGSM);
tseg_size = bgsm - tsegmb;
params->smram_base = tsegmb;
params->smram_size = 4 << 20;
params->ied_base = tsegmb + params->smram_size;
params->ied_size = tseg_size - params->smram_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) | MTRRphysMaskValid;
params->smrr_mask.hi = 0;
/* The EMRR and UNCORE_EMRR are at IEDBASE + 2MiB */
emrr_base = (params->ied_base + (2 << 20)) & rmask;
emrr_size = params->ied_size - (2 << 20);
/* EMRR has 46 bits of valid address aligned to 4KiB. It's dependent
* on the number of physical address bits supported. */
params->emrr_base.lo = emrr_base | MTRR_TYPE_WRBACK;
params->emrr_base.hi = 0;
params->emrr_mask.lo = (~(emrr_size - 1) & rmask) | MTRRphysMaskValid;
params->emrr_mask.hi = (1 << (phys_bits - 32)) - 1;
/* UNCORE_EMRR has 39 bits of valid address aligned to 4KiB. */
params->uncore_emrr_base.lo = emrr_base;
params->uncore_emrr_base.hi = 0;
params->uncore_emrr_mask.lo = (~(emrr_size - 1) & rmask) |
MTRRphysMaskValid;
params->uncore_emrr_mask.hi = (1 << (39 - 32)) - 1;
}
static int install_relocation_handler(int num_cpus,
struct smm_relocation_params *relo_params)
{
/* The default SMM entry happens serially at the default location.
* Therefore, there is only 1 concurrent save state area. Set the
* stack size to the save state size, and call into the
* do_relocation handler. */
int save_state_size = sizeof(em64t101_smm_state_save_area_t);
struct smm_loader_params smm_params = {
.per_cpu_stack_size = save_state_size,
.num_concurrent_stacks = num_cpus,
.per_cpu_save_state_size = save_state_size,
.num_concurrent_save_states = 1,
.handler = &cpu_smm_do_relocation,
.handler_arg = (void *)relo_params,
};
return smm_setup_relocation_handler(&smm_params);
}
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));
/* According to the BWG MP init section 2MiB of memory at IEDBASE +
* 2MiB should be zeroed as well. However, I suspect what is inteneded
* is to clear the memory covered by EMRR. TODO(adurbin): figure out if * this is really required. */
//memset(ied_base + (2 << 20), 0, (2 << 20));
}
static int install_permanent_handler(int num_cpus,
struct smm_relocation_params *relo_params)
{
/* There are num_cpus concurrent stacks and num_cpus concurrent save
* state areas. Lastly, set the stack size to the save state size. */
int save_state_size = sizeof(em64t101_smm_state_save_area_t);
struct smm_loader_params smm_params = {
.per_cpu_stack_size = save_state_size,
.num_concurrent_stacks = num_cpus,
.per_cpu_save_state_size = save_state_size,
.num_concurrent_save_states = num_cpus,
};
printk(BIOS_DEBUG, "Installing SMM handler to 0x%08x\n",
relo_params->smram_base);
return smm_load_module((void *)relo_params->smram_base,
relo_params->smram_size, &smm_params);
}
static int cpu_smm_setup(void)
{
device_t dev;
int num_cpus;
msr_t msr;
printk(BIOS_DEBUG, "Setting up SMI for CPU\n");
dev = dev_find_slot(0, PCI_DEVFN(0, 0));
fill_in_relocation_params(dev, &smm_reloc_params);
setup_ied_area(&smm_reloc_params);
msr = rdmsr(CORE_THREAD_COUNT_MSR);
num_cpus = msr.lo & 0xffff;
if (num_cpus > CONFIG_MAX_CPUS) {
printk(BIOS_CRIT,
"Error: Hardware CPUs (%d) > MAX_CPUS (%d)\n",
num_cpus, CONFIG_MAX_CPUS);
}
if (install_relocation_handler(num_cpus, &smm_reloc_params)) {
printk(BIOS_CRIT, "SMM Relocation handler install failed.\n");
return -1;
}
if (install_permanent_handler(num_cpus, &smm_reloc_params)) {
printk(BIOS_CRIT, "SMM Permanent handler install failed.\n");
return -1;
}
/* Ensure the SMM handlers hit DRAM before performing first SMI. */
/* TODO(adurbin): Is this really needed? */
wbinvd();
return 0;
}
void smm_init(void)
{
/* Return early if CPU SMM setup failed. */
if (cpu_smm_setup())
return;
southbridge_smm_init();
/* Initiate first SMI to kick off SMM-context relocation. Note: this
* SMI being triggered here queues up an SMI in the APs which are in
* wait-for-SIPI state. Once an AP gets an SIPI it will service the SMI
* at the SMM_DEFAULT_BASE before jumping to startup vector. */
southbridge_trigger_smi();
printk(BIOS_DEBUG, "Relocation complete.\n");
/* Lock down the SMRAM space. */
smm_lock();
}
void smm_lock(void)
{
/* LOCK the SMM memory window and enable normal SMM.
* After running this function, only a full reset can
* make the SMM registers writable again.
*/
printk(BIOS_DEBUG, "Locking SMM.\n");
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
D_LCK | G_SMRAME | C_BASE_SEG);
}

View File

@ -39,8 +39,6 @@
#include "../../../southbridge/intel/bd82x6x/pch.h" #include "../../../southbridge/intel/bd82x6x/pch.h"
#elif CONFIG_SOUTHBRIDGE_INTEL_I82801IX #elif CONFIG_SOUTHBRIDGE_INTEL_I82801IX
#include "../../../southbridge/intel/i82801ix/i82801ix.h" #include "../../../southbridge/intel/i82801ix/i82801ix.h"
#elif CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT
#include "../../../southbridge/intel/lynxpoint/pch.h"
#else #else
#error "Southbridge needs SMM handler support." #error "Southbridge needs SMM handler support."
#endif #endif
@ -50,9 +48,6 @@
#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE #if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE
#include <northbridge/intel/sandybridge/sandybridge.h> #include <northbridge/intel/sandybridge/sandybridge.h>
#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
#elif CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT
#include <northbridge/intel/haswell/haswell.h>
#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG)
#else #else
#error "Northbridge must define TSEG_BAR." #error "Northbridge must define TSEG_BAR."
#endif #endif

View File

@ -29,13 +29,13 @@
#include <arch/ioapic.h> #include <arch/ioapic.h>
#include <arch/acpi.h> #include <arch/acpi.h>
#include <cpu/cpu.h> #include <cpu/cpu.h>
#include <cpu/x86/smm.h>
#include <elog.h> #include <elog.h>
#include "pch.h" #include "pch.h"
#define NMI_OFF 0 #define NMI_OFF 0
#define ENABLE_ACPI_MODE_IN_COREBOOT 0 #define ENABLE_ACPI_MODE_IN_COREBOOT 0
#define TEST_SMM_FLASH_LOCKDOWN 0
typedef struct southbridge_intel_lynxpoint_config config_t; typedef struct southbridge_intel_lynxpoint_config config_t;
@ -428,58 +428,22 @@ static void enable_clock_gating(device_t dev)
#endif #endif
} }
#if CONFIG_HAVE_SMI_HANDLER static void pch_set_acpi_mode(void)
static void pch_lock_smm(struct device *dev)
{ {
#if TEST_SMM_FLASH_LOCKDOWN #if CONFIG_HAVE_SMI_HANDLER
u8 reg8;
#endif
if (acpi_slp_type != 3) { if (acpi_slp_type != 3) {
#if ENABLE_ACPI_MODE_IN_COREBOOT #if ENABLE_ACPI_MODE_IN_COREBOOT
printk(BIOS_DEBUG, "Enabling ACPI via APMC:\n"); printk(BIOS_DEBUG, "Enabling ACPI via APMC:\n");
outb(0xe1, 0xb2); // Enable ACPI mode outb(APM_CNT_ACPI_ENABLE, APM_CNT);
printk(BIOS_DEBUG, "done.\n"); printk(BIOS_DEBUG, "done.\n");
#else #else
printk(BIOS_DEBUG, "Disabling ACPI via APMC:\n"); printk(BIOS_DEBUG, "Disabling ACPI via APMC:\n");
outb(0x1e, 0xb2); // Disable ACPI mode outb(APM_CNT_ACPI_DISABLE, APM_CNT);
printk(BIOS_DEBUG, "done.\n"); printk(BIOS_DEBUG, "done.\n");
#endif #endif
} }
#endif /* CONFIG_HAVE_SMI_HANDLER */
/* Don't allow evil boot loaders, kernels, or
* userspace applications to deceive us:
*/
smm_lock();
#if TEST_SMM_FLASH_LOCKDOWN
/* Now try this: */
printk(BIOS_DEBUG, "Locking BIOS to RO... ");
reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
(reg8&1)?"rw":"ro");
reg8 &= ~(1 << 0); /* clear BIOSWE */
pci_write_config8(dev, 0xdc, reg8);
reg8 |= (1 << 1); /* set BLE */
pci_write_config8(dev, 0xdc, reg8);
printk(BIOS_DEBUG, "ok.\n");
reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
(reg8&1)?"rw":"ro");
printk(BIOS_DEBUG, "Writing:\n");
*(volatile u8 *)0xfff00000 = 0x00;
printk(BIOS_DEBUG, "Testing:\n");
reg8 |= (1 << 0); /* set BIOSWE */
pci_write_config8(dev, 0xdc, reg8);
reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
(reg8&1)?"rw":"ro");
printk(BIOS_DEBUG, "Done.\n");
#endif
} }
#endif
static void pch_disable_smm_only_flashing(struct device *dev) static void pch_disable_smm_only_flashing(struct device *dev)
{ {
@ -567,9 +531,7 @@ static void lpc_init(struct device *dev)
pch_disable_smm_only_flashing(dev); pch_disable_smm_only_flashing(dev);
#if CONFIG_HAVE_SMI_HANDLER pch_set_acpi_mode();
pch_lock_smm(dev);
#endif
pch_fixups(dev); pch_fixups(dev);
} }

View File

@ -97,6 +97,11 @@ void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue);
void pch_log_state(void); void pch_log_state(void);
#endif #endif
void acpi_create_intel_hpet(acpi_hpet_t * hpet); void acpi_create_intel_hpet(acpi_hpet_t * hpet);
/* These helpers are for performing SMM relocation. */
void southbridge_smm_init(void);
void southbridge_trigger_smi(void);
void southbridge_clear_smi_status(void);
#else #else
void enable_smbus(void); void enable_smbus(void);
void enable_usb_bar(void); void enable_usb_bar(void);

View File

@ -30,13 +30,6 @@
#include <string.h> #include <string.h>
#include "pch.h" #include "pch.h"
#if CONFIG_NORTHBRIDGE_INTEL_HASWELL
#include "northbridge/intel/haswell/haswell.h"
#endif
extern unsigned char _binary_smm_start;
extern unsigned char _binary_smm_size;
/* While we read PMBASE dynamically in case it changed, let's /* While we read PMBASE dynamically in case it changed, let's
* initialize it with a sane value * initialize it with a sane value
*/ */
@ -232,15 +225,17 @@ static void smi_set_eos(void)
outb(reg8, pmbase + SMI_EN); outb(reg8, pmbase + SMI_EN);
} }
extern uint8_t smm_relocation_start, smm_relocation_end; void southbridge_smm_init(void)
static void smm_relocate(void)
{ {
u32 smi_en; u32 smi_en;
u16 pm1_en; u16 pm1_en;
u32 gpe0_en; u32 gpe0_en;
printk(BIOS_DEBUG, "Initializing SMM handler..."); #if CONFIG_ELOG
/* Log events from chipset before clearing */
pch_log_state();
#endif
printk(BIOS_DEBUG, "Initializing Southbridge SMI...");
pmbase = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x1f, 0)), pmbase = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
PMBASE) & 0xff80; PMBASE) & 0xff80;
@ -253,10 +248,6 @@ static void smm_relocate(void)
return; return;
} }
/* copy the SMM relocation code */
memcpy((void *)0x38000, &smm_relocation_start,
&smm_relocation_end - &smm_relocation_start);
printk(BIOS_DEBUG, "\n"); printk(BIOS_DEBUG, "\n");
dump_smi_status(reset_smi_status()); dump_smi_status(reset_smi_status());
dump_pm1_status(reset_pm1_status()); dump_pm1_status(reset_pm1_status());
@ -305,7 +296,10 @@ static void smm_relocate(void)
smi_en |= EOS | GBL_SMI_EN; smi_en |= EOS | GBL_SMI_EN;
outl(smi_en, pmbase + SMI_EN); outl(smi_en, pmbase + SMI_EN);
}
void southbridge_trigger_smi(void)
{
/** /**
* There are several methods of raising a controlled SMI# via * There are several methods of raising a controlled SMI# via
* software, among them: * software, among them:
@ -324,78 +318,18 @@ static void smm_relocate(void)
outb(0x00, 0xb2); outb(0x00, 0xb2);
} }
static int smm_handler_copied = 0; void southbridge_clear_smi_status(void)
static void smm_install(void)
{ {
device_t dev = dev_find_slot(0, PCI_DEVFN(0, 0)); /* Clear SMI status */
u32 smm_base = 0xa0000; reset_smi_status();
struct ied_header ied = {
.signature = "INTEL RSVD",
.size = IED_SIZE,
.reserved = {0},
};
/* The first CPU running this gets to copy the SMM handler. But not all /* Clear PM1 status */
* of them. reset_pm1_status();
*/
if (smm_handler_copied)
return;
smm_handler_copied = 1;
/* enable the SMM memory window */ /* Set EOS bit so other SMIs can occur. */
pci_write_config8(dev, SMRAM, D_OPEN | G_SMRAME | C_BASE_SEG);
#if CONFIG_SMM_TSEG
smm_base = pci_read_config32(dev, TSEG) & ~1;
#endif
/* copy the real SMM handler */
printk(BIOS_DEBUG, "Installing SMM handler to 0x%08x\n", smm_base);
memcpy((void *)smm_base, &_binary_smm_start, (size_t)&_binary_smm_size);
/* copy the IED header into place */
if (CONFIG_SMM_TSEG_SIZE > IED_SIZE) {
/* Top of TSEG region */
smm_base += CONFIG_SMM_TSEG_SIZE - IED_SIZE;
printk(BIOS_DEBUG, "Installing IED header to 0x%08x\n",
smm_base);
memcpy((void *)smm_base, &ied, sizeof(ied));
}
wbinvd();
/* close the SMM memory window and enable normal SMM */
pci_write_config8(dev, SMRAM, G_SMRAME | C_BASE_SEG);
}
void smm_init(void)
{
#if CONFIG_ELOG
/* Log events from chipset before clearing */
pch_log_state();
#endif
/* Put SMM code to 0xa0000 */
smm_install();
/* Put relocation code to 0x38000 and relocate SMBASE */
smm_relocate();
/* We're done. Make sure SMIs can happen! */
smi_set_eos(); smi_set_eos();
} }
void smm_lock(void)
{
/* LOCK the SMM memory window and enable normal SMM.
* After running this function, only a full reset can
* make the SMM registers writable again.
*/
printk(BIOS_DEBUG, "Locking SMM.\n");
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
D_LCK | G_SMRAME | C_BASE_SEG);
}
void smm_setup_structures(void *gnvs, void *tcg, void *smi1) void smm_setup_structures(void *gnvs, void *tcg, void *smi1)
{ {
/* /*

View File

@ -33,12 +33,6 @@
#include "nvs.h" #include "nvs.h"
/* We are using PCIe accesses for now
* 1. the chipset can do it
* 2. we don't need to worry about how we leave 0xcf8/0xcfc behind
*/
#include <northbridge/intel/haswell/haswell.h>
/* While we read PMBASE dynamically in case it changed, let's /* While we read PMBASE dynamically in case it changed, let's
* initialize it with a sane value * initialize it with a sane value
*/ */
@ -53,28 +47,12 @@ static u8 smm_initialized = 0;
/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located /* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
* by coreboot. * by coreboot.
*/ */
static global_nvs_t *gnvs = (global_nvs_t *)0x0; static global_nvs_t *gnvs;
global_nvs_t *smm_get_gnvs(void) global_nvs_t *smm_get_gnvs(void)
{ {
return gnvs; return gnvs;
} }
#if CONFIG_SMM_TSEG
static u32 tseg_base = 0;
u32 smi_get_tseg_base(void)
{
if (!tseg_base)
tseg_base = pci_read_config32(PCI_DEV(0, 0, 0), TSEG) & ~1;
return tseg_base;
}
void tseg_relocate(void **ptr)
{
/* Adjust pointer with TSEG base */
if (*ptr && *ptr < (void*)smi_get_tseg_base())
*ptr = (void *)(((u8*)*ptr) + smi_get_tseg_base());
}
#endif
/** /**
* @brief read and clear PM1_STS * @brief read and clear PM1_STS
* @return PM1_STS register * @return PM1_STS register
@ -290,7 +268,7 @@ static void busmaster_disable_on_bus(int bus)
} }
} }
static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_sleep(void)
{ {
u8 reg8; u8 reg8;
u32 reg32; u32 reg32;
@ -305,8 +283,6 @@ static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *stat
outb(tmp70, 0x70); outb(tmp70, 0x70);
outb(tmp72, 0x72); outb(tmp72, 0x72);
void (*mainboard_sleep)(u8 slp_typ) = mainboard_smi_sleep;
/* First, disable further SMIs */ /* First, disable further SMIs */
reg8 = inb(pmbase + SMI_EN); reg8 = inb(pmbase + SMI_EN);
reg8 &= ~SLP_SMI_EN; reg8 &= ~SLP_SMI_EN;
@ -318,9 +294,7 @@ static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *stat
slp_typ = (reg32 >> 10) & 7; slp_typ = (reg32 >> 10) & 7;
/* Do any mainboard sleep handling */ /* Do any mainboard sleep handling */
tseg_relocate((void **)&mainboard_sleep); mainboard_smi_sleep(slp_typ-2);
if (mainboard_sleep)
mainboard_sleep(slp_typ-2);
#if CONFIG_ELOG_GSMI #if CONFIG_ELOG_GSMI
/* Log S3, S4, and S5 entry */ /* Log S3, S4, and S5 entry */
@ -390,17 +364,14 @@ static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *stat
* core in case we are not running on the same core that * core in case we are not running on the same core that
* initiated the IO transaction. * initiated the IO transaction.
*/ */
/* FIXME: Confirm Haswell's SMM save state area structure. */
static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd) static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd)
{ {
em64t101_smm_state_save_area_t *state; em64t101_smm_state_save_area_t *state;
u32 base = smi_get_tseg_base() + 0x8000 + 0x7d00;
int node; int node;
/* Check all nodes looking for the one that issued the IO */ /* Check all nodes looking for the one that issued the IO */
for (node = 0; node < CONFIG_MAX_CPUS; node++) { for (node = 0; node < CONFIG_MAX_CPUS; node++) {
state = (em64t101_smm_state_save_area_t *) state = smm_get_save_state(node);
(base - (node * 0x400));
/* Check for Synchronous IO (bit0==1) */ /* Check for Synchronous IO (bit0==1) */
if (!(state->io_misc_info & (1 << 0))) if (!(state->io_misc_info & (1 << 0)))
@ -447,11 +418,10 @@ static void southbridge_smi_gsmi(void)
} }
#endif #endif
static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_apmc(void)
{ {
u32 pmctrl; u32 pmctrl;
u8 reg8; u8 reg8;
int (*mainboard_apmc)(u8 apmc) = mainboard_smi_apmc;
em64t101_smm_state_save_area_t *state; em64t101_smm_state_save_area_t *state;
/* Emulate B2 register as the FADT / Linux expects it */ /* Emulate B2 register as the FADT / Linux expects it */
@ -504,12 +474,10 @@ static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state
#endif #endif
} }
tseg_relocate((void **)&mainboard_apmc); mainboard_smi_apmc(reg8);
if (mainboard_apmc)
mainboard_apmc(reg8);
} }
static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_pm1(void)
{ {
u16 pm1_sts; u16 pm1_sts;
@ -530,7 +498,7 @@ static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_
} }
} }
static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_gpe0(void)
{ {
u32 gpe0_sts; u32 gpe0_sts;
@ -538,27 +506,20 @@ static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state
dump_gpe0_status(gpe0_sts); dump_gpe0_status(gpe0_sts);
} }
static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_gpi(void)
{ {
void (*mainboard_gpi)(u16 gpi_sts) = mainboard_smi_gpi;
u16 reg16; u16 reg16;
reg16 = inw(pmbase + ALT_GP_SMI_STS); reg16 = inw(pmbase + ALT_GP_SMI_STS);
outw(reg16, pmbase + ALT_GP_SMI_STS); outw(reg16, pmbase + ALT_GP_SMI_STS);
reg16 &= inw(pmbase + ALT_GP_SMI_EN); reg16 &= inw(pmbase + ALT_GP_SMI_EN);
tseg_relocate((void **)&mainboard_gpi); mainboard_smi_gpi(reg16);
if (mainboard_gpi) {
mainboard_gpi(reg16);
} else {
if (reg16)
printk(BIOS_DEBUG, "GPI (mask %04x)\n",reg16);
}
outw(reg16, pmbase + ALT_GP_SMI_STS); outw(reg16, pmbase + ALT_GP_SMI_STS);
} }
static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_mc(void)
{ {
u32 reg32; u32 reg32;
@ -573,7 +534,7 @@ static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_s
static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_tco(void)
{ {
u32 tco_sts; u32 tco_sts;
@ -610,7 +571,7 @@ static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_
} }
} }
static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_periodic(void)
{ {
u32 reg32; u32 reg32;
@ -623,7 +584,7 @@ static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *s
printk(BIOS_DEBUG, "Periodic SMI.\n"); printk(BIOS_DEBUG, "Periodic SMI.\n");
} }
static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save) static void southbridge_smi_monitor(void)
{ {
#define IOTRAP(x) (trap_sts & (1 << x)) #define IOTRAP(x) (trap_sts & (1 << x))
u32 trap_sts, trap_cycle; u32 trap_sts, trap_cycle;
@ -677,8 +638,7 @@ static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *st
#undef IOTRAP #undef IOTRAP
} }
typedef void (*smi_handler_t)(unsigned int node, typedef void (*smi_handler_t)(void);
smm_state_save_area_t *state_save);
static smi_handler_t southbridge_smi[32] = { static smi_handler_t southbridge_smi[32] = {
NULL, // [0] reserved NULL, // [0] reserved
@ -721,7 +681,7 @@ static smi_handler_t southbridge_smi[32] = {
* @param smm_revision revision of the smm state save map * @param smm_revision revision of the smm state save map
*/ */
void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) void southbridge_smi_handler(void)
{ {
int i, dump = 0; int i, dump = 0;
u32 smi_sts; u32 smi_sts;
@ -738,15 +698,7 @@ void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_sav
for (i = 0; i < 31; i++) { for (i = 0; i < 31; i++) {
if (smi_sts & (1 << i)) { if (smi_sts & (1 << i)) {
if (southbridge_smi[i]) { if (southbridge_smi[i]) {
#if CONFIG_SMM_TSEG southbridge_smi[i]();
smi_handler_t handler = (smi_handler_t)
((u8*)southbridge_smi[i] +
smi_get_tseg_base());
if (handler)
handler(node, state_save);
#else
southbridge_smi[i](node, state_save);
#endif
} else { } else {
printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no " printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
"handler available.\n", i); "handler available.\n", i);