Kesäloman viimeisen viikon kunniaksi päätin koodata oman käyttöjärjestelmän. En niinkään tehdäkseni omaa Linux-kloonia, vaan kokeillakseni millaista nykyään on ohjelmoida raakaa x86-arkkitehtuurin PC:tä.

Oman käyttöjärjestelmän tekeminen on yllättävän helppoa, jos osaa koodata hieman assemblerilla. PC:t käynnistyvät nykyään USB-tikulta ja vielä kätevämpää on tehdä kokeiluja esimerkiksi VirtualBoxissa. Debuggaamisessa toimii hyvin Bochs, jonka logeista näkee heti, mikä meni vikaan ja mitä prosessorin rekistereissä oli silloin.

Assembly-koodaaminen onnistuu kätevimmin Linuxilla tai Mac OS X:llä, jonka mukana tulee NASM (Netwide Assembler) sekä GCC ja muut tarvikkeet. Yksinkertaisen MBR-bootloaderin voi tehdä pelkästään NASMia käyttäen (esimerkki alempana).

BIOS ja koneen käynnistyminen

Tavallisessa PC:ssä BIOSin asetuksista voi valita, miltä massamuistilta käyttöjärjestelmä ladataan. Ennen vanhaan oli kätevintä käyttää 1.44MB korppuasemaa kokeilemiseen, mutta nykyään USB-tikku on paras optio.

Kun kone käynnistyy, BIOS lataa USB-tikulta ensimmäisen blokin (512 tavua) muistiosoitteeseen 0x07c0:0000. Tätä blokkia kutsutaan MBR:ksi (Master Boot Record). Tavallisesti siihen mahtuu 440 tavua konekielistä ohjelmakoodia, jota seuraa partitiotaulu. Jos ei tarvitse partitioita, ohjelmakoodi voi olla 510 tavua pitkä.

BIOS tarkistaa, että muistiin ladattu MBR-blokki päättyy maagisiin tavuihin 0xaa55. Sitten se vain hyppää osoitteeseen 0x07c0:0000, jolloin kontrolli siirtyy omalle ohjelmakoodille. Tässä vaiheessa x86-prosessori on Real Modessa, eli rekisterit ovat 16-bittisiä, segmenttirekisterit toimivat perinteiseen tapaan ja muistin maksimimäärä on 1 megatavu.

Bootloaderin ohjelmointi

Tässä on esimerkki bootloaderista, joka tulostaa vain ruudulle "Loading..." ja pysäyttää sitten prosessorin:

; NASM MBR boot loader by Kenneth Falck 2009
[bits 16]                               ; 16-bit code
[org 0x7c00]                            ; BIOS loads us at 0x07c0:0000
jmp 0x0000:initialize_bios              ; reset code segment to 0x0000 with long jump

initialize_bios: xor ax, ax mov ds, ax ; reset data segments to 0x0000 mov es, ax mov [bootdrive], dl ; store boot drive mov si, welcome ; print welcome string call print ;jmp load_kernel_header ; proceed to load kernel

halt: hlt ; halt CPU to save power jmp halt ; loop if halt interrupted

print: ; Print string in SI with bios mov al, [si] inc si or al, al jz exit_function ; end at NUL mov ah, 0x0e ; op 0x0e mov bh, 0x00 ; page number mov bl, 0x07 ; color int 0x10 ; INT 10 - BIOS print char jmp print exit_function: ret

data: welcome db 'Loading…', 0 ; welcome message error db 'Error', 0 ; error message bootdrive db 0x00 ; original BIOS boot drive

times 510 - ($ - $$) db 0 ; filler to 510 bytes dw 0xaa55 ; boot signature (fills to 512 bytes)

Olettaen että koodinpätkä on tallennettu nimellä bootloader.asm, sen voi kääntää binääri-imageksi komentamalla:

$ nasm -f bin -o bootloader.img bootloader.asm

Käynnistäminen USB-tikulta

Syntynyt 512 tavun mittainen bootloader.img voidaan kirjoittaa suoraan USB-tikulle. Sitä ei siis kopioida tikulla olevaan FAT16-tiedostojärjestelmään tiedostoksi, vaan tikun koko sisältö ylikirjoitetaan raa'asti. (HUOM: tikun aiempi sisältö tuhoutuu.) Olettaen että USB-tikun laitenimi on /dev/diskX, komento kuuluu:

$ dd if=bootloader.img of=/dev/diskX
$ diskutil eject /dev/diskX # Mac OS X

Nyt tikun voi kytkeä mihin tahansa PC:hen ja määritellä BIOSista koneen käynnistymään USB-tikulta.

Käynnistäminen VirtualBoxissa

VirtualBox ei osaa käynnistyä USB-tikulta, joten siinä on helpointa tehdä MBR-imagesta virtuaalinen kovalevy. Tämä onnistuu seuraavalla komennolla (*):

$ VBoxManage convertfromraw -format VDI bootloader.img bootloader.vdi

Lopuksi luodaan uusi virtuaalikone ja määritellään sille tavallinen IDE-kovalevy, jonka sisältö tulee tiedostosta bootloader.vdi. VBoxManage-komennolla voi skriptata kovalevyn päivittymään automaattisesti siten, että aina kun bootloaderista käännetään uusi versio, VirtualBox generoi ja ottaa käyttöön uuden imagen.

(*) Jos VBoxManage valittelee, että bootloader.img on liian pieni, sen perään voi lisätä vaikkapa megatavun nollia /dev/zerosta. Näillä ei ole vaikutusta, koska BIOS kuitenkin lataa muistiin vain ensimmäiset 512 tavua.

Seuraavassa osassa Protected Mode ja A20-linjan aktivointi.