mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Jan Remmet <J.Remmet@phytec.de>
To: barebox@lists.infradead.org
Subject: Re: [PATCH] bootstate: add framework for redundant boot scenarios
Date: Thu, 26 Nov 2015 09:04:46 +0100	[thread overview]
Message-ID: <20151126080445.GA3245@lws-weitzel2@phytec.de> (raw)
In-Reply-To: <1432740178-7799-1-git-send-email-mkl@pengutronix.de>

On Wed, May 27, 2015 at 05:22:58PM +0200, Marc Kleine-Budde wrote:
> There are several use cases where a redundant Linux system is needed. The
> barebox,bootstate framework provides the building blocks to model different
> use cases without the need to start from the scratch over and over again.
Hello,
is there any reason why this wasn't applied? I tested it and it works fine.
There is also a watchdog handling now in commands/boot.c so we can rework the
bootchooser.

Jan
> 
> For more information see the included
> Documentation/devicetree/bindings/barebox/barebox,bootstate.rst file.
> 
> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
> ---
>  .../bindings/barebox/barebox,bootstate.rst         | 237 +++++++
>  arch/sandbox/dts/sandbox.dts                       |  83 +++
>  commands/Kconfig                                   |   5 +
>  commands/Makefile                                  |   1 +
>  commands/bootchooser.c                             | 101 +++
>  common/Kconfig                                     |   7 +
>  common/Makefile                                    |   1 +
>  common/bootstate.c                                 | 776 +++++++++++++++++++++
>  drivers/misc/Kconfig                               |   3 +
>  drivers/misc/Makefile                              |   1 +
>  drivers/misc/bootstate.c                           |  68 ++
>  include/bootstate.h                                |  38 +
>  12 files changed, 1321 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/barebox/barebox,bootstate.rst
>  create mode 100644 commands/bootchooser.c
>  create mode 100644 common/bootstate.c
>  create mode 100644 drivers/misc/bootstate.c
>  create mode 100644 include/bootstate.h
> 
> diff --git a/Documentation/devicetree/bindings/barebox/barebox,bootstate.rst b/Documentation/devicetree/bindings/barebox/barebox,bootstate.rst
> new file mode 100644
> index 000000000000..dd44e31ced80
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/barebox/barebox,bootstate.rst
> @@ -0,0 +1,237 @@
> +barebox bootstate
> +=================
> +
> +Overview
> +--------
> +
> +There are several use cases where a redundant Linux system is needed.
> +The ``barebox,bootstate`` framework provides the building blocks to
> +model different use cases without the need to start from the scratch
> +over and over again.
> +
> +The ``barebox,bootstate`` works on abstract boot targets, each with a
> +set of properties and implements an algorithm which selects the
> +highest priority target to boot.
> +
> +A set of boot targets can be described in a devicetree node. This
> +node could be part of the regular devicetree blob or it could be an
> +extra devicetree for the bootstate.
> +
> +A bootstate node contains a description of a set of boot targets along
> +with a place where to store the mutable state. Currently implemented
> +backends are :ref:`barebox,state` and ``nv`` (:ref:`command_nv`)
> +variables.
> +
> +Required properties:
> +
> +* ``compatible``: should be ``barebox,bootstate``;
> +* ``backend-type``: should be ``state`` or ``nv``.
> +
> +Optional properties:
> +
> +* ``backend``: phandle to the :ref:`barebox,state` backend
> +
> +
> +boot target nodes - immutable description
> +-----------------------------------------
> +
> +These are subnodes of a bootstate node, each describing a boot
> +target. The node name may end with ``@<ADDRESS>``, but the suffix is
> +sripped from the target name.
> +
> +Optional properties:
> +
> +* ``default_attempts``: If the boot attempts counter is reset, this
> +  value is used.
> +
> +Example::
> +
> +  bootstate: bootstate {
> +  	compatible = "barebox,bootstate";
> +  	backend-type = "state";
> +  	backend = <&state>;
> +
> +  	system0 {
> +  		default_attempts = <3>;
> +  	};
> +
> +  	system1 {
> +  		default_attempts = <3>;
> +  	};
> +  };
> +
> +In this example a bootstate, using a :ref:`barebox,state` backend with
> +two boot target ``system0`` and ``system1`` is defined. When the boot
> +attempts counter is reset, the default value of ``3`` is used for both
> +targets.
> +
> +
> +boot target nodes - mutable state
> +---------------------------------
> +
> +The above example uses a :ref:`barebox,state` backend, which requires
> +some additional configuration to hold the mutable
> +state. :ref:`barebox,state` has to be explicidly configured, while
> +``nv`` (:ref:`command_nv`) variables are created on the fly.
> +
> +The state of each boot target consists of the following ``uint32``
> +varibles:
> +
> +* ``remaining_attempts``: holds the number of remaining boot
> +  attempts. This variable is changed the the bootstate algorithm
> +  during boot.
> +* ``priority``: defines the priority of the boot target. Higher number
> +  indicate a higher priority, If two boot target have the same
> +  priority the one defined first in the device tree has precedence.
> +  The ``priority`` can optionally be changed by the algorithm to 0, if
> +  the boot target is decremented to ``0`` remaining boot attempts. A
> +  ``priority`` of ``0`` means the boot target is **deactivated** and
> +  will not be considered a valid target during further boots. If the
> +  remaining attempts counter is reset, a target with priority 0 is
> +  **not** changed.
> +* ``ok``: this is an opaque value, it's not accessed by the bootstate
> +  algorithm. It can be used be the Linux system to track the first
> +  boot after an update.
> +
> +The bootstate can also hold a default watchdog timeout (in seconds),
> +which can be activated by the bootstate algorithm.
> +
> +Example::
> +
> +  state: state {
> +  	magic = <0x4d433230>;
> +  	compatible = "barebox,state";
> +  	backend-type = "raw";
> +  	backend = <&backend_state>;
> +  	#address-cells = <1>;
> +  	#size-cells = <1>;
> +
> +  	bootstate {
> +  		#address-cells = <1>;
> +  		#size-cells = <1>;
> +
> +  		system0 {
> +  			#address-cells = <1>;
> +  			#size-cells = <1>;
> +
> +  			remaining_attempts {
> +  				reg = <0x0 0x4>;
> +  				type = "uint32";
> +  			};
> +  			priority {
> +  				reg = <0x4 0x4>;
> +  				type = "uint32";
> +  			};
> +  			ok {
> +  				reg = <0x8 0x4>;
> +  				type = "uint32";
> +  			};
> +  		};
> +
> +  		system1 {
> +  			#address-cells = <1>;
> +  			#size-cells = <1>;
> +
> +  			remaining_attempts {
> +  				reg = <0x10 0x4>;
> +  				type = "uint32";
> +  			};
> +  			priority {
> +  				reg = <0x14 0x4>;
> +  				type = "uint32";
> +  			};
> +  			ok {
> +  				reg = <0x18 0x4>;
> +  				type = "uint32";
> +  			};
> +  		};
> +
> +  		watchdog_timeout {
> +  			reg = <0x20 0x4>;
> +  			type = "uint32";
> +  			default = <60>;
> +  		};
> +  	};
> +  };
> +
> +This example defines two boot target (``system0`` and ``system1``) and
> +a watchdog timeout of ``60`` seconds.
> +
> +
> +Backends
> +--------
> +
> +Currently two backends exist. The :ref:`barebox,state` backend is a
> +bit more complicated to setup, as all boot target have to be described
> +in the referenced :ref:`barebox,state` in the device tree. On the
> +upside, the advantages of the (possible redundant storage, etc...) of
> +the :ref:`barebox,state` is gained for free.
> +
> +The :ref:`command_nv` backend is a lot simpler, no special setup is
> +needed, it should run on every board, which already implements a
> +read/writeable barebox environment.
> +
> +
> +Algorithm
> +---------
> +
> +The low level algorithm is implemented by the
> +``bootstate_get_target()`` function. Its job is to iterate over all
> +boot sources and return the name (as a string) of the choosen boot
> +target.
> +
> +The algorithm iterates over all boot target defined under the
> +associated device tree node and picks the one with the highest
> +``priority`` (higher number have a higher priority) where the
> +``remaining_attempts`` is greater than zero. A pointer to the name of
> +the boot target is returned, the string should be freed via ``free()``.
> +
> +The behaviour can be modified with the flags paramter. The following
> +flags are currently supported:
> +
> +* ``BOOTCHOOSER_FLAG_ATTEMPTS_KEEP``: the ``remaining_attempts``
> +  counter of the choosen boot target is not changed.
> +* ``BOOTCHOOSER_FLAG_ATTEMPTS_DEC``: the ``remaining_attempts``
> +  counter of the choosen boot target is decremented by one.
> +* ``BOOTCHOOSER_FLAG_ATTEMPTS_RESET``: the ``remaining_attempts``
> +  counter of all *active* boot targets (those with ``priority > 0``)
> +  are reset to their default values as defined in the immutable
> +  description by ``default_attempts``.
> +* ``BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS``: if used together
> +  with ``BOOTCHOOSER_FLAG_ATTEMPTS_DEC`` and the
> +  ``remaining_attempts`` counter of the choosen boot target is
> +  decremented to ``0``, the boot target is deactivated for further
> +  boot attempts (although *this* boot is attemped as usual). This is
> +  done by setting the ``priority`` to ``0``.
> +* ``BOOTCHOOSER_FLAG_VERBOSE``: increases the verbosity of the output
> +
> +
> +Frontend
> +--------
> +
> +The shell command ``bootchooser`` (:ref:`command_bootchooser`) can be
> +used to choose and start a boot target by a shell one-liner. The
> +command picks the boot target with the highes priority and calls the
> +``boot`` (:ref:`command_boot`) command with the selected boot target
> +as its first and only parameter.
> +
> +The ``bootchooser`` command implements command line paramter versions
> +of the above described flags:
> +
> +* ``-k``: keep boot attempts
> +* ``-d``: decrement boot attempts
> +* ``-r``: reset boot attempts
> +* ``-z``: deactivate on zero remaining attempts
> +* ``-v``: verbose output
> +
> +Next to the standard parameters, these additional options are
> +implemented:
> +
> +* ``-D``: dryrun - do not boot (all other functionality is active) - a
> +  specified watchdog timeout will be activated.
> +* ``-R``: retry - if booting fails, the chose next target, but
> +  decrement its attemts. Note: if the current target has still the
> +  highes priority and remaining attemts, it will be selected again.
> +* ``-w <TIMEOUT_IN_SEC>``: activate watchdog - if no parameter is
> +  given, the timeout from the device tree is used. A given parameter
> +  overwrites the device tree default.
> diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
> index 2595aa13fa62..e2bc8f76c2e3 100644
> --- a/arch/sandbox/dts/sandbox.dts
> +++ b/arch/sandbox/dts/sandbox.dts
> @@ -3,5 +3,88 @@
>  #include "skeleton.dtsi"
>  
>  / {
> +	aliases {
> +		state = &state;
> +	};
>  
> +	state: state {
> +		magic = <0x4d433230>;
> +		compatible = "barebox,state";
> +		backend-type = "dtb";
> +		backend = "/fd0";
> +
> +		bootstate {
> +			system0 {
> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +
> +				remaining_attempts {
> +					reg = <0x0 0x4>;
> +					type = "uint32";
> +				};
> +				priority {
> +					reg = <0x4 0x4>;
> +					type = "uint32";
> +				};
> +				ok {
> +					reg = <0x8 0x4>;
> +					type = "uint32";
> +				};
> +			};
> +
> +			system1 {
> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +
> +				remaining_attempts {
> +					reg = <0x10 0x4>;
> +					type = "uint32";
> +				};
> +				priority {
> +					reg = <0x14 0x4>;
> +					type = "uint32";
> +				};
> +				ok {
> +					reg = <0x18 0x4>;
> +					type = "uint32";
> +				};
> +			};
> +
> +			factory {
> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +
> +				remaining_attempts {
> +					reg = <0x20 0x4>;
> +					type = "uint32";
> +				};
> +				priority {
> +					reg = <0x24 0x4>;
> +					type = "uint32";
> +				};
> +				ok {
> +					reg = <0x28 0x4>;
> +					type = "uint32";
> +				};
> +			};
> +		};
> +	};
> +
> +	bootstate: bootstate {
> +		compatible = "barebox,bootstate";
> +		backend-type = "state"; // or "nv", or "efivar"
> +		backend = <&state>;
> +
> +		system0 {
> +			default_attempts = <3>;
> +		};
> +
> +		system1 {
> +			default_attempts = <3>;
> +		};
> +
> +		factory {
> +			default_attempts = <3>;
> +		};
> +	};
>  };
> diff --git a/commands/Kconfig b/commands/Kconfig
> index 25c77a85c5dc..85dd88f7615c 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -2101,6 +2101,11 @@ config CMD_STATE
>  	depends on STATE
>  	prompt "state"
>  
> +config CMD_BOOTCHOOSER
> +	tristate
> +	depends on BOOTSTATE
> +	prompt "bootchooser"
> +
>  # end Miscellaneous commands
>  endmenu
>  
> diff --git a/commands/Makefile b/commands/Makefile
> index b902f58ec5cc..f51cfb6ed492 100644
> --- a/commands/Makefile
> +++ b/commands/Makefile
> @@ -111,3 +111,4 @@ obj-$(CONFIG_CMD_CMP)		+= cmp.o
>  obj-$(CONFIG_CMD_NV)		+= nv.o
>  obj-$(CONFIG_CMD_DEFAULTENV)	+= defaultenv.o
>  obj-$(CONFIG_CMD_STATE)		+= state.o
> +obj-$(CONFIG_CMD_BOOTCHOOSER)	+= bootchooser.o
> diff --git a/commands/bootchooser.c b/commands/bootchooser.c
> new file mode 100644
> index 000000000000..34006175818f
> --- /dev/null
> +++ b/commands/bootchooser.c
> @@ -0,0 +1,101 @@
> +/*
> + * 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 <bootstate.h>
> +#include <command.h>
> +#include <common.h>
> +#include <getopt.h>
> +#include <malloc.h>
> +#include <stdio.h>
> +
> +static int do_bootchooser(int argc, char *argv[])
> +{
> +	unsigned flags = 0, timeout = 0;
> +	char *name = NULL;
> +	int opt, ret;
> +
> +	while ((opt = getopt(argc, argv, "kdrzvDRw::")) > 0) {
> +		switch (opt) {
> +		case 'k':
> +			flags |= BOOTCHOOSER_FLAG_ATTEMPTS_KEEP;
> +			break;
> +		case 'd':
> +			flags |= BOOTCHOOSER_FLAG_ATTEMPTS_DEC;
> +			break;
> +		case 'r':
> +			flags |= BOOTCHOOSER_FLAG_ATTEMPTS_RESET;
> +			break;
> +		case 'z':
> +			flags |= BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS;
> +			break;
> +		case 'v':
> +			flags |= BOOTCHOOSER_FLAG_VERBOSE;
> +			break;
> +		case 'D':
> +			flags |= BOOTCHOOSER_FLAG_DRYRUN;
> +			break;
> +		case 'R':
> +			flags |= BOOTCHOOSER_FLAG_RETRY_WITH_DEC;
> +			break;
> +		case 'w':
> +			if (optarg)
> +				timeout = simple_strtoul(optarg, NULL, 0);
> +			else
> +				flags |= BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE;
> +			flags |= BOOTCHOOSER_FLAG_WATCHDOG_ENABLE;
> +			break;
> +		default:
> +			return COMMAND_ERROR_USAGE;
> +		}
> +	}
> +
> +	if (optind < argc)
> +		name = argv[optind];
> +
> +	if (!(flags & (BOOTCHOOSER_FLAG_ATTEMPTS_KEEP |
> +		       BOOTCHOOSER_FLAG_ATTEMPTS_DEC |
> +		       BOOTCHOOSER_FLAG_ATTEMPTS_RESET))) {
> +		bootstate_info();
> +		return 0;
> +	}
> +
> +	if ((flags & BOOTCHOOSER_FLAG_ATTEMPTS_KEEP) &&
> +	    (flags & (BOOTCHOOSER_FLAG_ATTEMPTS_DEC | BOOTCHOOSER_FLAG_ATTEMPTS_RESET)))
> +		return COMMAND_ERROR_USAGE;
> +
> +	ret = bootstate_bootchooser(name, flags, timeout);
> +
> +	return ret ? COMMAND_ERROR : COMMAND_SUCCESS;
> +}
> +
> +BAREBOX_CMD_HELP_START(bootchooser)
> +BAREBOX_CMD_HELP_TEXT("Options:")
> +BAREBOX_CMD_HELP_OPT ("-k","keep - boot, don't modify attempts counter")
> +BAREBOX_CMD_HELP_OPT ("-d","decrement - boot, but decrement attempts counter by one")
> +BAREBOX_CMD_HELP_OPT ("-r","reset - boot, but reset _all_ attempts counter to default")
> +BAREBOX_CMD_HELP_OPT ("-z","deactivate choosen target in on zero remaining boot attemts")
> +BAREBOX_CMD_HELP_OPT ("-v","verbose output")
> +BAREBOX_CMD_HELP_OPT ("-D","dryrun. Do not boot - but handle watchdog and reset.")
> +BAREBOX_CMD_HELP_OPT ("-R","retry - boot, retry next boot target and decrement attempts")
> +BAREBOX_CMD_HELP_OPT ("-w","activate watchdog, use timeout specified in <BOOTSTATE>.watchdog_timeout")
> +BAREBOX_CMD_HELP_END
> +
> +BAREBOX_CMD_START(bootchooser)
> +	.cmd = do_bootchooser,
> +	BAREBOX_CMD_DESC("automatically select a boot target and boot")
> +	BAREBOX_CMD_OPTS("[-kdrzvDR] -w <TIMEOUT> [BOOTSTATE]")
> +	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
> +	BAREBOX_CMD_HELP(cmd_bootchooser_help)
> +BAREBOX_CMD_END
> diff --git a/common/Kconfig b/common/Kconfig
> index 3dfb5ac19494..791fd89d4c1e 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -716,6 +716,13 @@ config STATE
>  	select OFTREE
>  	select PARAMETER
>  
> +config BOOTSTATE
> +	bool "bootstate infrastructure"
> +	depends on OF_BAREBOX_DRIVERS
> +	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 2738238c67a8..ed336e257e33 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_RESET_SOURCE)	+= reset_source.o
>  obj-$(CONFIG_SHELL_HUSH)	+= hush.o
>  obj-$(CONFIG_SHELL_SIMPLE)	+= parser.o
>  obj-$(CONFIG_STATE)		+= state.o
> +obj-$(CONFIG_BOOTSTATE)		+= bootstate.o
>  obj-$(CONFIG_UIMAGE)		+= image.o uimage.o
>  obj-$(CONFIG_MENUTREE)		+= menutree.o
>  obj-$(CONFIG_EFI_GUID)		+= efi-guid.o
> diff --git a/common/bootstate.c b/common/bootstate.c
> new file mode 100644
> index 000000000000..5baaf03b7bfe
> --- /dev/null
> +++ b/common/bootstate.c
> @@ -0,0 +1,776 @@
> +/*
> + * 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 <bootstate.h>
> +#include <common.h>
> +#include <envfs.h>
> +#include <environment.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <fs.h>
> +#include <globalvar.h>
> +#include <init.h>
> +#include <ioctl.h>
> +#include <libbb.h>
> +#include <libfile.h>
> +#include <malloc.h>
> +#include <net.h>
> +#include <printk.h>
> +#include <state.h>
> +#include <stdio.h>
> +#include <watchdog.h>
> +#include <xfuncs.h>
> +
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/mtd/mtd-abi.h>
> +#include <linux/mtd/mtd.h>
> +
> +#include <asm/unaligned.h>
> +
> +/* list of all registered bootstate instances */
> +static LIST_HEAD(bootstate_list);
> +
> +struct state_backend;
> +
> +struct bootstate {
> +	struct device_d dev;
> +	const char *name;
> +	struct list_head list;
> +	struct list_head targets;
> +	struct list_head targets_unsorted;
> +	struct bootstate_backend *backend;
> +	bool dirty;
> +};
> +
> +struct bootstate_backend {
> +	int (*load)(struct bootstate_backend *backend, struct bootstate *bootstate);
> +	int (*save)(struct bootstate_backend *backend, struct bootstate *bootstate);
> +	const char *name;
> +	const char *path;
> +};
> +
> +struct bootstate_target {
> +	struct list_head list;
> +	struct list_head list_unsorted;
> +
> +	/* state */
> +	unsigned int priority;
> +	unsigned int remaining_attempts;
> +	bool ok;
> +
> +	/* spec */
> +	const char *name;
> +	unsigned int default_attempts;
> +};
> +
> +static void pr_target(struct bootstate *bootstate, struct bootstate_target *target)
> +{
> +	printf("%s: target: name=%s prio=%u, ok=%d, rem=%u, def=%u\n",
> +	       bootstate->name, target->name, target->priority, target->ok,
> +	       target->remaining_attempts, target->default_attempts);
> +}
> +
> +static struct bootstate *bootstate_new(const char *name)
> +{
> +	struct bootstate *bootstate;
> +	int ret;
> +
> +	bootstate = xzalloc(sizeof(*bootstate));
> +	safe_strncpy(bootstate->dev.name, name, MAX_DRIVER_NAME);
> +	bootstate->name = bootstate->dev.name;
> +	bootstate->dev.id = DEVICE_ID_DYNAMIC;
> +	INIT_LIST_HEAD(&bootstate->targets);
> +	INIT_LIST_HEAD(&bootstate->targets_unsorted);
> +
> +	ret = register_device(&bootstate->dev);
> +	if (ret) {
> +		free(bootstate);
> +		return ERR_PTR(ret);
> +	}
> +
> +	list_add_tail(&bootstate->list, &bootstate_list);
> +
> +	return bootstate;
> +}
> +
> +static void bootstate_release(struct bootstate *bootstate)
> +{
> +	unregister_device(&bootstate->dev);
> +	free(bootstate);
> +}
> +
> +static int bootstate_target_compare(struct list_head *a, struct list_head *b)
> +{
> +	struct bootstate_target *bootstate_a = list_entry(a, struct bootstate_target, list);
> +	struct bootstate_target *bootstate_b = list_entry(b, struct bootstate_target, list);
> +
> +	/* order descending */
> +	return bootstate_a->priority >= bootstate_b->priority ? -1 : 1;
> +}
> +
> +static void bootstate_target_add(struct bootstate *bootstate, struct bootstate_target *target)
> +{
> +	list_del(&target->list);
> +	list_add_sort(&target->list, &bootstate->targets, bootstate_target_compare);
> +}
> +
> +static int bootstate_variable_read_u32(const struct bootstate *bootstate,
> +				       const char *name, uint32_t *out_val)
> +{
> +	char *var;
> +	int ret;
> +
> +	var = asprintf("%s.%s.%s", bootstate->backend->path, bootstate->name, name);
> +	ret = getenv_uint(var, out_val);
> +	free(var);
> +
> +	return ret;
> +}
> +
> +static int bootstate_backend_variable_read_target_u32(const struct bootstate_backend *backend,
> +						      const struct bootstate *bootstate,
> +						      const struct bootstate_target *target,
> +						      const char *name, uint32_t *out_val)
> +{
> +	char *var;
> +	int ret;
> +
> +	var = asprintf("%s.%s.%s.%s", backend->path, bootstate->name,
> +		       target->name, name);
> +	ret = getenv_uint(var, out_val);
> +	free(var);
> +
> +	return ret;
> +}
> +
> +static int bootstate_backend_variable_write_target_u32(const struct bootstate_backend *backend,
> +						       const struct bootstate *bootstate,
> +						       const struct bootstate_target *target,
> +						       const char *name, uint32_t in_val)
> +{
> +	char *var;
> +	char *val;
> +	int ret;
> +
> +	var = asprintf("%s.%s.%s.%s", backend->path, bootstate->name,
> +		       target->name, name);
> +	val = asprintf("%d", in_val);
> +	ret = setenv(var, val);
> +	free(val);
> +	free(var);
> +
> +	return ret;
> +}
> +
> +static int bootstate_variable_nv_init_u32(const struct bootstate_backend *backend,
> +					  const struct bootstate *bootstate,
> +					  const struct bootstate_target *target,
> +					  const char *name)
> +{
> +	char *var;
> +	int ret;
> +
> +	var = asprintf("%s.%s.%s", bootstate->name, target->name, name);
> +	ret = nvvar_add(var, "0");
> +	free(var);
> +
> +	return ret;
> +}
> +
> +static struct bootstate_target *bootstate_target_find(const struct bootstate *bootstate,
> +						      const char *name)
> +{
> +	struct bootstate_target *target;
> +
> +	list_for_each_entry(target, &bootstate->targets, list) {
> +		if (!strcmp(target->name, name))
> +			return target;
> +	}
> +
> +	return ERR_PTR(-ENOENT);
> +}
> +
> +static int bootstate_target_from_node(struct bootstate *bootstate, const struct device_node *node, bool create)
> +{
> +	struct bootstate_target *target;
> +	char *name, *indexs;
> +	int ret;
> +
> +	name = xstrdup(node->name);
> +	indexs = strchr(name, '@');
> +	if (indexs)
> +		*indexs++ = 0;
> +
> +	if (create) {
> +		/* create*/
> +		target = xzalloc(sizeof(*target));
> +
> +		target->name = xstrdup(name);
> +		list_add_tail(&target->list, &bootstate->targets);
> +		list_add_tail(&target->list_unsorted,
> +			      &bootstate->targets_unsorted);
> +	} else {
> +		target = bootstate_target_find(bootstate, name);
> +		if (IS_ERR(target)) {
> +			int ret = PTR_ERR(target);
> +			pr_err("no such boot target: %s: %s\n",
> +			       name, strerror(-ret));
> +			return ret;
> +		}
> +	}
> +
> +	/* init */
> +	ret = of_property_read_u32(node, "default_attempts",
> +				   &target->default_attempts);
> +	if (ret)
> +		return ret;
> +
> +	free(name);
> +
> +	return 0;
> +}
> +
> +static int bootstate_from_node(struct bootstate *bootstate,
> +			       const struct device_node *node, bool create)
> +{
> +	struct device_node *child;
> +	int ret;
> +
> +	for_each_child_of_node(node, child) {
> +		ret = bootstate_target_from_node(bootstate, child, create);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int bootstate_backend_load_one(const struct bootstate_backend *backend,
> +				      const struct bootstate *bootstate,
> +				      struct bootstate_target *target)
> +{
> +	uint32_t tmp;
> +	int ret;
> +
> +	ret = bootstate_backend_variable_read_target_u32(backend, bootstate, target,
> +							 "remaining_attempts",
> +							 &target->remaining_attempts);
> +	if (ret)
> +		return ret;
> +
> +	ret = bootstate_backend_variable_read_target_u32(backend, bootstate, target,
> +							 "priority", &target->priority);
> +	if (ret)
> +		return ret;
> +
> +	ret = bootstate_backend_variable_read_target_u32(backend, bootstate, target,
> +							 "ok", &tmp);
> +	if (ret)
> +		return ret;
> +
> +	target->ok = !!tmp;
> +
> +	return ret;
> +}
> +
> +static int bootstate_backend_load(struct bootstate_backend *backend,
> +				  struct bootstate *bootstate)
> +{
> +	struct bootstate_target *target;
> +	int ret;
> +
> +	list_for_each_entry(target, &bootstate->targets_unsorted, list_unsorted) {
> +		ret = bootstate_backend_load_one(backend, bootstate, target);
> +		if (ret)
> +			return ret;
> +		bootstate_target_add(bootstate, target);
> +	}
> +
> +	return 0;
> +}
> +
> +static int bootstate_backend_save_one(const struct bootstate_backend *backend,
> +				      const struct bootstate *bootstate,
> +				      struct bootstate_target *target)
> +{
> +	int ret;
> +
> +	ret = bootstate_backend_variable_write_target_u32(backend, bootstate, target,
> +							  "remaining_attempts",
> +							  target->remaining_attempts);
> +	if (ret)
> +		return ret;
> +
> +	ret = bootstate_backend_variable_write_target_u32(backend, bootstate, target,
> +							  "priority", target->priority);
> +	if (ret)
> +		return ret;
> +
> +	ret = bootstate_backend_variable_write_target_u32(backend, bootstate, target,
> +							  "ok", target->ok);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int bootstate_backend_save(const struct bootstate_backend *backend,
> +				  const struct bootstate *bootstate)
> +{
> +	struct bootstate_target *target;
> +	int ret;
> +
> +	list_for_each_entry(target, &bootstate->targets, list) {
> +		ret = bootstate_backend_save_one(backend, bootstate, target);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int bootstate_backend_nv_init_one(const struct bootstate_backend *backend,
> +					 const struct bootstate *bootstate,
> +					 struct bootstate_target *target)
> +{
> +	int ret;
> +
> +	ret = bootstate_variable_nv_init_u32(backend, bootstate, target,
> +					     "remaining_attempts");
> +	if (ret)
> +		return ret;
> +
> +	ret = bootstate_variable_nv_init_u32(backend, bootstate, target,
> +					     "priority");
> +	if (ret)
> +		return ret;
> +
> +	ret = bootstate_variable_nv_init_u32(backend, bootstate, target,
> +					     "ok");
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int bootstate_backend_nv_init(struct bootstate_backend *backend,
> +				     struct bootstate *bootstate)
> +{
> +	struct bootstate_target *target;
> +	int ret;
> +
> +	list_for_each_entry(target, &bootstate->targets_unsorted, list_unsorted) {
> +		ret = bootstate_backend_nv_init_one(backend, bootstate, target);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int bootstate_backend_nv_save(struct bootstate_backend *backend,
> +				     struct bootstate *bootstate)
> +{
> +	int ret;
> +
> +	ret = bootstate_backend_save(backend, bootstate);
> +	if (ret)
> +		return ret;
> +
> +	return envfs_save(NULL, NULL, 0);
> +}
> +
> +static int bootstate_backend_nv_load(struct bootstate_backend *backend,
> +				     struct bootstate *bootstate)
> +{
> +	return bootstate_backend_load(backend, bootstate);
> +}
> +
> +struct bootstate_backend_nv {
> +	struct bootstate_backend backend;
> +};
> +
> +int bootstate_backend_nv(struct bootstate *bootstate)
> +{
> +	struct bootstate_backend_nv *backend_nv;
> +	struct bootstate_backend *backend;
> +
> +	if (bootstate->backend)
> +		return -EBUSY;
> +
> +	backend_nv = xzalloc(sizeof(*backend_nv));
> +	backend = &backend_nv->backend;
> +
> +	backend->load = bootstate_backend_nv_load;
> +	backend->save = bootstate_backend_nv_save;
> +	backend->name = "nv";
> +	backend->path = "nv";
> +
> +	bootstate->backend = backend;
> +
> +	return bootstate_backend_nv_init(backend, bootstate);
> +}
> +
> +struct bootstate_backend_state {
> +	struct bootstate_backend backend;
> +	struct state *state;
> +};
> +
> +static int bootstate_backend_state_save(struct bootstate_backend *backend,
> +					struct bootstate *bootstate)
> +{
> +	struct bootstate_backend_state *backend_state =
> +		container_of(backend, struct bootstate_backend_state, backend);
> +	int ret;
> +
> +	ret = bootstate_backend_save(backend, bootstate);
> +	if (ret)
> +		return ret;
> +
> +	return state_save(backend_state->state);
> +}
> +
> +static int bootstate_backend_state_load(struct bootstate_backend *backend,
> +					struct bootstate *bootstate)
> +{
> +	return bootstate_backend_load(backend, bootstate);
> +}
> +
> +int bootstate_backend_state(struct bootstate *bootstate, const struct device_node *node)
> +{
> +	struct bootstate_backend_state *backend_state;
> +	struct bootstate_backend *backend;
> +	const struct device_node *state_node;
> +
> +	if (bootstate->backend)
> +		return -EBUSY;
> +
> +	backend_state = xzalloc(sizeof(*backend_state));
> +	backend = &backend_state->backend;
> +
> +	backend->load = bootstate_backend_state_load;
> +	backend->save = bootstate_backend_state_save;
> +	backend->name = "state";
> +
> +	bootstate->backend = backend;
> +
> +	state_node = of_parse_phandle(node, "backend", 0);
> +	if (!state_node)
> +		return -EINVAL;
> +
> +	backend_state->state = state_by_node(state_node);
> +	if (!backend_state->state)
> +		return -EINVAL;
> +
> +	return state_get_name(backend_state->state, &backend->path);
> +}
> +
> +/*
> + * bootstate_new_from_node - create a new bootstate instance from a device_node
> + *
> + * @name	The name of the new bootstate instance
> + * @node	The device_node describing the new bootstate instance
> + */
> +struct bootstate *bootstate_new_from_node(const char *name, const struct device_node *node)
> +{
> +	struct bootstate *bootstate;
> +	int ret;
> +
> +	pr_debug("%s: node=%s, name=%s\n", __func__, node->full_name, name);
> +
> +	bootstate = bootstate_new(name);
> +	if (!bootstate)
> +		return ERR_PTR(-EINVAL);
> +
> +	ret = bootstate_from_node(bootstate, node, true);
> +	if (ret) {
> +		bootstate_release(bootstate);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return bootstate;
> +}
> +
> +/*
> + * bootstate_by_name - find a bootstate instance by name
> + *
> + * @name	The name of the state instance
> + */
> +struct bootstate *bootstate_by_name(const char *name)
> +{
> +	struct bootstate *bs;
> +
> +	list_for_each_entry(bs, &bootstate_list, list) {
> +		if (!strcmp(name, bs->name))
> +			return bs;
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * bootstate_load - load a bootstate from the backing store
> + *
> + * @bootstate	The state instance to load
> + */
> +static int bootstate_load(struct bootstate *bootstate)
> +{
> +	int ret;
> +
> +	if (!bootstate->backend)
> +		return -ENOSYS;
> +
> +	ret = bootstate->backend->load(bootstate->backend, bootstate);
> +	if (ret)
> +		bootstate->dirty = 1;
> +	else
> +		bootstate->dirty = 0;
> +
> +	return ret;
> +}
> +
> +/*
> + * bootstate_save - save a bootstate to the backing store
> + *
> + * @bootstate	The bootstate instance to save
> + */
> +static int bootstate_save(struct bootstate *bootstate)
> +{
> +	int ret;
> +
> +	if (!bootstate->dirty)
> +		return 0;
> +
> +	if (!bootstate->backend)
> +		return -ENOSYS;
> +
> +	ret = bootstate->backend->save(bootstate->backend, bootstate);
> +	if (ret)
> +		return ret;
> +
> +	bootstate->dirty = 0;
> +
> +	return 0;
> +}
> +
> +void bootstate_info(void)
> +{
> +	struct bootstate *bootstate;
> +
> +	printf("registered bootstate instances:\n");
> +
> +	list_for_each_entry(bootstate, &bootstate_list, list) {
> +		printf("%-20s ", bootstate->name);
> +		printf("(backend: %s, path: %s)\n",
> +		       bootstate->backend->name, bootstate->backend->path);
> +	}
> +}
> +
> +#define __BF(arg) [__BOOTCHOOSER_FLAG_##arg##_SHIFT] = __stringify(arg)
> +
> +static const char * const bootstate_flags_str[] = {
> +	__BF(ATTEMPTS_KEEP),
> +	__BF(ATTEMPTS_DEC),
> +	__BF(ATTEMPTS_RESET),
> +	__BF(DEACTIVATE_ON_ZERO_ATTEMPTS),
> +	__BF(VERBOSE),
> +	__BF(DRYRUN),
> +	__BF(RETRY_WITH_DEC),
> +	__BF(WATCHDOG_ENABLE),
> +	__BF(WATCHDOG_TIMEOUT_FROM_STATE),
> +};
> +
> +#undef __BF
> +
> +#define pr(verbose, format, args...) \
> +	({ \
> +		(verbose) ? pr_info((format), ##args) : 0; \
> +	})
> +
> +void _pr_flags(struct bootstate *bootstate, unsigned flags)
> +{
> +	int i;
> +
> +	pr_info("%s: flags=0x%08x\n", bootstate->name, flags);
> +
> +	for (i = 0; i < ARRAY_SIZE(bootstate_flags_str); i++) {
> +		if (flags & (1 << i))
> +			pr_info("%s: -> %s\n", bootstate->name,
> +				bootstate_flags_str[i]);
> +	}
> +}
> +
> +#define pr_flags(verbose, bootstate, flags) \
> +	({ \
> +		(verbose) ? _pr_flags(bootstate, flags) : 0; \
> +	})
> +
> +/*
> + * bootstate_get_target - create a new state instance from a device_node
> + *
> + * @bootstate	the bootstate instance to work in
> + * @flags	supported flags:
> + *		BOOTCHOOSER_FLAG_VERBOSE
> + *		BOOTCHOOSER_FLAG_ATTEMPTS_KEEP
> + *		BOOTCHOOSER_FLAG_ATTEMPTS_DEC
> + *		BOOTCHOOSER_FLAG_ATTEMPTS_RESET
> + *		BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS
> + * @target_out	a string to the choosen boot target is returned via
> + *		this paramater
> + */
> +int bootstate_get_target(struct bootstate *bootstate, unsigned flags, char **target_out)
> +{
> +	struct bootstate_target *target;
> +	int ret;
> +	bool found = false;
> +	bool v = flags & BOOTCHOOSER_FLAG_VERBOSE;
> +
> +	pr_flags(v, bootstate, flags);
> +
> +	ret = bootstate_load(bootstate);
> +	if (ret)
> +		return ret;
> +
> +	if (flags & BOOTCHOOSER_FLAG_ATTEMPTS_RESET) {
> +		list_for_each_entry(target, &bootstate->targets, list) {
> +			if (target->priority ==  0)
> +				continue;
> +
> +			target->remaining_attempts = target->default_attempts;
> +			bootstate->dirty = true;
> +
> +			pr(v, "%s: target: name=%s setting rem to %d due to %s\n",
> +			   bootstate->name, target->name, target->default_attempts,
> +			   bootstate_flags_str[__BOOTCHOOSER_FLAG_ATTEMPTS_RESET_SHIFT]);
> +		}
> +		pr(v, "%s: --------\n", bootstate->name);
> +	}
> +
> +	list_for_each_entry(target, &bootstate->targets, list) {
> +		pr_target(bootstate, target);
> +
> +		if (found)
> +			continue;
> +
> +		if (target->priority == 0) {
> +			pr(v, "%s: name=%s prio=%d - trying next\n",
> +			   bootstate->name, target->name, target->priority);
> +			continue;
> +		}
> +
> +		if (target->remaining_attempts == 0) {
> +			pr(v, "%s: name=%s remaining attempts == 0 - trying next\n",
> +			   bootstate->name, target->name);
> +			continue;
> +		}
> +
> +		if (flags & BOOTCHOOSER_FLAG_ATTEMPTS_DEC) {
> +			bootstate->dirty = true;
> +			target->remaining_attempts--;
> +
> +			pr(v, "%s: name=%s decrementing remaining_attempts to %d due to %s\n",
> +			   bootstate->name, target->name,
> +			   target->remaining_attempts,
> +			   bootstate_flags_str[__BOOTCHOOSER_FLAG_ATTEMPTS_DEC_SHIFT]);
> +
> +			if ((target->remaining_attempts == 0) &&
> +			    (flags & BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS)) {
> +				target->priority = 0;
> +
> +				pr(v, "%s: name=%s deactivating target (setting priority = 0) due to %s\n",
> +				   bootstate->name, target->name,
> +				   bootstate_flags_str[__BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS_SHIFT]);
> +			}
> +		}
> +
> +		found = true;
> +		*target_out = strdup(target->name);
> +		pr_debug("%s: selected target '%s'\n", __func__, target->name);
> +		if (!v)
> +			goto out;
> +
> +		pr(v, "%s: --- other bootsources ---\n", bootstate->name);
> +	}
> +
> + out:
> +	bootstate_save(bootstate);
> +
> +	if (!found)
> +		return -ENOENT;
> +
> +	return 0;
> +}
> +
> +int bootstate_bootchooser(char *name, unsigned flags, unsigned timeout)
> +{
> +	struct bootstate *bootstate;
> +	bool v = flags & BOOTCHOOSER_FLAG_VERBOSE;
> +	char *target;
> +	int ret;
> +
> +	if (!name)
> +		name = "bootstate";
> +
> +	bootstate = bootstate_by_name(name);
> +	if (!bootstate)
> +		return -ENOENT;
> +
> +	if (flags & BOOTCHOOSER_FLAG_WATCHDOG_ENABLE) {
> +		if (flags & BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE) {
> +			ret = bootstate_variable_read_u32(bootstate, "watchdog_timeout",
> +							  &timeout);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (timeout != 0) {
> +			pr(v, "%s: starting watchdog with timeout=%ds\n",
> +			   __func__, timeout);
> +
> +			ret = watchdog_set_timeout(timeout);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	while (true) {
> +		char *cmd;
> +
> +		ret = bootstate_get_target(bootstate, flags, &target);
> +		if (ret)
> +			return ret;
> +
> +		cmd = asprintf("boot %s", target);
> +		free(target);
> +		pr_info("%srunning: %s...\n",
> +			flags & BOOTCHOOSER_FLAG_DRYRUN ? "not " : "", cmd);
> +		if (!(flags & BOOTCHOOSER_FLAG_DRYRUN))
> +			ret = run_command(cmd);
> +		free(cmd);
> +
> +		if (flags & BOOTCHOOSER_FLAG_RETRY_WITH_DEC) {
> +			flags |= BOOTCHOOSER_FLAG_ATTEMPTS_DEC;
> +			flags &= ~(BOOTCHOOSER_FLAG_ATTEMPTS_RESET |
> +				   BOOTCHOOSER_FLAG_ATTEMPTS_KEEP);
> +			continue;
> +		}
> +
> +		return ret;
> +	}
> +
> +	return -ENOENT;
> +}
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 7a5b14697efd..9b3ac244832b 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -19,4 +19,7 @@ config STATE_DRV
>  	tristate "state driver"
>  	depends on STATE
>  
> +config BOOTSTATE_DRV
> +	tristate "bootstate driver"
> +
>  endmenu
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 487e4b8ba2e5..603e14ebb5de 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -5,3 +5,4 @@
>  obj-$(CONFIG_JTAG)		+= jtag.o
>  obj-$(CONFIG_SRAM)		+= sram.o
>  obj-$(CONFIG_STATE_DRV)		+= state.o
> +obj-$(CONFIG_BOOTSTATE_DRV)	+= bootstate.o
> diff --git a/drivers/misc/bootstate.c b/drivers/misc/bootstate.c
> new file mode 100644
> index 000000000000..96b9f23c44da
> --- /dev/null
> +++ b/drivers/misc/bootstate.c
> @@ -0,0 +1,68 @@
> +/*
> + * Copyright (C) 2013 Sascha Hauer <s.hauer@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 <bootstate.h>
> +#include <driver.h>
> +#include <init.h>
> +#include <malloc.h>
> +#include <of.h>
> +#include <string.h>
> +
> +#include <linux/err.h>
> +
> +static int bootstate_probe(struct device_d *dev)
> +{
> +	struct device_node *np = dev->device_node;
> +	struct bootstate *bootstate;
> +	const char *alias;
> +	const char *backend_type = NULL;
> +	int ret;
> +
> +	if (!np)
> +		return -EINVAL;
> +
> +	alias = of_alias_get(np);
> +	if (!alias)
> +		alias = "bootstate";
> +
> +	bootstate = bootstate_new_from_node(alias, np);
> +	if (IS_ERR(bootstate))
> +		return PTR_ERR(bootstate);
> +
> +	of_property_read_string(np, "backend-type", &backend_type);
> +	if (!strcmp(backend_type, "state"))
> +		ret = bootstate_backend_state(bootstate, np);
> +	else if (!strcmp(backend_type, "nv"))
> +		ret = bootstate_backend_nv(bootstate);
> +	else
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +
> +static __maybe_unused struct of_device_id bootstate_ids[] = {
> +	{
> +		.compatible = "barebox,bootstate",
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +
> +static struct driver_d bootstate_driver = {
> +	.name = "bootstate",
> +	.probe = bootstate_probe,
> +	.of_compatible = DRV_OF_COMPAT(bootstate_ids),
> +};
> +device_platform_driver(bootstate_driver);
> diff --git a/include/bootstate.h b/include/bootstate.h
> new file mode 100644
> index 000000000000..53102c527060
> --- /dev/null
> +++ b/include/bootstate.h
> @@ -0,0 +1,38 @@
> +#ifndef __BOOTSTATE_H
> +#define __BOOTSTATE_H
> +
> +#include <of.h>
> +
> +struct bootstate *bootstate_new_from_node(const char *name, const struct device_node *node);
> +struct bootstate *bootstate_find_by_name(const char *name);
> +struct bootstate *bootstate_by_name(const char *name);
> +void bootstate_info(void);
> +int bootstate_backend_nv(struct bootstate *bootstate);
> +int bootstate_backend_state(struct bootstate *bootstate, const struct device_node *node);
> +
> +enum {
> +	__BOOTCHOOSER_FLAG_ATTEMPTS_KEEP_SHIFT,
> +	__BOOTCHOOSER_FLAG_ATTEMPTS_DEC_SHIFT,
> +	__BOOTCHOOSER_FLAG_ATTEMPTS_RESET_SHIFT,
> +	__BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS_SHIFT,
> +	__BOOTCHOOSER_FLAG_VERBOSE_SHIFT,
> +	__BOOTCHOOSER_FLAG_DRYRUN_SHIFT,
> +	__BOOTCHOOSER_FLAG_RETRY_WITH_DEC_SHIFT,
> +	__BOOTCHOOSER_FLAG_WATCHDOG_ENABLE_SHIFT,
> +	__BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE_SHIFT,
> +};
> +
> +#define BOOTCHOOSER_FLAG_ATTEMPTS_KEEP (1 << __BOOTCHOOSER_FLAG_ATTEMPTS_KEEP_SHIFT)
> +#define BOOTCHOOSER_FLAG_ATTEMPTS_DEC (1 << __BOOTCHOOSER_FLAG_ATTEMPTS_DEC_SHIFT)
> +#define BOOTCHOOSER_FLAG_ATTEMPTS_RESET (1 << __BOOTCHOOSER_FLAG_ATTEMPTS_RESET_SHIFT)
> +#define BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS (1 << __BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS_SHIFT)
> +#define BOOTCHOOSER_FLAG_VERBOSE (1 << __BOOTCHOOSER_FLAG_VERBOSE_SHIFT)
> +#define BOOTCHOOSER_FLAG_DRYRUN (1 << __BOOTCHOOSER_FLAG_DRYRUN_SHIFT)
> +#define BOOTCHOOSER_FLAG_RETRY_WITH_DEC (1 << __BOOTCHOOSER_FLAG_RETRY_WITH_DEC_SHIFT)
> +#define BOOTCHOOSER_FLAG_WATCHDOG_ENABLE (1 << __BOOTCHOOSER_FLAG_WATCHDOG_ENABLE_SHIFT)
> +#define BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE (1 << __BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE_SHIFT)
> +
> +int bootstate_get_target(struct bootstate *bootstate, unsigned flags, char **target_out);
> +int bootstate_bootchooser(char *name, unsigned flags, unsigned watchdog_timeout_s);
> +
> +#endif /* __BOOTSTATE_H */
> -- 
> 2.1.4
> 
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox

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

  reply	other threads:[~2015-11-26  8:05 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-27 15:22 Marc Kleine-Budde
2015-11-26  8:04 ` Jan Remmet [this message]
2015-11-27  7:38   ` Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20151126080445.GA3245@lws-weitzel2@phytec.de \
    --to=j.remmet@phytec.de \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox