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