Architecture FAV

Android fournit une implémentation de référence de tous les composants nécessaires pour implémenter le cadre 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 à EL0. Le reste de ce qu'on appelle communément "Android" est le noyau Linux, qui tourne en EL1.

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

Hyperviseur

La machine virtuelle basée sur le noyau protégé (pKVM) est basée sur l' hyperviseur KVM Linux , qui a été étendu avec la possibilité de restreindre l'accès aux charges utiles exécutées sur les machines virtuelles invitées marquées comme « 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.

Procédure de démarrage

La procédure de démarrage pKVM est illustrée à la figure 1. La première étape consiste pour le chargeur de démarrage à entrer un noyau Linux compatible pKVM à EL2. Lors du démarrage précoce, le noyau détecte qu'il s'exécute à EL2, se prive de EL1, laissant pKVM derrière lui. À partir de ce moment, le noyau Linux démarre normalement, en chargeant tous les pilotes de périphériques nécessaires, jusqu'à atteindre l'espace utilisateur. Ces étapes se produisent sous le contrôle de pKVM.

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 du démarrage précoce. Lorsque le noyau est privé de privilèges, il n'est plus considéré comme fiable par l'hyperviseur, qui est alors chargé de se protéger même si le noyau est compromis.

Procédure de démarrage pKVM

Figure 1. Procédure de démarrage pKVM

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 de maintenir stable 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 à l'hyperviseur pKVM d'être déployé sur les appareils Android dans le même binaire que le noyau.

Protection d'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 d'accès à différentes parties de la mémoire. La MMU de l'étage 1 est contrôlée par EL1 et permet un premier niveau de traduction d'adresse. L'étape 1 MMU 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 translation 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 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 d'accès à la mémoire du processeur

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

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 mémoire de 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 de l'invité. D'autre part, 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é au lieu de l'hôte.

KVM utilise 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. Cependant, l'utilisation de la MMU de niveau 2 pour l'hôte est limitée au contrôle d'accès uniquement. L'étage hôte 2 est mappé 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 grands mappages dans la table des pages et réduit par conséquent la pression sur le tampon de recherche 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é de la page directement dans la table des pages.

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

Comme décrit précédemment, le démappage des pages invitées de l'hôte Linux dans les tables de pages CPU 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 mémoire effectués par des périphériques compatibles DMA sous le contrôle du noyau hôte, et contre la possibilité d'une attaque DMA initiée par un hôte malveillant. Pour empêcher un tel appareil d'accéder à la mémoire invité, pKVM nécessite un matériel d'unité de gestion de mémoire d'entrée-sortie (IOMMU) pour chaque appareil compatible DMA du système, comme illustré à la figure 3.

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

Figure 3. Protection d'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é de la page. 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 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 pouvoir être distinguées 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 à 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 à EL1 est responsable des tâches de gestion IOMMU auxiliaires, telles que la gestion de l'alimentation, l'initialisation et, le cas échéant, la gestion des interruptions.

Cependant, le fait de confier à l'hôte le contrôle de l'état de l'appareil 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 de l'appareil.

L'architecture Arm System Memory Management Unit (SMMU) est un IOMMU standard et bien pris en charge pour les périphériques 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 démarrage, toute la mémoire non hyperviseur 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 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 d'accès dans la table des pages de l'étape 2 de l'hébergeur pour l'empêcher d'accéder à nouveau aux pages, assurant 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 le partage de mémoire et/ou les opérations de prêt, qui sont toutes étroitement surveillées et contrôlées par pKVM à l'aide de la spécification Firmware Framework for Arm (FF-A) .

L'hyperviseur est chargé de suivre la propriété de toutes les pages 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 page 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 page (PTE) qui, comme leur nom l'indique, sont réservées à l'utilisation du logiciel.

L'hébergeur doit s'assurer qu'il ne tente pas d'accéder à des pages rendues inaccessibles par l'hyperviseur. Un accès illégal à l'hôte 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 de l'hôte. Pour éviter les accès accidentels, les pages données aux invités sont rendues inadmissibles à l'échange ou à la fusion par le noyau hôte.

Gestion des interruptions et minuteries

Les interruptions sont une partie essentielle de la façon dont un invité interagit avec les périphériques et pour la communication entre les processeurs, où les interruptions interprocesseurs (IPI) sont 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 à cet effet se comporte comme une partie non fiable de l'hyperviseur.

pKVM offre une émulation complète du 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 de GICv3

L'interface entre EL1 et EL2 doit garantir que l'état d'interruption complet 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 virtuelle (vCPU).

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

Côté MMIO, tout est émulé en EL1, réutilisant toute l'infrastructure actuelle en KVM. Enfin, Wait for Interrupt (WFI) est toujours relayé à 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 de comparateur pour le temporisateur virtuel doit être exposée à EL1 sur chaque WFI de piégeage afin que EL1 puisse injecter des interruptions de temporisateur pendant que le vCPU est bloqué. Le temporisateur physique est entièrement émulé et tous les déroutements sont relayés à 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 triage ultérieur. pKVM requiert 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é

Un invité peut communiquer avec un invité protégé en utilisant une combinaison d'hyperappels et d'accès mémoire aux régions piégées. Les hypercalls sont exposés selon la norme 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 vCPU, 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 l'entropie au pKVM qui relaie l'appel à 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 matériel (RNG).

Hyperappels pKVM

  • Partage de mémoire avec l'hôte. Toute la mémoire invitée 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 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'une poignée de main.
  • Interception de l'accès mémoire à 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 les détails de l'instruction défaillante tels que son adresse, les paramètres d'enregistrement et potentiellement leur contenu à l'hôte, ce qui pourrait involontairement exposer des données sensibles d'un invité protégé si le piège n'était pas anticipé. pKVM résout ce problème en traitant ces erreurs comme fatales à moins que l'invité n'ait précédemment émis un hyperappel pour identifier la plage d'IPA défectueuse comme une plage pour laquelle les accès sont autorisés à revenir à l'hôte. Cette solution est appelée 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 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 retour, de sorte que la plupart des transferts de données réels entre l'appareil et l'invité se produisent à l'aide d'un ensemble de virtqueues en mémoire. Une hypothèse clé de virtio est que l'hôte peut accéder arbitrairement à la mémoire de l'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é à l'hôte, ce partage est nécessairement effectué à 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.

Périphérique 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 qui sont inaccessibles à l'hôte. Comme 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é à 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 CPU entrant en ligne ou reprenant après une suspension est réécrit de sorte que la table de pages de l'étape 2 soit installée à EL2 avant de retourner à l'hôte à EL1. Au 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 en tant que micrologiciel 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 partie importante 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 proxie 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 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 d'étape 1.

Si l'appel SMC vers EL2 n'est pas un appel PSCI ou un message défini 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 en toute sécurité les SMC non gérés, 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.

Descripteurs de fichiers et ioctls

KVM expose le périphérique de caractères /dev/kvm à l'espace utilisateur avec des ioctls qui composent 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 CPU virtuelles (vCPU) et des périphériques, et affectent une pVM entière, comme l'inclusion de la disposition de la mémoire et le nombre de CPU virtuelles (vCPU) et de périphériques.
  • vCPU ioctls interroge et définit les attributs qui contrôlent le fonctionnement d'un seul CPU 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 ioctl pVM. Un ioctl KVM_CREATE_VCPU ou KVM_CREATE_DEVICE sur un VM FD crée un vCPU/périphérique et renvoie un descripteur de fichier pointant vers la nouvelle ressource. ioctls sur un vCPU ou un périphérique FD peut être utilisé pour contrôler le périphérique qui a été créé à l'aide de ioctl sur un VM FD. Pour les vCPU, cela inclut la tâche importante d'exécution du code invité.

En interne, crosvm enregistre les descripteurs de fichiers de la machine virtuelle avec le noyau à l'aide de l'interface epoll déclenchée par le bord. Le noyau notifie ensuite crosvm chaque fois qu'un nouvel événement est en attente dans l'un des descripteurs de fichier.

pKVM ajoute une nouvelle capacité, 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' --protected-vm est passé, pour interroger et réserver la quantité de mémoire appropriée 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 machine virtuelle et de gérer sa disposition de mémoire. crosvm génère une disposition de mémoire fixe décrite de manière approximative dans le tableau ci-dessous.

FDT en mode normal PHYS_MEMORY_END - 0x200000
Espace libre ...
Disque virtuel ALIGN_UP(KERNEL_END, 0x1000000)
Noyau 0x80080000
Chargeur de démarrage 0x80200000
FDT en mode BIOS 0x80000000
Socle 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 machine virtuelle 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 (arrêt de la VM) si l'hôte commence à manquer de mémoire libre. Lorsqu'une machine virtuelle 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 de l'invité est dé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 bloqué et entraîne un événement d'E/S sur le 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 le MMIO à l'aide d'un hyperappel, afin de réduire le risque de fuite accidentelle d'informations.

Planification

Chaque CPU virtuelle est représentée par un thread POSIX et planifiée 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 hôte comptabilise le temps passé dans un contexte d'invité comme temps utilisé par le thread vCPU correspondant. KVM_RUN renvoyé lorsqu'un événement doit être géré par le VMM, tel qu'une E/S, une fin d'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 n'importe quelle autre tâche de l'espace utilisateur, ils sont soumis à tous les mécanismes QoS standard. Plus précisément, chaque thread vCPU peut être affiné à des CPU physiques, placé dans des cpusets, boosté ou plafonné à l'aide du blocage de l'utilisation, voir sa politique de priorité/planification modifiée, etc.

Périphériques virtuels

crosvm prend en charge un certain nombre d'appareils, dont 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
  • pl030 horloge en temps réel (RTC)
  • 16550a UART pour 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 de la 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 à l'aide d' 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 de sa partition en mémoire et vérifie l'image.
  2. L'ABL obtient ses secrets Device Identifier Composition Engine (DICE) (identificateurs de périphérique composés (CDI) et chaîne de certificats 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 à la 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 passe le contrôle à Linux et Linux initialise pKVM.
  6. pKVM démappe la région de 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'une pVM, crosvm (ou une 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 primaire, aucun pour les vCPU secondaires). Les registres restants sont réservés et font partie de l'ABI hyperviseur-pvmfw.

Lorsque la pVM est exécutée, l'hyperviseur passe 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 de confiance à partir du FDT reçu, efface ses secrets de la mémoire et se branche au 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 provisionné sur la bonne instance.