tools: env: Add support for direct read/write UBI volumes
Up to now we were able to read/write environment data from/to UBI volumes only indirectly by gluebi driver. This driver creates NAND MTD on top of UBI volumes, which is quite a workaroung for this use case. Add support for direct read/write UBI volumes in order to not use obsolete gluebi driver. Forward-ported from this patch: http://patchwork.ozlabs.org/patch/619305/ Original patch: Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com> Forward port: Signed-off-by: S. Lockwood-Childs <sjl@vctlabs.com>
This commit is contained in:
		
							parent
							
								
									d36a27adbb
								
							
						
					
					
						commit
						34255b92e6
					
				|  | @ -25,6 +25,7 @@ | ||||||
| #include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  | #include <dirent.h> | ||||||
| 
 | 
 | ||||||
| #ifdef MTD_OLD | #ifdef MTD_OLD | ||||||
| # include <stdint.h> | # include <stdint.h> | ||||||
|  | @ -34,6 +35,8 @@ | ||||||
| # include <mtd/mtd-user.h> | # include <mtd/mtd-user.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #include <mtd/ubi-user.h> | ||||||
|  | 
 | ||||||
| #include "fw_env_private.h" | #include "fw_env_private.h" | ||||||
| #include "fw_env.h" | #include "fw_env.h" | ||||||
| 
 | 
 | ||||||
|  | @ -58,6 +61,7 @@ struct envdev_s { | ||||||
| 	ulong erase_size;		/* device erase size */ | 	ulong erase_size;		/* device erase size */ | ||||||
| 	ulong env_sectors;		/* number of environment sectors */ | 	ulong env_sectors;		/* number of environment sectors */ | ||||||
| 	uint8_t mtd_type;		/* type of the MTD device */ | 	uint8_t mtd_type;		/* type of the MTD device */ | ||||||
|  | 	int is_ubi;			/* set if we use UBI volume */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static struct envdev_s envdevices[2] = | static struct envdev_s envdevices[2] = | ||||||
|  | @ -76,6 +80,7 @@ static int dev_current; | ||||||
| #define DEVESIZE(i)   envdevices[(i)].erase_size | #define DEVESIZE(i)   envdevices[(i)].erase_size | ||||||
| #define ENVSECTORS(i) envdevices[(i)].env_sectors | #define ENVSECTORS(i) envdevices[(i)].env_sectors | ||||||
| #define DEVTYPE(i)    envdevices[(i)].mtd_type | #define DEVTYPE(i)    envdevices[(i)].mtd_type | ||||||
|  | #define IS_UBI(i)     envdevices[(i)].is_ubi | ||||||
| 
 | 
 | ||||||
| #define CUR_ENVSIZE ENVSIZE(dev_current) | #define CUR_ENVSIZE ENVSIZE(dev_current) | ||||||
| 
 | 
 | ||||||
|  | @ -120,6 +125,228 @@ static unsigned char obsolete_flag = 0; | ||||||
| #define DEFAULT_ENV_INSTANCE_STATIC | #define DEFAULT_ENV_INSTANCE_STATIC | ||||||
| #include <env_default.h> | #include <env_default.h> | ||||||
| 
 | 
 | ||||||
|  | #define UBI_DEV_START "/dev/ubi" | ||||||
|  | #define UBI_SYSFS "/sys/class/ubi" | ||||||
|  | #define UBI_VOL_NAME_PATT "ubi%d_%d" | ||||||
|  | 
 | ||||||
|  | static int is_ubi_devname(const char *devname) | ||||||
|  | { | ||||||
|  | 	return !strncmp(devname, UBI_DEV_START, sizeof(UBI_DEV_START) - 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ubi_check_volume_sysfs_name(const char *volume_sysfs_name, | ||||||
|  | 				       const char *volname) | ||||||
|  | { | ||||||
|  | 	char path[256]; | ||||||
|  | 	FILE *file; | ||||||
|  | 	char *name; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	strcpy(path, UBI_SYSFS "/"); | ||||||
|  | 	strcat(path, volume_sysfs_name); | ||||||
|  | 	strcat(path, "/name"); | ||||||
|  | 
 | ||||||
|  | 	file = fopen(path, "r"); | ||||||
|  | 	if (!file) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	ret = fscanf(file, "%ms", &name); | ||||||
|  | 	fclose(file); | ||||||
|  | 	if (ret <= 0 || !name) { | ||||||
|  | 		fprintf(stderr, | ||||||
|  | 			"Failed to read from file %s, ret = %d, name = %s\n", | ||||||
|  | 			path, ret, name); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!strcmp(name, volname)) { | ||||||
|  | 		free(name); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	free(name); | ||||||
|  | 
 | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ubi_get_volnum_by_name(int devnum, const char *volname) | ||||||
|  | { | ||||||
|  | 	DIR *sysfs_ubi; | ||||||
|  | 	struct dirent *dirent; | ||||||
|  | 	int ret; | ||||||
|  | 	int tmp_devnum; | ||||||
|  | 	int volnum; | ||||||
|  | 
 | ||||||
|  | 	sysfs_ubi = opendir(UBI_SYSFS); | ||||||
|  | 	if (!sysfs_ubi) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG | ||||||
|  | 	fprintf(stderr, "Looking for volume name \"%s\"\n", volname); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	while (1) { | ||||||
|  | 		dirent = readdir(sysfs_ubi); | ||||||
|  | 		if (!dirent) | ||||||
|  | 			return -1; | ||||||
|  | 
 | ||||||
|  | 		ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT, | ||||||
|  | 			     &tmp_devnum, &volnum); | ||||||
|  | 		if (ret == 2 && devnum == tmp_devnum) { | ||||||
|  | 			if (ubi_check_volume_sysfs_name(dirent->d_name, | ||||||
|  | 							volname) == 0) | ||||||
|  | 				return volnum; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ubi_get_devnum_by_devname(const char *devname) | ||||||
|  | { | ||||||
|  | 	int devnum; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = sscanf(devname + sizeof(UBI_DEV_START) - 1, "%d", &devnum); | ||||||
|  | 	if (ret != 1) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	return devnum; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const char *ubi_get_volume_devname(const char *devname, | ||||||
|  | 					  const char *volname) | ||||||
|  | { | ||||||
|  | 	char *volume_devname; | ||||||
|  | 	int volnum; | ||||||
|  | 	int devnum; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	devnum = ubi_get_devnum_by_devname(devname); | ||||||
|  | 	if (devnum < 0) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	volnum = ubi_get_volnum_by_name(devnum, volname); | ||||||
|  | 	if (volnum < 0) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	ret = asprintf(&volume_devname, "%s_%d", devname, volnum); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG | ||||||
|  | 	fprintf(stderr, "Found ubi volume \"%s:%s\" -> %s\n", | ||||||
|  | 		devname, volname, volume_devname); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return volume_devname; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ubi_check_dev(unsigned int dev_id) | ||||||
|  | { | ||||||
|  | 	char *devname = (char *)DEVNAME(dev_id); | ||||||
|  | 	char *pname; | ||||||
|  | 	const char *volname = NULL; | ||||||
|  | 	const char *volume_devname; | ||||||
|  | 
 | ||||||
|  | 	if (!is_ubi_devname(DEVNAME(dev_id))) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	IS_UBI(dev_id) = 1; | ||||||
|  | 
 | ||||||
|  | 	for (pname = devname; *pname != '\0'; pname++) { | ||||||
|  | 		if (*pname == ':') { | ||||||
|  | 			*pname = '\0'; | ||||||
|  | 			volname = pname + 1; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (volname) { | ||||||
|  | 		/* Let's find real volume device name */ | ||||||
|  | 		volume_devname = ubi_get_volume_devname(devname, volname); | ||||||
|  | 		if (!volume_devname) { | ||||||
|  | 			fprintf(stderr, "Didn't found ubi volume \"%s\"\n", | ||||||
|  | 				volname); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		free(devname); | ||||||
|  | 		DEVNAME(dev_id) = volume_devname; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ubi_update_start(int fd, int64_t bytes) | ||||||
|  | { | ||||||
|  | 	if (ioctl(fd, UBI_IOCVOLUP, &bytes)) | ||||||
|  | 		return -1; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ubi_read(int fd, void *buf, size_t count) | ||||||
|  | { | ||||||
|  | 	ssize_t ret; | ||||||
|  | 
 | ||||||
|  | 	while (count > 0) { | ||||||
|  | 		ret = read(fd, buf, count); | ||||||
|  | 		if (ret > 0) { | ||||||
|  | 			count -= ret; | ||||||
|  | 			buf += ret; | ||||||
|  | 
 | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (ret == 0) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * Happens in case of too short volume data size. If we | ||||||
|  | 			 * return error status we will fail it will be treated | ||||||
|  | 			 * as UBI device error. | ||||||
|  | 			 * | ||||||
|  | 			 * Leave catching this error to CRC check. | ||||||
|  | 			 */ | ||||||
|  | 			fprintf(stderr, "Warning: end of data on ubi volume\n"); | ||||||
|  | 			return 0; | ||||||
|  | 		} else if (errno == EBADF) { | ||||||
|  | 			/*
 | ||||||
|  | 			 * Happens in case of corrupted volume. The same as | ||||||
|  | 			 * above, we cannot return error now, as we will still | ||||||
|  | 			 * be able to successfully write environment later. | ||||||
|  | 			 */ | ||||||
|  | 			fprintf(stderr, "Warning: corrupted volume?\n"); | ||||||
|  | 			return 0; | ||||||
|  | 		} else if (errno == EINTR) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		fprintf(stderr, "Cannot read %u bytes from ubi volume, %s\n", | ||||||
|  | 			(unsigned int)count, strerror(errno)); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int ubi_write(int fd, const void *buf, size_t count) | ||||||
|  | { | ||||||
|  | 	ssize_t ret; | ||||||
|  | 
 | ||||||
|  | 	while (count > 0) { | ||||||
|  | 		ret = write(fd, buf, count); | ||||||
|  | 		if (ret <= 0) { | ||||||
|  | 			if (ret < 0 && errno == EINTR) | ||||||
|  | 				continue; | ||||||
|  | 
 | ||||||
|  | 			fprintf(stderr, "Cannot write %u bytes to ubi volume\n", | ||||||
|  | 				(unsigned int)count); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		count -= ret; | ||||||
|  | 		buf += ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int flash_io (int mode); | static int flash_io (int mode); | ||||||
| static int parse_config(struct env_opts *opts); | static int parse_config(struct env_opts *opts); | ||||||
| 
 | 
 | ||||||
|  | @ -960,6 +1187,12 @@ static int flash_write (int fd_current, int fd_target, int dev_target) | ||||||
| 		DEVOFFSET (dev_target), DEVNAME (dev_target)); | 		DEVOFFSET (dev_target), DEVNAME (dev_target)); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | 	if (IS_UBI(dev_target)) { | ||||||
|  | 		if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0) | ||||||
|  | 			return 0; | ||||||
|  | 		return ubi_write(fd_target, environment.image, CUR_ENVSIZE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	rc = flash_write_buf(dev_target, fd_target, environment.image, | 	rc = flash_write_buf(dev_target, fd_target, environment.image, | ||||||
| 			     CUR_ENVSIZE); | 			     CUR_ENVSIZE); | ||||||
| 	if (rc < 0) | 	if (rc < 0) | ||||||
|  | @ -984,6 +1217,12 @@ static int flash_read (int fd) | ||||||
| { | { | ||||||
| 	int rc; | 	int rc; | ||||||
| 
 | 
 | ||||||
|  | 	if (IS_UBI(dev_current)) { | ||||||
|  | 		DEVTYPE(dev_current) = MTD_ABSENT; | ||||||
|  | 
 | ||||||
|  | 		return ubi_read(fd, environment.image, CUR_ENVSIZE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, | 	rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, | ||||||
| 			    DEVOFFSET(dev_current)); | 			    DEVOFFSET(dev_current)); | ||||||
| 	if (rc != CUR_ENVSIZE) | 	if (rc != CUR_ENVSIZE) | ||||||
|  | @ -1165,7 +1404,8 @@ int fw_env_open(struct env_opts *opts) | ||||||
| 			   DEVTYPE(!dev_current) == MTD_UBIVOLUME) { | 			   DEVTYPE(!dev_current) == MTD_UBIVOLUME) { | ||||||
| 			environment.flag_scheme = FLAG_INCREMENTAL; | 			environment.flag_scheme = FLAG_INCREMENTAL; | ||||||
| 		} else if (DEVTYPE(dev_current) == MTD_ABSENT && | 		} else if (DEVTYPE(dev_current) == MTD_ABSENT && | ||||||
| 			   DEVTYPE(!dev_current) == MTD_ABSENT) { | 			   DEVTYPE(!dev_current) == MTD_ABSENT && | ||||||
|  | 			   IS_UBI(dev_current) == IS_UBI(!dev_current)) { | ||||||
| 			environment.flag_scheme = FLAG_INCREMENTAL; | 			environment.flag_scheme = FLAG_INCREMENTAL; | ||||||
| 		} else { | 		} else { | ||||||
| 			fprintf (stderr, "Incompatible flash types!\n"); | 			fprintf (stderr, "Incompatible flash types!\n"); | ||||||
|  | @ -1271,8 +1511,12 @@ int fw_env_close(struct env_opts *opts) | ||||||
| static int check_device_config(int dev) | static int check_device_config(int dev) | ||||||
| { | { | ||||||
| 	struct stat st; | 	struct stat st; | ||||||
|  | 	int32_t lnum = 0; | ||||||
| 	int fd, rc = 0; | 	int fd, rc = 0; | ||||||
| 
 | 
 | ||||||
|  | 	/* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */ | ||||||
|  | 	ubi_check_dev(dev); | ||||||
|  | 
 | ||||||
| 	fd = open(DEVNAME(dev), O_RDONLY); | 	fd = open(DEVNAME(dev), O_RDONLY); | ||||||
| 	if (fd < 0) { | 	if (fd < 0) { | ||||||
| 		fprintf(stderr, | 		fprintf(stderr, | ||||||
|  | @ -1288,7 +1532,14 @@ static int check_device_config(int dev) | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (S_ISCHR(st.st_mode)) { | 	if (IS_UBI(dev)) { | ||||||
|  | 		rc = ioctl(fd, UBI_IOCEBISMAP, &lnum); | ||||||
|  | 		if (rc < 0) { | ||||||
|  | 			fprintf(stderr, "Cannot get UBI information for %s\n", | ||||||
|  | 				DEVNAME(dev)); | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 	} else if (S_ISCHR(st.st_mode)) { | ||||||
| 		struct mtd_info_user mtdinfo; | 		struct mtd_info_user mtdinfo; | ||||||
| 		rc = ioctl(fd, MEMGETINFO, &mtdinfo); | 		rc = ioctl(fd, MEMGETINFO, &mtdinfo); | ||||||
| 		if (rc < 0) { | 		if (rc < 0) { | ||||||
|  |  | ||||||
|  | @ -28,3 +28,11 @@ | ||||||
| 
 | 
 | ||||||
| # VFAT example | # VFAT example | ||||||
| #/boot/uboot.env	0x0000          0x4000 | #/boot/uboot.env	0x0000          0x4000 | ||||||
|  | 
 | ||||||
|  | # UBI volume | ||||||
|  | #/dev/ubi0_0		0x0		0x1f000		0x1f000 | ||||||
|  | #/dev/ubi0_1		0x0		0x1f000		0x1f000 | ||||||
|  | 
 | ||||||
|  | # UBI volume by name | ||||||
|  | #/dev/ubi0:env		0x0		0x1f000		0x1f000 | ||||||
|  | #/dev/ubi0:env-redund	0x0		0x1f000		0x1f000 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue