AMD K8/FAM10 SMM support

Changes to last version:
- integrated Rudolf's suggestions
- use normal IO PCI access (and backup 0xcf8 accordingly)
- separate SMM init and locking
- prevent copying SMM code multiple times

Signed-off-by: Stefan Reinauer <stepan@coreboot.org>

Index: src/southbridge/via/vt8237r/vt8237r.h
===================================================================
--- src/southbridge/via/vt8237r/vt8237r.h	(revision 6169)
+++ src/southbridge/via/vt8237r/vt8237r.h	(working copy)
@@ -20,12 +20,11 @@
 #ifndef SOUTHBRIDGE_VIA_VT8237R_VT8237R_H
 #define SOUTHBRIDGE_VIA_VT8237R_VT8237R_H
 
-#include <stdint.h>
-
 /* Static resources for the VT8237R southbridge */
 
 #define VT8237R_APIC_ID			0x2
 #define VT8237R_ACPI_IO_BASE		0x500
+#define DEFAULT_PMBASE			VT8237R_ACPI_IO_BASE
 #define VT8237R_SMBUS_IO_BASE		0x400
 /* 0x0 disabled, 0x2 reserved, 0xf = IRQ15 */
 #define VT8237R_ACPI_IRQ		0x9
@@ -36,6 +35,36 @@
 #endif
 #define VT8237R_HPET_ADDR		0xfed00000ULL
 
+/* PMBASE FIXME mostly taken from ich7 */
+#define PM1_STS		0x00
+#define   WAK_STS	(1 << 15)
+#define   PCIEXPWAK_STS	(1 << 14)
+#define   PRBTNOR_STS	(1 << 11)
+#define   RTC_STS	(1 << 10)
+#define   PWRBTN_STS	(1 << 8)
+#define   GBL_STS	(1 << 5)
+#define   BM_STS	(1 << 4)
+#define   TMROF_STS	(1 << 0)
+#define PM1_EN		0x02
+#define   PCIEXPWAK_DIS	(1 << 14)
+#define   RTC_EN	(1 << 10)
+#define   PWRBTN_EN	(1 << 8)
+#define   GBL_EN	(1 << 5)
+#define   TMROF_EN	(1 << 0)
+#define PM1_CNT		0x04
+#define   SLP_EN	(1 << 13)
+#define   SLP_TYP	(7 << 10)
+#define   GBL_RLS	(1 << 2)
+#define   BM_RLD	(1 << 1)
+#define   SCI_EN	(1 << 0)
+#define PM1_TMR		0x08
+#define PROC_CNT	0x10
+#define LV2		0x14
+#define LV3		0x15
+#define SMI_STS		0x28
+#define SMI_EN		0x2d
+#define EOS		(1 << 0)
+
 /* IDE */
 #define IDE_CS				0x40
 #define IDE_CONF_I			0x41
@@ -107,6 +136,15 @@
 #endif
 ;
 
+#define MAINBOARD_POWER_OFF	0
+#define MAINBOARD_POWER_ON	1
+#define MAINBOARD_POWER_KEEP	2
+
+#ifndef CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL
+#define CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL MAINBOARD_POWER_ON
+#endif
+
+
 #ifdef __PRE_RAM__
 #ifndef __ROMCC__
 u8 smbus_read_byte(u8 dimm, u8 offset);
@@ -119,10 +157,9 @@
 int vt8237_early_network_init(struct vt8237_network_rom *rom);
 #endif
 #else
-#include <device/device.h>
-void writeback(struct device *dev, u16 where, u8 what);
+void writeback(device_t dev, u16 where, u8 what);
 void dump_south(device_t dev);
-u32 vt8237_ide_80pin_detect(struct device *dev);
+u32 vt8237_ide_80pin_detect(device_t dev);
 #endif
 
 #endif
Index: src/southbridge/via/vt8237r/Makefile.inc
===================================================================
--- src/southbridge/via/vt8237r/Makefile.inc	(revision 6169)
+++ src/southbridge/via/vt8237r/Makefile.inc	(working copy)
@@ -25,3 +25,4 @@
 driver-y += usb.c
 driver-$(CONFIG_PIRQ_ROUTE) += pirq.c
 ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += fadt.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c
Index: src/southbridge/via/vt8237r/nvs.h
===================================================================
--- src/southbridge/via/vt8237r/nvs.h	(revision 0)
+++ src/southbridge/via/vt8237r/nvs.h	(revision 0)
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+typedef struct {
+	/* Miscellaneous */
+	u16	osys; /* 0x00 - Operating System */
+	u8	smif; /* 0x02 - SMI function call ("TRAP") */
+	u8	prm0; /* 0x03 - SMI function call parameter */
+	u8	prm1; /* 0x04 - SMI function call parameter */
+	u8	scif; /* 0x05 - SCI function call (via _L00) */
+	u8	prm2; /* 0x06 - SCI function call parameter */
+	u8	prm3; /* 0x07 - SCI function call parameter */
+	u8	lckf; /* 0x08 - Global Lock function for EC */
+	u8	prm4; /* 0x09 - Lock function parameter */
+	u8	prm5; /* 0x0a - Lock function parameter */
+	u32	p80d; /* 0x0b - Debug port (IO 0x80) value */
+	u8	lids; /* 0x0f - LID state (open = 1) */
+	u8	pwrs; /* 0x10 - Power state (AC = 1) */
+	u8	dbgs; /* 0x11 - Debug state */
+	u8	linx; /* 0x12 - Linux OS */
+	u8	dckn; /* 0x13 - PCIe docking state */
+	u8      rsvd[0x28-0x14];
+	/* Processor Identification */
+	u8	apic; /* 0x28 - APIC enabled */
+	u8	mpen; /* 0x29 - MP capable/enabled */
+	u8	pcp0; /* 0x2a - PDC CPU/CORE 0 */
+	u8	pcp1; /* 0x2b - PDC CPU/CORE 1 */
+	u8	ppcm; /* 0x2c - Max. PPC state */
+} __attribute__((packed)) global_nvs_t;
+
Index: src/southbridge/via/vt8237r/lpc.c
===================================================================
--- src/southbridge/via/vt8237r/lpc.c	(revision 6169)
+++ src/southbridge/via/vt8237r/lpc.c	(working copy)
@@ -28,6 +28,7 @@
 #include <pc80/mc146818rtc.h>
 #include <arch/ioapic.h>
 #include <cpu/x86/lapic.h>
+#include <cpu/cpu.h>
 #include <pc80/keyboard.h>
 #include <pc80/i8259.h>
 #include <stdlib.h>
@@ -217,12 +218,9 @@
 	/* Disable SMI on GPIO. */
 	outw(0x0, VT8237R_ACPI_IO_BASE + 0x24);
 
-	/* Disable all global enable SMIs. */
-	outw(0x0, VT8237R_ACPI_IO_BASE + 0x2a);
+	/* Disable all global enable SMIs, except SW SMI */
+	outw(0x40, VT8237R_ACPI_IO_BASE + 0x2a);
 
-	/* All SMI off, both IDE buses ON, PSON rising edge. */
-	outw(0x0, VT8237R_ACPI_IO_BASE + 0x2c);
-
 	/* Primary activity SMI disable. */
 	outl(0x0, VT8237R_ACPI_IO_BASE + 0x34);
 
@@ -238,6 +236,10 @@
 	acpi_slp_type = ((tmp & (7 << 10)) >> 10) == 1 ? 3 : 0 ;
 	printk(BIOS_DEBUG, "SLP_TYP type was %x %x\n", tmp, acpi_slp_type);
 #endif
+
+	/* All SMI on, both IDE buses ON, PSON rising edge. */
+	outw(0x1, VT8237R_ACPI_IO_BASE + 0x2c);
+
 	/* clear sleep */
 	tmp &= ~(7 << 10);
 	tmp |= 1;
@@ -501,6 +503,7 @@
 	/* Enable serial IRQ, 6PCI clocks. */
 	pci_write_config8(dev, 0x52, 0x9);
 
+	smm_lock();
 #endif
 
 	/* Power management setup */
Index: src/southbridge/via/vt8237r/smihandler.c
===================================================================
--- src/southbridge/via/vt8237r/smihandler.c	(revision 0)
+++ src/southbridge/via/vt8237r/smihandler.c	(revision 0)
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2010 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * 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 <arch/io.h>
+#include <arch/romcc_io.h>
+#include <console/console.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/smm.h>
+#include <device/pci_def.h>
+#include "vt8237r.h"
+
+#define APM_CNT		0xb2
+#define   CST_CONTROL	0x85
+#define   PST_CONTROL	0x80
+#define   ACPI_DISABLE	0x1e
+#define   ACPI_ENABLE	0xe1
+#define   GNVS_UPDATE   0xea
+#define APM_STS		0xb3
+
+#include "nvs.h"
+
+/* While we read PMBASE dynamically in case it changed, let's
+ * initialize it with a sane value
+ */
+u16 pmbase = DEFAULT_PMBASE;
+u8 smm_initialized = 0;
+
+/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
+ * by coreboot.
+ */
+global_nvs_t *gnvs = (global_nvs_t *)0x0;
+void *tcg = (void *)0x0;
+void *smi1 = (void *)0x0;
+
+#if 0
+/**
+ * @brief read and clear PM1_STS
+ * @return PM1_STS register
+ */
+static u16 reset_pm1_status(void)
+{
+	u16 reg16;
+
+	reg16 = inw(pmbase + PM1_STS);
+	/* set status bits are cleared by writing 1 to them */
+	outw(reg16, pmbase + PM1_STS);
+
+	return reg16;
+}
+
+static void dump_pm1_status(u16 pm1_sts)
+{
+	printk(BIOS_SPEW, "PM1_STS: ");
+	if (pm1_sts & (1 << 15)) printk(BIOS_SPEW, "WAK ");
+	if (pm1_sts & (1 << 14)) printk(BIOS_SPEW, "PCIEXPWAK ");
+	if (pm1_sts & (1 << 11)) printk(BIOS_SPEW, "PRBTNOR ");
+	if (pm1_sts & (1 << 10)) printk(BIOS_SPEW, "RTC ");
+	if (pm1_sts & (1 <<  8)) printk(BIOS_SPEW, "PWRBTN ");
+	if (pm1_sts & (1 <<  5)) printk(BIOS_SPEW, "GBL ");
+	if (pm1_sts & (1 <<  4)) printk(BIOS_SPEW, "BM ");
+	if (pm1_sts & (1 <<  0)) printk(BIOS_SPEW, "TMROF ");
+	printk(BIOS_SPEW, "\n");
+	int reg16 = inw(pmbase + PM1_EN);
+	printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
+}
+#endif
+
+/**
+ * @brief read and clear SMI_STS
+ * @return SMI_STS register
+ */
+static u16 reset_smi_status(void)
+{
+	u16 reg16;
+
+	reg16 = inw(pmbase + SMI_STS);
+	/* set status bits are cleared by writing 1 to them */
+	outw(reg16, pmbase + SMI_STS);
+
+	return reg16;
+}
+
+static void dump_smi_status(u16 smi_sts)
+{
+	printk(BIOS_DEBUG, "SMI_STS: ");
+	if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "GPIO_RANGE_1 ");
+	if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "GPIO_RANGE_0 ");
+	if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "GP3_TIMEOUT ");
+	if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "GP2_TIMEOUT ");
+	if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "SERR_IRQ ");
+	if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "PMIO_5 ");
+	if (smi_sts & (1 <<  9)) printk(BIOS_DEBUG, "THRMTRIP# ");
+	if (smi_sts & (1 <<  8)) printk(BIOS_DEBUG, "CLKRUN# ");
+	if (smi_sts & (1 <<  7)) printk(BIOS_DEBUG, "PRIMARY_IRQ/NMI/SMI ");
+	if (smi_sts & (1 <<  6)) printk(BIOS_DEBUG, "SWSMI ");
+	if (smi_sts & (1 <<  5)) printk(BIOS_DEBUG, "BIOS_STATUS ");
+	if (smi_sts & (1 <<  4)) printk(BIOS_DEBUG, "LEGACY_USB ");
+	if (smi_sts & (1 <<  3)) printk(BIOS_DEBUG, "GP1_TIMEOUT ");
+	if (smi_sts & (1 <<  2)) printk(BIOS_DEBUG, "GP0_TIMEOUT ");
+	if (smi_sts & (1 <<  1)) printk(BIOS_DEBUG, "SECONDARY_EVENT_TIMEOUT ");
+	if (smi_sts & (1 <<  0)) printk(BIOS_DEBUG, "PRIMARY_ACTIVITY ");
+	printk(BIOS_DEBUG, "\n");
+}
+
+int southbridge_io_trap_handler(int smif)
+{
+	switch (smif) {
+	case 0x32:
+		printk(BIOS_DEBUG, "OS Init\n");
+		/* gnvs->smif:
+		 *  On success, the IO Trap Handler returns 0
+		 *  On failure, the IO Trap Handler returns a value != 0
+		 */
+		gnvs->smif = 0;
+		return 1; /* IO trap handled */
+	}
+
+	/* Not handled */
+	return 0;
+}
+
+/**
+ * @brief Set the EOS bit
+ */
+void southbridge_smi_set_eos(void)
+{
+	u8 reg8;
+
+	reg8 = inb(pmbase + SMI_EN);
+	reg8 |= EOS;
+	outb(reg8, pmbase + SMI_EN);
+}
+
+static void southbridge_smi_cmd(unsigned int node, smm_state_save_area_t *state_save)
+{
+	u16 pmctrl;
+	u8 reg8;
+
+	reg8 = inb(pmbase + 0x2f);
+	switch (reg8) {
+	case CST_CONTROL:
+		/* Calling this function seems to cause
+		 * some kind of race condition in Linux
+		 * and causes a kernel oops
+		 */
+		printk(BIOS_DEBUG, "C-state control\n");
+		break;
+	case PST_CONTROL:
+		/* Calling this function seems to cause
+		 * some kind of race condition in Linux
+		 * and causes a kernel oops
+		 */
+		printk(BIOS_DEBUG, "P-state control\n");
+		break;
+	case ACPI_DISABLE:
+		pmctrl = inw(pmbase + PM1_CNT);
+		pmctrl &= ~SCI_EN;
+		outw(pmctrl, pmbase + PM1_CNT);
+		printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
+		break;
+	case ACPI_ENABLE:
+		pmctrl = inw(pmbase + PM1_CNT);
+		pmctrl |= SCI_EN;
+		outw(pmctrl, pmbase + PM1_CNT);
+		printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
+		break;
+	case GNVS_UPDATE:
+		if (smm_initialized) {
+			printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
+			return;
+		}
+		gnvs = *(global_nvs_t **)0x500;
+		tcg  = *(void **)0x504;
+		smi1 = *(void **)0x508;
+		smm_initialized = 1;
+		printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
+		break;
+	default:
+		printk(BIOS_DEBUG, "SMI#: Unknown function SMI_CMD=%02x\n", reg8);
+	}
+}
+
+typedef void (*smi_handler_t)(unsigned int node,
+		smm_state_save_area_t *state_save);
+
+smi_handler_t southbridge_smi[32] = {
+	NULL,			  //  [0]
+	NULL,			  //  [1]
+	NULL,			  //  [2]
+	NULL,			  //  [3]
+	southbridge_smi_cmd,	  //  [4]
+	NULL,			  //  [5]
+	NULL,			  //  [6]
+	NULL,			  //  [7]
+	NULL,			  //  [8]
+	NULL,			  //  [9]
+	NULL,			  // [10]
+	NULL,			  // [11]
+	NULL,			  // [12]
+	NULL,			  // [13]
+	NULL,			  // [14]
+	NULL,			  // [15]
+};
+
+/**
+ * @brief Interrupt handler for SMI#
+ *
+ * @param smm_revision revision of the smm state save map
+ */
+
+void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
+{
+	int i, dump = 0;
+	u32 smi_sts;
+
+	/* Update global variable pmbase */
+	pmbase = pci_read_config16(PCI_DEV(0, 0x11, 0), 0x88) & 0xfffc;
+
+	/* We need to clear the SMI status registers, or we won't see what's
+	 * happening in the following calls.
+	 */
+	smi_sts = reset_smi_status();
+
+	/* Filter all non-enabled SMI events */
+	// FIXME Double check, this clears MONITOR
+	// smi_sts &= inl(pmbase + SMI_EN);
+
+	/* Call SMI sub handler for each of the status bits */
+	for (i = 0; i < 16; i++) {
+		if (smi_sts & (1 << i)) {
+			if (southbridge_smi[i])
+				southbridge_smi[i](node, state_save);
+			else {
+				printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
+						"handler available.\n", i);
+				dump = 1;
+			}
+		}
+	}
+
+	if(dump) {
+		dump_smi_status(smi_sts);
+	}
+
+}
Index: src/southbridge/intel/i82801gx/i82801gx.h
===================================================================
--- src/southbridge/intel/i82801gx/i82801gx.h	(revision 6169)
+++ src/southbridge/intel/i82801gx/i82801gx.h	(working copy)
@@ -38,19 +38,17 @@
 #ifndef __ACPI__
 #define DEBUG_PERIODIC_SMIS 0
 
-#if !defined(ASSEMBLY)
+#if !defined(ASSEMBLY) && !defined(__ROMCC__)
 #if !defined(__PRE_RAM__)
 #include "chip.h"
 extern void i82801gx_enable(device_t dev);
+#else
+void enable_smbus(void);
+int smbus_read_byte(unsigned device, unsigned address);
 #endif
 void i82801gx_enable_usbdebug(unsigned int port);
 #endif
 
-#if defined(__PRE_RAM__) && !defined(__ROMCC__) && !defined(ASSEMBLY)
-void enable_smbus(void);
-int smbus_read_byte(unsigned device, unsigned address);
-#endif
-
 #define MAINBOARD_POWER_OFF	0
 #define MAINBOARD_POWER_ON	1
 #define MAINBOARD_POWER_KEEP	2
Index: src/southbridge/intel/i82801gx/lpc.c
===================================================================
--- src/southbridge/intel/i82801gx/lpc.c	(revision 6169)
+++ src/southbridge/intel/i82801gx/lpc.c	(working copy)
@@ -27,6 +27,7 @@
 #include <pc80/i8259.h>
 #include <arch/io.h>
 #include <arch/ioapic.h>
+#include <cpu/cpu.h>
 #include "i82801gx.h"
 
 #define NMI_OFF	0
@@ -335,7 +336,6 @@
 #if CONFIG_HAVE_SMI_HANDLER
 static void i82801gx_lock_smm(struct device *dev)
 {
-	void smm_lock(void);
 #if TEST_SMM_FLASH_LOCKDOWN
 	u8 reg8;
 #endif
Index: src/southbridge/intel/i82801gx/smi.c
===================================================================
--- src/southbridge/intel/i82801gx/smi.c	(revision 6169)
+++ src/southbridge/intel/i82801gx/smi.c	(working copy)
@@ -318,8 +318,17 @@
 	outb(0x00, 0xb2);
 }
 
+static int smm_handler_copied = 0;
+
 static void smm_install(void)
 {
+	/* The first CPU running this gets to copy the SMM handler. But not all
+	 * of them.
+	 */
+	if (smm_handler_copied)
+		return;
+	smm_handler_copied = 1;
+
 	/* enable the SMM memory window */
 	pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
 				D_OPEN | G_SMRAME | C_BASE_SEG);
Index: src/include/cpu/x86/smm.h
===================================================================
--- src/include/cpu/x86/smm.h	(revision 6169)
+++ src/include/cpu/x86/smm.h	(working copy)
@@ -254,7 +254,7 @@
 
 void io_trap_handler(int smif);
 int southbridge_io_trap_handler(int smif);
-int mainboard_io_trap_handler(int smif);
+int __attribute__((weak)) mainboard_io_trap_handler(int smif);
 
 void southbridge_smi_set_eos(void);
 
Index: src/cpu/amd/socket_S1G1/Makefile.inc
===================================================================
--- src/cpu/amd/socket_S1G1/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_S1G1/Makefile.inc	(working copy)
@@ -7,7 +7,8 @@
 subdirs-y += ../../x86/lapic
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/smm
-subdirs-y += ../../x86/mtrr
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_940/Makefile.inc
===================================================================
--- src/cpu/amd/socket_940/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_940/Makefile.inc	(working copy)
@@ -8,5 +8,7 @@
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/smm
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/model_fxx/model_fxx_init.c
===================================================================
--- src/cpu/amd/model_fxx/model_fxx_init.c	(revision 6169)
+++ src/cpu/amd/model_fxx/model_fxx_init.c	(working copy)
@@ -499,11 +499,6 @@
 
 	k8_errata();
 
-	/* Set SMMLOCK to avoid exploits messing with SMM */
-	msr = rdmsr(HWCR_MSR);
-	msr.lo |= (1 << 0);
-	wrmsr(HWCR_MSR, msr);
-
 	enable_cache();
 
 	/* Set the processor name string */
Index: src/cpu/amd/socket_754/Makefile.inc
===================================================================
--- src/cpu/amd/socket_754/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_754/Makefile.inc	(working copy)
@@ -8,5 +8,7 @@
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/smm
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_AM2/Makefile.inc
===================================================================
--- src/cpu/amd/socket_AM2/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_AM2/Makefile.inc	(working copy)
@@ -9,5 +9,6 @@
 subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/pae
 subdirs-y += ../../x86/smm
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_AM3/Makefile.inc
===================================================================
--- src/cpu/amd/socket_AM3/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_AM3/Makefile.inc	(working copy)
@@ -7,7 +7,8 @@
 subdirs-y += ../../x86/lapic
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/smm
-subdirs-y += ../../x86/mtrr
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_AM2r2/Makefile.inc
===================================================================
--- src/cpu/amd/socket_AM2r2/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_AM2r2/Makefile.inc	(working copy)
@@ -7,7 +7,8 @@
 subdirs-y += ../../x86/lapic
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/smm
-subdirs-y += ../../x86/mtrr
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_939/Makefile.inc
===================================================================
--- src/cpu/amd/socket_939/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_939/Makefile.inc	(working copy)
@@ -8,5 +8,7 @@
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/smm
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/smm/smm_init.c
===================================================================
--- src/cpu/amd/smm/smm_init.c	(revision 0)
+++ src/cpu/amd/smm/smm_init.c	(revision 0)
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 coresystems GmbH
+ * Copyright (C) 2010 Rudolf Marek
+ *
+ * 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 <console/console.h>
+#include <arch/io.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/amd/mtrr.h>
+#include <cpu/amd/model_fxx_msr.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/smm.h>
+#include <string.h>
+
+#define SMM_BASE_MSR 0xc0010111
+#define SMM_ADDR_MSR 0xc0010112
+#define SMM_MASK_MSR 0xc0010113
+#define SMM_BASE 0xa0000
+
+extern unsigned char _binary_smm_start;
+extern unsigned char _binary_smm_size;
+
+static int smm_handler_copied = 0;
+
+void smm_init(void)
+{
+	msr_t msr;
+
+	msr = rdmsr(HWCR_MSR);
+	if (msr.lo & (1 << 0)) {
+		// This sounds like a bug... ? 
+		printk(BIOS_DEBUG, "SMM is still locked from last boot, using old handler.\n");
+		return;
+	}
+
+	/* Only copy SMM handler once, not once per CPU */
+	if (!smm_handler_copied) {
+		msr_t syscfg_orig, mtrr_aseg_orig;
+
+		smm_handler_copied = 1;
+
+		// XXX Really?
+		disable_cache();
+		syscfg_orig = rdmsr(SYSCFG_MSR);
+		mtrr_aseg_orig = rdmsr(MTRRfix16K_A0000_MSR);
+
+		// XXX Why?
+		msr = syscfg_orig;
+		msr.lo |= SYSCFG_MSR_MtrrFixDramModEn;
+		msr.lo &= ~SYSCFG_MSR_MtrrFixDramEn;
+		wrmsr(SYSCFG_MSR, msr);
+
+		/* set DRAM access to 0xa0000 */
+		// XXX but why?
+		msr.lo = 0x18181818;
+		msr.hi = 0x18181818;
+		wrmsr(MTRRfix16K_A0000_MSR, msr);
+#if 0 // obviously wrong stuff from Rudolf's patch
+		msr.lo |= SYSCFG_MSR_MtrrFixDramEn;
+		wrmsr(SYSCFG_MSR, msr);
+#endif
+
+		/* enable the SMM memory window */
+		msr = rdmsr(SMM_MASK_MSR);
+		msr.lo |= (1 << 0); // Enable ASEG SMRAM Range
+		msr.lo &= ~(1 << 2); // Open ASEG SMRAM Range
+		wrmsr(SMM_MASK_MSR, msr);
+
+		/* copy the real SMM handler */
+		memcpy((void *)SMM_BASE, &_binary_smm_start, (size_t)&_binary_smm_size);
+		wbinvd();
+
+		msr = rdmsr(SMM_MASK_MSR);
+		msr.lo |= ~(1 << 2); // Close ASEG SMRAM Range
+		wrmsr(SMM_MASK_MSR, msr);
+
+#if 0 // obviously wrong stuff from Rudolf's patch
+		msr.lo &= ~SYSCFG_MSR_MtrrFixDramEn;
+		wrmsr(SYSCFG_MSR, msr);
+#endif
+		// XXX But why?
+		wrmsr(MTRRfix16K_A0000_MSR, mtrr_aseg_orig);
+		wrmsr(SYSCFG_MSR, syscfg_orig);
+		enable_cache();
+
+	}
+
+	/* But set SMM base address on all CPUs/cores */
+	msr = rdmsr(SMM_BASE_MSR);
+	msr.lo = SMM_BASE;
+	wrmsr(SMM_BASE_MSR, msr);
+}
+
+void smm_lock(void)
+{
+	msr_t msr;
+
+	printk(BIOS_DEBUG, "Locking SMM.\n");
+
+	/* Set SMMLOCK to avoid exploits messing with SMM */
+	msr = rdmsr(HWCR_MSR);
+	msr.lo |= (1 << 0);
+	wrmsr(HWCR_MSR, msr);
+}
Index: src/cpu/amd/smm/Makefile.inc
===================================================================
--- src/cpu/amd/smm/Makefile.inc	(revision 0)
+++ src/cpu/amd/smm/Makefile.inc	(revision 0)
@@ -0,0 +1,2 @@
+
+ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smm_init.c
Index: src/cpu/amd/socket_F/Makefile.inc
===================================================================
--- src/cpu/amd/socket_F/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_F/Makefile.inc	(working copy)
@@ -9,5 +9,6 @@
 subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/pae
 subdirs-y += ../../x86/smm
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_ASB2/Makefile.inc
===================================================================
--- src/cpu/amd/socket_ASB2/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_ASB2/Makefile.inc	(working copy)
@@ -7,7 +7,8 @@
 subdirs-y += ../../x86/lapic
 subdirs-y += ../../x86/cache
 subdirs-y += ../../x86/pae
+subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/smm
-subdirs-y += ../../x86/mtrr
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/amd/socket_F_1207/Makefile.inc
===================================================================
--- src/cpu/amd/socket_F_1207/Makefile.inc	(revision 6169)
+++ src/cpu/amd/socket_F_1207/Makefile.inc	(working copy)
@@ -9,5 +9,6 @@
 subdirs-y += ../../x86/mtrr
 subdirs-y += ../../x86/pae
 subdirs-y += ../../x86/smm
+subdirs-y += ../smm
 
 cpu_incs += $(src)/cpu/amd/car/cache_as_ram.inc
Index: src/cpu/x86/smm/smihandler.c
===================================================================
--- src/cpu/x86/smm/smihandler.c	(revision 6169)
+++ src/cpu/x86/smm/smihandler.c	(working copy)
@@ -87,7 +87,26 @@
 	southbridge_smi_set_eos();
 }
 
+static u32 pci_orig;
+
 /**
+ * @brief Backup PCI address to make sure we do not mess up the OS
+ */
+static void smi_backup_pci_address(void)
+{
+	pci_orig = inl(0xcf8);
+}
+
+/**
+ * @brief Restore PCI address previously backed up
+ */
+static void smi_restore_pci_address(void)
+{
+	outl(pci_orig, 0xcf8);
+}
+
+
+/**
  * @brief Interrupt handler for SMI#
  *
  * @param smm_revision revision of the smm state save map
@@ -107,6 +126,8 @@
 		return;
 	}
 
+	smi_backup_pci_address();
+
 	node=nodeid();
 
 	console_init();
@@ -147,6 +168,8 @@
 	if (southbridge_smi_handler)
 		southbridge_smi_handler(node, &state_save);
 
+	smi_restore_pci_address();
+
 	smi_release_lock();
 
 	/* De-assert SMI# signal to allow another SMI */
Index: src/cpu/x86/smm/smmrelocate.S
===================================================================
--- src/cpu/x86/smm/smmrelocate.S	(revision 6169)
+++ src/cpu/x86/smm/smmrelocate.S	(working copy)
@@ -22,6 +22,8 @@
 // Make sure no stage 2 code is included:
 #define __PRE_RAM__
 
+#if !defined(CONFIG_NORTHBRIDGE_AMD_AMDK8) && !defined(CONFIG_NORTHBRIDGE_AMD_FAM10)
+
 // FIXME: Is this piece of code southbridge specific, or
 // can it be cleaned up so this include is not required?
 // It's needed right now because we get our DEFAULT_PMBASE from
@@ -175,4 +177,4 @@
 	/* That's it. return */
 	rsm
 smm_relocation_end:
-
+#endif
Index: src/mainboard/asus/m2v-mx_se/Kconfig
===================================================================
--- src/mainboard/asus/m2v-mx_se/Kconfig	(revision 6169)
+++ src/mainboard/asus/m2v-mx_se/Kconfig	(working copy)
@@ -37,6 +37,7 @@
 	select HAVE_ACPI_RESUME
 	select HAVE_MAINBOARD_RESOURCES
 	select QRANK_DIMM_SUPPORT
+	select HAVE_SMI_HANDLER
 	select SET_FIDVID
 
 config MAINBOARD_DIR
