SoftwareEmbedded Linux v kameře Jablocom EYE-02

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;
}

Přiložené soubory:

Autor WWW
Titulek
Číslo stojedna  
Severovýchod Valid XHTML 1.0 Strict Valid CSS 2 Text to HTML converter and formatter Jay Bee simple CMS