Architecture AVF

Android fournit une implémentation de référence de tous les composants nécessaires à la mise en œuvre du framework de virtualisation Android. Actuellement, cette implémentation est limitée à ARM64. Cette page explique l'architecture du framework.

Arrière-plan

L'architecture Arm autorise jusqu'à quatre niveaux d'exception, le niveau d'exception 0 (EL0) étant le moins privilégié et le niveau d'exception 3 (EL3) le plus. La plus grande partie de la base de code Android (tous les composants de l'espace utilisateur) s'exécute sur EL0. Le reste de ce que l'on appelle communément « Android » est le noyau Linux, qui s'exécute sur EL1.

La couche EL2 permet l'introduction d'un hyperviseur qui permet d'isoler la mémoire et les appareils dans des pVM individuelles au niveau EL1/EL0, avec de fortes garanties de confidentialité et d'intégrité.

Hyperviseur

La machine virtuelle protégée basée sur le noyau (pKVM) est construite sur l' hyperviseur Linux KVM , qui a été étendu avec la possibilité de restreindre l'accès aux charges utiles exécutées dans les machines virtuelles invitées marquées « protégées » au moment de la création.

KVM/arm64 prend en charge différents modes d'exécution en fonction de la disponibilité de certaines fonctionnalités du processeur, à savoir les extensions d'hôte de virtualisation (VHE) (ARMv8.1 et versions ultérieures). Dans l'un de ces modes, communément appelé mode non-VHE, le code de l'hyperviseur est séparé de l'image du noyau lors du démarrage et installé sur EL2, tandis que le noyau lui-même s'exécute sur EL1. Bien qu'il fasse partie de la base de code Linux, le composant EL2 de KVM est un petit composant chargé de la commutation entre plusieurs EL1, et entièrement contrôlé par le noyau de l'hôte. Le composant hyperviseur est compilé avec Linux, mais réside dans une section de mémoire dédiée distincte de l'image vmlinux . pKVM exploite cette conception en étendant le code de l'hyperviseur avec de nouvelles fonctionnalités lui permettant d'imposer des restrictions sur le noyau hôte Android et l'espace utilisateur, et en limitant l'accès de l'hôte à la mémoire invité et à l'hyperviseur.

Modules du fournisseur pKVM

Un module fournisseur pKVM est un module spécifique au matériel contenant des fonctionnalités spécifiques au périphérique, telles que les pilotes d'unité de gestion de mémoire d'entrée-sortie (IOMMU). Ces modules vous permettent de porter des fonctionnalités de sécurité nécessitant un accès de niveau d'exception 2 (EL2) à pKVM.

Pour savoir comment implémenter et charger un module fournisseur pKVM, reportez-vous à Implémenter un module fournisseur pKVM .

Procédure de démarrage

La figure suivante illustre la procédure de démarrage de pKVM :

Procédure de démarrage pKVM

Figure 1. Procédure de démarrage pKVM

  1. Le chargeur de démarrage entre dans le noyau générique en EL2.
  2. Le noyau générique détecte qu'il fonctionne en EL2 et se prive d'EL1 tandis que pKVM et ses modules continuent de fonctionner en EL2. De plus, les modules du fournisseur pKVM sont chargés à ce moment-là.
  3. Le noyau générique démarre normalement, chargeant tous les pilotes de périphérique nécessaires jusqu'à atteindre l'espace utilisateur. À ce stade, pKVM est en place et gère les tables de pages de l'étape 2.

La procédure de démarrage fait confiance au chargeur de démarrage pour maintenir l'intégrité de l'image du noyau uniquement lors d'un démarrage précoce. Lorsque le noyau est déprivilégié, il n'est plus considéré comme fiable par l'hyperviseur, qui se charge alors de se protéger même si le noyau est compromis.

Le fait d'avoir le noyau Android et l'hyperviseur dans la même image binaire permet une interface de communication très étroitement couplée entre eux. Ce couplage étroit garantit des mises à jour atomiques des deux composants, ce qui évite d'avoir à maintenir la stabilité de l'interface entre eux et offre une grande flexibilité sans compromettre la maintenabilité à long terme. Le couplage étroit permet également des optimisations de performances lorsque les deux composants peuvent coopérer sans impacter les garanties de sécurité fournies par l'hyperviseur.

De plus, l'adoption de GKI dans l'écosystème Android permet automatiquement de déployer l'hyperviseur pKVM sur les appareils Android dans le même binaire que le noyau.

Protection de l'accès à la mémoire du processeur

L'architecture Arm spécifie une unité de gestion de mémoire (MMU) divisée en deux étapes indépendantes, qui peuvent toutes deux être utilisées pour mettre en œuvre la traduction d'adresses et le contrôle d'accès à différentes parties de la mémoire. L'étage 1 MMU est contrôlé par EL1 et permet un premier niveau de traduction d'adresses. La MMU de l'étape 1 est utilisée par Linux pour gérer l'espace d'adressage virtuel fourni à chaque processus de l'espace utilisateur et à son propre espace d'adressage virtuel.

La MMU de l'étage 2 est contrôlée par EL2 et permet l'application d'une seconde traduction d'adresse sur l'adresse de sortie de la MMU de l'étage 1, aboutissant à une adresse physique (PA). La traduction de l'étape 2 peut être utilisée par les hyperviseurs pour contrôler et traduire les accès à la mémoire de toutes les machines virtuelles invitées. Comme le montre la figure 2, lorsque les deux étapes de traduction sont activées, l'adresse de sortie de l'étape 1 est appelée adresse physique intermédiaire (IPA). Remarque : L'adresse virtuelle (VA) est traduite en IPA puis en PA.

Protection de l'accès à la mémoire du processeur

Figure 2. Protection de l'accès à la mémoire du processeur

Historiquement, KVM fonctionne avec l'étape 2 de traduction activée lors de l'exécution des invités et avec l'étape 2 désactivée lors de l'exécution du noyau Linux hôte. Cette architecture permet aux accès à la mémoire depuis la MMU de l'étape 1 de l'hôte de passer par la MMU de l'étape 2, permettant ainsi un accès illimité de l'hôte aux pages de mémoire invité. D'un autre côté, pKVM permet une protection de niveau 2 même dans le contexte de l'hôte et confie à l'hyperviseur la responsabilité de protéger les pages de mémoire invité à la place de l'hôte.

KVM exploite pleinement la traduction d'adresses à l'étape 2 pour mettre en œuvre des mappages IPA/PA complexes pour les invités, ce qui crée l'illusion d'une mémoire contiguë pour les invités malgré la fragmentation physique. Cependant, l'utilisation de la MMU de niveau 2 pour l'hôte est limitée au contrôle d'accès uniquement. L'étape hôte 2 est mappée d'identité, garantissant que la mémoire contiguë dans l'espace IPA hôte est contiguë dans l'espace PA. Cette architecture permet l'utilisation de mappages volumineux dans la table des pages et réduit par conséquent la pression sur le tampon de traduction (TLB). Étant donné qu'un mappage d'identité peut être indexé par PA, l'étape hôte 2 est également utilisée pour suivre la propriété des pages directement dans la table des pages.

Protection contre l'accès direct à la mémoire (DMA)

Comme décrit précédemment, la suppression du mappage des pages invité de l'hôte Linux dans les tables des pages CPU est une étape nécessaire mais insuffisante pour protéger la mémoire invité. pKVM doit également se protéger contre les accès à la mémoire effectués par des appareils compatibles DMA sous le contrôle du noyau hôte, ainsi que contre la possibilité d'une attaque DMA initiée par un hôte malveillant. Pour empêcher un tel périphérique d'accéder à la mémoire invitée, pKVM nécessite un matériel d'unité de gestion de mémoire d'entrée-sortie (IOMMU) pour chaque périphérique compatible DMA du système, comme le montre la figure 3.

Protection d'accès à la mémoire DMA

Figure 3. Protection de l'accès à la mémoire DMA

Au minimum, le matériel IOMMU fournit les moyens d'accorder et de révoquer l'accès en lecture/écriture d'un périphérique à la mémoire physique à la granularité des pages. Cependant, ce matériel IOMMU limite l'utilisation de périphériques dans les pVM car ils supposent une étape 2 de mappage d'identité.

Pour garantir l'isolation entre les machines virtuelles, les transactions de mémoire générées pour le compte de différentes entités doivent être distinguables par l'IOMMU afin que l'ensemble approprié de tables de pages puisse être utilisé pour la traduction.

De plus, la réduction de la quantité de code spécifique au SoC au niveau EL2 est une stratégie clé pour réduire la base informatique de confiance (TCB) globale de pKVM et va à l'encontre de l'inclusion des pilotes IOMMU dans l'hyperviseur. Pour atténuer ce problème, l'hôte d'EL1 est responsable des tâches auxiliaires de gestion IOMMU, telles que la gestion de l'alimentation, l'initialisation et, le cas échéant, la gestion des interruptions.

Cependant, donner à l'hôte le contrôle de l'état du périphérique impose des exigences supplémentaires à l'interface de programmation du matériel IOMMU pour garantir que les contrôles d'autorisation ne peuvent pas être contournés par d'autres moyens, par exemple après une réinitialisation du périphérique.

L'architecture SMMU (Arm System Memory Management Unit) est une IOMMU standard et bien prise en charge pour les appareils Arm qui permet à la fois l'isolation et l'affectation directe. Cette architecture est la solution de référence recommandée.

Propriété de la mémoire

Au moment du démarrage, toute la mémoire non hyperviseure est supposée appartenir à l'hôte et est suivie comme telle par l'hyperviseur. Lorsqu'une pVM est générée, l'hôte fait don de pages de mémoire pour lui permettre de démarrer et l'hyperviseur transfère la propriété de ces pages de l'hôte à la pVM. Ainsi, l'hyperviseur met en place des restrictions de contrôle d'accès dans la table des pages de l'étape 2 de l'hôte pour l'empêcher d'accéder à nouveau aux pages, garantissant ainsi la confidentialité à l'invité.

La communication entre l'hôte et les invités est rendue possible par un partage contrôlé de la mémoire entre eux. Les invités sont autorisés à partager certaines de leurs pages avec l'hôte à l'aide d'un hypercall, qui demande à l'hyperviseur de remapper ces pages dans le tableau des pages de l'étape 2 de l'hôte. De même, la communication de l'hôte avec TrustZone est rendue possible par des opérations de partage et/ou de prêt de mémoire, qui sont toutes étroitement surveillées et contrôlées par pKVM à l'aide de la spécification Firmware Framework for Arm (FF-A) .

Étant donné que les besoins en mémoire d'une pVM peuvent changer avec le temps, un hypercall est fourni qui permet de restituer à l'hôte la propriété des pages spécifiées appartenant à l'appelant. En pratique, cet hyperappel est utilisé avec le protocole de ballon virtio pour permettre au VMM de demander de la mémoire au pVM, et pour que le pVM notifie au VMM les pages abandonnées, de manière contrôlée.

L'hyperviseur est chargé de suivre la propriété de toutes les pages de mémoire du système et de savoir si elles sont partagées ou prêtées à d'autres entités. La majeure partie de ce suivi d'état est effectuée à l'aide de métadonnées attachées aux tables de pages de l'étape 2 de l'hôte et des invités, en utilisant des bits réservés dans les entrées de table de pages (PTE) qui, comme leur nom l'indique, sont réservées à une utilisation logicielle.

L'hébergeur doit s'assurer qu'il ne tente pas d'accéder aux pages rendues inaccessibles par l'hyperviseur. Un accès illégal à l'hôte provoque l'injection d'une exception synchrone dans l'hôte par l'hyperviseur, ce qui peut soit entraîner la réception d'un signal SEGV par la tâche de l'espace utilisateur responsable, soit le crash du noyau hôte. Pour éviter les accès accidentels, les pages données aux invités ne peuvent plus être échangées ou fusionnées par le noyau hôte.

Gestion des interruptions et minuteries

Les interruptions constituent un élément essentiel de la manière dont un invité interagit avec les appareils et pour la communication entre les processeurs, où les interruptions interprocesseurs (IPI) constituent le principal mécanisme de communication. Le modèle KVM consiste à déléguer toute la gestion des interruptions virtuelles à l'hôte dans EL1, qui se comporte à cet effet comme une partie non fiable de l'hyperviseur.

pKVM propose une émulation complète de Generic Interrupt Controller version 3 (GICv3) basée sur le code KVM existant. La minuterie et les IPI sont gérés dans le cadre de ce code d'émulation non fiable.

Prise en charge du GICv3

L'interface entre EL1 et EL2 doit garantir que l'état complet de l'interruption est visible pour l'hôte EL1, y compris les copies des registres de l'hyperviseur liés aux interruptions. Cette visibilité est généralement obtenue à l'aide de régions de mémoire partagée, une par CPU virtuel (vCPU).

Le code de prise en charge de l'exécution du registre système peut être simplifié pour prendre en charge uniquement le trapping du registre d'interruption généré par logiciel (SGIR) et du registre d'interruption de désactivation (DIR). L'architecture exige que ces registres soient toujours interceptés vers EL2, tandis que les autres pièges n'ont jusqu'à présent été utiles que pour atténuer les errata. Tout le reste est géré matériellement.

Côté MMIO, tout est émulé en EL1, réutilisant toute l'infrastructure actuelle en KVM. Enfin, l'attente d'interruption (WFI) est toujours relayée vers EL1, car il s'agit de l'une des primitives de planification de base utilisées par KVM.

Prise en charge de la minuterie

La valeur du comparateur pour la minuterie virtuelle doit être exposée à EL1 sur chaque WFI de trapping afin que EL1 puisse injecter des interruptions de minuterie pendant que le vCPU est bloqué. Le minuteur physique est entièrement émulé et tous les pièges sont relayés vers EL1.

Gestion des MMIO

Pour communiquer avec le moniteur de machine virtuelle (VMM) et effectuer une émulation GIC, les interruptions MMIO doivent être relayées vers l'hôte dans EL1 pour un tri ultérieur. pKVM nécessite les éléments suivants :

  • IPA et taille de l'accès
  • Données en cas d'écriture
  • Endianness du CPU au point de piégeage

De plus, les interruptions avec un registre à usage général (GPR) comme source/destination sont relayées à l'aide d'un pseudo-registre de transfert abstrait.

Interfaces invités

Un invité peut communiquer avec un invité protégé en utilisant une combinaison d'hyperappels et d'accès à la mémoire aux régions piégées. Les hypercalls sont exposés selon le standard SMCCC , avec une plage réservée à une allocation fournisseur par KVM. Les hypercalls suivants sont particulièrement importants pour les invités pKVM.

Hyperappels génériques

  • PSCI fournit un mécanisme standard permettant à l'invité de contrôler le cycle de vie de ses processeurs virtuels, y compris la mise en ligne, la mise hors ligne et l'arrêt du système.
  • TRNG fournit un mécanisme standard permettant à l'invité de demander de l'entropie au pKVM qui relaie l'appel vers EL3. Ce mécanisme est particulièrement utile lorsqu'on ne peut pas faire confiance à l'hôte pour virtualiser un générateur de nombres aléatoires (RNG) matériel.

Hyperappels pKVM

  • Partage de mémoire avec l'hôte. Toute la mémoire invité est initialement inaccessible à l'hôte, mais l'accès à l'hôte est nécessaire pour la communication en mémoire partagée et pour les périphériques paravirtualisés qui s'appuient sur des tampons partagés. Les hyperappels pour partager et annuler le partage de pages avec l'hôte permettent à l'invité de décider exactement quelles parties de la mémoire sont rendues accessibles au reste d'Android sans avoir besoin d'une poignée de main.
  • Abandon de mémoire à l'hôte. Toute la mémoire de l'invité appartient généralement à l'invité jusqu'à ce qu'elle soit détruite. Cet état peut s'avérer inadéquat pour les machines virtuelles de longue durée dont les besoins en mémoire varient dans le temps. L'hyperappel relinquish permet à un invité de transférer explicitement la propriété des pages à l'hôte sans nécessiter la résiliation de l'invité.
  • Interception de l'accès à la mémoire vers l'hôte. Traditionnellement, si un invité KVM accède à une adresse qui ne correspond pas à une région de mémoire valide, le thread vCPU quitte l'hôte et l'accès est généralement utilisé pour MMIO et émulé par le VMM dans l'espace utilisateur. Pour faciliter cette gestion, pKVM est tenu d'annoncer à l'hôte les détails de l'instruction défaillante, tels que son adresse, ses paramètres d'enregistrement et potentiellement son contenu, ce qui pourrait involontairement exposer les données sensibles d'un invité protégé si l'interruption n'était pas anticipée. pKVM résout ce problème en traitant ces erreurs comme fatales, à moins que l'invité n'ait préalablement émis un hypercall pour identifier la plage IPA défaillante comme étant celle pour laquelle les accès sont autorisés à revenir vers l'hôte. Cette solution est appelée la garde MMIO .

Périphérique d'E/S virtuel (virtio)

Virtio est une norme populaire, portable et mature pour la mise en œuvre et l'interaction avec des appareils paravirtualisés. La majorité des appareils exposés aux invités protégés sont implémentés à l'aide de virtio. Virtio sous-tend également l'implémentation vsock utilisée pour la communication entre un invité protégé et le reste d'Android.

Les appareils Virtio sont généralement implémentés dans l'espace utilisateur de l'hôte par le VMM, qui intercepte les accès mémoire piégés de l'invité à l'interface MMIO du périphérique virtio et émule le comportement attendu. L'accès MMIO est relativement coûteux car chaque accès au périphérique nécessite un aller-retour vers le VMM et inversement, de sorte que la plupart du transfert de données réel entre le périphérique et l'invité s'effectue à l'aide d'un ensemble de files d'attente virtuelles en mémoire. Une hypothèse clé de virtio est que l'hôte peut accéder arbitrairement à la mémoire invité. Cette hypothèse est évidente dans la conception de la virtqueue, qui peut contenir des pointeurs vers des tampons dans l'invité auxquels l'émulation de périphérique est censée accéder directement.

Bien que les hyperappels de partage de mémoire décrits précédemment puissent être utilisés pour partager des tampons de données virtio de l'invité vers l'hôte, ce partage est nécessairement effectué au niveau de la granularité de la page et pourrait finir par exposer plus de données que nécessaire si la taille du tampon est inférieure à celle d'une page. . Au lieu de cela, l'invité est configuré pour allouer à la fois les virtqueues et leurs tampons de données correspondants à partir d'une fenêtre fixe de mémoire partagée, les données étant copiées (rebondies) vers et depuis la fenêtre selon les besoins.

Appareil virtuel

Figure 4. Appareil Virtio

Interaction avec TrustZone

Bien que les invités ne puissent pas interagir directement avec TrustZone, l'hôte doit toujours être en mesure d'émettre des appels SMC dans le monde sécurisé. Ces appels peuvent spécifier des tampons de mémoire adressés physiquement et inaccessibles à l'hôte. Étant donné que le logiciel sécurisé ignore généralement l'accessibilité du tampon, un hôte malveillant pourrait utiliser ce tampon pour effectuer une attaque adjointe confuse (analogue à une attaque DMA). Pour empêcher de telles attaques, pKVM intercepte tous les appels SMC de l'hôte vers EL2 et agit comme un proxy entre l'hôte et le moniteur sécurisé sur EL3.

Les appels PSCI de l'hôte sont transmis au micrologiciel EL3 avec des modifications minimes. Plus précisément, le point d'entrée d'un processeur entrant en ligne ou reprenant une suspension est réécrit de sorte que la table des pages de l'étape 2 soit installée sur EL2 avant de revenir à l'hôte sur EL1. Lors du démarrage, ces protections sont appliquées par pKVM.

Cette architecture s'appuie sur le SoC prenant en charge PSCI, de préférence via l'utilisation d'une version à jour de TF-A comme firmware EL3.

Firmware Framework for Arm (FF-A) standardise les interactions entre les mondes normal et sécurisé, notamment en présence d'un hyperviseur sécurisé. Une grande partie de la spécification définit un mécanisme de partage de mémoire avec le monde sécurisé, utilisant à la fois un format de message commun et un modèle d'autorisations bien défini pour les pages sous-jacentes. pKVM transmet les messages FF-A pour garantir que l'hôte ne tente pas de partager de la mémoire avec le côté sécurisé pour lequel il ne dispose pas des autorisations suffisantes.

Cette architecture s'appuie sur le logiciel du monde sécurisé appliquant le modèle d'accès à la mémoire, pour garantir que les applications de confiance et tout autre logiciel exécuté dans le monde sécurisé ne peuvent accéder à la mémoire que s'il appartient exclusivement au monde sécurisé ou s'il a été explicitement partagé avec lui à l'aide de FF. -UN. Sur un système avec S-EL2, l'application du modèle d'accès à la mémoire doit être effectuée par un Secure Partition Manager Core (SPMC), tel que Hafnium , qui gère les tables de pages de niveau 2 pour le monde sécurisé. Sur un système sans S-EL2, le TEE peut à la place appliquer un modèle d'accès à la mémoire via ses tables de pages de l'étape 1.

Si l'appel SMC vers EL2 n'est pas un appel PSCI ou un message défini par FF-A, les SMC non gérés sont transmis à EL3. L'hypothèse est que le micrologiciel sécurisé (nécessairement fiable) peut gérer les SMC non gérés en toute sécurité, car le micrologiciel comprend les précautions nécessaires pour maintenir l'isolation pVM.

Moniteur de machine virtuelle

crosvm est un moniteur de machine virtuelle (VMM) qui exécute des machines virtuelles via l'interface KVM de Linux. Ce qui rend crosvm unique, c'est l'accent mis sur la sécurité avec l'utilisation du langage de programmation Rust et un bac à sable autour des périphériques virtuels pour protéger le noyau hôte. Pour en savoir plus sur crosvm, consultez sa documentation officielle ici .

Descripteurs de fichiers et ioctls

KVM expose le périphérique de caractères /dev/kvm à l'espace utilisateur avec les ioctls qui constituent l'API KVM. Les ioctls appartiennent aux catégories suivantes :

  • Les ioctls système interrogent et définissent les attributs globaux qui affectent l'ensemble du sous-système KVM, et créent des pVM.
  • Les ioctls de VM interrogent et définissent les attributs qui créent des processeurs virtuels (vCPU) et des périphériques, et affectent une pVM entière, comme l'inclusion de la disposition de la mémoire et du nombre de processeurs virtuels (vCPU) et de périphériques.
  • Les ioctls vCPU interrogent et définissent les attributs qui contrôlent le fonctionnement d'un seul processeur virtuel.
  • Les ioctls de périphérique interrogent et définissent les attributs qui contrôlent le fonctionnement d'un seul périphérique virtuel.

Chaque processus crosvm exécute exactement une instance d'une machine virtuelle. Ce processus utilise l'ioctl système KVM_CREATE_VM pour créer un descripteur de fichier VM qui peut être utilisé pour émettre des ioctls pVM. Un ioctl KVM_CREATE_VCPU ou KVM_CREATE_DEVICE sur une VM FD crée un vCPU/périphérique et renvoie un descripteur de fichier pointant vers la nouvelle ressource. Les ioctls sur un vCPU ou un périphérique FD peuvent être utilisés pour contrôler le périphérique qui a été créé à l'aide de l'ioctl sur un VM FD. Pour les processeurs virtuels, cela inclut la tâche importante consistant à exécuter le code invité.

En interne, crosvm enregistre les descripteurs de fichiers de la VM auprès du noyau à l'aide de l'interface epoll déclenchée par Edge. Le noyau informe ensuite crosvm chaque fois qu'un nouvel événement est en attente dans l'un des descripteurs de fichier.

pKVM ajoute une nouvelle fonctionnalité, KVM_CAP_ARM_PROTECTED_VM , qui peut être utilisée pour obtenir des informations sur l'environnement pVM et configurer le mode protégé pour une VM. crosvm l'utilise lors de la création de pVM si l'indicateur --protected-vm est passé, pour interroger et réserver la quantité appropriée de mémoire pour le micrologiciel pVM, puis pour activer le mode protégé.

Allocation de mémoire

L'une des principales responsabilités d'un VMM est d'allouer la mémoire de la VM et de gérer sa disposition en mémoire. crosvm génère une disposition de mémoire fixe vaguement décrite dans le tableau ci-dessous.

FDT en mode normal PHYS_MEMORY_END - 0x200000
Espace libre ...
Disque RAM ALIGN_UP(KERNEL_END, 0x1000000)
Noyau 0x80080000
Chargeur de démarrage 0x80200000
FDT en mode BIOS 0x80000000
Base de mémoire physique 0x80000000
Micrologiciel pVM 0x7FE00000
Mémoire de l'appareil 0x10000 - 0x40000000

La mémoire physique est allouée avec mmap et la mémoire est donnée à la VM pour remplir ses régions de mémoire, appelées memslots , avec l'ioctl KVM_SET_USER_MEMORY_REGION . Toute la mémoire pVM invitée est donc attribuée à l'instance crosvm qui la gère et peut entraîner la mort du processus (terminant la VM) si l'hôte commence à manquer de mémoire libre. Lorsqu'une VM est arrêtée, la mémoire est automatiquement effacée par l'hyperviseur et renvoyée au noyau hôte.

Sous KVM standard, le VMM conserve l'accès à toute la mémoire invité. Avec pKVM, la mémoire invité n'est pas mappée de l'espace d'adressage physique de l'hôte lorsqu'elle est donnée à l'invité. La seule exception est la mémoire explicitement partagée par l'invité, comme pour les appareils virtio.

Les régions MMIO dans l’espace d’adressage de l’invité ne sont pas mappées. L'accès à ces régions par l'invité est intercepté et entraîne un événement d'E/S sur la VM FD. Ce mécanisme est utilisé pour implémenter des périphériques virtuels. En mode protégé, l'invité doit reconnaître qu'une région de son espace d'adressage est utilisée pour MMIO à l'aide d'un hypercall, afin de réduire le risque de fuite accidentelle d'informations.

Planification

Chaque CPU virtuel est représenté par un thread POSIX et planifié par le planificateur Linux hôte. Le thread appelle l'ioctl KVM_RUN sur le vCPU FD, ce qui entraîne le basculement de l'hyperviseur vers le contexte du vCPU invité. Le planificateur d'hôte prend en compte le temps passé dans un contexte invité comme temps utilisé par le thread vCPU correspondant. KVM_RUN revient lorsqu'un événement doit être géré par le VMM, tel qu'une E/S, la fin d'une interruption ou l'arrêt du vCPU. Le VMM gère l'événement et appelle à nouveau KVM_RUN .

Pendant KVM_RUN , le thread reste préemptif par le planificateur hôte, sauf pour l'exécution du code de l'hyperviseur EL2, qui n'est pas préemptif. La pVM invitée elle-même ne dispose d'aucun mécanisme pour contrôler ce comportement.

Étant donné que tous les threads vCPU sont planifiés comme toutes les autres tâches de l’espace utilisateur, ils sont soumis à tous les mécanismes QoS standard. Plus précisément, chaque thread vCPU peut être affiné à des processeurs physiques, placé dans des cpusets, amélioré ou plafonné à l'aide du limitation d'utilisation, voir sa politique de priorité/planification modifiée, et bien plus encore.

Appareils virtuels

crosvm prend en charge un certain nombre de périphériques, notamment les suivants :

  • virtio-blk pour les images disque composites, en lecture seule ou en lecture-écriture
  • vhost-vsock pour la communication avec l'hôte
  • virtio-pci comme transport virtio
  • horloge temps réel pl030 (RTC)
  • 16550a UART pour la communication série

Micrologiciel pVM

Le micrologiciel pVM (pvmfw) est le premier code exécuté par un pVM, similaire à la ROM de démarrage d'un périphérique physique. L'objectif principal de pvmfw est d'amorcer un démarrage sécurisé et de dériver le secret unique du pVM. pvmfw n'est pas limité à une utilisation avec un système d'exploitation spécifique, tel que Microdroid , tant que le système d'exploitation est pris en charge par crosvm et a été correctement signé.

Le binaire pvmfw est stocké dans une partition flash du même nom et est mis à jour via OTA .

Démarrage de l'appareil

La séquence d'étapes suivante est ajoutée à la procédure de démarrage d'un périphérique compatible pKVM :

  1. Le chargeur de démarrage Android (ABL) charge pvmfw depuis sa partition en mémoire et vérifie l'image.
  2. L'ABL obtient ses secrets DICE (Device Identifier Composition Engine) (identifiants de périphérique composés (CDI) et chaîne de certificat de démarrage (BCC)) à partir d'une racine de confiance.
  3. L'ABL effectue la mesure et la dérivation DICE des secrets de pvmfw (CDI) et les ajoute au binaire pvmfw.
  4. L'ABL ajoute un nœud de région de mémoire réservée linux,pkvm-guest-firmware-memory au DT, décrivant l'emplacement et la taille du binaire pvmfw et les secrets qu'il a dérivés à l'étape précédente.
  5. L'ABL confie le contrôle à Linux et Linux initialise pKVM.
  6. pKVM démappe la région mémoire pvmfw des tables de pages de l'étape 2 de l'hôte et la protège de l'hôte (et des invités) tout au long de la disponibilité de l'appareil.

Après le démarrage de l'appareil, Microdroid est démarré selon les étapes de la section Séquence de démarrage du document Microdroid .

démarrage pVM

Lors de la création d'un pVM, crosvm (ou un autre VMM) doit créer un memslot suffisamment grand pour être rempli avec l'image pvmfw par l'hyperviseur. Le VMM est également restreint dans la liste des registres dont il peut définir la valeur initiale (x0-x14 pour le vCPU principal, aucun pour les vCPU secondaires). Les registres restants sont réservés et font partie de l'ABI hypervisor-pvmfw.

Lorsque le pVM est exécuté, l’hyperviseur confie d’abord le contrôle du vCPU principal à pvmfw. Le micrologiciel s'attend à ce que crosvm ait chargé un noyau signé AVB, qui peut être un chargeur de démarrage ou toute autre image, et un FDT non signé en mémoire à des décalages connus. pvmfw valide la signature AVB et, en cas de succès, génère une arborescence de périphériques approuvés à partir du FDT reçu, efface ses secrets de la mémoire et se dirige vers le point d'entrée de la charge utile. Si l’une des étapes de vérification échoue, le micrologiciel émet un hyperappel PSCI SYSTEM_RESET .

Entre les démarrages, les informations sur l'instance pVM sont stockées dans une partition (périphérique virtio-blk) et chiffrées avec le secret de pvmfw pour garantir qu'après un redémarrage, le secret est fourni à la bonne instance.