blob: 8f99b34fa8d3c4a63e7b54c4a7f1fd1e74ea7476 [file] [log] [blame]
Kevin O'Connor84ad59a2008-07-04 05:47:26 -04001// Support for generating ACPI tables (on emulators)
Kevin O'Connor276d4a92008-06-11 22:47:01 -04002//
Kevin O'Connore2074bf2010-08-03 21:30:03 -04003// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor276d4a92008-06-11 22:47:01 -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'Connor276d4a92008-06-11 22:47:01 -04007
Kevin O'Connorb3064592012-08-14 21:20:10 -04008#include "byteorder.h" // cpu_to_le16
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04009#include "config.h" // CONFIG_*
10#include "dev-q35.h"
Kevin O'Connor5d369d82013-09-02 20:48:46 -040011#include "hw/pci.h" // pci_find_init_device
12#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL
13#include "hw/pci_regs.h" // PCI_INTERRUPT_LINE
Kevin O'Connor4bc49972012-05-13 22:58:08 -040014#include "ioport.h" // inl
Kevin O'Connor9dea5902013-09-14 20:23:54 -040015#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040016#include "output.h" // dprintf
17#include "paravirt.h" // RamSize
Kevin O'Connor41639f82013-09-14 19:37:36 -040018#include "romfile.h" // romfile_loadint
Kevin O'Connor5a7545c2013-09-14 22:54:44 -040019#include "std/acpi.h" // struct rsdp_descriptor
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040020#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040021#include "util.h" // MaxCountCPUs
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040022#include "x86.h" // readl
Kevin O'Connor276d4a92008-06-11 22:47:01 -040023
Kevin O'Connorccee6e82013-09-02 21:25:21 -040024#include "src/fw/acpi-dsdt.hex"
Kevin O'Connor276d4a92008-06-11 22:47:01 -040025
Gerd Hoffmann5b631092013-07-25 09:47:18 +020026u32 acpi_pm1a_cnt VARFSEG;
27
Kevin O'Connor2929c352009-07-25 13:48:27 -040028static void
Kevin O'Connora9242a22009-10-07 19:42:07 -040029build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev)
Kevin O'Connor276d4a92008-06-11 22:47:01 -040030{
Kevin O'Connor51684b72013-03-30 10:18:47 -040031 h->signature = cpu_to_le32(sig);
Kevin O'Connor276d4a92008-06-11 22:47:01 -040032 h->length = cpu_to_le32(len);
33 h->revision = rev;
Kevin O'Connore52ad392013-02-20 23:48:22 -050034 memcpy(h->oem_id, BUILD_APPNAME6, 6);
35 memcpy(h->oem_table_id, BUILD_APPNAME4, 4);
Kevin O'Connor9967ab72008-12-18 21:57:33 -050036 memcpy(h->oem_table_id + 4, (void*)&sig, 4);
Kevin O'Connor276d4a92008-06-11 22:47:01 -040037 h->oem_revision = cpu_to_le32(1);
Kevin O'Connore52ad392013-02-20 23:48:22 -050038 memcpy(h->asl_compiler_id, BUILD_APPNAME4, 4);
Kevin O'Connor276d4a92008-06-11 22:47:01 -040039 h->asl_compiler_revision = cpu_to_le32(1);
Kevin O'Connor523e5a92009-07-04 13:46:33 -040040 h->checksum -= checksum(h, len);
Kevin O'Connor276d4a92008-06-11 22:47:01 -040041}
42
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040043#define PIIX4_ACPI_ENABLE 0xf1
44#define PIIX4_ACPI_DISABLE 0xf0
45#define PIIX4_GPE0_BLK 0xafe0
46#define PIIX4_GPE0_BLK_LEN 4
47
Isaku Yamahata229e8e22012-11-28 10:17:31 +010048#define PIIX4_PM_INTRRUPT 9 // irq 9
49
Kevin O'Connord83c87b2013-01-21 01:14:12 -050050static void piix4_fadt_setup(struct pci_device *pci, void *arg)
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040051{
52 struct fadt_descriptor_rev1 *fadt = arg;
Isaku Yamahata229e8e22012-11-28 10:17:31 +010053
54 fadt->model = 1;
55 fadt->reserved1 = 0;
56 fadt->sci_int = cpu_to_le16(PIIX4_PM_INTRRUPT);
57 fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040058 fadt->acpi_enable = PIIX4_ACPI_ENABLE;
59 fadt->acpi_disable = PIIX4_ACPI_DISABLE;
Isaku Yamahata229e8e22012-11-28 10:17:31 +010060 fadt->pm1a_evt_blk = cpu_to_le32(PORT_ACPI_PM_BASE);
61 fadt->pm1a_cnt_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x04);
62 fadt->pm_tmr_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x08);
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040063 fadt->gpe0_blk = cpu_to_le32(PIIX4_GPE0_BLK);
Isaku Yamahata229e8e22012-11-28 10:17:31 +010064 fadt->pm1_evt_len = 4;
65 fadt->pm1_cnt_len = 2;
66 fadt->pm_tmr_len = 4;
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040067 fadt->gpe0_blk_len = PIIX4_GPE0_BLK_LEN;
Isaku Yamahata229e8e22012-11-28 10:17:31 +010068 fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported
69 fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported
70 /* WBINVD + PROC_C1 + SLP_BUTTON + RTC_S4 + USE_PLATFORM_CLOCK */
71 fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 7) |
72 (1 << 15));
Kevin O'Connor6e4583c2011-06-19 10:09:26 -040073}
74
Isaku Yamahata72a590e2012-11-28 10:17:33 +010075/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */
Kevin O'Connord83c87b2013-01-21 01:14:12 -050076void ich9_lpc_fadt_setup(struct pci_device *dev, void *arg)
Isaku Yamahata72a590e2012-11-28 10:17:33 +010077{
78 struct fadt_descriptor_rev1 *fadt = arg;
79
80 fadt->model = 1;
81 fadt->reserved1 = 0;
82 fadt->sci_int = cpu_to_le16(9);
83 fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD);
84 fadt->acpi_enable = ICH9_ACPI_ENABLE;
85 fadt->acpi_disable = ICH9_ACPI_DISABLE;
86 fadt->pm1a_evt_blk = cpu_to_le32(PORT_ACPI_PM_BASE);
87 fadt->pm1a_cnt_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x04);
88 fadt->pm_tmr_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x08);
89 fadt->gpe0_blk = cpu_to_le32(PORT_ACPI_PM_BASE + ICH9_PMIO_GPE0_STS);
90 fadt->pm1_evt_len = 4;
91 fadt->pm1_cnt_len = 2;
92 fadt->pm_tmr_len = 4;
93 fadt->gpe0_blk_len = ICH9_PMIO_GPE0_BLK_LEN;
94 fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported
95 fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported
Michael S. Tsirkined88f652013-07-17 08:14:53 +030096 /* WBINVD + PROC_C1 + SLP_BUTTON + RTC_S4 + USE_PLATFORM_CLOCK */
97 fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 7) |
98 (1 << 15));
Isaku Yamahata72a590e2012-11-28 10:17:33 +010099}
100
Isaku Yamahatafe54a532010-07-20 16:37:18 +0900101static const struct pci_device_id fadt_init_tbl[] = {
102 /* PIIX4 Power Management device (for ACPI) */
103 PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500104 piix4_fadt_setup),
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100105 PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC,
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500106 ich9_lpc_fadt_setup),
Isaku Yamahatafe54a532010-07-20 16:37:18 +0900107 PCI_DEVICE_END
108};
109
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900110static void fill_dsdt(struct fadt_descriptor_rev1 *fadt, void *dsdt)
111{
112 if (fadt->dsdt) {
113 free((void *)le32_to_cpu(fadt->dsdt));
114 }
115 fadt->dsdt = cpu_to_le32((u32)dsdt);
116 fadt->checksum -= checksum(fadt, sizeof(*fadt));
117 dprintf(1, "ACPI DSDT=%p\n", dsdt);
118}
119
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400120static void *
121build_fadt(struct pci_device *pci)
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400122{
Kevin O'Connor2929c352009-07-25 13:48:27 -0400123 struct fadt_descriptor_rev1 *fadt = malloc_high(sizeof(*fadt));
Kevin O'Connor415d4292009-08-30 19:19:31 -0400124 struct facs_descriptor_rev1 *facs = memalign_high(64, sizeof(*facs));
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400125
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900126 if (!fadt || !facs) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500127 warn_noalloc();
Kevin O'Connora9242a22009-10-07 19:42:07 -0400128 return NULL;
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400129 }
130
Kevin O'Connor2929c352009-07-25 13:48:27 -0400131 /* FACS */
Kevin O'Connor2929c352009-07-25 13:48:27 -0400132 memset(facs, 0, sizeof(*facs));
Kevin O'Connor51684b72013-03-30 10:18:47 -0400133 facs->signature = cpu_to_le32(FACS_SIGNATURE);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400134 facs->length = cpu_to_le32(sizeof(*facs));
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400135
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400136 /* FADT */
137 memset(fadt, 0, sizeof(*fadt));
Kevin O'Connor2929c352009-07-25 13:48:27 -0400138 fadt->firmware_ctrl = cpu_to_le32((u32)facs);
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500139 fadt->dsdt = 0; /* dsdt will be filled later in acpi_setup()
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900140 by fill_dsdt() */
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400141 pci_init_device(fadt_init_tbl, pci, fadt);
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400142
Kevin O'Connora9242a22009-10-07 19:42:07 -0400143 build_header((void*)fadt, FACP_SIGNATURE, sizeof(*fadt), 1);
144
145 return fadt;
Kevin O'Connor2929c352009-07-25 13:48:27 -0400146}
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400147
Kevin O'Connora9242a22009-10-07 19:42:07 -0400148static void*
149build_madt(void)
Kevin O'Connor2929c352009-07-25 13:48:27 -0400150{
Kevin O'Connor2929c352009-07-25 13:48:27 -0400151 int madt_size = (sizeof(struct multiple_apic_table)
Kevin O'Connora26df9b2009-10-09 09:42:11 -0400152 + sizeof(struct madt_processor_apic) * MaxCountCPUs
Kevin O'Connor2929c352009-07-25 13:48:27 -0400153 + sizeof(struct madt_io_apic)
Kenji Kaneshige70986382011-10-10 14:06:17 +0800154 + sizeof(struct madt_intsrcovr) * 16
155 + sizeof(struct madt_local_nmi));
156
Kevin O'Connor2929c352009-07-25 13:48:27 -0400157 struct multiple_apic_table *madt = malloc_high(madt_size);
158 if (!madt) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500159 warn_noalloc();
Kevin O'Connora9242a22009-10-07 19:42:07 -0400160 return NULL;
Kevin O'Connor2929c352009-07-25 13:48:27 -0400161 }
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500162 memset(madt, 0, madt_size);
163 madt->local_apic_address = cpu_to_le32(BUILD_APIC_ADDR);
164 madt->flags = cpu_to_le32(1);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400165 struct madt_processor_apic *apic = (void*)&madt[1];
166 int i;
Kevin O'Connora26df9b2009-10-09 09:42:11 -0400167 for (i=0; i<MaxCountCPUs; i++) {
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500168 apic->type = APIC_PROCESSOR;
169 apic->length = sizeof(*apic);
170 apic->processor_id = i;
171 apic->local_apic_id = i;
Eduardo Habkost008c1fc2012-07-25 15:45:30 -0300172 if (apic_id_is_present(apic->local_apic_id))
Kevin O'Connora26df9b2009-10-09 09:42:11 -0400173 apic->flags = cpu_to_le32(1);
174 else
175 apic->flags = cpu_to_le32(0);
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500176 apic++;
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400177 }
Kevin O'Connor2929c352009-07-25 13:48:27 -0400178 struct madt_io_apic *io_apic = (void*)apic;
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500179 io_apic->type = APIC_IO;
180 io_apic->length = sizeof(*io_apic);
Eduardo Habkoste39b9382012-07-25 15:45:29 -0300181 io_apic->io_apic_id = BUILD_IOAPIC_ID;
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500182 io_apic->address = cpu_to_le32(BUILD_IOAPIC_ADDR);
183 io_apic->interrupt = cpu_to_le32(0);
184
Kevin O'Connord10e4442009-03-01 12:41:20 -0500185 struct madt_intsrcovr *intsrcovr = (void*)&io_apic[1];
Kevin O'Connor56c50892013-02-09 19:25:51 -0500186 if (romfile_loadint("etc/irq0-override", 0)) {
Kevin O'Connorb64db302009-07-29 19:20:03 -0400187 memset(intsrcovr, 0, sizeof(*intsrcovr));
188 intsrcovr->type = APIC_XRUPT_OVERRIDE;
189 intsrcovr->length = sizeof(*intsrcovr);
190 intsrcovr->source = 0;
Kevin O'Connor51684b72013-03-30 10:18:47 -0400191 intsrcovr->gsi = cpu_to_le32(2);
192 intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */
Kevin O'Connorb64db302009-07-29 19:20:03 -0400193 intsrcovr++;
194 }
195 for (i = 1; i < 16; i++) {
Kevin O'Connord8388152013-03-18 20:26:54 -0400196 if (!(BUILD_PCI_IRQS & (1 << i)))
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500197 /* No need for a INT source override structure. */
198 continue;
Kevin O'Connord10e4442009-03-01 12:41:20 -0500199 memset(intsrcovr, 0, sizeof(*intsrcovr));
200 intsrcovr->type = APIC_XRUPT_OVERRIDE;
201 intsrcovr->length = sizeof(*intsrcovr);
202 intsrcovr->source = i;
Kevin O'Connor51684b72013-03-30 10:18:47 -0400203 intsrcovr->gsi = cpu_to_le32(i);
204 intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500205 intsrcovr++;
Kevin O'Connor7061eb62009-01-04 21:48:22 -0500206 }
207
Kenji Kaneshige70986382011-10-10 14:06:17 +0800208 struct madt_local_nmi *local_nmi = (void*)intsrcovr;
209 local_nmi->type = APIC_LOCAL_NMI;
210 local_nmi->length = sizeof(*local_nmi);
211 local_nmi->processor_id = 0xff; /* all processors */
Kevin O'Connor51684b72013-03-30 10:18:47 -0400212 local_nmi->flags = cpu_to_le16(0);
Kenji Kaneshige70986382011-10-10 14:06:17 +0800213 local_nmi->lint = 1; /* LINT1 */
214 local_nmi++;
215
216 build_header((void*)madt, APIC_SIGNATURE, (void*)local_nmi - (void*)madt, 1);
Kevin O'Connora9242a22009-10-07 19:42:07 -0400217 return madt;
Kevin O'Connor2929c352009-07-25 13:48:27 -0400218}
219
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400220// Encode a hex value
221static inline char getHex(u32 val) {
222 val &= 0x0f;
223 return (val <= 9) ? ('0' + val) : ('A' + val - 10);
224}
225
226// Encode a length in an SSDT.
227static u8 *
228encodeLen(u8 *ssdt_ptr, int length, int bytes)
229{
230 switch (bytes) {
231 default:
232 case 4: ssdt_ptr[3] = ((length >> 20) & 0xff);
233 case 3: ssdt_ptr[2] = ((length >> 12) & 0xff);
234 case 2: ssdt_ptr[1] = ((length >> 4) & 0xff);
235 ssdt_ptr[0] = (((bytes-1) & 0x3) << 6) | (length & 0x0f);
236 break;
237 case 1: ssdt_ptr[0] = length & 0x3f;
238 }
239 return ssdt_ptr + bytes;
240}
241
Kevin O'Connorccee6e82013-09-02 21:25:21 -0400242#include "src/fw/ssdt-proc.hex"
Michael S. Tsirkinc736f0a2011-10-04 15:26:19 +0200243
244/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */
Paolo Bonzinic8ed45e2012-08-02 15:07:22 +0200245#define PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2)
246#define PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4)
247#define PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start)
248#define PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start)
249#define PROC_AML (ssdp_proc_aml + *ssdt_proc_start)
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400250
Paolo Bonzini71ed8e32012-08-02 15:07:26 +0200251/* 0x5B 0x82 DeviceOp PkgLength NameString */
252#define PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1)
253#define PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start)
254#define PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start)
255#define PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start)
256#define PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start)
257#define PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start)
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200258#define PCI_SLOTS 32
259
Kevin O'Connor2929c352009-07-25 13:48:27 -0400260#define SSDT_SIGNATURE 0x54445353 // SSDT
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200261#define SSDT_HEADER_LENGTH 36
262
Kevin O'Connorccee6e82013-09-02 21:25:21 -0400263#include "src/fw/ssdt-misc.hex"
264#include "src/fw/ssdt-pcihp.hex"
Paolo Bonzini71ed8e32012-08-02 15:07:26 +0200265
266#define PCI_RMV_BASE 0xae0c
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200267
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200268static u8*
269build_notify(u8 *ssdt_ptr, const char *name, int skip, int count,
270 const char *target, int ofs)
271{
272 count -= skip;
273
274 *(ssdt_ptr++) = 0x14; // MethodOp
275 ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*count), 2);
276 memcpy(ssdt_ptr, name, 4);
277 ssdt_ptr += 4;
278 *(ssdt_ptr++) = 0x02; // MethodOp
279
280 int i;
281 for (i = skip; count-- > 0; i++) {
282 *(ssdt_ptr++) = 0xA0; // IfOp
283 ssdt_ptr = encodeLen(ssdt_ptr, 11, 1);
284 *(ssdt_ptr++) = 0x93; // LEqualOp
285 *(ssdt_ptr++) = 0x68; // Arg0Op
286 *(ssdt_ptr++) = 0x0A; // BytePrefix
287 *(ssdt_ptr++) = i;
288 *(ssdt_ptr++) = 0x86; // NotifyOp
289 memcpy(ssdt_ptr, target, 4);
290 ssdt_ptr[ofs] = getHex(i >> 4);
291 ssdt_ptr[ofs + 1] = getHex(i);
292 ssdt_ptr += 4;
293 *(ssdt_ptr++) = 0x69; // Arg1Op
294 }
295 return ssdt_ptr;
296}
297
Paolo Bonzini71ed8e32012-08-02 15:07:26 +0200298static void patch_pcihp(int slot, u8 *ssdt_ptr, u32 eject)
299{
300 ssdt_ptr[PCIHP_OFFSET_HEX] = getHex(slot >> 4);
301 ssdt_ptr[PCIHP_OFFSET_HEX+1] = getHex(slot);
302 ssdt_ptr[PCIHP_OFFSET_ID] = slot;
303 ssdt_ptr[PCIHP_OFFSET_ADR + 2] = slot;
304
305 /* Runtime patching of EJ0: to disable hotplug for a slot,
306 * replace the method name: _EJ0 by EJ0_. */
307 /* Sanity check */
308 if (memcmp(ssdt_ptr + PCIHP_OFFSET_EJ0, "_EJ0", 4)) {
309 warn_internalerror();
310 }
311 if (!eject) {
312 memcpy(ssdt_ptr + PCIHP_OFFSET_EJ0, "EJ0_", 4);
313 }
314}
315
Kevin O'Connora9242a22009-10-07 19:42:07 -0400316static void*
317build_ssdt(void)
Kevin O'Connor2929c352009-07-25 13:48:27 -0400318{
Kevin O'Connora26df9b2009-10-09 09:42:11 -0400319 int acpi_cpus = MaxCountCPUs > 0xff ? 0xff : MaxCountCPUs;
Kevin O'Connor3f8d7352013-02-27 20:46:40 -0500320 int length = (sizeof(ssdp_misc_aml) // _S3_ / _S4_ / _S5_
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200321 + (1+3+4) // Scope(_SB_)
Paolo Bonzinic8ed45e2012-08-02 15:07:22 +0200322 + (acpi_cpus * PROC_SIZEOF) // procs
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200323 + (1+2+5+(12*acpi_cpus)) // NTFY
324 + (6+2+1+(1*acpi_cpus)) // CPON
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200325 + (1+3+4) // Scope(PCI0)
Paolo Bonzini71ed8e32012-08-02 15:07:26 +0200326 + ((PCI_SLOTS - 1) * PCIHP_SIZEOF) // slots
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200327 + (1+2+5+(12*(PCI_SLOTS - 1)))); // PCNT
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200328 u8 *ssdt = malloc_high(length);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400329 if (! ssdt) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500330 warn_noalloc();
Kevin O'Connora9242a22009-10-07 19:42:07 -0400331 return NULL;
Kevin O'Connor2929c352009-07-25 13:48:27 -0400332 }
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200333 u8 *ssdt_ptr = ssdt;
334
335 // Copy header and encode fwcfg values in the S3_ / S4_ / S5_ packages
336 int sys_state_size;
337 char *sys_states = romfile_loadfile("etc/system-states", &sys_state_size);
338 if (!sys_states || sys_state_size != 6)
339 sys_states = (char[]){128, 0, 0, 129, 128, 128};
340
Kevin O'Connor3f8d7352013-02-27 20:46:40 -0500341 memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml));
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200342 if (!(sys_states[3] & 128))
343 ssdt_ptr[acpi_s3_name[0]] = 'X';
344 if (!(sys_states[4] & 128))
345 ssdt_ptr[acpi_s4_name[0]] = 'X';
346 else
347 ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127;
Kevin O'Connor76e58022013-03-06 21:50:09 -0500348
349 // store pci io windows
Kevin O'Connor51684b72013-03-30 10:18:47 -0400350 *(u32*)&ssdt_ptr[acpi_pci32_start[0]] = cpu_to_le32(pcimem_start);
351 *(u32*)&ssdt_ptr[acpi_pci32_end[0]] = cpu_to_le32(pcimem_end - 1);
Kevin O'Connor76e58022013-03-06 21:50:09 -0500352 if (pcimem64_start) {
353 ssdt_ptr[acpi_pci64_valid[0]] = 1;
Kevin O'Connor51684b72013-03-30 10:18:47 -0400354 *(u64*)&ssdt_ptr[acpi_pci64_start[0]] = cpu_to_le64(pcimem64_start);
355 *(u64*)&ssdt_ptr[acpi_pci64_end[0]] = cpu_to_le64(pcimem64_end - 1);
356 *(u64*)&ssdt_ptr[acpi_pci64_length[0]] = cpu_to_le64(
357 pcimem64_end - pcimem64_start);
Kevin O'Connor76e58022013-03-06 21:50:09 -0500358 } else {
359 ssdt_ptr[acpi_pci64_valid[0]] = 0;
360 }
361
Hu Taoe9725dd2013-04-05 15:17:51 +0800362 int pvpanic_port = romfile_loadint("etc/pvpanic-port", 0x0);
363 *(u16 *)(ssdt_ptr + *ssdt_isa_pest) = pvpanic_port;
364
Kevin O'Connor3f8d7352013-02-27 20:46:40 -0500365 ssdt_ptr += sizeof(ssdp_misc_aml);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400366
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400367 // build Scope(_SB_) header
Kevin O'Connor2929c352009-07-25 13:48:27 -0400368 *(ssdt_ptr++) = 0x10; // ScopeOp
Paolo Bonzinicf5bef42012-08-02 15:07:21 +0200369 ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3);
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400370 *(ssdt_ptr++) = '_';
371 *(ssdt_ptr++) = 'S';
372 *(ssdt_ptr++) = 'B';
Kevin O'Connor2929c352009-07-25 13:48:27 -0400373 *(ssdt_ptr++) = '_';
374
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400375 // build Processor object for each processor
Kevin O'Connor2929c352009-07-25 13:48:27 -0400376 int i;
377 for (i=0; i<acpi_cpus; i++) {
Paolo Bonzinic8ed45e2012-08-02 15:07:22 +0200378 memcpy(ssdt_ptr, PROC_AML, PROC_SIZEOF);
379 ssdt_ptr[PROC_OFFSET_CPUHEX] = getHex(i >> 4);
380 ssdt_ptr[PROC_OFFSET_CPUHEX+1] = getHex(i);
381 ssdt_ptr[PROC_OFFSET_CPUID1] = i;
382 ssdt_ptr[PROC_OFFSET_CPUID2] = i;
383 ssdt_ptr += PROC_SIZEOF;
Kevin O'Connor2929c352009-07-25 13:48:27 -0400384 }
385
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400386 // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}"
Eduardo Habkost008c1fc2012-07-25 15:45:30 -0300387 // Arg0 = Processor ID = APIC ID
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200388 ssdt_ptr = build_notify(ssdt_ptr, "NTFY", 0, acpi_cpus, "CP00", 2);
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400389
390 // build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })"
391 *(ssdt_ptr++) = 0x08; // NameOp
392 *(ssdt_ptr++) = 'C';
393 *(ssdt_ptr++) = 'P';
394 *(ssdt_ptr++) = 'O';
395 *(ssdt_ptr++) = 'N';
396 *(ssdt_ptr++) = 0x12; // PackageOp
397 ssdt_ptr = encodeLen(ssdt_ptr, 2+1+(1*acpi_cpus), 2);
398 *(ssdt_ptr++) = acpi_cpus;
399 for (i=0; i<acpi_cpus; i++)
Eduardo Habkost008c1fc2012-07-25 15:45:30 -0300400 *(ssdt_ptr++) = (apic_id_is_present(i)) ? 0x01 : 0x00;
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400401
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200402 // build Scope(PCI0) opcode
403 *(ssdt_ptr++) = 0x10; // ScopeOp
404 ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3);
405 *(ssdt_ptr++) = 'P';
406 *(ssdt_ptr++) = 'C';
407 *(ssdt_ptr++) = 'I';
408 *(ssdt_ptr++) = '0';
409
Paolo Bonzini71ed8e32012-08-02 15:07:26 +0200410 // build Device object for each slot
411 u32 rmvc_pcrm = inl(PCI_RMV_BASE);
412 for (i=1; i<PCI_SLOTS; i++) {
413 u32 eject = rmvc_pcrm & (0x1 << i);
414 memcpy(ssdt_ptr, PCIHP_AML, PCIHP_SIZEOF);
415 patch_pcihp(i, ssdt_ptr, eject != 0);
416 ssdt_ptr += PCIHP_SIZEOF;
417 }
418
Paolo Bonzini7dceba32012-08-02 15:07:25 +0200419 ssdt_ptr = build_notify(ssdt_ptr, "PCNT", 1, PCI_SLOTS, "S00_", 1);
420
Kevin O'Connora9242a22009-10-07 19:42:07 -0400421 build_header((void*)ssdt, SSDT_SIGNATURE, ssdt_ptr - ssdt, 1);
422
Kevin O'Connore2074bf2010-08-03 21:30:03 -0400423 //hexdump(ssdt, ssdt_ptr - ssdt);
424
Kevin O'Connora9242a22009-10-07 19:42:07 -0400425 return ssdt;
Kevin O'Connor2929c352009-07-25 13:48:27 -0400426}
427
Kevin O'Connor45cced12013-03-30 09:34:10 -0400428#define HPET_ID 0x000
429#define HPET_PERIOD 0x004
430
Kevin O'Connorf4343772009-10-08 22:05:21 -0400431static void*
432build_hpet(void)
433{
Jan Kiszkab8a90f52011-08-29 17:50:10 +0200434 struct acpi_20_hpet *hpet;
Kevin O'Connor7859eda2011-09-20 19:40:28 -0400435 const void *hpet_base = (void *)BUILD_HPET_ADDRESS;
Jan Kiszkab8a90f52011-08-29 17:50:10 +0200436 u32 hpet_vendor = readl(hpet_base + HPET_ID) >> 16;
437 u32 hpet_period = readl(hpet_base + HPET_PERIOD);
438
439 if (hpet_vendor == 0 || hpet_vendor == 0xffff ||
Kevin O'Connor8a161c92011-09-02 18:11:58 -0400440 hpet_period == 0 || hpet_period > 100000000)
Jan Kiszkab8a90f52011-08-29 17:50:10 +0200441 return NULL;
442
443 hpet = malloc_high(sizeof(*hpet));
Kevin O'Connorf4343772009-10-08 22:05:21 -0400444 if (!hpet) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500445 warn_noalloc();
Kevin O'Connorf4343772009-10-08 22:05:21 -0400446 return NULL;
447 }
448
449 memset(hpet, 0, sizeof(*hpet));
450 /* Note timer_block_id value must be kept in sync with value advertised by
451 * emulated hpet
452 */
453 hpet->timer_block_id = cpu_to_le32(0x8086a201);
Kevin O'Connor51684b72013-03-30 10:18:47 -0400454 hpet->addr.address = cpu_to_le64(BUILD_HPET_ADDRESS);
Kevin O'Connorf4343772009-10-08 22:05:21 -0400455 build_header((void*)hpet, HPET_SIGNATURE, sizeof(*hpet), 1);
456
457 return hpet;
458}
459
Kevin O'Connor590e5542009-10-08 22:09:02 -0400460static void
461acpi_build_srat_memory(struct srat_memory_affinity *numamem,
462 u64 base, u64 len, int node, int enabled)
463{
464 numamem->type = SRAT_MEMORY;
465 numamem->length = sizeof(*numamem);
Kevin O'Connor51684b72013-03-30 10:18:47 -0400466 memset(numamem->proximity, 0, 4);
Kevin O'Connor590e5542009-10-08 22:09:02 -0400467 numamem->proximity[0] = node;
468 numamem->flags = cpu_to_le32(!!enabled);
Kevin O'Connor51684b72013-03-30 10:18:47 -0400469 numamem->base_addr = cpu_to_le64(base);
470 numamem->range_length = cpu_to_le64(len);
Kevin O'Connor590e5542009-10-08 22:09:02 -0400471}
472
Kevin O'Connor590e5542009-10-08 22:09:02 -0400473static void *
474build_srat(void)
475{
Kevin O'Connorfb76cff2013-03-23 11:38:45 -0400476 int numadatasize, numacpusize;
477 u64 *numadata = romfile_loadfile("etc/numa-nodes", &numadatasize);
478 u64 *numacpumap = romfile_loadfile("etc/numa-cpu-map", &numacpusize);
479 if (!numadata || !numacpumap)
480 goto fail;
481 int max_cpu = numacpusize / sizeof(u64);
482 int nb_numa_nodes = numadatasize / sizeof(u64);
Kevin O'Connor590e5542009-10-08 22:09:02 -0400483
484 struct system_resource_affinity_table *srat;
485 int srat_size = sizeof(*srat) +
Kevin O'Connorf9e4e372013-02-09 19:45:45 -0500486 sizeof(struct srat_processor_affinity) * max_cpu +
Kevin O'Connor590e5542009-10-08 22:09:02 -0400487 sizeof(struct srat_memory_affinity) * (nb_numa_nodes + 2);
488
489 srat = malloc_high(srat_size);
490 if (!srat) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500491 warn_noalloc();
Kevin O'Connorfb76cff2013-03-23 11:38:45 -0400492 goto fail;
Kevin O'Connor590e5542009-10-08 22:09:02 -0400493 }
494
495 memset(srat, 0, srat_size);
Kevin O'Connor51684b72013-03-30 10:18:47 -0400496 srat->reserved1=cpu_to_le32(1);
Kevin O'Connor590e5542009-10-08 22:09:02 -0400497 struct srat_processor_affinity *core = (void*)(srat + 1);
498 int i;
499 u64 curnode;
500
Kevin O'Connorf9e4e372013-02-09 19:45:45 -0500501 for (i = 0; i < max_cpu; ++i) {
Kevin O'Connor590e5542009-10-08 22:09:02 -0400502 core->type = SRAT_PROCESSOR;
503 core->length = sizeof(*core);
504 core->local_apic_id = i;
Kevin O'Connorfb76cff2013-03-23 11:38:45 -0400505 curnode = *numacpumap++;
Kevin O'Connor590e5542009-10-08 22:09:02 -0400506 core->proximity_lo = curnode;
507 memset(core->proximity_hi, 0, 3);
508 core->local_sapic_eid = 0;
Eduardo Habkost008c1fc2012-07-25 15:45:30 -0300509 if (apic_id_is_present(i))
Kevin O'Connor590e5542009-10-08 22:09:02 -0400510 core->flags = cpu_to_le32(1);
511 else
Eduardo Habkost008c1fc2012-07-25 15:45:30 -0300512 core->flags = cpu_to_le32(0);
Kevin O'Connor590e5542009-10-08 22:09:02 -0400513 core++;
514 }
515
516
517 /* the memory map is a bit tricky, it contains at least one hole
518 * from 640k-1M and possibly another one from 3.5G-4G.
519 */
520 struct srat_memory_affinity *numamem = (void*)core;
521 int slots = 0;
522 u64 mem_len, mem_base, next_base = 0;
523
524 acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1);
525 next_base = 1024 * 1024;
526 numamem++;
527 slots++;
528 for (i = 1; i < nb_numa_nodes + 1; ++i) {
529 mem_base = next_base;
530 mem_len = *numadata++;
531 if (i == 1)
532 mem_len -= 1024 * 1024;
533 next_base = mem_base + mem_len;
534
535 /* Cut out the PCI hole */
536 if (mem_base <= RamSize && next_base > RamSize) {
537 mem_len -= next_base - RamSize;
538 if (mem_len > 0) {
539 acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1);
540 numamem++;
541 slots++;
542 }
543 mem_base = 1ULL << 32;
544 mem_len = next_base - RamSize;
545 next_base += (1ULL << 32) - RamSize;
546 }
547 acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1);
548 numamem++;
549 slots++;
550 }
551 for (; slots < nb_numa_nodes + 2; slots++) {
552 acpi_build_srat_memory(numamem, 0, 0, 0, 0);
553 numamem++;
554 }
555
556 build_header((void*)srat, SRAT_SIGNATURE, srat_size, 1);
557
Isaku Yamahatae1920be2010-02-12 11:36:20 +0900558 free(numadata);
Kevin O'Connorfb76cff2013-03-23 11:38:45 -0400559 free(numacpumap);
Kevin O'Connor590e5542009-10-08 22:09:02 -0400560 return srat;
Kevin O'Connorfb76cff2013-03-23 11:38:45 -0400561fail:
562 free(numadata);
563 free(numacpumap);
564 return NULL;
Kevin O'Connor590e5542009-10-08 22:09:02 -0400565}
566
Gerd Hoffmann7e269b42012-11-28 10:17:35 +0100567static void *
568build_mcfg_q35(void)
569{
570 struct acpi_table_mcfg *mcfg;
571
572 int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]);
573 mcfg = malloc_high(len);
574 if (!mcfg) {
575 warn_noalloc();
576 return NULL;
577 }
578 memset(mcfg, 0, len);
Kevin O'Connor51684b72013-03-30 10:18:47 -0400579 mcfg->allocation[0].address = cpu_to_le64(Q35_HOST_BRIDGE_PCIEXBAR_ADDR);
580 mcfg->allocation[0].pci_segment = cpu_to_le16(Q35_HOST_PCIE_PCI_SEGMENT);
Gerd Hoffmann7e269b42012-11-28 10:17:35 +0100581 mcfg->allocation[0].start_bus_number = Q35_HOST_PCIE_START_BUS_NUMBER;
582 mcfg->allocation[0].end_bus_number = Q35_HOST_PCIE_END_BUS_NUMBER;
583
584 build_header((void *)mcfg, MCFG_SIGNATURE, len, 1);
585 return mcfg;
586}
587
Isaku Yamahata4c67f902010-07-20 16:37:19 +0900588static const struct pci_device_id acpi_find_tbl[] = {
589 /* PIIX4 Power Management device. */
590 PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL),
Isaku Yamahata72a590e2012-11-28 10:17:33 +0100591 PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, NULL),
Isaku Yamahata4c67f902010-07-20 16:37:19 +0900592 PCI_DEVICE_END,
593};
594
Kevin O'Connor2929c352009-07-25 13:48:27 -0400595struct rsdp_descriptor *RsdpAddr;
596
Kevin O'Connora9242a22009-10-07 19:42:07 -0400597#define MAX_ACPI_TABLES 20
Kevin O'Connor2929c352009-07-25 13:48:27 -0400598void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500599acpi_setup(void)
Kevin O'Connor2929c352009-07-25 13:48:27 -0400600{
601 if (! CONFIG_ACPI)
602 return;
603
604 dprintf(3, "init ACPI tables\n");
605
606 // This code is hardcoded for PIIX4 Power Management device.
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400607 struct pci_device *pci = pci_find_init_device(acpi_find_tbl, NULL);
608 if (!pci)
Kevin O'Connor2929c352009-07-25 13:48:27 -0400609 // Device not found
610 return;
611
Kevin O'Connor8a161c92011-09-02 18:11:58 -0400612 // Build ACPI tables
Kevin O'Connora9242a22009-10-07 19:42:07 -0400613 u32 tables[MAX_ACPI_TABLES], tbl_idx = 0;
614
615#define ACPI_INIT_TABLE(X) \
616 do { \
Kevin O'Connor51684b72013-03-30 10:18:47 -0400617 tables[tbl_idx] = cpu_to_le32((u32)(X)); \
618 if (le32_to_cpu(tables[tbl_idx])) \
Kevin O'Connora9242a22009-10-07 19:42:07 -0400619 tbl_idx++; \
620 } while(0)
Kevin O'Connor2929c352009-07-25 13:48:27 -0400621
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900622 struct fadt_descriptor_rev1 *fadt = build_fadt(pci);
623 ACPI_INIT_TABLE(fadt);
Kevin O'Connora9242a22009-10-07 19:42:07 -0400624 ACPI_INIT_TABLE(build_ssdt());
625 ACPI_INIT_TABLE(build_madt());
Kevin O'Connorf4343772009-10-08 22:05:21 -0400626 ACPI_INIT_TABLE(build_hpet());
Kevin O'Connor590e5542009-10-08 22:09:02 -0400627 ACPI_INIT_TABLE(build_srat());
Gerd Hoffmann7e269b42012-11-28 10:17:35 +0100628 if (pci->device == PCI_DEVICE_ID_INTEL_ICH9_LPC)
629 ACPI_INIT_TABLE(build_mcfg_q35());
Kevin O'Connor2929c352009-07-25 13:48:27 -0400630
Kevin O'Connor188d9942013-02-09 15:24:08 -0500631 struct romfile_s *file = NULL;
632 for (;;) {
633 file = romfile_findprefix("acpi/", file);
634 if (!file)
635 break;
636 struct acpi_table_header *table = malloc_high(file->size);
637 if (!table) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500638 warn_noalloc();
Kevin O'Connorcc6dc462009-10-08 21:18:41 -0400639 continue;
640 }
Kevin O'Connor188d9942013-02-09 15:24:08 -0500641 int ret = file->copy(file, table, file->size);
642 if (ret <= sizeof(*table))
643 continue;
644 if (table->signature == DSDT_SIGNATURE) {
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900645 if (fadt) {
Kevin O'Connor188d9942013-02-09 15:24:08 -0500646 fill_dsdt(fadt, table);
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900647 }
648 } else {
Kevin O'Connor188d9942013-02-09 15:24:08 -0500649 ACPI_INIT_TABLE(table);
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900650 }
Kevin O'Connorcc6dc462009-10-08 21:18:41 -0400651 if (tbl_idx == MAX_ACPI_TABLES) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500652 warn_noalloc();
Kevin O'Connorcc6dc462009-10-08 21:18:41 -0400653 break;
654 }
655 }
Michael S. Tsirkin5aef5632013-03-18 15:18:25 +0200656
657 if (CONFIG_ACPI_DSDT && fadt && !fadt->dsdt) {
Isaku Yamahataf2fd79b2011-10-24 17:11:08 +0900658 /* default DSDT */
659 void *dsdt = malloc_high(sizeof(AmlCode));
660 if (!dsdt) {
661 warn_noalloc();
662 return;
663 }
664 memcpy(dsdt, AmlCode, sizeof(AmlCode));
665 fill_dsdt(fadt, dsdt);
666 }
Kevin O'Connorcc6dc462009-10-08 21:18:41 -0400667
Kevin O'Connor8a161c92011-09-02 18:11:58 -0400668 // Build final rsdt table
Kevin O'Connora9242a22009-10-07 19:42:07 -0400669 struct rsdt_descriptor_rev1 *rsdt;
670 size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx;
671 rsdt = malloc_high(rsdt_len);
Kevin O'Connora9242a22009-10-07 19:42:07 -0400672 if (!rsdt) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500673 warn_noalloc();
Kevin O'Connora9242a22009-10-07 19:42:07 -0400674 return;
675 }
676 memset(rsdt, 0, rsdt_len);
677 memcpy(rsdt->table_offset_entry, tables, sizeof(u32) * tbl_idx);
Kevin O'Connora9242a22009-10-07 19:42:07 -0400678 build_header((void*)rsdt, RSDT_SIGNATURE, rsdt_len, 1);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400679
680 // Build rsdp pointer table
Kevin O'Connor8a161c92011-09-02 18:11:58 -0400681 struct rsdp_descriptor *rsdp = malloc_fseg(sizeof(*rsdp));
682 if (!rsdp) {
683 warn_noalloc();
684 return;
685 }
Kevin O'Connor2929c352009-07-25 13:48:27 -0400686 memset(rsdp, 0, sizeof(*rsdp));
Kevin O'Connor51684b72013-03-30 10:18:47 -0400687 rsdp->signature = cpu_to_le64(RSDP_SIGNATURE);
Kevin O'Connore52ad392013-02-20 23:48:22 -0500688 memcpy(rsdp->oem_id, BUILD_APPNAME6, 6);
Kevin O'Connor2929c352009-07-25 13:48:27 -0400689 rsdp->rsdt_physical_address = cpu_to_le32((u32)rsdt);
690 rsdp->checksum -= checksum(rsdp, 20);
691 RsdpAddr = rsdp;
692 dprintf(1, "ACPI tables: RSDP=%p RSDT=%p\n", rsdp, rsdt);
Kevin O'Connor276d4a92008-06-11 22:47:01 -0400693}
Kevin O'Connor9967ab72008-12-18 21:57:33 -0500694
David Woodhouse3219dfa2013-02-03 23:41:41 +0100695static struct fadt_descriptor_rev1 *
696find_fadt(void)
Kevin O'Connor9967ab72008-12-18 21:57:33 -0500697{
698 dprintf(4, "rsdp=%p\n", RsdpAddr);
699 if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE)
David Woodhouse3219dfa2013-02-03 23:41:41 +0100700 return NULL;
Kevin O'Connor9967ab72008-12-18 21:57:33 -0500701 struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address;
702 dprintf(4, "rsdt=%p\n", rsdt);
703 if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
David Woodhouse3219dfa2013-02-03 23:41:41 +0100704 return NULL;
Kevin O'Connor9967ab72008-12-18 21:57:33 -0500705 void *end = (void*)rsdt + rsdt->length;
706 int i;
707 for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
708 struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i];
709 if (!fadt || fadt->signature != FACP_SIGNATURE)
710 continue;
711 dprintf(4, "fadt=%p\n", fadt);
David Woodhouse3219dfa2013-02-03 23:41:41 +0100712 return fadt;
Kevin O'Connor9967ab72008-12-18 21:57:33 -0500713 }
David Woodhouse3219dfa2013-02-03 23:41:41 +0100714 dprintf(4, "no fadt found\n");
715 return NULL;
716}
717
718u32
719find_resume_vector(void)
720{
721 struct fadt_descriptor_rev1 *fadt = find_fadt();
722 if (!fadt)
723 return 0;
724 struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl;
725 dprintf(4, "facs=%p\n", facs);
726 if (! facs || facs->signature != FACS_SIGNATURE)
727 return 0;
728 // Found it.
729 dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector);
730 return facs->firmware_waking_vector;
731}
732
733void
David Woodhoused304fe42013-02-23 00:24:48 +0000734find_acpi_features(void)
David Woodhouse3219dfa2013-02-03 23:41:41 +0100735{
736 struct fadt_descriptor_rev1 *fadt = find_fadt();
737 if (!fadt)
738 return;
David Woodhoused304fe42013-02-23 00:24:48 +0000739 u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk);
Gerd Hoffmann5b631092013-07-25 09:47:18 +0200740 u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk);
David Woodhouse3219dfa2013-02-03 23:41:41 +0100741 dprintf(4, "pm_tmr_blk=%x\n", pm_tmr);
David Woodhoused304fe42013-02-23 00:24:48 +0000742 if (pm_tmr)
Kevin O'Connor118605f2013-07-20 11:06:51 -0400743 pmtimer_setup(pm_tmr);
Gerd Hoffmann5b631092013-07-25 09:47:18 +0200744 if (pm1a_cnt)
745 acpi_pm1a_cnt = pm1a_cnt;
David Woodhoused338eb92013-02-23 00:24:49 +0000746
747 // Theoretically we should check the 'reset_reg_sup' flag, but Windows
748 // doesn't and thus nobody seems to *set* it. If the table is large enough
749 // to include it, let the sanity checks in acpi_set_reset_reg() suffice.
750 if (fadt->length >= 129) {
751 void *p = fadt;
752 acpi_set_reset_reg(p + 116, *(u8 *)(p + 128));
753 }
754}
755
756static struct acpi_20_generic_address acpi_reset_reg;
757static u8 acpi_reset_val;
758
Kevin O'Connor5a7545c2013-09-14 22:54:44 -0400759#define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff)
760
David Woodhoused338eb92013-02-23 00:24:49 +0000761void
762acpi_reboot(void)
763{
764 // Check it passed the sanity checks in acpi_set_reset_reg() and was set
765 if (acpi_reset_reg.register_bit_width != 8)
766 return;
767
768 u64 addr = le64_to_cpu(acpi_reset_reg.address);
769
770 dprintf(1, "ACPI hard reset %d:%llx (%x)\n",
771 acpi_reset_reg.address_space_id, addr, acpi_reset_val);
772
773 switch (acpi_reset_reg.address_space_id) {
774 case 0: // System Memory
775 writeb((void *)(u32)addr, acpi_reset_val);
776 break;
777 case 1: // System I/O
778 outb(acpi_reset_val, addr);
779 break;
780 case 2: // PCI config space
781 pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val);
782 break;
783 }
784}
785
786void
787acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val)
788{
789 if (!reg || reg->address_space_id > 2 ||
790 reg->register_bit_width != 8 || reg->register_bit_offset)
791 return;
792
793 acpi_reset_reg = *reg;
794 acpi_reset_val = val;
Kevin O'Connor9967ab72008-12-18 21:57:33 -0500795}