Architettura AVF

Android fornisce un'implementazione di riferimento di tutti i componenti necessari per implementare Android Virtualization Framework. Attualmente questa implementazione è limitata a ARM64. Questa pagina spiega l'architettura del framework.

Sfondo

L'architettura Arm consente fino a quattro livelli di eccezione, di cui il livello di eccezione 0 (EL0) è il meno privilegiato e il livello di eccezione 3 (EL3) il massimo. La maggior parte della base di codice Android (tutti i componenti dello spazio utente) viene eseguita a EL0. Il resto di quello che viene comunemente chiamato "Android" è il kernel Linux, che gira su EL1.

Il livello EL2 consente l'introduzione di un hypervisor che consente di isolare memoria e dispositivi in ​​singoli pVM su EL1/EL0, con forti garanzie di riservatezza e integrità.

Hypervisor

La macchina virtuale protetta basata su kernel (pKVM) è basata sull'hypervisor KVM Linux , che è stato esteso con la capacità di limitare l'accesso ai payload in esecuzione nelle macchine virtuali guest contrassegnate come "protette" al momento della creazione.

KVM/arm64 supporta diverse modalità di esecuzione a seconda della disponibilità di alcune funzionalità della CPU, ovvero Virtualization Host Extensions (VHE) (ARMv8.1 e versioni successive). In una di queste modalità, comunemente nota come modalità non VHE, il codice dell'hypervisor viene separato dall'immagine del kernel durante l'avvio e installato su EL2, mentre il kernel stesso viene eseguito su EL1. Sebbene faccia parte della codebase Linux, il componente EL2 di KVM è un piccolo componente responsabile del passaggio tra più EL1 e interamente controllato dal kernel dell'host. Il componente hypervisor è compilato con Linux, ma risiede in una sezione di memoria dedicata separata dell'immagine vmlinux . pKVM sfrutta questo design estendendo il codice dell'hypervisor con nuove funzionalità che gli consentono di imporre restrizioni al kernel host Android e allo spazio utente e di limitare l'accesso dell'host alla memoria guest e all'hypervisor.

Moduli del fornitore pKVM

Un modulo del fornitore pKVM è un modulo specifico dell'hardware contenente funzionalità specifiche del dispositivo, come i driver dell'unità di gestione della memoria di input-output (IOMMU). Questi moduli consentono di trasferire funzionalità di sicurezza che richiedono l'accesso di livello di eccezione 2 (EL2) a pKVM.

Per informazioni su come implementare e caricare un modulo del fornitore pKVM, fare riferimento a Implementare un modulo del fornitore pKVM .

Procedura di avvio

La figura seguente illustra la procedura di avvio di pKVM:

Procedura di avvio pKVM

Figura 1. Procedura di avvio di pKVM

  1. Il bootloader entra nel kernel generico su EL2.
  2. Il kernel generico rileva che è in esecuzione su EL2 e si priva dei privilegi di EL1 mentre pKVM e i suoi moduli continuano a funzionare su EL2. Inoltre, in questo momento vengono caricati i moduli del fornitore pKVM.
  3. Il kernel generico procede all'avvio normalmente, caricando tutti i driver di dispositivo necessari fino a raggiungere lo spazio utente. A questo punto, pKVM è a posto e gestisce le tabelle delle pagine della fase 2.

La procedura di avvio si affida al bootloader per mantenere l'integrità dell'immagine del kernel solo durante l'avvio anticipato. Quando il kernel viene privato dei privilegi, non viene più considerato affidabile dall'hypervisor, che è quindi responsabile di proteggersi anche se il kernel è compromesso.

Avere il kernel Android e l'hypervisor nella stessa immagine binaria consente un'interfaccia di comunicazione molto strettamente accoppiata tra loro. Questo stretto accoppiamento garantisce aggiornamenti atomici dei due componenti, evitando la necessità di mantenere stabile l'interfaccia tra di loro e offre una grande flessibilità senza compromettere la manutenibilità a lungo termine. Lo stretto accoppiamento consente inoltre ottimizzazioni delle prestazioni quando entrambi i componenti possono cooperare senza influire sulle garanzie di sicurezza fornite dall'hypervisor.

Inoltre, l'adozione di GKI nell'ecosistema Android consente automaticamente la distribuzione dell'hypervisor pKVM sui dispositivi Android nello stesso binario del kernel.

Protezione dell'accesso alla memoria della CPU

L'architettura Arm specifica un'unità di gestione della memoria (MMU) divisa in due fasi indipendenti, entrambe utilizzabili per implementare la traduzione degli indirizzi e il controllo dell'accesso a diverse parti della memoria. La MMU dello stadio 1 è controllata da EL1 e consente un primo livello di traduzione degli indirizzi. La MMU fase 1 viene utilizzata da Linux per gestire lo spazio degli indirizzi virtuali fornito a ciascun processo dello spazio utente e al proprio spazio degli indirizzi virtuali.

La MMU dello stadio 2 è controllata da EL2 e consente l'applicazione di una seconda traduzione di indirizzo sull'indirizzo di uscita della MMU dello stadio 1, risultando in un indirizzo fisico (PA). La traduzione della fase 2 può essere utilizzata dagli hypervisor per controllare e tradurre gli accessi alla memoria da tutte le VM guest. Come mostrato nella figura 2, quando entrambe le fasi di traduzione sono abilitate, l'indirizzo di uscita dello stadio 1 è chiamato indirizzo fisico intermedio (IPA). Nota: l'indirizzo virtuale (VA) viene tradotto in un IPA e quindi in un PA.

Protezione dell'accesso alla memoria della CPU

Figura 2. Protezione dell'accesso alla memoria della CPU

Storicamente, KVM viene eseguito con la traduzione della fase 2 abilitata durante l'esecuzione dei guest e con la fase 2 disabilitata durante l'esecuzione del kernel Linux host. Questa architettura consente agli accessi alla memoria dalla MMU dello stadio 1 dell'host di passare attraverso la MMU dello stadio 2, consentendo quindi l'accesso illimitato dall'host alle pagine di memoria del guest. D'altra parte, pKVM abilita la protezione di fase 2 anche nel contesto host e affida all'hypervisor il compito di proteggere le pagine della memoria guest anziché l'host.

KVM sfrutta appieno la traduzione degli indirizzi nella fase 2 per implementare complesse mappature IPA/PA per gli ospiti, creando l'illusione di una memoria contigua per gli ospiti nonostante la frammentazione fisica. Tuttavia, l'utilizzo della MMU fase 2 per l'host è limitato al solo controllo degli accessi. Lo stadio 2 dell'host è mappato a livello di identità, garantendo che la memoria contigua nello spazio IPA dell'host sia contigua nello spazio PA. Questa architettura consente l'uso di mapping di grandi dimensioni nella tabella delle pagine e di conseguenza riduce la pressione sul translation lookaside buffer (TLB). Poiché una mappatura di identità può essere indicizzata da PA, la fase 2 dell'host viene utilizzata anche per tenere traccia della proprietà della pagina direttamente nella tabella delle pagine.

Protezione dall'accesso diretto alla memoria (DMA).

Come descritto in precedenza, rimuovere la mappatura delle pagine guest dall'host Linux nelle tabelle delle pagine della CPU è un passaggio necessario ma non sufficiente per proteggere la memoria guest. pKVM deve inoltre proteggere dagli accessi alla memoria effettuati da dispositivi compatibili con DMA sotto il controllo del kernel host e dalla possibilità di un attacco DMA avviato da un host dannoso. Per impedire a tale dispositivo di accedere alla memoria ospite, pKVM richiede l'hardware dell'unità di gestione della memoria di input-output (IOMMU) per ogni dispositivo compatibile con DMA nel sistema, come mostrato nella figura 3.

Protezione dell'accesso alla memoria DMA

Figura 3. Protezione dell'accesso alla memoria DMA

Come minimo, l'hardware IOMMU fornisce i mezzi per concedere e revocare l'accesso in lettura/scrittura per un dispositivo alla memoria fisica con granularità della pagina. Tuttavia, questo hardware IOMMU limita l'uso dei dispositivi nelle pVM poiché presuppongono una fase 2 con mappatura dell'identità.

Per garantire l'isolamento tra macchine virtuali, le transazioni di memoria generate per conto di diverse entità devono essere distinguibili da IOMMU in modo che l'insieme appropriato di tabelle di pagina possa essere utilizzato per la traduzione.

Inoltre, ridurre la quantità di codice specifico del SoC su EL2 è una strategia chiave per ridurre la base di calcolo affidabile (TCB) complessiva di pKVM e va contro l'inclusione dei driver IOMMU nell'hypervisor. Per mitigare questo problema, l'host di EL1 è responsabile delle attività ausiliarie di gestione dell'IOMMU, come la gestione dell'alimentazione, l'inizializzazione e, ove appropriato, la gestione delle interruzioni.

Tuttavia, affidare all'host il controllo dello stato del dispositivo impone requisiti aggiuntivi all'interfaccia di programmazione dell'hardware IOMMU per garantire che i controlli delle autorizzazioni non possano essere aggirati con altri mezzi, ad esempio in seguito al ripristino del dispositivo.

Un'IOMMU standard e ben supportata per i dispositivi Arm che rende possibile sia l'isolamento che l'assegnazione diretta è l'architettura SMMU (Arm System Memory Management Unit). Questa architettura è la soluzione di riferimento consigliata.

Proprietà della memoria

Al momento dell'avvio, si presuppone che tutta la memoria non dell'hypervisor sia di proprietà dell'host e viene tracciata come tale dall'hypervisor. Quando viene generata una pVM, l'host dona pagine di memoria per consentirne l'avvio e l'hypervisor trasferisce la proprietà di tali pagine dall'host alla pVM. Pertanto, l'hypervisor inserisce restrizioni di controllo dell'accesso nella tabella delle pagine della fase 2 dell'host per impedirgli di accedere nuovamente alle pagine, garantendo riservatezza al guest.

La comunicazione tra l'host e gli ospiti è resa possibile dalla condivisione controllata della memoria tra di loro. Gli ospiti possono condividere alcune delle loro pagine con l'host utilizzando un hypercall, che indica all'hypervisor di rimappare quelle pagine nella tabella delle pagine della fase 2 dell'host. Allo stesso modo, la comunicazione dell'host con TrustZone è resa possibile dalle operazioni di condivisione e/o prestito della memoria, tutte strettamente monitorate e controllate da pKVM utilizzando la specifica Firmware Framework for Arm (FF-A) .

Poiché i requisiti di memoria di una pVM possono cambiare nel tempo, viene fornita una hypercall che consente di restituire all'host la proprietà di pagine specifiche appartenenti al chiamante. In pratica questa hypercall viene utilizzata con il protocollo virtio balloon per consentire al VMM di richiedere memoria al pVM e al pVM di notificare al VMM le pagine cedute, in modo controllato.

L'hypervisor è responsabile del monitoraggio della proprietà di tutte le pagine di memoria nel sistema e se vengono condivise o prestate ad altre entità. La maggior parte di questo monitoraggio dello stato viene eseguito utilizzando metadati allegati alle tabelle delle pagine della fase 2 dell'host e degli ospiti, utilizzando bit riservati nelle voci della tabella delle pagine (PTE) che, come suggerisce il nome, sono riservati per l'uso del software.

L'host deve garantire di non tentare di accedere a pagine rese inaccessibili dall'hypervisor. Un accesso illegale all'host provoca l'inserimento di un'eccezione sincrona nell'host da parte dell'hypervisor, che può comportare la ricezione di un segnale SEGV da parte dell'attività dello spazio utente responsabile o l'arresto anomalo del kernel dell'host. Per prevenire accessi accidentali, le pagine donate agli ospiti vengono rese non idonee allo scambio o alla fusione da parte del kernel host.

Gestione degli interrupt e timer

Gli interrupt sono una parte essenziale del modo in cui un guest interagisce con i dispositivi e per la comunicazione tra CPU, dove gli interrupt interprocessore (IPI) sono il principale meccanismo di comunicazione. Il modello KVM consiste nel delegare tutta la gestione degli interrupt virtuali all'host in EL1, che a tale scopo si comporta come una parte non attendibile dell'hypervisor.

pKVM offre un'emulazione completa del Generic Interrupt Controller versione 3 (GICv3) basata sul codice KVM esistente. Il timer e gli IPI vengono gestiti come parte di questo codice di emulazione non attendibile.

Supporto GICv3

L'interfaccia tra EL1 ed EL2 deve garantire che l'intero stato dell'interruzione sia visibile all'host EL1, comprese le copie dei registri dell'hypervisor relativi agli interrupt. Questa visibilità viene in genere ottenuta utilizzando aree di memoria condivisa, una per CPU virtuale (vCPU).

Il codice di supporto runtime del registro di sistema può essere semplificato per supportare solo il trapping del registro SGIR (Software Generated Interrupt Register) e DIR (Deactivate Interrupt Register). L'architettura impone che questi registri siano sempre intrappolati su EL2, mentre le altre trappole finora sono state utili solo per mitigare gli errata. Tutto il resto viene gestito dall'hardware.

Dal lato MMIO, tutto è emulato su EL1, riutilizzando tutta l'attuale infrastruttura in KVM. Infine, Wait for Interrupt (WFI) viene sempre inoltrato a EL1, poiché questa è una delle primitive di pianificazione di base utilizzate da KVM.

Supporto del timer

Il valore del comparatore per il timer virtuale deve essere esposto a EL1 su ogni WFI di trapping in modo che EL1 possa inserire interruzioni del timer mentre la vCPU è bloccata. Il timer fisico è interamente emulato e tutte le trappole vengono trasmesse a EL1.

Gestione dell'MMIO

Per comunicare con il monitor della macchina virtuale (VMM) ed eseguire l'emulazione GIC, le trap MMIO devono essere inoltrate all'host in EL1 per ulteriori triage. pKVM richiede quanto segue:

  • IPA e dimensione dell'accesso
  • Dati in caso di scrittura
  • Endianness della CPU al punto di intrappolamento

Inoltre, le trappole con un registro di uso generale (GPR) come origine/destinazione vengono inoltrate utilizzando uno pseudo-registro di trasferimento astratto.

Interfacce ospiti

Un ospite può comunicare con un ospite protetto utilizzando una combinazione di hypercall e accesso alla memoria alle regioni intrappolate. Le hypercall sono esposte secondo lo standard SMCCC , con un intervallo riservato all'allocazione del fornitore da parte di KVM. Le seguenti hypercall sono di particolare importanza per gli ospiti pKVM.

Hypercall generiche

  • PSCI fornisce un meccanismo standard che consente al guest di controllare il ciclo di vita delle sue vCPU, inclusi online, offline e arresto del sistema.
  • TRNG fornisce un meccanismo standard affinché l'ospite richieda entropia dal pKVM che inoltra la chiamata a EL3. Questo meccanismo è particolarmente utile quando non è possibile considerare attendibile l'host per la virtualizzazione di un generatore di numeri casuali (RNG) hardware.

Hypercall pKVM

  • Condivisione della memoria con l'host. Tutta la memoria del guest è inizialmente inaccessibile all'host, ma l'accesso all'host è necessario per la comunicazione della memoria condivisa e per i dispositivi paravirtualizzati che si basano su buffer condivisi. Le hypercall per la condivisione e l'annullamento della condivisione delle pagine con l'host consentono all'ospite di decidere esattamente quali parti della memoria rendere accessibili al resto di Android senza la necessità di un handshake.
  • Cessione della memoria all'host. Tutta la memoria dell'ospite di solito appartiene all'ospite finché non viene distrutta. Questo stato può essere inadeguato per le macchine virtuali di lunga durata con requisiti di memoria che variano nel tempo. L'hypercall relinquish consente a un guest di trasferire esplicitamente la proprietà delle pagine all'host senza richiedere la terminazione del guest.
  • Trapping dell'accesso alla memoria all'host. Tradizionalmente, se un guest KVM accede a un indirizzo che non corrisponde a un'area di memoria valida, il thread vCPU esce nell'host e l'accesso viene generalmente utilizzato per MMIO ed emulato da VMM nello spazio utente. Per facilitare questa gestione, pKVM è tenuto a pubblicizzare i dettagli sull'istruzione in errore come il suo indirizzo, i parametri di registro e potenzialmente il loro contenuto all'host, il che potrebbe esporre involontariamente dati sensibili di un ospite protetto se la trappola non fosse stata anticipata. pKVM risolve questo problema trattando questi errori come fatali a meno che l'ospite non abbia precedentemente emesso un hypercall per identificare l'intervallo IPA in errore come quello per il quale è consentito agli accessi di risalire all'host. Questa soluzione viene definita protezione MMIO .

Dispositivo I/O virtuale (virtio)

Virtio è uno standard popolare, portabile e maturo per l'implementazione e l'interazione con dispositivi paravirtualizzati. La maggior parte dei dispositivi esposti agli ospiti protetti vengono implementati utilizzando virtio. Virtio è anche alla base dell'implementazione vsock utilizzata per la comunicazione tra un ospite protetto e il resto di Android.

I dispositivi Virtio sono generalmente implementati nello spazio utente dell'host dal VMM, che intercetta gli accessi alla memoria bloccati dal guest all'interfaccia MMIO del dispositivo virtio ed emula il comportamento previsto. L'accesso MMIO è relativamente costoso perché ogni accesso al dispositivo richiede un viaggio di andata e ritorno verso VMM e ritorno, quindi la maggior parte del trasferimento effettivo dei dati tra il dispositivo e il guest avviene utilizzando un set di code virtuali in memoria. Un presupposto chiave di virtio è che l'host possa accedere arbitrariamente alla memoria dell'ospite. Questo presupposto è evidente nella progettazione della coda virtuale, che potrebbe contenere puntatori ai buffer nel guest a cui l'emulazione del dispositivo intende accedere direttamente.

Sebbene le hypercall di condivisione della memoria descritte in precedenza possano essere utilizzate per condividere buffer di dati virtio dal guest all'host, questa condivisione viene necessariamente eseguita alla granularità della pagina e potrebbe finire per esporre più dati del necessario se la dimensione del buffer è inferiore a quella di una pagina . Invece, il guest è configurato per allocare sia le code virtuali che i corrispondenti buffer di dati da una finestra fissa di memoria condivisa, con i dati che vengono copiati (rimbalzati) da e verso la finestra come richiesto.

Dispositivo virtuale

Figura 4. Dispositivo Virtio

Interazione con TrustZone

Sebbene gli ospiti non siano in grado di interagire direttamente con TrustZone, l'host deve comunque essere in grado di effettuare chiamate SMC nel mondo protetto. Queste chiamate possono specificare buffer di memoria indirizzati fisicamente che sono inaccessibili all'host. Poiché il software sicuro generalmente non è a conoscenza dell'accessibilità del buffer, un host malintenzionato potrebbe utilizzare questo buffer per eseguire un attacco confuso (analogo a un attacco DMA). Per prevenire tali attacchi, pKVM intercetta tutte le chiamate SMC dell'host a EL2 e agisce come proxy tra l'host e il monitor sicuro su EL3.

Le chiamate PSCI dall'host vengono inoltrate al firmware EL3 con modifiche minime. Nello specifico, il punto di ingresso per una CPU che viene online o riprende dalla sospensione viene riscritto in modo che la tabella delle pagine della fase 2 venga installata su EL2 prima di tornare all'host su EL1. Durante l'avvio, queste protezioni vengono applicate da pKVM.

Questa architettura si basa sul SoC che supporta PSCI, preferibilmente attraverso l'uso di una versione aggiornata di TF-A come firmware EL3.

Firmware Framework for Arm (FF-A) standardizza le interazioni tra il mondo normale e quello sicuro, in particolare in presenza di un hypervisor sicuro. Una parte importante delle specifiche definisce un meccanismo per condividere la memoria con il mondo sicuro, utilizzando sia un formato di messaggio comune che un modello di autorizzazioni ben definito per le pagine sottostanti. pKVM invia tramite proxy i messaggi FF-A per garantire che l'host non stia tentando di condividere memoria con il lato sicuro per il quale non dispone di autorizzazioni sufficienti.

Questa architettura si basa sul software del mondo sicuro che applica il modello di accesso alla memoria, per garantire che le app attendibili e qualsiasi altro software in esecuzione nel mondo sicuro possano accedere alla memoria solo se è di proprietà esclusiva del mondo sicuro o è stato esplicitamente condiviso con esso utilizzando FF -UN. Su un sistema con S-EL2, l'applicazione del modello di accesso alla memoria dovrebbe essere eseguita da un Secure Partition Manager Core (SPMC), come Hafnium , che mantiene le tabelle delle pagine della fase 2 per il mondo sicuro. Su un sistema senza S-EL2, il TEE può invece imporre un modello di accesso alla memoria attraverso le tabelle delle pagine della fase 1.

Se la chiamata SMC a EL2 non è una chiamata PSCI o un messaggio definito FF-A, gli SMC non gestiti vengono inoltrati a EL3. Il presupposto è che il firmware sicuro (necessariamente affidabile) possa gestire in modo sicuro gli SMC non gestiti perché comprende le precauzioni necessarie per mantenere l'isolamento pVM.

Monitoraggio della macchina virtuale

crosvm è un monitor di macchine virtuali (VMM) che esegue macchine virtuali tramite l'interfaccia KVM di Linux. Ciò che rende unico crosvm è la sua attenzione alla sicurezza con l'uso del linguaggio di programmazione Rust e un sandbox attorno ai dispositivi virtuali per proteggere il kernel host. Per ulteriori informazioni su crosvm, consulta la documentazione ufficiale qui .

Descrittori di file e ioctl

KVM espone il dispositivo a caratteri /dev/kvm nello spazio utente con ioctl che costituiscono l'API KVM. Gli ioctl appartengono alle seguenti categorie:

  • Gli ioctl di sistema interrogano e impostano attributi globali che influenzano l'intero sottosistema KVM e creano pVM.
  • Gli ioctl delle VM interrogano e impostano gli attributi che creano CPU virtuali (vCPU) e dispositivi e influiscono su un'intera pVM, ad esempio includendo il layout della memoria e il numero di CPU virtuali (vCPU) e dispositivi.
  • Gli ioctl vCPU eseguono query e impostano attributi che controllano il funzionamento di una singola CPU virtuale.
  • Gli ioctl del dispositivo interrogano e impostano gli attributi che controllano il funzionamento di un singolo dispositivo virtuale.

Ogni processo crosvm esegue esattamente un'istanza di una macchina virtuale. Questo processo utilizza l'ioctl del sistema KVM_CREATE_VM per creare un descrittore di file VM che può essere utilizzato per emettere ioctl pVM. Un ioctl KVM_CREATE_VCPU o KVM_CREATE_DEVICE su un FD VM crea un vCPU/dispositivo e restituisce un descrittore di file che punta alla nuova risorsa. ioctl su una vCPU o un FD del dispositivo può essere utilizzato per controllare il dispositivo creato utilizzando ioctl su un FD della VM. Per le vCPU, ciò include l'importante compito di eseguire il codice guest.

Internamente, crosvm registra i descrittori di file della VM con il kernel utilizzando l'interfaccia epoll attivata da edge. Il kernel quindi avvisa crosvm ogni volta che c'è un nuovo evento in sospeso in uno qualsiasi dei descrittori di file.

pKVM aggiunge una nuova funzionalità, KVM_CAP_ARM_PROTECTED_VM , che può essere utilizzata per ottenere informazioni sull'ambiente pVM e impostare la modalità protetta per una VM. crosvm lo utilizza durante la creazione della pVM se viene passato il flag --protected-vm , per interrogare e riservare la quantità appropriata di memoria per il firmware pVM e quindi per abilitare la modalità protetta.

Allocazione della memoria

Una delle responsabilità principali di una VMM è l'allocazione della memoria della VM e la gestione del layout della memoria. crosvm genera un layout di memoria fisso vagamente descritto nella tabella seguente.

FDT in modalità normale PHYS_MEMORY_END - 0x200000
Spazio libero ...
Ramdisk ALIGN_UP(KERNEL_END, 0x1000000)
Nocciolo 0x80080000
Boot loader 0x80200000
FDT in modalità BIOS 0x80000000
Base di memoria fisica 0x80000000
firmware pVM 0x7FE00000
Memoria del dispositivo 0x10000 - 0x40000000

La memoria fisica viene allocata con mmap e la memoria viene donata alla VM per popolare le sue regioni di memoria, chiamate memslots , con l'ioctl KVM_SET_USER_MEMORY_REGION . Tutta la memoria pVM del guest viene quindi attribuita all'istanza crosvm che la gestisce e può comportare la chiusura del processo (terminazione della VM) se l'host inizia a esaurire la memoria libera. Quando una VM viene arrestata, la memoria viene automaticamente cancellata dall'hypervisor e restituita al kernel host.

In KVM normale, VMM mantiene l'accesso a tutta la memoria guest. Con pKVM, la memoria guest non viene mappata dallo spazio degli indirizzi fisici dell'host quando viene donata al guest. L'unica eccezione è la memoria esplicitamente condivisa dall'ospite, come per i dispositivi virtio.

Le regioni MMIO nello spazio degli indirizzi dell'ospite vengono lasciate non mappate. L'accesso a queste regioni da parte del guest viene bloccato e determina un evento I/O sull'FD della VM. Questo meccanismo viene utilizzato per implementare i dispositivi virtuali. In modalità protetta, il guest deve riconoscere che una regione del suo spazio di indirizzi viene utilizzata per MMIO utilizzando un hypercall, per ridurre il rischio di fuga accidentale di informazioni.

Pianificazione

Ogni CPU virtuale è rappresentata da un thread POSIX e pianificata dallo scheduler Linux dell'host. Il thread chiama l'ioctl KVM_RUN sull'FD vCPU, facendo sì che l'hypervisor passi al contesto vCPU guest. Lo scheduler dell'host contabilizza il tempo trascorso in un contesto guest come tempo utilizzato dal thread vCPU corrispondente. KVM_RUN restituisce quando si verifica un evento che deve essere gestito da VMM, ad esempio I/O, fine dell'interruzione o arresto della vCPU. Il VMM gestisce l'evento e chiama nuovamente KVM_RUN .

Durante KVM_RUN , il thread rimane prerilasciabile dallo scheduler dell'host, ad eccezione dell'esecuzione del codice dell'hypervisor EL2, che non è prerilasciabile. La stessa pVM guest non dispone di alcun meccanismo per controllare questo comportamento.

Poiché tutti i thread vCPU sono pianificati come qualsiasi altra attività dello spazio utente, sono soggetti a tutti i meccanismi QoS standard. Nello specifico, ogni thread vCPU può essere affinato a CPU fisiche, inserito in cpuset, potenziato o limitato utilizzando il blocco dell'utilizzo, modificarne la politica di priorità/pianificazione e altro ancora.

Dispositivi virtuali

crosvm supporta numerosi dispositivi, inclusi i seguenti:

  • virtio-blk per immagini di dischi compositi, di sola lettura o lettura-scrittura
  • vhost-vsock per la comunicazione con l'host
  • virtio-pci come trasporto virtio
  • pl030 orologio in tempo reale (RTC)
  • 16550a UART per la comunicazione seriale

firmware pVM

Il firmware pVM (pvmfw) è il primo codice eseguito da un pVM, simile alla ROM di avvio di un dispositivo fisico. L'obiettivo principale di pvmfw è eseguire il bootstrap dell'avvio sicuro e derivare il segreto univoco di pVM. pvmfw non è limitato all'uso con qualsiasi sistema operativo specifico, come Microdroid , purché il sistema operativo sia supportato da crosvm e sia stato firmato correttamente.

Il binario pvmfw è archiviato in una partizione flash con lo stesso nome e viene aggiornato tramite OTA .

Avvio del dispositivo

La seguente sequenza di passaggi viene aggiunta alla procedura di avvio di un dispositivo abilitato per pKVM:

  1. Il Bootloader Android (ABL) carica pvmfw dalla sua partizione in memoria e verifica l'immagine.
  2. L'ABL ottiene i segreti del Device Identifier Composition Engine (DICE) (Compound Device Identifiers (CDI) e Boot Certificate Chain (BCC)) da una root of trust.
  3. L'ABL esegue la misurazione e la derivazione DICE dei segreti di pvmfw (CDI) e li aggiunge al file binario pvmfw.
  4. L'ABL aggiunge un nodo della regione di memoria riservata linux,pkvm-guest-firmware-memory al DT, descrivendo la posizione e la dimensione del binario pvmfw e i segreti che ha derivato nel passaggio precedente.
  5. L'ABL cede il controllo a Linux e Linux inizializza pKVM.
  6. pKVM rimuove la mappatura della regione di memoria pvmfw dalle tabelle delle pagine della fase 2 dell'host e la protegge dall'host (e dagli ospiti) durante il tempo di attività del dispositivo.

Dopo l'avvio del dispositivo, Microdroid viene avviato seguendo i passaggi nella sezione Sequenza di avvio del documento Microdroid .

avvio pVM

Quando si crea un pVM, crosvm (o un altro VMM) deve creare un memslot sufficientemente grande da essere popolato con l'immagine pvmfw dall'hypervisor. La VMM è inoltre vincolata nella lista dei registri di cui può impostare il valore iniziale (x0-x14 per la vCPU primaria, nessuno per le vCPU secondarie). I restanti registri sono riservati e fanno parte dell'ABI hypervisor-pvmfw.

Quando viene eseguito il pVM, l'hypervisor cede prima il controllo della vCPU primaria a pvmfw. Il firmware prevede che crosvm abbia caricato un kernel firmato AVB, che può essere un bootloader o qualsiasi altra immagine, e un FDT non firmato in memoria con offset noti. pvmfw convalida la firma AVB e, in caso di successo, genera un albero di dispositivi attendibili dall'FDT ricevuto, cancella i suoi segreti dalla memoria e si dirige al punto di ingresso del payload. Se uno dei passaggi di verifica fallisce, il firmware emette una hypercall PSCI SYSTEM_RESET .

Tra un avvio e l'altro, le informazioni sull'istanza pVM vengono archiviate in una partizione (dispositivo virtio-blk) e crittografate con il segreto di pvmfw per garantire che, dopo un riavvio, il segreto venga fornito all'istanza corretta.