開始start.s分析,根據S3C2410來跟蹤代碼,參考了S3C2410的datasheet和網上各個帖子
一般來說,大家都是從start.s來分析UBOOT,但是事實是流程是從makefile中來的,也就是說是在敲入make smdk2410_config和make all後才進入start.s中,makefile在這裏不做分析,韋東山的那本書有詳細的說明,這裏要提到的是makefile傳進來的,我們在UBOOT中所設計到的LDFLAGS,這個標誌確定了連接方式,其中的-T board/smd2410/U-Boot.lds -Ttext 0x33F8000(展開後的)指定了程序的佈局和地址,U-Boot.lds如下(參考martree的專欄):
/*******************************************************/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm"")
;指定輸出可執行文件是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)
;指定輸出可執行文件的平臺爲ARM
ENTRY(_start)
;指定輸出可執行文件的起始代碼段爲_start.
SECTIONS
{
. = 0x00000000 ; 指明目標代碼的起始地址從0x0位置開始,"."代表的是當前位置
. = ALIGN(4) ; 代碼以4字節對齊
.text : ;指定代碼段
{
cpu/arm920t/start.o (.text) ; 代碼的第一個代碼部分,指明start.s是入口程序代碼,被放到代碼段的開頭
*(.text) ;其它代碼部分
}
. = ALIGN(4)
.rodata : { *(.rodata) } ;指定只讀數據段,RO段
. = ALIGN(4);
.data : { *(.data) } ;指定讀/寫數據段,RW段
. = ALIGN(4);
.got : { *(.got) } ;指定got段, got段式是uboot自定義的一個段, 非標準段
__u_boot_cmd_start = . ;把__u_boot_cmd_start賦值爲當前位置, 即起始位置
.u_boot_cmd : { *(.u_boot_cmd) } ;指定u_boot_cmd段, uboot把所有的uboot命令放在該段.
__u_boot_cmd_end = .;把__u_boot_cmd_end賦值爲當前位置,即結束位置
. = ALIGN(4);
__bss_start = .; 把__bss_start賦值爲當前位置,即bss段的開始位置
.bss : { *(.bss) }; 指定bss段
_end = .; 把_end賦值爲當前位置,即bss段的結束位置
}
;指定輸出可執行文件是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)
;指定輸出可執行文件的平臺爲ARM
ENTRY(_start)
;指定輸出可執行文件的起始代碼段爲_start.
SECTIONS
{
. = 0x00000000 ; 指明目標代碼的起始地址從0x0位置開始,"."代表的是當前位置
. = ALIGN(4) ; 代碼以4字節對齊
.text : ;指定代碼段
{
cpu/arm920t/start.o (.text) ; 代碼的第一個代碼部分,指明start.s是入口程序代碼,被放到代碼段的開頭
*(.text) ;其它代碼部分
}
. = ALIGN(4)
.rodata : { *(.rodata) } ;指定只讀數據段,RO段
. = ALIGN(4);
.data : { *(.data) } ;指定讀/寫數據段,RW段
. = ALIGN(4);
.got : { *(.got) } ;指定got段, got段式是uboot自定義的一個段, 非標準段
__u_boot_cmd_start = . ;把__u_boot_cmd_start賦值爲當前位置, 即起始位置
.u_boot_cmd : { *(.u_boot_cmd) } ;指定u_boot_cmd段, uboot把所有的uboot命令放在該段.
__u_boot_cmd_end = .;把__u_boot_cmd_end賦值爲當前位置,即結束位置
. = ALIGN(4);
__bss_start = .; 把__bss_start賦值爲當前位置,即bss段的開始位置
.bss : { *(.bss) }; 指定bss段
_end = .; 把_end賦值爲當前位置,即bss段的結束位置
}
/****************************************/
從這裏可以看出start.s是程序的入口點,下面是start.s代碼
//global聲明一個符號可被其它文件引用,相當於聲明瞭一個全局變量,.globl與.global相同。
//該部分爲處理器的異常處理向量表。地址範圍爲0x0000 0000 ~ 0x0000 0020,剛好8條指令。
//該部分爲處理器的異常處理向量表。地址範圍爲0x0000 0000 ~ 0x0000 0020,剛好8條指令。
.globl _start
_start: b reset /*跳轉到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
_start: b reset /*跳轉到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
// .word僞操作用於分配一段字內存單元(分配的單元都是字對齊的),並用僞操作中的expr初始化。.long與.int作用與之//相同。
_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
/*
* Startup Code (reset vector)
*
* do important init only if we don't start from RAM!
* - relocate armboot to ram
* - setup stack
* - jump to second stage
*/
* Startup Code (reset vector)
*
* do important init only if we don't start from RAM!
* - relocate armboot to ram
* - setup stack
* - jump to second stage
*/
// TEXT_BASE在開發板相關的目錄中的config.mk文件中定義, 它定義了
// 代碼在運行時所在的地址, 那麼_TEXT_BASE中保存了這個地址
_TEXT_BASE:
.word TEXT_BASE
// 代碼在運行時所在的地址, 那麼_TEXT_BASE中保存了這個地址
_TEXT_BASE:
.word TEXT_BASE
// 聲明 _armboot_start 並用 _start 來進行初始化,在board/u-boot.lds中定義。
.globl _armboot_start
_armboot_start:
.word _start
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
* These are defined in the board-specific linker script.
*/
// 聲明_bss_start並用__bss_start來初始化,其中__bss_start定義在與板相關的u-boot.lds中。
// _bss_start保存的是__bss_start這個標號所在的地址, 這裏涉及到當前代碼所在
// 的地址不是編譯時的地址的情況, 這裏直接取得該標號對應的地址, 不受編譯時
// 地址的影響. _bss_end也是同樣的道理.
.globl _bss_start
_bss_start:
.word __bss_start
// 同上
.globl _bss_end
_bss_end:
.word _end
// _bss_start保存的是__bss_start這個標號所在的地址, 這裏涉及到當前代碼所在
// 的地址不是編譯時的地址的情況, 這裏直接取得該標號對應的地址, 不受編譯時
// 地址的影響. _bss_end也是同樣的道理.
.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 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
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
// MRS {<cond>} Rd,CPSR|SPSR 將CPSR|SPSR傳送到Rd
// 使用這兩條指令將狀態寄存器傳送到一般寄存器,只修改必要的位,再將結果傳送回狀態寄存器,這樣可以最好地完成對CRSP或者SPSR的修改
// MSR {<cond>} CPSR_<field>|SPSR_<field>,Rm 或者是 MSR {<cond>} CPSR_f|SPSR_f,#<32-bit immediate>
// MRS與MSR配合使用,作爲更新PSR的“讀取--修改--寫回”序列的一部分
// bic r0,r1,r2 ;r0:=r1 and not r2
// orr ro,r1,r2 ;r0:=r1 or r2
// 這幾條指令執行完畢後,進入SVC模式,該模式主要用來處理軟件中斷(SWI)
reset:
mrs r0,cpsr /* set the cpu to SVC32 mode */
bic r0,r0,#0x1f /* (superviser mode, M=10011) */
orr r0,r0,#0xb3 /*disable irq and frq SVC mode*/
msr cpsr,r0
// 使用這兩條指令將狀態寄存器傳送到一般寄存器,只修改必要的位,再將結果傳送回狀態寄存器,這樣可以最好地完成對CRSP或者SPSR的修改
// MSR {<cond>} CPSR_<field>|SPSR_<field>,Rm 或者是 MSR {<cond>} CPSR_f|SPSR_f,#<32-bit immediate>
// MRS與MSR配合使用,作爲更新PSR的“讀取--修改--寫回”序列的一部分
// bic r0,r1,r2 ;r0:=r1 and not r2
// orr ro,r1,r2 ;r0:=r1 or r2
// 這幾條指令執行完畢後,進入SVC模式,該模式主要用來處理軟件中斷(SWI)
reset:
mrs r0,cpsr /* set the cpu to SVC32 mode */
bic r0,r0,#0x1f /* (superviser mode, M=10011) */
orr r0,r0,#0xb3 /*disable irq and frq SVC mode*/
msr cpsr,r0
/* 關閉看門狗,S3C2410手則 */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* 屏蔽所有中斷
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
* 屏蔽所有中斷
*/
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 */
/*FCLK默認爲120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*FCLK默認爲120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*該語句首先調用cpu_init_crit進行CPU的初始化,並把下一條指令的地址保存在LR中,以使得執行完後能夠正常返回。後面會分析*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
//調試階段的代碼是直接在RAM中運行的,而最後需要把這些代碼固化到Flash中,因此U-Boot需要自己從Flash轉移到
//RAM中運行,這也是重定向的目的所在。
//通過adr指令得到當前代碼的地址信息:如果U-boot是從RAM開始運行,則從adr,r0,_start得到的地址信息爲
//r0=_start=_TEXT_BASE=TEXT_BASE=0xa3000000;如果U-boot從Flash開始運行,即從處理器對應的地址運行,
//則r0=0x0000,這時將會執行copy_loop標識的那段代碼了。
// _TEXT_BASE 定義在board/pxa255_idp/config.mk中
//RAM中運行,這也是重定向的目的所在。
//通過adr指令得到當前代碼的地址信息:如果U-boot是從RAM開始運行,則從adr,r0,_start得到的地址信息爲
//r0=_start=_TEXT_BASE=TEXT_BASE=0xa3000000;如果U-boot從Flash開始運行,即從處理器對應的地址運行,
//則r0=0x0000,這時將會執行copy_loop標識的那段代碼了。
// _TEXT_BASE 定義在board/pxa255_idp/config.mk中
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [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 */
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 */
// 這裏如果需要使用IRQ, 還有給IRQ保留堆棧空間
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
//這裏是直接減去12,但是在後面board.c中的cpu_init中代碼是減8再減4的(如下),大家看到後面再反過來看看,會覺得很有意//思
/*
/*
int cpu_init (void)
{
/*
* setup up stacks if necessary
*/
#ifdef CONFIG_USE_IRQ
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0;
}
{
/*
* setup up stacks if necessary
*/
#ifdef CONFIG_USE_IRQ
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0;
}
*/
sub sp, r0, #12 /* leave 3 words for abort-stack */
//該部分將未初始化數據段_bss_start----_bss_end中的數據清零。
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
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
add r0, r0, #4
cmp r0, r1
ble clbss_l
#if 0
/* try doing this stuff after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/* try doing this stuff after the relocation */
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, =INTMR
str r1, [r0]
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END stuff after relocation */
#endif
// 通過該語句跳轉到C代碼執行,stage1的使命也算完成。
ldr pc, _start_armboot
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END stuff after relocation */
#endif
// 通過該語句跳轉到C代碼執行,stage1的使命也算完成。
ldr pc, _start_armboot
_start_armboot: .word start_armboot
#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 */
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 */
/*
* 關閉MMU和緩存,這裏用到了協處理器P15,參看手則,哈哈,這裏有的很多看了
*/
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
* 關閉MMU和緩存,這裏用到了協處理器P15,參看手則,哈哈,這裏有的很多看了
*/
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
/*進入lowlevel_init,這裏主要是初始化存儲控制器,S3C2410的是Bank0-bank6,比如位寬等,這個要根據自己的板子來進行相應的配置,比如網卡放在第幾個bank,位寬多少,SDRAM放在哪裏,多大等,移植UBOOT的時候就有的學了*/
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
//程序後面的都是中斷的處理,定義了一系列的宏,然後就是中斷的處理函數,篇幅太長不列出