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.
247 lines
4.9 KiB
Bash
247 lines
4.9 KiB
Bash
#!/usr/bin/env 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
|
|
declare -A lspciid
|
|
|
|
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
|
|
}
|
|
|
|
format()
|
|
{
|
|
lspci -x > $tmpfile
|
|
|
|
IFS=$'\n' # make newlines the only separator
|
|
for line in $(lspci -F $tmpfile -n)
|
|
do
|
|
lspcimach+=("$line")
|
|
lspcibus+=("$(echo $line|awk '{print $1}')")
|
|
done
|
|
|
|
for line in "${lspcibus[@]}"
|
|
do
|
|
lspciid[$line]="$(lspci -F $tmpfile -ns $line|awk '{print $3}')"
|
|
done
|
|
|
|
for line in $(lspci -F $tmpfile -mm)
|
|
do
|
|
lspcihuma+=("line")
|
|
lspcifm+=("$(echo $line|awk -F\" '{print $4" "$2": "$6}')")
|
|
done
|
|
> $tmpfile
|
|
}
|
|
|
|
makelist()
|
|
{
|
|
if [ -e $genfile ]; then
|
|
cp $genfile $genfile.old
|
|
else
|
|
touch $genfile
|
|
fi
|
|
|
|
IFS=$'\n' # make newlines the only separator
|
|
store=0
|
|
for pci in "${lspcibus[@]}"
|
|
do
|
|
if grep -Eqi "${lspciids[$store]}" "$genfile" && grep -Eqi $pci "$genfile"
|
|
then
|
|
menuitems+=("$pci" "${lspcifm[$store]}" "on")
|
|
else
|
|
menuitems+=("$pci" "${lspcifm[$store]}" "off")
|
|
fi
|
|
let store++
|
|
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 "#$(cat $tmpfile)\n" > $genfile
|
|
printf "options vfio-pci ids=" >> $genfile
|
|
IFS=" "
|
|
for line in $(cat $tmpfile); do
|
|
printf "${lspciid[$line]}," >> $genfile
|
|
done
|
|
truncate -s-1 $genfile #Fix trailing comma
|
|
echo "$genfile written successfully."
|
|
}
|
|
|
|
bind()
|
|
{
|
|
#Unbind from device drivers, and bind to VFIO
|
|
for id in ${bindids[@]};do
|
|
echo "Binding $id"
|
|
#Get pci slot
|
|
pci="0000:$id"
|
|
pcipath="/sys/bus/pci/devices/$pci"
|
|
#Rebinding process
|
|
if [ -e $pcipath ]; then
|
|
echo "$pci" > "$pcipath/driver/unbind"
|
|
fi
|
|
echo "${lspciid[$id]}"|sed 's/:/ /'> /sys/bus/pci/drivers/vfio-pci/new_id
|
|
done
|
|
}
|
|
|
|
unbind()
|
|
{
|
|
# Unbind from VFIO, and bind to device drivers
|
|
for id in ${unbindids[@]};do
|
|
echo "Unbinding $id"
|
|
echo 1 > /sys/bus/pci/devices/0000:$id/remove
|
|
done
|
|
echo 1 > /sys/bus/pci/rescan
|
|
}
|
|
|
|
online()
|
|
{
|
|
IFS=$'\n'
|
|
#Get list of new and old ID's
|
|
for id in $(grep -Eoh "[0-9a-f]{2}:[0-9a-f]{2}\.[0-9]" $genfile); do
|
|
newids+=("$id")
|
|
done
|
|
for id in $(grep -Eoh "[0-9a-f]{2}:[0-9a-f]{2}\.[0-9]" $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
|
|
format
|
|
if makelist; then
|
|
makefile
|
|
else
|
|
cleanup
|
|
exit 130
|
|
fi
|
|
if $online; then
|
|
online
|
|
fi
|
|
cleanup
|
|
fi
|
|
}
|
|
|
|
main "$@"
|