From patchwork Wed Nov 15 07:01:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "S. Lockwood-Childs" X-Patchwork-Id: 838143 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3ycN9Z48Rhz9rxj for ; Wed, 15 Nov 2017 22:55:30 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 27940C21C35; Wed, 15 Nov 2017 11:54:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 30564C21D95; Wed, 15 Nov 2017 11:50:18 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 13875C21C45; Wed, 15 Nov 2017 06:58:58 +0000 (UTC) Received: from dent.vctlabs.com (net-cf9a4187.iis.impulse.net [207.154.65.135]) by lists.denx.de (Postfix) with ESMTPS id A5CCDC21C4C for ; Wed, 15 Nov 2017 06:58:57 +0000 (UTC) Received: by dent.vctlabs.com (Postfix, from userid 1000) id 3642E280490; Tue, 14 Nov 2017 23:01:26 -0800 (PST) Date: Tue, 14 Nov 2017 23:01:26 -0800 From: "S. Lockwood-Childs" To: u-boot@lists.denx.de Message-ID: <20171115070126.GX21009@vctlabs.com> Mail-Followup-To: "S. Lockwood-Childs" , u-boot@lists.denx.de MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) X-Mailman-Approved-At: Wed, 15 Nov 2017 11:50:14 +0000 Subject: [U-Boot] [PATCH] tools: env: Add support for direct read/write UBI volumes X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 Forward port: Signed-off-by: S. Lockwood-Childs --- tools/env/fw_env.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++- tools/env/fw_env.config | 8 ++ 2 files changed, 261 insertions(+), 2 deletions(-) diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index ab06415..867fba5 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef MTD_OLD # include @@ -34,6 +35,8 @@ # include #endif +#include + #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) @@ -122,6 +127,228 @@ static unsigned char obsolete_flag = 0; #define DEFAULT_ENV_INSTANCE_STATIC #include +#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); @@ -1022,6 +1249,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) @@ -1046,6 +1279,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) @@ -1234,7 +1473,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"); @@ -1347,8 +1587,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, @@ -1364,7 +1608,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) { diff --git a/tools/env/fw_env.config b/tools/env/fw_env.config index 7916ebd..053895a 100644 --- a/tools/env/fw_env.config +++ b/tools/env/fw_env.config @@ -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