1// Copyright 2016 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <zircon/compiler.h> 8 9#include <acpica/acpi.h> 10 11#include <assert.h> 12#include <err.h> 13#include <trace.h> 14 15#include <lk/init.h> 16 17#include <arch/x86/apic.h> 18#include <platform/pc/acpi.h> 19#include <zircon/types.h> 20 21#define LOCAL_TRACE 0 22 23#define ACPI_MAX_INIT_TABLES 32 24static ACPI_TABLE_DESC acpi_tables[ACPI_MAX_INIT_TABLES]; 25static bool acpi_initialized = false; 26 27static ACPI_STATUS acpi_set_apic_irq_mode(void); 28 29/** 30 * @brief Initialize early-access ACPI tables 31 * 32 * This function enables *only* the ACPICA Table Manager subsystem. 33 * The rest of the ACPI subsystem will remain uninitialized. 34 */ 35void platform_init_acpi_tables(uint level) { 36 DEBUG_ASSERT(!acpi_initialized); 37 38 ACPI_STATUS status; 39 status = AcpiInitializeTables(acpi_tables, ACPI_MAX_INIT_TABLES, FALSE); 40 41 if (status == AE_NOT_FOUND) { 42 TRACEF("WARNING: could not find ACPI tables\n"); 43 return; 44 } else if (status == AE_NO_MEMORY) { 45 TRACEF("WARNING: could not initialize ACPI tables\n"); 46 return; 47 } else if (status != AE_OK) { 48 TRACEF("WARNING: could not initialize ACPI tables for unknown reason\n"); 49 return; 50 } 51 52 acpi_initialized = true; 53 LTRACEF("ACPI tables initialized\n"); 54} 55 56/* initialize ACPI tables as soon as we have a working VM */ 57LK_INIT_HOOK(acpi_tables, &platform_init_acpi_tables, LK_INIT_LEVEL_VM + 1); 58 59static zx_status_t acpi_get_madt_record_limits(uintptr_t* start, uintptr_t* end) { 60 ACPI_TABLE_HEADER* table = NULL; 61 ACPI_STATUS status = AcpiGetTable((char*)ACPI_SIG_MADT, 1, &table); 62 if (status != AE_OK) { 63 TRACEF("could not find MADT\n"); 64 return ZX_ERR_NOT_FOUND; 65 } 66 ACPI_TABLE_MADT* madt = (ACPI_TABLE_MADT*)table; 67 uintptr_t records_start = ((uintptr_t)madt) + sizeof(*madt); 68 uintptr_t records_end = ((uintptr_t)madt) + madt->Header.Length; 69 if (records_start >= records_end) { 70 TRACEF("MADT wraps around address space\n"); 71 return ZX_ERR_INTERNAL; 72 } 73 // Shouldn't be too many records 74 if (madt->Header.Length > 4096) { 75 TRACEF("MADT suspiciously long: %u\n", madt->Header.Length); 76 return ZX_ERR_INTERNAL; 77 } 78 *start = records_start; 79 *end = records_end; 80 return ZX_OK; 81} 82 83/* @brief Enumerate all functioning CPUs and their APIC IDs 84 * 85 * If apic_ids is NULL, just returns the number of logical processors 86 * via num_cpus. 87 * 88 * @param apic_ids Array to write found APIC ids into. 89 * @param len Length of apic_ids. 90 * @param num_cpus Output for the number of logical processors detected. 91 * 92 * @return ZX_OK on success. Note that if len < *num_cpus, not all 93 * logical apic_ids will be returned. 94 */ 95zx_status_t platform_enumerate_cpus( 96 uint32_t* apic_ids, 97 uint32_t len, 98 uint32_t* num_cpus) { 99 if (num_cpus == NULL) { 100 return ZX_ERR_INVALID_ARGS; 101 } 102 103 uintptr_t records_start, records_end; 104 zx_status_t status = acpi_get_madt_record_limits(&records_start, &records_end); 105 if (status != ZX_OK) { 106 return status; 107 } 108 uint32_t count = 0; 109 uintptr_t addr; 110 ACPI_SUBTABLE_HEADER* record_hdr; 111 for (addr = records_start; addr < records_end; addr += record_hdr->Length) { 112 record_hdr = (ACPI_SUBTABLE_HEADER*)addr; 113 switch (record_hdr->Type) { 114 case ACPI_MADT_TYPE_LOCAL_APIC: { 115 ACPI_MADT_LOCAL_APIC* lapic = (ACPI_MADT_LOCAL_APIC*)record_hdr; 116 if (!(lapic->LapicFlags & ACPI_MADT_ENABLED)) { 117 LTRACEF("Skipping disabled processor %02x\n", lapic->Id); 118 continue; 119 } 120 if (apic_ids != NULL && count < len) { 121 apic_ids[count] = lapic->Id; 122 } 123 count++; 124 break; 125 } 126 } 127 } 128 if (addr != records_end) { 129 TRACEF("malformed MADT\n"); 130 return ZX_ERR_INTERNAL; 131 } 132 *num_cpus = count; 133 return ZX_OK; 134} 135 136/* @brief Enumerate all IO APICs 137 * 138 * If io_apics is NULL, just returns the number of IO APICs 139 * via num_io_apics. 140 * 141 * @param io_apics Array to populate descriptors into. 142 * @param len Length of io_apics. 143 * @param num_io_apics Number of IO apics found 144 * 145 * @return ZX_OK on success. Note that if len < *num_io_apics, not all 146 * IO APICs will be returned. 147 */ 148zx_status_t platform_enumerate_io_apics( 149 struct io_apic_descriptor* io_apics, 150 uint32_t len, 151 uint32_t* num_io_apics) { 152 if (num_io_apics == NULL) { 153 return ZX_ERR_INVALID_ARGS; 154 } 155 156 uintptr_t records_start, records_end; 157 zx_status_t status = acpi_get_madt_record_limits(&records_start, &records_end); 158 if (status != ZX_OK) { 159 return status; 160 } 161 162 uint32_t count = 0; 163 uintptr_t addr; 164 for (addr = records_start; addr < records_end;) { 165 ACPI_SUBTABLE_HEADER* record_hdr = (ACPI_SUBTABLE_HEADER*)addr; 166 switch (record_hdr->Type) { 167 case ACPI_MADT_TYPE_IO_APIC: { 168 ACPI_MADT_IO_APIC* io_apic = (ACPI_MADT_IO_APIC*)record_hdr; 169 if (io_apics != NULL && count < len) { 170 io_apics[count].apic_id = io_apic->Id; 171 io_apics[count].paddr = io_apic->Address; 172 io_apics[count].global_irq_base = io_apic->GlobalIrqBase; 173 } 174 count++; 175 break; 176 } 177 } 178 179 addr += record_hdr->Length; 180 } 181 if (addr != records_end) { 182 TRACEF("malformed MADT\n"); 183 return ZX_ERR_INVALID_ARGS; 184 } 185 *num_io_apics = count; 186 return ZX_OK; 187} 188 189/* @brief Enumerate all interrupt source overrides 190 * 191 * If isos is NULL, just returns the number of ISOs via num_isos. 192 * 193 * @param isos Array to populate overrides into. 194 * @param len Length of isos. 195 * @param num_isos Number of ISOs found 196 * 197 * @return ZX_OK on success. Note that if len < *num_isos, not all 198 * ISOs will be returned. 199 */ 200zx_status_t platform_enumerate_interrupt_source_overrides( 201 struct io_apic_isa_override* isos, 202 uint32_t len, 203 uint32_t* num_isos) { 204 if (num_isos == NULL) { 205 return ZX_ERR_INVALID_ARGS; 206 } 207 208 uintptr_t records_start, records_end; 209 zx_status_t status = acpi_get_madt_record_limits(&records_start, &records_end); 210 if (status != ZX_OK) { 211 return status; 212 } 213 214 uint32_t count = 0; 215 uintptr_t addr; 216 for (addr = records_start; addr < records_end;) { 217 ACPI_SUBTABLE_HEADER* record_hdr = (ACPI_SUBTABLE_HEADER*)addr; 218 switch (record_hdr->Type) { 219 case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: { 220 ACPI_MADT_INTERRUPT_OVERRIDE* iso = 221 (ACPI_MADT_INTERRUPT_OVERRIDE*)record_hdr; 222 if (isos != NULL && count < len) { 223 ASSERT(iso->Bus == 0); // 0 means ISA, ISOs are only ever for ISA IRQs 224 isos[count].isa_irq = iso->SourceIrq; 225 isos[count].remapped = true; 226 isos[count].global_irq = iso->GlobalIrq; 227 228 uint32_t flags = iso->IntiFlags; 229 uint32_t polarity = flags & ACPI_MADT_POLARITY_MASK; 230 uint32_t trigger = flags & ACPI_MADT_TRIGGER_MASK; 231 232 // Conforms below means conforms to the bus spec. ISA is 233 // edge triggered and active high. 234 switch (polarity) { 235 case ACPI_MADT_POLARITY_CONFORMS: 236 case ACPI_MADT_POLARITY_ACTIVE_HIGH: 237 isos[count].pol = IRQ_POLARITY_ACTIVE_HIGH; 238 break; 239 case ACPI_MADT_POLARITY_ACTIVE_LOW: 240 isos[count].pol = IRQ_POLARITY_ACTIVE_LOW; 241 break; 242 default: 243 panic("Unknown IRQ polarity in override: %u\n", 244 polarity); 245 } 246 247 switch (trigger) { 248 case ACPI_MADT_TRIGGER_CONFORMS: 249 case ACPI_MADT_TRIGGER_EDGE: 250 isos[count].tm = IRQ_TRIGGER_MODE_EDGE; 251 break; 252 case ACPI_MADT_TRIGGER_LEVEL: 253 isos[count].tm = IRQ_TRIGGER_MODE_LEVEL; 254 break; 255 default: 256 panic("Unknown IRQ trigger in override: %u\n", 257 trigger); 258 } 259 } 260 count++; 261 break; 262 } 263 } 264 265 addr += record_hdr->Length; 266 } 267 if (addr != records_end) { 268 TRACEF("malformed MADT\n"); 269 return ZX_ERR_INVALID_ARGS; 270 } 271 *num_isos = count; 272 return ZX_OK; 273} 274 275/* @brief Return information about the High Precision Event Timer, if present. 276 * 277 * @param hpet Descriptor to populate 278 * 279 * @return ZX_OK on success. 280 */ 281zx_status_t platform_find_hpet(struct acpi_hpet_descriptor* hpet) { 282 ACPI_TABLE_HEADER* table = NULL; 283 ACPI_STATUS status = AcpiGetTable((char*)ACPI_SIG_HPET, 1, &table); 284 if (status != AE_OK) { 285 TRACEF("could not find HPET\n"); 286 return ZX_ERR_NOT_FOUND; 287 } 288 ACPI_TABLE_HPET* hpet_tbl = (ACPI_TABLE_HPET*)table; 289 if (hpet_tbl->Header.Length != sizeof(ACPI_TABLE_HPET)) { 290 TRACEF("Unexpected HPET table length\n"); 291 return ZX_ERR_NOT_FOUND; 292 } 293 294 hpet->minimum_tick = hpet_tbl->MinimumTick; 295 hpet->sequence = hpet_tbl->Sequence; 296 hpet->address = hpet_tbl->Address.Address; 297 switch (hpet_tbl->Address.SpaceId) { 298 case ACPI_ADR_SPACE_SYSTEM_IO: 299 hpet->port_io = true; 300 break; 301 case ACPI_ADR_SPACE_SYSTEM_MEMORY: 302 hpet->port_io = false; 303 break; 304 default: 305 return ZX_ERR_NOT_SUPPORTED; 306 } 307 308 return ZX_OK; 309} 310