Hogyan írjunk saját BootManagert - 3. rész

 

Ahogy az előző részben ígértem, most a bootmanager konkrét megvalósításával fogunk foglalkozni. Egy olyan példa programot fogok bemutatni, ami 448 byte-nál kisebb, így elfér az MBR-ben, és nem kell egynél több szektort használni hozzá.

 

Foglaljuk össze mégegyszer mit is kell csinálnunk.

 

A teljes MBR-t a 0000:7C00 címre olvassa be a BIOS.

 

1.)

Ezt át kell másolnunk máshova, majd oda kell ugranunk.

 

2.)

Meg kell jeleníteni egy menüt, amiből a felhasználó kiválasztja a kívánt operációs rendszert. (ami most a lehető legegyszerűbb lesz.)

 

3.)

A kiválasztott operációsrendszerhez tartozó partícióról beolvassuk a bootszektort a 00:7c00 címre, majd odaugrunk.

 

Kezdjük az első lépéssel. Erre találunk egy kész rutint, ha visszafejtük az eredeti MBR-t.

 

00000000:   xor           ax,ax

00000002:   mov         ss,ax           ;veremszegmens nullázása

00000004:   mov         sp,07C00   ;veremmutató = 7c00

00000007:   sti                               ; megszakítások engedélyezése

00000008:   push        ax       

00000009:   pop         es                ;extraszegmens nullázása

0000000A:  push        ax

0000000B:  pop         ds                ;adatszegmens nullázása

0000000C:  cld                             ;directionflag törlése a movsb-hez

0000000D:  mov         si,07C1B   ; forrásindex

00000010:   mov         di,0061B   ; célindex

00000013:   push        ax              ;verembe 0 és 0061B

00000014:   push        di             

00000015:   mov         cx,001E5  ; ciklusszámláló

00000018:   repe         movsb      ; másolás forrás => cél, cx-szer.

0000001A:  retf                           ; ugrás a veremben megadott címre

0000001B: a másoló rutin után következő kód [...]

 

Ez a rutin másolja az 00:7C1B címről a 00:0061B címre a másoló rutin után lévő bootloadert vagy bootmanagert.

Most foglalkozzunk a kiíratásokkal és a “menüvel”. Fontos, hogy semmilyen DOS szolgáltatást, vagyis int 021H-t nem használhatunk ugyanis még semmilyen oprendszer nincs betöltve. A kiíratáshoz is csak a BIOS szolgáltatásokat használhatjuk. (INT 010H).

 

Csináljunk először is egy képernyő törlést. A videomemória szegmenscíme szöveges módban B800H. A képernyőről feltételezzük, hogy 80x25 karakteres módban van. Ekkor 80*25 = 2000, de mivel egy karakterhez 2 byte tartozik, az attribútum byte miatt, így 4000 byte-ot kell teleírnunk egy szóközzel és mindegyikhez kell egy fekete attribútum byte.

 

    mov    ax,0b800H    ;videomem szegmens es-ben

    mov    es,ax

    xor      di,di              ; célindex = 0

   mov    ax,0020H     ; 20H a szóköz 00 az attribútum

    mov    cx,2000        ; mivel szavakat írunk, így csak 2000-szer fut le a ciklus

    rep      stosw            ; ciklus amíg a cx nem 0.

 

Ezzel a képernyőt letöröltük. Most megjelenítjük rajta a szöveget. Ehhez a 10H megszakítás 13H funkcióját fogjuk használni.

AH = 13, AL=0, BL= attribútum, BH=képernyőoldal, DX=kurzor kezdőhelyzete, CX=karakterek száma, ES:BP = a string címe.

 

A program elején definiáljuk a következő változókat. (ez csak egy példa természetesen, mindenkinek más és más)

 

FIRST   db '[1] Mandrake'

LENGTH_FIRST equ $-FIRST

 

SECOND  db '[2] WindowsXP'

LENGTH_SECOND equ $-SECOND

 

THIRD   db '[3] Windows98'

LENGTH_THIRD equ $-THIRD

 

sTITLE  db '..:: BootManager by CodeX ::..',7

LENGTH_sTITLE equ $-sTITLE

 

sTEXT   db '--== Choose OperatingSystem. ==--'

LENGTH_sTEXT equ $-sTEXT

 

Kiírjuk az sTITLE szöveget, alá az sTEXT-et, majd egymás alá a 3 operációs rendszer nevét.

 

    mov     ax, 01300H

    mov     bl, 9                 ;kék színű szöveg

    xor       bh, bh

    mov     dl, TX              ; oszlop

    mov     dh, TY             ; sor

mov     cx, LENGTH_sTITLE                   ; a szöveg hossza

mov     bp, offset sTITLE-100H + 7c00H  ; ehhez később fűzök magyarázatot

    int     010H                                                 ; bios hívása

    add     dh, 2                                                ;2 sorral alá a következő szöveg

    mov     cx, LENGTH_sTEXT

    mov     bp, offset sTEXT-100H + 7c00H

    int     010H

 

    mov     dl, X               ;oszlop                    ; első oprendszer neve

    mov     dh, Y               ;sor

    mov     cx, LENGTH_FIRST

    mov     bp, offset FIRST-0100H + 7c00H

    int     010H

   

    inc     dh                  ;sor

    mov     cx, LENGTH_SECOND               ; második neve

    mov     bp, offset SECOND-0100H + 7c00H

    int     010H

 

    inc     dh                  ;sor

    mov     cx, LENGTH_THIRD                  ; harmadik neve

    mov     bp, offset THIRD-0100H + 7c00H

    int     010H

 

 

A címbeállításról annyit, hogy mivel com-ba fogjuk fordítani a programot, ezért az elhelyezkedés számlálót 100H-ra kell állítanunk a program elején az org direktívával. Így ezt ki kell vonnunk mindig belőle, és mivel a program még megtalálható a 00:7C00 címen, az offset string + 7c00H–nál vannak a szövegek, amiket ki akarunk írni. (az elején már átmásoltuk magunkat máshova, így ott is megvannak, azokat is használhatnánk).

 

Ezután egy várakozó ciklus következik, amíg a felhasználó meg nem nyomja az 1-3 billentyűk valamelyikét. (a 3 oprendszer közül kell választani). Ehhez a 16H-s megszakítás AH=0 funkcióját fogjuk használni.

 

Definiáljuk az ASCII kódokat.

 

KONE                  equ 031H

KTWO                 equ 032H

KTHREE             equ 033H

 

A 07BEH címen van az első partíciós bejegyzés. A 8 byte-tal arrébb pedig a partícióhoz tartozó bootszektor szektorszáma (logikai szektorszám, nem CHS). Addig várunk, amíg a felhasználó le nem üti az 1-3 billentyű valamelyikét. Ha az elsőt választja, akkor a 7beH+8, és 7beh+10 címről beolvassuk a dx és cx regiszterekbe a szektorszámot. A másodiknál a 7beH+8+16 és 7beH+10+16, a harmadiknál a 7beH+8+32 és 7beH+10+32 címekről olvasunk. Az si regiszterben pedig azt tároljuk, hogy hányas oprendszert választotta ki a felhasználó. (0, 1, 2 )

 

 

 

    mov bp, 07beh      ; A 07BEH címen van az első partíciós bejegyzés

kwait:

    xor ah, ah

    int 016h                  ;al = ASCII code

    cmp al, KONE

    jnz skip1  

    ;;;;;;;;;;;;;;;;;;;;;

    mov dx, [word ptr bp+8]                   ; szektor szám alsó szó

    mov cx, [word ptr bp+8+2]               ; szektor szám felső szó

    xor si, si                                             ;si = Partition 0-2  

    ;;;;;;;;;;;;;;;;;;;;;

    jmp end_cikl

skip1:

    cmp al, KTWO

    jnz skip2

    ;;;;;;;;;;;;;;;;;;;;;    2. oprendszer

    add bp, 16

    mov dx, [word ptr bp+8]             ; szektor szám alsó szó

    mov cx, [word ptr bp+8+2]         ; szektor szám felső szó 

    mov si, 1                                      ; si = 0-2

    jmp end_cikl

    ;;;;;;;;;;;;;;;;;;;;;

skip2:

    cmp al, KTHREE

    jnz kwait

    ;;;;;;;;;;;;;;;;;;;;;    3. oprendszer

    add bp, 32

    mov dx, [word ptr bp+8]            ; szektor szám felső szó

    mov cx, [word ptr bp+8+2]        ; szektor szám felső szó 

    mov si, 2                                     ;si = Partition 0-3

    ;;;;;;;;;;;;;;;;;;;;;

end_cikl:

 

 

Ezután következik a megfelelő bootszektor beolvasása. Továbbá van még egy fontos dolog amit el kell végeznünk. Mielőtt ráugranánk a bootszektor betöltő rutinjára, be kell állítanunk a bootflag-et, vagyis a kiválasztott partíciót aktívra kell állítanunk.

Ehhez előbb töröljük mind a négyet és beállítjuk 80H-ra a kiválasztottat.

Az első bootflag a 07c00H+01BEH címen van, a második 16byte-tal arrébb és így tovább.

 

 

 

pusha                              ; elmentjük a regisztereket, mert a dx-cx-ben tároltuk a szektorszámot

     xor    al, al

     mov    cx, 4

     mov    di, 07c00H+01BEH      ;1BEH + 7c00 első

Null_BootFlag:

     stosb    

add    di, 15                             ; azért csak 15 mert a stosb már eggyel növelte

     loop   Null_BootFlag

     mov    di, 07c00H+01BEH   

shl    si, 4                 ;bx (0..3)  * 16 si-ben van a kiválasztott partíció száma (0-3) ezt szorozzuk 16-tal. Ezzel 0-t, 16-t vagy  32-t adunk hozza a di regiszterhez

     add    di, si

     mov    al, 080H       ; így a kiválasztottat állítjuk aktívra

     stosb              

Most ezt az MBR-t vissza kell írnunk. Ehhez az előző részben bemutatott int13H megszakítás 3-as funkcióját fogjuk használni.

 

 

     mov    bx, 07c00         ;es:bx-ről írunk

     mov    ah, 3

     mov    al, 1                 ; 1 szektort írunk

     mov    dl, DRIVE       ; a program elején DRIVE equ 080H (első winchester)

     mov    dh, 0

     mov    cx, 1                 ; mov 0. oldal 0. sáv 1. szektor

     int    013h                 ; írás

 

 

 

Most lesz szükség a már az előző részben használt struktúrára.

 

tpacket struc

  psize                db  16         ;mérete 16 byte

  reserved          db  0            ; ez mindig 0

  number_of_blocks dw  1    ; 1 szektort fogunk olvasni

  buffer_ofs       dw  07c00H ; erre a címre (offset)

  buffer_seg      dw  0            ; erre a címre (szegmens)

  abssec_lo        dd  ?            ; ezt állítjuk be cx-dx-re

  absec_hi         dd  0            ; 64bitből 32 mindig 0

tpacket ends

packet tpacket <>

 

 

Visszatöltjük a regisztereket a veremből.

 

     popa

 

     lea    si, packet - 0100H + 07c00H    ;a packet címe si-ben

     mov    word ptr [si].abssec_lo,   dx    ; a partíció bootszektorának szektorszáma

     mov    word ptr [si+2].abssec_lo, cx  ;a partíció bootszektorának szektorszáma

     mov    ah, 042H

     mov    dl, DRIVE               

     int    013H                  ;EXTENDED BIOS READ olvasás a 0: 7C00-címre

 

     mov ax, 0A07H                ;Bee-Beep megszólaltatjuk a pcSpeakert J

     xor bh, bh

     mov cx, 1

     int 010H

 

     push ds                      ; ugrás a bootsectorra, 00:7c00 (ds =0 )

     mov  ax, 07c00H

     push ax

     retf                      

 

Innentől már a bootszektor betöltő rutinja vezérli a továbbiakat és ha minden jól megy akkor el kell indítania a kiválasztott operációs rendszert.

 

A komplett forráskódot nem csatolom, ugyanis ha valaki meg akarná valósítani, úgyis az ő partícióinak megfelelően kell megírnia a programot. Még egy nagyon fontos dolgot szeretnék leírni, a fenti példa csak elsődleges partíciókkal működik, ha esetleg elkövettük azt a hibát hogy logikai partícióra raktunk operációsrendszert, akkor máshogy kell megoldanunk a dolgot. J. Erre esetleg egy későbbi cikkben kitérünk.

 

A következő részben azzal foglalkozunk majd, hogyan másolhatjuk fel a bootmanagert az MBR-be.

 

Magyar Attila - m.magyar3@chello.hu