# 編譯 PREEMPT_RT 核心與安裝
contributed by < `OscarShiang` >
## 安裝 Ubuntu Linux
因為我們目標的環境是 64 位元的 Linux,但目前 Raspbian 還不支援 64 位元,所以我們改以 Ubuntu Linux Server 64-bit 作爲我們安裝的系統。
建議使用樹莓派基金會所提供的 [Raspberry Pi Imager](https://www.raspberrypi.com/software/) 來製作 SD card
:::warning
:warning: 目前 rpi imager 無法正確輸出供 Ubuntu 開機使用的設定檔,如果使用 rpi imager 的 Advanced setting 可能會導致 Ubuntu 開機失敗,詳見 [Bug #1914878 “At boot on Pi4 with 20.04.02](https://bugs.launchpad.net/ubuntu/+source/u-boot/+bug/1914878) 以及 [Unable to boot successfully when using advanced options with Ubuntu Server 20.04.3 LTS #286](https://github.com/raspberrypi/rpi-imager/issues/286) 的討論
:::
## 網路連線設定
為了讓樹莓派在連接電源後,可以主動連上無線網路,讓後續得以使用 SSH 的方式連進樹莓派中,我們首先需要進行 Wi-Fi 連線資訊的設定。
因為 Raspberry Pi Imager 目前還無法正確輸出 Ubuntu 所使用的設定檔,若我們利用 <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>X</kbd> 做諸如 Wi-Fi 密碼、SSH 連線等等資訊的設定,則會導致 Ubuntu 不能如預期啟動。
因此以下使用 netplan config 的方式來進行設定。
### 透過 netplan config 進行設定
如果我們查看製作完成的 SD 卡,應該會看到 system-boot 以及 writable 兩個 section。我們點開位在 system-boot 當中的 network-config 並在其中加入以下的內容:
```yaml
wlan0:
dhcp4: true
optional: false
<access-point-name>:
"<access-point-password>"
```
這樣在開機時系統就會主動載入設定檔中的內容,建立 Wi-Fi 的連線。
若樹莓派開機後順利的連上網路,我們就可以在區域網路中看到其被分配到的 IP 位址,就可以透過 SSH 連到樹莓派上
```shell
$ ssh ubuntu@<your-ip-address>
```
預設的使用者帳號密碼分別是:
- 帳號: `ubuntu`
- 密碼: `ubuntu`
完成樹莓派的網路設定後,接著就可以著手來編譯核心了!
## 編譯 PREEMPT_RT 核心
這邊主要是參考【[編譯 Raspberry Pi 4 的 Kernel Image](https://hackmd.io/@henrybear327/compile-kernel-for-raspberry-pi-4-from-source-tw)】的流程。
### 準備工具
因為在樹莓派上編譯核心的時間很長,建議使用在樹莓派之外的電腦使用交叉編譯器進行編譯。
使用 apt 進行安裝相關工具
```shell
sudo apt install crossbuild-essential-arm64 build-essential libncurses-dev linux-tools-common
```
### 下載原始碼
使用以下指令下載程式碼,並將核心程式碼
```shell
$ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.10/patch-5.10.73-rt54.patch.xz
$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.73.tar.xz
$ tar axf linux-5.10.73.tar.xz
```
將 rt-patch 的修改 apply 到核心程式碼中
```shell
$ xzcat ../patch-5.10.73-rt54.patch.xz | patch -p1
```
### 調整核心組態
我們首先設定欲輸出的系統架構並指定交叉編譯器
```shell
$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-linux-gnu-
```
接著開啟 menuconfig 調整核心組態
```shell
$ make defconfig
$ make menuconfig
```
:::info
:bulb: 因為核心組態之間可能存在相依性,直接修改的 `.config` 在編譯之前會被重新讀取,而將未符合相依性的組態關閉的情況。建議使用 `menuconfig` 操作
:::
確保以下的組態有被正確設定
- `LOCALVERSION="-raspi"`
- `LOCALVERSION_AUTO=n`
- `VIRTUALIZATION=n`
- `NO_HZ_FULL=y`
- `EMBEDDED=y`
- `PREEMPT_RT=y`
- `SQUASHFS_XZ=y`
硬體平台支援的部分只要有 `BCM2835 Family` 即可
- `ARCH_BCM2835=y`
其他的平台支援可以全部抑制掉
### 編譯核心
```shell
$ make -j$(nproc) bindeb-pkg
```
## 在 Ubuntu Linux for Raspberry 上安裝新的核心映像檔
使用 `dpkg` 安裝映像檔與標頭檔的 debian 套件
```shell
$ sudo dpkg -i <deb-for-linux-image> <deb-for-linux-header>
```
`dpkg` 在安裝完畢之後預設會檢查目前安裝的版本是否比開機時使用的版本更新,如果是的話就會自動執行 `flash-kernel` 置換開機的設定,我們也可以透過 `flash-kernel` 直接指定要使用的核心版本
```shell
$ sudo flash-kernel --force 5.10.78-rt55-raspi
```
完成置換並重新開機後,就可以看到結果了
```shell
$ uname -r
5.10.78-rt55-raspi
```
:::warning
:warning: Raspberry Pi 4B 預設使用 UART1 (mini UART) 作為 serial 輸出,但在置換核心過程中安裝的 firmware 並不會自動帶入啟動 UART1 的參數,所以需要在 `/boot/firmware/cmdline.txt` 中加入 `8250.nr_uarts=1`
:::
## 參考資料
- [編譯 Raspberry Pi 4 的 Kernel Image](https://hackmd.io/@henrybear327/compile-kernel-for-raspberry-pi-4-from-source-tw)
- [Bug #1914878 “At boot on Pi4 with 20.04.02](https://bugs.launchpad.net/ubuntu/+source/u-boot/+bug/1914878)
- [Unable to boot successfully when using advanced options with Ubuntu Server 20.04.3 LTS #286](https://github.com/raspberrypi/rpi-imager/issues/286)
- [HOWTO setup Linux with PREEMPT_RT properly](https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/preemptrt_setup)
- [using u-boot to load linux kernel fail to initiate mini-uart](https://forums.raspberrypi.com/viewtopic.php?t=246215)
###### tags: `raspi`