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 <types.h>
4 #include <arch/io.h>
5 #include <device/pci_ops.h>
6 #include <console/console.h>
7 #include <cpu/x86/cache.h>
8 #include <device/pci_def.h>
9 #include <cpu/x86/smm.h>
11 #include <elog.h>
12 #include <halt.h>
13 #include <option.h>
17 #include <soc/nvs.h>
18 #include <smmstore.h>
19 #include "me.h"
20 #include "pch.h"
21 
23 {
24  switch (smif) {
25  case 0x32:
26  printk(BIOS_DEBUG, "OS Init\n");
27  /* gnvs->smif:
28  * On success, the IO Trap Handler returns 0
29  * On failure, the IO Trap Handler returns a value != 0
30  */
31  gnvs->smif = 0;
32  return 1; /* IO trap handled */
33  }
34 
35  /* Not handled */
36  return 0;
37 }
38 
39 /**
40  * @brief Set the EOS bit
41  */
43 {
44  enable_smi(EOS);
45 }
46 
47 static void busmaster_disable_on_bus(int bus)
48 {
49  int slot, func;
50  unsigned int val;
51  unsigned char hdr;
52 
53  for (slot = 0; slot < 0x20; slot++) {
54  for (func = 0; func < 8; func++) {
55  pci_devfn_t dev = PCI_DEV(bus, slot, func);
56 
58 
59  if (val == 0xffffffff || val == 0x00000000 ||
60  val == 0x0000ffff || val == 0xffff0000)
61  continue;
62 
63  /* Disable Bus Mastering for this one device */
65 
66  /* If this is a bridge, then follow it. */
68  hdr &= 0x7f;
69  if (hdr == PCI_HEADER_TYPE_BRIDGE ||
70  hdr == PCI_HEADER_TYPE_CARDBUS) {
71  unsigned int buses;
72  buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
73  busmaster_disable_on_bus((buses >> 8) & 0xff);
74  }
75  }
76  }
77 }
78 
79 static int power_on_after_fail(void)
80 {
81  /* save and recover RTC port values */
82  u8 tmp70, tmp72;
83  tmp70 = inb(0x70);
84  tmp72 = inb(0x72);
85  const unsigned int s5pwr = get_uint_option("power_on_after_fail",
86  CONFIG_MAINBOARD_POWER_FAILURE_STATE);
87  outb(tmp70, 0x70);
88  outb(tmp72, 0x72);
89 
90  /* For "KEEP", switch to "OFF" - KEEP is software emulated. */
91  return (s5pwr == MAINBOARD_POWER_ON);
92 }
93 
94 static void southbridge_smi_sleep(void)
95 {
96  u32 reg32;
97  u8 slp_typ;
98  u16 pmbase = get_pmbase();
99 
100  /* First, disable further SMIs */
102 
103  /* Figure out SLP_TYP */
104  reg32 = inl(pmbase + PM1_CNT);
105  printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
106  slp_typ = acpi_sleep_from_pm1(reg32);
107 
108  /* Do any mainboard sleep handling */
109  mainboard_smi_sleep(slp_typ);
110 
111  /* USB sleep preparations */
112 #if !CONFIG(FINALIZE_USB_ROUTE_XHCI)
115 #endif
117 
118  /* Log S3, S4, and S5 entry */
119  if (slp_typ >= ACPI_S3)
121 
122  /* Next, do the deed.
123  */
124 
125  switch (slp_typ) {
126  case ACPI_S0:
127  printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
128  break;
129  case ACPI_S1:
130  printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n");
131  break;
132  case ACPI_S3:
133  printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
134 
135  /* Invalidate the cache before going to S3 */
136  wbinvd();
137  break;
138  case ACPI_S4:
139  printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
140  break;
141  case ACPI_S5:
142  printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
143 
144  /* Disable all GPE */
145  disable_all_gpe();
146 
147  /* Always set the flag in case CMOS was changed on runtime. */
148  if (power_on_after_fail())
150  else
152 
153  /* also iterates over all bridges on bus 0 */
155  break;
156  default:
157  printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
158  break;
159  }
160 
161  /*
162  * Write back to the SLP register to cause the originally intended
163  * event again. We need to set BIT13 (SLP_EN) though to make the
164  * sleep happen.
165  */
167 
168  /* Make sure to stop executing code here for S3/S4/S5 */
169  if (slp_typ >= ACPI_S3)
170  halt();
171 
172  /*
173  * In most sleep states, the code flow of this function ends at
174  * the line above. However, if we entered sleep state S1 and wake
175  * up again, we will continue to execute code in this function.
176  */
177  reg32 = inl(pmbase + PM1_CNT);
178  if (reg32 & SCI_EN) {
179  /* The OS is not an ACPI OS, so we set the state to S0 */
181  }
182 }
183 
184 /*
185  * Look for Synchronous IO SMI and use save state from that
186  * core in case we are not running on the same core that
187  * initiated the IO transaction.
188  */
189 static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd)
190 {
191  em64t101_smm_state_save_area_t *state;
192  int node;
193 
194  /* Check all nodes looking for the one that issued the IO */
195  for (node = 0; node < CONFIG_MAX_CPUS; node++) {
196  state = smm_get_save_state(node);
197 
198  /* Check for Synchronous IO (bit0 == 1) */
199  if (!(state->io_misc_info & (1 << 0)))
200  continue;
201 
202  /* Make sure it was a write (bit4 == 0) */
203  if (state->io_misc_info & (1 << 4))
204  continue;
205 
206  /* Check for APMC IO port */
207  if (((state->io_misc_info >> 16) & 0xff) != APM_CNT)
208  continue;
209 
210  /* Check AX against the requested command */
211  if ((state->rax & 0xff) != cmd)
212  continue;
213 
214  return state;
215  }
216 
217  return NULL;
218 }
219 
220 static void southbridge_smi_gsmi(void)
221 {
222  u32 *ret, *param;
223  u8 sub_command;
224  em64t101_smm_state_save_area_t *io_smi =
226 
227  if (!io_smi)
228  return;
229 
230  /* Command and return value in EAX */
231  ret = (u32 *)&io_smi->rax;
232  sub_command = (u8)(*ret >> 8);
233 
234  /* Parameter buffer in EBX */
235  param = (u32 *)&io_smi->rbx;
236 
237  /* drivers/elog/gsmi.c */
238  *ret = gsmi_exec(sub_command, param);
239 }
240 
241 static void southbridge_smi_store(void)
242 {
243  u8 sub_command, ret;
244  em64t101_smm_state_save_area_t *io_smi =
246  uint32_t reg_ebx;
247 
248  if (!io_smi)
249  return;
250  /* Command and return value in EAX */
251  sub_command = (io_smi->rax >> 8) & 0xff;
252 
253  /* Parameter buffer in EBX */
254  reg_ebx = io_smi->rbx;
255 
256  /* drivers/smmstore/smi.c */
257  ret = smmstore_exec(sub_command, (void *)reg_ebx);
258  io_smi->rax = ret;
259 }
260 
261 static void southbridge_smi_apmc(void)
262 {
263  u8 reg8;
264  static int chipset_finalized = 0;
265 
266  reg8 = apm_get_apmc();
267  switch (reg8) {
268  case APM_CNT_FINALIZE:
269  if (chipset_finalized) {
270  printk(BIOS_DEBUG, "SMI#: Already finalized\n");
271  return;
272  }
273 
276 
277  chipset_finalized = 1;
278  break;
281  break;
282  case APM_CNT_ACPI_ENABLE:
284  break;
287  break;
288  case APM_CNT_ELOG_GSMI:
289  if (CONFIG(ELOG_GSMI))
291  break;
292  case APM_CNT_SMMSTORE:
293  if (CONFIG(SMMSTORE))
295  break;
296  }
297 
298  mainboard_smi_apmc(reg8);
299 }
300 
301 static void southbridge_smi_pm1(void)
302 {
303  u16 pm1_sts = clear_pm1_status();
304 
305  /* While OSPM is not active, poweroff immediately
306  * on a power button event.
307  */
308  if (pm1_sts & PWRBTN_STS) {
309  /* power button pressed */
313  }
314 }
315 
316 static void southbridge_smi_gpe0(void)
317 {
319 }
320 
321 static void southbridge_smi_gpi(void)
322 {
324 
325  /* Clear again after mainboard handler */
327 }
328 
329 static void southbridge_smi_mc(void)
330 {
331  u32 reg32 = inl(get_pmbase() + SMI_EN);
332 
333  /* Are microcontroller SMIs enabled? */
334  if ((reg32 & MCSMI_EN) == 0)
335  return;
336 
337  printk(BIOS_DEBUG, "Microcontroller SMI.\n");
338 }
339 
340 static void southbridge_smi_tco(void)
341 {
342  u32 tco_sts = clear_tco_status();
343 
344  /* Any TCO event? */
345  if (!tco_sts)
346  return;
347 
348  // BIOSWR
349  if (tco_sts & (1 << 8)) {
351 
352  if (bios_cntl & 1) {
353  /*
354  * BWE is RW, so the SMI was caused by a
355  * write to BWE, not by a write to the BIOS
356  *
357  * This is the place where we notice someone
358  * is trying to tinker with the BIOS. We are
359  * trying to be nice and just ignore it. A more
360  * resolute answer would be to power down the
361  * box.
362  */
363  printk(BIOS_DEBUG, "Switching back to RO\n");
364  pci_write_config8(PCH_LPC_DEV, BIOS_CNTL, (bios_cntl & ~1));
365  } /* No else for now? */
366  } else if (tco_sts & (1 << 3)) { /* TIMEOUT */
367  /* Handle TCO timeout */
368  printk(BIOS_DEBUG, "TCO Timeout.\n");
369  }
370 }
371 
372 static void southbridge_smi_periodic(void)
373 {
374  u32 reg32 = inl(get_pmbase() + SMI_EN);
375 
376  /* Are periodic SMIs enabled? */
377  if ((reg32 & PERIODIC_EN) == 0)
378  return;
379 
380  printk(BIOS_DEBUG, "Periodic SMI.\n");
381 }
382 
383 static void southbridge_smi_monitor(void)
384 {
385 #define IOTRAP(x) (trap_sts & (1 << x))
386  u32 trap_sts, trap_cycle;
387  u32 mask = 0;
388  int i;
389 
390  trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register
391  RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR
392 
393  trap_cycle = RCBA32(0x1e10);
394  for (i = 16; i < 20; i++) {
395  if (trap_cycle & (1 << i))
396  mask |= (0xff << ((i - 16) << 2));
397  }
398 
399  /* IOTRAP(3) SMI function call */
400  if (IOTRAP(3)) {
401  if (gnvs && gnvs->smif)
402  io_trap_handler(gnvs->smif); // call function smif
403  return;
404  }
405 
406  /* IOTRAP(2) currently unused
407  * IOTRAP(1) currently unused */
408 
409  /* IOTRAP(0) SMIC */
410  if (IOTRAP(0)) {
411  // It's a write
412  if (!(trap_cycle & (1 << 24))) {
413  printk(BIOS_DEBUG, "SMI1 command\n");
414  (void)RCBA32(0x1e18);
415  // data = RCBA32(0x1e18);
416  // data &= mask;
417  // if (smi1)
418  // southbridge_smi_command(data);
419  // return;
420  }
421  // Fall through to debug
422  }
423 
424  printk(BIOS_DEBUG, " trapped io address = 0x%x\n",
425  trap_cycle & 0xfffc);
426  for (i = 0; i < 4; i++)
427  if (IOTRAP(i))
428  printk(BIOS_DEBUG, " TRAP = %d\n", i);
429  printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf);
430  printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask);
431  printk(BIOS_DEBUG, " read/write: %s\n",
432  (trap_cycle & (1 << 24)) ? "read" : "write");
433 
434  if (!(trap_cycle & (1 << 24))) {
435  /* Write Cycle */
436  printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", RCBA32(0x1e18));
437  }
438 #undef IOTRAP
439 }
440 
441 typedef void (*smi_handler_t)(void);
442 
444  NULL, // [0] reserved
445  NULL, // [1] reserved
446  NULL, // [2] BIOS_STS
447  NULL, // [3] LEGACY_USB_STS
448  southbridge_smi_sleep, // [4] SLP_SMI_STS
449  southbridge_smi_apmc, // [5] APM_STS
450  NULL, // [6] SWSMI_TMR_STS
451  NULL, // [7] reserved
452  southbridge_smi_pm1, // [8] PM1_STS
453  southbridge_smi_gpe0, // [9] GPE0_STS
454  southbridge_smi_gpi, // [10] GPI_STS
455  southbridge_smi_mc, // [11] MCSMI_STS
456  NULL, // [12] DEVMON_STS
457  southbridge_smi_tco, // [13] TCO_STS
458  southbridge_smi_periodic, // [14] PERIODIC_STS
459  NULL, // [15] SERIRQ_SMI_STS
460  NULL, // [16] SMBUS_SMI_STS
461  NULL, // [17] LEGACY_USB2_STS
462  NULL, // [18] INTEL_USB2_STS
463  NULL, // [19] reserved
464  NULL, // [20] PCI_EXP_SMI_STS
465  southbridge_smi_monitor, // [21] MONITOR_STS
466  NULL, // [22] reserved
467  NULL, // [23] reserved
468  NULL, // [24] reserved
469  NULL, // [25] EL_SMI_STS
470  NULL, // [26] SPI_STS
471  NULL, // [27] reserved
472  NULL, // [28] reserved
473  NULL, // [29] reserved
474  NULL, // [30] reserved
475  NULL // [31] reserved
476 };
477 
478 /**
479  * @brief Interrupt handler for SMI#
480  */
482 {
483  int i;
484  u32 smi_sts;
485 
486  /* We need to clear the SMI status registers, or we won't see what's
487  * happening in the following calls.
488  */
489  smi_sts = clear_smi_status();
490 
491  /* Call SMI sub handler for each of the status bits */
492  for (i = 0; i < 31; i++) {
493  if (smi_sts & (1 << i)) {
494  if (southbridge_smi[i]) {
495  southbridge_smi[i]();
496  } else {
498  "SMI_STS[%d] occurred, but no "
499  "handler available.\n", i);
500  }
501  }
502  }
503 }
#define SCI_EN
Definition: pm.h:30
#define MCSMI_EN
Definition: pm.h:41
#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
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 MAINBOARD_POWER_ON
Definition: pm.h:94
uint32_t clear_alt_smi_status(void)
Definition: pmutil.c:236
#define PWRBTN_STS
Definition: southbridge.h:30
#define ELOG_TYPE_ACPI_ENTER
Definition: elog.h:143
#define ELOG_TYPE_POWER_BUTTON
Definition: elog.h:133
#define printk(level,...)
Definition: stdlib.h:16
void intel_cpu_haswell_finalize_smm(void)
Definition: finalize.c:8
void io_trap_handler(int smif)
Definition: smihandler.c:58
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 __weak mainboard_smi_gpi(u32 gpi_sts)
Definition: smihandler.c:208
void * smm_get_save_state(int cpu)
Definition: smihandler.c:114
u8 inb(u16 port)
void outb(u8 val, u16 port)
u32 inl(u16 port)
uint32_t smmstore_exec(uint8_t command, void *param)
Definition: smi.c:144
@ CONFIG
Definition: dsi_common.h:201
u32 gsmi_exec(u8 command, u32 *param)
Definition: gsmi.c:46
void __noreturn halt(void)
halt the system reliably
Definition: halt.c:6
@ ACPI_S5
Definition: acpi.h:1385
@ ACPI_S1
Definition: acpi.h:1381
@ ACPI_S4
Definition: acpi.h:1384
@ ACPI_S3
Definition: acpi.h:1383
@ ACPI_S0
Definition: acpi.h:1380
static void wbinvd(void)
Definition: cache.h:15
#define APM_CNT
Definition: smm.h:19
#define APM_CNT_ELOG_GSMI
Definition: smm.h:29
#define APM_CNT_ACPI_DISABLE
Definition: smm.h:21
#define APM_CNT_ACPI_ENABLE
Definition: smm.h:22
#define APM_CNT_ROUTE_ALL_XHCI
Definition: smm.h:23
#define APM_CNT_SMMSTORE
Definition: smm.h:28
#define APM_CNT_FINALIZE
Definition: smm.h:24
static __always_inline void pci_and_config16(const struct device *dev, u16 reg, u16 andmask)
Definition: pci_ops.h:147
static __always_inline void pci_and_config8(const struct device *dev, u16 reg, u8 andmask)
Definition: pci_ops.h:136
static __always_inline void pci_or_config8(const struct device *dev, u16 reg, u8 ormask)
Definition: pci_ops.h:169
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_config8(const struct device *dev, u16 reg, u8 val)
Definition: pci_ops.h:64
static int elog_gsmi_add_event(u8 event_type)
Definition: elog.h:45
static int elog_gsmi_add_event_byte(u8 event_type, u8 data)
Definition: elog.h:46
#define SLP_EN
Definition: pmc.h:62
#define SLP_TYP_S5
Definition: pmc.h:69
#define SLP_TYP
Definition: pmc.h:64
#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
unsigned int get_uint_option(const char *name, const unsigned int fallback)
Definition: option.c:116
#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
#define BIOS_CNTL
Definition: lpc.h:20
#define GEN_PMCON_3
Definition: lpc.h:63
void usb_xhci_sleep_prepare(pci_devfn_t dev, u8 slp_typ)
static const int mask[4]
Definition: gpio.c:308
#define PCH_LPC_DEV
Definition: lpc.h:7
#define PCH_EHCI1_DEV
Definition: pch.h:77
#define PCH_XHCI_DEV
Definition: pch.h:88
#define PCH_EHCI2_DEV
Definition: pch.h:78
void southbridge_smi_monitor(void)
Definition: smihandler.c:89
void intel_pch_finalize_smm(void)
Definition: finalize.c:12
#define RCBA32(x)
Definition: rcba.h:14
u16 get_pmbase(void)
Definition: smihandler.c:20
u16 pmbase
Definition: smihandler.c:25
void usb_xhci_route_all(void)
void usb_ehci_sleep_prepare(pci_devfn_t dev, u8 slp_typ)
static void southbridge_smi_pm1(void)
Definition: smihandler.c:301
static void southbridge_smi_store(void)
Definition: smihandler.c:241
static void busmaster_disable_on_bus(int bus)
Definition: smihandler.c:47
static void southbridge_smi_apmc(void)
Definition: smihandler.c:261
static void southbridge_smi_periodic(void)
Definition: smihandler.c:372
static void southbridge_smi_gpe0(void)
Definition: smihandler.c:316
#define IOTRAP(x)
static em64t101_smm_state_save_area_t * smi_apmc_find_state_save(u8 cmd)
Definition: smihandler.c:189
static void southbridge_smi_gpi(void)
Definition: smihandler.c:321
static void southbridge_smi_gsmi(void)
Definition: smihandler.c:220
static void southbridge_smi_tco(void)
Definition: smihandler.c:340
static void southbridge_smi_mc(void)
Definition: smihandler.c:329
static void southbridge_smi_sleep(void)
Definition: smihandler.c:94
static int power_on_after_fail(void)
Definition: smihandler.c:79
#define NULL
Definition: stddef.h:19
unsigned int uint32_t
Definition: stdint.h:14
uint32_t u32
Definition: stdint.h:51
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
Definition: device.h:76
u8 smif
Definition: nvs.h:11
u8 val
Definition: sys.c:300
typedef void(X86APIP X86EMU_intrFuncs)(int num)