From patchwork Tue Apr 18 07:29:27 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyril Bur X-Patchwork-Id: 751709 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3w6cJZ38bKz9s3s for ; Tue, 18 Apr 2017 17:31:42 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3w6cJZ2KxfzDqJ7 for ; Tue, 18 Apr 2017 17:31:42 +1000 (AEST) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from mx0a-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3w6cHp66KwzDq9K for ; Tue, 18 Apr 2017 17:31:02 +1000 (AEST) Received: from pps.filterd (m0098417.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v3I7Nqxr086041 for ; Tue, 18 Apr 2017 03:31:00 -0400 Received: from e23smtp07.au.ibm.com (e23smtp07.au.ibm.com [202.81.31.140]) by mx0a-001b2d01.pphosted.com with ESMTP id 29w0kgvx3x-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Tue, 18 Apr 2017 03:30:59 -0400 Received: from localhost by e23smtp07.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 18 Apr 2017 17:30:56 +1000 Received: from d23relay10.au.ibm.com (202.81.31.229) by e23smtp07.au.ibm.com (202.81.31.204) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 18 Apr 2017 17:30:54 +1000 Received: from d23av06.au.ibm.com (d23av06.au.ibm.com [9.190.235.151]) by d23relay10.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v3I7UjO345744168 for ; Tue, 18 Apr 2017 17:30:53 +1000 Received: from d23av06.au.ibm.com (localhost [127.0.0.1]) by d23av06.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id v3I7ULuh027831 for ; Tue, 18 Apr 2017 17:30:21 +1000 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av06.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id v3I7ULCf027120; Tue, 18 Apr 2017 17:30:21 +1000 Received: from camb691.ozlabs.ibm.com (haven.au.ibm.com [9.192.254.114]) (using TLSv1.2 with cipher DHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.au.ibm.com (Postfix) with ESMTPSA id 1FB28A026B; Tue, 18 Apr 2017 17:29:57 +1000 (AEST) From: Cyril Bur To: skiboot@lists.ozlabs.org Date: Tue, 18 Apr 2017 17:29:27 +1000 X-Mailer: git-send-email 2.12.2 In-Reply-To: <20170418072927.692-1-cyril.bur@au1.ibm.com> References: <20170418072927.692-1-cyril.bur@au1.ibm.com> X-TM-AS-MML: disable x-cbid: 17041807-0044-0000-0000-000002429FCB X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17041807-0045-0000-0000-000006C9DD25 Message-Id: <20170418072927.692-5-cyril.bur@au1.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-04-18_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=3 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1702020001 definitions=main-1704180064 Subject: [Skiboot] [PATCH 4/4] libflash/blocklevel: Make read/write be ECC agnostic for callers X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: alistair@popple.id.au, sam@mendozajonas.com MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" The blocklevel abstraction allows for regions of the backing store to be marked as ECC protected so that blocklevel can decode/encode the ECC bytes into the buffer automatically without the caller having to be ECC aware. Unfortunately this abstraction is far from perfect, this is only useful if reads and writes are performed at the start of the ECC region or in some circumstances at an ECC aligned position - which requires the caller be aware of the ECC regions. The problem that has arisen is that the blocklevel abstraction is initialised somewhere but when it is later called the caller is unaware if ECC exists in the region it wants to arbitrarily read and write to. This should not have been a problem since blocklevel knows. Currently misaligned reads will fail ECC checks and misaligned writes will overwrite ECC bytes and the backing store will become corrupted. This patch add the smarts to blocklevel_read() and blocklevel_write() to cope with the problem. Note that ECC can always be bypassed by calling blocklevel_raw_() functions. All this work means that the gard tool can can safely call blocklevel_read() and blocklevel_write() and as long as the blocklevel knows of the presence of ECC then it will deal with all cases. This also commit removes code in the gard tool which compensated for inadequacies no longer present in blocklevel. Signed-off-by: Cyril Bur --- external/gard/gard.c | 32 ++-- libflash/blocklevel.c | 78 ++++++++-- libflash/test/test-blocklevel.c | 316 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 393 insertions(+), 33 deletions(-) diff --git a/external/gard/gard.c b/external/gard/gard.c index 4b26a3b8..fa505fd6 100644 --- a/external/gard/gard.c +++ b/external/gard/gard.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. @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -51,7 +50,6 @@ extern const char version[]; struct gard_ctx { - bool ecc; uint32_t f_size; uint32_t f_pos; @@ -63,15 +61,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) { @@ -243,11 +232,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; @@ -403,7 +392,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; @@ -415,17 +404,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"); @@ -443,7 +432,7 @@ static int reset_partition(struct gard_ctx *ctx) fprintf(stderr, "Couldn't erase the gard partition. Bailing out\n"); return rc; } - for (i = 0; i + sizeof_gard(ctx) < ctx->gard_data_len; i += sizeof_gard(ctx)) { + for (i = 0; i + sizeof(gard) < ctx->gard_data_len; i += sizeof(gard)) { rc = blocklevel_write(ctx->bl, ctx->gard_data_pos + i, &gard, sizeof(gard)); if (rc) { fprintf(stderr, "Couldn't reset the entire gard partition. Bailing out\n"); @@ -667,7 +656,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 { @@ -677,7 +666,6 @@ int main(int argc, char **argv) goto out; } - ctx->ecc = ecc; ctx->gard_data_pos = 0; ctx->gard_data_len = ctx->f_size; } diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c index bafeebc3..efb30261 100644 --- a/libflash/blocklevel.c +++ b/libflash/blocklevel.c @@ -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) @@ -111,7 +116,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; if (!bl || !buf) { errno = EINVAL; @@ -136,6 +141,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; @@ -143,11 +157,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; } @@ -184,7 +202,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; if (!bl || !buf) { errno = EINVAL; @@ -209,6 +227,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; @@ -216,12 +244,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: diff --git a/libflash/test/test-blocklevel.c b/libflash/test/test-blocklevel.c index d227f545..721efa13 100644 --- a/libflash/test/test-blocklevel.c +++ b/libflash/test/test-blocklevel.c @@ -30,11 +30,70 @@ /* Setting this to true prints quite a lot of debug */ bool libflash_debug = 0; -int main(void) +static int bl_test_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len) +{ + if (pos + len > 0x1000) + return FLASH_ERR_PARM_ERROR; + + memcpy(buf, bl->priv + pos, len); + + return 0; +} + +static int bl_test_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len) +{ + if (pos + len > 0x1000) + return FLASH_ERR_PARM_ERROR; + + memcpy(bl->priv + pos, buf, len); + + return 0; +} + +static int bl_test_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len) +{ + if (pos + len > 0x1000) + return FLASH_ERR_PARM_ERROR; + + memset(bl->priv + pos, 0xff, len); + + return 0; +} + +static void reset_buf(uint8_t *buf) +{ + int i; + + for (i = 0; i < 0x1000; i++) { + /* This gives repeating a - z which will be nice to visualise */ + buf[i] = (i % 26) + 'a'; + } +} + +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) +{ struct blocklevel_device bl_mem = { 0 }; struct blocklevel_device *bl = &bl_mem; + uint64_t with_ecc[10], without_ecc[10]; + char *buf, *data; + int i, rc; if (blocklevel_ecc_protect(bl, 0, 0x1000)) { ERR("Failed to blocklevel_ecc_protect!\n"); @@ -211,6 +270,259 @@ int main(void) } } + /* Test ECC reading and writing being 100% transparent to the + * caller */ + buf = malloc(0x1000); + data = malloc(0x100); + if (!buf || !data) { + ERR("Malloc failed\n"); + free(buf); + free(data); + rc = 1; + goto out; + } + memset(bl, 0, sizeof(*bl)); + bl_mem.read = &bl_test_read; + bl_mem.write = &bl_test_write; + bl_mem.erase = &bl_test_erase; + bl_mem.priv = buf; + reset_buf(buf); + + 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; + } - return 0; + 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; }