osdev.labedz.org

Procedury inicjujące tryb chroniony

Aby wykorzystać wszystkie możliwości trybu chronionego należy odpowiednio przygotować struktury systemowe oraz załadować rejestry specjalne. Przede wszystkim niezbędne są tablice deskryptorów GDT, IDT oraz segment stanu procesu TSS. Jeżeli zamierzamy korzystać z mechanizmu stronicowania należy stworzyć i odpowiednio wypełnić katalog stron oraz tablicę stron oraz ustawić rejestr CR3 i flagę PG.

Ustawienie tablic deskryptorów

  • ustawienie globalnej tablicy deskryptorów GDT (plik kernel.c, procedura setup_GDT)
  • W celu zainicjowania globalnej tablicy deskryptorów należy wywołać procedurę setup_GDT_entry(). Jej zadaniem jest stworzenie pod wskazanym adresem deskryptora o parametrach odpowiadających argumentom procedury.

    void setup_GDT_entry (SEG_DESCR *desc, dword base, dword limit, byte access, byte attribs);

    Tworzenie przykładowego deskryptora w tablicy GDT:

    /* 0x10 -- data segment descriptor */ setup_GDT_entry(&gdt[2], 0, 0xFFFFF, ACS_DATA, ATTR_GRANULARITY | ATTR_BIG);

    Tak utworzony deskryptor będzie miał pole adresu bazowego ustawione na początek obszaru adresowego, będzie miał on wielkość całego obszaru adresowego, będzie to deskryptor danych (do odczytu i zapisu ale nie wykonywania) o ziarnie 4KB i maksymalnym rozmiarze 4GB.

    Po ustawieniu wszystkich pól deskryptorów (przede wszystkim deskryptorów kodu, danych, stosu dla systemu operacyjnego, oraz kodu, danych i stosu dla aplikacji) i ustawienia deskryptora segmentu stanu zadania należy adres tak utworzonej tablicy załadować do rejestru GDTR procedurą load_gdtr. Kolejnym krokiem jest załadowanie selektorów do rejestrów segmentowych procesora i przeprowadzenie operacji ustawienia wartości selektora rejestru CS (procedura update_cs).

  • ustawienie tablicy deskryptorów przerwań IDT (plik kernel.c, procedura setup_IDT)
  • Procedura tworzenia tablicy deskryptorów przerwań przebiega analogicznie jak GDT - dla każdego obsługiwanego przerwania tworzymy wpis w tablicy deskryptorów za pomocą funkcji setup_IDT_entry.

    void setup_IDT_entry (INT_DESCR *desc, word selector, dword offset, byte access, byte param_cnt);

    przykładowe tworzenie deskryptora przerwania o identyfikatorze 0x11:

    setup_IDT_entry(&idt[0x11], 0x08, (dword) &isr_11, ACS_INT, 0);

    W tablicy deskryptorów przerwań umieszczamy także deskryptor bramki zadania, która będzie służyła jako bramka wywołań systemowych.

    Po stworzeniu całej tablicy deskryptorów należy jej adres załadować do rejestru IDTR procesora procedurą load_idtr.

Ustawienie segmentu stanu procesu TSS

Segment stanu procesu jest strukturą niezbędną jeżeli chcemy korzystać ze sprzętowego wsparcia mechanizmu przełączania zadań. Ponieważ jednak mechanizm ten w rodzinie procesorów IA-32 nie był praktycznie modyfikowany od czasu wprowadzenia procesora Intel386, jego wydajność nie jest najlepsza. Zdecydowanie prostszym i dużo szybszym sposobem na przełączenie zadań jest wykorzystanie przełączania 'na stosie'. W takim przypadku najczęściej jest potrzebny tylko jeden segment stanu procesu, zawierający adres stosu, katalog stron jądra systemowego, oraz adres stosu aplikacji. Podczas procesu inicjacji systemu należy wypełnić tylko pola odnoszące się do adresu stosu i katalogu stron jądra systemowego. Zadanie te wykonuje procesura setup_TSS w pliku kernel.c.

Inicjacja stronicowania

Przed użyciem mechanizmu stronicowania w procesorze Intel386 należy odpowiednio przygotować katalog stron oraz tablicę stron. Zadanie te wykonuje procedura init_paging w pliku kernel.c. Jej parametrami są wielkość pamięci konwencjonalnej oraz wielkość pamięci rozszerzonej pobrane ze struktury informacyjnej standardu Multiboot.

Na początku procedura init_paging wywołuje procedure init_page_dir w celu ustawienia katalogu stron. Parametrami tej procedury jest adres tablicy katalogu stron oraz atrybuty wpisów katalogu. Według tych parametrów zostaje ustawiona struktura katalogu stron, która odnosi się do obszaru pamięci o wielkości zdefiniowanej w stałej systemowej MAX_MEMORY.

Kolejnym zadaniem procedury init_paging jest ustawienie wpisów tablicy stron dla obszarów pamięci używanych przez system operacyjny procedurą map_pages. Argumentami tej procedury jest adres katalogu stron, adres wirtualny danej strony, adres fizyczny danej strony, wielkość ustawianego obszaru pamięci oraz atrybuty dostępu do danego obszaru. Obszarami ustawianymi przez procedurę są przede wszystkim obszary pamięci ekranu, BIOS'u, danych oraz kodu jądra systemu. Dzięki odpowiedniemu ustawianiu tych obszarów, zostają one zabezpieczone przed niepowołanym dostępem.

Po przygotowaniu odpowiednich struktur danych, należy wpisać adres katalogu stron do rejestru CR3 procesora (procedura write_cr3) i uruchomić mechanizm stronicowania ustawiając bit PG w rejestrze CR0 korzystając z zestawu procedur:

write_cr0(read_cr0() | 0x80000000); //set paging bit to '1' - enable paging

Użycie odpowiedniej maski powoduje, że zmianie podlega jedynie interesujący nas bit PG.