Alpine Bootvorgang auf dem Raspberry Pi
Heute wollen wir uns den Alpine Linux-Bootprozess auf einem Raspberry Pi im Detail ansehen.
Das Bild zeigt den Inhalt einer "jungfräulichen" SD-Karte mit dem Alpine-Image, die noch nicht gebootet wurde.
Dieses Bild zeigt den Inhalt des Boot-Ordners.
Erste Bootphasen auf dem Raspberry Pi
Unter diesem Link finden Sie eine ausführliche Anleitung zu den verschiedenen Bootmedien:
https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md
Der Raspberry Pi verfügt nicht über ein klassisches "BIOS" wie bei x86-basierten IBM-PC-kompatiblen Computern. Seine CPU sollte als Zusatz zum VideoCore IV für Boot-Zwecke betrachtet werden und nicht umgekehrt.
So hat zunächst der VideoCore IV, die GPU, die Kontrolle. Er startet aus seinem eigenen ROM (erste Stufe) und sucht in den Boot-Quellen nach einer Datei namens bootcode.bin. Für unsere Zwecke gehen wir davon aus, dass diese Datei von der SD-Karte geladen wird.
bootcode.bin (51 KB) wird in den lokalen 128K L2-Cache der GPU geladen. Er aktiviert den RAM und lädt die dritte Stufe: start.elf (2759 KB)
start.elf liest config.txt und richtet die entsprechenden Einstellungen für den VideoCore IV ein. config.txt ist eine Parameterdatei für den Raspberry Pi, in der Sie z.B. den Videomodus, Overscan, Audiooptionen, MPEG-Codecs, Übertaktungseinstellungen usw. einstellen können. - Sie ist "wie" der BIOS-Einstellungsteil eines herkömmlichen Computers. start.elf selbst ist die Firmware für die GPU, die ein eigenes Betriebssystem namens "VideoCore OS" ist.
NB: Es gibt verschiedene Versionen von start.elf, darunter start_x.elf, die Kameratreiber enthält, und start_cd.elf - eine abgespeckte Version von start.elf, die zu verwenden ist, wenn der GPU-Speicher auf das Maximum reduziert ist: gpu_mem=16. Diese letztere Version könnte für eingebettete Systeme interessant sein. Weitere Informationen finden Sie auf dieser Website: https://www.raspberrypi.org/documentation/configuration/boot_folder.md
config.txt ist optional. Wenn Sie dieselbe SD-Karte (oder dasselbe Image) für mehrere Pi's verwenden, können Sie bedingte Filter festlegen, die auf bestimmte Szenarien angewendet werden; Sie können sogar eine Bedingung basierend auf der Seriennummer des Raspberry Pi anwenden, oder auf den Zustand eines GPIO. Es besteht auch die Möglichkeit, zusätzliche Dateien in die config.txt aufzunehmen. Zusammengenommen erhalten Sie ein recht leistungsfähiges System für die Grundkonfiguration des Raspberry Pi.
Natürlich gibt es auch Boot-Optionen in der config.txt, die den weiteren Verlauf des Bootvorgangs steuern.
start.elf lädt dann den Kernel (wie in config.txt / defaults angegeben) und startet ihn mit cmdline.txt als Kernel-Kommandozeile.
config.txt Boot-Optionen
schauen Sie sich das an:
https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md
um mehr über die verfügbaren Boot-Optionen zu erfahren.
Die Standard-Config.txt von Alpine
Werfen wir einen Blick auf die config.txt, die mit der Version 3.8.1 ARMHF von Alpine ausgeliefert wird:
- disable_splash=1
- dies deaktiviert den Regenbogenbildschirm beim Start (rein kosmetisch)
- boot_delay=0
- warten Sie nicht in start.elf, bevor Sie den Kernel laden. (eine Boot-Verzögerung könnte für einige SD-Karten notwendig sein, um "bereit" zu werden)
- gpu_mem=256
- legt die Aufteilung des Speichers zwischen GPU und CPU fest. Der Speicher des Pi wird gemeinsam genutzt. Hier stellen wir 256 MB für die GPU ein. Bei eingebetteten Systemen, die keine Bildschirme ansteuern, sollten Sie diesen Wert möglicherweise verringern.
- gpu_mem_256=64
- legt die Aufteilung des Speichers für Pi's mit nur 256 MB Gesamtspeicher fest (nur einige Pi 1B's der alten Generation)
- [pi0]
- bedingte Anweisung für Pi Zero; dies gilt für Pi Zero und Pi Zero W
- alle Angaben bis zur nächsten Bedingung werden nur angewendet, wenn wir mit einem Pi Zero / Zero W arbeiten
- der Pi Zero / Zero W verwendet noch eine ältere ARMv6 ISA CPU (ARM11 @ 1 GHz)
- wir werden einen für ARMv6 kompilierten Kernel verwenden
- kernel=boot/vmlinuz-rpi
- dies gibt den zu ladenden Kernel an
- dies muss eine unkomprimierte Kernel-Image-Datei sein
- 32-Bit-Kernel werden standardmäßig an die Adresse 0x8000 geladen
- initramfs boot/initramfss-rpi
- "Der initramfs-Befehl gibt sowohl den ramfs-Dateinamen als auch die Speicheradresse an, in die er geladen werden soll"
- initramfs = initiales RAM-Dateisystem
- diese Datei kann z.B. mit 7Zip extrahiert werden (zweimal extrahieren)
- HINWEIS: Dieser Konfigurationsparameter wird ohne das "=" angegeben.
- [pi1]
- dies wird die folgenden Konfigurationszeilen auf alle Raspberry Pi 1 anwenden - wird nicht auf Pi 2 usw. passen.
- kernel=boot/vmlinuz-rpi
- initramfs boot/initramfss-rpi
- [pi2]
- kernel=boot/vmlinuz-rpi2
- Beachten Sie, dass wir einen anderen Kernel verwenden, da wir hier eine CPU haben, die ARMv7-Code ausführen kann
- initramfs boot/initramfss-rpi2
- und ein anderes initramfs ...
- [pi3]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfss-rpi2
- [pi3+]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfss-rpi2
- [alle]
- die folgende(n) Zeile(n) gilt/gelten wieder für alle Pi's
- usercfg.txt einschließen
- wird eine Datei usercfg.txt in die Konfiguration aufgenommen, die im Standard-Alpine-Image noch nicht vorhanden ist.
- Hier können Sie zusätzliche benutzerdefinierte Einstellungen vornehmen, z. B. die HDMI-Auflösung usw.
- Alternativ können Sie es auch einfach unten in die config.txt einfügen
Hinweis: Die in Alpine verwendete Konfiguration gibt keinen zweiten Parameter für das initramfs an (Adresse, unter der das initramfs gemountet werden soll); es ist also nicht ratsam, dies 1:1 zu kopieren. Abgesehen davon scheint es zu funktionieren, wahrscheinlich ist es eine sichere Standardeinstellung!
Lesen Sie mehr über 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
Der Kernel und das Initramfs
Der Kernel
Je nach den Einstellungen in der config.txt wird der richtige Kernel ausgewählt und in den RAM geladen. Sobald er geladen ist, werden die ARM-Prozessorkerne vom Reset befreit, damit sie den Kernel booten können.
Ihm wird der Inhalt von cmdline.txt als Kernel-Befehlszeile übergeben. start.elf übergibt auch zusätzliche eigene Parameter, z.B. die Einstellung der DMA-Kanäle, die MAC-Adresse des SMSC-LAN-Chips, usw.
Holen Sie sich die cmdline.txt:
cat /proc/cmdline
dmesg | grep "Befehlszeile"
cmdline.txt
Werfen wir einen Blick auf die standardmäßige cmdline.txt von Alpine:
modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=tty1
Dadurch werden die Module
- Schleife
- das Loop-Gerät wird verwendet, um eine Datei als Dateisystem einzuhängen
- squashfs
- komprimiertes read-ony-Dateisystem für Linux
- besonders geeignet für eingebettete Systeme
- https://en.wikipedia.org/wiki/SquashFS
- dies wird später für modloop verwendet
- sd-mod
- SCSI-Plattentreiber / Blocktreiber
- offenbar von der Alpine-Standardkonfiguration kopiert, z.B. hier: https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
- usb-speicher
- Linux USB-Massenspeicher-Treiber
NB: Modulnamen können sowohl - als auch _ enthalten, diese Symbole können offensichtlich ausgetauscht werden.
leise
- setzt den Standard-Kernel-Log-Level auf KERN_WARNING, was alle außer sehr schwerwiegenden Log-Meldungen während des Bootens unterdrückt (Quelle: Raspberry Pi Dokumentation)
dwc_otg.lpm_enable=0
- Mit dieser Einstellung deaktiviert der USB-Controller-Treiber die Energieverwaltung für die USB-Verbindung. (Dies ist die empfohlene Standardeinstellung)
- https://elinux.org/RPI_BCM2708_Parameters#module:_sdhci-bcm2708
Konsole=tty1
- definiert die serielle Konsole
Lesen Sie weiter:
- https://elinux.org/RPi_cmdline.txt
- https://www.raspberrypi.org/documentation/configuration/cmdline-txt.md
initramfs
" Der initramfs-Befehl gibt sowohl den ramfs-Dateinamen als auch die Speicheradresse an, in die er geladen werden soll. Er führt die Aktionen von ramfsfile und ramfsaddr in einem Parameter aus. Die Adresse kann auch followkernel (oder 0) sein, um sie nach dem Kernel-Image im Speicher zu platzieren. Beispielwerte sind: initramfs initramf.gz 0x00800000 oder initramfs init.gz followkernel. HINWEIS: Diese Option verwendet eine andere Syntax als alle anderen Optionen, und Sie sollten hier kein =-Zeichen verwenden." - Raspberry Pi Dokumentation
Es wird eine Kernelmeldung wie diese "[ 0.143709] Trying to unpack rootfs image as initramfs..." in dmesg erscheinen, die anzeigt, dass das Image entpackt wird.
Das initramfs ermöglicht es dem Linux-Kernel, Module hinzuzufügen und andere Aufgaben zur Vorbereitung des Einhängens des eigentlichen Dateisystems auszuführen. Damit ist es ein sehr vielseitiges Boot-System, ohne dass der Kernel neu kompiliert werden muss bzw. zusätzlicher Overhead für den Kernel entsteht. Für Benutzer mit einem verschlüsselten Dateisystem fragt initramfs zum Beispiel nach der Passphrase, bevor es die Dateisysteme entschlüsseln und einhängen kann.
Abhängig von der CPU in Ihrem Raspberry Pi werden unterschiedliche initramfs-Dateien verwendet, wie sie in config.txt:
- boot/initramfs-rpi für Pi 1 / 1B+ und Pi Zero / Zero W, und Compute Module 1
- boot/initramfs-rpi2 für alle anderen Pi's (2, 3B, 3B+, ...)
Die initramfs-Datei kann z. B. mit Windows 7Zip extrahiert werden. (Sie müssen die Datei zweimal entpacken: die erste Datei enthält eine zweite gzipped-Datei)
initramfs-Images können in verschiedenen Formaten vorliegen, je nachdem, welche Algorithmen statisch in den Kernel kompiliert wurden (z. B. gzip / bzip2 / LZMA / XZ / LZO / LZ4).
Diese zweite Datei enthält eine Ordner- und Dateistruktur, die Sie wiedererkennen werden, wenn Sie schon einmal mit Linux gearbeitet haben. Sie ist in einem speziellen Format, "cpio", was ähnlich wie Teer ist.
Wenn Sie neugierig sind, können Sie hier mehr über cpio lesen:
https://en.wikipedia.org/wiki/Cpio
Dies ist der Inhalt der Datei:
in bin, enthält es:
- busybox
- kompakte ausführbare Datei, die mehrere UNIX-Dienstprogramme in einer Anwendung enthält
- speziell für eingebettete Systeme
- https://busybox.net/about.html
- sh
in sbin, sie enthält
- apk
- Alpine-Paketverwaltungstool
- bootchartd
- ein Tool zur Erstellung von Profilen für die Startzeit
- https://elinux.org/Bootchart
- http://www.bootchart.org/
- nlplug-findfs
- wird verwendet, um Blockgeräte einzuhängen und sie nach verfügbaren apkovl's zu durchsuchen, wenn in der Kernel-Befehlszeile kein apkovl= übergeben wird
- https://github.com/alpinelinux/mkinitfs
die init Datei wird vom Linux-Kernel gestartet als erster Prozessund richtet im Grunde genommen Alpine ein.
Hier finden Sie das Tool, mit dem Sie das initramfs für Alpine erstellen können:
https://github.com/alpinelinux/mkinitfs
init
dies ist eine sequenzielle Auszug von dem, was das aus dem initramfs gestartete Init-Skript tut:
- erstellt Verzeichnisse /usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot , /media/cdrom, /media/usb, /tmp, /run rekursiv
- einige dieser Verzeichnisse scheinen bereits im initramfs vorhanden zu sein
- installiert busybox (richtet Symlinks zu den eingebetteten Befehlen von busybox ein)
- richtet PATH auf /usr/bin:/bin:/usr/sbin:/sbin ein
- erstellt /dev/null mit mknod
- mountet proc nach /proc
- proc ist ein Pseudodateisystem für Prozessinformationen
- Sie können Informationen über den Kernel erhalten, indem Sie bestimmte Dinge lesen und konfigurieren, indem Sie in einige Dateien in diesem Verzeichnis schreiben
- die nummerierten Einträge sind Verzeichnisse, die Informationen über den Prozess mit dieser bestimmten Prozess-ID enthalten
- https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
- ... und sysfs nach /sys
- dies sind Informationen über Hardware-Geräte, Gerätetreiber und Kernel-Subsysteme
- https://en.wikipedia.org/wiki/Sysfs
- die über cmdline.txt übergebenen Optionen analysiert, werden die folgenden erkannt:
- alpine_dev autodetect autoraid Tabelle 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
- schwarze Liste overlaytmpfs rootfstype rootflags nbd resume s390x_net dasd ssh_key
Tipp: Versuchen Sie, "leise" in cmdline.txt zu "noquiet", um einen ausführlichen Bootvorgang zu erhalten und viele Meldungen von init zu sehen
- mountet devtmpfs nach /dev
- lädt Treiber, um modloop später einbinden zu können
- wenn der Parameter nbd vorhanden und konfiguriert ist (network block device), versucht, eine IP-Adresse zu ermitteln und das network block device mit nbd-client zu konfigurieren
- wenn der Parameter root gesetzt ist
- if [ -n "$KOPT_root" ]; then
- –n wird ausgewertet, wenn die Länge von "$KOPT_root" ungleich Null ist
- dann wird nlplug-findfs ausgeführt
- overlaytmpfs wird optional behandelt
- die aktuellen Einhängepunkte werden nach /sysroot migriert
- switch_root wird ausgeführt und die Kontrolle wird an /sbin/init übergeben
- (dies ist ein Einstiegspunkt für die Installation von Alpine Linux in einem regulären Modus, nicht im Nur-Lese-Modus)
sonst:
- nlplug-findfs wird ausgeführt, um das Bootmedium zu finden,
- Krypto-Optionen sind hier möglich und werden verarbeitet
- gefundenen apkovls' Pfade werden hinzugefügt zu /tmp/apkovl (unter Verwendung des Schalters -a, siehe https://pi3g.com/2019/01/09/nlplug-findfs-documentation/)
- Offenbar wird die Datei nur erstellt, wenn apkovl's gefunden werden.
- https://github.com/alpinelinux/mkinitfs/blob/master/nlplug-findfs.c
- apkovls sind auf der Suche nach "*.apkovl.tar.gz*", Deshalb sind die von lbu commit erstellten Sicherungsdateien nicht automatisch gefunden/angewendet werden - sie haben eine Struktur von hostname.zeitstempel.tar.gz, z.B. pidoctor.20190110180745.tar.gz
- wenn der Parameter apkovl eingestellt ist,
- und sie ist leer - und /tmp/apkovls existiert (d.h. die Dateien wurden von nlplug-findfs gefunden) - das ovl ist gesetzt aus der ersten Zeile von /tmp/apkovls
- und es beginnt mit http:// / https:// / ftp:// - eine Netzwerkverbindung wird versucht, das apkovl zu ziehen
- andernfalls wird die ovl auf die Zeichenkette in der apkovl-Option gesetzt
- wenn das apkovl existiert,
- und eine .gz-Datei ist, wird das apkovl entpackt tar -C "$dest" -zxvf "$ovl" > $ovlfiles und /tmp/ovlfiles wird mit der Ausgabe von tar aufgefüllt
- es gibt Code für einen Splash-Screen, fbsplash ist eine der Funktionen, die in busybox enthalten sind - sie zeigt fbsplash*.ppm-Dateien an
- if [ -z "$ALPINE_REPO" ] -> bedeutet, wenn die Zeichenfolge $ALPINE_REPO leer ist
- unter Umständen werden Standard-Bootsdienste hinzugefügt, für die Runlevel sysinit, boot, shutdown und default (z.B. der Dienst firstboot)
- apk-Repositories werden hinzugefügt und erweitert
- das Paket alpine-base hängt von alpine-baselayout, alpine-conf und einigen anderen Paketen ab
- es enthält auch die Datei /etc/os-release -> die den Namen, die Version und den hübschen Namen von Alpine Linu enthält
- alpine-baselayout ist das Basisverzeichnislayout
- alpine-conf enthält unter anderem das lbu-Skript und die Setup-Skripte
- busybox enthält die busybox-Binärdatei
- busybox-initscripts enthält einige Initscripts
- schließlich wird das Stammverzeichnis gewechselt und /sbin/init gestartet
- dies startet OpenRC und führt die Runlevels aus
- /sbin/init ist ein Symlink zu /bin/busybox
Bitte beachten Sie: Das apkovl wird vor modloop und vor der Installation der Pakete eingebunden. Dadurch kann die apkovl unter anderem konfigurieren, welche Pakete installiert werden sollen.
OpenRC Runlevels
sysinit -> booten -> standardmäßig -> herunterfahren
https://hoverbear.org/2014/09/29/init-runlevels-and-targets/
Ausführungsebene sysinit:
modloop wird nach dev-mount, und vor checkfs, fsck, hwdriver, modules, hwclock, dev, sysfs gestartet
hwdrivers wird nach modloop gestartet. hwdrivers lädt die Treiber mit modprobe.
Runlevel boot:
Runlevel Standard:
Modloop
Es gibt auch die modloop-Datei (modloop-rpi oder modloop-rpi2).
wie er montiert wird
Die richtige Datei für Ihr System wird in dem Skript /etc/init.d/modloop mit der Funktion find_modloop() ermittelt
Das Ergebnis von uname -r wird mit dem Verzeichnis modules/ abgeglichen - das entsprechend benannte Unterverzeichnis muss sich darin befinden.
Zum Beispiel: 4.14.69-0-rpi2
Auf diese Weise werden die richtigen Module für Ihren Kernel verwendet (uname -r gibt die Kernel-Version aus).
modloop wird in das Verzeichnis /.modloop
Symlinks werden erstellt aus /lib/firmware zu /lib/modules/firmwareund /lib/modules ist symverknüpft mit /.modloop/modules
Hinweis: Wenn Sie die Raspberry Pi 3B+ WiFi-Firmware patchen wollen, müssen Sie sie in modloop.
Der Inhalt von Modloop
Um den Inhalt von modloop zu erforschen, können Sie (auf einem Mint-System getestet) verwenden:
apt-get install squashfs-tools
unsquashfs modloop-rpi2
Sie enthält Firmware und Treiber.
Referenzen:
- 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/