注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

男儿当自强的博客

每天进步一点

 
 
 

日志

 
 
 
 

WINCE6.0+S3C2443系统启动过程  

2010-11-08 08:26:36|  分类: wince操作系统 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

********************************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目录下,下面看这个文件的下面内容:

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

 

图2

也可以通过PB6.0中浏览NK.bin的功能来查看这个地址(file->open->file,选择Windows Embedded CE Run-Time Image类型,然后选择NK.bin)

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图3

从图2或图3  start address=0x80245c50对应的是NK.exe入口地址,而NK.exe就是OAL.exe,为什么呢?我们看看release目录下ce.bib的如下内容

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图4

由图4可知,NK.exe就是OAL.exe,那么OAL.exe的入口地址是哪个函数呢?看\Src\Oal\Oalexe下面的source文件

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图5

这样我们就知道eboot跳转到OAL.exe的startup函数中执行了。

 

1.       Statup函数的主要部分

Startup入口

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图6

因为EBOOT中已经初始化了相关硬件,所以OAL的启动代码就可以省去这部分工作,接着执行下面部分

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图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

 

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图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系列的流水线图

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图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入口处

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图10

设置页表的部分就不介绍了,下面看启动MMU和caches部分

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图11

图11启用了MMU和caches部分之后,WINCE操作系统后面就使用虚拟内存地址而不是物理地址来访问了。接着KernelStart函数为ABORT、IRQ、FIQ、UNDEF和supervisor这5个CPU的特权级状态设置堆栈

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图12

这些工作完成之后KernelStart函数就会调用ARMInit函数,下面看是如何调用ARMIint函数的

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图13

这样在调用ARMIint之前,r0就保存了KDataSrtuct这块数据区域的指针,也即r0指向了KDataSrtuct这块数据区域,这就是传递给ARMIint函数的参数,这个函数这个函数在\WINCE600\PRIVATE\WINCEOS\COREOS\NK\LDR\ARM\armint.c下面定义

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图14

 

ARMIint函数有三个主要的任务:

⑴是调用KernelRelocate()函数进行内核全局变量重定位。

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图15

在ARMInit函数执行之前,系统仍无法使用全局变量,因为系统的全局还在ROM区域,对于操作系统而言,出于安全的考虑,只有XIP程序才有读取ROM区域数据的权利,对于大部分WINCE操作系统,只有将数据拷贝到RAM区域才能进行读写,ARMInit函数通过调用KernelRelocate函数对pTOC全局变量重新定位之后,才能读写。对内核启动所需要的

 

⑵全局变量重新定位之后,对内核启动所需要的KDataStruct结构体(它是内核数据结构, 简单理解成共享内存好了, oal 和 kernel都可访问)进行初始化,是将位于OAL的OEMinitGlobals()函数指针赋值到pKdata的成员变量dwOEMInitGlobalsAddr,这个在后面的内核NKStartup函数将会用到,OEMInitGlobals便是交换oal.exe和kernel.dll之间的全局指针,下图是OEMInitGlobals函数体

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图16

⑶是调用函数FindKernelEntry通过TOC找到kernel.dll的入口点函数地址,是如何找到的呢?先看FindKernelEntry函数体

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图17

图17具体是如何找到kernel.dll的入口函数的呢?我们通过viewbin –o nk.bin >output.txt输出NK.bin的相关信息中的kernel.dll部分信息如下

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图18

我们看kernel.map文件

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图19

再来看下图

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图20

结合图18,图19和图20我们可以知道kernel.dll的入口函数是NKStartup,这样通过调用函数ARMInit函数就返回NKStartup,那么我们回到KernelStart函数来看看是如何跳转到NKStartup函数执行的

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图21

就这样跳转到NKStartup函数执行了。

 

3.       NKStartup函数

这个函数在\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\mdram.c中定义,我们先看看NKStartup函数头的注释吧

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图22

NKstartup函数的主要工作如下:

⑴从NK Loader传递过来的KDataStruct结构体数据中获取到供自身启动所需要的数据信息,如下图所示,其中http://blog.csdn.net/LoongEmbedded/archive/2010/11/06/5991971.aspx介绍了KDataStruct结构体结构体

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图23

⑵定位对WINCE6.0特有的OEMGLOBAL结构体的初始化函数OEMInitGlobals地址,该结构体构建了内核和OAL层之间进行通信的桥梁。在 OEMGLOBAL结构体定义了OAL层所必须的函数,该结构体在oemglobal.c文件中被初始化,并被编译在OEMMain.lib和 OEMMain_StaticKITL.lib两个库中,如果OAL链接这两个库,则必须要有该结构体中函数实现体;

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图24

这里涉及到一个很重要的全局变量g_pOemGlobal,它指向结构体OEMGLOBAL全局变量OemGlobal,而OemGlobal在oemglobal.c定义并对其成员函数赋值,下图是其中一部分

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图25

在这里,我们看到了一个很重要的函数OEMInit,这些OEM函数就是被内核用来和OAL部分通信的。

 

⑶调用函数ARMSetup为操作系统内核进程填充虚拟内存0XA0000000~0XBFFFFFFF范围的uncachable静态映射区域,在前面的KernelStartup函数中,依据g_oalAddressTable数组的数据内容将所有的系统物理地址单元(包括所有类型的CPU可直接寻址访问的物理存储设备和各类外设的控制与状态寄存器、数据缓冲区而后I/O端口)映射到虚拟内存的0x80000000~0x9FFFFFFF地址范围。这是WINCE操作系统的一个约束,正是由于这个缘故,在基于WINCE操作系统中所有的系统物理地址单元总容量不得超过512MB,在ARMSetup函数中再一次将全部的物理存储映射到虚拟地址的0xA0000000~0xBFFFFFFF地址范围。

WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客 

图26

另外ARMSetup函数还设置中断向量表,如下

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图27

 

⑷如果系统启用了KITL调试功能,则加载KITL模块

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图28

至于kitl.dll的入口函数值如何确定的呢?这个办法可以参考上面介绍到如何定位kernel.dll的入口函数一样,我们可以找到kitl.dll的入口函数是KitlDllMain()。当在编译选项(Build Options)中选中Enable KITL时,编译器就会将kitl.dll动态链接库链接到到内核中,在OEMGLOBAL结构体定义了利用KITLOEM宏将kitl.dll的入口函数KitlDllMain编译到系统中条件,这是系统链接的库为oemmain_statickitl.lib,下面为OEMGLOBAL结构体中KITL的定义部分,我们看OemGlobal全局数组中的pfnKITLGlobalInit成员的初始化

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图29

 

⑸调用OAL函数执行对目标平台板级硬件的初始化动作。

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图30

图30注释中的OEMInit是OAL中用来实现对板级硬件进行初始化的一个很重要的OEM函数,其主要的任务是初始化cache全局变量,初始化LCD控制器,初始化中断,初始化系统时钟和初始化KITL等,其他OAL函数也是通过这样的方式被内核调用来和OAL层通信的。

 

⑸清楚TLB,分隔RAM区域和跳转到KernelStartup函数执行。

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图31

KernelFindMemory()是查找系统可用内存并且分隔,在WINCE操作系统中,RAM空间主要分为存储内存和程序内存,存储内存主要为文件的存储空间,包括内核文件和复制到系统中所有目标文件,程序内存为运行程序时所需要的存储空间,见相关的博文http://blog.csdn.net/LoongEmbedded/archive/2010/10/16/5944846.aspx

 

NKStartup函数完成自己的工作之后跳转到第二个KernelStart函数,这里的KernelStart函数与前面的KernelStart函数的属于两个完全不同的函数,NKStartup函数中调用的KernelStart 函数为\WINCE600\PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\armtrap.s文件中的KernelStart 函数,主要完成调用内核初始化函数KernelInit,并跳转到操作系统的第一个启动的任务,这个KernelStartup函数与NKstartup函数同属一个静态库及同一个kernel.dll模块,而上一个KernelStartup函数所属的nkldr.lib库被链接进了WINCE系统模块oal.exe中。

 

4.       第二个KernelStart函数

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图32

这个函数的主要工作有两部分:

⑴调用KernelInit函数来初始化内核

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图33

在目标设备上的硬件初始化结束后,下一步就是调用KernelInit内核初始化函数来初始化WINCE操作系统本身,WINCE6.0的内核初始化函数同其他版本的内核初始化函数基本相近,主要完成在启动第一个线程前对内核进行初始化,主要包括API函数集初始化、堆的初始化、初始化内存池、进程初始化、线程初始化和文件映射初始化等操作。下面来看THRDIint函数体最重要的一部分,THRDInit()初始化了第一个线程是SystemStartupFunc。

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图34

⑵FirstSchedule

FirstSchedule函数为Windows CE操作系统启动过程中最后无条件跳转的一个函数,FirstSchedule调用HandleException函数来让系统进行重新调度,这样就选择了第一个就绪的线程(也就是SystemStartupFunc)来执行,下面接着看线程处理函数SystemStartupFunc

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图35

接着看RunApps线程

 WINCE6.0+S3C2443系统启动过程 - 男儿当自强 - 男儿当自强的博客

图36

RunApps线程处理函数主要是启动filesys.dll,启动后等待其执行的情况,在完成了文件系统的相应的初始化之后,这里继续初始化MUI和系统设置,完成后再通知filesys这边的工作已经完成,filesys继续启动,filesys会完成WinCE的最后启动过程,包括设备管理device.dll,窗体图像子系统gews.dll和shell程序 explore.exe,这样,WINCE6.0的启动就完成了。

 

备注:对于fiesys文件系统的启动过程,可以看MSDN的描述http://msdn.microsoft.com/en-us/library/aa912276.aspx

 

参考文档

http://blog.csdn.net/LoongEmbedded/archive/2010/11/06/5991297.aspx

http://www.cnblogs.com/we-hjb/archive/2008/10/12/1309596.html

http://apps.hi.baidu.com/share/detail/17484975

http://blog.chinaunix.net/u3/105630/showart_2181227.html

http://www.armce.com/bbs/thread-403-1-1.html

 

 

  评论这张
 
阅读(805)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017