转载:http://blog.chinaunix.net/uid-28236237-id-3867041.html
U-boot中通过环境参数保存一些配置,这些配置可以通过修改环境参数、保存环境参数、读取环境参数等操作进行灵活的配置,便于调试开发。这篇文章主要来分析一下u-boot中环境参数的实现。文章主要分为四个部分,第一是环境参数的存储格式,第二部分是环境参数的初始化,第三部分是环境参数的读取,第四个部分是环境参数保存过程。
首先,我们来看一下环境参数的存储格式。一般嵌入式系统的第一个分区是boot分区,而环境参数一般会采用一种格式保存到boot代码区之后,当然,这个位置不能超出第一个分区的边界。
typedef struct environment_s
{
unsigned long crc; /* CRC32 over data bytes */
#ifdef CFG_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;
|
环境参数就是以这样的格式存储到flash上的,其中crc表示对整个环境参数数据的校验码。Data中保存环境参数,参数的组织格式是这样的。
static uchar default_environment[] =
{
#if defined(CONFIG_BOOTARGS)
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#if defined(CONFIG_BOOTCOMMAND)
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
#if defined(CONFIG_RAMBOOTCOMMAND)
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_NFSBOOTCOMMAND)
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
#endif
)
"bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0"
#endif
)
"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_ETHPRIME
"ethprime=" CONFIG_ETHPRIME "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR (CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR (CONFIG_SERVERIP) "\0"
#endif
#ifdef CFG_AUTOLOAD
"autoload=" CFG_AUTOLOAD "\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_PREBOOT
"preboot=" CONFIG_PREBOOT "\0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=" "1" "\0"
#endif
)
"pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0" /* Termimate env_t data with 2 NULs */
};
|
实际上就是”xxxx=xxxx”’\0’”xxxxx=xxxxxx”’\0’,每个环境变量之间用NULL隔开
U-boot的环境变量最开始是保存在flash上的,在u-boot第二阶段中会将环境变量从flash上读到内存里,并进行相应的初始化。
/*最开始,调用env_init函数对环境变量进行初始化,
这里暂时不考虑ENV_IS_EMBEDDED的情况,所以,初始化
工作就是设置env_addr地址,并设置env_valid为有限
*/
int env_init(void)
{
#if defined(ENV_IS_EMBEDDED)
ulong total;
, crc2_ok = ;
env_t *tmp_env1, *tmp_env2;
total = CFG_ENV_SIZE;
tmp_env1 = env_ptr;
tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE);
crc1_ok = (crc32(, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
crc2_ok = (crc32(, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
if (!crc1_ok && !crc2_ok)
gd->env_valid = ;
else if(crc1_ok && !crc2_ok)
gd->env_valid = ;
else if(!crc1_ok && crc2_ok)
gd->env_valid = ;
else
{
/* both ok - check serial */
&& tmp_env2->flags == )
gd->env_valid = ;
&& tmp_env1->flags == )
gd->env_valid = ;
else if(tmp_env1->flags > tmp_env2->flags)
gd->env_valid = ;
else if(tmp_env2->flags > tmp_env1->flags)
gd->env_valid = ;
else /* flags are equal - almost impossible */
gd->env_valid = ;
}
)
env_ptr = tmp_env1;
)
env_ptr = tmp_env2;
#else /* ENV_IS_EMBEDDED */
gd->env_addr = (ulong)&default_environment[];
gd->env_valid = ;
#endif /* ENV_IS_EMBEDDED */
);
}
|
初始化环境变量地址为default值之后,调用下面env_relocate函数具体分配内存空间,将环境变量从flash中读到内存中来,完成初始化过程
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__, __LINE__,
gd->reloc_off);
/*后面需要从flash中读出环境变量来,首先分配一块buffer来装这些数据
这里调用malloc分配空间,env_ptr指向这个空间*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__, __LINE__, env_ptr);
/*
* After relocation to RAM, we can always use the "memory" functions
*/
env_get_char = env_get_char_memory;
)
{
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
SHOW_BOOT_PROGRESS (-);
#endif
if (sizeof(default_environment) > ENV_SIZE)
{
puts ("*** Error - default environment is too large\n\n");
return;
}
memset (env_ptr, , sizeof(env_t));
memcpy (env_ptr->data,
default_environment,
sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update ();
gd->env_valid = ;
}
else
{
/*调用下面的函数完成具体的环境参数读取动作*/
env_relocate_spec ();
}
/*将环境变量具体内存中buffer位置赋值给env_addr中*/
gd->env_addr = (ulong) & (env_ptr->data);
}
|
具体的读操作通过env_relocate_spec来说完成,u-boot根据flash种类不同,这个函数的实现方式也不一样。对于nandflash的实现,这个函数定义在env_nand.c这个文件中。函数具体实现如下
void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
ulong total = CFG_ENV_SIZE;
int ret;
#ifdef CONFIG_SURPORT_WINCE
nand_read_options_t opts;
memset(&opts, , sizeof(opts));
opts.buffer = (u_char *)env_ptr;
opts.length = total;
opts.offset = CFG_ENV_OFFSET;
opts.readoob = ;
opts.quiet = ;
opts.noecc = ;
opts.nocheckbadblk = ;
ret = nand_read_opts(&nand_info[], &opts);
#else
ret = nand_read(&nand_info[], CFG_ENV_OFFSET, &total, (u_char *)env_ptr);
#endif
if (ret || total != CFG_ENV_SIZE)
return use_default();
, env_ptr->data, ENV_SIZE) != env_ptr->crc)
return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}
|
前面介绍了环境变量初始化的过程,在完成了初始化之后。U-boot其它部分的代码在要调用环境变的时候可以调用相应的接口读取。这个接口就是getenv
char *getenv (char *name)
{
int i, nxt;
WATCHDOG_RESET();
; env_get_char(i) != )
{
int val;
for (nxt = i; env_get_char(nxt) != '\0'; ++nxt)
{
if (nxt >= CFG_ENV_SIZE)
{
return (NULL);
}
}
)
continue;
return ((char *)env_get_addr(val));
}
return (NULL);
}
|
Getenv函数就是在gd->env_addr这个buffer中不断的寻找name相对应的字符串,找到这个字符串”name=xxxxxx”之后将第一个x的地址返回。
本文还需要分析一下的就是对环境参数的保存,如果通过u-boot命令setenv修改了环境参数,我们必须还要通过saveenv将修改的参数保存在能在下次启动是继续使用设置的参数
int saveenv(void)
{
ulong total;
;
puts ("Erasing Nand...");
], CFG_ENV_OFFSET, CFG_ENV_SIZE))
;
puts ("Writing to Nand... ");
total = CFG_ENV_SIZE;
ret = nand_write(&nand_info[], CFG_ENV_OFFSET, &total, (u_char *)env_ptr);
if (ret || total != CFG_ENV_SIZE)
;
puts ("done\n");
return ret;
}
|
这个函数比较简单,首先就是擦除相应部分的flash,然后将环境变量结构体写到对应的flash部分,我分析的mini2440中环境变量的偏移地址是256K,总共大小为64K
#define CFG_ENV_OFFSET 0x40000
#define CFG_ENV_SIZE0x10000/* Total Size of Environment Sector */
- Linux中环境变量文件及配置
Linux中环境变量文件及配置 一.环境变量文件介绍 转自:http://blog.csdn.net/cscmaker/article/details/7261921 Linux中环境变量包括系统 ...
- Linux中环境变量文件及配置(转载)
一.环境变量文件介绍 转自:http://blog.csdn.net/cscmaker/article/details/7261921 Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登 ...
- Linux中环境变量中文件执行顺序
Linux 的变量可分为两类:环境变量和本地变量 环境变量:或者称为全局变量,存在于所有的shell 中,在你登陆系统的时候就已经有了相应的系统定义的环境变量了.Linux 的环境变量具有 ...
- Node.js中环境变量process.env详解
Node.js中环境变量process.env详解process | Node.js API 文档http://nodejs.cn/api/process.html官方解释:process 对象是一个 ...
- Linux中环境变量文件
一.环境变量文件介绍 转自:http://blog.csdn.net/cscmaker/article/details/7261921 Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登 ...
- shell中环境变量
Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登录到系统的用户都要读取的系统变量,而用户级的环境变量则是该用户使用系统时加载的环境变量. 所以管理环境变量的文件也分为系统级和用户级的, ...
- setlocal启动批处理文件中环境变量的本地化
setlocal启动批处理文件中环境变量的本地化 在执行 SETLOCAL 之后所做的环境改动只限于批处理文件.要还原原先的设置,必须执行 ENDLOCAL. 学习了:https://baike.ba ...
- Windows系统中环境变量不展开的问题
Windows系统中环境变量不展开的问题 问题现象:Windows.System32等系统目录里命令,无法通过Path搜索路径来执行.查看Path环境变量结果如下: D:\>echo %Path ...
- ubuntu下关于profile和bashrc中环境变量的理解(转)
ubuntu下关于profile和bashrc中环境变量的理解(转) (0) 写在前面 有些名词可能需要解释一下.(也可以先不看这一节,在后面看到有疑惑再上来看相关解释) $PS1和交互式运行(r ...
随机推荐
- Java文件上传与下载
文件上传与下载可谓上网中的常见现象.apache为我们准备了用于文件上传与下载的两个jar包(commons-fileupload-1.2.1.jar,commons-io-1.4.jar).我们在w ...
- HDU 1560 DNA sequence(IDA*)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1560 题目大意:给出n个字符串,让你找一个字符串使得这n个字符串都是它的子串,求最小长度. 解题思路: ...
- Django实现文章按年月归档、点赞和评论、发送邮件
文章归档的实现 我们在创建文章时,会在数据库中存储文章创建的时间这样的字段,一般用的都是datetime类型,记录文章创建的年月日和时分秒,所以我们直接使用文章的创建时间分类是无法实现文章的按年月归档 ...
- opencv的CMakeLists.txt与makefile写法
opencv的CMakeLists.txt cmake_minimum_required(VERSION 2.8) project(my_run_name) find_package(OpenCV R ...
- 【PAT】1005. 继续(3n+1)猜想 (25)
1005. 继续(3n+1)猜想 (25) 卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中 ...
- bean的singleton(没有看到生命周期范围??)
4.5.1 The singleton scope Only one shared instance of a singleton bean is managed, and all requests ...
- eclipse JavaEE的配置
Eclipse IDE for Java EE Developers(win32) 下载地址:http://mirror.bjtu.edu.cn/eclipse/technology/epp/down ...
- JSP与Servlet传值及对比
JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达. JSP编译后是”类servlet”. Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在 ...
- Eclipse酷炫项目、最新趋势介绍
作为Eclipse基金组织的执行董事,我需要经常审阅每一个新提交的Eclipse项目协议书.作为Eclipse的一分子,我很乐意与加入我们团队的新开发人员互动.这也是我工作中的乐趣之一.2013年,我 ...
- java8新特性——方法引用与构造器引用
上篇文章简单学习了java8内置得4大核心函数式接口,这类接口可以解决我们遇到得大多数得业务场景得问题.今天来简单学习一下方法引用与构造器引用. 一.方法引用 方法引用:若lambda 体中得内容已经 ...