Próbuję dowiedzieć się, czy można uruchomić maszynę wirtualną z systemem Linux, której pamięć RAM jest obsługiwana tylko przez jedną fizyczną stronę.
Aby to zasymulować, zmodyfikowałem moduł obsługi błędów zagnieżdżonej strony w KVM, aby usunąć obecny bit ze wszystkich pozycji zagnieżdżonej tablicy stron (NPT), oprócz tego odpowiadającego aktualnie przetwarzanej usterce strony.
Podczas próby uruchomienia gościa z systemem Linux zaobserwowałem instrukcje montażu, które używają operandów pamięci, takich jak
add [rbp+0x820DDA], ebp
prowadzić do pętli błędów strony, dopóki nie przywrócę obecnego bitu dla strony zawierającej instrukcję, a także dla strony, do której odwołuje się operand (w tym przykładzie [rbp+0x820DDA]
).
Zastanawiam się, dlaczego tak jest. Czy CPU nie powinien uzyskiwać dostępu do stron pamięci sekwencyjnie, tzn. Najpierw przeczytać instrukcję, a następnie uzyskać dostęp do operandu pamięci? Czy też x86 wymaga, aby strona instrukcji, jak również wszystkie strony argumentów były dostępne w tym samym czasie?
Testuję na AMD Zen 1.
Odpowiedzi:
Tak, wymagają kodu maszynowego i wszystkich operandów pamięci.
Tak, logicznie, co się dzieje, ale wyjątek błędu strony przerywa ten 2-etapowy proces i odrzuca wszelkie postępy. Procesor nie ma sposobu na zapamiętanie instrukcji, która była w środku, kiedy wystąpił błąd strony.
Gdy moduł obsługi błędów strony powróci po przetworzeniu prawidłowego błędu strony, RIP = adres instrukcji powodującej błąd, więc procesor próbuje wykonać ją od nowa .
Dopuszczalne byłoby, aby system operacyjny zmodyfikował kod maszynowy instrukcji powodującej błąd i oczekiwał, że wykona inną instrukcję później
iret
niż program obsługi błędów stronicowania (lub innego wyjątku lub procedury obsługi przerwań). AFAIK wymaga architektonicznie, aby CPU powtórzył pobieranie kodu z CS: RIP w przypadku, o którym mówisz. (Zakładając, że nawet wraca do błędu CS: RIP zamiast planować inny proces podczas oczekiwania na dysk z uszkodzeniem strony twardej lub dostarczanie SIGSEGV do procedury obsługi sygnału z powodu błędu nieprawidłowej strony).Prawdopodobnie jest to również wymagane architektonicznie do wejścia / wyjścia hiperwizora. I nawet jeśli nie jest to wyraźnie zabronione na papierze, nie tak działają procesory.
@torek komentuje, że niektóre mikroprocesory (CISC) częściowo dekodują instrukcje i zrzucają stan mikroprocesora na błąd strony , ale x86 nie jest taki.
Kilka instrukcji jest przerywalnych i może dokonywać częściowych postępów, takich jak
rep movs
(memcpy w puszce) i innych instrukcji łańcuchowych, lub gromadzić ładunki / sklepy rozproszone. Ale jedynym mechanizmem jest aktualizacja rejestrów architektonicznych, takich jak RCX / RSI / RDI dla operacji łańcuchowych, lub rejestrów docelowych i masek dla gromadzeń (np. Instrukcja dla AVX2vpgatherdd
). Nieprzestrzeganie kodu operacji / dekodowania powoduje ukryty rejestr wewnętrzny i ponowne uruchomienie go po przejściu do procedury obsługi błędów strony. Są to instrukcje, które wykonują wiele różnych dostępu do danych.Należy również pamiętać, że x86 (jak większość ISA) gwarantuje, że instrukcje są niepodzielne na wrt. przerwania / wyjątki: albo w pełni się zdarzają, albo w ogóle nie zdarzają się przed przerwaniem. Przerwanie instrukcji montażu podczas jej działania . Tak więc na przykład
add [mem], reg
konieczne byłoby odrzucenie ładunku, jeśli część sklepu uległa awarii, nawet bezlock
prefiksu.Najgorszy przypadek liczby stron w przestrzeni użytkownika gościa, które mogą wykonać postęp, może wynosić 6 (plus osobne poddrzewa tabeli stron jądra gościa dla każdego z nich):
movsq
lubmovsw
instrukcja 2-bajtowa obejmująca granicę strony, więc obie strony są potrzebne do jej odkodowania.[rsi]
również podział strony[rdi]
również podział stronyJeśli którakolwiek z tych 6 stron ulegnie awarii, wrócimy do pierwszej.
rep movsd
jest także instrukcją 2-bajtową, a zrobienie postępu na jednym jej etapie wymagałoby takich samych wymagań. Podobne przypadki, takie jakpush [mem]
lubpop [mem]
mogą być skonstruowane z niewłaściwie wyrównanym stosem.Jednym z powodów (lub korzyści dodatkowych) dla / spowodowania, aby gromadzenie ładunków / rozproszenie sklepów było „przerywalne” (aktualizowanie wektora maski wraz z ich postępem), jest unikanie zwiększania tego minimalnego poziomu zajmowanego miejsca w celu wykonania pojedynczej instrukcji. Również w celu poprawy wydajności obsługi wielu błędów podczas jednego zbierania lub rozpraszania.
@Brandon wskazuje w komentarzach, że gość będzie potrzebował swoich tabel stron w pamięci , a podziały stron w przestrzeni użytkownika mogą być również podziałami 1GiB, więc obie strony znajdują się w różnych poddrzewach PML4 najwyższego poziomu. Aby przejść do następnego kroku, przejdź do strony sprzętowej. Sytuacja, w której ta patologia nie wystąpi przypadkowo.
TLB (i elementy wewnętrzne walkera) mogą buforować niektóre dane tabeli stron i nie są wymagane do ponownego uruchomienia przejścia strony od zera, chyba że system operacyjny to zrobił
invlpg
lub nie ustawił nowego katalogu stron najwyższego poziomu CR3. Żadne z nich nie jest konieczne przy zmianie strony z nieobecnej na prezentowaną; x86 na papierze gwarantuje, że nie jest potrzebne (więc „negatywne buforowanie” nieobecnych PTE jest niedozwolone, przynajmniej niewidoczne dla oprogramowania). Dlatego procesor może nie VMexit, nawet jeśli niektóre strony fizycznej strony tabeli gościa nie są faktycznie obecne.Liczniki wydajności PMU można włączyć i skonfigurować tak, że instrukcja wymaga również zdarzenia perf do zapisu w buforze PEBS dla tej instrukcji. Gdy maska licznika jest skonfigurowana do liczenia tylko instrukcji przestrzeni użytkownika, a nie jądra, może być tak, że próbuje przepełnić licznik i przechowywać próbkę w buforze za każdym razem, gdy wracasz do przestrzeni użytkownika, powodując błąd strony.
źródło
push dword [foo
” (lub nawet po prostucall [foo]
), gdy wszystko jest źle wyrównane w „granicy tabeli wskaźników stronicowania” (dodając do 6 stron, 6 tabel stron, 6 katalogów stron, 6 PDPT i jednego PML4); z włączoną i skonfigurowaną funkcją „precyzyjnego próbkowania opartego na zdarzeniach z buforem PEBS”, którapush
powoduje dodanie danych monitorowania wydajności do bufora PEBS. Dla konserwatywnych „minimalnych stron dostarczonych przez gospodarza, aby gość mógł robić postępy w przypadkach patologicznych” chciałbym mieć co najmniej 16 stron.EIP
. Jest więc logiczne pytanie uzupełniające. Jaka jest minimalna potrzebna liczba stron przy założeniu inteligentnego schematu łatek instrukcji? Np. Skopiuj niezrównaną wartość do wyrównanego bufora scratch, emuluj instrukcję i IRET do następnej instrukcji.iret
również musi znajdować się w pamięci. Jest to instrukcja jednobajtowa, więc jedna dodatkowa strona. Adres przerwania procedury obsługi błędów strony również musi znajdować się w pamięci, ale może to być ta sama strona, co powyżej.