summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Kleine-Budde <mkl@pengutronix.de>2015-05-25 13:28:46 +0200
committerMarc Kleine-Budde <mkl@pengutronix.de>2015-05-25 21:39:07 +0200
commitf2243552be90b8cb1e37e56b70ff23a93836b0a3 (patch)
treeae2308564251bdd0fbf2d35bd9b6f70d14abf32e
parent17297aeea4e72a1a2b27b1475e3194f0004944bc (diff)
downloaddt-utils-f2243552be90b8cb1e37e56b70ff23a93836b0a3.tar.gz
dt-utils-f2243552be90b8cb1e37e56b70ff23a93836b0a3.tar.xz
barebox-state: import from barebox
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r--src/barebox-state.c288
-rw-r--r--src/dt/common.h10
-rw-r--r--src/dt/dt.h3
3 files changed, 213 insertions, 88 deletions
diff --git a/src/barebox-state.c b/src/barebox-state.c
index 7a20030..9959195 100644
--- a/src/barebox-state.c
+++ b/src/barebox-state.c
@@ -78,13 +78,14 @@ struct state {
struct list_head list;
struct state_backend *backend;
uint32_t magic;
- int dirty;
+ unsigned int dirty;
};
struct state_backend {
int (*load)(struct state_backend *backend, struct state *state);
int (*save)(struct state_backend *backend, struct state *state);
const char *name;
+ const char *of_path;
const char *path;
};
@@ -105,12 +106,20 @@ struct state_variable {
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 *);
+ int (*export)(struct state_variable *, struct device_node *,
+ enum state_convert);
int (*import)(struct state_variable *, const struct device_node *);
struct state_variable *(*create)(struct state *state,
const char *name, struct device_node *);
@@ -160,15 +169,15 @@ static inline struct state_uint32 *to_state_uint32(struct state_variable *s)
}
static int state_uint32_export(struct state_variable *var,
- struct device_node *node)
+ struct device_node *node, enum state_convert conv)
{
struct state_uint32 *su32 = to_state_uint32(var);
int ret;
- if (su32->value_default) {
+ if (su32->value_default || conv == STATE_CONVERT_FIXUP) {
ret = of_property_write_u32(node, "default",
su32->value_default);
- if (ret)
+ if (ret || conv == STATE_CONVERT_FIXUP)
return ret;
}
@@ -196,12 +205,13 @@ static struct state_variable *state_uint32_create(struct state *state,
su32 = xzalloc(sizeof(*su32));
param = dev_add_param_int(&state->dev, name, state_set_dirty,
- NULL, (int *)&su32->value, "%d", state);
+ NULL, &su32->value, "%u", 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;
@@ -226,16 +236,16 @@ static inline struct state_enum32 *to_state_enum32(struct state_variable *s)
}
static int state_enum32_export(struct state_variable *var,
- struct device_node *node)
+ 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) {
+ if (enum32->value_default || conv == STATE_CONVERT_FIXUP) {
ret = of_property_write_u32(node, "default",
enum32->value_default);
- if (ret)
+ if (ret || conv == STATE_CONVERT_FIXUP)
return ret;
}
@@ -311,7 +321,7 @@ static struct state_variable *state_enum32_create(struct state *state,
}
enum32->param = dev_add_param_enum(&state->dev, name, state_set_dirty,
- NULL, (int *)&enum32->value, enum32->names, num_names, state);
+ NULL, &enum32->value, enum32->names, num_names, state);
if (IS_ERR(enum32->param)) {
ret = PTR_ERR(enum32->param);
goto out;
@@ -342,14 +352,14 @@ static inline struct state_mac *to_state_mac(struct state_variable *s)
}
static int state_mac_export(struct state_variable *var,
- struct device_node *node)
+ struct device_node *node, enum state_convert conv)
{
struct state_mac *mac = to_state_mac(var);
int ret;
ret = of_property_write_u8_array(node, "default", mac->value_default,
ARRAY_SIZE(mac->value_default));
- if (ret)
+ if (ret || conv == STATE_CONVERT_FIXUP)
return ret;
return of_property_write_u8_array(node, "value", mac->value,
@@ -466,13 +476,6 @@ static struct state *state_new(const char *name)
return state;
}
-static void state_release(struct state *state)
-{
- list_del(&state->list);
- unregister_device(&state->dev);
- free(state);
-}
-
static struct state_variable *state_find_var(struct state *state,
const char *name)
{
@@ -486,12 +489,6 @@ static struct state_variable *state_find_var(struct state *state,
return ERR_PTR(-ENOENT);
}
-enum state_convert {
- STATE_CONVERT_FROM_NODE,
- STATE_CONVERT_FROM_NODE_CREATE,
- STATE_CONVERT_TO_NODE,
-};
-
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)
@@ -516,7 +513,8 @@ static int state_convert_node_variable(struct state *state,
parent_name, parent_name[0] ? "." : "", short_name);
free(short_name);
- if (conv == STATE_CONVERT_TO_NODE)
+ 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) {
@@ -529,6 +527,15 @@ static int state_convert_node_variable(struct state *state,
/* 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) {
@@ -552,13 +559,16 @@ static int state_convert_node_variable(struct state *state,
ret = of_property_read_u32_array(node, "reg", start_size,
ARRAY_SIZE(start_size));
- if (ret)
+ 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,
- "size mismatch: type=%s(size=%u) size=%u\n",
- type_name, sv->size, start_size[1]);
+ "%s: size mismatch: type=%s(size=%u) size=%u\n",
+ name, type_name, sv->size, start_size[1]);
ret = -EOVERFLOW;
goto out_free;
}
@@ -579,7 +589,8 @@ static int state_convert_node_variable(struct state *state,
}
free(name);
- if (conv == STATE_CONVERT_TO_NODE) {
+ 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);
@@ -596,8 +607,9 @@ static int state_convert_node_variable(struct state *state,
}
}
- if (conv == STATE_CONVERT_TO_NODE)
- ret = vtype->export(sv, new_node);
+ if ((conv == STATE_CONVERT_TO_NODE) ||
+ (conv == STATE_CONVERT_FIXUP))
+ ret = vtype->export(sv, new_node, conv);
else
ret = vtype->import(sv, node);
@@ -611,20 +623,21 @@ out:
return ret;
}
-static struct device_node *state_to_node(struct state *state)
+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(NULL, NULL);
+ 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, "",
- STATE_CONVERT_TO_NODE);
+ conv);
if (ret)
goto out;
}
@@ -697,6 +710,101 @@ static int state_from_node(struct state *state, struct device_node *node,
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;
+
+ /* address-cells + size-cells */
+ ret = of_property_write_u32(root, "#address-cells", 1);
+ if (ret)
+ goto out;
+
+ ret = of_property_write_u32(root, "#size-cells", 1);
+ if (ret)
+ goto out;
+
+ /* delete existing node */
+ if (node)
+ of_delete_node(node);
+
+ return 0;
+
+ out:
+ 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
*
@@ -718,27 +826,11 @@ struct state *state_new_from_node(const char *name, struct device_node *node)
return ERR_PTR(ret);
}
- return state;
-}
-
-/*
- * state_new_from_fdt - create a new state instance from a fdt binary blob
- *
- * @name The name of the new state instance
- * @fdt The fdt binary blob describing the new state instance
- */
-struct state *state_new_from_fdt(const char *name, void *fdt)
-{
- struct state *state;
- struct device_node *root;
-
- root = of_unflatten_dtb(fdt);
- if (!root)
- return ERR_PTR(-EINVAL);
-
- state = state_new_from_node(name, root);
-
- of_delete_node(root);
+ ret = of_register_fixup(of_state_fixup, state);
+ if (ret) {
+ state_release(state);
+ return ERR_PTR(ret);
+ }
return state;
}
@@ -903,7 +995,7 @@ static int state_backend_dtb_save(struct state_backend *backend,
struct device_node *root;
struct fdt_header *fdt;
- root = state_to_node(state);
+ root = state_to_node(state, NULL, STATE_CONVERT_TO_NODE);
if (IS_ERR(root))
return PTR_ERR(root);
@@ -946,7 +1038,7 @@ out:
* @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 *path)
+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;
@@ -961,6 +1053,7 @@ int state_backend_dtb_file(struct state *state, const char *path)
backend->load = state_backend_dtb_load;
backend->save = state_backend_dtb_save;
+ backend->of_path = xstrdup(of_path);
backend->path = xstrdup(path);
backend->name = "dtb";
@@ -978,9 +1071,9 @@ int state_backend_dtb_file(struct state *state, const char *path)
*/
struct state_backend_raw {
struct state_backend backend;
- unsigned long size_data; /* The raw data size (without magic and crc) */
- unsigned long size_full;
- unsigned long step; /* The step in bytes between two copies */
+ unsigned long size_data; /* The raw data size (without header) */
+ unsigned long size_full; /* The size header + raw data */
+ 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 */
@@ -1001,14 +1094,18 @@ static int backend_raw_load_one(struct state_backend_raw *backend_raw,
uint32_t crc;
struct state_variable *sv;
struct backend_raw_header header = {};
+ unsigned long max_len;
int ret;
void *buf;
+ 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)
return ret;
@@ -1027,6 +1124,13 @@ static int backend_raw_load_one(struct state_backend_raw *backend_raw,
return -EINVAL;
}
+ 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(header.data_len);
ret = read_full(fd, buf, header.data_len);
@@ -1068,7 +1172,7 @@ static int state_backend_raw_load(struct state_backend *backend,
return fd;
for (i = 0; i < RAW_BACKEND_COPIES; i++) {
- off_t offset = backend_raw->offset + i * backend_raw->step;
+ off_t offset = backend_raw->offset + i * backend_raw->stride;
ret = backend_raw_load_one(backend_raw, state, fd, offset);
if (!ret) {
@@ -1084,11 +1188,11 @@ static int state_backend_raw_load(struct state_backend *backend,
return ret;
}
-static int backend_raw_write_one(struct state_backend_raw *backend_raw,
+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->step;
+ off_t offset = backend_raw->offset + num * backend_raw->stride;
dev_dbg(&state->dev, "%s: 0x%08lx 0x%08zx\n",
__func__, offset, size);
@@ -1098,7 +1202,7 @@ static int backend_raw_write_one(struct state_backend_raw *backend_raw,
return ret;
if (backend_raw->need_erase) {
- ret = erase(fd, backend_raw->step, offset);
+ ret = erase(fd, backend_raw->stride, offset);
if (ret)
return ret;
}
@@ -1115,7 +1219,7 @@ static int state_backend_raw_save(struct state_backend *backend,
{
struct state_backend_raw *backend_raw = container_of(backend,
struct state_backend_raw, backend);
- int ret = 0, fd;
+ int ret = 0, fd, i;
void *buf, *data;
struct backend_raw_header *header;
struct state_variable *sv;
@@ -1138,13 +1242,20 @@ static int state_backend_raw_save(struct state_backend *backend,
if (fd < 0)
goto out_free;
- ret = backend_raw_write_one(backend_raw, state, fd,
- !backend_raw->num_copy_read, buf, backend_raw->size_full);
- if (ret)
- goto out_close;
+ /* save other slots first */
+ for (i = 0; i < RAW_BACKEND_COPIES; i++) {
+ if (i == backend_raw->num_copy_read)
+ continue;
- ret = backend_raw_write_one(backend_raw, state, fd,
- backend_raw->num_copy_read, buf, backend_raw->size_full);
+ 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;
@@ -1159,16 +1270,20 @@ out_free:
#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
-int get_size(const char *path, size_t *out_size)
+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;
- int fd;
ret = stat(path, &s);
if (ret)
@@ -1184,7 +1299,9 @@ int get_size(const char *path, size_t *out_size)
}
/* this works under Linux on block devs */
- if (S_ISBLK(s.st_mode)) {
+ if (BLKGET_GIVES_SIZE(s)) {
+ int fd;
+
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
@@ -1222,20 +1339,20 @@ int get_size(const char *path, size_t *out_size)
* 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 *path, off_t offset,
- size_t size)
+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;
+ size_t path_size = 0;
int ret;
if (state->backend)
return -EBUSY;
- ret = get_size(path, &path_size);
+ ret = state_backend_raw_file_get_size(path, &path_size);
if (ret)
return ret;
@@ -1249,6 +1366,7 @@ int state_backend_raw_file(struct state *state, const char *path, off_t offset,
backend->load = state_backend_raw_load;
backend->save = state_backend_raw_save;
+ backend->of_path = xstrdup(of_path);
backend->path = xstrdup(path);
backend->name = "raw";
@@ -1266,15 +1384,15 @@ int state_backend_raw_file(struct state *state, const char *path, off_t offset,
backend_raw->need_erase = true;
backend_raw->size_full = ALIGN(backend_raw->size_full,
meminfo.writesize);
- backend_raw->step = ALIGN(backend_raw->size_full,
- meminfo.erasesize);
+ backend_raw->stride = ALIGN(backend_raw->size_full,
+ meminfo.erasesize);
dev_dbg(&state->dev, "is a mtd, adjust stepsize to %ld\n",
- backend_raw->step);
+ backend_raw->stride);
} else {
- backend_raw->step = backend_raw->size_full;
+ backend_raw->stride = backend_raw->size_full;
}
- if (backend_raw->size / backend_raw->step < RAW_BACKEND_COPIES) {
+ if (backend_raw->size / backend_raw->stride < RAW_BACKEND_COPIES) {
dev_err(&state->dev, "not enough space for two copies\n");
ret = -ENOSPC;
goto err;
@@ -1511,7 +1629,7 @@ static struct state *state_get(const char *name)
of_property_read_string(node, "backend-type", &backend_type);
if (!strcmp(backend_type, "raw"))
- ret = state_backend_raw_file(state, devpath, offset, size);
+ ret = state_backend_raw_file(state, partition_node->full_name, devpath, offset, size);
else
fprintf(stderr, "invalid backend type: %s\n", backend_type);
diff --git a/src/dt/common.h b/src/dt/common.h
index 9b9c5bb..5645bad 100644
--- a/src/dt/common.h
+++ b/src/dt/common.h
@@ -285,6 +285,16 @@ static inline int unregister_device(struct device_d *d)
return 0;
}
+static int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context)
+{
+ return 0;
+}
+
+static inline int of_unregister_fixup(int (*fixup)(struct device_node *, void *), void *context)
+{
+ return 0;
+}
+
#define cpu_to_be32 __cpu_to_be32
#define be32_to_cpu __be32_to_cpu
diff --git a/src/dt/dt.h b/src/dt/dt.h
index 8a81e2c..b6088c4 100644
--- a/src/dt/dt.h
+++ b/src/dt/dt.h
@@ -61,9 +61,6 @@ void fdt_add_reserve_map(void *fdt);
struct device_d;
struct driver_d;
-int of_fix_tree(struct device_node *);
-int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context);
-
int of_match(struct device_d *dev, struct driver_d *drv);
struct fdt_header *fdt_get_tree(void);