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 <alxndr@bu.edu>
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 <jsnow@redhat.com>
This commit is contained in:
John Snow 2020-07-24 01:23:00 -04:00
parent 6f52e69f46
commit 55adb3c456
1 changed files with 40 additions and 18 deletions

View File

@ -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;