Proceso de arranque alpino en la Raspberry Pi

Hoy vamos a echar un vistazo al proceso de arranque de Alpine Linux en una Raspberry Pi con cierto detalle.

clip_image001

La imagen muestra el contenido de una tarjeta SD "virgen" con la imagen de Alpine, que aún no ha sido arrancada.

clip_image003

Esta imagen muestra el contenido de la carpeta de arranque.

Etapas de arranque inicial en la Raspberry Pi

Consulte este enlace para obtener una guía detallada sobre los diferentes medios de arranque:

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

La Raspberry Pi no tiene una "BIOS" clásica como en los ordenadores compatibles con IBM PC basados en x86. Su CPU debe considerarse como un complemento del VideoCore IV para el arranque, y no al revés.

Así, inicialmente el VideoCore IV, la GPU, tiene el control. Se inicia desde su propia ROM (primera etapa), y comprueba las fuentes de arranque para un archivo llamado bootcode.bin. Para nuestros propósitos asumiremos que este archivo se carga desde la tarjeta SD.

bootcode.bin (51 KB) se carga en la caché L2 local de 128K de la GPU. Habilita la RAM y carga la tercera etapa: start.elf (2759 KB)

start.elf leerá config.txt, y establecerá los ajustes apropiados para el VideoCore IV. config.txt es un archivo de parámetros para la Raspberry Pi donde se puede establecer, por ejemplo, el modo de vídeo, overscan, opciones de audio, codecs MPEG, ajustes de overclocking, etc. - Es "como" la parte de configuración de la BIOS del ordenador tradicional. start.elf es el firmware de la GPU, que es su propio sistema operativo llamado "VideoCore OS".

NB: Hay diferentes versiones de start.elf, incluyendo start_x.elf que incluye los controladores de la cámara, y start_cd.elf - que es una versión recortada de start.elf, para ser utilizada cuando la memoria de la GPU se reduce al máximo: gpu_mem=16. Esta última versión podría ser interesante para los sistemas embebidos. Consulta este sitio para obtener más información: https://www.raspberrypi.org/documentation/configuration/boot_folder.md

config.txt es opcional. Si utiliza la misma tarjeta SD (o imagen) para varias Pi, puede establecer filtros condicionales para aplicar a ciertos escenarios; incluso puede aplicar una condición basada en la serie de la Raspberry Pi, o en el estado de un GPIO. También existe la posibilidad de incluir archivos adicionales en el config.txt. En conjunto, se obtiene un sistema bastante potente para la configuración básica de la Raspberry Pi.

Por supuesto, también hay opciones de arranque en config.txt, que controlan cómo procede el arranque posterior.

start.elf entonces carga el kernel (según lo especificado por config.txt / defaults), y lo lanza con cmdline.txt como línea de comando del kernel .

config.txt opciones de arranque

echa un vistazo:

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

para leer más sobre las opciones de arranque disponibles.

El config.txt por defecto de Alpine

Echemos un vistazo al config.txt que viene con la versión 3.8.1 ARMHF de Alpine:

  • disable_splash=1
    • esto desactivará la pantalla arco iris en el inicio (puramente cosmético)
  • boot_delay=0
    • no espere en start.elf antes de cargar el kernel. (puede ser necesario un retraso en el arranque para que algunas tarjetas SD se "preparen")
  • gpu_mem=256
    • establece la división de la memoria entre la GPU y la CPU. La memoria de la Pi es compartida. Aquí establecemos 256 MB para la GPU, es posible que desee reducir esto en los sistemas embebidos que no manejan pantallas.
  • gpu_mem_256=64
    • establece la división de la memoria para los Pi con sólo 256 MB de memoria total (sólo algunos Pi 1B de la vieja generación)
  • [pi0]
    • declaración condicional para Pi Zero; esto cubre Pi Zero y Pi Zero W
    • todos los estados hasta el siguiente condicional se van a aplicar sólo si se ejecuta en un Pi Zero / Zero W
    • la Pi Zero / Zero W sigue utilizando una CPU ARMv6 ISA más antigua (ARM11 a 1 GHz)
    • utilizaremos un kernel compilado para ARMv6
  • kernel=boot/vmlinuz-rpi
    • esto especifica el kernel a cargar
    • debe ser un archivo de imagen del kernel sin comprimir
    • Los kernels de 32 bits se cargan por defecto en la dirección 0x8000
  • initramfs boot/initramfs-rpi
    • "el comando initramfs especifica tanto el nombre del archivo ramfs como la dirección de memoria donde cargarlo"
    • initramfs = sistema de archivos RAM inicial
    • este archivo se puede extraer con 7Zip, por ejemplo (extraer dos veces)
    • NOTA: este parámetro de configuración se especifica sin el "="
  • [pi1]
    • esto aplicará las siguientes líneas de configuración a todas las Raspberry Pi 1 - no coincidirán en las Pi 2, etc.
  • kernel=boot/vmlinuz-rpi
  • initramfs boot/initramfs-rpi
  •  [pi2]
  • kernel=boot/vmlinuz-rpi2
    • Obsérvese que utilizamos un kernel diferente, ya que aquí tenemos una CPU que puede ejecutar código ARMv7
  • initramfs boot/initramfs-rpi2
    • y un initramfs diferente ...
  • [pi3]
    • kernel=boot/vmlinuz-rpi2
    • initramfs boot/initramfs-rpi2
  • [pi3+]
    • kernel=boot/vmlinuz-rpi2
    • initramfs boot/initramfs-rpi2
  • [todos]
    • la(s) siguiente(s) línea(s) se aplicará(n) de nuevo a todos los Pi's
  • incluir usercfg.txt
    • incluirá un archivo usercfg.txt en la configuración, que aún no existe en la imagen predeterminada de Alpine.
    • Aquí puede poner la configuración adicional que desee, por ejemplo, la resolución HDMI, etc.
    • alternativamente, usted podría pegar a continuación en config.txt

Nota: la configuración utilizada en Alpine no especifica un segundo parámetro para el initramfs (dirección en la que montar el initramfs); por lo que no es aconsejable copiar esto 1:1. Dicho esto, parece que funciona, ¡probablemente por defecto!

Más información sobre config.txt:

El núcleo y el Initramfs

El núcleo

Dependiendo de la configuración del config.txt, el kernel correcto será seleccionado y cargado en la RAM. Una vez cargado, el núcleo del procesador ARM se libera del reinicio para que pueda arrancar el kernel.

Se le pasará el contenido de cmdline.txt como línea de comando del kernel. start.elf también pasará parámetros adicionales propios, por ejemplo, la configuración de los canales DMA, la dirección MAC del chip SMSC LAN, etc.

Obtenga el cmdline.txt:

cat /proc/cmdline

dmesg | grep "Línea de comandos"

cmdline.txt

echemos un vistazo al cmdline.txt por defecto de Alpine:

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

Esto carga los módulos

  • bucle
    • el dispositivo de bucle se utiliza para montar un archivo como sistema de archivos
  • squashfs
    • sistema de archivos de lectura comprimida para Linux
    • especialmente adecuado para los sistemas empotrados
    • https://en.wikipedia.org/wiki/SquashFS
    • esto se utiliza para el modloop más adelante
  • sd-mod
    • Controlador de disco SCSI / controlador de bloque
    • aparentemente copiado de la configuración por defecto de Alpine, por ejemplo, aquí: https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
  • usb-storage
    • Controlador de almacenamiento masivo USB de Linux

Nota: los nombres de los módulos pueden contener tanto - como _, estos símbolos pueden aparentemente ser intercambiados.

tranquilo

  • establece el nivel de registro del kernel por defecto en KERN_WARNING, que suprime todos los mensajes de registro, excepto los muy graves, durante el arranque (fuente: documentación de Raspberry Pi)

dwc_otg.lpm_enable=0

consola=tty1

  • define la consola en serie

Más información:

initramfs

" El comando initramfs especifica tanto el nombre del archivo ramfs como la dirección de memoria donde cargarlo. Realiza las acciones de ramfsfile y ramfsaddr en un solo parámetro. La dirección también puede ser followkernel (o 0) para colocarla en la memoria después de la imagen del núcleo. Los valores de ejemplo son: initramfs initramf.gz 0x00800000 o initramfs init.gz followkernel. NOTA: Esta opción utiliza una sintaxis diferente a la de todas las demás opciones, y no debe utilizar un carácter = aquí." - Documentación de Raspberry Pi

Habrá un mensaje del kernel como este "[ 0.143709] Trying to unpack rootfs image as initramfs..." en dmesg, mostrando que la imagen está siendo desempaquetada.

El initramfs permite al núcleo de Linux añadir módulos y realizar otras tareas para preparar el montaje del sistema de archivos real. Por lo tanto, es un sistema de arranque muy versátil, sin tener que recompilar el kernel o añadirle una sobrecarga. Por ejemplo, para los usuarios con un sistema de archivos encriptado, initramfs pedirá la frase de contraseña antes de poder descifrar y montar los sistemas de archivos.

Dependiendo de la CPU de su Raspberry Pi, se utilizarán diferentes archivos initramfs, como se establece en config.txt:

  • boot/initramfs-rpi para Pi 1 / 1B+ y Pi Zero / Zero W, y Compute Module 1
  • boot/initramfs-rpi2 para todas las demás Pi (2, 3B, 3B+, ...)

El archivo initramfs se puede extraer utilizando, por ejemplo, en Windows 7Zip. (Hay que extraerlo dos veces: el primer archivo contiene un segundo archivo comprimido con gzip)

Las imágenes initramfs pueden estar en diferentes formatos, dependiendo de los algoritmos que se hayan compilado estáticamente en el núcleo (por ejemplo, gzip / bzip2 / LZMA / XZ / LZO / LZ4).

clip_image005

Este segundo archivo empaqueta una estructura de carpetas y archivos, que reconocerás si has usado Linux antes. Está en un formato especial, "cpio", que es similar al alquitrán.

clip_image007

si tienes curiosidad, puedes leer más sobre cpio aquí:

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

Esto es lo que contiene el archivo:

clip_image008

en la papelera, contiene:

  • busybox
    • ejecutable compacto que contiene varias utilidades UNIX en una sola aplicación
    • especialmente para los sistemas embebidos
    • https://busybox.net/about.html
  • sh

en sbin, contiene

el init es lanzado por el kernel de Linux como primer proceso...y básicamente establece a Alpine.

Aquí está la herramienta utilizada para generar el initramfs para Alpine:

https://github.com/alpinelinux/mkinitfs

init

esto es una secuencia extracto de lo que hace el script init lanzado desde el initramfs:

  • crea los directorios /usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot , /media/cdrom, /media/usb, /tmp, /run recursivamente
    • algunos de estos directorios parecen estar ya presentes en el initramfs
  • instala busybox (establece enlaces simbólicos a los comandos incrustados de busybox)
  • establece el PATH en /usr/bin:/bin:/usr/sbin:/sbin
  • crea /dev/null usando mknod
  • monta proc en /proc
    • proc es un sistema de pseudoarchivos de información de procesos
    • puedes obtener información sobre el kernel leyendo y configurar ciertas cosas escribiendo en algunos archivos de este directorio
    • las entradas numeradas son directorios que contienen información sobre el proceso con este id de proceso en particular
    • https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
  • ... y sysfs a /sys
  • analiza las opciones pasadas a través de cmdline.txt, se reconocen las siguientes:
    • 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

Consejo: pruebe a cambiar "tranquilo" en cmdline.txt a "noquiet" para obtener un arranque más verboso, y ver muchos mensajes de init

  • monta devtmpfs en /dev
  • carga los drivers para poder montar el modloop más adelante
  • si el parámetro nbd está presente y configurado (dispositivo de bloque de red), intenta traer una dirección ip, y configurar el dispositivo de bloque de red usando nbd-client
  • si se establece el parámetro raíz
    • if [ -n "$KOPT_root" ]; then
    • n evalúa si la longitud de "$KOPT_root" es distinta de cero
    • entonces se ejecuta nlplug-findfs
    • overlaytmpfs se maneja opcionalmente
    • los puntos de montaje actuales se migran a /sysroot
    • switch_root se ejecuta y el control se pasa a /sbin/init
    • (este es un punto de entrada para instalar Alpine Linux en modo normal, no en modo de sólo lectura)

de lo contrario:

  • nlplug-findfs se ejecuta para encontrar el medio de arranque,
  • si el parámetro apkovl está ajustado,
    • y está vacío - y /tmp/apkovls existe (es decir, los archivos fueron encontrados por nlplug-findfs) - el ovl está configurado desde la primera línea de /tmp/apkovls
    • y comienza con http:// / https:// / ftp:// - se intenta una conexión de red para sacar el apkovl
    • de lo contrario, el ovl se establece en la cadena de la opción apkovl
  • si el apkovl existe,
    • y es un archivo .gz, el apkovl se desempaqueta tar -C "$dest" -zxvf "$ovl" > $ovlfiles y /tmp/ovlfiles se rellena con la salida de tar
  • hay código para una pantalla de bienvenida, fbsplash es una de las funciones que se incluyen en busybox - muestra archivos fbsplash*.ppm
  • if [ -z "$ALPINE_REPO" ] -> significa que la cadena $ALPINE_REPO está vacía
    • en algunas circunstancias se añaden servicios de arranque por defecto, para el nivel de ejecución sysinit, boot, shutdown y default (por ejemplo el servicio firstboot)
  • se añaden y amplían los repositorios apk
    • el paquete alpine-base depende de alpine-baselayout, alpine-conf y algunos otros paquetes
      • también incluye el archivo /etc/os-release -> que muestra el nombre, la versión y el nombre bonito de Alpine Linu
    • alpine-baselayout es el diseño del directorio base
    • alpine-conf incluye, entre otros, el script lbu y los scripts de configuración
    • busybox incluye el binario busybox
    • busybox-initscripts incluye algunos initscripts
  • finalmente, se cambia el directorio raíz y se inicia /sbin/init
    • esto iniciará OpenRC y ejecutará los niveles de ejecución
    • /sbin/init es un enlace simbólico a /bin/busybox

Nota: El apkovl se monta antes de modloop y antes de instalar los paquetes. Esto permite al apkovl configurar qué paquetes se van a instalar, entre otras cosas.

Niveles de ejecución de OpenRC

sysinit -> boot -> default -> shutdown

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

Runlevel sysinit:

clip_image010

modloop se iniciará después de dev-mount, y antes de checkfs, fsck, hwdriver, modules, hwclock, dev, sysfs

hwdrivers se iniciará después de modloop. hwdrivers cargará los drivers usando modprobe.

Arranque a nivel de carrera:

clip_image012

Nivel de ejecución por defecto:

clip_image014

Modloop

También existe el archivo modloop (modloop-rpi o modloop-rpi2).

cómo se monta

El archivo correcto para su sistema se determina en el script /etc/init.d/modloop en la función find_modloop()

El resultado de uname -r se comprueba con el directorio modules/ - el subdirectorio con el nombre apropiado tiene que estar en él.

Por ejemplo: 4.14.69-0-rpi2

De esta manera se utilizan los módulos adecuados para su núcleo (uname -r imprime el lanzamiento del kernel).

modloop está montado en el directorio /.modloop

clip_image015

Los enlaces simbólicos se crean a partir de /lib/firmware a /lib/modules/firmwarey /lib/módulos está vinculado a /.modloop/modules

SugerenciaSi quieres parchear el firmware de la Raspberry Pi 3B+ WiFi, tienes que ponerlo en modloop.

Contenido de Modloop

Para explorar el contenido de modloop, puedes utilizar (probado en un sistema mint):

apt-get install squashfs-tools

unsquashfs modloop-rpi2

Contiene el firmware y los controladores.

clip_image016

clip_image017

Referencias:

más lecturas: