嵌入式Linux启动优化手记3 BootStrap优化

 AT91SAM9G45 Linux 启动步骤:
    bootstrap -> U-boot 1.3.4 -> Linux 2.6.30
    CPU上电后从NAND FLASH 第0块读取bootstrap. 然后bootstrap读取u-boot,u-boot读取并启动linux.
为何要这样呢?
    一直在思考这个问题,我开始一直以为bootstrap是没有源码的,后来才发现它也有源码,读了它的代码后发现,它干的活就很少,初始化一些寄存器,读取u-boot并转入执行。那能不能去跳它直接把u-boot代码放到它的位置呢?答案是不能,因为AT91SAM9G45最大只读取23000字节的代码,U-boot 明显超出了限制。
    那么反过来呢?把U-boot 里面启动linux的功能挪过来,不用U-boot呢?可以,不过不能用U-boot提供的其它功能,开发过程会非常不方便,而且也没有必要。
    闲来无事,测试了一下bootstrap读取NAND的速度,发现差不多有6MB/S,太吸引人了,前面提到过,优化U-boot后,主要瓶颈就是其龟速的NAND读取速度。
    突然想到了一个新的启动方案:
    CPU上电读取并执行bootstrap, 在bootstrap中插入代码,先读取 OS Image,然后检查串口输入,如果有输入按键,就放弃读取的OS Image,读取U-boot 并转入执行U-boot, 如果没有输入按键,就直接启动Linux,岂不是两全其美?   
    在 ftp://www.at91.com/pub/at91bootstrap/AT91Bootstrap3.1/ 上下载了一个新版本的bootstrap.用当前编译环境编译了一下,没问题,发现其代码已经实现直接启动Linux(不使用u-boot),大喜,这样工作量就更小了,也更有信心了,开始工作。
    由于要读取串口数据,需要把串口初始化,这个版本只有当配置成DEBUG才初始化串口,没关系,修改代码,让它初始化就好了。需要实现一个函数,非阻塞检测串口有没有输入,如果有就读取,否则就返回0,实现如下:
    char dbgu_try_getc(void)
    {
        if (read_dbgu(DBGU_CSR) & AT91C_US_RXRDY)
             return (char)read_dbgu(DBGU_RHR);
        return 0;
    }


修改main()函数,加入读取linux os image 的代码
    int img_read_ret = 1;
    char stopchr = 0;
#define OS_LOAD_TO_MEM_ADDR (0x70008000 - sizeof(image_header_t))
    hw_init();
    ...   
    img_read_ret = read_nandflash((unsigned char *)OS_LOAD_TO_MEM_ADDR, 0x800000, 0x500000, 1);
    if (img_read_ret == 0)//读取OS Image成功
    {
         stopchr = dbgu_try_getc();    //recv & check stop cmd char 'u'
         if (stopchr != 'u')
             OsLoader(OS_LOAD_TO_MEM_ADDR);//启动Linux
    }
    //else read u-boot 
    read_nandflash((unsigned char *)JUMP_ADDR, (unsigned long)IMG_ADDRESS, (int)IMG_SIZE, 0);
    //jump to run u-boot
    ...
    大功告成,读取2.8M 非压缩 OS image 花费时间约400ms,CRC32 验证 IMAGE 344ms,启动Linux 1.0 秒,总共1.7秒(通常,我们不需要进行CRC32验证,可以把这344ms也省掉,共1.4秒即可启动完成)。
启动时按'u'进入U-boot,否则跳过U-boot直接进入Linux.
       
    附:修改过的函数代码,很简单,不再解释
#define IH_ARCH_ARM        2   
#define IH_OS_LINUX        5   
#define IH_TYPE_KERNEL        2   
#define IH_COMP_NONE        0   
#define PHYS_SDRAM            0x70000000      //物理内存起始地址
#define PHYS_SDRAM_SIZE            0x08000000  
#define OS_KERNEL_ARG_STRING "mem=128M lpj=99072 console=ttyS0,115200 noinitrd root=/dev/mtdblock2 init=/linuxrc rootfstype=yaffs2 rw mtdparts=atmel_nand:8M(uboot),8M(os),64M(root),-(vutc) quiet"


int get_os_img_len(unsigned long blockIdx, unsigned char *dst)
{
    image_header_t * hdr = (image_header_t *)dst;
    int ret;
    if (ntohl(hdr->ih_magic) != IMAGE_MAGIC)
        return -1;
    if (hdr->ih_arch != IH_ARCH_ARM)
        return -1;
    if (hdr->ih_os != IH_OS_LINUX)       
        return -1;
    if (hdr->ih_type != IH_TYPE_KERNEL)       
        return -1;
    if (hdr->ih_comp != IH_COMP_NONE)       
        return -1;
    ret = ntohl(hdr->ih_size);
    return ret + sizeof(image_header_t);
}


int read_nandflash(unsigned char *dst, unsigned long offset, int len, int is_os_img)
{
    SNandInfo sNandInfo;


    PSNandInitInfo pNandInitInfo;


    unsigned char *pOutBuffer = dst;


    unsigned int blockIdx, badBlock, length, sizeToRead, nbSector, sectorIdx, dataLeft;
    int osimglen = 0;


    nandflash_hw_init();
    reset_nandflash();


    // Read Nand Chip ID
    pNandInitInfo = AT91F_NandReadID();


    if (!pNandInitInfo) {
        dbg_log(DEBUG_INFO, "\n\r-E- No NandFlash detected !!!\n\r");
        return -1;
    }
    //dbg_log(1, "Copy %x bytes from %x to %x\r\n", len, offset, dst);


     // Initialize NandInfo Structure
     AT91F_NandInit(&sNandInfo, pNandInitInfo);


     if (!sNandInfo.uDataBusWidth)
         nandflash_cfg_8bits_dbw_init();


    // Initialize the block offset
    blockIdx = offset / sNandInfo.uBlockNbData;
    //Initialize the number of bad blocks
    badBlock = 0;
   
    length = len;


    while (length > 0) {
        //Read a buffer corresponding to a block in the origin file
        sizeToRead = (length < sNandInfo.uBlockNbData) ? length : sNandInfo.uBlockNbData;
        // Adjust the number of sectors to read
        nbSector = sizeToRead / sNandInfo.uDataNbBytes;
        if (sizeToRead % sNandInfo.uDataNbBytes) nbSector++;


        //Loop until a valid block has been read
        while (1) {// Read the sectors
            for (sectorIdx = 0; sectorIdx < nbSector; sectorIdx++) {
                dataLeft = sizeToRead - (sectorIdx * sNandInfo.uDataNbBytes);
                if (dataLeft < sNandInfo.uDataNbBytes) {
                    dataLeft = sizeToRead - (sectorIdx * sNandInfo.uDataNbBytes);
                } else {
                    dataLeft = sNandInfo.uDataNbBytes;
                }
                // Read the sector
                if (AT91F_NandRead(&sNandInfo, blockIdx, sectorIdx, ZONE_DATA, pOutBuffer) == FALSE)
                {// Move to next block                   
                    break;
                } else {
                    if (is_os_img){
                        osimglen = get_os_img_len(blockIdx, pOutBuffer);
                        if (osimglen < 0)//不是Image头部,不再读取,返回错误
                            return -1;//we just try 5 block for search os image.
                        length = osimglen;
                        is_os_img = 0;//只需检查第一块
                    }
                    pOutBuffer += sNandInfo.uDataNbBytes;
                }
            }
            blockIdx++;
            //The full block is valid, then  exit the loop
            if (sectorIdx >= nbSector)
                break;
         }
        // Decrement length
        length -= sizeToRead;
    }
    return 0;
}


int OsLoader(unsigned int Addr)
{
    unsigned long ep, load_addr, len;
    void (*theKernel) (int zero, int arch, unsigned int params);
    image_header_t *hdr;


    hdr = (image_header_t *) Addr;
    load_addr = ntohl(hdr->ih_load);
    ep = ntohl(hdr->ih_ep);   
    theKernel = (void (*)(int, int, unsigned int))ep;
    //verify crc
    {
         unsigned long dcrc, dcrc2, len;
         dcrc = ntohl(hdr->ih_dcrc);
         len = ntohl(hdr->ih_size);
         dcrc2 = crc32(0, Addr + sizeof(image_header_t), len);
         if (dcrc != dcrc2)
         {
             dbg_log(1, "Error, Image CRC check failed, need %x but %x\n\r", dcrc, dcrc2);
             return -1;
         }
    }
    setup_tags();


    if (ep != Addr + sizeof (image_header_t))
    {       
        dbgu_print(" Relocate Kernal...");
        len = ntohl(hdr->ih_size);
        memcpy((void *)load_addr,  (void *)((unsigned long)Addr + sizeof (image_header_t)), len);
        dbgu_print("OK\r\n");
    }   
    dbgu_print("Starting kernel...\r\n");   
    theKernel(0, MACH_TYPE, (unsigned int)(PHYS_SDRAM + 0x100));
    return 0;
}


void setup_tags()
{
    struct tag *tag = (struct tag *)(PHYS_SDRAM + 0x100);


    tag->hdr.tag = ATAG_CORE;
    tag->hdr.size = tag_size(tag_core);
    tag->u.core.flags = 0;
    tag->u.core.pagesize = 0;
    tag->u.core.rootdev = 0;
    tag = tag_next(tag);


    tag->hdr.tag = ATAG_MEM;
    tag->hdr.size = tag_size(tag_mem32);
    tag->u.mem.start = PHYS_SDRAM;
    tag->u.mem.size = PHYS_SDRAM_SIZE;
    tag = tag_next(tag);


    tag->hdr.tag = ATAG_CMDLINE;
    tag->hdr.size = (sizeof (struct tag_header) + strlen(OS_KERNEL_ARG_STRING) + 1 + 4) >> 2;
    strcpy(tag->u.cmdline.cmdline, OS_KERNEL_ARG_STRING);
    tag = tag_next(tag);
   
    tag->hdr.tag = ATAG_NONE;
    tag->hdr.size = 0;

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值