setup-vfio 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/bin/bash
  2. DEVICES_PATH=/sys/bus/pci/devices
  3. VFIO_MODULE="vfio-pci"
  4. VFIO_PCI_NEW_ID=/sys/bus/pci/drivers/${VFIO_MODULE}/new_id
  5. Interactive=true
  6. UnsafeInts=false
  7. CHOWN_USER=
  8. die() {
  9. echo "$@" >&2
  10. exit 1
  11. }
  12. dieusage() {
  13. die "Usage: $0 [--unsafe] [--user <username>] [--yes] <vendor> <device>"
  14. }
  15. yesno() {
  16. if ! $Interactive; then
  17. $2
  18. return
  19. fi
  20. while true; do
  21. echo -n "$1 "
  22. read answer
  23. case "$answer" in
  24. yes|y|ye)
  25. return 0
  26. ;;
  27. no|n)
  28. return 1
  29. ;;
  30. *)
  31. echo "Please enter 'yes' or 'no'."
  32. esac
  33. done
  34. }
  35. while [ "$#" -gt 0 ]; do
  36. case "$1" in
  37. --help|-h)
  38. dieusage
  39. ;;
  40. --unsafe)
  41. UnsafeInts=true
  42. ;;
  43. --user|-u)
  44. shift
  45. CHOWN_USER="$1"
  46. ;;
  47. --yes|-y)
  48. Interactive=false
  49. ;;
  50. *)
  51. if [ -n "$WANT_VENDOR" ]; then
  52. WANT_DEVICE="$1"
  53. else
  54. WANT_VENDOR="$1"
  55. fi
  56. ;;
  57. esac
  58. shift
  59. done
  60. [ -n "$WANT_DEVICE" ] || dieusage
  61. modprobe ${VFIO_MODULE} || die 'Failed to load ${VFIO_MODULE} module'
  62. for TARGET_DEVICE_ID in $(ls $DEVICES_PATH); do
  63. { grep -q '^0x'"${WANT_VENDOR}" "${DEVICES_PATH}/${TARGET_DEVICE_ID}/vendor" && grep -q '^0x'"${WANT_DEVICE}" "${DEVICES_PATH}/${TARGET_DEVICE_ID}/device"; } || continue
  64. echo "Found $(lspci -s "$TARGET_DEVICE_ID")"
  65. extradevs=
  66. extradevsn=0
  67. extradevsq=
  68. for RELATED_DEVICE_ID in $(ls "${DEVICES_PATH}/${TARGET_DEVICE_ID}/iommu_group/devices/"); do
  69. if [ "$RELATED_DEVICE_ID" = "$TARGET_DEVICE_ID" ] ||
  70. {
  71. { ! [ -e "${DEVICES_PATH}/${RELATED_DEVICE_ID}/driver" ]; } ||
  72. [ "$(basename "$(readlink "${DEVICES_PATH}/${RELATED_DEVICE_ID}/driver")")" = "${VFIO_MODULE}" ]
  73. } ||
  74. grep -q '^0x060400$' "${DEVICES_PATH}/${RELATED_DEVICE_ID}/class"; then
  75. extradevsq="$extradevsq $RELATED_DEVICE_ID"
  76. else
  77. extradevs="$extradevs $RELATED_DEVICE_ID"
  78. let ++extradevsn
  79. fi
  80. done
  81. if [ "$extradevsn" -gt 0 ]; then
  82. if [ $extradevsn -gt 1 ]; then
  83. echo "Enabling VFIO for this device will also disable the following $extradevsn devices:"
  84. else
  85. echo "Enabling VFIO for this device will also disable this device:"
  86. fi
  87. for RELATED_DEVICE_ID in ${extradevs}; do
  88. echo "- $(lspci -s "$RELATED_DEVICE_ID")"
  89. done
  90. fi
  91. yesno 'Enable VFIO?' true || continue
  92. if $UnsafeInts; then
  93. echo 1 >/sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts || die 'Failed to enable unsafe interrupts'
  94. fi
  95. for RELATED_DEVICE_ID in ${extradevsq} ${extradevs}; do
  96. if [ -e "${DEVICES_PATH}/${RELATED_DEVICE_ID}/driver" ]; then
  97. echo "$RELATED_DEVICE_ID" >"${DEVICES_PATH}/${RELATED_DEVICE_ID}/driver/unbind" || die "Failed to unbind $RELATED_DEVICE_ID"
  98. fi
  99. echo "$(<"${DEVICES_PATH}/${RELATED_DEVICE_ID}/vendor") $(<"${DEVICES_PATH}/${RELATED_DEVICE_ID}/device")" >"${VFIO_PCI_NEW_ID}" || die "Failed to associate device id with ${VFIO_MODULE} module"
  100. done
  101. IOMMU_GROUP="$(basename "$(readlink "${DEVICES_PATH}/${TARGET_DEVICE_ID}/iommu_group")")"
  102. VFIO_DEVICE="/dev/vfio/$IOMMU_GROUP"
  103. [ -e "$VFIO_DEVICE" ] || die "$VFIO_DEVICE does not exist"
  104. if [ -n "${CHOWN_USER}" ]; then
  105. chown "${CHOWN_USER}" "$VFIO_DEVICE" || die "Failed to chown $VFIO_DEVICE to $CHOWN_USER"
  106. fi
  107. echo "VFIO enabled on $VFIO_DEVICE"
  108. done