Created
January 27, 2023 23:59
-
-
Save pgaskin/6ff0e5fc51951500371873a3e1742332 to your computer and use it in GitHub Desktop.
Simple early-boot VFIO device binding by PCI address on Fedora 37.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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