diff --git a/scripts/gpu-passthrough.sh b/scripts/gpu-passthrough.sh index 7414aa7..74c669a 100755 --- a/scripts/gpu-passthrough.sh +++ b/scripts/gpu-passthrough.sh @@ -57,6 +57,24 @@ group_members() { done } +# The functions to hand to the guest with GPU $1: every device in its IOMMU group +# (mandatory for vfio), plus any sibling function of the same PCI device that is +# itself a display or HDMI/DP audio controller. An APU parks its PSP and USB +# controllers on the same PCI device in separate IOMMU groups - those are the +# host's, so siblings are filtered by class and never pulled in blindly. +passthrough_devs() { + local d="$1" m cls + { + group_members "$d" + for m in "/sys/bus/pci/devices/${d%.*}".*; do + [ -e "$m" ] || continue + m=$(basename "$m") + cls=$(cat "/sys/bus/pci/devices/$m/class" 2>/dev/null) + case "$cls" in 0x03*|0x0403*) echo "$m";; esac + done + } | sort -u +} + driver_of() { local l l=$(readlink -f "/sys/bus/pci/devices/$1/driver" 2>/dev/null) @@ -79,7 +97,7 @@ drives_display() { ids_of() { # vendor:device for vfio-pci binding local d - for d in $(group_members "$1"); do + for d in $(passthrough_devs "$1"); do local cls; cls=$(cat "/sys/bus/pci/devices/$d/class" 2>/dev/null) # only real functions of the card: skip bridges (class 0x0604xx) case "$cls" in 0x0604*) continue;; esac @@ -303,7 +321,7 @@ apply() { "$v" -c qemu:///system dumpxml --inactive "$dom" > "$backup/$dom.gpu-current.xml" grep -q 'ua-vfionative-gpu' "$backup/$dom.gpu-current.xml" || cp "$backup/$dom.gpu-current.xml" "$backup/$dom.before-gpu.xml" local devs="" m - for m in $( { group_members "$d"; ls -d "/sys/bus/pci/devices/${d%.*}".* | xargs -n1 basename; } | sort -u); do + for m in $(passthrough_devs "$d"); do case "$(cat "/sys/bus/pci/devices/$m/class" 2>/dev/null)" in 0x0604*) continue;; esac IFS=':.' read -r dm bs sl fn <<< "$m" devs+=" \n \n" @@ -330,7 +348,7 @@ xml() { echo "Add this to the domain, inside . Every device in the GPU's" echo "IOMMU group has to go together:" echo - for m in $(group_members "$d"); do + for m in $(passthrough_devs "$d"); do local cls; cls=$(cat "/sys/bus/pci/devices/$m/class" 2>/dev/null) case "$cls" in 0x0604*) continue;; esac IFS=':. ' read -r dom bus slot fn <<< "$(echo "$m" | tr ':.' ' ')"