ARM Linux oppstartsprosess

Av | februar 14, 2022

Dette vil være en serie artikler som forklarer hvordan Linux-kjernen starter opp på ARM-arkitektur. Dette er del én.

ARM Linux oppstartsprosess:

Vi vil forklare oppstartsprosessen til AT91RM9200 system-på-brikke, bygget rundt ARM920T ARM Thumb-prosessoren. Kwickbyte bygger et innebygd bord kalt kb9202 basert på AT91RM9200. Vi vil ta dette brettet som et eksempel og se hvordan Linux starter opp på dette brettet.

Før du begynner å lese dette må du lese AT91RM9200 datablad (spesifikasjon).

Du må også lese ARM Architecture Reference Manual for en bedre forståelse av oppstartsprosessen.

Komponenter i Linux Boot Process:

Linux-oppstartsprosessen involverer følgende komponenter.

Bootloader

Kjernebilde

Rotfilsystem

Før vi ser hvordan komponentene ovenfor fungerer, er det følgende anropsflyten til Linux Kernel-oppstartsprosessen for armarkitektur. Dette gir et stort bilde av hele Linux-oppstartsprosessen. Vi bruker U-boot bootloader.

ARM Linux Boot Process: Stort bilde

U-støvel:

_start (cpu / arm920t / start.S)

start_code (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 (drivere / seriell / at91rm9200_usart.c)

main_loop (lib_arm / board.c)

Nå er u-boot oppe og går og er i u-boot-prompt og klar til å godta kommandoer. Anta at kjernebildet er lastet inn i RAM og utstedt bootm-kommando.

do_bootm (vanlig / cmd_bootm.c)

bootm_start (vanlig / cmd_bootm.c)

bootm_load_os (vanlig / cmd_bootm.c)

do_bootm_linux (lib_arm / bootm.c)

stext (linux / arch / arm / kernel / head.S)

Kontroll er gitt til linux.

Linux-kjernen:

stext (bue / arm / kjerne / hode.S: 78)

__lookup_processor_type (bue / arm / kjerne / head-common.S: 160)

__lookup_machine_type (bue / arm / kjerne / head-common.S: 211)

__lage_sidetabeller (bue / arm / kjerne / hode.S: 219)

__arm920_oppsett (bue / arm / mm / proc-arm920.S: 389)

__enable_mmu (bue / arm / kjerne / hode.S: 160)

__turn_mmu_on (bue / arm / kjerne / hode.S: 205)

__switch_data (bue / arm / kjerne / head-common.S: 20)

start_kernel (init / main.c: 529)

start_kernel (init / main.c: 529)

tick_init (kjerne / tid / 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 (kjerne / params.c: 129)

mm_init (init / main.c: 516)

mem_init (bue / arm / mm / init.c: 528)

kmem_cache_init (mm / slab.c, mm / slob.c, mm / slub.c)

sched_init (kjerne / sched.c)

init_IRQ (bue / arm / kjerne / irq.c)

init_timers (kjerne / timer.c: 1713)

hrtimers_init (kjerne / hrtimer.c: 1741)

softirq_init (kjerne / softirq.c: 674)

console_init (drivere / 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 (kjerne / signal.c: 2737)

rest_init (init / main.c: 425)

kernel_thread (431, arch / arm / kernel / process.c: 388)

kernel_thread () lager en kjernetråd og kontroll gis til kernel_init ().

kernel_init (431, init / main.c: 856)

do_basic_setup (888, init / main.c: 787)

init_workqueues (789, kjerne / workqueue.c: 1204)

driver_init (793, drivere / base / init.c: 20)

do_initcalls (796, init / main.c: 769) / * Kaller alle undersystemer init-funksjoner * /

prepare_namespace (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) / * hvis initrd er gitt * /

identifis_ramdisk_image (179, init / do_mounts_rd.c: 53)

handle_initrd (119, init / do_mounts_initrd.c: 37) / * hvis rd_load_image er vellykket * /

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) / * hvis initrd ikke er gitt * /

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)

Brukerplass

init () / * brukerområde / sbin / init * /

Bootloader:

En bootloader er et lite program som laster kjernebildet inn i RAM og starter opp kjernebildet. Dette kalles også bootstrap da det henter (trekker) opp systemet ved å laste et operativsystem. Bootloader starter før noen annen programvare starter og initialiserer prosessoren og gjør cpu klar til å kjøre et program som et operativsystem. De fleste prosessorer har en standardadresse som de første bytene med kode hentes fra når strøm tilføres eller kortet tilbakestilles. Maskinvaredesignere bruker denne informasjonen til å lagre bootloader-koden på den adressen i ROM eller flash. Siden den skal initialisere cpuen og skal kjøre et program som er plassert på en arkitekturspesifikk adresse, er bootloaders svært prosessorspesifikke og kortspesifikke. Hvert innebygde brett kommer med en oppstartstrap for å laste ned kjernebildet eller den frittstående applikasjonen til brettet og begynne å kjøre kjernebildet eller applikasjonen. Bootloader vil bli utført når strøm tilføres et prosessorkort. I utgangspunktet vil det ha noen minimale funksjoner for å laste bildet og starte det opp.

Det er også mulig å kontrollere systemet ved å bruke et maskinvarefeilsøkingsgrensesnitt som JTAG. Dette grensesnittet kan brukes til å skrive oppstartslasterprogrammet inn i oppstartbart, ikke-flyktig minne (f.eks. flash) ved å instruere prosessorkjernen til å utføre de nødvendige handlingene for å programmere ikke-flyktig minne. Vanligvis gjort for første gang for å laste ned den grunnleggende bootloaderen og for en gjenopprettingsprosess. JTAG er et standard og populært grensesnitt levert av mange brettleverandører. Noen mikrokontrollere gir spesielle maskinvaregrensesnitt som ikke kan brukes til å ta vilkårlig kontroll over et system eller kjøre kode direkte, men i stedet tillater de innsetting av oppstartskode i oppstartbart ikke-flyktig minne (som flash-minne) via enkle protokoller. Så i produksjonsfasen brukes slike grensesnitt til å injisere oppstartskode (og muligens annen kode) i ikke-flyktig minne. Etter tilbakestilling av systemet begynner mikrokontrolleren å kjøre kode som er programmert inn i det ikke-flyktige minnet, akkurat som vanlige prosessorer bruker ROM-er for oppstart. I mange tilfeller er slike grensesnitt implementert av kablet logikk. I andre tilfeller kan slike grensesnitt opprettes av programvare som kjører i integrert oppstarts-ROM på brikken fra GPIO-pinner.

Det er noen andre tredjeparts bootloadere tilgjengelig som gir et rikt sett med funksjoner og enkelt brukergrensesnitt. Du kan laste ned disse tredjeparts bootloaderne til tavlen og kan gjøre dem til standard bootloadere for tavlen. Vanligvis erstattes bootloadere levert av brettleverandører med disse tredjeparts bootloader. Det er ganske mange tredjeparts booladere tilgjengelig, og noen av dem er åpen kildekode (eller gratis bootloadere) og noen er kommersielle. Noen av dem er Das U-Boot, Red boot, GRUB (for stasjonære), LILO, Loadlin ,, bootsect-loader, SYSLINUX, EtherBoot, ELILO.

Vi tar U-boot boot loader som vår boot loader. U-boot er den mye brukte oppstartslasteren i innebygde systemer. Vi vil forklare kode fra u-boot-2010.03-kilden. Du kan laste ned U-boot fra følgende nettsted. http://www.denx.de/wiki/U-Boot

Hvordan U-boot er bygget:

Basert på konfigurasjonen av U-boot, kompileres alle assembly-filene (.S) og C-filene (.c) ved hjelp av krysskompilator som er bygget for en bestemt arkitektur og objektfiler (.o) vil bli generert. Alle disse objektfilene er koblet sammen med linker og en kjørbar fil vil bli opprettet. En objektfil eller kjørbar fil er en samling av seksjoner som.tekst, .data, .bss osv. Objektfiler og kjørbare filer har et filformat som elf. Alle delene av objektfilene vil bli ordnet i den kjørbare filen basert på et skript kalt linkerskript. Dette skriptet forteller hvor alle seksjonene skal lastes inn i minnet når det kjører. Å forstå dette skriptet er veldig viktig for å vite hvordan oppstartslasteren og kjernen er sammensatt og hvordan forskjellige deler av oppstartslasteren eller kjernen lastes inn i minnet.

Vanligvis, når et program kjøres (kjøres), leser en laster den kjørbare filen og laster forskjellige deler av den kjørbare filen på den angitte minneplasseringen og begynner å utføre startfunksjonen (inngangspunktet) spesifisert i linkerskriptet. Men hvis du ønsker å kjøre (laste) en oppstartslaster, vil det ikke være noen laster for å laste (i utgangspunktet for å forstå filformatet) forskjellige deler av den kjørbare filen inn i minnet. Deretter må du bruke et verktøy som heter objcopy som tar alle deler fra den kjørbare filen og oppretter en binær fil som ikke har noe filformat. Denne binære filen kan lastes inn i minnet og kjøres eller kan skrives inn i ROM-en på en bestemt adresse (spesifikk for arkitekturen) som vil bli utført av cpu når strøm tilføres kortet.

Anta at basert på U-boot-konfigurasjonen blir alle filene kompilert og objektfiler opprettet. U-boot makefile bruker følgende linkerskript (spesifikt for arkitektur) for å bygge en kjørbar fil.

Fil: cpu / arm920t / u-boot.lds

32 OUTPUT_FORMAT («elf32-littlearm», «elf32-littlearm», «elf32-littlearm»)

33 OUTPUT_ARCH (arm)

34 ENTRY (_start)

35 SEKSJONER

36 {

37. = 0x00000000;

38

39. = ALIGN (4);

40.tekst:

41 {

42 cpu / arm920t / start.o (.tekst)

43 * (. Tekst)

44}

4546. = ALIGN (4);

47.dato: {* (SORT_BY_ALIGNMENT (SORT_BY_NAME (.rodata *)))}

48

49. = OPPRETT (4);

50.data: {* (. Data)}

51

52. = OPPRETT (4);

53.got: {* (. Got)}

54

55. = .;

56 __u_boot_cmd_start = .;

57.u_boot_cmd: {* (. U_boot_cmd)}

58 __u_boot_cmd_end = .;

59

60. = OPPRETT (4);

61 __bss_start = .;

62.bss (NOLOAD): {* (. Bss). = ALIGN (4); }

63 _end = .;

64}

OUTPUT_FORMAT i linje #32 spesifiserer filformatet til den kjørbare filen. Her er det kjørbare filformatet elf32 og endianness er lite endian. OUTPUT_ARCH i linje #33 spesifiserer arkitekturen som denne koden kjører på. ENTRY i linje #34 spesifiserer startfunksjonen (inngangspunktet) til u-boot-programmet. Her er inngangspunktet _start.

SECTIONS i linje #35 definerer hvordan ulike seksjoner er kartlagt i den kjørbare filen. Loader bruker adressene som er spesifisert i denne delen for å laste forskjellige deler av programmet inn i minnet.

‘.’ i linje #37 spesifiserer startadressen der følgende seksjoner skal lastes. I dette tilfellet er startadressen 0x00000000. Etter dette i linje #39 er minnet justert med 4 byte og tekstdelen følger i linje #40.

40.tekst:

41 {

42 cpu / arm920t / start.o (.tekst)

43 * (. Tekst)

44}

På ‘.’ posisjon (0x00000000) koden i cpu / arm920t / start.o er kartlagt og følger koden som er der i.text-seksjoner i alle andre objektfiler (.o). cpu / arm920t / start.o inneholder funksjonen _start () (på assemblerspråk) som er inngangspunktet til dette programmet.

Nå ‘.’ vil være på 0x00000000 + størrelsen på (.tekst). Igjen er minnet justert med 4 byte og.rodata-seksjonen følger i linje #47.

. = ALIGN (4);

47.dato: {* (SORT_BY_ALIGNMENT (SORT_BY_NAME (.rodata *)))}

.rodata-seksjoner fra alle objektfiler er kartlagt på denne adressen. Følger.data og.git-seksjonene.

49. = OPPRETT (4);

50.data: {* (. Data)}

51

52. = OPPRETT (4);

53.got: {* (. Got)}

Hver U-boot-kommando er et objekt av typen ‘cmd_tbl_t’ som inneholder kommandonavn, hjelpestreng og funksjonspeker som skal utføres når denne kommandoen kjøres. Alle disse kommandoobjektene plasseres i minnet sekvensielt. Hvert av disse kommandoobjektene er innebygd i en U-boot-definert seksjon kalt.u_boot_cmd i objektfilen. Disse all.u_boot_cmd-seksjonene er plassert i minnet etter seksjonene ovenfor (.data and.git).

. = .;

56 __u_boot_cmd_start = .;

57.u_boot_cmd: {* (. U_boot_cmd)}

58 __u_boot_cmd_end = .;

__u_boot_cmd_start inneholder starten på kommandoobjektene og __u_boot_cmd_end inneholder slutten av kommandoobjektene.

Og neste følger.bss (uinitialiserte globale variabler)-seksjoner.

60. = OPPRETT (4);

61 __bss_start = .;

62.bss (NOLOAD): {* (. Bss). = ALIGN (4); }

63 _end = .;

__bss_start peker på.bss-startadressen og _end inneholder slutten av alle seksjoner.

Ved å bruke denne linker script linker vil generere en kjørbar fil kalt u-boot. Objcopy-verktøyet brukes til å generere en binær fil fra den kjørbare filen u-boot.

u-boot.bin: u-boot

$ (OBJCOPY) $ {OBJCFLAGS} -O binær $

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.