1. 概述
在uboot中我们可以很方便的在uboot shell中通过printenv/setenv/savenv来查看/设置/保存env环境变量或者在uboot源码中通过env_get/env_set/env_save 来获取/设置/保存env环境变量。有时候我们希望在进入linux系统后去查看或者修改env环境变量,那么在Linux系统下是否有工具去做这样的事情吗或者Linux kernel是否提供什么方式可以做这样的事情呢。幸运的是uboot仓库下提供了fw_printenv工具可以做上面提到的查看/修改/设置保存env环境的事情。
2. fw_printenv源码
fw_printenv源码位于uboot仓库,uboot源码可以从github中获取GitHub - u-boot/u-boot: "Das U-Boot" Source Tree 。
fw_printenv在uboot中的路径:uboot/tools/env
3. fw_printenv编译
arm:
1. make A10-OLinuXino-Lime_defconfig
2.
a. dynamically linked:
make CROSS_COMPILE=arm-linux-gnueabihf- envtools
b. statically linked
make CROSS_COMPILE=arm-linux-gnueabihf- CC="arm-linux-gnueabihf-gcc -static" envtools
arm64:
1. make A10-OLinuXino-Lime_defconfig
2.
a. dynamically linked:
make CROSS_COMPILE=aarch64-linux-gnu- envtools
b. statically linked
make CROSS_COMPILE=aarch64-linux-gnu- CC="aarch64-linux-gnu-gcc -static" envtools
4. fw_printenv使用
1. 在uboot/tools/env/fw_env.config中按照例子格式添加自己的设备描述,把其他不需要的屏蔽掉。
2. 把修改好的fw_env.config放到系统的/etc/下
3. 建立fw_setenv软链接ln -s /bin/fw_printenv /bin/fw_setenv
4.1. show env
fw_printenv // show all env items
fw_printenv XXX // show "XXX" the one env item
4.2. set env
fw_setenv foo bar // set variable foo equal bar
fw_setenv foo // clear variable foo
fw_setenv --script file run batch script
5. 代码分析
5.1. 入口
//tools/env/fw_env_main.c
207 int main(int argc, char *argv[])
208 {
209 char *lockname = "/run/" CMD_PRINTENV ".lock";
210 int lockfd = -1;
211 int retval = EXIT_SUCCESS;
212 char *_cmdname;
213
214 _cmdname = *argv;
215 if (strrchr(_cmdname, '/') != NULL)
216 _cmdname = strrchr(_cmdname, '/') + 1; // get the basename from path
217
218 // confirm the command is "fw_printenv" or "fw_setenv"
219 if (strcmp(_cmdname, CMD_PRINTENV) == 0) {
220 do_printenv = 1;
221 } else if (strcmp(_cmdname, CMD_SETENV) == 0) {
222 do_printenv = 0;
223 } else {
224 fprintf(stderr,
225 "Identity crisis - may be called as `%s' or as `%s' but not as `%s'\n",
226 CMD_PRINTENV, CMD_SETENV, _cmdname);
227 exit(EXIT_FAILURE);
228 }
229
230 /* the parameters is different between "fw_printenv" with "fw_setenv".
231 * parse_printenv_args() for "fw_printenv" nand parse_setenv_args for
232 * "fw_setenv". */
233 if (do_printenv) {
234 if (parse_printenv_args(argc, argv))
235 exit(EXIT_FAILURE);
236 } else {
237 if (parse_setenv_args(argc, argv))
238 exit(EXIT_FAILURE);
239 }
240
241 /* shift parsed flags, jump to non-option arguments */
242 argc -= optind;
243 argv += optind;
5.2. 参数
fw_printenv与fw_setenv参数的差异
parameter | comment | fw_printenv | fw_setenv |
h | 帮助信息 | 有 | 有 |
n | 相同的变量不重复展示 | 有 | 无 |
s | 批量变量 | 无 | 有 |
v | 打印U-boot版本号 | 有 | 有 |
c | 指定配置信息的脚本,默认是 /etc/fw_env.config | 有 | 有 |
l | 指定锁 | 有 | 有 |
// tools/env/fw_env_main.c
115 static void parse_common_args(int argc, char *argv[])
116 {
117 int c;
118
119 // "/etc/fw_env.config" is default CONFIG_FILE, if no specify
120 #ifdef CONFIG_FILE
121 env_opts.config_file = CONFIG_FILE;
122 #endif
123
124 while ((c = getopt_long(argc, argv, ":a:c:l:h:v", long_options, NULL)) !=
125 EOF) {
126 switch (c) {
127 #ifdef CONFIG_FILE
128 case 'c': // specify config file
129 env_opts.config_file = optarg;
130 break;
131 #endif
132 case 'l': // specify lock
133 env_opts.lockname = optarg;
134 break;
135 case 'h': // help
136 do_printenv ? usage_printenv() : usage_env_set();
137 exit(EXIT_SUCCESS);
138 break;
139 case 'v': // display U-boot version
140 fprintf(stderr, "Compiled with " U_BOOT_VERSION "\n");
141 exit(EXIT_SUCCESS);
142 break;
143 default:
144 /* ignore unknown options */
145 break;
146 }
147 }
148
149 /* Reset getopt for the next pass. */
150 opterr = 1;
151 optind = 1;
152 }
154 int parse_printenv_args(int argc, char *argv[])
155 {
156 int c;
157
158 parse_common_args(argc, argv);
159
160 while ((c = getopt_long(argc, argv, "a:c:ns:l:h:v", long_options, NULL))
161 != EOF) {
162 switch (c) {
163 case 'n': // do not repeat variable name in output
164 noheader = 1;
165 break;
166 case 'a':
167 case 'c':
168 case 'h':
169 case 'l':
170 /* ignore common options */
171 break;
172 default: /* '?' */
173 usage_printenv();
174 exit(EXIT_FAILURE);
175 break;
176 }
177 }
178 return 0;
179 }
181 int parse_setenv_args(int argc, char *argv[])
182 {
183 int c;
184
185 parse_common_args(argc, argv);
186
187 while ((c = getopt_long(argc, argv, "a:c:ns:l:h:v", long_options, NULL))
188 != EOF) {
189 switch (c) {
190 case 's': // specify the script_file for batch mode to minimize writes
191 script_file = optarg;
192 break;
193 case 'a':
194 case 'c':
195 case 'h':
196 case 'l':
197 /* ignore common options */
198 break;
199 default: /* '?' */
200 usage_env_set();
201 exit(EXIT_FAILURE);
202 break;
203 }
204 }
205 return 0;
206 }
5.3. 配置文件fw_env.config
格式:
dev_name device_offset env_size device_erase_size env_sectors
注:如果env_secotrs不配置,那么默认它等于env_size / dev_erase_size.
对于字符设备支持NOR,SLCNAND,DATAFLASH,UBI类型的MTD设备。
对于块设备类型的设备,device_offset可以配置为负数,表示距设备结尾XX的位置。