码迷,mamicode.com
首页 > 其他好文 > 详细

u-boot启动第二阶段简要分析

时间:2016-01-19 14:22:45      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:

u-boot第一启动阶段的最后跳转到 start_armboot 函数。这个函数在 lib_arm/board.c 中定义。下面就来看看这个函数做了哪些工作。本文的分析过程比较肤浅。只能说是大致流程。更细的流程还需要仔细的钻研。

下面是两个整个u-boot所使用的最重要的两个全局变量结构体。u-boot的第二阶段也是围绕这两个结构体展开的。

typedef unsigned long long phys_size_t;

typedef struct bd_info {
    unsigned long   bi_memstart;    /* start of DRAM memory */
    phys_size_t        bi_memsize;     /* size  of DRAM memory in bytes */
    unsigned long   bi_flashstart;  /* start of FLASH memory */
    unsigned long   bi_flashsize;   /* size  of FLASH memory */
    unsigned long   bi_flashoffset; /* reserved area for startup monitor */
    unsigned long   bi_sramstart;   /* start of SRAM memory */
    unsigned long   bi_sramsize;    /* size  of SRAM memory */
    unsigned long   bi_ip_addr;     /* IP Address */
    unsigned long   bi_baudrate;    /* Console Baudrate */
    unsigned long    bi_boot_params; /* where this board expects params */
} bd_t;


typedef    struct    global_data {
    bd_t        *bd;
    unsigned long    flags;
    unsigned long    baudrate;
    unsigned long    cpu_clk;            /* CPU clock in Hz!        */
    unsigned long    have_console;        /* serial_init() was called */
    phys_size_t        ram_size;            /* RAM size */
    unsigned long    env_addr;            /* Address  of Environment struct */
    unsigned long    env_valid;            /* Checksum of Environment valid */
    void        **jt;                    /* Standalone app jump table */
} gd_t;

 

对bd gd结构体的初始化

/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");

memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));

 

各个初始化函数

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

初始化函数保存在init_fnc_t *init_sequence[] 数组中。

 

初始化环境变量相关

/* initialize environment */
env_relocate ();

这个函数展开来看看。

env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);
    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
    #endif

        if (gd->env_valid == 0) {
    #if defined(CONFIG_GTH)    || defined(CONFIG_ENV_IS_NOWHERE)    /* Environment not changable */
            puts ("Using default environment\n\n");
    #else
            puts ("*** Warning - bad CRC, using default environment\n\n");
            show_boot_progress (-60);
    #endif
            set_default_env();
    }
    else {
        env_relocate_spec ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);

如果我们使用 CONFIG_ENV_IS_NOWHERE 先是为存储环境变量分配一段空间。然后使用 set_default_env() 配置默认的参数。并且env_ptr保存在gd->env_addr中。
接下来看看 env_t 到底是个什么结构还有 set_default_env 到底是使用的哪些初始变量。

typedef    struct environment_s {
    uint32_t    crc;        /* CRC32 over data bytes    */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
    unsigned char    flags;        /* active/obsolete flags    */
#endif
    unsigned char    data[ENV_SIZE]; /* Environment data        */
} env_t;
void set_default_env(void)
{
    if (sizeof(default_environment) > ENV_SIZE) {
        puts ("*** Error - default environment is too large\n\n");
        return;
    }

    memset(env_ptr, 0, sizeof(env_t));
    memcpy(env_ptr->data, default_environment,
           sizeof(default_environment));
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
    env_ptr->flags = 0xFF;
#endif
    env_crc_update ();
    gd->env_valid = 1;
}

其实将 default_environment 拷贝到 env_ptr->data 。 那再去找找 default_environment 这个全局变量定义的地方。

static char default_environment[] = {
#if defined(CONFIG_BOOTARGS)
    "bootargs=" CONFIG_BOOTARGS "\0"
#endif
#if defined(CONFIG_BOOTCOMMAND)
    "bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
#if defined(CONFIG_RAMBOOTCOMMAND)
    "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_NFSBOOTCOMMAND)
    "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
    "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0"
#endif
#ifdef    CONFIG_LOADS_ECHO
    "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0"
#endif
#ifdef    CONFIG_ETHADDR
    "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0"
#endif
#ifdef    CONFIG_ETH1ADDR
    "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0"
#endif
#ifdef    CONFIG_ETH2ADDR
    "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0"
#endif
#ifdef    CONFIG_ETH3ADDR
    "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0"
#endif
#ifdef    CONFIG_ETH4ADDR
    "eth4addr=" MK_STR (CONFIG_ETH4ADDR) "\0"
#endif
#ifdef    CONFIG_ETH5ADDR
    "eth5addr=" MK_STR (CONFIG_ETH5ADDR) "\0"
#endif
#ifdef    CONFIG_ETHPRIME
    "ethprime=" CONFIG_ETHPRIME "\0"
#endif
#ifdef    CONFIG_IPADDR
    "ipaddr=" MK_STR (CONFIG_IPADDR) "\0"
#endif
#ifdef    CONFIG_SERVERIP
    "serverip=" MK_STR (CONFIG_SERVERIP) "\0"
#endif
#ifdef    CONFIG_SYS_AUTOLOAD
    "autoload=" CONFIG_SYS_AUTOLOAD "\0"
#endif
#ifdef    CONFIG_ROOTPATH
    "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0"
#endif
#ifdef    CONFIG_GATEWAYIP
    "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0"
#endif
#ifdef    CONFIG_NETMASK
    "netmask=" MK_STR (CONFIG_NETMASK) "\0"
#endif
#ifdef    CONFIG_HOSTNAME
    "hostname=" MK_STR (CONFIG_HOSTNAME) "\0"
#endif
#ifdef    CONFIG_BOOTFILE
    "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0"
#endif
#ifdef    CONFIG_LOADADDR
    "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0"
#endif
#ifdef    CONFIG_PREBOOT
    "preboot=" CONFIG_PREBOOT "\0"
#endif
#ifdef    CONFIG_CLOCKS_IN_MHZ
    "clocks_in_mhz=" "1" "\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
    "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
#endif
    "\0"        /* Termimate struct environment data with 2 NULs */
};

原来这些环境参数都保存在这个静态字符数组中。我们还是可以通过修改配置文件的方式来修改一些默认的环境变量的。

接下来来是一系列初始化函数,暂且跳过。

最后u-boot 进入主循环

/* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop ();
    }

看看 mian_loop() 函数

#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv ("bootcmd");

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    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);
# else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
# endif
    }

这段代码显示,如果在bootdelay时间内,用户没有在键盘输入字符,那么将使用 bootcmd 参数自动启动内核。再来看看 abortboot() 函数

    while ((bootdelay > 0) && (!abort)) {
        int i;

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

        printf("\b\b\b%2d ", bootdelay);
    }

其实是通过tstc() 函数不断测试uart寄存器的状态位来判断是否有用户从键盘输入字符。如果有的话,abort = 1最后再返回,那么 (bootdelay >= 0 && s && !abortboot (bootdelay)) 不成立。main_loop() 继续往下执行。下面来看看不断解析与执行命令部分的代码。readline函数用来读取用户输入。

len = readline (CONFIG_SYS_PROMPT);

readline() 实际上调用 readline_into_buffer()

c = getc();

        /*
         * Special character handling
         */
        switch (c) {
        case \r:                /* Enter        */
        case \n:
            *p = \0;
            puts ("\r\n");
            return (p - p_buf);

        case \0:                /* nul            */
            continue;

        case 0x03:                /* ^C - break        */
            p_buf[0] = \0;    /* discard input */
            return (-1);

        case 0x15:                /* ^U - erase line    */
            while (col > plen) {
                puts (erase_seq);
                --col;
            }
            p = p_buf;
            n = 0;
            continue;

        case 0x17:                /* ^W - erase word    */
            p=delete_char(p_buf, p, &col, &n, plen);
            while ((n > 0) && (*p !=  )) {
                p=delete_char(p_buf, p, &col, &n, plen);
            }
            continue;

        case 0x08:                /* ^H  - backspace    */
        case 0x7F:                /* DEL - backspace    */
            p=delete_char(p_buf, p, &col, &n, plen);
            continue;

        default:
            /*
             * Must be a normal character then
             */
            if (n < CONFIG_SYS_CBSIZE-2) {
                if (c == \t) {    /* expand TABs        */
#ifdef CONFIG_AUTO_COMPLETE
                    /* if auto completion triggered just continue */
                    *p = \0;
                    if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
                        p = p_buf + n;    /* reset */
                        continue;
                    }
#endif
                    puts (tab_seq+(col&07));
                    col += 8 - (col&07);
                } else {
                    ++col;        /* echo input        */
                    putc (c);
                }
                *p++ = c;
                ++n;
            } else {            /* Buffer full        */
                putc (\a);
            }
        }

该函数通过不断的循环读取用户输入并将参数保存在全局变量 console_buffer 中。下面是执行命令的代码。

strcpy (lastcommand, console_buffer);
rc = run_command (lastcommand, flag);

/* find macros in this token and replace them */
        process_macros (token, finaltoken);
        
/* Extract arguments */
        if ((argc = parse_line (finaltoken, argv)) == 0) {
            rc = -1;    /* no command at all */
            continue;
        }
        

    /* Look up command in command table */
if ((cmdtp = find_cmd(argv[0])) == NULL) {
    printf ("Unknown command ‘%s‘ - try ‘help‘\n", argv[0]);
    rc = -1;    /* give up after bad command */
    continue;
}

通过find_cmd来查询用户输入的命令。那么就来看看 find_cmd() 这个函数

cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
    cmd_tbl_t *cmdtp;
    cmd_tbl_t *cmdtp_temp = table;    /*Init value */
    const char *p;
    int len;
    int n_found = 0;

    /*
     * Some commands allow length modifiers (like "cp.b");
     * compare command name only until first dot.
     */
    len = ((p = strchr(cmd, .)) == NULL) ? strlen (cmd) : (p - cmd);

    for (cmdtp = table;
         cmdtp != table + table_len;
         cmdtp++) {
        if (strncmp (cmd, cmdtp->name, len) == 0) {
            if (len == strlen (cmdtp->name))
                return cmdtp;    /* full match */

            cmdtp_temp = cmdtp;    /* abbreviated command ? */
            n_found++;
        }
    }
    if (n_found == 1) {            /* exactly one match */
        return cmdtp_temp;
    }

    return NULL;    /* not found or ambiguous command */
}

下面是 cmd 所使用的结构体类型

struct cmd_tbl_s {
    char        *name;        /* Command Name            */
    int        maxargs;        /* maximum number of arguments    */
    int        repeatable;        /* autorepeat allowed?        */
                            /* Implementation function    */
    int        (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
    char        *usage;        /* Usage message    (short)    */
    char        *help;        /* Help  message    (long)    */
};

typedef struct cmd_tbl_s    cmd_tbl_t;

 

比较困惑的是这些cmd的结构体是保存在哪里,使用什么数据结构(结构体数组?) ,它们是如何组织在一起的。看到下面的这段代码就清楚了。

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

#ifdef  CONFIG_SYS_LONGHELP

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
{#name, maxargs, rep, cmd, usage, help}

#else    /* no long help info */

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
{#name, maxargs, rep, cmd, usage}

u-boot定义了一些宏使得编写的cmd结构可以很方便的保存在 u_boot_cmd section 中。而这个段的首地址就是
extern cmd_tbl_t __u_boot_cmd_start;

那么执行cmd的过程就是对查找到的命令调用其回调函数就可以了。

/* OK - call function to do the command */
        if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
            rc = -1;
        }

 

至此,算是对u-boot的第二阶段有了一个感性的认识,当然u-boot的第二阶段最重要的任务将启动参数bootargs传递给内核,让内核启动。

 

u-boot启动第二阶段简要分析

标签:

原文地址:http://www.cnblogs.com/Gru--/p/5141876.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!