小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

u-boot啟動內(nèi)核分析

 njy131 2012-06-06
在上一篇中分析到u-Boot啟動Linux內(nèi)核的函數(shù)do_bootm_linux,這一篇則著重分析,U-boot是如果一步一步啟動內(nèi)核的。
    我們可以看到在,start_armboot()函數(shù)的最后,在一個無限循環(huán)中調(diào)用了函數(shù)main_loop(),該函數(shù)在common/main.c文件中被定義,我們可以看到下面的一段代碼:

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");   //得到環(huán)境變量中bootdelay
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

  如果定義了CONFIG_BOOTDELAY,則在沒有CONFIG_BOOTDELAY秒中,串口上沒有輸入,則會進行自動的引導Linux內(nèi)核。也就是執(zhí)行bootcmd命令。

#ifdef CONFIG_BOOTCOUNT_LIMIT //啟動次數(shù)的限制功能,如果到達一定次數(shù),將不能啟動u-boot.
    if (bootlimit && (bootcount > bootlimit)) {//檢查是否超出啟動次數(shù)限制
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
         (unsigned)bootlimit);
        s = getenv ("altbootcmd");//啟動延時
    }
    else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv ("bootcmd");// 獲得啟動參數(shù)

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
// 這里如果bootdelay大于0,并且中間沒有被中斷的話,執(zhí)行命令行參數(shù)
    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
  int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CONFIG_SYS_HUSH_PARSER
  run_command (s, 0); //運行啟動的命令行,例如 可以使用tftp命令
# else
  parse_string_outer(s, FLAG_PARSE_SEMICOLON |
        FLAG_EXIT_FROM_LOOP);
# endif

   到這里我們就可以看到是怎么調(diào)用設(shè)置的命令行參數(shù)的,在這里還要使用到bootm命令,先來看看bootm命令的實現(xiàn),在common/cmd_bootm.c

#define CONFIG_BOOTM_LINUX 1
#define CONFIG_BOOTM_NETBSD 1
#define CONFIG_BOOTM_RTEMS 1

#ifdef CONFIG_BOOTM_LINUX
extern boot_os_fn do_bootm_linux;
#endif
#ifdef CONFIG_BOOTM_NETBSD
static boot_os_fn do_bootm_netbsd;
#endif

   可以看出如果定義了CONFIG_BOOTM_LINUX這個宏的話,就會使用外部文件定義的do_bootm_linux函數(shù),在arm體系結(jié)構(gòu)中,就是在lib_arm/bootm.c文件中,可以從lib_arm/bootm.c文件中的59行看到do_bootm_linux()的定義。其中第64行聲明了這樣一個函數(shù)指針theKernel

void (*theKernel)(int zero, int arch, uint params);

   看看它的名字和參數(shù)的命名我們也可以猜到這個其實就是內(nèi)核的入口函數(shù)的指針了。幾個參數(shù)的命名也說明了下文提到的ARM Linux內(nèi)核啟動要求的第一條,因為根據(jù)ACPS(ARM/Thumb Procedure Call Standard)的規(guī)定,這三個參數(shù)就是依次使用r0,r1和r2來傳遞的。接下來第73行就是給這個函數(shù)指針賦值: 

theKernel = (void (*)(int, int, uint))images->ep;

    可以看到theKernel被賦值為images->ep,這個image指使用tools/mkimage工具程序制作uImage時加在linux.bin.gz前面的一個頭部,而ep結(jié)構(gòu)體成員保存的就是使用mkimage時指定的-e參數(shù)的值,即內(nèi)核的入口點(Entry Point)。知道了images->ep的意義之后,給theKernel賦這個值也就是理所當然的了。
     image是bootm_headers結(jié)構(gòu)體的指針,可以在inlcude/image.h文件中看到這個結(jié)構(gòu)體的定義如下:

typedef struct bootm_headers {
     ............................

    int        fit_noffset_fdt;/* FDT blob subimage node offset */
#endif

#ifndef USE_HOSTCC
    image_info_t    os;        /* os image info */
    ulong        ep;        /* entry point of OS */

    ulong        rd_start, rd_end;/* ramdisk start/end */
...............

}

最后是對內(nèi)核入口函數(shù)的調(diào)用,發(fā)生在第128行:

theKernel (0, machid, bd->bi_boot_params);

   調(diào)用的時候?qū)?shù)進行賦值,r0=0,r1=bd->bi_arch_number,r2=bd-> bi_boot_params,一個都不少。至此U-Boot的使命完成,開始進入ARM Linux的世界。
   要知道哪個地址是啟動內(nèi)核,哪個地址啟動文件系統(tǒng),要分析common/cmd_bootm.c中的函數(shù) do_bootm,因為引導kernel就是bootm這條命令的工作,do_bootm是命令bootm的執(zhí)行函數(shù)現(xiàn)在我們來分析一下common/cmd_bootm.c中的函數(shù)do_bootm,這是bootm命令的處理函數(shù).do_bootm()函數(shù)中的很多功能都是分成了函數(shù)的形式,而在以前的版本中沒有這么有結(jié)構(gòu)層次,這里我們也只是分析對引導Linux內(nèi)核有作用的部分,因為這是一個在common文件夾下的文件,也就意味著,在引導別的操作系統(tǒng)時也會用到這個函數(shù),而不單單是Linux操作系統(tǒng).

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    ulong        iflag;
    ulong        load_end = 0;
    int        ret;
    boot_os_fn    *boot_fn;
#ifndef CONFIG_RELOC_FIXUP_WORKS
    static int relocated = 0;
    /* relocate boot function table */
    if (!relocated) {
        int i;
        for (i = 0; i < ARRAY_SIZE(boot_os); i++)
            if (boot_os[i] != NULL)
                boot_os[i] += gd->reloc_off;
        relocated = 1;
    }
#endif
    /* determine if we have a sub command */
    if (argc > 1) {
        char *endp;

        simple_strtoul(argv[1], &endp, 16);
    
        if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
            return do_bootm_subcommand(cmdtp, flag, argc, argv);
    }

    if (bootm_start(cmdtp, flag, argc, argv)) //提取mkimage生成的文件頭部,放到bootm_headers_t結(jié)構(gòu)體中
        return 1;

    iflag = disable_interrupts();

#if defined(CONFIG_CMD_USB)
       usb_stop();
#endif

#ifdef CONFIG_AMIGAONEG3SE
    /*
     * We've possible left the caches enabled during
     * bios emulation, so turn them off again
     */

    icache_disable();
    dcache_disable();
#endif

    ret = bootm_load_os(images.os, &load_end, 1); //加載操作系統(tǒng)的關(guān)鍵部分 確定使用的地址
    if (ret < 0) { //出錯處理
        if (ret == BOOTM_ERR_RESET)
            do_reset (cmdtp, flag, argc, argv);
        if (ret == BOOTM_ERR_OVERLAP) {
            if (images.legacy_hdr_valid) {
                if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
                    puts ("WARNING: legacy format multi component "
                        "image overwritten\n");
            } else {
                puts ("ERROR: new format image overwritten - "
                    "must RESET the board to recover\n");
                show_boot_progress (-113);
                do_reset (cmdtp, flag, argc, argv);
            }
        }
        if (ret == BOOTM_ERR_UNIMPLEMENTED) {
            if (iflag)
                enable_interrupts();
            show_boot_progress (-7);
            return 1;
        }
    }
    lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));

    if (images.os.type == IH_TYPE_STANDALONE) {//獨立的應(yīng)用程序
        if (iflag)
            enable_interrupts();
        /* This may return when 'autostart' is 'no' */
        bootm_start_standalone(iflag, argc, argv);
        return 0;
    }
    show_boot_progress (8);

#ifdef CONFIG_SILENT_CONSOLE //這里處理Linux操作系統(tǒng)
    if (images.os.os == IH_OS_LINUX)
        fixup_silent_linux(); //該函數(shù)中處理bootarg參數(shù)
#endif
    boot_fn = boot_os[images.os.os];
    if (boot_fn == NULL) {
        if (iflag)
            enable_interrupts();
        printf ("ERROR: booting os '%s' (%d) is not supported\n",
            genimg_get_os_name(images.os.os), images.os.os);
        show_boot_progress (-8);
        return 1;
    }

    arch_preboot_os();
/*下面的函數(shù),繼續(xù)引導內(nèi)核的鏡像,復制image header 到全局變量header;
檢查header的魔數(shù),檢查數(shù),header和image中的這兩個。確定image的體系結(jié)構(gòu)和類型(KERNEL  or MULTI),關(guān)閉中斷,加載image到header中的加載地址*/

    boot_fn(0, argc, argv, &images); //調(diào)用do_bootm_linux()函數(shù)
    show_boot_progress (-9);
#ifdef DEBUG
    puts ("\n## Control returned to monitor - resetting...\n");
#endif
    do_reset (cmdtp, flag, argc, argv);

    return 1;
}

下面我們看一下bootm_load_os()函數(shù)

static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)
{
    uint8_t comp = os.comp;
    ulong load = os.load;
    ulong blob_start = os.start;
    ulong blob_end = os.end;
    ulong image_start = os.image_start;
    ulong image_len = os.image_len;
    uint unc_len = CONFIG_SYS_BOOTM_LEN;

    const char *type_name = genimg_get_type_name (os.type);

    switch (comp) { //判斷image的壓縮類型
    case IH_COMP_NONE:
        if (load == blob_start) {
            printf (" XIP %s ... ", type_name);
        } else {
            printf (" Loading %s ... ", type_name);
//如果在Image head中加載的地址和bootm命令參數(shù)2指定的地址相同,則不需要復制,直接執(zhí)行
            if (load != image_start) {
                memmove_wd ((void *)load,
                        (void *)image_start, image_len, CHUNKSZ);
            }
        }
        *load_end = load + image_len;
        puts("OK\n");
        break;
    case IH_COMP_GZIP:
        printf (" Uncompressing %s ... ", type_name);
        if (gunzip ((void *)load, unc_len,
                    (uchar *)image_start, &image_len) != 0) {
            puts ("GUNZIP: uncompress, out-of-mem or overwrite error "
                "- must RESET board to recover\n");
            if (boot_progress)
                show_boot_progress (-6);
            return BOOTM_ERR_RESET;
        }
        *load_end = load + image_len;
        break;
#ifdef CONFIG_BZIP2
    case IH_COMP_BZIP2: //判斷是什么類型的壓縮類型
        printf (" Uncompressing %s ... ", type_name);
         int i = BZ2_bzBuffToBuffDecompress ((char*)load,
                    &unc_len, (char *)image_start, image_len,
                    CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
        if (i != BZ_OK) {
            printf ("BUNZIP2: uncompress or overwrite error %d "
                "- must RESET board to recover\n", i);
            if (boot_progress)
                show_boot_progress (-6);
            return BOOTM_ERR_RESET;
        }
        *load_end = load + unc_len;
        break;
#endif /* CONFIG_BZIP2 */
#ifdef CONFIG_LZMA
    case IH_COMP_LZMA:
        printf (" Uncompressing %s ... ", type_name);

....................
   return 0;
}

   如果image header中指示的加載地址和bootm命令中參數(shù)2指定的地址不相同,則表示要從image header中指示的加載地址處把image data copy到bootm命令中參數(shù)2指定的地址處,然后再執(zhí)行。
   bootm命令是用來引導經(jīng)過u-boot的工具mkimage打包后的kernel image的。
mkimage的用法
   uboot源代碼的tools/目錄下有mkimage工具,這個工具可以用來制作不壓縮或者壓縮的多種可啟動映象文件。
   mkimage在制作映象文件的時候,是在原來的可執(zhí)行映象文件的前面加上一個0x40字節(jié)的頭,記錄參數(shù)所指定的信息,這樣uboot才能識別這個映象是針對哪個CPU體系結(jié)構(gòu)的,哪個OS的,哪種類型,加載內(nèi)存中的哪個位置, 入口點在內(nèi)存的那個位置以及映象名是什么?到這里整個U-Boot是如何啟動Linux內(nèi)核的,基本上也就清楚了,特別是如何向Linux內(nèi)核傳送的參數(shù)。
PS:下面是“ARM Linux Kernel Boot Requirements”,這篇文章中介紹的,引導Linux內(nèi)核啟動的必須要滿足的幾個條件:

* CPU register settings //這里也就是我們的theKernel中的作用
          o r0 = 0.
          o r1 = machine type number.
          o r2 = physical address of tagged list in system RAM.
    * CPU mode
          o All forms of interrupts must be disabled (IRQs and FIQs.)
          o The CPU must be in SVC mode. (A special exception exists for Angel.)
    * Caches, MMUs
          o The MMU must be off.
          o Instruction cache may be on or off.
          o Data cache must be off and must not contain any stale data.
    * Devices
          o DMA to/from devices should be quiesced.
    * The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多