You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
4.7 KiB
Bash
224 lines
4.7 KiB
Bash
#!/bin/bash
|
|
# Script to bind the VFIO stub driver to PCI devices
|
|
# By Trent Arcuri, 2017
|
|
#
|
|
|
|
tmpfile=$(mktemp)
|
|
genfile='/etc/modprobe.d/vfioselect.conf'
|
|
online=false
|
|
|
|
usage()
|
|
{
|
|
echo """Usage: $(basename $0) [<arguments>]
|
|
-t,--tmpfile <path> Specify path to temporary file
|
|
-g,--genfile <path> Specify path to modprobe rule
|
|
-o,--online Attempt to bind/unbind without the need to reboot
|
|
-h,--help Display this help dialogue"""
|
|
}
|
|
|
|
cleanup()
|
|
{
|
|
rm $tmpfile
|
|
|
|
if [ -e $genfile.old ]; then
|
|
rm $genfile.old
|
|
fi
|
|
}
|
|
|
|
makelist()
|
|
{
|
|
if [ -e $genfile ]; then
|
|
cp $genfile $genfile.old
|
|
else
|
|
touch $genfile
|
|
fi
|
|
|
|
IFS=$'\n' # make newlines the only separator
|
|
for pci in $(lspci -n | awk '{print $1}')
|
|
do
|
|
if grep -qi $(lspci -ns $pci|awk '{print $3}') "$genfile"; then
|
|
menuitems+=("$pci" "$(lspci -nns $pci|awk '{sub($1 FS,"")}7')" "on")
|
|
else
|
|
menuitems+=("$pci" "$(lspci -nns $pci|awk '{sub($1 FS,"")}7')" "off")
|
|
fi
|
|
done
|
|
|
|
if dialog --keep-tite --checklist \
|
|
"Choose which devices to bind:" 30 180 30 \
|
|
"${menuitems[@]}" 2>$tmpfile; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
|
|
makefile()
|
|
{
|
|
printf "options vfio-pci ids=" > $genfile
|
|
IFS=" "
|
|
for line in $(cat $tmpfile); do
|
|
printf "$(lspci -ns $line|awk '{print $3}')," >> $genfile
|
|
done
|
|
echo "$genfile written successfully."
|
|
}
|
|
|
|
bind()
|
|
{
|
|
#Unbind from device drivers, and bind to VFIO
|
|
echo "Bind functionality is disabled temporarily."
|
|
exit 1
|
|
|
|
for id in ${bindids[@]};do
|
|
#Get pci slot
|
|
pci=$(lspci -Dd $id|grep -Eoh "[0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-9]")
|
|
pcipath="/sys/bus/pci/devices/$pci"
|
|
rmkd+=("$(stat $pcipath/driver -c %N|awk -F/ '{print $NF}'|sed "s/'//")")
|
|
#Rebinding process
|
|
if [ -e /sys/bus/pci/devices/$pci/driver ]; then
|
|
echo "$pci" > /sys/bus/pci/devices/$pci/driver/unbind
|
|
fi
|
|
echo "$id"|sed 's/:/ /'> /sys/bus/pci/drivers/vfio-pci/new_id
|
|
done
|
|
}
|
|
|
|
unbind()
|
|
{
|
|
# Unbind from VFIO, and bind to device drivers
|
|
echo "Unbind functionality is disabled temporarily."
|
|
exit 1
|
|
|
|
for id in ${unbindids[@]};do
|
|
#Get pci slot
|
|
pci=$(lspci -Dd $id|grep -Eoh "[0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-9]")
|
|
rmkd+=("$(lspci -ks $pci | awk '{print $5}' | tail -n1)")
|
|
#Rebinding process
|
|
echo 1 > /sys/bus/pci/devices/$pci/remove
|
|
echo 1 > /sys/bus/pci/rescan
|
|
done
|
|
}
|
|
|
|
online()
|
|
{
|
|
#Get list of new and old ID's
|
|
for id in $(grep -Eoh "[0-9a-z]{4}:[0-9a-z]{4}" $genfile); do
|
|
newids+=("$id")
|
|
done
|
|
for id in $(grep -Eoh "[0-9a-z]{4}:[0-9a-z]{4}" $genfile.old); do
|
|
oldids+=("$id")
|
|
done
|
|
|
|
#Get list of different ID's, determine what to do with them
|
|
for id in $(echo ${newids[@]} ${oldids[@]} | sed 's/ /\n/g'| sort | uniq -u)
|
|
do
|
|
diffids+=("$id")
|
|
done
|
|
for id in $(echo ${diffids[@]} ${oldids[@]} | sed 's/ /\n/g'| sort | uniq -d)
|
|
do
|
|
unbindids+=("$id")
|
|
done
|
|
for id in $(echo ${diffids[@]} ${newids[@]} | sed 's/ /\n/g'| sort | uniq -d)
|
|
do
|
|
bindids+=("$id")
|
|
done
|
|
|
|
echo "Bind: ${bindids[@]}"
|
|
echo "Unbind: ${unbindids[@]}"
|
|
echo
|
|
|
|
#Bind/Unbind the ID's respectively
|
|
if [[ ${bindids[@]} ]]; then
|
|
bind
|
|
fi
|
|
if [[ ${unbindids[@]} ]]; then
|
|
unbind
|
|
fi
|
|
}
|
|
|
|
main()
|
|
{
|
|
#Check dependencies
|
|
if which dialog > /dev/null ; then
|
|
:
|
|
else
|
|
dialog
|
|
exit 1
|
|
fi
|
|
|
|
#Argument formatting
|
|
local argv=("$@");
|
|
# argc is the count of arguments
|
|
local argc=${#argv[@]};
|
|
|
|
# this is important to ensure globbing is active
|
|
shopt -s extglob;
|
|
|
|
# Handle compressed short options
|
|
re="(^| )\\-[[:alnum:]]{2,}"; # regex to detect shortoptions
|
|
# we evaluate this as a long string, thus ${argv[*]}, instead of ${argv[@]}
|
|
if [[ "${argv[*]}" =~ $re ]]; then
|
|
local compiled_args=();
|
|
for ((i=0; i<argc; i++)); do
|
|
if [[ "${argv[$i]}" =~ $re ]]; then
|
|
local compressed_args="${argv[$i]#*-}";
|
|
for ((r=0; r<${#compressed_args}; r++)); do
|
|
compiled_args+=("-${compressed_args:$r:1}");
|
|
done
|
|
shift;
|
|
compiled_args+=("$@");
|
|
## recurse
|
|
main "${compiled_args[@]}";
|
|
## we "pass" the exit code back up the recursions to the OS
|
|
exit $?;
|
|
fi
|
|
compiled_args+=("${argv[$i]}");
|
|
shift;
|
|
done
|
|
exit;
|
|
fi
|
|
|
|
#Check arguments
|
|
while [ "$1" != "" ]; do
|
|
case $1 in
|
|
-t | --tmpfile )
|
|
shift
|
|
cleanup
|
|
tmpfile=$1
|
|
;;
|
|
-g | --genfile )
|
|
shift
|
|
genfile=$1
|
|
;;
|
|
-o | --online )
|
|
online=true
|
|
;;
|
|
-h | --help )
|
|
usage
|
|
exit
|
|
;;
|
|
-?*)
|
|
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
|
|
esac
|
|
shift
|
|
done
|
|
|
|
#Check for root access
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo "This program requires root access."
|
|
exit 1
|
|
else
|
|
if makelist; then
|
|
makefile
|
|
else
|
|
cleanup
|
|
exit 130
|
|
fi
|
|
if $online; then
|
|
online
|
|
fi
|
|
cleanup
|
|
fi
|
|
}
|
|
|
|
main "$@"
|