用以太网Boot启动WinCE的完整过程浅析
1、为什么需要Nboot
S3C2410处理器支持将启动代码存储在NAND Flash中。为了实现这一功能,2410配备了一个名为“Steppingstone”的内部SRAM。在启动时,NAND Flash中第一个4K字节的内容将被加载到Steppingstone中并执行。这个工作由MCU主动完成,而我们只需将NAND Flash配置为Auto Boot模式即可。
一般来说,启动代码会拷贝存储在NAND Flash中的内容至SDRAM中。在使用ECC校验时,NAND Flash中数据的有效性将被确认。在完成拷贝的工作后,启动代码将跳转到已加载到SDRAM中的主程序中,这时启动代码的使命完成,MCU由主程序来控制。
WinCE操作系统从文件的组成来看一般分为两部分:BOOTLOADER和NK.bin。在WinCE中,BOOTLOADER一般为EBOOT。它的主要功能是初始化硬件设备,主要包括CPU内部的相关控制器、内存、网络、串口甚至USB口和LCD。在初始化完成后,它将通过网络或USB从外部下载NK.bin,或从本地Flash中加载NK.bin并执行,从而启动WinCE操作系统。可以看到Eboot虽然是启动代码,但它得完成相当多的工作,最终的映像文件也将超过4K。所以,我们不能直接将Eboot存放在NAND Flash的第一个4K字节中。我们需要一个更小的启动代码,这就是通常所说的NBOOT(NAND BOOT)。
2、Nboot必须完成的工作
1. 初始化CPU内部相关控制器,如设置GPIO,关闭Watch Dog,关闭中断,设置系统时钟。
2. 初始化内存。
3. 初始化串口,主要用来输出调试信息。
4. 初始化NAND Flash,因为在MCU启动时默认是Auto Boot模式,为了从NAND Flash中读取EBOOT,需要将其配置成 NAND Flash Mode。将代码和数据拷贝到SDRAM运行
5. 读取NAND Flash中的EBOOT映像文件,并放在内存指定的位置,这个地址是跟EBOOT有关,介绍EBOOT时再详细说明。
6. 完成读取之后,跳转到EBOOT的起始位置,执行EBOOT代码。
3、Nboot的注意事项
1、用ADS编译Nboot时,注意RO_BASE必须设置为0x0,RW_BASE可以不设
2、Nboot要控制在4K以内
3、Nboot最后有个Launch(JumpAddr);这个Launch函数是nboot的2440slib.s中定义的,功能是将JumpAddr赋给PC完成跳转,根据JumpAddr地址来决定是跳到Eboot还是跳到NK内核中。JumpAddr地址的定义在2440loader.c中
这里在smdk2440\inc\Loader.h中有约定将Nboot烧入到Nandflash的第0块,将TOC烧入第1块,将Eboot烧入第2块中
4、Eboot中的动作
我们可以从3中看到Nboot是如何跳转到Eboot中的,下面看Eboot文件下的东西
5、Eboot的流程
1、eboot和nk公用一段起始代码fw.s,所以我们会在eboot文件夹下的arm子文件夹找到fw.s,里面就一句话: INCLUDE ..\\..\\kernel\\hal\\arm\\fw.s,对于这段起始代码我就不详细分析,无非是建立好中断向量表,设置好系统的工作频率,设置MMU等,然后就跳转到eboot的main函数。
2、eboot的main函数在eboot文件夹的main.c里面,代码如下:
3、BootloaderMain函数,这个就是eboot真正的main函数,这个函数是微软的ce对eboot的通用函数,它会调用在eboot文件夹里面由OEM商或者自己写的一些函数。
6、BootloaderMain函数的主要动作。
1、在BootloaderMain 函数里面首先执行KernelRelocate,这是把一些全局变量存放到ram里面去,这个函数不是很重要。
2、下面就是执行OEMDebugInit,看到OEM三个字母了没有,这就说明这个函数是OEM商,或者我们自己需要实现的,在eboot下的main函数里面可以找到这个函数,这主要是提供给blcommon一些回调函数如下所示:
BOOL OEMDebugInit()
{
// Assign callback functions to be usec by blcommon.
g_pOEMReportError = OEMReportError;//错误报告函数
g_pOEMVerifyMemory = OEMVerifyMemory;// 下载映象时检测内存是否正常
g_pOEMMultiBINNotify = OEMMultiBINNotify; //通知需要下载的所有bin文件
OEMInitDebugSerial();//初始化串口调试输出
return TRUE;
}
这些被调用的函数也是OEM商或者我们自己编写的。前面三个函数都可以在main.c里面找到,代码比较罗唆,而且基本上和硬件没有太大关系,我们看看最后一个初始化串口调试输出的函数,这个文件在D:\WINCE420\PLATFORM\smdk2410eboot+rtc\KERNEL\HAL\debug.c里面,我这里是设置串口0为调试输出口,三星自带的用的是串口1,并且把波特率设置为115200,大家如果需要用串口0作为调试输出口可以参考我的修改:
#define UART0BaudRate 115200
void OEMInitDebugSerial(void)
{
volatile UART1reg *s2410UART0 = (UART0reg *)UART0_BASE;
volatile IOPreg *s2410IOP = (IOPreg *)IOP_BASE;
s2410IOP->rGPHCON &= ~((3 << 8) | (3 << 10));
s2410IOP->rGPHCON |= ((2 << 4) | (2 << 6)); //
s2410IOP->rGPHUP |= (1 << 2) | (1 << 3);
s2410UART0->rUFCON = 0x0; // Disable the FIFO
s2410UART0->rUMCON = 0x0; // Disable AFC.
s2410UART0->rULCON = 0x3; //
s2410UART0->rUCON = 0x245;
s2410UART0->rUBRDIV = ( (int)(S2410PCLK/16.0/UART0BaudRate + 0.5) -1 );
}
调用完这个调试输出初始化函数以后,eboot的调试信息就会从串口0出来(当然nk的调试信息也会从这个串口出来了,因为这一部分是和nk复用的。
3、OEMPlatformInit,这个函数也在eboot的main.c里面可以找到,主要是初始化硬件平台,包括设置RTC时钟,初始化USB(InitUSB)、初始化中断Isr_Init、初始化一下NANDflash(BP_init),采用微软的BootPart模块提供对Flash设备的分区功能和对BinFS的支持,BP_init在G:\WINCE500\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BOOTPART\bootpart.cpp中定义。然后就是读TOC (table of contents),一般TOC都会烧到nand的block1里面,如果读TOC失败,就会用默认的参数重写TOC,读TOC这段代码比较简单,在fmd.cpp里面。然后就是进入倒计时,如果在设置的延迟时间内按键盘的话就会进入BootMonitor这个函数,这个函数主要是输出eboot的选择菜单,根据你的选择进行操作,如果在延迟时间结束你没有按键盘的话就会根据你设置的是Download new(下载新的映象)还是Launch existing(加载在nand中的映象)来进行下一步操作。
(1)、BootMonitor函数,这个函数虽然代码很多,但是其实非常简单,就是根据你的输入来设置改变一些全局变量,eboot在后面会根据这些变量来进行相应的操作。
(2)、如果选择了下载映象,在OEMPlatformInit函数里会调用InitEthDevice初始化网卡,然后返回true,InitEthDevice函数在ether.c里面,具体需要根据你使用的网卡,把一些接口提供给eboot
(3)、如果我们选择了Launch existing image,在eboot的OEMPlatformInit里就会利用ReadRamImageFromBootMedia或者ReadKernelRegionFromBootMedia函数把nk从nand中读到ram里面,然后再启动内核。第一个函数是直接把内核从nand中拷贝到RAM里面,第二个函数必须要选择了支持binfs文件格式,它会把nand进行格式化成binfs,这些对nand操作的函数都再fmd.cpp里面,具体实现可以参考里面的代码。
4、BootloaderMain下面就调用OEMPreDownload进行一些下载前的准备工作,之后就会调用DownloadImage下载内核,下载完了后就调用OEMLaunch启动RAM里面的内核,注意OEMLaunch里面会需要和PB建立连接,如果我们要绕过PB下载nk,我们就需要屏蔽这段代码(在#ifndef SIMULATOR #endif之间)。