| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <assert.h> |
| #include <limits.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include <acpica/acpi.h> |
| |
| static inline void do_indent(unsigned int level) { |
| while (level) { |
| printf(" "); |
| level--; |
| } |
| } |
| |
| #define INDENT_PRINTF(...) \ |
| do { \ |
| do_indent(level); \ |
| printf(__VA_ARGS__); \ |
| } while (0) |
| |
| enum print_resource_request { |
| CURRENT_RESOURCES, |
| POSSIBLE_RESOURCES, |
| }; |
| |
| static ACPI_STATUS acpi_print_resources( |
| ACPI_HANDLE object, |
| unsigned int level, |
| enum print_resource_request type) { |
| ACPI_BUFFER buffer = { |
| .Length = ACPI_ALLOCATE_BUFFER, |
| .Pointer = NULL, |
| }; |
| ACPI_STATUS status = AE_BAD_PARAMETER; |
| if (type == POSSIBLE_RESOURCES) { |
| status = AcpiGetPossibleResources(object, &buffer); |
| } else if (type == CURRENT_RESOURCES) { |
| status = AcpiGetCurrentResources(object, &buffer); |
| } else { |
| printf("Invalid resource type to print\n"); |
| return AE_BAD_PARAMETER; |
| ; |
| } |
| |
| if (status != AE_OK) { |
| if (buffer.Pointer) { |
| AcpiOsFree(buffer.Pointer); |
| } |
| return status; |
| } |
| if (type == POSSIBLE_RESOURCES) { |
| INDENT_PRINTF("PRS:\n"); |
| } else if (type == CURRENT_RESOURCES) { |
| INDENT_PRINTF("CRS:\n"); |
| } |
| |
| uintptr_t entry_addr = (uintptr_t)buffer.Pointer; |
| ACPI_RESOURCE* res = (ACPI_RESOURCE*)entry_addr; |
| level += 1; |
| while (res->Type != ACPI_RESOURCE_TYPE_END_TAG) { |
| INDENT_PRINTF("Entry: "); |
| level += 1; |
| switch (res->Type) { |
| case ACPI_RESOURCE_TYPE_IO: { |
| printf("IO\n"); |
| ACPI_RESOURCE_IO* io = &res->Data.Io; |
| INDENT_PRINTF("io_decode: %d\n", io->IoDecode); |
| INDENT_PRINTF("alignment: %d\n", io->Alignment); |
| INDENT_PRINTF("addrlen: %d\n", io->AddressLength); |
| INDENT_PRINTF("address min: %#04x\n", io->Minimum); |
| INDENT_PRINTF("address max: %#04x\n", io->Maximum); |
| break; |
| } |
| case ACPI_RESOURCE_TYPE_ADDRESS16: { |
| printf("Address16\n"); |
| ACPI_RESOURCE_ADDRESS16* a16 = &res->Data.Address16; |
| INDENT_PRINTF("res_type: %d\n", a16->ResourceType); |
| INDENT_PRINTF("produce_consume: %d\n", a16->ProducerConsumer); |
| INDENT_PRINTF("decode: %d\n", a16->Decode); |
| INDENT_PRINTF("min_addr_fixed: %d\n", a16->MinAddressFixed); |
| INDENT_PRINTF("max_addr_fixed: %d\n", a16->MaxAddressFixed); |
| INDENT_PRINTF("address granularity: %#04x\n", a16->Address.Granularity); |
| INDENT_PRINTF("address min: %#04x\n", a16->Address.Minimum); |
| INDENT_PRINTF("address max: %#04x\n", a16->Address.Maximum); |
| INDENT_PRINTF("address xlat offset: %#04x\n", a16->Address.TranslationOffset); |
| INDENT_PRINTF("address len: %#04x\n", a16->Address.AddressLength); |
| // TODO: extract MTRR info from a16->Info |
| break; |
| } |
| case ACPI_RESOURCE_TYPE_ADDRESS32: { |
| printf("Address32\n"); |
| ACPI_RESOURCE_ADDRESS32* a32 = &res->Data.Address32; |
| INDENT_PRINTF("res_type: %d\n", a32->ResourceType); |
| INDENT_PRINTF("produce_consume: %d\n", a32->ProducerConsumer); |
| INDENT_PRINTF("decode: %d\n", a32->Decode); |
| INDENT_PRINTF("min_addr_fixed: %d\n", a32->MinAddressFixed); |
| INDENT_PRINTF("max_addr_fixed: %d\n", a32->MaxAddressFixed); |
| INDENT_PRINTF("address granularity: %#08x\n", a32->Address.Granularity); |
| INDENT_PRINTF("address min: %#08x\n", a32->Address.Minimum); |
| INDENT_PRINTF("address max: %#08x\n", a32->Address.Maximum); |
| INDENT_PRINTF("address xlat offset: %#08x\n", a32->Address.TranslationOffset); |
| INDENT_PRINTF("address len: %#08x\n", a32->Address.AddressLength); |
| // TODO: extract MTRR info from a32->Info |
| break; |
| } |
| case ACPI_RESOURCE_TYPE_IRQ: { |
| printf("IRQ\n"); |
| ACPI_RESOURCE_IRQ* irq = &res->Data.Irq; |
| INDENT_PRINTF("trigger: %s\n", irq->Triggering == ACPI_EDGE_SENSITIVE ? "edge" : "level"); |
| const char* pol = "invalid"; |
| switch (irq->Polarity) { |
| case ACPI_ACTIVE_BOTH: |
| pol = "both"; |
| break; |
| case ACPI_ACTIVE_LOW: |
| pol = "low"; |
| break; |
| case ACPI_ACTIVE_HIGH: |
| pol = "high"; |
| break; |
| } |
| INDENT_PRINTF("polarity: %s\n", pol); |
| INDENT_PRINTF("sharable: %d\n", irq->Sharable); |
| INDENT_PRINTF("wake_cap: %d\n", irq->WakeCapable); |
| for (unsigned int i = 0; i < irq->InterruptCount; ++i) { |
| INDENT_PRINTF("irq #%d: %d\n", i, irq->Interrupts[i]); |
| } |
| break; |
| } |
| case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: { |
| printf("Extended IRQ\n"); |
| ACPI_RESOURCE_EXTENDED_IRQ* irq = &res->Data.ExtendedIrq; |
| INDENT_PRINTF("produce_consume: %d\n", irq->ProducerConsumer); |
| INDENT_PRINTF("trigger: %s\n", irq->Triggering == ACPI_EDGE_SENSITIVE ? "edge" : "level"); |
| const char* pol = "invalid"; |
| switch (irq->Polarity) { |
| case ACPI_ACTIVE_BOTH: |
| pol = "both"; |
| break; |
| case ACPI_ACTIVE_LOW: |
| pol = "low"; |
| break; |
| case ACPI_ACTIVE_HIGH: |
| pol = "high"; |
| break; |
| } |
| INDENT_PRINTF("polarity: %s\n", pol); |
| INDENT_PRINTF("sharable: %d\n", irq->Sharable); |
| INDENT_PRINTF("wake_cap: %d\n", irq->WakeCapable); |
| for (unsigned int i = 0; i < irq->InterruptCount; ++i) { |
| INDENT_PRINTF("irq #%d: %d\n", i, irq->Interrupts[i]); |
| } |
| break; |
| } |
| default: |
| printf("Unknown (type %u)\n", res->Type); |
| } |
| level -= 1; |
| |
| entry_addr += res->Length; |
| res = (ACPI_RESOURCE*)entry_addr; |
| } |
| level -= 1; |
| |
| AcpiOsFree(buffer.Pointer); |
| return AE_OK; |
| } |
| |
| static ACPI_STATUS acpi_get_pcie_devices_crs( |
| ACPI_HANDLE object, |
| UINT32 nesting_level, |
| void* context, |
| void** ret) { |
| printf("Found object %p\n", object); |
| return acpi_print_resources(object, 1, CURRENT_RESOURCES); |
| } |
| |
| static void acpi_debug_pcie_crs(void) { |
| ACPI_STATUS status = AcpiGetDevices( |
| (char*)"PNP0A08", |
| acpi_get_pcie_devices_crs, |
| NULL, |
| NULL); |
| if (status != AE_OK) { |
| printf("Could not find PCIe root complex\n"); |
| } |
| } |
| |
| static ACPI_STATUS acpi_print_prt(unsigned int level, ACPI_HANDLE object) { |
| ACPI_STATUS status = AE_OK; |
| |
| ACPI_BUFFER buffer = { |
| // Request that the ACPI subsystem allocate the buffer |
| .Length = ACPI_ALLOCATE_BUFFER, |
| .Pointer = NULL, |
| }; |
| status = AcpiGetIrqRoutingTable(object, &buffer); |
| if (status != AE_OK) { |
| if (buffer.Pointer) { |
| AcpiOsFree(buffer.Pointer); |
| } |
| return status; |
| } |
| assert(buffer.Pointer); |
| |
| uintptr_t entry_addr = (uintptr_t)buffer.Pointer; |
| ACPI_PCI_ROUTING_TABLE* entry; |
| for (entry = (ACPI_PCI_ROUTING_TABLE*)entry_addr; |
| entry->Length != 0; |
| entry_addr += entry->Length, entry = (ACPI_PCI_ROUTING_TABLE*)entry_addr) { |
| |
| assert(entry_addr <= (uintptr_t)buffer.Pointer + buffer.Length); |
| |
| INDENT_PRINTF("Entry:\n"); |
| level += 1; |
| if (entry->Pin > 3) { |
| INDENT_PRINTF("Pin: Invalid (%08x)\n", entry->Pin); |
| } else { |
| INDENT_PRINTF("Pin: INT%c\n", 'A' + entry->Pin); |
| } |
| INDENT_PRINTF("Address: %#016llx\n", entry->Address); |
| level += 1; |
| INDENT_PRINTF("Dev ID: %#04x\n", (uint16_t)(entry->Address >> 16)); |
| level -= 1; |
| |
| if (entry->Source[0]) { |
| // If the Source is not just a NULL byte, then it refers to a |
| // PCI Interrupt Link Device |
| INDENT_PRINTF("Source: %s\n", entry->Source); |
| INDENT_PRINTF("Source Index: %u\n", entry->SourceIndex); |
| ACPI_HANDLE ild; |
| status = AcpiGetHandle(object, entry->Source, &ild); |
| if (status != AE_OK) { |
| INDENT_PRINTF("Could not lookup Interrupt Link Device\n"); |
| continue; |
| } |
| status = acpi_print_resources(ild, 2, CURRENT_RESOURCES); |
| if (status != AE_OK) { |
| INDENT_PRINTF("Could not lookup ILD CRS\n"); |
| } |
| status = acpi_print_resources(ild, 2, POSSIBLE_RESOURCES); |
| if (status != AE_OK) { |
| INDENT_PRINTF("Could not lookup ILD PRS\n"); |
| } |
| } else { |
| // Otherwise, it just refers to a global IRQ number that the pin |
| // is connected to |
| INDENT_PRINTF("GlobalIRQ: %u\n", entry->SourceIndex); |
| } |
| level -= 1; |
| } |
| |
| AcpiOsFree(buffer.Pointer); |
| return AE_OK; |
| } |
| |
| static ACPI_STATUS acpi_get_pcie_devices_irq( |
| ACPI_HANDLE object, |
| UINT32 nesting_level, |
| void* context, |
| void** ret) { |
| ACPI_STATUS status = acpi_print_prt(nesting_level, object); |
| if (status != AE_OK) { |
| printf("Failed to print PRT for root complex\n"); |
| return status; |
| } |
| |
| // Enumerate root ports |
| ACPI_HANDLE child = NULL; |
| while (1) { |
| status = AcpiGetNextObject(ACPI_TYPE_DEVICE, object, child, &child); |
| if (status == AE_NOT_FOUND) { |
| break; |
| } else if (status != AE_OK) { |
| printf("Failed to get next child object of root complex\n"); |
| return status; |
| } |
| |
| ACPI_OBJECT object = {0}; |
| ACPI_BUFFER buffer = { |
| .Length = sizeof(object), |
| .Pointer = &object, |
| }; |
| status = AcpiEvaluateObject(child, (char*)"_ADR", NULL, &buffer); |
| if (status != AE_OK || |
| buffer.Length < sizeof(object) || |
| object.Type != ACPI_TYPE_INTEGER) { |
| |
| continue; |
| } |
| UINT64 data = object.Integer.Value; |
| unsigned int level = nesting_level; |
| INDENT_PRINTF( |
| "Device %#02x Function %#01x:\n", |
| (uint8_t)(data >> 16), |
| (uint8_t)(data & 0x7)); |
| status = acpi_print_prt(nesting_level + 1, child); |
| if (status != AE_OK) { |
| continue; |
| } |
| } |
| |
| return AE_OK; |
| } |
| |
| static void acpi_debug_pcie_irq_routing(void) { |
| ACPI_STATUS status = AcpiGetDevices( |
| (char*)"PNP0A08", |
| acpi_get_pcie_devices_irq, |
| NULL, |
| NULL); |
| if (status != AE_OK) { |
| printf("Could not enumerate PRTs\n"); |
| } |
| } |
| |
| static ACPI_STATUS acpi_debug_print_device_name( |
| ACPI_HANDLE object, |
| UINT32 nesting_level, |
| void* context, |
| void** ret) { |
| ACPI_DEVICE_INFO* info = NULL; |
| ACPI_STATUS status = AcpiGetObjectInfo(object, &info); |
| if (status != AE_OK) { |
| if (info) { |
| ACPI_FREE(info); |
| } |
| return status; |
| } |
| |
| unsigned int level = nesting_level; |
| INDENT_PRINTF("%4s\n", (char*)&info->Name); |
| |
| ACPI_FREE(info); |
| return AE_OK; |
| } |
| |
| static void acpi_debug_walk_ns(void) { |
| ACPI_STATUS status = AcpiWalkNamespace( |
| ACPI_TYPE_DEVICE, |
| ACPI_ROOT_OBJECT, |
| INT_MAX, |
| acpi_debug_print_device_name, |
| NULL, |
| NULL, |
| NULL); |
| if (status != AE_OK) { |
| printf("Failed to walk namespace\n"); |
| } |
| } |