专注linux
分类: IT业界
2014-11-22 22:37:06
闲来无事,研究下Uboot的启动代码。整理份笔记,也供大家分享学习。
Uboot的起始函数位于start.s中,属于板级代码,就从这里开始喽。。。
.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
_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
_pad:
.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram
* setup stack
*
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE
/*
* Below variable is very important because we use MMU in U-Boot.
* Without it, we cannot run code correctly before MMU is ON.
* by scsuh.
*/
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_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
*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr /* 取出cpsr中的内容 */
bic r0,r0,#0x1f /* 清除 bit[4:0] */
orr r0,r0,#0xd3 /* 设置 bit[4:0] 为管理模式,关闭irq和fiq*/
msr cpsr,r0 /* 写入cpsr */
即设置了M[4:0]为b10011,为管理模式。具体模式可参考下图。
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
cpu_init_crit:
/*
刷新命令和数据缓冲。mcr命令用于向协处理器写入数据。
格式:MCR{cond} P15,
P15是协处理器,Rd为普通寄存器(源寄存器),CRn为p15协处理器的一个寄存器(32位)。mcr p15, 0, r0, c7, c7, 0是什么意思呢?从下面图中可以找到答案:Invalidate Both Caches。
*/
/*
* flush v4 I/D caches
*/
mov r0, #0 /* 将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为从协处理器的寄存器中读取数据。
bic将操作数1的相应为清除。下图为p15协处理器c1寄存器的位图。
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
/* 参考上图可知,此句关闭V、R、S。这些位是什么意思呢?
V :决定了异常矢量的位置。
0 普通异常矢量。矢量基地址寄存器决定了地址范围。
1 高异常矢量。地方范围:0xFFFF0000-0xFFFF001C
R:ROM保护使能。如果设置了此位,不会影响已经在TLB中的访问权限。
0 关闭ROM保护
1 开启ROM保护
S:MMU保护使能。如果设置了此位,不会影响已经在TLB中的访问权限。
0 关闭MMU保护
1 开启MMU保护
*/
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
/*
B:大小端位
0 小端
1 大端
C:级别1数据缓冲位
0 关闭
1 开启
A:检测数据对齐位
0 关闭检测
1 开启检测
M:MMU使能位
0 关闭MMU
1 开启MMU
*/
orr r0, r0, #0x00000002 @ set bit 2 (A) Align /* 开启对齐检测 */
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache /* 开启1级指令缓冲 */
mcr p15, 0, r0, c1, c0, 0 /* 写p15的控制寄存器 */
/* Peri port setup */
ldr r0, =0x70000000 /* 将0x70000000装入r0中 */
orr r0, r0, #0x13 /* 设置bit[4:0] 为0b10011 */
mcr p15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff)
/*
上句,设置Peripheral Port Memory Remap。如下图,设置了size。那size有是怎么一回事呢?
*/
/*
参照下图中的值,即可对应其含义。
*/
#ifdef CONFIG_BOOT_ONENAND
ldr r0, =0x70000000 @ onenand controller setup
orr r0, r0, #0x100000
ldr r1, =0x4000
orr r1, r1, #0xe0
str r1, [r0]
#if defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)
orr r0, r0, #300 @ disable watchdog
mov r1, #1
str r1, [r0]
mov r1, #0x23000000 @ start buffer register
orr r1, r1, #0x30000
orr r1, r1, #0xc800
#else
mov r1, =0x20000000 @ start buffer register
orr r1, r1, #0xc30000
orr r1, r1, #0xc800
#endif
sub r0, r1, #0x0400 @ start address1 register
ldr r2, [r1, #0x84] @ ecc bypass
orr r2, r2, #0x100
str r2, [r1, #0x84]
mov r3, #0x0 @ DFS, FBA
str r3, [r0, #0x00]
str r3, [r0, #0x04] @ select dataram for DDP as 0
mov r4, #0x104 @ interrupt register
mov r5, #0x0002 @ FPA, FSA
mov r6, #0x0800 @ BSA
onenand_bl1_load:
str r5, [r0, #0x1c] @ save FPA, FSA
orr r6, r6, #0x02 @ BSC
str r6, [r1, #0x00] @ save BSA, BSC
str r3, [r1, r4] @ clear interrupt
str r3, [r1, #0x80] @ write load command
mov r7, #0x100 @ need small delay
onenand_wait_loop1:
subs r7, r7, #0x1
bne onenand_wait_loop1
add r5, r5, #0x2 @ next FPA, FSA
sub r6, r6, #0x2
add r6, r6, #0x200 @ next BSA
cmp r5, #0x8
bne onenand_bl1_load
#endif
/* 上面这一段我想比较简单,我就不多说了。大家可以耐下心来仔细研究即可看明白。 */
/*
* Go setup Memory and board specific bits prior to relocation.
*/
bl lowlevel_init /* go setup pll,mux,memory */ /* 此函数不在此文件中,先不说。后续文章中会有提及。 */
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
/* 查看uboot是否已经在内存中 */
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip flash copy */
#ifdef CONFIG_BOOT_NOR /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_PHY_BASE /* r1 <- destination */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
/* 循环去copy */
nor_copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] *//* 每次会copy一段内存到r3-r10 */
stmia r1!, {r3-r10} /* copy to target address [r1] *//* copy r3-r10 寄存器内容到 r1所指内存 */
cmp r0, r2 /* until source end addreee [r2] */
ble nor_copy_loop
b after_copy
#endif
after_copy:
#ifdef CONFIG_ENABLE_MMU
enable_mmu:
/* enable domain access */
ldr r5, =0x0000ffff
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
/*
这里又涉及另一个寄存器c3(Domain Access Control)。
开启D0-D7的域访问权限。
*/
/* Set the TTB register */
ldr r0, _mmu_table_base
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0
/*
c2 寄存器(Translation Table Base Register 0)
上图为c3的位格式。
*/
/* Enable the MMU */
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1 /* Set CR_M to enable MMU */
mcr p15, 0, r0, c1, c0, 0
nop
nop
nop
Nop
/* 上面这段也很简单了,在前面的设置中有提及。打开MMU */
#endif
skip_hw_init:
/* Set up the stack */
stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc)
#else
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 */
#endif
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 函数。此函数为C函数。 */
_start_armboot:
.word start_armboot
#ifdef CONFIG_ENABLE_MMU
_mmu_table_base:
.word mmu_table
#endif
目前的学习,不要太拘于细节,要从大体着手,把整体的流程捋清楚。有时间可以再回头研究细节。如果在初学阶段就研究那么细的话,很容易半途而废,挫折太多,使得自己对自己都没有了信心。我就是这样学习的,如果大家还有什么好的观点,不妨拿出来,与大家分享。
关于ARM相关的,希望大家能下一本ARM1174的参考手册。网上有很多,可以找来看看,或者去ARM的官方网站中找,也是很容易找到的。如果还是没有找到,也可以在下面的连接中去下载一份(需要csdn账户)。