From nobody Sun Feb 8 19:13:09 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1555538866; cv=none; d=zoho.com; s=zohoarc; b=Ykbc5s3C7xn1RbKRigemJpxUfTW9PznHppAKrrzcG18nWkNaB5lC2Ijyd4NUgcCnriANoMpBhj12S85Dj+DxrJPGJWOdPuPyJLyoUR+dfgUOD9NnGKTc8eb0I2h7seCIOFcShEV3p3r9RHYa1oqoln4xnsOeXOtIv/230uf9610= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1555538866; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=yX5ZwDpSj0Y8X44DGrh8m++0+hR79iAPvF+LYpzo/I8=; b=MpCzoppD9UetQXv55lbh7fxjdCxFm78AjGqjxdNm6u5Gk4pamKNt40uH834S3V3YP9/C1d7Beaf12C4zrcGQVkLwuQJWYTPQ8vT9FTrT4xU5Geqiei8EyDUEdWeODeEEFJVVy9RaReXZHYHBsfdqVBB57GH1xGzIb+wTXqpD0FI= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1555538866342599.4743532580632; Wed, 17 Apr 2019 15:07:46 -0700 (PDT) Received: from localhost ([127.0.0.1]:60356 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hGsiX-0002Vf-7o for importer@patchew.org; Wed, 17 Apr 2019 18:07:45 -0400 Received: from eggs.gnu.org ([209.51.188.92]:38054) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hGsdW-00079v-US for qemu-devel@nongnu.org; Wed, 17 Apr 2019 18:02:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hGsdV-0004db-01 for qemu-devel@nongnu.org; Wed, 17 Apr 2019 18:02:34 -0400 Received: from mail-it1-x144.google.com ([2607:f8b0:4864:20::144]:34451) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hGsdU-0004cy-Q2 for qemu-devel@nongnu.org; Wed, 17 Apr 2019 18:02:32 -0400 Received: by mail-it1-x144.google.com with SMTP id z17so6437762itc.1 for ; Wed, 17 Apr 2019 15:02:32 -0700 (PDT) Received: from worksec.wireless.oberlin.edu (ip-70-93.wireless.oberlin.edu. [132.162.70.93]) by smtp.gmail.com with ESMTPSA id e81sm147874itc.3.2019.04.17.15.02.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 17 Apr 2019 15:02:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oberlin-edu.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yX5ZwDpSj0Y8X44DGrh8m++0+hR79iAPvF+LYpzo/I8=; b=Fru4gYBChAWFY8kHV/oLxFOUQulgRWUWmLHmJHV3zH0NfzZt06+yFRnhntNmD1kXBh QTMmaxvX6VKZLwVZTGQjD16RyPGAzkWOVez8y250R+koTBgWg+N5JtX4941ceC7BxWs9 6TU/+NYoib+IOgaZqQwyKbLUuOK76CiCTPs2bL0A/FEPnZMhe8/RyTFvGIe1v2gc9456 7kcReooqbtzk1Z1JgrTVQcYk8l6KyPYhTaFTNo7GVJwyOOiub8uMe8BtlTlAlBS8r96w Ip4/oNVwRcjoKd7WSDLPZct1ZjukeNFW2A+DgWAKkR7JPETpQPKHHUE1Zj7JMRS1D70q lDaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yX5ZwDpSj0Y8X44DGrh8m++0+hR79iAPvF+LYpzo/I8=; b=bY1MudymbdIiL+XF14Y80T5qzpj8WW/eV49ndQ7+8dOEr4tSYzJHHhqknCCZAh5mme 2PQyhB3vLBb0auighbTZcJXA2dk0DCs+NMbyhhpKQKIobPha+Q/j93PMqUzgqoz2QDaE 4aF7i11iai3sgu1s5eSjwDmh5xc/OiMUgyQNh8OZ4gMRl+nF7fvS0hvT7SpaLIzTRGwa 8L9DiiQ2vga/dumMng4CvM1RHXubkRtvJ++vkymeUB+2ZV1lcJNBN5apbqiZUGiXI2BW D6RvAdoDiuB1F56jJ4KM8cYM1LOxYZ4TY9/dweUjuYaxn72kKHeYCwKoGlFEUgaBCOvw U/gQ== X-Gm-Message-State: APjAAAVRR3UMAlq0RCICz53ve7veGKmQrQOk8Kk1nRGzXfuf9f85QDbu JNqhMnhdZM8taPGFORQROcZwB0by1sRrbCR8 X-Google-Smtp-Source: APXvYqwE02F7xBAR7F/Sg6ZMcsoWwlLsqYvUZh7OQiwodX2DB40HQQjGZH0E13HDGIw+UaZlWmOcaw== X-Received: by 2002:a24:6110:: with SMTP id s16mr742358itc.169.1555538551979; Wed, 17 Apr 2019 15:02:31 -0700 (PDT) From: Stephen Checkoway To: QEMU Developers , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Kevin Wolf , Max Reitz , "open list:Block layer core" , Stephen Checkoway , Markus Armbruster , Laszlo Ersek , Laurent Vivier , Paolo Bonzini Date: Wed, 17 Apr 2019 18:01:45 -0400 Message-Id: <20190417220147.67648-9-stephen.checkoway@oberlin.edu> X-Mailer: git-send-email 2.20.1 (Apple Git-117) In-Reply-To: <20190417220147.67648-1-stephen.checkoway@oberlin.edu> References: <20190417220147.67648-1-stephen.checkoway@oberlin.edu> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:4864:20::144 Subject: [Qemu-devel] [PATCH v3 08/10] block/pflash_cfi02: Implement multi-sector erase X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Thomas Huth Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" After two unlock cycles and a sector erase command, the AMD flash chips start a 50 us erase time out. Any additional sector erase commands add a sector to be erased and restart the 50 us timeout. During the timeout, status bit DQ3 is cleared. After the time out, DQ3 is asserted during erasure. Signed-off-by: Stephen Checkoway Acked-by: Thomas Huth --- hw/block/pflash_cfi02.c | 94 +++++++++++++++++++++++++++++++-------- tests/pflash-cfi02-test.c | 59 ++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 22 deletions(-) diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index cb1160eb35..21ceb0823b 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -30,7 +30,6 @@ * * It does not implement software data protection as found in many real ch= ips * It does not implement erase suspend/resume commands - * It does not implement multiple sectors erase */ =20 #include "qemu/osdep.h" @@ -106,6 +105,7 @@ struct PFlashCFI02 { MemoryRegion orig_mem; int rom_mode; int read_counter; /* used for lazy switch-back to rom mode */ + int sectors_to_erase; char *name; void *storage; }; @@ -136,6 +136,22 @@ static inline void toggle_dq6(PFlashCFI02 *pfl) pfl->status ^=3D pfl->interleave_multiplier * 0x40; } =20 +/* + * Turn on DQ3. + */ +static inline void assert_dq3(PFlashCFI02 *pfl) +{ + pfl->status |=3D pfl->interleave_multiplier * 0x08; +} + +/* + * Turn off DQ3. + */ +static inline void reset_dq3(PFlashCFI02 *pfl) +{ + pfl->status &=3D ~(pfl->interleave_multiplier * 0x08); +} + /* * Set up replicated mappings of the same region. */ @@ -159,11 +175,37 @@ static void pflash_register_memory(PFlashCFI02 *pfl, = int rom_mode) pfl->rom_mode =3D rom_mode; } =20 -static void pflash_timer (void *opaque) +static void pflash_timer(void *opaque) { PFlashCFI02 *pfl =3D opaque; =20 trace_pflash_timer_expired(pfl->cmd); + if (pfl->cmd =3D=3D 0x30) { + /* + * Sector erase. If DQ3 is 0 when the timer expires, then the 50 + * us erase timeout has expired so we need to start the timer for = the + * sector erase algorithm. Otherwise, the erase completed and we s= hould + * go back to read array mode. + */ + if ((pfl->status & 0x08) =3D=3D 0) { + assert_dq3(pfl); + /* + * CFI address 0x21 is "Typical timeout per individual block e= rase + * 2^N ms" + */ + uint64_t timeout =3D ((1ULL << pfl->cfi_table[0x21]) * + pfl->sectors_to_erase) * 1000000; + timer_mod(&pfl->timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout); + DPRINTF("%s: erase timeout fired; erasing %d sectors\n", + __func__, pfl->sectors_to_erase); + return; + } + DPRINTF("%s: sector erase complete\n", __func__); + pfl->sectors_to_erase =3D 0; + reset_dq3(pfl); + } + /* Reset flash */ toggle_dq7(pfl); if (pfl->bypass) { @@ -307,13 +349,30 @@ static void pflash_update(PFlashCFI02 *pfl, int offse= t, int size) } } =20 +static void pflash_sector_erase(PFlashCFI02 *pfl, hwaddr offset) +{ + uint64_t sector_len =3D pflash_sector_len(pfl, offset); + offset &=3D ~(sector_len - 1); + DPRINTF("%s: start sector erase at %0*" PRIx64 "-%0*" PRIx64 "\n", + __func__, pfl->bank_width * 2, offset, + pfl->bank_width * 2, offset + sector_len - 1); + if (!pfl->ro) { + uint8_t *p =3D pfl->storage; + memset(p + offset, 0xFF, sector_len); + pflash_update(pfl, offset, sector_len); + } + set_dq7(pfl, 0x00); + ++pfl->sectors_to_erase; + /* Set (or reset) the 50 us timer for additional erase commands. */ + timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 50000); +} + static void pflash_write(void *opaque, hwaddr offset, uint64_t value, unsigned int width) { PFlashCFI02 *pfl =3D opaque; uint8_t *p; uint8_t cmd; - uint32_t sector_len; =20 cmd =3D value; if (pfl->cmd !=3D 0xA0) { @@ -486,20 +545,7 @@ static void pflash_write(void *opaque, hwaddr offset, = uint64_t value, break; case 0x30: /* Sector erase */ - p =3D pfl->storage; - sector_len =3D pflash_sector_len(pfl, offset); - offset &=3D ~(sector_len - 1); - DPRINTF("%s: start sector erase at %0*" PRIx64 "-%0*" PRIx64 "= \n", - __func__, pfl->bank_width * 2, offset, - pfl->bank_width * 2, offset + sector_len - 1); - if (!pfl->ro) { - memset(p + offset, 0xFF, sector_len); - pflash_update(pfl, offset, sector_len); - } - set_dq7(pfl, 0x00); - /* Let's wait 1/2 second before sector erase is done */ - timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / 2)); + pflash_sector_erase(pfl, offset); break; default: DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd); @@ -513,7 +559,19 @@ static void pflash_write(void *opaque, hwaddr offset, = uint64_t value, /* Ignore writes during chip erase */ return; case 0x30: - /* Ignore writes during sector erase */ + /* + * If DQ3 is 0, additional sector erase commands can be + * written and anything else (other than an erase suspend) res= ets + * the device. + */ + if ((pfl->status & 0x08) =3D=3D 0) { + if (cmd =3D=3D 0x30) { + pflash_sector_erase(pfl, offset); + } else { + goto reset_flash; + } + } + /* Ignore writes during the actual erase. */ return; default: /* Should never happen */ diff --git a/tests/pflash-cfi02-test.c b/tests/pflash-cfi02-test.c index 4039647604..67b6c40ad7 100644 --- a/tests/pflash-cfi02-test.c +++ b/tests/pflash-cfi02-test.c @@ -37,6 +37,7 @@ typedef struct { #define CFI_CMD 0x98 #define UNLOCK0_CMD 0xAA #define UNLOCK1_CMD 0x55 +#define SECOND_UNLOCK_CMD 0x80 #define AUTOSELECT_CMD 0x90 #define RESET_CMD 0xF0 #define PROGRAM_CMD 0xA0 @@ -224,7 +225,7 @@ static void reset(const FlashConfig *c) static void sector_erase(const FlashConfig *c, uint64_t byte_addr) { unlock(c); - flash_cmd(c, UNLOCK0_ADDR, 0x80); + flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); unlock(c); flash_write(c, byte_addr, replicate(c, SECTOR_ERASE_CMD)); } @@ -263,7 +264,7 @@ static void program(const FlashConfig *c, uint64_t byte= _addr, uint16_t data) static void chip_erase(const FlashConfig *c) { unlock(c); - flash_cmd(c, UNLOCK0_ADDR, 0x80); + flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); unlock(c); flash_cmd(c, UNLOCK0_ADDR, CHIP_ERASE_CMD); } @@ -386,6 +387,7 @@ static void test_geometry(const void *opaque) reset(c); const uint64_t dq7 =3D replicate(c, 0x80); const uint64_t dq6 =3D replicate(c, 0x40); + const uint64_t dq3 =3D replicate(c, 0x08); =20 uint64_t byte_addr =3D 0; for (int region =3D 0; region < nb_erase_regions; ++region) { @@ -403,18 +405,29 @@ static void test_geometry(const void *opaque) /* Erase and program sector. */ for (uint32_t i =3D 0; i < nb_sectors; ++i) { sector_erase(c, byte_addr); - /* Read toggle. */ + + /* Check that DQ3 is 0. */ + g_assert_cmpint(flash_read(c, byte_addr) & dq3, =3D=3D, 0); + qtest_clock_step_next(c->qtest); /* Step over the 50 us timeou= t. */ + + /* Check that DQ3 is 1. */ uint64_t status0 =3D flash_read(c, byte_addr); + g_assert_cmpint(status0 & dq3, =3D=3D, dq3); + /* DQ7 is 0 during an erase. */ g_assert_cmpint(status0 & dq7, =3D=3D, 0); uint64_t status1 =3D flash_read(c, byte_addr); + /* DQ6 toggles during an erase. */ g_assert_cmpint(status0 & dq6, =3D=3D, ~status1 & dq6); + /* Wait for erase to complete. */ - qtest_clock_step_next(c->qtest); + wait_for_completion(c, byte_addr); + /* Ensure DQ6 has stopped toggling. */ g_assert_cmpint(flash_read(c, byte_addr), =3D=3D, flash_read(c, byte_addr)); + /* Now the data should be valid. */ g_assert_cmpint(flash_read(c, byte_addr), =3D=3D, bank_mask(c)= ); =20 @@ -477,6 +490,44 @@ static void test_geometry(const void *opaque) g_assert_cmpint(flash_query(c, FLASH_ADDR(0)), =3D=3D, replicate(c, 0x= BF)); reset(c); =20 + /* + * Program a word on each sector, erase one or two sectors per region,= and + * verify that all of those, and only those, are erased. + */ + byte_addr =3D 0; + for (int region =3D 0; region < nb_erase_regions; ++region) { + for (int i =3D 0; i < config->nb_blocs[region]; ++i) { + program(c, byte_addr, 0); + byte_addr +=3D config->sector_len[region]; + } + } + unlock(c); + flash_cmd(c, UNLOCK0_ADDR, SECOND_UNLOCK_CMD); + unlock(c); + byte_addr =3D 0; + const uint64_t erase_cmd =3D replicate(c, SECTOR_ERASE_CMD); + for (int region =3D 0; region < nb_erase_regions; ++region) { + flash_write(c, byte_addr, erase_cmd); + if (c->nb_blocs[region] > 1) { + flash_write(c, byte_addr + c->sector_len[region], erase_cmd); + } + byte_addr +=3D c->sector_len[region] * c->nb_blocs[region]; + } + + qtest_clock_step_next(c->qtest); /* Step over the 50 us timeout. */ + wait_for_completion(c, 0); + byte_addr =3D 0; + for (int region =3D 0; region < nb_erase_regions; ++region) { + for (int i =3D 0; i < config->nb_blocs[region]; ++i) { + if (i < 2) { + g_assert_cmpint(flash_read(c, byte_addr), =3D=3D, bank_mas= k(c)); + } else { + g_assert_cmpint(flash_read(c, byte_addr), =3D=3D, 0); + } + byte_addr +=3D config->sector_len[region]; + } + } + qtest_quit(qtest); } =20 --=20 2.20.1 (Apple Git-117)