Raspberry Pi 3 (Raspbian Jessie)にRT Preemptカーネルを導入

# はじめに
Raspberry Pi 3 (Raspbian Jessie)を対象とします．別に用意したLinuxマシンを使ってカーネルをクロスコンパイルします．別のLinuxマシンが無い場合は，Raspberry Pi上でコンパイルすることもできると思いますが，かなり時間がかかるはずです．ここでは，Ubuntu16.04 (64bit)をインストールしたマシンが手元にあると思って話を進めます．Raspberry Piは有線でLANに繋げてください．

# 準備
もし，Raspbianをインストールした直後なら，以下のコマンドでシステムを更新しておく．

```console
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
```

# カーネルのソースとRT Preemptパッチのダウンロード
カーネルのバージョンとパッチのバージョンは，マイナーバージョンの番号（最初の数字とドットの後の次の数字）まで合わせる必要がある．
現在のRaspbianのカーネルバージョンを知りたい場合は，Raspberry Piにログインしてターミナルを開いて，

```console
uname -a
```

を打てば確認できます．

RT Preemptのパッチですが，実は全てのバージョンにパッチが用意されている訳ではないみたいです．ですので，とりあずパッチの方から選ぶのが良いです．パッチは，

https://www.kernel.org/pub/linux/kernel/projects/rt/

に置かれているので，バージョン番号を選び，その中に"patch-$x.$y.*-rt*.patch.gz"という名前のファイルが有ることを確認して下さい．"*"の部分の番号も後で使うのでメモっておいて下さい．
続いてカーネルのソースを手に入れます．

https://github.com/raspberrypi/linux.git

にアクセスし，"Branch:"のプルダウンメニューを展開して，さっき確認したパッチのバージョン番号"$x.$y"に該当するバージョン番号を持つカーネルのソースがあることを確認して下さい．ブランチ名は，"rpi-$x.$y.y"という感じだと思います．

では早速，それぞれを手元にダウンロードしたいと思います．Linuxマシン側にログインし，以下を実行して下さい．

```console
cd
git clone -b rpi-$x.$y.y --depth 1 https://github.com/raspberrypi/linux.git
cd linux
wget https://www.kernel/org/pub/linux/kernel/projects/rt/$x.Xy/patch-$x.$y.*-rt*.patch.gz
zcat patch-$x.$y.*-rt*.patch.gz | patch -p1
```

"*"の部分は，先にメモっておいた数字を入れて下さい．エラーがでちゃったら失敗です．他のバージョンを試してみるのが良いでしょう．エラーが出なければ，~/linux/の中にRT Preemptのパッチがあたったカーネルのソースが準備されたことになります．

# クロスコンパイラなどの準備
お次は，パッチのあたったカーネルのソースをコンパイルするための準備です．Linuxマシン側で以下を実行して下さい（usernameの部分は，自分の環境に合わせて下さい）．もし，Raspberry Pi上でコンパイルするなら，クロスコンパイラを準備する必要はありません．次にカーネルオプションをRaspberry PiからLinuxに引き継ぐ部分が出てきますが，そこで，オプションをmodprobeで取り出し，解凍して.configを作った後，Raspberry Pi側に保存されたパッチのあたったソースのディレクトリ(~/linux/)に入れてmenuconfigに進んで下さい．

```console
cd
git clone https://github.com/raspberrypi/tools.git -depth 1
export ARCH=arm
export CROSS_COMPILE=/home/username/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-
export INSTALL_MOD_PATH=/home/username/rtkernel
export KERNEL=kernel7
```

もし，使っているLinuxマシンが32bitであれば，環境変数"CROSS_COMPILE"を設定するところで，"-x64"を消す必要があります．あと，最後の"KERNEL"に代入しているkernel7という値は，Raspberry Pi 3用のカーネルを示す値です．今後，更に新しいハードウェアが出てきた際には，ここを変える必要が出てくると思います．

# カーネルオプションの設定
ここは，一歩間違うと悲惨なことになる箇所です．Raspberry Pi側とLinux側を行き来する必要があり，少しややこしいので注意して進めて下さい．もし，どっかの手順でつまづいて，それをはしょったとすると，高確率で失敗するので，その時はWebで十分に情報を探して下さい．

```console
Raspberry Pi側で：
cd
sudo modprobe configs

Linux 側で：
cd
cd linux
scp $(user)@$(host or IP):/proc/config.gz ./
zcat config.gz > .config
```

$(user)と$(host or IP)の部分は，各自の環境に合わせて下さい．次は細かな設定です．Linux側で，

```console
cd
cd linux
make menuconfig
```

とすると，ターミナル内に設定画面が出てきます．以下を設定して下さい．

* CONFIG_PREEMPT_RT_FULL 
     Kernel Features ---->
     Preemption Model (Fully Preemptible Kernel (RT)) ---->
     Fully Preemptible Kernel (RT)
* Enable HIGH_RES_TIMERS
     General setup ---->
     Timers subsystem ---->
     High Resolution Timer Support

２つ目の項目はデフォルトで設定されると思いますが念のため．Saveするのを忘れがちなので気をつけて下さい．
首尾よく終わったらカーネルコンパイルの準備完了です．

# カーネルコンパイル
カーネルをコンパイルしていきます．Linux側で以下を実行して下さい．

```console
cd
cd linux
make zImage modules dtbs
make modules_install
mkdir $INSTALL_MOD_PATH/boot
./scripts/mkknlimg ./arch/arm/boot/zImage $INSTALL_MOD_PATH/boot/$KERNEL.img
cp ./arch/arm/boot/dts/*.dtb $INSTALL_MOD_PATH/boot/
cp -r ./arch/arm/boot/dts/overlays $INSTALL_MOD_PATH/boot/
cd $INSTALL_MOD_PATH
tar czf /tmp/kernel-$x.$y.*-rt*-v7+.tgz *
```

最後のtarで，"kernel-"に続く＊は，パッチやカーネルのリビジョン番号などに合わせ，最後の＊は＊のままにして下さい．/tmp/以下にできたkernel-*.tgzファイルは，RT PreemptパッチがあたったRaspbianカーネルそのものです．複数のRaspberry Pi 3に使えるものなので，一度作ったら，ここまでは手順をとばせるようになります．

# カーネルの置換
コンパイルしたカーネルをRaspberry Piに導入します．まずはLinux側で以下を実行し，Raspberry Pi側にカーネルを送って下さい．

```console
scp /tmp/kernel-$x.$y.*-rt*-v7+.tgz $(user)@$(host or IP):/tmp
```

続いてRaspberry Pi側での作業です．

```console
cd /tmp
tar xzf kernel-$x.$y.*-rt*-v7+.tgz
sudo rm -r /lib/firmware/
sudo rm -r /boot/overlays/
cd boot
sudo cp -rd * /boot/
cd ../lib
sudo cp -dr * /lib/
cd /boot
vim cmdline.txt
      "sdhci_bcm2708.enable_llm=0"という行を末尾に追加
sudo reboot
```

再起動後，リアルタイムカーネルが使われているかを確認します．

```console
uname -a
```

で，先程作ったカーネルのバージョンが見れればOKです．

# おわりに
以上でインストール作業は終了です．以後，apt-get upgradeなどを実行すると，カーネルのバージョンが戻ってしまうことがあります．ただ，戻ったとしても，コンパイル済みカーネルを保存しておき，カーネルの置換の手順を踏めばまたリアルタイムカーネルにできるので，そこまで恐れる必要はないはずです．