u-boot 懂你没有那么难

680阅读 0评论2013-08-28 jonas_mao
分类:LINUX

U-boot第二阶段分析

一、U-boot第二阶段概述

上面有两篇文关于u-boot第一阶段的介绍,这两篇文章是从网上找到的,由于分析的很详细,看完这后觉得对这两篇文章u-boot第一阶段的介绍已经比较完美了,所以分享出来。从这篇文章开始分析u-boot的第二阶段。

如果你只把u-boot理解成引导kernel的一段代码的话,u-boot完全没有必要设计成现在这样的一种软件框架,直接写几个文件就能完成kernel的引导和启动。U-boot的功能很大一部还有起到调试的作用,也就是u-boot命令行的部分。所以它才有了现在这种相对比较复杂的框架。U-boot的第二阶段可以认为是初始化u-boot的软件框架,并实现引导kernel启动和命令行调试环境的过程

U-boot第二阶段总结来说主要可以概括为下面几点

1、硬件的初始化

2、运行环境的初始化

3、载入内核并启动内核

4、运行u-boot调试的命令机制

其中12阶段可以看成是u-boot软件框架的初始化过程,只不过包括了硬件设备的初始化和软件相关结构体的初始化。而3就是引导kernel启动过程,而4是调试的命令行环境运行过程。

本文首先沿着第一阶段调用_start_armboot函数开始第二阶段的分析。首先完成一个概述,之后会对一些关键的问题做专题分析。

二、硬件和软件框架的初始化

首先初始化一个全局性的数据结构gd_t

/* Pointer is writable since we allocated a register for it */ 
gd = (gd_t *)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 
/* compiler optimization barrier needed for GCC >= 3.4 */ 
__asm__ __volatile__("": : :"memory"); 
memset ((void *)gd, 0sizeof (gd_t)); 
gd->bd = (bd_t *)((char *)gd - sizeof(bd_t)); 
memset (gd->bd, 0sizeof (bd_t)); 
monitor_flash_len = _bss_start - _armboot_start; 

我们看到这里u-boot动态的分配了gd_tbd_t这两个数据结构,只是比较疑惑的是这两个数据结构完全可以定义成全局的数据,何必这样动态的分配呢?由于这个时候完全没有malloc这样的环境,u-boot只好跑马圈地,很野蛮的在_armboot_start前面画出一块位置作为上面结构体区域。这里我们就两个疑问?1u-boot到底被第一阶段的代码拷贝到什么地方去执行了?第二个疑问就是u-boot在内存区域中相关代码和数据区域是怎么分布的?

Question1

Answer

查看一下下面的配置文件就可以知道,mini2440开发板sdram的地址范围是3000000034000000,一共是64MU-boot首先把自己载入了最后的512K的地址空间中,坐在墙角的位置。

u-boot-1.1.6\board\open24x0\config.mk

#

# SMDK2410 has 1 bank of 64 MB DRAM

#

# 3000'0000 to 3400'0000

#

# Linux-Kernel is expected to be at 3000'8000, entry 3000'8000

# optionally with a ramdisk at 3080'0000

#

# we load ourself to 33F8'0000

#

# download area is 3300'0000

#

TEXT_BASE = 0x33F80000

Question2

Answer

U-boot被完全载入到内存后,各部分在内存中的位置如下图所示:


需要注意的是图中TEXT_BASE的地址在整个SDRAM空间的最后512K位置了。

初始化函数调用

接着u-boot对单板的硬件和软件框架做相应的初始化

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) 

    if ((*init_fnc_ptr)() != 0
    { 
        hang (); 
    } 

U-boot软件框架定义了这样的一个初始化表,不同的单板具体实现下面的函数进行相应的初始化动作

init_fnc_t *init_sequence[] = 

    cpu_init,        /* basic cpu dependent setup */ 
    board_init,        /* basic board dependent setup */ 
    interrupt_init,        /* set up exceptions */ 
    env_init,        /* initialize environment */ 
    init_baudrate,        /* initialze baudrate settings */ 
    serial_init,        /* serial communications setup */ 
    console_init_f,        /* stage 1 init of console */ 
    display_banner,        /* say that we are here */ 
#if defined(CONFIG_DISPLAY_CPUINFO) 
    print_cpuinfo,        /* display cpu info (and speed) */ 
#endif 
#if defined(CONFIG_DISPLAY_BOARDINFO) 
    checkboard,        /* display board info */ 
#endif 
    dram_init,        /* configure available RAM banks */ 
    display_dram_config, 
    NULL, 
}; 

后续的文章会将上面的初始化函数做逐一的分析,但是本篇本意是想对第二阶段的处理过程做一个提纲挈领的描述,所以暂时举一个例子分析如下:

int cpu_init (void

    /* 
     * setup up stacks if necessary 
     */ 
#ifdef CONFIG_USE_IRQ 
    IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4
    FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; 
    FREE_RAM_END = FIQ_STACK_START - CONFIG_STACKSIZE_FIQ - CONFIG_STACKSIZE; 
    FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; 
#else 
    FREE_RAM_END = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4 - CONFIG_STACKSIZE; 
    FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; 
#endif 
    return 0

Cpu_init函数基本上就是初始化了一下栈区空间和软件中断的栈空间

在完成了单板相应硬件初始化之后,u-boot在后继的代码里可以自由的使用u-boot框架提供的一些服务接口,比如从串口获取输入,从nandflash读写数据,网络接口通信等等。然后u-boot进入了一个大的循环中

for (;;) 

    main_loop (); 

main_loop函数中,有两条比较关键的路径需要分析,一条路径就是引导内核启动,另外一条路径就是用户通过串口输入中断了u-boot启动内核过程而进入了u-boot的命令行调试运行环境中。

三、u-boot引导内核启动的过程。

首先我们来分析一下u-boot引导内核启动的过程

1、首先获取等待用户串口按键中断引导kernel的时间,一般为3

s = getenv ("bootdelay"); 
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; 

2、获取启动内核的命令,这个靠编译u-boot之前开发者的配置

s = getenv ("bootcmd"); 

而这条启动命令在mini2440的配置就是如下,其实是两条命令组成的

#define CONFIG_BOOTCOMMAND "nand read.jffs2 0x32000000 kernel; bootm 0x32000000"

3、调用abortboot函数检查在等待的3s中内是不是从串口有用户输入了空格,如果有就说明用户想进入命令行调试模式,如果没有就说明可以开始运行启动内核的命令。

if (bootdelay >= 0 && s && !abortboot (bootdelay)) 

    { 
        printf("Booting Linux ...\n"); 
        run_command (s, 0); 
    } 

4、abortboot中比较关键的代码就是在一个循环中不断检测是不是有用户的串口输入,使用了一个udelay函数,10000us等于10ms,循环100次就是1s

while ((bootdelay > 0) && (!abort)) 

    int i; 
    --bootdelay; 
    /* delay 100 * 10ms */ 
    for (i = 0; !abort && i < 100; ++i) 
    { 
        if (tstc())      /* we got a key press    */ 
        { 
# ifdef CONFIG_MENUKEY 
            abort  = 1;    /* don't auto boot    */ 
            bootdelay = 0;    /* no more delay    */ 
            menukey = getc(); 
            break
else 
            /* consume input    */ 
            if (getc() == ' '
            { 
                abort  = 1/* don't auto boot    */ 
                bootdelay = 0;    /* no more delay    */ 
                break
            } 
# endif 
        } 
        udelay (10000); 
    } 
    printf ("\b\b\b%2d ", bootdelay); 

5、用户不中断kernel引导过程的话,最后会调用到run_command函数,这个函数不需要怎么分析,它的作用就就是解析输入的命令,然后根据命令的名字查找相应的命令执行函数进行调用,上面我们看到,启动的时候需要调用两个命令,一个是nand命令一个bootm命令。

6、Nand命令完成将kernel载入到内存中指定的位置,而bootm则是最后调用固定位置的一个kernel启动函数进入kernel的代码中开始执行。

7、执行nand命令,u-boot在其软件框架中定义了一个命令nand如下所示,执行这个命令最后会调用到函数do_nand

U_BOOT_CMD(nand, 51, do_nand, 
           "nand    - NAND sub-system\n"
           "info                  - show available NAND devices\n" 
           "nand device [dev]     - show or set current device\n" 
           "nand read[.jffs2]     - addr off|partition size\n" 
           "nand write[.jffs2]    - addr off|partiton size - read/write `size' bytes starting\n" 
           "    at offset `off' to/from memory address `addr'\n" 
           "nand read.yaffs addr off size - read the `size' byte yaffs image starting\n" 
           "    at offset `off' to memory address `addr'\n" 
           "nand write.yaffs addr off size - write the `size' byte yaffs image starting\n" 
           "    at offset `off' from memory address `addr'\n" 
           "nand read.raw addr off size - read the `size' bytes starting\n" 
           "    at offset `off' to memory address `addr', without oob and ecc\n" 
           "nand write.raw addr off size - write the `size' bytes starting\n" 
           "    at offset `off' from memory address `addr', without oob and ecc\n" 
           "nand erase [clean] [off size] - erase `size' bytes from\n" 
           "    offset `off' (entire device if not specified)\n" 
           "nand bad - show bad blocks\n" 
           "nand dump[.oob] off - dump page\n" 
           "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" 
           "nand markbad off - mark bad block at offset (UNSAFE)\n" 
           "nand biterr off - make a bit error at offset (UNSAFE)\n" 
           "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" 
           "nand unlock [offset] [size] - unlock section\n"); 

这里也没有必要去深入探究do_nand的作用,它完成的工作就是将kernelnandflash中准确的载入到内存指定的位置。

nand命令将kernel读入到内存后,接着执行bootm命令,最后执行linux内核的code

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" 
#ifdef CONFIG_OF_FLAT_TREE 
    "\tWhen booting a Linux kernel which requires a flat device-tree\n" 
    "\ta third argument is required which is the address of the of the\n" 
    "\tdevice-tree blob. To boot that kernel without an initrd image,\n" 
    "\tuse a '-' for the second argument. If you do not pass a third\n" 
    "\ta bd_info struct will be passed instead\n" 
#endif 
); 

这个命令的执行最后会调用到do_bootm函数

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

    ulong    iflag; 
    ulong    addr; 
    ulong    data, len, checksum; 
    ulong  *len_ptr; 
    uint    unc_len = CFG_BOOTM_LEN; 
    int    i, verify; 
    char    *name, *s; 
    int    (*appl)(intchar *[]); 
    image_header_t *hdr = &header; 
 
    s = getenv ("verify"); 
    verify = (s && (*s == 'n')) ? 0 : 1
    /*argv[1]传入的是从nandflash中载入到内存的地址0x3200000032M的位置*/ 
    if (argc < 2
    { 
        addr = load_addr; 
    } 
    else 
    { 
        addr = simple_strtoul(argv[1], NULL, 16); 
    } 
 
    SHOW_BOOT_PROGRESS (1); 
    printf ("## Booting image at %08lx ...\n", addr); 
 
    /*使用u-boot启动的内核在制作镜像的时候会增加一个64byte的头,这里要对这个头做相应的处理*/ 
    memmove (&header, (char *)addr, sizeof(image_header_t)); 
 
    /*检查这个header中的幻数对不对*/ 
    if (ntohl(hdr->ih_magic) != IH_MAGIC) 
    { 
        { 
            puts ("Bad Magic Number\n"); 
            SHOW_BOOT_PROGRESS (-1); 
            return 1
        } 
    } 
    SHOW_BOOT_PROGRESS (2); 
 
    data = (ulong)&header; 
    len  = sizeof(image_header_t); 
 
    /*对这个header进行crc校验,比较原来保存的crc看是不是一致*/ 
    checksum = ntohl(hdr->ih_hcrc); 
    hdr->ih_hcrc = 0
    if (crc32 (0, (uchar *)data, len) != checksum) 
    { 
        puts ("Bad Header Checksum\n"); 
        SHOW_BOOT_PROGRESS (-2); 
        return 1
    } 
    SHOW_BOOT_PROGRESS (3); 
 
    /* for multi-file images we need the data part, too */ 
    print_image_hdr ((image_header_t *)addr); 
 
    data = addr + sizeof(image_header_t); 
    len  = ntohl(hdr->ih_size); 
 
    if (verify) 
    { 
        puts ("   Verifying Checksum ... "); 
        if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) 
        { 
            printf ("Bad Data CRC\n"); 
            SHOW_BOOT_PROGRESS (-3); 
            return 1
        } 
        puts ("OK\n"); 
    } 
    SHOW_BOOT_PROGRESS (4); 
 
    len_ptr = (ulong *)data; 
    /*cpu体系结构玛是不是正确*/ 
#if defined(__PPC__) 
    if (hdr->ih_arch != IH_CPU_PPC) 
#elif defined(__ARM__) 
    if (hdr->ih_arch != IH_CPU_ARM) 
#elif defined(__I386__) 
    if (hdr->ih_arch != IH_CPU_I386) 
#elif defined(__mips__) 
    if (hdr->ih_arch != IH_CPU_MIPS) 
#elif defined(__nios__) 
    if (hdr->ih_arch != IH_CPU_NIOS) 
#elif defined(__M68K__) 
    if (hdr->ih_arch != IH_CPU_M68K) 
#elif defined(__microblaze__) 
    if (hdr->ih_arch != IH_CPU_MICROBLAZE) 
#elif defined(__nios2__) 
    if (hdr->ih_arch != IH_CPU_NIOS2) 
#elif defined(__blackfin__) 
    if (hdr->ih_arch != IH_CPU_BLACKFIN) 
#elif defined(__avr32__) 
    if (hdr->ih_arch != IH_CPU_AVR32) 
#else 
# error Unknown CPU type 
#endif 
    { 
        printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch); 
        SHOW_BOOT_PROGRESS (-4); 
        return 1
    } 
    SHOW_BOOT_PROGRESS (5); 
 
    switch (hdr->ih_type) 
    { 
    case IH_TYPE_STANDALONE: 
        name = "Standalone Application"
        /* A second argument overwrites the load address */ 
        if (argc > 2
        { 
            hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16)); 
        } 
        break
    case IH_TYPE_KERNEL: 
        name = "Kernel Image"
        break
    case IH_TYPE_MULTI: 
        name = "Multi-File Image"
        len  = ntohl(len_ptr[0]); 
        /* OS kernel is always the first image */ 
        data += 8/* kernel_len + terminator */ 
        for (i = 1; len_ptr[i]; ++i) 
            data += 4
        break
    default
        printf ("Wrong Image Type for %s command\n", cmdtp->name); 
        SHOW_BOOT_PROGRESS (-5); 
        return 1
    } 
    SHOW_BOOT_PROGRESS (6); 
 
    /* 
     * We have reached the point of no return: we are going to 
     * overwrite all exception vector code, so we cannot easily 
     * recover from any failures any more... 
     */ 
 
    iflag = disable_interrupts(); 
 
#ifdef CONFIG_AMIGAONEG3SE 
    /* 
     * We've possible left the caches enabled during 
     * bios emulation, so turn them off again 
     */ 
    icache_disable(); 
    invalidate_l1_instruction_cache(); 
    flush_data_cache(); 
    dcache_disable(); 
#endif 
    /*这个位置需要注意,ih_load表示内核自己希望运行的位置,如果这个位置和 
      boot载入内核到内存的位置data不一样,叫进行拷贝*/ 
    switch (hdr->ih_comp) 
    { 
    case IH_COMP_NONE: 
        if(ntohl(hdr->ih_load) == data) 
        { 
            printf ("   XIP %s ... ", name); 
        } 
        else 
        { 
            memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); 
        } 
        break
    case IH_COMP_GZIP: 
        printf ("   Uncompressing %s ... ", name); 
        if (gunzip ((void *)ntohl(hdr->ih_load), unc_len, 
                    (uchar *)data, &len) != 0
        { 
            puts ("GUNZIP ERROR - must RESET board to recover\n"); 
            SHOW_BOOT_PROGRESS (-6); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
        break
#ifdef CONFIG_BZIP2 
    case IH_COMP_BZIP2: 
        printf ("   Uncompressing %s ... ", name); 
        /* 
         * If we've got less than 4 MB of malloc() space, 
         * use slower decompression algorithm which requires 
         * at most 2300 KB of memory. 
         */ 
        i = BZ2_bzBuffToBuffDecompress ((char *)ntohl(hdr->ih_load), 
                                        &unc_len, (char *)data, len, 
                                        CFG_MALLOC_LEN < (4096 * 1024), 0); 
        if (i != BZ_OK) 
        { 
            printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i); 
            SHOW_BOOT_PROGRESS (-6); 
            udelay(100000); 
            do_reset (cmdtp, flag, argc, argv); 
        } 
        break
#endif /* CONFIG_BZIP2 */ 
    default
        if (iflag) 
            enable_interrupts(); 
        printf ("Unimplemented compression type %d\n", hdr->ih_comp); 
        SHOW_BOOT_PROGRESS (-7); 
        return 1
    } 
    puts ("OK\n"); 
    SHOW_BOOT_PROGRESS (7); 
    switch (hdr->ih_type) 
    { 
    case IH_TYPE_STANDALONE: 
        if (iflag) 
            enable_interrupts(); 
        /* load (and uncompress), but don't start if "autostart" 
         * is set to "no" 
         */ 
        if (((s = getenv("autostart")) != NULL) && (strcmp(s, "no") == 0)) 
        { 
            char buf[32]; 
            sprintf(buf, "%lX", len); 
            setenv("filesize", buf); 
            return 0
        } 
        appl = (int ( *)(intchar * []))ntohl(hdr->ih_ep); 
        (*appl)(argc - 1, &argv[1]); 
        return 0
    case IH_TYPE_KERNEL: 
    case IH_TYPE_MULTI: 
        /* handled below */ 
        break
    default
        if (iflag) 
            enable_interrupts(); 
        printf ("Can't boot image type %d\n", hdr->ih_type); 
        SHOW_BOOT_PROGRESS (-8); 
        return 1
    } 
    SHOW_BOOT_PROGRESS (8); 
 
    /*最后我们一般启动的linux内核,调用do_bootm_linux进行处理*/ 
    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); 
        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 
    } 
 
    SHOW_BOOT_PROGRESS (-9); 
#ifdef DEBUG 
    puts ("\n## Control returned to monitor - resetting...\n"); 
    do_reset (cmdtp, flag, argc, argv); 
#endif 
    return 1

最后调用do_linux_bootm完成内核启动,这个函数主要做三个工作,首先是拷贝inird到确定位置,然后初始化内核参数到确定的位置,最后调用内核的启动函数,并传递两个参数给内核一个是cpu number,一个是内核参数位置。

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

    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"); 
#endif 
 
    /*ih_ep的值正是kernel启动的第一条code的位置*/ 
    theKernel = (void ( *)(intint, uint))ntohl(hdr->ih_ep); 
 
    /* 
     * Check if there is an initrd image 
     */ 
    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 */ 
#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); 
    /*设置要传递的内核参数到内存开始的512字节位置,然后启动linux内核,并 
    向linux 内核传递两个参数,一个参数是cpu类型,一个参数是内核参数位置*/ 
#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 (); // cancled by www.arm9.net 
    } 
#endif 
 
    cleanup_before_linux (); 
 
    theKernel (0, bd->bi_arch_number, bd->bi_boot_params); 

四、U-boot命令行调试状态

上面是对u-boot载入引导kernel的分析。如果用户通过串口打断引导过程,或者引导过程失败了,就会进入u-boot命令行调试状态。U-boot进入一个无限的for循环等待用户从串口输入命令,找到匹配的命令后执行相应的操作。

for (;;) 

    /*从串口读出命令*/ 
    len = readline (CFG_PROMPT); 
 
    flag = 0;    /* assume no special flags for now */ 
    if (len > 0
        strcpy (lastcommand, console_buffer); 
    else if (len == 0
        flag |= CMD_FLAG_REPEAT; 
#ifdef CONFIG_BOOT_RETRY_TIME 
    else if (len == -2
    { 
        /* -2 means timed out, retry autoboot 
         */ 
        puts ("\nTimed out waiting for command\n"); 
# ifdef CONFIG_RESET_TO_RETRY 
        /* Reinit board to run initialization code again */ 
        do_reset (NULL, 00, NULL); 
else 
        return;        /* retry autoboot */ 
# endif 
    } 
#endif 
    /*执行相应的命令*/ 
    if (len == -1
        puts ("\n"); 
    else 
        rc = run_command (lastcommand, flag); 
 
    if (rc <= 0
    { 
        /* invalid command or not repeatable, forget it */ 
        lastcommand[0] = 0
    } 

#endif /*CFG_HUSH_PARSER*/ 

Andy Yixin Deng

2013-08-25 南京

 

上一篇:Tcp connect establishment and termination
下一篇:tcp close关闭连接和SIGPIPE信号