备注:FLASH为K9K8G08U0D-SCB0
1. 烧录镜像文件
相关部分见我之前的博文:http://blog.csdn.net/loongembedded/article/details/6648270
我们知道烧录nboot、eboot和nk的实现主要是在eboot部分,所以,我们就直接从eboot来学习这部分。
1.1 Eboot烧录stepldr
刚出厂时FLASH的单元除了坏块的标识(非0xFF)之外,其他单元保存的都是0xFF的值,也就是说并没有有效的数据,为了把nboot的数据写入FLASH中,而且S3C2451支持TF卡启动,所以开就就从TF卡启动,然后通过读取出TF卡中的nboot镜像文件烧录到FLASH中,下面就学习烧录过程中涉及的主要动作。
1.1.1 OEMPlatformInit函数
不管是通过FLASH启动还是TF卡启动,都会执行到OEMPlatformInit函数,此函数很重要,内容页很多,下面介绍重点及之前没有深入涉及的部分:
1.1.1.1 BP_Init函数
图1
BP_Init函数是BootPart模块的初始化函数,初始化Flash设备并初始化一块内存。该函数一般会在OEMPlatformInit函数中被调用。pMemory指向一块内存来存放MBR信息,dwSize为内存的大小,lpActiveReg,pRegIn和pRegOut会被FMD_Init用到,一般可以设置为NULL。先来看此函数中相关的FLASH结构体:
图2
Windows CE下的FLASH驱动分为两层,分别为FMD层和FAL层(flash abstraction layer),FMD(Flash Media Driver)属于底层,直接操作Flash硬件,比如读、写和擦除等,不同的Flash硬件则FMD_XXX接口实现函数各不相同,上层则是FAL (Flash Abstraction Layer)层,该层是由微软实现并提供的,是一个与硬件无关的层,可用于FAL层实现扇区的动态分配和坏块管理等。FAL层向应用层(如API)提供DSK接口。例如CreateFile中调用的设备即是调用该FAL层提供的接口。FMD层暴露FMD_XXX让FAL层调用。
微软NAND FLASH块驱动的FAL部分使用此8字节的SectorInfo结构体模拟flash实际扩展区域的典型物理布局,也就是说此结构体描述了spare area所保存的内容。下面是此函数主要实现部分:
图3
调用BP_Init函数后,内存中一些变量被初始化为指向具体的内存单元处,如下图:
图4
该函数可以说是BootPart模块的初始化函数,看看代码就知道他会调用FMD_Init来初始化Flash设备并初始化一块内存。该函数一般会在OEMPlatformInit函数中被调用。pMemory指向一块内存来存放MBR信息,dwSize为内存的大小,lpActiveReg,pRegIn和pRegOut会被FMD_Init用到,一般可以设置为NULL。
1.1.1.2 TOC_Read函数
接下来调用TOC相关的函数:
图5
图6
TOC_Read函数主要是从TOC所在的第1block中读取第1页的TOC数据,根据获取的TOC数据来指导下面的执行流程,根据FMD_ReadSector函数的实现部分,此函数的描述可参考我的博文:http://blog.csdn.net/loongembedded/article/details/6015302,我们可以初步指导每512bytes main area对应的16bytes的布局,在此先借用三星《spare_assignment_standard.pdf》中的一张图:
图7
有了这种图就可以很好理解spare area中的布局,但我们的这16bytes布局如下:
bBadBlock(1)+ dwReserved1(4)+ bOEMReserved(1)+ wReserved2(2)+ECC0~3(4)+S_ECC0~1(2)+
RESERVED(2),其中括号中的数据表示多少个字节,可知每512bytes main area的数据产生4个字节的MECC校验码,16bytes spare area的数据产生2个字节的SECC校验码。
1.1.1.3
1.1.2 第一次烧录前需要对FLASH进行的操作
由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。为了检测数据的可靠性,在应用NAND Flash的系统中一般都会采用一定的坏区管理策略,而管理坏区的前提是能比较可靠的进行坏区检测。
1.1.2.1 坏块的定义和产生。
FLASH出厂时一般都有坏块,最初坏块被定义为某块中,如果包含一位或多位初始无效的位就认为此块为坏块。
NAND FLASH中坏块的出现有下面四种情况:
⑴出厂时的坏块。
⑵操作过程中由于擦除失败造成的。
⑶擦除过程中写入操作失败引起的。
⑷出现超出ECC校验算法纠正能力的错误时,也认为出现了坏块。
1.1.2.2 标示坏块
在大部分情况下,最初的坏块信息是可擦除的,在块擦除的时候很有可能会恢复(recover)这些坏块信息,这样就会因为没有正确判别坏块造成这样的问题:把数据写入到坏块中,这样肯定会留下安全和性能隐患。所以,系统必须能够根据最初的坏块信息来识别最初的坏块(也就是统计出哪些块是坏块),并且创建最初的坏块表,并且确保禁止对最初坏块信息的擦除。
对于大页面(2K Bytes)的FLASH来说,比如K9K8G08U0D,初始坏块的状态定义在spare area的第一个字节,也就是第2048个字节,三星确保每个初始坏块的第1或是第2页的第2048个字节的数据位非0xFF,所以系统就是根据此字节的内容来判断的,下面是识别出初始坏块的流程:
图8
在第一次向FLASH写入镜像文件时,而且在写入之前需要执行“F) Low-level format the Smart Media card”和“9) Format Boot Media for BinFS\r\n”,然后再是烧录镜像文件,先来看F对应的内容:
图9
这样就可以把FLASH初始的坏块标识出来了,当然这里,对于bootloader所占用的块(前6个blcok)没有做判断,而是直接标识为坏块、预留与只读块。
⑴FMD_WriteSector函数
FMD_WriteSector函数主要是通过调用NAND_LB_WriteSectorInfo函数来把SectorInfo结构体数据及ECC校验码写入到nbootC和EBOOT之间占用块的spare area中,我们先来看NAND_LB_WriteSectorInfo函数的主要实现部分:
图10
结合图7和10的实现,可知每页中spare area的数据布局如下图所示:
图11
⑵FMD_GetBlockStatus函数
此函数主要是最终通过调用NAND_LB_ReadSectorInfo函数来读取block中spare area data来判断块的状态,下面来看NAND_LB_ReadSectorInfo函数的主要部分:
图12
图12中提到从NFSECC寄存器中读出spare area数据的ECC校验码之后,为什么还要写入到NFSECCD寄存器中呢?NAND FLASH控制器中有一段这样的描述:
图13
也就是说为了比较ECC硬件模块产生的ECC校验码,从FLASH中读取的ECC数据必须写入到NFMECCDn寄存器(对应于main area)和NFSECCD寄存器中(spare area),只有这样NAND FLASH控制器才能比较出写入和读取出来的数据是由一致。
接着看“9) Format Boot Media for BinFS\r\n”对应的动作:
图14
该函数用于低级格式化,它会格式化Flash设备中的Block,重新创建MBR并将MBR存到第一个Blockd的第一个扇区中。dwStartBlock为起始Block,dwNumBlocks为多少个Block,dwFlags为格式化标记位,表示采用何种格式化方式。该函数会根据需要来由EBOOT中的函数调用,下面来看此函数体:
图15
⑴EraseBlocks函数
此函数内容很多,分下面几部分说明:
第一部分:
图16
第二部分:
因为指示坏块的标识位能够被擦除,我们采取了谨慎的做法,就是对block写数据并读取出来来验证是否为好快,但此动作需要额外的时间,我们用的是1Gbytes大小的SLC FLASH,此动作需要约为10分钟的时间,下面看具体的实现细节:
图17
到此就已经能够全部把坏块找出来,并且确保把好块的数据写入0xFF。
⑵ CreateMBR
图18
其中,硬盘有效标志为(55AA),这样可知,WINCE的MBR数据布局如下所示:
图19
接下来看WriteMBR函数
图20
因为我们FLASH的布局是:nboot(第0block)+TOC(第1block)+EBOOT(第2到第5block)+MBR(从第6block开始的第一个好块)+NK+用户数据,而我假设第6block就是好块,结合图15可知,g_dwMBRSectorNum=6*64=384page,也就是第6个block的第0个page。
// end of sector - 2 bytes for signature - maximum of 4 16-byte partition records
1.1.3 DownloadImage函数
接下来我们选择S来通过TF卡下载镜像文件,BootloaderMain函数调用DownloadImage函数来实现下载,就是把nboot文件从TF卡中读取出来保存在SDRAM中。
⑴从TF卡中读取stepldr.nb0读取出来。
图21
主要是把nb0文件的7字节的magic number、校验和、文件数、文件起始地址、文件大小、文件名称和文件内容读取或是计算出来拷贝到0xA4500000开始的内存单元中,这段内部的数据布局如下所示:
图22
⑵DownloadImage函数
图23
下面分几部分就来学习DownloadImage函数:
第一部分:
图24
这部分主要是读取文件7字节的magic number来判断文件的类型,读取要烧录的文件数、校验和、文件开始地址、文件大小和文件名。
第二部分:
图25
第三部分:
*pdwImageStart = pCurDownloadFile->dwRegionStart;
*pdwLaunchAddr = pCurDownloadFile->dwRegionStart;
*pdwImageLength = pCurDownloadFile->dwRegionLength;
主要是把stpeldr.nb0文件的开始地址,长度复制给这些指针放回去,这样就知道了stepldr.nb0的这些信息。
1.1.4 OEMLaunch函数
OEMLaunch函数主要是通过调用WriteRawImageToBootMedia函数来实现的,下面就来看WriteRawImageToBootMedia函数的主要实现部分:
图26
接下来看WriteBlock函数体:
图27
下面就来看FMD_LB_WriteSector_Steploader函数是如何把stepldr.nb0文件写入到FLASH中的:
第一部分:
图28
第二部分:
图29
为了理解这部分的内容,我们来看NAND FLASH控制器中的相关描述:
图30
因为我们采用的FLASH页大小为2048+64的,大于512,所以需要先分别从NF8MECC0~NF8MECC3中拷贝ECC校验码到临时数组变量t8MECC中,然后再写入到spare area中,接下来看第三部分:
图31
到此这三部分可知,把stepldr.nb0写入第0block的过程中,没有写spare data到spare area中,这也是因为读取第0block的stepldr数据的ECC校验在IROM中已经check的原因吗?目前还不是很清楚,学习到这里,回到图9中,这里如果是从第64页,也就是第1block开始,也就是可以理解。