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

    仅对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 */
}

  1. 那么这个环境变量哈希表数据是由common/env_common.c中的env_import函数操作,

    common/cmd_nvedit.c文件中的do_env_import函数来操作则用来实现”import”命令。

    int env_import(const char *buf, int check)

  2. {

  3. env_t*ep = (env_t *)buf;

  4. if(check) {

  5. uint32_tcrc;

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

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

  8. set_default_env("!badCRC");

  9. return0;

  10. }

  11. }

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

  13. gd->flags|= GD_FLG_ENV_READY;

  14. return1;

  15. }


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

  17. set_default_env("!importfailed");

  18. return0;

  19. }


最后跟踪到common/env_auto.c文件中use_defaultenv_relocate_spec_onenandenv_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_defaultenv_relocate_spec_onenandenv_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 */

}

  1. 这里如果保存在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"


则默认的环境变量为

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

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

  3. bootdelay=3

  4. baudrate=115200

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

  6. ipaddr=192.168.0.20

  7. serverip=192.168.0.10

  8. gatewayip=192.168.0.1

  9. netmask=255.255.255.0

你可能感兴趣的:(linux内核,ARM,u-boot,uboot,移植,源码)