Linux系統移植⑨:uboot啟動流程詳解-bootz啟動Linux過程
bootz
是 U-Boot 中用于啟動 Linux 內核的命令,專為處理 zImage(壓縮內核映像) 設計。
啟動 Linux 的完整過程:
1. 加載內核與相關文件
U-Boot 先將以下文件加載到內存指定地址:
- zImage:壓縮的 Linux 內核映像(位于內存地址
KERNEL_ADDR
)。 - 設備樹二進制文件(DTB):硬件描述文件(位于
FDT_ADDR
)。 - initramfs(可選):初始內存文件系統(位于
INITRD_ADDR
)。
2. 執行 bootz
命令
bootz [內核地址] [initramfs地址]:[大小] [設備樹地址]
-
啟動命令:
bootz ${KERNEL_ADDR} ${INITRD_ADDR}:${INITRD_SIZE} ${FDT_ADDR}
若無需 initramfs:
bootz ${KERNEL_ADDR} - ${FDT_ADDR}
3. zImage 自解壓
- U-Boot 跳轉到
KERNEL_ADDR
,執行 zImage 頭部的小型解壓程序。 - 解壓程序將內核解壓到預設的 物理內存地址(如 ARM 架構通常為
0x8000
)。
4. 傳遞啟動參數
U-Boot 通過寄存器/設備樹傳遞關鍵參數:
架構 | 寄存器 | 參數說明 |
---|---|---|
ARM | r0 | 0(保留) |
r1 | 機器 ID(舊)或 0xFFFFFFFF (新) | |
r2 | 設備樹地址(FDT_ADDR ) | |
RISC-V | a0 | 設備樹地址 |
a1 | 0(保留) |
設備樹(DTB) 包含 CPU、內存、外設等硬件信息,內核據此初始化硬件。
5. 內核初始化
Linux 內核接管后執行:
- 硬件初始化:基于 DTB 設置 CPU、內存控制器、時鐘等。
- 驅動加載:識別并初始化存儲、網絡等設備驅動。
- 掛載根文件系統:
- 若有 initramfs,將其作為臨時根文件系統。
- 否則直接掛載
root=
參數指定的文件系統(如/dev/mmcblk0p2
)。
6. 用戶空間啟動
- 內核啟動第一個用戶進程
/init
(位于 initramfs 或根文件系統)。 init
進程加載系統服務(如 systemd 或 SysVinit)。- 最終進入登錄界面或指定應用。
關鍵依賴
- zImage 格式:必須為
gzip
壓縮的Image
文件(非 uImage)。 - 設備樹支持:現代內核強制要求 DTB(無 DTB 的內核無法啟動)。
- 地址對齊:內核/DTB 的加載地址需符合 CPU 架構要求(如 ARM 需 4KB 對齊)。
do_bootz源碼如下:
其內部主要函數調用關系如下:
do_bootz
-> bootz_start
-> do_bootm_states 階段為BOOTM_STATE_START
-> bootm_start 對images全局變量清零,
-> images->ep = 0X80800000
->bootz_setup 判斷zImage是否正確
-> bootm_find_images
-> boot_get_fdt 找到設備樹,然后將設備樹起始地址和長度,寫入到images的ft_addr和ft_len成員變量中。
-> bootm_disable_interrupts 關閉中斷相關
-> images.os.os = IH_OS_LINUX; 表示要啟動Linux系統
-> do_bootm_states 狀態BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 、BOOTM_STATE_OS_GO,
-> bootm_os_get_boot_func 查找Linux內核啟動函數。找到Linux內核啟動函數do_bootm_linux,賦值給boot_fn。
-> boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); 就是do_bootm_linux。
-> boot_prep_linux 啟動之前的一些工作,對于使用設備樹來說,他會將Bootargs傳遞給Linux內核,通過設備樹完成。也就是向Linux內核傳參。
-> boot_selected_os BOOTM_STATE_OS_GO, do_bootm_linux
-> do_bootm_linux,BOOTM_STATE_OS_GO
-> boot_jump_linux
-> machid= gd->bd->bi_arch_number;
-> void (kernel_entry)(int zero, int arch, uint params);
-> kernel_entry = (void ()(int, int, uint))images->ep; 0X80800000。
-> announce_and_cleanup 輸出Starting kernel……
-> kernel_entry(0, machid, r2); 啟動Linux內核。 Uboot的最終使命,啟動Linux內核。