Cấu trúc phân vùng và chuẩn bị môi trường đóng gói
Bước đầu tiên là xác định cấu trúc phân vùng trên thẻ SD Card để đảm bảo U-Boot và Kernel có thể đọc được đúng địa chỉ vật lý.
Chúng ta sẽ chia thẻ SD thành 2 phân vùng: Phân vùng 1 (Boot partition) dùng hệ tập tin FAT32 để chứa bootloader, kernel image và device tree; Phân vùng 2 (Root partition) dùng hệ tập tin ext4 chứa toàn bộ rootfs đã build ở phần trước.
Cấu trúc này giúp HackberryPi CM5 nhận diện đúng boot device và tải hệ điều hành một cách chuẩn xác theo kiến trúc ARM.
Xác định thiết bị thẻ SD Card
Cắm thẻ SD Card vào máy chủ build (thường qua USB card reader) và xác định tên thiết bị.
Chạy lệnh lsblk để liệt kê các thiết bị lưu trữ. Tìm thẻ SD dựa trên dung lượng (ví dụ 16GB, 32GB).
Giả sử thiết bị thẻ SD là /dev/sdb. Tuyệt đối không nhầm lẫn với ổ cứng hệ thống (thường là /dev/sda).
Xóa cấu trúc phân vùng cũ
Sử dụng lệnh fdisk để xóa toàn bộ bảng phân vùng cũ trên thẻ SD nhằm tránh xung đột.
Thực thi lệnh fdisk /dev/sdb. Trong giao diện fdisk, gõ lệnh d cho đến khi không còn phân vùng nào, sau đó gõ w để lưu và thoát.
Kết quả mong đợi: Thẻ SD trở về trạng thái raw, không có bất kỳ phân vùng nào.
Tạo phân vùng Boot (FAT32)
Tạo phân vùng đầu tiên dành cho boot với kích thước khoảng 512MB. Phân vùng này phải là Primary partition và được đánh dấu là Bootable.
Chạy fdisk /dev/sdb và thực hiện các lệnh sau trong giao diện:
n
p
1
+512M
t
c
w
Các lệnh trên lần lượt là: tạo mới (n), primary (p), số 1 (1), mặc định start (Enter), kích thước +512M (kết thúc), đổi type (t), đánh dấu bootable (c), lưu (w).
Kết quả: Phân vùng /dev/sdb1 được tạo với kiểu "W95 FAT32 (LBA)" và cờ bootable.
Tạo phân vùng Root (EXT4)
Dùng toàn bộ dung lượng còn lại cho phân vùng rootfs.
Chạy lại fdisk /dev/sdb và thực hiện:
n
p
2
w
Ở đây ta không nhập kích thước cuối để nó tự động lấy hết dung lượng còn lại.
Kết quả: Phân vùng /dev/sdb2 được tạo chiếm toàn bộ không gian còn lại.
Định dạng hệ thống tập tin
Định dạng phân vùng boot bằng FAT32 và phân vùng root bằng EXT4 để tương thích với U-Boot và Linux kernel.
Thực thi lệnh định dạng:
mkfs.vfat -F 32 /dev/sdb1
mkfs.ext4 -L rootfs /dev/sdb2
Kết quả mong đợi: Hệ thống thông báo "mkfs.vfat 4.x.x" và "mkfs.ext4 1.x.x" hoàn tất, không có lỗi.
Ghi Kernel, Device Tree và U-Boot vào Boot Partition
Sau khi đã có phân vùng, ta cần mount phân vùng boot để ghi các file hệ thống cần thiết để khởi động.
Mount phân vùng Boot
Tạo thư mục mount point và gắn phân vùng boot vào.
Chạy lệnh:
mkdir -p /tmp/boot_mount
mount /dev/sdb1 /tmp/boot_mount
Kết quả: Thư mục /tmp/boot_mount hiển thị nội dung rỗng hoặc chỉ có file hidden của FAT32.
Copy Kernel Image (zImage)
Chuyển file kernel đã biên dịch ở Phần 3 vào phân vùng boot. U-Boot cần file này để tải nhân Linux vào RAM.
Giả sử đường dẫn kernel build là /home/build/linux-arch-arm/arch/arm/boot/zImage.
Thực thi lệnh:
cp /home/build/linux-arch-arm/arch/arm/boot/zImage /tmp/boot_mount/
Kết quả mong đợi: File zImage xuất hiện trong /tmp/boot_mount.
Copy Device Tree Blob (DTB)
Copy file DTB tương ứng với HackberryPi CM5 vào cùng thư mục boot. File này chứa cấu hình phần cứng cụ thể cho board.
Giả sử file DTB nằm ở /home/build/linux-arch-arm/arch/arm/boot/dts/hackberry-pi-cm5.dtb.
Thực thi lệnh:
cp /home/build/linux-arch-arm/arch/arm/boot/dts/hackberry-pi-cm5.dtb /tmp/boot_mount/
Kết quả mong đợi: File hackberry-pi-cm5.dtb xuất hiện trong thư mục boot.
Copy U-Boot SPL và U-Boot Image
Ghi trực tiếp U-Boot SPL và U-Boot Image vào các địa chỉ offset cụ thể trên thẻ SD nếu U-Boot được cấu hình để boot trực tiếp từ SD card (trước khi vào Linux).
Tuy nhiên, với cấu hình U-Boot thông thường đã flash vào flash memory (SPI/SDIO) hoặc boot từ đầu thẻ, ta chỉ cần copy file u-boot.img vào boot partition để U-Boot tải kernel.
Thực thi lệnh copy file U-Boot chính:
cp /home/build/u-boot/u-boot.img /tmp/boot_mount/
Lưu ý: Nếu U-Boot yêu cầu boot từ offset 0 của thẻ SD (thường cho các board không có ROM boot loader riêng), ta dùng dd để ghi SPL vào offset 16KB (4096 * 4).
Trường hợp đặc biệt cần ghi SPL vào đầu thẻ:
dd if=/home/build/u-boot/u-boot-spl.bin of=/dev/sdb bs=1024 seek=4
Kết quả mong đợi: Lệnh dd chạy xong không báo lỗi, dữ liệu đã được ghi vào vùng header của thẻ SD.
Viết file cấu hình U-Boot (uEnv.txt)
Tạo file uEnv.txt trong phân vùng boot để định nghĩa các biến môi trường cho U-Boot (tên kernel, tên dtb, bootargs).
Tạo file /tmp/boot_mount/uEnv.txt với nội dung:
kernel=zImage
fdt=hackberry-pi-cm5.dtb
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw
Kết quả mong đợi: File uEnv.txt được tạo thành công trong phân vùng boot.
Ghi Rootfs vào Root Partition
Phân vùng root đã được định dạng ext4, bây giờ ta cần đưa toàn bộ cây thư mục rootfs đã build ở Phần 4 vào đây.
Mount phân vùng Root
Tạo thư mục mount point cho root partition và gắn nó vào hệ thống.
Thực thi lệnh:
mkdir -p /tmp/root_mount
mount /dev/sdb2 /tmp/root_mount
Kết quả: Thư mục /tmp/root_mount trở thành cửa sổ truy cập vào phân vùng root của thẻ SD.
Copy nội dung Rootfs
Sử dụng lệnh cp để sao chép toàn bộ nội dung từ thư mục rootfs build vào phân vùng đã mount.
Giả sử rootfs nằm ở /home/build/rootfs.
Thực thi lệnh:
cp -a /home/build/rootfs/. /tmp/root_mount/
Tham số -a (archive) đảm bảo giữ nguyên quyền truy cập, liên kết (symlink), và thuộc tính đặc biệt (device nodes).
Kết quả mong đợi: Quá trình copy hoàn tất, không có lỗi permission hay missing directory.
Chỉnh sửa fstab (Tùy chọn nhưng cần thiết)
Cập nhật file /etc/fstab trong rootfs để hệ thống nhận diện đúng UUID của phân vùng root khi boot lần sau.
Lấy UUID của phân vùng root:
blkid /dev/sdb2
Kết quả trả về dạng: UUID="1234-5678-ABCD-1234".
Chỉnh sửa file /tmp/root_mount/etc/fstab:
#
UUID=1234-5678-ABCD-1234 / ext4 defaults 0 1
Kết quả mong đợi: File fstab được cập nhật với UUID chính xác của phân vùng root.
Unmount và Kiểm tra cấu trúc hệ thống
Trước khi rút thẻ SD để flash vào HackberryPi CM5, cần đảm bảo tất cả dữ liệu đã được ghi vào thẻ và không có file hệ thống nào đang bị khóa.
Unmount các phân vùng
Gỡ mount các phân vùng để đảm bảo tính toàn vẹn của dữ liệu.
Thực thi lệnh:
umount /tmp/boot_mount
umount /tmp/root_mount
Kết quả mong đợi: Không có cảnh báo "target is busy".
Kiểm tra cấu trúc phân vùng
Quay lại lệnh lsblk -f để xác nhận lại cấu trúc phân vùng và hệ thống tập tin.
Kiểm tra các mục:
- Phân vùng 1 (sdb1) có FS_TYPE là vfat.
- Phân vùng 2 (sdb2) có FS_TYPE là ext4.
- Dung lượng phân vùng đúng với yêu cầu (512M và phần còn lại).
Kiểm tra nội dung Boot Partition
Mount lại phân vùng boot để kiểm tra nhanh các file đã copy.
mount /dev/sdb1 /tmp/boot_mount
ls -lh /tmp/boot_mount/
umount /tmp/boot_mount
Kết quả mong đợi: Danh sách file bao gồm zImage, hackberry-pi-cm5.dtb, u-boot.img, và uEnv.txt.
Kiểm tra nội dung Root Partition
Mount lại phân vùng root để kiểm tra cấu trúc thư mục cơ bản.
mount /dev/sdb2 /tmp/root_mount
ls -d /tmp/root_mount/{bin,etc,proc,sys,dev,mnt,root,tmp,usr,var}
umount /tmp/root_mount
Kết quả mong đợi: Các thư mục cơ bản của Linux (bin, etc, proc, sys, dev, mnt, root, tmp, usr, var) đều tồn tại và không bị lỗi quyền truy cập.
Verify Device Nodes
Kiểm tra các thiết bị đặc biệt trong thư mục /dev để đảm bảo chúng đã được copy đúng (cần thiết cho boot).
mount /dev/sdb2 /tmp/root_mount
ls -l /tmp/root_mount/dev/console /tmp/root_mount/dev/null /tmp/root_mount/dev/ttyS0
umount /tmp/root_mount
Kết quả mong đợi: Các file device node hiển thị đúng loại (c) và số major/minor tương ứng.
Điều hướng series:
Mục lục: Series: Xây dựng hệ điều hành Linux từ source cho HackberryPi CM5
« Phần 5: Tạo bootloader và cấu hình khởi động U-Boot
Phần 7: Flash hệ điều hành vào HackberryPi và kiểm tra khởi động »