uclinux启动过程详细分析
2) 为加载Boot Loader的Stage2准备RAM空间
3) 复制Boot Loader的Stage2到RAM空间中
4) 设置好堆栈
5) 跳转到Stage2的C入口点
Boot Loader的Stage2通常包括如下步骤:
1) 始化本阶段要使用的硬件设备
2) 检测系统内存映射(Memory Map)
3) 将Kernel映象和根文件系统映象从FLASH上读取到RAM空间中
4) 为内核设置启动参数
5) 调用内核
2.2 系统内存组织
由于嵌入式设备具有很好的制定性,因此通常硬件环境会变的千差万别。就算是用户使用了相同的处理器芯片,但是也很有可能因为外围设备电路设计的不同,而存在差异。对于BootLoader程序来说,存储设备的与处理器的连接方式,与其息息相关。对于我们采用的S3C44B0微处理器来说,在系统加电之后,指令指针是指向0x00000000的,也就是说系统是从0x00000000开始之行。正是因为这个原因,通常这个地址空间我们会安排给FLASH存储器。这样我们可以将BootLoader启动代码以及我们之后将会要启动的uClinux操作系统映像烧写到Flash里。对于RAM地址空间,S3C44B0芯片将其设定为从0x0C000000到0x0FFFFFFF一共64MB的范围里。我们可以通过设定存储器控制寄存器来重新设定RAM的大小。例如我们试验采用的存储设备安排如下:
0x00000000 – 0x003FFFFF 4MB Flash
0x0C000000 – 0x0C7FFFFF 8MB RAM
通常来说对于系统的引导和操作系统的启动,可以完全都在Flash中进行,但是Flash存储器的速度相对于RAM来说会慢很多,因此出于速度上的考虑,我们通常会将启动代码和uClinux操作系统的内核映像文件拷贝到RAM中之行。
下面我将对典型的BootLoader程序框架进行分析。
2.3 Stage1阶段
该阶段的主要工作是完成对系统中断向量的设置,初始化微处理器内部寄存器,初始化堆栈,初始化RAM地址空间,并且将Stage2部分的C代码拷贝到RAM空间的指定地点,然后跳转到C代码入口点继续执行。对于这段代码来说,做的都是一些准备工作,因此为了提高效率,这段代码通常都是使用汇编语言来完成的。下面我将结合具体的代码来分析一下Stage1的启动过程。
1)设置中断向量
设置S3C44B0处理器定义的8种系统中断的中断向量地址。这八种系统中断分别是复位中断、未定义指令中断、软件中断、指令预取异常中断、数据异常中断、地址异常中断、IRQ中断和FIQ中断。这8个中断通常是通过无条件跳转的方式来实现的。具体的代码如下。
__entry :
b ResetHandler /* Reset vector */
b HandlerUndef /* Undefined instruction */
b HandlerSWI /* SWI */
b HandlerPabort /* Prefetch abort */
b HandlerDabort /* Data abort */
b 。 /* Address exception */
b HandlerIRQ /* IRQ */
b HandlerFIQ /* FIQ */
2)初始化微处理器内部寄存器
这段代码主要是要完成硬件部分的初始化,包括关闭中断响应、初始化微处理器通用端口、设置CPU频率等操作。不过需要注意的是,在进行硬件初始化之前需要将微处理器的运行状态转换到SVC模式下。
MRS a1,CPSR /*; Pickup current CPSR*/
BIC a1,a1,#MODE_MASK /*; Clear the mode bits*/
ORR a1,a1,#SUP_MODE /*; Set the supervisor mode bits*/
ORR a1,a1,#LOCKOUT /*; Insure IRQ and FIQ intr are locked out*/
MSR CPSR_cxsf,a1 /*; Setup the new CPSR*/
3)初始化系统RAM空间
这个部分的工作主要是为之后启动代码和内核映像的拷贝操作做准备,并且也为之后的C代码的执行初始化堆栈。这部分的工作主要可以分成两个部分来处理。首先,根据系统配置的存储器特性来初始化相关的存储器控制寄存器。在我们使用的S3C44B0处理器中,存储空间被分成了BANK0-BANK7一共8个块,分别由BANKCON0-BANKCON7控制各个块存储器的读写时钟和片选时钟等信号参数。具体代码如下:
ldr r0,=rBANKCON0
ldr r1,=0x700
str r1,[r0]
ldr r0,=rBANKCON1
ldr r1,=0x700 /* 0x7ffc */
str r1,[r0]
ldr r0,=rBANKCON2
ldr r1,=0x700 /* 0x7ffc */
str r1,[r0]
ldr r0,=rBANKCON3
ldr r1,=0x7568
str r1,[r0]
ldr r0,=rBANKCON4
ldr r1,=0x700 /* 0x7ffc */
str r1,[r0]
ldr r0,=rBANKCON5
ldr r1,=0x700 /* 0x7ffc */
str r1,[r0]
ldr r0,=rBANKCON6
ldr r1,=0x18008
str r1,[r0]
ldr r0,=rBANKCON7
ldr r1,=0x18000
str r1,[r0]
ldr r0,=rREFRESH
ldr r1,=0xac03e1
str r1,[r0]
ldr r0,=rBANKSIZE
ldr r1,=0x16
str r1,[r0]
ldr r0,=rMRSRB6
ldr r1,=0x020
str r1,[r0]
ldr r0,=rMRSRB7
ldr r1,=0x020
str r1,[r0]
初始化RAM空间的第二个部分就是初始化连接脚本文件中指定的需要清0的地址空间,将该断地址空间的内容清0。该部分地址空间主要是用来存放C语言代码中的全局变量等内容的。实现代码如下:
LDR a1,=Image_ZI_Base /* Pickup the start of the BSS area */
MOV a3,#0 /* Clear value in a3 */
LDR a2,=Image_ZI_Limit /* Pickup the end of the BSS area */
CMP a1,a2
BEQ move_data
clear_loop :
STR a3,[a1],#4 /* Clear a word, a1 += 4 */
CMP a1,a2 /* end of ZI ? */
BNE clear_loop
4)为Stage2的C语言代码的执行准备必要的堆栈
因为在Stage2阶段一般都是采用C语言代码来完成的,因此必须在使用C语言代码之前先建立起必要的堆栈信息。通常为了避免堆栈数据被执行代码破坏,通常都是放在RAM的高端地址,并且使得堆栈指针的增长方向是向下增长的。
5)将初始化代码拷贝到RAM中,并且跳转到RAM中执行。因为在我们采用的S3C44B0微处理器里对于FLASH和RAM地址空间是使用的统一编址的,因此我们可以直接使用一个简单循环来完成拷贝。
加入微信
获取电子行业最新资讯
搜索微信公众号:EEPW
或用微信扫描左侧二维码