UBOOT——环境变量的实现原理

uboot 环境变量实现原理:

首先我们先要搞清楚uboot中环境变量的作用,uboot中环境变量的作用就是在不改变源码、不用重新编译的情况下,可以使我们通过

设置环境变量的值来改变uboot的一些设置,如bootdelay时间、机器码的值等等。

下面我们来具体看一下uboot中环境变量如何实现

首先看一下环境变量的初始化函数:

  env_init定义在commen/env_movi.c中 函数中实际执行的就是把default_environment的地址赋值给全局变量gd中的env_addr 和env_valid两个值;

  

int env_init(void)
{
#if defined(ENV_IS_EMBEDDED)
   #else /* ENV_IS_EMBEDDED */
    gd->env_addr  = (ulong)&default_environment[0];
    gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED */

    return (0);
}

来看一下default_environment数组

 

 

在来看一下env_relocate  这个函数

重点是一下两句代码

      env_ptr = (env_t *)malloc (CFG_ENV_SIZE); 这句代码作用是给uboot环境变量开辟一块16k大小的内存

      env_relocate_spec ();  这句代码的作用是把sd卡中的uboot环境变量整个分区复制到开辟的这个内存地址处;      

void env_relocate (void)
{
   

#ifdef ENV_IS_EMBEDDED
   #else
    /*
     * We must allocate a buffer for the environment
     */
    env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif

    if (gd->env_valid == 0) {

    }
    else {
        env_relocate_spec ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);


}

通过movi_read_env函数把sd卡中的环境变量复制到内存中

主药用到的是   movi_read_env(virt_to_phys((ulong)env_ptr)); 函数   

        crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc 

        use_default()

三个函数: movi_read_env把sd卡中的uboot的环境变量分区复制到env_ptr中;

     crc32 计算内存中环境变量的crc的值,如果不相等则执行

     use_default函数 

 

void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
    uint *magic = (uint*)(PHYS_SDRAM_1);

    if ((0x24564236 != magic[0]) || (0x20764316 != magic[1]))
        movi_read_env(virt_to_phys((ulong)env_ptr));

    if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
        return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}

 

下面看一下use_default函数的代码:

1:输出*** Warning - bad CRC or moviNAND, using default environment:

2:清0环境变量内存,把default_environment中的值复制到环境变量内存

3:计算crc,写入内存中的crc位

4:设置gd中的env_valid为1;

static void use_default()
{
    puts ("*** Warning - bad CRC or moviNAND, using default environment\n\n");

    if (default_environment_size > CFG_ENV_SIZE){
        puts ("*** Error - default environment is too large\n\n");
        return;
    }

    memset (env_ptr, 0, sizeof(env_t));
    memcpy (env_ptr->data,
            default_environment,
            default_environment_size);
    env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);
    gd->env_valid = 1;
}

这样环境变量初始化算是完成了;

我们在回顾一下环境变量初始化都做了哪些工作:

1:在uboot还没有初始化flash设备(nand movinand 等flash)的时候,先进行环境变量初级初始化,即把gd全局变量中的 env_valid = 1; env_addr 等于全局变量default_enviroment数组的首地址

2:初始化完flash设置以后就要进行环境变量的重定位工作了,即把环境变量从flash中copy到内存中,用一个全局变量env_ptr指向这段内存;复制工作时通过movi_read_env这个函数实现的,实际就是把sd卡中

环境变量分区全部复制到env_ptr指向的这段内存中,然后在对这段内存中的环境变量进行crc校验,如果失败的话,则把default_enviroment中的环境变量复制到这里;

(这里对代码分析env_relocate函数中用malloc设置了一段内存来存放环境变量,一直没有用free来释放这段内存,这里是否存在安全隐患?)

 

------------------------------------------------------------------------------------------------------------

接下来我们在看一下uboot中关于环境变量的一些命令

第一个 printenv 实现函数为do_printfenv

代码如下:

 1 int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 2 {
 3     int i, j, k, nxt;
 4     int rcode = 0;
 5 
 6     if (argc == 1) {        /* Print all env variables    */
 7         for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
 8             for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)
 9                 ;
10             for (k=i; k<nxt; ++k)
11                 putc(env_get_char(k));
12             putc  ('\n');
13 
14             if (ctrlc()) {
15                 puts ("\n ** Abort\n");
16                 return 1;
17             }
18         }
19 
20         printf("\nEnvironment size: %d/%ld bytes\n",
21             i, (ulong)ENV_SIZE);
22 
23         return 0;
24     }
25 
26     for (i=1; i<argc; ++i) {    /* print single env variables    */
27         char *name = argv[i];
28 
29         k = -1;
30 
31         for (j=0; env_get_char(j) != '\0'; j=nxt+1) {
32 
33             for (nxt=j; env_get_char(nxt) != '\0'; ++nxt)
34                 ;
35             k = envmatch((uchar *)name, j);
36             if (k < 0) {
37                 continue;
38             }
39             puts (name);
40             putc ('=');
41             while (k < nxt)
42                 putc(env_get_char(k++));
43             putc ('\n');
44             break;
45         }
46         if (k < 0) {
47             printf ("## Error: \"%s\" not defined\n", name);
48             rcode ++;
49         }
50     }
51     return rcode;
52 }

 

uchar env_get_char_memory (int index)
{
    if (gd->env_valid) {
        return ( *((uchar *)(gd->env_addr + index)) );
    } else {
        return ( default_environment[index] );
    }
}

 

第二个命令:setenv命令对用的函数是do_setenv 命令,实际上调用的是_do_setenv函数

下面我们来分析一下这个函数

int _do_setenv (int flag, int argc, char *argv[])
{
    int   i, len, oldval;
    int   console = -1;
    uchar *env, *nxt = NULL;
    char *name;
    bd_t *bd = gd->bd;

    uchar *env_data = env_get_addr(0);

    if (!env_data)    /* need copy in RAM */
        return 1;

    name = argv[1];

    if (strchr(name, '=')) {
        printf ("## Error: illegal character '=' in variable name \"%s\"\n", name);
        return 1;
    }

    /*
     * search if variable with this name already exists
     */
    oldval = -1;
    for (env=env_data; *env; env=nxt+1) {
        for (nxt=env; *nxt; ++nxt)
            ;
        if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0)
            break;
    }

    /*
     * Delete any existing definition
     */
    if (oldval >= 0) {


        /* Check for console redirection */
        if (strcmp(name,"stdin") == 0) {
            console = stdin;
        } else if (strcmp(name,"stdout") == 0) {
            console = stdout;
        } else if (strcmp(name,"stderr") == 0) {
            console = stderr;
        }

        if (console != -1) {
            if (argc < 3) {        /* Cannot delete it! */
                printf("Can't delete \"%s\"\n", name);
                return 1;
            }

            /* Try assigning specified device */
            if (console_assign (console, argv[2]) < 0)
                return 1;

#ifdef CONFIG_SERIAL_MULTI
            if (serial_assign (argv[2]) < 0)
                return 1;
#endif
        }

        /*
         * Switch to new baudrate if new baudrate is supported
         */
        if (strcmp(argv[1],"baudrate") == 0) {
            int baudrate = simple_strtoul(argv[2], NULL, 10);
            int i;
            for (i=0; i<N_BAUDRATES; ++i) {
                if (baudrate == baudrate_table[i])
                    break;
            }
            if (i == N_BAUDRATES) {
                printf ("## Baudrate %d bps not supported\n",
                    baudrate);
                return 1;
            }
            printf ("## Switch baudrate to %d bps and press ENTER ...\n",
                baudrate);
            udelay(50000);
            gd->baudrate = baudrate;


            serial_setbrg ();
            udelay(50000);
            for (;;) {
                if (getc() == '\r')
                      break;
            }
        }

        if (*++nxt == '\0') {
            if (env > env_data) {
                env--;
            } else {
                *env = '\0';
            }
        } else {
            for (;;) {
                *env = *nxt++;
                if ((*env == '\0') && (*nxt == '\0'))
                    break;
                ++env;
            }
        }
        *++env = '\0';
    }

#ifdef CONFIG_NET_MULTI
    if (strncmp(name, "eth", 3) == 0) {
        char *end;
        int   num = simple_strtoul(name+3, &end, 10);

        if (strcmp(end, "addr") == 0) {
            eth_set_enetaddr(num, argv[2]);
        }
    }
#endif


    /* Delete only ? */
    if ((argc < 3) || argv[2] == NULL) {
        env_crc_update ();
        return 0;
    }

    /*
     * Append new definition at the end
     */
    for (env=env_data; *env || *(env+1); ++env)
        ;
    if (env > env_data)
        ++env;
    /*
     * Overflow when:
     * "name" + "=" + "val" +"\0\0"  > ENV_SIZE - (env-env_data)
     */
    len = strlen(name) + 2;
    /* add '=' for first arg, ' ' for all others */
    for (i=2; i<argc; ++i) {
        len += strlen(argv[i]) + 1;
    }
    if (len > (&env_data[ENV_SIZE]-env)) {
        printf ("## Error: environment overflow, \"%s\" deleted\n", name);
        return 1;
    }
    while ((*env = *name++) != '\0')
        env++;
    for (i=2; i<argc; ++i) {
        char *val = argv[i];

        *env = (i==2) ? '=' : ' ';
        while ((*++env = *val++) != '\0')
            ;
    }

    /* end is marked with double '\0' */
    *++env = '\0';

    /* Update CRC */
    env_crc_update ();

    /*
     * Some variables should be updated when the corresponding
     * entry in the enviornment is changed
     */

    if (strcmp(argv[1],"ethaddr") == 0) {
        char *s = argv[2];    /* always use only one arg */
        char *e;
        for (i=0; i<6; ++i) {
            bd->bi_enetaddr[i] = s ? simple_strtoul(s, &e, 16) : 0;
            if (s) s = (*e) ? e+1 : e;
        }
#ifdef CONFIG_NET_MULTI
        eth_set_enetaddr(0, argv[2]);
#endif
        return 0;
    }

    if (strcmp(argv[1],"ipaddr") == 0) {
        char *s = argv[2];    /* always use only one arg */
        char *e;
        unsigned long addr;
        bd->bi_ip_addr = 0;
        for (addr=0, i=0; i<4; ++i) {
            ulong val = s ? simple_strtoul(s, &e, 10) : 0;
            addr <<= 8;
            addr  |= (val & 0xFF);
            if (s) s = (*e) ? e+1 : e;
        }
        bd->bi_ip_addr = htonl(addr);
        return 0;
    }
    if (strcmp(argv[1],"loadaddr") == 0) {
        load_addr = simple_strtoul(argv[2], NULL, 16);
        return 0;
    }
#if defined(CONFIG_CMD_NET)
    if (strcmp(argv[1],"bootfile") == 0) {
        copy_filename (BootFile, argv[2], sizeof(BootFile));
        return 0;
    }
#endif

#ifdef CONFIG_AMIGAONEG3SE
    if (strcmp(argv[1], "vga_fg_color") == 0 ||
        strcmp(argv[1], "vga_bg_color") == 0 ) {
        extern void video_set_color(unsigned char attr);
        extern unsigned char video_get_attr(void);

        video_set_color(video_get_attr());
        return 0;
    }
#endif    /* CONFIG_AMIGAONEG3SE */

    return 0;
}

 

posted @ 2017-02-28 15:13  biaohc  阅读(6808)  评论(0编辑  收藏  举报