u-boot 1.1.6版本学习笔记——Start.s分析
来源:互联网 发布:钉钉软件怎么样 编辑:程序博客网 时间:2024/06/07 07:37
首先直接看第一段代码:
.globl _start_start: b 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
.globl 和.globol是一样的,它的作用是声明一个符号,而这个符号可以被其他文档引用,相当于声明了一个全局变量。
那么这里就是声名_start,_start可以被其他文档使用。
_start就是整个start.S的最开始,即整个uboot的代码的开始。后面的冒号,表示_start是一个标号,它的值,就是代码的位置,这里,是代码的最开始,所以其值=0。
b reset :表示跳转到 reset 执行,并且不返回
ldr: LDR伪指令—–大范围的地址读取
详情请看:1,http://blog.chinaunix.net/uid-28458801-id-3753775.html
代码是:
ldr pc, _undefined_instruction
我们先看大体一下_undefined_instruction是什么:
_undefined_instruction: .word undefined_instruction
.word: word是字的意思,.word就是分配一段字内存单元,这里相当于分配了一个4字节的内存单元,里面放的是undefined_instruction的值,这个undefined_instruction跟 _start一样,是标号,它的值就是代码所在的位置,所以ldr pc, _undefined_instruction
就是把,undefined_instruction代码的地址放到PC中,那样,发生未定义异常的时候,就可以通过PC进入undefined_instruction对应的代码段处理了。
接下去的代码:.balignl 16,0xdeadbeef
意思:接下的代码,也就是
_TEXT_BASE: .word TEXT_BASE
它是需要16字节对齐的,如果不满足16个字节,那么就用0xdeadbeef补充。
TEXT_BASE是一个word类型的变量,它代表的是整个u-boot的基地址,是在\board\xx\config.mk中定义的,makefile中会用到,后面再分析,我们假设 TEXT_BASE = 0x33D00000
.globl _bss_start_bss_start: .word __bss_start.globl _bss_end_bss_end: .word _end
_bss_start和_bss_end都叧是两个标号,对应着此处的地址。
而两个地址里面分别存放的值是__bss_start和_end,这两个的值,定义在 \board\xx\u-boot.lds 中这是一个连接脚本。
而关于_bss_start和_bss_end定义为.glogl即全局变量,是因为uboot的其他源码中要用到这两个变量。
#ifdef CONFIG_USE_IRQ/* IRQ stack memory (calculated at run-time) */.globl IRQ_STACK_STARTIRQ_STACK_START: .word 0x0badc0de/* IRQ stack memory (calculated at run-time) */.globl FIQ_STACK_STARTFIQ_STACK_START: .word 0x0badc0de#endif
IRQ_STACK_START跟FIQ_STACK_START两个全局变量,当前的值是没有意义的,因为一开始初始化,并不知道堆栈的准确地址,这是留给以后再修改的。
/* IRQ stack memory (calculated at run-time) */
/* * the actual reset code *//*对 mrs 和msr 有疑问的请看:http://blog.chinaunix.net/uid-28458801-id-4083956.html*/reset: /* * set the cpu to SVC32 mode.设置cpu为 SVC模式。 * 如果对arm的个模式有疑问的请看:1,http://blog.chinaunix.net/uid-28458801-id-3788554.html * 2,http://blog.chinaunix.net/uid-28458801-id-3494646.html */ mrs r0,cpsr /* 传送CPSR的内容到R0 */ bic r0,r0,#0x1f /* 清除r0的bit[4:0]位 */ orr r0,r0,#0xd3 /* 把r0的bit[7:6]和bit[4]和bit[2:0]置为1 */ 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)# define pWTCON 0x53000000# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */# define INTSUBMSK 0x4A00001C# define CLKDIVN 0x4C000014 /* clock divisor register */#endif
首先,定义一些看门狗相关的寄存器地址。
关看门狗操作很简单,就是往pWTCON寄存器里写0
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) ldr r0, =pWTCON mov r1, #0x0 str r1, [r0]
然后就是关闭所有中断,方法:设置INTMSK寄存器
mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0]# endif
#if 0 /* 不编译*/ /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0]#endif
接下来:
/* * we do sys-critical inits only at reboot, * not when booting from ram! */#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit#endif
// B 转移指令,跳转到指令中指定的目的地址,即不返回
// BL 带链接的转移指令,像B 相同跳转并把转移后面紧接的一条指令地址保存到链接寄存器LR(R14)中,以此来完成子程式的调用,即能返回。
// 该语句首先调用cpu_init_crit 进行CPU 的初始化
那我们在看一下,#ifndef CONFIG_SKIP_LOWLEVEL_INIT
这句话的作用是什么,其实,注释写了,因为bl cpu_init_crit是底层操作
用这句话,来使得bl这句代码,只在u-boot在ram中运行的时候,才能执行,在外部的sdram就不执行了。
接下来分析cpu_init_crit 这部分代码:
#ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_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 */
第一个CONFIG_SKIP_LOWLEVEL_INIT 宏定义,在2440上是没用定义的,因此会执行这部分代码。
前面两部分很简单,就是操作cp15协处理器,invalidate icache, dcache,这里有个我的理解,看第二部份的代码注释
@set bit 12 (I) I-Cache
即,使能了icache,我一开始觉得跟之前一段代码invalidate cache是冲突的,但后来想想可能是这样:invalidate跟使能是可以同时的,使能是让cache可以用,以后需要使用cache时,不需要再在使能。invalidate只是让cache里的数据无效,好像是让cache每一段的最后一位都为0。
然后看第三部分:
这一部分,一开始mov ip, lr
这一句,是将lr保存起来,ip好像就是r11。为什么需要保存呢?因为在下面调用的lowlevel_init的子程序里,还需要使用lr,即还需要再调用子程序,所以将lr先保存,防止后面被修改。那这个lowlevel_init是干什么用的呢?
我们知道,u-boot很大,而2440内部只用4k的sram,所以,在这4k的代码中,你就要为后面的u-boot的代码存放的位置做好准备,这里,我们是将后面的代码放到外部的sdram中,那就需要把sdram初始化,初始化的意思就是让我2440的芯片可以管理外部的sdram。在直白点,就是配置2440的存储管理寄存器。
好,那我们就来看看这部分的代码:
首先第一部分是一些宏定义(看2440手册的第5章):
#define BWSCON 0x48000000
上面BWSCON 寄存器的的地址
#define DW8 (0x0)#define DW16 (0x1)#define DW32 (0x2)#define WAIT (0x1<<2)#define UBLB (0x1<<3)#define B1_BWSCON (DW32)#define B2_BWSCON (DW16)#define B3_BWSCON (DW16 + WAIT + UBLB)#define B4_BWSCON (DW16)#define B5_BWSCON (DW16)#define B6_BWSCON (DW32)#define B7_BWSCON (DW32)
这上面一开始是BWSCON 里面一些位设置的宏,接下来是对BANKn的数据总线宽度与功能的宏定义,对于BANK3因为要接网卡,网卡有wait信号和没有wait信号两种,所以这里有些特别。
/* BANK0CON */#define B0_Tacs 0x0 /* 0clk */#define B0_Tcos 0x0 /* 0clk */#define B0_Tacc 0x7 /* 14clk */#define B0_Tcoh 0x0 /* 0clk */#define B0_Tah 0x0 /* 0clk */#define B0_Tacp 0x0#define B0_PMC 0x0 /* normal *//* BANK1CON */#define B1_Tacs 0x0 /* 0clk */#define B1_Tcos 0x0 /* 0clk */#define B1_Tacc 0x7 /* 14clk */#define B1_Tcoh 0x0 /* 0clk */#define B1_Tah 0x0 /* 0clk */#define B1_Tacp 0x0#define B1_PMC 0x0#define B2_Tacs 0x0#define B2_Tcos 0x0#define B2_Tacc 0x7#define B2_Tcoh 0x0#define B2_Tah 0x0#define B2_Tacp 0x0#define B2_PMC 0x0#define B3_Tacs 0x0 /* 0clk */#define B3_Tcos 0x3 /* 4clk */#define B3_Tacc 0x7 /* 14clk */#define B3_Tcoh 0x1 /* 1clk */#define B3_Tah 0x0 /* 0clk */#define B3_Tacp 0x3 /* 6clk */#define B3_PMC 0x0 /* normal */#define B4_Tacs 0x0 /* 0clk */#define B4_Tcos 0x0 /* 0clk */#define B4_Tacc 0x7 /* 14clk */#define B4_Tcoh 0x0 /* 0clk */#define B4_Tah 0x0 /* 0clk */#define B4_Tacp 0x0#define B4_PMC 0x0 /* normal */#define B5_Tacs 0x0 /* 0clk */#define B5_Tcos 0x0 /* 0clk */#define B5_Tacc 0x7 /* 14clk */#define B5_Tcoh 0x0 /* 0clk */#define B5_Tah 0x0 /* 0clk */#define B5_Tacp 0x0#define B5_PMC 0x0 /* normal */
这上面的这段代码是对每一个BANK的BANKCON进行配置,这里要根据SRAM手册来配置对应的值,否则不能正常进行使用,在0~5中大多是对一些时序与Page模式的配置,只对应SRAM的要求就可以了。
#define B6_MT 0x3 /* SDRAM */#define B6_Trcd 0x1#define B6_SCAN 0x1 /* 9bit */#define B7_MT 0x3 /* SDRAM */#define B7_Trcd 0x1 /* 3clk */#define B7_SCAN 0x1 /* 9bit *//* REFRESH parameter */#define REFEN 0x1 /* Refresh enable */#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */#define Trp 0x0 /* 2clk */#define Trc 0x3 /* 7clk */#define Tchr 0x2 /* 3clk */#define REFCNT 1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
然后,上面的代码,就是配置B6,B7,这两个块是可以作为ROM,SRAM,SDRAM等存储器的。
还有刷新控制寄存器的配置,要根据SDRAM的具体参数来配置,移植需要注意。
好了,就下来就是lowlevel_init了:
_TEXT_BASE: .word TEXT_BASE
这里是获得代码段的起始地址,我的是0x33F80000(在board/xxx/config.mk中)
接下来是这段代码:
ldr r0, =SMRDATA ldr r1, _TEXT_BASE sub r0, r0, r1 ldr r1, =BWSCON /* Bus Width Status Controller */ add r2, r0, #13*40: ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne 0b
首先看第一句:ldr r0, =SMRDATA
SMRDATA是在下面定义的:
SMRDATA: .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) .word 0x32 .word 0x30 .word 0x30
SMRDATA表示的是这13个寄存器的值存放的开始地址,值为0x33F8xxxx,处于内存中,这一句就是将其值加载到r0中。
接下来的分析,我转的一个人的:
_TEXT_BASE: .word TEXT_BASE //这里是获得代码段的起始地址,我的是0x33F80000(在board/xxx/config.mk中 //可到找到“TEXT_BASE=0x33F80000”.globl lowlevel_init //这里相当于定义一个全局的lowlevel_init以方便调用lowlevel_init: /* memory control configuration */ /* make r0 relative the current location so that it */ /* reads SMRDATA out of FLASH rather than memory ! */ ldr r0, =SMRDATA //SMDATA表示这13个寄存器的值存放的开始地址,值为0x33F8xxxx,处于内 //存中,这一句的作用是把其值加载到r0中 ldr r1, _TEXT_BASE //把代码的起始地址(0x33F80000)加载到r1中 sub r0, r0, r1 //r0减去r1其结果存入r0,也即SMDATA中的起始地址0x33F8xxxx减去 //0x33F80000,其结果就是13个寄存器的值在NOR Flash存放的开始地址ldr r1, =BWSCON /* Bus Width Status Controller */ //存储控制器的基地址 add r2, r0, #13*4 //在计算出来的存放地址加上#13*4,然后其结果保存在r2中 //13个寄存器,每个寄存器占4个字节0: ldr r3, [r0], #4 //内存中r0的值加载到r3中,然后r0加4,即下一个寄存器的 str r3, [r1], #4 //读出寄存器的值保存到r1中,然后r1也偏移4 cmp r2, r0 //比较r0与r2的值,如果不等继续返回0:执行,也即13个寄存器的值 //是否读完 bne 0b /* everything is fine now */ mov pc, lr //程序跳转,返回到cpu_init_crit中
到这里,lowlevel_init就结束了。
上面结束后,程序就回到之前调用cpu_init_crit的地方,继续往下运行。
代码如下:
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
/* 这一段程序的目的是判断 _start 和 _TEXT_BASE 是否相同,如果程序开始执行的地方就位于程序的链接地址的话,就不用重定位了。如果是nor启动,_start即为代码的最开始,相对的0的位置;如果重新relocate代码之后,_start就是在/board/smdk2410/config.mk中定义的TEXT_BASE = 0x33F80000位置,即_start = TEXT_BASE = 0x33F80000,这样的话就不用重定位代码了。 */
ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ add r2, r0, r2 /* r2 <- source end address */
能执行到这,就说明_start 和 _TEXT_BASE 是不相同的。重定位代码就是把代码从源地址拷贝到目的地址,拷贝多大的问题。源就是r0中的值,为_start,目的就是r1中的值,为_TEXT_BASE,程序拷贝多大呢?从链接脚本里面可以看出来,用(_bss_start - _armboot_start)即为程序的大小。 为什么这么大,待思考。
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 */
设置栈,在经过上面的重定位代码以后, r0 = TEXT_BASE = 0x33F80000,去除分给malloc堆的空间以后,再取出一部分空间给bdinfo结构体,再留出12字节给终止异常的空间,然后将这个值赋给sp,即设置好了栈。
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
清BSS段,BSS段的大小为(_bss_end - _bss_start),将这个空间都清为0。
ldr pc, _start_armboot_start_armboot: .word start_armboot
将_start_armboot的值赋给pc,即调用start_armboot函数,即跳到uboot的第二阶段。
总结一下流程:
1,设置svc模式;
2,关看门狗;
3,屏蔽中断;
4,invalidate cache,关mmu
5,初始化存储管理器
6,代码重定位
7,设置栈
8,启动第二段代码
- u-boot 1.1.6版本学习笔记——Start.s分析
- U-Boot学习笔记(二):U-Boot--arm--Start.S的分析
- U-BOOT start.S分析
- u-boot分析--start.s
- u-boot start.s 分析
- U-BOOT start.S分析
- U-Boot源代码阅读笔记(一) —— 对start.S的分析
- U-Boot源代码阅读笔记(一) —— 对start.S的分析
- u-boot-1.1.6 start.S代码笔记
- 【Bootloader】u-boot 1.1.6 start.S 代码学习
- u-boot 1.1.6 start.S 代码学习
- u-boot 1.1.6 start.S 代码学习
- u-boot-1.1.6源代码---start.S
- U-Boot源码——start.S
- u-boot 学习记录 (三) 源码分析(start.S)
- U-boot-13.0-rc3 start.s 分析
- s3c2410 u-boot源码分析start.S
- s3c2410 u-boot源码分析start.S
- Linux的文件系统和文件类型
- 彻底明白如何设置minSdkVersion和targetSdkVersion
- 第四周《C语言及程序设计》实践项目14 开发一个电子词典
- Grafana+InfluxDB+Collectd搭建企业级监控方案一
- HTTP请求/响应报文结构
- u-boot 1.1.6版本学习笔记——Start.s分析
- 安装Ambari和部署HDP报错总结
- pt-show-grant使用
- 用java实现HTTPS工作原理的例子
- Opencv2.4.8在 VS2010平台上安装配置图解
- SparkSQL读取Hive中的数据
- android的Environment类
- 同一个android应用下面不同包下面activity 互相调用
- 第41篇 WebRTC(四)