u-boot啓動時間優化

原文鏈接: https://blog.csdn.net/linux_0416/article/details/79637110

1.去掉啓動時的按鍵等待

U-boot 啓動的時候出現一個 Hit any key to stopautoboot 不爽,幹嗎要停上1秒?雖然可以通過設置參數bootdelay=0來關掉這個延時,但這樣做了以後就再也進不去U-boot了,更煩。檢查代碼,發現是在main.c函數intabortboot(int bootdelay)來幹這個活的,好吧,改掉它

static __inline__ intabortboot(int bootdelay)
    {
       int abort = 0;
       char inputkey;

    if(tstc())
       { 
           inputkey =getc(); 
           abort = (inputkey == 'u');

    }
       #ifdef CONFIG_SILENT_CONSOLE
         if (abort)
              gd->flags&= ~GD_FLG_SILENT;
       #endif
       return abort;
   }

這樣,就不需要等待了,如果想進入U-boot,就在上電的時候按住u吧,把它改成一個固定的鍵而不是任意鍵,因爲串口線很容易受到干擾,如果是任意鍵的話,運行時即使不想進去有時也會進入U-boot的命令行。 

2.去掉網卡的初始化

每次上電,U-boot都會初始化網卡,其實這根本不需要,把配置文件中

#define CONFIG_MII   1

去掉,啓動時就不會初始化了,需要使用TFTP時,它會自動初始化,又節省了3.4秒的啓動時間。

3.智能讀取OSImage

U-boot 通過nand read 來讀取OSImage,通常爲了避免麻煩,我們設置的讀取長度要比實際OS長度長一些,多讀的那部分純粹是浪費CPU時間,能不能精確判斷讀取長度呢,當然可以,爲了不影響系統的正常功能,擴展一個nandread.os 指令來讀取,修改方法如下:

在 nand_read_options_t裏面添加一個成員 int is_os_img

在函數 int do_nand(cmd_tbl_t* cmdtp, int flag, int argc, char *argv[])

修改讀操作的判斷語句,添加 !strcmp(s,".os"),然後設置opts.is_os_img = !strcmp(s, ".os");大概修改後結果參考第7步代碼。

 
  最後,在函數intnand_read_opts(nand_info_t *meminfo, const nand_read_options_t*opts)中修改
代碼,檢測如果opts->is_os_img == 1並且是第一次讀取(2024B)之後,檢查度取得結果是否是OSImage,如果是更新需要讀取的長度,否則,也不需要再往下讀了,直接返回錯誤就可以了

   image_header_t  *hdr = (image_header_t*)buffer;    
   if(image_check_magic(hdr) &&image_check_hcrc (hdr))
   {
        size_t ossz= uimage_to_cpu(hdr->ih_size);//+image_get_header_size();
        imglen =ossz + + image_get_header_size();
        printf("##Find valid OS image, at 0x%x, Size: %d Bytes = %d KB\n",
                     (unsigned int)mtdoffset, ossz,ossz/1024);
   }
   else
   {
        printf("Invalid OS image at 0x%x\n", (unsignedint)mtdoffset);
        return -1;
   }

4.去掉OS Image內存複製過程

使用 nand read 讀取OS Image後,U-boot 使用 bootm 指令來啓動Linux,檢查其實現代碼

int do_bootm (cmd_tbl_t*cmdtp, int flag, int argc, char *argv[])

發現他會把已經讀取到內存中的OS Image在複製到一個指定的位置,OS Image 中的頭部參數,這個值一般是固定的,本系統使用的是 0x70008000, 如果在 nandread 時讀到的內存位置恰好合適,就可以省掉這些毫秒數了,做法如下:

nand read.os 0x70007FC0 0x100000  0x500000

(其中 0x70007FC0 =0x70008000 - sizeof(sizeof(image_header_t)))

然後在內存複製的地方(函數do_bootm中),加入修改,跳過內存複製

switch(comp) {
    caseIH_COMP_NONE:
       if(load_start == (ulong)os_hdr) {
          printf("   XIP %s ... ",type_name);
       }else{
        //add=====start
         if(load_start != os_data)//位置不匹配,依然移動,否則就跳過此部
         {
             printf("   Loading %s ... ",type_name);
             memmove_wd((void *)load_start, (void *)os_data, os_len,CHUNKSZ);
             puts("OK\n");
         }
         //add=====end
      }
      load_end =load_start + os_len;

  對於我們的Kernel,修改後大小是1.4M,省去這個搬移過程,節省了大約800ms的時間

5.減少內存初始化的時間

在U-boot 初始化時,在start_armboot 函數中,多次使用到了memset函數,其中最耗時的是在mem_malloc_init函數中調用memset 初始化 512K內存的調用,檢查U-boot1.3.4對memset的實現,發現是最簡單的字節複製,把它改爲按32bits複製的方式,這些memset調用所花費的時間立即從202ms減少到了45ms

修改方法,再 string.c中,找到memset函數,修改其實現(代碼是從U-boot 2011.12 中複製過來的)

void * memset(void *s,int c,size_t count)
    {
       unsigned long *sl = (unsigned long *) s;
       unsigned long cl = 0;
       char *s8;
       int i;
  
       if ( ((ulong)s& (sizeof(*sl) - 1)) == 0) {
          for (i = 0; i <sizeof(*sl); i++) {
             cl<<= 8;
             cl |= c & 0xff;
          }
          while (count>= sizeof(*sl)) {
             *sl++ = cl;
             count -= sizeof(*sl);
          }
       }
   
       s8 = (char *)sl;
       while (count--)
          *s8++ = c;
       return s;
    }

6.減少NAND初始化時間
    每次 U-boot啓動,發現NAND初始化需要大約3秒的時間,仔細追蹤發現,在nand_base.c文件中nand_scan函數的最後一步returnthis->scan_bbt(mtd);最花費時間,這個scan_bbt掃描整個NAND並檢查壞塊,重建壞塊表,在啓動過程中,這個耗時的操作毫無意義,去掉這一步,讓nand_scan函數直接返回0就可以了。

7.添加Yaffs2支持

從網上各位前輩的論述中,都發現YAFFS比JFFS2要快,也決定測試一下,從YAFFS網站下載最新的代碼,按照說明加入到Linux中,重新編譯內核,讓內核支持YAFFS2(按照默認的選項就可以了),弄一個空的分區,格式化成YAFFS2格式,感覺的確比較快,把ROOTFS複製到這個分區,然後修改Linux啓動參數讓它把YAFFS2分區當作根分區啓動,發現果然快了不少,初始化和掛載根分區僅需要370ms,比JFFS2的速度快多了,決定就採用YAFFS2作爲根文件系統了。自己在u-boot中添加對yaffs2image的支持

說起來容易,真正做起來還是很麻煩的,總是不能把yaffs2的image燒寫成功,不知道是Image不正確還是Uboot沒改對,折騰了幾天也沒搞定,最後終於發現了一個第三方的工具  http://code.google.com/p/yaffs2utils/ 

下載,編譯,製作Image,驗證,OK,把新工具生成的IMAGE與YAFFS2自帶的工具對照,發現YAFFS2自帶的工具生成的IMAGE不正確,暈死。

重新修改UBoot,改了很少一部份代碼,就可以了。

依然是在函數do_nand中修改,添加一個擴展nand write.y 指令來寫入Image:

按照慣例,YAFFS2的第一個塊不使用,留給文件系統自己使用,在 nand_write_options_t 裏面添加一個成員 intskip_first_block;

在函數 int do_nand(cmd_tbl_t* cmdtp, int flag, int argc, char *argv[])

修改讀寫操作的判斷語句,添加 !strcmp(s,".y"),然後設置opts.is_os_img = !strcmp(s, ".os");大概修改後結果如下(紅色部分)

  s = strchr(cmd, '.');
        //add -------|| !strcmp(s, ".os") || !strcmp(s,".y")
       if (s !=NULL && (!strcmp(s, ".jffs2") ||!strcmp(s, ".e") || !strcmp(s, ".i") || !strcmp(s, ".os") || !strcmp(s,".y")))    {
          if (read){
            
            nand_read_options_t opts;
            memset(&opts, 0, sizeof(opts));
            opts.buffer    =(u_char*) addr;
            opts.length    =size;
            opts.offset    =off;
             opts.readoob= 0;//remove this function.
            //add ====start
            opts.is_os_img = !strcmp(s,".os");
//add ===end
            opts.quiet     = quiet;
             ret =nand_read_opts(nand, &opts);
            //printf("call nand_read_opts buffer %lu len %lu offset %d off, ret%d\n", addr, size, off, ret);
          } else{
            
            nand_write_options_t opts;
            memset(&opts, 0, sizeof(opts));
            opts.buffer    =(u_char*) addr;
            opts.length    =size;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章