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                                            &param, sizeof(param),
252                                            &param, &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                                            &param, 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                                            &param, 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                                            &param, sizeof(param),
322                                            &param, &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                                            &param, sizeof(param),
344                                            &param, &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