Lekcja 03- Kernel

Wstęp

W 1991 Torvalds poinformował na grupie dyskusyjnej systemu Minix, że tworzy swój kernel systemy operacyjnego przeznaczonego na procesory i386 oraz i486. Kernel ten został nazwany później Linuksem. Specjalnie podkreśliłem słowo kernel aby wskazać, że Linux to tak de facto nie jest całym systemem operacyjnym tylko jego jądrem(kernelem).

Kernel to mózg systemu operacyjnego. To właśnie on odpowiada za zarządzenie zasobami, komunikację ze sprzętem oraz dostarczenie warstwy abstrakcji dla aplikacji użytkownika.

Architektura kernela Linuksa

Wyróżniamy dwie architektury kerneli systemów operacyjnych- mikrokernele oraz kernele monolityczne. W przypadku mikrokernela sam kernel odpowiada za bardzo nie wiele zadań, zazwyczaj jest to zarządzanie przydziałem procesora oraz za komunikację pomiędzy procesami. Wszelkie sterowniki nie stanowią części kernela tylko oddzielne procesy. W przypadku kernela monolitycznego kernel posiada wiele więcej funkcji oprócz szeregowania zadań i komunikacji pomiędzy procesami. Taki kernel obsługuje również komunikację z wszelkimi peryferiami sprzętowymi czy obsługę odpowiednich systemów plików.

Jaka jest różnica w działaniu tych kerneli? Dla nas najważniejsza będzie chyba stabilność systemu. W przypadku kernela monolitycznego gdy wystąpi błąd w jednym podsystemie np. w sterowniku myszki może to spowodować zawieszenie całego systemu natomiast w przypadku mikrokernela tylko moduł odpowiedzialny za pracę myszki zakończy działanie bez wpływu na resztę systemu. Kolejną cechą, którą możecie zauważyć to rozmiar kernela. Mikrokernel będzie z reguły mniejszy ze względu na niewielką ilość zadań, którą się zajmuje, kernel monolityczny będzie oczywiście odpowiednio większy.

Linux ma architekturę monolityczną.

Moduły

Wiele z części(podsystemów) kernela może zostać wkompilowana do jego obrazu lub zostać skompilowana jako moduł. Moduł to specjalny plik binarny, który może zostać włączony do kernela podczas jego pracy. Jakie są różnice pomiędzy wkompilowywaniem wszystkiego do kernela a używaniem modułów? W przypadku wkompilowywania wszystkiego do kernela nie musimy się martwić o ładowanie modułów i o potrzebne zależności niezbędne do jego działania. Wadą takiego rozwiązania może być niekiedy czas startowania systemu i ewentualnie rozmiar kernela. W takim przypadku jakakolwiek zmiana ustawień kernela będzie wymagała jego ponownej kompilacji i jego podmiany.

Co do modułów, na pewno umożliwiają one większą elastyczność pracy, możemy je ładować gdy są potrzebne i odmontowywać gdy nie są już potrzebne, przeniesienie części funkcjonalności do modułów umożliwia również zmniejszenie czasu potrzebnego do uruchomienia systemu. Używanie modułów daje nam duże pole do konfiguracji kernela bez potrzeby jego ponownej kompilacji. Wadą takiego rozwiązanie jest potrzeba spełnienia wszystkich zależności aby dany moduł mógł pracować jak np. załadowanie innych modułów od których ładowany moduł jest zależny.

Kod źródłowy kernela

Kod źródłowy kernela można pobrać ze strony kernel.org w postaci paczki tar.xz lub z repozytorium torvalds/linux dostępnego na githubie.

Z istotnych rzeczy trzeba zwrócić uwagę na dwa typy wersji wydań(release’ów) kodu Linuksa. Mamy wersję main, która cały czas żyje, do której dodawane są cały czas nowe funkcje, poprawki itd. Ta wersja kodu może nie zawsze działać. Drugim typem jest stable czyli mówiąc po polsku wersja stabilna. Do tego typu źródeł będą wchodzić tylko przetestowane zmiany dzięki czemu możemy oczekiwać, że Linux przebudowany z takich źródeł będzie działał tak jak tego chcemy(choć wiadomo, jakiś błąd może czasami przejść). W przypadku wersji stabilnej można wyróżnić jeszcze wersje LTS(Long Term Support), jest to kod danej wersji Linuksa, która jest utrzymywana przez dłuższy czas np. 3 lata.

Co do samych źródeł to nas póki co najbardziej będą interesowały katalogi arch oraz Documentation. Katalog arch zawiera pliki charakterystyczne dla danych architektur jak np. konfiguracje, a Documentation jak się pewnie domyślacie zawiera dokumentacje poszczególnych elementów Linuksa.

Konfiguracja kernela

Kernela rzadko konfiguruje się od zera, każdy wydawca SoCa, który chce wspierać Linuksa dostarcza plik defconfig do źródeł dzięki czemu mamy dostępną od ręki wiele konfiguracji dla wielu urządzeń. Aby użyć konkretnej konfiguracji musimy skorzystać z narzędzia make i wykonać komendę, która będzie wyglądać mniej więcej tak:

make ARCH=architektura CROSS_COMPILE=prefix-kompilatora- soc_defconfig

Parametr ARCH to oczywiście docelowa architektura procesora na którą budujemy kernel, a CROSS_COMPILE to przedrostek dla naszego kompilatora. Ostatni parametr to domyślna konfiguracja, której chcemy użyć. Na przykład, dla płytki BBB wywołanie takiej komendy wyglądało by następująco:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- omap2plus_defconfig

Teraz byśmy mogli skompilować kernel i bylibyśmy w stanie uruchomić go. Ale co jeśli byśmy chcieli coś zmodyfikować np. dodać jakiś sterownik albo przeciwnie, usunąć jakiś sterownik. Kernel dostarcza narzędzie menuconfig dzięki któremu konfiguracja kernela jest stosunkowo przyjazna dla człowieka. Aby uruchomić menuconfig znów używamy polecenia make(przykład dla BBB):

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

Pamiętaj aby zawsze wskazywać ARCH oraz CROSS_COMPILE, inaczej może to spowodować błędy w konfiguracji, ewentualnie możesz wyeksportować te parametry jako zmienne środowiskowe.

Po wykonaniu tej komendy powinieneś ujrzeć widok podobny do tego na poniższym rysunku:

Tutaj powinien być menuconfig Linuksa
Konfigurator Linuksa

Jest tam wiele różnych opcji, od sterowników po różne kernelowe haki(sekcja na samym dole menuconfiga). Opcje mają różną formą, niektóre wymagają aby podać jakieś ustawienie tekstowo lub liczbowo, ale najpopularniejsze są chyba opcje „tristate”. Jak nazwa sama wskazuję są opcje które mogą mieć trzy stany: wkompilowany w kernel(wtedy taka opcja jest zaznaczona znakiem asterisk(*) z lewej strony), kompilowana jako moduł(wtedy taka opcja jest zaznaczona jako M z lewej strony) oraz nie używane, wtedy nie ma nic z lewej strony takiej opcji.

Czasami jesteśmy zainteresowani tylko zmianą jednej opcji, ale nie do końca wiemy gdzie się ona znajduje w strukturze w menuconfiga, w takim przypadku możemy skorzystać z wyszukiwania. Aby włączyć tryb wyszukiwania wciskamy klawisz ukośnika(/) i możemy wyszukać interesującej nas opcji.

Dalsza lektura

To tyle co trzeba wiedzieć aby przejść do kolejnego etapu tej lekcji czyli budowania własnego kernela Linuksa. Osoby głębiej zainteresowane tematyką kernela mogą rzucić okiem na książkę „Jądro Linuksa” autorstwa Roberta Love. Książka nie jest najnowsza, ale ma jedną zasadniczą zaletę- jest po polsku. Zmiany w kernelu również nie są tak głębokie aby książka ta zdeaktualizowała się całkowicie, daje ona dobry ogląd na temat tego jak działa Linux.

A teraz przechodzimy już do praktyki, dla każdej platformy została przygotowana oddzielna sekcja więc jeśli jesteś zainteresowany tylko jedną platformą możesz przeskoczyć od razu do odpowiedniej sekcji.

Zanim przejdziesz do części praktycznej przypominam aby ścieżka z kompilatorami znajdowała się w zmiennej środowiskowej PATH.

BeagleBone Black

Aby zbudować kernel trzeba mieć oczywiście jego źródła, pobierzmy je i od razu rozpakujmy:

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.1.tar.xz
tar xf linux-5.14.1
cd linux-5.14.1

Teraz będąc już w katalogu z kodem możemy ustawić domyślną konfigurację dla BBB:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- omap2plus_defconfig

W zasadzie nie musimy nic zmieniać w konfiguracji kernela, ale abyś miał pewność, że to Twój świeżo zbudowany kernel się uruchamia zmodyfikujemy opcję LOCALVERSION w menuconfig. Uruchom więc konfigurator:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

Włącz tryb wyszukiwania poprzez wciśnięcie klawisza ukośnika(/), wpisz w pole LOCALVERSION i zatwierdź poprzez wciśnięcie Entera. Powinieneś zobaczyć dwa wyniki:

Tutaj powinny być wyniki wyszukiwania w menuconfigu
Wyniki wyszukiwania w menuconfigu

Każdy wynik wyszukiwania ma przypisany numer, który jest widoczny u dołu każdego znaleziska. Nas interesuje pierwszy wynik, wciśnij więc klawisz 1 na swojej klawiaturze. Zostaniesz przeniesiony do kolejnego widoku:

Edycja opcji LOCALVERSION

Wybierz tę opcje poprzez wciśnięcie Entera i w polu tekstowym wpisz coś w stylu:

-twojeimie-v1.0

Zatwierdź wartość poprzez ponowne wciśnięcie Entera.

Teraz musimy jakoś wyjść z tego całego menuconfiga. Na samym dole widać opcje Select, Exit, Help, Save oraz Load, nas interesuje Exit. Użyj strzałki w prawo aby przesunąć się na tę opcję i wyciśnij Enter, powtarzaj tę operacje dopóki nie wyjdziesz zupełnie z menuconfiga. Na samym końcu zostaniesz zapytany czy chcesz zapisać zmiany w konfiguracji. Wybierz oczywiście opcję Yes.

Mamy już skonfigurowany kernel teraz możemy przejść do jego budowania. Aby zbudować swój kernel użyj ponownie komendy make:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

Dla przypomnienia, opcja -j informuje ile procesów do kompilacji może użyć make. Kompilacja powinna potrwać ok. 10 minut. Po kompilacji powstani trochę plików, nas interesują jedynie dwa:

  1. arch/arm/boot/zImage- Obraz kernela Linuksa
  2. arch/arm/boot/dts/am335x-boneblack.dtb- skompilowany plik device-tree(konfiguracja sprzętowa)

Skopiuj oba pliki na drugą partycje karty uSD, której nadaliśmy etykietę kernel:

cp arch/arm/boot/zImage /media/user/kernel
cp arch/arm/boot/dts/ am335x-boneblack.dtb /media/user/kernel

Przełóż kartę do BBB, podłącz go do komputera za pomocą konwertera UART-USB uruchom go i przerwij automatyczny proces bootowania poprzez wciśnięcie dowolnego klawisza. Teraz możemy sprawdzić czy U-Boot widzi pliki, które przenieśliśmy na kartę uSD, użyj polecenia ext4ls:

ext4ls mmc 0:2

Pierwszy parametr to używany interfejs(karty uSD używają mmc), a kolejny to odpowiednio numer urządzenia i numer partycji. Po wykonaniu tego polecenia powinieneś zobaczyć coś podobnego do:

<DIR>       4096 .
<DIR>       4096 ..
<DIR>      16384 lost+found
         9753088 zImage
           65795 am335x-boneblack.dtb

Skoro wiemy, że U-Boot widzi te pliki to możemy je załadować do pamięci RAM:

ext4load mmc 0:2 ${kernel_addr_r} zImage
ext4load mmc 0:2 ${fdt_addr_r} am335x-boneblack.dtb

Pliki zostaną załadowane do adresów wskazywanych przez te zmienne, adresy gdzie należy ładować odpowiednie pliki są podawane przez producenta. Teraz możemy wystartować nasz system, aby tego dokonać musimy wykonać dwie czynności:

  1. Ustalić parametry dla kernela
  2. Wystartować system

Parametry kernela to podstawowe parametry systemu, które informują go którego portu szeregowego ma użyć do komunikacji albo gdzie ma szukać systemu plików. W U-Bootcie do przekazywania tych parametrów służy zmienna środowiskowa bootargs. Aby ją ustawić wykonaj następującą komendę:

setenv bootargs ”console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p3 rw rootwait”

Zwróć uwagę na brak znaku = w komendzie. Przekazujemy tutaj informacje o używanej przez system konsoli, prędkości transmisji danych(baudrate),każemy kernelowi wypisywać informacje z procesu bootowania(earlyprintk), wskazujemy mu również gdzie ma szukać rootfs, który jest do zapisu i odczytu(rw) oraz każemy Linuksowi czekać do momentu aż odpowiednie urządzenie z systemem plików zostanie wykryte przez system.

Teraz możemy wystartować Linuksa:

bootz ${kernel_addr_r} - ${fdt_addr_r}

bootz jest komendą, która startuje obrazy Linuksa w postaci zImage’a. Przyjmuje ona trzy parametry: adres kernela, adres initramfs oraz adres device-tree. W tym przypadku nie używamy initramfs więc podaliśmy myślnik(-) zamiast jakiegoś adresu.

Pewnie zauważyłeś już, że system nie wystartował tylko wyrzucił taki błąd:

[    3.210883] EXT4-fs (mmcblk0p3): mounted filesystem with ordered data mode. Opts: (null). Quota mode: disabled.
[    3.221231] VFS: Mounted root (ext4 filesystem) on device 179:3.
[    3.227605]  mmcblk1: p1 p2
[    3.235743] devtmpfs: error mounting -2
[    3.242666] Freeing unused kernel image (initmem) memory: 2048K
[    3.249383] Run /sbin/init as init process
[    3.253785] Run /etc/init as init process
[    3.257958] Run /bin/init as init process
[    3.262242] Run /bin/sh as init process
[    3.266204] Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
[    3.280442] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.1-adam-v1.0 #3
[    3.287266] Hardware name: Generic AM33XX (Flattened Device Tree)
[    3.293399] [<c03116b0>] (unwind_backtrace) from [<c030be3c>] (show_stack+0x10/0x14)
[    3.301212] [<c030be3c>] (show_stack) from [<c0fde258>] (dump_stack_lvl+0x40/0x4c)
[    3.308834] [<c0fde258>] (dump_stack_lvl) from [<c0fdca58>] (panic+0x108/0x320)
[    3.316184] [<c0fdca58>] (panic) from [<c0fe4908>] (kernel_init+0x110/0x124)
[    3.323277] [<c0fe4908>] (kernel_init) from [<c03001b0>] (ret_from_fork+0x14/0x24)
[    3.330888] Exception stack(0xc20bffb0 to 0xc20bfff8)
[    3.335965] ffa0:                                     00000000 00000000 00000000 00000000
[    3.344182] ffc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[    3.352398] ffe0: 00000000 00000000 00000000 00000000 00000013 00000000
[    3.359059] ---[ end Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance. ]---

Wynika to z faktu, że podaliśmy mu miejsce gdzie ma szukać rootfs i odpowiednie urządzenie jest znajdowane tylko, że nic tam nie ma, a przede wszystkim nie ma tam programu init, którego brak jest bezpośrednią przyczyną błędu.

W tym logu możesz również zobaczyć, że uruchomiony został twój kernel:

[    3.280442] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.1-adam-v1.0 #3

Pewnie też sobie pomyślałeś, że nie chce Ci się z każdym razem wpisywać tych wszystkich komend aby uruchomić Linuksa w U-Bootcie. Jest na to rozwiązanie- można ustawić zmienną bootcmd, która będzie uruchamiana podczas startowania U-Boota. Zresetuj BBB, przejdź do konsoli U-Boota i wydaj trzy polecenia:

setenv bootargs ”console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p3 rw rootwait”
setenv bootcmd ”ext4load mmc 0:2 0x82000000 zImage; ext4load mmc 0:2 0x88000000 am335x-boneblack.dtb; bootz 0x82000000 – 0x88000000”
saveenv

Komenda saveenv powoduje, że zmienne środowiskowe zostaną zapisane na karcie uSD i będą dostępne przy ponownym uruchomieniu płytki. Możesz teraz ponownie zresetować BBB i zobaczyć, że system zacznie startować teraz automatycznie.

Raspberry Pi 4

RPi4 ze względu na swoją architekturę sprzętową nie używa standardowego kernela, używa za to jego spatchowanej wersji. Źródła można pobrać z githuba:

git clone --depth=1 --branch rpi-5.10.y https://github.com/raspberrypi/linux
cd linux

Ja tutaj używałem brancha rpi-5.10.y, ale Ty możesz użyć innego jeśli chcesz. Teraz musimy ustawić domyślną konfigurację:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- KERNEL=kernel8 bcm2711_defconfig

Zwróć uwagę na dodatkowy parametr KERNEL=kernel8, jest to charakterystyczne dla całej serii RPi.

Podobnie jak w przypadku BBB nie potrzebujemy nic modyfikować aby uruchomić kernel, ale jeśli chcesz możesz zmodyfikować opcję LOCALVERSION tak jak to było zaprezentowane dla BBB. Teraz możemy zbudować kernel:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- KERNEL=kernel8 -j4

Kompilacja powinna trwać ok. 20 minut. Po kompilacji powstanie troche nowych plików, ale tak jak w przypadku BBB nas interesują tylko dwa:

  1. arch/arm64/boot/Image- obraz kernela Linuksa
  2. arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb- plik device-tree

Umieść te dwa pliki na karcie uSD na pierwszej partyji, której nadaliśmy etykietę boot:

cp arch/arm64/boot/Image /media/user/boot
cp arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /media/user/boot

Teraz musisz zmodyfikować plik config.txt, który tworzyliśmy w poprzedniej lekcji i dodać w nim następującą linijkę:

kernel=Image

Informuje ona bootloader RPi4 o tym, który plik ma zostać użyty jako kernel. Podobnie jak w przypadku BBB musimy jakoś przekazać parametry kernela. Aby to zrobić utwórz na karcie uSD plik cmdline.txt o następującej zawartości:

console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait

Wszystkie opcje są opisane w sekcji poświęconej BBB.

Teraz możesz umieścić kartę uSD w RPi4, podłączyć ją do komputera za pomocą konwertera UART-USB i uruchomić ją. Podobnie jak w przypadku BBB powinieneś zobaczyć informację o braku pliku init, który to powinien znajdować się w rootfs, a którego nie ma bo nie ma rootfs.

QEMU

Dla QEMU zbudujemy kernel dla trzech różnych architektur: ARM, ARM64 oraz x86_64. Pobierz paczkę z kodem i ją rozpakuj:

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.1.tar.xz
tar xf linux-5.14.1

Możesz utworzyć trzy wersje tego katalogu- dla każdej architektury:

cp -r linux-5.14.1 linux-5.14.1_arm
cp -r linux-5.14.1 linux-5.14.1_aarch64
mv linux-5.14.1 linux-5.14.1_x86_64

Uwagi dla wszystkich architektur- nie będziemy potrzebowali w żaden sposób modyfikować tych kerneli jednak jeśli chcesz mieć pewność, że uruchamia się twój kernel możesz zmodyfikować opcję LOCALVERSION tak jak to jest w przypadku BBB, ale najpierw musimy ustalić domyślne konfiguracje.

Odnośnie QEMU, w przypadku architektury ARM będziemy używać modelu versatilepb, w przypadku ARM64 użyjemy generycznego modelu virt, a dla x86_64 użyjemy modelu pc.

A teraz do rzeczy, musimy zdefiniować domyślne konfiguracje dla każdej z architektur:

# dla ARM:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- versatile_defconfig
# dla ARM64:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
# dla x86_64
make ARCH=x86 x86_64_defconfig

Zwróć uwagę, że w przypadku architektury x86_64 nie używamy opcji CROSS_COMPILE, wynika to z faktu, że budujemy kod na tę samą architekturę co maszyna budująca zatem nie dokonujemy tutaj cross-kompilacji.

Kompilacje kerneli będą się wahać od ok. 5 minut dla ARMa do ok. 15-20 minut dla ARM64 i x86_64.

W wyniku kompilacji powstanie troche plików nas interesują tylko następujące pliki:

  1. W przypadku ARM:
    1. arch/arm/boot/zImage- obraz kernela Linuksa
    2. arch/arm/boot/dts/versatile-pb.dtb- plik device-tree
  2. W przypadku ARM64:
    1. arch/arm64/boot/Image- obraz kernela Linuksa
  3. W przypadku x86_64:
    1. arch/x86/boot/bzImage- obraz kernela Linuksa

Mając wszystkie te pliki możemy uruchomić QEMU:

# ARM:
./qemu-system-arm -m 256 -M versatilepb \
    -kernel /ścieżka/do/linux-5.14.1_arm/arch/arm/boot/zImage \
    -dtb /ścieżka/do/linux-5.14.1_arm/arch/arm/boot/dts/versatile-pb.dtb \
    -append "console=ttyAMA0,115200" \
    -nographic
# ARM64:
./qemu-system-aarch64 -m 2048 -M virt \
    -cpu cortex-a57 -smp 1 \
    -kernel /ścieżka/do/linux-5.14.1_aarch64/arch/arm64/boot/Image \
    -append "console=ttyAMA0,115200 earlyprintk" \
    -nographic
# x86_64:
./qemu-system-x86_64 -m 4096 -M pc  \
    -kernel /ścieżka/do/linux-5.14.1_x86_64/arch/x86/boot/bzImage \
    -append "console=ttyS0" \
    -nographic

Omówienie powyższych komend zacznijmy od omówienia parametrów QEMU:

  1. -m: ilość pamięci RAM przydzielonej do emulatora
  2. -M: używany model
  3. -cpu: używany model procesora, nie jest konieczne gdy używamy konkretnego modelu
  4. -smp: ilość emulowanych rdzeni procesora, nie jest konieczne gdy używamy konkretnego modelu
  5. -kernel: obraz kernela, który ma zostać użyty
  6. -dtb: plik device-tree
  7. -append: parametry przekazywane do kernela
  8. -nographic: wyłączenie trybu graficznego, wszystkie dane będą przekazywane na port szeregowy

W przeciwieństwie do BBB oraz RPi4 w parametrach przekazywanych do kernela nie podawaliśmy miejsca gdzie jest rootfs, wynika to z faktu, że nie podłączaliśmy żadnego obrazu dysku do QEMU(to zostanie zaprezentowane przy okazji budowania rootfs w kolejnej lekcji). Z tego też powodu błąd który się pojawia podczas startowania Linuksa wygląda tutaj nieco inaczej niż w przypadku BBB oraz RPi4 i wygląda on tak:

[    2.854777] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[    2.855450] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.1-adam-v1.0 #1
[    2.855877] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014
[    2.856483] Call Trace:
[    2.857531]  dump_stack_lvl+0x33/0x42
[    2.857767]  panic+0xf3/0x2b4
[    2.857900]  mount_block_root+0x17d/0x21d
[    2.858099]  ? __SCT__tp_func_drv_return_u64+0x8/0x8
[    2.858278]  ? rdinit_setup+0x26/0x26
[    2.858394]  mount_root+0xec/0x10a
[    2.858514]  prepare_namespace+0x130/0x15f
[    2.858725]  kernel_init_freeable+0x217/0x226
[    2.858914]  ? rest_init+0xc0/0xc0
[    2.859075]  kernel_init+0x11/0x110
[    2.859240]  ret_from_fork+0x22/0x30
[    2.860387] Kernel Offset: 0x13200000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[    2.861172] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

Tutaj mamy informację o nie możliwości zamontowania rootfsa ponieważ nie został on wskazany. Możesz mieć jednak pewność, że startowałeś swój własnoręcznie zbudowany kernel, powinieneś widzidzieć log podobny do:

[    2.855450] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.14.1-adam-v1.0 #1

Teraz jest jeszcze ostatnia zagadka- jak wyłączyć to całe QEMU? Podpowiem, że znana kombinacja klawiszy Ctrl+C nie zadziała. Aby wyłączyć QEMU musimy przejść do jego monitora poprzez wciśnięcie kombinacji Ctrl+A, a następnie wciśnięciu klawisza C. Wtedy pojawi się znak zachęty wyglądający następująco:

(qemu)

Teraz wpisz komendę ”q”(tak, dosłownie jedna litera) i wciśnij Enter.

W tej lekcji budowaliśmy kernel dla trzech architektur w QEMU, w przyszłych lekcjach ograniczymy się do prezentowania przykładów tylko dla modelu versatilepb, a dla pozostałych będę podawać tylko konfiguracje, które są potrzebne do przebudowania odpowiedniego oprogramowania.

W kolejnej lekcji uruchomimy już nasz system, będziemy budować rootfs, przez którego to brak nie mogliśmy wystartować naszego systemu.

Ten wpis został opublikowany w kategorii Kurs budowania Linuksa. Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

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