仅对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