Jump to content

Проброс PCI-устройств в QEMU

From KolibriOS wiki

На примере Atheros AR9285 (168c:002b) для разработки драйверов KolibriOS

Проброс реализуется на хосте под управлением Linux. На потребительских версиях Windows проброс PCI-устройств в виртуальную машину невозможен:

  • Hyper-V DDA (Discrete Device Assignment) доступен только в Windows Server.
  • VMware Workstation PCI passthrough не поддерживает (только ESXi на голом железе).
  • VirtualBox удалил поддержку, начиная с 6.1.

Технически работа с IOMMU из userspace реализована через подсистему ядра VFIO, аналога которому в открытом доступе на Windows нет.

Требования

  • CPU с поддержкой VT-x/AMD-V и VT-d/AMD-Vi
  • IOMMU включён в BIOS/UEFI (опция называется VT-d, Intel Virtualization Technology for Directed I/O, IOMMU, AMD-Vi или SVM)
  • Linux с модулем vfio-pci (есть во всех современных ядрах из коробки)
  • QEMU с поддержкой KVM

Что можно пробрасывать

Тип Механизм QEMU Требует IOMMU
PCI / PCIe / Mini PCIe / M.2 -device vfio-pci да
USB -device usb-host нет
SR-IOV Virtual Functions -device vfio-pci да
Блочные устройства -drive file=/dev/sdX нет

Mini PCIe и M.2 — это формы того же PCIe, процедура проброса идентична обычному PCI.

Подготовка хоста Linux

1. Найти устройство

lspci -v | grep -i network
Вывод lspci с найденной картой и ID устройства

Адрес слева (02:00.0) используется во всех последующих командах.

2. Получить ID

lspci -n -s 02:00.0

Формат vendor:device, например 168c:002b.

3. Проверить IOMMU-группы

dmesg | grep -i "iommu group"
Список IOMMU-групп в выводе dmesg

Пусто → IOMMU не активирован (BIOS или параметры ядра).

При необходимости — развёрнутый вид групп с привязкой устройств:

for d in /sys/kernel/iommu_groups/*/devices/*; do
  n=${d#*/iommu_groups/*}; n=${n%%/*}
  printf 'IOMMU Group %s ' "$n"
  lspci -nns "${d##*/}"
done

Все устройства из одной группы пробрасываются только вместе. Если в группе с целевой картой висят посторонние устройства — нужен ACS override patch (пересборка ядра) или замена слота/материнской платы.

4. Параметры ядра

/etc/default/grub, строка GRUB_CMDLINE_LINUX_DEFAULT:

Intel:

GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on vfio-pci.ids=168c:002b"

AMD:

GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on vfio-pci.ids=168c:002b"
  • *_iommu=on — активирует IOMMU
  • vfio-pci.ids= — vfio-pci забирает устройство до загрузки штатного драйвера
Отредактированный /etc/default/grub

Применить:

sudo update-grub                              # Debian/Ubuntu
# sudo grub-mkconfig -o /boot/grub/grub.cfg   # Arch/Fedora
sudo reboot

5. Проверка привязки

lspci -v -s 02:00.0 | grep "Kernel driver"

Ожидаемое: Kernel driver in use: vfio-pci.

6. Если драйвер не сменился

Штатный драйвер (ath9k, iwlwifi, e1000e и т.п.) успевает захватить устройство раньше vfio-pci.

Принудительная загрузка vfio-pci:

echo 'vfio-pci' | sudo tee -a /etc/modules
sudo reboot

Чёрный список + дублирование привязки + initramfs:

sudo tee /etc/modprobe.d/blacklist-ath9k.conf <<EOF
blacklist ath9k
blacklist ath9k_htc
EOF

echo 'options vfio-pci ids=168c:002b' | sudo tee /etc/modprobe.d/vfio-pci.conf

sudo update-initramfs -u   # Debian/Ubuntu
# sudo dracut -f           # Fedora
# sudo mkinitcpio -P       # Arch
sudo reboot

Имена модулей в blacklist подставлять под свой чип (iwlwifi, r8169, nouveau и т.д.).

7. Права на /dev/vfio

sudo usermod -aG kvm $USER
echo 'SUBSYSTEM=="vfio", GROUP="kvm", MODE="0660"' | \
  sudo tee /etc/udev/rules.d/10-vfio.rules
sudo udevadm control --reload-rules
sudo udevadm trigger

Перелогиниться (новое членство в группе не подхватывается в текущей сессии).

Запуск

Минимальная команда:

qemu-system-x86_64 \
  -enable-kvm \
  -m 512M \
  -vga vmware \
  -device vfio-pci,host=02:00.0 \
  -cdrom /path/to/kolibri.iso
KolibriOS видит проброшенную карту как реальное PCI-устройство

Параметры QEMU

Базовые

Параметр Назначение
-enable-kvm Аппаратное ускорение KVM. Для VFIO обязательно.
-m 512M RAM гостя. Колибри хватает 64M, 512M — с запасом под отладку.
-cpu host Прокинуть весь набор инструкций хоста. Альтернативы: qemu64, kvm64.
-smp 2 Число vCPU. Для отладки одного драйвера — -smp 1.
-boot d Загрузка с CD. a — FDD, c — HDD, n — PXE.
-no-reboot triple fault «тройной сбой» = выход QEMU вместо перезагрузки гостя.
-vga vmware Модель видео гостя. Для Колибри подходят std, vmware, cirrus.
-fda kolibri.img Дискета — штатный формат образа Колибри. Альтернативы: -cdrom file.iso, -hda file.img.

vfio-pci

-device vfio-pci,host=02:00.0[,id=net0][,bus=pci.0,addr=05.0][,multifunction=on][,romfile=rom.bin][,x-vga=on]
Параметр Назначение
host=BUS:DEV.FN Физический адрес устройства
id=... Внутреннее имя в QEMU (для нескольких пробросов)
bus=...,addr=... Принудительное место на виртуальной PCI-шине гостя
multifunction=on Для multi-function устройств
romfile=... Подмена Option ROM (актуально для GPU)
x-vga=on Режим primary VGA passthrough (только для GPU)

Для AR9285 достаточно host=02:00.0.

Управление привязкой без перезагрузки

Вернуть устройство хосту:

echo "0000:02:00.0" | sudo tee /sys/bus/pci/drivers/vfio-pci/unbind
echo "0000:02:00.0" | sudo tee /sys/bus/pci/drivers/ath9k/bind

Обратно к vfio-pci:

echo "0000:02:00.0" | sudo tee /sys/bus/pci/drivers/ath9k/unbind
echo "168c 002b" | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id

Диагностика

lsmod | grep ath9k          # штатный драйвер должен быть выгружен
ls -la /dev/vfio/           # права на vfio-устройства
dmesg | grep -i -e vfio -e iommu
lspci -k -s 02:00.0         # текущий привязанный драйвер
pkill qemu                  # убить зависший QEMU

Типовые ошибки

Сообщение Причина Решение
vfio: failed to set iommu for container: Operation not permitted Нет прав на /dev/vfio/* Шаг 7, перелогин
vfio-pci: probe ... failed with error -22 Устройство не одно в IOMMU-группе Пробрасывать всю группу или ACS override
Group N is not viable То же То же
KVM: entry failed, hardware error 0x80000021 -cpu host несовместим с конкретным железом -cpu kvm64 или -cpu qemu64
Карта видна в госте, но не отвечает после перезапуска Reset bug устройства (нет корректного FLR) Полная перезагрузка хоста между запусками
BAR ... can't reserve [mem ...] Резервирование региона хост-ядром Добавить pci=realloc в параметры ядра

См. также

Внешние ссылки