Raspberry Piの実行ファイルをWSL上でクロスコンパイル


さて今回も地道に写経だ。

前回までカーネルのクロスコンパイルをやったのだが、今回は実行ファイルのビルドだ。
これができれば自作のアプリやドライバなどをクロスコンパイルできるようになる。
今回は定番の"Hello World"をARM 32bit版でクロスコンパイルする。
手順としては、

だ。

ツールチェーンの導入

まずはこちらに行ってみよう。
github.com
これを何と読む。

These toolchains are deprecated. They are outdated and easily available from other sources (e.g. direct from Ubuntu apt).

Raspberry Piはエコシステムがかなり進化している。
古い記事を読むとかなり面倒なツールチェーンの導入方法や、ビルド環境設定が書かれているが、今(2023年9月現在)ではかなり簡素化されている。
つまるところ、WSL上で

sudo apt-get install gcc-arm-linux-gnueabihf

で終わりなのだ。
しかも前回までのカーネルのクロスコンパイル環境をすでに構築している場合には、このaptはすでに入っているのだ。
実質何もしないでよろしい。

ソースコードMakefileの作成

ソースコード(helloworld.c)はこんな。

#include <stdio.h>

int main(int argc, char* argv[])
{
        printf("Hello Aho\n");
        return 0;
}

Makefileはこんな。

CC = arm-linux-gnueabihf-gcc

helloworld: helloworld.o
        $(CC) helloworld.o -o helloworld
helloworld.o: helloworld.c
        $(CC) -c helloworld.c
clean:
        rm *.o helloworld

見ての通りキーになるのはコンパイラの指定で、

がすでに上記のツールチェーンで導入されているのだ。
昔は"gcc (うんにゃらかんにゃらうんにゃらかんにゃら)"とARMのクロスコンパイルは長い呪文を書いたのだが、専用バイナリにしたのね。
ちなみに実体は2023年9月現在では”/usr/bin/arm-linux-gnueabihf-gcc-9"だ。

ビルド

make

"helloworld"ができているはず、ARMバイナリーかどうか確認してみよう。

file helloworld

とすれば

helloworld: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=(SHA1ハッシュコード), for GNU/Linux 3.2.0, not stripped

と表示されるはず。

実行ファイルのコピー

scp helloworld nsuzuki@macrpi:/home/nsuzuki/work

これでRaspberyy Piの/home/nsuzuki/workに実行ファイルがコピーされた。
コピーパスは自分の環境で適宜変更のこと。

実行

Raspberry Piにログインして、コピーしたパスから

./helloworld

面倒ならSSHで実行してしまえばよい。

Chuwi MiniBook X (N100版) レビュー - バッテリーどのくらい保つかな


さてモバイルなノートPCで気になることといえばバッテリー寿命。
今回もやってみようかな、バッテリー寿命測定。

OneMix4の時と同様に画面の輝度を150cd/m^2に設定してローカルなビデオ再生のバッテリー寿命を測定した。
さてどのmpeg4ファイルを使うべ。

  • 監督自身の故郷のロングショットで終わらせたあの伝説的なシリーズ
  • 自らモーションアクションアクターをする「長澤巨大化」なあの映画
  • またまた監督自らモーションアクションアクターをする、TVシリーズと違いホンダがサイクロン号を提供し、「え、長澤カット、それだけかよ」なあの映画

前回までの測定結果とビットレートや平均的な画面の明るさなどの条件を同じにするために、耐えがたきを耐え、忍び難きを忍び、映画は変えずにあの監督の「怪獣」が出てくる特撮映画だ。
今回もPowercfgの結果を使用した。
測定前に一度ほぼ0に近いところまで放電させてから、フル充電することでバッテリーゲージを較正させた。
そして注目の結果がこちら -

(20230909 訂正)
おっとしまった、ちょっとミスをしてしまった。
Windows 11からOSレベルで液晶ディスプレイの消費電力を最適化する操作ができるのを忘れていた。
特に動画再生時はこれを使うとかなり消費電力が抑えられるのだ(もうほとんど仕事だな)。
詳しくはこのDDIを参照。
learn.microsoft.com
この設定を解除していたのを忘れていた。
設定しなおして再度測定と。

すでに分解写真などが出回っており、バッテリー容量は変わらずに28880mWhrのようだ。
powercfgの最大バッテリー容量も同じである。
前回までの結果と比較するとこのようになる。

バッテリー容量 ビデオ再生時間 ビデオ再生時の消費電力
OneMix 4 38500mWhr 6.85Hr 5.62W
MiniBook X (2021年モデル) 28880mWhr 4.05Hr 7.11W
MiniBook X (N100版) 28880mWhr 6.15Hr 4.70W

低解像度、色再現性も低いディスプレイにしているので、それがだいぶん貢献しているようだ。
ビデオ再生時の消費電力はこの3機種の中で最も低い、これはいいな。

ということで結論、Chuwi MiniBook X(N100版)のバッテリーは十分に保ちます(おいおいまたかい
#いつも言うが、バッテリーなんて2時間も保てば十分だ

Chuwi MiniBook X (N100版) 着弾


さてこちらでこんな殊勝なことを言っていた筆者、4月に3年ぶりの海外出張に行ったあたりからまたしてもUMPC欲がうずうず。
さらに夏休みに家族と旅行に行った際に、GPD Pocketだけでは「こりゃあかん」となり、またしてもAli Express、GPD、One-netbookのサイトを徘徊。
中国White Brand PC OEMのGPDやOne-netbookはポータブルゲーミングPCにシフトしてしまい、UMPC熱はだいぶ下がってしまったようだ。
One-netbookOneMix 5の発表はあったものの、いつ出てくるか分からない状態。

そんな中、Chuwi(驰为)がなかなか飛ばしている。
この会社、ネーミングがなかなか香ばしいのだが、やっているのは安価なMTK、中国製SOCを使ったタブレットや、AtomPentiumなどインテルのローエンドCPUを使ったノートPCを製品化していて、けっこう地道(地味)だ。
そのChuwiが2021年から始めたのが10"のいわゆるサブノートPC、Minibook Xだ。
一発目はJasper Lakeに自社のタブレットの目玉つきディスプレイをそのまま流用して投入。
2022年は目玉付きディスプレイをもうちょっと安いディスプレイに交換。
そして2023年モデルはCPUを"Pコアなし"Alder LakeことN100に交換したものだ。
ようやく配達されたので、いろいろろ見てみよう。

いつも通りの充電。


ヒンジ側のJEITAのセンターハイの充電プラグのマークのついている方は、USB-C PDに対応していない。
USB-C PD対応充電器をつなぐとご覧の通り。

付属の充電器はChuwi専用で、つなぐといきなり12Vがどっかーん、と出る仕様なので、間違っても5VクラスのUSB-C機器を充電してはならない、運が悪ければ火事だ。
(20231120追記)
ヒンジ側のUSB-CポートがUSB PD対応していないは誤り、詳しくははこちらを参照のこと。

ヒンジから遠い方のUSB-CはUSB-C PD対応。
ご覧の通り12V/3Aで充電している。

(20230927訂正)
5chの投稿内容を見て、再度USB-Cの検証をしてみた。
USB3の方はどちらのポートも3.2Gbpsほど出ていたので、少なくとも3.0対応のようだ(USB3.2対応のM.2接続SSD)。
またUSB-C DP Altモードもどちらのポートでも対応しているので、どちらからも外付けモニタを接続できる。
DP MSTは検証環境がないのだが、少なくとも2つの外部モニターに同時に表示できるかもしれない(後日確認予定)。

Raspberry PiのLinux カーネルビルド - WSLで32bit版をクロスコンパイル編


さーて次はWSLでクロスコンパイルだ。
前回Surface Pro7+にUbuntuをインストールして環境を構築したのだが、筆者の場合にはデスクトップ環境があり、そいつの方がはるかにCPUパワーもある。
こいつをUbuntuデュアルブートにする手もあるのだが、ディスク容量に余裕がない。

過去にはVirtual BoxでLinuxの仮想環境を構築していたが、最近はずっとWindows Subsystem for Linux (WSL)だ。
WSLも2になり、各種ツールチェーンもいろいろ対応されるようになった。
今回は一発こいつでビルドしてみよう。
ついでに前回はこちらの事情から64bitをビルドしたのだが、今回は32bit版をビルドしてサイドロードしてみよう。
akkiesoft.hatenablog.jp
なのでステップは

前回同様に、ビルドの手順に関しては全てこちらに書かれているので、こちらを見ながら進める。
www.raspberrypi.com

Raspberry Piカーネルを32bit版に切り替え

Raspberry Piの/boot/config.txtに以下の行を追加する。
管理者ファイルなので、"sudo vi config.txt"で編集だ。

#option for 64bit kernel boot: 0=32bit, 1=64bit
arm_64bit=0

書き換えた後に再起動すれば、32bit版のカーネルで起動する。

WSL上にビルド環境を作成

WSLの構築方法はこちらを参照。
nobu-macsuzuki.hatenablog.com
その後は前回同様に、

sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev

ARM 32bitのクロスコンパイルなので、

sudo apt install crossbuild-essential-armhf

あとあと面倒が無いようにARM 64bitクロスコンパイルツールもインストールしておく。

sudo apt install crossbuild-essential-arm64

ソースコードのクローン

全く前回同様。

git clone --depth=1 https://github.com/raspberrypi/linux

32bit版のビルド

ほぼ前回同様。
まずcloneしたlinuxディレクトリーに移動。

cd linux

シェル変数"KERNEL"を定義、こいつをkernel7lにする(使うんかな?)

KERNEL=kernel7l

ビルド用のコンフィグファイル".config"を32bit、クロスコンパイル指定で作成する。

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

これまた前回同様に.configのCONFIG_LOCALVERSIONを書き換えておくとよい。

CONFIG_LOCALVERSION="-v7l-(自製と分かるような文字列を追加)"

んでもって32bit版をビルド。

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs

既存のイメージのカーネルカーネルモジュールを上書き

さてここで問題発生。
WSLではext4フォーマットのUSBドライブをマウントできないのだ。
learn.microsoft.com
UbuntuのようにRaspbery Piからmicro SDを引っこ抜いて、母艦に挿してサイドロード、という手が使えない。
よっしゃ、昔取った杵つき餅、おぢさん、NFSRaspberry Piのドライブをマウントして書き込んじゃうよ。
ところがこいつもダメ。
WSLのNICが仮想化されているためなのか、NFSがマウントできないのだ。
なので、作業フォルダーを物理的にコピーして、残りの作業を続けることにした。


Raspberry Piを起動してSSHで接続できる状態にしておく。
Rapberry Pi上に作業ディレクトリーを作成しておく(筆者の場合は/home/nsuzuki/work)。
WSL側で作業ディレクトリーを圧縮して、Raspberry Piにコピーする。
作業ディレクトリーの親ディレクトリーに移動。

cd ..

作業ディレクトリーを圧縮して、Raspberry Piにコピーする。

tar -zcvf linux.tar.gz linux
scp linux.tar.gz nsuzuki@macrpi:/home/nsuzuki/work

Raspberry PiSSHする。

ssh nsuzuki@macrpi

作業ディレクトリーに移動して、ファイルを解凍。

cd work
tar -zxvf linux.tar.gz

ここまでくれば、あとは公式のローカルサイドロードの通りやればよい。
リモートでも環境変数を設定し、

KERNEL=kernel7l

カーネルモジュールをインストール、

sudo make modules_install

最後にカーネルとDevice Tree Blob (DTB)を上書きする。

sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm/boot/zImage /boot/$KERNEL.img

再起動すれば自家製カーネルを拝むことができる。

Raspberry PiのLinux カーネルビルド - Ubuntuで64bit版をクロスコンパイル編

さて次はRaspberry Piカーネルのビルドだ。
基本的なステップは

Raspberry Piは生態系がよく整備されているので、全てのことはこちらに書かれている。
www.raspberrypi.com
ツールやビルドの方法も年々変わっているようなので、常に最新の記事を参照するとよい。
ちなみに筆者がこれをやったのは2023年の8月だ。

ビルド環境の作成

カーネルのビルドはRaspberry Pi本体でも可能だ。
しかしべらぼうに時間がかかる。
X64なLinuxを使ってクロスコンパイルした方が断然早い、それでも(環境によるが)1時間ほどはかかる。
今回はたまたま空いていたSurface Pro 7+上にUbuntuをインストールして、そいつをクロスコンパイル環境にすることにした。
Ubuntuのターミナル上で必要なビルドツールをインストールする

sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev

これにARM 32bitのクロスコンパイル環境なら

sudo apt install crossbuild-essential-armhf

ARM 64bitのクロスコンパイル環境なら

sudo apt install crossbuild-essential-arm64

面倒なので最初から両方入れておくとよい。

ソースコードのクローン

githubからクローンすればよい。
github.com

git clone --depth=1 https://github.com/raspberrypi/linux

ビルド

実はここから先で2週間ほどひっかかった。
ビルドは成功するのだが、カーネルカーネルモジュールを上書きすると起動時にビープがなり、更新したはずのカーネルがロードされていないのだ。
あれこれと試行錯誤の上で、見つけた記事がこれ。
akkiesoft.hatenablog.jp
この(2023年)4月からRaspberry Pi 4シリーズのデフォルトカーネルが64bitになったのだそうだ。
筆者は32bitイメージでRaspberry PiのブートSDカードを作成していたので、てっきりカーネルも32bitだと思い込んでいたのだ。
OMG、orz...
なので、以下64bitのカーネルビルドの方法を記載していく。
gitでcloneしたディレクトリー(linux)に移動して、

cd linux

シェル変数"KERNEL"を定義、

KERNEL=kernel8

ビルド用のコンフィグファイル".config"を64bit、クロスコンパイル指定で作成

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

あとで自分で作成したカーネルだと分かるように".config"内の以下の行を自分で編集しておくとよい。

CONFIG_LOCALVERSION="-v8-(自製と分かるような文字列を追加)"

いよいよビルドだ。

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs

時間がかかるので、ちゃーでも飲みながら気長に待とう。

既存のイメージのカーネルカーネルモジュールを上書き

ビルドが終わったらRaspberry PiのブートSDカードのカーネルカーネルモジュールを上書きする。
Raspberry PiからSDカードを外し、Ubuntuの走っている方のPCに挿して"lsblk"と唱えると、こんな感じの奴がいるはずだ。

このsda1とsda2がRasperry PiのOSの収納されているファイルシステムだ、名前は環境によって異なるので注意。
sda1がfat32Raspberry Pi上でマウントポイントが/boot、sda2がext4でマウントポイントが/になっているファイルシステムだ。
まずそれぞれをUbuntu PC上にマウントする(現在の作業ディレクトリーはcloneしたlinuxディレクトリーなので、注意)。

mkdir mnt
mkdir mnt/fat32
mkdir mnt/ext4
sudo mount /dev/sda1 mnt/fat32
sudo mount /dev/sda2 mnt/ext4

ビルドしたカーネルモジュールをインストールする

sudo env PATH=$PATH make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=mnt/ext4 modules_install

カーネルとDevice Tree Blob (DTB)を上書きする、ここでは一番最初に現在のカーネルを$KERNEL-backup.imgとリネームして保管してある。

sudo cp mnt/fat32/$KERNEL.img mnt/fat32/$KERNEL-backup.img
sudo cp arch/arm64/boot/Image mnt/fat32/$KERNEL.img
sudo cp arch/arm64/boot/dts/broadcom/*.dtb mnt/fat32/
sudo cp arch/arm64/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
sudo cp arch/arm64/boot/dts/overlays/README mnt/fat32/overlays/

最後にSDカードのファイルシステムをアンマウントする。

sudo umount mnt/fat32
sudo umount mnt/ext4

これで完了だ。

あとはSDカードをつっ挿して、Raspberry Piを起動すれば、自分でビルドしたカーネルからブートする。

筆者のようにCLIでログインしていない場合には、ターミナルを開いて

uname -r

と叩けばカーネルバージョンが表示される。

最近UNIX使ってなかったからコマンド忘れてるわ

さすがにcatやls、viは忘れなかったけど

  • grep
  • df
  • du
  • rm -r -f (ディレクトリー名): -r: ディレクトリーを消す、-f: force いちいち中身を消すか聞いてこない
  • su: Substitute User、Super Userぢゃないからね、デフォルトはrootにsuするけど
  • ifconfig: Windowsはipconfignなので間違えないように
  • tar -cvf (ファイル名) (ディレクトリー名): tarフォーマットでディレクトリーをシリアライズ
  • tar -zcvf (ファイル名) (ディレクトリー名): tarフォーマットでディレクトリーをシリアライズしてgz圧縮する
  • tar -xvf (ファイル名): tarフォーマットを解凍
  • tar -zxvf (ファイル名): gz圧縮されたtarフォーマットを解凍
  • scp (コピー元のファイル名) (コピー先のディレクトリー名): rcpのSSH版、リモートファイルシステムは "(ユーザ名)@(ホスト名):絶対ファイルパス"で指定、例: nsuzuki@macrpi:/home/nsuzuki/hoge/
  • sudo ifconfig (NICの名前) up/down: NICを有効/無効化、イーサネットなら通常eth0、WiFiは通常wlan0、有効にすればDHCPが動くはず(時間がかかる)
  • bannerを使えるようにする: sudo apt-get install sysvbanner