Android fournit une implémentation de référence de tous les composants nécessaires à l'implémentation 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 privilégié. La plus grande partie du codebase Android (tous les composants de l'espace utilisateur) s'exécute à EL0. Le reste de ce que l'on appelle communément "Android" est le noyau Linux, qui s'exécute à EL1.
La couche EL2 permet d'introduire un hyperviseur qui permet d'isoler la mémoire et les appareils dans des pVM individuels au niveau EL1/EL0, avec de solides garanties de confidentialité et d'intégrité.
Hyperviseur
La machine virtuelle protégée basée sur le noyau (pKVM) repose sur l'hyperviseur KVM Linux, qui a été étendu avec la possibilité de limiter l'accès aux charges utiles exécutées dans les machines virtuelles invitées marquées comme "protégées" au moment de la création.
KVM/arm64 accepte différents modes d'exécution en fonction de la disponibilité de certaines fonctionnalités de 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 extrait de l'image du noyau au démarrage et installé à EL2, tandis que le noyau lui-même s'exécute à EL1. Bien qu'il fasse partie du codebase Linux, le composant EL2 de KVM est un petit composant chargé de la commutation entre plusieurs EL1. Le composant de l'hyperviseur est compilé avec Linux, mais se trouve dans une section de mémoire distincte et dédiée 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 de limiter l'accès de l'hôte à la mémoire invitée et à l'hyperviseur.
Modules de fournisseurs pKVM
Un module fournisseur pKVM est un module spécifique au matériel contenant des fonctionnalités spécifiques à l'appareil, telles que les pilotes de l'unité de gestion de la mémoire d'E/S (IOMMU). Ces modules vous permettent de transférer des fonctionnalités de sécurité nécessitant un accès EL2 (Exception de niveau 2) à pKVM.
Pour savoir comment implémenter et charger un module de fournisseur pKVM, consultez la section Implémenter un module de fournisseur pKVM.
Procédure de démarrage
La figure suivante illustre la procédure de démarrage de pKVM:
- Le bootloader entre dans le noyau générique à l'EL2.
- Le noyau générique détecte qu'il s'exécute à EL2 et se déprivilège à EL1, tandis que le pKVM et ses modules continuent de s'exécuter à EL2. De plus, les modules du fournisseur pKVM sont chargés à ce moment-là.
- Le noyau générique démarre normalement, chargeant tous les pilotes de périphériques nécessaires jusqu'à atteindre l'espace utilisateur. À ce stade, le pKVM est en place et gère les tables de pages de l'étape 2.
La procédure de démarrage fait confiance au bootloader pour maintenir l'intégrité de l'image du noyau uniquement lors du démarrage précoce. Lorsque le kernel est déprivilégié, il n'est plus considéré comme fiable par l'hyperviseur, qui est alors chargé de se protéger même si le kernel est compromis.
Le fait de placer 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 de maintenir l'interface entre eux stable et offre une grande flexibilité sans compromettre la maintenabilité à long terme. Le couplage étroit permet également d'optimiser les performances lorsque les deux composants peuvent coopérer sans affecter 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 la mémoire (MMU) divisée en deux étapes indépendantes, qui peuvent toutes deux être utilisées pour implémenter la traduction d'adresses et le contrôle des accès à différentes parties de la mémoire. La MMU de l'étape 1 est contrôlée par EL1 et permet un premier niveau de traduction d'adresse. La MMU de l'étape 1 est utilisée par Linux pour gérer l'espace d'adressage virtuel fourni à chaque processus d'espace utilisateur et à son propre espace d'adressage virtuel.
La MMU de l'étape 2 est contrôlée par EL2 et permet d'appliquer une deuxième traduction d'adresse sur l'adresse de sortie de la MMU de l'étape 1, ce qui génère 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 VM invitées. Comme illustré dans 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 (AV) est traduite en IPA, puis en adresse physique (AP).
Historiquement, KVM s'exécute avec la traduction de l'étape 2 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 de la MMU de l'étape 1 d'hôte de passer par le MMU de l'étape 2, autorisant ainsi un accès illimité depuis l'hôte aux pages de mémoire de l'invité. D'autre part, pKVM active la protection de l'étape 2, même dans le contexte de l'hôte, et charge l'hyperviseur de protéger les pages de mémoire invitée au lieu de l'hôte.
KVM exploite pleinement la traduction d'adresses à l'étape 2 pour implémenter 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. Toutefois, l'utilisation de la MMU de l'étape 2 pour l'hôte est limitée au contrôle d'accès uniquement. L'étape 2 de l'hôte est mappée par identité, ce qui garantit que la mémoire contiguë dans l'espace IPA de l'hôte est contiguë dans l'espace PA. Cette architecture permet d'utiliser de grands mappages dans la table des pages et réduit ainsi la pression sur le tampon de traduction (TLB). Étant donné qu'un mappage d'identité peut être indexé par PA, l'étape 2 de l'hôte est également utilisée pour suivre la propriété des pages directement dans la table des pages.
Protection de l'accès direct à la mémoire (DMA, Direct Memory Access)
Comme décrit précédemment, le fait de dissocier les pages d'invité de l'hôte Linux dans les tables des pages du processeur est une étape nécessaire, mais insuffisante pour protéger la mémoire invitée. 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, et contre la possibilité d'une attaque par zone de marché désignée initiée par un hôte malveillant. Pour empêcher un tel appareil d'accéder à la mémoire invitée, pKVM nécessite un matériel IOMMU (Input-Output Memory Management Unit) pour chaque appareil compatible avec DMA du système, comme illustré à la figure 3.
Au minimum, le matériel IOMMU permet d'accorder et de révoquer l'accès en lecture/écriture d'un appareil à la mémoire physique avec une granularité de page. Toutefois, ce matériel IOMMU limite l'utilisation des appareils dans les pVM, car ils supposent une étape 2 mappée sur l'identité.
Pour assurer 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.
En outre, la réduction de la quantité de code spécifique au SoC au niveau d'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 en EL1 est responsable des tâches de gestion auxiliaire de l'IOMMU, telles que la gestion de l'alimentation, l'initialisation et, le cas échéant, la gestion des interruptions.
Toutefois, donner le contrôle de l'état de l'appareil à l'hôte impose des exigences supplémentaires à l'interface de programmation du matériel IOMMU pour garantir que les vérifications d'autorisation ne peuvent pas être contournées par d'autres moyens, par exemple après une réinitialisation d'appareil.
L'architecture de l'unité de gestion de la mémoire système (SMMU, System Memory Management Unit) Arm est un IOMMU standard et bien pris en charge pour les appareils Arm qui permet à la fois l'isolation et l'attribution directe. Il s'agit de la solution de référence recommandée.
Propriété de la mémoire
Au démarrage, toute mémoire autre que l'hyperviseur est supposée appartenir à l'hôte et est suivie en tant que telle par l'hyperviseur. Lorsqu'une pVM est créée, l'hôte donne des 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 des accès dans la table des pages de l'hôte de l'étape 2 pour l'empêcher d'accéder à nouveau aux pages, ce qui garantit la confidentialité à l'invité.
La communication entre l'hôte et les invités est rendue possible par un partage de mémoire contrôlé entre eux. Les invités sont autorisés à partager certaines de leurs pages avec l'hôte à l'aide d'un hyperappel, qui demande à l'hyperviseur de remapper ces pages dans la table 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, toutes surveillées et contrôlées de près par le pKVM à l'aide de la spécification Firmware Framework for Arm (FF-A).
Étant donné que les exigences de mémoire d'une pVM peuvent changer au fil du temps, une hyper-appel est fournie, ce qui permet de céder la propriété des pages spécifiées appartenant à l'appelant à l'hôte. En pratique, cette hyper-appel est utilisée avec le protocole de ballon virtio pour permettre au VMM de demander à la pVM de lui rendre de la mémoire et à la pVM de signaler au VMM les pages cédé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 de l'état est effectuée à l'aide de métadonnées associées aux tables de pages de l'hôte et des invités de l'étape 2, à l'aide de bits réservés dans les entrées de table de pages (PTE) qui, comme leur nom l'indique, sont réservés à l'utilisation par logiciel.
L'hôte doit s'assurer qu'il n'essaie pas d'accéder aux pages qui ont été rendues inaccessibles par l'hyperviseur. Un accès hôte illégal entraîne l'injection d'une exception synchrone dans l'hôte par l'hyperviseur, ce qui peut entraîner la réception d'un signal SEGV par la tâche d'espace utilisateur responsable ou le plantage du noyau hôte. Pour éviter tout accès accidentel, les pages données aux invités ne peuvent plus être échangées ni fusionnées par le noyau hôte.
Interrompre le traitement et les minuteurs
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 à cette fin comme une partie non approuvée de l'hyperviseur.
pKVM propose une émulation complète du contrôleur d'interruption générique (GICv3) basée sur le code KVM existant. Le minuteur et les IPI sont gérés dans le cadre de ce code d'émulation non approuvé.
Compatibilité avec GICv3
L'interface entre EL1 et EL2 doit s'assurer que l'état d'interruption complet est visible par l'hôte EL1, y compris les copies des registres de l'hyperviseur liées aux interruptions. Cette visibilité est généralement obtenue à l'aide de régions de mémoire partagées, une par processeur virtuel (vCPU).
Le code de prise en charge de l'environnement d'exécution du registre système peut être simplifié pour ne prendre en charge que le piégeage du registre SGIR (Software Generated Interrupt Register) et du registre d'interruption de désactivation (DIR). L'architecture exige que ces registres soient toujours piégés dans la méthode EL2, tandis que les autres pièges ne servent jusqu'à présent qu'à limiter les errata. Tout le reste est géré par le matériel.
Côté MMIO, tout est émulé à EL1, en réutilisant toute l'infrastructure actuelle dans KVM. Enfin, la fonctionnalité Wait for Interrupt (WFI) est toujours transmise à EL1, car il s'agit de l'une des primitives de planification de base utilisées par KVM.
Prise en charge des minuteurs
La valeur du comparateur pour le minuteur virtuel doit être exposée à EL1 sur chaque WFI de trappage afin qu'EL1 puisse injecter des interruptions de minuteur lorsque 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 l'émulation GIC, les pièges MMIO doivent être relayés vers l'hôte en EL1 pour un tri supplémentaire. pKVM nécessite les éléments suivants:
- IPA et taille de l'accès
- Données en cas d'écriture
- Finition du CPU au moment du piégeage
En outre, les interruptions avec un registre à usage général (GPR) en tant que source/destination sont relayées à l'aide d'un pseudo-registre de transfert abstrait.
Interfaces invité
Un invité peut communiquer avec un invité protégé à l'aide d'une combinaison d'hyperappels et d'un accès à la mémoire des régions piégées. Les hyperappels sont exposés conformément à la norme SMCCC, avec une plage réservée à l'allocation des fournisseurs par KVM. Les hyperappels 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 vCPU, y compris la mise en ligne, la mise hors service et l'arrêt du système.
- Le TRNG fournit un mécanisme standard permettant à l'invité de demander de l'entropie au pKVM, qui transfère l'appel à EL3. Ce mécanisme est particulièrement utile lorsque l'hôte ne peut pas être autorisé à 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 de l'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 appareils paravirtualisés qui reposent 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'un handshake.
- Renonciation de la mémoire à l'hôte. Toute la mémoire invitée appartient généralement à l'invité jusqu'à sa destruction. Cet état peut ne pas être adapté aux VM de longue durée dont les exigences de mémoire varient au fil du temps. L'hyperappel
relinquish
permet à un invité de transférer explicitement la propriété des pages à l'hôte sans avoir à le terminer. - Pièges d'accès à la mémoire sur 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 du processeur virtuel quitte l'hôte. L'accès est généralement utilisé pour MMIO et émulé par le VMM dans l'espace utilisateur. Pour faciliter cette gestion, pKVM doit annoncer des informations sur l'instruction défaillante, telles que son adresse, ses paramètres de registre et éventuellement leur contenu à l'hôte, ce qui pourrait exposer involontairement des données sensibles d'un invité protégé si le trap n'était pas prévu. pKVM résout ce problème en considérant ces erreurs comme fatales, sauf si l'invité a précédemment émis une hyper-appel pour identifier la plage d'adresses IPA défaillante comme celle pour laquelle les accès sont autorisés à renvoyer un trap à l'hôte. Cette solution est appelée défense MMIO.
Appareil d'E/S virtuel (virtio)
Virtio est une norme populaire, portable et mature pour implémenter et interagir avec des appareils paravirtualisés. La majorité des appareils exposés à des invités protégés sont implémentés à l'aide de virtio. Virtio est également à la base de l'implémentation de comparaison 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 à la mémoire piégés de l'invité à l'interface MMIO de l'appareil virtio et émule le comportement attendu. L'accès MMIO est relativement coûteux, car chaque accès à l'appareil nécessite un aller-retour vers le VMM et inversement. Par conséquent, la plupart du transfert de données réel entre l'appareil et l'invité se produit à l'aide d'un ensemble de virtqueues en mémoire. Une hypothèse clé de virtio est que l'hôte peut accéder de manière arbitraire à la mémoire invitée. 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 l'appareil est destiné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é à l'hôte, ce partage est nécessairement effectué au niveau de la page et peut exposer plus de données que nécessaire si la taille de la mémoire tampon est inférieure à celle d'une page. Au lieu de cela, l'invité est configuré pour allouer à la fois les files d'attente 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 (rejetées) vers et depuis la fenêtre selon les besoins.
Interaction avec TrustZone
Bien que les invités ne puissent pas interagir directement avec TrustZone, l'hôte doit toujours pouvoir émettre des appels SMC dans le monde sécurisé. Ces appels peuvent spécifier des tampons de mémoire adressés physiquement et qui sont inaccessibles à l'hôte. Étant donné que le logiciel sécurisé n'est généralement pas conscient de l'accessibilité du tampon, un hôte malveillant peut utiliser ce tampon pour effectuer une attaque par adjoint confus (analogue à une attaque DMA). Pour éviter de telles attaques, pKVM intercepte tous les appels SMC hôtes vers EL2 et agit en tant que proxy entre l'hôte et le moniteur sécurisé à EL3.
Les appels PSCI de l'hôte sont transférés au micrologiciel EL3 avec des modifications minimales. Plus précisément, le point d'entrée d'un processeur qui se met en ligne ou reprend après une suspension est réécrit afin que la table de pages de l'étape 2 soit installée à l'EL2 avant de revenir à l'hôte à l'EL1. Au démarrage, cette protection est appliquée par pKVM.
Cette architecture repose sur le SoC compatible avec PSCI, de préférence via l'utilisation d'une version à jour de TF-A comme micrologiciel EL3.
Le Framework de micrologiciel pour Arm (FF-A) standardise les interactions entre les mondes normal et sécurisé, en particulier 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é, à l'aide d'un format de message commun et d'un modèle d'autorisations bien défini pour les pages sous-jacentes. pKVM met en proxy les messages FF-A pour s'assurer 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 d'autorisations suffisantes.
Cette architecture repose sur le logiciel sécurisé appliquant le modèle d'accès à la mémoire, pour garantir que les applications approuvées et tout autre logiciel s'exécutant dans un environnement 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-A. Sur un système avec S-EL2, l'application du modèle d'accès à la mémoire doit être effectuée par un SPMC (Secure Partition Manager Core), tel que Hafnium, qui gère les tables de page de l'étape 2 pour le monde sécurisé. Sur un système sans S-EL2, le TEE peut appliquer un modèle d'accès à la mémoire via ses tables de pages d'étape 1.
Si l'appel SMC à EL2 n'est pas un appel PSCI ou un message défini par FF-A, les SMC non gérés sont transférées à EL3. On suppose que le micrologiciel sécurisé (nécessairement approuvé) peut gérer les SMC non gérées de manière sécurisée, car il comprend les précautions nécessaires pour maintenir l'isolation des 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 fait la particularité de crosvm est son accent mis sur la sécurité avec l'utilisation du langage de programmation Rust et d'un bac à sable autour des appareils virtuels pour protéger le noyau hôte. Pour en savoir plus sur crosvm, consultez sa documentation officielle.
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 des attributs globaux qui affectent l'ensemble du sous-système KVM, et créent des pVM.
- Les ioctls de VM interrogent et définissent des attributs qui créent des processeurs virtuels (vCPU) et des périphériques, et affectent une pVM entière, comme la mise en page de la mémoire et le nombre de processeurs virtuels (vCPU) et de périphériques.
- Les ioctls de vCPU interrogent et définissent des attributs qui contrôlent le fonctionnement d'un seul processeur virtuel.
- Les ioctls d'appareils interrogent et définissent des attributs qui contrôlent le fonctionnement d'un seul appareil virtuel.
Chaque processus crosvm exécute exactement une instance de machine virtuelle. Ce processus utilise l'ioctl du système KVM_CREATE_VM
pour créer un descripteur de fichier de VM pouvant être utilisé pour émettre des ioctls pVM. Un ioctl KVM_CREATE_VCPU
ou KVM_CREATE_DEVICE
sur un FD de VM crée un vCPU/un appareil et renvoie un descripteur de fichier pointant vers la nouvelle ressource. Les ioctls sur un vCPU ou un FD d'appareil peuvent être utilisés pour contrôler l'appareil créé à l'aide de l'ioctl sur un FD de VM. Pour les vCPU, cela inclut la tâche importante d'exécuter le code invité.
En interne, crosvm enregistre les descripteurs de fichier de la VM auprès du noyau à l'aide de l'interface epoll
déclenchée en périphérie. 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 permet d'obtenir des informations sur l'environnement de VM préemptives et de configurer le mode protégé pour une VM. crosvm l'utilise lors de la création de VM préemptives si l'indicateur --protected-vm
est transmis, pour interroger et réserver la quantité de mémoire appropriée au micrologiciel de VM préemptives, puis pour activer le mode protégé.
Allocation de mémoire
L'une des principales responsabilités d'un VMM consiste à allouer la mémoire de la VM et à gérer sa mise en page de mémoire. crosvm génère une mise en page de mémoire fixe décrite de manière approximative dans le tableau ci-dessous.
FDT en mode normal | PHYS_MEMORY_END - 0x200000
|
Libérer espace | ...
|
Ramdisk | ALIGN_UP(KERNEL_END, 0x1000000)
|
Noyau | 0x80080000
|
Bootloader (chargeur d'amorçage) | 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 de la pVM invitée est donc attribuée à l'instance crosvm qui la gère et peut entraîner l'arrêt du processus (arrêt de 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 de l'invité. Avec pKVM, la mémoire invitée n'est pas mappée à l'espace d'adressage physique de l'hôte lorsqu'elle est cédée à l'invité. La seule exception concerne la mémoire explicitement partagée en retour par l'invité, par exemple pour les appareils Virtio.
Les régions MMIO de l'espace d'adressage de l'invité ne sont pas mappées. L'accès de l'invité à ces régions est piégé et entraîne un événement d'E/S sur le FD de la VM. Ce mécanisme est utilisé pour implémenter des appareils virtuels. En mode protégé, l'invité doit confirmer qu'une région de son espace d'adressage est utilisée pour MMIO à l'aide d'une hyper-appel, afin de réduire le risque de fuite d'informations accidentelle.
Planification
Chaque processeur 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 FD du vCPU, ce qui entraîne le passage de l'hyperviseur au contexte du vCPU invité. Le planificateur hôte tient compte du temps passé dans un contexte invité en tant que temps utilisé par le thread de vCPU correspondant. KVM_RUN
est renvoyé lorsqu'un événement doit être géré par le VMM, tel que les E/S, la fin d'une interruption ou le processeur virtuel arrêté. Le VMM gère l'événement et appelle à nouveau KVM_RUN
.
Pendant KVM_RUN
, le thread reste préemptible par le planificateur hôte, sauf pour l'exécution du code de l'hyperviseur EL2, qui n'est pas préemptible. La pVM invitée elle-même ne dispose d'aucun mécanisme permettant de contrôler ce comportement.
Étant donné que tous les threads de vCPU sont planifiés comme n'importe quelle autre tâche d'espace utilisateur, ils sont soumis à tous les mécanismes QoS standards. Plus précisément, chaque thread de vCPU peut être affiné pour les processeurs physiques, placé dans des cpusets, boosté ou plafonné à l'aide d'un forçage de l'utilisation, avoir sa priorité/sa stratégie d'ordonnancement modifiée, etc.
Appareils virtuels
Crosvm est compatible avec un certain nombre d'appareils, y compris les suivants:
- virtio-blk pour les images de 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 (RTC) pl030
- UART 16550a pour la communication série
Micrologiciel pVM
Le micrologiciel pVM (pvmfw) est le premier code exécuté par une pVM, semblable à la mémoire ROM de démarrage d'un appareil physique. L'objectif principal de pvmfw est d'amorcer le démarrage sécurisé et de déduire son secret unique. pvmfw peut être utilisé avec un système d'exploitation spécifique, tel que Microdroid, tant que l'OS est correctement pris en charge et compatible avec pvmfw.
Le binaire pvmfw est stocké dans une partition Flash du même nom et mis à jour à l'aide d'une OTA.
Démarrage de l'appareil
La séquence d'étapes suivante est ajoutée à la procédure de démarrage d'un appareil compatible avec pKVM:
- Le bootloader Android (ABL) charge pvmfw à partir de sa partition dans la mémoire et vérifie l'image.
- L'ABL obtient ses secrets DICE (Device Identifier Composition Engine) (identifiants d'appareil composés et chaîne de certificats DICE) à partir d'une racine de confiance.
- L'ABL dérive les CDI nécessaires pour pvmfw et les ajoute au binaire pvmfw.
- 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. - ABL laisse le contrôle à Linux, et Linux initialise pKVM.
- pKVM annule le mappage de la région de mémoire pvmfw des tables des pages de l'étape 2 de l'hôte et le protège de l'hôte (et des invités) tout au long du temps d'activité de l'appareil.
Après le démarrage de l'appareil, Microdroid est démarré en suivant les étapes de la section Séquence de démarrage du document Microdroid.
Démarrage des VM préemptives
Lors de la création d'une pVM, crosvm (ou un autre VMM) doit créer un emplacement de mémoire suffisamment grand pour être rempli par l'image pvmfw par l'hyperviseur. Le VMM peut également figurer dans la liste des registres dont il peut définir la valeur initiale (x0-x14 pour le processeur virtuel principal, aucune pour les processeurs virtuels secondaires). Les autres registres sont réservés et font partie de l'ABI hyperviseur-pvmfw.
Lorsque la pVM est exécutée, l'hyperviseur transmet d'abord le contrôle du vCPU principal à pvmfw. Le micrologiciel s'attend à ce que crosvm ait chargé un kernel signé AVB, qui peut être un bootloader 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 un arbre d'appareils approuvé à partir du FDT reçu, efface ses secrets de la mémoire et se branche sur le point d'entrée de la charge utile. Si l'une des étapes de validation échoue, le micrologiciel génère une hyper-appel SYSTEM_RESET
PSCI.
Entre les démarrages, les informations sur l'instance de pVM sont stockées dans une partition (appareil virtio-blk) et chiffrées avec le secret de pvmfw pour s'assurer qu'après un redémarrage, le secret est provisionné sur la bonne instance.