[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] megasas: LSI MegaRAID SAS HBA emulation
From: |
Hannes Reinecke |
Subject: |
[Qemu-devel] [PATCH] megasas: LSI MegaRAID SAS HBA emulation |
Date: |
Thu, 12 Nov 2009 12:48:49 +0100 |
User-agent: |
Heirloom mailx 12.2 01/07/07 |
This patch adds an emulation for the LSI MegaRAID SAS HBA.
It is build on top of kraxel's scsi.v7 tree.
This is just a rough implementation, many of the more
advanced topics (like Windows booting :-) are missing.
Signed-off-by: Hannes Reinecke <address@hidden>
--
diff --git a/Makefile.hw b/Makefile.hw
index 62773a0..ccd512e 100644
--- a/Makefile.hw
+++ b/Makefile.hw
@@ -33,7 +33,7 @@ obj-y += ne2000.o
# SCSI layer
obj-y += scsi-disk.o scsi-generic.o
-obj-y += lsi53c895a.o
+obj-y += lsi53c895a.o megasas.o
obj-$(CONFIG_ESP) += esp.o
obj-y += dma-helpers.o sysbus.o isa-bus.o
diff --git a/hw/megasas.c b/hw/megasas.c
new file mode 100644
index 0000000..dc71bf4
--- /dev/null
+++ b/hw/megasas.c
@@ -0,0 +1,1184 @@
+/*
+ * QEMU MegaRAID SAS 8708EM2 Host Bus Adapter emulation
+ *
+ * Copyright (c) 2009 Hannes Reinecke, SUSE Linux Products GmbH
+ *
+ * This code is licenced under the LGPL.
+ */
+
+
+#include <assert.h>
+
+#include "hw.h"
+#include "pci.h"
+#include "dma.h"
+#include "scsi.h"
+#include "scsi-defs.h"
+#include "block_int.h"
+#ifdef __linux__
+# include <scsi/sg.h>
+#endif
+
+#undef DEBUG_MEGASAS
+#undef DEBUG_MEGASAS_REG
+#undef DEBUG_MEGASAS_QUEUE
+#undef DEBUG_MEGASAS_MFI
+
+#ifdef DEBUG_MEGASAS
+#define DPRINTF(fmt, ...) \
+do { printf("megasas: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "megasas: error: " fmt , ## __VA_ARGS__); exit(1);} while
(0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "megasas: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Static definitions */
+#define MEGASAS_MAX_FRAMES 64
+#define MEGASAS_MAX_SGE 8
+#define MEGASAS_MAX_LUNS 128
+
+/* Register definitions */
+#define MEGASAS_INBOUND_MSG_0 0x0010
+#define MEGASAS_INBOUND_MSG_1 0x0014
+#define MEGASAS_OUTBOUND_MSG_0 0x0018
+#define MEGASAS_OUTBOUND_MSG_1 0x001C
+#define MEGASAS_INBOUND_DOORBELL 0x0020
+#define MEGASAS_INBOUND_INTR_STATUS 0x0024
+#define MEGASAS_INBOUND_INTR_MASK 0x0028
+#define MEGASAS_OUTBOUND_DOORBELL 0x002C
+#define MEGASAS_OUTBOUND_INTR_STATUS 0x0030
+#define MEGASAS_OUTBOUND_INTR_MASK 0x0034
+#define MEGASAS_INBOUND_QUEUE_PORT 0x0040
+#define MEGASAS_OUTBOUND_QUEUE_PORT 0x0044
+#define MEGASAS_OUTBOUND_DOORBELL_CLEAR 0x00A0
+#define MEGASAS_OUTBOUND_SCRATCH_PAD 0x00B0
+#define MEGASAS_INBOUND_LOW_QUEUE_PORT 0x00C0
+#define MEGASAS_INBOUND_HIGH_QUEUE_PORT 0x00C4
+
+/* FW commands */
+#define MFI_INIT_ABORT 0x00000001
+#define MFI_INIT_READY 0x00000002
+#define MFI_INIT_MFIMODE 0x00000004
+#define MFI_INIT_CLEAR_HANDSHAKE 0x00000008
+#define MFI_INIT_HOTPLUG 0x00000010
+#define MFI_STOP_ADP 0x00000020
+
+/* MFI states */
+#define MFI_STATE_UNDEFINED 0x0
+#define MFI_STATE_BB_INIT 0x1
+#define MFI_STATE_FW_INIT 0x4
+#define MFI_STATE_WAIT_HANDSHAKE 0x6
+#define MFI_STATE_FW_INIT_2 0x7
+#define MFI_STATE_DEVICE_SCAN 0x8
+#define MFI_STATE_BOOT_MESSAGE_PENDING 0x9
+#define MFI_STATE_FLUSH_CACHE 0xA
+#define MFI_STATE_READY 0xB
+#define MFI_STATE_OPERATIONAL 0xC
+#define MFI_STATE_FAULT 0xF
+
+/*
+ * MFI command opcodes
+ */
+#define MFI_CMD_INIT 0x00
+#define MFI_CMD_LD_READ 0x01
+#define MFI_CMD_LD_WRITE 0x02
+#define MFI_CMD_LD_SCSI_IO 0x03
+#define MFI_CMD_PD_SCSI_IO 0x04
+#define MFI_CMD_DCMD 0x05
+#define MFI_CMD_ABORT 0x06
+#define MFI_CMD_SMP 0x07
+#define MFI_CMD_STP 0x08
+
+#define MR_DCMD_CTRL_GET_INFO 0x01010000
+
+#define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000
+#define MR_FLUSH_CTRL_CACHE 0x01
+#define MR_FLUSH_DISK_CACHE 0x02
+
+#define MR_DCMD_CTRL_SHUTDOWN 0x01050000
+#define MR_DCMD_HIBERNATE_SHUTDOWN 0x01060000
+#define MR_ENABLE_DRIVE_SPINDOWN 0x01
+
+#define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100
+#define MR_DCMD_CTRL_EVENT_GET 0x01040300
+#define MR_DCMD_CTRL_EVENT_WAIT 0x01040500
+#define MR_DCMD_LD_GET_PROPERTIES 0x03030000
+
+#define MR_DCMD_CLUSTER 0x08000000
+#define MR_DCMD_CLUSTER_RESET_ALL 0x08010100
+#define MR_DCMD_CLUSTER_RESET_LD 0x08010200
+#define MR_DCMD_PD_LIST_QUERY 0x02010100
+
+/*
+ * MFI frame flags
+ */
+#define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000
+#define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001
+#define MFI_FRAME_SGL32 0x0000
+#define MFI_FRAME_SGL64 0x0002
+#define MFI_FRAME_SENSE32 0x0000
+#define MFI_FRAME_SENSE64 0x0004
+#define MFI_FRAME_DIR_NONE 0x0000
+#define MFI_FRAME_DIR_WRITE 0x0008
+#define MFI_FRAME_DIR_READ 0x0010
+#define MFI_FRAME_DIR_BOTH 0x0018
+#define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000
+
+/*
+ * MFI command completion codes
+ */
+enum MFI_STAT {
+ MFI_STAT_OK = 0x00,
+ MFI_STAT_INVALID_CMD = 0x01,
+ MFI_STAT_INVALID_DCMD = 0x02,
+ MFI_STAT_INVALID_PARAMETER = 0x03,
+ MFI_STAT_INVALID_SEQUENCE_NUMBER = 0x04,
+ MFI_STAT_ABORT_NOT_POSSIBLE = 0x05,
+ MFI_STAT_APP_HOST_CODE_NOT_FOUND = 0x06,
+ MFI_STAT_APP_IN_USE = 0x07,
+ MFI_STAT_APP_NOT_INITIALIZED = 0x08,
+ MFI_STAT_ARRAY_INDEX_INVALID = 0x09,
+ MFI_STAT_ARRAY_ROW_NOT_EMPTY = 0x0a,
+ MFI_STAT_CONFIG_RESOURCE_CONFLICT = 0x0b,
+ MFI_STAT_DEVICE_NOT_FOUND = 0x0c,
+ MFI_STAT_DRIVE_TOO_SMALL = 0x0d,
+ MFI_STAT_FLASH_ALLOC_FAIL = 0x0e,
+ MFI_STAT_FLASH_BUSY = 0x0f,
+ MFI_STAT_FLASH_ERROR = 0x10,
+ MFI_STAT_FLASH_IMAGE_BAD = 0x11,
+ MFI_STAT_FLASH_IMAGE_INCOMPLETE = 0x12,
+ MFI_STAT_FLASH_NOT_OPEN = 0x13,
+ MFI_STAT_FLASH_NOT_STARTED = 0x14,
+ MFI_STAT_FLUSH_FAILED = 0x15,
+ MFI_STAT_HOST_CODE_NOT_FOUNT = 0x16,
+ MFI_STAT_LD_CC_IN_PROGRESS = 0x17,
+ MFI_STAT_LD_INIT_IN_PROGRESS = 0x18,
+ MFI_STAT_LD_LBA_OUT_OF_RANGE = 0x19,
+ MFI_STAT_LD_MAX_CONFIGURED = 0x1a,
+ MFI_STAT_LD_NOT_OPTIMAL = 0x1b,
+ MFI_STAT_LD_RBLD_IN_PROGRESS = 0x1c,
+ MFI_STAT_LD_RECON_IN_PROGRESS = 0x1d,
+ MFI_STAT_LD_WRONG_RAID_LEVEL = 0x1e,
+ MFI_STAT_MAX_SPARES_EXCEEDED = 0x1f,
+ MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20,
+ MFI_STAT_MFC_HW_ERROR = 0x21,
+ MFI_STAT_NO_HW_PRESENT = 0x22,
+ MFI_STAT_NOT_FOUND = 0x23,
+ MFI_STAT_NOT_IN_ENCL = 0x24,
+ MFI_STAT_PD_CLEAR_IN_PROGRESS = 0x25,
+ MFI_STAT_PD_TYPE_WRONG = 0x26,
+ MFI_STAT_PR_DISABLED = 0x27,
+ MFI_STAT_ROW_INDEX_INVALID = 0x28,
+ MFI_STAT_SAS_CONFIG_INVALID_ACTION = 0x29,
+ MFI_STAT_SAS_CONFIG_INVALID_DATA = 0x2a,
+ MFI_STAT_SAS_CONFIG_INVALID_PAGE = 0x2b,
+ MFI_STAT_SAS_CONFIG_INVALID_TYPE = 0x2c,
+ MFI_STAT_SCSI_DONE_WITH_ERROR = 0x2d,
+ MFI_STAT_SCSI_IO_FAILED = 0x2e,
+ MFI_STAT_SCSI_RESERVATION_CONFLICT = 0x2f,
+ MFI_STAT_SHUTDOWN_FAILED = 0x30,
+ MFI_STAT_TIME_NOT_SET = 0x31,
+ MFI_STAT_WRONG_STATE = 0x32,
+ MFI_STAT_LD_OFFLINE = 0x33,
+ MFI_STAT_PEER_NOTIFICATION_REJECTED = 0x34,
+ MFI_STAT_PEER_NOTIFICATION_FAILED = 0x35,
+ MFI_STAT_RESERVATION_IN_PROGRESS = 0x36,
+ MFI_STAT_I2C_ERRORS_DETECTED = 0x37,
+ MFI_STAT_PCI_ERRORS_DETECTED = 0x38,
+
+ MFI_STAT_INVALID_STATUS = 0xFF
+};
+
+#define MEGASAS_FRAME_CMD_OFFSET 0x00
+#define MEGASAS_FRAME_SENSE_LEN_OFFSET 0x01
+#define MEGASAS_FRAME_CMD_STATUS_OFFSET 0x02
+#define MEGASAS_FRAME_SCSI_STATUS_OFFSET 0x03
+#define MEGASAS_FRAME_TARGET_ID_OFFSET 0x04
+#define MEGASAS_FRAME_LUN_ID_OFFSET 0x05
+#define MEGASAS_FRAME_CDB_LEN_OFFSET 0x06
+#define MEGASAS_FRAME_SGE_COUNT_OFFSET 0x07
+#define MEGASAS_FRAME_CONTEXT_OFFSET 0x08
+#define MEGASAS_FRAME_FLAGS_OFFSET 0x10
+#define MEGASAS_FRAME_XFER_LEN_OFFSET 0x14
+
+#define MEGASAS_INIT_NEW_PHYS_ADDR_LO_OFFSET 0x18
+#define MEGASAS_INIT_NEW_PHYS_ADDR_HI_OFFSET 0x1C
+
+#define MEGASAS_INITQ_REPLY_Q_LEN_OFFSET 0x04
+#define MEGASAS_INITQ_REPLY_Q_ADDR_LO_OFFSET 0x08
+#define MEGASAS_INITQ_REPLY_Q_ADDR_HI_OFFSET 0x0C
+#define MEGASAS_INITQ_PRODUCER_ADDR_LO_OFFSET 0x10
+#define MEGASAS_INITQ_PRODUCER_ADDR_HI_OFFSET 0x14
+#define MEGASAS_INITQ_CONSUMER_ADDR_LO_OFFSET 0x18
+#define MEGASAS_INITQ_CONSUMER_ADDR_HI_OFFSET 0x1C
+
+#define MEGASAS_DCMD_OPCODE_OFFSET 0x18
+
+#define MEGASAS_PTHRU_SENSE_ADDR_LO_OFFSET 0x18
+#define MEGASAS_PTHRU_SENSE_ADDR_HI_OFFSET 0x1C
+#define MEGASAS_PTHRU_CDB_OFFSET 0x20
+#define MEGASAS_PTHRU_SGL_OFFSET 0x30
+
+#define MEGASAS_IO_TIMEOUT_OFFSET 0x12
+#define MEGASAS_IO_LBA_COUNT_OFFSET 0x14
+#define MEGASAS_IO_SENSE_BUFF_ADDR_LO_OFFSET 0x18
+#define MEGASAS_IO_SENSE_BUFF_ADDR_HI_OFFSET 0x1C
+#define MEGASAS_IO_START_LBA_LO_OFFSET 0x20
+#define MEGASAS_IO_START_LBA_HI_OFFSET 0x24
+#define MEGASAS_IO_SGL_OFFSET 0x28
+
+const char *mfi_frame_desc[] = {
+ "MFI init", "LD Read", "LD Write", "LD SCSI", "PD SCSI",
+ "MFI Doorbell", "MFI Abort", "MFI SMP", "MFI Stop"};
+
+struct megasas_lun_t {
+ SCSIDevice *sdev;
+ BlockDriverAIOCB *aiocb;
+};
+
+struct megasas_cmd_t {
+ int index;
+
+ uint32_t context;
+ target_phys_addr_t pa;
+ uint16_t flags;
+ uint8_t frame_cmd;
+ uint8_t sge_count;
+ uint32_t xfer_len;
+ uint8_t *sense;
+ uint8_t sense_len;
+ SCSIRequest *req;
+ QEMUSGList sg;
+
+ struct megasas_state_t *state;
+ struct megasas_lun_t *lun;
+};
+
+typedef struct megasas_state_t {
+ PCIDevice dev;
+ int mmio_io_addr;
+ int io_addr;
+ int queue_addr;
+
+ int fw_state;
+ int fw_sge;
+ int fw_cmds;
+ int fw_luns;
+ int intr_mask;
+ int doorbell;
+ int busy;
+
+ target_phys_addr_t reply_queue_pa;
+ void *reply_queue;
+ int reply_queue_len;
+ int reply_queue_index;
+ target_phys_addr_t consumer_pa;
+ target_phys_addr_t producer_pa;
+
+ struct megasas_cmd_t frames[MEGASAS_MAX_FRAMES];
+
+ struct megasas_lun_t luns[MEGASAS_MAX_LUNS];
+
+ SCSIBus bus;
+} MPTState;
+
+#define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF
+
+#define MEGASAS_INTR_ENABLED(s) (((s)->intr_mask & MEGASAS_INTR_DISABLED_MASK
) != MEGASAS_INTR_DISABLED_MASK)
+
+#define megasas_frame_get(f,o) \
+ ldub_phys((f) + MEGASAS_FRAME_ ## o ## _OFFSET);
+
+#define megasas_frame_get_cmd_status(f) \
+ ldub_phys((f) + MEGASAS_FRAME_CMD_STATUS_OFFSET);
+
+#define megasas_frame_set_cmd_status(f,v) \
+ stb_phys((f) + MEGASAS_FRAME_CMD_STATUS_OFFSET, v);
+
+#define megasas_frame_get_sense_len(f) \
+ ldub_phys((f) + MEGASAS_FRAME_SENSE_LEN_OFFSET);
+
+#define megasas_frame_set_sense_len(f,v) \
+ stb_phys((f) + MEGASAS_FRAME_SENSE_LEN_OFFSET, v);
+
+#define megasas_frame_set_scsi_status(f,v) \
+ stb_phys((f) + MEGASAS_FRAME_SCSI_STATUS_OFFSET, v);
+
+#define megasas_frame_get_flags(f) \
+ lduw_phys((f) + MEGASAS_FRAME_FLAGS_OFFSET);
+
+#define megasas_frame_get_sgecount(f) \
+ lduw_phys((f) + MEGASAS_FRAME_SGE_COUNT_OFFSET);
+
+static void megasas_soft_reset(MPTState *s);
+
+static void megasas_map_sgl(struct megasas_cmd_t *cmd, int pa_offset)
+{
+ int i;
+ int is_sgl64 = (cmd->flags & MFI_FRAME_SGL64) ? 1 : 0;
+ int sgl_addr_size = is_sgl64 ? sizeof(uint64_t) : sizeof(uint32_t);
+
+ qemu_sglist_init(&cmd->sg, cmd->sge_count);
+ for (i = 0; i < cmd->sge_count; i++) {
+ target_phys_addr_t pa, iov_pa;
+
+ pa = cmd->pa + pa_offset;
+ if (is_sgl64)
+ iov_pa = ldq_phys(pa);
+ else
+ iov_pa = ldl_phys(pa);
+ qemu_sglist_add(&cmd->sg, iov_pa, ldl_phys(pa + sgl_addr_size));
+ pa_offset += sgl_addr_size + sizeof(uint32_t);
+ }
+}
+
+static void megasas_unmap_sgl(struct megasas_cmd_t *cmd)
+{
+ if (cmd->frame_cmd != MFI_CMD_LD_READ &&
+ cmd->frame_cmd != MFI_CMD_LD_WRITE) {
+ cmd->sge_count = 0;
+ return;
+ }
+
+ if (cmd->sge_count) {
+ qemu_sglist_destroy(&cmd->sg);
+ cmd->sge_count = 0;
+ }
+}
+
+static void megasas_map_sense(struct megasas_cmd_t *cmd)
+{
+ target_phys_addr_t pa_lo, pa_hi;
+
+ pa_lo = ldl_phys(cmd->pa + MEGASAS_PTHRU_SENSE_ADDR_LO_OFFSET);
+ pa_hi = ldl_phys(cmd->pa + MEGASAS_PTHRU_SENSE_ADDR_HI_OFFSET);
+ cmd->sense_len = megasas_frame_get_sense_len(cmd->pa);
+ cmd->sense = cpu_physical_memory_map((pa_hi << 32) | pa_lo,
+ (target_phys_addr_t *)&cmd->sense_len,
1);
+}
+
+static void megasas_unmap_sense(struct megasas_cmd_t *cmd, int sense_len)
+{
+ if (cmd->sense) {
+ cpu_physical_memory_unmap(cmd->sense, cmd->sense_len, 1, sense_len);
+ megasas_frame_set_sense_len(cmd->pa, sense_len);
+ }
+}
+
+/*
+ * Frame handling
+ */
+
+static inline int megasas_next_index(MPTState *s, int index)
+{
+ index++;
+ if (index == s->fw_cmds)
+ index = 0;
+ return index;
+}
+
+static inline struct megasas_cmd_t *megasas_next_frame(MPTState *s)
+{
+ int num = 0, tail, index;
+
+ tail = index = s->reply_queue_index;
+
+ while (num < MEGASAS_MAX_FRAMES) {
+ if (!s->frames[index].pa)
+ break;
+ index = megasas_next_index(s, index);
+ num++;
+ }
+ if (num == MEGASAS_MAX_FRAMES)
+ return NULL;
+ return &s->frames[index];
+}
+
+static struct megasas_cmd_t *
+megasas_enqueue_frame(MPTState *s, target_phys_addr_t frame, uint8_t fcmd,
+ uint32_t context)
+{
+ struct megasas_cmd_t *cmd = megasas_next_frame(s);
+
+ /* All frames busy */
+ if (!cmd)
+ return NULL;
+
+ /* Classical 'this shouldn't happen' check */
+ if (cmd->pa)
+ BADF("Frame %d still active\n", cmd->index);
+
+ cmd->pa = frame;
+ cmd->frame_cmd = fcmd;
+ cmd->context = context;
+ s->busy++;
+
+#ifdef DEBUG_MEGASAS_QUEUE
+ DPRINTF("Enqueue frame context %d to reply queue, tail %d busy %d\n",
+ context, s->reply_queue_index, s->busy);
+#endif
+
+ return cmd;
+}
+
+static void megasas_dequeue_frame(MPTState *s, int context)
+{
+ int tail;
+
+ /* Put command on the reply queue */
+ tail = s->reply_queue_index;
+ stl_phys(s->reply_queue_pa + tail * sizeof(uint32_t), context);
+ s->busy--;
+
+ if (!MEGASAS_INTR_ENABLED(s)) {
+ DPRINTF("Complete frame context %x\n", context);
+ return;
+ }
+
+ s->reply_queue_index = megasas_next_index(s, tail);
+#ifdef DEBUG_MEGASAS_QUEUE
+ DPRINTF("Complete frame context %x tail %d index %d busy %d doorbell %d\n",
+ context, tail, s->reply_queue_index, s->busy, s->doorbell);
+#endif
+
+ /* Update reply queue pointer and notify HBA */
+ s->doorbell++;
+ if (s->doorbell == 1 || s->busy == 0) {
+ stl_phys(s->producer_pa, s->reply_queue_index);
+ qemu_irq_raise(s->dev.irq[0]);
+ }
+}
+
+static int megasas_finish_command(MPTState *s, struct megasas_cmd_t *cmd)
+{
+ int context;
+
+ if (!cmd) {
+#ifdef DEBUG_MEGASAS_QUEUE
+ DPRINTF("No frame to complete\n");
+#endif
+ return -1;
+ }
+ context = cmd->context;
+ cmd->xfer_len = 0;
+ cmd->context = -1;
+ cmd->pa = 0;
+ cmd->lun = NULL;
+
+ return context;
+}
+
+static void megasas_abort_command(struct megasas_cmd_t *cmd)
+{
+ if (cmd->lun && cmd->lun->aiocb) {
+ bdrv_aio_cancel(cmd->lun->aiocb);
+ cmd->lun->aiocb = NULL;
+ }
+}
+
+static int megasas_init_firmware(MPTState *s, target_phys_addr_t frame_addr)
+{
+ target_phys_addr_t iq_pa, iq_pl, pa_hi, pa_lo;
+
+ iq_pl = ldl_phys(frame_addr + MEGASAS_FRAME_XFER_LEN_OFFSET);
+ pa_lo = frame_addr + MEGASAS_INIT_NEW_PHYS_ADDR_LO_OFFSET;
+ pa_hi = frame_addr + MEGASAS_INIT_NEW_PHYS_ADDR_HI_OFFSET;
+ iq_pa = ((uint64_t)ldl_phys(pa_hi) << 32) | ldl_phys(pa_lo);
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI init firmware: xfer len %d pa %lx\n", (int)iq_pl,
+ (unsigned long)iq_pa);
+#endif
+ s->reply_queue_len = ldl_phys(iq_pa + MEGASAS_INITQ_REPLY_Q_LEN_OFFSET);
+ pa_lo = iq_pa + MEGASAS_INITQ_REPLY_Q_ADDR_LO_OFFSET;
+ pa_hi = iq_pa + MEGASAS_INITQ_REPLY_Q_ADDR_HI_OFFSET;
+ s->reply_queue_pa = ((uint64_t)ldl_phys(pa_hi) << 32) | ldl_phys(pa_lo);
+ pa_lo = iq_pa + MEGASAS_INITQ_CONSUMER_ADDR_LO_OFFSET;
+ pa_hi = iq_pa + MEGASAS_INITQ_CONSUMER_ADDR_HI_OFFSET;
+ s->consumer_pa = ((uint64_t)ldl_phys(pa_hi) << 32) | ldl_phys(pa_lo);
+ pa_lo = iq_pa + MEGASAS_INITQ_PRODUCER_ADDR_LO_OFFSET;
+ pa_hi = iq_pa + MEGASAS_INITQ_PRODUCER_ADDR_HI_OFFSET;
+ s->producer_pa = ((uint64_t)ldl_phys(pa_hi) << 32) | ldl_phys(pa_lo);
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI init firmware: queue at %lx len %d head %lx tail %lx\n",
+ (unsigned long)s->reply_queue_pa, s->reply_queue_len,
+ (unsigned long)s->producer_pa, (unsigned long)s->consumer_pa);
+#endif
+ s->reply_queue_index = ldl_phys(s->producer_pa);
+ s->fw_state = MFI_STATE_OPERATIONAL;
+ return 0;
+}
+
+static int megasas_handle_doorcmd(MPTState *s, target_phys_addr_t frame_addr)
+{
+ int opcode;
+ uint8_t sg_count;
+ int retval = 0;
+
+ opcode = ldl_phys(frame_addr + MEGASAS_DCMD_OPCODE_OFFSET);
+ sg_count = ldub_phys(frame_addr + MEGASAS_FRAME_SGE_COUNT_OFFSET);
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI DCMD opcode %x sg_count %d\n", opcode, sg_count);
+#endif
+ switch (opcode) {
+ case MR_DCMD_PD_LIST_QUERY:
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI DCMD query physical devices\n");
+#endif
+ retval = MFI_STAT_INVALID_DCMD;
+ break;
+ case MR_DCMD_CTRL_CACHE_FLUSH:
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI DCMD Cache flush\n");
+#endif
+ qemu_aio_flush();
+ retval = MFI_STAT_OK;
+ break;
+ case MR_DCMD_CTRL_SHUTDOWN:
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI DCMD Controller shutdown\n");
+#endif
+ s->fw_state = MFI_STATE_READY;
+ retval = MFI_STAT_OK;
+ break;
+ default:
+ retval = MFI_STAT_INVALID_DCMD;
+ break;
+ }
+ return retval;
+}
+
+#ifdef DEBUG_MEGASAS_MFI
+static void megasas_print_cdb(uint8_t *cdb, uint8_t cdb_len)
+{
+ switch (cdb_len) {
+ case 6:
+ DPRINTF("cdb %02x %02x %02x %02x %02x %02x\n",
+ cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5]);
+ break;
+ case 10:
+ DPRINTF("cdb %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x\n",
+ cdb[0], cdb[1], cdb[2], cdb[3], cdb[4],
+ cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]);
+ break;
+ case 12:
+ DPRINTF("cdb %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x\n",
+ cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5],
+ cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]);
+ break;
+ case 16:
+ DPRINTF("cdb %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ cdb[0], cdb[1], cdb[2], cdb[3],
+ cdb[4], cdb[5], cdb[6], cdb[7],
+ cdb[8], cdb[9], cdb[10], cdb[11],
+ cdb[12], cdb[13], cdb[14], cdb[15]);
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+static int megasas_handle_scsi(MPTState *s, struct megasas_cmd_t *cmd)
+{
+ uint8_t target, lun, cdb_len, sense_len;
+ uint8_t cdb[16];
+
+ target = megasas_frame_get(cmd->pa, TARGET_ID);
+ lun = megasas_frame_get(cmd->pa, LUN_ID);
+ cmd->xfer_len = megasas_frame_get(cmd->pa, XFER_LEN);
+ cmd->flags = megasas_frame_get_flags(cmd->pa);
+ cmd->sge_count = megasas_frame_get_sgecount(cmd->pa);
+ cdb_len = megasas_frame_get(cmd->pa, CDB_LEN);
+
+ cpu_physical_memory_read(cmd->pa + 0x20, (uint8_t *)cdb, 16);
+
+ if (target < s->fw_luns)
+ cmd->lun = &s->luns[target];
+
+ if (cmd->lun)
+ cmd->lun->sdev = s->bus.devs[target];
+
+ DPRINTF("%s dev %x lun %x sdev %p xfer %d\n",
+ mfi_frame_desc[cmd->frame_cmd],
+ target, lun, cmd->lun?cmd->lun->sdev:NULL, cmd->xfer_len);
+#ifdef DEBUG_MEGASAS_MFI
+ megasas_print_cdb(cdb, cdb_len);
+#endif
+
+ if (!cmd->lun || !cmd->lun->sdev) {
+ DPRINTF("%s dev %x/%x target not present\n",
+ mfi_frame_desc[cmd->frame_cmd], target, lun);
+ return MFI_STAT_DEVICE_NOT_FOUND;
+ }
+
+ megasas_map_sense(cmd);
+
+ if (cdb_len > 16) {
+ DPRINTF("%s dev %x/%x invalid cdb len %d\n",
+ mfi_frame_desc[cmd->frame_cmd],
+ target, lun, cdb_len);
+ sense_len = scsi_build_sense(SENSE_CODE(INVALID_OPCODE), cmd->sense,
+ cmd->sense_len, 0);
+ megasas_unmap_sense(cmd, sense_len);
+ megasas_frame_set_scsi_status(cmd->pa, CHECK_CONDITION);
+ return MFI_STAT_SCSI_DONE_WITH_ERROR;
+ }
+
+ cmd->req = scsi_req_get(cmd->lun->sdev, cmd->context, lun);
+ cmd->req->hba_private = cmd;
+ scsi_req_parse(cmd->req, cdb);
+ if (cmd->xfer_len != cmd->req->cmd.xfer) {
+ DPRINTF("xfer length mismatch, frame %u cdb %u\n",
+ cmd->xfer_len, (unsigned)cmd->req->cmd.xfer);
+ cmd->xfer_len = cmd->req->cmd.xfer;
+ }
+
+ megasas_map_sgl(cmd, MEGASAS_PTHRU_SGL_OFFSET);
+ scsi_req_sgl(cmd->req, &cmd->sg);
+
+ return MFI_STAT_INVALID_STATUS;
+}
+
+static int megasas_handle_io(MPTState *s, struct megasas_cmd_t *cmd)
+{
+ uint32_t lba_count, lba_start_hi, lba_start_lo;
+ uint64_t lba_start;
+ uint8_t cdb_len, sense_len;
+ int target, lun, write = cmd->frame_cmd == MFI_CMD_LD_WRITE ? 1 : 0;
+
+ target = megasas_frame_get(cmd->pa, TARGET_ID);
+ lun = megasas_frame_get(cmd->pa, LUN_ID);
+ cmd->flags = megasas_frame_get_flags(cmd->pa);
+ cmd->sge_count = megasas_frame_get_sgecount(cmd->pa);
+
+ lba_count = ldl_phys(cmd->pa + MEGASAS_IO_LBA_COUNT_OFFSET);
+ lba_start_lo = ldl_phys(cmd->pa + MEGASAS_IO_START_LBA_LO_OFFSET);
+ lba_start_hi = ldl_phys(cmd->pa + MEGASAS_IO_START_LBA_HI_OFFSET);
+ lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
+ cdb_len = megasas_frame_get(cmd->pa, CDB_LEN);
+
+ if (target < s->fw_luns)
+ cmd->lun = &s->luns[target];
+ if (cmd->lun)
+ cmd->lun->sdev = s->bus.devs[target];
+
+ DPRINTF("%s dev %x lun %x lba %lx count %lx\n",
+ mfi_frame_desc[cmd->frame_cmd], target, lun,
+ (unsigned long)lba_start, (unsigned long)lba_count);
+
+ if (!cmd->lun || !cmd->lun->sdev) {
+ DPRINTF("%s dev %x/%x LUN not present\n",
+ mfi_frame_desc[cmd->frame_cmd], target, lun);
+ return MFI_STAT_DEVICE_NOT_FOUND;
+ }
+
+ megasas_map_sense(cmd);
+
+ if (cdb_len > 16) {
+ DPRINTF("%s dev %x/%x invalid cdb len %d\n",
+ mfi_frame_desc[cmd->frame_cmd],
+ target, lun, cdb_len);
+ sense_len = scsi_build_sense(SENSE_CODE(INVALID_OPCODE), cmd->sense,
+ cmd->sense_len, 0);
+ megasas_unmap_sense(cmd, sense_len);
+ megasas_frame_set_scsi_status(cmd->pa, CHECK_CONDITION);
+ return MFI_STAT_SCSI_DONE_WITH_ERROR;
+ }
+
+ cmd->req = scsi_req_get(cmd->lun->sdev, cmd->context, lun);
+ cmd->req->hba_private = cmd;
+ megasas_map_sgl(cmd, MEGASAS_IO_SGL_OFFSET);
+
+ scsi_req_setup(cmd->req, write, lba_start, lba_count);
+ scsi_req_sgl(cmd->req, &cmd->sg);
+
+ return MFI_STAT_INVALID_STATUS;
+}
+
+static void megasas_command_complete(SCSIRequest *req)
+{
+ struct megasas_cmd_t *cmd;
+ uint8_t cmd_status;
+ int context;
+
+ cmd = req->hba_private;
+ if (!cmd) {
+ /*
+ * Bad. A command has been completed but we couldn't find it.
+ * Only safe way out of here is to terminate everything and
+ * hope the HBA recovers.
+ */
+ BADF("SCSI request context %d not found", req->tag);
+ return;
+ }
+
+ DPRINTF("%s finished with status %x len %u\n",
+ mfi_frame_desc[cmd->frame_cmd], cmd->req->status,
+ (unsigned)cmd->req->xferlen);
+ if (cmd->req->status == CHECK_CONDITION) {
+ cmd->sense_len = scsi_build_sense(cmd->lun->sdev->sense, cmd->sense,
+ cmd->sense_len, 0);
+ cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR;
+ scsi_dev_clear_sense(cmd->lun->sdev);
+ } else {
+ cmd_status = MFI_STAT_OK;
+ }
+
+ megasas_unmap_sgl(cmd);
+ megasas_frame_set_scsi_status(cmd->pa, cmd->req->status);
+ megasas_frame_set_cmd_status(cmd->pa, cmd_status);
+ scsi_req_put(cmd->req);
+ cmd->req = NULL;
+ context = megasas_finish_command(cmd->state, cmd);
+ megasas_dequeue_frame(cmd->state, context);
+}
+
+static void megasas_handle_frame(MPTState *s, target_phys_addr_t frame_addr,
+ uint32_t frame_count)
+{
+ uint8_t frame_cmd;
+ uint8_t frame_status = MFI_STAT_INVALID_CMD;
+ uint32_t frame_context;
+ struct megasas_cmd_t *cmd;
+
+ frame_cmd = ldub_phys(frame_addr + MEGASAS_FRAME_CMD_OFFSET);
+ frame_status = ldub_phys(frame_addr + MEGASAS_FRAME_CMD_STATUS_OFFSET);
+ frame_context = ldl_phys(frame_addr + MEGASAS_FRAME_CONTEXT_OFFSET);
+
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("MFI cmd %x context %x count %d status %x\n",
+ frame_cmd, frame_context, frame_count, frame_status);
+#endif
+ if (s->fw_state != MFI_STATE_OPERATIONAL) {
+ /* Firmware not initialized, only polled commands */
+ if (frame_cmd != MFI_CMD_INIT) {
+ frame_status = MFI_STAT_APP_NOT_INITIALIZED;
+ } else {
+ megasas_init_firmware(s, frame_addr);
+ frame_status = MFI_STAT_OK;
+ }
+ megasas_frame_set_cmd_status(frame_addr, frame_status);
+ return;
+ }
+
+ cmd = megasas_enqueue_frame(s, frame_addr, frame_cmd, frame_context);
+ if (!cmd) {
+ /* reply queue full */
+ megasas_frame_set_scsi_status(frame_addr, BUSY);
+ frame_status = MFI_STAT_SCSI_DONE_WITH_ERROR;
+ goto frame_done;
+ }
+ switch (cmd->frame_cmd) {
+ case MFI_CMD_DCMD:
+ frame_status = megasas_handle_doorcmd(s, frame_addr);
+ break;
+ case MFI_CMD_PD_SCSI_IO:
+ case MFI_CMD_LD_SCSI_IO:
+ frame_status = megasas_handle_scsi(s, cmd);
+ break;
+ case MFI_CMD_LD_READ:
+ case MFI_CMD_LD_WRITE:
+ frame_status = megasas_handle_io(s, cmd);
+ break;
+ default:
+ DPRINTF("Unhandled MFI cmd %x\n", frame_cmd);
+ break;
+ }
+ frame_done:
+ if (frame_status != MFI_STAT_INVALID_STATUS) {
+ megasas_frame_set_cmd_status(frame_addr, frame_status);
+ megasas_finish_command(s, cmd);
+ megasas_dequeue_frame(s, frame_context);
+ }
+}
+
+static uint32_t megasas_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readb mmio 0x%lx\n", (unsigned long)addr);
+#endif
+ return 0;
+}
+
+static uint32_t megasas_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readw mmio 0x%lx\n", (unsigned long)addr);
+#endif
+ return 0;
+}
+
+static uint32_t megasas_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ MPTState *s = opaque;
+
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readl mmio 0x%lx\n", (unsigned long)addr);
+#endif
+ switch (addr) {
+ case MEGASAS_INBOUND_DOORBELL:
+ return 0;
+ case MEGASAS_OUTBOUND_MSG_0:
+ case MEGASAS_OUTBOUND_SCRATCH_PAD:
+ return (s->fw_state) << 28 | (s->fw_sge << 16) | (s->fw_cmds &
0xFFFF);
+ case MEGASAS_OUTBOUND_INTR_STATUS:
+ if (MEGASAS_INTR_ENABLED(s) && s->doorbell)
+ return MFI_REPLY_1078_MESSAGE_INTERRUPT | s->doorbell;
+ break;
+ case MEGASAS_OUTBOUND_INTR_MASK:
+ return s->intr_mask;
+ case MEGASAS_OUTBOUND_DOORBELL_CLEAR:
+ return s->doorbell;
+ default:
+ BADF("readb 0x%lx\n", (unsigned long)addr);
+ break;
+ }
+ return 0;
+}
+
+static void megasas_mmio_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writeb mmio %lx: %x\n", (unsigned long)addr, val);
+#endif
+}
+
+static void megasas_mmio_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writew mmio %lx: %x\n", (unsigned long)addr, val);
+#endif
+}
+
+static void megasas_mmio_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MPTState *s = opaque;
+ target_phys_addr_t frame_addr;
+ uint32_t frame_count;
+ int i;
+
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writel mmio %lx: %x\n", (unsigned long)addr, val);
+#endif
+
+ switch (addr) {
+ case MEGASAS_INBOUND_DOORBELL:
+ if (val & MFI_INIT_ABORT) {
+ /* Abort all pending cmds */
+ for (i = 0; i <= s->fw_cmds; i++)
+ megasas_abort_command(&s->frames[i]);
+ }
+ if (val & MFI_INIT_READY) {
+ /* move to FW READY */
+ megasas_soft_reset(s);
+ }
+ if (val & MFI_INIT_MFIMODE) {
+ /* discard MFIs */
+ }
+ break;
+ case MEGASAS_OUTBOUND_INTR_MASK:
+ s->intr_mask = val;
+ if (!MEGASAS_INTR_ENABLED(s)) {
+ DPRINTF("Disable interrupts\n");
+ qemu_irq_lower(s->dev.irq[0]);
+ } else {
+ DPRINTF("Enable interrupts\n");
+ }
+ break;
+ case MEGASAS_OUTBOUND_DOORBELL_CLEAR:
+ s->doorbell = 0;
+ qemu_irq_lower(s->dev.irq[0]);
+ break;
+ case MEGASAS_INBOUND_HIGH_QUEUE_PORT:
+ if ((s->reply_queue_pa >> 32) == 0)
+ s->reply_queue_pa |= ((uint64_t)ldl_phys(val) << 32);
+ break;
+ case MEGASAS_INBOUND_LOW_QUEUE_PORT:
+ if ((s->reply_queue_pa & 0xffffffff) == 0)
+ s->reply_queue_pa |= ((uint64_t)ldl_phys(val));
+ break;
+ case MEGASAS_INBOUND_QUEUE_PORT:
+ /* Received MFI frames; up to 8 contiguous frames */
+ frame_addr = (val & ~0xF);
+ frame_count = (val >> 1) & 0x7;
+#ifdef DEBUG_MEGASAS_MFI
+ DPRINTF("Received frame addr %lx count %d\n",
+ (unsigned long)frame_addr, frame_count);
+#endif
+ megasas_handle_frame(s, frame_addr, frame_count);
+ break;
+ default:
+ BADF("writel 0x%lx: %x\n", (unsigned long)addr, val);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const megasas_mmio_readfn[3] = {
+ megasas_mmio_readb,
+ megasas_mmio_readw,
+ megasas_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const megasas_mmio_writefn[3] = {
+ megasas_mmio_writeb,
+ megasas_mmio_writew,
+ megasas_mmio_writel,
+};
+
+static uint32_t megasas_io_readb(void *opaque, uint32_t addr)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readb reg 0x%lx\n", (unsigned long)addr);
+#endif
+ return 0;
+}
+
+static uint32_t megasas_io_readw(void *opaque, uint32_t addr)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readw reg 0x%lx\n", (unsigned long)addr);
+#endif
+ return 0;
+}
+
+static uint32_t megasas_io_readl(void *opaque, uint32_t addr)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readl reg 0x%lx\n", (unsigned long)addr);
+#endif
+ return megasas_mmio_readl(opaque, addr & 0xff);
+}
+
+static void megasas_io_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writeb reg 0x%lx: %x\n", (unsigned long)addr, val);
+#endif
+}
+
+static void megasas_io_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writew reg 0x%lx: %x\n", (unsigned long)addr, val);
+#endif
+}
+
+static void megasas_io_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writel reg 0x%lx: %x\n", (unsigned long)addr, val);
+#endif
+ megasas_mmio_writel(opaque, addr & 0xff, val);
+}
+
+static CPUReadMemoryFunc * const megasas_io_readfn[3] = {
+ megasas_mmio_readb,
+ megasas_mmio_readw,
+ megasas_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const megasas_io_writefn[3] = {
+ megasas_mmio_writeb,
+ megasas_mmio_writew,
+ megasas_mmio_writel,
+};
+
+static uint32_t megasas_queue_readl(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("readl queue 0x%lx\n", (unsigned long)addr);
+#endif
+ return 0;
+}
+
+static void megasas_queue_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+#ifdef DEBUG_MEGASAS_REG
+ DPRINTF("writel queue %lx: %x\n", (unsigned long)addr, val);
+#endif
+}
+
+static CPUReadMemoryFunc * const megasas_queue_readfn[3] = {
+ NULL,
+ NULL,
+ megasas_queue_readl,
+};
+
+static CPUWriteMemoryFunc * const megasas_queue_writefn[3] = {
+ NULL,
+ NULL,
+ megasas_queue_writel,
+};
+
+static void megasas_soft_reset(MPTState *s)
+{
+ DPRINTF("Reset\n");
+
+ s->reply_queue_len = 0;
+ s->reply_queue_pa = 0;
+ s->consumer_pa = 0;
+ s->producer_pa = 0;
+ s->fw_state = MFI_STATE_READY;
+ s->doorbell = 0;
+ s->intr_mask = MEGASAS_INTR_DISABLED_MASK;
+}
+
+static void megasas_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
+
+ DPRINTF("Mapping MMIO region %d at %08x\n", region_num, addr);
+ cpu_register_physical_memory(addr, size, s->mmio_io_addr);
+}
+
+static void megasas_io_mapfunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
+
+ DPRINTF("Mapping IO region %d at %08x\n", region_num, addr);
+
+ register_ioport_write(addr, size, 1, megasas_io_writeb, s);
+ register_ioport_write(addr, size, 2, megasas_io_writew, s);
+ register_ioport_write(addr, size, 4, megasas_io_writel, s);
+ register_ioport_read(addr, size, 1, megasas_io_readb, s);
+ register_ioport_read(addr, size, 2, megasas_io_readw, s);
+ register_ioport_read(addr, size, 4, megasas_io_readl, s);
+}
+
+static void megasas_queue_mapfunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
+
+ DPRINTF("Mapping QUEUE region %d at %08x\n", region_num, addr);
+ cpu_register_physical_memory(addr, size, s->queue_addr);
+}
+
+static void megasas_scsi_save(QEMUFile *f, void *opaque)
+{
+ MPTState *s = opaque;
+
+ pci_device_save(&s->dev, f);
+
+ qemu_put_sbe32s(f, &s->fw_state);
+ qemu_put_sbe32s(f, &s->intr_mask);
+ qemu_put_sbe32s(f, &s->doorbell);
+ qemu_put_be64s(f, &s->reply_queue_pa);
+ qemu_put_be64s(f, &s->consumer_pa);
+ qemu_put_be64s(f, &s->producer_pa);
+}
+
+static int megasas_scsi_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MPTState *s = opaque;
+ int ret;
+
+ if (version_id > 0)
+ return -EINVAL;
+
+ if ((ret = pci_device_load(&s->dev, f)) < 0)
+ return ret;
+
+ qemu_get_sbe32s(f, &s->fw_state);
+ qemu_get_sbe32s(f, &s->intr_mask);
+ qemu_get_sbe32s(f, &s->doorbell);
+ qemu_get_be64s(f, &s->reply_queue_pa);
+ qemu_get_be64s(f, &s->consumer_pa);
+ qemu_get_be64s(f, &s->producer_pa);
+
+ return 0;
+}
+
+static int megasas_scsi_uninit(PCIDevice *d)
+{
+ MPTState *s = DO_UPCAST(MPTState, dev, d);
+
+ cpu_unregister_io_memory(s->mmio_io_addr);
+
+ return 0;
+}
+
+static int megasas_scsi_init(PCIDevice *dev)
+{
+ MPTState *s = DO_UPCAST(MPTState, dev, dev);
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = s->dev.config;
+
+ /* PCI Vendor ID (word) */
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_LSI_LOGIC);
+ /* PCI device ID (word) */
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_LSI_SAS1078);
+ /* PCI subsystem ID */
+ pci_set_word(&pci_conf[PCI_SUBSYSTEM_VENDOR_ID], 0x1000);
+ pci_set_word(&pci_conf[PCI_SUBDEVICE_ID], 0x1013);
+ /* PCI base class code */
+ pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_RAID);
+
+ /* PCI latency timer = 0 */
+ pci_conf[0x0d] = 0;
+ /* Interrupt pin 1 */
+ pci_conf[0x3d] = 0x01;
+
+ s->mmio_io_addr = cpu_register_io_memory(megasas_mmio_readfn,
+ megasas_mmio_writefn, s);
+ s->io_addr = cpu_register_io_memory(megasas_io_readfn,
+ megasas_io_writefn, s);
+ s->queue_addr = cpu_register_io_memory(megasas_queue_readfn,
+ megasas_queue_writefn, s);
+ pci_register_bar((struct PCIDevice *)s, 0, 0x40000,
+ PCI_ADDRESS_SPACE_MEM, megasas_mmio_mapfunc);
+ pci_register_bar((struct PCIDevice *)s, 2, 256,
+ PCI_ADDRESS_SPACE_IO, megasas_io_mapfunc);
+ pci_register_bar((struct PCIDevice *)s, 3, 0x40000,
+ PCI_ADDRESS_SPACE_MEM, megasas_queue_mapfunc);
+ s->fw_sge = MEGASAS_MAX_SGE;
+ s->fw_cmds = MEGASAS_MAX_FRAMES;
+ s->fw_luns = (MEGASAS_MAX_LUNS > MAX_SCSI_DEVS) ?
+ MAX_SCSI_DEVS : MEGASAS_MAX_LUNS;
+ s->producer_pa = 0;
+ s->consumer_pa = 0;
+ for (i = 0; i < s->fw_cmds; i++) {
+ s->frames[i].index = i;
+ s->frames[i].context = -1;
+ s->frames[i].pa = 0;
+ s->frames[i].state = s;
+ s->frames[i].sge_count = 0;
+ s->frames[i].xfer_len = 0;
+ }
+
+ megasas_soft_reset(s);
+
+ scsi_bus_new(&s->bus, &dev->qdev, 1, s->fw_luns, megasas_command_complete);
+ scsi_bus_legacy_handle_cmdline(&s->bus);
+ register_savevm("megasas", -1, 0, megasas_scsi_save, megasas_scsi_load, s);
+ return 0;
+}
+
+static PCIDeviceInfo megasas_info = {
+ .qdev.name = "LSI MegaRAID SAS 1078",
+ .qdev.alias = "megasas",
+ .qdev.size = sizeof(MPTState),
+ .init = megasas_scsi_init,
+ .exit = megasas_scsi_uninit,
+};
+
+static void megaraid1078_register_devices(void)
+{
+ pci_qdev_register(&megasas_info);
+}
+
+device_init(megaraid1078_register_devices);
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 63379c2..84419ba 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -15,6 +15,7 @@
#define PCI_CLASS_STORAGE_SCSI 0x0100
#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_RAID 0x0104
#define PCI_CLASS_STORAGE_OTHER 0x0180
#define PCI_CLASS_NETWORK_ETHERNET 0x0200
@@ -46,6 +47,7 @@
#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
#define PCI_DEVICE_ID_LSI_53C895A 0x0012
+#define PCI_DEVICE_ID_LSI_SAS1078 0x0060
#define PCI_VENDOR_ID_DEC 0x1011
#define PCI_DEVICE_ID_DEC_21154 0x0026
- [Qemu-devel] [PATCH] megasas: LSI MegaRAID SAS HBA emulation,
Hannes Reinecke <=