diff options
-rw-r--r-- | hw/block/fdc.c | 15 | ||||
-rw-r--r-- | hw/ide/ahci.c | 98 | ||||
-rw-r--r-- | hw/ide/core.c | 217 | ||||
-rw-r--r-- | hw/ide/internal.h | 1 | ||||
-rw-r--r-- | hw/ide/pci.c | 36 |
5 files changed, 214 insertions, 153 deletions
diff --git a/hw/block/fdc.c b/hw/block/fdc.c index a6f22ef200..9838d21cf5 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -41,14 +41,15 @@ /********************************************************/ /* debug Floppy devices */ -//#define DEBUG_FLOPPY -#ifdef DEBUG_FLOPPY +#define DEBUG_FLOPPY 0 + #define FLOPPY_DPRINTF(fmt, ...) \ - do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0) -#else -#define FLOPPY_DPRINTF(fmt, ...) -#endif + do { \ + if (DEBUG_FLOPPY) { \ + fprintf(stderr, "FLOPPY: " fmt , ## __VA_ARGS__); \ + } \ + } while (0) /********************************************************/ /* Floppy drive emulation */ @@ -353,7 +354,7 @@ static int pick_geometry(FDrive *drv) parse = &fd_formats[size_match]; FLOPPY_DPRINTF("User requested floppy drive type '%s', " "but inserted medium appears to be a " - "%d sector '%s' type\n", + "%"PRId64" sector '%s' type\n", FloppyDriveType_lookup[drv->drive], nb_sectors, FloppyDriveType_lookup[parse->drive]); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 7e87b1805e..f244bc01c9 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -199,52 +199,38 @@ static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr, * Check the cmd register to see if we should start or stop * the DMA or FIS RX engines. * - * @ad: Device to engage. - * @allow_stop: Allow device to transition from started to stopped? - * 'no' is useful for migration post_load, which does not expect a transition. + * @ad: Device to dis/engage. * * @return 0 on success, -1 on error. */ -static int ahci_cond_start_engines(AHCIDevice *ad, bool allow_stop) +static int ahci_cond_start_engines(AHCIDevice *ad) { AHCIPortRegs *pr = &ad->port_regs; - - if (pr->cmd & PORT_CMD_START) { - if (ahci_map_clb_address(ad)) { - pr->cmd |= PORT_CMD_LIST_ON; - } else { + bool cmd_start = pr->cmd & PORT_CMD_START; + bool cmd_on = pr->cmd & PORT_CMD_LIST_ON; + bool fis_start = pr->cmd & PORT_CMD_FIS_RX; + bool fis_on = pr->cmd & PORT_CMD_FIS_ON; + + if (cmd_start && !cmd_on) { + if (!ahci_map_clb_address(ad)) { + pr->cmd &= ~PORT_CMD_START; error_report("AHCI: Failed to start DMA engine: " "bad command list buffer address"); return -1; } - } else if (pr->cmd & PORT_CMD_LIST_ON) { - if (allow_stop) { - ahci_unmap_clb_address(ad); - pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON); - } else { - error_report("AHCI: DMA engine should be off, " - "but appears to still be running"); - return -1; - } + } else if (!cmd_start && cmd_on) { + ahci_unmap_clb_address(ad); } - if (pr->cmd & PORT_CMD_FIS_RX) { - if (ahci_map_fis_address(ad)) { - pr->cmd |= PORT_CMD_FIS_ON; - } else { + if (fis_start && !fis_on) { + if (!ahci_map_fis_address(ad)) { + pr->cmd &= ~PORT_CMD_FIS_RX; error_report("AHCI: Failed to start FIS receive engine: " "bad FIS receive buffer address"); return -1; } - } else if (pr->cmd & PORT_CMD_FIS_ON) { - if (allow_stop) { - ahci_unmap_fis_address(ad); - pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON); - } else { - error_report("AHCI: FIS receive engine should be off, " - "but appears to still be running"); - return -1; - } + } else if (!fis_start && fis_on) { + ahci_unmap_fis_address(ad); } return 0; @@ -286,8 +272,8 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK)); - /* Check FIS RX and CLB engines, allow transition to false: */ - ahci_cond_start_engines(&s->dev[port], true); + /* Check FIS RX and CLB engines */ + ahci_cond_start_engines(&s->dev[port]); /* XXX usually the FIS would be pending on the bus here and issuing deferred until the OS enables FIS receival. @@ -657,11 +643,22 @@ static bool ahci_map_fis_address(AHCIDevice *ad) AHCIPortRegs *pr = &ad->port_regs; map_page(ad->hba->as, &ad->res_fis, ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); - return ad->res_fis != NULL; + if (ad->res_fis != NULL) { + pr->cmd |= PORT_CMD_FIS_ON; + return true; + } + + pr->cmd &= ~PORT_CMD_FIS_ON; + return false; } static void ahci_unmap_fis_address(AHCIDevice *ad) { + if (ad->res_fis == NULL) { + DPRINTF(ad->port_no, "Attempt to unmap NULL FIS address\n"); + return; + } + ad->port_regs.cmd &= ~PORT_CMD_FIS_ON; dma_memory_unmap(ad->hba->as, ad->res_fis, 256, DMA_DIRECTION_FROM_DEVICE, 256); ad->res_fis = NULL; @@ -673,11 +670,22 @@ static bool ahci_map_clb_address(AHCIDevice *ad) ad->cur_cmd = NULL; map_page(ad->hba->as, &ad->lst, ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - return ad->lst != NULL; + if (ad->lst != NULL) { + pr->cmd |= PORT_CMD_LIST_ON; + return true; + } + + pr->cmd &= ~PORT_CMD_LIST_ON; + return false; } static void ahci_unmap_clb_address(AHCIDevice *ad) { + if (ad->lst == NULL) { + DPRINTF(ad->port_no, "Attempt to unmap NULL CLB address\n"); + return; + } + ad->port_regs.cmd &= ~PORT_CMD_LIST_ON; dma_memory_unmap(ad->hba->as, ad->lst, 1024, DMA_DIRECTION_FROM_DEVICE, 1024); ad->lst = NULL; @@ -1552,14 +1560,28 @@ static int ahci_state_post_load(void *opaque, int version_id) int i, j; struct AHCIDevice *ad; NCQTransferState *ncq_tfs; + AHCIPortRegs *pr; AHCIState *s = opaque; for (i = 0; i < s->ports; i++) { ad = &s->dev[i]; + pr = &ad->port_regs; + + if (!(pr->cmd & PORT_CMD_START) && (pr->cmd & PORT_CMD_LIST_ON)) { + error_report("AHCI: DMA engine should be off, but status bit " + "indicates it is still running."); + return -1; + } + if (!(pr->cmd & PORT_CMD_FIS_RX) && (pr->cmd & PORT_CMD_FIS_ON)) { + error_report("AHCI: FIS RX engine should be off, but status bit " + "indicates it is still running."); + return -1; + } - /* Only remap the CLB address if appropriate, disallowing a state - * transition from 'on' to 'off' it should be consistent here. */ - if (ahci_cond_start_engines(ad, false) != 0) { + /* After a migrate, the DMA/FIS engines are "off" and + * need to be conditionally restarted */ + pr->cmd &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON); + if (ahci_cond_start_engines(ad) != 0) { return -1; } diff --git a/hw/ide/core.c b/hw/ide/core.c index 4c46453e27..241e840de0 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -487,13 +487,27 @@ static void ide_cmd_done(IDEState *s) } } -void ide_transfer_stop(IDEState *s) +static void ide_transfer_halt(IDEState *s, + void(*end_transfer_func)(IDEState *), + bool notify) { - s->end_transfer_func = ide_transfer_stop; + s->end_transfer_func = end_transfer_func; s->data_ptr = s->io_buffer; s->data_end = s->io_buffer; s->status &= ~DRQ_STAT; - ide_cmd_done(s); + if (notify) { + ide_cmd_done(s); + } +} + +void ide_transfer_stop(IDEState *s) +{ + ide_transfer_halt(s, ide_transfer_stop, true); +} + +static void ide_transfer_cancel(IDEState *s) +{ + ide_transfer_halt(s, ide_transfer_cancel, false); } int64_t ide_get_sector(IDEState *s) @@ -609,6 +623,51 @@ BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, return aioreq; } +/** + * Cancel all pending DMA requests. + * Any buffered DMA requests are instantly canceled, + * but any pending unbuffered DMA requests must be waited on. + */ +void ide_cancel_dma_sync(IDEState *s) +{ + IDEBufferedRequest *req; + + /* First invoke the callbacks of all buffered requests + * and flag those requests as orphaned. Ideally there + * are no unbuffered (Scatter Gather DMA Requests or + * write requests) pending and we can avoid to drain. */ + QLIST_FOREACH(req, &s->buffered_requests, list) { + if (!req->orphaned) { +#ifdef DEBUG_IDE + printf("%s: invoking cb %p of buffered request %p with" + " -ECANCELED\n", __func__, req->original_cb, req); +#endif + req->original_cb(req->original_opaque, -ECANCELED); + } + req->orphaned = true; + } + + /* + * We can't cancel Scatter Gather DMA in the middle of the + * operation or a partial (not full) DMA transfer would reach + * the storage so we wait for completion instead (we beahve + * like if the DMA was completed by the time the guest trying + * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not + * set). + * + * In the future we'll be able to safely cancel the I/O if the + * whole DMA operation will be submitted to disk with a single + * aio operation with preadv/pwritev. + */ + if (s->bus->dma->aiocb) { +#ifdef DEBUG_IDE + printf("%s: draining all remaining requests", __func__); +#endif + blk_drain(s->blk); + assert(s->bus->dma->aiocb == NULL); + } +} + static void ide_sector_read(IDEState *s); static void ide_sector_read_cb(void *opaque, int ret) @@ -1175,11 +1234,86 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } +static void ide_reset(IDEState *s) +{ +#ifdef DEBUG_IDE + printf("ide: reset\n"); +#endif + + if (s->pio_aiocb) { + blk_aio_cancel(s->pio_aiocb); + s->pio_aiocb = NULL; + } + + if (s->drive_kind == IDE_CFATA) + s->mult_sectors = 0; + else + s->mult_sectors = MAX_MULT_SECTORS; + /* ide regs */ + s->feature = 0; + s->error = 0; + s->nsector = 0; + s->sector = 0; + s->lcyl = 0; + s->hcyl = 0; + + /* lba48 */ + s->hob_feature = 0; + s->hob_sector = 0; + s->hob_nsector = 0; + s->hob_lcyl = 0; + s->hob_hcyl = 0; + + s->select = 0xa0; + s->status = READY_STAT | SEEK_STAT; + + s->lba48 = 0; + + /* ATAPI specific */ + s->sense_key = 0; + s->asc = 0; + s->cdrom_changed = 0; + s->packet_transfer_size = 0; + s->elementary_transfer_size = 0; + s->io_buffer_index = 0; + s->cd_sector_size = 0; + s->atapi_dma = 0; + s->tray_locked = 0; + s->tray_open = 0; + /* ATA DMA state */ + s->io_buffer_size = 0; + s->req_nb_sectors = 0; + + ide_set_signature(s); + /* init the transfer handler so that 0xffff is returned on data + accesses */ + s->end_transfer_func = ide_dummy_transfer_stop; + ide_dummy_transfer_stop(s); + s->media_changed = 0; +} + static bool cmd_nop(IDEState *s, uint8_t cmd) { return true; } +static bool cmd_device_reset(IDEState *s, uint8_t cmd) +{ + /* Halt PIO (in the DRQ phase), then DMA */ + ide_transfer_cancel(s); + ide_cancel_dma_sync(s); + + /* Reset any PIO commands, reset signature, etc */ + ide_reset(s); + + /* RESET: ATA8-ACS3 7.10.4 "Normal Outputs"; + * ATA8-ACS3 Table 184 "Device Signatures for Normal Output" */ + s->status = 0x00; + + /* Do not overwrite status register */ + return false; +} + static bool cmd_data_set_management(IDEState *s, uint8_t cmd) { switch (s->feature) { @@ -1496,15 +1630,6 @@ static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd) return false; } -static bool cmd_device_reset(IDEState *s, uint8_t cmd) -{ - ide_set_signature(s); - s->status = 0x00; /* NOTE: READY is _not_ set */ - s->error = 0x01; - - return false; -} - static bool cmd_packet(IDEState *s, uint8_t cmd) { /* overlapping commands not supported */ @@ -1877,9 +2002,13 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) return; } - /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ - if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) - return; + /* Only RESET is allowed while BSY and/or DRQ are set, + * and only to ATAPI devices. */ + if (s->status & (BUSY_STAT|DRQ_STAT)) { + if (val != WIN_DEVICE_RESET || s->drive_kind != IDE_CD) { + return; + } + } if (!ide_cmd_permitted(s, val)) { ide_abort_command(s); @@ -2179,64 +2308,6 @@ static void ide_dummy_transfer_stop(IDEState *s) s->io_buffer[3] = 0xff; } -static void ide_reset(IDEState *s) -{ -#ifdef DEBUG_IDE - printf("ide: reset\n"); -#endif - - if (s->pio_aiocb) { - blk_aio_cancel(s->pio_aiocb); - s->pio_aiocb = NULL; - } - - if (s->drive_kind == IDE_CFATA) - s->mult_sectors = 0; - else - s->mult_sectors = MAX_MULT_SECTORS; - /* ide regs */ - s->feature = 0; - s->error = 0; - s->nsector = 0; - s->sector = 0; - s->lcyl = 0; - s->hcyl = 0; - - /* lba48 */ - s->hob_feature = 0; - s->hob_sector = 0; - s->hob_nsector = 0; - s->hob_lcyl = 0; - s->hob_hcyl = 0; - - s->select = 0xa0; - s->status = READY_STAT | SEEK_STAT; - - s->lba48 = 0; - - /* ATAPI specific */ - s->sense_key = 0; - s->asc = 0; - s->cdrom_changed = 0; - s->packet_transfer_size = 0; - s->elementary_transfer_size = 0; - s->io_buffer_index = 0; - s->cd_sector_size = 0; - s->atapi_dma = 0; - s->tray_locked = 0; - s->tray_open = 0; - /* ATA DMA state */ - s->io_buffer_size = 0; - s->req_nb_sectors = 0; - - ide_set_signature(s); - /* init the transfer handler so that 0xffff is returned on data - accesses */ - s->end_transfer_func = ide_dummy_transfer_stop; - ide_dummy_transfer_stop(s); - s->media_changed = 0; -} - void ide_bus_reset(IDEBus *bus) { bus->unit = 0; diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 2d1e2d2d2f..86bde26551 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -586,6 +586,7 @@ BlockAIOCB *ide_issue_trim(BlockBackend *blk, BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); +void ide_cancel_dma_sync(IDEState *s); /* hw/ide/atapi.c */ void ide_atapi_cmd(IDEState *s); diff --git a/hw/ide/pci.c b/hw/ide/pci.c index fa5b63bc3f..92ffee7264 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -234,41 +234,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) /* Ignore writes to SSBM if it keeps the old value */ if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) { if (!(val & BM_CMD_START)) { - /* First invoke the callbacks of all buffered requests - * and flag those requests as orphaned. Ideally there - * are no unbuffered (Scatter Gather DMA Requests or - * write requests) pending and we can avoid to drain. */ - IDEBufferedRequest *req; - IDEState *s = idebus_active_if(bm->bus); - QLIST_FOREACH(req, &s->buffered_requests, list) { - if (!req->orphaned) { -#ifdef DEBUG_IDE - printf("%s: invoking cb %p of buffered request %p with" - " -ECANCELED\n", __func__, req->original_cb, req); -#endif - req->original_cb(req->original_opaque, -ECANCELED); - } - req->orphaned = true; - } - /* - * We can't cancel Scatter Gather DMA in the middle of the - * operation or a partial (not full) DMA transfer would reach - * the storage so we wait for completion instead (we beahve - * like if the DMA was completed by the time the guest trying - * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not - * set). - * - * In the future we'll be able to safely cancel the I/O if the - * whole DMA operation will be submitted to disk with a single - * aio operation with preadv/pwritev. - */ - if (bm->bus->dma->aiocb) { -#ifdef DEBUG_IDE - printf("%s: draining all remaining requests", __func__); -#endif - blk_drain_all(); - assert(bm->bus->dma->aiocb == NULL); - } + ide_cancel_dma_sync(idebus_active_if(bm->bus)); bm->status &= ~BM_STATUS_DMAING; } else { bm->cur_addr = bm->addr; |