Buildroot dla Raspberry Pi krok po kroku


W tym wpisie dowiesz się:

  • Czym jest Buildroot?
  • Kiedy użyć Buildroota, a kiedy innych rozwiązań
  • Z czego składa się projekt
  • Jak stworzyć własnego Linuxa na Raspberry Pi
  • Jak dodać dodatkowe paczki i automatycznie łączyć się z WiFi

Czym jest Buildroot?

Buildroot to zautomatyzowany system budowania (build system), który przyjmuje określoną konfigurację — listę paczek, konfigurację jądra itp. — i automatycznie tworzy wszystko, co składa się na dystrybucję Linuxa. Konkretnie:

  • Główny system plików (rootfs)
  • Jądro Linuxa (kernel),
  • Bootloader,
  • Pliki konfiguracyjne (to te w /etc/)

Oprócz tych podstawowych rzeczy, w zależności od platformy budowane są również odpowiednie sterowniki, init system (np. systemd) i inne. Wynikiem końcowym jest obraz systemu, który możemy wgrać na partycję /boot karty SD.

I na koniec ważna informacja — Buildroot nie jest dystrybucją Linuxa. Jest narzędziem, które pozwala nam takie dystrybucje tworzyć, choć często potocznie mówi się, że dany system jest „zrobiony na Buildroocie” albo „Buildroot-based”.

Poniżej znajdziesz flowchart pokazujący ogólnie jakie kroki są wykonywane przez Buildroot podczas budowania. Każdy z nich można by rozbić na wiele mniejszych, ale taki ogólny obraz na razie wystarczy. Poszczególne kroki i co dokładnie jest budowane w bardzo dużym stopniu zależy od tego, na jaką platformę chcemy zbudować Linuxa i jak go skonfigurujemy. Przykładowo, czasem kernel będzie ściągany jako spakowany .tar z zewnętrznych źródeł, a czasem będziemy go budować w jednym z kroków bezpośrednio.

Wady i zalety

Dlaczego warto skorzystać z Buildroota budując system dla Raspberry Pi?

  • Łatwość użycia
    • Buildroot w porównaniu do swojej największej konkurencji, czyli Yocto, jest relatywnie łatwy w użyciu. Opiera się na znanych w branży programach takich jak menuconfig, Makefiles i skrypty bashowe. Dalej nie jest to łatwe narzędzie do opanowania, ale jest jak najbardziej do ogarnięcia nawet w projektach hobbystycznych.
  • Optymalizacja końcowego obrazu
    • Buildroot jest znany z produkowania bardzo oszczędnych dystrybucji, zabierają mało miejsca na dysku i zużywają niewielkie zasoby. Nawet jeśli dodamy mnóstwo paczek do obrazu.
  • Społeczność i dokumentacja
    • Dla mnie jest to jedna z największych zalet. Zarówno na liście mailowej, jak i na innych forach, w razie problemu można szybko znaleźć pomoc. Przynajmniej jeśli chodzi o popularne platformy, w przypadku bardziej niszowych może być trudniej.

Nie ma narzędzia idealnego — są tylko lepiej lub gorzej dopasowane do konkretnego przypadku użycia. Poniżej kilka rzeczy, które konkurencja Buildroota (np. Yocto) robi lepiej:

  • Skalowalność
    • Przy bardzo dużych i skomplikowanych projektach, Buildroot nie daje wystarczająco dużo kontroli nad ustawieniami docelowego systemu. Przykład: urządzenia audio/video obsługujące networking i szyfrowanie, takie jak np. SmartTV.
  • Zarządzanie paczkami
    • Buildroot jest przeznaczony do tworzenia dystrybucji, które raczej nigdy nie będą aktualizowane w czasie życia produktu. Na takiej dystrybucji, żeby wprowadzić zmiany będzie potrzebne zbudowanie obrazu od nowa i ponowne wgranie na metal.

Przygotowanie budowania

Krótka teoria za nami, możemy przystąpić do budowania naszego własnego Linuxa.

Uwaga: Przewodnik pisałem z myślą o Raspberry Pi 4 ale wszystko poza wyborem konfiguracji będzie działać również na innych wersjach tej platformy. Wystarczy tylko wybraź inną konfigurację z listy domyślnych. Wszystko inne będzie działało tak samo, również dla wersji 3b+ i 5. W razie pytań/problemów pisz na karol@linuxdev.pl.

Co będzie potrzebne

Potrzebny sprzęt:

  • Raspberry Pi 4 z wlutowanymi goldpinami
  • Zasilacz do Raspberry Pi
  • Karta SD np. link
  • Adapter do karty SD: np. link
  • Konwerter USB-UART np. link
  • Komputer z Linuxem

Nie jestem powiązany z żadnym z wymienionych sklepów – można także bez problemu użyć zamienników.

Pierwsza konfiguracja

Do użycia Buildroota potrzebujemy pewnych narzędzi deweloperskich. Poniższa komenda zainstaluje je na naszym systemie:

$ apt install -y git build-essential wget cpio unzip rsync bc libncurses5-dev screen

$ oznacza że komenda jest wykonywana w teminalu. Przepisywanie tego symbolu nie zadziała.

Ta komenda jest przewidziana na Ubuntu — jeśli masz inny system, możliwe, że będzie trzeba użyć innego package managera, np. dnf na Fedorze.

Następnie ściągamy źródła Buildroota z githuba:

$ mkdir ~/my_buildroot && cd ~/my_buildroot
$ git clone https://git.buildroot.org/buildroot
$ cd buildroot/

Naszym oczom ukaże się struktura folderów w projekcie:

$ tree -d -L 1
.
├── arch
├── board
├── boot
├── configs
├── docs
├── fs
├── linux
├── package
├── support
├── system
├── toolchain
└── utils

Mamy pobrane gotowe źródła, teraz czas zrobić z nimi coś użytecznego. Musimy tak skonfigurować projekt, żeby wyprodukować obraz Linuxa z takimi paczkami jakie chcemy/potrzebujemy, na nasz konkretny sprzęt.

Zanim zabierzemy się za zaawansowane grzebanie w systemie, skonfigurujemy i stworzymy obraz, który będzie minimalny w swych rozmiarach. Pozwoli to nam zapoznać się z całym procesem krok po kroku.

$ make list-defconfigs

Konfiguracje tylko dla modeli Raspberry Pi możemy znaleźć w taki sposób:

$ make list-defconfigs | grep -i raspberry
  raspberrypi0_defconfig              - Build for raspberrypi0
  raspberrypi0w_defconfig             - Build for raspberrypi0w
  raspberrypi2_defconfig              - Build for raspberrypi2
  raspberrypi3_64_defconfig           - Build for raspberrypi3_64
  raspberrypi3_defconfig              - Build for raspberrypi3
  raspberrypi3_qt5we_defconfig        - Build for raspberrypi3_qt5we
  raspberrypi4_64_defconfig           - Build for raspberrypi4_64
  raspberrypi4_defconfig              - Build for raspberrypi4
  raspberrypicm4io_64_defconfig       - Build for raspberrypicm4io_64
  raspberrypicm4io_defconfig          - Build for raspberrypicm4io
  raspberrypi_defconfig               - Build for raspberrypi
  raspberrypizero2w_defconfig         - Build for raspberrypizero2w

Czym właściwie jest defconfig?

Czas uchylić rąbka tajemnicy, co właściwie dzieje się w tej konfiguracji. Wszystkie pliki konfiguracyjne możemy znaleźć w naszych źródłach, w folderze configs/

$ ls configs/ | grep -i raspberrypi4
raspberrypi4_64_defconfig
raspberrypi4_defconfig

W pliku defconfig znajdziemy zbiór instrukcji dla Buildroota. Będą one dotyczyły tego, w jaki sposób ma być zbudowany obraz, jakiego firmware’u powinniśmy użyć, jakiego procesora itp. Te pliki są świetnym punktem startowym do budowania swojej dystrybucji. Zamiast martwić się podstawowym wsparciem sprzętu, możemy zająć się integracją specyficznych dla naszego projektu paczek.

Budowanie

Jak wspomniałem wcześniej, na początku ograniczymy się do samego domyślnego configa. Wybieramy wersję przeznaczoną na system 64-bitowy, czyli raspberrypi4_64_defconfig. Wpisujemy:

$ make raspberrypi4_64_defconfig

powinniśmy zobaczyć taki komunikat:

#
# configuration written to ~/buildroot/.config
#

Nasza wybrana konfiguracja została wpisana do pliku .config. Możesz ją podejrzeć używając polecenia $ cat config. Możemy też szybko podliczyć, ile linijek zawiera ten plik. Mniej więcej będzie to odpowiadało ilości różnych opcji konfiguracji:

$ wc -l .config 
3904 .config

Ponad trzy tysiące — wyobraź sobie, że trzeba by było wprowadzać to wszystko ręcznie! Na szczęście build system robi większość podstawowej roboty za nas.

Teraz możemy przystąpić do budowania. robimy to za pomocą polecenia make:

$ make 2>&1 | tee build.log

Porada: domyślnie make będzie budował system tylko przy użyciu jednego rdzenia procesora. Jeśli chcesz, żeby budował szybciej, możesz wpisać komendę make -j$(nproc). Wtedy system będzie budował się na wszystkich dostępnych rdzeniach.

Po zakończeniu budowania w głównej ścieżce pojawi się nowy katalog – output/. To w nim znajdują się wygenerowane pliki. Nas najbardziej interesuje output/images. To tam znajduje się obraz gotowy do wrzucenia na kartę SD, kernel, rootfs i wszystkie inne potrzebne pliki. Na przykład firmware, który w Raspberry pełni funkcję bootloadera, czyli programu, który inicjalizuje sprzęt, zanim kernel zostanie załadowany.

Test i wgranie na Raspberry Pi

Przygotuj kartę SD i adapter. Jeśli chcesz wiedzieć, jak wypalić obraz i zestawić połączenie z interfejsem szeregowym, polecam jeden z poprzednich wpisów: https://linuxdev.pl/poradnik-uart-raspberry/ znajdziesz w nim szczegółowy poradnik jak wszystko połączyć.

Wgrywamy obraz na kartę SD. Można do tego użyć programu z GUI np. Balena etcher albo z pomocą linii komend:

$ dd if=output/images/sdcard.img of=/dev/<DEVICE>

Koniecznie zestaw połączenie szeregowe z płytką – na razie to jedyna możliwość komunikacji z naszym targetem. Po włączeniu zasilania na konsoli szeregowej powinieneś zobaczyć log z bootowania płytki. Będzie wyglądał mniej więcej tak:

[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
/*reszta logów*/
Welcome to Buildroot
buildroot login:

Wpisujemy domyślną nazwę użytkownika: root i to wszystko! Mamy dostęp do naszego własnego Linuxa na Raspberry Pi. Bez żadnej zewnętrznej dystrybucji, wszystko zrobione „od zera”. Oczywiście, nic nie da się tak naprawdę zrobić od zera, tak jak pan Cagan powiedział:

Buildroot tworzy minimalistyczne obrazu Linuxa. Możemy to potwierdzić badając nasz świeżo zbudowany system. Mamy dostęp jedynie do bardzo podstawowych narzędzi takich jak cat, ping i grep. Jedyny edytor tekstowy na pokładzie to VI, nie ma też żadnego trybu graficznego ani SSH. Prompt systemowy również nie robi wrażenia, nie wyświetla się nawet ścieżka, w której obecnie jesteśmy, tylko i wyłącznie #. Połączenie z internetem również nie będzie działać od razu, trzeba je osobno skonfigurować.

Wniosek jest jeden — czas zmodyfikować nasz obecny obraz i zrobić coś ciekawego.

Modyfikacja obrazu

W naszym obecnym obrazie zrobimy następujące zmiany:

  • Dodamy edytor tekstowy nano
  • Sprawimy, żeby płytka łączyła się z WiFi automatycznie po włączeniu
  • Oraz dołożymy obsługę dla SSH

Dodajemy nano

Na razie jedyny edytor tekstowy jaki mamy na pokładzie to vi. Wystarczy dla wszystkich możliwych zastosowań, ale nie ukrywajmy, nie jest to najprostszy program do obsługi. Dodamy jeden z najbardziej user-friendly edytorów jaki istnieje, czyli nano. Żeby dodać nową paczkę korzystamy z $ make menuconfig. Za pomocą strzałek oraz klawiszy ENTER i SPACJA możemy wybrać, które programy znajdą się w końcowym obrazie systemu. Po wywołaniu make menuconfig nawiguj do menu:

Target packages → Text editors and viewers

Najedź strzałkami na pozycję o nazwie nano i zatwierdź wybór naciskając spację. Przy nazwie programu powinna pojawić się gwiazdka, jak na obrazku poniżej.

I to tyle. Strzałką w prawo najedź na opcję EXIT i potwierdź zapisanie konfiguracji klawiszem ENTER.

Hasło dla użytkownika root

Póki co nasz system jest dość niezabezpieczony, nie mamy nawet ustalonego hasła dla użytkownika root. Musimy to zmienić, szczególnie że bez hasła nie będzie się dało połączyć po SSH. Choć istnieje na to obejście… ale to nie temat na ten wpis 🙂

Żeby ustawić hasło dla root’a, zmieniamy pozycję w make menuconfig, w menu System configuration.

Połączenie z siecią i SSH

Naszym celem jest włączenie WiFi automatycznie po każdym starcie systemu. Umożliwi to połączenie się po SSH z naszą maliną. Im mniej kabli, tym lepiej.

Żeby to osiągnąć musimy dodać odpowiednie sterowniki dla kernela, wsparcie devtmpfs oraz paczki wpa_supplicant i dhcpd. Omówienie wszystkiego szczegółowo to temat na cały osobny wpis. Tutaj przedstawię skróconą instrukcję jak wszystko zrobić.

Zaczynamy od $ make menuconfig i zaznaczamy następujące paczki:

  • Firmware, czyli brcmfmac-sdio-firmware-rpi -> brcmfmac-sdio-firmware-rpi-wifi (w menu Target packages -> Hardware handling -> Firmware)
  • wpa_supplicant
  • dhcpd
  • dropbear

I jeszcze jedna rzecz – WiFi na Raspberry wstaje dość późno, więc po zainstalowaniu drivera musielibyśmy ręcznie go ładować przez użycie modprobe. Zamiast tego możemy zautomatyzować ten proces poprzez włączenie mechanizmu, który zarządza sprzętem hotplug, czyli MDEV. Znajdziemy go w menu System configuration -> /dev management -> Dynamic using Devtmpfs + mdev.

Trochę tej konfiguracji trzeba żeby mieć połączenie bezprzewodowe… Teraz czas na dopisanie potrzebnych kawałków kodu, zaczynamy od wpa_supplicant. Tworzymy konfigurację dla naszej sieci – musimy podać odpowiednią nazwę naszego wifi i hasło.

$ mkdir -p board/raspberrypi4/rootfs_overlay/etc

# Tworzymy plik wpa_supplicant.conf
$ cat > board/raspberrypi4/rootfs_overlay/etc/wpa_supplicant.conf <<EOF
update_config=1
country=PL

network={
    ssid="NAZWA SIECI"
    psk="HASLO"
    key_mgmt=WPA-PSK
}
EOF

Czas na skrypt, dzięki któremu WIFI będzie wstawało automatycznie po starcie systemu. Tworzymy plik tekstowy i wpisujemy do niego konfigurację:

$ nano board/raspberrypi4/rootfs_overlay/etc/init.d/S40wifi

#!/bin/sh

start() {
    echo "Starting WiFi..."
    wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf
    dhcpcd wlan0
}
stop() {
    echo "Stopping WiFi..."
    killall wpa_supplicant
}
restart() {
        stop
        start
}

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart|reload)
restart
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|reload}"
    exit 1
esac

exit 0

Nadajemy skryptowi uprawnienia do wykonywania:

$ chmod +x board/raspberrypi4/rootfs_overlay/etc/init.d/S40wifi

Przed chwilą stworzyliśmy tzw. rootfs overlay, czyli naszą własną nakładkę na domyślny system plików. Jest to najłatwiejszy sposób żeby wprowadzać swoje zmiany w systemie takie jak np. tworzenie nowych plików konfiguracyjnych. Teraz musimy powiedzieć build systemowi, że wprowadziliśmy takie zmiany i gdzie konkretnie się znajdują. Robimy to w menuconfig:

System configuration  --->
    Root filesystem overlay directories
        (board/raspberrypi4/rootfs_overlay)

I ostatnia rzecz – ponieważ dodaliśmy sporo nowych rzeczy, musimy przebudować projekt od nowa, przeprowadzić tzw. full rebuild. W innym wypadku nasz system nie dojdzie nawet do promptu logowania. Możemy to zrobić komendą:

$ make clean all 

Testujemy finalny obraz

Czas na przetestowanie systemu po modyfikacjach. Wgrywamy obraz na kartę SD dokładnie tak, jak robiliśmy poprzednio i obserwujemy zachowanie systemu.

Jedna rzecz prawdopodobnie od razu rzuci ci się w oczy – start systemu trwa o wiele dłużej, nawet o kilkanaście sekund. Zmiany dotyczące wifi i innego systemu plików spowodowały dodanie kilku nowych kroków do inicjalizacji naszej platformy. Taka cena korzystania z dobrodziejstw networkingu.

Po krótkiej chwili od zalogowania możesz sprawdzić stan połączenia do sieci:

$ ping 8.8.8.8

Zedytować plik za pomocą nowego edytora:

$ nano test.txt

I spróbować połączyć się ze swojego komputera do Raspberry Pi za pomocą SSH:

$ ssh root@<IP>

Skąd wziąć adres IP? Zanim porzucimy połączenie szeregowe po kablach, możesz zalogować się na Raspberry Pi po starcie sieci i wywołać ifconfig:

$ ifconfig wlan0

Możesz również zrobić to z komputera, wywołując polecenie $ ip neigh i patrząc które IP pojawiło się jako nowe w sieci (najlepiej wywołać to polecenie przed i po połączeniu Raspberry z WiFi).

Podsumowanie i kod

Teraz już wiesz jak zacząć przygodę z Buildrootem. Na początku warto przetestować budowanie domyślnej konfiguracji i jak wygląda proces wgrywania stworzonego obrazu na kartę SD albo inny nośnik, krok po krok. Gdy to już mamy opanowane, można dokładać kolejne elementy układanki, od prostych paczek jak np. edytor nano aż po całkiem złożone rzeczy – jak np. networking.

Buildroot nie zawsze będzie optymalnym rozwiązaniem. Jeśli nie interesuje nas szybki start platformy i chcemy mieć dużo programów zainstalowanych od razu, można rozważyć zostanie przy fabrycznym Raspbianie. Gdy chcemy mieć system budowania wspierający różne platformy sprzętowe i bardzo szczegółową kontrolę na wszystkim – wtedy optymalnym wyborem może być Yocto. Buildroot ląduje gdzieś pomiędzy tymi rozwiązaniami i dzięki temu jest szeroko stosowany w zastosowaniach komercyjnych.

Jeśli chcesz zacząć tworzyć Linuxa na systemy embedded bez sprzętu – sprawdź ten wpis: https://linuxdev.pl/emulacja-raspberry-pi-z-qemu/

Kod wykorzystany w tym poradniku znajdziesz TUTAJ

Konfiguracja z pliku .config TUTAJ – gdy wszystko zawiedzie, możesz skopiować ten plik do swojego katalogu buildroot/, zmienić nazwę na .config i zbudować obraz. Musi zadziałać.

Powodzenia w tworzeniu swoich własnych systemów wbudowanych! To dopiero początek podróży 🙂


2 odpowiedzi na „Buildroot dla Raspberry Pi krok po kroku”

  1. Awatar HiFiberryOS Master z GitHub
    HiFiberryOS Master z GitHub

    Serwus,

    nie jestem informatykiem i to wszystko jest za trudne od momentu jak nie nasladuję kroków w waszym podręczniku.

    Mój cel to zrobić .iso do instalacji na micro-SD z https://github.com/hifiberry/hifiberry-os na podstawie hifiberry-os-master.zip.

    Mam już zaistalowany buildroot i zrobiłem make raspberrypi4_defconfig.
    Ale nie wiem jak dalej tam wsadzić ten HiFiberryOs master i dalej uzyskać hifiberryOS.iso.

    Są instrukcje w Master jak to robić, ale nie umiem tego przenieść na raspberry 4 Pi czy Ubuntu/Mate.

    Może ktoś mi zrobiłby ściągę. buildroot mam w katalogu AKObuildfoot w /tolo/home.

    Tymczasem i dzięki

    Anton

    1. Awatar Karol

      Cześć, z tego co widzę w dokumentacji z linka, którego wysłałeś, generowanie configu powinno wyglądać tak jak tutaj: https://github.com/hifiberry/hifiberry-os/blob/master/doc/building.md czyli z użyciem skryptu build-config znajdującego się w repozytorium. Jeśli nie ma opcji dla RPi 4 to najlepiej zajrzeć jak jest to zrobione dla RPi 3 w skrypcie i pozmieniać odpowiednie opcje. Nie powinno ich być bardzo dużo.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *