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.
