Architektura AVF

System Android zapewnia referencyjną implementację wszystkich komponentów potrzebnych do wdrożenia platformy wirtualizacji systemu Android. Obecnie ta implementacja jest ograniczona do ARM64. Na tej stronie opisano architekturę frameworka.

Tło

Architektura Arm dopuszcza maksymalnie cztery poziomy wyjątków, przy czym poziom wyjątków 0 (EL0) jest najmniej uprzywilejowany, a poziom wyjątków 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 na poziomie EL1.

Warstwa EL2 umożliwia wprowadzenie hypervisora, który umożliwia izolowanie pamięci i urządzeń w poszczególnych maszynach pVM w EL1/EL0, z silnymi gwarancjami poufności i integralności.

Hiperwizor

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 w momencie tworzenia jako „chronione”.

KVM/arm64 obsługuje różne tryby wykonywania w zależności od dostępności niektórych funkcji procesora, a mianowicie rozszerzeń hosta wirtualizacji (VHE) (ARMv8.1 i nowsze). W jednym z tych trybów, powszechnie znanym jako tryb inny niż VHE, kod hypervisora ​​jest oddzielany od obrazu jądra podczas rozruchu i instalowany w EL2, podczas gdy samo jądro działa w EL1. Chociaż komponent EL2 KVM jest częścią kodu bazowego Linuksa, jest małym komponentem odpowiedzialnym za przełączanie między wieloma EL1 i całkowicie kontrolowanym przez jądro hosta. Komponent hypervisora ​​jest skompilowany z systemem Linux, ale znajduje się w osobnej, dedykowanej sekcji pamięci obrazu vmlinux . pKVM wykorzystuje ten projekt, rozszerzając kod hypervisora ​​o nowe funkcje, co pozwala mu nakładać ograniczenia na jądro hosta Androida i przestrzeń użytkownika oraz ograniczać dostęp hosta do pamięci gościa i hypervisora.

Moduły dostawcy pKVM

Moduł dostawcy pKVM to moduł specyficzny dla sprzętu, zawierający funkcje specyficzne dla urządzenia, takie jak sterowniki jednostki zarządzania pamięcią wejścia-wyjścia (IOMMU). Moduły te umożliwiają przenoszenie funkcji bezpieczeństwa wymagających dostępu na poziomie wyjątku 2 (EL2) do pKVM.

Aby dowiedzieć się, jak wdrożyć i załadować moduł dostawcy pKVM, zapoznaj się z sekcją Implementowanie modułu dostawcy pKVM .

Procedura rozruchu

Poniższy rysunek przedstawia procedurę uruchamiania pKVM:

Procedura rozruchu pKVM

Rysunek 1. Procedura uruchamiania pKVM

  1. Program ładujący wchodzi do jądra ogólnego w EL2.
  2. Ogólne jądro wykrywa, że ​​działa w EL2 i pozbawia się uprawnień do EL1, podczas gdy pKVM i jego moduły nadal działają w EL2. Dodatkowo w tym momencie ładowane są moduły dostawcy pKVM.
  3. Ogólne jądro uruchamia się normalnie, ładując wszystkie niezbędne sterowniki urządzeń, aż do osiągnięcia przestrzeni użytkownika. W tym momencie pKVM jest już na miejscu i obsługuje tabele stron etapu 2.

Procedura rozruchu ufa programowi ładującemu, że zachowa integralność obrazu jądra tylko podczas wczesnego rozruchu. Kiedy jądro zostaje pozbawione uprawnień, hiperwizor nie uważa go już za zaufane, który jest następnie odpowiedzialny za ochronę 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 obu 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 połączenie 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 można wykorzystać do implementacji translacji adresów i kontroli dostępu do różnych części pamięci. MMU etapu 1 jest kontrolowane przez EL1 i umożliwia pierwszy poziom translacji adresów. Jednostka MMU etapu 1 jest używana przez Linuksa do zarządzania wirtualną przestrzenią adresową udostępnianą każdemu procesowi przestrzeni użytkownika i jego własnej wirtualnej przestrzeni adresowej.

Jednostka MMU stopnia 2 jest kontrolowana przez EL2 i umożliwia zastosowanie drugiej translacji adresu na adresie wyjściowym jednostki MMU stopnia 1, w wyniku czego powstaje adres fizyczny (PA). Tłumaczenie etapu 2 może być wykorzystywane przez hypervisory do kontrolowania i translacji dostępu do pamięci ze wszystkich gościnnych maszyn wirtualnych. 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.

Ochrona dostępu do pamięci procesora

Rysunek 2. Ochrona dostępu do pamięci procesora

Historycznie rzecz biorąc, KVM działa z włączonym tłumaczeniem etapu 2 podczas uruchamiania gości i z wyłączonym etapem 2 podczas działania jądra Linuksa hosta. Architektura ta 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 hosta do stron pamięci gościa. Z drugiej strony pKVM umożliwia ochronę drugiego etapu nawet w kontekście hosta i stawia hiperwizora odpowiedzialnego za ochronę stron pamięci gościa zamiast hosta.

KVM w pełni wykorzystuje translację adresów na etapie 2, aby wdrożyć złożone mapowania IPA/PA dla gości, co stwarza iluzję ciągłej pamięci dla gości pomimo fizycznej fragmentacji. Jednakże użycie jednostki MMU etapu 2 dla hosta jest ograniczone wyłącznie do kontroli dostępu. Etap 2 hosta jest mapowany tożsamościowo, zapewniając, że ciągła pamięć w przestrzeni IPA hosta jest ciągła w przestrzeni PA. Architektura ta pozwala na użycie dużych mapowań w tabeli stron, a w 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 tabeli stron.

Ochrona bezpośredniego dostępu do pamięci (DMA).

Jak opisano wcześniej, odmapowanie stron gości z hosta Linux w tabelach stron procesora jest koniecznym, ale niewystarczającym krokiem w celu ochrony pamięci gościa. pKVM musi także chronić przed dostępem do pamięci przez urządzenia obsługujące DMA pod kontrolą jądra hosta i 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 jednostki zarządzania pamięcią wejścia-wyjścia (IOMMU) dla każdego urządzenia w systemie obsługującego DMA, jak pokazano na rysunku 3.

Ochrona dostępu do pamięci DMA

Rysunek 3. Ochrona dostępu do pamięci DMA

Sprzęt IOMMU umożliwia co najmniej przyznawanie i odbieranie dostępu do odczytu/zapisu urządzenia w pamięci fizycznej z zachowaniem szczegółowości strony. Jednak ten sprzęt IOMMU ogranicza użycie urządzeń w maszynach pVM, ponieważ zakładają one etap 2 mapowania tożsamości.

Aby zapewnić izolację pomiędzy maszynami wirtualnymi, transakcje pamięci generowane w imieniu różnych jednostek muszą być rozróżnialne przez IOMMU, tak aby do tłumaczenia można było użyć odpowiedniego zestawu tablic stron.

Ponadto zmniejszenie ilości kodu specyficznego dla SoC w EL2 jest kluczową strategią mającą na celu zmniejszenie 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 energią, inicjalizacja i, tam gdzie to konieczne, obsługa przerwań.

Jednak oddanie hostowi kontroli nad stanem urządzenia nakłada dodatkowe wymagania na interfejs programowania sprzętu IOMMU, aby zapewnić, że sprawdzanie uprawnień nie będzie mogło zostać ominięte 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 momencie rozruchu zakłada się, że cała pamięć inna niż hypervisor jest własnością hosta i jako taka jest śledzona przez hypervisor. Po uruchomieniu maszyny pVM host udostępnia strony pamięci, aby umożliwić jej uruchomienie, a hiperwizor przenosi własność tych stron z hosta na maszynę pVM. W związku z tym hiperwizor wprowadza ograniczenia kontroli dostępu do tabeli stron hosta w etapie 2, aby uniemożliwić mu ponowny dostęp do stron, zapewniając poufność gościowi.

Komunikacja między gospodarzem a gośćmi jest możliwa dzięki kontrolowanemu dzieleniu się pamięcią między nimi. Goście mogą udostępniać niektóre swoje strony gospodarzowi za pomocą hiperwywołania, które instruuje hiperwizora, aby ponownie zmapował te strony w tabeli stron drugiego etapu hosta. Podobnie komunikacja hosta z TrustZone jest możliwa dzięki operacjom współdzielenia i/lub pożyczania pamięci, z których wszystkie są ściśle monitorowane i kontrolowane przez pKVM przy użyciu specyfikacji Firmware Framework for Arm (FF-A) .

Ponieważ wymagania pamięci pVM mogą zmieniać się w czasie, zapewnione jest hiperwywołanie, które umożliwia zrzeczenie się własności określonych stron należących do osoby wywołującej z powrotem na hosta. W praktyce to hiperwywołanie jest używane w protokole balonowym virtio, aby umożliwić VMM zażądanie zwrotu pamięci od pVM, a pVM powiadomienie VMM o porzuconych stronach w kontrolowany sposób.

Hiperwizor jest odpowiedzialny za śledzenie własności wszystkich stron pamięci w systemie oraz tego, czy są one udostępniane, czy pożyczane innym podmiotom. Większość tego śledzenia stanu odbywa się przy użyciu metadanych dołączonych do tabel stron etapu 2 gospodarza i gości, przy użyciu zarezerwowanych bitów we wpisach tablicy stron (PTE), które, jak sugeruje ich nazwa, są zarezerwowane do użytku oprogramowania.

Host musi upewnić się, że nie będzie próbował uzyskać dostępu do stron, które zostały zablokowane przez hiperwizora. Nielegalny dostęp do hosta powoduje, że hiperwizor wprowadza do hosta wyjątek synchroniczny, co może skutkować otrzymaniem sygnału SEGV przez odpowiedzialne zadanie przestrzeni użytkownika lub awarią jądra hosta. Aby zapobiec przypadkowemu dostępowi, strony przekazane gościom nie kwalifikują się do zamiany ani łą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 oraz w komunikacji między procesorami, gdzie przerwania międzyprocesorowe (IPI) są głównym mechanizmem komunikacji. Model KVM polega na delegowaniu całego zarządzania przerwaniami wirtualnymi hostowi w EL1, który w tym celu zachowuje się jak niezaufana część hypervisora.

pKVM oferuje pełną emulację Generic Interrupt Controller w wersji 3 (GICv3) opartą na istniejącym kodzie KVM. Timer i IPI są obsługiwane jako część tego niezaufanego kodu emulacyjnego.

Obsługa GICv3

Interfejs pomiędzy EL1 i EL2 musi zapewniać, że pełny stan przerwania będzie widoczny dla hosta EL1, łącznie z kopiami rejestrów hypervisora ​​związanych z przerwaniami. Ta widoczność jest zwykle osiągana przy użyciu obszarów pamięci współdzielonej, po jednym na wirtualny procesor (vCPU).

Kod obsługi rejestru systemowego można uprościć, aby obsługiwał jedynie rejestrowanie przerwań generowanych programowo (SGIR) i rejestr przerwań dezaktywowanych (DIR). Architektura wymaga, aby te rejestry zawsze prowadziły do ​​EL2, podczas gdy inne pułapki były jak dotąd przydatne jedynie w celu ograniczenia błędów. Cała reszta jest obsługiwana sprzętowo.

Po stronie MMIO wszystko jest emulowane w EL1, ponownie wykorzystując całą obecną infrastrukturę KVM. Na koniec funkcja oczekiwania na przerwanie (WFI) jest zawsze przekazywana do EL1, ponieważ jest to jeden z podstawowych elementów planowania używanych przez KVM.

Obsługa timera

Wartość komparatora dla zegara wirtualnego musi być udostępniona EL1 w każdym przechwytującym 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 wykonywać emulację GIC, pułapki MMIO muszą zostać przekazane z powrotem do hosta w EL1 w celu dalszej selekcji. pKVM wymaga następujących elementów:

  • IPA i wielkość dostępu
  • Dane w przypadku zapisu
  • Endianowość procesora w punkcie zalewkowania

Dodatkowo pułapki z rejestrem ogólnego przeznaczenia (GPR) jako źródłem/miejscem docelowym są przekazywane przy użyciu abstrakcyjnego pseudorejestru transferu.

Interfejsy gościnne

Gość może komunikować się z chronionym gościem, korzystając z połączenia hiperpołączeń i dostępu do pamięci uwięzionych regionów. Hypercalls są udostępniane zgodnie ze standardem SMCCC , z zakresem zarezerwowanym dla alokacji dostawców przez KVM. Następujące hiperwywołania są szczególnie ważne dla gości pKVM.

Ogólne hiperpołączenia

  • PSCI zapewnia gościowi standardowy mechanizm kontrolowania cyklu życia jego procesorów vCPU, w tym pracy w trybie online, offline i zamykania systemu.
  • TRNG zapewnia gościowi standardowy mechanizm żądania entropii od pKVM, który przekazuje połączenie do EL3. Mechanizm ten jest szczególnie przydatny, gdy nie można ufać hostowi, że zwirtualizuje sprzętowy generator liczb losowych (RNG).

Hiperwywołania pKVM

  • Udostępnianie pamięci hostowi. Cała pamięć gościa jest początkowo niedostępna dla hosta, ale dostęp hosta jest niezbędny do komunikacji w pamięci współdzielonej oraz w przypadku urządzeń parawirtualnych korzystających ze współdzielonych buforów. Hiperwywołania umożliwiające udostępnianie i anulowanie udostępniania stron hostowi pozwalają gościowi dokładnie decydować, które części pamięci zostaną udostępnione reszcie systemu Android, bez konieczności podawania ręki.
  • Zrzeczenie się pamięci na rzecz hosta. Cała pamięć gościa zwykle należy do gościa, dopóki nie zostanie zniszczona. Ten stan może być nieodpowiedni w przypadku długotrwałych maszyn wirtualnych, których wymagania dotyczące pamięci zmieniają się w czasie. Funkcja relinquish hiperwywołania umożliwia gościowi wyraźne przeniesienie własności stron z powrotem na hosta bez konieczności kończenia połączenia przez gościa.
  • Pułapka dostępu do pamięci do hosta. Tradycyjnie, jeśli gość KVM uzyskuje dostęp do adresu, który nie odpowiada prawidłowemu obszarowi pamięci, wówczas wątek vCPU wychodzi do hosta, a dostęp jest zwykle używany dla MMIO i emulowany przez VMM w przestrzeni użytkownika. Aby ułatwić tę obsługę, pKVM musi ogłosić hostowi szczegóły dotyczące instrukcji powodującej błąd, takie jak jej adres, parametry rejestru i potencjalna zawartość, co mogłoby w sposób niezamierzony ujawnić wrażliwe dane chronionego gościa, jeśli nie przewidziano pułapki. pKVM rozwiązuje ten problem, traktując te błędy jako krytyczne, chyba że gość wcześniej wydał hiperwywołanie w celu zidentyfikowania zakresu IPA, w którym występuje błąd, jako takiego, do którego dostęp może być pułapką z powrotem do hosta. Rozwiązanie to określa się mianem osłony MMIO .

Wirtualne urządzenie we/wy (virtio)

Virtio to popularny, przenośny i dojrzały standard wdrażania i interakcji z urządzeniami parawirtualizowanymi. Większość urządzeń dostępnych dla chronionych gości jest implementowana 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 VMM, który przechwytuje dostęp gościa do uwięzionej pamięci do interfejsu MMIO urządzenia virtio i emuluje oczekiwane zachowanie. Dostęp do MMIO jest stosunkowo kosztowny, ponieważ każdy dostęp do urządzenia wymaga podróży w obie strony do VMM i z powrotem, więc większość rzeczywistego transferu danych między urządzeniem a gościem odbywa się przy użyciu zestawu wirtualnych kolejek w pamięci. Kluczowym założeniem virtio jest to, że host może dowolnie uzyskać dostęp do pamięci gościa. Założenie to jest widoczne w projekcie wirtualnej kolejki, która może zawierać wskaźniki do buforów gościa, do których emulacja urządzenia ma mieć bezpośredni dostęp.

Chociaż opisane wcześniej hiperwywołania współdzielenia pamięci można wykorzystać do udostępnienia buforów danych virtio od gościa hostowi, to udostępnianie jest koniecznie wykonywane z zachowaniem szczegółowości strony i może skutkować 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 wirtualne kolejki, jak i odpowiadające im bufory danych ze stałego okna pamięci współdzielonej, przy czym dane są kopiowane (odbijane) do i z okna w razie potrzeby.

Urządzenie wirtualne

Rysunek 4. Urządzenie Virtio

Interakcja z TrustZone

Chociaż goście nie mogą bezpośrednio wchodzić w interakcję z TrustZone, gospodarz musi nadal mieć możliwość wysyłania wywołań SMC do bezpiecznego świata. Wywołania te mogą określać fizycznie adresowane bufory pamięci, które są niedostępne dla hosta. Ponieważ bezpieczne oprogramowanie zazwyczaj nie jest świadome dostępności bufora, złośliwy host może wykorzystać ten bufor do przeprowadzenia zdezorientowanego ataku zastępczego (analogicznie do ataku DMA). Aby zapobiec takim atakom, pKVM przechwytuje wszystkie wywołania SMC hosta do EL2 i działa jako serwer proxy pomiędzy hostem a bezpiecznym monitorem w EL3.

Połączenia PSCI z hosta są przekazywane do oprogramowania sprzętowego EL3 z minimalnymi modyfikacjami. W szczególności punkt wejścia procesora przechodzącego w tryb online lub wznawiającego stan wstrzymania jest przepisywany w taki sposób, że tabela stron etapu 2 jest instalowana w EL2 przed powrotem do hosta w EL1. Podczas rozruchu ta ochrona jest egzekwowana przez pKVM.

Architektura ta opiera się na SoC obsługującym PSCI, najlepiej poprzez zastosowanie aktualnej wersji TF-A jako oprogramowania sprzętowego EL3.

Firmware Framework for Arm (FF-A) standaryzuje interakcje pomiędzy światem normalnym i bezpiecznym, szczególnie w obecności bezpiecznego hypervisora. Większa część specyfikacji definiuje mechanizm współdzielenia pamięci z bezpiecznym światem, wykorzystując zarówno wspólny format wiadomości, jak i dobrze zdefiniowany model uprawnień dla podstawowych stron. pKVM pośredniczy w przesyłaniu wiadomości FF-A, aby mieć pewność, że host nie próbuje współdzielić pamięci ze stroną bezpieczną, do której nie ma wystarczających uprawnień.

Architektura ta opiera się na oprogramowaniu bezpiecznego świata wymuszającym model dostępu do pamięci, aby zapewnić, że zaufane aplikacje i wszelkie inne oprogramowanie działające w bezpiecznym świecie będą mogły uzyskać dostęp do pamięci tylko wtedy, gdy jest ona wyłączną własnością bezpiecznego świata lub została mu wyraźnie udostępniona za pomocą FF -A. W systemie z S-EL2 wymuszanie modelu dostępu do pamięci powinno być wykonywane przez moduł Secure Partition Manager Core (SPMC), taki jak Hafnium , który utrzymuje 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 tabele stron etapu 1.

Jeśli 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 sprzętowe może bezpiecznie obsłużyć nieobsługiwane SMC, ponieważ oprogramowanie sprzętowe rozumie środki ostrożności potrzebne do utrzymania izolacji pVM.

Monitor maszyny wirtualnej

crosvm to monitor maszyn wirtualnych (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 dzięki zastosowaniu języka programowania Rust i piaskownicy wokół urządzeń wirtualnych w celu ochrony jądra hosta. Więcej informacji na temat crosvm można znaleźć w jego oficjalnej dokumentacji tutaj .

Deskryptory plików i ioctl

KVM udostępnia urządzenie znakowe /dev/kvm przestrzeni użytkownika za pomocą ioctl tworzących API KVM. Ioctl należą do następujących kategorii:

  • Systemowe ioctl wysyła zapytania i ustawia atrybuty globalne, które wpływają na cały podsystem KVM, a także tworzy maszyny pVM.
  • Ioctl maszyny wirtualnej wysyła zapytania i ustawia atrybuty, które tworzą wirtualne procesory (vCPU) i urządzenia oraz wpływają na całą maszynę pVM, na przykład obejmując układ pamięci oraz liczbę wirtualnych procesorów (vCPU) i urządzeń.
  • ioctls vCPU wysyła zapytania i ustawia atrybuty kontrolujące działanie pojedynczego wirtualnego procesora.
  • Device ioctls wysyła zapytania i ustawia atrybuty kontrolujące działanie pojedynczego urządzenia wirtualnego.

Każdy proces crosvm uruchamia dokładnie jedną instancję maszyny wirtualnej. Proces ten wykorzystuje systemowy ioctl KVM_CREATE_VM do utworzenia deskryptora pliku maszyny wirtualnej, którego można użyć do wystawienia poleceń ioctl pVM. Ioctl KVM_CREATE_VCPU lub KVM_CREATE_DEVICE na FD maszyny wirtualnej 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 polegające na uruchomieniu kodu gościa.

Wewnętrznie crosvm rejestruje deskryptory plików maszyny wirtualnej w jądrze przy użyciu interfejsu epoll wyzwalanego zboczem. Jądro następnie powiadamia crosvm za każdym razem, gdy w którymkolwiek z deskryptorów plików pojawi się nowe oczekujące zdarzenie.

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 zostanie przekazana flaga --protected-vm , do wysyłania zapytań i rezerwowania odpowiedniej ilości pamięci dla oprogramowania układowego pVM, a następnie do włączania trybu chronionego.

Alokacja pamięci

Jednym z głównych obowiązków 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 sprzętowe pVM 0x7FE00000
Pamięć urządzenia 0x10000 - 0x40000000

Pamięć fizyczna jest przydzielana za pomocą mmap i przekazywana maszynie wirtualnej w celu zapełnienia jej obszarów pamięci, zwanych memslotami , za pomocą ioctl KVM_SET_USER_MEMORY_REGION . Cała pamięć pVM gościa jest zatem przypisywana do instancji crosvm, która nią zarządza, co może skutkować zakończeniem procesu (zakończeniem maszyny wirtualnej), jeśli hostowi zacznie brakować wolnej pamięci. Po zatrzymaniu maszyny wirtualnej pamięć jest automatycznie czyszczona przez hypervisor i zwracana do jądra hosta.

W przypadku zwykłego KVM VMM zachowuje dostęp do całej pamięci gościa. Dzięki pKVM pamięć gościa jest usuwana z fizycznej przestrzeni adresowej hosta, gdy jest przekazywana gościowi. Jedynym wyjątkiem jest pamięć jawnie współdzielona przez gościa, na przykład w przypadku urządzeń virtio.

Regiony MMIO w przestrzeni adresowej gościa pozostają niezmapowane. Dostęp gościa do tych regionów jest pułapką i skutkuje zdarzeniem we/wy na FD maszyny wirtualnej. Mechanizm ten służy do implementacji urządzeń wirtualnych. W trybie chronionym gość musi potwierdzić, że obszar 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 planowany przez program planujący systemu Linux hosta. Wątek wywołuje ioctl KVM_RUN na FD vCPU, co powoduje przełączenie hypervisora ​​do kontekstu gościa vCPU. Harmonogram hosta uwzględnia czas spędzony w kontekście gościa jako czas używany przez odpowiedni wątek vCPU. KVM_RUN zwracany jest w przypadku zdarzenia, które musi obsłużyć VMM, takiego jak operacje we/wy, koniec przerwania lub zatrzymanie procesora wirtualnego. 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 hypervisora ​​EL2, którego nie można wywłaszczyć. Sama pVM gościa nie ma mechanizmu kontrolowania tego zachowania.

Ponieważ wszystkie wątki vCPU są zaplanowane jak inne zadania w przestrzeni użytkownika, podlegają 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/harmonogramu i nie tylko.

Urządzenia wirtualne

crosvm obsługuje wiele urządzeń, w tym następujące:

  • virtio-blk dla obrazów dysków kompozytowych, 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 sprzętowe 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 załadowanie bezpiecznego rozruchu i uzyskanie unikalnego sekretu pVM. Użycie pvmfw nie jest ograniczone do żadnego konkretnego systemu operacyjnego, takiego jak Microdroid , o ile system operacyjny jest obsługiwany przez crosvm i został poprawnie podpisany.

Plik binarny pvmfw jest przechowywany na partycji flash o tej samej nazwie i jest aktualizowany za pomocą OTA .

Uruchomienie urządzenia

Do procedury rozruchu urządzenia obsługującego pKVM dodawana jest następująca sekwencja kroków:

  1. Program ładujący Androida (ABL) ładuje pvmfw ze swojej partycji do pamięci i sprawdza obraz.
  2. ABL uzyskuje sekrety mechanizmu kompozycji identyfikatorów urządzeń (DICE) (złożone identyfikatory urządzeń (CDI) i łańcuch certyfikatów rozruchowych (BCC)) z głównego źródła zaufania.
  3. ABL dokonuje pomiaru i wyprowadza DICE sekretów pvmfw (CDI) i dołącza je do pliku binarnego pvmfw.
  4. ABL dodaje węzeł obszaru pamięci zarezerwowanej linux,pkvm-guest-firmware-memory do DT, opisując lokalizację i rozmiar pliku binarnego pvmfw oraz sekrety uzyskane w poprzednim kroku.
  5. ABL przekazuje kontrolę Linuksowi, a Linux inicjuje pKVM.
  6. pKVM odwzorowuje obszar pamięci pvmfw z tabel stron hosta etapu 2 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

Podczas tworzenia maszyny pVM crosvm (lub inny VMM) musi utworzyć wystarczająco dużą szczelinę pamięci, aby hiperwizor mógł zapełnić ją 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 dodatkowych vCPU). Pozostałe rejestry są zarezerwowane i stanowią część ABI hypervisor-pvmfw.

Kiedy maszyna pVM jest uruchomiona, hypervisor najpierw przekazuje kontrolę nad głównym procesorem vCPU pvmfw. Oprogramowanie sprzętowe oczekuje, że crosvm załaduje do pamięci jądro podpisane AVB, którym może być program ładujący lub dowolny inny obraz, oraz niepodpisany FDT do pamięci ze znanymi przesunięciami. pvmfw sprawdza podpis AVB i, jeśli się powiedzie, generuje drzewo zaufanych urządzeń na podstawie otrzymanego FDT, usuwa jego sekrety z pamięci i rozgałęzia się do punktu wejścia ładunku. Jeśli jeden z etapów weryfikacji zakończy się niepowodzeniem, oprogramowanie sprzętowe wysyła hiperwywołanie PSCI SYSTEM_RESET .

Pomiędzy rozruchami informacje o instancji pVM są przechowywane na partycji (urządzeniu virtio-blk) i szyfrowane za pomocą klucza pvmfw, aby zapewnić, że po ponownym uruchomieniu sekret zostanie przekazany właściwej instancji.