====== 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]]**