Creare una distro Linux per una scheda elettronica

Scheda Linux

Creare una distribuzione Linux personalizzata per una scheda elettronica può essere un’ardua impresa, persino per un appassionato di Linux o un esperto di microcontrollori: ci sono diverse parti in movimento, e sincronizzarle richiede una conoscenza approfondita dell’ecosistema Linux.

Ma non temere: questo post ti fornirà tutte le indicazioni necessarie per far apparire quell’agognato prompt di login sulla tua scheda quanto più semplicemente possibile. E’ necessario soltanto del tempo, voglia di imparare e qualche nozione di base su Linux e i suoi strumenti di sviluppo.

Un mare di alternative

Lungo la strada che conduce alla creazione della nostra distribuzione (o distro) Linux, ci troviamo immediatamente ad un bivio: creare l’intero sistema da zero e completamente a mano, o cercare qualche strumento che possa aiutarci in questa ardua impresa?

La prima opzione risulta certamente in un’esperienza gratificante, sebbene estremamente lunga e complicata. Ha il vantaggio di fornire all’utente uno sguardo veloce ai meccanismi interni della propria macchina, da cui può trarre diverse perle di saggezza. Tuttavia è anche un percorso che richiede diverse conoscenze avanzate sul funzionamento di un sistema operativo, e sicuramente non è la strada più efficiente. Nonostante ciò, esistono diversi progetti online che si propongono di guidare l’utente in quest’avventura: LFS è uno di questi.

La seconda strada è invece quella regolarmente battuta. Nel corso degli anni sono stati creati e perfezionati diversi strumenti per ovviare al problema della realizzazione di una distro personalizzata. Alcuni di questi sono semplici da usare ma mancano di flessibilità, mentre altri riescono a fare qualsiasi cosa, dati abbastanza tempo ed esperienza. Sono noti come Linux build systems, e si pongono come obiettivo quello di fare per noi tutto il lavoro sporco: scaricare i sorgenti necessari, ottenere una toolchain, compilare tutti i pezzi e metterli insieme. L’unica cosa che richiedono all’utente è di fornire una configurazione ed avviare la build: del resto si occupano loro!

In questo post intraprenderemo la strada più breve ed useremo uno di questi build systems. Nonostante ce ne siamo diversi in giro, al giorno d’oggi il mercato è praticamente dominato da soli due contendenti:

Esistono delle alternative, come OpenWRT o LTIB, ma sono spesso troppo settoriali (la prima) o mancano di alcune feature fondamentali (la seconda).

Per un neofita, Buildroot è certamente lo strumento migliore, per cui ci concentreremo su di esso nelle prossime sezioni. Yocto è il naturale next-step per quando Buildroot non è più sufficiente.

Le basi

Prima di iniziare ad usare Buildroot, è necessario avere un’idea generale dei componenti necessari per arrivare ad un sistema Linux funzionante. Alcuni di questi possono variare a seconda dell’architettura, ma solitamente servirà sempre:

Una volta ottenuti tutti e tre questi componenti, il passo finale sta nel metterli insieme e caricarli sulla scheda, secondo le metodologie previste da essa (che tipicamente varieranno da un modello all’altro). A titolo d’esempio, nel resto di questo post faremo riferimento a Raspberry Pi come scheda target dello sviluppo.

Preparare l’ambiente di build

Per iniziare, avremo bisogno di una macchina su cui è installata una distribuzione Linux, essendo il solo sistema operativo supportato da Buildroot. Non è importante la distribuzione utilizzata, a patto che sia possibile installare su di essa gli strumenti necessari per utilizzare Buildroot.

Come prossimo passo, avremo bisogno di scaricare i sorgenti di Buildroot. Abbiamo due scelte: scaricare un tarball o effettuare il checkout di un repository git. Personalmente consiglio l’utilizzo del repository: in questo modo viene gratis il setup del controllo di revisione e possiamo facilmente annullare potenziali cambiamenti distruttivi durante le nostre sperimentazioni.

Ciò si traduce di fatto in:

~ $ git clone git://git.buildroot.net/buildroot
~ $ cd buildroot/

È sempre buona norma iniziare a sviluppare partendo da un tag stabile, così da poter sempre tornare indietro ad un punto noto della storia:

~/buildroot $ git checkout 2018.08

Diamo adesso un’occhiata al contenuto della cartella buildroot/:

~/buildroot $ ls
arch/     docs/     toolchain/  CHANGES           DEVELOPERS
board/    fs/       support/    Config.in         Makefile
boot/     linux/    system/     Config.in.legacy  Makefile.legacy
configs/  package/  utils/      COPYING           README

Ci sono diversi file e cartelle dentro, ma un paio di cose saltano all’occhio:

Per il resto dei file sorgente, Buildroot fornisce un’ottima documentazione che è possibile consultare nel caso fossimo curiosi e volessimo saperne di più.

Configurare Buildroot

Come detto prima, questi sistemi di build richiedono una qualche configurazione per poter sapere cosa compilare e per quale target, e come visto prima, è presente una directory configs/ tra i sorgenti. Come ci si aspetterebbe, è possibile trovare al suo interno diversi file di configurazione pre-esistenti per diversi prodotti: evaluation kit, schede di sviluppo e consumer, ecc. Tra questi, c’è anche un comodo raspberrypi_defconfig che possiamo utilizzare come punto di partenza per la nostra distribuzione.

La configurazione di Buildroot segue il formato Kconfig, usato anche dal kernel Linux. Non è strettamente necessario conoscerlo per interagire con Buildroot, ma per i curiosi, qui è possibile trovare alcune informazioni su di esso. Inoltre, quando possibile, è bene non iniziare da zero a sviluppare una configurazione per una scheda custom, ma partire dalla configurazione di una evaluation board o scheda di sviluppo simile e costruirci sopra le nostre modifiche.

In Buildroot, è possibile applicare il contenuto di un file di configurazione tramite il comando make <config-file-name>, assunto che il nome del file termini in _defconfig e si trovi nella cartella configs/. Nel nostro caso lanceremo:

~/buildroot $ make raspberrypi_defconfig
...
#
# configuration written to ~/buildroot/.config
#

Passiamo adesso a dare un’occhiata al contenuto della configurazione che abbiamo appena applicato!

Modificare la distribuzione di base

Sebbene sia possibile aprire il file di configurazione tramite un editor di testo per analizzarlo, Buildroot fornisce degli strumenti che permettono di visualizzare e modificare la configurazione attuale con più semplicità.

Per gli amanti del terminale, il comando make menuconfig aprirà un configuratore interattivo basato su curses nel quale è possibile navigare per modificare la configurazione attiva. Questo è molto utile per quando si è loggati sulla macchina di build da remoto. Per gli amanti delle GUI, lo stesso risultato lo si può ottenere usando make xconfig o make gconfig, che apriranno rispettivamente un configuratore Qt o GTK.

In entrambi i casi, ci si troverà davanti ad un menu di questo tipo:

    Target options  --->
    Build options  --->
    Toolchain  --->
    System configuration  --->
    Kernel  --->
    Target packages  --->
    Filesystem images  --->
    Bootloaders  --->
    Host utilities  --->
    Legacy config options  --->

Le voci del menu sono abbastanza autoesplicative, e il modo migliore per farsi le ossa è girare un po’ tra le varie opzioni e leggerne la documentazione (accessibile tramite il tasto ? sulla singola opzione). Alcuni punti chiave:

Supponiamo ad esempio di voler modificare l’hostname della nostra distro custom in rpi e installare un server OpenSSH. Inoltre, aumenteremo la dimensione del filesystem a 100MB per poter ospitare queste modifiche.

L’hostname di default (buildroot) può essere modificato usando l’opzione System hostname in System configuration:

System configuration  --->
    (rpi) System hostname

OpenSSH può essere installato selezionando la voce openssh nel percorso Target packages -> Networking applications:

Target packages  --->
    Networking applications --->
        [*] openssh

Per finire, è possibile modificare la dimensione dell’immagine del root filesystem agendo sull’opzione exact size in Filesystem images (di default a 60M):

Filesystem images  --->
    (100M) exact size

E con questo abbiamo terminato le modifiche. Possiamo adesso salvare ed uscire dal configuratore per applicare i cambiamenti. Nel caso stiamo usando menuconfig, per uscire è sufficiente premere ESC diverse volte fino a quando apparirà il prompt di salvataggio, al qual punto possiamo selezionare <Yes> per salvare e uscire.

In Buildroot tutte le modifiche alla configurazione attuale sono salvate localmente nel file .config nella root del progetto. Questo file è temporaneo, e non va tracciato tramite controllo di versione. Per salvare definitivamente una modifica alla configurazione e condividerla con altri membri del team, è possibile lanciare il comando make savedefconfig. Questo andrà a sovrascrivere il file usato in origine per generare la configurazione (nel nostro caso raspberrypi_defconfig) applicando le nuove modifiche, pronte per essere committate su Git.

Finalmente è tempo di compilare la distribuzione!

Rimani aggiornato!

Ricevi novità su Develer e sulle tecnologie che utilizziamo iscrivendoti alla nostra newsletter.

Creare l’immagine di sistema

A seconda del numero di pacchetti selezionati, e se stiamo compilando o meno da zero toolchain, kernel e/o booloader, il tempo di build può variare da diversi minuti a diverse ore. Una build può essere avviata usando semplicemente il comando:

~/buildroot $ make

È importante notare che Buildroot non supporta l’esecuzione parallela di make tramite l’opzione -jN fornita al top-level. I singoli task verranno tuttavia lanciati parallelamente (questo comportamento è comunque configurabile).

Una volta terminata la build, verranno create due nuove cartelle:

In particolare, ciò che ci interessa principalmente si trova all’interno di output/images/, che contiene gli output finali della build. Il contenuto di questa cartella dipende principalmente dall’architettura target e da alcune opzioni di configurazione, ma in genere ci saranno:

Per alcune architetture, Buildroot è anche in grado di produrre un’immagine del disco da scrivere su SD card o chiavina USB, già correttamente partizionata e formattata in modo che il processore possa effettuare il boot da essa. È questo il caso della Rasbperry Pi: tale immagine viene salvata come output/images/sdcard.img.

Testare l’immagine

L’ultimo passo di questa procedura consiste nello scrivere tale immagine su una SD card e avviare la scheda. È possibile usare uno dei vari tool adatti allo scopo; una maniera veloce e universale per tutte le distribuzioni Linux è usare il comando dd. Se non si ha familiarità con dd, è bene prima leggerne attentamente il manuale, poiché passare dei parametri errati può comportare la perdita di dati.

Il comando completo sarà:

~/buildroot $ sudo dd if=output/images/sdcard.img of=<sd-device>

dove <sd-device> è il device sotto /dev/ corrispondente alla propria SD card (a seconda della macchina, potrebbe essere nel formato /dev/sdX o /dev/mmcblkX).

Una volta concluso questo passaggio, è possibile inserire l’SD sulla propria RPi e alimentarla. Dopo la sequenza di boot iniziale, si dovrebbe vedere un prompt sulla porta seriale: basta inserire root come username per effettuare il login sul nostro nuovo sistema!

Ulteriori modifiche

Abbiamo prima brevemente introdotto la cartella board/ all’interno della root di Buildroot. Questa può essere utilizzata per memorizzare tutto ciò che è specifico per una singola scheda o prodotto supportato da Buildroot.

Ad esempio, provando a guardare all’interno di board/raspberrypi/ possiamo notare dei file interessanti:

Come si può notare, sono tutti file relativi a RPi, che non troverebbero un posto adatto in altre parti del build system, per cui vengono tutte raggruppate in una sottocartella di board/.

Ulteriori utilizzi di questa directory sono il salvataggio delle configurazioni o di patchset per il kernel o il bootloader, asset pre-compilati e così via. Altre informazioni su come personalizzare un proprio progetto o scheda si possono trovare nella sezione dedicata della guida.

Conclusioni

Questo è tutto! Buildroot rende estremamente semplice la creazione di una distro Linux completamente personalizzata, anche con una conoscenza minima di ciò che sta effettivamente succedendo dietro le quinte.

E visto che fare è il modo migliore per imparare, prova a modificare ulteriormente questa distribuzione di base, o addirittura crea da zero un nuovo progetto basato su una tua scheda, applicando tutto quello che hai imparato oggi!