1. 程式人生 > >Uboot到底如何啟動核心

Uboot到底如何啟動核心

1.uboot啟動核心的程式碼縮減如下:

Uboot 1.16/lib_arm/board.c中start_armboot()函式呼叫/common/main.cmain_loop()函式,在main_loop()中有uboot啟動核心的程式碼:

s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s :"<UNDEFINED>");
if (bootdelay >= 0 && s && !abortboot (bootdelay))
{
                run_command(s, 0);
}


2.假設bootcmd = nandread.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

<1> nandread.jffs2 0x30007FC0 kernel

從nand讀出核心:

從哪裡讀?  :kernel分割槽

讀到哪裡去?:0x30007FC0

 

何為分割槽?

簡單的說就是將nand劃分為幾個區域,一般如下:

 

bootloader->params->kernel->root

 

這些分區劃分在/include/configs/100ask24x0.h中寫死的:

 

#define MTDPARTS_DEFAULT"mtdparts=nandflash0:[email protected](bootloader)," \
           "128k(params)," \
           "2m(kernel)," \
           "-(root)"


進入uboot執行mtd ,可以檢視已有分割槽:

#       name                    大小              在nand上的起始地址         

0       bootloader        0x00040000               0x00000000

1       params             0x00020000               0x00040000              

2       kernel               0x00200000               0x00060000

3       root                 0xfda00000               0x00260000

 

上面的nand read.jffs2 0x30007FC0 kernel等價於:

nand read.jffs20x30007FC0 0x00060000 0x00200000

注:read.jffs2並不是指定特定的格式,僅表示不需要塊/頁對齊,所以kernel的分割槽大小可以隨意定。

 

<2> bootm0x30007FC0

關鍵函式do_bootm()

 

flash上存的核心:uImage

uImage =頭部+真正的核心

 

 

頭部的定義如下:


typedef struct image_header {
        uint32_t       ih_magic;       

        uint32_t       ih_hcrc;        

        uint32_t       ih_time;       

        uint32_t       ih_size;       

        uint32_t       ih_load;      

        uint32_t       ih_ep;            

        uint32_t       ih_dcrc;      

        uint8_t               ih_os;         

        uint8_t               ih_arch;     

        uint8_t               ih_type;   

        uint8_t               ih_comp;   

        uint8_t              ih_name[IH_NMLEN];       

 } image_header_t;

 

我們需要關心:

       uint32_t       ih_load;             

        uint32_t       ih_ep;              

ih_load是載入地址,即核心執行是應該位於的地方   

ih_ep是入口地址,即核心的入口地址

 

這與uboot類似,uboot的載入地址是TEXT_BASE = 0x33F80000;入口地址是start.S中的_start。

 

從nand讀出來的核心可以放在ram中的任意地方,如0x31000000,0x32000000等等,只要它不破壞uboot所佔用的記憶體空間就可以

 

既然設定好了載入地址和入口地址,為什麼核心還能隨意放?

因為uImage有一個頭部!頭部裡有載入地址和入口地址,當我們用bootm xxx時,

do_bootm先去讀uImage的頭部以獲取該uImage的載入地址和入口地址,當發現該uImage目前所處的記憶體地址不等於它的載入地址時,會將uImage移動到它的載入地址上,程式碼中體現如下:

uboot 1.16/common/cmd_bootm.c中的bootm_load_os()函式

case IH_COMP_NONE::
if (load != image_start)
{
                memmove_wd((void *)load, (void *)image_start, image_len, CHUNKSZ);
}


另外,當核心正好處於頭部指定的載入地址,便不用uboot的do_bootm函式來幫我們搬運核心了,可以縮短啟動時間。這就是為什麼我們一般都下載uImage到0x30007FC0的原因。

 

核心載入地址是0x30008000,而頭部的大小64個位元組,將核心拷貝到0x30007FC0,加上頭部的64個位元組,核心正好位於0x30008000處。

 

總結bootm做了什麼:

1.讀取頭部

2.將核心移動到載入地址

3.啟動核心

 

具體如何啟動核心?

使用在/lib_arm/bootm.c定義的do_bootm_linux(),我們已經知道入口地址,只需跳到入口地址就可以啟動linux核心了,在這之前需要做一件事———— uboot傳遞引數(啟動引數)給核心。

啟動程式碼在do_bootm_linux()函式:

void (*theKernel)(int zero, int arch,uint params);  //定義函式指標theKernel

       theKernel = (void (*)(int, int, uint))images->ep;    //先是將入口地址賦值給theKernel

       theKernel (0, bd->bi_arch_number, bd->bi_boot_params); //然後是呼叫thekernel,以0,bd->bi_arch_number,bd->bi_boot_params為引數

 

下面分析這三個引數:

1.  0—相當於mov,ro #0

2.bd->bi_arch_number:uboot機器碼,這個在/board/100ask24x0.c設定:gd->bd->bi_arch_number = MACH_TYPE_S3C2440,MACH_TYPE_S3C2440在/arch/arm/asm/mach-types.h定義:362,核心機器碼和uboot機器碼必須一致才能啟動核心

 

2. bd->bi_boot_parmas--- 啟動引數地址

也是在在/board/100ask24x0.c設定:gd->bd->bi_boot_params = 0x30000100;

 

啟動引數(tag)在哪裡設定?

在lib_arm/armlinux.c設定:

setup_start_tag (bd);
setup_revision_tag (parmas);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
setup_initrd_tag (bd, images->rd_start, images->rd_end);
setup_videolfb_tag ((gd_t *) gd);
setup_end_tag (bd);


每一個啟動引數對應一個tag結構體,所謂的設定傳遞引數其實就是初始化這些tag的值,想了解這個結構體以及這些tag的值是如何設定的請看嵌入式Linux應用開發完全手冊的uboot章節
 
我們來看setup_start_tag(bd)函式:

static void setup_start_tag (bd_t *bd)
{
        params = (struct tag *) bd->bi_boot_params;  
        params->hdr.tag = ATAG_CORE;
        params->hdr.size = tag_size(tag_core);
        params->u.core.flags = 0;
        params->u.core.pagesize = 0;
        params->u.core.rootdev = 0;
        params = tag_next (params);
}


再看setup_commandline_tag (bd , commandline):

static void setup_commandline_tag (bd_t *bd, char*commandline)
{
 // commandline就是我們的bootargs
        char *p;
        if (!commandline)
                return;
        for (p = commandline; *p == ' '; p++);
        if (*p == '\0')
                return;
        params->hdr.tag = ATAG_CMDLINE;
        params->hdr.size =
                (sizeof(struct tag_header) + strlen (p) + 1 + 4) >> 2;
        strcpy (params->u.cmdline.cmdline, p);
        params = tag_next (params);
}

 

核心啟動時會讀取這些tag(引數)並跳轉啟動。

 

更多uboot啟動核心的細節觀看畢業班視訊自己寫uboot。
--------------------- 
作者:韋東山 
來源:CSDN 
原文:https://blog.csdn.net/thisway_diy/article/details/76064244 
版權宣告:本文為博主原創文章,轉載請附上博文連結!