(环境如下:U-BOOT S3C2440 LINUX) 记录自己的学习过程,如果分析有问题,请帮忙指正。
最近在研究U-BOOT的代码,其中的环境变量个人觉得用处非常大,所以重点学习和分析一下。
U-BOOT的第一个执行的文件为start.S,可以从链接文件分析出来(u-boot.lds)
进入U-BOOT执行过程如下:
1、设置CPU进入SVC32模式(set the cpu to SVC32 mode)
2、关看门狗(turn off the watchdog )
3、关中断(mask all IRQs by setting all bits in the INTMR - default)
4、比较_start和_TEXT_BASE(链接脚本里面定义代码的运行地址TEXT_BASE = 0x33F80000),判断如果不是仿真启动,则执行cpu_init_crit
注:如果代码是烧录到板子则_start地址没有调整前是0,如果是仿真器则直接下载到RAM,这会下载到链接地址上,即TEXT_BASE
5、cpu_init_crit执行如下(flush v4 I/D caches,disable MMU stuff and caches,setup RAM timing),即关mmu,设置存储管理器(lowlevel_init)
注:设置完存储管理器后SDRAM NANDFLASH 等外围设备才能使用
6、规划内存空间,并且设置栈(自己规划空间的分配如下:CFG_MALLOC_LEN,CFG_GBL_DATA_SIZE,CONFIG_STACKSIZE_IRQ,CONFIG_STACKSIZE_FIQ,12)
7、设置时钟 clock_init()
8、拷贝代码到SDRAM CopyCode2Ram()
注,r0 r1 r2分别为CopyCode2Ram的三个参数,即起始地址,目的地址,拷贝的长度(同样,如果是仿真器则跳过这段代码)
9、清除bss段(把未初始化的全部变量和则静态变量初始化为0)
10、设置PreLoadedONRAM标志位,方便后面判断是否是debug模式
11、获取start_armboot的链接地址,并且跳转到start_armboot,此时跳转到SDRAM中执行
12、start_armboot函数首先给两个结构体分配空间,并且设置结构体成员值为0
注:gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
13、接下来执行各种初始化,即init_sequence指针数组里面的各个初始化函数,其中env_init即环境变量初始化
环境变量分析:
1、进入env_init()函数
1 init_fnc_t *init_sequence[] = { 2 cpu_init, /* basic cpu dependent setup */ 3 board_init, /* basic board dependent setup */ 4 interrupt_init, /* set up exceptions */ 5 env_init, /* initialize environment */ //我们关注的函数 6 init_baudrate, /* initialze baudrate settings */ 7 serial_init, /* serial communications setup */ 8 console_init_f, /* stage 1 init of console */ 9 display_banner, /* say that we are here */ 10 #if defined(CONFIG_DISPLAY_CPUINFO) 11 print_cpuinfo, /* display cpu info (and speed) */ 12 #endif 13 #if defined(CONFIG_DISPLAY_BOARDINFO) 14 checkboard, /* display board info */ 15 #endif 16 dram_init, /* configure available RAM banks */ 17 display_dram_config, 18 NULL, 19 };
1 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { //循环执行上面结构体的内容,其中就有调用到env_init() 2 if ((*init_fnc_ptr)() != 0) { 3 hang (); 4 } 5 }
2、那么程序进入Env_nand.c执行
1 int env_init(void) 2 { 3 #if defined(ENV_IS_EMBEDDED) //搜寻代码 发现ENV_IS_EMBEDDED没有定义,所以执行#else部分 4 ulong total; 5 int crc1_ok = 0, crc2_ok = 0; 6 env_t *tmp_env1, *tmp_env2; 7 8 total = CFG_ENV_SIZE; 9 10 tmp_env1 = env_ptr; 11 tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE); 12 13 crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); 14 crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); 15 16 if (!crc1_ok && !crc2_ok) 17 gd->env_valid = 0; 18 else if(crc1_ok && !crc2_ok) 19 gd->env_valid = 1; 20 else if(!crc1_ok && crc2_ok) 21 gd->env_valid = 2; 22 else { 23 /* both ok - check serial */ 24 if(tmp_env1->flags == 255 && tmp_env2->flags == 0) 25 gd->env_valid = 2; 26 else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) 27 gd->env_valid = 1; 28 else if(tmp_env1->flags > tmp_env2->flags) 29 gd->env_valid = 1; 30 else if(tmp_env2->flags > tmp_env1->flags) 31 gd->env_valid = 2; 32 else /* flags are equal - almost impossible */ 33 gd->env_valid = 1; 34 } 35 36 if (gd->env_valid == 1) 37 env_ptr = tmp_env1; 38 else if (gd->env_valid == 2) 39 env_ptr = tmp_env2; 40 #else /* ENV_IS_EMBEDDED */ //执行此处 41 gd->env_addr = (ulong)&default_environment[0]; //获取默认的环境变量数组的地址 42 gd->env_valid = 1; //环境变量准备好标准位,=1表示准备OK 43 #endif /* ENV_IS_EMBEDDED */ 44 45 return (0); 46 }
3、继续往下分析发现和环境变量有关的函数,env_relocate ();
1 void env_relocate (void) 2 { 3 DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__, 4 gd->reloc_off); 5 6 #ifdef CONFIG_AMIGAONEG3SE //未定于,不执行 7 enable_nvram(); 8 #endif 9 10 #ifdef ENV_IS_EMBEDDED //未定义 不执行 11 /* 12 * The environment buffer is embedded with the text segment, 13 * just relocate the environment pointer 14 */ 15 env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off); 16 DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); 17 #else //执行 18 /* 19 * We must allocate a buffer for the environment 20 */ 21 env_ptr = (env_t *)malloc (CFG_ENV_SIZE); //给环境变量分配地址 22 DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); 23 #endif 24 25 /* 26 * After relocation to RAM, we can always use the "memory" functions 27 */ 28 env_get_char = env_get_char_memory; //env_get_char指向env_get_char_memory函数(这个函数执行返回对应便宜量的实际地址) 29 30 if (gd->env_valid == 0) { //gd->env_valid = 1,执行else 31 #if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */ 32 puts ("Using default environment\n\n"); 33 #else 34 puts ("*** Warning - bad CRC, using default environment\n\n"); 35 SHOW_BOOT_PROGRESS (-1); 36 #endif 37 38 if (sizeof(default_environment) > ENV_SIZE) 39 { 40 puts ("*** Error - default environment is too large\n\n"); 41 return; 42 } 43 44 memset (env_ptr, 0, sizeof(env_t)); 45 memcpy (env_ptr->data, 46 default_environment, 47 sizeof(default_environment)); 48 #ifdef CFG_REDUNDAND_ENVIRONMENT 49 env_ptr->flags = 0xFF; 50 #endif 51 env_crc_update (); 52 gd->env_valid = 1; 53 } 54 else { 55 env_relocate_spec (); //执行此处 56 } 57 gd->env_addr = (ulong)&(env_ptr->data); 58 59 #ifdef CONFIG_AMIGAONEG3SE 60 disable_nvram(); 61 #endif 62 }
4、跳去env_relocate_spec();
1 void env_relocate_spec (void) 2 { 3 #if !defined(ENV_IS_EMBEDDED) 4 ulong total = CFG_ENV_SIZE; 5 int ret; 6 7 ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr); //将nandlflash里面的环境变量,拷贝到env_ptr指向的内存 8 if (ret || total != CFG_ENV_SIZE) 9 return use_default(); 10 11 if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) 12 return use_default(); 13 #endif /* ! ENV_IS_EMBEDDED */ 14 }
如果没有执行过save,即没有把环境变量保存到nandflash。则执行use_default();使用默认的default_environment[]环境变量
5、跳出env_relocate_spec();
gd->env_addr = (ulong)&(env_ptr->data); //跳转地址,指向当前使用的环境变量数据
6、至此环境变量的准备工作已经完成。后面就可以正常使用了。例如s = getenv ("bootdelay");
7、getenv("bootdelay")函数会提取环境变量的指令(每条指令之间以("\0"结尾)),并且与传入的名字对比,如果相同则返回=后面的数据的地址,此数据即可拿来各种使用了
1 char *getenv (char *name) 2 { 3 int i, nxt; 4 5 WATCHDOG_RESET(); 6 7 for (i=0; env_get_char(i) != '\0'; i=nxt+1) { //循环提取字符串,与传入名字比较是否匹配 8 int val; 9 10 for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) { //提取一条完整的字符,使i=下一条字符的起始位置 11 if (nxt >= CFG_ENV_SIZE) { 12 return (NULL); 13 } 14 } 15 if ((val=envmatch((uchar *)name, i)) < 0) //比较函数传入参数name与提取到数据比较,如果匹配,则返回名字后面的数据的偏移量 16 continue; 17 return ((char *)env_get_addr(val)); //将偏移量转换为实际的地址,并且返回地址,此地址就可以拿来使用了 18 } 19 20 return (NULL); 21 }
8、执行save指令,可以调用int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])函数:
1 int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) 2 { 3 extern char * env_name_spec; 4 5 printf ("Saving Environment to %s...\n", env_name_spec); //打印 6 7 return (saveenv() ? 1 : 0); //跳转到下面的函数继续执行 8 }
1 int saveenv(void) 2 { 3 ulong total; 4 int ret = 0; 5 6 puts ("Erasing Nand..."); //打印 7 if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE)) //擦除nandflash中的环境变量 8 return 1; 9 10 puts ("Writing to Nand... "); //打印 11 total = CFG_ENV_SIZE; 12 ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr); //将当前环境变量的值写入到nandflash 13 if (ret || total != CFG_ENV_SIZE) 14 return 1; 15 16 puts ("done\n"); 17 return ret; 18 }
从上面分析,我们可以设置各种环境变量的各种参数,在u-boot需要使用的地方调用他们,使用非常方便。并且需要永久保存的直接写入nandflash。