Вступление.
В статье рассмотрена виртуализация Win10 посредством qemu—kvm совместно с libvirt и virt-manager, а так же проброс видеокарты Nvidia.
Выбор ОС и видеокарты обусловлен огромной проблемностью виртуализации, которую можно практически полностью нивелировать правильной настройкой, что создаёт интересный профессиональный вызов. К этому имеется некоторая востребованность для ряда специализированного проприетарного ПО, которое хоть и выходит из оборота, заменяясь на свободные (или не очень) нативные аналоги, но всё ещё эксплуатируется по остаточному принципу в узкоспециализированных нишах.
Несколько слов об Nvidia. Видеокарты данного вендора являются наихудшим вариантом для проброса из-за злонамеренного препятствования, которое подразумевает негласный запрет на проброс «непрофессиональных» моделей видеокарт. Проблема особенно характерна для Win10, в которой проброс без настройки приводит к неработоспособности системы.
В статье описано решение характерной для сочетания Win10 и Nvidia «Ошибки 43» (код 43) и VIDEO_TDR_FAILURE с драйвером nvlddmkm.sys, приводящей к «синему экрану смерти» (BSOD) из-за проблем с MSI (Message Signaled Interrupts). Не смотря на это, GTX 1060 возможно пробросить и она не имеет проблемы со сбросом GPU (reset bug) после выключения виртуальной машины. Проблема со сбросом выражается в том, что после завершения работы виртуальной машины реинициализация видеокарты не произойдёт и она останется в режиме выключения. Чтобы хост-компьютер смог взаимодействовать с таким «подвисшим» устройством, потребуется перезагрузка хоста, что, как правило, очень неудобно.
Примеры в статье отталкиваются от следующего набора оборудования и пакетов:
- Дистрибутив: Linux Mint 20 (Ubuntu 20.04).
- Ядро Linux 5.10.9. Вполне подойдёт и LTS.
- Процессор: Intel i7 6800K 12 потоков.
- Видеокарта для хоста: AMD RX 480 8 Гб.
- Видеокарта для проброса: Nvidia GTX 1060 6 Гб.
- На хосте проприетарный драйвер не применяется. Допустимо использование только свободного (nouveau) в виду специфики технологии проброса.
- qemu-kvm 4.2 из стандартного репозитория (Ubuntu 20.04).
Вопросы оптимизации виртуализации рассмотрены в отдельной статье: https://noostyche.ru/blog/2021/03/30/optimizaciya-virtualizacii-win10-qemu-kvm/
Базовые требования и рекомендации.
- В настройках материнской платы (UEFI) необходимо включить поддержку виртуализации.
- Базовая аппаратная виртуализация.
- Intel VT.
- AMD SVM.
- Поддержка виртуализации ввода-вывода — IOMMU.
- Intel. Активировать VT-d (Intel Virtualization Technology for Directed I/O).
- AMD. Активировать AMD-Vi.
- Базовая аппаратная виртуализация.
- Две дискретные видеокарты.
- Видеокарты AMD наиболее предпочтительны для проброса в виду отсутствия искусственного препятствования со стороны вендора, что делает проброс практически беспроблемным.
- Видеокарта, используемая для проброса, НЕ ДОЛЖНА быть в первом PCI-слоте.
- Для пробрасываемой видеокарты не должны использоваться проприетарные драйверы, только свободные. В данном случае nouveau. Иначе на этапе захвата видеокарты драйвером vfio_pci хост или видеокарта могут зависнуть.
- При наличии встроенной в процессор графики можно пробросить дискретную.
- Видеокарта для проброса должна иметь поддержку EFI (модели от 2012 года).
- Для вывода изображения с двух видеокарт достаточно одного монитора. Потребуется два кабеля. Первый будет от основной видеокарты, а второй — от пробрасываемой.
- Переключение между выводом изображения с первой и второй видеокарты будет осуществляться через переключение видеорежима в настройках монитора.
- Очень желательна материнская плата среднего ценового сегмента и выше, поддерживающая работу PCI в режиме 8x/8/x4 или 16x/8x.
- Процессор минимум с восемью потоками.
- В upstream-ядрах могут быть различные улучшения виртуализации, поэтому желательно использовать свежее ядро.
Содержание.
- Установка qemu-kvm, libvirt, virt-manager и сопутствующих пакетов.
- Базовая настройка libvirt.
- Загрузка модулей ядра.
- Модуль kvm.
- Модули IOMMU.
- Группы IOMMU и видеокарта.
- Модули vfio.
- Завершение подготовки проброса видеокарты GTX 1060.
- Создание и настройка виртуальной машины.
- Создание виртуальной машины с помощью Virt-manager.
- Подробная настройка виртуальной машины.
- Нюансы Windows.
- Проброс видеокарты Nvidia.
- Базовый проброс видеокарты.
- Установка драйвера Nvidia.
- Подготовка гостя.
- Включение MSI для видеокарты Nvidia.
- Маскировка виртуализации для решения «ошибки 43».
- Заключительная часть.
Предварительная настройка.
Установка qemu-kvm, libvirt, virt-manager и сопутствующих пакетов.
Примечание: Набор пакетов характерен именно для Ubuntu 20.04 и деривативов.
sudo apt install qemu-kvm libvirt-daemon libvirt-daemon-system libvirt-daemon-driver-qemu gir1.2-spiceclientgtk-3.0 virt-manager ovmf
qemu-kvm — средство виртуализации qemu с гипервизором kvm.
libvirt-daemon, libvirt-daemon-system и libvirt-daemon-driver-qemu — это основные пакеты, которые подтянут прочие необходимые. В них входит несколько демонов (сервисов) libvirt и утилит для управления виртуальными машинами.
virt-manager — графическая оболочка libvirt для управления виртуальными машинами и редактирования их конфигурации.
gir1.2-spiceclientgtk-3.0 — пакет для поддержки управления виртуальной машиной по протоколу SPICE. Поддерживает шифрование, общий буфер обмена, проброс USB и многое другое. Документация: https://www.linux-kvm.org/page/SPICE
ovmf — Open Virtual Machine Firmware — прошивка с поддержкой эмуляции EFI. Альтернатива seabios. Оптимальный выбор для ОС с поддержкой EFI (все актуальные Linux-дистрибутивы, Win8.1, Win10).
Место хранения прошивок /usr/share/qemu/
qemu-utils — набор утилит. Необязательный пакет для установки.
- qemu-img — утилита для создания и преобразования образов накопителей. К примеру, позволяет преобразовать образ в формате vdi (Virtualbox) в img.
- qemu-io — утилита для работы с QEMU I/O.
- qemu-nbd — экспорт образа накопителя QEMU с помощью протокола NBD.
После установки пакетов требуется перезагрузка.
Базовая настройка libvirt.
После установки пакетов и перезагрузки требуется осуществить подготовку libvirt к использованию.
Пользователь и группа libvirt.
Убедиться, что пользователь был добавлен в группу libvirt:
id $USER | grep libvirt
Это позволит пользователю запускать виртуальные машины без привилегий суперпользователя и получить доступ к расширенным сетевым опциям.
Если пользователь не в группе — добавить вручную. Команда на добавление активного пользователя в дополнительную группу libvirt без удаления из предыдущих групп:
sudo usermod -aG libvirt $USER
$USER — активный пользователь.
После добавление нужно перезапустить сессию, чтобы изменения вступили в силу.
Запуск демона libvirtd.
Запуск libvirtd вручную.
systemctl start libvirtd
Можно добавить сервис в автозагрузку.
systemctl enable libvirtd
Вместе с libvirtd будет запущен virtlogd для ведения журнала событий.
Место хранения журнала событий виртуальных машин: /var/log/libvirt/qemu/
Загрузка модулей ядра.
В данном примере будет рассмотрен вариант передачи параметров для ядра посредством Grub. Для этого потребуется внести изменения /etc/default/grub.
Модули для загрузки и параметры для ядра должны быть перечислены в строке GRUB_CMDLINE_LINUX_DEFAULT=
Итоговый набор параметров для процессоров Intel и видеокарты GTX 1060 будет выглядеть так:
GRUB_CMDLINE_LINUX_DEFAULT="
=onkvm_inte
lintel_iommu=on iommu=pt vfio-pci.ids=10de:1c03,10de:10f1 vfio_iommu_type1.allow_unsafe_interrupts=1 vfio_virqfd=1"
Далее подробно и по порядку.
Модуль kvm.
Основное предназначение — ускорение виртуализации.
Проверить поддержку kvm-ускорения можно утилитой kvm-ok:
sudo kvm-ok
Вывод, указывающий, что ускорение будет использоваться:
INFO: /dev/kvm exists
KVM acceleration can be used
Если модуль kvm не активирован, будет показано предупреждение:
INFO: /dev/kvm does not exist
HINT: sudo modprobe kvm_intel
Если ускорение не поддерживается:
INFO: Your CPU does not support KVM extensions
KVM acceleration can NOT be used
Проверить загружен ли модуль:
lsmod | grep -i kvm
Если вывод пустой, необходимо загрузить модуль.
Модуль kvm_intel зависит от модуля intel_iommu, загрузка которого описана ниже. То есть, чтобы загрузить модуль kvm_intel достаточно загрузить intel_iommu, но можно загрузить и вручную.
Утилита modprobe позволяет загрузить модуль, но только для текущей сессии:
sudo modprobe kvm_intel
Модули IOMMU.
Input Output Memory Management Unit https://ru.wikipedia.org/wiki/IOMMU
Настройка IOMMU является важным этапом для проброса видеокарты.
В настройках материнской платы (UEFI) нужно сделать следующее:
- Для Intel активировать VT-d (Intel Virtualization Technology for Directed I/O).
- Для AMD активировать AMD-Vi.
Загрузка с помощью Grub. В файле /etc/default/grub в строку GRUB_CMDLINE_LINUX_DEFAULT добавить intel_iommu=on iommu=pt.
Итоговый вид:
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt vfio-pci.ids=10de:1c03,10de:10f1 vfio_iommu_type1.allow_unsafe_interrupts=1 vfio_virqfd=1"
intel_iommu=on — включить поддержку транслятора виртуальных адресов в физические. Материнская плата должна иметь блок управления памятью ввода/вывода (IOMMU). По идее, этот блок есть во всех современных не супер бюджетных материнских платах.
iommu=pt — включает режим проброса. Это снижает нагрузку IOMMU для устройств принадлежащих хосту.
После необходимо обновить конфигурацию Grub, чтобы изменения вступили в силу после перезагруки:
sudo update-grub
Перезагрузиться.
Удостовериться, что IOMMU используется:
dmesg | grep -iE "(DMAR|IOMMU)"
i — игнорировать различие регистра.
E — расширенное регулярное выражение.
Если вывод пустой, то IOMMU выключено или отсутствует аппаратная поддержка в материнской плате.
Группы IOMMU и видеокарта.
Это наименьший связанный набор физических устройств, которые могут быть переданы виртуальной машине. В данном случае в целях проброса искомым набором является группа, содержащая устройства видеокарты.
Проверка наличия групп устройств IOMMU:
ls /sys/kernel/iommu_groups
Должен быть подобный вывод (у всех будет разный):
0 1 10 11 12 13 2 3 4 5 6 7 8 9
Если пусто, значит IOMMU не включено.
Видеокарта разделена на два устройства: видео и аудио (звук через HDMI). Нужно проверить в какую IOMMU-группу входит видео и аудио видеокарты. Сначала потребуется узнать их ID. Для Nvidia:
lspci -nn | grep -i nvidia
Для GTX 1060 вывод будет такой:
Искомые ID имеют следующий вид:
- Видео 02:00.0
- Аудио 02:00.1
Теперь, зная ID устройств видеокарты, можно провести проверку принадлежности устройств к конкретной группе IOMMU:
find /sys/kernel/iommu_groups/ -type l | grep -iE "(02:00.0|02:00.1)"
Вывод:
Это означает, что устройства видеокарты находятся в группе 27, то есть всё в порядке.
Модули vfio.
Брошюра о vfio: http://www.linux-kvm.org/images/b/b4/2012-forum-VFIO.pdf
Рассмотрим следующий необходимый набор модулей:
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt vfio-pci.ids=10de:1c03,10de:10f1 vfio_iommu_type1.allow_unsafe_interrupts=1 vfio_virqfd=1"
vfio-pci.ids — обеспечивает мягкий захват видеокарты драйвером vfio на этапе загрузки хост-системы. Это ключевой модуль, обеспечивающий успех проброса.
vfio_iommu_type1.allow_unsafe_interrupts=1 — уменьшает задержки (latency) прерываний. Позволит включить в Win-госте MSISupported для решения проблемы VIDEO_TDR_FAILURE nvlddmkm.sys.
vfio_virqfd=1 — драйвер для поддержки irqfd — механизма передачи прерываний между хостом и гостем.
Завершение подготовки проброса видеокарты GTX 1060.
Вернёмся к модулю vfio-pci.ids.
Видеокарта состоит из двух устройств: видео и аудио. В целях проброса модулю vfio-pci.ids необходимо передать vendor id и device id устройств видеокарты.
Вывести ID устройств видеокарты:
lspci -nn | grep -i nvidia
Вывод для GTX 1060:
02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP106 [GeForce GTX 1060 6GB] [10de:1c03] (rev a1)
02:00.1 Audio device [0403]: NVIDIA Corporation GP106 High Definition Audio Controller [10de:10f1] (rev a1)
Искомые vendor id и device id имеют такой вид:
- Видео [10de:1c03]
- Аудио [10de:10f1]
Итоговый результат для GTX 1060 выглядит так:
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt vfio-pci.ids=10de:1c03,10de:10f1 vfio_iommu_type1.allow_unsafe_interrupts=1 vfio_virqfd=1"
После перезагрузки vfio захватит видеокарту, что сделает её доступной для проброса.
Проверить успешность захвата по ID видеоустройства:
lspci -nnk -d 10de:1c03
Вывод:
В строке Kernel driver in use должно быть vfio-pci. Это означает, что видеокарта была успешно захвачена драйвером vfio_pci.
На этом непосредственная настройка хоста окончена.
Создание и настройка виртуальной машины.
Процесс будет осуществлён с помощью Virt-manager.
Если при запуске Virt-manager выводится ошибка «Не удалось подключиться к libvirtd», то стоит удостовериться в следующем:
- Установлены пакеты libvirt-daemon и libvirt-daemon-system.
- Пользователь находится в группе libvirt.
id $USER | grep libvirt
- libvirtd на самом деле запущен.
systemctl start libvirtd
Дополнительные варианты решения: https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF#virt-manager_has_permission_issues
Если есть какие-то другие проблемы, то их можно отследить через вывод в системном журнале:
journalctl -xb | grep -i libvirt
Создание виртуальной машины с помощью Virt-manager.
Создание подключения.
Необходимо создать подключение к гипервизору QEMU/KVM.
Предварительная настройка создания виртуальной машины.
В данном примере для установки Win10 в качестве гостевой ОС используется базовая конфигурация. Далее будет проделана дополнительная настройка виртуальной машины, чтобы осуществить проброс видеокарты, успешно установить драйвер и обойти нелепые искусственные ограничения Nvidia («ошибка 43» и BSOD из-за отключенного MSI).
Официальный установочный образ Win10 в формате ISO:
https://www.microsoft.com/ru-ru/software-download/windows10ISO
Образ будет использоваться для установки с использованием эмуляции CD-ROM.
Шаг 0.
Файл / Создать виртуальную машину
Шаг 1.
В методе установки выбрать «Локальный ISO или CDROM»:
Шаг 2.
Выбрать ранее загруженный установочный образ:
В этом же шаге выбрать тип устанавливаемой ОС. Это важно для подтягивания мета-информации, в зависимости от которой предоставляются те или иные настройки в Virt-manager и libvirt. Для отображения полного списка отметить «Include end of life operating systems»:
Шаг 3.
Указать количество ОЗУ и потоков для виртуальной машины.
В данном примере для гостевой ОС будет выделено 16384 Мб из 32768 Мб ОЗУ и 8 из 12 потоков процессора.
Не смотря на то, что с настройками по умолчанию виртуальная машина использует выделенные ресурсы не изолированно, то есть хост не теряет возможность их использовать, очень желательно оставить достаточное количество ресурсов для функционирования ОС хоста, чтобы избежать эффекта конкуренции за ресурсы, который может привести к возникновению заиканий.
Шаг 4.
Необходимо указать образ виртуального накопителя, в который будет установлена гостевая (виртуализируемая) ОС.
В libvirt используются специальные хранилища данных, что делает задачу несколько нетривиальной. Ознакомиться можно здесь: https://libvirt.org/storage.html
В примере создаётся хранилище данных с названием pool и типом «Каталог в файловой системе»; в строке Target Path указывается путь до каталога в файловой системе хоста, в котором по итогу расположится хранилище данных.
При выборе места для хранилища стоит учитывать достаточное количество свободного места на физическом накопителе. Для Win10 желательно от 60 Гб.
Хранилище создано. Теперь необходимо создать сам образ виртуального накопителя, в который будет установлена Win10.
Формат: raw. Оптимален для Windows-гостей.
Объём: 60 Гб
Подтвердить создание.
Созданный виртуальный накопитель появится в списке томов. Останется выбрать его.
На этом четвёртый шаг заканчивается.
Шаг 5.
Отметить пункт «Проверить конфигурацию перед установкой»:
Это позволит перейти к непосредственной настройке виртуальной машины.
Подробная настройка виртуальной машины.
Обзор.
Выбрать набор микросхем q35. Он лучше поддерживает проброс.
Выбрать прошивку (firmware) ovmf — OVMF_CODE.fd.
Применить изменения.
Информация об ОС.
Убедиться, что выбрано Windows 10, так как это подтягивает мета-информацию, которая влияет на доступные настройки Virt-manager и libvirt.
Процессоры.
В этой вкладке указывается число потоков, конфигурация (модель процессора) и топология процессора. В данном примере копируется конфигурация хоста с разделением на 8 потоков: 4 ядра умножаются на 2 потока, что имитирует гиперпоточность.
Память.
Для гостя выделяется 16384 Мб ОЗУ. Всего в системе 32 Гб.
Параметры загрузки.
Переставить SATA CDROM 1, к которому примонтирован установочный образ, на первое место, тем самым повысив приоритет загрузки:
Иначе процесс установки не сможет начаться. Можно будет лишь наблюдать интерфейс UEFI Interactive Shell, в котором придётся указать путь до загрузочной области вручную:
SATA Диск 1.
Ранее созданный виртуальный накопитель подключается через эмуляцию шины SATA:
В параметрах производительности переключить «Режим кэширования» на «none», а «Режим ввода-вывода» на «native». Это улучшит отзывчивость и позволит избежать потери данных при принудительном выключении виртуальной машины.
Можно подключать несколько виртуальных и даже физических накопителей. Последнее несколько нетривиально и имеет ряд ограничений.
SATA CDROM 1.
Эмуляция CD-ROM устройства, подключенного по шине SATA. В него примонтирован установочный образ.
После установки гостевой ОС устройство SATA CDROM 1 можно удалить.
NIC (Настройки сети).
Если используется обычная конфигурация сети, то можно оставить как есть.
Планшет.
Подобные устройства правильнее пробрасывать в виртуальную машину. Поэтому эмулируемое устройство лучше удалить.
Дисплей VNC.
Заменить VNC на «Сервер SPICE» и убрать прослушивание (Listen type), так как виртуализация будет локальной:
Этот протокол более предпочтителен, когда требуется передача звука от гостя к хосту. Так же у него нет проблемы с «залипанием» курсора по краям экрана.
Консоль.
Для Windows-гостей это не требуется, поэтому лучше удалить.
Видео QXL.
Это виртуальный графический адаптер, поддерживающий 2D ускорение. Будет выводить графику, пока не задействована проброшенная физическая видеокарта.
Controller USB 0.
Оставить по умолчанию.
Теперь всё готово для установки ОС, останется нажать кнопку «Начать установку».
После подтверждения будет запущена виртуальная машина с процессом установки:
Курсор из окна виртуальной машины освобождается комбинацией Ctrl + Alt.
С установкой Win10 предлагаю разобраться самостоятельно. Основная сложность в том, что каждое крупное обновление появляются всё новые и новые преграды для отказа от навязываемой учётной записи MS и прочего «ненужно». На момент 2021 года только для отказа от некоторой(!) части телеметрии предлагается снять более десятка галочек, которые, как в головоломке, надёжно спрятаны в графическом интерфейсе и разнесены на несколько окон. Стоит отметить, что без применения спецсредств отключить основную массу телеметрии невозможно.
После установки и завершения работы виртуальной машины можно удалить SATA CDROM 1:
Нюансы Windows.
- Интерфейс схож с KDE Plasma, но намного менее привлекательный и довольно неудобный, особенно меню запуска («Пуск») и файловый менеджер с его «десятиэтажной» верхней панелью (ужас-ужас). Всё это «приправляет» графический разнобой: мутные шрифты в одном месте и чёткие в другом; в файловом менеджере своё оформление окон, в панели управления другое, в настройках вообще третье. Вся эта «биполярная» картина создаёт удручающее впечатление, но пережить можно.
- «Из коробки» вас встретит ужасающее количество мусорных программ: от полностью никчёмных, навроде Paint 3D, OneDrive, ознакомительной версии MS Office и так далее, до рекламы в меню запуска («Пуск»). Вся эта «красота», если верить тестам, отбирает до 7% производительности в 3D(!) программах, создаёт невменяемое количество прерываний (interrupts) и очень любит устраивать зашкаливающую пилообразную нагрузку как для процессора, так и для накопителя. Поэтому первым делом вам предстоит избавиться от всего лишнего. Работа непростая, но необходимая.
- НИ В КОЕМ СЛУЧАЕ недопускайте обновления драйверов на видеокарту через менеджер обновлений Windows. Это может привести к полной неработоспособности Win10.
- Недопустимо, чтобы хост уходил в гибернацию и сон с запущенной виртуальной машиной. Это может привести к полному зависанию хоста или проброшенной видеокарты.
- На госте с Windows очень нежелательно применять гибернацию и сон, так как это может привести к зависанию проброшенной видеокарты.
- Образ виртуального накопителя в формате raw можно примонтировать, как обычный накопитель, что позволит производить чтение и запись хостом. При этом есть ряд важных ограничений, в особенности для файловой системы NTFS:
- Без крайней необходимости не осуществляйте запись от хоста на виртуальный накопитель. Одновременная запись хоста и гостя может привести к сбою файловой системы на виртуальном накопителе. Запись от хоста должна осуществляться только при выключенном(!) госте.
- Если работа гостя была завершена переходом в сон, гибернацию, сбоем или принудительным выключением, то запись от хоста даже при выключенном госте может повредить файловую систему.
- Восстановление файловой системы NTFS возможно Win-утилитой chkdsk, при этом повреждённые записи будут удалены с потерей затронутых файлов.
Проброс видеокарты Nvidia.
Базовый проброс видеокарты.
Осуществялется через Virt-manager.
Перед началом настройки необходимо завершить работу виртуальной машины.
Открыть настройки виртуальной машины и найти кнопку «Добавить оборудование». В списке выбрать видеоустройство видеокарты и аудиоустройство.
Для GTX 1060 результат выглядит так:
Установка драйвера Nvidia.
Подготовка гостя.
После зачистки Win10 и установки нормального браузера предстоит продолжить подготовку проброса для того, чтобы обойти препоны драйвера Nvidia.
Вручную загрузить драйвера для видеокарты с официального сайта: https://www.nvidia.ru/Download/
Для установки потребуется перейти в безопасный режим, иначе установка будет прервана BSOD («синим экраном смерти») и не завершится корректно. После такого сбоя потребуется удалять то, что успело установиться, специальной утилитой (DDU или подобной).
Переход в безопасный режим можно осуществить следующим образом:
- Нажать комбинацию клавиш META + R. В появившейся строке ввести msconfig и подтвердить.
- В открывшемся окне перейти на вкладку «Загрузка» и отметить «Безопасный режим»:
Перезагрузиться.
Визуально безопасный режим будет выглядеть подобным образом:
Теперь можно установить драйвер, не опасаясь BSOD.
Для этого запустить ранее загруженный установочный файл.
В установочнике отметить «Выборочную установку», чтобы установить только драйвер без дополнительного софта:
Ни в коем случае не устанавливать предлагаемый GeForce Experience. Это очень проблемный софт, который становится ещё проблемнее на виртуальной машине.
Перезагрузиться, всё ещё оставаясь в безопасном режиме.
Теперь предстоит настроить MSI (Message Signaled Interrupts). Без этого в обычном режиме запуска будет происходить зацикленный BSOD с ошибкой VIDEO_TDR_FAILURE в драйвере nvlddmkm.sys.
Включение MSI для видеокарты Nvidia.
Оптимально включить вручную через реестр, создав специальный параметр.
Сначала необходимо узнать ID видеокарты, по которому её можно найти в реестре.
META + R и в появившейся строке ввести devmgmt.msc, а затем подтвердить.
Это откроет окно «Диспетчера устройств»:
Развернуть свиток «Видеоадаптеры», нажать на видеокарте ПКМ и выбрать «Свойства».
В «Свойствах» перейти во вкладку «Сведения». В списке найти «ИД оборудования». Первым в списке будет искомый ID видеокарты. Для GTX 1060: VEN_10DE&DEV_1C03&SUBSYS_1C0310DE&REV_A1
Теперь можно редактировать реестр. Это осуществляется стандартной утилитой regedit.
META + R и в появившейся строке ввести regedit. Откроется окно редактора реестра:
Необходимо проследовать в самые дебри:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\PCI\
И вот здесь должен быть раздел с тем самым ID видеокарты. В данном примере это VEN_10DE&DEV_1C03&SUBSYS_1C0310DE&REV_A1
Развернуть раздел с ID видеокарты, нажать ПКМ на разделе Device Parameters и создать раздел Interrupt Management:
Нажать ПКМ на разделе Interrupt Management и создать раздел с именем MessageSignaledInterruptProperties:
Нажать ПКМ на созданном разделе и создать параметр DWORD (32 бита) с именем MSISupported:
Сделать двойной клик по параметру MSISupported и в появившемся окне присвоить значение равное 1 в шестнадцатеричной системе счисления:
Готово. Теперь можно закрыть редактор реестра.
Примечание: MSI может сбрасываться после обновления Win и драйвера для видеокарты. Поэтому придётся проделывать процедуру снова, но будет достаточно добавить параметр MSISupported, остальное должно остаться.
В msconfig включить обычный запуск:
Перезагрузиться.
Казалось бы, проделан такой большой путь и теперь уж точно всё должно работать, но не тут-то было. Nvidia так просто не сдаётся. Видеокарта всё ещё не будет работать из-за «ошибки 43»: «Система Windows остановила это устройство, так как оно сообщило что Nvidia в край обнаглела о возникновении неполадок».
Эта проблема решается маскировкой факта виртуализации для гостя.
Маскировка виртуализации для решения «ошибки 43».
Маскировка осуществляется в настройках виртуальной машины. Необходимо, чтобы гость был выключен.
Конфигурация виртуальной машины хранится в специальном файле в формате xml. Их можно редактировать с помощью утилиты virsh, но в данной статье ограничимся Virt-manager.
Редактирование конфига можно осуществить непосредственно через Virt-manager во вкладке XML (Edit libvrit XML). Пример настроенной конфигурации:
Маскировка разнесена на три блока:
- domain / features / hyperv
- domain / features / kvm
- domain / cpu
hyperv.
В этот блок нужно добавить следующую строку:
<
vendor_id state="on" value="FckYouNVIDIA/>
Целиком будет выглядеть так:
<hyperv>
<relaxed state="on"/>
<vapic state="on"/>
<spinlocks state="on" retries="8191"/>
<vendor_id state="on" value="FckYouNVIDIA"/>
</hyperv>
Строка vendor_id доступна для чтения из гостевой ОС. Драйвер Nvidia проверяет её наличие. Если она отсутствует, то это является одиним из поводов показать «ошибку 43».
Cтроке value может быть присвоен любой набор из 12 символов. В «классическом» варианте можно указать следующее: FckYouNVIDIA
kvm.
По умолчанию блок kvm отсутствует. Его необходимо самостоятельно добавить в блок features.
<kvm> <hidden state="on"/> </kvm>
Строка отвечает за опцию маскировки использования гипервизора kvm от обнаружения гостем. Драйвер Nvidia проверяет этот момент и при обнаружении признаков использования kvm покажет «ошибку 43».
cpu и host-passthrough.
Основной задачей является пробросить информацию о процессоре хоста, предоставить гостю возможность напрямую использовать процессорный кэш и отключить оптимизации гипервизора, которые явно указывают на то, что гостевая ОС запущена на виртуальной машине. Этот факт будет легко обнаружен драйвером Nvidia, что вновь приведёт к «ошибке 43».
По умолчанию этот блок будет выглядеть схожим образом:
<cpu mode="host-model" check="partial"> <topology sockets="1" cores="4" threads="2"/> </cpu>
Гостевой ОС будет предоставлено только название процессора без проброса детаельной информации и кэша. Это необходимо исправить.
Конечный результат будет выглядеть так:
<
cpu mode="host-passthrough" check="none">
<topology sockets="1" cores="4" threads="2"/>
<cache mode="passthrough"/>
<feature policy="require" name="invtsc"/>
<feature policy="disable" name="hypervisor"/>
</cpu>
Режим проброса указывается следующей строкой:
<
cpu mode="host-passthrough" check="none">
Далее указывается топология ядер процессора. В данном случае это 8 потоков (4 ядра умножаются на 2 потока, что имитирует гиперпоточность):
<topology sockets="1" cores="4" threads="2"/>
Проброс процессорного кэша:
<cache mode="passthrough"/>
Это значительно улучшает производительность гостевой ОС.
Опция invtsc улучшает обработку прерываний для Win-гостя, что позитивно сказывается на производительности:
<feature policy="require" name="invtsc"/>
Она не критична для маскировки, но очень желательна для улучшения работоспособности.
Отключение оптимизаций гипервизора:
<feature policy="disable" name="hypervisor"/>
Гостевая ОС не будет видеть, что запущена на виртуальной машине. Драйвер Nvidia тоже не сможет определить факт виртуализации, что наконец избавит от «ошибки 43».
Применить изменения.
Заключительная часть.
Наконец всё готово и видеокарта сможет свободно работать в гостевой ОС!
Ключевым признаком работы проброшенной видеокарты будет «зависание» картинки в окне просмотра Virt-manager. Если используется один монитор, то потребуется переключить видеорежим, чтобы увидеть графику гостевой ОС.
Рабочий стол будет отображаться в полноэкранном режиме:
Останется испытать производительность.
Рекомендую следующие программы для тестирования производительности:
- LatencyMon — предназначена для измерения задержек прерываний. Чем выше задержки, тем сильнее проявляются «заикания».
- Cinebench — тестирование производительности процессора.
- Unigine Superposition Benchmark — общее тестирование производительности.
Набор незамысловат, но позволяет ясно увидеть уровень производительности. Забегая вперёд скажу, что производительность будет заметно меньше, чем при нативном запуске, но это можно исправить, сократив накладные расходы до минимума.
Варианты оптимизации рассмотрены в отдельной статье: https://noostyche.ru/blog/2021/03/30/optimizaciya-virtualizacii-win10-qemu-kvm/
Проблема с отсутствием звука в виртуальной машине.
Как правило, при использовании протокола SPICE со звуком проблем нет, но бывают исключения.
Звук может отсутствовать из-за блокировки apparmor. Проверка наличия предупреждений о блокировке:
cat /var/log/syslog | grep -i 'apparmor="DENIED"'
Если блокировка отсутствует, то вывод будет пустой.
При наличии предупреждений, самым простым способом является отключение защиты через конфигурационный файл /etc/libvirt/qemu.conf. Защита будет отключена только для виртуальных машин, запускаемых с использованием libvirt.
За использование защиты отвечает следующая строка:
security_driver = [ "selinux", "apparmor" ]
Для отключения необходимо добавить строку со значением «none»:
security_driver = "none"
Для применения изменений перезапустить libvirtd:
sudo service libvirtd restart
Как заставить Intel GVT-g работать
Всем привет! Intel предложили отличное решение извечной проблемы: «у меня есть ноутбук на Linux и мне надо запускать Windows с аппаратным ускорением, но у меня нет тяжеленного ноутбука с двумя GPU и жидкостным охлаждением». С помощью архитектуры своих GPU или чего-то ещё им удалось сделать так, что вы можете разбить свой встроенный Intel GPU на два или более GPU.
К сожалению, это отнюдь не так просто… Документация слегка устарела, и некоторые вещи ломаются непонятным образом без очевидной причины. Поэтому в данном посте я расскажу вам, как настроить аппаратно ускоренную виртуальную машину с Windows с крутыми быстрыми драйверами virtio и Intel GVT-g.
Для этого вам понадобится более-менее современный GPU (Примечание переводчика: согласно официальной документации, GVT-g поддерживают интегрированные видеокарты, начиная с пятого поколения Intel Core и с четвёртого поколения Xeon).
Шаг 1: настраиваем ядро
Проверьте, что у вас свежая версия ядра. Похоже, что опции для GVT-g были включены и до версии 4.8, но тогда они точно работали хуже, поэтому я рекомендую использовать последнее доступное ядро. Если вы достаточно оригинальны, чтобы собирать своё ядро, включите эти опции. Также отключите удаление неиспользуемых ksyms, поскольку эта опция вызывает баг.
Теперь нужно поправить аргументы командной строки ядра. Важные опции таковы:
i915.enable_gvt=1 kvm.ignore_msrs=1 intel_iommu=on i915.enable_guc=0
Убедитесь, что вы не переопределили enable_guc=0
чем-то, что включит загрузку GuC, поскольку это приведёт к удручающему крашу драйвера i915. Когда загрузитесь, зайдите в своё рабочее окружение и проверьте наличие директории /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/
. Если её не существует, GVT-g не работает. Проверьте логи и/или поплачьте в подушку.
Как вариант решения проблемы можно добавить эти модули в initramfs и удалить оттуда i915.
Для более подробного логирования, можно задать переменной drm.debug
какое-нибудь значение, например, установка её в значение 0x02 включит сообщения от драйверов.
Шаг 2: создаём виртуального друга
Внутри mdev_supported_types
можно найти целый набор директорий. Этот набор определяется количеством вашей графической памяти, каждая поддиректория соответствует некоторому типу виртуального GPU. Файл description
в ней содержит информацию о памяти и разрешениях, поддерживаемых данным виртульным GPU. Если создание виртуального GPU с большой памятью с помощью вывода UUID в файл /create
вам выдаёт непонятную ошибку, то у вас есть несколько опций. Сперва стоит зайти в BIOS и добавить видеопамяти, если возможно. Если это не работает, можно остановить ваш DM, переключиться на фреймбуфер, создать нужный vGPU оттуда, а затем вернуться в x11. К сожалению, такой способ приводит ко многим багам и не даёт добиться 60 FPS на моём ноутбуке. Альтернативный вариант состоит в том, чтобы создать vGPU поменьше, и использовать специальную программу для увеличения разрешения (CRU). Таким способом мне удалось добиться 60 FPS и багов и зависаний встретилось гораздо меньше.
Создать vGPU можно такой командой:
$ echo ${vGPU_UUID} | sudo tee /sys/bus/pci/devices/0000:00:02.0/mdev_supported_types/${vGPU_TYPE}/create
А удалить — такой:
$ echo 1 | sudo tee /sys/bus/mdev/devices/${vGPU_UUID}/remove
Примечание переводчика:
Сгенерировать UUID для vGPU можно с помощью команды uuidgen
без аргументов. Переменная ${vGPU_TYPE} обозначает один из типов, перечисленных в директории mdev_supported_types
. Также стоит заметить, что vGPU при каждой перезагрузке надо создавать заново, они не сохраняются между запусками ОС.
Шаг 3: Cortana кричит на вас
На очереди существенно лучше поддерживаемая и существенно более медленная и болезненная вещь — установка Windows 10. Не стоит использовать торренты или неофициальную загрузку, или старую версию Windows, правильная ссылка здесь. Также стоит скачать образ диска с драйверами virtio, разработанными специально для ускорения гостей, здесь. Установите libvirt
и virt-manager
и запустите libvirtd
:
# systemctl start libvirtd
Запустите virt-manager
и убедитесь, что вы подключились к системной сессии libvirt
, а не пользовательской:
Примечание переводчика:
Для того, чтобы virt-manager мог подключиться к системной сессии, можно запустить его от имени суперпользователя, но лучше настроить авторизацию, например, как предложено на Arch Wiki.
Как только вы это сделали, можно приступить к созданию виртуальной машины. В диалоге настройки выберите загрузку с локального iso-образа и найдите скачанный образ. Если virt-manager
не распознал его, как образ Windows 10, выберите её вручную, поскольку это ускорит Windows, так как virt-manager
в этом случае предоставляет некоторые интерфейсы виртуализации от Microsoft. Создайте образ диска или LVM-раздел и настройте конфигурацию, как вам нужно. Интерфейс настройки перед установкой весьма ограничен, поэтому я обычно начинаю установку и сразу её останавливаю, чтобы полностью настроить всё. Вот некоторые настройки:
Примечание переводчика:
Несмотря на то, что пост о настройке виртуальной машины на ноутбуке, почему-то опущен вопрос насчёт раздачи сети с помощью беспроводного адаптера. Дело в том, что настройки сети по-умолчанию в virt-manager не годятся для беспроводной сети. В этой ситуации может помочь ответ на этот вопрос и комментарии к нему. Также автор предлагает использовать BIOS вместо UEFI, вероятно, из-за того, что UEFI требует дополнительной настройки. К тому же, похоже, tianocore ещё не работает с GVT-g, см. bug 935. В моём случае, впрочем, ВМ запускалась, но Windows не распознавала монитор, к которому подключена интегральная видеокарта.
Если вам нужен быстрый доступ к диску или даже способность виртуальной машины сжимать образ диска при удалении файлов виртуальной машины (проброс TRIM, для этого нужно создать образ командой qemu-img create -f qcow2 -o preallocation=metadata,lazy_refcounts
, полная инструкция здесь), настройте основной диск на использование SCSI. Для виртуальной машины понадобятся драйвера, чтобы Windows поняла этот формат, так что подключите ранее скачанный диск с драйверами virtio для Windows 10. По умолчанию они используют IDE, но вы можете ускорить установку в разы и задействовать меньше легаси-кода, если будете использовать вместо этого для dvd-дисков SCSI. Windows поддерживает это из коробки. Также вы можете:
- Заставить USB-шину использовать USB 3.0
- Добавить каналы spice, spice-webdav и qemu-ga, чтобы работали копирование и вставка и обмен файлами между VM и хостом
- Удалить неиспользуемое виртуальное оборудование
- Переключить эмулируемую видекарту на QXL и переключить дисплей на SPICE, НЕ слушающий сеть (даже loopback) (Примечание переводчика: иначе ВМ попросту крашится).
- Переключить тип микросхем на Q35, настройка рядом с настройками BIOS.
- Покормить собаку
Также вы можете начать привыкать к вашему новому другу virsh edit
. Если запустить его с помощью sudo -E
, ваши переменные окружения, в частности EDITOR, будут использованы для редактирования, при этом вы будете использовать системную сессию libvirt, а не пользовательскую. В этом файле можно, например, назначить соответствие между физическими и виртуальными процессорами, благодаря чему кэши процессоров более консистентны и планировщик ведёт себя не так странно. Вот пример XML, который вы можете туда поместить:
<vcpu placement='static'>6</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='1'/>
<vcpupin vcpu='1' cpuset='2'/>
<vcpupin vcpu='2' cpuset='3'/>
<vcpupin vcpu='3' cpuset='5'/>
<vcpupin vcpu='4' cpuset='6'/>
<vcpupin vcpu='5' cpuset='7'/>
</cputune>
<features>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
<runtime state='on'/>
<synic state='on'/>
<stimer state='on'/>
</hyperv>
</features>
<cpu mode='host-passthrough' check='none'>
<topology sockets='1' cores='3' threads='2'/>
</cpu>
В этом фрагменте я настраиваю ВМ так, чтобы она видела процессор с тремя физическими ядрами, каждое из которых имеет два гиперпотока. Далее, каждый процессор/гиперпоток прикрепляется к своему гиперпотоку, и это соответствие не меняется. Планировщик Windows знает о гиперпотоках и может их использовать корректно, не считая их отдельными процессорами. Также я включаю некоторые интерфейсы Hyper-V, которые по умолчанию выключены и могут не иметь эффекта. Если вы используете SPICE, можно добавить следующие строки, чтобы отключить сжатие, поскольку внешняя сеть для доступа к ВМ всё равно не используется.
<graphics type='spice'>
<listen type='none'/>
<image compression='off'/>
<jpeg compression='never'/>
<zlib compression='never'/>
<playback compression='off'/>
<streaming mode='off'/>
<!-- <gl enable='yes' rendernode='/dev/dri/by-path/pci-0000:00:02.0-render'/> пока что это не работает, этот комментарий будет удалён libvirt -->
</graphics>
Теперь можно настроить порядок загрузки и начать установку Windows. Если вы используете virtio
или SCSI-диск, Windows не найдёт его. Вам понадобится установить драйвер SCSI с диска, который вы подключили, они находятся в директории virtscsi/amd64
. Всё должно пройти гладко, и Windows должна загрузиться в медленном и убогом не ускоренном режиме. Cortana начнёт кричать на вас, а ваша сеть не будет работать. Прорвитесь через всё это к рабочему столу. Там запустите диспетчер устройств, найдите все неопознанные устройства, и обновите драйвера для них с диска, который вы подключили. Вы получите несколько более быструю Windows.
Шаг 4: Весёлая часть
Есть три способа получить ускоренный виртуальный дисплей VM с Windows на экране вашей машины.
- VNC или какой-то другой протокол удалённого доступа (обычно это весьма плохое решение). В этом варианте вам нужно только подключить vGPU и отключить все остальные дисплеи и видеокарты. Также выставьте настройку
display='off'
. Вам не нужна опцияigd-opregion
, показанная позже. - SPICE (у меня не получилось добиться 30 FPS или выше, но работает общий буфер обмена и передача файлов между VM и хостом).
- Встроенный интерфейс QEMU на GTK+ (общий буфер обмена и передача файлов не работают, но можно добиться 60 FPS с помощью патча).
Что бы вы ни собирались использовать, вам всё равно придётся использовать второй вариант, чтобы установить драйвера для GPU. Встроенные драйвера от Microsoft не очень хорошо работают с GVT-g на момент написания поста, и часто ломаются. До того, как вы подсоедините vGPU к ВМ, желательно скачать последнюю версию драйвера от Intel(Судя по всему, Intel меняет подход к распространению драйверов, так что в будущем этот шаг, возможно, будет другим, либо станет вообще не нужным). Теперь убедитесь, что у вас создан vGPU. Откройте virt-manager
и замените хороший быстрый QXL на медленный Cirrus во избежание конфликтов. Чтобы подключить vGPU к ВМ, нужно открыть virsh edit
и где-нибудь добавить такой фрагмент:
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
<hostdev mode='subsystem' type='mdev' managed='no' model='vfio-pci' display='on'>
<address uuid='fff6f017-3417-4ad3-b05e-17ae3e1a4615'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>
<rom enabled='no'/>
</hostdev>
<graphics type='spice'>
<listen type='none'/>
<image compression='off'/>
<jpeg compression='never'/>
<zlib compression='never'/>
<playback compression='off'/>
<streaming mode='off'/>
<gl enable='yes' rendernode='/dev/dri/by-path/pci-0000:00:02.0-render'/> <!-- Ваш путь к устройству может отличаться, virt-manager может проставить это за вас, если вы отметите чекбокс с аппаратным ускорением GL. Убедитесь, что тут прописан путь к устройству, а не auto. -->
</graphics>
<qemu:commandline>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev0.x-igd-opregion=on'/>
<!-- libvirt пока не поддерживает эту опцию, поэтому её надо выставить вручную -->
</qemu:commandline>
</domain>
Замечание: Когда я предоставляю XML-фрагмент вроде этого, вам по возможности следует добавить его к текущему, не заменяя ничего.
Проверьте, что вы создали уникальные UUID для всех vGPU, которые вы используете, и что номера слотов не конфликтуют ни с какими из остальных PCI-устройств. Если номер слота находится после Cirrus GPU, виртуальная машина упадёт. Теперь вы можете запустить виртуальную машину. Нужно установить virt-viewer
, чтобы увидеть оба дисплея! Подключиться к ВМ можно с помощью команды
$ sudo -E virt-viewer --attach
Один из дисплеев будет пустой или не инициализированный, второй — уже знакомый маленький не ускоренный дисплей. Раскройте его и, войдя, установите драйвер для GPU. Если вам повезёт, всё заработает сразу же. В противном случае, надо выключить и снова запустить ВМ (не перезагрузить) с помощью работающего экрана. Теперь самое время открыть терминал и запустить внутри dmesg -w
. Эта команда выдаст вам некоторую полезную информацию о проблемах и общем ходе работы с использованием vGPU. Например, при загрузке KVM будет жаловаться на заблокированные MSR, затем вы должны получить несколько сообщений о неправильном доступе, когда vGPU инициализируется. Если их слишком много — что-то не так.
Если система загрузилась, можно открыть настройки дисплея и отключить не ускоренный экран. Пустой экран можно скрыть через меню View в virt-viewer
. В принципе, ВМ уже можно использовать, но есть ещё пару вещей, которые можно сделать, чтобы добиться более высокого разрешения и более высокой скорости.
Утилита CRU весьма полезна. Можете поиграться с ней, и даже если вы наткнётесь на какие-то графические артефакты или даже почти целиком чёрный экран, как получилось у меня, вы можете запустить файл Restart64.exe
, идущий в комплекте с программой, чтобы перезагрузить графическую подсистему Windows. Лично я использую эту утилиту для использования более высокого разрешения на более скромном vGPU.
Чтобы добиться прекрасных 60 FPS, нужно переключиться на встроенный монитор QEMU на GTK+ без поддержки общего буфера обмена с хостом и подобных плюшек, а также изменить в нём одну строчку и пересобрать QEMU. Также понадобится добавить пачку противных аргументов командной строки в ваш XML. Удалите дисплей SPICE и видеокарту Cirrus и установите атрибут display
у вашего vGPU в off
(libvirt не поддерживает дисплей на GTK+ и не позволит загрузиться с display='on'
без дисплея).
<qemu:commandline>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev0.x-igd-opregion=on'/>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev0.display=on'/>
<qemu:arg value='-display'/>
<qemu:arg value='gtk,gl=on'/>
<qemu:env name='DISPLAY' value=':1'/>
<qemu:env name='GDK_SCALE' value='1.0'/>
</qemu:commandline>
Масштабирование для HiDPI у монитора QEMU работает из рук вон плохо, поэтому мы его отключим. Также, вам понадобится установить переменную DISPLAY
в тот номер дисплея, который вы используете. Чтобы дать пользователю, запускающему qemu, права доступа к X серверу, используйте команду:
# xhost si:localuser:nobody
Если это не сработало, попробуйте xhost +
, но убедитесь, что вы используете файрвол. Иначе попробуйте более безопасный метод.
При таких ухищрениях вы всё равно не получите выше 30 FPS из-за этого глупого бага в QEMU, если вы не пропатчите его, изменив строку, как указано в комментарии по ссылке. Убедитесь, что вы собрали только QEMU для x86-64, если только вы не собираетесь использовать его на другой платформе. Я прикрепил мой PKGBUILD, который не меняет строчку, а только лишь собирает QEMU для x86_64 без поддержки сетевого хранилища здесь.
Если вы потерялись где-то по пути, можете посмотреть мой текущий XML для libvirt.
Полезные ссылки
Официальный гайд по настройке GVT-g
Руководство пользователя по Dma-buf
Статья по настройке Intel GVT-g на NixOS Wiki
Статья на Arch Wiki про libvirt
Настройка сети в KVM на беспроводном интерфейсе
Сайт, посвящённый Intel GVT-g
P. S.: Спасибо aNNiMON за помощь в вычитке текста перевода и исправлении ошибок.
Running virtual machines is great for many things—testing software, running different OS environments, or even retro gaming. But sometimes, the default virtual graphics just doesn’t cut it, especially if you want to push your VM’s graphical limits. Enter GPU passthrough, an advanced virtualization feature that allows you to assign a dedicated graphics card directly to your virtual machine. With QEMU and a little bit of configuration, you can set up GPU passthrough and unlock near-native performance for graphically intensive tasks like gaming, video editing, and machine learning.
Let’s dive into how QEMU GPU passthrough works, why you’d want to set it up, and what steps you need to follow to get your VM running with GPU-boosted power!
What is QEMU GPU Passthrough?
In simple terms, GPU passthrough allows you to assign a physical GPU on your host system to a virtual machine, enabling the VM to access the GPU’s full capabilities directly. This is especially useful if you want to perform high-performance tasks in the VM, as it allows applications to use the GPU as if they were running on a standalone physical machine.
GPU passthrough works best with QEMU paired with KVM (Kernel-based Virtual Machine), a Linux-based virtualization tool that enables near-native performance. Using KVM with QEMU allows the virtual machine to utilize the host’s resources more efficiently, which makes a huge difference when handling demanding graphics tasks.
Why Use GPU Passthrough with QEMU?
There are many benefits to setting up GPU passthrough, particularly if you want to use your VM for more than basic software testing.
Use Case | Why GPU Passthrough Makes a Difference |
---|---|
Gaming | Enables high FPS, low-latency gaming on the VM |
Machine Learning | Leverages GPU for faster model training |
Video Editing | Provides the VM with hardware acceleration for rendering |
CAD/3D Modeling | Improves performance for graphic design applications |
Crypto Mining | Useful for testing mining setups without affecting the host |
With GPU passthrough, you can achieve much better graphics performance on a VM compared to standard virtualized GPU options.
Requirements for Setting Up GPU Passthrough
Setting up GPU passthrough is a bit more complex than running a regular VM, so make sure you meet these prerequisites:
- Hardware Virtualization: Your CPU must support virtualization extensions:
- Intel: VT-d for Directed I/O.
- AMD: AMD-Vi for IOMMU (Input-Output Memory Management Unit).
- GPU: Ideally, you’ll have two GPUs—one for the host system and one dedicated to the VM. This prevents your host from losing graphics capability when the GPU is passed through.
- Operating System: GPU passthrough works best on Linux with QEMU and KVM. You can use Ubuntu, Fedora, or any other modern Linux distribution that supports KVM.
- BIOS Settings: Enable virtualization and IOMMU support in your BIOS or UEFI settings. This is usually under “Advanced” or “Chipset” settings.
- Drivers and Configuration: Ensure your host has the appropriate GPU drivers and the VFIO (Virtual Function I/O) kernel modules installed to support passthrough.
Step-by-Step Guide to Setting Up GPU Passthrough with QEMU
Here’s a walkthrough on how to get GPU passthrough running with QEMU.
Step 1: Enable IOMMU in BIOS
- Restart your computer and enter BIOS (typically by pressing
Del
,F2
, orEsc
during startup). - Locate the virtualization settings. Enable Intel VT-d (for Intel) or AMD-Vi (for AMD) to activate IOMMU.
- Save the changes and reboot into your Linux OS.
Step 2: Configure IOMMU in Linux
Next, we need to configure IOMMU support in the Linux boot parameters.
- Open your GRUB configuration file:
sudo nano /etc/default/grub
- Find the line starting with
GRUB_CMDLINE_LINUX_DEFAULT
and add the appropriate IOMMU parameter:- For Intel:
intel_iommu=on
- For AMD:
amd_iommu=on
- For Intel:
- Save the file and update GRUB:
sudo update-grub
- Reboot your system to apply these changes.
Step 3: Identify Your GPU and Configure VFIO
We need to identify the GPU and load it for passthrough using the VFIO kernel module.
- List all PCI devices and find your GPU IDs:
lspci -nn
You should see lines like:01:00.0 VGA compatible controller [10de:1c82] (NVIDIA Corporation) 01:00.1 Audio device [10de:0fb9] (NVIDIA Corporation)
Note the device IDs (e.g.,10de:1c82
and10de:0fb9
). - Add these IDs to VFIO. Open the vfio.conf file:
sudo nano /etc/modprobe.d/vfio.conf
- Add the following line, replacing
XXXX:XXXX
with your device IDs:options vfio-pci ids=10de:1c82,10de:0fb9
- Update your initramfs to apply the changes:
sudo update-initramfs -u
Step 4: Launch QEMU with GPU Passthrough
Now we’re ready to launch the VM with GPU passthrough enabled. Here’s a sample command to start your VM:
qemu-system-x86_64 \
-enable-kvm \
-m 8192 \
-cpu host,hv_time,kvm=off \
-smp 4 \
-device vfio-pci,host=01:00.0,multifunction=on \
-device vfio-pci,host=01:00.1 \
-drive file=path_to_your_disk.qcow2,format=qcow2 \
-boot c \
-vga none
Explanation:
-enable-kvm
: Enables KVM for improved performance.-m 8192
: Allocates 8 GB of RAM to the VM.-cpu host,hv_time,kvm=off
: Exposes the host CPU to the VM and masks the KVM hypervisor (some applications check for hypervisor presence).-device vfio-pci,host=01:00.0,multifunction=on
: Passes through the GPU (replace01:00.0
with your GPU ID).-drive file=path_to_your_disk.qcow2,format=qcow2
: Specifies the virtual hard drive for the VM.-vga none
: Disables QEMU’s default VGA so that the VM fully uses the passed-through GPU.
Troubleshooting Common Issues
Setting up GPU passthrough can be challenging, so here are a few common issues and fixes.
Issue | Cause | Solution |
---|---|---|
No Video Output | The host is still using the GPU | Verify IOMMU/VFIO setup and check BIOS/UEFI settings |
VM Won’t Start | Conflicting drivers | Make sure vfio-pci is bound to the GPU, not other drivers |
Screen Flickering | Insufficient resources or incorrect drivers | Ensure adequate memory and that GPU drivers are correct |
IOMMU Not Detected | BIOS settings or unsupported hardware | Double-check BIOS and verify hardware support |
If issues persist, check your Linux distribution’s documentation on GPU passthrough or forums like Reddit’s VFIO or Level1Techs for troubleshooting tips.
QEMU GPU Passthrough vs. Traditional Virtualized Graphics
Curious how passthrough compares to standard virtualized graphics? Here’s a quick overview.
Feature | QEMU GPU Passthrough | Traditional Virtualized Graphics |
---|---|---|
Performance | Near-native performance | Moderate, often lagging |
Gaming Capability | High, supports AAA games | Limited |
Hardware Utilization | Direct hardware access | Shared resources with host |
Setup Complexity | High, requires IOMMU and VFIO | Simple |
Ideal Use Cases | Gaming, ML, video editing | General VM usage, office apps |
Final Thoughts on QEMU GPU Passthrough
GPU passthrough opens up a world of possibilities for your virtual machines, enabling them to perform as if they’re running on bare metal. While the setup process is involved, the performance gains for resource-intensive tasks make it well worth the effort. With QEMU and KVM, you get one of the most powerful virtualization environments available, allowing you to customize and optimize every aspect of your VM’s performance.
So, whether you’re setting up a gaming VM, experimenting with machine learning, or testing graphics-intensive software, QEMU’s GPU passthrough can make your virtual machine an impressive powerhouse. Happy virtualizing!
Бывает такой момент, что без Windows никак не обойтись, и даже wine не помогает, и тут к нам на помощь приходят виртуальный машины. Под Linux их хватает, VirtualBox, VMWare, XEN(гипервизор, но все же), qemu с kvm. Иногда нам необходима полноценная 3D графика, на виртуалке, и тут нам поможет проброс(passthrough) видеокарты в виртуалку.
В интернете полно статей, что да как делать, но интернет у нас большой и от еще одной статейки он не лопнет.
Для начала нам необходимо, что бы в компьютере было 2 видеокарты, например встроенная и дискретная. У меня основная видеокарта Geforce GXT 550ti, а прокидывал я GTX 650ti и Ati HD4850 и все успешно работало.
Существует 2 вида проброса, использовать OVMF, данный проект позволяет использовать UEFI в виртуалке, но и видеокарта должна быть не простая, а аж с двумя биосами, это можно выяснить следующим образом [ http://vfio.blogspot.ru/2014/08/does-my-graphics-card-rom-support-efi.html ].
Скачиваем, компилируем
git clone https://github.com/awilliam/rom-parser cd rom-parser make
Получаем BIOS видекарты
cd /sys/bus/pci/devices/0000:01:00.0/ echo 1 > rom cat rom > image.rom echo 0 > rom
0000:01:00.0 — надо изменить на код вашего девайса.
Проверяем BIOS ./rom-parser image.rom
Если вы видите подобное PCIR: type 3(EFI ROM), то ваша видеокарта поддерживает OVMF.
Valid ROM signature found @0h, PCIR offset 190h PCIR: type 0, vendor: 10de, device: 1280, class: 030000 PCIR: revision 0, vendor revision: 1 Valid ROM signature found @f400h, PCIR offset 1ch PCIR: type 3, vendor: 10de, device: 1280, class: 030000 PCIR: revision 3, vendor revision: 0 EFI: Signature Valid Last image
Ваш конфиг виртуалки будет немножко отличаться.
Я поэкспериментировал с ядрами, ни к чему интересному и хорошему это не привело, писали [https://bbs.archlinux.org/viewtopic.php?id=203240] что в ядре 4.2.2-1 поломали проброс, но как оказалось все прекрасно работает.
Но если же у вас все же есть желание, то устанавливайте из AUR’a:
1. [ https://aur.archlinux.org/packages/linux-vfio/ ] — на данный момент типа багнутое ядро, где сломан проброс, но используются полезные патчи для видюх интел и еще что-то там
2. [ https://aur.archlinux.org/packages/linux-vfio-lts/ ] — рабочее ядро, с теми же патчами, что и выше.
Как пользоваться makepkg, разберетесь сами.
1. Устанавливаем необходимый софт pacman -S qemu libvirt synergy
2. Нужно подкорректировать загрузку ядра, что бы не подцеплялась, наша видеокарта которую мы будем прокидывать. Нужно узнать id вендора и кода нашей видеокарты. Для этого выводим lspci
02:00.0 VGA compatible controller: NVIDIA Corporation GK106 [GeForce GTX 650 Ti] (rev a1) 02:00.1 Audio device: NVIDIA Corporation GK106 HDMI Audio Controller (rev a1)
Находим там код видеокарты, запоминаем его, и вводим уже lspci -n
02:00.0 0300: 10de:11c6 (rev a1) 02:00.1 0403: 10de:0e0b (rev a1)
Теперь правим параметры загрузки grub, для этого открываем /etc/default/grub
и добавляем параметр в GRUB_CMDLINE_LINUX_DEFAULT vfio-pci.ids=10de:11c6,10de:0e0b
У меня следующего вида
GRUB_CMDLINE_LINUX_DEFAULT="noresume iommu=noaperture vfio-pci.ids=10de:11c6,10de:0e0b"
Применяем параметры grub-mkconfig -o /boot/grub/grub.cfg
, после перезагрузки, должно быть нечто подобное dmesg | grep vfio_pci
:
[ 0.881226] vfio_pci: add [10de:11c6[ffff:ffff]] class 0x000000/00000000 [ 0.881257] vfio-pci 0000:02:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=none [ 0.893270] vfio_pci: add [10de:0e0b[ffff:ffff]] class 0x000000/00000000 [ 2.496137] vfio-pci 0000:02:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=none
Подкорректируем /etc/mkinitcpio.conf:
... MODULES="vfio vfio_iommu_type1 vfio_pci vfio_virqfd" ...
Применим mkinitcpio -p linux
(linux изменить на имя вашего ядра)
3. Настраиваем и создаем виртуалку.
Подправим конфиг qemu
/etc/libvirt/qemu.conf:
... user = "root" group = "root" clear_emulator_capabilities = 0 ... cgroup_device_acl = [ "/dev/null", "/dev/full", "/dev/zero", "/dev/random", "/dev/urandom", "/dev/ptmx", "/dev/kvm", "/dev/kqemu", "/dev/rtc","/dev/hpet", "/dev/vfio/vfio", "/dev/vfio/1" ] ...
В принципе действия для того что бы использовать ovmf те же, так вот если вам повезло и у вас видеокарта поддерживает EFI, то следующий конфиг для вас:
pacman -S ovmf
qemu-system-x86_64 -enable-kvm -m 1024 -cpu host,kvm=off \ -smp 4,sockets=1,cores=4,threads=1 \ -device virtio-scsi-pci,id=scsi \ -drive file=/Win/windows.img,id=disk,format=raw,if=none -device scsi-hd,drive=disk \ -drive file=/Win/virtio-win.iso.1,id=virtiocd,if=none -device ide-cd,bus=ide.1,drive=virtiocd \ -drive file=/Win/ru_windows_7_3in1_x64_by_AG_09.2015.iso,id=isocd,if=none -device scsi-cd,drive=isocd \ -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/ovmf_x64.bin \ -vga std \ #-device vfio-pci,host=02:00.0,x-vga=on -device vfio-pci,host=02:00.1 \
Если же нет, используем Seabios
qemu-system-x86_64 -enable-kvm -m 1024 -cpu host,kvm=off \ -smp 4,sockets=1,cores=4,threads=1 \ -device virtio-scsi-pci,id=scsi \ -drive file=/Win/windows.img,id=disk,format=raw,if=none -device scsi-hd,drive=disk \ -drive file=/Win/virtio-win.iso.1,id=virtiocd,if=none -device ide-cd,bus=ide.1,drive=virtiocd \ -drive file=/Win/windows.iso,id=isocd,if=none -device scsi-cd,drive=isocd \ -vga std \ #-device vfio-pci,host=02:00.0,x-vga=on -device vfio-pci,host=02:00.1 \
windows.img — образ жесткого диска, создается командой dd if=/dev/zero of=windows.img bs=1M seek=60000 count=0
seek=60000 размер диска в мегабайтах.
virtio-win.iso — образ с драйверами, берется последующей ссылки [ https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win.iso ]
windows.iso — образ диска с Windows, сами разберетесь где его брать, если используете ovmf убедитесь, что образ поддерживает загрузку UEFI. и при загрузке системы скорее всего надо будет ввести команду следующего вида: PS0:/EFI/BOOT/bootx64.efi
Данные конфиги для начальной установки Windows и все должно пройти на отлично.
3. После окончания установки, настало время прокинуть видеокарту.
Раскоментируем строку в конфиге
-device vfio-pci,host=02:00.0,x-vga=on -device vfio-pci,host=02:00.1 \
и запустим виртуалку.
Если все прошло хорошо, должно определиться новое устройство, скачиваем и устанавливаем драйвера для видеокарты, я не ставил никакого лишнего софта, только драйвера, пишут что Catalyst control center да и nvidia experience загоняет в синий экран, не проверял.
Нужно не забыть установить synergy, без этой программы управление(мышкой и клавиатурой) виртуалкой будет невозможно.
Найдете ее на торрентах или прочих сайтах, там в настройках надо указать client: 10.0.2.2(дефолтный ip хоста) и имя экрана, допустим Windows. Теперь настроим synergy на хосте(компьютер с Linux), создадим конфиг следующего содержания:
synergy.conf
# Example config section: screens Windows: halfDuplexCapsLock = false halfDuplexNumLock = false halfDuplexScrollLock = false xtestIsXineramaUnaware = false switchCorners = none switchCornerSize = 0 Linux: halfDuplexCapsLock = false halfDuplexNumLock = false halfDuplexScrollLock = false xtestIsXineramaUnaware = false switchCorners = none switchCornerSize = 0 end section: aliases Windows: 10.0.2.15 # default for vm Linux: 10.0.2.2 # default for host end section: links Windows: right = Linux Linux: left = Windows end section: options relativeMouseMoves = true screenSaverSync = true win32KeepForeground = false switchCorners = none switchCornerSize = 0 keystroke(f12) = lockCursorToScreen(toggle) end
Windows и Linux это имена экранов, измените на ваши
Запустим synergy synergys --config synergy.conf --debug INFO
, и при передвижение мышки за левую часть экрана, она должна переходить на виртуалку.
Если все получилось, выключаем виртуальную машину, в конфиге меняем строку -vga std
на -vga none
, подключаем к выходу проброшенной видеокарты кабель к монитору, запускаем виртуалку и у нас на экране должна появится картинка с процессом загрузки. Проверяем функциональность synergy, кнопка F12 блокирует курсор на текущем экране.
4. Прокидываем звук, если вы используете pulseaudio просто укажите перед запуском quemu QEMU_AUDIO_DRV=pa
если же ALSA, то предлагаю вам использовать утилиту apulse, она есть в AUR [ https://aur.archlinux.org/packages/apulse/ ] и запуск виртуалки будет следующим QEMU_AUDIO_DRV=pa apulse qemu-system-x86_64.....
5. У меня 2 монитора, и при запуске виртуалки на главный монитор(центральный) через vga идет картинка с Windows. а правый становится главным Linux’овым монитором.
К основной видеокарте правый монитор подключен через DVI, центральный монитор подключен к «виртуальной» видюхе через VGA, а к основной через HDMI.
Команда xrandr --output DVI-I-1 --auto && xrandr --output HDMI-0 --off
Ваши подключенные мониторы и возможные режимы работы можно посмотреть командой xrandr
Выклаываю полный скрипт запуска виртуалки
xrandr --output DVI-I-1 --auto && xrandr --output HDMI-0 --off synergys --config synergy.conf -d INFO > synergy.log apulse qemu-system-x86_64 -enable-kvm -m 3048 -cpu host,kvm=off \ -smp 6,cores=6,threads=1,sockets=1 \ -device virtio-scsi-pci,id=scsi \ -drive file=/Win/windows.img,id=disk,format=raw,if=none -device scsi-hd,drive=disk \ -drive file=/Win/virtio-win.iso.1,id=virtiocd,if=none -device ide-cd,bus=ide.1,drive=virtiocd \ -rtc base=localtime \ -soundhw hda \ -vga none \ -device vfio-pci,host=02:00.0,multifunction=on,x-vga=on xrandr --output HDMI-0 --auto && xrandr --output DVI-I-1 --off -soundhw hda \ -audiodev pa,id=pa,server=/run/user/1000/pulse/native kill -9 `pgrep synergys`
Так же рекомендую отключить запрос на восстановление системы при загрузке после сбоев
bcdedit /set {current} bootstatuspolicy ignoreallfailures bcdedit /set {current} recoveryenabled no
В данную тему я нырнул лишь ради интереса и возможности поиграть в ММО Black Desert, отлично кстати работает.
UPD 11.10.2016
Сегодня вновь столкнулся с пробросом видеокарты, но теперь мне нужен был еще и звук по hdmi, немного погуглив нашел решение http://vfio.blogspot.ru/2014/09/vfio-interrupts-and-how-to-coax-windows.html?m=1
Что бы нормально работал звук через HDMI проброшенный видюхи нужно включить Message Signaled Interrupts(MSI).
Для этого в реестре Windows необходимо внести правки, а именно в разделе HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ найти вашу видеокарту, там будет два устройства видео и звук, далее в Device Parameters\Interrupt Management создать(если его нет) раздел MessageSignaledInterruptProperties и в нем DWORD параметр MSISupported со значением 1, он включит MSI. После перезагрузки звук должен прекрасно заработать.
UPD 01.06.2019
Обновил компьютер до Ryzen и столкнулся с проблемой
vfio_bar_restore: 0000:0a:00.1 reset recovery - restoring bars vfio-pci 0000:0a:00.0: enabling device (0000 -> 0003) qemu-system-x86_64: vfio: Unable to power on device, stuck in D3
Подробности здесь AMD Ryzen и проблемы с пробросом видеокарты в QEMU KVM
UPD 15.09.2019
После очередного обновления начались неприятности, qemu падал в core-dump при попытке издать малейший звук, решилась эта проблема добавлением строки
-audiodev pa,id=pa,server=/run/user/1000/pulse/native
1000 — id вашего юзера, все переменные QEMU_AUDIO можно убрать.
Если будет писать что нет доступа
pulseaudio: Wrong context state pulseaudio: Reason: Access denied pulseaudio: Failed to initialize PA contextaudio: Could not init `pa' audio driver audio: warning: Using timer based audio emulation
Можно скопировать пользовательские cookie в root
cp ~/.config/pulse/cookie /root/.config/pulse/cookie
На 15.09 проблема с Ryzen еще присутствует, приходится патчить ядро…
GPU passthrough is a technology that allows the Linux kernel to directly present an internal PCI GPU as-is for direct use by a virtual machine.
The device acts as if it were directly driven by the VM, and the VM detects the PCI device as if it were physically connected. GPU passthrough is also often known as IOMMU, although this is a bit of a misnomer, since the IOMMU is the hardware technology that provides this feature but also provides other features such as some protection from DMA attacks or ability to address 64-bit memory spaces with 32-bit addresses.
The most common application for GPU passthrough is gaming, since GPU passthrough allows a VM direct access to the graphics card with the end result of being able to play games with nearly the same performance as bare metal running the operating system.
QEMU (Quick EMUlator) is a generic, open source hardware emulator and virtualization suite.
Note
This article typically uses KVM as the accelerator of choice due to its GPL licensing and availability. Without KVM nearly all commands described here will still work (unless KVM specific).
Installation
BIOS and UEFI firmware
In order to utilize KVM either VT-x or AMD-V must be supported by the processor. VT-x or AMD-V are Intel and AMD’s respective technologies for permitting multiple operating systems to concurrently execute operations on the processors.
To inspect hardware for virtualization support issue the following command:
user $
grep --color -E "vmx|svm" /proc/cpuinfo
For a period of time, manufacturers were shipping with virtualization turned off by default in the system BIOS.
Hardware
- A CPU that supports Intel VT-d or AMD-Vi. Check List of compatible Intel CPUs (Intel VT-x and Intel VT-d).
- A motherboard that supports the aforementioned technologies. To find this out, check in your motherboard’s BIOS configuration for an option to enable IOMMU or something similar. Chances are that your motherboard will support it if it’s from 2013 or newer, but make sure to check since this is a niche technology and some manufacturers may save costs by axing it from their motherboards or delivering a defective implementation (such as Gigabyte’s 2015-2016 series) simply because NORPs never use it.
- At least two GPUs: one for your physical OS, another for your VM. (You can in theory run your computer headless through SSH or a serial console, but it might not work and you risk locking the user away from your computer if done so).
- Optional but recommended: Additional monitor, keyboard and mouse.
EFI configuration
Go into BIOS (EFI) settings and turn on VT-d and IOMMU support.
Note
VT-d and Virtualization configuration params are same
Note
Some EFIs do not have IOMMU configuration settings
IOMMU
IOMMU – or input–output memory management unit – is a memory management unit (MMU) that connects a direct-memory-access–capable (DMA-capable) I/O bus to the main memory. The IOMMU maps a device-visible virtual address (I/O virtual address or IOVA) to a physical memory address. In other words, it translates the IOVA into a real physical address.
In an ideal world, every device would have its own IOVA address space and no two devices would share the same IOVA. But in practice this is often not the case. Moreover, the PCI-Express (PCIe) specifications allow PCIe devices to communicate with each other directly, called peer-to-peer transactions, thereby escaping the IOMMU.
That is where PCI Access Control Services (ACS) come to the rescue. ACS is able to tell whether or not these peer-to-peer transactions are possible between any two or more devices, and can disable them. ACS features are implemented within the CPU and the chipset.
Unfortunately, the implementation of ACS varies greatly between different CPU or chip-set models.
IOMMU kernel configuration
To enable IOMMU support in kernel:
KERNEL
Device Drivers ---> [*] IOMMU Hardware Support ---> Generic IOMMU Pagetable Support ---- [*] AMD IOMMU support <*> AMD IOMMU Version 2 driver [*] Support for Intel IOMMU using DMA Remapping Devices [*] Support for Shared Virtual Memory with Intel IOMMU [*] Enable Intel DMA Remapping Devices by default [*] Support for Interrupt Remapping
If the kernel has CONFIG_TRIM_UNUSED_KSYMS (Trim unused exported kernel symbols) enabled, then there will be a need to whitelist some symbols. Otherwise, error messages of the form Failed to add group <n> to KVM VFIO device: Invalid argument may occur. See the gentoo forum thread kernel 4.7.0 breaks pci passthrough [SOLVED] and the kvm mailing list thread KVM/VFIO passthrough not working when TRIM_UNUSED_KSYMS is enabled (list of symbols to whitelist in the second post).
KERNEL
[*] Enable loadable module support ---> [*] Trim unused exported kernel symbols (/path/to/whitelist) Whitelist of symbols to keep in ksymtab
FILE /path/to/whitelist
vfio_group_get_external_user vfio_external_group_match_file vfio_group_put_external_user vfio_group_set_kvm vfio_external_check_extension vfio_external_user_iommu_id mdev_get_iommu_device mdev_bus_type
Rebuild the kernel.
Editing the kernel parameters
Now, to enable IOMMU in the kernel, you must enable several kernel command-line parameters. Please add the parameters iommu=pt intel_iommu=on pcie_acs_override=downstream,multifunction
to your bootloader’s configuration file or your /etc/kernel/cmdline.
Then, rebuild the configuration if on GRUB, build a new UKI if you use unified kernel images, or whatever procedure must be done for your bootloader to ensure the new kernel parameters are going to be used.
Note
If the system hangs after rebooting, check the UEFI\BIOS IOMMU settings.
Verify IOMMU has been enabled and is operational:
user $
dmesg | grep 'IOMMU enabled'
[ 0.000000] DMAR: IOMMU enabled
Note
For CPUs on the XEN architecture, run:
user $
lspci -vv | grep -i 'Access Control Services'
IOMMU groups
Passing through PCI or VGA devices requires you to pass through all devices within an IOMMU group. The exception to this rule is PCI root devices that reside in the same IOMMU group with the device(s) we want to pass through. These root devices cannot be passed through as they often perform important tasks for the host. A number of (Intel) CPUs, usually consumer-grade CPUs with integrated graphics (IGD), share a root device in the same IOMMU group as the first PCIe 16x slot.
user $
for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*}; printf 'IOMMU Group %s ' "$n"; lspci -nns "${d##*/}"; done;
... IOMMU Group 13 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1080] [10de:1b80] (rev a1) IOMMU Group 15 02:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon Pro WX 7100] [1002:67c4] IOMMU Group 16 02:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 580] [1002:aaf0] ...
Nvidia in IOMMU Group 13 and AMD Video Card in IOMMU group 15 and 16. Everything looks fine. But if you have buggy IOMMU support and all devices within one IOMMU group, hardware can’t guarantee good device isolation. Unfortunately, it is not possible to fix that. The only workaround is to use ACS override patch which ignores the IOMMU hardware check. See ACS override patch (Arch Wiki).
VFIO
Kernel drivers:
KERNEL
Device Drivers ---> <M> VFIO Non-Privileged userpsace driver framework ---> [*] VFIO No-IOMMU support ---- <M> VFIO support for PCI devices [*] VFIO PCI support for VGA devices < > Mediated device driver framework
Search for VGA card IDs and audio device. Run:
root #
lspci -nn
... 04:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Vega 10 XL/XT [Radeon RX Vega 56/64] [1002:687f] (rev c1) 04:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Device [1002:aaf8] ..
Add PCI IDs for both VGA and audio to VFIO:
FILE /etc/modprobe.d/vfio.conf
options vfio-pci ids=1002:687f,1002:aaf8
Loading KVM and VFIO kernel modules at boot (systemd):
FILE /etc/modules-load.d/vfio-pci.conf
vfio vfio_iommu_type1 vfio_pci kvm kvm_intel
libvirt
Ensure you have virt-manager installed.
If you need SPICE or USB redirection support (which depends on SPICE), enable the spice and/or usbredir USE flags.
Windows
- Create a Windows 10/11 VM and install Windows as per usual with virt-manager.
- Edit the VM in virt-manager (open the VM and then either click the «light bulb» icon or select «View» -> «Details» from the menu).
- At the bottom left corner of the window, click the «Add Hardware» button. (If you don’t see buttons at the bottom of the screen, ensure your host screen resolution is higher than 1024×768).
- Select «PCI Host Device» from the left side.
- Locate the graphics card device(s) you exported via VFIO earlier (e.g., «NVIDIA Corporation GM200GL [Tesla M40]», «AMD/ATI Vega 64», or «AMD/ATI Device»). AMD GPUs have two devices on the PCIe bus, one with video and one with audio output. Windows drivers will only work if both of them are passed to Windows.
- Click the «Finish» button.
- Click the «Apply» button.
- Attempt to boot the VM.
- Inside Windows: open Device Manager to confirm that the device is appearing.
- Install the appropriate graphics drivers.
If booting is unsuccessful or drivers don’t load in Windows with an error code like 12 or 34, see «Troubleshooting» section at the bottom of this document.
Sound
root #
mkdir /home/qemu
root #
cp /home/<user>/.config/pulse /home/qemu
root #
chown qemu:qemu -R /home/qemu
Change the home directory for the qemu user:
root #
usermod -d /home/qemu qemu
Input Devices
One of the easiest ways of dealing with mouse and keyboard issues when using passthrough is through evdev proxy. This allows the ability to switch the mouse and keyboard between the guest and host with special key combinations. First, identify the mouse and keyboard in /dev/input. The easiest way to do this is through the symlink found in /dev/input/by-id/.
user $
ls -l /dev/input/by-id/*-event-{k,m}*
This a list of symlinks to event devices limited to mouse and keyboard entries. In order to access these nodes, either add the user Qemu runs as in the input group or, if using libvirt, edit /etc/libvirt/qemu.conf looking for
FILE /etc/libvirt/qemu.conf
cgroup_device_acl = [ ... ]
Add the symlinks and then restart libvirtd. Next, edit the XML libvirt uses for the domain. Do this by either through virsh or using virt-manager. With virt-manager, select the XML tab in the Overview option at the top of the device tree. With virsh, enter interactive:
user $
virsh --connect qemu:///system
Welcome to virsh, the virtualization interactive terminal. Type: 'help' for help with commands 'quit' to quit
virsh #
list --all
virsh #
edit $DOMAIN
Within the XML tree under the <devices> node, add the following lines
CODE
<input type="evdev"> <source dev="/dev/input/by-id/$YOURMOUSE-event-mouse"/> </input> <input type="evdev"> <source dev="/dev/input/by-id/$YOURKEYBOARD-event-kbd" grab="all" repeat="on"/> </input>
By default, the key combination to change input between host and guest is both Ctrl keys. If multiple GPUs have been passed through to multiple VMs, use the grabToggle argument to change the combination to a fixed set of key combinations that can be found in the Libvirt documentation.
QEMU
In case someone wants to use QEMU directly, here are some configurations to get started. In general, as a typical QEMU call will usually require many command-line flags, it is typically advised to place the QEMU call in a bash script and to run it that way. Don’t forget to make the script file executable!
Minimal
This minimal configuration will simply boot into the BIOS — there aren’t any drives connected, so there is nothing else for QEMU to do. However, this allows us to verify that the GPU passthrough is actually working.
FILE MinimalPassthrough.sh
#!/bin/bash virsh nodedev-detach pci_0000_09_00_0 virsh nodedev-detach pci_0000_09_00_1 qemu-system-x86_64 \ -nodefaults \ -enable-kvm \ -cpu host,kvm=off \ -m 8G \ -name "BlankVM" \ -smp cores=4 \ -device pcie-root-port,id=pcie.1,bus=pcie.0,addr=1c.0,slot=1,chassis=1,multifunction=on \ -device vfio-pci,host=09:00.0,bus=pcie.1,addr=00.0,x-vga=on,multifunction=on,romfile=GP107_patched.rom \ -device vfio-pci,host=09:00.1,bus=pcie.1,addr=00.1 \ -monitor stdio \ -nographic \ -vga none \ $@ virsh nodedev-reattach pci_0000_09_00_0 virsh nodedev-reattach pci_0000_09_00_1
Here’s an explanation of each line:
-nodefaults
stops qemu from creating some default devices. Specifically, it creates a VGA device by default, which interferes with our attempt to pass through the video card (in a multi-video card host system this may not be an issue)-enable-kvm
enables acceleration-cpu host, kvm=off \
this makes the virtual machine match the CPU architecture of the host.kvm=off
hides the KVM signature from the guest.-m 8G
give the guest 8 gigabytes of RAM-name "BlankVM"
Gives the virtual machine a name-smp cores=4
how many cores the guest should have.-device pcie-root-port,id=pcie.1...
a dedicate root port other than pcie.0 is required by amd gpu for windows driver-device vfio-pci,host=09:00.0...
add a device using vfio-pci kernel module, from the host’s address «09:00.0»...addr=..
video must on .0 and audio on .1 while both video and audio must be on the same pci-root-port other than pcie.0...x-vga=on
this is an option for the vfio-pci module (citation needed)...multifunction=on
since our card is doing both audio and video, it needs multifunction...romfile=GP107_patched.rom
due to known issues on NVIDIA cards, it may be necessary to use a modified vbios. This is how you make qemu use that modified vbios.-device vfio-pci,host=09:00.1
just like above — this is the audio device that is in the same IOMMU group as the video device.-monitor stdio
this will drop you into a qemu «command line» (they call it a monitor) once you launch the VM, allowing you to do things.-vga none
this is probably redundant.
As noted above, there are certain known issues with NVIDIA drivers. I used this tool to patch my vbios, after first downloading my vbios in windows 10 using this gpuz tool.
Linux Guest
Here is a slightly more complicated qemu call, that actually loads a Gentoo VM.
FILE GentooPassthrough.sh
#!/bin/bash exec qemu-system-x86_64 \ -nodefaults \ -enable-kvm \ -cpu host,kvm=off,hv_vendor_id=1234567890ab \ -m 8G \ -name "Gentoo VM" \ -smp cores=4 \ -boot order=d \ -drive file=Gentoo_VM.img,if=virtio \ -monitor stdio \ -serial none \ -net nic \ -net user,hostfwd=tcp::50000-:22,hostfwd=tcp::50001-:5900,hostname=gentoo_qemu \ -nographic \ -vga none \ -device vfio-pci,host=09:00.0,x-vga=on,multifunction=on,romfile=GP107_patched.rom \ -device vfio-pci,host=09:00.1 \ -usb \ -device usb-host,vendorid=0x1532,productid=0x0101,id=mouse \ -device usb-host,vendorid=0x04f2,productid=0x0833,id=keyboard \ $@
Here is an explanation of the new configuration options:
...hv_vendor_id=...
despite the patched vbios, the NVIDIA driver still recognized that it is being run in a virtual machine and refuses to load. This «spoofs» the vendor id (somewhere) and tricks the driver-boot order=d
boot the hard drive first-drive file=Gentoo_VM.img,if=virtio
this is a drive that is emulated in the VM. The «Gentoo_VM.img» file is a qcow QEMU-style virtual drive file.-serial none
May no longer be required.-net nic
create a Ethernet in the guest vm-net user,hostfwd...
forwards the ports from host 50000 and 50001 to the guest ports 22 and 5900. Now, from the host, you can ssh into the guest usingssh -p 50000 myuser@127.0.0.1
, and if a vnc server running in the guest on port 5900, it can access it using port 50001 in the host-nographic
this may not be needed if you have a dedicated graphics card for the guest-usb
emulate a USB device on the guest-device usb-host,...
these two lines forward the keyboard and mouse from the host to the guest. The vendorid and productid can be found using lsusb in the host.
Please note that without the hv_vendor_id
portion, a user can boot in and use the console in the guest with the forwarded graphics card. But whenever X is launch, which initialized the proprietary NVIDIA driver, it will fail.
The following example does not work with latest nvidia-drivers. The latest driver where it works for me is NVIDIA-Linux-x86_64-470.63.01.run. Here is a little variation of the above qemu script for Gentoo host and Gentoo guest. It uses separate CPUs for the guest. Works on a notebook with Ryzen CPU, where the 2nd NVIDIA GPU is passed through to the guest. The guest runs the NVIDIA driver. Installation is performed according to the Gentoo installation guide using UEFI and a GPT partition table. It uses no custom ROMs.
FILE gentooPassthrough.sh
#!/bin/bash name=genpass pid="${$}" cpus="8-15" ncpus=8 cgrouprootfs="/sys/fs/cgroup" cgroupfs="${cgrouprootfs}/${name}" echo "PID: ${pid}" # using separate CPUs for VM # cgroup usage see https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html # 'lscpu -e' to see which cpus to use echo "+cpuset" > ${cgrouprootfs}/cgroup.subtree_control mkdir -p ${cgroupfs} echo ${cpus} > ${cgroupfs}/cpuset.cpus echo "root" > ${cgroupfs}/cpuset.cpus.partition echo "${pid}" > ${cgroupfs}/cgroup.procs # setting performance governor for QEMU CPUs for i in `seq 8 15` ; do echo performance >/sys/devices/system/cpu/cpu${i}/cpufreq/scaling_governor done qemu-system-x86_64 \ -M q35 \ -monitor stdio \ -bios /usr/share/edk2-ovmf/OVMF_CODE.fd \ -accel kvm,kernel-irqchip=on \ -cpu host,kvm=off \ -smp ${ncpus} \ -m 4G \ -name "${name}" \ -device vfio-pci,host=01:00.0,multifunction=on \ -device vfio-pci,host=01:00.1 \ -nographic \ -vga none \ -serial none \ -parallel none \ -hda hda.qcow2 \ -usb \ -device usb-host,vendorid=0x046D,productid=0xC52B \ $@ # removing cgroup cpuset echo "${pid}" > ${cgrouprootfs}/cgroup.procs rmdir ${cgroupfs} # setting schedutil governor for qemu cpus for i in `seq 8 15` ; do echo schedutil >/sys/devices/system/cpu/cpu${i}/cpufreq/scaling_governor done
The kernel of the Gentoo host has been build with genkernel —virtio all. The NVIDIA GPU has been bound to vfio-pci with /etc/modprobe.d/local.conf on the host:
FILE /etc/modprobe.d/local.conf
alias pci:v000010DEd00001F95sv0000103Csd000087B2bc03sc00i00 vfio-pci alias pci:v000010DEd000010FAsv0000103Csd000087B2bc04sc03i00 vfio-pci options vfio-pci ids=10de:1f95,10de:10fa
This way the internal graphic of the Ryzen processor shows the host on the laptop display, Gentoo guest is displayed on the monitor connected to the HDMI of the NVIDIA graphic. To get sound in the VM, i have to replug the HDMI cable after the VM has booted. Maybe this issue is related to the HDMI cable or the external monitor.
Using Multiple Monitors
An example setup is:
980ti -> Gentoo Host
1650 -> Kali VM
3090 -> Windows 10 VM
This example uses six displays and often want to rotate between guests. If the monitors are able to auto switch to the active link then this will work. For example, to turn off the main display for Linux and switch to Windows use:
xrandr --output $DISPLAY --off
If using a WM like i3, setting the hotkey that to $mod4+shift+k. On Windows then it is possible to use the presentation settings to make the change back.
<windows-key> + p, set secondary monitor
Troubleshooting
Code 12 / PCI Resource Allocation Errors in Guest
You may find that after installing drivers for the GPU in your guest OS, the drivers do not load and the OS reports an error related to finding a large enough memory region or resources to allocate to the GPU. In a Windows guest, this is typically code 12 («This device cannot find enough free resources that it can use») while in Linux it appears in dmesg output as errors like «BAR XX: no space for [mem size 0x600000000 64bit pref]» or «BAR XX: failed to assign [mem size 0x600000000 64bit pref]».
Potential causes for this issue include:
- virt-manager has nested the GPU under a PCI bridge but the device and/or drivers require the GPU to be attached to the root PCI bridge.
- Your host machine is not using UEFI mode, is using CSM/legacy boot mode, or is not using 64-bit PCI memory addressing (aka, «Above 4g decoding»).
- The TianoCore UEFI BIOS inside the guest is not using 64-bit addressing.
Adjusting PCI Address of GPU inside Guest
- Edit the VM in virt-manager (open the VM and then either click the «light bulb» icon or select «View» -> «Details» from the menu).
- Select «Overview» on the left side of the window.
- In the tabs at the top of the right side of the window, select the «XML» tab to switch into the XML view of the guest.
- Locate the
<hostdev>
section(s) for your GPU (thebus
andslot
values inside thesource
tag should match the PCI address of your GPU). For example, the snippet for the GPU at84:00.0
might look like this:<hostdev mode='subsystem' type='pci' managed='yes'> <source> <address domain='0x0000' bus='0x84' slot='0x00' function='0x0'/> </source> <address type='pci' domain='0x0000' bus='0x06' slot='0x0' function='0x0'/> </hostdev>
- Adjust the
bus
andslot
attributes of the secondaddress
tag (the one outside thesource
tag) so that thebus
becomes0x0
and theslot
does not conflict with theslot
value of any other device having abus
of0x0
elsewhere in the XML (a value of0x10
is often, but not always, safe, if you have a guest with a lot of PCI bridges or other devices at the root). - If your GPU has multiple devices (e.g., it’s an ATI/AMD card), repeat steps 4 and 5 for each remaining GPU-related device, being sure to set the
bus
andslot
of each device to the same value while leaving thefunction
values (typically,0x0
,0x01
, and so on for each additional component of the GPU) alone. - Click the «Apply» button.
- Confirm that your change to the
bus
andslot
attributes of the<hostdev>
section(s) have not been reverted. If they have been reverted, you inadvertently picked slot IDs that conflicted with other devices, and you will need to repeat steps 4-8 again with a different slot ID. - Attempt to boot the guest VM.
- Confirm that the PCI resource allocation errors no longer appear in the guest.
If you end up with a black screen (typically before even the TianoCode UEFI splash screen), proceed to the next two sections related to 64-bit PCI memory addressing.
Switching Host to UEFI 64-bit PCI Resource Addressing
Note
If you originally setup Gentoo while in Legacy CSM mode, you will need to reconfigure your partitions, kernel, and boot loader for EFI. See the Gentoo installation handbook for details.
Some GPUs (especially server GPUs like Tesla or Quadro data center GPUs) have so much on-board video RAM that they will not function without 64-bit PCI memory addressing, commonly known as «Above 4g decoding». Even on a 64-bit Intel-based system, your host BIOS might be configured to still allocate memory windows for all PCI devices within a 1 gigabyte «hole» at the high end of the first 4 gigabytes of your machine’s memory space, for compatibility with older drivers and older operating systems that were written to work with PCI devices on 32-bit systems. This has no effect on the total RAM you can have in your machine, and when running a 64-bit operating system, it doesn’t affect your ability to use all the RAM you’ve installed, but it does affect the ability to use a GPU with 6+ GBs of VRAM since it’s not possible to fit that VRAM within the small legacy PCI memory region.
To fix this, enter the UEFI BIOS of your host machine, and make sure that you are using the following settings:
- UEFI Boot: Enabled.
- CSM Boot: Disabled (some BIOSes may have you toggle between an EFI vs. CSM or «Legacy» boot mode; choose EFI).
- Above 4g Decoding: Enabled (some BIOSes might call this «64-bit BAR» or «64-bit PCI Addressing»).
If you’ve confirmed that these settings are all correct but you are still not able to use the passed-through GPU, proceed to the next section.
Enabling 64-bit PCI Addressing in the Switching the TianoCore UEFI BIOS
If enabling 64-bit PCI addressing on the host wasn’t sufficient to make the device function, it might also need to be enabled in the guest EFI bios. Unfortunately, even if you enter the TianoCore UEFI BIOS Setup, there does not appear to be a setting for this.
Luckily, the setting can be enabled through a snippet added to the guest XML:
- Edit the VM in virt-manager (open the VM and then either click the «light bulb» icon or select «View» -> «Details» from the menu).
- Select «Overview» on the left side of the window.
- In the tabs at the top of the right side of the window, select the «XML» tab to switch into the XML view of the guest.
- At the top of the file, locate the
domain
tag. - Add
xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'
to the end of the tag. For example, change: To:<domain type="kvm" xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
- Add the following inside the
domain
tag:<qemu:commandline> <qemu:arg value='-fw_cfg'/> <qemu:arg value='opt/ovmf/X-PciMmio64Mb,string=65536'/> </qemu:commandline>
- Click the «Apply» button.
- Confirm that your changes have not been reverted. If they have been reverted, you may have a typo, and you will need to repeat steps 4-7 again.
- Attempt to boot the guest VM.
- Confirm that the PCI resource allocation errors no longer appear in the guest.
Machine Does Not Boot into UEFI
If you get only a black screen and not even the TianoCore UEFI boot splash before the guest OS loads, the GPU may require 64-bit PCI memory addressing to be enabled. Follow the steps in the «Enabling 64-bit PCI Addressing in the Switching the TianoCore UEFI BIOS» section above.
Code 43 / nvidia-smi
in Windows Guest Cannot Communicate with Nvidia Card
You may find that after installing drivers for the GPU in Windows, the card fails to initialize and nvidia-smi
fails to communicate with it. In a Windows guest, Device Manager will typically report code 43 («Windows has stopped this device because it has reported problems») for this situation.
Potential causes for this issue include:
- Arch Wiki indicates that sometimes it’s necessary to add
<ioapic driver='kvm'/>
to thefeatures
section of the VM so that a KVM-native APIC implementation is used. - If you are using a datacenter card (e.g., a Tesla or Quadro) and have using
nvidia-smi
to toggle it into WDDM mode for use as a graphics card, you may still need to make registry edits to the device configuration so that the Nvidia drivers properly initialize the driver for graphics mode.
Hangs during Benchmarks (e.g., 3DMark)
Benchmark applications running in a Windows guest may rely on high-precision timing features that aren’t fully emulated by QEMU hardware, resulting in a crash when this functionality is accessed. This appears to manifest as the entire guest VM freezing and needing to be halted.
To fix this, you can enable several «Hyper-V enablements» that make Windows believe it is running on Hyper-V rather than on hardware, enabling the Linux kernel to provide Windows with these features instead of expecting that they will be provided by the emulated hardware:
- Edit the VM in virt-manager (open the VM and then either click the «light bulb» icon or select «View» -> «Details» from the menu).
- Select «Overview» on the left side of the window.
- In the tabs at the top of the right side of the window, select the «XML» tab to switch into the XML view of the guest.
- Locate the
hyperv
tag inside thefeatures
tag. - Modify the contents of the
hyperv
tag to match the following:<hyperv mode='custom'> <relaxed state='on'/> <vapic state='on'/> <spinlocks state='on' retries='8191'/> <vpindex state='on'/> <runtime state='on'/> <synic state='on'/> <stimer state='on'> <direct state='on'/> </stimer> <reset state='off'/> <frequencies state='on'/> <reenlightenment state='on'/> <tlbflush state='on'/> <ipi state='on'/> <evmcs state='off'/> </hyperv>
- Click the «Apply» button.
- Confirm that your changes have not been reverted. If they have been reverted, you may have a typo, and you will need to repeat steps 4-7 again.
- Attempt to boot the guest VM.
See also
- QEMU — a generic, open-source hardware emulator and virtualization suite.
- Virt-manager — lightweight GUI application designed for managing virtual machines and containers via the libvirt API.
- KVM — a generic, open-source hardware emulator and virtualization suite.
External resources
- https://heiko-sieger.info/iommu-groups-what-you-need-to-consider/#What_is_IOMMU_and_why_do_I_need_it
- https://wiki.installgentoo.com/index.php/PCI_passthrough — PCI passthrough on gentoo
- https://www.reddit.com/r/VFIO/comments/ahg1ta/bsod_when_launching_gpuz/
- https://forum.level1techs.com/t/navi-reset-kernel-patch/147547
- https://forum.level1techs.com/t/linux-host-windows-guest-gpu-passthrough-reinitialization-fix/121097?source_topic_id=121737 — AMD GPU on windows guest
- https://www.reddit.com/r/VFIO/comments/baa8e3/issue_unable_to_power_on_device_stuck_in_d3/
- https://github.com/gnif/vendor-reset
- https://www.tsunderechen.io/2021/11/OVMF-PCIE-passthrough-with-large-VRAM-GPU/
- https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF — Useful page to learn some of the more advanced configurations a VM can be set with.
- https://github.com/bryansteiner/gpu-passthrough-tutorial — Lots of screenshots of several steps of this process and even CPU + RAM optimization.