基于Nandflash的Bootloader开发简介

来源:互联网 发布:淘宝12月更新公告 编辑:程序博客网 时间:2024/04/28 01:46

S3C2410支持直接从Nand Flash启动,所以要用到Nand Bootloader。

大多数Nand Bootloader都分为Nboot和Eboot两部分。依赖于CPU体系结构的代码,放在Nboot中,通常用汇编语言+C语言实现;而Eboot通常用C语言实现,这样可以实现复杂的功能,而且代码会具有更好的可读性和可移植性。

Nboot:

  1. 硬件设备初始化:
    • 屏蔽所有中断。可以通过写CPU的中断屏蔽寄存器或状态寄存器(ARM中是CPSR寄存器)来完成。
    • 设置CPU的速度和时钟速率。
    • RAM初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。
    • 初始化LED。典型地,通过GPIO来驱动LED,其目的是表明系统的状态是正确还是错误。也可以通过初始化UART向串口打印Nand Bootloader的logo字符信息来完成测试。
    • 关闭CPU内部指令/数据cache。
  2. 为加载Nand Bootloader的Eboot准备RAM空间;
    • 为了获得更好的执行速度,通常把Eboot加载到RAM空间来执行,因此必须准备好一段可用的RAM空间范围。
    • 空间大小最好是memory page(通常是4KB)大小的倍数。
    • 由于是C语言执行代码,因此在考虑空间大小时,除了stage2可执行映像的大小外,还必须把堆栈空间也考虑进来(一般情况下1MB就可以了)。
    • Eboot_end = Eboot_start + Eboot_size
    • 必须确保所安排的地址范围是可读写的RAM空间。因此需要对所安排的地址范围进行测试。测试方法:以为memory page为测试单位,测试每个page开始的两个字是否可读写。
  3. 拷贝Nand Bootloader的Eboot到RAM空间。拷贝时要确定两点:Eboot的可执行镜像在固态存储设备的存放起始地址和终止地址;RAM空间的起始地址。
  4. 设置堆栈指针SP;通常把SP的值设置为Eboot_end - 4,堆栈向下生长。
  5. 跳转到Eboot的C入口点。在ARM中,可以通过修改PC为合适的地址来实现。

Eboot:

  1. 初始化本阶段要使用的硬件设备:至少要初始化一个串口,以便和终端用户进行I/O输出信息。
  2. 检测系统内存映射(memory map):
    • 所谓内存映射就是指在整个4GB物理地址空间(32位的总线)中有哪些地址范围被分配用来寻址系统的RAM单元。
    • 在s3c2410中,从0x8000,000到0xA000,000之间的512M地址空间被用作系统的RAM地址空间。
    • 但是在具体的开发板上,却不一定会实现CPU预留的全部RAM地址空间。
    • 由上可知,检测系统内存映射,即要知道CPU预留的全部RAM地址空间中的哪些被真正映射到RAM地址单元。
  3. 加载kernel内核镜像:
    • 规划内存占用的布局,主要考虑基地址大小和内核镜像的大小两个方面。这些信息在TOC中,TOC位于Nand Flash的第1块。关于TOC的详细介绍,可以看我写的《TOC(Table of Content)数据结构》。
    • 从Flash上拷贝Kernel镜像。由于内核在Nand Flash上的存储是以sector为单位(512 Bytes)的,因此Nboot从中读取数据也是以sector为单位的。
    • 读取数据的基本函数为FMD_ReadSector:
      view plaincopy to clipboardprint?
      1. FMD_ReadSector(  
      2.     SECTOR_ADDR startSectorAddr,    //sector的起始位置  
      3.     LPBYTE pSectorBuff,             //为存储数据所分配的缓冲区地址  
      4.     PSectorInfo pSectorInfoBuff,    //存储sector信息的缓冲区地址  
      5.     DWORD dwNumSectors              //读取的sector数量  
      6.     )  
    • 镜像拷贝的具体程序如下:
      view plaincopy to clipboardprint?
      1.    //  
      2.    // Load the disk image directly into RAM  
      3.    // BUGBUG: recover from read failures  
      4.    //  
      5.    i = 0;  
      6. while (dwSectorsNeeded && i < MAX_SG_SECTORS)  
      7. {  
      8.        dwSector = toc.id[dwEntry].sgList[i].dwSector;  
      9.        dwLength = toc.id[dwEntry].sgList[i].dwLength;  
      10.        // read each sg segment  
      11.        while (dwLength) {  
      12.   
      13.            if ( !FMD_ReadSector(dwSector,  
      14.                                (LPBYTE)dwRAM,  
      15.                                NULL, 1) )  
      16.            {  
      17.                Uart_SendString("ERR_DISK_OP_FAIL2: ");  
      18.                Uart_SendDWORD(dwSector, TRUE);  
      19.   
      20.             dwSector++;  
      21.             continue;  
      22.   
      23.            }  
      24.              
      25.         dwSector++;  
      26.         dwLength--;  
      27.            dwRAM += SECTOR_SIZE;  
      28.        }  
      29.   
      30.        dwSectorsNeeded -= toc.id[dwEntry].sgList[i].dwLength;  
      31.        i++;  
      32.    }  
  4. 调用内核。Bootloader调用系统内核的方法是跳转到内核的启动地址处,这个启动地址是在下载镜像过程中由EBoot程序根据内核镜像提供的信息获得的,然后将其存储到TOC里。Nboot可以读取这个地址,然后将其转化成物理地址,当把内核载入内存后把指针跳转到这个地址处,内核就启动了。一般情况下,内核的加载地址并不是内核的启动地址,有一个偏移。这是因为系统预留了一个空间来放置系统参数(如中断向量等)。
原创粉丝点击