您的位置:首页 > 其它

UBOOT之源码分析——初始化环境变量

2014-09-17 10:54 316 查看
仅对x4412从emmc引导的环境变量执行处理过程进行分析,从uboot的print命令打印环境变量开始逆向分析源码到板级初始化函数board_init_r的环境变量初始化函数。

我们在u-boot命令行中输入print会得到所有的环境变量,接下来我们来分析这些环境变量都是在哪里实现的。
先看看common/cmd_nvedit.c文件中定义的print命令:
U_BOOT_CMD(

printenv,CONFIG_SYS_MAXARGS, 1, do_env_print,

"print environmentvariables",

"\n - print values of all environmentvariables\n"

"printenv name ...\n"

" - print value of environment variable 'name'"

);
宏中定义print打印执行的函数do_env_print:

int do_env_print (cmd_tbl_t *cmdtp, int flag, int argc, char * constargv[])

{

int i;

int rcode = 0;

if (argc == 1){

/* print all env vars */

rcode = env_print(NULL);

if(!rcode)

return 1;
printf("\nEnvironmentsize: %d/%ld bytes\n",

rcode, (ulong)ENV_SIZE);

return 0;

}
/*print selected env vars */

for (i = 1; i < argc; ++i){

int rc = env_print(argv[i]);

if (!rc) {

printf("##Error: \"%s\" not defined\n",argv[i]);

++rcode;

}

}

return rcode;

}

打印一个或所有环境变量的命令接口:

static int env_print(char *name)
{
char*res = NULL;
size_tlen;

if(name) { /* print a single name */
ENTRYe, *ep;

e.key= name;
e.data= NULL;
ep= hsearch (e, FIND);
if(ep == NULL)
return0;
len= printf ("%s=%s\n", ep->key, ep->data);
return len;
}

/*print whole list */
len= hexport('\n', &res, 0);

if(len > 0) {
puts(res);
free(res);
return len;
}

/*should never happen */
return0;
}

其中函数env_print导出存储在哈希表中的线性形式的数据,在lib/hashtable.c中实现

ssize_th export(const char sep, char **resp, size_t size)

{

return hexport_r(&htab, sep, resp, size);

}
ssize_th export_r(struct hsearch_data *htab, const char sep, char **resp,size_t size)

{
ENTRY*list[htab->size];

char *res, *p;

size_t totlen;

inti, n;
/*Test for correct arguments. */

if ((resp == NULL) || (htab ==NULL)) {

__set_errno(EINVAL);

return (-1);

}
debug("EXPORT table = %p, htab.size = %d, htab.filled = %d, size = %d\n",

htab,htab->size, htab->filled, size);
/*

* Pass 1:

* search used entries,

* save addresses andcompute total length

*/
for(i = 1, n = 0, totlen = 0; i <= htab->size; ++i) {

if(htab->table[i].used) {

ENTRY *ep =&htab->table[i].entry;
list[n++]= ep;
totlen+= strlen(ep->key) + 2;
if(sep == '\0') {

totlen += strlen(ep->data);

} else{ /* check if escapes are needed */

char *s = ep->data;
while(*s) {

++totlen;

/* add room for needed escapechars */

if ((*s == sep) || (*s =='\\'))

++totlen;

++s;

}

}

totlen+= 2; /* for '=' and 'sep' char */

}

}

#ifdef DEBUG

/* Pass 1a: print unsorted list */

printf("Unsorted:n=%d\n", n);

for (i = 0; i < n; ++i) {

printf("\t%3d:%p ==> %-10s => %s\n",

i, list[i],list[i]->key, list[i]->data);

}
#endif
/*Sort list by keys */

qsort(list, n, sizeof(ENTRY *), cmpkey);
/*Check if the user supplied buffer size is sufficient */

if(size) {

if (size < totlen + 1) { /* provided buffer toosmall */

debug("### buffer too small: %d, but need%d\n",

size, totlen +1);

__set_errno(ENOMEM);

return (-1);

}

}else {

size = totlen + 1;

}
/*Check if the user provided a buffer */

if (*resp) {

/*yes; clear it */

res = *resp;

memset(res, '\0',size);

} else {

/* no, allocate and clear one */

*resp= res = calloc(1, size);

if (res == NULL){

__set_errno(ENOMEM);

return (-1);

}

}
/*

* Pass 2:

* export sorted list of resultdata将结构体htab中的table结构体中的entry项的地址给ep然后把ep的地址给数组list,在下文打印出list数组

*/

for (i = 0, p = res; i < n; ++i) {

char *s;
s= list[i]->key;

while (*s)

*p++ = *s++;

*p++ ='=';
s= list[i]->data;
while(*s) {

if ((*s == sep) || (*s == '\\'))

*p++ ='\\'; /* escape */

*p++ = *s++;

}

*p++ =sep;

}

*p = '\0'; /* terminate result */
return size;

}
文件中定义了htab变量static struct hsearch_data
htab;这个变量的结构体位于include/search.h
/*重载函数数据类型. */
struc thsearch_data {

struct _ENTRY *table;

unsigned intsize;

unsigned int filled;

};
在lib/hashtable.c文件中定义

typedef struct _ENTRY {
unsignedint used;
ENTRYentry;
}_ENTRY;
//填充这个htab结构体的函数hcreate也在lib/hashtable.c文件中

int hcreate(size_t nel)

{

return hcreate_r(nel,&htab);

}

//函数直接调用了hcreate_r函数。

int hcreate_r(size_t nel, struct hsearch_data *htab)

{
/*Test for correct arguments. */

if (htab == NULL){

__set_errno(EINVAL);

return 0;

}
/*There is still another table active. Return with error. */

if(htab->table != NULL)

return 0;
/*Change nel to the first prime number not smaller as nel. */

nel|= 1; /* make odd */

while (!isprime(nel))

nel += 2;
htab->size= nel;

htab->filled = 0;
/*allocate memory and zero out */

htab->table = (_ENTRY *)calloc(htab->size + 1, sizeof(_ENTRY));

if (htab->table ==NULL)

return 0;
/*everything went alright */

return 1;

}
在目前这个版本源码中没有使用hcreate,而是“将线性化的数据填充到到哈希表”的函数himport_r调用,而源码中himport_r是由himport直接调用实现。这两个函数依然在lib/hashtable.c文件中
int himport(const char *env, size_t size, const char sep, intflag)

{

return himport_r(&htab, env, size, sep,flag);

}
int himport_r(struct hsearch_data *htab, const char *env, size_t size,const char sep, int flag)

{
char*data, *sp, *dp, *name, *value;
/*Test for correct arguments. */

if (htab == NULL){

__set_errno(EINVAL);

return 0;

}
/*we allocate new space to make sure we can write to the array */

if((data = malloc(size)) == NULL) {

debug("himport_r: can'tmalloc %d bytes\n", size);

__set_errno(ENOMEM);

return 0;

}
memcpy(data,env, size);

dp = data;
if((flag & H_NOCLEAR) == 0) {

/* Destroy old hash table ifone exists */

debug("Destroy Hash Table: %p table = %p\n",htab,

htab->table);

if(htab->table)

hdestroy_r(htab);

}

/*

* Create new hash table (if needed). The computation of the hash

* table size is based on heuristics: in a sample of some 70+

*existing systems we found an average size of 39+ bytes per entry

* in the environment (for the whole key=value pair). Assuming a

* size of 8 per entry (= safety factor of ~5) should provideenough

* safety margin for any existing environment definitionsand still

* allow for more than enough dynamic additions. Notethat the

* "size" argument is supposed to give themaximum enviroment size

* (CONFIG_ENV_SIZE). This heuristicswill result in

* unreasonably large numbers (and thus memoryfootprint) for

* big flash environments (>8,000 entries for64 KB

* envrionment size), so we clip it to a reasonablevalue.

* On the other hand we need to add some more entries forfree

* space when importing very small buffers. Both boundariescan

* be overwritten in the board config file if needed.

*/
if(!htab->table) {
intnent = CONFIG_ENV_MIN_ENTRIES + size / 8;
if(nent > CONFIG_ENV_MAX_ENTRIES)

nent =CONFIG_ENV_MAX_ENTRIES;
debug("CreateHash Table: N=%d\n", nent);
if(hcreate_r(nent, htab) == 0) {

free(data);

return 0;

}

}
/*Parse environment; allow for '\0' and 'sep' as separators */

do{

ENTRY e, *rv;
/*skip leading white space */

while ((*dp == ' ') || (*dp =='\t'))

++dp;
/*skip comment lines */

if (*dp == '#') {

while (*dp &&(*dp != sep))

++dp;

++dp;

continue;

}
/*parse name */

for (name = dp; *dp != '=' && *dp &&*dp != sep; ++dp)
;
/*deal with "name" and "name=" entries (delete var)*/
if(*dp == '\0' || *(dp + 1) == '\0' ||

*dp == sep || *(dp +1) == sep) {

if (*dp == '=')
*dp++= '\0';

*dp++ = '\0'; /* terminate name */
debug("DELETECANDIDATE: \"%s\"\n", name);
if(hdelete_r(name, htab) == 0)

debug("DELETE ERROR##############################\n");
continue;

}
*dp++= '\0'; /* terminate name */
/*parse value; deal with escapes */
for(value = sp = dp; *dp && (*dp != sep); ++dp) {

if((*dp == '\\') && *(dp + 1))

++dp;

*sp++ =*dp;

}
*sp++= '\0'; /* terminate value */

++dp;
/*enter into hash table */

e.key = name;

e.data = value;
hsearch_r(e,ENTER, &rv, htab);

if (rv == NULL) {

printf("himport_r:can't insert \"%s=%s\" into hash table\n",

name,value);

return 0;

}
debug("INSERT:table %p, filled %d/%d rv %p ==> name=\"%s\"value=\"%s\"\n",htab, htab->filled,htab->size,rv, name, value);
}while ((dp < data + size) && *dp); /* size check neededfor text */
/*without '\0' termination */
debug("INSERT:free(data = %p)\n", data);

free(data);
debug("INSERT:done\n");

return 1; /* everything OK */

}

那么这个环境变量哈希表数据是由common/env_common.c中的env_import函数操作,
而common/cmd_nvedit.c文件中的do_env_import函数来操作则用来实现”import”命令。
int env_import(const char *buf, int check)

{

env_t*ep = (env_t *)buf;

if(check) {

uint32_tcrc;

memcpy(&crc,&ep->crc, sizeof(crc));

if(crc32(0, ep->data, ENV_SIZE) != crc) {

set_default_env("!badCRC");

return0;

}

}

if(himport((char *)ep->data, ENV_SIZE, '\0', 0)) {

gd->flags|= GD_FLG_ENV_READY;

return1;

}

error("Cannotimport environment: errno = %d\n", errno);

set_default_env("!importfailed");

return0;

}

最后跟踪到common/env_auto.c文件中use_default,env_relocate_spec_onenand和env_relocate_spec_movinand函数。这三个函数分别不同的引导设备进行环境变量填充。

我们再回到板级第二阶段初始化函数board_init_r,其中就调用了环境变量初始化函数env_relocate()(在common/env_common.c文件中);而env_relocate()函数调用了env_relocate_spec函数(common/env_auto.c文件),然后env_relocate_spec根据引导设备判断使用use_default,env_relocate_spec_onenand和env_relocate_spec_movinand这三个函数中的其中一个进行环境变量初始化。我们这个开发板中使用的是EMMC,并且在x4412.h中定义BOOT_EMMC的值为6,因此本例中由env_relocate_spec_movinand函数对环境变量进行初始化填充。具体函数源代码在此就不一一列出。

具体看下env_relocate_spec_movinand函数:
void env_relocate_spec_movinand(void)
{
#if!defined(ENV_IS_EMBEDDED)
uint*magic = (uint*)(PHYS_SDRAM_1);
movi_read_env(virt_to_phys((ulong)env_ptr));
if(crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
returnuse_default();
env_import((constchar *)env_ptr, 1);
#endif/* ! ENV_IS_EMBEDDED */
}

这里如果保存在environment[]环境变量空间中的数据为不可用数据(CRC32校验错误)。则装载默认环境变量填充。我们看下默认环境变量default_environment的定义(在common/env_common.c文件中):

uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS

"bootargs=" CONFIG_BOOTARGS "\0"

#endif
#ifdef CONFIG_BOOTCOMMAND

"bootcmd=" CONFIG_BOOTCOMMAND "\0"

#endif
#ifdef CONFIG_RAMBOOTCOMMAND

"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"

#endif
#ifdef CONFIG_NFSBOOTCOMMAND

"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"

#endif
#ifdefined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >=0)

"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"

#endif
#ifdefined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >=0)

"baudrate=" MK_STR(CONFIG_BAUDRATE) "\0"

#endif
#ifdef CONFIG_LOADS_ECHO

"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"

#endif
#ifdef CONFIG_ETHADDR

"ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"

#endif
#ifdef CONFIG_ETH1ADDR

"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"

#endif
#ifdef CONFIG_ETH2ADDR

"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"

#endif
#ifdef CONFIG_ETH3ADDR

"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"

#endif
#ifdef CONFIG_ETH4ADDR

"eth4addr=" MK_STR(CONFIG_ETH4ADDR) "\0"

#endif
#ifdef CONFIG_ETH5ADDR

"eth5addr=" MK_STR(CONFIG_ETH5ADDR) "\0"

#endif
#ifdef CONFIG_IPADDR

"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"

#endif
#ifdef CONFIG_SERVERIP

"serverip=" MK_STR(CONFIG_SERVERIP) "\0"

#endif
#ifdef CONFIG_SYS_AUTOLOAD

"autoload=" CONFIG_SYS_AUTOLOAD "\0"

#endif
#ifdef CONFIG_PREBOOT

"preboot=" CONFIG_PREBOOT "\0"

#endif
#ifdef CONFIG_ROOTPATH

"rootpath=" MK_STR(CONFIG_ROOTPATH) "\0"

#endif
#ifdef CONFIG_GATEWAYIP

"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0"

#endif
#ifdef CONFIG_NETMASK

"netmask=" MK_STR(CONFIG_NETMASK) "\0"

#endif
#ifdef CONFIG_HOSTNAME

"hostname=" MK_STR(CONFIG_HOSTNAME) "\0"

#endif
#ifdef CONFIG_BOOTFILE

"bootfile=" MK_STR(CONFIG_BOOTFILE) "\0"

#endif
#ifdef CONFIG_LOADADDR

"loadaddr=" MK_STR(CONFIG_LOADADDR) "\0"

#endif
#ifdef CONFIG_CLOCKS_IN_MHZ

"clocks_in_mhz=1\0"

#endif
#ifdefined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY >0)

"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"

#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS

CONFIG_EXTRA_ENV_SETTINGS

#endif
"\0"

};
int default_environment_size = sizeof(default_environment);
默认环境变量参数在配置文件include/configs/x4412.h中定义
/*

* BOOTP options

*/
#defineCONFIG_BAUDRATE 115200

#define CONFIG_BOOTP_SUBNETMASK

#defineCONFIG_BOOTP_GATEWAY

#define CONFIG_BOOTP_HOSTNAME

#defineCONFIG_BOOTP_BOOTPATH
#defineCONFIG_ETHADDR 00:40:5c:26:0a:5b

#define CONFIG_NETMASK 255.255.255.0

#define CONFIG_IPADDR 192.168.0.20

#defineCONFIG_SERVERIP 192.168.0.10

#defineCONFIG_GATEWAYIP 192.168.0.1
#defineCONFIG_BOOTDELAY 3

/* Default boot commands for Android booting.*/

#define CONFIG_BOOTCOMMAND "movi read kernel 040008000;movi read rootfs 0 41000000 400000;bootm 4000800041000000"

#define CONFIG_BOOTARGS "lcd=vs070cxntp=ft5x06-1024x600 cam=ov2655 mac=00:09:c0:ff:ee:58"

#defineCONFIG_BOOTARGS_QT "root=/dev/mmcblk0p2 rw rootfstype=ext4lcd=vs070cxn tp=ft5x06-1024x600 cam=ov2655mac=00:09:c0:ff:ee:58"

#define CONFIG_BOOTCOMMAND2 "fdisk-c 0;sdfuse flashall;reset"

#defineCONFIG_BOOTCOMMAND3 "ext3format mmc 0:3;ext3format mmc0:4;" \

"movi read kernel 0 40008000;movi readrootfs 0 41000000 100000;bootm 40008000 41000000"
#defineCONFIG_BOOTCOMMAND_QT "movi read kernel 0 40008000;bootm40008000"

则默认的环境变量为

bootargs=lcd=vs070cxn tp=ft5x06-1024x600 cam=ov2655 mac=00:09:c0:ff:ee:58

bootcmd=moviread kernel 0 40008000;movi read rootfs 0 41000000 400000;bootm40008000 41000000

bootdelay=3

baudrate=115200

ethaddr=00:40:5c:26:0a:5b

ipaddr=192.168.0.20

serverip=192.168.0.10

gatewayip=192.168.0.1

netmask=255.255.255.0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: