× Aktuálně z oboru

Programátoři po celém světě dnes slaví Den programátorů [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]
Celá zprávička [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]

JamesM's kernel development tutorials - 2. díl

[ http://programujte.com/profil/2881-ondrej-splichal/ ]Google [ :?rel=author ]       [ http://programujte.com/profil/2327-tomas-barton/ ]Google [ ?rel=author ]       18. 2. 2009       18 488×

Genesis
Překlad tutoriálů pro začínající OS vývojáře od Jamese Molloye (www.jamesmolloy.co.uk [ http://www.jamesmolloy.co.uk ]).

Chtěl bych poděkovat Jamesi Molloyovi, který mi dovolil jeho tutoriál(y) přeložit. Tuším, že se tak stanou vůbec prvními svého druhu na českém Internetu.

Kontakt na autora:

  • www.jamesmolloy.co.uk [ http://www.jamesmolloy.co.uk ] - autorův "internetový domov", najdete zde jednak tutoriály v originálním anglickém podání, jednak "Jimix" - jádro psané v C++
  • james<at>jamesmolloy.co.uk - emaily na tuto adresu pište prosím POUZE v angličtině

OK, přišel čas kódit! Ačkoli hlavní část jádra bude napsána v C, v určitých oblastech se bez assembleru zkrátka neobejdeme. Jedním z těchto případů je počáteční bootovací kód.

Tady ho máme:

;
; boot.s -- Startovní pozice jádra. Rovněž definuje multiboot hlavičku.
; Založeno na Bran's kernel development tutorial souboru start.asm
;

MBOOT_PAGE_ALIGN    equ 1<<0    ; Nahraje jádro a moduly na začátek stránky
MBOOT_MEM_INFO      equ 1<<1    ; Poskytne jádru informace o paměti
MBOOT_HEADER_MAGIC  equ 0x1BADB002 ; hodnota Multiboot Magic
; POZNÁMKA: Nepoužíváme MBOOT_AOUT_KLUDGE. To znamená, že nám GRUB 
; nepředá tabulku symbolů.
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)


[BITS 32]                       ; Všechny instrukce by měli být 32-bitové

[GLOBAL mboot]                  ; Zpřístupní 'mboot' z C.
[EXTERN code]                   ; Start '.text' sekce.
[EXTERN bss]                    ; Start .bss sekce.
[EXTERN end]                    ; Konec poslední načítané sekce.

mboot:
  dd  MBOOT_HEADER_MAGIC        ; GRUB bude hledat tuto hodnotu na
                                ; začátku každé stránky jádra
  dd  MBOOT_HEADER_FLAGS        ; Jak by měl GRUB nahrát váš soubor / nastavení
  dd  MBOOT_CHECKSUM            ; Zaručí, že jsou horní hodnoty správné.
   
  dd  mboot                     ; Pozice tohoto deskriptoru.
  dd  code                      ; Začátek sekce '.text' (kód).
  dd  bss                       ; Konec sekce '.data'.
  dd  end                       ; Konec jádra.
  dd  start                     ; Vstupní bod jádra (počáteční EIP).

[GLOBAL start]                  ; Vstupní bod jádra.
[EXTERN main]                   ; Vstupbní bod pro kód v C

start:
  push    ebx                   ; Předá pozici multiboot hlavičky

  ; Spuštění jádra:
  cli                         ; Zakáže přerušení.
  call main                   ; Zavolá naši funkci main().
  jmp $                       ; Vstoupíme do nekonečné smyčky, abychom zabránili
                              ; procesoru ve spouštění všelijakého bordelu nacházejícího se
                              ; v paměti za jádrem! 

Je tam vlastně jen pár řádků kódu:

push ebx
cli
call main
jmp $

To ostatní je multiboot hlavička.

Multiboot

GRUB předpokladá, že jádro dodržuje multiboot standard. Je to způsob, jakým může bootloader

  1. přesně zjistit, jaké prostředí bude jádro při zavádění potřebovat/požadovat
  2. poskytnout jádru informace o prostředí, které si pro něj připravil

Takže například pokud se bude vaše jádro potřebovat načíst ve specifickém VESA módu (což je mimochodem špatný nápad), informujete bootloader a on se už o to postará za vás.

Aby bylo jádro multiboot-kompatibilní, musíte do něj někam umístit multiboot hlavičku (přesněji, hlavička musí tvořit první 4KB jádra). Existuje NASM příkaz, který do kódu vloží potřebné konstanty - dd.

dd MBOOT_HEADER_MAGIC
dd MBOOT_HEADER_FLAGS
dd MBOOT_CHECKSUM
dd mboot
dd code
dd bss
dd end
dd start

Konstanty MBOOT_* jsme definovali na začátku.

MBOOT_HEADER_MAGIC
Magické číslo. Značí, že je jádro multiboot-kompatibilní.

MBOOT_HEADER_FLAGS
Pole příznaků. Požádáme GRUB o zarovnaní každé sekce jádra (MBOOT_PAGE_ALIGN) a informace o paměti (MBOOT_MEM_INFO). V některých tutoriálech je použito také MBOOT_AOUT_KLUDGE. Jelikož my používáme formát ELF, tento hack není potřeba a jeho přidání by zabránilo GRUBu v předání tabulky symbolů při startu.

MBOOT_CHECKSUM
Slouží k detekci chyb. Součet magic number, příznaků a této hodnoty musí být nula.

mboot
Adresa struktury, kterou právě píšeme.

code,bss,end,start
Tyto symboly definuje linker. Pomocí nich předáváme GRUBu pozici jednotlivých sekcí jádra.

Při startu GRUB do registru EBX uloží ukazatel na strukturu, ve které se nacházejí informací o prostředí, které pro nás připravil.

Zpátky ke kódu...

Bezprostředně po startu povíme CPU, aby přidal obsah EBX na stack (v EBX se nyní nachází ukazatel na multiboot informační strukturu), zakázal přerušení (CLI), zavolal naši main funkci (kterou jsme zatím nedefinovali) a vstoupil do nekonečné smyčky.

To všechno je sice moc pěkné, ale kód se neslinkuje. Nemáme definovanou main()!

Přidání trošky Céčkovského kódu

Propojování C a ASM je neuvěřitelně jednoduché. Stačí znát použitou "volací dohodu". GCC na architektuře x86 používá konvenci __cdecl:

  • Všechny parametry jsou funkci předávány přes stack.
  • Parametry jsou přidávány zprava doleva.
  • Návratová hodnota funkce se nachází v EAX.

...takže volání funkce:

d = func(a, b, c);

vypadá takto:

push [c]
push [b]
push [a]
call func
mov [d], eax

Nic na tom není! Jak můžete vidět v ASM kódu výše, to push ebx vlastně jen předává hodnotu EBX jako parametr funkci main().

C kód

// main.c -- definuje vstupní bod C kódu, provádí inicializaci
// JamesM's tutorials

int main(struct multiboot *mboot_ptr)
{
  // tady budeme volat všechny inicializační funkce
  return 0xDEADBABA;
}

Takhle tedy vypadá naše první funkce main(). Jak můžete vidět, funkci je předán jeden parametr - ukazatel na multiboot strukturu. Tu si definujeme později (nepotřebujeme ji definovat k úspěšné kompilaci!).

Vše, co funkce dělá, je navrácení konstanty - 0xDEADBABA. Ta je snad dost neobvyklá, abychom si ji všimli, až program za chvilku spustíme.

Kompilování, linkování a spuštění!

Teď, když jsme do našeho projektu přidali nový soubor, musíme ho přidat i do makefile. Tyto řádky:

SOURCES=boot.o
CFLAGS=

nahraďte za:

SOURCES=boot.o main.o
CFLAGS=-nostdlib -nostdinc -fno-builtin -fno-stack-protector

Musíme zastavit GCC, který se bude snažit přilinkovat linuxové C knihovny k našemu jádru - nebude to fungovat (zatím). K tomu slouží CFLAGS.

OK, teď byste měli být schopni zkompilovat, slinkovat a spustit vaše jádro!

cd src
make clean  # Ignorujte jakékoli chyby, které tu nastanou
make
cd ..
./update_image.sh
./run_bochs.sh  # Možná budete vyzvání k zadání root hesla

To by mělo spustit bochs. Pár sekund uvidíte GRUB, následně se spustí samotné jádro, které vlastně neudělá nic jiného, než zamrzne s hláškou "starting up...".

Jestliže si prohlédnete bochsout.txt, vespod byste měli uvidět něco podobného:

00074621500i[CPU  ] | EAX=deadbaba  EBX=0002d000  ECX=0001edd0 EDX=00000001
00074621500i[CPU  ] | ESP=00067ec8  EBP=00067ee0  ESI=00053c76 EDI=00053c77
00074621500i[CPU  ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af pf cf
00074621500i[CPU  ] | SEG selector     base    limit G D
00074621500i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00074621500i[CPU  ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00074621500i[CPU  ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074621500i[CPU  ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074621500i[CPU  ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074621500i[CPU  ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074621500i[CPU  ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074621500i[CPU  ] | EIP=00100027 (00100027)
00074621500i[CPU  ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00074621500i[CPU  ] | CR3=0x00000000 CR4=0x00000000
00074621500i[CPU  ] >> jmp .+0xfffffffe (0x00100027) : EBFE

Všimli jste si hodnoty EAX? 0xDEADBABA - návratová hodnota main(). Gratuluji, už máte multiboot-kompatibilní "trampolínu", a tak vám nic nebrání začít psát na obrazovku!

Vzorový kód tutoriálu naleznete zde [ http://www.jamesmolloy.co.uk/downloads/genesis.tar ].

Zdroj: http://www.jamesmolloy.co.uk/tutorial_html/2.-Genesis.html

Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2008051100-jamesm-s-kernel-development-tutorials-2-dil/ ].