uboot中start.S文件详解
1:第一段代码
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>
#ifndef CONFIG_ENABLE_MMU
#ifndef CFG_PHY_UBOOT_BASE
#define CFG_PHY_UBOOT_BASE CFG_UBOOT_BASE
#endif
#endif
头文件包含config.h在mkconfig脚本中生成,内容为#include <configs/x210.h>
头文件包含version.h文件中的内容是#include "version_autogenerated.h"
version_autogenerated.h文件是在主Makefile中自动生成的
生成代码为
$(VERSION_FILE):
@( printf ‘#define U_BOOT_VERSION "U-Boot %s%s"\n‘ "$(U_BOOT_VERSION)" ‘$(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion $(TOPDIR))‘ ) > $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
生成的内容为
#define U_BOOT_VERSION "U-Boot 1.3.4"
包含regs.h:regs.h在之前makefile中分析过来 连接的s5pc110.h
因为定义了CONFIG_ENABLE_MMU所以包含asm/proc/domain.h
同样asm、proc也是符号连接实际文件为:include\asm-arm\proc-arm\domain.h文件
2:第二段代码:
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif
因为if语句成立,所以定义了4字,值为0x2000、0x0、0x0、0x0 .word是arm汇编的伪指令:含义是当前地址的值为XX,
XX的内容一般为数值或者地址;
如:.word 0x2000表示当前地址的值为0x2000;
.word _start 表示当前地址的值为_start函数
3:第三段代码
1 .globl _start
2 _start: b reset
3 ldr pc, _undefined_instruction
4 ldr pc, _software_interrupt
5 ldr pc, _prefetch_abort
6 ldr pc, _data_abort
7 ldr pc, _not_used
8 ldr pc, _irq
9 ldr pc, _fiq
10
11 _undefined_instruction:
12 .word undefined_instruction
13 _software_interrupt:
14 .word software_interrupt
15 _prefetch_abort:
16 .word prefetch_abort
17 _data_abort:
18 .word data_abort
19 _not_used:
20 .word not_used
21 _irq:
22 .word irq
23 _fiq:
24 .word fiq
25 _pad:
26 .word 0x12345678 /* now 16*4=64 */
27 .global _end_vect
28 _end_vect:
29
30 .balignl 16,0xdeadbeef
这段代码的作用是建立异常向量表;
比如说uboot在sd卡中,先复制前16K到0xD0020010地址处,建立异常向量表的位置也应该在这个位置,初始化内存以后把整个uboot复制到内存中以后,异常向量表应该在内存的地址处0x23e00000处,
所以说这个异常向量表应该只是虚有其表,实际上是没有什么用的;(注:个人理解可能有误);
1 _TEXT_BASE:
2 .word TEXT_BASE
3
4 /*
5 * Below variable is very important because we use MMU in U-Boot.
6 * Without it, we cannot run code correctly before MMU is ON.
7 * by scsuh.
8 */
9 _TEXT_PHY_BASE:
10 .word CFG_PHY_UBOOT_BASE
11
12 .globl _armboot_start
13 _armboot_start:
14 .word _start
15
16 /*
17 * These are defined in the board-specific linker script.
18 */
19 .globl _bss_start
20 _bss_start:
21 .word __bss_start
22
23 .globl _bss_end
24 _bss_end:
25 .word _end
26
27 #if defined(CONFIG_USE_IRQ)
28 /* IRQ stack memory (calculated at run-time) */
29 .globl IRQ_STACK_START
30 IRQ_STACK_START:
31 .word 0x0badc0de
32
33 /* IRQ stack memory (calculated at run-time) */
34 .globl FIQ_STACK_START
35 FIQ_STACK_START:
36 .word 0x0badc0de
37 #endif
下面这段代码的意思:
_TEXT_BASE(4字节)这个内存地址出的值为TEXT_BASE:0xc3e00000
_TEXT_PHY_BASE(4字节)这个内存存放的值为 CFG_PHY_UBOOT_BASE(uboot的物理基地址):
(定义在x210_sd.h中)CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0x3e00000
(定义在x210_sd.h中)#define MEMORY_BASE_ADDRESS 0x20000000
CFG_PHY_UBOOT_BASE 的值为0x23e00000 同样后面的代码 把_start、__bss_start、_end的值放到相应的内存地址处
reset:
/*
* set the cpu to SVC32 mode and IRQ & FIQ disable
*/
@;mrs r0,cpsr
@;bic r0,r0,#0x1f
@;orr r0,r0,#0xd3
@;msr cpsr,r0
msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
reset:设置为SVC模式,禁止irq、frq中断;
4:第四段代码
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
cpu_init_crit:
#ifndef CONFIG_EVT1
#if 0
bl v7_flush_dcache_all
#else
bl disable_l2cache
mov r0, #0x0 @
mov r1, #0x0 @ i
mov r3, #0x0
mov r4, #0x0
lp1:
mov r2, #0x0 @ j
lp2:
mov r3, r1, LSL #29 @ r3 = r1(i) <<29
mov r4, r2, LSL #6 @ r4 = r2(j) <<6
orr r4, r4, #0x2 @ r3 = (i<<29)|(j<<6)|(1<<1)
orr r3, r3, r4
mov r0, r3 @ r0 = r3
bl CoInvalidateDCacheIndex
add r2, #0x1 @ r2(j)++
cmp r2, #1024 @ r2 < 1024
bne lp2 @ jump to lp2
add r1, #0x1 @ r1(i)++
cmp r1, #8 @ r1(i) < 8
bne lp1 @ jump to lp1
bl set_l2cache_auxctrl
bl enable_l2cache
#endif
#endif
因为在x210_sd.h中定义了 CONFIG_EVT1,所以这段代码是不执行的;
1 bl disable_l2cache
2
3 bl set_l2cache_auxctrl_cycle
4
5 bl enable_l2cache
6
7 /*
8 * Invalidate L1 I/D
9 */
10 mov r0, #0 @ set up for MCR
11 mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
12 mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
13
14 /*
15 * disable MMU stuff and caches
16 */
17 mrc p15, 0, r0, c1, c0, 0
18 bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
19 bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
20 orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
21 orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
22 mcr p15, 0, r0, c1, c0, 0
上面这段代码是作用是cpu 的初始化
disable_l2cache对应代码如下:
disable_l2cache:
mrc p15, 0, r0, c1, c0, 1
bic r0, r0, #(1<<1)
mcr p15, 0, r0, c1, c0, 1
mov pc, lr
set_l2cache_auxctrl_cycle:设置L2cache
.global set_l2cache_auxctrl_cycle
set_l2cache_auxctrl_cycle:
mrc p15, 1, r0, c9, c0, 2
bic r0, r0, #(0x1<<29)
bic r0, r0, #(0x1<<21)
bic r0, r0, #(0x7<<6)
bic r0, r0, #(0x7<<0)
mcr p15, 1, r0, c9, c0, 2
mov pc,lr
enable_l2cache:使能L2 cache
enable_l2cache:
mrc p15, 0, r0, c1, c0, 1
orr r0, r0, #(1<<1)
mcr p15, 0, r0, c1, c0, 1
mov pc, lr
第五段代码:读取启动信息
/读取ompin引脚寄存器的值放入r0中 ,留下bit2—bit6中的值,并赋值给r2;
1 /* NAND BOOT */ 2 cmp r2, #0x0 @ 512B 4-cycle 3 moveq r3, #BOOT_NAND 4 5 cmp r2, #0x2 @ 2KB 5-cycle 6 moveq r3, #BOOT_NAND 7 8 cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC 9 moveq r3, #BOOT_NAND 10 11 cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC 12 moveq r3, #BOOT_NAND 13 14 cmp r2, #0x8 @ OneNAND Mux 15 moveq r3, #BOOT_ONENAND 16 17 /* SD/MMC BOOT */ 18 cmp r2, #0xc 19 moveq r3, #BOOT_MMCSD 20 21 /* NOR BOOT */ 22 cmp r2, #0x14 23 moveq r3, #BOOT_NOR 24 25 /* Uart BOOTONG failed */ 26 cmp r2, #(0x1<<4) 27 moveq r3, #BOOT_SEC_DEV
* Read booting information */
ldr r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
通过判断r2中的值,来确定是从哪里启动的,如我们从SD卡启动的话r2中的值应该为0xc,然后把#BOOT_MMCSD 赋值给r3
#define INF_REG_BASE 0xE010F000
#define INF_REG3_OFFSET 0x0c
把BOOT_MMCSD这个值放入寄存器 0xE010F00C中。
1 ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */
2 sub sp, sp, #12 /* set stack */
3 mov fp, #0
4
5 bl lowlevel_init /* go setup pll,mux,memory */
6 /* To hold max8698 output before releasing power on switch,
7 * set PS_HOLD signal to high
8 */
9 ldr r0, =0xE010E81C /* PS_HOLD_CONTROL register */
10 ldr r1, =0x00005301 /* PS_HOLD output high */
11 str r1, [r0]
12
13 /* get ready to call C functions */
14 ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
15 sub sp, sp, #12
16 mov fp, #0 /* no previous frame, so fp=0 */
这段代码是设置栈,因为接下来调用lowlevel_init函数中还要调用其他函数,lr中保存pc的话,在调用其他函数就无法保存pc了,所以先要把lr入栈,调用完函数以后在出栈然后在pop {pc}
lowlevel_init 函数:如下
1 _TEXT_BASE:
2 .word TEXT_BASE
3
4 .globl lowlevel_init
5 lowlevel_init:
6 push {lr} //先把lr入栈
7
8 /* check reset status */
9
10 ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) //#define ELFIN_CLOCK_POWER_BASE 0xE0100000 #define RST_STAT_OFFSET 0xa000
11 ldr r1, [r0] //这个寄存器为reset状态寄存器,
12 bic r1, r1, #0xfff6ffff
13 cmp r1, #0x10000
14 beq wakeup_reset_pre
15 cmp r1, #0x80000
16 beq wakeup_reset_from_didle
17
18 /* IO Retention release */ //I/O相关的一些初始化
19 ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
20 ldr r1, [r0]
21 ldr r2, =IO_RET_REL
22 orr r1, r1, r2
23 str r1, [r0]
24
25 /* Disable Watchdog */ //关看门狗
26 ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
27 mov r1, #0
28 str r1, [r0]
29
30 /* SRAM(2MB) init for SMDKC110 */ // #define ELFIN_GPIO_BASE 0xE0200000
31 /* GPJ1 SROM_ADDR_16to21 */ //SRAM和SROM相关的一些初始化
32 ldr r0, =ELFIN_GPIO_BASE
33
34 ldr r1, [r0, #GPJ1CON_OFFSET] // #define GPJ1CONPDN_OFFSET 0x270 寄存器为0xE0200270
35 bic r1, r1, #0xFFFFFF
36 ldr r2, =0x444444
37 orr r1, r1, r2
38 str r1, [r0, #GPJ1CON_OFFSET]
39
40 ldr r1, [r0, #GPJ1PUD_OFFSET]
41 ldr r2, =0x3ff
42 bic r1, r1, r2
43 str r1, [r0, #GPJ1PUD_OFFSET]
44
45 /* GPJ4 SROM_ADDR_16to21 */
46 ldr r1, [r0, #GPJ4CON_OFFSET]
47 bic r1, r1, #(0xf<<16)
48 ldr r2, =(0x4<<16)
49 orr r1, r1, r2
50 str r1, [r0, #GPJ4CON_OFFSET]
51
52 ldr r1, [r0, #GPJ4PUD_OFFSET]
53 ldr r2, =(0x3<<8)
54 bic r1, r1, r2
55 str r1, [r0, #GPJ4PUD_OFFSET]
56
57
58 /* CS0 - 16bit sram, enable nBE, Byte base address */
59 ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
60 mov r1, #0x1
61 str r1, [r0]
62
63 /* PS_HOLD pin(GPH0_0) set to high */ //供电锁存 同裸机中的一致
64 ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
65 ldr r1, [r0]
66 orr r1, r1, #0x300
67 orr r1, r1, #0x1
68 str r1, [r0]
69
70 /* when we already run in ram, we don‘t need to relocate U-Boot.
71 * and actually, memory controller must be configured before U-Boot
72 * is running in ram.
73 */
74 ldr r0, =0xff000fff //这几行的代码是判断现在程序是在内存中执行还是在SRAM中执行,
75 bic r1, pc, r0 /* r0 <- current base addr of code */ //如果在内存中执行的话不用初始化时钟和sdram直接跳转到1去执行
76 ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
77 bic r2, r2, r0 /* r0 <- current base addr of code */
78 cmp r1, r2 /* compare r0, r1 */
79 beq 1f /* r0 == r1 then skip sdram init */
80
81 /* init system clock */
82 bl system_clock_init //如果没有在内存中执行首先要初始化时钟
83
84 /* Memory initialize */
85 bl mem_ctrl_asm_init //然后初始化内存
86
87 1:
88 /* for UART */
89 bl uart_asm_init //初始化uart在结尾输出了一个‘O‘字符
90
91 bl tzpc_init //trust zone的初始化
92
93 #if defined(CONFIG_ONENAND)
94 bl onenandcon_init
95 #endif
96
97 #if defined(CONFIG_NAND)
98 /* simple init for NAND */
99 bl nand_asm_init
100 #endif
101
102 /* check reset status */
103
104 ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
105 ldr r1, [r0]
106 bic r1, r1, #0xfffeffff
107 cmp r1, #0x10000
108 beq wakeup_reset_pre
109
110 /* ABB disable */
111 ldr r0, =0xE010C300
112 orr r1, r1, #(0x1<<23)
113 str r1, [r0]
114
115 /* Print ‘K‘ */ //UTXH_OFFSET寄存器中写入0x4b4b4b4b即输出K
116 ldr r0, =ELFIN_UART_CONSOLE_BASE //所以说输出OK就说明lowlevel_init初始化成功
117 ldr r1, =0x4b4b4b4b
118 str r1, [r0, #UTXH_OFFSET]
119
120 pop {pc} //pc 出栈
第六段代码
1 ldr r0, =0xE010E81C /* PS_HOLD_CONTROL register */ //电源锁存
2 ldr r1, =0x00005301 /* PS_HOLD output high */
3 str r1, [r0]
4
5 /* get ready to call C functions */
6 ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */ //重新这是栈,因为初始化了内存以后执行代码就要在内存中执行代码了
7 sub sp, sp, #12
8 mov fp, #0 /* no previous frame, so fp=0 */ //设置的地址为0x23e00000因为arm的栈为慢减栈,所以是向下增长的
9
10 /* when we already run in ram, we don‘t need to relocate U-Boot.
11 * and actually, memory controller must be configured before U-Boot
12 * is running in ram.
13 */
14 ldr r0, =0xff000fff //判断现在是否在内存中执行,如果在内存中执行的话就不需要重定位了
15 bic r1, pc, r0 /* r0 <- current base addr of code */ //如果不是在内存中执行的话则需要重定位
16 ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
17 bic r2, r2, r0 /* r0 <- current base addr of code */
18 cmp r1, r2 /* compare r0, r1 */
19 beq after_copy /* r0 == r1 then skip flash copy */
下面这段代码的作用是判断启动是从哪里启动的,然后采用相应的复制函数把uboot复制到内存中;
1 #if defined(CONFIG_EVT1) //定义了 CONFIG_EVT1的值为1
2 /* If BL1 was copied from SD/MMC CH2 */
3 ldr r0, =0xD0037488
4 ldr r1, [r0] //把内存0xD0037488中的值放入r1中,r2的值为0xEB200000
5 ldr r2, =0xEB200000
6 cmp r1, r2
7 beq mmcsd_boot //如果相等则跳转到 mmcsd_boot中,这个函数是从sd卡中复制内容到内存中,代码执行结果直接跳转
8 #endif
9
10 ldr r0, =INF_REG_BASE //在上面的代码中设置了INF_REG3_OFFSET中的值,对应的位SD启动的值
11 ldr r1, [r0, #INF_REG3_OFFSET] //所以这段代码的作用也是跳转到mmcsd_boot函数中
12 cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
13 beq nand_boot
14 cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */
15 beq onenand_boot
16 cmp r1, #BOOT_MMCSD
17 beq mmcsd_boot
18 cmp r1, #BOOT_NOR
19 beq nor_boot
20 cmp r1, #BOOT_SEC_DEV
21 beq mmcsd_boot
22
23 nand_boot:
24 mov r0, #0x1000
25 bl copy_from_nand
26 b after_copy
27
28 onenand_boot:
29 bl onenand_bl2_copy
30 b after_copy
31
32 mmcsd_boot: //上面一段代码的作用就是判断启动方式,然后跳转到相应的复制函数中
33 #if DELETE //因为我们采用的是sd卡启动,所以最后进入了mmcsd_boot函数中
34 ldr sp, _TEXT_PHY_BASE
35 sub sp, sp, #12
36 mov fp, #0
37 #endif //函数的作用是跳转到 movi_bl2_copy函数中,把uboot从sd卡复制到内存地址中
38 bl movi_bl2_copy
39 b after_copy //然后在跳转到 after_copy函数中;
下面分析一下 movi_bl2_copy函数,和裸机中的重定位是一致的。
1 extern raw_area_t raw_area_control;
2
3 typedef u32(*copy_sd_mmc_to_mem)
4 (u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init);
5
6 void movi_bl2_copy(void)
7 {
8 ulong ch;
9 #if defined(CONFIG_EVT1)
10 ch = *(volatile u32 *)(0xD0037488);
11 copy_sd_mmc_to_mem copy_bl2 =
12 (copy_sd_mmc_to_mem) (*(u32 *) (0xD0037F98));
13
14 #if defined(CONFIG_SECURE_BOOT)
15 ulong rv;
16 #endif
17 #else
18 ch = *(volatile u32 *)(0xD003A508);
19 copy_sd_mmc_to_mem copy_bl2 =
20 (copy_sd_mmc_to_mem) (*(u32 *) (0xD003E008));
21 #endif
22 u32 ret;
23 if (ch == 0xEB000000) {
24 ret = copy_bl2(0, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
25 CFG_PHY_UBOOT_BASE, 0);
26
27 #if defined(CONFIG_SECURE_BOOT)
28 /* do security check */
29 rv = Check_Signature( (SecureBoot_CTX *)SECURE_BOOT_CONTEXT_ADDR,
30 (unsigned char *)CFG_PHY_UBOOT_BASE, (1024*512-128),
31 (unsigned char *)(CFG_PHY_UBOOT_BASE+(1024*512-128)), 128 );
32 if (rv != 0){
33 while(1);
34 }
35 #endif
36 }
37 else if (ch == 0xEB200000) {
38 ret = copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
39 CFG_PHY_UBOOT_BASE, 0);
40
41 #if defined(CONFIG_SECURE_BOOT)
42 /* do security check */
43 rv = Check_Signature( (SecureBoot_CTX *)SECURE_BOOT_CONTEXT_ADDR,
44 (unsigned char *)CFG_PHY_UBOOT_BASE, (1024*512-128),
45 (unsigned char *)(CFG_PHY_UBOOT_BASE+(1024*512-128)), 128 );
46 if (rv != 0) {
47 while(1);
48 }
49 #endif
50 }
51 else
52 return;
53
54 if (ret == 0)
55 while (1)
56 ;
57 else
58 return;
59 }
第七段代码:最后一段代码,作用是配置MMU、重新设置栈、清bss、跳转到start_armboot函数中去执行BL2阶段;
下面详细分析一下代码:初始化mmu这一段请看另一篇专门关于mmu的博客
1 after_copy:
2
3 #if defined(CONFIG_ENABLE_MMU)
4 enable_mmu:
5 /* enable domain access */
6 ldr r5, =0x0000ffff
7 mcr p15, 0, r5, c3, c0, 0 @load domain access register
8
9 /* Set the TTB register */
10 ldr r0, _mmu_table_base
11 ldr r1, =CFG_PHY_UBOOT_BASE
12 ldr r2, =0xfff00000
13 bic r0, r0, r2
14 orr r1, r0, r1
15 mcr p15, 0, r1, c2, c0, 0
16
17 /* Enable the MMU */
18 mmu_on:
19 mrc p15, 0, r0, c1, c0, 0
20 orr r0, r0, #1
21 mcr p15, 0, r0, c1, c0, 0
22 nop
23 nop
24 nop
25 nop
26 #endif
27
28 skip_hw_init: //重新设置栈:0x23e00000 + 2M - 0x1000 = 0x23e00000 + 0x200000 - 0x1000 = 0x240FF000
29 /* Set up the stack */ //最后设置sp = 0x240FF000
30 stack_setup:
31 #if defined(CONFIG_MEMORY_UPPER_CODE)
32 ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
33 #else
34 ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
35 sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
36 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
37 #if defined(CONFIG_USE_IRQ)
38 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
39 #endif
40 sub sp, r0, #12 /* leave 3 words for abort-stack */
41
42 #endif
43
44 clear_bss: //清bss代码同裸机中一样
45 ldr r0, _bss_start /* find start of bss segment */
46 ldr r1, _bss_end /* stop here */
47 mov r2, #0x00000000 /* clear */
48
49 clbss_l:
50 str r2, [r0] /* clear loop... */
51 add r0, r0, #4
52 cmp r0, r1
53 ble clbss_l
54
55 ldr pc, _start_armboot //跳转执行bl2阶段start_armboot函数。
56
57 _start_armboot:
58 .word start_armboot
总结:
uboot的第一阶段到这里算是执行完成了,uboot在第一阶段中做的事包括:
1:建立异常向量表
2:cpu的初始化
3:判断启动方式
4:初始化 i/o、关看门狗、初始化SROM、初始化时钟、初始化sdram、初始化uart、初始化trust zone
5:供电锁存、重定位、MMU配置、清bss、跳转到star_armboot执行BL2阶段
原文:http://www.cnblogs.com/biaohc/p/6368341.html