[uboot]uboot起動kernel編(二)-bootmがkernelにジャンプする流れ

30421 ワード

一、bootm説明
bootmこのコマンドは、オペレーティングシステムイメージを起動するために使用されます.イメージファイルのベースのcpuアーキテクチャ、オペレーティングシステムのタイプ、イメージのタイプ、圧縮方法、メモリ内のイメージファイルのロードアドレス、イメージファイルの実行のエントリアドレス、イメージファイル名など、イメージファイルのヘッダから情報を取得します.次にbootmはイメージを指定したアドレスにロードし、必要に応じてイメージを解凍し、カーネルに必要なパラメータを渡し、最後にエントリアドレスにジャンプしてカーネルに入ります.ここでの説明は(http://blog.chinaunix.net/uid-20799298-id-99666.html)
開く必要があるマクロ
CONFIG_BOOTM_LINUX=y
CONFIG_CMD_BOOTM=y

二、bootmの使い方
『uboot起動kernel編(一)——Legacy-uImage&FIT-uImage』では、uImageには2つのフォーマットがあることがわかりました.
  • Legacy-uImage Legacy-uImageの場合、RAMにramdiskとfdtを別途ロードする必要があります.実行するコマンドは次の
  • です.
      Legacy-uImage      0x20008000,ramdisk      0x21000000,fdt      0x22000000
    
    (1)    kernel    
    bootm 0x20008000
    
    (2)   kernel ramdisk
    bootm 0x20008000 0x21000000
    
    (3)   kernel fdt
    bootm 0x20008000 - 0x22000000
    
    (4)   kernel、ramdisk、fdt
    bootm 0x20008000 0x21000000 0x22000000
  • FIT-uImage FIT-uImageでは、kernelミラー、ramdiskミラー、fdtがFIT-uImageのミラーにパッケージされています.実行するコマンドは次の
  • です.
      FIT-uImage      0x30000000,  kernel     :
    bootm 0x30000000

    三、bootm実行プロセス
    まず、「[uboot](第6章)ubootプロセス--コマンドラインモードおよびコマンド処理の紹介」を参照することをお勧めします.
  • 対応U_BOOT_CMD bootmコマンドに対応するU_を見つけましたBOOT_CMDは、cmd/bootm.c
  • U_BOOT_CMD(
        bootm,  CONFIG_SYS_MAXARGS, 1,  do_bootm,
        "boot application image from memory", bootm_help_text
    );
  • do_bootmパラメータ説明「[uboot](第6章)ubootプロセス-コマンドラインモードおよびコマンド処理紹介」によりbootmコマンドを実行するとdo_bootmは呼び出されます.パラメータは次のとおりです.
  •    ‘bootm 0x20008000 0x21000000 0x22000000int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    cmdtp:    bootm       ,   _u_boot_list_2_cmd_2_bootm   
    argc=4
    argv[0]="bootm", argv[1]=0x20008000, arv[2]=0x21000000, argv[3]=0x22000000
  • do_bootm実装do_bootmは以下のように実現される:
  • 
    /*******************************************************************/
    /* bootm - boot application image from image in memory */
    /*******************************************************************/
    
    int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
        /* determine if we have a sub command */
        argc--; argv++;
        if (argc > 0) {
            char *endp;
    
            simple_strtoul(argv[0], &endp, 16);
            /* endp pointing to NULL means that argv[0] was just a
             * valid number, pass it along to the normal bootm processing
             *
             * If endp is ':' or '#' assume a FIT identifier so pass
             * along for normal processing.
             *
             * Right now we assume the first arg should never be '-'
             */
            if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
                return do_bootm_subcommand(cmdtp, flag, argc, argv);
        }
            //            ,      
            //    ,    bootm      ,
            //     'bootm 0x20008000 0x21000000 0x22000000' 
            // argc=3, argv[0]=0x20008000 , argv[1]=0x21000000, argv[2]=0x22000000
            //  ‘bootm 0x30000000’ 
            // argc=1, argv[0]=0x30000000
    
        return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
            BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
            BOOTM_STATE_LOADOS |
            BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
            BOOTM_STATE_OS_GO, &images, 1);
            //       do_bootm_states, do_bootm_states       states    :
            // BOOTM_STATE_START
            // BOOTM_STATE_FINDOS 
            // BOOTM_STATE_FINDOTHER 
            // BOOTM_STATE_LOADOS 
            // BOOTM_STATE_OS_PREP 
            // BOOTM_STATE_OS_FAKE_GO 
            // BOOTM_STATE_OS_GO
    }

    だからbootmのコアはdo_bootm_states、グローバル変数bootm_headers_t imagesをdo_としてbootm_statesのパラメータ.この関数について詳しく説明します.
    四、do_bootm_statesソフトウェアプロセス
    1、データ構造説明
  • bootm_headers_t bootm_headers_tはbootmがkernelを起動するいくつかの情報を表す構造体であり、os/initrd/fdt imagesの情報が含まれている.bootmは、パラメータとパラメータが指すミラーに基づいて、この構造問題のメンバーを埋めます.最終的には、この構造体の中の情報を使用してkernel起動情報を埋め込み、kernelにジャンプします.ubootでグローバルなbootm_を使用しましたheaders_t images.
  • typedef struct bootm_headers {
        /*
         * Legacy os image header, if it is a multi component image
         * then boot_get_ramdisk() and get_fdt() will attempt to get
         * data from second and third component accordingly.
         */
        image_header_t  *legacy_hdr_os;     /* image header pointer */  // Legacy-uImage    
        image_header_t  legacy_hdr_os_copy; /* header copy */ // Legacy-uImage      
        ulong       legacy_hdr_valid; // Legacy-uImage           
    
    #if IMAGE_ENABLE_FIT
        const char  *fit_uname_cfg; /* configuration node unit name */ //      
    
        void        *fit_hdr_os;    /* os FIT image header */ // FIT-uImage kernel   
        const char  *fit_uname_os;  /* os subimage node unit name */ // FIT-uImage kernel    
        int     fit_noffset_os; /* os subimage node offset */ // FIT-uImage kernel     
    
        void        *fit_hdr_rd;    /* init ramdisk FIT image header */ // FIT-uImage ramdisk    
        const char  *fit_uname_rd;  /* init ramdisk subimage node unit name */ // FIT-uImage ramdisk    
        int     fit_noffset_rd; /* init ramdisk subimage node offset */ // FIT-uImage ramdisk     
    
        void        *fit_hdr_fdt;   /* FDT blob FIT image header */ // FIT-uImage FDT    
        const char  *fit_uname_fdt; /* FDT blob subimage node unit name */ // FIT-uImage FDT    
        int     fit_noffset_fdt;/* FDT blob subimage node offset */ // FIT-uImage FDT     
    #endif
    
        image_info_t    os;     /* os image info */ //           
        ulong       ep;     /* entry point of OS */ //          
    
        ulong       rd_start, rd_end;/* ramdisk start/end */ // ramdisk              
    
        char        *ft_addr;   /* flat dev tree address */ // fdt       
        ulong       ft_len;     /* length of flat device tree */ // fdt       
    
        ulong       initrd_start; // 
        ulong       initrd_end; // 
        ulong       cmdline_start; // 
        ulong       cmdline_end; // 
        bd_t        *kbd; // 
    
        int     verify;     /* getenv("verify")[0] != 'n' */ //       
        int     state; //     ,       bootm       ,     2.
    
    #ifdef CONFIG_LMB
        struct lmb  lmb;        /* for memory mgmt */
    #endif
    
    } bootm_headers_t;

    2、状態説明
  • BOOTM_STATE_START #define BOOTM_STATE_START(0 x 000000000001)はbootmのいくつかの準備動作を開始する.
  • BOOTM_STATE_FINDOS #define BOOTM_STATE_FINDOS(0 x 000000002)オペレーティングシステムミラーの検索
  • BOOTM_STATE_FINDOTHER #define BOOTM_STATE_FINDOTHER(0 x 0000004)は、FDTramdiskなどのオペレーティングシステムのミラー以外のミラーを検索します.
  • BOOTM_STATE_LOADOS #define BOOTM_STATE_LOADOS(0 x 0000008)オペレーティングシステム
  • をロード
  • BOOTM_STATE_RAMDISK #define BOOTM_STATE_RAM DISK(0 x 0000010)操作ramdisk
  • BOOTM_STATE_FDT #define BOOTM_STATE_FDT(0 x 0000020)動作FDT
  • BOOTM_STATE_OS_CMDLINE #define BOOTM_STATE_OS_CMDLINE(0 x 0000040)操作commandline
  • BOOTM_STATE_OS_BD_T #define BOOTM_STATE_OS_BD_T (0x00000080)
  • BOOTM_STATE_OS_PREP #define BOOTM_STATE_OS_PREP(0 x 0000 0100)オペレーティングシステムにジャンプする前の準備動作
  • BOOTM_STATE_OS_FAKE_GO #define BOOTM_STATE_OS_FAKE_GO(0 x 0000200)/*‘Almost’run the OS*/擬似ジャンプは、一般的にkernelに直接ジャンプして
  • に行くことができます.
  • BOOTM_STATE_OS_GO #define BOOTM_STATE_OS_GO(0 x 0000 0400)kernelにジャンプして

  • 3、ソフトウェアプロセスの説明
    do_bootm_statesはstatesに基づいて実行する操作を判断する.
  • 主な流れは以下のように簡単に説明される.
  • bootmの準備動作BOOTM_STATE_START
  • kernel情報BOOTM_を取得STATE_FINDOS
  • ramdiskとfdtの情報BOOTM_を取得STATE_FINDOTHER
  • kernelを対応する位置にロード(この位置にある可能性があります)BOOTM_STATE_LOADOS
  • リダイレクトramdiskとfdt(必ずしも必要としない)BOOTM_STATE_RAMDISK、BOOTM_STATE_FDT
  • ジャンプ実行前の準備動作BOOTM_STATE_OS_PREP
  • 起動パラメータを設定し、kernelがあるアドレスのBOOTM_にジャンプSTATE_OS_GO


  • これらのプロセスでは、bootm_が伝達されます.headers_t imagesというデータ構造は、ミラーを解析し、この構造体にデータを書く流れがあります.ジャンプするときは、この構造体の中のデータを使う必要があります.
  • ソフトウェアコードは以下のcommon/bootm.c
  • int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
                int states, bootm_headers_t *images, int boot_progress)
    {
        boot_os_fn *boot_fn;
        ulong iflag = 0;
        int ret = 0, need_boot_fn;
    
        images->state |= states;
           //  states  bootm_headers_t images  
    
        /*
         * Work through the states and see how far we get. We stop on
         * any error.
         */
            //   states    BOOTM_STATE_START  ,   bootm     ,       bootm_start
        if (states & BOOTM_STATE_START)
            ret = bootm_start(cmdtp, flag, argc, argv);
    
            //   states    BOOTM_STATE_FINDOS  ,     kernel  ,       bootm_find_os
        if (!ret && (states & BOOTM_STATE_FINDOS))
            ret = bootm_find_os(cmdtp, flag, argc, argv);
    
        ·   //   states    BOOTM_STATE_FINDOTHER  ,     ramdisk fdt        ,       bootm_find_other
        if (!ret && (states & BOOTM_STATE_FINDOTHER)) {
            ret = bootm_find_other(cmdtp, flag, argc, argv);
            argc = 0;   /* consume the args */
        }
    
            /*        ,          uImage     bootm_headers_t images */
            /*       uImage        */
            /*        bootm_headers_t images             */
    
    
    
        /* Load the OS */
            //   states    BOOTM_STATE_LOADOS  ,            ,       bootm_load_os
        if (!ret && (states & BOOTM_STATE_LOADOS)) {
            ulong load_end;
    
            iflag = bootm_disable_interrupts();
            ret = bootm_load_os(images, &load_end, 0);
            if (ret == 0)
                lmb_reserve(&images->lmb, images->os.load,
                        (load_end - images->os.load));
            else if (ret && ret != BOOTM_ERR_OVERLAP)
                goto err;
            else if (ret == BOOTM_ERR_OVERLAP)
                ret = 0;
    #if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)
            if (images->os.os == IH_OS_LINUX)
                fixup_silent_linux();
    #endif
        }
    
            //        ramdinsk,do_bootm         
        /* Relocate the ramdisk */
    #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
        if (!ret && (states & BOOTM_STATE_RAMDISK)) {
            ulong rd_len = images->rd_end - images->rd_start;
    
            ret = boot_ramdisk_high(&images->lmb, images->rd_start,
                rd_len, &images->initrd_start, &images->initrd_end);
            if (!ret) {
                setenv_hex("initrd_start", images->initrd_start);
                setenv_hex("initrd_end", images->initrd_end);
            }
        }
    #endif
    
            //        fdt,do_bootm         
    #if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)
        if (!ret && (states & BOOTM_STATE_FDT)) {
            boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
            ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,
                        &images->ft_len);
        }
    #endif
    
        /* From now on, we need the OS boot function */
        if (ret)
            return ret;
    
            //              ,   boot_fn 
        boot_fn = bootm_os_get_boot_func(images->os.os);
        need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
                BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
                BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
        if (boot_fn == NULL && need_boot_fn) {
            if (iflag)
                enable_interrupts();
            printf("ERROR: booting os '%s' (%d) is not supported
    "
    , genimg_get_os_name(images->os.os), images->os.os); bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); return 1; } /* Call various other states that are not generally used */ if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); if (!ret && (states & BOOTM_STATE_OS_BD_T)) ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); // , , BOOTM_STATE_OS_PREP if (!ret && (states & BOOTM_STATE_OS_PREP)) ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); /* Check for unsupported subcommand. */ if (ret) { puts("subcommand not supported
    "
    ); return ret; } // BOOTM_STATE_OS_GO , , /* Now run the OS! We hope this doesn't return */ if (!ret && (states & BOOTM_STATE_OS_GO)) ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, images, boot_fn); /* Deal with any fallout */ err: if (iflag) enable_interrupts(); if (ret == BOOTM_ERR_UNIMPLEMENTED) bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); else if (ret == BOOTM_ERR_RESET) do_reset(cmdtp, flag, argc, argv); return ret; }

    主に次のような順序でいくつかの関数を使用して実現されます.
  • bootm_start
  • bootm_find_os
  • bootm_find_other
  • bootm_load_os
  • bootm_os_get_boot_func
  • boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
  • boot_selected_os
  • boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,images, boot_fn);

  • 4、bootm_start & bootm_find_os & bootm_find_other
    主に環境変数、パラメータ、uImageを解析してbootm_を埋めますheaders_t imagesというデータ構造.最終的な目的はbootm_を実現することですheaders_t imagesのメンバー:
    typedef struct bootm_headers {
        image_info_t    os;     /* os image info */
        ulong       ep;     /* entry point of OS */
    
        ulong       rd_start, rd_end;/* ramdisk start/end */
    
        char        *ft_addr;   /* flat dev tree address */
        ulong       ft_len;     /* length of flat device tree */
    
        ulong       initrd_start;
        ulong       initrd_end;
        ulong       cmdline_start;
        ulong       cmdline_end;
        bd_t        *kbd;
        int     verify;     /* getenv("verify")[0] != 'n' */
    #ifdef CONFIG_LMB
        struct lmb  lmb;        /* for memory mgmt */
    #endif
    }
  • bootm_startはverifyとlmb
  • を実現する
  • bootm_find_osはosとepを実現する.つまり、Legacy-uImageでもFIT-uImageでも、最終的に解析されるのはこの2人のメンバーです.『uboot起動kernel編(三)——uImageを解析するkernelミラー』で具体的に説明します.
  • bootm_find_other実装rd_start, rd_end,ft_addrとinitrd_end. つまり、Legacy-uImageでもFIT-uImageでも、最終的に解析されるのはこの2人のメンバーです.『uboot起動kernel編(四)——uImageを解析するfdt』と『uboot起動kernel編(五)——uImageを解析するramdisk』で具体的に説明します.

  • 5、bootm_load_os
    簡単に説明するとbootm_load_osでは、kernelミラーが対応する位置にloadされ、kernelミラーがmkimageで圧縮されている場合は、解凍してからloadが行われます.(ここで注意したいのは、ここの圧縮とImageをzImageに圧縮するのは同じではなく、ubootがImageまたはzImageをベースにした圧縮です!!)
    static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end,
                 int boot_progress)
    {
        image_info_t os = images->os;
        ulong load = os.load; // kernel      
        ulong blob_start = os.start;
        ulong blob_end = os.end;
        ulong image_start = os.image_start; // kernel       
        ulong image_len = os.image_len; // kernel   
        bool no_overlap;
        void *load_buf, *image_buf;
        int err;
    
        load_buf = map_sysmem(load, 0);
        image_buf = map_sysmem(os.image_start, image_len);
    
    //   bootm_decomp_image, image_buf        , load load_buf 
        err = bootm_decomp_image(os.comp, load, os.image_start, os.type,
                     load_buf, image_buf, image_len,
                     CONFIG_SYS_BOOTM_LEN, load_end);

    その結果、上記の手順の後、kernelミラーは対応する位置にloadされます.
    6、bootm_os_get_boot_func
    bootm_os_get_boot_funcは、対応するオペレーティングシステムの起動関数を取得するためにboot_に格納されるfnで.次のようになります.
    int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
                int states, bootm_headers_t *images, int boot_progress)
    {
    ...
        boot_fn = bootm_os_get_boot_func(images->os.os);
    ...
    }
    
    boot_os_fn *bootm_os_get_boot_func(int os)
    {
        return boot_os[os];
    //                   
    }
    
    static boot_os_fn *boot_os[] = {
    ...
    #ifdef CONFIG_BOOTM_LINUX
        [IH_OS_LINUX] = do_bootm_linux,
    #endif
    }

    最終的にlinuxを起動するコア関数はdo_であることがわかります.bootm_linux. 他のいくつかの関数も最終的にboot_に呼び出されますfn、linuxに対応するdo_bootm_linuxなので、ここでは説明しません.次にdo_について説明しますbootm_linuxのプロセス
    五、do_bootm_linux
    arch/arm/lib/bootm.c
    int do_bootm_linux(int flag, int argc, char * const argv[],
               bootm_headers_t *images)
    {
        /* No need for those on ARM */
        if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
            return -1;
    
            //  flag BOOTM_STATE_OS_PREP,           boot_prep_linux
        if (flag & BOOTM_STATE_OS_PREP) {
            boot_prep_linux(images);
            return 0;
        }
    
            //  flag BOOTM_STATE_OS_GO ,            
            if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
            boot_jump_linux(images, flag);
            return 0;
        }
    
        boot_prep_linux(images); //      bootm_headers_t images      boot_prep_linux
        boot_jump_linux(images, flag);//      bootm_headers_t images      boot_jump_linux
        return 0;
    }

    boot_prep_linuxは、linuxにジャンプする前の準備動作を実現するために使用されます.そしてboot_jump_linuxはlinuxにジャンプするために使用されます.グローバル変数bootm_headers_t imagesはパラメータであり、前のステップで得られたkernelミラー、ramdisk、fdtの情報を直接取得することができる.
  • boot_prep_linuxはまずLMBの概念を説明します.LMBとはlogical memory blocksのことで、主にメモリの保持領域を表すために用いられ、主にfdtの領域、ramdiskの領域などがある.boot_prep_linuxの主な目的はLMBを修正し、LMBをfdtに記入することです.実現は以下の通りである:
  • static void boot_prep_linux(bootm_headers_t *images)
    {
        char *commandline = getenv("bootargs");
    
        if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
    #ifdef CONFIG_OF_LIBFDT
            debug("using: FDT
    "
    ); if (image_setup_linux(images)) { printf("FDT creation failed! hanging..."); hang(); } #endif }

    ここではimageを深く勉強していません.setup_linuxは、後続に必要があればさらに深く進みます.
  • boot_jump_linuxはarmを例にとる:arch/arm/lib/bootm.c
  • static void boot_jump_linux(bootm_headers_t *images, int flag)
    {
        unsigned long machid = gd->bd->bi_arch_number; //  bd   machine-id,machine-id uboot             
        char *s;
        void (*kernel_entry)(int zero, int arch, uint params); // kernel    ,   kernel     ,  kernel _start  。
        unsigned long r2;
        int fake = (flag & BOOTM_STATE_OS_FAKE_GO); //    ,        kernel 
    
        kernel_entry = (void (*)(int, int, uint))images->ep; 
             //  kernel_entry   images  ep(kernel     ),      kernel_entry      kernel  
            //             
    
        debug("## Transferring control to Linux (at address %08lx)" \
            "...
    "
    , (ulong) kernel_entry); bootstage_mark(BOOTSTAGE_ID_RUN_OS); announce_and_cleanup(fake); // images->ft_addr(fdt ) r2 if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) r2 = (unsigned long)images->ft_addr; else r2 = gd->bd->bi_boot_params; if (!fake) { kernel_entry(0, machid, r2); // kernel_entry, images->ep , kernel , kernel _start 。 // 0 r0 , machid r1 , images->ft_addr(fdt ) r2 // kernel } }

    ここまで、kernel_を通ってentryはその後kernel環境に移行しました.興味があればkernelの起動プロセスを見てみましょう.
    ======================================================================================================================================================================第1段階の——proc infoの取得[kernel起動フロー](第4章)第1段階の——dtbの検証[kernel起動フロー](第5章)第1段階の——一時カーネルページテーブルの作成[kernel起動フロー](第6章)第1段階の——MMU[kernel起動フロー](第7章)第1段階の——start_にジャンプkernel