Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 1 | // Support for generating ACPI tables (on emulators) |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 2 | // |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 3 | // Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 4 | // Copyright (C) 2006 Fabrice Bellard |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 7 | |
| 8 | #include "acpi.h" // struct rsdp_descriptor |
| 9 | #include "util.h" // memcpy |
Kevin O'Connor | b306459 | 2012-08-14 21:20:10 -0400 | [diff] [blame] | 10 | #include "byteorder.h" // cpu_to_le16 |
Kevin O'Connor | 0cd7005 | 2011-07-02 14:04:19 -0400 | [diff] [blame] | 11 | #include "pci.h" // pci_find_init_device |
Kevin O'Connor | 2ed2f58 | 2008-11-08 15:53:36 -0500 | [diff] [blame] | 12 | #include "pci_ids.h" // PCI_VENDOR_ID_INTEL |
| 13 | #include "pci_regs.h" // PCI_INTERRUPT_LINE |
Kevin O'Connor | 4bc4997 | 2012-05-13 22:58:08 -0400 | [diff] [blame] | 14 | #include "ioport.h" // inl |
Kevin O'Connor | f9e4e37 | 2013-02-09 19:45:45 -0500 | [diff] [blame] | 15 | #include "config.h" // CONFIG_* |
Kevin O'Connor | f85e4bc | 2013-02-19 01:33:45 -0500 | [diff] [blame] | 16 | #include "paravirt.h" // RamSize |
Kevin O'Connor | f9e4e37 | 2013-02-09 19:45:45 -0500 | [diff] [blame] | 17 | #include "dev-q35.h" |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 18 | |
| 19 | /****************************************************/ |
| 20 | /* ACPI tables init */ |
| 21 | |
| 22 | /* Table structure from Linux kernel (the ACPI tables are under the |
| 23 | BSD license) */ |
| 24 | |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 25 | struct acpi_table_header /* ACPI common table header */ |
| 26 | { |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 27 | ACPI_TABLE_HEADER_DEF |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 28 | } PACKED; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 29 | |
| 30 | /* |
| 31 | * ACPI 1.0 Root System Description Table (RSDT) |
| 32 | */ |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 33 | #define RSDT_SIGNATURE 0x54445352 // RSDT |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 34 | struct rsdt_descriptor_rev1 |
| 35 | { |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 36 | ACPI_TABLE_HEADER_DEF /* ACPI common table header */ |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 37 | u32 table_offset_entry[0]; /* Array of pointers to other */ |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 38 | /* ACPI tables */ |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 39 | } PACKED; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 40 | |
| 41 | /* |
| 42 | * ACPI 1.0 Firmware ACPI Control Structure (FACS) |
| 43 | */ |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 44 | #define FACS_SIGNATURE 0x53434146 // FACS |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 45 | struct facs_descriptor_rev1 |
| 46 | { |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 47 | u32 signature; /* ACPI Signature */ |
| 48 | u32 length; /* Length of structure, in bytes */ |
| 49 | u32 hardware_signature; /* Hardware configuration signature */ |
| 50 | u32 firmware_waking_vector; /* ACPI OS waking vector */ |
| 51 | u32 global_lock; /* Global Lock */ |
| 52 | u32 S4bios_f : 1; /* Indicates if S4BIOS support is present */ |
| 53 | u32 reserved1 : 31; /* Must be 0 */ |
| 54 | u8 resverved3 [40]; /* Reserved - must be zero */ |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 55 | } PACKED; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 56 | |
| 57 | |
| 58 | /* |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 59 | * Differentiated System Description Table (DSDT) |
| 60 | */ |
| 61 | #define DSDT_SIGNATURE 0x54445344 // DSDT |
| 62 | |
| 63 | /* |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 64 | * MADT values and structures |
| 65 | */ |
| 66 | |
| 67 | /* Values for MADT PCATCompat */ |
| 68 | |
| 69 | #define DUAL_PIC 0 |
| 70 | #define MULTIPLE_APIC 1 |
| 71 | |
| 72 | |
| 73 | /* Master MADT */ |
| 74 | |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 75 | #define APIC_SIGNATURE 0x43495041 // APIC |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 76 | struct multiple_apic_table |
| 77 | { |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 78 | ACPI_TABLE_HEADER_DEF /* ACPI common table header */ |
| 79 | u32 local_apic_address; /* Physical address of local APIC */ |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 80 | #if 0 |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 81 | u32 PCATcompat : 1; /* A one indicates system also has dual 8259s */ |
| 82 | u32 reserved1 : 31; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 83 | #else |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 84 | u32 flags; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 85 | #endif |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 86 | } PACKED; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 87 | |
| 88 | |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 89 | /* Values for Type in APIC sub-headers */ |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 90 | |
| 91 | #define APIC_PROCESSOR 0 |
| 92 | #define APIC_IO 1 |
| 93 | #define APIC_XRUPT_OVERRIDE 2 |
| 94 | #define APIC_NMI 3 |
| 95 | #define APIC_LOCAL_NMI 4 |
| 96 | #define APIC_ADDRESS_OVERRIDE 5 |
| 97 | #define APIC_IO_SAPIC 6 |
| 98 | #define APIC_LOCAL_SAPIC 7 |
| 99 | #define APIC_XRUPT_SOURCE 8 |
| 100 | #define APIC_RESERVED 9 /* 9 and greater are reserved */ |
| 101 | |
| 102 | /* |
| 103 | * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) |
| 104 | */ |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 105 | #define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\ |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 106 | u8 type; \ |
| 107 | u8 length; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 108 | |
| 109 | /* Sub-structures for MADT */ |
| 110 | |
| 111 | struct madt_processor_apic |
| 112 | { |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 113 | ACPI_SUB_HEADER_DEF |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 114 | u8 processor_id; /* ACPI processor id */ |
| 115 | u8 local_apic_id; /* Processor's local APIC id */ |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 116 | #if 0 |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 117 | u32 processor_enabled: 1; /* Processor is usable if set */ |
| 118 | u32 reserved2 : 31; /* Reserved, must be zero */ |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 119 | #else |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 120 | u32 flags; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 121 | #endif |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 122 | } PACKED; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 123 | |
| 124 | struct madt_io_apic |
| 125 | { |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 126 | ACPI_SUB_HEADER_DEF |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 127 | u8 io_apic_id; /* I/O APIC ID */ |
| 128 | u8 reserved; /* Reserved - must be zero */ |
| 129 | u32 address; /* APIC physical address */ |
| 130 | u32 interrupt; /* Global system interrupt where INTI |
| 131 | * lines start */ |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 132 | } PACKED; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 133 | |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 134 | struct madt_intsrcovr { |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 135 | ACPI_SUB_HEADER_DEF |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 136 | u8 bus; |
| 137 | u8 source; |
| 138 | u32 gsi; |
| 139 | u16 flags; |
| 140 | } PACKED; |
| 141 | |
Kenji Kaneshige | 7098638 | 2011-10-10 14:06:17 +0800 | [diff] [blame] | 142 | struct madt_local_nmi { |
| 143 | ACPI_SUB_HEADER_DEF |
| 144 | u8 processor_id; /* ACPI processor id */ |
| 145 | u16 flags; /* MPS INTI flags */ |
| 146 | u8 lint; /* Local APIC LINT# */ |
| 147 | } PACKED; |
| 148 | |
| 149 | |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 150 | /* |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 151 | * HPET Description Table |
| 152 | */ |
| 153 | struct acpi_20_hpet { |
| 154 | ACPI_TABLE_HEADER_DEF /* ACPI common table header */ |
| 155 | u32 timer_block_id; |
| 156 | struct acpi_20_generic_address addr; |
| 157 | u8 hpet_number; |
| 158 | u16 min_tick; |
| 159 | u8 page_protect; |
| 160 | } PACKED; |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 161 | |
Jan Kiszka | b8a90f5 | 2011-08-29 17:50:10 +0200 | [diff] [blame] | 162 | #define HPET_ID 0x000 |
| 163 | #define HPET_PERIOD 0x004 |
| 164 | |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 165 | /* |
| 166 | * SRAT (NUMA topology description) table |
| 167 | */ |
| 168 | |
| 169 | #define SRAT_PROCESSOR 0 |
| 170 | #define SRAT_MEMORY 1 |
| 171 | |
| 172 | struct system_resource_affinity_table |
| 173 | { |
| 174 | ACPI_TABLE_HEADER_DEF |
| 175 | u32 reserved1; |
| 176 | u32 reserved2[2]; |
| 177 | } PACKED; |
| 178 | |
| 179 | struct srat_processor_affinity |
| 180 | { |
| 181 | ACPI_SUB_HEADER_DEF |
| 182 | u8 proximity_lo; |
| 183 | u8 local_apic_id; |
| 184 | u32 flags; |
| 185 | u8 local_sapic_eid; |
| 186 | u8 proximity_hi[3]; |
| 187 | u32 reserved; |
| 188 | } PACKED; |
| 189 | |
| 190 | struct srat_memory_affinity |
| 191 | { |
| 192 | ACPI_SUB_HEADER_DEF |
| 193 | u8 proximity[4]; |
| 194 | u16 reserved1; |
| 195 | u32 base_addr_low,base_addr_high; |
| 196 | u32 length_low,length_high; |
| 197 | u32 reserved2; |
| 198 | u32 flags; |
| 199 | u32 reserved3[2]; |
| 200 | } PACKED; |
| 201 | |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 202 | #include "acpi-dsdt.hex" |
| 203 | |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 204 | static void |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 205 | build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 206 | { |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 207 | h->signature = sig; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 208 | h->length = cpu_to_le32(len); |
| 209 | h->revision = rev; |
Kevin O'Connor | e52ad39 | 2013-02-20 23:48:22 -0500 | [diff] [blame] | 210 | memcpy(h->oem_id, BUILD_APPNAME6, 6); |
| 211 | memcpy(h->oem_table_id, BUILD_APPNAME4, 4); |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 212 | memcpy(h->oem_table_id + 4, (void*)&sig, 4); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 213 | h->oem_revision = cpu_to_le32(1); |
Kevin O'Connor | e52ad39 | 2013-02-20 23:48:22 -0500 | [diff] [blame] | 214 | memcpy(h->asl_compiler_id, BUILD_APPNAME4, 4); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 215 | h->asl_compiler_revision = cpu_to_le32(1); |
Kevin O'Connor | 523e5a9 | 2009-07-04 13:46:33 -0400 | [diff] [blame] | 216 | h->checksum -= checksum(h, len); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 217 | } |
| 218 | |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 219 | #define PIIX4_ACPI_ENABLE 0xf1 |
| 220 | #define PIIX4_ACPI_DISABLE 0xf0 |
| 221 | #define PIIX4_GPE0_BLK 0xafe0 |
| 222 | #define PIIX4_GPE0_BLK_LEN 4 |
| 223 | |
Isaku Yamahata | 229e8e2 | 2012-11-28 10:17:31 +0100 | [diff] [blame] | 224 | #define PIIX4_PM_INTRRUPT 9 // irq 9 |
| 225 | |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 226 | static void piix4_fadt_setup(struct pci_device *pci, void *arg) |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 227 | { |
| 228 | struct fadt_descriptor_rev1 *fadt = arg; |
Isaku Yamahata | 229e8e2 | 2012-11-28 10:17:31 +0100 | [diff] [blame] | 229 | |
| 230 | fadt->model = 1; |
| 231 | fadt->reserved1 = 0; |
| 232 | fadt->sci_int = cpu_to_le16(PIIX4_PM_INTRRUPT); |
| 233 | fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 234 | fadt->acpi_enable = PIIX4_ACPI_ENABLE; |
| 235 | fadt->acpi_disable = PIIX4_ACPI_DISABLE; |
Isaku Yamahata | 229e8e2 | 2012-11-28 10:17:31 +0100 | [diff] [blame] | 236 | fadt->pm1a_evt_blk = cpu_to_le32(PORT_ACPI_PM_BASE); |
| 237 | fadt->pm1a_cnt_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x04); |
| 238 | fadt->pm_tmr_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x08); |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 239 | fadt->gpe0_blk = cpu_to_le32(PIIX4_GPE0_BLK); |
Isaku Yamahata | 229e8e2 | 2012-11-28 10:17:31 +0100 | [diff] [blame] | 240 | fadt->pm1_evt_len = 4; |
| 241 | fadt->pm1_cnt_len = 2; |
| 242 | fadt->pm_tmr_len = 4; |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 243 | fadt->gpe0_blk_len = PIIX4_GPE0_BLK_LEN; |
Isaku Yamahata | 229e8e2 | 2012-11-28 10:17:31 +0100 | [diff] [blame] | 244 | fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported |
| 245 | fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported |
| 246 | /* WBINVD + PROC_C1 + SLP_BUTTON + RTC_S4 + USE_PLATFORM_CLOCK */ |
| 247 | fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 7) | |
| 248 | (1 << 15)); |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 249 | } |
| 250 | |
Isaku Yamahata | 72a590e | 2012-11-28 10:17:33 +0100 | [diff] [blame] | 251 | /* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 252 | void ich9_lpc_fadt_setup(struct pci_device *dev, void *arg) |
Isaku Yamahata | 72a590e | 2012-11-28 10:17:33 +0100 | [diff] [blame] | 253 | { |
| 254 | struct fadt_descriptor_rev1 *fadt = arg; |
| 255 | |
| 256 | fadt->model = 1; |
| 257 | fadt->reserved1 = 0; |
| 258 | fadt->sci_int = cpu_to_le16(9); |
| 259 | fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); |
| 260 | fadt->acpi_enable = ICH9_ACPI_ENABLE; |
| 261 | fadt->acpi_disable = ICH9_ACPI_DISABLE; |
| 262 | fadt->pm1a_evt_blk = cpu_to_le32(PORT_ACPI_PM_BASE); |
| 263 | fadt->pm1a_cnt_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x04); |
| 264 | fadt->pm_tmr_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x08); |
| 265 | fadt->gpe0_blk = cpu_to_le32(PORT_ACPI_PM_BASE + ICH9_PMIO_GPE0_STS); |
| 266 | fadt->pm1_evt_len = 4; |
| 267 | fadt->pm1_cnt_len = 2; |
| 268 | fadt->pm_tmr_len = 4; |
| 269 | fadt->gpe0_blk_len = ICH9_PMIO_GPE0_BLK_LEN; |
| 270 | fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported |
| 271 | fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported |
| 272 | /* WBINVD + PROC_C1 + SLP_BUTTON + FIX_RTC + RTC_S4 */ |
| 273 | fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 6) | |
| 274 | (1 << 7)); |
| 275 | } |
| 276 | |
Isaku Yamahata | fe54a53 | 2010-07-20 16:37:18 +0900 | [diff] [blame] | 277 | static const struct pci_device_id fadt_init_tbl[] = { |
| 278 | /* PIIX4 Power Management device (for ACPI) */ |
| 279 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 280 | piix4_fadt_setup), |
Isaku Yamahata | 72a590e | 2012-11-28 10:17:33 +0100 | [diff] [blame] | 281 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 282 | ich9_lpc_fadt_setup), |
Isaku Yamahata | fe54a53 | 2010-07-20 16:37:18 +0900 | [diff] [blame] | 283 | PCI_DEVICE_END |
| 284 | }; |
| 285 | |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 286 | static void fill_dsdt(struct fadt_descriptor_rev1 *fadt, void *dsdt) |
| 287 | { |
| 288 | if (fadt->dsdt) { |
| 289 | free((void *)le32_to_cpu(fadt->dsdt)); |
| 290 | } |
| 291 | fadt->dsdt = cpu_to_le32((u32)dsdt); |
| 292 | fadt->checksum -= checksum(fadt, sizeof(*fadt)); |
| 293 | dprintf(1, "ACPI DSDT=%p\n", dsdt); |
| 294 | } |
| 295 | |
Kevin O'Connor | 278b19f | 2011-06-21 22:41:15 -0400 | [diff] [blame] | 296 | static void * |
| 297 | build_fadt(struct pci_device *pci) |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 298 | { |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 299 | struct fadt_descriptor_rev1 *fadt = malloc_high(sizeof(*fadt)); |
Kevin O'Connor | 415d429 | 2009-08-30 19:19:31 -0400 | [diff] [blame] | 300 | struct facs_descriptor_rev1 *facs = memalign_high(64, sizeof(*facs)); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 301 | |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 302 | if (!fadt || !facs) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 303 | warn_noalloc(); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 304 | return NULL; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 305 | } |
| 306 | |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 307 | /* FACS */ |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 308 | memset(facs, 0, sizeof(*facs)); |
| 309 | facs->signature = FACS_SIGNATURE; |
| 310 | facs->length = cpu_to_le32(sizeof(*facs)); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 311 | |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 312 | /* FADT */ |
| 313 | memset(fadt, 0, sizeof(*fadt)); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 314 | fadt->firmware_ctrl = cpu_to_le32((u32)facs); |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 315 | fadt->dsdt = 0; /* dsdt will be filled later in acpi_setup() |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 316 | by fill_dsdt() */ |
Kevin O'Connor | 278b19f | 2011-06-21 22:41:15 -0400 | [diff] [blame] | 317 | pci_init_device(fadt_init_tbl, pci, fadt); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 318 | |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 319 | build_header((void*)fadt, FACP_SIGNATURE, sizeof(*fadt), 1); |
| 320 | |
| 321 | return fadt; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 322 | } |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 323 | |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 324 | static void* |
| 325 | build_madt(void) |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 326 | { |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 327 | int madt_size = (sizeof(struct multiple_apic_table) |
Kevin O'Connor | a26df9b | 2009-10-09 09:42:11 -0400 | [diff] [blame] | 328 | + sizeof(struct madt_processor_apic) * MaxCountCPUs |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 329 | + sizeof(struct madt_io_apic) |
Kenji Kaneshige | 7098638 | 2011-10-10 14:06:17 +0800 | [diff] [blame] | 330 | + sizeof(struct madt_intsrcovr) * 16 |
| 331 | + sizeof(struct madt_local_nmi)); |
| 332 | |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 333 | struct multiple_apic_table *madt = malloc_high(madt_size); |
| 334 | if (!madt) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 335 | warn_noalloc(); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 336 | return NULL; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 337 | } |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 338 | memset(madt, 0, madt_size); |
| 339 | madt->local_apic_address = cpu_to_le32(BUILD_APIC_ADDR); |
| 340 | madt->flags = cpu_to_le32(1); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 341 | struct madt_processor_apic *apic = (void*)&madt[1]; |
| 342 | int i; |
Kevin O'Connor | a26df9b | 2009-10-09 09:42:11 -0400 | [diff] [blame] | 343 | for (i=0; i<MaxCountCPUs; i++) { |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 344 | apic->type = APIC_PROCESSOR; |
| 345 | apic->length = sizeof(*apic); |
| 346 | apic->processor_id = i; |
| 347 | apic->local_apic_id = i; |
Eduardo Habkost | 008c1fc | 2012-07-25 15:45:30 -0300 | [diff] [blame] | 348 | if (apic_id_is_present(apic->local_apic_id)) |
Kevin O'Connor | a26df9b | 2009-10-09 09:42:11 -0400 | [diff] [blame] | 349 | apic->flags = cpu_to_le32(1); |
| 350 | else |
| 351 | apic->flags = cpu_to_le32(0); |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 352 | apic++; |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 353 | } |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 354 | struct madt_io_apic *io_apic = (void*)apic; |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 355 | io_apic->type = APIC_IO; |
| 356 | io_apic->length = sizeof(*io_apic); |
Eduardo Habkost | e39b938 | 2012-07-25 15:45:29 -0300 | [diff] [blame] | 357 | io_apic->io_apic_id = BUILD_IOAPIC_ID; |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 358 | io_apic->address = cpu_to_le32(BUILD_IOAPIC_ADDR); |
| 359 | io_apic->interrupt = cpu_to_le32(0); |
| 360 | |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 361 | struct madt_intsrcovr *intsrcovr = (void*)&io_apic[1]; |
Kevin O'Connor | 56c5089 | 2013-02-09 19:25:51 -0500 | [diff] [blame] | 362 | if (romfile_loadint("etc/irq0-override", 0)) { |
Kevin O'Connor | b64db30 | 2009-07-29 19:20:03 -0400 | [diff] [blame] | 363 | memset(intsrcovr, 0, sizeof(*intsrcovr)); |
| 364 | intsrcovr->type = APIC_XRUPT_OVERRIDE; |
| 365 | intsrcovr->length = sizeof(*intsrcovr); |
| 366 | intsrcovr->source = 0; |
| 367 | intsrcovr->gsi = 2; |
| 368 | intsrcovr->flags = 0; /* conforms to bus specifications */ |
| 369 | intsrcovr++; |
| 370 | } |
| 371 | for (i = 1; i < 16; i++) { |
Kevin O'Connor | d838815 | 2013-03-18 20:26:54 -0400 | [diff] [blame] | 372 | if (!(BUILD_PCI_IRQS & (1 << i))) |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 373 | /* No need for a INT source override structure. */ |
| 374 | continue; |
Kevin O'Connor | d10e444 | 2009-03-01 12:41:20 -0500 | [diff] [blame] | 375 | memset(intsrcovr, 0, sizeof(*intsrcovr)); |
| 376 | intsrcovr->type = APIC_XRUPT_OVERRIDE; |
| 377 | intsrcovr->length = sizeof(*intsrcovr); |
| 378 | intsrcovr->source = i; |
| 379 | intsrcovr->gsi = i; |
| 380 | intsrcovr->flags = 0xd; /* active high, level triggered */ |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 381 | intsrcovr++; |
Kevin O'Connor | 7061eb6 | 2009-01-04 21:48:22 -0500 | [diff] [blame] | 382 | } |
| 383 | |
Kenji Kaneshige | 7098638 | 2011-10-10 14:06:17 +0800 | [diff] [blame] | 384 | struct madt_local_nmi *local_nmi = (void*)intsrcovr; |
| 385 | local_nmi->type = APIC_LOCAL_NMI; |
| 386 | local_nmi->length = sizeof(*local_nmi); |
| 387 | local_nmi->processor_id = 0xff; /* all processors */ |
| 388 | local_nmi->flags = 0; |
| 389 | local_nmi->lint = 1; /* LINT1 */ |
| 390 | local_nmi++; |
| 391 | |
| 392 | build_header((void*)madt, APIC_SIGNATURE, (void*)local_nmi - (void*)madt, 1); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 393 | return madt; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 394 | } |
| 395 | |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 396 | // Encode a hex value |
| 397 | static inline char getHex(u32 val) { |
| 398 | val &= 0x0f; |
| 399 | return (val <= 9) ? ('0' + val) : ('A' + val - 10); |
| 400 | } |
| 401 | |
| 402 | // Encode a length in an SSDT. |
| 403 | static u8 * |
| 404 | encodeLen(u8 *ssdt_ptr, int length, int bytes) |
| 405 | { |
| 406 | switch (bytes) { |
| 407 | default: |
| 408 | case 4: ssdt_ptr[3] = ((length >> 20) & 0xff); |
| 409 | case 3: ssdt_ptr[2] = ((length >> 12) & 0xff); |
| 410 | case 2: ssdt_ptr[1] = ((length >> 4) & 0xff); |
| 411 | ssdt_ptr[0] = (((bytes-1) & 0x3) << 6) | (length & 0x0f); |
| 412 | break; |
| 413 | case 1: ssdt_ptr[0] = length & 0x3f; |
| 414 | } |
| 415 | return ssdt_ptr + bytes; |
| 416 | } |
| 417 | |
Michael S. Tsirkin | c736f0a | 2011-10-04 15:26:19 +0200 | [diff] [blame] | 418 | #include "ssdt-proc.hex" |
Michael S. Tsirkin | c736f0a | 2011-10-04 15:26:19 +0200 | [diff] [blame] | 419 | |
| 420 | /* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ |
Paolo Bonzini | c8ed45e | 2012-08-02 15:07:22 +0200 | [diff] [blame] | 421 | #define PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) |
| 422 | #define PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) |
| 423 | #define PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) |
| 424 | #define PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) |
| 425 | #define PROC_AML (ssdp_proc_aml + *ssdt_proc_start) |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 426 | |
Paolo Bonzini | 71ed8e3 | 2012-08-02 15:07:26 +0200 | [diff] [blame] | 427 | /* 0x5B 0x82 DeviceOp PkgLength NameString */ |
| 428 | #define PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) |
| 429 | #define PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) |
| 430 | #define PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) |
| 431 | #define PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) |
| 432 | #define PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) |
| 433 | #define PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 434 | #define PCI_SLOTS 32 |
| 435 | |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 436 | #define SSDT_SIGNATURE 0x54445353 // SSDT |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 437 | #define SSDT_HEADER_LENGTH 36 |
| 438 | |
Kevin O'Connor | 3f8d735 | 2013-02-27 20:46:40 -0500 | [diff] [blame] | 439 | #include "ssdt-misc.hex" |
Paolo Bonzini | 71ed8e3 | 2012-08-02 15:07:26 +0200 | [diff] [blame] | 440 | #include "ssdt-pcihp.hex" |
| 441 | |
| 442 | #define PCI_RMV_BASE 0xae0c |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 443 | |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 444 | static u8* |
| 445 | build_notify(u8 *ssdt_ptr, const char *name, int skip, int count, |
| 446 | const char *target, int ofs) |
| 447 | { |
| 448 | count -= skip; |
| 449 | |
| 450 | *(ssdt_ptr++) = 0x14; // MethodOp |
| 451 | ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*count), 2); |
| 452 | memcpy(ssdt_ptr, name, 4); |
| 453 | ssdt_ptr += 4; |
| 454 | *(ssdt_ptr++) = 0x02; // MethodOp |
| 455 | |
| 456 | int i; |
| 457 | for (i = skip; count-- > 0; i++) { |
| 458 | *(ssdt_ptr++) = 0xA0; // IfOp |
| 459 | ssdt_ptr = encodeLen(ssdt_ptr, 11, 1); |
| 460 | *(ssdt_ptr++) = 0x93; // LEqualOp |
| 461 | *(ssdt_ptr++) = 0x68; // Arg0Op |
| 462 | *(ssdt_ptr++) = 0x0A; // BytePrefix |
| 463 | *(ssdt_ptr++) = i; |
| 464 | *(ssdt_ptr++) = 0x86; // NotifyOp |
| 465 | memcpy(ssdt_ptr, target, 4); |
| 466 | ssdt_ptr[ofs] = getHex(i >> 4); |
| 467 | ssdt_ptr[ofs + 1] = getHex(i); |
| 468 | ssdt_ptr += 4; |
| 469 | *(ssdt_ptr++) = 0x69; // Arg1Op |
| 470 | } |
| 471 | return ssdt_ptr; |
| 472 | } |
| 473 | |
Paolo Bonzini | 71ed8e3 | 2012-08-02 15:07:26 +0200 | [diff] [blame] | 474 | static void patch_pcihp(int slot, u8 *ssdt_ptr, u32 eject) |
| 475 | { |
| 476 | ssdt_ptr[PCIHP_OFFSET_HEX] = getHex(slot >> 4); |
| 477 | ssdt_ptr[PCIHP_OFFSET_HEX+1] = getHex(slot); |
| 478 | ssdt_ptr[PCIHP_OFFSET_ID] = slot; |
| 479 | ssdt_ptr[PCIHP_OFFSET_ADR + 2] = slot; |
| 480 | |
| 481 | /* Runtime patching of EJ0: to disable hotplug for a slot, |
| 482 | * replace the method name: _EJ0 by EJ0_. */ |
| 483 | /* Sanity check */ |
| 484 | if (memcmp(ssdt_ptr + PCIHP_OFFSET_EJ0, "_EJ0", 4)) { |
| 485 | warn_internalerror(); |
| 486 | } |
| 487 | if (!eject) { |
| 488 | memcpy(ssdt_ptr + PCIHP_OFFSET_EJ0, "EJ0_", 4); |
| 489 | } |
| 490 | } |
| 491 | |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 492 | static void* |
| 493 | build_ssdt(void) |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 494 | { |
Kevin O'Connor | a26df9b | 2009-10-09 09:42:11 -0400 | [diff] [blame] | 495 | int acpi_cpus = MaxCountCPUs > 0xff ? 0xff : MaxCountCPUs; |
Kevin O'Connor | 3f8d735 | 2013-02-27 20:46:40 -0500 | [diff] [blame] | 496 | int length = (sizeof(ssdp_misc_aml) // _S3_ / _S4_ / _S5_ |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 497 | + (1+3+4) // Scope(_SB_) |
Paolo Bonzini | c8ed45e | 2012-08-02 15:07:22 +0200 | [diff] [blame] | 498 | + (acpi_cpus * PROC_SIZEOF) // procs |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 499 | + (1+2+5+(12*acpi_cpus)) // NTFY |
| 500 | + (6+2+1+(1*acpi_cpus)) // CPON |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 501 | + (1+3+4) // Scope(PCI0) |
Paolo Bonzini | 71ed8e3 | 2012-08-02 15:07:26 +0200 | [diff] [blame] | 502 | + ((PCI_SLOTS - 1) * PCIHP_SIZEOF) // slots |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 503 | + (1+2+5+(12*(PCI_SLOTS - 1)))); // PCNT |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 504 | u8 *ssdt = malloc_high(length); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 505 | if (! ssdt) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 506 | warn_noalloc(); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 507 | return NULL; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 508 | } |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 509 | u8 *ssdt_ptr = ssdt; |
| 510 | |
| 511 | // Copy header and encode fwcfg values in the S3_ / S4_ / S5_ packages |
| 512 | int sys_state_size; |
| 513 | char *sys_states = romfile_loadfile("etc/system-states", &sys_state_size); |
| 514 | if (!sys_states || sys_state_size != 6) |
| 515 | sys_states = (char[]){128, 0, 0, 129, 128, 128}; |
| 516 | |
Kevin O'Connor | 3f8d735 | 2013-02-27 20:46:40 -0500 | [diff] [blame] | 517 | memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 518 | if (!(sys_states[3] & 128)) |
| 519 | ssdt_ptr[acpi_s3_name[0]] = 'X'; |
| 520 | if (!(sys_states[4] & 128)) |
| 521 | ssdt_ptr[acpi_s4_name[0]] = 'X'; |
| 522 | else |
| 523 | ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127; |
Kevin O'Connor | 76e5802 | 2013-03-06 21:50:09 -0500 | [diff] [blame] | 524 | |
| 525 | // store pci io windows |
| 526 | *(u32*)&ssdt_ptr[acpi_pci32_start[0]] = pcimem_start; |
| 527 | *(u32*)&ssdt_ptr[acpi_pci32_end[0]] = pcimem_end - 1; |
| 528 | if (pcimem64_start) { |
| 529 | ssdt_ptr[acpi_pci64_valid[0]] = 1; |
| 530 | *(u64*)&ssdt_ptr[acpi_pci64_start[0]] = pcimem64_start; |
| 531 | *(u64*)&ssdt_ptr[acpi_pci64_end[0]] = pcimem64_end - 1; |
| 532 | *(u64*)&ssdt_ptr[acpi_pci64_length[0]] = pcimem64_end - pcimem64_start; |
| 533 | } else { |
| 534 | ssdt_ptr[acpi_pci64_valid[0]] = 0; |
| 535 | } |
| 536 | |
Kevin O'Connor | 3f8d735 | 2013-02-27 20:46:40 -0500 | [diff] [blame] | 537 | ssdt_ptr += sizeof(ssdp_misc_aml); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 538 | |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 539 | // build Scope(_SB_) header |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 540 | *(ssdt_ptr++) = 0x10; // ScopeOp |
Paolo Bonzini | cf5bef4 | 2012-08-02 15:07:21 +0200 | [diff] [blame] | 541 | ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 542 | *(ssdt_ptr++) = '_'; |
| 543 | *(ssdt_ptr++) = 'S'; |
| 544 | *(ssdt_ptr++) = 'B'; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 545 | *(ssdt_ptr++) = '_'; |
| 546 | |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 547 | // build Processor object for each processor |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 548 | int i; |
| 549 | for (i=0; i<acpi_cpus; i++) { |
Paolo Bonzini | c8ed45e | 2012-08-02 15:07:22 +0200 | [diff] [blame] | 550 | memcpy(ssdt_ptr, PROC_AML, PROC_SIZEOF); |
| 551 | ssdt_ptr[PROC_OFFSET_CPUHEX] = getHex(i >> 4); |
| 552 | ssdt_ptr[PROC_OFFSET_CPUHEX+1] = getHex(i); |
| 553 | ssdt_ptr[PROC_OFFSET_CPUID1] = i; |
| 554 | ssdt_ptr[PROC_OFFSET_CPUID2] = i; |
| 555 | ssdt_ptr += PROC_SIZEOF; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 556 | } |
| 557 | |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 558 | // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}" |
Eduardo Habkost | 008c1fc | 2012-07-25 15:45:30 -0300 | [diff] [blame] | 559 | // Arg0 = Processor ID = APIC ID |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 560 | ssdt_ptr = build_notify(ssdt_ptr, "NTFY", 0, acpi_cpus, "CP00", 2); |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 561 | |
| 562 | // build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" |
| 563 | *(ssdt_ptr++) = 0x08; // NameOp |
| 564 | *(ssdt_ptr++) = 'C'; |
| 565 | *(ssdt_ptr++) = 'P'; |
| 566 | *(ssdt_ptr++) = 'O'; |
| 567 | *(ssdt_ptr++) = 'N'; |
| 568 | *(ssdt_ptr++) = 0x12; // PackageOp |
| 569 | ssdt_ptr = encodeLen(ssdt_ptr, 2+1+(1*acpi_cpus), 2); |
| 570 | *(ssdt_ptr++) = acpi_cpus; |
| 571 | for (i=0; i<acpi_cpus; i++) |
Eduardo Habkost | 008c1fc | 2012-07-25 15:45:30 -0300 | [diff] [blame] | 572 | *(ssdt_ptr++) = (apic_id_is_present(i)) ? 0x01 : 0x00; |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 573 | |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 574 | // build Scope(PCI0) opcode |
| 575 | *(ssdt_ptr++) = 0x10; // ScopeOp |
| 576 | ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); |
| 577 | *(ssdt_ptr++) = 'P'; |
| 578 | *(ssdt_ptr++) = 'C'; |
| 579 | *(ssdt_ptr++) = 'I'; |
| 580 | *(ssdt_ptr++) = '0'; |
| 581 | |
Paolo Bonzini | 71ed8e3 | 2012-08-02 15:07:26 +0200 | [diff] [blame] | 582 | // build Device object for each slot |
| 583 | u32 rmvc_pcrm = inl(PCI_RMV_BASE); |
| 584 | for (i=1; i<PCI_SLOTS; i++) { |
| 585 | u32 eject = rmvc_pcrm & (0x1 << i); |
| 586 | memcpy(ssdt_ptr, PCIHP_AML, PCIHP_SIZEOF); |
| 587 | patch_pcihp(i, ssdt_ptr, eject != 0); |
| 588 | ssdt_ptr += PCIHP_SIZEOF; |
| 589 | } |
| 590 | |
Paolo Bonzini | 7dceba3 | 2012-08-02 15:07:25 +0200 | [diff] [blame] | 591 | ssdt_ptr = build_notify(ssdt_ptr, "PCNT", 1, PCI_SLOTS, "S00_", 1); |
| 592 | |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 593 | build_header((void*)ssdt, SSDT_SIGNATURE, ssdt_ptr - ssdt, 1); |
| 594 | |
Kevin O'Connor | e2074bf | 2010-08-03 21:30:03 -0400 | [diff] [blame] | 595 | //hexdump(ssdt, ssdt_ptr - ssdt); |
| 596 | |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 597 | return ssdt; |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 598 | } |
| 599 | |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 600 | #define HPET_SIGNATURE 0x54455048 // HPET |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 601 | static void* |
| 602 | build_hpet(void) |
| 603 | { |
Jan Kiszka | b8a90f5 | 2011-08-29 17:50:10 +0200 | [diff] [blame] | 604 | struct acpi_20_hpet *hpet; |
Kevin O'Connor | 7859eda | 2011-09-20 19:40:28 -0400 | [diff] [blame] | 605 | const void *hpet_base = (void *)BUILD_HPET_ADDRESS; |
Jan Kiszka | b8a90f5 | 2011-08-29 17:50:10 +0200 | [diff] [blame] | 606 | u32 hpet_vendor = readl(hpet_base + HPET_ID) >> 16; |
| 607 | u32 hpet_period = readl(hpet_base + HPET_PERIOD); |
| 608 | |
| 609 | if (hpet_vendor == 0 || hpet_vendor == 0xffff || |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 610 | hpet_period == 0 || hpet_period > 100000000) |
Jan Kiszka | b8a90f5 | 2011-08-29 17:50:10 +0200 | [diff] [blame] | 611 | return NULL; |
| 612 | |
| 613 | hpet = malloc_high(sizeof(*hpet)); |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 614 | if (!hpet) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 615 | warn_noalloc(); |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 616 | return NULL; |
| 617 | } |
| 618 | |
| 619 | memset(hpet, 0, sizeof(*hpet)); |
| 620 | /* Note timer_block_id value must be kept in sync with value advertised by |
| 621 | * emulated hpet |
| 622 | */ |
| 623 | hpet->timer_block_id = cpu_to_le32(0x8086a201); |
Kevin O'Connor | 7859eda | 2011-09-20 19:40:28 -0400 | [diff] [blame] | 624 | hpet->addr.address = cpu_to_le32(BUILD_HPET_ADDRESS); |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 625 | build_header((void*)hpet, HPET_SIGNATURE, sizeof(*hpet), 1); |
| 626 | |
| 627 | return hpet; |
| 628 | } |
| 629 | |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 630 | static void |
| 631 | acpi_build_srat_memory(struct srat_memory_affinity *numamem, |
| 632 | u64 base, u64 len, int node, int enabled) |
| 633 | { |
| 634 | numamem->type = SRAT_MEMORY; |
| 635 | numamem->length = sizeof(*numamem); |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 636 | memset(numamem->proximity, 0 ,4); |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 637 | numamem->proximity[0] = node; |
| 638 | numamem->flags = cpu_to_le32(!!enabled); |
| 639 | numamem->base_addr_low = base & 0xFFFFFFFF; |
| 640 | numamem->base_addr_high = base >> 32; |
| 641 | numamem->length_low = len & 0xFFFFFFFF; |
| 642 | numamem->length_high = len >> 32; |
| 643 | } |
| 644 | |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 645 | #define SRAT_SIGNATURE 0x54415253 // SRAT |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 646 | static void * |
| 647 | build_srat(void) |
| 648 | { |
Kevin O'Connor | fb76cff | 2013-03-23 11:38:45 -0400 | [diff] [blame] | 649 | int numadatasize, numacpusize; |
| 650 | u64 *numadata = romfile_loadfile("etc/numa-nodes", &numadatasize); |
| 651 | u64 *numacpumap = romfile_loadfile("etc/numa-cpu-map", &numacpusize); |
| 652 | if (!numadata || !numacpumap) |
| 653 | goto fail; |
| 654 | int max_cpu = numacpusize / sizeof(u64); |
| 655 | int nb_numa_nodes = numadatasize / sizeof(u64); |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 656 | |
| 657 | struct system_resource_affinity_table *srat; |
| 658 | int srat_size = sizeof(*srat) + |
Kevin O'Connor | f9e4e37 | 2013-02-09 19:45:45 -0500 | [diff] [blame] | 659 | sizeof(struct srat_processor_affinity) * max_cpu + |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 660 | sizeof(struct srat_memory_affinity) * (nb_numa_nodes + 2); |
| 661 | |
| 662 | srat = malloc_high(srat_size); |
| 663 | if (!srat) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 664 | warn_noalloc(); |
Kevin O'Connor | fb76cff | 2013-03-23 11:38:45 -0400 | [diff] [blame] | 665 | goto fail; |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 666 | } |
| 667 | |
| 668 | memset(srat, 0, srat_size); |
| 669 | srat->reserved1=1; |
| 670 | struct srat_processor_affinity *core = (void*)(srat + 1); |
| 671 | int i; |
| 672 | u64 curnode; |
| 673 | |
Kevin O'Connor | f9e4e37 | 2013-02-09 19:45:45 -0500 | [diff] [blame] | 674 | for (i = 0; i < max_cpu; ++i) { |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 675 | core->type = SRAT_PROCESSOR; |
| 676 | core->length = sizeof(*core); |
| 677 | core->local_apic_id = i; |
Kevin O'Connor | fb76cff | 2013-03-23 11:38:45 -0400 | [diff] [blame] | 678 | curnode = *numacpumap++; |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 679 | core->proximity_lo = curnode; |
| 680 | memset(core->proximity_hi, 0, 3); |
| 681 | core->local_sapic_eid = 0; |
Eduardo Habkost | 008c1fc | 2012-07-25 15:45:30 -0300 | [diff] [blame] | 682 | if (apic_id_is_present(i)) |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 683 | core->flags = cpu_to_le32(1); |
| 684 | else |
Eduardo Habkost | 008c1fc | 2012-07-25 15:45:30 -0300 | [diff] [blame] | 685 | core->flags = cpu_to_le32(0); |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 686 | core++; |
| 687 | } |
| 688 | |
| 689 | |
| 690 | /* the memory map is a bit tricky, it contains at least one hole |
| 691 | * from 640k-1M and possibly another one from 3.5G-4G. |
| 692 | */ |
| 693 | struct srat_memory_affinity *numamem = (void*)core; |
| 694 | int slots = 0; |
| 695 | u64 mem_len, mem_base, next_base = 0; |
| 696 | |
| 697 | acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); |
| 698 | next_base = 1024 * 1024; |
| 699 | numamem++; |
| 700 | slots++; |
| 701 | for (i = 1; i < nb_numa_nodes + 1; ++i) { |
| 702 | mem_base = next_base; |
| 703 | mem_len = *numadata++; |
| 704 | if (i == 1) |
| 705 | mem_len -= 1024 * 1024; |
| 706 | next_base = mem_base + mem_len; |
| 707 | |
| 708 | /* Cut out the PCI hole */ |
| 709 | if (mem_base <= RamSize && next_base > RamSize) { |
| 710 | mem_len -= next_base - RamSize; |
| 711 | if (mem_len > 0) { |
| 712 | acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); |
| 713 | numamem++; |
| 714 | slots++; |
| 715 | } |
| 716 | mem_base = 1ULL << 32; |
| 717 | mem_len = next_base - RamSize; |
| 718 | next_base += (1ULL << 32) - RamSize; |
| 719 | } |
| 720 | acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); |
| 721 | numamem++; |
| 722 | slots++; |
| 723 | } |
| 724 | for (; slots < nb_numa_nodes + 2; slots++) { |
| 725 | acpi_build_srat_memory(numamem, 0, 0, 0, 0); |
| 726 | numamem++; |
| 727 | } |
| 728 | |
| 729 | build_header((void*)srat, SRAT_SIGNATURE, srat_size, 1); |
| 730 | |
Isaku Yamahata | e1920be | 2010-02-12 11:36:20 +0900 | [diff] [blame] | 731 | free(numadata); |
Kevin O'Connor | fb76cff | 2013-03-23 11:38:45 -0400 | [diff] [blame] | 732 | free(numacpumap); |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 733 | return srat; |
Kevin O'Connor | fb76cff | 2013-03-23 11:38:45 -0400 | [diff] [blame] | 734 | fail: |
| 735 | free(numadata); |
| 736 | free(numacpumap); |
| 737 | return NULL; |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 738 | } |
| 739 | |
Gerd Hoffmann | 7e269b4 | 2012-11-28 10:17:35 +0100 | [diff] [blame] | 740 | static void * |
| 741 | build_mcfg_q35(void) |
| 742 | { |
| 743 | struct acpi_table_mcfg *mcfg; |
| 744 | |
| 745 | int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); |
| 746 | mcfg = malloc_high(len); |
| 747 | if (!mcfg) { |
| 748 | warn_noalloc(); |
| 749 | return NULL; |
| 750 | } |
| 751 | memset(mcfg, 0, len); |
| 752 | mcfg->allocation[0].address = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; |
| 753 | mcfg->allocation[0].pci_segment = Q35_HOST_PCIE_PCI_SEGMENT; |
| 754 | mcfg->allocation[0].start_bus_number = Q35_HOST_PCIE_START_BUS_NUMBER; |
| 755 | mcfg->allocation[0].end_bus_number = Q35_HOST_PCIE_END_BUS_NUMBER; |
| 756 | |
| 757 | build_header((void *)mcfg, MCFG_SIGNATURE, len, 1); |
| 758 | return mcfg; |
| 759 | } |
| 760 | |
Isaku Yamahata | 4c67f90 | 2010-07-20 16:37:19 +0900 | [diff] [blame] | 761 | static const struct pci_device_id acpi_find_tbl[] = { |
| 762 | /* PIIX4 Power Management device. */ |
| 763 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), |
Isaku Yamahata | 72a590e | 2012-11-28 10:17:33 +0100 | [diff] [blame] | 764 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, NULL), |
Isaku Yamahata | 4c67f90 | 2010-07-20 16:37:19 +0900 | [diff] [blame] | 765 | PCI_DEVICE_END, |
| 766 | }; |
| 767 | |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 768 | struct rsdp_descriptor *RsdpAddr; |
| 769 | |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 770 | #define MAX_ACPI_TABLES 20 |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 771 | void |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 772 | acpi_setup(void) |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 773 | { |
| 774 | if (! CONFIG_ACPI) |
| 775 | return; |
| 776 | |
| 777 | dprintf(3, "init ACPI tables\n"); |
| 778 | |
| 779 | // This code is hardcoded for PIIX4 Power Management device. |
Kevin O'Connor | 278b19f | 2011-06-21 22:41:15 -0400 | [diff] [blame] | 780 | struct pci_device *pci = pci_find_init_device(acpi_find_tbl, NULL); |
| 781 | if (!pci) |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 782 | // Device not found |
| 783 | return; |
| 784 | |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 785 | // Build ACPI tables |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 786 | u32 tables[MAX_ACPI_TABLES], tbl_idx = 0; |
| 787 | |
| 788 | #define ACPI_INIT_TABLE(X) \ |
| 789 | do { \ |
| 790 | tables[tbl_idx] = (u32)(X); \ |
| 791 | if (tables[tbl_idx]) \ |
| 792 | tbl_idx++; \ |
| 793 | } while(0) |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 794 | |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 795 | struct fadt_descriptor_rev1 *fadt = build_fadt(pci); |
| 796 | ACPI_INIT_TABLE(fadt); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 797 | ACPI_INIT_TABLE(build_ssdt()); |
| 798 | ACPI_INIT_TABLE(build_madt()); |
Kevin O'Connor | f434377 | 2009-10-08 22:05:21 -0400 | [diff] [blame] | 799 | ACPI_INIT_TABLE(build_hpet()); |
Kevin O'Connor | 590e554 | 2009-10-08 22:09:02 -0400 | [diff] [blame] | 800 | ACPI_INIT_TABLE(build_srat()); |
Gerd Hoffmann | 7e269b4 | 2012-11-28 10:17:35 +0100 | [diff] [blame] | 801 | if (pci->device == PCI_DEVICE_ID_INTEL_ICH9_LPC) |
| 802 | ACPI_INIT_TABLE(build_mcfg_q35()); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 803 | |
Kevin O'Connor | 188d994 | 2013-02-09 15:24:08 -0500 | [diff] [blame] | 804 | struct romfile_s *file = NULL; |
| 805 | for (;;) { |
| 806 | file = romfile_findprefix("acpi/", file); |
| 807 | if (!file) |
| 808 | break; |
| 809 | struct acpi_table_header *table = malloc_high(file->size); |
| 810 | if (!table) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 811 | warn_noalloc(); |
Kevin O'Connor | cc6dc46 | 2009-10-08 21:18:41 -0400 | [diff] [blame] | 812 | continue; |
| 813 | } |
Kevin O'Connor | 188d994 | 2013-02-09 15:24:08 -0500 | [diff] [blame] | 814 | int ret = file->copy(file, table, file->size); |
| 815 | if (ret <= sizeof(*table)) |
| 816 | continue; |
| 817 | if (table->signature == DSDT_SIGNATURE) { |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 818 | if (fadt) { |
Kevin O'Connor | 188d994 | 2013-02-09 15:24:08 -0500 | [diff] [blame] | 819 | fill_dsdt(fadt, table); |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 820 | } |
| 821 | } else { |
Kevin O'Connor | 188d994 | 2013-02-09 15:24:08 -0500 | [diff] [blame] | 822 | ACPI_INIT_TABLE(table); |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 823 | } |
Kevin O'Connor | cc6dc46 | 2009-10-08 21:18:41 -0400 | [diff] [blame] | 824 | if (tbl_idx == MAX_ACPI_TABLES) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 825 | warn_noalloc(); |
Kevin O'Connor | cc6dc46 | 2009-10-08 21:18:41 -0400 | [diff] [blame] | 826 | break; |
| 827 | } |
| 828 | } |
Michael S. Tsirkin | 5aef563 | 2013-03-18 15:18:25 +0200 | [diff] [blame] | 829 | |
| 830 | if (CONFIG_ACPI_DSDT && fadt && !fadt->dsdt) { |
Isaku Yamahata | f2fd79b | 2011-10-24 17:11:08 +0900 | [diff] [blame] | 831 | /* default DSDT */ |
| 832 | void *dsdt = malloc_high(sizeof(AmlCode)); |
| 833 | if (!dsdt) { |
| 834 | warn_noalloc(); |
| 835 | return; |
| 836 | } |
| 837 | memcpy(dsdt, AmlCode, sizeof(AmlCode)); |
| 838 | fill_dsdt(fadt, dsdt); |
| 839 | } |
Kevin O'Connor | cc6dc46 | 2009-10-08 21:18:41 -0400 | [diff] [blame] | 840 | |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 841 | // Build final rsdt table |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 842 | struct rsdt_descriptor_rev1 *rsdt; |
| 843 | size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; |
| 844 | rsdt = malloc_high(rsdt_len); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 845 | if (!rsdt) { |
Kevin O'Connor | cfdc13f | 2010-02-14 13:07:54 -0500 | [diff] [blame] | 846 | warn_noalloc(); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 847 | return; |
| 848 | } |
| 849 | memset(rsdt, 0, rsdt_len); |
| 850 | memcpy(rsdt->table_offset_entry, tables, sizeof(u32) * tbl_idx); |
Kevin O'Connor | a9242a2 | 2009-10-07 19:42:07 -0400 | [diff] [blame] | 851 | build_header((void*)rsdt, RSDT_SIGNATURE, rsdt_len, 1); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 852 | |
| 853 | // Build rsdp pointer table |
Kevin O'Connor | 8a161c9 | 2011-09-02 18:11:58 -0400 | [diff] [blame] | 854 | struct rsdp_descriptor *rsdp = malloc_fseg(sizeof(*rsdp)); |
| 855 | if (!rsdp) { |
| 856 | warn_noalloc(); |
| 857 | return; |
| 858 | } |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 859 | memset(rsdp, 0, sizeof(*rsdp)); |
| 860 | rsdp->signature = RSDP_SIGNATURE; |
Kevin O'Connor | e52ad39 | 2013-02-20 23:48:22 -0500 | [diff] [blame] | 861 | memcpy(rsdp->oem_id, BUILD_APPNAME6, 6); |
Kevin O'Connor | 2929c35 | 2009-07-25 13:48:27 -0400 | [diff] [blame] | 862 | rsdp->rsdt_physical_address = cpu_to_le32((u32)rsdt); |
| 863 | rsdp->checksum -= checksum(rsdp, 20); |
| 864 | RsdpAddr = rsdp; |
| 865 | dprintf(1, "ACPI tables: RSDP=%p RSDT=%p\n", rsdp, rsdt); |
Kevin O'Connor | 276d4a9 | 2008-06-11 22:47:01 -0400 | [diff] [blame] | 866 | } |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 867 | |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 868 | static struct fadt_descriptor_rev1 * |
| 869 | find_fadt(void) |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 870 | { |
| 871 | dprintf(4, "rsdp=%p\n", RsdpAddr); |
| 872 | if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE) |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 873 | return NULL; |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 874 | struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address; |
| 875 | dprintf(4, "rsdt=%p\n", rsdt); |
| 876 | if (!rsdt || rsdt->signature != RSDT_SIGNATURE) |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 877 | return NULL; |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 878 | void *end = (void*)rsdt + rsdt->length; |
| 879 | int i; |
| 880 | for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) { |
| 881 | struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i]; |
| 882 | if (!fadt || fadt->signature != FACP_SIGNATURE) |
| 883 | continue; |
| 884 | dprintf(4, "fadt=%p\n", fadt); |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 885 | return fadt; |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 886 | } |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 887 | dprintf(4, "no fadt found\n"); |
| 888 | return NULL; |
| 889 | } |
| 890 | |
| 891 | u32 |
| 892 | find_resume_vector(void) |
| 893 | { |
| 894 | struct fadt_descriptor_rev1 *fadt = find_fadt(); |
| 895 | if (!fadt) |
| 896 | return 0; |
| 897 | struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl; |
| 898 | dprintf(4, "facs=%p\n", facs); |
| 899 | if (! facs || facs->signature != FACS_SIGNATURE) |
| 900 | return 0; |
| 901 | // Found it. |
| 902 | dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector); |
| 903 | return facs->firmware_waking_vector; |
| 904 | } |
| 905 | |
| 906 | void |
David Woodhouse | d304fe4 | 2013-02-23 00:24:48 +0000 | [diff] [blame] | 907 | find_acpi_features(void) |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 908 | { |
| 909 | struct fadt_descriptor_rev1 *fadt = find_fadt(); |
| 910 | if (!fadt) |
| 911 | return; |
David Woodhouse | d304fe4 | 2013-02-23 00:24:48 +0000 | [diff] [blame] | 912 | u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk); |
David Woodhouse | 3219dfa | 2013-02-03 23:41:41 +0100 | [diff] [blame] | 913 | dprintf(4, "pm_tmr_blk=%x\n", pm_tmr); |
David Woodhouse | d304fe4 | 2013-02-23 00:24:48 +0000 | [diff] [blame] | 914 | if (pm_tmr) |
| 915 | pmtimer_setup(pm_tmr, 3579); |
David Woodhouse | d338eb9 | 2013-02-23 00:24:49 +0000 | [diff] [blame] | 916 | |
| 917 | // Theoretically we should check the 'reset_reg_sup' flag, but Windows |
| 918 | // doesn't and thus nobody seems to *set* it. If the table is large enough |
| 919 | // to include it, let the sanity checks in acpi_set_reset_reg() suffice. |
| 920 | if (fadt->length >= 129) { |
| 921 | void *p = fadt; |
| 922 | acpi_set_reset_reg(p + 116, *(u8 *)(p + 128)); |
| 923 | } |
| 924 | } |
| 925 | |
| 926 | static struct acpi_20_generic_address acpi_reset_reg; |
| 927 | static u8 acpi_reset_val; |
| 928 | |
| 929 | void |
| 930 | acpi_reboot(void) |
| 931 | { |
| 932 | // Check it passed the sanity checks in acpi_set_reset_reg() and was set |
| 933 | if (acpi_reset_reg.register_bit_width != 8) |
| 934 | return; |
| 935 | |
| 936 | u64 addr = le64_to_cpu(acpi_reset_reg.address); |
| 937 | |
| 938 | dprintf(1, "ACPI hard reset %d:%llx (%x)\n", |
| 939 | acpi_reset_reg.address_space_id, addr, acpi_reset_val); |
| 940 | |
| 941 | switch (acpi_reset_reg.address_space_id) { |
| 942 | case 0: // System Memory |
| 943 | writeb((void *)(u32)addr, acpi_reset_val); |
| 944 | break; |
| 945 | case 1: // System I/O |
| 946 | outb(acpi_reset_val, addr); |
| 947 | break; |
| 948 | case 2: // PCI config space |
| 949 | pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val); |
| 950 | break; |
| 951 | } |
| 952 | } |
| 953 | |
| 954 | void |
| 955 | acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val) |
| 956 | { |
| 957 | if (!reg || reg->address_space_id > 2 || |
| 958 | reg->register_bit_width != 8 || reg->register_bit_offset) |
| 959 | return; |
| 960 | |
| 961 | acpi_reset_reg = *reg; |
| 962 | acpi_reset_val = val; |
Kevin O'Connor | 9967ab7 | 2008-12-18 21:57:33 -0500 | [diff] [blame] | 963 | } |