Alpine opstartproces op de Raspberry Pi
Vandaag zullen we het opstartproces van Alpine Linux op een Raspberry Pi eens in detail bekijken.
De afbeelding toont de inhoud van een "maagdelijke" SD-kaart met de Alpine image, die nog niet is opgestart.
Deze foto toont de inhoud van de opstartmap.
Eerste opstartfasen op de Raspberry Pi
Raadpleeg deze link voor een uitgebreide handleiding over verschillende opstartmedia:
https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md
De Raspberry Pi heeft geen klassiek "BIOS" zoals in x86-gebaseerde IBM PC-compatibele computers. De CPU moet worden gezien als een toevoeging aan de VideoCore IV voor opstartdoeleinden, in plaats van andersom.
Dus, in eerste instantie heeft de VideoCore IV, de GPU, de controle. Hij start vanuit zijn eigen ROM (eerste stadium), en controleert de boot bronnen voor een bestand genaamd bootcode.bin. Voor onze doeleinden zullen we aannemen dat dit bestand van de SD-kaart wordt geladen.
bootcode.bin (51 KB) wordt in de lokale 128K L2 cache van de GPU geladen. Het schakelt RAM in en laadt de derde trap: start.elf (2759 KB)
start.elf leest config.txt, en stelt de juiste instellingen in voor de VideoCore IV. config.txt is een parameter bestand voor de Raspberry Pi waar je bijv. de video mode, overscan, audio opties, MPEG codecs, overklok instellingen, etc. kunt instellen. - Het is "zoals" het BIOS instellingen gedeelte van de traditionele computer. start.elf zelf is de firmware voor de GPU, dat zijn eigen besturingssysteem is, genaamd "VideoCore OS".
NB: Er zijn verschillende versies van start.elf, waaronder start_x.elf die cameradrivers bevat, en start_cd.elf - dat is een verkorte versie van start.elf, om te gebruiken wanneer het GPU-geheugen beperkt is tot het maximum: gpu_mem=16. Deze laatste versie kan interessant zijn voor embedded systemen. Raadpleeg deze site voor meer informatie: https://www.raspberrypi.org/documentation/configuration/boot_folder.md
config.txt is optioneel. Als u dezelfde SD-kaart (of image) voor meerdere Pi's gebruikt, kunt u voorwaardelijke filters instellen om op bepaalde scenario's toe te passen; u kunt zelfs een voorwaarde toepassen op basis van de serie van de Raspberry Pi, of op de toestand van een GPIO. Er is ook de mogelijkheid om bijkomende bestanden in de config.txt op te nemen. Alles bij elkaar krijg je een vrij krachtig systeem voor de basisconfiguratie van de Raspberry Pi.
Natuurlijk zijn er ook opstartopties in config.txt, die bepalen hoe het verdere opstarten verloopt.
start.elf laadt dan de kernel (zoals gespecificeerd door config.txt / defaults), en start deze met cmdline.txt als kernel commandoregel .
config.txt opstart opties
kijk eens aan:
https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md
om meer te lezen over de beschikbare opstartopties.
Alpine's standaard config.txt
Laten we eens kijken naar de config.txt die geleverd wordt met Alpine's 3.8.1 ARMHF release:
- disable_splash=1
- dit zal het regenboogscherm bij het opstarten uitschakelen (puur cosmetisch)
- boot_delay=0
- wacht niet in start.elf alvorens de kernel te laden. (een opstartvertraging kan nodig zijn voor sommige SD-kaarten om "klaar te komen")
- gpu_mem=256
- bepaalt de verdeling van het geheugen tussen GPU en CPU. Het geheugen van de Pi wordt gedeeld. Hier stellen we 256 MB in voor de GPU, u zou dit kunnen verminderen op embedded systemen die geen displays aansturen.
- gpu_mem_256=64
- stelt de verdeling van het geheugen in voor Pi's met slechts 256 MB totaal geheugen (alleen sommige oude generatie Pi 1B's)
- [pi0]
- voorwaardelijke verklaring voor Pi Zero; deze omvat Pi Zero en Pi Zero W
- alle statemens tot de volgende voorwaardelijke worden alleen toegepast als we draaien op een Pi Zero / Zero W
- de Pi Zero / Zero W gebruiken nog steeds een oudere ARMv6 ISA CPU (ARM11 @ 1 GHz)
- we zullen een kernel gebruiken die gecompileerd is voor ARMv6
- kernel=boot/vmlinuz-rpi
- dit specificeert de kernel om te laden
- dit moet een ongecomprimeerd kernel image bestand zijn
- 32 bit kernels worden standaard geladen op adres 0x8000
- initramfs boot/initramfs-rpi
- "het initramfs commando specificeert zowel de ramfs bestandsnaam als het geheugenadres waar het geladen moet worden"
- initramfs = initieel RAM-bestandssysteem
- dit bestand kan worden uitgepakt met 7Zip, bijvoorbeeld (twee keer uitpakken)
- OPMERKING: deze configuratieparameter wordt gespecificeerd zonder de "="
- [pi1]
- dit zal de volgende configuratie regels toepassen op alle Raspberry Pi 1 - zal niet overeenkomen op Pi 2, enz.
- kernel=boot/vmlinuz-rpi
- initramfs boot/initramfs-rpi
- [pi2]
- kernel=boot/vmlinuz-rpi2
- Merk op hoe we een andere kernel gebruiken, aangezien we hier een CPU hebben die ARMv7 code kan draaien
- initramfs boot/initramfs-rpi2
- en een andere initramfs ...
- [pi3]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfs-rpi2
- [pi3+]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfs-rpi2
- [alle]
- de volgende regel(s) zal weer van toepassing zijn op alle Pi's
- usercfg.txt opnemen
- zal een bestand usercfg.txt opnemen in de configuratie, dat nog niet bestaat in het standaard Alpine image.
- Hier kunt u extra instellingen opgeven die u wilt, bijv. HDMI-resolutie, enz.
- Als alternatief zou je het hieronder in config.txt kunnen plakken
Opmerking: de configuratie die in Alpine gebruikt wordt, specificeert geen tweede parameter voor de initramfs (adres waarop de initramfs gemount moet worden); het is dus niet aan te raden om dit 1:1 te kopiëren. Dit gezegd zijnde, lijkt het te werken, waarschijnlijk met een veilige standaardinstelling!
Lees meer over config.txt:
- https://www.raspberrypi.org/documentation/configuration/config-txt/README.md
- https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md
- https://www.raspberrypi.org/documentation/configuration/config-txt/conditional.md
- https://www.raspberrypi.org/documentation/configuration/config-txt/misc.md
- https://elinux.org/RPiconfig
- https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=10532
- https://raspberrypi.stackexchange.com/questions/49980/raspbian-kernel-and-initramfs
De Kernel en de Initramfs
De kernel
Afhankelijk van de config.txt instellingen, wordt de juiste kernel geselecteerd en in RAM geladen. Zodra deze geladen is, wordt/worden de ARM processorkern(en) vrijgegeven van reset zodat deze de kernel kan booten.
De inhoud van cmdline.txt wordt doorgegeven als kernel commandoregel. start.elf geeft ook zelf aanvullende parameters door, bijvoorbeeld voor het instellen van DMA kanalen, het MAC adres van de SMSC LAN chip, etc.
Krijg de cmdline.txt:
cat /proc/cmdline
dmesg | grep "Command line"
cmdline.txt
laten we eens kijken naar Alpine's standaard cmdline.txt:
modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=tty1
Dit laadt de modules
- lus
- het loop device wordt gebruikt om een bestand als een bestandssysteem te mounten
- squashfs
- gecomprimeerd lees-ony bestandssysteem voor Linux
- vooral geschikt voor ingebedde systemen
- https://en.wikipedia.org/wiki/SquashFS
- dit wordt later gebruikt voor modloop
- sd-mod
- SCSI-schijfstuurprogramma / blokstuurprogramma
- blijkbaar gekopieerd van Alpine standaard configuratie, b.v. hier: https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
- usb-opslag
- Linux USB-stuurprogramma voor massaopslag
NB: modulenamen kunnen zowel - als _ bevatten, deze symbolen kunnen blijkbaar worden verwisseld.
rustig
- stelt het standaard kernel-logniveau in op KERN_WARNING, wat alle behalve zeer ernstige logberichten onderdrukt tijdens het opstarten (bron: Raspberry Pi-documentatie)
dwc_otg.lpm_enable=0
- Het USB-stuurprogramma schakelt met deze instelling het energiebeheer van de USB-link uit. (Dit is de aanbevolen standaardinstelling)
- https://elinux.org/RPI_BCM2708_Parameters#module:_sdhci-bcm2708
console=tty1
- definieert de seriële console
Verder lezen:
- https://elinux.org/RPi_cmdline.txt
- https://www.raspberrypi.org/documentation/configuration/cmdline-txt.md
initramfs
" Het initramfs commando specificeert zowel de ramfs bestandsnaam als het geheugenadres waarnaar het geladen moet worden. Het voert de acties uit van zowel ramfsfile als ramfsaddr in één parameter. Het adres kan ook followkernel (of 0) zijn om het in het geheugen te plaatsen na de kernel image. Voorbeeldwaarden zijn: initramfs initramf.gz 0x00800000 of initramfs init.gz followkernel. OPMERKING: Deze optie gebruikt een andere syntaxis dan alle andere opties, en u moet hier geen = teken gebruiken." - Raspberry Pi Documentatie
Er zal een kernelboodschap verschijnen zoals deze "[ 0.143709] Trying to unpack rootfs image as initramfs..." in dmesg, die aantoont dat de image uitgepakt wordt.
De initramfs stelt de Linux kernel in staat om modules toe te voegen, en andere taken uit te voeren ter voorbereiding van het mounten van het eigenlijke bestandssysteem. Het zorgt dus voor een zeer veelzijdig boot-systeem, zonder de kernel opnieuw te hoeven compileren of overhead aan de kernel toe te voegen. Bijvoorbeeld voor gebruikers met een versleuteld bestandssysteem, zal initramfs om de passphrase vragen voordat het het bestandssysteem kan ontsleutelen en mounten.
Afhankelijk van de CPU in uw Raspberry Pi, zullen verschillende initramfs-bestanden worden gebruikt, zoals ingesteld in config.txt:
- boot/initramfs-rpi voor Pi 1 / 1B+ en Pi Zero / Zero W, en Compute Module 1
- boot/initramfs-rpi2 voor alle andere Pi's (2, 3B, 3B+, ...)
Het initramfs bestand kan worden uitgepakt met, bijvoorbeeld, Windows 7Zip. (Je moet het twee keer uitpakken: het eerste bestand bevat een gzipped tweede bestand)
initramfs images kunnen verschillende formaten hebben, afhankelijk van welke algoritmen statisch in de kernel werden gecompileerd (b.v. gzip / bzip2 / LZMA / XZ / LZO / LZ4).
Dit tweede bestand bevat een map- en bestandsstructuur, die u zult herkennen als u Linux eerder hebt gebruikt. Het is in een speciaal formaat, "cpio", wat lijkt op teer.
Als u nieuwsgierig bent, kunt u hier meer lezen over cpio:
https://en.wikipedia.org/wiki/Cpio
Dit is wat er in het bestand staat:
in bin, het bevat:
- busybox
- compact uitvoerbaar bestand met verschillende UNIX-hulpprogramma's in één toepassing
- vooral voor ingebedde systemen
- https://busybox.net/about.html
- sh
in sbin, het bevat
- apk
- Alpine-pakketbeheerprogramma
- bootchartd
- dit is een hulpmiddel voor het profileren van de opstarttijd
- https://elinux.org/Bootchart
- http://www.bootchart.org/
- nlplug-findfs
- wordt gebruikt om block devices te mounten en ze te doorzoeken op beschikbare apkovl's, als geen apkovl= is doorgegeven in de kernel commandoregel
- https://github.com/alpinelinux/mkinitfs
de init bestand wordt door de Linux-kernel gestart als eerste procesen zet Alpine op.
Hier is het gereedschap dat gebruikt wordt om de initramfs voor Alpine te genereren:
https://github.com/alpinelinux/mkinitfs
init
dit is een opeenvolgende uittreksel van wat het init-script, gestart vanuit de initramfs, doet:
- maakt recursief de mappen /usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot , /media/cdrom, /media/usb, /tmp, /run aan
- sommige van deze directories lijken al aanwezig te zijn in de initramfs
- installeert busybox (zet symlinks op naar busybox' embedded commands)
- stelt PATH in op /usr/bin:/bin:/usr/sbin:/sbin
- creëert /dev/null met mknod
- mounten proc naar /proc
- proc is een pseudo-bestandssysteem met procesinformatie
- u kunt informatie over de kernel verkrijgen door te lezen en bepaalde dingen te configureren door naar sommige bestanden in deze directory te schrijven
- de genummerde ingangen zijn mappen met informatie over het proces met dit specifieke proces-id
- https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
- ... en sysfs naar /sys
- dit is informatie over hardware-apparaten, apparaatstuurprogramma's en kernelsubsystemen
- https://en.wikipedia.org/wiki/Sysfs
- de via cmdline.txt doorgegeven opties parseert, worden de volgende herkend:
- alpine_dev autodetect autoraid grafiek cryptroot cryptdm cryptheader cryptoffset
- cryptdiscards cryptkey debug_init dma init_args keep_apk_new modules ovl_dev
- pkgs quiet root_size root usbdelay ip alpine_repo apkovl alpine_start splash
- blacklist overlaytmpfs rootfstype rootflags nbd resume s390x_net dasd ssh_key
Tip: probeer "rustig" in cmdline.txt naar "noquiet"om een meer verbose boot te krijgen, en veel berichten van init te zien
- mounten devtmpfs naar /dev
- laadt drivers om modloop later te kunnen mounten
- indien de parameter nbd aanwezig en geconfigureerd is (netwerk block device), probeert dan een ip adres op te halen, en het netwerk block device te configureren met nbd-client
- als de wortelparameter is ingesteld
- if [ -n "$KOPT_root" ]; then
- –n evalueert als de lengte van "$KOPT_root" niet nul is
- dan wordt nlplug-findfs uitgevoerd
- overlaytmpfs wordt optioneel behandeld
- de huidige mount punten worden gemigreerd naar /sysroot
- switch_root wordt uitgevoerd en de controle wordt doorgegeven aan /sbin/init
- (dit is een startpunt om Alpine Linux te installeren in een normale modus, niet in alleen-lezen modus)
anders:
- nlplug-findfs wordt uitgevoerd om het opstartmedium te vinden,
- crypto-opties zijn mogelijk en worden hier verwerkt
- gevonden apkovls' paden worden toegevoegd aan /tmp/apkovl (met behulp van de -a schakelaar, zie https://pi3g.com/2019/01/09/nlplug-findfs-documentation/)
- blijkbaar wordt het bestand alleen aangemaakt als er apkovl's worden gevonden.
- https://github.com/alpinelinux/mkinitfs/blob/master/nlplug-findfs.c
- apkovls zijn gematched op zoek naar "*.apkovl.tar.gz*", dat is waarom backup bestanden die lbu commit aanmaakt niet automatisch gevonden / toegepast - zij hebben een structuur van hostname.timestamp.tar.gz, b.v. pidoctor.20190110180745.tar.gz
- als de parameter apkovl is ingesteld,
- en het is leeg - en /tmp/apkovls bestaat (dat wil zeggen, bestanden werden gevonden door nlplug-findfs) - de ovl is ingesteld uit de eerste regel van /tmp/apkovls
- en het begint met http:// / https:// / ftp:// - een netwerkverbinding wordt geprobeerd om de apkovl te trekken
- anders wordt de ovl ingesteld op de string in de apkovl optie
- als de apkovl bestaat,
- en is een .gz bestand, de apkovl is uitgepakt tar -C "$dest" -zxvf "$ovl" > $ovlfiles en /tmp/ovlfiles is gevuld met de uitvoer van tar
- er is code voor een splash screen, fbsplash is een van de functies die is opgenomen in busybox - het toont fbsplash*.ppm bestanden
- if [ -z "$ALPINE_REPO" ] -> betekent, als de string $ALPINE_REPO leeg is
- onder sommige omstandigheden worden standaard bootservices toegevoegd, voor de runlevels sysinit, boot, shutdown en default (b.v. de service firstboot)
- apk repositories zijn toegevoegd en uitgebreid
- het pakket alpine-base hangt af van alpine-baselayout, alpine-conf en enkele andere pakketten
- het bevat ook het bestand /etc/os-release -> dat de naam, versie, en mooie naam van Alpine Linu weergeeft
- alpine-baselayout is de basismap lay-out
- alpine-conf bevat, onder andere, het lbu script en setup scripts
- busybox bevat de busybox binary
- busybox-initscripts bevat enkele initscripts
- tenslotte, wordt de root directory omgeschakeld, en /sbin/init wordt gestart
- dit zal OpenRC starten en de runlevels uitvoeren
- /sbin/init is een symlink naar /bin/busybox
Let op: De apkovl wordt gemount vóór modloop en vóór de installatie van de pakketten. Hierdoor kan de apkovl onder andere configureren welke pakketten geïnstalleerd gaan worden.
OpenRC Runlevels
sysinit -> boot -> default -> shutdown
https://hoverbear.org/2014/09/29/init-runlevels-and-targets/
Runlevel sysinit:
modloop zal gestart worden na dev-mount, en voor checkfs, fsck, hwdriver, modules, hwclock, dev, sysfs
hwdrivers zal gestart worden na modloop. hwdrivers zal de drivers laden met modprobe.
Runlevel opstarten:
Runlevel standaard:
Modloop
Er is ook het modloop-bestand (modloop-rpi of modloop-rpi2).
hoe het gemonteerd is
Het juiste bestand voor uw systeem wordt bepaald in het script /etc/init.d/modloop in de functie find_modloop()
Het resultaat van uname -r wordt gecontroleerd met de directory modules/ - de subdirectory met de juiste naam moet er in staan.
Bijvoorbeeld: 4.14.69-0-rpi2
Op deze manier worden de juiste modules voor uw kernel gebruikt (uname -r drukt de kernel release af).
modloop is aangekoppeld in de directory /.modloop
Symlinks worden gemaakt van /lib/firmware naar /lib/modules/firmwareen /lib/modules is gesymlinked naar /.modloop/modules
Hint: Als je de Raspberry Pi 3B+ WiFi firmware wilt patchen, moet je het in modloop.
Modloop's inhoud
Om de inhoud van modloop te verkennen, kun je gebruik maken van (getest op een mint systeem):
apt-get install squashfs-tools
unsquashfs modloop-rpi2
Het bevat firmware en drivers.
Referenties:
- https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md
- https://raspberrypi.stackexchange.com/questions/10442/what-is-the-boot-sequence
- https://wiki.alpinelinux.org/wiki/Alpine_Linux:Overview
- https://pi-ltsp.net/advanced/kernels.html
- https://www.raspberrypi.org/documentation/configuration/boot_folder.md
- https://wiki.beyondlogic.org/index.php?title=Understanding_RaspberryPi_Boot_Process
- https://thekandyancode.wordpress.com/2013/09/21/how-the-raspberry-pi-boots-up/