summaryrefslogtreecommitdiffstats
path: root/doc/pcie_passthrough.tex
blob: 69c7fe75a74a88f4e3375efecb3baf32f2df5a4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
\documentclass[a4paper,10pt]{article}

\usepackage[outer=2cm,inner=2cm]{geometry}
\usepackage[hidelinks]{hyperref}
\usepackage{framed}
\usepackage{lmodern}

\usepackage{fontspec}
\usepackage{ragged2e}
\setmainfont{Latin Modern Mono}

\title{PCIe GPU passthrough on Debian}
\author{Nao Pross}

\begin{document}\tt
    \maketitle
    
    \section{Introduzione}
    Recentemente grazie all'evoluzione delle tecnologie di virtualizzazione \`e
    diventato possibile passare direttamente un dispositivo collegato in uno
    slot PCI ad una macchina virtuale, che può quindi usufruire dell'hardware a
    performance quasi nativa. Un computer configurato per funzionare in tale
    maniera apparirà come se fossero due computer distinti con due monitors (o
    ad uno con un kvm switch). Il vantaggio di un setup del genere è la
    possibilità di cambiare immediatamente da un sistema operativo all’altro
    senza dover attendere che ma macchina si riavvii (in un dualboot) e si
    evitano potenziali problemi come Windows Update che rendono il PC inusabile.
    
    \section{Limitazioni}
    Come ogni sistema si ha delle limitazioni quali
    \begin{itemize}
        \item Per poter vedere l'uscita video della VM si deve collegare un
            display ad una delle uscite della scheda grafica passata
        \item Non \`e possibile passare la iGPU ad una VM (\`e possibile ma con
            altre tecnologie ancora sperimentali)
        \item Non \`e possibile passare una GPU discreta di un laptop (es NVIDIA
            Optimus) poich\`e non \`e possibile visualizzare il video in uscita
    \end{itemize}
    
    \section{Funzionamento (In grandi linee)}
    Le macchine virtuali vengono emulate grazie ad un software chiamato QEMU che
    supporta il passaggio di hardware PCI alla VM, inoltre QEMU utilizza un
    estensione chiamata KVM che permette di creare delle macchine virtuali
    collegate alla kernel dunque ottimizzate. Altrimenti QEMU dovrebbe emulare
    l’intero processore su cui viene virtualizzato il sistema operativo. Infine
    per gestire questi due componenti LibVirt è un software che salva le
    configurazioni di QEMU in dei file XML e che permette alle VM di essere
    tenute accese in background. \\
    
    Passando una scheda video in PCI ad una macchina virtuale permette di
    utilizzare la potenza grafica hardware che sarebbe altrimenti emulata (le
    grafichce virtuali di QEMU non sono per niente ottimizzate, per tali scopi è
    meglio utilizzare VirtualBox o VMWare Fusion). Quindi per poter vedere la
    grafica della macchina virtuale si deve collegare un monitor ad una delle
    entrate della scheda video.
    
    \section{Hardware Requirements}
    \begin{itemize}
        \item Intel processor with iGPU (or 2 GPUs)
        \item Processor with VT-x and VT-d
        \item NVIDIA or AMD graphics card (2 if with no iGPU)
        \item Motherboard with VT-d support
        \item Motherboard with UEFI based bootloader
        \item Motherboard with IOMMU support (per configurazioni avanzate)
    \end{itemize}
    
    \section{Software Requirements}
    \begin{itemize}
        \item Linux Kernel with iommu\_groups support ( kernel >= 3.9 )
    \end{itemize}
    
    \section{Hardware utilizzato}
    Per questo questo test ho usato una configurazione hardware esagerata, anche
    con un sistema decisamente meno costoso \`e possibile realizzare il
    progetto.
    \begin{itemize}
        \item ASUS Z170-A motherboard
        \item Intel Core i7 Skylake 6700 CPU with Intel Graphics 530
        \item MSI NVIDIA GTX1050Ti OC 4GB
        \item 32GB DDR4
    \end{itemize}
    
    \section{BIOS Settings}
    Per incominciare è necessario controllare che nel BIOS siano attivate le
    tecnologie di virtualizzazione VT-x e VT-d. Inoltre se si vuole utilizzare
    la iGPU in alcune motherboard (come nel mio caso) si deve modificare un
    impostazione nel bios per non disattivarla qundo viene inserita una scheda
    grafica secondaria.
    
    \section{Sistema Operativo}
    Per avere un sistema stabile consiglio di utilizzare Debian Linux poiché
    essendo utilizzato soprattuto nei servers il ciclo di testing e updates è
    molto lento in modo da avere sempre un supporto legacy e un sistema stabile.
    Purtroppo però attualmente (12.2016) Debian 8 Jessie ha ancora la kernel
    3.16 quindi si deve utilizzare la versione testing (9) Stretch. Anche se
    Debian Testing non \`e pensato per essere utilizzato come sistema primario
    (i bug possono essere ignorati a lungo nei repo) il supporto di
    aggiornamenti software \`e garantito per un perido molto pi\`u esteso delle
    altre distro. \\
    
    Prima di installare il sistema operativo è consigliato rimuovere la GPU che
    si utilizzerà per il passthrough dallo slot PCI per evitare che Linux si
    autoconfiguri per utilizzare i suoi driver grafici. Una volta installato il
    sistema si devono installare i seguenti pacchetti:
    \begin{framed}\raggedright
        \% sudo apt-get install \textbackslash \\
        ~ qemu-kvm libvirt-bin virtinst bridge-utils virt-manager ssh-askpass ovmf
    \end{framed}
    
    \section{Kernel Modules and GRUB}
    Normalmente Debian viene con GRUB2 preinstallato come bootloader, se si
    dovesse avere un altro bootloader si deve semplicemente passare gli stessi
    parametri alla kernel quando si avvia.
    
    \subsection{IOMMU}
    Dalla kernel 3.9 Linux ha introdotto gli iommu\_groups che permettono di
    mappare dispositivi di memoria reale ad indirizzi virtuali che possono
    essere a loro volta passati alla VM. Per poter utilizzare questa
    funzionalit\`a la si deve abilitare inoltre \`e necessario anche il supporto
    degli ACS per poter suddividere i sottocomponenti del gruppo iommu (per
    esempio le porte individuali di un estensione di USB via PCI). Per attivare
    le funzionalit\`a si deve passare alla kernel i seguenti parametri:
    \begin{framed}\raggedright \footnotesize
        \# file: /etc/default/grub \\
        ... \\
        GRUB\_DEFAULT\_CMDLINE\_LINUX\_DEFAULT="intel\_iommu=on iommu=1 pcie\_acs\_override=downstream" \\
        ...
    \end{framed}
    Per aggiornale le impostazioni installate
    \begin{framed}\raggedright
        \% sudo update-grub
    \end{framed}
    
    \section{GPU Settings}
    Per evitare che Debian utilizzi la GPU all’avvio si deve modificare le
    impostazioni dell’initramfs in maniera tale che il modulo della kernel non
    venga avviato. Per sapere il nome del modulo della kernel caricato per la
    GPU si può usare il seguente comando (nel mio caso la scheda è una NVIDIA):
    \begin{framed}\raggedright \scriptsize
        \% lspci -nnk | grep -i -A2 nvidia \\
        02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1) \\
        ~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] GP107 [GeForce GTX 1050 Ti] [1462:8c96] \\
        ~~~~~~~~Kernel driver in use: {\bf nouveau} \\
        -- \\
        02:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:0fb9] (rev a1) \\
        ~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] Device [1462:8c96] \\
        ~~~~~~~~Kernel driver in use: snd\_hda\_intel
    \end{framed}
    Nel file di configurazione
    \begin{framed}\raggedright
        \# file: /etc/modprobe.d/blacklist.conf \\
        ... \\
        blacklist nouveau
    \end{framed}
    Infine per aggiornare la configurazione
    \begin{framed}\raggedright
        \% sudo update-initramfs -u
    \end{framed}
    
    \section{Driver binding}
    Per passare un dispositivo ad una VM si deve indicare alla kernel di
    utilizzare il modulo `vfio-pci', perci\`o \`e necessario inizializzare
    questi dispositivi all'avvio. Per questo possiamo usare questo script preso
    da \href{https://www.reddit.com/r/pcmasterrace/comments/3lno0t/gpu_passthrough_revisited_an_updated_guide_on_how/?ref=share&ref_source=link}{qui}
    salvandolo come `/usr/local/bin/vfio-bind'.
    \begin{framed}\raggedright
        \# file: /usr/local/bin/vfio-bind
        \begin{verbatim}
#!/bin/bash

modprobe vfio-pci

for dev in "$@"; do
        vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
        device=$(cat /sys/bus/pci/devices/$dev/device)
        if [ -e /sys/bus/pci/devices/$dev/driver ]; then
                echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
        fi
        echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done \end{verbatim}
    \end{framed}
    Attivando la flag per poterlo eseguire:
    \begin{framed}\raggedright
        \% sudo chmod +x /usr/local/bin/vfio-bind
    \end{framed}
    Con questo script possiamo collegare il driver per la virtualizzazione a
    qualsiasi dispositivo PCI nella seguente maniera:
    \begin{framed}\raggedright
        \% sudo vfio-bind <indirizzi pci>
    \end{framed}
    Per trovare l'indizirro dei dispositivi che vogliamo passare utilizziamo di
    nuovo il seguente comando:
    \begin{framed}\raggedright \scriptsize
        \% lspci -nnk | grep -i -A2 nvidia \\
        02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1) \\
        ~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] GP107 [GeForce GTX 1050 Ti] [1462:8c96] \\
        ~~~~~~~~Kernel driver in use: nouveau \\
        -- \\
        02:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:0fb9] (rev a1) \\
        ~~~~~~~~Subsystem: Micro-Star International Co., Ltd. [MSI] Device [1462:8c96] \\
        ~~~~~~~~Kernel driver in use: snd\_hda\_intel
    \end{framed}
    Nel mio caso gli indirizzi da passare alla VM sono `0000:02:00.0' per il
    video e `0000:02:00.1' per l'audio.  Successivamente possiamo automatizzare
    la configurazione in modo che il driver per la virtualizzazione venga
    avviato subito all'avvio della macchina. Quindi lo facciamo con una unit di
    SystemD.
    \begin{framed}\raggedright
        \# file: /etc/systemd/system/vfio-pci-bind.service
        
        \begin{verbatim}
[Unit]
Description=Bind PCI devices to the virtio driver

[Service]
Type=oneshot
ExecStart=/usr/local/bin/vfio-bind 0000:02:00.0 0000:02:00.1

[Install]
WantedBy=multi-user.target
Before=libvirt-guests.service \end{verbatim}
    \end{framed}

    \section{Installare la VM}
    Come sistema operativo guest conglio Windows 8+, poich\`e il supporto
    dell'UEFI \`e migliorato e per esperienza trovo che Windows 7 sia pi\`u
    complicato da configurare (errori di drivers video).

    \subsection{Dispositivo di installazione}
    Per installare l'OS guest \`e necessario avere un immagine di installazione
    bootable perch\`e OVMF non supporta l'avvio di CDROM (o di altri sistemi
    legacy). Da un disco di installazione normalmente \`e possibile genereare un
    disco di installazione uefi utilizzando una memoria USB formattata in FAT32.

    \subsection{Installazione}
    Per creare la VM in libvirt \`e possibile utilizzare virt-manager che ha uno
    strumento con interfaccia grafica, in alternativa \`e possibile utilizzare
    virt-install dalla linea di comando.

    \begin{framed}
        \begin{verbatim}
% virt-install --connect qemu:///system \
    --name win8 \
    --memory 8169 \
    --vcpus sockets=1,cores=2,threads=2 \
    --cpu Skylake-Client \
    --metadata title="Windows 8.1" \
    --disk ~/vm_images/windows8.raw,cache=none \
    --network ... \end{verbatim}
    \end{framed}

\end{document}