Вступление
В статье описывается способ проброса физических устройств из гипервизора KVM в гостевую ОС с помощью технологии VFIO, реализованной в linux kernel 3.9.*.
Материал не является руководством к действию (прим. туториалом). Все описанные инструкции вы производите на свой страх и риск.
Эксперимент выполняется под ОС Ubuntu 13.10:
- kernel: 3.11.0-15-generic
- qemu: 1.5.0
- seabios: 1.7.3
Аппаратная часть стенда:
- мат. плата: AMD990FX
- процессор: AMD FX-8120
- опер. память: DDR3 PC3-14900
- видеокарта: ATI RADEON HD 7750
- видеокарта (гость): NVIDIA GTX560-TI
Видеокарта для гостевой системы и хоста, может быть, предположительно, любая (NVIDIA, ATI RADEON).
Материнская плата должна иметь блок управления памятью ввода/вывода (IOMMU) — технология AMD-Vi или VT-d.
Если используется процессор Intel, то он также должен поддерживать VT-d.
Независимо от выбранной платформы в биосе материнской платы должны быть реализованы IVRS/DMAR таблицы.
Подготовка
Настоятельно рекомендуется обновить биос мат. платы с сайта производителя перед началом эксперимента.
Проверяем поддержку технологии проброса.
$ dmesg | grep AMD-Vi
[ 1.279788] AMD-Vi: Found IOMMU at 0000:00:00.2 cap 0x40
[ 1.279790] AMD-Vi: Interrupt remapping enabled
[ 1.292879] AMD-Vi: Lazy IO/TLB flushing enabled
Включаем модули в ядре.
$ cat /etc/modules
# /etc/modules: kernel modules to load at boot time.
lp
rtc
pci_stub
vfio
vfio_iommu_type1
vfio_pci
#vfio_pci_vga (For now, this component is in the stock Ubuntu kernel.)
kvm
kvm_amd
$ sudo update-initramfs -u
Отключаем пробрасываемые устройства (для удобства также проброшены порты usb контроллера).
$ lspci -nn | grep NVIDIA
03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GF114 [GeForce GTX 560 Ti] [10de:1200] (rev a1)
03:00.1 Audio device [0403]: NVIDIA Corporation GF114 HDMI Audio Controller [10de:0e0c] (rev a1)
$ cat /etc/default/grub | grep GRUB_CMDLINE_LINUX_DEFAULT
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash pci-stub.ids=10de:1200,10de:0e0c,1002:4397,1002:4396"
$ sudo update-grub
После перезагрузки должно получиться:
$ dmesg | grep pci-stub
[ 3.163062] pci-stub: add 10DE:1200 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
[ 3.163088] pci-stub 0000:03:00.0: claimed by stub
[ 3.163100] pci-stub: add 10DE:0E0C sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
[ 3.163116] pci-stub 0000:03:00.1: claimed by stub
[ 3.163124] pci-stub: add 1002:4397 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
[ 3.163136] pci-stub: add 1002:4396 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000
Создаем скрипт для подключения устройства к VFIO-PCI.
$ cat /usr/bin/vfio-bind
#!/bin/bash
modprobe vfio-pci
for dev in "$@"; do
vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
device=$(cat /sys/bus/pci/devices/$dev/device)
if [ -e /sys/bus/pci/devices/$dev/driver ]; then
echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
fi
echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done
$ sudo chmod 755 /usr/bin/vfio-bind
И добавляем его в автозапуск (ключи берем из вызова lspci — рассматривалось выше).
$ cat /etc/rc.local
#!/bin/sh -e
/usr/bin/vfio-bind 0000:03:00.0 0000:03:00.1 0000:00:12.0 0000:00:12.2
exit 0
Небольшая автоматизация процесса
Внимание: задать ваши параметры!
$ cat pass.sh
#!/bin/sh
#configs
modules="pci_stub;vfio;vfio_iommu_type1;vfio_pci;kvm;kvm_amd"
grub="quiet splash pci_stub.ids=10de:1200,10de:0e0c,1002:4397,1002:4396"
autorun="/usr/bin/vfio-bind 0000:03:00.0 0000:03:00.1 0000:00:12.0 0000:00:12.2"
#functions
split() {
#$1 - file, $2 - param
arr=$(echo $2 | tr ";" "\n")
for x in $arr
do
echo $x >> $1
done
}
replaceParam() {
#$1 - file, $2 - param, $3 - value
cur_val=`cat $1 | grep $2`
sed "s/$cur_val/$2=\"$3\"/g" -i $1
}
autorunAdd() {
#$1 - param
str='$i \'"$1"'\n'
sed -i -e "$str" /etc/rc.local
}
pause() {
read -p "$1" -n1 -s
echo
}
#info virtualization
dmesg | grep AMD-Vi
pause 'Press any key to continue or CTRL-C to exit...'
#modules load
split '/etc/modules' "$modules"
update-initramfs -u
#grub config
replaceParam '/etc/default/grub' 'GRUB_CMDLINE_LINUX_DEFAULT' "$grub"
update-grub
autorunAdd "$autorun"
$ cat vfio-bind
#!/bin/bash
modprobe vfio-pci
for dev in "$@"; do
vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
device=$(cat /sys/bus/pci/devices/$dev/device)
if [ -e /sys/bus/pci/devices/$dev/driver ]; then
echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
fi
echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done
$ sudo cp vfio-bind /usr/bin/
$ sudo chmod 755 /usr/bin/vfio-bind
$ sudo chmod 755 pass.sh
$ sudo bash pass.sh
Запуск
В качестве гостевой ОС используется WINDOWS 7 с паравиртуальными драйверами VIRTIO. В соответствии с этим выбрано оборудование виртуальной машины.
$ cat win7_vfio_test.sh
#! /bin/sh
IS_TAP0_EXISTS=`ifconfig | grep -c tap0`
if [ $IS_TAP0_EXISTS = 0 ] ; then
sudo tunctl -t tap0 && sudo ifconfig tap0 0.0.0.0 promisc up && sudo brctl addif br0 tap0 && echo 'Success: tap0 interface created.' || echo 'Error: tap0 interface is not created.'
else
echo 'Success: tap0 interface already exists.'
fi
#init kvm-qemu
sudo qemu-system-x86_64 \
-boot menu=on \
-enable-kvm \
-M q35 \
-m 1024 \
-cpu host \
-rtc base=localtime \
-smp 1,sockets=1,cores=1,threads=1 \
-bios /usr/share/qemu/bios.bin \
-acpitable file=/usr/share/seabios/q35-acpi-dsdt.aml \
-device ioh3420,bus=pcie.0,addr=1c.0,multifunction=on,port=1,chassis=1,id=root \
-device ahci,bus=pcie.0,id=ahci \
-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x6 \
-drive file='/dev/sdc6',if=none,id=drive-virtio-disk0,format=raw -device virtio-blk-pci,scsi=off,bus=pcie.0,addr=0x5,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=2 \
-drive file='windows7.iso',if=none,id=drive-ide0-1-0,readonly=on,format=raw -device ide-cd,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0,bootindex=1 \
-drive file='virtio-win-0.1-74.iso',if=none,id=drive-ide0-2-0,readonly=on,format=raw -device ide-cd,bus=ide.2,unit=0,drive=drive-ide0-2-0,id=ide0-2-0 \
-netdev tap,ifname=tap0,id=hostnet0,script=no,downscript=no -device virtio-net-pci,netdev=hostnet0,mac=52:54:00:26:7F:96,id=net0 \
-device vfio-pci,host=00:12.0,bus=pcie.0 \
-device vfio-pci,host=00:12.2,bus=pcie.0 \
-device vfio-pci,host=03:00.0,bus=root,addr=00.0,multifunction=on,x-vga=on \
-device vfio-pci,host=03:00.1,bus=root,addr=00.1 \
-vga none \
-nographic
#-vnc 127.0.0.1:0
В ВМ проброшен раздел жесткого диска -drive file=/dev/sdc6.
Начальная установка выполнялась без проброса физических устройств (можно использовать -vnc 127.0.0.1:0 или стандартную консоль).
Аргументы -vga none -nographic следует добавить при пробросе компонентов видеокарты (-device vfio-pci).