!tutorials, tryb chroniony 3, Modele pamięci


Wyznaczanie adresu fizycznego w trybie rzeczywistym

Adres fizyczny komórki pamięci w trybie rzeczywistym procesorów serii Intel x86 jest wyznaczany z adresu logicznego, wg. następującej zależności:

Adres fizyczny = segment? * 16 + offset?

segment - obszar w przestrzeni logicznej

offset - identyfikuje komórkę pamięci w obszarze pamięci (segmencie); jest to liczba wskazująca na komórkę pamięci względem początku segmentu

Wyznaczanie adresu fizycznego w trybie chronionym

Adres fizycznej komórki pamięci w trybie chronionym jest wyznaczany po wykonaniu szeregu obliczeń. Proces ten możemy podzielić na 2 etapy:

  1. Wyznaczeniu adresu liniowego (segmentacja)

  2. Przekształcenie adresu liniowego do adresu fizycznego (stronicowanie)

Na poniższym rysunku pokazany został proces translacji adresu logicznego do fizycznego.

0x01 graphic

Daną wejściową w procesie jest adres logiczny opisywany za pomocą selektora i offsetu. Adres liniowy jest wyznaczany na podstawie danych pamiętanych w strukturach deskryptorowych (globalna tablica deksryptorów, lokalna tablica deskryptorów, tablica deskryptorów przerwań). Adres fizyczny wyznaczany jest z adresu liniowego przy wykorzystaniu struktur danych jakimi są tablice katalogów stron i tablice stron. W związku, z tym że decyzje o uruchomieniu stronicowania podejmowania jest przez projektanta oprogramowania, adres liniowy może być równoważny adresowi fizycznemu (przy wyłączonym stronicowaniu).

Poniższy rysunek uszczegóławia proces wyznaczania adresu fizycznego w trybie chronionym.

0x01 graphic

Poniższy rysunek pokazuje metodę wyliczania adresu liniowego.

0x01 graphic

Adres liniowy zatem można wyznaczyć wg następującej zależności:

Adres liniowy = TD[selektor? >> 3].base + offset

gdzie:

Modele pamięci

Płaski model pamięci

Cechy modelu:

  1. Wspólna przestrzeń adresowa dla wszystkich uruchamianych procesów, lub programów

  2. Brak ochrony przed próbą adresacji pamięci znajdującą się poza przestrzenią pamięci fizycznej

  3. Najprostszy do implementacji w trybie chronionym

Na poniższym rysunku pokazana została konfiguracja oprogramownaia systemowego (np. systemu operacyjnego), która organizuje pamięć zgodnie z modelem płaskim.

0x01 graphic

W celu uruchomienia oprogramowania korzystającego z płaskiego modelu pamięci należy wykonać następujące działania:

  1. Utworzyć globalną tablicę deskryptorów

  2. W GDT utworzyć następujące deskryptory:

  1. W deskryptorach przypisać następujące wartości do pól:

  1. Do rejestrów selektorów wpisać:

Po inicjalizacji uzyskuje się środowisko, w którym poszczególne procesy, lub programy współdzielą się dostępną pamięcią. Programista widzi pamięć jako jeden blok o wielkości 4GB.

Płaski chroniony model pamięci

Cechy modelu:

  1. Wspólna przestrzeń adresowa dla wszystkich procesów, lub programów

  2. Adresacja możliwa w obrębie dostępnej pamięci fizycznej; dostęp poza zdefiniowane obszary pamięci fizycznej sygnalizowany wyjątkami

  3. Obszary pamięci przeznaczone na kod i dane mogą być odseparowane

Na poniższym rysunku pokazana została konfiguracja oprogramowaina systemowego (np. systemu operacyjnego), która organizuje pamięć zgodnie z modelem płaskim chronionym.

0x01 graphic

W celu uruchomienia oprogramowania korzystającego z płaskiego chronionego modelu pamięci należy wykonać następujące działania:

  1. Utworzyć globalną tablicę deskryptorów

  2. W GDT utworzyć następujące deskryptory:

  1. W deskryptorze segmentu danych przypisać następujące wartości do pól:

  1. W deskryptorze segmentu kodu przypisać następujące wartości do pól:

  1. Do rejestrów selektorów wpisać:

Pozostałe pola danych deskryptorów należy dopasować do organizacji dostępnej pamięci. Model ten ma zastosowanie w przypadkach, gdy system komputerowy posiada zorganizowaną pamięć fizyczną w postaci bloków mapowanych do przestrzeni adresowej w taki sposób, że pamieć ta nie ma ciągłej adresacji (występują obszary, które nie są powiązane z fizyczną pamięcią). Zastosowanie w takim przypadku modelu płaskiego może prowadzić do sytuacji awarii na skutek nieuważnego napisania oprogramowania systemowego.

Model pamięci z segmentacją

Cechy modelu:

  1. Przestrzeń adresowa pamięci dzielona jest na segmenty

  2. Obszary pamięci (segmenty) są przypisywane do określonego oprogramownaia (procesów)

  3. Możliwa separacja obszarów pamięci przypisanych do różnych procesów, lub programów

  4. Wykrywanie naruszeń obszarów pamięci i sygnalizacja ich w postaci wyjątków

Na poniższym rysunku pokazana została ogólna idea konfiguracji oprogramownaia systemowego (np. systemu operacyjnego) w modelu z segmentacją.

0x01 graphic

Rozwijanie oprogramowania przy organizacji pamięci zgodnie z modelem z segmentacją wymaga od projektanata systemu szeregu decyzji. Funkcjonalność, którą otrzymuje on, jest na tyle duża, że może wybierać w szeregu wariantów. W związku z tym należy podjąć szereg decyzji, do których należy zaliczyć:

  1. Czy wykorzystywana jest tylko globalna tablica deskryptorów, czy także lokalne

  2. Czy każy z uruchamianych procesów ma definiowane segmenty: kodu, danych, stosu, czy może semgent stosu zawsze jest lokowany w segmencie danych

  3. Czy przestrzeń adresowa segmentów danych i kodu procesu (programu) jest wspólna czy rozdzielona (segmentu kodu i danych współdzielą fizyczną przestrzeń adresową)

Tablice deskryptorów

Ogólne infomacje

W poprzednim rozdziale opisane zostały modele pamięci, które można wykorzystać do budowania architektury oprogramowania systemowego. W niniejszym rozdziale uszczegółowione zostaną pojęcia jakimi są tablice deskryptorów (GDT, LDT, IDT).

Tablica deksryptorów to struktura danych wykorzystywana przez mikroprocesor w operacjach wykonywania oprogramowania. Dostarcza ona informacji niezbędnych do wyznaczania adresów fizycznych komórek pamięci i zarządzania dostępem do wykorzystywanych obiektów systemowych (segmenty kodu, danych, zadania, furtki, itp). Przyjęte rozwiązanie ma za zadanie dostarczenie mechanizmów sprzętowych wspierających implementację systemów operacyjnych. Zastosowanie tych mechanizmów pozwala na wspieraną sprzętowo implementację wielozadaniowości (wieloprocesowości), współbieżnego wykonywania się procesów na jednej jednostce wykonawczej, mechanizmów zarządzania pamięcią, w tym tzw. swap'ami, mechanizmami ochrony obszarów pamięci zarządzanych przez system operacyjny i oprogramowanie użytkowników.

Elementem tablicy deskryptorów jest obiekt danych noszący nazwę deskryptora. Deskryptor jest strukturą danych opisujących jakiś obiekt systemowy (np. segment danych, czy kodu). Posiada zawsze jednakową szerokość, wynoszącą 8 bajtów. W tablicy deskryptorów można pomieścić do 8192 deskryptory.

Na poniższym rysunku pokazane zostały elementy systemu wykorzystującego tablice deskryptorów (bez tablicy deskryptorów przerwań).

0x01 graphic

Wyróżniamy następujące tablice deskryptorów:

1. GDT - globalną tablicę deskryptorów

2. LDT - lokalną tablicę deskryptorów

3. IDT - tablicę deskryptorów przerwań

Globalna tablica deskryptorów

GDT jest jedyną wymaganą, do funkcjonowania oprogramowania, w trybie chronionym tablicą deskryptorów. Położenie i wielkość tej tablicy jest zapisana w rejestrze GDTR. Mogą być w niej zdefniowane wszystkie typy deskryptorów z wyjątkiem furtek przerwania, potrzesku. Pierwszy deskryptor w GDT (indeks 0) nosi nazwę zerowego deskryptora i nie może być wykorzystany dla celów opisu obiektu systemowego.

Lokalna tablica deskryptorów

LDT jest tablicą, która zwyczajowo jest wykorzystywana do definiowania obiektów systemowych związanych z jakimś procesem. W takim przypadku przed uruchomieniem procesu jest ona aktywowana przez załadowanie prawidłowej wartości do rejestru LDTR. Deskryptor definiujący tablicę LDT jest pamiętany w GDT. Załadowanie rejestru LDTR odbywa się przez wskazanie deskryptora w GDT. W lokalnej tablicy deskryptorów mogą być zdefiniowane następujący typy deskryptorów:

1. segmentów kodu

2. segmentów danych

3. furtek wywołania

4. furtek zadania

Tablica deskryptorów przerwań

IDT jest tablicą przenoszącą informację o podprogramach obsługi przerwań (zarówno sprzętowych, programowych i wyjątków). Jej definicja jest niezbędna w momencie, gdy jakiekolwiek oprogramowanie pracujące w trybie chronionym korzysta, lub obsługuje zdarzenia zgłaszane przy pomocy przerwań, lub wyjątków. Tablice można pominąć tylko w przypadku całkowitego zamaskowania przerwań. Położenie i wielkość tabeli jest określane zapomocą rejestru IDTR.

Rejestry GDTR, LDTR, IDTR

Na poniższym rysunku pokazana została struktura rejestrów tablic deskryptorowych.

0x01 graphic

Jak widać z rysunku:

1. Lokalizację tablic IDT i GDT są opisywane za pomocą 32bitowego adresu liniowego (gdy wyłączone stronicowanie adresu fizycznego)

2. Tablice mogą zajmować conajwyżej 65536 bajtów (maksymalna wartość, którą można wpisać w pole limit to 0FFFFh). Powoduje to, że w tabelach można maksymalnie zmieścić 8192 deskryptory

3. Lokalizacja tablicy LDT jest opisywana za pomocą deskryptora zlokalizowanego w GDT

Selektor

Wprowadzenie

W poprzednich rozdziałach pojęcie selektora było wykorzystanie do opisu metody wyznaczania adresu fizycznej komórki pamięci. Określony został wzór na wyznaczanie adresu liniowego, z którego wynikało, że selektor nie przenosi tylko numeru deskryptora w tablicy deskryptorów. W niniejszy rozdział dostarczy informacji uzupełniających dotyczących selektora.

Struktura selektora

Poniższy rysunek pokazuje budowę slektora.

0x01 graphic

Selektor jest strukturą danych zawierającą następujące pola:

1. Index - indeks w tabeli deskryptorów (GDT, lub LDT)

2. TI - wskaźnik tablicy deskryptorów (0 - GDT, 1 - LDT)

3. RPL - wymagany poziom uprzywilejowania (pole omawiane w rozdziale poświęconym poziomom ochrony)

Liczba jest interpretowana przez procesor jako selektor w momencie ładowania jej do rejestrów selektorów (CS, DS, ES, FS, GS).

W poniższej tabeli zamieszczone zostały przykłady selektorów

LP

Wartość

Interpretacja

1

8h

Indeks: 1; TI: 0; RPL: 0

Odwołanie do pierwszego deskryptora GDT wymagany poziom uprzywilejowania 0

2

0Ch

Indeks: 1; TI: 1; RPL: 0

Odwołanie do pierwszego deskryptora LDT wymagany poziom uprzywilejowania 0

3

0Fh

Indeks: 1; TI: 1; RPL: 3

Odwołanie do pierwszego deskryptora LDT wymagany poziom uprzywilejowania 3

Poniżej zamieszczone zostały przykłady rozkazów ładujących rejestry selektorów:

1. mov ax, 8 ; modyfikacja rejestru DS
mov ds,ax
2. push 0Ch ; modyfikacja rejestru ES
pop es
3. jmp 0fh:100h ; modyfikacja rejestru CS

Rejestry selektorów

Na poniższym rysunku przedstawiona została budowa rejestru selektorów.

0x01 graphic

Rejestry selektorów są zbudowane z 2 części:

1. Dostępnej do modyfikacji przez programistę (część widoczna)

2. Części ukrytej, ładowanej z deskryptora w czasie ładowania selektora do rejestru selektora

Deskryptor

Opis struktury deskryptora

Jak już wcześniej zostało to napisane deskryptor to obiekt danych zlokalizowany w tablicy deskryptorów.

Na poniższym rysunku opisana została struktura deskryptora.

0x01 graphic

Pole: limit

Szerokość: 20 bitów

Opis:

Pole definiuje wielkość opisywanego za pomocą deskryptora segmentu. Procesor interpretuje pole w zależności o od wartości pamiętanej przez bit pola G. W przypadku, gdy pole G jest wyzerowane wówczas wielkość segmentu może dochodzić do maksymalnie 1MB. W przypadku przeciwnym segment może posiadać wielkość nawet do 4GB.

Pole: base

Szerokość: 32 bity

Opis:

Pole definiuje położenie segmentu w przestrzeni liniowej adrsowej (w przypadku, gdy segmentacja jest wyłączona przestrzeń liniowa jest równocześnie przestrzenią fizyczną).

Pole: type

Szerokość: 4 bity

Opis:

Pole definiuje typ segmentu, lub furtki. Intepretacja tego pola jest powiązana z polem S. Zakres możliwych wartości opisany został w rozdziale poświęconym typom segmentów i furtek.

Pole: S

Szerokość: 1 bit

Opis:

Definiuje typ deskryptora. W przypadku gdy pole przyjmuje wartość 0 wówczas deskryptor definiuje segment, lub furtę systemową. W przypadku przeciwnym definiuje segment kodu, lub danych.

Pole: DPL

Szerokość: 2 bitów

Opis:

Definiuje poziom uprzywilejowania segmentu, lub furtki. Dozwolony zakres wartości: 0 (najbardziej uprzywilejowany) - 3 (najmniej uprzywilejowany). Szerszy opis poziomów uprzywilejowania można znaleźć w rozdziale poświęconym ochronie w trybie chronionym.

Pole: P

Szerokość: 1 bit

Opis:

Określa czy segment definiowany przy pomocy deskryptora jest obecny w pamięci, czy też nie. W przypadku, gdy pole jest wyzerowane procesor generuje wyjątek: "segment nieobecny" (#NP). Wyjątek ten może zostać wykorzystany przez oprogramowanie zarządzające pamięcią do załadowania do pamięci fizycznej segmentu np. z dysku twardego. Na poniższym rysunku pokazana została struktura deskryptora w przypadku, gdy pole P jest wyzerowane.

0x01 graphic

Obszary oznaczone jako "Available" w takim przypadku mogą zostać wykorzystane do składowania niezbędnych danych dla oprogramowania managera pamięci, które ten może wykorzystywać do ładowania do pamięci fizycznej segmentu.

Pole: D/B

Szerokość: 1 bitów

Opis:

Pole jest wykorzystywane do różnych funkcji w zależności od definiowanego segmentu. Dla segmentu:

Pole: G

Szerokość: 1 bit

Opis:

Określa sposób interpretowania pola limit. W przypadku, gdy pole to jest wyzerowane wielkość segmentu jest interpretowana w jednoskach miary - bajtach. W przypadku, gdy jest ustawione wielkość jest intepretowana w 4KB jednostkach miary. Dodatkowo, gdy pole G jest ustawione 12 najmniej znaczących bitów offsetu nie jest sprawdzane w czasie realizacji testów przekroczenia wielkości segmentu. Pozwala to na definiowanie segmenów o wielkości 4KB (wartość 0 pola limit).

Typy deskryptorów

Wprowadzenie

W poprzednim rozdziale opisana została struktura deskryptora w tym pole S definiujące typ deskryptora. W niniejszym rozdziale wprowadzimy sobie klasyfikację deskryptorów i opiszemy każdy z poszczególnych typów, wskazując różnicę między nimi.

Podział deskryptorów

Pole S deskryptora wprowadzało podział na:

Deskryptory segmentów kodu i danych definiują obszary pamięci, wykorzystywane do przechowywania kodu, danych (w tym sotsów).

Do deskryptorów systemowych zaliczamy:

Obiekty, opisywane za pomocą tych deskryptorów, są przeznaczone do wykorzystania przez oprogramowanie systemowe np. w celu zarządzania procesami i przydzielania im zasobów systemowych.

Różnice w definicji poszczególnych deskryptorów

Na poniższym rysunku przedstawione zostały różnice w definicji poszczególnych typów deskryptorów.

0x01 graphic

Różnice między poszczególnymi typami deskryptorów:

1. Deskryptory systemowe nie posiadaja pól D/B i AVL

2. Pole type deskryptorów segmentów kodu i danych budowane jest w oparciu o następujące zasady:

Deskryptory segmentów danych i kodu

Typy deskryptorów segmentów kodu i danych

W poniższej tabeli zebrane zostały dozwolone wartości pola typ deskryptora w przypadku definicji deskryptora segmentu kodu, lub danych.

0x01 graphic

UWAGA
Powyższa tabela definiuje typy segmentów danych i kodu w inny sposób niż w książce "Mikroprocesory 80286, 80386 i i486". Różnice wynikają z różnego potraktowania bitu (pola) A w dostępnych materiałach.

Podsumowując, do dyspozycji programisty są:

Wybór uprawnień (odczyt, zapis, wykonanie) ma decydujący wpływ związany z wykonaniem się oprogramowania. Próba zapisu do segmentu danych, który został opisany za pomocą deskryptora segment danych tylko do odczytu, kończy się wyjątkiem. To samo jest związane z próbą odczytu danych z segmentu kodu, jeżeli segment taki nie został opisany za pomocą deskryptora z uprawnieniem do odczyt procesor będzie generował wyjątki.

Deskryptory systemowe

Typy deskryptorów segmentów systemowych

Na poniższym rysunku przedstawione zostały wszystkie możliwe wartości pola typ w zakresie deskryptorów systemowych.

0x01 graphic

Do deskryptorów systemowych zaliczamy:

Wszystkie powyżej wymienione deskryptory możemy podzielić na 2 grupy:

Deskryptory segmentów systemowych wskazują na obszary pamięci, w których zlokalizowane są segmenty stanów zadania, lub tablic LDT. Natomiast deskryptory furtek wskazują punkty wejścia do udostępnianych przez kod podprogramów (furtki wywołania, przerwania, potrzasku), lub wskazują na segmenty stanu zadadnia (furtka zadania).

Furtki

Furtki

Furtki są przezonaczone do organizowania kontrolowanego dostępu do segmentów kodu znajdujących się na bardziej uprzywilejowanych poziomach. Wyróżniamy następujące typy furtek:

1. Wywołania

2. Pułapki

3. Przerwania

4. Zadania

Furtki zadań są wykorzystywane do przełączania zadań. Furtki półapek, przerwań są wykorzystywane do obsługi zdarzeń sygnalizowanych przy pomocy przerwań.

Furtka wywołania

Furtka wywołania jest wykorzystywana do organizowania operacji zmiany sterowania miedzy segmentami kodu znajdującymi się na różnych poziomach uprzywilejowania. Na poniższym rysunku pokazany został format deskryptora furtki wywołania.

0x01 graphic

Pola danych deskryptora furtki:

1. Segment selektor - selektor wskazujący na deskryptor segmentu kodu

2. Offset - offset we wskazywanym segmencie kodu wejścia do uruchamianego furtką kodu

3. Param count - określa liczbę argumentów wywołania procedury uruchamianej furtką; parametr ten definiuje liczbę argumentów pamiętanych na stosie w czasie wywołania, która musi zostać skopiowana do nowego stosu w przypadku gdy następuje zmiana stosów w związku z zmianą poziomów uprzywilejowania; parametr definiuje liczbę słów (furtka 16 bitowa), lub podwójnych słów furtka 32 bitowa

4. DPL - poziom uprzywilejowania deskryptora furtki

Mechanizmy ochrony

Wprowadzenie

Tryb chroniony dostarcza mechanizmów ochrony, które są zarówno dostępne na płaszczyźnie segmentacji jaki i stronicowania. Umożliwiają one do budowy oprogramowania, w którym kluczowe jego moduły są chronione przed nieupoważnionym dostępem. Ochrona polega na limitowaniu dostępu do kluczowych dla funkcjonowania oprogramowania segmentów kodu, danych, stosu z oprogramowania nieuprzywilejowanego. Realizowane to jest przez zastosowanie tzw. poziomów uprzywilejowania (ochrony).

Zastosowanie mechanizmów ochrony powoduje realizacje przez procesor weryfikacji wszystkich odwołań do pamięci. Każda operacja powoduje realizację testów. Każdy błąd jest sygnalizowany w postaci wyjątku.

Testy realizowane przez procesor możemy podzielić na następujące grupy:

W niniejszym rozdziale opisane zostały wszystkie mechanizmy ochrony. Opis wyjątków został przedstawiony w rozdziale poświęconym przerwaniom.

Uruchomienie i wyłączenie mechanizmów ochrony

Sterowanie mechanizmami ochrony dla segmentacji

Mechanizmy ochrony są włączane w momencie uruchomienia trybu chronionego (ustawienie bitu PE rejestru CR0). Są wbudowane w tryb i zawsze działają. Nie ma dostępnego, żadnego rozkazu, lub flagi w rejestrach procesora, które mogą wyłączyć mechanizmy ochrony. Jedyną metodą na "wyłączenie" mechanizmów weryfikacji poziomów uprzywilejowania jest uruchomienie oprogramowania na najbardziej uprzywilejowanym poziomie ochrony (poziom 0). Nie mniej zawsze będą realizowane testy związane z przekroczeniem wielkości segmentów, czy weryfikacją typów.

Sterowanie mechanizmami ochrony dla stronicowania

Podobnie jak w przypadku segmentacji mechanizmy ochrony zostają automatycznie uruchomione w momencie startu stronicowania (ustawiony bit PG w rejestrze CR0). Podobnie jak w segmentacji nie ma bezpośredniej możliwości wyłączenia mechanizmów ochrony. Nie mniej jednak można to zrobić wykonując następujące operacje:

1.Wyzerowanie flagi WP w rejestrze CR0

2. Ustawienie flag R/W (zapis i odczyt) i U/S (użytkownik/ administrator) we wszystkich katalogach stron i stronach

Wykonanie tych operacji spowoduje, że każda strona będzie dostępna do zapisu z wyłączonym mechanizmem ochrony na poziomie stron.

Pola danych wykorzystywane przez mechanizmy ochrony

Pola danych struktur systemowych wykorzystywane przez mechanizmy ochrony

Pola danych, flagi wykorzystywane przez mechanizmy ochrony:

1. S - pole deskryptora określające typ deskryptora (deskryptor segmentu danych, kodu, czy deskryptor systemowy)

2. Type - definiuje typ segmentu opisywanego przez deskryptor

3. Limit - wielkość obiektu systemowego (wspólnie z polami G i E (deskryptor segmentu danych element pola type)

4. G - pole ziarnistości; wspólnie z polem limit określa wielkość segmentu

5. E - flaga (pole type deskryptora segmentu danych) definiująca czy segment danych jest rozszerzalny w dół; co za tym idzie określająca także wielkość segmentu

6. DPL - pole definiujące poziom uprzywilejowania deskryptora

7. RPL - pole selektora określające żądany poziom uprzywilejowania

8. CPL - pole selektora CS (pozycje bitowe 0 i 1); określa bieżący poziom ochrony (uprzywilejowania)

9. U/S - flaga katalogu stron, lub tablicy stron określająca typ strony (użytkownik,supervisor)

10. W/R - flaga strony określająca sposób dostępu do obszaru pamięci wskazywanego przez stronę (zapisy/odczyt, czy tylko odczyt)

Na poniższym rysunku pokazane zostało rozlokowanie opisanych powyżej pól w deskryptorach.

0x01 graphic

Weryfikacja pola limit

Weryfikacja pola limit

Pole limit deskryptora jest wykorzystywane do weryfikacji, czy realizowana operacja nie próbuje się odwołać poza dozwolony obszar segmentu. Efektywna wartość pola limit jest uzależniona od bitu G deskryptora (ziarnistość). W segmentach danych zależy także od pola E i B. W przypadku gdy G jest wyzerowane wówczas wartość efektywna pola jest równa wartości zapisywanej na 20 bitach (liczba z zakresu 0 - 0FFFFFh (1MB)). Kiedy G jest ustawione wówczas efektywna wartość pola jest liczbą z zakresu 0FFFh (4KB) do 0FFFFFFFFh (4GB). Wyliczana jest przez pomnożenie wartości zapisanej w deskryptorze przez wielkość strony ziarnistości (4KB - 0FFFh).

UWAGA
W przypadku, gdy pole G jest równe 1, wówczas 12 najmłodszych bitów offsetu (adresu) realizowanej operacji nie weryfikowane w operacji weryfikacji przekroczenia dozwolonej wielkości segmentu. Przykładowo w przypadku gdy pole limit przyjmuje wartość 0, wówczas dozwolone są wartości offsetu adresu z zakresu 0 - 0FFFH.

Weryfikacja pola limit dla segmentów z wyjątkiem segmentu rozszerzalnego w dół

Dla wszystkich typów segmentów z wyjątkiem segmentu rozszerzalnego w dół efektywna wartość pola limit jest ostatnim dozwolonym adresem dostępu do segmentu. Oznacza to że ostatni dozwolony offset w segmencie jest równy wartości efektywnej pola limit i jest o conajmniej 1 bajt mniejszy od wielkości segmentu. W przypadku przekroczenia wielkości segmentu generowany jest wyjątek #GP. Przez przekroczenie wielkości segmentu rozumiana jest realizacja operacji, w której adres nie spełnia jednego z poniżej wymienionych warunków:

Weryfikacja pola limit dla segmentu danych rozszerzalnego w dół

Dla segmentów rozszerzalnych w dół pole limit ma tą samą funkcje, tylko jest interpretowane w inny sposób. Wartość efektywna pola limit określa ostatni niedozwolony adres. Oznacza to że adresy dozwolone są z zakresu od efektywny limit + 1 do 0FFFFFh, lub 0FFFFFFFFh (w zależności od tego czy pole B jest ustawione czy nie). Segment rozszerzalny w dół ma maksymalną wielkość, gdy pole limit równe jest 0.

Weryfikacja pola limit rejestrów GDTR i IDTR

Rejestry GDTR i IDTR zawierają 16 bitowe pole limit. Pole to określa wielkość segmentów zawierających tabele GDT i IDT. Weryfikacja prawidłowości odczytu desktryptora odbywa się na takich samych zasadach jak w przypadku testów opisanych w rozdziale poświęconym "weryfikacji pola limit dla wszystkich segmentów z wyjątkiem segmentów rozszerzalnych w dół".

Weryfikacja typów segmentów i deskryptorów

Wprowadzenie

Deskryptory zawierają dwa pola definiujące typ:

Procesor wykorzystuje te informacje do wykrywania błędów, które są wynikiem prób wykorzystania segmentów, lub deskryptorów niezgodnie z ich przeznaczeniem. Pola są wykorzystywane w różnych miejscach w dalszej części rozdziału omawiane będą poszczególne przypadki. Nie mniej należy założyć że zamieszczona lista jest niepełna i zawiera tylko najistotniejsze przypadki operacji, w których testy pól typów są realizowane.

Ładowanie selektora do rejestru segmentowego

1. Rejestr CS może być tylko ładowany selektorem segmentu kodu

2. Selektory segmentów kodu, które nie mają zdefiniowanego uprawnienia do odczytu nie mogą być ładowane do rejestrów DS,ES,FS, GS

3. Rejestr SS może być ładowany selektorem wskazującym na deskryptor segmentu danych odczyt i zapis

Ładowanie selektora do rejestru LDTR

1. Rejestr LDTR może być ładowany selektorem wskazującym na deskryptor LDT.

2. Rejestr TR może być ładowany selektorem wskazującym na deskryptor segmentu TSS.

Dostęp do segmentu, którego selektor jest już załadowany do rejestru segmentowego

1. Żaden rozkaz nie może zapisywać do segmentu kodu

2. Żaden rozkaz nie może zapisywać do segmentu danych tylko do odczytu

3. Żaden rozkaz nie może czytać danych z segmentu kodu tylko wykonywanie

Wykonanie rozkazu, którego operand zawiera selektor

1. Skok daleki (CALL, lub JMP) może być realizowany do segmentu kodu zgodnego, segmentu kodu, furtki wywołania, furtki zadania, lub segmentu stanu zadania

2. Rozkaz LLDT może być realizowany przez wskazanie deskryptora LDT

3. Rozkaz LTR może być realizowany przez wskazanie deskryptora segmentu stanu zadania

4. Rozkaz LAR może być realizowany przez wskazanie deskryptora LDT, TSS, furtki wywołania, furtki zadania, segmentu kodu, segmentu danych

5. Rozkaz LSL może być realizowany przez wskazanie deskryptora LDT, TSS, segmentu kodu, segmentu danych

6. Pozycją tabeli IDT musi być: furtka przerwania, pułapki, zadania

Wykonanie wewnętrznych operacji

1. Wykonanie operacji skoku dalekiego (CALL, JMP) powoduje weryfikację przez procesor pola type. Jeżeli rozkaz wskazuje na deskryptor segmentu kodu, furtki wywołania realizowana jest operacja skosku do innego segmentu. Jeżeli rozkaz wskazuje na segment stanu zadania, lub furtkę zadania wykonywana jest operacja przełączenia zadań

2. W przypadkach skoków z wykorzystaniem furtek przerwania, wywołania, pułapki, skoków do podprogramów, procedur obsługi przerwań, wyjątków procesor weryfikuje czy skok odbywa się do segmentu kodu (deskryptor segmenty jest typu segment kodu)

3. W przypadkach skoków z wskazaniem na furtkę zadania, procesor weryfikuje czy wskazany w deskryptorze segment jest segmentem stanu zadania

4. W przypadku skoków do zadania, procesor weryfikuje czy selektor wskazuje na deskryptor segmentu TSS

5. W przypadku powrotu z zagnieżdżonego zadania (rozkaz IRET), procesor sprawdza, czy w polu poprzedniego zagnieżdżonego zadania w segmencie TSS znajduje się wskazanie na segment TSS

Weryfikacja selektora zerowego segmentu

Weryfikacja operacji na selektorze zerowego segmentu

    1. Próba załadowania do rejestru segmentowego CS, lub SS selektora wskazującego na zerowy deskryptor powoduje wygenerowanie wyjątku #GP.

    2. Selektor zerowego segmentu może być ładowany do rejestrów segmentowych: DS, ES, GS, FS. Próba odwołania się do segmentów przez załadowany selektor do rejestru segmentu spowoduje wygnerowanie wyjątku #GP.

Możliwość ładowania zerowego selektora do rejestrów segmentów można wykorzystać jako metodę wykrywania operacji na niewykorzystywanych rejestrach segmentowych, lub nieoczekiwanych próbach dostępu do segmentów danych.

Poziomy uprzywilejowania

Mechanizm ochrony segmentów

Mechanizm ochrony składa się z 4 poziomów uprzywilejowania numerowanych od 0 do 3. Poniższy rysunek pokazuje interpretację poziomów uprzywilejowania jako okręgów ochrony.

0x01 graphic

Centralny okrąg (zarazerwowany dla najbardziej uprzywilejowanych segmentów danych, kodu i stosu) jest wykorzystywany dla semgnetów najbardziej krytycznego oprogramowania (jądra systemu operacyjnego). Bardziej zewnętrzne okręgi są wykorzystywane przez mniej krytyczne dla funkcjonowania systemu komputerowego oprogramowanie. Systemy, które wykorzystują 2 poziomy uprzywilejowania powinny korzystać z poziomów 0 i 3 (np. Microsoft Windows NT). Generalnym zadaniem zastosowania poziomów ochrony jest ograniczenie dostępu oprogramowania osadzonego na niższych poziomach do zasobów zlokalizowanych na bardziej uprzywilejowanych poziomach. Rozwiązania te są wspomagane przez testy realizowane przez procesor w czasie wykonywania oprogramowania. Każde naruszenie mechanizmów ochrony powoduje wygenerowanie wyjątku #GP.

Poziomy uprzywilejowania są weryfikowane w czasie realizacji operacji ładowania rejestrów segmentowych selektorami.

Dane wykorzystywane do weryfikacji praw dostępu do segmentów

Bieżący poziom uprzywilejowania (CPL)

Bieżący poziom uprzywilejowania jest poziomem na jakim wykonuje się bieżący proces (program, lub zadanie). Jest pamiętany na 2 najmłodszych bitach selektora zapisanego w rejestrze CS. CPL zmienia się zawsze w związku z zmianą sterowania wyniającego z wykonania rozkazu skoku między segmentowego (z wyjątkiem skoku do segmentu zgodnego, wówczas CPL pozostaje na swoim poprzednim poziomie.

Poziom uprzywilejowania deskryptora (DPL)

DPL jest poziomem uprzywilejowania definiowanym w polu DPL deskryptora segmentu, lub furtki. W przypadku realizacji operacji dostępu do segmentu, lub furtki DPL zapisany w deskryptorze jest porównywany z CPL i RPL wykorzystywanego w operacji selektora. Interpretacja DPL jest uzależniona od użytego w operacji deskryptora segmentu lub furtki:

      1. Segment danych: