1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <assert.h>
6#include <limits.h>
7#include <stdint.h>
8#include <stdio.h>
9
10#include <acpica/acpi.h>
11
12static inline void do_indent(unsigned int level) {
13    while (level) {
14        printf("  ");
15        level--;
16    }
17}
18
19#define INDENT_PRINTF(...)   \
20    do {                     \
21        do_indent(level);    \
22        printf(__VA_ARGS__); \
23    } while (0)
24
25enum print_resource_request {
26    CURRENT_RESOURCES,
27    POSSIBLE_RESOURCES,
28};
29
30static ACPI_STATUS acpi_print_resources(
31    ACPI_HANDLE object,
32    unsigned int level,
33    enum print_resource_request type) {
34    ACPI_BUFFER buffer = {
35        .Length = ACPI_ALLOCATE_BUFFER,
36        .Pointer = NULL,
37    };
38    ACPI_STATUS status = AE_BAD_PARAMETER;
39    if (type == POSSIBLE_RESOURCES) {
40        status = AcpiGetPossibleResources(object, &buffer);
41    } else if (type == CURRENT_RESOURCES) {
42        status = AcpiGetCurrentResources(object, &buffer);
43    } else {
44        printf("Invalid resource type to print\n");
45        return AE_BAD_PARAMETER;
46        ;
47    }
48
49    if (status != AE_OK) {
50        if (buffer.Pointer) {
51            AcpiOsFree(buffer.Pointer);
52        }
53        return status;
54    }
55    if (type == POSSIBLE_RESOURCES) {
56        INDENT_PRINTF("PRS:\n");
57    } else if (type == CURRENT_RESOURCES) {
58        INDENT_PRINTF("CRS:\n");
59    }
60
61    uintptr_t entry_addr = (uintptr_t)buffer.Pointer;
62    ACPI_RESOURCE* res = (ACPI_RESOURCE*)entry_addr;
63    level += 1;
64    while (res->Type != ACPI_RESOURCE_TYPE_END_TAG) {
65        INDENT_PRINTF("Entry: ");
66        level += 1;
67        switch (res->Type) {
68        case ACPI_RESOURCE_TYPE_IO: {
69            printf("IO\n");
70            ACPI_RESOURCE_IO* io = &res->Data.Io;
71            INDENT_PRINTF("io_decode: %d\n", io->IoDecode);
72            INDENT_PRINTF("alignment: %d\n", io->Alignment);
73            INDENT_PRINTF("addrlen: %d\n", io->AddressLength);
74            INDENT_PRINTF("address min: %#04x\n", io->Minimum);
75            INDENT_PRINTF("address max: %#04x\n", io->Maximum);
76            break;
77        }
78        case ACPI_RESOURCE_TYPE_ADDRESS16: {
79            printf("Address16\n");
80            ACPI_RESOURCE_ADDRESS16* a16 = &res->Data.Address16;
81            INDENT_PRINTF("res_type: %d\n", a16->ResourceType);
82            INDENT_PRINTF("produce_consume: %d\n", a16->ProducerConsumer);
83            INDENT_PRINTF("decode: %d\n", a16->Decode);
84            INDENT_PRINTF("min_addr_fixed: %d\n", a16->MinAddressFixed);
85            INDENT_PRINTF("max_addr_fixed: %d\n", a16->MaxAddressFixed);
86            INDENT_PRINTF("address granularity: %#04x\n", a16->Address.Granularity);
87            INDENT_PRINTF("address min: %#04x\n", a16->Address.Minimum);
88            INDENT_PRINTF("address max: %#04x\n", a16->Address.Maximum);
89            INDENT_PRINTF("address xlat offset: %#04x\n", a16->Address.TranslationOffset);
90            INDENT_PRINTF("address len: %#04x\n", a16->Address.AddressLength);
91            // TODO: extract MTRR info from a16->Info
92            break;
93        }
94        case ACPI_RESOURCE_TYPE_ADDRESS32: {
95            printf("Address32\n");
96            ACPI_RESOURCE_ADDRESS32* a32 = &res->Data.Address32;
97            INDENT_PRINTF("res_type: %d\n", a32->ResourceType);
98            INDENT_PRINTF("produce_consume: %d\n", a32->ProducerConsumer);
99            INDENT_PRINTF("decode: %d\n", a32->Decode);
100            INDENT_PRINTF("min_addr_fixed: %d\n", a32->MinAddressFixed);
101            INDENT_PRINTF("max_addr_fixed: %d\n", a32->MaxAddressFixed);
102            INDENT_PRINTF("address granularity: %#08x\n", a32->Address.Granularity);
103            INDENT_PRINTF("address min: %#08x\n", a32->Address.Minimum);
104            INDENT_PRINTF("address max: %#08x\n", a32->Address.Maximum);
105            INDENT_PRINTF("address xlat offset: %#08x\n", a32->Address.TranslationOffset);
106            INDENT_PRINTF("address len: %#08x\n", a32->Address.AddressLength);
107            // TODO: extract MTRR info from a32->Info
108            break;
109        }
110        case ACPI_RESOURCE_TYPE_IRQ: {
111            printf("IRQ\n");
112            ACPI_RESOURCE_IRQ* irq = &res->Data.Irq;
113            INDENT_PRINTF("trigger: %s\n", irq->Triggering == ACPI_EDGE_SENSITIVE ? "edge" : "level");
114            const char* pol = "invalid";
115            switch (irq->Polarity) {
116            case ACPI_ACTIVE_BOTH:
117                pol = "both";
118                break;
119            case ACPI_ACTIVE_LOW:
120                pol = "low";
121                break;
122            case ACPI_ACTIVE_HIGH:
123                pol = "high";
124                break;
125            }
126            INDENT_PRINTF("polarity: %s\n", pol);
127            INDENT_PRINTF("sharable: %d\n", irq->Sharable);
128            INDENT_PRINTF("wake_cap: %d\n", irq->WakeCapable);
129            for (unsigned int i = 0; i < irq->InterruptCount; ++i) {
130                INDENT_PRINTF("irq #%d: %d\n", i, irq->Interrupts[i]);
131            }
132            break;
133        }
134        case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: {
135            printf("Extended IRQ\n");
136            ACPI_RESOURCE_EXTENDED_IRQ* irq = &res->Data.ExtendedIrq;
137            INDENT_PRINTF("produce_consume: %d\n", irq->ProducerConsumer);
138            INDENT_PRINTF("trigger: %s\n", irq->Triggering == ACPI_EDGE_SENSITIVE ? "edge" : "level");
139            const char* pol = "invalid";
140            switch (irq->Polarity) {
141            case ACPI_ACTIVE_BOTH:
142                pol = "both";
143                break;
144            case ACPI_ACTIVE_LOW:
145                pol = "low";
146                break;
147            case ACPI_ACTIVE_HIGH:
148                pol = "high";
149                break;
150            }
151            INDENT_PRINTF("polarity: %s\n", pol);
152            INDENT_PRINTF("sharable: %d\n", irq->Sharable);
153            INDENT_PRINTF("wake_cap: %d\n", irq->WakeCapable);
154            for (unsigned int i = 0; i < irq->InterruptCount; ++i) {
155                INDENT_PRINTF("irq #%d: %d\n", i, irq->Interrupts[i]);
156            }
157            break;
158        }
159        default:
160            printf("Unknown (type %u)\n", res->Type);
161        }
162        level -= 1;
163
164        entry_addr += res->Length;
165        res = (ACPI_RESOURCE*)entry_addr;
166    }
167    level -= 1;
168
169    AcpiOsFree(buffer.Pointer);
170    return AE_OK;
171}
172
173static ACPI_STATUS acpi_get_pcie_devices_crs(
174    ACPI_HANDLE object,
175    UINT32 nesting_level,
176    void* context,
177    void** ret) {
178    printf("Found object %p\n", object);
179    return acpi_print_resources(object, 1, CURRENT_RESOURCES);
180}
181
182static void acpi_debug_pcie_crs(void) {
183    ACPI_STATUS status = AcpiGetDevices(
184        (char*)"PNP0A08",
185        acpi_get_pcie_devices_crs,
186        NULL,
187        NULL);
188    if (status != AE_OK) {
189        printf("Could not find PCIe root complex\n");
190    }
191}
192
193static ACPI_STATUS acpi_print_prt(unsigned int level, ACPI_HANDLE object) {
194    ACPI_STATUS status = AE_OK;
195
196    ACPI_BUFFER buffer = {
197        // Request that the ACPI subsystem allocate the buffer
198        .Length = ACPI_ALLOCATE_BUFFER,
199        .Pointer = NULL,
200    };
201    status = AcpiGetIrqRoutingTable(object, &buffer);
202    if (status != AE_OK) {
203        if (buffer.Pointer) {
204            AcpiOsFree(buffer.Pointer);
205        }
206        return status;
207    }
208    assert(buffer.Pointer);
209
210    uintptr_t entry_addr = (uintptr_t)buffer.Pointer;
211    ACPI_PCI_ROUTING_TABLE* entry;
212    for (entry = (ACPI_PCI_ROUTING_TABLE*)entry_addr;
213         entry->Length != 0;
214         entry_addr += entry->Length, entry = (ACPI_PCI_ROUTING_TABLE*)entry_addr) {
215
216        assert(entry_addr <= (uintptr_t)buffer.Pointer + buffer.Length);
217
218        INDENT_PRINTF("Entry:\n");
219        level += 1;
220        if (entry->Pin > 3) {
221            INDENT_PRINTF("Pin: Invalid (%08x)\n", entry->Pin);
222        } else {
223            INDENT_PRINTF("Pin: INT%c\n", 'A' + entry->Pin);
224        }
225        INDENT_PRINTF("Address: %#016llx\n", entry->Address);
226        level += 1;
227        INDENT_PRINTF("Dev ID: %#04x\n", (uint16_t)(entry->Address >> 16));
228        level -= 1;
229
230        if (entry->Source[0]) {
231            // If the Source is not just a NULL byte, then it refers to a
232            // PCI Interrupt Link Device
233            INDENT_PRINTF("Source: %s\n", entry->Source);
234            INDENT_PRINTF("Source Index: %u\n", entry->SourceIndex);
235            ACPI_HANDLE ild;
236            status = AcpiGetHandle(object, entry->Source, &ild);
237            if (status != AE_OK) {
238                INDENT_PRINTF("Could not lookup Interrupt Link Device\n");
239                continue;
240            }
241            status = acpi_print_resources(ild, 2, CURRENT_RESOURCES);
242            if (status != AE_OK) {
243                INDENT_PRINTF("Could not lookup ILD CRS\n");
244            }
245            status = acpi_print_resources(ild, 2, POSSIBLE_RESOURCES);
246            if (status != AE_OK) {
247                INDENT_PRINTF("Could not lookup ILD PRS\n");
248            }
249        } else {
250            // Otherwise, it just refers to a global IRQ number that the pin
251            // is connected to
252            INDENT_PRINTF("GlobalIRQ: %u\n", entry->SourceIndex);
253        }
254        level -= 1;
255    }
256
257    AcpiOsFree(buffer.Pointer);
258    return AE_OK;
259}
260
261static ACPI_STATUS acpi_get_pcie_devices_irq(
262    ACPI_HANDLE object,
263    UINT32 nesting_level,
264    void* context,
265    void** ret) {
266    ACPI_STATUS status = acpi_print_prt(nesting_level, object);
267    if (status != AE_OK) {
268        printf("Failed to print PRT for root complex\n");
269        return status;
270    }
271
272    // Enumerate root ports
273    ACPI_HANDLE child = NULL;
274    while (1) {
275        status = AcpiGetNextObject(ACPI_TYPE_DEVICE, object, child, &child);
276        if (status == AE_NOT_FOUND) {
277            break;
278        } else if (status != AE_OK) {
279            printf("Failed to get next child object of root complex\n");
280            return status;
281        }
282
283        ACPI_OBJECT object = {0};
284        ACPI_BUFFER buffer = {
285            .Length = sizeof(object),
286            .Pointer = &object,
287        };
288        status = AcpiEvaluateObject(child, (char*)"_ADR", NULL, &buffer);
289        if (status != AE_OK ||
290            buffer.Length < sizeof(object) ||
291            object.Type != ACPI_TYPE_INTEGER) {
292
293            continue;
294        }
295        UINT64 data = object.Integer.Value;
296        unsigned int level = nesting_level;
297        INDENT_PRINTF(
298            "Device %#02x Function %#01x:\n",
299            (uint8_t)(data >> 16),
300            (uint8_t)(data & 0x7));
301        status = acpi_print_prt(nesting_level + 1, child);
302        if (status != AE_OK) {
303            continue;
304        }
305    }
306
307    return AE_OK;
308}
309
310static void acpi_debug_pcie_irq_routing(void) {
311    ACPI_STATUS status = AcpiGetDevices(
312        (char*)"PNP0A08",
313        acpi_get_pcie_devices_irq,
314        NULL,
315        NULL);
316    if (status != AE_OK) {
317        printf("Could not enumerate PRTs\n");
318    }
319}
320
321static ACPI_STATUS acpi_debug_print_device_name(
322    ACPI_HANDLE object,
323    UINT32 nesting_level,
324    void* context,
325    void** ret) {
326    ACPI_DEVICE_INFO* info = NULL;
327    ACPI_STATUS status = AcpiGetObjectInfo(object, &info);
328    if (status != AE_OK) {
329        if (info) {
330            ACPI_FREE(info);
331        }
332        return status;
333    }
334
335    unsigned int level = nesting_level;
336    INDENT_PRINTF("%4s\n", (char*)&info->Name);
337
338    ACPI_FREE(info);
339    return AE_OK;
340}
341
342static void acpi_debug_walk_ns(void) {
343    ACPI_STATUS status = AcpiWalkNamespace(
344        ACPI_TYPE_DEVICE,
345        ACPI_ROOT_OBJECT,
346        INT_MAX,
347        acpi_debug_print_device_name,
348        NULL,
349        NULL,
350        NULL);
351    if (status != AE_OK) {
352        printf("Failed to walk namespace\n");
353    }
354}
355