0、前言
在網上看到N多“大牛”們的移植文檔,什麼S3C2410,S3C6410,ARM7,ARM9的,一大堆一大堆,大致看了下,百分之九十以上的文章都是介紹了在哪兒修改代碼,至於爲什麼這樣修改卻是隻字未提。當然,這樣的文檔對於做產品是好樣的,因爲產品只追求結果,開發人員如何實現,爲什麼要這樣實現已經不重要了。所以,本系列計劃與大家分享移植如何實現,爲什麼要這樣移植作詳細介紹,由於個人並非天才,所以在寫作過程中需要閱讀大量的datasheet,甚至於反覆讀,所以更新不會太快。當然如有不當之處,敬請各位不吝指正。
1、入門第一天得學會站好位置
1.1、同學們都在哪兒呢?
剛入手bootloader的人可能都在想這樣一個問題,把編譯好的編程下載到目標平臺後,目標板怎麼知道從哪兒運行呢?剛開始時,我也在想這個的問題,有問題可不是什麼丟人的問題喲,關鍵不知道而且還假裝沒有問題可就有點業餘了喲!廢話少說,直接讓內存集合,看看U-Boot把內存分配並組織起來。
有過Linux編程的朋友都知道,單一文件可以直接通過GCC命令行方式進行,但如果有多個文件或者是上百個以至於上千文件的項目,用這種方式只能對他說一句“兄弟,我真的服咯U”。Makefile提供了對源文件進行管理的方式,通過查看U-Boot根目錄下的Makefile文件,可以看到U-Boot的編譯過程。對於內存的管理,我們只需要關注最後的鏈接方式,在Makefile中有如下部分:
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y)
smap=`$(call SYSTEM_MAP,u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
-c common/system_map.c -o $(obj)common/system_map.o
$(GEN_UBOOT) $(obj)common/system_map.o
endif
不知道有沒有看到一個u-boot.lds呢,當然如果直接在源碼中找的話,會找到一大堆的鏈接文件,這個是需要配置的,需要移植到什麼樣的平臺,可以查看源碼根目錄cpu/對應平臺/u-boot.lds即可。如果目標板是ARM的處理器,可以到cpu/arm926ejs、arm920t等目錄下查找與處理器對應的文件,如果是mips處理器,則對應到cpu/mips下查找。相信學嵌入式的都是強人,所以此處略去三千字的說明。偷懶的感覺就是好,嘿嘿........
1.2、到操場集合了
選擇一個文件打開,這裏以cpu\arm_cortexa8\u-boot.lds爲例,其它文件類似。打開文件後,代碼並不多,加文件說明、空行、括號都不足百行,是不是覺得有點竊喜呢?既然如此,親,我就直接把源碼貼出來了喲,一會再逐個“包郵”喲!
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm_cortexa8/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
1.3、對號入座
直接貼出上面的一段代碼,不知道親們看明白了多少呢?如果能很輕鬆過了,那隻能說恭喜你,已經入門了,下面的內容請直接跳過。上一節賣了一個關子,接下來針對不明白的親們進行逐行分析:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm"")
該行代碼指定輸出的可執行文件是elf格式(理解爲二進制的文件就行),32位ARM指令,其中處理器支持爲小端(目前ARM平臺均爲小端模式,大端與小端的差別請參考大小端字節序)OUTPUT_ARCH(arm)
指定輸出可執行文件的運行平臺爲ARM處理器平臺,當然如果爲其它平臺如mips,ppc等,這裏作相應的變更。ENTRY(_start)
指定輸出可執行文件的起始代碼段爲_start.SECTIONS
{
. = 0x00000000
指明目標代碼的起始地址從0x0位置開始,其中前面的"."代表的是當前位置,ARM平臺起始地址都是從0x00000000開始執行,這是由硬件屬性決定的,是平臺上後上能找到的第一個地址。 . = ALIGN(4)
代碼對齊方式,這裏是以4字節對齊,如果設置過大,則會找成資源浪費,如果設置過小,則降低系統性能,如取一個數據時需要多次訪問內存,後面重複該代碼將不再作解釋。 .text :
{
cpu/arm_cortexa8/start.o (.text)
*(.text)
}
其中.text是聲明爲代碼段,在存儲裏的代碼段,數據段等分別用不同的符號說明,而cpu/arm_cortexa8/start.o (.text)是指定的一個目錄,這裏是針對cortex-A8的平臺,在對應u-boot根目錄下,可以找到所指的文件,這裏說明引導文件的位置在cpu/arm_cortexa8/下(由於U-Boot對代碼組織結構進行調整,新U-Boot位置位於arch/arm/cup/arm_cortexa8),對應start.s編譯後的目標文件,start.s文件將在下一節作詳細分析。 .rodata : { *(.rodata) }
指定只讀數據段,RO段。 .data : { *(.data) }
指定讀/寫數據段,RW段。 .got : { *(.got) }
指定got段, got段式是uboot自定義的一個段, 非標準段。 __u_boot_cmd_start = .
右邊的“.”在前面有解釋,表示當前位置,這裏把__u_boot_cmd_start賦值爲當前位置,即起始位置。 .u_boot_cmd : { *(.u_boot_cmd) }
指定u_boot_cmd段,,uboot把所有的uboot命令放在該段,除了uboot默認的命令外,可以自己增加新的命令,後面再作詳細介紹。 __u_boot_cmd_end = .
意思同上,把__u_boot_cmd_end賦值爲當前位置,即結束位置。 __bss_start = .
把__bss_start賦值爲當前位置,即bss段的開始位置,BSS段的作用參見內存管理。 .bss : { *(.bss) }
指定bss段 _end = .
}
把_end賦值爲當前位置,即bss段的結束位置,最後一個括號表示定義的SECTIOS段的結束OK,本節提到的還留下幾個問題。
問題一:大小端字節序,將隨後補一文章單獨介紹。
問題二:start.s文件的解析,將在下一節作詳細分析。
問題三:BSS段,堆,棧段的介紹,將隨後補一文章內存管理。