https://ke.qq.com/course/4032547?flowToken=1042705
目录
https://ke.qq.com/course/4032547?flowToken=1042705
前言
1 环境变量是什么
就是uboot中的配置信息,可保存在外存中,供启动时使用。
2 什么时候使用环境变量
有更改的需求,无需修改代码,就可以动态配置uboot的运行状态
3 环境变量的初始化过程
环境变量分两个阶段初始化,
第一阶段,加载代码中的数组中的默认内容,构成原始的环境变量,
第二阶段,如果外存中有环境变量,使用外存中的值,
第三阶段,根据硬件状态的变化,会被uboot重新设定的环境变量,例如支持DHCP功能的uboot,或者需要根据DIP拨码决定启动状态的电路板。
一 环境变量命令族
printenv
=> help printenv
printenv - print environment variables
Usage:
printenv [-a]
- print [all] values of all environment variables
printenv name ...
- print value of environment variable 'name'
测试验证
=> printenv
UUID_ID0=5d468f33
UUID_ID1=452f91d7
/*
略
*/
stdin=serial
stdout=serial
Environment size: 2692/8188 bytes
=>
最后一行表示当前环境变量占用空间2726字节,最大可用空间为8188字节。
首先环境变量被存放到了1536块处,分配了16个扇区,每个扇区512字节,所以大小应该是16*512 = 8192。这里显示的是8188。
验证环境变量的头四个字节是什么
=> mmc dev 1 0
=> mmc read 80800000 600 10
MMC read: dev # 1, block # 1536, count 16 ... 16 blocks read: OK
=>
=> md.b 80800000 10
80800000: 48 e8 a8 91 55 55 49 44 5f 49 44 30 3d 35 64 34 H...UUID_ID0=5d4
=>
=> md.b 80801FF0 10
80801ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
=>
这里先将emmc中的环境变量读到DDR中,然后打印出DDR中的值,应该写一个直接打印emmc中值的命令,看到前4个字节是48 e8 a8 91,然后最后16个字节都是0。48 e8 a8 91这个看不出是什么意思,下面分析代码的时候再来研究。
指定一个值
=> printenv ipaddr
ipaddr=192.168.5.25
=>
打印多个值
=> printenv ipaddr serverip
ipaddr=192.168.5.25
serverip=192.168.5.20
setenv
=> help setenv
setenv - set environment variables
Usage:
setenv [-f] name value ...
- [forcibly] set environment variable 'name' to 'value ...'
setenv [-f] name
- [forcibly] delete environment variable 'name'
添加一个新环境变量
注意添加时没有等号,最好养成在值的两边加单引号的习惯
=> setenv hehe 'wo shi yige zhi'
=> printenv hehe
hehe=wo shi yige zhi
=>
删除一个变量
setenv [-f] name
- [forcibly] delete environment variable 'name'
=> setenv hehe
=> printenv hehe
## Error: "hehe" not defined
=>
saveenv
=> help saveenv
saveenv - save environment variables to persistent storage
Usage:
saveenv
这个简单,测试一下
=> saveenv
Saving Environment to MMC...
common/env_mmc.c,saveenv,line=161:emmc env
Writing to MMC(1)... done
=>
setenv设置的环境变量只会保存在DDR中,saveenv以后才会保存到emmc中。上面的信息中 common/env_mmc.c这一行是我加的调试信息。
二 printenv源码分析
1 printenv定义
Nvedit.c (cmd)
U_BOOT_CMD_COMPLETE(
printenv, CONFIG_SYS_MAXARGS, 1, do_env_print,
"print environment variables",
"[-a]\n - print [all] values of all environment variables\n"
"printenv name ...\n"
" - print value of environment variable 'name'",
var_complete
);
do_env_print还是print的命令的执行函数,这就解释了为什么print和printenv的执行结果一致。
U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""),
2 printenv执行函数do_env_print
Nvedit.c (cmd)
static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
int i;
int rcode = 0;
int env_flag = H_HIDE_DOT;
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'a') {
argc--;
argv++;
env_flag &= ~H_HIDE_DOT;
}
if (argc == 1) {
/* print all env vars */
rcode = env_print(NULL, env_flag);
if (!rcode)
return 1;
printf("\nEnvironment size: %d/%ld bytes\n",
rcode, (ulong)ENV_SIZE);
return 0;
}
/* print selected env vars */
env_flag &= ~H_HIDE_DOT;
for (i = 1; i < argc; ++i) {
int rc = env_print(argv[i], env_flag);
if (!rc) {
printf("## Error: \"%s\" not defined\n", argv[i]);
++rcode;
}
}
return rcode;
}
上面函数有一个宏ENV_SIZE,这个宏的定义如下所示
#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
ENV_HEADER_SIZE宏定义如下,它的值显然是4。
# define ENV_HEADER_SIZE (sizeof(uint32_t))
在代码添加了打印信息如下所示,输出结果确实是8192和4。
printf("CONFIG_ENV_SIZE=%d,ENV_HEADER_SIZE=%d",CONFIG_ENV_SIZE,ENV_HEADER_SIZE);
do_env_print函数又调用了env_print,env_print(Nvedit.c (cmd))的源码如下,当传递给name的值是NULL时,打印全部的环境变量,当name值不为空时,打印name值对应的单一的环境变量,上面已经测试过用法了。
/*
* Command interface: print one or all environment variables
*
* Returns 0 in case of error, or length of printed string
*/
static int env_print(char *name, int flag)
{
char *res = NULL;
ssize_t len;
if (name) { /* print a single name */
ENTRY e, *ep;
e.key = name;
e.data = NULL;
hsearch_r(e, FIND, &ep, &env_htab, flag);
if (ep == NULL)
return 0;
len = printf("%s=%s\n", ep->key, ep->data);
return len;
}
/* print whole list */
len = hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL);
if (len > 0) {
puts(res);
free(res);
return len;
}
/* should never happen */
printf("## Error: cannot export environment\n");
return 0;
}
3 env_print中调用了hash算法
env_print函数中又调用了hsearch_r(Hashtable.c (lib))和hexport_r(Hashtable.c (lib)
这两个都是算法相关,下一阶段写几篇关于算法的学习记录。
1 hsearch_r只打印一个
hs就是哈希,哈希这里貌似是一个哈希查找表。
hsearch_r(e, FIND, &ep, &env_htab, flag);
int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
struct hsearch_data *htab, int flag)
hsearch_r函数很长,现在这里只知道它是哈希查找就行了,后面如果需要的时候再来分析系它。
2 hexport_r打印全部
hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL);
3 env_htab(Env_common.c (common) )
这里两个函数都调用了全局变量env_htab(Env_common.c (common) )
/* Data type for reentrant functions. */
struct hsearch_data env_htab = {
.change_ok = env_flags_validate,
};
/*
* Family of hash table handling functions. The functions also
* have reentrant counterparts ending with _r. The non-reentrant
* functions all work on a signle internal hashing table.
*/
/* Data type for reentrant functions. */
struct hsearch_data {
struct _ENTRY *table;
unsigned int size;
unsigned int filled;
/*
* Callback function which will check whether the given change for variable
* "item" to "newval" may be applied or not, and possibly apply such change.
* When (flag & H_FORCE) is set, it shall not print out any error message and
* shall force overwriting of write-once variables.
.* Must return 0 for approval, 1 for denial.
*/
int (*change_ok)(const ENTRY *__item, const char *newval, enum env_op,
int flag);
};
三 setenv源码分析
1 setenv定义
U_BOOT_CMD_COMPLETE(
setenv, CONFIG_SYS_MAXARGS, 0, do_env_set,
"set environment variables",
"[-f] name value ...\n"
" - [forcibly] set environment variable 'name' to 'value ...'\n"
"setenv [-f] name\n"
" - [forcibly] delete environment variable 'name'",
var_complete
);
2 setenv执行函数
static int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
if (argc < 2)
return CMD_RET_USAGE;
return _do_env_set(flag, argc, argv, H_INTERACTIVE);
}
_do_env_set函数
static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
{
int i, len;
char *name, *value, *s;
ENTRY e, *ep;
debug("Initial value for argc=%d\n", argc);
while (argc > 1 && **(argv + 1) == '-') {
char *arg = *++argv;
--argc;
while (*++arg) {
switch (*arg) {
case 'f': /* force */
env_flag |= H_FORCE;
break;
default:
return CMD_RET_USAGE;
}
}
}
debug("Final value for argc=%d\n", argc);
name = argv[1];
value = argv[2];
if (strchr(name, '=')) {
printf("## Error: illegal character '='"
"in variable name \"%s\"\n", name);
return 1;
}
env_id++;
/* Delete only ? */
if (argc < 3 || argv[2] == NULL) {
int rc = hdelete_r(name, &env_htab, env_flag);
return !rc;
}
/*
* Insert / replace new value
*/
for (i = 2, len = 0; i < argc; ++i)
len += strlen(argv[i]) + 1;
value = malloc(len);
if (value == NULL) {
printf("## Can't malloc %d bytes\n", len);
return 1;
}
for (i = 2, s = value; i < argc; ++i) {
char *v = argv[i];
while ((*s++ = *v++) != '\0')
;
*(s - 1) = ' ';
}
if (s != value)
*--s = '\0';
e.key = name;
e.data = value;
hsearch_r(e, ENTER, &ep, &env_htab, env_flag);
free(value);
if (!ep) {
printf("## Error inserting \"%s\" variable, errno=%d\n",
name, errno);
return 1;
}
return 0;
}
这脸调用了hdelete_r来删除环境变量,
从代码可以看出,新的节点被构造成结构体ENTRY
typedef struct entry {
const char *key;
char *data;
int (*callback)(const char *name, const char *value, enum env_op op,
int flags);
int flags;
} ENTRY;
这个地方也放到算法里再去分析。
四 saveenv源码分析
1 saveenv定义
U_BOOT_CMD(
saveenv, 1, 0, do_env_save,
"save environment variables to persistent storage",
""
);
2 saveenv执行函数
static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
printf("Saving Environment to %s...\n", env_name_spec);
return saveenv() ? 1 : 0;
}
经过添加调试信息,找到saveenv()的函数原型在common/env_mmc.c文件中,
int saveenv(void)
{
ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
int dev = mmc_get_env_dev();
struct mmc *mmc = find_mmc_device(dev);
u32 offset;
int ret, copy = 0;
const char *errmsg;
debug("dev = %d\n",dev);
errmsg = init_mmc_for_env(mmc);
if (errmsg) {
printf("%s\n", errmsg);
return 1;
}
ret = env_export(env_new);
if (ret)
goto fini;
#ifdef CONFIG_ENV_OFFSET_REDUND
env_new->flags = ++env_flags; /* increase the serial */
if (gd->env_valid == 1)
copy = 1;
#endif
if (mmc_get_env_addr(mmc, copy, &offset)) {
ret = 1;
goto fini;
}
printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
puts("failed\n");
ret = 1;
goto fini;
}
puts("done\n");
ret = 0;
#ifdef CONFIG_ENV_OFFSET_REDUND
gd->env_valid = gd->env_valid == 2 ? 1 : 2;
#endif
fini:
fini_mmc_for_env(mmc);
return ret;
}
这里面调用了函数write_env()写外存
static inline int write_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
{
uint blk_start, blk_cnt, n;
blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
debug("blk_start = %d,blk_cnt=%d",blk_start ,blk_cnt);
n = mmc->block_dev.block_write(&mmc->block_dev, blk_start,
blk_cnt, (u_char *)buffer);
return (n == blk_cnt) ? 0 : -1;
}
我在其中加了一行打印信息,打印起始块和数量,执行结果如下所示
common/env_mmc.c,write_env,line=145:blk_start = 1536,blk_cnt=16
CONFIG_ENV_OFFSET 环境变量的地址
这个起始块地址值是从哪里来的呢,肯定是可以配置的吧,在函数mmc_get_env_addr中找到了,
__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
{
s64 offset;
offset = CONFIG_ENV_OFFSET;
#ifdef CONFIG_ENV_OFFSET_REDUND
debug("CONFIG_ENV_OFFSET_REDUND is defined copy = %d,CONFIG_ENV_OFFSET_REDUND=%d",copy,CONFIG_ENV_OFFSET_REDUND);
if (copy){
offset = CONFIG_ENV_OFFSET_REDUND;
#else
debug("CONFIG_ENV_OFFSET_REDUND is not defined");
#endif
debug("offset = %d,mmc->capacity=%d",offset,mmc->capacity);
if (offset < 0)
offset += mmc->capacity;
*env_addr = offset;
debug("CONFIG_ENV_OFFSET = %x",CONFIG_ENV_OFFSET);
return 0;
}
原来函数没有这么长,我加了一堆的打印信息。输出结果如下所示
common/env_mmc.c,mmc_get_env_addr,line=56:CONFIG_ENV_OFFSET_REDUND is not defined
common/env_mmc.c,mmc_get_env_addr,line=58:offset = 786432,mmc->capacity=0
common/env_mmc.c,mmc_get_env_addr,line=63:CONFIG_ENV_OFFSET = c0000
看到这个宏,它的值是0xc0000,等于786432,然后除以512(扇区大小)等于1536,1536就是十六进制的0x600,所如果我们选项修改环境变量的存储位置,就修改CONFIG_ENV_OFFSET宏就可以了。
五 环境变量的初始化
Env_default.h (include) 在这个文件中,可以修改默认的IP地址
#ifndef CONFIG_IPADDR
#define CONFIG_IPADDR 192.168.0.4
#endif
知道改IP地址就够了,剩下的内容,不看也罢,都是对代码的意淫而已。
1 env_init函数
不需要知道env_init()函数到底是执行的哪儿文件中定义的,因为他们做的事情都一样,都是两行代码,所以关于找env_init的内容可以略过啦。
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
寻找env_init
它被赋值在static init_fnc_t init_sequence_f[]中,init_sequence_f数组是第一阶段初始化的数组,所以env_init是第一阶段初始化的函数。肯定还有第二阶段初始化的函数,为什么呢?从这个简短的代码和前面的分析可知,还需要加载用户配置过的环境变量
int env_init(void)
{
/* use default */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
debug("gd->env_addr = %p",gd->env_addr);
return 0;
}
default_environment是一个字符数组,一个全局变量,我可以在其中添加自己的环境的默认值,例如设置默认的IP地址,bootdelay值,bootcmd、bootargs等等。
记得加上调试宏,为什么加它呢,这可以保证你的信息肯定是能输出的,可以定制打印信息,打印信息数量还可以在可控的范围内,如果打开全局宏,信息爆炸,就很难到自己的调试信息。
#ifdef debug
#undef debug
#endif
#define debug(format,...) printf("%s,%s,line=%d:"format"\n",__FILE__,__func__,__LINE__,##__VA_ARGS__)
我在这里添加的调试函数没有输出,但是我不确定是不是这个初始化函数怎么办,有可能执行到这里的时候,串口还没初始化呢,所以呢,在这里写一个全局变量,然后让另一个地方去输出。mmc_get_env_addr,就在这里吧,
static int init_flag = 0;
__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
{
/*略*/
if(init_flag){
debug("env_init is run");
}else{
debug("env_init is not run");
}
return 0;
}
int env_init(void)
{
/*略*/
init_flag = 1;
}
编译执行,看看效果,结果完全出乎我的意料,两种可能
1)被执行的env_init可能不是我们找到的这个。
2)env_init可能就没有被执行。
common/env_mmc.c,mmc_get_env_addr,line=68:env_init is not run
再找一个env_init
分析有误,开始大范围添加调试信息,测试的时候发现,我添加了一堆的调试信息后,以往的信息也不见的,只剩下u-boot本来自己输出的调试信息了,好诡异。我猜可能是我乱加宏的关系,去掉刚才加的debug宏,没找到原因make clean了一下,重新编译好了。还是没有env_init被执行的迹象。常规手段在这里好像不灵了,分析,可能执行到这里的时候,内存没有被正常初始化,在这里属于汇编的阶段。串口也没有被初始化,那么问题来了,这里调用gd指针啦,好家伙,矛盾啊,看看gd指针是个啥神仙,
#ifdef __clang__
#define DECLARE_GLOBAL_DATA_PTR
#define gd get_gd()
static inline gd_t *get_gd(void)
{
gd_t *gd_ptr;
#ifdef CONFIG_ARM64
/*
* Make will already error that reserving x18 is not supported at the
* time of writing, clang: error: unknown argument: '-ffixed-x18'
*/
__asm__ volatile("mov %0, x18\n" : "=r" (gd_ptr));
#else
__asm__ volatile("mov %0, r9\n" : "=r" (gd_ptr));
#endif
return gd_ptr;
}
#else
#ifdef CONFIG_ARM64
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("x18")
#else
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
#endif
#endif
好家伙,首先它是个指针,它竟然是定义在寄存器中的指针,我的芯片是32位的,所以它肯定是
r9不是x18。知识不够用啊。算了,只能用魔法打败魔法啦。
在board_f中加了一个函数,然后在mmc_get_env_addr中调用这个函数,发现执行的就是上面分析的这个common/env_mmc.c,里面的env_init。结束,到此结束。
void a_test_function(void){
env_init();
}
定义一个自己的变量在r8寄存器试试
static inline int *get_init_flag(void)
{
int *ptr;
__asm__ volatile("mov %0, r8\n" : "=r" (ptr));
return ptr;
}
#define init_flag get_init_flag()
然后输出它的地址,看看它是什么,同时也输出gd的地址看看,
init_flag = 9ffb8794 gd = 9ef70ee8
分析一下mov指令,move x1 x2,这的意思是,将x2的值放到x1里,那么__asm__ volatile("mov %0, r8\n" : "=r" (ptr));它的含义应该就是将r8的值放到ptr里,gd是这样赋值的,r9可能提前被初始化过,经过一番折腾,r9真的在别的地方初始化过。
crt0.S (arch\arm\lib)
mov r0, sp
bl board_init_f_alloc_reserve
mov sp, r0
/* set up gd here, outside any C code */
mov r9, r0
bl board_init_f_init_reserve
虽然看不懂,里面都两个函数
Board_init.c (common\init)
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
top -= CONFIG_SYS_MALLOC_F_LEN;
#endif
/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
top = rounddown(top-sizeof(struct global_data), 16);
return top;
}
#define roundup(x, y) ( \
{ \
const typeof(y) __y = y; \
(((x) + (__y - 1)) / __y) * __y; \
} \
)
#define rounddown(x, y) ( \
{ \
typeof(x) __x = (x); \
__x - (__x % (y)); \
} \
)
void board_init_f_init_reserve(ulong base)
{
struct global_data *gd_ptr;
#ifndef _USE_MEMCPY
int *ptr;
#endif
/*
* clear GD entirely and set it up.
* Use gd_ptr, as gd may not be properly set yet.
*/
gd_ptr = (struct global_data *)base;
/* zero the area */
#ifdef _USE_MEMCPY
memset(gd_ptr, '\0', sizeof(*gd));
#else
for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
*ptr++ = 0;
#endif
/* set GD unless architecture did it already */
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
/* next alloc will be higher by one GD plus 16-byte alignment */
base += roundup(sizeof(struct global_data), 16);
/*
* record early malloc arena start.
* Use gd as it is now properly set for all architectures.
*/
#if defined(CONFIG_SYS_MALLOC_F)
/* go down one 'early malloc arena' */
gd->malloc_base = base;
/* next alloc will be higher by one 'early malloc arena' size */
base += CONFIG_SYS_MALLOC_F_LEN;
#endif
}
(⊙o⊙)…假装忘记发生了什么,继续下面的话题。
gd指针没有得到寄存器?
这个地址范围,好像是DDR内存中的啊,bdinfo看看,DRAM地址从8000 0000开始,长度是2000 0000,那么这两个地址就在DRAM地址范围内的,就可以说明gd没有得到寄存器吗?(⊙o⊙)…
=> bdinfo
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
eth0name = FEC0
ethaddr = 6c:30:45:2f:91:d7
current eth = FEC0
ip_addr = 192.168.0.3
baudrate = 115200 bps
TLB addr = 0x9FFF0000
relocaddr = 0x9FF73000
reloc off = 0x18773000
irq_sp = 0x9EF70ED0
sp start = 0x9EF70EC0
=>
2 第二阶段初始化函数initr_env
Board_r.c (common):static int initr_env(void)
initr_env函数在哪里找到的呢,在数组init_sequence_r中。
这就是第二阶段的初始化函数了。在此函数执行之前,内存中已经了有了一份默认的环境变量,如果是第一次启动,外存中没有存储过环境变量,则将当前内存中的环境变量保存到外存中,下次启动时,在第二阶段初始化时,就可以成功加载保存在外存中的环境变量了。
在下面的代码中,通过should_load_env函数判断外存是否有环境变量,如果有,则使用env_relocate将外存的值加载到内存,根据名字,它使用了重定位机制,等会通过这个函数分析,想知道环境变量存到哪里了,看saveenv函数就知道了。
static int initr_env(void)
{
/* initialize environment */
if (should_load_env())
env_relocate();
else
set_default_env(NULL);
#ifdef CONFIG_OF_CONTROL
setenv_addr("fdtcontroladdr", gd->fdt_blob);
#endif
/* Initialize from environment */
load_addr = getenv_ulong("loadaddr", 16, load_addr);
#if defined(CONFIG_SYS_EXTBDINFO)
#if defined(CONFIG_405GP) || defined(CONFIG_405EP)
#if defined(CONFIG_I2CFAST)
/*
* set bi_iic_fast for linux taking environment variable
* "i2cfast" into account
*/
{
char *s = getenv("i2cfast");
if (s && ((*s == 'y') || (*s == 'Y'))) {
gd->bd->bi_iic_fast[0] = 1;
gd->bd->bi_iic_fast[1] = 1;
}
}
#endif /* CONFIG_I2CFAST */
#endif /* CONFIG_405GP, CONFIG_405EP */
#endif /* CONFIG_SYS_EXTBDINFO */
return 0;
}
六 一个grep使用技巧
现在,假如你忘记了,默认的IP地址是在哪里修改的,假如一年不碰这个代码,并且也没有记笔记,大概率是会忘记的。然后测试员高告诉你,你的板子的默认IP地址是192.168.0.3,好了,这就够了,在uboot下载执行这个命令
$ grep -nR 192.168.0.4 *
如下所示,它匹配到了几个文件,我的运气不错,其中只有一个.h文件,那肯定就是它了,如果不放心,修改一下就行了。从这个搜索结果中还可以看出,grep还能在二进制文件中搜索。这也说明了,IP地址在uboot.bin中同样是以字符串形式存在的,如果用hex工具,肯定能看到它,这里就不试了。
$ grep -nR 192.168.0.4 *
匹配到二进制文件 common/built-in.o
匹配到二进制文件 common/env_common.o
examples/standalone/README.smc91111_eeprom:168:BOOT> ping 192.168.0.4
examples/standalone/README.smc91111_eeprom:171:host 192.168.0.4 is alive
include/env_default.h:62:#define CONFIG_IPADDR 192.168.0.4
匹配到二进制文件 u-boot
匹配到二进制文件 u-boot.bin
匹配到二进制文件 u-boot.imx
匹配到二进制文件 u-boot-nodtb.bin
$
七 其他函数
envmatch函数
/*
* Match a name / name=value pair
*
* s1 is either a simple 'name', or a 'name=value' pair.
* i2 is the environment index for a 'name2=value2' pair.
* If the names match, return the index for the value2, else -1.
*/
int envmatch(uchar *s1, int i2)
{
if (s1 == NULL)
return -1;
while (*s1 == env_get_char(i2++))
if (*s1++ == '=')
return i2;
if (*s1 == '\0' && env_get_char(i2-1) == '=')
return i2;
return -1;
}
getenv函数
/*
* Look up variable from environment,
* return address of storage for that variable,
* or NULL if not found
*/
char *getenv(const char *name)
{
if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */
ENTRY e, *ep;
WATCHDOG_RESET();
e.key = name;
e.data = NULL;
hsearch_r(e, FIND, &ep, &env_htab, 0);
return ep ? ep->data : NULL;
}
/* restricted capabilities before import */
if (getenv_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) > 0)
return (char *)(gd->env_buf);
return NULL;
}