#include <assert.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitKeys.h>
enum {
kACPIMethodAddressSpaceRead = 0,
kACPIMethodAddressSpaceWrite = 1,
kACPIMethodDebuggerCommand = 2,
kACPIMethodCount
};
#pragma pack(1)
typedef UInt32 IOACPIAddressSpaceID;
enum {
kIOACPIAddressSpaceIDSystemMemory = 0,
kIOACPIAddressSpaceIDSystemIO = 1,
kIOACPIAddressSpaceIDPCIConfiguration = 2,
kIOACPIAddressSpaceIDEmbeddedController = 3,
kIOACPIAddressSpaceIDSMBus = 4
};
union IOACPIAddress {
UInt64 addr64;
struct {
unsigned int offset :16;
unsigned int function :3;
unsigned int device :5;
unsigned int bus :8;
unsigned int segment :16;
unsigned int reserved :16;
} pci;
};
typedef union IOACPIAddress IOACPIAddress;
#pragma pack()
enum {
kIOPCIConfigVendorID = 0x00,
kIOPCIConfigDeviceID = 0x02,
kIOPCIConfigCommand = 0x04,
kIOPCIConfigStatus = 0x06,
kIOPCIConfigRevisionID = 0x08,
kIOPCIConfigClassCode = 0x09,
kIOPCIConfigCacheLineSize = 0x0C,
kIOPCIConfigLatencyTimer = 0x0D,
kIOPCIConfigHeaderType = 0x0E,
kIOPCIConfigBIST = 0x0F,
kIOPCIConfigBaseAddress0 = 0x10,
kIOPCIConfigBaseAddress1 = 0x14,
kIOPCIConfigBaseAddress2 = 0x18,
kIOPCIConfigBaseAddress3 = 0x1C,
kIOPCIConfigBaseAddress4 = 0x20,
kIOPCIConfigBaseAddress5 = 0x24,
kIOPCIConfigCardBusCISPtr = 0x28,
kIOPCIConfigSubSystemVendorID = 0x2C,
kIOPCIConfigSubSystemID = 0x2E,
kIOPCIConfigExpansionROMBase = 0x30,
kIOPCIConfigCapabilitiesPtr = 0x34,
kIOPCIConfigInterruptLine = 0x3C,
kIOPCIConfigInterruptPin = 0x3D,
kIOPCIConfigMinimumGrant = 0x3E,
kIOPCIConfigMaximumLatency = 0x3F
};
enum {
kIOPCICapabilityIDOffset = 0x00,
kIOPCINextCapabilityOffset = 0x01,
kIOPCIPowerManagementCapability = 0x01,
kIOPCIAGPCapability = 0x02,
kIOPCIVitalProductDataCapability = 0x03,
kIOPCISlotIDCapability = 0x04,
kIOPCIMSICapability = 0x05,
kIOPCICPCIHotswapCapability = 0x06,
kIOPCIPCIXCapability = 0x07,
kIOPCILDTCapability = 0x08,
kIOPCIVendorSpecificCapability = 0x09,
kIOPCIDebugPortCapability = 0x0a,
kIOPCICPCIResourceControlCapability = 0x0b,
kIOPCIHotplugCapability = 0x0c,
kIOPCIAGP8Capability = 0x0e,
kIOPCISecureCapability = 0x0f,
kIOPCIPCIExpressCapability = 0x10,
kIOPCIMSIXCapability = 0x11,
kIOPCIExpressErrorReportingCapability = -1UL,
kIOPCIExpressVirtualChannelCapability = -2UL,
kIOPCIExpressDeviceSerialNumberCapability = -3UL,
kIOPCIExpressPowerBudgetCapability = -4UL
};
enum {
kIOPCIConfigSpace = 0,
kIOPCIIOSpace = 1,
kIOPCI32BitMemorySpace = 2,
kIOPCI64BitMemorySpace = 3
};
enum {
kIOPCICommandIOSpace = 0x0001,
kIOPCICommandMemorySpace = 0x0002,
kIOPCICommandBusMaster = 0x0004,
kIOPCICommandSpecialCycles = 0x0008,
kIOPCICommandMemWrInvalidate = 0x0010,
kIOPCICommandPaletteSnoop = 0x0020,
kIOPCICommandParityError = 0x0040,
kIOPCICommandAddressStepping = 0x0080,
kIOPCICommandSERR = 0x0100,
kIOPCICommandFastBack2Back = 0x0200,
kIOPCICommandInterruptDisable = 0x0400
};
enum {
kIOPCIStatusCapabilities = 0x0010,
kIOPCIStatusPCI66 = 0x0020,
kIOPCIStatusUDF = 0x0040,
kIOPCIStatusFastBack2Back = 0x0080,
kIOPCIStatusDevSel0 = 0x0000,
kIOPCIStatusDevSel1 = 0x0200,
kIOPCIStatusDevSel2 = 0x0400,
kIOPCIStatusDevSel3 = 0x0600,
kIOPCIStatusTargetAbortCapable = 0x0800,
kIOPCIStatusTargetAbortActive = 0x1000,
kIOPCIStatusMasterAbortActive = 0x2000,
kIOPCIStatusSERRActive = 0x4000,
kIOPCIStatusParityErrActive = 0x8000
};
enum
{
kPCIPMCPMESupportFromD3Cold = 0x8000,
kPCIPMCPMESupportFromD3Hot = 0x4000,
kPCIPMCPMESupportFromD2 = 0x2000,
kPCIPMCPMESupportFromD1 = 0x1000,
kPCIPMCPMESupportFromD0 = 0x0800,
kPCIPMCD2Support = 0x0400,
kPCIPMCD1Support = 0x0200,
kPCIPMCD3Support = 0x0001
};
enum
{
kPCIPMCSPMEStatus = 0x8000,
kPCIPMCSPMEEnable = 0x0100,
kPCIPMCSPowerStateMask = 0x0003,
kPCIPMCSPowerStateD3 = 0x0003,
kPCIPMCSPowerStateD2 = 0x0002,
kPCIPMCSPowerStateD1 = 0x0001,
kPCIPMCSPowerStateD0 = 0x0000,
kPCIPMCSDefaultEnableBits = (~(IOOptionBits)0)
};
union IOPCIAddressSpace {
UInt32 bits;
struct {
#if __BIG_ENDIAN__
unsigned int resv:4;
unsigned int registerNumExtended:4;
unsigned int busNum:8;
unsigned int deviceNum:5;
unsigned int functionNum:3;
unsigned int registerNum:8;
#elif __LITTLE_ENDIAN__
unsigned int registerNum:8;
unsigned int functionNum:3;
unsigned int deviceNum:5;
unsigned int busNum:8;
unsigned int registerNumExtended:4;
unsigned int resv:4;
#endif
} es;
};
typedef union IOPCIAddressSpace IOPCIAddressSpace;
enum {
kPCIHeaderType0 = 0,
kPCIHeaderType1 = 1,
kPCIHeaderType2 = 2
};
enum {
kPCI2PCIPrimaryBus = 0x18,
kPCI2PCISecondaryBus = 0x19,
kPCI2PCISubordinateBus = 0x1a,
kPCI2PCISecondaryLT = 0x1b,
kPCI2PCIIORange = 0x1c,
kPCI2PCIMemoryRange = 0x20,
kPCI2PCIPrefetchMemoryRange = 0x24,
kPCI2PCIPrefetchUpperBase = 0x28,
kPCI2PCIPrefetchUpperLimit = 0x2c,
kPCI2PCIUpperIORange = 0x30,
kPCI2PCIBridgeControl = 0x3e
};
struct AddressSpaceParam {
UInt64 value;
UInt32 spaceID;
IOACPIAddress address;
UInt32 bitWidth;
UInt32 bitOffset;
UInt32 options;
};
typedef struct AddressSpaceParam AddressSpaceParam;
static uint32_t configRead32(io_connect_t connect, uint32_t segment,
uint32_t bus, uint32_t device, uint32_t function,
uint32_t offset)
{
AddressSpaceParam param;
kern_return_t status;
param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration;
param.bitWidth = 32;
param.bitOffset = 0;
param.options = 0;
param.address.pci.offset = offset;
param.address.pci.function = function;
param.address.pci.device = device;
param.address.pci.bus = bus;
param.address.pci.segment = segment;
param.address.pci.reserved = 0;
param.value = -1ULL;
size_t outSize = sizeof(param);
status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceRead,
¶m, sizeof(param),
¶m, &outSize);
assert(kIOReturnSuccess == status);
return ((uint32_t) param.value);
}
static void configWrite32(io_connect_t connect, uint32_t segment,
uint32_t bus, uint32_t device, uint32_t function,
uint32_t offset,
uint32_t data)
{
AddressSpaceParam param;
kern_return_t status;
param.spaceID = kIOACPIAddressSpaceIDPCIConfiguration;
param.bitWidth = 32;
param.bitOffset = 0;
param.options = 0;
param.address.pci.offset = offset;
param.address.pci.function = function;
param.address.pci.device = device;
param.address.pci.bus = bus;
param.address.pci.segment = segment;
param.address.pci.reserved = 0;
param.value = data;
size_t outSize = 0;
status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceWrite,
¶m, sizeof(param),
NULL, &outSize);
assert(kIOReturnSuccess == status);
}
static void physWrite32(io_connect_t connect, uint64_t offset, uint32_t data)
{
AddressSpaceParam param;
kern_return_t status;
param.spaceID = kIOACPIAddressSpaceIDSystemMemory;
param.bitWidth = 32;
param.bitOffset = 0;
param.options = 0;
param.address.addr64 = offset;
param.value = data;
size_t outSize = 0;
status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceWrite,
¶m, sizeof(param),
NULL, &outSize);
assert(kIOReturnSuccess == status);
}
static uint32_t physRead32(io_connect_t connect, uint64_t offset)
{
AddressSpaceParam param;
kern_return_t status;
param.spaceID = kIOACPIAddressSpaceIDSystemMemory;
param.bitWidth = 32;
param.bitOffset = 0;
param.options = 0;
param.address.addr64 = offset;
param.value = -1ULL;
size_t outSize = sizeof(param);
status = IOConnectCallStructMethod(connect, kACPIMethodAddressSpaceRead,
¶m, sizeof(param),
¶m, &outSize);
assert(kIOReturnSuccess == status);
return ((uint32_t) param.value);
}
io_registry_entry_t lookService(uint32_t segment,
uint32_t bus, uint32_t device, uint32_t function)
{
kern_return_t status;
io_iterator_t iter;
io_service_t service;
IOPCIAddressSpace space;
space.bits = 0;
space.es.busNum = bus;
space.es.deviceNum = device;
space.es.functionNum = function;
status = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOPCIDevice"), &iter);
assert(kIOReturnSuccess == status);
while ((service = IOIteratorNext(iter)))
{
CFDataRef reg;
UInt32 bits;
reg = IORegistryEntryCreateCFProperty(service, CFSTR("reg"),
kCFAllocatorDefault, kNilOptions);
bits = 0;
if (reg)
{
if (CFDataGetTypeID() == CFGetTypeID(reg))
bits = ((UInt32 *)CFDataGetBytePtr(reg))[0];
CFRelease(reg);
}
if (bits == space.bits)
{
IOObjectRetain(service);
break;
}
}
IOObjectRelease(iter);
return (service);
}
static void dump( const uint8_t * bytes, size_t len )
{
int i;
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
for (i = 0; i < len; i++)
{
if( 0 == (i & 15))
printf("\n %02X:", i);
printf(" %02x", bytes[i]);
}
printf("\n");
}
static void
dumpDevice(io_connect_t connect, uint32_t segment,
uint32_t bus, uint32_t device, uint32_t fn,
uint32_t * maxBus, uint32_t * maxFn)
{
io_registry_entry_t service;
kern_return_t status;
io_name_t name;
uint32_t off;
uint32_t vendProd;
uint32_t vend;
uint32_t prod;
uint32_t headerType;
uint32_t priBusNum;
uint32_t secBusNum;
uint32_t subBusNum;
uint32_t data[256/sizeof(uint32_t)];
uint8_t *bytes = (uint8_t *)&data[0];
for(off = 0; off < 256; off += 4)
data[off >> 2] = configRead32(connect, segment, bus, device, fn, off);
vendProd = data[0];
vend = vendProd & 0xffff;
prod = vendProd >> 16;
printf("[%d, %d, %d] 0x%04x, 0x%04x - ", bus, device, fn, vend, prod);
service = lookService(segment, bus, device, fn);
if (service)
{
status = IORegistryEntryGetName(service, name);
assert(kIOReturnSuccess == status);
printf("\"%s\" - ", name);
IOObjectRelease(service);
}
headerType = bytes[kIOPCIConfigHeaderType];
if (maxFn && (0x80 & headerType))
*maxFn = 7;
headerType &= 0x7f;
if (!headerType)
{
printf("class: 0x%x, 0x%x, 0x%x\n",
bytes[kIOPCIConfigRevisionID + 3],
bytes[kIOPCIConfigRevisionID + 2],
bytes[kIOPCIConfigRevisionID + 1]);
}
else
{
priBusNum = bytes[kPCI2PCIPrimaryBus];
secBusNum = bytes[kPCI2PCISecondaryBus];
subBusNum = bytes[kPCI2PCISubordinateBus];
printf("bridge: [%d, %d, %d]\n", priBusNum, secBusNum, subBusNum);
if (maxBus && (subBusNum > *maxBus))
*maxBus = subBusNum;
}
dump(bytes, sizeof(data));
printf("\n");
}
int main(int argc, char **argv)
{
io_registry_entry_t service;
io_connect_t connect;
kern_return_t status;
service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("AppleACPIPlatformExpert"));
assert(service);
if (service)
{
status = IOServiceOpen(service, mach_task_self(), 0, &connect);
IOObjectRelease(service);
assert(kIOReturnSuccess == status);
}
uint32_t count = 0;
uint32_t segment = 0;
uint32_t maxBus = 0;
uint32_t bus, device, fn, maxFn;
uint32_t vendProd;
if (argc > 3)
{
bus = strtoul(argv[1], NULL, 0);
device = strtoul(argv[2], NULL, 0);
fn = strtoul(argv[3], NULL, 0);
dumpDevice(connect, segment, bus, device, fn, NULL, NULL);
count++;
if (argc > 5)
{
uint32_t offs;
uint32_t data;
offs = strtoul(argv[4], NULL, 0);
data = strtoul(argv[5], NULL, 0);
configWrite32(connect, segment, bus, device, fn, offs, data);
printf("wrote 0x%08x to [%d, %d, %d]:0x%X\n", data, bus, device, fn, offs);
}
}
else if (argc > 2)
{
uint64_t offs;
uint32_t data;
offs = strtoull(argv[1], NULL, 0);
data = strtoul(argv[2], NULL, 0);
physWrite32(connect, offs, data);
printf("wrote 0x%08x to 0x%llX\n", data, offs);
}
else if (argc > 1)
{
uint64_t offs;
uint32_t data;
offs = strtoull(argv[1], NULL, 0);
data = physRead32(connect, offs);
printf("read 0x%08x from 0x%llX\n", data, offs);
}
else for (bus = 0; bus <= maxBus; bus++)
{
for (device = 0; device < 32; device++)
{
maxFn = 0;
for (fn = 0; fn <= maxFn; fn++)
{
vendProd = configRead32(connect, segment, bus, device, fn, kIOPCIConfigVendorID);
if ((0xFFFFFFFF == vendProd) || !vendProd)
continue;
count++;
dumpDevice(connect, segment, bus, device, fn, &maxBus, &maxFn);
}
}
}
printf("total: %d\n", count);
exit(0);
}