diff --git a/Documentation/devicetree/bindings/barebox/barebox,state.rst b/Documentation/devicetree/bindings/barebox/barebox,state.rst index 40d7bc2..cebb5f8 100644 --- a/Documentation/devicetree/bindings/barebox/barebox,state.rst +++ b/Documentation/devicetree/bindings/barebox/barebox,state.rst @@ -1,55 +1,91 @@ .. _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. - -A state node contains a description of a set of variables along with a -place where the variables are stored. - -Required properties: - -* ``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 +* ``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. -Optional properties: +.. _barebox,state_magic: +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. + +.. 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. + +The ``backend`` property uses the *phandle* mechanism to link the *state* to +a real persistent memory. Refer :ref:`Backend ` for +supported persistent memories. + +The ``backend-type`` should be ``raw`` or ``dtb``. Refer +:ref:`Backend Types ` for further details. + +Optional Properties +################### + +* ``backend-stridesize``: stride counted in bytes. See note below. +* ``backend-storage-type``: Defines the backend storage type to ``direct``, + ``circular`` or ``noncircular``. If the backend memory needs to be erased + prior a write it defaults to the ``circular`` storage backend type, for backend + memories like RAMs or EEPROMs it defaults to the ``direct`` storage backend type. * ``algo``: A HMAC algorithm used to detect manipulation of the data or header, sensible values follow this pattern ``hmac()``, - 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. + e.g. ``hmac(sha256)``. Only available for the ``backend-type`` ``raw``. -Variable nodes --------------- +.. 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. -These are subnodes of a state node each describing a single +.. _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 `. + +.. 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 ``@
``, but the suffix is stripped from the variable name. @@ -57,105 +93,161 @@ ``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``. +* ``names``: For ``enum32`` values only, this specifies the possible values for + ``enum32``. -Optional properties: +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. +* ``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. -Example:: +.. 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. - /aliases { - state = &state; - }; +Variable Examples +################# - state: state { - magic = <0x27031977>; - compatible = "barebox,state"; - backend-type = "raw"; - backend = <&state_part>; +``uint8``: - foo { - reg = <0x00 0x4>; - type = "uint32"; - default = <0x0>; - }; +.. code-block:: text - bar { - reg = <0x10 0x4>; - type = "enum32"; - names = "baz", "qux"; - default = <1>; - }; - }; + uint8_example@0 { + reg = <0x0 0x1>; + type = "uint8"; + default = <0x00>; + }; - &nand_flash { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - state_part: state@10000 { - label = "state"; - reg = <0x10000 0x10000>; - }; - }; - }; +``uint32``: -Variable Types --------------- +.. code-block:: text -* ``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. + uint32_example@0 { + reg = <0x0 0x4>; + type = "uint32"; + default = <100>; + }; -Backends --------- +``enum32``: -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. +.. 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()";`` a HMAC algorithm -can be defined to detect unauthorized modification of the state's +With the optional property ``algo = "hmac()";`` 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. -Frontend --------- +.. _barebox,state_setup: -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. +Configuring the *state* variable set +------------------------------------ -After initializing the variable can be accessed with ``$state.foo``. -``state -s`` stores the state to eeprom. +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>; + }; + }; + +The operating system's devicetree defines instead: + +.. code-block:: text + + persistent_state_memory@01 { + compatible = "somevalue"; + reg = <1>; + }; diff --git a/Documentation/devicetree/index.rst b/Documentation/devicetree/index.rst index c5992c0..de57154 100644 --- a/Documentation/devicetree/index.rst +++ b/Documentation/devicetree/index.rst @@ -12,3 +12,4 @@ bindings/leds/* bindings/misc/* bindings/mtd/* + bindings/rtc/* diff --git a/Documentation/user/bootchooser.rst b/Documentation/user/bootchooser.rst index cef1d4a..3d473b5 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.`` 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..boot`` - This controls what barebox actually boots for this target. This string can contain - anything that the :ref:`boot ` command understands. + This controls what barebox actually boots for this boot target. This string can + contain anything that the :ref:`boot ` command understands. ``global.bootchooser..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..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..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..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 targets that have a priority -> 0 and a remaining_attempts counter > 0. +The *bootchooser* algorithm generally only starts boot 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. +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 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: +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: -- ``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. +- 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:`command_bootchooser` command. This allows for custom conditions under which -a system is marked as good. +Additionally the ``remaining_attempts`` counter can be reset manually using the +:ref:`bootchoser command `. This allows for custom conditions +under which a system is marked as good. In case only the booted system itself knows when it is in a good state, the -barebox-state tool from the dt-utils_ package can 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 ` ``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..`` variables must exist. ``global.bootchooser.last_chosen`` - bootchooser sets this to the target that was chosen on last boot (index). + *bootchooser* sets this to the boot target that was chosen on last boot (index). -Using the State Framework as Backend for Runtime Variable Data --------------------------------------------------------------- +.. _bootchooser,setup_example: -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``. +Setup Example +------------- -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. -The following example shows how to initialize two targets, 'system0' and 'system1'. -Both boot from an UBIFS on nand0, the former has a priority of 21 and boots from -the volume 'system0' whereas the latter has a priority of 20 and boots from -the volume 'system1'. +.. code-block:: text + + 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 ` for further details). + +Either device can be booted with the :ref:`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 ` 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 @@ 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 @@ 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 @@ 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 `. + +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 = ; + 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 = ; /* -> read note below */ + }; + + priority { + [...] + default = ; /* -> 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.`` 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 `. 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..default_priority`` + and ``bootchooser..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: +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: -- 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. +- 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 73c4be8..7d6c577 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. +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: -Devicetree ----------- +* compact binary format to make it suitable for small non-volatile memories +* atomic save/restore of the whole variable set +* redundancy -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. +*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. -Data Formats ------------- +Already known users of the *state* information are the :ref:`bootchooser` and +RAUC_. -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. +.. _RAUC: https://rauc.readthedocs.io/en/latest/ -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. +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. -Redundancy ----------- +.. _tool: https://git.pengutronix.de/cgit/tools/dt-utils/ -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. +To define a *state* variable set, a devicetree based description is used. Refer to +:ref:`barebox,state` for further details. -Storage Backends ----------------- +There are several software components involved, which are described in this +section. -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. +.. _state_framework,backends: -For all backends multiple copies are written to handle read errors. +Backends (e.g. Supported Memory Types) +-------------------------------------- -Commands +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 ` 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 ` 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 ` 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 ` 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 `). + +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 + + |||... + +--------------------------------+--------------------------------+-- + |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 ------->|<- + +** 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 ``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. +The following examples intends to show how to setup and interconnect all +required components for various non-volatile memories. -Starting Barebox will automatically load the last written state. If loading the -state fails the defaults are used. +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 +-------- + +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. + +After initializing the variable can be accessed with ``$state.foo``. +``state -s`` stores the *state* to the backend device. diff --git a/commands/state.c b/commands/state.c index c57a906..0dbca9f 100644 --- a/commands/state.c +++ b/commands/state.c @@ -72,12 +72,14 @@ BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("options:") BAREBOX_CMD_HELP_OPT ("-s", "save state") +BAREBOX_CMD_HELP_OPT ("-l", "load state") +BAREBOX_CMD_HELP_OPT ("-n", "no authentication") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(state) .cmd = do_state, - BAREBOX_CMD_DESC("save state information") - BAREBOX_CMD_OPTS("[-s] [STATENAME]") + BAREBOX_CMD_DESC("load or save state information") + BAREBOX_CMD_OPTS("[-sln] [STATENAME]") BAREBOX_CMD_GROUP(CMD_GRP_MISC) BAREBOX_CMD_HELP(cmd_state_help) BAREBOX_CMD_END diff --git a/common/Kconfig b/common/Kconfig index bc7cb0f..57418ca 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -937,6 +937,14 @@ See Documentation/devicetree/bindings/barebox/barebox,state.rst for more information. +config STATE_BACKWARD_COMPATIBLE + bool "backward compatible 'direct storage backend'" + depends on STATE + help + With this option enabled the 'direct' storage backend keeps backward + compatibility with older revisions of the state framework. Newer + revisions expect an additional 'meta header' and fail otherwise. + config BOOTCHOOSER bool "bootchooser infrastructure" depends on !SHELL_NONE diff --git a/common/bootchooser.c b/common/bootchooser.c index 455f290..83b15e0 100644 --- a/common/bootchooser.c +++ b/common/bootchooser.c @@ -367,18 +367,16 @@ char *state_devname; delim = strchr(state_prefix, '.'); - if (!delim) { - pr_err("state_prefix '%s' has invalid format\n", - state_prefix); - goto err; - } - state_devname = xstrndup(state_prefix, delim - state_prefix); + if (delim) + state_devname = xstrndup(state_prefix, delim - state_prefix); + else + state_devname = xstrdup(state_prefix); bc->state_prefix = xstrdup(state_prefix); bc->state = state_by_name(state_devname); if (!bc->state) { - free(state_devname); pr_err("Cannot get state '%s'\n", state_devname); + free(state_devname); ret = -ENODEV; goto err; } diff --git a/common/state/backend_bucket_direct.c b/common/state/backend_bucket_direct.c index 4465ed0..958696e 100644 --- a/common/state/backend_bucket_direct.c +++ b/common/state/backend_bucket_direct.c @@ -69,6 +69,11 @@ if (meta.magic == direct_magic) { read_len = meta.written_length; } else { + if (!IS_ENABLED(CONFIG_STATE_BACKWARD_COMPATIBLE)) { + dev_err(direct->dev, "No meta data header found\n"); + dev_dbg(direct->dev, "Enable backward compatibility or increase stride size\n"); + return -EINVAL; + } read_len = direct->max_size; ret = lseek(direct->fd, direct->offset, SEEK_SET); if (ret < 0) { @@ -103,21 +108,26 @@ int ret; struct state_backend_storage_bucket_direct_meta meta; - if (len > direct->max_size - sizeof(meta)) - return -E2BIG; - ret = lseek(direct->fd, direct->offset, SEEK_SET); if (ret < 0) { dev_err(direct->dev, "Failed to seek file, %d\n", ret); return ret; } - meta.magic = direct_magic; - meta.written_length = len; - ret = write_full(direct->fd, &meta, sizeof(meta)); - if (ret < 0) { - dev_err(direct->dev, "Failed to write metadata to file, %d\n", ret); - return ret; + /* write the meta data only if there is head room */ + if (len <= direct->max_size - sizeof(meta)) { + meta.magic = direct_magic; + meta.written_length = len; + ret = write_full(direct->fd, &meta, sizeof(meta)); + if (ret < 0) { + dev_err(direct->dev, "Failed to write metadata to file, %d\n", ret); + return ret; + } + } else { + if (!IS_ENABLED(CONFIG_STATE_BACKWARD_COMPATIBLE)) { + dev_dbg(direct->dev, "Too small stride size: must skip metadata! Increase stride size\n"); + return -EINVAL; + } } ret = write_full(direct->fd, buf, len); diff --git a/common/state/backend_storage.c b/common/state/backend_storage.c index d6e7adc..c6ebe86 100644 --- a/common/state/backend_storage.c +++ b/common/state/backend_storage.c @@ -146,6 +146,7 @@ struct state_backend_storage_bucket *bucket, *bucket_used = NULL; int ret; + dev_dbg(storage->dev, "Checking redundant buckets...\n"); /* * Iterate over all buckets. The first valid one we find is the * one we want to use. @@ -164,8 +165,12 @@ ret = format->verify(format, magic, bucket->buf, &bucket->len, flags); if (!ret && !bucket_used) bucket_used = bucket; + if (ret) + dev_info(storage->dev, "Ignoring broken bucket %d@0x%08lx...\n", bucket->num, bucket->offset); } + dev_dbg(storage->dev, "Checking redundant buckets finished.\n"); + if (!bucket_used) { dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n"); @@ -209,7 +214,7 @@ fd = open(path, O_RDONLY); if (fd < 0) { - pr_err("Failed to open '%s', %d\n", path, ret); + pr_err("Failed to open '%s', %d\n", path, fd); return fd; } @@ -352,11 +357,12 @@ * @param dev_offset Offset in the device to start writing at. * @param max_size Maximum size of the data. May be 0 for infinite. * @param stridesize Distance between two copies of the data. Not relevant for MTD - * @param storagetype Type of the storage backend. This may be NULL where we - * autoselect some backwardscompatible backend options + * @param storagetype Type of the storage backend. May be NULL for autoselection. * @return 0 on success, -errno otherwise * - * Depending on the filetype, we create mtd buckets or normal file buckets. + * If the backend memory needs to be erased prior a write, the @b storagetype + * defaults to 'circular' storage backend type, for backend memories like RAMs + * or EEPROMs @b storagetype defaults to the 'direct' storage backend type. */ int state_storage_init(struct state *state, const char *path, off_t offset, size_t max_size, uint32_t stridesize, @@ -368,6 +374,7 @@ INIT_LIST_HEAD(&storage->buckets); storage->dev = &state->dev; + storage->name = storagetype; storage->stridesize = stridesize; storage->offset = offset; storage->max_size = max_size; @@ -382,11 +389,10 @@ storage->name = "circular"; circular = true; } else if (!strcmp(storagetype, "noncircular")) { - storage->name = "noncircular"; dev_warn(storage->dev, "using old format circular storage type.\n"); circular = false; } else { - dev_warn(storage->dev, "unknown storage type '%s'\n", storagetype); + dev_dbg(storage->dev, "unknown storage type '%s'\n", storagetype); return -EINVAL; } return state_storage_mtd_buckets_init(storage, &meminfo, circular); diff --git a/common/state/state.c b/common/state/state.c index 121ba0c..266d211 100644 --- a/common/state/state.c +++ b/common/state/state.c @@ -240,6 +240,7 @@ vtype = state_find_type_by_name(type_name); if (!vtype) { + dev_dbg(&state->dev, "Error: invalid variable type '%s'\n", type_name); ret = -ENOENT; goto out_free; } @@ -608,11 +609,13 @@ ret = of_property_read_string(node, "backend-type", &backend_type); if (ret) { + dev_dbg(&state->dev, "Missing 'backend-type' property\n"); goto out_release_state; } ret = of_property_read_u32(node, "backend-stridesize", &stridesize); if (ret) { + dev_dbg(&state->dev, "'backend-stridesize' property undefined, trying to continue without\n"); stridesize = 0; }