学者之家电子论坛--打造电子工程师共同致富的平台's Archiver

robinyuan 发表于 2010-1-4 22:44

微软Windows CE 5.0 板级支持包,引导程序和内核启动顺序

[color=Red][b]微软Windows CE 5.0 板级支持包,引导程序和内核启动顺序[/b][/color]
信息来源:[url]http://omale.blogbus.com/logs/38322258.html[/url]

概述
学习关于当开发OEM抽象层(OAL)和运行内核时所进行的初始化,底层的启动顺序和硬件平台功能。启动顺序是开发OAL和BSP的一部分,开发过程必须正确的实现初始化CPU和芯片上下的设备。
本文中描述的过程是需要实现的最小功能集合。有可能需要在合适的地方实现用户定义的功能,取决于你正在开发的设备和外设。尽管Windows CE支持的不同CPU架构的硬件平台启动顺序非常相似,为了简单起见,本文讨论ARM内核的启动顺序。
内容
介绍
BSP开发过程概述
引导程序和内核启动顺序概述
引导程序的启动顺序
内核的启动顺序
结论
介绍
建立基于Windows CE的设备首先要创建BSP。你可以从你自己的,或者微软或第三方提供的BSP开始开发你的BSP。充分利用现存的BSP可以大大减少开发工作的数量。
不管你选择从什么地方开始,开发过程和引导程序跟内核的启动过程几乎是一样的。本文描述这个启动过程和你开发用来启动Windows CE的引导程序和内核所必须实现的功能。也提供了供引导程序和内核调用的每个功能中推荐实现的功能。因为有一些功能非常相近,内核启动开发工作可以充分利用开发引导程序时所做的工作。
BSP开发过程概述
像很多开发工作一样,开发BSP也涉及到上层和底层的工作。例如,上层你必须选择硬件平台,底层你必须开发引导程序和为内核开发OEM抽象层(OAL)。下图显示了为BSP开发底层软件所需的上层步骤。

你首先从选择或开发BSP所要运行的硬件平台开始。这包括开发一块带有可以运行Windows CE操作系统的CPU的开发板,选择与之交互的外设,还有包含用来支持BSP开发调试的接口。例如,对某类消费者,你有可能不需要支持串口连接,但是串口连接通常对底层软件开发非常重要。你可以考虑在开发时为你的设备组装一个调试插头用来与调试板交互,而在产品发布时把它拿掉。
当你确定并建立了硬件平台之后,你必须开发一个用来在此平台上运行Windows CE的BSP。通常,你可以修改一个来自于微软或第三方的BSP,这样可以大大降低BSP开发的工作量。从微软的BSP开始建立新的BSP的过程叫做BSP克隆(cloning)。你可以从Windows CE的帮助文档中找到这一过程的信息。如果没有一个可用的BSP,你必须从头开始完全自己开发BSP。由于完全自己开发BSP工作量相当大,微软建议你从微软或第三方的BSP开始开发新的BSP。
从现在起,你必须转到底层开发上来,你要开发一个可以放在设备上的持久存储中的引导程序。引导程序的主要功能是初始化足够的硬件和CPU来允许硬件与开发环境交互来用来下载给予Windows CE的运行时映像。你也可以扩展引导成功的功能来实现你的设备所需要的任何其他功能。下文会讨论你必须在引导程序中实现的底层启动步骤。
当引导程序开发好之后,你使用硬件供应商提供的工具把它下载并写到设备上的持久存储中。然后你就开始BSP中OAL部分的开发工作。OAL的部分工作是实现用来启动Windows CE内核的引导启动函数。开发OAL是个一步一步的过程,实现启动函数实现用来初始化设备上的硬件的代码来准备运行Windows CE内核。你在开发OAL的时候可以利用或共享在引导程序中实现的函数。一旦你开发,下载并调试完了OAL,一个小的Windows CE内核就可以在硬件平台上运行了。
下一步,你增加对外部设备的支持。为此你要一个一个的增加并调试外设的驱动程序。如果有多个开发者在开发驱动程序,驱动程序可以并行的开发并且添加到BSP上去。如果你使用片上系统,你可以从微软或者第三方获得并添加驱动。你可以在开发驱动之初利用这些驱动,这取决于你选择的外设。
当设备驱动程序开发完之后,你就有了一个BSP,它包含OAL和你希望在硬件平台上支持的每个外设的驱动程序。下一步你要计划实现电源管理。
电源管理是开发驱动程序和BSP中重要的一个部分。当你开始计划电源管理系统的时候,它包括确定你的设备多大程度上依赖电池,你要使用电源管理器来帮助你实现电源管理功能。尽管电源管理并不是本文的焦点,你可以在Windows CE帮助和MSDN中找到关于电源管理的详细文档。那里你将会找到如何让每个外设知道电源管理的细节,还有驱动程序如何与电源管理器和OAL交互。
当你开发完OAL,编写好设备驱动程序,实现了电源管理之后,你必须测试每一个模块。Windows CE测试工具包(CETK)提供大量的测试方法来帮助你完成这一过程。你可以在Windows CE帮助和MSDN中找到CETK的信息。
开发BSP的最后一步是建立软件开发工具包(SDK)并把BSP打包到.msi安装文件里这样它就可以被其他人安装。SDK是开发者用来为某个OS编写应用程序的一系列头文件,库,相关联的文件,运行时文件,OS扩展和帮助文档。SDK的内容允许开发者在你的OS得运行时镜像上建立和调试应用程序。Windows CE提供用来从BSP建立SDK的SDK向导,还有用来把BSP打包成.msi安装文件的导出向导。
如果需要BSP开发过程中所有步骤的详细信息,可以查阅Windows CE文档。
引导程序和内核启动顺序概述
很大程度上,引导程序和内核共享同样的Startup函数,还有一些后来需要调用的函数。因此,你可以在内核启动顺序中利用或共享很多最初在引导程序启动顺序中使用的代码。
下表显示了基于ARM的引导程序和内核使用的启动函数。内核启动顺序调用了一些安装在private目录下的共享源代码中的函数。因为如此,你必须按照下列的顺序实现函数。在每个函数内,你可以自定义每个模块初始化的先后顺序,取决于你的CPU,内存和外设集。
引导程序启动顺序   内核启动顺序   
Startup()   Startup()   
EbootMain()   KernelStart()  
  BootloaderMain()   ARMInit()
  OEMDebugInit()    OEMInitDebugSerial()
  OEMPlatformInit()    OEMInit()
  OEMPreDownload()   KernelInit()
   Download   HeapInit()
  OEMLaunch()    InitMemoryPool()
       ProcInit()
       SchedInit()
     FirstSchedule()  
      SystemStartupFunc()
      IOCTL_HAL_POSTINIT
粗体字的函数是OEM需要实现的函数。
Platform Builder和MSDN的文档有关于一步步建立引导程序和OAL的详细文档。
引导程序的启动顺序
下表显示了引导程序的启动顺序。
Boot Loader Startup Sequence   
Startup()   
EbootMain()  
  BootloaderMain()
  OEMDebugInit()
  OEMPlatformInit()
  OEMPreDownload()
   Download occurs
  OEMLaunch()
粗体字的函数是OEM需要实现的函数。
Startup() -> Startup.s
引导程序Startup函数是硬件复位,运行时复位或系统唤醒之后执行的第一段代码。这段函数执行下列任务:
·把设备设置到超级用户模式
·为下列硬件进行必要的初始化:
·CPU
·内存控制器
·系统时钟
·串口
·缓存
·转换高速缓存 (TLBs)
当唤醒事件产生时,Startup函数为唤醒事件读取保存的地址,然后跳转到那个位置。如果不是唤醒事件,引导程序就会被复制到内存里,然后运行。
Startup的代码一般在Startup.s文件里在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader 或者 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader\Eboot 目录中。Startup的代码一般使用汇编语言因为这是运行在设备上的第一段代码并且需要做一些底层的硬件初始化和与CPU交互。
因为每种CPU和硬件平台需要不同的初始化,你必须修改Startup.s文件,依赖于你所使用的CPU。硬件供应商会提供CPU初始化信息。Startup代码可以与OAL共享并且可以在后面的那些部分的开发过程中被重用。
EbootMain() -> Main.c
取决于你用来开发BSP的CPU,EbootMain通常是C语言代码的入口点。EbootMain通常位于Main.c文件中在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader 目录或者 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader\Eboot目录中。尽管Main.c可以执行任意设备上的初始化操作,它必须以调用BLCOMMON库中的入口BootloaderMain点结束。
BootloaderMain() -> Blcommon.c
BLCOMMON库的入口点是BootloaderMain函数。BootloaderMain是BLCOMMON库中的主要调用函数,它为引导程序实现了通用和持久的框架。BLCOMMON库的源代码位于Blcommon.c文件中,在%_WINCEROOT%\Public\Common\Oak\Drivers\Ethdbg 目录中。
BLCOMMON库提供了如下的功能:
·把引导程序重新部署到RAM中来更快的执行
·解码.bin文件内容
·检查校验和
·记录加载进度
BLCOMMON库调用精心定义的OEM函数,遍布整个过程来处理硬件平台或者某个特定方案的定制。
OEMDebugInit() -> Main.c
当引导程序启动之后,OEMDebugInit是BootloaderMain调用的第一个函数。一般这个调用是用来初始化串口UART调试输出。OEM通常选择调用OEMInitDebugSerial函数初始化串口UART调试信息。
当OEMDebugInit结束后,Windows CE标语会出现指示此接口已经可用。在OEMDebugInit完成后,OEM函数接口可以使用OEMWriteDebugString,OEMWriteDebugByte和OEMReadDebugByte。
OAL和引导程序可以共享这个实现。
OEMDebugInit位于Main.c文件,在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader 或者 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader\Eboot 目录。
OEMPlatformInit() -> Main.c
OEMPlatformInit是在OEMDebugInit函数结束Windows CE标语出现后BLCOMMON调用的第二个函数。
OEMPlatformInit是OEM硬件平台初始化时钟,PCI接口或者NIC接口的函数。OEMPlatformInit也是打断启动过程,显示引导程序菜单的地方。用来下载映像的NIC接口假定有下面的函数。
OEMPlatformInit位于Main.c文件,在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader 或者 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader\Eboot 目录。
OEMPreDownload() -> Main.c
OEMPreDownload也在真正的下载运行时映像之前在BLCOMMON里面被调用。这个函数得到设备的IP地址并且联接到开发的工作站。你可以自定义OEMPreDownload来提醒用户反馈一个静态的IP或者跳过下载跳到一个已经存在于设备上的映像。首选的方法是使用Platform Builder提供的设置。
OEMPreDownload的返回值一般通过调用EbootInitEtherTransport来获得,它从Platform Builder获得信息。你可以通过EbootInitEtherTransport中从Platform Builder传递到引导程序的选项选择下载还是启动一个已经存在的运行时镜像。你也可以让OEMPreDownload读取硬件开关来决定所需要的返回值。
下表显示了OEMPreDownload的可能的返回值。
The following tables shows the possible return codes for the OEMPreDownload function.
返回值描述
BL_DOWNLOAD (0)以太网传输必须调用EbootInitEtherTransport来初始化。
BL_JUMP (1)BLCOMMON中的下载函数被跳过。
BL_ERROR (-1)出错。
如果一个签名的运行时环境被下载,签名会在调用OEMLaunch之前被校验。
OEMPreDownload位于Main.c文件,在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader 或 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader\Eboot 目录。
OEMLaunch() -> Main.c
OEMLaunch是BLCOMMON中所调用的最后一个函数,它负责跳转和执行运行时镜像。
OEMLaunch可以使用定义在Eboot.lib中的EbootWaitForHostConnect函数来等待返回参数,如果你选择被动KITL或者没有在运行的时候连接到目标设备。其它参数被设置用来定义传输机制,用串口,USB还是以太网连接。
OEMLaunch跳转到dwLaunchAddr参数指定的第一条指令,它是运行时镜像的Startup函数的位置。Startup调用下面定义的KernelStart函数,启动地址可以从.bin文件中获得并且被存储在最后一个块的length字段内。
到现在为止,引导程序已经完成了执行,并且OAL的Startup函数被调用。
OEMLaunch位于Main.c文件内,在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader 或 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Bootloader\Eboot 目录中。
内核的启动顺序
下表显示了基于ARM的内核启动时所使用的启动函数。内核启动顺序调用了一些安装在private目录下的共享源代码中的函数。因为如此,你必须按照下列的顺序实现函数。在每个函数内,你可以自定义每个模块初始化的先后顺序,取决于你的CPU,内存和外设集。
内核启动顺序   
Startup()   
KernelStart()  
  ARMInit()
   OEMInitDebugSerial()
   OEMInit()
  KernelInit()
   HeapInit()
   InitMemoryPool()
   ProcInit()
   SchedInit()
FirstSchedule()  
  SystemStartupFunc()
  IOCTL_HAL_POSTINIT
粗体字的函数是OEM需要实现的函数。
ARM内核启动概述
Startup() -> Startup.s
内核的Startup函数是引导程序跳转到已经位于设备上的运行时镜像后执行的第一段代码,或者开发时的引导程序被移除,产品引导程序被烧到设备上,内核的Startup被产品引导程序调用。
尽管在这个例子中内核Startup与引导程序的Startup函数位于不同的源文件中,它们的代码是相同的并且可以被共享。Startup负责检测硬件或者运行时重置,或者检测系统的唤醒。内核Startup函数执行必要的硬件初始化,包括初始化CPU,内存控制器,系统时钟,串口和缓存,并且检测系统何时被唤醒。
内核Startup代码一般位于Startup.s文件内,在 %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Kernel\OAL 目录。这个目录也可以共享引导程序的Startup.s代码。Startup的代码一般使用汇编语言因为这是运行在设备上的第一段代码并且需要做一些底层的硬件初始化和与CPU交互。
因为每种CPU和硬件平台需要不同的初始化,你必须修改Startup.s文件,依赖于你所使用的CPU。硬件供应商会提供CPU初始化信息。
内核Startup负责计算OEMAddressTable的物理地址并把它加载到内存里供KernelStart函数使用。Startup函数为内核访问ROM和DRAM建立所有硬件平台和特定CPU相关的配置,然后跳转到KernelStart。Startup函数必须初始化所有处理器相关的缓存或内存管理单元(MMU),但并不启用它们。
KernelStart() -> Armtrap.s (Private)
KernelStart函数是内核的主入口点。KernelStart根据MemoryMap数组的内容初始化第一级页表并且启用MMU和缓存。在MMU初始化之前内核的符号地址并不是合法的,必须经过MemoryMap数组的翻译来获得正确的物理地址。KernelStart也为每一个模式的操作初始化栈,并初始化内核的全局数据。
KernelStart 函数位于Armtrap.s文件,在 %_WINCEROOT%\Private\Winceos\Coreos\Nk\Kernel\ARM 目录。
ARMInit() -> Mdarm.s (Private)
ARM初始化的主函数ArmInit负责调用OEM提供的初始化函数。ARMInit使用下列的函数调用:
·调用 OEMInitDebugSerial 来初始化调试串口
·使用 OEMWriteDebugString 来显示Windows CE标语
·调用OEMInit 来进行硬件平台初始化
ARMInit函数在Mdarm.c文件内,在%_WINCEROOT%\Private\Winceos\Coreos\Nk\Kernel\ARM目录。
OEMInitDebugSerial() -> Mdarm.s (Private)
OEMInitDebugSerial函数初始化设备上的调试串口。调试函数是:OEMDebugInit, OEMWriteDebugString, OEMWriteDebugByte, 和 OEMReadDebugByte
OEMInitDebugSerial的代码与引导程序调用的OEMDebugInit函数非常相似。所以大多数OEMDebugInit的代码可以被共享或重用。
OEMInit() -> Init.c
内核在ARMInit做了最小的初始化之后会调用OEMInit。当OEMInit被调用后,中断就被禁用了内核不能处理异常。OEMInit初始化下列硬件:
·缓存
·中断
·时钟
·所有其他的需要被硬件平台支持的板级外设
注意   如果使用了KITL连接,KITL连接也在OEMInit里面通过调用OALKitlStart函数被初始化。
OEMInit函数包含大多数引导程序中OEMPlatformInit函数所包含的功能。
OEMInit函数是ARMInit的内核调用函数,可以在Init.c文件里面找到。它在%_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Kernel\OAL目录中。
KernelInit() -> Kwin32.c (Private)
KernelInit函数是内核初始化的地方。KernelInit调用函数初始化下列组件:
·堆
·内存池
·内核进程
·调度
当KernelInit返回的时候,内核已经准备通过SystemStatupFunc调度第一个线程
KernelInit函数可以在Kwin32.c文件中找到,它位于 %_WINCEROOT%\Private\Winceos\Coreos\Nk\Kernel 目录。
HeapInit() -> Heap.c (Private)
HeapInit 函数初始化内核堆,它位于Heap.c文件内,在%_WINCEROOT%\Private\Winceos\Coreos\Nk\Kernel 目录。
InitMemoryPool() -> Physmem.c (Private)
InitMemoryPool 函数初始化内核内存池,它在Physmem.c 文件中,位于%_WINCEROOT%\Private\Winceos\Coreos\Nk\Kernel 目录。
ProcInit() -> Schedule.c (Private)
ProcInit 函数初始化内核进程,它在Schedule.c 文件中,位于%_WINCEROOT\Private\Winceos\Coreos\Nk\Kernel目录。
SchedInit() -> Schedule.c (Private)
SchedInit 函数初始化调度系统,它在Schedule.c文件中,位于%_WINCEROOT\Private\Winceos\Coreos\Nk\Kernel目录。
SchedInit 创建 SystemStatupFunc 线程。
FirstSchedule() -> Schedule.c (Private)
FirstSchedule 函数启动调度系统,它在Schedule.c文件中,位于%_WINCEROOT\Private\Winceos\Coreos\Nk\Kernel 目录。
SystemStartupFunc() -> Schedule.c (Private)
当所有必需的初始化已经完成并且系统已经准备调度和运行内核线程时,SystemStartupFunc函数被调用。SystemStartupFunc函数通过调用CreateKernelThread,来通过OEMIoControl函数执行IOCTL_HAL_POSTINIT。
SystemStartupFunc 在Schedule.c 文件中,位于 %_WINCEROOT\Private\Winceos\Coreos\Nk\Kernel 目录。
结论
所有的Windows CE设备开发从建立目标设备的BSP开始。通过从BSP中导出SDK,BSP也是运行在设备上的应用程序的基础。因此,你必须保证上层BSP过程被认真对待底层的引导程序的函数调用和内核被合适的实现。借助本文的信息,你应该对BSP启动过程和让引导程序和内核在设备上运行起来要做的工作有了基本的了解。
更多信息
关于最新的Windows CE信息,请访问Microsoft Web site.
对于联机文档和Windows CE内上下文敏感的帮助,请参考Microsoft Web site.

max 发表于 2010-1-5 14:48

顶~~

lincolnlin 发表于 2010-1-5 17:13

嗯,这个文章写得不错。

页: [1]

Powered by Discuz! Archiver 7.1  © 2001-2009 Comsenz Inc.

302 Found

302 Found


nginx/1.8.0