aboutsummaryrefslogtreecommitdiff
path: root/hw/ide/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ide/pci.c')
-rw-r--r--hw/ide/pci.c88
1 files changed, 78 insertions, 10 deletions
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index a4726adbea..9f3050a15e 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -169,7 +169,7 @@ static int bmdma_set_inactive(IDEDMA *dma)
return 0;
}
-static void bmdma_restart_dma(BMDMAState *bm, int is_read)
+static void bmdma_restart_dma(BMDMAState *bm, enum ide_dma_cmd dma_cmd)
{
IDEState *s = bmdma_active_if(bm);
@@ -177,33 +177,48 @@ static void bmdma_restart_dma(BMDMAState *bm, int is_read)
s->io_buffer_index = 0;
s->io_buffer_size = 0;
s->nsector = bm->nsector;
- s->is_read = is_read;
+ s->dma_cmd = dma_cmd;
bm->cur_addr = bm->addr;
bm->dma_cb = ide_dma_cb;
bmdma_start_dma(&bm->dma, s, bm->dma_cb);
}
+/* TODO This should be common IDE code */
static void bmdma_restart_bh(void *opaque)
{
BMDMAState *bm = opaque;
+ IDEBus *bus = bm->bus;
int is_read;
+ int error_status;
qemu_bh_delete(bm->bh);
bm->bh = NULL;
- is_read = !!(bm->status & BM_STATUS_RETRY_READ);
+ if (bm->unit == (uint8_t) -1) {
+ return;
+ }
+
+ is_read = !!(bus->error_status & BM_STATUS_RETRY_READ);
- if (bm->status & BM_STATUS_DMA_RETRY) {
- bm->status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ);
- bmdma_restart_dma(bm, is_read);
- } else if (bm->status & BM_STATUS_PIO_RETRY) {
- bm->status &= ~(BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ);
+ /* The error status must be cleared before resubmitting the request: The
+ * request may fail again, and this case can only be distinguished if the
+ * called function can set a new error status. */
+ error_status = bus->error_status;
+ bus->error_status = 0;
+
+ if (error_status & BM_STATUS_DMA_RETRY) {
+ if (error_status & BM_STATUS_RETRY_TRIM) {
+ bmdma_restart_dma(bm, IDE_DMA_TRIM);
+ } else {
+ bmdma_restart_dma(bm, is_read ? IDE_DMA_READ : IDE_DMA_WRITE);
+ }
+ } else if (error_status & BM_STATUS_PIO_RETRY) {
if (is_read) {
ide_sector_read(bmdma_active_if(bm));
} else {
ide_sector_write(bmdma_active_if(bm));
}
- } else if (bm->status & BM_STATUS_RETRY_FLUSH) {
+ } else if (error_status & BM_STATUS_RETRY_FLUSH) {
ide_flush_cache(bmdma_active_if(bm));
}
}
@@ -351,6 +366,43 @@ static bool ide_bmdma_current_needed(void *opaque)
return (bm->cur_prd_len != 0);
}
+static bool ide_bmdma_status_needed(void *opaque)
+{
+ BMDMAState *bm = opaque;
+
+ /* Older versions abused some bits in the status register for internal
+ * error state. If any of these bits are set, we must add a subsection to
+ * transfer the real status register */
+ uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS;
+
+ return ((bm->status & abused_bits) != 0);
+}
+
+static void ide_bmdma_pre_save(void *opaque)
+{
+ BMDMAState *bm = opaque;
+ uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS;
+
+ bm->migration_compat_status =
+ (bm->status & ~abused_bits) | (bm->bus->error_status & abused_bits);
+}
+
+/* This function accesses bm->bus->error_status which is loaded only after
+ * BMDMA itself. This is why the function is called from ide_pci_post_load
+ * instead of being registered with VMState where it would run too early. */
+static int ide_bmdma_post_load(void *opaque, int version_id)
+{
+ BMDMAState *bm = opaque;
+ uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS;
+
+ if (bm->status == 0) {
+ bm->status = bm->migration_compat_status & ~abused_bits;
+ bm->bus->error_status |= bm->migration_compat_status & abused_bits;
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_bmdma_current = {
.name = "ide bmdma_current",
.version_id = 1,
@@ -365,15 +417,26 @@ static const VMStateDescription vmstate_bmdma_current = {
}
};
+const VMStateDescription vmstate_bmdma_status = {
+ .name ="ide bmdma/status",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(status, BMDMAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
static const VMStateDescription vmstate_bmdma = {
.name = "ide bmdma",
.version_id = 3,
.minimum_version_id = 0,
.minimum_version_id_old = 0,
+ .pre_save = ide_bmdma_pre_save,
.fields = (VMStateField []) {
VMSTATE_UINT8(cmd, BMDMAState),
- VMSTATE_UINT8(status, BMDMAState),
+ VMSTATE_UINT8(migration_compat_status, BMDMAState),
VMSTATE_UINT32(addr, BMDMAState),
VMSTATE_INT64(sector_num, BMDMAState),
VMSTATE_UINT32(nsector, BMDMAState),
@@ -385,6 +448,9 @@ static const VMStateDescription vmstate_bmdma = {
.vmsd = &vmstate_bmdma_current,
.needed = ide_bmdma_current_needed,
}, {
+ .vmsd = &vmstate_bmdma_status,
+ .needed = ide_bmdma_status_needed,
+ }, {
/* empty */
}
}
@@ -399,7 +465,9 @@ static int ide_pci_post_load(void *opaque, int version_id)
/* current versions always store 0/1, but older version
stored bigger values. We only need last bit */
d->bmdma[i].unit &= 1;
+ ide_bmdma_post_load(&d->bmdma[i], -1);
}
+
return 0;
}