Zaawansowane Architektury Procesorów 1. Rozwój
Transkrypt
Zaawansowane Architektury Procesorów 1. Rozwój
Zaawansowane Architektury Procesorów Część 1 1. Rozwój architektury x86 Intel 8086: Procesor w architekturze CISC. Posiadał tylko jeden tryb pracy tj. rzeczywisty, a więc wszystkie programy działały na poziomie zaufania systemowym. W stosunku do 8080 wprowadzono rozdzielnie pamięci na obszary danych, kodu i stosu. - przestrzeń adresowa pamięci - 1MB w trybie rzeczywistym; - 16-bitowa magistrala danych;, 20-bitowa magistrala adresowa; - 91 podstawowych typów rozkazów; - 7 trybów adresowania argumentów w pamięci; Intel 286: - potrafił jedynie przełączyć się w tryb chroniony, powrót do trybu rzeczywistego nie był możliwy bez resetu procesora - 24 bitowa magistrala adresowa ( 16 MB pamięci ) - pamięć powyżej 1 MB dostępna jedynie w trybie emulacji pamięci rozszerzonej ( tryb chroniony ) - możliwość wrzucenia na stos stałej natychmiastowej Intel 386: Pierwszy 32 bitowy procesor, posiadał 32 bitowe szyny danych i adresu. Rejestry zostały rozszerzone do 32 bitów ( przedrostek E- ) i rejestry ogólnego przeznaczenia mógłby być używane dowolnie poza - adres postaci [Rejestr_bazowy + Rejestr_indeksowy * skala + stała_przemieszczenia] - dodanie trybu wirtualnego ( v86 emulacja tryby rzeczywistego w trybie chronionym ) Format instrukcji 386: Prefiksy Kod operacyjny ModRM SIB Przemieszczenie 0..1B 1..3B 1..3B 0..1B 0,1,2,4B ModRM – specyfikacja argumentów lub rozszerzenie kodu operacyjnego SIB – Bajt specyfikacji trybu adresowania pamięci dla trybu indeksowego Stała 0,1,2,4B ModRM ( bajt różnie interpretowany w trybach 16- i 32-bitowym ) : - jeśli nie ma indeksu [czyli Rej_indeksowy=0], to cała informacja o trybie adresowania jest zakodowana w ModRM; - jeśli jednakże adresujemy z wykorzystaniem rejestru indeksowego, to jeszcze dodatkowo w SIB znajduje się informacja o skali i numer rejestru [czyli który rejestr jest wybierany do roli rejestru indeksowego] 2. Model systemowy x86 2.1 Globalna tablica deskryptorów, segmentacja Kiedy odwołujemy się do pamięci w trybie chronionym, wszystkie odwołania przechodzą przez globalną tablicę deskryptorów segmentów GDT ( lub przez lokalną LDT, ale w praktyce nie jest ona używana ). Deskryptory segmentów zawierają informację o adresie bazowym segmentu, prawach dostępu, długości segmentu oraz dodatkowe informacje jak np określenie czy jest to segment zwykły. Każdy deskryptor segmentu zawiera powiązany ze sobą selektor. Program chcąc odwołać się do obszaru w pamięci, musi przejść przez selektor, który wskazuje na deskryptor segmentu w GDT, który to zawiera adres bazowy obszaru w pamięci. Aby to było możliwe, procesor musi pracować w większym lub równym poziomie zaufania ( CPL ) niż określony w deskryptorze minimalny poziom zaufania ( DPL ), upoważniający do dostępu do danego segmentu. Jeżeli DPL posiada mniejszy poziom zaufania niż CPL, nadpisuje on CPL. Wyjątkiem są tzw. Conforming Code Segments, do których dostęp można uzyskać posiadając jedynie równy lub niższy poziom zaufania niż ich DPL oraz w takim wypadku CPL nie ulega zmianie. Poza segmentami niezbędnymi to działania systemu czyli segmencie danych, kodu oraz stosu, architektura x86 określa też segment stanu zadania ( TSS ) oraz segment lokalnej tablicy deskryptorów ( LDT ). GDT nie jest określane jako oddzielny segment, ponieważ nie istnieje selektor ani deskryptor segmentu dla niej. TSS i LDT posiadają zdefiniowane dla siebie selektory i deskryptory segmentów. Selektor jest 16 bitowym identyfikatorem segmentu. Składa się z indeksu ( 3-15 bitu ) wskazującego na jeden z 8192 deskryptorów w GDT lub LDT, bitu TI wskazującego, czy selektor wskazuje na deskryptor w LDT lub GDT oraz 2 bitów RPL ( Requested Privilege Level ). RPL porównuje aktualny CPL i określa, czy proces ma prawo do czytania deskryptora segmentu. Jeżeli autoryzacja się powiedzie, a RPL ma mniejszy poziom zaufania niż CPL, nadpisuje on CPL. 2.2 Bramy: Architektura x86 określa dodatkowo specjalne deskryptory segmentów zwane bramami. Kiedy jakiś proces chce uzyskać dostęp do jakiegoś bloku kodu, dostarcza on selektor wskazujący na bramę. Ta natomiast porównuje aktualny CPL z minimalną wartością poziomu zaufania która upoważnia do dostępu do danego kawałka kodu. Następnie po stwierdzeniu możliwości odwołania się przez wołający proces do danego bloku, procesor uzyskuje selektor wskazujący na odpowiedni deskryptor segmentu i offset do bloku kodu. Jeżeli wołanie wymaga zmiany CPL, procesor zamienia w tym momencie stosy. Selektor segmentu jest uzyskiwany z TSS. Bramy dodatkowo ułatwiają przełączanie się między segmentami kodu 16 i 32 bitowego Wprowadzono cztery rodzaje bram: a) Brama pułapki – stwierdza, czy dana procedura ma prawo do wołania procedury lub handlera w segmencie kodu wedle CPL i jeśli tak ładuje odpowiedni deskryptor do CS. b) Brama przerwania – analogicznie do bramy pułapki, jednak dodatkowo blokuje przerwania c) Brama wołania -analogicznie do bramy pułapki, dodatkowo kopiowanie ze stosu użytkownika danych z pola limitu do stosu systemowego d) Brama zadania – wskazuje na selektor TSS 2.3 Segment stanu zadania Segment stanu zadania definiuje stan środowiska dla aktualnie wykonywanego procesu. Dostęp do TSS odbywa się za pomocą deskryptora TSS zawartego w GDT, na który wskazuje rejestr zadania ( TR ). TSS jest obsługiwane przez mikrokod procesora. TSS zawiera aktualną zawartość dla rejestrów ogólnego przeznaczenia, rejestry segmentów, rejestr znaczników EFLAGS, rejestr wskaźnika instrukcji EIP oraz selektory segmentów. Zawiera też selektor dla LDT skojarzonego z aktualnym procesem oraz adres bazowy do jednostki stronicowania. Podczas przełączania się do innego procesu za pomocą instrukcji JUMP lub CALL, procesor otrzymuje selektor na TSS wołanego procesu, a następnie: a) Zapamiętywany jest kontekst aktualnego zadania w TSS b) Do rejestru zadań ładowany jest selektor wskazujący na TSS wołanego procesu c) Uzyskiwany jest dostęp do nowego TSS za pomocą deskryptora zawartego w GDT d) Do rejestrów procesora ładowane są dane z TSS ( wymienione wcześniej ) v86: TSS zostało później rozszerzone o mapę odbić wyjątków, wskazującą, czy dany wyjątek ma być obsługiwany przez system, czy też obsługa może zostać w trybie v86 Zawiera cztery wskaźniki stosu, po jednym dla każdego poziomu zaufania. IO Map Adress Base – offset od adresu TSS który wskazuje, gdzie znajduje się tablica portów I/O do których proces ma dostęp. 2.4 Obsługa przerwań Wszystkie przerwania w architekturze x86 są obsługiwane przez tablicę deskryptorów przerwań ( IDT ). Zawiera ona kolekcje deskryptorów bram ( maksymalnie 256 ) , które umożliwiają dostęp do handlerów obsługi przerwań i wyjątków. Podobnie jak GDT, IDT nie jest segmentem. Adres do IDT przechowywany jest w rejestrze IDTR. Wyjątki systemowe i przerwania mają minimalny poziom zaufania ustawiony na 0 czyli systemowy. Sprzęt, dodatkowy kontroler obsługi przerwań lub oprogramowanie dostarcza procesowi wektor obsługi przerwań. Wektor obsługi przerwań zawiera indeks do IDT. Jeżeli wskazywana brama jest bramą pułapki lub przerwań, dostęp do procedury obsługi przerwania jest analogiczny jak wołanie procedury przez bramę wołania. Jeżeli jest to brama zadania, dostęp do procedury obsługi jest wykonywany poprzez przełączanie zadań. W trybie rzeczywistym każdy program może ładować własne deskryptory do IDT i w ten sposób nadpisywać obsługę przerwań własnymi handlerami. W trybie chronionym muszą tam znajdować się bramy których program użytkownika nie może modyfikować. W trybie v86 generowany jest wyjątek, procesor przechodzi do poziomu zaufania systemowego, a następnie pyta jednostkę stronicowania, gdzie w pamięci znajduje się sesja DOSowa, sięga w niej do zapisanej dla niej tablicy przerwań i tam dopiero przechodzi do obsługi wyjątku ( co jest bardzo nieoptymalne ). Mikrokod procesora zapamiętuje na stosie starą ramkę obsługi wyjątku i przygotowuje nową – odbijanie wyjątków. Błąd podwójny – jeżeli proces wyleci na wyjątku, a następnie w obsłudze tego wyjątku przez system znów wygenerowany zostanie wyjątek. Taka sytuacja nie powinna mieć miejsca w dobrze napisanym systemie operacyjnym. 3. Tryby działania w x86 a) Tryb rzeczywisty: procesor zawsze startuje w tym trybie. Działa wtedy na poziomie zaufania systemowym i posiada dostęp do pierwszego megabajta pamięci. b) Tryb chroniony: tryb z ochroną zasobów. c) Tryb wirtualnej emulacji 8086: tryb udawanego tryby rzeczywistego. Umożliwia za pomocą emulacji maszyny wirtualnej odpalanie programów DOSowych, które muszą pracować w trybie rzeczywistym ( albo myśleć jedynie, że w takim pracują, czyli właśnie w trybie emulacji 8086 ) . Tryb ten nie może działać bez stronicowania. W trybie v86 sesja jest uruchamiana na poziomie zaufania 3 i udaje tryb niechroniony, czyli proces ze swojego punktu widzenia może pisać po wszystkich rejestrach i znacznikach ( ale tak naprawdę czuwa nad tym system ) d) Tryb SMM: urzęduje w pierwszym megabajcie pamięci, jest on równoległym trybem przeznaczonym do obsługi specyficznych zadań sprzętu, jak np zarządzaniu energią w notebookach. System operacyjny nie widzi zasobów tego trybu. Procesor przechodzi do trybu SMM poprzez przerwanie SMI e) Tryb IA-32e: czyli jak sama nazwa wskazuje tryb pracy 64 bitowy. W tym trybie procesor udostępnia dwa tryby: tryb zgodności oraz tryb 64 bitowy. 3.1 Przejście z trybu rzeczywistego do tryby chronionego Aby przejść z trybu rzeczywistego do tryby chronionego muszą zostać zainicjowane następujące struktury danych: a) IDT, GDT, TSS b) chociaż jedna strona oraz tablica stron dla stronicowania c) segment kodu, który zawiera kod jaki zostanie wykonany po przejściu w tryb chroniony d) części kodu zawierające kod obsługi przerwań i wyjątków Ponadto trzeba zainicjować rejestry systemowe: a) GDTR, CR0 do CR4, IDTR Procedura przełączania trybu jest następująca: a) Wyłączenie przerwań b) Wywołanie instrukcji LGDT, w celu załadowania adresu bazowego GDT do GDTR c) Ustawienie flagi PE w rejestrze CR0 d) Wywołanie instrukcji CALL lub JMP e) Wywołanie LTR w celu załadowania adresu bazowego TSS do TR f) Ponowne wywołanie JMP lub CALL aby przełączyć procesor na nowe zadanie g) Wywołanie LIDT w celu załądowania adresu bazowego IDT do IDTR h) Włączenie przerwań STI 4. Znaczniki systemowe w rejestrze EFLAGS Skrót Nazwa Opis TF Trap Flag po każdej instrukcji będzie wywoływana pułapka śledzenia, co jest potrzebne do debugowania programu. IF Interrupt Flag kontroluje odpowiedź procesora na sprzętowe przerwania maskowalne. Wyzerowana powoduje brak reakcji na sprzętowe przerwania maskowalne przez procesor. IOPL I/O Privilege Level ustawiana podczas przełączania zadań, informuje o tym na jakim poziomie zaufania można wykonywać instrukcje I/O NT Nested Task kontroluje łańcuch wywołań przerwań lub przełączania zadań. Ustawiana w przypadku wołania innego zadania lub osługi wyjątku RF Resume Flag wyłączenie pułapki śledzenie na czas trwania jeden instrukcji, dzięki czemu można uniknąć zakleszczenia VM Virtual Machine ustawiona, oznacza, że proces pracuje w trybie v86 AC Aligment Check ustawiona, powoduje sprawdzanie wyrównania danych. W przypadku negatywnej weryfikacji generowany jest wyjątek braku wyrównania danych. ID Identyfication VIF umożliwia wywołanie instrukcji CPUID, która zwraca informacje o procesorze ( dzięki czemu wiadomo jakie instrukcje on obsługuje np. ) odpowiednik IF w trybie wirtualnym. 5. Rejestry Kontrolne ( CR0..4 ) a) CR0 – zawiera znaczniki systemowe oraz stany procesora b) CR2 – zawiera adres bazowy strony która spowodowała błąd stronicowania c) CR3 – zawiera adres bazowy do tablicy deskryptorów jednostki stronicowania d) CR4 – zawiera rejestr znaczników, które rozszerzają architekturę o specyficzne ficzery które oferuje dany model procesora Znaczniki CR0: Skrót Nazwa Opis PG Paging CD Cache Disable NW włącza jednostkę stronicowania wyłączenie kieszeni Not Write through wyłączenie „write-back” czyli zapisu do pamięci podręcznej z odłożeniem zapisu do pamięci głównej na później oraz „writethrough” czyli zapisu i do kieszeni i do pamięci głównej AM Aligment Mask włączenie automatycznego wyrównania danych WP Write Protection uniemożliwienie zapisu strony przez system, która jest tylko do odczytu, umożliwia leniwą alokację NE Numeric Error włącza natywny mechanizm komunikowania błędów przez jednostkę zmiennopozycyjną TS Task Switched włącza leniwe przełączanie kontekstu EM Emulation Znaczniki CR3: Skrót włącza emulowanie jednostki zmiennopozycyjnej Nazwa Opis TSD Time Stamp Disable wyłącza licznik czasu, określa czy aplikacja może używać TSC DE Debugging Extenstions włącza dodatkowe rejestry dla debuggowania PGE Page Global Enable włącza włącza obsługę globalnych deskryptorów stron, które nie bedą wyrzucane z bufora translacji przy przełączaniu kontekstów ( co jest drogie ) PSE Page Size Extensions Włącza 4MB strony PAE Page Adress Extensions włącza ulepszone stronicowanie, generowanie adresu z więcej niż 32 bitów, wymagane do obsługi trybu 64bitowego MCE Machine Check Enable włącza diagnostyki sprzętowej PCE Performance Monitoring włączenie licznika wydajności Enable OSFXSR Operating System włącza instrukcje SSE Support for FXSAVE and FXRSTOR PCIDE PCID-Enable Bit włącza identyfikowanie właściciela strony Tryb 64 bitowy ( IA-32e ) Tryb 64 bitowy wprowadza zestaw 2x więcej rejestrów, stare zostają rozszerzone do 64 bitów ( dodano przedrostek R, który umożliwa dostęp do dodatkowych rejestrów, operowanie na 64 bitowych rejestrach oraz do SK i DPL ). Dodano dwa razy więcej rejestrów dla jednostki wektorowej i opcjonalność wołanie rejestru ramki. Wszystkie rejestry mają widoczne 8 bitowe części. Usunięto parę nadmiarowych instrukcji ( np. 1 bajtowe INC ). Wprowadzono podział pamięci na cześć użytkownika ( od 0 do 2^48 ) i część systemu ( 2^64 do 2^64 – 2^48 ). Część pamięci między nimi jest niemożliwa do zaadresowania. Maszyna Wirtualna - Udawanie przed systemem operacyjnym, że ten ma dostęp do sprzętu i zasobów, do których on tak naprawdę nie ma dostępu. Aby ten mechanizm działał, system nie może się dowiedzieć, że nie jest prawdziwym systemem operacyjnym. W wirtualizacji spychamy emulowany system operacyjny do poziomu użytkownika. Aby komputer umiał się wirtualizować, musi mieć możliwość zabraniania dostępu do zasobów systemowych, co spowoduje, że emulowany program wyleci na błędzie i już prawdziwy system będzie mógł zasymulować działanie tych zasobów. Leniwa alokacja - Kiedy proces chce zaalokować pamięć, system wpisuje do tablicy deskryptorów deskryptory jednej strony oznaczone jako tylko do odczytu. Kiedy później użytkownik próbuje zapisać coś do tych stron, wylatuje na błędzie i dopiero wtedy jest ona odblokowywana i fizycznie przydzielana.