User Tools

Site Tools


pci_configuration_space

This is an old revision of the document!


PCI Configuration Space

Jedes PCI-Gerät hat seinen eigenen PCI Configuration Space im Speicher. Mit pciconf -l können darüber Informationen zu PCI-Geräten ausgegeben werden. Hier die Ausgabe einer FreeBSD 16.0-CURRENT VM, die über einen VirtualBox Hypervisor auf macOS mit Apple Silicon läuft.

% pciconf -l
vgapci0@pci0:0:0:0:	class=0x030000 rev=0x00 hdr=0x00 vendor=0x15ad device=0x0406 subvendor=0x15ad subdevice=0x0406
none0@pci0:0:1:0:	class=0x088000 rev=0x00 hdr=0x00 vendor=0x80ee device=0xcafe subvendor=0x0000 subdevice=0x0000
none1@pci0:0:2:0:	class=0x040300 rev=0x01 hdr=0x00 vendor=0x8086 device=0x2668 subvendor=0x8384 subdevice=0x7680
pcib1@pci0:0:3:0:	class=0x060401 rev=0xf2 hdr=0x01 vendor=0x8086 device=0x2448 subvendor=0x0000 subdevice=0x0000
xhci0@pci0:0:6:0:	class=0x0c0330 rev=0x00 hdr=0x00 vendor=0x8086 device=0x1e31 subvendor=0x0000 subdevice=0x0000
em0@pci0:0:8:0:	class=0x020000 rev=0x02 hdr=0x00 vendor=0x8086 device=0x100e subvendor=0x8086 subdevice=0x001e
em1@pci0:0:9:0:	class=0x020000 rev=0x02 hdr=0x00 vendor=0x8086 device=0x100e subvendor=0x8086 subdevice=0x001e
virtio_pci0@pci0:0:10:0:	class=0x020000 rev=0x00 hdr=0x00 vendor=0x1af4 device=0x1000 subvendor=0x1af4 subdevice=0x0001
ahci0@pci0:0:16:0:	class=0x010601 rev=0x02 hdr=0x00 vendor=0x8086 device=0x2829 subvendor=0x0000 subdevice=0x0000

Hier betrachte ich den VirtIO Netzwerkadapter genauer.

Mit dem Parameter v gibt pciconf die Namen zu den IDs der class, device, usw. aus.

% pciconf -lv virtio_pci0@pci0:0:10:0
virtio_pci0@pci0:0:10:0:	class=0x020000 rev=0x00 hdr=0x00 vendor=0x1af4 device=0x1000 subvendor=0x1af4 subdevice=0x0001
    vendor     = 'Red Hat, Inc.'
    device     = 'Virtio network device'
    class      = network
    subclass   = ethernet

Mit dem Parameter r kann pciconf den PCI Configuration Space direkt ausgeben. Der Parameter b gibt an, dass der Configuration Space Byteweise ausgelesen werden soll und 0x0:0x3f gibt den Bereich an, der ausgelesen werden soll.

% sudo pciconf -rb virtio_pci0@pci0:0:10:0 0x0:0x3f
f4 1a 00 10 17 00 10 00  00 00 00 02 00 00 00 00 
01 00 00 00 00 00 00 00  00 60 85 e1 00 00 00 00 
00 00 00 00 00 00 00 00  00 00 00 00 f4 1a 01 00 
00 00 00 00 40 00 00 00  00 00 00 00 0c 01 00 00 

Der PCI Configuration Space enthält mehr, als pciconf bisher ausgegeben hat. Es nutzt das folgende Format.

   Bit 0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 Byte +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    0 |           Vendor ID           |            Device ID          |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 |            Command            |             Status            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |  Revision ID  |                  Class Code                   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   12 | Cache Line S. |  Lat. Timer   |  Header Type  |     BIST      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   16 |                             BAR 1                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   20 |                             BAR 2                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   24 |                             BAR 3                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   28 |                             BAR 4                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   32 |                             BAR 5                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   36 |                             BAR 6                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   40 |                       Cardbus CIS Pointer                     |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   44 |       Subsystem Vendor ID      |        Subsystem ID          |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   48 |                 Expansion ROM Base Address                    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   52 | Cap. Pointer  |                   Reserved                    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   56 |.                           Reserved                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   60 |Interrupt Line | Interrupt Pin |    Min Gnt.   |    Max Lat.   |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Die Bytes sind in Little Endian. Die ersten zwei Bytes sind also 0x1af4, die die Vendor ID angeben.

Interessant sind die Base Address Registers (BARs), über die weitere Informationen zum PCI-Gerät angegeben werden können. Das BAR selbst ist hauptsächlich die Base Adress, das ist die Adresse unter der sich die BAR Region im Speicher befindet. Darin kodiert ist z. B. der Typ des BAR. Es gibt Memory (0) und I/O (1).

Bei dem PCI-Gerät gibt es zwei BARs. Hier Configuration Space von eben der Teil zu den BARs.

   | BAR 1     |            | BAR 2     |
    01 00 00 00 00 00 00 00  00 60 85 e1 00 00 00 00 
    00 00 00 00 00 00 00 00

BAR 1 hat Typ 1 (I/O) und Base Address 0. BAR 2 hat Typ 0 (Memory) und Base Address 0xe1856000. Die BARs lassen sich mit pciconf mit dem Parameter b ausgeben.

% pciconf -lb virtio_pci0@pci0:0:10:0
virtio_pci0@pci0:0:10:0:	class=0x020000 rev=0x00 hdr=0x00 vendor=0x1af4 device=0x1000 subvendor=0x1af4 subdevice=0x0001
    bar   [10] = type I/O Port, range 32, base 0, size 32, enabled
    bar   [18] = type Memory, range 32, base 0xe1856000, size 8192, enabled

Die Zahl in eckigen Klammern gibt über ein Byte-Offset hexadezimal an wo im Configuration Space der BAR angegeben ist.

VirtIO nutzt im legacy mode den BAR vom Typ I/O und im modern mode den BAR vom Typ Memory. Das PCI-Gerät, was VirtualBox bereistellt ist ein Transitional Device. Das heißt, der unterstützt beide Modi. FreeBSD hat einen Treiber pro Modus (src/sys/dev/virtio/pci/virtio_pci_[legacy|modern].c). Es ruft jeweils die entsprechende device_probe Methode auf und trifft die Entscheidung anhand des Rückgabewertes.

Im Modern Mode zeigen die PCI Capabilities auf, wo sich im der BAR Region welche Informationen finden. Der Capabilites Pointer ist in dem PCI Configuration Space von oben auf 0x40 gesetzt. Das ist ein Pointer auf des erste Element in einer linked list von Capabilites. Die Capabtilites lassen sich mit pciconf über den Parameter c ausgeben.

% sudo pciconf -lc virtio_pci0@pci0:0:10:0
virtio_pci0@pci0:0:10:0:	class=0x020000 rev=0x00 hdr=0x00 vendor=0x1af4 device=0x1000 subvendor=0x1af4 subdevice=0x0001
    cap 09[40] = vendor (length 16)
    cap 09[50] = vendor (length 20)
    cap 09[64] = vendor (length 16)
    cap 09[74] = vendor (length 20)
    cap 09[88] = vendor (length 16)

Der erste Wert nach cap gibt die Capability ID an. Der Wert in eckigen Klammern gibt über ein Byte-Offset hexadezimal an wo im Configuration Space die Capability beschrieben steht. Die erste Capability ist also 0x40 = 64 Byte hinter dem Start des Configuration Space zu finden, also direkt hinter dem eben angegebenen Format. Die Bytes lassen sich ebenfalls mit pciconf -rb auslesen.

% sudo pciconf -rb virtio_pci0@pci0:0:10:0 0x40:0x97
09 50 10 01 02 00 00 00  00 00 00 00 38 00 00 00 
09 64 14 02 02 00 00 00  38 00 00 00 32 00 00 00 
02 00 00 00 09 74 10 03  02 00 00 00 6c 00 00 00 
01 00 00 00 09 88 14 05  02 00 00 00 00 00 00 00 
04 00 00 00 00 00 00 00  09 00 10 04 02 00 00 00 
70 00 00 00 0a 00 00 00

Das jeweils erste Byte gibt die Capability ID an und das zweite Byte den Offset zur nächsten Capability. Eine bessere Ausgabe zu den Capabilities bietet lspci, was sich über

% sudo pkg install pciutils

installieren lässt. Hier die Ausgabe zu dem PCI-Gerät von lspci.

% sudo lspci -vv
...
00:0a.0 Ethernet controller: Red Hat, Inc. Virtio network device
	Subsystem: Red Hat, Inc. Device 0001
	Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV+ VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
	Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
	Latency: 0
	Interrupt: pin A routed to IRQ 12
	Region 0: I/O ports at 0000
	Region 2: Memory at e1856000 (32-bit, non-prefetchable)
	Capabilities: [40] Vendor Specific Information: VirtIO: CommonCfg
		BAR=2 offset=00000000 size=00000038
	Capabilities: [50] Vendor Specific Information: VirtIO: Notify
		BAR=2 offset=00000038 size=00000032 multiplier=00000002
	Capabilities: [64] Vendor Specific Information: VirtIO: ISR
		BAR=2 offset=0000006c size=00000001
	Capabilities: [74] Vendor Specific Information: VirtIO: <unknown>
		BAR=2 offset=00000000 size=00000004
	Capabilities: [88] Vendor Specific Information: VirtIO: DeviceCfg
		BAR=2 offset=00000070 size=0000000a
...

Alle Capabilites sind also VirtIO spezifische Capabilites und die Informationen befinden sich im BAR 2, also in dem vom Typ Memory. Das Offset gibt an ab welcher Byte Position die Informationen im BAR sind und size wie viele Bytes die Informationen umfassen.

Mit pciconf kann über den Parameter D eine BAR Region ausgelesen werden.

% sudo pciconf -D pci0:0:10:0 0x18 | hexdump -C
00000000  01 00 00 00 01 00 00 00  01 00 00 00 01 00 00 00  |................|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  00 90 fa 03 01 00 00 00  00 d0 fa 03 01 00 00 00  |................|
00000030  00 e0 fa 03 01 00 00 00  ff ff ff ff ff ff ff ff  |................|
00000040  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00000060  ff ff ff ff ff ff ff ff  ff ff ff ff 00 00 00 00  |................|
00000070  08 00 27 54 00 00 00 00  01 00 00 00 ff ff ff ff  |..'T............|
00000080  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00002000

Die erste Capability vom Typ VirtIO: CommonCfg startet bei Byte 0 und ist 0x38 Byte groß, ist also der Bereich vor den ganzen 0xffff. Beschrieben ist das Format in der VirtIO Spezifikation im Abschnitt 4.1.4.3 Common configuration structure layout mit

struct virtio_pci_common_cfg { 
        /* About the whole device. */ 
        le32 device_feature_select;     /* read-write */ 
        le32 device_feature;            /* read-only for driver */ 
        le32 driver_feature_select;     /* read-write */ 
        le32 driver_feature;            /* read-write */ 
        le16 config_msix_vector;        /* read-write */ 
        le16 num_queues;                /* read-only for driver */ 
        u8 device_status;               /* read-write */ 
        u8 config_generation;           /* read-only for driver */ 
 
        /* About a specific virtqueue. */ 
        le16 queue_select;              /* read-write */ 
        le16 queue_size;                /* read-write */ 
        le16 queue_msix_vector;         /* read-write */ 
        le16 queue_enable;              /* read-write */ 
        le16 queue_notify_off;          /* read-only for driver */ 
        le64 queue_desc;                /* read-write */ 
        le64 queue_driver;              /* read-write */ 
        le64 queue_device;              /* read-write */ 
        le16 queue_notif_config_data;   /* read-only for driver */ 
        le16 queue_reset;               /* read-write */ 
 
        /* About the administration virtqueue. */ 
        le16 admin_queue_index;         /* read-only for driver */ 
        le16 admin_queue_num;         /* read-only for driver */ 
};

Das VirtualBox PCI-Gerät gibt darüber also z. B. an, welche Features es unterstützt. Für die Features gibt es insgesamt 64 Bit. In device_feature_select wird geschrieben, ob device_feature die unteren 32 Bit (0) oder die oberen 32 Bit (1) anzeigt. In der Ausgabe oben ist in Little Endian gelesen device_feature_select=1, somit gibt das device_feature mit 1 die oberen oberen 32 Bit an. Auf die gleiche Weise schreibt der FreeBSD Treiber die Features, die es davon unterstützt und genutzt werden sollen in driver_feature_select und driver_feature. Auch hier ist in der Ausgabe oben in Little Endian gelesen driver_feature_select=1, somit gibt das driver_feature mit 1 die oberen oberen 32 Bit an.

Mit device_feature_select=0 und driver_feature_select=0 sieht die erste Zeile der Ausgabe so aus.

00000000  00 00 00 00 dd a3 00 2f  00 00 00 00 23 98 0f 00

In Little Endian gelesen sind device_feature=0x1002fdda3, d. h. das PCI-Gerät unterstützt die folgenden Features

Version1,GuestAnnounce,CtrlVLANFilter,CtrlRxMode,CtrlVq,Status,MrgRxBuf,TxUFO,TxTSOv6,TxTSOv4,RxUFO,RxLROv6,RxLROv4,MAC,RxChecksum,TxChecksum

und driver_feature=0x1000f9823, d. h. ausgehandelt wurden die folgenden Features

Version1,CtrlVLANFilter,CtrlRxMode,CtrlVq,Status,MrgRxBuf,TxTSOv6,TxTSOv4,MAC,RxChecksum,TxChecksum

VirtIO: DeviceCfg

Die Capability VirtIO: DeviceCfg gibt VirtIO-Gerätspezifische Einstellungen an. Im BAR startet sie bei Byte-Offset 0x70 und ist 0xa = 10 Byte lang, was diesem Teil entspricht:

00000070  08 00 27 54 00 00 00 00  01 00

Beschrieben ist das Format in der VirtIO Spezifikation im Abschnitt 5.1.4 Device configuration layout mit:

struct virtio_net_config { 
        u8 mac[6]; 
        le16 status; 
        le16 max_virtqueue_pairs; 
        le16 mtu; 
        le32 speed; 
        u8 duplex; 
        u8 rss_max_key_size; 
        le16 rss_max_indirection_table_length; 
        le32 supported_hash_types; 
        le32 supported_tunnel_types; 
};

Es sind jedoch nicht immer alle Werte angegeben. Hier ist lediglich die MAC Adresse 08:00:27:54:00:00, der Status=0 und max_virtqueue_pairs=1 angegeben. Weitere Werte, wie z. B. die MTU, werden nur angegeben, wenn entsprechende Features ausgehandelt wurden (z. B. VIRTIO_NET_F_MTU).

pci_configuration_space.1772293223.txt.gz · Last modified: by msvoelker