Accessing PCI Device Configuration Space

Some operations on a peripheral component interconnect (PCI) device are reserved for the device's function driver. Such operations include, for example, accessing the device-specific configuration space of a bus and programming a direct memory access (DMA) controller. Microsoft provides system support for accessing the configuration space of PCI devices by two methods:

The Windows XP and Windows Server 2003 and later operating systems have exclusive control over the configuration space header, as defined by the PCI Local Bus specification, as well as all of the capabilities in the capabilities linked list. Drivers must not attempt to modify these registers.

However, drivers can write to the configuration space that does not belong to the header or the capabilities list that is vendor-defined, using the IRP_MN_WRITE_CONFIG request or the SetBusData method of BUS_INTERFACE_STANDARD. Drivers can also read a device's capabilities, using the IRP_MN_READ_CONFIG request or the GetBusData method of BUS_INTERFACE_STANDARD. To use IRP_MN_READ_CONFIG or IRP_MN_WRITE_CONFIG, drivers must be running at PASSIVE_LEVEL. For a list of capabilities and the corresponding structures that drivers can query for, see the PCI Structures section.

Drivers can read from the extended PCI device configuration space (that is, more than 256 bytes of configuration data) using the IRP_MN_READ_CONFIG request or the GetBusData method of BUS_INTERFACE_STANDARD. Likewise, drivers can write to the extended PCI device configuration space using the IRP_MN_WRITE_CONFIG request or the SetBusData method of BUS_INTERFACE_STANDARD. If a device does not have an extended configuration space or the platform does not define a path for an extended configuration space on a device, the read requests will return 0xFFFF and the write requests will have no effect. To determine if the operation succeeded, drivers can examine the number of bytes read or written.

PCI Express and PCI-X mode 2 support an extended PCI device configuration space of greater than 256 bytes. Drivers can read and write to this configuration space, but only with the appropriate hardware and BIOS support. Within the ACPI BIOS, the root bus must have a PNP ID of either PNP0A08 or PNP0A03. For root buses with PNP ID of PNP0A03, the _DSM method with function 4 should indicate that the current mode is PCI-X mode 2. All the bridges and devices should either be PCI express or operate in PCI-X mode 2.

In addition, the system should support memory-mapped configuration space accesses. This is by defining an MCFG table in the system BIOS/firmware. Windows Vista and Windows Server 2008 and later operating systems automatically support memory-mapped configuration space accesses.

The following code example shows how to query for the power management capability data of a device:

#define LSZ sizeof(ULONG)

 // The PCI_COMMON_CONFIG structure includes 
// device specific data. The following
// structure is used to retrieve the
// 64 bytes of data that precedes the
// device-specific data.

typedef struct {
    ULONG  Reserved[HEADERSIZE];

// declare power management capabilities header
 PCI_PM_CAPABILITY  PowerMgmtCapability;
PCI_PM_CAPABILITY  *pPowerMgmtCapability = &Capability; 
UCHAR CapabilityOffset;

// Read the first part of the header
// to get the status register and
// the capabilities pointer.
// The "capabilities pointer" is
// actually an offset from the
// beginning of the header to a
// linked list of capabilities.
    pPciConfig, // output buffer
    0, // offset of the capability to read
 sizeof(PCI_COMMON_HEADER)); // just 64 bytes

// Check the Status register to see if 
// this device supports capability lists.

if ((pPciConfig->Status &
   // does not support capabilities list

// The device supports capability lists.
// Find the capabilities.

// The position of the capabilities pointer
// in the header depends on whether this is 
// a bridge type device. Check the type.

if ((pPciConfig->HeaderType & 
   CapabilityOffset = 
} else if ((pPciConfig->HeaderType & 
   CapabilityOffset = 
} else {
   CapabilityOffset = 

// Loop through the capabilities in search
// of the power management capability. The
// list is NULL-terminated, so the last 
// offset will always be zero.

while (CapabilityOffset != 0) {

    // Read the header of the capability at 
    // this offset.

    // If the retrieved capability is not
    // the power management capability that
    // we are looking for, follow the
    // link to the next capability and
    // continue looping.


    if (Capability->Header.CapabilityID ==
        // Found the power management capability
    } else {
        // This is some other capability.
        // Keep looking for the power 
        // management capability.
        CapabilityOffset = Capability->Header.Next;

if (CapabilityOffset == 0) {
    // We didn't find a power management
    // capability. Return an error.

// Skip past the capabilities header and read
// the rest of the power management capability

   // write to location immediately following header
   & (pPowerMgmtCapability->Header) + 1, 
   CapabilityOffset + 
   sizeof(PCI_PM_CAPABILITY) -