Task 2: PCI Device
A PCI (Peripheral Component Interconnect) device is a type of hardware device that connects to a computer's motherboard using the PCI interface. PCI devices can be internal or external and are used for a wide range of applications, such as graphics and audio processing, networking, and storage. The PCI bus is a high-speed data path that allows the CPU to communicate with the device and provides a common interface for a variety of hardware devices. PCI devices are identified by a unique vendor ID and device ID and require a specific driver to communicate with the operating system. They can be added or removed while the computer is running, making them a convenient and flexible option for expanding a system's capabilities.
In this task, you will be emulating a PCI device using QEMU and interacting with it. The first step in this process is to set up the QEMU environment.
Step 1: Launch a VM on QEMU
Continuing from the steps outlined in Task 0, add an additional flag to the QEMU command to emulate the PCI device.
You can add more copies of the -device edu
flag to create multiple instances
of the PCI device.
Step 2: Explore the PCI device
Use the lspci
command to discover the PCI slot name of the device with a
vendor ID of 0x1234 and a device ID of 0x11e8. Refer to the man page of the
lspci
command for additional information if needed.
Linux provides a mechanism to access PCI device resources through the sysfs. You
can read the kernel documentation on Accessing PCI device resources through
sysfs. As a next step,
you can use the hexdump
command to read the content of the PCI device's
configuration space. Refer to the specification of the PCI configuration space,
available
here,
to make sense of the output.
The file resource0
in the directory is mapped to the device's memory-mapped
I/O (MMIO) area, and part of its specification is given below.
MMIO area spec
--------------
Only size == 4 accesses are allowed for addresses < 0x80. size == 4 or
size == 8 for the rest.
0x00 (RO) : identification (0xRRrr00edu)
RR -- major version
rr -- minor version
0x04 (RW) : card liveness check
It is a simple value inversion (~ C operator).
0x08 (RW) : factorial computation
The stored value is taken and factorial of it is put back here.
This happens only after factorial bit in the status register (0x20
below) is cleared.
0x20 (RW) : status register, bitwise OR
0x01 -- computing factorial (RO)
0x80 -- raise interrupt after finishing factorial computation
You may find the source code of the device here and a full copy of its specification here.
However, when attempting to read the first four bytes of the file using the
head -c 4 resource0
command, an "Input/output error" is encountered:
Question
Why can't we read from the file resource0
?
Hint:
- According to the
documentation,
rw
access is allowed forIORESOURCE_IO
regions only in files namedresource0..N
. - You should refer to the information from the configuration space.
Step 3: Access the PCI device
Since the file is mmapable, we can use the mmap
system call to map the file
into the process's address space and then access the device's registers. For
more information about the mmap
system call, you can refer to the man
page.
Please write a program that accepts two arguments:
- The path to the PCI device's "resource0".
- A 32-bit unsigned integer in hexadecimal format.
The program should successfully output the identification of the device and the inversion of the given number.
Here's the expected behavior of the program:
$ sudo ./pci-mmap /sys/path/to/the/device/resource0 0x11111111
Identification: 0x010000edu
Inversion: 0xeeeeeeee
Tip
You should be able to accomplish the task by inserting some lines in the given slot. However, feel free to modify other parts of the code if needed.
Question
Please finish the implementation under the directory task2-mmap/
in the
repository.