null

Разблокировка vGPU на пользовательских видеокартах NVIDIA

vGPU

Одной из функций, поддерживаемых линейкой профессиональных видеокарт NVIDIA (Tesla, Qudro) - является виртуализация графического процессора. Это потенциально удобная функция в некоторых средах, поскольку она позволяет более чем одной виртуальной машине использовать ресурсы одного графического процессора. Эта функция официально недоступна на потребительских графических процессорах NVIDIA, но, как оказалось, ее можно активировать с помощью одного из взломов, предложенный сообществом.

Nvidia vGPU — это запатентованная технология, разработанная NVIDIA для использования в центрах обработки данных с видеокартами Tesla и Quadro. Эта технология нацелена на рынок VDI для работы с различным программным обеспечением вкючающим в себя CAD/CAM системы, игры (GeForce Now) и многое другое ПО требующее графическое ускорение.

vgpu_unlock (https://github.com/DualCoder/vgpu_unlock) разблокирует функцию vGPU для графических процессоров потребительского уровня, это не проверенный на многих системах инструмент, и его использование возможно только на свой страх и риск.

​​​​​​​Реализация на Rust: https://github.com/mbilker/vgpu_unlock-rs
​​​​​​​NVIDIA GRID драйвера: https://drive.google.com/file/d/1dCyUteA2MqJaemRKqqTu5oed5mINu9Bw/view
NVIDIA vGPU документация: docs.nvidia.com/grid

Требования

Обязательным требованием для работы - включённая в BIOS виртуализация, для Intel это VT-X, для AMD это AMD-V. Обратите внимание, что IOMMU может быть необходим для правильной работы vGPU на некоторых системах. Также невомзожна работа на версии ядра Linux 5.13, желательно использовать более старое ядро.

Одна из рекомендаций - использовать ядро Linux версии ниже 5.13.

Для предварительной настройки системы, можно воспользоваться: ArchWiki: PCI passthrough via OVMF

How it works

Инициализация устройства vGPU обрабатывается модулем ядра, и он выполняет собственную проверку возможностей видеокарты на vGPU. Модуль ядра отображает диапазон физических адресов PCI 0xf0000000-0xf1000000 в свое виртуальное адресное пространство, а затем выполняет какие-то волшебные операции, которые мы на самом деле не знаем, что они делают. Что мы знаем, так это то, что после этих операций он получает доступ к 128-битному значению по физическому адресу 0xf0029624, которое мы называем магическим значением. Модуль ядра также обращается к 128-битному значению по физическому адресу 0xf0029634, которое мы называем значением ключа.

Затем модуль ядра использует две таблицы для поиска для магического значения, одну для графических процессоров с поддержкой vGPU и одну для остальных. Таким образом, модуль ядра ищет магическое значение в обеих этих таблицах поиска, и если обнаруживается, что запись таблицы также содержит набор зашифрованных блоков данных AES-128 и подписанный HMAC-SHA256. Затем подпись проверяется с использованием значения ключа, упомянутого ранее, для вычисления подписи HMAC-SHA256 по зашифрованным блокам данных. Если подпись правильная, то блоки расшифровываются с помощью AES-128 и того же ключа. Внутри расшифрованных данных находится PCI Device ID. Таким образом, для того, чтобы модуль ядра принял процессор GPU как способный к работе с vGPU, магическое значение должно совпадать со значением в таблице магических значений, поддерживающих vGPU, ключ должен генерировать действительную подпись HMAC-SHA256, а дешифрованные блоки данных AES-128 должны содержать идентификатор устройства PCI с поддержкой vGPU. Если какая-либо из этих проверок не пройдена, возвращается код ошибки 0x56 «Call not supported».

Пример Device ID видеокарт для индентификации устройств:

                // GM107
                if(actual_devid == 0x1390 || // GTX 845M
                   actual_devid == 0x1391 || // GTX 850M
                   actual_devid == 0x1392 || // GTX 860M
                   actual_devid == 0x139a || // GTX 950M
                   actual_devid == 0x139b || // GTX 960M
                   actual_devid == 0x139c || // GTX 940M
                   actual_devid == 0x139d || // GTX 750 Ti Maxwell
                   actual_devid == 0x179c || // GTX 940MX
                   actual_devid == 0x1380 || // GTX 750 Ti Maxwell
                   actual_devid == 0x1381 || // GTX 750 Maxwell
                   actual_devid == 0x1382 || // GTX 745 Maxwell
                   actual_devid == 0x13b0 || // Quadro M2000 Mobile
                   actual_devid == 0x13b1 || // Quadro M1000 Mobile
                   actual_devid == 0x13b2 || // Quadro M600 Mobile
                   actual_devid == 0x13b3 || // Quadro K2200 Mobile
                   actual_devid == 0x13b4 || // Quadro M620 Mobile
                   actual_devid == 0x13b6 || // Quadro M1200 Mobile
                   actual_devid == 0x13b9 || // NVS 810
                   actual_devid == 0x13ba || // Quadro K2200
                   actual_devid == 0x13bb || // Quadro K620
                   actual_devid == 0x13bc) { // Quadro K1200               
                    spoofed_devid = 0x13bd; // Tesla M10
                }
                // GM204
                if(actual_devid == 0x13c3 || // GTX 960 GM204 OEM Edition
                   actual_devid == 0x13d9 || // GTX 965M
                   actual_devid == 0x13d8 || // GTX 970M
                   actual_devid == 0x13c2 || // GTX 970
                   actual_devid == 0x13d7 || // GTX 980M
                   actual_devid == 0x13c0 || // GTX 980
                   actual_devid == 0x13c1 || // GM204 Unknown
                   actual_devid == 0x13f1 || // Quadro M4000
                   actual_devid == 0x13f0) { // Quadro M5000
                    spoofed_devid = 0x13f2; // Tesla M60
                }
                // GP102
                if(actual_devid == 0x1b00 || // TITAN X (Pascal)
                   actual_devid == 0x1b02 || // TITAN Xp
                   actual_devid == 0x1b06 || // GTX 1080 Ti
                   actual_devid == 0x1b30) { // Quadro P6000
                    spoofed_devid = 0x1b38; // Tesla P40
                }
                // GP108 (Merged with Tesla P4, will move to M10 in future due to 2GB VRAM)
                if(actual_devid == 0x1d01 || // GT 1030
                   actual_devid == 0x1d10 || // MX150 Mobile
                   actual_devid == 0x1d11 || // MX230 Mobile
                   actual_devid == 0x1d12 || // MX150 Mobile
                   actual_devid == 0x1d13 || // MX250 Mobile
                   actual_devid == 0x1d16 || // MX330 Mobile
                // GP107 (Merged with Tesla P4, may move to M10 in future due to low VRAM)
                   actual_devid == 0x1cb1 || // Quadro P1000
                   actual_devid == 0x1c81 || // GTX 1050 2GB
                   actual_devid == 0x1c82 || // GTX 1050 Ti
                   actual_devid == 0x1c83 || // GTX 1050 3GB
                   actual_devid == 0x1c8c || // GTX 1050 Ti Mobile
                   actual_devid == 0x1c8d || // GTX 1050 Mobile
                   actual_devid == 0x1c8f || // GTX 1050 Ti Max-Q
                   actual_devid == 0x1c90 || // MX150 Mobile
                   actual_devid == 0x1c92 || // GTX 1050 Mobile
                   actual_devid == 0x1c94 || // MX350 Mobile
                   actual_devid == 0x1c96 || // MX350 Mobile
                // GP106 (Merged with Tesla P4)
                   actual_devid == 0x1c03 || // GTX 1060 6GB
                   actual_devid == 0x1c04 || // GTX 1060 5GB
                   actual_devid == 0x1c02 || // GTX 1060 3GB
                   actual_devid == 0x1c07 || // P106-100 6GB
                   actual_devid == 0x1c09 || // P106-90  3GB
                   actual_devid == 0x1c22 || // GTX 1050 Mobile
                   actual_devid == 0x1c23 || // GTX 1060 Mobile Rev. 2
                   actual_devid == 0x1c20 || // GTX 1060 Mobile
                   actual_devid == 0x1c21 || // GTX 1050 Ti Mobile
                   actual_devid == 0x1c2d || // GP106M Generic
                   actual_devid == 0x1c60 || // GTX 1060 Mobile 6GB
                   actual_devid == 0x1c61 || // GTX 1050 Ti Mobile
                   actual_devid == 0x1c62 || // GTX 1050 Mobile
                   actual_devid == 0x1c70 || // GP106GL Generic
                   actual_devid == 0x1c30 || // Quadro P2000 
                   actual_devid == 0x1c31 || // Quadro P2200
                // GP104
                   actual_devid == 0x1b80 || // GTX 1080
                   actual_devid == 0x1b81 || // GTX 1070
                   actual_devid == 0x1b82 || // GTX 1070 Ti
                   actual_devid == 0x1b83 || // GTX 1060 6GB GP104 Refresh
                   actual_devid == 0x1b84 || // GTX 1060 3GB GP104 Refresh
                   actual_devid == 0x1b87 || // P104-100 Mining Card
                   actual_devid == 0x1ba0 || // GTX 1080 Mobile
                   actual_devid == 0x1ba1 || // GTX 1070 Mobile
                   actual_devid == 0x1bb0) { // Quadro P5000 (This will be moved to Tesla P6 in the future)
                    spoofed_devid = 0x1bb3; // Tesla P4
                }
                // GV100 (For the one person who owns a Titan Volta)
                if(actual_devid == 0x1d81 || // TITAN V
                   actual_devid == 0x1dba) { // Quadro GV100 32GB
                    spoofed_devid = 0x1db4; // Tesla V100
                }
                // TU102
                if(actual_devid == 0x1e02 || // TITAN RTX
                   actual_devid == 0x1e04 || // RTX 2080 Ti
                   actual_devid == 0x1e07) { // RTX 2080 Ti Rev. A
                    spoofed_devid = 0x1e30; // Quadro RTX 6000
                    spoofed_subsysid = 0x12ba;
                }
                // TU117 (Merged with Tesla T4)
                if(actual_devid == 0x1ff9 || // Quadro T1000 Mobile
                   actual_devid == 0x1f99 || // TU1117 Mobile Unknown
                   actual_devid == 0x1fae || // TU1117GL Unknown
                   actual_devid == 0x1fb8 || // Quadro T2000 Mobile Max-Q
                   actual_devid == 0x1fb9 || // Quadro T1000 Mobile
                   actual_devid == 0x1fbf || // TU1117GL Unknown
                   actual_devid == 0x1f97 || // GeForce MX450
                   actual_devid == 0x1f98 || // GeForce MX450
                   actual_devid == 0x1f9c || // GeForce MX450
                   actual_devid == 0x1fbb || // Quadro T500 Mobile
                   actual_devid == 0x1fd9 || // GeForce GTX 1650 Mobile Refresh
                   actual_devid == 0x1f81 || // TU117 Unknown
                   actual_devid == 0x1f82 || // GeForce GTX 1650
                   actual_devid == 0x1f91 || // GTX 1650 Mobile Max-Q
                   actual_devid == 0x1f92 || // GTX 1650 Mobile
                   actual_devid == 0x1f94 || // GTX 1650 Mobile
                   actual_devid == 0x1f95 || // GTX 1650 Ti Mobile
                   actual_devid == 0x1f96 || // GTX 1650 Mobile Max-Q
                // TU116 (Merged with Tesla T4)
                   actual_devid == 0x2182 || // GTX 1660 Ti
                   actual_devid == 0x2183 || // TU116 Unknown
                   actual_devid == 0x2184 || // GTX 1660
                   actual_devid == 0x2187 || // GTX 1650 SUPER
                   actual_devid == 0x2188 || // GTX 1650
                   actual_devid == 0x2191 || // GTX 1660 Ti Mobile
                   actual_devid == 0x2192 || // GTX 1650 Ti Mobile
                   actual_devid == 0x21ae || // TU116GL Unknown
                   actual_devid == 0x21bf || // TU116GL Unknown
                   actual_devid == 0x21c4 || // GTX 1660 Super
                   actual_devid == 0x21d1 || // GTX 1660 Ti Mobile
                // TU106 (Merged with Tesla T4)
                   actual_devid == 0x1f02 || // RTX 2070 8GB
                   actual_devid == 0x1f04 || // TU106 Unknown
                   actual_devid == 0x1f06 || // RTX 2060 SUPER
                   actual_devid == 0x1f07 || // RTX 2070 Rev. A
                   actual_devid == 0x1f08 || // RTX 2060 6GB
                   actual_devid == 0x1f09 || // GTX 1660 Super
                   actual_devid == 0x1f0a || // GTX 1650
                   actual_devid == 0x1f10 || // RTX 2070 Mobile
                   actual_devid == 0x1f11 || // RTX 2060 Mobile
                   actual_devid == 0x1f12 || // RTX 2060 Mobile Max-Q
                   actual_devid == 0x1f14 || // RTX 2070 Mobile Max-Q
                   actual_devid == 0x1f15 || // RTX 2060 Mobile
                   actual_devid == 0x1f2e || // TU106M Mobile Unknown
                   actual_devid == 0x1f36 || // TU106GLM Mobile Unknown
                   actual_devid == 0x1f42 || // RTX 2060 SUPER
                   actual_devid == 0x1f47 || // RTX 2060 SUPER
                   actual_devid == 0x1f50 || // RTX 2070 Mobile
                   actual_devid == 0x1f51 || // RTX 2060 Mobile
                // TU104
                   actual_devid == 0x1e81 || // RTX 2080 Super
                   actual_devid == 0x1e82 || // RTX 2080
                   actual_devid == 0x1e84 || // RTX 2070 Super
                   actual_devid == 0x1e87 || // RTX 2080 Rev. A
                   actual_devid == 0x1e89 || // RTX 2060
                   actual_devid == 0x1eb0 || // Quadro RTX 5000
                   actual_devid == 0x1eb1) { // Quadro RTX 4000
                    spoofed_devid = 0x1eb8; // Tesla T4
                }
                // GA102
                if(actual_devid == 0x2204 || // RTX 3090
                   actual_devid == 0x2205 || // RTX 3080 Ti
                   actual_devid == 0x2206) { // RTX 3080
                    spoofed_devid = 0x2235; // RTX A40
                }

Использование

​​​​​​​В данном сценарии, используется ОС Ubuntu 18.04 с версией ядра 5.4.0-12.15.

Включить IOMMU в boot параметрах GRUB.
​​​​​​​В /etc/default/grub необходимо добавить:

GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on" - для процессоров Intel  
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on" - для процессоров AMD

После чего, перезагружаемся.

Проверяем работу IOMMU:

dmesg | grep -e DMAR -e IOMMU

​​​​​​​Должен получится вывод следующего вида:

[    0.011690] ACPI: DMAR 0x000000009BC4C000 0000A8 (v01 LENOVO CB-01    00000001      01000013)
[    0.011734] ACPI: Reserving DMAR table memory at [mem 0x9bc4c000-0x9bc4c0a7]
[    0.255464] DMAR: Host address width 39
[    0.255465] DMAR: DRHD base: 0x000000fed90000 flags: 0x0
[    0.255471] DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap 1c0000c40660462 ecap 19e2ff0505e
[    0.255474] DMAR: DRHD base: 0x000000fed91000 flags: 0x1
[    0.255479] DMAR: dmar1: reg_base_addr fed91000 ver 1:0 cap d2008c40660462 ecap f050da
[    0.255481] DMAR: RMRR base: 0x0000009b145000 end: 0x0000009b164fff
[    0.255483] DMAR: RMRR base: 0x0000009d000000 end: 0x0000009f7fffff
[    0.255485] DMAR-IR: IOAPIC id 2 under DRHD base  0xfed91000 IOMMU 1
[    0.255487] DMAR-IR: HPET id 0 under DRHD base 0xfed91000
[    0.255488] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.258742] DMAR-IR: Enabled IRQ remapping in x2apic mode

Устанавливаем необходимые пакеты:

apt install -y git build-essential dkms git

Устанавливаем Rust и компилириуем vgpu_unlock-rs:

git clone https://github.com/mbilker/vgpu_unlock-rs.git
curl https://sh.rustup.rs -sSf | sh -s -- -y
source $HOME/.cargo/env
cd vgpu_unlock-rs/
cargo build --release

Скачиваем NVIDIA Grid драйвер, указанный в начале статьи и устанавливаем его:

chmod +x NVIDIA-Linux-x86_64-460.73.01-grid-vgpu-kvm-v5.run
./NVIDIA-Linux-x86_64-460.73.01-grid-vgpu-kvm-v5.run --dkms

(перезагружаемся)

reboot

Проверяем успешную установку драйвера:
​​​​​​

nvidia-smi
Tue Nov 22 14:14:51 2022       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 515.65.01    Driver Version: 515.65.01    CUDA Version: 11.7     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  Off  | 00000000:01:00.0  On |                  N/A |
| N/A   45C    P8     3W /  N/A |     54MiB /  4096MiB |     21%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A      1395      G   /usr/lib/xorg/Xorg                 53MiB |
+-----------------------------------------------------------------------------+


​​​​​Проверяем разблокировку vGPU:

mdevctl types

Вывод:

0000:01:00.0
  nvidia-256
    Available instances: 24
    Device API: vfio-pci
    Name: GRID RTX6000-1Q
    Description: num_heads=4, frl_config=60, framebuffer=1024M, max_resolution=5120x2880, max_instance=24
  nvidia-257
    Available instances: 12
    Device API: vfio-pci
    Name: GRID RTX6000-2Q
    Description: num_heads=4, frl_config=60, framebuffer=2048M, max_resolution=7680x4320, max_instance=12
  nvidia-258
    Available instances: 8
    Device API: vfio-pci
    Name: GRID RTX6000-3Q
    Description: num_heads=4, frl_config=60, framebuffer=3072M, max_resolution=7680x4320, max_instance=8

Данные инстансы могут в дальнейшем изспользоваться для GPU-Passtrought в гостевые виртуальные машины.

Коротко о себе:



​​​​​​​​​​​​​​Работаю инженером в компании Tune-it.

Nothing has been found. n is 0