====== QEMU USB device passthrough ======
Come passare completamente la gestione di una periferica USB da un sistema virtualizzante (host) **QEMU** (GNU/Linux con KVM) ad un sistema virtualizzato (guest) **Windows 10**. Procedura sperimentata su Debian GNU/Linux 11 Bullseye.
Il programma emulatore **qemu-system-x86_64** deve sapere quale periferica USB deve essere collegata alla macchina guest. È possibile identificare la periferica in tre modi diversi:
* Identificazione con **vendorid** e **productid**. Ogni periferica USB ha un identificativo produttore/prodotto che dovrebbe essere univoco. Il sistema operativo GNU/Linux mostra tali codici con il comando **lsusb**.
* Identificazione tramite **hostbus** e **hostaddr**. Quando una periferica viene connessa ad una porta USB, il sistema operativo GNU/Linux le assegna un indirizzo host relativo al bus USB (un PC generalmente ha diversi bus USB). L'indirizzo è univoco rispetto al bus; si tratta di un numero che si incrementa ad ogni nuova periferica aggiunta. Quindi una periferica che ha ricevuto ad esempio //hostaddr// **8**, se viene scollegata e poi nuovamente collegata può ricevere //hostaddr// **9**. Quando una periferica è connessa è possibile vedere quali hostbus e hostaddr le sono stati assegnati con il comando **lsusb**.
* Identificazione tramite **hostbus** e **hostport**. La posizione fisica di una porta USB è univocamente determinata da due identificatori: uno per il bus USB ed un altro per la porta. Questa modalità consente di determinare a priori la periferica USB da passare all'emulatore, senza sapere marca e modello della periferica e senza consultare quale address ha ricevuto dal kernel. Sarà sufficiente **utilizzare sempre la stessa porta fisica** per il collegamento. Per ispezionare la gerarchia di bus USB e porte è possibile utilizzare il comando **lsusb -t**.
===== Esempio: lettore di flash card USB =====
==== Identificare la periferica ====
Consideriamo come esempio un **lettore di flash card**. Inserendo il dispositivo in una porta USB, il sistema ospitante GNU/Linux lo identifica come segue (output del comando **lsusb**):
Bus 001 Device 026: ID 05e3:0723 Genesys Logic, Inc. GL827L SD/MMC/MS Flash Card Reader
In particolare notiamo:
* **Bus 001** - La periferica è collegata al primo bus USB dell'host. Altre porte USB dell'host possono essere collegate a bus diversi.
* **Device 026** - Il sistema operativo ha essegnato alla periferica indirizzo 26 sul bus #1. Tale indirizzo cambia se la periferica viene scollegata e quindi ricollegata.
* **ID 05e3:0723** - I due numeri esadecimale sono il //vendorid// e //productid// codificati nell'hardware della periferica.
Se vogliamo conoscere la posizione fisica sul bus USB utilizziamo il comando **lsusb -t**:
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/3p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/6p, 480M
|__ Port 1: Dev 26, If 0, Class=Mass Storage, Driver=usbfs, 480M
In questo caso notiamo:
* **Bus 01** - Il bus USB è sempre il numero **1**.
* **Port 1.1** - La porta ha un identificativo gerarchico con il punto utilzzato come separatore: abbiamo l'hub numero **1** seguito dal numero della porta **1**.
==== Assegnare i permessi ====
È possibile eseguire il programma ''qemu-system-x86_64'' da utente non privilegiato, ma l'emulatore dovrà avere pieno accesso alla periferica USB di cui si desidera il passthrough a Windows. Dopo aver identificato **hostbus** e **hostaddr** assegnati dal kernel alla periferica, l'utente root potrà assegnare i permessi necessari. Ad esempio con:
chmod 0666 /dev/bus/usb/001/026
È possibile configurare il sistema in modo tale che una determinata periferica riceva automaticamente gli opportuni permessi ad ogni connessione. Ad esempio si potrebbe volere che la periferica sia in lettura/scrittura per gli utenti che appartengono al gruppo **plugdev** (in Debian è il gruppo degli utenti autorizzati a montare e smontare i dispostivi rimuovibili). Per ottenere questo risultato si crea un file ad esempio **/etc/udev/rules.d/99-flash-card-reader.rules** con il seguente contenuto:
SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="05e3", ATTR{idProduct}=="0723", GROUP="plugdev", MODE="0660"
Per forzare il sistema **udev** a rileggere i file di configurazione si esegue:
udevadm control --reload-rules && udevadm trigger
Alla successiva connessione della periferica si può verificare che abbia ricevuto i giusti permessi.
==== Aggiungere la periferica all'avvio di QEMU ====
Se la periferica è già collegata all'host al momento dell'avvio di QEMU, è possibile passare dalla riga di comando gli opportuni parametri all'emulatore **qemu-system-x86_64**, in modo che la periferica venga **aggiunta** e compaia in **Gesione dispositivi**. Qui di seguito i **parametri** per fare il passthrough della periferica identificandola in uno dei tre modi possibili:
-usb -device usb-host,vendorid=0x05e3,productid=0x0723
-usb -device usb-host,hostbus=1,hostaddr=26
-usb -device usb-host,hostbus=1,hostport=1.1'
Ecco una riga di comando minimale, ma completa per avviare l'emulatore:
qemu-system-x86_64 -m 4096 -machine accel=kvm \
-device qemu-xhci \
-usb -device 'usb-host,vendorid=0x05e3,productid=0x0723' \
-drive 'file=win10_64bit_c.img,format=raw' -boot c
Notare la presenza del device **qemu-xhci**; se la macchina ospite supporta le specifiche hardware XHCI (ogni macchina successiva al 2010 dovrebbe) è opportuno caricare tale emulazione, che supporta con un solo controller le periferiche USB 1.1, USB 2.0 e USB 3.0. Utilizzando questo driver non è necessario aggiungere la specifica ''%%bus={usb-bus.0|ehci.0}%%'' nei comandi che aggiungono le periferiche USB. Senza esplicitare quel device l'emulazione delle periferiche **USB 3.0 non funziona** e la periferica compare in Gestione dispositivi di Windows con un **triangolo giallo con punto esclamativo**.
==== Aggiungere la periferica dalla console QEMU ====
Se la periferica USB viene collegata **dopo che QEMU è stato avviato**, è possibile utilzzare la console di QEMU per aggiungere la periferica al sistema Windows guest.
Attivando **Show Tabs** dal menu **View** di QEMU sarà possibile accedere alla console **compat_monitor0**, quindi si possono eseguire i comandi **info usbhost** per verificare che a periferica sia stata riconosciuta:
(qemu) info usbhost
Bus 1, Addr 26, Port 1.1, Speed 480 Mb/s
Class 00: USB device 05e3:0723, USB Storage
**ATTENZIONE**! Se QEMU non ha identificato correttamente il tipo della periferica (//USB Storage// in questo caso), è possibile che manchino i permessi di lettura/scrittura sulla periferica stessa.
A questo punto la periferica può essere aggiunta con un comando del tipo:
(qemu) device_add usb-host,vendorid=0x05e3,productid=0x0723
Immediatamente il **Gestione dispositivi** di Windows dovrebbe riconoscere la nuova periferica.
===== Web References =====
* **[[https://qemu-project.gitlab.io/qemu/system/devices/usb.html|USB emulation]]**
* **[[https://gist.github.com/ichisadashioko/cfc6446764516bf7eccaffdb3799f041|QEMU USB passthrough user guide]]**