预备文章:
//-----------------------------------------------------------------------//
By: cnblogs-HJB
//-----------------------------------------------------------------------//
By:Gooogleman
//-----------------------------------------------------------------------//
By:gliethttp
//-----------------------------------------------------------------------//
By:CSDN-fan227
//-----------------------------------------------------------------------//
By:kernel
//-----------------------------------------------------------------------//
《2440从NANDFlash启动之bootloader运行以前》
By:rightsoft
//-----------------------------------------------------------------------//
By:驱动开发网-fishly_0
//-----------------------------------------------------------------------//
By:昔日之ID:formerman
//-----------------------------------------------------------------------//
By:Mercury
//-----------------------------------------------------------------------//
好了,下面进入正文。昨天我们一起先看了下config.bib。于是我们就想更进一步。来在仔细研究下到底bootloader是有哪些事儿呢?
于是我们就找了上面的一些文章来看看。首先我们看HJB大牛的文章。文章很清晰的告诉了我们wince6.0下如何操作bootloader,下面我们再说一次,本人非常笨,不多说两边记不住,所以大家就忍受吧。首先系统要运行,需要一个引导程序,在以前有NOR flash,友善系列的开发板就是带NOR这个东东,然后伴随的是有一个vivisuper的loader,这里我们不去细谈这个,现在主流是nandflash,所以,对应的有一个nandflash的boot,我们叫他nboot,那我们再来看ARM,我们拿Samsung这个arm来做例子,在HJB大牛的文章中提及了,ARM自身有一个4K的空间来存储应到程序,所以在上电之后通过一系列的配置,将nboot加载到ARM 4k的空间里去执行,nboot就开始对系统的nandflash,内存,USB等等只要是你想初始化的设备进行一个初始化,这里的初始化程度就看你自己写的初始化程序了,按需所设计。接下来是nboot的重要使命,去叫eboot或者直接叫nk,然后结束自己的生命周期。
这里要特别说的是,在上一篇文章完结后,有个朋友问我nboot怎么去找到nk或者eboot在nandflash里的地址啊?这个问题一下也把我问懵了。百思不得其解,看nboot的程序里面到处都是什么loadaddress,或者是jumpaddress之类的东西,以为那个就是,后来根据以上一些文章的阅读发现,其实那些地址只是SDRAM和ARM直接一个虚拟和实际物理地址。根本不是nandflash里的地址。
那如何去从nandflash里面找到我们要的eboot或者image呢?这个时候被我们忽略的TOC蹦出来了,大叫道那些东西都是我扒的。这个时候我们就仔细来看看gliethttp的文章,关于wince TOC启动的文章,这里gliethttp给我们详细介绍了两个结构体:TOC和_IMAGE_DESCRIPTOR,具体这两个结构体的分析我们这里也贴出来,给这两扒东西的留个纪念,并给出一些注释,注释来自gliethttp,具体如下:
1: typedef struct _TOC
2: {
3: //和CSW中的认证域类似,只用来验证接下去内容的合法
4: DWORD dwSignature;
5: //包含image的索引(我的是1)、启动delay时间、ip地址、MAC地址和掩码等
6: BOOT_CFG BootCfg;
7: //用来描述3个之多的ce内核image数组,我用的是id[1]
8: IMAGE_DESCRIPTOR id[MAX_TOC_DESCRIPTORS];
9: CHAININFO chainInfo;
10: } TOC, *PTOC; // 512 字节
11:
12: typedef struct _IMAGE_DESCRIPTOR
13: {
14: DWORD dwVersion; //编译时的版本号
15: DWORD dwSignature; //“EBOOT”或“CFSH”等
16: UCHAR ucString[IMAGE_STRING_LEN];
//描述字符串:如"eboot.nb0"之类
17: DWORD dwImageType; //image的类型nk.nb0为0x04
18: DWORD dwTtlSectors; //image文件用到的NAND的扇区总数
19: DWORD dwLoadAddress; //image加载时的虚拟地址
20: DWORD dwJumpAddress; //image加载完成后的跳转地址
21: SG_SECTOR sgList[MAX_SG_SECTORS];
//image的段描述,包括起始扇区号和所需扇区数目
22: ULONG dwStoreOffset;
23: } IMAGE_DESCRIPTOR, *PIMAGE_DESCRIPTOR;
24:
25:
这两个相依为命的结构体是扒eboot和image的罪魁祸首。他们到底是怎么扒的呢,在gliethttp文中介绍道:并不需要一次性将所有nk.nb0数据都加载到内存,应该按需加载,那就是ce的镜像文件image“按需加载”[“段式加载”]方式,TOC就是用来描述ce内核镜像文件image的xipkernel段核心结构体,我们只需加载核文件xipkernel就能正常启动进入wince界面。
好了,到此为止我们搞清楚了原理了,那内存SDRAM和ARM之间的地址又回到了重要的位置,我们怎么去设定这个地址呢?首先我们可以看下最新的mini2440 nboot的写法,首先是给出几个固定的地址,也就是原始的物理地址和起始地址,一般的情况下大家可以在oemaddrtab_cfg.inc中的对照表中找到SDRAM的地址映射,一般情况下是类似如下的描述:
1: DCD 0x80000000, 0x30000000, 64
2: ; 64 MB DRAM BANK 6
好了,这完成了第一步,第二步我们来写个比较简单的小程序,如何把虚拟地址转换成实际物理地址呢,这里又回到mini2440的最新5.0代码或者6.0代码中,有一句这样的话,写的很好:
1: #define VIRTUAL_TO_PHYSICAL(va) ( ( (va>0x8c000000) ? (va-0xc000000) : (va) ) - VA_BASE + _RAM_STARTADDRESS )
这里作出了一个判断,保证了地址的大小。同时也转换了地址。
这样一来我们知道如何在两个地址之间转换了,不过这个事情其实是在内存管理单元没有工作的情况下要用一下。当MMU工作了这个事情按照常理是交给MMU处理了.这些都是后话,我们继续看,有了TOC有了地址转换的工具,那地址到底在哪里呢?
现在我们在来看看gooogleman的文章,做wince开发驱动的应该都会用pb自带的一个命令行,这个是很好用的,有的时候makeimg啊,notepad一个什么文件什么的,很方便,这里gooogleman带我们回忆了一个非常重要,但是又往往被我们忽略的一个命令:viewbin,有了这个东东,我们可以仔细的看看我们要引导的eboot.bin或者nk.bin的起始地址是多少了。方法很简单,直接viewbin nk.bin或者viewbin eboot.bin就可以了。起始地址,长度以及开始位置都列的很清晰,而这些个信息我们在TOC扒的时候已经全然送到他们该送的地方去了,系统自己该JUMP的也就JUMP过去,该Lunch的也就去Lunch过去,最新的会自己Run过去,呵呵这个完全看自己写的了。都是一样,无非是用一个指针指去那个地址,下面给个参考例子,我们就RUN过去,首先定义下怎么个run法:
首先初始化一下我们的run
1: #define DOWNLOAD_ADDRESS 0x30138000
2: void (*run)(void)=(void (*)(void))(DOWNLOAD_ADDRESS);
然后我们通过TOC给run加速,一般是在char ReadImageFromNand(unsigned int dwEntry)这个函数中
1: run = (void (*)(void))(pToc->id[dwEntry].dwJumpAddress ? VIRTUAL_TO_PHYSICAL(pToc->id[dwEntry].dwJumpAddress) :
最后我们要在main函数里run起来
1: if (run) run();
这句话说的很好听,如果想跑你就跑吧。
好了。到此为止该run到eboot了就去run eboot了,该run image就去run image了。
这里补充下,其实eboot也是一样,eboot起来也就是去run image,这个看设计的要求来定,理论是一样的。
其他的文章对nboot+eboot+image模式进行了详细的阐述,大家可以自己阅读,这里不对说了。
祝贺大家run的愉快。