blob: 3f01207e170ee366a5cb7011ef074a71cb9ddb33 [file] [log] [blame]
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -04001// System Management Mode support (on emulators)
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// 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'Connor5d369d82013-09-02 20:48:46 -04008#include "hw/pci.h" // pci_config_writel
9#include "hw/pci_regs.h" // PCI_DEVICE_ID
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040010#include "util.h" // wbinvd
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "config.h" // CONFIG_*
12#include "ioport.h" // outb
Kevin O'Connor5d369d82013-09-02 20:48:46 -040013#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
Isaku Yamahata72a590e2012-11-28 10:17:33 +010014#include "dev-q35.h"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040015
Kevin O'Connorfd459e82013-08-10 11:39:14 -040016extern u8 smm_relocation_start, smm_relocation_end;
Kevin O'Connor52a300f2009-12-26 23:32:57 -050017ASM32FLAT(
Kevin O'Connorfd459e82013-08-10 11:39:14 -040018 ".global smm_relocation_start, smm_relocation_end\n"
19 " .code16gcc\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040020
21 /* code to relocate SMBASE to 0xa0000 */
22 "smm_relocation_start:\n"
Kevin O'Connorfd459e82013-08-10 11:39:14 -040023 " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7efc, %ebx\n"
24 " addr32 movb (%ebx), %al\n" /* revision ID to see if x86_64 or x86 */
25 " cmpb $0x64, %al\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040026 " je 1f\n"
Kevin O'Connorfd459e82013-08-10 11:39:14 -040027 " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7ef8, %ebx\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040028 " jmp 2f\n"
29 "1:\n"
Kevin O'Connorfd459e82013-08-10 11:39:14 -040030 " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7f00, %ebx\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040031 "2:\n"
Kevin O'Connore682cbc2008-12-06 23:11:56 -050032 " movl $" __stringify(BUILD_SMM_ADDR) " - 0x8000, %eax\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040033 " addr32 movl %eax, (%ebx)\n"
34 /* indicate to the BIOS that the SMM code was executed */
Kevin O'Connorfd459e82013-08-10 11:39:14 -040035 " movb $0x00, %al\n"
Kevin O'Connore682cbc2008-12-06 23:11:56 -050036 " movw $" __stringify(PORT_SMI_STATUS) ", %dx\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040037 " outb %al, %dx\n"
38 " rsm\n"
39 "smm_relocation_end:\n"
Kevin O'Connorfd459e82013-08-10 11:39:14 -040040 " .code32\n"
41 );
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040042
Kevin O'Connorfd459e82013-08-10 11:39:14 -040043extern u8 smm_code_start, smm_code_end;
44ASM32FLAT(
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040045 /* minimal SMM code to enable or disable ACPI */
Kevin O'Connorfd459e82013-08-10 11:39:14 -040046 ".global smm_code_start, smm_code_end\n"
47 " .code16gcc\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040048 "smm_code_start:\n"
Kevin O'Connore682cbc2008-12-06 23:11:56 -050049 " movw $" __stringify(PORT_SMI_CMD) ", %dx\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040050 " inb %dx, %al\n"
Kevin O'Connorfd459e82013-08-10 11:39:14 -040051 " cmpb $0xf0, %al\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040052 " jne 1f\n"
53
54 /* ACPI disable */
Kevin O'Connorfd459e82013-08-10 11:39:14 -040055 " movw $" __stringify(PORT_ACPI_PM_BASE) " + 0x04, %dx\n" /* PMCNTRL */
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040056 " inw %dx, %ax\n"
57 " andw $~1, %ax\n"
58 " outw %ax, %dx\n"
59
60 " jmp 2f\n"
61
62 "1:\n"
Kevin O'Connorfd459e82013-08-10 11:39:14 -040063 " cmpb $0xf1, %al\n"
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040064 " jne 2f\n"
65
66 /* ACPI enable */
Kevin O'Connorfd459e82013-08-10 11:39:14 -040067 " movw $" __stringify(PORT_ACPI_PM_BASE) " + 0x04, %dx\n" /* PMCNTRL */
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040068 " inw %dx, %ax\n"
69 " orw $1, %ax\n"
70 " outw %ax, %dx\n"
71
72 "2:\n"
73 " rsm\n"
74 "smm_code_end:\n"
75 " .code32\n"
76 );
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040077
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040078static void
Isaku Yamahataaec19c92010-07-20 16:50:46 +090079smm_save_and_copy(void)
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040080{
Kevin O'Connore682cbc2008-12-06 23:11:56 -050081 /* save original memory content */
82 memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE);
83
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040084 /* copy the SMM relocation code */
Kevin O'Connore682cbc2008-12-06 23:11:56 -050085 memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_relocation_start,
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040086 &smm_relocation_end - &smm_relocation_start);
Isaku Yamahataaec19c92010-07-20 16:50:46 +090087}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040088
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040089static void
Isaku Yamahataaec19c92010-07-20 16:50:46 +090090smm_relocate_and_restore(void)
91{
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040092 /* init APM status port */
Kevin O'Connore682cbc2008-12-06 23:11:56 -050093 outb(0x01, PORT_SMI_STATUS);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040094
95 /* raise an SMI interrupt */
Kevin O'Connore682cbc2008-12-06 23:11:56 -050096 outb(0x00, PORT_SMI_CMD);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -040097
98 /* wait until SMM code executed */
Kevin O'Connore682cbc2008-12-06 23:11:56 -050099 while (inb(PORT_SMI_STATUS) != 0x00)
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400100 ;
101
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500102 /* restore original memory content */
103 memcpy((void *)BUILD_SMM_INIT_ADDR, (void *)BUILD_SMM_ADDR, BUILD_SMM_SIZE);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400104
105 /* copy the SMM code */
Kevin O'Connore682cbc2008-12-06 23:11:56 -0500106 memcpy((void *)BUILD_SMM_ADDR, &smm_code_start
107 , &smm_code_end - &smm_code_start);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400108 wbinvd();
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900109}
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400110
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400111#define I440FX_SMRAM 0x72
112#define PIIX_DEVACTB 0x58
113#define PIIX_APMC_EN (1 << 25)
114
115// This code is hardcoded for PIIX4 Power Management device.
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500116static void piix4_apmc_smm_setup(int isabdf, int i440_bdf)
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400117{
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400118 /* check if SMM init is already done */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500119 u32 value = pci_config_readl(isabdf, PIIX_DEVACTB);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400120 if (value & PIIX_APMC_EN)
121 return;
122
123 /* enable the SMM memory window */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500124 pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x48);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400125
126 smm_save_and_copy();
127
128 /* enable SMI generation when writing to the APMC register */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500129 pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_APMC_EN);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400130
131 smm_relocate_and_restore();
132
133 /* close the SMM memory window and enable normal SMM */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500134 pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x08);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -0400135}
136
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100137/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500138void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf)
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100139{
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100140 /* check if SMM init is already done */
141 u32 value = inl(PORT_ACPI_PM_BASE + ICH9_PMIO_SMI_EN);
142 if (value & ICH9_PMIO_SMI_EN_APMC_EN)
143 return;
144
145 /* enable the SMM memory window */
146 pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48);
147
148 smm_save_and_copy();
149
150 /* enable SMI generation when writing to the APMC register */
151 outl(value | ICH9_PMIO_SMI_EN_APMC_EN,
152 PORT_ACPI_PM_BASE + ICH9_PMIO_SMI_EN);
153
154 smm_relocate_and_restore();
155
156 /* close the SMM memory window and enable normal SMM */
157 pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08);
158}
159
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500160static int SMMISADeviceBDF = -1, SMMPMDeviceBDF = -1;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900161
162void
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500163smm_device_setup(void)
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900164{
Kevin O'Connora2a86e22013-02-13 19:35:12 -0500165 if (!CONFIG_USE_SMM)
Ian Campbell12c991e2012-06-28 11:08:33 +0100166 return;
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900167
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500168 struct pci_device *isapci, *pmpci;
169 isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3);
170 pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441);
171 if (isapci && pmpci) {
172 SMMISADeviceBDF = isapci->bdf;
173 SMMPMDeviceBDF = pmpci->bdf;
174 return;
175 }
176 isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC);
177 pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH);
178 if (isapci && pmpci) {
179 SMMISADeviceBDF = isapci->bdf;
180 SMMPMDeviceBDF = pmpci->bdf;
181 }
182}
183
184void
185smm_setup(void)
186{
187 if (!CONFIG_USE_SMM || SMMISADeviceBDF < 0)
188 return;
189
Isaku Yamahataaec19c92010-07-20 16:50:46 +0900190 dprintf(3, "init smm\n");
Kevin O'Connorcdbac7f2013-03-08 19:33:39 -0500191 u16 device = pci_config_readw(SMMISADeviceBDF, PCI_DEVICE_ID);
192 if (device == PCI_DEVICE_ID_INTEL_82371AB_3)
193 piix4_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF);
194 else
195 ich9_lpc_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF);
Kevin O'Connorf7ba6d72008-07-04 05:05:54 -0400196}