Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1858064
  • 博文数量: 473
  • 博客积分: 13997
  • 博客等级: 上将
  • 技术积分: 5953
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-22 11:52
文章分类

全部博文(473)

文章存档

2014年(8)

2013年(38)

2012年(95)

2011年(181)

2010年(151)

分类: LINUX

2012-02-11 12:14:35

下面我们就以引导linux内核的命令bootm为例,说一下u-bootlinux过渡及参数传递的整个过程。

common/Cmd_bootm.c:

U_BOOT_CMD(

    bootm,  CFG_MAXARGS,    1,  do_bootm,

    "bootm   - boot application image from memory/n",

    "[addr [arg ...]]/n    - boot application image stored in memory/n"

    "/tpassing arguments 'arg ...'; when booting a Linux kernel,/n"

    "/t'arg' can be the address of an initrd image/n"

);

从注释中可以看到这个命令的作用是从memory中引导application,我们这里就是引导linux内核,前提就是linux kernel已经在memory中了。这条命令的处理函数就是do_bootm()

common/Cmd_bootm.c:

 int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

    ………

    /*看到了吧,u-boot是如何支持多个操作系统引导的*/

    switch (hdr->ih_os) {

    default:           /* handled by (original) Linux case */

    case IH_OS_LINUX:

#ifdef CONFIG_SILENT_CONSOLE

        fixup_silent_linux();

#endif

        do_bootm_linux  (cmdtp, flag, argc, argv,

                 addr, len_ptr, verify);  /*linux引导的函数*/

        break;

    case IH_OS_NETBSD:

        do_bootm_netbsd (cmdtp, flag, argc, argv,

                 addr, len_ptr, verify);

        break;

 

#ifdef CONFIG_LYNXKDI

    case IH_OS_LYNXOS:

        do_bootm_lynxkdi (cmdtp, flag, argc, argv,

                 addr, len_ptr, verify);

        break;

#endif

 

    case IH_OS_RTEMS:

        do_bootm_rtems (cmdtp, flag, argc, argv,

                 addr, len_ptr, verify);

        break;

 

#if (CONFIG_COMMANDS & CFG_CMD_ELF)

    case IH_OS_VXWORKS:

        do_bootm_vxworks (cmdtp, flag, argc, argv,

                  addr, len_ptr, verify);

        break;

    case IH_OS_QNX:

        do_bootm_qnxelf (cmdtp, flag, argc, argv,

                  addr, len_ptr, verify);

        break;

#endif /* CFG_CMD_ELF */

#ifdef CONFIG_ARTOS

    case IH_OS_ARTOS:

        do_bootm_artos  (cmdtp, flag, argc, argv,

                 addr, len_ptr, verify);

        break;

#endif

    }

  ………

}

do_bootm()函数很大,上面没有全部列出来, 但是大部分代码都很简单,就是做一些检测,看是否是正确的image,做的检测有:magic number, crc校验,体系结构是否正确等等,如果是压缩的还会先解压缩(这里的压缩和linux编译出来的压缩没关系,应该是application是否被gzip等压缩过的概念),最后如果是linux kernel则会调用do_bootm_linux()

我们一段段的分析do_bootm_linux()

lib_arm/Armlinux.c:

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; /*linux image的头*/

    bd_t *bd = gd->bd; /*用来存放传递给linux kernel参数的地址*/

 

#ifdef CONFIG_CMDLINE_TAG

    char *commandline = getenv ("bootargs");

#endif

 

    theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);/*指向linux kernel的入口*/

    ………

}

继续

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

                                 ulong addr, ulong *len_ptr, int verify)

{

 ………

    /*

     * Check if there is an initrd image

     */

    if (argc >= 3) { /*存在initrd image*/

        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 */

#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)); /*获取initrd的头*/

 

        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, (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, (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)

        /*

         *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;

    }

………

}

上面的代码主要是检查是否有initrd image,如有则记住它在内存的地址。

继续

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

                                 ulong addr, ulong *len_ptr, int verify)

{

   ………

    if (data) {  /*保存initrd的地址*/

        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);

 

  /*下面这些实际上就是设置好参数*/

#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);

#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)

        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前,为linux做好准备*/

   

    /*正式跳转到linux kernel*/

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

}

上面这部分就是为进入linux准备好环境,然后就正式把控制权交给linux kernel

我们以setup_serial_tag为例看下是如何设置参数的

lib_arm/Armlinux.c:

void setup_serial_tag (struct tag **tmp)

{

    struct tag *params = *tmp;

    struct tag_serialnr serialnr;

    void get_board_serial(struct tag_serialnr *serialnr);

 

    get_board_serial(&serialnr);

    params->hdr.tag = ATAG_SERIAL;

    params->hdr.size = tag_size (tag_serialnr);

    params->u.serialnr.low = serialnr.low;

    params->u.serialnr.high= serialnr.high;

    params = tag_next (params);

    *tmp = params;

}

上面这个函数传进来的参数是¶ms,paramssetup_start_tag()里已经被初始化为

    params = (struct tag *) bd->bi_boot_params;

因此该函数对params里变量的赋值实际上就是在对bd->bi_boot_params下赋值,因此bi_boot_params地址处存放了所有相关的参数。最后这个地址被传给了linux kernel

接下来看cleanup_before_linux ()

cpu/arm920t/cpu.c:

int cleanup_before_linux (void)

{

    /*

     * this function is called just before we call linux

     * it prepares the processor for linux

     *

     * we turn off caches etc ...

     */

 

    unsigned long i;

 

    disable_interrupts ();  /*关中断*/

 

    /* turn off I/D-cache */

    /*关掉数据和指令的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));

 

    /* flush I/D-cache */   /*复位数据和指令的cache*/

    i = 0;

    asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i));

    return (0);

}

呵呵,有人要问了为什么要做这个操作,其实这是linux kernel规定的,请看:

arch/arm/kernel/head.S:

/*

 * Kernel startup entry point.

 * ---------------------------

 *

 * This is normally called from the decompressor code.  The requirements

 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,

 * r1 = machine nr.

 *

 * This code is mostly position independent, so if you link the kernel at

 * 0xc0008000, you call this at __pa(0xc0008000).

 *

 * See linux/arch/arm/tools/mach-types for the complete list of machine

 * numbers for r1.

 *

 * We're trying to keep crap to a minimum; DO NOT add any machine specific

 * crap here - that's what the boot loader (or in extreme, well justified

 * circumstances, zImage) is for.

 */

 

分析到这里整个u-boot的流程大概就分析完了, 至于

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

它是如何按什么顺序把参数放入哪个寄存器的,就请自行分析吧,可以参考APCS规范。

 

有时间在分析linux启动顺序, 这样应该可以有个衔接。
阅读(2140) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~