From: Andrey Smirnov <andrew.smirnov@gmail.com>
To: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Cory Tusar <cory.tusar@zii.aero>,
Barebox List <barebox@lists.infradead.org>
Subject: Re: [PATCH 4/7] misc: Add a driver to expose U-Boot environment variable data
Date: Tue, 28 May 2019 18:19:51 -0700 [thread overview]
Message-ID: <CAHQ1cqE+ghjtyvBGzORzKXTerQbEQ1c-r0yrSAu3-JwEcz4c8A@mail.gmail.com> (raw)
In-Reply-To: <20190528093429.5lsf74vvrkngia2x@pengutronix.de>
On Tue, May 28, 2019 at 2:34 AM Sascha Hauer <s.hauer@pengutronix.de> wrote:
>
> On Mon, May 27, 2019 at 01:18:50PM -0700, Andrey Smirnov wrote:
> > Add a driver to expose U-Boot environment variable data as a single
> > mmap-able device. Not very useful on its own, it is a crucial
> > low-level plumbing needed by filesystem driver introduced in the
> > following commit.
>
> It wasn't clear to me why we need this driver at all, so it might be
> worth adding a comment that this is for handling redundant env.
>
Was it before of after you read the Kconfig help that talks about it?
Asking to see if copying that text into commit would be enough, or if
should write a more detailed explanation.
> > +static int ubootvar_flush(struct cdev *cdev)
> > +{
> > + struct device_d *dev = cdev->dev;
> > + struct ubootvar_data *ubdata = dev->priv;
> > + const char *path = ubdata->path[!ubdata->current];
> > + uint32_t crc = 0xffffffff;
> > + struct resource *mem;
> > + resource_size_t size;
> > + const void *data;
> > + int fd, ret = 0;
> > +
> > + mem = dev_get_resource(dev, IORESOURCE_MEM, 0);
> > + if (IS_ERR(mem)) {
> > + dev_err(dev, "Failed to get associated memory resource\n");
> > + return PTR_ERR(mem);
> > + }
>
> Resources of IORESOURCE_MEM are meant to describe an area in the
> physical memory space. You shouldn't abuse it to describe some
> arbitrarily allocated memory area. Why not simply put the pointers into
> struct ubootvar_data?
>
> > +
> > + fd = open(path, O_WRONLY);
> > + if (fd < 0) {
> > + dev_err(dev, "Failed to open %s\n", path);
> > + return -errno;
> > + }
> > + /*
> > + * FIXME: This code needs to do a proper protect/unprotect and
> > + * erase calls to work on MTD devices
> > + */
> > +
> > + /*
> > + * Write a dummy CRC first as a way of invalidating the
> > + * environment in case we fail mid-flushing
> > + */
> > + if (write_full(fd, &crc, sizeof(crc)) != sizeof(crc)) {
> > + dev_err(dev, "Failed to write dummy CRC\n");
> > + ret = -errno;
> > + goto close_fd;
> > + }
> > +
> > + if (ubdata->count > 1) {
> > + /*
> > + * FIXME: This assumes FLAG_INCREMENTAL
> > + */
> > + const uint8_t flag = ++ubdata->flag;
> > +
> > + if (write_full(fd, &flag, sizeof(flag)) != sizeof(flag)) {
> > + dev_dbg(dev, "Failed to write flag\n");
> > + ret = -errno;
> > + goto close_fd;
> > + }
> > + }
> > +
> > + data = (const void *)mem->start;
> > + size = resource_size(mem);
> > +
> > + /*
> > + * Write out and flush all of the new environement data
> > + */
>
> s/environement/environment/
>
> > + if (write_full(fd, data, size) != size) {
> > + dev_dbg(dev, "Failed to write data\n");
> > + ret = -errno;
> > + goto close_fd;
> > + }
> > +
> > + if (flush(fd)) {
> > + dev_dbg(dev, "Failed to flush written data\n");
> > + ret = -errno;
> > + goto close_fd;
> > + }
> > + /*
> > + * Now that all of the environment data is out, we can go back
> > + * to the start of the block and write correct CRC, to finish
> > + * the processs.
> > + */
> > + if (lseek(fd, 0, SEEK_SET) != 0) {
> > + dev_dbg(dev, "lseek() failed\n");
> > + ret = -errno;
> > + goto close_fd;
> > + }
> > +
> > + crc = crc32(0, data, size);
> > + if (write_full(fd, &crc, sizeof(crc)) != sizeof(crc)) {
> > + dev_dbg(dev, "Failed to write valid CRC\n");
> > + ret = -errno;
> > + goto close_fd;
> > + }
> > + /*
> > + * Now that we've successfuly written new enviroment blob out,
> > + * swtich current partition.
>
> s/swtich/switch/
>
> > + */
> > + ubdata->current = !ubdata->current;
> > +
> > +close_fd:
> > + close(fd);
> > + return ret;
> > +}
> > +
> > +static struct cdev_operations ubootvar_ops = {
> > + .read = mem_read,
> > + .write = mem_write,
> > + .memmap = generic_memmap_rw,
>
> Ok, now I understand this struct resource thingy, you want to reuse
> mem_read and friends. Given that these functions are oneliners to
> implement I still suggest implementing them.
>
OK, I'll have to make mem_copy() visible globally for them to remain
one-liners, but that should be doable.
> > + .flush = ubootvar_flush,
> > +};
> > +
> > +static void ubootenv_info(struct device_d *dev)
> > +{
> > + struct ubootvar_data *ubdata = dev->priv;
> > +
> > + printf("Current environment copy: device-path-%d\n", ubdata->current);
> > +}
> > +
> > +static int ubootenv_probe(struct device_d *dev)
> > +{
> > + struct ubootvar_data *ubdata;
> > + unsigned int crc_ok = 0;
> > + int ret, i, count = 0;
> > + uint32_t crc[2];
> > + uint8_t flag[2];
> > + size_t size[2];
> > + void *blob[2] = { NULL, NULL };
> > + uint8_t *data[2];
> > +
> > + /*
> > + * FIXME: Flag scheme is determined by the type of underlined
> > + * non-volatible device, so it should probably come from
> > + * Device Tree binding. Currently we just assume incremental
> > + * scheme since that is what is used on SD/eMMC devices.
> > + */
> > + enum ubootvar_flag_scheme flag_scheme = FLAG_INCREMENTAL;
> > +
> > + ubdata = xzalloc(sizeof(*ubdata));
> > +
> > + ret = of_find_path(dev->device_node, "device-path-0",
> > + &ubdata->path[0],
> > + OF_FIND_PATH_FLAGS_BB);
>
> Maybe allow "device-path" without -x suffix when the U-Boot environment
> is not redundant?
>
I was modelling this after "pinctrl" property in which always has
trailing "-0". I can add the support for "device-path" in v2, though.
> > + if (ret) {
> > + dev_err(dev, "Failed to find first device\n");
> > + goto out;
> > + }
> > +
> > + count++;
> > +
> > + if (!of_find_path(dev->device_node, "device-path-1",
> > + &ubdata->path[1],
> > + OF_FIND_PATH_FLAGS_BB)) {
> > + count++;
> > + } else {
> > + /*
> > + * If there's no redundant environment partition we
> > + * configure both paths to point to the same device,
> > + * so that writing logic could stay the same for both
> > + * redundant and non-redundant cases
> > + */
> > + ubdata->path[1] = ubdata->path[0];
>
> ubdata->path[0] contains an allocated string, so we must be careful when
> freeing this. You don't free it at all, so there's no problem currently.
> I think you should either free it correctly in the error path or make
> sure that both ubdata->path[0] and ubdata->path[1] contain allocated
> strings.
>
Oh, missed the fact that those strings are allocated. Will fix in v2.
> > + }
> > +
> > + for (i = 0; i < count; i++) {
> > + data[i] = blob[i] = read_file(ubdata->path[i], &size[i]);
> > + if (!blob[i]) {
> > + dev_err(dev, "Failed to read U-Boot environment\n");
> > + ret = -EIO;
> > + goto out;
> > + }
> > +
> > + crc[i] = *(uint32_t *)data[i];
> > +
> > + size[i] -= sizeof(uint32_t);
> > + data[i] += sizeof(uint32_t);
> > +
> > + if (count > 1) {
> > + /*
> > + * When used in primary/redundant
> > + * configuration, environment header has an
> > + * additional "flag" byte
> > + */
> > + flag[i] = *data[i];
> > + size[i] -= sizeof(uint8_t);
> > + data[i] += sizeof(uint8_t);
> > + }
> > +
> > + crc_ok |= (crc32(0, data[i], size[i]) == crc[i]) << i;
> > + }
> > +
> > + switch (crc_ok) {
> > + case 0b00:
> > + ret = -EINVAL;
> > + dev_err(dev, "Bad CRC, can't continue further\n");
> > + goto out;
>
> So when there's no valid U-Boot env is found (erased for example) then
> this driver errors out and we won't have a chance to ever write
> something there. Is this intended?
>
The only use-case we have for this is to communicate data to already
existing U-Boot installation with a valid environment data, so the
scenario you are talking about never came up. I guess I can change the
code to zero the buffered data out and proceed, so that FS layer would
perceive this as an empty environment.
> > + case 0b11:
> > + /*
> > + * Both partition are valid, so we need to examine
> > + * flags to determine which one to use as current
> > + */
> > + switch (flag_scheme) {
> > + case FLAG_INCREMENTAL:
> > + if ((flag[0] == 0xff && flag[1] == 0) ||
> > + (flag[1] == 0xff && flag[0] == 0)) {
> > + /*
> > + * When flag overflow happens current
> > + * partition is the one whose counter
> > + * reached zero first. That is if
> > + * flag[1] == 0 is true (1), then i
> > + * would be 1 as well
> > + */
> > + i = flag[1] == 0;
> > + } else {
> > + /*
> > + * In no-overflow case the partition
> > + * with higher flag value is
> > + * considered current
> > + */
> > + i = flag[1] > flag[0];
> > + }
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + dev_err(dev, "Unknown flag scheme %u\n", flag_scheme);
> > + goto out;
> > + }
> > + break;
> > + default:
> > + /*
> > + * Only one partition is valid, so the choice of the
> > + * current one is obvious
> > + */
> > + i = __ffs(crc_ok);
>
> 'i' changes its meaning from a loop counter to the partition currently
> used. This makes this code harder to read, could you use a separate
> variable for the currently used partition?
>
Sure, will do in v2.
> > + break;
> > + };
> > +
> > + ret = device_add_resource(dev, "data", (resource_size_t)data[i],
> > + size[i], IORESOURCE_MEM);
> > + if (ret) {
> > + dev_err(dev, "Failed to add resource\n");
> > + goto out;
> > + }
> > +
> > + ubdata->cdev.name = basprintf("ubootvar%d",
> > + cdev_find_free_index("ubootvar"));
> > + ubdata->cdev.size = size[i];
> > + ubdata->cdev.ops = &ubootvar_ops;
> > + ubdata->cdev.dev = dev;
> > + ubdata->cdev.filetype = filetype_ubootvar;
> > + ubdata->current = i;
> > + ubdata->count = count;
> > + ubdata->flag = flag[i];
> > +
> > + dev->priv = ubdata;
> > +
> > + ret = devfs_create(&ubdata->cdev);
> > + if (ret) {
> > + dev_err(dev, "Failed to create corresponding cdev\n");
> > + goto out;
> > + }
> > +
> > + cdev_create_default_automount(&ubdata->cdev);
> > +
> > + if (count > 1) {
> > + /*
> > + * We won't be using read data from redundant
> > + * parttion, so we may as well free at this point
> > + */
> > + free(blob[!i]);
> > + }
> > +
> > + dev->info = ubootenv_info;
> > +
> > + return 0;
> > +out:
> > + for (i = 0; i < count; i++)
> > + free(blob[i]);
> > +
> > + free(ubdata);
> > +
> > + return ret;
> > +}
> > +
> > +static struct of_device_id ubootenv_dt_ids[] = {
> > + {
> > + .compatible = "barebox,uboot-environment",
>
> Please add
> Documentation/devicetree/bindings/barebox/barebox,uboot-environment.rst
>
Will do.
Thanks,
Andrey Smirnov
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
next prev parent reply other threads:[~2019-05-29 1:20 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-27 20:18 [PATCH 0/7] U-Boot environment data as a filesystem Andrey Smirnov
2019-05-27 20:18 ` [PATCH 1/7] filetype: Add "U-Boot environmemnt variable data" filetype Andrey Smirnov
2019-05-27 20:18 ` [PATCH 2/7] filetype: Allow specifying cdev's filetype explicitly Andrey Smirnov
2019-05-27 20:18 ` [PATCH 3/7] drivers: Introduce late_platform_driver() Andrey Smirnov
2019-05-27 20:18 ` [PATCH 4/7] misc: Add a driver to expose U-Boot environment variable data Andrey Smirnov
2019-05-28 9:34 ` Sascha Hauer
2019-05-29 1:19 ` Andrey Smirnov [this message]
2019-05-29 5:25 ` Sascha Hauer
2019-05-27 20:18 ` [PATCH 5/7] fs: Add a driver to access U-Boot environment variables Andrey Smirnov
2019-05-28 9:56 ` Sascha Hauer
2019-05-29 2:05 ` Andrey Smirnov
2019-05-29 6:08 ` Sascha Hauer
2019-05-27 20:18 ` [PATCH 6/7] ARM: rdu2: Add U-Boot environment partitions Andrey Smirnov
2019-05-28 9:57 ` Sascha Hauer
2019-05-29 0:56 ` Andrey Smirnov
2019-05-27 20:18 ` [PATCH 7/7] ARM: rdu1: Add U-Boot environment partition Andrey Smirnov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAHQ1cqE+ghjtyvBGzORzKXTerQbEQ1c-r0yrSAu3-JwEcz4c8A@mail.gmail.com \
--to=andrew.smirnov@gmail.com \
--cc=barebox@lists.infradead.org \
--cc=cory.tusar@zii.aero \
--cc=s.hauer@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox