Partager via


Hypercall Interface

L’hyperviseur fournit un mécanisme d’appel pour les invités. Ces appels sont appelés hypercalls. Chaque hypercall définit un ensemble de paramètres d’entrée et/ou de sortie. Ces paramètres sont spécifiés en termes de structure de données basée sur la mémoire. Tous les éléments des structures de données d’entrée et de sortie sont rembourrés aux limites naturelles jusqu’à 8 octets (autrement dit, les éléments à deux octets doivent se trouver sur des limites à deux octets, etc.).

Une deuxième convention d’appel hypercall peut éventuellement être utilisée pour un sous-ensemble d’hypercalls , en particulier ceux qui ont deux paramètres d’entrée ou moins et aucun paramètre de sortie. Lors de l’utilisation de cette convention d’appel, les paramètres d’entrée sont transmis dans des registres à usage général.

Une troisième convention d’appel hypercall peut éventuellement être utilisée pour un sous-ensemble d’hypercalls où le bloc de paramètres d’entrée est jusqu’à 112 octets. Lors de l’utilisation de cette convention d’appel, les paramètres d’entrée sont passés dans les registres, y compris les registres XMM volatiles.

Les structures de données d’entrée et de sortie doivent être placées en mémoire sur une limite de 8 octets et complétées à un multiple de 8 octets de taille. Les valeurs dans les régions de remplissage sont ignorées par l’hyperviseur.

Pour la sortie, l’hyperviseur est autorisé à remplacer (mais pas garanti) les régions de remplissage. S’il remplace les régions de remplissage, il écrit des zéros.

Hypercall Classes

Il existe deux classes d’hypercalls : simple et rep (court pour « répéter »). Un hypercall simple effectue une opération unique et a un ensemble de paramètres d’entrée et de sortie de taille fixe. Un hypercall de rep agit comme une série d’hypercalls simples. En plus d’un ensemble de paramètres d’entrée et de sortie de taille fixe, les hypercalls de rep impliquent une liste d’éléments d’entrée et/ou de sortie de taille fixe.

Lorsqu’un appelant appelle initialement un hypercall de rep, il spécifie un nombre de rep qui indique le nombre d’éléments dans la liste des paramètres d’entrée et/ou de sortie. Les appelants spécifient également un index de début de rep qui indique l’élément d’entrée et/ou de sortie suivant qui doit être consommé. L’hyperviseur traite les paramètres de rep dans l’ordre de liste, c’est-à-dire en augmentant l’index d’élément.

Pour les appels suivants de l’hypercall de rep, l’index de début du rep indique le nombre d’éléments terminés et, conjointement avec la valeur du nombre de rep, le nombre d’éléments restants. Par exemple, si un appelant spécifie un nombre de rep de 25 et que seulement 20 itérations sont effectuées dans les contraintes de temps, l’hypercall retourne le contrôle au processeur virtuel appelant après la mise à jour de l’index de début de rep à 20. Lorsque l’hypercall est réexécuté, l’hyperviseur reprend à l’élément 20 et termine les 5 éléments restants.

Si une erreur est rencontrée lors du traitement d’un élément, un code d’état approprié est fourni avec un nombre de représentants terminés, indiquant le nombre d’éléments qui ont été traités avec succès avant la rencontre de l’erreur. En supposant que le mot de contrôle hypercall spécifié est valide (voir la section suivante) et que les listes de paramètres d’entrée/sortie sont accessibles, l’hyperviseur est garanti pour tenter au moins un représentant, mais il n’est pas nécessaire de traiter la liste entière avant de retourner le contrôle à l’appelant.

Hypercall Continuation

Un hypercall peut être considéré comme une instruction complexe qui prend de nombreux cycles. L’hyperviseur tente de limiter l’exécution de l’hypercall à 50μs ou moins avant de retourner le contrôle au processeur virtuel qui a appelé l’hypercall. Certaines opérations hypercall sont suffisamment complexes qu’une garantie de 50μs est difficile à réaliser. L’hyperviseur s’appuie donc sur un mécanisme de continuation d’hypercall pour certains hypercalls, y compris tous les formulaires d’hypercall de rep.

Le mécanisme de continuation hypercall est principalement transparent pour l’appelant. Si un hypercall n’est pas en mesure de se terminer dans la limite de temps prescrite, le contrôle est retourné à l’appelant, mais le pointeur d’instruction n’est pas avancé après l’instruction qui a appelé l’hypercall. Cela permet de gérer les interruptions en attente et d’autres processeurs virtuels à planifier. Lorsque le thread appelant d’origine reprend l’exécution, il réexécute l’instruction hypercall et avance vers la fin de l’opération.

La plupart des hypercalls simples sont garantis à se terminer dans le délai prescrit. Toutefois, un petit nombre d’hypercalls simples peut nécessiter plus de temps. Ces hypercalls utilisent la continuation hypercall de la même manière que les hypercalls. Dans ce cas, l’opération implique au moins deux états internes. Le premier appel place l’objet (par exemple, la partition ou le processeur virtuel) dans un état, et après des appels répétés, l’état passe enfin à un état terminal. Pour chaque hypercall qui suit ce modèle, les effets secondaires visibles des états internes intermédiaires sont décrits.

Atomicité et classement Hypercall

Sauf indication contraire, l’action effectuée par un hypercall est atomique à la fois par rapport à toutes les autres opérations invitées (par exemple, les instructions exécutées au sein d’un invité) et tous les autres hypercalls exécutés sur le système. Un hypercall simple effectue une action atomique unique ; un hypercall de rep effectue plusieurs actions atomiques indépendantes.

Les hypercalls simples qui utilisent la continuation hypercall peuvent impliquer plusieurs états internes visibles en externe. Ces appels comprennent plusieurs opérations atomiques.

Chaque action hypercall peut lire les paramètres d’entrée et/ou écrire des résultats. Les entrées de chaque action peuvent être lues à n’importe quelle granularité et à tout moment après l’exécution de l’hypercall et avant l’exécution de l’action. Les résultats (autrement dit, les paramètres de sortie) associés à chaque action peuvent être écrits à n’importe quelle granularité et à tout moment après l’exécution de l’action et avant le retour de l’hypercall.

L’invité doit éviter l’examen et/ou la manipulation de tous les paramètres d’entrée ou de sortie liés à un hypercall en cours d’exécution. Bien qu’un processeur virtuel exécutant un hypercall ne soit pas en mesure de le faire (comme son exécution invitée est suspendue jusqu’à ce que l’hypercall retourne), il n’y a rien pour empêcher d’autres processeurs virtuels de le faire. Les invités qui se comportent de cette façon peuvent se bloquer ou provoquer une altération au sein de leur partition.

Les hypercalls ne peuvent être appelés qu’à partir du mode processeur invité le plus privilégié. Sur les platfoms x64, cela signifie le mode protégé avec un niveau de privilège actuel (CPL) égal à zéro. Bien que le code en mode réel s’exécute avec une CPL effective de zéro, les hypercalls ne sont pas autorisés en mode réel. Une tentative d’appel d’un hypercall dans un mode processeur illégal génère une exception #UD (opération non définie).

Tous les hypercalls doivent être appelés via l’interface hypercall définie de manière architecturale (voir ci-dessous). Une tentative d’appel d’un hypercall par tout autre moyen (par exemple, la copie du code de la page de codes hypercall vers un autre emplacement et son exécution à partir de là) peut entraîner une exception d’opération non définie (#UD). L’hyperviseur n’est pas garanti pour fournir cette exception.

Configuration requise pour l’alignement

Les appelants doivent spécifier l’adresse physique invité 64 bits (GPA) des paramètres d’entrée et/ou de sortie. Les pointeurs GPA doivent être alignés sur 8 octets. Si l’hypercall n’implique aucun paramètre d’entrée ou de sortie, l’hyperviseur ignore le pointeur GPA correspondant.

Les listes de paramètres d’entrée et de sortie ne peuvent pas chevaucher ou franchir les limites de page. Les pages d’entrée et de sortie Hypercall sont censées être des pages GPA et non des pages de superposition. Si le processeur virtuel écrit les paramètres d’entrée dans une page de superposition et spécifie un GPA dans cette page, l’accès hyperviseur à la liste des paramètres d’entrée n’est pas défini.

L’hyperviseur vérifie que la partition appelante peut lire à partir de la page d’entrée avant d’exécuter l’hypercall demandé. Cette validation se compose de deux vérifications : l’application GPA spécifiée est mappée et l’objet GPA est marqué comme lisible. Si l’un de ces tests échoue, l’hyperviseur génère un message d’interception de mémoire. Pour les hypercalls qui ont des paramètres de sortie, l’hyperviseur vérifie que la partition peut être écriture dans la page de sortie. Cette validation se compose de deux vérifications : l’application GPA spécifiée est mappée et l’objet GPA est marqué en écriture.

Entrées Hypercall

Les appelants spécifient un hypercall par une valeur 64 bits appelée valeur d’entrée hypercall. Il est mis en forme comme suit :

Champ Bits Informations fournies
Code d’appel 15-0 Spécifie l’hypercall demandé
Rapide 16 Spécifie si l’hypercall utilise la convention d’appel basée sur le registre : 0 = mémoire, 1 = registre
Taille d’en-tête variable 26-17 Taille d’un en-tête de variable, en QWORDS.
RsvdZ 30-27 Doit être égal à zéro
Est imbriqué 31 Spécifie que l’hypercall doit être géré par l’hyperviseur L0 dans un environnement imbriqué.
Nombre de reps 43-32 Nombre total de reps (pour l’appel de rep, doit être égal à zéro dans le cas contraire)
RsvdZ 47-44 Doit être égal à zéro
Index de début de rep 59-48 Index de départ (pour l’appel rep, doit être égal à zéro dans le cas contraire)
RsvdZ 63-60 Doit être égal à zéro

Pour les hypercalls de rep, le champ rep count indique le nombre total de reps. L’index de début du rep indique la répétition particulière par rapport au début de la liste (zéro indique que le premier élément de la liste doit être traité). Par conséquent, la valeur du nombre de rep doit toujours être supérieure à l’index de début du rep.

Inscrivez le mappage pour les entrées hypercall lorsque l’indicateur Fast est égal à zéro :

x64 x86 Informations fournies
RCX EDX:EAX Valeur d’entrée Hypercall
RDX EBX:ECX GPA des paramètres d’entrée
R8 EDI:ESI GPA des paramètres de sortie

La valeur d’entrée hypercall est passée dans les registres avec un GPA qui pointe vers les paramètres d’entrée et de sortie.

Sur x64, les mappages d’inscription dépendent du mode 32 bits (x86) ou 64 bits (x64). L’hyperviseur détermine le mode de l’appelant en fonction de la valeur d’EFER. LMA et CS.L. Si ces deux indicateurs sont définis, l’appelant est supposé être un appelant 64 bits.

Inscrivez le mappage pour les entrées hypercall lorsque l’indicateur Fast est un :

x64 x86 Informations fournies
RCX EDX:EAX Valeur d’entrée Hypercall
RDX EBX:ECX Paramètre d’entrée
R8 EDI:ESI Paramètre de sortie

La valeur d’entrée hypercall est passée dans les registres ainsi que les paramètres d’entrée.

En-têtes d’entrée Hypercall de taille variable

La plupart des en-têtes d’entrée hypercall ont une taille fixe. La quantité de données d’en-tête transmises de l’invité à l’hyperviseur est donc implicitement spécifiée par le code d’hypercall et n’a pas besoin d’être spécifiée séparément. Toutefois, certains hypercalls nécessitent une quantité variable de données d’en-tête. Ces hypercalls ont généralement un en-tête d’entrée de taille fixe et une entrée d’en-tête supplémentaire de taille variable.

Un en-tête de taille variable est similaire à une entrée hypercall fixe (alignée sur 8 octets et dimensionnée à un multiple de 8 octets). L’appelant doit spécifier la quantité de données qu’il fournit en tant qu’en-têtes d’entrée. Cette taille est fournie dans le cadre de la valeur d’entrée hypercall (voir « Taille d’en-tête variable » dans le tableau ci-dessus).

Étant donné que la taille d’en-tête fixe est implicite, au lieu de fournir la taille totale de l’en-tête, seule la partie variable est fournie dans les contrôles d’entrée :

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

Variable HeaderSize = Variable Header Bytes / 8

Il est illégal de spécifier une taille d’en-tête variable non nulle pour un hypercall qui n’est pas explicitement documenté comme acceptant des en-têtes d’entrée de taille variable. Dans ce cas, l’hypercall entraîne un code de retour de HV_STATUS_INVALID_HYPERCALL_INPUT.

Il est possible que pour un appel donné d’un hypercall qui accepte les en-têtes d’entrée de taille variable que toutes les entrées d’en-tête d’en-tête correspondent entièrement à l’en-tête de taille fixe. Dans ce cas, l’en-tête d’entrée de taille variable est de taille zéro et les bits correspondants dans l’entrée hypercall doivent être définis sur zéro.

En tout cas, les hypercalls acceptant des en-têtes d’entrée de taille variable sont autrement similaires aux hypercalls d’en-tête d’entrée de taille fixe en ce qui concerne les conventions d’appel. Il est également possible pour un hypercall d’en-tête de taille variable de prendre en charge la sémantique rep. Dans ce cas, les éléments rep se trouvent après l’en-tête de la manière habituelle, sauf que la taille totale de l’en-tête inclut à la fois les parties fixes et variables. Toutes les autres règles restent identiques, par exemple, le premier élément rep doit être aligné sur 8 octets.

Entrée Hypercall rapide XMM

Sur les plateformes x64, l’hyperviseur prend en charge l’utilisation d’hypercalls rapides XMM, ce qui permet à certains hypercalls de tirer parti des performances améliorées de l’interface d’hypercall rapide, même s’ils nécessitent plus de deux paramètres d’entrée. L’interface hypercall rapide XMM utilise six registres XMM pour permettre à l’appelant de passer un bloc de paramètres d’entrée jusqu’à 112 octets de taille.

La disponibilité de l’interface d’hypercall rapide XMM est indiquée par le biais de la feuille CPUID cpuID (0x40000003) « Hypervisor Feature Identification » (0x40000003) :

  • Bit 4 : la prise en charge de la transmission d’une entrée hypercall via des registres XMM est disponible.

Notez qu’il existe un indicateur distinct pour indiquer la prise en charge de la sortie rapide XMM. Toute tentative d’utilisation de cette interface lorsque l’hyperviseur n’indique pas la disponibilité entraîne une erreur #UD.

Inscrire le mappage (entrée uniquement)

x64 x86 Informations fournies
RCX EDX:EAX Valeur d’entrée Hypercall
RDX EBX:ECX Bloc de paramètres d’entrée
R8 EDI:ESI Bloc de paramètres d’entrée
XMM0 XMM0 Bloc de paramètres d’entrée
XMM1 XMM1 Bloc de paramètres d’entrée
XMM2 XMM2 Bloc de paramètres d’entrée
XMM3 XMM3 Bloc de paramètres d’entrée
XMM4 XMM4 Bloc de paramètres d’entrée
XMM5 XMM5 Bloc de paramètres d’entrée

La valeur d’entrée hypercall est passée dans les registres ainsi que les paramètres d’entrée. Les mappages d’inscription dépendent du mode 32 bits (x86) ou 64 bits (x64). L’hyperviseur détermine le mode de l’appelant en fonction de la valeur d’EFER. LMA et CS.L. Si ces deux indicateurs sont définis, l’appelant est supposé être un appelant 64 bits. Si le bloc de paramètres d’entrée est inférieur à 112 octets, tous les octets supplémentaires dans les registres sont ignorés.

Sorties Hypercall

Tous les hypercalls retournent une valeur 64 bits appelée valeur de résultat hypercall. Il est mis en forme comme suit :

Champ Bits Commentaire
Résultat 15-0 HV_STATUS code indiquant la réussite ou l’échec
Rsvd 31-16 Les appelants doivent ignorer la valeur dans ces bits
Reps terminé 43-32 Nombre de représentants correctement terminés
RsvdZ 63-40 Les appelants doivent ignorer la valeur dans ces bits

Pour les hypercalls de rep, le champ complet des reps est le nombre total de reps terminé et non relatif à l’index de début du rep. Par exemple, si l’appelant a spécifié un index de début de rep de 5 et qu’un nombre de rep de 10, le champ complet des reps indique 10 à la fin réussie.

La valeur de résultat hypercall est renvoyée dans les registres. Le mappage d’inscription dépend de l’exécution de l’appelant en mode 32 bits (x86) ou 64 bits (x64) (voir ci-dessus). Le mappage d’inscription pour les sorties hypercall est le suivant :

x64 x86 Informations fournies
RAX EDX:EAX Valeur de résultat Hypercall

Sortie Hypercall rapide XMM

Similaire à la façon dont l’hyperviseur prend en charge les entrées d’hypercall rapide XMM, les mêmes registres peuvent être partagés pour retourner la sortie. Cela n’est pris en charge que sur les plateformes x64.

La possibilité de retourner la sortie via les registres XMM est indiquée via la feuille CPUID « Identification des fonctionnalités d’hyperviseur » (0x40000003) :

  • Bit 15 : la prise en charge du retour de la sortie hypercall via les registres XMM est disponible.

Notez qu’il existe un indicateur distinct pour indiquer la prise en charge de l’entrée rapide XMM. Toute tentative d’utilisation de cette interface lorsque l’hyperviseur n’indique pas la disponibilité entraîne une erreur #UD.

Inscrire le mappage (entrée et sortie)

Les registres qui ne sont pas utilisés pour transmettre les paramètres d’entrée peuvent être utilisés pour retourner la sortie. En d’autres termes, si le bloc de paramètres d’entrée est inférieur à 112 octets (arrondi au plus proche segment aligné de 16 octets), les registres restants retournent la sortie hypercall.

x64 Informations fournies
RDX Bloc d’entrée ou de sortie
R8 Bloc d’entrée ou de sortie
XMM0 Bloc d’entrée ou de sortie
XMM1 Bloc d’entrée ou de sortie
XMM2 Bloc d’entrée ou de sortie
XMM3 Bloc d’entrée ou de sortie
XMM4 Bloc d’entrée ou de sortie
XMM5 Bloc d’entrée ou de sortie

Par exemple, si le bloc de paramètres d’entrée est de 20 octets de taille, l’hyperviseur ignore les 12 octets suivants. Les 80 octets restants contiennent la sortie hypercall (le cas échéant).

Registres volatiles

Hypercalls modifie uniquement les valeurs d’inscription spécifiées dans les conditions suivantes :

  1. RAX (x64) et EDX:EAX (x86) sont toujours remplacés par la valeur de résultat hypercall et les paramètres de sortie, le cas échéant.
  2. Les hypercalls de rep modifient RCX (x64) et EDX:EAX (x86) avec le nouvel index de début de rep.
  3. HvCallSetVpRegisters peut modifier tous les registres pris en charge avec cet hypercall.
  4. RDX, R8 et XMM0 via XMM5, lorsqu’il est utilisé pour l’entrée hypercall rapide, restent non modifiés. Toutefois, les registres utilisés pour la sortie hypercall rapide peuvent être modifiés, notamment RDX, R8 et XMM0 via XMM5. Hyper-V modifie uniquement ces registres pour la sortie hypercall rapide, limitée à x64.

Hypercall Restrictions

Les hypercalls peuvent avoir des restrictions associées à celles-ci pour qu’elles effectuent leur fonction prévue. Si toutes les restrictions ne sont pas remplies, l’hypercall se termine par une erreur appropriée. Les restrictions suivantes sont répertoriées, le cas échéant :

  • La partition appelante doit posséder un privilège particulier
  • La partition en cours d’exécution doit être dans un état particulier (par exemple, « Actif »)

Codes d’état Hypercall

Chaque hypercall est documenté comme renvoyant une valeur de sortie qui contient plusieurs champs. Un champ de valeur d’état (de type HV_STATUS) est utilisé pour indiquer si l’appel a réussi ou échoué.

Validité du paramètre de sortie sur les hypercalls ayant échoué

Sauf indication explicite, lorsqu’un hypercall échoue (autrement dit, le champ de résultat de la valeur de résultat hypercall contient une valeur autre que HV_STATUS_SUCCESS), le contenu de tous les paramètres de sortie est indéterminé et ne doit pas être examiné par l’appelant. Uniquement lorsque l’hypercall réussit, tous les paramètres de sortie appropriés contiennent des résultats valides et attendus.

Ordre des conditions d’erreur

L’ordre dans lequel les conditions d’erreur sont détectées et signalées par l’hyperviseur n’est pas définie. En d’autres termes, si plusieurs erreurs existent, l’hyperviseur doit choisir la condition d’erreur à signaler. La priorité doit être donnée à ces codes d’erreur offrant une plus grande sécurité, l’intention d’empêcher l’hyperviseur de révéler des informations aux appelants n’ayant pas suffisamment de privilège. Par exemple, le code d’état est le code HV_STATUS_ACCESS_DENIED d’état préféré par rapport à celui qui révélerait des informations de contexte ou d’état purement basées sur le privilège.

Codes d’état Hypercall courants

Plusieurs codes de résultat sont communs à tous les hypercalls et ne sont donc pas documentés pour chaque hypercall individuellement. Ces options en question sont les suivantes :

Code d’état État d’erreur
HV_STATUS_SUCCESS L’appel a réussi.
HV_STATUS_INVALID_HYPERCALL_CODE Le code hypercall n’est pas reconnu.
HV_STATUS_INVALID_HYPERCALL_INPUT Le nombre de rep est incorrect (par exemple, un nombre de rep non nul est passé à un appel non-rep ou un nombre de rep zéro est passé à un appel de rep).
L’index de début du rep n’est pas inférieur au nombre de rep.
Un bit réservé dans la valeur d’entrée hypercall spécifiée n’est pas zéro.
HV_STATUS_INVALID_ALIGNMENT Le pointeur GPA d’entrée ou de sortie spécifié n’est pas aligné sur 8 octets.
Les listes de paramètres d’entrée ou de sortie spécifiées s’étendent sur des pages.
Le pointeur GPA d’entrée ou de sortie n’est pas dans les limites de l’espace GPA.

Le code HV_STATUS_SUCCESS de retour indique qu’aucune condition d’erreur n’a été détectée.

Création de rapports sur l’identité du système d’exploitation invité

Le système d’exploitation invité s’exécutant dans la partition doit s’identifier à l’hyperviseur en écrivant sa signature et sa version dans un MSR (HV_X64_MSR_GUEST_OS_ID) avant de pouvoir appeler des hypercalls. Ce MSR est à l’échelle de la partition et est partagé entre tous les processeurs virtuels.

La valeur de ce registre est initialement zéro. Une valeur non nulle doit être écrite dans le MSR ID de système d’exploitation invité avant que la page de codes Hypercall puisse être activée (voir Établissement de l’interface Hypercall). Si ce registre est ensuite mis à zéro, la page de codes hypercall est désactivée.

#define HV_X64_MSR_GUEST_OS_ID 0x40000000

Identité du système d’exploitation invité pour les systèmes d’exploitation propriétaires

Voici l’encodage recommandé pour ce MSR. Certains champs peuvent ne pas s’appliquer à certains systèmes d’exploitation invités.

Bits Champ Description
15:0 Numéro de build Indique le numéro de build du système d’exploitation
23:16 Service Version Indique la version du service (par exemple, le numéro « Service Pack »)
31:24 Version mineure Indique la version mineure du système d’exploitation
39:32 Version majeure Indique la version principale du système d’exploitation
47:40 ID du système d’exploitation Indique la variante du système d’exploitation. L’encodage est propre au fournisseur. Les systèmes d’exploitation Microsoft sont encodés comme suit : 0=Undefined, 1=MS-DOS®, 2=Windows ® 3.x, 3=Windows ® 9x, 4=Windows ® NT (et dérivés), 5=Windows ® CE
62:48 ID de fournisseur Indique le fournisseur du système d’exploitation invité. La valeur 0 est réservée. Consultez la liste des fournisseurs ci-dessous.
63 Type de système d’exploitation Indique les types de système d’exploitation. La valeur 0 indique un système d’exploitation source propriétaire et fermé. La valeur 1 indique un système d’exploitation open source.

Les valeurs du fournisseur sont allouées par Microsoft. Pour demander un nouveau fournisseur, envoyez un problème dans le référentiel de documentation de virtualisation GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS).

Fournisseur Valeur
Microsoft 0x0001
HPE 0x0002
LANCOM 0x0200

MSR d’identité du système d’exploitation invité pour les systèmes d’exploitation open source

L’encodage suivant est proposé comme conseils pour open source fournisseurs de système d’exploitation qui ont l’intention de se conformer à cette spécification. Il est suggéré que open source systèmes d’exploitation adaptent la convention suivante.

Bits Champ Description
15:0 Numéro de build Informations supplémentaires
47:16 Version Informations sur la version du noyau en amont.
55:48 ID du système d’exploitation Informations supplémentaires sur le fournisseur
62:56 Type de système d’exploitation Type de système d’exploitation (par exemple, Linux, FreeBSD, etc.). Voir la liste des types de système d’exploitation connus ci-dessous
63 Open source La valeur 1 indique un système d’exploitation open source.

Les valeurs de type de système d’exploitation sont allouées par Microsoft. Pour demander un nouveau type de système d’exploitation, envoyez un problème dans le référentiel de documentation de virtualisation GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS).

Type de système d’exploitation Valeur
Linux 0x1
FreeBSD 0x2
Xen 0x3
Illumos 0x4

Établissement de l’interface Hypercall

Les hypercalls sont appelés à l’aide d’un opcode spécial. Étant donné que ce opcode diffère entre les implémentations de virtualisation, il est nécessaire que l’hyperviseur extrait cette différence. Cette opération s’effectue par le biais d’une page hypercall spéciale. Cette page est fournie par l’hyperviseur et apparaît dans l’espace GPA de l’invité. L’invité doit spécifier l’emplacement de la page en programmant le MSR Guest Hypercall.

#define HV_X64_MSR_HYPERCALL 0x40000001
Bits Description Attributs
63:12 GPFN Hypercall : indique le numéro de page physique invité de la page hypercall Lecture/écriture
11:2 RsvdP. Les bits doivent être ignorés sur les lectures et conservés sur les écritures. Réservé
1 Verrouillé. Indique si le MSR est immuable. S’il est défini, ce MSR est verrouillé, empêchant ainsi le déplacement de la page hypercall. Une fois définie, seule une réinitialisation système peut effacer le bit. Lecture/écriture
0 Activer la page Hypercall Lecture/écriture

La page hypercall peut être placée n’importe où dans l’espace GPA de l’invité, mais doit être alignée sur la page. Si l’invité tente de déplacer la page hypercall au-delà des limites de l’espace GPA, une erreur #GP se produit lorsque le MSR est écrit.

Ce MSR est un MSR à l’échelle de la partition. En d’autres termes, il est partagé par tous les processeurs virtuels dans la partition. Si un processeur virtuel écrit correctement dans le MSR, un autre processeur virtuel lit la même valeur.

Avant l’activation de la page hypercall, le système d’exploitation invité doit signaler son identité en écrivant sa signature de version dans un MSR distinct (HV_X64_MSR_GUEST_OS_ID). Si aucune identité de système d’exploitation invité n’a été spécifiée, les tentatives d’activation de l’hypercall échouent. Le bit d’activation reste zéro même si un bit est écrit dans celui-ci. En outre, si l’identité du système d’exploitation invité est effacée à zéro une fois que la page hypercall a été activée, elle devient désactivée.

La page hypercall s’affiche sous la forme d’une « superposition » dans l’espace GPA ; autrement dit, elle couvre tout ce qui est mappé à la plage GPA. Son contenu est lisible et exécutable par l’invité. Les tentatives d’écriture dans la page hypercall entraînent une exception de protection (#GP). Une fois la page hypercall activée, l’appel d’un hypercall implique simplement un appel au début de la page.

Voici une liste détaillée des étapes impliquées dans l’établissement de la page hypercall :

  1. L’invité lit cpuID leaf 1 et détermine si un hyperviseur est présent en vérifiant le bit 31 du registre ECX.
  2. L’invité lit la feuille CPUID 0x40000000 pour déterminer la feuille CPUID maximale de l’hyperviseur (retournée dans le registre EAX) et la feuille CPUID 0x40000001 pour déterminer la signature d’interface (retournée dans le registre EAX). Il vérifie que la valeur feuille maximale est au moins 0x40000005 et que la signature d’interface est égale à « Hv#1 ». Cette signature implique que HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL et HV_X64_MSR_VP_INDEX sont implémentés.
  3. L’invité écrit son identité de système d’exploitation dans le MSR HV_X64_MSR_GUEST_OS_ID si ce registre est égal à zéro.
  4. L’invité lit le MSR Hypercall (HV_X64_MSR_HYPERCALL).
  5. L’invité vérifie le bit Activer la page Hypercall. Si elle est définie, l’interface est déjà active et les étapes 6 et 7 doivent être omises.
  6. L’invité trouve une page dans son espace GPA, de préférence une page qui n’est pas occupée par ram, MMIO, et ainsi de suite. Si la page est occupée, l’invité doit éviter d’utiliser la page sous-jacente à d’autres fins.
  7. L’invité écrit une nouvelle valeur dans le MSR Hypercall (HV_X64_MSR_HYPERCALL) qui inclut l’objet GPA de l’étape 6 et définit le bit Activer la page Hypercall pour activer l’interface.
  8. L’invité crée un mappage VA exécutable sur la page Hypercall GPA.
  9. L’invité consulte cpuID leaf 0x40000003 pour déterminer quelles installations d’hyperviseur sont disponibles. Une fois l’interface établie, l’invité peut lancer un hypercall. Pour ce faire, il remplit les registres conformément au protocole hypercall et émet un APPEL au début de la page hypercall. L’invité doit supposer que la page hypercall effectue l’équivalent d’un retour proche (0xC3) pour revenir à l’appelant. Par conséquent, l’hypercall doit être appelé avec une pile valide.

Interface Hypercall étendue

Les hypercalls avec des codes d’appel ci-dessus 0x8000 sont appelés hypercalls étendus. Les hypercalls étendus utilisent la même convention d’appel que les hypercalls normaux et apparaissent identiques du point de vue d’une machine virtuelle invitée. Les hypercalls étendus sont gérés en interne différemment dans l’hyperviseur Hyper-V.

Les fonctionnalités hypercall étendues peuvent être interrogées avec HvExtCallQueryCapabilities.