Bootloader
Skládá se z několika hlavních částí
- načítání dat z MMC karty (sdcard.c)
- práce s debugovacím sériovým portem (board.c)
- nastavení procesoru (MMU, cache) (hw.c)
- nastavení GPIO (rozsvícení LED) (board.c)
- struktury pro nastavení parametrů kernelu (atag.c)
- načtení tabulky oddílů (partition.c)
- vlastní bootloader – řízení načtení, spuštění a předání parametrů (main.c)
Celý projekt bootloaderu je napsán v CrossWorks 1.7. Je to zatím jediný neopensource nástroj, který se mě nepodařilo nahradit.
První kód, který je nutné spustit v zařízení je bootloader. Jeho úkolem je připravit periferie k úspěšnému načtení jádra. Pro vývoj je nejprve inicializována sériová linka. K tomuto účelu je vyhrazena periferie DBGU (Debug Unit). Pomocí tohoto rozhraní je možné se připojit například k nástroji SAM-BA, který slouží pro nahrání programu do pamětí připojených k procesoru. Z toho důvodu je volba vhodná i pro zobrazování ladících informací, ke kterým by normální uživatel zařízení neměl mít přístup. Dále je provedena inicializace hodinových signálu do periferií. Jsou inicializovány některé GPIO piny a periferie potřebné pro start systému, jako například rozhraní MCI pro práci s paměťovou kartou.
Rozdělení paměťové karty na oddíly
Bootloader se na ladícím rozhraní ohlásí a začne provádět
přípravné práce. Po inicializaci pinů a rozhraní MCI následuje
inicializace paměťové karty. Paměťová karta je rozdělena na několik
primárních oddílů. Každý oddíl obsahuje část systému. Bootloader
nejprve načte tabulku rozdělení disku (partition table) a na ní se snaží
nalézt oddíl se souborovým systémem s identifikačním číslem
0x8A. Pro porovnání, NTFS má číslo 0x07,
souborové systémy Linuxu (Ext2, Ext3, …) mají číslo 0x83.
Identifikační číslo 0x8A bylo zvoleno tak, aby nekolidovalo
s žádným známým souborovým systémem pro běžné operační systémy
(Linux, Windows). Takto označený oddíl neobsahuje souborový systém, ale
přímo obraz jádra. Podle informací
je pro označení oddílu obsahujícího přímo obraz jádra používáno
identifikační číslo 0x8A programem AiR-BOOT.
Tato hodnota byla zachována i pro vlastní bootloader.
Pokud je nalezen oddíl, který má nastaveno identifikační číslo
0x8A a má zároveň nastaven startovací příznak, je z tohoto
oddílu načten obraz jádra do paměti. Nejprve je načten jen první sektor
oddílu. Pokud sektor reprezentuji jako pole neznaménkových celočíselných
32bit proměnných, je na indexu 9 magické číslo 0x016F2818,
které informuje bootloader, že jde o obraz jádra Linuxu ve formátu
zImage pro architekturu ARM. Na pozici 10 je adresa začátku
obrazu a na pozici 11 je adresa konce obrazu. Velikost celého obrazu je
rozdíl těchto dvou čísel. Tím jsou zjištěny všechny informace pro
načtení obrazu jádra do paměti. Paměť RAM začíná na adrese
0x20000000 a obraz jádra je načten na adresu
0x20008000. Adresa počátku obrazu je brána jako adresa funkce
s třemi parametry. První parametr je vždy 0, druhý parametr je
identifikační číslo zařízení, u kamery EYE-02 jde o číslo
1887. Poslední parametr je ukazatel na strukturu s parametry, které
informují jádro například o velikosti paměti RAM.
theKernel(0, machine_type, parm_at);
Podle identifikačního čísla zařízení se jádro rozhodne, jestli je schopno obsluhovat daný hardware. Není dobré používat vlastní identifikační číslo bez předchozí registrace. V případě potřeby aktualizace verze jádra může nastat kolize identifikačních čísel.
Soubor main.c
#include "board.h" #include "debug.h" #include "sdcard.h" #include "atag.h" #include "partition.h" int main(void) { int i; void (*theKernel)(int zero, int arch, atag_t * params); void * exec_at; atag_t * parm_at; void * ramdisk_at; u32 machine_type; char buffer[128]; int result; // oddil, ze ktereho se bude bootovat int boot_partition; // cela tabulka oddilu SD karty partition_t partition_table[4]; board_init(); sdcard_init(); exec_at = (void *)ZIMAGE_LOAD_ADDRESS; parm_at = (atag_t *)(DRAM_BASE + 0x100); ramdisk_at = (void * )INITRD_LOAD_ADDRESS; dbg_print("\r\n======= EYE-02 Bootloader ========\r\n"); // ---------------- nacteni tabulky oddilu dbg_print("Loading partition table..."); if (0 != load_partition_table(partition_table)) { dbg_print("\t\t[ !! ]\r\n"); return -1; } dbg_print("\t\t[ OK ]\r\n"); // --------------- detekce butovatelneho oddilu dbg_print("Searching bootable partition..."); boot_partition = boot_partition_search(partition_table); if (0 > boot_partition) { dbg_print("\t\t[ !! ]\r\n"); return -1; } dbg_print("\t\t[ OK ]\r\n"); // --------------- nacteni obrazu kernelu linuxu dbg_print("Loading kernel"); result = load_image(&partition_table[boot_partition], exec_at); if(result < 0) { if(-1 == result) dbg_print(": zImage not found"); if(-2 == result) dbg_print(": invalid zImage size"); if(-3 == result) dbg_print(": partition < zImage"); dbg_print("\t[ !! ]\r\n"); return -1; } sprintf(buffer, " %d kB ", result); dbg_print(buffer); dbg_print("\t\t[ OK ]\r\n"); dbg_print("Preparing kernel parameters..."); setup_tags(parm_at); /* sets up parameters */ dbg_print("\t\t[ OK ]\r\n"); machine_type = MACH_TYPE; /* get machine type */ theKernel = (void (*)(int, int, atag_t *))exec_at; /* set the kernel address */ theKernel(0, machine_type, parm_at); /* jump to kernel with register set */ return 0; }
Část pro načtení tabulky oddílů a nalezení správného oddílu
#include "board.h" #include "debug.h" #include "sdcard.h" #include "atag.h" #include "partition.h" int main(void) { int i; void (*theKernel)(int zero, int arch, atag_t * params); void * exec_at; atag_t * parm_at; void * ramdisk_at; u32 machine_type; char buffer[128]; int result; // oddil, ze ktereho se bude bootovat int boot_partition; // cela tabulka oddilu SD karty partition_t partition_table[4]; board_init(); sdcard_init(); exec_at = (void *)ZIMAGE_LOAD_ADDRESS; parm_at = (atag_t *)(DRAM_BASE + 0x100); ramdisk_at = (void * )INITRD_LOAD_ADDRESS; dbg_print("\r\n======= EYE-02 Bootloader ========\r\n"); // ---------------- nacteni tabulky oddilu dbg_print("Loading partition table..."); if (0 != load_partition_table(partition_table)) { dbg_print("\t\t[ !! ]\r\n"); return -1; } dbg_print("\t\t[ OK ]\r\n"); // --------------- detekce butovatelneho oddilu dbg_print("Searching bootable partition..."); boot_partition = boot_partition_search(partition_table); if (0 > boot_partition) { dbg_print("\t\t[ !! ]\r\n"); return -1; } dbg_print("\t\t[ OK ]\r\n"); // --------------- nacteni obrazu kernelu linuxu dbg_print("Loading kernel"); result = load_image(&partition_table[boot_partition], exec_at); if(result < 0) { if(-1 == result) dbg_print(": zImage not found"); if(-2 == result) dbg_print(": invalid zImage size"); if(-3 == result) dbg_print(": partition < zImage"); dbg_print("\t[ !! ]\r\n"); return -1; } sprintf(buffer, " %d kB ", result); dbg_print(buffer); dbg_print("\t\t[ OK ]\r\n"); // dbg_print("Loading initial ramdisk"); // dbg_print(" done.\r\n"); // load_image(rdname, INITRD_LOAD_ADDRESS);/* copy initial ramdisk image into RAM */ dbg_print("Preparing kernel parameters..."); setup_tags(parm_at); /* sets up parameters */ dbg_print("\t\t[ OK ]\r\n"); machine_type = MACH_TYPE; /* get machine type */ theKernel = (void (*)(int, int, atag_t *))exec_at; /* set the kernel address */ theKernel(0, machine_type, parm_at); /* jump to kernel with register set */ return 0; }

