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