回味经典——uboot1.1.6 之 第一阶段
转自:http://blog.csdn.net/lizuobin2/article/details/52054293
最近打算移植一个比较新的 uboot 到开发板,回想起来上一次移植 uboot1.1.6 已经差不多是一年前了,手头保留了一些当时移植分析时的笔记,但是没有归纳梳理,在移植新版 uboot 之前,再来回味一下经典。本文重点在于分析 uboot 启动流程以及 uboot 自身的细节,比如栈空间的划分、如何设置 tag 、如何添加一个自定义命令等。但是不涉及基本的硬件驱动的分析,比如内存初始化、时钟初始化、mmu 、nandflash
等等这些细节不是我们的重点。
一、链接脚本
uboot1.1.6 的链接脚本 u-boot.lds 位于 u-boot-1.1.6\board\smdk2410 目录下:
- ENTRY(_start)
- SECTIONS
- {
- . = 0x00000000;
- . = ALIGN(4);
- .text :
- {
- cpu/arm920t/start.o (.text)
- *(.text)
- }
- . = ALIGN(4);
- .rodata : { *(.rodata) }
- . = ALIGN(4);
- .data : { *(.data) }
- . = ALIGN(4);
- .got : { *(.got) }
- . = .;
- __u_boot_cmd_start = .;
- .u_boot_cmd : { *(.u_boot_cmd) }
- __u_boot_cmd_end = .;
- . = ALIGN(4);
- __bss_start = .;
- .bss : { *(.bss) }
- _end = .;
- }
链接地址为 0 ?显然不应该,实际编译的时候执行的大概是这样一条语句:
arm-Linux-ld -Bstatic
-T u-boot.lds -Ttext 0x33F80000 start.o ...
0x33F80000 在 board/smdk2410/config.mk 中定义,为 TEXT_BASE = 0x33F80000 (链接地址)
整个 uboot 的入口 _start 包含在 cpu/arm920t/start.S 中
二、第一阶段
uboot 的第一阶段主要工作是作基本的初始化工作,例如关看门狗、初始化时钟、初始化 sdram 以及代码重定位,为第二阶段做准备。这里的代码都是没有经过移植的源代码~!
1、设置异常向量
- .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
- _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
- .balignl 16,0xdeadbeef
第一条 b reset ,因为刚开始运行时代码都是在片内 sram 里,我们在 sram 里调来跳去的话就需要用位置无关码,那么 b 就是最佳选择,因为它是相对跳转。
ldr pc, _undefined_instruction
_undefined_instruction:.word undefined_instruction
感觉真是在卖弄,两条指令连起来的结果就是,CPU 会跳转到 undefined_instruction 链接地址处去执行(sdram里)。
那么其实,一条 ldr pc,=undefined_instruction 就够了,它是位置有关码,绝对跳转。
或许,uboot 的作者别有用意我没看透,不知道这是不是个伏笔。在u-boot2015里,就只有一个 reset 一个异常入口了。
2、进入管理模式
- reset:
- /*
- * set the cpu to SVC32 mode
- */
- mrs r0,cpsr
- bic r0,r0,#0x1f
- orr r0,r0,#0xd3
- msr cpsr,r0
ARM每种工作模式除R0~R15共16个寄存器外,还有第17个寄存器CPSR,叫做 当前程序状态寄存器,CPSR中一些位被用于标识各种状态,一些位被用于标识当前出于什么工作模式。
有时候我们会碰到 CPSR_C ,它其实就是 CPSR 的低 8 位而已。
I:1-禁止irq中断 0-允许irq中断
F:1-禁止fiq中断 1-允许fiq中断
T:1-Thumb 0-arm 指令集
M0-M4 : 工作模式
说了这么多,前边两条指令,先将 cpsr 低 5位 清零,然后或上 1101 0011B
禁止了 irq 和 fiq 中断,工作在 arm 指令集,管理模式。
3、关看门狗
- #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
- ldr r0, =pWTCON
- mov r1, #0x0
- str r1, [r0]
4、屏蔽中断
- /*
- * 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]
- # endif
前边通过 cpsr 禁止 irq 和 fiq 使 cpu 不接受来自中断控制器的中断请求,而这里通过中断屏蔽使中断发生时,中断控制寄存器自身就不会上报给 cpu 双保险。
5、设置时钟
- /* FCLK:HCLK:PCLK = 1:2:4 */
- /* default FCLK is 120 MHz ! */
- ldr r0, =CLKDIVN
- mov r1, #3
- str r1, [r0]
6、关 I/D cache 关 TLB
- /*
- * 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 */
协处理器 p15 在2410的数据手册附录有介绍
或者参考:http://blog.sina.com.cn/s/blog_858820890102v1gc.html
7、关 mmu
- /*
- * 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
这里主要涉及 P15 的 C1寄存器,用到的各位:
8、初始化 sdram 控制器
- .globl 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
- ldr r1, _TEXT_BASE
- sub r0, r0, r1
- ldr r1, =BWSCON /* Bus Width Status Controller */
- add r2, r0, #13*4
- 0:
- ldr r3, [r0], #4
- str r3, [r1], #4
- cmp r2, r0
- bne 0b
- /* everything is fine now */
- mov pc, lr
- .ltorg
- /* the literal pools origin */
- 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
写裸机代码的入门操作,初始化 sdram 寄存器。
9、代码重定位
- 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
- 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_loop:
- 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
adr 位置无关码,获取_start实际当前位于的地方,_TEXT_BASE 为 0x33f80000 ,这里判断的是代码是否直接运行在 sdram 里了,如果是就不需要重定位了。
拷贝范围:_start 至 _bss_start 前,拷贝到 0x33f80000 处。
33f80048 <_bss_start>:
33f80048: 33fb064c
0x33fb064c - 0x33f80000 > 192K ,什么意思呢?整个 uboot 除了 bss 段 > 4k,如果是 nandflash 启动的话需要从 nandflash 里读取 uboot 到内核,而这里是直接从 0 地址开始读,并读取 > 193k 的东西,显然 uboot 运行在 Norflash 才可以。默认
uboot 不支持 nandflash 启动。
10、设置栈
- 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 */
- 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
TEXT_BASE = 0x33F80000 其它的宏在 Smdk2410.h (include\configs):
- #define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024)
- #define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
- #define CFG_GBL_DATA_SIZE 128
- #define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */
- #define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */
- 0x34000000:
- (512K) 存放 uboot
- 0x33F80000: <span style="white-space:pre"> </span>TEXT_BASE
- (64K+128K == 192K) <span style="white-space:pre"> </span>mallo区
- 0x33F50000:
- (128bytes) global data区,后边会提到主要放的gd、bd全局结构体
- 0x33F4FF80:
- (4*1024*2) IRQ+FIQ的栈
- 0x33F4DF80:
- (12byte) abort-stack,栈溢出
- 0x33F4DF74: sp
11、清 BSS 段
- 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: .word start_armboot
跳转到 sdram 里的 start_armboot 函数执行。
回味经典——uboot1.1.6 之 第一阶段的更多相关文章
- 回味经典——uboot1.1.6 之 第二阶段 第三阶段
转自:http://blog.csdn.net/lizuobin2/article/details/52061530 上篇文章说到,再清 BSS 段之后,CPU 跳转到 sdram 里的 start_ ...
- u-boot-1.1.6第2阶段入口函数start_armboot分析
学习目标: 1.分析u-boot-1.1.6第2阶段入口函数void start_armboot (void),熟悉该函数所实现的功能 2.为后面能够掌握u-boot-1.1.6如何启动内核过程打下基 ...
- PHP四个阶段目标以及第一阶段学习内容
PHP课程体系主要分为四个阶段,第一阶段讲的是网页HTML和数据库MySQL,第一阶段要学会网页制作的基础知识,熟用各种基本标签,对数据库进行操作,各项考核都能够达标,拿出出众的项目展示. 在第二个阶 ...
- Bete冲刺第一阶段
Bete冲刺第一阶段 今日工作: github团队协作流程 web:调整dao层设计,增加新的dao组件 客户端:之前遗留的界面跳转的BUG 目前所遇问题: 第一,COCOAPODS的安装上还是有点问 ...
- java - 第一阶段总结
java - 第一阶段总结 递归 递归:能不用就不用,因为效率极低 package over; //递归 public class Fi { public static void main(Strin ...
- 自我总结(六)---(学习j2ee+j2ee第一阶段项目)
自我完善的过程就是在不断的自我总结不断的改进. 学习了Struts2 Spring Hibernate. 十天前结束了这个课程.也考试了.这次考试老师说机试考的还不错.其实就是一个简单的用户登录,进行 ...
- JAVA EE 第一阶段考试
在第一阶段中我们学习了Spring Struts2 Hibernate.虽然在外面的公司中,公司项目的框架中都不在使用Struts2了.他好像出现了不可修复的bug.但是在学校,依然还是要学习这个.在 ...
- Java第一阶段总结
学习java已经一个多月的时间了,第一阶段总算完成了. 这中间遇到很多问题,通过问同学问学长,收获了很多,但也知道自己和其他同学相差很远.他们java第一阶段只用了不到一个月的时间,而我拖了很长时间, ...
- [原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇)
原文:[原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) .NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) 前言:接着上篇来. 系列文章链接: [ ...
随机推荐
- 怎样高效利用GitHub(非常多资料可供下载)
正是Github.让社会化编程成为现实.本文尝试谈谈GitHub的文化.技巧与影响. Q1:GitHub是什么 Q2:GitHub风格 Q3: 在GitHub.怎样跟牛人学习 Q4: 享受纯粹的写作与 ...
- nodejs while-loop
node-while-loop A while loop alternative for Nodejs based on promises. Install $ npm install --save ...
- 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 & 纹理混合
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星云 ...
- 【剑指Offer学习】【面试题62:序列化二叉树】
题目:请实现两个函数,分别用来序列化和反序列化二叉树. 解题思路 通过分析解决前面的面试题6.我们知道能够从前序遍历和中序遍历构造出一棵二叉树.受此启示.我们能够先把一棵二叉树序列化成一个前序遍历序列 ...
- JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对照
1.什么是JVM? JVM本质上就是一个软件,是计算机硬件的一层软件抽象,在这之上才干够运行Java程序,JAVA在编译后会生成相似于汇编语言的JVM字节码,与C语言编译后产生的汇编语言不同的是,C编 ...
- MySQL 创建自定义函数(1)
1. 创建测试自定义函数(1) CREATE DEFINER=`dbdh`@`localhost` FUNCTION `test`.`sp_function_dbdh_three`() RETURNS ...
- Dubbo Spring Cloud Motan
跨语言统一治理.Golang,谈谈另辟蹊径的开源RPC框架Motan_搜狐科技_搜狐网 https://www.sohu.com/a/207389967_467759
- 使用 QWorker 做为计划任务引擎
QWorker 提供了 Plan 函数来提供计划任务功能的支持.每个任务做为一个作业,可以在指定的时间点被触发执行.而 cron 作为 Linux 操作系统下计划任务的标准被广大用户所熟知,QWork ...
- Hibernate总结(转)
原文:http://blog.csdn.net/yuebinghaoyuan/article/details/7300599 那我们看一下hibernate中整体的内容: 我们一一介绍其中的内容. H ...
- iOS 第三方登录之 QQ登录
一. 首先需要下载腾讯qq登录所需的库,下载地址是http://open.qq.com/ . 需要用到的有TencentOpenAPI.framework 和TencentOpenApi_IOS_Bu ...