标签:
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传递给内核,让内核启动。
标签:
原文地址:http://www.cnblogs.com/Gru--/p/5141876.html