Jetson Nanoのクロスビルド環境の構築

私は、昨年の春頃から、Jetson Nanoを使用してきましたが、カーネルの再構築なども、まずは、Jetson Nano本体でのNativeな環境でやってきました。ですが、今回、Ubuntuを入れたホストPCを用いて、Jetson Nanoの一通りのセットアップを行ってみまして一区切りつきましたので、この記事にまとめておこうと思います。

ちなみに、今回、NVIDIA Jetson Linux Driver Package (L4T) のバージョンは、2021/1/21にリリースされた32.5 をベースに環境構築しています。

また、カスタマイズの内容に依っては、SDK Managerは必須ではないと思いますが、Jetson Nanoが起動しなくなったときの復旧や、Device Treeの更新などを行うために必要となる書き込みツール(flash.sh)もSDK Managerと共に提供されている、などで有用なツールですので、この記事では、SDK Managerのインストールを前提に書いてみます。

<details><summary>flash.sh</summary><div>
flash.shは、SDK Managerをインストールすると、Linux_for_Tegraディレクトリの直下に置かれます。 また、SDK Managerをインストールせずとも、[L4T Driver Package (BSP)](https://developer.nvidia.com/embedded/l4t/r32_release_v5.1/r32_release_v5.1/t210/jetson-210_linux_r32.5.1_aarch64.tbz2)にも含まれています。
あと、32.5から、[New way of flashing](https://docs.nvidia.com/jetson/l4t/#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/flashing.html#wwpID0E0XG0HA)という新しいフィーチャも用意されたみたい(まだちゃんと読めてませんが、NFSで書けるようになったとか)です。</div></details>

## SDK Managerのダウンロード＆インストール

[NVIDIA SDK Manager](https://developer.nvidia.com/nvidia-sdk-manager)

から、SDK Managerをダウンロード。インストールなどのやり方は上記のページにも書かれているが、

```$ sudo apt install sdkmanager_1.4.0-7363_amd64.deb```

などとインストール。実行は、

```$ sdkmanager```

と起動してみたが、`No available releases for Ubuntu 20.04`
などと言われた。

[os-releaseを編集してごまかしたと言っている人がQiitaにいらした](https://qiita.com/hama6767/items/2351cf1f7f57bddd9f2a)ので、まあ、VERSION_IDを編集するだけぐらいならば変な副作用も出ないだろうということで、同じようにやってみた。(その後も特に支障なし。ありがとう。)

もう一度、

```$ sdkmanager```

で起動し、無事、SDK Managerが動作した。ここで、ダウンロードとインストールまでは実行して、ターゲットのJetson Nanoへのflash(書き込み)はSkipとし、この後で手動で対応することにしている。

また、SDK ManagerからダウンロードしたJetPackの環境の上に、この下の手順で、カーネルのクロスコンパイル環境を構築しているので、また、SDK Managerで新しいJetPackをダウンロード＆インストールする際には気をつけるべきだろう。(そのとき、本記事をメンテする)

## ソースファイルのダウンロードと展開

[Manually Downloading and Expanding Kernel Sources]
(https://docs.nvidia.com/jetson/l4t/#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/kernel_custom.html#wwpID0E0LD0HA)

に従って、手動でソースファイル一式をダウンロード。
ソースファイル一式のファイル名は、public_sources.tbz2。

```$ tar -tjf public_sources.tbz2```

などと、圧縮されているパスを確認すると、Linux_for_Tegraから下で固められているので、

そのファイルを、~/nvidia/nvidia_sdk/JetPack_4.5_Linux_JETSON_NANO_DEVKITの下に置いて、そこにcdして、

```$ tar -xvjf public_sources.tbz2```

などと展開。そして、カーネルのソースは、kernel_src.tbz2というファイル名で、Linux_for_Tegra/source/publicに置かれているので、それも展開。

## カーネルのビルド作業

[Building the NVIDIA Kernel]
(https://docs.nvidia.com/jetson/l4t/#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/kernel_custom.html#wwpID0E06C0HA)

に従ってカーネルのビルド作業。上記のページの対応を書くと、

* ツールチェインのインストール(To build the Jetson Linux Kernelの2.)
* 環境変数の設定(To build the Jetson Linux Kernelの1.など)
* ビルド実行(To build the Jetson Linux Kernelの3.と4.)
* インストール領域へのコピー(To build the Jetson Linux Kernelの5.と6.と7.)

という感じ。

### ツールチェインのインストール

[Jetson Linux Driver Package Toolchain]
(https://docs.nvidia.com/jetson/l4t/#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/xavier_toolchain.html#)

に従って実行。

既にビルドされているGCCのパッケージ
gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu.tar.xz
をダウンロードし、例えば、ホームディレクトリの直下に、l4t-gccというサブディレクトリを切って、そこにファイルを展開し、環境変数CROSS_COMPILEにその環境を設定しておいて、後のクロスコンパイル作業時に使用する形。

### 環境変数の設定

```
export JETSON_NANO_KERNEL_SOURCE=$HOME/nvidia/nvidia_sdk/JetPack_4.5_Linux_JETSON_NANO_DEVKIT/Linux_for_Tegra/source/public
export CROSS_COMPILE=$HOME/l4t-gcc/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
export TEGRA_KERNEL_OUT=$JETSON_NANO_KERNEL_SOURCE/build
export LOCALVERSION=-tegra
export NVIDIA_SDK_MANAGER=$HOME/nvidia/nvidia_sdk/
export DTB=tegra210-p3448-0000-p3449-0000-b00.dtb
export JETPACK_L4T=$NVIDIA_SDK_MANAGER/JetPack_4.5_Linux_JETSON_NANO_DEVKIT/Linux_for_Tegra
```

これらの環境変数の設定は、例えば、build_setup.shに上記のような環境変数一式の設定を記述しておいて、sourceコマンドにて実行する。

### ビルド作業

```
$ cd (kernel-4.9のディレクトリ)
$ source build_setup.sh
$ mkdir -p $TEGRA_KERNEL_OUT // 最初のビルド作業時に一度だけ実行
```

そして、いよいよ、カーネルのビルドの実行。
まずは、カーネルのコンフィグの初期化。

```
$ make ARCH=arm64 O=$TEGRA_KERNEL_OUT tegra_defconfig
```

既に実施すべきカーネルのカスタマイズがあるのならば、その対応をここで実施。
例えば、新規のドライバのソースファイルの追加、Makefileの編集、など。
.configも編集。

そして、以下のように、コンフィグ、ビルド、と作業を進める。

```
$ make ARCH=arm64 O=$TEGRA_KERNEL_OUT oldconfig
$ make ARCH=arm64 O=$TEGRA_KERNEL_OUT -j8
```

ここまでで、カーネル、Device Tree、モジュール、などが一式生成される。

そして、以下は、上記のビルド作業で生成された成果物(カーネル、Device Tree、ドライバ)を一式、インストール領域にコピーする作業。

この部分、NVIDIAのサイトの記述(To build the Jetson Linux Kernelの5.と6.と7.)はちょっと分かりにくい。要するに、この後の、flash.shの実行に備えて、カーネルイメージはkernel/Imageに、Device Treeファイルはkernel/dtb/に、modulesは、ターゲットのrootファイルシステム内のlib/modulesに、それぞれコピーする、ということ。

```
$ sudo make ARCH=arm64 O=$TEGRA_KERNEL_OUT INSTALL_MOD_PATH=$JETPACK_L4T/rootfs modules_install
$ cd $JETPACK_L4T
$ sudo cp -p $TEGRA_KERNEL_OUT/arch/arm64/boot/Image kernel/
$ sudo cp -p $TEGRA_KERNEL_OUT/arch/arm64/boot/dts/$DTB kernel/dtb/
```

ここまでで、ターゲットのJetson Nanoのカスタマイズした環境がホストPC上に構築されている状態になっている。

## Jetson Nanoのシステムへの書き込み

後は、何らかの形で、ターゲットのJetson Nanoのシステムに書き込めばよい。その一つの方法として、SDK Managerが実施しているはずの、flash.shを用いた書き込みを、以下で手動で実施する。

<details><summary>参考サイト</summary><div>例えば、RidgeRunのサイトのこのページ([How to build NVIDIA Jetson Nano kernel - RidgeRun Developer](https://developer.ridgerun.com/wiki/index.php?title=Jetson_Nano/Development/Building_the_Kernel_from_Source))。NVIDIAのサイトは、多分、開発者キットだけではなく、量産系のことも考えているからだと思うが、どうにも分かりにくい。production moduleになったときのことも考えると、[Flashing and Booting the Target Device](https://docs.nvidia.com/jetson/l4t/#page/Tegra%20Linux%20Driver%20Package%20Development%20Guide/flashing.html#wwpID0E0TJ0HA)も、また別途読み解いておこうと思う。</div></details>

<details><summary>他の方法について</summary><div>
NVIDIAから提供されているJetPackのSDイメージで、Jetson Nanoを起動およびセットアップした後、そのSDをホストのUbuntu PCからマウントして、その中のファイルシステムに、上の手順で生成したカスタマイズしたカーネルイメージその他をコピーする、ということでもよいかもしれない。だが、Device Treeの更新がなく、Imageとmodulesだけのカスタマイズならば、それで行ける気がするが、Device Treeは、実体がどこか、いまいち把握できていない。ただし、jetson-ioを用いてDevice Treeをカスタマイズする手法を用いれば、flash.shに依存しなくてもよい(はず)ことはわかっている。
</div></details>

Jetson NanoとUSBで接続し、Force Recovery Modeで起動。
次の、flash.shを実行することで、Jetson Nanoのシステムに書き込みを行う。

```
sudo ./flash.sh jetson-nano-qspi-sd mmcblk0p1
```

これで、Jetson Nanoへの書き込みが終わると、自動的にリブートされて、Jetson Nano側のセットアップ作業に進むことになる。

最後の、flash.shによるJetson Nanoへの書き込みの部分は、SDK Managerから実行すればよいのかもしれないが、ここでは、カーネルのカスタマイズを入れるためのビルド作業を行う必要があるため、このような手順としている。

## その他、予告など

以上が、Ubuntu PCを用いたJetson Nanoのセットアップの方法でした。

予告ですが、この記事の一番上でも書きましたが、私は、ずっと、ほぼJetson Nano単体で、カーネルのカスタマイズなど行って来まして、あまり速くないPCを使うぐらならば、Jetson Nanoの方が速いようなところもありますし、Jetson-IOなどを使えば、それなりにNative環境だけでもやれているみたいですので、その手順をまた別途まとめてみようかと思います(⇒[別記事](https://qiita.com/MakotoA67/items/8d2118e9a34eccb1cc6f)参照)。

2021/9/7追記: 2021/8/3リリースの32.6.1でも同様の環境構築を行い、

* flash.shを実行した際に、xmllint not foundと言われ、libxml2-utilsをインストールしたこと
* デバイスツリーのセンサ系の構成が若干変わっていたことで、編集の仕方が若干変わったこと

の2点を除けばほぼ問題なく同じように構築できたと思います。

2021/12/28追記: Jetson Xavier NX開発者キット及びJetson TX2 NX moduleでも、L4T 32.6.1環境にて本記事と同様の環境構築を行い、特に支障ありませんでした。