BOOT TUT



#Start Contents

Pisanie boot-sektorow

Gdy juz choc srednio znacie asemblera, to po pewnym czasie pojawiaja
sie pytania (moga one byc spowodowane tym, co uslyszeliscie lub Wasza
wlasna ciekawoscia):
1. Co sie dzieje, gdy ma zostac uruchomiony jest system operacyjny?
2. Skad BIOS ma wiedziec, ktora czesc systemu uruchomic?
3. Jak BIOS odroznia systemy operacyjne, aby moc je uruchomic?

Odpowiedz na pytanie 2 brzmi: nie wie. Odpowiedz na pytanie 3 brzmi:
wcale. Wszystkie Wasze watpliwosci rozwieje odpowiedz na pytanie 1.

Gdy zakonczyl sie POST (Power-On Self Test), wykrywanie dyskow i
innych urzadzen, BIOS przystepuje do czytania pierwszych sektorow
tych urzadzen, na ktorych ma byc szukany system operacyjny (u mnie
jest ustawiona kolejnosc: CD-ROM, stacja dyskietek, dysk twardy).
Gdy znajdzie sektor odpowiednio zaznaczony: bajt nr 510 = 55h i bajt
511 = AAh (pamietajmy, ze 1 sektor ma 512 bajtow, a liczymy od zera),
to wczytuje go pod adres bezwzgledny 07C00h i uruchamia kod w nim
zawarty (po prostu wykonuje skok pod ten adres). Nie nalezy jednak
polegac na tym, ze segment kodu CS = 0, a adres instrukcji IP=7C00h
(choc najczesciej tak jest).
To wlasnie boot-sektor jest odpowiedzialny za ladowanie odpowiednich
czesci wlasciwego systemu operacyjnego. Na komputerach z wieloma
systemami operacyjnymi sprawa tez nie jest tak bardzo skomplikowana.
Pierwszy sektor dysku twardego, zwany Master Boot Record (MBR),
zawiera program ladujacy (Boot Manager, jak LILO czy GRUB), ktory z
kolei uruchamia boot-sektor wybranego systemu operacyjnego.

My oczywiscie nie bedziemy operowac na dyskach twardych, gdyz byloby
to niebezpieczne. Z dyskietkami zas mozna eksperymentowac do woli...
A instrukcja jest prosta: umieszczamy nasz programik w pierwszym
sektorze dyskietki, zaznaczamy go odpowiednimi ostatnimi bajtami i
tyle. No wlasnie... niby proste, ale jak o tym pomyslec to ani to
pierwsze, ani to drugie nie jest sprawa banalna.
Do zapisania naszego bootsektorka na dyskietke mozemy oczywiscie uzyc
"gotowcow" - programow typu "rawwrite" itp. Ma to pewne zalety -
program byl juz uzywany przez duza liczbe osob, jest sprawdzony i
dziala.
Ale cos by bylo nie tak, gdybym w kursie programowania w asemblerze
kazal Wam uzywac cudzych programow. Do napisania swojego wlasnego
programu zapisujacego dany plik w pierwszym sektorze dyskietki w
zupelnosci wystarczy Wam wiedza uzyskana po przeczytaniu czesci
mojego kursu poswieconej operacjom na plikach wraz z ta krotka
informacja ze Spisu Przerwan Ralfa Brown'a:
(przeskocz opis int 13h, ah=3)
INT 13 - DISK - WRITE DISK SECTOR(S)
AH = 03h
AL = number of sectors to write (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer
Return: CF set on error
CF clear if successful

Jak widac, sprawa juz staje sie prosta. Oczywiscie, AL=1 (bo
zapisujemy 1 sektor), DX=0 (bo stacja ma 2 glowice, a pierwsza ma
numer 0, zas numer dysku 0 wskazuje stacje A:), CX=1 (bo numery
sektorow zaczynaja sie od 1, a zapisujemy w pierwszym cylindrze,
ktory ma numer 0).
Schemat dzialania jest taki:
* Otworz plik zawierajacy skompilowany bootsektor
* Przeczytaj z niego 512 bajtow (do zadeklarowanej tablicy w
pamieci)
* Zamknij plik
* Zapisz odczytane dane na dyskietce, korzystajac z int 13h

Sprawa jest tak prosta, ze tym razem nie podam "gotowca". Gdy juz
mamy program zapisujacy bootsektor na dyskietke, trzeba sie postarac
o to, aby nasz programik (ktory ma stac sie tym bootsektorem) mial
dokladnie 512 bajtow i aby 2 ostatnie jego bajty to 55h, AAh.
Oczywiscie, nie bedziemy recznie dokladac tylu bajtow, ile trzeba,
aby dopelnic nasz program do tych 512. Zrobi to za nas kompilator.
Wystarczy po calym kodzie i wszystkich danych, na szarym koncu,
umiescic takie cos (NASM/FASM):
(przeskocz tworzenie sygnatury)
times 510 - ($ - start) db 0
dw 0aa55h

Dla TASMa powinno to wygladac mniej-wiecej tak:
db 510 - ($ - offset start) dup (0)
dw 0aa55h
end start

To wyrazenie mowi tyle: od biezacej pozycji w kodzie odejmij pozycje
poczatku kodu (tym samym obliczajac dlugosc calego kodu), otrzymana
liczbe odejmij od 510 - i doloz tyle wlasnie bajtow zerowych. Gdy juz
mamy program dlugosci 510 bajtow, to dokladamy jeszcze znacznik i
wszystko jest dobrze.

Jest jednak jeszcze jedna sprawa, o ktorej nie wspomnialem -
ustawienie DS i wartosci "org" dla naszego kodu. Otoz, jesli
stwierdzimy, ze nasz kod powinien zaczynac sie od offsetu 0 w naszym
segmencie, to ustawmy sobie "org 0" i DS=07C0h (tak, ilosc zer sie
zgadza), ale mozemy tez miec "org 7C00h" i DS=0. Zadne z tych nie
wplywa w zaden sposob na dlugosc otrzymanego programu, a nalezy o to
zadbac, gdyz nie mamy gwarancji, ze DS bedzie pokazywal na nasze dane
po uruchomieniu bootsektora.
Teraz, uzbrojeni w niezbedna wiedze, zasiadamy do pisania kodu
naszego bootsektora. Nie musi to byc cos wielkiego - tutaj pokaze
cos, co w lewym gornym rogu ekranu pokaze cyfre jeden (o
bezposredniej manipulacji ekranem mozecie przeczytac w moim innym
artykule) i po nacisnieciu dowolnego klawisza zresetuje komputer (na
jeden ze sposobow podanych w jeszcze innym artykule...).
Oto nasz kod (NASM):
(przeskocz przykladowy bootsektor)
; nasm -o boot.bin -f bin boot.asm

org 7c00h ; lub "org 0"

start:
mov ax, 0b800h
mov es, ax ; ES = segment pamieci ekranu

mov byte [es:0], "1" ; piszemy "1"

xor ah, ah
int 16h ; czekamy na klawisz

mov bx, 40h
mov ds, bx
mov word [ds:72h], 1234h ; 40h:72h = 1234h -
; wybieramy goracy reset

jmp 0ffffh:0000h ; reset

times 510 - ($ - start) db 0 ; dopelnienie do 510 bajtow
dw 0aa55h ; znacznik

Nie bylo to dlugie ani trudne, prawda? Rzecz jasna, nie mozna w
bootsektorach uzywac zadnych przerwan systemowych, np. DOS-owego int
21h, bo zaden system po prostu nie jest uruchomiony i zaladowany. Tak
napisany programik kompilujemy do formatu binarnego. W TASM-ie
kompilacja wygladalaby jakos tak (po dodaniu w programie dyrektyw
".model tiny", ".code", ".8086" i "end start"):
tasm bootsec1.asm
tlink bootsec1.obj,bootsec1.bin /t

Po kompilacji umieszczamy go na dyskietce przy uzyciu programu
napisanego juz przez nas wczesniej. Resetujemy komputer (i upewniamy
sie, ze BIOS sprobuje uruchomic system z dyskietki), wkladamy
dyskietke i.... cieszymy sie swoim dzielem (co prawda ta jedynka
bedzie malo widoczna, ale rzeczywiscie znajduje sie na ekranie).
Zauwazcie tez, ze ani DOS ani Windows nie rozpoznaje juz naszej
dyskietki, mimo iz przedtem byla sformatowana. Dzieje sie tak
dlatego, ze w bootsektorze umieszczane sa informacje o dysku.
Bootsektor typu FAT12 (DOSowy/Windowsowy) powinien sie zaczynac
mniej-wiecej tak:
(przeskocz systemowy obszar bootsektora)
org 7c00h ; lub org 0, oczywiscie

start:
jmp short kod
nop

db " " ; nazwa OS i wersja OEM (8B)
dw 512 ; bajtow/sektor (2B)
db 1 ; sektory/jednostke alokacji (1B)
dw 1 ; zarezerwowane sektory (2B)
db 2 ; liczba tablic alokacji (1B)
dw 224 ; liczba pozycji w katalogu glownym (2B)
; 224 to typowa wartosc
dw 2880 ; liczba sektorow (2B)
db 0f0h ; Media Descriptor Byte (1B)
dw 9 ; sektory/FAT (2B)
dw 18 ; sektory/sciezke (2B)
dw 2 ; liczba glowic (2B)
dd 0 ; liczba ukrytych sektorow (4B)
dd 0 ; liczba sektorow (czesc 2),
; jesli wczesniej bylo 0 (4B)
db 0 ; numer dysku (1B)
db 0 ; zarezerwowane (1B)
db 0 ; rozszerzona sygnatura bloku ladujacego
dd 0bbbbddddh ; numer seryjny dysku (4B)
db " "; etykieta (11B)
db "FAT 12 " ; typ FAT (8B), zwykle "FAT 12 "

kod:
; tutaj dopiero kod bootsektora

Ta porcja danych oczywiscie uszczupla ilosc kodu, ktora mozna
umiescic w bootsektorze. Nie jest to jednak duzy problem, gdyz i tak
jedyna rola wiekszosci bootsektorow jest uruchomienie innych
programow (second stage bootloaders), ktore dopiero zajmuja sie
ladowaniem wlasciwego systemu.
Jeszcze ciekawostka: co wypisuje BIOS, gdy dysk jest niewlasciwy (bez
systemu)?
Otoz - nic! BIOS bardzo chetnie przeszedlby do kolejnego urzadzenia.
Dlaczego wiec tego nie robi i skad ten napis o niewlasciwym dysku
systemowym??
Odpowiedz jest prosta - sformatowana dyskietka posiada bootsektor!
Dla BIOSu jest wszystko OK, uruchamia wiec ten bootsektor. Dopiero
ten wypisuje informacje o niewlasciwym dysku, czeka na nacisniecie
klawisza, po czym uruchamia int 19h. O tym, co robi przerwanie 19h
mozecie przeczytac w artykule o resetowaniu.
Milego bootowania systemu!
P.S. Jesli nie chcecie przy najdrobniejszej zmianie kodu resetowac
komputera, mozecie poszukac w Internecie programow, ktore symuluja
procesor (w tym faze ladowania systemu). Jednym z takich programow
jest Bochs.
________________________________________________________________

Co dalej?

O ile bootsektor jest ograniczony do 512 bajtow, to moze w dosc latwy
sposob posluzyc do wczytania do pamieci o wiele wiekszych programow.
Wystarczy uzyc funkcji czytania sektorow:
(przeskocz opis int 13h, ah=2)
INT 13 - DISK - READ SECTOR(S) INTO MEMORY
AH = 02h
AL = number of sectors to read (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer
Return: CF set on error
CF clear if successful

Jak widac, poza wartoscia rejestru AH, jej parametry nie roznia sie
od parametrow funkcji zapisu sektorow.
Wystarczy wiec wybrac nieuzywany segment pamieci, np. ES=8000h i
poczawszy od offsetu BX=0, czytac sektory zawierajace nasz kod,
zwiekszajac BX o 512 za kazdym razem. Kod do zaladowania nie musi byc
oczywiscie w postaci pliku na dyskietce, to by tylko utrudnilo prace
(gdyz trzeba wtedy czytac tablice plikow FAT). Najlatwiej zaladowac
kod tym samym sposobem, co bootsektor, ale oczywiscie do innych
sektorow. Polecam zaczac od sektora dziesiatego lub wyzej, gdyz
zapisanie tam danych nie zamaze tablicy FAT i przy probie odczytu
zawartosci dyskietki przez system nie pojawia sie zadne dziwne
obiekty.
Po zaladowaniu calego potrzebnego kodu do pamieci przez bootsektor,
wystarczy wykonac skok:
jmp 8000h:0000h

Wtedy kontrole przejmuje kod wczytany z dyskietki.
Ale jest jeden kruczek - trzeba wiedziec, jakie numery cylindra,
glowicy i sektora podac do funkcji czytajace sektory, zeby
rzeczywiscie odczytala te wlasciwe.
Struktura standardowej dyskietki jest nastepujaca: 512 bajtow na
sektor, 18 sektorow na sciezke, 2 sciezki na cylinder (bo sa dwie
strony dyskietki, co daje 36 sektorow na cylinder), 80 cylindrow na
glowice. Razem 2880 sektorow po 512 bajtow, czyli 1.474.560 bajtow.
Majac numer sektora (bo wiemy, pod jakimi sektorami zapisalismy swoj
kod na dyskietce), odejmujemy od niego 1 (tak by zawsze wszystkie
numery sektorow zaczynaly sie od zera), po czym dzielimy go przez 36.
Uzyskany iloraz to numer cylindra (rejestr CH), reszta zas oznacza
numer sektora w tymze cylindrze (rejestr CL). Jesli ta reszta jest
wieksza badz rowna 18, nalezy wybrac glowice numer 1 (rejestr DH),
zas od numeru sektora (rejestr CL) odjac 18. W przeciwnym przypadku
nalezy wybrac glowice numer 0 i nie robic nic z numerem sektora.
W ten sposob otrzymujemy wszystkie niezbedne dane i mozemy bez
przeszkod w petli czytac kolejne sektory zawierajace nasz kod.
Cala te procedure ilustruje ten przykladowy kod:
(przeskocz procedure czytania sektorow)
secrd:
;wejscie: ax=sektor, es:bx -> dane

dec ax ; z numerow 1-36 na 0-35
mov cl,36 ; liczba sektorow na cylinder = 36
xor dx,dx ; zakladamy na poczatek: glowica 0, dysk 0 (a:)
div cl ; AX (numer sektora) dzielimy przez 36
mov ch,al ; AL=cylinder, AH=przesuniecie wzgledem
; poczatku cylindra, czyli sektor
cmp ah,18 ; czy numer sektora mniejszy od 18?
jb .sec_ok ; jesli tak, to nie robimy nic
sub ah,18 ; jesli nie, to odejmujemy 18
inc dh ; i zmieniamy glowice
.sec_ok:
mov cl, ah ; CL = numer sektora
mov ax,0201h ; odczytaj 1 sektor
inc cl ; zwieksz z powrotem z zakresu 0-17 do 1-18

push dx ; niektore biosy niszcza DX, nie ustawiaja
; flagi CF, lub zeruja flage IF
stc
int 13h ; wykonaj czytanie
sti
pop dx

Spis tresci off-line (Alt+1)
Spis tresci on-line (Alt+2)
Ulatwienia dla niepelnosprawnych (Alt+0)


Wyszukiwarka

Podobne podstrony:
DOS BOOT TUT
BOOT TUT
BOOT TUT
Hirens Boot
CDRLab pl Hiren s Boot USB
ART121 tut 2
phys tut 08
phys tut 12
remote boot 3 atgrgfg4ak5imci6eqdc4jtbdubmqdnh3wdeb5q atgrgfg4ak5imci6eqdc4jtbdubmqdnh3wdeb5q
DOS DIOD TUT
advanced ntfs boot and mft repair
moto boot menu
SYS TUT
ART121 tut 3
MYSZ TUT
PWR TUT
BOOT

więcej podobnych podstron