NSB-3NR1T1MLVをLinux箱として使う (Debian インストール)

# はじめに

ELECOMのNAS NSB-3NR1T1MLV をLinux箱化する計画を、ここまで3回 ( [インタフェース調査](https://qiita.com/ac100v_net/items/ac9df1a84aee9c962105)、[OLED制御](https://qiita.com/ac100v_net/items/3061645c29239eebddbe)、[ツールチェーンとTFTPブート](https://qiita.com/ac100v_net/items/42f2814823f45fa24dd1) ) に渡って記事にしてきましたが、ようやく Linux インストール編です。
本製品への Linux インストールは既に Gentoo で成功された方がいますが、 Gentoo はめんどくさそうなので(個人の感想です)、この手の改造では定番の Debian を入れてみることにしました。 Linux カーネルのビルドが必要となりますが、最小限の変更で動かすことを目指します。


# 準備

## 機材

1. NSB-3NR1T1MLV (本体)

2. CISCO準拠のシリアルケーブル

3. ターミナルエミュレータ
115,200bpsでシリアル通信ができるもの。 インストールの後半パートで ssh を使いますが、このターミナルエミュレータで兼用してもよいし、Linux PCからsshしてもよいです。

4. Linux PC
カーネルのクロスコンパイル用、TFTPサーバとして使います。

5. インターネット接続
ネットワークインストールで使います。1ギガバイトぐらいダウンロードしても遅さや課金で泣かなくてすむ回線をご用意ください。


## Debian のバージョン選択 ... Jessie? Stretch?

結論からいえば、細かいことを気にしなければどちらも動きました。

2018年8月時点では、 Debian 9 (Stretch) が安定版、 1つ前の安定版 は Debian 8 (Jessie) です。
今回は、カーネルだけを本製品用のものに差し替えて Debian を動かそうと言う魂胆ですが、本製品のカーネルバージョンは 3.4.86 と古いので、新しいカーネルを前提とする Debian との非互換で動かない懸念があります。敢えて古いバージョンの Debian を選ぶ手堅い(?)方法もありますが、せっかくなのでなるべく新しい Debian を動かせるかどうか模索していきます。

各バージョンのカーネルやlibcのバージョンを [Debian Wiki](https://wiki.debian.org/DebianReleases) などから調べました。

|Ver|Code name|kernel|gcc|libc|MIN_KERNEL_<br/>SUPPORTED|
|:-:|:-:|:-:|:-:|:-:|:-:|
|7|Wheezy |3.2 |4.7.2|eglibc 2.13  |2.6.26|
|8|Jessie |3.16|4.9  |libc6 2.19-18|2.6.32|
|9|Stretch|4.9 |6.3  |libc6 2.24-11|3.2|

MIN_KERNEL_SUPPORTED は glibc が対応する最低限のカーネルバージョンです。 Debian の glibc ソースコードパッケージの debian/sysdeps/linux.mk に書いてあります。たいていのプログラムは glibc に依存しているので、この条件を満たさないと壊滅的な状況になります。
本製品付属のカーネル 3.4.86 は今のところ MIN_KERNEL_SUPPORTED をクリアしているので、 Stretch でも glibc は動作します。次のバージョン (Debian 10 Buster) ではサポート外になりそうですが、当分先のことなので気にしないことにします。

他にもカーネル固有の機能を応用したソフトが旧カーネルでうまく動かない可能性もありますが、実際に動かして問題が出てから考える方針で。

# 手順

## クロスコンパイラ

armhf 用の gcc 4.8.5 を Buildroot で作りました。要領は [以前の記事](https://qiita.com/ac100v_net/items/42f2814823f45fa24dd1) と同様ですが、 Target ABI を EABIhf にしました。

## カスタムカーネルのビルド

製品版のカーネルそのままでは Debian は起動しません。インストールの前に、コンフィグレーションを変更したカスタムカーネルを作成します。

### ソースファイル入手

ELECOM が配布している GPL ソース一式をダウンロード。 もちろん Linux カーネル (linux-3.4) も含まれています。

http://www.elecom.co.jp/support/download/storage/hd/nas/linux-nas/nsb-3nr_3ms/


### カーネルのコンフィグレーション

```
# make ARCH=arm menuconfig
```

もともとの .config に対して、以下3つは Y に変更する必要があります。

- CONFIG_ARM_THUMB (これが無いと `runaway loop modprobe binfmt-464c` が出てインストーラ起動不能)
- CONFIG_CGROUPS (systemdが使う)
- CONFIG_FHANDLE (systemdが使う)

M になっている項目も Y にしておくと面倒が少ないかもしれません。

### カーネルのビルド

あらかじめ u-boot-tools をインストールしておいて、

```
# make ARCH=arm uImage
```

これで u-boot 形式でカーネルが生成されます。
できあがった `arch/arm/boot/uImage` を TFTP サーバにコピーして、カーネルの準備完了です。

## Debian インストール

### インストール用 initrd

armhf 用のネットワークインストール用 initrd をダウンロードして、 TFTP サーバにコピーします。
自分がやったときは Debian 9.4 でしたが、記事作成時点で 9.5 が出ており、下記リンクは切れています。

http://ftp.jaist.ac.jp/pub/Linux/debian/dists/Debian9.4/main/installer-armhf/current/images/network-console/initrd.gz

### u-boot

あらかじめ、本体のCONSOLE端子にシリアルコンソール用ケーブルを接続、LAN1～LAN3のどこかにLANケーブルを接続してTFTPサーバおよびインターネットが見えるようにしておきます。
本体の電源をオンしてからシリアルコンソールで何か入力して、 u-boot のプロンプトを出します。
以下のように入力するとカスタムカーネルが起動し、 Debian インストーラが立ち上がるはずです。

```
U-BOOT # set ipaddr 192.168.0.222【注1】
U-BOOT # set serverip 192.168.0.210【注2】
U-BOOT # tftpboot 0x2100000 uImage
U-BOOT # tftpboot 0x30000000 initrd.gz
U-BOOT # set bootargs initrd=0x30000000,0x0139638e【注3】 root=/dev/ram0 rw console=ttyS0,115200 SB_PHY=PPSS ni_napi_budget=16 qm_acp_enable=0 ni_rx_noncache=0 qm_int_buff=0 ethaddr0=XX:XX:XX:XX:XX:XX【注4】 ethaddr1=XX:XX:XX:XX:XX:XX ethaddr2=XX:XX:XX:XX:XX:XX  mtdparts=cs752x_nand_flash:1024K@0x00100000(uboot-env0),1024K@0x00300000(uboot-env1),1024K@0x00400000(sb0),1024K@0x00500000(sb1),153600K@0x15200000(rootfs),174080K@0x0A800000(rootfs_data),6144K@0x00600000(kernel1),153600K@0x01200000(rootfs1)
U-BOOT # bootm 0x2100000
```

【注1】u-boot でTFTP通信するときだけ使うIPアドレスです。
【注2】TFTPサーバのIPアドレスです。
【注3】2個目の値は initrd.gz のサイズです。
【注4】本体記載の値。

### インストーラ前半

ここではシリアルコンソールで作業するパートを前半、SSHで接続するパートを後半とよびます。
インストーラの前半では、表示が乱れないようにコンソールは80桁24行にしましょう。

ネットワークインストール用の Ethernet インタフェースを聞かれるので eth1 を選択。
ホスト名やドメイン名はお好みで。
インストール用のSSH接続に誘導されたら、そちらに移行します。

### インストーラ後半

最初にインストール方法を聞かれます。 Start installer でよいです。 
次に、 No kernel modules were found と怒られますが、 Debian 由来でないカスタムカーネルを使っているせいなので、気にせず Yes を選択して次に進みます。

ディスクのパーティショニングは、製品版ファームウェアも動くように /dev/sda1～5 は温存、 /dev/sda6 を縮小して、 /dev/sda7 を Debian インストール用に新規作成しようとしたのですが、インストーラではうまくパーティションを切れませんでした。仕方なく製品版のファームウェアでパーティション編集してからインストールやり直し。
製品版のファームウェアを二度と使わないつもりなら、ディスク全体を　Debian に割り当ててもよいのかもしれません。

あとはほぼ自動でインストールが進みます。所要時間は正味20分ぐらいです。GRUB のインストールに失敗して真っ赤な画面が出ますが、続行してインストールを完了させます。

## 試運転

```
U-BOOT # set ipaddr 192.168.0.222
U-BOOT # set serverip 192.168.0.210
U-BOOT # set debian_env 'root=/dev/sda7 init=/sbin/init console=ttyS0,115200 SB_PHY=PPSS ni_napi_budget=16 qm_acp_enable=0 ni_rx_noncache=0 qm_int_buff=0 ethaddr0=XX:XX:XX:XX:XX:XX ethaddr1=XX:XX:XX:XX:XX:XX ethaddr2=XX:XX:XX:XX:XX:XX mtdparts=cs752x_nand_flash:1024K@0x00100000(uboot-env0),1024K@0x00300000(uboot-env1),1024K@0x00400000(sb0),1024K@0x00500000(sb1),153600K@0x15200000(rootfs),174080K@0x0A800000(rootfs_data),6144K@0x00600000(kernel1),153600K@0x01200000(rootfs1)'
U-BOOT # set debian 'tftpboot 0x2100000 uImage;setenv bootargs ${debian_env};bootm 0x2100000'
U-BOOT # run debian
```

シリアルコンソールに `login: ` が表示されたらおめでとうございます。

## TFTPブートからの脱却

このままだと、別のファイルサーバに接続しないと起動できないNASという前衛的な製品が完成してしまいます。これでは普段使いには不便なので、電源を入れればひとりでに Debian が立ち上がる状態を目指します。
具体的にはカスタムカーネルを内蔵Flashの /dev/mtd6 に書き込んで、起動時に u-boot が内蔵Flashのカーネルを自動的に読み出して起動するようにします。 /dev/mtd6 は製品版のファームウェアではバージョンアップ時に使われるようで、古いカーネルがここに入ります。製品版ファームウェアのバージョンアップなど二度とないでしょうから、ここをカスタムカーネル置き場とします。

### mtd-utils をインストール

インストールした Debian をさっそく活用します。 mtd-utils は内蔵Flashを操作するためのツールです。

```
# apt install mtd-utils
```

### カーネル uImage のサイズを 128KiB の倍数にする

次に使う flashcp というツールは、書き込むファイルのサイズが内蔵Flashの書き込み単位 (128KiB) の倍数でないとエラーを出します。uImage の後ろに 0 でもパディングしてサイズ調整してください。

### Flash に書き込み

書き込み元のファイルや書き込み先を間違わないように注意。この手のツールにありがちな、「データが消えますがよろしいですか？」みたいな確認は無しで容赦なく書き込みが行われます。

```
# flashcp -v uImage.128k_aligned /dev/mtd6
Erasing blocks: 34/34 (100%)
Writing data: 4352k/0k (100%))
Verifying data: 4352k/0k (100%))
```

### u-boot

自動的に Debian をFlashブートするように設定します。
もしも元の製品ファームウェアが恋しくなったら環境変数 bootcmd を元に戻してください。

```
U-BOOT # set debian_env 'root=/dev/sda7 init=/sbin/init console=ttyS0,115200 SB_PHY=PPSS ni_napi_budget=16 qm_acp_enable=0 ni_rx_noncache=0 qm_int_buff=0 ethaddr0=XX:XX:XX:XX:XX:XX ethaddr1=XX:XX:XX:XX:XX:XX ethaddr2=XX:XX:XX:XX:XX:XX mtdparts=cs752x_nand_flash:1024K@0x00100000(uboot-env0),1024K@0x00300000(uboot-env1),1024K@0x00400000(sb0),1024K@0x00500000(sb1),153600K@0x15200000(rootfs),174080K@0x0A800000(rootfs_data),6144K@0x00600000(kernel1),153600K@0x01200000(rootfs1)'
U-BOOT # set debian 'nboot 0x2100000 0 0x600000;setenv bootargs ${debian_env};bootm 0x2100000'
U-BOOT # set bootcmd 'run debian'
U-BOOT # saveenv
U-BOOT # run debian
```

# 問題点

当初は細かいことは気にせず、動けば勝ちとして逃げ切るつもりでしたが、細かいとは言いにくい現象も起こっているのでメモ。いずれも Debian 9 で起こった現象です。

* 本体の電源ボタンを押してもシャットダウンしない

shutdown コマンドで電源を切ることはできます。
ちなみに製品版ファームウェアでは、電源ボタンを監視するソフトが動いていました (/sbin/pwr-test) 。

* シャットダウン時にエラーが出る

systemd がカーネルにちょっかいを出しているようです。他の問題の予兆なのかもしれませんが、ともかくシャットダウンそのものは完了するので見なかったことにします。

```
systemd-shutdown[1]: Sending SIGTERM to remaining processes...
systemd-journald[1082]: Received SIGTERM from PID 1 (systemd-shutdow).
systemd-shutdown[1]: Sending SIGKILL to remaining processes...
systemd-shutdown[1]: Hardware watchdog 'G2 Watchdog', version 0
systemd-shutdown[1]: Failed to ping hardware watchdog: Bad address
```

```
systemd-shutdown[1]: Detaching DM devices.
systemd-shutdown[1]: Detaching DM 253:1.
device-mapper: ioctl: ioctl interface mismatch: kernel(4.22.0), user(4.27.0), cmd(4)
systemd-shutdown[1]: Could not detach DM /dev/dm-1: Invalid argument
```

* 電源オフの死に際にカーネルパニックを出す

```
systemd-shutdown[1]: Powering off.
sd 2:0:0:0: [sda] Synchronizing SCSI cache
sd 2:0:0:0: [sda] Stopping disk
Power down.
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
```

* reboot してから5分ぐらい後に勝手に再リセットがかかることがある

OSのシャットダウンが動くとかカーネルパニックとかでなく、突然マイコンがリセットされるような感じです。
原因が分かっていませんが、最初の5分を乗り切れば大丈夫っぽいので深く考えないことにします。

# まとめ

* ELECOMのNAS NSB-3NR1T1MLV に Debian 9 (Stretch) の armhf 版をインストールしました。
* Debian を動かすため、製品版のカーネルのコンフィグレーションを一部変更してカスタムカーネルをビルドしました。
* カスタムカーネルを内蔵Flashに書き込み、Debianを自動起動するようu-bootを設定しました。
* シャットダウン動作が一部怪しいものの、動作しているようです。

# 参考情報

1. [NSB-3NR1T1MLVにGentooを入れる](http://misodengaku.hatenablog.com/entry/2018/01/24/234518)
ベースシステムのインストールは Gentoo のほうが簡単です。インストール後の運用はなんだかめんどくさそう (個人の感想) ですが、 本記事のように Debian をだましだまし使うのと比較して、どちらが自分に適しているか判断されるとよいと思います。

1. [Debianインストール中の動画](https://youtu.be/NhVnK2fq74I)
インストーラ前半部分の様子をキャプチャしたものです。何かの参考になるというわけでもないですが、低画質も相まって1990年代風の絵になって面白かったのでアップロードしました。