mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* Re: [PATCH 2/2] ARM: Fix calling of arm_mem_barebox_image()
From: Trent Piepho @ 2016-09-22  0:35 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: Barebox List
In-Reply-To: <20160915071008.tbpw5d222h2chd5w@pengutronix.de>

On Thu, 2016-09-15 at 09:10 +0200, Sascha Hauer wrote:
> On Wed, Sep 14, 2016 at 06:27:04PM +0000, Trent Piepho wrote:
> > On Wed, 2016-09-14 at 10:21 +0200, Sascha Hauer wrote:
> > > arm_mem_barebox_image() is used to pick a suitable place where to
> > > put the final image to. This is called from both the PBL uncompression
> > > code and also from the final image. To make it work properly it is
> > > crucial that it's called with the same arguments both times. Currently
> > 
> > This code has changed since I was working with it, but wouldn't
> > arm_mem_barebox_image() returning a different value from when the PBL
> > code calls it versus barebox_non_pbl_start() just result in an
> > unnecessary relocation of the uncompressed barebox from the PBL's choice
> > to the main barebox choice?
> 
> That may work when both regions do not overlap.

Ah, good point.  I suppose barebox could check that it doesn't try to
relocate on top of itself while running.


> I already tried. Somehow I didn't like the result that much, see the
> patch below. The patch also still misses the single pbl handling.

Here's what I was thinking.  multi and single pbl should work, getting
the size (data+bss) from the compressed data's size word.  Non-pbl and
PBL main barebox know the data via the linker symbols.

From: Trent Piepho <tpiepho@kymetacorp.com>
Date: Sat, 17 Sep 2016 16:24:59 -0700
Subject: [PATCH] Include BSS in the size appended to compressed barebox

In a PBL config the compressed main barebox image includes the
uncompressed size as a LE 32-bit word at the end of the compressed
image.  This allows the pbl to know how much space the main barebox
will need.  But this value alone is not correct, as the image also
needs BSS space which, being all zero, is not actually stored in the
compressed image.

While the BSS space could be estimated, it is important that both the
PBL and the main barebox use the same value.

To fix this, when making the barebox.z compressed data for the PBL,
include BSS in the size word.  Now the PBL knows the size it needs.

The main boxbox image (both in PBL and non-PBL modes) already knows
the correct values as they are embedded in the binary directly by the
linker, and are used elsewhere like setup_c() and
mem_malloc_resource().
---
 arch/arm/cpu/start-pbl.c  |  7 +++++--
 arch/arm/cpu/uncompress.c |  8 ++++++--
 images/Makefile           |  4 ++++
 scripts/Makefile.lib      | 23 ++++++++++-------------
 4 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/arch/arm/cpu/start-pbl.c b/arch/arm/cpu/start-pbl.c
index f723edc..1869a96 100644
--- a/arch/arm/cpu/start-pbl.c
+++ b/arch/arm/cpu/start-pbl.c
@@ -28,6 +28,7 @@
 #include <asm/sections.h>
 #include <asm/pgtable.h>
 #include <asm/cache.h>
+#include <asm/unaligned.h>
 
 #include "mmu-early.h"
 
@@ -49,7 +50,7 @@ __noreturn void barebox_single_pbl_start(unsigned long membase,
 		unsigned long memsize, void *boarddata)
 {
 	uint32_t offset;
-	uint32_t pg_start, pg_end, pg_len;
+	uint32_t pg_start, pg_end, pg_len, barebox_mem_len;
 	void __noreturn (*barebox)(unsigned long, unsigned long, void *);
 	uint32_t endmem = membase + memsize;
 	unsigned long barebox_base;
@@ -63,9 +64,11 @@ __noreturn void barebox_single_pbl_start(unsigned long membase,
 	pg_start = (uint32_t)&input_data - offset;
 	pg_end = (uint32_t)&input_data_end - offset;
 	pg_len = pg_end - pg_start;
+	/* Size word at end of image includes space for BSS */
+	barebox_mem_len = get_unaligned((const u32 *)(pg_start + pg_len - 4));
 
 	if (IS_ENABLED(CONFIG_RELOCATABLE))
-		barebox_base = arm_mem_barebox_image(membase, endmem, pg_len);
+		barebox_base = arm_mem_barebox_image(membase, endmem, barebox_mem_len);
 	else
 		barebox_base = TEXT_BASE;
 
diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c
index b8e2e9f..9bec77f 100644
--- a/arch/arm/cpu/uncompress.c
+++ b/arch/arm/cpu/uncompress.c
@@ -29,6 +29,7 @@
 #include <asm/sections.h>
 #include <asm/pgtable.h>
 #include <asm/cache.h>
+#include <asm/unaligned.h>
 
 #include <debug_ll.h>
 
@@ -44,7 +45,7 @@ static int __attribute__((__used__))
 void __noreturn barebox_multi_pbl_start(unsigned long membase,
 		unsigned long memsize, void *boarddata)
 {
-	uint32_t pg_len;
+	uint32_t pg_len, barebox_mem_len;
 	void __noreturn (*barebox)(unsigned long, unsigned long, void *);
 	uint32_t endmem = membase + memsize;
 	unsigned long barebox_base;
@@ -69,13 +70,16 @@ void __noreturn barebox_multi_pbl_start(unsigned long membase,
 	/*
 	 * image_end is the first location after the executable. It contains
 	 * the size of the appended compressed binary followed by the binary.
+	 * The final word of the compressed binary is the space (uncompressed
+	 * image plus bss room) needed by barebox.
 	 */
 	pg_start = image_end + 1;
 	pg_len = *(image_end);
+	barebox_mem_len = get_unaligned((const u32 *)(pg_start + pg_len - 4));
 
 	if (IS_ENABLED(CONFIG_RELOCATABLE))
 		barebox_base = arm_mem_barebox_image(membase, endmem,
-						     pg_len);
+						     barebox_mem_len);
 	else
 		barebox_base = TEXT_BASE;
 
diff --git a/images/Makefile b/images/Makefile
index da9cc8d..cc76958 100644
--- a/images/Makefile
+++ b/images/Makefile
@@ -89,8 +89,12 @@ suffix_$(CONFIG_IMAGE_COMPRESSION_NONE) = shipped
 
 # barebox.z - compressed barebox binary
 # ----------------------------------------------------------------
+quiet_cmd_sizebss = SIZEBSS $@
+cmd_sizebss = $(call size_append, $<, $(obj)/../barebox) >> $@
+
 $(obj)/barebox.z: $(obj)/../barebox.bin FORCE
 	$(call if_changed,$(suffix_y))
+	$(call if_changed,sizebss)
 
 # %.img - create a copy from another file
 # ----------------------------------------------------------------
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index e55bc27..5b9d172 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -299,20 +299,17 @@ cmd_env=$(srctree)/scripts/genenv $(srctree) $(objtree) $@ $<
 
 # Bzip2 and LZMA do not include size in file... so we have to fake that;
 # append the size as a 32-bit littleendian number as gzip does.
-size_append = printf $(shell						\
-dec_size=0;								\
-for F in $1; do								\
-	fsize=$$(stat -c "%s" $$F);					\
-	dec_size=$$(expr $$dec_size + $$fsize);				\
+# If a second argument is supplied, include the BSS space it uses, as
+# this gives the memory needs to load a compressed binary.
+size_append = size=0;							\
+[ -n "$2" ] && size=`size -A $2 | awk '$$1==".bss"{print $$2}'`;	\
+for F in $1; do 							\
+	fsize=`stat -c %s $$F`;						\
+	size=$$((size + fsize));					\
 done;									\
-printf "%08x\n" $$dec_size |						\
-	sed 's/\(..\)/\1 /g' | {					\
-		read ch0 ch1 ch2 ch3;					\
-		for ch in $$ch3 $$ch2 $$ch1 $$ch0; do			\
-			printf '%s%03o' '\\' $$((0x$$ch)); 		\
-		done;							\
-	}								\
-)
+printf `printf '\\\\%03o\\\\%03o\\\\%03o\\\\%03o' 			\
+	$$((size&0xff)) $$((size>>8&0xff)) 				\
+	$$((size>>16&0xff)) $$((size>>24&0xff))`
 
 quiet_cmd_bzip2 = BZIP2   $@
 cmd_bzip2 = (cat $(filter-out FORCE,$^) | \
-- 
2.7.0.25.gfc10eb5.dirty


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH] environment: "wrong magic" give the impression of an error
From: Sam Ravnborg @ 2016-09-22  5:00 UTC (permalink / raw)
  To: Barebox List

From 144e3252f9604e44c48f90735489611f636e3e36 Mon Sep 17 00:00:00 2001
From: Sam Ravnborg <srn@skov.dk>
Date: Thu, 22 Sep 2016 06:54:42 +0200
Subject: [PATCH 1/1] environment: "wrong magic" give the impression of an
 error

Introduce a more soft wording when the magic of
the superblock does not match.
Include a hint to the typical reason "(envfs never written?)"

This prevents a "what is wrong?" moment when looking at
the boot log.

Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
---
 common/environment.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/common/environment.c b/common/environment.c
index db127d7..e0dfc12 100644
--- a/common/environment.c
+++ b/common/environment.c
@@ -382,7 +382,7 @@ EXPORT_SYMBOL(envfs_save);
 static int envfs_check_super(struct envfs_super *super, size_t *size)
 {
 	if (ENVFS_32(super->magic) != ENVFS_MAGIC) {
-		printf("envfs: wrong magic\n");
+		printf("envfs: no envfs (magic mismatch) - envfs newer written?\n");
 		return -EIO;
 	}
 
@@ -436,7 +436,7 @@ static int envfs_load_data(struct envfs_super *super, void *buf, size_t size,
 		buf += sizeof(struct envfs_inode);
 
 		if (ENVFS_32(inode->magic) != ENVFS_INODE_MAGIC) {
-			printf("envfs: wrong magic\n");
+			printf("envfs: no envfs (magic mismatch) - envfs newer written?\n");
 			ret = -EIO;
 			goto out;
 		}
-- 
1.8.3.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* Add and use function API for UBI
From: Sascha Hauer @ 2016-09-22  7:49 UTC (permalink / raw)
  To: Barebox List

We currently use a ioctl API to manipulate UBI devices. This is
suboptimal since we have to carry code for all ioctls with us, even
when some of it may be unused. This series expands the UBI API with
calls to add/remove volumes and uses it inside the UBI commands.
The next step would be to move the commands to separate files and
to make them Kconfig configurable individually.

Sascha

----------------------------------------------------------------
Sascha Hauer (4):
      mtd: ubi: Add API calls to create/remove volumes
      mtd: ubi: introduce barebox specific ioctl to get ubi_num
      mtd: ubi: commands: use function API to access ubi volumes
      mtd: ubi: remove now unused ioctls

 commands/ubi.c            | 40 +++++++++++++++++++++++++++++++---------
 drivers/mtd/ubi/barebox.c | 43 ++++++++++++++++++++++++++++++-------------
 include/linux/mtd/ubi.h   |  3 +++
 include/mtd/ubi-user.h    |  2 ++
 4 files changed, 66 insertions(+), 22 deletions(-)

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply

* [PATCH 2/4] mtd: ubi: introduce barebox specific ioctl to get ubi_num
From: Sascha Hauer @ 2016-09-22  7:49 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474530566-23625-1-git-send-email-s.hauer@pengutronix.de>

Code wishing to manipulate ubi devices from outside the ubi
layer needs the ubi_num as reference. Add an ioctl to get
the ubi_num from a filedescriptor.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/ubi/barebox.c | 3 +++
 include/mtd/ubi-user.h    | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/mtd/ubi/barebox.c b/drivers/mtd/ubi/barebox.c
index 13c2a47..cb9223e 100644
--- a/drivers/mtd/ubi/barebox.c
+++ b/drivers/mtd/ubi/barebox.c
@@ -299,6 +299,9 @@ static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
 		if (!req->bytes)
 			req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size;
 		return ubi_create_volume(ubi, req);
+	case UBI_IOCGETUBINUM:
+		*(u32 *)buf = ubi->ubi_num;
+		break;
 	};
 
 	return 0;
diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
index 8c02f96..9425533 100644
--- a/include/mtd/ubi-user.h
+++ b/include/mtd/ubi-user.h
@@ -161,6 +161,8 @@
 /* Re-name volumes */
 #define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
 
+#define UBI_IOCGETUBINUM _IOR(UBI_IOC_MAGIC, 32, __u32)
+
 /* ioctl commands of the UBI control character device */
 
 #define UBI_CTRL_IOC_MAGIC 'o'
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 1/4] mtd: ubi: Add API calls to create/remove volumes
From: Sascha Hauer @ 2016-09-22  7:49 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474530566-23625-1-git-send-email-s.hauer@pengutronix.de>

Currently we use a ioctl API to create/remove ubi volumes. This
means we always have to carry all function code for ubi volume
manipulation when the ioctl is compiled in.
This adds a function API to create/remove volumes so that the linker
can throw the unused code away later.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/ubi/barebox.c | 23 +++++++++++++++++++++++
 include/linux/mtd/ubi.h   |  3 +++
 2 files changed, 26 insertions(+)

diff --git a/drivers/mtd/ubi/barebox.c b/drivers/mtd/ubi/barebox.c
index fc60aae..13c2a47 100644
--- a/drivers/mtd/ubi/barebox.c
+++ b/drivers/mtd/ubi/barebox.c
@@ -257,6 +257,29 @@ void ubi_volume_cdev_remove(struct ubi_volume *vol)
 	kfree(priv);
 }
 
+int ubi_api_create_volume(int ubi_num, struct ubi_mkvol_req *req)
+{
+	struct ubi_device *ubi;
+	int ret;
+
+	ubi = ubi_get_device(ubi_num);
+	if (!ubi)
+		return -ENODEV;
+
+	if (!req->bytes)
+		req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size;
+	ret = ubi_create_volume(ubi, req);
+
+	ubi_put_device(ubi);
+
+	return ret;
+}
+
+int ubi_api_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
+{
+	return ubi_remove_volume(desc, no_vtbl);
+}
+
 static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
 {
 	struct ubi_volume_desc *desc;
diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h
index 0614681..c72f95b 100644
--- a/include/linux/mtd/ubi.h
+++ b/include/linux/mtd/ubi.h
@@ -218,6 +218,9 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum);
 int ubi_sync(int ubi_num);
 int ubi_flush(int ubi_num, int vol_id, int lnum);
 
+int ubi_api_create_volume(int ubi_num, struct ubi_mkvol_req *req);
+int ubi_api_remove_volume(struct ubi_volume_desc *desc, int no_vtbl);
+
 /*
  * This function is the same as the 'ubi_leb_read()' function, but it does not
  * provide the checking capability.
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 3/4] mtd: ubi: commands: use function API to access ubi volumes
From: Sascha Hauer @ 2016-09-22  7:49 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474530566-23625-1-git-send-email-s.hauer@pengutronix.de>

We have a function API to manipulate ubi volumes, use it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 commands/ubi.c | 40 +++++++++++++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/commands/ubi.c b/commands/ubi.c
index 26b521f..7c55195 100644
--- a/commands/ubi.c
+++ b/commands/ubi.c
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/stat.h>
 #include <linux/mtd/mtd-abi.h>
+#include <linux/mtd/ubi.h>
 #include <mtd/ubi-user.h>
 #include <mtd/ubi-media.h>
 
@@ -104,6 +105,7 @@ static int do_ubimkvol(int argc, char *argv[])
 	struct ubi_mkvol_req req;
 	int fd, ret;
 	uint64_t size;
+	uint32_t ubinum;
 
 	req.vol_type = UBI_DYNAMIC_VOLUME;
 
@@ -140,9 +142,14 @@ static int do_ubimkvol(int argc, char *argv[])
 		return 1;
 	}
 
-	ret = ioctl(fd, UBI_IOCMKVOL, &req);
-	if (ret)
-		printf("failed to create: %s\n", strerror(-ret));
+	ret = ioctl(fd, UBI_IOCGETUBINUM, &ubinum);
+	if (ret) {
+		printf("failed to get ubinum: %s\n", strerror(-ret));
+		goto err;
+	}
+
+	ret = ubi_api_create_volume(ubinum, &req);
+err:
 	close(fd);
 
 	return ret ? 1 : 0;
@@ -272,24 +279,39 @@ BAREBOX_CMD_END
 
 static int do_ubirmvol(int argc, char *argv[])
 {
-	struct ubi_mkvol_req req;
+	struct ubi_volume_desc *desc;
+	uint32_t ubinum;
 	int fd, ret;
 
 	if (argc != 3)
 		return COMMAND_ERROR_USAGE;
 
-	strncpy(req.name, argv[2], UBI_VOL_NAME_MAX);
-	req.name[UBI_VOL_NAME_MAX] = 0;
-
 	fd = open(argv[1], O_WRONLY);
 	if (fd < 0) {
 		perror("open");
 		return 1;
 	}
 
-	ret = ioctl(fd, UBI_IOCRMVOL, &req);
+	ret = ioctl(fd, UBI_IOCGETUBINUM, &ubinum);
+	if (ret) {
+		printf("failed to get ubinum: %s\n", strerror(-ret));
+		goto err;
+	}
+
+	desc = ubi_open_volume_nm(ubinum, argv[2], UBI_EXCLUSIVE);
+	if (IS_ERR(desc)) {
+		ret = PTR_ERR(desc);
+		printf("failed to open volume %s: %s\n", argv[2], strerror(-ret));
+		goto err1;
+	}
+
+	ret = ubi_api_remove_volume(desc, 0);
 	if (ret)
-		printf("failed to delete: %s\n", strerror(-ret));
+		printf("failed to remove volume %s: %s\n", argv[2], strerror(-ret));
+
+err1:
+	ubi_close_volume(desc);
+err:
 	close(fd);
 
 	return ret ? 1 : 0;
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 4/4] mtd: ubi: remove now unused ioctls
From: Sascha Hauer @ 2016-09-22  7:49 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474530566-23625-1-git-send-email-s.hauer@pengutronix.de>

The only ioctl needed is the one to get the ubi_num from a file
descriptor. The remaining ioctls are now implemented as regular
function calls.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/ubi/barebox.c | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)

diff --git a/drivers/mtd/ubi/barebox.c b/drivers/mtd/ubi/barebox.c
index cb9223e..23c4bfd 100644
--- a/drivers/mtd/ubi/barebox.c
+++ b/drivers/mtd/ubi/barebox.c
@@ -282,23 +282,14 @@ int ubi_api_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
 
 static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf)
 {
-	struct ubi_volume_desc *desc;
 	struct ubi_device *ubi = cdev->priv;
-	struct ubi_mkvol_req *req = buf;
 
 	switch (cmd) {
-	case UBI_IOCRMVOL:
-		desc = ubi_open_volume_nm(ubi->ubi_num, req->name,
-                                           UBI_EXCLUSIVE);
-		if (IS_ERR(desc))
-			return PTR_ERR(desc);
-		ubi_remove_volume(desc, 0);
-		ubi_close_volume(desc);
-		break;
-	case UBI_IOCMKVOL:
-		if (!req->bytes)
-			req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size;
-		return ubi_create_volume(ubi, req);
+	/*
+	 * Only supported ioctl is a barebox specific one to get the ubi_num
+	 * from the file descriptor. The rest is implemented as function calls
+	 * directly.
+	 */
 	case UBI_IOCGETUBINUM:
 		*(u32 *)buf = ubi->ubi_num;
 		break;
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* Re: [PATCH 2/2] commands: ubi: added the new command 'ubirename' to rename ubi volumes.
From: Sascha Hauer @ 2016-09-22  8:04 UTC (permalink / raw)
  To: Giorgio Dal Molin; +Cc: barebox, Giorgio Dal Molin
In-Reply-To: <20160921080443.21522-3-iw3gtf@arcor.de>

On Wed, Sep 21, 2016 at 10:04:43AM +0200, Giorgio Dal Molin wrote:
> From: Giorgio Dal Molin <giorgio.dal.molin@mobotix.com>
> 
> The syntax was taken from the corresponding command of the 'mts-utils'
> userland package:
> 
> # ubirename UBIDEV OLD_NAME NEW_NAME [OLD_NAME NEW_NAME ...]
> 
> Signed-off-by: Giorgio Dal Molin <iw3gtf@arcor.de>
> ---
>  commands/ubi.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 87 insertions(+)
> 
> diff --git a/commands/ubi.c b/commands/ubi.c
> index 26b521f..3479340 100644
> --- a/commands/ubi.c
> +++ b/commands/ubi.c
> @@ -6,6 +6,8 @@
>  #include <errno.h>
>  #include <getopt.h>
>  #include <linux/mtd/mtd.h>
> +#include <linux/mtd/ubi.h>
> +#include <libgen.h>
>  #include <linux/kernel.h>
>  #include <linux/stat.h>
>  #include <linux/mtd/mtd-abi.h>
> @@ -306,3 +308,88 @@ BAREBOX_CMD_START(ubirmvol)
>  	BAREBOX_CMD_GROUP(CMD_GRP_PART)
>  	BAREBOX_CMD_HELP(cmd_ubirmvol_help)
>  BAREBOX_CMD_END
> +
> +
> +static int get_vol_id(const char *vol_name)
> +{
> +	struct ubi_volume_desc *desc;
> +	struct cdev *vol_cdev;
> +	struct ubi_volume_info vi;
> +
> +	vol_cdev = cdev_by_name(vol_name);
> +	if(!vol_cdev) {
> +		perror("cdev_by_name");
> +		return -1;
> +	}
> +	desc = ubi_open_volume_cdev(vol_cdev, UBI_READONLY);
> +	if(IS_ERR(desc)) {
> +		perror("ubi_open_volume_cdev");
> +		return -1;
> +	}
> +	ubi_get_volume_info(desc, &vi);
> +	ubi_close_volume(desc);
> +
> +	return vi.vol_id;
> +};

This get_vol_id() is not particularly nice. Also I do not like having
the rename operation inside the ioctl parser as this means we cannot
make the ubirename code optional for users that do not need renaming.

I just sent out a series which should help you in this regard. Can you
base your code ontop of this?

You should be able to get the vol_id with ubi_open_volume_nm() followed
by ubi_get_volume_info().

The rename_volumes() from patch #1 should become a
ubi_api_rename_volumes() directly called from the command code.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply

* Re: [PATCH] environment: "wrong magic" give the impression of an error
From: Sascha Hauer @ 2016-09-22  8:08 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: Barebox List
In-Reply-To: <20160922050041.GA12923@ravnborg.org>

Hi Sam,

On Thu, Sep 22, 2016 at 07:00:41AM +0200, Sam Ravnborg wrote:
> From 144e3252f9604e44c48f90735489611f636e3e36 Mon Sep 17 00:00:00 2001
> From: Sam Ravnborg <srn@skov.dk>
> Date: Thu, 22 Sep 2016 06:54:42 +0200
> Subject: [PATCH 1/1] environment: "wrong magic" give the impression of an
>  error
> 
> Introduce a more soft wording when the magic of
> the superblock does not match.
> Include a hint to the typical reason "(envfs never written?)"
> 
> This prevents a "what is wrong?" moment when looking at
> the boot log.
> 
> Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
> ---
>  common/environment.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/common/environment.c b/common/environment.c
> index db127d7..e0dfc12 100644
> --- a/common/environment.c
> +++ b/common/environment.c
> @@ -382,7 +382,7 @@ EXPORT_SYMBOL(envfs_save);
>  static int envfs_check_super(struct envfs_super *super, size_t *size)
>  {
>  	if (ENVFS_32(super->magic) != ENVFS_MAGIC) {
> -		printf("envfs: wrong magic\n");
> +		printf("envfs: no envfs (magic mismatch) - envfs newer written?\n");

When the super block cannot be found it could indeed be that an envfs
was never written,...

>  		return -EIO;
>  	}
>  
> @@ -436,7 +436,7 @@ static int envfs_load_data(struct envfs_super *super, void *buf, size_t size,
>  		buf += sizeof(struct envfs_inode);
>  
>  		if (ENVFS_32(inode->magic) != ENVFS_INODE_MAGIC) {
> -			printf("envfs: wrong magic\n");
> +			printf("envfs: no envfs (magic mismatch) - envfs newer written?\n");

... but when the super block is fine then a failure to read the data is
really that - a failure, no?

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply

* [PATCH] nv: Fix variable removal in nvvar_save()
From: Sascha Hauer @ 2016-09-22  8:10 UTC (permalink / raw)
  To: Barebox List

When nv variables are removed during runtime then they are
present again when saved with nvvar_save(). This is because nvvar_save()
does not delete variables that exist on the saved environment. Delete
/nv on the saved environment before saving the new variables.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/globalvar.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/common/globalvar.c b/common/globalvar.c
index 44e6528..9d67348 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -471,6 +471,7 @@ int nvvar_save(void)
 		defaultenv_load(TMPDIR, 0);
 
 	envfs_load(env, TMPDIR, 0);
+	unlink_recursive(TMPDIR "/nv", NULL);
 
 	list_for_each_entry(param, &nv_device.parameters, list) {
 		ret = __nv_save(TMPDIR "/nv", param->name,
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 4/4] globalvar: Also create globalvars from for nonvolatile device vars
From: Sascha Hauer @ 2016-09-22  8:11 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474531872-15000-1-git-send-email-s.hauer@pengutronix.de>

nv variables beginning with "nv.dev.<devname>.<varname>" are directly
mirrored to <devname>.<varname> and there is no globalvar for it. To
make it a bit more consistent and to increase the visibility of the
nonvolatile device variables create globalvars for them aswell.

With this the propagation flow has changed from:

nv.dev.<devname>.<varname>
  -> <devname>.<varname>

to:

nv.dev.<devname>.<varname>
  -> global.dev.<devname>.<varname>
    -> <devname>.<varname>

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/globalvar.c | 75 ++++++++++++++++++++++--------------------------------
 1 file changed, 30 insertions(+), 45 deletions(-)

diff --git a/common/globalvar.c b/common/globalvar.c
index 8f37c99..a2555cf 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -179,34 +179,14 @@ static int nvvar_device_dispatch(const char *name, struct device_d **dev,
 static int nv_set(struct device_d *dev, struct param_d *p, const char *val)
 {
 	int ret;
-	int devspace;
-	struct device_d *rdev;
-	const char *pname;
 
 	if (!val)
 		val = "";
 
-	ret = nvvar_device_dispatch(p->name, &rdev, &pname);
-	if (ret < 0)
+	ret = dev_set_param(&global_device, p->name, val);
+	if (ret)
 		return ret;
 
-	devspace = ret;
-
-	if (devspace) {
-		if (rdev) {
-			ret = dev_set_param(rdev, pname, val);
-			if (ret) {
-				pr_err("Cannot init param from nv: %s.%s=%s: %s\n",
-					dev_name(rdev), pname, val, strerror(-ret));
-				return ret;
-			}
-		}
-	} else {
-		ret = dev_set_param(&global_device, p->name, val);
-		if (ret)
-			return ret;
-	}
-
 	free(p->value);
 	p->value = xstrdup(val);
 
@@ -233,19 +213,10 @@ static int __nvvar_add(const char *name, const char *value)
 {
 	struct param_d *p;
 	int ret;
-	int devspace;
-	struct device_d *dev;
-	const char *pname;
 
 	if (!IS_ENABLED(CONFIG_NVVAR))
 		return -ENOSYS;
 
-	ret = nvvar_device_dispatch(name, &dev, &pname);
-	if (ret < 0)
-		return ret;
-
-	devspace = ret;
-
 	/* Get param. If it doesn't exist yet, create it */
 	p = get_param_by_name(&nv_device, name);
 	if (!p) {
@@ -254,20 +225,13 @@ static int __nvvar_add(const char *name, const char *value)
 			return PTR_ERR(p);
 	}
 
-	if (!devspace) {
-		ret = globalvar_add_simple(name, value);
-		if (ret && ret != -EEXIST)
-			return ret;
-	}
+	/* Create corresponding globalvar if it doesn't exist yet */
+	ret = globalvar_add_simple(name, value);
+	if (ret && ret != -EEXIST)
+		return ret;
 
-	if (!value) {
-		if (devspace) {
-			if (dev)
-				value = dev_get_param(dev, pname);
-		} else {
-			value = dev_get_param(&global_device, name);
-		}
-	}
+	if (!value)
+		value = dev_get_param(&global_device, name);
 
 	return nv_set(&nv_device, p, value);
 }
@@ -409,6 +373,27 @@ void globalvar_set_match(const char *match, const char *val)
 	}
 }
 
+int globalvar_simple_set(struct device_d *dev, struct param_d *p, const char *val)
+{
+	struct device_d *rdev;
+	const char *pname;
+	int ret;
+
+	ret = nvvar_device_dispatch(p->name, &rdev, &pname);
+	if (ret < 0)
+		return ret;
+
+	if (ret && rdev) {
+		ret = dev_set_param(rdev, pname, val);
+		if (ret)
+			pr_err("Cannot init param from global: %s.%s=%s: %s\n",
+				dev_name(rdev), pname, val, strerror(-ret));
+	}
+
+	/* Pass to the generic function we have overwritten */
+	return dev_param_set_generic(dev, p, val);
+}
+
 /*
  * globalvar_add_simple
  *
@@ -418,7 +403,7 @@ int globalvar_add_simple(const char *name, const char *value)
 {
 	struct param_d *param;
 
-	param = dev_add_param(&global_device, name, NULL, NULL,
+	param = dev_add_param(&global_device, name, globalvar_simple_set, NULL,
 			      PARAM_GLOBALVAR_UNQUALIFIED);
 	if (IS_ERR(param)) {
 		if (PTR_ERR(param) != -EEXIST)
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 2/4] global: Make 'global' command behaviour consistent to 'nv'
From: Sascha Hauer @ 2016-09-22  8:11 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474531872-15000-1-git-send-email-s.hauer@pengutronix.de>

The 'nv' command can add/remove multiple variables. Implement that
for the 'global' command aswell. Also with the 'nv' command the -r
option is for removal of variables, not for "set match". Looking at
the users of "global -r" the only user uses the command for removal
of variables and not for "Setting multiple variables to the same value"
as stated in the command help text. Let "global -r" remove variables
aswell.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/boards/at91sam9m10ihd/env/boot/android |  2 +-
 commands/global.c                               | 35 ++++++++++++-------------
 2 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/arch/arm/boards/at91sam9m10ihd/env/boot/android b/arch/arm/boards/at91sam9m10ihd/env/boot/android
index 8492e41..ce5aa32 100644
--- a/arch/arm/boards/at91sam9m10ihd/env/boot/android
+++ b/arch/arm/boards/at91sam9m10ihd/env/boot/android
@@ -3,4 +3,4 @@
 global.bootm.image="/dev/nand0.kernel.bb"
 global.linux.bootargs.dyn.root="root=/dev/mtdblock1 rootfstype=jffs2 rw init=/init rootdelay=1"
 # clean the mtdparts otherwise android does not boot
-global -r linux.mtdparts.
+global -r "linux.mtdparts.*"
diff --git a/commands/global.c b/commands/global.c
index 581913d..d21b829 100644
--- a/commands/global.c
+++ b/commands/global.c
@@ -25,14 +25,14 @@
 
 static int do_global(int argc, char *argv[])
 {
-	int opt;
-	int do_set_match = 0;
+	int opt, i;
+	int do_remove = 0;
 	char *value;
 
 	while ((opt = getopt(argc, argv, "r")) > 0) {
 		switch (opt) {
 		case 'r':
-			do_set_match = 1;
+			do_remove = 1;
 			break;
 		}
 	}
@@ -45,37 +45,36 @@ static int do_global(int argc, char *argv[])
 	argc -= optind;
 	argv += optind;
 
-	if (argc != 1)
+	if (argc < 1)
 		return COMMAND_ERROR_USAGE;
 
-	value = strchr(argv[0], '=');
-	if (value) {
-		*value = 0;
-		value++;
-	}
-
-	if (do_set_match) {
-		if (!value)
-			value = "";
+	for (i = 0; i < argc; i++) {
+		value = strchr(argv[i], '=');
+		if (value) {
+			*value = 0;
+			value++;
+		}
 
-		globalvar_set_match(argv[0], value);
-		return 0;
+		if (do_remove)
+			globalvar_remove(argv[i]);
+		else
+			globalvar_add_simple(argv[i], value);
 	}
 
-	return globalvar_add_simple(argv[0], value);
+	return 0;
 }
 
 BAREBOX_CMD_HELP_START(global)
 BAREBOX_CMD_HELP_TEXT("Add a new global variable named VAR, optionally set to VALUE.")
 BAREBOX_CMD_HELP_TEXT("")
 BAREBOX_CMD_HELP_TEXT("Options:")
-BAREBOX_CMD_HELP_OPT("-r", "set value of all global variables beginning with 'match'")
+BAREBOX_CMD_HELP_OPT("-r", "Remove globalvars")
 BAREBOX_CMD_HELP_END
 
 BAREBOX_CMD_START(global)
 	.cmd		= do_global,
 	BAREBOX_CMD_DESC("create or set global variables")
-	BAREBOX_CMD_OPTS("[-r] VAR[=VALUE]")
+	BAREBOX_CMD_OPTS("[-r] VAR[=VALUE] ...")
 	BAREBOX_CMD_GROUP(CMD_GRP_ENV)
 	BAREBOX_CMD_HELP(cmd_global_help)
 BAREBOX_CMD_END
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 3/4] nv: simplify nvvar_add
From: Sascha Hauer @ 2016-09-22  8:11 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474531872-15000-1-git-send-email-s.hauer@pengutronix.de>

We do not need to have an extra code path when the variable already
exists, instead setting an existing variable can be done in the
variable creation code path aswell.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/globalvar.c | 25 +++++++------------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/common/globalvar.c b/common/globalvar.c
index 007e955..8f37c99 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -231,7 +231,7 @@ static int nv_param_set(struct device_d *dev, struct param_d *p, const char *val
 
 static int __nvvar_add(const char *name, const char *value)
 {
-	struct param_d *p, *gp;
+	struct param_d *p;
 	int ret;
 	int devspace;
 	struct device_d *dev;
@@ -246,19 +246,12 @@ static int __nvvar_add(const char *name, const char *value)
 
 	devspace = ret;
 
-	gp = get_param_by_name(&nv_device, name);
-	if (gp) {
-		if (!devspace) {
-			ret = dev_set_param(&global_device, name, value);
-			if (ret)
-				return ret;
-		}
-
-		ret = dev_set_param(&nv_device, name, value);
-		if (ret)
-			return ret;
-
-		return 0;
+	/* Get param. If it doesn't exist yet, create it */
+	p = get_param_by_name(&nv_device, name);
+	if (!p) {
+		p = dev_add_param(&nv_device, name, nv_param_set, nv_param_get, 0);
+		if (IS_ERR(p))
+			return PTR_ERR(p);
 	}
 
 	if (!devspace) {
@@ -267,10 +260,6 @@ static int __nvvar_add(const char *name, const char *value)
 			return ret;
 	}
 
-	p = dev_add_param(&nv_device, name, nv_param_set, nv_param_get, 0);
-	if (IS_ERR(p))
-		return PTR_ERR(p);
-
 	if (!value) {
 		if (devspace) {
 			if (dev)
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 1/4] globalvar: Allow to remove multiple globalvars using wildcards
From: Sascha Hauer @ 2016-09-22  8:11 UTC (permalink / raw)
  To: Barebox List

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/globalvar.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/common/globalvar.c b/common/globalvar.c
index 3fd8221..007e955 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -44,12 +44,14 @@ int globalvar_add(const char *name,
 
 void globalvar_remove(const char *name)
 {
-	struct param_d *param = get_param_by_name(&global_device, name);
+	struct param_d *p, *tmp;
 
-	if (!param)
-		return;
+	list_for_each_entry_safe(p, tmp, &global_device.parameters, list) {
+		if (fnmatch(name, p->name, 0))
+			continue;
 
-	dev_remove_param(param);
+		dev_remove_param(p);
+	}
 }
 
 static int __nv_save(const char *prefix, const char *name, const char *val)
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 1/2] nv: Allow full variable name in nvvar_add
From: Sascha Hauer @ 2016-09-22  9:13 UTC (permalink / raw)
  To: Barebox List

As a convenience for users allow to pass the full name, including
the leading "nv.", to nvvar_add().

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/globalvar.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/common/globalvar.c b/common/globalvar.c
index a2555cf..6466d1d 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -240,6 +240,9 @@ int nvvar_add(const char *name, const char *value)
 {
 	int ret;
 
+	if (!strncmp(name, "nv.", 3))
+		name += 3;
+
 	ret = __nvvar_add(name, value);
 	if (ret)
 		return ret;
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 2/2] globalvar: Allow full variable name in globalvar_add
From: Sascha Hauer @ 2016-09-22  9:13 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474535615-28133-1-git-send-email-s.hauer@pengutronix.de>

As a convenience for users allow to pass the full name, including
the leading "global.", to globalvar_add().

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/globalvar.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/common/globalvar.c b/common/globalvar.c
index 6466d1d..d6a46de 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -36,6 +36,9 @@ int globalvar_add(const char *name,
 {
 	struct param_d *param;
 
+	if (!strncmp(name, "global.", 7))
+		name += 7;
+
 	param = dev_add_param(&global_device, name, set, get, flags);
 	if (IS_ERR(param))
 		return PTR_ERR(param);
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 1/2] param: introduce param_bitmask
From: Sascha Hauer @ 2016-09-22  9:14 UTC (permalink / raw)
  To: Barebox List

param_bitmask behaves similar to an enum, except that with a bitmask
multiple values can be specified. On the command line the bits are
represented as a space separated list of strings. In memory a
unsigned long * is used as backend storage, this can be modified
using the regular bitmap functions.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/param.h |   5 +++
 lib/parameter.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+)

diff --git a/include/param.h b/include/param.h
index 3fb4740..d25db9e 100644
--- a/include/param.h
+++ b/include/param.h
@@ -52,6 +52,11 @@ struct param_d *dev_add_param_enum(struct device_d *dev, const char *name,
 		int (*get)(struct param_d *p, void *priv),
 		int *value, const char * const *names, int max, void *priv);
 
+struct param_d *dev_add_param_bitmask(struct device_d *dev, const char *name,
+		int (*set)(struct param_d *p, void *priv),
+		int (*get)(struct param_d *p, void *priv),
+		unsigned long *value, const char * const *names, int max, void *priv);
+
 struct param_d *dev_add_param_int_ro(struct device_d *dev, const char *name,
 		int value, const char *format);
 
diff --git a/lib/parameter.c b/lib/parameter.c
index 656a603..529d7ab 100644
--- a/lib/parameter.c
+++ b/lib/parameter.c
@@ -505,6 +505,141 @@ struct param_d *dev_add_param_enum(struct device_d *dev, const char *name,
 	return &pe->param;
 }
 
+struct param_bitmask {
+	struct param_d param;
+	unsigned long *value;
+	const char * const *names;
+	int num_names;
+	int (*set)(struct param_d *p, void *priv);
+	int (*get)(struct param_d *p, void *priv);
+};
+
+static inline struct param_bitmask *to_param_bitmask(struct param_d *p)
+{
+	return container_of(p, struct param_bitmask, param);
+}
+
+static int param_bitmask_set(struct device_d *dev, struct param_d *p, const char *val)
+{
+	struct param_bitmask *pb = to_param_bitmask(p);
+	void *value_save;
+	int i, ret;
+	char *freep, *dval, *str;
+
+	if (!val)
+		val = "";
+
+	freep = dval = xstrdup(val);
+	value_save = xmemdup(pb->value, BITS_TO_LONGS(pb->num_names) * sizeof(unsigned long));
+
+	while (1) {
+		str = strsep(&dval, " ");
+		if (!str || !*str)
+			break;
+
+		for (i = 0; i < pb->num_names; i++) {
+			if (pb->names[i] && !strcmp(str, pb->names[i])) {
+				set_bit(i, pb->value);
+				break;
+			}
+		}
+
+		if (i == pb->num_names) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	if (!pb->set) {
+		ret = 0;
+		goto out;
+	}
+
+	ret = pb->set(p, p->driver_priv);
+	if (ret)
+		memcpy(pb->value, value_save, BITS_TO_LONGS(pb->num_names) * sizeof(unsigned long));
+
+out:
+	free(value_save);
+	free(freep);
+	return ret;
+}
+
+static const char *param_bitmask_get(struct device_d *dev, struct param_d *p)
+{
+	struct param_bitmask *pb = to_param_bitmask(p);
+	int ret, bit;
+	char *pos;
+
+	if (pb->get) {
+		ret = pb->get(p, p->driver_priv);
+		if (ret)
+			return NULL;
+	}
+
+	pos = p->value;
+
+	for_each_set_bit(bit, pb->value, pb->num_names)
+		if (pb->names[bit])
+			pos += sprintf(pos, "%s ", pb->names[bit]);
+
+	return p->value;
+}
+
+static void param_bitmask_info(struct param_d *p)
+{
+	struct param_bitmask *pb = to_param_bitmask(p);
+	int i;
+
+	if (pb->num_names <= 1)
+		return;
+
+	printf(" (list: ");
+
+	for (i = 0; i < pb->num_names; i++) {
+		if (!pb->names[i] || !*pb->names[i])
+			continue;
+		printf("\"%s\"%s", pb->names[i],
+				i ==  pb->num_names - 1 ? ")" : ", ");
+	}
+}
+
+struct param_d *dev_add_param_bitmask(struct device_d *dev, const char *name,
+		int (*set)(struct param_d *p, void *priv),
+		int (*get)(struct param_d *p, void *priv),
+		unsigned long *value, const char * const *names, int max, void *priv)
+{
+	struct param_bitmask *pb;
+	struct param_d *p;
+	int ret, i, len = 0;
+
+	pb = xzalloc(sizeof(*pb));
+
+	pb->value = value;
+	pb->set = set;
+	pb->get = get;
+	pb->names = names;
+	pb->num_names = max;
+	p = &pb->param;
+	p->driver_priv = priv;
+
+	for (i = 0; i < pb->num_names; i++)
+		if (pb->names[i])
+			len += strlen(pb->names[i]) + 1;
+
+	p->value = xzalloc(len);
+
+	ret = __dev_add_param(p, dev, name, param_bitmask_set, param_bitmask_get, 0);
+	if (ret) {
+		free(pb);
+		return ERR_PTR(ret);
+	}
+
+	p->info = param_bitmask_info;
+
+	return &pb->param;
+}
+
 /**
  * dev_add_param_bool - add an boolean parameter to a device
  * @param dev	The device
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 2/2] globalvar: introduce globalvar_add_simple_bitmask
From: Sascha Hauer @ 2016-09-22  9:14 UTC (permalink / raw)
  To: Barebox List
In-Reply-To: <1474535686-7608-1-git-send-email-s.hauer@pengutronix.de>

Using the just introduced param_bitmask this adds the corresponding
globalvar convenience function.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/globalvar.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/include/globalvar.h b/include/globalvar.h
index 1cd8d21..e503207 100644
--- a/include/globalvar.h
+++ b/include/globalvar.h
@@ -74,6 +74,20 @@ static inline int globalvar_add_simple_enum(const char *name,
 	return 0;
 }
 
+static inline int globalvar_add_simple_bitmask(const char *name,
+		unsigned long *value, const char * const *names, int max)
+{
+	struct param_d *p;
+
+	p = dev_add_param_bitmask(&global_device, name, NULL, NULL,
+		value, names, max, NULL);
+
+	if (IS_ERR(p))
+		return PTR_ERR(p);
+
+	return 0;
+}
+
 static inline int globalvar_add_simple_ip(const char *name,
 		IPaddr_t *ip)
 {
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH] boot: add framework for redundant boot scenarios
From: Sascha Hauer @ 2016-09-22  9:14 UTC (permalink / raw)
  To: Barebox List

From: Marc Kleine-Budde <mkl@pengutronix.de>

There are several use cases where a redundant Linux system is needed. The
barebox bootchooser framework provides the building blocks to model different
use cases without the need to start from the scratch over and over again.

The bootchooser works on abstract boot targets, each with a set of properties
and implements an algorithm which selects the highest priority target to boot.

See the documentation contained in this patch for more information.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 Documentation/user/bootchooser.rst | 274 +++++++++++
 Documentation/user/state.rst       |   2 +
 Documentation/user/user-manual.rst |   1 +
 commands/Kconfig                   |   5 +
 commands/Makefile                  |   1 +
 commands/bootchooser.c             | 148 ++++++
 common/Kconfig                     |   6 +
 common/Makefile                    |   1 +
 common/boot.c                      |   7 +
 common/bootchooser.c               | 928 +++++++++++++++++++++++++++++++++++++
 include/bootchooser.h              |  37 ++
 11 files changed, 1410 insertions(+)
 create mode 100644 Documentation/user/bootchooser.rst
 create mode 100644 commands/bootchooser.c
 create mode 100644 common/bootchooser.c
 create mode 100644 include/bootchooser.h

diff --git a/Documentation/user/bootchooser.rst b/Documentation/user/bootchooser.rst
new file mode 100644
index 0000000..5baa66d
--- /dev/null
+++ b/Documentation/user/bootchooser.rst
@@ -0,0 +1,274 @@
+Barebox Bootchooser
+===================
+
+In many cases embedded systems are layed out redundantly with multiple
+kernels and multiple root file systems. The bootchooser framework provides
+the building blocks to model different use cases without the need to start
+from scratch over and over again.
+
+The bootchooser works on abstract boot targets, each with a set of properties
+and implements an algorithm which selects the highest priority target to boot.
+
+Bootchooser Targets
+-------------------
+
+A bootchooser target represents one target that barebox can boot. It consists
+of a set of variables in the ``global.bootchooser.<targetname>`` namespace. The
+following configuration variables are needed to describe a bootchooser target:
+
+``global.bootchooser.<targetname>.boot``
+  This controls what barebox actually boots for this target. This string can contain
+  anything that the :ref:`boot <command_boot>` command understands.
+
+``global.bootchooser.<targetname>.default_attempts``
+  The default number of attempts that a target shall be tried starting.
+``global.bootchooser.<targetname>.default_priority``
+  The default priority of a target.
+
+
+Additionally the following runtime variables are needed. Unlinke the configuration
+variables these are automatically changed by the bootchooser algorithm:
+
+``global.bootchooser.<targetname>.priority``
+  The current priority of the target. Higher numbers have higher priorities. A priority
+  of 0 means the target is disabled and won't be started.
+``global.bootchooser.<targetname>.remaining_attempts``
+  The remaining_attempts counter. Only targets with a remaining_attempts counter > 0
+  are started.
+
+The bootchooser algorithm generally only starts targets that have a priority
+> 0 and a remaining_attempts counter > 0.
+
+The Bootchooser Algorithm
+-------------------------
+
+The bootchooser algorithm is very simple. It works with two variables per target
+and some optional flags. The variables are the remaining_attempts counter that
+tells how many times the target will be started. The other variable is the priority,
+the target with the highest priority will be used first, a zero priority means
+the target is disabled.
+
+When booting, bootchooser starts the target with the highest priority that has a
+nonzero remaining_attempts counter. With every start of a target the remaining
+attempts counter of this target is decremented by one. This means every targets
+remaining_attempts counter reaches zero sooner or later and the target won't be
+booted anymore. To prevent that, the remaining_attempts counter must be reset to
+its default. There are different flags in the bootchooser which control resetting
+the remaining_attempts counter, controlled by the ``global.bootchooser.reset_attempts``
+variable. It holds a list of space separated flags. Possible values are:
+
+- ``power-on``: The remaining_attempts counters of all enabled targets are reset
+  after a power-on reset (``$global.system.reset="POR"``). This means after a power
+  cycle all targets will be tried again for the configured number of retries
+- ``all-zero``: The remaining_attempts counters of all enabled targets are reset
+  when none of them has any remaining_attempts left.
+
+Additionally the remaining_attempts counter can be reset manually using the
+:ref:`command_bootchooser` command. This allows for custom conditions under which
+a system is marked as good.
+In case only the booted system itself knows when it is in a good state, the
+barebox-state tool from the dt-utils_ package can used to reset the remaining_attempts
+counter from the currently running system.
+
+.. _dt-utils: http://git.pengutronix.de/?p=tools/dt-utils.git;a=summary
+
+Bootchooser General Options
+---------------------------
+
+Additionally to the target options described above, bootchooser has some general
+options not specific to any target.
+
+``global.bootchooser.disable_on_zero_attempts``
+  Boolean flag. if 1, bootchooser disables a target (sets priority to 0) whenever the
+  remaining attempts counter reaches 0.
+``global.bootchooser.default_attempts``
+  The default number of attempts that a target shall be tried starting, used when not
+  overwritten with the target specific variable of the same name.
+``global.bootchooser.default_priority``
+  The default priority of a target when not overwritten with the target specific variable
+  of the same name.
+``global.bootchooser.reset_attempts``
+  A space separated list of events that cause bootchooser to reset the
+  remaining_attempts counters of each target that has a non zero priority. possible values:
+  * empty:  counters will never be reset``
+  * power-on: counters will be reset after power-on-reset
+  * all-zero: counters will be reset when all targets have zero remaining attempts
+``global.bootchooser.reset_priorities``
+  A space separated list of events that cause bootchooser to reset the priorities of
+  all targets. Possible values:
+  * empty: priorities will never be reset
+  * all-zero: priorities will be reset when all targets have zero priority
+``global.bootchooser.retry``
+  If 1, bootchooser retries booting until one succeeds or no more valid targets exist.
+``global.bootchooser.state_prefix``
+  Variable prefix when bootchooser used with state framework as backend for storing runtime
+  data, see below.
+``global.bootchooser.targets``
+  Space separated list of targets that are used. For each entry in the list a corresponding
+  set of ``global.bootchooser.<name>``. variables must exist.
+``global.bootchooser.last_chosen``
+  bootchooser sets this to the target that was chosen on last boot (index)
+
+Using the State Framework as Backend for Runtime Variable Data
+--------------------------------------------------------------
+
+Normally the data that is modified by the bootchooser during runtime is stored
+in global variables (backed with NV). Alternatively the :ref:`state_framework`
+can be used for this data, which allows to store this data redundantly
+and in small EEPROM spaces. See :ref:`state_framework` to setup the state framework.
+During barebox runtime each state instance will create a device
+(usually named 'state' when only one is used) with a set of parameters. Set
+``global.bootchooser.state_prefix`` to the name of the device and optionally the
+namespace inside this device. For example when your state device is called 'state'
+and inside that the 'bootchooser' namespace is used for describing the targets,
+then set ``global.bootchooser.state_prefix`` to ``state.bootchooser``.
+
+Example
+-------
+
+The following example shows how to initialize two targets, 'system0' and 'system1'.
+Both boot from an UBIFS on nand0, the former has a priority of 21 and boots from
+the volume 'system0' whereas the latter has a priority of 20 and boots from
+the volume 'system1'.
+
+.. code-block:: sh
+
+  # initialize target 'system0'
+  nv bootchooser.system0.boot=nand0.ubi.system0
+  nv bootchooser.system0.default_attempts=3
+  nv bootchooser.system0.default_priority=21
+
+  # initialize target 'system1'
+  nv bootchooser.system1.boot=nand0.ubi.system1
+  nv bootchooser.system1.default_attempts=3
+  nv bootchooser.system1.default_priority=20
+
+  # make targets known
+  nv bootchooser.targets="system0 system1"
+
+  # retry until one target succeeds
+  nv bootchooser.retry="true"
+
+  # First try bootchooser, when no targets remain boot from network
+  nv boot.default="bootchooser net"
+
+Note that this example is for testing, normally the NV variables would be
+initialized directly by files in the default environment, not with a script.
+
+Scenarios
+---------
+
+This section describes some scenarios that can be solved with bootchooser. All
+scenarios assume multiple slots that can be booted, where 'multiple' is anything
+higher than one.
+
+Scenario 1
+##########
+
+A system that shall always boot without user interaction. Staying in the bootloader
+is not an option. In this scenario a target is started for the configured number
+of remaining attempts. If it cannot successfully be started, the next target is chosen.
+This happens until no targets are left to start, then all remaining attempts are
+reset to their defaults and the first target is tried again.
+
+Settings
+^^^^^^^^
+- ``global.bootchooser.reset_attempts="all-zero"``
+- ``global.bootchooser.reset_priorities="all-zero"``
+- ``global.bootchooser.disable_on_zero_attempts=0``
+- ``global.bootchooser.retry=1``
+- ``global.boot.default="bootchooser recovery"``
+- Userspace marks as good
+
+Deployment
+^^^^^^^^^^
+
+#. barebox or flash robot fills all slots with valid systems.
+#. The all-zero settings will lead to automatically enabling the slots, no
+   default settings are needed here.
+
+Recovery
+^^^^^^^^
+
+Recovery will only be called when all targets are not startable (That is, no valid
+Kernel found or read failure). Once a target is startable (A valid kernel is found
+and started) Bootchooser will never fall through to the recovery target.
+
+Scenario 2
+##########
+
+A system with multiple slots, a slot that was booted three times without success
+shall never be booted again (except after update or user interaction).
+
+Settings
+^^^^^^^^
+
+- ``global.bootchooser.reset_attempts=""``
+- ``global.bootchooser.reset_priorities=""``
+- ``global.bootchooser.disable_on_zero_attempts=0``
+- ``global.bootchooser.retry=1``
+- ``global.boot.default="bootchooser recovery"``
+- Userspace marks as good
+
+Deployment
+^^^^^^^^^^
+
+#. barebox or flash robot fills all slots with valid systems
+#. barebox or flash robot marks slots as good or state contains non zero
+   defaults for the remaining_attempts / priorities
+
+Recovery
+^^^^^^^^
+done by 'recovery' boot target which is booted after the bootchooser falls through due to
+the lack of bootable targets. This target can be:
+- A system that will be booted as recovery
+- A barebox script that will be started
+
+Scenario 3
+##########
+
+A system with multiple slots and one recovery system. Booting a slot three times
+without success disables it. A power cycle shall not be counted as failed boot.
+
+Settings
+^^^^^^^^
+
+- ``global.bootchooser.reset_attempts="power-on"``
+- ``global.bootchooser.reset_priorities=""``
+- ``global.bootchooser.disable_on_zero_attempts=1``
+- ``global.bootchooser.retry=1``
+- ``global.boot.default="bootchooser recovery"``
+- Userspace marks as good
+
+Deployment
+^^^^^^^^^^
+
+- barebox or flash robot fills all slots with valid systems
+- barebox or flash robot marks slots as good
+
+Recovery
+^^^^^^^^
+
+Done by 'recovery' boot target which is booted after the bootchooser falls through
+due to the lack of bootable targets. This target can be:
+- A system that will be booted as recovery
+- A barebox script that will be started
+
+Updating systems
+----------------
+
+Updating a slot is the same among the different scenarios. It is assumed that the
+update is done under a running Linux system which can be one of the regular bootchooser
+slots or a dedicated recovery system. For the regular slots updating is done like:
+
+- Set the priority of the inactive slot to 0.
+- Update the inactive slot
+- Set priority of the inactive slot to a higher value than the active slot
+- Set remaining_attempts of the inactive slot to nonzero
+- Reboot
+- If necessary update the now inactive, not yet updated slot the same way
+
+One way of updating systems is using RAUC_ which integrates well with the bootchooser
+in barebox.
+
+.. _RAUC: https://rauc.readthedocs.io/en/latest/ RAUC (
diff --git a/Documentation/user/state.rst b/Documentation/user/state.rst
index c401f10..5dd5c48 100644
--- a/Documentation/user/state.rst
+++ b/Documentation/user/state.rst
@@ -1,3 +1,5 @@
+.. _state_framework:
+
 Barebox State Framework
 =======================
 
diff --git a/Documentation/user/user-manual.rst b/Documentation/user/user-manual.rst
index 5841ea6..435649f 100644
--- a/Documentation/user/user-manual.rst
+++ b/Documentation/user/user-manual.rst
@@ -27,6 +27,7 @@ Contents:
    usb
    ubi
    booting-linux
+   bootchooser
    remote-control
    system-setup
    reset-reason
diff --git a/commands/Kconfig b/commands/Kconfig
index 3c79831..21d9212 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2097,6 +2097,11 @@ config CMD_STATE
 	depends on STATE
 	prompt "state"
 
+config CMD_BOOTCHOOSER
+	tristate
+	depends on BOOTCHOOSER
+	prompt "bootchooser"
+
 config CMD_DHRYSTONE
 	bool
 	prompt "dhrystone"
diff --git a/commands/Makefile b/commands/Makefile
index 7abd6dd..601f15f 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -115,6 +115,7 @@ obj-$(CONFIG_CMD_NV)		+= nv.o
 obj-$(CONFIG_CMD_DEFAULTENV)	+= defaultenv.o
 obj-$(CONFIG_CMD_STATE)		+= state.o
 obj-$(CONFIG_CMD_DHCP)		+= dhcp.o
+obj-$(CONFIG_CMD_BOOTCHOOSER)	+= bootchooser.o
 obj-$(CONFIG_CMD_DHRYSTONE)	+= dhrystone.o
 obj-$(CONFIG_CMD_SPD_DECODE)	+= spd_decode.o
 obj-$(CONFIG_CMD_MMC_EXTCSD)	+= mmc_extcsd.o
diff --git a/commands/bootchooser.c b/commands/bootchooser.c
new file mode 100644
index 0000000..91938fe
--- /dev/null
+++ b/commands/bootchooser.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de>
+ * Copyright (C) 2015 Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <bootchooser.h>
+#include <globalvar.h>
+#include <command.h>
+#include <common.h>
+#include <getopt.h>
+#include <malloc.h>
+#include <stdio.h>
+
+#define DONTTOUCH	-2
+#define DEFAULT		-1
+
+static void target_reset(struct bootchooser_target *target, int priority, int attempts)
+{
+	printf("Resetting target %s to ", bootchooser_target_name(target));
+
+	if (priority >= 0)
+		printf("priority %d", priority);
+	else if (priority == DEFAULT)
+		printf("default priority");
+
+	if (priority > DONTTOUCH && attempts > DONTTOUCH)
+		printf(", ");
+
+	if (attempts >= 0)
+		printf("%d attempts", attempts);
+	else if (attempts == DEFAULT)
+		printf("default attempts");
+
+	printf("\n");
+
+	if (priority > DONTTOUCH)
+		bootchooser_target_set_priority(target, priority);
+	if (attempts > DONTTOUCH)
+		bootchooser_target_set_attempts(target, attempts);
+}
+
+static int do_bootchooser(int argc, char *argv[])
+{
+	int opt, ret = 0, i;
+	struct bootchooser *bootchooser;
+	struct bootchooser_target *target;
+	int attempts = DONTTOUCH;
+	int priority = DONTTOUCH;
+	int info = 0;
+	bool done_something = false;
+	bool last_boot_successful = false;
+
+	while ((opt = getopt(argc, argv, "a:p:is")) > 0) {
+		switch (opt) {
+		case 'a':
+			if (!strcmp(optarg, "default"))
+				attempts = DEFAULT;
+			else
+				attempts = simple_strtoul(optarg, NULL, 0);
+			break;
+		case 'p':
+			if (!strcmp(optarg, "default"))
+				priority = DEFAULT;
+			else
+				priority = simple_strtoul(optarg, NULL, 0);
+			break;
+		case 'i':
+			info = 1;
+			break;
+		case 's':
+			last_boot_successful = true;
+			break;
+		default:
+			return COMMAND_ERROR_USAGE;
+		}
+	}
+
+	bootchooser = bootchooser_get();
+	if (IS_ERR(bootchooser)) {
+		printf("No bootchooser found\n");
+		return COMMAND_ERROR;
+	}
+
+	if (last_boot_successful) {
+		bootchooser_last_boot_successful();
+		done_something = true;
+	}
+
+	if (attempts != DONTTOUCH || priority != DONTTOUCH) {
+		if (optind < argc) {
+			for (i = optind; i < argc; i++) {
+				target = bootchooser_target_by_name(bootchooser, argv[i]);
+				if (!target) {
+					printf("No such target: %s\n", argv[i]);
+					ret = COMMAND_ERROR;
+					goto out;
+				}
+
+				target_reset(target, priority, attempts);
+			}
+		} else {
+			bootchooser_for_each_target(bootchooser, target)
+				target_reset(target, priority, attempts);
+		}
+		done_something = true;
+	}
+
+	if (info) {
+		bootchooser_info(bootchooser);
+		done_something = true;
+	}
+
+	if (!done_something) {
+		printf("Nothing to do\n");
+		ret = COMMAND_ERROR_USAGE;
+	}
+out:
+	bootchooser_put(bootchooser);
+
+	return ret;
+}
+
+BAREBOX_CMD_HELP_START(bootchooser)
+BAREBOX_CMD_HELP_TEXT("Control misc behaviour of the bootchooser")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-a <n|default> [TARGETS]",  "set priority of given targets to 'n' or the default priority")
+BAREBOX_CMD_HELP_OPT ("-p <n|default> [TARGETS]",  "set remaining attempts of given targets to 'n' or the default attempts")
+BAREBOX_CMD_HELP_OPT ("-i",  "Show information about the bootchooser")
+BAREBOX_CMD_HELP_OPT ("-s",  "Mark the last boot successful")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(bootchooser)
+	.cmd = do_bootchooser,
+	BAREBOX_CMD_DESC("bootchooser control")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_HELP(cmd_bootchooser_help)
+BAREBOX_CMD_END
diff --git a/common/Kconfig b/common/Kconfig
index f2badc7..095566b 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -936,6 +936,12 @@ config STATE_CRYPTO
 	  See Documentation/devicetree/bindings/barebox/barebox,state.rst
 	  for more information.
 
+config BOOTCHOOSER
+	bool "bootchooser infrastructure"
+	select ENVIRONMENT_VARIABLES
+	select OFTREE
+	select PARAMETER
+
 config RESET_SOURCE
 	bool "detect Reset cause"
 	depends on GLOBALVAR
diff --git a/common/Makefile b/common/Makefile
index 00bc0e8..a36ae5e 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_SHELL_HUSH)	+= hush.o
 obj-$(CONFIG_SHELL_SIMPLE)	+= parser.o
 obj-$(CONFIG_STATE)		+= state/
 obj-$(CONFIG_RATP)		+= ratp.o
+obj-$(CONFIG_BOOTCHOOSER)	+= bootchooser.o
 obj-$(CONFIG_UIMAGE)		+= image.o uimage.o
 obj-$(CONFIG_FITIMAGE)		+= image-fit.o
 obj-$(CONFIG_MENUTREE)		+= menutree.o
diff --git a/common/boot.c b/common/boot.c
index a971cb7..123b874 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -10,6 +10,7 @@
  */
 
 #include <environment.h>
+#include <bootchooser.h>
 #include <globalvar.h>
 #include <magicvar.h>
 #include <watchdog.h>
@@ -274,6 +275,12 @@ int bootentry_create_from_name(struct bootentries *bootentries,
 		}
 	}
 
+	if (IS_ENABLED(CONFIG_BOOTCHOOSER) && !strcmp(name, "bootchooser")) {
+		ret = bootchooser_create_bootentry(bootentries);
+		if (ret > 0)
+			found += ret;
+	}
+
 	if (!found) {
 		char *path;
 
diff --git a/common/bootchooser.c b/common/bootchooser.c
new file mode 100644
index 0000000..9e3367f
--- /dev/null
+++ b/common/bootchooser.c
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de>
+ * Copyright (C) 2015 Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt)	"bootchooser: " fmt
+
+#include <bootchooser.h>
+#include <environment.h>
+#include <globalvar.h>
+#include <magicvar.h>
+#include <command.h>
+#include <libfile.h>
+#include <common.h>
+#include <malloc.h>
+#include <printk.h>
+#include <xfuncs.h>
+#include <envfs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ioctl.h>
+#include <libbb.h>
+#include <state.h>
+#include <stdio.h>
+#include <init.h>
+#include <crc.h>
+#include <net.h>
+#include <fs.h>
+#include <reset_source.h>
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/err.h>
+
+#define BOOTCHOOSER_PREFIX "global.bootchooser"
+
+static char *available_targets;
+static char *state_prefix;
+static int global_default_attempts = 3;
+static int global_default_priority = 1;
+static int deactivate_on_zero_attempts;
+static int retry;
+static int last_boot_successful;
+
+struct bootchooser {
+	struct bootentry entry;
+	struct list_head targets;
+	struct bootchooser_target *last_chosen;
+
+	struct state *state;
+	char *state_prefix;
+
+	int verbose;
+	int dryrun;
+};
+
+struct bootchooser_target {
+	struct bootchooser *bootchooser;
+	struct list_head list;
+
+	/* state */
+	unsigned int priority;
+	unsigned int remaining_attempts;
+	int id;
+
+	/* spec */
+	char *name;
+	unsigned int default_attempts;
+	unsigned int default_priority;
+
+	char *boot;
+
+	char *prefix;
+	char *state_prefix;
+};
+
+enum reset_attempts {
+	RESET_ATTEMPTS_POWER_ON,
+	RESET_ATTEMPTS_ALL_ZERO,
+};
+
+static unsigned long reset_attempts;
+
+enum reset_priorities {
+	RESET_PRIORITIES_ALL_ZERO,
+};
+
+static unsigned long reset_priorities;
+
+static int bootchooser_target_ok(struct bootchooser_target *target, const char **reason)
+{
+	if (!target->priority) {
+		if (reason)
+			*reason = "Target disabled (priority = 0)";
+		return false;
+	}
+
+	if (!target->remaining_attempts) {
+		if (reason)
+			*reason = "remaining attempts = 0";
+		return false;
+	}
+
+	if (reason)
+		*reason = "target OK";
+
+	return true;
+}
+
+static void pr_target(struct bootchooser_target *target)
+{
+	const char *reason;
+	int ok;
+
+	ok = bootchooser_target_ok(target, &reason);
+
+	printf("%s\n"
+	       "    id: %u\n"
+	       "    priority: %u\n"
+	       "    default_priority: %u\n"
+	       "    remaining attempts: %u\n"
+	       "    default attempts: %u\n"
+	       "    boot: '%s'\n",
+	       target->name, target->id, target->priority, target->default_priority,
+	       target->remaining_attempts, target->default_attempts,
+	       target->boot);
+	if (!ok)
+		printf("    disabled due to %s\n", reason);
+}
+
+static int pr_setenv(struct bootchooser *bc, const char *fmt, ...)
+{
+	va_list ap;
+	int ret = 0;
+	char *str, *val;
+	const char *oldval;
+
+	va_start(ap, fmt);
+	str = bvasprintf(fmt, ap);
+	va_end(ap);
+
+	if (!str)
+		return -ENOMEM;
+
+	val = strchr(str, '=');
+	if (!val) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	*val++ = '\0';
+
+	oldval = getenv(str);
+	if (!oldval || strcmp(oldval, val)) {
+		if (bc->state)
+			ret = setenv(str, val);
+		else
+			ret = nvvar_add(str, val);
+	}
+
+err:
+	free(str);
+
+	return ret;
+}
+
+static const char *pr_getenv(const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+	const char *val;
+
+	va_start(ap, fmt);
+	str = bvasprintf(fmt, ap);
+	va_end(ap);
+
+	if (!str)
+		return NULL;
+
+	val = getenv(str);
+
+	free(str);
+
+	return val;
+}
+
+static int getenv_u32(const char *prefix, const char *name, uint32_t *retval)
+{
+	char *str;
+	const char *val;
+
+	str = xasprintf("%s.%s", prefix, name);
+
+	val = getenv(str);
+
+	free(str);
+
+	if (!val)
+		return -ENOENT;
+
+	*retval = simple_strtoul(val, NULL, 0);
+
+	return 0;
+}
+
+static int bootchooser_target_compare(struct list_head *a, struct list_head *b)
+{
+	struct bootchooser_target *bootchooser_a =
+		list_entry(a, struct bootchooser_target, list);
+	struct bootchooser_target *bootchooser_b =
+		list_entry(b, struct bootchooser_target, list);
+
+	/* order with descending priority */
+	return bootchooser_a->priority >= bootchooser_b->priority ? -1 : 1;
+}
+
+/**
+ * bootchooser_target_new - Create a new bootchooser target
+ * @bc: The bootchooser
+ * @name: The name of the new target
+ *
+ * Parses the variables associated with @name, creates a bootchooser
+ * target from it and returns it.
+ */
+static struct bootchooser_target *bootchooser_target_new(struct bootchooser *bc,
+							 const char *name)
+{
+	struct bootchooser_target *target = xzalloc(sizeof(*target));
+	const char *val;
+	int ret;
+
+	target->name = xstrdup(name);
+	target->prefix = basprintf("%s.%s", BOOTCHOOSER_PREFIX, name);
+	target->state_prefix = basprintf("%s.%s", bc->state_prefix, name);
+	target->default_attempts = global_default_attempts;
+	target->default_priority = global_default_priority;
+
+	getenv_u32(target->prefix, "default_priority",
+			    &target->default_priority);
+	getenv_u32(target->prefix, "default_attempts",
+			    &target->default_attempts);
+
+	ret = getenv_u32(target->state_prefix, "priority", &target->priority);
+	if (ret) {
+		pr_warn("Cannot read priority for target %s, using default %d\n",
+		       target->name, target->default_priority);
+		target->priority = target->default_priority;
+	}
+
+	ret = getenv_u32(target->state_prefix, "remaining_attempts", &target->remaining_attempts);
+	if (ret) {
+		pr_warn("Cannot read remaining attempts for target %s, using default %d\n",
+		       target->name, target->default_attempts);
+		target->remaining_attempts = target->default_attempts;
+	}
+
+	if (target->remaining_attempts && !target->priority) {
+		pr_warn("Disabled target %s has remaining attempts %d, setting to 0\n",
+			target->name, target->remaining_attempts);
+		target->remaining_attempts = 0;
+	}
+
+	val = pr_getenv("%s.boot", target->prefix);
+	if (!val)
+		val = target->name;
+	target->boot = xstrdup(val);
+
+	return target;
+}
+
+/**
+ * bootchooser_target_by_id - Return a target given its id
+ *
+ * Each target has an id, simply counted by the order they appear in
+ * global.bootchooser.targets. We start counting at one to leave 0
+ * for detection of uninitialized variables.
+ */
+static struct bootchooser_target *bootchooser_target_by_id(struct bootchooser *bc,
+							   uint32_t id)
+{
+	struct bootchooser_target *target;
+
+	list_for_each_entry(target, &bc->targets, list)
+		if (target->id == id)
+			return target;
+
+	return NULL;
+}
+
+/**
+ * bootchooser_target_disable - Disable a bootchooser target
+ */
+static void bootchooser_target_disable(struct bootchooser_target *target)
+{
+	target->priority = 0;
+	target->remaining_attempts = 0;
+}
+
+/**
+ * bootchooser_target_enabled - test if a target is enabled
+ *
+ * Returns true if a target is enabled, false if it's not.
+ */
+static bool bootchooser_target_enabled(struct bootchooser_target *target)
+{
+	return target->priority != 0;
+}
+
+/**
+ * bootchooser_reset_attempts - reset remaining attempts of targets
+ *
+ * Reset the remaining_attempts counter of all enabled targets
+ * to their default values.
+ */
+static void bootchooser_reset_attempts(struct bootchooser *bc)
+{
+	struct bootchooser_target *target;
+
+	bootchooser_for_each_target(bc, target) {
+		if (bootchooser_target_enabled(target))
+			bootchooser_target_set_attempts(target, -1);
+	}
+}
+
+/**
+ * bootchooser_reset_priorities - reset priorities of targets
+ *
+ * Reset the priorities counter of all targets to their default
+ * values.
+ */
+static void bootchooser_reset_priorities(struct bootchooser *bc)
+{
+	struct bootchooser_target *target;
+
+	bootchooser_for_each_target(bc, target)
+		bootchooser_target_set_priority(target, -1);
+}
+
+/**
+ * bootchooser_get - get a bootchooser instance
+ *
+ * This evaluates the different globalvars and eventually state variables,
+ * creates a bootchooser instance from it and returns it.
+ */
+struct bootchooser *bootchooser_get(void)
+{
+	struct bootchooser *bc;
+	struct bootchooser_target *target;
+	char *targets, *str, *freep = NULL, *delim;
+	int ret = -EINVAL, id = 1;
+	uint32_t last_chosen;
+	static int attempts_resetted;
+
+	bc = xzalloc(sizeof(*bc));
+
+	if (*state_prefix) {
+		if (IS_ENABLED(CONFIG_STATE)) {
+			char *state_devname;
+
+			delim = strchr(state_prefix, '.');
+			if (!delim) {
+				pr_err("state_prefix '%s' has invalid format\n",
+				       state_prefix);
+				goto err;
+			}
+			state_devname = xstrndup(state_prefix, delim - state_prefix);
+			bc->state_prefix = xstrdup(state_prefix);
+			bc->state = state_by_name(state_devname);
+			if (!bc->state) {
+				free(state_devname);
+				pr_err("Cannot get state '%s'\n",
+				       state_devname);
+				ret = -ENODEV;
+				goto err;
+			}
+			free(state_devname);
+		} else {
+			pr_err("State disabled, cannot use nv.state_prefix=%s\n",
+			       state_prefix);
+			ret = -ENODEV;
+			goto err;
+		}
+	} else {
+		bc->state_prefix = xstrdup("nv.bootchooser");
+	}
+
+	INIT_LIST_HEAD(&bc->targets);
+
+	freep = targets = xstrdup(available_targets);
+
+	while (1) {
+		str = strsep(&targets, " ");
+		if (!str || !*str)
+			break;
+
+		target = bootchooser_target_new(bc, str);
+		if (!IS_ERR(target)) {
+			target->id = id;
+			list_add_sort(&target->list, &bc->targets,
+				      bootchooser_target_compare);
+		}
+
+		id++;
+	}
+
+	if (id == 1) {
+		pr_err("Target list $global.bootchooser.targets is empty\n");
+		goto err;
+	}
+
+	if (list_empty(&bc->targets)) {
+		pr_err("No targets could be initialized\n");
+		goto err;
+	}
+
+	free(freep);
+
+	if (test_bit(RESET_PRIORITIES_ALL_ZERO, &reset_priorities)) {
+		int priority = 0;
+
+		bootchooser_for_each_target(bc, target)
+			priority += target->priority;
+
+		if (!priority) {
+			pr_info("All targets disabled, re-enabling them\n");
+			bootchooser_reset_priorities(bc);
+		}
+	}
+
+	if (test_bit(RESET_ATTEMPTS_POWER_ON, &reset_attempts) &&
+	    reset_source_get() == RESET_POR && !attempts_resetted) {
+		pr_info("Power-on Reset, resetting remaining attempts\n");
+		bootchooser_reset_attempts(bc);
+		attempts_resetted = 1;
+	}
+
+	if (test_bit(RESET_ATTEMPTS_ALL_ZERO, &reset_attempts)) {
+		int attempts = 0;
+
+		bootchooser_for_each_target(bc, target)
+			attempts += target->remaining_attempts;
+
+		if (!attempts) {
+			pr_info("All enabled targets have 0 remaining attempts, resetting them\n");
+			bootchooser_reset_attempts(bc);
+		}
+	}
+
+	ret = getenv_u32(bc->state_prefix, "last_chosen", &last_chosen);
+	if (!ret && last_chosen > 0) {
+		bc->last_chosen = bootchooser_target_by_id(bc, last_chosen);
+		if (!bc->last_chosen)
+			pr_warn("Last booted target with id %d does not exist\n", last_chosen);
+	}
+
+	if (bc->last_chosen && last_boot_successful)
+		bootchooser_target_set_attempts(bc->last_chosen, -1);
+
+	if (disable_on_zero_attempts) {
+		bootchooser_for_each_target(bc, target) {
+			if (!target->remaining_attempts) {
+				pr_info("target %s has 0 remaining attempts, disabling\n",
+					target->name);
+				bootchooser_target_disable(target);
+			}
+		}
+
+	}
+
+	return bc;
+
+err:
+	free(freep);
+	free(bc);
+
+	return ERR_PTR(ret);
+}
+
+/**
+ * bootchooser_save - save a bootchooser to the backing store
+ * @bc:		The bootchooser instance to save
+ *
+ * Return: 0 for success, negative error code otherwise
+ */
+int bootchooser_save(struct bootchooser *bc)
+{
+	struct bootchooser_target *target;
+	int ret;
+
+	if (bc->last_chosen)
+		pr_setenv(bc, "%s.last_chosen=%d", bc->state_prefix,
+			  bc->last_chosen->id);
+
+	list_for_each_entry(target, &bc->targets, list) {
+		ret = pr_setenv(bc, "%s.remaining_attempts=%d",
+				target->state_prefix,
+				target->remaining_attempts);
+		if (ret)
+			return ret;
+
+		ret = pr_setenv(bc, "%s.priority=%d",
+				target->state_prefix, target->priority);
+		if (ret)
+			return ret;
+	}
+
+	if (IS_ENABLED(CONFIG_STATE) && bc->state) {
+		ret = state_save(bc->state);
+		if (ret) {
+			pr_err("Cannot save state: %s\n", strerror(-ret));
+			return ret;
+		}
+	} else {
+		ret = nvvar_save();
+		if (ret) {
+			pr_err("Cannot save nv variables: %s\n", strerror(-ret));
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * bootchooser_put - release a bootchooser instance
+ * @bc: The bootchooser instance
+ *
+ * This releases a bootchooser instance and the memory associated with it.
+ */
+int bootchooser_put(struct bootchooser *bc)
+{
+	struct bootchooser_target *target, *tmp;
+	int ret;
+
+	ret = bootchooser_save(bc);
+	if (ret)
+		pr_err("Failed to save bootchooser state: %s\n", strerror(-ret));
+
+	list_for_each_entry_safe(target, tmp, &bc->targets, list) {
+		free(target->boot);
+		free(target->prefix);
+		free(target->state_prefix);
+		free(target->name);
+		free(target);
+	}
+
+	free(bc);
+
+	return ret;
+}
+
+/**
+ * bootchooser_info - Show information about a bootchooser instance
+ * @bc: The bootchooser
+ */
+void bootchooser_info(struct bootchooser *bc)
+{
+	struct bootchooser_target *target;
+	const char *reason;
+	int count = 0;
+
+	printf("Good targets (first will be booted next):\n");
+	list_for_each_entry(target, &bc->targets, list) {
+		if (bootchooser_target_ok(target, NULL)) {
+			count++;
+			pr_target(target);
+		}
+	}
+
+	if (!count)
+		printf("none\n");
+
+	count = 0;
+
+	printf("\nDisabled targets:\n");
+	list_for_each_entry(target, &bc->targets, list) {
+		if (!bootchooser_target_ok(target, &reason)) {
+			count++;
+			pr_target(target);
+		}
+	}
+
+	if (!count)
+		printf("none\n");
+
+	printf("\nlast booted target: %s\n", bc->last_chosen ?
+	       bc->last_chosen->name : "unknown");
+}
+
+/**
+ * bootchooser_get_target - get the target that shall be booted next
+ * @bc:		The bootchooser
+ *
+ * This is the heart of the bootchooser. This function selects the next
+ * target to boot and returns it. The remaining_attempts counter of the
+ * selected target is decreased and the bootchooser state is saved to the
+ * backend.
+ *
+ * Return: The next target
+ */
+struct bootchooser_target *bootchooser_get_target(struct bootchooser *bc)
+{
+	struct bootchooser_target *target;
+
+	list_for_each_entry(target, &bc->targets, list) {
+		if (bootchooser_target_ok(target, NULL))
+			goto found;
+	}
+
+	pr_err("No valid targets found:\n");
+	list_for_each_entry(target, &bc->targets, list)
+		pr_target(target);
+
+	return ERR_PTR(-ENOENT);
+
+found:
+	target->remaining_attempts--;
+
+	if (bc->verbose)
+		pr_info("name=%s decrementing remaining_attempts to %d\n",
+			target->name, target->remaining_attempts);
+
+	if (bc->verbose)
+		pr_info("selected target '%s', boot '%s'\n", target->name, target->boot);
+
+	bc->last_chosen = target;
+
+	bootchooser_save(bc);
+
+	return target;
+}
+
+/**
+ * bootchooser_target_name - get the name of a target
+ * @target:	The target
+ *
+ * Given a bootchooser target this function returns its name.
+ *
+ * Return: The name of the target
+ */
+const char *bootchooser_target_name(struct bootchooser_target *target)
+{
+	return target->name;
+}
+
+/**
+ * bootchooser_target_by_name - get a target from name
+ * @bc:		The bootchooser
+ * @name:	The name of the target to retrieve
+ *
+ * Given a name this function returns the corresponding target.
+ *
+ * Return: The target if found, NULL otherwise
+ */
+struct bootchooser_target *bootchooser_target_by_name(struct bootchooser *bc,
+						      const char *name)
+{
+	struct bootchooser_target *target;
+
+	bootchooser_for_each_target(bc, target)
+		if (!strcmp(target->name, name))
+			return target;
+	return NULL;
+}
+
+/**
+ * bootchooser_target_set_attempts - set remaining attempts of a target
+ * @target:	The target to change
+ * @attempts:	The number of attempts
+ *
+ * This sets the number of remaining attempts for a bootchooser target.
+ * If @attempts is < 0 then the remaining attempts is reset to the default
+ * value.
+ *
+ * Return: 0 for success, negative error code otherwise
+ */
+int bootchooser_target_set_attempts(struct bootchooser_target *target, int attempts)
+{
+	if (attempts >= 0)
+		target->remaining_attempts = attempts;
+	else
+		target->remaining_attempts = target->default_attempts;
+
+	return 0;
+}
+
+/**
+ * bootchooser_target_set_priority - set priority of a target
+ * @target:	The target to change
+ * @priority:	The priority
+ *
+ * This sets the priority of a bootchooser target. If @priority is < 0
+ * then the priority reset to the default value.
+ *
+ * Return: 0 for success, negative error code otherwise
+ */
+int bootchooser_target_set_priority(struct bootchooser_target *target, int priority)
+{
+	if (priority >= 0)
+		target->priority = priority;
+	else
+		target->priority = target->default_priority;
+
+	return 0;
+}
+
+/**
+ * bootchooser_target_first - get the first target from a bootchooser
+ * @bc:		The bootchooser
+ *
+ * Gets the first target from a bootchooser, used for the bootchooser
+ * target iterator.
+ *
+ * Return: The first target or NULL if no target exists
+ */
+struct bootchooser_target *bootchooser_target_first(struct bootchooser *bc)
+{
+	return list_first_entry_or_null(&bc->targets,
+					struct bootchooser_target, list);
+}
+
+/**
+ * bootchooser_target_next - get the next target from a bootchooser
+ * @bc:		The bootchooser
+ * @target:	The current target
+ *
+ * Gets the next target from a bootchooser, used for the bootchooser
+ * target iterator.
+ *
+ * Return: The first target or NULL if no more targets exist
+ */
+struct bootchooser_target *bootchooser_target_next(struct bootchooser *bc,
+					       struct bootchooser_target *target)
+{
+	struct list_head *next = target->list.next;
+
+	if (next == &bc->targets)
+		return NULL;
+
+	return list_entry(next, struct bootchooser_target, list);
+}
+
+/**
+ * bootchooser_last_boot_successful - tell that the last boot was successful
+ *
+ * This tells bootchooser that the last boot was successful.
+ */
+void bootchooser_last_boot_successful(void)
+{
+	last_boot_successful = true;
+}
+
+/**
+ * bootchooser_get_last_chosen - get the target which was chosen last time
+ * @bc:		The bootchooser
+ *
+ * Bootchooser stores the id of the target which was last booted in
+ * <state_prefix>.last_chosen. This function returns the target associated
+ * with this id.
+ *
+ * Return: The target which was booted last time
+ */
+struct bootchooser_target *bootchooser_get_last_chosen(struct bootchooser *bc)
+{
+	if (!bc->last_chosen)
+		return ERR_PTR(-ENODEV);
+
+	return bc->last_chosen;
+}
+
+static int bootchooser_boot_one(struct bootchooser *bc, int *tryagain)
+{
+	char *system;
+	struct bootentries *entries;
+	struct bootentry *entry;
+	struct bootchooser_target *target;
+	int ret = 0;
+
+	entries = bootentries_alloc();
+
+	target = bootchooser_get_target(bc);
+	if (IS_ERR(target)) {
+		ret = PTR_ERR(target);
+		*tryagain = 0;
+		goto out;
+	}
+
+	system = basprintf("bootchooser.active=%s", target->name);
+	globalvar_add_simple("linux.bootargs.bootchooser", system);
+	free(system);
+
+	ret = bootentry_create_from_name(entries, target->boot);
+	if (ret <= 0) {
+		printf("Nothing bootable found on '%s'\n", target->boot);
+		*tryagain = 1;
+		ret = -ENODEV;
+		goto out;
+	}
+
+	last_boot_successful = false;
+
+	ret = -ENOENT;
+
+	bootentries_for_each_entry(entries, entry) {
+		ret = boot_entry(entry, bc->verbose, bc->dryrun);
+		if (!ret) {
+			*tryagain = 0;
+			goto out;
+		}
+	}
+
+	*tryagain = 1;
+out:
+	globalvar_set_match("linux.bootargs.bootchooser", NULL);
+
+	bootentries_free(entries);
+
+	return ret;
+}
+
+static int bootchooser_boot(struct bootentry *entry, int verbose, int dryrun)
+{
+	struct bootchooser *bc = container_of(entry, struct bootchooser,
+						       entry);
+	int ret, tryagain;
+
+	bc->verbose = verbose;
+	bc->dryrun = dryrun;
+
+	do {
+		ret = bootchooser_boot_one(bc, &tryagain);
+
+		if (!retry)
+			break;
+	} while (tryagain);
+
+	return ret;
+}
+
+static void bootchooser_release(struct bootentry *entry)
+{
+	struct bootchooser *bc = container_of(entry, struct bootchooser,
+						       entry);
+
+	bootchooser_put(bc);
+}
+
+/**
+ * bootchooser_create_bootentry - create a boot entry
+ * @entries:	The list of bootentries
+ *
+ * This adds a bootchooser to the list of boot entries. Called
+ * by the 'boot' code.
+ *
+ * Return: The number of entries added to the list
+ */
+int bootchooser_create_bootentry(struct bootentries *entries)
+{
+	struct bootchooser *bc = bootchooser_get();
+
+	if (IS_ERR(bc))
+		return PTR_ERR(bc);
+
+	bc->entry.boot = bootchooser_boot;
+	bc->entry.release = bootchooser_release;
+	bc->entry.title = xstrdup("bootchooser");
+	bc->entry.description = xstrdup("bootchooser");
+
+	bootentries_add_entry(entries, &bc->entry);
+
+	return 1;
+}
+
+static const char * const reset_attempts_names[] = {
+	[RESET_ATTEMPTS_POWER_ON] = "power-on",
+	[RESET_ATTEMPTS_ALL_ZERO] = "all-zero",
+};
+
+static const char * const reset_priorities_names[] = {
+	[RESET_PRIORITIES_ALL_ZERO] = "all-zero",
+};
+
+static int bootchooser_init(void)
+{
+	state_prefix = xstrdup("");
+	available_targets = xstrdup("");
+
+	globalvar_add_simple_bool("bootchooser.disable_on_zero_attempts", &disable_on_zero_attempts);
+	globalvar_add_simple_bool("bootchooser.retry", &retry);
+	globalvar_add_simple_string("bootchooser.targets", &available_targets);
+	globalvar_add_simple_string("bootchooser.state_prefix", &state_prefix);
+	globalvar_add_simple_int("bootchooser.default_attempts", &global_default_attempts, "%u");
+	globalvar_add_simple_int("bootchooser.default_priority", &global_default_priority, "%u");
+	globalvar_add_simple_bitmask("bootchooser.reset_attempts", &reset_attempts,
+				  reset_attempts_names, ARRAY_SIZE(reset_attempts_names));
+	globalvar_add_simple_bitmask("bootchooser.reset_priorities", &reset_priorities,
+				  reset_priorities_names, ARRAY_SIZE(reset_priorities_names));
+	return 0;
+}
+device_initcall(bootchooser_init);
+
+BAREBOX_MAGICVAR_NAMED(global_bootchooser_disable_on_zero_attempts,
+		       global.bootchooser.disable_on_zero_attempts,
+		       "bootchooser: Disable target when remaining attempts counter reaches 0");
+BAREBOX_MAGICVAR_NAMED(global_bootchooser_retry,
+		       global.bootchooser.retry,
+		       "bootchooser: Try again when booting a target fails");
+BAREBOX_MAGICVAR_NAMED(global_bootchooser_targets,
+		       global.bootchooser.targets,
+		       "bootchooser: Space separated list of target names");
+BAREBOX_MAGICVAR_NAMED(global_bootchooser_default_attempts,
+		       global.bootchooser.default_attempts,
+		       "bootchooser: Default number of attempts for a target");
+BAREBOX_MAGICVAR_NAMED(global_bootchooser_default_priority,
+		       global.bootchooser.default_priority,
+		       "bootchooser: Default priority for a target");
+BAREBOX_MAGICVAR_NAMED(global_bootchooser_state_prefix,
+		       global.bootchooser.state_prefix,
+		       "bootchooser: state name prefix, empty for nv backend");
diff --git a/include/bootchooser.h b/include/bootchooser.h
new file mode 100644
index 0000000..c948247
--- /dev/null
+++ b/include/bootchooser.h
@@ -0,0 +1,37 @@
+#ifndef __BOOTCHOOSER_H
+#define __BOOTCHOOSER_H
+
+#include <of.h>
+#include <boot.h>
+
+struct bootchooser;
+struct bootchooser_target;
+
+struct bootchooser *bootchooser_get(void);
+int bootchooser_save(struct bootchooser *bootchooser);
+int bootchooser_put(struct bootchooser *bootchooser);
+
+void bootchooser_info(struct bootchooser *bootchooser);
+
+struct bootchooser_target *bootchooser_get_last_chosen(struct bootchooser *bootchooser);
+const char *bootchooser_target_name(struct bootchooser_target *target);
+struct bootchooser_target *bootchooser_target_by_name(struct bootchooser *bootchooser,
+						      const char *name);
+void bootchooser_target_force_boot(struct bootchooser_target *target);
+
+int bootchooser_create_bootentry(struct bootentries *entries);
+
+int bootchooser_target_set_attempts(struct bootchooser_target *target, int attempts);
+int bootchooser_target_set_priority(struct bootchooser_target *target, int priority);
+
+void bootchooser_last_boot_successful(void);
+
+struct bootchooser_target *bootchooser_target_first(struct bootchooser *bootchooser);
+struct bootchooser_target *bootchooser_target_next(struct bootchooser *bootchooser,
+					       struct bootchooser_target *cur);
+
+#define bootchooser_for_each_target(bootchooser, target) \
+	for (target = bootchooser_target_first(bootchooser); target; \
+	     target = bootchooser_target_next(bootchooser, target))
+
+#endif /* __BOOTCHOOSER_H */
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 3/3] ARM: socfpga: dtsi: add dw-wdt reset lines
From: Steffen Trumtrar @ 2016-09-22 12:33 UTC (permalink / raw)
  To: barebox; +Cc: Steffen Trumtrar
In-Reply-To: <1474547628-16751-1-git-send-email-s.trumtrar@pengutronix.de>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 arch/arm/dts/socfpga.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm/dts/socfpga.dtsi b/arch/arm/dts/socfpga.dtsi
index d16758fdab46..66d7f21dc6a3 100644
--- a/arch/arm/dts/socfpga.dtsi
+++ b/arch/arm/dts/socfpga.dtsi
@@ -49,3 +49,13 @@
 &f2s_sdram_ref_clk {
 	clock-frequency = <0>;
 };
+
+&watchdog0 {
+	resets = <&rst L4WD0_RESET>;
+	reset-names = "dw-wdt";
+};
+
+&watchdog1 {
+	resets = <&rst L4WD1_RESET>;
+	reset-names = "dw-wdt";
+};
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 1/3] reset: import socfpga-reset driver from linux
From: Steffen Trumtrar @ 2016-09-22 12:33 UTC (permalink / raw)
  To: barebox; +Cc: Steffen Trumtrar

Port the linux v4.8-rc1 reset-socfpga driver to barebox.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/reset/Makefile        |   1 +
 drivers/reset/reset-socfpga.c | 125 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 drivers/reset/reset-socfpga.c

diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 1e2d83f2b995..52b10cd48055 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_RESET_CONTROLLER) += core.o
+obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
new file mode 100644
index 000000000000..e30db8134739
--- /dev/null
+++ b/drivers/reset/reset-socfpga.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * based on
+ * Allwinner SoCs Reset Controller driver
+ *
+ * Copyright 2013 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <linux/err.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define NR_BANKS		4
+
+struct socfpga_reset_data {
+	spinlock_t			lock;
+	void __iomem			*membase;
+	u32				modrst_offset;
+	struct reset_controller_dev	rcdev;
+};
+
+static int socfpga_reset_assert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	struct socfpga_reset_data *data = container_of(rcdev,
+						     struct socfpga_reset_data,
+						     rcdev);
+	int bank = id / BITS_PER_LONG;
+	int offset = id % BITS_PER_LONG;
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&data->lock, flags);
+
+	reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS));
+	writel(reg | BIT(offset), data->membase + data->modrst_offset +
+				 (bank * NR_BANKS));
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	return 0;
+}
+
+static int socfpga_reset_deassert(struct reset_controller_dev *rcdev,
+				  unsigned long id)
+{
+	struct socfpga_reset_data *data = container_of(rcdev,
+						     struct socfpga_reset_data,
+						     rcdev);
+
+	int bank = id / BITS_PER_LONG;
+	int offset = id % BITS_PER_LONG;
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&data->lock, flags);
+
+	reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS));
+	writel(reg & ~BIT(offset), data->membase + data->modrst_offset +
+				  (bank * NR_BANKS));
+
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	return 0;
+}
+
+static struct reset_control_ops socfpga_reset_ops = {
+	.assert		= socfpga_reset_assert,
+	.deassert	= socfpga_reset_deassert,
+};
+
+static int socfpga_reset_probe(struct device_d *dev)
+{
+	struct socfpga_reset_data *data;
+	struct resource *res;
+	struct device_node *np = dev->device_node;
+
+	data = xzalloc(sizeof(*data));
+
+	res = dev_get_resource(dev, IORESOURCE_MEM, 0);
+	if (IS_ERR(res))
+		return PTR_ERR(res);
+
+	data->membase = IOMEM(res->start);
+
+	if (of_property_read_u32(np, "altr,modrst-offset", &data->modrst_offset)) {
+		dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n");
+		data->modrst_offset = 0x10;
+	}
+
+	spin_lock_init(&data->lock);
+
+	data->rcdev.nr_resets = NR_BANKS * BITS_PER_LONG;
+	data->rcdev.ops = &socfpga_reset_ops;
+	data->rcdev.of_node = np;
+
+	return reset_controller_register(&data->rcdev);
+}
+
+static const struct of_device_id socfpga_reset_dt_ids[] = {
+	{ .compatible = "altr,rst-mgr", },
+	{ /* sentinel */ },
+};
+
+static struct driver_d socfpga_reset_driver = {
+	.probe	= socfpga_reset_probe,
+	.of_compatible	= DRV_OF_COMPAT(socfpga_reset_dt_ids),
+};
+
+static int socfpga_reset_init(void)
+{
+	return platform_driver_register(&socfpga_reset_driver);
+}
+postcore_initcall(socfpga_reset_init);
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 2/3] watchdog: add designware driver
From: Steffen Trumtrar @ 2016-09-22 12:33 UTC (permalink / raw)
  To: barebox; +Cc: Steffen Trumtrar
In-Reply-To: <1474547628-16751-1-git-send-email-s.trumtrar@pengutronix.de>

Port the linux v4.8-rc1 Synopsys DesignWare watchdog driver to barebox.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/watchdog/Kconfig  |   6 ++
 drivers/watchdog/Makefile |   1 +
 drivers/watchdog/dw_wdt.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 201 insertions(+)
 create mode 100644 drivers/watchdog/dw_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 60a56bf4b030..63fb1a8c5701 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -16,6 +16,12 @@ config WATCHDOG_DAVINCI
 	help
 	  Add support for watchdog on the TI Davinci SoC.
 
+config WATCHDOG_DW
+	bool "Synopsys DesignWare watchdog"
+	select RESET_CONTROLLER
+	help
+	  Add support for the Synopsys DesignWare watchdog timer.
+
 config WATCHDOG_MXS28
 	bool "i.MX28"
 	depends on ARCH_IMX28
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e3afe1c27efb..5fca4c368c40 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -2,5 +2,6 @@ obj-$(CONFIG_WATCHDOG) += wd_core.o
 obj-$(CONFIG_WATCHDOG_DAVINCI) += davinci_wdt.o
 obj-$(CONFIG_WATCHDOG_OMAP) += omap_wdt.o
 obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o
+obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o
 obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
 obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
new file mode 100644
index 000000000000..9cd323ba4e83
--- /dev/null
+++ b/drivers/watchdog/dw_wdt.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2010-2011 Picochip Ltd., Jamie Iles
+ * http://www.picochip.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This file implements a driver for the Synopsys DesignWare watchdog device
+ * in the many subsystems. The watchdog has 16 different timeout periods
+ * and these are a function of the input clock frequency.
+ *
+ * The DesignWare watchdog cannot be stopped once it has been started so we
+ * do not implement a stop function. The watchdog core will continue to send
+ * heartbeat requests after the watchdog device has been closed.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <restart.h>
+#include <watchdog.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/reset.h>
+
+#define WDOG_CONTROL_REG_OFFSET		    0x00
+#define WDOG_CONTROL_REG_WDT_EN_MASK	    0x01
+#define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04
+#define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT    4
+#define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08
+#define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
+#define WDOG_COUNTER_RESTART_KICK_VALUE	    0x76
+
+/* The maximum TOP (timeout period) value that can be set in the watchdog. */
+#define DW_WDT_MAX_TOP		15
+
+#define DW_WDT_DEFAULT_SECONDS	30
+
+struct dw_wdt {
+	void __iomem		*regs;
+	struct clk		*clk;
+	struct restart_handler	restart;
+	struct watchdog		wdd;
+	struct reset_control	*rst;
+};
+
+#define to_dw_wdt(wdd)	container_of(wdd, struct dw_wdt, wdd)
+
+static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top)
+{
+	/*
+	 * There are 16 possible timeout values in 0..15 where the number of
+	 * cycles is 2 ^ (16 + i) and the watchdog counts down.
+	 */
+	return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk);
+}
+
+static int dw_wdt_start(struct watchdog *wdd)
+{
+	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
+
+	writel(WDOG_CONTROL_REG_WDT_EN_MASK,
+	       dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+
+	return 0;
+}
+
+static int dw_wdt_stop(struct watchdog *wdd)
+{
+	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
+
+	if (IS_ERR(dw_wdt->rst)) {
+		pr_warn("No reset line. Will not stop.\n");
+		return PTR_ERR(dw_wdt->rst);
+	}
+
+	reset_control_assert(dw_wdt->rst);
+	reset_control_deassert(dw_wdt->rst);
+
+	return 0;
+}
+
+static int dw_wdt_set_timeout(struct watchdog *wdd, unsigned int top_s)
+{
+	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
+	int i, top_val = DW_WDT_MAX_TOP;
+	printk("%s\n", __func__);
+
+	if (top_s == 0)
+		return dw_wdt_stop(wdd);
+
+	/*
+	 * Iterate over the timeout values until we find the closest match. We
+	 * always look for >=.
+	 */
+	for (i = 0; i <= DW_WDT_MAX_TOP; ++i) {
+		printk("%s: %d\n", __func__, dw_wdt_top_in_seconds(dw_wdt, i));
+		if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) {
+			top_val = i;
+			break;
+		}
+	}
+
+	/*
+	 * Set the new value in the watchdog.  Some versions of dw_wdt
+	 * have have TOPINIT in the TIMEOUT_RANGE register (as per
+	 * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1).  On those we
+	 * effectively get a pat of the watchdog right here.
+	 */
+	writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
+	       dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
+
+	dw_wdt_start(wdd);
+
+	return 0;
+}
+
+static void __noreturn dw_wdt_restart_handle(struct restart_handler *rst)
+{
+	struct dw_wdt *dw_wdt;
+
+	dw_wdt = container_of(rst, struct dw_wdt, restart);
+
+	dw_wdt->wdd.set_timeout(&dw_wdt->wdd, -1);
+
+	mdelay(1000);
+
+	hang();
+}
+
+static int dw_wdt_drv_probe(struct device_d *dev)
+{
+	struct watchdog *wdd;
+	struct dw_wdt *dw_wdt;
+	struct resource *mem;
+	int ret;
+
+	dw_wdt = xzalloc(sizeof(*dw_wdt));
+
+	mem = dev_request_mem_resource(dev, 0);
+	dw_wdt->regs = IOMEM(mem->start);
+	if (IS_ERR(dw_wdt->regs))
+		return PTR_ERR(dw_wdt->regs);
+
+	dw_wdt->clk = clk_get(dev, NULL);
+	if (IS_ERR(dw_wdt->clk))
+		return PTR_ERR(dw_wdt->clk);
+
+	ret = clk_enable(dw_wdt->clk);
+	if (ret)
+		return ret;
+
+	dw_wdt->rst = reset_control_get(dev, "dw-wdt");
+	if (IS_ERR(dw_wdt->rst))
+		dev_warn(dev, "No reset lines. Will not be able to stop once started.\n");
+
+	wdd = &dw_wdt->wdd;
+	wdd->name = "dw_wdt";
+	wdd->set_timeout = dw_wdt_set_timeout;
+
+	ret = watchdog_register(wdd);
+	if (ret)
+		goto out_disable_clk;
+
+	dw_wdt->restart.name = "dw_wdt";
+	dw_wdt->restart.restart = dw_wdt_restart_handle;
+
+	ret = restart_handler_register(&dw_wdt->restart);
+	if (ret)
+		pr_warn("cannot register restart handler\n");
+
+	if (!IS_ERR(dw_wdt->rst))
+		reset_control_deassert(dw_wdt->rst);
+
+	return 0;
+
+out_disable_clk:
+	clk_disable(dw_wdt->clk);
+	return ret;
+}
+
+static struct of_device_id dw_wdt_of_match[] = {
+	{ .compatible = "snps,dw-wdt", },
+	{ /* sentinel */ }
+};
+
+static struct driver_d dw_wdt_driver = {
+	.probe		= dw_wdt_drv_probe,
+	.of_compatible	= DRV_OF_COMPAT(dw_wdt_of_match),
+};
+device_platform_driver(dw_wdt_driver);
-- 
2.8.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 1/3] nand: denali: use correct interrupts in read_page
From: Steffen Trumtrar @ 2016-09-22 14:46 UTC (permalink / raw)
  To: barebox; +Cc: Steffen Trumtrar

The interrupt mask is incorrect in case of HW error correction.
The driver will time out waiting for the wrong interrupts.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/nand/nand_denali.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index bf9a05d85264..ceb5a8b87e42 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -1102,8 +1102,9 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	size_t size = denali->mtd.writesize + denali->mtd.oobsize;
 
 	uint32_t irq_status;
-	uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE |
-			    INTR_STATUS__ECC_ERR;
+	uint32_t irq_mask = denali->have_hw_ecc_fixup ?
+		(INTR_STATUS__DMA_CMD_COMP) :
+		(INTR_STATUS__ECC_TRANSACTION_DONE | INTR_STATUS__ECC_ERR);
 	bool check_erased_page = false;
 
 	if (page != denali->page) {
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 3/3] nand: denali: get rid of compile-time debug information
From: Steffen Trumtrar @ 2016-09-22 14:46 UTC (permalink / raw)
  To: barebox; +Cc: Steffen Trumtrar
In-Reply-To: <20160922144639.28305-1-s.trumtrar@pengutronix.de>

Remove dev_dbgs containing __FILE__ and __LINE__ and no other
interesting debug informations.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/nand/nand_denali.c | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index 6a6c0e462514..8b09b3722fd9 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -174,9 +174,6 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
 {
 	int i;
 
-	dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
-		__FILE__, __LINE__, __func__);
-
 	for (i = 0; i < denali->max_banks; i++)
 		iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
 		denali->flash_reg + INTR_STATUS(i));
@@ -227,9 +224,6 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali,
 	uint16_t acc_clks;
 	uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
 
-	dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
-		__FILE__, __LINE__, __func__);
-
 	en_lo = CEIL_DIV(Trp[mode], CLK_X);
 	en_hi = CEIL_DIV(Treh[mode], CLK_X);
 #if ONFI_BLOOM_TIME
@@ -492,9 +486,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
 	uint8_t maf_id, device_id;
 	int i;
 
-	dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
-			__FILE__, __LINE__, __func__);
-
 	/*
 	 * Use read id method to get device ID and other params.
 	 * For some NAND chips, controller can't report the correct
@@ -551,9 +542,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
 static void denali_set_intr_modes(struct denali_nand_info *denali,
 					uint16_t INT_ENABLE)
 {
-	dev_dbg(denali->dev, "%s, Line %d, Function: %s\n",
-		__FILE__, __LINE__, __func__);
-
 	if (INT_ENABLE)
 		iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
 	else
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related

* [PATCH 2/3] nand: denali: use is_timeout in while loop
From: Steffen Trumtrar @ 2016-09-22 14:46 UTC (permalink / raw)
  To: barebox; +Cc: Steffen Trumtrar
In-Reply-To: <20160922144639.28305-1-s.trumtrar@pengutronix.de>

Instead of using udelay and a countdown, use the is_timeout function.
Also, move the code closer to the kernel version, i.e. check for the
correct bank and clean the interrupt status.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/nand/nand_denali.c | 39 +++++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/nand_denali.c
index ceb5a8b87e42..6a6c0e462514 100644
--- a/drivers/mtd/nand/nand_denali.c
+++ b/drivers/mtd/nand/nand_denali.c
@@ -25,6 +25,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <io.h>
+#include <clock.h>
 #include <of_mtd.h>
 #include <errno.h>
 #include <asm/io.h>
@@ -633,26 +634,32 @@ static uint32_t read_interrupt_status(struct denali_nand_info *denali)
 
 static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
 {
-	unsigned long comp_res = 1000;
 	uint32_t intr_status = 0;
+	uint64_t start;
 
-	do {
+	if (!is_flash_bank_valid(denali->flash_bank)) {
+		dev_dbg(denali->dev, "No valid chip selected (%d)\n",
+			denali->flash_bank);
+		return 0;
+	}
+
+	start = get_time_ns();
+
+	while (!is_timeout(start, 1000 * MSECOND)) {
 		intr_status = read_interrupt_status(denali);
-		if (intr_status & irq_mask) {
-			/* our interrupt was detected */
-			break;
-		}
-		udelay(1);
-		comp_res--;
-	} while (comp_res != 0);
-
-	if (comp_res == 0) {
-		/* timeout */
-		intr_status = 0;
-		dev_dbg(denali->dev, "timeout occurred, status = 0x%x, mask = 0x%x\n",
-				intr_status, irq_mask);
+
+		if (intr_status != 0)
+			clear_interrupt(denali, intr_status);
+
+		if (intr_status & irq_mask)
+			return intr_status;
 	}
-	return intr_status;
+
+	/* timeout */
+	dev_dbg(denali->dev, "timeout occurred, status = 0x%x, mask = 0x%x\n",
+		intr_status, irq_mask);
+
+	return 0;
 }
 
 /*
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox