厦门手机网站建设是什么,百度权重概念,专业营销型网站建设公司,优化wordpress速度一、uboot 的环境变量基础
1、环境变量的作用
(1) 让我们可以不用修改 uboot 的源代码#xff0c;而是通过修改环境变量#xff0c;来影响 uboot 运行时的一些数据和特性。譬如说#xff0c;通过修改 bootdelay 环境变量#xff0c;就可以更改系统开机自动启动时倒数的秒…一、uboot 的环境变量基础
1、环境变量的作用
(1) 让我们可以不用修改 uboot 的源代码而是通过修改环境变量来影响 uboot 运行时的一些数据和特性。譬如说通过修改 bootdelay 环境变量就可以更改系统开机自动启动时倒数的秒数。 2、环境变量的优先级
(1) uboot 代码当中有一个值环境变量中也有一个值。uboot 程序实际运行时规则是如果环境变量为空则使用代码中的值如果环境变量不为空则优先使用环境变量对应的值。
(2) 譬如 machid机器码。uboot 中在 x210_sd.h 中定义了一个机器码 2456写死在程序中的不能更改。如果要修改 uboot 中配置的机器码可以修改 x210_sd.h 中的机器码但是修改源代码后需要重新编译烧录很麻烦 比较简单的方法就是使用环境变量 machid。set machid 0x998 类似这样有了 machid 环境变量后系统启动时会优先使用 machid 对应的环境变量这就是优先级问题。 3、环境变量在 uboot 中工作方式
(1) 默认环境变量在 uboot/common/env_common.c 中 default_environment这东西本质是一个字符数组大小为 CFG_ENV_SIZE16 kByte里面内容就是很多个环境变量连续分布组成的每个环境变量最末端以 ‘\0’ 结束。 (2) SD 卡中的环境变量分区在 uboot 的 raw 分区中。SD 卡中其实就是给了个分区专门用来存储而已。存储时其实是把 DDR 中的环境变量整体的写入 SD 卡中分区里。所以当我们 saveenv 时其实整个所有的环境变量都被保存了一遍而不是只保存更改了的。
(3) DDR 中环境变量在 default_environment 中实质是字符数组。在 uboot 中其实是一个全局变量链接时在数据段重定位时default_environment 就被重定位到 DDR 中一个内存地址处了。这个地址处这个全局字符数组就是我们 uboot 运行时的 DDR 中的环境变量了。 总结
刚烧录的系统中环境变量分区是空白的uboot 第一次运行时加载的是 uboot 代码中自带的一份环境变量叫默认环境变量 default_environment。我们在 saveenv 时DDR 中的环境变量会被更新到 SD 卡中的环境变量中就可以被保存下来下次开机会在环境变量 relocate 时SD 卡中的环境变量会被加载到 DDR 中去。default_environment 中的内容虽然被 uboot 源代码初始化为一定的值这个值就是我们的默认环境变量但是在 uboot 启动的第二阶段env_relocate 时代码会去判断 SD 卡中的 env 分区的 crc 是否通过。如果 crc 校验通过说明 SD 卡中有正确的环境变量存储则 relocate 函数会从 SD 卡中读取环境变量来覆盖 default_environment 字符数组从而每次开机可以保持上一次更改过的环境变量。 二、环境变量相关命令源码解析1
1、printenv
(1) 找到 printenv 命令所对应的函数。通过 printenv 的 help 可以看出这个命令有 2 种使用方法。第一种直接使用不加参数则打印所有的环境变量第二种是 printenv name 则只打印出 name 这个环境变量的值。 (2) 分析 do_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 (i0; env_get_char(i) ! \0; inxt1) {for (nxti; env_get_char(nxt) ! \0; nxt);for (ki; knxt; 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 (i1; iargc; i) { /* print single env variables */char *name argv[i];k -1;for (j0; env_get_char(j) ! \0; jnxt1) {for (nxtj; 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;
}不论 SD 卡中是否有环境变量以及是否发生环境变量覆盖环境变量的首地址总是 default_environment 字符数组的首地址。 (3) do_printenv 函数首先区分 argc1 还是不等于 1 的情况若 argc1 那么就循环打印所有的环境变量出来如果 argc 不等于 1则后面的参数就是要打印的环境变量给哪个就打印哪个。
(4) argc1 时用双重 for 循环来依次处理所有的环境变量的打印。第一重 for 循环就是处理各个环境变量。所以有多少个环境变量则第一重就执行循环多少圈。 (5) 这个函数要看懂首先要明白整个环境变量在内存中如何存储的问题。
(6) 关键点第一要明白环境变量在内存中存储的方式第二要 C 语言处理字符串的功底要好。 三、环境变量相关命令源码解析 2 1、setenv
(1) 命令定义和对应的函数在 uboot/common/cmd_nvedit.c 中对应的函数为 do_setenv。 (2) setenv 的思路就是先去 DDR 中的环境变量处寻找原来有没有这个环境变量如果原来就有则需要覆盖原来的环境变量如果原来没有则在最后新增一个环境变量即可。
第1步遍历 DDR 中环境变量的数组找到原来就有的那个环境变量对应的地址。168-174 行。
第2步擦除原来的环境变量259-265 行。 第3步写入新的环境变量266-273 行。 (3)本来 setenv 做完上面的就完了但是还要考虑一些附加的问题。
问题一环境变量太多超出 DDR 中的字符数组溢出的解决方法。 问题二有些环境变量如 baudrate、ipaddr 等在 gd 中有对应的全局变量。这种环境变量在 set 更新的时候要同时去更新对应的全局变量否则就会出现在本次运行中环境变量和全局变量的值不一致的情况。 四、环境变量相关命令源码解析 2
1、saveenv
(1) 在 uboot/common/cmd_nvedit.c 中对应函数为 do_saveenv。 (2) 从 uboot 实际执行 saveenv 命令的输出和 x210_sd.h 中的配置#define CFG_ENV_IS_IN_AUTO可以分析出我们实际使用的是 env_auto.c 中相关的内容。没有一种芯片叫 auto 的env_auto.c 中是使用宏定义的方式去条件编译了各种常见的 flash 芯片如 movinand、norflash、nand 等。然后在程序中读取 INF_REGOMpin 内部对应的寄存器从而知道我们的启动介质然后调用这种启动介质对应的操作函数来操作。 (3) do_saveenv 内部调用 env_auto.c 中的 saveenv 函数来执行实际的环境变量保存操作。 (4) 寄存器地址E010F000 0CE010_F00C含义是用户自定义数据。我们在 start.S 中判断启动介质后将 #BOOT_MMCSD就是 3定义在x210_sd.h写入了这个寄存器所以这里读出的肯定是 3经过判断就是 movinand。所以实际执行的函数是saveenv_movinand。 (5) 真正执行保存环境变量操作的是cpu/s5pc11x/movi.c 中的 movi_write_env 函数这个函数肯定是写 sd卡将 DDR 中的环境变量数组其实就是 default_environment 这个数组大小 16kb刚好 32 个扇区写入 iNand 中的 ENV 分区中。 (6) raw_area_control 是 uboot 中规划 iNnad/SD 卡的原始分区表这个里面记录了我们对 iNand 的分区env 分区也在这里下标是2。 追到这一层就够了再里面就是调用驱动部分的写 SD卡/iNand 的底层函数了。 int init_raw_area_table (block_dev_desc_t * dev_desc)
{struct mmc *host find_mmc_device(dev_desc-dev);/* when last block does not have raw_area definition. */if (raw_area_control.magic_number ! MAGIC_NUMBER_MOVI) {int i 0;member_t *image;u32 capacity;if (host-high_capacity) {capacity host-capacity;#ifdef CONFIG_S3C6410if(IS_SD(host))capacity - 1024;#endif} else {capacity host-capacity;}dev_desc-block_read(dev_desc-dev,capacity - (eFUSE_SIZE/MOVI_BLKSIZE) - 1,1, raw_area_control);if (raw_area_control.magic_number MAGIC_NUMBER_MOVI) {return 0;}dbg(Warning: cannot find the raw area table(%p) %08x\n,raw_area_control, raw_area_control.magic_number);/* add magic number */raw_area_control.magic_number MAGIC_NUMBER_MOVI;/* init raw_area will be 16MB */raw_area_control.start_blk 16*1024*1024/MOVI_BLKSIZE;raw_area_control.total_blk capacity;raw_area_control.next_raw_area 0;strcpy(raw_area_control.description, initial raw table);image raw_area_control.image;#if defined(CONFIG_EVT1)#if defined(CONFIG_FUSED)/* image 0 should be fwbl1 */image[0].start_blk (eFUSE_SIZE/MOVI_BLKSIZE);image[0].used_blk MOVI_FWBL1_BLKCNT;image[0].size FWBL1_SIZE;image[0].attribute 0x0;strcpy(image[0].description, fwbl1);dbg(fwbl1: %d\n, image[0].start_blk);#endif
#endif/* image 1 should be bl2 */
#if defined(CONFIG_EVT1)#if defined(CONFIG_FUSED)image[1].start_blk image[0].start_blk MOVI_FWBL1_BLKCNT;#elseimage[1].start_blk (eFUSE_SIZE/MOVI_BLKSIZE);#endif
#elseimage[1].start_blk capacity - (eFUSE_SIZE/MOVI_BLKSIZE) -MOVI_BL1_BLKCNT;
#endifimage[1].used_blk MOVI_BL1_BLKCNT;image[1].size SS_SIZE;image[1].attribute 0x1;strcpy(image[1].description, u-boot parted);dbg(bl1: %d\n, image[1].start_blk);/* image 2 should be environment */
#if defined(CONFIG_EVT1)image[2].start_blk image[1].start_blk MOVI_BL1_BLKCNT;
#elseimage[2].start_blk image[1].start_blk - MOVI_ENV_BLKCNT;
#endifimage[2].used_blk MOVI_ENV_BLKCNT;image[2].size CFG_ENV_SIZE;image[2].attribute 0x10;strcpy(image[2].description, environment);dbg(env: %d\n, image[2].start_blk);/* image 3 should be bl2 */
#if defined(CONFIG_EVT1)image[3].start_blk image[2].start_blk MOVI_ENV_BLKCNT;
#elseimage[3].start_blk image[2].start_blk - MOVI_BL2_BLKCNT;
#endifimage[3].used_blk MOVI_BL2_BLKCNT;image[3].size PART_SIZE_BL;image[3].attribute 0x2;strcpy(image[3].description, u-boot);dbg(bl2: %d\n, image[3].start_blk);/* image 4 should be kernel */
#if defined(CONFIG_EVT1)image[4].start_blk image[3].start_blk MOVI_BL2_BLKCNT;
#elseimage[4].start_blk image[3].start_blk - MOVI_ZIMAGE_BLKCNT;
#endifimage[4].used_blk MOVI_ZIMAGE_BLKCNT;image[4].size PART_SIZE_KERNEL;image[4].attribute 0x4;strcpy(image[4].description, kernel);dbg(knl: %d\n, image[4].start_blk);/* image 5 should be RFS */
#if defined(CONFIG_EVT1)image[5].start_blk image[4].start_blk MOVI_ZIMAGE_BLKCNT;
#elseimage[5].start_blk image[4].start_blk - MOVI_ROOTFS_BLKCNT;
#endifimage[5].used_blk MOVI_ROOTFS_BLKCNT;image[5].size PART_SIZE_ROOTFS;image[5].attribute 0x8;strcpy(image[5].description, rfs);dbg(rfs: %d\n, image[5].start_blk);for (i6; i15; i) {raw_area_control.image[i].start_blk 0;raw_area_control.image[i].used_blk 0;}}
} 五、uboot 内部获取环境变量
1、getenv
(1) 应该是不可重入的。
(2) 实现方式就是去遍历 default_environment 数组挨个拿出所有的环境变量比对 name找到相等的直接返回这个环境变量的首地址即可。 2、getenv_r
(1) 可重入版本。可自行搜索补充可重入函数的概念
(2) getenv 函数是直接返回这个找到的环境变量在 DDR 中环境变量处的地址而 getenv_r 函数的做法是找到了 DDR 中环境变量地址后将这个环境变量复制一份到提供的 buf 中而不去动原来 DDR 中环境变量。 所以差别就是getenv 中返回的地址只能读不能随便乱写而 getenv_r 中返回的环境变量是在自己提供的 buf 中是可以随便改写加工的。 3、总结
(1) 功能是一样的但是可重入版本会比较安全一些建议使用。
(2) 有关于环境变量的所有操作主要理解了环境变量在 DDR 中的存储方法理解了环境变量和 gd 全局变量的关联和优先级理解了环境变量在存储介质中的存储方式专用raw分区整个环境变量相关的都清楚了。 源自朱有鹏老师.