--- qemu/hw/ide/pci.c 2018/04/24 18:40:25 1.1.1.3 +++ qemu/hw/ide/pci.c 2018/04/24 19:02:20 1.1.1.4 @@ -28,7 +28,6 @@ #include #include "block.h" #include "block_int.h" -#include "sysemu.h" #include "dma.h" #include @@ -170,7 +169,7 @@ static int bmdma_set_inactive(IDEDMA *dm 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); @@ -178,33 +177,48 @@ static void bmdma_restart_dma(BMDMAState 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)); } } @@ -297,12 +311,8 @@ void bmdma_cmd_writeb(void *opaque, uint */ if (bm->bus->dma->aiocb) { qemu_aio_flush(); -#ifdef DEBUG_IDE - if (bm->bus->dma->aiocb) - printf("ide_dma_cancel: aiocb still pending"); - if (bm->status & BM_STATUS_DMAING) - printf("ide_dma_cancel: BM_STATUS_DMAING still pending"); -#endif + assert(bm->bus->dma->aiocb == NULL); + assert((bm->status & BM_STATUS_DMAING) == 0); } } else { bm->cur_addr = bm->addr; @@ -356,6 +366,43 @@ static bool ide_bmdma_current_needed(voi 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, @@ -370,15 +417,26 @@ static const VMStateDescription vmstate_ } }; +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), @@ -390,6 +448,9 @@ static const VMStateDescription vmstate_ .vmsd = &vmstate_bmdma_current, .needed = ide_bmdma_current_needed, }, { + .vmsd = &vmstate_bmdma_status, + .needed = ide_bmdma_status_needed, + }, { /* empty */ } } @@ -404,7 +465,9 @@ static int ide_pci_post_load(void *opaqu /* 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; }