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
24 static ACPI_TABLE_DESC acpi_tables[ACPI_MAX_INIT_TABLES];
25 static bool acpi_initialized = false;
26
27 static 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 */
platform_init_acpi_tables(uint level)35 void 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 */
57 LK_INIT_HOOK(acpi_tables, &platform_init_acpi_tables, LK_INIT_LEVEL_VM + 1);
58
acpi_get_madt_record_limits(uintptr_t * start,uintptr_t * end)59 static 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 */
platform_enumerate_cpus(uint32_t * apic_ids,uint32_t len,uint32_t * num_cpus)95 zx_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 */
platform_enumerate_io_apics(struct io_apic_descriptor * io_apics,uint32_t len,uint32_t * num_io_apics)148 zx_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 */
platform_enumerate_interrupt_source_overrides(struct io_apic_isa_override * isos,uint32_t len,uint32_t * num_isos)200 zx_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 */
platform_find_hpet(struct acpi_hpet_descriptor * hpet)281 zx_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