Processo di avvio alpino sul Raspberry Pi

Oggi daremo un'occhiata al processo di avvio di Alpine Linux su un Raspberry Pi in qualche dettaglio.

clip_image001

L'immagine mostra il contenuto di una scheda SD "vergine" con l'immagine Alpine, che non è stata ancora avviata.

clip_image003

Questa immagine mostra il contenuto della cartella di avvio.

Fasi iniziali di avvio sul Raspberry Pi

Fate riferimento a questo link per una guida approfondita ai diversi supporti di avvio:

https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md

Il Raspberry Pi non ha un classico "BIOS" come nei computer IBM PC compatibili basati su x86. La sua CPU dovrebbe essere pensata come un'aggiunta al VideoCore IV per l'avvio, piuttosto che il contrario.

Così, inizialmente il VideoCore IV, la GPU, ha il controllo. Parte dalla propria ROM (primo stadio), e controlla i sorgenti di avvio per un file chiamato bootcode.bin. Per i nostri scopi assumeremo che questo file sia caricato dalla scheda SD.

bootcode.bin (51 KB) viene caricato nella cache L2 locale da 128K della GPU. Abilita la RAM e carica il terzo stadio: start.elf (2759 KB)

start.elf leggerà il file config.txt e configurerà le impostazioni appropriate per il VideoCore IV. config.txt è un file di parametri per il Raspberry Pi dove è possibile impostare ad esempio la modalità video, l'overscan, le opzioni audio, i codec MPEG, le impostazioni di overclock, ecc. - è "come" la parte delle impostazioni del BIOS del computer tradizionale. start.elf stesso è il firmware per la GPU, che è il suo proprio sistema operativo chiamato "VideoCore OS".

NB: Ci sono diverse versioni di start.elf, incluso start_x.elf che include i driver della telecamera, e start_cd.elf - che è una versione ridotta di start.elf, da usare quando la memoria della GPU è ridotta al massimo: gpu_mem=16. Quest'ultima versione potrebbe essere interessante per i sistemi embedded. Fate riferimento a questo sito per maggiori informazioni: https://www.raspberrypi.org/documentation/configuration/boot_folder.md

config.txt è opzionale. Se usate la stessa scheda SD (o immagine) per diversi Pi, potete impostare dei filtri condizionali da applicare a certi scenari; potete anche applicare una condizione basata sul seriale del Raspberry Pi, o sullo stato di un GPIO. C'è anche la possibilità di includere file aggiuntivi nel config.txt. Preso insieme, si ottiene un sistema abbastanza potente per la configurazione di base del Raspberry Pi.

Naturalmente, ci sono anche opzioni di avvio in config.txt, che controllano come procede l'ulteriore avvio.

start.elf carica quindi il kernel (come specificato da config.txt / defaults), e lo lancia con cmdline.txt come linea di comando del kernel.

opzioni di avvio config.txt

dare un'occhiata a:

https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md

per leggere di più sulle opzioni di avvio disponibili.

Il config.txt predefinito di Alpine

Diamo un'occhiata al config.txt che viene fornito con la versione 3.8.1 ARMHF di Alpine:

  • disable_splash=1
    • questo disabiliterà la schermata arcobaleno all'avvio (puramente cosmetico)
  • boot_delay=0
    • non aspettare in start.elf prima di caricare il kernel. (un ritardo di avvio potrebbe essere necessario per alcune schede SD per "prepararsi")
  • gpu_mem=256
    • imposta la divisione della memoria tra GPU e CPU. La memoria del Pi è condivisa. Qui abbiamo impostato 256 MB per la GPU, si potrebbe voler ridurre questo nei sistemi embedded che non guidano i display.
  • gpu_mem_256=64
    • imposta la divisione della memoria per i Pi con solo 256 MB di memoria totale (solo alcuni Pi 1B di vecchia generazione)
  • [pi0]
    • dichiarazione condizionale per Pi Zero; questo copre Pi Zero e Pi Zero W
    • tutti gli statemens fino al prossimo condizionale saranno applicati solo se corriamo su un Pi Zero / Zero W
    • il Pi Zero / Zero W usa ancora una vecchia CPU ARMv6 ISA (ARM11 @ 1 GHz)
    • useremo un kernel compilato per ARMv6
  • kernel=boot/vmlinuz-rpi
    • questo specifica il kernel da caricare
    • questo deve essere un file immagine del kernel non compresso
    • I kernel a 32 bit sono caricati all'indirizzo 0x8000 per impostazione predefinita
  • initramfs boot/initramfs-rpi
    • "il comando initramfs specifica sia il nome del file ramfs che l'indirizzo di memoria in cui caricarlo"
    • initramfs = filesystem RAM iniziale
    • questo file può essere estratto con 7Zip, per esempio (estrarre due volte)
    • NOTA: questo parametro di configurazione è specificato senza l'"=".
  • [pi1]
    • questo applicherà le seguenti linee di configurazione a tutti i Raspberry Pi 1 - non corrisponderà su Pi 2, ecc.
  • kernel=boot/vmlinuz-rpi
  • initramfs boot/initramfs-rpi
  •  [pi2]
  • kernel=boot/vmlinuz-rpi2
    • notate come usiamo un kernel diverso, dato che qui abbiamo una CPU che può eseguire codice ARMv7
  • initramfs boot/initramfs-rpi2
    • e un diverso initramfs ...
  • [pi3]
    • kernel=boot/vmlinuz-rpi2
    • initramfs boot/initramfs-rpi2
  • [pi3+]
    • kernel=boot/vmlinuz-rpi2
    • initramfs boot/initramfs-rpi2
  • [tutti]
    • la seguente linea(e) si applicherà nuovamente a tutti i Pi
  • includere usercfg.txt
    • includerà un file usercfg.txt nella configurazione, che non esiste ancora nell'immagine Alpine di default.
    • qui si potrebbe mettere la configurazione aggiuntiva personalizzata che si desidera, ad esempio la risoluzione HDMI, ecc.
    • In alternativa potresti semplicemente incollarlo qui sotto nel config.txt

Nota: la configurazione usata in Alpine non specifica un secondo parametro per l'initramfs (indirizzo su cui montare l'initramfs); quindi non è consigliabile copiarlo 1:1. Detto questo, sembra funzionare, probabilmente con un default sicuro!

Leggi di più su config.txt:

Il kernel e Initramfs

Il kernel

A seconda delle impostazioni del config.txt, il kernel corretto verrà selezionato e caricato nella RAM. Una volta caricato, il/i core del processore ARM viene/vengono liberato/i dal reset in modo da poter avviare il kernel.

Gli verrà passato il contenuto di cmdline.txt come linea di comando del kernel. start.elf passerà anche parametri aggiuntivi, per esempio l'impostazione dei canali DMA, l'indirizzo MAC del chip SMSC LAN, ecc.

Ottenere il cmdline.txt:

cat /proc/cmdline

dmesg | grep "Linea di comando"

cmdline.txt

diamo un'occhiata al cmdline.txt di default di Alpine:

modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=tty1

Questo carica i moduli

  • loop
    • il dispositivo loop è usato per montare un file come file system
  • squashfs
  • sd-mod
    • Driver del disco SCSI / block driver
    • apparentemente copiato dalla configurazione di default di Alpine, per esempio qui: https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
  • usb-storage
    • Driver di archiviazione di massa USB per Linux

NB: i nomi dei moduli possono contenere sia - che _, questi simboli possono apparentemente essere scambiati.

tranquillo

  • imposta il livello predefinito di log del kernel su KERN_WARNING, che sopprime tutti i messaggi di log tranne quelli molto gravi durante l'avvio (fonte: documentazione Raspberry Pi)

dwc_otg.lpm_enable=0

console=tty1

  • definisce la console seriale

Ulteriori letture:

initramfs

" Il comando initramfs specifica sia il nome del file ramfs che l'indirizzo di memoria in cui caricarlo. Esegue le azioni di entrambi ramfsfile e ramfsaddr in un solo parametro. L'indirizzo può anche essere followkernel (o 0) per metterlo in memoria dopo l'immagine del kernel. Valori di esempio sono: initramfs initramf.gz 0x00800000 o initramfs init.gz followkernel. NOTA: Questa opzione usa una sintassi diversa da tutte le altre opzioni, e non si dovrebbe usare il carattere = qui". - Documentazione su Raspberry Pi

Ci sarà un messaggio del kernel come questo "[ 0.143709] Trying to unpacking rootfs image as initramfs..." in dmesg, mostrando che l'immagine è stata spacchettata.

L'initramfs permette al kernel Linux di aggiungere moduli ed eseguire altri compiti per preparare il montaggio del file system attuale. Rende quindi un sistema di avvio molto versatile, senza dover ricompilare il kernel / aggiungere overhead al kernel. Per esempio per gli utenti con un filesystem criptato, initramfs chiederà la passphrase prima di poter decriptare e montare i filesystem.

A seconda della CPU del vostro Raspberry Pi, verranno utilizzati diversi file initramfs, come impostato in config.txt:

  • boot/initramfs-rpi per Pi 1 / 1B+ e Pi Zero / Zero W, e Compute Module 1
  • boot/initramfs-rpi2 per tutti gli altri Pi (2, 3B, 3B+, ...)

Il file initramfs può essere estratto usando, per esempio, su Windows 7Zip. (Dovete estrarlo due volte: il primo file contiene un secondo file gzippato)

Le immagini initramfs possono essere in diversi formati, a seconda di quali algoritmi sono stati compilati staticamente nel kernel (ad esempio gzip / bzip2 / LZMA / XZ / LZO / LZ4).

clip_image005

Questo secondo file confeziona una struttura di cartelle e file, che riconoscerete se avete usato Linux in precedenza. È in un formato speciale, "cpio", che è simile al catrame.

clip_image007

se siete curiosi, potete leggere di più su cpio qui:

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

Questo è ciò che il file contiene:

clip_image008

in cestino, contiene:

  • busybox
  • sh

in sbin, contiene

il init è lanciato dal kernel Linux come primo processo, e fondamentalmente imposta Alpine.

Ecco lo strumento usato per generare l'initramfs per Alpine:

https://github.com/alpinelinux/mkinitfs

init

questo è un sequenziale estratto da quello che fa lo script di init lanciato da initramfs:

  • crea le directory /usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot, /media/cdrom, /media/usb, /tmp, /run ricorsivamente
    • alcune di queste directory sembrano essere presenti già in initramfs
  • installa busybox (imposta i collegamenti simbolici ai comandi incorporati di busybox)
  • imposta PATH a /usr/bin:/bin:/usr/sbin:/sbin
  • crea /dev/null usando mknod
  • monta proc su /proc
    • proc è uno pseudo-file di informazioni sul processo
    • è possibile ottenere informazioni sul kernel leggendo e configurare certe cose scrivendo su alcuni file in questa directory
    • le voci numerate sono directory che contengono informazioni sul processo con questo particolare id di processo
    • https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
  • ... e sysfs a /sys
  • analizza le opzioni passate tramite cmdline.txt, le seguenti sono riconosciute:
    • alpine_dev autodetect autoraid chart 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

Suggerimento: prova a cambiare "tranquillo" in cmdline.txt a "noquiet" per ottenere un avvio più verboso, e vedere molti messaggi di init

  • monta devtmpfs su /dev
  • carica i driver per poter montare il modloop in seguito
  • se il parametro nbd è presente e configurato (dispositivo di blocco di rete), tenta di portare un indirizzo ip e di configurare il dispositivo di blocco di rete utilizzando nbd-client
  • se il parametro root è impostato
    • se [ -n "$KOPT_root" ]; allora
    • n valuta se la lunghezza di "$KOPT_root" non è zero
    • allora viene eseguito nlplug-findfs
    • overlaytmpfs è gestito opzionalmente
    • gli attuali punti di montaggio sono migrati in /sysroot
    • switch_root viene eseguito e il controllo viene passato a /sbin/init
    • (questo è un punto di ingresso per installare Alpine Linux in modalità regolare, non in modalità di sola lettura)

altrimenti:

  • nlplug-findfs viene eseguito per trovare il supporto di avvio,
  • se il parametro apkovl è impostato,
    • ed è vuoto - e /tmp/apkovls esiste (cioè i file sono stati trovati da nlplug-findfs) - l'ovl è impostato dalla prima riga di /tmp/apkovls
    • e inizia con http:// / https:// / ftp:// - si cerca una connessione di rete per estrarre l'apkovl
    • altrimenti l'ovl è impostato alla stringa nell'opzione apkovl
  • se l'apkovl esiste,
    • ed è un file .gz, l'apkovl è scompattato tar -C "$dest" -zxvf "$ovl" > $ovlfiles e /tmp/ovlfiles è riempito con l'output di tar
  • c'è del codice per uno splash screen, fbsplash è una delle funzioni incluse in busybox - visualizza i file fbsplash*.ppm
  • if [ -z "$ALPINE_REPO" ] -> significa, se la stringa $ALPINE_REPO è vuota
    • in alcune circostanze vengono aggiunti bootservice predefiniti, per i runlevel sysinit, boot, shutdown e default (ad esempio il servizio firstboot)
  • i repository apk vengono aggiunti ed espansi
    • il pacchetto alpine-base dipende da alpine-baselayout, alpine-conf e alcuni altri pacchetti
      • include anche il file /etc/os-release -> che mostra il nome, la versione e il bel nome di Alpine Linu
    • alpine-baselayout è il layout della directory di base
    • alpine-conf include, tra gli altri, lo script lbu e gli script di setup
    • busybox include il binario busybox
    • busybox-initscripts include alcuni initscripts
  • infine, la directory root viene commutata e /sbin/init viene avviato
    • questo avvierà OpenRC ed eseguirà i runlevel
    • /sbin/init è un collegamento simbolico a /bin/busybox

Nota: l'apkovl viene montato prima di modloop e prima di installare i pacchetti. Questo permette all'apkovl di configurare quali pacchetti stanno per essere installati, tra le altre cose.

Runlevels OpenRC

sysinit -> boot -> default -> shutdown

https://hoverbear.org/2014/09/29/init-runlevels-and-targets/

Runlevel sysinit:

clip_image010

modloop sarà avviato dopo dev-mount, e prima di checkfs, fsck, hwdriver, modules, hwclock, dev, sysfs

hwdrivers sarà avviato dopo modloop. hwdrivers caricherà i driver usando modprobe.

Avvio del runlevel:

clip_image012

Runlevel predefinito:

clip_image014

Modloop

C'è anche il file modloop (modloop-rpi o modloop-rpi2).

come è montato

Il file giusto per il vostro sistema è determinato nello script /etc/init.d/modloop nella funzione find_modloop()

Il risultato di uname -r è controllato rispetto alla directory modules/ - la sottodirectory appropriatamente nominata deve essere in essa.

Per esempio: 4.14.69-0-rpi2

In questo modo vengono usati i moduli giusti per il vostro kernel (uname -r stampa il rilascio del kernel).

modloop è montato nella directory /.modloop

clip_image015

I collegamenti simbolici sono creati da /lib/firmware a /lib/moduli/firmware, e /lib/moduli è collegato in modo simbolico a /.modloop/moduli

Suggerimentose volete patchare il firmware WiFi del Raspberry Pi 3B+, dovete metterlo in modloop.

Contenuto di Modloop

Per esplorare i contenuti di modloop, potete usare (testato su un sistema mint):

apt-get installa squashfs-tools

unsquashfs modloop-rpi2

Contiene il firmware e i driver.

clip_image016

clip_image017

Riferimenti:

ulteriori letture: