操作系统下对uboot 环境变量的读写

1.适用平台

ARMV8架构芯片,小端存储,uboot版本为2019.07-Ver1.7.4。为了省事,代码里默认是小端程序,如果是大端存储,需要修改部分代码。操作系统为某国产系统,不是Linux。但是基本解析代码是与平台无关的。

2.uboot环境变量存储格式解析

读取uboot代码,发现使用uboot版本有两种格式的uboot env。下面贴出格式代码:

struct env_image_single {
	uint32_t crc;		/* CRC32 over data bytes    */
	char data[];
};

struct env_image_redundant {
	uint32_t crc;		/* CRC32 over data bytes    */
	unsigned char flags;	/* active or obsolete */
	char data[];
};

两种格式是由uboot中宏定义 CONFIG_SYS_REDUNDAND_ENVIRONMENT 决定的,我的uboot使用的是上面那种。首先是4个字节的CRC校验码,后面紧跟着的就是所有的环境变量。

下面那种格式中的lags不太清楚是什么意思,似乎是标志着当前uboot使用哪个环境变量。粗略地看了一下,似乎定义了 CONFIG_SYS_REDUNDAND_ENVIRONMENT ,就会有个冗余的uboot环境变量,那么flags很可能指示了当前环境变量是否是被使用的,还是备份的。

3.其他一些相关宏定义

我的uboot环境变量是放在spi flash中的,相关的环境变量如下:

#define ENV_HEADER_SIZE	4
#define CONFIG_ENV_OFFSET	0x70000
#define CONFIG_ENV_SIZE 4096
#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)

ENV_HEADER_SIZE:其实是CRC的字节数,如果加上flags,那应该就是5字节。

CONFIG_ENV_SIZE:环境变量占用空间的字节数,最大就这么多。

CONFIG_ENV_OFFSET:环境变量的偏移地址,也就是说放在FLASH的地址。

ENV_SIZE:环境变量数据部分占用的字节数,不包括CRC、flags等数据。

4.CRC相关代码

uboot加载环境变量时,会计算环境变量的CRC,与放在最前面的四个字节的CRC做比较,如果不同的话,就会从代码中加载默认的环境变量。因此每次修改环境变量都需要更新CRC的值并写入FLASH中。此部分代码是从uboot中copy出来的,不一定适用于其他版本的uboot。

crc32.c

#include <stdio.h>
#include "crc32.h"
#include "crc32table.h"

#define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)

u32 crc32_le(u32 crc, unsigned char const *p, size_t len)
{
	const u32      *b =(u32 *)p;
	const u32      *tab = crc32table;

	/* printf("Crc32_le crc=%x\n",crc); */
	/* Align it */
	if((((long)b)&3 && len)){
		do {
			u8 *p = (u8 *)b;
			DO_CRC(*p++);
			b = (void *)p;
		} while ((--len) && ((long)b)&3 );
	}
	if((len >= 4)){
		/* load data 32 bits wide, xor data 32 bits wide. */
		size_t save_len = len & 3;
		len = len >> 2;
		--b; /* use pre increment below(*++b) for speed */
		do {
			crc ^= *++b;
			DO_CRC(0);
			DO_CRC(0);
			DO_CRC(0);
			DO_CRC(0);
		} while (--len);
		b++; /* point to next byte(s) */
		len = save_len;
	}
	/* And the last few bytes */
	if(len){
		do {
			u8 *p = (u8 *)b;
			DO_CRC(*p++);
			b = (void *)p;
		} while (--len);
	}
	return (crc);
}

/* No ones complement version. JFFS2 (and other things ?)
 * don't use ones compliment in their CRC calculations.
 */
u32 crc32_no_comp(u32 crc, const u8 *buf, u32 len)
{
    const u32 *tab = crc32table;
    const u32 *b =(const u32 *)buf;
    size_t rem_len;
#ifdef CONFIG_DYNAMIC_CRC_TABLE
    if (crc_table_empty)
      make_crc_table();
#endif

    /* Align it */
    if (((long)b) & 3 && len) {
	 u8 *p = (u8 *)b;
	 do {
	      DO_CRC(*p++);
	 } while ((--len) && ((long)p)&3);
	 b = (u32 *)p;
    }

    rem_len = len & 3;
    len = len >> 2;
    for (--b; len; --len) {
	 /* load data 32 bits wide, xor data 32 bits wide. */
	 crc ^= *++b; /* use pre increment for speed */
	 DO_CRC(0);
	 DO_CRC(0);
	 DO_CRC(0);
	 DO_CRC(0);
    }
    len = rem_len;
    /* And the last few bytes */
    if (len) {
	 u8 *p = (u8 *)(b + 1) - 1;
	 do {
	      DO_CRC(*++p); /* use pre increment for speed */
	 } while (--len);
    }

    return crc;
}

u32 crc32(u32 crc, const u8 *p, u32 len)
{
     return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL;
}

crc32.h

#ifndef _CRC_32_H_
#define _CRC_32_H_



#define CRC_LE_BITS 8

#define __LITTLE_ENDIAN	1

//extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len);
//#define crc32(seed, data, length)  crc32_le(seed, (unsigned char const *)data, length)
extern u32 crc32(u32 crc, const u8 *p, u32 len);



#endif

 crc32table.h

/* this file is generated - do not edit */

static const u32 crc32table[] = {
(0x00000000L), (0x77073096L), (0xee0e612cL), (0x990951baL),
(0x076dc419L), (0x706af48fL), (0xe963a535L), (0x9e6495a3L),
(0x0edb8832L), (0x79dcb8a4L), (0xe0d5e91eL), (0x97d2d988L),
(0x09b64c2bL), (0x7eb17cbdL), (0xe7b82d07L), (0x90bf1d91L),
(0x1db71064L), (0x6ab020f2L), (0xf3b97148L), (0x84be41deL),
(0x1adad47dL), (0x6ddde4ebL), (0xf4d4b551L), (0x83d385c7L),
(0x136c9856L), (0x646ba8c0L), (0xfd62f97aL), (0x8a65c9ecL),
(0x14015c4fL), (0x63066cd9L), (0xfa0f3d63L), (0x8d080df5L),
(0x3b6e20c8L), (0x4c69105eL), (0xd56041e4L), (0xa2677172L),
(0x3c03e4d1L), (0x4b04d447L), (0xd20d85fdL), (0xa50ab56bL),
(0x35b5a8faL), (0x42b2986cL), (0xdbbbc9d6L), (0xacbcf940L),
(0x32d86ce3L), (0x45df5c75L), (0xdcd60dcfL), (0xabd13d59L),
(0x26d930acL), (0x51de003aL), (0xc8d75180L), (0xbfd06116L),
(0x21b4f4b5L), (0x56b3c423L), (0xcfba9599L), (0xb8bda50fL),
(0x2802b89eL), (0x5f058808L), (0xc60cd9b2L), (0xb10be924L),
(0x2f6f7c87L), (0x58684c11L), (0xc1611dabL), (0xb6662d3dL),
(0x76dc4190L), (0x01db7106L), (0x98d220bcL), (0xefd5102aL),
(0x71b18589L), (0x06b6b51fL), (0x9fbfe4a5L), (0xe8b8d433L),
(0x7807c9a2L), (0x0f00f934L), (0x9609a88eL), (0xe10e9818L),
(0x7f6a0dbbL), (0x086d3d2dL), (0x91646c97L), (0xe6635c01L),
(0x6b6b51f4L), (0x1c6c6162L), (0x856530d8L), (0xf262004eL),
(0x6c0695edL), (0x1b01a57bL), (0x8208f4c1L), (0xf50fc457L),
(0x65b0d9c6L), (0x12b7e950L), (0x8bbeb8eaL), (0xfcb9887cL),
(0x62dd1ddfL), (0x15da2d49L), (0x8cd37cf3L), (0xfbd44c65L),
(0x4db26158L), (0x3ab551ceL), (0xa3bc0074L), (0xd4bb30e2L),
(0x4adfa541L), (0x3dd895d7L), (0xa4d1c46dL), (0xd3d6f4fbL),
(0x4369e96aL), (0x346ed9fcL), (0xad678846L), (0xda60b8d0L),
(0x44042d73L), (0x33031de5L), (0xaa0a4c5fL), (0xdd0d7cc9L),
(0x5005713cL), (0x270241aaL), (0xbe0b1010L), (0xc90c2086L),
(0x5768b525L), (0x206f85b3L), (0xb966d409L), (0xce61e49fL),
(0x5edef90eL), (0x29d9c998L), (0xb0d09822L), (0xc7d7a8b4L),
(0x59b33d17L), (0x2eb40d81L), (0xb7bd5c3bL), (0xc0ba6cadL),
(0xedb88320L), (0x9abfb3b6L), (0x03b6e20cL), (0x74b1d29aL),
(0xead54739L), (0x9dd277afL), (0x04db2615L), (0x73dc1683L),
(0xe3630b12L), (0x94643b84L), (0x0d6d6a3eL), (0x7a6a5aa8L),
(0xe40ecf0bL), (0x9309ff9dL), (0x0a00ae27L), (0x7d079eb1L),
(0xf00f9344L), (0x8708a3d2L), (0x1e01f268L), (0x6906c2feL),
(0xf762575dL), (0x806567cbL), (0x196c3671L), (0x6e6b06e7L),
(0xfed41b76L), (0x89d32be0L), (0x10da7a5aL), (0x67dd4accL),
(0xf9b9df6fL), (0x8ebeeff9L), (0x17b7be43L), (0x60b08ed5L),
(0xd6d6a3e8L), (0xa1d1937eL), (0x38d8c2c4L), (0x4fdff252L),
(0xd1bb67f1L), (0xa6bc5767L), (0x3fb506ddL), (0x48b2364bL),
(0xd80d2bdaL), (0xaf0a1b4cL), (0x36034af6L), (0x41047a60L),
(0xdf60efc3L), (0xa867df55L), (0x316e8eefL), (0x4669be79L),
(0xcb61b38cL), (0xbc66831aL), (0x256fd2a0L), (0x5268e236L),
(0xcc0c7795L), (0xbb0b4703L), (0x220216b9L), (0x5505262fL),
(0xc5ba3bbeL), (0xb2bd0b28L), (0x2bb45a92L), (0x5cb36a04L),
(0xc2d7ffa7L), (0xb5d0cf31L), (0x2cd99e8bL), (0x5bdeae1dL),
(0x9b64c2b0L), (0xec63f226L), (0x756aa39cL), (0x026d930aL),
(0x9c0906a9L), (0xeb0e363fL), (0x72076785L), (0x05005713L),
(0x95bf4a82L), (0xe2b87a14L), (0x7bb12baeL), (0x0cb61b38L),
(0x92d28e9bL), (0xe5d5be0dL), (0x7cdcefb7L), (0x0bdbdf21L),
(0x86d3d2d4L), (0xf1d4e242L), (0x68ddb3f8L), (0x1fda836eL),
(0x81be16cdL), (0xf6b9265bL), (0x6fb077e1L), (0x18b74777L),
(0x88085ae6L), (0xff0f6a70L), (0x66063bcaL), (0x11010b5cL),
(0x8f659effL), (0xf862ae69L), (0x616bffd3L), (0x166ccf45L),
(0xa00ae278L), (0xd70dd2eeL), (0x4e048354L), (0x3903b3c2L),
(0xa7672661L), (0xd06016f7L), (0x4969474dL), (0x3e6e77dbL),
(0xaed16a4aL), (0xd9d65adcL), (0x40df0b66L), (0x37d83bf0L),
(0xa9bcae53L), (0xdebb9ec5L), (0x47b2cf7fL), (0x30b5ffe9L),
(0xbdbdf21cL), (0xcabac28aL), (0x53b39330L), (0x24b4a3a6L),
(0xbad03605L), (0xcdd70693L), (0x54de5729L), (0x23d967bfL),
(0xb3667a2eL), (0xc4614ab8L), (0x5d681b02L), (0x2a6f2b94L),
(0xb40bbe37L), (0xc30c8ea1L), (0x5a05df1bL), (0x2d02ef8dL)
};

5.env.c与env.h

这两个文件主要实现了读取、解析、修改、保存环境变量的功能。

env.c

#include <stdio.h>
#include <string.h>
#include <memory.h>
#include "env.h"
#include "crc32/crc32.h"


unsigned char uboot_env[ENV_SIZE];
static env_t environment;

extern int bootNorErase(u32 ulStart, u32 uLength);
extern int bootNorWrite(u32 ulStart, u32 uLength, void *pBuffer);
extern int bootNorRead(u32 ulStart, u16 uLength, void *pBuffer);
/*
 * Check if CRC is valid and (if yes) import the environment.
 * Note that "buf" may or may not be aligned.
 */
static int import_env(const char *buf, int check)
{
	memcpy((void*)(&environment), (void*)buf, sizeof(env_t));
	if (check) {
		uint32_t crc;
		crc = environment.crc;

		if (crc32(0, (u8*)environment.data, ENV_SIZE) != crc) {
			printk("Calculate CRC = 0x%x, read CRC = 0x%x.\n", crc32(0, (u8*)environment.data, ENV_SIZE), crc);
			printk("bad CRC.\n");
			return -1; /* needed for env_load() */
		}
		return 0;
	}

	printk("Cannot import environment.\n");

	printk("import env failed\n");

	return -1;
}

// read uboot env from flash to buff
int load_env(void)
{
	int ret;
	char buf_env[CONFIG_ENV_SIZE];

	memset(&environment, 0, sizeof(env_t));
	
	bootNorRead(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, &buf_env);

	ret = import_env(buf_env, 1);
	if (!ret)
	{	
		printk("valid env\n");
		return ENV_VALID;
	}
	else
	{
		printk("Invalid env.\n");
		return ret;
	}
}

// write buff env to flash
int save_env(void)
{
	int	ret = 1;
	env_t	env_new;

	// update crc
	environment.crc = crc32(0, (u8 *) environment.data, ENV_SIZE);
	bootNorErase(CONFIG_ENV_OFFSET, BLOCK_SIZE);

	bootNorWrite(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, (void*)&environment);
	
 	printf("lhl:done\n");
	return ret;
}

/*
 * s1 is either a simple 'name', or a 'name=value' pair.
 * s2 is a 'name=value' pair.
 * If the names match, return the value of s2, else NULL.
 */
static u8 *envmatch(u8 *s1, u8 *s2)
{
	if (s1 == NULL || s2 == NULL)
		return NULL;

	while (*s1 == *s2++)
		if (*s1++ == '=')
			return s2;
	if (*s1 == '\0' && *(s2 - 1) == '=')
		return s2;
	return NULL;
}

/**
 * Search the environment for a variable.
 * Return the value, if found, or NULL, if not found.
 */
u8 * get_env(u8 *name)
{
	u8 *env, *nxt;

	for (env = environment.data; *env; env = nxt + 1) {
		char *val;

		for (nxt = env; *nxt; ++nxt) {
			if (nxt >= &environment.data[ENV_SIZE]) {
				fprintf(stderr, "## Error: "
					"environment not terminated\n");
				return NULL;
			}
		}
		val = envmatch(name, env);
		if (!val)
			continue;
		return val;
	}
	return NULL;
}

/*
 * Set/Clear a single variable in the environment.
 * This is called in sequence to update the environment
 * in RAM without updating the copy in flash after each set
 */
int write_env(u8 *name, u8 *value)
{
	int len;
	u8 *env, *nxt;
	u8 *oldval = NULL;
	int deleting, creating, overwriting;

	/*
	 * search if variable with this name already exists
	 */
	for (nxt = env = environment.data; *env; env = nxt + 1) {
		for (nxt = env; *nxt; ++nxt) {
			if (nxt >= &environment.data[ENV_SIZE]) {
				printk("## Error: environment not terminated\n");
//				errno = EINVAL;
				return -1;
			}
		}
		oldval = envmatch(name, env);
		if (oldval)
			break;
	}

	deleting = (oldval && !(value && strlen(value)));
	creating = (!oldval && (value && strlen(value)));
	overwriting = (oldval && (value && strlen(value)));

	if (deleting || overwriting) {
		if (*++nxt == '\0') {
			*env = '\0';
		} else {
			for (;;) {
				*env = *nxt++;
				if ((*env == '\0') && (*nxt == '\0'))
					break;
				++env;
			}
		}
		*++env = '\0';
	}

	/* Delete only ? */
	if (!value || !strlen(value))
		return 0;

	/*
	 * Append new definition at the end
	 */
	for (env = environment.data; *env || *(env + 1); ++env)
		;
	if (env > environment.data)
		++env;
	/*
	 * Overflow when:
	 * "name" + "=" + "val" +"\0\0"  > CUR_ENVSIZE - (env-environment)
	 */
	len = strlen(name) + 2;
	/* add '=' for first arg, ' ' for all others */
	len += strlen(value) + 1;

//	if (len > (&environment.data[ENV_SIZE] - env)) {
//		printk("Error: environment overflow, \"%s\" deleted\n", name);
//		return -1;
//	}

	while ((*env = *name++) != '\0')
		env++;
	*env = '=';
	while ((*++env = *value++) != '\0')
		;

	/* end is marked with double '\0' */
	*++env = '\0';

	return 0;
}

void uboot_printenv(void)
{
	unsigned char *env, *nxt;
//	load_env();
	
	for (env = environment.data; *env; env = nxt + 1) {
		for (nxt = env; *nxt; ++nxt) {
			if (nxt >= &environment.data[ENV_SIZE]) {
				fprintf(stderr, "## Error: "
					"environment not terminated\n");
				return;
			}
		}

		printf("%s\n", env);
	}
}



 解析:

5.1. flash读写函数

下面三个函数是我自己实现flash读写的函数,不同平台不同芯片不一样。 

extern int bootNorErase(u32 ulStart, u32 uLength);
extern int bootNorWrite(u32 ulStart, u32 uLength, void *pBuffer);
extern int bootNorRead(u32 ulStart, u16 uLength, void *pBuffer);

 5.2. 读取uboot环境变量

load_env 函数会调用import_env 函数从flash中读取环境变量到内存中,并且校验CRC码。如果打印校验失败,说明环境变量读取有问题。 

int load_env(void);
static int import_env(const char *buf, int check);

5.3. 保存uboot环境变量

 save_env 函数会重新计算内存中环境变量的 CRC 值,并更新。之后调用flash读写函数,来将内存中数据写入flash。

int save_env(void);

5.4. 获取相应的环境变量

 get_env 调用 envmatch 来查找 内存中的环境变量的相应值。

一条环境变量的结束符号是'\0',格式是: name=val 。

static u8 *envmatch(u8 *s1, u8 *s2);
u8 * get_env(u8 *name);

5.5. 修改环境变量

 write_env 用来创建、修改、删除内存中的环境变量的相应值。修改我测试过,创建删除没试过,但是应该可以的。因为他们原理一样,都是先将原来的值清除,然后写入新的值。

注意:此函数修改的是内存中的环境变量,要真正生效还需要调用函数写入存储芯片中。

int write_env(u8 *name, u8 *value);

5.6. 打印环境变量

此函数仅用来测试读取到的uboot环境变量是否正确,调用前需要先load_env,然后再调用。

void uboot_printenv(void);

env.h

#ifndef _ENV_H_
#define _ENV_H_


//#define ENV_HEADER_SIZE	(sizeof(unsigned int))
#define ENV_HEADER_SIZE	4
#define CONFIG_ENV_OFFSET	0x70000
#define CONFIG_ENV_SIZE 4096
#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
#define BLOCK_SIZE	0x10000

#define readl(addr) (*(volatile unsigned int *) (addr))
#define writel(data,addr) (*(volatile unsigned int *) (addr ) = (data))

#define readb(addr) (*(volatile unsigned char *) (addr))
#define writeb(data,addr) (*(volatile unsigned char *) (addr ) = (data))

/* Value for environment validity */
enum env_valid {
	ENV_INVALID,	/* No valid environment */
	ENV_VALID,	/* First or only environment is valid */
	ENV_REDUND,	/* Redundant environment is valid */
};

typedef struct environment_s {
	uint32_t	crc;		/* CRC32 over data bytes	*/
	unsigned char	data[ENV_SIZE]; /* Environment data		*/
} env_t;




#endif

6. 保存默认环境变量

此处代码可以写入一个默认的环境变量到存储芯片中,适合移植到不同的系统中,而不用修改编译uboot。

下面为环境变量的宏定义

#define CONFIG_DEFAULT_ENV_SETTINGS	\
			"arch=arm\0"	\
			"cpu=armv8\0"	\
			"baudrate=115200\0"	\
			"board=ft2004\0"	\
			"board_name=ft2004\0"	\
			"bootargs=console=ttyAMA0,115200 earlycon=pl011,0x28001000 root=/dev/sda2 rw\0"	\
			"eth1addr=98:0e:24:33:25:8b\0"	\
			"ethact=ethernet@2820c000\0"	\
			"ethaddr=98:0e:24:33:25:8a\0"	\
			"ipaddr=192.168.3.136\0"	\
			"gatewayip=192.168.3.1\0"	\
			"netmask=255.255.255.0\0"	\
			"serverip=192.168.3.111\0"	\
			"load_kernel=ext4load scsi 0:1 0x90100000 uImage\0"	\
			"load_fdt=ext4load scsi 0:1 0x95000000 dtb/ft2004-clock_64.dtb\0"	\
			"boot_fdt=bootm 0x90100000 -:- 0x95000000\0"	\
			"distro_bootcmd=run load_kernel; run load_fdt; run boot_fdt\0"	\
			"bootOS=cp.b 0x00600000 0xfa000000 0x600000;bootelf 0xfa000000\0"	\
			"bak_boot_OS=cp.b 0x01000000 0xfa000000 0x600000;bootelf 0xfa000000\0"	\
			"bootcmd=run bootOS\0"	\
			"bootdelay=0\0"	\
			"stderr=uart@28001000\0"	\
			"stdin=uart@28001000\0"		\
			"stdout=uart@28001000\0"	\
			"watchdog=off\0"	\
			"watchdogcnt=40000"

下面为写入存储的函数,部分声明在本文前面章节有描述。

// write default uboot envirment to flash
void saveDefaultEnv()
{
	unsigned char data[ENV_SIZE] = CONFIG_DEFAULT_ENV_SETTINGS, *env, *nxt;
	int num = 0, offset = 0;

	memset(&environment, 0, sizeof(env_t));
	for (env = data; *env; env = nxt + 1) {
		num = 0;
		for (nxt = env; *nxt; ++nxt) {
			num++;
			if (nxt >= &data[ENV_SIZE]) {
				printf("## Error: environment not terminated\n");
				return;
			}
		}
		memcpy(&environment.data[offset], env, num+1);
		offset += num+1;
	}
	save_env();
}

7. 哈希链表

uboot环境变量中似乎用到了哈希链表等数据结构。但是经过简略分析,似乎哈希链表只是影响了对uboot环境变量的修改删除等的权限。只要谨慎地读写环境变量,这部分代码不用关心移植。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值