aboutsummaryrefslogtreecommitdiff
path: root/libflash
diff options
context:
space:
mode:
authorCyril Bur <cyril.bur@au1.ibm.com>2015-09-03 17:47:07 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2015-09-07 14:18:06 +1000
commitba98f282aee64a24d3c949ed34ff66d362fcf4c9 (patch)
tree26377d184974500f206e8fb78b3e1fea0ec759c4 /libflash
parentcc65fb88f8a360c8744ac9459eea2ebd98966244 (diff)
downloadskiboot-ba98f282aee64a24d3c949ed34ff66d362fcf4c9.zip
skiboot-ba98f282aee64a24d3c949ed34ff66d362fcf4c9.tar.gz
skiboot-ba98f282aee64a24d3c949ed34ff66d362fcf4c9.tar.bz2
libflash/blocklevel: Smarten up blocklevel_smart_write()
Blocklevel_smart_write() can write in a smart fashion which avoids the need for the caller to erase the flash before writing, except it never actually does the erase if it needs to. Furthermore, a very simple optimisation can be made where it should detect if it doesn't actually need to clear the flash, but still needs to write. This optimisation is from flash_smart_write(), which, for reasons, should continue to exist at least for now. In the process, this also addresses a bug. blocklevel_smart_write() operates on erase block covered by the write, detects to see if an erase needs to be performed and if so, performs the erase and then performs the write. Currently blocklevel_smart_write() won't detect that a write can span two erase blocks and that it should split this process in two. This patch fixes that. Signed-off-by: Cyril Bur <cyril.bur@au1.ibm.com> Reviewed-by: Samuel Mendoza-Jonas <sam.mj@au1.ibm.com> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'libflash')
-rw-r--r--libflash/blocklevel.c43
1 files changed, 40 insertions, 3 deletions
diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c
index 90bfffb..83823c5 100644
--- a/libflash/blocklevel.c
+++ b/libflash/blocklevel.c
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
+#include <stdbool.h>
#include <errno.h>
#include <string.h>
@@ -161,6 +162,34 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_
return rc;
}
+/*
+ * Compare flash and memory to determine if:
+ * a) Erase must happen before write
+ * b) Flash and memory are identical
+ * c) Flash can simply be written to
+ *
+ * returns -1 for a
+ * returns 0 for b
+ * returns 1 for c
+ */
+static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint32_t len)
+{
+ int i, same = true;
+ const uint8_t *f_buf, *m_buf;
+
+ f_buf = flash_buf;
+ m_buf = mem_buf;
+
+ for (i = 0; i < len; i++) {
+ if (m_buf[i] & ~f_buf[i])
+ return -1;
+ if (same && (m_buf[i] != f_buf[i]))
+ same = false;
+ }
+
+ return same ? 0 : 1;
+}
+
int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len)
{
uint32_t erase_size;
@@ -209,18 +238,26 @@ int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const voi
uint32_t erase_block = pos & ~(erase_size - 1);
uint32_t block_offset = pos & (erase_size - 1);
uint32_t size = erase_size > len ? len : erase_size;
+ int cmp;
+
+ /* Write crosses an erase boundary, shrink the write to the boundary */
+ if (erase_size < block_offset + size) {
+ size = erase_size - block_offset;
+ }
rc = bl->read(bl, erase_block, erase_buf, erase_size);
if (rc)
goto out;
- if (memcmp(erase_buf + block_offset, write_buf, size) != 0) {
+ cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size);
+ if (cmp != 0) {
+ if (cmp == -1)
+ bl->erase(bl, erase_block, erase_size);
memcpy(erase_buf + block_offset, write_buf, size);
- rc = bl->write(bl, erase_block, erase_buf + block_offset, size);
+ rc = bl->write(bl, erase_block, erase_buf, erase_size);
if (rc)
goto out;
}
-
len -= size;
pos += size;
write_buf += size;