summaryrefslogtreecommitdiff
path: root/hw/ide/pci.c
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2011-05-26 15:00:34 +0200
committerKevin Wolf <kwolf@redhat.com>2011-06-15 14:36:15 +0200
commitdef93791f22a3536c3508244ec7d270098484c7d (patch)
treed2730ee2e84d06a24048e8d1abbe937207b60824 /hw/ide/pci.c
parent9e2a3701a1fcfec0316b9dc1a6cd62869de5542c (diff)
downloadqemu-def93791f22a3536c3508244ec7d270098484c7d.tar.gz
ide: Split error status from status register
When adding the werror=stop mode, some flags were added to s->status which are used to determine what kind of operation should be restarted when the VM is continued. Unfortunately, it turns out that s->status is in fact a device register and as such is visible to the guest (some of the abused bits are even writable for the guest). For migration we keep on using the old VMState field (renamed to migration_compat_status) if the status register doesn't use any of the previously abused bits. If it does, we use a subsection with a clean copy of the status register. The error status is always sent in a subsection if there is any error. It can't use the old field because errors happen even without PCI. Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'hw/ide/pci.c')
-rw-r--r--hw/ide/pci.c73
1 files changed, 66 insertions, 7 deletions
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index a4726adbea..7fa32bdff1 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -183,27 +183,33 @@ static void bmdma_restart_dma(BMDMAState *bm, int is_read)
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;
qemu_bh_delete(bm->bh);
bm->bh = NULL;
- is_read = !!(bm->status & BM_STATUS_RETRY_READ);
+ if (bm->unit == (uint8_t) -1) {
+ return;
+ }
- if (bm->status & BM_STATUS_DMA_RETRY) {
- bm->status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ);
+ is_read = !!(bus->error_status & BM_STATUS_RETRY_READ);
+
+ if (bus->error_status & BM_STATUS_DMA_RETRY) {
+ bus->error_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);
+ } else if (bus->error_status & BM_STATUS_PIO_RETRY) {
+ bus->error_status &= ~(BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ);
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 (bus->error_status & BM_STATUS_RETRY_FLUSH) {
ide_flush_cache(bmdma_active_if(bm));
}
}
@@ -351,6 +357,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 +408,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 +439,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 +456,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;
}