Android udostępnia implementację referencyjną wszystkich komponentów potrzebnych do wdrożenia platformy wirtualizacji Androida. Obecnie to rozwiązanie jest ograniczone do urządzeń z architekturą ARM64. Na tej stronie opisujemy architekturę platformy.
Tło
Architektura Arm umożliwia stosowanie maksymalnie 4 poziomów uprawnień, przy czym poziom 0 (EL0) ma najmniejsze uprawnienia, a poziom 3 (EL3) – największe. Największa część bazy kodu Androida (wszystkie komponenty przestrzeni użytkownika) działa na poziomie EL0. Pozostała część tego, co powszechnie nazywa się „Androidem”, to jądro systemu Linux, które działa na poziomie uprawnień EL1.
Warstwa EL2 umożliwia wprowadzenie hiperwizora, który pozwala na izolowanie pamięci i urządzeń w poszczególnych maszynach pVM na poziomach EL1/EL0, z solidnymi gwarancjami poufności i integralności.
Hipernadzorca
Chroniona maszyna wirtualna oparta na jądrze (pKVM) jest oparta na hiperwizorze Linux KVM, który został rozszerzony o możliwość ograniczania 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 rozszerzeń hosta wirtualizacji (VHE) (ARMv8.1 i nowsze). W jednym z tych trybów, powszechnie znanym jako tryb non-VHE, kod hiperwizora jest wyodrębniany z obrazu jądra podczas uruchamiania i instalowany na poziomie EL2, a samo jądro działa na poziomie EL1. Chociaż komponent EL2 KVM jest częścią kodu źródłowego systemu Linux, jest to niewielki komponent odpowiedzialny za przełączanie między wieloma poziomami EL1. Komponent hipernadzorcy jest kompilowany w systemie Linux, ale znajduje się w osobnej, dedykowanej sekcji pamięci obrazu vmlinux
. pKVM wykorzystuje tę konstrukcję, rozszerzając kod hipernadzorcy 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 hipernadzorcy.
Moduły dostawcy pKVM
Moduł dostawcy pKVM to moduł specyficzny dla sprzętu, który zawiera funkcje specyficzne dla urządzenia, takie jak sterowniki jednostki zarządzania pamięcią wejścia-wyjścia (IOMMU). Te moduły umożliwiają przenoszenie do pKVM funkcji zabezpieczeń wymagających dostępu na poziomie wyjątku 2 (EL2).
Aby dowiedzieć się, jak wdrożyć i wczytać moduł dostawcy pKVM, przeczytaj artykuł Wdrażanie modułu dostawcy pKVM.
Procedura uruchamiania
Na poniższym rysunku przedstawiono procedurę uruchamiania pKVM:
- Program rozruchowy wchodzi do ogólnego jądra na poziomie EL2.
- Ogólne jądro 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. W tym momencie są też ładowane moduły dostawcy pKVM.
- Ogólne jądro uruchamia się normalnie, wczytując wszystkie niezbędne sterowniki urządzeń, aż do osiągnięcia przestrzeni użytkownika. Na tym etapie pKVM jest już w użyciu i obsługuje tablice stron drugiego etapu.
Procedura uruchamiania ufa programowi rozruchowemu w zakresie utrzymania integralności obrazu jądra tylko na wczesnym etapie uruchamiania. Gdy jądro traci uprawnienia, hipernadzorca przestaje je uważać za zaufane. W takiej sytuacji hipernadzorca musi chronić się samodzielnie, nawet jeśli jądro zostanie naruszone.
Umieszczenie jądra Androida i hiperwizora w tym samym obrazie binarnym umożliwia bardzo ścisłe powiązanie interfejsu komunikacyjnego między nimi. Takie ścisłe powiązanie gwarantuje niepodzielne aktualizacje obu komponentów, co eliminuje konieczność utrzymywania stabilnego interfejsu między nimi i zapewnia dużą elastyczność bez obniżania długoterminowej możliwości konserwacji. Ścisłe powiązanie umożliwia też optymalizację wydajności, gdy oba komponenty mogą współpracować bez wpływu na gwarancje bezpieczeństwa zapewniane przez hiperwizor.
Ponadto wdrożenie 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 2 niezależne etapy, z których oba mogą być używane do implementowania 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 tłumaczenie adresów pierwszego poziomu. MMU na etapie 1 jest używana przez system Linux do zarządzania przestrzenią adresów wirtualnych udostępnianą każdemu procesowi w przestrzeni użytkownika i własną przestrzenią adresów wirtualnych.
MMU drugiego etapu jest kontrolowany przez EL2 i umożliwia zastosowanie drugiego tłumaczenia adresu do adresu wyjściowego MMU pierwszego etapu, co daje adres fizyczny (PA). Tłumaczenie na etapie 2 może być używane przez hiperwizory do kontrolowania i tłumaczenia dostępu do pamięci ze wszystkich maszyn wirtualnych gościa. Jak pokazano na rysunku 2, gdy oba etapy translacji są włączone, adres wyjściowy etapu 1 jest nazywany pośrednim adresem fizycznym (IPA). Uwaga: adres wirtualny (VA) jest tłumaczony na IPA, a następnie na PA.
Tradycyjnie KVM działa z włączonym tłumaczeniem na poziomie 2 podczas uruchamiania gości i z wyłączonym tłumaczeniem na poziomie 2 podczas uruchamiania jądra systemu Linux na hoście. Ta architektura umożliwia przekazywanie dostępu do pamięci z jednostki MMU hosta na etapie 1 do jednostki MMU na etapie 2, co pozwala na nieograniczony dostęp hosta do stron pamięci gościa. Z drugiej strony pKVM umożliwia ochronę na etapie 2 nawet w kontekście hosta i powierza hiperwizorowi ochronę stron pamięci gościa zamiast hosta.
KVM w pełni wykorzystuje translację adresów na etapie 2 do implementowania złożonych mapowań IPA/PA dla gości, co stwarza iluzję ciągłej pamięci dla gości pomimo fizycznej fragmentacji. Jednak użycie MMU etapu 2 na hoście jest ograniczone tylko do kontroli dostępu. Host na etapie 2 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 używanie dużych mapowań w tabeli stron, co zmniejsza obciążenie bufora TLB. Mapowanie tożsamości może być indeksowane przez PA, dlatego etap hosta 2 jest też używany do śledzenia własności strony bezpośrednio w tabeli stron.
Ochrona przed bezpośrednim dostępem do pamięci (DMA)
Jak wspomnieliśmy wcześniej, usunięcie mapowania stron gościa z tabel stron hosta Linux w procesorze jest niezbędnym, ale niewystarczającym krokiem w ochronie pamięci gościa. pKVM musi też chronić przed dostępem do pamięci dokonywanym przez urządzenia obsługujące DMA pod kontrolą jądra hosta oraz przed 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 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.
Sprzęt IOMMU zapewnia co najmniej możliwość przyznawania i cofania dostępu do odczytu/zapisu w pamięci fizycznej na poziomie strony. Sprzęt IOMMU ogranicza jednak używanie 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 podmiotów muszą być rozróżnialne przez IOMMU, aby można było użyć odpowiedniego zestawu tabel stron do tłumaczenia.
Dodatkowo zmniejszenie ilości kodu specyficznego dla SoC na poziomie EL2 jest kluczową strategią ograniczania ogólnej zaufanej bazy obliczeniowej (TCB) pKVM i jest sprzeczne z uwzględnieniem sterowników IOMMU w hiperwizorze. Aby rozwiązać ten problem, host na poziomie EL1 odpowiada za pomocnicze zadania związane z zarządzaniem IOMMU, takie jak zarządzanie zasilaniem, inicjowanie i w odpowiednich przypadkach obsługa przerwań.
Jednak przekazanie hostowi kontroli nad stanem urządzenia nakłada dodatkowe wymagania na interfejs programowania sprzętu IOMMU, aby zapewnić, że nie można pominąć sprawdzania uprawnień w inny sposób, np. po zresetowaniu urządzenia.
Standardowym i dobrze obsługiwanym modułem IOMMU na urządzeniach z architekturą Arm, który umożliwia zarówno izolację, jak i bezpośrednie przypisywanie, jest architektura Arm System Memory Management Unit (SMMU). Ta architektura jest zalecanym rozwiązaniem referencyjnym.
Własność pamięci
Podczas uruchamiania cała pamięć nieużywana przez hipernadzorcę jest uznawana za należącą do hosta i jest śledzona przez hipernadzorcę. Gdy uruchamiana jest maszyna pVM, host przekazuje strony pamięci, aby umożliwić jej uruchomienie, a hiperwizor przenosi własność tych stron z hosta na maszynę pVM. Hiperwizor umieszcza w tabeli stron hosta na etapie 2 ograniczenia dostępu, aby uniemożliwić mu ponowny dostęp do stron, zapewniając poufność gościowi.
Komunikacja między hostem a gośćmi jest możliwa dzięki kontrolowanemu udostępnianiu pamięci. Goście mogą udostępniać niektóre swoje strony hostowi za pomocą wywołania hiperwywołania, które instruuje hiperwizora, aby ponownie zmapował te strony w tabeli stron hosta na poziomie 2. Podobnie komunikacja hosta z TrustZone jest możliwa dzięki operacjom udostępniania lub wypożyczania pamięci. Wszystkie te operacje są ściśle monitorowane i kontrolowane przez pKVM za pomocą specyfikacji Firmware Framework for Arm (FF-A).
Wymagania dotyczące pamięci pVM mogą się zmieniać z czasem, dlatego udostępniamy wywołanie hiperwizora, które umożliwia przekazanie z powrotem do hosta własności określonych stron należących do wywołującego. W praktyce to wywołanie hipernadzorcy jest używane z protokołem balonu virtio, aby umożliwić VMM żądanie pamięci z pVM i powiadamianie VMM przez pVM o zwolnionych stronach w kontrolowany sposób.
Hiperwizor odpowiada za śledzenie własności wszystkich stron pamięci w systemie oraz za to, czy są one udostępniane lub wypożyczane innym podmiotom. Większość tego śledzenia stanu odbywa się za pomocą metadanych dołączonych do hosta i tabel stron gości na etapie 2, z użyciem zarezerwowanych bitów w pozycjach tabeli stron (PTE), które, jak sama nazwa wskazuje, są zarezerwowane do użytku przez oprogramowanie.
Host musi mieć pewność, że nie próbuje uzyskać dostępu do stron, które zostały udostępnione przez hiperwizor. Nielegalny dostęp do hosta powoduje wstrzyknięcie do niego przez hiperwizor synchronicznego wyjątku, co może skutkować otrzymaniem przez odpowiedzialne zadanie w przestrzeni użytkownika sygnału SEGV lub awarią jądra hosta. Aby zapobiec przypadkowemu dostępowi, strony przekazane gościom są wykluczane z możliwości zamiany lub scalenia przez jądro hosta.
Obsługa przerwań i liczniki czasu
Przerwania są niezbędne do interakcji gościa z urządzeniami i do komunikacji między procesorami, w której głównym mechanizmem są przerwania międzyprocesorowe (IPI). Model KVM polega na przekazaniu całego zarządzania wirtualnymi przerwaniami do hosta w EL1, który w tym celu zachowuje się jak niezaufana część hipernadzorcy.
pKVM oferuje pełną emulację kontrolera przerwań ogólnych w wersji 3 (GICv3) na podstawie istniejącego kodu KVM. Timery i IPI są obsługiwane w ramach tego niezaufanego kodu emulacji.
Obsługa GICv3
Interfejs między EL1 i EL2 musi zapewniać, że pełny stan przerwania jest widoczny dla hosta EL1, w tym kopie rejestrów hiperwizora związanych z przerwaniami. Zwykle odbywa się to za pomocą regionów pamięci współdzielonej, po jednym na każdy procesor wirtualny.
Kod obsługi czasu działania rejestru systemowego można uprościć, aby obsługiwał tylko rejestr przerwań generowanych przez oprogramowanie (SGIR) i rejestr dezaktywacji przerwań (DIR). Architektura wymaga, aby te rejestry zawsze powodowały pułapkę do EL2, podczas gdy inne pułapki były dotychczas przydatne tylko do łagodzenia błędów. Wszystkie pozostałe czynności są wykonywane przez sprzęt.
W przypadku MMIO wszystko jest emulowane na poziomie EL1, z wykorzystaniem całej obecnej infrastruktury w KVM. Wreszcie instrukcja Wait for Interrupt (WFI) jest zawsze przekazywana do poziomu uprawnień EL1, ponieważ jest to jeden z podstawowych elementów planowania używanych przez KVM.
Obsługa minutnika
Wartość komparatora wirtualnego timera musi być udostępniana na poziomie EL1 przy każdym przechwytywaniu instrukcji WFI, aby poziom EL1 mógł wstrzykiwać przerwania timera, gdy procesor wirtualny jest zablokowany. Fizyczny zegar jest w całości emulowany, a wszystkie przerwania są przekazywane do EL1.
Obsługa MMIO
Aby komunikować się z monitorem maszyny wirtualnej (VMM) i przeprowadzać emulację GIC, pułapki MMIO muszą być przekazywane z powrotem do hosta na poziomie EL1 w celu dalszej analizy. pKVM wymaga:
- IPA i rozmiar dostępu
- Dane w przypadku zapisu
- Kolejność bajtów procesora w momencie przechwycenia
Dodatkowo pułapki z rejestrem ogólnego przeznaczenia jako źródłem lub miejscem docelowym są przekazywane za pomocą abstrakcyjnego pseudorejestru transferu.
Interfejsy gości
Gość może komunikować się z chronionym gościem za pomocą kombinacji wywołań hiperwywołań i dostępu do pamięci w obszarach przechwyconych. Wywołania hiperwizora są udostępniane zgodnie ze standardem SMCCC, a zakres jest zarezerwowany na potrzeby przydziału dostawcy przez KVM. Te wywołania hiperwizora mają szczególne znaczenie dla gości pKVM.
Ogólne hiperwywołania
- PSCI zapewnia gościowi standardowy mechanizm kontrolowania cyklu życia jego vCPU, w tym włączania, wyłączania i zamykania systemu.
- TRNG zapewnia standardowy mechanizm, za pomocą którego gość może wysyłać do pKVM żądania dotyczące entropii. pKVM przekazuje wywołanie do EL3. Ten mechanizm jest szczególnie przydatny w sytuacjach, gdy nie można mieć pewności, że host zwirtualizuje sprzętowy generator liczb losowych (RNG).
Hiperwywołania pKVM
- Udostępnianie pamięci hostowi. Początkowo cała pamięć gościa jest niedostępna dla hosta, ale dostęp hosta jest niezbędny do komunikacji za pomocą pamięci współdzielonej i do urządzeń parawirtualizowanych, które korzystają ze współdzielonych buforów. Wywołania hiperwizora do udostępniania i cofania udostępniania stron hostowi pozwalają gościowi dokładnie określić, które części pamięci mają być dostępne dla reszty Androida, bez konieczności uzgadniania tego z hostem.
- Zwalnianie 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ć niewystarczający w przypadku długo działających maszyn wirtualnych, których wymagania dotyczące pamięci zmieniają się z czasem. Wywołanie hiperwizora
relinquish
umożliwia gościowi wyraźne przekazanie z powrotem hostowi własności stron bez konieczności zamykania gościa. - Przechwytywanie dostępu do pamięci na hoście. Zwykle, jeśli gość KVM uzyskuje dostęp do adresu, który nie odpowiada prawidłowemu regionowi pamięci, wątek vCPU kończy działanie na hoście, a dostęp jest zwykle używany w przypadku MMIO i emulowany przez VMM w przestrzeni użytkownika. Aby ułatwić obsługę, pKVM musi przekazywać do hosta szczegóły dotyczące instrukcji powodującej błąd, takie jak jej adres, parametry rejestru i potencjalnie ich zawartość. Jeśli pułapka nie została przewidziana, może to nieumyślnie ujawnić dane wrażliwe z chronionego gościa. pKVM rozwiązuje ten problem, traktując te błędy jako krytyczne, chyba że gość wcześniej wydał wywołanie hiperwywołania, aby zidentyfikować zakres IPA powodujący błąd jako zakres, w przypadku którego dostęp jest dozwolony w celu przechwytywania z powrotem do hosta. To rozwiązanie jest nazywane ochroną MMIO.
Wirtualne urządzenie wejścia/wyjścia (virtio)
Virtio to popularny, przenośny i sprawdzony standard implementacji urządzeń parawirtualizowanych i interakcji z nimi. Większość urządzeń udostępnianych gościom chronionym jest implementowana przy użyciu virtio. Virtio jest też podstawą implementacji vsock, która służy do komunikacji między chronionym gościem a pozostałą częścią Androida.
Urządzenia Virtio są zwykle implementowane w przestrzeni użytkownika hosta przez VMM, który przechwytuje pułapkowane dostępy do pamięci z 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 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 interfejsu virtio jest to, że host może dowolnie uzyskiwać dostęp do pamięci gościa. To założenie jest widoczne w konstrukcji kolejki wirtualnej, która może zawierać wskaźniki do buforów w systemie gościa, do których emulacja urządzenia ma mieć bezpośredni dostęp.
Opisane wcześniej hiperwywołania udostępniania pamięci mogą służyć do udostępniania buforów danych virtio z gościa do hosta, ale to udostępnianie jest koniecznie wykonywane z dokładnością do strony i może ujawniać więcej 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 z ustalonego okna pamięci współdzielonej, przy czym dane są kopiowane (przekazywane) do i z tego okna w razie potrzeby.
Interakcja z TrustZone
Goście nie mogą wchodzić w interakcję bezpośrednio z TrustZone, ale organizator musi mieć możliwość wykonywania wywołań SMC w bezpiecznym środowisku. Połączenia te mogą określać bufory pamięci adresowane fizycznie, które są niedostępne dla hosta. Ponieważ bezpieczne oprogramowanie zwykle nie wie o dostępności bufora, złośliwy host może użyć tego bufora do przeprowadzenia ataku typu „confused deputy” (analogicznego do ataku DMA). Aby zapobiec takim atakom, pKVM przechwytuje wszystkie wywołania SMC hosta do EL2 i działa jako proxy między hostem a bezpiecznym monitorem na poziomie 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, który jest włączany lub wznawia działanie po zawieszeniu, jest przepisywany tak, aby tabela stron drugiego etapu 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 układzie SoC obsługującym PSCI, najlepiej przy użyciu aktualnej wersji TF-A jako oprogramowania układowego EL3.
Standard Firmware Framework for Arm (FF-A) ujednolica interakcje między normalnym i bezpiecznym środowiskiem, szczególnie w przypadku obecności bezpiecznego hiperwizora. Ważna część specyfikacji określa mechanizm udostępniania pamięci bezpiecznemu środowisku za pomocą wspólnego formatu wiadomości i dobrze zdefiniowanego modelu uprawnień dla bazowych stron. pKVM przekazuje wiadomości FF-A, aby mieć pewność, że host nie próbuje udostępniać pamięci bezpiecznej stronie, do której nie ma wystarczających uprawnień.
Ta architektura opiera się na oprogramowaniu bezpiecznego środowiska, które wymusza model dostępu do pamięci, aby zapewnić, że zaufane aplikacje i inne oprogramowanie działające w bezpiecznym środowisku mogą uzyskiwać dostęp do pamięci tylko wtedy, gdy jest ona na wyłączność bezpiecznego środowiska lub została z nim wyraźnie udostępniona za pomocą FF-A. W systemie z S-EL2 egzekwowanie modelu dostępu do pamięci powinno być realizowane przez rdzeń menedżera bezpiecznych partycji (SPMC), np. Hafnium, który utrzymuje tablice stron drugiego etapu dla bezpiecznego środowiska. W systemie bez S-EL2 środowisko TEE może zamiast tego wymuszać model dostępu do pamięci za pomocą tabel stron na poziomie 1.
Jeśli wywołanie SMC do EL2 nie jest wywołaniem PSCI ani wiadomością zdefiniowaną przez FF-A, nieobsługiwane wywołania SMC są przekazywane do EL3. Zakłada się, że bezpieczne oprogramowanie układowe (które musi być zaufane) może bezpiecznie obsługiwać nieobsługiwane wywołania SMC, ponieważ 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 pomocą interfejsu KVM systemu Linux. Crosvm wyróżnia się naciskiem na bezpieczeństwo dzięki użyciu języka programowania Rust i piaskownicy wokół urządzeń wirtualnych, która chroni jądro hosta. Więcej informacji o crosvm znajdziesz w oficjalnej dokumentacji tutaj.
Deskryptory plików i ioctl
KVM udostępnia urządzenie znakowe /dev/kvm
przestrzeni użytkownika za pomocą wywołań ioctl, które składają się na interfejs API KVM. Te ioctle należą do tych kategorii:
- System ioctls wysyła zapytania i ustawia atrybuty globalne, które wpływają na cały podsystem KVM, oraz tworzy pVM.
- Wywołania ioctl maszyny wirtualnej wysyłają zapytania i ustawiają atrybuty, które tworzą wirtualne procesory i urządzenia, oraz wpływają na całą maszynę pVM, np. układ pamięci oraz liczbę wirtualnych procesorów i urządzeń.
- Wywołania ioctl procesora wirtualnego służą do wysyłania zapytań o atrybuty, które kontrolują działanie pojedynczego procesora wirtualnego, i do ustawiania tych atrybutów.
- Wywołania ioctl urządzenia wysyłają zapytania i ustawiają atrybuty, które kontrolują działanie pojedynczego urządzenia wirtualnego.
Każdy proces crosvm uruchamia dokładnie 1 instancję maszyny wirtualnej. W tym procesie używany jest system KVM_CREATE_VM
ioctl do utworzenia deskryptora pliku maszyny wirtualnej, którego można używać do wydawania poleceń ioctl pVM. Wywołanie KVM_CREATE_VCPU
lub KVM_CREATE_DEVICE
ioctl na deskryptorze pliku maszyny wirtualnej tworzy wirtualny procesor lub urządzenie i zwraca deskryptor pliku wskazujący nowe zasoby. Wywołania ioctl na deskryptorze pliku wirtualnego procesora lub urządzenia mogą służyć do sterowania urządzeniem utworzonym za pomocą wywołania ioctl na deskryptorze pliku maszyny wirtualnej. W przypadku procesorów wirtualnych obejmuje to ważne zadanie, jakim jest uruchamianie kodu gościa.
Wewnętrznie crosvm rejestruje deskryptory plików maszyny wirtualnej w jądrze za pomocą interfejsu epoll
wyzwalanego zboczem. Jądro powiadamia crosvm, gdy w dowolnym 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. crosvm używa tej funkcji podczas tworzenia pVM, jeśli przekazana zostanie flaga --protected-vm
, aby wysyłać zapytania i rezerwować odpowiednią ilość pamięci dla oprogramowania układowego pVM, a następnie włączać tryb chroniony.
Alokacja pamięci
Jednym z głównych zadań VMM jest przydzielanie pamięci maszyny wirtualnej i zarządzanie jej układem. crosvm generuje stały układ pamięci, który został ogólnie opisany w tabeli poniżej.
FDT w trybie normalnym | PHYS_MEMORY_END - 0x200000
|
Zwolnij miejsce | ...
|
Ramdisk | ALIGN_UP(KERNEL_END, 0x1000000)
|
Bąbelki | 0x80080000
|
Program rozruchowy | 0x80200000
|
FDT w trybie BIOS | 0x80000000
|
Podstawa pamięci fizycznej | 0x80000000
|
Oprogramowanie pVM | 0x7FE00000
|
Pamięć urządzenia | 0x10000 - 0x40000000
|
Pamięć fizyczna jest przydzielana za pomocą mmap
, a następnie przekazywana do maszyny wirtualnej, aby wypełnić jej regiony pamięci, zwane memslots, za pomocą wywołania systemowego KVM_SET_USER_MEMORY_REGION
. Cała pamięć pVM gościa jest przypisywana do instancji crosvm, która nią zarządza, co może spowodować zamknięcie procesu (zakończenie działania maszyny wirtualnej), jeśli na hoście zacznie brakować wolnej pamięci. Gdy maszyna wirtualna zostanie zatrzymana, hiperwizor automatycznie wyczyści pamięć i zwróci ją do jądra hosta.
W przypadku zwykłego KVM menedżer maszyn wirtualnych zachowuje dostęp do całej pamięci gościa. W przypadku pKVM pamięć gościa jest odłączana od przestrzeni adresowej fizycznej hosta, gdy jest przekazywana gościowi. Jedynym wyjątkiem jest pamięć jawnie udostępniona przez gościa, np. w przypadku urządzeń virtio.
Regiony MMIO w przestrzeni adresowej gościa pozostają nieprzypisane. Dostęp gościa do tych regionów jest blokowany i powoduje zdarzenie wejścia/wyjścia na deskryptorze pliku maszyny wirtualnej. Ten mechanizm służy do implementowania urządzeń wirtualnych. W trybie chronionym gość musi potwierdzić, że obszar jego przestrzeni adresowej jest używany do MMIO, za pomocą wywołania hiperwywołania, aby zmniejszyć ryzyko przypadkowego wycieku informacji.
Harmonogram
Każdy wirtualny procesor jest reprezentowany przez wątek POSIX i planowany przez harmonogram hosta Linux. Wątek wywołuje KVM_RUN
ioctl na FD procesora wirtualnego, co powoduje, że hiperwizor przełącza się na kontekst procesora wirtualnego gościa. Harmonogram hosta uwzględnia czas spędzony w kontekście gościa jako czas wykorzystany przez odpowiedni wątek procesora wirtualnego. KVM_RUN
jest zwracana, gdy wystąpi zdarzenie, które musi zostać obsłużone przez VMM, takie jak operacja wejścia/wyjścia, koniec przerwania lub zatrzymanie wirtualnego procesora. Menedżer maszyn wirtualnych obsługuje zdarzenie i ponownie wywołuje funkcję KVM_RUN
.
W trakcie KVM_RUN
wątek może być wywłaszczany przez harmonogram hosta, z wyjątkiem wykonywania kodu hiperwizora EL2, który nie może być wywłaszczany. Sama maszyna wirtualna gościa nie ma mechanizmu kontrolującego to zachowanie.
Wszystkie wątki procesora wirtualnego są planowane jak inne zadania w przestrzeni użytkownika, dlatego podlegają wszystkim standardowym mechanizmom jakości usług. Każdy wątek wirtualnego procesora może być powiązany z fizycznymi procesorami, umieszczony w zestawach procesorów, wzmocniony lub ograniczony za pomocą ograniczenia wykorzystania, mieć zmienioną politykę priorytetu lub planowania itp.
Urządzenia wirtualne
crosvm obsługuje wiele urządzeń, w tym:
- virtio-blk w przypadku złożonych obrazów dysków, tylko do odczytu lub odczytu i zapisu;
- vhost-vsock do komunikacji z hostem,
- virtio-pci jako transport virtio
- pl030 real time clock (RTC)
- 16550a UART do komunikacji szeregowej
Oprogramowanie pVM
Oprogramowanie układowe pVM (pvmfw) to pierwszy kod wykonywany przez pVM, podobnie jak pamięć ROM rozruchu urządzenia fizycznego. Głównym celem pvmfw jest uruchomienie bezpiecznego rozruchu i uzyskanie unikalnego klucza tajnego pVM. Oprogramowanie pvmfw nie jest ograniczone do używania 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 w partycji flash o tej samej nazwie i jest aktualizowany za pomocą OTA.
Uruchamianie urządzenia
Do procedury uruchamiania urządzenia z włączoną technologią pKVM dodawana jest ta sekwencja działań:
- Program ładujący Androida (ABL) ładuje pvmfw z partycji do pamięci i weryfikuje obraz.
- ABL pobiera tajne dane DICE (Device Identifier Composition Engine) (złożone identyfikatory urządzeń (CDI) i łańcuch certyfikatów DICE) z korzenia zaufania.
- ABL uzyskuje niezbędne identyfikatory CDI dla pvmfw i dołącza je do pliku binarnego pvmfw.
- ABL dodaje do DT węzeł
linux,pkvm-guest-firmware-memory
reserved memory region node, który opisuje lokalizację i rozmiar pliku binarnego pvmfw oraz wygenerowane w poprzednim kroku klucze tajne. - ABL przekazuje kontrolę do systemu Linux, który inicjuje pKVM.
- pKVM usuwa mapowanie regionu pamięci pvmfw z tabel stron poziomu 2 hosta i chroni go przed hostem (i gośćmi) przez cały czas działania urządzenia.
Po uruchomieniu urządzenia Microdroid jest uruchamiany zgodnie z instrukcjami w sekcji Sekwencja uruchamiania w dokumencie Microdroid.
uruchamianie pVM,
Podczas tworzenia pVM crosvm (lub inny VMM) musi utworzyć wystarczająco duży memslot, który będzie wypełniony obrazem pvmfw przez hiperwizor. Monitor maszyny wirtualnej ma też ograniczenia dotyczące listy rejestrów, których wartość początkową może ustawić (x0–x14 w przypadku głównego procesora wirtualnego, brak w przypadku dodatkowych procesorów wirtualnych). Pozostałe rejestry są zarezerwowane i stanowią część interfejsu ABI hypervisor-pvmfw.
Po uruchomieniu pVM hiperwizor najpierw przekazuje kontrolę nad podstawowym procesorem wirtualnym do pvmfw. Oprogramowanie sprzętowe oczekuje, że crosvm załaduje do pamięci jądro podpisane za pomocą AVB (może to być program rozruchowy lub dowolny inny obraz) oraz niepodpisany FDT pod znanymi adresami. pvmfw weryfikuje podpis AVB i w razie powodzenia generuje z otrzymanego FDT zaufane drzewo urządzeń, usuwa z pamięci swoje dane tajne i przechodzi do punktu wejścia ładunku. Jeśli jeden z etapów weryfikacji zakończy się niepowodzeniem, oprogramowanie sprzętowe wyda wywołanie systemowe PSCI SYSTEM_RESET
.
Między uruchomieniami informacje o instancji pVM są przechowywane na partycji (urządzenie virtio-blk) i szyfrowane za pomocą klucza tajnego pvmfw, aby po ponownym uruchomieniu klucz tajny został udostępniony prawidłowej instancji.