天天看點

Uboot16之start.S七種異常模式及向量表

#ifndef CONFIG_ENABLE_MMU    //首先我們肯定開啟了MMU的,是以下面不看了

#ifndef CFG_PHY_UBOOT_BASE

#define CFG_PHY_UBOOT_BASE    CFG_UBOOT_BASE

#endif

#endif

上面我們不管

#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)

這裡的EVT1或者後面的EVT1是晶片的子版本号,EVT0是指第一版,EVT1是第二版。

Uboot16之start.S七種異常模式及向量表

X210.h頭檔案中定義了CONFIG_EVT1

    .word 0x2000

.word相當于int類型,是GNU彙編中的元素,word=32bit=4位元組。

    .word 0x0

    .word 0x0

    .word 0x0

#endif

裸機中講過,在SD卡啟動/Nand啟動等整個鏡像開頭需要16位元組的校驗頭。(mkv210image.c中就是為了計算這個校驗頭)。我們以前做裸機程式時根本沒考慮這16位元組校驗頭,因為:1、如果我們是usb啟動直接下載下傳的方式啟動的則不需要16位元組校驗頭(irom application note);2、如果是SD卡啟動mkv210image.c中會給原鏡像前加16位元組的校驗頭。

Uboot16之start.S七種異常模式及向量表

uboot這裡start.S中在開頭位置放了16位元組的填充占位,這個占位的16位元組隻是保證正式的image的頭部确實有16位元組,但是這16位元組的内容是不對的,還是需要後面去計算校驗和然後重新填充的。

.globl _start

_start是GNU彙編器的預設入口标簽,.globl将_start聲明為外部程式可通路的标簽,.globl是GNU彙編的保留關鍵字,前面加點是GNU彙編的文法。

.global就是相當于C語言中的Extern,聲明此變量,并且告訴連結器此變量是全局的,外部可以通路 。

是以,你可以看到在連接配接腳本中:

Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表

,有用到此變量:

ENTRY(_start)

即指定入口為_start,而由下面的_start的含義可以得知,_start就是整個start.S的最開始,即整個uboot的代碼的開始。

_start: b    reset

_start後面加上一個冒号' :' ,表示其是一個标号Label,類似于C語言goto後面的标号。

而同時,_start的值,也就是這個代碼的位置了,此處即為代碼的最開始,相對的0的位置。而此處最開始的相對的0位置,在程式開始運作的時候,如果是從NorFlash啟動,那麼其位址是0,_start=0。但是片内RAM的記憶體起始位址是BL0占用了,是以連結位址才是我們的代碼的起始位址。從我們的makefile中看到我們的連結位址為0xc3e00000.

Uboot16之start.S七種異常模式及向量表

但我們起始的代碼中有16個位元組的校驗和,是以我們的_start應該是在0xc3e00010開始的。我們使用反彙編來看下reset的指令。

Uboot16之start.S七種異常模式及向量表

事實證明_start表示是代碼段的基位址,即_start=TEXT_BASE=0xc3e00010。

其他知識點:

@reset用b,就是因為reset在MMU建立前後都有可能發生

@其他的異常隻有在MMU建立之後才會發生

1)異常向量表是硬體決定的,軟體隻是參照硬體的設計來實作它。

Uboot16之start.S七種異常模式及向量表

2)異常向量表中每種異常都應該被處理,否則真遇到了這種異常就跑飛了。但是我們在uboot中并未非常細緻的處理各種異常。

3)複位異常處的代碼是:b reset,是以在CPU複位後真正去執行的有效代碼是reset處的代碼,是以reset符号處才是真正的有意義的代碼開始的地方。

注:ARM微處理器支援位元組(8位)、半字(16位)、字(32位)3種資料類型向量跳轉表,每條占四個位元組(一個字),位址範圍為0x0000 0000~0x0000 0020 。

ARM體系結構規定在上電複位後的起始位置,必須有8條連續的跳轉指令,通過硬體實作。他們就是異常向量表。ARM在上電複位後,是從0x00000000開始啟動的,其實如果bootloader存在,在執行下面第一條指令後,就無條件跳轉到start_code,下面一部分并沒執行。設定異常向量表的作用是識别bootloader。以後系統每當有異常出現,則CPU會根據異常号,從記憶體的0x00000000處開始查表做相應的處理:

;當一個異常出現以後,ARM會自動執行以下幾個步驟:

;1.把下一條指令的位址放到連接配接寄存器LR(通常是R14).----------------------儲存位置

;2.将相應的CPSR(目前程式狀态寄存器)複制到SPSR(備份的程式狀态寄存器)中--------------------儲存CPSR

;3.根據異常類型,強制設定CPSR的運作模式位

;4.強制PC(程式計數器)從相關異常向量位址取出下一條指令執行,進而跳轉到相應的異常處理程式中

-------------------------------------reset的代碼講解-------------------------------------------------------------------

reset:

更改處理器模式為管理模式

31 30 29 28 --- 7 6 - 4 3 2 1 0

N Z C V I F M4 M3 M2 M1 M0

0 0 0 0 0 User26 模式

0 0 0 0 1 FIQ26 模式

0 0 0 1 0 IRQ26 模式

0 0 0 1 1 SVC26 模式

1 0 0 0 0 User 模式

1 0 0 0 1 FIQ 模式

1 0 0 1 0 IRQ 模式

1 0 0 1 1 SVC 模式

1 0 1 1 1 ABT 模式

1 1 0 1 1 UND 模式

1 1 1 1 1 SYS 模式

對狀态寄存器的修改要按照:讀-改-寫的順序來執行

    @;mrs    r0,cpsr

MRS指令用于将程式狀态寄存器的内容傳送到通用寄存器中。

    @;bic    r0,r0,#0x1f

BIC指令用于清除操作數1的某些位,并把結果放置到目的寄存器中。

    @;orr    r0,r0,#0xd3

ORR指令用于在兩個操作數上迕行邏輯戒運算,幵把結果放置到目的寄存器中。

    @;msr    cpsr,r0

MSR指令用于将操作數的内容傳送到程式狀态寄存器的特定域中。

    msr    cpsr_c, #0xd3        @ I & F disable, Mode: 0x13 – SVC

1)msr    cpsr_c, #0xd3    将CPU設定為禁止FIQ IRQ,ARM狀态,SVC模式。

2)其實ARM CPU在複 位時預設就會進入SVC模式,但是這裡還是使用軟體将其置為SVC模式。整個uboot工作時CPU一直處于SVC模式。

Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表

---------------------------------------------reset代碼結束--------------------------------------------------------------

    ldr    pc, _undefined_instruction

    ldr    pc, _software_interrupt

    ldr    pc, _prefetch_abort

    ldr    pc, _data_abort

    ldr    pc, _not_used

    ldr    pc, _irq

    ldr    pc, _fiq一般用在實時性比較高的中斷

LDR指令用于從存儲器中将一個32位的字資料傳送到目的寄存器中。該指令通常用于從存儲器中讀取32位的字資料到通用寄存器,然後對資料進行處理。當程式計數器PC作為目的寄存器時,指令從存儲器中讀取的字資料被當作目的位址,進而可以實作程式流程的跳轉。

Uboot16之start.S七種異常模式及向量表

_undefined_instruction:

    .word undefined_instruction

_software_interrupt:

    .word software_interrupt

_prefetch_abort:

    .word prefetch_abort

_data_abort:

    .word data_abort

_not_used:

    .word not_used

_irq:

    .word irq

_fiq:

    .word fiq

_undefined_instruction: .word undefined_instruction。_undefined_instruction是一個标号,處理到這裡時,asm會把undefined_instruction的值按32bit的形式放在此标号處。 .word就是在這個地方放一個值。相當于在這裡定義一個資料變量。用.word定義了一個32bit的資料。ldr pc, _undefined_instruction 就是從_undefined_instruction處取值,即undefined_instruction, 并設定到pc中。并将underfined_instruction的值本身放在這裡,是以pc=undefined_instruction,實作跳轉。

-------------------------------- undefined_instruction代碼講解-------------------

    .align    5

從這段文字來看,ARM的.align 5就是2的5次方對齊,也就是4位元組對齊。

undefined_instruction:

在跳轉中斷服務程式前,首先要有兩個宏操作,一個是對stack的擷取操作,一個是對使用者reg儲存;

    get_bad_stack

------------------------get_bad_stack代碼講解----------------

此處的get_bad_stack被後面undefined_instruction,software_interrupt等處調用,目前能了解的意思是,在出錯的時候,獲得對應的堆棧的值

    .macro get_bad_stack

    ldr    r13, _armboot_start        @ setup our mode stack (enter in banked mode)

    sub    r13, r13, #(CFG_MALLOC_LEN)    @ move past malloc pool

    sub    r13, r13, #(CFG_GBL_DATA_SIZE+8) @ move to reserved a couple spots for abort stack

    str    lr, [r13]            @ save caller lr in position 0 of saved stack

    mrs    lr, spsr            @ get the spsr

    str    lr, [r13, #4]            @ save spsr in position 1 of saved stack

    mov    r13, #MODE_SVC            @ prepare SVC-Mode

    @ msr    spsr_c, r13

    msr    spsr, r13            @ switch modes, make sure moves will execute

    mov    lr, pc                @ capture return pc

    movs    pc, lr                @ jump to next instruction & switch modes.

    .endm

-------------------------get_bad_stack代碼講解結束----------------

    bad_save_user_regs

------------------------- bad_save_user_regs代碼講解----------------

.macro    bad_save_user_regs

Macro聲明的是一個函數。

    sub    sp, sp, #S_FRAME_SIZE        @ carve out a frame on current user stack

S_FRAME_SIZE=72。這句話意思就是sp = sp – 72;

    stmia    sp, {r0 - r12}            @ Save user registers (now in svc mode) r0-r12

LDM(或STM)指令用于從由基址寄存器所訓示的一片連續存儲器到寄存器清單所訓示的多個寄存器之間傳送資料,該指令的常見用途是将多個寄存器的内容入棧或出棧。

将r0---r12一共13個寄存器的資料推入堆棧上,位址空間為13*4加上其中的狀态寄存器等需要72 位元組的空間。

Uboot16之start.S七種異常模式及向量表

    ldr    r2, _armboot_start

此處的含義是将_armboot_start的值傳給r2,即0xc3e00010。

    sub    r2, r2, #(CFG_MALLOC_LEN)

此處

Uboot16之start.S七種異常模式及向量表
Uboot16之start.S七種異常模式及向量表

r2

= r2 - CFG_MALLOC_LEN

= r2 –(16*1024+896*1024)

= 0xc3e00010 - 912KB

= 0xC3D1C010

    sub    r2, r2, #(CFG_GBL_DATA_SIZE+8)    @ set base 2 words into abort stack

r2

= r2 –136

= 0xc3d1c010-136

= 0xC3D1BF88

    Ldmia r2, {r2 - r3}        @ get values for "aborted" pc and cpsr (into parm regs)

分别将位址為r2和r2+4的内容,即位址為0xC3D1BF88和0xC3D1BF8C中的内容,load載入給r2和r3寄存器。

    add    r0, sp, #S_FRAME_SIZE        @ grab pointer to old stack

将sp的值,加上72,送給r0

    add    r5, sp, #S_SP

将sp的值,加上52,送給r5

    mov    r1, lr

将lr給r1

    stmia        r5, {r0 - r3}            @ save sp_SVC, lr_SVC, pc, cpsr

然後将r0到r3中的内容,存儲到位址為r5---àr5+12中的位置去。

    mov    r0, sp                @ save current stack into r0 (param register)

将sp再指派給r0

    .endm

------------------------- bad_save_user_regs代碼講解結束----------------

    bl    do_undefined_instruction

Uboot16之start.S七種異常模式及向量表

其做的事情依次是:

獲得出錯時候的堆棧

儲存使用者模式寄存器

跳轉到對應的函數:do_undefined_instructi

做了一些操作:列印提示警告,并列印什麼中斷号、pc指針的位置、寄存器的數值等。。。。然後重新開機CPU:reset

-------------------------------- undefined_instruction代碼講解結束-------------------

_pad:

    .word 0x12345678

.global _end_vect

_end_vect:

    .balignl 16,0xdeadbeef

1).balignl 16,0xdeadbeef.    這一句指令是讓目前位址對齊排布,如果目前位址不對齊則自動向後走位址直到對齊,并且向後走的那些記憶體要用0xdeadbeef來填充。

2)0xdeadbeef這是一個十六進制的數字,這個數字很有意思,組成這個數字的十六進制數全是abcdef之中的字母,而且這8個字母剛好組成了英文的dead beef這兩個單詞,字面意思是壞牛肉。

3)為什麼要對齊通路?有時候是效率的要求,有時候是硬體的特殊要求。