blob: 6363c7df59078996efd045556361c28137efccb0 [file] [log] [blame]
// 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->Shareable);
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->Shareable);
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");
}
}