mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +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/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifdef MTD_OLD
|
||||
# include <stdint.h>
|
||||
|
@ -34,6 +35,8 @@
|
|||
# include <mtd/mtd-user.h>
|
||||
#endif
|
||||
|
||||
#include <mtd/ubi-user.h>
|
||||
|
||||
#include "fw_env_private.h"
|
||||
#include "fw_env.h"
|
||||
|
||||
|
@ -58,6 +61,7 @@ struct envdev_s {
|
|||
ulong erase_size; /* device erase size */
|
||||
ulong env_sectors; /* number of environment sectors */
|
||||
uint8_t mtd_type; /* type of the MTD device */
|
||||
int is_ubi; /* set if we use UBI volume */
|
||||
};
|
||||
|
||||
static struct envdev_s envdevices[2] =
|
||||
|
@ -76,6 +80,7 @@ static int dev_current;
|
|||
#define DEVESIZE(i) envdevices[(i)].erase_size
|
||||
#define ENVSECTORS(i) envdevices[(i)].env_sectors
|
||||
#define DEVTYPE(i) envdevices[(i)].mtd_type
|
||||
#define IS_UBI(i) envdevices[(i)].is_ubi
|
||||
|
||||
#define CUR_ENVSIZE ENVSIZE(dev_current)
|
||||
|
||||
|
@ -120,6 +125,228 @@ static unsigned char obsolete_flag = 0;
|
|||
#define DEFAULT_ENV_INSTANCE_STATIC
|
||||
#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 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));
|
||||
#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,
|
||||
CUR_ENVSIZE);
|
||||
if (rc < 0)
|
||||
|
@ -984,6 +1217,12 @@ static int flash_read (int fd)
|
|||
{
|
||||
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,
|
||||
DEVOFFSET(dev_current));
|
||||
if (rc != CUR_ENVSIZE)
|
||||
|
@ -1165,7 +1404,8 @@ int fw_env_open(struct env_opts *opts)
|
|||
DEVTYPE(!dev_current) == MTD_UBIVOLUME) {
|
||||
environment.flag_scheme = FLAG_INCREMENTAL;
|
||||
} 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;
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
struct stat st;
|
||||
int32_t lnum = 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);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr,
|
||||
|
@ -1288,7 +1532,14 @@ static int check_device_config(int dev)
|
|||
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;
|
||||
rc = ioctl(fd, MEMGETINFO, &mtdinfo);
|
||||
if (rc < 0) {
|
||||
|
|
8
tools/env/fw_env.config
vendored
8
tools/env/fw_env.config
vendored
|
@ -28,3 +28,11 @@
|
|||
|
||||
# VFAT example
|
||||
#/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 a new issue