6.2. Barebox state

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.

6.2.1. Devicetree state Node

A state node contains a description of a set of variables along with a place where the variable set gets stored.

6.2.1.1. Required Properties

  • 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.

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 Backend for supported persistent memories.

The backend-type should be raw or dtb. Refer Backend Types for further details.

6.2.1.2. 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(<HASH>), e.g. hmac(sha256). Only available for the backend-type raw.
  • keep-previous-content: Check if a the bucket meta magic field contains other data than the magic value. If so, the backend will not write the state to prevent unconditionally overwrites of existing data.

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.

The backend-stridesize is still optional but required whenever the underlying backend doesn’t provide an information how to pad an instance of a state variable set. This is valid for all underlying backends which support 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 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.

6.2.2. 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.

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.

6.2.2.1. 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 type. Variables are not allowed to overlap.
  • type: Should be uint8, uint32, enum32, mac or string for the type of the variable
  • names: For enum32 values only, this specifies the possible values for enum32.

6.2.2.2. 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.

6.2.2.3. Variable Examples

uint8:

uint8_example@0 {
    reg = <0x0 0x1>;
    type = "uint8";
    default = <0x00>;
};

uint32:

uint32_example@0 {
    reg = <0x0 0x4>;
    type = "uint32";
    default = <100>;
};

enum32:

enum_example@0 {
    reg = <0x0 0x4>;
    type = "enum32";
    names = "value#1", "value#2";
    default = <1>; /* selects "value#2" as the default */
};

mac:

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:

name {
    reg = <0x0 0x10>;
    type = "string";
};

In this example the length of the string is limited to 16 characters.

6.2.3. HMAC

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. Refer keystore - manage keys for further details.

6.2.4. 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:

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:

persistent_state_memory@01 {
    compatible = "somevalue";
    reg = <1>;
};