* [PATCH 7/7] docs: rework and extend the 'state' and 'bootchooser' documentation
2017-08-15 13:46 Rework of the state/bootchooser feature documentaiton Juergen Borleis
` (5 preceding siblings ...)
2017-08-15 13:46 ` [PATCH 6/7] state: add debugging help Juergen Borleis
@ 2017-08-15 13:46 ` Juergen Borleis
2017-09-06 12:19 ` Rework of the state/bootchooser feature documentaiton Sascha Hauer
7 siblings, 0 replies; 9+ messages in thread
From: Juergen Borleis @ 2017-08-15 13:46 UTC (permalink / raw)
To: barebox
Many links between sections and examples were added to give the developer
the help to get it work.
Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
---
.../devicetree/bindings/barebox/barebox,state.rst | 332 ++++++----
Documentation/user/bootchooser.rst | 493 +++++++++++----
Documentation/user/state.rst | 696 +++++++++++++++++++--
3 files changed, 1204 insertions(+), 317 deletions(-)
diff --git a/Documentation/devicetree/bindings/barebox/barebox,state.rst b/Documentation/devicetree/bindings/barebox/barebox,state.rst
index 40d7bc2e6..4b1aade66 100644
--- a/Documentation/devicetree/bindings/barebox/barebox,state.rst
+++ b/Documentation/devicetree/bindings/barebox/barebox,state.rst
@@ -1,55 +1,89 @@
.. _barebox,state:
-barebox state
+Barebox state
=============
-Overview
---------
+A *state* variable set can be fully described as a devicetree based *state* node.
+This *state* node could be part of the regular platform's devicetree blob or it
+could be an extra devicetree solely for the *state*.
+Devicetree *state* Node
+-----------------------
-Boards often have the need to store variables in persistent memory.
-The constraints are often different from what the regular environment
-can do:
+A *state* node contains a description of a set of variables along with a
+place where the variable set gets stored.
-* compact binary format to make it suitable for small EEPROMs/MRAMs
-* atomic save/restore of the whole variable set
-* redundancy
+Required Properties
+###################
-``barebox,state`` is a framework to describe, access, store and
-restore a set of variables. A state variable set can be fully
-described in a devicetree node. This node could be part of the regular
-devicetree blob or it could be an extra devicetree solely for the
-state. The state variable set contains variables of different types
-and a place to store the variable set.
+* ``compatible``: should be ``barebox,state``
+* ``magic``: a 32bit number
+* ``backend``: phandle to persistent memory
+* ``backend-type``: defines the *state* variable set storage format
+* additionally a *state* node must have an alias in the ``/aliases`` node pointing
+ to it.
-A state node contains a description of a set of variables along with a
-place where the variables are stored.
+.. _barebox,state_magic:
-Required properties:
+The ``magic`` property is a unique number which identifies the *state* variable
+set's variable types and their layout. It should be kept stable as long as the
+variable types and the layout are kept stable. It should also be kept stable
+if new trailing variables are added to the existing layout to be backward
+compatible. Only if the *state* variable set's variable types and/or their layout
+change, the ``magic`` property's number must be changed to be unique again
+with the new *state* variable set's content.
-* ``compatible``: should be ``barebox,state``;
-* ``magic``: A 32bit number used as a magic to identify the state
-* ``backend``: contains a phandle to the device/partition which holds the
- actual state data.
-* ``backend-type``: should be ``raw`` or ``dtb``.
-* additionally a state node must have an alias in the /aliases/ node pointing
- to it.
+.. important:: You should not use the values 0x2354fdf3 and 0x14fa2d02 for your
+ magic value. They're already reserved by the ``direct`` and ``circular``
+ storage backends.
-Optional properties:
+The ``backend`` property uses the *phandle* mechanism to link the *state* to
+a real persistent memory. Refer :ref:`Backend <state_framework,backends>` for
+supported persistent memories.
-* ``algo``: A HMAC algorithm used to detect manipulation of the data
- or header, sensible values follow this pattern ``hmac(<HASH>)``,
- e.g. ``hmac(sha256)``. Only used for ``raw``.
-* ``backend-stridesize``: Maximum size per copy of the data. Only important for
- non-MTD devices
-* ``backend-storage-type``: Normally the correct storage type is detected auto-
- matically. The circular backend supports the option ``noncircular`` to fall
- back to an old storage format.
+The ``backend-type`` should be ``raw`` or ``dtb``. Refer
+:ref:`Backend Types <state_framework,backend_types>` for further details.
-Variable nodes
---------------
+Optional Properties
+###################
-These are subnodes of a state node each describing a single
+* ``backend-stridesize``: stride counted in bytes. See note below.
+* ``backend-storage-type``: Defines the backend storage type to ``direct`` or
+ ``circular``. Defaults to ``circular`` for media which requires erase cycles.
+* ``algo``: A HMAC algorithm used to detect manipulation of the data
+ or header, sensible values follow this pattern ``hmac(<HASH>)``,
+ e.g. ``hmac(sha256)``. Only available for the ``backend-type`` ``raw``.
+
+.. note:: For the ``backend-storage-type`` the keyword ``noncircular`` is still
+ supported as a fall back to an old storage format. Recommendation is to not
+ use this type anymore.
+
+.. _barebox,state_backend_stridesize:
+
+The ``backend-stridesize`` is still optional but required whenever the
+underlaying backend doesn't provide an information how to pad an instance of a
+*state* variable set. This is valid for all underlaying backends which supports
+writes on a byte-by-byte manner or don't have eraseblocks (EEPROM, SRAM and NOR
+type flash backends).
+The ``backend-stridesize`` value is used by the ``direct`` backend storage type
+to place the redundant *state* variable set copies side by side in the backend.
+And it's used by the ``circular`` backend storage type to place the *state*
+variable set copies side by side into the eraseblock.
+You should calculate the ``backend-stridesize`` value very carefully based on
+the used ``backend-type``, the size of the used backend (e.g. partition size
+for example) and its eraseblock size. Refer
+:ref:`Backend Types <state_framework,backend_types>`.
+
+.. note:: It might be useful to add some spare space to the
+ ``backend-stridesize`` to ensure the ability to extend the *state* variable
+ set later on.
+
+.. _barebox,state_variable:
+
+Variable Subnodes
+-----------------
+
+These are subnodes of a *state* node each describing a single
variable. The node name may end with ``@<ADDRESS>``, but the suffix is
stripped from the variable name.
@@ -57,105 +91,161 @@ State variables have a type. Currenty supported types are: ``uint8``,
``uint32``, ``enum32``, ``mac`` address or ``string`` (fixed length string).
Variable length strings are not planned.
-Required properties:
+Required Properties
+###################
* ``reg``: Standard ``reg`` property with ``#address-cells = <1>`` and
``#size-cells = <1>``. Defines the ``offset`` and ``size`` of the
- variable in the ``raw`` backend. ``size`` must fit the node
+ variable in the ``raw`` backend. ``size`` **must fit** the node
``type``. Variables are not allowed to overlap.
-* ``type``: Should be ``uint8``, ``uint32``, ``int32``. ``enum32``, ``mac``
+* ``type``: Should be ``uint8``, ``uint32``, ``enum32``, ``mac``
or ``string`` for the type of the variable
-* ``names``: For ``enum32`` values only, this specifies the values
- possible for ``enum32``.
-
-Optional properties:
-
-* ``default``: The default value if the variable cannot be read from
- storage. For ``enum32`` values it is an integer representing an
- offset into the names array.
-
-Example::
-
- /aliases {
- state = &state;
- };
-
- state: state {
- magic = <0x27031977>;
- compatible = "barebox,state";
- backend-type = "raw";
- backend = <&state_part>;
-
- foo {
- reg = <0x00 0x4>;
- type = "uint32";
- default = <0x0>;
- };
-
- bar {
- reg = <0x10 0x4>;
- type = "enum32";
- names = "baz", "qux";
- default = <1>;
- };
- };
-
- &nand_flash {
- partitions {
- compatible = "fixed-partitions";
- #address-cells = <1>;
- #size-cells = <1>;
- state_part: state@10000 {
- label = "state";
- reg = <0x10000 0x10000>;
- };
- };
- };
-
-Variable Types
---------------
-
-* ``uint8``:
-* ``uint32``:
-* ``int32``:
-* ``enum32``: The ``default`` value is an integer representing an
- offset into the names array.
-* ``mac``:
-* ``string``: The length of the string excluding the trailing 0 is
- determined by the length given in the ``reg`` property.
-
-Backends
---------
-
-Currently two backends exist. The raw backend is a very compact format
-consisting of a magic value for identification, the raw values and a
-CRC. Two copies are maintained for making sure that during update the
-storage device still contains a valid state. The dtb backend stores
-the state as a devicetree binary blob. This is exactly the original
-devicetree description of the state itself, but additionally contains
-the actual values of the variables. Unlike the raw state backend the
-dtb state backend can describe itself.
+* ``names``: For ``enum32`` values only, this specifies the possible values for
+ ``enum32``.
+
+Optional Properties
+###################
+
+* ``default``: The default value if the variable's content cannot be read from
+ the backend. For ``enum32`` values it is an integer representing an offset
+ into the names array.
+
+.. note:: Since the ``default`` property is optional, keep in mind you may need
+ a valid default value if other instances (like the bootchooser for example)
+ depends on it. Due to this, a ``default`` might be a required property instead.
+
+Variable Examples
+#################
+
+``uint8``:
+
+.. code-block:: text
+
+ uint8_example@0 {
+ reg = <0x0 0x1>;
+ type = "uint8";
+ default = <0x00>;
+ };
+
+``uint32``:
+
+.. code-block:: text
+
+ uint32_example@0 {
+ reg = <0x0 0x4>;
+ type = "uint32";
+ default = <100>;
+ };
+
+``enum32``:
+
+.. code-block:: text
+
+ enum_example@0 {
+ reg = <0x0 0x4>;
+ type = "enum32";
+ names = "value#1", "value#2";
+ default = <1>; /* selects "value#2" as the default */
+ };
+
+``mac``:
+
+.. code-block:: text
+
+ mac_example@0 {
+ reg = <0x0 0x6>;
+ type = "mac";
+ };
+
+Since a 'MAC' is a unique system identifier it makes no sense for a default
+value here. It must be set individually at run-time instead.
+
+``string``:
+
+.. code-block:: text
+
+ name {
+ reg = <0x0 0x10>;
+ type = "string";
+ };
+
+In this example the length of the string is limited to 16 characters.
+
+.. _barebox,state_hmac:
HMAC
----
-With the optional property ``algo = "hmac(<HASH>)";`` a HMAC algorithm
-can be defined to detect unauthorized modification of the state's
+With the optional property ``algo = "hmac(<HASH>)";`` an HMAC algorithm
+can be defined to detect unauthorized modification of the state's variable set
header and/or data. For this to work the HMAC and the selected hash
algorithm have to be compiled into barebox.
The shared secret for the HMAC is requested via
``keystore_get_secret()``, using the state's name, from the barebox
simple keystore. It's up to the developer to populate the keystore via
-``keystore_set_secret()`` in beforehand.
+``keystore_set_secret()`` in beforehand. Refer :ref:`command_keystore` for
+further details.
+
+.. _barebox,state_setup:
+
+Configuring the *state* variable set
+------------------------------------
+
+Since the *state* variable set is intended to be shared between the bootloader
+and the kernel, the view to the *state* variable set must be the same in both
+worlds.
+
+This can be achieved by defining all *state* variable set related definitions
+inside the barebox's devicetree only. It's **not** required to keep and maintain
+the same information inside the Linux kernel's devicetree again.
+
+When barebox is instructed to load and forward a devicetree to a Linux kernel
+to be started, it "silently" copies all *state* variable set related definitions
+from its own devicetree into the Linux kernel devicetree. This way both worlds
+behave the same when *state* variable sets should be read or modified.
+
+In order to enable barebox to copy the required information to a dedicated
+location inside the Linux kernel devicetree the name of the memory node to
+store the *state* variable set must be the same in the barebox's devicetree
+and the operating system's devicetree.
+
+With this "interconnection" barebox extends the operating system's devicetree
+with:
+
+- the layout and variable definition of the *state* variable set (in case of
+ the ``raw`` backend-type)
+- the store definition (backend type, backend storage type and so on)
+- partitioning information for the persistent memory in question (on demand)
+- the connection between the backend and the memory (device, partition)
+
+Example:
+
+Lets assume the barebox's devicetree uses the name ``persistent_state_memory@01``
+to define its own *state* variable set backend.
+
+Barebox's devicetree defines:
+
+.. code-block:: text
+
+ persistent_state_memory@01 {
+ compatible = "somevalue";
+ reg = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ state: partition@0 {
+ label = "state";
+ reg = <0x0 0x100>;
+ };
+ };
-Frontend
---------
+The operating system's devicetree defines instead:
-As frontend a state instance is a regular barebox device which has
-device parameters for the state variables. With this the variables can
-be accessed like normal shell variables. The ``state`` command is used
-to save/restore a state to the backend device.
+.. code-block:: text
-After initializing the variable can be accessed with ``$state.foo``.
-``state -s`` stores the state to eeprom.
+ persistent_state_memory@01 {
+ compatible = "somevalue";
+ reg = <1>;
+ };
diff --git a/Documentation/user/bootchooser.rst b/Documentation/user/bootchooser.rst
index cef1d4abb..3d473b596 100644
--- a/Documentation/user/bootchooser.rst
+++ b/Documentation/user/bootchooser.rst
@@ -1,178 +1,217 @@
+.. _bootchooser:
+
Barebox Bootchooser
===================
In many cases embedded systems are layed out redundantly with multiple
-kernels and multiple root file systems. The bootchooser framework provides
+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
+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
+A *bootchooser* boot 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:
+following configuration variables are needed to describe a *bootchooser* boot 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.
+ This controls what barebox actually boots for this boot 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.
+ The default number of attempts that a boot target shall be tried before skipping it.
``global.bootchooser.<targetname>.default_priority``
- The default priority of a target.
+ The default priority of a boot target.
-Additionally the following runtime variables are needed. Unlinke the configuration
-variables these are automatically changed by the bootchooser algorithm:
+Additionally the following run-time variables are needed. Unlike the configuration
+variables their values are automatically updated 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.
+ The current ``priority`` of the boot target. Higher numbers have higher priorities.
+ A ``priority`` of 0 means the boot 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 ``remaining_attempts`` counter. Only boot targets with a ``remaining_attempts``
+ counter > 0 are started.
+
+The *bootchooser* algorithm generally only starts boot targets that have a ``priority``
+> 0 and a ``remaining_attempts`` counter > 0.
-The bootchooser algorithm generally only starts targets that have a priority
-> 0 and a remaining_attempts counter > 0.
+.. _bootchooser,algorithm:
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.
+The *bootchooser* algorithm is very simple. It works with two variables per boot target
+and some optional flags. The variables are the ``remaining_attempts`` counter that
+tells how many times the boot target will be started. The other variable is the ``priority``,
+the boot target with the highest ``priority`` will be used first, a zero ``priority``
+means the boot target is disabled.
+
+When booting, *bootchooser* starts the boot target with the highest ``priority`` that
+has a non-zero ``remaining_attempts`` counter. With every start of a boot target the
+``remaining_attempts`` counter of this boot target is decremented by one. This means
+every boot target's ``remaining_attempts`` counter reaches zero sooner or later and
+the boot 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:
+
+- empty: counters will never be reset
+- ``power-on``: The ``remaining_attempts`` counters of all enabled boot targets are reset
+ after a ``power-on`` reset (``$global.system.reset="POR"``). This means after a power
+ cycle all boot targets will be tried again for the configured number of retries.
+- ``all-zero``: The ``remaining_attempts`` counters of all enabled boot targets are
+ reset when none of them has any ``remaining_attempts`` left.
+
+Additionally the ``remaining_attempts`` counter can be reset manually using the
+:ref:`bootchoser command <command_bootchooser>`. 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 be used to reset the remaining_attempts
-counter from the currently running system.
+barebox-state tool from the dt-utils_ package can be used to reset the
+``remaining_attempts`` counter from the running system.
.. _dt-utils: http://git.pengutronix.de/?p=tools/dt-utils.git;a=summary
-Bootchooser General Options
+General Bootchooser Options
---------------------------
-Additionally to the target options described above, bootchooser has some general
-options not specific to any target.
+In addition to the boot target options described above, *bootchooser* has some general
+options not specific to any boot target.
``global.bootchooser.disable_on_zero_attempts``
- Boolean flag. If set to 1, bootchooser disables a target (sets priority to 0) whenever
- the remaining attempts counter reaches 0.
+ Boolean flag. If set to 1, *bootchooser* disables a boot 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.
+ The default number of attempts that a boot target shall be tried before skipping
+ it, used when not overwritten with the boot 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.
+ The default priority of a boot 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
+ Already described in :ref:`Bootchooser Algorithm <bootchooser,algorithm>`
``global.bootchooser.reset_priorities``
- A space separated list of events that cause bootchooser to reset the priorities of
- all targets. Possible values:
+ A space separated list of events that cause *bootchooser* to reset the priorities of
+ all boot targets. Possible values:
* empty: priorities will never be reset
- * all-zero: priorities will be reset when all targets have zero priority
+ * ``all-zero``: priorities will be reset when all targets have zero priority
``global.bootchooser.retry``
- If set to 1, bootchooser retries booting until one succeeds or no more valid targets
- exist.
+ If set to 1, *bootchooser* retries booting until one succeeds or no more valid
+ boot targets exist.
+ Otherwise the ``boot`` command will return with an error after the first failed
+ boot target.
``global.bootchooser.state_prefix``
- Variable prefix when bootchooser is used with the state framework as backend for storing
- runtime data, see below.
+ Variable prefix when *bootchooser* is used with the *state* framework as backend
+ for storing run-time data, see below.
``global.bootchooser.targets``
- Space separated list of targets that are used. For each entry in the list a corresponding
+ Space separated list of boot targets that are used. For each entry in the list
+ a corresponding
set of ``global.bootchooser.<targetname>.<variablename>`` 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``.
+ *bootchooser* sets this to the boot target that was chosen on last boot (index).
-Example
--------
+.. _bootchooser,setup_example:
+
+Setup Example
+-------------
+
+We want to set up a redundant machine with two bootable systems within one shared
+memory, here a NAND type flash memory with a UBI partition. We have a 512 MiB NAND
+type flash, to be used only for the root filesystem. The devicetree doesn't
+define any partition, because we want to run one UBI partition with two volumens
+for the redundant root filesystems on this flash memory.
+
+.. code-block:: text
-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'.
+ nand@0 {
+ [...]
+ };
+
+In order to configure this machine the following steps can be used:
.. 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
+ ubiformat /dev/nand0 -y
+ ubiattach /dev/nand0
+ ubimkvol /dev/nand0.ubi root_filesystem_1 256MiB
+ ubimkvol /dev/nand0.ubi root_filesystem_2 0
+
+The last command creates a volume which fills the remaining available space
+on the NAND type flash memory, which will be most of the time smaller than
+256 MiB due to factory bad blocks and lost data blocks for UBI's management.
+
+After this preparation we can find two devices in ``/dev``:
+
+- ``nand0.ubi.root_filesystem_1``
+- ``nand0.ubi.root_filesystem_2``
+
+These two devices can now be populated with their filesystem content. In our
+example here we additionally assume, that these root filesystems contain a Linux
+kernel with its corresponding devicetree via boot spec (refer to
+:ref:`Bootloader Spec <bootloader_spec>` for further details).
+
+Either device can be booted with the :ref:`boot <command_boot>` command command,
+and thus can be used by the *bootchooser* and we can start to configure the
+*bootchooser* variables.
+
+The following example shows how to initialize two boot targets, ``system1`` and
+``system2``. Both boot from a UBIFS on ``nand0``, the former has a priority of
+21 and boots from the volume ``root_filesystem_1`` whereas the latter has a
+priority of 20 and boots from the volume ``root_filesystem_2``.
+
+.. code-block:: sh
# initialize target 'system1'
- nv bootchooser.system1.boot=nand0.ubi.system1
+ nv bootchooser.system1.boot=nand0.ubi.root_filesystem_1
nv bootchooser.system1.default_attempts=3
- nv bootchooser.system1.default_priority=20
+ nv bootchooser.system1.default_priority=21
+
+ # initialize target 'system2'
+ nv bootchooser.system2.boot=nand0.ubi.root_filesystem_2
+ nv bootchooser.system2.default_attempts=3
+ nv bootchooser.system2.default_priority=20
# make targets known
- nv bootchooser.targets="system0 system1"
+ nv bootchooser.targets="system1 system2"
# retry until one target succeeds
- nv bootchooser.retry="true"
+ nv bootchooser.retry=1
# 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.
+.. note:: This example is for testing only, normally the NV variables would be
+ initialized directly by files in the default environment, not with a script.
+
+The run-time values are stored in environment variables as well. Alternatively,
+they can be stored in a *state* variable set instead. Refer to
+:ref:`using the state framework <bootchooser,state_framework>` for further
+details.
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.
+This section describes some scenarios that can be handled by bootchooser. All
+scenarios assume multiple boot targets 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.
+- a system that shall always boot without user interaction
+- staying in the bootloader is not an option.
+
+In this scenario a boot target is started for the configured number of remaining
+attempts. If it cannot be started successfully, the next boot target is chosen.
+This repeats until no boot targets are left to start, then all remaining attempts
+are reset to their defaults and the first boot target is tried again.
Settings
^^^^^^^^
@@ -186,22 +225,26 @@ Settings
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.
+#. barebox or flash robot fills all boot targets with valid systems.
+#. The all-zero settings will lead to automatically enabling the boot targets,
+ 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.
+Recovery will only be called when all boot targets are not startable (That is,
+no valid kernel found or read failure). Once a boot target is startable (a
+valid kernel is found and started) *bootchooser* will never fall through to
+the recovery boot 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).
+- a system with multiple boot targets
+- one recovery system
+
+A boot target that was booted three times without success shall never be booted
+again (except after update or user interaction).
Settings
^^^^^^^^
@@ -216,23 +259,27 @@ Settings
Deployment
^^^^^^^^^^
-#. barebox or flash robot fills all slots with valid systems.
-#. barebox or flash robot marks slots as good or state contains non zero
+#. barebox or flash robot fills all boot targets with valid systems.
+#. barebox or flash robot marks boot targets 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.
+Done by 'recovery' boot target which is booted after the *bootchooser* falls
+through due to the lack of bootable targets. This boot 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.
+- a system with multiple boot targets
+- one recovery system
+- a power cycle shall not be counted as failed boot.
+
+Booting a boot target three times without success disables it.
Settings
^^^^^^^^
@@ -247,33 +294,205 @@ Settings
Deployment
^^^^^^^^^^
-#. barebox or flash robot fills all slots with valid systems.
-#. barebox or flash robot marks slots as good.
+#. barebox or flash robot fills all boot targets with valid systems.
+#. barebox or flash robot marks boot targets 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:
+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.
-- A system that will be booted as recovery.
-- A barebox script that will be started.
+.. _bootchooser,state_framework:
+
+Using the *State* Framework as Backend for Run-Time Variable Data
+-----------------------------------------------------------------
+
+Usually the *bootchooser* modifies its data in global variables which are
+connected to :ref:`non volatile variables <config_device>`.
+
+Alternatively the :ref:`state_framework` can be used for this data, which
+allows to store this data redundantly in some kind of persistent memory.
+
+In order to let the *bootchooser* use the *state* framework for its storage
+backend, configure the ``bootchooser.state_prefix`` variable with the *state*
+variable set instance name.
+
+Usually a generic *state* variable set in the devicetree is defined like this
+(refer to :ref:`barebox,state` for more details):
+
+.. code-block:: text
+
+ some_kind_of_state {
+ [...]
+ };
+
+At barebox run-time this will result in a *state* variable set instance called
+*some_kind_of_state*. You can also store variables unrelated to *bootchooser* (a
+serial number, MAC address, …) in it.
+
+Extending this *state* variable set by information required by the *bootchooser*
+is simply done by adding so called 'boot targets' and optionally one ``last_chosen``
+node. It then looks like:
+
+.. code-block:: text
+
+ some_kind_of_state {
+ [...]
+ boot_target_1 {
+ [...]
+ };
+ boot_target_2 {
+ [...]
+ };
+ };
+
+It could makes sense to store the result of the last *bootchooser* operation
+in the *state* variable set as well. In order to do so, add a node with the name
+``last_chosen`` to the *state* variable set. *bootchooser* will use it if present.
+The *state' variable set definition then looks like:
+
+.. code-block:: text
+
+ some_kind_of_state {
+ [...]
+ boot_target_1 {
+ [...]
+ };
+ boot_target_2 {
+ [...]
+ };
+ last_chosen {
+ reg = <offset 0x4>;
+ type = "uint32";
+ };
+ };
+
+The ``boot_target_*`` names shown above aren't variables themselves (like the other
+variables in the *state* variable set), they are named containers instead, which
+are used to group variables specific to *bootchooser*.
+
+A 'boot target' container has the following fixed content:
+
+.. code-block:: text
+
+ some_boot_target {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ remaining_attempts {
+ [...]
+ default = <some value>; /* -> read note below */
+ };
+
+ priority {
+ [...]
+ default = <some value>; /* -> read note below */
+ };
+ };
+
+.. important:: Since each variable in a *state* variable set requires a ``reg``
+ property, the value of its ``reg`` property must be unique, e.g. the offsets
+ must be consecutive from a global point of view, as they describe the
+ storage layout in the backend memory.
+
+So, ``remaining_attempts`` and ``priority`` are required variable nodes and are
+used to setup the corresponding run-time environment variables in the
+``global.bootchooser.<targetname>`` namespace.
+
+.. important:: It is important to provide a ``default`` value for each variable
+ for the case when the *state* variable set backend memory is uninitialized.
+ This is also true if default values through the *bootchooser's* environment
+ variables are defined (e.g. ``bootchooser.default_attempts``,
+ ``bootchooser.default_priority`` and their corresponding boot target specific
+ variables). The former ones are forwarded to the *bootchooser* to make a
+ decision, the latter ones are used by the *bootchooser* to make a decision
+ the next time.
+
+Example
+#######
+
+For this example we use the same system and its setup described in
+:ref:`setup example <bootchooser,setup_example>`. The resulting devicetree
+content for the *state* variable set looks like:
+
+.. code-block:: text
+
+ system_state {
+ [...]
+ system1 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ remaining_attempts@0 {
+ reg = <0x0 0x4>;
+ type = "uint32";
+ default = <3>;
+ };
+ priority@4 {
+ reg = <0x4 0x4>;
+ type = "uint32";
+ default = <20>;
+ };
+ };
+
+ system2 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ remaining_attempts@8 {
+ reg = <0x8 0x4>;
+ type = "uint32";
+ default = <3>;
+ };
+ priority@c {
+ reg = <0xc 0x4>;
+ type = "uint32";
+ default = <21>;
+ };
+ };
+
+ last_chosen@10 {
+ reg = <0x10 0x4>;
+ type = "uint32";
+ };
+ };
+
+.. important:: While the ``system1/2`` nodes suggest a different namespace inside the
+ *state* variable set, the actual variable's ``reg``-properties and their offset
+ part are always relative to the whole *state* variable set and thus must be
+ consecutive globally.
+
+To make *bootchooser* use the so called ``system_state`` *state* variable set
+instead of the NV run-time environment variables, we just set:
+
+.. code-block:: text
+
+ global.bootchooser.state_prefix=system_state
+
+.. note:: Its a good idea to keep the ``bootchooser.<targetname>.default_priority``
+ and ``bootchooser.<targetname>.default_attempts`` values in sync with the
+ corresponding default values in the devicetree.
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.
+Updating a boot target 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* boot targets or a dedicated recovery system. For the
+regular *bootchooser* boot targets updating is done like:
+
+- Disable the inactive (e.g. not used right now) boot target by setting its
+ ``priority`` to 0.
+- Update the inactive boot target.
+- Set ``remaining_attempts`` of the inactive boot target to nonzero.
+- Enable the inactive boot target by setting its ``priority`` to a higher value
+ than any other boot target (including the used one right now).
- Reboot.
-- If necessary update the now inactive, not yet updated slot the same way.
+- If necessary update the now inactive, not yet updated boot target the same way.
-One way of updating systems is using RAUC_ which integrates well with the bootchooser
+One way of updating systems is using RAUC_ which integrates well with the *bootchooser*
in barebox.
-.. _RAUC: https://rauc.readthedocs.io/en/latest/ RAUC (
+.. _RAUC: https://rauc.readthedocs.io/en/latest/
diff --git a/Documentation/user/state.rst b/Documentation/user/state.rst
index 73c4be815..7d6c5770d 100644
--- a/Documentation/user/state.rst
+++ b/Documentation/user/state.rst
@@ -3,65 +3,643 @@
Barebox State Framework
=======================
-The state framework is build to exchange data between Barebox and Linux
-userspace using a non-volatile storage. There are several components involved.
-Barebox has a state driver to access the variables. For the Linux Userspace
-there is a userspace tool.
-
-Devicetree
-----------
-
-Currently the devicetree defines the layout of the variables and data.
-Variables are fixed size. Several types are supported, see the binding
-documentation for details.
-
-Data Formats
-------------
-
-The state data can be stored in different ways. Currently two formats are
-available, ``raw`` and ``dtb``. Both format the state data differently.
-Basically these are serializers. The raw serializer additionally supports a
-HMAC algorithm to detect manipulations.
-
-The data is always stored in a logical unit called ``bucket``. A ``bucket`` has
-its own size depending on some external contraints. These contraints are listed
-in more detail below depending on the used memory type and storage backend. A
-``bucket`` stores exactly one state. A default number of three buckets is used
-to store data redundantely.
-
-Redundancy
-----------
-
-The state framework is safe against powerfailures during write operations. To
-archieve that multiple buckets are stored to disk. When writing all buckets are
-written in order. When reading, the buckets are read in order and the first
-one found that passes CRC tests is used. When all data is read the buckets
-containing invalid or outdated data are written with the data just read. Also
-NAND blocks need cleanup due to excessive bitflips are rewritten in this step.
-With this it is made sure that after successful initialization of a state the
-data on the storage device is consistent and redundant.
-
-Storage Backends
-----------------
-
-The serialized data can be stored to different backends. Currently two
-implementations exist, ``circular`` and ``direct``. The state framework automatically
-selects the correct backend depending on the storage medium. Media requiring
-erase operations (NAND, NOR flash) use the ``circular`` backend, others use the ``direct``
-backend. The purpose of the ``circular`` backend is to save erase cycles which may
-wear out the flash blocks. It continuously fills eraseblocks with updated data
-and only when an eraseblock if fully written erases it and starts over writing
-new data to the same eraseblock again.
-
-For all backends multiple copies are written to handle read errors.
-
-Commands
+Boards often have the need to store variable sets in persistent memory. barebox
+could handle that with its regular environment. But the constraints for such a
+variable set are often different from what the regular environment can do:
+
+* compact binary format to make it suitable for small non-volatile memories
+* atomic save/restore of the whole variable set
+* redundancy
+
+*state* is a framework to describe, access, store and restore a set of variables
+and is prepared to exchange/share data between barebox and Linux userspace using
+some kind of persistent memory.
+
+Already known users of the *state* information are the :ref:`bootchooser` and
+RAUC_.
+
+.. _RAUC: https://rauc.readthedocs.io/en/latest/
+
+barebox itself uses a *state* driver to access the variables in the
+persistent memory. For the Linux run-time there is a userspace tool_ to do
+the same.
+
+.. _tool: https://git.pengutronix.de/cgit/tools/dt-utils/
+
+To define a *state* variable set, a devicetree based description is used. Refer to
+:ref:`barebox,state` for further details.
+
+There are several software components involved, which are described in this
+section.
+
+.. _state_framework,backends:
+
+Backends (e.g. Supported Memory Types)
+--------------------------------------
+
+Some non-volatile memory is need for storing a *state* variable set:
+
+- all kinds of NOR flash memories
+- all kinds of NAND flash memories
+- MRAM
+- EEPROM
+- all kind of SRAMs (backup battery assumed)
+
+For classic MTDs (NOR/NAND/SRAM), a partition is required and understood by
+the Linux kernel as well to define the location inside the device where to store
+the *state* variable set. For non-MTDs (MRAM/EEPROM) a different approach is
+required to define the location where to store the *state* variable set.
+
+.. _state_framework,backend_types:
+
+Backend-Types (e.g. *state* storage format)
+-------------------------------------------
+
+The *state* variable set itself can be stored in different ways. Currently two
+formats are available, ``raw`` and ``dtb``.
+
+Both serialize the *state* variable set differently.
+
+.. note:: The ``raw`` serializer additionally supports an HMAC algorithm to
+ detect manipulations. Refer to :ref:`HMAC <barebox,state_hmac>` for further
+ details.
+
+.. _state_framework,raw:
+
+The ``raw`` *state* storage format
+##################################
+
+``raw`` means the *state* variable set is a simple binary data blob only. In
+order to handle it, the *state* framework puts a management header in front of
+the binary data blob with the following content and layout:
+
+ +---------+---------+---------------------------------------------------+
+ | Offset | Size | Content |
+ +---------+---------+---------------------------------------------------+
+ | 0x00 | 4 bytes | 'magic value' |
+ +---------+---------+---------------------------------------------------+
+ | 0x04 | 2 bytes | reserved, filled with zero bits |
+ +---------+---------+---------------------------------------------------+
+ | 0x06 | 2 bytes | byte count of binary data blob |
+ +---------+---------+---------------------------------------------------+
+ | 0x08 | 4 bytes | CRC32 of binary data blob (offset 0x10...) |
+ +---------+---------+---------------------------------------------------+
+ | 0x0c | 4 bytes | CRC32 of header (offset 0x00...0x0b) |
+ +---------+---------+---------------------------------------------------+
+ | 0x10... | | binary data blob |
+ +---------+---------+---------------------------------------------------+
+
+- 'magic value' is an unsigned value with native endianness, refer to
+ :ref:`'magic' property <barebox,state_magic>` about its value.
+- 'byte count' is an unsigned value with native endianness
+- 'binary data blob CRC32' is an unsigned value with native endianness
+- 'header CRC32' is an unsigned value with native endianness
+
+.. note:: the 32-bit CRC calculation uses the polynomial:
+
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1
+
+Since the binary data blob has no built-in description of the embedded *state*
+variable set, it depends on an external layout definition to encode
+and decode it correctly. A devicetree based description is used to describe the
+embedded *state* variable set. Refer to
+:ref:`Variable Subnodes <barebox,state_variable>` for further details.
+
+.. important:: It is important to share this layout definition in all
+ 'worlds' which want to read or manipulate the *state* variable set. This
+ includes offsets, sizes and endianesses of the binary data. Refer to
+ :ref:`Configuring the state variable set <barebox,state_setup>` on how to
+ setup barebox to ensure this is done automatically for devicetree based
+ operating systems.
+
+.. note:: When calculating the ``backend-stridesize`` take the header overhead
+ into account. The header overhead is always 16 bytes.
+
+.. _state_framework,dtb:
+
+The ``dtb`` *state* storage format
+##################################
+
+.. note:: The ``dtb`` backend type isn't well tested. Use the ``raw`` backend
+ when in doubt.
+
+The ``dtb`` backend type stores the *state* variable set as a devicetree binary
+blob. This is exactly the original devicetree description of the *state* variable
+set itself, but additionally contains the actual values of the variable set.
+Unlike the ``raw`` *state* backend the ``dtb`` *state* backend can describe itself.
+
+.. _state_framework,backend_storage_type:
+
+Backend Storage Types (e.g. media storage layout)
+-------------------------------------------------
+
+The serialized data (``raw`` or ``dtb``) can be stored to different backend
+storage types. These types are dedicated to different memory types.
+
+Currently two backend storage type implementations do exist, ``circular`` and
+``direct``.
+
+The state framework can select the correct backend storage type depending on the
+backend medium. Media requiring erase operations (NAND, NOR flash) defaults to
+the ``circular`` backend storage type automatically. In contrast EEPROMs and
+RAMs are candidates for the ``direct`` backend storage type.
+
+Direct Storage Backend
+######################
+
+This kind of backend storage type is intended to be used with persistent RAMs or
+EEPROMs.
+These media are characterized by:
+
+- memory cells can be simply written at any time (no previous erase required)
+- memory cells can be written as often as required (unlimted or very high endurance)
+- can be written on a byte-by-byte manner
+
+Example: MRAM with 64 bytes at device's offset 0:
+
+.. code-block:: text
+
+ 0 0x3f
+ +-------------------------------------------------------------------+
+ | |
+ +-------------------------------------------------------------------+
+
+Writing the *state* variable set always happens at the same offset:
+
+.. code-block:: text
+
+ 0 0x3f
+ +-------------------------------------------+-----------------------+
+ | copy | |
+ +-------------------------------------------+-----------------------+
+
+.. important:: The ``direct`` storage backend needs 8 bytes of additional space
+ per *state* variable set for its meta data.
+
+Circular Storage Backend
+########################
+
+This kind of backend storage type is intended to be used with regular flash memory devices.
+
+Flash memories are characterized by:
+
+- only erased memory cells can be written with new data
+- written data cannot be written twice (at least not for modern flash devices)
+- erase can happen on eraseblock sizes only (detectable, physical value)
+- an eraseblock only supports a limited number of write-erase-cycles (as low as a few thousand cycles)
+
+The purpose of the ``circular`` backend storage type is to save erase cycles
+which may wear out the flash's eraseblocks. This type instead incrementally fills
+an eraseblock with updated data and only when an eraseblock
+is fully written, it erases it and starts over writing new data to the same
+eraseblock again.
+
+**NOR type flash memory is additionally characterized by**
+
+- can be written on a byte-by-byte manner
+
+.. _state_framework,nor:
+
+Example: NOR type flash memory with 64 kiB eraseblock size
+
+.. code-block:: text
+
+ 0 0x0ffff
+ +--------------------------------------------------------------------+
+ | |
+ +--------------------------------------------------------------------+
+ |<--------------------------- eraseblock --------------------------->|
+
+Writing the *state* variable set the very first time:
+
+.. code-block:: text
+
+ 0
+ +------------+------------
+ | copy |
+ | #1 |
+ +------------+------------
+ |<- stride ->|
+ |<---- eraseblock -------
+
+'copy#1' will be used.
+
+Changing the *state* variable set the first time (e.g. writing it the second time):
+
+.. code-block:: text
+
+ 0
+ +------------+------------+------------
+ | copy | copy |
+ | #1 | #2 |
+ +------------+------------+------------
+ |<- stride ->|<- stride ->|
+ |<------------- eraseblock -----------
+
+'copy#2' will now be used and 'copy#1' will be ignored.
+
+Changing the *state* variable set the n-th time:
+
+.. code-block:: text
+
+ 0 0x0ffff
+ +------------+------------+-------- -------+------------+------------+
+ | copy | copy | | copy | copy |
+ | #1 | #2 | | #n-1 | #n |
+ +------------+------------+-------- -------+------------+------------+
+ |<- stride ->|<- stride ->| |<- stride ->|<- stride ->|
+ |<---------------------------- eraseblock -------------------------->|
+
+'copy#n' will now be used and all other copies will be ignored.
+
+The next time the *state* variable set changes again, the whole block will be
+erased and the *state* variable set gets stored at the first position inside
+the eraseblock again. This reduces the need for a flash memory erase by factors.
+
+**NAND type flash memory is additionally characterized by**
+
+- organized in pages (size is a detectable, physical value)
+- writes can only happen in multiples of the page size (which much less than the eraseblock size)
+- partially writing a page can be limited in count or be entirely forbidden (in
+ the case of *MLC* NANDs)
+
+Example: NAND type flash memory with 128 kiB eraseblock size and 2 kiB page
+size and a 2 kiB write size
+
+.. code-block:: text
+
+ 0 0x20000
+ +------+------+------+------+---- ----+------+------+------+------+
+ | page | page | page | page | | page | page | page | page |
+ | #1 | #2 | #3 | #4 | | #61 | #62 | #63 | #64 |
+ +------+------+------+------+---- ----+------+------+------+------+
+ |<-------------------------- eraseblock ------------------------->|
+
+Writing the *state* variable set the very first time:
+
+.. code-block:: text
+
+ |<--- page #1---->|
+ +-------+---------+--
+ | copy | |
+ | #1 | |
+ +-------+---------+--
+ |<---- eraseblock ---
+
+'copy#1' will be used.
+
+Changing the *state* variable set the first time (e.g. writing it the second time):
+
+.. code-block:: text
+
+ |<-- page #1 -->|<-- page #2 -->|
+ +-------+-------+-------+-------+----
+ | copy | | copy | |
+ | #1 | | #2 | |
+ +-------+-------+-------+-------+----
+ |<--------- eraseblock --------------
+
+'copy#2' will now be used and 'copy#1' will be ignored.
+
+Changing the *state* variable set the 64th time:
+
+.. code-block:: text
+
+ |<-- page #1 -->|<-- page #2 -->| |<- page #63 -->|<- page #64 -->|
+ +-------+-------+-------+-------+-- --+-------+-------+-------+-------+
+ | copy | | copy | | | copy | | copy | |
+ | #1 | | #2 | | | #63 | | #64 | |
+ +-------+-------+-------+-------+-- --+-------+-------+-------+-------+
+ |<----------------------------- eraseblock ----------------------------->|
+
+'copy#n' will now be used and all other copies will be ignored.
+
+The next time the *state* variable set changes again, the eraseblock will be
+erased and the *state* variable set gets stored at the first position inside
+the eraseblock again. This significantly reduces the need for a block erases.
+
+.. important:: One copy of the *state* variable set is limited to the page size
+ of the used backend (e.g. NAND type flash memory)
+
+Redundant *state* variable set copies
+-------------------------------------
+
+To avoid data loss when changing the *state* variable set, more than one
+*state* variable set copy can be stored into the backend. Whenever the *state*
+variable set changes, only one *state* variable set copy gets changed at a time.
+In the case of an interruption and/or power loss resulting into an incomplete
+write to the backend, the system can fall back to a different *state* variable
+set copy (previous *state* variable set).
+
+Direct Storage Backend Redundancy
+#################################
+
+For this kind of backend storage type a value for the stride size must be
+defined by the developer (refer to
+:ref:`backend-stridesize <barebox,state_backend_stridesize>`).
+
+It always stores **three** redundant copies of the backend-type. Keep this in
+mind when calculating the stride size and defining the backend size (e.g. the
+size of a partition).
+
+.. code-block:: text
+
+ +----------------+------+----------------+------+----------------+------+
+ | redundant copy | free | redundant copy | free | redundant copy | free |
+ +----------------+------+----------------+------+----------------+------+
+ |<---- stride size ---->|<---- stride size ---->|<---- stride size ---->|
+
+.. important:: The ``direct`` storage backend needs 8 bytes of additional space
+ per *state* variable set for its meta data. Keep this in mind when calculating
+ the stridesize. For example, if your variable set needs 8 bytes, the ``raw``
+ header needs 16 bytes and the ``direct`` storage backend additionally 8 bytes.
+ The full space for one *state* variable set is now 8 + 16 + 8 = 32 bytes.
+
+Circular Storage Backend Redundancy
+###################################
+
+**NOR type flash memory**
+
+Redundant copies of the *state* variable set are stored based on the memory's
+eraseblocks and this size is automatically detected at run-time.
+It needs a stride size as well, because a NOR type flash memory can be written
+on a byte-by-byte manner.
+In contrast to the ``direct`` storage backend redundancy, the
+stride size for the ``circular`` storage backend redundancy defines the
+side-by-side location of the *state* variable set copies.
+
+.. code-block:: text
+
+ |<X>|<X>|...
+ +--------------------------------+--------------------------------+--
+ |C#1|C#2|C#3|C#4|C#5| |C#1|C#2|C#3|C#4|C#5| |
+ +--------------------------------+--------------------------------+--
+ |<--------- eraseblock --------->|<--------- eraseblock --------->|<-
+ |<------- redundant area ------->|<------- redundant area ------->|<-
+
+*<X>* defines the stride size, *C#1*, *C#2* the *state* variable set copies.
+
+Since these kinds of MTD devices are partitioned, its a good practice to always
+reserve multiple eraseblocks for the barebox's *state* feature. Keep in mind:
+Even NOR type flash memories can be worn out.
+
+**NAND type flash memory**
+
+Redundant copies of the *state* variable set are stored based on the memory's
+eraseblocks and this size is automatically detected at run-time.
+
+.. code-block:: text
+
+ +------+------+--- ---+------+------+------+------+--- ---+------+------+--
+ | copy | copy | | copy | copy | copy | copy | | copy | copy |
+ | #1 | #2 | | #63 | #64 | #1 | #2 | | #63 | #64 |
+ +------+------+--- ---+------+------+------+------+--- ---+------+------+--
+ |<----------- eraseblock ---------->|<----------- eraseblock ---------->|<-
+ |<-------- redundant area --------->|<-------- redundant area --------->|<-
+
+Since these kinds of MTD devices are partitioned, its a good practice to always
+reserve multiple eraseblocks for the barebox's *state* feature. Keep in mind:
+NAND type flash memories can be worn out, factory bad blocks can exist from the
+beginning.
+
+Handling of Bad Blocks
+----------------------
+
+NAND type flash memory can have factory bad eraseblocks and more bad
+eraseblocks can appear over the life time of the memory. They are detected by
+the MTD layer, marked as bad and never used again.
+
+.. important:: If NAND type flash memory should be used as a backend, at least
+ three eraseblocks are used to keep three redundant copies of the *state*
+ variable set. You should add some spare eraseblocks to the backend
+ partition by increasing the partition's size to a suitable value to handle
+ factory bad eraseblocks and worn-out eraseblocks.
+
+Examples
+--------
+
+The following examples intends to show how to setup and interconnect all
+required components for various non-volatile memories.
+
+All examples use just one *state* variable of type *uint8* named ``variable``
+to keep them simple. For the ``raw`` backend-type it means one *state*
+variable set has a size of 17 bytes (16 bytes header plus one byte variables).
+
+.. note:: The mentioned ``aliases`` and the *state* variable set node entries
+ are members of the devicetree's root node.
+
+.. note:: For a more detailed description of the used *state* variable set
+ properties here, refer to :ref:`barebox,state`.
+
+NOR flash memories
+##################
+
+This type of memory can be written on a single byte/word basis (depending on its bus
+width), but must be erased prior writing the same byte/word again and erasing
+must happen on an eraseblock basis. Typical eraseblock sizes are 128 kiB or
+(much) larger for parallel NOR flashes and 4 kiB or larger for serial NOR
+flashes.
+
+From the Linux kernel perspective this type of memory is a *Memory Technologie
+Device* (aka 'MTD') and handled by barebox in the same manner. It needs a
+partition configuration.
+
+The following devicetree node entry defines some kind of NOR flash memory and
+a partition at a specific offset to be used as the backend for the
+*state* variable set.
+
+.. code-block:: text
+
+ norflash@0 {
+ backend_state_nor: partition@120000 {
+ [...]
+ };
+ };
+
+With this 'backend' definition its possible to define the *state* variable set
+content, its backend-type and *state* variable set layout.
+
+.. code-block:: text
+
+ aliases {
+ state = &state_nor;
+ };
+
+ state_nor: nor_state_memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "barebox,state";
+ magic = <0x512890a0>;
+ backend-type = "raw";
+ backend = <&backend_state_nor>;
+ backend-storage-type = "circular";
+ backend-stridesize = <32>;
+
+ variable {
+ reg = <0x0 0x1>;
+ type ="uint8";
+ default = <0x1>;
+ };
+ };
+
+NAND flash memories
+###################
+
+This type of memory can be written on a *page* base (typically 512 bytes,
+2048 or (much) larger), but must be erased prior writing the same page again and
+erasing must happen on an eraseblock base. Typical eraseblock sizes are
+64 kiB or (much) larger.
+
+From the Linux kernel perspective this type of memory is a *Memory Technologie
+Device* (aka 'MTD') and handled by barebox in the same manner. It needs a
+partition configuration.
+
+The following devicetree node entry defines some kind of NAND flash memory and
+a partition at a specific offset inside it to be used as the backend for the
+*state* variable set.
+
+.. code-block:: text
+
+ nandflash@0 {
+ backend_state_nand: partition@500000 {
+ [...]
+ };
+ };
+
+With this 'backend' definition its possible to define the *state* variable set
+content, its backend-type and *state* variable layout.
+
+.. code-block:: text
+
+ aliases {
+ state = &state_nand;
+ };
+
+ state_nand: nand_state_memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "barebox,state";
+ magic = <0xab67421f>;
+ backend-type = "raw";
+ backend = <&backend_state_nand>;
+ backend-storage-type = "circular";
+
+ variable {
+ reg = <0x0 0x1>;
+ type ="uint8";
+ default = <0x1>;
+ };
+ };
+
+SRAM
+####
+
+This type of memory can be written on a byte base and there is no need for an
+erase prior writing a new value.
+
+From the Linux kernel perspective this type of memory is a *Memory Technologie
+Device* (aka 'MTD') and handled by barebox in the same manner. It needs a
+partition definition.
+
+The following devicetree node entry defines some kind of SRAM memory and
+a partition at a specific offset inside it to be used as the backend for the
+*state* variable set.
+
+.. code-block:: text
+
+ sram@0 {
+ backend_state_sram: partition@10000 {
+ [...]
+ };
+ };
+
+With this 'backend' definition its possible to define the *state* variable set
+content, its backend-type and *state* variable layout.
+
+.. code-block:: text
+
+ aliases {
+ state = &state_sram;
+ };
+
+ state_sram: sram_state_memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "barebox,state";
+ magic = <0xab67421f>;
+ backend-type = "raw";
+ backend = <&backend_state_sram>;
+ backend-storage-type = "direct";
+ backend-stridesize = <32>;
+
+ variable {
+ reg = <0x0 0x1>;
+ type ="uint8";
+ default = <0x1>;
+ };
+ };
+
+EEPROM
+######
+
+This type of memory can be written on a byte base and must be erased prior
+writing, but in contrast to the other flash memories, an EEPROM does the erase
+of the address to be written to by its own, so its transparent to the
+application.
+
+While from the Linux kernel perspective this type of memory does not support
+partitions, barebox and the *state* userland tool will use partition definitions
+on an EEPROM memory as well, to exactly define the location in a generic manner
+within the EEPROM.
+
+.. code-block:: text
+
+ eeprom@50 {
+ partitions {
+ compatible = "fixed-partitions";
+ #size-cells = <1>;
+ #address-cells = <1>;
+ backend_state_eeprom: eeprom_state_memory@400 {
+ reg = <0x400 0x100>;
+ label = "state-eeprom";
+ };
+ };
+ };
+};
+
+With this 'backend' definition its possible to define the *state* variable set
+content, its backend-type and *state* variable layout.
+
+.. code-block:: text
+
+ aliases {
+ state = &state_eeprom;
+ };
+
+ state_eeprom: eeprom_memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "barebox,state";
+ magic = <0x344682db>;
+ backend-type = "raw";
+ backend = <&backend_state_eeprom>;
+ backend-storage-type = "direct";
+ backend-stridesize = <32>;
+
+ variable {
+ reg = <0x0 0x1>;
+ type ="uint8";
+ default = <0x1>;
+ };
+ };
+
+Frontend
--------
-The ``state`` command can be used to store and manipulate the state. Using
-``state`` without argument lists you all available states with their name.
-``devinfo STATE_NAME`` shows you all variables and their values. ``state -s``
-stores the state.
+As frontend a *state* instance is a regular barebox device which has
+device parameters for the *state* variables. With this the variables can
+be accessed like normal shell variables. The ``state`` command is used
+to save/restore a *state* variable set to the backend device.
-Starting Barebox will automatically load the last written state. If loading the
-state fails the defaults are used.
+After initializing the variable can be accessed with ``$state.foo``.
+``state -s`` stores the *state* to the backend device.
--
2.11.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread