Message ID | 1446002245-24360-1-git-send-email-pbonzini@redhat.com |
---|---|
State | New |
Headers | show |
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 <Don@CloudSwitch.com> > Message-Id: <1347382813-5662-1-git-send-email-Don@CloudSwitch.com> > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- Since Don@CloudSwitch.com is no longer accessible by me, it would be good to add my new e-mail: Don Slutz <Don.Slutz@Gmail.com> 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: [root@C63-min-tools ~]# cat /proc/scsi/scsi Attached devices: [root@C63-min-tools ~]# 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 30844@1449341974.699455:mpt_mmio_writel addr 0x34: 0xffffffff drbl=0 nextEntry=0 drbl_idx=0 drbl_size=0 30844@1449341974.699502:mpt_irq_lower INTx sts=00000000 mask=00000309 30844@1449341974.699542:mpt_mmio_writel addr 0x30: 0x0 drbl=0 nextEntry=0 drbl_idx=0 drbl_size=0 30844@1449341974.699550:mpt_irq_lower INTx sts=00000000 mask=00000309 30844@1449341974.699616:mpt_mmio_writel addr 0x34: 0xffffffff drbl=0 nextEntry=0 drbl_idx=0 drbl_size=0 30844@1449341974.699654:mpt_irq_lower INTx sts=00000000 mask=00000309 30844@1449341974.699724:mpt_mmio_readl addr 0x0: 0x10000000 30844@1449341974.699800:mpt_mmio_writel addr 0x30: 0x0 drbl=0 nextEntry=0 drbl_idx=0 drbl_size=0 30844@1449341974.699843:mpt_irq_lower INTx sts=00000000 mask=00000309 30844@1449341974.699912:mpt_mmio_writel addr 0x0: 0x42030000 drbl=0 nextEntry=0 drbl_idx=0 drbl_size=0 30844@1449341974.699956:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.700024:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.700161:mpt_mmio_readl addr 0x0: 0x18000000 30844@1449341974.700277:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=0 drbl_idx=0 drbl_size=3 30844@1449341974.700293:mpt_irq_lower INTx sts=00000000 mask=00000309 30844@1449341974.702182:mpt_mmio_readl addr 0x30: 0x0 30844@1449341974.702232:mpt_mmio_writel addr 0x0: 0x3000000 drbl=1 nextEntry=0 drbl_idx=0 drbl_size=3 30844@1449341974.704332:mpt_mmio_readl addr 0x30: 0x0 30844@1449341974.704382:mpt_mmio_writel addr 0x0: 0x0 drbl=1 nextEntry=0 drbl_idx=1 drbl_size=3 30844@1449341974.706177:mpt_mmio_readl addr 0x30: 0x0 30844@1449341974.706225:mpt_mmio_writel addr 0x0: 0x0 drbl=1 nextEntry=0 drbl_idx=2 drbl_size=3 30844@1449341974.706309:mpt_process_message MPT cmd IOC_FACTS 30844@1449341974.706319:mpt_ioc_facts ports=8 max_devices=256 30844@1449341974.706331:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.708200:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.708248:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.708305:mpt_mmio_readl addr 0x0: 0x18000105 30844@1449341974.708348:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=0 drbl_idx=3 drbl_size=3 30844@1449341974.708356:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.708408:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.708488:mpt_mmio_readl addr 0x0: 0x1800030f 30844@1449341974.708556:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=1 drbl_idx=3 drbl_size=3 30844@1449341974.708600:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.708712:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.708785:mpt_mmio_readl addr 0x0: 0x18000000 30844@1449341974.708826:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=2 drbl_idx=3 drbl_size=3 30844@1449341974.708838:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.708878:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.708924:mpt_mmio_readl addr 0x0: 0x18000000 30844@1449341974.708974:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=3 drbl_idx=3 drbl_size=3 30844@1449341974.708981:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.709022:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.709063:mpt_mmio_readl addr 0x0: 0x18000000 30844@1449341974.709145:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=4 drbl_idx=3 drbl_size=3 30844@1449341974.709155:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.709193:mpt_mmio_readl addr 0x30: 0x1 30844@1449341974.709233:mpt_mmio_readl addr 0x0: 0x18000000 30844@1449341974.709281:mpt_mmio_writel addr 0x30: 0x0 drbl=1 nextEntry=5 drbl_idx=3 drbl_size=3 30844@1449341974.709293:mpt_irq_lower INTx sts=00000001 mask=00000309 30844@1449341974.709332:mpt_mmio_readl addr 0x30: 0x1 TestCloud1:~/qemu>grep mptsas_ /var/log/xen/qemu-dm-C53-full.log | head -50 30844@1449341974.802097:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000034 value 0xffffffff 30844@1449341974.802114:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.802189:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.802205:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.802260:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000034 value 0xffffffff 30844@1449341974.802272:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.802322:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x10000000 30844@1449341974.802379:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.802391:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.802430:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000000 value 0x42030000 30844@1449341974.802443:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.802482:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.802525:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000000 30844@1449341974.802569:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.802578:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.804437:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.804485:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000000 value 0x3000000 30844@1449341974.806176:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.806224:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000000 value 0x0 30844@1449341974.808181:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.808229:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000000 value 0x0 30844@1449341974.808236:mptsas_process_message dev 0x7fe244322060 cmd 3 context 0x00000000 30844@1449341974.808243:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.810516:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.810564:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.810603:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000105 30844@1449341974.810655:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.810666:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.810705:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.810752:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000314 30844@1449341974.810795:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.810807:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.810846:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.810885:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000000 30844@1449341974.810930:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.810939:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.810977:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.811021:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000000 30844@1449341974.811066:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.811077:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.811153:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.811197:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000000 30844@1449341974.811238:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.811259:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.811318:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.811362:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000000 value 0x18000000 30844@1449341974.811404:mptsas_mmio_write dev 0x7fe244322060 addr 0x00000030 value 0x0 30844@1449341974.811415:mptsas_irq_intx dev 0x7fe244322060 level 0 30844@1449341974.811460:mptsas_mmio_read dev 0x7fe244322060 addr 0x00000030 value 0x1 30844@1449341974.811503: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 <pbonzini@redhat.com> > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include "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" >
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 <pbonzini@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "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; + 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"