aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libflash/blocklevel.c122
-rw-r--r--libflash/blocklevel.h10
2 files changed, 130 insertions, 2 deletions
diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c
index 79ff00f..9802ad0 100644
--- a/libflash/blocklevel.c
+++ b/libflash/blocklevel.c
@@ -1,4 +1,4 @@
-/* Copyright 2013-2015 IBM Corp.
+/* Copyright 2013-2017 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -198,6 +198,11 @@ int blocklevel_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
}
/* Programmer may be making a horrible mistake without knowing it */
+ if (pos & bl->erase_mask) {
+ fprintf(stderr, "blocklevel_erase: pos (0x%"PRIx64") is not erase block (0x%08x) aligned\n",
+ pos, bl->erase_mask + 1);
+ }
+
if (len & bl->erase_mask) {
fprintf(stderr, "blocklevel_erase: len (0x%"PRIx64") is not erase block (0x%08x) aligned\n",
len, bl->erase_mask + 1);
@@ -270,6 +275,121 @@ static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint6
return same ? 0 : 1;
}
+int blocklevel_smart_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
+{
+ uint64_t block_size;
+ void *erase_buf;
+ int rc;
+
+ if (!bl) {
+ errno = EINVAL;
+ return FLASH_ERR_PARM_ERROR;
+ }
+
+ /* Nothing smart needs to be done, pos and len are aligned */
+ if ((pos & bl->erase_mask) == 0 && (len & bl->erase_mask) == 0)
+ return blocklevel_erase(bl, pos, len);
+
+ block_size = bl->erase_mask + 1;
+ erase_buf = malloc(block_size);
+ if (!erase_buf) {
+ errno = ENOMEM;
+ return FLASH_ERR_MALLOC_FAILED;
+ }
+
+ rc = reacquire(bl);
+ if (rc) {
+ free(erase_buf);
+ return rc;
+ }
+
+ if (pos & bl->erase_mask) {
+ /*
+ * base_pos and base_len are the values in the first erase
+ * block that we need to preserve: the region up to pos.
+ */
+ uint64_t base_pos = pos & ~(bl->erase_mask);
+ uint64_t base_len = pos - base_pos;
+
+ /*
+ * Read the entire block in case this is the ONLY block we're
+ * modifying, we may need the end chunk of it later
+ */
+ rc = bl->read(bl, base_pos, erase_buf, block_size);
+ if (rc)
+ goto out;
+
+ rc = bl->erase(bl, base_pos, block_size);
+ if (rc)
+ goto out;
+
+ rc = bl->write(bl, base_pos, erase_buf, base_len);
+ if (rc)
+ goto out;
+
+ /*
+ * The requested erase fits entirely into this erase block and
+ * so we need to write back the chunk at the end of the block
+ */
+ if (base_pos + base_len + len < base_pos + block_size) {
+ rc = bl->write(bl, pos + len, erase_buf + pos + len,
+ block_size - base_len - len);
+ goto out;
+ }
+
+ pos += block_size - base_len;
+ len -= block_size - base_len;
+ }
+
+ /* Now we should be aligned, best to double check */
+ if (pos & bl->erase_mask) {
+ rc = FLASH_ERR_PARM_ERROR;
+ goto out;
+ }
+
+ if (len & ~(bl->erase_mask)) {
+ rc = bl->erase(bl, pos, len & ~(bl->erase_mask));
+ if (rc)
+ goto out;
+
+ pos += len & ~(bl->erase_mask);
+ len -= len & ~(bl->erase_mask);
+ }
+
+ /* Length should be less than a block now */
+ if (len > block_size) {
+ rc = FLASH_ERR_PARM_ERROR;
+ goto out;
+ }
+
+ if (len & bl->erase_mask) {
+ /*
+ * top_pos is the first byte that must be preserved and
+ * top_len is the length from top_pos to the end of the erase
+ * block: the region that must be preserved
+ */
+ uint64_t top_pos = pos + len;
+ uint64_t top_len = block_size - len;
+
+ rc = bl->read(bl, top_pos, erase_buf, top_len);
+ if (rc)
+ goto out;
+
+ rc = bl->erase(bl, pos, block_size);
+ if (rc)
+ goto out;
+
+ rc = bl->write(bl, top_pos, erase_buf, top_len);
+ if (rc)
+ goto out;
+ }
+
+out:
+ free(erase_buf);
+ release(bl);
+ return rc;
+}
+
int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
{
uint32_t erase_size;
diff --git a/libflash/blocklevel.h b/libflash/blocklevel.h
index 09f0096..ba42c83 100644
--- a/libflash/blocklevel.h
+++ b/libflash/blocklevel.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2015 IBM Corp.
+/* Copyright 2013-2017 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,6 +75,14 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint64_
*/
int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
+/*
+ * blocklevel_smart_erase() will handle unaligned erases.
+ * blocklevel_erase() expects a erase_granule aligned buffer and the
+ * erase length to be an exact multiple of erase_granule,
+ * blocklevel_smart_erase() solves this requirement by performing a
+ * read erase write under the hood.
+ */
+int blocklevel_smart_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len);
/* Implemented in software at this level */
int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len);