上篇我們說到uimage的生成流程,核心真正的入口位址是在arch/arm/boot/compressed中,所以我們首先看下該目錄下面的vmlinux.lds。
…入口位址就是(.start)段,定義在head.s中。在這裡要注意一下乙個段是(.piggydata),它的定義在piggy.gzip.s中。.text :
…
piggy.gzip.s檔案內容:
.section
.piggydata,#alloc
.globl input_data
input_data:
.incbin
"arch/arm/boot/compressed/piggy.gzip"
.globl input_data_end
input_data_end:
被壓縮後的核心以乙個段資料的形式鏈結進來,所以自解壓程式首先找到這個段資料的位址,然後解壓縮到指定的位址。
入口在head.s中的.start段,如果把所有**都貼出來會讓文章顯得很長,所有我會盡量少地貼**,各位讀者請參考head.s來看。
具體的執行流程為:
1. 將r1、r2暫存器儲存到r7、r8暫存器中,根據aapcs對函式呼叫中暫存器的責任規定,r0、r1、r2暫存器分別儲存的是前三個引數,所以實際上此時r1和r2的值就是uboot中thekernel的第二個(機器id)和第三個引數(核心引數位址)。
2. 進入svc模式,關閉中斷。
3. ldr r4, =zreladdr;將zreladdr賦值給r4暫存器。zreladdr的定義在同目錄下的makefile中:
ifneq ($(config_auto_zreladdr),y)即巨集zerladdr的值,而zerladdr又在arch/arm/boot/makefile中定義為ldflags_vmlinux += –defsym zreladdr=$(zreladdr)
endif
zreladdr := $(zreladdr-y)$(zreladdr-y)的定義在arch/arm/mach-***/makefile.boot中,每個板子的定義各不相同,實際上zreladdr就是核心解壓縮到的位址。解壓之後的核心被放在以zreladdr開始的記憶體上。也是解壓之後核心入口位址。
4. bl cache_on;開啟i/d快取,為了加速cpu執行速度,略過。
5. 載入lc0表,lc0的定義為:
lc0: .word lc0 @ r1裡面的定義可以結合vmlinux.lds一起看。.word __bss_start @ r2
.word _end @ r3
.word _edata @ r6
.word input_data_end - 4 @ r10 (inflated size location)
.word _got_start @ r11
.word _got_end @ ip
.word .l_user_stack_end @ sp
.size lc0, . - lc0
restart:
adr r0, lc0;相當於add r0, pc, lc0
ldmia r0, ;載入表
ldr sp, [r0, #28];載入sp指標,即.l_user_stack_end,定義在同一檔案,一段4k的堆疊
/* * we might be running at a different address. we need
* to fix up various pointers.
* 由於linux載入位址可能跟鏈結位址不一致,所以重新計算_edata和input_data_end - 4的值
*/subr0, r0, r1 @ calculate the delta offset
addr6, r6, r0 @ _edata
addr10, r10, r0 @ inflated kernel size location
/** decompressed kernel at the end of the compressed data
* in little-endian form.
* 計算出被壓縮核心的長度,儲存到r9
*/ldrb r9, [r10, #0]
ldrb lr, [r10, #1]
orr r9, r9, lr, lsl
#8 ldrb lr, [r10, #2]
ldrb r10, [r10, #3]
orr r9, r9, lr, lsl
#16 orr r9, r9, r10, lsl
#24/* malloc space is above the relocated stack (64k max) */
add sp, sp, r0
;重新計算堆疊的位址
addr10, sp, #0x10000;再申請64k
在這裡先稍微說一下鏈結位址和執行位址。鏈結位址就是鏈結程式給執行**,變數符號等指定的位址。例如長跳轉呼叫乙個子函式,編譯器會給這個子函式指定乙個固定的鏈結位址******xx,跳轉的時候就是b ******xx;。鏈結之後的程式中,這些位址都已固定。
而執行位址就是實際上程式被載入到執行的位址。我們寫的應用程式被載入的時候執行位址和鏈結位址是一致的,所以長跳轉的時候不會有問題。如果鏈結位址和執行位址不一致,就會出現問題,比如長跳轉到乙個子函式位址為******xx,但是由於該子函式的**不是被載入到******xx而是被載入到yyyyyyyy的位址,此時就會出錯,同理變數也是。
在上面那段彙編**中有個重新計算位址的就是出於這個考慮,如果鏈結位址和執行位址不一致,那麼_edata和input_data_end所指向的位址就有問題。
計算的原理是:adr r0, lc0後r0裡面儲存的就是lc0的真實位址,然後載入lc0表,注意第乙個資料就是定義lc0的鏈結位址,然後載入到r1暫存器中。sub r0, r0, r1就計算出執行位址和鏈結位址的偏移量,儲存到暫存器r0中。接著就重新計算_edata和input_data_end的值。
暫無
IMX6Solo啟動流程 從上電到Uboot
ivt主要包含 詳見 8.6.1.1 image vector table structure 名稱含義 header ivt頭部,標識ivt和ivt長度 entry 第一條指令的入口位址,即uboot的入口 dcddcd資料的位址,緊跟在boot data後面 boot data boot dat...
IMX6ULL啟動流程
假設板子設定為sd tf卡啟動,boot rom程式會做什麼?把程式從sd tf卡讀出來,執行。從 讀?從sd tf卡讀,這需要先初始化sd tf卡 根據efuse或gpio的設定初始化sd tf卡。讀到 去?讀到記憶體即ddr去,這需要先初始化ddr。除了初始化啟動裝置 初始化ddr,還需要初始化...
IMX6ULL uboot啟動分析(六)
已經對board init f函式進行了簡單介紹,在這個函式當中,會呼叫一系列的函式去初始化一些早期的板子外設和gd結構體的成員變數,但是board init f函式並沒有將所有的外設進行初始化,還有一些後續的工作需要完成,這些工作就是由board init r函式去完成。在介紹board init...