Skip to content

U-Boot

U-Boot (Das U-Boot) là bootloader de facto trên embedded Linux, hỗ trợ ARM, ARM64, RISC-V, MIPS, PowerPC, x86. Vị trí của U-Boot trong chuỗi boot (sau Boot ROM và SPL/FSBL, trước kernel) xem tại Bootloader trong hệ nhúng Linux. Phần riêng của Zynq (FSBL, BOOT.BIN, bootgen) xem tại U-Boot on Zynq.

Source tree và build

U-Boot dùng Kconfig và Kbuild giống Linux kernel, nên quy trình build quen thuộc:

git clone https://source.denx.de/u-boot/u-boot.git   # mainline
cd u-boot
export CROSS_COMPILE=arm-linux-gnueabihf-             # aarch64-linux-gnu- cho ARM64
make <board>_defconfig                                # ví dụ: xilinx_zynq_virt_defconfig
make menuconfig                                       # tùy chọn: chỉnh cấu hình
make -j$(nproc)

Vendor thường giữ một fork riêng, ví dụ u-boot-xlnx của Xilinx/AMD hay uboot-imx của NXP. Fork có hỗ trợ phần cứng sớm hơn, mainline thì có bản vá bảo mật và tính năng mới.

Các thư mục quan trọng:

Thư mục Nội dung
configs/ Các file *_defconfig cho từng board
board/<vendor>/<board>/ Code khởi tạo riêng cho board
arch/<arch>/ Code theo kiến trúc và SoC (arch/arm/mach-zynq/, arch/arm/mach-imx/)
arch/<arch>/dts/ Device tree U-Boot dùng cho chính nó
drivers/ Driver theo Driver Model (DM)
cmd/ Các lệnh của U-Boot shell
include/configs/<board>.h Cấu hình legacy dạng macro, đang được chuyển dần sang Kconfig
tools/ Công cụ chạy trên host: mkimage, fw_printenv...

File output thường gặp:

File Ý nghĩa
u-boot ELF, có symbol, dùng để debug bằng GDB/JTAG
u-boot.elf ELF dùng để đóng gói (bootgen trên Xilinx)
u-boot.bin Raw binary
u-boot.img u-boot.bin có header legacy để SPL nạp
spl/u-boot-spl.bin SPL raw binary
u-boot.itb FIT image chứa U-Boot (và ATF, DTB) cho SPL nạp, hay gặp trên ARM64

Driver Model và device tree trong U-Boot

U-Boot hiện đại dùng Driver Model (DM) và đọc device tree để biết phần cứng (CONFIG_OF_CONTROL), giống cách Linux probe driver. Lưu ý:

  • DTB của U-Boot và DTB truyền cho kernel có thể là hai file khác nhau. U-Boot có các node riêng như u-boot,dm-pre-reloc hoặc bootph-all (đánh dấu node cần dùng trong SPL hoặc trước relocation), thường đặt trong file *-u-boot.dtsi.
  • CONFIG_OF_SEPARATE: DTB được nối vào cuối u-boot.bin (u-boot-dtb.bin). CONFIG_OF_EMBED: DTB nhúng vào ELF, chỉ khuyến nghị cho debug.
  • U-Boot có thể sửa DTB của kernel trước khi boot (fixup): thêm node memory theo dung lượng DRAM thực, ghi bootargs vào /chosen, thêm MAC address cho Ethernet.

Relocation

U-Boot proper thường được nạp vào một địa chỉ trong DRAM (CONFIG_TEXT_BASE), sau khi khởi tạo xong DRAM nó tự copy (relocate) bản thân lên vùng cuối của DRAM để giải phóng vùng thấp cho kernel. Khi debug bằng GDB sau relocation, cần nạp lại symbol với offset mới (xem gd->relocaddr, lệnh bdinfo in ra relocaddr).

Environment

Environment là tập biến dạng key=value điều khiển hành vi của U-Boot. Environment mặc định được build vào binary, có thể lưu vào flash, eMMC, file FAT (uboot.env) để giữ thay đổi.

Biến Ý nghĩa
bootcmd Lệnh chạy tự động khi hết bootdelay
bootdelay Số giây chờ người dùng bấm phím để vào shell. -1 tắt autoboot, 0 boot ngay (vẫn có thể ngắt nếu cấu hình)
bootargs Command line truyền cho kernel
loadaddr, kernel_addr_r, fdt_addr_r, ramdisk_addr_r Địa chỉ DRAM để nạp kernel, DTB, initramfs
ipaddr, serverip, ethaddr Cấu hình mạng cho TFTP/NFS
fdtfile Tên file DTB cần nạp
=> printenv bootcmd
=> setenv bootargs console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait earlycon
=> saveenv

Từ Linux, đọc và sửa environment bằng fw_printenv / fw_setenv (gói libubootenv hoặc u-boot-fw-utils), cấu hình vị trí environment trong /etc/fw_env.config. Đây là cách các framework OTA (SWUpdate, RAUC, Mender) đổi slot boot.

Các lệnh hay dùng

Nhóm Lệnh Ví dụ
Thông tin version, bdinfo, help, coninfo bdinfo in kích thước DRAM, địa chỉ relocation
Bộ nhớ md, mw, cp, cmp, mtest md.l 0xF8000000 10 đọc 16 word từ thanh ghi
Storage mmc, sf, nand, usb, part mmc dev 0; mmc part, sf probe 0; sf read ${loadaddr} 0x100000 0x500000
Filesystem fatls, fatload, ext4load, load, ls load mmc 0:1 ${kernel_addr_r} zImage
Mạng dhcp, tftpboot, ping, nfs tftpboot ${kernel_addr_r} zImage
Device tree fdt addr, fdt print, fdt set Sửa DTB trước khi boot
Boot bootm, bootz, booti, bootefi, bootflow Xem bảng dưới
Script source, run, env import run bootcmd
FPGA (Zynq) fpga load, fpga loadb Nạp bitstream cho PL từ U-Boot

Lệnh boot kernel

Lệnh Kiểu image Dùng cho
bootz zImage (ARM 32-bit, tự giải nén) bootz ${kernel_addr_r} - ${fdt_addr_r}
booti Image (ARM64, RISC-V) booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}
bootm uImage (legacy header) hoặc FIT image bootm ${loadaddr} hoặc bootm ${loadaddr}#conf-1
bootefi Ứng dụng EFI (GRUB, kernel EFI stub) bootefi ${kernel_addr_r} ${fdt_addr_r}

Dấu - ở vị trí initramfs nghĩa là không dùng initramfs.

uImage là image có header 64 byte của U-Boot, tạo bằng mkimage (kernel có sẵn target make uImage với LOADADDR/UIMAGE_LOADADDR). Định dạng này đã cũ: chỉ chứa một image, checksum CRC32, không có chữ ký. Thiết kế mới nên dùng zImage/Image với bootz/booti, hoặc FIT image.

FIT image

FIT (Flattened Image Tree) dùng chính cú pháp device tree để mô tả một gói gồm nhiều image: kernel, nhiều DTB, initramfs, bitstream FPGA, kèm hash (SHA256) và chữ ký RSA/ECDSA. Đây là nền tảng của verified boot trong U-Boot.

/dts-v1/;
/ {
    description = "Kernel + DTB";
    #address-cells = <1>;

    images {
        kernel-1 {
            data = /incbin/("zImage");
            type = "kernel";
            arch = "arm";
            os = "linux";
            compression = "none";
            load = <0x8000>;
            entry = <0x8000>;
            hash-1 { algo = "sha256"; };
        };
        fdt-1 {
            data = /incbin/("system.dtb");
            type = "flat_dt";
            arch = "arm";
            compression = "none";
            hash-1 { algo = "sha256"; };
        };
    };

    configurations {
        default = "conf-1";
        conf-1 {
            kernel = "kernel-1";
            fdt = "fdt-1";
            signature-1 {
                algo = "sha256,rsa2048";
                key-name-hint = "dev";
                sign-images = "kernel", "fdt";
            };
        };
    };
};
# Đóng gói, ký bằng key trong thư mục keys/, ghi public key vào DTB của U-Boot
mkimage -f fit-image.its -k keys -K u-boot.dtb -r image.itb

Petalinux và Yocto (class kernel-fitimage) tạo FIT image tự động, ví dụ image.ub của Petalinux chính là một FIT image. Xem thêm List of Petalinux Images.

Boot script

Thay vì sửa bootcmd trong environment, có thể đặt logic boot trong một script trên phân vùng boot. Script viết dạng text rồi đóng gói bằng mkimage:

boot.cmd
setenv bootargs console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait
load mmc 0:1 ${kernel_addr_r} zImage
load mmc 0:1 ${fdt_addr_r} system.dtb
bootz ${kernel_addr_r} - ${fdt_addr_r}
mkimage -A arm -O linux -T script -C none -d boot.cmd boot.scr

Một số board (trong đó có các bản Xilinx cũ) còn đọc file uEnv.txt dạng key=value rồi env import vào environment.

Distro boot và Standard Boot

Để một bản phân phối (Debian, Fedora, Yocto) boot được trên nhiều board mà không phải sửa bootcmd cho từng board, U-Boot có cơ chế quét boot media theo thứ tự (boot_targets) và tìm một trong các file:

  • extlinux/extlinux.conf (cú pháp giống syslinux, chỉ định kernel, DTB, append)
  • boot.scr / boot.scr.uimg
  • Ứng dụng EFI mặc định /EFI/BOOT/bootaa64.efi, bootarm.efi...
/boot/extlinux/extlinux.conf
label linux
    kernel /zImage
    fdt /system.dtb
    append console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait

Cơ chế cũ dùng script trong include/config_distro_bootcmd.h (distro boot). Từ khoảng 2023, U-Boot chuyển sang Standard Boot (CONFIG_BOOTSTD) viết bằng C với các khái niệm bootdev, bootmeth, bootflow:

=> bootflow scan -l      # liệt kê các cách boot tìm được
=> bootflow select 0
=> bootflow boot

Cập nhật firmware và fallback

Khi cập nhật firmware ngoài hiện trường, bootloader là chỗ quyết định boot bản nào:

  • A/B (dual copy): hai bộ kernel + rootfs. Framework OTA ghi bản mới vào slot không dùng, đổi biến môi trường bằng fw_setenv, rồi reboot.
  • bootcount (CONFIG_BOOTCOUNT_LIMIT): U-Boot tăng bootcount mỗi lần boot. Linux reset nó về 0 khi boot thành công. Nếu vượt bootlimit, U-Boot chạy altbootcmd để quay về slot cũ.
  • Bản thân bootloader hiếm khi được cập nhật OTA vì nếu hỏng thì board không boot được nữa. Một số SoC hỗ trợ nhiều bản stage 1 (Zynq có multiboot/golden image, i.MX có secondary image) để giảm rủi ro.

References

  1. The U-Boot Documentation - docs.u-boot.org
  2. U-Boot Standard Boot - docs.u-boot.org
  3. Flattened Image Tree (FIT) Format - docs.u-boot.org
  4. Environment Variables - docs.u-boot.org
  5. Embedded Linux system development - bootlin.com, phần Bootloaders