2010-12-14 03:34:40 +03:00
|
|
|
/*
|
|
|
|
* QEMU AHCI Emulation
|
|
|
|
*
|
|
|
|
* Copyright (c) 2010 qiaochong@loongson.cn
|
|
|
|
* Copyright (c) 2010 Roland Elek <elek.roland@gmail.com>
|
|
|
|
* Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
|
|
|
|
* Copyright (c) 2010 Alexander Graf <agraf@suse.de>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2020-10-23 15:44:24 +03:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2010-12-14 03:34:40 +03:00
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-01-26 21:17:09 +03:00
|
|
|
#include "qemu/osdep.h"
|
2023-02-10 01:01:55 +03:00
|
|
|
#include "hw/irq.h"
|
2016-06-22 20:11:19 +03:00
|
|
|
#include "hw/pci/msi.h"
|
|
|
|
#include "hw/pci/pci.h"
|
2019-08-12 08:23:51 +03:00
|
|
|
#include "hw/qdev-properties.h"
|
2019-08-12 08:23:45 +03:00
|
|
|
#include "migration/vmstate.h"
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2018-06-08 20:17:37 +03:00
|
|
|
#include "qemu/log.h"
|
Include qemu/main-loop.h less
In my "build everything" tree, changing qemu/main-loop.h triggers a
recompile of some 5600 out of 6600 objects (not counting tests and
objects that don't depend on qemu/osdep.h). It includes block/aio.h,
which in turn includes qemu/event_notifier.h, qemu/notify.h,
qemu/processor.h, qemu/qsp.h, qemu/queue.h, qemu/thread-posix.h,
qemu/thread.h, qemu/timer.h, and a few more.
Include qemu/main-loop.h only where it's needed. Touching it now
recompiles only some 1700 objects. For block/aio.h and
qemu/event_notifier.h, these numbers drop from 5600 to 2800. For the
others, they shrink only slightly.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20190812052359.30071-21-armbru@redhat.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
2019-08-12 08:23:50 +03:00
|
|
|
#include "qemu/main-loop.h"
|
2019-05-23 17:35:07 +03:00
|
|
|
#include "qemu/module.h"
|
2014-10-07 15:59:18 +04:00
|
|
|
#include "sysemu/block-backend.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/dma.h"
|
2016-06-22 20:11:19 +03:00
|
|
|
#include "hw/ide/internal.h"
|
|
|
|
#include "hw/ide/pci.h"
|
2018-05-03 22:50:31 +03:00
|
|
|
#include "ahci_internal.h"
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
#include "trace.h"
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
static void check_cmd(AHCIState *s, int port);
|
2015-07-04 09:06:05 +03:00
|
|
|
static int handle_cmd(AHCIState *s, int port, uint8_t slot);
|
2010-12-14 03:34:40 +03:00
|
|
|
static void ahci_reset_port(AHCIState *s, int port);
|
2015-09-01 23:50:41 +03:00
|
|
|
static bool ahci_write_fis_d2h(AHCIDevice *ad);
|
2011-02-01 17:51:29 +03:00
|
|
|
static void ahci_init_d2h(AHCIDevice *ad);
|
2020-05-12 22:49:17 +03:00
|
|
|
static int ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit);
|
2015-03-27 22:48:11 +03:00
|
|
|
static bool ahci_map_clb_address(AHCIDevice *ad);
|
|
|
|
static bool ahci_map_fis_address(AHCIDevice *ad);
|
2015-03-27 22:48:11 +03:00
|
|
|
static void ahci_unmap_clb_address(AHCIDevice *ad);
|
|
|
|
static void ahci_unmap_fis_address(AHCIDevice *ad);
|
2014-10-02 02:55:47 +04:00
|
|
|
|
2018-06-08 20:17:37 +03:00
|
|
|
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",
|
|
|
|
};
|
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
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",
|
|
|
|
};
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = {
|
|
|
|
[AHCI_PORT_IRQ_BIT_DHRS] = "DHRS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_PSS] = "PSS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_DSS] = "DSS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_SDBS] = "SDBS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_UFS] = "UFS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_DPS] = "DPS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_PCS] = "PCS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_DMPS] = "DMPS",
|
|
|
|
[8 ... 21] = "RESERVED",
|
|
|
|
[AHCI_PORT_IRQ_BIT_PRCS] = "PRCS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_IPMS] = "IPMS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_OFS] = "OFS",
|
|
|
|
[25] = "RESERVED",
|
|
|
|
[AHCI_PORT_IRQ_BIT_INFS] = "INFS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_IFS] = "IFS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_HBDS] = "HBDS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_HBFS] = "HBFS",
|
|
|
|
[AHCI_PORT_IRQ_BIT_TFES] = "TFES",
|
|
|
|
[AHCI_PORT_IRQ_BIT_CPDS] = "CPDS"
|
|
|
|
};
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
uint32_t val;
|
2018-06-08 20:17:36 +03:00
|
|
|
AHCIPortRegs *pr = &s->dev[port].port_regs;
|
|
|
|
enum AHCIPortReg regnum = offset / sizeof(uint32_t);
|
|
|
|
assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t)));
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
switch (regnum) {
|
|
|
|
case AHCI_PORT_REG_LST_ADDR:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->lst_addr;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_LST_ADDR_HI:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->lst_addr_hi;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_FIS_ADDR:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->fis_addr;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_FIS_ADDR_HI:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->fis_addr_hi;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_IRQ_STAT:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->irq_stat;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_IRQ_MASK:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->irq_mask;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_CMD:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->cmd;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_TFDATA:
|
2014-08-21 21:44:36 +04:00
|
|
|
val = pr->tfdata;
|
2010-12-14 03:34:40 +03:00
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_SIG:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->sig;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_SCR_STAT:
|
2014-10-07 15:59:18 +04:00
|
|
|
if (s->dev[port].port.ifs[0].blk) {
|
2010-12-14 03:34:40 +03:00
|
|
|
val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP |
|
|
|
|
SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE;
|
|
|
|
} else {
|
|
|
|
val = SATA_SCR_SSTATUS_DET_NODEV;
|
|
|
|
}
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_SCR_CTL:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->scr_ctl;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_SCR_ERR:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->scr_err;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_SCR_ACT:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->scr_act;
|
|
|
|
break;
|
2018-06-08 20:17:36 +03:00
|
|
|
case AHCI_PORT_REG_CMD_ISSUE:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = pr->cmd_issue;
|
|
|
|
break;
|
|
|
|
default:
|
2018-06-08 20:17:36 +03:00
|
|
|
trace_ahci_port_read_default(s, port, AHCIPortReg_lookup[regnum],
|
|
|
|
offset);
|
2010-12-14 03:34:40 +03:00
|
|
|
val = 0;
|
|
|
|
}
|
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
trace_ahci_port_read(s, port, AHCIPortReg_lookup[regnum], offset, val);
|
2017-09-18 22:01:26 +03:00
|
|
|
return val;
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2018-06-08 20:17:35 +03:00
|
|
|
static void ahci_irq_raise(AHCIState *s)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2015-09-08 19:38:45 +03:00
|
|
|
DeviceState *dev_state = s->container;
|
|
|
|
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
|
|
|
|
TYPE_PCI_DEVICE);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_irq_raise(s);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2014-03-18 23:36:13 +04:00
|
|
|
if (pci_dev && msi_enabled(pci_dev)) {
|
2013-06-30 16:19:24 +04:00
|
|
|
msi_notify(pci_dev, 0);
|
2010-12-14 03:34:40 +03:00
|
|
|
} else {
|
|
|
|
qemu_irq_raise(s->irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-08 20:17:35 +03:00
|
|
|
static void ahci_irq_lower(AHCIState *s)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2015-09-08 19:38:45 +03:00
|
|
|
DeviceState *dev_state = s->container;
|
|
|
|
PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
|
|
|
|
TYPE_PCI_DEVICE);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_irq_lower(s);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2014-03-18 23:36:13 +04:00
|
|
|
if (!pci_dev || !msi_enabled(pci_dev)) {
|
2010-12-14 03:34:40 +03:00
|
|
|
qemu_irq_lower(s->irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ahci_check_irq(AHCIState *s)
|
|
|
|
{
|
|
|
|
int i;
|
2017-09-18 22:01:26 +03:00
|
|
|
uint32_t old_irq = s->control_regs.irqstatus;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2012-01-31 02:29:48 +04:00
|
|
|
s->control_regs.irqstatus = 0;
|
2011-02-01 17:51:31 +03:00
|
|
|
for (i = 0; i < s->ports; i++) {
|
2010-12-14 03:34:40 +03:00
|
|
|
AHCIPortRegs *pr = &s->dev[i].port_regs;
|
|
|
|
if (pr->irq_stat & pr->irq_mask) {
|
|
|
|
s->control_regs.irqstatus |= (1 << i);
|
|
|
|
}
|
|
|
|
}
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus);
|
2010-12-14 03:34:40 +03:00
|
|
|
if (s->control_regs.irqstatus &&
|
|
|
|
(s->control_regs.ghc & HOST_CTL_IRQ_EN)) {
|
2018-06-08 20:17:35 +03:00
|
|
|
ahci_irq_raise(s);
|
2010-12-14 03:34:40 +03:00
|
|
|
} else {
|
2018-06-08 20:17:35 +03:00
|
|
|
ahci_irq_lower(s);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d,
|
2017-09-18 22:01:26 +03:00
|
|
|
enum AHCIPortIRQ irqbit)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2017-09-21 04:38:21 +03:00
|
|
|
g_assert((unsigned)irqbit < 32);
|
2017-09-18 22:01:26 +03:00
|
|
|
uint32_t irq = 1U << irqbit;
|
|
|
|
uint32_t irqstat = d->port_regs.irq_stat | irq;
|
|
|
|
|
|
|
|
trace_ahci_trigger_irq(s, d->port_no,
|
|
|
|
AHCIPortIRQ_lookup[irqbit], irq,
|
|
|
|
d->port_regs.irq_stat, irqstat,
|
|
|
|
irqstat & d->port_regs.irq_mask);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
d->port_regs.irq_stat = irqstat;
|
2010-12-14 03:34:40 +03:00
|
|
|
ahci_check_irq(s);
|
|
|
|
}
|
|
|
|
|
2014-07-03 12:26:27 +04:00
|
|
|
static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr,
|
|
|
|
uint32_t wanted)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr len = wanted;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
if (*ptr) {
|
2014-07-03 12:26:27 +04:00
|
|
|
dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
dma: Let dma_memory_map() take MemTxAttrs argument
Let devices specify transaction attributes when calling
dma_memory_map().
Patch created mechanically using spatch with this script:
@@
expression E1, E2, E3, E4;
@@
- dma_memory_map(E1, E2, E3, E4)
+ dma_memory_map(E1, E2, E3, E4, MEMTXATTRS_UNSPECIFIED)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Li Qiang <liq3ea@gmail.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20211223115554.3155328-7-philmd@redhat.com>
2020-09-03 12:00:47 +03:00
|
|
|
*ptr = dma_memory_map(as, addr, &len, DMA_DIRECTION_FROM_DEVICE,
|
|
|
|
MEMTXATTRS_UNSPECIFIED);
|
2020-07-18 10:28:54 +03:00
|
|
|
if (len < wanted && *ptr) {
|
2014-07-03 12:26:27 +04:00
|
|
|
dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len);
|
2010-12-14 03:34:40 +03:00
|
|
|
*ptr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-22 21:13:44 +03:00
|
|
|
/**
|
|
|
|
* Check the cmd register to see if we should start or stop
|
|
|
|
* the DMA or FIS RX engines.
|
|
|
|
*
|
2016-02-10 21:29:40 +03:00
|
|
|
* @ad: Device to dis/engage.
|
2015-05-22 21:13:44 +03:00
|
|
|
*
|
|
|
|
* @return 0 on success, -1 on error.
|
|
|
|
*/
|
2016-02-10 21:29:40 +03:00
|
|
|
static int ahci_cond_start_engines(AHCIDevice *ad)
|
2015-05-22 21:13:44 +03:00
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
2016-02-10 21:29:40 +03:00
|
|
|
bool cmd_start = pr->cmd & PORT_CMD_START;
|
|
|
|
bool cmd_on = pr->cmd & PORT_CMD_LIST_ON;
|
|
|
|
bool fis_start = pr->cmd & PORT_CMD_FIS_RX;
|
|
|
|
bool fis_on = pr->cmd & PORT_CMD_FIS_ON;
|
2015-05-22 21:13:44 +03:00
|
|
|
|
2016-02-10 21:29:40 +03:00
|
|
|
if (cmd_start && !cmd_on) {
|
2016-02-10 21:29:40 +03:00
|
|
|
if (!ahci_map_clb_address(ad)) {
|
2016-02-10 21:29:40 +03:00
|
|
|
pr->cmd &= ~PORT_CMD_START;
|
2015-05-22 21:13:44 +03:00
|
|
|
error_report("AHCI: Failed to start DMA engine: "
|
|
|
|
"bad command list buffer address");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-10 21:29:40 +03:00
|
|
|
} else if (!cmd_start && cmd_on) {
|
|
|
|
ahci_unmap_clb_address(ad);
|
2015-05-22 21:13:44 +03:00
|
|
|
}
|
|
|
|
|
2016-02-10 21:29:40 +03:00
|
|
|
if (fis_start && !fis_on) {
|
2016-02-10 21:29:40 +03:00
|
|
|
if (!ahci_map_fis_address(ad)) {
|
2016-02-10 21:29:40 +03:00
|
|
|
pr->cmd &= ~PORT_CMD_FIS_RX;
|
2015-05-22 21:13:44 +03:00
|
|
|
error_report("AHCI: Failed to start FIS receive engine: "
|
|
|
|
"bad FIS receive buffer address");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-10 21:29:40 +03:00
|
|
|
} else if (!fis_start && fis_on) {
|
|
|
|
ahci_unmap_fis_address(ad);
|
2015-05-22 21:13:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &s->dev[port].port_regs;
|
2018-06-08 20:17:37 +03:00
|
|
|
enum AHCIPortReg regnum = offset / sizeof(uint32_t);
|
|
|
|
assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t)));
|
2018-06-08 20:17:37 +03:00
|
|
|
trace_ahci_port_write(s, port, AHCIPortReg_lookup[regnum], offset, val);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2018-06-08 20:17:37 +03:00
|
|
|
switch (regnum) {
|
|
|
|
case AHCI_PORT_REG_LST_ADDR:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->lst_addr = val;
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_LST_ADDR_HI:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->lst_addr_hi = val;
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_FIS_ADDR:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->fis_addr = val;
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_FIS_ADDR_HI:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->fis_addr_hi = val;
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_IRQ_STAT:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->irq_stat &= ~val;
|
|
|
|
ahci_check_irq(s);
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_IRQ_MASK:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->irq_mask = val & 0xfdc000ff;
|
|
|
|
ahci_check_irq(s);
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_CMD:
|
2018-06-08 20:17:36 +03:00
|
|
|
/* 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;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_TFDATA:
|
|
|
|
case AHCI_PORT_REG_SIG:
|
|
|
|
case AHCI_PORT_REG_SCR_STAT:
|
2018-06-08 20:17:36 +03:00
|
|
|
/* Read Only */
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_SCR_CTL:
|
2018-06-08 20:17:36 +03:00
|
|
|
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;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_SCR_ERR:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->scr_err &= ~val;
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_SCR_ACT:
|
2018-06-08 20:17:36 +03:00
|
|
|
/* RW1 */
|
|
|
|
pr->scr_act |= val;
|
|
|
|
break;
|
2018-06-08 20:17:37 +03:00
|
|
|
case AHCI_PORT_REG_CMD_ISSUE:
|
2018-06-08 20:17:36 +03:00
|
|
|
pr->cmd_issue |= val;
|
|
|
|
check_cmd(s, port);
|
|
|
|
break;
|
|
|
|
default:
|
2018-06-08 20:17:37 +03:00
|
|
|
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);
|
2018-06-08 20:17:36 +03:00
|
|
|
break;
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:02 +03:00
|
|
|
static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2011-08-08 17:09:14 +04:00
|
|
|
AHCIState *s = opaque;
|
2010-12-14 03:34:40 +03:00
|
|
|
uint32_t val = 0;
|
|
|
|
|
|
|
|
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
|
2018-06-08 20:17:38 +03:00
|
|
|
enum AHCIHostReg regnum = addr / 4;
|
|
|
|
assert(regnum < AHCI_HOST_REG__COUNT);
|
|
|
|
|
|
|
|
switch (regnum) {
|
|
|
|
case AHCI_HOST_REG_CAP:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = s->control_regs.cap;
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_CTL:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = s->control_regs.ghc;
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_IRQ_STAT:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = s->control_regs.irqstatus;
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_PORTS_IMPL:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = s->control_regs.impl;
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_VERSION:
|
2010-12-14 03:34:40 +03:00
|
|
|
val = s->control_regs.version;
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
default:
|
2018-06-08 20:17:38 +03:00
|
|
|
trace_ahci_mem_read_32_host_default(s, AHCIHostReg_lookup[regnum],
|
|
|
|
addr);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
2018-06-08 20:17:38 +03:00
|
|
|
trace_ahci_mem_read_32_host(s, AHCIHostReg_lookup[regnum], addr, val);
|
2010-12-14 03:34:40 +03:00
|
|
|
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
|
2011-02-01 17:51:31 +03:00
|
|
|
(addr < (AHCI_PORT_REGS_START_ADDR +
|
|
|
|
(s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
|
2010-12-14 03:34:40 +03:00
|
|
|
val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
|
|
|
|
addr & AHCI_PORT_ADDR_OFFSET_MASK);
|
2018-06-08 20:17:38 +03:00
|
|
|
} else {
|
|
|
|
trace_ahci_mem_read_32_default(s, addr, val);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_mem_read_32(s, addr, val);
|
2010-12-14 03:34:40 +03:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 09:06:02 +03:00
|
|
|
/**
|
|
|
|
* AHCI 1.3 section 3 ("HBA Memory Registers")
|
|
|
|
* Support unaligned 8/16/32 bit reads, and 64 bit aligned reads.
|
|
|
|
* Caller is responsible for masking unwanted higher order bytes.
|
|
|
|
*/
|
|
|
|
static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size)
|
|
|
|
{
|
|
|
|
hwaddr aligned = addr & ~0x3;
|
|
|
|
int ofst = addr - aligned;
|
|
|
|
uint64_t lo = ahci_mem_read_32(opaque, aligned);
|
|
|
|
uint64_t hi;
|
2015-11-06 22:09:00 +03:00
|
|
|
uint64_t val;
|
2015-07-04 09:06:02 +03:00
|
|
|
|
|
|
|
/* if < 8 byte read does not cross 4 byte boundary */
|
|
|
|
if (ofst + size <= 4) {
|
2015-11-06 22:09:00 +03:00
|
|
|
val = lo >> (ofst * 8);
|
|
|
|
} else {
|
2018-06-08 20:02:31 +03:00
|
|
|
g_assert(size > 1);
|
2015-11-06 22:09:00 +03:00
|
|
|
|
|
|
|
/* If the 64bit read is unaligned, we will produce undefined
|
|
|
|
* results. AHCI does not support unaligned 64bit reads. */
|
|
|
|
hi = ahci_mem_read_32(opaque, aligned + 4);
|
|
|
|
val = (hi << 32 | lo) >> (ofst * 8);
|
2015-07-04 09:06:02 +03:00
|
|
|
}
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_mem_read(opaque, size, addr, val);
|
2015-11-06 22:09:00 +03:00
|
|
|
return val;
|
2015-07-04 09:06:02 +03:00
|
|
|
}
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2012-10-23 14:30:10 +04:00
|
|
|
static void ahci_mem_write(void *opaque, hwaddr addr,
|
2011-08-08 17:09:14 +04:00
|
|
|
uint64_t val, unsigned size)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2011-08-08 17:09:14 +04:00
|
|
|
AHCIState *s = opaque;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_mem_write(s, size, addr, val);
|
2015-11-06 22:09:00 +03:00
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
/* Only aligned reads are allowed on AHCI */
|
|
|
|
if (addr & 3) {
|
2021-01-12 14:29:55 +03:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"ahci: Mis-aligned write to addr 0x%03" HWADDR_PRIX "\n",
|
|
|
|
addr);
|
2010-12-14 03:34:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
|
2018-06-08 20:17:38 +03:00
|
|
|
enum AHCIHostReg regnum = addr / 4;
|
|
|
|
assert(regnum < AHCI_HOST_REG__COUNT);
|
|
|
|
|
|
|
|
switch (regnum) {
|
|
|
|
case AHCI_HOST_REG_CAP: /* R/WO, RO */
|
2018-06-08 20:17:38 +03:00
|
|
|
/* FIXME handle R/WO */
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_CTL: /* R/W */
|
2018-06-08 20:17:38 +03:00
|
|
|
if (val & HOST_CTL_RESET) {
|
|
|
|
ahci_reset(s);
|
|
|
|
} else {
|
|
|
|
s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
|
2010-12-14 03:34:40 +03:00
|
|
|
ahci_check_irq(s);
|
2018-06-08 20:17:38 +03:00
|
|
|
}
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_IRQ_STAT: /* R/WC, RO */
|
2018-06-08 20:17:38 +03:00
|
|
|
s->control_regs.irqstatus &= ~val;
|
|
|
|
ahci_check_irq(s);
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_PORTS_IMPL: /* R/WO, RO */
|
2018-06-08 20:17:38 +03:00
|
|
|
/* FIXME handle R/WO */
|
|
|
|
break;
|
2018-06-08 20:17:38 +03:00
|
|
|
case AHCI_HOST_REG_VERSION: /* RO */
|
2018-06-08 20:17:38 +03:00
|
|
|
/* FIXME report write? */
|
|
|
|
break;
|
|
|
|
default:
|
2018-06-08 20:17:38 +03:00
|
|
|
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);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
2018-06-08 20:17:38 +03:00
|
|
|
trace_ahci_mem_write_host(s, size, AHCIHostReg_lookup[regnum],
|
|
|
|
addr, val);
|
2010-12-14 03:34:40 +03:00
|
|
|
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
|
2011-02-01 17:51:31 +03:00
|
|
|
(addr < (AHCI_PORT_REGS_START_ADDR +
|
2018-06-08 20:17:38 +03:00
|
|
|
(s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
|
2010-12-14 03:34:40 +03:00
|
|
|
ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
|
|
|
|
addr & AHCI_PORT_ADDR_OFFSET_MASK, val);
|
2018-06-08 20:17:38 +03:00
|
|
|
} 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);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-05 14:19:07 +04:00
|
|
|
static const MemoryRegionOps ahci_mem_ops = {
|
2011-08-08 17:09:14 +04:00
|
|
|
.read = ahci_mem_read,
|
|
|
|
.write = ahci_mem_write,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
2010-12-14 03:34:40 +03:00
|
|
|
};
|
|
|
|
|
2012-10-23 14:30:10 +04:00
|
|
|
static uint64_t ahci_idp_read(void *opaque, hwaddr addr,
|
2011-08-27 13:12:28 +04:00
|
|
|
unsigned size)
|
|
|
|
{
|
|
|
|
AHCIState *s = opaque;
|
|
|
|
|
|
|
|
if (addr == s->idp_offset) {
|
|
|
|
/* index register */
|
|
|
|
return s->idp_index;
|
|
|
|
} else if (addr == s->idp_offset + 4) {
|
|
|
|
/* data register - do memory read at location selected by index */
|
|
|
|
return ahci_mem_read(opaque, s->idp_index, size);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 14:30:10 +04:00
|
|
|
static void ahci_idp_write(void *opaque, hwaddr addr,
|
2011-08-27 13:12:28 +04:00
|
|
|
uint64_t val, unsigned size)
|
|
|
|
{
|
|
|
|
AHCIState *s = opaque;
|
|
|
|
|
|
|
|
if (addr == s->idp_offset) {
|
|
|
|
/* index register - mask off reserved bits */
|
|
|
|
s->idp_index = (uint32_t)val & ((AHCI_MEM_BAR_SIZE - 1) & ~3);
|
|
|
|
} else if (addr == s->idp_offset + 4) {
|
|
|
|
/* data register - do memory write at location selected by index */
|
|
|
|
ahci_mem_write(opaque, s->idp_index, val, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-05 14:19:07 +04:00
|
|
|
static const MemoryRegionOps ahci_idp_ops = {
|
2011-08-27 13:12:28 +04:00
|
|
|
.read = ahci_idp_read,
|
|
|
|
.write = ahci_idp_write,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
static void ahci_reg_init(AHCIState *s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2011-02-01 17:51:31 +03:00
|
|
|
s->control_regs.cap = (s->ports - 1) |
|
2010-12-14 03:34:40 +03:00
|
|
|
(AHCI_NUM_COMMAND_SLOTS << 8) |
|
|
|
|
(AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) |
|
2017-02-10 19:47:11 +03:00
|
|
|
HOST_CAP_NCQ | HOST_CAP_AHCI | HOST_CAP_64;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2011-02-01 17:51:31 +03:00
|
|
|
s->control_regs.impl = (1 << s->ports) - 1;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
s->control_regs.version = AHCI_VERSION_1_0;
|
|
|
|
|
2011-02-01 17:51:31 +03:00
|
|
|
for (i = 0; i < s->ports; i++) {
|
2010-12-14 03:34:40 +03:00
|
|
|
s->dev[i].port_state = STATE_RUN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void check_cmd(AHCIState *s, int port)
|
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &s->dev[port].port_regs;
|
2015-07-04 09:06:05 +03:00
|
|
|
uint8_t slot;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
|
|
|
|
for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
|
2014-03-28 19:12:55 +04:00
|
|
|
if ((pr->cmd_issue & (1U << slot)) &&
|
2010-12-14 03:34:40 +03:00
|
|
|
!handle_cmd(s, port, slot)) {
|
2014-03-28 19:12:55 +04:00
|
|
|
pr->cmd_issue &= ~(1U << slot);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ahci_check_cmd_bh(void *opaque)
|
|
|
|
{
|
|
|
|
AHCIDevice *ad = opaque;
|
|
|
|
|
|
|
|
qemu_bh_delete(ad->check_bh);
|
|
|
|
ad->check_bh = NULL;
|
|
|
|
|
|
|
|
check_cmd(ad->hba, ad->port_no);
|
|
|
|
}
|
|
|
|
|
2011-02-01 17:51:29 +03:00
|
|
|
static void ahci_init_d2h(AHCIDevice *ad)
|
|
|
|
{
|
|
|
|
IDEState *ide_state = &ad->port.ifs[0];
|
2015-09-01 23:50:39 +03:00
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
2011-02-01 17:51:29 +03:00
|
|
|
|
2015-09-01 23:50:41 +03:00
|
|
|
if (ad->init_d2h_sent) {
|
|
|
|
return;
|
|
|
|
}
|
2011-02-01 17:51:29 +03:00
|
|
|
|
2015-09-01 23:50:41 +03:00
|
|
|
if (ahci_write_fis_d2h(ad)) {
|
|
|
|
ad->init_d2h_sent = true;
|
|
|
|
/* We're emulating receiving the first Reg H2D Fis from the device;
|
|
|
|
* Update the SIG register, but otherwise proceed as normal. */
|
2015-10-16 19:19:35 +03:00
|
|
|
pr->sig = ((uint32_t)ide_state->hcyl << 24) |
|
2015-09-01 23:50:41 +03:00
|
|
|
(ide_state->lcyl << 16) |
|
|
|
|
(ide_state->sector << 8) |
|
|
|
|
(ide_state->nsector & 0xFF);
|
|
|
|
}
|
2011-02-01 17:51:29 +03:00
|
|
|
}
|
|
|
|
|
2015-09-01 23:50:39 +03:00
|
|
|
static void ahci_set_signature(AHCIDevice *ad, uint32_t sig)
|
|
|
|
{
|
|
|
|
IDEState *s = &ad->port.ifs[0];
|
|
|
|
s->hcyl = sig >> 24 & 0xFF;
|
|
|
|
s->lcyl = sig >> 16 & 0xFF;
|
|
|
|
s->sector = sig >> 8 & 0xFF;
|
|
|
|
s->nsector = sig & 0xFF;
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_set_signature(ad->hba, ad->port_no, s->nsector, s->sector,
|
|
|
|
s->lcyl, s->hcyl, sig);
|
2015-09-01 23:50:39 +03:00
|
|
|
}
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
static void ahci_reset_port(AHCIState *s, int port)
|
|
|
|
{
|
|
|
|
AHCIDevice *d = &s->dev[port];
|
|
|
|
AHCIPortRegs *pr = &d->port_regs;
|
|
|
|
IDEState *ide_state = &d->port.ifs[0];
|
|
|
|
int i;
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_reset_port(s, port);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
ide_bus_reset(&d->port);
|
|
|
|
ide_state->ncq_queues = AHCI_MAX_CMDS;
|
|
|
|
|
|
|
|
pr->scr_stat = 0;
|
|
|
|
pr->scr_err = 0;
|
|
|
|
pr->scr_act = 0;
|
2014-08-21 21:44:36 +04:00
|
|
|
pr->tfdata = 0x7F;
|
|
|
|
pr->sig = 0xFFFFFFFF;
|
2010-12-14 03:34:40 +03:00
|
|
|
d->busy_slot = -1;
|
2013-01-15 19:12:09 +04:00
|
|
|
d->init_d2h_sent = false;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
ide_state = &s->dev[port].port.ifs[0];
|
2014-10-07 15:59:18 +04:00
|
|
|
if (!ide_state->blk) {
|
2010-12-14 03:34:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset ncq queue */
|
|
|
|
for (i = 0; i < AHCI_MAX_CMDS; i++) {
|
|
|
|
NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i];
|
2015-07-04 09:06:04 +03:00
|
|
|
ncq_tfs->halt = false;
|
2010-12-14 03:34:40 +03:00
|
|
|
if (!ncq_tfs->used) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ncq_tfs->aiocb) {
|
2014-10-07 15:59:18 +04:00
|
|
|
blk_aio_cancel(ncq_tfs->aiocb);
|
2010-12-14 03:34:40 +03:00
|
|
|
ncq_tfs->aiocb = NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-07 15:59:18 +04:00
|
|
|
/* Maybe we just finished the request thanks to blk_aio_cancel() */
|
2012-01-31 02:29:47 +04:00
|
|
|
if (!ncq_tfs->used) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
qemu_sglist_destroy(&ncq_tfs->sglist);
|
|
|
|
ncq_tfs->used = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->dev[port].port_state = STATE_RUN;
|
2015-09-01 23:50:38 +03:00
|
|
|
if (ide_state->drive_kind == IDE_CD) {
|
2015-09-01 23:50:39 +03:00
|
|
|
ahci_set_signature(d, SATA_SIGNATURE_CDROM);\
|
2010-12-14 03:34:40 +03:00
|
|
|
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
|
|
|
|
} else {
|
2015-09-01 23:50:39 +03:00
|
|
|
ahci_set_signature(d, SATA_SIGNATURE_DISK);
|
2010-12-14 03:34:40 +03:00
|
|
|
ide_state->status = SEEK_STAT | WRERR_STAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ide_state->error = 1;
|
2011-02-01 17:51:29 +03:00
|
|
|
ahci_init_d2h(d);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
/* Buffer pretty output based on a raw FIS structure. */
|
2021-01-19 19:40:51 +03:00
|
|
|
static char *ahci_pretty_buffer_fis(const uint8_t *fis, int cmd_len)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
int i;
|
2017-09-18 22:01:26 +03:00
|
|
|
GString *s = g_string_new("FIS:");
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
for (i = 0; i < cmd_len; i++) {
|
|
|
|
if ((i & 0xf) == 0) {
|
2017-09-18 22:01:26 +03:00
|
|
|
g_string_append_printf(s, "\n0x%02x: ", i);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
2017-09-18 22:01:26 +03:00
|
|
|
g_string_append_printf(s, "%02x ", fis[i]);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
2017-09-18 22:01:26 +03:00
|
|
|
g_string_append_c(s, '\n');
|
|
|
|
|
|
|
|
return g_string_free(s, FALSE);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2015-03-27 22:48:11 +03:00
|
|
|
static bool ahci_map_fis_address(AHCIDevice *ad)
|
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
|
|
|
map_page(ad->hba->as, &ad->res_fis,
|
|
|
|
((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
|
2016-02-10 21:29:40 +03:00
|
|
|
if (ad->res_fis != NULL) {
|
|
|
|
pr->cmd |= PORT_CMD_FIS_ON;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr->cmd &= ~PORT_CMD_FIS_ON;
|
|
|
|
return false;
|
2015-03-27 22:48:11 +03:00
|
|
|
}
|
|
|
|
|
2015-03-27 22:48:11 +03:00
|
|
|
static void ahci_unmap_fis_address(AHCIDevice *ad)
|
|
|
|
{
|
2016-02-10 21:29:40 +03:00
|
|
|
if (ad->res_fis == NULL) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_unmap_fis_address_null(ad->hba, ad->port_no);
|
2016-02-10 21:29:40 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-02-10 21:29:40 +03:00
|
|
|
ad->port_regs.cmd &= ~PORT_CMD_FIS_ON;
|
2015-03-27 22:48:11 +03:00
|
|
|
dma_memory_unmap(ad->hba->as, ad->res_fis, 256,
|
|
|
|
DMA_DIRECTION_FROM_DEVICE, 256);
|
|
|
|
ad->res_fis = NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-27 22:48:11 +03:00
|
|
|
static bool ahci_map_clb_address(AHCIDevice *ad)
|
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
|
|
|
ad->cur_cmd = NULL;
|
|
|
|
map_page(ad->hba->as, &ad->lst,
|
|
|
|
((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
|
2016-02-10 21:29:40 +03:00
|
|
|
if (ad->lst != NULL) {
|
|
|
|
pr->cmd |= PORT_CMD_LIST_ON;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr->cmd &= ~PORT_CMD_LIST_ON;
|
|
|
|
return false;
|
2015-03-27 22:48:11 +03:00
|
|
|
}
|
|
|
|
|
2015-03-27 22:48:11 +03:00
|
|
|
static void ahci_unmap_clb_address(AHCIDevice *ad)
|
|
|
|
{
|
2016-02-10 21:29:40 +03:00
|
|
|
if (ad->lst == NULL) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no);
|
2016-02-10 21:29:40 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-02-10 21:29:40 +03:00
|
|
|
ad->port_regs.cmd &= ~PORT_CMD_LIST_ON;
|
2015-03-27 22:48:11 +03:00
|
|
|
dma_memory_unmap(ad->hba->as, ad->lst, 1024,
|
|
|
|
DMA_DIRECTION_FROM_DEVICE, 1024);
|
|
|
|
ad->lst = NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2015-07-04 09:06:05 +03:00
|
|
|
AHCIDevice *ad = ncq_tfs->drive;
|
2014-08-21 21:44:36 +04:00
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
2010-12-14 03:34:40 +03:00
|
|
|
IDEState *ide_state;
|
2014-10-02 02:55:51 +04:00
|
|
|
SDBFIS *sdb_fis;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
if (!ad->res_fis ||
|
2010-12-14 03:34:40 +03:00
|
|
|
!(pr->cmd & PORT_CMD_FIS_RX)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-02 02:55:51 +04:00
|
|
|
sdb_fis = (SDBFIS *)&ad->res_fis[RES_FIS_SDBFIS];
|
2014-08-21 21:44:36 +04:00
|
|
|
ide_state = &ad->port.ifs[0];
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2014-11-13 13:24:41 +03:00
|
|
|
sdb_fis->type = SATA_FIS_TYPE_SDB;
|
2014-10-02 02:55:51 +04:00
|
|
|
/* Interrupt pending & Notification bit */
|
2015-07-04 09:06:05 +03:00
|
|
|
sdb_fis->flags = 0x40; /* Interrupt bit, always 1 for NCQ */
|
2014-10-02 02:55:51 +04:00
|
|
|
sdb_fis->status = ide_state->status & 0x77;
|
|
|
|
sdb_fis->error = ide_state->error;
|
|
|
|
/* update SAct field in SDB_FIS */
|
|
|
|
sdb_fis->payload = cpu_to_le32(ad->finished);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2014-08-21 21:44:36 +04:00
|
|
|
/* Update shadow registers (except BSY 0x80 and DRQ 0x08) */
|
|
|
|
pr->tfdata = (ad->port.ifs[0].error << 8) |
|
|
|
|
(ad->port.ifs[0].status & 0x77) |
|
|
|
|
(pr->tfdata & 0x88);
|
2015-07-04 09:06:05 +03:00
|
|
|
pr->scr_act &= ~ad->finished;
|
|
|
|
ad->finished = 0;
|
2014-08-21 21:44:36 +04:00
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
/* Trigger IRQ if interrupt bit is set (which currently, it always is) */
|
|
|
|
if (sdb_fis->flags & 0x40) {
|
2017-09-18 22:01:26 +03:00
|
|
|
ahci_trigger_irq(s, ad, AHCI_PORT_IRQ_BIT_SDBS);
|
2015-07-04 09:06:05 +03:00
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2018-06-25 23:50:48 +03:00
|
|
|
static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i)
|
2014-08-05 01:11:18 +04:00
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
2015-07-04 09:06:05 +03:00
|
|
|
uint8_t *pio_fis;
|
2014-10-02 02:55:46 +04:00
|
|
|
IDEState *s = &ad->port.ifs[0];
|
2014-08-05 01:11:18 +04:00
|
|
|
|
|
|
|
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pio_fis = &ad->res_fis[RES_FIS_PSFIS];
|
|
|
|
|
2014-11-13 13:24:41 +03:00
|
|
|
pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP;
|
2018-06-25 23:50:48 +03:00
|
|
|
pio_fis[1] = (pio_fis_i ? (1 << 6) : 0);
|
2014-10-02 02:55:46 +04:00
|
|
|
pio_fis[2] = s->status;
|
|
|
|
pio_fis[3] = s->error;
|
|
|
|
|
|
|
|
pio_fis[4] = s->sector;
|
|
|
|
pio_fis[5] = s->lcyl;
|
|
|
|
pio_fis[6] = s->hcyl;
|
|
|
|
pio_fis[7] = s->select;
|
|
|
|
pio_fis[8] = s->hob_sector;
|
|
|
|
pio_fis[9] = s->hob_lcyl;
|
|
|
|
pio_fis[10] = s->hob_hcyl;
|
|
|
|
pio_fis[11] = 0;
|
2015-07-04 09:06:05 +03:00
|
|
|
pio_fis[12] = s->nsector & 0xFF;
|
|
|
|
pio_fis[13] = (s->nsector >> 8) & 0xFF;
|
2014-08-05 01:11:18 +04:00
|
|
|
pio_fis[14] = 0;
|
2014-10-02 02:55:46 +04:00
|
|
|
pio_fis[15] = s->status;
|
2014-08-05 01:11:18 +04:00
|
|
|
pio_fis[16] = len & 255;
|
|
|
|
pio_fis[17] = len >> 8;
|
|
|
|
pio_fis[18] = 0;
|
|
|
|
pio_fis[19] = 0;
|
|
|
|
|
2014-08-21 21:44:36 +04:00
|
|
|
/* Update shadow registers: */
|
|
|
|
pr->tfdata = (ad->port.ifs[0].error << 8) |
|
|
|
|
ad->port.ifs[0].status;
|
|
|
|
|
2014-08-05 01:11:18 +04:00
|
|
|
if (pio_fis[2] & ERR_STAT) {
|
2017-09-18 22:01:26 +03:00
|
|
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
|
2014-08-05 01:11:18 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-01 23:50:41 +03:00
|
|
|
static bool ahci_write_fis_d2h(AHCIDevice *ad)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIPortRegs *pr = &ad->port_regs;
|
|
|
|
uint8_t *d2h_fis;
|
|
|
|
int i;
|
2014-10-02 02:55:46 +04:00
|
|
|
IDEState *s = &ad->port.ifs[0];
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
|
2015-09-01 23:50:41 +03:00
|
|
|
return false;
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
|
|
|
|
|
2014-11-13 13:24:41 +03:00
|
|
|
d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
|
2018-06-25 23:50:48 +03:00
|
|
|
d2h_fis[1] = (1 << 6); /* interrupt bit */
|
2014-10-02 02:55:46 +04:00
|
|
|
d2h_fis[2] = s->status;
|
|
|
|
d2h_fis[3] = s->error;
|
|
|
|
|
|
|
|
d2h_fis[4] = s->sector;
|
|
|
|
d2h_fis[5] = s->lcyl;
|
|
|
|
d2h_fis[6] = s->hcyl;
|
|
|
|
d2h_fis[7] = s->select;
|
|
|
|
d2h_fis[8] = s->hob_sector;
|
|
|
|
d2h_fis[9] = s->hob_lcyl;
|
|
|
|
d2h_fis[10] = s->hob_hcyl;
|
|
|
|
d2h_fis[11] = 0;
|
2015-07-04 09:06:05 +03:00
|
|
|
d2h_fis[12] = s->nsector & 0xFF;
|
|
|
|
d2h_fis[13] = (s->nsector >> 8) & 0xFF;
|
2012-05-23 03:26:42 +04:00
|
|
|
for (i = 14; i < 20; i++) {
|
2010-12-14 03:34:40 +03:00
|
|
|
d2h_fis[i] = 0;
|
|
|
|
}
|
|
|
|
|
2014-08-21 21:44:36 +04:00
|
|
|
/* Update shadow registers: */
|
|
|
|
pr->tfdata = (ad->port.ifs[0].error << 8) |
|
|
|
|
ad->port.ifs[0].status;
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
if (d2h_fis[2] & ERR_STAT) {
|
2017-09-18 22:01:26 +03:00
|
|
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS);
|
2015-09-01 23:50:41 +03:00
|
|
|
return true;
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2014-07-01 15:13:27 +04:00
|
|
|
static int prdt_tbl_entry_size(const AHCI_SG *tbl)
|
|
|
|
{
|
ide: add limit to .prepare_buf()
prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.
For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.
PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.
AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.
Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.
This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.
For BMDMA, the existing pathways should see no guest-visible difference,
but any bytes described in the overage will no longer be transferred
before indicating to the guest that there was an underflow.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1435767578-32743-2-git-send-email-jsnow@redhat.com
2015-07-04 09:06:04 +03:00
|
|
|
/* flags_size is zero-based */
|
2014-07-01 15:13:27 +04:00
|
|
|
return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
|
|
|
|
}
|
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
/**
|
|
|
|
* Fetch entries in a guest-provided PRDT and convert it into a QEMU SGlist.
|
|
|
|
* @ad: The AHCIDevice for whom we are building the SGList.
|
|
|
|
* @sglist: The SGList target to add PRD entries to.
|
|
|
|
* @cmd: The AHCI Command Header that describes where the PRDT is.
|
|
|
|
* @limit: The remaining size of the S/ATA transaction, in bytes.
|
|
|
|
* @offset: The number of bytes already transferred, in bytes.
|
|
|
|
*
|
|
|
|
* The AHCI PRDT can describe up to 256GiB. S/ATA only support transactions of
|
|
|
|
* up to 32MiB as of ATA8-ACS3 rev 1b, assuming a 512 byte sector size. We stop
|
|
|
|
* building the sglist from the PRDT as soon as we hit @limit bytes,
|
|
|
|
* which is <= INT32_MAX/2GiB.
|
|
|
|
*/
|
ide: Correct handling of malformed/short PRDTs
This impacts both BMDMA and AHCI HBA interfaces for IDE.
Currently, we confuse the difference between a PRDT having
"0 bytes" and a PRDT having "0 complete sectors."
When we receive an incomplete sector, inconsistent error checking
leads to an infinite loop wherein the call succeeds, but it
didn't give us enough bytes -- leading us to re-call the
DMA chain over and over again. This leads to, in the BMDMA case,
leaked memory for short PRDTs, and infinite loops and resource
usage in the AHCI case.
The .prepare_buf() callback is reworked to return the number of
bytes that it successfully prepared. 0 is a valid, non-error
answer that means the table was empty and described no bytes.
-1 indicates an error.
Our current implementation uses the io_buffer in IDEState to
ultimately describe the size of a prepared scatter-gather list.
Even though the AHCI PRDT/SGList can be as large as 256GiB, the
AHCI command header limits transactions to just 4GiB. ATA8-ACS3,
however, defines the largest transaction to be an LBA48 command
that transfers 65,536 sectors. With a 512 byte sector size, this
is just 32MiB.
Since our current state structures use the int type to describe
the size of the buffer, and this state is migrated as int32, we
are limited to describing 2GiB buffer sizes unless we change the
migration protocol.
For this reason, this patch begins to unify the assertions in the
IDE pathways that the scatter-gather list provided by either the
AHCI PRDT or the PCI BMDMA PRDs can only describe, at a maximum,
2GiB. This should be resilient enough unless we need a sector
size that exceeds 32KiB.
Further, the likelihood of any guest operating system actually
attempting to transfer this much data in a single operation is
very slim.
To this end, the IDEState variables have been updated to more
explicitly clarify our maximum supported size. Callers to the
prepare_buf callback have been reworked to understand the new
return code, and all versions of the prepare_buf callback have
been adjusted accordingly.
Lastly, the ahci_populate_sglist helper, relied upon by the
AHCI implementation of .prepare_buf() as well as the PCI
implementation of the callback have had overflow assertions
added to help make clear the reasonings behind the various
type changes.
[Added %d -> %"PRId64" fix John sent because off_pos changed from int to
int64_t.
--Stefan]
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1414785819-26209-4-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-10-31 23:03:39 +03:00
|
|
|
static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
|
2015-11-06 22:09:00 +03:00
|
|
|
AHCICmdHdr *cmd, int64_t limit, uint64_t offset)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2015-07-04 09:06:03 +03:00
|
|
|
uint16_t opts = le16_to_cpu(cmd->opts);
|
|
|
|
uint16_t prdtl = le16_to_cpu(cmd->prdtl);
|
|
|
|
uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr);
|
|
|
|
uint64_t prdt_addr = cfis_addr + 0x80;
|
|
|
|
dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG));
|
2012-06-27 08:50:41 +04:00
|
|
|
dma_addr_t real_prdt_len = prdt_len;
|
2010-12-14 03:34:40 +03:00
|
|
|
uint8_t *prdt;
|
|
|
|
int i;
|
|
|
|
int r = 0;
|
ide: Correct handling of malformed/short PRDTs
This impacts both BMDMA and AHCI HBA interfaces for IDE.
Currently, we confuse the difference between a PRDT having
"0 bytes" and a PRDT having "0 complete sectors."
When we receive an incomplete sector, inconsistent error checking
leads to an infinite loop wherein the call succeeds, but it
didn't give us enough bytes -- leading us to re-call the
DMA chain over and over again. This leads to, in the BMDMA case,
leaked memory for short PRDTs, and infinite loops and resource
usage in the AHCI case.
The .prepare_buf() callback is reworked to return the number of
bytes that it successfully prepared. 0 is a valid, non-error
answer that means the table was empty and described no bytes.
-1 indicates an error.
Our current implementation uses the io_buffer in IDEState to
ultimately describe the size of a prepared scatter-gather list.
Even though the AHCI PRDT/SGList can be as large as 256GiB, the
AHCI command header limits transactions to just 4GiB. ATA8-ACS3,
however, defines the largest transaction to be an LBA48 command
that transfers 65,536 sectors. With a 512 byte sector size, this
is just 32MiB.
Since our current state structures use the int type to describe
the size of the buffer, and this state is migrated as int32, we
are limited to describing 2GiB buffer sizes unless we change the
migration protocol.
For this reason, this patch begins to unify the assertions in the
IDE pathways that the scatter-gather list provided by either the
AHCI PRDT or the PCI BMDMA PRDs can only describe, at a maximum,
2GiB. This should be resilient enough unless we need a sector
size that exceeds 32KiB.
Further, the likelihood of any guest operating system actually
attempting to transfer this much data in a single operation is
very slim.
To this end, the IDEState variables have been updated to more
explicitly clarify our maximum supported size. Callers to the
prepare_buf callback have been reworked to understand the new
return code, and all versions of the prepare_buf callback have
been adjusted accordingly.
Lastly, the ahci_populate_sglist helper, relied upon by the
AHCI implementation of .prepare_buf() as well as the PCI
implementation of the callback have had overflow assertions
added to help make clear the reasonings behind the various
type changes.
[Added %d -> %"PRId64" fix John sent because off_pos changed from int to
int64_t.
--Stefan]
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1414785819-26209-4-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-10-31 23:03:39 +03:00
|
|
|
uint64_t sum = 0;
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
int off_idx = -1;
|
ide: Correct handling of malformed/short PRDTs
This impacts both BMDMA and AHCI HBA interfaces for IDE.
Currently, we confuse the difference between a PRDT having
"0 bytes" and a PRDT having "0 complete sectors."
When we receive an incomplete sector, inconsistent error checking
leads to an infinite loop wherein the call succeeds, but it
didn't give us enough bytes -- leading us to re-call the
DMA chain over and over again. This leads to, in the BMDMA case,
leaked memory for short PRDTs, and infinite loops and resource
usage in the AHCI case.
The .prepare_buf() callback is reworked to return the number of
bytes that it successfully prepared. 0 is a valid, non-error
answer that means the table was empty and described no bytes.
-1 indicates an error.
Our current implementation uses the io_buffer in IDEState to
ultimately describe the size of a prepared scatter-gather list.
Even though the AHCI PRDT/SGList can be as large as 256GiB, the
AHCI command header limits transactions to just 4GiB. ATA8-ACS3,
however, defines the largest transaction to be an LBA48 command
that transfers 65,536 sectors. With a 512 byte sector size, this
is just 32MiB.
Since our current state structures use the int type to describe
the size of the buffer, and this state is migrated as int32, we
are limited to describing 2GiB buffer sizes unless we change the
migration protocol.
For this reason, this patch begins to unify the assertions in the
IDE pathways that the scatter-gather list provided by either the
AHCI PRDT or the PCI BMDMA PRDs can only describe, at a maximum,
2GiB. This should be resilient enough unless we need a sector
size that exceeds 32KiB.
Further, the likelihood of any guest operating system actually
attempting to transfer this much data in a single operation is
very slim.
To this end, the IDEState variables have been updated to more
explicitly clarify our maximum supported size. Callers to the
prepare_buf callback have been reworked to understand the new
return code, and all versions of the prepare_buf callback have
been adjusted accordingly.
Lastly, the ahci_populate_sglist helper, relied upon by the
AHCI implementation of .prepare_buf() as well as the PCI
implementation of the callback have had overflow assertions
added to help make clear the reasonings behind the various
type changes.
[Added %d -> %"PRId64" fix John sent because off_pos changed from int to
int64_t.
--Stefan]
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1414785819-26209-4-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-10-31 23:03:39 +03:00
|
|
|
int64_t off_pos = -1;
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
int tbl_entry_size;
|
2013-06-03 16:17:19 +04:00
|
|
|
IDEBus *bus = &ad->port;
|
|
|
|
BusState *qbus = BUS(bus);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_populate_sglist(ad->hba, ad->port_no);
|
|
|
|
|
2015-07-04 09:06:03 +03:00
|
|
|
if (!prdtl) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_populate_sglist_no_prdtl(ad->hba, ad->port_no, opts);
|
2010-12-14 03:34:40 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* map PRDT */
|
2013-04-10 20:15:49 +04:00
|
|
|
if (!(prdt = dma_memory_map(ad->hba->as, prdt_addr, &prdt_len,
|
dma: Let dma_memory_map() take MemTxAttrs argument
Let devices specify transaction attributes when calling
dma_memory_map().
Patch created mechanically using spatch with this script:
@@
expression E1, E2, E3, E4;
@@
- dma_memory_map(E1, E2, E3, E4)
+ dma_memory_map(E1, E2, E3, E4, MEMTXATTRS_UNSPECIFIED)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Li Qiang <liq3ea@gmail.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20211223115554.3155328-7-philmd@redhat.com>
2020-09-03 12:00:47 +03:00
|
|
|
DMA_DIRECTION_TO_DEVICE,
|
|
|
|
MEMTXATTRS_UNSPECIFIED))){
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_populate_sglist_no_map(ad->hba, ad->port_no);
|
2010-12-14 03:34:40 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prdt_len < real_prdt_len) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_populate_sglist_short_map(ad->hba, ad->port_no);
|
2010-12-14 03:34:40 +03:00
|
|
|
r = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get entries in the PRDT, init a qemu sglist accordingly */
|
2015-07-04 09:06:03 +03:00
|
|
|
if (prdtl > 0) {
|
2010-12-14 03:34:40 +03:00
|
|
|
AHCI_SG *tbl = (AHCI_SG *)prdt;
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
sum = 0;
|
2015-07-04 09:06:03 +03:00
|
|
|
for (i = 0; i < prdtl; i++) {
|
2014-07-01 15:13:27 +04:00
|
|
|
tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
|
ide: add limit to .prepare_buf()
prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.
For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.
PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.
AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.
Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.
This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.
For BMDMA, the existing pathways should see no guest-visible difference,
but any bytes described in the overage will no longer be transferred
before indicating to the guest that there was an underflow.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1435767578-32743-2-git-send-email-jsnow@redhat.com
2015-07-04 09:06:04 +03:00
|
|
|
if (offset < (sum + tbl_entry_size)) {
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
off_idx = i;
|
|
|
|
off_pos = offset - sum;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sum += tbl_entry_size;
|
|
|
|
}
|
|
|
|
if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_populate_sglist_bad_offset(ad->hba, ad->port_no,
|
|
|
|
off_idx, off_pos);
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
r = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:03 +03:00
|
|
|
qemu_sglist_init(sglist, qbus->parent, (prdtl - off_idx),
|
2013-06-03 16:17:19 +04:00
|
|
|
ad->hba->as);
|
2015-03-20 03:24:15 +03:00
|
|
|
qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos,
|
ide: add limit to .prepare_buf()
prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.
For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.
PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.
AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.
Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.
This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.
For BMDMA, the existing pathways should see no guest-visible difference,
but any bytes described in the overage will no longer be transferred
before indicating to the guest that there was an underflow.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1435767578-32743-2-git-send-email-jsnow@redhat.com
2015-07-04 09:06:04 +03:00
|
|
|
MIN(prdt_tbl_entry_size(&tbl[off_idx]) - off_pos,
|
|
|
|
limit));
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
|
ide: add limit to .prepare_buf()
prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.
For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.
PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.
AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.
Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.
This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.
For BMDMA, the existing pathways should see no guest-visible difference,
but any bytes described in the overage will no longer be transferred
before indicating to the guest that there was an underflow.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1435767578-32743-2-git-send-email-jsnow@redhat.com
2015-07-04 09:06:04 +03:00
|
|
|
for (i = off_idx + 1; i < prdtl && sglist->size < limit; i++) {
|
2010-12-14 03:34:40 +03:00
|
|
|
qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
|
ide: add limit to .prepare_buf()
prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.
For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.
PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.
AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.
Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.
This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.
For BMDMA, the existing pathways should see no guest-visible difference,
but any bytes described in the overage will no longer be transferred
before indicating to the guest that there was an underflow.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1435767578-32743-2-git-send-email-jsnow@redhat.com
2015-07-04 09:06:04 +03:00
|
|
|
MIN(prdt_tbl_entry_size(&tbl[i]),
|
|
|
|
limit - sglist->size));
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2013-04-10 20:15:49 +04:00
|
|
|
dma_memory_unmap(ad->hba->as, prdt, prdt_len,
|
2012-06-27 08:50:41 +04:00
|
|
|
DMA_DIRECTION_TO_DEVICE, prdt_len);
|
2010-12-14 03:34:40 +03:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:03 +03:00
|
|
|
static void ncq_err(NCQTransferState *ncq_tfs)
|
|
|
|
{
|
|
|
|
IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
|
|
|
|
|
|
|
|
ide_state->error = ABRT_ERR;
|
|
|
|
ide_state->status = READY_STAT | ERR_STAT;
|
|
|
|
ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
|
2016-07-19 09:47:46 +03:00
|
|
|
qemu_sglist_destroy(&ncq_tfs->sglist);
|
2016-01-11 22:10:42 +03:00
|
|
|
ncq_tfs->used = 0;
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:04 +03:00
|
|
|
static void ncq_finish(NCQTransferState *ncq_tfs)
|
|
|
|
{
|
2015-07-04 09:06:05 +03:00
|
|
|
/* If we didn't error out, set our finished bit. Errored commands
|
|
|
|
* do not get a bit set for the SDB FIS ACT register, nor do they
|
|
|
|
* clear the outstanding bit in scr_act (PxSACT). */
|
|
|
|
if (!(ncq_tfs->drive->port_regs.scr_err & (1 << ncq_tfs->tag))) {
|
|
|
|
ncq_tfs->drive->finished |= (1 << ncq_tfs->tag);
|
|
|
|
}
|
2015-07-04 09:06:04 +03:00
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs);
|
2015-07-04 09:06:04 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ncq_finish(ncq_tfs->drive->hba, ncq_tfs->drive->port_no,
|
|
|
|
ncq_tfs->tag);
|
2015-07-04 09:06:04 +03:00
|
|
|
|
|
|
|
block_acct_done(blk_get_stats(ncq_tfs->drive->port.ifs[0].blk),
|
|
|
|
&ncq_tfs->acct);
|
|
|
|
qemu_sglist_destroy(&ncq_tfs->sglist);
|
|
|
|
ncq_tfs->used = 0;
|
|
|
|
}
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
static void ncq_cb(void *opaque, int ret)
|
|
|
|
{
|
|
|
|
NCQTransferState *ncq_tfs = (NCQTransferState *)opaque;
|
|
|
|
IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
|
|
|
|
|
2016-09-26 21:33:37 +03:00
|
|
|
ncq_tfs->aiocb = NULL;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
if (ret < 0) {
|
2015-07-04 09:06:04 +03:00
|
|
|
bool is_read = ncq_tfs->cmd == READ_FPDMA_QUEUED;
|
|
|
|
BlockErrorAction action = blk_get_error_action(ide_state->blk,
|
|
|
|
is_read, -ret);
|
|
|
|
if (action == BLOCK_ERROR_ACTION_STOP) {
|
|
|
|
ncq_tfs->halt = true;
|
|
|
|
ide_state->bus->error_status = IDE_RETRY_HBA;
|
|
|
|
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
|
|
|
|
ncq_err(ncq_tfs);
|
|
|
|
}
|
|
|
|
blk_error_action(ide_state->blk, action, is_read, -ret);
|
2010-12-14 03:34:40 +03:00
|
|
|
} else {
|
|
|
|
ide_state->status = READY_STAT | SEEK_STAT;
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:04 +03:00
|
|
|
if (!ncq_tfs->halt) {
|
|
|
|
ncq_finish(ncq_tfs);
|
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2014-11-04 02:56:15 +03:00
|
|
|
static int is_ncq(uint8_t ata_cmd)
|
|
|
|
{
|
|
|
|
/* Based on SATA 3.2 section 13.6.3.2 */
|
|
|
|
switch (ata_cmd) {
|
|
|
|
case READ_FPDMA_QUEUED:
|
|
|
|
case WRITE_FPDMA_QUEUED:
|
|
|
|
case NCQ_NON_DATA:
|
|
|
|
case RECEIVE_FPDMA_QUEUED:
|
|
|
|
case SEND_FPDMA_QUEUED:
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:04 +03:00
|
|
|
static void execute_ncq_command(NCQTransferState *ncq_tfs)
|
|
|
|
{
|
|
|
|
AHCIDevice *ad = ncq_tfs->drive;
|
|
|
|
IDEState *ide_state = &ad->port.ifs[0];
|
|
|
|
int port = ad->port_no;
|
2015-07-04 09:06:04 +03:00
|
|
|
|
2015-07-04 09:06:04 +03:00
|
|
|
g_assert(is_ncq(ncq_tfs->cmd));
|
2015-07-04 09:06:04 +03:00
|
|
|
ncq_tfs->halt = false;
|
2015-07-04 09:06:04 +03:00
|
|
|
|
|
|
|
switch (ncq_tfs->cmd) {
|
|
|
|
case READ_FPDMA_QUEUED:
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_execute_ncq_command_read(ad->hba, port, ncq_tfs->tag,
|
|
|
|
ncq_tfs->sector_count, ncq_tfs->lba);
|
2015-07-04 09:06:04 +03:00
|
|
|
dma_acct_start(ide_state->blk, &ncq_tfs->acct,
|
|
|
|
&ncq_tfs->sglist, BLOCK_ACCT_READ);
|
|
|
|
ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist,
|
2016-05-23 15:54:05 +03:00
|
|
|
ncq_tfs->lba << BDRV_SECTOR_BITS,
|
2016-10-27 23:29:13 +03:00
|
|
|
BDRV_SECTOR_SIZE,
|
2016-05-23 15:54:05 +03:00
|
|
|
ncq_cb, ncq_tfs);
|
2015-07-04 09:06:04 +03:00
|
|
|
break;
|
|
|
|
case WRITE_FPDMA_QUEUED:
|
2023-02-17 13:31:30 +03:00
|
|
|
trace_execute_ncq_command_write(ad->hba, port, ncq_tfs->tag,
|
|
|
|
ncq_tfs->sector_count, ncq_tfs->lba);
|
2015-07-04 09:06:04 +03:00
|
|
|
dma_acct_start(ide_state->blk, &ncq_tfs->acct,
|
|
|
|
&ncq_tfs->sglist, BLOCK_ACCT_WRITE);
|
|
|
|
ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist,
|
2016-05-23 15:54:05 +03:00
|
|
|
ncq_tfs->lba << BDRV_SECTOR_BITS,
|
2016-10-27 23:29:13 +03:00
|
|
|
BDRV_SECTOR_SIZE,
|
2016-05-23 15:54:05 +03:00
|
|
|
ncq_cb, ncq_tfs);
|
2015-07-04 09:06:04 +03:00
|
|
|
break;
|
|
|
|
default:
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_execute_ncq_command_unsup(ad->hba, port,
|
|
|
|
ncq_tfs->tag, ncq_tfs->cmd);
|
2015-07-04 09:06:04 +03:00
|
|
|
ncq_err(ncq_tfs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-19 19:40:51 +03:00
|
|
|
static void process_ncq_command(AHCIState *s, int port, const uint8_t *cmd_fis,
|
2015-07-04 09:06:05 +03:00
|
|
|
uint8_t slot)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2015-07-04 09:06:03 +03:00
|
|
|
AHCIDevice *ad = &s->dev[port];
|
2021-01-19 19:40:51 +03:00
|
|
|
const NCQFrame *ncq_fis = (NCQFrame *)cmd_fis;
|
2010-12-14 03:34:40 +03:00
|
|
|
uint8_t tag = ncq_fis->tag >> 3;
|
2015-07-04 09:06:03 +03:00
|
|
|
NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag];
|
2015-07-04 09:06:03 +03:00
|
|
|
size_t size;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2015-07-04 09:06:04 +03:00
|
|
|
g_assert(is_ncq(ncq_fis->command));
|
2010-12-14 03:34:40 +03:00
|
|
|
if (ncq_tfs->used) {
|
|
|
|
/* error - already in use */
|
2021-01-12 14:29:55 +03:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: tag %d already used\n",
|
|
|
|
__func__, tag);
|
2010-12-14 03:34:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ncq_tfs->used = 1;
|
2015-07-04 09:06:03 +03:00
|
|
|
ncq_tfs->drive = ad;
|
2010-12-14 03:34:40 +03:00
|
|
|
ncq_tfs->slot = slot;
|
2015-07-04 09:06:05 +03:00
|
|
|
ncq_tfs->cmdh = &((AHCICmdHdr *)ad->lst)[slot];
|
2015-07-04 09:06:04 +03:00
|
|
|
ncq_tfs->cmd = ncq_fis->command;
|
2010-12-14 03:34:40 +03:00
|
|
|
ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) |
|
|
|
|
((uint64_t)ncq_fis->lba4 << 32) |
|
|
|
|
((uint64_t)ncq_fis->lba3 << 24) |
|
|
|
|
((uint64_t)ncq_fis->lba2 << 16) |
|
|
|
|
((uint64_t)ncq_fis->lba1 << 8) |
|
|
|
|
(uint64_t)ncq_fis->lba0;
|
2015-07-04 09:06:03 +03:00
|
|
|
ncq_tfs->tag = tag;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2015-07-04 09:06:03 +03:00
|
|
|
/* Sanity-check the NCQ packet */
|
|
|
|
if (tag != slot) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command_mismatch(s, port, tag, slot);
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ncq_fis->aux0 || ncq_fis->aux1 || ncq_fis->aux2 || ncq_fis->aux3) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command_aux(s, port, tag);
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
|
|
|
if (ncq_fis->prio || ncq_fis->icc) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command_prioicc(s, port, tag);
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
|
|
|
if (ncq_fis->fua & NCQ_FIS_FUA_MASK) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command_fua(s, port, tag);
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
|
|
|
if (ncq_fis->tag & NCQ_FIS_RARC_MASK) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command_rarc(s, port, tag);
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
ncq_tfs->sector_count = ((ncq_fis->sector_count_high << 8) |
|
|
|
|
ncq_fis->sector_count_low);
|
|
|
|
if (!ncq_tfs->sector_count) {
|
|
|
|
ncq_tfs->sector_count = 0x10000;
|
|
|
|
}
|
2020-08-14 11:28:38 +03:00
|
|
|
size = ncq_tfs->sector_count * BDRV_SECTOR_SIZE;
|
2015-07-04 09:06:05 +03:00
|
|
|
ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0);
|
2015-07-04 09:06:03 +03:00
|
|
|
|
|
|
|
if (ncq_tfs->sglist.size < size) {
|
2022-01-04 11:42:21 +03:00
|
|
|
error_report("ahci: PRDT length for NCQ command (0x" DMA_ADDR_FMT ") "
|
2015-07-04 09:06:03 +03:00
|
|
|
"is smaller than the requested size (0x%zx)",
|
|
|
|
ncq_tfs->sglist.size, size);
|
|
|
|
ncq_err(ncq_tfs);
|
2017-09-18 22:01:26 +03:00
|
|
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_OFS);
|
2015-07-04 09:06:03 +03:00
|
|
|
return;
|
2015-07-04 09:06:03 +03:00
|
|
|
} else if (ncq_tfs->sglist.size != size) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command_large(s, port, tag,
|
|
|
|
ncq_tfs->sglist.size, size);
|
2015-07-04 09:06:03 +03:00
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_process_ncq_command(s, port, tag,
|
|
|
|
ncq_fis->command,
|
|
|
|
ncq_tfs->lba,
|
|
|
|
ncq_tfs->lba + ncq_tfs->sector_count - 1);
|
2015-07-04 09:06:04 +03:00
|
|
|
execute_ncq_command(ncq_tfs);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
static AHCICmdHdr *get_cmd_header(AHCIState *s, uint8_t port, uint8_t slot)
|
|
|
|
{
|
|
|
|
if (port >= s->ports || slot >= AHCI_MAX_CMDS) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return s->dev[port].lst ? &((AHCICmdHdr *)s->dev[port].lst)[slot] : NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-04 02:56:19 +03:00
|
|
|
static void handle_reg_h2d_fis(AHCIState *s, int port,
|
2021-01-19 19:40:51 +03:00
|
|
|
uint8_t slot, const uint8_t *cmd_fis)
|
2014-11-04 02:56:19 +03:00
|
|
|
{
|
|
|
|
IDEState *ide_state = &s->dev[port].port.ifs[0];
|
2015-07-04 09:06:05 +03:00
|
|
|
AHCICmdHdr *cmd = get_cmd_header(s, port, slot);
|
2015-07-04 09:06:03 +03:00
|
|
|
uint16_t opts = le16_to_cpu(cmd->opts);
|
2014-11-04 02:56:19 +03:00
|
|
|
|
|
|
|
if (cmd_fis[1] & 0x0F) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_reg_h2d_fis_pmp(s, port, cmd_fis[1],
|
|
|
|
cmd_fis[2], cmd_fis[3]);
|
2014-11-04 02:56:19 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_fis[1] & 0x70) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_reg_h2d_fis_res(s, port, cmd_fis[1],
|
|
|
|
cmd_fis[2], cmd_fis[3]);
|
2014-11-04 02:56:19 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cmd_fis[1] & SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER)) {
|
|
|
|
switch (s->dev[port].port_state) {
|
|
|
|
case STATE_RUN:
|
|
|
|
if (cmd_fis[15] & ATA_SRST) {
|
|
|
|
s->dev[port].port_state = STATE_RESET;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_RESET:
|
|
|
|
if (!(cmd_fis[15] & ATA_SRST)) {
|
|
|
|
ahci_reset_port(s, port);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for NCQ command */
|
|
|
|
if (is_ncq(cmd_fis[2])) {
|
|
|
|
process_ncq_command(s, port, cmd_fis, slot);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decompose the FIS:
|
|
|
|
* AHCI does not interpret FIS packets, it only forwards them.
|
|
|
|
* SATA 1.0 describes how to decode LBA28 and CHS FIS packets.
|
|
|
|
* Later specifications, e.g, SATA 3.2, describe LBA48 FIS packets.
|
|
|
|
*
|
|
|
|
* ATA4 describes sector number for LBA28/CHS commands.
|
|
|
|
* ATA6 describes sector number for LBA48 commands.
|
|
|
|
* ATA8 deprecates CHS fully, describing only LBA28/48.
|
|
|
|
*
|
|
|
|
* We dutifully convert the FIS into IDE registers, and allow the
|
|
|
|
* core layer to interpret them as needed. */
|
|
|
|
ide_state->feature = cmd_fis[3];
|
|
|
|
ide_state->sector = cmd_fis[4]; /* LBA 7:0 */
|
|
|
|
ide_state->lcyl = cmd_fis[5]; /* LBA 15:8 */
|
|
|
|
ide_state->hcyl = cmd_fis[6]; /* LBA 23:16 */
|
|
|
|
ide_state->select = cmd_fis[7]; /* LBA 27:24 (LBA28) */
|
|
|
|
ide_state->hob_sector = cmd_fis[8]; /* LBA 31:24 */
|
|
|
|
ide_state->hob_lcyl = cmd_fis[9]; /* LBA 39:32 */
|
|
|
|
ide_state->hob_hcyl = cmd_fis[10]; /* LBA 47:40 */
|
|
|
|
ide_state->hob_feature = cmd_fis[11];
|
|
|
|
ide_state->nsector = (int64_t)((cmd_fis[13] << 8) | cmd_fis[12]);
|
|
|
|
/* 14, 16, 17, 18, 19: Reserved (SATA 1.0) */
|
|
|
|
/* 15: Only valid when UPDATE_COMMAND not set. */
|
|
|
|
|
|
|
|
/* Copy the ACMD field (ATAPI packet, if any) from the AHCI command
|
|
|
|
* table to ide_state->io_buffer */
|
|
|
|
if (opts & AHCI_CMD_ATAPI) {
|
|
|
|
memcpy(ide_state->io_buffer, &cmd_fis[AHCI_COMMAND_TABLE_ACMD], 0x10);
|
2017-09-18 22:01:26 +03:00
|
|
|
if (trace_event_get_state_backends(TRACE_HANDLE_REG_H2D_FIS_DUMP)) {
|
|
|
|
char *pretty_fis = ahci_pretty_buffer_fis(ide_state->io_buffer, 0x10);
|
|
|
|
trace_handle_reg_h2d_fis_dump(s, port, pretty_fis);
|
|
|
|
g_free(pretty_fis);
|
|
|
|
}
|
2014-11-04 02:56:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ide_state->error = 0;
|
2018-06-25 23:50:48 +03:00
|
|
|
s->dev[port].done_first_drq = false;
|
2014-11-04 02:56:19 +03:00
|
|
|
/* Reset transferred byte counter */
|
|
|
|
cmd->status = 0;
|
|
|
|
|
|
|
|
/* We're ready to process the command in FIS byte 2. */
|
2023-02-09 13:26:20 +03:00
|
|
|
ide_bus_exec_cmd(&s->dev[port].port, cmd_fis[2]);
|
2014-11-04 02:56:19 +03:00
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
static int handle_cmd(AHCIState *s, int port, uint8_t slot)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
IDEState *ide_state;
|
|
|
|
uint64_t tbl_addr;
|
|
|
|
AHCICmdHdr *cmd;
|
|
|
|
uint8_t *cmd_fis;
|
2012-06-27 08:50:41 +04:00
|
|
|
dma_addr_t cmd_len;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
|
|
|
|
/* Engine currently busy, try again later */
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_cmd_busy(s, port);
|
2010-12-14 03:34:40 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!s->dev[port].lst) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_cmd_nolist(s, port);
|
2010-12-14 03:34:40 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 09:06:05 +03:00
|
|
|
cmd = get_cmd_header(s, port, slot);
|
2010-12-14 03:34:40 +03:00
|
|
|
/* remember current slot handle for later */
|
|
|
|
s->dev[port].cur_cmd = cmd;
|
|
|
|
|
2014-11-04 02:56:17 +03:00
|
|
|
/* The device we are working for */
|
|
|
|
ide_state = &s->dev[port].port.ifs[0];
|
|
|
|
if (!ide_state->blk) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_cmd_badport(s, port);
|
2014-11-04 02:56:17 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
tbl_addr = le64_to_cpu(cmd->tbl_addr);
|
|
|
|
cmd_len = 0x80;
|
2013-04-10 20:15:49 +04:00
|
|
|
cmd_fis = dma_memory_map(s->as, tbl_addr, &cmd_len,
|
dma: Let dma_memory_map() take MemTxAttrs argument
Let devices specify transaction attributes when calling
dma_memory_map().
Patch created mechanically using spatch with this script:
@@
expression E1, E2, E3, E4;
@@
- dma_memory_map(E1, E2, E3, E4)
+ dma_memory_map(E1, E2, E3, E4, MEMTXATTRS_UNSPECIFIED)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Li Qiang <liq3ea@gmail.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20211223115554.3155328-7-philmd@redhat.com>
2020-09-03 12:00:47 +03:00
|
|
|
DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED);
|
2010-12-14 03:34:40 +03:00
|
|
|
if (!cmd_fis) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_cmd_badfis(s, port);
|
2010-12-14 03:34:40 +03:00
|
|
|
return -1;
|
2014-11-04 02:56:17 +03:00
|
|
|
} else if (cmd_len != 0x80) {
|
2017-09-18 22:01:26 +03:00
|
|
|
ahci_trigger_irq(s, &s->dev[port], AHCI_PORT_IRQ_BIT_HBFS);
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_cmd_badmap(s, port, cmd_len);
|
2010-12-14 03:34:40 +03:00
|
|
|
goto out;
|
|
|
|
}
|
2017-09-18 22:01:26 +03:00
|
|
|
if (trace_event_get_state_backends(TRACE_HANDLE_CMD_FIS_DUMP)) {
|
|
|
|
char *pretty_fis = ahci_pretty_buffer_fis(cmd_fis, 0x80);
|
|
|
|
trace_handle_cmd_fis_dump(s, port, pretty_fis);
|
|
|
|
g_free(pretty_fis);
|
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
switch (cmd_fis[0]) {
|
|
|
|
case SATA_FIS_TYPE_REGISTER_H2D:
|
2014-11-04 02:56:19 +03:00
|
|
|
handle_reg_h2d_fis(s, port, slot, cmd_fis);
|
2010-12-14 03:34:40 +03:00
|
|
|
break;
|
|
|
|
default:
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_handle_cmd_unhandled_fis(s, port,
|
|
|
|
cmd_fis[0], cmd_fis[1], cmd_fis[2]);
|
2010-12-14 03:34:40 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2021-01-19 19:40:51 +03:00
|
|
|
dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_TO_DEVICE,
|
2012-06-27 08:50:41 +04:00
|
|
|
cmd_len);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
|
|
|
|
/* async command, complete later */
|
|
|
|
s->dev[port].busy_slot = slot;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* done handling the command */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-06 22:09:51 +03:00
|
|
|
/* Transfer PIO data between RAM and device */
|
2020-05-12 22:49:17 +03:00
|
|
|
static void ahci_pio_transfer(const IDEDMA *dma)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
|
|
|
IDEState *s = &ad->port.ifs[0];
|
|
|
|
uint32_t size = (uint32_t)(s->data_end - s->data_ptr);
|
|
|
|
/* write == ram -> device */
|
2015-07-04 09:06:03 +03:00
|
|
|
uint16_t opts = le16_to_cpu(ad->cur_cmd->opts);
|
2010-12-14 03:34:40 +03:00
|
|
|
int is_write = opts & AHCI_CMD_WRITE;
|
|
|
|
int is_atapi = opts & AHCI_CMD_ATAPI;
|
|
|
|
int has_sglist = 0;
|
2018-06-25 23:50:48 +03:00
|
|
|
bool pio_fis_i;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2018-06-25 23:50:48 +03:00
|
|
|
/* The PIO Setup FIS is received prior to transfer, but the interrupt
|
|
|
|
* is only triggered after data is received.
|
|
|
|
*
|
|
|
|
* The device only sets the 'I' bit in the PIO Setup FIS for device->host
|
|
|
|
* requests (see "DPIOI1" in the SATA spec), or for host->device DRQs after
|
|
|
|
* the first (see "DPIOO1"). The latter is consistent with the spec's
|
|
|
|
* description of the PACKET protocol, where the command part of ATAPI requests
|
|
|
|
* ("DPKT0") has the 'I' bit clear, while the data part of PIO ATAPI requests
|
|
|
|
* ("DPKT4a" and "DPKT7") has the 'I' bit set for both directions for all DRQs.
|
|
|
|
*/
|
|
|
|
pio_fis_i = ad->done_first_drq || (!is_atapi && !is_write);
|
|
|
|
ahci_write_fis_pio(ad, size, pio_fis_i);
|
ahci: move PIO Setup FIS before transfer, fix it for ATAPI commands
The PIO Setup FIS is written in the PIO:Entry state, which comes before
the ATA and ATAPI data transfer states. As a result, the PIO Setup FIS
interrupt is now raised before DMA ends for ATAPI commands, and tests have
to be adjusted.
This is also hinted by the description of the command header in the AHCI
specification, where the "A" bit is described as
When ‘1’, indicates that a PIO setup FIS shall be sent by the device
indicating a transfer for the ATAPI command.
and also by the description of the ACMD (ATAPI command region):
The ATAPI command must be either 12 or 16 bytes in length. The length
transmitted by the HBA is determined by the PIO setup FIS that is sent
by the device requesting the ATAPI command.
QEMU, which conflates the "generator" and the "receiver" of the FIS into
one device, always uses ATAPI_PACKET_SIZE, aka 12, for the length.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-id: 20180606190955.20845-3-jsnow@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
2018-06-06 22:09:50 +03:00
|
|
|
|
2018-06-25 23:50:48 +03:00
|
|
|
if (is_atapi && !ad->done_first_drq) {
|
2010-12-14 03:34:40 +03:00
|
|
|
/* already prepopulated iobuffer */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
ide: add limit to .prepare_buf()
prepare_buf should not always grab as many descriptors
as it can, sometimes it should self-limit.
For example, an NCQ transfer of 1 sector with a PRDT that
describes 4GiB of data should not copy 4GiB of data, it
should just transfer that first 512 bytes.
PIO is not affected, because the dma_buf_rw dma helpers
already have a byte limit built-in to them, but DMA/NCQ
will exhaust the entire list regardless of requested size.
AHCI 1.3 specifies in section 6.1.6 Command List Underflow that
NCQ is not required to detect underflow conditions. Non-NCQ
pathways signal underflow by writing to the PRDBC field, which
will already occur by writing the actual transferred byte count
to the PRDBC, signaling the underflow.
Our NCQ pathways aren't required to detect underflow, but since our DMA
backend uses the size of the PRDT to determine the size of the transer,
if our PRDT is bigger than the transaction (the underflow condition) it
doesn't cost us anything to detect it and truncate the PRDT.
This is a recoverable error and is not signaled to the guest, in either
NCQ or normal DMA cases.
For BMDMA, the existing pathways should see no guest-visible difference,
but any bytes described in the overage will no longer be transferred
before indicating to the guest that there was an underflow.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1435767578-32743-2-git-send-email-jsnow@redhat.com
2015-07-04 09:06:04 +03:00
|
|
|
if (ahci_dma_prepare_buf(dma, size)) {
|
2010-12-14 03:34:40 +03:00
|
|
|
has_sglist = 1;
|
|
|
|
}
|
|
|
|
|
2018-06-06 22:09:51 +03:00
|
|
|
trace_ahci_pio_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read",
|
|
|
|
size, is_atapi ? "atapi" : "ata",
|
|
|
|
has_sglist ? "" : "o");
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2011-12-15 17:32:04 +04:00
|
|
|
if (has_sglist && size) {
|
2021-12-16 01:02:21 +03:00
|
|
|
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
|
|
|
|
|
2011-12-15 17:32:04 +04:00
|
|
|
if (is_write) {
|
2021-12-16 11:36:38 +03:00
|
|
|
dma_buf_write(s->data_ptr, size, NULL, &s->sg, attrs);
|
2011-12-15 17:32:04 +04:00
|
|
|
} else {
|
2021-12-16 11:36:38 +03:00
|
|
|
dma_buf_read(s->data_ptr, size, NULL, &s->sg, attrs);
|
2011-12-15 17:32:04 +04:00
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
ahci: move PIO Setup FIS before transfer, fix it for ATAPI commands
The PIO Setup FIS is written in the PIO:Entry state, which comes before
the ATA and ATAPI data transfer states. As a result, the PIO Setup FIS
interrupt is now raised before DMA ends for ATAPI commands, and tests have
to be adjusted.
This is also hinted by the description of the command header in the AHCI
specification, where the "A" bit is described as
When ‘1’, indicates that a PIO setup FIS shall be sent by the device
indicating a transfer for the ATAPI command.
and also by the description of the ACMD (ATAPI command region):
The ATAPI command must be either 12 or 16 bytes in length. The length
transmitted by the HBA is determined by the PIO setup FIS that is sent
by the device requesting the ATAPI command.
QEMU, which conflates the "generator" and the "receiver" of the FIS into
one device, always uses ATAPI_PACKET_SIZE, aka 12, for the length.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-id: 20180606190955.20845-3-jsnow@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
2018-06-06 22:09:50 +03:00
|
|
|
/* Update number of transferred bytes, destroy sglist */
|
|
|
|
dma_buf_commit(s, size);
|
2018-06-25 23:50:48 +03:00
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
out:
|
|
|
|
/* declare that we processed everything */
|
|
|
|
s->data_ptr = s->data_end;
|
2018-06-25 23:50:48 +03:00
|
|
|
|
|
|
|
ad->done_first_drq = true;
|
|
|
|
if (pio_fis_i) {
|
|
|
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS);
|
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2020-05-12 22:49:17 +03:00
|
|
|
static void ahci_start_dma(const IDEDMA *dma, IDEState *s,
|
2014-10-07 15:59:15 +04:00
|
|
|
BlockCompletionFunc *dma_cb)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_start_dma(ad->hba, ad->port_no);
|
ahci: Fix ahci cdrom read corruptions for reads > 128k
While testing q35, which has its cdrom attached to the ahci controller, I found
that the Fedora 17 install would panic on boot. The panic occurs while
squashfs is trying to read from the cdrom. The errors are:
[ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt
[ 8.625180] SQUASHFS error: squashfs_read_data failed to read block
0x20be48a
I was also able to produce corrupt data reads using an installed piix based
qemu machine, using 'dd'. I found that the corruptions were only occuring when
then read size was greater than 128k. For example, the following command
results in corrupted reads:
dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct
The > 128k size reads exercise a different code path than 128k and below. In
ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus,
ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k.
However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k.
Thus, resulting in a corrupted read.
To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep
track of the offset. I've also modified ahci_populate_sglist() to take a new
3rd offset argument, so that the sglist is property initialized.
I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots
and installs on q35 with the cdrom ahci controller.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-08-03 23:57:06 +04:00
|
|
|
s->io_buffer_offset = 0;
|
2010-12-14 03:34:40 +03:00
|
|
|
dma_cb(s, 0);
|
|
|
|
}
|
|
|
|
|
2020-05-12 22:49:17 +03:00
|
|
|
static void ahci_restart_dma(const IDEDMA *dma)
|
2015-02-23 19:18:04 +03:00
|
|
|
{
|
|
|
|
/* Nothing to do, ahci_start_dma already resets s->io_buffer_offset. */
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:04 +03:00
|
|
|
/**
|
|
|
|
* IDE/PIO restarts are handled by the core layer, but NCQ commands
|
|
|
|
* need an extra kick from the AHCI HBA.
|
|
|
|
*/
|
2020-05-12 22:49:17 +03:00
|
|
|
static void ahci_restart(const IDEDMA *dma)
|
2015-07-04 09:06:04 +03:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < AHCI_MAX_CMDS; i++) {
|
|
|
|
NCQTransferState *ncq_tfs = &ad->ncq_tfs[i];
|
|
|
|
if (ncq_tfs->halt) {
|
|
|
|
execute_ncq_command(ncq_tfs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 02:55:47 +04:00
|
|
|
/**
|
2015-09-17 21:17:04 +03:00
|
|
|
* Called in DMA and PIO R/W chains to read the PRDT.
|
|
|
|
* Not shared with NCQ pathways.
|
2014-10-02 02:55:47 +04:00
|
|
|
*/
|
2020-05-12 22:49:17 +03:00
|
|
|
static int32_t ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
|
|
|
IDEState *s = &ad->port.ifs[0];
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd,
|
|
|
|
limit, s->io_buffer_offset) == -1) {
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_dma_prepare_buf_fail(ad->hba, ad->port_no);
|
ide: Correct handling of malformed/short PRDTs
This impacts both BMDMA and AHCI HBA interfaces for IDE.
Currently, we confuse the difference between a PRDT having
"0 bytes" and a PRDT having "0 complete sectors."
When we receive an incomplete sector, inconsistent error checking
leads to an infinite loop wherein the call succeeds, but it
didn't give us enough bytes -- leading us to re-call the
DMA chain over and over again. This leads to, in the BMDMA case,
leaked memory for short PRDTs, and infinite loops and resource
usage in the AHCI case.
The .prepare_buf() callback is reworked to return the number of
bytes that it successfully prepared. 0 is a valid, non-error
answer that means the table was empty and described no bytes.
-1 indicates an error.
Our current implementation uses the io_buffer in IDEState to
ultimately describe the size of a prepared scatter-gather list.
Even though the AHCI PRDT/SGList can be as large as 256GiB, the
AHCI command header limits transactions to just 4GiB. ATA8-ACS3,
however, defines the largest transaction to be an LBA48 command
that transfers 65,536 sectors. With a 512 byte sector size, this
is just 32MiB.
Since our current state structures use the int type to describe
the size of the buffer, and this state is migrated as int32, we
are limited to describing 2GiB buffer sizes unless we change the
migration protocol.
For this reason, this patch begins to unify the assertions in the
IDE pathways that the scatter-gather list provided by either the
AHCI PRDT or the PCI BMDMA PRDs can only describe, at a maximum,
2GiB. This should be resilient enough unless we need a sector
size that exceeds 32KiB.
Further, the likelihood of any guest operating system actually
attempting to transfer this much data in a single operation is
very slim.
To this end, the IDEState variables have been updated to more
explicitly clarify our maximum supported size. Callers to the
prepare_buf callback have been reworked to understand the new
return code, and all versions of the prepare_buf callback have
been adjusted accordingly.
Lastly, the ahci_populate_sglist helper, relied upon by the
AHCI implementation of .prepare_buf() as well as the PCI
implementation of the callback have had overflow assertions
added to help make clear the reasonings behind the various
type changes.
[Added %d -> %"PRId64" fix John sent because off_pos changed from int to
int64_t.
--Stefan]
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1414785819-26209-4-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-10-31 23:03:39 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2011-12-15 17:32:04 +04:00
|
|
|
s->io_buffer_size = s->sg.size;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_dma_prepare_buf(ad->hba, ad->port_no, limit, s->io_buffer_size);
|
ide: Correct handling of malformed/short PRDTs
This impacts both BMDMA and AHCI HBA interfaces for IDE.
Currently, we confuse the difference between a PRDT having
"0 bytes" and a PRDT having "0 complete sectors."
When we receive an incomplete sector, inconsistent error checking
leads to an infinite loop wherein the call succeeds, but it
didn't give us enough bytes -- leading us to re-call the
DMA chain over and over again. This leads to, in the BMDMA case,
leaked memory for short PRDTs, and infinite loops and resource
usage in the AHCI case.
The .prepare_buf() callback is reworked to return the number of
bytes that it successfully prepared. 0 is a valid, non-error
answer that means the table was empty and described no bytes.
-1 indicates an error.
Our current implementation uses the io_buffer in IDEState to
ultimately describe the size of a prepared scatter-gather list.
Even though the AHCI PRDT/SGList can be as large as 256GiB, the
AHCI command header limits transactions to just 4GiB. ATA8-ACS3,
however, defines the largest transaction to be an LBA48 command
that transfers 65,536 sectors. With a 512 byte sector size, this
is just 32MiB.
Since our current state structures use the int type to describe
the size of the buffer, and this state is migrated as int32, we
are limited to describing 2GiB buffer sizes unless we change the
migration protocol.
For this reason, this patch begins to unify the assertions in the
IDE pathways that the scatter-gather list provided by either the
AHCI PRDT or the PCI BMDMA PRDs can only describe, at a maximum,
2GiB. This should be resilient enough unless we need a sector
size that exceeds 32KiB.
Further, the likelihood of any guest operating system actually
attempting to transfer this much data in a single operation is
very slim.
To this end, the IDEState variables have been updated to more
explicitly clarify our maximum supported size. Callers to the
prepare_buf callback have been reworked to understand the new
return code, and all versions of the prepare_buf callback have
been adjusted accordingly.
Lastly, the ahci_populate_sglist helper, relied upon by the
AHCI implementation of .prepare_buf() as well as the PCI
implementation of the callback have had overflow assertions
added to help make clear the reasonings behind the various
type changes.
[Added %d -> %"PRId64" fix John sent because off_pos changed from int to
int64_t.
--Stefan]
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1414785819-26209-4-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2014-10-31 23:03:39 +03:00
|
|
|
return s->io_buffer_size;
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2014-10-02 02:55:47 +04:00
|
|
|
/**
|
2015-09-17 21:17:04 +03:00
|
|
|
* Updates the command header with a bytes-read value.
|
|
|
|
* Called via dma_buf_commit, for both DMA and PIO paths.
|
|
|
|
* sglist destruction is handled within dma_buf_commit.
|
2014-10-02 02:55:47 +04:00
|
|
|
*/
|
2020-05-12 22:49:17 +03:00
|
|
|
static void ahci_commit_buf(const IDEDMA *dma, uint32_t tx_bytes)
|
2014-10-02 02:55:47 +04:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
|
|
|
|
|
|
|
tx_bytes += le32_to_cpu(ad->cur_cmd->status);
|
|
|
|
ad->cur_cmd->status = cpu_to_le32(tx_bytes);
|
|
|
|
}
|
|
|
|
|
2020-05-12 22:49:17 +03:00
|
|
|
static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
|
|
|
IDEState *s = &ad->port.ifs[0];
|
|
|
|
uint8_t *p = s->io_buffer + s->io_buffer_index;
|
|
|
|
int l = s->io_buffer_size - s->io_buffer_index;
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd, l, s->io_buffer_offset)) {
|
2010-12-14 03:34:40 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_write) {
|
2021-12-16 11:36:38 +03:00
|
|
|
dma_buf_read(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
|
2010-12-14 03:34:40 +03:00
|
|
|
} else {
|
2021-12-16 11:36:38 +03:00
|
|
|
dma_buf_write(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2014-10-02 02:55:47 +04:00
|
|
|
/* free sglist, update byte count */
|
2015-09-17 21:17:04 +03:00
|
|
|
dma_buf_commit(s, l);
|
2010-12-14 03:34:40 +03:00
|
|
|
s->io_buffer_index += l;
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_dma_rw_buf(ad->hba, ad->port_no, l);
|
2010-12-14 03:34:40 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-05-12 22:49:17 +03:00
|
|
|
static void ahci_cmd_done(const IDEDMA *dma)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
|
|
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_cmd_done(ad->hba, ad->port_no);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
/* no longer busy */
|
|
|
|
if (ad->busy_slot != -1) {
|
|
|
|
ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
|
|
|
|
ad->busy_slot = -1;
|
|
|
|
}
|
|
|
|
|
2010-12-14 03:34:40 +03:00
|
|
|
/* update d2h status */
|
2015-09-01 23:50:40 +03:00
|
|
|
ahci_write_fis_d2h(ad);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2018-06-08 20:17:36 +03:00
|
|
|
if (ad->port_regs.cmd_issue && !ad->check_bh) {
|
2023-04-28 00:10:09 +03:00
|
|
|
ad->check_bh = qemu_bh_new_guarded(ahci_check_cmd_bh, ad,
|
|
|
|
&ad->mem_reentrancy_guard);
|
2011-05-09 19:48:19 +04:00
|
|
|
qemu_bh_schedule(ad->check_bh);
|
|
|
|
}
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ahci_irq_set(void *opaque, int n, int level)
|
|
|
|
{
|
2020-05-04 12:48:58 +03:00
|
|
|
qemu_log_mask(LOG_UNIMP, "ahci: IRQ#%d level:%d\n", n, level);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static const IDEDMAOps ahci_dma_ops = {
|
|
|
|
.start_dma = ahci_start_dma,
|
2015-07-04 09:06:04 +03:00
|
|
|
.restart = ahci_restart,
|
2015-02-23 19:18:04 +03:00
|
|
|
.restart_dma = ahci_restart_dma,
|
2018-06-06 22:09:51 +03:00
|
|
|
.pio_transfer = ahci_pio_transfer,
|
2010-12-14 03:34:40 +03:00
|
|
|
.prepare_buf = ahci_dma_prepare_buf,
|
2014-10-02 02:55:47 +04:00
|
|
|
.commit_buf = ahci_commit_buf,
|
2010-12-14 03:34:40 +03:00
|
|
|
.rw_buf = ahci_dma_rw_buf,
|
2014-08-05 01:11:17 +04:00
|
|
|
.cmd_done = ahci_cmd_done,
|
2010-12-14 03:34:40 +03:00
|
|
|
};
|
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
void ahci_init(AHCIState *s, DeviceState *qdev)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2015-09-08 19:38:45 +03:00
|
|
|
s->container = qdev;
|
2011-08-08 17:09:14 +04:00
|
|
|
/* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
|
2013-06-07 05:25:08 +04:00
|
|
|
memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s,
|
|
|
|
"ahci", AHCI_MEM_BAR_SIZE);
|
|
|
|
memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s,
|
|
|
|
"ahci-idp", 32);
|
2015-11-06 22:09:00 +03:00
|
|
|
}
|
2011-08-27 13:12:28 +04:00
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
|
|
|
|
{
|
|
|
|
qemu_irq *irqs;
|
|
|
|
int i;
|
2010-12-14 03:34:40 +03:00
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
s->as = as;
|
|
|
|
s->ports = ports;
|
|
|
|
s->dev = g_new0(AHCIDevice, ports);
|
|
|
|
ahci_reg_init(s);
|
|
|
|
irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
|
2011-02-01 17:51:31 +03:00
|
|
|
for (i = 0; i < s->ports; i++) {
|
2010-12-14 03:34:40 +03:00
|
|
|
AHCIDevice *ad = &s->dev[i];
|
|
|
|
|
2021-09-23 15:11:53 +03:00
|
|
|
ide_bus_init(&ad->port, sizeof(ad->port), qdev, i, 1);
|
2023-02-09 13:27:23 +03:00
|
|
|
ide_bus_init_output_irq(&ad->port, irqs[i]);
|
2010-12-14 03:34:40 +03:00
|
|
|
|
|
|
|
ad->hba = s;
|
|
|
|
ad->port_no = i;
|
|
|
|
ad->port.dma = &ad->dma;
|
|
|
|
ad->port.dma->ops = &ahci_dma_ops;
|
2023-02-14 18:33:38 +03:00
|
|
|
ide_bus_register_restart_cb(&ad->port);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
2016-07-14 19:03:40 +03:00
|
|
|
g_free(irqs);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
|
2011-02-01 17:51:31 +03:00
|
|
|
void ahci_uninit(AHCIState *s)
|
|
|
|
{
|
2017-03-16 03:50:14 +03:00
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < s->ports; i++) {
|
|
|
|
AHCIDevice *ad = &s->dev[i];
|
|
|
|
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
IDEState *s = &ad->port.ifs[j];
|
|
|
|
|
|
|
|
ide_exit(s);
|
|
|
|
}
|
ide: ahci: unparent children buses before freeing their memory
Fixes read after freeing error reported
https://lists.gnu.org/archive/html/qemu-devel/2017-08/msg04243.html
Message-Id: <59a56959-ca12-ea75-33fa-ff07eba1b090@redhat.com>
ich9-ahci device creates ide buses and attaches them as QOM children
at realize time, however it forgets to properly clean them up
at unrealize time and frees memory containing these children,
with following call-chain:
qdev_device_add()
object_property_set_bool('realized', true)
device_set_realized()
...
pci_qdev_realize() -> pci_ich9_ahci_realize() -> ahci_realize()
...
s->dev = g_new0(AHCIDevice, ports);
...
AHCIDevice *ad = &s->dev[i];
ide_bus_new(&ad->port, sizeof(ad->port), qdev, i, 1);
^^^ creates bus in memory allocated by above gnew()
and adds it as child propety to ahci device
...
hotplug_handler_plug(); -> goto post_realize_fail;
pci_qdev_unrealize() -> pci_ich9_uninit() -> ahci_uninit()
...
g_free(s->dev);
^^^ free memory that holds children busses
return with error from device_set_realized()
As result later when qdev_device_add() tries to unparent ich9-ahci
after failed device_set_realized(),
object_unparent() -> object_property_del_child()
iterates over existing QOM children including buses added by
ide_bus_new() and tries to unparent them, which causes access to
freed memory where they where located.
Reported-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 1503938085-169486-1-git-send-email-imammedo@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
2017-09-18 22:01:25 +03:00
|
|
|
object_unparent(OBJECT(&ad->port));
|
2017-03-16 03:50:14 +03:00
|
|
|
}
|
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(s->dev);
|
2011-02-01 17:51:31 +03:00
|
|
|
}
|
|
|
|
|
2012-05-11 18:42:36 +04:00
|
|
|
void ahci_reset(AHCIState *s)
|
2010-12-14 03:34:40 +03:00
|
|
|
{
|
2011-09-12 12:19:25 +04:00
|
|
|
AHCIPortRegs *pr;
|
2010-12-14 03:34:40 +03:00
|
|
|
int i;
|
|
|
|
|
2017-09-18 22:01:26 +03:00
|
|
|
trace_ahci_reset(s);
|
|
|
|
|
2012-05-11 18:42:36 +04:00
|
|
|
s->control_regs.irqstatus = 0;
|
2013-09-29 01:09:35 +04:00
|
|
|
/* AHCI Enable (AE)
|
|
|
|
* The implementation of this bit is dependent upon the value of the
|
|
|
|
* CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and
|
|
|
|
* shall have a reset value of '0'. If CAP.SAM is '1', then AE shall be
|
|
|
|
* read-only and shall have a reset value of '1'.
|
|
|
|
*
|
|
|
|
* We set HOST_CAP_AHCI so we must enable AHCI at reset.
|
|
|
|
*/
|
|
|
|
s->control_regs.ghc = HOST_CTL_AHCI_EN;
|
2011-02-01 17:51:30 +03:00
|
|
|
|
2012-05-11 18:42:36 +04:00
|
|
|
for (i = 0; i < s->ports; i++) {
|
|
|
|
pr = &s->dev[i].port_regs;
|
2011-09-12 12:19:25 +04:00
|
|
|
pr->irq_stat = 0;
|
|
|
|
pr->irq_mask = 0;
|
|
|
|
pr->scr_ctl = 0;
|
ahci: properly reset PxCMD on HBA reset
While testing q35, I found that windows 7 (specifically, windows 7 ultimate
with sp1 x64), wouldn't install because it can't find the cdrom or disk drive.
The failure message is: 'A required cd/dvd device driver is missing. If you
have a driver floppy disk, CD, DVD, or USB flash drive, please insert it now.'
This can also be reproduced on piix by adding an ahci controller, and
observing that windows 7 does not see any devices behind it.
The problem is that when windows issues a HBA reset, qemu does not reset the
individual ports' PxCMD register. Windows 7 then reads back the PxCMD register
and presumably assumes that the ahci controller has already been initialized.
Windows then never sets up the PxIE register to enable interrupts, and thus it
never gets irqs back when it sends ata device inquiry commands.
This change brings qemu into ahci 1.3 specification compliance.
Section 10.4.3 HBA Reset:
"
When GHC.HR is set to '1', GHC.AE, GHC.IE, the IS register, and all port
register fields (except PxFB/PxFBU/PxCLB/PxCLBU) that are not HwInit in the
HBA's register memory space are reset.
"
I've also re-tested Fedora 16 and 17 to verify that they continue to work with
this change.
Signed-off-by: Jason Baron <jbaron@redhat.com>
Acked-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-09-05 00:08:08 +04:00
|
|
|
pr->cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON;
|
2012-05-11 18:42:36 +04:00
|
|
|
ahci_reset_port(s, i);
|
2010-12-14 03:34:40 +03:00
|
|
|
}
|
|
|
|
}
|
2012-01-26 15:43:47 +04:00
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
static const VMStateDescription vmstate_ncq_tfs = {
|
|
|
|
.name = "ncq state",
|
|
|
|
.version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_UINT32(sector_count, NCQTransferState),
|
|
|
|
VMSTATE_UINT64(lba, NCQTransferState),
|
|
|
|
VMSTATE_UINT8(tag, NCQTransferState),
|
|
|
|
VMSTATE_UINT8(cmd, NCQTransferState),
|
|
|
|
VMSTATE_UINT8(slot, NCQTransferState),
|
|
|
|
VMSTATE_BOOL(used, NCQTransferState),
|
|
|
|
VMSTATE_BOOL(halt, NCQTransferState),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-01-04 23:44:42 +04:00
|
|
|
static const VMStateDescription vmstate_ahci_device = {
|
|
|
|
.name = "ahci port",
|
|
|
|
.version_id = 1,
|
2014-04-16 17:32:32 +04:00
|
|
|
.fields = (VMStateField[]) {
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_IDE_BUS(port, AHCIDevice),
|
2015-02-23 19:18:03 +03:00
|
|
|
VMSTATE_IDE_DRIVE(port.ifs[0], AHCIDevice),
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_UINT32(port_state, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(finished, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.lst_addr_hi, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.fis_addr, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.fis_addr_hi, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.irq_stat, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.irq_mask, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.cmd, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.tfdata, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.sig, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.scr_stat, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.scr_ctl, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.scr_err, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.scr_act, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice),
|
2018-06-25 23:50:48 +03:00
|
|
|
VMSTATE_BOOL(done_first_drq, AHCIDevice),
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_INT32(busy_slot, AHCIDevice),
|
|
|
|
VMSTATE_BOOL(init_d2h_sent, AHCIDevice),
|
2015-07-04 09:06:05 +03:00
|
|
|
VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS,
|
|
|
|
1, vmstate_ncq_tfs, NCQTransferState),
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ahci_state_post_load(void *opaque, int version_id)
|
|
|
|
{
|
2015-07-04 09:06:05 +03:00
|
|
|
int i, j;
|
2013-01-04 23:44:42 +04:00
|
|
|
struct AHCIDevice *ad;
|
2015-07-04 09:06:05 +03:00
|
|
|
NCQTransferState *ncq_tfs;
|
2016-02-10 21:29:40 +03:00
|
|
|
AHCIPortRegs *pr;
|
2013-01-04 23:44:42 +04:00
|
|
|
AHCIState *s = opaque;
|
|
|
|
|
|
|
|
for (i = 0; i < s->ports; i++) {
|
|
|
|
ad = &s->dev[i];
|
2016-02-10 21:29:40 +03:00
|
|
|
pr = &ad->port_regs;
|
|
|
|
|
|
|
|
if (!(pr->cmd & PORT_CMD_START) && (pr->cmd & PORT_CMD_LIST_ON)) {
|
|
|
|
error_report("AHCI: DMA engine should be off, but status bit "
|
|
|
|
"indicates it is still running.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!(pr->cmd & PORT_CMD_FIS_RX) && (pr->cmd & PORT_CMD_FIS_ON)) {
|
|
|
|
error_report("AHCI: FIS RX engine should be off, but status bit "
|
|
|
|
"indicates it is still running.");
|
|
|
|
return -1;
|
|
|
|
}
|
2013-01-04 23:44:42 +04:00
|
|
|
|
2016-02-10 21:29:40 +03:00
|
|
|
/* After a migrate, the DMA/FIS engines are "off" and
|
|
|
|
* need to be conditionally restarted */
|
|
|
|
pr->cmd &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON);
|
|
|
|
if (ahci_cond_start_engines(ad) != 0) {
|
2015-05-22 21:13:44 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-07-04 09:06:05 +03:00
|
|
|
for (j = 0; j < AHCI_MAX_CMDS; j++) {
|
|
|
|
ncq_tfs = &ad->ncq_tfs[j];
|
|
|
|
ncq_tfs->drive = ad;
|
|
|
|
|
|
|
|
if (ncq_tfs->used != ncq_tfs->halt) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!ncq_tfs->halt) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!is_ncq(ncq_tfs->cmd)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ncq_tfs->slot != ncq_tfs->tag) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* If ncq_tfs->halt is justly set, the engine should be engaged,
|
|
|
|
* and the command list buffer should be mapped. */
|
|
|
|
ncq_tfs->cmdh = get_cmd_header(s, i, ncq_tfs->slot);
|
|
|
|
if (!ncq_tfs->cmdh) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ahci_populate_sglist(ncq_tfs->drive, &ncq_tfs->sglist,
|
2020-08-14 11:28:38 +03:00
|
|
|
ncq_tfs->cmdh,
|
|
|
|
ncq_tfs->sector_count * BDRV_SECTOR_SIZE,
|
2015-07-04 09:06:05 +03:00
|
|
|
0);
|
|
|
|
if (ncq_tfs->sector_count != ncq_tfs->sglist.size >> 9) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-04 23:44:42 +04:00
|
|
|
/*
|
2015-02-23 19:18:04 +03:00
|
|
|
* If an error is present, ad->busy_slot will be valid and not -1.
|
|
|
|
* In this case, an operation is waiting to resume and will re-check
|
|
|
|
* for additional AHCI commands to execute upon completion.
|
|
|
|
*
|
|
|
|
* In the case where no error was present, busy_slot will be -1,
|
|
|
|
* and we should check to see if there are additional commands waiting.
|
2013-01-04 23:44:42 +04:00
|
|
|
*/
|
2015-02-23 19:18:04 +03:00
|
|
|
if (ad->busy_slot == -1) {
|
|
|
|
check_cmd(s, i);
|
2015-02-23 19:18:05 +03:00
|
|
|
} else {
|
|
|
|
/* We are in the middle of a command, and may need to access
|
|
|
|
* the command header in guest memory again. */
|
|
|
|
if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-04 09:06:05 +03:00
|
|
|
ad->cur_cmd = get_cmd_header(s, i, ad->busy_slot);
|
2013-01-04 23:44:42 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const VMStateDescription vmstate_ahci = {
|
|
|
|
.name = "ahci",
|
|
|
|
.version_id = 1,
|
|
|
|
.post_load = ahci_state_post_load,
|
2014-04-16 17:32:32 +04:00
|
|
|
.fields = (VMStateField[]) {
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports,
|
|
|
|
vmstate_ahci_device, AHCIDevice),
|
|
|
|
VMSTATE_UINT32(control_regs.cap, AHCIState),
|
|
|
|
VMSTATE_UINT32(control_regs.ghc, AHCIState),
|
|
|
|
VMSTATE_UINT32(control_regs.irqstatus, AHCIState),
|
|
|
|
VMSTATE_UINT32(control_regs.impl, AHCIState),
|
|
|
|
VMSTATE_UINT32(control_regs.version, AHCIState),
|
|
|
|
VMSTATE_UINT32(idp_index, AHCIState),
|
2017-06-23 17:48:23 +03:00
|
|
|
VMSTATE_INT32_EQUAL(ports, AHCIState, NULL),
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2012-01-26 15:43:47 +04:00
|
|
|
static const VMStateDescription vmstate_sysbus_ahci = {
|
|
|
|
.name = "sysbus-ahci",
|
2014-04-16 17:32:32 +04:00
|
|
|
.fields = (VMStateField[]) {
|
2014-03-18 23:36:13 +04:00
|
|
|
VMSTATE_AHCI(ahci, SysbusAHCIState),
|
2013-01-04 23:44:42 +04:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
},
|
2012-01-26 15:43:47 +04:00
|
|
|
};
|
|
|
|
|
2012-05-11 18:42:36 +04:00
|
|
|
static void sysbus_ahci_reset(DeviceState *dev)
|
|
|
|
{
|
2013-07-01 14:18:30 +04:00
|
|
|
SysbusAHCIState *s = SYSBUS_AHCI(dev);
|
2012-05-11 18:42:36 +04:00
|
|
|
|
|
|
|
ahci_reset(&s->ahci);
|
|
|
|
}
|
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
static void sysbus_ahci_init(Object *obj)
|
2012-01-26 15:43:47 +04:00
|
|
|
{
|
2015-11-06 22:09:00 +03:00
|
|
|
SysbusAHCIState *s = SYSBUS_AHCI(obj);
|
|
|
|
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
2012-01-26 15:43:47 +04:00
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
ahci_init(&s->ahci, DEVICE(obj));
|
2013-07-01 14:18:31 +04:00
|
|
|
|
|
|
|
sysbus_init_mmio(sbd, &s->ahci.mem);
|
|
|
|
sysbus_init_irq(sbd, &s->ahci.irq);
|
2012-01-26 15:43:47 +04:00
|
|
|
}
|
|
|
|
|
2015-11-06 22:09:00 +03:00
|
|
|
static void sysbus_ahci_realize(DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
SysbusAHCIState *s = SYSBUS_AHCI(dev);
|
|
|
|
|
|
|
|
ahci_realize(&s->ahci, dev, &address_space_memory, s->num_ports);
|
|
|
|
}
|
|
|
|
|
2011-12-08 07:34:16 +04:00
|
|
|
static Property sysbus_ahci_properties[] = {
|
|
|
|
DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, num_ports, 1),
|
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
2012-01-24 23:12:29 +04:00
|
|
|
static void sysbus_ahci_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2012-01-24 23:12:29 +04:00
|
|
|
|
2013-07-01 14:18:31 +04:00
|
|
|
dc->realize = sysbus_ahci_realize;
|
2011-12-08 07:34:16 +04:00
|
|
|
dc->vmsd = &vmstate_sysbus_ahci;
|
2020-01-10 18:30:32 +03:00
|
|
|
device_class_set_props(dc, sysbus_ahci_properties);
|
2012-05-11 18:42:36 +04:00
|
|
|
dc->reset = sysbus_ahci_reset;
|
2013-07-29 18:17:45 +04:00
|
|
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
2012-01-24 23:12:29 +04:00
|
|
|
}
|
|
|
|
|
2013-01-10 19:19:07 +04:00
|
|
|
static const TypeInfo sysbus_ahci_info = {
|
2013-07-01 14:18:30 +04:00
|
|
|
.name = TYPE_SYSBUS_AHCI,
|
2011-12-08 07:34:16 +04:00
|
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
|
|
.instance_size = sizeof(SysbusAHCIState),
|
2015-11-06 22:09:00 +03:00
|
|
|
.instance_init = sysbus_ahci_init,
|
2011-12-08 07:34:16 +04:00
|
|
|
.class_init = sysbus_ahci_class_init,
|
2012-01-26 15:43:47 +04:00
|
|
|
};
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
static void sysbus_ahci_register_types(void)
|
2012-01-26 15:43:47 +04:00
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
type_register_static(&sysbus_ahci_info);
|
2012-01-26 15:43:47 +04:00
|
|
|
}
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
type_init(sysbus_ahci_register_types)
|
2014-10-01 22:19:29 +04:00
|
|
|
|
2017-07-18 18:47:56 +03:00
|
|
|
int32_t ahci_get_num_ports(PCIDevice *dev)
|
|
|
|
{
|
2020-09-03 01:42:40 +03:00
|
|
|
AHCIPCIState *d = ICH9_AHCI(dev);
|
2017-07-18 18:47:56 +03:00
|
|
|
AHCIState *ahci = &d->ahci;
|
|
|
|
|
|
|
|
return ahci->ports;
|
|
|
|
}
|
|
|
|
|
2014-10-01 22:19:29 +04:00
|
|
|
void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd)
|
|
|
|
{
|
2020-09-03 01:42:40 +03:00
|
|
|
AHCIPCIState *d = ICH9_AHCI(dev);
|
2014-10-01 22:19:29 +04:00
|
|
|
AHCIState *ahci = &d->ahci;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ahci->ports; i++) {
|
|
|
|
if (hd[i] == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-02-09 13:31:51 +03:00
|
|
|
ide_bus_create_drive(&ahci->dev[i].port, 0, hd[i]);
|
2014-10-01 22:19:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|