Architektura AVF

Android udostępnia implementację referencyjną wszystkich komponentów potrzebnych do implementacji platformy wirtualizacji Androida. Obecnie ta implementacja jest ograniczona do ARM64. Na tej stronie znajdziesz informacje o architekturze frameworka.

Tło

Architektura Arm umożliwia stosowanie do 4 poziomów wyjątków, z tym że poziom 0 (EL0) jest najmniej uprzywilejowanym, a poziom 3 (EL3) – najbardziej uprzywilejowanym. Większa część kodu źródłowego Androida (wszystkie komponenty w przestrzeni użytkownika) działa na poziomie EL0. Pozostała część 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 pVM na poziomie EL1/EL0 z gwarancjami dotyczącymi integralności i poufności.

Hipernadzorca

Ochrona maszyn wirtualnych z jądrem (pKVM) opiera się na hipervisorze Linux KVM, który został rozszerzony o możliwość ograniczania dostępu do ładunków danych działających na maszynach wirtualnych gościa oznaczonych jako „chronione” w momencie ich utworzenia.

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 lub nowszych). W jednym z tych trybów, powszechnie nazywanym trybem bezVHE, kod hypervisora jest wyodrębniany z obrazu jądra podczas uruchamiania i instalowany na poziomie EL2, podczas gdy samo jądro działa na poziomie EL1. Chociaż jest częścią kodu źródłowego systemu Linux, komponent EL2 w KVM jest małym komponentem odpowiedzialnym za przełączanie między wieloma komponentami EL1. Komponent hiperwizora jest kompilowany w systemie Linux, ale znajduje się w oddzielnej, dedykowanej sekcji pamięci obrazu vmlinux. pKVM wykorzystuje tę konstrukcję, rozszerzając kod hiperwizora o nowe funkcje, które umożliwiają nakładanie ograniczeń na jądro i przestrzeń użytkownika hosta Androida oraz ograniczanie dostępu hosta do pamięci gościa i hiperwizora.

Moduły dostawców pKVM

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

Aby dowiedzieć się, jak zaimplementować i wczytać moduł dostawcy pKVM, przeczytaj artykuł Wdrażanie i wczytywanie modułu dostawcy pKVM.

Procedura rozruchu

Poniższy rysunek przedstawia procedurę uruchamiania pKVM:

Procedura uruchamiania pKVM

Rysunek 1. Procedura uruchamiania pKVM

  1. Program rozruchowy przechodzi do ogólnego jądra na poziomie EL2.
  2. Kernel ogólny wykrywa, że działa na poziomie EL2, i obniża swoje uprawnienia do poziomu EL1, podczas gdy pKVM i jego moduły nadal działają na poziomie EL2. Dodatkowo w tym czasie wczytywane są moduły dostawców pKVM.
  3. Ogólne jądro uruchamia się normalnie, wczytując wszystkie niezbędne sterowniki urządzeń, aż do dotarcia do przestrzeni użytkownika. W tym momencie usługa pKVM jest już gotowa i obsługuje tabele stron na etapie 2.

Procedura rozruchu ufa programowi rozruchowemu, który zachowuje integralność obrazu jądra tylko podczas wczesnego rozruchu. Gdy ją pozbawisz uprawnień, hipernadzorca nie będzie jej już ufać, dlatego będzie ona musiała się chronić nawet wtedy, gdy ją naruszy.

Kernel Androida i hypervisor w tym samym pliku binarnym umożliwiają bardzo ścisłe połączenie interfejsu komunikacyjnego. Ta ścisła zależność gwarantuje atomowe aktualizacje obu komponentów, co eliminuje potrzebę utrzymywania stabilnego interfejsu między nimi. Zapewnia też dużą elastyczność bez kompromisów w zakresie długoterminowej konserwacji. Dzięki ścisłemu połączeniu można też optymalizować wydajność, gdy oba komponenty mogą ze sobą współpracować, nie wpływając na gwarancje bezpieczeństwa zapewniane przez hypervisora.

Ponadto wdrożenie GKI w ekosystemie Androida automatycznie umożliwia wdrożenie hypervisora pKVM na urządzeniach z Androidem w tym samym binarnym pliku co jądro.

Ochrona dostępu do pamięci procesora

Architektura Arm określa jednostkę zarządzania pamięcią (MMU) podzieloną na 2 niezależne etapy, z których każdy może służyć do implementowania tłumaczenia adresów i kontroli dostępu do różnych części pamięci. MMU etapu 1 jest kontrolowany przez EL1 i umożliwia przetłumaczenie adresu na pierwszym poziomie. MMU etapu 1 jest używane przez system Linux do zarządzania przestrzenią adresów wirtualnych udostępnianą poszczególnym procesom w przestrzeni użytkownika oraz ich własnymi przestrzeniami adresów wirtualnych.

MMU etapu 2 jest kontrolowany przez EL2 i umożliwia zastosowanie drugiej translacji adresu na adresie wyjściowym MMU etapu 1, co powoduje powstanie adresu fizycznego (PA). Przetłumaczone dane z drugiego etapu mogą być używane przez hypervisory do kontrolowania i przekształcania dostępów do pamięci ze wszystkich maszyn wirtualnych gościa. Jak widać na rysunku 2, gdy oba etapy tłumaczenia są włączone, adres wyjściowy etapu 1 nazywa się pośrednim adresem fizycznym (IPA). Uwaga: adres wirtualny (VA) jest tłumaczony na adres IPA, a potem na adres PA.

Ochrona dostępu do pamięci procesora

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

Do tej pory KVM działa z włączoną fazą 2 podczas uruchamiania gości i z wyłączoną fazą 2 podczas uruchamiania jądra hosta Linux. Ta architektura umożliwia dostęp do pamięci z MMU hosta etapu 1 przez MMU etapu 2, co pozwala na nieograniczony dostęp z hosta do stron pamięci gościa. Z drugiej strony, pKVM umożliwia ochronę etapu 2 nawet w kontekście hosta, a hypervisor odpowiada za ochronę stron pamięci gościa zamiast hosta.

KVM w pełni wykorzystuje tłumaczenie adresów na etapie 2, aby wdrożyć złożone mapowania IPA/PA dla gości, co tworzy iluzję ciągłej pamięci dla gości pomimo fizycznej fragmentacji. Jednak w przypadku hosta MMU etapu 2 jest ograniczone tylko do kontroli dostępu. Etap 2 hosta jest mapowany na tożsamość, co zapewnia, że ciągła pamięć w przestrzeni IPA hosta jest ciągła w przestrzeni PA. Ta architektura umożliwia korzystanie z dużych mapowań w tabeli stron, a w konsekwencji zmniejsza obciążenie bufora pośredniego tłumaczenia (TLB). Ponieważ mapowanie tożsamości może być indeksowane przez PA, host stage 2 jest również używany do śledzenia własności strony bezpośrednio w tabeli stron.

Ochrona przed bezpośrednim dostępem do pamięci (DMA)

Jak już wspomnieliśmy, odłączanie stron gościa od hosta Linuxa w tabelach stron procesora jest konieczne, ale niewystarczające do ochrony pamięci gościa. pKVM musi też chronić przed dostępem do pamięci przez urządzenia z obsługą DMA pod kontrolą jądra hosta oraz przed możliwością ataku DMA zainicjowanego przez złośliwego hosta. Aby zapobiec dostępowi takiego urządzenia do pamięci gościa, pKVM wymaga sprzętowej jednostki zarządzania pamięcią wejścia/wyjścia (IOMMU) dla każdego urządzenia z możliwością DMA w systemie, jak pokazano na rysunku 3.

Ochrona przed dostępem do pamięci Dma

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

Co najmniej sprzęt IOMMU zapewnia środki do przyznawania i odwoływania dostępu do odczytu/zapisu na urządzeniu do pamięci fizycznej na poziomie strony. Jednak sprzętowy IOMMU ogranicza korzystanie z urządzeń w pVM, ponieważ zakłada on etap 2 z mapowaniem tożsamości.

Aby zapewnić izolację między maszynami wirtualnymi, transakcje pamięci generowane w imieniu różnych obiektów muszą być możliwe do odróżnienia przez IOMMU, aby można było użyć odpowiedniego zestawu tabel stron do przetłumaczenia.

Ponadto zmniejszenie ilości kodu związanego z procesorem SoC na poziomie EL2 jest kluczową strategią w celu zmniejszenia całkowitej zaufanej bazy komputerowej (TCB) w pKVM i działa na niekorzyść włączania sterowników IOMMU do hypervisora. Aby rozwiązać ten problem, gospodarz na poziomie 1 jest odpowiedzialny za dodatkowe zadania związane z zarządzaniem IOMMU, takie jak zarządzanie zasilaniem, inicjalizacja i w odpowiednich przypadkach obsługa przerwań.

Jednak przeniesienie kontroli nad stanem urządzenia na hosta wiąże się z dodatkowymi wymaganiami dotyczącymi interfejsu programowania sprzętu IOMMU, aby zapewnić, że weryfikacji uprawnień nie można obejść innymi sposobami, na przykład po resecie urządzenia.

Standardowa i dobrze obsługiwana jednostka IOMMU na urządzeniach z procesorami ARM, która umożliwia zarówno izolację, jak i przypisanie bezpośrednie, to architektura jednostki zarządzania pamięcią systemu Arm (SMMU). Ta architektura to zalecane rozwiązanie referencyjne.

Własność pamięci

Podczas uruchamiania zakłada się, że cała pamięć poza pamięcią hipernadzorcy należy do hosta i jest śledzona przez hipernadzorcę. Gdy tworzy się pVM, host przekazuje mu strony pamięci, aby umożliwić jego uruchomienie, a hypervisor przekazuje własność tych stron z hosta do pVM. Dlatego hypervisor wprowadza ograniczenia kontroli dostępu w tabeli stron etapu 2 gospodarza, aby uniemożliwić mu ponowny dostęp do stron i zapewnić poufność danych gościa.

Komunikacja między gospodarzem a gośćmi jest możliwa dzięki kontrolowanemu udostępnianiu pamięci. Użytkownicy mogą udostępniać niektóre ze swoich stron hostowi za pomocą hiperwywołania, które instruuje hypervisora, aby ponownie przypisał te strony do tabeli stron hosta etapu 2. Podobnie komunikacja gospodarza z TrustZone jest możliwa dzięki operacjom udostępniania lub wypożyczenia pamięci, które są dokładnie monitorowane i kontrolowane przez pKVM za pomocą specyfikacji Firmware Framework for Arm (FF-A).

Ponieważ wymagania dotyczące pamięci pVM mogą się zmienić z czasem, udostępniamy wywołanie hiperłącza, które pozwala przekazać prawo własności do określonych stron należących do wywołującego z powrotem do hosta. W praktyce hiperwywołanie jest używane z protokołem wirtualnej pamięci wirtualnej, aby umożliwić VMM żądanie pamięci z pVM i aby pVM powiadamiał VMM o zwolnionych stronach w kontrolowany sposób.

Hypervisor odpowiada za śledzenie własności wszystkich stron pamięci w systemie oraz za to, czy są one udostępniane czy wypożyczone innym podmiotom. Większość tego śledzenia stanu odbywa się za pomocą metadanych dołączonych do tabel stron hosta i gości na etapie 2, przy użyciu zarezerwowanych bitów w elementach tabeli stron (PTE), które, jak sugeruje ich nazwa, są zarezerwowane dla oprogramowania.

Host musi zadbać o to, aby nie próbował uzyskać dostępu do stron, które stały się niedostępne z powodu działania hypervisora. Nielegalny dostęp do hosta powoduje, że hypervisor wstrzykuje do hosta symetryczną wyjątek, co może spowodować, że odpowiadające zadanie w przestrzeni użytkownika otrzyma sygnał SEGV lub że jądro hosta ulegnie awarii. Aby zapobiec przypadkowemu dostępowi, kernel hosta nie zezwala na przełączanie ani scalanie stron udostępnionych gościom.

Obsługa przerw i minutników

Przerwy są istotną częścią sposobu, w jaki gość wchodzi w interakcje z urządzeniami, oraz komunikacji między procesorami, w której przerwy między procesorami (IPI) są głównym mechanizmem komunikacji. Model KVM polega na delegowaniu wszystkich operacji zarządzania przerwaniem wirtualnym do hosta w EL1, który w tym celu zachowuje się jak niezaufana część hipernadzorcy.

pKVM zapewnia pełną emulację Generic Interrupt Controller w wersji 3 (GICv3) na podstawie dotychczasowego kodu KVM. Timer i IPI są obsługiwane w ramach tego nieufnego kodu emulacji.

Obsługa GICv3

Interfejs między poziomami EL1 i EL2 musi zapewniać, że pełny stan przerwania jest widoczny dla hosta EL1, w tym kopie rejestrów hiperwizora związane z przerwaniami. Zazwyczaj jest to możliwe dzięki używaniu wspólnych regionów pamięci, po jednym na procesor wirtualny (vCPU).

Kod obsługi rejestru systemu w czasie wykonywania można uprościć, aby obsługiwał tylko rejestrowanie przerwania generowanego przez oprogramowanie (SGIR) i rejestrowanie przerwania dezaktywowanego (DIR). Architektura wymaga, aby te rejestry zawsze były przechwytywane na poziomie EL2, podczas gdy inne pułapki były do tej pory przydatne tylko do łagodzenia błędów. Pozostałe zadania są wykonywane przez sprzęt.

Po stronie MMIO wszystko jest emulowane na poziomie EL1, wykorzystując ponownie całą bieżącą infrastrukturę w KVM. Na koniec: oczekuj na przerwanie (WFI) jest zawsze przekazywane do EL1, ponieważ jest to jedna z podstawowych prymitywów harmonogramowania używanych przez KVM.

Obsługa timera

Wartość funkcji porównywania dla wirtualnego timera musi być udostępniona procesowi EL1 w każdym blokującym WFI, aby EL1 mógł wstrzyknąć przerwania timera, gdy procesor vCPU jest zablokowany. Fizyczny timer jest całkowicie emulowany, a wszystkie pułapki są przekazywane do EL1.

Obsługa MMIO

Aby komunikować się z monitorem maszyny wirtualnej (VMM) i wykonywać emulację GIC, pułapki MMIO muszą być przekazywane z powrotem do hosta w EL1 w celu dalszego sortowania. Funkcja pKVM wymaga:

  • IPA i zakres dostępu
  • Dane w przypadku zapisu
  • Endianność procesora w momencie przechwytywania

Dodatkowo pułapki z rejestrem ogólnego przeznaczenia (GPR) jako źródłem/miejscem docelowym są przekierowywane za pomocą abstrakcyjnego pseudorejestru transferu.

Interfejsy dla gości

Użytkownik może komunikować się z innym chronionym użytkownikiem za pomocą połączenia hiperłącza i dostępu do pamięci w zamkniętym regionie. Połączenia hipertekstowe są udostępniane zgodnie ze standardem SMCCC, z zakresem zarezerwowanym na potrzeby przydziału dostawcy przez KVM. Poniższe hiperwywołania są szczególnie ważne dla gości pKVM.

Hiperwywołania ogólne

  • PSCI udostępnia standardowy mechanizm, który umożliwia użytkownikowi kontrolowanie cyklu życia jego vCPU, w tym włączania, wyłączania i zamykania systemu.
  • TRNG udostępnia standardowy mechanizm, który umożliwia gościowi żądanie entropii z pKVM, który przekazuje wywołanie do EL3. Ten mechanizm jest szczególnie przydatny w przypadku hosta, któremu nie można ufać, że zwirtualizuje sprzętowy generator liczb losowych (RNG).

Hiperwywołania pKVM

  • Udostępnianie wspomnień gospodarzowi. Cała pamięć gościa jest początkowo niedostępna dla hosta, ale dostęp hosta jest niezbędny do komunikacji z pamięcią współdzieloną i do urządzeń parawirtualnych, które korzystają ze współdzielonych buforów. Hypercall do udostępniania i odzyskiwania stron z hostem pozwala gościowi dokładnie określić, które części pamięci są dostępne dla reszty systemu Android bez konieczności użycia protokołu nawiązywania połączenia.
  • Zwolnienie pamięci przez 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 o wymaganiach pamięci, które zmieniają się z czasem. Hiperwywołanie relinquish pozwala gościowi wyraźnie przekazać prawa własności do stron gospodarzowi bez konieczności zakończenia sesji gościa.
  • Przechwytywanie dostępu do pamięci przez hosta. Tradycyjnie, jeśli gość KVM uzyskuje dostęp do adresu, który nie odpowiada prawidłowemu regionowi pamięci, wątek vCPU przechodzi do hosta, a dostęp jest zwykle używany do MMIO i emulowany przez VMM w przestrzeni użytkownika. Aby ułatwić to przetwarzanie, pKVM musi przekazywać szczegóły dotyczące instrukcji, która spowodowała błąd, takie jak jej adres, parametry rejestru i potencjalnie ich zawartość, do hosta. Może to nieumyślnie ujawnić poufne dane z chronionego gościa, jeśli 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ł hiperwywołanie, aby zidentyfikować zakres IPA, dla którego dostępy są dozwolone do pułapki z powrotem do hosta. To rozwiązanie nazywa się MMIO guard.

Wirtualne urządzenie I/O (virtio)

Virtio to popularny, przenośny i dojrzały standard implementowania i interakcji z urządzeniami parawirtualnymi. Większość urządzeń udostępnianych chronionym gościom jest implementowana za pomocą virtio. Virtio jest też podstawą implementacji vsock służącej do komunikacji między chronionym gościem a resztą systemu Android.

Urządzenia virtio są zwykle implementowane w przestrzeni użytkownika hosta przez VMM, który przechwytuje dostęp do zablokowanej pamięci z poziomu gościa do interfejsu MMIO urządzenia virtio i emuluje oczekiwane działanie. Dostęp MMIO jest stosunkowo drogi, ponieważ każdy dostęp do urządzenia wymaga dwukierunkowej komunikacji z VMM i z powrotem, więc większość rzeczywistego przesyłania danych między urządzeniem a gościem odbywa się za pomocą zestawu kolejek wirtualnych 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 projektowaniu kolejki wirtualnej, która może zawierać wskaźniki do buforów w gościu, do których emulacja urządzenia ma uzyskiwać bezpośredni dostęp.

Chociaż opisana wcześniej hiperwywołanie do udostępniania pamięci może służyć do udostępniania buforów danych virtio z gościa do hosta, udostępnianie to jest koniecznie wykonywane z dokładnością do strony i może prowadzić do ujawnienia większej ilości danych niż to wymagane, jeśli rozmiar bufora jest mniejszy niż rozmiar strony. Zamiast tego gość jest skonfigurowany tak, aby przydzielić zarówno kolejki wirtualne, jak i odpowiadające im bufory danych z okna stałej pamięci współdzielonej, a dane są kopiowane (przesyłane) do i z okna zgodnie z wymaganiami.

Urządzenie wirtualne

Rysunek 4. Urządzenie Virtio

Interakcja z TrustZone

Goście nie mogą wchodzić w interakcje bezpośrednio z TrustZone, ale gospodarz musi mieć możliwość wywoływania SMC w bezpiecznym środowisku. Te wywołania mogą określać bufory pamięci z adresami fizycznymi, które są niedostępne dla hosta. Ponieważ oprogramowanie zabezpieczające zazwyczaj nie ma dostępu do bufora, złośliwy host może użyć tego bufora do przeprowadzenia ataku polegającego na wprowadzaniu w błąd (analogicznego do ataku DMA). Aby zapobiegać takim atakom, pKVM przechwytuje wszystkie wywołania SMC hosta do EL2 i działa jako serwer pośredniczący między hostem a bezpiecznym monitorem na poziomie EL3.

Wywołania PSCI z hosta są przekierowywane do oprogramowania układowego EL3 z minimalnymi modyfikacjami. W szczególności punkt wejścia dla procesora, który łączy się z siecią lub wznawia działanie po zawieszeniu, jest przepisany tak, aby tabela stron etapu 2 była instalowana na poziomie EL2 przed powrotem do hosta na poziomie EL1. Podczas uruchamiania ta ochrona jest egzekwowana przez pKVM.

Ta architektura opiera się na procesorze system on a chip (SoC) obsługującym PSCI, najlepiej z aktualną wersją TF-A jako oprogramowaniem układu EL3.

Firmware Framework for Arm (FF-A) standaryzuje interakcje między normalnym a bezpiecznym światem, szczególnie w przypadku bezpiecznego hypervisora. Większość specyfikacji definiuje mechanizm udostępniania pamięci w bezpiecznym środowisku, korzystając z wspólnego formatu wiadomości i dobrze zdefiniowanego modelu uprawnień dla stron docelowych. pKVM działa jako serwer pośredniczący dla wiadomości FF-A, aby host nie próbował udostępniać pamięci stronie zabezpieczonej, do której nie ma wystarczających uprawnień.

Ta architektura opiera się na oprogramowaniu w środowisku bezpiecznym, które egzekwuje model dostępu do pamięci. Dzięki temu zaufane aplikacje i inne oprogramowanie działające w środowisku bezpiecznym mogą uzyskać dostęp do pamięci tylko wtedy, gdy jest ona własnością tego środowiska lub została udostępniona za pomocą FF-A. W systemie z S-EL2 wymuszanie modelu dostępu do pamięci powinno być realizowane przez moduł Secure Partition Manager Core (SPMC), taki jak Hafnium, który zarządza tabelami stron etapu 2 w bezpiecznym świecie. W systemie bez S-EL2 TEE może zamiast tego wymusić model dostępu do pamięci za pomocą tabel stron etapu 1.

Jeśli wywołanie SMC do EL2 nie jest wywołaniem PSCI ani wiadomością zdefiniowaną w FF-A, nieobsługiwane wywołania SMC są przekierowywane do EL3. Zakładamy, że bezpieczne (czyli zaufane) oprogramowanie układowe może bezpiecznie obsługiwać nieobsługiwane SMC, ponieważ rozumie środki ostrożności potrzebne do zachowania izolacji pVM.

Monitor maszyny wirtualnej

crosvm to monitor maszyn wirtualnych (VMM), który uruchamia maszyny wirtualne za pomocą interfejsu KVM systemu Linux. Unikalną cechą crosvm jest nacisk na bezpieczeństwo dzięki zastosowaniu języka programowania Rust i piaskownicy dla urządzeń wirtualnych, która chroni jądro hosta. Więcej informacji o crosvm znajdziesz w oficjalnej dokumentacji tutaj.

Deskryptory plików i ioctly

KVM udostępnia urządzenie znakowe /dev/kvm w przestrzeni użytkownika za pomocą ioctls, które tworzą interfejs KVM API. Polecenia ioctl należą do tych kategorii:

  • System ioctls zapytanie i ustawić atrybuty globalne, które wpływają na cały podsystem KVM, oraz utworzyć pVM.
  • Zapytania ioctl maszyny wirtualnej i atrybuty zestawów, które tworzą procesory wirtualne (vCPU) i urządzenia, oraz wpływają na całą maszynę pVM, takie jak układ pamięci i liczba vCPU oraz urządzeń.
  • Zapytania ioctl vCPU i atrybuty ustawień, które kontrolują działanie pojedynczego procesora wirtualnego.
  • Zapytania ioctl urządzenia i ustawienia atrybutów, które kontrolują działanie pojedynczego wirtualnego urządzenia.

Każdy proces crosvm uruchamia dokładnie jedną instancję maszyny wirtualnej. W tym procesie używamy polecenia ioctl systemu KVM_CREATE_VM do utworzenia deskryptora pliku maszyny wirtualnej, którego można używać do wydawania poleceń ioctl pVM. Polecenie ioctl KVM_CREATE_VCPU lub KVM_CREATE_DEVICE na gniazdach FD maszyny wirtualnej tworzy vCPU lub urządzenie i zwraca deskryptor pliku wskazujący na nowy zasób. Polecenie ioctl na vCPU lub gniazdach FD urządzenia może służyć do sterowania urządzeniem utworzonym za pomocą polecenia ioctl na gniazdach FD maszyny wirtualnej. W przypadku procesorów wirtualnych obejmuje to ważne zadanie polegające na uruchamianiu kodu gościa.

Wewnętrznie crosvm rejestruje deskryptory plików maszyny wirtualnej w jądrze za pomocą interfejsu epoll. Następnie jądro wysyła powiadomienie do crosvm, gdy w jakimś deskryptorze pliku pojawi się nowe zdarzenie.

pKVM dodaje nową funkcję KVM_CAP_ARM_PROTECTED_VM, która może służyć do uzyskiwania informacji o środowisku pVM i konfigurowania trybu chronionego dla maszyny wirtualnej. crossvm używa tej funkcji podczas tworzenia pVM, jeśli przekazano mu flagę --protected-vm, aby zapytać i zarezerwować odpowiednią ilość pamięci dla oprogramowania układowego pVM, a następnie włączyć tryb chroniony.

Przydział pamięci

Jedną z głównych funkcji VMM jest przydzielanie pamięci maszynie wirtualnej i zarządzanie jej układem pamięci. crosvm generuje układ pamięci statyczne, który jest ogólnie opisany w tabeli poniżej.

FDT w trybie normalnym PHYS_MEMORY_END - 0x200000
Zwolnij miejsce ...
Ramdisk ALIGN_UP(KERNEL_END, 0x1000000)
Jądro 0x80080000
Program rozruchowy 0x80200000
FDT w trybie BIOS-u 0x80000000
Fizyczna baza pamięci 0x80000000
Oprogramowanie pVM 0x7FE00000
Pamięć urządzenia 0x10000 - 0x40000000

Pamięć fizyczna jest przydzielana za pomocą funkcji mmap i przekazywana maszynie wirtualnej, aby wypełnić jej regiony pamięci, zwane memslots, za pomocą ioctl KVM_SET_USER_MEMORY_REGION. W związku z tym cała pamięć pVM gościa jest przypisana do instancji crosvm, która nią zarządza, i może spowodować zabicie procesu (zakończenie maszyny wirtualnej), jeśli hostowi zacznie brakować wolnej pamięci. Gdy maszyna wirtualna zostanie zatrzymana, hypervisor automatycznie usunie pamięć i zwróci ją do jądra hosta.

W przypadku zwykłego KVM VMM zachowuje dostęp do całej pamięci gościa. W przypadku pKVM pamięć gościa jest odmapowywana z fizycznej przestrzeni adresowej hosta, gdy jest przekazywana do gościa. Jedynym wyjątkiem jest pamięć, którą gość udostępnia, na przykład w przypadku urządzeń wirtualnych.

Regiony MMIO w przestrzeni adresowej gościa pozostają niezmapowane. Dostęp gości do tych regionów jest blokowany i powoduje zdarzenie we/wy w przypadku FD maszyny wirtualnej. Ten mechanizm jest używany do implementowania urządzeń wirtualnych. W trybie chronionym gość musi potwierdzić, że region jego przestrzeni adresowej jest używany do MMIO za pomocą hiperwywołania, aby zmniejszyć ryzyko przypadkowego wycieku informacji.

Harmonogram

Każdy wirtualny procesor jest reprezentowany przez wątek POSIX i jest planowany przez gospodarza za pomocą planisty Linuxa. Wątek wywołuje ioctl KVM_RUN na FD procesora vCPU, co powoduje przełączenie hypervisora na kontekst procesora vCPU gościa. Harmonizer hosta uwzględnia czas spędzony w kontekście gościa jako czas wykorzystany przez odpowiedni wątek procesora wirtualnego. KVM_RUN zwraca wartość, gdy wystąpiło zdarzenie, które musi obsłużyć VMM, takie jak I/O, koniec przerwania lub zatrzymanie vCPU. VMM obsługuje to zdarzenie i ponownie wywołuje funkcję KVM_RUN.

Podczas KVM_RUN wątek może zostać przerwany przez planistę hosta, z wyjątkiem kodu hypervisora EL2, który nie może zostać przerwany. Sam gość pVM nie ma mechanizmu kontrolowania tego zachowania.

Wszystkie wątki procesora wirtualnego są zaplanowane tak jak inne zadania w przestrzeni użytkownika, dlatego podlegają wszystkim standardowym mechanizmom QoS. Każdy wątek vCPU może być przypisany do fizycznych procesorów, umieszczony w grupach procesorów, przyspieszony lub ograniczony za pomocą ograniczania wykorzystania zasobów. Możesz też zmienić jego priorytet lub strategię planowania.

Urządzenia wirtualne

crosvm obsługuje wiele urządzeń, w tym:

  • virtio-blk – do obrazu dysków złożonych, tylko do odczytu lub do odczytu i zapisu.
  • vhost-vsock do komunikacji z hostem
  • virtio-pci jako transport wirtualny
  • pl030 zegar czasu rzeczywistego (RTC)
  • 16550a UART do komunikacji szeregowej

oprogramowanie pVM;

Firmware pVM (pvmfw) to pierwszy kod wykonywany przez pVM, podobny do ROM-u startowego urządzenia fizycznego. Głównym celem pvmfw jest bootstrapping bezpiecznego rozruchu i wyprowadzenie unikalnego sekretu pVM. pvmfw nie jest ograniczony do używania z konkretnym systemem operacyjnym, takim jak Microdroid, o ile tylko system jest obsługiwany przez crosvm i został prawidłowo podpisany.

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

Uruchamianie urządzenia

Do procedury uruchamiania urządzenia z obsługą pKVM dodano następującą sekwencję czynności:

  1. Bootloader Androida (ABL) wczytuje pvmfw z partycji do pamięci i weryfikuje obraz.
  2. ABL uzyskuje tajne dane z urządzenia identyfikującego składającego się z kombinacji identyfikatorów (CDI) i łańcucha certyfikatów DICE od zaufanego źródła.
  3. ABL wyprowadza niezbędne identyfikatory CD dla pvmfw i dołącza je do pliku binarnego pvmfw.
  4. ABL dodaje do DT węzeł linux,pkvm-guest-firmware-memory zarezerwowanej pamięci, który opisuje lokalizację i rozmiar binarnego pliku pvmfw oraz wygenerowanych w poprzednim kroku tajemnic.
  5. ABL przekazuje kontrolę Linuksowi, a Linux inicjuje pKVM.
  6. pKVM odmapowuje region pamięci pvmfw z tabel stron hosta na etapie 2 i chroni go przed hostem (oraz gośćmi) przez cały czas działania urządzenia.

Po uruchomieniu urządzenia Microdroid jest uruchamiany zgodnie z instrukcjami podanymi w sekcji sekwencja uruchamiania w dokumentacji Microdroid.

uruchamianie pVM,

Podczas tworzenia pVM crosvm (lub inny VMM) musi utworzyć wystarczająco duży slot pamięci, który hypervisor wypełni obrazem pvmfw. VMM jest też ograniczony listą rejestrów, których wartość początkową może ustawić (x0-x14 dla głównego procesora wirtualnego, brak dla dodatkowych procesorów wirtualnych). Pozostałe rejestry są zarezerwowane i stanowią część ABI hypervisor-pvmfw.

Gdy pVM jest uruchamiany, hypervisor najpierw przekazuje kontrolę nad głównym procesorem vCPU do pvmfw. Oprogramowanie sprzętowe oczekuje, że crosvm załaduje jądro z podpisem AVB, który może być bootloaderem lub dowolnym innym obrazem, oraz nie podpisany FDT do pamięci w znanych przesunięciach. pvmfw weryfikuje podpis AVB i, jeśli to się powiedzie, generuje zaufane drzewo urządzenia z otrzymanego FDT, kasuje jego sekrety z pamięci i przechodzi do punktu wejścia ładunku. Jeśli któryś z etapów weryfikacji zakończy się niepowodzeniem, oprogramowanie sprzętowe wyśle hiperwywołanie PSCI SYSTEM_RESET.

Pomiędzy kolejnymi uruchomieniami informacje o instancji pVM są przechowywane na partycji (urządzeniu virtio-blk) i szyfrowane przy użyciu klucza tajnego pvmfw, aby po ponownym uruchomieniu klucz ten był udostępniany odpowiedniej instancji.