blob: dabc67744274068034e0baa74c55bbfc85ce2b8d [file] [log] [blame]
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -04001// System Management Mode support (on emulators)
2//
Kevin O'Connorf4c511c2014-04-07 19:49:12 -04003// Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -04004// Copyright (C) 2006 Fabrice Bellard
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -04007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "config.h" // CONFIG_*
Isaku Yamahata72a590e2012-11-28 10:17:33 +01009#include "dev-q35.h"
Paolo Bonzini40d03122014-05-15 13:22:26 +020010#include "dev-piix.h"
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "hw/pci.h" // pci_config_writel
12#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
13#include "hw/pci_regs.h" // PCI_DEVICE_ID
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "output.h" // dprintf
Kevin O'Connor4ade5232013-09-18 21:41:48 -040015#include "paravirt.h" // PORT_SMI_STATUS
Kevin O'Connor55215cd2014-04-11 11:20:41 -040016#include "stacks.h" // HaveSmmCall32
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040017#include "string.h" // memcpy
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040018#include "util.h" // smm_setup
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040019#include "x86.h" // wbinvd
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040020
Kevin O'Connor31bcda22014-05-28 13:33:50 -040021#define SMM_REV_I32 0x00020000
22#define SMM_REV_I64 0x00020064
23
24struct smm_state {
25 union {
26 struct {
27 u8 pad_000[0xf8];
28 u32 smm_base;
29 u32 smm_rev;
30 u8 pad_100[0xd0];
31 u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags;
32 u8 pad_1f8[0x08];
33 } i32;
34 struct {
35 u8 pad_000[0xfc];
36 u32 smm_rev;
37 u32 smm_base;
38 u8 pad_104[0x6c];
39 u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8;
40 u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
41 } i64;
42 };
43};
44
45struct smm_layout {
Kevin O'Connor55215cd2014-04-11 11:20:41 -040046 struct smm_state backup1;
47 struct smm_state backup2;
48 u8 stack[0x7c00];
Kevin O'Connor31bcda22014-05-28 13:33:50 -040049 u64 codeentry;
50 u8 pad_8008[0x7df8];
51 struct smm_state cpu;
52};
53
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040054void VISIBLE32FLAT
55handle_smi(u16 cs)
56{
Kevin O'Connord71d52b2014-06-05 11:08:44 -040057 if (!CONFIG_USE_SMM)
58 return;
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040059 u8 cmd = inb(PORT_SMI_CMD);
Kevin O'Connor31bcda22014-05-28 13:33:50 -040060 struct smm_layout *smm = MAKE_FLATPTR(cs, 0);
61 dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm);
Paolo Bonzini21269942014-05-15 13:22:29 +020062
Kevin O'Connor31bcda22014-05-28 13:33:50 -040063 if (smm == (void*)BUILD_SMM_INIT_ADDR) {
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040064 // relocate SMBASE to 0xa0000
Kevin O'Connor31bcda22014-05-28 13:33:50 -040065 if (smm->cpu.i32.smm_rev == SMM_REV_I32) {
66 smm->cpu.i32.smm_base = BUILD_SMM_ADDR;
67 } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) {
68 smm->cpu.i64.smm_base = BUILD_SMM_ADDR;
69 } else {
70 warn_internalerror();
71 return;
72 }
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040073 // indicate to smm_relocate_and_restore() that the SMM code was executed
74 outb(0x00, PORT_SMI_STATUS);
Kevin O'Connor55215cd2014-04-11 11:20:41 -040075
76 if (CONFIG_CALL32_SMM) {
77 // Backup current cpu state for SMM trampolining
78 struct smm_layout *newsmm = (void*)BUILD_SMM_ADDR;
79 memcpy(&newsmm->backup1, &smm->cpu, sizeof(newsmm->backup1));
80 memcpy(&newsmm->backup2, &smm->cpu, sizeof(newsmm->backup2));
81 HaveSmmCall32 = 1;
82 }
83
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040084 return;
85 }
Kevin O'Connor55215cd2014-04-11 11:20:41 -040086
87 if (CONFIG_CALL32_SMM && cmd == CALL32SMM_CMDID) {
88 if (smm->cpu.i32.smm_rev == SMM_REV_I32) {
89 u32 regs[8];
90 memcpy(regs, &smm->cpu.i32.eax, sizeof(regs));
91 if (smm->cpu.i32.ecx == CALL32SMM_ENTERID) {
92 dprintf(9, "smm cpu call pc=%x esp=%x\n", regs[3], regs[4]);
93 memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2));
94 memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu));
95 memcpy(&smm->cpu.i32.eax, regs, sizeof(regs));
96 smm->cpu.i32.eip = regs[3];
97 } else if (smm->cpu.i32.ecx == CALL32SMM_RETURNID) {
98 dprintf(9, "smm cpu ret %x esp=%x\n", regs[3], regs[4]);
99 memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu));
100 memcpy(&smm->cpu.i32.eax, regs, sizeof(regs));
101 smm->cpu.i32.eip = regs[3];
102 }
103 } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) {
104 u64 regs[8];
105 memcpy(regs, &smm->cpu.i64.rdi, sizeof(regs));
106 if ((u32)smm->cpu.i64.rcx == CALL32SMM_ENTERID) {
107 memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2));
108 memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu));
109 memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs));
110 smm->cpu.i64.rip = (u32)regs[4];
111 } else if ((u32)smm->cpu.i64.rcx == CALL32SMM_RETURNID) {
112 memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu));
113 memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs));
114 smm->cpu.i64.rip = (u32)regs[4];
115 }
116 }
117 }
Kevin O'Connorf4c511c2014-04-07 19:49:12 -0400118}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400119
Kevin O'Connorf4c511c2014-04-07 19:49:12 -0400120extern void entry_smi(void);
121// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR)
122#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \
123 | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24))
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400124
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400125static void
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900126smm_save_and_copy(void)
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400127{
Kevin O'Connorf4c511c2014-04-07 19:49:12 -0400128 // save original memory content
Kevin O'Connor31bcda22014-05-28 13:33:50 -0400129 struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR;
130 struct smm_layout *smm = (void*)BUILD_SMM_ADDR;
131 memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu));
132 memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry));
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500133
Kevin O'Connorf4c511c2014-04-07 19:49:12 -0400134 // Setup code entry point.
Kevin O'Connor31bcda22014-05-28 13:33:50 -0400135 initsmm->codeentry = SMI_INSN;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900136}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400137
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400138static void
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900139smm_relocate_and_restore(void)
140{
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400141 /* init APM status port */
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500142 outb(0x01, PORT_SMI_STATUS);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400143
144 /* raise an SMI interrupt */
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500145 outb(0x00, PORT_SMI_CMD);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400146
147 /* wait until SMM code executed */
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500148 while (inb(PORT_SMI_STATUS) != 0x00)
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400149 ;
150
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500151 /* restore original memory content */
Kevin O'Connor31bcda22014-05-28 13:33:50 -0400152 struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR;
153 struct smm_layout *smm = (void*)BUILD_SMM_ADDR;
154 memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu));
155 memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry));
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400156
Kevin O'Connorf4c511c2014-04-07 19:49:12 -0400157 // Setup code entry point.
Kevin O'Connor31bcda22014-05-28 13:33:50 -0400158 smm->codeentry = SMI_INSN;
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400159 wbinvd();
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900160}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400161
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400162// This code is hardcoded for PIIX4 Power Management device.
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500163static void piix4_apmc_smm_setup(int isabdf, int i440_bdf)
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400164{
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400165 /* check if SMM init is already done */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500166 u32 value = pci_config_readl(isabdf, PIIX_DEVACTB);
Paolo Bonzini40d03122014-05-15 13:22:26 +0200167 if (value & PIIX_DEVACTB_APMC_EN)
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400168 return;
169
170 /* enable the SMM memory window */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500171 pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x48);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400172
173 smm_save_and_copy();
174
175 /* enable SMI generation when writing to the APMC register */
Paolo Bonzini40d03122014-05-15 13:22:26 +0200176 pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400177
Paolo Bonzini457ba422014-05-15 13:22:28 +0200178 /* enable SMI generation */
179 value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL);
Kevin O'Connora9f48162014-08-23 12:10:29 -0400180 outl(acpi_pm_base + PIIX_PMIO_GLBCTL, value | PIIX_PMIO_GLBCTL_SMI_EN);
Paolo Bonzini457ba422014-05-15 13:22:28 +0200181
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400182 smm_relocate_and_restore();
183
184 /* close the SMM memory window and enable normal SMM */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500185 pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x08);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400186}
187
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100188/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500189void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf)
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100190{
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100191 /* check if SMM init is already done */
Gerd Hoffmanna217de92014-05-13 14:01:22 +0200192 u32 value = inl(acpi_pm_base + ICH9_PMIO_SMI_EN);
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100193 if (value & ICH9_PMIO_SMI_EN_APMC_EN)
194 return;
195
196 /* enable the SMM memory window */
197 pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48);
198
199 smm_save_and_copy();
200
201 /* enable SMI generation when writing to the APMC register */
Paolo Bonzini457ba422014-05-15 13:22:28 +0200202 outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN,
Gerd Hoffmanna217de92014-05-13 14:01:22 +0200203 acpi_pm_base + ICH9_PMIO_SMI_EN);
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100204
Paolo Bonzini457ba422014-05-15 13:22:28 +0200205 /* lock SMI generation */
206 value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1);
207 pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1,
208 value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK);
209
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100210 smm_relocate_and_restore();
211
212 /* close the SMM memory window and enable normal SMM */
213 pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08);
214}
215
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500216static int SMMISADeviceBDF = -1, SMMPMDeviceBDF = -1;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900217
218void
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500219smm_device_setup(void)
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900220{
Kevin O'Connora2a86e22013-02-13 19:35:12 -0500221 if (!CONFIG_USE_SMM)
Kevin O'Connor028f3482014-04-11 12:08:33 -0400222 return;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900223
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500224 struct pci_device *isapci, *pmpci;
225 isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3);
226 pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441);
227 if (isapci && pmpci) {
228 SMMISADeviceBDF = isapci->bdf;
229 SMMPMDeviceBDF = pmpci->bdf;
230 return;
231 }
232 isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC);
233 pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH);
234 if (isapci && pmpci) {
235 SMMISADeviceBDF = isapci->bdf;
236 SMMPMDeviceBDF = pmpci->bdf;
237 }
238}
239
240void
241smm_setup(void)
242{
243 if (!CONFIG_USE_SMM || SMMISADeviceBDF < 0)
Kevin O'Connor028f3482014-04-11 12:08:33 -0400244 return;
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500245
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900246 dprintf(3, "init smm\n");
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500247 u16 device = pci_config_readw(SMMISADeviceBDF, PCI_DEVICE_ID);
248 if (device == PCI_DEVICE_ID_INTEL_82371AB_3)
249 piix4_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF);
250 else
251 ich9_lpc_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400252}