Commit f1636845 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/jnsnow/tags/ide-pull-request' into staging



# gpg: Signature made Wed 10 Feb 2016 19:23:29 GMT using RSA key ID AAFC390E
# gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>"

* remotes/jnsnow/tags/ide-pull-request:
  ahci: prohibit "restarting" the FIS or CLB engines
  ahci: explicitly reject bad engine states on post_load
  ahci: handle LIST_ON and FIS_ON in map helpers
  ahci: Do not unmap NULL addresses
  fdc: always compile-check debug prints
  ide: fix device_reset to not ignore pending AIO
  ide: Add silent DRQ cancellation
  ide: replace blk_drain_all by blk_drain
  ide: move buffered DMA cancel to core
  ide: code motion
  ide: Prohibit RESET on IDE drives

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 36a9abd9 d5904749
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -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]);
+60 −38
Original line number Diff line number Diff line
@@ -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) {
    } else if (!cmd_start && cmd_on) {
        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;
        }
    }

    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) {
    } else if (!fis_start && fis_on) {
        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;
        }
    }

    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;
        }

+144 −73
Original line number Diff line number Diff line
@@ -487,14 +487,28 @@ 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;
    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)
    /* 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;
+1 −0
Original line number Diff line number Diff line
@@ -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);
+1 −35
Original line number Diff line number Diff line
@@ -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;