blob: c2ce5c38463993a7fc565abd720f2b26620a929c [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'Connorfa9c66a2013-09-14 19:10:40 -040016#include "string.h" // memcpy
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040017#include "util.h" // smm_setup
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040018#include "x86.h" // wbinvd
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040019
Kevin O'Connor31bcda22014-05-28 13:33:50 -040020#define SMM_REV_I32 0x00020000
21#define SMM_REV_I64 0x00020064
22
23struct smm_state {
24 union {
25 struct {
26 u8 pad_000[0xf8];
27 u32 smm_base;
28 u32 smm_rev;
29 u8 pad_100[0xd0];
30 u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags;
31 u8 pad_1f8[0x08];
32 } i32;
33 struct {
34 u8 pad_000[0xfc];
35 u32 smm_rev;
36 u32 smm_base;
37 u8 pad_104[0x6c];
38 u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8;
39 u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
40 } i64;
41 };
42};
43
44struct smm_layout {
45 u8 stack[0x8000];
46 u64 codeentry;
47 u8 pad_8008[0x7df8];
48 struct smm_state cpu;
49};
50
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040051void VISIBLE32FLAT
52handle_smi(u16 cs)
53{
54 u8 cmd = inb(PORT_SMI_CMD);
Kevin O'Connor31bcda22014-05-28 13:33:50 -040055 struct smm_layout *smm = MAKE_FLATPTR(cs, 0);
56 dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm);
Paolo Bonzini21269942014-05-15 13:22:29 +020057
Kevin O'Connor31bcda22014-05-28 13:33:50 -040058 if (smm == (void*)BUILD_SMM_INIT_ADDR) {
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040059 // relocate SMBASE to 0xa0000
Kevin O'Connor31bcda22014-05-28 13:33:50 -040060 if (smm->cpu.i32.smm_rev == SMM_REV_I32) {
61 smm->cpu.i32.smm_base = BUILD_SMM_ADDR;
62 } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) {
63 smm->cpu.i64.smm_base = BUILD_SMM_ADDR;
64 } else {
65 warn_internalerror();
66 return;
67 }
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040068 // indicate to smm_relocate_and_restore() that the SMM code was executed
69 outb(0x00, PORT_SMI_STATUS);
70 return;
71 }
72}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040073
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040074extern void entry_smi(void);
75// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR)
76#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \
77 | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24))
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040078
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040079static void
Isaku Yamahataaec19c92010-07-20 16:50:46 +090080smm_save_and_copy(void)
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040081{
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040082 // save original memory content
Kevin O'Connor31bcda22014-05-28 13:33:50 -040083 struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR;
84 struct smm_layout *smm = (void*)BUILD_SMM_ADDR;
85 memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu));
86 memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry));
Kevin O'Connore682cbc2008-12-06 23:11:56 -050087
Kevin O'Connorf4c511c2014-04-07 19:49:12 -040088 // Setup code entry point.
Kevin O'Connor31bcda22014-05-28 13:33:50 -040089 initsmm->codeentry = SMI_INSN;
Isaku Yamahataaec19c92010-07-20 16:50:46 +090090}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040091
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040092static void
Isaku Yamahataaec19c92010-07-20 16:50:46 +090093smm_relocate_and_restore(void)
94{
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040095 /* init APM status port */
Kevin O'Connore682cbc2008-12-06 23:11:56 -050096 outb(0x01, PORT_SMI_STATUS);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040097
98 /* raise an SMI interrupt */
Kevin O'Connore682cbc2008-12-06 23:11:56 -050099 outb(0x00, PORT_SMI_CMD);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400100
101 /* wait until SMM code executed */
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500102 while (inb(PORT_SMI_STATUS) != 0x00)
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400103 ;
104
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500105 /* restore original memory content */
Kevin O'Connor31bcda22014-05-28 13:33:50 -0400106 struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR;
107 struct smm_layout *smm = (void*)BUILD_SMM_ADDR;
108 memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu));
109 memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry));
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400110
Kevin O'Connorf4c511c2014-04-07 19:49:12 -0400111 // Setup code entry point.
Kevin O'Connor31bcda22014-05-28 13:33:50 -0400112 smm->codeentry = SMI_INSN;
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400113 wbinvd();
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900114}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400115
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400116// This code is hardcoded for PIIX4 Power Management device.
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500117static void piix4_apmc_smm_setup(int isabdf, int i440_bdf)
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400118{
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400119 /* check if SMM init is already done */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500120 u32 value = pci_config_readl(isabdf, PIIX_DEVACTB);
Paolo Bonzini40d03122014-05-15 13:22:26 +0200121 if (value & PIIX_DEVACTB_APMC_EN)
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400122 return;
123
124 /* enable the SMM memory window */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500125 pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x48);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400126
127 smm_save_and_copy();
128
129 /* enable SMI generation when writing to the APMC register */
Paolo Bonzini40d03122014-05-15 13:22:26 +0200130 pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400131
Paolo Bonzini457ba422014-05-15 13:22:28 +0200132 /* enable SMI generation */
133 value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL);
134 outl(acpi_pm_base + PIIX_PMIO_GLBCTL,
135 value | PIIX_PMIO_GLBCTL_SMI_EN);
136
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400137 smm_relocate_and_restore();
138
139 /* close the SMM memory window and enable normal SMM */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500140 pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x08);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400141}
142
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100143/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500144void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf)
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100145{
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100146 /* check if SMM init is already done */
Gerd Hoffmanna217de92014-05-13 14:01:22 +0200147 u32 value = inl(acpi_pm_base + ICH9_PMIO_SMI_EN);
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100148 if (value & ICH9_PMIO_SMI_EN_APMC_EN)
149 return;
150
151 /* enable the SMM memory window */
152 pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48);
153
154 smm_save_and_copy();
155
156 /* enable SMI generation when writing to the APMC register */
Paolo Bonzini457ba422014-05-15 13:22:28 +0200157 outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN,
Gerd Hoffmanna217de92014-05-13 14:01:22 +0200158 acpi_pm_base + ICH9_PMIO_SMI_EN);
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100159
Paolo Bonzini457ba422014-05-15 13:22:28 +0200160 /* lock SMI generation */
161 value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1);
162 pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1,
163 value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK);
164
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100165 smm_relocate_and_restore();
166
167 /* close the SMM memory window and enable normal SMM */
168 pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08);
169}
170
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500171static int SMMISADeviceBDF = -1, SMMPMDeviceBDF = -1;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900172
173void
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500174smm_device_setup(void)
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900175{
Kevin O'Connora2a86e22013-02-13 19:35:12 -0500176 if (!CONFIG_USE_SMM)
Kevin O'Connor028f3482014-04-11 12:08:33 -0400177 return;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900178
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500179 struct pci_device *isapci, *pmpci;
180 isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3);
181 pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441);
182 if (isapci && pmpci) {
183 SMMISADeviceBDF = isapci->bdf;
184 SMMPMDeviceBDF = pmpci->bdf;
185 return;
186 }
187 isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC);
188 pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH);
189 if (isapci && pmpci) {
190 SMMISADeviceBDF = isapci->bdf;
191 SMMPMDeviceBDF = pmpci->bdf;
192 }
193}
194
195void
196smm_setup(void)
197{
198 if (!CONFIG_USE_SMM || SMMISADeviceBDF < 0)
Kevin O'Connor028f3482014-04-11 12:08:33 -0400199 return;
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500200
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900201 dprintf(3, "init smm\n");
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500202 u16 device = pci_config_readw(SMMISADeviceBDF, PCI_DEVICE_ID);
203 if (device == PCI_DEVICE_ID_INTEL_82371AB_3)
204 piix4_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF);
205 else
206 ich9_lpc_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400207}