AVF-Architektur

Android bietet eine Referenzimplementierung aller Komponenten, die zur Implementierung des Android Virtualization Framework erforderlich sind. Derzeit ist diese Implementierung auf ARM64 beschränkt. Auf dieser Seite wird die Framework-Architektur erläutert.

Hintergrund

Die Arm-Architektur erlaubt bis zu vier Ausnahmeebenen, wobei Ausnahmeebene 0 (EL0) die geringste Privilegierung und Ausnahmeebene 3 (EL3) die höchste Privilegierung aufweist. Der größte Teil der Android-Codebasis (alle Userspace-Komponenten) läuft auf EL0. Der Rest dessen, was allgemein als „Android“ bezeichnet wird, ist der Linux-Kernel, der auf EL1 läuft.

Die EL2-Schicht ermöglicht die Einführung eines Hypervisors, der die Isolierung von Speicher und Geräten in einzelne pVMs bei EL1/EL0 ermöglicht, mit starken Vertraulichkeits- und Integritätsgarantien.

Hypervisor

Die geschützte Kernel-basierte virtuelle Maschine (pKVM) basiert auf dem Linux-KVM-Hypervisor , der um die Möglichkeit erweitert wurde, den Zugriff auf die Nutzdaten einzuschränken, die in virtuellen Gastmaschinen ausgeführt werden, die zum Zeitpunkt der Erstellung als „geschützt“ gekennzeichnet waren.

KVM/arm64 unterstützt verschiedene Ausführungsmodi, abhängig von der Verfügbarkeit bestimmter CPU-Funktionen, nämlich der Virtualization Host Extensions (VHE) (ARMv8.1 und höher). In einem dieser Modi, der allgemein als Nicht-VHE-Modus bekannt ist, wird der Hypervisor-Code beim Booten aus dem Kernel-Image abgespalten und auf EL2 installiert, während der Kernel selbst auf EL1 läuft. Obwohl Teil der Linux-Codebasis, ist die EL2-Komponente von KVM eine kleine Komponente, die für den Wechsel zwischen mehreren EL1s verantwortlich ist und vollständig vom Kernel des Hosts gesteuert wird. Die Hypervisor-Komponente wird mit Linux kompiliert, befindet sich jedoch in einem separaten, dedizierten Speicherabschnitt des vmlinux -Images. pKVM nutzt dieses Design, indem es den Hypervisor-Code um neue Funktionen erweitert, die es ermöglichen, Einschränkungen für den Android-Host-Kernel und den Benutzerbereich festzulegen und den Host-Zugriff auf Gastspeicher und Hypervisor zu beschränken.

pKVM-Anbietermodule

Ein pKVM-Anbietermodul ist ein hardwarespezifisches Modul, das gerätespezifische Funktionen enthält, beispielsweise IOMMU-Treiber (Input-Output Memory Management Unit). Mit diesen Modulen können Sie Sicherheitsfunktionen, die Ausnahmeebene 2 (EL2)-Zugriff erfordern, auf pKVM portieren.

Informationen zum Implementieren und Laden eines pKVM-Anbietermoduls finden Sie unter Implementieren eines pKVM-Anbietermoduls .

Bootvorgang

Die folgende Abbildung zeigt den pKVM-Startvorgang:

pKVM-Startvorgang

Abbildung 1. pKVM-Startvorgang

  1. Der Bootloader betritt den generischen Kernel bei EL2.
  2. Der generische Kernel erkennt, dass er auf EL2 läuft und verweigert sich selbst die Nutzung von EL1, während pKVM und seine Module weiterhin auf EL2 laufen. Darüber hinaus werden zu diesem Zeitpunkt pKVM-Anbietermodule geladen.
  3. Der generische Kernel startet normal und lädt alle erforderlichen Gerätetreiber, bis er den Benutzerbereich erreicht. Zu diesem Zeitpunkt ist pKVM vorhanden und verwaltet die Seitentabellen der Stufe 2.

Der Startvorgang vertraut darauf, dass der Bootloader die Integrität des Kernel-Images nur während des frühen Startvorgangs aufrechterhält. Wenn der Kernel deprivilegiert wird, wird er vom Hypervisor nicht mehr als vertrauenswürdig betrachtet, der dann dafür verantwortlich ist, sich selbst zu schützen, selbst wenn der Kernel gefährdet ist.

Da sich der Android-Kernel und der Hypervisor im selben Binärbild befinden, ist eine sehr eng gekoppelte Kommunikationsschnittstelle zwischen ihnen möglich. Diese enge Kopplung garantiert atomare Aktualisierungen der beiden Komponenten, wodurch die Notwendigkeit entfällt, die Schnittstelle zwischen ihnen stabil zu halten, und ein hohes Maß an Flexibilität bietet, ohne die langfristige Wartbarkeit zu beeinträchtigen. Die enge Kopplung ermöglicht auch Leistungsoptimierungen, wenn beide Komponenten zusammenarbeiten können, ohne die vom Hypervisor bereitgestellten Sicherheitsgarantien zu beeinträchtigen.

Darüber hinaus ermöglicht die Einführung von GKI im Android-Ökosystem automatisch die Bereitstellung des pKVM-Hypervisors auf Android-Geräten in derselben Binärdatei wie der Kernel.

Zugriffsschutz auf den CPU-Speicher

Die Arm-Architektur spezifiziert eine Speicherverwaltungseinheit (MMU), die in zwei unabhängige Stufen aufgeteilt ist, die beide zur Implementierung der Adressübersetzung und Zugriffskontrolle auf verschiedene Teile des Speichers verwendet werden können. Die MMU der Stufe 1 wird von EL1 gesteuert und ermöglicht eine erste Ebene der Adressübersetzung. Die MMU der Stufe 1 wird von Linux verwendet, um den virtuellen Adressraum zu verwalten, der jedem Userspace-Prozess und seinem eigenen virtuellen Adressraum zur Verfügung gestellt wird.

Die MMU der Stufe 2 wird von EL2 gesteuert und ermöglicht die Anwendung einer zweiten Adressübersetzung auf die Ausgangsadresse der MMU der Stufe 1, was zu einer physikalischen Adresse (PA) führt. Die Übersetzung der Stufe 2 kann von Hypervisoren verwendet werden, um Speicherzugriffe von allen Gast-VMs zu steuern und zu übersetzen. Wie in Abbildung 2 dargestellt, wird die Ausgangsadresse der Stufe 1, wenn beide Übersetzungsstufen aktiviert sind, als intermediäre physische Adresse (IPA) bezeichnet. Hinweis: Die virtuelle Adresse (VA) wird in eine IPA und dann in eine PA übersetzt.

Zugriffsschutz auf den CPU-Speicher

Abbildung 2. CPU-Speicherzugriffsschutz

In der Vergangenheit wurde KVM mit aktivierter Übersetzung der Stufe 2 ausgeführt, während Gäste ausgeführt wurden, und mit deaktivierter Stufe 2, während der Host-Linux-Kernel ausgeführt wurde. Diese Architektur ermöglicht, dass Speicherzugriffe von der MMU der Host-Stufe 1 über die MMU der Stufe 2 erfolgen und somit einen uneingeschränkten Zugriff vom Host auf die Gast-Speicherseiten ermöglichen. Andererseits ermöglicht pKVM den Schutz der Stufe 2 auch im Host-Kontext und überträgt dem Hypervisor die Verantwortung für den Schutz der Gastspeicherseiten anstelle des Hosts.

KVM nutzt die Adressübersetzung in Stufe 2 voll aus, um komplexe IPA/PA-Zuordnungen für Gäste zu implementieren, wodurch trotz physischer Fragmentierung die Illusion eines zusammenhängenden Speichers für Gäste entsteht. Die Verwendung der MMU der Stufe 2 für den Host ist jedoch nur auf die Zugriffskontrolle beschränkt. Die Host-Stufe 2 ist identitätszugeordnet, wodurch sichergestellt wird, dass zusammenhängender Speicher im IPA-Bereich des Hosts im PA-Bereich zusammenhängend ist. Diese Architektur ermöglicht die Verwendung großer Zuordnungen in der Seitentabelle und verringert somit den Druck auf den Translation Lookaside Buffer (TLB). Da eine Identitätszuordnung durch PA indiziert werden kann, wird die Hoststufe 2 auch verwendet, um den Seitenbesitz direkt in der Seitentabelle zu verfolgen.

DMA-Schutz (Direct Memory Access).

Wie bereits beschrieben, ist das Aufheben der Zuordnung von Gastseiten vom Linux-Host in den CPU-Seitentabellen ein notwendiger, aber unzureichender Schritt zum Schutz des Gastspeichers. pKVM muss außerdem vor Speicherzugriffen durch DMA-fähige Geräte unter der Kontrolle des Host-Kernels und vor der Möglichkeit eines DMA-Angriffs durch einen böswilligen Host schützen. Um zu verhindern, dass ein solches Gerät auf den Gastspeicher zugreift, erfordert pKVM Hardware für die Eingabe-Ausgabe-Speicherverwaltungseinheit (IOMMU) für jedes DMA-fähige Gerät im System, wie in Abbildung 3 dargestellt.

DMA-Speicherzugriffsschutz

Abbildung 3. DMA-Speicherzugriffsschutz

Zumindest bietet die IOMMU-Hardware die Möglichkeit, einem Gerät Lese-/Schreibzugriff auf den physischen Speicher mit Seitengranularität zu gewähren und zu entziehen. Allerdings schränkt diese IOMMU-Hardware die Verwendung von Geräten in pVMs ein, da sie eine Identitätszuordnung der Stufe 2 voraussetzen.

Um die Isolation zwischen virtuellen Maschinen sicherzustellen, müssen für verschiedene Entitäten generierte Speichertransaktionen von der IOMMU unterscheidbar sein, damit der entsprechende Satz von Seitentabellen für die Übersetzung verwendet werden kann.

Darüber hinaus ist die Reduzierung der Menge an SoC-spezifischem Code bei EL2 eine Schlüsselstrategie zur Reduzierung der gesamten Trusted Computing Base (TCB) von pKVM und steht im Widerspruch zur Einbeziehung von IOMMU-Treibern in den Hypervisor. Um dieses Problem zu entschärfen, ist der Host bei EL1 für zusätzliche IOMMU-Verwaltungsaufgaben verantwortlich, wie z. B. Energieverwaltung, Initialisierung und gegebenenfalls Interrupt-Verarbeitung.

Wenn der Host jedoch die Kontrolle über den Gerätestatus erhält, werden zusätzliche Anforderungen an die Programmierschnittstelle der IOMMU-Hardware gestellt, um sicherzustellen, dass Berechtigungsprüfungen nicht auf andere Weise umgangen werden können, beispielsweise nach einem Geräte-Reset.

Eine standardmäßige und gut unterstützte IOMMU für Arm-Geräte, die sowohl Isolierung als auch direkte Zuweisung ermöglicht, ist die Arm System Memory Management Unit (SMMU)-Architektur. Diese Architektur ist die empfohlene Referenzlösung.

Speicherbesitz

Beim Booten wird davon ausgegangen, dass der gesamte Nicht-Hypervisor-Speicher dem Host gehört und als solcher vom Hypervisor verfolgt wird. Wenn eine pVM erzeugt wird, spendet der Host Speicherseiten, um das Booten zu ermöglichen, und der Hypervisor überträgt den Besitz dieser Seiten vom Host auf die pVM. Daher führt der Hypervisor Zugriffskontrollbeschränkungen in der Seitentabelle der Stufe 2 des Hosts ein, um zu verhindern, dass er erneut auf die Seiten zugreift, und sorgt so für Vertraulichkeit für den Gast.

Die Kommunikation zwischen Gastgeber und Gästen wird durch die kontrollierte gemeinsame Nutzung des Speichers zwischen ihnen ermöglicht. Gästen ist es gestattet, einige ihrer Seiten über einen Hypercall wieder mit dem Host zu teilen, der den Hypervisor anweist, diese Seiten in der Host-Seitentabelle der Stufe 2 neu zuzuordnen. Ebenso wird die Kommunikation des Hosts mit TrustZone durch Speicherfreigabe- und/oder Leihvorgänge ermöglicht, die alle von pKVM unter Verwendung der Firmware Framework for Arm (FF-A)-Spezifikation genau überwacht und gesteuert werden.

Da sich der Speicherbedarf einer pVM im Laufe der Zeit ändern kann, wird ein Hypercall bereitgestellt, der es ermöglicht, das Eigentum an bestimmten Seiten, die dem Aufrufer gehören, wieder an den Host abzugeben. In der Praxis wird dieser Hypercall mit dem Virtio-Ballon-Protokoll verwendet, um dem VMM zu ermöglichen, Speicher von der pVM zurückzufordern, und damit der pVM den VMM auf kontrollierte Weise über freigegebene Seiten benachrichtigen kann.

Der Hypervisor ist dafür verantwortlich, den Besitz aller Speicherseiten im System zu verfolgen und festzustellen, ob diese gemeinsam genutzt oder an andere Entitäten verliehen werden. Der größte Teil dieser Statusverfolgung erfolgt mithilfe von Metadaten, die an die Seitentabellen der Stufe 2 des Hosts und der Gäste angehängt sind, wobei reservierte Bits in den Seitentabelleneinträgen (PTEs) verwendet werden, die, wie der Name schon sagt, für die Verwendung durch Software reserviert sind.

Der Host muss sicherstellen, dass er nicht versucht, auf Seiten zuzugreifen, die durch den Hypervisor unzugänglich gemacht wurden. Ein illegaler Host-Zugriff führt dazu, dass der Hypervisor eine synchrone Ausnahme in den Host einfügt, was entweder dazu führen kann, dass die zuständige Userspace-Task ein SEGV-Signal empfängt oder der Host-Kernel abstürzt. Um versehentliche Zugriffe zu verhindern, werden Seiten, die an Gäste gespendet werden, vom Host-Kernel nicht zum Austauschen oder Zusammenführen zugelassen.

Interrupt-Handling und Timer

Interrupts sind ein wesentlicher Bestandteil der Art und Weise, wie ein Gast mit Geräten interagiert, und für die Kommunikation zwischen CPUs, wobei Interprozessor-Interrupts (IPIs) der Hauptkommunikationsmechanismus sind. Das KVM-Modell besteht darin, die gesamte virtuelle Interrupt-Verwaltung an den Host in EL1 zu delegieren, der sich zu diesem Zweck als nicht vertrauenswürdiger Teil des Hypervisors verhält.

pKVM bietet eine vollständige Generic Interrupt Controller Version 3 (GICv3)-Emulation basierend auf dem vorhandenen KVM-Code. Timer und IPIs werden als Teil dieses nicht vertrauenswürdigen Emulationscodes behandelt.

GICv3-Unterstützung

Die Schnittstelle zwischen EL1 und EL2 muss sicherstellen, dass der vollständige Interrupt-Status für den EL1-Host sichtbar ist, einschließlich Kopien der Hypervisor-Register im Zusammenhang mit Interrupts. Diese Sichtbarkeit wird in der Regel durch gemeinsam genutzte Speicherbereiche erreicht, einen pro virtueller CPU (vCPU).

Der Systemregister-Laufzeitunterstützungscode kann vereinfacht werden, um nur das Software Generated Interrupt Register (SGIR) und das Deactivate Interrupt Register (DIR)-Register-Trapping zu unterstützen. Die Architektur schreibt vor, dass diese Register immer auf EL2 trappen, während die anderen Traps bisher nur nützlich waren, um Errata zu mildern. Alles andere wird in Hardware erledigt.

Auf der MMIO-Seite wird alles bei EL1 emuliert und die gesamte aktuelle Infrastruktur in KVM wiederverwendet. Schließlich wird Wait for Interrupt (WFI) immer an EL1 weitergeleitet, da dies eines der grundlegenden Planungsprimitive ist, die KVM verwendet.

Timer-Unterstützung

Der Komparatorwert für den virtuellen Timer muss EL1 auf jedem Trapping-WFI zur Verfügung gestellt werden, damit EL1 Timer-Interrupts injizieren kann, während die vCPU blockiert ist. Der physische Timer wird vollständig emuliert und alle Traps werden an EL1 weitergeleitet.

MMIO-Handhabung

Um mit dem Virtual Machine Monitor (VMM) zu kommunizieren und eine GIC-Emulation durchzuführen, müssen MMIO-Traps zur weiteren Triage an den Host in EL1 zurückgeleitet werden. pKVM erfordert Folgendes:

  • IPA und Größe des Zugriffs
  • Daten im Falle eines Schreibvorgangs
  • Endianness der CPU zum Zeitpunkt des Trappings

Darüber hinaus werden Traps mit einem General Purpose Register (GPR) als Quelle/Ziel mithilfe eines abstrakten Transfer-Pseudoregisters weitergeleitet.

Gastschnittstellen

Ein Gast kann mit einem geschützten Gast über eine Kombination aus Hypercalls und Speicherzugriff auf eingeschlossene Regionen kommunizieren. Hypercalls werden gemäß dem SMCCC-Standard bereitgestellt, wobei ein Bereich für eine Anbieterzuweisung durch KVM reserviert ist. Die folgenden Hypercalls sind für pKVM-Gäste von besonderer Bedeutung.

Generische Hypercalls

  • PSCI bietet dem Gast einen Standardmechanismus zur Steuerung des Lebenszyklus seiner vCPUs, einschließlich Online-, Offline- und Herunterfahren des Systems.
  • TRNG bietet einen Standardmechanismus für den Gast, um Entropie vom pKVM anzufordern, der den Anruf an EL3 weiterleitet. Dieser Mechanismus ist besonders nützlich, wenn dem Host nicht vertraut werden kann, dass er einen Hardware-Zufallszahlengenerator (RNG) virtualisiert.

pKVM-Hypercalls

  • Speicherfreigabe mit dem Host. Der Host hat zunächst keinen Zugriff auf den gesamten Gastspeicher, der Hostzugriff ist jedoch für die Kommunikation über gemeinsam genutzten Speicher und für paravirtualisierte Geräte erforderlich, die auf gemeinsam genutzte Puffer angewiesen sind. Hypercalls zum Teilen und Aufheben der Freigabe von Seiten mit dem Host ermöglichen es dem Gast, genau zu entscheiden, welche Teile des Speichers für den Rest von Android zugänglich gemacht werden, ohne dass ein Handshake erforderlich ist.
  • Speicherüberlassung an den Host. Der gesamte Gastspeicher gehört normalerweise dem Gast, bis er zerstört wird. Dieser Zustand kann für langlebige VMs mit im Laufe der Zeit schwankenden Speicheranforderungen unzureichend sein. Der Hypercall relinquish ermöglicht es einem Gast, den Besitz von Seiten explizit zurück an den Host zu übertragen, ohne dass eine Beendigung des Gastes erforderlich ist.
  • Speicherzugriffs-Trapping auf den Host. Wenn ein KVM-Gast auf eine Adresse zugreift, die keinem gültigen Speicherbereich entspricht, verlässt der vCPU-Thread traditionell den Host und der Zugriff wird normalerweise für MMIO verwendet und vom VMM im Benutzerbereich emuliert. Um diese Handhabung zu erleichtern, muss pKVM Details über die fehlerhafte Anweisung wie ihre Adresse, Registerparameter und möglicherweise deren Inhalte an den Host zurückmelden, wodurch vertrauliche Daten eines geschützten Gasts unbeabsichtigt offengelegt werden könnten, wenn die Falle nicht vorhergesehen wurde. pKVM löst dieses Problem, indem es diese Fehler als schwerwiegend behandelt, es sei denn, der Gast hat zuvor einen Hypercall ausgegeben, um den fehlerhaften IPA-Bereich als einen zu identifizieren, für den Zugriffe zurück zum Host erfolgen dürfen. Diese Lösung wird als MMIO-Guard bezeichnet.

Virtuelles E/A-Gerät (virtio)

Virtio ist ein beliebter, portabler und ausgereifter Standard für die Implementierung und Interaktion mit paravirtualisierten Geräten. Die meisten Geräte, die geschützten Gästen zugänglich sind, werden mit Virtio implementiert. Virtio unterstützt auch die vsock-Implementierung, die für die Kommunikation zwischen einem geschützten Gast und dem Rest von Android verwendet wird.

Virtio-Geräte werden normalerweise vom VMM im Benutzerbereich des Hosts implementiert, der abgefangene Speicherzugriffe vom Gast auf die MMIO-Schnittstelle des Virtio-Geräts abfängt und das erwartete Verhalten emuliert. Der MMIO-Zugriff ist relativ teuer, da jeder Zugriff auf das Gerät einen Roundtrip zum VMM und zurück erfordert, sodass der Großteil der eigentlichen Datenübertragung zwischen dem Gerät und dem Gast über eine Reihe von Virtqueues im Speicher erfolgt. Eine zentrale Annahme von virtio ist, dass der Host beliebig auf den Gastspeicher zugreifen kann. Diese Annahme wird im Design der Virtqueue deutlich, die möglicherweise Zeiger auf Puffer im Gast enthält, auf die die Geräteemulation direkt zugreifen soll.

Obwohl die zuvor beschriebenen Speicherfreigabe-Hyperaufrufe verwendet werden könnten, um Virtio-Datenpuffer vom Gast an den Host weiterzugeben, erfolgt diese Freigabe notwendigerweise auf Seitengranularität und könnte dazu führen, dass mehr Daten als erforderlich offengelegt werden, wenn die Puffergröße kleiner als die einer Seite ist . Stattdessen ist der Gast so konfiguriert, dass er sowohl die Virtqueues als auch die entsprechenden Datenpuffer aus einem festen Fenster des gemeinsam genutzten Speichers zuweist, wobei die Daten je nach Bedarf in das Fenster kopiert (bounced) werden.

Virtuelles Gerät

Abbildung 4. Virtio-Gerät

Interaktion mit TrustZone

Obwohl Gäste nicht direkt mit TrustZone interagieren können, muss der Host dennoch in der Lage sein, SMC-Aufrufe in die sichere Welt zu tätigen. Diese Aufrufe können physisch adressierte Speicherpuffer angeben, auf die der Host keinen Zugriff hat. Da die sichere Software die Zugänglichkeit des Puffers im Allgemeinen nicht kennt, könnte ein böswilliger Host diesen Puffer verwenden, um einen verwirrten Stellvertreterangriff durchzuführen (analog zu einem DMA-Angriff). Um solche Angriffe zu verhindern, fängt pKVM alle Host-SMC-Aufrufe an EL2 ab und fungiert als Proxy zwischen dem Host und dem sicheren Monitor an EL3.

PSCI-Aufrufe vom Host werden mit minimalen Änderungen an die EL3-Firmware weitergeleitet. Insbesondere wird der Einstiegspunkt für eine CPU, die online geht oder aus dem Suspend-Modus wieder aufgenommen wird, neu geschrieben, sodass die Seitentabelle der Stufe 2 bei EL2 installiert wird, bevor sie bei EL1 zum Host zurückkehrt. Während des Bootens wird dieser Schutz durch pKVM erzwungen.

Diese Architektur basiert darauf, dass der SoC PSCI unterstützt, vorzugsweise durch die Verwendung einer aktuellen Version von TF-A als EL3-Firmware.

Das Firmware Framework for Arm (FF-A) standardisiert die Interaktionen zwischen der normalen und der sicheren Welt, insbesondere bei Vorhandensein eines sicheren Hypervisors. Ein Großteil der Spezifikation definiert einen Mechanismus zur gemeinsamen Nutzung von Speicher mit der sicheren Welt, wobei sowohl ein gemeinsames Nachrichtenformat als auch ein klar definiertes Berechtigungsmodell für die zugrunde liegenden Seiten verwendet werden. pKVM leitet FF-A-Nachrichten weiter, um sicherzustellen, dass der Host nicht versucht, Speicher mit der sicheren Seite zu teilen, für die er nicht über ausreichende Berechtigungen verfügt.

Diese Architektur basiert auf der Software der sicheren Welt, die das Speicherzugriffsmodell durchsetzt, um sicherzustellen, dass vertrauenswürdige Apps und jede andere Software, die in der sicheren Welt ausgeführt wird, nur dann auf den Speicher zugreifen können, wenn dieser entweder ausschließlich der sicheren Welt gehört oder über FF explizit mit ihr geteilt wurde -A. Auf einem System mit S-EL2 sollte die Durchsetzung des Speicherzugriffsmodells durch einen Secure Partition Manager Core (SPMC) wie Hafnium erfolgen, der Seitentabellen der Stufe 2 für die sichere Welt verwaltet. Auf einem System ohne S-EL2 kann der TEE stattdessen ein Speicherzugriffsmodell über seine Seitentabellen der Stufe 1 erzwingen.

Wenn der SMC-Aufruf an EL2 kein PSCI-Aufruf oder keine FF-A-definierte Nachricht ist, werden nicht behandelte SMCs an EL3 weitergeleitet. Es wird davon ausgegangen, dass die (notwendigerweise vertrauenswürdige) sichere Firmware nicht verarbeitete SMCs sicher verarbeiten kann, da die Firmware die erforderlichen Vorsichtsmaßnahmen zur Aufrechterhaltung der pVM-Isolation versteht.

Monitor für virtuelle Maschinen

crosvm ist ein Virtual Machine Monitor (VMM), der virtuelle Maschinen über die KVM-Schnittstelle von Linux ausführt. Was crosvm einzigartig macht, ist sein Fokus auf Sicherheit durch die Verwendung der Programmiersprache Rust und einer Sandbox um virtuelle Geräte zum Schutz des Host-Kernels. Weitere Informationen zu crosvm finden Sie in der offiziellen Dokumentation hier .

Dateideskriptoren und Ioctls

KVM stellt das /dev/kvm Zeichengerät dem Userspace mit ioctls zur Verfügung, aus denen die KVM-API besteht. Die Ioctls gehören zu den folgenden Kategorien:

  • System-ioctls fragen globale Attribute ab und legen sie fest, die sich auf das gesamte KVM-Subsystem auswirken, und erstellen pVMs.
  • VM-Ioctls fragen Attribute ab und legen sie fest, die virtuelle CPUs (vCPUs) und Geräte erstellen und sich auf eine gesamte pVM auswirken, beispielsweise einschließlich des Speicherlayouts und der Anzahl virtueller CPUs (vCPUs) und Geräte.
  • vCPU-ioctls fragen Attribute ab und legen sie fest, die den Betrieb einer einzelnen virtuellen CPU steuern.
  • Geräte-Ioctls fragen Attribute ab und legen sie fest, die den Betrieb eines einzelnen virtuellen Geräts steuern.

Jeder Crosvm-Prozess führt genau eine Instanz einer virtuellen Maschine aus. Dieser Prozess verwendet das KVM_CREATE_VM -System-Ioctl, um einen VM-Dateideskriptor zu erstellen, der zum Ausgeben von pVM-Ioctls verwendet werden kann. Ein KVM_CREATE_VCPU oder KVM_CREATE_DEVICE ioctl auf einem VM-FD erstellt eine vCPU/ein Gerät und gibt einen Dateideskriptor zurück, der auf die neue Ressource verweist. ioctls auf einer vCPU oder einem Geräte-FD können zur Steuerung des Geräts verwendet werden, das mit ioctl auf einem VM-FD erstellt wurde. Für vCPUs umfasst dies die wichtige Aufgabe, Gastcode auszuführen.

Intern registriert crosvm die Dateideskriptoren der VM mithilfe der Edge-getriggerten epoll Schnittstelle beim Kernel. Der Kernel benachrichtigt Crosvm dann, wenn in einem der Dateideskriptoren ein neues Ereignis ansteht.

pKVM fügt eine neue Funktion hinzu, KVM_CAP_ARM_PROTECTED_VM , die verwendet werden kann, um Informationen über die pVM-Umgebung abzurufen und den geschützten Modus für eine VM einzurichten. crosvm verwendet dies während der pVM-Erstellung, wenn das Flag --protected-vm übergeben wird, um die entsprechende Menge an Speicher für die pVM-Firmware abzufragen und zu reservieren und dann den geschützten Modus zu aktivieren.

Speicherzuweisung

Eine der Hauptaufgaben eines VMM besteht darin, den Speicher der VM zuzuweisen und ihr Speicherlayout zu verwalten. crosvm generiert ein festes Speicherlayout, das in der folgenden Tabelle näher beschrieben wird.

FDT im Normalmodus PHYS_MEMORY_END - 0x200000
Freiraum ...
Ramdisk ALIGN_UP(KERNEL_END, 0x1000000)
Kernel 0x80080000
Bootloader 0x80200000
FDT im BIOS-Modus 0x80000000
Physische Speicherbasis 0x80000000
pVM-Firmware 0x7FE00000
Gerätespeicher 0x10000 - 0x40000000

Physischer Speicher wird mit mmap zugewiesen und der Speicher wird der VM gespendet, um ihre Speicherbereiche, genannt memslots , mit dem KVM_SET_USER_MEMORY_REGION ioctl zu füllen. Der gesamte Gast-pVM-Speicher wird daher der Crosvm-Instanz zugeordnet, die ihn verwaltet, und kann dazu führen, dass der Prozess beendet wird (die VM beendet wird), wenn dem Host der freie Speicher ausgeht. Wenn eine VM gestoppt wird, wird der Speicher automatisch vom Hypervisor gelöscht und an den Host-Kernel zurückgegeben.

Unter regulärem KVM behält der VMM Zugriff auf den gesamten Gastspeicher. Bei pKVM wird die Zuordnung des Gastspeichers zum physischen Adressraum des Hosts aufgehoben, wenn er dem Gast gespendet wird. Die einzige Ausnahme ist Speicher, der explizit vom Gast zurückgegeben wird, z. B. bei Virtio-Geräten.

MMIO-Regionen im Adressraum des Gastes bleiben nicht zugeordnet. Der Zugriff des Gastes auf diese Regionen wird blockiert und führt zu einem E/A-Ereignis auf dem VM-FD. Dieser Mechanismus wird zur Implementierung virtueller Geräte verwendet. Im geschützten Modus muss der Gast mithilfe eines Hypercalls bestätigen, dass ein Bereich seines Adressraums für MMIO verwendet wird, um das Risiko eines versehentlichen Informationsverlusts zu verringern.

Terminplanung

Jede virtuelle CPU wird durch einen POSIX-Thread repräsentiert und vom Host-Linux-Scheduler geplant. Der Thread ruft KVM_RUN ioctl auf dem vCPU-FD auf, was dazu führt, dass der Hypervisor zum Gast-vCPU-Kontext wechselt. Der Host-Scheduler berücksichtigt die in einem Gastkontext verbrachte Zeit als vom entsprechenden vCPU-Thread genutzte Zeit. KVM_RUN kehrt zurück, wenn ein Ereignis vorliegt, das vom VMM verarbeitet werden muss, z. B. E/A, Ende des Interrupts oder Anhalten der vCPU. Der VMM verarbeitet das Ereignis und ruft KVM_RUN erneut auf.

Während KVM_RUN bleibt der Thread vom Host-Scheduler präemptiv, mit Ausnahme der Ausführung des EL2-Hypervisor-Codes, der nicht präemptiv ist. Die Gast-PVM selbst verfügt über keinen Mechanismus zur Steuerung dieses Verhaltens.

Da alle vCPU-Threads wie alle anderen Userspace-Aufgaben geplant werden, unterliegen sie allen Standard-QoS-Mechanismen. Insbesondere kann jeder vCPU-Thread physischen CPUs zugeordnet, in CPU-Sets platziert, mithilfe der Auslastungsbegrenzung erhöht oder begrenzt werden, seine Prioritäts-/Planungsrichtlinie kann geändert werden und vieles mehr.

Virtuelle Geräte

crosvm unterstützt eine Reihe von Geräten, darunter die folgenden:

  • virtio-blk für zusammengesetzte Disk-Images, schreibgeschützt oder Lese-/Schreibzugriff
  • vhost-vsock für die Kommunikation mit dem Host
  • Virtio-pci als Virtio-Transport
  • pl030 Echtzeituhr (RTC)
  • 16550a UART für serielle Kommunikation

pVM-Firmware

Die pVM-Firmware (pvmfw) ist der erste Code, der von einer pVM ausgeführt wird, ähnlich dem Boot-ROM eines physischen Geräts. Das Hauptziel von pvmfw besteht darin, einen sicheren Bootstrap durchzuführen und das einzigartige Geheimnis der pVM abzuleiten. pvmfw ist nicht auf die Verwendung mit einem bestimmten Betriebssystem wie Microdroid beschränkt, solange das Betriebssystem von crosvm unterstützt wird und ordnungsgemäß signiert wurde.

Die pvmfw-Binärdatei wird in einer gleichnamigen Flash-Partition gespeichert und über OTA aktualisiert.

Gerätestart

Die folgende Schrittfolge wird zum Startvorgang eines pKVM-fähigen Geräts hinzugefügt:

  1. Der Android Bootloader (ABL) lädt pvmfw von seiner Partition in den Speicher und überprüft das Image.
  2. Die ABL erhält ihre DICE-Geheimnisse (Compound Device Identifiers (CDIs) und Boot Certificate Chain (BCC)) von einem Root of Trust.
  3. Die ABL führt die Messung und DICE-Ableitung der Geheimnisse (CDIs) von pvmfw durch und hängt sie an die Binärdatei von pvmfw an.
  4. Die ABL fügt dem DT einen linux,pkvm-guest-firmware-memory reservierten Speicherbereichsknoten hinzu, der den Speicherort und die Größe der pvmfw-Binärdatei und die Geheimnisse beschreibt, die sie im vorherigen Schritt abgeleitet hat.
  5. Der ABL übergibt die Kontrolle an Linux und Linux initialisiert pKVM.
  6. pKVM hebt die Zuordnung des pvmfw-Speicherbereichs zu den Seitentabellen der Stufe 2 des Hosts auf und schützt ihn während der gesamten Betriebszeit des Geräts vor dem Host (und den Gästen).

Nach dem Gerätestart wird Microdroid gemäß den Schritten im Abschnitt „Startsequenz“ des Microdroid- Dokuments gestartet.

pVM-Boot

Beim Erstellen einer pVM muss crosvm (oder ein anderer VMM) einen ausreichend großen Memslot erstellen, der vom Hypervisor mit dem pvmfw-Image gefüllt werden kann. Der VMM ist auch in der Liste der Register eingeschränkt, deren Anfangswert er festlegen kann (x0–x14 für die primäre vCPU, keine für sekundäre vCPUs). Die restlichen Register sind reserviert und Teil der hypervisor-pvmfw-ABI.

Wenn die pVM ausgeführt wird, übergibt der Hypervisor zunächst die Kontrolle über die primäre vCPU an pvmfw. Die Firmware geht davon aus, dass crosvm einen AVB-signierten Kernel, der ein Bootloader oder ein anderes Image sein kann, und ein unsigniertes FDT an bekannten Offsets in den Speicher geladen hat. pvmfw validiert die AVB-Signatur und generiert bei Erfolg einen vertrauenswürdigen Gerätebaum aus dem empfangenen FDT, löscht seine Geheimnisse aus dem Speicher und verzweigt zum Einstiegspunkt der Nutzlast. Wenn einer der Überprüfungsschritte fehlschlägt, gibt die Firmware einen PSCI SYSTEM_RESET -Hyperaufruf aus.

Zwischen den Startvorgängen werden Informationen über die pVM-Instanz in einer Partition (virtio-blk-Gerät) gespeichert und mit dem Geheimnis von pvmfw verschlüsselt, um sicherzustellen, dass das Geheimnis nach einem Neustart der richtigen Instanz bereitgestellt wird.