Dienstag, 2. April 2024

USB Geräte in Windows WSL nutzen - oder Virtualisierung tief verschachtelt

 Da hab ich ein "neues" Projekt gefunden (gibt es offenbar schon 2 Jahre) womit es nun möglich ist USB Geräte in WSL 2 zu verwenden. Ich probiere solche Dinge gerne in einer virtuellen Maschine - damit geht die Odyssee der Stolpersteine los ...

Manchmal entdeckt man dabei Dinge die man schon immer wissen wollte, die habe ich jetzt einfach mit aufgeschrieben. Das Ergebnis diese Artikels ist ein WSL/Linux auf einem Windows/Hyper-V welches auf Linux/KVM auf einer physischen CPU läuft! In dem WSL könnte man noch Docker installieren!

Ausgangssituation: Meine vorhandene Hyper-V VM ließ sich nicht in meine neue Libvirt Umgebung übertragen (mittlerweile ahne ich warum), deshalb einfach fix was neues installiert: 
  1. Windows 11 Setup ausgeführt, 
  2. Windows Update gemacht, 
  3. wsl --install erfordert einen Neustart - rumms: 
  4. Bootloop - das System startet nicht mehr.

Ok: nach einiger Suche ist es klar, die "nested virtualization" erfordert (immer) ein besonderes Vorgehen. Als kompletter Ablauf in Kurzform, ich arbeite wie so oft mit ein paar Variablen um die Kommandozeilen universell zu halten.

1. Windows 11 Setup

Download Windows ISO Image direkt bei Microsoft:

iso_name=Win11_23H2_German_x64v2.iso
wget -c -nc -O /mnt/data-shares/ISO/${iso_name} https://software.download.prss.microsoft.com/dbazure/${iso_name}
iso_name=/mnt/data-shares/ISO/${iso_name}

Das CPU Model bestimmen und die VM benennen: 

cpu_model=$(virsh capabilities | grep -oP '<model>\K[^<]+' | head -n 1)
vm_name="win11-vm"

Setup in der Kommandozeile ohne Konsole am Schluß - da keiner eine Taste drückt, wird noch kein Windows Setup gestartet:

virt-install \
--name "$vm_name" \
--ram 8192 \
--vcpus 4 \
--cpu custom,model="$cpu_model" \
--disk path=/var/lib/libvirt/images/$vm_name.qcow2,size=64 \
--os-variant "win11" \
--graphics spice \
--cdrom "$iso_name" \
--boot hd,cdrom \
--noautoconsole

Das Setup steht (im Hintergrund) im Punkt "press any Key ..." und wird erstmal abgebrochen. Die Definition wird modifiziert (elegantere Methode siehe Schluss des Artikels), damit die VM selbst wieder Hyper-V verwenden kann. Damit dieser patch wirksam wird, muss der Verwaltungsdienst neu gestartet werden.

virsh destroy $vm_name
sudo sed -i "/<\/cpu>/i \    <feature policy='require' name='vmx' />" "/etc/libvirt/qemu/${vm_name}.xml"
sudo systemctl restart libvirtd

Jetzt verbindet man sich zur Konsole (virt-manager) und startet das Setup von Windows interaktiv.

In der virt-manager Konsole kann man nach dem Setup das USB Gerät in die VM verbinden. Siehe hier ganz am Anfang.

2. WSL installieren

Ein Befehl erledigt mittlerweile alles (VM Platform aktivieren, wsl aktivieren, Ubuntu installieren). 

wsl --install

Nach dem Systemneustart wird automatisch Ubuntu gestartet und final konfiguriert (Benutzer angelegt).

3. winget reparieren

Das aktuelle ISO 23H2_German_x64v2 installiert winget in einer alte Version v1.2.10691 die quasi funktionsfähig ist. Was Offizielles habe ich nicht gefunden, dafür jede Menge, eher verzweifelt anmutende, Anleitungen. Mir hat diese Zeile in Powershell eine funktionierende Version v1.7.10861 beschert:

Add-AppxPackage https://aka.ms/getwinget
winget -v

Offenbar muss winget einfach aktuell installiert werden.

4. usbipd einrichten

Man hätte auch das msi Paket herunterladen können,aber ich bin ein Fan von winget und wollte, dass es funktioniert.

winget install usbipd

Nach der Installation muss man das Terminal (cmd) schließen und neu öffnen, sonst das Tool nicht gefunden. Jetzt kann man sich die USB Geräte in Windows anzeigen lassen, wichtig ist die BUSID ganz vorn.

usbipd list

Mit Hilfe diese ID kann man das USB Gerät an die usbipd Verwaltung binden, das Gerät wird bei einem list als shared angezeigt:

usbipd bind -b 1-1

Und an alle (oder eine) wsl Umgebung anschließen. Das Gerät wird bei einem list als attached angezeigt.

usbipd attach -w -b 1-1

Jetzt kann man in der wsl Umgebung das USB Gerät verwenden, in ubuntu sind die notwendigen Tools installiert:

lsusb 
ls -lha /dev/ttyU* 

Hat alles funktioniert sollte lsusb das Gerät zeigen und in meinem Falle als serielles Gerät an die /dev/ttyUSB0 Schnittstelle gebunden sein.

Man kann auch alles wieder lösen.

usbipd detach -b 1-1
usbipd unbind -b 1-1

USBIP ist offenbar seit geraumer Zeit eine implementierte Linux Funktionalität. Man kann innerhalb von linux das tool usbip installieren. Eine Beschreibung habe ich im Ubuntu Wiki gefunden. 

Damit kann man die Anbindung vom Client aus vornehmen, siehe hier. Da es über das Netzwerk funktioniert, muss der USBIP Host nicht der wsl/Linux Host sein!

Man kann die XML Dateien von libvirt mit dem Tool xmlstarlet auch per Kommandozeile editieren. Der Syntax ist aufwendig. Das Tool gibt die Veränderung über stdout aus und schreibt nicht in die Quell-Datei.(die Option --inplace ändert direkt)

Das feature vmx (siehe oben) mal als Beispiel: 

sudo xmlstarlet edit \
     --subnode "/domain/cpu" --type elem --name feature \
     --var new_node '$prev' \
     --insert '$new_node' --type attr --name policy --value 'require' \
     --insert '$new_node' --type attr --name name --value 'vmx' \
       /etc/libvirt/qemu/${vm_name}.xml | xmlstarlet select --template --copy-of "//cpu"

 Der Befehl am Ende filtert die Ausgabe auf das Element <cpu ... /> . 

Weitere Informationen

Eigentlich steht die CPU Option "host-passtrough" (in meiner Installation Standardwert) für: nimm die CPU so wie sie ist damit auch nested virtualization funktioniert und irgendwo steht: man sollte dies nicht als Standard nehmen. ??? Woanders habe ich gelesen, dass es wohl das hier beschriebene Verfahren braucht wenn P und E Kerne im Spiel sind? Mal schauen ob ich hier noch Informationen finde. 

  ToDo? 

Code

 

Keine Kommentare:

Kommentar veröffentlichen