blob: 49a2c1fe697842828cc3580d32277569057fb98a [file] [log] [blame]
Aaron Durbin50a34642013-01-03 17:38:47 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 ChromeOS Authors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Aaron Durbin50a34642013-01-03 17:38:47 -060014 */
15
16#include <arch/io.h>
Aaron Durbin50a34642013-01-03 17:38:47 -060017#include <console/console.h>
18#include <cpu/x86/smm.h>
Aaron Durbin3eb8eb72014-03-10 16:13:58 -050019#include <rmodule.h>
Aaron Durbin50a34642013-01-03 17:38:47 -060020
David Hendricksbb0d5ef2014-06-19 15:39:29 -070021#if CONFIG_SPI_FLASH_SMM
22#include <spi-generic.h>
23#endif
24
25static int do_driver_init = 1;
26
Aaron Durbin50a34642013-01-03 17:38:47 -060027typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore;
28
29/* SMI multiprocessing semaphore */
30static volatile
31smi_semaphore smi_handler_status __attribute__ ((aligned (4))) = SMI_UNLOCKED;
32
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
49static void smi_release_lock(void)
50{
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
60void io_trap_handler(int smif)
61{
62 /* If a handler function handled a given IO trap, it
63 * shall return a non-zero value
64 */
65 printk(BIOS_DEBUG, "SMI function trap 0x%x: ", smif);
66
67 if (southbridge_io_trap_handler(smif))
68 return;
69
70 if (mainboard_io_trap_handler(smif))
71 return;
72
73 printk(BIOS_DEBUG, "Unknown function\n");
74}
75
76/**
77 * @brief Set the EOS bit
78 */
79static void smi_set_eos(void)
80{
81 southbridge_smi_set_eos();
82}
83
84
85static u32 pci_orig;
86
87/**
88 * @brief Backup PCI address to make sure we do not mess up the OS
89 */
90static void smi_backup_pci_address(void)
91{
92 pci_orig = inl(0xcf8);
93}
94
95/**
96 * @brief Restore PCI address previously backed up
97 */
98static void smi_restore_pci_address(void)
99{
100 outl(pci_orig, 0xcf8);
101}
102
103
104static const struct smm_runtime *smm_runtime;
105
106void *smm_get_save_state(int cpu)
107{
108 char *base;
109
110 /* This function assumes all save states start at top of default
111 * SMRAM size space and are staggered down by save state size. */
112 base = (void *)smm_runtime->smbase;
113 base += SMM_DEFAULT_SIZE;
114 base -= (cpu + 1) * smm_runtime->save_state_size;
115
116 return base;
117}
118
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500119void asmlinkage smm_handler_start(void *arg)
Aaron Durbin50a34642013-01-03 17:38:47 -0600120{
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500121 const struct smm_module_params *p;
122 const struct smm_runtime *runtime;
123 int cpu;
124
125 p = arg;
126 runtime = p->runtime;
127 cpu = p->cpu;
128
Aaron Durbin50a34642013-01-03 17:38:47 -0600129 /* Make sure to set the global runtime. It's OK to race as the value
130 * will be the same across CPUs as well as multiple SMIs. */
131 if (smm_runtime == NULL)
132 smm_runtime = runtime;
133
134 if (cpu >= CONFIG_MAX_CPUS) {
135 console_init();
136 printk(BIOS_CRIT,
137 "Invalid CPU number assigned in SMM stub: %d\n", cpu);
138 return;
139 }
140
141 /* Are we ok to execute the handler? */
142 if (!smi_obtain_lock()) {
143 /* For security reasons we don't release the other CPUs
144 * until the CPU with the lock is actually done */
145 while (smi_handler_status == SMI_LOCKED) {
146 asm volatile (
147 ".byte 0xf3, 0x90\n" /* PAUSE */
148 );
149 }
150 return;
151 }
152
153 smi_backup_pci_address();
154
155 console_init();
156
157 printk(BIOS_SPEW, "\nSMI# #%d\n", cpu);
158
David Hendricksbb0d5ef2014-06-19 15:39:29 -0700159 /* Allow drivers to initialize variables in SMM context. */
160 if (do_driver_init) {
161#if CONFIG_SPI_FLASH_SMM
162 spi_init();
163#endif
164 do_driver_init = 0;
165 }
166
Aaron Durbin50a34642013-01-03 17:38:47 -0600167 cpu_smi_handler();
168 northbridge_smi_handler();
169 southbridge_smi_handler();
170
171 smi_restore_pci_address();
172
173 smi_release_lock();
174
175 /* De-assert SMI# signal to allow another SMI */
176 smi_set_eos();
177}
178
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500179RMODULE_ENTRY(smm_handler_start);
180
Aaron Durbin50a34642013-01-03 17:38:47 -0600181/* Provide a default implementation for all weak handlers so that relocation
182 * entries in the modules make sense. Without default implementations the
183 * weak relocations w/o a symbol have a 0 address which is where the modules
184 * are linked at. */
185int __attribute__((weak)) mainboard_io_trap_handler(int smif) { return 0; }
186void __attribute__((weak)) cpu_smi_handler(void) {}
187void __attribute__((weak)) northbridge_smi_handler() {}
188void __attribute__((weak)) southbridge_smi_handler() {}
Duncan Laurie0edc2242013-04-29 15:04:30 -0700189void __attribute__((weak)) mainboard_smi_gpi(u32 gpi_sts) {}
Aaron Durbin50a34642013-01-03 17:38:47 -0600190int __attribute__((weak)) mainboard_smi_apmc(u8 data) { return 0; }
191void __attribute__((weak)) mainboard_smi_sleep(u8 slp_typ) {}