Newsy|Artykuły|Projekty|Dodaj newsa|Dodaj na bugtraq|Rejestracja|Archiwum|Forum
Własny bootloader w nasm

---------------------------------------------------------------------------------------------------
        WLASNY BOOTLOADER W NASM by t0m_k
---------------------------------------------------------------------------------------------------
   0x00 - zaopatrzenie
   0x01 - troche teorii
   0x02 - pamiec bios
   0x03 - hello world
   0x04 - tryb graficzny
   0x05 - stos
   0x06 - ladowanie kernela
   0x07 - A20
   0x08 - tryb chroniony
   0x09 - koniec, czyli gotowiec plus kernel ;)
---------------------------------------------------------------------------------------------------
        0x00 ZAOPATRZENIE
---------------------------------------------------------------------------------------------------
Na poczatku trzeba sie zaopatrzyc w jakis system Linux, Unix, badz Windows, w kilka narzedzi oraz
wypadaloby znac podstawy asemblera i orientowac sie w architekturze x86.
Programy, z ktorych bede korzystal:
- NASM  - http://sourceforge.net/projects/nasm/files/
- DD    - standardowo w Uniksach powinien byc, dla Windowsa: http://www.chrysocome.net/download
- QEMU  - http://www.qemu.org/download.html
- KQEMU - j.w.
- DJGPP - dla Windowsa, z MinGW moga wystepowac problemy przy linkowaniu calego kodu
- merge - maly program, tworzacy z naszych kodow obraz dyskietki, przydatny na Linuksie, kod
             programu znajduje sie w paczce na koncu tutorialu
---------------------------------------------------------------------------------------------------
        0x01 TROCHE TEORII
---------------------------------------------------------------------------------------------------
Komputer po uruchomieniu pracuje w trybie rzeczywistym procesora, w ktorym mamy dostep do okolo
1MB pamieci, pamiec jest podzielona na segmenty po 64kB oraz mamy dostep do przerwan, dzieki
systemowi bios, ktory tworzy tablice wektorow przerwan na najnizszych adresach pamieci.
Przerwan jest tylko 256. Bios oprocz tej tablicy umieszcza w pamieci kilka potrzenych dla siebie
danych. Nastepnie wykonywana jest sekwencja bootujaca.
Jesli ustawimy w biosie, ze od razu chcemy korzystac z dysku twardego, to bios sprawdzi, czy jest
taka mozliwosc. Tutaj wlasnie mozemy zaobserwowac jak wazna jest rola bootloadera do uruchomienia
systemu, poniewaz bez bootloader'a nie ma mowy o korzystaniu z systemu.
Bios nie uruchamia systemu ale ma za zadanie znalezc program bootujacy, czyli wlasnie bootloader,
ktory to uczyni.
Bootloader, aby byc rozpoznanym przez bios, musi :
        - znajdowac sie w pierwszym sektorze dysku, plyty, dyskietki, czy tez innego nosnika danych
        - byc zaladowany pod adres 0x7C00
        - zajmowac dokladnie jeden sektor dysku
        - konczyc sie dwoma bajtami o wartosciach 0x55 oraz 0xAA
---------------------------------------------------------------------------------------------------
        0x02 PAMIEC BIOS
---------------------------------------------------------------------------------------------------
0x00000000 - 0x000003FF        - tablica wektorow przerwan
0x00000400 - 0x000004FF        - BDA przestrzen danych biosu
0x00000500 - 0x00007BFF        - wolna, dostepna pamiec
0x00007C00 - 0x00007DFF        - miejsce na bootloader
0x00007E00 - 0x0007FFFF        - wolna, dostepna pamiec
0x00080000 - 0x0009FBFF        - wolna, dostepna pamiec, jesli istnieje
0x0009FC00 - 0x0009FFFF        - EBDA rozszerzona przestzen danych biosu
-----------------------        -------------------------------------------
0x0000A000 : 0x00000000        - pamiec video karty VGA
0x0000B000 : 0x00000000        - pamiec video karty Hercules Monochrome
0x0000B800 : 0x00000000        - pamiec trybu tekstowego karty VGA
---------------------------------------------------------------------------------------------------
        0x03 HELLO WORLD
---------------------------------------------------------------------------------------------------
Na poczatek stworzymy obraz dyskietki, poniewaz watpie zeby ktokolwiek mial jeszcze stacje
dyskietek ale zanim to zrobimy na Linuksach, musimy zaladowac modul kqemu:
      modprobe kqemu
Obraz tworzymy poleceniem:
      qemu-img create test.img 2880         ; lub 1440
Mozemy rownie dobrze podac inny rozmiar ale inaczej bedzie wygladalo bootowanie go przez emulator,
a nie jest to miejsce na opisywanie jak korzystac z qemu.                
Wystarczajace minimum zostalo omowione, wiec mozemy sie zabrac za pisanie, oto przykladowy kod:
///////////////////////////////////////////////////////////////////////////////////////////////////
                [ORG 0x7C00]                        ; ustawiamy CS na 0x7C00
                        xor        ax, ax                ; zerujemy AX
                        mov        ds, ax                ; do DS dajemy zero

                        mov        si, hello        ; kopiujemy hello do SI
                pisz:
                        lodsb                        ; pobieramy jeden znak z SI do AL
                        or        al, al                ; sprawdzamy czy AL == 0
                        jz        dalej                ; jesli tak to przestajemy pisac
                        mov        ah, 0x0E        ; do ah kopiujemy numer funkcji
                        int        0x10                ; wywolujemy przerwanie
                        jmp        pisz                ; wracamy do dalszego pisania
                dalej:
                        jmp        dalej                
;------------------------------------------------ ponizej dane
                hello        db        "Hello World!", 13, 10, 0        ; nasz napis
                times        510-($-$$)         db        0                ; wypelniamy pozostale bajty zerami
                db        0x55                                        ; nie             chyba
                db        0xAA                                        ;     trzeba           tlumaczyc
///////////////////////////////////////////////////////////////////////////////////////////////////
Kompilujemy kod poleceniem:
  -LINUX, WINDOWS:
      nasm -f bin boot.asm -o boot.bin
Zapisujemy w naszym obrazie:
  -LINUX, WINDOWS:
      dd if=boot.bin of=test.img count=1
Wreszcie testujemy:
  -LINUX, WINDOWS:
      qemu -fda test.img -boot a
---------------------------------------------------------------------------------------------------
        0x04 TRYB GRAFICZNY
---------------------------------------------------------------------------------------------------
Standardowo nasze kody pracuja w trybie tekstowym karty VGA, dopoki nie zmienimy trybu.
Ekran w tym trybie ma rozmiar 80x25, co oznacza 2000 znakow, czyli 4000 bajtow, poniewaz kazdy
znak sklada sie z dwoch bajtow, odpowiednio samego znaku oraz jego atrybutu.
Znak 1, czyli znak w lewym gornym rogu znajduje sie pod adresem 0xB800:0x0000, a jego atrybut
pod adresem 0xB800:0x0001. Oczywiscie przy bezposrednim odwolywaniu sie do pamieci ekranu
sami musimy zadbac o przejscie do nowej linii.
Przykladowy kod wypisujacy Hello World na kolorowo:
///////////////////////////////////////////////////////////////////////////////////////////////////
                 [ORG 0x7C00]
                        xor        ax, ax
                        mov        ds, ax

                        mov        ax, 0xB800        
                        mov        es, ax                ; ustawienie pamieci ekranu w segmencie ES

                        mov        di, 0x0100        ; skopiowanie do DI pozycji poczatkowej tekstu
                        mov        si, msg                ; do SI tekst
                        mov        cx, dlg                ; do CX dlugosc tekstu, czyli ilosc powtorzen rep
                        rep        movsb                ; skopiuj bajt z DS:SI do ES:DI

                        jmp        $                ; nieskonczona petla
;------------------------------------------------ ponizej dane
                msg     db      "H",0x01,"e",0x02,"l",0x23,"l",0x32,"o",0x5b," ",
                                    0x96,"W",0x62,"o",0x7d,"r",0x3e,"l",0x23,"d",0x67
                dlg     equ     $-msg
                times   510-($-$$)      db      0
                db      0x55
                db      0xAA
///////////////////////////////////////////////////////////////////////////////////////////////////
Kolory znakow sa okreslone liczbami od 0x00 do 0x0F, a kolory tla od 0x00 do 0x70.
Mozemy oczywiscie skorzystac z trybu 13h, badz innego:
///////////////////////////////////////////////////////////////////////////////////////////////////
                ; inicjalizacja trybu graficznego
                        xor        ah, ah
                        mov        al, 0x13
                        int        0x10
                ; powrot do trybu tekstowego
                        xor        ah, ah
                        mov        al, 0x3
                        int        0x10
///////////////////////////////////////////////////////////////////////////////////////////////////
---------------------------------------------------------------------------------------------------
        0x05 STOS
---------------------------------------------------------------------------------------------------
Jakby nie bylo przydal by nam sie stos oraz mozna by do wypisywania tekstow napisac funkcje:
///////////////////////////////////////////////////////////////////////////////////////////////////
                [ORG 0x7C00]
                        jmp        short        start        ; skaczemy na poczatek, pomijajac funkcje
                print:                                ; funkcja wypisujaca tekst
                  pisz:
                        lodsb                        ; pobieramy jeden znak z SI do AL
                        or        al, al
                        jz        dalej
                        mov        ah, 0x0E
                        int        0x10
                        jmp        pisz
                  dalej:
                        ret                        ; powrot do miejsca wywolania funkcji

                start:
                        xor        ax, ax
                        mov        ds, ax
                        mov        ss, ax                ; ustawiamy segment stosu
                        mov        sp, 0x9C00        ; ustawiamy wskaznik stosu
        
                        push         hello                ; odkladamy na stos napis
                        pop        si                ; zdejmujemy ze stosu napis i zapisujemy w SI
                        call        print                ; wywolanie naszej funkcji

                        mov        si, copy
                        call        print                ; znowu wywolanie naszej funkcji
                
                        jmp        $                ; petla nieskonczona
;------------------------------------------------ ponizej dane
                hello        db        "Bootloader", 13, 10, 0
                copy        db        "(c) 2010 by t0m_k", 13, 10, 0
                times        510-($-$$)        db        0
                db        0x55
                db        0xAA
///////////////////////////////////////////////////////////////////////////////////////////////////
W powyzszym przykladzie przydzielilismy na stos 0x9C00 bajtow oraz moglismy skorzystac
z instrukcji push, pop, jak rowniez wykonywac wszystkie operacje na stosie.
Nie jest nigdzie napisane, ani nie jest powiedziane ze wskaznik oraz wierzcholek stosu musza byc
wlasnie pod tymi adresami. Wazne, aby byly w dostepnej dla nas pamieci oraz wszystko zeby
bylo rozmieszczone ze zdrowym rozsadkiem.
---------------------------------------------------------------------------------------------------
        0x06 LADOWANIE KERNELA
---------------------------------------------------------------------------------------------------
Do zaladowania jadra systemu mozemy wykorzystac funkcje numer 0x02, przerwania 0x13 w nastepujacy
sposob:
///////////////////////////////////////////////////////////////////////////////////////////////////
                [ORG 0x7C00]
                        xor        ax, ax
                        mov        ds, ax

                        mov        ah, 0x02        ; czytaj sektory dyskietki
                        mov        al, 0x01        ; ilosc sektorow(kazdy 512 bajtow) do zaladowania
                        xor        ch, ch                ; czytaj z cylindra 0
                        mov        cl, 0x02        ; sektora 2 - w 1 jest bootloader
                        xor        dx, dx                ; glowicy 0, dysku 0
                        mov        bx, 0x0800        ; zaladuj kernel pod adres ES:BX
                        mov        es, bx                ; czyli
                        xor        bx, bx                ; 0x0800:0x0000
                        int        0x13

                        xor        ah, ah                ; pobierz znak z klawiatury do al
                        int        0x16
                        
                        cmp        al, 27                ; jesli znak == ESC
                        je        reset                ; restartuj komputer

                        jmp        0x0800:0x0000        ; skaczemy do kodu kernela

                reset:
                        mov        bx, 0x40
                        mov        ds, bx                
                        mov        word [ds:72h], 0x1234        ; wybieramy typ restartu
                        jmp        0xFFFF:0x0000                ; restartujemy
;------------------------------------------------ ponizej dane
                times        510-($-$$)        db        0
                db        0x55
                db        0xAA
///////////////////////////////////////////////////////////////////////////////////////////////////
Artykul dotyczy tylko programowania bootloaderow, wiec kernel musicie napisac sobie sami, ale
mysle ze nikt nie bedzie mial z tym problemow. Na tym etapie wystarczy jakis kod w asemblerze, aby
sprawdzic czy wszystko dziala jak nalezy.
Nalezy jedynie pamietac, aby kernel znalazl sie na dyskietce, badz jej obrazie w sektorze ktory
odczytalismy oraz jesli odczytujemy dokladnie tak jak pokazalem to wypadaloby go zaczac [ORG 0x0800]
ORG nie mozna stosowac przy przejsciu na kod jezyka C, badz tez innego poniewaz kompilacja sie nie
powiedzie.
Po skompilowaniu kodu bootloadera i bootsectora laczymy je w calosc:
-LINUX:
      cat boot.bin kernel.bin > os.bin
-WINDOWS:
        copy /b boot.bin+kernel.bin os.bin
Nastepnie nagrywamy i testujemy.
-LINUX:
        merge        os.bin obraz.img                ; moj program utworzy obraz dyskietki, wiec
                                                ; nie trzeba bedzie go nagrywac
; zalecam stosowanie programiku, ktory wrzucam, poniewaz dd, nie wiedziec czemu przy
; kopiowaniu na utworzony wczesniej obraz zmiejsza go do rozmiaru pliku os.bin i uniemozliwia
; bootloaderowi odpalenie kernela
-WINDOWS:
        dd if=os.bin of=obraz.img bs=512        ; lepiej dd wedlug mnie w tej kwesti sprawuje
                                                ; sie wlasnie pod windowsem :D nie ucina zer !
Dodam jeszcze, ze aby nie martwic sie, w ktorym sektorze co sie znajduje nalezy zaimplementowac
w kodzie bootloadera obsluge systemu plikow. Na poczatek polecam FAT12, poniewaz jest to chyba
najprostszy system plikow, dzieki ktoremu bedziecie mogli wczytywac pliki za pomoca ich nazw, a nie
polozenia na dyskietce. Warto takze zapoznac sie z pojeciami CHS i LBA, napewno sie przydadza ;)
---------------------------------------------------------------------------------------------------
        0x07 A20
---------------------------------------------------------------------------------------------------
A20 jest to bramka logiczna AND podlaczona do kontrolera klawiatury oraz polaczona z 20 linia
adresowa. Dlatego tez czesto sie mowi linia A20. W dawnych komputerach bylo tylko 20 linii, czyli
mozna bylo zaadresowac tylko 2^20 bitow pamieci. Z czasem szyna adresowa sie rozrosla o kolejne
linie, ale aby zachowac wsteczna kompatybilnosc wymyslono wlasnie bramke A20, ktora po wlaczeniu
komputera jest wylaczona, wiec aby skorzystac z pelnej mocy adresowej i dostepnej pamieci
musi zostac odblokowana. Tym zadaniem zajmuje sie bootloader albo system operacyjny.
Znane mi sa trzy metody odblokowania tej linii i wszystkie z nich przedstawie, lecz to Waszym
zadaniem bedzie wybor tej najbardziej Wam odpowiadajacej.
Pierwsza z nich polega na wykorzystaniu 0x0F przerwania biosu.
Niestety niewiem jak wyglada procent biosow, ktore zaimplementowaly ta mozliwosc. Oto i kod:
///////////////////////////////////////////////////////////////////////////////////////////////////
                [ORG 0x7C00]
                        jmp        start
                print:                                ; funkcja znana z poprzednich przykladow
                  pisz:
                        lodsb
                        or        al, al
                        jz        dalej
                        mov        ah, 0x0E
                        int        0x10
                        jmp        pisz
                  dalej:
                        ret
        
                check_a20:                        ; funkcja sprawdzajaca, czy bramka jest odblokowana
                        mov        ax, 0x962        ; zwraca wynik do AL,jesli jest odblokowana        
                        int        0xF                ; AL = 1, a jesli zablokowana AL = 0
                        ret
                enable_a20:                        ; funkcja odblokowujaca bramke A20
                        mov        ax, 0x961        ; jesli sie uda zeruje flage CF oraz AH = 0
                        int        0xF                ; jesli nie CF = 1, a w AH status bledu
                        ret
                disable_a20:                        ; funkcja blokujaca bramke A20
                        mov        ax, 0x960        ; ustawia rejestry i flagi tak samo jak funkcja 0x961
                        int        0xF
                        ret

                start:
                        xor        ax, ax
                        mov        ds, ax
                        mov        ss, ax
                        mov        sp, 0x9C00

                sprawdz_a20:
                        call        check_a20        ; wywolanie funkcji sprawdzajacej stan A20
                        or        al, al                ; czy AL = 0 ?
                        jnz        koniec                ; jesli tak to odblokuj
                                                ; jesli nie skacz na koniec
                wlacz_a20:
                        call        enable_a20        ; wywolanie funkcji odblokowujacej
                        jc        blad_a20        ; jesli wystapil blad poinformuj o nim

                        mov        si, odblok        ; jesli sie udalo wypisz komunikat
                        call        print        
                        jmp        koniec        
                blad_a20:
                        mov        si, blad        ; wypisz komunikat o bledzie
                        call        print
                koniec:
                        mov        si, end
                        call        print

                        jmp        $
;------------------------------------------------ ponizej dane
                odblok        db        "Pomyslnie odblokowano bramke A20.", 13, 10, 0
                blad                db        "Nie mozna odblokowac bramki A20!", 13, 10, 0
                end                db        "To juz koniec :)", 13, 10, 0
                times                510-($-$$)        db        0
                dw                0xAA55                        ; patrz co to LITTLE ENDIAN
///////////////////////////////////////////////////////////////////////////////////////////////////
Jest jeszcze funkcja 0x963 tego przerwania sprawdzajaca, z ktorego portu nalezy skorzystac, aby
odblokowac A20, jesli z portow 0x64 i 0x60 to BX = 0, a jesli z 0x92 to BX = 1.
Druga metoda polega na zapisywaniu oraz odczytywaniu odpowiednich wartosci z portow kontrolera
klawiatury. Ta metoda bedzie dzialac na 99,99% komputerow, wiec napewno jest warta uwagi.
Przedstawiam ponizej kod funkcji, ktorej jedynym zadaniem jest odblokowanie linii A20:
///////////////////////////////////////////////////////////////////////////////////////////////////
                enable_a20:
                        cli

                        call    a20wait
                        mov     al, 0xAD        ; wysylamy sygnal do portu kotrolera klawiatury
                        out     0x64, al        ; ktory wylaczy klawiature

                        call    a20wait                        
                        mov     al, 0xD0        ; ustawiamy czytanie z portu
                        out     0x64, al        ; 0x60

                        call    a20wait2                        
                        in      al, 0x60        ; pobieramy wartosc z portu 0x60
                        push    eax                ; oraz umieszczamy na stosie

                        call    a20wait
                        mov     al, 0xD1        ; ustawiamy zapisywanie do portu
                        out     0x64, al        ; 0x60

                        call    a20wait
                        pop     eax                ; zdejmujemy ze stosu pobrana wczesniej wartosc
                        or      al, 2                ; do eax oraz wykonujemy roznice logiczna na
                        out     0x60, al        ; najmlodszym slowie eax i zapisyjemy do portu 0x60

                      call    a20wait
                        mov     al, 0xAE        ; wysylamy sygnal
                        out     0x64, al        ; wlaczajacy klawiature

                        call    a20wait
                        ret

                a20wait:                        ; w tej funkcji szukamy utawionego
                  .zero:                        ; bitu blokady bramki A20
                        mov     ecx, 65536                
                  .jeden:
                        in      al, 0x64
                        test    al, 2
                        jz      .dwa
                        loop    .jeden
                        jmp     .zero
                  .dwa:
                        ret

                a20wait2:                        ; w tej funkcji robimy to samo co w poprzedniej
                  .zero:                        ; zaleznie od naszego sprzetu bedzie
                        mov     ecx, 65536        ; ustawiony na 1 lub na 2
                  .jeden:        
                        in      al, 0x64
                        test    al, 1
                        jnz     .dwa
                      loop            .jeden
                        jmp     .zero
                  .dwa:
                        ret
///////////////////////////////////////////////////////////////////////////////////////////////////
Zostala trzecia metoda, mysle ze tez dostepna na wiekszosci komputerow oraz napewno szybsza od
swoich poprzedniczek, poniewaz zostala wymyslona po to aby przyspieszyc wlaczenie tej bramki oraz,
aby ominac kontroler klawiatury. Polega na wykorzystaniu Systemowego Kontrolera Portu A.
///////////////////////////////////////////////////////////////////////////////////////////////////
                        in        al, 0x92        ; odczytujemy wartosc z portu 0x92 do al
                        or        al, 0x02        ; zerujemy bit blokady A20
                        out        0x92, al        ; zapisujemy zmiany
///////////////////////////////////////////////////////////////////////////////////////////////////
Mam nadzieje, ze wszystko do tej pory, bylo zrozumiale, poniewaz przejdziemy teraz do troszke
trudniejszych rzeczy, ktorymi nie koniecznie zajmuja sie bootloadery, ale nic nie stoi na
przeszkodzie zeby sie nimi zajmowaly.
---------------------------------------------------------------------------------------------------
        0x08 Tryb chroniony
---------------------------------------------------------------------------------------------------
W trybie chronionym jak zapewne wiecie rejestry segmentowe nie zawieraja adresow bezposrednich
segmentu, tylko zawieraja indeks deskryptora segmentu w tablicy GDT (globalna tablica deskryptorow)
lub LDT (lokalna tablica deskryptorow), czyli rejestry segmentowe sa selektorami segmentow.
Selektory wskazuja bezposrednio na deskryptor segmentu, a posrednio na segment.
Nasuwa sie oczywisty wniosek, ze aby przejsc do trybu chronionego procesora nalezy najpierw
stworzyc tablice deskryptorow. Stworzymy tylko globalna tablice, poniewaz lokalna jest glownie
wykorzystywana w metodach ochrony pamieci, a narazie nie ma co utrudniac.
Deskryptor segmentu sklada sie z 8 bajtow, to jest 32 bitow, a wyglada tak:
///////////////////////////////////////////////////////////////////////////////////////////////////
        --------------------------------------------------------------------------------------
        |  BASE 32:24  | G |D/B| L |AVL|SEG-LIMIT 16:19| P |DPL| S |   TYPE   |  BASE 23:16  |
        --------------------------------------------------------------------------------------
     31                                                 16:15                                        0
        --------------------------------------------------------------------------------------
        |          BASE ADDRESS 15:00              |         SEGMENT LIMIT 15:00             |
        --------------------------------------------------------------------------------------
G (granularity) - umozliwia opis segmentu do 4GB
        - G = 0 - max 1MB
        - G = 1 - max 4GB
D (default) - bit dlugosci slowa, wartosci dla segmentu kodu
        - D = 0 - 16 bitowe przemieszczenia
        - D = 1 - 32 bitowe przemieszczenia
L (large) - dla trybu 64 bitowego, wiec dla nas bez znaczenia narazie
AVL (available to software) - nie wykorzystywane przez procesor, bez znaczenia dla nas
P (present) - obecnosc segmentu
        - P = 0 - nie ma go
        - P = 1 - jest :D
DPL (descriptor privilage level) - poziom ochrony, tzw ring
S - rodzaj segmentu
        - S = 1 - segment pamieci, kodu lub danych
        - S = 0 - segmenty specjalne, furtki, LDT, itp
TYPE (11:8) - prawa do segmentu
        - dla danych (zwykly/rozszerzalny w dol):
                0000/0100 - odczyt
                0001/0101 - odczyt, dostep
                0010/0110 - odczyt, zapis
                0011/0111 - odczyt, zapis, dostep
        - dla kodu (zwykly/zgodny):
                1000/1100 - wykonanie
                1001/1101 - wykonanie i dostep
                1010/1110 - wykonanie i odczyt
                1011/1111 - wykonanie, odczyt, dostep
///////////////////////////////////////////////////////////////////////////////////////////////////
W asemblerze tablica GDT, bedzie wiec wygladac nastepujaco:
///////////////////////////////////////////////////////////////////////////////////////////////////
                gdtr:                                        ; opis tablicy potrzebny do jej zaladowania
                        dw        gdt - gdtend - 1        ; rozmiar
                        dd        gdt                        ; adres
                gdt:
                        dd        0x00000000, 0x00000000        ; zerowy deskryptor
                        dd        0x0000FFFF, 0x00CF9A00  ; deskryptor kodu, DPL - 0, limit - 4GB, baza - 0
                        dd        0x0000FFFF, 0x00CF9200  ; deskryptor danych, DPL - 0, limit - 4GB, baza - 0
                        dd        0x0000FFFF, 0x00CFFA00  ; deskryptor kodu, DPL - 3, limit - 4GB, baza - 0
                        dd        0x0000FFFF, 0x00CFF200  ; deskryptor danych, DPL - 3, limit - 4GB, baza - 0
                gdtend:
///////////////////////////////////////////////////////////////////////////////////////////////////
Teraz juz mozemy jej uzyc w naszym kodzie i przejsc do trybu chronionego, wiec do dziela:
///////////////////////////////////////////////////////////////////////////////////////////////////
                        cli                                ; obowiazkowo wylaczamy przerwania
                        lgdt [gdtr]                        ; ladujemy wczesniej przygotowana tablice do GDT
                        mov        eax, cr0                ; kopiujemy rejestr CR0 do EAX
                        or        eax, 1                        ; zapalamy bit trybu chronionego
                        mov        cr0, eax                ; zapisujemy zmiany w CR0
                                                        ; po tych czynnosciach musimy od razu wykonac
                        jmp        0x08:pmode                ; daleki skok, aby wyczyscic chache procesora

                [BITS 32]                                ; sygnalizujemy, ze bedziemy korzystac z 32bit mode
                pmode:                
                        mov        ax, 0x10                ; ustawiamy rejestry na segment danych
                        mov        ds, ax
                        mov        es, ax
                        mov        ss, ax
                        xor        ax, ax
                        mov        fs, ax                        ; FS i GS najbezpieczniej ustawic na 0
                        mov        gs, ax
                        mov        esp, 0x200000

                        jmp        $                        ; to by bylo na tyle, mozemy korzystac z PMODE :)
///////////////////////////////////////////////////////////////////////////////////////////////////
---------------------------------------------------------------------------------------------------
        0x09 KONIEC
---------------------------------------------------------------------------------------------------
Dziekuje za uwage i mam nadzieje, ze przyda sie komus ten tutorial ;)
Na koniec jeszcze paczka z programem merge kodem bootloadera i malutkim kernelem.
Przed kompilacja nalezy przeczytac plik README.

materiały do artykułu: klik


Autor:t0m_k
Czytań: 910
Data publikacji: 2010-05-08 02:04:08


Copyrights 2002 - 2009 © CC-Team.org design by fre3ke. Hosted by Net1.pl