\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 abilitare il supporto backport per poter installare una
    versione aggiornata della kernel. \\
    
    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. \\
    
    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: 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*{Appendici}
    \subsection*{i915 Skylake iGPU support} \label{skylakeigpu}
    Attualmente (12.2016) i driver grafici per i nuovi processori intel di
    architettura skylake sono disponibili e relativamente stabili dalle kernel
    4.7+, quindi nella mia installazione di debian ho deciso di utilizzare il
    backport della kernel 4.8.0. Per abilitare i backports si deve aggiungere 
    un nuovo repo nel file `/etc/apt/sources.list'.
    \begin{framed}\raggedright
        \# file: /etc/apt/sources.list \\
        deb http://ftp.debian.org/debian jessie-backports main non-free
    \end{framed}
    Successivamente si deve aggiornare la kernel e i driver grafici mesa 

\end{document}
