Uboot啟動分析筆記-----Stage1(start.S與lowlevel_init.S詳解)


Uboot啟動分析筆記-----Stage1(start.S與lowlevel_init.S詳解)

 

1  u-boot.lds

    首先了解uboot的鏈接腳本board/my2410/u-boot.lds,它定義了目標程序各部分的鏈接順序。

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")  /*指定輸出可執行文件為ELF格式,32為,ARM小端*/

OUTPUT_ARCH(arm)/*指定輸出可執行文件為ARM平台*/

ENTRY(_start)/*起始代碼段為 _start*/

SECTIONS

{

         /* 指定可執行image文件的全局入口點,通常這個地址都放在ROM(flash)0x0位置*、

         . = 0x00000000;從 0x0位置開始

         . = ALIGN(4); 4字節對齊

         .text :

         {

                   cpu/arm920t/start.o    (.text)

                   board/my2440/lowlevel_init.o      (.text)

                   *(.text)

         }

         . = ALIGN(4);

         .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

         . = ALIGN(4);

         .data : { *(.data) }  /*  只讀數據段 ,所有的只讀數據段都放在這個位置*/

         . = ALIGN(4);

         .got : { *(.got) }  /*指定got段, got段式是uboot自定義的一個段, 非標准段*/

         . = .;

         __u_boot_cmd_start = .; /*把__u_boot_cmd_start賦值為當前位置, 即起始位置*/

         .u_boot_cmd : { *(.u_boot_cmd) }  /*  u_boot_cmd段,所有的u-boot命令相關的定義都放在這個位置,因為每個命令定義等長,

                       所以只要以__u_boot_cmd_start為起始地址 進行查找就可以很快查找到某一個命令的定義,並依據定義的命令指針調用相應的函數進行處理用戶的任務*/

         __u_boot_cmd_end = .;   /* u_boot_cmd段結束位置,由此可以看出,這段空間的長度並沒有嚴格限制,

                                                 用戶可以添加一些u-boot的命令,最終都會在連接是存放在這個位置。*/

         . = ALIGN(4);

         __bss_start = .; /*把__bss_start賦值為當前位置,即bss段的開始位置*/

         .bss (NOLOAD) : { *(.bss) . = ALIGN(4); } /*指定bss段,這里NOLOAD的意思是這段不需裝載,僅在執行域中才會有這段*/

         _end = .; /*把_end賦值為當前位置,即bss段的結束位置*/

}

第一個鏈接的是cpu/board/start.o,也即Uboot的入口指令在start中,下面詳細分析程序的跳轉和函數調用關系。

 

2 Stage1 : cpu/arm920t/start.S

    這個匯編程序時UBoot的入口程序,以復位向量開頭。

                    reset

                        ↓

                cpu_init_crit

                        ↓

                   relocate

                        ↓

                stack_setup

                        ↓

               start_armboot()

                        ↓

             init_sequence[]

                        ↓

                  getenv()

                        ↓

                 main_loop()

 其中前面4步為Stage1下面來詳細分析一下  cpu/arm920t/start.S

這里以ARM9 2410為例,2440移植時需要修改一些配置,具體的再后面的移植中介紹.

/* 這段代碼的主要作用:

進入SVC模式

關閉看門狗

屏蔽所有IRG掩碼

設置時鍾頻率 FCLK HCLK PCLK

清楚I/D Cache

禁止MMU和CACHE

配置memory control

重定位:如果代碼不在指定的地址上需要把uboot從當前位置copy到RAM指定位置上

建立堆棧,為進入C函數做准備

清0 .bss段

跳入start_armboot函數進入stage2(lib_arm/board.c)*/

/****************************************************/

.globl _start
_start: /* 系統復位位置, 各個異常向量對應的跳轉代碼 */
b 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 /* 快速中斷異常向量 */
_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
.balignl 16,0xdeadbeef


/**ARM9支持7種異常。下面是異常的響應過程

*第一個復位異常,它放在0x0的位置,一上電就執行它,而且我們的程序總是從復位異常處理程序

*    開始執行的,因此復位異常處理程序不需要返回。

*其他異常處理的如下:

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

*(1) 把下一條指令的地址放到連接寄存器LR(通常是R14),這樣就能夠在處理異常返回時從正確的位置繼續執行。

*(2) 將相應的CPSR(當前程序狀態寄存器)復制到SPSR(備份的程序狀態寄存器)中。從異常退出的時候,就可以由SPSR來恢復CPSR。

*(3) 根據異常類型,強制設置CPSR的運行模式位。

*(4) PC(程序計數器)被強制成相關異常向量處理函數地址,從而跳轉到相應的異常處理程序中。

* 當異常處理完畢后,ARM會執行以下幾步操作從異常返回:

*(1)將連接寄存器LR的值減去相應的偏移量后送到PC中

*(2) 將SPSR復制回CPSR中

*(3) 若在進入異常處理時設置了中斷禁止位,要在此清除

*

* ARM規定了異常向量的地址:
* b reset ; 復位 0x0

* ldr pc, _undefined_instruction ; 未定義的指令異常 0x4

* ldr pc, _software_interrupt ; 軟件中斷異常 0x8

* ldr pc, _prefetch_abort ; 預取指令 0xc

* ldr pc, _data_abort ; 數據 0x10

* ldr pc, _not_used ; 未使用 0x14

* ldr pc, _irq ; 慢速中斷異常 0x18

* ldr pc, _fiq ; 快速中斷異常 0x1c
* 當處理器碰到異常時,PC會被強制設置為對應的異常向量,從而跳轉到

* 相應的處理程序,然后再返回到主程序繼續執行。

* .balignl 16,0xdeadbeef, 將地址對齊到16的倍數,如果地址寄存器的值(PC)跳過4個字節才是16的倍數,

* 則使用0xdeadbeef填充這4個字節,如果它跳過1、2、3個字節,則填充值不確定。如果地址寄存器的值(PC)是16的倍數,則無需移動。********************/

 

/*************************************************************************
* Startup Code (reset vector) ………………….

*************************************************************************/
/* 保存變量的數據區 */
_TEXT_BASE:
.word TEXT_BASE   ;定義一個字並分配空間 4bytes

.globl _armboot_start
_armboot_start:
.word _start  ;聲明一個全局的,並用 _start 初始化它, 在u-boot.lds中定義

/* These are defined in the board-specific linker script.*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/* the actual reset code*/
/* 系統的復位代碼。系統一上電,就跳到這里運行 */

 

reset:
    mrs r0,cpsr          /* 取得當前程序狀態寄存器cpsr到r0 */
    bic r0,r0,#0x1f    /* 這里使用位清除指令,把中斷全部清除,只置位模式控制位為中斷提供服務通常是 OS設備驅動程序的責任,

                                 因此在 Boot Loader 的執行全過程中可以不必響應任何中斷*/
    orr r0,r0,#0xd3   /* 計算為超級保護模式,並disable IRQ和FIQ */
    msr cpsr,r0 /* 設置cpsr為超級保護模式 */
/*****************

*CPSR 的底8位為I     F        T        M4       M3       M2       M1       M0

          IRQdisable FIQdisable StateBit

SVC[M4~M0] = 10011

StateBit = set:THUMB state, others:ARM state

* 設置cpu運行在SVC32模式。ARM9共有7種模式:
* 用戶模式(usr): arm處理器正常的程序執行狀態
* 快速中斷模式(fiq): 用於高速數據傳輸或通道處理
* 外部中斷模式(irq): 用於通用的中斷處理
* 超級保護模式(svc): 操作系統使用的保護模式
* 數據訪問終止模式(abt): 當數據或指令預取終止時進入該模式,可用於虛擬存儲及存儲保護
* 系統模式(sys): 運行具有特權的操作系統任務
* 未定義指令中止模式(und): 當未定義的指令執行時進入該模式,可用於支持硬件協處理器的軟件仿真
* 通過設置ARM的CPSR寄存器,讓CPU運行在操作系統保護模式,為后面進行其它操作作好准備了。

*********************************************************/

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

     /* turn off the watchdog */

//這段是關watchdog

#if defined(CONFIG_S3C2400)

#  define pWTCON   0x15300000

#  define INTMSK   0x14400008    /* Interupt-Controller base addresses */

#  define CLKDIVN  0x14800014    /* clock divisor register */

#else

#  define pWTCON   0x53000000

#  define INTMSK   0x4A000008    /* Interupt-Controller base addresses */

#  define INTSUBMSK    0x4A00001C

#  define CLKDIVN  0x4C000014    /* clock divisor register */

# endif

     ldr  r0, =pWTCON   //具體可以查看手冊

     mov  r1, #0x0

     str  r1, [r0]

     /*

      * mask all IRQs by setting all bits in the INTMR - default

      */

     mov  r1, #0xffffffff //禁止所有中斷

     ldr  r0, =INTMSK

     str  r1, [r0]

# if defined(CONFIG_S3C2410)

     ldr  r1, =0x3ff

     ldr  r0, =INTSUBMSK

     str  r1, [r0]

# endif

     /* FCLK:HCLK:PCLK = 1:2:4 */

     /* default FCLK is 120 MHz ! */

     ldr  r0, =CLKDIVN

     mov  r1, #3

     str  r1, [r0]

#endif  /* defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)*/

/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
       bl cpu_init_crit  ;cpu初始化,其中會調用lowlevel_init.S

 

/******************************************************************************
* BL為相對尋址,以程序計數器PC 的當前值為基地址,指令中的地址標號作為偏移量,將兩者相加之后得到操作數的有效地址
* ARM 指令集中的4條跳轉指令可以完成從當前指令向前或向后的32MB 的地址空間的跳轉,
* 用的是相對尋址,它們是:B、BL、BLX、BX
*******************************************************************************/

#endif


#ifndef CONFIG_SKIP_RELOCATE_UBOOT
/* 重定位Boot代碼到RAM內存,將Boot代碼從FLASH移到RAM中 。因為flash中執行速度很慢,而且系統每次復位了都會在0x00000000處執行*/


relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */

/**************************************************************************
* 把_start的相對地址移到r0, 相對尋址以程序計數器PC 的當前值為基地址,
* 指令中的地址標號作為偏移量,將兩者相加之后得到操作數的有效地址。
* 它是與位置無關的,主要看Boot在哪里運行,也就是PC指針在哪里 (假設_start偏移量為0),
* 例如這段代碼在 0x05000000 (FLASH起始地址)運行,即此時PC=0x05000000,

那么 adr r0, _start 得到 r0 = 0x05000000;
* 如果在地址 0x33008000(Boot在RAM中加載地址)運行,即此時PC=0x33008000,那么r0就是 0x33008000 了。

*通過adr指令得到當前代碼的地址信息:如果U-boot是從RAM開始運行,則從adr,r0,_start得到的地址信息為*r0=_start=_TEXT_BASE=TEXT_BASE=0x3ff80000;如果U-boot從Flash開始運行,即從處理器對應的地址運 行,則*r0=0x0000,這時將會執行copy_loop標識的那段代碼了
**************************************************************************/
     ldr r1, _TEXT_BASE/* test if we run from flash or RAM */

/* 把_TEXT_BASE地址處的值TEXT_BASE,也就是BOOT在RAM中運行地址移到r1 */
   cmp r0, r1 /* don't reloc during debug */

/* 比較兩個地址是否相同,如果相同,就已經在RAM運行,否則就是FLASH中運行 */

beq stack_setup
/* 如果是在FLASH中運行, 則把FLASH中的Boot代碼移到RAM中,然后再運行 */
ldr r2, _armboot_start /* 把_armboot_start地址處的值也就是_start絕對地址(也即在內存中的地址,這個絕對
* 地址是在 link 的時候確定的,如0x81008000)移到r2 */
        ldr r3, _bss_start /* 把_bss_start地址處的值也就是__bss_start絕對地址(也即在內存中的地址,這個絕對
* 地址是在 link 的時候確定的)移到r3 */
        sub r2, r3, r2 /* r2 <- size of armboot */ /* 計算引導代碼大小並存到r2 */
        add r2, r0, r2 /* r2 <- source end address */ /* 計算引導代碼最后相對地址並存入r2 */
copy_loop:
        ldmia r0!, {r3-r10} /* copy from source address [r0] */ /* 從源地址[r0]讀取32個字節到寄存器,並更新r0 */
        stmia r1!, {r3-r10} /* copy to target address [r1] */ /* 拷貝寄存器r3-r10的32個字節值保存到 [r1]指明的地址,並更新r1的值 */
        cmp r0, r2 /* until source end addreee [r2] */ /* 循環拷貝,直到把所有引導代碼都移到內存 */
        ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

 

/* Set up the stack */
/* 在內存中建立起堆棧 */

 

stack_setup:
        ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
        sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
        sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
        sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
        sub sp, r0, #12 /* leave 3 words for abort-stack */

 

/* 初始化內存中bss段中數據為0 */
clear_bss:
        ldr r0, _bss_start /* find start of bss segment*/

 /* 把_bss_start地址處存儲的絕對地址移到r0 */
ldr r1, _bss_end /* stop here */ /* 把_bss_end地址處存儲的絕對地址移到r1 */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... STR 指令用於從源寄存器中r2將一個32 位的字數據傳送到存儲器中[r0]*/
add r0, r0, #4
cmp r0, r1
ble clbss_l /* 小於或等於跳轉 */
ldr pc, _start_armboot

 

/***********************************************************
* 已經准備好了堆棧,就可跳到C寫的代碼里了,也就是
* 跳到內存中的/u-boot-1.1.6/board.c --> start_armboot中運行了
* 把_start_armboot地址處的值也就是start_armboot絕對地址值移到pc
* 神啊!終於跳到C代碼了。
***********************************************************/
_start_armboot:
.word start_armboot
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
/**************************************************************************
* 1、關閉 MMU和CPU 內部指令/數據 (I/D)cache。
* 2、設置 CPU 的速度和時鍾頻率。
* 3 、RAM 初始化。
****************************************************************************/
cpu_init_crit:
/* flush v4 I/D caches*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
/******************************************************************************************************
* MCR 指令用於將ARM 處理器寄存器中的數據傳送到協處理器寄存器中,格式為:
* MCR 協處理器編碼,協處理器操作碼1,源寄存器,目的寄存器1,目的寄存器2,協處理器操作碼2。
* 其中協處理器操作碼1 和協處理器操作碼2 為協處理器將要執行的操作,
* 源寄存器為ARM 處理器的寄存器,目的寄存器1 和目的寄存器2 均為協處理器的寄存器。
******************************************************************************************************/
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/ * disable MMU stuff and caches*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */
bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */
orr r0, r0, #0x00000002 /* set bit 2 (A) Align */
orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
mcr p15, 0, r0, c1, c0, 0
/ * Go setup Memory and board specific bits prior to relocation.*/
mov ip, lr /* perserve link reg across call */
bl lowlevel_init /* go setup pll,mux,memory */ /* 位於u-boot-1.1.6/board/xxx(開發板目錄名稱)/lowlevel_init.S */
mov lr, ip /* restore link */
mov pc, lr /* back to my caller */ /* 從cpu_init_crit子函數返回 */
/*************************************************************************
*
* Interrupt handling
*
*************************************************************************/

/*下面是中斷相關的東西,這里略去*/

 

 

 

3 ./board/my2410/lowlevel_init.S

 

.globl lowlevel_init //讀取下面標號為SMRDATA處的地址到R0中
    ldr r0, =SMRDATA
     /*讀取上面標號為_TEXT_BASE處的地址內容到R1中
     *也就是取得TEXT_BASE的值到R1中*/
    ldr    r1, _TEXT_BASE
     /*計算SMRDATA的相對地址保存到R0中
     *SMRDATA為虛擬地址,而TEXT_BASE為虛擬地址的起始地址
     *而現在Uboot的起始地址並不為虛擬地址
     *TEXT_BASE為0x33F8 0000,SMRDATA為0x33F8 06C8
     *而現在程序運行在起始地址為0x0000 0000的地方
     *所以需要計算以0x0000 0000為標准的相對地址*/
    sub    r0, r0, r1
     //取得帶寬與等待狀態控制寄存器地址到R1中
    ldr    r1, =BWSCON    /* Bus Width Status Controller */
     //一共需要設置13個寄存器,每個寄存器4字節,詳見芯片手冊
    add r2, r0, #13*4
0:
     //讀取R0所指的項的值到R3中后R0自加4字節
    ldr r3, [r0], #4
     //將R3中的值保存到R1所指的地址中后R1自加4字節
    str r3, [r1], #4
     //比較R0和R2是否相等,相等則說明13個寄存器全部設置完畢
    cmp r2, r0
     //不等則跳轉到上面標號為0處的地址繼續執行
    bne 0b
     //跳回到返回地址中繼續執行
    mov    pc, lr
    .ltorg
/* the literal pools origin */
SMRDATA:
    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

//設置每個BWSCON,注意BANK0由硬件連線決定了
    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
   //設置BANKCON0~BANKCON5    

    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))

//設置BANKCON6~BANKCON7
    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

//設置REFRESH,在S3C2440中11~17位是保留的,也即(Tchr<<16)無意義
    .word 0x32

//設置BANKSIZE,對於容量可以設置大寫,多出來的空內存會被自動檢測出來
    .word 0x30

//設置MRSRB6
    .word 0x30

//設置MRSRB7


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM