From 55adb3c45620c31f29978f209e2a44a08d34e2da Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 24 Jul 2020 01:23:00 -0400 Subject: [PATCH] ide: cancel pending callbacks on SRST The SRST implementation did not keep up with the rest of IDE; it is possible to perform a weak reset on an IDE device to remove the BSY/DRQ bits, and then issue writes to the control/device registers which can cause chaos with the state machine. Fix that by actually performing a real reset. Reported-by: Alexander Bulekov Fixes: https://bugs.launchpad.net/qemu/+bug/1878253 Fixes: https://bugs.launchpad.net/qemu/+bug/1887303 Fixes: https://bugs.launchpad.net/qemu/+bug/1887309 Signed-off-by: John Snow --- hw/ide/core.c | 58 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 0d745d63a1..0e32abd779 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2241,6 +2241,37 @@ uint32_t ide_status_read(void *opaque, uint32_t addr) return ret; } +static void ide_perform_srst(IDEState *s) +{ + s->status |= BUSY_STAT; + + /* Halt PIO (Via register state); PIO BH remains scheduled. */ + ide_transfer_halt(s); + + /* Cancel DMA -- may drain block device and invoke callbacks */ + ide_cancel_dma_sync(s); + + /* Cancel PIO callback, reset registers/signature, etc */ + ide_reset(s); + + if (s->drive_kind == IDE_CD) { + /* ATAPI drives do not set READY or SEEK */ + s->status = 0x00; + } +} + +static void ide_bus_perform_srst(void *opaque) +{ + IDEBus *bus = opaque; + IDEState *s; + int i; + + for (i = 0; i < 2; i++) { + s = &bus->ifs[i]; + ide_perform_srst(s); + } +} + void ide_ctrl_write(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; @@ -2249,26 +2280,17 @@ void ide_ctrl_write(void *opaque, uint32_t addr, uint32_t val) trace_ide_ctrl_write(addr, val, bus); - /* common for both drives */ - if (!(bus->cmd & IDE_CTRL_RESET) && - (val & IDE_CTRL_RESET)) { - /* reset low to high */ - for(i = 0;i < 2; i++) { + /* Device0 and Device1 each have their own control register, + * but QEMU models it as just one register in the controller. */ + if ((bus->cmd & IDE_CTRL_RESET) && + !(val & IDE_CTRL_RESET)) { + /* SRST triggers on falling edge */ + for (i = 0; i < 2; i++) { s = &bus->ifs[i]; - s->status = BUSY_STAT | SEEK_STAT; - s->error = 0x01; - } - } else if ((bus->cmd & IDE_CTRL_RESET) && - !(val & IDE_CTRL_RESET)) { - /* high to low */ - for(i = 0;i < 2; i++) { - s = &bus->ifs[i]; - if (s->drive_kind == IDE_CD) - s->status = 0x00; /* NOTE: READY is _not_ set */ - else - s->status = READY_STAT | SEEK_STAT; - ide_set_signature(s); + s->status |= BUSY_STAT; } + aio_bh_schedule_oneshot(qemu_get_aio_context(), + ide_bus_perform_srst, bus); } bus->cmd = val;