2410 bootloader分析(二)
来源:互联网 发布:大智慧崩溃数据丢失 编辑:程序博客网 时间:2024/04/29 02:09
转载请注明出处
作者:小马
接下来几行一直到while循环就是打印公司log,设置led闪烁等操作,难度不大, 不分析.
下面就直接进入bootloader菜单功能分析了. 如下图所示:
Bootloader共有七个功能, 每一个功能都涉及到很多知识, 比如0号功能usb下载文件, 就涉及到usb的很多知识,如描述符,枚举等,每一个都可以专门写一篇文章来分析了. 我这里只分析加载操作系统这个功能. 以 加载wince举例.用到的函数是void NandLoadRunW(void).
下面就一步一步跟进去这个函数去剖析操作系统的加载机制.
void NandLoadRunW(void){ printf("Now boot Wince/n"); ClearMemory(); InitNandFlash(); LoadRun(3);}
ClearMemory是把wince本身运行用的内存空间清0, 我觉得这个操作意义不大,因为我试过不要这个操作,系统运行也没有问题, 这段内存应该会被覆盖的,清空也无意义. 不知道我的说法对不对.
InitNandFlash的实现如下:
static void InitNandFlash(void){ U32 i; InitNandCfg(); i = ReadChipId(); if((i==0x9873)||(i==0xec75)) NandAddr = 0; else if(i==0xec76) { support=1; NandAddr = 1; } else { puts("Chip id error!!!/n"); return; }// printf("Nand flash status = %x/n", ReadStatus());}
InitNandCfg初始化nandflash寄存器,它的实现非常简单,就一行.
rNFCONF = (1<<15)|(1<<12)|(1<<11)|(7<<8)|(7<<4)|(7);
操作的结果是使能nand flash 控制器, 初始化ecc校验.
ReadChipId读nand flash芯片的ID值. 这块板子所用的nand flash型号是K9F1208U0B, 打开它的datasheet , 从下图我们可以获得两个信息, 一是读ID的指令流, 二是芯片ID的组成.
ReadChipId的实现,可以参考K9F1208U0B,难度不大,但是比较繁琐, 不做分析. 另外,根据上图可知, 只有0xec76是合法的可识别的ID号.
下面是重头戏LoadRun函数, 该函数承担了加载wince的主要工作, 它的实现如下(把和linux相关的代码去掉了):
static void LoadRun(int part_sel)//part_sel = 1或3.{ U32 i, ram_addr, buf = 0x30200000; int size; StartPage = NandPart[part_sel].offset>>9; size = NandPart[part_sel].size; ram_addr = buf; for(i=0; size>0; ) { if(!(i&0x1f)) { if(CheckBadBlk(i+StartPage)) { printf("Skipped bad block at 0x%x/n", i+StartPage); i += 32; size -= 32<<9; continue; } } ReadPage((i+StartPage), (U8 *)ram_addr); i++; size -= 512; ram_addr += 512; } DsNandFlash(); rBWSCON |= 0x0000d000; rBWSCON &= ~0x00040000; putch('/n'); call_linux(0, 193, buf);}
先看下面两行
StartPage = NandPart[part_sel].offset>>9;size = NandPart[part_sel].size;
part_sel是传来的参数, 这里是3. NandPart的定义如下:
static struct Partition NandPart[] = { {0, 0x00040000, "bootloader"}, //256K {0x00040000, 0x001c0000, "zImage"},//0.75M {0x00200000, 0x01e00000, "cramfs"}, //30M {0x02000000, 0x02000000, "WinCE"}, //32M. {0, 0 , 0}};
很明显这里说明, nandFlash被分成了四区,第0个分区是存bootloader, 1,2分区是linux相关的, 3区是放wince映象, 最后一组是一个结束标志. 3区的两个0x02000000分别表示起始地址和大小(offset和size).这里表示留给3区的大小为0x02000000(32M),所以你编译的wince映象文件不能大于这个值,否则bootloader将无法加载.
现在有个问题是NandPart[3].offset为什么要左移9位呢. 要弄清这个问题,得看这个值用在了哪里.
这个值赋给StartPage, 然后作为参数传进去了CheckBadBlk函数里(参见上段LoadRun实现代码).
CheckBadBlk函数是检查flash芯片坏块的, 查看K9F1208U0B的文档,我们知道, page的地址是A9~A25, 低9位是列字节位置标识, 如下图所示:
至于CheckBadBlk的实现原理这里不分析了,K9F1208U0B的文档里有详细的流程说明, 只说明两点:
1 flash芯片是允许坏块存在的, 厂家只要保证低于某一个坏快量就可以了.
2 一个健壮的程序是应该识别所用芯片的坏块的.
所以, 很明显, 上面一段代码中的for循环是读整个nand flash的3号分区, 如果读到坏块就跳过当前的page继续读,否则就据读到的信息存放到ram_addr里(ReadPage((i+StartPage), (U8 *)ram_addr)实现). 而ram_addr指向了0x30200000这个地址. 而这个地址正是wince系统运行所用的RAM的地址(你可以理解为内存). 这块优龙的板子, SDRAM的大小是64M,起始地址是0x30000000, 结束地址是0x34000000.注意我这里说的SDRAM地址全部是物理地址, 操作系统都还没加载,哪里会有虚拟地址呢?
其实 bootloader存在的真正意义就两个, 一是初始化CPU相关硬件,如cache,MMU等, 二是完成将操作系统映象文件加载到RAM中(上段LoadRun的实现代码中for循环就是做这个事情). 然后,它就失去了存在的价值,不起任何作用了.
继续看loadRun的实现代码, 下面三行:
DsNandFlash();rBWSCON |= 0x0000d000;rBWSCON &= ~0x00040000;
第一行使nandFlash 无效.
二三两行的是用来的操作2410的memory data bus width的, 这里是操作 bank3和bank4, 优龙的板子, 两块SDRAM是接到bank6的, 用到bank3的是cs900(这是一个ethernet 驱动IC), 不明白为什么要把bank3的设置放在这里,我估计是要用到ethernet下载调试系统时才会用到,因为我这里用的是USB进行调试, 应该可以去掉这两行.
还有一个问题,我们已经把wince的映像加载到RAM中指定的地址了, 得去执行它,否则它怎么运行. 这就是call_linux的任务了. 它的实现代码如下:
void call_linux(U32 a0, U32 a1, U32 a2){ int i, j; void (*goto_start)(U32, U32); cache_clean_invalidate(); tlb_invalidate(); disable_irq(); //If write-back is used,the DCache should be cleared. for(i=0; i<64; i++) for(j=0; j<8; j++) MMU_CleanInvalidateDCacheIndex((i<<26)|(j<<5)); __asm { mov r0, #0 mcr p15, 0, r0, c7, c10, 4 // drain WB } MMU_DisableDCache(); MMU_DisableICache(); MMU_InvalidateICache(); MMU_DisableMMU(); MMU_InvalidateTLB(); goto_start = (void (*)(U32, U32))a2; (*goto_start)(a0, a1); }
其实要跳转指定的地址去执行, 只要下面三行语句就可以了.
void (*goto_start)(U32, U32);goto_start = (void (*)(U32, U32))a2;(*goto_start)(a0, a1);
中间的就是一些关闭MMU和TLB等操作, 这是系统运行对硬件的要求, 这里主要想说一下对a0, a1这两个参数的疑问. 表面看起来,这两个参数是传给系统内核的, 我在CSND上看到有人问这个问题, googleman回复的帖子说是没什么用,我写成下面这种形式测试了一下,
void (*goto_start)(void);goto_start = (void (*)(void))a2;(*goto_start)();
- 2410 bootloader分析(二)
- 2410 bootloader分析(一)
- bootloader分析
- bootloader分析
- bootloader 分析
- Bootloader分析
- bootloader分析
- Bootloader分析
- bootloader分析
- Bootloader分析
- bootloader分析
- bootloader分析
- X86内核启动分析二 从bootloader到内核
- 关于bootloader(二)
- Bootloader学习(二)
- Bootloader系列(二)vivi bootloader解析
- Bootloader(Vivi)源代码分析
- MINIX3的BootLoader分析
- JAVA多线程
- DWR学习
- 成为程序精英,我奋斗了7年--非常有教育意义
- 这是一个好的开始
- 计算机类的权威和核心期刊
- 2410 bootloader分析(二)
- 软件开发人员的简历项目经验怎么写?
- json
- [转]漏电流
- (ZJU-2006复试)-HDOJ-1235-统计同成绩学生人数
- 自动重起服务器脚本
- 使用VS2005开发64位驱动程序需要注意的一些问题
- Myeclipse新建jsp文件时默认代码
- 电路