*/
#include "qemu/osdep.h"
+#include "hw/irq.h"
#include "hw/pci/msi.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "trace.h"
static void check_cmd(AHCIState *s, int port);
-static int handle_cmd(AHCIState *s, int port, uint8_t slot);
+static void handle_cmd(AHCIState *s, int port, uint8_t slot);
static void ahci_reset_port(AHCIState *s, int port);
-static bool ahci_write_fis_d2h(AHCIDevice *ad);
+static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i);
+static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot);
static void ahci_init_d2h(AHCIDevice *ad);
static int ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit);
static bool ahci_map_clb_address(AHCIDevice *ad);
if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
- if ((pr->cmd_issue & (1U << slot)) &&
- !handle_cmd(s, port, slot)) {
- pr->cmd_issue &= ~(1U << slot);
+ if (pr->cmd_issue & (1U << slot)) {
+ handle_cmd(s, port, slot);
}
}
}
return;
}
- if (ahci_write_fis_d2h(ad)) {
+ if (ahci_write_fis_d2h(ad, true)) {
ad->init_d2h_sent = true;
/* We're emulating receiving the first Reg H2D Fis from the device;
* Update the SIG register, but otherwise proceed as normal. */
s->dev[port].port_state = STATE_RUN;
if (ide_state->drive_kind == IDE_CD) {
- ahci_set_signature(d, SATA_SIGNATURE_CDROM);\
+ ahci_set_signature(d, SATA_SIGNATURE_CDROM);
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
} else {
ahci_set_signature(d, SATA_SIGNATURE_DISK);
}
}
-static bool ahci_write_fis_d2h(AHCIDevice *ad)
+static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i)
{
AHCIPortRegs *pr = &ad->port_regs;
uint8_t *d2h_fis;
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
- d2h_fis[1] = (1 << 6); /* interrupt bit */
+ d2h_fis[1] = d2h_fis_i ? (1 << 6) : 0; /* interrupt bit */
d2h_fis[2] = s->status;
d2h_fis[3] = s->error;
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
}
- ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS);
+ if (d2h_fis_i) {
+ ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS);
+ }
+
return true;
}
ncq_cb, ncq_tfs);
break;
case WRITE_FPDMA_QUEUED:
- trace_execute_ncq_command_read(ad->hba, port, ncq_tfs->tag,
- ncq_tfs->sector_count, ncq_tfs->lba);
+ trace_execute_ncq_command_write(ad->hba, port, ncq_tfs->tag,
+ ncq_tfs->sector_count, ncq_tfs->lba);
dma_acct_start(ide_state->blk, &ncq_tfs->acct,
&ncq_tfs->sglist, BLOCK_ACCT_WRITE);
ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist,
return;
}
+ /*
+ * A NCQ command clears the bit in PxCI after the command has been QUEUED
+ * successfully (ERROR not set, BUSY and DRQ cleared).
+ *
+ * For NCQ commands, PxCI will always be cleared here.
+ *
+ * (Once the NCQ command is COMPLETED, the device will send a SDB FIS with
+ * the interrupt bit set, which will clear PxSACT and raise an interrupt.)
+ */
+ ahci_clear_cmd_issue(ad, slot);
+
+ /*
+ * In reality, for NCQ commands, PxCI is cleared after receiving a D2H FIS
+ * without the interrupt bit set, but since ahci_write_fis_d2h() can raise
+ * an IRQ on error, we need to call them in reverse order.
+ */
+ ahci_write_fis_d2h(ad, false);
+
ncq_tfs->used = 1;
ncq_tfs->drive = ad;
ncq_tfs->slot = slot;
ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0);
if (ncq_tfs->sglist.size < size) {
- error_report("ahci: PRDT length for NCQ command (0x%zx) "
+ error_report("ahci: PRDT length for NCQ command (0x" DMA_ADDR_FMT ") "
"is smaller than the requested size (0x%zx)",
ncq_tfs->sglist.size, size);
ncq_err(ncq_tfs);
{
IDEState *ide_state = &s->dev[port].port.ifs[0];
AHCICmdHdr *cmd = get_cmd_header(s, port, slot);
+ AHCIDevice *ad = &s->dev[port];
uint16_t opts = le16_to_cpu(cmd->opts);
if (cmd_fis[1] & 0x0F) {
/* Reset transferred byte counter */
cmd->status = 0;
+ /*
+ * A non-NCQ command clears the bit in PxCI after the command has COMPLETED
+ * successfully (ERROR not set, BUSY and DRQ cleared).
+ *
+ * For non-NCQ commands, PxCI will always be cleared by ahci_cmd_done().
+ */
+ ad->busy_slot = slot;
+
/* We're ready to process the command in FIS byte 2. */
- ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
+ ide_bus_exec_cmd(&s->dev[port].port, cmd_fis[2]);
}
-static int handle_cmd(AHCIState *s, int port, uint8_t slot)
+static void handle_cmd(AHCIState *s, int port, uint8_t slot)
{
IDEState *ide_state;
uint64_t tbl_addr;
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
/* Engine currently busy, try again later */
trace_handle_cmd_busy(s, port);
- return -1;
+ return;
}
if (!s->dev[port].lst) {
trace_handle_cmd_nolist(s, port);
- return -1;
+ return;
}
cmd = get_cmd_header(s, port, slot);
/* remember current slot handle for later */
ide_state = &s->dev[port].port.ifs[0];
if (!ide_state->blk) {
trace_handle_cmd_badport(s, port);
- return -1;
+ return;
}
tbl_addr = le64_to_cpu(cmd->tbl_addr);
DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED);
if (!cmd_fis) {
trace_handle_cmd_badfis(s, port);
- return -1;
+ return;
} else if (cmd_len != 0x80) {
ahci_trigger_irq(s, &s->dev[port], AHCI_PORT_IRQ_BIT_HBFS);
trace_handle_cmd_badmap(s, port, cmd_len);
out:
dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_TO_DEVICE,
cmd_len);
-
- if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
- /* async command, complete later */
- s->dev[port].busy_slot = slot;
- return -1;
- }
-
- /* done handling the command */
- return 0;
}
/* Transfer PIO data between RAM and device */
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
if (is_write) {
- dma_buf_write(s->data_ptr, size, &s->sg, attrs);
+ dma_buf_write(s->data_ptr, size, NULL, &s->sg, attrs);
} else {
- dma_buf_read(s->data_ptr, size, &s->sg, attrs);
+ dma_buf_read(s->data_ptr, size, NULL, &s->sg, attrs);
}
}
}
if (is_write) {
- dma_buf_read(p, l, &s->sg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
} else {
- dma_buf_write(p, l, &s->sg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_write(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
}
/* free sglist, update byte count */
return 1;
}
+static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot)
+{
+ IDEState *ide_state = &ad->port.ifs[0];
+
+ if (!(ide_state->status & (BUSY_STAT | DRQ_STAT))) {
+ ad->port_regs.cmd_issue &= ~(1 << slot);
+ }
+}
+
+/* Non-NCQ command is done - This function is never called for NCQ commands. */
static void ahci_cmd_done(const IDEDMA *dma)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
/* no longer busy */
if (ad->busy_slot != -1) {
- ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
+ ahci_clear_cmd_issue(ad, ad->busy_slot);
ad->busy_slot = -1;
}
- /* update d2h status */
- ahci_write_fis_d2h(ad);
+ /*
+ * In reality, for non-NCQ commands, PxCI is cleared after receiving a D2H
+ * FIS with the interrupt bit set, but since ahci_write_fis_d2h() will raise
+ * an IRQ, we need to call them in reverse order.
+ */
+ ahci_write_fis_d2h(ad, true);
if (ad->port_regs.cmd_issue && !ad->check_bh) {
- ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
+ ad->check_bh = qemu_bh_new_guarded(ahci_check_cmd_bh, ad,
+ &ad->mem_reentrancy_guard);
qemu_bh_schedule(ad->check_bh);
}
}
AHCIDevice *ad = &s->dev[i];
ide_bus_init(&ad->port, sizeof(ad->port), qdev, i, 1);
- ide_init2(&ad->port, irqs[i]);
+ ide_bus_init_output_irq(&ad->port, irqs[i]);
ad->hba = s;
ad->port_no = i;
ad->port.dma = &ad->dma;
ad->port.dma->ops = &ahci_dma_ops;
- ide_register_restart_cb(&ad->port);
+ ide_bus_register_restart_cb(&ad->port);
}
g_free(irqs);
}
if (hd[i] == NULL) {
continue;
}
- ide_create_drive(&ahci->dev[i].port, 0, hd[i]);
+ ide_bus_create_drive(&ahci->dev[i].port, 0, hd[i]);
}
}