u-boot源码个别分析
深入Bootloader系列
http://ftp.denx.de/pub/u-boot/
简介===>
1.U-Boot系统加载器
U-Boot是一个规模庞大的开源Bootloader软件,最初是由denx(www.denx.de)发起。U-Boot的前身是PPCBoot,目前是SourceForge(www.sourceforge.net)的一个项目。
最初的U-Boot仅支持PowerPC架构的系统,称做PPCBoot。从0.3.2官方版本之后开始逐步支持多种架构的处理器,目前可以支持 PowerPC(MPC5xx、MPC8xx、MPC82xx、MPC7xx、MPC74xx)、ARM(ARM7、ARM9、StrongARM、 Xscale)、MIPS(4kc、5kc)、X86等处理器,支持的嵌入式操作系统有Linux、Vx-Works、NetBSD、QNX、 RTEMS、ARTOS、LynxOS等,是PowerPC、ARM9、Xscale、X86等系统通用的Boot方案。
U-Boot支持的处理器和操作系统很多,但是它对PowerPC系列处理器和Linux操作系统支持最好。U-Boot支持的功能也较多,对于嵌 入式开发常用的查看、修改内存,从网络下载操作系统镜像等功能都提供了很好的支持。U-Boot的项目更新较快,支持的目标板众多,是学习底层开发很好的 示例。
2.ViVi系统加载器
ViVi是韩国的mizi公司专门针对ARM9处理器设计的一款Bootloader。它的特点是操作简便,同时提供了完备的命令体系,目前在三星系列的ARM9处理器上ViVi也比较流行。
与U-Boot相比,由于ViVi支持的处理器单一,ViVi的代码也要小很多。同时,ViVi的软件架构和配置方法采用和Linux内核类似的风格,对于有过配置编译Linux内核经验的读者,ViVi更容易上手。
与其他的Bootloader一样,ViVi有两种工作模式:启动加载模式和下载模式。使用启动加载模式,在目标板上电后,ViVi会从预先配置好 的Flash分区读取Linux或者其他系统的镜像并且启动系统;使用下载模式,ViVi向用户提供了一个命令行接口,通过该接口用户可以使用ViVi提 供的命令。ViVi主要提供了5个命令如下:
Load:把二进制文件载入Flash或RAM。
Part:操作MTD分区信息。显示、增加、删除、复位、保存MTD分区。
Param:设置参数。
Boot:启动系统。
Flash:管理Flash,如删除Flash的数据。
与Linux内核的组织类似,ViVi的源代码主要包括arch、init、lib、drivers和include等几个目录,共200多个代码文件。各目录的具体功能请参考ViVi相关的信息。
=====================================================================》》》
=====================================================================》》》
=====================================================================》》》
基本目录分类:
common目录是与体系结构无关的文件,包括实现各种命令的C语言源代码文件。
cpu目录 存放与CPU相关的文件,每种CPU需要的代码文件存放在以CPU名称命名的子目录下,arm920t存放了arm920t为内核的 CPU相关的文件。在每个特定的子目录下都包括cpu.c、interrupt.c和start.S这3个文件,这3个文件是CPU初始化以及配置中断的 代码。U-Boot自带了很多CPU相关的代码,用户可以在现有CPU支持的基础上修改自己所需要的配置。
通用设备的驱动程序存放在drivers目录下。U-Boot自带了许多设备的驱动,包括显示芯片、网络接口控制器、USB控制器、I2C器件等,对于大多数用户而言已经够用,用户也可以按照自己的需求增加或者修改设备驱动。
fs 存放支持的文件系统代码,U-Boot目前支持cramfs、ext2、fat、jffs、reiserfs、yaffs等多种常见的文件系统。
net目录 是与网络协议有关的代码,比如BOOTP协议、TFTP协议、RARP协议等。
post 存放与硬件自检有关的代码。
rtc目录 存放与硬件实时时钟相关的代码。
tools目录 存放U-Boot编译过程中用到的一些工具代码。 // 例如:mkimage
==========================================
hao:
start_armboot => bootm.c
向量表在_start开始阶段已经汇编搞定。
其实主要就是个寄存器和内存的基本处理。
===》》》
列出了U-Boot在ARM处理器启动过程中的几个关键点,
从图中看出U-Boot的启动代码分布在start.S、low_level_init.S、 board.c和main.c文件中。
start.S 是U-Boot整个程序的入口,该文件使用汇编语言编写,不同体系结构的启动代码是不同 的;
low_level_init.S 是特定开发板的设置代码;
board.c 包含开发板底层设备驱动;
main.c是一个与平台无关的代码,U-Boot应用程序的入口在此文件中。
取出CPSR寄存器的值,CPSR寄存器保存当前系统状态,
使用比特清除命令清空了CPSR寄存器的中断控制位,表示清除 中断。
设置了CPSR寄存器的处理器模式位为管理模式,然后在第117行写入 CPSR的值强制切换处理器为超级保护模式。
定义看门狗控制器有关的变量,
根据平台设置看门狗定时器。
设置时钟分频寄存器的值。
需要根据CONFIG_SKIP_LOWLEVEL_INIT宏的值是否跳转到cpu_init_crit标号执行
===========cpu_init_crit==========
cpu_init_crit标号处的代码初始化ARM处理器关键的寄存器
- 228 /*
- 229 *****************************************************************
- 230 *
- 231 * CPU_init_critical registers
- 232 *
- 233 * setup important registers
- 234 * setup memory timing
- 235 *
- 236 ******************************************************************
- 237 */
- 238
- 239
- 240 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
- 241 cpu_init_crit:
- 242 /*
- 243 * flush v4 I/D caches
- 244 */
- 245 mov r0, #0
-
246 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ // 1.刷新cache
-
247 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ // 2.刷新TLB
- 248
- 249 /*
- 250 * disable MMU stuff and caches // 3.关闭MMU
- 251 */
- 252 mrc p15, 0, r0, c1, c0, 0
- 253 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
- 254 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
- 255 orr r0, r0, #0x00000002 @ set bit 2 (A) Align
- 256 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
- 257 mcr p15, 0, r0, c1, c0, 0
- 258
- 259 /*
- 260 * before relocating, we have to setup RAM timing
- 261 * because memory timing is board-dependend, you will
- 262 * find a lowlevel_init.S in your board directory.
- 263 */
- 264 mov ip, lr
-
265 bl lowlevel_init // 跳转到lowlevel_init
- 266 mov lr, ip
- 267 mov pc, lr
-
268 #endif /* CONFIG_SKIP_LOWLEVEL_INIT */
==>2.
TLB的作用是在处理器访问内存数据的时候做地址转换。TLB的全称是Translation Lookaside Buffer,可以翻译做旁路缓冲。
TLB中存放了一些页表文件,文件中记录了虚拟地址和物理地址的映射关系。当应用程序访问一个虚拟地址的时候,会从
TLB中查询出对应的物理地址,然后访问物理地址。TLB通常是一个分层结构,使用与Cache类似的原理。处理器使用一定的算法把最常用的页表放在最先 访问的层次。
==>3.
程序第252~257行关闭MMU。MMU是内存管理单元(Memory Management Unit)的缩写。在现代计算机体系结构上,MMU被广泛应用。使用MMU技术可以向应用程序提供一个巨大的虚拟地址空间。在U-Boot初始化的时候,
程序看到的地址都是物理地址,无须使用MMU。
=========================lowlevel_init=========================
开发板相关的初始化配置
- 133 lowlevel_init:
- 134 /* memory control configuration */
- 135 /* make r0 relative the current location so that it */
- 136 /* reads SMRDATA out of FLASH rather than memory ! */
-
137 ldr r0, =SMRDATA // 读取SMRDATA变量地址
- 138 ldr r1, _TEXT_BASE // 读取_TEXT_BASE变量地址
- 139 sub r0, r0, r1 // 得出相对偏移
- 140 ldr r1, =BWSCON // 主要是了解BANK的位宽,16位
-
141 add r2, r0, #13*4
// 得到SMRDATA占用的大小,结尾处的偏移 - 142 0:
- 143 ldr r3, [r0], #4 // 加载SMRDATA到内存 ,相当于一个while循环
- 144 str r3, [r1], #4
- 145 cmp r2, r0
-
146 bne 0b
// 循环,相当于while循环 - 147
- 148 /* everything is fine now */
- 149 mov pc, lr
- 152 /* the literal pools origin */
- 153
-
154 SMRDATA: // 定义SMRDATA值
-
155 .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON
-
<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
-
156 .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_ Tcoh<<6)+
-
(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
-
157 .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+
-
(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
-
158 .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+
-
(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
-
159 .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+
-
(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
-
160 .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+
-
(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
-
161 .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+
-
(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
-
162 .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
-
163 .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
-
164 .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+
-
(Tchr<<16)+REFCNT)
- 165 .word 0x32
- 166 .word 0x30
- 167 .word 0x30
程序第137~141行计算SMRDATA需要加载的内存地址和大小。首先在137行读取SMRDATA的变量地址,之后计算存放的内存地址并且记录在r0寄存器,然后根据总线宽度计算需要加载的SMRDATA大小,并且把加载结束地址存放在r2寄存器。
程序第14