[IMX6Q]u-boot環境變數原理分析
阿新 • • 發佈:2019-02-14
u-boot版本: v2009.08
一些重要引數如串列埠波特率,bootmcmd,loadaddr等引數,可能需要
動態修改除錯,u-boot提供了環境變數env用於儲存這些資訊到永久性儲存
介質如SD或者RAM中。RAM會丟失,而存於SD則下次開機依然存在。
u-boot命令列表中,提供了setenv, saveenv兩個命令,前者用於臨時儲存到
RAM中,後者會儲存到SD中。
幾個概念:
a. 儲存介質
對於不同的儲存介質,它的讀寫操作方式也不同,本例使用SD。
uboot-imx/include/configs/mx6q_sabresd.h
uboot-imx/common/Makefile#define CONFIG_FSL_ENV_IN_MMC
COBJS-$(CONFIG_ENV_IS_IN_MMC) += env_mmc.o
所以本例對應的是env_mmc.c
b. 事實上env即可在開機後從RAM中中分配一個數據結構臨時儲存,也可以直接將env放在u-boot的text段從而
節省RAM空間,本例描述的是前者,後者後面文件會描述如何實現。
c. environment_s結構
uboot-imx/include/environment.h
crc: 讀取環境變數時先會校驗,其中當第一次讀取的時候,因為flash沒有環境變數儲存,所以會失敗,這是正常的。typedef struct environment_s { uint32_t crc; /* CRC32 over data bytes */ #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT unsigned char flags; /* active/obsolete flags */ #endif unsigned char data[ENV_SIZE]; /* Environment data */ } env_t;
data: 存放環境變數。
d. 全域性資料訪問env
typedef struct global_data { bd_t *bd; unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long reloc_off; /* Relocation Offset */ unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long fb_base; /* base address of frame buffer */ ...... } gd_t;
reloc_off: env一開始是儲存在sd中的,開機之後為了快速讀取,它會被重定位到RAM中。
env_add: 上面env_t的地址,一開始是指向SD中的,後來是指向RAM中的拷貝資料。
env_valid: 如果crc檢驗成功,則此變數為1.
流程分析:
u-boot和env相關的啟動流程有:
start_armboot -> init_sequence -> env_init -> env_relocate
env_init:
uboot-imx/common/env_mmc.c
int env_init(void)
{
/* use default */
/*初始化為預設值以及env有效*/
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
#ifdef CONFIG_DYNAMIC_MMC_DEVNO
/*讀取當前使用sd的mmc number*/
extern int get_mmc_env_devno(void);
mmc_env_devno = get_mmc_env_devno();
#else
mmc_env_devno = CONFIG_SYS_MMC_ENV_DEV;
#endif
return 0;
}
default_environment:
uboot-imx/common/env_common.c
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"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_ETH4ADDR
"eth4addr=" MK_STR(CONFIG_ETH4ADDR) "\0"
#endif
#ifdef CONFIG_ETH5ADDR
"eth5addr=" MK_STR(CONFIG_ETH5ADDR) "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR(CONFIG_SERVERIP) "\0"
#endif
#ifdef CONFIG_SYS_AUTOLOAD
"autoload=" CONFIG_SYS_AUTOLOAD "\0"
#endif
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "\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_RD_LOADADDR
"rd_loadaddr=" MK_STR(CONFIG_RD_LOADADDR) "\0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};
本例定義了:CONFIG_BOOTDELAY,CONFIG_BAUDRATE, CONFIG_LOADADDR, CONFIG_RD_LOADADDR, CONFIG_EXTRA_ENV_SETTINGS。
env_relocate:
uboot-imx/common/env_common.c
void env_relocate (void)
{
......
/*未定義*/
#ifdef ENV_IS_EMBEDDED
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
/*
* We must allocate a buffer for the environment
*/
/*從RAM中新分配一個struct env_t*/
env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
/*env_init()初始化為1了*/
if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CONFIG_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 (-60);
#endif
set_default_env();
}
else {
env_relocate_spec ();
}
/*替換掉預設env,重新指向從SD讀取的新env*/
gd->env_addr = (ulong)&(env_ptr->data);
......
}
env_relocate_spec:
void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
/*獲取sd資訊*/
struct mmc *mmc = find_mmc_device(mmc_env_devno);
/*初始化sd*/
if (init_mmc_for_env(mmc))
return;
/*從sd的CONFIG_ENV_OFFSET處讀取CONFIG_ENV_SIZE大小資料,也就是讀取env*/
if (read_env(mmc, CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET, env_ptr))
return use_default();
/*讀取的env進行crc校驗,由於第一次開機沒有env儲存到SD,所以會失敗。*/
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
return use_default();
gd->env_valid = 1;
#endif
}
#if !defined(ENV_IS_EMBEDDED)
static void use_default()
{
puts ("*** Warning - bad CRC or MMC, using default environment\n\n");
set_default_env();
}
#endif
void set_default_env(void)
{
if (sizeof(default_environment) > ENV_SIZE) {
puts ("*** Error - default environment is too large\n\n");
return;
}
memset(env_ptr, 0, sizeof(env_t));
/*使用預設的env*/
memcpy(env_ptr->data, default_environment,
sizeof(default_environment));
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
/*再做crc校驗*/
env_crc_update ();
/*當前env有效*/
gd->env_valid = 1;
}
因此開機之後,env是使用default_environment裡的引數,而它又是臨時儲存在RAM中的。
setenv和saveenv命令:
兩者對應的函式分別是:do_setenv()和saveenv().
uboot-imx/common/env_nvedit.c
setenv:
U_BOOT_CMD(
setenv, CONFIG_SYS_MAXARGS, 0, do_setenv,
"set environment variables",
"name value ...\n"
" - set environment variable 'name' to 'value ...'\n"
"setenv name\n"
" - delete environment variable 'name'"
);
do_setenv -> _do_setenv -> env_get_addr
uchar *env_get_addr (int index)
{
/*env 有效*/
if (gd->env_valid) {
/*返回之前儲存在記憶體中env地址*/
return ( ((uchar *)(gd->env_addr + index)) );
} else {
return (&default_environment[index]);
}
}
saveenv:
U_BOOT_CMD(
saveenv, 1, 0, do_saveenv,
"save environment variables to persistent storage",
""
);
do_saveenv -> saveenv
int saveenv(void)
{
struct mmc *mmc = find_mmc_device(mmc_env_devno);
if (init_mmc_for_env(mmc))
return 1;
printf("Writing to MMC(%d)... ", mmc_env_devno);
/*寫到SD中永久儲存*/
if (write_env(mmc, CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET, env_ptr)) {
puts("failed\n");
return 1;
}
puts("done\n");
return 0;
}