注:本文是学习朱老师课程整理的笔记,基于uboot-1.3.4和s5pc11x分析。

环境变量的作用

可以不用修改uboot的源代码,而是通过修改环境变量来影响uboot运行时的一些数据和特性。譬如说通过修改bootdelay环境变量就可以更改系统开机自动启动时倒数的秒数。

环境变量的优先级

如果环境变量为空则使用代码中的值;如果环境变量不为空则优先使用环境变量对应的值。

譬如machid(机器码)。uboot中在x210_sd.h中定义了一个机器码2456,如果要修改uboot中配置的机器码,可以修改x210_sd.h中的机器码,但是修改源代码后需要重新编译烧录,很麻烦;比较简单的方法就是使用环境变量machid。如:set machid 0x998,有了machid环境变量后,系统启动时会优先使用machid对应的环境变量,这就是优先级问题。

环境变量的工作方式

默认环境变量,在uboot/common/env_common.c中default_environment:

uchar default_environment[CFG_ENV_SIZE] = {"bootargs=" CONFIG_BOOTARGS         "\0""bootcmd="  CONFIG_BOOTCOMMAND      "\0""mtdpart="  CONFIG_MTDPARTITION     "\0""nfsboot="  CONFIG_NFSBOOTCOMMAND       "\0""bootdelay="    MK_STR(CONFIG_BOOTDELAY)    "\0""baudrate=" MK_STR(CONFIG_BAUDRATE)     "\0""ethaddr="  MK_STR(CONFIG_ETHADDR)      "\0""ipaddr="   MK_STR(CONFIG_IPADDR)       "\0""serverip=" MK_STR(CONFIG_SERVERIP)     "\0""gatewayip="    MK_STR(CONFIG_GATEWAYIP)    "\0""netmask="  MK_STR(CONFIG_NETMASK)      "\0""\0"
};

环境变量在内存中的存储大体如下:

这东西本质是一个字符数组,大小为CFG_ENV_SIZE(16kb),里面内容就是很多个环境变量连续分布组成的,每个环境变量最末端以’\0’结束。

SD卡中环境变量分区,在uboot的raw分区中。存储时是把DDR中的环境变量整体的写入SD卡中分区里。所以当我们saveenv时其实整个所有的环境变量都被保存了一遍,而不是只保存更改了的。

DDR中环境变量就是default_environment字符数组,在uboot中其实是一个全局变量,链接时在数据段,重定位时default_environment就被重定位到DDR中一个内存地址处了。

刚烧录的SD卡中环境变量分区是空白的,uboot第一次运行时加载的是uboot代码中自带的一份环境变量,叫默认环境变量。我们在saveenv时DDR中的环境变量会被更新到SD卡中的环境变量中,就可以被保存下来,下次开机会将环境变量从SD卡中relocate到DDR中去。

default_environment中的内容虽然被uboot源代码初始化为一定的值(这个值就是我们的默认环境变量),但是在uboot启动的第二阶段,env_relocate时代码会去判断SD卡中的env分区的crc是否通过。如果crc校验通过说明SD卡中有正确的环境变量存储,则relocate函数会从SD卡中读取环境变量来覆盖default_environment字符数组,从而每次开机可以保持上一次更改过的环境变量。

环境变量相关命令源码解析

  • printenv
int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{int i, j, k, nxt;int rcode = 0;if (argc == 1) {        /* Print all env variables  */for (i=0; env_get_char(i) != '\0'; i=nxt+1) {for (nxt=i; env_get_char(nxt) != '\0'; ++nxt);for (k=i; k<nxt; ++k)putc(env_get_char(k));putc  ('\n');if (ctrlc()) {puts ("\n ** Abort\n");return 1;}}printf("\nEnvironment size: %d/%ld bytes\n",i, (ulong)ENV_SIZE);return 0;}for (i=1; i<argc; ++i) {    /* print single env variables   */char *name = argv[i];k = -1;for (j=0; env_get_char(j) != '\0'; j=nxt+1) {for (nxt=j; env_get_char(nxt) != '\0'; ++nxt);k = envmatch((uchar *)name, j);if (k < 0) {continue;}puts (name);putc ('=');while (k < nxt)putc(env_get_char(k++));putc ('\n');break;}if (k < 0) {printf ("## Error: \"%s\" not defined\n", name);rcode ++;}}return rcode;
}

找到printenv命令所对应的函数。通过printenv的help可以看出,这个命令有2种使用方法。第一种直接使用不加参数则打印所有的环境变量;第二种是printenv name则只打印出name这个环境变量的值。

do_printenv函数首先判断argc是否等于1,若argc=1那么就循环打印所有的环境变量出来;如果argc不等于1,则后面的参数就是要打印的环境变量,给哪个环境变量就打印哪个。

argc=1时用双重for循环来依次打印所有的环境变量。第一重for循环就是处理各个环境变量。所以有多少个环境变量则第一重就执行循环多少圈。

env_get_char函数中又调用了 env_get_char_memory:

uchar env_get_char_memory (int index)
{if (gd->env_valid) {return ( *((uchar *)(gd->env_addr + index)) );} else {return ( default_environment[index] );}
}

上面两条return的语句其实可以相等。
在env_init函数中可以看出:

gd->env_addr  = (ulong)&default_environment[0];
gd->env_valid = 1;

总结:这个函数要看懂,首先要明白整个环境变量在内存中如何存储的。

  • setenv
    命令定义对应的函数在uboot/common/cmd_nvedit.c中,对应的函数为do_setenv。
int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{if (argc < 2) {printf ("Usage:\n%s\n", cmdtp->usage);return 1;}return _do_setenv (flag, argc, argv);
}

do_setenv中又调用了_do_setenv,_do_setenv的思路就是:先去DDR中的环境变量处寻找原来有没有这个环境变量,如果原来就有则需要覆盖原来的环境变量,如果原来没有则在最后新增一个环境变量即可。

第1步:遍历DDR中环境变量的数组,找到原来就有的那个环境变量对应的地址。

/** search if variable with this name already exists*/oldval = -1;for (env=env_data; *env; env=nxt+1) {for (nxt=env; *nxt; ++nxt);if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0)break;}

第2步:擦除原来的环境变量
第3步:写入新的环境变量

if (*++nxt == '\0') {     /* 擦除原来的环境变量 */if (env > env_data) {env--;} else {*env = '\0';}} else {      for (;;) {   /* 写入新的环境变量 */*env = *nxt++;if ((*env == '\0') && (*nxt == '\0'))break;++env;}}*++env = '\0';}

本来setenv做完上面的就完了,但是还要考虑一些附加的问题。
问题一:环境变量太多超出DDR中的字符数组,溢出的解决方法。
问题二:有些环境变量如baudrate、ipaddr等,在gd中有对应的全局变量。这种环境变量在set更新的时候要同时去更新对应的全局变量,否则就会出现在本次运行中环境变量和全局变量不一致的情况。

  • saveenv

在uboot/common/cmd_nvedit.c中,对应函数为do_saveenv

int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{extern char * env_name_spec;printf ("Saving Environment to %s...\n", env_name_spec);return (saveenv() ? 1 : 0);
}

从uboot实际执行saveenv命令的输出,可以知道env_name_spec的定义在env_auto.c中。接着saveenv()就是定义在env_auto.c中:

int saveenv(void)
{#if defined(CONFIG_S5PC100) || defined(CONFIG_S5PC110) ||           defined(CONFIG_S5P6442)if (INF_REG3_REG == 2)saveenv_nand();else if (INF_REG3_REG == 3)saveenv_movinand();else if (INF_REG3_REG == 1)saveenv_onenand();else if (INF_REG3_REG == 4)saveenv_nor();elseprintf("Unknown boot device\n");return 0;
}

使用宏定义的方式去条件编译了各种常见的flash芯片(如movinand、norflash、nand等)。然后在程序中读取INF_REG(OMpin内部对应的寄存器)从而知道我们的启动介质,然后调用这种启动介质对应的函数来操作。这里我们的INF_REG3_REG =3,它的赋值在start.s中:

/* SD/MMC BOOT */cmp     r2, #0xcmoveq   r3, #BOOT_MMCSD  /* #define BOOT_MMCSD      0x3 */……ldr r0, =INF_REG_BASEstr r3, [r0, #INF_REG3_OFFSET]     

INF_REG3_REG 寄存器地址:E010F000+0C=E010_F00C,在芯片数据手册中查到该寄存器是用户自定义数据。我们在start.S中判断启动介质后将#BOOT_MMCSD(就是3,定义在x210_sd.h)写入了这个寄存器,所以这里读出的肯定是3,经过判断就是movinand。所以实际执行的函数是:saveenv_movinand。

int saveenv_movinand(void)
{movi_write_env(virt_to_phys((ulong)env_ptr));puts("done\n");return 1;
}

真正执行保存环境变量操作的是:cpu/s5pc11x/movi.c中的movi_write_env函数,这个函数肯定是写sd卡,将DDR中的环境变量数组(其实就是default_environment这个数组,大小16kb,刚好32个扇区)写入iNand中的ENV分区中。

void movi_write_env(ulong addr)
{movi_write(raw_area_control.image[2].start_blk,raw_area_control.image[2].used_blk, addr);
}

raw_area_control是uboot中规划iNnad/SD卡的原始分区表,这个里面记录了我们对iNand的分区,env分区也在这里,下标是2。追到这一层就够了,再里面就是调用驱动部分的写SD卡/iNand的底层函数了。

  • getenv和getenv_r

getenv是不可重入函数(关于函数的可重入性分析见函数的可重入性理解)。实现方式就是去遍历default_environment数组,挨个拿出所有的环境变量比对name,找到相等的直接返回这个环境变量的首地址即可。

getenv_r是可重入函数。getenv函数是直接返回这个找到的环境变量在DDR中环境变量处的地址,而getenv_r函数的做法是找到了DDR中环境变量地址后,将这个环境变量复制一份到提供的buf中,而不动原来DDR中环境变量。

所以差别就是:getenv中返回的地址只能读不能随便乱写,而getenv_r中返回的环境变量是在自己提供的buf中,是可以随便改写加工的。两者功能是一样的,但是可重入版本会比较安全一些,建议使用。

uboot的环境变量相关推荐

  1. 从bootm 命令讲起/U-boot的环境变量: bootcmd 和bootargs

    从bootm 命令讲起 1 找到linux的内核入口 Bootm命令通过读取uImage的头部0×40字节的信息,将uImage定位到正确的地址,同时找到linux的内核入口地址. 这个地方就涉及到u ...

  2. uboot默认环境变量修改

    uboot的默认环境变量决定了系统是通过何种方式启动的,对于定制化的嵌入式系统,uboot的定制化修改也是必要的. uboot有两种修改方式: 1.直接修改源码或者修改uboot配置 2.在uboot ...

  3. uboot之环境变量

    一.环境变量基础知识 1.环境变量的作用 环境变量的最大作用就是在我们不需要改变源代码的情况下,改变程序的执行情况.比如我们的bootdelay时间,通过修改对应的环境变量的值,就可以改变uboot开 ...

  4. uboot中环境变量的加载、写入过程详解

    1.uboot启动中环境变量的加载 1.1.uboot加载环境变量流程分析 (1)首先使用默认的环境变量default_environment[]: (2)然后加载SD卡中env分区的环境变量,校验读 ...

  5. uboot中环境变量的实现

    1.环境变量介绍 uboot中环境变量的作用类似于全局变量,需要某个环境变量的值时调用getenv函数就可以得到.环境变量会指导程序的运行,不必修改代码重新编译,通过修改环境变量就可以改变uboot的 ...

  6. 如何修改uboot的环境变量env的值来指定uImage的名字

    今天继续玩基于uboot的nfs.昨天总算是基本搞清了make zImage和make uImage的区别,那么今天就来实际编译几个玩一玩. 不过,在利用mkimage工具对zImage镜像文件加工完 ...

  7. 嵌入式Linux读写uboot的环境变量 —— fw_printenv 的使用

    下载 u-boot-2010.06 官网下载 ftp://ftp.denx.de/pub/u-boot/ 积分多的可以去这里下载,网速会快一点 https://download.csdn.net/do ...

  8. 提取u-boot fw_env环境变量生成工具

    u-boot源码目录tools/env下定义有用来制作u-boot环境变量固件的fwenv工具,虽然可以使用make envtools直接编译生成fwenv,不过在u-boot工程下直接编译生成的fw ...

  9. 香橙派orangepi pc plus h3 uboot保存环境变量失败解决——Unable to use mmc 1:1... Failed (1)

    前言 环境介绍: 1.编译环境 Ubuntu 18.04.5 LTS 2.SDK orangepi Linux 5.4 SDK 3.uboot v2020.04 一.现象 根据<OrangePi ...

最新文章

  1. Python多进程 AttributeError: Can't get attribute 'worker' on module '__main__' from
  2. 香港中文大学 (深圳) -博士硕士招生 -智能优化及规划方向
  3. 北京驾照到期换证简记
  4. display:inline-block+text-align:justify实现列表元素的两端对齐
  5. (转载)hive文件存储格式
  6. PyTorch 1.0 中文文档:torch.autograd
  7. 最详细win7下手动搭建PHP环境:apache2.4.23+php7.0.11
  8. “数学不好,干啥都不行!”骨灰级程序员:别再瞎努力了!
  9. JS日历控件优化(增加时分秒)
  10. 解决Spring对静态变量无法注入问题(转)
  11. 剑指offer——面试题61:按之字形顺序打印二叉树
  12. java爬虫登录_Java爬虫中怎么爬取需要登录的网站
  13. STM32串口通信 中断配置
  14. ora11g 安装报错ins_emagent.mk
  15. php判断floor,php floor()函数
  16. 30天自制操作系统 学习笔记1
  17. 离职,我应该做什么?
  18. 微信小程序点赞成功,取消点赞、评论。
  19. 用java画太阳图_使用Java制作太阳系模型(3)
  20. 良知的清醒常常意味着糟糕的记忆力的标志。

热门文章

  1. 计算机管理任务计划程序全部禁用,win10计划任务如何关闭_win10怎么禁用计划任务...
  2. 流媒体后视镜方案关键技术--摄像头硬件系统
  3. 外行假装内行,我也来谈谈SAP BAPI和BADI
  4. 强制删除五笔字型输入法
  5. 搞懂敏感性、特异性以及精确率和召回率的关系
  6. 杨辉三角 C语言实现【一维数组】
  7. 思岚科技—SLAMTEC对于激光雷达的执着与坚持
  8. 解决电脑搜不到WiFi6无线路由信号问题
  9. win7网络适配器_WIN7笔记本搜不到WiFi怎么办?
  10. 《机器学习实战》学习笔记第十一章 —— Apriori算法