See on artiklite sari, mis selgitab, kuidas Linuxi kernel ARM-i arhitektuuriga käivitub. See on esimene osa.
ARM Linuxi alglaadimisprotsess:
Selgitame ARM920T ARM Thumb protsessori ümber ehitatud süsteemi AT91RM9200 alglaadimisprotsessi. Kwickbyte ehitab AT91RM9200 baasil manustatud plaadi nimega kb9202. Võtame selle tahvli näitena ja vaatame, kuidas Linux sellel tahvlil käivitub.
Enne selle lugemise alustamist peate lugema AT91RM9200 andmelehte (spetsifikatsioon).
Alglaadimisprotsessi paremaks mõistmiseks peate lugema ka ARM-i arhitektuuri juhendit.
Linuxi alglaadimisprotsessi komponendid:
Linuxi alglaadimisprotsess hõlmab järgmisi komponente.
Alglaadur
Kerneli kujutis
Juurfailisüsteem
Enne kui näeme, kuidas ülaltoodud komponendid töötavad, on järgmine Linuxi kerneli alglaadimisprotsessi kõnevoog arm-arhitektuuri jaoks. See annab suure pildi kogu Linuxi alglaadimisprotsessist. Kasutame U-boot bootloaderit.
ARM Linuxi alglaadimisprotsess: suur pilt
U-saabas:
_start (cpu/arm920t/start.S)
alguskood (cpu/arm920t/start.S)
start_armboot (lib_arm/board.c)
board_init (board/kb9202/kb9202.c)
timer_init (cpu/arm920t/at91/timer.c)
serial_init (drivers/serial/at91rm9200_usart.c)
main_loop (lib_arm/board.c)
Nüüd on u-boot käivitatud ja käivitatud ning on u-boot-viipaga ja valmis käske vastu võtma. Oletame, et kerneli kujutis laaditakse RAM-i ja väljastatakse käsk bootm.
do_bootm (common/cmd_bootm.c)
bootm_start (common/cmd_bootm.c)
bootm_load_os (common/cmd_bootm.c)
do_bootm_linux (lib_arm/bootm.c)
stext (linux/arch/arm/kernel/head.S)
Juhtimine on antud Linuxile.
Linuxi kernel:
stext (arch/arm/kernel/head.S:78)
__lookup_processor_type (arch/arm/kernel/head-common.S:160)
__lookup_machine_type (arch/arm/kernel/head-common.S:211)
__create_page_tables (arch/arm/kernel/head.S:219)
__arm920_setup (arch/arm/mm/proc-arm920.S:389)
__enable_mmu (arch/arm/kernel/head.S:160)
__turn_mmu_on (arch/arm/kernel/head.S:205)
__switch_data (arch/arm/kernel/head-common.S:20)
start_kernel (init/main.c:529)
start_kernel (init/main.c:529)
tick_init(kernel/time/tick-common.c:413)
setup_arch (arch/arm/kernel/setup.c:666)
setup_machine (arch/arm/kernel/setup.c:369)
lookup_machine_type ( )
setup_command_line (init/main.c:408)
build_all_zonelists (mm/page_alloc.c:3031)
parse_args (kernel/params.c:129)
mm_init (init/main.c:516)
mem_init (arch/arm/mm/init.c:528)
kmem_cache_init (mm/slab.c, mm/slob.c, mm/slub.c)
sched_init (kernel/sched.c)
init_IRQ (arch/arm/kernel/irq.c)
init_timers (kernel/timer.c:1713)
hrtimers_init (kernel/hrtimer.c:1741)
softirq_init (kernel/softirq.c:674)
console_init (drivers/char/tty_io.c:3084)
vfs_caches_init (fs/dcache.c:2352)
mnt_init (fs/namespace.c:2308)
init_rootfs()
init_mount_tree (fs/namespace.c:2285)
do_kern_mount (fs/namespace.c:1053)
set_fs_pwd(fs/fs_struct.c:29)
set_fs_root(fs/fs_struct.c:12)
bdev_cache_init (fs/block_dev.c:465)
chrdev_init (fs/char_dev.c:566)
signals_init (kernel/signal.c:2737)
rest_init (init/main.c:425)
kernel_thread (431, arch/arm/kernel/process.c:388)
kernel_thread() loob kerneli lõime ja juhtimine antakse kernel_init().
kernel_init (431, init/main.c:856)
do_basic_setup (888, init/main.c:787)
init_workqueues (789, kernel/workqueue.c:1204)
driver_init (793, drivers/base/init.c:20)
do_initcalls (796, init/main.c:769) /* Kutsub kõiki alamsüsteeme init-funktsioone */
ettevalmistus_nimeruum (906, init/do_mounts.c:366)
initrd_load (399, init/do_mounts_initrd.c:107)
rd_load_image (117, init/do_mounts_rd.c:158) /* kui initrd on antud */
identifitseerimis_ramdisk_image (179, init/do_mounts_rd.c:53)
hand_initrd (119, init/do_mounts_initrd.c:37) /*kui rd_load_image on edukas */
mount_block_root (45, init/do_mounts.c:233)
do_mount_root (247, init/do_mounts.:218)
mount_root (417, init/do_mounts.c:334) /* kui initrd pole antud */
mount_block_root (359, init/do_mounts.c:233)
do_mount_root (247, init/do_mounts.c:218)
init_post (915, init/main.c:816)
run_init_process (847, init/main.c:807)
kernel_execve (810, arch/arm/kernel/sys_arm.c:81)
Kasutaja ruum
init() /*kasutajaruum /sbin/init */
Alglaadur:
Alglaadur on väike programm, mis laadib kerneli pildi RAM-i ja käivitab kerneli kujutise. Seda nimetatakse ka alglaadimiseks, kuna see toob (tõmbab) süsteemi üles, laadides operatsioonisüsteemi. Bootloader käivitub enne mis tahes muu tarkvara käivitumist ja lähtestab protsessori ning teeb protsessori valmis käivitama programmi nagu operatsioonisüsteem. Enamikul protsessoritel on vaikeaadress, kust toite sisselülitamisel või plaadi lähtestamisel tuuakse koodi esimesed baidid. Riistvaradisainerid kasutavad seda teavet alglaaduri koodi salvestamiseks sellel aadressil ROM-is või välkmällu. Kuna see peaks initsialiseerima protsessori ja käivitama programmi, mis asub arhitektuurispetsiifilisel aadressil, on alglaadurid väga protsessori- ja plaadispetsiifilised. Igal manustatud tahvlil on alglaadimisseade, mille abil saab plaadile alla laadida kerneli kujutise või eraldiseisva rakenduse ja alustada tuuma kujutise või rakenduse käivitamist. Alglaadur käivitatakse, kui protsessoriplaadile on toide. Põhimõtteliselt on sellel pildi laadimiseks ja käivitamiseks minimaalsed funktsioonid.
Süsteemi on võimalik juhtida ka riistvara silumisliidese (nt JTAG) abil. Seda liidest saab kasutada alglaaduri programmi kirjutamiseks buutivasse püsimällu (nt välkmällu), andes protsessori tuumale käsu teha püsimälu programmeerimiseks vajalikke toiminguid. Tavaliselt tehakse seda esimest korda põhilaadimislaaduri allalaadimiseks ja mõne taastamisprotsessi jaoks. JTAG on tavaline ja populaarne liides, mida pakuvad paljud plaadimüüjad. Mõned mikrokontrollerid pakuvad spetsiaalseid riistvaraliideseid, mida ei saa kasutada süsteemi suvaliseks juhtimiseks või koodi otseseks käitamiseks, kuid selle asemel võimaldavad need sisestada alglaadimiskoodi lihtsate protokollide abil alglaaditavasse püsimällu (nt välkmällu). Seejärel kasutatakse tootmisetapis selliseid liideseid alglaadimiskoodi (ja võib-olla ka muu koodi) sisestamiseks püsimällu. Pärast süsteemi lähtestamist hakkab mikrokontroller täitma oma püsimällu programmeeritud koodi, nagu tavalised protsessorid kasutavad käivitamiseks ROM-e. Paljudel juhtudel rakendatakse selliseid liideseid juhtmega loogika abil. Muudel juhtudel võib selliseid liideseid luua tarkvaraga, mis töötab integreeritud kiibil olevas alglaadimis-ROM-is GPIO kontaktidest.
Saadaval on ka mõned muud kolmanda osapoole alglaadurid, mis pakuvad rikkalikku funktsioonide komplekti ja lihtsat kasutajaliidest. Saate need kolmanda osapoole alglaadurid pardale alla laadida ja muuta need oma tahvli jaoks vaikelaadijateks. Üldjuhul asendatakse tahvlite tarnijate pakutavad alglaadurid nende kolmanda osapoole alglaaduritega. Saadaval on üsna palju kolmanda osapoole alglaadureid ja mõned neist on avatud lähtekoodiga (või tasuta alglaadurid) ja mõned on kaubanduslikud. Mõned neist on Das U-Boot, Red boot, GRUB (lauaarvutitele), LILO, Loadlin,, bootsect-loader, SYSLINUX, EtherBoot, ELILO.
Võtame alglaaduriks U-boot boot loaderi. U-boot on manustatud süsteemides laialdaselt kasutatav alglaadur. Selgitame koodi allikast u-boot-2010.03. U-booti saate alla laadida järgmiselt saidilt. http://www.denx.de/wiki/U-Boot
Kuidas U-boot on ehitatud:
U-buutimise konfiguratsiooni põhjal kompileeritakse kõik koostefailid (.S) ja C-failid (.c) konkreetse arhitektuuri jaoks loodud ristkompilaatori abil ja luuakse objektifailid (.o). Kõik need objektifailid lingib linker ja luuakse käivitatav fail. Objektfail või käivitatav fail on jaotiste kogum, nagu.text,.data,.bss jne. Objektifailidel ja käivitatavatel failidel on failivorming nagu elf. Kõik objektifailide jaotised korraldatakse käivitatavas failis skripti alusel, mida nimetatakse linkeri skriptiks. See skript ütleb, kuhu kõik jaotised käivitamisel mällu laaditakse. Selle skripti mõistmine on väga oluline, et teada saada, kuidas alglaadur ja kernel koosnevad ning kuidas alglaaduri või kerneli erinevad osad mällu laaditakse.
Üldiselt loeb laadur programmi käivitamisel (käivitamisel) käivitatava faili ja laadib käivitatava faili erinevad jaotised määratud mälukohta ning alustab linkeri skriptis määratud algusfunktsiooni (sisenemispunkti) täitmist. Kuid kui soovite käivitada (laadida) alglaadurit, ei pea laadima (põhimõtteliselt failivormingu mõistmiseks) käivitatava faili erinevaid jaotisi mällu. Seejärel peate kasutama tööriista nimega objcopy, mis võtab käivitatavast failist kõik jaotised ja loob binaarfaili, millel pole failivormingut. Selle binaarfaili saab mällu laadida ja käivitada või ROM-i sisse kirjutada kindlal aadressil (spetsiifilisel arhitektuuril), mida protsessor käivitab, kui plaadile toide antakse.
Oletame, et U-boot konfiguratsiooni põhjal kompileeritakse kõik failid ja luuakse objektifailid. U-boot makefile kasutab käivitatava faili koostamiseks järgmist linkeri skripti (spetsiifiline arhitektuurile).
Fail: cpu/arm920t/u-boot.lds
32 OUTPUT_FORMAT(“elf32-littlearm”, “elf32-littlearm”, “elf32-littlearm”)
33 OUTPUT_ARCH(õlg)
34 SISESTAMINE(_start)
35 OSA
36 {
37. = 0x00000000;
38
39. = JOONDA(4);
40. tekst:
41 {
42 cpu/arm920t/start.o (.text)
43 *(.tekst)
44 }
4546. = JOONDA(4);
47.rodata: { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
48
49. = JOONDA(4);
50.kuupäev: { *(.kuupäev)}
51
52. = JOONDA(4);
53.sai: { *(.sai)}
54
55.
56 __u_boot_cmd_start =.;
57.u_boot_cmd: { *(.u_boot_cmd) }
58 __u_boot_cmd_end =.;
59
60. = JOONDA(4);
61 __bss_start =.;
62.bss (NOLOAD): { *(.bss). = JOONDA(4); }
63 _end =.;
64 }
OUTPUT_FORMAT real #32 määrab käivitatava faili vormingu. Siin on käivitatav failivorming elf32 ja endianness on little endian. OUTPUT_ARCH real #33 määrab arhitektuuri, millel see kood töötab. ENTRY real #34 määrab u-boot programmi algusfunktsiooni (sisenemispunkti). Siin on sisestuspunkt _start.
SECTIONS real #35 määrab, kuidas käivitatavas failis erinevad jaotised vastendatakse. Loader kasutab programmi erinevate osade mällu laadimiseks selles jaotises määratud aadresse.
‘.’ real #37 määrab algusaadressi, kuhu järgmised jaotised laadida. Sel juhul on algusaadress 0x00000000. Pärast seda real #39 joondatakse mälu 4 baiti ja reale #40 järgneb osa.text.
40. tekst:
41 {
42 cpu/arm920t/start.o (.text)
43 *(.tekst)
44 }
Juures ‘.’ asukoht (0x00000000) kaardistatakse failis cpu/arm920t/start.o olev kood ja see järgib koodi, mis on kõigi teiste objekti (.o) failide tekstiosades. cpu/arm920t/start.o sisaldab funktsiooni _start() (koostekeeles), mis on selle programmi sisenemispunkt.
Nüüd on “.” on 0x00000000 + suurus (.tekst). Jällegi on mälu joondatud 4 baiti ja reale #47 järgneb jaotis.rodata.
. = JOONDA(4);
47.rodata: { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
Kõikide objektifailide .rodata sektsioonid vastendatakse sellel aadressil. Järgib jaotisi.data ja.git.
49. = JOONDA(4);
50.kuupäev: { *(.kuupäev)}
51
52. = JOONDA(4);
53.sai: { *(.sai)}
Iga U-buutimise käsk on ‘cmd_tbl_t’ tüüpi objekt, mis sisaldab käsu nime, abistringi ja funktsioonikursorit, mis tuleb selle käsu käivitamisel käivitada. Kõik need käsuobjektid paigutatakse mällu järjestikku. Kõik need käsuobjektid on objektifailis sisse ehitatud U-boot määratletud jaotisesse nimega.u_boot_cmd. Need all.u_boot_cmd jaotised paigutatakse mällu pärast ülaltoodud jaotisi (.data ja.git).
. =.;
56 __u_boot_cmd_start =.;
57.u_boot_cmd: { *(.u_boot_cmd) }
58 __u_boot_cmd_end =.;
__u_boot_cmd_start sisaldab käsuobjektide algust ja __u_boot_cmd_end sisaldab käsuobjektide lõppu.
Järgmisena järgneb jaotised.bss (initsialiseeritud globaalsed muutujad).
60. = JOONDA(4);
61 __bss_start =.;
62.bss (NOLOAD): { *(.bss). = JOONDA(4); }
63 _end =.;
__bss_start osutab algusaadressile.bss ja _end sisaldab kõigi jaotiste lõppu.
Selle linkeri skripti kasutamine loob käivitatava faili nimega u-boot. Objcopy tööriista kasutatakse binaarfaili genereerimiseks u-boot käivitatavast failist.
u-boot.bin: u-boot
$(OBJCOPY) ${OBJCFLAGS} – või binaarne $