summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2017-04-07 09:59:38 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2017-04-07 09:59:38 +0200
commit5d834decbe062273393acfcf172fdd29ead6a166 (patch)
tree1eeea5cf915f7779aaa77421327caaa7525e91e0
parent53adf0648c330357103e4a7103c3d7f05c0c4bcf (diff)
parent7b3d284f4bad78d61e9f5d32ec5aa1efc19ce733 (diff)
downloadbarebox-5d834decbe062273393acfcf172fdd29ead6a166.tar.gz
Merge branch 'for-next/state'
-rw-r--r--Documentation/devicetree/bindings/barebox/barebox,state.rst33
-rw-r--r--Documentation/user/state.rst33
-rw-r--r--commands/Kconfig6
-rw-r--r--commands/Makefile1
-rw-r--r--commands/keystore.c100
-rw-r--r--commands/state.c21
-rw-r--r--common/state/Makefile2
-rw-r--r--common/state/backend.c189
-rw-r--r--common/state/backend_bucket_cached.c155
-rw-r--r--common/state/backend_bucket_circular.c96
-rw-r--r--common/state/backend_bucket_direct.c20
-rw-r--r--common/state/backend_format_dtb.c13
-rw-r--r--common/state/backend_format_raw.c127
-rw-r--r--common/state/backend_storage.c456
-rw-r--r--common/state/state.c189
-rw-r--r--common/state/state.h82
-rw-r--r--common/state/state_variables.c5
-rw-r--r--crypto/hmac.c2
-rw-r--r--crypto/keystore.c53
-rw-r--r--crypto/sha1.c2
-rw-r--r--crypto/sha2.c2
-rw-r--r--crypto/sha4.c2
-rw-r--r--drivers/misc/state.c6
-rw-r--r--include/crypto/keystore.h4
-rw-r--r--include/state.h1
25 files changed, 713 insertions, 887 deletions
diff --git a/Documentation/devicetree/bindings/barebox/barebox,state.rst b/Documentation/devicetree/bindings/barebox/barebox,state.rst
index 438cc43..00fb592 100644
--- a/Documentation/devicetree/bindings/barebox/barebox,state.rst
+++ b/Documentation/devicetree/bindings/barebox/barebox,state.rst
@@ -29,7 +29,8 @@ Required properties:
* ``compatible``: should be ``barebox,state``;
* ``magic``: A 32bit number used as a magic to identify the state
-* ``backend``: describes where the data for this state is stored
+* ``backend``: contains a phandle to the device/partition which holds the
+ actual state data.
* ``backend-type``: should be ``raw`` or ``dtb``.
Optional properties:
@@ -39,9 +40,9 @@ Optional properties:
e.g. ``hmac(sha256)``. Only used for ``raw``.
* ``backend-stridesize``: Maximum size per copy of the data. Only important for
non-MTD devices
-* ``backend-storage-type``: Type of the storage. This has two options at the
- moment. For MTD with erasing the correct type is ``circular``. For all other
- devices and files, ``direct`` is the needed type.
+* ``backend-storage-type``: Normally the correct storage type is detected auto-
+ matically. The circular backend supports the option ``noncircular`` to fall
+ back to an old storage format.
Variable nodes
--------------
@@ -77,19 +78,31 @@ Example::
magic = <0x27031977>;
compatible = "barebox,state";
backend-type = "raw";
- backend = &eeprom, "partname:state";
+ backend = &state_part;
foo {
- reg = <0x00 0x4>;
- type = "uint32";
+ reg = <0x00 0x4>;
+ type = "uint32";
default = <0x0>;
};
bar {
- reg = <0x10 0x4>;
- type = "enum32";
+ reg = <0x10 0x4>;
+ type = "enum32";
names = "baz", "qux";
- default = <1>;
+ default = <1>;
+ };
+ };
+
+ &nand_flash {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ state_part: state@10000 {
+ label = "state";
+ reg = <0x10000 0x10000>;
+ };
};
};
diff --git a/Documentation/user/state.rst b/Documentation/user/state.rst
index 5dd5c48..73c4be8 100644
--- a/Documentation/user/state.rst
+++ b/Documentation/user/state.rst
@@ -23,16 +23,35 @@ available, ``raw`` and ``dtb``. Both format the state data differently.
Basically these are serializers. The raw serializer additionally supports a
HMAC algorithm to detect manipulations.
+The data is always stored in a logical unit called ``bucket``. A ``bucket`` has
+its own size depending on some external contraints. These contraints are listed
+in more detail below depending on the used memory type and storage backend. A
+``bucket`` stores exactly one state. A default number of three buckets is used
+to store data redundantely.
+
+Redundancy
+----------
+
+The state framework is safe against powerfailures during write operations. To
+archieve that multiple buckets are stored to disk. When writing all buckets are
+written in order. When reading, the buckets are read in order and the first
+one found that passes CRC tests is used. When all data is read the buckets
+containing invalid or outdated data are written with the data just read. Also
+NAND blocks need cleanup due to excessive bitflips are rewritten in this step.
+With this it is made sure that after successful initialization of a state the
+data on the storage device is consistent and redundant.
+
Storage Backends
----------------
-The serialized data can be stored to different backends which are automatically
-selected depending on the defined backend in the devicetree. Currently two
-implementations exist, ``circular`` and ``direct``. ``circular`` writes the
-data sequentially on the backend storage device. Each save is appended until
-the storage area is full. It then erases the block and starts from offset 0.
-``circular`` is used for MTD devices with erase functionality. ``direct``
-writes the data directly to the file without erasing.
+The serialized data can be stored to different backends. Currently two
+implementations exist, ``circular`` and ``direct``. The state framework automatically
+selects the correct backend depending on the storage medium. Media requiring
+erase operations (NAND, NOR flash) use the ``circular`` backend, others use the ``direct``
+backend. The purpose of the ``circular`` backend is to save erase cycles which may
+wear out the flash blocks. It continuously fills eraseblocks with updated data
+and only when an eraseblock if fully written erases it and starts over writing
+new data to the same eraseblock again.
For all backends multiple copies are written to handle read errors.
diff --git a/commands/Kconfig b/commands/Kconfig
index 43b8ded..ae2dc4b 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1967,6 +1967,12 @@ config CMD_FIRMWARELOAD
Provides the "firmwareload" command which deals with devices which need
firmware to work. It is also used to upload firmware to FPGA devices.
+config CMD_KEYSTORE
+ depends on CRYPTO_KEYSTORE
+ bool
+ prompt "keystore"
+ help
+ keystore provides access to the barebox keystore.
config CMD_LINUX_EXEC
bool "linux exec"
diff --git a/commands/Makefile b/commands/Makefile
index edd713c..37486dc 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_CMD_READLINK) += readlink.o
obj-$(CONFIG_CMD_LET) += let.o
obj-$(CONFIG_CMD_LN) += ln.o
obj-$(CONFIG_CMD_CLK) += clk.o
+obj-$(CONFIG_CMD_KEYSTORE) += keystore.o
obj-$(CONFIG_CMD_TFTP) += tftp.o
obj-$(CONFIG_CMD_FILETYPE) += filetype.o
obj-$(CONFIG_CMD_BAREBOX_UPDATE)+= barebox-update.o
diff --git a/commands/keystore.c b/commands/keystore.c
new file mode 100644
index 0000000..52c4be2
--- /dev/null
+++ b/commands/keystore.c
@@ -0,0 +1,100 @@
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <libfile.h>
+#include <crypto/keystore.h>
+#include <linux/kernel.h>
+#include <fs.h>
+
+static int do_keystore(int argc, char *argv[])
+{
+ int opt;
+ int ret;
+ int do_remove = 0;
+ const char *name;
+ const char *file = NULL;
+ char *secret_str = NULL;
+ void *secret;
+ int s_len;
+
+ while ((opt = getopt(argc, argv, "rs:f:")) > 0) {
+ switch (opt) {
+ case 'r':
+ do_remove = 1;
+ break;
+ case 's':
+ secret_str = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (argc == optind)
+ return COMMAND_ERROR_USAGE;
+
+ if (!do_remove && !file && !secret_str)
+ return COMMAND_ERROR_USAGE;
+
+ if (file && secret_str)
+ return COMMAND_ERROR_USAGE;
+
+ name = argv[optind];
+
+ if (do_remove) {
+ keystore_forget_secret(name);
+ printf("forgotten secret for key %s\n", name);
+ return 0;
+ }
+
+ if (file) {
+ ret = read_file_2(file, &s_len, (void *)&secret_str, FILESIZE_MAX);
+ if (ret) {
+ printf("Cannot open %s: %s\n", file, strerror(-ret));
+ return 1;
+ }
+ } else if (secret_str) {
+ s_len = strlen(secret_str);
+ }
+
+ if (s_len & 1) {
+ printf("invalid secret len. Must be whole bytes\n");
+ return 1;
+ }
+
+ secret = xzalloc(s_len / 2);
+ ret = hex2bin(secret, secret_str, s_len / 2);
+ if (ret) {
+ printf("Cannot convert %s to binary: %s\n", secret_str, strerror(-ret));
+ return 1;
+ }
+
+ ret = keystore_set_secret(name, secret, s_len / 2);
+ if (ret)
+ printf("cannot set secret for key %s: %s\n", name, strerror(-ret));
+ else
+ printf("Added secret for key %s\n", name);
+
+ free(secret);
+
+ return ret ? 1 : 0;
+}
+
+BAREBOX_CMD_HELP_START(keystore)
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-r", "remove a key from the keystore")
+BAREBOX_CMD_HELP_OPT("-s <key>", "set a key in the keystore")
+BAREBOX_CMD_HELP_OPT("-f <keyfile>", "set a key in the keystore, read secret from file")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(keystore)
+ .cmd = do_keystore,
+ BAREBOX_CMD_DESC("manage keys")
+ BAREBOX_CMD_OPTS("[-rsf] <keyname>")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_keystore_help)
+BAREBOX_CMD_END
diff --git a/commands/state.c b/commands/state.c
index 4b51759..c57a906 100644
--- a/commands/state.c
+++ b/commands/state.c
@@ -21,20 +21,27 @@ static int do_state(int argc, char *argv[])
{
int opt, ret = 0;
struct state *state = NULL;
- int do_save = 0;
+ int do_save = 0, do_load = 0;
const char *statename = "state";
+ int no_auth = 0;
- while ((opt = getopt(argc, argv, "s")) > 0) {
+ while ((opt = getopt(argc, argv, "sln")) > 0) {
switch (opt) {
case 's':
do_save = 1;
break;
+ case 'l':
+ do_load = 1;
+ break;
+ case 'n':
+ no_auth = 1;
+ break;
default:
return COMMAND_ERROR_USAGE;
}
}
- if (!do_save) {
+ if (!do_save && !do_load) {
state_info();
return 0;
}
@@ -48,8 +55,14 @@ static int do_state(int argc, char *argv[])
return -ENOENT;
}
- if (do_save)
+ if (do_load) {
+ if (no_auth)
+ ret = state_load_no_auth(state);
+ else
+ ret = state_load(state);
+ } else if (do_save) {
ret = state_save(state);
+ }
return ret;
}
diff --git a/common/state/Makefile b/common/state/Makefile
index 3e0e2c6..fcf9add 100644
--- a/common/state/Makefile
+++ b/common/state/Makefile
@@ -1,9 +1,7 @@
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-$(CONFIG_MTD) += backend_bucket_circular.o
-obj-y += backend_bucket_cached.o
diff --git a/common/state/backend.c b/common/state/backend.c
deleted file mode 100644
index 5235bb0..0000000
--- a/common/state/backend.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@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)
-{
- 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;
-
- backend->of_path = xstrdup(of_path);
-
- return 0;
-
-out_free_format:
- state_format_free(backend->format);
- backend->format = NULL;
-
- return ret;
-}
-
-void state_backend_set_readonly(struct state_backend *backend)
-{
- state_storage_set_readonly(&backend->storage);
-}
-
-void state_backend_free(struct state_backend *backend)
-{
- state_storage_free(&backend->storage);
- if (backend->format)
- state_format_free(backend->format);
- free(backend->of_path);
-}
diff --git a/common/state/backend_bucket_cached.c b/common/state/backend_bucket_cached.c
deleted file mode 100644
index ba0af7f..0000000
--- a/common/state/backend_bucket_cached.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@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 "state.h"
-
-struct state_backend_storage_bucket_cache {
- struct state_backend_storage_bucket bucket;
-
- struct state_backend_storage_bucket *raw;
-
- u8 *data;
- ssize_t data_len;
- bool force_write;
-
- /* For outputs */
- struct device_d *dev;
-};
-
-static inline struct state_backend_storage_bucket_cache
- *get_bucket_cache(struct state_backend_storage_bucket *bucket)
-{
- return container_of(bucket,
- struct state_backend_storage_bucket_cache,
- bucket);
-}
-
-static inline void state_backend_bucket_cache_drop(
- struct state_backend_storage_bucket_cache *cache)
-{
- if (cache->data) {
- free(cache->data);
- cache->data = NULL;
- cache->data_len = 0;
- }
-}
-
-static int state_backend_bucket_cache_fill(
- struct state_backend_storage_bucket_cache *cache)
-{
- int ret;
-
- ret = cache->raw->read(cache->raw, &cache->data, &cache->data_len);
- if (ret == -EUCLEAN) {
- cache->force_write = true;
- ret = 0;
- }
-
- return ret;
-}
-
-static int state_backend_bucket_cache_read(struct state_backend_storage_bucket *bucket,
- uint8_t ** buf_out,
- ssize_t * len_hint)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
- int ret;
-
- if (!cache->data) {
- ret = state_backend_bucket_cache_fill(cache);
- if (ret)
- return ret;
- }
-
- if (cache->data) {
- *buf_out = xmemdup(cache->data, cache->data_len);
- if (!*buf_out)
- return -ENOMEM;
- *len_hint = cache->data_len;
- }
-
- return 0;
-}
-
-static int state_backend_bucket_cache_write(struct state_backend_storage_bucket *bucket,
- const uint8_t * buf, ssize_t len)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
- int ret;
-
- if (!cache->force_write) {
- if (!cache->data)
- ret = state_backend_bucket_cache_fill(cache);
-
- if (cache->data_len == len && !memcmp(cache->data, buf, len))
- return 0;
- }
-
- state_backend_bucket_cache_drop(cache);
-
- ret = cache->raw->write(cache->raw, buf, len);
- if (ret)
- return ret;
-
- cache->data = xmemdup(buf, len);
- cache->data_len = len;
- return 0;
-}
-
-static int state_backend_bucket_cache_init(
- struct state_backend_storage_bucket *bucket)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
-
- if (cache->raw->init) {
- return cache->raw->init(cache->raw);
- }
-
- return 0;
-}
-
-static void state_backend_bucket_cache_free(
- struct state_backend_storage_bucket *bucket)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
-
- state_backend_bucket_cache_drop(cache);
- cache->raw->free(cache->raw);
- free(cache);
-}
-
-int state_backend_bucket_cached_create(struct device_d *dev,
- struct state_backend_storage_bucket *raw,
- struct state_backend_storage_bucket **out)
-{
- struct state_backend_storage_bucket_cache *cache;
-
- cache = xzalloc(sizeof(*cache));
- cache->raw = raw;
- cache->dev = dev;
-
- cache->bucket.free = state_backend_bucket_cache_free;
- cache->bucket.read = state_backend_bucket_cache_read;
- cache->bucket.write = state_backend_bucket_cache_write;
- cache->bucket.init = state_backend_bucket_cache_init;
-
- *out = &cache->bucket;
-
- return 0;
-}
diff --git a/common/state/backend_bucket_circular.c b/common/state/backend_bucket_circular.c
index 0bce900..5279ec9 100644
--- a/common/state/backend_bucket_circular.c
+++ b/common/state/backend_bucket_circular.c
@@ -25,7 +25,21 @@
#include "state.h"
-
+/*
+ * The circular backend bucket code. The circular backend bucket is intended
+ * for mtd devices which need an erase operation.
+ *
+ * Erasing blocks is an operation that should be avoided. On NOR flashes erasing
+ * blocks is very time consuming and on NAND flashes each block only has a limited
+ * number of erase cycles allowed. For this reason we continuously write more data
+ * into each eraseblock and only erase it when no more free space is available.
+ * Don't confuse these multiple writes into a single eraseblock with buckets. A bucket
+ * is the whole eraseblock, we just happen to reuse the same bucket for storing
+ * new data.
+ *
+ * If your device is a mtd device, but does not have eraseblocks, like MRAMs, then
+ * the direct bucket is used instead.
+ */
struct state_backend_storage_bucket_circular {
struct state_backend_storage_bucket bucket;
@@ -47,6 +61,11 @@ struct state_backend_storage_bucket_circular {
struct device_d *dev;
};
+/*
+ * The metadata will be written directly before writesize aligned offsets.
+ * When searching backwards through the pages it allows us to find the
+ * beginning of the data.
+ */
struct __attribute__((__packed__)) state_backend_storage_bucket_circular_meta {
uint32_t magic;
uint32_t written_length;
@@ -65,7 +84,7 @@ static inline struct state_backend_storage_bucket_circular
#ifdef __BAREBOX__
static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ,
- char *buf, int offset, int len)
+ void *buf, int offset, int len)
{
int ret;
@@ -102,7 +121,7 @@ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ
}
static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ,
- const char *buf, int offset, int len)
+ const void *buf, int offset, int len)
{
int ret;
@@ -133,7 +152,7 @@ static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *cir
}
#else
static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ,
- char *buf, int suboffset, int len)
+ void *buf, int suboffset, int len)
{
int ret;
off_t offset = suboffset;
@@ -152,40 +171,19 @@ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ
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) {
- ret = -EUCLEAN;
- dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n",
- circ->eraseblock);
- } else if (stat2.corrected - stat1.corrected > 0) {
- ret = -EUCLEAN;
- dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n",
- circ->eraseblock);
}
return ret;
}
static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ,
- const char *buf, int suboffset, int len)
+ const void *buf, int suboffset, int len)
{
int ret;
off_t offset = suboffset;
@@ -226,14 +224,14 @@ static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *cir
#endif
static int state_backend_bucket_circular_read(struct state_backend_storage_bucket *bucket,
- uint8_t ** buf_out,
- ssize_t * len_hint)
+ void ** buf_out,
+ ssize_t * len_out)
{
struct state_backend_storage_bucket_circular *circ =
get_bucket_circular(bucket);
ssize_t read_len;
off_t offset;
- uint8_t *buf;
+ void *buf;
int ret;
/* Storage is empty */
@@ -282,13 +280,13 @@ static int state_backend_bucket_circular_read(struct state_backend_storage_bucke
}
*buf_out = buf;
- *len_hint = read_len - sizeof(struct state_backend_storage_bucket_circular_meta);
+ *len_out = read_len - sizeof(struct state_backend_storage_bucket_circular_meta);
return ret;
}
static int state_backend_bucket_circular_write(struct state_backend_storage_bucket *bucket,
- const uint8_t * buf,
+ const void * buf,
ssize_t len)
{
struct state_backend_storage_bucket_circular *circ =
@@ -297,7 +295,7 @@ static int state_backend_bucket_circular_write(struct state_backend_storage_buck
struct state_backend_storage_bucket_circular_meta *meta;
uint32_t written_length = ALIGN(len + sizeof(*meta), circ->writesize);
int ret;
- uint8_t *write_buf;
+ void *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",
@@ -379,26 +377,24 @@ static int state_backend_bucket_circular_init(
int sub_offset;
uint32_t written_length = 0;
uint8_t *buf;
+ int ret;
- buf = xmalloc(circ->writesize);
+ buf = xmalloc(circ->max_size);
if (!buf)
return -ENOMEM;
+ ret = state_mtd_peb_read(circ, buf, 0, circ->max_size);
+ if (ret && ret != -EUCLEAN)
+ return ret;
+
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 && ret != -EUCLEAN)
- return ret;
-
- ret = mtd_buf_all_ff(buf, circ->writesize);
+ ret = mtd_buf_all_ff(buf + sub_offset, 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));
+ (buf + sub_offset + circ->writesize - sizeof(*meta));
if (meta->magic != circular_magic)
written_length = 0;
@@ -458,12 +454,14 @@ 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,
- bool lazy_init)
+ struct mtd_info_user *mtd_uinfo)
{
struct state_backend_storage_bucket_circular *circ;
int ret;
+ if (writesize < 8)
+ writesize = 8;
+
circ = xzalloc(sizeof(*circ));
circ->eraseblock = eraseblock;
circ->writesize = writesize;
@@ -495,13 +493,9 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path,
circ->bucket.free = state_backend_bucket_circular_free;
*bucket = &circ->bucket;
- if (!lazy_init) {
- ret = state_backend_bucket_circular_init(*bucket);
- if (ret)
- goto out_free;
- } else {
- circ->bucket.init = state_backend_bucket_circular_init;
- }
+ ret = state_backend_bucket_circular_init(*bucket);
+ if (ret)
+ goto out_free;
return 0;
diff --git a/common/state/backend_bucket_direct.c b/common/state/backend_bucket_direct.c
index 5225433..4465ed0 100644
--- a/common/state/backend_bucket_direct.c
+++ b/common/state/backend_bucket_direct.c
@@ -46,14 +46,14 @@ static inline struct state_backend_storage_bucket_direct
}
static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
- *bucket, uint8_t ** buf_out,
- ssize_t * len_hint)
+ *bucket, void ** buf_out,
+ ssize_t * len_out)
{
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;
+ void *buf;
int ret;
ret = lseek(direct->fd, direct->offset, SEEK_SET);
@@ -69,18 +69,13 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
if (meta.magic == direct_magic) {
read_len = meta.written_length;
} else {
- if (*len_hint)
- read_len = *len_hint;
- else
- read_len = direct->max_size;
+ 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)
@@ -94,13 +89,13 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
}
*buf_out = buf;
- *len_hint = read_len;
+ *len_out = read_len;
return 0;
}
static int state_backend_bucket_direct_write(struct state_backend_storage_bucket
- *bucket, const uint8_t * buf,
+ *bucket, const void * buf,
ssize_t len)
{
struct state_backend_storage_bucket_direct *direct =
@@ -108,7 +103,7 @@ static int state_backend_bucket_direct_write(struct state_backend_storage_bucket
int ret;
struct state_backend_storage_bucket_direct_meta meta;
- if (direct->max_size && len > direct->max_size)
+ if (len > direct->max_size - sizeof(meta))
return -E2BIG;
ret = lseek(direct->fd, direct->offset, SEEK_SET);
@@ -161,7 +156,6 @@ int state_backend_bucket_direct_create(struct device_d *dev, const char *path,
fd = open(path, O_RDWR);
if (fd < 0) {
dev_err(dev, "Failed to open file '%s', %d\n", path, -errno);
- close(fd);
return -errno;
}
diff --git a/common/state/backend_format_dtb.c b/common/state/backend_format_dtb.c
index dc19c88..55fa1fc 100644
--- a/common/state/backend_format_dtb.c
+++ b/common/state/backend_format_dtb.c
@@ -39,13 +39,14 @@ static inline struct state_backend_format_dtb *get_format_dtb(struct
}
static int state_backend_format_dtb_verify(struct state_backend_format *format,
- uint32_t magic, const uint8_t * buf,
- ssize_t len)
+ uint32_t magic, const void * buf,
+ ssize_t *lenp, enum state_flags flags)
{
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);
+ size_t len = *lenp;
if (dtb_len > len) {
dev_err(fdtb->dev, "Error, stored DTB length (%d) longer than read buffer (%d)\n",
@@ -67,18 +68,20 @@ static int state_backend_format_dtb_verify(struct state_backend_format *format,
fdtb->root = root;
+ *lenp = be32_to_cpu(fdt->totalsize);
+
return 0;
}
static int state_backend_format_dtb_unpack(struct state_backend_format *format,
struct state *state,
- const uint8_t * buf, ssize_t len)
+ const void * 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);
+ state_backend_format_dtb_verify(format, 0, buf, &len, 0);
}
ret = state_from_node(state, fdtb->root, 0);
@@ -89,7 +92,7 @@ static int state_backend_format_dtb_unpack(struct state_backend_format *format,
}
static int state_backend_format_dtb_pack(struct state_backend_format *format,
- struct state *state, uint8_t ** buf,
+ struct state *state, void ** buf,
ssize_t * len)
{
struct state_backend_format_dtb *fdtb = get_format_dtb(format);
diff --git a/common/state/backend_format_raw.c b/common/state/backend_format_raw.c
index e028ea6..232856a 100644
--- a/common/state/backend_format_raw.c
+++ b/common/state/backend_format_raw.c
@@ -35,6 +35,10 @@ struct state_backend_format_raw {
/* For outputs */
struct device_d *dev;
+
+ char *secret_name;
+ int needs_secret;
+ char *algo;
};
struct __attribute__((__packed__)) backend_raw_header {
@@ -53,15 +57,53 @@ static inline struct state_backend_format_raw *get_format_raw(
return container_of(format, struct state_backend_format_raw, format);
}
+static int backend_raw_digest_init(struct state_backend_format_raw *raw)
+{
+ const unsigned char *key;
+ int key_len;
+ int ret;
+
+ if (!raw->digest) {
+ raw->digest = digest_alloc(raw->algo);
+ if (!raw->digest) {
+ dev_err(raw->dev, "algo %s not found\n",
+ raw->algo);
+ return -ENODEV;
+ }
+ raw->digest_length = digest_length(raw->digest);
+ }
+
+ ret = keystore_get_secret(raw->secret_name, &key, &key_len);
+ if (ret) {
+ dev_err(raw->dev, "Could not get secret '%s'\n",
+ raw->secret_name);
+ return ret;
+ }
+
+ ret = digest_set_key(raw->digest, key, key_len);
+ if (ret)
+ return ret;
+
+ ret = digest_init(raw->digest);
+ if (ret) {
+ dev_err(raw->dev, "Failed to initialize digest: %s\n",
+ strerror(-ret));
+ return ret;
+ }
+
+ return 0;
+}
+
static int backend_format_raw_verify(struct state_backend_format *format,
- uint32_t magic, const uint8_t * buf,
- ssize_t len)
+ uint32_t magic, const void * buf,
+ ssize_t *lenp, enum state_flags flags)
{
uint32_t crc;
struct backend_raw_header *header;
int d_len = 0;
int ret;
- const uint8_t *data;
+ const void *data;
+ ssize_t len = *lenp;
struct state_backend_format_raw *backend_raw = get_format_raw(format);
ssize_t complete_len;
@@ -85,7 +127,11 @@ static int backend_format_raw_verify(struct state_backend_format *format,
return -EINVAL;
}
- if (backend_raw->digest) {
+ if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTIFICATION)) {
+ ret = backend_raw_digest_init(backend_raw);
+ if (ret)
+ return ret;
+
d_len = digest_length(backend_raw->digest);
}
@@ -105,26 +151,20 @@ static int backend_format_raw_verify(struct state_backend_format *format,
return -EINVAL;
}
- if (backend_raw->digest) {
- struct digest *d = backend_raw->digest;
- const void *hmac = data + header->data_len;
+ *lenp = header->data_len + sizeof(*header);
- ret = digest_init(d);
- if (ret) {
- dev_err(backend_raw->dev, "Failed to initialize digest, %d\n",
- ret);
- return ret;
- }
+ if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTIFICATION)) {
+ const void *hmac = data + header->data_len;
/* hmac over header and data */
- ret = digest_update(d, buf, sizeof(*header) + header->data_len);
+ ret = digest_update(backend_raw->digest, buf, sizeof(*header) + header->data_len);
if (ret) {
dev_err(backend_raw->dev, "Failed to update digest, %d\n",
ret);
return ret;
}
- ret = digest_verify(d, hmac);
+ ret = digest_verify(backend_raw->digest, hmac);
if (ret < 0) {
dev_err(backend_raw->dev, "Failed to verify data, hmac, %d\n",
ret);
@@ -136,12 +176,12 @@ static int backend_format_raw_verify(struct state_backend_format *format,
}
static int backend_format_raw_unpack(struct state_backend_format *format,
- struct state *state, const uint8_t * buf,
+ struct state *state, const void * buf,
ssize_t len)
{
struct state_variable *sv;
const struct backend_raw_header *header;
- const uint8_t *data;
+ const void *data;
struct state_backend_format_raw *backend_raw = get_format_raw(format);
header = (const struct backend_raw_header *)buf;
@@ -160,7 +200,7 @@ static int backend_format_raw_unpack(struct state_backend_format *format,
}
static int backend_format_raw_pack(struct state_backend_format *format,
- struct state *state, uint8_t ** buf_out,
+ struct state *state, void ** buf_out,
ssize_t * len_out)
{
struct state_backend_format_raw *backend_raw = get_format_raw(format);
@@ -192,25 +232,20 @@ static int backend_format_raw_pack(struct state_backend_format *format,
header->header_crc = crc32(0, header,
sizeof(*header) - sizeof(uint32_t));
- if (backend_raw->digest) {
- struct digest *d = backend_raw->digest;
-
- ret = digest_init(d);
- if (ret) {
- dev_err(backend_raw->dev, "Failed to initialize digest for packing, %d\n",
- ret);
- goto out_free;
- }
+ if (backend_raw->algo) {
+ ret = backend_raw_digest_init(backend_raw);
+ if (ret)
+ return ret;
/* hmac over header and data */
- ret = digest_update(d, buf, sizeof(*header) + size_data);
+ ret = digest_update(backend_raw->digest, buf, sizeof(*header) + size_data);
if (ret) {
dev_err(backend_raw->dev, "Failed to update digest for packing, %d\n",
ret);
goto out_free;
}
- ret = digest_final(d, hmac);
+ ret = digest_final(backend_raw->digest, hmac);
if (ret < 0) {
dev_err(backend_raw->dev, "Failed to finish digest for packing, %d\n",
ret);
@@ -240,11 +275,9 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw,
struct device_node *root,
const char *secret_name)
{
- struct digest *digest;
struct property *p;
const char *algo;
- const unsigned char *key;
- int key_len, ret;
+ int ret;
p = of_find_property(root, "algo", NULL);
if (!p) /* does not exist */
@@ -260,30 +293,7 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw,
return -EINVAL;
}
- ret = keystore_get_secret(secret_name, &key, &key_len);
- if (ret == -ENOENT) { /* -ENOENT == does not exist */
- dev_info(raw->dev, "Could not get secret '%s' - probe deferred\n",
- secret_name);
- return -EPROBE_DEFER;
- } else if (ret) {
- return ret;
- }
-
- digest = digest_alloc(algo);
- if (!digest) {
- dev_info(raw->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;
- }
-
- raw->digest = digest;
- raw->digest_length = digest_length(digest);
+ raw->algo = xstrdup(algo);
return 0;
}
@@ -301,15 +311,14 @@ int backend_format_raw_create(struct state_backend_format **format,
raw->dev = dev;
ret = backend_format_raw_init_digest(raw, node, secret_name);
- if (ret == -EPROBE_DEFER) {
- return ret;
- } else if (ret) {
+ if (ret) {
dev_err(raw->dev, "Failed initializing digest for raw format, %d\n",
ret);
free(raw);
return ret;
}
+ raw->secret_name = xstrdup(secret_name);
raw->format.pack = backend_format_raw_pack;
raw->format.unpack = backend_format_raw_unpack;
raw->format.verify = backend_format_raw_verify;
diff --git a/common/state/backend_storage.c b/common/state/backend_storage.c
index 5dc8c50..9ed6ad7 100644
--- a/common/state/backend_storage.c
+++ b/common/state/backend_storage.c
@@ -25,24 +25,29 @@
#include "state.h"
-const unsigned int min_copies_written = 1;
-
-static int bucket_lazy_init(struct state_backend_storage_bucket *bucket)
-{
- int ret;
-
- if (bucket->initialized)
- return 0;
-
- if (bucket->init) {
- ret = bucket->init(bucket);
- if (ret)
- return ret;
- }
- bucket->initialized = true;
+/*
+ * The state framework stores data in so called buckets. A bucket is
+ * exactly one copy of the state we want to store. On flash type media
+ * a bucket corresponds to a single eraseblock. On media which do not
+ * need an erase operation a bucket corresponds to a storage area of
+ * @stridesize bytes.
+ *
+ * For redundancy and to make sure that we have valid data on the storage
+ * device at any time the state framework stores multiple buckets. The strategy
+ * is as follows:
+ *
+ * When loading the state from the storage we iterate over the buckets. We
+ * take the first one we find which has valid crcs. The next step is to
+ * restore consistency between the different buckets. This means rewriting
+ * a bucket when it signalled it needs refresh (i.e. returned -EUCLEAN)
+ * or when contains data different from the bucket we use.
+ *
+ * When the state backend initialized successfully we already restored
+ * consistency which means all buckets contain the same data. This means
+ * when storing a new state we can just write all buckets in order.
+ */
- return 0;
-}
+static const unsigned int min_buckets_written = 1;
/**
* state_storage_write - Writes the given data to the storage
@@ -55,60 +60,64 @@ static int bucket_lazy_init(struct state_backend_storage_bucket *bucket)
* operation on all of them. Writes are always in the same sequence. This
* ensures, that reading in the same sequence will always return the latest
* written valid data first.
- * We try to at least write min_copies_written. If this fails we return with an
+ * We try to at least write min_buckets_written. If this fails we return with an
* error.
*/
int state_storage_write(struct state_backend_storage *storage,
- const uint8_t * buf, ssize_t len)
+ const void * buf, ssize_t len)
{
struct state_backend_storage_bucket *bucket;
int ret;
- int copies_written = 0;
+ int buckets_written = 0;
if (storage->readonly)
return 0;
list_for_each_entry(bucket, &storage->buckets, bucket_list) {
- ret = bucket_lazy_init(bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to init bucket/write state backend bucket, %d\n",
- ret);
- continue;
- }
-
ret = bucket->write(bucket, buf, len);
if (ret) {
dev_warn(storage->dev, "Failed to write state backend bucket, %d\n",
ret);
} else {
- ++copies_written;
+ ++buckets_written;
}
}
- if (copies_written >= min_copies_written)
+ if (buckets_written >= min_buckets_written)
return 0;
dev_err(storage->dev, "Failed to write state to at least %d buckets. Successfully written to %d buckets\n",
- min_copies_written, copies_written);
+ min_buckets_written, buckets_written);
return -EIO;
}
-/**
- * state_storage_restore_consistency - Restore consistency on all storage backends
- * @param storage Storage object
- * @param buf Buffer with valid data that should be on all buckets after this operation
- * @param len Length of the buffer
- * @return 0 on success, -errno otherwise
- *
- * This function brings valid data onto all buckets we have to ensure that all
- * data copies are in sync. In the current implementation we just write the data
- * to all buckets. Bucket implementations that need to keep the number of writes
- * low, can read their own copy first and compare it.
- */
-int state_storage_restore_consistency(struct state_backend_storage *storage,
- const uint8_t * buf, ssize_t len)
+static int bucket_refresh(struct state_backend_storage *storage,
+ struct state_backend_storage_bucket *bucket, void *buf, ssize_t len)
{
- return state_storage_write(storage, buf, len);
+ int ret;
+
+ if (bucket->needs_refresh)
+ goto refresh;
+
+ if (bucket->len != len)
+ goto refresh;
+
+ if (memcmp(bucket->buf, buf, len))
+ goto refresh;
+
+ return 0;
+
+refresh:
+ ret = bucket->write(bucket, buf, len);
+
+ if (ret)
+ dev_warn(storage->dev, "Failed to restore bucket %d@0x%08lx\n",
+ bucket->num, bucket->offset);
+ else
+ dev_info(storage->dev, "restored bucket %d@0x%08lx\n",
+ bucket->num, bucket->offset);
+
+ return ret;
}
/**
@@ -118,7 +127,7 @@ int state_storage_restore_consistency(struct state_backend_storage *storage,
* @param magic state magic value
* @param buf The newly allocated data area will be stored in this pointer
* @param len The resulting length of the buffer
- * @param len_hint Hint of how big the data may be.
+ * @param flags flags controlling how to load state
* @return 0 on success, -errno otherwise. buf and len will be set to valid
* values on success.
*
@@ -129,43 +138,65 @@ int state_storage_restore_consistency(struct state_backend_storage *storage,
*/
int state_storage_read(struct state_backend_storage *storage,
struct state_backend_format *format,
- uint32_t magic, uint8_t ** buf, ssize_t * len,
- ssize_t len_hint)
+ uint32_t magic, void **buf, ssize_t *len,
+ enum state_flags flags)
{
- struct state_backend_storage_bucket *bucket;
+ struct state_backend_storage_bucket *bucket, *bucket_used = NULL;
int ret;
+ /*
+ * Iterate over all buckets. The first valid one we find is the
+ * one we want to use.
+ */
list_for_each_entry(bucket, &storage->buckets, bucket_list) {
- *len = len_hint;
- ret = bucket_lazy_init(bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to init bucket/read state backend bucket, %d\n",
- ret);
+ ret = bucket->read(bucket, &bucket->buf, &bucket->len);
+ if (ret == -EUCLEAN)
+ bucket->needs_refresh = 1;
+ else if (ret)
continue;
- }
- ret = bucket->read(bucket, buf, len);
- if (ret) {
- dev_warn(storage->dev, "Failed to read from state backend bucket, trying next, %d\n",
- ret);
+ /*
+ * Verify the buffer crcs. The buffer length is passed in the len argument,
+ * .verify overwrites it with the length actually used.
+ */
+ ret = format->verify(format, magic, bucket->buf, &bucket->len, flags);
+ if (!ret && !bucket_used)
+ bucket_used = bucket;
+ }
+
+ if (!bucket_used) {
+ dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n");
+
+ return -ENOENT;
+ }
+
+ dev_info(storage->dev, "Using bucket %d@0x%08lx\n", bucket_used->num, bucket_used->offset);
+
+ /*
+ * Restore/refresh all buckets except the one we currently use (in case
+ * it's the only usable bucket at the moment)
+ */
+ list_for_each_entry(bucket, &storage->buckets, bucket_list) {
+ if (bucket == bucket_used)
continue;
- }
- ret = format->verify(format, magic, *buf, *len);
- if (!ret) {
- goto found;
- }
- free(*buf);
- dev_warn(storage->dev, "Failed to verify read copy, trying next bucket, %d\n",
- ret);
+
+ ret = bucket_refresh(storage, bucket, bucket_used->buf, bucket_used->len);
+
+ /* Free buffer from the unused buckets */
+ free(bucket->buf);
+ bucket->buf = NULL;
}
- dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n");
+ /*
+ * Restore/refresh the bucket we currently use
+ */
+ ret = bucket_refresh(storage, bucket_used, bucket_used->buf, bucket_used->len);
- return -ENOENT;
+ *buf = bucket_used->buf;
+ *len = bucket_used->len;
-found:
- /* A failed restore consistency is not a failure of reading the state */
- state_storage_restore_consistency(storage, *buf, *len);
+ /* buffer from the used bucket is passed to the caller, do not free */
+ bucket_used->buf = NULL;
return 0;
}
@@ -187,267 +218,127 @@ static int mtd_get_meminfo(const char *path, struct mtd_info_user *meminfo)
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
-#else
-#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode))
-#define BLKGET_GIVES_SIZE(s) (S_ISBLK(s.st_mode))
-#endif
-#ifndef BLKGETSIZE64
-#define BLKGETSIZE64 -1
-#endif
-
-static int state_backend_storage_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;
-}
-
-/* Number of copies that should be allocated */
-const int desired_copies = 3;
+/* Number of buckets that should be used */
+static const int desired_buckets = 3;
/**
* state_storage_mtd_buckets_init - Creates storage buckets for mtd devices
* @param storage Storage object
* @param meminfo Info about the mtd device
- * @param path Path to the device
- * @param non_circular Use non-circular mode to write data that is compatible with the old on-flash format
- * @param dev_offset Offset to start at in the device.
- * @param max_size Maximum size to use for data. May be 0 for infinite.
+ * @param circular If false, use non-circular mode to write data that is compatible with the old on-flash format
* @return 0 on success, -errno otherwise
*
- * Starting from offset 0 this function tries to create circular buckets on
- * different offsets in the device. Different copies of the data are located in
- * different eraseblocks.
- * For MTD devices we use circular buckets to minimize the number of erases.
- * Circular buckets write new data always in the next free space.
+ * This function iterates over the eraseblocks and creates one bucket on
+ * each eraseblock until we have the number of desired buckets. Bad blocks
+ * will be skipped and the next block will be used.
*/
static int state_storage_mtd_buckets_init(struct state_backend_storage *storage,
- struct mtd_info_user *meminfo,
- const char *path, bool non_circular,
- off_t dev_offset, size_t max_size)
+ struct mtd_info_user *meminfo, bool circular)
{
struct state_backend_storage_bucket *bucket;
- ssize_t end = dev_offset + max_size;
- int nr_copies = 0;
+ ssize_t end = storage->offset + storage->max_size;
+ int n_buckets = 0;
off_t offset;
+ ssize_t writesize;
if (!end || end > meminfo->size)
end = meminfo->size;
- if (!IS_ALIGNED(dev_offset, meminfo->erasesize)) {
+ if (!IS_ALIGNED(storage->offset, meminfo->erasesize)) {
dev_err(storage->dev, "Offset within the device is not aligned to eraseblocks. Offset is %ld, erasesize %zu\n",
- dev_offset, meminfo->erasesize);
+ storage->offset, meminfo->erasesize);
return -EINVAL;
}
- for (offset = dev_offset; offset < end; offset += meminfo->erasesize) {
+ if (circular)
+ writesize = meminfo->writesize;
+ else
+ writesize = meminfo->erasesize;
+
+ for (offset = storage->offset; offset < end; offset += meminfo->erasesize) {
int ret;
- ssize_t writesize = meminfo->writesize;
unsigned int eraseblock = offset / meminfo->erasesize;
- bool lazy_init = true;
-
- if (non_circular)
- writesize = meminfo->erasesize;
- ret = state_backend_bucket_circular_create(storage->dev, path,
+ ret = state_backend_bucket_circular_create(storage->dev, storage->path,
&bucket,
eraseblock,
writesize,
- meminfo,
- lazy_init);
- if (ret) {
- dev_warn(storage->dev, "Failed to create bucket at '%s' eraseblock %u\n",
- path, eraseblock);
+ meminfo);
+ if (ret)
continue;
- }
- ret = state_backend_bucket_cached_create(storage->dev, bucket,
- &bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n",
- ret);
- }
+ bucket->offset = offset;
+ bucket->num = n_buckets;
list_add_tail(&bucket->bucket_list, &storage->buckets);
- ++nr_copies;
- if (nr_copies >= desired_copies)
+ ++n_buckets;
+ if (n_buckets >= desired_buckets)
return 0;
}
- if (!nr_copies) {
+ if (!n_buckets) {
dev_err(storage->dev, "Failed to initialize any state storage bucket\n");
return -EIO;
}
dev_warn(storage->dev, "Failed to initialize desired amount of buckets, only %d of %d succeeded\n",
- nr_copies, desired_copies);
+ n_buckets, desired_buckets);
return 0;
}
-static int state_storage_file_create(struct device_d *dev, const char *path,
- size_t fd_size)
-{
- int fd;
- uint8_t *buf;
- int ret;
-
- fd = open(path, O_RDWR | O_CREAT, 0600);
- if (fd < 0) {
- dev_err(dev, "Failed to open/create file '%s', %d\n", path,
- -errno);
- return -errno;
- }
-
- buf = xzalloc(fd_size);
- if (!buf) {
- ret = -ENOMEM;
- goto out_close;
- }
-
- ret = write_full(fd, buf, fd_size);
- if (ret < 0) {
- dev_err(dev, "Failed to initialize empty file '%s', %d\n", path,
- ret);
- goto out_free;
- }
- ret = 0;
-
-out_free:
- free(buf);
-out_close:
- close(fd);
- return ret;
-}
-
/**
* state_storage_file_buckets_init - Create buckets for a conventional file descriptor
* @param storage Storage object
- * @param path Path to file/device
- * @param dev_offset Offset in the device to start writing at.
- * @param max_size Maximum size of the data. May be 0 for infinite.
- * @param stridesize How far apart the different data copies are placed. If
- * stridesize is 0, only one copy can be created.
* @return 0 on success, -errno otherwise
*
- * For blockdevices and other regular files we create direct buckets beginning
- * at offset 0. Direct buckets are simple and write data always to offset 0.
+ * direct buckets are simpler than circular buckets and can be used on blockdevices
+ * and mtd devices that don't need erase (MRAM). Also used for EEPROMs.
*/
-static int state_storage_file_buckets_init(struct state_backend_storage *storage,
- const char *path, off_t dev_offset,
- size_t max_size, uint32_t stridesize)
+static int state_storage_file_buckets_init(struct state_backend_storage *storage)
{
struct state_backend_storage_bucket *bucket;
- size_t fd_size = 0;
- int ret;
+ int ret, n;
off_t offset;
- int nr_copies = 0;
-
- ret = state_backend_storage_get_size(path, &fd_size);
- if (ret) {
- if (ret != -ENOENT) {
- dev_err(storage->dev, "Failed to get the filesize of '%s', %d\n",
- path, ret);
- return ret;
- }
- if (!stridesize) {
- dev_err(storage->dev, "File '%s' does not exist and no information about the needed size. Please specify stridesize\n",
- path);
- return ret;
- }
-
- if (max_size)
- fd_size = min(dev_offset + stridesize * desired_copies,
- dev_offset + max_size);
- else
- fd_size = dev_offset + stridesize * desired_copies;
- dev_info(storage->dev, "File '%s' does not exist, creating file of size %zd\n",
- path, fd_size);
- ret = state_storage_file_create(storage->dev, path, fd_size);
- if (ret) {
- dev_info(storage->dev, "Failed to create file '%s', %d\n",
- path, ret);
- return ret;
- }
- } else if (max_size) {
- fd_size = min(fd_size, (size_t)dev_offset + max_size);
- }
+ int n_buckets = 0;
+ uint32_t stridesize = storage->stridesize;
+ size_t max_size = storage->max_size;
if (!stridesize) {
- dev_warn(storage->dev, "WARNING, no stridesize given although we use a direct file write. Starting in degraded mode\n");
- stridesize = fd_size;
+ dev_err(storage->dev, "stridesize unspecified\n");
+ return -EINVAL;
}
- for (offset = dev_offset; offset < fd_size; offset += stridesize) {
- size_t maxsize = min((size_t)stridesize,
- (size_t)(fd_size - offset));
+ if (max_size && max_size < desired_buckets * stridesize) {
+ dev_err(storage->dev, "device is too small to hold %d copies\n", desired_buckets);
+ return -EINVAL;
+ }
- ret = state_backend_bucket_direct_create(storage->dev, path,
+ for (n = 0; n < desired_buckets; n++) {
+ offset = storage->offset + n * stridesize;
+ ret = state_backend_bucket_direct_create(storage->dev, storage->path,
&bucket, offset,
- maxsize);
+ stridesize);
if (ret) {
dev_warn(storage->dev, "Failed to create direct bucket at '%s' offset %ld\n",
- path, offset);
+ storage->path, offset);
continue;
}
- ret = state_backend_bucket_cached_create(storage->dev, bucket,
- &bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n",
- ret);
- }
+ bucket->offset = offset;
+ bucket->num = n_buckets;
list_add_tail(&bucket->bucket_list, &storage->buckets);
- ++nr_copies;
- if (nr_copies >= desired_copies)
- return 0;
+ ++n_buckets;
}
- if (!nr_copies) {
+ if (!n_buckets) {
dev_err(storage->dev, "Failed to initialize any state direct storage bucket\n");
return -EIO;
}
- dev_warn(storage->dev, "Failed to initialize desired amount of direct buckets, only %d of %d succeeded\n",
- nr_copies, desired_copies);
+
+ if (n_buckets < desired_buckets)
+ dev_warn(storage->dev, "Failed to initialize desired amount of direct buckets, only %d of %d succeeded\n",
+ n_buckets, desired_buckets);
return 0;
}
@@ -455,7 +346,6 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage
/**
* state_storage_init - Init backend storage
- * @param storage Storage object
* @param path Path to the backend storage file
* @param dev_offset Offset in the device to start writing at.
* @param max_size Maximum size of the data. May be 0 for infinite.
@@ -466,37 +356,39 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage
*
* Depending on the filetype, we create mtd buckets or normal file buckets.
*/
-int state_storage_init(struct state_backend_storage *storage,
- struct device_d *dev, const char *path,
+int state_storage_init(struct state *state, const char *path,
off_t offset, size_t max_size, uint32_t stridesize,
const char *storagetype)
{
+ struct state_backend_storage *storage = &state->storage;
int ret = -ENODEV;
struct mtd_info_user meminfo;
INIT_LIST_HEAD(&storage->buckets);
- storage->dev = dev;
+ storage->dev = &state->dev;
storage->name = storagetype;
storage->stridesize = stridesize;
+ storage->offset = offset;
+ storage->max_size = max_size;
+ storage->path = xstrdup(path);
if (IS_ENABLED(CONFIG_MTD))
ret = mtd_get_meminfo(path, &meminfo);
if (!ret && !(meminfo.flags & MTD_NO_ERASE)) {
- bool non_circular = false;
- if (!storagetype) {
- non_circular = true;
- } else if (strcmp(storagetype, "circular")) {
- dev_warn(storage->dev, "Unknown storagetype '%s', falling back to old format circular storage type.\n",
- storagetype);
- non_circular = true;
+ bool circular;
+ if (!storagetype || !strcmp(storagetype, "circular")) {
+ circular = true;
+ } else if (!strcmp(storagetype, "noncircular")) {
+ dev_warn(storage->dev, "using old format circular storage type.\n");
+ circular = false;
+ } else {
+ dev_warn(storage->dev, "unknown storage type '%s'\n", storagetype);
+ return -EINVAL;
}
- return state_storage_mtd_buckets_init(storage, &meminfo, path,
- non_circular, offset,
- max_size);
+ return state_storage_mtd_buckets_init(storage, &meminfo, circular);
} else {
- return state_storage_file_buckets_init(storage, path, offset,
- max_size, stridesize);
+ return state_storage_file_buckets_init(storage);
}
dev_err(storage->dev, "storage init done\n");
@@ -524,4 +416,6 @@ void state_storage_free(struct state_backend_storage *storage)
list_del(&bucket->bucket_list);
bucket->free(bucket);
}
+
+ free(storage->path);
}
diff --git a/common/state/state.c b/common/state/state.c
index 02bb1bb..8369aed 100644
--- a/common/state/state.c
+++ b/common/state/state.c
@@ -34,6 +34,125 @@
/* list of all registered state instances */
static LIST_HEAD(state_list);
+/**
+ * Save the state
+ * @param state
+ * @return
+ */
+int state_save(struct state *state)
+{
+ void *buf;
+ ssize_t len;
+ int ret;
+
+ if (!state->dirty)
+ return 0;
+
+ ret = state->format->pack(state->format, state, &buf, &len);
+ if (ret) {
+ dev_err(&state->dev, "Failed to pack state with backend format %s, %d\n",
+ state->format->name, ret);
+ return ret;
+ }
+
+ ret = state_storage_write(&state->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.
+ */
+static int state_do_load(struct state *state, enum state_flags flags)
+{
+ void *buf;
+ ssize_t len;
+ int ret;
+
+ ret = state_storage_read(&state->storage, state->format,
+ state->magic, &buf, &len, flags);
+ if (ret) {
+ dev_err(&state->dev, "Failed to read state with format %s, %d\n",
+ state->format->name, ret);
+ return ret;
+ }
+
+ ret = state->format->unpack(state->format, state, buf, len);
+ if (ret) {
+ dev_err(&state->dev, "Failed to unpack read data with format %s although verified, %d\n",
+ state->format->name, ret);
+ goto out;
+ }
+
+ state->dirty = 0;
+
+out:
+ free(buf);
+ return ret;
+}
+
+int state_load(struct state *state)
+{
+ return state_do_load(state, 0);
+}
+
+int state_load_no_auth(struct state *state)
+{
+ return state_do_load(state, STATE_FLAG_NO_AUTHENTIFICATION);
+}
+
+static int state_format_init(struct state *state, const char *backend_format,
+ struct device_node *node, const char *state_name)
+{
+ int ret;
+
+ if (!backend_format || !strcmp(backend_format, "raw")) {
+ ret = backend_format_raw_create(&state->format, node,
+ state_name, &state->dev);
+ } else if (!strcmp(backend_format, "dtb")) {
+ ret = backend_format_dtb_create(&state->format, &state->dev);
+ } else {
+ dev_err(&state->dev, "Invalid backend format %s\n",
+ backend_format);
+ return -EINVAL;
+ }
+
+ if (ret && ret != -EPROBE_DEFER)
+ dev_err(&state->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)
+ return;
+
+ if (format->free)
+ format->free(format);
+}
+
+void state_backend_set_readonly(struct state *state)
+{
+ state_storage_set_readonly(&state->storage);
+}
+
static struct state *state_new(const char *name)
{
struct state *state;
@@ -152,7 +271,6 @@ static int state_convert_node_variable(struct state *state,
sv->name = name;
sv->start = start_size[0];
- sv->type = vtype->type;
state_add_var(state, sv);
} else {
sv = state_find_var(state, name);
@@ -328,21 +446,21 @@ static int of_state_fixup(struct device_node *root, void *ctx)
}
/* backend-type */
- if (!state->backend.format) {
+ if (!state->format) {
ret = -ENODEV;
goto out;
}
p = of_new_property(new_node, "backend-type",
- state->backend.format->name,
- strlen(state->backend.format->name) + 1);
+ state->format->name,
+ strlen(state->format->name) + 1);
if (!p) {
ret = -ENOMEM;
goto out;
}
/* backend phandle */
- backend_node = of_find_node_by_path_from(root, state->backend.of_path);
+ backend_node = of_find_node_by_devpath(root, state->backend_path);
if (!backend_node) {
ret = -ENODEV;
goto out;
@@ -353,9 +471,9 @@ static int of_state_fixup(struct device_node *root, void *ctx)
if (ret)
goto out;
- if (!strcmp("raw", state->backend.format->name)) {
+ if (!strcmp("raw", state->format->name)) {
struct digest *digest =
- state_backend_format_raw_get_digest(state->backend.format);
+ state_backend_format_raw_get_digest(state->format);
if (digest) {
p = of_new_property(new_node, "algo",
digest_name(digest),
@@ -367,19 +485,19 @@ static int of_state_fixup(struct device_node *root, void *ctx)
}
}
- if (state->backend.storage.name) {
+ if (state->storage.name) {
p = of_new_property(new_node, "backend-storage-type",
- state->backend.storage.name,
- strlen(state->backend.storage.name) + 1);
+ state->storage.name,
+ strlen(state->storage.name) + 1);
if (!p) {
ret = -ENOMEM;
goto out;
}
}
- if (state->backend.storage.stridesize) {
+ if (state->storage.stridesize) {
ret = of_property_write_u32(new_node, "backend-stridesize",
- state->backend.storage.stridesize);
+ state->storage.stridesize);
if (ret)
goto out;
}
@@ -408,7 +526,9 @@ void state_release(struct state *state)
of_unregister_fixup(of_state_fixup, state);
list_del(&state->list);
unregister_device(&state->dev);
- state_backend_free(&state->backend);
+ state_storage_free(&state->storage);
+ state_format_free(state->format);
+ free(state->backend_path);
free(state->of_path);
free(state);
}
@@ -452,19 +572,16 @@ struct state *state_new_from_node(struct device_node *node, char *path,
}
if (!path) {
- /* guess if of_path is a path, not a phandle */
- if (of_path[0] == '/' && len > 1) {
- ret = of_find_path(node, "backend", &path, 0);
- } else {
- struct device_node *partition_node;
-
- partition_node = of_parse_phandle(node, "backend", 0);
- if (!partition_node)
- goto out_release_state;
-
- of_path = partition_node->full_name;
- ret = of_find_path_by_node(partition_node, &path, 0);
+ struct device_node *partition_node;
+
+ partition_node = of_parse_phandle(node, "backend", 0);
+ if (!partition_node) {
+ dev_err(&state->dev, "Cannot resolve \"backend\" phandle\n");
+ goto out_release_state;
}
+
+ of_path = partition_node->full_name;
+ ret = of_find_path_by_node(partition_node, &path, 0);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&state->dev, "state failed to parse path to backend: %s\n",
@@ -473,6 +590,8 @@ struct state *state_new_from_node(struct device_node *node, char *path,
}
}
+ state->backend_path = xstrdup(path);
+
ret = of_property_read_string(node, "backend-type", &backend_type);
if (ret) {
goto out_release_state;
@@ -490,14 +609,17 @@ struct state *state_new_from_node(struct device_node *node, char *path,
dev_info(&state->dev, "No backend-storage-type found, using default.\n");
}
- ret = state_backend_init(&state->backend, &state->dev, node,
- backend_type, path, alias, of_path, offset,
+ ret = state_format_init(state, backend_type, node, alias);
+ if (ret)
+ goto out_release_state;
+
+ ret = state_storage_init(state, path, offset,
max_size, stridesize, storage_type);
if (ret)
goto out_release_state;
if (readonly)
- state_backend_set_readonly(&state->backend);
+ state_backend_set_readonly(state);
ret = state_from_node(state, node, 1);
if (ret) {
@@ -509,11 +631,6 @@ struct state *state_new_from_node(struct device_node *node, char *path,
goto out_release_state;
}
- ret = state_load(state);
- if (ret) {
- dev_warn(&state->dev, "Failed to load persistent state, continuing with defaults, %d\n", ret);
- }
-
dev_info(&state->dev, "New state registered '%s'\n", alias);
return state;
@@ -572,10 +689,10 @@ void state_info(void)
list_for_each_entry(state, &state_list, list) {
printf("%-20s ", state->name);
- if (state->backend.format)
+ if (state->format)
printf("(backend: %s, path: %s)\n",
- state->backend.format->name,
- state->backend.of_path);
+ state->format->name,
+ state->backend_path);
else
printf("(no backend)\n");
}
diff --git a/common/state/state.h b/common/state/state.h
index bc6917d..81aaec2 100644
--- a/common/state/state.h
+++ b/common/state/state.h
@@ -5,6 +5,10 @@
struct state;
struct mtd_info_user;
+enum state_flags {
+ STATE_FLAG_NO_AUTHENTIFICATION = (1 << 0),
+};
+
/**
* state_backend_storage_bucket - This class describes a single backend storage
* object copy
@@ -20,15 +24,20 @@ struct mtd_info_user;
* @bucket_list A list element struct to attach this bucket to a list
*/
struct state_backend_storage_bucket {
- int (*init) (struct state_backend_storage_bucket * bucket);
int (*write) (struct state_backend_storage_bucket * bucket,
- const uint8_t * buf, ssize_t len);
+ const void * buf, ssize_t len);
int (*read) (struct state_backend_storage_bucket * bucket,
- uint8_t ** buf, ssize_t * len_hint);
+ void ** buf, ssize_t * len_hint);
void (*free) (struct state_backend_storage_bucket * bucket);
- bool initialized;
+ int num;
+ off_t offset;
+
struct list_head bucket_list;
+
+ void *buf;
+ ssize_t len;
+ bool needs_refresh;
};
/**
@@ -48,13 +57,11 @@ struct state_backend_storage_bucket {
*/
struct state_backend_format {
int (*verify) (struct state_backend_format * format, uint32_t magic,
- const uint8_t * buf, ssize_t len);
+ const void * buf, ssize_t *lenp, enum state_flags flags);
int (*pack) (struct state_backend_format * format, struct state * state,
- uint8_t ** buf, ssize_t * len);
+ void ** buf, ssize_t * len);
int (*unpack) (struct state_backend_format * format,
- struct state * state, const uint8_t * buf, ssize_t len);
- ssize_t(*get_packed_len) (struct state_backend_format * format,
- struct state * state);
+ struct state * state, const void * buf, ssize_t len);
void (*free) (struct state_backend_format * format);
const char *name;
};
@@ -63,6 +70,9 @@ struct state_backend_format {
* state_backend_storage - Storage backend of the state.
*
* @buckets List of storage buckets that are available
+ * @stridesize The distance between copies
+ * @offset Offset in the backend device where the data starts
+ * @max_size The maximum size of the data we can use
*/
struct state_backend_storage {
struct list_head buckets;
@@ -73,23 +83,13 @@ struct state_backend_storage {
const char *name;
uint32_t stridesize;
+ off_t offset;
+ size_t max_size;
+ char *path;
bool readonly;
};
-/**
- * state_backend - State Backend object
- *
- * @format Backend format object
- * @storage Backend storage object
- * @of_path Path to the DT node
- */
-struct state_backend {
- struct state_backend_format *format;
- struct state_backend_storage storage;
- char *of_path;
-};
-
struct state {
struct list_head list; /* Entry to enqueue on list of states */
@@ -102,7 +102,9 @@ struct state {
unsigned int dirty;
unsigned int save_on_shutdown;
- struct state_backend backend;
+ struct state_backend_format *format;
+ struct state_backend_storage storage;
+ char *backend_path;
};
enum state_convert {
@@ -112,20 +114,10 @@ enum state_convert {
STATE_CONVERT_FIXUP,
};
-enum state_variable_type {
- STATE_TYPE_INVALID = 0,
- STATE_TYPE_ENUM,
- STATE_TYPE_U8,
- STATE_TYPE_U32,
- STATE_TYPE_MAC,
- STATE_TYPE_STRING,
-};
-
struct state_variable;
/* 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 *,
@@ -139,7 +131,6 @@ struct variable_type {
/* instance of a single variable */
struct state_variable {
struct state *state;
- enum state_variable_type type;
struct list_head list;
const char *name;
unsigned int start;
@@ -199,8 +190,7 @@ int backend_format_raw_create(struct state_backend_format **format,
struct device_d *dev);
int backend_format_dtb_create(struct state_backend_format **format,
struct device_d *dev);
-int state_storage_init(struct state_backend_storage *storage,
- struct device_d *dev, const char *path,
+int state_storage_init(struct state *state, const char *path,
off_t offset, size_t max_size, uint32_t stridesize,
const char *storagetype);
void state_storage_set_readonly(struct state_backend_storage *storage);
@@ -210,34 +200,24 @@ 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,
- bool lazy_init);
+ struct mtd_info_user *mtd_uinfo);
int state_backend_bucket_cached_create(struct device_d *dev,
struct state_backend_storage_bucket *raw,
struct state_backend_storage_bucket **out);
struct state_variable *state_find_var(struct state *state, const char *name);
struct digest *state_backend_format_raw_get_digest(struct state_backend_format
*format);
-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);
-void state_backend_set_readonly(struct state_backend *backend);
-void state_backend_free(struct state_backend *backend);
+void state_backend_set_readonly(struct state *state);
void state_storage_free(struct state_backend_storage *storage);
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 state_storage_write(struct state_backend_storage *storage,
- const uint8_t * buf, ssize_t len);
-int state_storage_restore_consistency(struct state_backend_storage
- *storage, const uint8_t * buf,
- ssize_t len);
+ const void * buf, ssize_t len);
int state_storage_read(struct state_backend_storage *storage,
struct state_backend_format *format,
- uint32_t magic, uint8_t **buf, ssize_t *len,
- ssize_t len_hint);
+ uint32_t magic, void **buf, ssize_t *len,
+ enum state_flags flags);
static inline struct state_uint32 *to_state_uint32(struct state_variable *s)
{
diff --git a/common/state/state_variables.c b/common/state/state_variables.c
index fd072a0..5b8e628 100644
--- a/common/state/state_variables.c
+++ b/common/state/state_variables.c
@@ -439,31 +439,26 @@ static struct state_variable *state_string_create(struct state *state,
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,
diff --git a/crypto/hmac.c b/crypto/hmac.c
index 05b9b50..37b270d 100644
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -196,4 +196,4 @@ static int digest_hmac_initcall(void)
return 0;
}
-crypto_initcall(digest_hmac_initcall);
+coredevice_initcall(digest_hmac_initcall);
diff --git a/crypto/keystore.c b/crypto/keystore.c
index 90b470f..f2b25ca 100644
--- a/crypto/keystore.c
+++ b/crypto/keystore.c
@@ -16,8 +16,8 @@ static LIST_HEAD(keystore_list);
struct keystore_key {
struct list_head list;
- const char *name;
- const u8 *secret;
+ char *name;
+ u8 *secret;
int secret_len;
};
@@ -29,6 +29,17 @@ static int keystore_compare(struct list_head *a, struct list_head *b)
return strcmp(na, nb);
}
+static struct keystore_key *get_key(const char *name)
+{
+ struct keystore_key *key;
+
+ for_each_key(key)
+ if (!strcmp(name, key->name))
+ return key;
+
+ return NULL;
+};
+
/**
* @param[in] name Name of the secret to get
* @param[out] secret Double pointer to memory representing the secret, do _not_ free() after use
@@ -38,19 +49,17 @@ int keystore_get_secret(const char *name, const u8 **secret, int *secret_len)
{
struct keystore_key *key;
- for_each_key(key) {
- if (!strcmp(name, key->name)) {
- if (!secret || !secret_len)
- return 0;
+ if (!secret || !secret_len)
+ return 0;
- *secret = key->secret;
- *secret_len = key->secret_len;
+ key = get_key(name);
+ if (!key)
+ return -ENOENT;
- return 0;
- }
- }
+ *secret = key->secret;
+ *secret_len = key->secret_len;
- return -ENOENT;
+ return 0;
}
/**
@@ -61,11 +70,10 @@ int keystore_get_secret(const char *name, const u8 **secret, int *secret_len)
int keystore_set_secret(const char *name, const u8 *secret, int secret_len)
{
struct keystore_key *key;
- int ret;
/* check if key is already in store */
- ret = keystore_get_secret(name, NULL, NULL);
- if (!ret)
+ key = get_key(name);
+ if (key)
return -EBUSY;
key = xzalloc(sizeof(*key));
@@ -78,3 +86,18 @@ int keystore_set_secret(const char *name, const u8 *secret, int secret_len)
return 0;
}
+
+void keystore_forget_secret(const char *name)
+{
+ struct keystore_key *key;
+
+ key = get_key(name);
+ if (!key)
+ return;
+
+ list_del(&key->list);
+
+ free(key->name);
+ free(key->secret);
+ free(key);
+}
diff --git a/crypto/sha1.c b/crypto/sha1.c
index cbde4d2..f4b2ded 100644
--- a/crypto/sha1.c
+++ b/crypto/sha1.c
@@ -303,4 +303,4 @@ static int sha1_digest_register(void)
{
return digest_algo_register(&m);
}
-device_initcall(sha1_digest_register);
+coredevice_initcall(sha1_digest_register);
diff --git a/crypto/sha2.c b/crypto/sha2.c
index cb0f11c..c62ddb8 100644
--- a/crypto/sha2.c
+++ b/crypto/sha2.c
@@ -372,4 +372,4 @@ static int sha256_digest_register(void)
return digest_algo_register(&m256);
}
-device_initcall(sha256_digest_register);
+coredevice_initcall(sha256_digest_register);
diff --git a/crypto/sha4.c b/crypto/sha4.c
index 4ce37b7..aad8081 100644
--- a/crypto/sha4.c
+++ b/crypto/sha4.c
@@ -292,4 +292,4 @@ static int sha512_digest_register(void)
return digest_algo_register(&m512);
}
-device_initcall(sha512_digest_register);
+coredevice_initcall(sha512_digest_register);
diff --git a/drivers/misc/state.c b/drivers/misc/state.c
index b43aee6..98ed42e 100644
--- a/drivers/misc/state.c
+++ b/drivers/misc/state.c
@@ -26,6 +26,7 @@ static int state_probe(struct device_d *dev)
struct device_node *np = dev->device_node;
struct state *state;
bool readonly = false;
+ int ret;
state = state_new_from_node(np, NULL, 0, 0, readonly);
if (IS_ERR(state)) {
@@ -35,6 +36,11 @@ static int state_probe(struct device_d *dev)
return ret;
}
+ ret = state_load(state);
+ if (ret)
+ dev_warn(dev, "Failed to load persistent state, continuing with defaults, %d\n",
+ ret);
+
return 0;
}
diff --git a/include/crypto/keystore.h b/include/crypto/keystore.h
index 2991585..89d9626 100644
--- a/include/crypto/keystore.h
+++ b/include/crypto/keystore.h
@@ -12,6 +12,7 @@
#ifdef CONFIG_CRYPTO_KEYSTORE
int keystore_get_secret(const char *name, const u8 **secret, int *secret_len);
int keystore_set_secret(const char *name, const u8 *secret, int secret_len);
+void keystore_forget_secret(const char *name);
#else
static inline int keystore_get_secret(const char *name, const u8 **secret, int *secret_len)
{
@@ -21,6 +22,9 @@ static inline int keystore_set_secret(const char *name, const u8 *secret, int se
{
return 0;
}
+static inline void keystore_forget_secret(const char *name)
+{
+}
#endif
#endif
diff --git a/include/state.h b/include/state.h
index bc9a574..63164f9 100644
--- a/include/state.h
+++ b/include/state.h
@@ -18,6 +18,7 @@ struct state *state_by_name(const char *name);
struct state *state_by_node(const struct device_node *node);
int state_get_name(const struct state *state, char const **name);
+int state_load_no_auth(struct state *state);
int state_load(struct state *state);
int state_save(struct state *state);
void state_info(void);