1/*
2cc tools/pcidump.c -o /tmp/pcidump -Wall -framework IOKit -framework CoreFoundation
3 */
4
5#include <assert.h>
6#include <CoreFoundation/CoreFoundation.h>
7#include <IOKit/IOKitLib.h>
8#include <IOKit/IOKitKeys.h>
9#include <IOKit/pci/IOPCIDevice.h>
10#include <IOKit/pci/IOPCIPrivate.h>
11
12static uint32_t configRead32(io_connect_t connect, uint32_t segment,
13                                uint32_t bus, uint32_t device, uint32_t function,
14                                uint32_t offset)
15{
16    IOPCIDiagnosticsParameters param;
17    kern_return_t              status;
18
19    param.spaceType = kIOPCIConfigSpace;
20    param.bitWidth  = 32;
21    param.options   = 0;
22
23    param.address.pci.offset   = offset;
24    param.address.pci.function = function;
25    param.address.pci.device   = device;
26    param.address.pci.bus      = bus;
27    param.address.pci.segment  = segment;
28    param.address.pci.reserved = 0;
29    param.value                = -1ULL;
30
31    size_t outSize = sizeof(param);
32    status = IOConnectCallStructMethod(connect, kIOPCIDiagnosticsMethodRead,
33                                       &param, sizeof(param),
34                                       &param, &outSize);
35    assert(kIOReturnSuccess == status);
36    return ((uint32_t) param.value);
37}
38
39
40static void configWrite32(io_connect_t connect, uint32_t segment,
41                                uint32_t bus, uint32_t device, uint32_t function,
42                                uint32_t offset,
43                                uint32_t data)
44{
45    IOPCIDiagnosticsParameters param;
46    kern_return_t              status;
47
48    param.spaceType = kIOPCIConfigSpace;
49    param.bitWidth  = 32;
50    param.options   = 0;
51
52    param.address.pci.offset   = offset;
53    param.address.pci.function = function;
54    param.address.pci.device   = device;
55    param.address.pci.bus      = bus;
56    param.address.pci.segment  = segment;
57    param.address.pci.reserved = 0;
58    param.value                = data;
59
60    size_t outSize = 0;
61    status = IOConnectCallStructMethod(connect, kIOPCIDiagnosticsMethodWrite,
62                                       &param, sizeof(param),
63                                       NULL, &outSize);
64    assert(kIOReturnSuccess == status);
65}
66
67static void physWrite32(io_connect_t connect, uint64_t offset, uint32_t data)
68{
69    IOPCIDiagnosticsParameters param;
70    kern_return_t              status;
71
72    param.spaceType = kIOPCI64BitMemorySpace;
73    param.bitWidth  = 32;
74    param.options   = 0;
75
76    param.address.addr64 = offset;
77    param.value          = data;
78
79    size_t outSize = 0;
80    status = IOConnectCallStructMethod(connect, kIOPCIDiagnosticsMethodWrite,
81                                       &param, sizeof(param),
82                                       NULL, &outSize);
83    assert(kIOReturnSuccess == status);
84}
85
86static uint32_t physRead32(io_connect_t connect, uint64_t offset)
87{
88    IOPCIDiagnosticsParameters param;
89    kern_return_t              status;
90
91    param.spaceType = kIOPCI64BitMemorySpace;
92    param.bitWidth  = 32;
93    param.options   = 0;
94
95    param.address.addr64 = offset;
96    param.value          = -1ULL;
97
98    size_t outSize = sizeof(param);
99    status = IOConnectCallStructMethod(connect, kIOPCIDiagnosticsMethodRead,
100                                            &param, sizeof(param),
101                                            &param, &outSize);
102    assert(kIOReturnSuccess == status);
103
104    return ((uint32_t) param.value);
105}
106
107static uint32_t ioRead32(io_connect_t connect, uint64_t offset)
108{
109    IOPCIDiagnosticsParameters param;
110    kern_return_t              status;
111
112    param.spaceType = kIOPCIIOSpace;
113    param.bitWidth  = 16;
114    param.options   = 0;
115
116    param.address.addr64 = offset;
117    param.value          = -1ULL;
118
119    size_t outSize = sizeof(param);
120    status = IOConnectCallStructMethod(connect, kIOPCIDiagnosticsMethodRead,
121                                            &param, sizeof(param),
122                                            &param, &outSize);
123    assert(kIOReturnSuccess == status);
124
125    return ((uint32_t) param.value);
126}
127
128io_registry_entry_t lookService(uint32_t segment,
129                                uint32_t bus, uint32_t device, uint32_t function)
130{
131    kern_return_t status;
132    io_iterator_t iter;
133    io_service_t service;
134
135    IOPCIAddressSpace space;
136    space.bits           = 0;
137    space.es.busNum      = bus;
138    space.es.deviceNum   = device;
139    space.es.functionNum = function;
140
141    status = IOServiceGetMatchingServices(kIOMasterPortDefault,
142                                            IOServiceMatching("IOPCIDevice"), &iter);
143    assert(kIOReturnSuccess == status);
144
145    while ((service = IOIteratorNext(iter)))
146    {
147        CFDataRef reg;
148        UInt32    bits;
149
150        reg = IORegistryEntryCreateCFProperty(service, CFSTR("reg"),
151                    kCFAllocatorDefault, kNilOptions);
152        bits = 0;
153
154        if (reg)
155        {
156            if (CFDataGetTypeID() == CFGetTypeID(reg))
157                bits = ((UInt32 *)CFDataGetBytePtr(reg))[0];
158            CFRelease(reg);
159        }
160        if (bits == space.bits)
161        {
162            IOObjectRetain(service);
163            break;
164        }
165    }
166    IOObjectRelease(iter);
167
168    return (service);
169}
170
171static void dump( const uint8_t * bytes, size_t len )
172{
173    int i;
174
175    printf("        0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F");
176    for (i = 0; i < len; i++)
177    {
178        if( 0 == (i & 15))
179            printf("\n    %02X:", i);
180        printf(" %02x", bytes[i]);
181    }
182    printf("\n");
183}
184
185static void
186dumpDevice(io_connect_t connect, uint32_t segment,
187                                uint32_t bus, uint32_t device, uint32_t fn,
188                                uint32_t * maxBus, uint32_t * maxFn)
189{
190    io_registry_entry_t service;
191    kern_return_t       status;
192    io_name_t           name;
193    uint64_t     		entryID;
194    uint32_t off;
195    uint32_t vendProd;
196    uint32_t vend;
197    uint32_t prod;
198    uint32_t headerType;
199    uint32_t priBusNum;
200    uint32_t secBusNum;
201    uint32_t subBusNum;
202    uint32_t data[256/sizeof(uint32_t)];
203    uint8_t *bytes = (uint8_t *)&data[0];
204
205    for(off = 0; off < 256; off += 4)
206        data[off >> 2] = configRead32(connect, segment, bus, device, fn, off);
207
208    vendProd = data[0];
209    vend = vendProd & 0xffff;
210    prod = vendProd >> 16;
211    printf("[%d, %d, %d] 0x%04x, 0x%04x - ", bus, device, fn, vend, prod);
212
213    service = lookService(segment, bus, device, fn);
214    if (service)
215    {
216        status = IORegistryEntryGetName(service, name);
217        assert(kIOReturnSuccess == status);
218        status = IORegistryEntryGetRegistryEntryID(service, &entryID);
219        assert(kIOReturnSuccess == status);
220        printf("\"%s\", 0x%qx - ", name, entryID);
221        IOObjectRelease(service);
222    }
223
224    headerType = bytes[kIOPCIConfigHeaderType];
225    if (maxFn && (0x80 & headerType))
226        *maxFn = 7;
227    headerType &= 0x7f;
228    if (!headerType)
229    {
230        // device dump
231        printf("class: 0x%x, 0x%x, 0x%x\n",
232                bytes[kIOPCIConfigRevisionID + 3],
233                bytes[kIOPCIConfigRevisionID + 2],
234                bytes[kIOPCIConfigRevisionID + 1]);
235    }
236    else
237    {
238        priBusNum = bytes[kPCI2PCIPrimaryBus];
239        secBusNum = bytes[kPCI2PCISecondaryBus];
240        subBusNum = bytes[kPCI2PCISubordinateBus];
241        printf("bridge: [%d, %d, %d]\n", priBusNum, secBusNum, subBusNum);
242        if (maxBus && (subBusNum > *maxBus))
243            *maxBus = subBusNum;
244    }
245
246    dump(bytes, sizeof(data));
247    printf("\n");
248}
249
250
251int main(int argc, char **argv)
252{
253    io_registry_entry_t    service;
254    io_connect_t           connect;
255    kern_return_t          status;
256
257    service = IOServiceGetMatchingService(kIOMasterPortDefault,
258                                            IOServiceMatching("IOPCIBridge"));
259    assert(service);
260    if (service)
261    {
262        status = IOServiceOpen(service, mach_task_self(), kIOPCIDiagnosticsClientType, &connect);
263        IOObjectRelease(service);
264        assert(kIOReturnSuccess == status);
265    }
266
267    uint32_t count = 0;
268    uint32_t segment = 0;
269    uint32_t maxBus = 0;
270    uint32_t bus, device, fn, maxFn;
271    uint32_t vendProd;
272
273    if (argc > 3)
274    {
275        bus    = strtoul(argv[1], NULL, 0);
276        device = strtoul(argv[2], NULL, 0);
277        fn     = strtoul(argv[3], NULL, 0);
278		if (argc == 4)
279		{
280            dumpDevice(connect, segment, bus, device, fn, NULL, NULL);
281    	    count++;
282		}
283        if (argc > 5)
284        {
285            uint32_t offs;
286            uint32_t data;
287            offs    = strtoul(argv[4], NULL, 0);
288            data = strtoul(argv[5], NULL, 0);
289            configWrite32(connect, segment, bus, device, fn, offs, data);
290            printf("wrote 0x%08x to [%d, %d, %d]:0x%X\n", data, bus, device, fn, offs);
291        }
292        else if (argc > 4)
293        {
294            uint32_t offs;
295            uint32_t data;
296            offs    = strtoul(argv[4], NULL, 0);
297            data = configRead32(connect, segment, bus, device, fn, offs);
298            printf("read 0x%08x from [%d, %d, %d]:0x%X\n", data, bus, device, fn, offs);
299        }
300    }
301    else if (argc > 2)
302    {
303        uint64_t offs;
304        uint32_t data;
305        offs = strtoull(argv[1], NULL, 0);
306        data = strtoul(argv[2], NULL, 0);
307        physWrite32(connect, offs, data);
308        printf("wrote 0x%08x to 0x%llX\n", data, offs);
309    }
310    else if (argc > 1)
311    {
312        uint64_t offs;
313        uint32_t data;
314        offs = strtoull(argv[1], NULL, 0);
315		if (true || (offs > 0x10000ULL))
316		{
317			data = physRead32(connect, offs);
318			printf("read 0x%08x from mem 0x%llX\n", data, offs);
319		}
320		else
321		{
322			data = ioRead32(connect, offs);
323			printf("read 0x%08x from i/o 0x%llX\n", data, offs);
324		}
325    }
326    else for (bus = 0; bus <= maxBus; bus++)
327    {
328        for (device = 0; device < 32; device++)
329        {
330            maxFn = 0;
331            for (fn = 0; fn <= maxFn; fn++)
332            {
333                vendProd = configRead32(connect, segment, bus, device, fn, kIOPCIConfigVendorID);
334                if ((0xFFFFFFFF == vendProd) || !vendProd)
335                    continue;
336                count++;
337                dumpDevice(connect, segment, bus, device, fn, &maxBus, &maxFn);
338            }
339        }
340    }
341
342    printf("total: %d\n", count);
343    exit(0);
344}
345