coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
smihandler.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <stdint.h>
4 #include <arch/hlt.h>
5 #include <arch/io.h>
6 #include <device/pci_ops.h>
7 #include <console/console.h>
8 #include <cpu/x86/cache.h>
9 #include <cpu/x86/smm.h>
11 #include <device/pci_def.h>
12 #include <intelblocks/fast_spi.h>
13 #include <smmstore.h>
14 #include <spi-generic.h>
15 #include <soc/iomap.h>
16 #include <soc/soc_util.h>
17 #include <soc/pm.h>
18 #include <soc/nvs.h>
19 
21 {
22  switch (smif) {
23  case 0x32:
24  printk(BIOS_DEBUG, "OS Init\n");
25  /* gnvs->smif:
26  * On success, the IO Trap Handler returns 0
27  * On failure, the IO Trap Handler returns a value != 0
28  */
29  gnvs->smif = 0;
30  return 1; /* IO trap handled */
31  }
32 
33  /* Not handled */
34  return 0;
35 }
36 
38 {
39  enable_smi(EOS);
40 }
41 
42 static void busmaster_disable_on_bus(int bus)
43 {
44  int slot, func;
45  unsigned int val;
46  unsigned char hdr;
47 
48  for (slot = 0; slot < 0x20; slot++) {
49  for (func = 0; func < 8; func++) {
50  u16 reg16;
51 
52  pci_devfn_t dev = PCI_DEV(bus, slot, func);
54 
55  if (val == 0xffffffff || val == 0x00000000 ||
56  val == 0x0000ffff || val == 0xffff0000)
57  continue;
58 
59  /* Disable Bus Mastering for this one device */
60  reg16 = pci_read_config16(dev, PCI_COMMAND);
61  reg16 &= ~PCI_COMMAND_MASTER;
62  pci_write_config16(dev, PCI_COMMAND, reg16);
63 
64  /* If this is a bridge, then follow it. */
66  hdr &= 0x7f;
67  if (hdr == PCI_HEADER_TYPE_BRIDGE ||
68  hdr == PCI_HEADER_TYPE_CARDBUS) {
69  unsigned int buses;
70  buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
71  busmaster_disable_on_bus((buses >> 8) & 0xff);
72  }
73  }
74  }
75 }
76 
77 static void southbridge_smi_sleep(void)
78 {
79  uint32_t reg32;
80  uint8_t slp_typ;
82 
83  /* First, disable further SMIs */
85 
86  /* Figure out SLP_TYP */
87  reg32 = inl((uint16_t)(pmbase + PM1_CNT));
88  printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
89  slp_typ = (reg32 >> 10) & 7;
90 
91  /* Do any mainboard sleep handling */
92  mainboard_smi_sleep((uint8_t)(slp_typ - 2));
93 
94  /* Next, do the deed.
95  */
96 
97  switch (slp_typ) {
98  case SLP_TYP_S0:
99  printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
100  break;
101  case SLP_TYP_S1:
102  printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n");
103  break;
104  case SLP_TYP_S3:
105  printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
106 
107  /* Invalidate the cache before going to S3 */
108  wbinvd();
109  break;
110  case SLP_TYP_S4:
111  printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
112  break;
113  case SLP_TYP_S5:
114  printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
115 
116  /* Disable all GPE */
117  disable_all_gpe();
118 
119  /* also iterates over all bridges on bus 0 */
121  break;
122  default:
123  printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
124  break;
125  }
126 
127  /* Write back to the SLP register to cause the originally intended
128  * event again. We need to set BIT13 (SLP_EN) though to make the
129  * sleep happen.
130  */
132 
133  /* Make sure to stop executing code here for S3/S4/S5 */
134  if (slp_typ > 1)
135  hlt();
136 
137  /* In most sleep states, the code flow of this function ends at
138  * the line above. However, if we entered sleep state S1 and wake
139  * up again, we will continue to execute code in this function.
140  */
141  reg32 = inl((uint16_t)(pmbase + PM1_CNT));
142  if (reg32 & SCI_EN) {
143  /* The OS is not an ACPI OS, so we set the state to S0 */
145  }
146 }
147 
148 /*
149  * Look for Synchronous IO SMI and use save state from that
150  * core in case we are not running on the same core that
151  * initiated the IO transaction.
152  */
153 static em64t100_smm_state_save_area_t *smi_apmc_find_state_save(uint8_t cmd)
154 {
155  em64t100_smm_state_save_area_t *state;
156  int node;
157 
158  /* Check all nodes looking for the one that issued the IO */
159  for (node = 0; node < CONFIG_MAX_CPUS; node++) {
160  state = smm_get_save_state(node);
161 
162  /* Check for Synchronous IO (bit0==1) */
163  if (!(state->io_misc_info & (1 << 0)))
164  continue;
165 
166  /* Make sure it was a write (bit4==0) */
167  if (state->io_misc_info & (1 << 4))
168  continue;
169 
170  /* Check for APMC IO port */
171  if (((state->io_misc_info >> 16) & 0xff) != APM_CNT)
172  continue;
173 
174  /* Check AX against the requested command */
175  if ((state->rax & 0xff) != cmd)
176  continue;
177 
178  return state;
179  }
180 
181  return NULL;
182 }
183 
184 static void finalize(void)
185 {
186  static int finalize_done;
187 
188  if (finalize_done) {
189  printk(BIOS_DEBUG, "SMM already finalized.\n");
190  return;
191  }
192  finalize_done = 1;
193 
194  if (CONFIG(SPI_FLASH_SMM))
195  /* Re-init SPI driver to handle locked BAR */
196  fast_spi_init();
197 }
198 
199 static void southbridge_smi_store(void)
200 {
201  u8 sub_command, ret;
202  em64t100_smm_state_save_area_t *io_smi =
204  uint32_t reg_ebx;
205 
206  if (!io_smi)
207  return;
208  /* Command and return value in EAX */
209  sub_command = (io_smi->rax >> 8) & 0xff;
210 
211  /* Parameter buffer in EBX */
212  reg_ebx = io_smi->rbx;
213 
214  /* drivers/smmstore/smi.c */
215  ret = smmstore_exec(sub_command, (void *)reg_ebx);
216  io_smi->rax = ret;
217 }
218 
219 static void southbridge_smi_apmc(void)
220 {
221  uint8_t reg8;
222 
223  reg8 = apm_get_apmc();
224  switch (reg8) {
227  break;
228  case APM_CNT_ACPI_ENABLE:
230  break;
231  case APM_CNT_FINALIZE:
232  finalize();
233  break;
234  case APM_CNT_SMMSTORE:
235  if (CONFIG(SMMSTORE))
237  break;
238  }
239 
240  mainboard_smi_apmc(reg8);
241 }
242 
243 static void southbridge_smi_pm1(void)
244 {
245  uint16_t pm1_sts = clear_pm1_status();
246 
247  /* While OSPM is not active, poweroff immediately
248  * on a power button event.
249  */
250  if (pm1_sts & PWRBTN_STS) {
251  // power button pressed
252  disable_pm1_control(-1UL);
254  }
255 }
256 
257 static void southbridge_smi_gpe0(void) { clear_gpe_status(); }
258 
259 static void southbridge_smi_tco(void)
260 {
261  uint32_t tco_sts = clear_tco_status();
262 
263  /* Any TCO event? */
264  if (!tco_sts)
265  return;
266 
267  if (tco_sts & TCO1_STS_TIMEOUT) { /* TIMEOUT */
268  /* Handle TCO timeout */
269  printk(BIOS_DEBUG, "TCO Timeout.\n");
270  }
271 }
272 
273 static void southbridge_smi_periodic(void)
274 {
275  uint32_t reg32;
276 
277  reg32 = inl((uint16_t)(get_pmbase() + SMI_EN));
278 
279  /* Are periodic SMIs enabled? */
280  if ((reg32 & PERIODIC_EN) == 0)
281  return;
282 
283  printk(BIOS_DEBUG, "Periodic SMI.\n");
284 }
285 
286 typedef void (*smi_handler_t)(void);
287 
288 static const smi_handler_t southbridge_smi[32] = {
289  NULL, // [0] reserved
290  NULL, // [1] reserved
291  NULL, // [2] BIOS_STS
292  NULL, // [3] LEGACY_USB_STS
293  southbridge_smi_sleep, // [4] SLP_SMI_STS
294  southbridge_smi_apmc, // [5] APM_STS
295  NULL, // [6] SWSMI_TMR_STS
296  NULL, // [7] reserved
297  southbridge_smi_pm1, // [8] PM1_STS
298  southbridge_smi_gpe0, // [9] GPE0_STS
299  NULL, // [10] reserved
300  NULL, // [11] reserved
301  NULL, // [12] reserved
302  southbridge_smi_tco, // [13] TCO_STS
303  southbridge_smi_periodic, // [14] PERIODIC_STS
304  NULL, // [15] SERIRQ_SMI_STS
305  NULL, // [16] SMBUS_SMI_STS
306  NULL, // [17] LEGACY_USB2_STS
307  NULL, // [18] INTEL_USB2_STS
308  NULL, // [19] reserved
309  NULL, // [20] PCI_EXP_SMI_STS
310  NULL, // [21] reserved
311  NULL, // [22] reserved
312  NULL, // [23] reserved
313  NULL, // [24] reserved
314  NULL, // [25] reserved
315  NULL, // [26] SPI_STS
316  NULL, // [27] reserved
317  NULL, // [28] PUNIT
318  NULL, // [29] GUNIT
319  NULL, // [30] reserved
320  NULL // [31] reserved
321 };
322 
324 {
325  int i;
326  uint32_t smi_sts;
327 
328  /* We need to clear the SMI status registers, or we won't see what's
329  * happening in the following calls.
330  */
331  smi_sts = clear_smi_status();
332 
333  /* Call SMI sub handler for each of the status bits */
334  for (i = 0; i < ARRAY_SIZE(southbridge_smi); i++) {
335  if (!(smi_sts & (1 << i)))
336  continue;
337 
338  if (southbridge_smi[i] != NULL) {
339  southbridge_smi[i]();
340  } else {
341  printk(BIOS_DEBUG, "SMI_STS[%d] occurred, but no "
342  "handler available.\n",
343  i);
344  }
345  }
346 }
#define SCI_EN
Definition: pm.h:30
#define SLP_SMI_EN
Definition: pm.h:45
#define PM1_CNT
Definition: pm.h:27
#define SMI_EN
Definition: pm.h:32
#define EOS
Definition: pm.h:48
#define PERIODIC_EN
Definition: pm.h:39
static __always_inline void hlt(void)
Definition: hlt.h:6
uint16_t clear_pm1_status(void)
Definition: pmutil.c:152
void enable_pm1_control(uint32_t mask)
Definition: pmutil.c:105
void disable_smi(uint32_t mask)
Definition: pmutil.c:97
void enable_smi(uint32_t mask)
Definition: pmutil.c:89
uint32_t clear_gpe_status(void)
Definition: pmutil.c:265
void disable_pm1_control(uint32_t mask)
Definition: pmutil.c:113
void disable_all_gpe(void)
Definition: pmutil.c:210
uint32_t clear_tco_status(void)
Definition: pmutil.c:189
uint32_t clear_smi_status(void)
Definition: pmutil.c:84
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define PWRBTN_STS
Definition: southbridge.h:30
#define printk(level,...)
Definition: stdlib.h:16
void __weak southbridge_smi_handler(void)
Definition: smihandler.c:207
void __weak mainboard_smi_sleep(u8 slp_typ)
Definition: smihandler.c:210
int __weak mainboard_smi_apmc(u8 data)
Definition: smihandler.c:209
void * smm_get_save_state(int cpu)
Definition: smihandler.c:114
u32 inl(u16 port)
uint32_t smmstore_exec(uint8_t command, void *param)
Definition: smi.c:144
@ CONFIG
Definition: dsi_common.h:201
void fast_spi_init(void)
Definition: fast_spi.c:41
static void wbinvd(void)
Definition: cache.h:15
#define APM_CNT
Definition: smm.h:19
#define APM_CNT_ACPI_DISABLE
Definition: smm.h:21
#define APM_CNT_ACPI_ENABLE
Definition: smm.h:22
#define APM_CNT_SMMSTORE
Definition: smm.h:28
#define APM_CNT_FINALIZE
Definition: smm.h:24
static __always_inline u16 pci_read_config16(const struct device *dev, u16 reg)
Definition: pci_ops.h:52
static __always_inline u32 pci_read_config32(const struct device *dev, u16 reg)
Definition: pci_ops.h:58
static __always_inline u8 pci_read_config8(const struct device *dev, u16 reg)
Definition: pci_ops.h:46
static __always_inline void pci_write_config16(const struct device *dev, u16 reg, u16 val)
Definition: pci_ops.h:70
#define TCO1_STS_TIMEOUT
Definition: pmc.h:221
#define SLP_TYP_S4
Definition: pmc.h:68
#define SLP_EN
Definition: pmc.h:62
#define SLP_TYP_S3
Definition: pmc.h:67
#define SLP_TYP_S1
Definition: pmc.h:66
#define SLP_TYP_S5
Definition: pmc.h:69
#define SLP_TYP
Definition: pmc.h:64
#define SLP_TYP_S0
Definition: pmc.h:65
#define SLP_TYP_SHIFT
Definition: pmc.h:63
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
state
Definition: raminit.c:1787
#define PCI_HEADER_TYPE
Definition: pci_def.h:47
#define PCI_PRIMARY_BUS
Definition: pci_def.h:100
#define PCI_HEADER_TYPE_CARDBUS
Definition: pci_def.h:50
#define PCI_COMMAND_MASTER
Definition: pci_def.h:13
#define PCI_COMMAND
Definition: pci_def.h:10
#define PCI_HEADER_TYPE_BRIDGE
Definition: pci_def.h:49
#define PCI_VENDOR_ID
Definition: pci_def.h:8
#define PCI_DEV(SEGBUS, DEV, FN)
Definition: pci_type.h:14
u32 pci_devfn_t
Definition: pci_type.h:8
u8 apm_get_apmc(void)
Definition: smi_trigger.c:46
struct global_nvs * gnvs
int southbridge_io_trap_handler(int smif)
Definition: smihandler.c:131
const smi_handler_t southbridge_smi[SMI_STS_BITS]
Definition: smihandler.c:17
void southbridge_smi_set_eos(void)
Definition: smihandler.c:41
void(* smi_handler_t)(void)
Definition: smihandler.c:361
static void finalize(void)
Definition: smihandler.c:184
static void southbridge_smi_pm1(void)
Definition: smihandler.c:243
static void southbridge_smi_store(void)
Definition: smihandler.c:199
static em64t100_smm_state_save_area_t * smi_apmc_find_state_save(uint8_t cmd)
Definition: smihandler.c:153
static void busmaster_disable_on_bus(int bus)
Definition: smihandler.c:42
static void southbridge_smi_apmc(void)
Definition: smihandler.c:219
static void southbridge_smi_periodic(void)
Definition: smihandler.c:273
static void southbridge_smi_gpe0(void)
Definition: smihandler.c:257
static void southbridge_smi_tco(void)
Definition: smihandler.c:259
static void southbridge_smi_sleep(void)
Definition: smihandler.c:77
u16 get_pmbase(void)
Definition: smihandler.c:20
u16 pmbase
Definition: smihandler.c:25
#define NULL
Definition: stddef.h:19
unsigned short uint16_t
Definition: stdint.h:11
unsigned int uint32_t
Definition: stdint.h:14
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
Definition: device.h:76
u8 smif
Definition: nvs.h:11
u8 val
Definition: sys.c:300
typedef void(X86APIP X86EMU_intrFuncs)(int num)