Processo de arranque alpino no Raspberry Pi
Hoje vamos dar uma olhada no processo de inicialização do Alpine Linux em um Raspberry Pi com algum detalhe.
A imagem mostra o conteúdo de um cartão SD "virgem" com a imagem alpina, que ainda não foi inicializado.
Esta imagem mostra o conteúdo da pasta de arranque.
Etapas iniciais do boot no Raspberry Pi
Consulte este link para um guia detalhado em diferentes mídias de inicialização:
https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md
O Raspberry Pi não tem uma "BIOS" clássica como nos computadores compatíveis com IBM PC baseados em x86. A sua CPU deve ser pensada como um add-on para o VideoCore IV para efeitos de arranque, em vez do contrário.
Assim, inicialmente o VideoCore IV, a GPU, tem controle. Ele parte de sua própria ROM (primeira etapa), e verifica as fontes de inicialização para um arquivo chamado bootcode.bin. Para os nossos propósitos, vamos assumir que este arquivo é carregado a partir do cartão SD.
bootcode.bin (51 KB) é carregado no cache local de 128K L2 da GPU. Ele habilita a RAM e carrega o terceiro estágio: start.elf (2759 KB)
start.elf lerá config.txt e definirá as configurações apropriadas para o VideoCore IV. config.txt é um arquivo de parâmetros para o Raspberry Pi onde você pode definir, por exemplo, o modo de vídeo, overscan, opções de áudio, codecs MPEG, configurações de overclocking, etc. - é "como" as configurações da BIOS parte do computador tradicional. start.elf em si é o firmware para a GPU, que é o seu próprio sistema operacional chamado "VideoCore OS".
NB: Existem diferentes versões de start.elf, incluindo start_x.elf que inclui drivers de câmera, e start_cd.elf - que é uma versão reduzida de start.elf, a ser usada quando a memória da GPU é reduzida ao máximo: gpu_mem=16. Esta última versão pode ser interessante para sistemas embarcados. Consulte este site para mais informações: https://www.raspberrypi.org/documentation/configuration/boot_folder.md
config.txt é opcional. Se você usar o mesmo cartão SD (ou imagem) para vários Pi's, você pode definir filtros condicionais para aplicar a certos cenários; você pode até aplicar uma condição baseada na série do Raspberry Pi's, ou sobre o estado de uma GPIO. Há também a possibilidade de incluir arquivos adicionais no config.txt. Em conjunto, você obtém um sistema bastante poderoso para a configuração básica do Raspberry Pi.
Claro que também existem opções de inicialização no config.txt, que controlam como a continuação da inicialização.
start.elf então carrega o kernel (como especificado por config.txt / default), e o lança com cmdline.txt como linha de comando do kernel .
opções de inicialização config.txt
dê uma vista de olhos:
https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md
para ler mais sobre as opções de inicialização disponíveis.
O config.txt padrão da Alpine
Vamos dar uma olhada no config.txt que navega com a versão 3.8.1 do ARMHF da Alpine:
- disable_splash=1
- isto irá desactivar a tela do arco-íris no início (puramente cosmética)
- boot_delay=0
- não espere em start.elf antes de carregar o kernel. (um atraso de inicialização pode ser necessário para que alguns cartões SD "se preparem")
- gpu_mem=256
- define a divisão da memória entre a GPU e a CPU. A memória da Pi é compartilhada. Aqui definimos 256 MB para a GPU, você pode querer reduzir isso em sistemas incorporados que não conduzem monitores.
- gpu_mem_256=64
- define a divisão de memória para Pi's com apenas 256 MB de memória total (apenas algumas Pi 1B de geração antiga)
- [pi0]
- declaração condicional para Pi Zero; isto abrange Pi Zero e Pi Zero W
- todas as declarações até ao próximo condicional só serão aplicadas se corrermos com um Pi Zero / Zero W
- o Pi Zero / Zero W ainda usa uma CPU ARMv6 ISA mais antiga (ARM11 @ 1 GHz)
- vamos usar um kernel compilado para o ARMv6
- kernel=boot/vmlinuz-rpi
- isto especifica o kernel a carregar
- este deve ser um ficheiro de imagem de kernel não comprimido
- Os kernels de 32 bits são carregados para o endereço 0x8000, por padrão.
- initramfs boot/initramfs-rpi
- "o comando initramfs especifica tanto o nome do arquivo ramfs quanto o endereço de memória para o qual ele deve ser carregado".
- initramfs = sistema de ficheiros RAM inicial
- este arquivo pode ser extraído com 7Zip, por exemplo (extrair duas vezes)
- NOTA: este parâmetro de configuração é especificado sem o "=".
- [pi1]
- isto aplicará as seguintes linhas de configuração a todas as Raspberry Pi 1 - não será igual em Pi 2, etc.
- kernel=boot/vmlinuz-rpi
- initramfs boot/initramfs-rpi
- [pi2]
- kernel=boot/vmlinuz-rpi2
- repare como usamos um kernel diferente, já que aqui temos uma CPU que pode executar o código ARMv7
- initramfs boot/initramfs-rpi2
- e um initramas diferente...
- [pi3]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfs-rpi2
- [pi3+]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfs-rpi2
- [todos]
- a(s) seguinte(s) linha(s) será(ão) novamente aplicada(s) a todos os Pi's
- incluir usercfg.txt
- incluirá um arquivo usercfg.txt na configuração, que ainda não existe na imagem alpina padrão.
- aqui você pode colocar a configuração personalizada adicional que quiser, por exemplo, a resolução HDMI, etc.
- alternativamente, você poderia simplesmente colá-lo abaixo no config.txt
Nota: a configuração utilizada no Alpine não especifica um segundo parâmetro para os initramfs (endereço para a montagem dos initramfs); portanto, não é aconselhável copiar este 1:1. Dito isto, parece funcionar, provavelmente por omissão, para um padrão seguro!
Leia mais sobre o 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
O Kernel e os Initramfs
O núcleo
Dependendo das configurações do config.txt, o kernel correto será selecionado e carregado na RAM. Uma vez carregado, o(s) núcleo(s) do(s) processador(es) ARM (são/estão) liberados do reset para que (ele/eles) possa(m) inicializar o kernel.
Será passado o conteúdo do cmdline.txt como linha de comando do kernel. start.elf também passará parâmetros adicionais próprios, por exemplo, a definição de canais DMA, o endereço MAC do chip SMSC LAN, etc.
Obtenha o cmdline.txt:
gato /proc/cmdline
dmesg | grep "Linha de Comando
cmdline.txt
vamos dar uma olhada no cmdline.txt padrão da Alpine:
modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=tty1
Isto carrega os módulos
- etapa
- o dispositivo de laço é usado para montar um arquivo como um sistema de arquivo
- squashfs
- sistema de arquivo comprimido de leitura para Linux
- especialmente adequado para sistemas incorporados
- https://en.wikipedia.org/wiki/SquashFS
- isto é usado para modloop mais tarde
- sd-mod
- Controlador de disco SCSI / driver de bloco
- aparentemente copiado da configuração padrão alpina, por exemplo, aqui: https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
- usb-storage
- Driver de armazenamento em massa USB Linux
NB: os nomes dos módulos podem conter ambos - e _, estes símbolos podem aparentemente ser intercambiáveis.
tranquilo
- define o nível de log padrão do kernel para KERN_WARNING, que suprime todas as mensagens de log, exceto as muito sérias, durante o boot (fonte: documentação do Raspberry Pi)
dwc_otg.lpm_enable=0
- O driver do Controlador USB, desabilita o gerenciamento de energia do link USB com esta configuração. (Este é o padrão recomendado)
- https://elinux.org/RPI_BCM2708_Parameters#module:_sdhci-bcm2708
consola=tty1
- define o console serial
Leitura adicional:
- https://elinux.org/RPi_cmdline.txt
- https://www.raspberrypi.org/documentation/configuration/cmdline-txt.md
initramfs
” O comando initramfs especifica tanto o nome do arquivo ramfs quanto o endereço de memória para o qual ele deve ser carregado. Ele executa as ações do ramfsfile e do ramfsaddr em um parâmetro. O endereço também pode ser seguido pelo kernel (ou 0) para colocá-lo na memória após a imagem do kernel. Os valores de exemplo são: initramfs initramf.gz 0x00800000 ou initramfs initramfs init.gz followkernel. NOTA: Esta opção usa uma sintaxe diferente de todas as outras opções, e você não deve usar um = caractere aqui". – Documentação Raspberry Pi
Haverá uma mensagem do kernel como esta "[ 0.143709] Tentando descompactar a imagem rootfs como initramfs..." no dmesg, mostrando que a imagem está sendo descompactada.
O initramfs permite ao kernel Linux adicionar módulos e realizar outras tarefas na preparação para a montagem do sistema de arquivos real. Assim, ele faz um sistema de inicialização muito versátil, sem a necessidade de recompilar o kernel / adicionar sobrecarga ao kernel. Por exemplo, para utilizadores com um sistema de ficheiros encriptado, o initramfs irá pedir a frase-chave antes de poder desencriptar e montar os sistemas de ficheiros.
Dependendo da CPU do seu Raspberry Pi, serão utilizados ficheiros initramfs diferentes, conforme definido em config.txt:
- boot/initramfs-rpi para Pi 1 / 1B+ e Pi Zero / Zero W, e Módulo de Cálculo 1
- boot/initramfs-rpi2 para todos os outros Pi's (2, 3B, 3B+, ...)
O arquivo initramfs pode ser extraído usando, por exemplo, no Windows 7Zip. (Você tem que extraí-lo duas vezes: o primeiro arquivo contém um segundo arquivo gzip)
imagens initramfs podem estar em diferentes formatos, dependendo de quais algoritmos foram compilados estaticamente no kernel (por exemplo, gzip / bzip2 / LZMA / XZ / LZO / LZ4).
Este segundo arquivo empacota uma pasta e uma estrutura de arquivos, que você reconhecerá se já usou o Linux antes. Ele está em um formato especial, "cpio", que é semelhante ao alcatrão.
se estiveres curioso, podes ler mais sobre o cpio aqui:
https://en.wikipedia.org/wiki/Cpio
Isto é o que o arquivo contém:
no caixote do lixo, contém:
- busybox
- executável compacto contendo vários utilitários UNIX em uma aplicação
- especialmente para sistemas incorporados
- https://busybox.net/about.html
- sh
em alguém, ele contém
- apk
- Ferramenta de gestão de pacotes alpinos
- boicotartd
- esta é uma ferramenta para traçar o perfil do tempo de inicialização
- https://elinux.org/Bootchart
- http://www.bootchart.org/
- nlplug-findfs
- é usado para montar dispositivos de bloco e procurá-los por apkovl disponíveis, se nenhum apkovl= for passado na linha de comando do kernel
- https://github.com/alpinelinux/mkinitfs
o init é lançado pelo kernel do Linux como primeiro processoe, basicamente, estabelece a Alpine.
Aqui está a ferramenta utilizada para gerar os initramas para Alpine:
https://github.com/alpinelinux/mkinitfs
init
esta é uma sequência excerto do que o roteiro do initramfs faz:
- cria diretórios /usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot , /media/cdrom, /media/usb, /tmp, /run recursively
- alguns desses diretórios parecem já estar presentes nos initramas
- instala o busybox (configura os links simbólicos para os comandos embutidos do busybox)
- estabelece o PATH para /usr/bin:/bin:/usr/sbin:/sbin
- cria /dev/null usando mknod
- montagens proc a /proc
- proc é um sistema de pseudo-arquivo de informação de processos
- você pode obter informações sobre o kernel lendo e configurando certas coisas, escrevendo em alguns arquivos deste diretório
- as entradas numeradas são diretórios contendo informações sobre o processo com esta identificação de processo em particular
- https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
- ... e sysfs para /sys
- estas são informações sobre dispositivos de hardware, drivers de dispositivos e subsistemas do kernel
- https://en.wikipedia.org/wiki/Sysfs
- analisando as opções passadas via cmdline.txt, são reconhecidas as seguintes:
- alpine_dev autodetect autoraid chart cryptroot cryptroot cryptrootheader cryptoffset
- cryptdiscards cryptkey debug_init dma init_args keep_apk_new modules ovl_dev
- pkgs raiz_size pkgs quiet root usbdelay ip alpine_repo apkovl alpine_start splash
- blacklist overlaytmpfs rootfstype rootflags nbd resume s390x_net dasd ssh_key
Dica: tente mudar "tranquilo" em cmdline.txt para "noquiet"para obter uma bota mais verbosa, e ver muitas mensagens do init
- monta devtmpfs a /dev
- carrega os condutores para poder montar o modloop mais tarde
- se o parâmetro nbd estiver presente e configurado (dispositivo de bloco de rede), tenta trazer um endereço ip e configura o dispositivo de bloco de rede usando nbd-client
- se o parâmetro raiz estiver definido
- se [ -n "$KOPT_root" ]; então
- –n avalia se o comprimento do "$KOPT_root" é diferente de zero
- então o nlplug-findfs é executado
- opcionalmente, o overlaytmpfs é tratado
- os pontos de montagem atuais são migrados para /sysroot
- switch_root é executado e o controle é passado para /sbin/init
- (este é um ponto de entrada para instalar o Alpine Linux em modo regular, não em modo somente leitura)
caso contrário:
- nlplug-findfs é executado para encontrar a mídia de inicialização,
- as opções criptográficas são possíveis e processadas aqui
- os caminhos de apkovls encontrados são adicionados a /tmp/apkovl (usando o -a switch, veja https://pi3g.com/2019/01/09/nlplug-findfs-documentation/)
- aparentemente o arquivo só é criado se apkovl's forem encontrados.
- https://github.com/alpinelinux/mkinitfs/blob/master/nlplug-findfs.c
- apkovls são correspondidos à procura de "*.apkovl.tar.gz*", é por isso que os arquivos de backup que o lbu commit cria são não encontrados / aplicados automaticamente - têm uma estrutura de hostname.timestamp.tar.gzp.ex. pidoctor.20190110180745.tar.gz
- se o parâmetro apkovl está pronto,
- e está vazio - e /tmp/apkovls existe (ou seja, foram encontrados arquivos por nlplug-findfs) - o ovl é definido desde a primeira linha de /tmp/apkovls
- e começa com http:// / https:// / ftp:// - tenta-se uma conexão de rede para puxar o apkovl
- caso contrário o ovl está definido para a corda na opção apkovl
- se a apkovl existir,
- e é um arquivo .gz, o apkovl é desempacotado tar -C "$dest" -zxvf "$ovl" > $ovlfiles e /tmp/ovlfiles é preenchido com a produção de alcatrão
- há código para uma tela de respingo, fbsplash é uma das funções incluídas no busybox - ele exibe arquivos fbsplash*.ppm
- se [ -z "$ALPINE_REPO" ] -> significa, se a string $ALPINE_REPO estiver vazia
- em algumas circunstâncias são adicionados serviços de inicialização padrão, para o runlevel sysinit, boot, shutdown e padrão (por exemplo, o serviço firstboot)
- Os repositórios da apk são adicionados e expandidos
- o pacote base alpina depende de baselayout alpino, alpine-conf e alguns outros pacotes
- também inclui o arquivo /etc/os-release -> que mostra o nome, versão e bonito nome do Alpine Linu
- alpine-baselayout é o layout do diretório base
- alpine-conf inclui, entre outros, o script lbu e os scripts de configuração
- busybox inclui o binário busybox
- busybox-initscripts inclui alguns initscripts
- finalmente, o diretório raiz é trocado, e o /sbin/init é iniciado
- isto irá iniciar o OpenRC e executar os níveis de execução
- /sbin/init é um link simbólico para /bin/busybox
Atenção: O apkovl é montado antes do modloop e antes de instalar os pacotes. Isto permite ao apkovl configurar quais os pacotes a serem instalados, entre outras coisas.
Runlevels OpenRC
sysinit -> boot -> default -> shutdown
https://hoverbear.org/2014/09/29/init-runlevels-and-targets/
Sistema de runlevel sysinit:
modloop será iniciado após o dev-mount, e antes do checkfs, fsck, hwdriver, módulos, hwclock, dev, sysfs
hwdrivers serão iniciados após o modloop. hwdrivers irão carregar os drivers usando o modprobe.
Bota de nível de corrida:
Nível de execução padrão:
Modloop
Existe também o ficheiro modloop (modloop-rpi ou modloop-rpi2).
como ele é montado
O ficheiro certo para o seu sistema é determinado no script /etc/init.d/modloop na função find_modloop()
O resultado de uname -r é verificado em relação aos módulos de directório/ - o subdirectório devidamente nomeado tem de estar nele.
Por exemplo: 4.14.69-0-rpi2
Desta forma são utilizados os módulos certos para o seu kernel (uname -r imprime a liberação do kernel).
modloop é montado no diretório /.modloop
Os Symlinks são criados a partir de /lib/firmware para /módulos/módulos/firmwaree /lib/módulos está ligado simbolicamente a /.modloop/módulos
Dica: se você quiser corrigir o firmware Raspberry Pi 3B+ WiFi, você tem que colocá-lo em modloop.
Conteúdo do Modloop
Para explorar o conteúdo do modloop, você pode usar (testado em um sistema de menta):
apt-get install squashfs-tools
unsquashfs modloop-rpi2
Ele contém firmware e drivers.
Referências:
- 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/