mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-17 12:41:32 +00:00
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
2 changed files with 261 additions and 2 deletions
255
tools/env/fw_env.c
vendored
255
tools/env/fw_env.c
vendored
|
@ -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) {
|
||||||
|
|
8
tools/env/fw_env.config
vendored
8
tools/env/fw_env.config
vendored
|
@ -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…
Add table
Reference in a new issue