Skip to content

PCI Capability Structure

opencode-agent[bot] edited this page May 10, 2026 · 1 revision

PCI Capability Structure

Optional extended features that PCI devices advertise and use, such as MSI interrupts, power management, and hot-swap.

Overview

PCI devices can implement optional capability structures that extend the base PCI feature set. These capabilities are exposed through a linked-list chain in the device's configuration space. Each capability has a unique ID, an offset to the next capability, and capability-specific registers. The PCI base specification defines several well-known capability IDs, and vendors can implement vendor-specific capabilities.

Capability discovery happens during PCI device enumeration. The configuration header's CAP_POINTER (offset 0x34, lower 8 bits, bit 4 = 1 indicates capabilities list present) points to the first capability. Subsequent capabilities are linked via the NEXT pointer. JNode's Capability.createCapability() factory method dispatches on the capability ID to instantiate the appropriate concrete subclass.

The capability structure enables a clean extension mechanism: base PCI provides the discovery and access infrastructure, while each capability defines its own register layout and behavior. This allows drivers to query which capabilities a device supports and configure them accordingly.

Key Components

Class / File Role
core/src/driver/org/jnode/driver/bus/pci/Capability.java Abstract base class for all capabilities; defines id, offset, device access, and factory method
core/src/driver/org/jnode/driver/bus/pci/MSICapability.java Message Signaled Interrupts capability
core/src/driver/org/jnode/driver/bus/pci/PMCapability.java Power Management capability (PCI PM)
core/src/driver/org/jnode/driver/bus/pci/AGPCapability.java Accelerated Graphics Port capability
core/src/driver/org/jnode/driver/bus/pci/VPDCapability.java Vital Product Data capability
core/src/driver/org/jnode/driver/bus/pci/SlotIDCapability.java Slot Identification capability
core/src/driver/org/jnode/driver/bus/pci/CompactHotSwapCapability.java CompactPCI HotSwap capability
core/src/driver/org/jnode/driver/bus/pci/PCIDevice.java PCI device; provides configuration space read/write via readConfigByte/Word/Dword

How It Works

Capability Chain Layout

The PCI capability chain lives in configuration space. Each capability is at least 4 bytes:

Offset Field Description
+0 CAP_LIST_ID Capability ID (0x01-0xFF)
+1 CAP_LIST_NEXT Offset to next capability (0x00 = end of list)
+2 CAP_FLAGS Capability-specific flags (16 bits)
Header (type 0 device) at 0x34: CAP_POINTER = 0xDC (capabilities list present)
Capability at 0xDC:
  +0x00: ID (e.g., 0x05 = MSI)
  +0x01: NEXT pointer
  +0x02: flags
  +0x04: capability-specific data...
Next capability at [NEXT]:
  +0x00: ID
  +0x01: NEXT
  +0x02: flags
  ...

Capability Enumeration in JNode

During PCI device initialization, PCIDeviceConfig reads the capabilities pointer and traverses the chain:

// Capability.java:93-97 — constructor reads the ID at the capability offset
Capability(PCIDevice device, int offset) {
    this.device = device;
    this.offset = offset;
    this.id = device.readConfigByte(offset + PCI_CAP_LIST_ID);
}

// Capability.java:107-126 — factory dispatches on capability ID
static final Capability createCapability(PCIDevice device, int offset) {
    final int id = device.readConfigByte(offset + PCI_CAP_LIST_ID);
    switch (id) {
        case Id.PM: return new PMCapability(device, offset);
        case Id.AGP: return new AGPCapability(device, offset);
        case Id.VPD: return new VPDCapability(device, offset);
        case Id.SLOTID: return new SlotIDCapability(device, offset);
        case Id.MSI: return new MSICapability(device, offset);
        case Id.CHSWP: return new CompactHotSwapCapability(device, offset);
        default: return new Capability(device, offset) {}; // generic
    }
}

MSICapability

Message Signaled Interrupts (MSI) allows a device to write a message (effectively a memory write) to trigger an interrupt rather than using a traditional wire-based IRQ line. This is advantageous because it supports multiple messages, enables more flexible interrupt routing (especially in multi-core systems), and reduces hardware complexity.

JNode's MSICapability currently provides the base class structure. The capability ID is 0x05. MSI capability registers include address low, address high (for 64-bit), data (for multi-message), and control fields.

PMCapability

Power Management (PMCapability, ID 0x01) extends PCI devices with standardized power state control:

  • Capabilities register (offset +0x02, 16-bit): Specifies supported power management features (D0-D3 hot, D1/D2 support, aux current)
  • PMC register (offset +0x04, 16-bit): Power Management Capabilities
  • PMCSR register (offset +0x06, 16-bit): Power Management Control/Status — controls current power state
  • Data register (offset +0x07, 8-bit): Provides power consumption data for the current state
// PMCapability.java — accessor methods read from capability-specific offsets
public final int getCapabilities() { return readConfigWord(0x02); }
public final int getStatus() { return readConfigWord(0x04); }
public final int getData() { return readConfigByte(0x07); }
public final int getBridgeSupportExtensions() { return readConfigByte(0x06); }

Other Capability Types

  • AGPCapability (ID 0x02): Graphics port capability for AGP video cards
  • VPDCapability (ID 0x03): Vital Product Data for device identification and configuration storage
  • SlotIDCapability (ID 0x04): Slot identification for add-in cards
  • CompactHotSwapCapability (ID 0x06): Hot-swap support for CompactPCI

Configuration Access Pattern

Capabilities use their parent PCIDevice's configuration space access methods with an offset shift to account for the capability's position:

// Capability.java:150-169 — device configuration access with offset translation
protected final int readConfigDword(int offset) {
    return device.readConfigDword(this.offset + offset);
}
protected final void writeConfigWord(int offset, int value) {
    device.writeConfigWord(this.offset + offset, value);
}
// ... readConfigByte, readConfigWord, writeConfigByte, writeConfigDword

The this.offset translation means capabilities work correctly regardless of their position in configuration space.

Gotchas & Non-Obvious Behavior

  • Capabilities are optional: Not all PCI devices have a capabilities list. The CAP_POINTER bit in the status register (bit 4) indicates whether the list exists. Attempting to traverse a non-existent list will read garbage.
  • Link order is device-specific: The linked list order does not indicate importance or priority. A device may place MSI before PM or vice versa.
  • Unknown capability IDs return a generic anonymous subclass: When createCapability() encounters an unknown ID, it returns an anonymous Capability subclass that provides the base capability structure without specialized behavior.
  • MSI requires platform support: MSI capability being present does not guarantee the platform APIC or OS can route MSI messages. The capability must be enabled explicitly.
  • Power management state transitions: PM capability state transitions (D0→D3) have implications for device availability. Transitioning to D3 may clear configuration.
  • 64-bit MSI requires address high: When enabling 64-bit MSI, both address low and address high registers must be written. 32-bit MSI uses only address low.
  • Multi-message MSI: The MSI control register contains a MME (Multi-Message Enable) field that controls how many message vectors are allocated (1, 2, 4, 8, 16, or 32).

Related Pages

Clone this wiki locally