1/**
2 * \file acpi_parse_madt.c
3 * \brief
4 */
5
6
7/*
8 * Copyright (c) 2017 ETH Zurich.
9 * All rights reserved.
10 *
11 * This file is distributed under the terms in the attached LICENSE file.
12 * If you do not find this file, copies can be found by writing to:
13 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14 */
15
16#include <barrelfish/barrelfish.h>
17
18#include <arch/aarch64/hw_records_arch.h>
19#include <arch/x86_64/hw_records_arch.h>
20
21#include <skb/skb.h>
22#include <octopus/getset.h>
23#include <trace/trace.h>
24
25
26#include "acpi_debug.h"
27#include "acpi_shared.h"
28
29static coreid_t barrelfish_id_counter = 1;
30
31
32/*
33 * local_apic(proc_id:8, apicid:8, flags:32);
34 * io_apic(apic_id:8, pbaseaddress:32, globalirqbase:32)
35 * interrupt_override(bus:8, sourceirq:8, globalirq:32, intiflags:16)
36 * nmi_source(intiflags:16, globalirq:32)
37 * local_apic_nmi(proc_id:8, intiflags:16, lint:8)
38 * local_apic_overritde(address:64)
39 * io_sapic(id:8, globalirqbase:32, address:64)
40 * local_sapic(proc_id:8, id:8, eid:8, lapicflags:32, uid:32, uidstring:x)
41 * interrupt_source(intiflags:16, type:8, id:8, eid:8, iosapicvector:8, globalirq:32, flags:32)
42 * local_x2apic(localapicid:32, laicflags:32,uid:32)
43 * local_x2apic_nmi(intiflags:16, uid:32, lint:8)
44 * generic_interrupt(
45     CpuInterfaceNumber:32; Uid:32, Flags:32, ParkingVersion:32,
46    PerformanceInterrupt:32, ParkedAddress:64, BaseAddress:64,
47    GicvBaseAddress:64, GichBaseAddress:64, VgicInterrupt:32, GicrBaseAddress:64,
48    ArmMpidr:64, EfficiencyClass:8)
49 * generic_distributor(gicid:32, baseaddr:64, globalirqbase:32, version:8)
50 * generic_msi_frame(msiframeid:32, baseaddr:64, flags:32, spicount:16, spibase:16)
51 * generic_redistributor(baseaddr: 64, length:32)
52 * generic_translator(translationid:32, baseaddr:64)
53 */
54
55#define SKB_SCHEMA_LOCAL_APIC \
56    "local_apic(%" PRIu8 ", %" PRIu8 ", %" PRIu32 ")."
57#define SKB_SCHEMA_IO_APIC \
58    "io_apic(%" PRIu8 ", %" PRIu32 ", %" PRIu32 ")."
59#define SKB_SCHEMA_INTERRUPT_OVERRIDE \
60    "interrupt_override(%" PRIu8 ", %" PRIu8 ", %" PRIu32 ", %" PRIu16 ")."
61#define SKB_SCHEMA_NMI_SOURCE \
62    "nmi_source(%" PRIu16 ", %" PRIu32 ")."
63#define SKB_SCHEMA_LOCAL_APIC_NMI \
64    "local_apic_nmi(%" PRIu8 ", %" PRIu16 ", %" PRIu8 ")."
65#define SKB_SCHEMA_LOCAL_APIC_OVERRIDE \
66    "local_apic_override(%" PRIu64 ")."
67#define SKB_SCHEMA_IO_SAPIC \
68    "io_sapic(%" PRIu8 ", %" PRIu32 ", %" PRIu64 ")."
69#define SKB_SCHEMA_LOCAL_SAPIC \
70    "local_sapic(%" PRIu8 ", %" PRIu8 ", %" PRIu8", %" PRIu32 ", %" PRIu32 ", %s)."
71#define SKB_SCHEMA_INTERRUPT_SOURCE \
72    "interrupt_source(%" PRIu16 ", %" PRIu8 ", %" PRIu8", %" PRIu8 ", %" PRIu32 ", %" PRIu32 ")."
73#define SKB_SCHEMA_LOCAL_X2APIC \
74    "local_x2apic(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ")."
75#define SKB_SCHEMA_LOCAL_X2APIC_NMI \
76    "local_x2apic_nmi(%" PRIu16 ", %" PRIu32 ", %" PRIu8 ")."
77#define SKB_SCHEMA_GENERIC_INTERRUPT \
78    "generic_interrupt(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ", %" PRIu32 ", %" PRIu32 ", %" \
79                          PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu64 ", %" PRIu32 ", %" \
80                          PRIu64 ", %" PRIu64 ", %" PRIu8 ")."
81#define SKB_SCHEMA_GENERIC_DISTRIBUTOR \
82    "generic_distributor(%" PRIu32 ", %" PRIu64 ", %" PRIu32 ", %" PRIu8 ")."
83#define SKB_SCHEMA_GENERIC_MSI_FRAME \
84    "generic_msi_frame(%" PRIu32 ", %" PRIu64 ", %" PRIu32 ", %" PRIu16 ", %" PRIu16 ")."
85#define SKB_SCHEMA_GENERIC_REDISTRIBUTOR \
86    "generic_redistributor(%" PRIu64 ", %" PRIu32 ")."
87#define SKB_SCHEMA_GENERIC_TRANSLATOR \
88    "generic_translator(%" PRIu32 ", %" PRIu64 ")."
89
90
91
92
93static errval_t parse_entry_local_apic(ACPI_MADT_LOCAL_APIC *s)
94{
95    errval_t err;
96
97    APCI_DEBUG(SKB_SCHEMA_LOCAL_APIC, s->ProcessorId, s->Id, s->LapicFlags);
98
99    ACPI_DEBUG("Found local APIC: CPU = %d, ID = %d, usable = %d\n",
100            s->ProcessorId, s->Id,
101            s->LapicFlags & ACPI_MADT_ENABLED);
102
103    trace_event(TRACE_SUBSYS_ACPI, TRACE_EVENT_ACPI_APIC_ADDED, s->ProcessorId);
104
105    err = skb_add_fact(SKB_SCHEMA_LOCAL_APIC, s->ProcessorId, s->Id, s->LapicFlags);
106    if (err_is_fail(err)) {
107        return err;
108    }
109
110    coreid_t barrelfish_id;
111    if (my_hw_id == s->Id) {
112        barrelfish_id = 0; // BSP core is 0
113    } else {
114        barrelfish_id = barrelfish_id_counter++;
115    }
116
117    /* compatibility */
118    skb_add_fact("apic(%d,%d,%"PRIu32").", s->ProcessorId, s->Id,
119                 s->LapicFlags & ACPI_MADT_ENABLED);
120
121    return oct_set(HW_PROCESSOR_X86_RECORD_FORMAT, barrelfish_id,
122                   s->LapicFlags & ACPI_MADT_ENABLED, barrelfish_id,
123                   s->Id, CPU_X86_64, s->ProcessorId, s->Id);
124}
125
126static errval_t parse_entry_io_apic(ACPI_MADT_IO_APIC *s)
127{
128    errval_t err;
129
130    ACPI_DEBUG(SKB_SCHEMA_IO_APIC, s->Id, s->Address, s->GlobalIrqBase);
131    skb_add_fact(SKB_SCHEMA_IO_APIC, s->Id, s->Address, s->GlobalIrqBase);
132
133    ACPI_DEBUG("Found I/O APIC: ID = %d, mem base = 0x%"PRIx32", "
134           "INTI base = %"PRIu32"\n", s->Id, s->Address, s->GlobalIrqBase);
135
136    skb_add_fact("ioapic(%d,%"PRIu32",%"PRIu32").", s->Id, s->Address, s->GlobalIrqBase);
137    skb_add_fact("memory_region(%"PRIu32",%u,%zu, %u,%u).",
138                 s->Address,
139                 BASE_PAGE_BITS, //as used elswhere in acpi.c
140                 ((size_t)1) << BASE_PAGE_BITS, //as used elswhere in acpi.c
141                 RegionType_IOAPIC,
142                 0);
143
144    char ioapic_lbl[255];
145    char query_buf[1024];
146
147    snprintf(query_buf,1024, "add_ioapic_controller(Lbl, %d, %d).",
148            s->Id, s->GlobalIrqBase);
149    err = skb_execute(query_buf);
150    if(err_is_fail(err)){
151        DEBUG_SKB_ERR(err,"add_ioapic_controller");
152    }
153    skb_read_output_at(strchr(skb_get_output(),'\n'),"%s",ioapic_lbl);
154    ACPI_DEBUG("Added ioapic ctrl, lbl=%s\n",ioapic_lbl);
155
156    err = init_one_ioapic(s, ioapic_lbl);
157    if(err_is_fail(err)) {
158        DEBUG_ERR(err, "Unable to initialize I/O APIC (ID = %d)",
159                  s->Id);
160        abort();
161    }
162
163    return SYS_ERR_OK;
164}
165
166static errval_t parse_entry_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *s)
167{
168    ACPI_DEBUG(SKB_SCHEMA_INTERRUPT_OVERRIDE, s->Bus, s->SourceIrq, s->GlobalIrq,
169               s->IntiFlags);
170
171    skb_add_fact(SKB_SCHEMA_INTERRUPT_OVERRIDE, s->Bus, s->SourceIrq, s->GlobalIrq,
172                   s->IntiFlags);
173
174    ACPI_DEBUG("Found interrupt override: bus = %d, bus_irq = %d, "
175           "GSI = %"PRIu32", flags = %x\n", s->Bus, s->SourceIrq,
176           s->GlobalIrq, s->IntiFlags);
177
178    // ACPI spec says these are only for ISA interrupts
179    assert(s->SourceIrq < N_ISA_INTERRUPTS);
180
181    interrupt_overrides[s->SourceIrq] = s->GlobalIrq;
182
183    if (s->IntiFlags == 0) {
184        break;
185    }
186
187    lpc_ioapic_redir_tbl_t entry = ioapic_redir_tmpl_isa;
188    struct ioapic *a = find_ioapic(s->GlobalIrq);
189    if (a == NULL) {
190        ACPI_DEBUG("Warning: unknown IOAPIC for GSI %"PRIu32", ignored"
191                  " interrupt override flags.\n", s->GlobalIrq);
192        break;
193    }
194
195    // Set polarity
196    assert((s->IntiFlags & ACPI_MADT_POLARITY_MASK)
197           != ACPI_MADT_POLARITY_RESERVED);
198
199    switch(s->IntiFlags & ACPI_MADT_POLARITY_MASK) {
200    case ACPI_MADT_POLARITY_ACTIVE_HIGH:
201        entry.polarity = lpc_ioapic_active_high;
202        break;
203
204    case ACPI_MADT_POLARITY_ACTIVE_LOW:
205        entry.polarity = lpc_ioapic_active_low;
206        break;
207    }
208
209    // Set trigger mode
210    assert((s->IntiFlags & ACPI_MADT_TRIGGER_MASK)
211           != ACPI_MADT_TRIGGER_RESERVED);
212
213    switch(s->IntiFlags & ACPI_MADT_TRIGGER_MASK) {
214    case ACPI_MADT_TRIGGER_EDGE:
215        entry.trigger = lpc_ioapic_edge;
216        break;
217
218    case ACPI_MADT_TRIGGER_LEVEL:
219        // XXX: should be lpc_ioapic_level
220        entry.trigger = lpc_ioapic_edge;
221        break;
222    }
223
224    ioapic_setup_inti(a, s->GlobalIrq - a->irqbase, entry);
225
226    return SYS_ERR_OK;
227}
228
229static errval_t parse_entry_nmi_source(ACPI_MADT_NMI_SOURCE *e)
230{
231    ACPI_DEBUG(SKB_SCHEMA_NMI_SOURCE, e->IntiFlags, e->GlobalIrq);
232
233    return skb_add_fact(SKB_SCHEMA_NMI_SOURCE, e->IntiFlags, e->GlobalIrq);;
234}
235
236static errval_t parse_entry_local_apic_nmi(ACPI_MADT_LOCAL_APIC_NMI *s)
237{
238    ACPI_DEBUG(SKB_SCHEMA_LOCAL_APIC_NMI, s->ProcessorId, s->IntiFlags, s->Lint);
239
240    /* compatibility entry */
241    skb_add_fact("apic_nmi(%d,%d,%d).",s->ProcessorId, s->IntiFlags, s->Lint);
242
243    return skb_add_fact(SKB_SCHEMA_LOCAL_APIC_NMI, s->ProcessorId, s->IntiFlags,
244                        s->Lint);
245}
246
247static errval_t parse_entry_local_apic_override(ACPI_MADT_LOCAL_APIC_OVERRIDE *e)
248{
249    ACPI_DEBUG(SKB_SCHEMA_LOCAL_APIC_OVERRIDE, e->Address);
250
251    return skb_add_fact(SKB_SCHEMA_LOCAL_APIC_OVERRIDE, e->Address);
252}
253
254static errval_t parse_entry_io_sapic(ACPI_MADT_IO_SAPIC *e)
255{
256    ACPI_DEBUG(SKB_SCHEMA_IO_SAPIC, e->Id, e->GlobalIrqBase, e->Address);
257
258    return skb_add_fact(SKB_SCHEMA_IO_SAPIC, e->Id, e->GlobalIrqBase, e->Address);
259}
260
261static errval_t parse_entry_local_sapic(ACPI_MADT_LOCAL_SAPIC *e)
262{
263    ACPI_DEBUG(SKB_SCHEMA_LOCAL_SAPIC, e->ProcessorId, e->Id, e->Eid, e->LapicFlags,
264               e->Uid, e->UidString);
265
266    return skb_add_facts(SKB_SCHEMA_LOCAL_SAPIC, e->ProcessorId, e->Id, e->Eid,
267                         e->LapicFlags, e->Uid, e->UidString);;
268}
269
270static errval_t parse_entry_interrupt_source(ACPI_MADT_INTERRUPT_SOURCE *e)
271{
272    ACPI_DEBUG(SKB_SCHEMA_INTERRUPT_SOURCE, e->IntiFlags, e->Type, e->Id, e->Eid,
273               e->IoSapicVector, e->GlobalIrq, e->Flags);
274
275    return skb_add_facts(SKB_SCHEMA_INTERRUPT_SOURCE, e->IntiFlags, e->Type, e->Id,
276                         e->Eid, e->IoSapicVector, e->GlobalIrq, e->Flags);
277}
278
279static errval_t parse_entry_local_x2apic(ACPI_MADT_LOCAL_X2APIC *e)
280{
281    ACPI_DEBUG(SKB_SCHEMA_LOCAL_X2APIC, e->LocalApicId, e->LapicFlags, e->Uid);
282
283    return skb_add_facts(SKB_SCHEMA_LOCAL_X2APIC, e->LocalApicId, e->LapicFlags, e->Uid);
284}
285
286static errval_t parse_entry_local_x2apic_nmi(ACPI_MADT_LOCAL_X2APIC_NMI *e)
287{
288    ACPI_DEBUG(SKB_SCHEMA_LOCAL_X2APIC_NMI, e->Uid, e->Lint);
289
290    return skb_add_facts(SKB_SCHEMA_LOCAL_X2APIC_NMI, e->Uid, e->Lint);
291}
292
293static errval_t parse_entry_generic_interrupt(ACPI_MADT_GENERIC_INTERRUPT *s)
294{
295    errval_t err;
296
297    ACPI_DEBUG(SKB_SCHEMA_GENERIC_INTERRUPT, s->CpuInterfaceNumber, s->Uid, s->Flags,
298               s->ParkingVersion, s->PerformanceInterrupt, s->ParkedAddress,
299               s->BaseAddress, s->GicvBaseAddress, s->GichBaseAddress,
300               s->VgicInterrupt, s->GicrBaseAddress, s->ArmMpidr, s->EfficiencyClass);
301
302    err = skb_add_facts(SKB_SCHEMA_GENERIC_INTERRUPT, s->CpuInterfaceNumber,
303                        s->Uid, s->Flags, s->ParkingVersion, s->PerformanceInterrupt,
304                        s->ParkedAddress, s->BaseAddress, s->GicvBaseAddress,
305                        s->GichBaseAddress, s->VgicInterrupt, s->GicrBaseAddress,
306                        s->ArmMpidr, s->EfficiencyClass);
307    if (err_is_fail(err)) {
308        return err;
309    }
310
311    /* figure out the barrelfish ID */
312    coreid_t barrelfish_id;
313    if (my_hw_id == s->Uid) {
314        barrelfish_id = 0; // BSP core is 0
315    } else {
316        barrelfish_id = barrelfish_id_counter++;
317    }
318
319    /* figure out the boot protocol */
320    if (s->ParkingVersion) {
321        /* parking */
322        err = skb_add_fact("boot_driver_entry(%"PRIu64",%s).", s->ArmMpidr,
323                           "armBootParking");
324    } else {
325        /* psci */
326        err = skb_add_fact("boot_driver_entry(%"PRIu64",%s).", s->ArmMpidr,
327                            "armBootPSCI");
328    }
329
330    if (err_is_fail(err)) {
331        DEBUG_ERR(err, "failed to add the boot protocol, continuing anyway\n");
332    }
333
334    /* setting the octopus record */
335    return oct_set(HW_PROCESSOR_ARMV8_RECORD_FORMAT, barrelfish_id,
336                   s->Flags & ACPI_MADT_ENABLED, barrelfish_id, s->ArmMpidr,
337                   CPU_ARM8, s->CpuInterfaceNumber, s->Uid, s->Flags, s->ParkingVersion,
338                   s->PerformanceInterrupt, s->ParkedAddress, s->BaseAddress,
339                   s->GicvBaseAddress, s->GichBaseAddress, s->VgicInterrupt,
340                   s->GicrBaseAddress, s->ArmMpidr);
341}
342
343static errval_t parse_entry_generic_distributor(ACPI_MADT_GENERIC_DISTRIBUTOR *s)
344{
345    ACPI_DEBUG(SKB_SCHEMA_GENERIC_DISTRIBUTOR, s->GicId, s->BaseAddress,
346               s->GlobalIrqBase, s->Version);
347
348    return skb_add_facts(SKB_SCHEMA_GENERIC_DISTRIBUTOR, s->GicId, s->BaseAddress,
349                         s->GlobalIrqBase, s->Version);
350}
351
352static errval_t parse_entry_generic_msi_frame(ACPI_MADT_GENERIC_MSI_FRAME *s)
353{
354    ACPI_DEBUG(SKB_SCHEMA_GENERIC_REDISTRIBUTOR, s->MsiFrameId, s->BaseAddress,
355               s->Flags, s->SpiCount, s->SpiBase);
356
357    return skb_add_facts(SKB_SCHEMA_GENERIC_REDISTRIBUTOR, s->MsiFrameId,
358                         s->BaseAddress, s->Flags, s->SpiCount, s->SpiBase);
359}
360
361static errval_t parse_entry_generic_redistributor(ACPI_MADT_GENERIC_REDISTRIBUTOR *s)
362{
363    ACPI_DEBUG(SKB_SCHEMA_GENERIC_REDISTRIBUTOR, s->BaseAddress, s->Length);
364
365    return skb_add_fact(SKB_SCHEMA_GENERIC_REDISTRIBUTOR, s->BaseAddress, s->Length);
366}
367
368static errval_t parse_entry_generic_translator(ACPI_MADT_GENERIC_TRANSLATOR *s)
369{
370    ACPI_DEBUG(SKB_SCHEMA_GENERIC_TRANSLATOR, s->TranslationId, s->BaseAddress);
371
372    return skb_add_fact(SKB_SCHEMA_GENERIC_TRANSLATOR, s->TranslationId,
373                       s->BaseAddress);
374}
375
376
377
378errval_t acpi_parse_madt(void)
379{
380    errval_t err;
381
382    ACPI_STATUS         as;
383    ACPI_TABLE_MADT     *madt;
384    ACPI_TABLE_HEADER   *ath;
385
386    // Get the ACPI APIC table (the MADT)
387    as = AcpiGetTable("APIC", 1, (ACPI_TABLE_HEADER **)&ath);
388
389    if(ACPI_FAILURE(as)) {
390        ACPI_DEBUG("No MADT found in ACPI! Cannot initialize I/O APICs.\n");
391        return -1; // TODO: error value
392    }
393    else {
394        madt = (ACPI_TABLE_MADT*)ath;
395    }
396
397
398    ACPI_DEBUG("MADT Revision: %u, Size=%u, OEM=%s\n", madt->Header.Revision,
399               madt->Header.Length, madt->Header.OemId);
400
401    void *p = (void *)madt + sizeof(ACPI_TABLE_MADT);
402    void *table_end = (void *)madt + madt->Header.Length;
403
404    while(p < table_end) {
405        ACPI_SUBTABLE_HEADER *sh = (ACPI_SUBTABLE_HEADER *)p;
406
407        switch(sh->Type) {
408        case ACPI_MADT_TYPE_LOCAL_APIC :
409            err = parse_entry_local_apic((ACPI_MADT_LOCAL_APIC *)sh);
410            break;
411        case ACPI_MADT_TYPE_IO_APIC :
412            err = parse_entry_io_apic((ACPI_MADT_IO_APIC *)sh);
413            break;
414        case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE :
415            err = parse_entry_interrupt_override((ACPI_MADT_INTERRUPT_OVERRIDE *)sh);
416            break;
417        case ACPI_MADT_TYPE_NMI_SOURCE :
418            err = parse_entry_nmi_source((ACPI_MADT_NMI_SOURCE *)sh);
419            break;
420        case ACPI_MADT_TYPE_LOCAL_APIC_NMI :
421            err = parse_entry_local_apic_nmi((ACPI_MADT_LOCAL_APIC_NMI *)sh);
422            break;
423        case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE :
424            err = parse_entry_local_apic_override((ACPI_MADT_LOCAL_APIC_OVERRIDE *)sh);
425            break;
426        case ACPI_MADT_TYPE_IO_SAPIC :
427            err = parse_entry_io_sapic((ACPI_MADT_IO_SAPIC *)sh);
428            break;
429        case ACPI_MADT_TYPE_LOCAL_SAPIC :
430            err = parse_entry_local_sapic((ACPI_MADT_LOCAL_SAPIC *)sh);
431            break;
432        case ACPI_MADT_TYPE_INTERRUPT_SOURCE :
433            err = parse_entry_interrupt_source((ACPI_MADT_INTERRUPT_SOURCE *)sh);
434            break;
435        case ACPI_MADT_TYPE_LOCAL_X2APIC :
436            err = parse_entry_local_x2apic((ACPI_MADT_LOCAL_X2APIC *)sh);
437            break;
438        case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI :
439            err = parse_entry_local_x2apic_nmi((ACPI_MADT_LOCAL_X2APIC_NMI *)sh);
440            break;
441        case ACPI_MADT_TYPE_GENERIC_INTERRUPT :
442            err = parse_entry_generic_interrupt((ACPI_MADT_GENERIC_INTERRUPT *)sh);
443            break;
444        case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR :
445            err = parse_entry_generic_distributor((ACPI_MADT_GENERIC_DISTRIBUTOR *)sh);
446            break;
447        case ACPI_MADT_TYPE_GENERIC_MSI_FRAME :
448            err = parse_entry_generic_msi_frame((ACPI_MADT_GENERIC_MSI_FRAME *)sh);
449            break;
450        case ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR :
451            err = parse_entry_generic_redistributor((ACPI_MADT_GENERIC_REDISTRIBUTOR *)sh);
452            break;
453        case ACPI_MADT_TYPE_GENERIC_TRANSLATOR :
454            err = parse_entry_generic_translator((ACPI_MADT_GENERIC_TRANSLATOR *)sh);
455            break;
456        default:
457            /* reserved */
458            err = SYS_ERR_OK;
459        }
460
461        if (err_is_fail(err)) {
462            DEBUG_ERR(err, "failed to parse the entry. continuing\n");
463        }
464
465        assert(sh->Length);
466        p += sh->Length;
467    }
468
469    return SYS_ERR_OK;
470}
471