mtd: cfi: cmdset_0001: Do not allow read/write to suspend erase block.

commit 6510bbc88e3258631831ade49033537081950605 upstream.

Currently it is possible to read and/or write to suspend EB's.
Writing /dev/mtdX or /dev/mtdblockX from several processes may
break the flash state machine.

Signed-off-by: Joakim Tjernlund <joakim.tjernlund@infinera.com>
Cc: <stable@vger.kernel.org>
Reviewed-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Joakim Tjernlund 2018-03-01 14:39:39 +01:00 committed by Greg Kroah-Hartman
parent 13169025cf
commit 869a31dfe4
2 changed files with 12 additions and 5 deletions

View file

@ -825,21 +825,25 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long
(mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
goto sleep; goto sleep;
/* Do not allow suspend iff read/write to EB address */
if ((adr & chip->in_progress_block_mask) ==
chip->in_progress_block_addr)
goto sleep;
/* Erase suspend */ /* Erase suspend */
map_write(map, CMD(0xB0), adr); map_write(map, CMD(0xB0), chip->in_progress_block_addr);
/* If the flash has finished erasing, then 'erase suspend' /* If the flash has finished erasing, then 'erase suspend'
* appears to make some (28F320) flash devices switch to * appears to make some (28F320) flash devices switch to
* 'read' mode. Make sure that we switch to 'read status' * 'read' mode. Make sure that we switch to 'read status'
* mode so we get the right data. --rmk * mode so we get the right data. --rmk
*/ */
map_write(map, CMD(0x70), adr); map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_ERASING; chip->oldstate = FL_ERASING;
chip->state = FL_ERASE_SUSPENDING; chip->state = FL_ERASE_SUSPENDING;
chip->erase_suspended = 1; chip->erase_suspended = 1;
for (;;) { for (;;) {
status = map_read(map, adr); status = map_read(map, chip->in_progress_block_addr);
if (map_word_andequal(map, status, status_OK, status_OK)) if (map_word_andequal(map, status, status_OK, status_OK))
break; break;
@ -1035,8 +1039,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
sending the 0x70 (Read Status) command to an erasing sending the 0x70 (Read Status) command to an erasing
chip and expecting it to be ignored, that's what we chip and expecting it to be ignored, that's what we
do. */ do. */
map_write(map, CMD(0xd0), adr); map_write(map, CMD(0xd0), chip->in_progress_block_addr);
map_write(map, CMD(0x70), adr); map_write(map, CMD(0x70), chip->in_progress_block_addr);
chip->oldstate = FL_READY; chip->oldstate = FL_READY;
chip->state = FL_ERASING; chip->state = FL_ERASING;
break; break;
@ -1927,6 +1931,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0xD0), adr); map_write(map, CMD(0xD0), adr);
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
chip->in_progress_block_mask = ~(len - 1);
ret = INVAL_CACHE_AND_WAIT(map, chip, adr, ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
adr, len, adr, len,

View file

@ -85,6 +85,7 @@ struct flchip {
unsigned int write_suspended:1; unsigned int write_suspended:1;
unsigned int erase_suspended:1; unsigned int erase_suspended:1;
unsigned long in_progress_block_addr; unsigned long in_progress_block_addr;
unsigned long in_progress_block_mask;
struct mutex mutex; struct mutex mutex;
wait_queue_head_t wq; /* Wait on here when we're waiting for the chip wait_queue_head_t wq; /* Wait on here when we're waiting for the chip