Raspberry PI NFS rootfs 和 Remote linux kernel
===
本篇主要透過TFTP和NFS功能,實作遠端系統開機(就是Rootfs和Linux kernel在遠端主機上)。
之前看到了一些介紹NFS rootfs的文章,感到有興趣,於是打算來實作看看。由於是第一次嘗試,所以遇到了一些問題。感謝各位社群網友的幫忙,才讓我成功完成。
---
### 環境:
Linux Kernel: https://github.com/raspberrypi/linux
U-Boot: https://github.com/u-boot/u-boot
Raspberry pi 4b+
Debian rootfs: https://rcn-ee.com/rootfs/eewiki/minfs/debian-11-minimal-arm64-2021-08-17.tar.xz
Raspberry pi Firmware: https://github.com/raspberrypi/firmware
Raspberry pi官方建置Linux Kernel文件: https://www.raspberrypi.org/documentation/computers/linux_kernel.html
Ubuntu Host
USB Ethernet Card
Raspberry pi PXE參考資料: https://linuxhit.com/raspberry-pi-pxe-boot-netbooting-a-pi-4-without-an-sd-card/
---
### Linux Kernel,Uboot編譯過程,Raspberry pi Firmware安裝請參考:
[Raspberry pi Build System (64bits)](https://hackmd.io/p_eQ-5QdRE-NpweHBE4_Eg?view)
請事先編譯好Linux Kernel, Uboot,分割好SDCARD,
並將Raspberry pi Firmware, config.txt安裝在SDCARD第一分區(FAT32開機磁區)。
:::info
* 由於Raspberry pi 預設的Linux Kernel設定檔案(bcm2711_defconfig)並沒有啟用Network NFS Rootfs,要在設定檔中補上設定。
* 需要補上設定的檔案為(Linux Kernel資料夾裡):
arch/arm64/configs/bcm2711_defconfig
* 確認圖中的設定,都有在檔案裡:

:::
本次實作僅保留第一分區的檔案(Raspberry pi Firmware, FDT, config.txt),並且砍掉Image(Linux Kernel)。
* Image(Linux Kernel)會由tftp load進來。
* Rootfs(第二分區)會由NFS load進來。
本次實作開機過程:
Mount the FAT32 boot partition => bootcode.bin => GPU firmware => UBoot (u-boot.bin) => Linux Kernel(tftp load) => Rootfs(Linux kernel load over NFS)
---
### TFTP架設:
* 透過xinetd將TFTP-server放置至背景執行。
[參考](https://yulun.me/2016/setup-tftp-server-on-ubuntu/)
1. 安裝套件
```bash=
sudo apt-get install xinetd tftpd tftp
```
2. 新增設定檔,/etc/xinetd.d/tftp
```text=
service tftp
{
protocol = udp
port = 69
socket_type = dgram
wait = yes
user = nobody
server = /usr/sbin/in.tftpd
server_args = -s /tftpboot
flags = IPv4
disable = no
}
```
:::info
* server_args: TFTP資料夾位置,這裡我直接指定Kernel編譯完成後,Image產生的資料夾
* user: nobody [參考](https://zh.wikipedia.org/wiki/Nobody_(%E7%94%A8%E6%88%B7%E5%90%8D))
* nobody是一個沒有任何權限的用戶。該用戶不擁有任何文件,也沒有任何特殊權限。只能訪問所有帳戶都能訪問的目錄或檔案。
:::
3. 建立目錄:
```bash=
sudo mkdir /tftpboot
sudo chmod -R 777 /tftpboot
```
4. 重新啟動xinetd:
```bash=
sudo service xinetd restart
```
5. 搬移編譯好的Linux Kernel到指定目錄:
```bash=
sudo cp arch/arm64/boot/Image /tftpboot
```
---
### NFS Server架設:
1. 安裝nfs-server
```bash=
sudo apt-get install nfs-kernel-server
```
2. 指定NFS server版本到V3,編輯/etc/default/nfs-kernel-server
```bash=
#RPCNFSDCOUNT=8
RPCNFSDCOUNT="--no-nfs-version 4"
```
3. 指定NFS分享目錄,編輯cat /etc/exports
```bash=
/export/user/fs *(rw,nohide,insecure,no_subtree_check,sync,no_root_squash)
```
:::info
/export/user/fs 為放置Rootfs的目錄
:::
:::info
no_root_squash: 用戶端使用 NFS 檔案系統的帳號若為 root 時,系統不會壓縮成nobody使用者,直接給予用戶端root權限
:::
4. 輸出設定內容並重新啟動nfs server完成設定
```bash=
sudo exportfs -ra
sudo systemctl restart nfs-kernel-server
```
---
### 安裝rootf到指定目錄:
請參考[完整範例](https://hackmd.io/p_eQ-5QdRE-NpweHBE4_Eg)
1. 從官方git將rootfs下載下來:
```bash=
wget https://rcn-ee.com/rootfs/eewiki/minfs/debian-11-minimal-arm64-2021-08-17.tar.xz
```
2. 解壓縮後,檔案如下:
```bash=
user@user-machine:~/test$ tar Jxvf debian-11-minimal-arm64-2021-08-17.tar.xz
./debian-11-minimal-arm64-2021-08-17/
./debian-11-minimal-arm64-2021-08-17/image-builder.project
./debian-11-minimal-arm64-2021-08-17/arm64-rootfs-debian-bullseye.tar
./debian-11-minimal-arm64-2021-08-17/user_password.list
```
3. 將Rootfs放置到目標目錄(/export/user/fs):
```bash=
sudo tar xvf debian-11-minimal-arm64-2021-08-17/arm64-rootfs-debian-bullseye.tar -C /export/user/fs
```
---
### 安裝Kernel到指定目錄:
Kernel編譯請參考[連結](https://hackmd.io/p_eQ-5QdRE-NpweHBE4_Eg)
* 安裝Linux module
```bash=
sudo make -j6 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=/export/user/fs modules_install
```
---
### 設定Uboot開機參數:
Uboot編譯請參考[連結](https://hackmd.io/p_eQ-5QdRE-NpweHBE4_Eg?view)
1. 新增u-boot script檔案。
```bash=
touch boot.cmd
```
內容如下:
```bash=
setenv ipaddr 192.168.1.10
setenv serverip 192.168.1.8
setenv bootargs 8250.nr_uarts=1 console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.8:/export/user/fs,proto=tcp,nfsvers=3 rw ip=192.168.1.10 rootfstype=nfs elevator=deadline rootwait
tftp ${kernel_addr_r} Image
booti ${kernel_addr_r} - ${fdt_addr}
```
:::info
如果是非靜態IP,"setenv ipaddr 192.168.1.10" 請改成 "dhcp"
192.168.1.8: 請填入自己遠端Server IP
192.168.1.10: 請填入自己Raspberry pi端IP(靜態IP請自己設定,但要與Server IP在同一個網域底下。如果是DHCP就請填上dhcp就可以了)
:::
2. 編譯(將boot.cmd編譯成boot.scr)
```bash=
mkimage -C none -A arm64 -T script -d boot.cmd boot.scr
```
3. 複製u-boot映像檔到SDCard第一分區(FAT32)
```bash=
sudo cp -v boot.scr ${SDCard第一分區}
```
---
最後
插上Raspberry pi開機,即可在console操作Raspberry pi
這裡透過screen操作console
sudo screen /dev/ttyUSB0 115200
* TFTP載入Linux Kernel:

* Linux Kernel 載入 Rootfs:
