Alpine Bootvorgang auf dem Raspberry Pi

Heute wollen wir uns den Alpine Linux-Bootprozess auf einem Raspberry Pi im Detail ansehen.

clip_image001

Das Bild zeigt den Inhalt einer "jungfräulichen" SD-Karte mit dem Alpine-Image, die noch nicht gebootet wurde.

clip_image003

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:

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
  • 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

Konsole=tty1

  • definiert die serielle Konsole

Lesen Sie weiter:

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).

clip_image005

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.

clip_image007

Wenn Sie neugierig sind, können Sie hier mehr über cpio lesen:

https://en.wikipedia.org/wiki/Cpio

Dies ist der Inhalt der Datei:

clip_image008

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

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
  • 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,
  • 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:

clip_image010

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:

clip_image012

Runlevel Standard:

clip_image014

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

clip_image015

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.

clip_image016

clip_image017

Referenzen:

weitere Lektüre: