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:
parent
6f52e69f46
commit
55adb3c456
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user