Skip to content

Instantly share code, notes, and snippets.

@pgaskin
Created January 27, 2023 23:59
Show Gist options
  • Save pgaskin/6ff0e5fc51951500371873a3e1742332 to your computer and use it in GitHub Desktop.
Save pgaskin/6ff0e5fc51951500371873a3e1742332 to your computer and use it in GitHub Desktop.
Simple early-boot VFIO device binding by PCI address on Fedora 37.
# enable AMD IOMMU with DMA passthrough
# note: iommu=pt is much faster than the default translated DMA since memory access doesn't need to go through the hypervisor
sudo sed -i '1 s/$/ amd_iommu=on iommu=pt/' /etc/kernel/cmdline
# list IOMMU groups
# record the PCI IDs of all devices in the target group
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*};
n=${n%%/*};
test $n -eq $pn || printf '\nIOMMU Group %s\n' $n;
lspci -nns "${d##*/}";
pn=$n;
done
# add dracut module to override and unbind PCI devices
# note: this works better than 'options vfio-pci ids=' since we can be sure it'll run before anything else, especially systemd udev, and it's also more resilient (it will attempt to unbind existing drivers properly), forceful (it runs before anything else has a chance to), specific (it matches on the PCI address like libvirt, rather than the product/vendor ID), can delay vfio-pci loading (as a workaround), and easier to work with
# note: this is the same logic libvirt uses to detach devices at runtime, except we're doing it much earlier in the process
sudo mkdir /usr/lib/dracut/modules.d/90vfio-pci-rebind
cat << 'EOF' | sudo tee /usr/lib/dracut/modules.d/90vfio-pci-rebind/module-setup.sh
#!/usr/bin/bash
# called by dracut
check() {
return 0
}
# called by dracut
depends() {
return 0
}
# called by dracut
installkernel() {
hostonly='' instmods '=drivers/vfio' '=drivers/vfio/pci'
}
# called by dracut
install() {
inst_hook pre-udev 00 "$moddir/rebind-vfio-pci.sh"
inst_hook pre-pivot 99 "$moddir/probe-vfio-pci.sh"
}
EOF
cat << 'EOF' | sudo tee /usr/lib/dracut/modules.d/90vfio-pci-rebind/rebind-vfio-pci.sh
#!/usr/bin/sh
type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh
torebind=""
for devlist in $(getargs rd.vfio.pcidevs); do
IFS=,
for dev in $devlist; do
if strglob $dev "????:??:??.?"; then
dev="$dev"
elif strglob $dev "??:??.?"; then
dev="0000:$dev"
else
warn "vfio-pci-rebind: unrecognized device path $dev"
fi
if [ -d /sys/bus/pci/devices/$dev ]; then
info "vfio-pci-rebind: rebinding $dev"
torebind="$torebind $dev"
else
warn "vfio-pci-rebind: skipping invalid device $dev"
fi
done
unset IFS
done
for dev in $torebind; do
if ! echo vfio-pci > /sys/bus/pci/devices/$dev/driver_override; then
warn "vfio-pci-rebind: failed to set driver override for $dev"
fi
done
for dev in $torebind; do
if [ -f /sys/bus/pci/devices/$dev/driver/unbind ]; then
if ! echo $dev > /sys/bus/pci/devices/$dev/driver/unbind; then
warn "vfio-pci-rebind: failed to unbind loaded driver from $dev"
fi
fi
done
EOF
cat << 'EOF' | sudo tee /usr/lib/dracut/modules.d/90vfio-pci-rebind/probe-vfio-pci.sh
#!/usr/bin/sh
type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh
# note: I'd prefer to do the probes at the end of the other hook, but since kernel 6.0.4, this will mean the console will stop updating (although it still receives input), complicating LUKS stuff
# https://www.reddit.com/r/VFIO/comments/ykd4ww/comment/j0278ii/?utm_source=reddit&utm_medium=web2x&context=3
# https://lore.kernel.org/linux-fbdev/1e7fafae-4b1b-4d9d-d619-b1a61d214c8a@bstnet.org/T/#u
torebind=""
for devlist in $(getargs rd.vfio.pcidevs); do
IFS=,
for dev in $devlist; do
if strglob $dev "????:??:??.?"; then
dev="$dev"
elif strglob $dev "??:??.?"; then
dev="0000:$dev"
else
warn "vfio-pci-rebind: unrecognized device path $dev"
fi
if [ -d /sys/bus/pci/devices/$dev ]; then
info "vfio-pci-rebind: rebinding $dev"
torebind="$torebind $dev"
else
warn "vfio-pci-rebind: skipping invalid device $dev"
fi
done
unset IFS
done
# we don't even actually need to load it early driver_override will stop other drivers, and libvirt will load the module when neede (see virpci.c#virPCIProbeStubDriver), but give the option to
if getargbool 0 rd.vfio.probe; then
for dev in $torebind; do
if ! echo $dev > /sys/bus/pci/drivers_probe; then
warn "vfio-pci-rebind: failed to probe drivers for $dev"
fi
done
for drv in vfio vfio-pci; do
modprobe $drv || warn "vfio-pci-rebind: failed to load module $drv"
done
fi
EOF
sudo chmod +x /usr/lib/dracut/modules.d/90vfio-pci-rebind/*.sh
echo 'add_dracutmodules+=" vfio-pci-rebind "' | sudo tee /etc/dracut.conf.d/vfio.conf
# rebind NVIDIA drivers to VFIO
sudo sed -i '1 s/$/ rd.vfio.pcidevs=10:00.0,10:00.1,10:00.2,10:00.3/' /etc/kernel/cmdline
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment