*/
#include "qemu/osdep.h"
-#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/pci.h"
+#include "migration/vmstate.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
+#include "qemu/module.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
static void ahci_unmap_clb_address(AHCIDevice *ad);
static void ahci_unmap_fis_address(AHCIDevice *ad);
-__attribute__((__unused__)) /* TODO */
static const char *AHCIHostReg_lookup[AHCI_HOST_REG__COUNT] = {
[AHCI_HOST_REG_CAP] = "CAP",
[AHCI_HOST_REG_CTL] = "GHC",
val = s->control_regs.version;
break;
default:
- break;
+ trace_ahci_mem_read_32_host_default(s, AHCIHostReg_lookup[regnum],
+ addr);
}
+ trace_ahci_mem_read_32_host(s, AHCIHostReg_lookup[regnum], addr, val);
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
(addr < (AHCI_PORT_REGS_START_ADDR +
(s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK);
+ } else {
+ trace_ahci_mem_read_32_default(s, addr, val);
}
trace_ahci_mem_read_32(s, addr, val);
if (ofst + size <= 4) {
val = lo >> (ofst * 8);
} else {
- g_assert_cmpint(size, >, 1);
+ g_assert(size > 1);
/* If the 64bit read is unaligned, we will produce undefined
* results. AHCI does not support unaligned 64bit reads. */
}
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
- switch (addr) {
- case HOST_CAP: /* R/WO, RO */
- /* FIXME handle R/WO */
- break;
- case HOST_CTL: /* R/W */
- if (val & HOST_CTL_RESET) {
- ahci_reset(s);
- } else {
- s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
- ahci_check_irq(s);
- }
- break;
- case HOST_IRQ_STAT: /* R/WC, RO */
- s->control_regs.irqstatus &= ~val;
+ enum AHCIHostReg regnum = addr / 4;
+ assert(regnum < AHCI_HOST_REG__COUNT);
+
+ switch (regnum) {
+ case AHCI_HOST_REG_CAP: /* R/WO, RO */
+ /* FIXME handle R/WO */
+ break;
+ case AHCI_HOST_REG_CTL: /* R/W */
+ if (val & HOST_CTL_RESET) {
+ ahci_reset(s);
+ } else {
+ s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
ahci_check_irq(s);
- break;
- case HOST_PORTS_IMPL: /* R/WO, RO */
- /* FIXME handle R/WO */
- break;
- case HOST_VERSION: /* RO */
- /* FIXME report write? */
- break;
- default:
- trace_ahci_mem_write_unknown(s, size, addr, val);
+ }
+ break;
+ case AHCI_HOST_REG_IRQ_STAT: /* R/WC, RO */
+ s->control_regs.irqstatus &= ~val;
+ ahci_check_irq(s);
+ break;
+ case AHCI_HOST_REG_PORTS_IMPL: /* R/WO, RO */
+ /* FIXME handle R/WO */
+ break;
+ case AHCI_HOST_REG_VERSION: /* RO */
+ /* FIXME report write? */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "Attempted write to unimplemented register: "
+ "AHCI host register %s, "
+ "offset 0x%"PRIx64": 0x%"PRIx64,
+ AHCIHostReg_lookup[regnum], addr, val);
+ trace_ahci_mem_write_host_unimpl(s, size,
+ AHCIHostReg_lookup[regnum], addr);
}
+ trace_ahci_mem_write_host(s, size, AHCIHostReg_lookup[regnum],
+ addr, val);
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
(addr < (AHCI_PORT_REGS_START_ADDR +
- (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
+ (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK, val);
+ } else {
+ qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: "
+ "AHCI global register at offset 0x%"PRIx64": 0x%"PRIx64,
+ addr, val);
+ trace_ahci_mem_write_unimpl(s, size, addr, val);
}
-
}
static const MemoryRegionOps ahci_mem_ops = {
}
}
-static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
+static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i)
{
AHCIPortRegs *pr = &ad->port_regs;
uint8_t *pio_fis;
pio_fis = &ad->res_fis[RES_FIS_PSFIS];
pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP;
- pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+ pio_fis[1] = (pio_fis_i ? (1 << 6) : 0);
pio_fis[2] = s->status;
pio_fis[3] = s->error;
if (pio_fis[2] & ERR_STAT) {
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
}
-
- ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS);
}
static bool ahci_write_fis_d2h(AHCIDevice *ad)
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
- d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+ d2h_fis[1] = (1 << 6); /* interrupt bit */
d2h_fis[2] = s->status;
d2h_fis[3] = s->error;
trace_handle_reg_h2d_fis_dump(s, port, pretty_fis);
g_free(pretty_fis);
}
- s->dev[port].done_atapi_packet = false;
- /* XXX send PIO setup FIS */
}
ide_state->error = 0;
-
+ s->dev[port].done_first_drq = false;
/* Reset transferred byte counter */
cmd->status = 0;
return 0;
}
-/* DMA dev <-> ram */
-static void ahci_start_transfer(IDEDMA *dma)
+/* Transfer PIO data between RAM and device */
+static void ahci_pio_transfer(IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
int is_write = opts & AHCI_CMD_WRITE;
int is_atapi = opts & AHCI_CMD_ATAPI;
int has_sglist = 0;
+ bool pio_fis_i;
- if (is_atapi && !ad->done_atapi_packet) {
+ /* The PIO Setup FIS is received prior to transfer, but the interrupt
+ * is only triggered after data is received.
+ *
+ * The device only sets the 'I' bit in the PIO Setup FIS for device->host
+ * requests (see "DPIOI1" in the SATA spec), or for host->device DRQs after
+ * the first (see "DPIOO1"). The latter is consistent with the spec's
+ * description of the PACKET protocol, where the command part of ATAPI requests
+ * ("DPKT0") has the 'I' bit clear, while the data part of PIO ATAPI requests
+ * ("DPKT4a" and "DPKT7") has the 'I' bit set for both directions for all DRQs.
+ */
+ pio_fis_i = ad->done_first_drq || (!is_atapi && !is_write);
+ ahci_write_fis_pio(ad, size, pio_fis_i);
+
+ if (is_atapi && !ad->done_first_drq) {
/* already prepopulated iobuffer */
- ad->done_atapi_packet = true;
- size = 0;
goto out;
}
has_sglist = 1;
}
- trace_ahci_start_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read",
- size, is_atapi ? "atapi" : "ata",
- has_sglist ? "" : "o");
+ trace_ahci_pio_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read",
+ size, is_atapi ? "atapi" : "ata",
+ has_sglist ? "" : "o");
if (has_sglist && size) {
if (is_write) {
}
}
-out:
- /* declare that we processed everything */
- s->data_ptr = s->data_end;
-
/* Update number of transferred bytes, destroy sglist */
dma_buf_commit(s, size);
- s->end_transfer_func(s);
+out:
+ /* declare that we processed everything */
+ s->data_ptr = s->data_end;
- if (!(s->status & DRQ_STAT)) {
- /* done with PIO send/receive */
- ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status));
+ ad->done_first_drq = true;
+ if (pio_fis_i) {
+ ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS);
}
}
.start_dma = ahci_start_dma,
.restart = ahci_restart,
.restart_dma = ahci_restart_dma,
- .start_transfer = ahci_start_transfer,
+ .pio_transfer = ahci_pio_transfer,
.prepare_buf = ahci_dma_prepare_buf,
.commit_buf = ahci_commit_buf,
.rw_buf = ahci_dma_rw_buf,
VMSTATE_UINT32(port_regs.scr_err, AHCIDevice),
VMSTATE_UINT32(port_regs.scr_act, AHCIDevice),
VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice),
- VMSTATE_BOOL(done_atapi_packet, AHCIDevice),
+ VMSTATE_BOOL(done_first_drq, AHCIDevice),
VMSTATE_INT32(busy_slot, AHCIDevice),
VMSTATE_BOOL(init_d2h_sent, AHCIDevice),
VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS,