要注意mips具有流水線可見性,所以跟在跳轉指令後的下一條指令,在執行跳轉到的地方前,都會執行,這個叫分支延遲。但是編譯器會隱藏該特性,但可以通過設定」.set noreorder」來禁止編譯器重新組織**順序。
每個板子都有自己的lds檔案。這個主要是用來說明編譯生成的指令,及執行過程中用到的資料放置的位置。這個可以參考ld的手冊。比如board/dbau1x00/u-boot.lds。
output_format(「elf32-tradbigmips」, 「elf32-tradbigmips」, 「elf32-tradbigmips」)
/* 這裡是生成格式為elf。大端,mips */
output_arch(mips)
/* 平台為mips */
entry(_start) /* 入口點為_start */
sections
. = align(4); /* 表示以4位元組對齊 */
.rodata :
. = align(4);
.data :
. = .;
_gp = align(16) + 0x7ff0;
.got :
.sdata :
.u_boot_cmd :
uboot_end_data = .;
num_got_entries = (__got_end – __got_start) >> 2;
. = align(4);
.sbss (noload) :
.bss (noload) :
uboot_end = .;
}
下面來分析cpu/mips/start.s
首先從_start開始。前面是128個字(不是位元組),是留給異常入口點的。
1. 最前面兩個分別是硬復位和軟復位,這兩個都跳到reset處。
2. 下面就是清一些cp0(協處理器0,mips對cpu的控制都是通過它實現的)的一些主要位。
3. 然後是關閉cache
4. 下面這個比較有意思。為什麼還非要跳一下呢?這樣就可以知道**的位置,而不是標號值。比如可能在ram中或rom中。
bal 1f
nop.word _gp
1:lw gp, 0(ra)
5. 這裡執行lowlevel_init 。這是第乙個需要我們自己定義的函式 。由於沒有初始化堆疊,這裡只能用彙編。我們看到在jalr後跟了個nop,這就是分支延遲槽了,在這裡什麼也沒有執行。
6. 下面執行了mips_cache_reset,它會來清理資料和指令的cache,並設定為正確的值。然後就可以開啟cache了。
7. 由於我們的記憶體可能還沒有始初化(有些人會有lowlevel_init中初始化,但有的人沒有這樣做)。但我們使用c函式的話,就需要堆疊,所以需要乙個記憶體空間。於是這裡執行了mips_cache_lock,將cache的位址鎖定,就是將cache當記憶體用了。然後我們將堆疊的位址設定在我們鎖定的cache的最高位址(因為堆疊是向下生長的)。這時我們就可以用c函式了,當然你還用不了malloc,也不可以太多的浪費堆疊。
8. 這裡就跑到c的初始化函式中去了--board_init_f。
對於mips,board_init_f在lib_mips/board.c下。在board_init_f()函式中,主要完成了一些功能初始化,和劃分ram。
/* 所以最後ram中是這樣子的
—ram 0×0000,0000—
…………
— ^ sp ^ —
— boot params — config_sys_bootparams_len bd->bi_boot_params
— global data — sizeof(gd_t) gd
— board info — sizeof(bd_t) gd->bd = bd
— mallco(+env) — config_sys_malloc_len + config_env_size
— uboot code — 16kb
—ram end —
*/
再看一下都初始化了什麼功能。初始化的函式都在init_fnc_t *init_sequence裡。
init_fnc_t *init_sequence = ;
最後這個函式呼叫了relocate_code (addr_sp, id, addr)。注意,這個函式,準確的說不是函式(因為不能返回),是不返回的。
現在我們又回到start.s中了。我們可以看到,這裡和c語言傳遞引數是用a0,a1,a2。relocate_code的工作就是將**搬移到ram中執行。這裡做的工作是:
1. 移動gp指標
2. 複製**到ram中
3. 重新整理一下cache
4. 跳到ram**當中去(in_ram)
in_ram的主要工作是:更新got;清空bss段;最後跳到board_init_r。我們可以看到board_init_r最後乙個引數是在分支延遲槽中賦值的。
這其實這裡主要說一下gots(global offset tables)這個東東,這是uboot能跳轉到不同空間執行的原理。uboot編譯時用到了pic(position-independent code)(也可以說成position-independent executable (pie))。這個其實是很早之前,在沒有mmu的年代引進來的東西。為了在沒有mmu時,不同程序也能同時執行,就需要他們的執行位址可以改變。 gots用來儲存所有的全域性變數位址,所以我們只要改變gots的值就可以了。gp就是指向gots位置的指標。這個功能需要在gcc編譯時指定 -fpic。然後就像我們看到的,我們只要改變gots裡的值,加上位址偏移就可以了。
下面再看一下board_init_r。這裡的工作:
1. 複製cmd段的資訊過來。這裡只複製了cmd,name,usage,usage。幫助資訊的字串還在flash中。
2. 然後是初始化malloc功能。注意這裡env有malloc的方式分配到了空間,並複製到ram。
3. 再就是stdio,串列埠,入口函式,以及全域性變最根據env的初始化了。
4. 再接著就是網路的初始化。eth_initialize(gd->bd)。對於mips,如果設了config_net_multi。我們需要自己寫board_eth_init和cpu_eth_init 兩個函式。 只有前者返回值小於0時,我們才需要執行後者。
5. 最後進入main_loop()。
乙太網驅動移植
對於mips,如果設了config_net_multi。會用到board_eth_init()和cpu_eth_init()兩個函式。只有前者返回值小於0時,才會執行後者。這裡要初始化結構體eth_device,然後使用eth_register()註冊網路裝置。主要要設定 init,halt,send,recv四個函式,及其它一些變數。如支援mii讀寫命令還需使用miiphy_register()註冊mii讀寫的兩個函式。
當註冊完成後,u-boot不會自動呼叫init,而是只有當用到網路裝置時,才後呼叫。每次使用網路介面時,是先呼叫halt之後,才呼叫init。
當我們使用命令make (型號)_config時,會產生以下效果。
(1) 將include/asm軟鏈結到include/asm-(arch)。
(2) 修改include/config.h檔案,增加#include 和#include 。
(3) 在include/config.mk設定變數arch,cpu,board,vendor。
編譯時,makefile會包含進幾個目錄的config.mk檔案。board/***/(***/)config.mk在前面已經介紹過了。在 lib_***/config.mk中會指定交叉編譯器,及增加平台相關的編譯引數。在cpu/***/config.mk會指定體系結構的相關引數,如大小端。
基於mips架構的uboot啟動流程(4)
特點和功能 u boot yamon 支援的 cpu 和 board 1.支援種類繁多,包括 arm 的各個系列 ppc,mips 2.mips 的支援較差,目前支援 au1100,au1500 系列,4kec 的需要自己移植 工作量主要在起始 的初始化部分,包括 cahe 和中斷的初始化 1.基本...
基於mips架構的uboot啟動流程(4)
特點和功能 u boot yamon 支援的 cpu 和 board 1.支援種類繁多,包括 arm 的各個系列 ppc,mips 2.mips 的支援較差,目前支援 au1100,au1500 系列,4kec 的需要自己移植 工作量主要在起始 的初始化部分,包括 cahe 和中斷的初始化 1.基本...
基於mips架構的uboot啟動流程(4)
特點和功能 u boot yamon 支援的 cpu 和 board 1.支援種類繁多,包括 arm 的各個系列 ppc,mips 2.mips 的支援較差,目前支援 au1100,au1500 系列,4kec 的需要自己移植 工作量主要在起始 的初始化部分,包括 cahe 和中斷的初始化 1.基本...