520 likes | 761 Views
国家 ASIC 系统工程技术研究中心 SOC 系统部. Windows CE 操作系统. BootLoader 的设计与实现. 重定位问题??. 定义 1. 当编译器或汇编器编译一个单独的模块时,它不知道这个模块会被加载到内存的何处,这时编译器输出代码时就遵循一个惯例,编译每个模块都是从地址 0 开始。当这个模块真正装入内存时,他一般不可能从 0 开始,这时就要一个地址转换的过程,称之为重定位. 重地位 分类. 1 )静态重定位 在程序被加载到内存之前已经知道了它将要加载到内存的开始地址,这样就可以事先进行地址转换,把相对地址转换成绝对地址。
E N D
国家ASIC系统工程技术研究中心 SOC系统部 Windows CE操作系统 BootLoader的设计与实现
重定位问题?? • 定义 1. 当编译器或汇编器编译一个单独的模块时,它不知道这个模块会被加载到内存的何处,这时编译器输出代码时就遵循一个惯例,编译每个模块都是从地址0开始。当这个模块真正装入内存时,他一般不可能从0开始,这时就要一个地址转换的过程,称之为重定位 LOGO
重地位 分类 1)静态重定位 在程序被加载到内存之前已经知道了它将要加载到内存的开始地址,这样就可以事先进行地址转换,把相对地址转换成绝对地址。 2)动态重地位 作业装入内存后所有的地址仍然是相对地址,将相对地址转换成绝对地址的过程被推迟到程序指令要真正执行时进行。动态重定位要硬件--重定位寄存器支持 LOGO
内容安排 1 BootLoader概述 1.1 BootLoader的概念 1.2 BootLoader的操作模式 1.3 嵌入式系统的引导模式 1.4 BootLoader的功能特性 ★2 BootLoader的体系结构 2.1 BooterLoader 的架构 2.2 BooterLoader 的支持库 LOGO
内容安排 3 BootLoader的代码的组件和执行流程 3.1 BooterLoader代码组件 3.2 BooterLoader代码的执行流程 4 BootLoader开发前的硬件考虑 4.1 考虑的原因 4.2 考虑的因素 4.3 基于s3c2440的开发板 ★5 BootLoader的设计与实现 5.1 构建创建环境 LOGO
内容安排 5.2 startup函数的设计与实现 5.3 主函数main的设计与实现 5.4 控制流函数的设计与实现 5.5 调试函数的设计与实现 5.6 以太网控制器相关的函数 5.7 写FLASH函数的设计与实现 5.8 镜像下载函数的设计与实现 5.9 时间相关函数的设计与实现 5.10 配置文件 LOGO
1 BootLoader概述 • 1.1 BootLoader的概念 ◆ BootLoader的描述 操作系统内核运行前运行地一段小程序 。初始化硬件、建立内存空间映射图,为OS的运行准备好正确的环境。带有BootLoader的固态存储介质典型空间分配如图1所示 ◆ BootLoader的功能 ● 引导系统 ● 加载操作系统 图1 LOGO
1.2 BootLoader的操作模式 ◆ 启动加载模式 又叫自主模式,BootLoader将预先存储在永久固态介质上 的OS镜像加载到RAM中运行。 ◆ 下载模式 目标机通过串口、网口或者USB从主机端下载镜像到目标机端点RAM中,然后再被 BootLoader 写到目标机上的FLASH 类固态存储设备中 LOGO
1.3 嵌入式系统的引导模式 ◆ BootLoader引导模式 优点:可以轻松实现内核升级 ◆ 非BootLoader引导模式?? 操作系统事先写入到CPU开始执行的地址位置,或者在CPU开始执行的地址位置写入一个RESETVECTOR变量,由它来定义一个跳转地址,指向操作系统的起始位置 LOGO
1.4 BootLoader的功能特性 并非所有平台都支持下面列出的功能特性,但大部分特性对所有平台来说都是有用的 ◆ BootLoader典型特性 ● BootLoader应驻留在FLASH等永久存储介质里 ● BootLoader应提供消息反馈机 ● 尽量使用微软提供的支持库开发BootLoader ● BootLoader要能加载BootLoader镜像和OS镜像,对OS镜像要既能从RAM加载,也能从FLASH加载 ● 提供数据正确性保障 ● 要和OAL共享相同的硬件初始化代码 LOGO
◆ BootLoader的附加功能特性 ● 在BootLoader的输出信息中,应包含版本、开发者和构建日期等信息 ● 设计一个序列化的功能选项菜单,为用户开发提供便利 ● 支持自更新 ● 与PB协调工作,实现复位下载,复位跳转和自更新 ● 提供一种监视和反馈机制,监控下载过程 ● 在其中尽肯能的包含以太网调试驱动 LOGO
2 BootLoader的体系结构 • 2.1 BooterLoader 的架构 BooterLoader的结构如图2所是: 图2 LOGO
◆ 五个组成部分 BLCommon库,OEM代码,Eboot库,EDBG驱动,以及存储管理 ● OEM代码必须由用户自己编写 ● EDBG驱动,以及存储管理可能需要修改,视具体情况 ● BLCommon库和Eboot库完全不用改 ◆ OEM代码 用户根据自己的平台特点编写或修改的代码 ● 存放路径为: %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Eboot LOGO
2.1 BooterLoader 支持库 微软提供的众多静态库中,BootLoader中常用的有以下所述的四种 ◆ BLCommon库 ● 存放路径: %_WINCEROOT%\Public\Common\Oak\Drivers\Ethdbg\Blcommon ● 功能:把bootloader搬运到RAM里以获得更快的访问速度,即重定位BooterLoader在RAM中的位置,解码.bin文件的内容、验证校验及加载跟踪过程,调用OEM代码处理硬件初始化等 ● Blcommond代码的入口点:BootloaderMain LOGO
◆ Eboot库 ● 存放路径: %_WINCEROOT%\Public\Common\Oak\Drivers\Ethdbg\Eboot ● 功能:为BooterLoader提供DHCP、TFTP和UDP服务 DHCP是动态主机分配协议 TFTP是简单文件传输协议 UDP是用户数据协议 ◆ EDBG驱动 是一组以太网卡的驱动程序代码 ● 存放路径: %_WINCEROOT%\Public\Common\Oak\Drivers\Ethdbg ● 功能:提供常用以太网卡驱动,为BootLoader和OS的通信提供通用接口 ● WINCE5.0下已有的驱动: NE2000 、CS8900、RTL8139、SMC9000、DEC21140等 LOGO
◆ 存储管理 ● BootPart部分存放路径: %_WINCEROOT%\Public\Common\Oak\Drivers\Ethdbg\BootPart ● 功能:辅助BootLoader进行分区管理,存储设备读 /写等操作和管理 ● Flash FMD部分是用户自己编写的,存放在: %_WINCEROOT%\Platform\<Hardware Platform Name>\Src\Eboot LOGO
3 BootLoader的代码的组件和执行流程 • 2.1 BootLoader代码组件 由两部分组成 ● OEM启动代码 汇编编写,最先执行,完成对CPU时钟频率,内部寄存器,高速缓存及其他一些硬件相关功能的初始化。 ● OEM主代码 C语言编写,BootLoader的主要代码区,通常包含镜像下载,串口调试,写Flash(可选),硬件监视器(可选)等几部分的代码 LOGO
BootLoader代码的控制流程如图3所示: 图3 LOGO
2.1 BootLoader代码的执行流程 根据BootLoader的任务来划分,可将其代码的执行分为两个阶段: ◆ Stage1 完成对CPU的初始化,地址的映射,然后跳到main函数 ◆ Stage2 数据的重定位,平台的初始化(主要是各种外围模块),镜像的加载 整个BootLoader代码的执行过程,用交互图表示如下, LOGO
系统初始化函数 BootLoader代码的执行过程的交互图 图4 LOGO
4 BootLoader开发前的硬件考虑 4.1 考虑的原因 ◆ 硬件密切相关 ◆ 不同操作系统BootLoader的功能相同,但实现方式不同 每种不同的 CPU 体系结构都有不同的 Boot Loader。有些 Boot Loader 也支持多种体系结构的 CPU,比如 U-Boot 就同时支持 ARM 体系结构和MIPS 体系结构。除了依赖于 CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种 CPU 而构建的,要想让运行在一块板子上的 Boot Loader 程序也能运行在另一块板子上,通常也都需要修改 Boot Loader 的源程序。 LOGO
5 BootLoader的设计与实现 以下设计过程中的代码均是以s3c2440BSP为例 5.1 构建创建环境 ● 为要新建的BootLoader创建一个新的目录,路径为: %_WINCEROOT%\Platform\MyPlatform\Src\BootLoader\Eboot MyPlatform是要创建BootLoader的平台名 ● 创建命令行环境 为BootLoader提供所需要的静态库 ● 创建SOURCES文件和makefile文件 SOURCES文件为编译器提供编译信息 makefile文件用于驱动编译过程 LOGO
5.2 startup函数的设计与实现 ◆ BootLoader的入口代码 最早编写的BootLoader函数,汇编编写,存放路径如下: %_WINCEROOT%\Platform\MyPlatform\Src\BootLoader\Eboot MyPlatform是要创建BootLoader的平台名,文件扩展名为.s ◆ s3c2440的startup函数 在这函数中主要完成的工作是对CPU的初始化,地址映射。2440BSP中的startup.s函数的流程图如图5所示: LOGO
图5 LOGO
◆ 5.3 主函数main的设计与实现 由Startup函数调用,C语言代码的入口点,在该函数中调用Blcommon库中的BootLoaderMain函数。2440BSP中主函数为main ● main函数的代码 void main(void) { // Clear LEDs. // OEMWriteDebugLED(0, 0x3); // Common boot loader (blcommon) main routine. // BootloaderMain(); // Should never get here. // SpinForever(); } LOGO
● BootLoaderMain函数的代码 BootLoader 的主控函数,完成的下工作通过图6所示的流程可以看出。 ● OEMWriteDebugLED函数的代码 开发者根据自己的平台特点编写该代码,不是必须的。 2440BSP包下的示例代码如下: void OEMWriteDebugLED(UINT16 Index, DWORD Pattern) { volatile S3C2440A_IOPORT_REG *s2440IOP = (S3C2440A_IOPORT_REG *)OALPAtoVA(S3C2440A_BASE_REG_PA_IOPORT, FALSE); // The S24x0X01 Eval platform supports //4 LEDs.. // s2440IOP->GPFDAT=(s2440IOP->GPFDAT & 0xf) | ((Pattern & 0xf)<<4); } 图6 LOGO
◆ 5.4 控制流函数的设计与实现 BootLoader的控制流函数有以下几个: ◆ OEMDebugInit () ◆ OEMPlatformInit () ◆ OEMPreDownload () ◆OEMLaunch () 这几个函数是BootLoader主控函数BootLoaderMain的重要组成部分,它们共同完成了内核代码的引导和加载 LOGO
◆ OEMDebugInit ()函数 该函数的主要作用: ● 初始化调试端口,通常识异步串行通信口(UART) 实例代码: BOOL OEMDebugInit(void) { // Set up function callbacks used by blcommon. // g_pOEMVerifyMemory = OEMVerifyMemory; // Verify RAM. g_pOEMMultiBINNotify = OEMMultiBINNotify; // Call serial initialization routine (shared with the OAL). // OEMInitDebugSerial(); return(TRUE); } LOGO
◆ OEMPlatformInit ()函数 该函数和硬件高度相关,主要作用: ● 初始化RTC(Real Time Clock)或任何被OEMEthGetSecs函数使用的和时间保存相关的设备 ● 初始化显示屏,此为可选项。如开机显示LOGO ● 初始化Flash或Flash控制器。 ● 如果网络设备挂载在PCI总线上,初始化主PCI桥,对总线进行枚举来确定网络控制器以便进行镜像下载,配置PCI的硬件需求来使能网络控制器 ● 如果网络设备挂载在PCMCIA总线上,则初始化PCMCIA桥或是控制器 ● 初始化网络控制器,以便进行镜像下载 2440BSP中OEMPlatformInit函数的流程图如下: LOGO
◆ OEMPreDownload ()函数 该函数的主要作用: ● 为PB下载镜像产生一个唯一的目标硬件平台名,该平台名一般是与网络接口卡的MAC地址相关联的静态文本字符串 ● 从DHCP(Dynamic Host Configuration Protocol)服务获得一个动态IP地址,或是直接分配一个静态IP 地址 ● 初始化TFTP传输协议或是PB下载协议。 LOGO
◆OEMLaunch ()函数 该函数的主要任务: ● OEMLaunch可以用在Eboot.lib中定义的EbootWaitForHostConnect函数,等待PB发送关于启动什么服务和从哪个端口启动服务 ● 检查EbootWaitForHostConnect的返回值,然后将端口信息和IP保存到DriverGlobals参数里,后面OAL将会用到这些 ● 跳转到 dwLaunchAddr参数指定的第一条指定点地址,该指令就是BootLoader镜像的Startup函数 2440BSP中OEMLaunch函数的流程图如下: LOGO
OEMLaunch函数流程 图8 LOGO
◆5.5 调试函数的设计与实现 BootLoader和OAL共享,包含以下几个函数: ◆ OEMInitDebugSerial () ◆ OEMReadDebugByte() ◆ OEMWriteDebugByte() ◆ OEMWriteDebugString() 实现这些函数的文件路径: MyPlatfor\Src\Kernel\Oal\debug.c LOGO
◆ OEMInitDebugSerial ()函数 初始化调试端口,一般是异步串口(UART) ◆ OEMReadDebugByte ()函数 从调试端口读取一个字节 ◆ OEMWriteDebugByte ()函数 向调试端口输出一个字节 ◆ OEMWriteDebugString() 函数 向调试端口输出一个字符串 LOGO
◆ 5.6 以太网网控制器相关的函数 主要有以下几个函数: ◆ OEMEthGetFrame() ◆ OEMEthSendFrame() ◆ EbootInitEtherTransport() ◆ EbootGetDHCPAddr() ◆ EbootEtherReadData () ◆ EbootWaitForHostConnect () ◆ OEMEthGetFrame()函数 //从网卡读取数据 BOOL OEMEthGetFrame(PUCHAR pData, PUSHORT pwLength) { return(pfnEDbgGetFrame(pData, pwLength)); } pfnEDbgGetFrame是在OEMPlatformInit函数中调用的InitEthDevice函数中初始化的 LOGO
◆ OEMEthSendFrame()函数 //写数据到网卡 BOOL OEMEthSendFrame(PUCHAR pData, DWORD dwLength) { BYTE Retries = 0; while (Retries++ < 4) //允许重发4次 { if (!pfnEDbgSendFrame(pData, dwLength)) return(TRUE); EdbgOutputDebugString("INFO: OEMEthSendFrame: retrying send (%u)\r\n", Retries); } return(FALSE); } pfnEDbgSendFrame是在OEMPlatformInit函数中调用的InitEthDevice函数中初始化的 LOGO
◆ EbootInitEtherTransport()函数 初始化以太网镜像下载 ◆ EbootGetDHCPAddr()函数 通过DHCP获取IP地址 ◆ EbootEtherReadData ()函数 从传输端口读取指定数目的数据到调用者指定的缓冲区中。该函数最终调用的还是函数OEMEthGetFrame ◆ EbootWaitForHostConnect ()函数 与工作站上的PB通信,获取EDBG_OS_CONFIG_DATA 结构体内容。 EDBG_OS_CONFIG_DATA的具体内容如下: LOGO
typedef struct _EDBG_OS_CONFIG_DATA { UCHAR Flags; // Flags that will be used to determine what //features are // enabled over ethernet (saved in driver globals //by bootloader) #define EDBG_FL_DBGMSG 0x01 // Debug messages #define EDBG_FL_PPSH 0x02 // Text shell #define EDBG_FL_KDBG 0x04 // Kernel debugger #define EDBG_FL_CLEANBOOT 0x08 // Force a clean boot UCHAR KitlTransport; // Tells KITL which transport to start // The following specify addressing info, only valid if the corresponding // flag is set in the Flags field. DWORD DbgMsgIPAddr; USHORT DbgMsgPort; DWORD PpshIPAddr; USHORT PpshPort; DWORD KdbgIPAddr; USHORT KdbgPort; } EDBG_OS_CONFIG_DATA, *PEDBG_OS_CONFIG_DATA; LOGO
◆ 5.7 写FLASH代码的设计与实现 BootLoader中操作FLASH存储器的函数,包含以下几个函数: ◆ OEMContinueEraseFlash () ◆ OEMFinishEraseFlash () ◆ OEMIsFlashAddr () ◆ OEMStartEraseFlash() ◆ OEMWriteFlash () ◆ OEMContinueEraseFlash ()函数 void OEMContinueEraseFlash(void) //下载过程中不断调用该函数,继续对 // FLASH进行擦除操作 { //2440BSP尚未空函数,自己编写 } LOGO
◆ OEMStartEraseFlash ()函数 //初始化FLASH存储器的擦除过程 BOOL OEMStartEraseFlash(DWORD dwStartAddr, DWORD dwLength) { //2440BSP中尚为空,直接返回TRUE,需要自己编写 return(TRUE); } ◆ OEMFinishEraseFlash () //写OS镜像时调用,以所需要的FLASH存储空间是否完全擦除 BOOL OEMFinishEraseFlash(void) { //2440BSP中尚为空,直接返回TRUE,需要自己编写 return(TRUE); } LOGO
◆ OEMIsFlashAddr ()函数 //判断地址是FLASH地址还是RAM地址 BOOL OEMIsFlashAddr(DWORD dwAddr) { //2440BSP中尚为空函数,直接返回FLASE,表示不在FLASH内 //需要自己编写 return(FALSE); } ◆ OEMWriteFlash ()函数 //向FLASH中写OS镜像 BOOL OEMWriteFlash(DWORD dwStartAddr, DWORD dwLength) { //2440BSP尚为空,需要自己编写 return(TRUE); } LOGO
◆ 5.8 镜像下载代码的设计与实现 镜像下载函数主要有以下几个: ◆ OEMMapMemAddr() ◆ OEMReadData () ◆ OEMShowProgress () ◆ OEMMapMemAddr()函数 //为要烧写到FLASH中的镜像提供一个位于RAM中的临时缓冲区,存放当前已下载的镜像,以便下载完成后一并写入FLASH LPBYTE OEMMapMemAddr(DWORD dwImageStart, DWORD dwAddr) { if (g_ImageType & IMAGE_TYPE_STEPLDR) { dwAddr = (FILE_CACHE_START + (dwAddr - STEPLDR_RAM_IMAGE_BASE)); return (LPBYTE)dwAddr; } else LOGO
if (g_ImageType & IMAGE_TYPE_LOADER) { dwAddr = (FILE_CACHE_START + (dwAddr - EBOOT_RAM_IMAGE_BASE)); return (LPBYTE)dwAddr; } else if (g_ImageType & IMAGE_TYPE_RAWBIN) { OALMSG(TRUE, (TEXT("OEMMapMemAddr 0x%x 0x%x\r\n"),dwAddr,(FILE_CACHE_START + dwAddr))); dwAddr = FILE_CACHE_START + dwAddr; return (LPBYTE)dwAddr; } return (LPBYTE)dwAddr; } LOGO
◆ OEMReadData ()函数 //镜像下载所用的传输函数 BOOL OEMReadData(DWORD dwData, PUCHAR pData) { //判断下载方式 if ( g_bUSBDownload == FALSE ) { ret = EbootEtherReadData(dwData, pData); } else { ret = UbootReadData(dwData, pData); } return ret; } LOGO
◆ OEMShowProgress ()函数 //提供下载进度指示 void OEMShowProgress(DWORD dwPacketNum) { //2440BSP中该函数尚为空,需要自己编写 OALMSG(OAL_FUNC, (TEXT("+OEMShowProgress.\r\n"))); } LOGO
◆ 5.9 时间相关函数的设计与实现 BootLoader用来返回从某个过去的时间到现在的秒数用到如下两个函数 ◆ OEMEthGetSecs() ◆ OEMGetRealTime () ◆ OEMEthGetSecs()函数 DWORD OEMEthGetSecs(void) //返回从某个特定时间到目前为止的秒数 { SYSTEMTIME sTime; OEMGetRealTime(&sTime); //内核调用函数,获取RTC,自己编写 return((60UL * (60UL * (24UL * (31UL * sTime.wMonth + sTime.wDay) + sTime.wHour) + sTime.wMinute)) + sTime.wSecond); } LOGO
◆ OEMGetRealTime ()函数 //获取系统时间 static BOOL OEMGetRealTime(LPSYSTEMTIME lpst) { volatile S3C2440A_RTC_REG *s2440RTC = (S3C2440A_RTC_REG *)OALPAtoVA(S3C2440A_BASE_REG_PA_RTC, FALSE); do { lpst->wYear = FROM_BCD(s2440RTC->BCDYEAR) + 2000 ; lpst->wMonth = FROM_BCD(s2440RTC->BCDMON & 0x1f); lpst->wDay = FROM_BCD(s2440RTC->BCDDAY & 0x3f); lpst->wDayOfWeek = (s2440RTC->BCDDATE - 1); lpst->wHour = FROM_BCD(s2440RTC->BCDHOUR & 0x3f); lpst->wMinute = FROM_BCD(s2440RTC->BCDMIN & 0x7f); lpst->wSecond = FROM_BCD(s2440RTC->BCDSEC & 0x7f); lpst->wMilliseconds = 0; } while (!(lpst->wSecond)); return(TRUE); } LOGO
●SYSTEMTIME结构体的定义如下: typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; 星期(如Sunday = 0,Monday = 1) WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME; ●OEMGetRealTime是OAL中实现的函数,要被内核调用的,函数必须是可重入函数 LOGO
◆ 5.10 配置文件的设计与实现 ●sources文件 和普通的sources有差异 ●boot.bib文件 ●makefile.inc文件 可选的伪目标文件。用户定制自己的规则 LOGO