内核启动分析(二)——do_bootm_linux分析

do_bootm_linux分析


do_bootm函数位于common/cmd_bootm.c文件中。

do_bootm函数调用do_bootm_linux函数启动linux内核,当定义了CONFIG_PPC时将使用common/cmd_bootm.c文件中的do_bootm_linux函数;当系统中没有定义该宏时,系统将使用lib_arm/armlinux.c文件中定义的do_bootm_linux函数。注意:这两个函数有很大的区别!
lib_arm/armlinux.c中do_bootm_linux函数源代码:

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char*argv[],
 
      ulong addr, ulong *len_ptr, int verify)
{
DECLARE_GLOBAL_DATA_PTR;

ulong len = 0, checksum;
ulong initrd_start, initrd_end;
ulong data;
void (*theKernel)(int zero, int arch, uint params);
image_header_t *hdr = &header;
bd_t *bd = gd->bd;

#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv (”bootargs”);//获取bootargs 这个环境变量相当重要 制定了root 根文件系统类型 内存大   小 等等信息
#endif

theKernel = (void (*)(int, int,uint))ntohl(hdr->ih_ep); // 设置kernel加载地址

    /*
     * Check if there is an initrd image
     initrd 的英文含义是 boot loader initialized RAM disk,就是由 boot loader 初始化的内存盘。
     在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,
     内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。
     在 boot loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,
     第一阶段先执行 initrd 文件系统中的"某个文件",完成加载驱动模块等任务,
     第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。
     */

    if (argc >= 3) {
        SHOW_BOOT_PROGRESS (9);

        addr = simple_strtoul (argv[2], NULL, 16);

        printf ("## Loading Ramdisk Image at %08lx ...\n", addr);

        /* Copy header so we can blank CRC field for re-calculation 应该是atmel公司定义的一种flash
        接着下面一堆代码都是校验 拷贝内存 什么的 移植也不用看  */

#ifdef CONFIG_HAS_DATAFLASH
        if (addr_dataflash (addr)) {
            read_dataflash (addr, sizeof (image_header_t),
                    (char *) &header);
        } else
#endif
            memcpy (&header, (char *) addr,
                sizeof (image_header_t));

        if (ntohl (hdr->ih_magic) != IH_MAGIC) {
            printf ("Bad Magic Number\n");
            SHOW_BOOT_PROGRESS (-10);
            do_reset (cmdtp, flag, argc, argv);
        }

        data = (ulong) & header;
        len = sizeof (image_header_t);

        checksum = ntohl (hdr->ih_hcrc);
        hdr->ih_hcrc = 0;

        if (crc32 (0, (unsigned char *) data, len) != checksum) {
            printf ("Bad Header Checksum\n");
            SHOW_BOOT_PROGRESS (-11);
            do_reset (cmdtp, flag, argc, argv);
        }

        SHOW_BOOT_PROGRESS (10);

        print_image_hdr (hdr);

        data = addr + sizeof (image_header_t);
        len = ntohl (hdr->ih_size);

#ifdef CONFIG_HAS_DATAFLASH
        if (addr_dataflash (addr)) {
            read_dataflash (data, len, (char *) CFG_LOAD_ADDR);
            data = CFG_LOAD_ADDR;
        }
#endif

        if (verify) {
            ulong csum = 0;

            printf ("   Verifying Checksum ... ");
            csum = crc32 (0, (unsigned char *) data, len);
            if (csum != ntohl (hdr->ih_dcrc)) {
                printf ("Bad Data CRC\n");
                SHOW_BOOT_PROGRESS (-12);
                do_reset (cmdtp, flag, argc, argv);
            }
            printf ("OK\n");
        }

        SHOW_BOOT_PROGRESS (11);

        if ((hdr->ih_os != IH_OS_LINUX) ||
            (hdr->ih_arch != IH_CPU_ARM) ||
            (hdr->ih_type != IH_TYPE_RAMDISK)) {
            printf ("No Linux ARM Ramdisk Image\n");
            SHOW_BOOT_PROGRESS (-13);
            do_reset (cmdtp, flag, argc, argv);
        }

#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
        /*
         *we need to copy the ramdisk to SRAM to let Linux boot
         */
        memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
        data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 || CONFIG_EVB4510 */

        /*
         * Now check if we have a multifile image
         */
    } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
        ulong tail = ntohl (len_ptr[0]) % 4;
        int i;

        SHOW_BOOT_PROGRESS (13);

        /* skip kernel length and terminator */
        data = (ulong) (&len_ptr[2]);
        /* skip any additional image length fields */
        for (i = 1; len_ptr[i]; ++i)
            data += 4;
        /* add kernel length, and align */
        data += ntohl (len_ptr[0]);
        if (tail) {
            data += 4 - tail;
        }

        len = ntohl (len_ptr[1]);

    } else {
        /*
         * no initrd image
         */
        SHOW_BOOT_PROGRESS (14);

        len = data = 0;
    }

#ifdef    DEBUG
    if (!data) {
        printf ("No initrd\n");
    }
#endif

    if (data) {
        initrd_start = data;
        initrd_end = initrd_start + len;
    } else {
        initrd_start = 0;
        initrd_end = 0;
    }

    SHOW_BOOT_PROGRESS (15);

    debug ("## Transferring control to Linux (at address %08lx) ...\n",
           (ulong) theKernel);

// 在psbec270.h文件中定义了如下宏
// #define CONFIG_CMDLINE_TAG
// #define CONFIG_SETUP_MEMORY_TAGS
// #define CONFIG_INITRD_TAG

// 根据上面不同的宏加载不同的TAG
// 注意的是必须定义CONFIG_CMDLINE_TAG和CONFIG_SETUP_MEMORY_TAGS
// 除非内核已经根据系统初始化了这些值, 否则必须定义, 不定义将导致无法启动.


//下面根据定义 对tag结构体进行赋值:设置启动参数,主要的有四个函数。
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
    defined (CONFIG_CMDLINE_TAG) || \
    defined (CONFIG_INITRD_TAG) || \
    defined (CONFIG_SERIAL_TAG) || \
    defined (CONFIG_REVISION_TAG) || \
    defined (CONFIG_LCD) || \
    defined (CONFIG_VFD)


    setup_start_tag (bd);//先设置start tag,然后把params这个指针指向下一个tag的首地址,
#ifdef CONFIG_SERIAL_TAG
    setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
    setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
    setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
    setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
    if (initrd_start && initrd_end)//存在ramdisk时 设置initrdtag
        setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
    setup_videolfb_tag ((gd_t *) gd);
#endif
    setup_end_tag (bd);
#endif
// 接下来开始调用执行内核启动
    /* we assume that the kernel is in place */
    printf ("\nStarting kernel ...\n\n");

#ifdef CONFIG_USB_DEVICE
    {
        extern void udc_disconnect (void);
        udc_disconnect ();
    }
#endif

    cleanup_before_linux ();//为linux引导做一些准备工作,启动之前先做一些清理工作,见下面说明

    theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
    //这句话来引导内核,R0:必须为0,bd->bi_arch_number指向机器类型ID,这个ID在board_init函数中设置。bd->bi_boot_params
    //指向的是tag的标记的起始地址。 这三个变量分别存在r0,r1,r2中给了linux。


}


// 清理工作,调用内核之前必须满足如下条件才可以:
// CPU模式:
// 
  必须禁止中断(IRQs和FIQs);
// 
  CPU 必须 SVC 模式;
// Cache和MMU的设置:
// 
  MMU 必须关闭;
// 
  指令 Cache 可以打开也可以关闭;
// 
  数据 Cache 必须关闭;
int cleanup_before_linux (void)
{
unsigned long i;

disable_interrupts (); 
  //禁止中断
// 关闭指令和数据cache
asm (”mrc p15, 0, %0, c1, c0, 0″:”=r” (i));
i &= ~(C1_DC | C1_IC);
asm (”mcr p15, 0, %0, c1, c0, 0″: :”r” (i));
i = 0;
asm (”mcr p15, 0, %0, c7, c7, 0″: :”r” (i));
return (0);
}

你可能感兴趣的:(Linux移植开发)