Budowa Linuksa na MMneta1002

Płyta ewaluacyjna MMnet1002 firmy Propox jest świetną zabawką dla miłośników Linuksa zainteresowanych jego wykorzystaniem w systemach wbudowanych. Od kilku tygodni eksperymentuję z tą platformą i próbuję zbudować własnego Linuksa z użyciem narzędzi takich jak Buildroot. Płyta jest podobna do atmelowskiej AT91SAM9260-EK, jednak występują pewne różnice, które sprawiają, że nie sposób wykorzystać bez zmian pliki konfiguracyjne przewidziane dla AT91SAM9260-EK. Dotyczy to w szczególności MMneta z pamięcią NANDFlash — 1GiB pamięci nieulotnej to świetna sprawa, ale podstawową i najbardziej popularną konfiguracją jest procesor połączony z pamięcią DataFlash, słusznie należy więc oczekiwać, że czeka nas trochę programistycznej gimnastyki. Nie obędzie się też bez patchowania źródeł, ale w zamian otrzymamy system operacyjny, nad którego każdą częścią będziemy jako-tako panowali i nie będziemy skazani tylko na dostarczaną przez Propox binarną wersję dystrybucji OpenWRT.

Poniższy tutorial nie jest zbyt szczegółowy, zakładam, że Czytelnik pracuje na Linuksie i umie się nim posłużyć w podstawowym zakresie, a także, że zapoznał się z dokumentacją rzeczonej płyty ewaluacyjnej i umie posługiwać się programatorem SAM-BA.

Buildroot

Buildroot jest zestawem Makefile’i i patchy, ułatwiających budowę toolchaina z cross-kompilatorem, jądra Linux i głównego systemu plików z BusyBoksem na systemy wbudowane, na których potrzebujemy lekkiego systemu operacyjnego opartego o uClibc w miejsce GNU libc. Eksperymentowałem wprawdzie ze środowiskiem wyrosłym z Buildroota — OpenEmbedded — jest ono bardziej rozbudowane, ale dla mnie mniej wygodne. Nie udało mi się zresztą skompilować w nim działającego jądra, zdecydowałem się więc na Buildroota.

Wadą Buildroota są problemy z kompilacją tylko wybranej części systemu. W zasadzie powinniśmy zawsze kompilować wszystkie składniki, łącznie z toolchainem. Niestety, wciąż nie udało mi się wydzielić budowy systemu plików jako oddzielnego zadania; jedynie jądro z powodzeniem udawało mi się rekompilować bez budowania od nowa całości, dzięki czemu proces kompilacji trwał najwyżej kilkanaście zamiast kilkudziesięciu minut.

Po co nam dwa systemy?

Ze względu na pewne problemy w działaniu i powolne przesyłanie danych chciałem w miarę możliwości pozbyć się konieczności używania programatora SAM-BA. Poza tym SAM-BA raczej nie nadaje się do flashowania obrazów UBIFS, co wyjaśnię dalej. Zdecydowałem się zatem na następującą metodologię.

Budujemy dwa systemy: pierwszy zawiera tylko podstawowe narzędzia, nie posiada oddzielnego obrazu systemu plików, ale ma wkompilowany intramfs, dzięki czemu całość pracuje tylko w RAM-ie i może swobodnie zarządzać całym obszarem pamięci NANDFlash. Drugi to nasz właściwy system, który zawiera wszystkie narzędzia na obrazie UBIFS, a jego jądro nie posiada ani obrazu initramfs, ani initrd, dlatego może on wstać tylko z pamięci nieulotnej.

Pierwszy system będzie pobierany przez bootloader z sieci lokalnej i uruchamiany tylko po to, by wgrać ten drugi. Taka sytuacja będzie jednak miała miejsce tylko podczas rozwijania naszych projektów — podczas codziennej pracy od razu będzie uruchamiany drugi system, wstający z pamięci nieulotnej.

Budowa obu systemów

Po pobraniu i rozpakowania Buildroota uruchamiamy polecenie

$ make menuconfig

Uruchomi się konfigurator, który nas jednak w tej chwili nie interesuje — chcieliśmy tylko, by zostały utworzone dodatkowe pliki konfiguracyjne, niezbędne do dalszej pracy. Zamykamy więc program, wybierając Exit i nie zapisują zmian. Ścieżkę do Buildroota ustawiamy w zmiennej BUILDROOT w skrypcie Buildroot/build_root z mojego archiwum. Skrypt ten jest protezą, którą stworzyłem w miejsce własnej płytki, którą powinienem był dodać do środowiska Buildroota. Wygodniej mi jednak było wykorzystać pliki konfiguracyjne płytki AT91SAM9260-EK, na które skrypt nanosi pewne poprawki. Poprawki te zgromadzone są w katalogach mmnet1002 i mmnet1002-initramfs:

config
główny plik konfiguracyjny zastępujący .config Buildroota.
linux26-config
plik konfiguracyjny z wytycznymi kompilacji dla jądra.
target_skeleton
katalog z systemem plików (z wyjątkiem urządzeń w /dev).
device_table.txt
lista urządzeń, które będą dostępne w /dev.
ubifs.cfg
konfiguracja obrazu UBIFS.
dl
patche, które zostaną dodane do źródeł w katalogu dl Buildroota.

(Opracowując poprawki bazowałem między innymi na tutorialu, który opisuje, inne, być może rozsądniejsze, podejście do modyfikacji Buildroota na potrzeby MMneta).

Budowa pierwszego systemu sprowadza się do:

$ ./build_root all mmnet1002-initramfs

zaś drugiego do:

$ ./build_root all mmnet1002

Po kompilacji trwającej każdorazowo zapewne od 30 do 60 minut, powinniśmy otrzymać obrazy systemów w podkatalogach images-mmnet1002-initramfs i images-mmnet1002, które znajdą się w tym samym katalogu, w którym umieściliśmy skrypt build_root.

Za każdym razem skrypt będzie chciał usunąć podkatalog output w katalogu Buildroota — należy mu na to zezwolić po upewnieniu się, że nie usuwamy sobie np. home’a (jedna zbędna spacja w zmiennej BUILDROOT i może dojść do tragedii).

Warto też na początku zastąpić opcję all opcją source, skrypt powinien wtedy tylko pobrać niezbędne źródła, bez budowania ich. Polecam to zwłaszcza użytkownikom powolnych i niepewnych łączy.

Partycje MTD i woluminy UBIFS

Na tym etapie warto zwrócić uwagę na plik linux-2.6.32.9-mmnet1002.patch w katalogu dl. Jest to patch, który odpowiada między innymi za podział Flasha na partycje MTD. Nie są to partycje w ścisłym tego słowa znaczeniu. Urządzenia MTD, a więc surowe pamięci Flash, w przeciwieństwie do np. kart SD czy pendrive’ów nie są urządzeniami blokowymi (raczej czymś pośrednim między urządzeniem blokowym a znakowym) i nie mogą być obsługiwane przez tradycyjne systemy plików. Nie zapisujemy też na nich tablicy partycji — podział surowej pamięci Flash jest umowny i może być zapisany na stałe w jądrze lub podawany jako argument mtdparts przez bootloader. Wybrałem to pierwsze rozwiązanie, a podział zachowałem taki sam jak w fabrycznej wersji:

0x00000000-0x00040000 : "bootstrap"
0x00040000-0x00080000 : "u-boot"
0x00080000-0x00200000 : "u-boot-env"
0x00200000-0x00800000 : "kernel"
0x00800000-0x40000000 : "filesystems"

Jako system plików dla MTD wybrałem UBIFS, również tak, jak w rozwiązaniu fabrycznym, z tym, że poprzestałem na jednym woluminie UBI. Do wygenerowania obrazu UBI niezbędne są programy mkfs.ubifs i ubinize, które użytkownicy Arch Linuksa znajdą w AUR w pakiecie mtd-utils-git. Pierwszy z tych programów wymaga uprawnień roota (poza tym musimy operować na plikach urządzeń, co też wymaga uprawnień superużytkownika), dlatego też skrypt build_root pyta się o to hasło przed stworzeniem obrazu systemu plików.

Należy pamiętać, że odradza się bezpośrednie flashowanie obrazów UBI, a preferuje się wykorzystanie programu ubiformat, gdyż daje to gwarancję, że system plików poprawnie obsłuży baderaseblocki. Jest to jeden z powodów, dla których starałem się odejść od stosowania SAM-B-y.

Bootloadery

AT91Bootstrap to bootloader pierwszego stopnia, którego najważniejszym zadaniem jest wczytanie większego i znacznie bardziej funkcjonalnego U-Boota. Nie możemy wczytać od razu tego drugiego, ponieważ ROM Boot Program mikrokontrolera może udostępnić bootloaderowi pierwszego stopnia tylko 4KB wbudowanej pamięci RAM. Z tego też względu należy upewnić się, że skompilowany AT91Bootstrap ma mniej niż 4KB!

Do kompilacji obu bootloaderów niezbędny jest cross-kompilator, np. polecany przez twórców U-Boota ELDK. My jednak nie będziemy zaśmiecać sobie dysku i wykorzystamy toolchaina skompilowanego przez Buildroota. W tym celu należy ustawić odpowiednio zmienną PATH w skryptach build_at91bootstrap i build_u-boot. Przy okazji należy zmodyfikować też odpowiednio ścieżki do źródeł obu programów. Źródła z kolei trzeba przed kompilacją spatchować:

Bootstrap-v1.15-mmnet1002.patch
zawiera poprawki uwzględniające różnice między MMnetem1002 a AT91SAM9260-EK (m.in. inną pamięć nieulotną i inny pin przywracania SAM-B-y).
u-boot-2009.11.1-mmnet1002.patch
zawiera poprawki dla interfejsu sieciowego i domyślnych parametrów uruchamiania systemu.

Podobnie jak w przypadku Buildroota, skrypty możemy umieścić w zupełnie innym miejscu niż źródła. Po zbudowaniu obu aplikacji ich obrazy binarne zostaną skopiowane do katalogów, w których znajdują się odpowiednie skrypty.

SAM-BA

Mamy już wszystkie obrazy, więc możemy przystąpić do flashowania. W katalogu Flashing znajdziemy skrypt flash_now. Najpierw sprawdźmy, czy zmienna SAMBA wskazuje na plik wykonywalny SAM-B-y. Następnie skopiujmy do wspomnianego katalogu obrazy obu bootloaderów, tj. at91bootstrap.bin i u-boot.bin. Po uruchomieniu skrypt przekaże do SAM-B-y instrukcje zawarte w MMnet1002_prog.tcl i wgra do pamięci NANDFlash oba bootloadery oraz zmienne środowiskowe U-Boota. Należy przy tym zauważyć, że:

  1. SAM-BA automatycznie generuje plik z ustawieniami U-Boota (u-boot-env.bin).
  2. SAM-BA, a później U-Boot (dzięki u-boot-env.bin) mają rozmiar drugiego jądra ustawiony na sztywno na 2MiB (0x200000B). Wynika to z tego, iż podczas dalszej pracy nowe jądra będą wgrywane bez udziału SAM-B-y i nie będzie ona mogła aktualizować ustawień U-Boota. Wartość 2MiB dla jądra bez initrafms zawiera spory zapas (przykładowe jądro waży tylko 1,2MiB), a nie ma sensu wczytywanie 6MiB całej partycji, gdyż zauważalnie wydłuża to czas startu systemu.
  3. Do U-Boota zostaje dodane polecenie upgrade, które posłuży do wgrywania nowych wersji systemu.

TFTP

Przed wgraniem systemu z sieci należy upewnić się, że mamy uruchomiony serwer DHCP i TFTP pod adresem 192.168.42.1, gdzie powinna też być brama sieciowa. U-Boot bowiem będzie szukał obrazów pod adresem bramy sieciowej, zaś skrypt /root/upgrade konkretnie pod wspomnianym IP. Jeżeli chcielibyśmy zastosować inne rozwiązanie (np. hardkodować w obu przypadkach wszystkie adresy sieciowe), należy zmodyfikować polecenie upgrade w U-Boocie i skrypt /root/upgrade w obu wersjach systemu.

W katalogu serwera TFTP na naszym PC-cie (np. /var/tftpboot) należy umieścić te pliki, które będziemy chcieli zaktualizować w systemie. Na początek wgramy wszystko na nowo, skopiujmy więc:

  • z katalogu Flashing lub katalogów odpowiednich bootloaderów: at91bootstrap.bin, u-boot.bin, u-boot-env.bin,
  • z katalogu images-mmnet1002: uImage i rootfs.ubi,
  • z katalogu images-mmnet1002-initramfs: uImage, którego nazwy nie zapomnijmy zmienić na uImage-initramfs!

Teraz uruchamiamy MMneta. Po chwili U-Boot poskarży się nam, że nie znalazł jądra, jednak nie zważając na to wpiszmy w jego wierszu poleceń:

U-Boot> run upgrade

Teraz U-Boot uzyska niezbędne adresy IP z DHCP, pobierze pierwszą wersję systemu (uImage-initramfs), która natychmiast po wystartowaniu uruchomi skrypt /root/upgrade, pobierający i instalujący właściwą, drugą wersję systemu. Po chwili płyta zrestartuje się, a z NANDFlasha powinien wstać nowy system, który po wygenerowaniu kluczy dla Dropbeara będzie gotowy do pracy.

Teraz, gdy mamy system na NANDFlashu, jądro możemy aktualizować za pomocą polecenia:

# /root/upgrade

Nie da się jednak w ten sposób zaktualizować głównego systemu plików — należy zrestartować płytę i wywołać polecenie upgrade w U-Boocie.

Uwagi końcowe

Jak wspomniałem, powyższe rozwiązanie to raczej partyzantka. Warto byłoby wszystko dodać do Buildroota jako oddzielną płytkę. Także oba bootloadery mógłby budować Buildroot, jednak miał u mnie z tym pewne kłopoty, więc uznałem, że wygodniej będzie to robić ręcznie.

Co do SAM-B-y, to na Arch Linuksie nie chciała działać, dlatego zmuszony jestem ją uruchamiać na Xubuntu w wirtualnej maszynie.

W załączonym archiwum znajdziecie skrypty, patche, gotowe obrazy i logi z SAM-B-y oraz startu obu wersji systemu, które mogą pomóc w wyśledzeniu błędów.

Pliki

mmnet1002-buildroot-vmario.tar.bz2
archiwum z opisywanymi skryptami i dodatkami.

Komentarzy: 10 do „Budowa Linuksa na MMneta1002“

  1. PrzemCio komentuje:

    Od jakiegoś czasu zastanawiam się nad zainwestowaniem w tę płytkę z propoxu…

    dziękówa za ten wpis i pisz dalej jakie aplikacje będziesz robił :)

  2. Czaro komentuje:

    Hej, a czy działa Ci funkcja „hwclock” ? Mam problemy z ustawieniem RTC na tej płytce. Po prostu po restarcie cofam się do 1970 roku :)

  3. vmario komentuje:

    Upewnij się, że bateria podtrzymująca RTC jest w porządku i sprawdź, czy w /etc/TZ masz polską strefę czasową (MET-1METDST-2,M3.5.0/02:00:00,M10.5.0/03:00:00). Następnie ustaw czas:

    $ date 2010.04.20-10:26:15

    i prześlij go do RTC (zakładam, że RTC pracuje w UTC):

    $ hwclock -w -u

    Później prawdź czas według RTC (obecnie będzie o dwie godziny wcześniejszy od naszego czasu letniego):

    $ hwclock -r

    Odłącz zasilanie, odczekaj chwilę, włącz powtórnie i sprawdź, czy RTC poprawnie przez ten czas pracował.

    U mnie RTC działa, choć występują dwa problemy: jest trochę mało dokładny… ;), a zapisywanie czasu za pomocą hwclock trwa nawet kilkanaście sekund, choć w OpenWRT odbywało się natychmiast.

  4. Czaro komentuje:

    Ja mam oryginalnego OpenWrt który był dostarczony z MMnetem.
    Jak próbuję użyć hwclock to dostaję:

    root@MMnet:/ hwclock -w
    RTC_RD_TIME: Invalid or incomplete multibyte or wide character
    ioctl() to /dev/rtc to read the time failed.

    W pliku /etc/TZ mam wpisane UTC. Próbowałem go podmienić Twoim wpisem, ale nie działa to u mnie. Datę i czas mogę ustawić, ale nie udaje mi się połączyć tego z RTC. Dzięki za i tak poświęcony czas.

  5. Czaro komentuje:

    Chyba dam sobie spokój na razie z tym ustawianiem czasu, bo znalazłem info na stronie z płytką z Olimexu o braku wsparcia dla L9260 i jego RTC:
    http://www.olimex.com/dev/pdf/ARM/ATMEL/SAM9-L9260.pdf
    Także pozostaje mi wierzyć, że kiedyś ktoś to zrobi.

  6. hashishin komentuje:

    @Czaro – co do hwclock, sprawdź http://www.elektroda.pl/rtvforum/viewtopic.php?t=1645427

  7. piotreq komentuje:

    Chciałbym się z Tobą skontaktowac jednak wysyłając maila na vmario na vmario.org dostaje zwrotki.

  8. vmario komentuje:

    @piotreq: Mam problemy z tym kontem. Pisz, proszę, na adres vmario1986 na gmail.com.

  9. Jacek komentuje:

    Zainstalowałem sobie z binarek na MMnecie 1002 uzywajac samby na windowsie. Narazie śmiga, fajne.

  10. Krychu komentuje:

    Fajne rozwiązanie od jakiegoś czasu szukałem takiego poradnika. Dzięki za tak dokładny opis na pewno będę wracał:)

Dodawanie komentarzy

XHTML: Możesz używać tagów: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">