Implementation is in /linux/drviers/vfio/pci. Main driver code is vfio_pci.c. This kernel module is compiled as vfio_pci, and we load the module as modprobe vfio-pci for VFIO based PCI-passthrough.
Main kernel module file is vfio_pci.c. When Linux kernel initializes this module, vfio_pci_init() is called.
staticint__initvfio_pci_init(void){// Allocate shared config space permision data used by all devices
vfio_pci_init_perm_bits();// Register and scan for devices
pci_register_device(&vfio_pci_driver);vfio_pci_fill_ids();return0;}
Also when loading the kernel module, vfio_pci_probe() function is called. This is a part of kernel module operation data structure static struct pci_driver vfio_pci_driver.
Here, VFIO PCI device structure is mapped topdev, which represents the actual PCI device.
To use VFIO, we pass a kind of parameter to QEMU, like -device vfio-pci,host=01:00.0,bus=root.1.addr=00.0,multifunction=on,x-vga=on
The above option is for passing a GPU (01:00.0) to a virtual machine.
When QEMU opens a VFIO device driver to load the device, vfio_pci_open() is called. This function is a part of operations struct static const struct vfio_device_ops vfio_pci_ops.
Device information can be passed via vfio_pci_ioctl(), as said earlier.
To read and write a data, vfio_pci_read() and vfio_pci_write() functions are used.
These functions are wrappers of vfio_pci_rw() (/linux/drivers/vfio/pci/vfio_pci.c:985,994).
vfio_pci_rw() is defined right above vfio_pci_read(), which calls
vfio_pci_config_rw (defined in /linux/drivers/vfio/pci/vfio_pci_config.c:1678)
vfio_pci_bar_rw (defined in /linux/drivers/vfio/pci/vfio_pci_rdwr.c:116)
vfio_pci_vga_rw (defined in /linux/drivers/vfio/pci/vfio_pci_rdwr.c:183)
in terms of the index passed to the function.
For GPU, vfio_pci_rw() is mostly called with the index 7 (=VFIO_PCI_CONFIG_REGION_INDEX) and 8 (=VFIO_PCI_VGA_REGION_INDEX). Small number of calls are done with index 0 (=VFIO_PCI_BAR0_REGION_INDEX). Note that each index can be found in /linux/include/uapi/linux/vfio.h:418.
After a VM starts execution, it communicates with a PCI device via QEMU (vfio_pci_read_config() and vfio_pci_write_config()) and KVM VFIO device driver (vfio_pci_rw()).