Architektura AVF

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

Tło

Architektura Arm umożliwia użycie maksymalnie 4 poziomów wyjątków, z tym że poziom 0 (EL0) jest najmniej uprzywilejowanym, a poziom 3 (EL3) – najbardziej uprzywilejowanym. Największa część bazy kodu Androida (wszystkie komponenty przestrzeni użytkownika) działa w EL0. Reszta tzw. „Androida” to jądro Linuksa, które działa w środowisku EL1.

Poziom EL2 umożliwia wprowadzenie hypervisora, który umożliwia izolowanie pamięci i urządzeń w poszczególnych pVM na poziomie EL1/EL0 z gwarancją zachowania poufności i integralności.

Hipernadzorca

Ochrona maszyn wirtualnych opartych na jądrze (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 IOMMU do zarządzania pamięcią wejścia-wyjścia. Te moduły umożliwiają przeniesienie do pKVM funkcji zabezpieczeń wymagających dostępu na poziomie wyjątku 2 (EL2).

Aby dowiedzieć się, jak wdrażać i wczytywać moduł dostawcy pKVM, przeczytaj artykuł Wdrażanie modułu dostawcy pKVM.

Procedura rozruchu

Poniższy rysunek przedstawia procedurę uruchamiania pKVM:

Procedura uruchamiania pKVM

Rysunek 1. Procedura uruchamiania pKVM

  1. Program rozruchowy wchodzi do ogólnego jądra w 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 rozruchowa ufa programowi rozruchowemu do utrzymania integralności obrazu jądra tylko podczas jego wczesnego rozruchu. Gdy ją pozbawisz uprawnień, hipernadzorca nie będzie już jej ufać, a w takim przypadku to hipernadzorca będzie odpowiedzialny za ochronę siebie, nawet jeśli ją skompromitujesz.

Jądro Androida i hipernadzorca w tym samym obrazie binarnym umożliwiają bardzo ściśle powiązany interfejs komunikacyjny. To ścisłe połączenie gwarantuje niepodzielne aktualizacje 2 komponentów, co pozwala uniknąć konieczności utrzymywania stabilności interfejsu między nimi i zapewnia dużą elastyczność bez negatywnego wpływu na długoterminowe działanie. 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 rozpowszechnienie GKI w ekosystemie Androida automatycznie pozwala hipernadzorcy pKVM na urządzenia 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. Oba te etapy można wykorzystać do wdrożenia 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. System MMU etapu 1 jest używany w Linuksie do zarządzania wirtualną przestrzenią adresową udostępnianą każdemu procesowi przestrzeni użytkownika i do jej własnej wirtualnej przestrzeni adresowej.

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 stwarza wrażenie ciągłej pamięci dla gości pomimo fragmentacji fizycznej. Jednak korzystanie z pamięci MMU etapu 2 w przypadku hosta 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 używanie dużych mapowań w tabeli stron, co zmniejsza presję na bufor porównawczy translacji (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 opisano wcześniej, odmapowanie stron gościa z hosta Linux w tabelach stron procesora jest niezbędnym, ale niewystarczającym krokiem w ochronie pamięci gościa. pKVM musi też chronić przed dostępem do pamięci 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 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 dostępu 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 ten 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 podmiotó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 układem SOC w elemencie EL2 to kluczowa strategia zmniejszenia ogólnej zaufanej bazy obliczeniowej (TCB) pKVM i przeciwdziałanie uwzględnieniu sterowników IOMMU w hipernadzorcy. 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 stosownych 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 jest zalecanym rozwiązaniem referencyjnym.

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ć gościowi poufność.

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 udostępnianiu pamięci lub operacjom wypożyczenia, które są ściśle 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ępniana jest funkcja hypercall, która pozwala przekazać prawo własności do określonych stron należących do wywołującego z powrotem do hosta. W praktyce to hiperwywołanie jest używane razem z protokołem virtio balloon, aby umożliwić VMM żądanie pamięci z powrotem z pVM, a pVM w sposób kontrolowany powiadamiać VMM o wycofanych stronach.

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. W większości przypadków śledzenie stanu odbywa się z wykorzystaniem metadanych dołączonych do dwustronicowych tabel gospodarza i gości, wykorzystując zarezerwowane bity we wpisach tabeli stron (PTE), które, jak sama nazwa wskazuje, są zarezerwowane do użytku w oprogramowaniu.

Host musi upewnić się, że nie próbuje uzyskać dostępu do stron, które zostały zablokowane przez hipernadzorcę. 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, strony przekazane gościom nie kwalifikują się do zamiany ani scalania przez jądro hosta.

Przerwij obsługę i liczniki czasu

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. Licznik czasu i IPI są obsługiwane w ramach tego niezaufanego kodu emulacji.

Obsługa GICv3

Interfejs między poziomami EL1 i EL2 musi zapewniać, aby pełny stan przerwania był widoczny dla hosta EL1, w tym kopie rejestrów hiperwizora związane z przerwą. Zwykle jest zapewniana z użyciem współdzielonych 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 rejestrowania przerwania dezaktywowanego (DIR). Zgodnie z architekturą te rejestry zawsze przechwytują się na EL2, podczas gdy inne pułapki służyły dotychczas jedynie do ograniczania niedokładności. Pozostałe kwestie są obsługiwane przez sprzęt.

Po stronie MMIO wszystko jest emulowane na poziomie EL1, co pozwala ponownie wykorzystać całą bieżącą infrastrukturę w KVM. Na koniec opcja Oczekiwanie na przerwanie (WFI) jest zawsze przekazywana do EL1, ponieważ jest to jeden z podstawowych podstawowych funkcji planowania używany 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 minutnik jest w pełni 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. pKVM wymaga:

  • IPA i zakres dostępu
  • Dane w przypadku zapisu
  • Endianność procesora w miejscu pułapki

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ą kombinacji hiperwywołań 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.

Ogólne hiperwywołania

  • 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 zaufać w zakresie wirtualizacji sprzętowego generatora 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 do hosta jest niezbędny do komunikacji w pamięci współdzielonej i w przypadku parawirtualizowanych urządzeń, 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 uzgadniania.
  • 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 kończy działanie na hoście, a dostęp jest zwykle używany do MMIO i emulowany przez VMM w przestrzeni użytkownika. Aby ułatwić obsługę, pKVM musi reklamować szczegółowe informacje o poleceniu związanym z błędami, takie jak adres, rejestrowanie parametrów i potencjalnie ich zawartość z powrotem do hosta. Może to w sposób niezamierzony ujawnić dane wrażliwe chronionego gościa, jeśli pułapka nie była przewidywana. pKVM rozwiązuje ten problem, traktując te błędy jako poważne, chyba że użytkownik wcześniej dozwolony był dozwolonym dostępem w celu umożliwienia hiperwywołania dostępu 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 opiera się również na implementacji przy użyciu interfejsu API w celu komunikacji między chronionym gościem a resztą Androida.

VMM zwykle wdraża w przestrzeni użytkownika hosta urządzenia Virtio, które przechwytują uwięzione w pamięci dostęp z gościa do interfejsu MMIO urządzenia virtio i emulują 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.

Choć opisane wcześniej hiperwywołania udostępniania pamięci można wykorzystać do udostępniania buforów danych virtio między gościa a hostem, udostępnianie odbywa się z dokładnością do 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 konfiguracja gościa jest skonfigurowana tak, aby przydzielać zarówno wirtuały, jak i odpowiadające im bufory danych ze stałego okna współdzielonej pamięci, przy czym dane są kopiowane (odrzucane) do i z okna zgodnie z potrzebami.

Urządzenie wirtualne

Rysunek 4. Urządzenie Virtio

Interakcje z TrustZone

Chociaż goście nie mogą wchodzić w bezpośrednią interakcję z TrustZone, gospodarz musi mieć możliwość wywoływania SMC w bezpiecznym świecie. Te wywołania mogą określać bufory pamięci z adresami fizycznymi, które są niedostępne dla hosta. Ponieważ bezpieczne oprogramowanie jest zasadniczo nieświadome dostępności bufora, szkodliwy host może go wykorzystać do przeprowadzenia zdezorientowanego ataku typu „podstępny” (analogicznie do ataku DMA). Aby zapobiec takim atakom, pKVM przechwytuje wszystkie wywołania SMC hosta na EL2 i działa jako serwer proxy między hostem a bezpiecznym monitorem w 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 przepisywany, aby tabela strony 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 SoC obsługującym PSCI, najlepiej z użyciem aktualnej wersji TF-A jako oprogramowania układowego EL3.

Firmware Framework for Arm (FF-A) standaryzuje interakcje między normalnym a bezpiecznym światem, szczególnie w przypadku bezpiecznego hypervisora. W dużej części specyfikacji definiuje się mechanizm udostępniania pamięci bezpiecznemu światu, wykorzystując zarówno popularny format wiadomości, jak i dobrze zdefiniowany model uprawnień dla stron bazowych. pKVM pośredniczy w wiadomościach FF-A, aby host nie próbował udostępniać pamięci bezpiecznej stronie, do której nie ma wystarczających uprawnień.

Ta architektura opiera się na oprogramowaniu zabezpieczeń w bezpiecznym świecie egzekwującym model dostępu do pamięci, aby zapewnić, że zaufane aplikacje i każde inne oprogramowanie działające w bezpiecznym świecie mają dostęp do pamięci tylko wtedy, gdy należy ona wyłącznie do bezpiecznego świata lub została jej jawnie 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 na stronie etapu 1.

Jeśli wywołanie SMC do EL2 nie jest wywołaniem PSCI ani komunikatem zdefiniowanym w FF-A, nieobsłużone konta SMC są przekazywane do EL3. Zakładamy, że bezpieczne oprogramowanie układowe (niezbędnie zaufane) może bezpiecznie obsługiwać nieobsłużone urządzenia SMC, ponieważ rozumie ono środki ostrożności niezbędne do zachowania izolacji urządzeń pVM.

Monitor maszyny wirtualnej

crosvm to monitor maszyn wirtualnych, który uruchamia maszyny wirtualne przez interfejs KVM systemu Linux. Unikalną cechą crosvm jest nacisk na bezpieczeństwo dzięki zastosowaniu języka programowania Rust i piaskownicy wokół urządzeń wirtualnych, aby chronić jądro hosta. Więcej informacji o crosvm znajdziesz w oficjalnej dokumentacji tutaj.

Deskryptory plików i ioctle

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 tworzyć 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ń.
  • i ustawia atrybuty, które kontrolują działanie pojedynczego procesora wirtualnego.
  • Zapytania ioctl urządzenia i ustawienie atrybutów, które kontrolują działanie pojedynczego wirtualnego urządzenia.

Każdy proces crosvm uruchamia dokładnie 1 instancję maszyny wirtualnej. W tym procesie używane jest ioctl systemu KVM_CREATE_VM do tworzenia deskryptorów plików maszyn wirtualnych, które można wykorzystać do wydawania ioctlów pVM. Polecenie ioctl KVM_CREATE_VCPU lub KVM_CREATE_DEVICE na gniazdach FD maszyny wirtualnej tworzy vCPU lub urządzenie i zwraca opis 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 wyzwalanego przez krawędzie. 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 pozwala uzyskać informacje o środowisku pVM i skonfigurować tryb chroniony maszyny wirtualnej. crosvm wykorzystuje ją podczas tworzenia maszyny wirtualnej, jeśli zostanie przesłana flaga --protected-vm, do wysyłania zapytań i zarezerwowania odpowiedniej ilości pamięci na potrzeby oprogramowania pVM, a następnie do włączenia trybu chronionego.

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 stały układ pamięci, który jest ogólnie opisany w tabeli poniżej.

FDT w trybie normalnym PHYS_MEMORY_END - 0x200000
Zwolnij miejsce ...
Ramdysk 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 zablokowany 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 procesor wirtualny jest reprezentowany przez wątek POSIX i zaplanowany przez algorytm szeregowania hosta z systemem Linux. Wątek wywołuje ioctl KVM_RUN na FD procesora vCPU, co powoduje przełączenie hypervisora na kontekst procesora vCPU gościa. Algorytm szeregowania hosta zlicza czas spędzony w kontekście gościa jako czas wykorzystany przez odpowiedni wątek procesorów wirtualnych. 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 zdarzenie i ponownie wywołuje 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ą planowane 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, a także może mieć zmienioną zasadę priorytetu lub harmonogramu.

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 virtio
  • pl030 zegar czasu rzeczywistego (RTC)
  • 16550a UART do komunikacji szeregowej

Oprogramowanie pVM

Firmware pVM (pvmfw) to pierwszy kod wykonywany przez pVM, podobny do obrazu ROM uruchamiania fizycznego urządzenia. Głównym celem pvmfw jest inicjowanie bezpiecznego uruchamiania i wyprowadzanie 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 binarny pvmfw jest przechowywany w partycji Flash o tej samej nazwie i jest aktualizowany przy użyciu OTA.

Uruchamianie urządzenia

Do procedury rozruchu urządzenia obsługującego pKVM jest dodawana ta sekwencja:

  1. Bootloader Androida (ABL) wczytuje pvmfw z partycji do pamięci i weryfikuje obraz.
  2. ABL pobiera obiekty tajne DICE (identyfikatory urządzeń złożonych (CDI) i łańcuch certyfikatów DICE) z katalogu Root of Trust.
  3. Narzędzie ABL pobiera dane CDI wymagane 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 mikrodroid 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 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.

Po uruchomieniu maszyny wirtualnej hipernadzorca najpierw kontroluje podstawowe procesory wirtualne, a następnie 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 jest poprawny, generuje zaufane drzewo urządzenia z otrzymanego FDT, usuwa 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.

Między rozruchami informacje o instancji pVM są przechowywane na partycji (urządzenie virtio-blk) i zaszyfrowane za pomocą obiektu tajnego pvmfw, aby po ponownym uruchomieniu udostępnić obiekt tajny do właściwej instancji.