uboot代码分析8:env命令集实践和相关函数整理

https://ke.qq.com/course/4032547?flowToken=1042705

目录

https://ke.qq.com/course/4032547?flowToken=1042705

前言

1 环境变量是什么

2 什么时候使用环境变量

3 环境变量的初始化过程

一 环境变量命令族

printenv

setenv

saveenv

二 printenv源码分析

1 printenv定义

2 printenv执行函数do_env_print

3 env_print中调用了hash算法

三 setenv源码分析

1 setenv定义

2 setenv执行函数

四 saveenv源码分析

1 saveenv定义

2 saveenv执行函数

CONFIG_ENV_OFFSET 环境变量的地址

五 环境变量的初始化

1 env_init函数

寻找env_init

再找一个env_init

定义一个自己的变量在r8寄存器试试

gd指针没有得到寄存器?

2 第二阶段初始化函数initr_env

六 一个grep使用技巧

七 其他函数

envmatch函数

getenv函数


前言

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;
}

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千册

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值