blob: 977df3cacce2469e98b03e2892813af02b03cf5d [file] [log] [blame]
Stefan Reinauer269563a2009-01-19 21:20:22 +00001/*
2 * This file is part of the coreboot project.
3 *
Stefan Reinauerc35a4512009-10-24 17:59:36 +00004 * Copyright (C) 2008-2009 coresystems GmbH
Stefan Reinauer269563a2009-01-19 21:20:22 +00005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Stefan Reinauer269563a2009-01-19 21:20:22 +000015 */
16
17#include <arch/io.h>
Stefan Reinauer269563a2009-01-19 21:20:22 +000018#include <console/console.h>
19#include <cpu/x86/cache.h>
20#include <cpu/x86/smm.h>
21
David Hendricksbb0d5ef2014-06-19 15:39:29 -070022#if CONFIG_SPI_FLASH_SMM
23#include <spi-generic.h>
24#endif
25
26static int do_driver_init = 1;
27
Stefan Reinauer269563a2009-01-19 21:20:22 +000028typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore;
29
30/* SMI multiprocessing semaphore */
Stefan Reinauer3aa067f2012-04-02 13:24:04 -070031static volatile smi_semaphore smi_handler_status __attribute__ ((aligned (4))) = SMI_UNLOCKED;
Stefan Reinauer269563a2009-01-19 21:20:22 +000032
33static int smi_obtain_lock(void)
34{
35 u8 ret = SMI_LOCKED;
36
37 asm volatile (
38 "movb %2, %%al\n"
39 "xchgb %%al, %1\n"
40 "movb %%al, %0\n"
41 : "=g" (ret), "=m" (smi_handler_status)
42 : "g" (SMI_LOCKED)
43 : "eax"
44 );
45
46 return (ret == SMI_UNLOCKED);
47}
48
Sven Schnellebfe8e512011-06-14 20:55:54 +020049void smi_release_lock(void)
Stefan Reinauer269563a2009-01-19 21:20:22 +000050{
51 asm volatile (
52 "movb %1, %%al\n"
53 "xchgb %%al, %0\n"
54 : "=m" (smi_handler_status)
55 : "g" (SMI_UNLOCKED)
56 : "eax"
57 );
58}
59
60#define LAPIC_ID 0xfee00020
61static inline __attribute__((always_inline)) unsigned long nodeid(void)
62{
63 return (*((volatile unsigned long *)(LAPIC_ID)) >> 24);
64}
65
Stefan Reinauer269563a2009-01-19 21:20:22 +000066void io_trap_handler(int smif)
67{
Stefan Reinauer3b387452009-03-06 19:52:36 +000068 /* If a handler function handled a given IO trap, it
69 * shall return a non-zero value
70 */
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000071 printk(BIOS_DEBUG, "SMI function trap 0x%x: ", smif);
Stefan Reinauer3b387452009-03-06 19:52:36 +000072
73 if (southbridge_io_trap_handler(smif))
74 return;
75
76 if (mainboard_io_trap_handler(smif))
77 return;
78
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000079 printk(BIOS_DEBUG, "Unknown function\n");
Stefan Reinauer269563a2009-01-19 21:20:22 +000080}
81
82/**
83 * @brief Set the EOS bit
84 */
85static void smi_set_eos(void)
86{
87 southbridge_smi_set_eos();
88}
89
Stefan Reinauercadc5452010-12-18 23:29:37 +000090static u32 pci_orig;
91
92/**
93 * @brief Backup PCI address to make sure we do not mess up the OS
94 */
95static void smi_backup_pci_address(void)
96{
97 pci_orig = inl(0xcf8);
98}
99
100/**
101 * @brief Restore PCI address previously backed up
102 */
103static void smi_restore_pci_address(void)
104{
105 outl(pci_orig, 0xcf8);
106}
107
Stefan Reinauer01327d12015-07-30 16:28:44 -0700108static inline void *smm_save_state(uintptr_t base, int arch_offset, int node)
Aaron Durbin62f100b2012-11-07 12:27:29 -0600109{
110 base += SMM_SAVE_STATE_BEGIN(arch_offset) - (node * 0x400);
111 return (void *)base;
112}
113
Stefan Reinauer269563a2009-01-19 21:20:22 +0000114/**
115 * @brief Interrupt handler for SMI#
116 *
117 * @param smm_revision revision of the smm state save map
118 */
119
120void smi_handler(u32 smm_revision)
121{
Stefan Reinauer269563a2009-01-19 21:20:22 +0000122 unsigned int node;
123 smm_state_save_area_t state_save;
Aaron Durbin62f100b2012-11-07 12:27:29 -0600124 u32 smm_base = 0xa0000; /* ASEG */
Stefan Reinauer269563a2009-01-19 21:20:22 +0000125
126 /* Are we ok to execute the handler? */
Stefan Reinauerc35a4512009-10-24 17:59:36 +0000127 if (!smi_obtain_lock()) {
128 /* For security reasons we don't release the other CPUs
129 * until the CPU with the lock is actually done
130 */
Rudolf Marek7f762902011-07-02 16:03:24 +0200131 while (smi_handler_status == SMI_LOCKED) {
132 asm volatile (
133 ".byte 0xf3, 0x90\n" /* hint a CPU we are in spinlock (PAUSE instruction, REP NOP) */
134 );
135 }
Stefan Reinauer269563a2009-01-19 21:20:22 +0000136 return;
Stefan Reinauerc35a4512009-10-24 17:59:36 +0000137 }
Stefan Reinauer269563a2009-01-19 21:20:22 +0000138
Stefan Reinauercadc5452010-12-18 23:29:37 +0000139 smi_backup_pci_address();
140
Stefan Reinauer269563a2009-01-19 21:20:22 +0000141 node=nodeid();
142
Stefan Reinauerc35a4512009-10-24 17:59:36 +0000143 console_init();
Stefan Reinauer269563a2009-01-19 21:20:22 +0000144
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000145 printk(BIOS_SPEW, "\nSMI# #%d\n", node);
Stefan Reinauer269563a2009-01-19 21:20:22 +0000146
147 switch (smm_revision) {
Stefan Reinauer881a5532010-02-22 09:32:33 +0000148 case 0x00030002:
Stefan Reinauer269563a2009-01-19 21:20:22 +0000149 case 0x00030007:
150 state_save.type = LEGACY;
Aaron Durbin62f100b2012-11-07 12:27:29 -0600151 state_save.legacy_state_save =
152 smm_save_state(smm_base, 0x7e00, node);
Stefan Reinauer269563a2009-01-19 21:20:22 +0000153 break;
154 case 0x00030100:
155 state_save.type = EM64T;
Aaron Durbin62f100b2012-11-07 12:27:29 -0600156 state_save.em64t_state_save =
157 smm_save_state(smm_base, 0x7d00, node);
Patrick Georgi20e2f3c2014-08-09 20:52:21 +0200158 break;
Aaron Durbin62f100b2012-11-07 12:27:29 -0600159 case 0x00030101: /* SandyBridge, IvyBridge, and Haswell */
Duncan Laurie51cb26d2012-06-23 15:22:43 -0700160 state_save.type = EM64T101;
161 state_save.em64t101_state_save =
Aaron Durbin62f100b2012-11-07 12:27:29 -0600162 smm_save_state(smm_base,
163 SMM_EM64T101_ARCH_OFFSET, node);
Stefan Reinauer269563a2009-01-19 21:20:22 +0000164 break;
165 case 0x00030064:
166 state_save.type = AMD64;
Aaron Durbin62f100b2012-11-07 12:27:29 -0600167 state_save.amd64_state_save =
168 smm_save_state(smm_base, 0x7e00, node);
Stefan Reinauer269563a2009-01-19 21:20:22 +0000169 break;
170 default:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000171 printk(BIOS_DEBUG, "smm_revision: 0x%08x\n", smm_revision);
172 printk(BIOS_DEBUG, "SMI# not supported on your CPU\n");
Stefan Reinauer269563a2009-01-19 21:20:22 +0000173 /* Don't release lock, so no further SMI will happen,
174 * if we don't handle it anyways.
175 */
176 return;
177 }
178
David Hendricksbb0d5ef2014-06-19 15:39:29 -0700179 /* Allow drivers to initialize variables in SMM context. */
180 if (do_driver_init) {
181#if CONFIG_SPI_FLASH_SMM
182 spi_init();
183#endif
184 do_driver_init = 0;
185 }
186
Stefan Reinauer92b85aa2010-02-22 14:55:16 +0000187 /* Call chipset specific SMI handlers. */
Kyösti Mälkki48b3dbc2014-12-29 19:36:50 +0200188 cpu_smi_handler(node, &state_save);
189 northbridge_smi_handler(node, &state_save);
190 southbridge_smi_handler(node, &state_save);
Stefan Reinauer269563a2009-01-19 21:20:22 +0000191
Stefan Reinauercadc5452010-12-18 23:29:37 +0000192 smi_restore_pci_address();
193
Stefan Reinauer269563a2009-01-19 21:20:22 +0000194 smi_release_lock();
Stefan Reinauer269563a2009-01-19 21:20:22 +0000195
196 /* De-assert SMI# signal to allow another SMI */
197 smi_set_eos();
198}
Kyösti Mälkki48b3dbc2014-12-29 19:36:50 +0200199
200/* Provide a default implementation for all weak handlers so that relocation
201 * entries in the modules make sense. Without default implementations the
202 * weak relocations w/o a symbol have a 0 address which is where the modules
203 * are linked at. */
204int __attribute__((weak)) mainboard_io_trap_handler(int smif) { return 0; }
205void __attribute__((weak)) cpu_smi_handler(unsigned int node, smm_state_save_area_t *state_save) {}
206void __attribute__((weak)) northbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) {}
207void __attribute__((weak)) southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) {}
208void __attribute__((weak)) mainboard_smi_gpi(u32 gpi_sts) {}
209int __attribute__((weak)) mainboard_smi_apmc(u8 data) { return 0; }
210void __attribute__((weak)) mainboard_smi_sleep(u8 slp_typ) {}