Pull request

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJbGsDwAAoJEH3vgQaq/DkOpiUP/24TYTN6FcfBRQbpHJp2/p5Z
 Z0uu1Wo3OFVEPQ63IOqoubMgYJwiw7DnM9dVtY7TfMsvlEX+FVGPp59Rd1L0ciec
 /c4L2ZhkLFk0VZB8moVpBCyJHHcHl0Do0v6dkxvJC5B82lkL59M3oCQzr9ADRJ+t
 7IpgvnYBHQ2P1hEOF+df/2mbUDErioIb+3yEJEwfP/4/wvJZxYrtykZKYQOySHty
 NDyHoTgbIVecE5FeDMyij4WFt25F/OafH6p4Z1w5ieqmN7LT5sfR6oLzpyYmf7fM
 uDpCGQrQdeaFNgwcPbxnpuXFig0Tf2E8g319uJAtLfEUxy68yEcNgiGpavhAprxN
 P/vj+PR4n+eKv74sRG+td6nsIgmm9AjKr2rV1CgZcIL0F6sMu104E6kthAVzCYJD
 wQY/t14RdSdLBlVvsC+A0I1WUTtir/cjT0+ZE9w5z1nJ25tqJ3wli8MK/73cGZEk
 sNRHmHCOlP7MrkliOmXD3DVd1MpnIjHfRuHhMvOPSpYxO5qm00v6Gbg8ypmvCT3q
 iNgrXC9L/WJO3+RKezGspcGqGvnTQeVcZvu3y3Np6gCERGJ2WFqcNnu1h0uoGlNo
 NoRLToLuWyYSfqhk8URW/DclUUGWgnlDjSRxcv1eA8c9aWXnUroyXzQBqUhwQEbC
 yamyJ2rujwjDa8uwCO3F
 =QZ8V
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Peter Maydell 2018-06-11 11:12:46 +01:00
commit a7a7309ca5
12 changed files with 572 additions and 244 deletions

View File

@ -1003,6 +1003,7 @@ F: hw/block/cdrom.c
F: hw/block/hd-geometry.c F: hw/block/hd-geometry.c
F: tests/ide-test.c F: tests/ide-test.c
F: tests/ahci-test.c F: tests/ahci-test.c
F: tests/cdrom-test.c
F: tests/libqos/ahci* F: tests/libqos/ahci*
T: git git://github.com/jnsnow/qemu.git ide T: git git://github.com/jnsnow/qemu.git ide

View File

@ -27,6 +27,7 @@
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/log.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "sysemu/dma.h" #include "sysemu/dma.h"
#include "hw/ide/internal.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_clb_address(AHCIDevice *ad);
static void ahci_unmap_fis_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] = { static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = {
[AHCI_PORT_IRQ_BIT_DHRS] = "DHRS", [AHCI_PORT_IRQ_BIT_DHRS] = "DHRS",
[AHCI_PORT_IRQ_BIT_PSS] = "PSS", [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" [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; uint32_t val;
AHCIPortRegs *pr; AHCIPortRegs *pr = &s->dev[port].port_regs;
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) { switch (regnum) {
case PORT_LST_ADDR: case AHCI_PORT_REG_LST_ADDR:
val = pr->lst_addr; val = pr->lst_addr;
break; break;
case PORT_LST_ADDR_HI: case AHCI_PORT_REG_LST_ADDR_HI:
val = pr->lst_addr_hi; val = pr->lst_addr_hi;
break; break;
case PORT_FIS_ADDR: case AHCI_PORT_REG_FIS_ADDR:
val = pr->fis_addr; val = pr->fis_addr;
break; break;
case PORT_FIS_ADDR_HI: case AHCI_PORT_REG_FIS_ADDR_HI:
val = pr->fis_addr_hi; val = pr->fis_addr_hi;
break; break;
case PORT_IRQ_STAT: case AHCI_PORT_REG_IRQ_STAT:
val = pr->irq_stat; val = pr->irq_stat;
break; break;
case PORT_IRQ_MASK: case AHCI_PORT_REG_IRQ_MASK:
val = pr->irq_mask; val = pr->irq_mask;
break; break;
case PORT_CMD: case AHCI_PORT_REG_CMD:
val = pr->cmd; val = pr->cmd;
break; break;
case PORT_TFDATA: case AHCI_PORT_REG_TFDATA:
val = pr->tfdata; val = pr->tfdata;
break; break;
case PORT_SIG: case AHCI_PORT_REG_SIG:
val = pr->sig; val = pr->sig;
break; break;
case PORT_SCR_STAT: case AHCI_PORT_REG_SCR_STAT:
if (s->dev[port].port.ifs[0].blk) { if (s->dev[port].port.ifs[0].blk) {
val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP | val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP |
SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE; 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; val = SATA_SCR_SSTATUS_DET_NODEV;
} }
break; break;
case PORT_SCR_CTL: case AHCI_PORT_REG_SCR_CTL:
val = pr->scr_ctl; val = pr->scr_ctl;
break; break;
case PORT_SCR_ERR: case AHCI_PORT_REG_SCR_ERR:
val = pr->scr_err; val = pr->scr_err;
break; break;
case PORT_SCR_ACT: case AHCI_PORT_REG_SCR_ACT:
val = pr->scr_act; val = pr->scr_act;
break; break;
case PORT_CMD_ISSUE: case AHCI_PORT_REG_CMD_ISSUE:
val = pr->cmd_issue; val = pr->cmd_issue;
break; break;
case PORT_RESERVED:
default: default:
trace_ahci_port_read_default(s, port, AHCIPortReg_lookup[regnum],
offset);
val = 0; val = 0;
} }
trace_ahci_port_read(s, port, offset, val); trace_ahci_port_read(s, port, AHCIPortReg_lookup[regnum], offset, val);
return val; return val;
} }
static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) static void ahci_irq_raise(AHCIState *s)
{ {
DeviceState *dev_state = s->container; DeviceState *dev_state = s->container;
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), 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; DeviceState *dev_state = s->container;
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), 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); trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus);
if (s->control_regs.irqstatus && if (s->control_regs.irqstatus &&
(s->control_regs.ghc & HOST_CTL_IRQ_EN)) { (s->control_regs.ghc & HOST_CTL_IRQ_EN)) {
ahci_irq_raise(s, NULL); ahci_irq_raise(s);
} else { } else {
ahci_irq_lower(s, NULL); ahci_irq_lower(s);
} }
} }
@ -253,85 +294,88 @@ static int ahci_cond_start_engines(AHCIDevice *ad)
return 0; 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; 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 (regnum) {
switch (offset) { case AHCI_PORT_REG_LST_ADDR:
case PORT_LST_ADDR: pr->lst_addr = val;
pr->lst_addr = val; break;
break; case AHCI_PORT_REG_LST_ADDR_HI:
case PORT_LST_ADDR_HI: pr->lst_addr_hi = val;
pr->lst_addr_hi = val; break;
break; case AHCI_PORT_REG_FIS_ADDR:
case PORT_FIS_ADDR: pr->fis_addr = val;
pr->fis_addr = val; break;
break; case AHCI_PORT_REG_FIS_ADDR_HI:
case PORT_FIS_ADDR_HI: pr->fis_addr_hi = val;
pr->fis_addr_hi = val; break;
break; case AHCI_PORT_REG_IRQ_STAT:
case PORT_IRQ_STAT: pr->irq_stat &= ~val;
pr->irq_stat &= ~val; ahci_check_irq(s);
ahci_check_irq(s); break;
break; case AHCI_PORT_REG_IRQ_MASK:
case PORT_IRQ_MASK: pr->irq_mask = val & 0xfdc000ff;
pr->irq_mask = val & 0xfdc000ff; ahci_check_irq(s);
ahci_check_irq(s); break;
break; case AHCI_PORT_REG_CMD:
case PORT_CMD: /* Block any Read-only fields from being set;
/* Block any Read-only fields from being set; * including LIST_ON and FIS_ON.
* including LIST_ON and FIS_ON. * The spec requires to set ICC bits to zero after the ICC change
* The spec requires to set ICC bits to zero after the ICC change * is done. We don't support ICC state changes, therefore always
* is done. We don't support ICC state changes, therefore always * force the ICC bits to zero.
* force the ICC bits to zero. */
*/ pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | (val & ~(PORT_CMD_RO_MASK | PORT_CMD_ICC_MASK));
(val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK));
/* Check FIS RX and CLB engines */ /* Check FIS RX and CLB engines */
ahci_cond_start_engines(&s->dev[port]); ahci_cond_start_engines(&s->dev[port]);
/* XXX usually the FIS would be pending on the bus here and /* XXX usually the FIS would be pending on the bus here and
issuing deferred until the OS enables FIS receival. issuing deferred until the OS enables FIS receival.
Instead, we only submit it once - which works in most Instead, we only submit it once - which works in most
cases, but is a hack. */ cases, but is a hack. */
if ((pr->cmd & PORT_CMD_FIS_ON) && if ((pr->cmd & PORT_CMD_FIS_ON) &&
!s->dev[port].init_d2h_sent) { !s->dev[port].init_d2h_sent) {
ahci_init_d2h(&s->dev[port]); ahci_init_d2h(&s->dev[port]);
} }
check_cmd(s, port); check_cmd(s, port);
break; break;
case PORT_TFDATA: case AHCI_PORT_REG_TFDATA:
/* Read Only. */ case AHCI_PORT_REG_SIG:
break; case AHCI_PORT_REG_SCR_STAT:
case PORT_SIG: /* Read Only */
/* Read Only */ break;
break; case AHCI_PORT_REG_SCR_CTL:
case PORT_SCR_STAT: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
/* Read Only */ ((val & AHCI_SCR_SCTL_DET) == 0)) {
break; ahci_reset_port(s, port);
case PORT_SCR_CTL: }
if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && pr->scr_ctl = val;
((val & AHCI_SCR_SCTL_DET) == 0)) { break;
ahci_reset_port(s, port); case AHCI_PORT_REG_SCR_ERR:
} pr->scr_err &= ~val;
pr->scr_ctl = val; break;
break; case AHCI_PORT_REG_SCR_ACT:
case PORT_SCR_ERR: /* RW1 */
pr->scr_err &= ~val; pr->scr_act |= val;
break; break;
case PORT_SCR_ACT: case AHCI_PORT_REG_CMD_ISSUE:
/* RW1 */ pr->cmd_issue |= val;
pr->scr_act |= val; check_cmd(s, port);
break; break;
case PORT_CMD_ISSUE: default:
pr->cmd_issue |= val; trace_ahci_port_write_unimpl(s, port, AHCIPortReg_lookup[regnum],
check_cmd(s, port); offset, val);
break; qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: "
default: "AHCI port %d register %s, offset 0x%x: 0x%"PRIx32,
break; 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; uint32_t val = 0;
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
switch (addr) { enum AHCIHostReg regnum = addr / 4;
case HOST_CAP: assert(regnum < AHCI_HOST_REG__COUNT);
switch (regnum) {
case AHCI_HOST_REG_CAP:
val = s->control_regs.cap; val = s->control_regs.cap;
break; break;
case HOST_CTL: case AHCI_HOST_REG_CTL:
val = s->control_regs.ghc; val = s->control_regs.ghc;
break; break;
case HOST_IRQ_STAT: case AHCI_HOST_REG_IRQ_STAT:
val = s->control_regs.irqstatus; val = s->control_regs.irqstatus;
break; break;
case HOST_PORTS_IMPL: case AHCI_HOST_REG_PORTS_IMPL:
val = s->control_regs.impl; val = s->control_regs.impl;
break; break;
case HOST_VERSION: case AHCI_HOST_REG_VERSION:
val = s->control_regs.version; val = s->control_regs.version;
break; 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) && } else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
(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)))) {
val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK); addr & AHCI_PORT_ADDR_OFFSET_MASK);
} else {
trace_ahci_mem_read_32_default(s, addr, val);
} }
trace_ahci_mem_read_32(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) { if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
switch (addr) { enum AHCIHostReg regnum = addr / 4;
case HOST_CAP: /* R/WO, RO */ assert(regnum < AHCI_HOST_REG__COUNT);
/* FIXME handle R/WO */
break; switch (regnum) {
case HOST_CTL: /* R/W */ case AHCI_HOST_REG_CAP: /* R/WO, RO */
if (val & HOST_CTL_RESET) { /* FIXME handle R/WO */
ahci_reset(s); break;
} else { case AHCI_HOST_REG_CTL: /* R/W */
s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; if (val & HOST_CTL_RESET) {
ahci_check_irq(s); ahci_reset(s);
} } else {
break; s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
case HOST_IRQ_STAT: /* R/WC, RO */
s->control_regs.irqstatus &= ~val;
ahci_check_irq(s); ahci_check_irq(s);
break; }
case HOST_PORTS_IMPL: /* R/WO, RO */ break;
/* FIXME handle R/WO */ case AHCI_HOST_REG_IRQ_STAT: /* R/WC, RO */
break; s->control_regs.irqstatus &= ~val;
case HOST_VERSION: /* RO */ ahci_check_irq(s);
/* FIXME report write? */ break;
break; case AHCI_HOST_REG_PORTS_IMPL: /* R/WO, RO */
default: /* FIXME handle R/WO */
trace_ahci_mem_write_unknown(s, size, addr, val); 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) && } else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
(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, ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK, val); 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 const MemoryRegionOps ahci_mem_ops = {
@ -532,13 +600,6 @@ static void ahci_check_cmd_bh(void *opaque)
qemu_bh_delete(ad->check_bh); qemu_bh_delete(ad->check_bh);
ad->check_bh = NULL; 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); 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); g_free(pretty_fis);
} }
s->dev[port].done_atapi_packet = false; s->dev[port].done_atapi_packet = false;
/* XXX send PIO setup FIS */
} }
ide_state->error = 0; ide_state->error = 0;
@ -1280,8 +1340,8 @@ out:
return 0; return 0;
} }
/* DMA dev <-> ram */ /* Transfer PIO data between RAM and device */
static void ahci_start_transfer(IDEDMA *dma) static void ahci_pio_transfer(IDEDMA *dma)
{ {
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0]; 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 is_atapi = opts & AHCI_CMD_ATAPI;
int has_sglist = 0; int has_sglist = 0;
/* PIO FIS gets written prior to transfer */
ahci_write_fis_pio(ad, size);
if (is_atapi && !ad->done_atapi_packet) { if (is_atapi && !ad->done_atapi_packet) {
/* already prepopulated iobuffer */ /* already prepopulated iobuffer */
ad->done_atapi_packet = true; ad->done_atapi_packet = true;
size = 0;
goto out; goto out;
} }
@ -1303,9 +1365,9 @@ static void ahci_start_transfer(IDEDMA *dma)
has_sglist = 1; has_sglist = 1;
} }
trace_ahci_start_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read", trace_ahci_pio_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read",
size, is_atapi ? "atapi" : "ata", size, is_atapi ? "atapi" : "ata",
has_sglist ? "" : "o"); has_sglist ? "" : "o");
if (has_sglist && size) { if (has_sglist && size) {
if (is_write) { 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: out:
/* declare that we processed everything */ /* declare that we processed everything */
s->data_ptr = s->data_end; 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, 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); 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 */ /* update d2h status */
ahci_write_fis_d2h(ad); ahci_write_fis_d2h(ad);
if (!ad->check_bh) { if (ad->port_regs.cmd_issue && !ad->check_bh) {
/* maybe we still have something to process, check later */
ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
qemu_bh_schedule(ad->check_bh); qemu_bh_schedule(ad->check_bh);
} }
@ -1443,7 +1502,7 @@ static const IDEDMAOps ahci_dma_ops = {
.start_dma = ahci_start_dma, .start_dma = ahci_start_dma,
.restart = ahci_restart, .restart = ahci_restart,
.restart_dma = ahci_restart_dma, .restart_dma = ahci_restart_dma,
.start_transfer = ahci_start_transfer, .pio_transfer = ahci_pio_transfer,
.prepare_buf = ahci_dma_prepare_buf, .prepare_buf = ahci_dma_prepare_buf,
.commit_buf = ahci_commit_buf, .commit_buf = ahci_commit_buf,
.rw_buf = ahci_dma_rw_buf, .rw_buf = ahci_dma_rw_buf,

View File

@ -55,11 +55,20 @@
#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ #define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
/* global controller registers */ /* global controller registers */
#define HOST_CAP 0x00 /* host capabilities */ enum AHCIHostReg {
#define HOST_CTL 0x04 /* global host control */ AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */
#define HOST_IRQ_STAT 0x08 /* interrupt status */ AHCI_HOST_REG_CTL = 1, /* GHC: global host control */
#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ AHCI_HOST_REG_IRQ_STAT = 2, /* IS: interrupt status */
#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ 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 */ /* HOST_CTL bits */
#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ #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 */ #define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */
/* registers for each SATA port */ /* registers for each SATA port */
#define PORT_LST_ADDR 0x00 /* command list DMA addr */ enum AHCIPortReg {
#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */
#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */
#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */
#define PORT_IRQ_STAT 0x10 /* interrupt status */ AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */
#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */
#define PORT_CMD 0x18 /* port command */ AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */
#define PORT_TFDATA 0x20 /* taskfile data */ AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */
#define PORT_SIG 0x24 /* device TF signature */ /* RESERVED */
#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */
#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */
#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */
#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */
#define PORT_CMD_ISSUE 0x38 /* command issue */ AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */
#define PORT_RESERVED 0x3c /* reserved */ 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 */ /* Port interrupt bit descriptors */
enum AHCIPortIRQ { enum AHCIPortIRQ {
@ -198,8 +218,7 @@ enum AHCIPortIRQ {
#define SATA_SIGNATURE_CDROM 0xeb140101 #define SATA_SIGNATURE_CDROM 0xeb140101
#define SATA_SIGNATURE_DISK 0x00000101 #define SATA_SIGNATURE_DISK 0x00000101
#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20 #define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c
/* Shouldn't this be 0x2c? */
#define AHCI_PORT_REGS_START_ADDR 0x100 #define AHCI_PORT_REGS_START_ADDR 0x100
#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f #define AHCI_PORT_ADDR_OFFSET_MASK 0x7f

View File

@ -245,15 +245,11 @@ static uint16_t atapi_byte_count_limit(IDEState *s)
void ide_atapi_cmd_reply_end(IDEState *s) void ide_atapi_cmd_reply_end(IDEState *s)
{ {
int byte_count_limit, size, ret; int byte_count_limit, size, ret;
trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, while (s->packet_transfer_size > 0) {
s->elementary_transfer_size, trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size,
s->io_buffer_index); s->elementary_transfer_size,
if (s->packet_transfer_size <= 0) { s->io_buffer_index);
/* end of transfer */
ide_atapi_cmd_ok(s);
ide_set_irq(s->bus);
trace_ide_atapi_cmd_reply_end_eot(s, s->status);
} else {
/* see if a new sector must be read */ /* see if a new sector must be read */
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
if (!s->elementary_transfer_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; size = s->cd_sector_size - s->io_buffer_index;
if (size > s->elementary_transfer_size) if (size > s->elementary_transfer_size)
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 { } else {
/* a new transfer is needed */ /* a new transfer is needed */
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
ide_set_irq(s->bus);
byte_count_limit = atapi_byte_count_limit(s); byte_count_limit = atapi_byte_count_limit(s);
trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit);
size = s->packet_transfer_size; 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)) if (size > (s->cd_sector_size - s->io_buffer_index))
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); 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 */ /* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */

View File

@ -523,18 +523,28 @@ static void ide_clear_retry(IDEState *s)
} }
/* prepare data transfer and tell what to do after */ /* prepare data transfer and tell what to do after */
void ide_transfer_start(IDEState *s, uint8_t *buf, int size, bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func) EndTransferFunc *end_transfer_func)
{ {
s->end_transfer_func = end_transfer_func;
s->data_ptr = buf; s->data_ptr = buf;
s->data_end = buf + size; s->data_end = buf + size;
ide_set_retry(s); ide_set_retry(s);
if (!(s->status & ERR_STAT)) { if (!(s->status & ERR_STAT)) {
s->status |= DRQ_STAT; s->status |= DRQ_STAT;
} }
if (s->bus->dma->ops->start_transfer) { if (!s->bus->dma->ops->pio_transfer) {
s->bus->dma->ops->start_transfer(s->bus->dma); 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, static void ide_transfer_halt(IDEState *s)
void(*end_transfer_func)(IDEState *),
bool notify)
{ {
s->end_transfer_func = end_transfer_func; s->end_transfer_func = ide_transfer_stop;
s->data_ptr = s->io_buffer; s->data_ptr = s->io_buffer;
s->data_end = s->io_buffer; s->data_end = s->io_buffer;
s->status &= ~DRQ_STAT; s->status &= ~DRQ_STAT;
if (notify) {
ide_cmd_done(s);
}
} }
void ide_transfer_stop(IDEState *s) void ide_transfer_stop(IDEState *s)
{ {
ide_transfer_halt(s, ide_transfer_stop, true); ide_transfer_halt(s);
} ide_cmd_done(s);
static void ide_transfer_cancel(IDEState *s)
{
ide_transfer_halt(s, ide_transfer_cancel, false);
} }
int64_t ide_get_sector(IDEState *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) static bool cmd_device_reset(IDEState *s, uint8_t cmd)
{ {
/* Halt PIO (in the DRQ phase), then DMA */ /* Halt PIO (in the DRQ phase), then DMA */
ide_transfer_cancel(s); ide_transfer_halt(s);
ide_cancel_dma_sync(s); ide_cancel_dma_sync(s);
/* Reset any PIO commands, reset signature, etc */ /* Reset any PIO commands, reset signature, etc */

View File

@ -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" ide_atapi_cmd_packet(void *s, uint16_t limit, const char *packet) "IDEState: %p; limit=0x%x packet: %s"
# hw/ide/ahci.c # 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_raise(void *s) "ahci(%p): raise irq"
ahci_irq_lower(void *s) "ahci(%p): lower 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_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_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(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_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(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_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_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" 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_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_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" 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_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(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" ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed"

View File

@ -444,7 +444,7 @@ struct IDEState {
struct IDEDMAOps { struct IDEDMAOps {
DMAStartFunc *start_dma; DMAStartFunc *start_dma;
DMAVoidFunc *start_transfer; DMAVoidFunc *pio_transfer;
DMAInt32Func *prepare_buf; DMAInt32Func *prepare_buf;
DMAu32Func *commit_buf; DMAu32Func *commit_buf;
DMAIntFunc *rw_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, void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
EndTransferFunc *end_transfer_func); 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_transfer_stop(IDEState *s);
void ide_set_inactive(IDEState *s, bool more); void ide_set_inactive(IDEState *s, bool more);
BlockAIOCB *ide_issue_trim( BlockAIOCB *ide_issue_trim(

View File

@ -179,6 +179,7 @@ check-qtest-generic-y = tests/qmp-test$(EXESUF)
gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c
check-qtest-generic-y += tests/device-introspect-test$(EXESUF) check-qtest-generic-y += tests/device-introspect-test$(EXESUF)
gcov-files-generic-y = qdev-monitor.c qmp.c 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 gcov-files-ipack-y += hw/ipack/ipack.c
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) 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/numa-test$(EXESUF): tests/numa-test.o
tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.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/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 tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")

View File

@ -68,8 +68,11 @@ static uint8_t x86_boot_sector[512] = {
}; };
/* For s390x, use a mini "kernel" with the appropriate signature */ /* For s390x, use a mini "kernel" with the appropriate signature */
static const uint8_t s390x_psw[] = { static const uint8_t s390x_psw_and_magic[] = {
0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00 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[] = { static const uint8_t s390x_code[] = {
0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */ 0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */
@ -110,7 +113,7 @@ int boot_sector_init(char *fname)
} else if (g_str_equal(arch, "s390x")) { } else if (g_str_equal(arch, "s390x")) {
len = 0x10000 + sizeof(s390x_code); len = 0x10000 + sizeof(s390x_code);
boot_code = g_malloc0(len); 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)); memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code));
} else { } else {
g_assert_not_reached(); g_assert_not_reached();

222
tests/cdrom-test.c Normal file
View File

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

View File

@ -90,6 +90,7 @@ struct AHCICommand {
uint32_t interrupts; uint32_t interrupts;
uint64_t xbytes; uint64_t xbytes;
uint32_t prd_size; uint32_t prd_size;
uint32_t sector_size;
uint64_t buffer; uint64_t buffer;
AHCICommandProp *props; AHCICommandProp *props;
/* Data to be transferred to the guest */ /* 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); g_free(d2h);
} }
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd)
uint8_t slot, size_t buffsize)
{ {
PIOSetupFIS *pio = g_malloc0(0x20); PIOSetupFIS *pio = g_malloc0(0x20);
uint8_t port = cmd->port;
/* We cannot check the Status or E_Status registers, because /* We cannot check the Status or E_Status registers, because
* the status may have again changed between the PIO Setup FIS * 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); qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20);
g_assert_cmphex(pio->fis_type, ==, 0x5f); g_assert_cmphex(pio->fis_type, ==, 0x5f);
/* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire /* Data transferred by PIO will either be:
* transfer size in a uint16_t field. The maximum transfer size can * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or
* eclipse this; the field is meant to convey the size of data per * (2) Actual data from the drive.
* each Data FIS, not the entire operation as a whole. For now, * If we do both, (2) winds up erasing any evidence of (1).
* we will sanity check the broken case where applicable. */ */
if (buffsize <= UINT16_MAX) { if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) {
g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize); 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); g_free(pio);
} }
@ -796,7 +804,7 @@ static void command_header_init(AHCICommand *cmd)
static void command_table_init(AHCICommand *cmd) static void command_table_init(AHCICommand *cmd)
{ {
RegH2DFIS *fis = &(cmd->fis); 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->fis_type = REG_H2D_FIS;
fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */ 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) { if (cmd->props->lba28 || cmd->props->lba48) {
fis->device = ATA_DEVICE_LBA; fis->device = ATA_DEVICE_LBA;
} }
fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE); fis->count = (cmd->xbytes / cmd->sector_size);
} }
fis->icc = 0x00; fis->icc = 0x00;
fis->control = 0x00; fis->control = 0x00;
@ -831,9 +839,9 @@ void ahci_command_enable_atapi_dma(AHCICommand *cmd)
RegH2DFIS *fis = &(cmd->fis); RegH2DFIS *fis = &(cmd->fis);
g_assert(cmd->props->atapi); g_assert(cmd->props->atapi);
fis->feature_low |= 0x01; 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->dma = true;
cmd->props->pio = false;
/* BUG: We expect the DMA Setup interrupt for DMA commands */ /* BUG: We expect the DMA Setup interrupt for DMA commands */
/* cmd->interrupts |= AHCI_PX_IS_DSS; */ /* cmd->interrupts |= AHCI_PX_IS_DSS; */
} }
@ -845,7 +853,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
g_assert(props); g_assert(props);
cmd = g_new0(AHCICommand, 1); 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->lba28 && props->lba48));
g_assert(!(props->read && props->write)); g_assert(!(props->read && props->write));
g_assert(!props->size || props->data); g_assert(!props->size || props->data);
@ -857,6 +865,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
cmd->xbytes = props->size; cmd->xbytes = props->size;
cmd->prd_size = 4096; cmd->prd_size = 4096;
cmd->buffer = 0xabad1dea; cmd->buffer = 0xabad1dea;
cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE;
if (!cmd->props->ncq) { if (!cmd->props->ncq) {
cmd->interrupts = AHCI_PX_IS_DHRS; 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) static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
{ {
unsigned char *cbd = cmd->atapi_cmd; unsigned char *cbd = cmd->atapi_cmd;
uint64_t nsectors = xbytes / 2048; uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE;
uint32_t tmp; uint32_t tmp;
g_assert(cbd); g_assert(cbd);
@ -1080,7 +1089,7 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
cmd->prd_size = prd_size; cmd->prd_size = prd_size;
} }
cmd->xbytes = xbytes; cmd->xbytes = xbytes;
sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); sect_count = (cmd->xbytes / cmd->sector_size);
if (cmd->props->ncq) { if (cmd->props->ncq) {
NCQFIS *nfis = (NCQFIS *)&(cmd->fis); 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); ahci_port_check_d2h_sanity(ahci, port, slot);
} }
if (cmd->props->pio) { if (cmd->props->pio) {
ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes); ahci_port_check_pio_sanity(ahci, cmd);
} }
} }

View File

@ -596,8 +596,7 @@ void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
uint32_t intr_mask); uint32_t intr_mask);
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); 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_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
uint8_t slot, size_t buffsize);
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
/* Misc */ /* Misc */