/arch/arm920t/cpu/start.s文件就是uboot的第一阶段,uboot的前4K内容,即nandflash中的前4K内容会被拷贝到cpu中的SRAM中运行,这一小段代码负责初始化硬件环境,并将剩余的Uboot代码加载到内存中去。从而跳转到第二阶段,在第二阶段运行之前需要建立堆栈。
IRQ_STACK_START .word 0x0badc0de
FRQ_STACK_START .word 0x0badc0de
/*
* armboot - Startup Code for ARM920 CPU-core
*
*/
#include <config.h>
#include <version.h>
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start
_start: b reset //b是跳转指令,即跳转到reset标签处。接着返回到此处。
ldr pc, _undefined_instruction设置中断向量表 0x0 0x4.....0x8 0xc
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
利用.word来在当前位置放置一个值,这个值实际上就用对应的中断处理函数的地址
;.word的意义为在当前地址处放入一个16bits值
.balignl 16,0xdeadbeef
/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage //从英文注释中可以看出starts.o的作用。
*
*************************************************************************
*/
_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 reset code
*/
_start:入口处,马上跳转到reset处,在SVC状态下要关闭所有中断。
reset:
/*
* set the cpu to SVC32 mode //超级用户态,操作系统使用的保护模式。系统复位和软件中断时进入该模式。
*/
mrs r0,cpsr //将当前cpsr的状态为保存到r0中。
bic r0,r0,#0x1f //bic,位清零指令。0x1f=00011111,相当于清除低5位。刚好是模式位。
orr r0,r0,#0xd3 //或指令。很明显是置模式位。0xd3=11010011以及设置5,6,7位的状态位。禁止FIQ,IRQ,处于arm状态。低5位为10011,则对应超级用户态。
msr cpsr,r0 //在将r0中的值赋给状态寄存器cpsr
/* turn off the watchdog */关闭看门狗
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)我们的是2410,如果移植其它的,则可以添加相关的基地址
# define pWTCON 0x53000000 //pWTCON 看门狗寄存器的基地址,手册上就是WTCON,这里多一个p
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C 中断子掩码寄存器
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
将看门狗寄存器清空,其各位含义为,第0位为1则当看门狗定时器溢出时重启,为0则不重启,初始值为1
第2位为中断使能位,初值为0
第3,4位为时钟分频银子,初值为00
第5位为看门狗的使能位,初值为1
第8~15位为比例因子,初值为0x80
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON //将pWTCON寄存器的地址赋给r0
mov r1, #0x0
str r1, [r0] //将pWTCON所有位置为0,就关闭看门狗了,实际上只要将第5位置0即可。
/*
* 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, =0x7ff 011111111111,很多资料上是0x3ff。手册上规定有13位可用
ldr r0, =INTSUBMSK如果是2410,这个寄存器也要屏蔽
str r1, [r0] //将低13位全都置1 13位可用 而不是12位
# endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */时钟设置
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT 防止重复引用cpu_init_crit函数。
bl cpu_init_crit
#endif
橙色的部分代码,是针对从nor flash中启动的设备。对于不是从nandflash中启动的设备,没什么用。这一段好像也能执行代码复制.
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
查看当前代码的地址信息,若从ram中运行,则_start=_TEXT_BASE,否则_start = 0x00000000
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 若不等,则表示从flash中运行,重定向代码
ldr r3, _bss_start 获取未初始化数据段地址
sub r2, r3, r2 /* r2 <- size of armboot */代码段的长度
add r2, r0, r2 /* r2 <- source end address */代码的长度
copy_loop:
复制代码,r0为代码的起始地址,r1为ram中地址,r2为代码的终止地址,每次copy后将r0的值递增同r2比较来判断是否复制完成。
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 192K */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo 分配128字节来存储开发板信息 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif再减去12字节用于栈起点
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
#if 0
/* 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]
/* 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
现在的设别,大都是利用SDRAM与NANDFLASH共同作用,因此为了支持能从nand_flash中启动,需要添加下面的红色代码。
#ifdef CONFIG_S3C2410_NAND_BOOT
bl copy_myself
@jump to ram
ldr r1,=on_the_ram
add pc,r1,#0
nop
nop
1:
b 1b
on_the_ram
#endif
ldr pc, _start_armboot
_start_armboot: .word start_armboot
#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
mov r10.lr 将当前的lr保存到r10中,保存断点地址
@reset nand
mov r1,#NAND_CTL_BASE nandflash基地址
ldr r2,0xf830 r2 = 1111,1000,0011,0000 第15位为nandflash控制器使能位,第12位初始化ECC,第11位nFCE使能?????有问题
str r2,[r1,#oNFCONF]
ldr r2,[r1,#oNFCONF]
bic r2,r2,#0x800 将第11位清零指令,使能芯片
str r2,[r1,#oNFCONF]
写入命令
mov r2,#0xff @reset command
strb r2,[r1,#oNFCMD]
mov r3,#0
1:
add r3, r3, #0x1
//
循环等待
cmp r3, #0xa
blt 1b
2:
利用状态寄存器测试flash内部操作是否完成,如果完成则状态寄存器将返回1.NFSTAT最后一位为0时,表示正在忙,为1表示空闲
ldr r2, [r1, #oNFSTAT]
@ wait ready
tst r2, #0x1
//tst
位测试指令
beq 2b
ldr r2, [r1, #oNFCONF]
禁止芯片
orr r2, r2, #0x800
@ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
建立堆栈,栈起点0x33f00000,大小为0x8000
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
。r0 r1 r2为传入到Nand_read_ll中的三个参数,
nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
可知,
ldr r0, =UBOOT_RAM_BASE
mov r1, #0x0
//mov r2, #0x20000 //UBOOT
大小为128K 这三个参数传入到nand_read_ll中
mov r2. #0x30000
//
设置为192k。
bl nand_read_ll
tst r0, #0x0
r0
为返回值nand_read_ll的返回值
beq ok_nand_read
bad_nand_read:
loop2: b loop2 @ infinite loop
ok_nand_read:
nand_flash
通过ecc校验来保证拷贝到SDRAM中的内容是正确的
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400
@ 4 bytes * 1024 = 4K-bytes
,每条指令占4个字节,共移动0x400即1024次,加起来就移动了4*1024即4K的大小
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
#endif @ CONFIG_S3C2410_NAND_BOOT
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu的设置,清楚I/D cache,清除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 */
/*
* 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 //返回到reset
bl lowlevel_init
mov lr, ip
mov pc, lr //从lowlevel_init中,返回到reset
#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+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_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 r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #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+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_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