mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
hw: Add support for LSI SAS1068 (mptsas) device
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. 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>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1153
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* QEMU LSI SAS1068 Host Bus Adapter emulation
|
||||
* Endianness conversion for MPI data structures
|
||||
*
|
||||
* Copyright (c) 2016 Red Hat, Inc.
|
||||
*
|
||||
* Authors: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "sysemu/dma.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "hw/scsi/scsi.h"
|
||||
#include "block/scsi.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "mptsas.h"
|
||||
#include "mpi.h"
|
||||
|
||||
static void mptsas_fix_sgentry_endianness(MPISGEntry *sge)
|
||||
{
|
||||
le32_to_cpus(&sge->FlagsLength);
|
||||
if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
|
||||
le64_to_cpus(&sge->u.Address64);
|
||||
} else {
|
||||
le32_to_cpus(&sge->u.Address32);
|
||||
}
|
||||
}
|
||||
|
||||
static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge)
|
||||
{
|
||||
if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) {
|
||||
cpu_to_le64s(&sge->u.Address64);
|
||||
} else {
|
||||
cpu_to_le32s(&sge->u.Address32);
|
||||
}
|
||||
cpu_to_le32s(&sge->FlagsLength);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le32_to_cpus(&req->Control);
|
||||
le32_to_cpus(&req->DataLength);
|
||||
le32_to_cpus(&req->SenseBufferLowAddr);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le32s(&reply->TransferCount);
|
||||
cpu_to_le32s(&reply->SenseCount);
|
||||
cpu_to_le32s(&reply->ResponseInfo);
|
||||
cpu_to_le16s(&reply->TaskTag);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le32_to_cpus(&req->TaskMsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le32s(&reply->TerminationCount);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le16_to_cpus(&req->ReplyFrameSize);
|
||||
le32_to_cpus(&req->HostMfaHighAddr);
|
||||
le32_to_cpus(&req->SenseBufferHighAddr);
|
||||
le32_to_cpus(&req->ReplyFifoHostSignalingAddr);
|
||||
mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE);
|
||||
le16_to_cpus(&req->MsgVersion);
|
||||
le16_to_cpus(&req->HeaderVersion);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply)
|
||||
{
|
||||
cpu_to_le16s(&reply->MsgVersion);
|
||||
cpu_to_le16s(&reply->HeaderVersion);
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCExceptions);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le16s(&reply->ReplyQueueDepth);
|
||||
cpu_to_le16s(&reply->RequestFrameSize);
|
||||
cpu_to_le16s(&reply->ProductID);
|
||||
cpu_to_le32s(&reply->CurrentHostMfaHighAddr);
|
||||
cpu_to_le16s(&reply->GlobalCredits);
|
||||
cpu_to_le32s(&reply->CurrentSenseBufferHighAddr);
|
||||
cpu_to_le16s(&reply->CurReplyFrameSize);
|
||||
cpu_to_le32s(&reply->FWImageSize);
|
||||
cpu_to_le32s(&reply->IOCCapabilities);
|
||||
cpu_to_le16s(&reply->HighPriorityQueueDepth);
|
||||
mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE);
|
||||
cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr);
|
||||
}
|
||||
|
||||
void mptsas_fix_config_endianness(MPIMsgConfig *req)
|
||||
{
|
||||
le16_to_cpus(&req->ExtPageLength);
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
le32_to_cpus(&req->PageAddress);
|
||||
mptsas_fix_sgentry_endianness(&req->PageBufferSGE);
|
||||
}
|
||||
|
||||
void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply)
|
||||
{
|
||||
cpu_to_le16s(&reply->ExtPageLength);
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le16s(&reply->MaxDevices);
|
||||
cpu_to_le16s(&reply->PortSCSIID);
|
||||
cpu_to_le16s(&reply->ProtocolFlags);
|
||||
cpu_to_le16s(&reply->MaxPostedCmdBuffers);
|
||||
cpu_to_le16s(&reply->MaxPersistentIDs);
|
||||
cpu_to_le16s(&reply->MaxLanBuckets);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply)
|
||||
{
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
}
|
||||
|
||||
void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req)
|
||||
{
|
||||
le32_to_cpus(&req->MsgContext);
|
||||
}
|
||||
|
||||
void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply)
|
||||
{
|
||||
int length = reply->EventDataLength;
|
||||
int i;
|
||||
|
||||
cpu_to_le16s(&reply->EventDataLength);
|
||||
cpu_to_le32s(&reply->MsgContext);
|
||||
cpu_to_le16s(&reply->IOCStatus);
|
||||
cpu_to_le32s(&reply->IOCLogInfo);
|
||||
cpu_to_le32s(&reply->Event);
|
||||
cpu_to_le32s(&reply->EventContext);
|
||||
|
||||
/* Really depends on the event kind. This will do for now. */
|
||||
for (i = 0; i < length; i++) {
|
||||
cpu_to_le32s(&reply->Data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
+1441
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
#ifndef MPTSAS_H
|
||||
#define MPTSAS_H
|
||||
|
||||
#include "mpi.h"
|
||||
|
||||
#define MPTSAS_NUM_PORTS 8
|
||||
#define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */
|
||||
|
||||
#define MPTSAS_REQUEST_QUEUE_DEPTH 128
|
||||
#define MPTSAS_REPLY_QUEUE_DEPTH 128
|
||||
|
||||
#define MPTSAS_MAXIMUM_CHAIN_DEPTH 0x22
|
||||
|
||||
typedef struct MPTSASState MPTSASState;
|
||||
typedef struct MPTSASRequest MPTSASRequest;
|
||||
|
||||
enum {
|
||||
DOORBELL_NONE,
|
||||
DOORBELL_WRITE,
|
||||
DOORBELL_READ
|
||||
};
|
||||
|
||||
struct MPTSASState {
|
||||
PCIDevice dev;
|
||||
MemoryRegion mmio_io;
|
||||
MemoryRegion port_io;
|
||||
MemoryRegion diag_io;
|
||||
QEMUBH *request_bh;
|
||||
|
||||
uint32_t msi_available;
|
||||
uint64_t sas_addr;
|
||||
|
||||
bool msi_in_use;
|
||||
|
||||
/* Doorbell register */
|
||||
uint32_t state;
|
||||
uint8_t who_init;
|
||||
uint8_t doorbell_state;
|
||||
|
||||
/* Buffer for requests that are sent through the doorbell register. */
|
||||
uint32_t doorbell_msg[256];
|
||||
int doorbell_idx;
|
||||
int doorbell_cnt;
|
||||
|
||||
uint16_t doorbell_reply[256];
|
||||
int doorbell_reply_idx;
|
||||
int doorbell_reply_size;
|
||||
|
||||
/* Other registers */
|
||||
uint8_t diagnostic_idx;
|
||||
uint32_t diagnostic;
|
||||
uint32_t intr_mask;
|
||||
uint32_t intr_status;
|
||||
|
||||
/* Request queues */
|
||||
uint32_t request_post[MPTSAS_REQUEST_QUEUE_DEPTH + 1];
|
||||
uint16_t request_post_head;
|
||||
uint16_t request_post_tail;
|
||||
|
||||
uint32_t reply_post[MPTSAS_REPLY_QUEUE_DEPTH + 1];
|
||||
uint16_t reply_post_head;
|
||||
uint16_t reply_post_tail;
|
||||
|
||||
uint32_t reply_free[MPTSAS_REPLY_QUEUE_DEPTH + 1];
|
||||
uint16_t reply_free_head;
|
||||
uint16_t reply_free_tail;
|
||||
|
||||
/* IOC Facts */
|
||||
hwaddr host_mfa_high_addr;
|
||||
hwaddr sense_buffer_high_addr;
|
||||
uint16_t max_devices;
|
||||
uint16_t max_buses;
|
||||
uint16_t reply_frame_size;
|
||||
|
||||
SCSIBus bus;
|
||||
QTAILQ_HEAD(, MPTSASRequest) pending;
|
||||
};
|
||||
|
||||
void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req);
|
||||
void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply);
|
||||
void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req);
|
||||
void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply);
|
||||
void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req);
|
||||
void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply);
|
||||
void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req);
|
||||
void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply);
|
||||
void mptsas_fix_config_endianness(MPIMsgConfig *req);
|
||||
void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply);
|
||||
void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req);
|
||||
void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply);
|
||||
void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req);
|
||||
void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply);
|
||||
void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req);
|
||||
void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply);
|
||||
|
||||
void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply);
|
||||
|
||||
void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req);
|
||||
|
||||
#endif /* MPTSAS_H */
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -726,6 +726,28 @@ 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/scsi/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/mptconfig.c
|
||||
mptsas_config_sas_device(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d"
|
||||
mptsas_config_sas_phy(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d"
|
||||
|
||||
# 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"
|
||||
|
||||
Reference in New Issue
Block a user