はじめに
去年 NVIDIA から Jetson Orin 向けの JetPack 6.0 Developer Preview (Jetson Linux R36.2) がリリースされました。
遅ればせながらちょっとずついじっていこうと思います。
色々深いことをやろうとすると kernel カスタマイズは必要になってくるので、kernel とドライバのビルドをやってみました。
要約
- Jetson Linuxの kernel と module をビルドしてみたよ
- 公式手順には乗ってないけど
./nvbuild.sh
でもビルドできたよ - R36.2からは kernel と Jetson 向けドライバはソースツリーが完全に分離したよ
- Jetson向けのドライバは kernel config では有効/無効の変更ができないよ
先人たちに感謝
先人たちのトレースになりますが、自分で手を動かさないと理解できない人なので、トレースしながら気になる点をメモしていこうと思います。
以下の記事を参考にしました。
公式サイトはこちら
準備
ホスト環境
公式サイトのQuickStartによると Ubuntu 22.04LTS か 20.04LTS を使う前提になっているようでしたが、手元の環境が Ubutnu 23.10 のため、23.10 で試しました。
You have a Jetson developer kit and a separate Linux (Ubuntu 22.04 or Ubuntu 20.04) host system.
公式ドキュメントのPrerequisitesでは以下のパッケージをインストールすればビルドできるとのことです。私の環境では、すでにビルド関係のパッケージが入っていたので検証はできていないです。エラーが出たら都度必要なパッケージを入れてみてください。
$ sudo apt install git-core
$ sudo apt install build-essential bc
ファイルのダウンロード
kernel のソースとビルドに必要な toolchain (コンパイラ等)をダウンロードします。
以下の Jetson Linux のサイトの「Downloads and Links」から以下の2つをダウンロードします。
- Driver Package (BSP) Sources
- → public_sources.tbz2
- Bootlin Toolchain gcc 11.3
- → aarch64--glibc--stable-2022.08-1.tar.bz2
Toolchainの展開
以下の手順では作業ディレクトリを $WORK
とします。
まず、作業ディレクトリを作成し、コンパイラ類である aarch64--glibc--stable-2022.08-1.tar.bz2 を展開します。
$ mkdir -p $WORK
$ cd $WORK
$ tar xf PATH/TO/aarch64--glibc--stable-2022.08-1.tar.bz2
aarch64--glibc--stable-2022.08-1
というディレクトリが作られ、その下に toolchain が展開されます。
ソースツリーの展開
公式のビルド手順を参考にアーカイブを展開していきます。
作業ディレクトリに public_sources.tbz2 を展開します。
公式の手順では <installer-path>/Linux_for_Tegra/..
と、Linux_for_Tegraの展開先に上書きする形で展開していますが、特に他のディレクトリに依存していないみたいなので作業ディレクトリに直に展開してしまいます。
$ cd $WORK
$ tar xf PATH/TO/public_sources.tbz2
Linux_for_Tegra/source/
以下にソースのアーカイブが展開されます。
たくさんありますが、kernelとmoduleに関連しているのは以下の 3 つだけのようです。
- kernel_src.tbz2
- kernel_oot_modules_src.tbz2
- nvidia_kernel_display_driver_source.tbz2
Linux_for_Tegra/source 以下に展開する手順になっているのですが、source 直下にもいろいろスクリプトとかMakefileが展開されて、管理が難しくなりそうなので、別ディレクトリに展開します。
$WORK/source
というディレクトリを作成し、その下にアーカイブを展開します。
$ mkdir -p $WORK/source
$ cd $WORK/source
$ tar xf $WORK/Linux_for_Tegra/source/kernel_src.tbz2
$ tar xf $WORK/Linux_for_Tegra/source/kernel_oot_modules_src.tbz2
$ tar xf $WORK/Linux_for_Tegra/source/nvidia_kernel_display_driver_source.tbz2
展開後は以下のようなファイル/ディレクトリが作成されます。
$WORK/source/
├── Makefile
├── generic_rt_build.sh
├── hardware/
├── hwpm/
├── kernel/
├── kernel_src_build_env.sh
├── nvbuild.sh
├── nvcommon_build.sh
├── nvdisplay/
├── nvethernetrm/
├── nvgpu/
└── nvidia-oot/
R36.2 では kernel の下はクリーンなカーネルが格納されていて(R36.2ではUbuntu Jammyのkernel)、Jetson用のコードは nvidia-oot
に括りだされているのが特徴的です(oot は out-of-tree の略)。
R35.4.1 までは Jetson 用の処理がマージされた形で kernel-tree が提供されていたのですが、kernel バージョンをユーザが(ある程度)自由に選択できるように完全に別ツリーの形で提供されています。
これは linux の driver を書いてた身からすると、結構挑戦的な変更のように思います。
かなりきれいにドライバを設計しないと、kernel 側と oot 側に分けるのが難しそうな気がしてます。
(kernel側の共通コードをちょっといじって対応するとか、足りてないAPIを勝手に追加して対応といった dirty hack が使えない)
さすが NVIDIA。Jeston の固有コードを積極的に upstream にマージしていっているだけありますね。
公式手順でビルド
ソースやtoolchainの準備ができたのでビルドしていきます。
kernel本体のビルド
Building the Jetson Linux Kernelに沿って make します。
toolchain は $WORK/aarch64--glibc--stable-2022.08-1
以下に展開されているので、CROSS_COMPILE=
でフルパスで参照します。
今回は real-time ではない normal な kernel をビルドするため、公式の2の手順(./generic_rt_build.sh "enable"
)はスキップします。
$ cd $WORK/source
$ export CROSS_COMPILE=$WORK/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu-
$ make -C kernel
defconfig に何が使われるのか気になったので Makefile を見てみたところ、defconfig
が指定されていました。kernel config を変更する場合は $KERNEL_DEF_CONFIG
を設定すると良いようです。
KERNEL_SRC_DIR ?= kernel-jammy-src
KERNEL_DEF_CONFIG ?= defconfig
...
$(MAKE) \
ARCH=arm64 \
-C $(kernel_source_dir) \
LOCALVERSION=$(version) \
$(KERNEL_DEF_CONFIG)
次に、ビルドした kernel と *.ko ファイルを rootfs のディレクトリにコピーします。
ここでは、どのようなファイルがコピーされるのかを調べたいので、適当に rootfs ディレクトリを用意してそこにインストールしてみます。
テストなので非rootで実行していますが、Linux_for_Tegra/rootfs にインストールする場合は公式ドキュメントのようにsudo -E make install
が必要です。
$ mkdir -p $WORK/rootfs/boot
$ mkdir -p $WORK/rootfs/lib
$ INSTALL_MOD_PATH=$WORK/rootfs make install -C kernel
$ cp kernel/kernel-jammy-src/arch/arm64/boot/Image /boot/
Out-of-Tree モジュールのビルド
kernel 本体のビルドが終わったら、Jetson 固有のモジュールをビルドします。
ここでも RT kernel は使わないので、2の手順はスキップします。
oot モジュールをビルドするときには、上でビルドした kernel 本体のパスを $KERNEL_HEADERS
に設定する必要があります。
$ cd $WORK/source
$ export CROSS_COMPILE=$WORK/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu-
$ export KERNEL_HEADERS=$WORK/kernel/kernel-jammy-src
$ make modules
ビルドしたモジュールを rootfs にインストールします。上のビルド時に作成した rootfs ディレクトリにインストールします。
$ INSTALL_MOD_PATH=$WORK/rootfs make modules_install
インストール前後のファイルを見ると、oot モジュールは rootfs/lib/modules/5.15.122-tegra/extra/
以下にインストールされるようです。
extra に配置することでオリジナルの kernel module と棲み分けをしているみたいですね。
nvbuild.sh でもビルドできた
公式手順では上のようなビルド手順が必要なのですが、なんと nvbuild.sh で一発でビルドできました。
toolchainとソースツリーの展開が終わったら、CROSS_COMPILE= を設定して ./nvbuild.sh だけでもビルドできました。楽ちん
$ cd $WORK/source
$ export CROSS_COMPILE=$WORK/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu-
$ ./nvbuild.sh
nvbuild.sh を使ったビルドでは、kernel_out
というディレクトリにソース等をコピーしてビルドするようです。
ビルドした .ko ファイルをインストールするには、INSTALL_MOD_PATH
を設定して./nvbuild.sh -i
でできるようです。
$ mkdir -p $WORK/rootfs/boot
$ mkdir -p $WORK/rootfs/lib
$ INSTALL_MOD_PATH=$WORK/rootfs ./nvbuild.sh -i
カスタマイズの方法は?
以上でビルドはできたので、どうすれば config を変更できるのか調べてみました。
まず kernel 本体側の config について。
こちらは上で記載したように KERNEL_DEF_CONFIG
に defconfig の名前を指定することでカスタマイズできそうです。
Linux kernel で一般的な、make menuconfig
などで .config を変更後、make savedefconfig
で defconfig を生成するなどで対応できそうです。
一方で nvidia-oot
ディレクトリについては、Makefileを変更しないとカスタマイズはできないようでした。
例えば camera 関係のドライバの Makefile は以下のようになっています。
obj-m
とモジュール化が強制的に指定されています。
個別に無効化するには Makefile を修正するしかなさそうです。
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
subdir-ccflags-y += -Werror
obj-m += max9295.o
obj-m += max9296.o
ifeq ($(findstring ack_src,$(NV_BUILD_KERNEL_OPTIONS)),)
obj-m += max96712.o
obj-m += ar1335_common.o
obj-m += lt6911uxc.o
obj-m += nv_imx185.o
obj-m += nv_imx219.o
obj-m += nv_imx274.o
obj-m += nv_imx318.o
obj-m += nv_imx390.o
obj-m += nv_imx477.o
obj-m += nv_ov5693.o
obj-m += nv_ar0234.o
obj-m += pca9570.o
obj-m += nv_hawk_owl.o
obj-m += virtual_i2c_mux.o
endif
kernel ツリーと nvidia-oot を完全に分離したため、oot 側の Kconfig を読み込ませることができないためと思われます。実際 nvidia-oot ディレクトリには Kconfig ファイルはありませんでした。
今の所、Jetson Linux R36.2 は Jetson Orin のみサポートしており、Xavier はサポートから外されたため、Orin で使われないドライバがツリー上から削除されたようなので、それほど気にしなくてもよいのかもしれません。
おわりに
Jetson Linux R36.2 の kernel と module をビルドして、カスタマイズする方法を調べてみました。
kernel ツリーと粗結合にすることで、ユーザが使いたい kernel バージョンで Jetson を使えるようにするという意欲的な試みを感じることができました。
一方で NVIDIA 製ドライバではカスタマイズ性が犠牲になっている面もあることがわかりました。