Píšeme operační systém: boot

20. 1. 2006
Doba čtení: 7 minut

Sdílet

Chtěli jste si někdy napsat vlastní operační systém? Nebo jste alespoň chtěli vědět, jak se to dělá? V následujícím článku si popíšeme jednoduchý postup, jak vytvořit boot disketu.

Operační Systém

Začnu tím, že se pokusím definovat pojem operační systém. Kdysi „dávno” před osobními počítači, kdy byly počítače velké jako tělocvičny a neexistovaly OS, program se nadírkoval (nahrál) a když skončil, nadírkoval se další. Ale počítačový čas byl moc drahý na takové plýtvání, a tak vznikly první OS, a to jako programy, které jen postupně pouští ostatní programy. Bylo také velmi nepraktické, že každý program musel mít vlastní ovladače k hardwaru a skoro každý prvek hardwaru byl unikátní, takže přenositelnost programů byla minimální. Takže se do OS začalo integrovat jednotné rozhraní mezi programy a periferiemi (ovladač).

Postupným vývojem jsme se dostali až k dnešní podobě, kdy, díky hardwarové podpoře CPU multitaskingu a multiprocesingu, může dnešní OS střídat běžící programy tak, že slyšíme hudbu, stahujeme soubory a zároveň třeba programujeme. Co ale tedy je operační systém? Je to program, který přiděluje programům paměť a hlavně je „pouští” na procesor. Nedílnou součástí je jednotný přístup k periferiím, sítím, diskům, souborovým systémům. Dnešní OS obsahují i širokou škálu vnitřních nástrojů jako sdílené knihovny nebo komunikaci mezi jednotlivými programy (tzv. systém zpráv). Je to tedy program, který je správce (hardwarových) prostředků.

CPU

Při každém novém procesoru se v minulosti také skoro vždy změnila architektura, buď změnou registrů, nebo se jednoduše přešlo z 4 bitů na 8 bitů. To znamenalo, že se každý program, OS nevyjímaje, musel přepsat, ne-li napsat zcela znovu, což bylo velice nákladné a brzdilo rozvoj.

Intel & x86

V roce 1978 firma Intel přišla na trh z procesorem Intel 8086. Byl zpětně kompatibilní s 8b procesory. Byl 16 bitový, ale adresovací sběrnici měl 20 bitovou, mohl tedy adresovat 1 MB fyzické paměti, pracoval v reálném režimu (real mode). Každý program mohl přistupovat do celé paměti, takže bezpečnost a separovatelnost programů byla na minimální úrovni. Podporoval přerušení jak hardwarové, tak i softwarové, které známe třeba z programování pod dosem. Důležité ale je, že od roku 1978 mluvíme o architektuře x86.

Dalším milníkem se stal procesor Intel 386, který byl 32b. A podporoval chráněný režim (protected mode), což je systém správy paměti, který umožňuje alokovat 4GB fyzické paměti, podporuje tzv. stránkování a virtuální paměť, kde může OS oddělit jednotlivé programy od sebe a simulovat větší paměť ukládáním stránek na disk (swap). Chráněný režim také napomáhá operačnímu systému s multitaskingem. Není ale zpětně kompatibilní s reálným režimem, což nám ale i386 vynahrazuje virtuálním režimem, který simuluje reálný. A tak můžeme programy psané pro reálný režim za kooperace OS pouštět i v chráněném. Chráněný režim podporoval už procesor Intel 286, který byl ale ještě 16bitový.

Budeme se zabývat pouze procesory x86 kompatibilními, což je asi nejpoužívanější architektura osobních počítačů. Důležité je, že každý procesor po startu (restartu) pracuje v 16 bitovém reálném adresovacím režimu a nezáleží na tom, jestli to je „stařičká” i386 nebo nejnovější 64bitový procesor architektury ia64. Je už jen na operačním systému, aby se přepnul do jiných režimů.

Boot systému

Jak tedy probíhá boot samotného systému? Po startu se nahraje do paměti BIOS a pustí se. My si nastavíme bootování z disketové mechaniky. BIOS vezme náš označený (poslední dva Byte mají speciální hodnotu) boot sector, což je první sector na disketě. Nahraje ho do paměti na místo 0000:7c00h a spustí ho.

Reálný režim

Teď si více osvětlíme reálný režim, ať víme, jak náš boot sector napsat. K dispozici máme 14 16-bitových registrů. Všeobecné: AX, BX, CX, DX, indexové a ukazatelové: SP, BP, SI, DI, segmentové: CS, DS, SS, ES a dále: IP a FLAGS. Důležitý je vztah segmentových registrů. Jak již víme, adresa je tvořena 20bity, ale registry máme 16 bitové. Adresa se tedy vytváří sečtením jednoho ze segmentových registrů posunutého o 4 bity (a vynásobeného 16) a zbytku adresy, tzv. offsetu. Adresa = segment * 16 + offset. Zapisujeme: „segment:offset”. 12 bitů se překrývá, neboli jednu adresu můžeme zapsat několika způsoby, třeba 0000:7c00h = 07c0:0000h = 00c0:7000h. Podle následujícího schematu se implicitně používají segmentové registry: SS:SP, SS:BP, DS:BX, DS:SI, DS:DI, ES:DI (řetězcové operace). Explicitní přiřazení v intel syntaxi vypadá následovně:

mov ah,es:[ukazatel]
mov ah,es:offset 

Příklady

Ukážeme si příklad boot sectoru:

; boot_sector1.asm

org 0x7c00  ;nastaveni segmentu

jmp main

;promene

boot_msg db 'Muj prvni OS...',13,10,0
klav_msg db 'Stisknete jakoukoliv klavesu k restartu',13,10,0

bootdev db 0

zprava:   ;funkce na vypis na obrazovku
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret

klavesa:        ;funkce na stisk klavesy
mov ah,00h
int 16h
ret

main:
cli
mov ax,9000h
mov ss,ax
mov sp,0xffff     ;nastaveni zasobniku
sti

mov [bootdev],dl  ;dl = zarizeni ze ktereho je bootovano

mov si,boot_msg   ;vypis boot zpravy
call zprava

mov si,klav_msg
call zprava
call klavesa
jmp 0xffff:0000h        ;restart

times 510-($-$) db 90h   ;sector musi byt dlouhy 512B

dw 0xaa55         ;oznaceni boot sectoru 

Program přeložíme do výstupního formátu: plain binary file a nahrajeme na disketu:

nasm -o boot_sector.asm -f bin boot_sector.bin
dd if=boot_sector.bin of=/dev/fd0 bs=512 

Pokud se budeme chtít podívat, jak soubor formátu plain binary vypadá po přeložení:

ndisasm -b 16 boot_sector.bin 

První direktiva programu (org 0×7c00) říká překladači, aby každému skoku na návěstí (call zprava), popřípadě adresaci proměnné (mov si,boot_msg) přičetl danou hodnotu, a to z důvodu umístění programu v paměti. Program se začne vykonávat prvním bytem souboru, a proto nesmí být začátek souboru datový, ale instrukční, bohatě však postačí, když nás jednoduše první instrukce přesune do těla programu ( main ) a hned potom následuje datová část. Hned na začátku těla programu nastavíme zásobník, abychom mohli volat funkce. Dále si uložíme hodnotu registru DL uchovávající driver, z nějž je bootováno. Zbytek je už na vaší fantazii. V celém programu můžeme užívat pouze funkce (přerušení) BIOSu, nikoliv DOSu, protože dos se vůbec nenahraje. Dále adresa ffff:0000h je adresou, kde začíná procesor po startu vykonávat první instrukci, proto nám k restartu stačí skočit na tuto adresu. Na konci zdrojového kódu musíme uvést direktivu, která nastaví soubor na délku 512 B a na konec přidá hodnotu ( AA55h ), která označuje boot sector.

Při bootu se z diskety nahraje pouze jeden sector dlouhý 512 B, což není nijak moc, ale bohatě stačí na program, který nahraje další sectory z diskety a spustí je.

Příklad takového boot sectoru:

;boot_sector2.asm

org 0x7c00

jmp main

;promene

boot_msg db 'Muj prvni OS...',13,10,0
load_msg db 'Zavadim jadro ...',13,10,0
bootdev db 0

zprava:
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret

reset_dev:      ;reset driveru
mov ah,00h
mov dl,[bootdev]
int 13h
ret

load_image:
mov ax,0h
mov es,ax
mov ah,02h
mov dl,[bootdev]
mov dh,0h
mov cx,2
mov al,1   ;pocet sectoru k nahrani
mov bx,8000h
int 13h

ret

main:
cli
mov ax,9000h
mov ss,ax
mov sp,0xffff
sti

mov [bootdev],dl

mov si,boot_msg
call zprava
mov si,load_msg
call zprava

call reset_dev
call load_image

jmp 0000:8000h ;skok do pameti k nahranemu programu

smycka:
jmp smycka

times 510-($-$) db 90h

dw 0xaa55 

Tento program nahraje druhý sector z diskety do paměti na adresu 0000:8000h a spustí jej. Můžeme dokonce nahrát více sectorů naráz, a to změnou hodnoty registru CX, ve funkci: load_image. Takže nahrávaný program nemusí mít pouze 512 B. Program může být libovolný, stejně jako u boot sectoru, musí být na začátku instrukce. Zásobník je již nastaven. A na konci kódu již nemusíme soubor zarovnávat na délku dělitelnou 512 B.

Ukázka:

bitcoin_skoleni

; jadro.asm

org 8000h

jmp main

;promnene

wel_msg db 'Jadro nahrano.',13,10,0
end_msg db 'Stisknete jakoukoliv klavesu k restartu',13,10,0

zprava:
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret

klavesa:
mov ah,00h
int 16h
ret

main:

mov si,wel_msg
call zprava
mov si,end_msg
call zprava
call klavesa

jmp 0xffff:0000h 

Pokud máme tyto dva soubory, musíme je spojit v jeden image soubor, a to následovně:

; image.asm

incbin 'boot_sector.bin'
incbin 'jadro.bin' 

Boot sector a druhý program nahrajeme na disketu následovně:

nasm -o boot_sector.asm -f bin boot_sector.bin
    nasm -o jadro.asm -f bin jadro.bin
    nasm -o image.asm -f bin image.bin
    dd if=image.bin of=/dev/fd0 bs=512 

Autor článku