
简介面向PXA255处理器的EBOOT启动源码包聚焦嵌入式系统上电后的硬件初始化、内核加载与CF卡烧录机制尤其适合嵌入式底层开发者、系统移植工程师和Bootloader学习者研读。压缩包总计64个文件主体为C源码、头文件与目标文件另含makefile、sources配置、日志以及编译生成的eboot.bin和eboot.nb0整体仅356KB目录紧凑清晰。当前已有99人学习。代码中可见cfdisk、CFLOAD、MSDOS等模块覆盖CF卡控制器驱动、FAT文件系统读取、烧录流程与启动配置同时包含ARM汇编、bib和inc等文件便于梳理从汇编入口到C语言主流程的完整脉络。通过研读该源码可以深入理解EBOOT与CF卡交互的实现细节并能为后续裁剪功能、适配不同存储介质或设计固件升级机制提供直接参考。 做嵌入式这行很多人手里都捏着这样一份老古董压缩包EBOOT.rar_eboot_pxa_pxa255。文件名拆开看就是为Intel XScale PXA255处理器准备的EBOOT引导程序源码或镜像。PXA255这芯片在2005年前后是PDA、车载终端、工控板里的绝对主力跑Windows CE 5.0/6.0几乎是那几年的标配方案。EBOOT作为Windows CE下负责初始化硬件、下载内核镜像、烧写Flash的引导加载程序当年不知道卡了多少人。这篇文章我结合自己那几年在PXA255平台上折腾EBOOT的经验把引导程序的启动流程、网络下载机制、Flash烧写这几块核心内容从头到尾梳理一遍顺便把那些最折磨人的坑也和你说清楚。不管是手里还压着旧项目的工程师还是现在转做嵌入式Linux想了解Bootloader异同的朋友这篇东西应该都能给你点参考。1. EBOOT在Windows CE体系中的定位与PXA255平台特性1.1 EBOOT不是操作系统它是“先遣队”先说清楚一个概念EBOOT全称Ethernet Boot Loader最早是微软在Platform Builder里提供的一套参考Bootloader代码后面逐渐演变成Windows CE下事实上的标准引导方案。它干的活和桌面电脑上的BIOS很像但又不完全一样BIOS完成初始化后把控制权交给操作系统EBOOT除了要把硬件初始化好还要负责把操作系统镜像从开发机通过网络下载到设备内存里运行或者烧写到Flash中长期保存。EBOOT的代码结构里最核心的两个部分是Startup汇编代码和Main函数。Startup负责最底层的CPU初始化包括关闭看门狗、设置异常向量表、初始化内存控制器这部分不跑起来后面全是空谈。Main函数则进入C语言世界完成以太网控制器、Flash芯片、串口、LED指示灯的初始化工作然后根据用户按键或预设标志决定进入AUTOBOOT模式还是下载模式。在当年的BSP结构里EBOOT通常被编译成一个独立的二进制文件用Platform Builder的Make Run-Time Image生成路径一般在WINCE500\PLATFORM\你的平台名字\SRC\BOOTLOADER\EBOOT目录下最终的生成物可以烧到Nor Flash的起始块也可以通过网络用JTAG或更早的Bootloader加载到RAM里运行。1.2 PXA255留给Bootloader的功课PXA255是Intel在2003年前后推出的XScale架构处理器主频200MHz到400MHz基于ARM v5TE指令集。为啥那时候大家都爱拿它做PDA和工控设备主要原因是它的功耗控制做得相当好动态电压频率调整DVFS给了嵌入式设备续航的底气。从Bootloader的角度来看PXA255有四个必须处理的难点。内存控制器初始化是第一步硬仗。PXA255的SDRAM控制器通过MSC0、MSC1这几个寄存器控制片选时序不同厂商的SDRAM颗粒对刷新率、CAS延迟的容忍度不一样参数设错了轻则进不了系统重则突然死机。EBOOT里通常用汇编循环配合延时函数来保证SDRAM稳定。第二块是中断控制器的配置。PXA255的中断控制器寄存器比较多EBOOT阶段要保证所有中断屏蔽只留定时器中断做延时不然一个意外的中断就可能让下载程序死掉。第三块是GPIO复用。PXA255的很多引脚是多功能的比如UART、网卡芯片、Flash的片选都可能在同一个引脚组上打架。EBOOT里要把GPIO配置成正确的Alternate Function这一步错了串口根本没有输出。最后是时钟管理。PXA255可以通过CCCR寄存器调整CPU和内存总线的频率比例EBOOT里为了稳定通常先用默认频率跑起来等系统起来后再动态调节。这些初始化工作完成后EBOOT会通过串口输出类似这样的信息Microsoft Windows CE Ethernet Bootloader Common Library Version 1.4 Built Mar 20 2005 18:12:34 Press [ENTER] to launch image stored in flash, or [SPACE] to enter boot monitor.反正看到这个提示符说明EBOOT已经成功接管了硬件下一步就是等你的指令了。2. EBOOT启动流程逐段拆解2.1 Startup汇编代码里藏着哪些细节EBOOT的启动入口是Startup函数通常写在startup.s或者boot.s里。这段代码不算长三五百行但每一行都直接和硬件打交道。我现在回忆起来最考验耐心的其实是内存初始化的时序等待。; 关闭看门狗 ldr r0, OSMR0 ldr r1, 0xFFFFFFFF str r1, [r0] ldr r0, OWER ldr r1, 0 str r1, [r0] ldr r0, OIER ldr r1, 0 str r1, [r0]这段代码把PXA255的定时器屏蔽寄存器清零防止系统启动过程中OS定时器溢出触发看门狗复位。看门狗在设计上是“程序跑飞了帮你看门”的好帮手但在Bootloader这种前期阶段CPU和内存间的时序还没稳定任何中断都可能让系统卡死所以第一件事永远是把它关掉。接下来是SDRAM初始化。PXA255的SDCFG0、SDCFG1寄存器控制着SDRAM的CAS延迟、刷新周期、总线宽度。不同厂商的芯片参数天差地别Hynix和Samsung的颗粒对刷新周期的容忍度就不一样。这块地方最让人头疼的是刷新计数器设大了SDRAM数据会丢失设小了浪费CPU带宽影响性能。PXA255里通常还要设置MDREFR寄存器让SDRAM进入自刷新状态才能稳定初始化。我当时调试的时候发现在自刷新和正常模式切换之间必须插入一段足够长的延时这个延时不是随便写个空循环就行要根据CPU主频精确算出需要的时钟周期数写少了SDRAM里读出来的数据全是坏的。很多奇怪的“下载镜像校验失败”问题最后追根溯源都是这里埋下的雷。2.2 从AUTOBOOT到下载模式的博弈Startup执行完毕进入CEntry函数这是C语言的入口。在这个阶段EBOOT会初始化串口、以太网控制器和Flash驱动然后进入一个死循环不断扫描键盘输入。EBOOT允许开发者通过配置文件修改启动超时时间和默认启动策略。在Platform Builder里修改config.bib和platform.reg中的相关项可以设置默认从Flash启动还是等待网络下载。实际开发阶段我通常把超时时间设为3秒手上连着串口终端方便随时打断它进入下载模式。量产阶段则把超时时间改成0让设备上电后直接加载Flash里的系统避免误触按键导致生产线上烧录好的机器变砖。这个环节有个细节值得注意EBOOT的键盘缓冲区非常小如果你连续按空格键的时机不对可能正好错过了扫描窗口。后来我都是手动在串口终端里发送空格字符而不是用手敲键盘这样时序控制更精确不会出现“明明按了按键为什么还是进了旧系统”的情况。2.3 图像下载协议与TFTP服务器配置EBOOT与开发机之间的通信协议历史上有过Serial下载和Ethernet下载两个版本Ethernet版用的最广的就是TFTP。TFTP是个非常简单的UDP协议没有TCP那样复杂的握手和拥塞控制特别适合在Bootloader这种还没有完整TCP/IP协议栈的环境下实现。EBOOT里的TFTP服务端需要预先指定一个IP地址这个地址要么存在Flash的环境变量里要么在EBOOT的DHCP过程中自动获取。手动指定的时候一定要确认Windows CE目标机和电脑的IP在同一个网段网关不一样没有路由TFTP请求永远发不过来。我在PC上配的是192.168.1.100EBOOT的静态IP写成192.168.1.71直连网线连交换机都不用最简单也最稳。微软官方的Platform Builder自带了一个TFTP工具但我自己用下来还是第三方的TFTP服务器更顺手。它最大的优点是能显示传输进度条和失败重传日志能看到“Received 1234567 bytes in 3.8 seconds”这样的信息方便确认下载速度和文件完整性。Ethernet Boot Loader Configuration ------------------------------------------------------------ 0) IP address: 192.168.1.71 1) Subnet mask: 255.255.255.0 2) DHCP: Disabled 3) Boot delay: 3 seconds 4) Reset to factory default configuration ------------------------------------------------------------3. PXA255下EBOOT的硬件适配3.1 网卡芯片驱动的移植思路PXA255时代的开发板上最常用的以太网控制器是SMSC LAN91C111系列。这个芯片性能放到今天看很一般但胜在CPU接口简单、寄存器操作直接、占用的GPIO少。EBOOT里为了它专门写一个DM9000或LAN91C111的网卡驱动核心工作是完成MAC初始化和数据包的收发。初始化时先通过MII接口读取PHY的ID寄存器确认网卡芯片和PHY芯片都正常响应然后设置MAC地址寄存器。网卡的MAC地址可以烧写在Flash里也可以从EBOOT配置菜单中临时指定。量产的时候我习惯在一条产线上统一烧写MAC避免局域网内有重名设备导致TFTP下载张冠李戴。数据包收发方面EBOOT里对收包做了最简单的轮询没有中断。这是因为Bootloader阶段任务非常单一没有多线程并发的需求轮询方式实现起来最简单而且不会漏包。实测在10Mbps的链路速度下轮询收满一个4MB的NK.bin只要几秒钟完全够用。3.2 闪存驱动中的坏块管理Nor Flash和NAND Flash在EBOOT眼中的待遇完全不同。PXA255平台用Nor Flash比较多读取就像访问内存一样简单不需要专门的驱动程序。写入则需要先擦除整块再编程这是它的物理特性决定的。NOR的擦除是按扇区大小进行的一个扇区通常是64KB或者128KB。擦除一块需要几百毫秒到几秒编程一个字节大概要几微秒到几十微秒。EBOOT里写Flash时必须先写入一个特殊的命令序列比如往0x555地址写0xAA、往0x2AA地址写0x55再通过往0x555地址写0xA0进入编程模式。这套时序在不同厂商的NOR芯片上略有差异编写代码时要用芯片手册里的命令状态机来对照不能照搬另一块开发板的代码。更麻烦的是NAND Flash。NAND的坏块是出厂就存在或者使用中产生的擦写操作必须跳过坏块。EBOOT里的Flash驱动要学会读OOB区域检查Factory Bad Block Marker并在写入过程中动态标记新出现的坏块。如果我当年不了解这个机制直接把镜像按顺序满盘烧写用不了多久设备就会出现随机启动失败就是因为镜像写进了坏块区域。在我当时的项目中Flash的分区规划大致长这样分区起始地址大小用途Bootloader0x00000000256KBEBOOT和参数区Environment0x00040000128KB环境变量存储Kernel0x000600006MBNK.bin主镜像UserSpace0x00660000剩余UserFS或应用程序环境变量分区专门用来保存IP地址、启动延迟、MAC地址等运行时配置。这个分区的损坏可能比内核分区损坏更麻烦因为它会导致EBOOT找不到有效的网络配置没法通过网络恢复系统只能把整个Flash重新烧写一遍。4. 从EBOOT到Windows CE内核的交接4.1 镜像格式的选择BIN还是NB0Windows CE的内核镜像有两种格式NB0和BIN。NB0是纯粹的原始二进制镜像加载地址就是实际运行的物理地址。BIN格式则是经过压缩和打包的头部有记录段地址的记录头EBOOT需要解包后再加载到对应位置。EBOOT在下载时需要知道镜像类型。Platform Builder的Project Properties里可以选择生成哪种镜像文件。开发阶段我直接下载NB0格式省去解包过程省时间缺点是没有压缩网络传输时间长一点。量产阶段换成BIN格式镜像体积能压缩将近一半下载速度快但要求EBOOT里的解包逻辑必须正确。我遇到过一种问题EBOOT能正常下载BIN格式并解压但在跳转到内核入口时总是发生数据中止异常。排查到最后发现是Record中的RAM起始地址和config.bib里的配置不一致跳转时跳到了一条没有映射的物理地址上自然就崩溃了。4.2 跳转前的最后一步关闭MMU和缓存这个坑我记忆特别深。PXA255的MMU是可以通过CP15协处理器控制的EBOOT阶段为了提高代码执行效率会开启ICache和DCache。但在把控制权交给内核之前必须先把Cache关闭并把数据写回内存否则内核启动时可能读到脏数据。按常规流程跳转前要执行这样一个序列; 清空I-Cache mov r0, #0 mcr p15, 0, r0, c7, c5, 0 ; 清空D-Cache并写回 mcr p15, 0, r0, c7, c6, 0 ; 关闭MMU mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00000001 mcr p15, 0, r0, c1, c0, 0每次跳转前做一遍这套清理保证CPU的流水线和Cache状态是干净的。如果漏了DCache清理这一部内核启动后可能随机崩溃而且这种随机性特别难复现。这个习惯我后来在其它ARM平台做引导程序时也坚持下来都是被PXA255时代的教训逼出来的。5. 调试EBOOT的实用技巧与常见问题5.1 串口没输出怎么排查先分清是EBOOT没跑到串口初始化还是串口初始化后的输出报文没发出来。PXA255的UART初始化要把GPIO复用成UART功能同时配置波特率、数据位、停止位、校验位。板子上最常见的调试串口是UART0通常采用115200、8、N、1的配置。如果串口终端一个字符都没有先用示波器看UART引脚有没有电平翻转没波形就是串口初始化都没执行到有波形但看不到文字一般是波特率或流控设置错了。EBOOT在串口正常后第一件事就是打印启动横幅。如果从启动横幅开始打印到提示输入只有一两行说明后面某一步初始化卡住。这时候就得往代码里加串口打印来分段定位把printf语句插到以太网初始化前、后以及Flash初始化前后通过逐段注释锁定问题代码的范围。5.2 TFTP下载失败但有超时这个问题九成出在网络参数配置上。先确认EBOOT里设置的IP地址、子网掩码和开发机的配置在同一网段再确认没有防火墙拦截UDP 69端口。Windows自带的防火墙经常拦下TFTP请求关掉防火墙临时测一下能通就能确定问题。还有一个隐蔽的坑就是路由器上的UDP超时设置。TFTP传输大文件时会出现ACK包丢失导致重传重传次数超过EBOOT的预设值就中断传输了。解决办法是让EBOOT以尽可能大的包长发送数据减少包的数量同时给PC端TFTP工具设置更长的超时重传周期。我当时调成4096字节的块大小后4MB镜像的传输基本能一把过。5.3 烧写Flash后重启不引导这种情况最常见的原因就是EBOOT镜像本身没有成功烧进去。PXA255从Nor Flash启动需要处理一个启动模式的细节有些板子支持从Flash启动有些板子要靠DIP开关或跳线设置启动源。硬件没配置对CPU上电后去错误的地址取指令那当然跑不起来。另一个容易忽视的地方是Flash起始地址并不一定是物理地址0。比如有些开发板从Nor Flash启动时CPU的重映射机制会把Flash内容映射到0x00000000位置。EBOOT代码必须知道自己在Flash中的实际位置通过链接脚本和启动代码的配合保证执行流正确。如果EBOOT是在RAM里调试的那跳转地址、Flash烧写地址和链接地址三者必须对齐错一个就白烧。6. 老平台经验对现代嵌入式开发的启发现在做嵌入式Linux引导程序大家主要用U-Boot。U-Boot和PXA255时代的EBOOT在架构上有很多相似的思想比如硬件初始化、网络下载、Flash烧写、环境变量这些概念都是共通的。虽然代码风格差别很大但本质的启动流程几乎一样。U-Boot把硬件的差异通过board目录下的板级文件隔离EBOOT则把板级差异直接写在BSP相关代码中。这一点上EBOOT的封装做得相对粗放换一块底板就要大动干戈改代码而U-Boot只需要替换设备树文件和板级配置就可以灵活适配。这是从老平台身上能看到的最明显的架构演进。如果你有PXA255的开发板我建议试一下在EBOOT上做一次完整的启动、下载、烧写流程用串口把每个阶段输出记录下来然后打开EBOOT源码对照着看。这种从硬件到软件形成闭环的练习比单纯看资料理解的深度完全是两个级别。我自己当年在PXA255上踩过的坑后来换到其它平台有八成可以直接迁移使用。关中断、清缓存、别乱动Timer、网络配置先查网段、Flash操作先去查坏块这些经验放在今天依然管用。有些道理看起来简单但在当时只有亲自在寄存器层面撞过墙才能记得这么深。本文还有配套的精品资源点击获取