Udostępnij przez


Hypercall, interfejs

Funkcja hypervisor udostępnia mechanizm wywołujący gości. Takie wywołania są nazywane hiperwołami. Każdy hipercall definiuje zestaw parametrów wejściowych i/lub wyjściowych. Te parametry są określane pod względem struktury danych opartej na pamięci. Wszystkie elementy struktur danych wejściowych i wyjściowych są dopełniane do granic naturalnych do 8 bajtów (czyli dwubajtowych elementów musi znajdować się w granicach dwubajtowych itd.).

Drugą konwencję wywoływania hiperwołów można opcjonalnie użyć dla podzbioru hipercalls — w szczególności tych, które mają dwa lub mniej parametrów wejściowych i nie ma parametrów wyjściowych. W przypadku korzystania z tej konwencji wywoływania parametry wejściowe są przekazywane w rejestrach ogólnego przeznaczenia.

Trzecia konwencja wywoływania hiperwołów może być opcjonalnie używana dla podzbioru hiperwołów, gdzie blok parametru wejściowego wynosi do 112 bajtów. W przypadku korzystania z tej konwencji wywoływania parametry wejściowe są przekazywane w rejestrach, w tym w rejestrach nietrwałych XMM.

Struktury danych wejściowych i wyjściowych muszą być umieszczane w pamięci na granicy 8 bajtów i dopełniane do wielokrotności 8 bajtów w rozmiarze. Wartości w regionach uzupełniania są ignorowane przez funkcję hypervisor.

W przypadku danych wyjściowych funkcja hypervisor może (ale nie ma gwarancji) zastąpić regiony uzupełniania. Jeśli zastąpi on regiony uzupełniania, zapisze zera.

Klasy funkcji Hypercall

Istnieją dwie klasy hipercalls: simple i rep (skrót od "repeat"). Prosta funkcja hypercall wykonuje pojedynczą operację i ma zestaw parametrów wejściowych i wyjściowych o stałym rozmiarze. Rep hipercall działa jak seria prostych hipercalls. Oprócz zestawu parametrów wejściowych i wyjściowych o stałym rozmiarze funkcja hypercalls rep obejmuje listę elementów wejściowych o stałym rozmiarze i/lub wyjściowych.

Gdy obiekt wywołujący początkowo wywołuje funkcję hypercall repozytorium, określa liczbę powtórzeń wskazującą liczbę elementów na liście parametrów wejściowych i/lub wyjściowych. Osoby wywołujące określają również indeks rozpoczęcia repozytorium, który wskazuje następny element wejściowy i/lub wyjściowy, który powinien zostać użyty. Funkcja hypervisor przetwarza parametry rep w kolejności listy — czyli przez zwiększenie indeksu elementów.

W przypadku kolejnych wywołań funkcji hypercall repozytorium indeks początkowy repozytorium wskazuje, ile elementów zostało ukończonych — i w połączeniu z wartością liczby powtórzeń — ile elementów pozostało. Jeśli na przykład obiekt wywołujący określa liczbę powtórzeń 25, a tylko 20 iteracji jest wykonywanych w ramach ograniczeń czasowych, funkcja hypercall zwraca kontrolę z powrotem do wywołującego procesora wirtualnego po zaktualizowaniu indeksu początkowego repozytorium do 20. Po ponownym wykonaniu funkcji hypercall funkcja hypervisor zostanie wznowiona w elemecie 20 i ukończy pozostałe 5 elementów.

Jeśli podczas przetwarzania elementu wystąpi błąd, zostanie podany odpowiedni kod stanu wraz z ukończoną liczbą powtórzeń wskazującą liczbę elementów, które zostały pomyślnie przetworzone przed wystąpieniem błędu. Przy założeniu, że określony wyraz kontrolki hypercall jest prawidłowy (patrz poniżej), a listy parametrów wejściowych/wyjściowych są dostępne, funkcja hypervisor ma gwarancję próby wykonania co najmniej jednego powtórzenia, ale nie jest wymagana do przetworzenia całej listy przed zwróceniem kontrolki z powrotem do wywołującego.

Kontynuacja funkcji Hypercall

Hipercall można traktować jako złożoną instrukcję, która wymaga wielu cykli. Funkcja hypervisor próbuje ograniczyć wykonywanie funkcji hypercall do 50μs lub mniej przed zwróceniem kontroli do procesora wirtualnego, który wywołał funkcję hypercall. Niektóre operacje hypercall są wystarczająco złożone, że gwarancja 50μs jest trudna do wykonania. W związku z tym funkcja hypervisor opiera się na mechanizmie kontynuacji hipercall dla niektórych hiperwołów — w tym wszystkich formularzy hiperwołów rep.

Mechanizm kontynuacji funkcji hypercall jest w większości niewidoczny dla elementu wywołującego. Jeśli funkcja hypercall nie jest w stanie ukończyć w określonym limicie czasu, kontrolka jest zwracana z powrotem do obiektu wywołującego, ale wskaźnik instrukcji nie jest zaawansowany obok instrukcji, która wywołała hiperwołanie. Umożliwia to obsługę oczekujących przerwań i zaplanowanie innych procesorów wirtualnych. Gdy oryginalny wątek wywołujący wznowi wykonywanie, wykona ponownie instrukcję hypercall i wykona postęp w kierunku ukończenia operacji.

W ramach określonego limitu czasu gwarantowane jest wykonanie większości prostych hiperwzlotów. Jednak niewielka liczba prostych hipercalls może wymagać więcej czasu. Te hipercalls używają kontynuacji hipercall w podobny sposób do rep hypercalls. W takich przypadkach operacja obejmuje co najmniej dwa stany wewnętrzne. Pierwsze wywołanie umieszcza obiekt (na przykład partycję lub procesor wirtualny) w jeden stan, a po powtórzeniu wywołań stan ostatecznie przechodzi do stanu terminalu. Dla każdego hiperwzlotu, który jest zgodny z tym wzorcem, opisane są widoczne skutki uboczne pośrednich stanów wewnętrznych.

Hipercall Atomicity and Ordering (Hipercall Niepodzielność i kolejność)

Z wyjątkiem przypadków, w których zaznaczono, akcja wykonywana przez funkcję hypercall jest niepodzielna zarówno w odniesieniu do wszystkich innych operacji gościa (na przykład instrukcji wykonywanych w ramach gościa) i wszystkich innych hipercalls wykonywanych w systemie. Prosta funkcja hypercall wykonuje jedną akcję niepodzielna; rep hypercall wykonuje wiele niezależnych akcji niepodzielnych.

Proste hiperkazwy korzystające z kontynuacji hipercall mogą obejmować wiele stanów wewnętrznych, które są widoczne zewnętrznie. Takie wywołania składają się z wielu operacji niepodzielnych.

Każda akcja hipercall może odczytywać parametry wejściowe i/lub zapisywać wyniki. Dane wejściowe każdej akcji można odczytać w dowolnym stopniach szczegółowości i w dowolnym momencie po wykonaniu funkcji hypercall i przed wykonaniem akcji. Wyniki (czyli parametry wyjściowe) skojarzone z każdą akcją mogą być zapisywane w dowolnym stopniach szczegółowości i w dowolnym momencie po wykonaniu akcji i przed zwróceniem funkcji hypercall.

Gość musi unikać badania i/lub manipulowania dowolnymi parametrami wejściowymi lub wyjściowymi związanymi z wykonywaniem funkcji hypercall. Podczas gdy procesor wirtualny wykonujący funkcję hypercall nie będzie w stanie tego zrobić (ponieważ jego wykonanie gościa jest zawieszone do momentu powrotu funkcji hypercall), nie ma nic, aby zapobiec temu innym procesorom wirtualnym. Goście zachowują się w ten sposób mogą ulec awarii lub spowodować uszkodzenie w ramach partycji.

Hiperwołyny mogą być wywoływane tylko z najbardziej uprzywilejowanego trybu procesora gościa. Na platformach x64 oznacza to tryb chroniony z bieżącym poziomem uprawnień (CPL) równym zero. Mimo że kod w trybie rzeczywistym działa z obowiązującą wartością CPL zerową, hipercalls nie są dozwolone w trybie rzeczywistym. Próba wywołania funkcji hypercall w trybie nielegalnego procesora spowoduje wygenerowanie wyjątku #UD (niezdefiniowanej operacji) w architekturze x64 i niezdefiniowanego wyjątku instrukcji w usłudze ARM64.

Wszystkie hiperwołania powinny być wywoływane za pomocą interfejsu hipercall zdefiniowanego architekturą (patrz poniżej). Próba wywołania funkcji hypercall za pomocą innych środków (na przykład skopiowanie kodu ze strony kodu funkcji hypercall do lokalizacji alternatywnej i wykonanie jej z tego miejsca) może spowodować niezdefiniowaną operację (#UD). Funkcja hypervisor nie ma gwarancji dostarczenia tego wyjątku.

Wymagania dotyczące wyrównania

Wywołujące muszą określić 64-bitowy adres fizyczny gościa (GPA) parametrów wejściowych i/lub wyjściowych. Wskaźniki GPA muszą być wyrównane 8 bajtów. Jeśli funkcja hypercall nie zawiera parametrów wejściowych ani wyjściowych, funkcja hypervisor ignoruje odpowiedni wskaźnik GPA.

Listy parametrów wejściowych i wyjściowych nie mogą nakładać się ani przekraczać granic stron. Oczekuje się, że strony wejściowe i wyjściowe funkcji Hypercall będą stronami gpA, a nie stronami "nakładki". Jeśli procesor wirtualny zapisuje parametry wejściowe na stronie nakładki i określa gpA na tej stronie, funkcja hypervisor dostępu do listy parametrów wejściowych jest niezdefiniowana.

Funkcja hypervisor sprawdzi, czy partycja wywołująca może odczytywać dane ze strony wejściowej przed wykonaniem żądanego hiperwołania. Ta weryfikacja składa się z dwóch kontroli: określony gpA jest mapowany, a GPA jest oznaczony jako czytelny. Jeśli którykolwiek z tych testów zakończy się niepowodzeniem, funkcja hypervisor generuje komunikat przechwytywania pamięci. W przypadku funkcji hypercall, które mają parametry wyjściowe, funkcja hypervisor sprawdzi, czy partycja może zapisywać dane na stronie wyjściowej. Ta weryfikacja składa się z dwóch kontroli: określony gpA jest mapowany, a GPA jest oznaczony jako zapisywalny.

Dane wejściowe funkcji Hypercall

Wywołujące określają hiperwołanie przez wartość 64-bitową nazywaną wartością wejściową funkcji hypercall. Formatowany jest w następujący sposób:

(No changes needed) Bity Podane informacje
Kod wywołania 15-0 Określa, które funkcja hypercall jest żądana
Szybko 16 Określa, czy funkcja hypercall używa konwencji wywoływania opartej na rejestrze: 0 = opartej na pamięci, 1 = oparte na rejestrze
Rozmiar nagłówka zmiennej 26-17 Rozmiar nagłówka zmiennej w języku QWORDS.
RsvdZ 30-27 Musi być zero
Jest zagnieżdżona 31 Określa, że funkcja hypercall powinna być obsługiwana przez funkcję hypervisor L0 w środowisku zagnieżdżonym.
Liczba powtórzeń 43-32 Całkowita liczba powtórzeń (w przypadku wywołania rep musi być zero w przeciwnym razie)
RsvdZ 47-44 Musi być zero
Indeks rozpoczęcia repozytorium 59-48 Indeks początkowy (w przypadku wywołania rep musi być zero w przeciwnym razie)
RsvdZ 63-60 Musi być zero

W przypadku funkcji hypercalls rep pole liczba powtórzeń wskazuje łączną liczbę powtórzeń. Indeks startowy rep wskazuje konkretne powtórzenie względem początku listy (zero wskazuje, że pierwszy element na liście ma zostać przetworzony). W związku z tym wartość liczby powtórzeń musi być zawsze większa niż indeks początkowy repozytorium.

Konwencje rejestrów funkcji Hypercall (x86/x64)

W systemie x86/x64 zarejestruj mapowanie dla danych wejściowych funkcji hypercall, gdy flaga Fast ma wartość zero, są następujące:

x64 x86 Podane informacje
RCX EDX:EAX Wartość wejściowa funkcji Hypercall
RDX EBX:ECX Parametry wejściowe GPA
R8 EDI:ESI Parametry wyjściowe GPA

Wartość wejściowa funkcji hypercall jest przekazywana w rejestrach wraz z gpA wskazującą parametry wejściowe i wyjściowe.

Mapowania rejestru zależą od tego, czy obiekt wywołujący działa w trybie 32-bitowym (x86) czy 64-bitowym (x64). Funkcja hypervisor określa tryb obiektu wywołującego na podstawie wartości EFER. LMA i CS.L. Jeśli obie te flagi są ustawione, przyjmuje się, że obiekt wywołujący jest obiektem wywołującym 64-bitowym.

Zarejestruj mapowanie danych wejściowych funkcji hypercall, gdy flaga Szybka jest jedną:

x64 x86 Podane informacje
RCX EDX:EAX Wartość wejściowa funkcji Hypercall
RDX EBX:ECX Parametr wejściowy
R8 EDI:ESI Parametr wyjściowy

Wartość wejściowa funkcji hypercall jest przekazywana w rejestrach wraz z parametrami wejściowymi.

Konwencje rejestrów funkcji Hypercall (ARM64 SMCCC)

W usłudze ARM64 hipercalls są wykonywane przy użyciu instrukcji "HVC #0". Wywołania są zgodne z konwencją wywołań ARM64 SMCCC (SMC Calling Convention).

Mapowanie rejestru dla danych wejściowych funkcji hypercall są następujące:

Register Podane informacje
X0 Identyfikator funkcji SMCCC
X1 Wartość wejściowa funkcji Hypercall
X2 Parametry wejściowe GPA
X3 Parametry wyjściowe GPA

Identyfikator funkcji SMCCC w X0 jest następujący format:

Bity (No changes needed) Wartość Description
31 Zwracanie wywołania 0 Zawsze 0
30 Konwencja wywoływania 1 1 dla konwencji wywoływania HVC64
29:24 Typ wywołania usługi 6 6 w przypadku wywołań usługi hypervisor specyficznych dla dostawcy
23:16 Zarezerwowana 0 Zarezerwowane, musi mieć wartość zero (Res0)
15:0 Numer funkcji 1 1 wskazuje, że kod wywołania HV jest zdefiniowany w X1

Pełny format identyfikatora funkcji: 0x46000001

Konwencje rejestracji funkcji Hypercall (ARM64 HVC #1)

Ze względów historycznych interfejs funkcji hypervisor arm64 obsługuje również inną konwencję wywoływania. Hipercalls są wykonywane za pomocą instrukcji "HVC #1". Zalecamy użycie konwencji wywoływania SMCCC dla nowego kodu.

Mapowanie rejestru dla danych wejściowych funkcji hypercall są następujące:

Register Podane informacje
X0 Wartość wejściowa funkcji Hypercall
X1 Parametry wejściowe GPA
X2 Parametry wyjściowe GPA

Nagłówki wejściowe hypercall o zmiennym rozmiarze

Większość nagłówków wejściowych funkcji hypercall ma stały rozmiar. Ilość danych nagłówka przekazywanych z gościa do funkcji hypervisor jest zatem niejawnie określona przez kod funkcji hypercall i nie musi być określona oddzielnie. Jednak niektóre hipercalls wymagają zmiennej ilości danych nagłówka. Te hiperwzloty zwykle mają stały rozmiar nagłówka wejściowego i dodatkowe dane wejściowe nagłówka o zmiennym rozmiarze.

Nagłówek o zmiennym rozmiarze jest podobny do stałego wejścia hipercall (wyrównanego do 8 bajtów i rozmiaru wielokrotności 8 bajtów). Obiekt wywołujący musi określić, ile danych dostarcza jako nagłówki wejściowe. Ten rozmiar jest dostarczany jako część wartości wejściowej funkcji hypercall (zobacz "Zmienny rozmiar nagłówka" w powyższej tabeli).

Ponieważ stały rozmiar nagłówka jest niejawny, zamiast podawać całkowity rozmiar nagłówka, tylko część zmiennej jest dostarczana w kontrolkach wejściowych:

Variable Header Bytes = {Total Header Bytes - sizeof(Fixed Header)} rounded up to nearest multiple of 8

Variable Header Size = Variable Header Bytes / 8

Niedozwolone jest określenie niezerowego rozmiaru nagłówka dla funkcji hypercall, która nie jest jawnie udokumentowana jako akceptowanie nagłówków wejściowych o zmiennym rozmiarze. W takim przypadku funkcja hypercall spowoduje zwrócenie kodu HV_STATUS_INVALID_HYPERCALL_INPUT.

Istnieje możliwość, że w przypadku danego wywołania funkcji hypercall, która akceptuje nagłówki wejściowe o zmiennym rozmiarze, że wszystkie dane wejściowe nagłówka mieszczą się całkowicie w nagłówku o stałym rozmiarze. W takich przypadkach nagłówek wejściowy o zmiennym rozmiarze ma zerowy rozmiar, a odpowiednie bity w danych wejściowych funkcji hypercall powinny być ustawione na zero.

We wszystkich innych kwestiach hiperwoływały akceptujące nagłówki wejściowe o zmiennym rozmiarze są w inny sposób podobne do hiperwołów nagłówka wejściowego o stałym rozmiarze w odniesieniu do konwencji wywoływania. Funkcja hypercall nagłówka o zmiennym rozmiarze umożliwia również obsługę semantyki repozytorium. W takim przypadku elementy rep leżą po nagłówku w zwykły sposób, z tą różnicą, że całkowity rozmiar nagłówka obejmuje zarówno części stałe, jak i zmienne. Wszystkie inne reguły pozostają takie same, np. pierwszy element rep musi być wyrównany 8 bajtów.

Szybkie wejście funkcji Hypercall XMM (x86/x64)

Na platformach x86/x64 funkcja hypervisor obsługuje korzystanie z szybkich hiperwołów XMM, dzięki czemu niektóre hiperwołyny mogą korzystać z ulepszonej wydajności szybkiego interfejsu hypercall, mimo że wymagają więcej niż dwóch parametrów wejściowych. Interfejs szybkiej funkcji hypercall programu XMM używa sześciu rejestrów XMM, aby umożliwić wywołującym przekazanie bloku parametru wejściowego do 112 bajtów w rozmiarze.

Dostępność interfejsu funkcji hypercall programu XMM jest wskazywana za pośrednictwem liścia CPUID cpuID funkcji (0x40000003):

  • Bit 4: dostępna jest obsługa przekazywania danych wejściowych funkcji hypercall za pośrednictwem rejestrów XMM.

Należy pamiętać, że istnieje oddzielna flaga wskazująca obsługę szybkich danych wyjściowych programu XMM. Każda próba użycia tego interfejsu, gdy funkcja hypervisor nie wskazuje dostępności, spowoduje błąd #UD.

Rejestrowanie mapowania (tylko dane wejściowe)

x64 x86 Podane informacje
RCX EDX:EAX Wartość wejściowa funkcji Hypercall
RDX EBX:ECX Blok parametrów wejściowych
R8 EDI:ESI Blok parametrów wejściowych
XMM0 XMM0 Blok parametrów wejściowych
XMM1 XMM1 Blok parametrów wejściowych
XMM2 XMM2 Blok parametrów wejściowych
XMM3 XMM3 Blok parametrów wejściowych
XMM4 XMM4 Blok parametrów wejściowych
XMM5 XMM5 Blok parametrów wejściowych

Wartość wejściowa funkcji hypercall jest przekazywana w rejestrach wraz z parametrami wejściowymi. Mapowania rejestru zależą od tego, czy obiekt wywołujący działa w trybie 32-bitowym (x86) czy 64-bitowym (x64). Funkcja hypervisor określa tryb obiektu wywołującego na podstawie wartości EFER. LMA i CS.L. Jeśli obie te flagi są ustawione, przyjmuje się, że obiekt wywołujący jest obiektem wywołującym 64-bitowym. Jeśli blok parametrów wejściowych jest mniejszy niż 112 bajtów, wszelkie dodatkowe bajty w rejestrach są ignorowane.

Rejestrowanie danych wejściowych szybkiego wywołania (ARM64 SMCCC)

Na platformach ARM64 funkcja hypervisor obsługuje korzystanie z rejestru szybkich hipercalls, dzięki czemu niektóre hiperwołyny mogą korzystać z ulepszonej wydajności szybkiego interfejsu hypercall, mimo że wymagają więcej niż dwóch parametrów wejściowych. Interfejs szybkiego wywoływania rejestru używa 16 rejestrów ogólnego przeznaczenia, aby umożliwić wywołującym przekazanie bloku parametru wejściowego do 128 bajtów w rozmiarze.

Rejestrowanie mapowania (tylko dane wejściowe)

Register Podane informacje
X0 Identyfikator funkcji SMCCC
X1 Wartość wejściowa funkcji Hypercall
X2 – X17 Blok parametrów wejściowych

Jeśli blok parametrów wejściowych jest mniejszy niż 128 bajtów, wszelkie dodatkowe bajty w rejestrach są ignorowane.

Rejestrowanie wejścia szybkiego wywołania (ARM64 HVC #1)

Interfejs szybkiego wywołania rejestru używa szesnastu rejestrów ogólnego przeznaczenia, aby umożliwić wywołującym przekazanie bloku parametru wejściowego do 128 bajtów rozmiaru.

Rejestrowanie mapowania (tylko dane wejściowe)

Register Podane informacje
X0 Wartość wejściowa funkcji Hypercall
X1 – X17 Blok parametrów wejściowych

Jeśli blok parametrów wejściowych jest mniejszy niż 128 bajtów, wszelkie dodatkowe bajty w rejestrach są ignorowane.

Dane wyjściowe funkcji Hypercall

Wszystkie hiperwołyny zwracają wartość 64-bitową nazywaną wartością wyniku funkcji hypercall. Formatowany jest w następujący sposób:

(No changes needed) Bity Comment
Wynik 15-0 HV_STATUS kod wskazujący powodzenie lub niepowodzenie
Rsvd 31-16 Obiekty wywołujące powinny ignorować wartość w tych bitach
Powtórzenia zostały ukończone 43-32 Liczba powtórzeń zakończonych pomyślnie
RsvdZ 63-40 Obiekty wywołujące powinny ignorować wartość w tych bitach

W przypadku funkcji hypercalls repozytorium pełne pole jest całkowitą liczbą ukończonych powtórzeń, a nie względem indeksu początkowego repozytorium. Jeśli na przykład obiekt wywołujący określił indeks początkowy powtórzenia 5, a liczba powtórzeń 10, pełne pole reps będzie wskazywać 10 po pomyślnym zakończeniu.

Wartość wyniku funkcji hypercall jest przekazywana z powrotem w rejestrach.

W przypadku architektury x64 mapowanie rejestru zależy od tego, czy obiekt wywołujący jest uruchomiony w trybie 32-bitowym (x86) czy 64-bitowym (x64) (patrz powyżej). Mapowanie rejestru dla danych wyjściowych funkcji hypercall jest następujące:

x64 x86 Podane informacje
RAX EDX:EAX Wartość wyniku funkcji Hypercall

W usłudze ARM64 mapowanie rejestru dla danych wyjściowych funkcji hypercall jest następujące:

Register Podane informacje
X0 Wartość wyniku funkcji Hypercall

Szybkie wyjście funkcji Hypercall XMM (x86/x64)

Podobnie jak funkcja hypervisor obsługuje szybkie dane wejściowe funkcji hypercall programu XMM, te same rejestry mogą być współużytkowane w celu zwrócenia danych wyjściowych. Jest to obsługiwane tylko na platformach x64.

Możliwość zwracania danych wyjściowych za pośrednictwem rejestrów XMM jest wskazywana za pośrednictwem liścia CPUID CPUID funkcji "Hypervisor" (0x40000003):

  • Bit 15: dostępna jest obsługa zwracania danych wyjściowych funkcji hypercall za pośrednictwem rejestrów XMM.

Należy pamiętać, że istnieje oddzielna flaga wskazująca obsługę szybkiego wprowadzania kodu XMM. Każda próba użycia tego interfejsu, gdy funkcja hypervisor nie wskazuje dostępności, spowoduje błąd #UD.

Rejestrowanie mapowania (dane wejściowe i wyjściowe)

Rejestry, które nie są używane do przekazywania parametrów wejściowych, mogą służyć do zwracania danych wyjściowych. Innymi słowy, jeśli blok parametrów wejściowych jest mniejszy niż 112 bajtów (zaokrąglony do najbliższego 16 bajtów wyrównanego fragmentu), pozostałe rejestry będą zwracać dane wyjściowe funkcji hypercall.

x64 Podane informacje
RDX Blok wejściowy lub wyjściowy
R8 Blok wejściowy lub wyjściowy
XMM0 Blok wejściowy lub wyjściowy
XMM1 Blok wejściowy lub wyjściowy
XMM2 Blok wejściowy lub wyjściowy
XMM3 Blok wejściowy lub wyjściowy
XMM4 Blok wejściowy lub wyjściowy
XMM5 Blok wejściowy lub wyjściowy

Jeśli na przykład blok parametru wejściowego ma rozmiar 20 bajtów, funkcja hypervisor zignoruje następujące 12 bajtów. Pozostałe 80 bajtów będzie zawierać dane wyjściowe funkcji hypercall (jeśli ma to zastosowanie).

Rejestrowanie danych wyjściowych szybkiego wywołania (ARM64 SMCCC)

Na platformach ARM64, podobnie jak funkcja hypervisor obsługuje rejestrowanie szybkich danych wejściowych funkcji hypercall, te same rejestry mogą być udostępniane w celu zwrócenia danych wyjściowych.

Rejestrowanie mapowania (dane wejściowe i wyjściowe)

Rejestry, które nie są używane do przekazywania parametrów wejściowych, mogą służyć do zwracania danych wyjściowych. Innymi słowy, jeśli blok parametrów wejściowych jest mniejszy niż 128 bajtów (zaokrąglony do najbliższego 8 bajtów wyrównanego fragmentu), pozostałe rejestry będą zwracać dane wyjściowe funkcji hypercall.

Register Podane informacje
X2 – X17 Blok wejściowy lub wyjściowy

Jeśli na przykład blok parametru wejściowego ma rozmiar 20 bajtów, funkcja hypervisor zignoruje następujące 4 bajty. Pozostałe 104 bajty będą zawierać dane wyjściowe funkcji hypercall (jeśli ma to zastosowanie).

Rejestrowanie danych wyjściowych szybkiego wywołania (ARM64 HVC #1)

Podobnie jak w przypadku wersji SMCCC, interfejs HVC #1 używa tych samych rejestrów do zwracania danych wyjściowych.

Rejestrowanie mapowania (dane wejściowe i wyjściowe)

Rejestry, które nie są używane do przekazywania parametrów wejściowych, mogą służyć do zwracania danych wyjściowych. Innymi słowy, jeśli blok parametrów wejściowych jest mniejszy niż 128 bajtów (zaokrąglony do najbliższego 8 bajtów wyrównanego fragmentu), pozostałe rejestry będą zwracać dane wyjściowe funkcji hypercall.

Register Podane informacje
X1 – X17 Blok wejściowy lub wyjściowy

Jeśli na przykład blok parametru wejściowego ma rozmiar 20 bajtów, funkcja hypervisor zignoruje następujące 4 bajty. Pozostałe 104 bajty będą zawierać dane wyjściowe funkcji hypercall (jeśli ma to zastosowanie).

Rejestry lotne (x86/x64)

Funkcja Hypercalls zmodyfikuje tylko określone wartości rejestru w następujących warunkach:

  1. RaX (x64) i EDX:EAX (x86) są zawsze zastępowane wartością wyniku funkcji hypercall i parametrami wyjściowymi, jeśli istnieją.
  2. Rep hypercalls zmodyfikuje RCX (x64) i EDX:EAX (x86) przy użyciu nowego indeksu startowego repozytorium.
  3. HvCallSetVpRegisters może modyfikować wszystkie rejestry obsługiwane przez tę funkcję hypercall.
  4. RDX, R8 i XMM0 do XMM5, jeśli są używane do szybkiego wejścia funkcji hypercall, pozostają niezmodyfikowane. Rejestry używane na potrzeby szybkich danych wyjściowych funkcji hypercall można jednak modyfikować, w tym RDX, R8 i XMM0 do XMM5. Hyper-V zmodyfikuje te rejestry tylko dla szybkich danych wyjściowych funkcji hypercall, które są ograniczone do x64.

Rejestry lotne (ARM64 SMCCC)

Funkcja Hypercalls zmodyfikuje tylko określone wartości rejestru w następujących warunkach:

  1. X0 jest zawsze zastępowany wartością wyniku funkcji hypercall i parametrami wyjściowymi, jeśli istnieją.
  2. Rep hypercalls zmodyfikuje X1 przy użyciu nowego indeksu startowego repozytorium.
  3. HvCallSetVpRegisters może modyfikować wszystkie rejestry obsługiwane przez tę funkcję hypercall.
  4. X2 - X17, jeśli jest używany do szybkiego wejścia funkcji hypercall, pozostają niezmodyfikowane. Jednak rejestry używane do szybkiego wyjścia funkcji hypercall można modyfikować, w tym X2 - X17. Hyper-V zmodyfikuje te rejestry tylko na potrzeby szybkich danych wyjściowych funkcji hypercall.

Rejestry lotne (ARM64 HVC #1)

Funkcja Hypercalls zmodyfikuje tylko określone wartości rejestru w następujących warunkach:

  1. X0 jest zawsze zastępowany wartością wyniku funkcji hypercall i parametrami wyjściowymi, jeśli istnieją.
  2. Rep hypercalls zmodyfikuje X0 przy użyciu nowego indeksu startowego repozytorium.
  3. HvCallSetVpRegisters może modyfikować wszystkie rejestry obsługiwane przez tę funkcję hypercall.
  4. X1 — X17, jeśli jest używany do szybkiego wejścia funkcji hypercall, pozostają niezmodyfikowane. Rejestry używane do szybkiego wyjścia funkcji hypercall można jednak modyfikować, w tym X1 - X17. Hyper-V zmodyfikuje te rejestry tylko na potrzeby szybkich danych wyjściowych funkcji hypercall.

Ograniczenia funkcji Hypercall

Hipercalls może mieć skojarzone ograniczenia, które muszą być spełnione, aby mogły wykonywać ich zamierzone funkcje. Jeśli wszystkie ograniczenia nie zostaną spełnione, funkcja hypercall zakończy działanie z odpowiednim błędem. Jeśli istnieją jakiekolwiek ograniczenia, zostaną wyświetlone następujące ograniczenia:

  • Partycja wywołująca musi mieć określone uprawnienia
  • Partycja, na która jest wykonywana, musi być w określonym stanie (np. "Aktywny")

Kody stanu funkcji Hypercall

Każdy hipercall jest udokumentowany jako zwracanie wartości wyjściowej zawierającej kilka pól. Pole wartości stanu (typu HV_STATUS) służy do wskazania, czy wywołanie zakończyło się pomyślnie, czy nie powiodło się.

Ważność parametru wyjściowego w przypadku nieudanych funkcji Hypercalls

O ile jawnie nie określono inaczej, gdy hiperwołanie kończy się niepowodzeniem (oznacza to, że pole wyniku funkcji hypercall zawiera wartość inną niż HV_STATUS_SUCCESS), zawartość wszystkich parametrów wyjściowych jest nieokreślona i nie powinna być badana przez obiekt wywołujący. Tylko wtedy, gdy funkcja hypercall powiedzie się, wszystkie odpowiednie parametry wyjściowe zawierają prawidłowe, oczekiwane wyniki.

Kolejność warunków błędu

Kolejność wykrywania i zgłaszania warunków błędu przez funkcję hypervisor jest niezdefiniowana. Innymi słowy, jeśli istnieje wiele błędów, funkcja hypervisor musi wybrać warunek błędu do raportowania. Należy podać priorytet tym kodom błędów zapewniającym większe bezpieczeństwo. Celem jest uniemożliwienie funkcji hypervisor ujawnienia informacji obiektom wywołującym, którzy nie mają wystarczających uprawnień. Na przykład kod HV_STATUS_ACCESS_DENIED stanu jest preferowanym kodem stanu na jeden, który ujawniałby pewne informacje kontekstowe lub informacje o stanie wyłącznie na podstawie uprawnień.

Typowe kody stanu funkcji Hypercall

Kilka kodów wyników jest wspólnych dla wszystkich hiperwzlotów i dlatego nie są udokumentowane dla każdego hipercall indywidualnie. Należą do nich następujące elementy:

Kod stanu Warunek błędu
HV_STATUS_SUCCESS Wywołanie powiodło się.
HV_STATUS_INVALID_HYPERCALL_CODE Kod funkcji hypercall nie jest rozpoznawany.
HV_STATUS_INVALID_HYPERCALL_INPUT Liczba powtórzeń jest niepoprawna (na przykład liczba powtórzeń niezerowych jest przekazywana do wywołania innego niż rep lub liczba powtórzeń zero jest przekazywana do wywołania rep).
Indeks rozpoczęcia repozytorium nie jest mniejszy niż liczba powtórzeń.
Bit zarezerwowany w określonej wartości wejściowej funkcji hypercall jest inny niż zero.
HV_STATUS_INVALID_ALIGNMENT Określony wskaźnik wejścia lub wyjścia GPA nie jest wyrównany do 8 bajtów.
Określony parametr wejściowy lub wyjściowy zawiera strony.
Wejściowy lub wyjściowy wskaźnik GPA nie znajduje się w granicach przestrzeni gpA.

Kod HV_STATUS_SUCCESS zwracany wskazuje, że nie wykryto warunku błędu.

Raportowanie tożsamości systemu operacyjnego gościa

System operacyjny gościa uruchomiony w ramach partycji musi zidentyfikować się w funkcji hypervisor, zapisując jego podpis i wersję do msR (HV_X64_MSR_GUEST_OS_ID/HvRegisterGuestOsId), zanim będzie mógł wywołać hiperwołyny. Ten protokół MSR jest podzielony na partycje i jest współużytkowany przez wszystkie procesory wirtualne.

Wartość tego rejestru początkowo wynosi zero.

Na x86/x64 wartość niezerowa musi być zapisywana w identyfikatorze systemu operacyjnego gościa przed włączeniem strony kodu funkcji hypercall (zobacz Ustanawianie interfejsu funkcji Hypercall (x86/x64)). Jeśli rejestr zostanie później zerowany, strona kodowa funkcji hypercall zostanie wyłączona.

W usłudze ARM64 wartość niezerowa musi być zapisywana w identyfikatorze MSR systemu operacyjnego gościa, zanim można wywołać kody hipercall. Wyjątkiem jest hypercallSetVpRegisters/HvCallGetVpRegisters hypercallers . Aby uzyskać więcej informacji, zobacz odpowiednią dokumentację.

#define HV_X64_MSR_GUEST_OS_ID 0x40000000
#define HvRegisterGuestOsId 0x00090002

W usłudze ARM64 obsługiwane są tylko elementy HvRegisterGuestOsId, które muszą być zapisywane przy użyciu funkcji hypercallSetVpRegisters HvCallSetRegisters na procesorze rozruchowym.

Tożsamość systemu operacyjnego gościa dla zastrzeżonych systemów operacyjnych

Poniżej przedstawiono zalecane kodowanie dla tego msR. Niektóre pola mogą nie dotyczyć niektórych systemów operacyjnych gościa.

Bity (No changes needed) Description
15:0 Numer kompilacji Wskazuje numer kompilacji systemu operacyjnego
23:16 Wersja usługi Wskazuje wersję usługi (na przykład numer dodatku Service Pack)
31:24 Wersja pomocnicza Wskazuje wersję pomocniczą systemu operacyjnego
39:32 Wersja główna Wskazuje wersję główną systemu operacyjnego
47:40 Identyfikator systemu operacyjnego Wskazuje wariant systemu operacyjnego. Kodowanie jest unikatowe dla dostawcy. Systemy operacyjne firmy Microsoft są kodowane w następujący sposób: 0=Undefined, 1=MS-DOS®, 2=Windows 3.x, 3=Windows®® 9x, 4=Windows® NT (i pochodne), 5=Windows® CE
62:48 Identyfikator dostawcy Wskazuje dostawcę systemu operacyjnego gościa. Wartość 0 jest zarezerwowana. Zobacz listę dostawców poniżej.
63 Typ systemu operacyjnego Wskazuje typ systemu operacyjnego. Wartość 0 reprezentuje zastrzeżony (zamknięty) system operacyjny. Wartość 1 reprezentuje system operacyjny typu open source.

Wartości dostawcy są przydzielane przez firmę Microsoft. Aby zażądać nowego dostawcy, zgłoś problem w repozytorium dokumentacji wirtualizacji usługi GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS).

Sprzedawca Wartość
Microsoft 0x0001
HPE 0x0002
BlackBerry 0x0003
LANCOM 0x0200

Usługa MSR tożsamości systemu operacyjnego gościa dla systemów operacyjnych typu open source

Poniższe kodowanie jest oferowane jako wskazówki dla dostawców systemów operacyjnych typu open source, którzy zamierzają być zgodni z tą specyfikacją. Zaleca się, aby systemy operacyjne typu open source przyjęły następującą konwencję.

Bity (No changes needed) Description
15:0 Numer kompilacji Informacje specyficzne dla dystrybucji (np. numer kompilacji).
47:16 wersja Nadrzędne informacje o wersji jądra.
55:48 Identyfikator systemu operacyjnego Dodatkowe informacje o dostawcy
62:56 Typ systemu operacyjnego Typ systemu operacyjnego (np. Linux, FreeBSD itp.). Zobacz listę znanych typów systemu operacyjnego poniżej
63 Oprogramowanie open source Wartość 1 wskazuje system operacyjny typu open source.

Wartości typu systemu operacyjnego są przydzielane przez firmę Microsoft. Aby zażądać nowego typu systemu operacyjnego, zgłoś problem w repozytorium dokumentacji wirtualizacji usługi GitHub (https://aka.ms/VirtualizationDocumentationIssuesTLFS).

Typ systemu operacyjnego Wartość
Linux 0x1
FreeBSD 0x2
Xen 0x3
Illumos 0x4

Ustanawianie interfejsu funkcji Hypercall (x86/x64)

W systemie x86/x64 wywołania funkcji Hypercalls są wywoływane przy użyciu specjalnego kodu opcode. Ponieważ ten kod operacyjny różni się między implementacjami wirtualizacji, konieczne jest, aby funkcja hypervisor wyodrębniła tę różnicę. Odbywa się to za pośrednictwem specjalnej strony hipercall. Ta strona jest udostępniana przez funkcję hypervisor i pojawia się w obszarze gpa gościa. Gość jest wymagany do określenia lokalizacji strony przez programowanie funkcji Hypercall MSR gościa.

#define HV_X64_MSR_HYPERCALL 0x40000001
Bity Description Attributes
63:12 Hypercall GPFN — wskazuje numer strony fizycznej gościa strony hypercall Czytaj/zapisz
11:2 RsvdP. Bity powinny być ignorowane podczas operacji odczytu i zachowywania na zapisach. Zarezerwowana
1 Zablokowany. Wskazuje, czy msR jest niezmienny. W przypadku ustawienia ta funkcja MSR jest zablokowana, co uniemożliwia przeniesienie strony hiperwzlotu. Po ustawieniu tylko resetowanie systemu może wyczyścić bit. Czytaj/zapisz
0 Włączanie strony funkcji hypercall Czytaj/zapisz

Strona hipercall może zostać umieszczona w dowolnym miejscu w obszarze GPA gościa, ale musi być wyrównana do strony. Jeśli gość spróbuje przenieść stronę hipercall poza granice miejsca gpA, błąd #GP spowoduje zapisanie msR.

To msR to msR obejmujący całą partycję. Innymi słowy, jest on współużytkowany przez wszystkie procesory wirtualne w partycji. Jeśli jeden procesor wirtualny pomyślnie zapisze w msR, inny procesor wirtualny odczyta tę samą wartość.

Przed włączeniem strony hypercall system operacyjny gościa musi zgłosić swoją tożsamość, pisząc podpis wersji do oddzielnego msR (HV_X64_MSR_GUEST_OS_ID). Jeśli nie określono tożsamości systemu operacyjnego gościa, próby włączenia funkcji hypercall nie powiedzą się. Bit włączania pozostanie zerowy nawet podczas pisania do niego. Ponadto jeśli tożsamość systemu operacyjnego gościa zostanie wyczyszczone do zera po włączeniu strony hypercall, stanie się wyłączona.

Strona hipercall jest wyświetlana jako "nakładka" do przestrzeni GPA; oznacza to, że obejmuje wszystkie inne elementy mapowane na zakres GPA. Jego zawartość jest czytelna i wykonywalna przez gościa. Próby zapisu na stronie hipercall spowodują wyjątek ochrony (#GP). Po włączeniu strony hiperwołania wywołanie funkcji hypercall polega po prostu na wywołaniu do początku strony.

Poniżej znajduje się szczegółowa lista kroków związanych z ustanowieniem strony hipercall:

  1. Gość odczytuje identyfikator CPUID liścia 1 i określa, czy funkcja hypervisor jest obecna, sprawdzając bit 31 rejestru ECX.
  2. Gość odczytuje 0x40000000 liścia CPUID procesora CPUID procesora CPUID (zwrócony w rejestrze EAX) i cpuID liścia 0x40000001 w celu określenia sygnatury interfejsu (zwróconej w rejestrze EAX). Sprawdza, czy maksymalna wartość liścia jest co najmniej 0x40000005 i że podpis interfejsu jest równy "Hv#1". Ten podpis oznacza, że HV_X64_MSR_GUEST_OS_IDHV_X64_MSR_HYPERCALL i HV_X64_MSR_VP_INDEX są implementowane.
  3. Gość zapisuje swoją tożsamość systemu operacyjnego w msR HV_X64_MSR_GUEST_OS_ID , jeśli rejestr ma wartość zero.
  4. Gość odczytuje funkcję MSR funkcji Hypercall (HV_X64_MSR_HYPERCALL).
  5. Gość sprawdza bit Włącz stronę funkcji Hypercall. Jeśli został ustawiony, interfejs jest już aktywny, a kroki 6 i 7 powinny zostać pominięte.
  6. Gość znajduje stronę w przestrzeni GPA, najlepiej, która nie jest zajęta przez ram, MMIO itd. Jeśli strona jest zajęta, gość powinien unikać używania strony bazowej do innych celów.
  7. Gość zapisuje nową wartość w funkcji Hypercall MSR (HV_X64_MSR_HYPERCALL), która zawiera gpA z kroku 6 i ustawia bit Włącz stronę funkcji Hypercall w celu włączenia interfejsu.
  8. Gość tworzy wykonywalne mapowanie va na stronę hipercall strona GPA.
  9. Gość konsultuje 0x40000003 liścia CPUID, aby określić, które funkcje funkcji hypervisor są dostępne. Po ustanowieniu interfejsu gość może zainicjować funkcję hypercall. W tym celu wypełnia rejestry zgodnie z protokołem hypercall i wystawia wywołanie na początku strony hipercall. Gość powinien przyjąć, że strona hiperwołania wykonuje odpowiednik zbliżonego zwrotu (0xC3), aby powrócić do obiektu wywołującego. W związku z tym funkcja hypercall musi być wywoływana z prawidłowym stosem.

Ustanawianie interfejsu funkcji Hypercall (ARM64)

Ponieważ arm64 natywnie obsługuje instrukcję HVC, funkcja hypervisor nie wymaga dodatkowej konfiguracji w celu włączenia funkcji hypercalls.

Rozszerzony interfejs funkcji Hypercall

Hipercalls z kodami wywołań powyżej 0x8000 są nazywane rozszerzonymi hiperwołami. Rozszerzone hiperwoływały używają tej samej konwencji wywoływania co normalne hipercalls i wyglądają identycznie z perspektywy maszyny wirtualnej gościa. Rozszerzone hiperwoły są obsługiwane wewnętrznie w ramach funkcji hypervisor Hyper-V.

Rozszerzone możliwości funkcji hypercall mogą być odpytywane za pomocą funkcji HvExtCallQueryCapabilities.