********************************LoongEmbedded********************************
作者:LoongEmbedded(kandi)
时间:2010.11.07
类别:WINCE操作系统
********************************LoongEmbedded********************************
WINCE6.0+S3C2443系统启动过程
我们知道是NBOOT用来引导EBOOT,继而EBOOT加载并引导WinCE操作系统(NK)。那么,WinCE6.0是如何启动的呢?在前面文章我们知道eboot的OEMLaunch函数通过调用下面函数来跳转到dwPhysLaunchAddr处执行
Launch(dwPhysLaunchAddr)
我们先来看看NK.bin的image start等一些信息吧
图1
上图的start address=0x80245c50,这个地址经过OALVAtoPA()函数转换之后获得的对应的物理地址0x30245C50就是dwPhysLaunchAddr地址,那么这个地址对应的是NK.bin中哪个文件的哪个函数呢?通过命令viewbin –o nk.bin >output.txt输出output.txt到release目录下,下面看这个文件的下面内容:
图2
也可以通过PB6.0中浏览NK.bin的功能来查看这个地址(file->open->file,选择Windows Embedded CE Run-Time Image类型,然后选择NK.bin)
图3
从图2或图3 start address=0x80245c50对应的是NK.exe入口地址,而NK.exe就是OAL.exe,为什么呢?我们看看release目录下ce.bib的如下内容
图4
由图4可知,NK.exe就是OAL.exe,那么OAL.exe的入口地址是哪个函数呢?看/Src/Oal/Oalexe下面的source文件
图5
这样我们就知道eboot跳转到OAL.exe的startup函数中执行了。
1. Statup函数的主要部分
Startup入口
图6
因为EBOOT中已经初始化了相关硬件,所以OAL的启动代码就可以省去这部分工作,接着执行下面部分
图7
图7 tst指令先用r0来和0x8进行与运算,结果为0,所以设置标志位Zero=1,后面的beq指定在zero=1的时候,跳转到下面标号4的地方,下面的链接是ARM汇编beq和bne的介绍http://blog.csdn.net/LoongEmbedded/archive/2010/11/04/5987565.aspx
图8
图8的是将g_oalAddressTable保存到r0,作为参数传递给kernelStart函数,以便KernelStart函数中初始化MMU地址映射(g_oalAddressTable就是地址映射表的地址)。我们怎么来理解
add r0, pc, #g_oalAddressTable - (. + 8)
这条指令呢?我们知道PC总是指向取指的指令,而不是指向正在执行的指令或是正在译码的指令,一般情况下,人们总是习惯把正在执行的指令作为参考点,称为当前第一条指令,因此,PC总是指向第3条指令。对于ARM指令,有:PC的值=当前指令所在的存储地址+8;对于Thumb指令,则有:PC的值=当前指令所在的存储地址+4,下图是ARM系列的流水线图
图9
(.+8)这个表达式的. (+之前的点) 是在MS的ARM编译器在预处理时决定的,其代表它所在的指令的地址(也即这条指令的存储地址)。这里还要知道一点,就是ARM架构的特点,一条指令从加载到执行主要分为预取、译码、执行三个阶段。结合上面提到的“PC的值=当前指令所在的存储地址+8”,因此有恒等式.+8=pc,上面语句可以理解为
r0 = pc+g_oalAddressTable-(.+8) = g_oalAddressTable+ (pc-(.+8)) = g_oalAddressTable
也可以这样理解
r0 = pc+g_oalAddressTable-(.+8)=pc-(.+8)+ g_oalAddressTable=当前指令所在的存储地址+8-(.+8)+ g_oalAddressTable=.+8--(.+8)+ g_oalAddressTable= g_oalAddressTable
为什么要采用这样的方式给r0赋值,而不采用下面的语句呢
Ldr r0,g_oalAddressTable (我试过用这条语句,就不能成功跳转,系统无法正常启动)
为什么startup函数中不能直接把g_oalAddressTable符号的值赋值给r0来作为KernelStart函数的参数呢?因为在调用KernelStart函数的时候MMU单元还处于关闭的状态,CPU访问外部物理存储器仍然使用的是系统物理地址,而g_oalAddressTable符号记录的是g_oalAddressTable数组的起始存储位置在0x80000000~0x9FFFFFFF范围内的虚拟内存地址。之所以g_oalAddressTable符号记录的是虚拟内存地址而不是物理地址,并且落在了0x80000000~0x9FFFFFFF虚拟内存地址范围内,因为在汇编源代码中使用的用来表达程序地址的字符串在程序链接阶段要接受地址重定位的处理,而地址重定位的依据是config.bib,其中对系统物理存储的使用分配作出安排所使用的全部是0x80000000~0x9FFFFFFF范围内的虚拟内存地址
我们接下来看看从startup函数跳转到KernelStart函数的语句
bl KernelStart ; Call the WinCE kernel.
首先我们要知道arm指令集与X86不同,不是通过栈空间来传递参数,而是通过寄存R0,R1,R2,R3来传递,而前面的语句就是把g_oalAddressTable的物理存储地址赋值给r0后传递给KernelStart函数的,到此,硬件平台初始化完成后,oal.exe的启动任务基本完成,余下的启动工作由内核相关且独立于内核的OAL层实现体kernel.dll接管
2. KernelStart函数
这个函数在/WINCE600/PRIVATE/WINCEOS/COREOS/NK/LDR/ARM/armstart.s中定义,这个函数的主要任务就是为WINCE操作系统设置页表并且启用MMU,先看KernelStart入口处
图10
设置页表的部分就不介绍了,下面看启动MMU和caches部分
图11
图11启用了MMU和caches部分之后,WINCE操作系统后面就使用虚拟内存地址而不是物理地址来访问了。接着KernelStart函数为ABORT