[PATCH 1/2] state: Refactor state framework

Markus Pargmann mpa at pengutronix.de
Wed Jun 22 22:57:21 PDT 2016


Hi,

On Friday 17 June 2016 11:14:34 Markus Pargmann wrote:
> The state framework grew organically over the time. Unfortunately the
> architecture and abstractions disappeared during this period.
> 
> This patch refactors the framework to recreate the abstractions. The
> main focus was the backend with its storage. The main use-case was to
> offer better NAND support with less erase cycles and interchangeable
> data formats (dtb,raw).
> 
> The general architecture now has a backend which consists of a data
> format and storage. The storage consists of multiple storage buckets
> each holding exactly one copy of the state data. A data format describes
> a data serialization for the state framework. This can be either dtb or
> raw. A storage bucket is a storage location which is used to store any
> data. There is a (new) circular type which writes changes behind the
> last written data and therefore reduces the number of erases. The other
> type is a direct bucket which writes directly to a storage offset for
> all non-erase storage.
> 
> Furthermore this patch splits up all classes into different files in a
> subdirectory.
> 
> This is currently all in one patch as I can't see a good way to split
> the changes up without having a non-working state framework in between.

We discovered some small bugs in this. I will send a new version.

Best Regards,

Markus

> 
> The following diagram shows the new architecture roughly:
> 
>            .----------.
>            |  state   |
>            '----------'
>                  |
>                  |
>                  v
>   .----------------------------.
>   |       state_backend        |
>   |----------------------------|
>   | + state_load(*state);      |
>   | + state_save(*state);      |
>   | + state_backend_init(...); |
>   |                            |
>   |                            |
>   '----------------------------'
>     |            |                   The format describes
>     |            |                   how the state data
>     |            '------------->     is serialized
>     |   .--------------------------------------------.
>     |   |      state_backend_format <INTERFACE>      |
>     |   |--------------------------------------------|
>     |   | + verify(*format, magic, *buf, len);       |
>     |   | + pack(*format, *state, **buf, len);       |
>     |   | + unpack(*format, *state, *buf, len);      |
>     |   | + get_packed_len(*format, *state);         |
>     |   | + free(*format);                           |
>     |   '--------------------------------------------'
>     |              ^                      ^
>     |              *                      *
>     |              *                      *
>     |   .--------------------. .--------------------.
>     |   | backend_format_dtb | | backend_format_raw |
>     |   '--------------------' '--------------------'
>     |
>     |
>     |
>     v
> .----------------------------------------------------------.
> |                  state_backend_storage                   |
> |----------------------------------------------------------|
> | + init(...);                                             |
> | + free(*storage);                                        |
> | + read(*storage, *format, magic, **buf, *len, len_hint); |
> | + write(*storage, *buf, len);                            |
> | + restore_consistency(*storage, *buf, len);              |
> '----------------------------------------------------------'
>                               |
>      The backend storage is responsible to manage multiple
>      data copies and distribute them onto several buckets.
>      Read data is verified against the given format to
>      ensure that the read data is correct.
>                               |
>                               |
>                               |
>                               |
>                               |
>                               v
>         .------------------------------------------.
>         | state_backend_storage_bucket <INTERFACE> |
>         |------------------------------------------|
>         | + init(*bucket);                         |
>         | + write(*bucket, *buf, len);             |
>         | + read(*bucket, **buf, len_hint);        |
>         | + free(*bucket);                         |
>         '------------------------------------------'
>                       ^         ^
>                      *           *
>                     *             *
>  A storage bucket represents exactly one data copy at one
>  data location. A circular bucket writes any new data to
>  the end of the bucket (for reduced erases on NAND). A
>  direct bucket directly writes at one location.
>                *                       *
>               *                         *
>              *                           *
>  .-----------------------.  .-------------------------.
>  | backend_bucket_direct |  | backend_bucket_circular |
>  '-----------------------'  '-------------------------'
> 
> Signed-off-by: Markus Pargmann <mpa at pengutronix.de>
> ---
>  common/Makefile                        |    2 +-
>  common/state.c                         | 1720 --------------------------------
>  common/state/Makefile                  |    8 +
>  common/state/backend.c                 |  209 ++++
>  common/state/backend_bucket_circular.c |  580 +++++++++++
>  common/state/backend_bucket_direct.c   |  180 ++++
>  common/state/backend_format_dtb.c      |  150 +++
>  common/state/backend_format_raw.c      |  327 ++++++
>  common/state/backend_storage.c         |  470 +++++++++
>  common/state/state.c                   |  564 +++++++++++
>  common/state/state.h                   |  266 +++++
>  common/state/state_variables.c         |  493 +++++++++
>  drivers/misc/state.c                   |   64 +-
>  include/state.h                        |    4 +-
>  14 files changed, 3252 insertions(+), 1785 deletions(-)
>  delete mode 100644 common/state.c
>  create mode 100644 common/state/Makefile
>  create mode 100644 common/state/backend.c
>  create mode 100644 common/state/backend_bucket_circular.c
>  create mode 100644 common/state/backend_bucket_direct.c
>  create mode 100644 common/state/backend_format_dtb.c
>  create mode 100644 common/state/backend_format_raw.c
>  create mode 100644 common/state/backend_storage.c
>  create mode 100644 common/state/state.c
>  create mode 100644 common/state/state.h
>  create mode 100644 common/state/state_variables.c
> 
> diff --git a/common/Makefile b/common/Makefile
> index 99681e21215b..17fcb5f24a04 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -44,7 +44,7 @@ obj-$(CONFIG_POLLER)		+= poller.o
>  obj-$(CONFIG_RESET_SOURCE)	+= reset_source.o
>  obj-$(CONFIG_SHELL_HUSH)	+= hush.o
>  obj-$(CONFIG_SHELL_SIMPLE)	+= parser.o
> -obj-$(CONFIG_STATE)		+= state.o
> +obj-$(CONFIG_STATE)		+= state/
>  obj-$(CONFIG_RATP)		+= ratp.o
>  obj-$(CONFIG_UIMAGE)		+= image.o uimage.o
>  obj-$(CONFIG_FITIMAGE)		+= image-fit.o
> diff --git a/common/state.c b/common/state.c
> deleted file mode 100644
> index 87afff305661..000000000000
> --- a/common/state.c
> +++ /dev/null
> @@ -1,1720 +0,0 @@
> -/*
> - * Copyright (C) 2012-2014 Pengutronix, Jan Luebbe <j.luebbe at pengutronix.de>
> - * Copyright (C) 2013-2014 Pengutronix, Sascha Hauer <s.hauer at pengutronix.de>
> - * Copyright (C) 2015 Pengutronix, Marc Kleine-Budde <mkl at pengutronix.de>
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU General Public License for more details.
> - */
> -
> -#include <common.h>
> -#include <digest.h>
> -#include <environment.h>
> -#include <errno.h>
> -#include <fcntl.h>
> -#include <fs.h>
> -#include <crc.h>
> -#include <init.h>
> -#include <ioctl.h>
> -#include <libbb.h>
> -#include <libfile.h>
> -#include <malloc.h>
> -#include <net.h>
> -#include <state.h>
> -#include <xfuncs.h>
> -
> -#include <crypto/keystore.h>
> -
> -#include <linux/mtd/mtd-abi.h>
> -#include <linux/mtd/mtd.h>
> -#include <linux/list.h>
> -#include <linux/err.h>
> -
> -#include <asm/unaligned.h>
> -
> -#define RAW_BACKEND_COPIES 2
> -
> -struct state_backend;
> -
> -struct state {
> -	struct device_d dev;
> -	struct device_node *root;
> -	struct list_head variables;
> -	const char *name;
> -	struct list_head list;
> -	struct state_backend *backend;
> -	uint32_t magic;
> -	unsigned int dirty;
> -};
> -
> -struct state_backend {
> -	int (*save)(struct state_backend *backend, struct state *state);
> -	const char *name;
> -	const char *of_path;
> -	const char *path;
> -	struct digest *digest;
> -};
> -
> -enum state_variable_type {
> -	STATE_TYPE_INVALID = 0,
> -	STATE_TYPE_ENUM,
> -	STATE_TYPE_U8,
> -	STATE_TYPE_U32,
> -	STATE_TYPE_S32,
> -	STATE_TYPE_MAC,
> -	STATE_TYPE_STRING,
> -};
> -
> -/* instance of a single variable */
> -struct state_variable {
> -	enum state_variable_type type;
> -	struct list_head list;
> -	const char *name;
> -	unsigned int start;
> -	unsigned int size;
> -	void *raw;
> -};
> -
> -enum state_convert {
> -	STATE_CONVERT_FROM_NODE,
> -	STATE_CONVERT_FROM_NODE_CREATE,
> -	STATE_CONVERT_TO_NODE,
> -	STATE_CONVERT_FIXUP,
> -};
> -
> -/* A variable type (uint32, enum32) */
> -struct variable_type {
> -	enum state_variable_type type;
> -	const char *type_name;
> -	struct list_head list;
> -	int (*export)(struct state_variable *, struct device_node *,
> -			enum state_convert);
> -	int (*import)(struct state_variable *, struct device_node *);
> -	struct state_variable *(*create)(struct state *state,
> -			const char *name, struct device_node *);
> -};
> -
> -/* list of all registered state instances */
> -static LIST_HEAD(state_list);
> -
> -static int state_set_dirty(struct param_d *p, void *priv)
> -{
> -	struct state *state = priv;
> -
> -	state->dirty = 1;
> -
> -	return 0;
> -}
> -
> -/*
> - *  uint32
> - */
> -struct state_uint32 {
> -	struct state_variable var;
> -	struct param_d *param;
> -	struct state *state;
> -	uint32_t value;
> -	uint32_t value_default;
> -};
> -
> -static int state_var_compare(struct list_head *a, struct list_head *b)
> -{
> -	struct state_variable *va = list_entry(a, struct state_variable, list);
> -	struct state_variable *vb = list_entry(b, struct state_variable, list);
> -
> -	return va->start < vb->start ? -1 : 1;
> -}
> -
> -static void state_add_var(struct state *state, struct state_variable *var)
> -{
> -	list_add_sort(&var->list, &state->variables, state_var_compare);
> -}
> -
> -static inline struct state_uint32 *to_state_uint32(struct state_variable *s)
> -{
> -	return container_of(s, struct state_uint32, var);
> -}
> -
> -static int state_uint32_export(struct state_variable *var,
> -		struct device_node *node, enum state_convert conv)
> -{
> -	struct state_uint32 *su32 = to_state_uint32(var);
> -	int ret;
> -
> -	if (su32->value_default) {
> -		ret = of_property_write_u32(node, "default",
> -					    su32->value_default);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	if (conv == STATE_CONVERT_FIXUP)
> -		return 0;
> -
> -	return of_property_write_u32(node, "value", su32->value);
> -}
> -
> -static int state_uint32_import(struct state_variable *sv,
> -			       struct device_node *node)
> -{
> -	struct state_uint32 *su32 = to_state_uint32(sv);
> -
> -	of_property_read_u32(node, "default", &su32->value_default);
> -	if (of_property_read_u32(node, "value", &su32->value))
> -		su32->value = su32->value_default;
> -
> -	return 0;
> -}
> -
> -static int state_uint8_set(struct param_d *p, void *priv)
> -{
> -	struct state_uint32 *su32 = priv;
> -	struct state *state = su32->state;
> -
> -	if (su32->value > 255)
> -		return -ERANGE;
> -
> -	return state_set_dirty(p, state);
> -}
> -
> -static struct state_variable *state_uint8_create(struct state *state,
> -		const char *name, struct device_node *node)
> -{
> -	struct state_uint32 *su32;
> -	struct param_d *param;
> -
> -	su32 = xzalloc(sizeof(*su32));
> -
> -	param = dev_add_param_int(&state->dev, name, state_uint8_set,
> -				  NULL, &su32->value, "%u", su32);
> -	if (IS_ERR(param)) {
> -		free(su32);
> -		return ERR_CAST(param);
> -	}
> -
> -	su32->param = param;
> -	su32->var.size = sizeof(uint8_t);
> -#ifdef __LITTLE_ENDIAN
> -	su32->var.raw = &su32->value;
> -#else
> -	su32->var.raw = &su32->value + 3;
> -#endif
> -	su32->state = state;
> -
> -	return &su32->var;
> -}
> -
> -static struct state_variable *state_int32_create(struct state *state,
> -		const char *name, struct device_node *node, const char *format)
> -{
> -	struct state_uint32 *su32;
> -	struct param_d *param;
> -
> -	su32 = xzalloc(sizeof(*su32));
> -
> -	param = dev_add_param_int(&state->dev, name, state_set_dirty,
> -				  NULL, &su32->value, format, state);
> -	if (IS_ERR(param)) {
> -		free(su32);
> -		return ERR_CAST(param);
> -	}
> -
> -	su32->param = param;
> -	su32->var.size = sizeof(uint32_t);
> -	su32->var.raw = &su32->value;
> -
> -	return &su32->var;
> -}
> -
> -static struct state_variable *state_uint32_create(struct state *state,
> -		const char *name, struct device_node *node)
> -{
> -	return state_int32_create(state, name, node, "%u");
> -}
> -
> -static struct state_variable *state_sint32_create(struct state *state,
> -		const char *name, struct device_node *node)
> -{
> -	return state_int32_create(state, name, node, "%d");
> -}
> -
> -/*
> - *  enum32
> - */
> -struct state_enum32 {
> -	struct state_variable var;
> -	struct param_d *param;
> -	uint32_t value;
> -	uint32_t value_default;
> -	const char **names;
> -	int num_names;
> -};
> -
> -static inline struct state_enum32 *to_state_enum32(struct state_variable *s)
> -{
> -	return container_of(s, struct state_enum32, var);
> -}
> -
> -static int state_enum32_export(struct state_variable *var,
> -		struct device_node *node, enum state_convert conv)
> -{
> -	struct state_enum32 *enum32 = to_state_enum32(var);
> -	int ret, i, len;
> -	char *prop, *str;
> -
> -	if (enum32->value_default) {
> -		ret = of_property_write_u32(node, "default",
> -					    enum32->value_default);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	len = 0;
> -
> -	for (i = 0; i < enum32->num_names; i++)
> -		len += strlen(enum32->names[i]) + 1;
> -
> -	prop = xzalloc(len);
> -	str = prop;
> -
> -	for (i = 0; i < enum32->num_names; i++)
> -		str += sprintf(str, "%s", enum32->names[i]) + 1;
> -
> -	ret = of_set_property(node, "names", prop, len, 1);
> -
> -	free(prop);
> -
> -	if (conv == STATE_CONVERT_FIXUP)
> -		return 0;
> -
> -	ret = of_property_write_u32(node, "value", enum32->value);
> -	if (ret)
> -		return ret;
> -
> -	return ret;
> -}
> -
> -static int state_enum32_import(struct state_variable *sv,
> -			       struct device_node *node)
> -{
> -	struct state_enum32 *enum32 = to_state_enum32(sv);
> -	int len;
> -	const __be32 *value, *value_default;
> -
> -	value = of_get_property(node, "value", &len);
> -	if (value && len != sizeof(uint32_t))
> -		return -EINVAL;
> -
> -	value_default = of_get_property(node, "default", &len);
> -	if (value_default && len != sizeof(uint32_t))
> -		return -EINVAL;
> -
> -	if (value_default)
> -		enum32->value_default = be32_to_cpu(*value_default);
> -	if (value)
> -		enum32->value = be32_to_cpu(*value);
> -	else
> -		enum32->value = enum32->value_default;
> -
> -	return 0;
> -}
> -
> -static struct state_variable *state_enum32_create(struct state *state,
> -		const char *name, struct device_node *node)
> -{
> -	struct state_enum32 *enum32;
> -	int ret, i, num_names;
> -
> -	enum32 = xzalloc(sizeof(*enum32));
> -
> -	num_names = of_property_count_strings(node, "names");
> -	if (num_names < 0) {
> -		dev_err(&state->dev, "enum32 node without \"names\" property\n");
> -		return ERR_PTR(-EINVAL);
> -	}
> -
> -	enum32->names = xzalloc(sizeof(char *) * num_names);
> -	enum32->num_names = num_names;
> -	enum32->var.size = sizeof(uint32_t);
> -	enum32->var.raw = &enum32->value;
> -
> -	for (i = 0; i < num_names; i++) {
> -		const char *name;
> -
> -		ret = of_property_read_string_index(node, "names", i, &name);
> -		if (ret)
> -			goto out;
> -		enum32->names[i] = xstrdup(name);
> -	}
> -
> -	enum32->param = dev_add_param_enum(&state->dev, name, state_set_dirty,
> -			NULL, &enum32->value, enum32->names, num_names, state);
> -	if (IS_ERR(enum32->param)) {
> -		ret = PTR_ERR(enum32->param);
> -		goto out;
> -	}
> -
> -	return &enum32->var;
> -out:
> -	for (i--; i >= 0; i--)
> -		free((char *)enum32->names[i]);
> -	free(enum32->names);
> -	free(enum32);
> -	return ERR_PTR(ret);
> -}
> -
> -/*
> - *  MAC address
> - */
> -struct state_mac {
> -	struct state_variable var;
> -	struct param_d *param;
> -	uint8_t value[6];
> -	uint8_t value_default[6];
> -};
> -
> -static inline struct state_mac *to_state_mac(struct state_variable *s)
> -{
> -	return container_of(s, struct state_mac, var);
> -}
> -
> -static int state_mac_export(struct state_variable *var,
> -		struct device_node *node, enum state_convert conv)
> -{
> -	struct state_mac *mac = to_state_mac(var);
> -	int ret;
> -
> -	if (!is_zero_ether_addr(mac->value_default)) {
> -		ret = of_property_write_u8_array(node, "default", mac->value_default,
> -						 ARRAY_SIZE(mac->value_default));
> -		if (ret)
> -			return ret;
> -	}
> -
> -	if (conv == STATE_CONVERT_FIXUP)
> -		return 0;
> -
> -	return of_property_write_u8_array(node, "value", mac->value,
> -					  ARRAY_SIZE(mac->value));
> -}
> -
> -static int state_mac_import(struct state_variable *sv,
> -			    struct device_node *node)
> -{
> -	struct state_mac *mac = to_state_mac(sv);
> -
> -	of_property_read_u8_array(node, "default", mac->value_default,
> -				  ARRAY_SIZE(mac->value_default));
> -	if (of_property_read_u8_array(node, "value", mac->value,
> -				      ARRAY_SIZE(mac->value)))
> -		memcpy(mac->value, mac->value_default, ARRAY_SIZE(mac->value));
> -
> -	return 0;
> -}
> -
> -static struct state_variable *state_mac_create(struct state *state,
> -		const char *name, struct device_node *node)
> -{
> -	struct state_mac *mac;
> -	int ret;
> -
> -	mac = xzalloc(sizeof(*mac));
> -
> -	mac->var.size = ARRAY_SIZE(mac->value);
> -	mac->var.raw = mac->value;
> -
> -	mac->param = dev_add_param_mac(&state->dev, name, state_set_dirty,
> -			NULL, mac->value, state);
> -	if (IS_ERR(mac->param)) {
> -		ret = PTR_ERR(mac->param);
> -		goto out;
> -	}
> -
> -	return &mac->var;
> -out:
> -	free(mac);
> -	return ERR_PTR(ret);
> -}
> -
> -/*
> - *  string
> - */
> -struct state_string {
> -	struct state_variable var;
> -	struct param_d *param;
> -	struct state *state;
> -	char *value;
> -	const char *value_default;
> -	char raw[];
> -};
> -
> -static inline struct state_string *to_state_string(struct state_variable *s)
> -{
> -	return container_of(s, struct state_string, var);
> -}
> -
> -static int state_string_export(struct state_variable *var,
> -		struct device_node *node, enum state_convert conv)
> -{
> -	struct state_string *string = to_state_string(var);
> -	int ret = 0;
> -
> -	if (string->value_default) {
> -		ret = of_set_property(node, "default", string->value_default,
> -				      strlen(string->value_default) + 1, 1);
> -
> -		if (ret)
> -			return ret;
> -	}
> -
> -	if (conv == STATE_CONVERT_FIXUP)
> -		return 0;
> -
> -	if (string->value)
> -		ret = of_set_property(node, "value", string->value,
> -				      strlen(string->value) + 1, 1);
> -
> -	return ret;
> -}
> -
> -static int state_string_copy_to_raw(struct state_string *string,
> -				    const char *src)
> -{
> -	size_t len;
> -
> -	len = strlen(src);
> -	if (len > string->var.size)
> -		return -EILSEQ;
> -
> -	/* copy string and clear remaining contents of buffer */
> -	memcpy(string->raw, src, len);
> -	memset(string->raw + len, 0x0, string->var.size - len);
> -
> -	return 0;
> -}
> -
> -static int state_string_import(struct state_variable *sv,
> -			       struct device_node *node)
> -{
> -	struct state_string *string = to_state_string(sv);
> -	const char *value = NULL;
> -	size_t len;
> -	int ret;
> -
> -	of_property_read_string(node, "default", &string->value_default);
> -	if (string->value_default) {
> -		len = strlen(string->value_default);
> -		if (len > string->var.size)
> -			return -EILSEQ;
> -	}
> -
> -	ret = of_property_read_string(node, "value", &value);
> -	if (ret)
> -		value = string->value_default;
> -
> -	if (value)
> -		return state_string_copy_to_raw(string, value);
> -
> -	return 0;
> -}
> -
> -static int state_string_set(struct param_d *p, void *priv)
> -{
> -	struct state_string *string = priv;
> -	struct state *state = string->state;
> -	int ret;
> -
> -	ret = state_string_copy_to_raw(string, string->value);
> -	if (ret)
> -		return ret;
> -
> -	return state_set_dirty(p, state);
> -}
> -
> -static int state_string_get(struct param_d *p, void *priv)
> -{
> -	struct state_string *string = priv;
> -
> -	free(string->value);
> -	if (string->raw[0])
> -		string->value = xstrndup(string->raw, string->var.size);
> -	else
> -		string->value = xstrdup("");
> -
> -	return 0;
> -}
> -
> -static struct state_variable *state_string_create(struct state *state,
> -		const char *name, struct device_node *node)
> -{
> -	struct state_string *string;
> -	u32 start_size[2];
> -	int ret;
> -
> -	ret = of_property_read_u32_array(node, "reg", start_size,
> -					 ARRAY_SIZE(start_size));
> -	if (ret) {
> -		dev_err(&state->dev,
> -			"%s: reg property not found\n", name);
> -		return ERR_PTR(ret);
> -	}
> -
> -	/* limit to arbitrary len of 4k */
> -	if (start_size[1] > 4096)
> -		return ERR_PTR(-EILSEQ);
> -
> -	string = xzalloc(sizeof(*string) + start_size[1]);
> -	string->var.size = start_size[1];
> -	string->var.raw = &string->raw;
> -	string->state = state;
> -
> -	string->param = dev_add_param_string(&state->dev, name, state_string_set,
> -					     state_string_get, &string->value, string);
> -	if (IS_ERR(string->param)) {
> -		ret = PTR_ERR(string->param);
> -		goto out;
> -	}
> -
> -	return &string->var;
> -out:
> -	free(string);
> -	return ERR_PTR(ret);
> -}
> -
> -static struct variable_type types[] =  {
> -	{
> -		.type = STATE_TYPE_U8,
> -		.type_name = "uint8",
> -		.export = state_uint32_export,
> -		.import = state_uint32_import,
> -		.create = state_uint8_create,
> -	}, {
> -		.type = STATE_TYPE_U32,
> -		.type_name = "uint32",
> -		.export = state_uint32_export,
> -		.import = state_uint32_import,
> -		.create = state_uint32_create,
> -	}, {
> -		.type = STATE_TYPE_ENUM,
> -		.type_name = "enum32",
> -		.export = state_enum32_export,
> -		.import = state_enum32_import,
> -		.create = state_enum32_create,
> -	}, {
> -		.type = STATE_TYPE_MAC,
> -		.type_name = "mac",
> -		.export = state_mac_export,
> -		.import = state_mac_import,
> -		.create = state_mac_create,
> -	}, {
> -		.type = STATE_TYPE_STRING,
> -		.type_name = "string",
> -		.export = state_string_export,
> -		.import = state_string_import,
> -		.create = state_string_create,
> -	}, {
> -		.type = STATE_TYPE_S32,
> -		.type_name = "int32",
> -		.export = state_uint32_export,
> -		.import = state_uint32_import,
> -		.create = state_sint32_create,
> -	},
> -};
> -
> -static struct variable_type *state_find_type_by_name(const char *name)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(types); i++) {
> -		if (!strcmp(name, types[i].type_name)) {
> -			return &types[i];
> -		}
> -	}
> -
> -	return NULL;
> -}
> -
> -/*
> - * Generic state functions
> - */
> -
> -static struct state *state_new(const char *name)
> -{
> -	struct state *state;
> -	int ret;
> -
> -	state = xzalloc(sizeof(*state));
> -	safe_strncpy(state->dev.name, name, MAX_DRIVER_NAME);
> -	state->name = state->dev.name;
> -	state->dev.id = DEVICE_ID_SINGLE;
> -	INIT_LIST_HEAD(&state->variables);
> -
> -	ret = register_device(&state->dev);
> -	if (ret) {
> -		free(state);
> -		return ERR_PTR(ret);
> -	}
> -
> -	state->dirty = 1;
> -	dev_add_param_bool(&state->dev, "dirty", NULL, NULL, &state->dirty,
> -			   NULL);
> -
> -	list_add_tail(&state->list, &state_list);
> -
> -	return state;
> -}
> -
> -static struct state_variable *state_find_var(struct state *state,
> -					     const char *name)
> -{
> -	struct state_variable *sv;
> -
> -	list_for_each_entry(sv, &state->variables, list) {
> -		if (!strcmp(sv->name, name))
> -			return sv;
> -	}
> -
> -	return ERR_PTR(-ENOENT);
> -}
> -
> -static int state_convert_node_variable(struct state *state,
> -		struct device_node *node, struct device_node *parent,
> -		const char *parent_name, enum state_convert conv)
> -{
> -	const struct variable_type *vtype;
> -	struct device_node *child;
> -	struct device_node *new_node = NULL;
> -	struct state_variable *sv;
> -	const char *type_name;
> -	char *short_name, *name, *indexs;
> -	unsigned int start_size[2];
> -	int ret;
> -
> -	/* strip trailing @<ADDRESS> */
> -	short_name = xstrdup(node->name);
> -	indexs = strchr(short_name, '@');
> -	if (indexs)
> -		*indexs = 0;
> -
> -	/* construct full name */
> -	name = basprintf("%s%s%s", parent_name, parent_name[0] ? "." : "",
> -			   short_name);
> -	free(short_name);
> -
> -	if ((conv == STATE_CONVERT_TO_NODE) ||
> -	    (conv == STATE_CONVERT_FIXUP))
> -		new_node = of_new_node(parent, node->name);
> -
> -	for_each_child_of_node(node, child) {
> -		ret = state_convert_node_variable(state, child, new_node, name,
> -						  conv);
> -		if (ret)
> -			goto out_free;
> -	}
> -
> -	/* parents are allowed to have no type */
> -	ret = of_property_read_string(node, "type", &type_name);
> -	if (!list_empty(&node->children) && ret == -EINVAL) {
> -		if (conv == STATE_CONVERT_FIXUP) {
> -			ret = of_property_write_u32(new_node, "#address-cells", 1);
> -			if (ret)
> -				goto out_free;
> -
> -			ret = of_property_write_u32(new_node, "#size-cells", 1);
> -			if (ret)
> -				goto out_free;
> -		}
> -		ret = 0;
> -		goto out_free;
> -	} else if (ret) {
> -		goto out_free;
> -	}
> -
> -	vtype = state_find_type_by_name(type_name);
> -	if (!vtype) {
> -		dev_err(&state->dev, "unkown type: %s in %s\n", type_name,
> -			node->full_name);
> -		ret = -ENOENT;
> -		goto out_free;
> -	}
> -
> -	if (conv == STATE_CONVERT_FROM_NODE_CREATE) {
> -		sv = vtype->create(state, name, node);
> -		if (IS_ERR(sv)) {
> -			ret = PTR_ERR(sv);
> -			dev_err(&state->dev, "failed to create %s: %s\n",
> -				name, strerror(-ret));
> -			goto out_free;
> -		}
> -
> -		ret = of_property_read_u32_array(node, "reg", start_size,
> -						 ARRAY_SIZE(start_size));
> -		if (ret) {
> -			dev_err(&state->dev,
> -				"%s: reg property not found\n", name);
> -			goto out_free;
> -		}
> -
> -		if (start_size[1] != sv->size) {
> -			dev_err(&state->dev,
> -				"%s: size mismatch: type=%s(size=%u) size=%u\n",
> -				name, type_name, sv->size, start_size[1]);
> -			ret = -EOVERFLOW;
> -			goto out_free;
> -		}
> -
> -		sv->name = name;
> -		sv->start = start_size[0];
> -		sv->type = vtype->type;
> -		state_add_var(state, sv);
> -	} else {
> -		sv = state_find_var(state, name);
> -		if (IS_ERR(sv)) {
> -			/* we ignore this error */
> -			dev_dbg(&state->dev,
> -				"no such variable: %s: %s\n",
> -				name, strerror(-ret));
> -			ret = 0;
> -			goto out_free;
> -		}
> -		free(name);
> -
> -		if ((conv == STATE_CONVERT_TO_NODE) ||
> -		    (conv == STATE_CONVERT_FIXUP)) {
> -			ret = of_set_property(new_node, "type",
> -					      vtype->type_name,
> -					      strlen(vtype->type_name) + 1, 1);
> -			if (ret)
> -				goto out;
> -
> -			start_size[0] = sv->start;
> -			start_size[1] = sv->size;
> -			ret = of_property_write_u32_array(new_node, "reg",
> -							  start_size,
> -							  ARRAY_SIZE(start_size));
> -			if (ret)
> -				goto out;
> -		}
> -	}
> -
> -	if ((conv == STATE_CONVERT_TO_NODE) ||
> -	    (conv == STATE_CONVERT_FIXUP))
> -		ret = vtype->export(sv, new_node, conv);
> -	else
> -		ret = vtype->import(sv, node);
> -
> -	if (ret)
> -		goto out;
> -
> -	return 0;
> -out_free:
> -	free(name);
> -out:
> -	return ret;
> -}
> -
> -static struct device_node *state_to_node(struct state *state, struct device_node *parent,
> -					 enum state_convert conv)
> -{
> -	struct device_node *child;
> -	struct device_node *root;
> -	int ret;
> -
> -	root = of_new_node(parent, state->root->name);
> -	ret = of_property_write_u32(root, "magic", state->magic);
> -	if (ret)
> -		goto out;
> -
> -	for_each_child_of_node(state->root, child) {
> -		ret = state_convert_node_variable(state, child, root, "",
> -						  conv);
> -		if (ret)
> -			goto out;
> -	}
> -
> -	return root;
> -out:
> -	of_delete_node(root);
> -	return ERR_PTR(ret);
> -}
> -
> -static int state_from_node(struct state *state, struct device_node *node,
> -			   bool create)
> -{
> -	struct device_node *child;
> -	enum state_convert conv;
> -	int ret;
> -	uint32_t magic;
> -
> -	ret = of_property_read_u32(node, "magic", &magic);
> -	if (ret)
> -		return ret;
> -
> -	if (create) {
> -		conv = STATE_CONVERT_FROM_NODE_CREATE;
> -		state->root = node;
> -		state->magic = magic;
> -	} else {
> -		conv = STATE_CONVERT_FROM_NODE;
> -		if (state->magic && state->magic != magic) {
> -			dev_err(&state->dev,
> -					"invalid magic 0x%08x, should be 0x%08x\n",
> -					magic, state->magic);
> -			return -EINVAL;
> -		}
> -	}
> -
> -	for_each_child_of_node(node, child) {
> -		ret = state_convert_node_variable(state, child, NULL, "", conv);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	/* check for overlapping variables */
> -	if (create) {
> -		const struct state_variable *sv;
> -
> -		/* start with second entry */
> -		sv = list_first_entry(&state->variables,
> -				      struct state_variable, list);
> -
> -		list_for_each_entry_continue(sv, &state->variables, list) {
> -			const struct state_variable *last_sv;
> -
> -			last_sv = list_last_entry(&sv->list,
> -						  struct state_variable, list);
> -			if ((last_sv->start + last_sv->size - 1) < sv->start)
> -				continue;
> -
> -			dev_err(&state->dev,
> -				"ERROR: Conflicting variable position between: "
> -				"%s (0x%02x..0x%02x) and %s (0x%02x..0x%02x)\n",
> -				last_sv->name, last_sv->start,
> -				last_sv->start + last_sv->size - 1,
> -				sv->name, sv->start, sv->start + sv->size - 1);
> -
> -			ret |= -EINVAL;
> -		}
> -	}
> -
> -	return ret;
> -}
> -
> -static int of_state_fixup(struct device_node *root, void *ctx)
> -{
> -	struct state *state = ctx;
> -	const char *compatible = "barebox,state";
> -	struct device_node *new_node, *node, *parent, *backend_node;
> -	struct property *p;
> -	int ret;
> -	phandle phandle;
> -
> -	node = of_find_node_by_path_from(root, state->root->full_name);
> -	if (node) {
> -		/* replace existing node - it will be deleted later */
> -		parent = node->parent;
> -	} else {
> -		char *of_path, *c;
> -
> -		/* look for parent, remove last '/' from path */
> -		of_path = xstrdup(state->root->full_name);
> -		c = strrchr(of_path, '/');
> -		if (!c)
> -			return -ENODEV;
> -		*c = '0';
> -		parent = of_find_node_by_path(of_path);
> -		if (!parent)
> -			parent = root;
> -
> -		free(of_path);
> -	}
> -
> -	/* serialize variable definitions */
> -	new_node = state_to_node(state, parent, STATE_CONVERT_FIXUP);
> -	if (IS_ERR(new_node))
> -		return PTR_ERR(new_node);
> -
> -	/* compatible */
> -	p = of_new_property(new_node, "compatible", compatible,
> -			    strlen(compatible) + 1);
> -	if (!p) {
> -		ret = -ENOMEM;
> -		goto out;
> -	}
> -
> -	/* backend-type */
> -	if (!state->backend) {
> -		ret = -ENODEV;
> -		goto out;
> -	}
> -
> -	p = of_new_property(new_node, "backend-type", state->backend->name,
> -			    strlen(state->backend->name) + 1);
> -	if (!p) {
> -		ret = -ENOMEM;
> -		goto out;
> -	}
> -
> -	/* backend phandle */
> -	backend_node = of_find_node_by_path_from(root, state->backend->of_path);
> -	if (!backend_node) {
> -		ret = -ENODEV;
> -		goto out;
> -	}
> -
> -	phandle = of_node_create_phandle(backend_node);
> -	ret = of_property_write_u32(new_node, "backend", phandle);
> -	if (ret)
> -		goto out;
> -
> -	if (state->backend->digest) {
> -		p = of_new_property(new_node, "algo",
> -				    digest_name(state->backend->digest),
> -				    strlen(digest_name(state->backend->digest)) + 1);
> -		if (!p) {
> -			ret = -ENOMEM;
> -			goto out;
> -		}
> -	}
> -
> -	/* address-cells + size-cells */
> -	ret = of_property_write_u32(new_node, "#address-cells", 1);
> -	if (ret)
> -		goto out;
> -
> -	ret = of_property_write_u32(new_node, "#size-cells", 1);
> -	if (ret)
> -		goto out;
> -
> -	/* delete existing node */
> -	if (node)
> -		of_delete_node(node);
> -
> -	return 0;
> -
> - out:
> -	dev_err(&state->dev, "error fixing up device tree with boot state\n");
> -	of_delete_node(new_node);
> -	return ret;
> -}
> -
> -void state_release(struct state *state)
> -{
> -	of_unregister_fixup(of_state_fixup, state);
> -	list_del(&state->list);
> -	unregister_device(&state->dev);
> -	free(state);
> -}
> -
> -/*
> - * state_new_from_node - create a new state instance from a device_node
> - *
> - * @name	The name of the new state instance
> - * @node	The device_node describing the new state instance
> - */
> -struct state *state_new_from_node(const char *name, struct device_node *node)
> -{
> -	struct state *state;
> -	int ret;
> -
> -	state = state_new(name);
> -	if (IS_ERR(state))
> -		return state;
> -
> -	ret = state_from_node(state, node, 1);
> -	if (ret) {
> -		state_release(state);
> -		return ERR_PTR(ret);
> -	}
> -
> -	ret = of_register_fixup(of_state_fixup, state);
> -	if (ret) {
> -		state_release(state);
> -		return ERR_PTR(ret);
> -	}
> -
> -	return state;
> -}
> -
> -/*
> - * state_by_name - find a state instance by name
> - *
> - * @name	The name of the state instance
> - */
> -struct state *state_by_name(const char *name)
> -{
> -	struct state *state;
> -
> -	list_for_each_entry(state, &state_list, list) {
> -		if (!strcmp(name, state->name))
> -			return state;
> -	}
> -
> -	return NULL;
> -}
> -
> -/*
> - * state_by_node - find a state instance by of node
> - *
> - * @node	The of node of the state intance
> - */
> -struct state *state_by_node(const struct device_node *node)
> -{
> -	struct state *state;
> -
> -	list_for_each_entry(state, &state_list, list) {
> -		if (state->root == node)
> -			return state;
> -	}
> -
> -	return NULL;
> -}
> -
> -int state_get_name(const struct state *state, char const **name)
> -{
> -	*name = xstrdup(state->name);
> -
> -	return 0;
> -}
> -
> -/*
> - * state_save - save a state to the backing store
> - *
> - * @state	The state instance to save
> - */
> -int state_save(struct state *state)
> -{
> -	int ret;
> -
> -	if (!state->dirty)
> -		return 0;
> -
> -	if (!state->backend)
> -		return -ENOSYS;
> -
> -	ret = state->backend->save(state->backend, state);
> -	if (ret)
> -		return ret;
> -
> -	state->dirty = 0;
> -
> -	return 0;
> -}
> -
> -void state_info(void)
> -{
> -	struct state *state;
> -
> -	printf("registered state instances:\n");
> -
> -	list_for_each_entry(state, &state_list, list) {
> -		printf("%-20s ", state->name);
> -		if (state->backend)
> -			printf("(backend: %s, path: %s)\n",
> -			       state->backend->name, state->backend->path);
> -		else
> -			printf("(no backend)\n");
> -	}
> -}
> -
> -static int mtd_get_meminfo(const char *path, struct mtd_info_user *meminfo)
> -{
> -	int fd, ret;
> -
> -	fd = open(path, O_RDONLY);
> -	if (fd < 0)
> -		return fd;
> -
> -	ret = ioctl(fd, MEMGETINFO, meminfo);
> -
> -	close(fd);
> -
> -	return ret;
> -}
> -
> -/*
> - * DTB backend implementation
> - */
> -struct state_backend_dtb {
> -	struct state_backend backend;
> -	bool need_erase;
> -};
> -
> -static int state_backend_dtb_load(struct state_backend *backend,
> -				  struct state *state)
> -{
> -	struct device_node *root;
> -	void *fdt;
> -	int ret;
> -	size_t len;
> -
> -	fdt = read_file(backend->path, &len);
> -	if (!fdt) {
> -		dev_err(&state->dev, "cannot read %s\n", backend->path);
> -		return -EINVAL;
> -	}
> -
> -	root = of_unflatten_dtb(fdt);
> -
> -	free(fdt);
> -
> -	if (IS_ERR(root))
> -		return PTR_ERR(root);
> -
> -	ret = state_from_node(state, root, 0);
> -
> -	return ret;
> -}
> -
> -static int state_backend_dtb_save(struct state_backend *backend,
> -				  struct state *state)
> -{
> -	struct state_backend_dtb *backend_dtb = container_of(backend,
> -			struct state_backend_dtb, backend);
> -	int ret, fd;
> -	struct device_node *root;
> -	struct fdt_header *fdt;
> -
> -	root = state_to_node(state, NULL, STATE_CONVERT_TO_NODE);
> -	if (IS_ERR(root))
> -		return PTR_ERR(root);
> -
> -	fdt = of_flatten_dtb(root);
> -	if (!fdt)
> -		return -EINVAL;
> -
> -	fd = open(backend->path, O_WRONLY);
> -	if (fd < 0) {
> -		ret = fd;
> -		goto out;
> -	}
> -
> -	if (backend_dtb->need_erase) {
> -		ret = erase(fd, fdt32_to_cpu(fdt->totalsize), 0);
> -		if (ret) {
> -			close(fd);
> -			goto out;
> -		}
> -	}
> -
> -	ret = write_full(fd, fdt, fdt32_to_cpu(fdt->totalsize));
> -
> -	close(fd);
> -
> -	if (ret < 0)
> -		goto out;
> -
> -	ret = 0;
> -out:
> -	free(fdt);
> -	of_delete_node(root);
> -
> -	return ret;
> -}
> -
> -/*
> - * state_backend_dtb_file - create a dtb backend store for a state instance
> - *
> - * @state	The state instance to work on
> - * @path	The path where the state will be stored to
> - */
> -int state_backend_dtb_file(struct state *state, const char *of_path, const char *path)
> -{
> -	struct state_backend_dtb *backend_dtb;
> -	struct state_backend *backend;
> -	struct mtd_info_user meminfo;
> -	int ret;
> -
> -	if (state->backend)
> -		return -EBUSY;
> -
> -	backend_dtb = xzalloc(sizeof(*backend_dtb));
> -	backend = &backend_dtb->backend;
> -
> -	backend->save = state_backend_dtb_save;
> -	backend->of_path = xstrdup(of_path);
> -	backend->path = xstrdup(path);
> -	backend->name = "dtb";
> -
> -	state->backend = backend;
> -
> -	ret = mtd_get_meminfo(backend->path, &meminfo);
> -	if (!ret && !(meminfo.flags & MTD_NO_ERASE))
> -		backend_dtb->need_erase = true;
> -
> -	ret = state_backend_dtb_load(backend, state);
> -	if (ret) {
> -		dev_warn(&state->dev, "load failed - using defaults\n");
> -	} else {
> -		dev_info(&state->dev, "load successful\n");
> -		state->dirty = 0;
> -	}
> -
> -	/* ignore return value of load() */
> -	return 0;
> -}
> -
> -/*
> - * Raw backend implementation
> - */
> -struct state_backend_raw {
> -	struct state_backend backend;
> -	unsigned long size_data; /* The raw data size (without header) */
> -	unsigned long size_full; /* The size header + raw data + hmac */
> -	unsigned long stride; /* The stride size in bytes of the copies */
> -	off_t offset; /* offset in the storage file */
> -	size_t size; /* size of the storage area */
> -	int num_copy_read; /* The first successfully read copy */
> -	bool need_erase;
> -};
> -
> -struct backend_raw_header {
> -	uint32_t magic;
> -	uint16_t reserved;
> -	uint16_t data_len;
> -	uint32_t data_crc;
> -	uint32_t header_crc;
> -};
> -
> -static int backend_raw_load_one(struct state_backend_raw *backend_raw,
> -		struct state *state, int fd, off_t offset)
> -{
> -	uint32_t crc;
> -	struct state_variable *sv;
> -	struct backend_raw_header header = {};
> -	unsigned long max_len;
> -	int d_len = 0;
> -	int ret;
> -	void *buf, *data, *hmac;
> -
> -	max_len = backend_raw->stride;
> -
> -	ret = lseek(fd, offset, SEEK_SET);
> -	if (ret < 0)
> -		return ret;
> -
> -	ret = read_full(fd, &header, sizeof(header));
> -	max_len -= sizeof(header);
> -	if (ret < 0) {
> -		dev_err(&state->dev,
> -			"cannot read header from backend device\n");
> -		return ret;
> -	}
> -
> -	crc = crc32(0, &header, sizeof(header) - sizeof(uint32_t));
> -	if (crc != header.header_crc) {
> -		dev_err(&state->dev,
> -			"invalid header crc, calculated 0x%08x, found 0x%08x\n",
> -			crc, header.header_crc);
> -		return -EINVAL;
> -	}
> -
> -	if (state->magic && state->magic != header.magic) {
> -		dev_err(&state->dev,
> -			"invalid magic 0x%08x, should be 0x%08x\n",
> -			header.magic, state->magic);
> -		return -EINVAL;
> -	}
> -
> -	if (backend_raw->backend.digest) {
> -		d_len = digest_length(backend_raw->backend.digest);
> -		max_len -= d_len;
> -	}
> -
> -	if (header.data_len > max_len) {
> -		dev_err(&state->dev,
> -			"invalid data_len %u in header, max is %lu\n",
> -			header.data_len, max_len);
> -		return -EINVAL;
> -	}
> -
> -	buf = xzalloc(sizeof(header) + header.data_len + d_len);
> -	data = buf + sizeof(header);
> -	hmac = data + header.data_len;
> -
> -	ret = lseek(fd, offset, SEEK_SET);
> -	if (ret < 0)
> -		goto out_free;
> -
> -	ret = read_full(fd, buf, sizeof(header) + header.data_len + d_len);
> -	if (ret < 0)
> -		goto out_free;
> -
> -	crc = crc32(0, data, header.data_len);
> -	if (crc != header.data_crc) {
> -		dev_err(&state->dev,
> -			"invalid crc, calculated 0x%08x, found 0x%08x\n",
> -			crc, header.data_crc);
> -		ret = -EINVAL;
> -		goto out_free;
> -	}
> -
> -	if (backend_raw->backend.digest) {
> -		struct digest *d = backend_raw->backend.digest;
> -
> -		ret = digest_init(d);
> -		if (ret)
> -			goto out_free;
> -
> -		/* hmac over header and data */
> -		ret = digest_update(d, buf, sizeof(header) + header.data_len);
> -		if (ret)
> -			goto out_free;
> -
> -		ret = digest_verify(d, hmac);
> -		if (ret < 0)
> -			goto out_free;
> -	}
> -
> -	list_for_each_entry(sv, &state->variables, list) {
> -		if (sv->start + sv->size > header.data_len)
> -			break;
> -		memcpy(sv->raw, data + sv->start, sv->size);
> -	}
> -
> -	free(buf);
> -	return 0;
> -
> - out_free:
> -	free(buf);
> -	return ret;
> -}
> -
> -static int state_backend_raw_load(struct state_backend *backend,
> -				  struct state *state)
> -{
> -	struct state_backend_raw *backend_raw = container_of(backend,
> -			struct state_backend_raw, backend);
> -	int ret = 0, fd, i;
> -
> -	fd = open(backend->path, O_RDONLY);
> -	if (fd < 0) {
> -		dev_err(&state->dev, "cannot open %s\n", backend->path);
> -		return fd;
> -	}
> -
> -	for (i = 0; i < RAW_BACKEND_COPIES; i++) {
> -		off_t offset = backend_raw->offset + i * backend_raw->stride;
> -
> -		ret = backend_raw_load_one(backend_raw, state, fd, offset);
> -		if (!ret) {
> -			backend_raw->num_copy_read = i;
> -			dev_dbg(&state->dev,
> -				"copy %d successfully loaded\n", i);
> -			break;
> -		}
> -	}
> -
> -	close(fd);
> -
> -	return ret;
> -}
> -
> -static int backend_raw_save_one(struct state_backend_raw *backend_raw,
> -		struct state *state, int fd, int num, void *buf, size_t size)
> -{
> -	int ret;
> -	off_t offset = backend_raw->offset + num * backend_raw->stride;
> -
> -	dev_dbg(&state->dev, "%s: 0x%08lx 0x%08zx\n",
> -			__func__, offset, size);
> -
> -	ret = lseek(fd, offset, SEEK_SET);
> -	if (ret < 0)
> -		return ret;
> -
> -	protect(fd, backend_raw->stride, offset, false);
> -
> -	if (backend_raw->need_erase) {
> -		ret = erase(fd, backend_raw->stride, offset);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	ret = write_full(fd, buf, size);
> -	if (ret < 0)
> -		return ret;
> -
> -	protect(fd, backend_raw->stride, offset, true);
> -
> -	return 0;
> -}
> -
> -static int state_backend_raw_save(struct state_backend *backend,
> -				  struct state *state)
> -{
> -	struct state_backend_raw *backend_raw = container_of(backend,
> -			struct state_backend_raw, backend);
> -	int ret = 0, fd, i;
> -	void *buf, *data, *hmac;
> -	struct backend_raw_header *header;
> -	struct state_variable *sv;
> -
> -	buf = xzalloc(backend_raw->size_full);
> -
> -	header = buf;
> -	data = buf + sizeof(*header);
> -	hmac = data + backend_raw->size_data;
> -
> -	list_for_each_entry(sv, &state->variables, list)
> -		memcpy(data + sv->start, sv->raw, sv->size);
> -
> -	header->magic = state->magic;
> -	header->data_len = backend_raw->size_data;
> -	header->data_crc = crc32(0, data, backend_raw->size_data);
> -	header->header_crc = crc32(0, header,
> -				   sizeof(*header) - sizeof(uint32_t));
> -
> -	if (backend_raw->backend.digest) {
> -		struct digest *d = backend_raw->backend.digest;
> -
> -		ret = digest_init(d);
> -		if (ret)
> -			goto out_free;
> -
> -		/* hmac over header and data */
> -		ret = digest_update(d, buf, sizeof(*header) + backend_raw->size_data);
> -		if (ret)
> -			goto out_free;
> -
> -		ret = digest_final(d, hmac);
> -		if (ret < 0)
> -			goto out_free;
> -	}
> -
> -	fd = open(backend->path, O_WRONLY);
> -	if (fd < 0)
> -		goto out_free;
> -
> -	/* save other slots first */
> -	for (i = 0; i < RAW_BACKEND_COPIES; i++) {
> -		if (i == backend_raw->num_copy_read)
> -			continue;
> -
> -		ret = backend_raw_save_one(backend_raw, state, fd,
> -					   i, buf, backend_raw->size_full);
> -		if (ret)
> -			goto out_close;
> -
> -	}
> -
> -	ret = backend_raw_save_one(backend_raw, state, fd,
> -				   backend_raw->num_copy_read, buf, backend_raw->size_full);
> -	if (ret)
> -		goto out_close;
> -
> -	dev_dbg(&state->dev, "wrote state to %s\n", backend->path);
> -out_close:
> -	close(fd);
> -out_free:
> -	free(buf);
> -
> -	return ret;
> -}
> -
> -#ifdef __BAREBOX__
> -#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode) || S_ISCHR(s.st_mode))
> -#define BLKGET_GIVES_SIZE(s) 0
> -#ifndef BLKGETSIZE64
> -#define BLKGETSIZE64 -1
> -#endif
> -#else
> -#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode))
> -#define BLKGET_GIVES_SIZE(s) (S_ISBLK(s.st_mode))
> -#endif
> -
> -static int state_backend_raw_file_get_size(const char *path, size_t *out_size)
> -{
> -	struct mtd_info_user meminfo;
> -	struct stat s;
> -	int ret;
> -
> -	ret = stat(path, &s);
> -	if (ret)
> -		return -errno;
> -
> -	/*
> -	 * under Linux, stat() gives the size only on regular files
> -	 * under barebox, it works on char dev, too
> -	 */
> -	if (STAT_GIVES_SIZE(s)) {
> -		*out_size = s.st_size;
> -		return 0;
> -	}
> -
> -	/* this works under Linux on block devs */
> -	if (BLKGET_GIVES_SIZE(s)) {
> -		int fd;
> -
> -		fd = open(path, O_RDONLY);
> -		if (fd < 0)
> -			return -errno;
> -
> -		ret = ioctl(fd, BLKGETSIZE64, out_size);
> -		close(fd);
> -		if (!ret)
> -			return 0;
> -	}
> -
> -	/* try mtd next */
> -	ret = mtd_get_meminfo(path, &meminfo);
> -	if (!ret) {
> -		*out_size = meminfo.size;
> -		return 0;
> -	}
> -
> -	return ret;
> -}
> -
> -static int state_backend_raw_file_init_digest(struct state *state, struct state_backend_raw *backend_raw)
> -{
> -	struct digest *digest;
> -	struct property *p;
> -	const char *algo;
> -	const unsigned char *key;
> -	int key_len, ret;
> -
> -	p = of_find_property(state->root, "algo", NULL);
> -	if (!p)			/* does not exist */
> -		return 0;
> -
> -	ret = of_property_read_string(state->root, "algo", &algo);
> -	if (ret)
> -		return ret;
> -
> -	if (!IS_ENABLED(CONFIG_STATE_CRYPTO)) {
> -		dev_err(&state->dev,
> -			"algo %s specified, but crypto support for state framework (CONFIG_STATE_CRYPTO) not enabled.\n",
> -			algo);
> -		return -EINVAL;
> -	}
> -
> -	ret = keystore_get_secret(state->name, &key, &key_len);
> -	if (ret == -ENOENT)	/* -ENOENT == does not exist */
> -		return -EPROBE_DEFER;
> -	else if (ret)
> -		return ret;
> -
> -	digest = digest_alloc(algo);
> -	if (!digest) {
> -		dev_info(&state->dev, "algo %s not found - probe deferred\n", algo);
> -		return -EPROBE_DEFER;
> -	}
> -
> -	ret = digest_set_key(digest, key, key_len);
> -	if (ret) {
> -		digest_free(digest);
> -		return ret;
> -	}
> -
> -	backend_raw->backend.digest = digest;
> -	backend_raw->size_full = digest_length(digest);
> -
> -	return 0;
> -}
> -
> -/*
> - * state_backend_raw_file - create a raw file backend store for a state instance
> - *
> - * @state	The state instance to work on
> - * @path	The path where the state will be stored to
> - * @offset	The offset in the storage file
> - * @size	The maximum size to use in the storage file
> - *
> - * This backend stores raw binary data from a state instance. The
> - * binary data is protected with a magic value which has to match and
> - * a crc32 that must be valid.  Two copies are stored, sufficient
> - * space must be available.
> -
> - * @path can be a path to a device or a regular file. When it's a
> - * device @size may be 0. The two copies are spread to different
> - * eraseblocks if approriate for this device.
> - */
> -int state_backend_raw_file(struct state *state, const char *of_path,
> -		const char *path, off_t offset, size_t size)
> -{
> -	struct state_backend_raw *backend_raw;
> -	struct state_backend *backend;
> -	struct state_variable *sv;
> -	struct mtd_info_user meminfo;
> -	size_t path_size = 0;
> -	int ret;
> -
> -	if (state->backend)
> -		return -EBUSY;
> -
> -	ret = state_backend_raw_file_get_size(path, &path_size);
> -	if (ret)
> -		return ret;
> -
> -	if (size == 0)
> -		size = path_size;
> -	else if (offset + size > path_size)
> -		return -EINVAL;
> -
> -	backend_raw = xzalloc(sizeof(*backend_raw));
> -
> -	ret = state_backend_raw_file_init_digest(state, backend_raw);
> -	if (ret) {
> -		free(backend_raw);
> -		return ret;
> -	}
> -
> -	backend = &backend_raw->backend;
> -	backend->save = state_backend_raw_save;
> -	backend->of_path = xstrdup(of_path);
> -	backend->path = xstrdup(path);
> -	backend->name = "raw";
> -
> -	sv = list_last_entry(&state->variables, struct state_variable, list);
> -	backend_raw->size_data = sv->start + sv->size;
> -	backend_raw->offset = offset;
> -	backend_raw->size = size;
> -	backend_raw->size_full += backend_raw->size_data +
> -		sizeof(struct backend_raw_header);
> -
> -	state->backend = backend;
> -
> -	ret = mtd_get_meminfo(backend->path, &meminfo);
> -	if (!ret && !(meminfo.flags & MTD_NO_ERASE)) {
> -		backend_raw->need_erase = true;
> -		backend_raw->size_full = ALIGN(backend_raw->size_full,
> -					       meminfo.writesize);
> -		backend_raw->stride = ALIGN(backend_raw->size_full,
> -					    meminfo.erasesize);
> -		dev_dbg(&state->dev, "is a mtd, adjust stepsize to %ld\n",
> -			backend_raw->stride);
> -	} else {
> -		backend_raw->stride = backend_raw->size_full;
> -	}
> -
> -	if (backend_raw->size / backend_raw->stride < RAW_BACKEND_COPIES) {
> -		dev_err(&state->dev, "not enough space for two copies (%lu each)\n",
> -			backend_raw->stride);
> -		ret = -ENOSPC;
> -		goto err;
> -	}
> -
> -	ret = state_backend_raw_load(backend, state);
> -	if (ret) {
> -		dev_warn(&state->dev, "load failed - using defaults\n");
> -	} else {
> -		dev_info(&state->dev, "load successful\n");
> -		state->dirty = 0;
> -	}
> -
> -	/* ignore return value of load() */
> -	return 0;
> -err:
> -	digest_free(backend_raw->backend.digest);
> -
> -	free(backend_raw);
> -	return ret;
> -}
> diff --git a/common/state/Makefile b/common/state/Makefile
> new file mode 100644
> index 000000000000..5d6dcb26dfeb
> --- /dev/null
> +++ b/common/state/Makefile
> @@ -0,0 +1,8 @@
> +obj-y += state.o
> +obj-y += state_variables.o
> +obj-y += backend.o
> +obj-y += backend_format_dtb.o
> +obj-y += backend_format_raw.o
> +obj-y += backend_storage.o
> +obj-y += backend_bucket_direct.o
> +obj-y += backend_bucket_circular.o
> diff --git a/common/state/backend.c b/common/state/backend.c
> new file mode 100644
> index 000000000000..054f8d646679
> --- /dev/null
> +++ b/common/state/backend.c
> @@ -0,0 +1,209 @@
> +/*
> + * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa at pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/string.h>
> +#include <malloc.h>
> +#include <printk.h>
> +
> +#include "state.h"
> +
> +
> +/**
> + * Save the state
> + * @param state
> + * @return
> + */
> +int state_save(struct state *state)
> +{
> +	uint8_t *buf;
> +	ssize_t len;
> +	int ret;
> +	struct state_backend *backend = &state->backend;
> +
> +	if (!state->dirty)
> +		return 0;
> +
> +	ret = backend->format->pack(backend->format, state, &buf, &len);
> +	if (ret) {
> +		dev_err(&state->dev, "Failed to pack state with backend format %s, %d\n",
> +			backend->format->name, ret);
> +		return ret;
> +	}
> +
> +	ret = state_storage_write(&backend->storage, buf, len);
> +	if (ret) {
> +		dev_err(&state->dev, "Failed to write packed state, %d\n", ret);
> +		goto out;
> +	}
> +
> +	state->dirty = 0;
> +
> +out:
> +	free(buf);
> +	return ret;
> +}
> +
> +/**
> + * state_load - Loads a state from the backend
> + * @param state The state that should be updated to contain the loaded data
> + * @return 0 on success, -errno on failure. If no state is loaded the previous
> + * values remain in the state.
> + *
> + * This function uses the registered storage backend to read data. All data that
> + * we read is checked for integrity by the formatter. After that we unpack the
> + * data into our state.
> + */
> +int state_load(struct state *state)
> +{
> +	uint8_t *buf;
> +	ssize_t len;
> +	ssize_t len_hint = 0;
> +	int ret;
> +	struct state_backend *backend = &state->backend;
> +
> +	if (backend->format->get_packed_len)
> +		len_hint = backend->format->get_packed_len(backend->format,
> +							   state);
> +	ret = state_storage_read(&backend->storage, backend->format,
> +				 state->magic, &buf, &len, len_hint);
> +	if (ret) {
> +		dev_err(&state->dev, "Failed to read state with format %s, %d\n",
> +			backend->format->name, ret);
> +		return ret;
> +	}
> +
> +	ret = backend->format->unpack(backend->format, state, buf, len);
> +	if (ret) {
> +		dev_err(&state->dev, "Failed to unpack read data with format %s although verified, %d\n",
> +			backend->format->name, ret);
> +		goto out;
> +	}
> +
> +	state->dirty = 0;
> +
> +out:
> +	free(buf);
> +	return ret;
> +}
> +
> +static int state_format_init(struct state_backend *backend,
> +			     struct device_d *dev, const char *backend_format,
> +			     struct device_node *node, const char *state_name)
> +{
> +	int ret;
> +
> +	if (!strcmp(backend_format, "raw")) {
> +		ret = backend_format_raw_create(&backend->format, node,
> +						state_name, dev);
> +	} else if (!strcmp(backend_format, "dtb")) {
> +		ret = backend_format_dtb_create(&backend->format, dev);
> +	} else {
> +		dev_err(dev, "Invalid backend format %s\n",
> +			backend_format);
> +		return -EINVAL;
> +	}
> +
> +	if (ret && ret != -EPROBE_DEFER)
> +		dev_err(dev, "Failed to initialize format %s, %d\n",
> +			backend_format, ret);
> +
> +	return ret;
> +}
> +
> +static void state_format_free(struct state_backend_format *format)
> +{
> +	if (format->free)
> +		format->free(format);
> +}
> +
> +/**
> + * state_backend_init - Initiates the backend storage and format using the
> + * passed arguments
> + * @param backend state backend
> + * @param dev Device pointer used for prints
> + * @param node the DT device node corresponding to the state
> + * @param backend_format a string describing the format. Valid values are 'raw'
> + * and 'dtb' currently
> + * @param storage_path Path to the backend storage file/device/partition/...
> + * @param state_name Name of the state
> + * @param of_path Path in the devicetree
> + * @param stridesize stridesize in case we have a medium without eraseblocks.
> + * stridesize describes how far apart copies of the same data should be stored.
> + * For blockdevices it makes sense to align them on blocksize.
> + * @param storagetype Type of the storage backend. This may be NULL where we
> + * autoselect some backwardscompatible backend options
> + * @return 0 on success, -errno otherwise
> + */
> +int state_backend_init(struct state_backend *backend, struct device_d *dev,
> +		       struct device_node *node, const char *backend_format,
> +		       const char *storage_path, const char *state_name, const
> +		       char *of_path, off_t offset, size_t max_size,
> +		       uint32_t stridesize, const char *storagetype)
> +{
> +	struct state_backend_storage_bucket *bucket;
> +	struct state_backend_storage_bucket *bucket_tmp;
> +	int ret;
> +
> +	ret = state_format_init(backend, dev, backend_format, node, state_name);
> +	if (ret)
> +		return ret;
> +
> +	ret = state_storage_init(&backend->storage, dev, storage_path, offset,
> +				 max_size, stridesize, storagetype);
> +	if (ret)
> +		goto out_free_format;
> +
> +	list_for_each_entry_safe(bucket, bucket_tmp, &backend->storage.buckets,
> +				 bucket_list) {
> +		if (!bucket->init)
> +			continue;
> +
> +		ret = bucket->init(bucket);
> +		if (ret) {
> +			dev_warn(dev, "Bucket init failed, state degraded, %d\n",
> +				 ret);
> +			list_del(&bucket->bucket_list);
> +			bucket->free(bucket);
> +			continue;
> +		}
> +	}
> +
> +	if (list_empty(&backend->storage.buckets)) {
> +		dev_err(dev, "Failed to initialize any state bucket\n");
> +		ret = -EIO;
> +		goto out_free_storage;
> +	}
> +
> +
> +	backend->of_path = of_path;
> +
> +	return 0;
> +
> +out_free_storage:
> +	state_storage_free(&backend->storage);
> +out_free_format:
> +	state_format_free(backend->format);
> +	backend->format = NULL;
> +
> +	return ret;
> +}
> +
> +void state_backend_free(struct state_backend *backend)
> +{
> +	state_storage_free(&backend->storage);
> +	if (backend->format)
> +		state_format_free(backend->format);
> +}
> diff --git a/common/state/backend_bucket_circular.c b/common/state/backend_bucket_circular.c
> new file mode 100644
> index 000000000000..64e9be2e9979
> --- /dev/null
> +++ b/common/state/backend_bucket_circular.c
> @@ -0,0 +1,580 @@
> +/*
> + * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa at pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <asm-generic/ioctl.h>
> +#include <common.h>
> +#include <fcntl.h>
> +#include <fs.h>
> +#include <libfile.h>
> +#include <linux/kernel.h>
> +#include <linux/mtd/mtd-abi.h>
> +#include <malloc.h>
> +#include <mtd/mtd-peb.h>
> +#include <string.h>
> +
> +#include "state.h"
> +
> +
> +struct state_backend_storage_bucket_circular {
> +	struct state_backend_storage_bucket bucket;
> +
> +	unsigned int eraseblock; /* Which eraseblock is used */
> +	ssize_t writesize; /* Alignment of writes */
> +	ssize_t max_size; /* Maximum size of this bucket */
> +
> +	off_t write_area; /* Start of the write area (relative offset) */
> +	uint32_t last_written_length; /* Size of the data written in the storage */
> +
> +#ifdef __BAREBOX__
> +	struct mtd_info *mtd; /* mtd info (used for io in Barebox)*/
> +#else
> +	struct mtd_info_user *mtd;
> +	int fd;
> +#endif
> +
> +	/* Cached data of the last read/write */
> +	u8 *current_data;
> +	ssize_t current_data_len;
> +
> +	bool force_rewrite; /* In case of degradation, force a rewrite */
> +
> +	/* For outputs */
> +	struct device_d *dev;
> +};
> +
> +struct state_backend_storage_bucket_circular_meta {
> +	uint32_t magic;
> +	uint32_t written_length;
> +};
> +
> +static const uint32_t circular_magic = 0x14fa2d02;
> +static const uint8_t free_pattern = 0xff;
> +
> +static inline struct state_backend_storage_bucket_circular
> +    *get_bucket_circular(struct state_backend_storage_bucket *bucket)
> +{
> +	return container_of(bucket,
> +			    struct state_backend_storage_bucket_circular,
> +			    bucket);
> +}
> +
> +#ifdef __BAREBOX__
> +static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ,
> +			      char *buf, int offset, int len)
> +{
> +	int ret;
> +
> +	ret = mtd_peb_read(circ->mtd, buf, circ->eraseblock, offset, len);
> +	if (ret == -EBADMSG) {
> +		ret = mtd_peb_torture(circ->mtd, circ->eraseblock);
> +		if (ret == -EIO) {
> +			dev_err(circ->dev, "Tortured eraseblock failed and is marked bad now, PEB %u\n",
> +				circ->eraseblock);
> +			return -EIO;
> +		} else if (ret < 0) {
> +			dev_err(circ->dev, "Failed to torture eraseblock, %d\n",
> +				ret);
> +			return ret;
> +		}
> +		/*
> +		 * Fill with invalid data so that the next write is done
> +		 * behind this area
> +		 */
> +		memset(buf, 0, len);
> +		circ->force_rewrite = true;
> +		circ->write_area = 0;
> +		dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n",
> +			circ->eraseblock);
> +	} else if (ret == -EUCLEAN) {
> +		circ->force_rewrite = true;
> +		dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n",
> +			circ->eraseblock);
> +	} else if (ret < 0) {
> +		dev_err(circ->dev, "Failed to read PEB %u, %d\n",
> +			circ->eraseblock, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ,
> +			       const char *buf, int offset, int len)
> +{
> +	int ret;
> +
> +	ret = mtd_peb_write(circ->mtd, buf, circ->eraseblock, offset, len);
> +	if (ret == -EBADMSG) {
> +		ret = mtd_peb_torture(circ->mtd, circ->eraseblock);
> +		if (ret == -EIO) {
> +			dev_err(circ->dev, "Tortured eraseblock failed and is marked bad now, PEB %u\n",
> +				circ->eraseblock);
> +			return -EIO;
> +		} else if (ret < 0) {
> +			dev_err(circ->dev, "Failed to torture eraseblock, %d\n",
> +				ret);
> +			return ret;
> +		}
> +		circ->force_rewrite = true;
> +	} else if (ret < 0 && ret != -EUCLEAN) {
> +		dev_err(circ->dev, "Failed to write PEB %u, %d\n",
> +			circ->eraseblock, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *circ)
> +{
> +	return mtd_peb_erase(circ->mtd, circ->eraseblock);
> +}
> +#else
> +static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ,
> +			      char *buf, int suboffset, int len)
> +{
> +	int ret;
> +	off_t offset = suboffset;
> +	struct mtd_ecc_stats stat1, stat2;
> +	bool nostats = false;
> +
> +	offset += (off_t)circ->eraseblock * circ->mtd->erasesize;
> +
> +	ret = lseek(circ->fd, offset, SEEK_SET);
> +	if (ret < 0) {
> +		dev_err(circ->dev, "Failed to set circular read position to %lld, %d\n",
> +			offset, ret);
> +		return ret;
> +	}
> +
> +	dev_dbg(circ->dev, "Read state from %ld length %zd\n", offset,
> +		len);
> +
> +	ret = ioctl(circ->fd, ECCGETSTATS, &stat1);
> +	if (ret)
> +		nostats = true;
> +
> +	ret = read_full(circ->fd, buf, len);
> +	if (ret < 0) {
> +		dev_err(circ->dev, "Failed to read circular storage len %zd, %d\n",
> +			len, ret);
> +		free(buf);
> +		return ret;
> +	}
> +
> +	if (nostats)
> +		return 0;
> +
> +	ret = ioctl(circ->fd, ECCGETSTATS, &stat2);
> +	if (ret)
> +		return 0;
> +
> +	if (stat2.failed - stat1.failed > 0) {
> +		circ->force_rewrite = true;
> +		dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n",
> +			circ->eraseblock);
> +	} else if (stat2.corrected - stat1.corrected > 0) {
> +		circ->force_rewrite = true;
> +		dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n",
> +			circ->eraseblock);
> +	}
> +
> +	return 0;
> +}
> +
> +static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ,
> +			       const char *buf, int suboffset, int len)
> +{
> +	int ret;
> +	off_t offset = suboffset;
> +
> +	offset += circ->eraseblock * circ->mtd->erasesize;
> +
> +	ret = lseek(circ->fd, offset, SEEK_SET);
> +	if (ret < 0) {
> +		dev_err(circ->dev, "Failed to set position for circular write %ld, %d\n",
> +			offset, ret);
> +		return ret;
> +	}
> +
> +	ret = write_full(circ->fd, buf, len);
> +	if (ret < 0) {
> +		dev_err(circ->dev, "Failed to write circular to %ld length %zd, %d\n",
> +			offset, len, ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * We keep the fd open, so flush is necessary. We ignore the return
> +	 * value as flush is currently not supported for mtd under linux.
> +	 */
> +	flush(circ->fd);
> +
> +	dev_dbg(circ->dev, "Written state to offset %ld length %zd data length %zd\n",
> +		offset, len, len);
> +
> +	return 0;
> +}
> +
> +static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *circ)
> +{
> +	return erase(circ->fd, circ->mtd->erasesize,
> +		     (off_t)circ->eraseblock * circ->mtd->erasesize);
> +}
> +#endif
> +
> +static int state_backend_bucket_circular_fill_cache(
> +		struct state_backend_storage_bucket *bucket)
> +{
> +	struct state_backend_storage_bucket_circular *circ =
> +	    get_bucket_circular(bucket);
> +	ssize_t read_len;
> +	off_t offset;
> +	uint8_t *buf;
> +	int ret;
> +
> +	/* Storage is empty */
> +	if (circ->write_area == 0)
> +		return -ENODATA;
> +
> +	if (!circ->last_written_length) {
> +		/*
> +		 * Last write did not contain length information, assuming old
> +		 * state and reading from the beginning.
> +		 */
> +		offset = 0;
> +		read_len = min(circ->write_area, (off_t)(circ->max_size -
> +			       sizeof(struct state_backend_storage_bucket_circular_meta)));
> +		circ->write_area = 0;
> +		dev_dbg(circ->dev, "Detected old on-storage format\n");
> +	} else if (circ->last_written_length > circ->write_area
> +		   || !IS_ALIGNED(circ->last_written_length, circ->writesize)) {
> +		circ->write_area = 0;
> +		dev_err(circ->dev, "Error, invalid number of bytes written last time %d\n",
> +			circ->last_written_length);
> +		return -EINVAL;
> +	} else {
> +		/*
> +		 * Normally we read at the end of the non-free area. The length
> +		 * of the read is then what we read from the meta data
> +		 * (last_written_length)
> +		 */
> +		read_len = circ->last_written_length;
> +		offset = circ->write_area - read_len;
> +	}
> +
> +	buf = xmalloc(read_len);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	dev_dbg(circ->dev, "Read state from PEB %u global offset %ld length %zd\n",
> +		circ->eraseblock, offset, read_len);
> +
> +	ret = state_mtd_peb_read(circ, buf, offset, read_len);
> +	if (ret < 0) {
> +		dev_err(circ->dev, "Failed to read circular storage len %zd, %d\n",
> +			read_len, ret);
> +		free(buf);
> +		return ret;
> +	}
> +
> +	circ->current_data = buf;
> +	circ->current_data_len = read_len;
> +
> +	return 0;
> +}
> +
> +static int state_backend_bucket_circular_read(struct state_backend_storage_bucket *bucket,
> +					      uint8_t ** buf_out,
> +					      ssize_t * len_hint)
> +{
> +	struct state_backend_storage_bucket_circular *circ =
> +	    get_bucket_circular(bucket);
> +	int ret;
> +
> +	if (!circ->current_data) {
> +		ret = state_backend_bucket_circular_fill_cache(bucket);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/*
> +	 * We keep the internal copy in the cache and duplicate the data for
> +	 * external use
> +	 */
> +	*buf_out = xmemdup(circ->current_data, circ->current_data_len);
> +	if (!*buf_out)
> +		return -ENOMEM;
> +
> +	*len_hint = circ->current_data_len - sizeof(struct state_backend_storage_bucket_circular_meta);
> +
> +	return 0;
> +}
> +
> +static int state_backend_bucket_circular_write(struct state_backend_storage_bucket *bucket,
> +					       const uint8_t * buf,
> +					       ssize_t len)
> +{
> +	struct state_backend_storage_bucket_circular *circ =
> +	    get_bucket_circular(bucket);
> +	off_t offset;
> +	struct state_backend_storage_bucket_circular_meta *meta;
> +	uint32_t written_length = ALIGN(len + sizeof(*meta), circ->writesize);
> +	int ret;
> +	uint8_t *write_buf;
> +
> +	if (written_length > circ->max_size) {
> +		dev_err(circ->dev, "Error, state data too big to be written, to write: %zd, writesize: %zd, length: %zd, available: %zd\n",
> +			written_length, circ->writesize, len, circ->max_size);
> +		return -E2BIG;
> +	}
> +
> +	/*
> +	 * We need zero initialization so that our data comparisons don't show
> +	 * random changes
> +	 */
> +	write_buf = xzalloc(written_length);
> +	if (!write_buf)
> +		return -ENOMEM;
> +
> +	memcpy(write_buf, buf, len);
> +	meta = (struct state_backend_storage_bucket_circular_meta *)
> +			(write_buf + written_length - sizeof(*meta));
> +	meta->magic = circular_magic;
> +	meta->written_length = written_length;
> +
> +	/* Nothing in cache? Then read to see what is on the device currently */
> +	if (!circ->current_data) {
> +		state_backend_bucket_circular_fill_cache(bucket);
> +	}
> +
> +	/*
> +	 * If we would write the same data that is currently on the device, we
> +	 * can return successfully without writing.
> +	 * Note that the cache may still be empty if the storage is empty or
> +	 * errors occured.
> +	 */
> +	if (circ->current_data) {
> +		dev_dbg(circ->dev, "Comparing cached data, writing %zd bytes, cached %zd bytes\n",
> +			written_length, circ->current_data_len);
> +		if (!circ->force_rewrite &&
> +		    written_length == circ->current_data_len &&
> +		    !memcmp(circ->current_data, write_buf, written_length)) {
> +			dev_dbg(circ->dev, "Data already on device, not writing again\n");
> +			goto out_free;
> +		} else {
> +			free(circ->current_data);
> +			circ->current_data = NULL;
> +		}
> +	}
> +
> +	if (circ->write_area + written_length >= circ->max_size) {
> +		circ->write_area = 0;
> +	}
> +	/*
> +	 * If the write area is at the beginning of the page, erase it and write
> +	 * at offset 0. As we only erase right before writing there are no
> +	 * conditions where we regularly erase a block multiple times without
> +	 * writing.
> +	 */
> +	if (circ->write_area == 0) {
> +		dev_dbg(circ->dev, "Erasing PEB %u\n", circ->eraseblock);
> +		ret = state_mtd_peb_erase(circ);
> +		if (ret) {
> +			dev_err(circ->dev, "Failed to erase PEB %u\n",
> +				circ->eraseblock);
> +			goto out_free;
> +		}
> +	}
> +
> +	offset = circ->write_area;
> +
> +	/*
> +	 * Update write_area before writing. The write operation may put
> +	 * arbitrary amount of the data into the storage before failing. In this
> +	 * case we want to start after that area.
> +	 */
> +	circ->write_area += written_length;
> +
> +	ret = state_mtd_peb_write(circ, write_buf, offset, written_length);
> +	if (ret < 0) {
> +		dev_err(circ->dev, "Failed to write circular to %ld length %zd, %d\n",
> +			offset, written_length, ret);
> +		goto out_free;
> +	}
> +
> +	dev_dbg(circ->dev, "Written state to PEB %u offset %ld length %zd data length %zd\n",
> +		circ->eraseblock, offset, written_length, len);
> +
> +	/* Put written data into the cache */
> +	WARN_ON(circ->current_data);
> +	circ->current_data = write_buf;
> +	circ->current_data_len = written_length;
> +	write_buf = NULL;
> +	circ->force_rewrite = false;
> +
> +out_free:
> +	if (write_buf)
> +		free(write_buf);
> +	return 0;
> +}
> +
> +/**
> + * state_backend_bucket_circular_init - Initialize circular bucket
> + * @param bucket
> + * @return 0 on success, -errno otherwise
> + *
> + * This function searches for the beginning of the written area from the end of
> + * the MTD device. This way it knows where the data ends and where the free area
> + * starts.
> + */
> +static int state_backend_bucket_circular_init(
> +		struct state_backend_storage_bucket *bucket)
> +{
> +	struct state_backend_storage_bucket_circular *circ =
> +	    get_bucket_circular(bucket);
> +	int sub_offset;
> +	uint32_t written_length = 0;
> +	uint8_t *buf;
> +
> +	buf = xmalloc(circ->writesize);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	for (sub_offset = circ->max_size - circ->writesize; sub_offset >= 0;
> +	     sub_offset -= circ->writesize) {
> +		int ret;
> +
> +		ret = state_mtd_peb_read(circ, buf, sub_offset,
> +					 circ->writesize);
> +		if (ret)
> +			return ret;
> +
> +		ret = mtd_buf_all_ff(buf, circ->writesize);
> +		if (!ret) {
> +			struct state_backend_storage_bucket_circular_meta *meta;
> +
> +			meta = (struct state_backend_storage_bucket_circular_meta *)
> +					(buf + circ->writesize - sizeof(*meta));
> +
> +			if (meta->magic != circular_magic)
> +				written_length = 0;
> +			else
> +				written_length = meta->written_length;
> +
> +			break;
> +		}
> +	}
> +
> +	circ->write_area = sub_offset + circ->writesize;
> +	circ->last_written_length = written_length;
> +
> +	free(buf);
> +
> +	return 0;
> +}
> +
> +static void state_backend_bucket_circular_free(struct
> +					       state_backend_storage_bucket
> +					       *bucket)
> +{
> +	struct state_backend_storage_bucket_circular *circ =
> +	    get_bucket_circular(bucket);
> +
> +	if (circ->current_data)
> +		free(circ->current_data);
> +	free(circ);
> +}
> +
> +#ifdef __BAREBOX__
> +static int bucket_circular_is_block_bad(struct state_backend_storage_bucket_circular *circ)
> +{
> +	int ret;
> +
> +	ret = mtd_peb_is_bad(circ->mtd, circ->eraseblock);
> +	if (ret < 0)
> +		dev_err(circ->dev, "Failed to determine whether eraseblock %u is bad, %d\n",
> +			circ->eraseblock, ret);
> +
> +	return ret;
> +}
> +#else
> +static int bucket_circular_is_block_bad(struct state_backend_storage_bucket_circular *circ)
> +{
> +	int ret;
> +	loff_t offs = circ->eraseblock * circ->mtd->erasesize;
> +
> +	ret = ioctl(circ->fd, MEMGETBADBLOCK, &offs);
> +	if (ret < 0)
> +		dev_err(circ->dev, "Failed to use ioctl to check for bad block at offset %ld, %d\n",
> +			offs, ret);
> +
> +	return ret;
> +}
> +#endif
> +
> +int state_backend_bucket_circular_create(struct device_d *dev, const char *path,
> +					 struct state_backend_storage_bucket **bucket,
> +					 unsigned int eraseblock,
> +					 ssize_t writesize,
> +					 struct mtd_info_user *mtd_uinfo)
> +{
> +	struct state_backend_storage_bucket_circular *circ;
> +	int ret;
> +
> +	circ = xzalloc(sizeof(*circ));
> +	circ->eraseblock = eraseblock;
> +	circ->writesize = writesize;
> +	circ->max_size = mtd_uinfo->erasesize;
> +	circ->dev = dev;
> +
> +#ifdef __BAREBOX__
> +	circ->mtd = mtd_uinfo->mtd;
> +#else
> +	circ->mtd = xzalloc(sizeof(*mtd_uinfo));
> +	memcpy(circ->mtd, mtd_uinfo, sizeof(*mtd_uinfo));
> +	circ->fd = open(path, O_RDWR);
> +	if (circ->fd < 0) {
> +		pr_err("Failed to open circular bucket '%s'\n", path);
> +		return -errno;
> +	}
> +#endif
> +
> +	ret = bucket_circular_is_block_bad(circ);
> +	if (ret) {
> +		dev_info(dev, "Not using eraseblock %u, it is marked as bad (%d)\n",
> +			 circ->eraseblock, ret);
> +		ret = -EIO;
> +		goto out_free;
> +	}
> +
> +	circ->bucket.read = state_backend_bucket_circular_read;
> +	circ->bucket.write = state_backend_bucket_circular_write;
> +	circ->bucket.free = state_backend_bucket_circular_free;
> +	*bucket = &circ->bucket;
> +
> +	ret = state_backend_bucket_circular_init(*bucket);
> +	if (ret)
> +		goto out_free;
> +
> +	return 0;
> +
> +out_free:
> +#ifndef __BAREBOX__
> +	close(circ->fd);
> +#endif
> +	free(circ);
> +
> +	return ret;
> +}
> diff --git a/common/state/backend_bucket_direct.c b/common/state/backend_bucket_direct.c
> new file mode 100644
> index 000000000000..08892f001e25
> --- /dev/null
> +++ b/common/state/backend_bucket_direct.c
> @@ -0,0 +1,180 @@
> +/*
> + * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa at pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <fcntl.h>
> +#include <fs.h>
> +#include <libfile.h>
> +#include <linux/kernel.h>
> +#include <malloc.h>
> +#include <printk.h>
> +
> +#include "state.h"
> +
> +struct state_backend_storage_bucket_direct {
> +	struct state_backend_storage_bucket bucket;
> +
> +	ssize_t offset;
> +	ssize_t max_size;
> +
> +	int fd;
> +
> +	struct device_d *dev;
> +};
> +
> +struct state_backend_storage_bucket_direct_meta {
> +	uint32_t magic;
> +	uint32_t written_length;
> +};
> +static const uint32_t direct_magic = 0x2354fdf3;
> +
> +static inline struct state_backend_storage_bucket_direct
> +    *get_bucket_direct(struct state_backend_storage_bucket *bucket)
> +{
> +	return container_of(bucket, struct state_backend_storage_bucket_direct,
> +			    bucket);
> +}
> +
> +static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
> +					    *bucket, uint8_t ** buf_out,
> +					    ssize_t * len_hint)
> +{
> +	struct state_backend_storage_bucket_direct *direct =
> +	    get_bucket_direct(bucket);
> +	struct state_backend_storage_bucket_direct_meta meta;
> +	ssize_t read_len;
> +	uint8_t *buf;
> +	int ret;
> +
> +	ret = lseek(direct->fd, direct->offset, SEEK_SET);
> +	if (ret < 0) {
> +		dev_err(direct->dev, "Failed to seek file, %d\n", ret);
> +		return ret;
> +	}
> +	ret = read_full(direct->fd, &meta, sizeof(meta));
> +	if (ret < 0) {
> +		dev_err(direct->dev, "Failed to read meta data from file, %d\n", ret);
> +		return ret;
> +	}
> +	if (meta.magic == direct_magic) {
> +		read_len = meta.written_length;
> +	} else {
> +		if (*len_hint)
> +			read_len = *len_hint;
> +		else
> +			read_len = direct->max_size;
> +		ret = lseek(direct->fd, direct->offset, SEEK_SET);
> +		if (ret < 0) {
> +			dev_err(direct->dev, "Failed to seek file, %d\n", ret);
> +			return ret;
> +		}
> +	}
> +	if (direct->max_size)
> +		read_len = min(read_len, direct->max_size);
> +
> +	buf = xmalloc(read_len);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ret = read_full(direct->fd, buf, read_len);
> +	if (ret < 0) {
> +		dev_err(direct->dev, "Failed to read from file, %d\n", ret);
> +		free(buf);
> +		return ret;
> +	}
> +
> +	*buf_out = buf;
> +	*len_hint = read_len;
> +
> +	return 0;
> +}
> +
> +static int state_backend_bucket_direct_write(struct state_backend_storage_bucket
> +					     *bucket, const uint8_t * buf,
> +					     ssize_t len)
> +{
> +	struct state_backend_storage_bucket_direct *direct =
> +	    get_bucket_direct(bucket);
> +	int ret;
> +	struct state_backend_storage_bucket_direct_meta meta;
> +
> +	if (direct->max_size && len > direct->max_size)
> +		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;
> +	}
> +
> +	ret = write_full(direct->fd, buf, len);
> +	if (ret < 0) {
> +		dev_err(direct->dev, "Failed to write file, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = flush(direct->fd);
> +	if (ret < 0) {
> +		dev_err(direct->dev, "Failed to flush file, %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void state_backend_bucket_direct_free(struct
> +					     state_backend_storage_bucket
> +					     *bucket)
> +{
> +	struct state_backend_storage_bucket_direct *direct =
> +	    get_bucket_direct(bucket);
> +
> +	close(direct->fd);
> +	free(direct);
> +}
> +
> +int state_backend_bucket_direct_create(struct device_d *dev, const char *path,
> +				       struct state_backend_storage_bucket **bucket,
> +				       off_t offset, ssize_t max_size)
> +{
> +	int fd;
> +	struct state_backend_storage_bucket_direct *direct;
> +
> +	fd = open(path, O_RDWR);
> +	if (fd < 0) {
> +		dev_err(dev, "Failed to open file '%s', %d\n", path, -errno);
> +		close(fd);
> +		return -errno;
> +	}
> +
> +	direct = xzalloc(sizeof(*direct));
> +	direct->offset = offset;
> +	direct->max_size = max_size;
> +	direct->fd = fd;
> +	direct->dev = dev;
> +
> +	direct->bucket.read = state_backend_bucket_direct_read;
> +	direct->bucket.write = state_backend_bucket_direct_write;
> +	direct->bucket.free = state_backend_bucket_direct_free;
> +	*bucket = &direct->bucket;
> +
> +	return 0;
> +}
> diff --git a/common/state/backend_format_dtb.c b/common/state/backend_format_dtb.c
> new file mode 100644
> index 000000000000..dc19c888e5b6
> --- /dev/null
> +++ b/common/state/backend_format_dtb.c
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (C) 2012-2014 Pengutronix, Jan Luebbe <j.luebbe at pengutronix.de>
> + * Copyright (C) 2013-2014 Pengutronix, Sascha Hauer <s.hauer at pengutronix.de>
> + * Copyright (C) 2015 Pengutronix, Marc Kleine-Budde <mkl at pengutronix.de>
> + * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa at pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <common.h>
> +#include <of.h>
> +#include <linux/kernel.h>
> +#include <malloc.h>
> +
> +#include "state.h"
> +
> +struct state_backend_format_dtb {
> +	struct state_backend_format format;
> +
> +	struct device_node *root;
> +
> +	/* For outputs */
> +	struct device_d *dev;
> +};
> +
> +static inline struct state_backend_format_dtb *get_format_dtb(struct
> +							      state_backend_format
> +							      *format)
> +{
> +	return container_of(format, struct state_backend_format_dtb, format);
> +}
> +
> +static int state_backend_format_dtb_verify(struct state_backend_format *format,
> +					   uint32_t magic, const uint8_t * buf,
> +					   ssize_t len)
> +{
> +	struct state_backend_format_dtb *fdtb = get_format_dtb(format);
> +	struct device_node *root;
> +	struct fdt_header *fdt = (struct fdt_header *)buf;
> +	size_t dtb_len = fdt32_to_cpu(fdt->totalsize);
> +
> +	if (dtb_len > len) {
> +		dev_err(fdtb->dev, "Error, stored DTB length (%d) longer than read buffer (%d)\n",
> +			dtb_len, len);
> +		return -EINVAL;
> +	}
> +
> +	if (fdtb->root) {
> +		of_delete_node(fdtb->root);
> +		fdtb->root = NULL;
> +	}
> +
> +	root = of_unflatten_dtb(buf);
> +	if (IS_ERR(root)) {
> +		dev_err(fdtb->dev, "Failed to unflatten dtb from buffer with length %zd, %ld\n",
> +			len, PTR_ERR(root));
> +		return PTR_ERR(root);
> +	}
> +
> +	fdtb->root = root;
> +
> +	return 0;
> +}
> +
> +static int state_backend_format_dtb_unpack(struct state_backend_format *format,
> +					   struct state *state,
> +					   const uint8_t * buf, ssize_t len)
> +{
> +	struct state_backend_format_dtb *fdtb = get_format_dtb(format);
> +	int ret;
> +
> +	if (!fdtb->root) {
> +		state_backend_format_dtb_verify(format, 0, buf, len);
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.infradead.org/pipermail/barebox/attachments/20160623/99bf0e95/attachment-0001.sig>


More information about the barebox mailing list