U-boot-2009.03移植之五:第一階段之start.s詳解

 

如前文U-boot-2009.03移植之四中所講,第一階段,也就是支持2440的階段,首先要做的事情就是修改/cpu/arm920t/start.s 

start.s 源碼註解參考

http://blog.chinaunix.net/u1/34474/showart.php?id=2217073

如何添加支持2440修改參考

http://zjbintsystem.blog.51cto.com/964211/211345

(針對UBOOT從Nand啓動的支持部分,參考這個網頁修改,實際上這個網頁給出的移植攻略很全,可以多看。)

值得注意的是,在以前版本的Uboot都是從nor啓動,所以在拷貝代碼那部分的源碼跟我們貼出來的這個修改後的源碼不同,原因就是那些支持從nor啓動的代碼已經被刪除,取而代之的是我們加上了從nand啓動的代碼

http://blog.csdn.net/Charistain_huang/archive/2010/04/09/5469007.aspx

(針對u-boot-2009,但是有不少錯誤,尤其是對於start.s的解釋,更多要結合下面這一篇看)

http://home.eeworld.com.cn/my/space.php?uid=135723&do=blog&id=25347

(針對u-boot-1.3.4,但是個人認爲是寫的最好最全的一篇,與2009非常相近)

 

需要注意的是,兩片文章在“代碼搬移”這部分代碼的區別,個人認爲1.3.4的是正確的。

我在這裏只附上修改後的源碼並做簡要解釋,源碼如下:

註釋部分由“//hadise=========”開頭

 

 

#include <config.h>

#include <version.h>

 

 

//hadise===========#include <status_led.h> 已經被刪除掉 /*這是針對AT91RM9200DK開發板的,刪掉。*/

 

/*

 *************************************************************************

 *

 * Jump vector table as in table 3.1 in [1]

 *

 *************************************************************************

 */

 

 

.globl _start

_start: b       start_code

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

 

//hadise=========以上幾句是裝載特定的地址到PC寄存器,當發生特殊的事件時候,程序會跳轉到這幾句話中的某一句,裝載

//地址到PC中,實際上就是跳轉到相應的地址指向特定的代碼

.balignl 16,0xdeadbeef        //hadise=====4字節對齊,0xdeadbeef是magic碼

 

 

/*

 *************************************************************************

 *

 * Startup Code (called from the ARM reset exception vector)

 *

 * do important init only if we don't start from memory!

 * relocate armboot to ram

 * setup stack

 * jump to second stage

 *

 *************************************************************************

 */

 

_TEXT_BASE:

.word TEXT_BASE

 

.globl _armboot_start

_armboot_start:

.word _start

 

/*

 * 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 start code

 */

 

start_code:

/*

* set the cpu to SVC32 mode

*/

mrs r0,cpsr       //hadise複製cpsr 的內容到r0中     

bic r0,r0,#0x1f   //hadise==bic是位清除指令,這句話的意思是,讓r0與立即數#0x1f相與,返回r0。實質是取低5位

orr r0,r0,#0xd3   //hadise==因爲低5位的值代表了ARM狀態,其中,d3這個值,代表了svc32模式

msr cpsr,r0       //hadise==然後把設置好的狀態,寫回到cpsr

//hadise=======以上4句話,是要設置ARM狀態爲SVC32模式,這裏說點小知識:

ARM6開始有了32位模式,32 位的與 26 位的不同是: 

PC (R15)是完全的 32 位寬,並只用做程序計數器。 

PSR 包含在它自己的寄存器 CPSR(當前程序狀態寄存器) 中。 

每個有特權的模式都有一個專有的 SPSR 寄存器,用來保存 CPSR。 

這裏有兩個新的特權模式,每個有特權的模式都有 R13 和 R14 的專有復件。

這就造成對於R15的操作,也就是對於PC的操作,不能用以前的指令,要用專用的指令 mrs 和 msr 

 

 

@bl coloured_LED_init

@bl red_LED_on

//hadise========以上兩句是針對AT91RM9200DK開發板的,因此刪除

#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)

/*

* relocate exception table

*/

ldr r0, =_start

ldr r1, =0x0

mov r2, #16

copyex:

subs r2, r2, #1

ldr r3, [r0], #4

str r3, [r1], #4

bne copyex

#endif

//hadise=========針對AT91RM9200DK開發板的代碼

 

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

/* turn off the 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 INTMOD 0x4A000004

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

#  define INTSUBMSK 0x4A00001C

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

# endif

 

//hadise========以上這裏有自己修改,針對2440和2410的不同,設置了相應寄存器的地址

 

 

#define CLK_CTL_BASE 0x4C000000

#define MDIV_405 0x7f<<12

#define PSDIV_405 0x21

#define MDIV_200 0xa1<<12

#define PSDIV_200 0x31

 

ldr     r0, =pWTCON

mov     r1, #0x0

str     r1, [r0]

//hadise====以上3句關閉看門狗

/*

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

*/

mov r1, #0xffffffff

ldr r0, =INTMSK

str r1, [r0]

//hadise=====以上3句關閉所有中斷

# if defined(CONFIG_S3C2410)

ldr r1, =0x3ff

ldr r0, =INTSUBMSK

str r1, [r0]

# endif

# if defined(CONFIG_S3C2440)

ldr r1, =0x7fff

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]

//hadise=====以上屏蔽了時鐘的修改,我們在以後的函數中再修改

 

# if defined(CONFIG_S3C2440)    /* FCLK:HCLK:PCLK = 1:4:8 */    

ldr r0, =CLKDIVN    

mov r1, #5    

str r1, [r0]       

mrc p15, 0, r1, c1, c0, 0            /*read ctrl register */    

orr r1, r1, #0xc0000000              /*Asynchronous */    

mcr p15, 0, r1, c1, c0, 0            /*write ctrl register */    /*now, CPU clock is 405.00 Mhz */    

mov r1, #CLK_CTL_BASE               /* */    

mov r2, #MDIV_405                    /* mpll_405mhz */    

add r2, r2, #PSDIV_405               /* mpll_405mhz */    

str r2, [r1, #0x04]                   /* MPLLCON */

//hadise======================================以上設置時鐘的代碼很重要,很多時候UBOOT移植不成功就是時鐘沒設置對

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

ldr r0, =CLKDIVN    

mov r1, #3    

str r1, [r0]    

mrc p15, 0, r1, c1, c0, 0                 /*read ctrl register */    

orr r1, r1, #0xc0000000                  /*Asynchronous */    

mcr p15, 0, r1, c1, c0, 0                /*write ctrl register */  /*now, CPU clock is 202.8 Mhz */    

mov r1, #CLK_CTL_BASE                     /* */    

mov r2, #MDIV_200                         /* mpll_200mhz */    

add r2, r2, #PSDIV_200                    /* mpll_200mhz */    

str r2, [r1, #0x04] # endif

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

 

 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

     adr r0,_start

ldr r1,_TEXT_BASE

cmp r0,r1

blne cpu_init_crit

#endif

//hadise===============比較_start和_TEXT_BASE,以判斷當前UBOOT是在flash中運行還是內存中運行,如果不等於,表示在

//hadise===============flash中運行,那麼,跳轉到cpu_init_crit

//hadise=====由board/samsung/mini2440/u-boot.lds鏈接文件中可以看出,testbase就是start.o開始的地方,如果是在flash中,

//haidise===則應該等於0,否則等於內存起始物理地址,對於mini2440來說,應該是0x30000000,而start則絕對的是0x0000000.

//hadise====跳入cpu_init_crit ,這是一個系統初始化函數,他還會調用board/*/lowlevel_init.S中的lowlevel_init函數。

主要是對系統總線的初始化,初始化了連接存儲器的位寬、速度、刷新率等重要參數。經過這個函數的正確初始化,Nor Flash、SDRAM纔可以被系統使用。下面的代碼重定向就依

 

賴它。

 

 

/*

* we do sys-critical inits only at reboot,

* not when booting from ram!

*/

 

@#ifdef CONFIG_S3C2440_NAND_BOOT

#define NAND_CTL_BASE 0x4E000000/* Offset */

#define oNFCONF 0x00

#define oNFCONT 0x04

#define oNFCMD 0x08

#define oNFSTAT 0x20

#define LENGTH_UBOOT 0x40000    

 @ reset NAND

 

/*往下四段內容都是針對S3C2440的關於NAND-FLASH的寄存器的設置,具體有什麼作用,看了datasheet,有些明白有些不明白*/

    mov    r1, #NAND_CTL_BASE            

    ldr    r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )

    str    r2, [r1, #oNFCONF]             /*這些宏是在include/configs/qljt2440.h中被定義的*/

    ldr    r2, [r1, #oNFCONF]  /*還是弄不懂爲什麼上面一句str以後還要有這句的ldr命令?why?難道是多餘的?*/

 

    ldr    r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control 

    str    r2, [r1, #oNFCONT]

    ldr    r2, [r1, #oNFCONT]

 

    ldr    r2, =(0x6)        @ RnB Clear

    str    r2, [r1, #oNFSTAT]

    ldr    r2, [r1, #oNFSTAT]

 

    mov    r2, #0xff        @ RESET command

    strb    r2, [r1, #oNFCMD]

/*delay一段時間*/

  mov r3, #0                   @ wait

nand1: 

  add  r3, r3, #0x1

  cmp r3, #0xa

  blt   nand1

/*等待nand-flash的復位完畢信號*/

nand2:

  ldr   r2, [r1, #oNFSTAT]      @ wait ready

  tst    r2, #0x4

  beq  nand2

 

  ldr    r2, [r1, #oNFCONT]

 orr    r2, r2, #0x2        @ Flash Memory Chip Disable  /*在這裏先Display fansh CE先,在C函數中對falsh進行*/

 

str    r2, [r1, #oNFCONT]                          /*操作的時候才enable,爲什麼這樣操作不太清楚*/   

/*下面這段用來初始化棧指針sp和幀指針fp,至於它們的定義和作用參考文件夾” 棧指針sp和幀指針fp”裏面的內容

 

記住它們都是與函數調用時候相關的。簡單來講就是子函數被調用以後是通過指針的相對位置來查找調用參數和局部變量的,但是由於sp經常變化,所以需要fp來協助。*/

@ get ready to call C functions (for nand_read())

  ldr   sp, DW_STACK_START       @ setup stack pointer /*sp 是指堆棧指針*/

  mov fp, #0                    @ no previous frame, so fp=0  

 

 

 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    adr r0,_start

    ldr r1,_TEXT_BASE

cmp r0,r1

beq stack_setup

@@@@@@@@@@@@@@@@@@@@@@@@@@@

 

   //這部分做了特殊記號,是因爲當初吃了虧,忘記了加這部分。參考網中提到的攻略裏面沒有寫到這一部分,但是一定要加上這幾句話。

作用是,如果不是在ram中運行,則要先跳到初始化堆棧的代碼部分,在stack_setup代碼段中,完成堆棧初始化,然後調用_start_armboot進入第二階段C語言部分,如果不加這句話,則會繼續把代碼從nand搬運到ram中,不必要!

 

@ copy U-Boot to RAM               /*vivi裏面應該是有一段是針對gpio的程序,也許使用來debug用的信號燈,這裏省略了*/

         /* TEXT_BASE 是uboot自己的入口地址,在u-boot-1.3.4-board/qljt/qljt2440的config.mk中定義

 

有趣的是外國人的逆向思維很厲害,它們很靈活地把它放在SDRAM的最後0x80000地方,也就是0x33F80000 

 

*/

 

  ldr   r0, =TEXT_BASE /*r0 : 把u-boot複製到ram的那個位置*/

         mov     r1, #0x0                         /*r1 : 從falsh的那個位置開始複製*/

         mov r2, #0x20000                     /*r2 : 複製多大的內容*/

         bl    nand_read_ll            /*跳到執行uboot複製的程序入口,這個函數從哪裏來?也是來自vivi的,沒辦法*/

         tst    r0, #0x0                            /*這裏特別注意r0的值是指nand_read_ll 執行完以後的返回值,而不是上面

 

ldr   r0, =TEXT_BASE 的值,初學者往往在這裏想不通*/     

         beq  ok_nand_read

bad_nand_read:                  /*如果讀nand_read失敗的話,那麼sorry,重來,或者檢查硬件*/

loop2:    b     loop2          @ infinite loop

ok_nand_read:

@ verify             

 

/*計算機就是好,很容易就可以檢測我們放在SDRAM中的u-boot是不是flash中的uboot。

 

本開發板使用的是nand-falsh的啓動方式,板子一上電並不是馬上進入SDRAM執行程序的。是這樣的:板子一上電,S3C2440自動把nand-falsh中從0地址開始的4Kbytes複製到

 

S3C2440集成的某個緩衝區裏面(起始地址是0x00),從那裏開始執行,那4K程序負責把整個uboot複製到SDRAM,然後才跳到SDRAM開始正真的UBOOT(這個技術是有個專業名字叫做

 

stepingstone),*/

 

/*下面這段程序的作用就是用開始執行的4Kbytes程序跟我們複製到SDRAM中的uboot的前4K程序進行比較,從而校驗*/

  mov r0, #0

  ldr   r1, =TEXT_BASE

  mov r2, #0x400     @ 4 bytes * 1024 = 4K-bytes

go_next:

  ldr   r3, [r0], #4

  ldr   r4, [r1], #4

  teq   r3, r4

  bne  notmatch

  subs r2, r2, #4

  beq  stack_setup

  bne  go_next

 

notmatch:

loop3:     b     loop3         @ infinite loop

 

//=========================================================

/* Set up the stack */

stack_setup:

ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot   */

sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area                      */

sub r0, r0, #CONFIG_SYS_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    */

 

clear_bss:

ldr r0, _bss_start /* find start of bss segment        */

ldr r1, _bss_end /* stop here                        */

mov r2, #0x00000000 /* clear                            */

 

clbss_l:str r2, [r0] /* clear loop...                    */

add r0, r0, #4

cmp r0, r1

ble clbss_l

 

ldr pc, _start_armboot

 

_start_armboot: .word start_armboot

#define STACK_BASE 0x33f00000

#define STACK_SIZE 0x10000

   .align 2

DW_STACK_START: .word STACK_BASE+STACK_SIZE-4

 

/*

 *************************************************************************

 *

 * CPU_init_critical registers

 *

 * setup important registers

 * setup memory timing

 *

 *************************************************************************

 */

 

 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

cpu_init_crit:

/*

* flush v4 I/D caches

*/

mov r0, #0

mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */

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

 

/*

* before relocating, we have to setup RAM timing

* because memory timing is board-dependend, you will

* find a lowlevel_init.S in your board directory.

*/

mov ip, lr

#if defined(CONFIG_AT91RM9200EK)

 

#else

bl lowlevel_init

//hadise=========================這一句很重要,跳轉到相應板子的lowlevel_init.S,注意看上面的英文解釋

#endif

mov lr, ip

mov pc, lr

#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

 

/*

 *************************************************************************

 *

 * Interrupt handling

 *

 *************************************************************************

 */

 

@

@ IRQ stack frame.

@

#define S_FRAME_SIZE 72

 

#define S_OLD_R0 68

#define S_PSR 64

#define S_PC 60

#define S_LR 56

#define S_SP 52

 

#define S_IP 48

#define S_FP 44

#define S_R10 40

#define S_R9 36

#define S_R8 32

#define S_R7 28

#define S_R6 24

#define S_R5 20

#define S_R4 16

#define S_R3 12

#define S_R2 8

#define S_R1 4

#define S_R0 0

 

#define MODE_SVC 0x13

#define I_BIT 0x80

 

/*

 * use bad_save_user_regs for abort/prefetch/undef/swi ...

 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling

 */

 

.macro bad_save_user_regs

sub sp, sp, #S_FRAME_SIZE

stmia sp, {r0 - r12} @ Calling r0-r12

ldr r2, _armboot_start

sub r2, r2, #(CONFIG_STACKSIZE)

sub r2, r2, #(CONFIG_SYS_MALLOC_LEN)

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

ldmia r2, {r2 - r3} @ get pc, cpsr

add r0, sp, #S_FRAME_SIZE @ restore sp_SVC

 

add r5, sp, #S_SP

mov r1, lr

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

mov r0, sp

.endm

 

.macro irq_save_user_regs

sub sp, sp, #S_FRAME_SIZE

stmia sp, {r0 - r12} @ Calling r0-r12

add     r7, sp, #S_PC

stmdb   r7, {sp, lr}^                   @ Calling SP, LR

str     lr, [r7, #0]                    @ Save calling PC

mrs     r6, spsr

str     r6, [r7, #4]                    @ Save CPSR

str     r0, [r7, #8]                    @ Save OLD_R0

mov r0, sp

.endm

 

.macro irq_restore_user_regs

ldmia sp, {r0 - lr}^ @ Calling r0 - lr

mov r0, r0

ldr lr, [sp, #S_PC] @ Get PC

add sp, sp, #S_FRAME_SIZE

subs pc, lr, #4 @ return & move spsr_svc into cpsr

.endm

 

.macro get_bad_stack

ldr r13, _armboot_start @ setup our mode stack

sub r13, r13, #(CONFIG_STACKSIZE)

sub r13, r13, #(CONFIG_SYS_MALLOC_LEN)

sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack

 

str lr, [r13] @ save caller lr / spsr

mrs lr, spsr

str     lr, [r13, #4]

 

mov r13, #MODE_SVC @ prepare SVC-Mode

@ msr spsr_c, r13

msr spsr, r13

mov lr, pc

movs pc, lr

.endm

 

.macro get_irq_stack @ setup IRQ stack

ldr sp, IRQ_STACK_START

.endm

 

.macro get_fiq_stack @ setup FIQ stack

ldr sp, FIQ_STACK_START

.endm

 

/*

 * exception handlers

 */

.align  5

undefined_instruction:

get_bad_stack

bad_save_user_regs

bl do_undefined_instruction

 

.align 5

software_interrupt:

get_bad_stack

bad_save_user_regs

bl do_software_interrupt

 

.align 5

prefetch_abort:

get_bad_stack

bad_save_user_regs

bl do_prefetch_abort

 

.align 5

data_abort:

get_bad_stack

bad_save_user_regs

bl do_data_abort

 

.align 5

not_used:

get_bad_stack

bad_save_user_regs

bl do_not_used

 

#ifdef CONFIG_USE_IRQ

 

.align 5

irq:

get_irq_stack

irq_save_user_regs

bl do_irq

irq_restore_user_regs

 

.align 5

fiq:

get_fiq_stack

/* someone ought to write a more effiction fiq_save_user_regs */

irq_save_user_regs

bl do_fiq

irq_restore_user_regs

 

#else

 

.align 5

irq:

get_bad_stack

bad_save_user_regs

bl do_irq

 

.align 5

fiq:

get_bad_stack

bad_save_user_regs

bl do_fiq

 

#endif

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章