Hypercall-Schnittstelle

Der Hypervisor bietet einen Anrufmechanismus für Gäste. Solche Anrufe werden als Hypercalls bezeichnet. Jeder Hypercall definiert einen Satz von Eingabe- und/oder Ausgabeparametern. Diese Parameter werden in Bezug auf eine speicherbasierte Datenstruktur angegeben. Alle Elemente der Eingabe- und Ausgabedatenstrukturen werden auf natürliche Grenzen bis zu 8 Bytes gepolstert (also zwei Byteelemente müssen sich auf zwei Bytegrenzen befinden und so weiter).

Eine zweite Hypercall-Anrufkonvention kann optional für eine Teilmenge von Hypercalls verwendet werden – insbesondere diejenigen, die zwei oder weniger Eingabeparameter haben und keine Ausgabeparameter haben. Bei Verwendung dieser Aufrufkonvention werden die Eingabeparameter in allgemeinen Registern übergeben.

Eine dritte Hypercall-Anrufkonvention kann optional für eine Teilmenge von Hypercalls verwendet werden, bei denen der Eingabeparameterblock bis zu 112 Bytes beträgt. Bei Verwendung dieser Aufrufkonvention werden die Eingabeparameter in Registern übergeben, einschließlich der veränderliche XMM-Register.

Eingabe- und Ausgabedatenstrukturen müssen sowohl im Arbeitsspeicher auf einer 8-Byte-Grenze platziert werden, als auch auf ein Vielfaches von 8 Bytes in Größe gepolstert werden. Die Werte in den Abstandsbereichen werden vom Hypervisor ignoriert.

Für die Ausgabe darf der Hypervisor (aber nicht garantiert) Abstandsbereiche überschreiben. Wenn er Abstandsbereiche überschreibt, schreibt er Nullen.

Hypercall-Klassen

Es gibt zwei Klassen von Hypercalls: einfache und rep (kurz für "repeat"). Ein einfacher Hypercall führt einen einzelnen Vorgang aus und verfügt über einen festen Satz von Eingabe- und Ausgabeparametern. Ein Rep-Hypercall fungiert wie eine Reihe einfacher Hypercalls. Zusätzlich zu einem festen Satz von Eingabe- und Ausgabeparametern umfassen Rep Hypercalls eine Liste von Eingabe- und/oder Ausgabeelementen mit fester Größe.

Wenn ein Anrufer zunächst einen Rep-Hypercall aufruft, gibt es eine Rep-Anzahl an, die die Anzahl der Elemente in der Eingabe- und/oder Ausgabeparameterliste angibt. Anrufer geben auch einen Rep-Startindex an, der das nächste Eingabe- und/oder Ausgabeelement angibt, das verbraucht werden sollte. Der Hypervisor verarbeitet Rep-Parameter in Listenreihenfolge – das heißt, indem der Elementindex erhöht wird.

Bei nachfolgenden Aufrufen des Rep-Hypercalls gibt der Rep-Startindex an, wie viele Elemente abgeschlossen wurden – und in Verbindung mit dem Rep Count-Wert – wie viele Elemente links sind. Wenn beispielsweise ein Anrufer eine Rep-Anzahl von 25 angibt und nur 20 Iterationen innerhalb der Zeiteinschränkungen abgeschlossen sind, gibt der Hypercall die Kontrolle zurück an den aufrufenden virtuellen Prozessor, nachdem der Rep-Startindex auf 20 aktualisiert wurde. Wenn der Hypercall erneut ausgeführt wird, wird der Hypervisor bei Element 20 fortgesetzt und die verbleibenden 5 Elemente abgeschlossen.

Wenn beim Verarbeiten eines Elements ein Fehler aufgetreten ist, wird ein geeigneter Statuscode zusammen mit einer abgeschlossenen Anzahl bereitgestellt, die die Anzahl der Elemente angibt, die erfolgreich verarbeitet wurden, bevor der Fehler aufgetreten ist. Wenn das angegebene Hypercall-Steuerelementwort gültig ist (siehe folgendes) und die Eingabe-/Ausgabeparameterlisten zugänglich sind, ist der Hypervisor garantiert, mindestens einen Rep zu versuchen, aber es ist nicht erforderlich, die gesamte Liste zu verarbeiten, bevor die Steuerung wieder an den Aufrufer zurückgegeben wird.

Hypercall-Fortsetzung

Ein Hypercall kann als komplexe Anweisung gedacht werden, die viele Zyklen dauert. Der Hypervisor versucht, die Hypercallausführung auf 50μs oder weniger zu beschränken, bevor die Steuerung an den virtuellen Prozessor zurückgegeben wird, der den Hypercall aufgerufen hat. Einige Hypercall-Vorgänge sind ausreichend komplex, dass eine Garantie von 50μs schwierig zu machen ist. Der Hypervisor basiert daher auf einem Hypercall-Fortsetzungsmechanismus für einige Hypercalls – einschließlich aller Hypercallformulare.

Der Hypercall-Fortsetzungsmechanismus ist meist transparent für den Anrufer. Wenn ein Hypercall innerhalb des vorgeschriebenen Zeitlimits nicht abgeschlossen werden kann, wird das Steuerelement wieder an den Anrufer zurückgegeben, aber der Anweisungszeiger wird nicht über die Anweisung erweitert, die den Hypercall aufgerufen hat. Dadurch können ausstehende Unterbrechungen behandelt werden und andere virtuelle Prozessoren geplant werden. Wenn der ursprüngliche Aufrufthread die Ausführung fortgesetzt, führt er die Hypercall-Anweisung erneut aus und führt den Fortschritt zum Abschluss des Vorgangs weiter aus.

Die meisten einfachen Hypercalls werden innerhalb der vorgeschriebenen Zeitgrenze garantiert abgeschlossen. Eine kleine Anzahl einfacher Hypercalls kann jedoch mehr Zeit erfordern. Diese Hypercalls verwenden hypercall-Fortsetzungen auf ähnliche Weise, um Hypercalls zu wiederholen. In solchen Fällen umfasst der Vorgang zwei oder mehrere interne Zustände. Der erste Aufruf platziert das Objekt (z. B. die Partition oder den virtuellen Prozessor) in einen Zustand, und nach wiederholten Aufrufen wechselt der Zustand schließlich zu einem Terminalzustand. Bei jedem Hypercall, der diesem Muster folgt, wird die sichtbaren Nebenwirkungen von zwischen internen Zuständen beschrieben.

Hypercall-Atomität und -Reihenfolge

Außer bei der Angabe ist die aktion, die von einem Hypercall ausgeführt wird, sowohl in Bezug auf alle anderen Gastvorgänge (z. B. Anweisungen, die innerhalb eines Gasts ausgeführt werden) als auch alle anderen Hypercalls, die auf dem System ausgeführt werden, atomisch. Ein einfacher Hypercall führt eine einzelne atome Aktion aus; Ein Rep hypercall führt mehrere, unabhängige atome Aktionen aus.

Einfache Hypercalls, die hypercall-Fortsetzung verwenden, können mehrere interne Zustände umfassen, die extern sichtbar sind. Solche Aufrufe umfassen mehrere atome Vorgänge.

Jede Hypercall-Aktion kann Eingabeparameter lesen und/oder Ergebnisse schreiben. Die Eingaben für jede Aktion können jederzeit nach abschluss des Hypercalls und vor der Ausführung der Aktion gelesen werden. Die Ergebnisse (das heißt die Ausgabeparameter), die jeder Aktion zugeordnet sind, können jederzeit und jederzeit geschrieben werden, nachdem die Aktion ausgeführt wird und bevor der Hypercall zurückgegeben wird.

Der Gast muss die Prüfung und/oder Manipulation von Eingabe- oder Ausgabeparametern im Zusammenhang mit einem ausführenden Hypercall vermeiden. Während ein virtueller Prozessor, der einen Hypercall ausführt, in der Lage ist, dies zu tun (da die Gastausführung angehalten wird, bis der Hypercall zurückgibt), gibt es nichts, um andere virtuelle Prozessoren daran zu hindern. Gäste, die auf diese Weise auftreten, können abstürzen oder Korruption in ihrer Partition verursachen.

Hypercalls können nur aus dem privilegierten Gastprozessormodus aufgerufen werden. Auf x64 Platfoms bedeutet dies den geschützten Modus mit einer aktuellen Berechtigungsstufe (CPL) von Null. Obwohl Der Realmoduscode mit einer effektiven CPL von Null ausgeführt wird, sind Hypercalls im realen Modus nicht zulässig. Ein Versuch, einen Hypercall innerhalb eines illegalen Prozessormodus aufzurufen, generiert eine #UD (nicht definierte Operation) Ausnahme.

Alle Hypercalls sollten über die architektonisch definierte Hypercall-Schnittstelle aufgerufen werden (siehe unten). Ein Versuch, einen Hypercall auf andere Weise aufzurufen (z. B. das Kopieren des Codes aus der Hypercall-Codeseite in einen alternativen Speicherort und die Ausführung von dort) kann zu einer nicht definierten Operation (#UD) führen. Der Hypervisor ist nicht garantiert, diese Ausnahme bereitzustellen.

Ausrichtungsanforderungen

Anrufer müssen die 64-Bit-Gast-physische Adresse (GPA) der Eingabe- und/oder Ausgabeparameter angeben. GPA-Zeiger müssen um 8-Byte ausgerichtet sein. Wenn der Hypercall keine Eingabe- oder Ausgabeparameter umfasst, ignoriert der Hypervisor den entsprechenden GPA-Zeiger.

Die Eingabe- und Ausgabeparameterlisten können nicht überlappen oder seitenübergreifende Grenzen überschreiten. Hypercall-Eingabe- und Ausgabeseiten werden als GPA-Seiten und nicht als "Überlagerungsseiten" erwartet. Wenn der virtuelle Prozessor die Eingabeparameter in eine Überlagerungsseite schreibt und einen GPA auf dieser Seite angibt, wird der Hypervisorzugriff auf die Eingabeparameterliste nicht definiert.

Der Hypervisor überprüft, ob die aufrufende Partition aus der Eingabeseite gelesen werden kann, bevor sie den angeforderten Hypercall ausführt. Diese Überprüfung besteht aus zwei Überprüfungen: Das angegebene GPA wird zugeordnet, und die GPA ist lesbar. Wenn eine dieser Tests fehlschlägt, generiert der Hypervisor eine Speicherfangennachricht. Für Hypercalls mit Ausgabeparametern überprüft der Hypervisor, dass die Partition auf die Ausgabeseite geschrieben werden kann. Diese Überprüfung besteht aus zwei Überprüfungen: Die angegebene GPA wird zugeordnet, und die GPA ist schreibbar.

Hypercall-Eingaben

Anrufer geben einen Hypercall durch einen 64-Bit-Wert namens Hypercall-Eingabewert an. Dabei wird das folgende Format verwendet:

Feld Bits Informationen bereitgestellt
Aufrufcode 15-0 Gibt an, welche Hypercall angefordert wird
Fast 16 Gibt an, ob der Hypercall die registerbasierte Anrufkonvention verwendet: 0 = speicherbasierte, 1 = registerbasierte
Variable Kopfzeilengröße 26-17 Die Größe einer variablen Kopfzeile in QWORDS.
RsvdZ 30-27 Muss null sein
Geschachtelt 31 Gibt an, dass der Hypercall vom L0-Hypervisor in einer geschachtelten Umgebung behandelt werden soll.
Rep Count 43-32 Die Gesamtzahl der Reps (für den Rep-Anruf muss andernfalls null sein)
RsvdZ 47-44 Muss null sein
Rep Startindex 59-48 Startindex (für Den Rep-Aufruf muss andernfalls null sein)
RsvdZ 63-60 Muss null sein

Bei Rep Hypercalls gibt das Rep-Anzahlsfeld die Gesamtanzahl der Reps an. Der Rep-Startindex gibt die bestimmte Wiederholung relativ zum Anfang der Liste an (Null gibt an, dass das erste Element in der Liste verarbeitet werden soll). Daher muss der Rep count-Wert immer größer sein als der Repstartindex.

Registrieren Sie die Zuordnung für Hypercall-Eingaben, wenn das Fast-Flag null ist:

x64 x86 Informationen bereitgestellt
RCX EDX:EAX Hypercall-Eingabewert
RDX EBX:ECX Eingabeparameter GPA
R8 EDI:ESI Ausgabeparameter GPA

Der Hypercall-Eingabewert wird zusammen mit einem GPA übergeben, der auf die Eingabe- und Ausgabeparameter verweist.

Bei x64 hängt die Registerzuordnung davon ab, ob der Anrufer im 32-Bit-Modus (x86) oder im 64-Bit-Modus (x64) ausgeführt wird. Der Hypervisor bestimmt den Anrufermodus basierend auf dem Wert von EFER. LMA und CS.L. Wenn beide Flags festgelegt sind, wird der Anrufer als 64-Bit-Anrufer angenommen.

Registrieren der Zuordnung für Hypercall-Eingaben, wenn das Fast-Flag eine ist:

x64 x86 Informationen bereitgestellt
RCX EDX:EAX Hypercall-Eingabewert
RDX EBX:ECX Eingabeparameter
R8 EDI:ESI Ausgabeparameter

Der Hypercall-Eingabewert wird zusammen mit den Eingabeparametern in Registern übergeben.

Variable Größe Hypercall-Eingabeheader

Die meisten Hypercall-Eingabeheader haben feste Größe. Die Menge an Kopfzeilendaten, die vom Gast an den Hypervisor übergeben werden, werden daher implizit vom Hypercall-Code angegeben und müssen nicht separat angegeben werden. Einige Hypercalls erfordern jedoch eine variable Menge an Kopfzeilendaten. Diese Hypercalls verfügen in der Regel über einen Eingabeheader mit fester Größe und zusätzlichen Headereingaben, die von variabler Größe sind.

Eine variable Kopfzeile ähnelt einer festen Hypercalleingabe (ausgerichtet auf 8 Bytes und größe auf mehrere von 8 Bytes). Der Anrufer muss angeben, wie viel Daten es als Eingabeheader bereitstellt. Diese Größe wird als Teil des Hypercall-Eingabewerts bereitgestellt (siehe "Variable Kopfzeilengröße" in tabelle oben).

Da die feste Kopfzeilengröße implizit ist, anstatt die Gesamtkopfgröße zu geben, wird nur der variable Teil in den Eingabesteuerelementen bereitgestellt:

Variable Header Bytes = {Total Header Bytes - sizeof(Fixed Header)} rounded up to nearest multiple of 8

Variable HeaderSize = Variable Header Bytes / 8

Es ist unzulässig, eine Nicht-Null-Variable-Headergröße für einen Hypercall anzugeben, der nicht explizit als variablen Eingabeheader dokumentiert ist. In diesem Fall führt der Hypercall zu einem Rückgabecode von HV_STATUS_INVALID_HYPERCALL_INPUT.

Es ist möglich, dass für einen bestimmten Aufruf eines Hypercalls, der variable Eingabeheader akzeptiert, die alle Kopfzeilen der Kopfzeile vollständig innerhalb der Kopfzeile mit fester Größe passen. In solchen Fällen ist der Eingabeheader der variablen Größe null, und die entsprechenden Bits in der Hypercall-Eingabe sollten auf Null festgelegt werden.

In allen anderen Bereichen sind Hypercalls, die variable Eingabeheader akzeptieren, andernfalls ähnlich wie feste Eingabeheader hypercalls in Bezug auf Aufrufkonventionen. Es ist auch möglich, dass eine variable Kopfzeile hypercall zusätzlich Rep-Semantik unterstützt. In diesem Fall liegen die Rep-Elemente nach der Kopfzeile in der üblichen Weise, außer dass die Gesamtgröße der Kopfzeile sowohl die festen als auch variablen Teile umfasst. Alle anderen Regeln bleiben identisch, z. B. das erste Rep-Element muss 8 Byte ausgerichtet sein.

XMM Schnelle Hypercall-Eingabe

Auf x64-Plattformen unterstützt der Hypervisor die Verwendung von XMM schnellen Hypercalls, wodurch einige Hypercalls die verbesserte Leistung der schnellen Hypercall-Schnittstelle nutzen können, obwohl sie mehr als zwei Eingabeparameter erfordern. Die XMM schnelle Hypercall-Schnittstelle verwendet sechs XMM-Register, damit der Anrufer einen Eingabeparameterblock bis zu 112 Bytes in Größe übergeben kann.

Die Verfügbarkeit der XMM fast hypercall-Schnittstelle wird über das CPUID-Blatt "Hypervisor feature identification" (0x40000003) angegeben:

  • Bit 4: Unterstützung für das Übergeben von Hypercalleingaben über XMM-Register ist verfügbar.

Beachten Sie, dass es ein separates Flag gibt, um die Unterstützung für die schnelle XMM-Ausgabe anzugeben. Jeder Versuch, diese Schnittstelle zu verwenden, wenn der Hypervisor keine Verfügbarkeit angibt, führt zu einem #UD Fehler.

Registrieren der Zuordnung (nur Eingabe)

x64 x86 Informationen bereitgestellt
RCX EDX:EAX Hypercall-Eingabewert
RDX EBX:ECX Eingabeparameterblock
R8 EDI:ESI Eingabeparameterblock
XMM0 XMM0 Eingabeparameterblock
XMM1 XMM1 Eingabeparameterblock
XMM2 XMM2 Eingabeparameterblock
XMM3 XMM3 Eingabeparameterblock
XMM4 XMM4 Eingabeparameterblock
XMM5 XMM5 Eingabeparameterblock

Der Hypercall-Eingabewert wird zusammen mit den Eingabeparametern in Registern übergeben. Die Registerzuordnungen hängen davon ab, ob der Anrufer im 32-Bit-Modus (x86) oder im 64-Bit-Modus (x64) ausgeführt wird. Der Hypervisor bestimmt den Anrufermodus basierend auf dem Wert von EFER. LMA und CS.L. Wenn beide Flags festgelegt sind, wird der Anrufer als 64-Bit-Anrufer angenommen. Wenn der Eingabeparameterblock kleiner als 112 Bytes ist, werden alle zusätzlichen Bytes in den Registern ignoriert.

Hypercall-Ausgabe

Alle Hypercalls geben einen 64-Bit-Wert zurück, der als Hypercall-Ergebniswert bezeichnet wird. Dabei wird das folgende Format verwendet:

Feld Bits Kommentar
Ergebnis 15-0 HV_STATUS Code, der den Erfolg oder Fehler angibt
Rsvd 31-16 Anrufer sollten den Wert in diesen Bits ignorieren.
Abgeschlossene Reps 43-32 Anzahl der erfolgreich abgeschlossenen Reps
RsvdZ 63-40 Anrufer sollten den Wert in diesen Bits ignorieren.

Bei Rep Hypercalls ist das vollständige Feld der Reps die Gesamtanzahl der Reps abgeschlossen und nicht relativ zum Rep-Startindex. Wenn der Anrufer beispielsweise einen Rep-Startindex von 5 und eine Rep-Anzahl von 10 angegeben hat, würde das vollständige Reps-Feld 10 nach erfolgreicher Fertigstellung angeben.

Der Hypercall-Ergebniswert wird wieder in Registern übergeben. Die Registerzuordnung hängt davon ab, ob der Anrufer im 32-Bit-Modus (x86) oder im 64-Bit-Modus (x64) ausgeführt wird (siehe oben). Die Registerzuordnung für Hypercall-Ausgabe ist wie folgt:

x64 x86 Informationen bereitgestellt
RAX EDX:EAX Hypercall-Ergebniswert

XMM Schnelle Hypercall-Ausgabe

Ähnlich wie der Hypervisor XMM schnelle Hypercalleingaben unterstützt, können dieselben Register freigegeben werden, um die Ausgabe zurückzugeben. Dies wird nur auf x64-Plattformen unterstützt.

Die Möglichkeit, die Ausgabe über XMM-Register zurückzugeben, wird über das CPUID-Blatt (0x40000003) angegeben:

  • Bit 15: Unterstützung für die Rückgabe von Hypercall-Ausgabe über XMM-Register ist verfügbar.

Beachten Sie, dass es ein separates Flag gibt, um die Unterstützung für die schnelle Eingabe von XMM anzugeben. Jeder Versuch, diese Schnittstelle zu verwenden, wenn der Hypervisor keine Verfügbarkeit angibt, führt zu einem #UD Fehler.

Registrieren der Zuordnung (Eingabe und Ausgabe)

Register, die nicht zum Übergeben von Eingabeparametern verwendet werden, können verwendet werden, um die Ausgabe zurückzugeben. Mit anderen Worten, wenn der Eingabeparameterblock kleiner als 112 Bytes ist (auf den nächsten 16 byte ausgerichteten Teil gerundet), gibt die restlichen Register hypercall-Ausgabe zurück.

x64 Informationen bereitgestellt
RDX Eingabe- oder Ausgabeblock
R8 Eingabe- oder Ausgabeblock
XMM0 Eingabe- oder Ausgabeblock
XMM1 Eingabe- oder Ausgabeblock
XMM2 Eingabe- oder Ausgabeblock
XMM3 Eingabe- oder Ausgabeblock
XMM4 Eingabe- oder Ausgabeblock
XMM5 Eingabe- oder Ausgabeblock

Wenn der Eingabeparameterblock beispielsweise 20 Bytes groß ist, würde der Hypervisor die folgenden 12 Bytes ignorieren. Die restlichen 80 Bytes würden hypercall-Ausgabe (falls zutreffend) enthalten.

Veränderliche Register

Hypercalls ändern nur die angegebenen Registerwerte unter den folgenden Bedingungen:

  1. RAX (x64) und EDX:EAX (x86) werden immer mit dem Hypercall-Ergebniswert und den Ausgabeparametern überschrieben.
  2. Rep Hypercalls ändern RCX (x64) und EDX:EAX (x86) mit dem neuen Rep-Startindex.
  3. HvCallSetVpRegisters können alle Register ändern, die mit diesem Hypercall unterstützt werden.
  4. RDX, R8 und XMM0 bis XMM5, wenn für schnelle Hypercalleingaben verwendet, bleiben unverändert. Register, die für schnelle Hypercall-Ausgabe verwendet werden, können jedoch geändert werden, einschließlich RDX, R8 und XMM0 bis XMM5. Hyper-V ändert diese Register nur für schnelle Hypercall-Ausgabe, die auf x64 beschränkt ist.

Hypercall-Einschränkungen

Hypercalls können Einschränkungen aufweisen, die ihnen zugeordnet sind, um ihre beabsichtigte Funktion auszuführen. Wenn alle Einschränkungen nicht erfüllt sind, wird der Hypercall mit einem entsprechenden Fehler beendet. Die folgenden Einschränkungen werden aufgeführt, falls zutreffend:

  • Die aufrufende Partition muss über ein bestimmtes Recht verfügen.
  • Die zu bearbeitende Partition muss sich in einem bestimmten Zustand (z. B. "Aktiv") befinden.

Hypercall-Statuscodes

Jeder Hypercall wird als Rückgabe eines Ausgabewerts dokumentiert, der mehrere Felder enthält. Ein Statuswertfeld (vom Typ HV_STATUS) wird verwendet, um anzugeben, ob der Aufruf erfolgreich war oder fehlgeschlagen ist.

Ausgabeparameter-Gültigkeit bei fehlgeschlagenen Hypercalls

Sofern nicht explizit angegeben, wenn ein Hypercall fehlschlägt (das heißt, das Ergebnisfeld des Hypercall-Ergebniswerts enthält einen anderen Wert als HV_STATUS_SUCCESS), sind die Inhalte aller Ausgabeparameter unbestimmt und sollten nicht vom Aufrufer untersucht werden. Nur wenn der Hypercall erfolgreich ist, enthalten alle entsprechenden Ausgabeparameter gültige, erwartete Ergebnisse.

Reihenfolge der Fehlerbedingungen

Die Reihenfolge, in der Fehlerbedingungen erkannt und vom Hypervisor gemeldet werden, ist nicht definiert. Mit anderen Worten, wenn mehrere Fehler vorhanden sind, muss der Hypervisor auswählen, welche Fehlerbedingung gemeldet werden soll. Die Priorität sollte diesen Fehlercodes gegeben werden, die größere Sicherheit bieten, die Absicht, den Hypervisor daran zu hindern, Informationen zu entblenden, um Anrufer ohne ausreichende Berechtigungen zu entblenden. Der Statuscode ist z. B. der bevorzugte Statuscode HV_STATUS_ACCESS_DENIED für einen Code, der kontext- oder zustandsbezogene Informationen rein auf Der Grundlage von Berechtigungen anzeigt.

Allgemeine Hypercall-Statuscodes

Mehrere Ergebniscodes sind für alle Hypercalls üblich und werden daher für jeden Hypercall nicht einzeln dokumentiert. Dabei handelt es sich z. B. um:

Statuscode Fehlerzustand
HV_STATUS_SUCCESS Der Aufruf war erfolgreich.
HV_STATUS_INVALID_HYPERCALL_CODE Der Hypercall-Code wird nicht erkannt.
HV_STATUS_INVALID_HYPERCALL_INPUT Die Rep-Anzahl ist falsch (z. B. wird eine Nicht-Null-Rep-Anzahl an einen Nicht-Rep-Aufruf übergeben oder eine Null-Rep-Anzahl an einen Rep-Aufruf übergeben).
Der Rep-Startindex ist nicht kleiner als die Rep-Anzahl.
Ein reserviertes Bit im angegebenen Hypercall-Eingabewert ist nicht null.
HV_STATUS_INVALID_ALIGNMENT Der angegebene Eingabe- oder Ausgabe-GPA-Zeiger wird nicht auf 8 Bytes ausgerichtet.
Die angegebenen Eingabe- oder Ausgabeparameterlisten umfassen Seiten.
Der Eingabe- oder Ausgabe-GPA-Zeiger befindet sich nicht innerhalb der Grenzen des GPA-Raums.

Der Rückgabecode HV_STATUS_SUCCESS gibt an, dass keine Fehlerbedingung erkannt wurde.

Melden der Gastbetriebssystemidentität

Das Gastbetriebssystem, das innerhalb der Partition ausgeführt wird, muss sich mit dem Hypervisor identifizieren, indem er seine Signatur und Version in ein MSRHV_X64_MSR_GUEST_OS_ID () schreibt, bevor es Hypercalls aufrufen kann. Dieser MSR ist partitionsweit und wird für alle virtuellen Prozessoren freigegeben.

Der Wert dieses Registers ist zunächst null. Ein Nicht-Null-Wert muss in die Gastbetriebs-ID MSR geschrieben werden, bevor die Hypercall-Codeseite aktiviert werden kann (siehe Einrichten der Hypercall-Schnittstelle). Wenn dieses Register anschließend null ist, wird die Hypercall-Codeseite deaktiviert.

#define HV_X64_MSR_GUEST_OS_ID 0x40000000

Gastbetriebssystemidentität für proprietäre Betriebssysteme

Im Folgenden wird die empfohlene Codierung für diesen MSR empfohlen. Einige Felder gelten möglicherweise nicht für einige Gast-OSs.

Bits Feld BESCHREIBUNG
15:0 Buildnummer Gibt die Buildnummer des Betriebssystems an.
23:16 Dienstversion Gibt die Dienstversion an (z. B. "Service Pack"-Nummer)
31:24 Nebenversion Gibt die Nebenversion des Betriebssystems an
39:32 Hauptversion Gibt die Hauptversion des Betriebssystems an
47:40 Betriebssystem-ID Gibt die Betriebssystemvariante an. Die Codierung ist für den Anbieter eindeutig. Microsoft-Betriebssysteme werden wie folgt codiert: 0=Undefined, 1=MS-DOS®, 2 ®=Windows 3.x, 3=Windows ® 9x, 4=Windows NT (und Derivate), 5=Windows ® ® CE
62:48 Herstellerkennung Gibt den Gastbetriebssystemanbieter an. Ein Wert von 0 ist reserviert. Siehe Liste der nachstehenden Anbieter.
63 Betriebssystemtyp Gibt die Betriebssystemtypen an. Ein Wert von 0 gibt ein proprietäres, geschlossenes Quellbetriebssystem an. Ein Wert von 1 gibt ein Open Source Betriebssystem an.

Anbieterwerte werden von Microsoft zugewiesen. Um einen neuen Anbieter anzufordern, geben Sie bitte ein Problem im Repository für die Virtualisierungsdokumentation GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS) ein.

Hersteller Wert
Microsoft 0x0001
HPE 0x0002
LANCOM 0x0200

Gastbetriebssystemidentitäts-MSR für Open Source-Betriebssysteme

Die folgende Codierung wird als Anleitung für Open Source Betriebssystemanbieter angeboten, die diese Spezifikation erfüllen möchten. Es wird vorgeschlagen, dass Open Source Betriebssysteme die folgende Konvention anpassen.

Bits Feld BESCHREIBUNG
15:0 Buildnummer Zusätzliche Informationen
47:16 Version Upstream-Kernelversionsinformationen.
55:48 Betriebssystem-ID Weitere Anbieterinformationen
62:56 Betriebssystemtyp Betriebssystemtyp (z. B. Linux, FreeBSD usw.). Siehe Liste der bekannten Betriebssystemtypen unten
63 Open Source Ein Wert von 1 gibt ein Open Source Betriebssystem an.

Betriebssystemtypwerte werden von Microsoft zugewiesen. Um einen neuen Betriebssystemtyp anzufordern, geben Sie bitte ein Problem im Repository für die Virtualisierungsdokumentation GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS) ein.

Betriebssystemtyp Wert
Linux 0x1
FreeBSD 0x2
Xen 0x3
Illumos 0x4

Einrichten der Hypercall-Schnittstelle

Hypercalls werden mithilfe eines speziellen Opcodes aufgerufen. Da sich dieser Opcode zwischen Virtualisierungsimplementierungen unterscheidet, ist es notwendig, dass der Hypervisor diesen Unterschied abstrahiert. Dies erfolgt über eine spezielle Hypercallseite. Diese Seite wird vom Hypervisor bereitgestellt und wird im GPA-Bereich des Gasts angezeigt. Der Gast ist erforderlich, um den Speicherort der Seite anzugeben, indem sie den Gast Hypercall MSR programmieren.

#define HV_X64_MSR_HYPERCALL 0x40000001
Bits Beschreibung Attribute
63:12 Hypercall GPFN – Gibt die Anzahl der Gast-physischen Seiten der Hypercall-Seite an Lesen/Schreiben
11:2 RsvdP. Bits sollten auf Lesevorgängen ignoriert und auf Schreibvorgängen beibehalten werden. Reserviert
1 Gesperrt. Gibt an, ob die MSR unveränderlich ist. Wenn festgelegt, wird dieser MSR gesperrt, wodurch die Verlagerung der Hypercall-Seite verhindert wird. Einmal festgelegt, kann nur ein Systemrücksetzung den Bit löschen. Lesen/Schreiben
0 Hypercall-Seite aktivieren Lesen/Schreiben

Die Hypercall-Seite kann überall im GPA-Bereich des Gasts platziert werden, muss jedoch seitenbündig sein. Wenn der Gast versucht, die Hypercallseite über die Grenzen des GPA-Leerzeichens hinaus zu verschieben, führt ein #GP Fehler, wenn der MSR geschrieben wird.

Dieser MSR ist ein partitionsweites MSR. Mit anderen Worten, sie wird von allen virtuellen Prozessoren in der Partition freigegeben. Wenn ein virtueller Prozessor erfolgreich in den MSR schreibt, liest ein anderer virtueller Prozessor denselben Wert.

Bevor die Hypercallseite aktiviert ist, muss das Gastbetriebssystem seine Identität melden, indem er die Versionssignatur in einen separaten MSR (HV_X64_MSR_GUEST_OS_ID) schreibt. Wenn keine Gastbetriebssystemidentität angegeben wurde, schlägt die Aktivierung des Hypercalls fehl. Das aktivierende Bit bleibt auch dann null, wenn eine in sie geschrieben wird. Wenn die Gastbetriebssystemidentität nach der Aktivierung der Hypercall-Seite auf Null gelöscht wird, wird sie deaktiviert.

Die Hypercall-Seite wird als "Überlagerung" im GPA-Bereich angezeigt; d. h. es deckt alles andere ab, was dem GPA-Bereich zugeordnet ist. Der Inhalt ist von dem Gast lesbar und ausführbarer. Versuche, in die Hypercall-Seite zu schreiben, führt zu einer Schutz-Ausnahme (#GP). Nachdem die Hypercall-Seite aktiviert wurde, umfasst das Aufrufen eines Hypercalls einfach einen Aufruf zum Anfang der Seite.

Im Folgenden finden Sie eine detaillierte Liste der schritte, die bei der Einrichtung der Hypercall-Seite beteiligt sind:

  1. Der Gast liest CPUID-Blatt 1 und bestimmt, ob ein Hypervisor vorhanden ist, indem Bit 31 des Registers ECX überprüft wird.
  2. Der Gast liest das CPUID-Blatt 0x40000000, um das maximale Hypervisor-CPUID-Blatt (zurückgegeben in register EAX) und CPUID-Blatt 0x40000001 zu ermitteln, um die Schnittstellensignatur zu bestimmen (zurückgegeben in register EAX). Es überprüft, dass der maximale Blattwert mindestens 0x40000005 ist und dass die Schnittstellensignatur "Hv#1" entspricht. Diese Signatur bedeutet, dass HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL und HV_X64_MSR_VP_INDEX implementiert werden.
  3. Der Gast schreibt seine Betriebssystemidentität in den MSR HV_X64_MSR_GUEST_OS_ID , wenn dieses Register null ist.
  4. Der Gast liest den Hypercall MSR (HV_X64_MSR_HYPERCALL).
  5. Der Gast überprüft den Bit "Hypercall Page aktivieren". Wenn die Schnittstelle festgelegt ist, ist die Schnittstelle bereits aktiv, und die Schritte 6 und 7 sollten nicht angegeben werden.
  6. Der Gast findet eine Seite im GPA-Raum, vorzugsweise eines, das nicht von RAM, MMIO und so weiter besetzt ist. Wenn die Seite besetzt ist, sollte der Gast die zugrunde liegende Seite für andere Zwecke vermeiden.
  7. Der Gast schreibt einen neuen Wert in den Hypercall MSR (HV_X64_MSR_HYPERCALL) mit dem GPA aus Schritt 6 und legt den Bit "Hypercall Page aktivieren" fest, um die Schnittstelle zu aktivieren.
  8. Der Gast erstellt eine ausführbare VA-Zuordnung zu der Hypercall-Seite GPA.
  9. Der Gast berät CPUID-Blatt 0x40000003, um zu ermitteln, welche Hypervisor-Einrichtungen verfügbar sind. Nachdem die Schnittstelle eingerichtet wurde, kann der Gast einen Hypercall initiieren. Dazu füllt er die Register pro Hypercall-Protokoll auf und stellt einen ANRUF am Anfang der Hypercallseite aus. Der Gast sollte davon ausgehen, dass die Hypercall-Seite das Äquivalent einer Näherung (0xC3) ausführt, um zum Anrufer zurückzukehren. So muss der Hypercall mit einem gültigen Stapel aufgerufen werden.

Erweiterte Hypercall-Schnittstelle

Hypercalls mit Anrufcodes über 0x8000 werden als erweiterte Hypercalls bezeichnet. Erweiterte Hypercalls verwenden die gleiche Anrufkonvention wie normale Hypercalls und werden von einer Gast-VM identisch angezeigt. Erweiterte Hypercalls werden intern innerhalb des Hyper-V-Hypervisors unterschiedlich behandelt.

Erweiterte Hypercallfunktionen können mit HvExtCallQueryCapabilities abgefragt werden.