1/* 2cc pcidump.c -o /tmp/pcidump -Wall -framework IOKit -framework CoreFoundation -arch i386 3 */ 4 5#include <assert.h> 6#include <CoreFoundation/CoreFoundation.h> 7#include <IOKit/IOKitLib.h> 8#include <IOKit/IOKitKeys.h> 9 10 11enum { 12 kACPIMethodAddressSpaceRead = 0, 13 kACPIMethodAddressSpaceWrite = 1, 14 kACPIMethodDebuggerCommand = 2, 15 kACPIMethodCount 16}; 17 18#pragma pack(1) 19 20typedef UInt32 IOACPIAddressSpaceID; 21 22enum { 23 kIOACPIAddressSpaceIDSystemMemory = 0, 24 kIOACPIAddressSpaceIDSystemIO = 1, 25 kIOACPIAddressSpaceIDPCIConfiguration = 2, 26 kIOACPIAddressSpaceIDEmbeddedController = 3, 27 kIOACPIAddressSpaceIDSMBus = 4 28}; 29 30/* 31 * 64-bit ACPI address 32 */ 33union IOACPIAddress { 34 UInt64 addr64; 35 struct { 36 unsigned int offset :16; 37 unsigned int function :3; 38 unsigned int device :5; 39 unsigned int bus :8; 40 unsigned int segment :16; 41 unsigned int reserved :16; 42 } pci; 43}; 44typedef union IOACPIAddress IOACPIAddress; 45 46#pragma pack() 47 48/* Definitions of PCI Config Registers */ 49enum { 50 kIOPCIConfigVendorID = 0x00, 51 kIOPCIConfigDeviceID = 0x02, 52 kIOPCIConfigCommand = 0x04, 53 kIOPCIConfigStatus = 0x06, 54 kIOPCIConfigRevisionID = 0x08, 55 kIOPCIConfigClassCode = 0x09, 56 kIOPCIConfigCacheLineSize = 0x0C, 57 kIOPCIConfigLatencyTimer = 0x0D, 58 kIOPCIConfigHeaderType = 0x0E, 59 kIOPCIConfigBIST = 0x0F, 60 kIOPCIConfigBaseAddress0 = 0x10, 61 kIOPCIConfigBaseAddress1 = 0x14, 62 kIOPCIConfigBaseAddress2 = 0x18, 63 kIOPCIConfigBaseAddress3 = 0x1C, 64 kIOPCIConfigBaseAddress4 = 0x20, 65 kIOPCIConfigBaseAddress5 = 0x24, 66 kIOPCIConfigCardBusCISPtr = 0x28, 67 kIOPCIConfigSubSystemVendorID = 0x2C, 68 kIOPCIConfigSubSystemID = 0x2E, 69 kIOPCIConfigExpansionROMBase = 0x30, 70 kIOPCIConfigCapabilitiesPtr = 0x34, 71 kIOPCIConfigInterruptLine = 0x3C, 72 kIOPCIConfigInterruptPin = 0x3D, 73 kIOPCIConfigMinimumGrant = 0x3E, 74 kIOPCIConfigMaximumLatency = 0x3F 75}; 76 77/* Definitions of Capabilities PCI Config Register */ 78enum { 79 kIOPCICapabilityIDOffset = 0x00, 80 kIOPCINextCapabilityOffset = 0x01, 81 82 kIOPCIPowerManagementCapability = 0x01, 83 kIOPCIAGPCapability = 0x02, 84 kIOPCIVitalProductDataCapability = 0x03, 85 kIOPCISlotIDCapability = 0x04, 86 kIOPCIMSICapability = 0x05, 87 kIOPCICPCIHotswapCapability = 0x06, 88 kIOPCIPCIXCapability = 0x07, 89 kIOPCILDTCapability = 0x08, 90 kIOPCIVendorSpecificCapability = 0x09, 91 kIOPCIDebugPortCapability = 0x0a, 92 kIOPCICPCIResourceControlCapability = 0x0b, 93 kIOPCIHotplugCapability = 0x0c, 94 kIOPCIAGP8Capability = 0x0e, 95 kIOPCISecureCapability = 0x0f, 96 kIOPCIPCIExpressCapability = 0x10, 97 kIOPCIMSIXCapability = 0x11, 98 99 kIOPCIExpressErrorReportingCapability = -1UL, 100 kIOPCIExpressVirtualChannelCapability = -2UL, 101 kIOPCIExpressDeviceSerialNumberCapability = -3UL, 102 kIOPCIExpressPowerBudgetCapability = -4UL 103}; 104 105/* Space definitions */ 106enum { 107 kIOPCIConfigSpace = 0, 108 kIOPCIIOSpace = 1, 109 kIOPCI32BitMemorySpace = 2, 110 kIOPCI64BitMemorySpace = 3 111}; 112 113/* Command register definitions */ 114enum { 115 kIOPCICommandIOSpace = 0x0001, 116 kIOPCICommandMemorySpace = 0x0002, 117 kIOPCICommandBusMaster = 0x0004, 118 kIOPCICommandSpecialCycles = 0x0008, 119 kIOPCICommandMemWrInvalidate = 0x0010, 120 kIOPCICommandPaletteSnoop = 0x0020, 121 kIOPCICommandParityError = 0x0040, 122 kIOPCICommandAddressStepping = 0x0080, 123 kIOPCICommandSERR = 0x0100, 124 kIOPCICommandFastBack2Back = 0x0200, 125 kIOPCICommandInterruptDisable = 0x0400 126}; 127 128/* Status register definitions */ 129enum { 130 kIOPCIStatusCapabilities = 0x0010, 131 kIOPCIStatusPCI66 = 0x0020, 132 kIOPCIStatusUDF = 0x0040, 133 kIOPCIStatusFastBack2Back = 0x0080, 134 kIOPCIStatusDevSel0 = 0x0000, 135 kIOPCIStatusDevSel1 = 0x0200, 136 kIOPCIStatusDevSel2 = 0x0400, 137 kIOPCIStatusDevSel3 = 0x0600, 138 kIOPCIStatusTargetAbortCapable = 0x0800, 139 kIOPCIStatusTargetAbortActive = 0x1000, 140 kIOPCIStatusMasterAbortActive = 0x2000, 141 kIOPCIStatusSERRActive = 0x4000, 142 kIOPCIStatusParityErrActive = 0x8000 143}; 144 145// constants which are part of the PCI Bus Power Management Spec. 146enum 147{ 148 // capabilities bits in the 16 bit capabilities register 149 kPCIPMCPMESupportFromD3Cold = 0x8000, 150 kPCIPMCPMESupportFromD3Hot = 0x4000, 151 kPCIPMCPMESupportFromD2 = 0x2000, 152 kPCIPMCPMESupportFromD1 = 0x1000, 153 kPCIPMCPMESupportFromD0 = 0x0800, 154 kPCIPMCD2Support = 0x0400, 155 kPCIPMCD1Support = 0x0200, 156 157 kPCIPMCD3Support = 0x0001 158}; 159 160enum 161{ 162 // bits in the power management control/status register 163 kPCIPMCSPMEStatus = 0x8000, 164 kPCIPMCSPMEEnable = 0x0100, 165 kPCIPMCSPowerStateMask = 0x0003, 166 kPCIPMCSPowerStateD3 = 0x0003, 167 kPCIPMCSPowerStateD2 = 0x0002, 168 kPCIPMCSPowerStateD1 = 0x0001, 169 kPCIPMCSPowerStateD0 = 0x0000, 170 171 kPCIPMCSDefaultEnableBits = (~(IOOptionBits)0) 172}; 173 174union IOPCIAddressSpace { 175 UInt32 bits; 176 struct { 177#if __BIG_ENDIAN__ 178 unsigned int resv:4; 179 unsigned int registerNumExtended:4; 180 unsigned int busNum:8; 181 unsigned int deviceNum:5; 182 unsigned int functionNum:3; 183 unsigned int registerNum:8; 184#elif __LITTLE_ENDIAN__ 185 unsigned int registerNum:8; 186 unsigned int functionNum:3; 187 unsigned int deviceNum:5; 188 unsigned int busNum:8; 189 unsigned int registerNumExtended:4; 190 unsigned int resv:4; 191#endif 192 } es; 193}; 194typedef union IOPCIAddressSpace IOPCIAddressSpace; 195 196 197enum { 198 kPCIHeaderType0 = 0, 199 kPCIHeaderType1 = 1, 200 kPCIHeaderType2 = 2 201}; 202 203enum { 204 kPCI2PCIPrimaryBus = 0x18, 205 kPCI2PCISecondaryBus = 0x19, 206 kPCI2PCISubordinateBus = 0x1a, 207 kPCI2PCISecondaryLT = 0x1b, 208 kPCI2PCIIORange = 0x1c, 209 kPCI2PCIMemoryRange = 0x20, 210 kPCI2PCIPrefetchMemoryRange = 0x24, 211 kPCI2PCIPrefetchUpperBase = 0x28, 212 kPCI2PCIPrefetchUpperLimit = 0x2c, 213 kPCI2PCIUpperIORange = 0x30, 214 kPCI2PCIBridgeControl = 0x3e 215}; 216 217 218 219struct AddressSpaceParam { 220 UInt64 value; 221 UInt32 spaceID; 222 IOACPIAddress address; 223 UInt32 bitWidth; 224 UInt32 bitOffset; 225 UInt32 options; 226}; 227typedef struct AddressSpaceParam AddressSpaceParam; 228 229static uint32_t configRead32(io_connect_t connect, uint32_t segment, 230 uint32_t bus, uint32_t device, uint32_t function, 231 uint32_t offset) 232{ 233 AddressSpaceParam param; 234 kern_return_t status; 235 236 param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration; 237 param.bitWidth = 32; 238 param.bitOffset = 0; 239 param.options = 0; 240 241 param.address.pci.offset = offset; 242 param.address.pci.function = function; 243 param.address.pci.device = device; 244 param.address.pci.bus = bus; 245 param.address.pci.segment = segment; 246 param.address.pci.reserved = 0; 247 param.value = -1ULL; 248 249 size_t outSize = sizeof(param); 250 status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceRead, 251 ¶m, sizeof(param), 252 ¶m, &outSize); 253 assert(kIOReturnSuccess == status); 254 return ((uint32_t) param.value); 255} 256 257 258static void configWrite32(io_connect_t connect, uint32_t segment, 259 uint32_t bus, uint32_t device, uint32_t function, 260 uint32_t offset, 261 uint32_t data) 262{ 263 AddressSpaceParam param; 264 kern_return_t status; 265 266 param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration; 267 param.bitWidth = 32; 268 param.bitOffset = 0; 269 param.options = 0; 270 271 param.address.pci.offset = offset; 272 param.address.pci.function = function; 273 param.address.pci.device = device; 274 param.address.pci.bus = bus; 275 param.address.pci.segment = segment; 276 param.address.pci.reserved = 0; 277 param.value = data; 278 279 size_t outSize = 0; 280 status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceWrite, 281 ¶m, sizeof(param), 282 NULL, &outSize); 283 assert(kIOReturnSuccess == status); 284} 285 286static void physWrite32(io_connect_t connect, uint64_t offset, uint32_t data) 287{ 288 AddressSpaceParam param; 289 kern_return_t status; 290 291 param.spaceID = kIOACPIAddressSpaceIDSystemMemory; 292 param.bitWidth = 32; 293 param.bitOffset = 0; 294 param.options = 0; 295 296 param.address.addr64 = offset; 297 param.value = data; 298 299 size_t outSize = 0; 300 status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceWrite, 301 ¶m, sizeof(param), 302 NULL, &outSize); 303 assert(kIOReturnSuccess == status); 304} 305 306static uint32_t physRead32(io_connect_t connect, uint64_t offset) 307{ 308 AddressSpaceParam param; 309 kern_return_t status; 310 311 param.spaceID = kIOACPIAddressSpaceIDSystemMemory; 312 param.bitWidth = 32; 313 param.bitOffset = 0; 314 param.options = 0; 315 316 param.address.addr64 = offset; 317 param.value = -1ULL; 318 319 size_t outSize = sizeof(param); 320 status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceRead, 321 ¶m, sizeof(param), 322 ¶m, &outSize); 323 assert(kIOReturnSuccess == status); 324 325 return ((uint32_t) param.value); 326} 327 328static uint32_t ioRead32(io_connect_t connect, uint64_t offset) 329{ 330 AddressSpaceParam param; 331 kern_return_t status; 332 333 param.spaceID = kIOACPIAddressSpaceIDSystemIO; 334 param.bitWidth = 16; 335 param.bitOffset = 0; 336 param.options = 0; 337 338 param.address.addr64 = offset; 339 param.value = -1ULL; 340 341 size_t outSize = sizeof(param); 342 status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceRead, 343 ¶m, sizeof(param), 344 ¶m, &outSize); 345 assert(kIOReturnSuccess == status); 346 347 return ((uint32_t) param.value); 348} 349 350io_registry_entry_t lookService(uint32_t segment, 351 uint32_t bus, uint32_t device, uint32_t function) 352{ 353 kern_return_t status; 354 io_iterator_t iter; 355 io_service_t service; 356 357 IOPCIAddressSpace space; 358 space.bits = 0; 359 space.es.busNum = bus; 360 space.es.deviceNum = device; 361 space.es.functionNum = function; 362 363 status = IOServiceGetMatchingServices(kIOMasterPortDefault, 364 IOServiceMatching("IOPCIDevice"), &iter); 365 assert(kIOReturnSuccess == status); 366 367 while ((service = IOIteratorNext(iter))) 368 { 369 CFDataRef reg; 370 UInt32 bits; 371 372 reg = IORegistryEntryCreateCFProperty(service, CFSTR("reg"), 373 kCFAllocatorDefault, kNilOptions); 374 bits = 0; 375 376 if (reg) 377 { 378 if (CFDataGetTypeID() == CFGetTypeID(reg)) 379 bits = ((UInt32 *)CFDataGetBytePtr(reg))[0]; 380 CFRelease(reg); 381 } 382 if (bits == space.bits) 383 { 384 IOObjectRetain(service); 385 break; 386 } 387 } 388 IOObjectRelease(iter); 389 390 return (service); 391} 392 393static void dump( const uint8_t * bytes, size_t len ) 394{ 395 int i; 396 397 printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); 398 for (i = 0; i < len; i++) 399 { 400 if( 0 == (i & 15)) 401 printf("\n %02X:", i); 402 printf(" %02x", bytes[i]); 403 } 404 printf("\n"); 405} 406 407static void 408dumpDevice(io_connect_t connect, uint32_t segment, 409 uint32_t bus, uint32_t device, uint32_t fn, 410 uint32_t * maxBus, uint32_t * maxFn) 411{ 412 io_registry_entry_t service; 413 kern_return_t status; 414 io_name_t name; 415 uint64_t entryID; 416 uint32_t off; 417 uint32_t vendProd; 418 uint32_t vend; 419 uint32_t prod; 420 uint32_t headerType; 421 uint32_t priBusNum; 422 uint32_t secBusNum; 423 uint32_t subBusNum; 424 uint32_t data[256/sizeof(uint32_t)]; 425 uint8_t *bytes = (uint8_t *)&data[0]; 426 427 for(off = 0; off < 256; off += 4) 428 data[off >> 2] = configRead32(connect, segment, bus, device, fn, off); 429 430 vendProd = data[0]; 431 vend = vendProd & 0xffff; 432 prod = vendProd >> 16; 433 printf("[%d, %d, %d] 0x%04x, 0x%04x - ", bus, device, fn, vend, prod); 434 435 service = lookService(segment, bus, device, fn); 436 if (service) 437 { 438 status = IORegistryEntryGetName(service, name); 439 assert(kIOReturnSuccess == status); 440 status = IORegistryEntryGetRegistryEntryID(service, &entryID); 441 assert(kIOReturnSuccess == status); 442 printf("\"%s\", 0x%qx - ", name, entryID); 443 IOObjectRelease(service); 444 } 445 446 headerType = bytes[kIOPCIConfigHeaderType]; 447 if (maxFn && (0x80 & headerType)) 448 *maxFn = 7; 449 headerType &= 0x7f; 450 if (!headerType) 451 { 452 // device dump 453 printf("class: 0x%x, 0x%x, 0x%x\n", 454 bytes[kIOPCIConfigRevisionID + 3], 455 bytes[kIOPCIConfigRevisionID + 2], 456 bytes[kIOPCIConfigRevisionID + 1]); 457 } 458 else 459 { 460 priBusNum = bytes[kPCI2PCIPrimaryBus]; 461 secBusNum = bytes[kPCI2PCISecondaryBus]; 462 subBusNum = bytes[kPCI2PCISubordinateBus]; 463 printf("bridge: [%d, %d, %d]\n", priBusNum, secBusNum, subBusNum); 464 if (maxBus && (subBusNum > *maxBus)) 465 *maxBus = subBusNum; 466 } 467 468 dump(bytes, sizeof(data)); 469 printf("\n"); 470} 471 472 473int main(int argc, char **argv) 474{ 475 io_registry_entry_t service; 476 io_connect_t connect; 477 kern_return_t status; 478 479 service = IOServiceGetMatchingService(kIOMasterPortDefault, 480 IOServiceMatching("AppleACPIPlatformExpert")); 481 assert(service); 482 if (service) 483 { 484 status = IOServiceOpen(service, mach_task_self(), 0, &connect); 485 IOObjectRelease(service); 486 assert(kIOReturnSuccess == status); 487 } 488 489 uint32_t count = 0; 490 uint32_t segment = 0; 491 uint32_t maxBus = 0; 492 uint32_t bus, device, fn, maxFn; 493 uint32_t vendProd; 494 495 if (argc > 3) 496 { 497 bus = strtoul(argv[1], NULL, 0); 498 device = strtoul(argv[2], NULL, 0); 499 fn = strtoul(argv[3], NULL, 0); 500 if (argc == 4) 501 { 502 dumpDevice(connect, segment, bus, device, fn, NULL, NULL); 503 count++; 504 } 505 if (argc > 5) 506 { 507 uint32_t offs; 508 uint32_t data; 509 offs = strtoul(argv[4], NULL, 0); 510 data = strtoul(argv[5], NULL, 0); 511 configWrite32(connect, segment, bus, device, fn, offs, data); 512 printf("wrote 0x%08x to [%d, %d, %d]:0x%X\n", data, bus, device, fn, offs); 513 } 514 else if (argc > 4) 515 { 516 uint32_t offs; 517 uint32_t data; 518 offs = strtoul(argv[4], NULL, 0); 519 data = configRead32(connect, segment, bus, device, fn, offs); 520 printf("read 0x%08x from [%d, %d, %d]:0x%X\n", data, bus, device, fn, offs); 521 } 522 } 523 else if (argc > 2) 524 { 525 uint64_t offs; 526 uint32_t data; 527 offs = strtoull(argv[1], NULL, 0); 528 data = strtoul(argv[2], NULL, 0); 529 physWrite32(connect, offs, data); 530 printf("wrote 0x%08x to 0x%llX\n", data, offs); 531 } 532 else if (argc > 1) 533 { 534 uint64_t offs; 535 uint32_t data; 536 offs = strtoull(argv[1], NULL, 0); 537 if (true || (offs > 0x10000ULL)) 538 { 539 data = physRead32(connect, offs); 540 printf("read 0x%08x from mem 0x%llX\n", data, offs); 541 } 542 else 543 { 544 data = ioRead32(connect, offs); 545 printf("read 0x%08x from i/o 0x%llX\n", data, offs); 546 } 547 } 548 else for (bus = 0; bus <= maxBus; bus++) 549 { 550 for (device = 0; device < 32; device++) 551 { 552 maxFn = 0; 553 for (fn = 0; fn <= maxFn; fn++) 554 { 555 vendProd = configRead32(connect, segment, bus, device, fn, kIOPCIConfigVendorID); 556 if ((0xFFFFFFFF == vendProd) || !vendProd) 557 continue; 558 count++; 559 dumpDevice(connect, segment, bus, device, fn, &maxBus, &maxFn); 560 } 561 } 562 } 563 564 printf("total: %d\n", count); 565 exit(0); 566} 567