diff options
Diffstat (limited to 'common/state.c')
-rw-r--r-- | common/state.c | 1720 |
1 files changed, 0 insertions, 1720 deletions
diff --git a/common/state.c b/common/state.c deleted file mode 100644 index 87afff3056..0000000000 --- a/common/state.c +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * Copyright (C) 2012-2014 Pengutronix, Jan Luebbe <j.luebbe@pengutronix.de> - * Copyright (C) 2013-2014 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> - * Copyright (C) 2015 Pengutronix, Marc Kleine-Budde <mkl@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; -} |