嵌入式 Linux 移植中,uBoot 傳遞給核心的位址參數(載入位址、執行位址、虛擬位址等)最易搞混。本文整理 Makefile 定義、mkimage 參數、uBoot do_bootm 流程,建立完整位址映射心智模型。
| 變數 | 檔案 | 典型值 | 含義 |
|---|---|---|---|
| TEXTADDR | arch/arm/Makefile | 0xC0008000 | 核心運行虛擬位址(內核映像連結位址) |
| ZRELADDR | arch/arm/boot/Makefile | 0x20008000 | 核心載入實體位址(解壓後映像放置處) |
| ZTEXTADDR | arch/arm/boot/compressed/Makefile | 0x00000000 | 自解壓程式連結位址(0 表不使用自解壓) |
mkimage -A arm -O linux -C gzip \
-a 0x20008000 -e 0x20008000 \
-d linux.bin.gz uImage
-a (Load Address) = ZRELADDR = 0x20008000:核心解壓後存放實體位址。-e (Entry Point) = 0x20008000:核心啟動入口(通常等於 Load Address)。ih_magic、ih_hcrc、ih_dcrc。gunzip((void *)ntohl(hdr->ih_load), 0x400000, data, (int *)&len);
// ih_load = 0x20008000 (輸出位址)
// 0x400000 = 最大解壓 4MB
// data = 0x21000000 + sizeof(image_header_t) (輸入位址,uImage 存放處)
kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
// r3: board info (bd_t *)
// r4: initrd_start (0 if none)
// r5: initrd_end
// r6: cmdline start
// r7: cmdline end
(*kernel)(kbd, initrd_start, initrd_end, cmd_start, cmd_end);
ih_ep = 0x20008000,核心在實體位址 0x20008000 開始運行,MMU 開啟後映射至虛擬位址 0xC0008000。實體記憶體佈局 (例:256MB RAM @ 0x20000000) +---------------------------+ 0x30000000 | uBoot | +---------------------------+ 0x21000000 ← uImage 存放位置 | uImage (含頭部) | +---------------------------+ 0x20008000 ← ZRELADDR / Load Address / Entry Point | 解壓後核心映像 | (核心實際運行實體位址) | ... | +---------------------------+ 0x20400000 (約 4MB 限制) | 可用 RAM | +---------------------------+ 虛擬位址映射 (核心啟動 MMU 後) 0xC0008000 (TEXTADDR) ←→ 0x20008000 (ZRELADDR)
ZRELADDR 與 TEXTADDR 物理/虛擬對應錯誤 → 核心啟動即崩潰或無輸出。mkimage -a/-e 與 ZRELADDR 不一致 → uBoot 解壓到錯誤位址。ZTEXTADDR != 0 但未提供正確自解壓程式 → 解壓失敗掛起。arch/arm/mach-xxx/include/mach/memory.h 中 PHYS_OFFSET 是否為 0x20000000。Uncompressing Linux... done, booting the kernel. 確認解壓成功。掌握這組位址鏈(uImage 存放 → 解壓目標 → 核心連結虛擬位址 → MMU 映射),移植新板卡時才能精準配置 uBoot 環境變數與核心配置。