@@ -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.
@@ -36,7 +36,6 @@
#include <libflash/libflash.h>
#include <libflash/libffs.h>
#include <libflash/file.h>
-#include <libflash/ecc.h>
#include <libflash/blocklevel.h>
#include <common/arch_flash.h>
@@ -58,7 +57,6 @@ extern const char version[];
#define __unused __attribute__((unused))
struct gard_ctx {
- bool ecc;
uint32_t f_size;
uint32_t f_pos;
@@ -70,15 +68,6 @@ struct gard_ctx {
struct ffs_handle *ffs;
};
-/*
- * Return the size of a struct gard_ctx depending on if the buffer contains
- * ECC bits
- */
-static inline size_t sizeof_gard(struct gard_ctx *ctx)
-{
- return ctx->ecc ? ecc_buffer_size(sizeof(struct gard_record)) : sizeof(struct gard_record);
-}
-
static void show_flash_err(int rc)
{
switch (rc) {
@@ -374,11 +363,11 @@ static int do_iterate(struct gard_ctx *ctx,
struct gard_record gard, null_gard;
memset(&null_gard, UINT_MAX, sizeof(gard));
- for (i = 0; i * sizeof_gard(ctx) < ctx->gard_data_len && rc == 0; i++) {
+ for (i = 0; i * sizeof(gard) < ctx->gard_data_len && rc == 0; i++) {
memset(&gard, 0, sizeof(gard));
- rc = blocklevel_read(ctx->bl, ctx->gard_data_pos +
- (i * sizeof_gard(ctx)), &gard, sizeof(gard));
+ rc = blocklevel_read(ctx->bl, ctx->gard_data_pos + (i * sizeof(gard)),
+ &gard, sizeof(gard));
/* It isn't super clear what constitutes the end, this should do */
if (rc || memcmp(&gard, &null_gard, sizeof(gard)) == 0)
break;
@@ -397,7 +386,7 @@ static int do_iterate(struct gard_ctx *ctx,
*/
static int __gard_next(struct gard_ctx *ctx, int pos, struct gard_record *gard, int *rc)
{
- uint32_t offset = pos * sizeof_gard(ctx);
+ uint32_t offset = pos * sizeof(*gard);
if (offset > ctx->gard_data_len) /* too big */
return -1;
@@ -576,7 +565,7 @@ static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, v
if (pos < largest) {
/* We're not clearing the last record, shift all the records up */
int buf_len = ((largest - pos) * sizeof(struct gard_record));
- int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof_gard(ctx));
+ int buf_pos = ctx->gard_data_pos + ((pos + 1) * sizeof(struct gard_record));
buf = malloc(buf_len);
if (!buf)
return -ENOMEM;
@@ -588,17 +577,17 @@ static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, v
return rc;
}
- rc = blocklevel_smart_write(ctx->bl, buf_pos - sizeof_gard(ctx), buf, buf_len);
+ rc = blocklevel_smart_write(ctx->bl, buf_pos - sizeof(gard), buf, buf_len);
free(buf);
if (rc) {
fprintf(stderr, "Couldn't write to flash at 0x%08x for len 0x%08x\n",
- buf_pos - (int) sizeof_gard(ctx), buf_len);
+ buf_pos - (int) sizeof(struct gard_record), buf_len);
return rc;
}
}
/* Now wipe the last record */
- rc = blocklevel_smart_write(ctx->bl, ctx->gard_data_pos + (largest * sizeof_gard(ctx)),
+ rc = blocklevel_smart_write(ctx->bl, ctx->gard_data_pos + (largest * sizeof(null_gard)),
&null_gard, sizeof(null_gard));
printf("done\n");
@@ -607,27 +596,24 @@ static int do_clear_i(struct gard_ctx *ctx, int pos, struct gard_record *gard, v
static int reset_partition(struct gard_ctx *ctx)
{
- int len, num_entries, rc = 0;
struct gard_record *gard;
+ int rc = 0;
- num_entries = ctx->gard_data_len / sizeof_gard(ctx);
- len = num_entries * sizeof(*gard);
- gard = malloc(len);
+ gard = malloc(ctx->gard_data_len);
if (!gard) {
return FLASH_ERR_MALLOC_FAILED;
}
- memset(gard, 0xFF, len);
+ memset(gard, 0xFF, ctx->gard_data_len);
rc = blocklevel_smart_erase(ctx->bl, ctx->gard_data_pos, ctx->gard_data_len);
if (rc) {
fprintf(stderr, "Couldn't erase the gard partition. Bailing out\n");
goto out;
}
- rc = blocklevel_write(ctx->bl, ctx->gard_data_pos, gard, len);
- if (rc) {
+
+ rc = blocklevel_write(ctx->bl, ctx->gard_data_pos, gard, ctx->gard_data_len);
+ if (rc)
fprintf(stderr, "Couldn't reset the entire gard partition. Bailing out\n");
- goto out;
- }
out:
free(gard);
@@ -698,7 +684,7 @@ static int do_create(struct gard_ctx *ctx, int argc, char **argv)
/* do we have an empty record to write into? */
if (!rc && !is_valid_record(&gard)) {
- int offset = last_pos * sizeof_gard(ctx);
+ int offset = last_pos * sizeof(gard);
memset(&gard, 0xff, sizeof(gard));
@@ -975,7 +961,7 @@ int main(int argc, char **argv)
goto out;
rc = ffs_part_info(ctx->ffs, ctx->gard_part_idx, NULL, &(ctx->gard_data_pos),
- &(ctx->gard_data_len), NULL, &(ctx->ecc));
+ &(ctx->gard_data_len), NULL, NULL);
if (rc)
goto out;
} else {
@@ -985,7 +971,6 @@ int main(int argc, char **argv)
goto out;
}
- ctx->ecc = ecc;
ctx->gard_data_pos = 0;
ctx->gard_data_len = ctx->f_size;
}
@@ -68,6 +68,11 @@ static int ecc_protected(struct blocklevel_device *bl, uint64_t pos, uint64_t le
return 0;
}
+static uint64_t with_ecc_pos(uint64_t ecc_start, uint64_t pos)
+{
+ return pos + ((pos - ecc_start) / (BYTES_PER_ECC));
+}
+
static int reacquire(struct blocklevel_device *bl)
{
if (!bl->keep_alive && bl->reacquire)
@@ -112,7 +117,7 @@ int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint6
{
int rc, ecc_protection;
struct ecc64 *buffer;
- uint64_t ecc_start, ecc_len = ecc_buffer_size(len);
+ uint64_t ecc_pos, ecc_start, ecc_diff, ecc_len;
FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len);
if (!bl || !buf) {
@@ -139,6 +144,15 @@ int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint6
return FLASH_ERR_PARM_ERROR;
}
+ pos = with_ecc_pos(ecc_start, pos);
+
+ ecc_pos = ecc_buffer_align(ecc_start, pos);
+ ecc_diff = pos - ecc_pos;
+ ecc_len = ecc_buffer_size(len + ecc_diff);
+
+ FL_DBG("%s: adjusted_pos: 0x%" PRIx64 ", ecc_pos: 0x%" PRIx64
+ ", ecc_diff: 0x%" PRIx64 ", ecc_len: 0x%" PRIx64 "\n",
+ __func__, pos, ecc_pos, ecc_diff, ecc_len);
buffer = malloc(ecc_len);
if (!buffer) {
errno = ENOMEM;
@@ -146,11 +160,15 @@ int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint6
goto out;
}
- rc = blocklevel_raw_read(bl, pos, buffer, ecc_len);
+ rc = blocklevel_raw_read(bl, ecc_pos, buffer, ecc_len);
if (rc)
goto out;
- if (memcpy_from_ecc(buf, buffer, len)) {
+ /*
+ * Could optimise and simply call memcpy_from_ecc() if ecc_diff
+ * == 0 but _unaligned checks and bascially does that for us
+ */
+ if (memcpy_from_ecc_unaligned(buf, buffer, len, ecc_diff)) {
errno = EBADF;
rc = FLASH_ERR_ECC_INVALID;
}
@@ -188,7 +206,7 @@ int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf
int rc, ecc_protection;
struct ecc64 *buffer;
uint64_t ecc_len = ecc_buffer_size(len);
- uint64_t ecc_start;
+ uint64_t ecc_start, ecc_pos, ecc_diff;
FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len);
if (!bl || !buf) {
@@ -215,6 +233,16 @@ int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf
return FLASH_ERR_PARM_ERROR;
}
+ pos = with_ecc_pos(ecc_start, pos);
+
+ ecc_pos = ecc_buffer_align(ecc_start, pos);
+ ecc_diff = pos - ecc_pos;
+ ecc_len = ecc_buffer_size(len + ecc_diff);
+
+ FL_DBG("%s: adjusted_pos: 0x%" PRIx64 ", ecc_pos: 0x%" PRIx64
+ ", ecc_diff: 0x%" PRIx64 ", ecc_len: 0x%" PRIx64 "\n",
+ __func__, pos, ecc_pos, ecc_diff, ecc_len);
+
buffer = malloc(ecc_len);
if (!buffer) {
errno = ENOMEM;
@@ -222,12 +250,44 @@ int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf
goto out;
}
- if (memcpy_to_ecc(buffer, buf, len)) {
- errno = EBADF;
- rc = FLASH_ERR_ECC_INVALID;
- goto out;
- }
+ if (ecc_diff) {
+ uint64_t start_chunk = ecc_diff;
+ uint64_t end_chunk = BYTES_PER_ECC - ecc_diff;
+ uint64_t end_len = ecc_len - end_chunk;
+
+ /*
+ * Read the start bytes that memcpy_to_ecc_unaligned() will need
+ * to calculate the first ecc byte
+ */
+ rc = blocklevel_raw_read(bl, ecc_pos, buffer, start_chunk);
+ if (rc) {
+ errno = EBADF;
+ rc = FLASH_ERR_ECC_INVALID;
+ }
+
+ /*
+ * Read the end bytes that memcpy_to_ecc_unaligned() will need
+ * to calculate the last ecc byte
+ */
+ rc = blocklevel_raw_read(bl, ecc_pos + end_len, ((char *)buffer) + end_len,
+ end_chunk);
+ if (rc) {
+ errno = EBADF;
+ rc = FLASH_ERR_ECC_INVALID;
+ }
+ if (memcpy_to_ecc_unaligned(buffer, buf, len, ecc_diff)) {
+ errno = EBADF;
+ rc = FLASH_ERR_ECC_INVALID;
+ goto out;
+ }
+ } else {
+ if (memcpy_to_ecc(buffer, buf, len)) {
+ errno = EBADF;
+ rc = FLASH_ERR_ECC_INVALID;
+ goto out;
+ }
+ }
rc = blocklevel_raw_write(bl, pos, buffer, ecc_len);
out:
@@ -71,7 +71,6 @@ static int bl_test_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t le
return 0;
}
-
static void dump_buf(uint8_t *buf, int start, int end, int miss)
{
int i;
@@ -109,12 +108,30 @@ static void reset_buf(uint8_t *buf)
}
}
+static void print_ptr(void *ptr, int len)
+{
+ int i;
+ char *p = ptr;
+
+ printf("0x");
+ for (i = 0; i < len; i++) {
+ putchar(*p);
+ if (i && i % 8 == 0) {
+ putchar('\n');
+ if (len - i)
+ printf("0x");
+ }
+ }
+ putchar('\n');
+}
+
int main(void)
{
- int i, miss;
- char *buf;
struct blocklevel_device bl_mem = { 0 };
struct blocklevel_device *bl = &bl_mem;
+ uint64_t with_ecc[10], without_ecc[10];
+ char *buf = NULL, *data = NULL;
+ int i, rc, miss;
if (blocklevel_ecc_protect(bl, 0, 0x1000)) {
ERR("Failed to blocklevel_ecc_protect!\n");
@@ -291,14 +308,14 @@ int main(void)
}
}
- /*
- * Test blocklevel_smart_erase()
- * Probably safe to zero the blocklevel we've got
- */
+ /* Test ECC reading and writing being 100% transparent to the
+ * caller */
buf = malloc(0x1000);
- if (!buf) {
+ data = malloc(0x100);
+ if (!buf || !data) {
ERR("Malloc failed\n");
- return 1;
+ rc = 1;
+ goto out;
}
memset(bl, 0, sizeof(*bl));
bl_mem.read = &bl_test_read;
@@ -317,7 +334,7 @@ int main(void)
bl_mem.read = &bl_test_bad_read;
if (blocklevel_smart_erase(bl, 0x100, 0x100)) {
ERR("Failed to blocklevel_smart_erase(0x100, 0x100)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x100, 0x200);
if (miss) {
@@ -325,7 +342,7 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
dump_buf(buf, 0x1fc, 0x205, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
bl_mem.read = &bl_test_read;
bl_mem.write = &bl_test_write;
@@ -334,21 +351,21 @@ int main(void)
/* Test 2: Only touch one erase block */
if (blocklevel_smart_erase(bl, 0x20, 0x40)) {
ERR("Failed to blocklevel_smart_erase(0x20, 0x40)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x20, 0x60);
if (miss) {
ERR("Buffer mismatch after blocklevel_smart_erase(0x20, 0x40) at 0x%x\n",
miss == -1 ? 0 : miss);
dump_buf(buf, 0x1c, 0x65, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
reset_buf(buf);
/* Test 3: Start aligned but finish somewhere in it */
if (blocklevel_smart_erase(bl, 0x100, 0x50)) {
ERR("Failed to blocklevel_smart_erase(0x100, 0x50)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x100, 0x150);
if (miss) {
@@ -356,14 +373,14 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
dump_buf(buf, 0x14c, 0x155, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
reset_buf(buf);
/* Test 4: Start somewhere in it, finish aligned */
if (blocklevel_smart_erase(bl, 0x50, 0xb0)) {
ERR("Failed to blocklevel_smart_erase(0x50, 0xb0)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x50, 0x100);
if (miss) {
@@ -371,14 +388,14 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0x4c, 0x55, miss == -1 ? 0 : miss);
dump_buf(buf, 0x100, 0x105, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
reset_buf(buf);
/* Test 5: Cover two erase blocks exactly */
if (blocklevel_smart_erase(bl, 0x100, 0x200)) {
ERR("Failed to blocklevel_smart_erase(0x100, 0x200)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x100, 0x300);
if (miss) {
@@ -386,14 +403,14 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
dump_buf(buf, 0x2fc, 0x305, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
reset_buf(buf);
/* Test 6: Erase 1.5 blocks (start aligned) */
if (blocklevel_smart_erase(bl, 0x100, 0x180)) {
ERR("Failed to blocklevel_smart_erase(0x100, 0x180)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x100, 0x280);
if (miss) {
@@ -401,14 +418,14 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
dump_buf(buf, 0x27c, 0x285, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
reset_buf(buf);
/* Test 7: Erase 1.5 blocks (end aligned) */
if (blocklevel_smart_erase(bl, 0x80, 0x180)) {
ERR("Failed to blocklevel_smart_erase(0x80, 0x180)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x80, 0x200);
if (miss) {
@@ -416,14 +433,14 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0x7c, 0x85, miss == -1 ? 0 : miss);
dump_buf(buf, 0x1fc, 0x205, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
reset_buf(buf);
/* Test 8: Erase a big section, not aligned */
if (blocklevel_smart_erase(bl, 0x120, 0x544)) {
ERR("Failed to blocklevel_smart_erase(0x120, 0x544)\n");
- return 1;
+ goto out;
}
miss = check_buf(buf, 0x120, 0x664);
if (miss) {
@@ -431,10 +448,247 @@ int main(void)
miss == -1 ? 0 : miss);
dump_buf(buf, 0x11c, 0x125, miss == -1 ? 0 : miss);
dump_buf(buf, 0x65f, 0x669, miss == -1 ? 0 : miss);
- return 1;
+ goto out;
}
- free(buf);
+ bl_mem.priv = buf;
+ reset_buf(buf);
- return 0;
+ for (i = 0; i < 0x100; i++)
+ data[i] = i;
+
+ /* This really shouldn't fail */
+ rc = blocklevel_ecc_protect(bl, 0, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_ecc_protect(0, 0x100)\n");
+ goto out;
+ }
+
+ rc = blocklevel_write(bl, 0, data, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0, 0x100)\n");
+ goto out;
+ }
+
+ rc = blocklevel_write(bl, 0x200, data, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0x200, 0x100)\n");
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned.
+ */
+ rc = blocklevel_read(bl, 0x50, with_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x50, 8) with ecc rc=%d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x250, without_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x250, 8) without ecc rc=%d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 8) || memcmp(with_ecc, &data[0x50], 8)) {
+ ERR("ECC read and non-ECC read don't match or are wrong line: %d\n", __LINE__);
+ print_ptr(with_ecc, 8);
+ print_ptr(without_ecc, 8);
+ print_ptr(&data[50], 8);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned.
+ * So 0x4f won't be aligned!
+ */
+ rc = blocklevel_read(bl, 0x4f, with_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x4f, 8) with ecc %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x24f, without_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x24f, 8) without ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 8) || memcmp(with_ecc, &data[0x4f], 8)) {
+ ERR("ECC read and non-ECC read don't match or are wrong line: %d\n", __LINE__);
+ print_ptr(with_ecc, 8);
+ print_ptr(without_ecc, 8);
+ print_ptr(&data[0x4f], 8);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned.
+ */
+ rc = blocklevel_read(bl, 0x50, with_ecc, 16);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x50, 16) with ecc %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x250, without_ecc, 16);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x250, 16) without ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 16)|| memcmp(with_ecc, &data[0x50], 16)) {
+ ERR("(long read )ECC read and non-ECC read don't match or are wrong line: %d\n", __LINE__);
+ print_ptr(with_ecc, 16);
+ print_ptr(without_ecc, 16);
+ print_ptr(&data[0x50], 16);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned. So 4f won't be.
+ */
+ rc = blocklevel_read(bl, 0x4f, with_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x4f, 24) with ecc %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x24f, without_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x24f, 24) without ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 24)|| memcmp(with_ecc, &data[0x4f], 24)) {
+ ERR("(long read )ECC read and non-ECC read don't match or are wrong: %d\n", __LINE__);
+ print_ptr(with_ecc, 24);
+ print_ptr(without_ecc, 24);
+ print_ptr(&data[0x4f], 24);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * Now lets try to write at non ECC aligned positions
+ * Go easy first, 0x50 becomes 0x5a which is ECC byte aligned but
+ * not aligned to the start of the partition
+ */
+
+ rc = blocklevel_write(bl, 0x50, data, 0xb0);
+ if (rc) {
+ ERR("Couldn't blocklevel_write()\n");
+ goto out;
+ }
+ /* Read 8 bytes before to make sure we didn't ruin that */
+ rc = blocklevel_read(bl, 0x48, with_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read() with ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, data + 0x48, 8) || memcmp(with_ecc + 1, data, 16)) {
+ rc = 1;
+ ERR("Couldn't read back what we thought we wrote line: %d\n", __LINE__);
+ print_ptr(with_ecc, 24);
+ print_ptr(&data[0x48], 8);
+ print_ptr(data, 16);
+ goto out;
+ }
+
+ /* Ok lets get tricky */
+ rc = blocklevel_write(bl, 0x31, data, 0xcf);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0x31, 0xcf)\n");
+ goto out;
+ }
+ /* Read 8 bytes before to make sure we didn't ruin that */
+ rc = blocklevel_read(bl, 0x29, with_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x29, 24) with ecc rc=%d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, &data[0x29], 8) || memcmp(with_ecc + 1, data, 16)) {
+ ERR("Couldn't read back what we thought we wrote line: %d\n", __LINE__);
+ print_ptr(with_ecc, 24);
+ print_ptr(&data[0x29], 8);
+ print_ptr(data, 16);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * Rewrite the pattern that we've messed up
+ */
+ rc = blocklevel_write(bl, 0, data, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0, 0x100) to reset\n");
+ goto out;
+ }
+
+ /* Be unalignmed as possible from now on, starting somewhat easy */
+ rc = blocklevel_read(bl, 0, with_ecc, 5);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0, 5)\n");
+ goto out;
+ }
+ if (memcmp(with_ecc, data, 5)) {
+ ERR("blocklevel_read 5, 0) didn't match line: %d\n", __LINE__);
+ print_ptr(with_ecc, 5);
+ print_ptr(data, 5);
+ rc = 1;
+ goto out;
+ }
+
+ /* 39 is neither divisible by 8 or by 9 */
+ rc = blocklevel_read(bl, 39, with_ecc, 5);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(39, 5)\n");
+ goto out;
+ }
+ if (memcmp(with_ecc, &data[39], 5)) {
+ ERR("blocklevel_read(5, 39() didn't match line: %d\n", __LINE__);
+ print_ptr(with_ecc, 5);
+ print_ptr(&data[39], 5);
+ rc = 1;
+ goto out;
+ }
+
+ rc = blocklevel_read(bl, 0xb, &with_ecc, 39);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0xb, 39)\n");
+ goto out;
+ }
+ if (memcmp(with_ecc, &data[0xb], 39)) {
+ ERR("Strange sized and positioned read failed, blocklevel_read(0xb, 39) line: %d\n", __LINE__);
+ print_ptr(with_ecc, 39);
+ print_ptr(&data[0xb], 39);
+ rc = 1;
+ goto out;
+ }
+
+ rc = blocklevel_write(bl, 39, data, 50);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(39, 50)\n");
+ goto out;
+ }
+
+ rc = blocklevel_read(bl, 32, with_ecc, 39);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(32, 39)\n");
+ goto out;
+ }
+
+ if (memcmp(with_ecc, &data[32], 7) || memcmp(((char *)with_ecc) + 7, data, 32)) {
+ ERR("Read back of odd placed/odd sized write failed, blocklevel_read(32, 39) line: %d\n", __LINE__);
+ print_ptr(with_ecc, 39);
+ print_ptr(&data[32], 7);
+ print_ptr(data, 32);
+ rc = 1;
+ goto out;
+ }
+
+out:
+ free(buf);
+ free(data);
+return rc;
}