TPM für Bhyve-VMs

Yamagi

Possessed With Psi Powers
Teammitglied
Ein Forenmitglied fragte mich auf Matrix wie es mit Windows 11 unter Bhyve aussähe, da ab FreeBSD 14.3 nun ja TPM unterstützt würde. Das führte zu etwa Bastelei und da TPM in Bhyve-VMs eher dünn dokumentiert ist, fasse ich die Erkenntnisse hier einmal zusammen. Das ist der manuelle Weg, Management-Scripte müssen das natürlich entsprechend integrieren. Und es ist der einfachst mögliche Fall, ein simples Software-TPM ohne Certificate Authority. Das ist genug für Windows 11 - wo die allermeisten Nutzer das TPM für verwenden werden wollen - aber wahrscheinlich zu wenig für spezielle Szenarien.

Zuerst muss swtpm installieren werden. Es kann beliebige Software-TPMs zur Verfügung stellen: pkg install swtpm

Jetzt kann ein Software-TPM erstellt werden: swtpm socket --tpmstate backend-uri=file:///pfad/zu/tpm.state --server type=unixio,path=/pfad/zu/tpm.sock --tpm2 --flags not-need-init

Der swtmp-Prozess muss mindestens solange laufen, wie auch die Bhyve-VM läuft. Ein beenden des swtmp-Prozesses beendet auch den Bhyve-Prozess und damit die VM. Die Optionen sind:

  • --tpmstate backend-uri=file:///pfad/zu/tpm.state: Datei, in welcher der TPM-State (also die Daten im TPM) gespeichert werden. Muss für jede VM eindeutig sein, mehrere VM dürfen sich kein TPM teilen. Das führt zu Chaos. Und muss dauerhaft erhalten bleiben, da sonst die im TPM gespeicherten Daten verloren gehen. Windows 11 findet das eher mäßig lustig. Existiert die Datei noch nicht, wird sie angelegt.
  • --server type=unixio,path=/pfad/zu/tpm.sock: Ein Unix Domain Socket für die Kommunikation zwischen swtmp und Bhyve. Muss wieder für jede VM eindeutig sein, wobei sich das eigentlich schon aus der Anforderung, ein TPM pro VM zu nutzen, ergibt.
  • --tpm2: Byhve unterstützt nur und und ausschließlich TPM 2.0. Das muss hier explizit gesagt werden, denn sonst erhält man ein TPM 1.2 und das wird nicht funktionieren. Es führt dann zu seltsamen Fehler im Gast-Betriebssystem.
  • --flags not-need-init: Weist swtpm an auch dann Lese- und Schreibzugriffe auf das TPM zuzulassen, wenn es nicht explizit initialisiert wurde. Notwendig, da die aktuelle UEFI-Firmware für Bhyve es nur so lala initiert. Mit einem zukünftigen Update auf ein aktuelles Tianocorewird das voraussichtlich nicht mehr notwendig sein.

Der swtpm-Prozess kann auch unter einem unpriviligierten Nutzer ausgeführt werden. In jedem Fall sollte die Datei des TPM-State mit minimalen Rechten versehen werden. Wenn sie abhanden kommt, ist es doof. Der Witz eines TPM ist ja gerade, dass die darin gespeicherten Secrets vor unbefugten Zugriffen (einigermaßen) sicher sind.

Nun kann das Software-TPM an die Bhyve-VM gebunden werden.

Für die Kommandozeile ist es: -l tpm,swtpm,/pfad/zu/tpm.sock

Wer wie ich lieber die Config-Datei nutzt, kann es alternativ auch dort einbinden:

Code:
tpm.path=/pfad/zu/tpm.sock
tpm.type=swtpm
tpm.version=2.0

Anschließend wird der Gast das TPM erkennen. Unter Linux taucht ein /dev/tpm0 auf, unter Windows kann man in tpm.msc schauen. Windows 11 lässt sich ohne die bisher notwendigen Hacks installieren und funktioniert. Kleine Erkenntnis am Rande ist noch, dass Windows 11 die Minimalanforderung an die CPU anscheinend nicht prüft, wenn es virtualisiert läuft. Selbst auf dem wirklich alten Core i5-3350 in meinem Testsystem, der weitab von der Minimalanforderung ist, installierte es ohne zu Murren aus dem offiziellen ISO bis zum Desktop durch und ließ sich anschließend updaten.
 
Ein Forenmitglied fragte mich auf Matrix wie es mit Windows 11 unter Bhyve aussähe, da ab FreeBSD 14.3 nun ja TPM unterstützt würde. Das führte zu etwa Bastelei und da TPM in Bhyve-VMs eher dünn dokumentiert ist, fasse ich die Erkenntnisse hier einmal zusammen.
Wer könnte das nur gewesen sein? Aber lustiger Zufall, ich bin auch sehr interessiert an TPM in Bhyve. Um deinen Topic und den Einleitungssatz aber etwas zu präzisieren: Es geht konkret um swtpm. TPM selbst konnte Bhyve schon vor 14.3 (ab 14.2 iirc) via passthru. Voraussetzung hierfür war jedoch, dass auf dem Board ein TPM-Device existiert, das von FreeBSD unterstützt wird (was bei mir nicht der Fall ist).

Anschließend wird der Gast das TPM erkennen. Unter Linux taucht ein /dev/tpm0 auf, unter Windows kann man in tpm.msc schauen. Windows 11 lässt sich ohne die bisher notwendigen Hacks installieren und funktioniert. Kleine Erkenntnis am Rande ist noch, dass Windows 11 die Minimalanforderung an die CPU anscheinend nicht prüft, wenn es virtualisiert läuft. Selbst auf dem wirklich alten Core i5-3350 in meinem Testsystem, der weitab von der Minimalanforderung ist, installierte es ohne zu Murren aus dem offiziellen ISO bis zum Desktop durch und ließ sich anschließend updaten.
Ein erster Zwischenfazit (auch für die Restwelt): Scheinbar funktioniert swtpm ebenfalls nur unter bestimmten Voraussetzungen, denn hier (AMD Threadripper) funktioniert es nicht. Im Logfile von swtpm kommt hier zwar deutlich Output an, sobald ich den Gast starte. Aber weder ein Linux, noch ein Windows-Gast hat ein TPM Device.

Das Logfile:
 

Anhänge

Nein. Das hatte ich zum Schluss auch nur testweise reingenommen, das Ergebnis war aber identlich. Zuvor hatte ich statt backend-uri dir= verwendet, mit dem gleichen Ergebnis.
 
Wer könnte das nur gewesen sein?
Ein erster Zwischenfazit (auch für die Restwelt): Scheinbar funktioniert swtpm ebenfalls nur unter bestimmten Voraussetzungen, denn hier (AMD Threadripper) funktioniert es nicht.
Ja, wer wohl? ;) Zufälle gibt's... Ich schaue gerade auf einem Ryzen. Also etwas Live-Debugging:

Es geht damit los, dass Windows 11 nicht bootet, es bleibt einfach früh im Bootprozess hängen. Leider zu früh für die serielle Konsole. Gestochere zeigt dann, dass Windows das MSR 0xc0010114 abfragt. Das ist AMD SVM. Die virtuelle CPU unterstützt das korrekterweise nicht, daher reagiert Byhve mit General Protection Fault. Windows fängt das anscheinend nicht ab und hängt sich auf. Zum Glück gibt es x86.strictmsr=false, damit gestartet ignoriert Bhyve Zugriffe auf unbekannte MSR. Windows 11 fährt dann hoch, lässt sicher mangels unterstützter Hardware nicht installieren. Leider sagt es nicht, was ihm fehlt.

Also Windows 10 installiert. Das funktioniert einwandfrei, findet aber tatsächlich kein TPM. Dü-düm. Typisch für Windows lässt sich auch durch Graben in der Ereignisanzeige nicht herausfinden, was das Problem sein könnte. Ein Gegentest mit einem Windows 10 in qemu mit KVM Backend zeigt, dass dort ein mit der gleichen Kommandozeile gestartetes swtpm erkannt wird. Also kann es nicht daran liegen... Dabei fällt ins Auge, dass das TPM ein ACPI-Device ist. Ein kurzer Abgleich zeigt, dass Windows es anscheinend unabhängig von Intel oder AMD CPUs unter MSFT0101\TPM erwartet. Buddeln wir im Quellcode, und finden raus, das Bhyve es genau da positioniert: https://github.com/freebsd/freebsd-src/blob/releng/14.3/usr.sbin/bhyve/tpm_device.c#L25

Nun hat Bhyve mehrere Optionen, wer und wie die ACPI-Tabellen generiert werden. Also ein wenig Rumprobieren, letztendlich sind das nur vier Kombinationen. Tatsächlich ist schon der erste Versuch erfolgreich, man muss halt mal Glück haben:

Code:
acpi_tables=true
acpi_tables_in_memory=true

Die erste Option lässt Bhyve und nicht das UEFI die Tabellen generieren und die zweite Option cached sie im RAM. Das widerspricht der Manpage, die sagt:

Code:
     acpi_tables_in_memory   bool       true       bhyve(8) always exposes
                                                   ACPI tables by FwCfg.  For
                                                   backward compatibility
                                                   bhyve copies them into the
                                                   guest memory as well.  This
                                                   can cause problems if the
                                                   guest uses the in-memory
                                                   version, since certain
                                                   advanced features, such as
                                                   TPM emulation, are exposed
                                                   only via FwCfg.  Therefore,
                                                   it is recommended to set
                                                   this flag to false when
                                                   running Windows guests.

Zumindest ist das genug, um Hoffnung zu wecken, dass Win 11 nun funktioniert. Also ein neues ZFS Volume gebaut und ab dafür. Und tatsächlich ist Windows 11 nun glücklich und installiert bis zum Desktop durch. Die ganze Sache hat gute 90 Minuten gedauert. Vor allem, da Windows immer so langsam ist...

Zusammengefasst:

  • Für Windows 11 auf AMD muss Bhyve mit -w gestartet oder in der Config x86.strictmsr=false gesetzt sein.
  • Damit das TPM funktioniert muss Bhyve die ACPI-Tabellen erstellen und im Speicher des Gast cachen. Letzteres ist die Standardeinstellung, die Doku empfielt fälschlicherweise das abzuschalten. Damit reicht es aus, Bhyve mit -A zu starten oder in der Config acpi_tables=true zu setzen.

Zumindest funktioniert das für mich. Für dich schauen wir mal. :)
 
Der Hauptknackpunkt waren die acpi_tables=true. Danach lief TPM sofort. Aktuelle Windows 11 ISOs booten hier weiterhin nicht. Mit einer älteren ISO funktioniert es hingegegen. x86.strichtmsr=false war hier übrigens bereits gesetzt.

To be continued...
 
... für diese "Zwischeninformation" möchte ich meinen letzten Beitrag nicht editieren. Was mal (wieder) gesagt werden "muss":

Danke, Yamagi!
 
Ich hab da noch ein paar Gedanken zu, die aber nicht hilfreich sind:

  • Wir müssen bedenken, dass dein Threadripper 2000 von Windows 11 nicht unterstützt wird. Es kann also durchaus sein, dass ihm Funktionen fehlen, Windows das nich sauber abfängt - siehe auch den General Protection Fault oben - und einfach hängen bleibt...
  • ...allerdings sollte es dann auch meinem viele Jahre älteren Ivy Bridge Core i5 erst recht nicht laufen. Keine Ahnung, ob der Threadripper überhaupt Funktionen nicht hat, die meine alte CPU hatte. Aber selbst wenn, sind die so obskur, dass es Windows nicht am Booten hindern sollte.

Mit spukt noch im Kopf rum, dass Windows 11 bei mit an dem MSR für AMD SVM rumrührte. Das ist einfach nur falsch. Denn eigentlich soll das Betriebssystem in der CPUID schauen, ob ein Feature unterstützt ist. Und nur wenn es das ist, auf die MSR zugreifen. Sonst kann und wird Murks passieren, bis eben hin zur hängenden CPU. Bhyve setzt AMD SVM in der CPUID nicht, d.h. Windows 11 geht blind davon aus, dass das Feature unterstützt wäre. Daraus kann man folgern, dass Windows 11 generell CPU-Features nicht abfragt, sondern auf eine Tabelle zurückgreift, in der steht, welche CPU was kann. Und wenn da noch Features drin sind, die Bhyve nicht gegenüber dem Gastsystem anbietet, fliegt die ganze Suppe auseinander... Richtig obskur wäre es, wenn eine Installation von einem der bootenden älteren Win 11 ISO mit allen Updates eingespielt dann doch bootet.

Man könnte nun im nächsten Schritt erstmal schauen, ob das Windows 11 ISO nativ bootet. Wenn es das tut, mal ausprobieren, ob es mit qemu + KVM oder VirtualBox geht. Wenn ja, die CPUID davon mit der von Bhyve generierten abgleichen und schauen, welche CPU-Features Bhyve nicht aktiviert. Dann mit etwas logisch Denken versuchen zu isolieren, welches Schuld ist und schauen, ob man dem beikommt. Nicht schön, aber immer noch schöner als mit einem Low Lever Debugger dabeizugehen und zu versuchen aus Windows 11 Binärsuppe was rauszukratzen.

Eher danke dir :) Ganz im Ernst, das hat mir wirklich Spaß gemacht. Genau wegen sowas bin ich mal in die IT gegangen. Und sind wir ehrlich: Wenn Computer funktionieren würden, wären viele in diesem Forum arbeitslos... ^^
 
Zurück
Oben