嵌入式系统,常常需要保存一些私有数据到设备,并且保证系统升级、重启不丢失。
常用做法
1、外挂eeprom,flash
2、现有存储介质,开辟新的mtd分区,进行操作。
等等
本节,透过uboot_envtools 操作uboot_env分区
uboot_envtools , 其实就是uboot源码一部分。具体实现在uboot/tools/env/目录下,通过在u-boot根目录下执行make env,就可生成fw_printenv应用程序,并将该应用程序拷贝到根文件系统中,就可以查看到uboot的相应的环境变量了。
1. 查看系统分区
root@OpenWrt:/usr# cat /proc/mtd
dev: size erasesize name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "factory"
mtd3: 00fa0000 00010000 "firmware"
mtd4: 0016c8dc 00010000 "kernel"
mtd5: 00e33724 00010000 "rootfs"
mtd6: 00750000 00010000 "rootfs_data"
2. menuconfig添加uboot_envtools模块
.config - OpenWrt Configuration
> Utilities > Boot Loaders ─────────────────────────────────────────────────────────────────
┌──────────────────────────────────── Boot Loaders ─────────────────────────────────────┐
│ Arrow keys navigate the menu. <Enter> selects submenus ---> (or empty submenus │
│ ----). Highlighted letters are hotkeys. Pressing <Y> includes, <N> excludes, <M> │
│ modularizes features. Press <Esc><Esc> to exit, <?> for Help, </> for Search. │
│ Legend: [*] built-in [ ] excluded <M> module < > module capable │
│ ┌───────────────────────────────────────────────────────────────────────────────────┐ │
│ │ < > fconfig..................................... RedBoot configuration editor│ │
│ │ <*> uboot-envtools................. read/modify U-Boot bootloader environment│ │
│ │ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────────────────┘ │
├───────────────────────────────────────────────────────────────────────────────────────┤
│ <Select> < Exit > < Help > < Save > < Load > │
└───────────────────────────────────────────────────────────────────────────────────────┘
3. 编译后,进入系统,测试
- 查看env
root@OpenWrt:/# fw_printenv
bootcmd=bootp; setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; bootm
bootdelay=5
baudrate=115200
ssid=12345678
- 添加env,uuid=1234567890
root@OpenWrt:/# fw_setenv uuid 1234567890
root@OpenWrt:/# fw_printenv
bootcmd=bootp; setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; bootm
bootdelay=5
baudrate=115200
ssid=12345678
uuid=1234567890
- 删除env
root@OpenWrt:/# fw_setenv uuid
root@OpenWrt:/# fw_printenv
bootcmd=bootp; setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; bootm
bootdelay=5
baudrate=115200
ssid=12345678
root@OpenWrt:/#
4. 透过system、popen 管道,在c代码中读写操作
int fw_env_del(char *key)
{
char buffer[256];
sprintf(buffer,"fw_setenv %s",key);
return system(buffer);
}
int fw_env_set(char *key,char *val)
{
char buffer[256];
sprintf(buffer,"fw_setenv %s %s",key,val);
return system(buffer);
}
int fw_env_get(char *key,char *val)
{
FILE * fp;
char buffer[256];
char read_key[32];
char read_val[128];
fp=popen("fw_printenv","r");
if (fp<0){
return -1;
}
while(fgets(buffer,sizeof(buffer),fp)){
sscanf(buffer,"%[^=]=%s",read_key,read_val);
if(!strcmp(key,read_key)){
strcpy(val,read_val);
pclose(fp);
return 0;
}
}
pclose(fp);
return -404;
}
5. 脚本读取env
#!/bin/sh
# 加载ubootenv中的数据,抓取uuid的值,赋给变量data
data="$(fw_printenv | grep uuid | cut -d= -f2)"
echo data=$uuid
exit 0
6. 修改fw_env分区
- 查看当前fw_env分区
root@OpenWrt:/# cat /etc/fw_env.config
/dev/mtd1 0x0 0x10000 0x1000
- fw_env.config由脚本生成,路径package/boot/uboot-envtools/files/ramips
ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x1000" "0x10000"
- 参数定义在文件package/boot/uboot-envtools/files/uboot-envtools.sh中
ubootenv_add_uci_config() {
local dev=$1
local offset=$2
local envsize=$3
local secsize=$4
local numsec=$5
uci batch <<EOF
add ubootenv ubootenv
set ubootenv.@ubootenv[-1].dev='$dev'
set ubootenv.@ubootenv[-1].offset='$offset'
set ubootenv.@ubootenv[-1].envsize='$envsize'
set ubootenv.@ubootenv[-1].secsize='$secsize'
set ubootenv.@ubootenv[-1].numsec='$numsec'
EOF
uci commit ubootenv
}
参数 | 含义 |
---|---|
/dev/mtd1 | 指定boot_env所在flash上的分区 |
0x0 | 指定boot_env所在分区上的偏移量(应该么有实际意义,mtd分区已经指定了偏移) |
0x1000 | 指定uboot的环境变量存储空间大小与package/boot/uboot-${boardname}/Makefile中定义的ENV_SIZE大小一致,否则会报CRC校验出错,比如mtd1分区的大小为1MiB,uboot指定的大小为0x40000,那么应该使用0x40000 |
0x10000 | 指定flash的sector大小 |
- 修改package/boot/uboot-envtools/files/ramips,就可以将数据保存到其他mtd分区
例如:
ubootenv_add_uci_config "/dev/mtd7" "0x0" "0x10000" "0x1000"