From patchwork Wed Apr 19 04:11:40 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyril Bur X-Patchwork-Id: 752077 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 3w77sP6hqTz9s4s for ; Wed, 19 Apr 2017 14:13:29 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3w77sP2vjMzDq9D for ; Wed, 19 Apr 2017 14:13:29 +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 3w77sJ0qmyzDq8M for ; Wed, 19 Apr 2017 14:13:23 +1000 (AEST) Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v3J48i5K118575 for ; Wed, 19 Apr 2017 00:13:21 -0400 Received: from e23smtp08.au.ibm.com (e23smtp08.au.ibm.com [202.81.31.141]) by mx0b-001b2d01.pphosted.com with ESMTP id 29wqpu2pmr-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Wed, 19 Apr 2017 00:13:21 -0400 Received: from localhost by e23smtp08.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 19 Apr 2017 14:13:18 +1000 Received: from d23relay08.au.ibm.com (202.81.31.227) by e23smtp08.au.ibm.com (202.81.31.205) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 19 Apr 2017 14:13:16 +1000 Received: from d23av02.au.ibm.com (d23av02.au.ibm.com [9.190.235.138]) by d23relay08.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v3J4D8TH38207672 for ; Wed, 19 Apr 2017 14:13:16 +1000 Received: from d23av02.au.ibm.com (localhost [127.0.0.1]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id v3J4CdTa004822 for ; Wed, 19 Apr 2017 14:12:39 +1000 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id v3J4Cdcb004313; Wed, 19 Apr 2017 14:12:39 +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 C837BA0248; Wed, 19 Apr 2017 14:12:19 +1000 (AEST) From: Cyril Bur To: skiboot@lists.ozlabs.org Date: Wed, 19 Apr 2017 14:11:40 +1000 X-Mailer: git-send-email 2.12.2 In-Reply-To: <20170419041143.12042-1-cyril.bur@au1.ibm.com> References: <20170419041143.12042-1-cyril.bur@au1.ibm.com> X-TM-AS-MML: disable x-cbid: 17041904-0048-0000-0000-0000021E05F4 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17041904-0049-0000-0000-000047CB3E66 Message-Id: <20170419041143.12042-3-cyril.bur@au1.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-04-19_02:, , 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-1703280000 definitions=main-1704190039 Subject: [Skiboot] [PATCH v2 2/5] libflash/blocklevel: Add blocklevel_smart_erase() 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" With recent changes to flash drivers in linux not all erase blocks are 4K anymore. While most level of the pflash/gard tool stacks were written to not mind, it turns out there are bugs which means not 4K erase block backing stores aren't handled all that well. Part of the problem is the FFS layout that is 4K aligned and with larger block sizes pflash and the gard tool don't check if their erase commands are erase block aligned - which they are usually not with 64K erase blocks. This patch aims to add common functionality to blocklevel so that (at least) pflash and the gard tool don't need to worry about the problem anymore. Signed-off-by: Cyril Bur --- libflash/blocklevel.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++-- libflash/blocklevel.h | 10 +++- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c index 79ff00f4..36c7b99f 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. @@ -14,16 +14,17 @@ * limitations under the License. */ +#include +#include #include -#include #include #include -#include #include -#include +#include #include +#include "libflash.h" #include "blocklevel.h" #include "ecc.h" @@ -198,6 +199,12 @@ 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); + return FLASH_ERR_ERASE_BOUNDARY; + } + 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 +277,125 @@ 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; + } + + FL_DBG("%s: pos: 0x%" PRIx64 ", len: 0x%" PRIx64 "\n", __func__, pos, len); + + /* Nothing smart needs to be done, pos and len are aligned */ + if ((pos & bl->erase_mask) == 0 && (len & bl->erase_mask) == 0) { + FL_DBG("No smarts needed, calling blocklevel_erase()\n"); + 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 09f0096a..ba42c83d 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);