[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