在嵌入式系统中,Boot Loader是系统运行必不可少的前提。在特定的硬件平台上运行Windows CE最困难的工作之一就是让Boot Loader正常启动这个硬件平台。 Boot Loader从代码层面上来说由OEM启动代码(OEM startup code)和主代码(main code)两部分组成。 其中, OEM启动代码是Boot Loader的入口点,是目标平台上电后最先执行的部分。一般是对处理器内核进行配置和初始化,为后面的引导程序作准备。这类代码直接面向处理器进行编程,一般用汇编语言实现。通用的内容包括:(1)设置中断向量表;(2)初始化存储器系统;(3)初始化堆栈;(4)初始化调试端口和设备;(5)初始化C语言环境;(6)转到主代码main. c。
下面以i.MX21平台上的启动代码文件Startup.s为例进行分析.
(一) %WINCE\PLATFORM\COMMON\SRC\ARM\Freescale\MX21\Startup\startup.s
WinCE_MX21_BSP用户手册里提到的OAL层源代码,用于在OEM板级初始化前先初始化MX21处理器的核心模块。
主要的函数代码解读如下:
1. StartUp()---这部分代码是Eboot和OAL共享的。
mov r0, #(SVCMode:OR:IRQDisable:OR:FIQDisable)
msr cpsr_c, r0
//在板子上电后,第一步是将处理器设置为特权模式
bl MMUCacheDisable
//跳转到MMUCacheDisable( ),使TLB和cache、写缓冲无效,并关闭 MMU、cache。
ldr r1, =CSP_BASE_REG_PA_AIPI1
ldr r0, =0x00040304
……
str r0, [r1, #AIPI_PAR_OFFSET]
//设置AIPI寄存器
bl OALSetUpSystemControl
bl OALSetUpFrequencies
//跳转到OALSetUpSystemControl 和OALSetUpFrequencies,初始化系统控制模式和时钟频率。
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #0x00001000
mcr p15, 0, r0, c1, c0, 0
//启用I Cache
bl OALIsImageInRAM
bl OALSetUpExtMemories
//跳转到OALIsImageInRAM、OALSetUpExtMemories以初始化外部存储设备。
ldr r1, =CSP_BASE_REG_PA_MAX
ldr r0, =0x77123045
str r0, [r1, #MAX_MPR0_OFFSET]
……
str r0, [r1, #AITC_NIPRIORITY0_OFFSET]
//设置MAX寄存器、配置AITC中断控制器、屏蔽和清中断的一段代码。
bl OALSetUpGpio
bl OALSetUpKeypad
b OALStartUp
//先后跳转到OALSetUpGpio、OALSetUpKeypad,即初始化系统GPIO和键盘设备。最后程序转到OALStartUp(),执行代码重定位和从RAM中启动Eboot功能。
上面提到的OALSetUpSystemControl 、OALSetUpFrequencies、OALSetUpExtMemories、OALSetUpGpio、OALSetUpKeypad这几个函数位于 %WINCE\PLATFORM \iMX21\ Imx21ads\Src\Kernel\Oal\oal_startup.c中;OALIsImageInRAM()和OALStartUp()则位于%WINCE\PLATFORM\Imx21ads\Src\Kernel\Oal\startup.s中。
2.MMUSetup()---这个函数将被%Eboot\startup.s调用,它根据OEMAddressTable设置MMU,并使能MMU、启用cache。
(二)%WINCE\PLATFORM\Imx21ads\Src\Kernel\Oal\startup.s
功能:OEM板级初始化
这部分代码首先定义了flash、RAM的物理地址和虚拟地址:
FlashPABase EQU CSP_BASE_MEM_PA_CS0
FlashVABase EQU 0x80000000
RamPABase EQU CSP_BASE_MEM_PA_CSD0
RamVABase EQU 0x88000000
RamMaxSize EQU 0x04000000 ; 64M
接下来的代码可以分成四个函数:
OALStartUp()
adr r0, g_oalAddressTable
b KernelStart
//加载OEMAddressTable地址,跳转到KernelStart()
OALIsImageInRAM()---检查Image是否正跑在RAM里
BSPAmdBurstCfg44M()---将iMX21标准板的flash设置为burst模式44M频率
BSPAmdBurstCfg66M()---将iMX21标准板的flash设置为burst模式66M频率
(三)%WINCE\PLATFORM\Imx21ads\Src\Bootloader\Eboot\startup.s
功能:调用了OAL的Startup.s(位于%WINCE\Platform\Common\Src\Arm\Freescale\MX21),将Eboot重定位至RAM中运行,并初始化MMU,使能MMU、cache,引导启动CE内核等。
基本流程:
首先给出了一系列宏定义,比如Eboot的大小和地址信息:
EbootRamOffset EQU 0x00010000
EbootImageSize EQU 0x00040000
EbootFlashPAStart EQU (FlashPABase)
EbootFlashVAStart EQU (FlashVABase)
EbootRamPAStart EQU (RamPABase + EbootRamOffset)
EbootRamVAStart EQU (RamVABase + EbootRamOffset)
//这里的信息是和eboot.bib相对应的,要对照着改动。
MmuPageTableOffset EQU 0x00001000
MmuPageTableBase EQU (RamPABase + MmuPageTableOffset)
//MMU页表在RAM中的物理地址和大小
StackEndOffset EQU 0x00070000
StackEndVA EQU (RamVABase + StackEndOffset)
//栈尾的偏移地址和虚拟地址
接着进入KernelStart()函数,先跳转到OALIsImageInRAM程序段判断Image是否正跑在RAM里。
bl OALIsImageInRAM
cmp r0, #1
beq RamStart
//如果已经在RAM里,则跳转到RamStart程序段
如果不是,则在接下来的RelocateEBOOT程序段里进行代码重定位:
ldr r1, =FlashPABase
ldr r0, =EbootRamPAStart
ldr r2, =(EbootImageSize/16)
ldmia r1!, {r3-r6}
stmia r0!, {r3-r6}
subs r2, r2, #1
bne %b10
adr r2, RamStart
ldr r3, =(FlashPABase)
sub r3, r2, r3
ldr r4, =(EbootRamPAStart)
add r2, r4, r3
bx r2
RamStart段代码如下:
adr r2, MMUSetupDone
ldr r3, =(EbootRamPAStart)
sub r2, r2, r3
ldr r3, =(EbootRamVAStart)
add r2, r2, r3
mov lr, r2
//设置连接寄存器LR
adrl r0, g_oalAddressTable
//加载OEMAddressTable和页表的地址
ldr r1, =(MmuPageTableBase)
//加载页表的地址
b MMUSetup
nop
nop
nop
nop
nop
//跳转到OAL层核心源代码的MMUSetup函数,使能MMU、设置虚拟内存模式,并通过g_oalAddressTable初始化MMU页表
MMUSetupDone
ldr sp, =(StackEndVA-4)
//MMU配置完成,使栈指针处于虚拟内存中
b BootloaderMain
//跳转到% blcommon.c中的BootloaderMain()
最后的Launch()代码段是由EBOOT启动函数OEMLaunch( )所调用的,用来关闭MMU,跳转到CE内核。
mov r1, #0
mcr p15, 0, r1, c7, c7, 0 // 初始化I, D cache
mcr p15, 0, r1, c7, c10, 4 // 初始化write buffer
mcr p15, 0, r1, c8, c7, 0 // 设置TLB无效
mov r1, #0x0078
mcr p15, 0, r1, c1, c0, 0 // Disable MMU, caches 和 write buffer
mov pc, r0 // 跳转到 PhysicalStart
nop
nop
nop
nop
一般认为%WINCE\PLATFORM\COMMON\SRC\ARM\Freescale\MX21\Startup\startup.s里的Startup( )是i.MX21标准板Eboot的第一个函数,上电后首先完成系统CPU初始化。