// 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 #include #include #include #include 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"); } }