1. 程式人生 > >u-boot(五)核心啟動

u-boot(五)核心啟動

目錄


title: u-boot(五)核心啟動
tags: linux
date: 2018-09-26 19:58:05
---

u-boot(五)核心啟動

概述

啟動命令:bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

,具體程式碼實現的重點是以下:

  1. s=getenv ("bootcmd") 獲取環境變數

  2. run_command (s, 0); 啟動核心,這個s=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
  3. s就是先讀出核心,再啟動核心了

備註 jffs2是一種檔案格式,在這裡並不需要檔案格式,但是使用這個jffs2 可以不使用頁對齊,如果使用nandread,需要考慮頁對齊或者塊對齊,最終會使用nand_read_opts

  • 我們也可以在u-boot 命令列直接輸入boot來啟動核心,但是實際的命令是一樣的,是在cmd_bootm.c中呼叫do_bootd
    >run_command (getenv ("bootcmd"), flag)

分割槽空間

常見的內部空間佈局如下:

Bootloader Boot parameters Kernel Root filesystem
u-boot,它會在記憶體的某個地方存放著核心啟動的一些引數,也稱為tag u-boot 引數,包含傳遞給核心的一些東西 核心 根檔案系統

嵌入式的FLASH沒有實際的分割槽,所謂分割槽只是一個名稱,具體的地址是寫死的. 在include/configs/100ask24x0.h

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

這裡定義了mtdparts分割槽,位於nandflash0bootloader大小是256k,從0開始,然後是128k大小的params,接下去是2M的kernel核心,剩餘的都是root檔案系統.

核心檔案格式

Flash上儲存的核心格式為uImage,包含了一個頭部加真正的核心.

/*
 * all data in network byte order (aka natural aka bigendian)
 */
#define IH_NMLEN        32  /* Image Name Length        */

typedef struct image_header {
    uint32_t    ih_magic;   /* Image Header Magic Number    */
    uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
    uint32_t    ih_time;    /* Image Creation Timestamp */
    uint32_t    ih_size;    /* Image Data Size      */
    uint32_t    ih_load;    /* Data  Load  Address      */
    uint32_t    ih_ep;      /* Entry Point Address      */
    uint32_t    ih_dcrc;    /* Image Data CRC Checksum  */
    uint8_t     ih_os;      /* Operating System     */
    uint8_t     ih_arch;    /* CPU architecture     */
    uint8_t     ih_type;    /* Image Type           */
    uint8_t     ih_comp;    /* Compression Type     */
    uint8_t     ih_name[IH_NMLEN];  /* Image Name       */
} image_header_t;
  • ih_load表示載入地址,表示核心應該放在哪裡
  • ih_ep表示入口地址,表示跳轉的地址,也就是核心程式碼段的入口,廣義上的main入口

核心複製跳轉

bootm會先判斷核心是否在載入地址,否則先移動核心到指定的載入地址,然後跳轉。

命令中0x30007FC0 地址可以隨便放,只要不破壞已經用到的資訊就好, bootm命令如果發現當前核心並不在載入地址,需要移動核心到載入地址。do_bootm函式中memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); 移動核心。

所以如果ih_load=我們核心的地址的時候,就不需要move,節省時間.jz2440 的載入地址是0x30008000,頭部是64位元組,所以,0x30008000-64=0x30007FC0,所以我們copy核心到0x30007FC0 .

核心啟動

//在 bootm命令中有linux核心跳轉,//lib_arm/armlinux.c-->do_bootm_linux
do_bootm_linux  (cmdtp, flag, argc, argv,addr, len_ptr, verify);
    //theKernel 就是uimage的頭部中的入口地址
    -theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
    // 設定一些引數
    setup_start_tag (bd);
    setup_memory_tags (bd);
    setup_commandline_tag (bd, commandline);
    setup_end_tag (bd);
    // 所以核心的入口引數
    -theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

機器ID

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);中的第二個引數是機器ID,核心通過比對機器ID判斷是否支援啟動.gd->bd->bi_arch_number = MACH_TYPE_S3C2440;

啟動引數

核心跳轉之前,同樣需要設定核心的啟動引數.核心的引數是按照tag組織的.也就是在某個地址(0x30000100,在100ask24x0.c中定義),按照某種格式儲存,這種格式具體為【size....tagid....tag值】

mark

do_bootm_linux中有設定記憶體,命令列引數等,程式碼片段如下

bd_t *bd = gd->bd;
//設定起始的頭
setup_start_tag (bd);
//設定記憶體
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
//....
// 設定結束的id
setup_end_tag (bd);

具體有以下幾種tag,程式碼中以聯合體定義,這樣方便使用同一個指標指向它,方便之處見setup_start_tag分析.

//這個tag 就是一個包含了所有型別tag的一個聯合體,是實際tag的內容值
struct tag {
    struct tag_header hdr;
    union {
        struct tag_core     core;
        struct tag_mem32    mem;
        struct tag_videotext    videotext;
        struct tag_ramdisk  ramdisk;
        struct tag_initrd   initrd;
        struct tag_serialnr serialnr;
        struct tag_revision revision;
        struct tag_videolfb videolfb;
        struct tag_cmdline  cmdline;

        /*
         * Acorn specific
         */
        struct tag_acorn    acorn;

        /*
         * DC21285 specific
         */
        struct tag_memclk   memclk;
    } u;
};

(起始tag)setup_start_tag

static void setup_start_tag (bd_t *bd)
{
    // 這個tag 就是一個包含了所有型別tag的一個聯合體
    // 使用聯合體之後,下面就可以使用 params->具體的tag型別
    params = (struct tag *) bd->bi_boot_params;

    params->hdr.tag = ATAG_CORE;
    params->hdr.size = tag_size (tag_core);
    
    //tag_core 也就是接下去這三個引數了
    //tag_size =zise + tag + 實際的值
    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;
    //指向下一個引數
    params = tag_next (params);
}

#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
// 這裡 tag_header就是 size+tag   , type 就是實際的tag的內容
// tag_size就是包含 id 和 size 和 內容的大小了

因為bd_t *bd = gd->bd;,所以搜尋下gd->bd->bi_boot_params,也就是在board/100ask24x0/100ask24x0.c中定義,也就是說引數是放在0x30000100.

gd->bd->bi_boot_params = 0x30000100;

記憶體設定

static void setup_memory_tags (bd_t *bd)
{
    int i;

    for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
        params->hdr.tag = ATAG_MEM;
        params->hdr.size = tag_size (tag_mem32);

        params->u.mem.start = bd->bi_dram[i].start;
        params->u.mem.size = bd->bi_dram[i].size;

        params = tag_next (params);
    }
}

搜尋下gd->bd->bi_dram[0],同樣在board/100ask24x0/100ask24x0.c定義

int dram_init (void)
{
    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
    gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

    return 0;
}
#define PHYS_SDRAM_1        0x30000000 /* SDRAM Bank #1 */
#define PHYS_SDRAM_1_SIZE   0x04000000 /* 64 MB */

這個函式是在lib_arm/board.c中的init_sequence呼叫,也就是start_armboot中呼叫,也就是在u-boot(三)第一階段的C中使用的

根檔案系統,啟動程式,串列埠裝置

char *commandline = getenv ("bootargs");
setup_commandline_tag (bd, commandline);

static void setup_commandline_tag (bd_t *bd, char *commandline)
{
    char *p;

    if (!commandline)
        return;

    /* eat leading white space */
    for (p = commandline; *p == ' '; p++);

    /* skip non-existent command lines so the kernel will still
     * use its default command line.
     */
    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,多了引數commandline,源自環境變數bootargs檢視下環境變數bootargs,使用print檢視,也可搜尋下程式碼

"bootargs=" CONFIG_BOOTARGS         "\0"
//include/configs/100ask24x0.h
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"
  • root=/dev/mtdblock3表示根檔案系統從第四個FLASH分割槽開始(從0開始計數)可以往上看分割槽空間
  • init=/linuxrc指示第一個應用程式
  • console=ttySAC0,核心列印資訊從串列埠0 列印

(結束)setup_end_tag

設定結束標誌