Step 4 – building the kernel image and modules

從使用者的角度而言,這非常的簡單,只需要 make 就行了

make help 可以看到 help,這裡可以先觀察 target all

$ make help
[...]
Other generic targets:
  all - Build all targets marked with [*]
* vmlinux - Build the bare kernel
* modules - Build all modules
[...]
Architecture specific targets (x86):
* bzImage - Compressed kernel image (arch/x86/boot/bzImage)
[...]

從這裡可以得知,make all 的時候會有 vmlinux, modules, bzImage 這三個 target 被觸發

  • vmlinux: 未壓縮的 kernel image
  • modules: 在 config 中被標記為 M 的會變成 kernel modules (.ko files)
  • bzImage: 壓縮過後的 kernel image (x86)

實際在開機時,會用壓縮過後的 bzImagevmlinux 是作為 debug 用,(不在這本書的範圍)

這裡推薦的平行化程度 n

n = num-CPU-cores * factor

factor 推薦為 2,或是 1.5 (如果是高階有著 100 cpu cores 的系統) 01.png 02.png 我自己在 virtural box 的設定是 8 core,根據書上的說法,我要使用 -j16,大約花了 9 分鐘

(#2) 代表的是這是第二次 build,這個數字會在之後遞增

make all 之後,現在我們得到了 3 個東西:

  • 未壓縮的 vmlinux
  • Symbol-address mapping file, System.map
  • 壓縮過後的 bzImage

03.png vmlinux 的檔案非常大,vmlinuxSystem.map 這兩個檔案室用來 debug 用的

實際上用來開機的會是壓縮過後的 bzImage,會放在 arch/<arch>/boot/ 04.png bzImage 只有 8.2 MB 跟 582 MB 的 vmlinux 差很多

Step 5 – installing the kernel modules

在前一個步驟中,我們看到了 vmlinuxbzImage,現在還少了一個東西:modules,Step 5 會告訴我們 modules 該如何處理

Locating the kernel modules within the kernel source

kernel moduels 會以 *.ko 作為結尾 05.png 06.png

find . -name "*.ko" -ls | egrep -i "vbox|msdos|uio" | awk '{printf "%-40s %9d\n", $11, $7}'

07.png 這裡對應到的是在 chapter 2 做 config 的時候,

  1. VirtualBox support, n -> m
  2. Userspace I/O (UIO) drivers, n -> m
  3. MS-DOS filesystem support, n -> m

現在我們知道需要的 .ko 已經存在了,但我們需要放在 filesystem 中的 /lib/modules/$(uname -r)/ 這是下一小節的任務

Getting the kernel modules installed

只需要用這個命令就可以做到 kernel module installation

sudo make modules_install

08.png 09.png

/lib/modules/ 中多了 5.4.1-llkd01,當之後用 5.4.1-llkd01 開機時,就會 load /lib/modules/5.4.1-llkd01/ 中的 modules

檢查剛剛我們看到的那些 .ko 有沒有放到 /lib/modules/5.4.1-llkd01/kernel/

find /lib/modules/5.4.1-llkd01/kernel/ -name "*.ko" | egrep "vboxguest|msdos|uio"

10.png

最後,書中提及,我們其實可以自己指定 moduel 的 path 例如

export STG_MYKMODS=../staging/rootfs/my_kernel_modules
make INSTALL_MOD_PATH=${STG_MYKMODS} modules_install

並且在這個例子中,如果 STG_MYKMODS 在 user 可以 access 的權限,則可以不使用 sudo

Step 6 – generating the initramfs image and bootloader setup

  • initramfs 代表 initial RAM filesystem
  • 要注意的是這裡在討論的是 x86 底下的架構,分為兩個部份
  1. 產生 initramfs image
  2. (GRUB) bootloader setup 之所以會變成一個步驟 Step 6 是因為有很方便的工具可以讓兩個步驟變成一個步驟
sudo make install

這個命令就包含了

  1. 產生 initramfs image
  2. (GRUB) bootloader setup

11.png

Generating the initramfs image – under the hood

在最一開始的 sudo make install 的時候,實際上會執行 ./arch/x86/boot/install.sh 這個腳本

$ sudo make install
sh ./arch/x86/boot/install.sh 5.4.0-llkd01 arch/x86/boot/bzImage \
System.map "/boot"

並且在腳本 ./arch/x86/boot/install.sh 執行完之後,會在 /boot 中產生這些檔案 12.png

  • initramfs(initrd.img-5.4.0-llkd01) image 是藉由腳本 update-initramfs 產生
  • 如果所有的 file 複製到了 boot 中,原本的檔案會變成 <filename>-$(uname -r).old
  • 壓縮過後的 vmlinuz-<kernel-ver>arch/x86/boot/bzImage 的 copy
  • vmlinuz-<kernel-ver>arch/x86/boot/bzImage 的 copy
  • 回想一下 vmlinux 是未壓縮過後的,vmlinuz 則是壓縮過後的
  • GRUB 的 config 檔也會更新在 /boot/grub/grub.cfg
  • 這些流程是非常的 architecture-specific

Understanding the initramfs framework

Why the initramfs framework?

  • 可以跑一些 user space application 在 kernel boot 之前

Understanding the basics of the boot process on the x86

  1. BIOS
  2. bootloader (grub)
  3. vmlinuz + initramfs
    • low-level hardware initialization
    • Load these images to RAM and uncompressing
    • jump to the kernel entry point

More on the initramfs framework

Step 7 – customizing the GRUB bootloader

Customizing GRUB – the basics

  1. keep a backup
sudo cp /etc/default/grub /etc/default/grub.orig
  1. 修改 config
sudo vim /etc/default/grub
  1. 設定一定要顯示 grub menu
GRUB_HIDDEN_TIMEOUT_QUIET=false
sudo update-grub

Selecting the default kernel to boot into

Booting our VM via the GNU GRUB bootloader

grub1.png grub2.png

Kernel build for the Raspberry Pi

Step 1 – cloning the kernel source tree

我這裡是使用自己的 ubuntu 筆電,而不是像書上使用 virtual box 上的 gest VM

export RPI_STG=~/rpi_work
mkdir -p ${RPI_STG}/kernel_rpi ${RPI_STG}/rpi_tools
cd ${RPI_STG}/kernel_rpi
git clone --depth=1 --branch rpi-5.4.y https://github.com/raspberrypi/linux.git

rpi01.png

現在在路徑 ~/rpi_work/kernel_rpi/linux 中,我們拿到了 5.4.83 Raspberry Pi kernel 跟書上的有一點不一樣 (5.4.51 Raspberry Pi kernel) 但沒這裡差一點點沒有關係。

Step 2 – installing a cross-toolchain

First method – package install via apt

這裡在安裝一些編譯時所需要的工具,書上的範例是使用 32-bit 的樹莓派,我手上的樹莓派是 64-bit,所以要用:

# Install general build tools
sudo apt update
sudo apt install build-essential git bc bison flex libssl-dev make libc6-dev libncurses5-dev
# Install the 64-bit ARM cross-compiler toolchain
sudo apt install crossbuild-essential-arm64

Step 3 – configuring and building the kernel

# Set target architecture to ARM 64-bit
export ARCH=arm64
# Set cross-compiler prefix
export CROSS_COMPILE=aarch64-linux-gnu-
# Set the kernel image filename for the Pi 4 (64-bit)
export KERNEL=kernel8
# Load the default configuration for BCM2711 (Pi 4) 64-bit
make bcm2711_defconfig

rpi02.png

make menuconfig
# Compile the kernel image (Image), modules, and device tree blobs (dtbs)
make -j$(nproc) Image modules dtbs

rpi03.png 最後的 kernel image 會存放於 arch/arm64/boot/Image
到了這裡,我們已經完成了編譯的步驟

放到 SD Card 中

在 build 之後要把以下三個東西

  1. kernel image
  2. device tree files
  3. modules

放入 SD Card 中

為了把東西放入 SD card 中,要先把 SD card 的 boot partition 與 root partition mount 到 ~/mnt/pi-boot~/mnt/pi-root: rpi04.png

# Example mounting setup (create directories first if they don't exist)
sudo mkdir -p ~/mnt/pi-boot ~/mnt/pi-root
# Replace /dev/sdX1 and /dev/sdX2 with your SD card device names
sudo mount /dev/mmcblk0p1 ~/mnt/pi-boot   # Boot partition (FAT32)
sudo mount /dev/mmcblk0p2 ~/mnt/pi-root   # Root partition (Ext4)

Copy Kernel Image

在 SD card 中 boot 中的 kernel8.img 是原本從官方下載的 kernel (6.12.47),現在我們把剛剛從 kernel source 5.4.83 build 出來的 arch/arm64/boot/Image 複製到 boot 分區的 kernel54.img pi-boot.png

  • Copy the kernel binary
sudo cp ~/rpi_work/kernel_rpi/linux/arch/arm64/boot/Image ~/mnt/pi-boot/kernel54.img
ls ~/mnt/pi-boot/kernel54.img

Install Kernel Modules

# The path INSTALL_MOD_PATH must point to the root partition of the Pi
sudo env PATH=$PATH make modules_install INSTALL_MOD_PATH=~/mnt/pi-root
ls ~/mnt/pi-root/lib/modules

現在多了一個 5.4.83-v8+ 的 module rpi-modules2.png

Update config.txt

sudo vim ~/mnt/pi-boot/config.txt
[all]
# Current kernel is still kernel8.img (6.12)

# --- Start of 5.4 Kernel Block ---
[include boot54.rc]
# This setting is specific to the 5.4 kernel
kernel=kernel54.img
# Optional: If you customized the 5.4 DTB, you can point to it here:
# device_tree=bcm2711-rpi-4-b-54.dtb 
[all]
# --- End of 5.4 Kernel Block ---

boot/config.txt 中所新增的 [include boot54.rc] 的區域,代表在 boot/boot54.rc 存在,這個 section 的設定才成立

  • 所以現在新增一個空的 boot/boot54.rc 來讓這個區塊的設定成立
# create the trigger file
sudo touch ~/mnt/pi-boot/boot54.rc

Unmount and Boot

sudo umount ~/mnt/pi-boot
sudo umount ~/mnt/pi-root