Android zapewnia referencyjną implementację wszystkich składników potrzebnych do wdrożenia Android Virtualization Framework. Obecnie ta implementacja jest ograniczona do ARM64. Ta strona wyjaśnia architekturę ramową.
Tło
Architektura Arm dopuszcza do czterech poziomów wyjątków, przy czym poziom wyjątku 0 (EL0) jest najmniej uprzywilejowany, a poziom wyjątku 3 (EL3) najbardziej. Największa część bazy kodu Androida (wszystkie komponenty przestrzeni użytkownika) działa na poziomie EL0. Reszta tego, co powszechnie nazywa się „Androidem”, to jądro Linuksa, które działa w EL1.
Warstwa EL2 umożliwia wprowadzenie hiperwizora, który umożliwia izolację pamięci i urządzeń w poszczególnych pVM na poziomie EL1/EL0, z silnymi gwarancjami poufności i integralności.
Hypervisor
Chroniona maszyna wirtualna oparta na jądrze (pKVM) jest zbudowana na hiperwizorze Linux KVM , który został rozszerzony o możliwość ograniczenia dostępu do ładunków działających na gościnnych maszynach wirtualnych oznaczonych jako „chronione” w momencie tworzenia.
KVM/arm64 obsługuje różne tryby wykonywania w zależności od dostępności niektórych funkcji procesora, a mianowicie Virtualization Host Extensions (VHE) (ARMv8.1 i nowsze). W jednym z tych trybów, powszechnie znanym jako tryb inny niż VHE, kod hiperwizora jest wydzielany z obrazu jądra podczas rozruchu i instalowany w EL2, podczas gdy samo jądro działa w EL1. Chociaż jest częścią bazy kodu Linuksa, komponent EL2 KVM jest małym komponentem odpowiedzialnym za przełączanie między wieloma EL1 i całkowicie kontrolowanym przez jądro hosta. Komponent hypervisor jest kompilowany z Linuksem, ale znajduje się w oddzielnej, dedykowanej sekcji pamięci obrazu vmlinux
. pKVM wykorzystuje ten projekt, rozszerzając kod hiperwizora o nowe funkcje, które pozwalają mu nakładać ograniczenia na jądro hosta Androida i przestrzeń użytkownika oraz ograniczać dostęp hosta do pamięci gościa i hiperwizora.
Procedura uruchamiania
Procedura rozruchu pKVM jest przedstawiona na rysunku 1. Pierwszym krokiem jest wprowadzenie przez program ładujący jądra Linuksa obsługującego pKVM w EL2. Podczas wczesnego rozruchu jądro wykrywa, że działa w EL2, pozbawia się uprawnień do EL1, pozostawiając pKVM. Od tego momentu jądro Linuksa uruchamia się normalnie, ładując wszystkie niezbędne sterowniki urządzeń, aż do osiągnięcia przestrzeni użytkownika. Te kroki mają miejsce pod kontrolą pKVM.
Procedura rozruchu ufa programowi ładującemu, że zachowa integralność obrazu jądra tylko podczas wczesnego rozruchu. Kiedy jądro jest pozbawione uprawnień, nie jest już uważane za zaufane przez hiperwizora, który jest wówczas odpowiedzialny za ochronę samego siebie, nawet jeśli jądro zostanie naruszone.
Posiadanie jądra Androida i hiperwizora w tym samym obrazie binarnym pozwala na bardzo ściśle powiązany interfejs komunikacyjny między nimi. To ścisłe powiązanie gwarantuje atomowe aktualizacje dwóch komponentów, co pozwala uniknąć konieczności utrzymywania stabilnego interfejsu między nimi i zapewnia dużą elastyczność bez uszczerbku dla długoterminowej łatwości konserwacji. Ścisłe sprzężenie umożliwia również optymalizację wydajności, gdy oba komponenty mogą współpracować bez wpływu na gwarancje bezpieczeństwa zapewniane przez hiperwizora.
Co więcej, przyjęcie GKI w ekosystemie Androida automatycznie umożliwia wdrożenie hiperwizora pKVM na urządzeniach z Androidem w tym samym pliku binarnym co jądro.
Ochrona dostępu do pamięci procesora
Architektura Arm określa jednostkę zarządzania pamięcią (MMU) podzieloną na dwa niezależne etapy, z których oba mogą być użyte do implementacji translacji adresów i kontroli dostępu do różnych części pamięci. Jednostka MMU etapu 1 jest kontrolowana przez EL1 i umożliwia pierwszy poziom translacji adresów. Jednostka MMU etapu 1 jest używana przez system Linux do zarządzania wirtualną przestrzenią adresową udostępnianą każdemu procesowi w przestrzeni użytkownika i jego własnej wirtualnej przestrzeni adresowej.
MMU stopnia 2 jest sterowane przez EL2 i umożliwia zastosowanie drugiej translacji adresu na adresie wyjściowym MMU stopnia 1, co daje adres fizyczny (PA). Translacja etapu 2 może być używana przez hiperwizory do kontrolowania i tłumaczenia dostępów do pamięci ze wszystkich maszyn wirtualnych-gości. Jak pokazano na rysunku 2, gdy włączone są oba etapy translacji, adres wyjściowy etapu 1 nazywany jest pośrednim adresem fizycznym (IPA). Uwaga: Adres wirtualny (VA) jest tłumaczony na IPA, a następnie na PA.
Historycznie rzecz biorąc, KVM działa z włączoną translacją etapu 2 podczas uruchamiania gości i z wyłączoną fazą 2 podczas uruchamiania jądra systemu Linux hosta. Ta architektura umożliwia dostęp do pamięci z MMU etapu 1 hosta przez MMU etapu 2, umożliwiając w ten sposób nieograniczony dostęp od hosta do stron pamięci gościa. Z drugiej strony pKVM umożliwia ochronę poziomu 2 nawet w kontekście hosta i powierza hiperwizorowi zadanie ochrony stron pamięci gości zamiast hosta.
KVM w pełni wykorzystuje translację adresów na etapie 2 do implementacji złożonych mapowań IPA/PA dla gości, co stwarza dla gości iluzję ciągłej pamięci pomimo fizycznej fragmentacji. Jednak użycie MMU etapu 2 dla hosta jest ograniczone tylko do kontroli dostępu. Etap hosta 2 jest mapowany tożsamościowo, zapewniając, że ciągła pamięć w przestrzeni IPA hosta jest ciągła w przestrzeni PA. Taka architektura umożliwia stosowanie dużych mapowań w tablicy stron, aw konsekwencji zmniejsza obciążenie bufora translacji (TLB). Ponieważ mapowanie tożsamości może być indeksowane przez PA, etap hosta 2 jest również używany do śledzenia własności strony bezpośrednio w tablicy stron.
Ochrona bezpośredniego dostępu do pamięci (DMA).
Jak opisano wcześniej, usunięcie mapowania stron gościa z hosta Linux w tablicach stron procesora jest koniecznym, ale niewystarczającym krokiem do ochrony pamięci gościa. pKVM musi również chronić przed dostępem do pamięci dokonywanym przez urządzenia obsługujące DMA pod kontrolą jądra hosta oraz możliwością ataku DMA zainicjowanego przez złośliwego hosta. Aby uniemożliwić takiemu urządzeniu dostęp do pamięci gościa, pKVM wymaga sprzętu do zarządzania pamięcią wejścia-wyjścia (IOMMU) dla każdego urządzenia obsługującego DMA w systemie, jak pokazano na rysunku 3.
Jako minimum sprzęt IOMMU zapewnia środki do przyznawania i odbierania dostępu do odczytu/zapisu urządzenia do pamięci fizycznej na poziomie szczegółowości strony. Jednak ten sprzęt IOMMU ogranicza użycie urządzeń w pVM, ponieważ zakładają one etap 2 mapowania tożsamości.
Aby zapewnić izolację między maszynami wirtualnymi, transakcje pamięci generowane w imieniu różnych podmiotów muszą być rozróżnialne przez IOMMU, tak aby można było użyć odpowiedniego zestawu tablic stron do tłumaczenia.
Ponadto zmniejszenie ilości kodu specyficznego dla SoC w EL2 jest kluczową strategią zmniejszania ogólnej zaufanej bazy obliczeniowej (TCB) pKVM i jest sprzeczne z włączeniem sterowników IOMMU do hiperwizora. Aby złagodzić ten problem, host w EL1 jest odpowiedzialny za pomocnicze zadania zarządzania IOMMU, takie jak zarządzanie zasilaniem, inicjalizacja i, w stosownych przypadkach, obsługa przerwań.
Jednak przekazanie hostowi kontroli nad stanem urządzenia nakłada dodatkowe wymagania na interfejs programistyczny sprzętu IOMMU, aby zapewnić, że kontroli uprawnień nie można ominąć w inny sposób, na przykład po zresetowaniu urządzenia.
Standardowym i dobrze obsługiwanym IOMMU dla urządzeń Arm, który umożliwia zarówno izolację, jak i bezpośrednie przypisanie, jest architektura Arm System Memory Management Unit (SMMU). Ta architektura jest zalecanym rozwiązaniem referencyjnym.
Własność pamięci
W czasie rozruchu zakłada się, że cała pamięć nienależąca do hiperwizora należy do hosta i jako taka jest śledzona przez hiperwizora. Kiedy pVM jest tworzony, host przekazuje strony pamięci, aby umożliwić jego uruchomienie, a hiperwizor przenosi własność tych stron z hosta na pVM. W ten sposób hiperwizor nakłada ograniczenia kontroli dostępu w tabeli stron etapu 2 hosta, aby uniemożliwić mu ponowne uzyskanie dostępu do stron, zapewniając gościowi poufność.
Komunikacja między gospodarzem a gośćmi jest możliwa dzięki kontrolowanemu współdzieleniu pamięci między nimi. Goście mogą ponownie udostępnić hostowi niektóre ze swoich stron za pomocą hiperwywołania, które nakazuje hiperwizorowi ponowne mapowanie tych stron w tabeli stron 2. etapu hosta. Podobnie, komunikacja hosta z TrustZone jest możliwa dzięki operacjom współdzielenia pamięci i/lub użyczania, z których wszystkie są ściśle monitorowane i kontrolowane przez pKVM przy użyciu specyfikacji Firmware Framework for Arm (FF-A) .
Hiperwizor jest odpowiedzialny za śledzenie własności wszystkich stron pamięci w systemie oraz za to, czy są one udostępniane, czy wypożyczane innym podmiotom. Większość tego śledzenia stanu odbywa się przy użyciu metadanych dołączonych do tabel stron hosta i gości etapu 2, przy użyciu zarezerwowanych bitów we wpisach tablicy stron (PTE), które, jak sugeruje ich nazwa, są zarezerwowane do użytku przez oprogramowanie.
Host musi upewnić się, że nie próbuje uzyskać dostępu do stron, które zostały zablokowane przez hiperwizora. Niedozwolony dostęp do hosta powoduje wstrzyknięcie synchronicznego wyjątku do hosta przez hipernadzorcę, co może spowodować, że odpowiedzialne zadanie w przestrzeni użytkownika otrzyma sygnał SEGV lub awarię jądra hosta. Aby zapobiec przypadkowemu dostępowi, strony przekazane gościom nie kwalifikują się do zamiany ani połączenia przez jądro hosta.
Obsługa przerwań i timery
Przerwania są istotną częścią sposobu, w jaki gość wchodzi w interakcję z urządzeniami i komunikacji między procesorami, gdzie przerwania międzyprocesorowe (IPI) są głównym mechanizmem komunikacji. Model KVM polega na delegowaniu całego zarządzania wirtualnymi przerwaniami do hosta w EL1, który w tym celu zachowuje się jak niezaufana część hiperwizora.
pKVM oferuje pełną emulację Generic Interrupt Controller w wersji 3 (GICv3) w oparciu o istniejący kod KVM. Timer i IPI są obsługiwane jako część tego niezaufanego kodu emulacji.
Obsługa GICv3
Interfejs między EL1 i EL2 musi gwarantować, że pełny stan przerwania jest widoczny dla hosta EL1, w tym kopie rejestrów hiperwizora związanych z przerwaniami. Ta widoczność jest zwykle osiągana przy użyciu regionów pamięci współużytkowanej, po jednym na wirtualny procesor (vCPU).
Kod obsługi środowiska wykonawczego rejestru systemowego można uprościć, aby obsługiwał tylko pułapki rejestru generowane przez oprogramowanie (SGIR) i dezaktywowany rejestr przerwań (DIR). Architektura nakazuje, aby te rejestry zawsze ograniczały się do EL2, podczas gdy inne pułapki były jak dotąd przydatne tylko do ograniczania błędów. Wszystko inne jest obsługiwane sprzętowo.
Po stronie MMIO wszystko jest emulowane w EL1, ponownie wykorzystując całą obecną infrastrukturę w KVM. Wreszcie, oczekiwanie na przerwanie (WFI) jest zawsze przekazywane do EL1, ponieważ jest to jeden z podstawowych prymitywów planowania używanych przez KVM.
Obsługa timera
Wartość komparatora dla wirtualnego timera musi być wystawiona na EL1 na każdym pułapkowym WFI, aby EL1 mógł wstrzykiwać przerwania timera, gdy vCPU jest zablokowany. Fizyczny timer jest całkowicie emulowany, a wszystkie pułapki przekazywane do EL1.
Obsługa MMIO
Aby komunikować się z monitorem maszyny wirtualnej (VMM) i przeprowadzić emulację GIC, pułapki MMIO muszą zostać przekazane z powrotem do hosta w EL1 w celu dalszej oceny. pKVM wymaga:
- IPA i wielkość dostępu
- Dane w przypadku zapisu
- Endianowość procesora w punkcie pułapkowania
Dodatkowo pułapki z rejestrem ogólnego przeznaczenia (GPR) jako źródłem/miejscem docelowym są przekazywane za pomocą pseudorejestru transferu abstrakcyjnego.
Interfejsy gościa
Gość może komunikować się z chronionym gościem za pomocą kombinacji hiperwywołań i dostępu do pamięci w uwięzionych regionach. Hypercalls są udostępniane zgodnie ze standardem SMCCC , z zakresem zarezerwowanym dla alokacji dostawcy przez KVM. Następujące wywołania hipertekstowe mają szczególne znaczenie dla gości pKVM.
Ogólne wywołania hipertekstowe
- PSCI zapewnia gościowi standardowy mechanizm kontrolowania cyklu życia jego procesorów wirtualnych, w tym trybu online, offline i zamykania systemu.
- TRNG zapewnia gościowi standardowy mechanizm żądania entropii z pKVM, który przekazuje połączenie do EL3. Ten mechanizm jest szczególnie przydatny, gdy hostowi nie można ufać w kwestii wirtualizacji sprzętowego generatora liczb losowych (RNG).
hiperwywołania pKVM
- Współdzielenie pamięci z hostem. Cała pamięć gościa jest początkowo niedostępna dla hosta, ale dostęp do hosta jest niezbędny do komunikacji w pamięci współużytkowanej oraz w przypadku urządzeń parawirtualizowanych, które opierają się na współdzielonych buforach. Hypercalls do udostępniania i cofania udostępniania stron hostowi pozwalają gościowi zdecydować, które dokładnie części pamięci mają być dostępne dla reszty Androida bez konieczności uzgadniania.
- Uwięzienie dostępu do pamięci do hosta. Tradycyjnie, jeśli gość KVM uzyska dostęp do adresu, który nie odpowiada prawidłowemu regionowi pamięci, wówczas wątek vCPU wychodzi do hosta, a dostęp jest zwykle używany przez MMIO i emulowany przez VMM w przestrzeni użytkownika. Aby ułatwić tę obsługę, pKVM jest zobowiązany do rozgłaszania szczegółowych informacji na temat błędnej instrukcji, takich jak jej adres, parametry rejestru i potencjalnie ich zawartość, z powrotem do hosta, co mogłoby nieumyślnie ujawnić poufne dane chronionego gościa, gdyby pułapka nie została przewidziana. pKVM rozwiązuje ten problem, traktując te błędy jako krytyczne, chyba że gość wcześniej wysłał wywołanie hipertekstowe, aby zidentyfikować wadliwy zakres IPA jako taki, dla którego dostęp jest dozwolony z powrotem do hosta. To rozwiązanie jest określane mianem strażnika MMIO .
Wirtualne urządzenie we/wy (virtio)
Virtio jest popularnym, przenośnym i dojrzałym standardem wdrażania i interakcji z urządzeniami parawirtualizowanymi. Większość urządzeń eksponowanych na chronionych gościach jest zaimplementowana przy użyciu virtio. Virtio stanowi również podstawę implementacji vsock używanej do komunikacji między chronionym gościem a resztą Androida.
Urządzenia Virtio są zwykle implementowane w przestrzeni użytkownika hosta przez program VMM, który przechwytuje uwięzione dostępy do pamięci od gościa do interfejsu MMIO urządzenia virtio i emuluje oczekiwane zachowanie. Dostęp MMIO jest stosunkowo kosztowny, ponieważ każdy dostęp do urządzenia wymaga podróży w obie strony do VMM iz powrotem, więc większość rzeczywistego transferu danych między urządzeniem a gościem odbywa się przy użyciu zestawu virtqueues w pamięci. Kluczowym założeniem virtio jest to, że host może dowolnie uzyskiwać dostęp do pamięci gościa. To założenie jest widoczne w projekcie Virtqueue, który może zawierać wskaźniki do buforów w gościu, do których emulacja urządzenia ma mieć bezpośredni dostęp.
Chociaż wcześniej opisane hiperwywołania współdzielenia pamięci mogą być używane do udostępniania buforów danych virtio od gościa do hosta, udostępnianie to jest koniecznie wykonywane z zachowaniem szczegółowości strony i może skończyć się ujawnieniem większej ilości danych niż jest to wymagane, jeśli rozmiar bufora jest mniejszy niż rozmiar strony . Zamiast tego gość jest skonfigurowany tak, aby przydzielać zarówno kolejki virtqueues, jak i odpowiadające im bufory danych ze stałego okna pamięci współdzielonej, przy czym dane są kopiowane (odbijane) do iz okna zgodnie z wymaganiami.
Interakcja z TrustZone
Chociaż goście nie mogą bezpośrednio wchodzić w interakcje z TrustZone, host musi nadal mieć możliwość wysyłania połączeń SMC do bezpiecznego świata. Te wywołania mogą określać fizycznie adresowane bufory pamięci, które są niedostępne dla hosta. Ponieważ bezpieczne oprogramowanie jest generalnie nieświadome dostępności bufora, złośliwy host może użyć tego bufora do przeprowadzenia zdezorientowanego ataku zastępczego (analogicznego do ataku DMA). Aby zapobiec takim atakom, pKVM przechwytuje wszystkie wywołania SMC hosta do EL2 i działa jako pośrednik między hostem a bezpiecznym monitorem w EL3.
Wywołania PSCI z hosta są przekazywane do oprogramowania układowego EL3 z minimalnymi modyfikacjami. W szczególności punkt wejścia dla procesora przechodzącego w tryb online lub wznawiającego pracę ze stanu wstrzymania jest przepisywany tak, że tablica stron etapu 2 jest instalowana w EL2 przed powrotem do hosta w EL1. Podczas uruchamiania ta ochrona jest wymuszana przez pKVM.
Ta architektura opiera się na SoC obsługującym PSCI, najlepiej poprzez wykorzystanie aktualnej wersji TF-A jako oprogramowania układowego EL3.
Firmware Framework for Arm (FF-A) standaryzuje interakcje między normalnymi i bezpiecznymi światami, szczególnie w obecności bezpiecznego hiperwizora. Większa część specyfikacji definiuje mechanizm współdzielenia pamięci z bezpiecznym światem, używając zarówno wspólnego formatu wiadomości, jak i dobrze zdefiniowanego modelu uprawnień dla bazowych stron. pKVM przekazuje komunikaty FF-A, aby upewnić się, że host nie próbuje współdzielić pamięci z bezpieczną stroną, do której nie ma wystarczających uprawnień.
Ta architektura opiera się na oprogramowaniu bezpiecznego świata wymuszającym model dostępu do pamięci, aby zapewnić zaufanym aplikacjom i wszelkim innym programom działającym w bezpiecznym świecie dostęp do pamięci tylko wtedy, gdy jest ona wyłączną własnością bezpiecznego świata lub została mu jawnie udostępniona za pomocą FF -A. W systemie z S-EL2 wymuszanie modelu dostępu do pamięci powinno być wykonywane przez Secure Partition Manager Core (SPMC), taki jak Hafnium , który obsługuje tabele stron etapu 2 dla bezpiecznego świata. W systemie bez S-EL2 TEE może zamiast tego wymusić model dostępu do pamięci poprzez swoje tablice stron etapu 1.
Jeżeli wywołanie SMC do EL2 nie jest wywołaniem PSCI lub komunikatem zdefiniowanym przez FF-A, nieobsługiwane SMC są przekazywane do EL3. Założenie jest takie, że (koniecznie zaufane) bezpieczne oprogramowanie układowe może bezpiecznie obsługiwać nieobsługiwane SMC, ponieważ oprogramowanie układowe rozumie środki ostrożności potrzebne do utrzymania izolacji pVM.
Monitor maszyny wirtualnej
crosvm to monitor maszyny wirtualnej (VMM), który uruchamia maszyny wirtualne za pośrednictwem interfejsu KVM systemu Linux. To, co czyni crosvm wyjątkowym, to skupienie się na bezpieczeństwie przy użyciu języka programowania Rust i piaskownicy wokół urządzeń wirtualnych w celu ochrony jądra hosta.
Deskryptory plików i ioctls
KVM udostępnia urządzenie znakowe /dev/kvm
w przestrzeni użytkownika za pomocą ioctls, które tworzą API KVM. Ioctle należą do następujących kategorii:
- Systemowe ioctls wysyłają zapytania i ustawiają atrybuty globalne, które wpływają na cały podsystem KVM i tworzą maszyny pVM.
- VM ioctls wysyła zapytania i ustawia atrybuty, które tworzą wirtualne procesory (vCPU) i urządzenia oraz wpływają na całą pVM, takie jak układ pamięci i liczba wirtualnych procesorów (vCPU) i urządzeń.
- vCPU ioctls wysyła zapytania i ustawia atrybuty kontrolujące działanie pojedynczego wirtualnego procesora.
- Urządzenie ioctl wysyła zapytanie i ustawia atrybuty kontrolujące działanie pojedynczego urządzenia wirtualnego.
Każdy proces crosvm uruchamia dokładnie jedną instancję maszyny wirtualnej. Ten proces wykorzystuje system ioctl KVM_CREATE_VM
do utworzenia deskryptora pliku VM, którego można użyć do wydawania poleceń ioctl pVM. KVM_CREATE_VCPU
lub KVM_CREATE_DEVICE
ioctl na VM FD tworzy vCPU/urządzenie i zwraca deskryptor pliku wskazujący nowy zasób. ioctls na vCPU lub urządzeniu FD może służyć do sterowania urządzeniem, które zostało utworzone przy użyciu ioctl na VM FD. W przypadku procesorów wirtualnych obejmuje to ważne zadanie uruchamiania kodu gościa.
Wewnętrznie crosvm rejestruje deskryptory plików maszyny wirtualnej w jądrze za pomocą wyzwalanego krawędzią interfejsu epoll
. Następnie jądro powiadamia crosvm, gdy w którymkolwiek z deskryptorów plików pojawi się nowe zdarzenie oczekujące.
pKVM dodaje nową funkcję, KVM_CAP_ARM_PROTECTED_VM
, której można użyć do uzyskania informacji o środowisku pVM i skonfigurowania trybu chronionego dla maszyny wirtualnej. crosvm używa tego podczas tworzenia pVM, jeśli przekazana jest flaga --protected-vm
, do zapytania i zarezerwowania odpowiedniej ilości pamięci dla oprogramowania układowego pVM, a następnie do włączenia trybu chronionego.
Alokacja pamięci
Jednym z głównych zadań VMM jest przydzielanie pamięci maszyny wirtualnej i zarządzanie jej układem pamięci. crosvm generuje stały układ pamięci luźno opisany w poniższej tabeli.
FDT w trybie normalnym | PHYS_MEMORY_END - 0x200000 |
Wolna przestrzeń | ... |
Ramdysk | ALIGN_UP(KERNEL_END, 0x1000000) |
Jądro | 0x80080000 |
Program rozruchowy | 0x80200000 |
FDT w trybie BIOS | 0x80000000 |
Baza pamięci fizycznej | 0x80000000 |
Oprogramowanie układowe pVM | 0x7FE00000 |
Pamięć urządzenia | 0x10000 - 0x40000000 |
Pamięć fizyczna jest przydzielana za pomocą mmap
, a pamięć jest przekazywana do maszyny wirtualnej w celu wypełnienia jej regionów pamięci, zwanych memslotami , za pomocą ioctl KVM_SET_USER_MEMORY_REGION
. Cała pamięć maszyny wirtualnej gościa jest zatem przypisywana do zarządzającej nią instancji crosvm i może spowodować zabicie procesu (zakończenie maszyny wirtualnej), jeśli hostowi zacznie brakować wolnej pamięci. Po zatrzymaniu maszyny wirtualnej pamięć jest automatycznie czyszczona przez hiperwizor i zwracana do jądra hosta.
W zwykłym KVM VMM zachowuje dostęp do całej pamięci gościa. Dzięki pKVM pamięć gościa jest odwzorowywana z fizycznej przestrzeni adresowej hosta, gdy jest przekazywana gościowi. Jedynym wyjątkiem jest pamięć jawnie współdzielona z powrotem przez gościa, na przykład dla urządzeń virtio.
Regiony MMIO w przestrzeni adresowej gościa pozostają niezmapowane. Dostęp gościa do tych regionów jest uwięziony i skutkuje zdarzeniem we/wy w FD maszyny wirtualnej. Mechanizm ten służy do implementacji urządzeń wirtualnych. W trybie chronionym gość musi potwierdzić, że region jego przestrzeni adresowej jest używany przez MMIO przy użyciu hiperwywołania, aby zmniejszyć ryzyko przypadkowego wycieku informacji.
Planowanie
Każdy wirtualny procesor jest reprezentowany przez wątek POSIX i zaplanowany przez program planujący hosta w systemie Linux. Wątek wywołuje KVM_RUN
ioctl na vCPU FD, co skutkuje przełączeniem hiperwizora na kontekst vCPU gościa. Harmonogram hosta uwzględnia czas spędzony w kontekście gościa jako czas wykorzystany przez odpowiedni wątek vCPU. KVM_RUN
powraca, gdy wystąpi zdarzenie, które musi obsłużyć VMM, takie jak we/wy, koniec przerwania lub zatrzymanie vCPU. VMM obsługuje zdarzenie i ponownie wywołuje KVM_RUN
.
Podczas KVM_RUN
wątek pozostaje wywłaszczany przez program planujący hosta, z wyjątkiem wykonania kodu hiperwizora EL2, który nie jest wywłaszczany. Sama maszyna pVM gościa nie ma mechanizmu do kontrolowania tego zachowania.
Ponieważ wszystkie wątki vCPU są zaplanowane tak jak inne zadania w przestrzeni użytkownika, podlegają one wszystkim standardowym mechanizmom QoS. W szczególności każdy wątek vCPU można powiązać z fizycznymi procesorami, umieścić w zestawach procesorów, zwiększyć lub ograniczyć za pomocą ograniczania wykorzystania, zmienić zasady priorytetów/planowania i nie tylko.
Urządzenia wirtualne
crosvm obsługuje wiele urządzeń, w tym następujące:
- virtio-blk dla kompozytowych obrazów dysków, tylko do odczytu lub do odczytu i zapisu
- vhost-vsock do komunikacji z hostem
- virtio-pci jako transport virtio
- pl030 zegar czasu rzeczywistego (RTC)
- 16550a UART do komunikacji szeregowej
Oprogramowanie układowe pVM
Oprogramowanie układowe pVM (pvmfw) to pierwszy kod wykonywany przez pVM, podobny do rozruchowej pamięci ROM urządzenia fizycznego. Głównym celem pvmfw jest uruchomienie bezpiecznego rozruchu i uzyskanie unikalnego sekretu pVM. pvmfw nie jest ograniczony do użycia z żadnym konkretnym systemem operacyjnym, takim jak Microdroid , o ile system operacyjny jest obsługiwany przez crosvm i został prawidłowo podpisany.
Plik binarny pvmfw jest przechowywany na partycji flash o tej samej nazwie i jest aktualizowany za pomocą OTA .
Rozruch urządzenia
Do procedury rozruchu urządzenia obsługującego pKVM dodano następującą sekwencję kroków:
- Android Bootloader (ABL) ładuje pvmfw ze swojej partycji do pamięci i weryfikuje obraz.
- ABL uzyskuje swoje sekrety mechanizmu składu identyfikatora urządzenia (DICE) (złożone identyfikatory urządzeń (CDI) i łańcuch certyfikatów rozruchowych (BCC)) z korzenia zaufania.
- ABL wykonuje pomiary i wyprowadza DICE sekretów pvmfw (CDI) i dołącza je do pliku binarnego pvmfw.
- ABL dodaje węzeł obszaru pamięci zarezerwowanej
linux,pkvm-guest-firmware-memory
do ID, opisując lokalizację i rozmiar pliku binarnego pvmfw oraz sekrety, które wyprowadził w poprzednim kroku. - ABL przekazuje kontrolę Linuksowi, a Linux inicjuje pKVM.
- pKVM odwzorowuje region pamięci pvmfw z tablic stron etapu 2 hosta i chroni go przed hostem (i gośćmi) przez cały czas pracy urządzenia.
Po uruchomieniu urządzenia Microdroid jest uruchamiany zgodnie z krokami opisanymi w sekcji Sekwencja rozruchu dokumentu Microdroid .
rozruch pVM
Tworząc pVM, crosvm (lub inny VMM) musi utworzyć wystarczająco duży memslot, aby hiperwizor mógł go wypełnić obrazem pvmfw. VMM jest również ograniczony na liście rejestrów, których wartość początkową może ustawić (x0-x14 dla podstawowego vCPU, brak dla drugorzędnych vCPU). Pozostałe rejestry są zarezerwowane i stanowią część ABI hypervisor-pvmfw.
Gdy pVM jest uruchomiona, hiperwizor najpierw przekazuje kontrolę nad podstawowym vCPU do pvmfw. Oprogramowanie układowe oczekuje, że crosvm załadował jądro podpisane przez AVB, którym może być program ładujący lub dowolny inny obraz, oraz niepodpisany FDT do pamięci ze znanymi przesunięciami. pvmfw weryfikuje sygnaturę AVB i, jeśli się powiedzie, generuje zaufane drzewo urządzeń z odebranego FDT, usuwa jego sekrety z pamięci i rozgałęzia się do punktu wejścia ładunku. Jeśli jeden z etapów weryfikacji nie powiedzie się, oprogramowanie sprzętowe wysyła hiperwywołanie PSCI SYSTEM_RESET
.
Pomiędzy rozruchami informacje o instancji pVM są przechowywane na partycji (urządzenie virtio-blk) i szyfrowane za pomocą klucza tajnego pvmfw, aby zapewnić, że po ponownym uruchomieniu klucz tajny zostanie przekazany do właściwej instancji.