<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:1; mso-generic-font-family:roman; mso-font-format:other; mso-font-pitch:variable; mso-font-signature:0 0 0 0 0 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} @font-face {font-family:Verdana; panose-1:2 11 6 4 3 5 4 4 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:536871559 0 0 0 415 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:宋体; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} -->
我很庆幸在公司的产品开发过程中并没有受到 Boot Loader 带来的阻力,因为我们采用 MSDOS + Loadcepc 来启动 CE 操作系统。显然这样的幸运不是永远的,所以对 Boot Loader 应该有足够的研究和了解,做到未雨绸缪。
Boot Loader 是定制 Windows CE 操作系统过程中一个重要的开发环节。 Boot Loader 的作用正如名字中的两个单词: Boot ,既引导系统,如果基于 CE 的产品采用 BIOS 实现 硬件 初始化和配置,那么 Boot Loader 只需引导 软件 系统。如果没有采用 BIOS ,那么 Boot Loader 的作用还包括实现 BIOS 的基本功能; Loader ,既加载操作系统,在整个系统正常启动后 Boot Loader 通过不同的方式加载 CE 的内核文件 nk.bin 。当 Boot Loader 把 nk.bin 解压到 RAM 后就把 CPU 控制权交给 CE 内核。 x86 平台的 Boot Loader 种类最多,下面就对 x86 平台的 Boot Loader 做一说明:
x86 ROM Boot Loader
又叫 Rom Boot ,记得以前写过的文章中提到了 Rom Boot 。 Rom Boot 被设计存放在 Flash/EEPROM 中,也就是原来 BIOS 的位置,这样当上电后 CPU 到固定地址执行代码,也就是执行了 Rom Boot 包含的代码,它对整个硬件系统进行初始化和检测,并且支持通过网卡从远程机器上下载 nk.bin 或者从本地 IDE/ATA 硬盘的活动分区中寻找 nk.bin 文件加载。 Rom Boot 的优点就是引导并且加载速度快,而且它自身完成了所有的操作,这样就不用 BIOS 、 MSDOS ,更不用 Loadcepc 了。缺点就是需要 CE 开发者读懂它的源码并修改。 CE 提供了 Rom Boot 的所有源码,读者可以查找标题为 “x86 Source Organization” 的帮助文档,在这个文档中列举了所有相关的目录及内容,另外还列举了四种网卡的驱动 程序 源码所在目录。
x86 BIOS Boot Loader
BIOS Boot Loader 和 MSDOS + Loadcepc 两种方式差不多, BIOS Boot Loader 只是不需要 MSDOS 操作系统,它仍然需要 BIOS 和 FAT 文件系统。下面讲一下采用 BIOS Boot Loader 的系统的引导顺序:系统上电后 BIOS 执行完硬件初始化和配置后, BIOS 检查引导设备的启动顺序,如果引导设备是硬盘、 CF 卡、 DOC ( Disk-On-Chip )一类的存储设备,那么就加载这些存储器上的主引导扇区( Master Boot Sector )中的实模式代码到 内存 ,然后执行这些代码。这里提到的代码被称为主引导记录( MBR )。 MBR 首先在分区表(同样位于主引导扇区)中寻找活动分区,如果存在活动分区,那么加载位于这个活动分区的第一个扇区上的代码到内存,然后执行这些代码。这里提到的活动分区的第一个扇区被称为引导扇区( Boot Sector )。引导扇区上的代码的功能是找到并且加载 BIOS Boot Loader , BIOS Boot Loader 再加载 nk.bin 。引导扇区的源码位于 %_WINCEROOT%PublicCommonOakCspi486BiosloaderBootsector 目录下。有一个现成的引导扇区镜像文件,它的路径为 %_WINCEROOT%PublicCommonOakCspi486BiosloaderDiskimagesSetupdiskBsect.img 。而对于 BIOS Boot Loader , CE 提供了 Setupdisk.144 和 Bootdisk.144 两个文件,以 “.144” 为扩展名的文件的解压我在前面的文章中讲过了。这两个文件解开后都包含了引导扇区和 Boot Loader 的镜像文件。执行 “mkdisk C:” 批处理命令将这两个镜像文件写到磁盘上。 mkdisk 会设置 Boot Loader 的隐藏属性,这样在列出根目录下所有文件时不会显示 Boot Loader 的文件。
MSDOS + Loadcepc
这种方式非常简单,在 MSDOS 启动后再执行 loadcepc.exe ,让 loadcepc 加载 nk.bin 到内存后再把 CPU 控制权交给 CE 内核程序。 loadcepc 在前面的文章中已经讲过了。
下面根据一般的 Boot Loader 源码来分析一下 Boot Loader 的组成:
Boot Loader 由两部分组成: OEM 启动代码( OEM startup code )和主代码( main code )。 OEM 启动代码是最先执行的部分,它的功能是初始化内存寄存器、设置 CPU 频率、初始化高速缓存等。之后它跳转到主代码中执行。一般 OEM 启动代码都是用汇编编写。主代码一般用 C 语言编写,它负责其它所有任务,在执行的同时还能够将执行的相关信息显示在屏幕上。一般添加公司 LOGO 或者其它启动 LOGO 都在此修改。
主代码主要由几个部分组成:镜像下载代码,通过并口或者网卡来实现从远程计算机下载 nk.bin ;串口调试代码,包含对串口的读写函数,用户调用这些函数就可以通过串口在远程计算机和本地计算机之间通信;写 flash 代码,包含写镜像到 flash 的函数;硬件监控代码。
一般的 Boot Loader 的执行流程见下图:
上图中每个函数的功能如下:
StartUp() : CPU 最先执行的函数。也就是启动代码。
BootLoaderMain() :先后调用 KernelRelocate 、 OEMDebugInit 、 OEMPlatformInit 、 OEMPreDownload 等函数。此函数源码文件路径为 %_WINCEROOT%publiccommonoakdriversethdbgblcommon 。
OEMDebugInit() :初始化串口。
OEMPlatformInit() :执行特定平台的初始化工作,如时钟、一些驱动程序。
OEMPreDownload() :做下载前的准备工作。一般用于反馈给用户一些信息。
DownloadImage() :下载 操作系统 镜像到 RAM 或者 Flash 。
OEMLaunch() :负责启动镜像。
OEMReadData() :从远程计算机读取数据。
OEMMapMemAddr() :专用于写 Flash 时使用。因为写 flash 的速度非常慢,所以此函数将 Flash 镜像临时缓冲到 RAM 中。
OEMShowProgress() :从函数名就能看出。
OEMIsFLashAddr() :判断一个地址是否是 Flash 的地址。
OEMFinishEraseFlash() :判断是否完成了擦除 Flash 内容工作。
OEMWriteFlash() :写镜像到 Flash 。
OEMStartEraseFlash() :开始擦除 Flash 。
OEMContinueEraseFlash() :继续擦除 Flash 工作。