Raspberry pi 4 Build System (64bits)
===
本篇紀錄如何透過建置U-boot, Linux Kernel和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
在開始建置之前,請先將Cross compiler -- aarch64-linux-gnu,安裝到自己的電腦上。
* 如果Host是使用ubuntu或是debian,可使用以下指令輕易安裝Cross compiler:
```bash=
sudo apt install -y gcc-aarch64-linux-gnu
```
---
Raspberry pi開機流程:
[參考RPi_Software](https://elinux.org/RPi_Software)
:::info
The boot order and components are as follows:
* First stage bootloader - This is used to mount the FAT32 boot partition on the SD card so that the second stage bootloader can be accessed. It is programmed into the SoC itself during manufacture of the RPi and cannot be reprogrammed by a user.
* Second stage bootloader (bootcode.bin) - This is used to retrieve the GPU firmware from the SD card, program the firmware, then start the GPU.
* GPU firmware (start.elf) - Once loaded, this allows the GPU to start up the CPU. An additional file, fixup.dat, is used to configure the SDRAM partition between the GPU and the CPU. At this point, the CPU is release from reset and execution is transferred over.
* User code - This can be one of any number of binaries. By default, it is the Linux kernel (usually named kernel.img), but it can also be another bootloader (e.g. U-Boot), or a bare-bones application.
:::
簡單來說就是:
```
Mount the FAT32 boot partition => bootcode.bin => GPU firmware (start4.elf) => User code(EX: Linux kernel)
```
但本篇目標是經由uboot起動Linux Kernel,所以開機流程會改成:
```
Mount the FAT32 boot partition => bootcode.bin => GPU firmware (start4.elf) => UBoot (u-boot.bin) => Linux Kernel
```
---
### 分割記憶卡:
Raspberry pi系統將開機儲存裝置(記憶卡),分成兩個區塊。
1. 第一分區,FAT32開機磁區,存放Linux Kernel, Device tree等開機相關資訊。
2. 第二分區,EXT4分區,存放Rootfs。
因此需分割兩個磁區。第一分區,因為僅僅存放開機相關資訊,大小約100MB即可。如下範例:
```bash=
Device Boot Start End Sectors Size Id Type
/dev/sdb1 * 2048 198655 196608 96M c W95 FAT32 (LBA)
/dev/sdb2 198656 30375935 30177280 14.4G 83 Linux
```
---
### Raspberry pi firmware
Raspberry pi firmware放置開機相關的設定和檔案(bootcode.bin, start4.elf, device tree...),所以需要將其燒錄到記憶卡之中。
1. 下載FW:
```bash=
git clone https://github.com/raspberrypi/firmware
```
2. 拷貝FW開機資訊(firmware/boot)到第一分區(FAT32)
```bash=
sudo cp -rfv firmware/boot/* ${SDCard第一分區}
sync
```
---
### 建置U-boot
1. 從官方git將uboot下載下來:
```bash=
git clone --depth=1 https://github.com/u-boot/u-boot
```
:::info
因為uboot commit實在太多了。所以加上depth=1,只下載最新的commit。
:::
2. 編譯Uboot
```bash=
cd uboot
make CROSS_COMPILE=aarch64-linux-gnu- distclean
make CROSS_COMPILE=aarch64-linux-gnu- rpi_arm64_defconfig
make CROSS_COMPILE=aarch64-linux-gnu- -j6
```
3. 編譯完成後,可以在跟目錄看到編譯完成的檔案
```bash=
u-boot.bin
```
4. 複製u-boot映像檔到第一分區(FAT32)
```bash=
sudo cp -v u-boot.bin ${SDCard第一分區}
sync
```
5. 新增u-boot script檔案。
```bash=
touch boot64.cmd
```
內容如下:
```bash=
setenv bootargs 8250.nr_uarts=1 console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw
fatload mmc 0:1 ${kernel_addr_r} Image
booti ${kernel_addr_r} - ${fdt_addr}
```
編譯(將boot.cmd編譯成boot.scr)
```bash=
mkimage -C none -A arm64 -T script -d boot.cmd boot.scr
```
複製u-boot映像檔到第一分區(FAT32)
```bash=
sudo cp -v boot.scr ${SDCard第一分區}
```
:::info
* setenv bootargs 8250.nr_uarts=1 console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw <= 開啟UART, 設定Rootfs所在位置
* fatload mmc 0:1 ${kernel_addr_r} Image <= 從記憶卡載入Linux kernel映像檔
* booti ${kernel_addr_r} - ${fdt_addr} <= 開始載入Linux kernel
:::
:::info
${fdt_addr} <= Flatted Device Tree,u-boot已經幫你載好了。
[踩過的坑](https://www.raspberrypi.org/forums/viewtopic.php?f=71&t=319125)
:::
:::info
u-boot script可將要執行的命令放在script,減少自行輸入命令的麻煩。
:::
---
### 安裝Debian Rootfs
我直接使用Debian Rootfs當作我的Rootfs:
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放置到第二分區(EXT4):
```bash=
sudo tar xvf debian-11-minimal-arm64-2021-08-17/arm64-rootfs-debian-bullseye.tar -C ${SDCard第二分區}
```
---
### 建置Linux Kernel
1. 從官方git將Linux kernel下載下來:
```bash=
git clone --depth=1 https://github.com/raspberrypi/linux
```
2. 編譯Kernel
```bash=
make -j6 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- distclean
make -j6 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
make -j6 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs
```
3. 安裝Kernel, device tree 和 modules
* 安裝device tree
```bash=
sudo cp -vf arch/arm64/boot/dts/broadcom/*.dtb ${SDCard第一分區}
```
* 安裝Linux Kernel
```bash=
sudo cp -vf arch/arm64/boot/Image ${SDCard第一分區}
```
* 安裝Linux module
```bash=
sudo make -j6 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=${SDCard第二分區} modules_install
```
:::info
除了module是安裝在安裝在第二分區下,其餘都是安裝到第一分區(開機磁區)
:::
---
### 撰寫設定檔案
1. 在第一分區新增名為config.txt檔案,內容如下:
```text=
kernel=u-boot.bin
arm_64bit=1
enable_uart=1
```
---
### 最後
插上Raspberry pi開機,即可在console操作Raspberry pi
這裡透過screen操作console
```bash=
sudo screen /dev/ttyUSB0 115200
```
