您的位置:首页 > 其它

Windows CE内核启动分析

2009-11-11 16:13 295 查看
Windows CE内核启动分析
移植或者创建一个BSP,也许需要先熟悉Windows CE的内核启动过程.
目录
基于ARM的Windows CE内核启动分析1
1.startup.s2
2.KernelStart2
2.1 ARMInit()3
2.1.1 OALIntrInit3
2.1.2 OALTimerInit4
2.1.2.1 Variable Tick Scheduler4
2.2 KernelInit()4
2.3 FirstSchedule5

1.startup.s
内核入口点startup.S,内核从这里启动.因为内核经过bootloader加载,内核运行时候,已经由bootloader完成了硬件的基本初始化(关闭watchdog, pll设置等等)所以,startup.S的任务比较简单,只是将oemaddrtab_cfg.inc里面的g_oalAddressTable数组地址作为参数,传递给KernelStart,这个数组用来描述和实现物理地址到虚拟地址的映射.
(. + 8)是流水线处理.KernelStart()位于
PRIVATE/WINCEOS/COREOS/NK/KERNEL/ARM/armtrap.s
2.KernelStart
ARMInit()位于本目录的mdram.c文件.
KernelInit()位于PRIVATE/WINCEOS/COREOS/NK/KERNEL/kwin32.c中.
FirstSchedule()位于armtrap.s的一个label.
主要关注ARMInit()和KernelInit(),前一个进行目标板的初始化,后一个负责内核的初始化.FirstSchdule()开始调度第一个程序.
2.1 ARMInit()
先看看ARMInit()它的几个关键性动作如下:
KernelRelocate()是进行重定位.KernelFindMemory()是查找系统可用内存,并分成应用内存和object store两部分.这2个函数都已由MS自己实现.我们需要添加的函数是名字以OEM开头的函数.
OEMInitDebugSerial()初始化一个调试口,我们一般使用一个串口来作为调试口,这个函数需要自己实现,在 PLATFORM/SMDK2440A/Src/Kernel/Oal/debug.c中定义这个函数.比如可以将串口0设置为调试口,在这个函数中对串口0进行初始化.
OEMInit()是一个比较重要的函数,
OALCacheGlobalsInit()在PLATFORM/COMMON/SRC/ARM/COMMON/CACHE/init.s中实现,这部分代码以PQOAL的形式提供.
OALIntrInit()初始化中断.
OALTimerInit()初始化定时器TIMER4,作为系统时钟(tick),
configGPIO()初始化gpio口,设置相关寄存器.
InitDisplay()初始化LCD.有时候,我们希望在oal启动和内核加载期间显示一副等待图片或者显示LOGO,为达到这个目的,需要先初始化LCD.
OALKitlStart()准备启动KITL.
此外,在ARMInit还会通过调试口打印一些基本信息,开始时候打印”Windows CE Kernel for ARM….”字样, 中间打印处理器类型等等信息.结束时候打印” ARMInit done.”
2.1.1 OALIntrInit
调用OALIntrMapInit()初始化2个数组g_oalSysIntr2Irq,g_oalIrq2SysIntr,这2个数组表征irq和逻辑中断SysIntr的映射关系.
然后初始化中断寄存器,
最后,留一个接口给oem: BSPIntrInit(),如果oem需要在这个阶段初始化一些中断,可以定义这个函数并实现.
2.1.2 OALTimerInit
这个函数比较重要. 都知道所有WinCE系统都需要一个定时器来提供一个heartbeat,

g_oalTimer包含各种系统时钟相关的变量.
curridlehigh, curridlelow,这2个32位的DWORD变量合起来实现一个64位的计数器,反映了系统处于空闲模式(Idle mode)的时间。一般在OEMIdle()函数内更新。用户程序通过调用GetIdleTime()函数可以得到这个值。
初始化内核函数指针:pQueryPerformanceFrequency, pQueryPerformanceCounter.通过这两个函数实现高精度的计时器. 这两个函数的原型也已经由PQOAL实现.
初始化TIMER4作为系统时钟.TIMER4是一个16bit的定时器.此函数将TIMER4设置成为自动转载模式.
2.1.2.1 Variable Tick Scheduler
可变的系统时钟节拍,这个是WinCE5.0中增加的新的性能.
每一次定时器中断时候,内核分析所有线程后决定切换到哪个线程运行.假如所有线程都在等待状态,系统将进入idle状态.在这个状态的时候,任何中断都会唤醒系统重新开始调度.一般系统大部分时间是处于idle状态的,内核会调用OEMIdle()进入idle状态,我们已经知道这个状态会被任何中断唤醒. 在以前的版本中,系统中断(即上面的TIMER4中断)每毫秒产生一次,查看系统是否需要重新调度. 为了节电,不希望中断那么频繁.于是WinCE5.0中,在调用OEMIdle()之前会先调用pOEMUpdateRescheduleTime().通过这个函数重新设置侠义次系统时钟中断的时间.
2.2 KernelInit()
再看看KernelInit()函数
不过多关注KernelInit().
2.3 FirstSchedule
位于armtrap.s的一个label.开始第一个线程调度.整个内核开始运行.
============================================================================
另一篇介绍
本文简单描述一下wince5.0内核的启动流程,以mips cpu为例。msdn有一篇文章叫做Microsoft Windows CE 5.0 Board support Package,Boot Loader,and Kernel Startup Sequence非常不错,可以参考。
1. startup.首先,内核最先执行的代码位于oal当中,叫做startup,这段代码由微软留给开发者定制。当然,各个参考bsp里面都有现成的代码,开发者只需在此基础上改动。在startup()的末尾,会跳转到kernelstart函数。
2. kernelstart. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/mips/startup.s 这里面是汇编代码。是所有的mips开发板都要执行的操作。所以这里面会根据不同cpu类型作判断。虽然是汇编代码,好在里面还是有不少注释,通过这些注释,可以看出它里面主要在干什么。
3. KernelRelocate. kernelstart在完成一些必要的初始化之后,会调用KernelRelocate函数,这是一个比较重要的函数,位于WINCEROOT/Private/winceos/coreos/nk/kernel/loader.c. 它会把kernel用到的数据copy的ram里面。具体的功能msdn里面有解释。 这里的ram就是在config.bib里面指定的具有ram属性的存储区域,不是ramimage. kernelRelocate以pToc为参数,那么pToc的值从何而来呢?即便你搜索完所有的文件也找不到在那里pToc被赋值。因为pToc是在makeimage阶段被romimage.exe赋值的,也就是说pToc并不是在代码中被赋值的,是由外力(romimage.exe)改动nk.bin的内容赋值的。
4。MIPSInit. KernelRelocate处理完成之后,MIPSInit会被调用。位于WINCEROOT/Private/winceos/coreos/nk/kernel/mips/mdsched.c.这里是通用的mips的处理,其中会调用oal当中的OEMInitDebugSerial去初始化调试用的串口。
5。OEMInit。接下来就是大名鼎鼎的OEMInit了。这个函数由开发者定制。是c语言的。由上面的分析我们知道,在进入OEMInit的时候,串口已经初始化完毕,所以现在我们已经可以通过串口打印出一些调试信息了。而在此之前,我们只能通过led的方式作一些简单的显示。
6。KernelFindMemory. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/loader.c
OEMInit返回之后调用该函数。这个函数主要是把ram划分为两部分:object store和应用程序可以使用的部分。object store就是用于存贮wince的ram file system的,例如开机以后我们看到的/windows目录就是位于ram file system.
7。KernelInit. 位于WINCEROOT/Private/winceos/coreos/nk/kernel/kwin32.c
这部分跟cpu无关,是kernel要完成自己的初始化。至此,kernel得初始化全部完成,可以开始线程调度。
还有一点需要说明的时,kernel在完成初始化之后,会以IOCTL_HAL_POSTINIT为参数调用OEMIoControl,所以我们可以在这里打印出一句话表明kernel已经初始化完成。
除了kernel本身(nk.exe)之外,第一个被创建的进程是谁呢,对,就是文件系统,filesys.exe.
虽然他不是kernel本身的一部分,但是如果没有文件系统,wince也是玩不转的,注册表的初始化就是由文件系统来完成。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ln2002/archive/2007/06/29/1671360.aspx
============================================================================
另一篇介绍
分析着眼点:非原始BSP开发;在platform<pf>private<pr>之间的连接函数和一些重要的函数分析.
1.<pf>中的startup.s内核启动的第一个文件,汇编写的.里面是一些必须的初始化(一般不需要做任何修改),其中的KernelStart实现了<pf>到<pr>的跳转.
2.<pr>下面的KernelStart,在armtrap.s文件,也是汇编写的,注释很清晰,最好不修改.其中调用了(1)ARMInit;(2)KernelInit,其他忽略即可.WINCEROOT/Private/winceos/coreos/nk/kernel/一般在这里目录下面找到的.
3<pr>下面的ARMInit,在mdarm.c文件中,依次调用KernelRelocate(后面紧接着更新OEMaddresstable)->OEMInitDebugSerial(<pf> 中的函数)->->OEMInit(<pf> 中的函数)->KernelFindMemory函数.KernelRelocate(在loader.c),实现了将内核数据掉到RAM中,并初始化.OEMInitDebugSerial初始化板子上的串口.OEMInit比较重要的函数,设置GPIO initdiaplay等等工作.KernelFindMemory,在loader.c中,主要是完成系统内存和用户可使用内存的分配.
4.<pr> 下面的KernelInit.这部分与CPU无关,是内核要完成自己的初始化.HeapInit->InitMemoryPool->ProcInit->SchedInit依次调用以上函数.HeapInit初始化内核的堆.InitMemoryPool初始化内存池.ProcInit初始化系统进程.SchedInit初始化系统调度,SchedInit调用SystemStartupFunc,实现了内核监视线程Monitor1.最后在用户应用程序之前实现OEMIoControl(IOCTL_HAL_POSTINIT, NULL, 0, NULL, 0, NULL);
至此整个系统的启动完成.nk.exe被创建,然后就是filesys.exe(SystemStartupFunc中创建RunApps线程),实现文件系统,由filesys.exe完成注册表的初始化.
这里只是简单的分析了一下内核启动流程中比较重要的函数.因为整个系统的启动是很复杂的过程,我们不可能也完全没有必要了解所以,只要掌握其关键点即可.

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tracera/archive/2008/04/10/2277947.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: