aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-06-11 11:12:46 +0100
committerPeter Maydell <peter.maydell@linaro.org>2018-06-11 11:12:46 +0100
commita7a7309ca52c327c6603d60db90ae4feeae719f7 (patch)
tree6951a90343b7720974921c64b48dc157f24dd98b
parent0d2fa03dae4fbe185a082f361342b1e30aed4582 (diff)
parentc173723f247c69974a83af1395020d0f01a0d334 (diff)
Merge remote-tracking branch 'remotes/jnsnow/tags/ide-pull-request' into staging
Pull request # gpg: Signature made Fri 08 Jun 2018 18:46:24 BST # gpg: using RSA key 7DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" # Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB # Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E * remotes/jnsnow/tags/ide-pull-request: (30 commits) ide: introduce ide_transfer_start_norecurse atapi: call ide_set_irq before ide_transfer_start ide: make ide_transfer_stop idempotent ide: call ide_cmd_done from ide_transfer_stop ide: push end_transfer_func out of start_transfer callback, rename callback ahci: move PIO Setup FIS before transfer, fix it for ATAPI commands libqos/ahci: track sector size MAINTAINERS: Add the cdrom-test to John's section tests/cdrom-test: Test that -cdrom parameter is working tests/cdrom-test: Test booting from CD-ROM ISO image file tests/boot-sector: Add magic bytes to s390x boot code header ahci: make ahci_mem_write traces more descriptive ahci: delete old host register address definitions ahci: adjust ahci_mem_write to work on registers ahci: fix spacing damage on ahci_mem_write ahci: make mem_read_32 traces more descriptive ahci: modify ahci_mem_read_32 to work on register numbers ahci: fix host register max address ahci: add host register enumeration ahci: delete old port register address definitions ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS1
-rw-r--r--hw/ide/ahci.c373
-rw-r--r--hw/ide/ahci_internal.h63
-rw-r--r--hw/ide/atapi.c44
-rw-r--r--hw/ide/core.c39
-rw-r--r--hw/ide/trace-events15
-rw-r--r--include/hw/ide/internal.h4
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/boot-sector.c9
-rw-r--r--tests/cdrom-test.c222
-rw-r--r--tests/libqos/ahci.c45
-rw-r--r--tests/libqos/ahci.h3
12 files changed, 574 insertions, 246 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 4c73c16fee..a40f558694 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1003,6 +1003,7 @@ F: hw/block/cdrom.c
F: hw/block/hd-geometry.c
F: tests/ide-test.c
F: tests/ahci-test.c
+F: tests/cdrom-test.c
F: tests/libqos/ahci*
T: git git://github.com/jnsnow/qemu.git ide
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 24dbad5125..f7852be842 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -27,6 +27,7 @@
#include "hw/pci/pci.h"
#include "qemu/error-report.h"
+#include "qemu/log.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
@@ -46,6 +47,44 @@ static bool ahci_map_fis_address(AHCIDevice *ad);
static void ahci_unmap_clb_address(AHCIDevice *ad);
static void ahci_unmap_fis_address(AHCIDevice *ad);
+static const char *AHCIHostReg_lookup[AHCI_HOST_REG__COUNT] = {
+ [AHCI_HOST_REG_CAP] = "CAP",
+ [AHCI_HOST_REG_CTL] = "GHC",
+ [AHCI_HOST_REG_IRQ_STAT] = "IS",
+ [AHCI_HOST_REG_PORTS_IMPL] = "PI",
+ [AHCI_HOST_REG_VERSION] = "VS",
+ [AHCI_HOST_REG_CCC_CTL] = "CCC_CTL",
+ [AHCI_HOST_REG_CCC_PORTS] = "CCC_PORTS",
+ [AHCI_HOST_REG_EM_LOC] = "EM_LOC",
+ [AHCI_HOST_REG_EM_CTL] = "EM_CTL",
+ [AHCI_HOST_REG_CAP2] = "CAP2",
+ [AHCI_HOST_REG_BOHC] = "BOHC",
+};
+
+static const char *AHCIPortReg_lookup[AHCI_PORT_REG__COUNT] = {
+ [AHCI_PORT_REG_LST_ADDR] = "PxCLB",
+ [AHCI_PORT_REG_LST_ADDR_HI] = "PxCLBU",
+ [AHCI_PORT_REG_FIS_ADDR] = "PxFB",
+ [AHCI_PORT_REG_FIS_ADDR_HI] = "PxFBU",
+ [AHCI_PORT_REG_IRQ_STAT] = "PxIS",
+ [AHCI_PORT_REG_IRQ_MASK] = "PXIE",
+ [AHCI_PORT_REG_CMD] = "PxCMD",
+ [7] = "Reserved",
+ [AHCI_PORT_REG_TFDATA] = "PxTFD",
+ [AHCI_PORT_REG_SIG] = "PxSIG",
+ [AHCI_PORT_REG_SCR_STAT] = "PxSSTS",
+ [AHCI_PORT_REG_SCR_CTL] = "PxSCTL",
+ [AHCI_PORT_REG_SCR_ERR] = "PxSERR",
+ [AHCI_PORT_REG_SCR_ACT] = "PxSACT",
+ [AHCI_PORT_REG_CMD_ISSUE] = "PxCI",
+ [AHCI_PORT_REG_SCR_NOTIF] = "PxSNTF",
+ [AHCI_PORT_REG_FIS_CTL] = "PxFBS",
+ [AHCI_PORT_REG_DEV_SLEEP] = "PxDEVSLP",
+ [18 ... 27] = "Reserved",
+ [AHCI_PORT_REG_VENDOR_1 ...
+ AHCI_PORT_REG_VENDOR_4] = "PxVS",
+};
+
static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = {
[AHCI_PORT_IRQ_BIT_DHRS] = "DHRS",
[AHCI_PORT_IRQ_BIT_PSS] = "PSS",
@@ -68,41 +107,42 @@ static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = {
[AHCI_PORT_IRQ_BIT_CPDS] = "CPDS"
};
-static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
+static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
{
uint32_t val;
- AHCIPortRegs *pr;
- pr = &s->dev[port].port_regs;
+ AHCIPortRegs *pr = &s->dev[port].port_regs;
+ enum AHCIPortReg regnum = offset / sizeof(uint32_t);
+ assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t)));
- switch (offset) {
- case PORT_LST_ADDR:
+ switch (regnum) {
+ case AHCI_PORT_REG_LST_ADDR:
val = pr->lst_addr;
break;
- case PORT_LST_ADDR_HI:
+ case AHCI_PORT_REG_LST_ADDR_HI:
val = pr->lst_addr_hi;
break;
- case PORT_FIS_ADDR:
+ case AHCI_PORT_REG_FIS_ADDR:
val = pr->fis_addr;
break;
- case PORT_FIS_ADDR_HI:
+ case AHCI_PORT_REG_FIS_ADDR_HI:
val = pr->fis_addr_hi;
break;
- case PORT_IRQ_STAT:
+ case AHCI_PORT_REG_IRQ_STAT:
val = pr->irq_stat;
break;
- case PORT_IRQ_MASK:
+ case AHCI_PORT_REG_IRQ_MASK:
val = pr->irq_mask;
break;
- case PORT_CMD:
+ case AHCI_PORT_REG_CMD:
val = pr->cmd;
break;
- case PORT_TFDATA:
+ case AHCI_PORT_REG_TFDATA:
val = pr->tfdata;
break;
- case PORT_SIG:
+ case AHCI_PORT_REG_SIG:
val = pr->sig;
break;
- case PORT_SCR_STAT:
+ case AHCI_PORT_REG_SCR_STAT:
if (s->dev[port].port.ifs[0].blk) {
val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP |
SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE;
@@ -110,28 +150,29 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
val = SATA_SCR_SSTATUS_DET_NODEV;
}
break;
- case PORT_SCR_CTL:
+ case AHCI_PORT_REG_SCR_CTL:
val = pr->scr_ctl;
break;
- case PORT_SCR_ERR:
+ case AHCI_PORT_REG_SCR_ERR:
val = pr->scr_err;
break;
- case PORT_SCR_ACT:
+ case AHCI_PORT_REG_SCR_ACT:
val = pr->scr_act;
break;
- case PORT_CMD_ISSUE:
+ case AHCI_PORT_REG_CMD_ISSUE:
val = pr->cmd_issue;
break;
- case PORT_RESERVED:
default:
+ trace_ahci_port_read_default(s, port, AHCIPortReg_lookup[regnum],
+ offset);
val = 0;
}
- trace_ahci_port_read(s, port, offset, val);
+ trace_ahci_port_read(s, port, AHCIPortReg_lookup[regnum], offset, val);
return val;
}
-static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
+static void ahci_irq_raise(AHCIState *s)
{
DeviceState *dev_state = s->container;
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
@@ -146,7 +187,7 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
}
}
-static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev)
+static void ahci_irq_lower(AHCIState *s)
{
DeviceState *dev_state = s->container;
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
@@ -174,9 +215,9 @@ static void ahci_check_irq(AHCIState *s)
trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus);
if (s->control_regs.irqstatus &&
(s->control_regs.ghc & HOST_CTL_IRQ_EN)) {
- ahci_irq_raise(s, NULL);
+ ahci_irq_raise(s);
} else {
- ahci_irq_lower(s, NULL);
+ ahci_irq_lower(s);
}
}
@@ -253,85 +294,88 @@ static int ahci_cond_start_engines(AHCIDevice *ad)
return 0;
}
-static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
+static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
{
AHCIPortRegs *pr = &s->dev[port].port_regs;
+ enum AHCIPortReg regnum = offset / sizeof(uint32_t);
+ assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t)));
+ trace_ahci_port_write(s, port, AHCIPortReg_lookup[regnum], offset, val);
- trace_ahci_port_write(s, port, offset, val);
- switch (offset) {
- case PORT_LST_ADDR:
- pr->lst_addr = val;
- break;
- case PORT_LST_ADDR_HI:
- pr->lst_addr_hi = val;
- break;
- case PORT_FIS_ADDR:
- pr->fis_addr = val;
- break;
- case PORT_FIS_ADDR_HI:
- pr->fis_addr_hi = val;
- break;
- case PORT_IRQ_STAT:
- pr->irq_stat &= ~val;
- ahci_check_irq(s);
- break;
- case PORT_IRQ_MASK:
- pr->irq_mask = val & 0xfdc000ff;
- ahci_check_irq(s);
- break;
- case PORT_CMD:
- /* Block any Read-only fields from being set;
- * including LIST_ON and FIS_ON.
- * The spec requires to set ICC bits to zero after the ICC change
- * is done. We don't support ICC state changes, therefore always
- * force the ICC bits to zero.
- */
- pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
- (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK));
-
- /* 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.
- Instead, we only submit it once - which works in most
- cases, but is a hack. */
- if ((pr->cmd & PORT_CMD_FIS_ON) &&
- !s->dev[port].init_d2h_sent) {
- ahci_init_d2h(&s->dev[port]);
- }
+ switch (regnum) {
+ case AHCI_PORT_REG_LST_ADDR:
+ pr->lst_addr = val;
+ break;
+ case AHCI_PORT_REG_LST_ADDR_HI:
+ pr->lst_addr_hi = val;
+ break;
+ case AHCI_PORT_REG_FIS_ADDR:
+ pr->fis_addr = val;
+ break;
+ case AHCI_PORT_REG_FIS_ADDR_HI:
+ pr->fis_addr_hi = val;
+ break;
+ case AHCI_PORT_REG_IRQ_STAT:
+ pr->irq_stat &= ~val;
+ ahci_check_irq(s);
+ break;
+ case AHCI_PORT_REG_IRQ_MASK:
+ pr->irq_mask = val & 0xfdc000ff;
+ ahci_check_irq(s);
+ break;
+ case AHCI_PORT_REG_CMD:
+ /* Block any Read-only fields from being set;
+ * including LIST_ON and FIS_ON.
+ * The spec requires to set ICC bits to zero after the ICC change
+ * is done. We don't support ICC state changes, therefore always
+ * force the ICC bits to zero.
+ */
+ pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
+ (val & ~(PORT_CMD_RO_MASK | PORT_CMD_ICC_MASK));
+
+ /* 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.
+ Instead, we only submit it once - which works in most
+ cases, but is a hack. */
+ if ((pr->cmd & PORT_CMD_FIS_ON) &&
+ !s->dev[port].init_d2h_sent) {
+ ahci_init_d2h(&s->dev[port]);
+ }
- check_cmd(s, port);
- break;
- case PORT_TFDATA:
- /* Read Only. */
- break;
- case PORT_SIG:
- /* Read Only */
- break;
- case PORT_SCR_STAT:
- /* Read Only */
- break;
- case PORT_SCR_CTL:
- if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
- ((val & AHCI_SCR_SCTL_DET) == 0)) {
- ahci_reset_port(s, port);
- }
- pr->scr_ctl = val;
- break;
- case PORT_SCR_ERR:
- pr->scr_err &= ~val;
- break;
- case PORT_SCR_ACT:
- /* RW1 */
- pr->scr_act |= val;
- break;
- case PORT_CMD_ISSUE:
- pr->cmd_issue |= val;
- check_cmd(s, port);
- break;
- default:
- break;
+ check_cmd(s, port);
+ break;
+ case AHCI_PORT_REG_TFDATA:
+ case AHCI_PORT_REG_SIG:
+ case AHCI_PORT_REG_SCR_STAT:
+ /* Read Only */
+ break;
+ case AHCI_PORT_REG_SCR_CTL:
+ if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
+ ((val & AHCI_SCR_SCTL_DET) == 0)) {
+ ahci_reset_port(s, port);
+ }
+ pr->scr_ctl = val;
+ break;
+ case AHCI_PORT_REG_SCR_ERR:
+ pr->scr_err &= ~val;
+ break;
+ case AHCI_PORT_REG_SCR_ACT:
+ /* RW1 */
+ pr->scr_act |= val;
+ break;
+ case AHCI_PORT_REG_CMD_ISSUE:
+ pr->cmd_issue |= val;
+ check_cmd(s, port);
+ break;
+ default:
+ trace_ahci_port_write_unimpl(s, port, AHCIPortReg_lookup[regnum],
+ offset, val);
+ qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: "
+ "AHCI port %d register %s, offset 0x%x: 0x%"PRIx32,
+ port, AHCIPortReg_lookup[regnum], offset, val);
+ break;
}
}
@@ -341,28 +385,37 @@ static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr)
uint32_t val = 0;
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
- switch (addr) {
- case HOST_CAP:
+ enum AHCIHostReg regnum = addr / 4;
+ assert(regnum < AHCI_HOST_REG__COUNT);
+
+ switch (regnum) {
+ case AHCI_HOST_REG_CAP:
val = s->control_regs.cap;
break;
- case HOST_CTL:
+ case AHCI_HOST_REG_CTL:
val = s->control_regs.ghc;
break;
- case HOST_IRQ_STAT:
+ case AHCI_HOST_REG_IRQ_STAT:
val = s->control_regs.irqstatus;
break;
- case HOST_PORTS_IMPL:
+ case AHCI_HOST_REG_PORTS_IMPL:
val = s->control_regs.impl;
break;
- case HOST_VERSION:
+ case AHCI_HOST_REG_VERSION:
val = s->control_regs.version;
break;
+ default:
+ 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);
@@ -415,38 +468,53 @@ static void ahci_mem_write(void *opaque, hwaddr addr,
}
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 = {
@@ -532,13 +600,6 @@ static void ahci_check_cmd_bh(void *opaque)
qemu_bh_delete(ad->check_bh);
ad->check_bh = NULL;
- if ((ad->busy_slot != -1) &&
- !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) {
- /* no longer busy */
- ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
- ad->busy_slot = -1;
- }
-
check_cmd(ad->hba, ad->port_no);
}
@@ -1198,7 +1259,6 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
g_free(pretty_fis);
}
s->dev[port].done_atapi_packet = false;
- /* XXX send PIO setup FIS */
}
ide_state->error = 0;
@@ -1280,8 +1340,8 @@ out:
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];
@@ -1292,10 +1352,12 @@ static void ahci_start_transfer(IDEDMA *dma)
int is_atapi = opts & AHCI_CMD_ATAPI;
int has_sglist = 0;
+ /* PIO FIS gets written prior to transfer */
+ ahci_write_fis_pio(ad, size);
+
if (is_atapi && !ad->done_atapi_packet) {
/* already prepopulated iobuffer */
ad->done_atapi_packet = true;
- size = 0;
goto out;
}
@@ -1303,9 +1365,9 @@ static void ahci_start_transfer(IDEDMA *dma)
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) {
@@ -1315,19 +1377,11 @@ static void ahci_start_transfer(IDEDMA *dma)
}
}
+ /* Update number of transferred bytes, destroy sglist */
+ dma_buf_commit(s, size);
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);
-
- if (!(s->status & DRQ_STAT)) {
- /* done with PIO send/receive */
- ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status));
- }
}
static void ahci_start_dma(IDEDMA *dma, IDEState *s,
@@ -1425,11 +1479,16 @@ static void ahci_cmd_done(IDEDMA *dma)
trace_ahci_cmd_done(ad->hba, ad->port_no);
+ /* no longer busy */
+ if (ad->busy_slot != -1) {
+ ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
+ ad->busy_slot = -1;
+ }
+
/* update d2h status */
ahci_write_fis_d2h(ad);
- if (!ad->check_bh) {
- /* maybe we still have something to process, check later */
+ if (ad->port_regs.cmd_issue && !ad->check_bh) {
ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
qemu_bh_schedule(ad->check_bh);
}
@@ -1443,7 +1502,7 @@ static const IDEDMAOps ahci_dma_ops = {
.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,
diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h
index 1a25d6c039..2953243929 100644
--- a/hw/ide/ahci_internal.h
+++ b/hw/ide/ahci_internal.h
@@ -55,11 +55,20 @@
#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
/* global controller registers */
-#define HOST_CAP 0x00 /* host capabilities */
-#define HOST_CTL 0x04 /* global host control */
-#define HOST_IRQ_STAT 0x08 /* interrupt status */
-#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
-#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
+enum AHCIHostReg {
+ AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */
+ AHCI_HOST_REG_CTL = 1, /* GHC: global host control */
+ AHCI_HOST_REG_IRQ_STAT = 2, /* IS: interrupt status */
+ AHCI_HOST_REG_PORTS_IMPL = 3, /* PI: bitmap of implemented ports */
+ AHCI_HOST_REG_VERSION = 4, /* VS: AHCI spec. version compliancy */
+ AHCI_HOST_REG_CCC_CTL = 5, /* CCC_CTL: CCC Control */
+ AHCI_HOST_REG_CCC_PORTS = 6, /* CCC_PORTS: CCC Ports */
+ AHCI_HOST_REG_EM_LOC = 7, /* EM_LOC: Enclosure Mgmt Location */
+ AHCI_HOST_REG_EM_CTL = 8, /* EM_CTL: Enclosure Mgmt Control */
+ AHCI_HOST_REG_CAP2 = 9, /* CAP2: host capabilities, extended */
+ AHCI_HOST_REG_BOHC = 10, /* BOHC: firmare/os handoff ctrl & status */
+ AHCI_HOST_REG__COUNT = 11
+};
/* HOST_CTL bits */
#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */
@@ -75,21 +84,32 @@
#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */
/* registers for each SATA port */
-#define PORT_LST_ADDR 0x00 /* command list DMA addr */
-#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
-#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
-#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
-#define PORT_IRQ_STAT 0x10 /* interrupt status */
-#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
-#define PORT_CMD 0x18 /* port command */
-#define PORT_TFDATA 0x20 /* taskfile data */
-#define PORT_SIG 0x24 /* device TF signature */
-#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
-#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
-#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
-#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
-#define PORT_CMD_ISSUE 0x38 /* command issue */
-#define PORT_RESERVED 0x3c /* reserved */
+enum AHCIPortReg {
+ AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */
+ AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */
+ AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */
+ AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */
+ AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */
+ AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */
+ AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */
+ /* RESERVED */
+ AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */
+ AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */
+ AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */
+ AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */
+ AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */
+ AHCI_PORT_REG_SCR_ACT = 13, /* PxSACT: SATA phy register: SActive */
+ AHCI_PORT_REG_CMD_ISSUE = 14, /* PxCI: command issue */
+ AHCI_PORT_REG_SCR_NOTIF = 15, /* PxSNTF: SATA phy register: SNotification */
+ AHCI_PORT_REG_FIS_CTL = 16, /* PxFBS: Port multiplier switching ctl */
+ AHCI_PORT_REG_DEV_SLEEP = 17, /* PxDEVSLP: device sleep control */
+ /* RESERVED */
+ AHCI_PORT_REG_VENDOR_1 = 28, /* PxVS: Vendor Specific */
+ AHCI_PORT_REG_VENDOR_2 = 29,
+ AHCI_PORT_REG_VENDOR_3 = 30,
+ AHCI_PORT_REG_VENDOR_4 = 31,
+ AHCI_PORT_REG__COUNT = 32
+};
/* Port interrupt bit descriptors */
enum AHCIPortIRQ {
@@ -198,8 +218,7 @@ enum AHCIPortIRQ {
#define SATA_SIGNATURE_CDROM 0xeb140101
#define SATA_SIGNATURE_DISK 0x00000101
-#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
- /* Shouldn't this be 0x2c? */
+#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c
#define AHCI_PORT_REGS_START_ADDR 0x100
#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index c0509c8bf5..39e473f9c2 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -245,15 +245,11 @@ static uint16_t atapi_byte_count_limit(IDEState *s)
void ide_atapi_cmd_reply_end(IDEState *s)
{
int byte_count_limit, size, ret;
- trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
- s->elementary_transfer_size,
- s->io_buffer_index);
- if (s->packet_transfer_size <= 0) {
- /* end of transfer */
- ide_atapi_cmd_ok(s);
- ide_set_irq(s->bus);
- trace_ide_atapi_cmd_reply_end_eot(s, s->status);
- } else {
+ while (s->packet_transfer_size > 0) {
+ trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
+ s->elementary_transfer_size,
+ s->io_buffer_index);
+
/* see if a new sector must be read */
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
if (!s->elementary_transfer_size) {
@@ -279,14 +275,10 @@ void ide_atapi_cmd_reply_end(IDEState *s)
size = s->cd_sector_size - s->io_buffer_index;
if (size > s->elementary_transfer_size)
size = s->elementary_transfer_size;
- s->packet_transfer_size -= size;
- s->elementary_transfer_size -= size;
- s->io_buffer_index += size;
- ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
- size, ide_atapi_cmd_reply_end);
} else {
/* a new transfer is needed */
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+ ide_set_irq(s->bus);
byte_count_limit = atapi_byte_count_limit(s);
trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit);
size = s->packet_transfer_size;
@@ -304,15 +296,27 @@ void ide_atapi_cmd_reply_end(IDEState *s)
if (size > (s->cd_sector_size - s->io_buffer_index))
size = (s->cd_sector_size - s->io_buffer_index);
}
- s->packet_transfer_size -= size;
- s->elementary_transfer_size -= size;
- s->io_buffer_index += size;
- ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
- size, ide_atapi_cmd_reply_end);
- ide_set_irq(s->bus);
trace_ide_atapi_cmd_reply_end_new(s, s->status);
}
+ s->packet_transfer_size -= size;
+ s->elementary_transfer_size -= size;
+ s->io_buffer_index += size;
+
+ /* Some adapters process PIO data right away. In that case, we need
+ * to avoid mutual recursion between ide_transfer_start
+ * and ide_atapi_cmd_reply_end.
+ */
+ if (!ide_transfer_start_norecurse(s,
+ s->io_buffer + s->io_buffer_index - size,
+ size, ide_atapi_cmd_reply_end)) {
+ return;
+ }
}
+
+ /* end of transfer */
+ trace_ide_atapi_cmd_reply_end_eot(s, s->status);
+ ide_atapi_cmd_ok(s);
+ ide_set_irq(s->bus);
}
/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */
diff --git a/hw/ide/core.c b/hw/ide/core.c
index cc9ca28c33..2c62efc536 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -523,18 +523,28 @@ static void ide_clear_retry(IDEState *s)
}
/* prepare data transfer and tell what to do after */
-void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
- EndTransferFunc *end_transfer_func)
+bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func)
{
- s->end_transfer_func = end_transfer_func;
s->data_ptr = buf;
s->data_end = buf + size;
ide_set_retry(s);
if (!(s->status & ERR_STAT)) {
s->status |= DRQ_STAT;
}
- if (s->bus->dma->ops->start_transfer) {
- s->bus->dma->ops->start_transfer(s->bus->dma);
+ if (!s->bus->dma->ops->pio_transfer) {
+ s->end_transfer_func = end_transfer_func;
+ return false;
+ }
+ s->bus->dma->ops->pio_transfer(s->bus->dma);
+ return true;
+}
+
+void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func)
+{
+ if (ide_transfer_start_norecurse(s, buf, size, end_transfer_func)) {
+ end_transfer_func(s);
}
}
@@ -545,27 +555,18 @@ static void ide_cmd_done(IDEState *s)
}
}
-static void ide_transfer_halt(IDEState *s,
- void(*end_transfer_func)(IDEState *),
- bool notify)
+static void ide_transfer_halt(IDEState *s)
{
- s->end_transfer_func = end_transfer_func;
+ s->end_transfer_func = ide_transfer_stop;
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);
+ ide_transfer_halt(s);
+ ide_cmd_done(s);
}
int64_t ide_get_sector(IDEState *s)
@@ -1362,7 +1363,7 @@ static bool cmd_nop(IDEState *s, uint8_t cmd)
static bool cmd_device_reset(IDEState *s, uint8_t cmd)
{
/* Halt PIO (in the DRQ phase), then DMA */
- ide_transfer_cancel(s);
+ ide_transfer_halt(s);
ide_cancel_dma_sync(s);
/* Reset any PIO commands, reset signature, etc */
diff --git a/hw/ide/trace-events b/hw/ide/trace-events
index 5c0e59ec42..65d6f9034d 100644
--- a/hw/ide/trace-events
+++ b/hw/ide/trace-events
@@ -63,16 +63,23 @@ ide_atapi_cmd_read_dma_cb_aio(void *s, int lba, int n) "IDEState: %p; aio read:
ide_atapi_cmd_packet(void *s, uint16_t limit, const char *packet) "IDEState: %p; limit=0x%x packet: %s"
# hw/ide/ahci.c
-ahci_port_read(void *s, int port, int offset, uint32_t ret) "ahci(%p)[%d]: port read @ 0x%x: 0x%08x"
+ahci_port_read(void *s, int port, const char *reg, int offset, uint32_t ret) "ahci(%p)[%d]: port read [reg:%s] @ 0x%x: 0x%08x"
+ahci_port_read_default(void *s, int port, const char *reg, int offset) "ahci(%p)[%d]: unimplemented port read [reg:%s] @ 0x%x"
ahci_irq_raise(void *s) "ahci(%p): raise irq"
ahci_irq_lower(void *s) "ahci(%p): lower irq"
ahci_check_irq(void *s, uint32_t old, uint32_t new) "ahci(%p): check irq 0x%08x --> 0x%08x"
ahci_trigger_irq(void *s, int port, const char *name, uint32_t val, uint32_t old, uint32_t new, uint32_t effective) "ahci(%p)[%d]: trigger irq +%s (0x%08x); irqstat: 0x%08x --> 0x%08x; effective: 0x%08x"
-ahci_port_write(void *s, int port, int offset, uint32_t val) "ahci(%p)[%d]: port write @ 0x%x: 0x%08x"
+ahci_port_write(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: port write [reg:%s] @ 0x%x: 0x%08x"
+ahci_port_write_unimpl(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: unimplemented port write [reg:%s] @ 0x%x: 0x%08x"
ahci_mem_read_32(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x"
+ahci_mem_read_32_default(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x"
+ahci_mem_read_32_host(void *s, const char *reg, uint64_t addr, uint32_t val) "ahci(%p): mem read [reg:%s] @ 0x%"PRIx64": 0x%08x"
+ahci_mem_read_32_host_default(void *s, const char *reg, uint64_t addr) "ahci(%p): unimplemented mem read [reg:%s] @ 0x%"PRIx64
ahci_mem_read(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): read%u @ 0x%"PRIx64": 0x%016"PRIx64
ahci_mem_write(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u @ 0x%"PRIx64": 0x%016"PRIx64
-ahci_mem_write_unknown(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64
+ahci_mem_write_host_unimpl(void *s, unsigned size, const char *reg, uint64_t addr) "ahci(%p) unimplemented write%u [reg:%s] @ 0x%"PRIx64
+ahci_mem_write_host(void *s, unsigned size, const char *reg, uint64_t addr, uint64_t val) "ahci(%p) write%u [reg:%s] @ 0x%"PRIx64": 0x%016"PRIx64
+ahci_mem_write_unimpl(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64
ahci_set_signature(void *s, int port, uint8_t nsector, uint8_t sector, uint8_t lcyl, uint8_t hcyl, uint32_t sig) "ahci(%p)[%d]: set signature sector:0x%02x nsector:0x%02x lcyl:0x%02x hcyl:0x%02x (cumulatively: 0x%08x)"
ahci_reset_port(void *s, int port) "ahci(%p)[%d]: reset port"
ahci_unmap_fis_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL FIS address"
@@ -101,7 +108,7 @@ handle_cmd_badport(void *s, int port) "ahci(%p)[%d]: guest accessed unused port"
handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cmd FIS"
handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80"
handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x"
-ahci_start_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist"
+ahci_pio_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist"
ahci_start_dma(void *s, int port) "ahci(%p)[%d]: start dma"
ahci_dma_prepare_buf(void *s, int port, int32_t io_buffer_size, int32_t limit) "ahci(%p)[%d]: prepare buf limit=%"PRId32" prepared=%"PRId32
ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed"
diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h
index 88212f59df..594081e57f 100644
--- a/include/hw/ide/internal.h
+++ b/include/hw/ide/internal.h
@@ -444,7 +444,7 @@ struct IDEState {
struct IDEDMAOps {
DMAStartFunc *start_dma;
- DMAVoidFunc *start_transfer;
+ DMAVoidFunc *pio_transfer;
DMAInt32Func *prepare_buf;
DMAu32Func *commit_buf;
DMAIntFunc *rw_buf;
@@ -623,6 +623,8 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val);
void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func);
+bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func);
void ide_transfer_stop(IDEState *s);
void ide_set_inactive(IDEState *s, bool more);
BlockAIOCB *ide_issue_trim(
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 400d8890e7..d098a104bb 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -179,6 +179,7 @@ check-qtest-generic-y = tests/qmp-test$(EXESUF)
gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c
check-qtest-generic-y += tests/device-introspect-test$(EXESUF)
gcov-files-generic-y = qdev-monitor.c qmp.c
+check-qtest-generic-y += tests/cdrom-test$(EXESUF)
gcov-files-ipack-y += hw/ipack/ipack.c
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
@@ -844,6 +845,7 @@ tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
tests/numa-test$(EXESUF): tests/numa-test.o
tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o
tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y)
+tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y)
tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
diff --git a/tests/boot-sector.c b/tests/boot-sector.c
index c373f0e715..7824286b9a 100644
--- a/tests/boot-sector.c
+++ b/tests/boot-sector.c
@@ -68,8 +68,11 @@ static uint8_t x86_boot_sector[512] = {
};
/* For s390x, use a mini "kernel" with the appropriate signature */
-static const uint8_t s390x_psw[] = {
- 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00
+static const uint8_t s390x_psw_and_magic[] = {
+ 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, /* Program status word */
+ 0x02, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x50, /* Magic: */
+ 0x02, 0x00, 0x00, 0x68, 0x60, 0x00, 0x00, 0x50, /* see linux_s390_magic */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */
};
static const uint8_t s390x_code[] = {
0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */
@@ -110,7 +113,7 @@ int boot_sector_init(char *fname)
} else if (g_str_equal(arch, "s390x")) {
len = 0x10000 + sizeof(s390x_code);
boot_code = g_malloc0(len);
- memcpy(boot_code, s390x_psw, sizeof(s390x_psw));
+ memcpy(boot_code, s390x_psw_and_magic, sizeof(s390x_psw_and_magic));
memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code));
} else {
g_assert_not_reached();
diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c
new file mode 100644
index 0000000000..7a1fce5dfb
--- /dev/null
+++ b/tests/cdrom-test.c
@@ -0,0 +1,222 @@
+/*
+ * Various tests for emulated CD-ROM drives.
+ *
+ * Copyright (c) 2018 Red Hat Inc.
+ *
+ * Author:
+ * Thomas Huth <thuth@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2
+ * or later. See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "boot-sector.h"
+#include "qapi/qmp/qdict.h"
+
+static char isoimage[] = "cdrom-boot-iso-XXXXXX";
+
+static int exec_genisoimg(const char **args)
+{
+ gchar *out_err = NULL;
+ gint exit_status = -1;
+ bool success;
+
+ success = g_spawn_sync(NULL, (gchar **)args, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL,
+ NULL, NULL, NULL, &out_err, &exit_status, NULL);
+ if (!success) {
+ return -ENOENT;
+ }
+ if (out_err) {
+ fputs(out_err, stderr);
+ g_free(out_err);
+ }
+
+ return exit_status;
+}
+
+static int prepare_image(const char *arch, char *isoimage)
+{
+ char srcdir[] = "cdrom-test-dir-XXXXXX";
+ char *codefile = NULL;
+ int ifh, ret = -1;
+ const char *args[] = {
+ "genisoimage", "-quiet", "-l", "-no-emul-boot",
+ "-b", NULL, "-o", isoimage, srcdir, NULL
+ };
+
+ ifh = mkstemp(isoimage);
+ if (ifh < 0) {
+ perror("Error creating temporary iso image file");
+ return -1;
+ }
+ if (!mkdtemp(srcdir)) {
+ perror("Error creating temporary directory");
+ goto cleanup;
+ }
+
+ if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") ||
+ g_str_equal(arch, "s390x")) {
+ codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir);
+ ret = boot_sector_init(codefile);
+ if (ret) {
+ goto cleanup;
+ }
+ } else {
+ /* Just create a dummy file */
+ char txt[] = "empty disc";
+ codefile = g_strdup_printf("%s/readme.txt", srcdir);
+ if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) {
+ fprintf(stderr, "Failed to create '%s'\n", codefile);
+ goto cleanup;
+ }
+ }
+
+ args[5] = strchr(codefile, '/') + 1;
+ ret = exec_genisoimg(args);
+ if (ret) {
+ fprintf(stderr, "genisoimage failed: %i\n", ret);
+ }
+
+ unlink(codefile);
+
+cleanup:
+ g_free(codefile);
+ rmdir(srcdir);
+ close(ifh);
+
+ return ret;
+}
+
+/**
+ * Check that at least the -cdrom parameter is basically working, i.e. we can
+ * see the filename of the ISO image in the output of "info block" afterwards
+ */
+static void test_cdrom_param(gconstpointer data)
+{
+ QTestState *qts;
+ char *resp;
+
+ qts = qtest_startf("-M %s -cdrom %s", (const char *)data, isoimage);
+ resp = qtest_hmp(qts, "info block");
+ g_assert(strstr(resp, isoimage) != 0);
+ g_free(resp);
+ qtest_quit(qts);
+}
+
+static void add_cdrom_param_tests(const char **machines)
+{
+ while (*machines) {
+ char *testname = g_strdup_printf("cdrom/param/%s", *machines);
+ qtest_add_data_func(testname, *machines, test_cdrom_param);
+ g_free(testname);
+ machines++;
+ }
+}
+
+static void test_cdboot(gconstpointer data)
+{
+ QTestState *qts;
+
+ qts = qtest_startf("-accel kvm:tcg -no-shutdown %s%s", (const char *)data,
+ isoimage);
+ boot_sector_test(qts);
+ qtest_quit(qts);
+}
+
+static void add_x86_tests(void)
+{
+ qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot);
+ qtest_add_data_func("cdrom/boot/virtio-scsi",
+ "-device virtio-scsi -device scsi-cd,drive=cdr "
+ "-blockdev file,node-name=cdr,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/isapc", "-M isapc "
+ "-drive if=ide,media=cdrom,file=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/am53c974",
+ "-device am53c974 -device scsi-cd,drive=cd1 "
+ "-drive if=none,id=cd1,format=raw,file=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/dc390",
+ "-device dc390 -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/lsi53c895a",
+ "-device lsi53c895a -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/megasas", "-M q35 "
+ "-device megasas -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+ qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 "
+ "-device megasas-gen2 -device scsi-cd,drive=cd1 "
+ "-blockdev file,node-name=cd1,filename=", test_cdboot);
+}
+
+static void add_s390x_tests(void)
+{
+ qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot);
+ qtest_add_data_func("cdrom/boot/virtio-scsi",
+ "-device virtio-scsi -device scsi-cd,drive=cdr "
+ "-blockdev file,node-name=cdr,filename=", test_cdboot);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ const char *arch = qtest_get_arch();
+ const char *genisocheck[] = { "genisoimage", "-version", NULL };
+
+ g_test_init(&argc, &argv, NULL);
+
+ if (exec_genisoimg(genisocheck)) {
+ /* genisoimage not available - so can't run tests */
+ return 0;
+ }
+
+ ret = prepare_image(arch, isoimage);
+ if (ret) {
+ return ret;
+ }
+
+ if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) {
+ add_x86_tests();
+ } else if (g_str_equal(arch, "s390x")) {
+ add_s390x_tests();
+ } else if (g_str_equal(arch, "ppc64")) {
+ const char *ppcmachines[] = {
+ "pseries", "mac99", "g3beige", "40p", "prep", NULL
+ };
+ add_cdrom_param_tests(ppcmachines);
+ } else if (g_str_equal(arch, "sparc")) {
+ const char *sparcmachines[] = {
+ "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4",
+ "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL
+ };
+ add_cdrom_param_tests(sparcmachines);
+ } else if (g_str_equal(arch, "sparc64")) {
+ const char *sparc64machines[] = {
+ "niagara", "sun4u", "sun4v", NULL
+ };
+ add_cdrom_param_tests(sparc64machines);
+ } else if (!strncmp(arch, "mips64", 6)) {
+ const char *mips64machines[] = {
+ "magnum", "malta", "mips", "pica61", NULL
+ };
+ add_cdrom_param_tests(mips64machines);
+ } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) {
+ const char *armmachines[] = {
+ "realview-eb", "realview-eb-mpcore", "realview-pb-a8",
+ "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15",
+ "vexpress-a9", "virt", NULL
+ };
+ add_cdrom_param_tests(armmachines);
+ } else {
+ const char *nonemachine[] = { "none", NULL };
+ add_cdrom_param_tests(nonemachine);
+ }
+
+ ret = g_test_run();
+
+ unlink(isoimage);
+
+ return ret;
+}
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index bc201d762b..7264e085d0 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -90,6 +90,7 @@ struct AHCICommand {
uint32_t interrupts;
uint64_t xbytes;
uint32_t prd_size;
+ uint32_t sector_size;
uint64_t buffer;
AHCICommandProp *props;
/* Data to be transferred to the guest */
@@ -477,10 +478,10 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
g_free(d2h);
}
-void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
- uint8_t slot, size_t buffsize)
+void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd)
{
PIOSetupFIS *pio = g_malloc0(0x20);
+ uint8_t port = cmd->port;
/* We cannot check the Status or E_Status registers, because
* the status may have again changed between the PIO Setup FIS
@@ -488,15 +489,22 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20);
g_assert_cmphex(pio->fis_type, ==, 0x5f);
- /* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire
- * transfer size in a uint16_t field. The maximum transfer size can
- * eclipse this; the field is meant to convey the size of data per
- * each Data FIS, not the entire operation as a whole. For now,
- * we will sanity check the broken case where applicable. */
- if (buffsize <= UINT16_MAX) {
- g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize);
+ /* Data transferred by PIO will either be:
+ * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or
+ * (2) Actual data from the drive.
+ * If we do both, (2) winds up erasing any evidence of (1).
+ */
+ if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) {
+ g_assert(le16_to_cpu(pio->tx_count) == 12 ||
+ le16_to_cpu(pio->tx_count) == 16);
+ } else {
+ /* The AHCI test suite here does not test any PIO command that specifies
+ * a DRQ block larger than one sector (like 0xC4), so this should always
+ * be one sector or less. */
+ size_t pio_len = ((cmd->xbytes % cmd->sector_size) ?
+ (cmd->xbytes % cmd->sector_size) : cmd->sector_size);
+ g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len);
}
-
g_free(pio);
}
@@ -796,7 +804,7 @@ static void command_header_init(AHCICommand *cmd)
static void command_table_init(AHCICommand *cmd)
{
RegH2DFIS *fis = &(cmd->fis);
- uint16_t sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ uint16_t sect_count = (cmd->xbytes / cmd->sector_size);
fis->fis_type = REG_H2D_FIS;
fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */
@@ -819,7 +827,7 @@ static void command_table_init(AHCICommand *cmd)
if (cmd->props->lba28 || cmd->props->lba48) {
fis->device = ATA_DEVICE_LBA;
}
- fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ fis->count = (cmd->xbytes / cmd->sector_size);
}
fis->icc = 0x00;
fis->control = 0x00;
@@ -831,9 +839,9 @@ void ahci_command_enable_atapi_dma(AHCICommand *cmd)
RegH2DFIS *fis = &(cmd->fis);
g_assert(cmd->props->atapi);
fis->feature_low |= 0x01;
- cmd->interrupts &= ~AHCI_PX_IS_PSS;
+ /* PIO is still used to transfer the ATAPI command */
+ g_assert(cmd->props->pio);
cmd->props->dma = true;
- cmd->props->pio = false;
/* BUG: We expect the DMA Setup interrupt for DMA commands */
/* cmd->interrupts |= AHCI_PX_IS_DSS; */
}
@@ -845,7 +853,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
g_assert(props);
cmd = g_new0(AHCICommand, 1);
- g_assert(!(props->dma && props->pio));
+ g_assert(!(props->dma && props->pio) || props->atapi);
g_assert(!(props->lba28 && props->lba48));
g_assert(!(props->read && props->write));
g_assert(!props->size || props->data);
@@ -857,6 +865,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
cmd->xbytes = props->size;
cmd->prd_size = 4096;
cmd->buffer = 0xabad1dea;
+ cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE;
if (!cmd->props->ncq) {
cmd->interrupts = AHCI_PX_IS_DHRS;
@@ -1033,7 +1042,7 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
{
unsigned char *cbd = cmd->atapi_cmd;
- uint64_t nsectors = xbytes / 2048;
+ uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE;
uint32_t tmp;
g_assert(cbd);
@@ -1080,7 +1089,7 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
cmd->prd_size = prd_size;
}
cmd->xbytes = xbytes;
- sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ sect_count = (cmd->xbytes / cmd->sector_size);
if (cmd->props->ncq) {
NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
@@ -1216,7 +1225,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
ahci_port_check_d2h_sanity(ahci, port, slot);
}
if (cmd->props->pio) {
- ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes);
+ ahci_port_check_pio_sanity(ahci, cmd);
}
}
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
index 715ca1e226..13f6d87b75 100644
--- a/tests/libqos/ahci.h
+++ b/tests/libqos/ahci.h
@@ -596,8 +596,7 @@ void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
uint32_t intr_mask);
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
-void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
- uint8_t slot, size_t buffsize);
+void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
/* Misc */