最简单的bootloader的编写步骤:
1.设置CPU工作模式
2. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
3. 如果bootloader比较大,要把它重定位到SDRAM
4. 把内核从NAND FLASH读到SDRAM
5. 设置"要传给内核的参数"
6. 跳转执行内核
我们学习uboot start.s 首先我么要很好的理解上面的几个个图片
另外 在ARM中
ROM主要指:NAND Flash,Nor Flash
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
首先我们去Uboot官网下载一个UBOOT(ftp://ftp.denx.de/pub/u-boot/),然后找到相应的start.s文件。
1. uboot 硬件初始化 CPU工作模式设置
.globl _start //声明一个全局变量 系统复位位置,真个程序的入口
_start: b start_code //各个异常向量对应的相对跳转代码 ,0x00
ldr pc, _undefined_instruction //未定义指令异常,0x04 ldr pc, _software_interrupt //软中断异常,0x08 ldr pc, _prefetch_abort //内存操作异常,0x0c ldr pc, _data_abort //数据异常,0x10 ldr pc, _not_used //未适用,0x14 ldr pc, _irq //慢速中断异常,0x18 ldr pc, _fiq //快速中断异常,0x1c
这是系统异常向量表, 不是让你一条一条来顺序执行的。这是系统remap之前用的中断向量。除了reset vector 有意义外,其他基本上是死循环。 系统重启动时, 就走第一个向量 执行 b start_code, 跳到下面start_code下执行startup code. 如果系统出现问题,执行到一条不认识的指令时,这时系统异常中断发生,系统跳转到第二个向量, 执行ldr pc, _undefined_instruction;这时pc被设置成undefined_instruction,CPU到_undefined_instruction地址去执行这一地址下的指令, 结果还是一个未定义指令,如此循环下去; 其他异常向量,原理类似。 |
.balignl 16,0xdeadbeef //对齐内存为16的倍数
start_code: /*复位启动子程序,设置cpu运行在SVC32模式。共有7种模式*/
mrs r0,cpsr /*复制当前程序状态寄存器cpsr到r0*/ bic r0,r0,#0x1f //这里使用位清除指令,把中断全部清除,只置位模式控制位 这里我们可以看到把第五位全部清理了 orr r0,r0,#0xd3 /*选择新模式,(现在设为超级保护模式)*/ 11010011 管理模式 msr cpsr,r0 /*设置cpsr为超级保护模式*/ /*通过设置ARM的CPSR寄存器,让CPU运行在操作系统模式,为后面进行其它操作作好准备*/
通过上面 我们可以看到我们把CPU初始化成SVC模式了 可是为什么我们要初始化SVC模式了 1中止模式和未定义模式 首先可以排除的是,中止abt和未定义und模式,那都是不太正常的模式,此处程序是正常运行的,所以不应该设置CPU为其中任何一种模式,所以可以排除。 2.快中断fiq和中断irq模式 其次,对于快中断fiq和中断irq来说,此处uboot初始化的时候,也还没啥中断要处理和能够处理,而且即使是注册了终端服务程序后,能够处理中断,那么这两种模式,也是自动切换过去的,所以,此处也不应该设置为其中任何一种模式。 3.用户usr模式 虽然从理论上来说,可以设置CPU为用户usr模式,但是由于此模式无法直接访问很多的硬件资源,而uboot初始化,就必须要去访问这类资源,所以此处可以排除,不能设置为用户usr模式。 4.系统sys模式 vs 管理svc模式 首先,sys模式和usr模式相比,所用的寄存器组,都是一样的,但是增加了一些访问一些在usr模式下不能访问的资源。而svc模式本身就属于特权模式,本身就可以访问那些受控资源,而且,比sys模式还多了些自己模式下的影子寄存器,所以,相对sys模式来说,可以访问资源的能力相同,但是拥有更多的硬件资源。
2. 初始化硬件:关看门狗、设置时钟
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
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]
# elif defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
2. 初始化CPU
bl cpu_init_crit 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
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
MCR的用法简介 MCR{<cond>} <p>,< opcode_1>,<Rd>,<CRn>,<CRm>{,<opcode_2>} • <cond> 指令执行的条件码.当<cond>忽略时指令为无条件执行。 • <opcode_1> 协处理器将执行的操作的操作码。对于CP15协处理器来说,<opcode_1>永远为0b000,当<opcode_1>不为0b000时,该指令操作结果不可预知。 • <Rd> 作为源寄存器的ARM寄存器,其值将被传送到协处理器寄存器中 • <CRn> 作为目标寄存器的协处理器寄存器,其编号可能是C0,C1,…,C15。 <CRm>和<opcode_2>两者组合决定对协处理器寄存器进行所需要的操作,如果没有指定,则将为<CRm>为C0,opcode_2为0 C7 Any data written to this location will cause the selected cache to be flushed.
I 表示指令寄存器 D表示数据寄存器
<CRm>和<opcode_2>
C7 - 0 :Flush I + D
C5 - 0 :Flush I
C6 - 0 :Flush D
C6 - 1 :Flush D single Virtual address
CA - 1 :Clean D entry Virtual address
CA - 4 :drain write buf.
C8 Any data written to this location will cause the selected TLB flush operation
I 表示指令寄存器 D表示数据寄存器
<CRm>和<opcode_2>
C7 - 0 :Flush I + D
C5 - 0 :Flush I
C6 - 0 :Flush D
C6 - 1 :Flush D single Virtual address
3.设置堆栈
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 */ bl clock_init
3.从定位
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 clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */