Linux系统下访问U-BOOT环境变量
移植过U-BOOT的人,都知道:在U-BOOT中存有ENV。但U-BOOT在引导内核启动之后,U-BOOT的生命周期就结束了。那么启动LINUX内核之后,U-Boot的环境变量还在么?
按照u-boot对Flash的规划:ENV存放在Flash的0x60000~0x80000。
那么我们如何在Linux下查看U-boot的环境变量呢?
其实U-BOOT源码内已经提供了这么一种方式:在tools/env/目录下,有相应的源码,通过在u-boot根目录下执行:make env就可生成fw_printenv应用程序,并将该应用程序拷贝到根文件系统中,就可以查看到U-BOOT的相应的环境变量了。
具体实现方法,及操作步骤见下面:(转)
1、编译fw_printenv工具
在你使用的uboot代码中用以下编译指令:
make env
这样就可以编译tools/env下的代码,编译出的fw_printenv工具有读写uboot环境变量区的能力。这个工具是针对目标机的,也就是说如果你的uboot代码之前是针对ARM编译的话,fw_printenv也是交叉编译给ARM芯片的。如果编译不成功,则按照如下操作执行:
make distclean
make mini2440_config
make env
2、安装fw_printenv工具
到/tools/env目录中,将编译好的fw_printenv拷贝到目标机的文件系统中,并通过"ln -s fw_printenv fw_setenv",创建一个fw_setenv到fw_printenv的软链。
这个工具还需要一个配置文件,以获取uboot的ENV区域的位置信息。默认状态下,请将fw_env.config文件拷贝到目标机的文件系统的/etc目录下。然后结合uboot配置中定义的ENV区和Linux下mtd分区的情况修改配置文件。具体的修改方法见fw_env.config文件中的说明及/tools/env/README文件。
3、fw_printenv工具的使用
其实fw_printenv使用起来和uboot下的printenv和setenv指令是一模一样的。
• 打印uboot环境变量:
1. fw_printenv [[ -n name ] | [ name ... ]]
2. # ./fw_printenv -n baudrate
3. 115200
4. # ./fw_printenv baudrate
5. baudrate=115200
如果不指定name,fw_printenv会打印出ENV区中的所有环境变量
• 设置uboot环境变量:
1. fw_setenv name [ value ... ]
2. 如果不指定value,表示要删除这个name的环境变量。
3. # ./fw_setenv temp tekkaman
4. # ./fw_printenv -n temp
5. tekkaman
6. # ./fw_setenv temp
7. # ./fw_printenv -n temp
8. ## Error: "temp" not defined
linux程序读写uboot env变量:
分析发现uboot env的存储格式是,开始4个字节是crc32,后面是一个接一个\0结束的字符串,每个串都是envname=enavalue的格式。
挪用uboot部分的相关代码,实现以下函数:
int set_env_ptr(unsigned char * ptr);
char *get_env (char *name);
int set_env (char *varname, char *varvalue);
int printenv(void);
使用时,我们读取env分区的数据到内存;
调用set_env_ptr(数据指针),该函数会进行crc校正确保env分区正确,并且返回0;
get_env, set_env, printenv相应uboot 中的echo $envname; setenv envname envstr; printenv
Usage: ./uboot_env read|set [envname] [value] -i envimage -o outimage
linux 程序读写Env分区 --- mtdparts,/dev/mtd*,mtd_debug
1. uboot $mtdparts 和 linux /dev/mtd* 的联系
比如:
uboot:# setenv mtdparts 'mtdparts=nx_2016:1408k@0k(boot),128k@1408k(env),-(extra);nx_2017:16m(k0),16m(k1),-(nandextra)'
linux:# cat /proc/mtd
dev: size erasesize name
mtd0: 00160000 00010000 "boot"
mtd1: 00020000 00010000 "env"
mtd2: 00280000 00010000 "extra"
mtd3: 00400000 00010000 "nx_2016"
mtd4: 04000000 00004000 "nx_2017"
mtd5: 01000000 00004000 "k0"
mtd6: 01000000 00004000 "k1"
mtd7: 02000000 00004000 "nandextra"
2. mtd_debug usage
参考
http://blog.csdn.net/yinkaizhong/archive/2008/12/25/3604794.aspx
比如,进一步看env分区的信息:
linux:# mtd_debug info /dev/mtd1
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NORFLASH
mtd.size = 131072 (128K)
mtd.erasesize = 65536 (64K)
mtd.writesize = 1
mtd.oobsize = 0
regions = 0
3. 分区的操作
比如对env分区进行擦除:
# mtd_debug erase /dev/mtd1 0 0x20000
linux 下更新uboot环境变量:
参考网址:
http://www.denx.de/wiki/DULG/HowCanIAccessUBootEnvironmentVariablesInLinux
http://blog.csdn.net/hangbing0203/article/details/4314576
http://labs.igep.es/index.php/How_to_modify_the_uboot_environment_from_userspace
代码分析:
README:
This is a demo implementation of a Linux command line tool to access
the U-Boot's environment variables.
For the run-time utiltity configuration uncomment the line
#define CONFIG_FILE "/etc/fw_env.config"
in fw_env.h.
这个有误,应该是用配置文件时,不要注释掉这个宏
See comments in the fw_env.config file for definitions for the
particular board.
Configuration can also be done via #defines in the fw_env.h file. The
following lines are relevant:
#define HAVE_REDUND /* For systems with 2 env sectors */
#define DEVICE1_NAME "/dev/mtd1"
#define DEVICE2_NAME "/dev/mtd2"
#define DEVICE1_OFFSET 0x0000
#define ENV1_SIZE 0x4000
#define DEVICE1_ESIZE 0x4000
#define DEVICE2_OFFSET 0x0000
#define ENV2_SIZE 0x4000
#define DEVICE2_ESIZE 0x4000
Current configuration matches the environment layout of the TRAB
board.
HAVE_REDUND是用来环境变量备份用的
Un-define HAVE_REDUND, if you want to use the utlities on a system
that does not have support for redundant environment enabled.
If HAVE_REDUND is undefined, DEVICE2_NAME is ignored,
as is ENV2_SIZE and DEVICE2_ESIZE.
The DEVICEx_NAME constants define which MTD character devices are to
be used to access the environment.
The DEVICEx_OFFSET constants define the environment offset within the
MTD character device.
ENVx_SIZE defines the size in bytes taken by the environment, which
may be less then flash sector size, if the environment takes less
then 1 sector.
DEVICEx_ESIZE defines the size of the first sector in the flash
partition where the environment resides.
DEVICEx_NAME设备名
DEVICEx_OFFSET设备中环境变量的偏移
ENVx_SIZE 环境变量大小,可能比1sector小
查看环境变量大小:
pri查看
Environment size: 1560/4092 bytes
include/configs/mv_kv.h
#define CFG_ENV_SIZE 0x1000
所以大小是0x1000
查看环境变量偏移:
#define CFG_MONITOR_LEN (512 << 10) /* Reserve 512 kB for Monitor */ uboot镜像长度 0x80000 =512k
//uboot的基址和环境变量的基址
#define CFG_MONITOR_BASE (CFG_FLASH_BASE)
#define CFG_ENV_ADDR (CFG_MONITOR_BASE + CFG_MONITOR_LEN) 环境变量开始位置
#define CFG_ENV_SIZE 0x1000 //4k
环境变量从0x80000开始 到0x80000+0x1000
根据CFG_ENV_ADDR 知道环境变量偏移是0x80000
查看erasesize
/opt/qtmarvell/mvqt/tools # cat /proc/mtd
dev: size erasesize name
mtd0: 00100000 00010000 "uboot"
mtd1: 00700000 00010000 "linux-root"
编译fw_printenv:
下载http://ftp.denx.de/pub/u-boot/u-boot-2011.03.tar.bz2
根目录下:
make env HOSTCC=arm-mv5sft-linux-gnueabi-gcc
ln -s fw_printenv fw_setenv
修改fw_env.config ,根据上面的信息:
/opt/qtmarvell/mvqt/tools # cat fw_env.config
# Configuration file for fw_(printenv/saveenv) utility.
# Up to two entries are valid, in this case the redundant
# environment sector is assumed present.
# Notice, that the "Number of sectors" is ignored on NOR.
# MTD device name Device offset Env. size Flash sector size Number of sectors
/dev/mtd0 0x80000 0x1000 0x10000
U-Boot添加命令的方法及U-Boot命令执行过程(转)
下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。
(1) 建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。
(2) 定义“menu”命令
在cmd_menu.c中使用如下的代码定义“menu”命令:
_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);
其中U_BOOT_CMD命令格式如下:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
在内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/mini2440.h中定义了CONFIG_SYS_LONGHELP宏,则在U-Boot中使用help命令查看某个命令的帮助信息时将显示usage和help字段的内容,否则就只显示usage字段的内容。
U_BOOT_CMD宏在include/command.h中定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。
其中的cmd_tbl_t在include/command.h中定义如下:
struct cmd_tbl_s {
char *name; /* 命令名 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 是否自动重复 */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
char *usage; /* 简短的帮助信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 较详细的帮助信息 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* 自动补全参数 */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。
其中Struct_Section在include/command.h中定义如下:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
在U-Boot连接脚本u-boot.lds中定义了".u_boot_cmd"段:
. = .;
__u_boot_cmd_start = .; /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; /* 将__u_boot_cmd_end指定为当前地址 */
这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。这样只要将U-Boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在__u_boot_cmd_start与__u_boot_cmd_end之间查找就可以了。
因此“menu”命令的定义经过宏展开后如下:
cmd_tbl_t __u_boot_cmd_menu __attribute__ ((unused,section (".u_boot_cmd"))) = {menu, 3, 0, do_menu, "menu - display a menu, to select the items to do something\n", " - display a menu, to select the items to do something"}
实质上就是用U_BOOT_CMD宏定义的信息构造了一个cmd_tbl_t类型的结构体。编译器将该结构体放在“u_boot_cmd”段,执行命令时就可以在“u_boot_cmd”段查找到对应的cmd_tbl_t类型结构体。
(3) 实现命令的函数
在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
/* 实现代码略 */
}
(4) 将common/cmd_menu.c编译进u-boot.bin
在common/Makefile中加入如下代码:
COBJS-$(CONFIG_BOOT_MENU) += cmd_menu.o
在include/configs/mini2440.h加入如代码:
#define CONFIG_BOOT_MENU 1
重新编译下载U-Boot就可以使用menu命令了
(5)menu命令执行的过程
在U-Boot中输入“menu”命令执行时,U-Boot接收输入的字符串“menu”,传递给run_command函数。run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回menu命令的cmd_tbl_t结构。然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用menu命令的响应函数do_menu,从而完成了命令的执行。