现在的位置: 首页 > 综合 > 正文

u-boot源码分析 — 启动第二阶段007

2013年08月18日 ⁄ 综合 ⁄ 共 7772字 ⁄ 字号 评论关闭

下面我们就以引导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 (&params);

#endif

#ifdef
CONFIG_REVISION_TAG

    setup_revision_tag (&params);

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

}

上面这个函数传进来的参数是&params,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);

抱歉!评论已关闭.