summaryrefslogtreecommitdiffstats
path: root/qemu/hw/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/hw/scsi')
-rw-r--r--qemu/hw/scsi/Makefile.objs1
-rw-r--r--qemu/hw/scsi/esp-pci.c2
-rw-r--r--qemu/hw/scsi/esp.c2
-rw-r--r--qemu/hw/scsi/lsi53c895a.c2
-rw-r--r--qemu/hw/scsi/megasas.c19
-rw-r--r--qemu/hw/scsi/mpi.h1153
-rw-r--r--qemu/hw/scsi/mptconfig.c905
-rw-r--r--qemu/hw/scsi/mptendian.c204
-rw-r--r--qemu/hw/scsi/mptsas.c1442
-rw-r--r--qemu/hw/scsi/mptsas.h100
-rw-r--r--qemu/hw/scsi/scsi-bus.c31
-rw-r--r--qemu/hw/scsi/scsi-disk.c166
-rw-r--r--qemu/hw/scsi/scsi-generic.c178
-rw-r--r--qemu/hw/scsi/spapr_vscsi.c4
-rw-r--r--qemu/hw/scsi/vhost-scsi.c23
-rw-r--r--qemu/hw/scsi/virtio-scsi-dataplane.c218
-rw-r--r--qemu/hw/scsi/virtio-scsi.c228
-rw-r--r--qemu/hw/scsi/vmw_pvscsi.c98
18 files changed, 4395 insertions, 381 deletions
diff --git a/qemu/hw/scsi/Makefile.objs b/qemu/hw/scsi/Makefile.objs
index 40c79d34c..5a2248be3 100644
--- a/qemu/hw/scsi/Makefile.objs
+++ b/qemu/hw/scsi/Makefile.objs
@@ -1,6 +1,7 @@
common-obj-y += scsi-disk.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
+common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o
common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o
common-obj-$(CONFIG_ESP) += esp.o
diff --git a/qemu/hw/scsi/esp-pci.c b/qemu/hw/scsi/esp-pci.c
index 8d2242d0a..595f88b35 100644
--- a/qemu/hw/scsi/esp-pci.c
+++ b/qemu/hw/scsi/esp-pci.c
@@ -23,10 +23,12 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/nvram/eeprom93xx.h"
#include "hw/scsi/esp.h"
#include "trace.h"
+#include "qapi/error.h"
#include "qemu/log.h"
#define TYPE_AM53C974_DEVICE "am53c974"
diff --git a/qemu/hw/scsi/esp.c b/qemu/hw/scsi/esp.c
index 272d13d63..8961be2f3 100644
--- a/qemu/hw/scsi/esp.c
+++ b/qemu/hw/scsi/esp.c
@@ -23,9 +23,11 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/scsi/esp.h"
#include "trace.h"
+#include "qapi/error.h"
#include "qemu/log.h"
/*
diff --git a/qemu/hw/scsi/lsi53c895a.c b/qemu/hw/scsi/lsi53c895a.c
index c5b0cc5ca..df205cdaf 100644
--- a/qemu/hw/scsi/lsi53c895a.c
+++ b/qemu/hw/scsi/lsi53c895a.c
@@ -13,7 +13,7 @@
* as well-behaved operating systems will not try to use them.
*/
-#include <assert.h>
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
diff --git a/qemu/hw/scsi/megasas.c b/qemu/hw/scsi/megasas.c
index a04369c5a..a63a58155 100644
--- a/qemu/hw/scsi/megasas.c
+++ b/qemu/hw/scsi/megasas.c
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "sysemu/dma.h"
@@ -431,7 +432,7 @@ static uint64_t megasas_fw_time(void)
static uint64_t megasas_get_sata_addr(uint16_t id)
{
uint64_t addr = (0x1221ULL << 48);
- return addr & (id << 24);
+ return addr | ((uint64_t)id << 24);
}
/*
@@ -718,7 +719,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
BusChild *kid;
int num_pd_disks = 0;
- memset(&info, 0x0, cmd->iov_size);
+ memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
dcmd_size);
@@ -744,7 +745,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
info.device.type = MFI_INFO_DEV_SAS3G;
info.device.port_count = 8;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
uint16_t pd_id;
if (num_pd_disks < 8) {
@@ -757,7 +758,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
memcpy(info.product_name, base_class->product_name, 24);
snprintf(info.serial_number, 32, "%s", s->hba_serial);
- snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
+ snprintf(info.package_version, 0x60, "%s-QEMU", qemu_hw_version());
memcpy(info.image_component[0].name, "APP", 3);
snprintf(info.image_component[0].version, 10, "%s-QEMU",
base_class->product_version);
@@ -960,7 +961,7 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
max_pd_disks = MFI_MAX_SYS_PDS;
}
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
uint16_t pd_id;
if (num_pd_disks >= max_pd_disks)
@@ -1136,7 +1137,7 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
max_ld_disks = MFI_MAX_LD;
}
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
if (num_ld_disks >= max_ld_disks) {
break;
@@ -1187,7 +1188,7 @@ static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
max_ld_disks = MFI_MAX_LD;
}
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
if (num_ld_disks >= max_ld_disks) {
break;
@@ -1327,7 +1328,7 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF);
struct mfi_array *array;
struct mfi_ld_config *ld;
@@ -2237,7 +2238,7 @@ static void megasas_soft_reset(MegasasState *s)
* after the initial reset.
*/
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ SCSIDevice *sdev = SCSI_DEVICE(kid->child);
sdev->unit_attention = SENSE_CODE(NO_SENSE);
scsi_device_unit_attention_reported(sdev);
diff --git a/qemu/hw/scsi/mpi.h b/qemu/hw/scsi/mpi.h
new file mode 100644
index 000000000..0568e1950
--- /dev/null
+++ b/qemu/hw/scsi/mpi.h
@@ -0,0 +1,1153 @@
+/*-
+ * Based on FreeBSD sys/dev/mpt/mpilib headers.
+ *
+ * Copyright (c) 2000-2010, LSI Logic Corporation and its contributors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon including
+ * a substantially similar Disclaimer requirement for further binary
+ * redistribution.
+ * 3. Neither the name of the LSI Logic Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT
+ * OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MPI_H
+#define MPI_H
+
+enum {
+ MPI_FUNCTION_SCSI_IO_REQUEST = 0x00,
+ MPI_FUNCTION_SCSI_TASK_MGMT = 0x01,
+ MPI_FUNCTION_IOC_INIT = 0x02,
+ MPI_FUNCTION_IOC_FACTS = 0x03,
+ MPI_FUNCTION_CONFIG = 0x04,
+ MPI_FUNCTION_PORT_FACTS = 0x05,
+ MPI_FUNCTION_PORT_ENABLE = 0x06,
+ MPI_FUNCTION_EVENT_NOTIFICATION = 0x07,
+ MPI_FUNCTION_EVENT_ACK = 0x08,
+ MPI_FUNCTION_FW_DOWNLOAD = 0x09,
+ MPI_FUNCTION_TARGET_CMD_BUFFER_POST = 0x0A,
+ MPI_FUNCTION_TARGET_ASSIST = 0x0B,
+ MPI_FUNCTION_TARGET_STATUS_SEND = 0x0C,
+ MPI_FUNCTION_TARGET_MODE_ABORT = 0x0D,
+ MPI_FUNCTION_FC_LINK_SRVC_BUF_POST = 0x0E,
+ MPI_FUNCTION_FC_LINK_SRVC_RSP = 0x0F,
+ MPI_FUNCTION_FC_EX_LINK_SRVC_SEND = 0x10,
+ MPI_FUNCTION_FC_ABORT = 0x11,
+ MPI_FUNCTION_FW_UPLOAD = 0x12,
+ MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND = 0x13,
+ MPI_FUNCTION_FC_PRIMITIVE_SEND = 0x14,
+
+ MPI_FUNCTION_RAID_ACTION = 0x15,
+ MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH = 0x16,
+
+ MPI_FUNCTION_TOOLBOX = 0x17,
+
+ MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR = 0x18,
+
+ MPI_FUNCTION_MAILBOX = 0x19,
+
+ MPI_FUNCTION_SMP_PASSTHROUGH = 0x1A,
+ MPI_FUNCTION_SAS_IO_UNIT_CONTROL = 0x1B,
+ MPI_FUNCTION_SATA_PASSTHROUGH = 0x1C,
+
+ MPI_FUNCTION_DIAG_BUFFER_POST = 0x1D,
+ MPI_FUNCTION_DIAG_RELEASE = 0x1E,
+
+ MPI_FUNCTION_SCSI_IO_32 = 0x1F,
+
+ MPI_FUNCTION_LAN_SEND = 0x20,
+ MPI_FUNCTION_LAN_RECEIVE = 0x21,
+ MPI_FUNCTION_LAN_RESET = 0x22,
+
+ MPI_FUNCTION_TARGET_ASSIST_EXTENDED = 0x23,
+ MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST = 0x24,
+ MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST = 0x25,
+
+ MPI_FUNCTION_INBAND_BUFFER_POST = 0x28,
+ MPI_FUNCTION_INBAND_SEND = 0x29,
+ MPI_FUNCTION_INBAND_RSP = 0x2A,
+ MPI_FUNCTION_INBAND_ABORT = 0x2B,
+
+ MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET = 0x40,
+ MPI_FUNCTION_IO_UNIT_RESET = 0x41,
+ MPI_FUNCTION_HANDSHAKE = 0x42,
+ MPI_FUNCTION_REPLY_FRAME_REMOVAL = 0x43,
+ MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL = 0x44,
+};
+
+/****************************************************************************/
+/* Registers */
+/****************************************************************************/
+
+enum {
+ MPI_IOC_STATE_RESET = 0x00000000,
+ MPI_IOC_STATE_READY = 0x10000000,
+ MPI_IOC_STATE_OPERATIONAL = 0x20000000,
+ MPI_IOC_STATE_FAULT = 0x40000000,
+
+ MPI_DOORBELL_OFFSET = 0x00000000,
+ MPI_DOORBELL_ACTIVE = 0x08000000, /* DoorbellUsed */
+ MPI_DOORBELL_WHO_INIT_MASK = 0x07000000,
+ MPI_DOORBELL_WHO_INIT_SHIFT = 24,
+ MPI_DOORBELL_FUNCTION_MASK = 0xFF000000,
+ MPI_DOORBELL_FUNCTION_SHIFT = 24,
+ MPI_DOORBELL_ADD_DWORDS_MASK = 0x00FF0000,
+ MPI_DOORBELL_ADD_DWORDS_SHIFT = 16,
+ MPI_DOORBELL_DATA_MASK = 0x0000FFFF,
+ MPI_DOORBELL_FUNCTION_SPECIFIC_MASK = 0x0000FFFF,
+
+ MPI_DB_HPBAC_VALUE_MASK = 0x0000F000,
+ MPI_DB_HPBAC_ENABLE_ACCESS = 0x01,
+ MPI_DB_HPBAC_DISABLE_ACCESS = 0x02,
+ MPI_DB_HPBAC_FREE_BUFFER = 0x03,
+
+ MPI_WRITE_SEQUENCE_OFFSET = 0x00000004,
+ MPI_WRSEQ_KEY_VALUE_MASK = 0x0000000F,
+ MPI_WRSEQ_1ST_KEY_VALUE = 0x04,
+ MPI_WRSEQ_2ND_KEY_VALUE = 0x0B,
+ MPI_WRSEQ_3RD_KEY_VALUE = 0x02,
+ MPI_WRSEQ_4TH_KEY_VALUE = 0x07,
+ MPI_WRSEQ_5TH_KEY_VALUE = 0x0D,
+
+ MPI_DIAGNOSTIC_OFFSET = 0x00000008,
+ MPI_DIAG_CLEAR_FLASH_BAD_SIG = 0x00000400,
+ MPI_DIAG_PREVENT_IOC_BOOT = 0x00000200,
+ MPI_DIAG_DRWE = 0x00000080,
+ MPI_DIAG_FLASH_BAD_SIG = 0x00000040,
+ MPI_DIAG_RESET_HISTORY = 0x00000020,
+ MPI_DIAG_RW_ENABLE = 0x00000010,
+ MPI_DIAG_RESET_ADAPTER = 0x00000004,
+ MPI_DIAG_DISABLE_ARM = 0x00000002,
+ MPI_DIAG_MEM_ENABLE = 0x00000001,
+
+ MPI_TEST_BASE_ADDRESS_OFFSET = 0x0000000C,
+
+ MPI_DIAG_RW_DATA_OFFSET = 0x00000010,
+
+ MPI_DIAG_RW_ADDRESS_OFFSET = 0x00000014,
+
+ MPI_HOST_INTERRUPT_STATUS_OFFSET = 0x00000030,
+ MPI_HIS_IOP_DOORBELL_STATUS = 0x80000000,
+ MPI_HIS_REPLY_MESSAGE_INTERRUPT = 0x00000008,
+ MPI_HIS_DOORBELL_INTERRUPT = 0x00000001,
+
+ MPI_HOST_INTERRUPT_MASK_OFFSET = 0x00000034,
+ MPI_HIM_RIM = 0x00000008,
+ MPI_HIM_DIM = 0x00000001,
+
+ MPI_REQUEST_QUEUE_OFFSET = 0x00000040,
+ MPI_REQUEST_POST_FIFO_OFFSET = 0x00000040,
+
+ MPI_REPLY_QUEUE_OFFSET = 0x00000044,
+ MPI_REPLY_POST_FIFO_OFFSET = 0x00000044,
+ MPI_REPLY_FREE_FIFO_OFFSET = 0x00000044,
+
+ MPI_HI_PRI_REQUEST_QUEUE_OFFSET = 0x00000048,
+};
+
+#define MPI_ADDRESS_REPLY_A_BIT 0x80000000
+
+/****************************************************************************/
+/* Scatter/gather elements */
+/****************************************************************************/
+
+typedef struct MPISGEntry {
+ uint32_t FlagsLength;
+ union
+ {
+ uint32_t Address32;
+ uint64_t Address64;
+ } u;
+} QEMU_PACKED MPISGEntry;
+
+/* Flags field bit definitions */
+
+enum {
+ MPI_SGE_FLAGS_LAST_ELEMENT = 0x80000000,
+ MPI_SGE_FLAGS_END_OF_BUFFER = 0x40000000,
+ MPI_SGE_FLAGS_ELEMENT_TYPE_MASK = 0x30000000,
+ MPI_SGE_FLAGS_LOCAL_ADDRESS = 0x08000000,
+ MPI_SGE_FLAGS_DIRECTION = 0x04000000,
+ MPI_SGE_FLAGS_64_BIT_ADDRESSING = 0x02000000,
+ MPI_SGE_FLAGS_END_OF_LIST = 0x01000000,
+
+ MPI_SGE_LENGTH_MASK = 0x00FFFFFF,
+ MPI_SGE_CHAIN_LENGTH_MASK = 0x0000FFFF,
+
+ MPI_SGE_FLAGS_TRANSACTION_ELEMENT = 0x00000000,
+ MPI_SGE_FLAGS_SIMPLE_ELEMENT = 0x10000000,
+ MPI_SGE_FLAGS_CHAIN_ELEMENT = 0x30000000,
+
+ /* Direction */
+
+ MPI_SGE_FLAGS_IOC_TO_HOST = 0x00000000,
+ MPI_SGE_FLAGS_HOST_TO_IOC = 0x04000000,
+
+ MPI_SGE_CHAIN_OFFSET_MASK = 0x00FF0000,
+};
+
+#define MPI_SGE_CHAIN_OFFSET_SHIFT 16
+
+/****************************************************************************/
+/* Standard message request header for all request messages */
+/****************************************************************************/
+
+typedef struct MPIRequestHeader {
+ uint8_t Reserved[2]; /* function specific */
+ uint8_t ChainOffset;
+ uint8_t Function;
+ uint8_t Reserved1[3]; /* function specific */
+ uint8_t MsgFlags;
+ uint32_t MsgContext;
+} QEMU_PACKED MPIRequestHeader;
+
+
+typedef struct MPIDefaultReply {
+ uint8_t Reserved[2]; /* function specific */
+ uint8_t MsgLength;
+ uint8_t Function;
+ uint8_t Reserved1[3]; /* function specific */
+ uint8_t MsgFlags;
+ uint32_t MsgContext;
+ uint8_t Reserved2[2]; /* function specific */
+ uint16_t IOCStatus;
+ uint32_t IOCLogInfo;
+} QEMU_PACKED MPIDefaultReply;
+
+/* MsgFlags definition for all replies */
+
+#define MPI_MSGFLAGS_CONTINUATION_REPLY (0x80)
+
+enum {
+
+ /************************************************************************/
+ /* Common IOCStatus values for all replies */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_SUCCESS = 0x0000,
+ MPI_IOCSTATUS_INVALID_FUNCTION = 0x0001,
+ MPI_IOCSTATUS_BUSY = 0x0002,
+ MPI_IOCSTATUS_INVALID_SGL = 0x0003,
+ MPI_IOCSTATUS_INTERNAL_ERROR = 0x0004,
+ MPI_IOCSTATUS_RESERVED = 0x0005,
+ MPI_IOCSTATUS_INSUFFICIENT_RESOURCES = 0x0006,
+ MPI_IOCSTATUS_INVALID_FIELD = 0x0007,
+ MPI_IOCSTATUS_INVALID_STATE = 0x0008,
+ MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED = 0x0009,
+
+ /************************************************************************/
+ /* Config IOCStatus values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_CONFIG_INVALID_ACTION = 0x0020,
+ MPI_IOCSTATUS_CONFIG_INVALID_TYPE = 0x0021,
+ MPI_IOCSTATUS_CONFIG_INVALID_PAGE = 0x0022,
+ MPI_IOCSTATUS_CONFIG_INVALID_DATA = 0x0023,
+ MPI_IOCSTATUS_CONFIG_NO_DEFAULTS = 0x0024,
+ MPI_IOCSTATUS_CONFIG_CANT_COMMIT = 0x0025,
+
+ /************************************************************************/
+ /* SCSIIO Reply = SPI & FCP, initiator values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_SCSI_RECOVERED_ERROR = 0x0040,
+ MPI_IOCSTATUS_SCSI_INVALID_BUS = 0x0041,
+ MPI_IOCSTATUS_SCSI_INVALID_TARGETID = 0x0042,
+ MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE = 0x0043,
+ MPI_IOCSTATUS_SCSI_DATA_OVERRUN = 0x0044,
+ MPI_IOCSTATUS_SCSI_DATA_UNDERRUN = 0x0045,
+ MPI_IOCSTATUS_SCSI_IO_DATA_ERROR = 0x0046,
+ MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR = 0x0047,
+ MPI_IOCSTATUS_SCSI_TASK_TERMINATED = 0x0048,
+ MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH = 0x0049,
+ MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED = 0x004A,
+ MPI_IOCSTATUS_SCSI_IOC_TERMINATED = 0x004B,
+ MPI_IOCSTATUS_SCSI_EXT_TERMINATED = 0x004C,
+
+ /************************************************************************/
+ /* For use by SCSI Initiator and SCSI Target end-to-end data protection*/
+ /************************************************************************/
+
+ MPI_IOCSTATUS_EEDP_GUARD_ERROR = 0x004D,
+ MPI_IOCSTATUS_EEDP_REF_TAG_ERROR = 0x004E,
+ MPI_IOCSTATUS_EEDP_APP_TAG_ERROR = 0x004F,
+
+ /************************************************************************/
+ /* SCSI Target values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_TARGET_PRIORITY_IO = 0x0060,
+ MPI_IOCSTATUS_TARGET_INVALID_PORT = 0x0061,
+ MPI_IOCSTATUS_TARGET_INVALID_IO_INDEX = 0x0062,
+ MPI_IOCSTATUS_TARGET_ABORTED = 0x0063,
+ MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE = 0x0064,
+ MPI_IOCSTATUS_TARGET_NO_CONNECTION = 0x0065,
+ MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH = 0x006A,
+ MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT = 0x006B,
+ MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR = 0x006D,
+ MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA = 0x006E,
+ MPI_IOCSTATUS_TARGET_IU_TOO_SHORT = 0x006F,
+ MPI_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT = 0x0070,
+ MPI_IOCSTATUS_TARGET_NAK_RECEIVED = 0x0071,
+
+ /************************************************************************/
+ /* Fibre Channel Direct Access values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_FC_ABORTED = 0x0066,
+ MPI_IOCSTATUS_FC_RX_ID_INVALID = 0x0067,
+ MPI_IOCSTATUS_FC_DID_INVALID = 0x0068,
+ MPI_IOCSTATUS_FC_NODE_LOGGED_OUT = 0x0069,
+ MPI_IOCSTATUS_FC_EXCHANGE_CANCELED = 0x006C,
+
+ /************************************************************************/
+ /* LAN values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND = 0x0080,
+ MPI_IOCSTATUS_LAN_DEVICE_FAILURE = 0x0081,
+ MPI_IOCSTATUS_LAN_TRANSMIT_ERROR = 0x0082,
+ MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED = 0x0083,
+ MPI_IOCSTATUS_LAN_RECEIVE_ERROR = 0x0084,
+ MPI_IOCSTATUS_LAN_RECEIVE_ABORTED = 0x0085,
+ MPI_IOCSTATUS_LAN_PARTIAL_PACKET = 0x0086,
+ MPI_IOCSTATUS_LAN_CANCELED = 0x0087,
+
+ /************************************************************************/
+ /* Serial Attached SCSI values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED = 0x0090,
+ MPI_IOCSTATUS_SAS_SMP_DATA_OVERRUN = 0x0091,
+
+ /************************************************************************/
+ /* Inband values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_INBAND_ABORTED = 0x0098,
+ MPI_IOCSTATUS_INBAND_NO_CONNECTION = 0x0099,
+
+ /************************************************************************/
+ /* Diagnostic Tools values */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_DIAGNOSTIC_RELEASED = 0x00A0,
+
+ /************************************************************************/
+ /* IOCStatus flag to indicate that log info is available */
+ /************************************************************************/
+
+ MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE = 0x8000,
+ MPI_IOCSTATUS_MASK = 0x7FFF,
+
+ /************************************************************************/
+ /* LogInfo Types */
+ /************************************************************************/
+
+ MPI_IOCLOGINFO_TYPE_MASK = 0xF0000000,
+ MPI_IOCLOGINFO_TYPE_SHIFT = 28,
+ MPI_IOCLOGINFO_TYPE_NONE = 0x0,
+ MPI_IOCLOGINFO_TYPE_SCSI = 0x1,
+ MPI_IOCLOGINFO_TYPE_FC = 0x2,
+ MPI_IOCLOGINFO_TYPE_SAS = 0x3,
+ MPI_IOCLOGINFO_TYPE_ISCSI = 0x4,
+ MPI_IOCLOGINFO_LOG_DATA_MASK = 0x0FFFFFFF,
+};
+
+/****************************************************************************/
+/* SCSI IO messages and associated structures */
+/****************************************************************************/
+
+typedef struct MPIMsgSCSIIORequest {
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t CDBLength; /* 04h */
+ uint8_t SenseBufferLength; /* 05h */
+ uint8_t Reserved; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t LUN[8]; /* 0Ch */
+ uint32_t Control; /* 14h */
+ uint8_t CDB[16]; /* 18h */
+ uint32_t DataLength; /* 28h */
+ uint32_t SenseBufferLowAddr; /* 2Ch */
+} QEMU_PACKED MPIMsgSCSIIORequest;
+
+/* SCSI IO MsgFlags bits */
+
+#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH (0x01)
+#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 (0x00)
+#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 (0x01)
+
+#define MPI_SCSIIO_MSGFLGS_SENSE_LOCATION (0x02)
+#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00)
+#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02)
+
+#define MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR (0x04)
+
+/* SCSI IO LUN fields */
+
+#define MPI_SCSIIO_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI_SCSIIO_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI_SCSIIO_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI_SCSIIO_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI_SCSIIO_LUN_LEVEL_1_WORD (0xFF00)
+#define MPI_SCSIIO_LUN_LEVEL_1_DWORD (0x0000FF00)
+
+/* SCSI IO Control bits */
+
+#define MPI_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000)
+#define MPI_SCSIIO_CONTROL_NODATATRANSFER (0x00000000)
+#define MPI_SCSIIO_CONTROL_WRITE (0x01000000)
+#define MPI_SCSIIO_CONTROL_READ (0x02000000)
+
+#define MPI_SCSIIO_CONTROL_ADDCDBLEN_MASK (0x3C000000)
+#define MPI_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26)
+
+#define MPI_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700)
+#define MPI_SCSIIO_CONTROL_SIMPLEQ (0x00000000)
+#define MPI_SCSIIO_CONTROL_HEADOFQ (0x00000100)
+#define MPI_SCSIIO_CONTROL_ORDEREDQ (0x00000200)
+#define MPI_SCSIIO_CONTROL_ACAQ (0x00000400)
+#define MPI_SCSIIO_CONTROL_UNTAGGED (0x00000500)
+#define MPI_SCSIIO_CONTROL_NO_DISCONNECT (0x00000700)
+
+#define MPI_SCSIIO_CONTROL_TASKMANAGE_MASK (0x00FF0000)
+#define MPI_SCSIIO_CONTROL_OBSOLETE (0x00800000)
+#define MPI_SCSIIO_CONTROL_CLEAR_ACA_RSV (0x00400000)
+#define MPI_SCSIIO_CONTROL_TARGET_RESET (0x00200000)
+#define MPI_SCSIIO_CONTROL_LUN_RESET_RSV (0x00100000)
+#define MPI_SCSIIO_CONTROL_RESERVED (0x00080000)
+#define MPI_SCSIIO_CONTROL_CLR_TASK_SET_RSV (0x00040000)
+#define MPI_SCSIIO_CONTROL_ABORT_TASK_SET (0x00020000)
+#define MPI_SCSIIO_CONTROL_RESERVED2 (0x00010000)
+
+/* SCSI IO reply structure */
+typedef struct MPIMsgSCSIIOReply
+{
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t CDBLength; /* 04h */
+ uint8_t SenseBufferLength; /* 05h */
+ uint8_t Reserved; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t SCSIStatus; /* 0Ch */
+ uint8_t SCSIState; /* 0Dh */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint32_t TransferCount; /* 14h */
+ uint32_t SenseCount; /* 18h */
+ uint32_t ResponseInfo; /* 1Ch */
+ uint16_t TaskTag; /* 20h */
+ uint16_t Reserved1; /* 22h */
+} QEMU_PACKED MPIMsgSCSIIOReply;
+
+/* SCSI IO Reply SCSIStatus values (SAM-2 status codes) */
+
+#define MPI_SCSI_STATUS_SUCCESS (0x00)
+#define MPI_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI_SCSI_STATUS_BUSY (0x08)
+#define MPI_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI_SCSI_STATUS_COMMAND_TERMINATED (0x22)
+#define MPI_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI_SCSI_STATUS_ACA_ACTIVE (0x30)
+
+#define MPI_SCSI_STATUS_FCPEXT_DEVICE_LOGGED_OUT (0x80)
+#define MPI_SCSI_STATUS_FCPEXT_NO_LINK (0x81)
+#define MPI_SCSI_STATUS_FCPEXT_UNASSIGNED (0x82)
+
+
+/* SCSI IO Reply SCSIState values */
+
+#define MPI_SCSI_STATE_AUTOSENSE_VALID (0x01)
+#define MPI_SCSI_STATE_AUTOSENSE_FAILED (0x02)
+#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI_SCSI_STATE_TERMINATED (0x08)
+#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
+#define MPI_SCSI_STATE_QUEUE_TAG_REJECTED (0x20)
+
+/* SCSI IO Reply ResponseInfo values */
+/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */
+
+#define MPI_SCSI_RSP_INFO_FUNCTION_COMPLETE (0x00000000)
+#define MPI_SCSI_RSP_INFO_FCP_BURST_LEN_ERROR (0x01000000)
+#define MPI_SCSI_RSP_INFO_CMND_FIELDS_INVALID (0x02000000)
+#define MPI_SCSI_RSP_INFO_FCP_DATA_RO_ERROR (0x03000000)
+#define MPI_SCSI_RSP_INFO_TASK_MGMT_UNSUPPORTED (0x04000000)
+#define MPI_SCSI_RSP_INFO_TASK_MGMT_FAILED (0x05000000)
+#define MPI_SCSI_RSP_INFO_SPI_LQ_INVALID_TYPE (0x06000000)
+
+#define MPI_SCSI_TASKTAG_UNKNOWN (0xFFFF)
+
+
+/****************************************************************************/
+/* SCSI Task Management messages */
+/****************************************************************************/
+
+typedef struct MPIMsgSCSITaskMgmt {
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved; /* 04h */
+ uint8_t TaskType; /* 05h */
+ uint8_t Reserved1; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t LUN[8]; /* 0Ch */
+ uint32_t Reserved2[7]; /* 14h */
+ uint32_t TaskMsgContext; /* 30h */
+} QEMU_PACKED MPIMsgSCSITaskMgmt;
+
+enum {
+ /* TaskType values */
+
+ MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK = 0x01,
+ MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET = 0x02,
+ MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET = 0x03,
+ MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS = 0x04,
+ MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET = 0x05,
+ MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET = 0x06,
+ MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK = 0x07,
+ MPI_SCSITASKMGMT_TASKTYPE_CLR_ACA = 0x08,
+
+ /* MsgFlags bits */
+
+ MPI_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU = 0x01,
+
+ MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION = 0x00,
+ MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION = 0x02,
+ MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION = 0x04,
+
+ MPI_SCSITASKMGMT_MSGFLAGS_SOFT_RESET_OPTION = 0x08,
+};
+
+/* SCSI Task Management Reply */
+typedef struct MPIMsgSCSITaskMgmtReply {
+ uint8_t TargetID; /* 00h */
+ uint8_t Bus; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t ResponseCode; /* 04h */
+ uint8_t TaskType; /* 05h */
+ uint8_t Reserved1; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[2]; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint32_t TerminationCount; /* 14h */
+} QEMU_PACKED MPIMsgSCSITaskMgmtReply;
+
+/* ResponseCode values */
+enum {
+ MPI_SCSITASKMGMT_RSP_TM_COMPLETE = 0x00,
+ MPI_SCSITASKMGMT_RSP_INVALID_FRAME = 0x02,
+ MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED = 0x04,
+ MPI_SCSITASKMGMT_RSP_TM_FAILED = 0x05,
+ MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED = 0x08,
+ MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN = 0x09,
+ MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC = 0x80,
+};
+
+/****************************************************************************/
+/* IOCInit message */
+/****************************************************************************/
+
+typedef struct MPIMsgIOCInit {
+ uint8_t WhoInit; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Flags; /* 04h */
+ uint8_t MaxDevices; /* 05h */
+ uint8_t MaxBuses; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t ReplyFrameSize; /* 0Ch */
+ uint8_t Reserved1[2]; /* 0Eh */
+ uint32_t HostMfaHighAddr; /* 10h */
+ uint32_t SenseBufferHighAddr; /* 14h */
+ uint32_t ReplyFifoHostSignalingAddr; /* 18h */
+ MPISGEntry HostPageBufferSGE; /* 1Ch */
+ uint16_t MsgVersion; /* 28h */
+ uint16_t HeaderVersion; /* 2Ah */
+} QEMU_PACKED MPIMsgIOCInit;
+
+enum {
+ /* WhoInit values */
+
+ MPI_WHOINIT_NO_ONE = 0x00,
+ MPI_WHOINIT_SYSTEM_BIOS = 0x01,
+ MPI_WHOINIT_ROM_BIOS = 0x02,
+ MPI_WHOINIT_PCI_PEER = 0x03,
+ MPI_WHOINIT_HOST_DRIVER = 0x04,
+ MPI_WHOINIT_MANUFACTURER = 0x05,
+
+ /* Flags values */
+
+ MPI_IOCINIT_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04,
+ MPI_IOCINIT_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02,
+ MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE = 0x01,
+
+ /* MsgVersion */
+
+ MPI_IOCINIT_MSGVERSION_MAJOR_MASK = 0xFF00,
+ MPI_IOCINIT_MSGVERSION_MAJOR_SHIFT = 8,
+ MPI_IOCINIT_MSGVERSION_MINOR_MASK = 0x00FF,
+ MPI_IOCINIT_MSGVERSION_MINOR_SHIFT = 0,
+
+ /* HeaderVersion */
+
+ MPI_IOCINIT_HEADERVERSION_UNIT_MASK = 0xFF00,
+ MPI_IOCINIT_HEADERVERSION_UNIT_SHIFT = 8,
+ MPI_IOCINIT_HEADERVERSION_DEV_MASK = 0x00FF,
+ MPI_IOCINIT_HEADERVERSION_DEV_SHIFT = 0,
+};
+
+typedef struct MPIMsgIOCInitReply {
+ uint8_t WhoInit; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Flags; /* 04h */
+ uint8_t MaxDevices; /* 05h */
+ uint8_t MaxBuses; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+} QEMU_PACKED MPIMsgIOCInitReply;
+
+
+
+/****************************************************************************/
+/* IOC Facts message */
+/****************************************************************************/
+
+typedef struct MPIMsgIOCFacts {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 01h */
+ uint8_t Function; /* 02h */
+ uint8_t Reserved1[3]; /* 03h */
+ uint8_t MsgFlags; /* 04h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgIOCFacts;
+
+/* IOC Facts Reply */
+typedef struct MPIMsgIOCFactsReply {
+ uint16_t MsgVersion; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t HeaderVersion; /* 04h */
+ uint8_t IOCNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t IOCExceptions; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint8_t MaxChainDepth; /* 14h */
+ uint8_t WhoInit; /* 15h */
+ uint8_t BlockSize; /* 16h */
+ uint8_t Flags; /* 17h */
+ uint16_t ReplyQueueDepth; /* 18h */
+ uint16_t RequestFrameSize; /* 1Ah */
+ uint16_t Reserved_0101_FWVersion; /* 1Ch */ /* obsolete 16-bit FWVersion */
+ uint16_t ProductID; /* 1Eh */
+ uint32_t CurrentHostMfaHighAddr; /* 20h */
+ uint16_t GlobalCredits; /* 24h */
+ uint8_t NumberOfPorts; /* 26h */
+ uint8_t EventState; /* 27h */
+ uint32_t CurrentSenseBufferHighAddr; /* 28h */
+ uint16_t CurReplyFrameSize; /* 2Ch */
+ uint8_t MaxDevices; /* 2Eh */
+ uint8_t MaxBuses; /* 2Fh */
+ uint32_t FWImageSize; /* 30h */
+ uint32_t IOCCapabilities; /* 34h */
+ uint8_t FWVersionDev; /* 38h */
+ uint8_t FWVersionUnit; /* 39h */
+ uint8_t FWVersionMinor; /* 3ah */
+ uint8_t FWVersionMajor; /* 3bh */
+ uint16_t HighPriorityQueueDepth; /* 3Ch */
+ uint16_t Reserved2; /* 3Eh */
+ MPISGEntry HostPageBufferSGE; /* 40h */
+ uint32_t ReplyFifoHostSignalingAddr; /* 4Ch */
+} QEMU_PACKED MPIMsgIOCFactsReply;
+
+enum {
+ MPI_IOCFACTS_MSGVERSION_MAJOR_MASK = 0xFF00,
+ MPI_IOCFACTS_MSGVERSION_MAJOR_SHIFT = 8,
+ MPI_IOCFACTS_MSGVERSION_MINOR_MASK = 0x00FF,
+ MPI_IOCFACTS_MSGVERSION_MINOR_SHIFT = 0,
+
+ MPI_IOCFACTS_HDRVERSION_UNIT_MASK = 0xFF00,
+ MPI_IOCFACTS_HDRVERSION_UNIT_SHIFT = 8,
+ MPI_IOCFACTS_HDRVERSION_DEV_MASK = 0x00FF,
+ MPI_IOCFACTS_HDRVERSION_DEV_SHIFT = 0,
+
+ MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL = 0x0001,
+ MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID = 0x0002,
+ MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL = 0x0004,
+ MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL = 0x0008,
+ MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED = 0x0010,
+
+ MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT = 0x01,
+ MPI_IOCFACTS_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02,
+ MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04,
+
+ MPI_IOCFACTS_EVENTSTATE_DISABLED = 0x00,
+ MPI_IOCFACTS_EVENTSTATE_ENABLED = 0x01,
+
+ MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q = 0x00000001,
+ MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL = 0x00000002,
+ MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING = 0x00000004,
+ MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER = 0x00000008,
+ MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER = 0x00000010,
+ MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER = 0x00000020,
+ MPI_IOCFACTS_CAPABILITY_EEDP = 0x00000040,
+ MPI_IOCFACTS_CAPABILITY_BIDIRECTIONAL = 0x00000080,
+ MPI_IOCFACTS_CAPABILITY_MULTICAST = 0x00000100,
+ MPI_IOCFACTS_CAPABILITY_SCSIIO32 = 0x00000200,
+ MPI_IOCFACTS_CAPABILITY_NO_SCSIIO16 = 0x00000400,
+ MPI_IOCFACTS_CAPABILITY_TLR = 0x00000800,
+};
+
+/****************************************************************************/
+/* Port Facts message and Reply */
+/****************************************************************************/
+
+typedef struct MPIMsgPortFacts {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t PortNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgPortFacts;
+
+typedef struct MPIMsgPortFactsReply {
+ uint16_t Reserved; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t Reserved1; /* 04h */
+ uint8_t PortNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint8_t Reserved3; /* 14h */
+ uint8_t PortType; /* 15h */
+ uint16_t MaxDevices; /* 16h */
+ uint16_t PortSCSIID; /* 18h */
+ uint16_t ProtocolFlags; /* 1Ah */
+ uint16_t MaxPostedCmdBuffers; /* 1Ch */
+ uint16_t MaxPersistentIDs; /* 1Eh */
+ uint16_t MaxLanBuckets; /* 20h */
+ uint8_t MaxInitiators; /* 22h */
+ uint8_t Reserved4; /* 23h */
+ uint32_t Reserved5; /* 24h */
+} QEMU_PACKED MPIMsgPortFactsReply;
+
+
+enum {
+ /* PortTypes values */
+ MPI_PORTFACTS_PORTTYPE_INACTIVE = 0x00,
+ MPI_PORTFACTS_PORTTYPE_SCSI = 0x01,
+ MPI_PORTFACTS_PORTTYPE_FC = 0x10,
+ MPI_PORTFACTS_PORTTYPE_ISCSI = 0x20,
+ MPI_PORTFACTS_PORTTYPE_SAS = 0x30,
+
+ /* ProtocolFlags values */
+ MPI_PORTFACTS_PROTOCOL_LOGBUSADDR = 0x01,
+ MPI_PORTFACTS_PROTOCOL_LAN = 0x02,
+ MPI_PORTFACTS_PROTOCOL_TARGET = 0x04,
+ MPI_PORTFACTS_PROTOCOL_INITIATOR = 0x08,
+};
+
+
+/****************************************************************************/
+/* Port Enable Message */
+/****************************************************************************/
+
+typedef struct MPIMsgPortEnable {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t PortNumber; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgPortEnable;
+
+typedef struct MPIMsgPortEnableReply {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t PortNumber; /* 05h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+} QEMU_PACKED MPIMsgPortEnableReply;
+
+/****************************************************************************/
+/* Event Notification messages */
+/****************************************************************************/
+
+typedef struct MPIMsgEventNotify {
+ uint8_t Switch; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[3]; /* 04h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+} QEMU_PACKED MPIMsgEventNotify;
+
+/* Event Notification Reply */
+
+typedef struct MPIMsgEventNotifyReply {
+ uint16_t EventDataLength; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[2]; /* 04h */
+ uint8_t AckRequired; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[2]; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint32_t Event; /* 14h */
+ uint32_t EventContext; /* 18h */
+ uint32_t Data[1]; /* 1Ch */
+} QEMU_PACKED MPIMsgEventNotifyReply;
+
+/* Event Acknowledge */
+
+typedef struct MPIMsgEventAck {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[3]; /* 04h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint32_t Event; /* 0Ch */
+ uint32_t EventContext; /* 10h */
+} QEMU_PACKED MPIMsgEventAck;
+
+typedef struct MPIMsgEventAckReply {
+ uint8_t Reserved[2]; /* 00h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint8_t Reserved1[3]; /* 04h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint16_t Reserved2; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+} QEMU_PACKED MPIMsgEventAckReply;
+
+enum {
+ /* Switch */
+
+ MPI_EVENT_NOTIFICATION_SWITCH_OFF = 0x00,
+ MPI_EVENT_NOTIFICATION_SWITCH_ON = 0x01,
+
+ /* Event */
+
+ MPI_EVENT_NONE = 0x00000000,
+ MPI_EVENT_LOG_DATA = 0x00000001,
+ MPI_EVENT_STATE_CHANGE = 0x00000002,
+ MPI_EVENT_UNIT_ATTENTION = 0x00000003,
+ MPI_EVENT_IOC_BUS_RESET = 0x00000004,
+ MPI_EVENT_EXT_BUS_RESET = 0x00000005,
+ MPI_EVENT_RESCAN = 0x00000006,
+ MPI_EVENT_LINK_STATUS_CHANGE = 0x00000007,
+ MPI_EVENT_LOOP_STATE_CHANGE = 0x00000008,
+ MPI_EVENT_LOGOUT = 0x00000009,
+ MPI_EVENT_EVENT_CHANGE = 0x0000000A,
+ MPI_EVENT_INTEGRATED_RAID = 0x0000000B,
+ MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE = 0x0000000C,
+ MPI_EVENT_ON_BUS_TIMER_EXPIRED = 0x0000000D,
+ MPI_EVENT_QUEUE_FULL = 0x0000000E,
+ MPI_EVENT_SAS_DEVICE_STATUS_CHANGE = 0x0000000F,
+ MPI_EVENT_SAS_SES = 0x00000010,
+ MPI_EVENT_PERSISTENT_TABLE_FULL = 0x00000011,
+ MPI_EVENT_SAS_PHY_LINK_STATUS = 0x00000012,
+ MPI_EVENT_SAS_DISCOVERY_ERROR = 0x00000013,
+ MPI_EVENT_IR_RESYNC_UPDATE = 0x00000014,
+ MPI_EVENT_IR2 = 0x00000015,
+ MPI_EVENT_SAS_DISCOVERY = 0x00000016,
+ MPI_EVENT_SAS_BROADCAST_PRIMITIVE = 0x00000017,
+ MPI_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE = 0x00000018,
+ MPI_EVENT_SAS_INIT_TABLE_OVERFLOW = 0x00000019,
+ MPI_EVENT_SAS_SMP_ERROR = 0x0000001A,
+ MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE = 0x0000001B,
+ MPI_EVENT_LOG_ENTRY_ADDED = 0x00000021,
+
+ /* AckRequired field values */
+
+ MPI_EVENT_NOTIFICATION_ACK_NOT_REQUIRED = 0x00,
+ MPI_EVENT_NOTIFICATION_ACK_REQUIRED = 0x01,
+};
+
+/****************************************************************************
+* Config Request Message
+****************************************************************************/
+
+typedef struct MPIMsgConfig {
+ uint8_t Action; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t ChainOffset; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t ExtPageLength; /* 04h */
+ uint8_t ExtPageType; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[8]; /* 0Ch */
+ uint8_t PageVersion; /* 14h */
+ uint8_t PageLength; /* 15h */
+ uint8_t PageNumber; /* 16h */
+ uint8_t PageType; /* 17h */
+ uint32_t PageAddress; /* 18h */
+ MPISGEntry PageBufferSGE; /* 1Ch */
+} QEMU_PACKED MPIMsgConfig;
+
+/* Action field values */
+
+enum {
+ MPI_CONFIG_ACTION_PAGE_HEADER = 0x00,
+ MPI_CONFIG_ACTION_PAGE_READ_CURRENT = 0x01,
+ MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT = 0x02,
+ MPI_CONFIG_ACTION_PAGE_DEFAULT = 0x03,
+ MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM = 0x04,
+ MPI_CONFIG_ACTION_PAGE_READ_DEFAULT = 0x05,
+ MPI_CONFIG_ACTION_PAGE_READ_NVRAM = 0x06,
+};
+
+
+/* Config Reply Message */
+typedef struct MPIMsgConfigReply {
+ uint8_t Action; /* 00h */
+ uint8_t Reserved; /* 01h */
+ uint8_t MsgLength; /* 02h */
+ uint8_t Function; /* 03h */
+ uint16_t ExtPageLength; /* 04h */
+ uint8_t ExtPageType; /* 06h */
+ uint8_t MsgFlags; /* 07h */
+ uint32_t MsgContext; /* 08h */
+ uint8_t Reserved2[2]; /* 0Ch */
+ uint16_t IOCStatus; /* 0Eh */
+ uint32_t IOCLogInfo; /* 10h */
+ uint8_t PageVersion; /* 14h */
+ uint8_t PageLength; /* 15h */
+ uint8_t PageNumber; /* 16h */
+ uint8_t PageType; /* 17h */
+} QEMU_PACKED MPIMsgConfigReply;
+
+enum {
+ /* PageAddress field values */
+ MPI_CONFIG_PAGEATTR_READ_ONLY = 0x00,
+ MPI_CONFIG_PAGEATTR_CHANGEABLE = 0x10,
+ MPI_CONFIG_PAGEATTR_PERSISTENT = 0x20,
+ MPI_CONFIG_PAGEATTR_RO_PERSISTENT = 0x30,
+ MPI_CONFIG_PAGEATTR_MASK = 0xF0,
+
+ MPI_CONFIG_PAGETYPE_IO_UNIT = 0x00,
+ MPI_CONFIG_PAGETYPE_IOC = 0x01,
+ MPI_CONFIG_PAGETYPE_BIOS = 0x02,
+ MPI_CONFIG_PAGETYPE_SCSI_PORT = 0x03,
+ MPI_CONFIG_PAGETYPE_SCSI_DEVICE = 0x04,
+ MPI_CONFIG_PAGETYPE_FC_PORT = 0x05,
+ MPI_CONFIG_PAGETYPE_FC_DEVICE = 0x06,
+ MPI_CONFIG_PAGETYPE_LAN = 0x07,
+ MPI_CONFIG_PAGETYPE_RAID_VOLUME = 0x08,
+ MPI_CONFIG_PAGETYPE_MANUFACTURING = 0x09,
+ MPI_CONFIG_PAGETYPE_RAID_PHYSDISK = 0x0A,
+ MPI_CONFIG_PAGETYPE_INBAND = 0x0B,
+ MPI_CONFIG_PAGETYPE_EXTENDED = 0x0F,
+ MPI_CONFIG_PAGETYPE_MASK = 0x0F,
+
+ MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT = 0x10,
+ MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER = 0x11,
+ MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE = 0x12,
+ MPI_CONFIG_EXTPAGETYPE_SAS_PHY = 0x13,
+ MPI_CONFIG_EXTPAGETYPE_LOG = 0x14,
+ MPI_CONFIG_EXTPAGETYPE_ENCLOSURE = 0x15,
+
+ MPI_SCSI_PORT_PGAD_PORT_MASK = 0x000000FF,
+
+ MPI_SCSI_DEVICE_FORM_MASK = 0xF0000000,
+ MPI_SCSI_DEVICE_FORM_BUS_TID = 0x00000000,
+ MPI_SCSI_DEVICE_TARGET_ID_MASK = 0x000000FF,
+ MPI_SCSI_DEVICE_TARGET_ID_SHIFT = 0,
+ MPI_SCSI_DEVICE_BUS_MASK = 0x0000FF00,
+ MPI_SCSI_DEVICE_BUS_SHIFT = 8,
+ MPI_SCSI_DEVICE_FORM_TARGET_MODE = 0x10000000,
+ MPI_SCSI_DEVICE_TM_RESPOND_ID_MASK = 0x000000FF,
+ MPI_SCSI_DEVICE_TM_RESPOND_ID_SHIFT = 0,
+ MPI_SCSI_DEVICE_TM_BUS_MASK = 0x0000FF00,
+ MPI_SCSI_DEVICE_TM_BUS_SHIFT = 8,
+ MPI_SCSI_DEVICE_TM_INIT_ID_MASK = 0x00FF0000,
+ MPI_SCSI_DEVICE_TM_INIT_ID_SHIFT = 16,
+
+ MPI_FC_PORT_PGAD_PORT_MASK = 0xF0000000,
+ MPI_FC_PORT_PGAD_PORT_SHIFT = 28,
+ MPI_FC_PORT_PGAD_FORM_MASK = 0x0F000000,
+ MPI_FC_PORT_PGAD_FORM_INDEX = 0x01000000,
+ MPI_FC_PORT_PGAD_INDEX_MASK = 0x0000FFFF,
+ MPI_FC_PORT_PGAD_INDEX_SHIFT = 0,
+
+ MPI_FC_DEVICE_PGAD_PORT_MASK = 0xF0000000,
+ MPI_FC_DEVICE_PGAD_PORT_SHIFT = 28,
+ MPI_FC_DEVICE_PGAD_FORM_MASK = 0x0F000000,
+ MPI_FC_DEVICE_PGAD_FORM_NEXT_DID = 0x00000000,
+ MPI_FC_DEVICE_PGAD_ND_PORT_MASK = 0xF0000000,
+ MPI_FC_DEVICE_PGAD_ND_PORT_SHIFT = 28,
+ MPI_FC_DEVICE_PGAD_ND_DID_MASK = 0x00FFFFFF,
+ MPI_FC_DEVICE_PGAD_ND_DID_SHIFT = 0,
+ MPI_FC_DEVICE_PGAD_FORM_BUS_TID = 0x01000000,
+ MPI_FC_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00,
+ MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT = 8,
+ MPI_FC_DEVICE_PGAD_BT_TID_MASK = 0x000000FF,
+ MPI_FC_DEVICE_PGAD_BT_TID_SHIFT = 0,
+
+ MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK = 0x000000FF,
+ MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT = 0,
+
+ MPI_SAS_EXPAND_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_EXPAND_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000,
+ MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM = 0x00000001,
+ MPI_SAS_EXPAND_PGAD_FORM_HANDLE = 0x00000002,
+ MPI_SAS_EXPAND_PGAD_GNH_MASK_HANDLE = 0x0000FFFF,
+ MPI_SAS_EXPAND_PGAD_GNH_SHIFT_HANDLE = 0,
+ MPI_SAS_EXPAND_PGAD_HPN_MASK_PHY = 0x00FF0000,
+ MPI_SAS_EXPAND_PGAD_HPN_SHIFT_PHY = 16,
+ MPI_SAS_EXPAND_PGAD_HPN_MASK_HANDLE = 0x0000FFFF,
+ MPI_SAS_EXPAND_PGAD_HPN_SHIFT_HANDLE = 0,
+ MPI_SAS_EXPAND_PGAD_H_MASK_HANDLE = 0x0000FFFF,
+ MPI_SAS_EXPAND_PGAD_H_SHIFT_HANDLE = 0,
+
+ MPI_SAS_DEVICE_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_DEVICE_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000,
+ MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID = 0x00000001,
+ MPI_SAS_DEVICE_PGAD_FORM_HANDLE = 0x00000002,
+ MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_DEVICE_PGAD_GNH_HANDLE_SHIFT = 0,
+ MPI_SAS_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00,
+ MPI_SAS_DEVICE_PGAD_BT_BUS_SHIFT = 8,
+ MPI_SAS_DEVICE_PGAD_BT_TID_MASK = 0x000000FF,
+ MPI_SAS_DEVICE_PGAD_BT_TID_SHIFT = 0,
+ MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_DEVICE_PGAD_H_HANDLE_SHIFT = 0,
+
+ MPI_SAS_PHY_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_PHY_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER = 0x0,
+ MPI_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX = 0x1,
+ MPI_SAS_PHY_PGAD_PHY_NUMBER_MASK = 0x000000FF,
+ MPI_SAS_PHY_PGAD_PHY_NUMBER_SHIFT = 0,
+ MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK = 0x0000FFFF,
+ MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_SHIFT = 0,
+
+ MPI_SAS_ENCLOS_PGAD_FORM_MASK = 0xF0000000,
+ MPI_SAS_ENCLOS_PGAD_FORM_SHIFT = 28,
+ MPI_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000,
+ MPI_SAS_ENCLOS_PGAD_FORM_HANDLE = 0x00000001,
+ MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_SHIFT = 0,
+ MPI_SAS_ENCLOS_PGAD_H_HANDLE_MASK = 0x0000FFFF,
+ MPI_SAS_ENCLOS_PGAD_H_HANDLE_SHIFT = 0,
+};
+
+/* Too many structs and definitions... see mptconfig.c for the few
+ * that are used.
+ */
+
+/****************************************************************************/
+/* Firmware Upload message and associated structures */
+/****************************************************************************/
+
+enum {
+ /* defines for using the ProductId field */
+ MPI_FW_HEADER_PID_TYPE_MASK = 0xF000,
+ MPI_FW_HEADER_PID_TYPE_SCSI = 0x0000,
+ MPI_FW_HEADER_PID_TYPE_FC = 0x1000,
+ MPI_FW_HEADER_PID_TYPE_SAS = 0x2000,
+
+ MPI_FW_HEADER_PID_PROD_MASK = 0x0F00,
+ MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI = 0x0100,
+ MPI_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI = 0x0200,
+ MPI_FW_HEADER_PID_PROD_TARGET_SCSI = 0x0300,
+ MPI_FW_HEADER_PID_PROD_IM_SCSI = 0x0400,
+ MPI_FW_HEADER_PID_PROD_IS_SCSI = 0x0500,
+ MPI_FW_HEADER_PID_PROD_CTX_SCSI = 0x0600,
+ MPI_FW_HEADER_PID_PROD_IR_SCSI = 0x0700,
+
+ MPI_FW_HEADER_PID_FAMILY_MASK = 0x00FF,
+
+ /* SCSI */
+ MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI = 0x0001,
+ MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI = 0x0002,
+ MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI = 0x0003,
+ MPI_FW_HEADER_PID_FAMILY_1030C0_SCSI = 0x0004,
+ MPI_FW_HEADER_PID_FAMILY_1020A0_SCSI = 0x0005,
+ MPI_FW_HEADER_PID_FAMILY_1020B0_SCSI = 0x0006,
+ MPI_FW_HEADER_PID_FAMILY_1020B1_SCSI = 0x0007,
+ MPI_FW_HEADER_PID_FAMILY_1020C0_SCSI = 0x0008,
+ MPI_FW_HEADER_PID_FAMILY_1035A0_SCSI = 0x0009,
+ MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI = 0x000A,
+ MPI_FW_HEADER_PID_FAMILY_1030TA0_SCSI = 0x000B,
+ MPI_FW_HEADER_PID_FAMILY_1020TA0_SCSI = 0x000C,
+
+ /* Fibre Channel */
+ MPI_FW_HEADER_PID_FAMILY_909_FC = 0x0000,
+ MPI_FW_HEADER_PID_FAMILY_919_FC = 0x0001, /* 919 and 929 */
+ MPI_FW_HEADER_PID_FAMILY_919X_FC = 0x0002, /* 919X and 929X */
+ MPI_FW_HEADER_PID_FAMILY_919XL_FC = 0x0003, /* 919XL and 929XL */
+ MPI_FW_HEADER_PID_FAMILY_939X_FC = 0x0004, /* 939X and 949X */
+ MPI_FW_HEADER_PID_FAMILY_959_FC = 0x0005,
+ MPI_FW_HEADER_PID_FAMILY_949E_FC = 0x0006,
+
+ /* SAS */
+ MPI_FW_HEADER_PID_FAMILY_1064_SAS = 0x0001,
+ MPI_FW_HEADER_PID_FAMILY_1068_SAS = 0x0002,
+ MPI_FW_HEADER_PID_FAMILY_1078_SAS = 0x0003,
+ MPI_FW_HEADER_PID_FAMILY_106xE_SAS = 0x0004, /* 1068E, 1066E, and 1064E */
+};
+
+#endif
diff --git a/qemu/hw/scsi/mptconfig.c b/qemu/hw/scsi/mptconfig.c
new file mode 100644
index 000000000..707185469
--- /dev/null
+++ b/qemu/hw/scsi/mptconfig.c
@@ -0,0 +1,905 @@
+/*
+ * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/scsi/scsi.h"
+
+#include "mptsas.h"
+#include "mpi.h"
+#include "trace.h"
+
+/* Generic functions for marshaling and unmarshaling. */
+
+#define repl1(x) x
+#define repl2(x) x x
+#define repl3(x) x x x
+#define repl4(x) x x x x
+#define repl5(x) x x x x x
+#define repl6(x) x x x x x x
+#define repl7(x) x x x x x x x
+#define repl8(x) x x x x x x x x
+
+#define repl(n, x) glue(repl, n)(x)
+
+typedef union PackValue {
+ uint64_t ll;
+ char *str;
+} PackValue;
+
+static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap)
+{
+ size_t ofs;
+ PackValue val;
+ const char *p;
+
+ ofs = 0;
+ p = fmt;
+ while (*p) {
+ memset(&val, 0, sizeof(val));
+ switch (*p) {
+ case '*':
+ p++;
+ break;
+ case 'b':
+ case 'w':
+ case 'l':
+ val.ll = va_arg(ap, int);
+ break;
+ case 'q':
+ val.ll = va_arg(ap, int64_t);
+ break;
+ case 's':
+ val.str = va_arg(ap, void *);
+ break;
+ }
+ switch (*p++) {
+ case 'b':
+ if (data) {
+ stb_p(data + ofs, val.ll);
+ }
+ ofs++;
+ break;
+ case 'w':
+ if (data) {
+ stw_le_p(data + ofs, val.ll);
+ }
+ ofs += 2;
+ break;
+ case 'l':
+ if (data) {
+ stl_le_p(data + ofs, val.ll);
+ }
+ ofs += 4;
+ break;
+ case 'q':
+ if (data) {
+ stq_le_p(data + ofs, val.ll);
+ }
+ ofs += 8;
+ break;
+ case 's':
+ {
+ int cnt = atoi(p);
+ if (data) {
+ if (val.str) {
+ strncpy((void *)data + ofs, val.str, cnt);
+ } else {
+ memset((void *)data + ofs, 0, cnt);
+ }
+ }
+ ofs += cnt;
+ break;
+ }
+ }
+ }
+
+ return ofs;
+}
+
+static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1)
+{
+ size_t size = 0;
+ uint8_t *data = NULL;
+
+ if (p_data) {
+ va_list ap2;
+
+ va_copy(ap2, ap1);
+ size = vfill(NULL, 0, fmt, ap2);
+ *p_data = data = g_malloc(size);
+ va_end(ap2);
+ }
+ return vfill(data, size, fmt, ap1);
+}
+
+static size_t fill(uint8_t *data, size_t size, const char *fmt, ...)
+{
+ va_list ap;
+ size_t ret;
+
+ va_start(ap, fmt);
+ ret = vfill(data, size, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/* Functions to build the page header and fill in the length, always used
+ * through the macros.
+ */
+
+#define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...) \
+ mptsas_config_pack(data, "b*bbb" fmt, version, number, type, \
+ ## __VA_ARGS__)
+
+static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...)
+{
+ va_list ap;
+ size_t ret;
+
+ va_start(ap, fmt);
+ ret = vpack(data, fmt, ap);
+ va_end(ap);
+
+ if (data) {
+ assert(ret < 256 && (ret % 4) == 0);
+ stb_p(*data + 1, ret / 4);
+ }
+ return ret;
+}
+
+#define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...) \
+ mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number, \
+ MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__)
+
+static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...)
+{
+ va_list ap;
+ size_t ret;
+
+ va_start(ap, fmt);
+ ret = vpack(data, fmt, ap);
+ va_end(ap);
+
+ if (data) {
+ assert(ret < 65536 && (ret % 4) == 0);
+ stw_le_p(*data + 4, ret / 4);
+ }
+ return ret;
+}
+
+/* Manufacturing pages */
+
+static
+size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "s16s8s16s16s16",
+ "QEMU MPT Fusion",
+ "2.5",
+ "QEMU MPT Fusion",
+ "QEMU",
+ "0000111122223333");
+}
+
+static
+size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address)
+{
+ /* VPD - all zeros */
+ return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "s256");
+}
+
+static
+size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
+ return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "wb*b*l",
+ pcic->device_id, pcic->revision);
+}
+
+static
+size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
+ return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "wb*b*l",
+ pcic->device_id, pcic->revision);
+}
+
+static
+size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address)
+{
+ /* All zeros */
+ return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05,
+ "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l"
+ "*b*b*w*b*b*w*l*l");
+}
+
+static
+size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02,
+ "q*b*b*w*l*l", s->sas_addr);
+}
+
+static
+size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+static
+size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l*l*l*s16*b*b*w", MPTSAS_NUM_PORTS);
+}
+
+static
+size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+static
+size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+static
+size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00,
+ "*l");
+}
+
+/* I/O unit pages */
+
+static
+size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+ uint64_t unique_value = 0x53504D554D4551LL; /* "QEMUMPTx" */
+
+ unique_value |= (uint64_t)pci->devfn << 56;
+ return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00,
+ "q", unique_value);
+}
+
+static
+size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l",
+ 0x41 /* single function, RAID disabled */ );
+}
+
+static
+size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+ uint8_t devfn = pci->devfn;
+ return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02,
+ "llbbw*b*b*w*b*b*w*b*b*w*l",
+ 0, 0x100, 0 /* pci bus? */, devfn, 0);
+}
+
+static
+size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01,
+ "*b*b*w*l");
+}
+
+static
+size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q");
+}
+
+/* I/O controller pages */
+
+static
+size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address)
+{
+ PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s);
+
+ return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01,
+ "*l*lwwb*b*b*blww",
+ pcic->vendor_id, pcic->device_id, pcic->revision,
+ pcic->subsystem_vendor_id,
+ pcic->subsystem_id);
+}
+
+static
+size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03,
+ "*l*l*b*b*b*b");
+}
+
+static
+size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04,
+ "*l*b*b*b*b");
+}
+
+static
+size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00,
+ "*b*b*w");
+}
+
+static
+size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00,
+ "*b*b*w");
+}
+
+static
+size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00,
+ "*l*b*b*w");
+}
+
+static
+size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01,
+ "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w"
+ "*w*w*w*w*l*l*l");
+}
+
+/* SAS I/O unit pages (extended) */
+
+#define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16
+
+#define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02
+#define MPI_SAS_IOUNIT0_RATE_1_5 0x08
+#define MPI_SAS_IOUNIT0_RATE_3_0 0x09
+
+#define MPI_SAS_DEVICE_INFO_NO_DEVICE 0x00000000
+#define MPI_SAS_DEVICE_INFO_END_DEVICE 0x00000001
+#define MPI_SAS_DEVICE_INFO_SSP_TARGET 0x00000400
+
+#define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS 0x00
+
+#define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT 0x0001
+#define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED 0x0002
+#define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT 0x0004
+
+
+
+static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i,
+ int *phy_handle, int *dev_handle)
+{
+ SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0);
+
+ if (phy_handle) {
+ *phy_handle = i + 1;
+ }
+ if (dev_handle) {
+ *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0;
+ }
+ return d;
+}
+
+static
+size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address)
+{
+ size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04,
+ "*w*wb*b*w"
+ repl(MPTSAS_NUM_PORTS, "*s16"),
+ MPTSAS_NUM_PORTS);
+
+ if (data) {
+ size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE;
+ int i;
+
+ for (i = 0; i < MPTSAS_NUM_PORTS; i++) {
+ int phy_handle, dev_handle;
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE,
+ "bbbblwwl", i, 0, 0,
+ (dev
+ ? MPI_SAS_IOUNIT0_RATE_3_0
+ : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION),
+ (dev
+ ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET
+ : MPI_SAS_DEVICE_INFO_NO_DEVICE),
+ dev_handle,
+ dev_handle,
+ 0);
+ ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE;
+ }
+ assert(ofs == size);
+ }
+ return size;
+}
+
+#define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12
+
+static
+size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address)
+{
+ size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07,
+ "*w*w*w*wb*b*b*b"
+ repl(MPTSAS_NUM_PORTS, "*s12"),
+ MPTSAS_NUM_PORTS);
+
+ if (data) {
+ size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE;
+ int i;
+
+ for (i = 0; i < MPTSAS_NUM_PORTS; i++) {
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL);
+ fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE,
+ "bbbblww", i, 0, 0,
+ (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
+ (dev
+ ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET
+ : MPI_SAS_DEVICE_INFO_NO_DEVICE),
+ 0, 0);
+ ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE;
+ }
+ assert(ofs == size);
+ }
+ return size;
+}
+
+static
+size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06,
+ "*b*b*w*w*w*b*b*w");
+}
+
+static
+size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address)
+{
+ return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06,
+ "*l*l*l*l*l*l*l*l*l");
+}
+
+/* SAS PHY pages (extended) */
+
+static int mptsas_phy_addr_get(MPTSASState *s, int address)
+{
+ int i;
+ if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) {
+ i = address & 255;
+ } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) {
+ i = address & 65535;
+ } else {
+ return -EINVAL;
+ }
+
+ if (i >= MPTSAS_NUM_PORTS) {
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+static
+size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_phy_addr_get(s, address);
+ SCSIDevice *dev;
+
+ if (i < 0) {
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0);
+ return i;
+ }
+
+ dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0);
+
+ return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
+ "w*wqwb*blbb*b*b*l",
+ dev_handle, s->sas_addr, dev_handle, i,
+ (dev
+ ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */
+ : MPI_SAS_DEVICE_INFO_NO_DEVICE),
+ (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
+ (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5);
+}
+
+static
+size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_phy_addr_get(s, address);
+
+ if (i < 0) {
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1);
+ return i;
+ }
+
+ (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+ trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1);
+
+ return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
+ "*l*l*l*l*l");
+}
+
+/* SAS device pages (extended) */
+
+static int mptsas_device_addr_get(MPTSASState *s, int address)
+{
+ uint32_t handle, i;
+ uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT;
+ if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) {
+ handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK;
+ do {
+ if (handle == 65535) {
+ handle = MPTSAS_NUM_PORTS + 1;
+ } else {
+ ++handle;
+ }
+ i = handle - 1 - MPTSAS_NUM_PORTS;
+ } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0));
+
+ } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) {
+ if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) {
+ return -EINVAL;
+ }
+ i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK;
+
+ } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) {
+ handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK;
+ i = handle - 1 - MPTSAS_NUM_PORTS;
+
+ } else {
+ return -EINVAL;
+ }
+
+ if (i >= MPTSAS_NUM_PORTS) {
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+static
+size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_device_addr_get(s, address);
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0);
+ if (!dev) {
+ return -ENOENT;
+ }
+
+ return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05,
+ "*w*wqwbbwbblwb*b",
+ dev->wwn, phy_handle, i,
+ MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS,
+ dev_handle, i, 0,
+ MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET,
+ (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT |
+ MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED |
+ MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i);
+}
+
+static
+size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_device_addr_get(s, address);
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1);
+ if (!dev) {
+ return -ENOENT;
+ }
+
+ return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00,
+ "*lq*lwbb*s20",
+ dev->wwn, dev_handle, i, 0);
+}
+
+static
+size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address)
+{
+ int phy_handle = -1;
+ int dev_handle = -1;
+ int i = mptsas_device_addr_get(s, address);
+ SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
+
+ trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2);
+ if (!dev) {
+ return -ENOENT;
+ }
+
+ return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01,
+ "ql", dev->wwn, 0);
+}
+
+typedef struct MPTSASConfigPage {
+ uint8_t number;
+ uint8_t type;
+ size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address);
+} MPTSASConfigPage;
+
+static const MPTSASConfigPage mptsas_config_pages[] = {
+ {
+ 0, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_0,
+ }, {
+ 1, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_1,
+ }, {
+ 2, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_2,
+ }, {
+ 3, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_3,
+ }, {
+ 4, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_4,
+ }, {
+ 5, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_5,
+ }, {
+ 6, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_6,
+ }, {
+ 7, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_7,
+ }, {
+ 8, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_8,
+ }, {
+ 9, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_9,
+ }, {
+ 10, MPI_CONFIG_PAGETYPE_MANUFACTURING,
+ mptsas_config_manufacturing_10,
+ }, {
+ 0, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_0,
+ }, {
+ 1, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_1,
+ }, {
+ 2, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_2,
+ }, {
+ 3, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_3,
+ }, {
+ 4, MPI_CONFIG_PAGETYPE_IO_UNIT,
+ mptsas_config_io_unit_4,
+ }, {
+ 0, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_0,
+ }, {
+ 1, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_1,
+ }, {
+ 2, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_2,
+ }, {
+ 3, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_3,
+ }, {
+ 4, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_4,
+ }, {
+ 5, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_5,
+ }, {
+ 6, MPI_CONFIG_PAGETYPE_IOC,
+ mptsas_config_ioc_6,
+ }, {
+ 0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_0,
+ }, {
+ 1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_1,
+ }, {
+ 2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_2,
+ }, {
+ 3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ mptsas_config_sas_io_unit_3,
+ }, {
+ 0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY,
+ mptsas_config_phy_0,
+ }, {
+ 1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY,
+ mptsas_config_phy_1,
+ }, {
+ 0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ mptsas_config_sas_device_0,
+ }, {
+ 1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ mptsas_config_sas_device_1,
+ }, {
+ 2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ mptsas_config_sas_device_2,
+ }
+};
+
+static const MPTSASConfigPage *mptsas_find_config_page(int type, int number)
+{
+ const MPTSASConfigPage *page;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) {
+ page = &mptsas_config_pages[i];
+ if (page->type == type && page->number == number) {
+ return page;
+ }
+ }
+
+ return NULL;
+}
+
+void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req)
+{
+ PCIDevice *pci = PCI_DEVICE(s);
+
+ MPIMsgConfigReply reply;
+ const MPTSASConfigPage *page;
+ size_t length;
+ uint8_t type;
+ uint8_t *data = NULL;
+ uint32_t flags_and_length;
+ uint32_t dmalen;
+ uint64_t pa;
+
+ mptsas_fix_config_endianness(req);
+
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ /* Copy common bits from the request into the reply. */
+ memset(&reply, 0, sizeof(reply));
+ reply.Action = req->Action;
+ reply.Function = req->Function;
+ reply.MsgContext = req->MsgContext;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.PageType = req->PageType;
+ reply.PageNumber = req->PageNumber;
+ reply.PageLength = req->PageLength;
+ reply.PageVersion = req->PageVersion;
+
+ type = req->PageType & MPI_CONFIG_PAGETYPE_MASK;
+ if (type == MPI_CONFIG_PAGETYPE_EXTENDED) {
+ type = req->ExtPageType;
+ if (type <= MPI_CONFIG_PAGETYPE_MASK) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE;
+ goto out;
+ }
+
+ reply.ExtPageType = req->ExtPageType;
+ }
+
+ page = mptsas_find_config_page(type, req->PageNumber);
+
+ switch(req->Action) {
+ case MPI_CONFIG_ACTION_PAGE_DEFAULT:
+ case MPI_CONFIG_ACTION_PAGE_HEADER:
+ case MPI_CONFIG_ACTION_PAGE_READ_NVRAM:
+ case MPI_CONFIG_ACTION_PAGE_READ_CURRENT:
+ case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT:
+ case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT:
+ case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM:
+ break;
+
+ default:
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION;
+ goto out;
+ }
+
+ if (!page) {
+ page = mptsas_find_config_page(type, 1);
+ if (page) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ } else {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE;
+ }
+ goto out;
+ }
+
+ if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT ||
+ req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) {
+ length = page->mpt_config_build(s, NULL, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ goto out;
+ } else {
+ goto done;
+ }
+ }
+
+ if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
+ req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
+ length = page->mpt_config_build(s, NULL, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ } else {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT;
+ }
+ goto out;
+ }
+
+ flags_and_length = req->PageBufferSGE.FlagsLength;
+ dmalen = flags_and_length & MPI_SGE_LENGTH_MASK;
+ if (dmalen == 0) {
+ length = page->mpt_config_build(s, NULL, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ goto out;
+ } else {
+ goto done;
+ }
+ }
+
+ if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ pa = req->PageBufferSGE.u.Address64;
+ } else {
+ pa = req->PageBufferSGE.u.Address32;
+ }
+
+ /* Only read actions left. */
+ length = page->mpt_config_build(s, &data, req->PageAddress);
+ if ((ssize_t)length < 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE;
+ goto out;
+ } else {
+ assert(data[2] == page->number);
+ pci_dma_write(pci, pa, data, MIN(length, dmalen));
+ goto done;
+ }
+
+ abort();
+
+done:
+ if (type > MPI_CONFIG_PAGETYPE_MASK) {
+ reply.ExtPageLength = length / 4;
+ reply.ExtPageType = req->ExtPageType;
+ } else {
+ reply.PageLength = length / 4;
+ }
+
+out:
+ mptsas_fix_config_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+ g_free(data);
+}
diff --git a/qemu/hw/scsi/mptendian.c b/qemu/hw/scsi/mptendian.c
new file mode 100644
index 000000000..b7fe2a2a3
--- /dev/null
+++ b/qemu/hw/scsi/mptendian.c
@@ -0,0 +1,204 @@
+/*
+ * QEMU LSI SAS1068 Host Bus Adapter emulation
+ * Endianness conversion for MPI data structures
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Authors: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+#include "sysemu/block-backend.h"
+#include "hw/pci/msi.h"
+#include "qemu/iov.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "trace.h"
+
+#include "mptsas.h"
+#include "mpi.h"
+
+static void mptsas_fix_sgentry_endianness(MPISGEntry *sge)
+{
+ le32_to_cpus(&sge->FlagsLength);
+ if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ le64_to_cpus(&sge->u.Address64);
+ } else {
+ le32_to_cpus(&sge->u.Address32);
+ }
+}
+
+static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge)
+{
+ if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ cpu_to_le64s(&sge->u.Address64);
+ } else {
+ cpu_to_le32s(&sge->u.Address32);
+ }
+ cpu_to_le32s(&sge->FlagsLength);
+}
+
+void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req)
+{
+ le32_to_cpus(&req->MsgContext);
+ le32_to_cpus(&req->Control);
+ le32_to_cpus(&req->DataLength);
+ le32_to_cpus(&req->SenseBufferLowAddr);
+}
+
+void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le32s(&reply->TransferCount);
+ cpu_to_le32s(&reply->SenseCount);
+ cpu_to_le32s(&reply->ResponseInfo);
+ cpu_to_le16s(&reply->TaskTag);
+}
+
+void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req)
+{
+ le32_to_cpus(&req->MsgContext);
+ le32_to_cpus(&req->TaskMsgContext);
+}
+
+void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le32s(&reply->TerminationCount);
+}
+
+void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req)
+{
+ le32_to_cpus(&req->MsgContext);
+ le16_to_cpus(&req->ReplyFrameSize);
+ le32_to_cpus(&req->HostMfaHighAddr);
+ le32_to_cpus(&req->SenseBufferHighAddr);
+ le32_to_cpus(&req->ReplyFifoHostSignalingAddr);
+ mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE);
+ le16_to_cpus(&req->MsgVersion);
+ le16_to_cpus(&req->HeaderVersion);
+}
+
+void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+}
+
+void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply)
+{
+ cpu_to_le16s(&reply->MsgVersion);
+ cpu_to_le16s(&reply->HeaderVersion);
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCExceptions);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le16s(&reply->ReplyQueueDepth);
+ cpu_to_le16s(&reply->RequestFrameSize);
+ cpu_to_le16s(&reply->ProductID);
+ cpu_to_le32s(&reply->CurrentHostMfaHighAddr);
+ cpu_to_le16s(&reply->GlobalCredits);
+ cpu_to_le32s(&reply->CurrentSenseBufferHighAddr);
+ cpu_to_le16s(&reply->CurReplyFrameSize);
+ cpu_to_le32s(&reply->FWImageSize);
+ cpu_to_le32s(&reply->IOCCapabilities);
+ cpu_to_le16s(&reply->HighPriorityQueueDepth);
+ mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE);
+ cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr);
+}
+
+void mptsas_fix_config_endianness(MPIMsgConfig *req)
+{
+ le16_to_cpus(&req->ExtPageLength);
+ le32_to_cpus(&req->MsgContext);
+ le32_to_cpus(&req->PageAddress);
+ mptsas_fix_sgentry_endianness(&req->PageBufferSGE);
+}
+
+void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply)
+{
+ cpu_to_le16s(&reply->ExtPageLength);
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+}
+
+void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le16s(&reply->MaxDevices);
+ cpu_to_le16s(&reply->PortSCSIID);
+ cpu_to_le16s(&reply->ProtocolFlags);
+ cpu_to_le16s(&reply->MaxPostedCmdBuffers);
+ cpu_to_le16s(&reply->MaxPersistentIDs);
+ cpu_to_le16s(&reply->MaxLanBuckets);
+}
+
+void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply)
+{
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+}
+
+void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req)
+{
+ le32_to_cpus(&req->MsgContext);
+}
+
+void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply)
+{
+ int length = reply->EventDataLength;
+ int i;
+
+ cpu_to_le16s(&reply->EventDataLength);
+ cpu_to_le32s(&reply->MsgContext);
+ cpu_to_le16s(&reply->IOCStatus);
+ cpu_to_le32s(&reply->IOCLogInfo);
+ cpu_to_le32s(&reply->Event);
+ cpu_to_le32s(&reply->EventContext);
+
+ /* Really depends on the event kind. This will do for now. */
+ for (i = 0; i < length; i++) {
+ cpu_to_le32s(&reply->Data[i]);
+ }
+}
+
diff --git a/qemu/hw/scsi/mptsas.c b/qemu/hw/scsi/mptsas.c
new file mode 100644
index 000000000..499c1465a
--- /dev/null
+++ b/qemu/hw/scsi/mptsas.c
@@ -0,0 +1,1442 @@
+/*
+ * QEMU LSI SAS1068 Host Bus Adapter emulation
+ * Based on the QEMU Megaraid emulator
+ *
+ * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs
+ * Copyright (c) 2012 Verizon, Inc.
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Authors: Don Slutz, Paolo Bonzini
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+#include "sysemu/block-backend.h"
+#include "hw/pci/msi.h"
+#include "qemu/iov.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "trace.h"
+
+#include "mptsas.h"
+#include "mpi.h"
+
+#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL
+#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400
+
+#define TYPE_MPTSAS1068 "mptsas1068"
+
+#define MPT_SAS(obj) \
+ OBJECT_CHECK(MPTSASState, (obj), TYPE_MPTSAS1068)
+
+#define MPTSAS1068_PRODUCT_ID \
+ (MPI_FW_HEADER_PID_FAMILY_1068_SAS | \
+ MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI | \
+ MPI_FW_HEADER_PID_TYPE_SAS)
+
+struct MPTSASRequest {
+ MPIMsgSCSIIORequest scsi_io;
+ SCSIRequest *sreq;
+ QEMUSGList qsg;
+ MPTSASState *dev;
+
+ QTAILQ_ENTRY(MPTSASRequest) next;
+};
+
+static void mptsas_update_interrupt(MPTSASState *s)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ uint32_t state = s->intr_status & ~(s->intr_mask | MPI_HIS_IOP_DOORBELL_STATUS);
+
+ if (s->msi_in_use && msi_enabled(pci)) {
+ if (state) {
+ trace_mptsas_irq_msi(s);
+ msi_notify(pci, 0);
+ }
+ }
+
+ trace_mptsas_irq_intx(s, !!state);
+ pci_set_irq(pci, !!state);
+}
+
+static void mptsas_set_fault(MPTSASState *s, uint32_t code)
+{
+ if ((s->state & MPI_IOC_STATE_FAULT) == 0) {
+ s->state = MPI_IOC_STATE_FAULT | code;
+ }
+}
+
+#define MPTSAS_FIFO_INVALID(s, name) \
+ ((s)->name##_head > ARRAY_SIZE((s)->name) || \
+ (s)->name##_tail > ARRAY_SIZE((s)->name))
+
+#define MPTSAS_FIFO_EMPTY(s, name) \
+ ((s)->name##_head == (s)->name##_tail)
+
+#define MPTSAS_FIFO_FULL(s, name) \
+ ((s)->name##_head == ((s)->name##_tail + 1) % ARRAY_SIZE((s)->name))
+
+#define MPTSAS_FIFO_GET(s, name) ({ \
+ uint32_t _val = (s)->name[(s)->name##_head++]; \
+ (s)->name##_head %= ARRAY_SIZE((s)->name); \
+ _val; \
+})
+
+#define MPTSAS_FIFO_PUT(s, name, val) do { \
+ (s)->name[(s)->name##_tail++] = (val); \
+ (s)->name##_tail %= ARRAY_SIZE((s)->name); \
+} while(0)
+
+static void mptsas_post_reply(MPTSASState *s, MPIDefaultReply *reply)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ uint32_t addr_lo;
+
+ if (MPTSAS_FIFO_EMPTY(s, reply_free) || MPTSAS_FIFO_FULL(s, reply_post)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ addr_lo = MPTSAS_FIFO_GET(s, reply_free);
+
+ pci_dma_write(pci, addr_lo | s->host_mfa_high_addr, reply,
+ MIN(s->reply_frame_size, 4 * reply->MsgLength));
+
+ MPTSAS_FIFO_PUT(s, reply_post, MPI_ADDRESS_REPLY_A_BIT | (addr_lo >> 1));
+
+ s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT;
+ if (s->doorbell_state == DOORBELL_WRITE) {
+ s->doorbell_state = DOORBELL_NONE;
+ s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
+ }
+ mptsas_update_interrupt(s);
+}
+
+void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply)
+{
+ if (s->doorbell_state == DOORBELL_WRITE) {
+ /* The reply is sent out in 16 bit chunks, while the size
+ * in the reply is in 32 bit units.
+ */
+ s->doorbell_state = DOORBELL_READ;
+ s->doorbell_reply_idx = 0;
+ s->doorbell_reply_size = reply->MsgLength * 2;
+ memcpy(s->doorbell_reply, reply, s->doorbell_reply_size * 2);
+ s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
+ mptsas_update_interrupt(s);
+ } else {
+ mptsas_post_reply(s, reply);
+ }
+}
+
+static void mptsas_turbo_reply(MPTSASState *s, uint32_t msgctx)
+{
+ if (MPTSAS_FIFO_FULL(s, reply_post)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ /* The reply is just the message context ID (bit 31 = clear). */
+ MPTSAS_FIFO_PUT(s, reply_post, msgctx);
+
+ s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT;
+ mptsas_update_interrupt(s);
+}
+
+#define MPTSAS_MAX_REQUEST_SIZE 52
+
+static const int mpi_request_sizes[] = {
+ [MPI_FUNCTION_SCSI_IO_REQUEST] = sizeof(MPIMsgSCSIIORequest),
+ [MPI_FUNCTION_SCSI_TASK_MGMT] = sizeof(MPIMsgSCSITaskMgmt),
+ [MPI_FUNCTION_IOC_INIT] = sizeof(MPIMsgIOCInit),
+ [MPI_FUNCTION_IOC_FACTS] = sizeof(MPIMsgIOCFacts),
+ [MPI_FUNCTION_CONFIG] = sizeof(MPIMsgConfig),
+ [MPI_FUNCTION_PORT_FACTS] = sizeof(MPIMsgPortFacts),
+ [MPI_FUNCTION_PORT_ENABLE] = sizeof(MPIMsgPortEnable),
+ [MPI_FUNCTION_EVENT_NOTIFICATION] = sizeof(MPIMsgEventNotify),
+};
+
+static dma_addr_t mptsas_ld_sg_base(MPTSASState *s, uint32_t flags_and_length,
+ dma_addr_t *sgaddr)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ dma_addr_t addr;
+
+ if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
+ addr = ldq_le_pci_dma(pci, *sgaddr + 4);
+ *sgaddr += 12;
+ } else {
+ addr = ldl_le_pci_dma(pci, *sgaddr + 4);
+ *sgaddr += 8;
+ }
+ return addr;
+}
+
+static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ hwaddr next_chain_addr;
+ uint32_t left;
+ hwaddr sgaddr;
+ uint32_t chain_offset;
+
+ chain_offset = req->scsi_io.ChainOffset;
+ next_chain_addr = addr + chain_offset * sizeof(uint32_t);
+ sgaddr = addr + sizeof(MPIMsgSCSIIORequest);
+ pci_dma_sglist_init(&req->qsg, pci, 4);
+ left = req->scsi_io.DataLength;
+
+ for(;;) {
+ dma_addr_t addr, len;
+ uint32_t flags_and_length;
+
+ flags_and_length = ldl_le_pci_dma(pci, sgaddr);
+ len = flags_and_length & MPI_SGE_LENGTH_MASK;
+ if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK)
+ != MPI_SGE_FLAGS_SIMPLE_ELEMENT ||
+ (!len &&
+ !(flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) &&
+ !(flags_and_length & MPI_SGE_FLAGS_END_OF_BUFFER))) {
+ return MPI_IOCSTATUS_INVALID_SGL;
+ }
+
+ len = MIN(len, left);
+ if (!len) {
+ /* We reached the desired transfer length, ignore extra
+ * elements of the s/g list.
+ */
+ break;
+ }
+
+ addr = mptsas_ld_sg_base(s, flags_and_length, &sgaddr);
+ qemu_sglist_add(&req->qsg, addr, len);
+ left -= len;
+
+ if (flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) {
+ break;
+ }
+
+ if (flags_and_length & MPI_SGE_FLAGS_LAST_ELEMENT) {
+ if (!chain_offset) {
+ break;
+ }
+
+ flags_and_length = ldl_le_pci_dma(pci, next_chain_addr);
+ if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK)
+ != MPI_SGE_FLAGS_CHAIN_ELEMENT) {
+ return MPI_IOCSTATUS_INVALID_SGL;
+ }
+
+ sgaddr = mptsas_ld_sg_base(s, flags_and_length, &next_chain_addr);
+ chain_offset =
+ (flags_and_length & MPI_SGE_CHAIN_OFFSET_MASK) >> MPI_SGE_CHAIN_OFFSET_SHIFT;
+ next_chain_addr = sgaddr + chain_offset * sizeof(uint32_t);
+ }
+ }
+ return 0;
+}
+
+static void mptsas_free_request(MPTSASRequest *req)
+{
+ MPTSASState *s = req->dev;
+
+ if (req->sreq != NULL) {
+ req->sreq->hba_private = NULL;
+ scsi_req_unref(req->sreq);
+ req->sreq = NULL;
+ QTAILQ_REMOVE(&s->pending, req, next);
+ }
+ qemu_sglist_destroy(&req->qsg);
+ g_free(req);
+}
+
+static int mptsas_scsi_device_find(MPTSASState *s, int bus, int target,
+ uint8_t *lun, SCSIDevice **sdev)
+{
+ if (bus != 0) {
+ return MPI_IOCSTATUS_SCSI_INVALID_BUS;
+ }
+
+ if (target >= s->max_devices) {
+ return MPI_IOCSTATUS_SCSI_INVALID_TARGETID;
+ }
+
+ *sdev = scsi_device_find(&s->bus, bus, target, lun[1]);
+ if (!*sdev) {
+ return MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE;
+ }
+
+ return 0;
+}
+
+static int mptsas_process_scsi_io_request(MPTSASState *s,
+ MPIMsgSCSIIORequest *scsi_io,
+ hwaddr addr)
+{
+ MPTSASRequest *req;
+ MPIMsgSCSIIOReply reply;
+ SCSIDevice *sdev;
+ int status;
+
+ mptsas_fix_scsi_io_endianness(scsi_io);
+
+ trace_mptsas_process_scsi_io_request(s, scsi_io->Bus, scsi_io->TargetID,
+ scsi_io->LUN[1], scsi_io->DataLength);
+
+ status = mptsas_scsi_device_find(s, scsi_io->Bus, scsi_io->TargetID,
+ scsi_io->LUN, &sdev);
+ if (status) {
+ goto bad;
+ }
+
+ req = g_new(MPTSASRequest, 1);
+ QTAILQ_INSERT_TAIL(&s->pending, req, next);
+ req->scsi_io = *scsi_io;
+ req->dev = s;
+
+ status = mptsas_build_sgl(s, req, addr);
+ if (status) {
+ goto free_bad;
+ }
+
+ if (req->qsg.size < scsi_io->DataLength) {
+ trace_mptsas_sgl_overflow(s, scsi_io->MsgContext, scsi_io->DataLength,
+ req->qsg.size);
+ status = MPI_IOCSTATUS_INVALID_SGL;
+ goto free_bad;
+ }
+
+ req->sreq = scsi_req_new(sdev, scsi_io->MsgContext,
+ scsi_io->LUN[1], scsi_io->CDB, req);
+
+ if (req->sreq->cmd.xfer > scsi_io->DataLength) {
+ goto overrun;
+ }
+ switch (scsi_io->Control & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK) {
+ case MPI_SCSIIO_CONTROL_NODATATRANSFER:
+ if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
+ goto overrun;
+ }
+ break;
+
+ case MPI_SCSIIO_CONTROL_WRITE:
+ if (req->sreq->cmd.mode != SCSI_XFER_TO_DEV) {
+ goto overrun;
+ }
+ break;
+
+ case MPI_SCSIIO_CONTROL_READ:
+ if (req->sreq->cmd.mode != SCSI_XFER_FROM_DEV) {
+ goto overrun;
+ }
+ break;
+ }
+
+ if (scsi_req_enqueue(req->sreq)) {
+ scsi_req_continue(req->sreq);
+ }
+ return 0;
+
+overrun:
+ trace_mptsas_scsi_overflow(s, scsi_io->MsgContext, req->sreq->cmd.xfer,
+ scsi_io->DataLength);
+ status = MPI_IOCSTATUS_SCSI_DATA_OVERRUN;
+free_bad:
+ mptsas_free_request(req);
+bad:
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = scsi_io->TargetID;
+ reply.Bus = scsi_io->Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = scsi_io->Function;
+ reply.CDBLength = scsi_io->CDBLength;
+ reply.SenseBufferLength = scsi_io->SenseBufferLength;
+ reply.MsgContext = scsi_io->MsgContext;
+ reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS;
+ reply.IOCStatus = status;
+
+ mptsas_fix_scsi_io_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+
+ return 0;
+}
+
+typedef struct {
+ Notifier notifier;
+ MPTSASState *s;
+ MPIMsgSCSITaskMgmtReply *reply;
+} MPTSASCancelNotifier;
+
+static void mptsas_cancel_notify(Notifier *notifier, void *data)
+{
+ MPTSASCancelNotifier *n = container_of(notifier,
+ MPTSASCancelNotifier,
+ notifier);
+
+ /* Abusing IOCLogInfo to store the expected number of requests... */
+ if (++n->reply->TerminationCount == n->reply->IOCLogInfo) {
+ n->reply->IOCLogInfo = 0;
+ mptsas_fix_scsi_task_mgmt_reply_endianness(n->reply);
+ mptsas_post_reply(n->s, (MPIDefaultReply *)n->reply);
+ g_free(n->reply);
+ }
+ g_free(n);
+}
+
+static void mptsas_process_scsi_task_mgmt(MPTSASState *s, MPIMsgSCSITaskMgmt *req)
+{
+ MPIMsgSCSITaskMgmtReply reply;
+ MPIMsgSCSITaskMgmtReply *reply_async;
+ int status, count;
+ SCSIDevice *sdev;
+ SCSIRequest *r, *next;
+ BusChild *kid;
+
+ mptsas_fix_scsi_task_mgmt_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = req->TargetID;
+ reply.Bus = req->Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.TaskType = req->TaskType;
+ reply.MsgContext = req->MsgContext;
+
+ switch (req->TaskType) {
+ case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
+ case MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK:
+ status = mptsas_scsi_device_find(s, req->Bus, req->TargetID,
+ req->LUN, &sdev);
+ if (status) {
+ reply.IOCStatus = status;
+ goto out;
+ }
+ if (sdev->lun != req->LUN[1]) {
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN;
+ goto out;
+ }
+
+ QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) {
+ MPTSASRequest *cmd_req = r->hba_private;
+ if (cmd_req && cmd_req->scsi_io.MsgContext == req->TaskMsgContext) {
+ break;
+ }
+ }
+ if (r) {
+ /*
+ * Assert that the request has not been completed yet, we
+ * check for it in the loop above.
+ */
+ assert(r->hba_private);
+ if (req->TaskType == MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
+ /* "If the specified command is present in the task set, then
+ * return a service response set to FUNCTION SUCCEEDED".
+ */
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED;
+ } else {
+ MPTSASCancelNotifier *notifier;
+
+ reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply));
+ reply_async->IOCLogInfo = INT_MAX;
+
+ count = 1;
+ notifier = g_new(MPTSASCancelNotifier, 1);
+ notifier->s = s;
+ notifier->reply = reply_async;
+ notifier->notifier.notify = mptsas_cancel_notify;
+ scsi_req_cancel_async(r, &notifier->notifier);
+ goto reply_maybe_async;
+ }
+ }
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET:
+ case MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET:
+ status = mptsas_scsi_device_find(s, req->Bus, req->TargetID,
+ req->LUN, &sdev);
+ if (status) {
+ reply.IOCStatus = status;
+ goto out;
+ }
+ if (sdev->lun != req->LUN[1]) {
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN;
+ goto out;
+ }
+
+ reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply));
+ reply_async->IOCLogInfo = INT_MAX;
+
+ count = 0;
+ QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) {
+ if (r->hba_private) {
+ MPTSASCancelNotifier *notifier;
+
+ count++;
+ notifier = g_new(MPTSASCancelNotifier, 1);
+ notifier->s = s;
+ notifier->reply = reply_async;
+ notifier->notifier.notify = mptsas_cancel_notify;
+ scsi_req_cancel_async(r, &notifier->notifier);
+ }
+ }
+
+reply_maybe_async:
+ if (reply_async->TerminationCount < count) {
+ reply_async->IOCLogInfo = count;
+ return;
+ }
+ g_free(reply_async);
+ reply.TerminationCount = count;
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
+ status = mptsas_scsi_device_find(s, req->Bus, req->TargetID,
+ req->LUN, &sdev);
+ if (status) {
+ reply.IOCStatus = status;
+ goto out;
+ }
+ if (sdev->lun != req->LUN[1]) {
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN;
+ goto out;
+ }
+ qdev_reset_all(&sdev->qdev);
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ if (req->Bus != 0) {
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_BUS;
+ goto out;
+ }
+ if (req->TargetID > s->max_devices) {
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_TARGETID;
+ goto out;
+ }
+
+ QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+ sdev = SCSI_DEVICE(kid->child);
+ if (sdev->channel == 0 && sdev->id == req->TargetID) {
+ qdev_reset_all(kid->child);
+ }
+ }
+ break;
+
+ case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS:
+ qbus_reset_all(&s->bus.qbus);
+ break;
+
+ default:
+ reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED;
+ break;
+ }
+
+out:
+ mptsas_fix_scsi_task_mgmt_reply_endianness(&reply);
+ mptsas_post_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_ioc_init(MPTSASState *s, MPIMsgIOCInit *req)
+{
+ MPIMsgIOCInitReply reply;
+
+ mptsas_fix_ioc_init_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ s->who_init = req->WhoInit;
+ s->reply_frame_size = req->ReplyFrameSize;
+ s->max_buses = req->MaxBuses;
+ s->max_devices = req->MaxDevices ? req->MaxDevices : 256;
+ s->host_mfa_high_addr = (hwaddr)req->HostMfaHighAddr << 32;
+ s->sense_buffer_high_addr = (hwaddr)req->SenseBufferHighAddr << 32;
+
+ if (s->state == MPI_IOC_STATE_READY) {
+ s->state = MPI_IOC_STATE_OPERATIONAL;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ reply.WhoInit = s->who_init;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.MaxDevices = s->max_devices;
+ reply.MaxBuses = s->max_buses;
+ reply.MsgContext = req->MsgContext;
+
+ mptsas_fix_ioc_init_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_ioc_facts(MPTSASState *s,
+ MPIMsgIOCFacts *req)
+{
+ MPIMsgIOCFactsReply reply;
+
+ mptsas_fix_ioc_facts_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.MsgVersion = 0x0105;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.MsgContext = req->MsgContext;
+ reply.MaxChainDepth = MPTSAS_MAXIMUM_CHAIN_DEPTH;
+ reply.WhoInit = s->who_init;
+ reply.BlockSize = MPTSAS_MAX_REQUEST_SIZE / sizeof(uint32_t);
+ reply.ReplyQueueDepth = ARRAY_SIZE(s->reply_post) - 1;
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->reply_post) != ARRAY_SIZE(s->reply_free));
+
+ reply.RequestFrameSize = 128;
+ reply.ProductID = MPTSAS1068_PRODUCT_ID;
+ reply.CurrentHostMfaHighAddr = s->host_mfa_high_addr >> 32;
+ reply.GlobalCredits = ARRAY_SIZE(s->request_post) - 1;
+ reply.NumberOfPorts = MPTSAS_NUM_PORTS;
+ reply.CurrentSenseBufferHighAddr = s->sense_buffer_high_addr >> 32;
+ reply.CurReplyFrameSize = s->reply_frame_size;
+ reply.MaxDevices = s->max_devices;
+ reply.MaxBuses = s->max_buses;
+ reply.FWVersionDev = 0;
+ reply.FWVersionUnit = 0x92;
+ reply.FWVersionMinor = 0x32;
+ reply.FWVersionMajor = 0x1;
+
+ mptsas_fix_ioc_facts_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_port_facts(MPTSASState *s,
+ MPIMsgPortFacts *req)
+{
+ MPIMsgPortFactsReply reply;
+
+ mptsas_fix_port_facts_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+ reply.PortNumber = req->PortNumber;
+ reply.MsgContext = req->MsgContext;
+
+ if (req->PortNumber < MPTSAS_NUM_PORTS) {
+ reply.PortType = MPI_PORTFACTS_PORTTYPE_SAS;
+ reply.MaxDevices = MPTSAS_NUM_PORTS;
+ reply.PortSCSIID = MPTSAS_NUM_PORTS;
+ reply.ProtocolFlags = MPI_PORTFACTS_PROTOCOL_LOGBUSADDR | MPI_PORTFACTS_PROTOCOL_INITIATOR;
+ }
+
+ mptsas_fix_port_facts_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_port_enable(MPTSASState *s,
+ MPIMsgPortEnable *req)
+{
+ MPIMsgPortEnableReply reply;
+
+ mptsas_fix_port_enable_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ memset(&reply, 0, sizeof(reply));
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.PortNumber = req->PortNumber;
+ reply.Function = req->Function;
+ reply.MsgContext = req->MsgContext;
+
+ mptsas_fix_port_enable_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_event_notification(MPTSASState *s,
+ MPIMsgEventNotify *req)
+{
+ MPIMsgEventNotifyReply reply;
+
+ mptsas_fix_event_notification_endianness(req);
+
+ QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req));
+ QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply));
+
+ /* Don't even bother storing whether event notification is enabled,
+ * since it is not accessible.
+ */
+
+ memset(&reply, 0, sizeof(reply));
+ reply.EventDataLength = sizeof(reply.Data) / 4;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->Function;
+
+ /* This is set because events are sent through the reply FIFOs. */
+ reply.MsgFlags = MPI_MSGFLAGS_CONTINUATION_REPLY;
+
+ reply.MsgContext = req->MsgContext;
+ reply.Event = MPI_EVENT_EVENT_CHANGE;
+ reply.Data[0] = !!req->Switch;
+
+ mptsas_fix_event_notification_reply_endianness(&reply);
+ mptsas_reply(s, (MPIDefaultReply *)&reply);
+}
+
+static void mptsas_process_message(MPTSASState *s, MPIRequestHeader *req)
+{
+ trace_mptsas_process_message(s, req->Function, req->MsgContext);
+ switch (req->Function) {
+ case MPI_FUNCTION_SCSI_TASK_MGMT:
+ mptsas_process_scsi_task_mgmt(s, (MPIMsgSCSITaskMgmt *)req);
+ break;
+
+ case MPI_FUNCTION_IOC_INIT:
+ mptsas_process_ioc_init(s, (MPIMsgIOCInit *)req);
+ break;
+
+ case MPI_FUNCTION_IOC_FACTS:
+ mptsas_process_ioc_facts(s, (MPIMsgIOCFacts *)req);
+ break;
+
+ case MPI_FUNCTION_PORT_FACTS:
+ mptsas_process_port_facts(s, (MPIMsgPortFacts *)req);
+ break;
+
+ case MPI_FUNCTION_PORT_ENABLE:
+ mptsas_process_port_enable(s, (MPIMsgPortEnable *)req);
+ break;
+
+ case MPI_FUNCTION_EVENT_NOTIFICATION:
+ mptsas_process_event_notification(s, (MPIMsgEventNotify *)req);
+ break;
+
+ case MPI_FUNCTION_CONFIG:
+ mptsas_process_config(s, (MPIMsgConfig *)req);
+ break;
+
+ default:
+ trace_mptsas_unhandled_cmd(s, req->Function, 0);
+ mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_FUNCTION);
+ break;
+ }
+}
+
+static void mptsas_fetch_request(MPTSASState *s)
+{
+ PCIDevice *pci = (PCIDevice *) s;
+ char req[MPTSAS_MAX_REQUEST_SIZE];
+ MPIRequestHeader *hdr = (MPIRequestHeader *)req;
+ hwaddr addr;
+ int size;
+
+ if (s->state != MPI_IOC_STATE_OPERATIONAL) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE);
+ return;
+ }
+
+ /* Read the message header from the guest first. */
+ addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post);
+ pci_dma_read(pci, addr, req, sizeof(hdr));
+
+ if (hdr->Function < ARRAY_SIZE(mpi_request_sizes) &&
+ mpi_request_sizes[hdr->Function]) {
+ /* Read the rest of the request based on the type. Do not
+ * reread everything, as that could cause a TOC/TOU mismatch
+ * and leak data from the QEMU stack.
+ */
+ size = mpi_request_sizes[hdr->Function];
+ assert(size <= MPTSAS_MAX_REQUEST_SIZE);
+ pci_dma_read(pci, addr + sizeof(hdr), &req[sizeof(hdr)],
+ size - sizeof(hdr));
+ }
+
+ if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
+ /* SCSI I/O requests are separate from mptsas_process_message
+ * because they cannot be sent through the doorbell yet.
+ */
+ mptsas_process_scsi_io_request(s, (MPIMsgSCSIIORequest *)req, addr);
+ } else {
+ mptsas_process_message(s, (MPIRequestHeader *)req);
+ }
+}
+
+static void mptsas_fetch_requests(void *opaque)
+{
+ MPTSASState *s = opaque;
+
+ while (!MPTSAS_FIFO_EMPTY(s, request_post)) {
+ mptsas_fetch_request(s);
+ }
+}
+
+static void mptsas_soft_reset(MPTSASState *s)
+{
+ uint32_t save_mask;
+
+ trace_mptsas_reset(s);
+
+ /* Temporarily disable interrupts */
+ save_mask = s->intr_mask;
+ s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM;
+ mptsas_update_interrupt(s);
+
+ qbus_reset_all(&s->bus.qbus);
+ s->intr_status = 0;
+ s->intr_mask = save_mask;
+
+ s->reply_free_tail = 0;
+ s->reply_free_head = 0;
+ s->reply_post_tail = 0;
+ s->reply_post_head = 0;
+ s->request_post_tail = 0;
+ s->request_post_head = 0;
+ qemu_bh_cancel(s->request_bh);
+
+ s->state = MPI_IOC_STATE_READY;
+}
+
+static uint32_t mptsas_doorbell_read(MPTSASState *s)
+{
+ uint32_t ret;
+
+ ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_MASK;
+ ret |= s->state;
+ switch (s->doorbell_state) {
+ case DOORBELL_NONE:
+ break;
+
+ case DOORBELL_WRITE:
+ ret |= MPI_DOORBELL_ACTIVE;
+ break;
+
+ case DOORBELL_READ:
+ /* Get rid of the IOC fault code. */
+ ret &= ~MPI_DOORBELL_DATA_MASK;
+
+ assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT);
+ assert(s->doorbell_reply_idx <= s->doorbell_reply_size);
+
+ ret |= MPI_DOORBELL_ACTIVE;
+ if (s->doorbell_reply_idx < s->doorbell_reply_size) {
+ /* For more information about this endian switch, see the
+ * commit message for commit 36b62ae ("fw_cfg: fix endianness in
+ * fw_cfg_data_mem_read() / _write()", 2015-01-16).
+ */
+ ret |= le16_to_cpu(s->doorbell_reply[s->doorbell_reply_idx++]);
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ return ret;
+}
+
+static void mptsas_doorbell_write(MPTSASState *s, uint32_t val)
+{
+ if (s->doorbell_state == DOORBELL_WRITE) {
+ if (s->doorbell_idx < s->doorbell_cnt) {
+ /* For more information about this endian switch, see the
+ * commit message for commit 36b62ae ("fw_cfg: fix endianness in
+ * fw_cfg_data_mem_read() / _write()", 2015-01-16).
+ */
+ s->doorbell_msg[s->doorbell_idx++] = cpu_to_le32(val);
+ if (s->doorbell_idx == s->doorbell_cnt) {
+ mptsas_process_message(s, (MPIRequestHeader *)s->doorbell_msg);
+ }
+ }
+ return;
+ }
+
+ switch ((val & MPI_DOORBELL_FUNCTION_MASK) >> MPI_DOORBELL_FUNCTION_SHIFT) {
+ case MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET:
+ mptsas_soft_reset(s);
+ break;
+ case MPI_FUNCTION_IO_UNIT_RESET:
+ break;
+ case MPI_FUNCTION_HANDSHAKE:
+ s->doorbell_state = DOORBELL_WRITE;
+ s->doorbell_idx = 0;
+ s->doorbell_cnt = (val & MPI_DOORBELL_ADD_DWORDS_MASK)
+ >> MPI_DOORBELL_ADD_DWORDS_SHIFT;
+ s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
+ mptsas_update_interrupt(s);
+ break;
+ default:
+ trace_mptsas_unhandled_doorbell_cmd(s, val);
+ break;
+ }
+}
+
+static void mptsas_write_sequence_write(MPTSASState *s, uint32_t val)
+{
+ /* If the diagnostic register is enabled, any write to this register
+ * will disable it. Otherwise, the guest has to do a magic five-write
+ * sequence.
+ */
+ if (s->diagnostic & MPI_DIAG_DRWE) {
+ goto disable;
+ }
+
+ switch (s->diagnostic_idx) {
+ case 0:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_1ST_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 1:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_2ND_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 2:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_3RD_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 3:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_4TH_KEY_VALUE) {
+ goto disable;
+ }
+ break;
+ case 4:
+ if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_5TH_KEY_VALUE) {
+ goto disable;
+ }
+ /* Prepare Spaceball One for departure, and change the
+ * combination on my luggage!
+ */
+ s->diagnostic |= MPI_DIAG_DRWE;
+ break;
+ }
+ s->diagnostic_idx++;
+ return;
+
+disable:
+ s->diagnostic &= ~MPI_DIAG_DRWE;
+ s->diagnostic_idx = 0;
+}
+
+static int mptsas_hard_reset(MPTSASState *s)
+{
+ mptsas_soft_reset(s);
+
+ s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM;
+
+ s->host_mfa_high_addr = 0;
+ s->sense_buffer_high_addr = 0;
+ s->reply_frame_size = 0;
+ s->max_devices = MPTSAS_NUM_PORTS;
+ s->max_buses = 1;
+
+ return 0;
+}
+
+static void mptsas_interrupt_status_write(MPTSASState *s)
+{
+ switch (s->doorbell_state) {
+ case DOORBELL_NONE:
+ case DOORBELL_WRITE:
+ s->intr_status &= ~MPI_HIS_DOORBELL_INTERRUPT;
+ break;
+
+ case DOORBELL_READ:
+ /* The reply can be read continuously, so leave the interrupt up. */
+ assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT);
+ if (s->doorbell_reply_idx == s->doorbell_reply_size) {
+ s->doorbell_state = DOORBELL_NONE;
+ }
+ break;
+
+ default:
+ abort();
+ }
+ mptsas_update_interrupt(s);
+}
+
+static uint32_t mptsas_reply_post_read(MPTSASState *s)
+{
+ uint32_t ret;
+
+ if (!MPTSAS_FIFO_EMPTY(s, reply_post)) {
+ ret = MPTSAS_FIFO_GET(s, reply_post);
+ } else {
+ ret = -1;
+ s->intr_status &= ~MPI_HIS_REPLY_MESSAGE_INTERRUPT;
+ mptsas_update_interrupt(s);
+ }
+
+ return ret;
+}
+
+static uint64_t mptsas_mmio_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MPTSASState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (addr & ~3) {
+ case MPI_DOORBELL_OFFSET:
+ ret = mptsas_doorbell_read(s);
+ break;
+
+ case MPI_DIAGNOSTIC_OFFSET:
+ ret = s->diagnostic;
+ break;
+
+ case MPI_HOST_INTERRUPT_STATUS_OFFSET:
+ ret = s->intr_status;
+ break;
+
+ case MPI_HOST_INTERRUPT_MASK_OFFSET:
+ ret = s->intr_mask;
+ break;
+
+ case MPI_REPLY_POST_FIFO_OFFSET:
+ ret = mptsas_reply_post_read(s);
+ break;
+
+ default:
+ trace_mptsas_mmio_unhandled_read(s, addr);
+ break;
+ }
+ trace_mptsas_mmio_read(s, addr, ret);
+ return ret;
+}
+
+static void mptsas_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ MPTSASState *s = opaque;
+
+ trace_mptsas_mmio_write(s, addr, val);
+ switch (addr) {
+ case MPI_DOORBELL_OFFSET:
+ mptsas_doorbell_write(s, val);
+ break;
+
+ case MPI_WRITE_SEQUENCE_OFFSET:
+ mptsas_write_sequence_write(s, val);
+ break;
+
+ case MPI_DIAGNOSTIC_OFFSET:
+ if (val & MPI_DIAG_RESET_ADAPTER) {
+ mptsas_hard_reset(s);
+ }
+ break;
+
+ case MPI_HOST_INTERRUPT_STATUS_OFFSET:
+ mptsas_interrupt_status_write(s);
+ break;
+
+ case MPI_HOST_INTERRUPT_MASK_OFFSET:
+ s->intr_mask = val & (MPI_HIM_RIM | MPI_HIM_DIM);
+ mptsas_update_interrupt(s);
+ break;
+
+ case MPI_REQUEST_POST_FIFO_OFFSET:
+ if (MPTSAS_FIFO_FULL(s, request_post)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ } else {
+ MPTSAS_FIFO_PUT(s, request_post, val & ~0x03);
+ qemu_bh_schedule(s->request_bh);
+ }
+ break;
+
+ case MPI_REPLY_FREE_FIFO_OFFSET:
+ if (MPTSAS_FIFO_FULL(s, reply_free)) {
+ mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ } else {
+ MPTSAS_FIFO_PUT(s, reply_free, val);
+ }
+ break;
+
+ default:
+ trace_mptsas_mmio_unhandled_write(s, addr, val);
+ break;
+ }
+}
+
+static const MemoryRegionOps mptsas_mmio_ops = {
+ .read = mptsas_mmio_read,
+ .write = mptsas_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static const MemoryRegionOps mptsas_port_ops = {
+ .read = mptsas_mmio_read,
+ .write = mptsas_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static uint64_t mptsas_diag_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MPTSASState *s = opaque;
+ trace_mptsas_diag_read(s, addr, 0);
+ return 0;
+}
+
+static void mptsas_diag_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ MPTSASState *s = opaque;
+ trace_mptsas_diag_write(s, addr, val);
+}
+
+static const MemoryRegionOps mptsas_diag_ops = {
+ .read = mptsas_diag_read,
+ .write = mptsas_diag_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static QEMUSGList *mptsas_get_sg_list(SCSIRequest *sreq)
+{
+ MPTSASRequest *req = sreq->hba_private;
+
+ return &req->qsg;
+}
+
+static void mptsas_command_complete(SCSIRequest *sreq,
+ uint32_t status, size_t resid)
+{
+ MPTSASRequest *req = sreq->hba_private;
+ MPTSASState *s = req->dev;
+ uint8_t sense_buf[SCSI_SENSE_BUF_SIZE];
+ uint8_t sense_len;
+
+ hwaddr sense_buffer_addr = req->dev->sense_buffer_high_addr |
+ req->scsi_io.SenseBufferLowAddr;
+
+ trace_mptsas_command_complete(s, req->scsi_io.MsgContext, status, resid);
+
+ sense_len = scsi_req_get_sense(sreq, sense_buf, SCSI_SENSE_BUF_SIZE);
+ if (sense_len > 0) {
+ pci_dma_write(PCI_DEVICE(s), sense_buffer_addr, sense_buf,
+ MIN(req->scsi_io.SenseBufferLength, sense_len));
+ }
+
+ if (sreq->status != GOOD || resid ||
+ req->dev->doorbell_state == DOORBELL_WRITE) {
+ MPIMsgSCSIIOReply reply;
+
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = req->scsi_io.TargetID;
+ reply.Bus = req->scsi_io.Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->scsi_io.Function;
+ reply.CDBLength = req->scsi_io.CDBLength;
+ reply.SenseBufferLength = req->scsi_io.SenseBufferLength;
+ reply.MsgFlags = req->scsi_io.MsgFlags;
+ reply.MsgContext = req->scsi_io.MsgContext;
+ reply.SCSIStatus = sreq->status;
+ if (sreq->status == GOOD) {
+ reply.TransferCount = req->scsi_io.DataLength - resid;
+ if (resid) {
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN;
+ }
+ } else {
+ reply.SCSIState = MPI_SCSI_STATE_AUTOSENSE_VALID;
+ reply.SenseCount = sense_len;
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN;
+ }
+
+ mptsas_fix_scsi_io_reply_endianness(&reply);
+ mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply);
+ } else {
+ mptsas_turbo_reply(req->dev, req->scsi_io.MsgContext);
+ }
+
+ mptsas_free_request(req);
+}
+
+static void mptsas_request_cancelled(SCSIRequest *sreq)
+{
+ MPTSASRequest *req = sreq->hba_private;
+ MPIMsgSCSIIOReply reply;
+
+ memset(&reply, 0, sizeof(reply));
+ reply.TargetID = req->scsi_io.TargetID;
+ reply.Bus = req->scsi_io.Bus;
+ reply.MsgLength = sizeof(reply) / 4;
+ reply.Function = req->scsi_io.Function;
+ reply.CDBLength = req->scsi_io.CDBLength;
+ reply.SenseBufferLength = req->scsi_io.SenseBufferLength;
+ reply.MsgFlags = req->scsi_io.MsgFlags;
+ reply.MsgContext = req->scsi_io.MsgContext;
+ reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS;
+ reply.IOCStatus = MPI_IOCSTATUS_SCSI_TASK_TERMINATED;
+
+ mptsas_fix_scsi_io_reply_endianness(&reply);
+ mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply);
+ mptsas_free_request(req);
+}
+
+static void mptsas_save_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ MPTSASRequest *req = sreq->hba_private;
+ int i;
+
+ qemu_put_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io));
+ qemu_put_be32(f, req->qsg.nsg);
+ for (i = 0; i < req->qsg.nsg; i++) {
+ qemu_put_be64(f, req->qsg.sg[i].base);
+ qemu_put_be64(f, req->qsg.sg[i].len);
+ }
+}
+
+static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq)
+{
+ SCSIBus *bus = sreq->bus;
+ MPTSASState *s = container_of(bus, MPTSASState, bus);
+ PCIDevice *pci = PCI_DEVICE(s);
+ MPTSASRequest *req;
+ int i, n;
+
+ req = g_new(MPTSASRequest, 1);
+ qemu_get_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io));
+
+ n = qemu_get_be32(f);
+ /* TODO: add a way for SCSIBusInfo's load_request to fail,
+ * and fail migration instead of asserting here.
+ * When we do, we might be able to re-enable NDEBUG below.
+ */
+#ifdef NDEBUG
+#error building with NDEBUG is not supported
+#endif
+ assert(n >= 0);
+
+ pci_dma_sglist_init(&req->qsg, pci, n);
+ for (i = 0; i < n; i++) {
+ uint64_t base = qemu_get_be64(f);
+ uint64_t len = qemu_get_be64(f);
+ qemu_sglist_add(&req->qsg, base, len);
+ }
+
+ scsi_req_ref(sreq);
+ req->sreq = sreq;
+ req->dev = s;
+
+ return req;
+}
+
+static const struct SCSIBusInfo mptsas_scsi_info = {
+ .tcq = true,
+ .max_target = MPTSAS_NUM_PORTS,
+ .max_lun = 1,
+
+ .get_sg_list = mptsas_get_sg_list,
+ .complete = mptsas_command_complete,
+ .cancel = mptsas_request_cancelled,
+ .save_request = mptsas_save_request,
+ .load_request = mptsas_load_request,
+};
+
+static void mptsas_scsi_init(PCIDevice *dev, Error **errp)
+{
+ DeviceState *d = DEVICE(dev);
+ MPTSASState *s = MPT_SAS(dev);
+
+ dev->config[PCI_LATENCY_TIMER] = 0;
+ dev->config[PCI_INTERRUPT_PIN] = 0x01;
+
+ memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s,
+ "mptsas-mmio", 0x4000);
+ memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s,
+ "mptsas-io", 256);
+ memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s,
+ "mptsas-diag", 0x10000);
+
+ if (s->msi_available &&
+ msi_init(dev, 0, 1, true, false) >= 0) {
+ s->msi_in_use = true;
+ }
+
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
+ pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io);
+ pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_32, &s->diag_io);
+
+ if (!s->sas_addr) {
+ s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) |
+ IEEE_COMPANY_LOCALLY_ASSIGNED) << 36;
+ s->sas_addr |= (pci_bus_num(dev->bus) << 16);
+ s->sas_addr |= (PCI_SLOT(dev->devfn) << 8);
+ s->sas_addr |= PCI_FUNC(dev->devfn);
+ }
+ s->max_devices = MPTSAS_NUM_PORTS;
+
+ s->request_bh = qemu_bh_new(mptsas_fetch_requests, s);
+
+ QTAILQ_INIT(&s->pending);
+
+ scsi_bus_new(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info, NULL);
+ if (!d->hotplugged) {
+ scsi_bus_legacy_handle_cmdline(&s->bus, errp);
+ }
+}
+
+static void mptsas_scsi_uninit(PCIDevice *dev)
+{
+ MPTSASState *s = MPT_SAS(dev);
+
+ qemu_bh_delete(s->request_bh);
+ if (s->msi_in_use) {
+ msi_uninit(dev);
+ }
+}
+
+static void mptsas_reset(DeviceState *dev)
+{
+ MPTSASState *s = MPT_SAS(dev);
+
+ mptsas_hard_reset(s);
+}
+
+static int mptsas_post_load(void *opaque, int version_id)
+{
+ MPTSASState *s = opaque;
+
+ if (s->doorbell_idx > s->doorbell_cnt ||
+ s->doorbell_cnt > ARRAY_SIZE(s->doorbell_msg) ||
+ s->doorbell_reply_idx > s->doorbell_reply_size ||
+ s->doorbell_reply_size > ARRAY_SIZE(s->doorbell_reply) ||
+ MPTSAS_FIFO_INVALID(s, request_post) ||
+ MPTSAS_FIFO_INVALID(s, reply_post) ||
+ MPTSAS_FIFO_INVALID(s, reply_free) ||
+ s->diagnostic_idx > 4) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_mptsas = {
+ .name = "mptsas",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = mptsas_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, MPTSASState),
+ VMSTATE_BOOL(msi_in_use, MPTSASState),
+
+ VMSTATE_UINT32(state, MPTSASState),
+ VMSTATE_UINT8(who_init, MPTSASState),
+ VMSTATE_UINT8(doorbell_state, MPTSASState),
+ VMSTATE_UINT32_ARRAY(doorbell_msg, MPTSASState, 256),
+ VMSTATE_INT32(doorbell_idx, MPTSASState),
+ VMSTATE_INT32(doorbell_cnt, MPTSASState),
+
+ VMSTATE_UINT16_ARRAY(doorbell_reply, MPTSASState, 256),
+ VMSTATE_INT32(doorbell_reply_idx, MPTSASState),
+ VMSTATE_INT32(doorbell_reply_size, MPTSASState),
+
+ VMSTATE_UINT32(diagnostic, MPTSASState),
+ VMSTATE_UINT8(diagnostic_idx, MPTSASState),
+
+ VMSTATE_UINT32(intr_status, MPTSASState),
+ VMSTATE_UINT32(intr_mask, MPTSASState),
+
+ VMSTATE_UINT32_ARRAY(request_post, MPTSASState,
+ MPTSAS_REQUEST_QUEUE_DEPTH + 1),
+ VMSTATE_UINT16(request_post_head, MPTSASState),
+ VMSTATE_UINT16(request_post_tail, MPTSASState),
+
+ VMSTATE_UINT32_ARRAY(reply_post, MPTSASState,
+ MPTSAS_REPLY_QUEUE_DEPTH + 1),
+ VMSTATE_UINT16(reply_post_head, MPTSASState),
+ VMSTATE_UINT16(reply_post_tail, MPTSASState),
+
+ VMSTATE_UINT32_ARRAY(reply_free, MPTSASState,
+ MPTSAS_REPLY_QUEUE_DEPTH + 1),
+ VMSTATE_UINT16(reply_free_head, MPTSASState),
+ VMSTATE_UINT16(reply_free_tail, MPTSASState),
+
+ VMSTATE_UINT16(max_buses, MPTSASState),
+ VMSTATE_UINT16(max_devices, MPTSASState),
+ VMSTATE_UINT16(reply_frame_size, MPTSASState),
+ VMSTATE_UINT64(host_mfa_high_addr, MPTSASState),
+ VMSTATE_UINT64(sense_buffer_high_addr, MPTSASState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mptsas_properties[] = {
+ DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0),
+ /* TODO: test MSI support under Windows */
+ DEFINE_PROP_BIT("msi", MPTSASState, msi_available, 0, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mptsas1068_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+ pc->realize = mptsas_scsi_init;
+ pc->exit = mptsas_scsi_uninit;
+ pc->romfile = 0;
+ pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+ pc->device_id = PCI_DEVICE_ID_LSI_SAS1068;
+ pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+ pc->subsystem_id = 0x8000;
+ pc->class_id = PCI_CLASS_STORAGE_SCSI;
+ dc->props = mptsas_properties;
+ dc->reset = mptsas_reset;
+ dc->vmsd = &vmstate_mptsas;
+ dc->desc = "LSI SAS 1068";
+}
+
+static const TypeInfo mptsas_info = {
+ .name = TYPE_MPTSAS1068,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(MPTSASState),
+ .class_init = mptsas1068_class_init,
+};
+
+static void mptsas_register_types(void)
+{
+ type_register(&mptsas_info);
+}
+
+type_init(mptsas_register_types)
diff --git a/qemu/hw/scsi/mptsas.h b/qemu/hw/scsi/mptsas.h
new file mode 100644
index 000000000..595f81fb5
--- /dev/null
+++ b/qemu/hw/scsi/mptsas.h
@@ -0,0 +1,100 @@
+#ifndef MPTSAS_H
+#define MPTSAS_H
+
+#include "mpi.h"
+
+#define MPTSAS_NUM_PORTS 8
+#define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */
+
+#define MPTSAS_REQUEST_QUEUE_DEPTH 128
+#define MPTSAS_REPLY_QUEUE_DEPTH 128
+
+#define MPTSAS_MAXIMUM_CHAIN_DEPTH 0x22
+
+typedef struct MPTSASState MPTSASState;
+typedef struct MPTSASRequest MPTSASRequest;
+
+enum {
+ DOORBELL_NONE,
+ DOORBELL_WRITE,
+ DOORBELL_READ
+};
+
+struct MPTSASState {
+ PCIDevice dev;
+ MemoryRegion mmio_io;
+ MemoryRegion port_io;
+ MemoryRegion diag_io;
+ QEMUBH *request_bh;
+
+ uint32_t msi_available;
+ uint64_t sas_addr;
+
+ bool msi_in_use;
+
+ /* Doorbell register */
+ uint32_t state;
+ uint8_t who_init;
+ uint8_t doorbell_state;
+
+ /* Buffer for requests that are sent through the doorbell register. */
+ uint32_t doorbell_msg[256];
+ int doorbell_idx;
+ int doorbell_cnt;
+
+ uint16_t doorbell_reply[256];
+ int doorbell_reply_idx;
+ int doorbell_reply_size;
+
+ /* Other registers */
+ uint8_t diagnostic_idx;
+ uint32_t diagnostic;
+ uint32_t intr_mask;
+ uint32_t intr_status;
+
+ /* Request queues */
+ uint32_t request_post[MPTSAS_REQUEST_QUEUE_DEPTH + 1];
+ uint16_t request_post_head;
+ uint16_t request_post_tail;
+
+ uint32_t reply_post[MPTSAS_REPLY_QUEUE_DEPTH + 1];
+ uint16_t reply_post_head;
+ uint16_t reply_post_tail;
+
+ uint32_t reply_free[MPTSAS_REPLY_QUEUE_DEPTH + 1];
+ uint16_t reply_free_head;
+ uint16_t reply_free_tail;
+
+ /* IOC Facts */
+ hwaddr host_mfa_high_addr;
+ hwaddr sense_buffer_high_addr;
+ uint16_t max_devices;
+ uint16_t max_buses;
+ uint16_t reply_frame_size;
+
+ SCSIBus bus;
+ QTAILQ_HEAD(, MPTSASRequest) pending;
+};
+
+void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req);
+void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply);
+void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req);
+void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply);
+void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req);
+void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply);
+void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req);
+void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply);
+void mptsas_fix_config_endianness(MPIMsgConfig *req);
+void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply);
+void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req);
+void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply);
+void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req);
+void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply);
+void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req);
+void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply);
+
+void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply);
+
+void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req);
+
+#endif /* MPTSAS_H */
diff --git a/qemu/hw/scsi/scsi-bus.c b/qemu/hw/scsi/scsi-bus.c
index f0ae4625f..ad6f398c3 100644
--- a/qemu/hw/scsi/scsi-bus.c
+++ b/qemu/hw/scsi/scsi-bus.c
@@ -1,4 +1,6 @@
+#include "qemu/osdep.h"
#include "hw/hw.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
@@ -7,6 +9,7 @@
#include "sysemu/blockdev.h"
#include "trace.h"
#include "sysemu/dma.h"
+#include "qemu/cutils.h"
static char *scsibus_get_dev_path(DeviceState *dev);
static char *scsibus_get_fw_dev_path(DeviceState *dev);
@@ -136,7 +139,8 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
return;
}
if (!s->bh) {
- s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+ AioContext *ctx = blk_get_aio_context(s->conf.blk);
+ s->bh = aio_bh_new(ctx, scsi_dma_restart_bh, s);
qemu_bh_schedule(s->bh);
}
}
@@ -452,7 +456,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */
memcpy(&r->buf[8], "QEMU ", 8);
memcpy(&r->buf[16], "QEMU TARGET ", 16);
- pstrcpy((char *) &r->buf[32], 4, qemu_get_version());
+ pstrcpy((char *) &r->buf[32], 4, qemu_hw_version());
}
return true;
}
@@ -557,7 +561,7 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
const int memset_off = offsetof(SCSIRequest, sense)
+ sizeof(req->sense);
- req = g_slice_alloc(reqops->size);
+ req = g_malloc(reqops->size);
memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off);
req->refcount = 1;
req->bus = bus;
@@ -987,7 +991,6 @@ static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
}
/* fall through */
case READ_10:
- case RECOVER_BUFFERED_DATA:
case READ_12:
case READ_16:
cmd->xfer *= dev->blocksize;
@@ -1621,7 +1624,7 @@ void scsi_req_unref(SCSIRequest *req)
}
object_unref(OBJECT(req->dev));
object_unref(OBJECT(qbus->parent));
- g_slice_free1(req->ops->size, req);
+ g_free(req);
}
}
@@ -1759,8 +1762,14 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier)
notifier_list_add(&req->cancel_notifiers, notifier);
}
if (req->io_canceled) {
+ /* A blk_aio_cancel_async is pending; when it finishes,
+ * scsi_req_cancel_complete will be called and will
+ * call the notifier we just added. Just wait for that.
+ */
+ assert(req->aiocb);
return;
}
+ /* Dropped in scsi_req_cancel_complete. */
scsi_req_ref(req);
scsi_req_dequeue(req);
req->io_canceled = true;
@@ -1777,6 +1786,8 @@ void scsi_req_cancel(SCSIRequest *req)
if (!req->enqueued) {
return;
}
+ assert(!req->io_canceled);
+ /* Dropped in scsi_req_cancel_complete. */
scsi_req_ref(req);
scsi_req_dequeue(req);
req->io_canceled = true;
@@ -1840,17 +1851,19 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
{
SCSIRequest *req;
+ aio_context_acquire(blk_get_aio_context(sdev->conf.blk));
while (!QTAILQ_EMPTY(&sdev->requests)) {
req = QTAILQ_FIRST(&sdev->requests);
- scsi_req_cancel(req);
+ scsi_req_cancel_async(req, NULL);
}
-
+ blk_drain(sdev->conf.blk);
+ aio_context_release(blk_get_aio_context(sdev->conf.blk));
scsi_device_set_ua(sdev, sense);
}
static char *scsibus_get_dev_path(DeviceState *dev)
{
- SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
+ SCSIDevice *d = SCSI_DEVICE(dev);
DeviceState *hba = dev->parent_bus->parent;
char *id;
char *path;
@@ -2023,7 +2036,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
static void scsi_dev_instance_init(Object *obj)
{
DeviceState *dev = DEVICE(obj);
- SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev);
+ SCSIDevice *s = SCSI_DEVICE(dev);
device_add_bootindex_property(obj, &s->conf.bootindex,
"bootindex", NULL,
diff --git a/qemu/hw/scsi/scsi-disk.c b/qemu/hw/scsi/scsi-disk.c
index 64f069473..c3ce54a20 100644
--- a/qemu/hw/scsi/scsi-disk.c
+++ b/qemu/hw/scsi/scsi-disk.c
@@ -28,7 +28,8 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do {} while(0)
#endif
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
@@ -37,6 +38,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "sysemu/blockdev.h"
#include "hw/block/block.h"
#include "sysemu/dma.h"
+#include "qemu/cutils.h"
#ifdef __linux
#include <scsi/sg.h>
@@ -76,8 +78,6 @@ struct SCSIDiskState
bool media_changed;
bool media_event;
bool eject_request;
- uint64_t wwn;
- uint64_t port_wwn;
uint16_t port_index;
uint64_t max_unmap_size;
uint64_t max_io_size;
@@ -90,7 +90,7 @@ struct SCSIDiskState
bool tray_locked;
};
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
static void scsi_free_request(SCSIRequest *req)
{
@@ -169,18 +169,18 @@ static void scsi_aio_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
scsi_req_complete(&r->req, GOOD);
done:
@@ -217,6 +217,8 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
@@ -235,22 +237,17 @@ done:
scsi_req_unref(&r->req);
}
-static void scsi_dma_complete_noio(void *opaque, int ret)
+static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ assert(r->req.aiocb == NULL);
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -271,9 +268,17 @@ done:
static void scsi_dma_complete(void *opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
assert(r->req.aiocb != NULL);
- scsi_dma_complete_noio(opaque, ret);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_dma_complete_noio(r, ret);
}
static void scsi_read_complete(void * opaque, int ret)
@@ -284,18 +289,18 @@ static void scsi_read_complete(void * opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
n = r->qiov.size / 512;
@@ -308,23 +313,20 @@ done:
}
/* Actually issue a read to the block device. */
-static void scsi_do_read(void *opaque, int ret)
+static void scsi_do_read(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
+ assert (r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -349,6 +351,22 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_do_read_cb(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ assert (r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_do_read(opaque, ret);
+}
+
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIRequest *req)
{
@@ -384,7 +402,7 @@ static void scsi_read_data(SCSIRequest *req)
if (first && scsi_is_cmd_fua(&r->req.cmd)) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
BLOCK_ACCT_FLUSH);
- r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read, r);
+ r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r);
} else {
scsi_do_read(r, 0);
}
@@ -397,14 +415,17 @@ static void scsi_read_data(SCSIRequest *req)
* scsi_handle_rw_error always manages its reference counts, independent
* of the return value.
*/
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
{
- bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
+ bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk,
is_read, error);
if (action == BLOCK_ERROR_ACTION_REPORT) {
+ if (acct_failed) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
switch (error) {
case ENOMEDIUM:
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
@@ -430,23 +451,19 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
return action != BLOCK_ERROR_ACTION_IGNORE;
}
-static void scsi_write_complete(void * opaque, int ret)
+static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
+ assert (r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -467,6 +484,22 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ assert (r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_write_complete_noio(r, ret);
+}
+
static void scsi_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
@@ -480,18 +513,18 @@ static void scsi_write_data(SCSIRequest *req)
scsi_req_ref(&r->req);
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
- scsi_write_complete(r, -EINVAL);
+ scsi_write_complete_noio(r, -EINVAL);
return;
}
if (!r->req.sg && !r->qiov.size) {
/* Called for the first time. Ask the driver to send us more data. */
r->started = true;
- scsi_write_complete(r, 0);
+ scsi_write_complete_noio(r, 0);
return;
}
if (s->tray_open) {
- scsi_write_complete(r, -ENOMEDIUM);
+ scsi_write_complete_noio(r, -ENOMEDIUM);
return;
}
@@ -500,7 +533,7 @@ static void scsi_write_data(SCSIRequest *req)
if (r->req.sg) {
scsi_dma_complete_noio(r, 0);
} else {
- scsi_write_complete(r, 0);
+ scsi_write_complete_noio(r, 0);
}
return;
}
@@ -599,21 +632,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memcpy(outbuf+buflen, str, id_len);
buflen += id_len;
- if (s->wwn) {
+ if (s->qdev.wwn) {
outbuf[buflen++] = 0x1; // Binary
outbuf[buflen++] = 0x3; // NAA
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = 8;
- stq_be_p(&outbuf[buflen], s->wwn);
+ stq_be_p(&outbuf[buflen], s->qdev.wwn);
buflen += 8;
}
- if (s->port_wwn) {
+ if (s->qdev.port_wwn) {
outbuf[buflen++] = 0x61; // SAS / Binary
outbuf[buflen++] = 0x93; // PIV / Target port / NAA
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = 8;
- stq_be_p(&outbuf[buflen], s->port_wwn);
+ stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
buflen += 8;
}
@@ -1557,22 +1590,24 @@ typedef struct UnmapCBData {
int count;
} UnmapCBData;
-static void scsi_unmap_complete(void *opaque, int ret)
+static void scsi_unmap_complete(void *opaque, int ret);
+
+static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
{
- UnmapCBData *data = opaque;
SCSIDiskReq *r = data->r;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint64_t sector_num;
uint32_t nb_sectors;
- r->req.aiocb = NULL;
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -1601,6 +1636,17 @@ done:
g_free(data);
}
+static void scsi_unmap_complete(void *opaque, int ret)
+{
+ UnmapCBData *data = opaque;
+ SCSIDiskReq *r = data->r;
+
+ assert(r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ scsi_unmap_complete_noio(data, ret);
+}
+
static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
@@ -1638,7 +1684,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
/* The matching unref is in scsi_unmap_complete, before data is freed. */
scsi_req_ref(&r->req);
- scsi_unmap_complete(data, 0);
+ scsi_unmap_complete_noio(data, 0);
return;
invalid_param_len:
@@ -1665,24 +1711,29 @@ static void scsi_write_same_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+
data->nb_sectors -= data->iov.iov_len / 512;
data->sector += data->iov.iov_len / 512;
data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len);
if (data->iov.iov_len) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
data->iov.iov_len, BLOCK_ACCT_WRITE);
+ /* blk_aio_write doesn't like the qiov size being different from
+ * nb_sectors, make sure they match.
+ */
+ qemu_iovec_init_external(&data->qiov, &data->iov, 1);
r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector,
&data->qiov, data->iov.iov_len / 512,
scsi_write_same_complete, data);
@@ -2280,7 +2331,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
}
if (!s->version) {
- s->version = g_strdup(qemu_get_version());
+ s->version = g_strdup(qemu_hw_version());
}
if (!s->vendor) {
s->vendor = g_strdup("QEMU");
@@ -2523,6 +2574,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS);
scsi_realize(&s->qdev, errp);
+ scsi_generic_read_device_identification(&s->qdev);
}
static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
@@ -2616,8 +2668,8 @@ static Property scsi_hd_properties[] = {
SCSI_DISK_F_REMOVABLE, false),
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
- DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0),
- DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0),
+ DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0),
+ DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0),
DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0),
DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
DEFAULT_MAX_UNMAP_SIZE),
@@ -2666,8 +2718,8 @@ static const TypeInfo scsi_hd_info = {
static Property scsi_cd_properties[] = {
DEFINE_SCSI_DISK_PROPERTIES(),
- DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0),
- DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0),
+ DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0),
+ DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0),
DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0),
DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size,
DEFAULT_MAX_IO_SIZE),
@@ -2731,8 +2783,8 @@ static Property scsi_disk_properties[] = {
SCSI_DISK_F_REMOVABLE, false),
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
- DEFINE_PROP_UINT64("wwn", SCSIDiskState, wwn, 0),
- DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, port_wwn, 0),
+ DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0),
+ DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0),
DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0),
DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
DEFAULT_MAX_UNMAP_SIZE),
diff --git a/qemu/hw/scsi/scsi-generic.c b/qemu/hw/scsi/scsi-generic.c
index e53470f85..7459465f6 100644
--- a/qemu/hw/scsi/scsi-generic.c
+++ b/qemu/hw/scsi/scsi-generic.c
@@ -11,6 +11,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
@@ -31,10 +33,6 @@ do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include <scsi/sg.h>
#include "block/scsi.h"
@@ -88,12 +86,12 @@ static void scsi_free_request(SCSIRequest *req)
}
/* Helper function for command completion. */
-static void scsi_command_complete(void *opaque, int ret)
+static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
{
int status;
- SCSIGenericReq *r = (SCSIGenericReq *)opaque;
- r->req.aiocb = NULL;
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
@@ -142,6 +140,15 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_command_complete(void *opaque, int ret)
+{
+ SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+
+ assert(r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+ scsi_command_complete_noio(r, ret);
+}
+
static int execute_command(BlockBackend *blk,
SCSIGenericReq *r, int direction,
BlockCompletionFunc *complete)
@@ -172,33 +179,51 @@ static void scsi_read_complete(void * opaque, int ret)
SCSIDevice *s = r->req.dev;
int len;
+ assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
+
if (ret || r->req.io_canceled) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return;
}
+
len = r->io_header.dxfer_len - r->io_header.resid;
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
r->len = -1;
if (len == 0) {
- scsi_command_complete(r, 0);
- } else {
- /* Snoop READ CAPACITY output to set the blocksize. */
- if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
- (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
- s->blocksize = ldl_be_p(&r->buf[4]);
- s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
- } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
- (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
- s->blocksize = ldl_be_p(&r->buf[8]);
- s->max_lba = ldq_be_p(&r->buf[0]);
- }
- blk_set_guest_block_size(s->conf.blk, s->blocksize);
+ scsi_command_complete_noio(r, 0);
+ return;
+ }
- scsi_req_data(&r->req, len);
- scsi_req_unref(&r->req);
+ /* Snoop READ CAPACITY output to set the blocksize. */
+ if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
+ (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
+ s->blocksize = ldl_be_p(&r->buf[4]);
+ s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
+ } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
+ (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
+ s->blocksize = ldl_be_p(&r->buf[8]);
+ s->max_lba = ldq_be_p(&r->buf[0]);
+ }
+ blk_set_guest_block_size(s->conf.blk, s->blocksize);
+
+ /* Patch MODE SENSE device specific parameters if the BDS is opened
+ * readonly.
+ */
+ if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) &&
+ blk_is_read_only(s->conf.blk) &&
+ (r->req.cmd.buf[0] == MODE_SENSE ||
+ r->req.cmd.buf[0] == MODE_SENSE_10) &&
+ (r->req.cmd.buf[1] & 0x8) == 0) {
+ if (r->req.cmd.buf[0] == MODE_SENSE) {
+ r->buf[2] |= 0x80;
+ } else {
+ r->buf[3] |= 0x80;
+ }
}
+ scsi_req_data(&r->req, len);
+ scsi_req_unref(&r->req);
}
/* Read more data from scsi device into buffer. */
@@ -213,14 +238,14 @@ static void scsi_read_data(SCSIRequest *req)
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
if (r->len == -1) {
- scsi_command_complete(r, 0);
+ scsi_command_complete_noio(r, 0);
return;
}
ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
scsi_read_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
}
@@ -230,9 +255,12 @@ static void scsi_write_complete(void * opaque, int ret)
SCSIDevice *s = r->req.dev;
DPRINTF("scsi_write_complete() ret = %d\n", ret);
+
+ assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
+
if (ret || r->req.io_canceled) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return;
}
@@ -242,7 +270,7 @@ static void scsi_write_complete(void * opaque, int ret)
DPRINTF("block size %d\n", s->blocksize);
}
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
/* Write data to a scsi device. Returns nonzero on failure.
@@ -264,7 +292,7 @@ static void scsi_write_data(SCSIRequest *req)
scsi_req_ref(&r->req);
ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
}
@@ -306,7 +334,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
scsi_command_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return 0;
}
return 0;
@@ -328,6 +356,96 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
}
}
+static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
+{
+ int i;
+
+ if ((p[1] & 0xF) == 3) {
+ /* NAA designator type */
+ if (p[3] != 8) {
+ return -EINVAL;
+ }
+ *p_wwn = ldq_be_p(p + 4);
+ return 0;
+ }
+
+ if ((p[1] & 0xF) == 8) {
+ /* SCSI name string designator type */
+ if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
+ return -EINVAL;
+ }
+ if (p[3] > 20 && p[24] != ',') {
+ return -EINVAL;
+ }
+ *p_wwn = 0;
+ for (i = 8; i < 24; i++) {
+ char c = toupper(p[i]);
+ c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
+ *p_wwn = (*p_wwn << 4) | c;
+ }
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+void scsi_generic_read_device_identification(SCSIDevice *s)
+{
+ uint8_t cmd[6];
+ uint8_t buf[250];
+ uint8_t sensebuf[8];
+ sg_io_hdr_t io_header;
+ int ret;
+ int i, len;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = INQUIRY;
+ cmd[1] = 1;
+ cmd[2] = 0x83;
+ cmd[4] = sizeof(buf);
+
+ memset(&io_header, 0, sizeof(io_header));
+ io_header.interface_id = 'S';
+ io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_header.dxfer_len = sizeof(buf);
+ io_header.dxferp = buf;
+ io_header.cmdp = cmd;
+ io_header.cmd_len = sizeof(cmd);
+ io_header.mx_sb_len = sizeof(sensebuf);
+ io_header.sbp = sensebuf;
+ io_header.timeout = 6000; /* XXX */
+
+ ret = blk_ioctl(s->conf.blk, SG_IO, &io_header);
+ if (ret < 0 || io_header.driver_status || io_header.host_status) {
+ return;
+ }
+
+ len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
+ for (i = 0; i + 3 <= len; ) {
+ const uint8_t *p = &buf[i + 4];
+ uint64_t wwn;
+
+ if (i + (p[3] + 4) > len) {
+ break;
+ }
+
+ if ((p[1] & 0x10) == 0) {
+ /* Associated with the logical unit */
+ if (read_naa_id(p, &wwn) == 0) {
+ s->wwn = wwn;
+ }
+ } else if ((p[1] & 0x10) == 0x10) {
+ /* Associated with the target port */
+ if (read_naa_id(p, &wwn) == 0) {
+ s->port_wwn = wwn;
+ }
+ }
+
+ i += p[3] + 4;
+ }
+}
+
static int get_stream_blocksize(BlockBackend *blk)
{
uint8_t cmd[6];
@@ -431,6 +549,8 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
}
DPRINTF("block size %d\n", s->blocksize);
+
+ scsi_generic_read_device_identification(s);
}
const SCSIReqOps scsi_generic_req_ops = {
diff --git a/qemu/hw/scsi/spapr_vscsi.c b/qemu/hw/scsi/spapr_vscsi.c
index 891424fae..b00edf7fd 100644
--- a/qemu/hw/scsi/spapr_vscsi.c
+++ b/qemu/hw/scsi/spapr_vscsi.c
@@ -31,6 +31,9 @@
* - Add indirect descriptors support
* - Maybe do autosense (PAPR seems to mandate it, linux doesn't care)
*/
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
#include "hw/hw.h"
#include "hw/scsi/scsi.h"
#include "block/scsi.h"
@@ -750,7 +753,6 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req)
len = n+8;
resp_data = g_malloc0(len);
- memset(resp_data, 0, len);
stl_be_p(resp_data, n);
i = found_lun0 ? 8 : 16;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
diff --git a/qemu/hw/scsi/vhost-scsi.c b/qemu/hw/scsi/vhost-scsi.c
index a69918bef..9261d51da 100644
--- a/qemu/hw/scsi/vhost-scsi.c
+++ b/qemu/hw/scsi/vhost-scsi.c
@@ -14,8 +14,9 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
-#include "config.h"
#include "qemu/error-report.h"
#include "qemu/queue.h"
#include "monitor/monitor.h"
@@ -26,6 +27,8 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
#include "hw/fw-path-provider.h"
+#include "linux/vhost.h"
+#include "qemu/cutils.h"
/* Features supported by host kernel. */
static const int kernel_feature_bits[] = {
@@ -45,7 +48,7 @@ static int vhost_scsi_set_endpoint(VHostSCSI *s)
memset(&backend, 0, sizeof(backend));
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
- ret = vhost_ops->vhost_call(&s->dev, VHOST_SCSI_SET_ENDPOINT, &backend);
+ ret = vhost_ops->vhost_scsi_set_endpoint(&s->dev, &backend);
if (ret < 0) {
return -errno;
}
@@ -60,7 +63,7 @@ static void vhost_scsi_clear_endpoint(VHostSCSI *s)
memset(&backend, 0, sizeof(backend));
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
- vhost_ops->vhost_call(&s->dev, VHOST_SCSI_CLEAR_ENDPOINT, &backend);
+ vhost_ops->vhost_scsi_clear_endpoint(&s->dev, &backend);
}
static int vhost_scsi_start(VHostSCSI *s)
@@ -76,8 +79,7 @@ static int vhost_scsi_start(VHostSCSI *s)
return -ENOSYS;
}
- ret = vhost_ops->vhost_call(&s->dev,
- VHOST_SCSI_GET_ABI_VERSION, &abi_version);
+ ret = vhost_ops->vhost_scsi_get_abi_version(&s->dev, &abi_version);
if (ret < 0) {
return -errno;
}
@@ -118,7 +120,7 @@ static int vhost_scsi_start(VHostSCSI *s)
* enabling/disabling irqfd.
*/
for (i = 0; i < s->dev.nvqs; i++) {
- vhost_virtqueue_mask(&s->dev, vdev, i, false);
+ vhost_virtqueue_mask(&s->dev, vdev, s->dev.vq_index + i, false);
}
return ret;
@@ -217,11 +219,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
}
if (vs->conf.vhostfd) {
- vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, &err);
+ vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp);
if (vhostfd == -1) {
- error_setg(errp, "vhost-scsi: unable to parse vhostfd: %s",
- error_get_pretty(err));
- error_free(err);
+ error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
return;
}
} else {
@@ -277,6 +277,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
/* This will stop vhost backend. */
vhost_scsi_set_status(vdev, 0);
+ vhost_dev_cleanup(&s->dev);
g_free(s->dev.vqs);
virtio_scsi_common_unrealize(dev, errp);
@@ -291,7 +292,7 @@ static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus,
{
VHostSCSI *s = VHOST_SCSI(dev);
/* format: channel@channel/vhost-scsi@target,lun */
- return g_strdup_printf("channel@%x/%s@%x,%x", s->channel,
+ return g_strdup_printf("/channel@%x/%s@%x,%x", s->channel,
qdev_fw_name(dev), s->target, s->lun);
}
diff --git a/qemu/hw/scsi/virtio-scsi-dataplane.c b/qemu/hw/scsi/virtio-scsi-dataplane.c
index 5575648a9..1a49f1e4b 100644
--- a/qemu/hw/scsi/virtio-scsi-dataplane.c
+++ b/qemu/hw/scsi/virtio-scsi-dataplane.c
@@ -11,6 +11,7 @@
*
*/
+#include "qemu/osdep.h"
#include "hw/virtio/virtio-scsi.h"
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
@@ -18,7 +19,6 @@
#include <block/scsi.h>
#include <hw/virtio/virtio-bus.h>
#include "hw/virtio/virtio-access.h"
-#include "stdio.h"
/* Context: QEMU global mutex held */
void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
@@ -38,120 +38,57 @@ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
}
}
-static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s,
- VirtQueue *vq,
- EventNotifierHandler *handler,
- int n)
+static void virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev,
+ VirtQueue *vq)
{
- BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
- VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
- VirtIOSCSIVring *r;
- int rc;
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
- /* Set up virtqueue notify */
- rc = k->set_host_notifier(qbus->parent, n, true);
- if (rc != 0) {
- fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
- rc);
- s->dataplane_fenced = true;
- return NULL;
- }
-
- r = g_slice_new(VirtIOSCSIVring);
- r->host_notifier = *virtio_queue_get_host_notifier(vq);
- r->guest_notifier = *virtio_queue_get_guest_notifier(vq);
- aio_set_event_notifier(s->ctx, &r->host_notifier, handler);
-
- r->parent = s;
-
- if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) {
- fprintf(stderr, "virtio-scsi: VRing setup failed\n");
- goto fail_vring;
- }
- return r;
-
-fail_vring:
- aio_set_event_notifier(s->ctx, &r->host_notifier, NULL);
- k->set_host_notifier(qbus->parent, n, false);
- g_slice_free(VirtIOSCSIVring, r);
- return NULL;
+ assert(s->ctx && s->dataplane_started);
+ virtio_scsi_handle_cmd_vq(s, vq);
}
-VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s,
- VirtIOSCSIVring *vring)
+static void virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev,
+ VirtQueue *vq)
{
- VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL);
- int r;
-
- req->vring = vring;
- r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem);
- if (r < 0) {
- virtio_scsi_free_req(req);
- req = NULL;
- }
- return req;
-}
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
-void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req)
-{
- VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent);
-
- vring_push(vdev, &req->vring->vring, &req->elem,
- req->qsgl.size + req->resp_iov.size);
-
- if (vring_should_notify(vdev, &req->vring->vring)) {
- event_notifier_set(&req->vring->guest_notifier);
- }
+ assert(s->ctx && s->dataplane_started);
+ virtio_scsi_handle_ctrl_vq(s, vq);
}
-static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier)
+static void virtio_scsi_data_plane_handle_event(VirtIODevice *vdev,
+ VirtQueue *vq)
{
- VirtIOSCSIVring *vring = container_of(notifier,
- VirtIOSCSIVring, host_notifier);
- VirtIOSCSI *s = VIRTIO_SCSI(vring->parent);
- VirtIOSCSIReq *req;
-
- event_notifier_test_and_clear(notifier);
- while ((req = virtio_scsi_pop_req_vring(s, vring))) {
- virtio_scsi_handle_ctrl_req(s, req);
- }
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+
+ assert(s->ctx && s->dataplane_started);
+ virtio_scsi_handle_event_vq(s, vq);
}
-static void virtio_scsi_iothread_handle_event(EventNotifier *notifier)
+static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n,
+ void (*fn)(VirtIODevice *vdev, VirtQueue *vq))
{
- VirtIOSCSIVring *vring = container_of(notifier,
- VirtIOSCSIVring, host_notifier);
- VirtIOSCSI *s = vring->parent;
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
-
- event_notifier_test_and_clear(notifier);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int rc;
- if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- return;
+ /* Set up virtqueue notify */
+ rc = k->set_host_notifier(qbus->parent, n, true);
+ if (rc != 0) {
+ fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
+ rc);
+ s->dataplane_fenced = true;
+ return rc;
}
- if (s->events_dropped) {
- virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
- }
+ virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn);
+ return 0;
}
-static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier)
+void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req)
{
- VirtIOSCSIVring *vring = container_of(notifier,
- VirtIOSCSIVring, host_notifier);
- VirtIOSCSI *s = (VirtIOSCSI *)vring->parent;
- VirtIOSCSIReq *req, *next;
- QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
-
- event_notifier_test_and_clear(notifier);
- while ((req = virtio_scsi_pop_req_vring(s, vring))) {
- if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
- QTAILQ_INSERT_TAIL(&reqs, req, next);
- }
- }
-
- QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
- virtio_scsi_handle_cmd_req_submit(s, req);
+ if (virtio_should_notify(vdev, req->vq)) {
+ event_notifier_set(virtio_queue_get_guest_notifier(req->vq));
}
}
@@ -161,43 +98,10 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s)
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
int i;
- if (s->ctrl_vring) {
- aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
- }
- if (s->event_vring) {
- aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
- }
- if (s->cmd_vrings) {
- for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) {
- aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
- }
- }
-}
-
-static void virtio_scsi_vring_teardown(VirtIOSCSI *s)
-{
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
- int i;
-
- if (s->ctrl_vring) {
- vring_teardown(&s->ctrl_vring->vring, vdev, 0);
- g_slice_free(VirtIOSCSIVring, s->ctrl_vring);
- s->ctrl_vring = NULL;
- }
- if (s->event_vring) {
- vring_teardown(&s->event_vring->vring, vdev, 1);
- g_slice_free(VirtIOSCSIVring, s->event_vring);
- s->event_vring = NULL;
- }
- if (s->cmd_vrings) {
- for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) {
- vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i);
- g_slice_free(VirtIOSCSIVring, s->cmd_vrings[i]);
- s->cmd_vrings[i] = NULL;
- }
- free(s->cmd_vrings);
- s->cmd_vrings = NULL;
+ virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL);
+ virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL);
+ for (i = 0; i < vs->conf.num_queues; i++) {
+ virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL);
}
}
@@ -224,30 +128,24 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s)
if (rc != 0) {
fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), "
"ensure -enable-kvm is set\n", rc);
- s->dataplane_fenced = true;
goto fail_guest_notifiers;
}
aio_context_acquire(s->ctx);
- s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq,
- virtio_scsi_iothread_handle_ctrl,
- 0);
- if (!s->ctrl_vring) {
+ rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0,
+ virtio_scsi_data_plane_handle_ctrl);
+ if (rc) {
goto fail_vrings;
}
- s->event_vring = virtio_scsi_vring_init(s, vs->event_vq,
- virtio_scsi_iothread_handle_event,
- 1);
- if (!s->event_vring) {
+ rc = virtio_scsi_vring_init(s, vs->event_vq, 1,
+ virtio_scsi_data_plane_handle_event);
+ if (rc) {
goto fail_vrings;
}
- s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues);
for (i = 0; i < vs->conf.num_queues; i++) {
- s->cmd_vrings[i] =
- virtio_scsi_vring_init(s, vs->cmd_vqs[i],
- virtio_scsi_iothread_handle_cmd,
- i + 2);
- if (!s->cmd_vrings[i]) {
+ rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2,
+ virtio_scsi_data_plane_handle_cmd);
+ if (rc) {
goto fail_vrings;
}
}
@@ -260,13 +158,14 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s)
fail_vrings:
virtio_scsi_clear_aio(s);
aio_context_release(s->ctx);
- virtio_scsi_vring_teardown(s);
for (i = 0; i < vs->conf.num_queues + 2; i++) {
k->set_host_notifier(qbus->parent, i, false);
}
k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
fail_guest_notifiers:
+ s->dataplane_fenced = true;
s->dataplane_starting = false;
+ s->dataplane_started = true;
}
/* Context: QEMU global mutex held */
@@ -277,12 +176,14 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
int i;
+ if (!s->dataplane_started || s->dataplane_stopping) {
+ return;
+ }
+
/* Better luck next time. */
if (s->dataplane_fenced) {
s->dataplane_fenced = false;
- return;
- }
- if (!s->dataplane_started || s->dataplane_stopping) {
+ s->dataplane_started = false;
return;
}
s->dataplane_stopping = true;
@@ -290,21 +191,12 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
aio_context_acquire(s->ctx);
- aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
- aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
- for (i = 0; i < vs->conf.num_queues; i++) {
- aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
- }
+ virtio_scsi_clear_aio(s);
blk_drain_all(); /* ensure there are no in-flight requests */
aio_context_release(s->ctx);
- /* Sync vring state back to virtqueue so that non-dataplane request
- * processing can continue when we disable the host notifier below.
- */
- virtio_scsi_vring_teardown(s);
-
for (i = 0; i < vs->conf.num_queues + 2; i++) {
k->set_host_notifier(qbus->parent, i, false);
}
diff --git a/qemu/hw/scsi/virtio-scsi.c b/qemu/hw/scsi/virtio-scsi.c
index 811c3da8b..30415c6a9 100644
--- a/qemu/hw/scsi/virtio-scsi.c
+++ b/qemu/hw/scsi/virtio-scsi.c
@@ -13,6 +13,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "standard-headers/linux/virtio_ids.h"
#include "hw/virtio/virtio-scsi.h"
#include "qemu/error-report.h"
@@ -22,7 +24,6 @@
#include <block/scsi.h>
#include <hw/virtio/virtio-bus.h>
#include "hw/virtio/virtio-access.h"
-#include "migration/migration.h"
static inline int virtio_scsi_get_lun(uint8_t *lun)
{
@@ -40,29 +41,23 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
}
-VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
+void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
{
- VirtIOSCSIReq *req;
- VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
- const size_t zero_skip = offsetof(VirtIOSCSIReq, elem)
- + sizeof(VirtQueueElement);
+ const size_t zero_skip =
+ offsetof(VirtIOSCSIReq, resp_iov) + sizeof(req->resp_iov);
- req = g_slice_alloc(sizeof(*req) + vs->cdb_size);
req->vq = vq;
req->dev = s;
qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory);
qemu_iovec_init(&req->resp_iov, 1);
memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip);
- return req;
}
void virtio_scsi_free_req(VirtIOSCSIReq *req)
{
- VirtIOSCSICommon *vs = (VirtIOSCSICommon *)req->dev;
-
qemu_iovec_destroy(&req->resp_iov);
qemu_sglist_destroy(&req->qsgl);
- g_slice_free1(sizeof(*req) + vs->cdb_size, req);
+ g_free(req);
}
static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
@@ -72,11 +67,10 @@ static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
VirtIODevice *vdev = VIRTIO_DEVICE(s);
qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size);
- if (req->vring) {
- assert(req->vq == NULL);
- virtio_scsi_vring_push_notify(req);
+ virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
+ if (s->dataplane_started && !s->dataplane_fenced) {
+ virtio_scsi_dataplane_notify(vdev, req);
} else {
- virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size);
virtio_notify(vdev, vq);
}
@@ -145,7 +139,7 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
*
* TODO: always disable this workaround for virtio 1.0 devices.
*/
- if (!virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) {
if (req->elem.out_num) {
req_size = req->elem.out_sg[0].iov_len;
}
@@ -176,11 +170,14 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
{
- VirtIOSCSIReq *req = virtio_scsi_init_req(s, vq);
- if (!virtqueue_pop(vq, &req->elem)) {
- virtio_scsi_free_req(req);
+ VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
+ VirtIOSCSIReq *req;
+
+ req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size);
+ if (!req) {
return NULL;
}
+ virtio_scsi_init_req(s, vq, req);
return req;
}
@@ -192,7 +189,7 @@ static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
assert(n < vs->conf.num_queues);
qemu_put_be32s(f, &n);
- qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
+ qemu_put_virtqueue_element(f, &req->elem);
}
static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
@@ -205,17 +202,8 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
qemu_get_be32s(f, &n);
assert(n < vs->conf.num_queues);
- req = virtio_scsi_init_req(s, vs->cmd_vqs[n]);
- qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
- /* TODO: add a way for SCSIBusInfo's load_request to fail,
- * and fail migration instead of asserting here.
- * When we do, we might be able to re-enable NDEBUG below.
- */
-#ifdef NDEBUG
-#error building with NDEBUG is not supported
-#endif
- assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg));
- assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg));
+ req = qemu_get_virtqueue_element(f, sizeof(VirtIOSCSIReq) + vs->cdb_size);
+ virtio_scsi_init_req(s, vs->cmd_vqs[n], req);
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) {
@@ -245,7 +233,7 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data)
if (--n->tmf_req->remaining == 0) {
virtio_scsi_complete_req(n->tmf_req);
}
- g_slice_free(VirtIOSCSICancelNotifier, n);
+ g_free(n);
}
/* Return 0 if the request is ready to be completed and return to guest;
@@ -259,7 +247,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
int target;
int ret = 0;
- if (s->dataplane_started) {
+ if (s->dataplane_started && d) {
assert(blk_get_aio_context(d->conf.blk) == s->ctx);
}
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
@@ -296,7 +284,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
VirtIOSCSICancelNotifier *notifier;
req->remaining = 1;
- notifier = g_slice_new(VirtIOSCSICancelNotifier);
+ notifier = g_new(VirtIOSCSICancelNotifier, 1);
notifier->tmf_req = req;
notifier->notifier.notify = virtio_scsi_cancel_notify;
scsi_req_cancel_async(r, &notifier->notifier);
@@ -345,7 +333,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
VirtIOSCSICancelNotifier *notifier;
req->remaining++;
- notifier = g_slice_new(VirtIOSCSICancelNotifier);
+ notifier = g_new(VirtIOSCSICancelNotifier, 1);
notifier->notifier.notify = virtio_scsi_cancel_notify;
notifier->tmf_req = req;
scsi_req_cancel_async(r, &notifier->notifier);
@@ -361,7 +349,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
target = req->req.tmf.lun[1];
s->resetting++;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
- d = DO_UPCAST(SCSIDevice, qdev, kid->child);
+ d = SCSI_DEVICE(kid->child);
if (d->channel == 0 && d->id == target) {
qdev_reset_all(&d->qdev);
}
@@ -386,7 +374,7 @@ fail:
return ret;
}
-void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
+static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
VirtIODevice *vdev = (VirtIODevice *)s;
uint32_t type;
@@ -424,20 +412,28 @@ void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
}
}
-static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq)
{
- VirtIOSCSI *s = (VirtIOSCSI *)vdev;
VirtIOSCSIReq *req;
- if (s->ctx && !s->dataplane_disabled) {
- virtio_scsi_dataplane_start(s);
- return;
- }
while ((req = virtio_scsi_pop_req(s, vq))) {
virtio_scsi_handle_ctrl_req(s, req);
}
}
+static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ if (s->ctx) {
+ virtio_scsi_dataplane_start(s);
+ if (!s->dataplane_fenced) {
+ return;
+ }
+ }
+ virtio_scsi_handle_ctrl_vq(s, vq);
+}
+
static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req)
{
/* Sense data is not in req->resp and is copied separately
@@ -520,7 +516,7 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
virtio_scsi_complete_cmd_req(req);
}
-bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
+static bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
VirtIOSCSICommon *vs = &s->parent_obj;
SCSIDevice *d;
@@ -562,7 +558,7 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
return true;
}
-void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
+static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
SCSIRequest *sreq = req->sreq;
if (scsi_req_enqueue(sreq)) {
@@ -572,17 +568,11 @@ void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
scsi_req_unref(sreq);
}
-static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
+void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
{
- /* use non-QOM casts in the data path */
- VirtIOSCSI *s = (VirtIOSCSI *)vdev;
VirtIOSCSIReq *req, *next;
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
- if (s->ctx && !s->dataplane_disabled) {
- virtio_scsi_dataplane_start(s);
- return;
- }
while ((req = virtio_scsi_pop_req(s, vq))) {
if (virtio_scsi_handle_cmd_req_prepare(s, req)) {
QTAILQ_INSERT_TAIL(&reqs, req, next);
@@ -594,6 +584,20 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
}
}
+static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /* use non-QOM casts in the data path */
+ VirtIOSCSI *s = (VirtIOSCSI *)vdev;
+
+ if (s->ctx) {
+ virtio_scsi_dataplane_start(s);
+ if (!s->dataplane_fenced) {
+ return;
+ }
+ }
+ virtio_scsi_handle_cmd_vq(s, vq);
+}
+
static void virtio_scsi_get_config(VirtIODevice *vdev,
uint8_t *config)
{
@@ -662,6 +666,11 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
static void virtio_scsi_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+
+ if (s->dataplane_started) {
+ virtio_scsi_dataplane_stop(s);
+ }
virtio_save(vdev, f);
}
@@ -694,11 +703,7 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
aio_context_acquire(s->ctx);
}
- if (s->dataplane_started) {
- req = virtio_scsi_pop_req_vring(s, s->event_vring);
- } else {
- req = virtio_scsi_pop_req(s, vs->event_vq);
- }
+ req = virtio_scsi_pop_req(s, vs->event_vq);
if (!req) {
s->events_dropped = true;
goto out;
@@ -736,17 +741,24 @@ out:
}
}
+void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq)
+{
+ if (s->events_dropped) {
+ virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
+ }
+}
+
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
- if (s->ctx && !s->dataplane_disabled) {
+ if (s->ctx) {
virtio_scsi_dataplane_start(s);
- return;
- }
- if (s->events_dropped) {
- virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
+ if (!s->dataplane_fenced) {
+ return;
+ }
}
+ virtio_scsi_handle_event_vq(s, vq);
}
static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
@@ -754,13 +766,29 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
VirtIODevice *vdev = VIRTIO_DEVICE(s);
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) &&
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) &&
dev->type != TYPE_ROM) {
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
sense.asc | (sense.ascq << 8));
}
}
+static void virtio_scsi_blk_insert_notifier(Notifier *n, void *data)
+{
+ VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier,
+ n, n);
+ assert(cn->sd->conf.blk == data);
+ blk_op_block_all(cn->sd->conf.blk, cn->s->blocker);
+}
+
+static void virtio_scsi_blk_remove_notifier(Notifier *n, void *data)
+{
+ VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier,
+ n, n);
+ assert(cn->sd->conf.blk == data);
+ blk_op_unblock_all(cn->sd->conf.blk, cn->s->blocker);
+}
+
static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
@@ -768,7 +796,9 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
SCSIDevice *sd = SCSI_DEVICE(dev);
- if (s->ctx && !s->dataplane_disabled) {
+ if (s->ctx && !s->dataplane_fenced) {
+ VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier;
+
if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
return;
}
@@ -776,9 +806,23 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
aio_context_acquire(s->ctx);
blk_set_aio_context(sd->conf.blk, s->ctx);
aio_context_release(s->ctx);
+
+ insert_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1);
+ insert_notifier->n.notify = virtio_scsi_blk_insert_notifier;
+ insert_notifier->s = s;
+ insert_notifier->sd = sd;
+ blk_add_insert_bs_notifier(sd->conf.blk, &insert_notifier->n);
+ QTAILQ_INSERT_TAIL(&s->insert_notifiers, insert_notifier, next);
+
+ remove_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1);
+ remove_notifier->n.notify = virtio_scsi_blk_remove_notifier;
+ remove_notifier->s = s;
+ remove_notifier->sd = sd;
+ blk_add_remove_bs_notifier(sd->conf.blk, &remove_notifier->n);
+ QTAILQ_INSERT_TAIL(&s->remove_notifiers, remove_notifier, next);
}
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd,
VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_RESCAN);
@@ -791,8 +835,9 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
SCSIDevice *sd = SCSI_DEVICE(dev);
+ VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier;
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd,
VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_REMOVED);
@@ -801,6 +846,25 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
if (s->ctx) {
blk_op_unblock_all(sd->conf.blk, s->blocker);
}
+
+ QTAILQ_FOREACH(insert_notifier, &s->insert_notifiers, next) {
+ if (insert_notifier->sd == sd) {
+ notifier_remove(&insert_notifier->n);
+ QTAILQ_REMOVE(&s->insert_notifiers, insert_notifier, next);
+ g_free(insert_notifier);
+ break;
+ }
+ }
+
+ QTAILQ_FOREACH(remove_notifier, &s->remove_notifiers, next) {
+ if (remove_notifier->sd == sd) {
+ notifier_remove(&remove_notifier->n);
+ QTAILQ_REMOVE(&s->remove_notifiers, remove_notifier, next);
+ g_free(remove_notifier);
+ break;
+ }
+ }
+
qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
}
@@ -856,31 +920,6 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
}
}
-/* Disable dataplane thread during live migration since it does not
- * update the dirty memory bitmap yet.
- */
-static void virtio_scsi_migration_state_changed(Notifier *notifier, void *data)
-{
- VirtIOSCSI *s = container_of(notifier, VirtIOSCSI,
- migration_state_notifier);
- MigrationState *mig = data;
-
- if (migration_in_setup(mig)) {
- if (!s->dataplane_started) {
- return;
- }
- virtio_scsi_dataplane_stop(s);
- s->dataplane_disabled = true;
- } else if (migration_has_finished(mig) ||
- migration_has_failed(mig)) {
- if (s->dataplane_started) {
- return;
- }
- blk_drain_all(); /* complete in-flight non-dataplane requests */
- s->dataplane_disabled = false;
- }
-}
-
static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -911,10 +950,11 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
virtio_scsi_save, virtio_scsi_load, s);
- s->migration_state_notifier.notify = virtio_scsi_migration_state_changed;
- add_migration_state_change_notifier(&s->migration_state_notifier);
error_setg(&s->blocker, "block device is in use by data plane");
+
+ QTAILQ_INIT(&s->insert_notifiers);
+ QTAILQ_INIT(&s->remove_notifiers);
}
static void virtio_scsi_instance_init(Object *obj)
@@ -943,8 +983,6 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
error_free(s->blocker);
unregister_savevm(dev, "virtio-scsi", s);
- remove_migration_state_change_notifier(&s->migration_state_notifier);
-
virtio_scsi_common_unrealize(dev, errp);
}
diff --git a/qemu/hw/scsi/vmw_pvscsi.c b/qemu/hw/scsi/vmw_pvscsi.c
index 9c71f31fe..e690b4ec0 100644
--- a/qemu/hw/scsi/vmw_pvscsi.c
+++ b/qemu/hw/scsi/vmw_pvscsi.c
@@ -25,6 +25,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/scsi/scsi.h"
#include <block/scsi.h>
#include "hw/pci/msi.h"
@@ -32,7 +34,6 @@
#include "trace.h"
-#define PVSCSI_MSI_OFFSET (0x50)
#define PVSCSI_USE_64BIT (true)
#define PVSCSI_PER_VECTOR_MASK (false)
@@ -49,9 +50,33 @@
(stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \
(m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val))
+typedef struct PVSCSIClass {
+ PCIDeviceClass parent_class;
+ DeviceRealize parent_dc_realize;
+} PVSCSIClass;
+
#define TYPE_PVSCSI "pvscsi"
#define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI)
+#define PVSCSI_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PVSCSIClass, (klass), TYPE_PVSCSI)
+#define PVSCSI_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI)
+
+/* Compatability flags for migration */
+#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0
+#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \
+ (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT)
+#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1
+#define PVSCSI_COMPAT_DISABLE_PCIE \
+ (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT)
+
+#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \
+ ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION)
+#define PVSCSI_MSI_OFFSET(s) \
+ (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c)
+#define PVSCSI_EXP_EP_OFFSET (0x40)
+
typedef struct PVSCSIRingInfo {
uint64_t rs_pa;
uint32_t txr_len_mask;
@@ -100,6 +125,8 @@ typedef struct {
PVSCSIRingInfo rings; /* Data transfer rings manager */
uint32_t resetting; /* Reset in progress */
+
+ uint32_t compat_flags;
} PVSCSIState;
typedef struct PVSCSIRequest {
@@ -1019,7 +1046,7 @@ pvscsi_init_msi(PVSCSIState *s)
int res;
PCIDevice *d = PCI_DEVICE(s);
- res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS,
+ res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK);
if (res < 0) {
trace_pvscsi_init_msi_fail(res);
@@ -1069,9 +1096,16 @@ pvscsi_init(PCIDevice *pci_dev)
trace_pvscsi_state("init");
- /* PCI subsystem ID */
- pci_dev->config[PCI_SUBSYSTEM_ID] = 0x00;
- pci_dev->config[PCI_SUBSYSTEM_ID + 1] = 0x10;
+ /* PCI subsystem ID, subsystem vendor ID, revision */
+ if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) {
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000);
+ } else {
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
+ PCI_VENDOR_ID_VMWARE);
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
+ PCI_DEVICE_ID_VMWARE_PVSCSI);
+ pci_config_set_revision(pci_dev->config, 0x2);
+ }
/* PCI latency timer = 255 */
pci_dev->config[PCI_LATENCY_TIMER] = 0xff;
@@ -1085,6 +1119,10 @@ pvscsi_init(PCIDevice *pci_dev)
pvscsi_init_msi(s);
+ if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus)) {
+ pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET);
+ }
+
s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s);
if (!s->completion_worker) {
pvscsi_cleanup_msi(s);
@@ -1139,6 +1177,27 @@ pvscsi_post_load(void *opaque, int version_id)
return 0;
}
+static bool pvscsi_vmstate_need_pcie_device(void *opaque)
+{
+ PVSCSIState *s = PVSCSI(opaque);
+
+ return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE);
+}
+
+static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id)
+{
+ return !pvscsi_vmstate_need_pcie_device(opaque);
+}
+
+static const VMStateDescription vmstate_pvscsi_pcie_device = {
+ .name = "pvscsi/pcie",
+ .needed = pvscsi_vmstate_need_pcie_device,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(parent_obj, PVSCSIState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_pvscsi = {
.name = "pvscsi",
.version_id = 0,
@@ -1146,7 +1205,9 @@ static const VMStateDescription vmstate_pvscsi = {
.pre_save = pvscsi_pre_save,
.post_load = pvscsi_post_load,
.fields = (VMStateField[]) {
- VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState),
+ VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState,
+ pvscsi_vmstate_test_pci_device, 0,
+ vmstate_pci_device, PCIDevice),
VMSTATE_UINT8(msi_used, PVSCSIState),
VMSTATE_UINT32(resetting, PVSCSIState),
VMSTATE_UINT64(reg_interrupt_status, PVSCSIState),
@@ -1171,18 +1232,40 @@ static const VMStateDescription vmstate_pvscsi = {
VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_pvscsi_pcie_device,
+ NULL
}
};
static Property pvscsi_properties[] = {
DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1),
+ DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags,
+ PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false),
+ DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags,
+ PVSCSI_COMPAT_DISABLE_PCIE_BIT, false),
DEFINE_PROP_END_OF_LIST(),
};
+static void pvscsi_realize(DeviceState *qdev, Error **errp)
+{
+ PVSCSIClass *pvs_c = PVSCSI_DEVICE_GET_CLASS(qdev);
+ PCIDevice *pci_dev = PCI_DEVICE(qdev);
+ PVSCSIState *s = PVSCSI(qdev);
+
+ if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
+ pvs_c->parent_dc_realize(qdev, errp);
+}
+
static void pvscsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ PVSCSIClass *pvs_k = PVSCSI_DEVICE_CLASS(klass);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
k->init = pvscsi_init;
@@ -1191,6 +1274,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI;
k->class_id = PCI_CLASS_STORAGE_SCSI;
k->subsystem_id = 0x1000;
+ pvs_k->parent_dc_realize = dc->realize;
+ dc->realize = pvscsi_realize;
dc->reset = pvscsi_reset;
dc->vmsd = &vmstate_pvscsi;
dc->props = pvscsi_properties;
@@ -1202,6 +1287,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
static const TypeInfo pvscsi_info = {
.name = TYPE_PVSCSI,
.parent = TYPE_PCI_DEVICE,
+ .class_size = sizeof(PVSCSIClass),
.instance_size = sizeof(PVSCSIState),
.class_init = pvscsi_class_init,
.interfaces = (InterfaceInfo[]) {