[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [RFC PATCH] hw: Add support for LSI SAS1068 (mptsas) de
From: |
Don Slutz |
Subject: |
Re: [Qemu-devel] [RFC PATCH] hw: Add support for LSI SAS1068 (mptsas) device |
Date: |
Sat, 5 Dec 2015 14:09:48 -0500 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0 |
On 10/27/15 23:17, Paolo Bonzini wrote:
> This adds the SAS1068 device, a SAS disk controller used in VMware that
> is oldish but widely supported and has decent performance. Unlike
> megasas, it presents itself as a SAS controller and not as a RAID
> controller. The device corresponds to the mptsas kernel driver in
> Linux.
>
> A few small things in the device setup are based on Don Slutz's old
> patch, but the device emulation was written from scratch based on Don's
> SeaBIOS patch and on the FreeBSD and Linux drivers. It is 2400 lines
> shorter than Don's patch (and roughly the same size as MegaSAS---also
> because it doesn't support the similar SPI controller), implements SCSI
> task management functions (with asynchronous cancellation), supports
> big-endian hosts, has complete support for migration and follows the
> QEMU coding standards much more closely.
>
> To write the driver, I first split Don's patch in two parts, with
> the configuration bits in one file and the rest in a separate file.
> I first left mptconfig.c in place and rewrote the rest, then deleted
> mptconfig.c as well. The configuration pages are still based mostly on
> VirtualBox's, though not exactly the same. However, the implementation
> is completely different. The contents of the pages themselves should
> not be copyrightable.
>
> Tested with FreeBSD for now. The previous version (before the
> configuration page rewrite) worked with RHEL and Windows guests as well.
>
> TODO: write qtest for (at least) config pages, test Linux and Windows.
>
> Signed-off-by: Don Slutz <address@hidden>
> Message-Id: <address@hidden>
> Signed-off-by: Paolo Bonzini <address@hidden>
> ---
Since address@hidden is no longer accessible by me, it would be
good to add my new e-mail:
Don Slutz <address@hidden>
Not sure the QEMU convention for doing so.
The patch is not "git clean":
Applying: hw: Add support for LSI SAS1068 (mptsas) device
/home/don/qemu/.git/rebase-apply/patch:2298: new blank line at EOF.
+
warning: 1 line adds whitespace errors.
Well, CentOS 5.3 & 6.3 do not see disks, but does see the controller:
address@hidden ~]# cat /proc/scsi/scsi
Attached devices:
address@hidden ~]# cat /proc/scsi/scsi
Here are the 1st 50 lines of trace output (for both old mpt_ and new
mptsas_):
TestCloud1:~/qemu>wc /var/log/xen/qemu-dm-C53-full.log
2630 15467 207356 /var/log/xen/qemu-dm-C53-full.log
TestCloud1:~/qemu>grep mpt_ /var/log/xen/qemu-dm-C53-full.log | head -50
address@hidden:mpt_mmio_writel addr 0x34: 0xffffffff drbl=0
nextEntry=0 drbl_idx=0 drbl_size=0
address@hidden:mpt_irq_lower INTx sts=00000000 mask=00000309
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=0
nextEntry=0 drbl_idx=0 drbl_size=0
address@hidden:mpt_irq_lower INTx sts=00000000 mask=00000309
address@hidden:mpt_mmio_writel addr 0x34: 0xffffffff drbl=0
nextEntry=0 drbl_idx=0 drbl_size=0
address@hidden:mpt_irq_lower INTx sts=00000000 mask=00000309
address@hidden:mpt_mmio_readl addr 0x0: 0x10000000
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=0
nextEntry=0 drbl_idx=0 drbl_size=0
address@hidden:mpt_irq_lower INTx sts=00000000 mask=00000309
address@hidden:mpt_mmio_writel addr 0x0: 0x42030000 drbl=0
nextEntry=0 drbl_idx=0 drbl_size=0
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x18000000
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=0 drbl_idx=0 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000000 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x0
address@hidden:mpt_mmio_writel addr 0x0: 0x3000000 drbl=1
nextEntry=0 drbl_idx=0 drbl_size=3
address@hidden:mpt_mmio_readl addr 0x30: 0x0
address@hidden:mpt_mmio_writel addr 0x0: 0x0 drbl=1 nextEntry=0
drbl_idx=1 drbl_size=3
address@hidden:mpt_mmio_readl addr 0x30: 0x0
address@hidden:mpt_mmio_writel addr 0x0: 0x0 drbl=1 nextEntry=0
drbl_idx=2 drbl_size=3
address@hidden:mpt_process_message MPT cmd IOC_FACTS
address@hidden:mpt_ioc_facts ports=8 max_devices=256
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x18000105
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=0 drbl_idx=3 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x1800030f
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=1 drbl_idx=3 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x18000000
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=2 drbl_idx=3 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x18000000
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=3 drbl_idx=3 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x18000000
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=4 drbl_idx=3 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
address@hidden:mpt_mmio_readl addr 0x0: 0x18000000
address@hidden:mpt_mmio_writel addr 0x30: 0x0 drbl=1
nextEntry=5 drbl_idx=3 drbl_size=3
address@hidden:mpt_irq_lower INTx sts=00000001 mask=00000309
address@hidden:mpt_mmio_readl addr 0x30: 0x1
TestCloud1:~/qemu>grep mptsas_ /var/log/xen/qemu-dm-C53-full.log | head -50
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000034 value 0xffffffff
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000034 value 0xffffffff
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x10000000
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000000 value 0x42030000
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000000
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000000 value 0x3000000
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000000 value 0x0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000000 value 0x0
address@hidden:mptsas_process_message dev 0x7fe244322060 cmd 3
context 0x00000000
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000105
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000314
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000000
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000000
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000000
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000000
address@hidden:mptsas_mmio_write dev 0x7fe244322060 addr
0x00000030 value 0x0
address@hidden:mptsas_irq_intx dev 0x7fe244322060 level 0
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000030 value 0x1
address@hidden:mptsas_mmio_read dev 0x7fe244322060 addr
0x00000000 value 0x18000000
> default-configs/pci.mak | 1 +
> hw/scsi/Makefile.objs | 1 +
> hw/scsi/mpi.h | 1153 +++++++++++++++++++++++++++++++++++++
> hw/scsi/mptconfig.c | 883 ++++++++++++++++++++++++++++
> hw/scsi/mptendian.c | 203 +++++++
> hw/scsi/mptsas.c | 1438
> ++++++++++++++++++++++++++++++++++++++++++++++
> hw/scsi/mptsas.h | 100 ++++
> include/hw/pci/pci_ids.h | 1 +
> trace-events | 18 +
> 9 files changed, 3798 insertions(+)
> create mode 100644 hw/scsi/mpi.h
> create mode 100644 hw/scsi/mptconfig.c
> create mode 100644 hw/scsi/mptendian.c
> create mode 100644 hw/scsi/mptsas.c
> create mode 100644 hw/scsi/mptsas.h
>
> diff --git a/default-configs/pci.mak b/default-configs/pci.mak
> index 7e10903..016eb22 100644
> --- a/default-configs/pci.mak
> +++ b/default-configs/pci.mak
> @@ -15,6 +15,7 @@ CONFIG_ES1370=y
> CONFIG_LSI_SCSI_PCI=y
> CONFIG_VMW_PVSCSI_SCSI_PCI=y
> CONFIG_MEGASAS_SCSI_PCI=y
> +CONFIG_MPTSAS_SCSI_PCI=y
> CONFIG_RTL8139_PCI=y
> CONFIG_E1000_PCI=y
> CONFIG_VMXNET3_PCI=y
> diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs
> index 40c79d3..5a2248b 100644
> --- a/hw/scsi/Makefile.objs
> +++ b/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/hw/scsi/mpi.h b/hw/scsi/mpi.h
> new file mode 100644
> index 0000000..0568e19
> --- /dev/null
> +++ b/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/hw/scsi/mptconfig.c b/hw/scsi/mptconfig.c
> new file mode 100644
> index 0000000..7dc927e
> --- /dev/null
> +++ b/hw/scsi/mptconfig.c
> @@ -0,0 +1,883 @@
> +/*
> + * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages
> + *
> + * Copyright (c) 2015 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 "hw/hw.h"
> +#include "hw/pci/pci.h"
> +#include "hw/scsi/scsi.h"
> +
> +#include "mptsas.h"
> +#include "mpi.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) {
> + switch (*p) {
> + case '*':
> + val.ll = 0;
> + 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++;
> + case 'w':
> + if (data) {
> + stw_le_p(data + ofs, val.ll);
> + }
> + ofs += 2;
> + 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, val.str, cnt);
> + } else {
> + memset((void *)data, 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);
> + }
> + 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_6_0 0x0A
> +
> +#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_6_0
> + : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION),
> + (dev
> + ? MPI_SAS_DEVICE_INFO_NO_DEVICE
> + : MPI_SAS_DEVICE_INFO_END_DEVICE |
> MPI_SAS_DEVICE_INFO_SSP_TARGET),
> + dev_handle,
> + phy_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_6_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5,
> + (dev
> + ? MPI_SAS_DEVICE_INFO_NO_DEVICE
> + : MPI_SAS_DEVICE_INFO_END_DEVICE |
> MPI_SAS_DEVICE_INFO_SSP_TARGET),
> + 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)
> +{
> + uint64_t wwn = 0; /* TODO */
> + int phy_handle, dev_handle;
> + int i = mptsas_phy_addr_get(s, address);
> + SCSIDevice *dev;
> +
> + if (i < 0) {
> + return i;
> + }
> +
> + dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
> + return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01,
> + "w*wqwb*bwbb*b*b*l",
> + dev_handle, wwn, dev_handle, i,
> + (dev
> + ? MPI_SAS_DEVICE_INFO_NO_DEVICE
> + : MPI_SAS_DEVICE_INFO_END_DEVICE |
> MPI_SAS_DEVICE_INFO_SSP_TARGET),
> + (MPI_SAS_IOUNIT0_RATE_6_0 << 4) |
> MPI_SAS_IOUNIT0_RATE_1_5,
> + (MPI_SAS_IOUNIT0_RATE_6_0 << 4) |
> MPI_SAS_IOUNIT0_RATE_1_5);
> +}
> +
> +static
> +size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address)
> +{
> + int i = mptsas_phy_addr_get(s, address);
> +
> + if (i < 0) {
> + return i;
> + }
> +
> + 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 {
> + ++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)
> +{
> + uint64_t wwn = 0; /* TODO */
> + int phy_handle, dev_handle;
> + int i = mptsas_device_addr_get(s, address);
> + SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle);
> +
> + if (!dev) {
> + return -ENOENT;
> + }
> +
> + return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05,
> + "*w*wqwbbwbblwb*b",
> + 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)
> +{
> + uint64_t wwn = 0; /* TODO */
> + int dev_handle;
> + int i = mptsas_device_addr_get(s, address);
> + SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, &dev_handle);
> +
> + if (!dev) {
> + return -ENOENT;
> + }
> +
> + return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00,
> + "*lq*lwbb*s20",
> + wwn, dev_handle, i, 0);
> +}
> +
> +static
> +size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int
> address)
> +{
> + uint64_t wwn = 0; /* TODO */
> + int dev_handle;
> + int i = mptsas_device_addr_get(s, address);
> + SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, &dev_handle);
> +
> + if (!dev) {
> + return -ENOENT;
> + }
> +
> + return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01,
> + "ql", 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;
> + 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);
> +}
> diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c
> new file mode 100644
> index 0000000..c01765f
> --- /dev/null
> +++ b/hw/scsi/mptendian.c
> @@ -0,0 +1,203 @@
> +/*
> + * QEMU LSI SAS1068 Host Bus Adapter emulation
> + * Endianness conversion for MPI data structures
> + *
> + * Copyright (c) 2015 Red Hat, Inc.
> + *
> + * Authors: Paolo Bonzini <address@hidden>
> + *
> + * 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 "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/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
> new file mode 100644
> index 0000000..e814b86
> --- /dev/null
> +++ b/hw/scsi/mptsas.c
> @@ -0,0 +1,1438 @@
> +/*
> + * 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) 2015 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 "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 flags_and_length, 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;
> +
> + 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_LAST_ELEMENT) &&
> + !(flags_and_length & MPI_SGE_FLAGS_END_OF_LIST)) {
> + continue;
> + }
> +
> + if (!chain_offset || (flags_and_length & MPI_SGE_FLAGS_END_OF_LIST))
> {
> + 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, ¬ifier->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, ¬ifier->notifier);
> + }
> + }
> +
> +reply_maybe_async:
> + if (reply_async->TerminationCount < count) {
> + reply_async->IOCLogInfo = count;
> + return;
> + }
> + 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_SHIFT;
> + 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->max_devices = MPTSAS_NUM_PORTS;
> + s->max_buses = 1;
> +
> + s->state = MPI_IOC_STATE_READY;
> + 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;
> + }
> +
> + if (MPTSAS_FIFO_EMPTY(s, reply_post)) {
> + 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, request_post)) {
> + 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 = 8,
> + .max_access_size = 8,
> + }
> +};
> +
> +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 = 8,
> + .max_access_size = 8,
> + }
> +};
> +
> +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;
This should be PCI_CLASS_STORAGE_SAS.
-Don Slutz
> + 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/hw/scsi/mptsas.h b/hw/scsi/mptsas.h
> new file mode 100644
> index 0000000..416341c
> --- /dev/null
> +++ b/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 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/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
> index d98e6c9..db85afa 100644
> --- a/include/hw/pci/pci_ids.h
> +++ b/include/hw/pci/pci_ids.h
> @@ -64,6 +64,7 @@
> #define PCI_VENDOR_ID_LSI_LOGIC 0x1000
> #define PCI_DEVICE_ID_LSI_53C810 0x0001
> #define PCI_DEVICE_ID_LSI_53C895A 0x0012
> +#define PCI_DEVICE_ID_LSI_SAS1068 0x0054
> #define PCI_DEVICE_ID_LSI_SAS1078 0x0060
> #define PCI_DEVICE_ID_LSI_SAS0079 0x0079
>
> diff --git a/trace-events b/trace-events
> index bdfe79f..e7dab4c 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -722,6 +722,24 @@ lm32_uart_memory_write(uint32_t addr, uint32_t value)
> "addr 0x%08x value 0x%08x"
> lm32_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value
> 0x%08x"
> lm32_uart_irq_state(int level) "irq state %d"
>
> +# hw/mptsas.c
> +mptsas_command_complete(void *dev, uint32_t ctx, uint32_t status, uint32_t
> resid) "dev %p context 0x%08x status %x resid %d"
> +mptsas_diag_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x
> value 0x%08x"
> +mptsas_diag_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr
> 0x%08x value 0x%08x"
> +mptsas_irq_intx(void *dev, int level) "dev %p level %d"
> +mptsas_irq_msi(void *dev) "dev %p "
> +mptsas_mmio_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x
> value 0x%x"
> +mptsas_mmio_unhandled_read(void *dev, uint32_t addr) "dev %p addr 0x%08x"
> +mptsas_mmio_unhandled_write(void *dev, uint32_t addr, uint32_t val) "dev %p
> addr 0x%08x value 0x%x"
> +mptsas_mmio_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr
> 0x%08x value 0x%x"
> +mptsas_process_message(void *dev, int msg, uint32_t ctx) "dev %p cmd %d
> context 0x%08x\n"
> +mptsas_process_scsi_io_request(void *dev, int bus, int target, int lun,
> uint64_t len) "dev %p dev %d:%d:%d length %"PRIu64""
> +mptsas_reset(void *dev) "dev %p "
> +mptsas_scsi_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found)
> "dev %p context 0x%08x: %"PRIu64"/%"PRIu64""
> +mptsas_sgl_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found)
> "dev %p context 0x%08x: %"PRIu64"/%"PRIu64""
> +mptsas_unhandled_cmd(void *dev, uint32_t ctx, uint8_t msg_cmd) "dev %p
> context 0x%08x: Unhandled cmd %x"
> +mptsas_unhandled_doorbell_cmd(void *dev, int cmd) "dev %p value 0x%08x"
> +
> # hw/scsi/megasas.c
> megasas_init_firmware(uint64_t pa) "pa %" PRIx64 " "
> megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t
> tail, uint32_t flags) "queue at %" PRIx64 " len %d head %" PRIx64 " tail %"
> PRIx64 " flags %x"
>
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [Qemu-devel] [RFC PATCH] hw: Add support for LSI SAS1068 (mptsas) device,
Don Slutz <=