組込みLinux Beaglebone Black でやってみる その6 Linux ソース、Kernel・モジュールビルド

ここではLinux KernelとKernel モジュールをビルドします。 Linux Kernelもいろいろあるようで、Beagleboard専用のサイトからLinux Kernelのソースコードとデバイスツリーを一括でダウンロードしてビルドします。 Starting KernelでFreezeした時のDDR内のKernel Messageログを見る対処方方法も記載しました。

ディレクトリ

(注意)U-BOOTのイメージと、Linux Kernelイメージと、ルートファイルシステムを作るディレクトリは必ず別でないといけません。 それぞれに、.configがありmenuconfigで設定できますが、同じディレクトリでビルドすると、.configが上書きされてしまいビルドできなくなります。 私は下のようにしています。

BBB用のLinuxダウンロード

https://github.com/beagleboard/linux/tree/5.10のサイトのLinux KernelソースはBeagleboard用に用意されており、Beagleboard用の各種ドライバ、デバイスツリー、初期設定が既に格納されていて、それらとの整合が取れています。

zipの方が速くていいのでzipダウンロードしていので、たら、RobertCNelsonさんに『zipダウンロードはやめて、git cloneしなさい』と怒られたので、git cloneします。

$ cd ~/BBB_Workspace/Download/
$ git clone -b 5.10 https://github.com/beagleboard/linux --depth=10

https://github.com/beagleboard/linux/tree/5.10からzipとしてもダウンロードしたり、別のバージョンを確認できます。

適当な5.18.rcバージョンをダウンロードしてビルドしても、ルートFSをマウント出来ませんでした。 rcのない、Releasedバージョンで試すのが良いと思います。

Linux Kernel Source

  • Processor(Architecture)別にサポートされている(arm、mips、powerpc etc)
    • arch/arm/kernel
    • arch/arm/mm
    • arch/arm/boot/compressed
    • arch/arm/boot/lib
  • mach-omap2 —TI AM33xx-SoC etc

Board別のLinuxとDTBの関係

  • TIのOmap2のBoard用は、/linux/arch/arm/mach-omap2/board-generic.cだけで、残りはデバイスツリーを参照するようになっている。昔は全てのBoardの個別.cファイルがあった。その為、Linux Kernel Sourceが膨大になったため、デバイスツリーが使われるようになった。
  • dtbには.dt_compatというフィールドがあり、generic.cにも.dt_compatフィールドがあり、Linuxはdtbファイルからdt_compatを読み出し、プラットフォームが何なのかを判断する。
  • 値は “ti, dm8168” や “ti, am33xx” など

Linuxコンパイル

上のリンクからダウンロードすると、bb.org_defconfigは~/BBB_Workspace/Downloads//linux/arch/arm/configs/bb.org_defconfigにはいってる。

Clean

  • Linux Kernelのビルドは全て、~/BBB_Workspace/Download/linux-5.10/で実行します。
  • まず前回生成したオブジェクトファイルなどをきれいに削除
  • Permission errorが出たのでsudoで実行
  • (注意)arm-linux-gnueabihf- のパスは通しておかないといけない。
cd ~/BBB_Workspace/Downloads/linux-5.10
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean

環境設定(.config)ファイル生成

  • mkdir: cannot create directory ‘.tmp_xxxxx’: Permission denied が沢山でるのでsudoで実行
  • *** Can’t find default configuration “arch/arm/configs/bb.org_defconfig”! が出た場合bb.org_defconfigを取得して ~/BBB_Workspace/Downloads/linux/arch/arm/configs/bb.org_defconfigに保存
  • sudo make … bb.org_defconfig で/bin/sh: 1: flex: not found と bison not foundエラーが出たので以下を実施
    • sudo apt update
    • sudo apt install flex bison
  • これでお決まりの .configがこのディレクトリに出来る。 このファイルは絶対に編集してはいけない。 .configの設定を変更する際は、次のmenuconfigを使う。
  • bb.org_defconfigの代わりにomap2plus_defconfigでも良いと言っている人もいるが、まずは正規と思われる、bb.org_defconfigで実行する
//必要になるライブラリを一括してインストールしておく
$ sudo apt install build-essential lzop u-boot-tools net-tools bison flex libssl-dev libncurses5-dev libncursesw5-dev unzip chrpath xz-utils minicom wget git-core
$ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bb.org_defconfig
  • 設定変更が必要な時は、 .configを作ったこのディレクトリで以下を実行して設定変更する。
  • 設定変更の必要がない場合は、スキップしても良い
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig

初めて、上記でkernelの menuconfigを出そうとした時以下のようなエラーが出ました。これはuBuntuにncursesライブラリがインストールされていないfatal error curses.hエラーとなるようで、その下の様にsearchしてインストールされていなければ、2つのライブラリをインストールしました。

$ sudo apt update
$ sudo apt search libncurses  //以下の図のように表示される
$ sudo apt install libncursesw5 libncurses5-dev   //これで解決

Kernelイメージとモジュールのビルド 

  • uImageとしなければ、zImageだけの生成となってしまう。 U-BOOTでの起動のため、uImageを指定。
  • dtbsと入れると、dtbを生成します。 これで生成したdtbを使わないといけません。
  • LOADADDRはRAM(DDR)内にuImageを入れるアドレス。 U-BOOTはこれを読み取る。
  • -j4はコア数。 このコンパイルには結構時間がかかるので、コア数を多めにしたほうが良い。
  • コア数が不明の時は、getconf _NPROCESSORS_ONLN を実行するとコア数が表示される。
  •  私の古いPCは2コアx2スレッド@2.5GHzなので、-j2で実行。
  • 1時間20分ほどかかりました。
  • LOADADDRには、0x80008000としています。 これは、uImageがDDRの0x80000000から配置していくのですが、uImageにはいろいろ付加情報がついていて、実際のKernelイメージが実際始まるのが0x8000(32K)のオフセットを付けるのが慣習となっているからです。 この32Kの中にパラメータ等を配置したりしています。 LOADADDRを付けて、U-BOOTにKernelはここから始まると明記しているのです。
  • lzop not found となったら以下を実行
    • sudo apt update
    • sudo apt install lzop
$ getconf _NPROCESSORS_ONLN
4
$ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage dtbs LOADADDR=0x80008000 -j4
  • uImageが./arch/arm/boot/の下に出来ました。
  • そして、./arch/arm/boot/dtsの下にam335x-boneblack.dtbを使います。  注意、Kernelビルド時にここで生成されたdtbを使用しないと、Starting Kernelでfreezeします。

『’/lib/firmware/am335x-pm-firmware.elf’, needed by ‘drivers/base/firmware_loader/builtin/am335x-pm-firmware.elf.gen.o’. Stop.』の解決方法はここ

Kernel Moduleのビルド(ここまで)

  • Kernel ModuleはKernelに機能を追加するためのバイナリファイル。
    • デバイスドライバなどもある
    • Kernelとは別にインストールするので、
  • modulesのビルドでcerts/extract-cert.c:21:10: fatal error: openssl/bio.h: No such file or directoryが出てきたので、
  • sudo apt install libssl-dev
  • ~/BBB_Workspace/Downloads/linux/ で実行する
  • これも1時間程かかりました
  • これでKernel Modulesが生成されます
  • これが完了したら、この『その6』終了です。
  • ここで作ったKernelとam355x_boneblack.dtbは試せますが、Kernel起動の途中でルートFSマウントする前に止まると思います。
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j4
  • Kernelモジュールをビルドしたら、『その7で』BusyBoxでルートファイルシステムを作って、そこにKernelモジュールをインストールします。
  • 『組込みLinux Beaglebone Black でやってみる その7 ルートファイルシステム、 BusyBox』をして、ルートファイルシステムを作り、以下のコマンドで実行していきます。
  • sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- <INSTALL_MOD_PATH>= modules_install

No rule to make target エラー

  • これが出るのは、大体make ARCH=arm … を実行するディレクトリが間違っている。
  • ruleの記載されたMakefileは、make ARCH=arm … を実行するディレクトリにある。 以下がそれぞれの実行ディレクトリ(私の場合です)。 SourceをExtractしたそれぞれのディレクトリです。
    • Linux kernel ーーーー ~/BBB_Workspace/Downloads/linux/
    • U-BOOT ーーーー  ~/BBB_Workspace/Downloads/u-boot-2022.10/
    • BusyBox ーーーー  ~/BBB_Workspace/Downloads/busybox-1.35.0/

Starting Kernelで止まる

  • Starting Kernelで止まって何もメッセージが表示されない場合の対応方法。 Starting Kernelで止まるという事は、既に、U-BOOTからKernelに引き渡されたが、Kernelがデバイスツリーを読み込んで、デバイスとドライバ(Kernel モジュール)を結びつけて起動する途中で問題を起こしていると思います。 これらが終われば、ルートFS をマウントしてしまえば、シェルコマンドが使えるのですが、U-BOOTコマンドもシェルコマンドも使えない状態です。 この状態では、Kernel起動時のシステムログを見に行くしかありません。
  • Kernelは起動時にDDRのメモリに起動ログを入れているので、それを見に行きます。
  • ログが記録されているDDRのメモリアドレスは、~/BBB_Workspace/Downloads/linux/System.map内の 『__log_buf』の横に記載されています。 System.mapにDDRのどのアドレスに何が書かれているかを定義されています。 0x0000002C ~ 0xc0004000 の間は定義されておらず、ここにu-boot.img、uImage、.dtb、intramfs等が入ります。 14万行ほどありました。
  •  以下のようにして確かめてみます。
$ cd ~/BBB_Workspace/Downloads/linux
$ grep __log_buf System.map
c166fc28 b __log_buf    //この様に表示されます。
$ vi System.map         // Sytem.mapを開いて、『__log_buf』を検索します。
// 『__log_buf』はDDRの0xc166fc28から0xc167fc27まで使用出来る事になっています。
c166fc28 b __log_buf
c167fc28 b safe_read_lock
  • __log_bufは0x10000使用出来ますが、実際のログはそこまで入ってないことが多いので、md.l 0xc166fc28 0x500 くらいで見てみましょう。 これは、U-BOOTコマンドのメモリーダンプ(md)で、DDRのメモリーの内容を表示する方法です。
  • DDRの中身は電源を切ると消えてしまうので、Starting Kernelが表示されて、3分ほどすると自動的にU-BOOTからリトライするので、その時U-BOOTコマンドプロンプトに入って mdコマンドで実際に見てみましょう。
  • 以下は、別の時に実行した例なので、__log_bufのアドレスは0xc1932588だったので、以下の様にしました。
  • 結果は、長かったので不要部分は削除して、最後の2行は読みやすいように文字だけ取り出してみました。 すると、system timersを取り込めておらず初期化が出来ていません。 これで止まっていたんですね。 timer@44e31000というのは、アドレス44e31000の/ocp/timerが読めないとなっているので、デバイスツリーに異常があることが分かります。
  • 私は最初、am355x_boneblack.dtbというファイルなら何でも同じと思って、このファイルを何処からかコピーしてきていました。 その為、Starting Kernelで止まってしまったのです。 しかし、このファイルもKernelビルド時に生成されて、そのファイルを使用すれば、Kernelは正常に起動してくれました。
  • この方法はKernel起動が止まった時にデバッグ方法で良いと思ったのでメモしておきます。 StackExchangeに書かれていました。
=> md.l 0xc1932588 0x400
 :      
c1932ef8: 646e6172 625f6d6f 73657479 6c616320  random_bytes cal                 
c1932f08: 2064656c 6d6f7266 61747320 6b5f7472  led from start_k                 
c1932f18: 656e7265 78302b6c 2f383035 65367830  ernel+0x508/0x6e                 
c1932f28: 69772030 63206874 5f676e72 74696e69  0 with crng_init                 
c1932f38: 303d303d 0000000a 3fffe027 635f6974  =0=0....'..?ti_c                 
c1932f48: 675f6b6c 725f7465 615f6765 3a726464  lk_get_reg_addr:                 
c1932f58: 6c706420 706d5f6c 6b635f75 20726f20   dpll_mpu_ck or                  
c1932f68: 65726170 6d20746e 20747375 65766168  parent must have                 
c1932f78: 67657220 215d335b 000a215d 00000000   reg[3]!]!.
 :
missing clk                 
c1933038: 6c727463 646f6e20 202c7365 61656c70  ctrl nodes, plea                 
c1933048: 75206573 74616470 6f792065 64207275  se update your d                 
c1933058: 0a2e7374 00000000 00000000 00000000  ts............
 :
Failed to initialize  '/ocp/timer@44e31000': -2222...=..?dmtimer_systimer_init: unable to detect system timers, update dtb?b?....>..?Failed to initialize '/ocp/timer@4804000000': -22...............

その他 参考

用語

  • BusyBox — Linux/Unixの標準コマンド(200個以上)を単一の実行ファイルに詰め込んであり、簡単に取り入れることができるので、組込Linuxでよく使用される。
  • EFI —- EFIファイルはブートローダーの実行ファイルで、UEFI(Unified Extensible Firmware Interface)ベースのコンピュータシステム上
  • initrd(gzip形式) 起動時にKernel によってマウントされるようにアンパックされたブロックデバイス。 CONFIG_BLK_DEV_INITRDを選択してもCONFIG_BLK_DEV_RAMがなれば『VFS: Cannot open root device mapper/root or unknown-block(0,0): error -2』となる。 今は、initramfsが主流。
  • ramfs  cpioを介してメモリに展開される
  • initramfs(cpio+gzip形式) initramfsはtmpfsのインスタンスでramfsの改良版。 cpioアーカイブファイルをgzipに圧縮している。
  • squashfs =m -> y Linux用の圧縮された読み込み専用ファイルシステム

Kconfigとは

  • Kernelのコンフィギュレーション画面に表示する内容を定義したファイル。
  • make menuconfig のメニュー形式でも呼び出せる

Linux Kernel Source

  • Processor(Architecture)別にサポートされている(arm、mips、powerpc etc)
    • arch/arm/kernel
    • arch/arm/mm
    • arch/arm/boot/compressed
    • arch/arm/boot/lib
  • mach-omap2 —TI AM33xx-SoC etc

ELFフォーマットとは

  • binファイルは、メモリ修正や再配置のない純粋なバイナリファイル。 特定のメモリアドレスにロードされる明示的な命令を持っている可能性が高い。 プログラムを実行するROMまたは特定アドレスにはいります。 しかしベースアドレスが存在しないため、ベースアドレスがなにかを知る必要がある。
  • elfファイル(Executable and Linkable Format)は、binファイルを含んだシンボルルックアップと再配置可能なテーブルで構成される実行可能なリンク可能方式。 
  • elfは、実行ファイル、オブジェクトコード、共有ライブラリやコアダンプ用の共通の標準ファイルフォーマット。 Unix用のABI(Application Binary Interface)の仕様として最初に制定された。 
  • Kernelによって任意のメモリアドレスにロード出来る。 使用される全てのシンボルがロードされたメモリアドレスからのオフセットに自動的に調節される。 eflには『.data』、『.text』、『.bss』、『.rodata』など、いくつかのセクションがある。

コメント