[Skiboot] [PATCH 2/7] libflash/blocklevel: Extend the block level to be able to do ecc
Cyril Bur
cyril.bur at au1.ibm.com
Fri Jun 5 14:11:26 AEST 2015
At the moment ECC reads and writes are still being handled by the low level
core of libflash. ECC should be none of libflashes concern, it is primarily
a hardware access backend.
It makes sense for blocklevel to take care of ECC but currently it has no
way of knowing. With some simple modifications (which are rudimentary at
the moment and will need a performance improvement) blocklevel can handle
ECC, and with a little more effort this can be extended to provide read and
write protection in blocklevel.
Signed-off-by: Cyril Bur <cyril.bur at au1.ibm.com>
---
libc/include/errno.h | 2 +
libflash/blocklevel.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++--
libflash/blocklevel.h | 19 +++++++
3 files changed, 167 insertions(+), 4 deletions(-)
diff --git a/libc/include/errno.h b/libc/include/errno.h
index d585934..c2bd987 100644
--- a/libc/include/errno.h
+++ b/libc/include/errno.h
@@ -21,6 +21,7 @@ extern int errno;
#define EPERM 1 /* not permitted */
#define ENOENT 2 /* file or directory not found */
#define EIO 5 /* input/output error */
+#define EBADF 9 /* Bad file number */
#define ENOMEM 12 /* not enough space */
#define EACCES 13 /* permission denied */
#define EFAULT 14 /* bad address */
@@ -30,5 +31,6 @@ extern int errno;
#define EINVAL 22 /* invalid argument */
#define EDOM 33 /* math argument out of domain of func */
#define ERANGE 34 /* math result not representable */
+#define ENOSYS 38 /* Function not implemented */
#endif
diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c
index bc03b9b..c3ca95e 100644
--- a/libflash/blocklevel.c
+++ b/libflash/blocklevel.c
@@ -14,16 +14,76 @@
* limitations under the License.
*/
+#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
+#include <errno.h>
+#include <string.h>
#include <libflash/libflash.h>
+
#include "blocklevel.h"
+#include "ecc.h"
+
+static int ecc_protected(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
+{
+ int i;
+
+ for (i = 0; i < bl->n_ecc_prot; i++) {
+ if ((bl->ecc_prot[i].start >= pos && bl->ecc_prot[i].start < pos + len) ||
+ (bl->ecc_prot[i].start < pos && bl->ecc_prot[i].start + bl->ecc_prot[i].len > pos))
+ return 1;
+ }
+ return 0;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+static int read_protected(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
+{
+ return 0;
+}
+
+static int write_protected(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
+{
+ return 0;
+}
+#pragma GCC diagnostic pop
int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len)
{
if (!bl || !bl->read || !buf)
- return -1;
+ return -EPERM;
+
+ if (read_protected(bl, pos, len))
+ return -EACCES;
+
+ if (ecc_protected(bl, pos, len)) {
+ int rc;
+ struct ecc64 *buffer;
+ uint32_t ecc_len = ecc_buffer_size(len);
+
+ buffer = malloc(ecc_len);
+ if (!buffer)
+ return -ENOMEM;
+
+ /*
+ * Some backends may pass back information with errno, try to
+ * capture that
+ */
+ errno = 0;
+ rc = bl->read(bl, pos, buffer, ecc_len);
+ if (rc) {
+ rc = errno ? errno : rc;
+ goto out;
+ }
+
+ rc = memcpy_from_ecc(buf, buffer, len);
+
+ out:
+ free(buffer);
+ return rc;
+ }
return bl->read(bl, pos, buf, len);
}
@@ -31,7 +91,31 @@ int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint3
int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len)
{
if (!bl || !bl->write || !buf)
- return -1;
+ return -EPERM;
+
+ if (write_protected(bl, pos, len))
+ return -EACCES;
+
+ if (ecc_protected(bl, pos, len)) {
+ int rc;
+ struct ecc64 *buffer;
+ uint32_t ecc_len = ecc_buffer_size(len);
+
+ buffer = malloc(ecc_len);
+ if (!buffer)
+ return -ENOMEM;
+
+ if (memcpy_to_ecc(buffer, buf, len)) {
+ rc = -EBADF;
+ goto out;
+ }
+
+ rc = bl->write(bl, pos, buffer, ecc_len);
+
+ out:
+ free(buffer);
+ return rc;
+ }
return bl->write(bl, pos, buf, len);
}
@@ -39,7 +123,10 @@ int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf
int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
{
if (!bl || !bl->erase)
- return -1;
+ return -EPERM;
+
+ if (write_protected(bl, pos, len))
+ return -EACCES;
/* Programmer may be making a horrible mistake without knowing it */
if (len & bl->erase_mask) {
@@ -57,7 +144,7 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_
int rc;
if (!bl || !bl->get_info)
- return -1;
+ return -EPERM;
rc = bl->get_info(bl, name, total_size, erase_granule);
@@ -68,3 +155,58 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_
return rc;
}
+
+static int insert_bl_prot_range(struct bl_prot_range **ranges, int *n_ranges,
+ struct bl_prot_range range)
+{
+ struct bl_prot_range *new_ranges;
+
+ new_ranges = realloc(*ranges, sizeof(range) * ((*n_ranges) + 1));
+ if (new_ranges) {
+ memcpy(new_ranges + *n_ranges, &range, sizeof(range));
+ *ranges = new_ranges;
+ (*n_ranges)++;
+ }
+
+ return !new_ranges;
+}
+
+int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len)
+{
+ /*
+ * Could implement this at hardware level by having an accessor to the
+ * backend in struct blocklevel_device and as a result do nothing at
+ * this level (although probably not for ecc!)
+ */
+ struct bl_prot_range range = { .start = start, .len = len };
+ return insert_bl_prot_range(&bl->ecc_prot, &bl->n_ecc_prot, range);
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
+int blocklevel_read_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len)
+{
+
+ /*
+ * Could implement this at hardware level by having an accessor to the
+ * backend in struct blocklevel_device and as a result do nothing at
+ * this level
+ */
+
+ /* TODO */
+ return -ENOSYS;
+}
+
+int blocklevel_write_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len)
+{
+ /*
+ * Could implement this at hardware level by having an accessor to the
+ * backend in struct blocklevel_device and as a result do nothing at
+ * this level
+ */
+
+ /* TODO */
+ return -ENOSYS;
+}
+#pragma GCC diagnostic pop
diff --git a/libflash/blocklevel.h b/libflash/blocklevel.h
index a22ecb4..3a20677 100644
--- a/libflash/blocklevel.h
+++ b/libflash/blocklevel.h
@@ -18,6 +18,11 @@
#include <stdint.h>
+struct bl_prot_range {
+ uint32_t start;
+ uint32_t len;
+};
+
/*
* libffs may be used with different backends, all should provide these for
* libflash to get the information it needs
@@ -34,6 +39,15 @@ struct blocklevel_device {
* Keep the erase mask so that blocklevel_erase() can do sanity checking
*/
uint32_t erase_mask;
+
+ struct bl_prot_range *ecc_prot;
+ int n_ecc_prot;
+
+ struct bl_prot_range *read_prot;
+ int n_read_prot;
+
+ struct bl_prot_range *write_prot;
+ int n_write_prot;
};
int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len);
@@ -42,4 +56,9 @@ int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len);
int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size,
uint32_t *erase_granule);
+/* Implemented in software at this level */
+int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len);
+int blocklevel_read_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len);
+int blocklevel_write_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len);
+
#endif /* __LIBFLASH_BLOCKLEVEL_H */
--
1.9.1
More information about the Skiboot
mailing list