— lkmp, linux, kernel, pci — 6 min read
Cheerio hoomans o/
Today we'll have a very brief intro what PCI is all about. PCI as such is a very vast specification and I honestly, there are still a lot of things I do not understand. So let's start with the basic for now and as and when I get more time I'll keep writing more blogs :)
Why am I reading/writing about PCI, Welll because that's the subsytem I'll be working with during my Linux Kernel Mentorship under Bjorn Helgaas :)
PCI stands for Peripheral Component Interconnect. This was introduced in 1993 by the Intel Corporation. A PCI device is any computer hardware that plugs directly into a PCI slot on a computer hardware.
We can use the lspci
tool to list all PCI devices. This tool is part of the
PCI Utilities (pciutils) package created by Martin Mares.
lspci utility is used to display information about the PCI buses present in the system and devices connected to them.
lspci
: Show a brief list of deviceslspci -v
: Display additional infolspci -t
: Display devices in a tree like mannerlspci -k
: Display drivers and modules handling each devicelspci -s {{00:18.3}}
: Show a specific devicelspci -vm
: Dump info in a readable form
In the above image we can see that each device in the list has a unique number. The unique number format is as follows:
1(BB:DD.F) == (Bus Number :: Device Number . Function Number)
For eg: The VGA compatible controller has the unique ID of (00::02.0), this means that the VGA compatible controller is present on Bus Number 0, and the device number for this is 1 and the function number is 0.
Similarly if you see the above image with lspci tree, the device with unique id (00::06.0) which is the PCI bridge: Intel Corporation Device 9a09 (rev 01) is a Bridge Device and to this bridge, the device (01::00.0) which is the Sandisk Corp Device 5006 is connected. We’ll learn more about how the addressing of devices happens below.
PCI is a local computer bus for attaching hardware devices in a computer and is part of the PCI Local Bus standard. The major advantage of PCI is that the bus supports the functions found on a processor bus in a standardized format and is independent of any given processor’s native bus. This helps because we no longer need to worry about a device not being able to work natively with another processor.
It is a replacement for the ISA (Industry Standard Architecture) standard and has three main goals:
PCI supports 3 address spaces:
PCI Configuration space - Use this space to configure the device. The information present here, will be used by the software to determine the amount of memory and I/o address space needed by the device
Input Output Space (I/O space) - Used for data transfer between CPU and PCI device
Memory Address Space - Could either be MMIO (Memory Mapped IO) or PIP (Port Mapped IO) - Use to store data on the PCI device
When we say that PCI supports 3 address space, it means that each PCI device that is connected to the PCI Bus is allocated an address space. That allocated address space can then be divided into 3 different spaces i.e the configuration space, I/O address and Memory address space.
For eg: Say a CPU wants to configure a PCI device it will write into the configuration address space of the device.
Each PCI peripheral is identified by a bus number, a device number, and a function number.
The PCI specification states that:
Thus, each PCI device can be identified at hardware level by their 16-bit address, or unique key.
There can be three types of devices in the PCI.
The configuration space of the PCI devices are of two types:
The registers marked in Blue
are mandatory. It is from these registers the
lspci
tool fetches it’s information from.
To learn how PCI works, we’ll have to start from what happens when a system boots.
When power is applied to a PCI device, the hardware remains inactive. That means, the device will only respond to configuration transactions. When the power is just applied, the device has no memory and no I/O ports mapped in the computer’s address space.
Since every PCI motherboard is equipped with PCI-aware firmware, called the BIOS
To summarize, the firmware (or operating system) queries all PCI buses at startup time (via PCI Configuration Space) to find out what devices are present and what system resources (memory space, I/O space, interrupt lines, etc.) each needs. It then allocates the resources and tells each device what its allocation is. By the time a device driver accesses the device, its memory and I/O regions have already been mapped into the processor’s address space.
The Bus that starts from the CPU is always given the number zero (0). And the rest of the bus that gets created using PCI bridge are given number starting from one (1).
Before we proceed further let’s talk about few terms:
Each Bridge is associated with three number: (Primary Bus Number/ Secondary Bus Number / Subordinate Bus Number) or (P/Sec/Sub)
Let’s take the above image to look into how PCI Bus Enumeration happens.
The configuration cycle starts when the system is booted up. During this configuration cycle, the PCI devices have no address mapped so the BIOS issues IDSEL (Individual Device) signal on the Bus 0 to configure the device.
When the signal comes to the first device, it gets the header type from the
device and finds that it is Type 1
which means a PCI Bridge Device. And since
it is a PCI Bridge Device is given the unique ID of (0::0) because the Bus
number is 0 and it is the first device in the bus. Also the (P/Sec/Sub) is tuple
set to (0/1/FF). The Primary number is 0
because the immediate upstream bus to
the device is Bus 0. And since it’s a bridge device, we move down and the number
of that bus will be given as 1. And hence the Secondary Number of that bridge is
given as 1
and since we do not yet know the highest number of all the buses
that can be reached downstream of the bridge, the Subordinate Bus Number is
assigned to the highest bus number which is 255 or FF.
Moving down the device (0,0) we reach the bus number (1). And then the software starts checking each of the devices. The first device is an edge device and since there will be no bus below it, the number (1::1) is assigned. Because the bus number where the device is 1 and the device number is 1. Similarly since all the devices connected to the Bus Number 1 are Endpoint devices, the unique id given to them are (1,1) and (1,2). The signal is then polled until 31 because each bus can have 31 devices. If no devices are found then it returns back to the bridge above it.
Now that we revisit Bridge 0, it is time to update the Sub
field of the
(P/Sec/Sub) tuple. The software sees that there was only 1 bus below the device
and thus updates the Sub
value from FF to 1. Therefore the final (P/Sec/Sub)
value becomes (0/1/1)
Similarly, following the above procedure, the signal reads each device present on the bus, gets the information about them and assigns the required memory space of each device. Following is what the final unique ID of each device will look like:
That's a very brief intro to PCI devices folks. I'll keep updating this post as and when I understand more about it :)
Ciao for now! Tata o/