qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] megasas: LSI MegaRAID SAS HBA emulation
@ 2009-11-12 11:48 Hannes Reinecke
  2009-11-17 10:22 ` [Qemu-devel] " Gerd Hoffmann
  0 siblings, 1 reply; 4+ messages in thread
From: Hannes Reinecke @ 2009-11-12 11:48 UTC (permalink / raw)
  To: Gerd Hoffman; +Cc: qemu-devel


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 <hare@suse.de>
--
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

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [Qemu-devel] Re: [PATCH] megasas: LSI MegaRAID SAS HBA emulation
  2009-11-12 11:48 [Qemu-devel] [PATCH] megasas: LSI MegaRAID SAS HBA emulation Hannes Reinecke
@ 2009-11-17 10:22 ` Gerd Hoffmann
  2009-11-17 10:51   ` Hannes Reinecke
  0 siblings, 1 reply; 4+ messages in thread
From: Gerd Hoffmann @ 2009-11-17 10:22 UTC (permalink / raw)
  To: Hannes Reinecke; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 427 bytes --]

On 11/12/09 12:48, Hannes Reinecke wrote:
>
> 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<hare@suse.de>

Added, will be in scsi.v8.  Needs a patch after rebasing due to pci 
changes (attached fyi).

cheers,
   Gerd

[-- Attachment #2: 0001-megasas-adapt-to-pci-changes.patch --]
[-- Type: text/plain, Size: 2297 bytes --]

>From ae3c2b55f84f9b12551369b21fd7b89d84ddfff6 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Mon, 16 Nov 2009 23:12:53 +0100
Subject: [PATCH] megasas: adapt to pci changes.

---
 hw/megasas.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/hw/megasas.c b/hw/megasas.c
index 723c586..3d15cc6 100644
--- a/hw/megasas.c
+++ b/hw/megasas.c
@@ -1034,7 +1034,7 @@ static void megasas_soft_reset(MPTState *s)
 }
 
 static void megasas_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
-				 uint32_t addr, uint32_t size, int type)
+				 pcibus_t addr, pcibus_t size, int type)
 {
     MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
 
@@ -1043,7 +1043,7 @@ static void megasas_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
 }
 
 static void megasas_io_mapfunc(PCIDevice *pci_dev, int region_num,
-				 uint32_t addr, uint32_t size, int type)
+                               pcibus_t addr, pcibus_t size, int type)
 {
     MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
 
@@ -1058,7 +1058,7 @@ static void megasas_io_mapfunc(PCIDevice *pci_dev, int region_num,
 }
 
 static void megasas_queue_mapfunc(PCIDevice *pci_dev, int region_num,
-				  uint32_t addr, uint32_t size, int type)
+                                  pcibus_t addr, pcibus_t size, int type)
 {
     MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
 
@@ -1140,11 +1140,11 @@ static int megasas_scsi_init(PCIDevice *dev)
     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_BASE_ADDRESS_SPACE_MEMORY, megasas_mmio_mapfunc);
     pci_register_bar((struct PCIDevice *)s, 2, 256,
-                           PCI_ADDRESS_SPACE_IO, megasas_io_mapfunc);
+                     PCI_BASE_ADDRESS_SPACE_IO, megasas_io_mapfunc);
     pci_register_bar((struct PCIDevice *)s, 3, 0x40000,
-                           PCI_ADDRESS_SPACE_MEM, megasas_queue_mapfunc);
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, megasas_queue_mapfunc);
     s->fw_sge = MEGASAS_MAX_SGE;
     s->fw_cmds = MEGASAS_MAX_FRAMES;
     s->fw_luns = (MEGASAS_MAX_LUNS > MAX_SCSI_DEVS) ?
-- 
1.6.2.5


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [Qemu-devel] Re: [PATCH] megasas: LSI MegaRAID SAS HBA emulation
  2009-11-17 10:22 ` [Qemu-devel] " Gerd Hoffmann
@ 2009-11-17 10:51   ` Hannes Reinecke
  2009-11-17 16:39     ` Gerd Hoffmann
  0 siblings, 1 reply; 4+ messages in thread
From: Hannes Reinecke @ 2009-11-17 10:51 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

Gerd Hoffmann wrote:
> On 11/12/09 12:48, Hannes Reinecke wrote:
>>
>> 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<hare@suse.de>
> 
> Added, will be in scsi.v8.  Needs a patch after rebasing due to pci
> changes (attached fyi).
> 
Grand. Meanwhile I've dug up some more register definitions,
so I'll doing some more updates to the driver.

Thanks for including it.

Meanwhile I've stumbled across another issue:
The megasas HBA insists on returning some data like inquiry
or VPD pages during init/configuration.

I can't really use the normal command completion here,
as eg on megasas configuration command requires me
to issue several requests to the underlying device.

So what to do?
Easiest would it to have a non-completion interface,
like req_buf() but without the callback bit.
But really don't know if that's best.

We could also attach the inquiry data and vpd 0x83
as a property to the device; might be easier as
we don't have to add another interface.
But the properties stuff is a bit beyond me currently.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke		      zSeries & Storage
hare@suse.de			      +49 911 74053 688
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: Markus Rex, HRB 16746 (AG Nürnberg)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Qemu-devel] Re: [PATCH] megasas: LSI MegaRAID SAS HBA emulation
  2009-11-17 10:51   ` Hannes Reinecke
@ 2009-11-17 16:39     ` Gerd Hoffmann
  0 siblings, 0 replies; 4+ messages in thread
From: Gerd Hoffmann @ 2009-11-17 16:39 UTC (permalink / raw)
  To: Hannes Reinecke; +Cc: qemu-devel

On 11/17/09 11:51, Hannes Reinecke wrote:
> Grand. Meanwhile I've dug up some more register definitions,
> so I'll doing some more updates to the driver.

Feel free to send updates (both incremental and replacement are fine).

> Meanwhile I've stumbled across another issue:
> The megasas HBA insists on returning some data like inquiry
> or VPD pages during init/configuration.
>
> I can't really use the normal command completion here,
> as eg on megasas configuration command requires me
> to issue several requests to the underlying device.

Well, you can, but it probably isn't very convenient ...

Easy way out would be calling qemu_aio_wait(), when it returns you can 
be sure the request is finished (and the completion callback was 
called).  You probably have to flag the request as being special 
somewhere in req->hba_private so your completion callback will not apply 
the usual processing.

Calling qemu_aio_wait() is only needed if there is actually something in 
flight asynchronously.  req->status is initialized to -1 and set to a 
status code on completion, so you can use that to figure whenever the 
request is still being processed.  All commands emulated by scsi-disk 
will complete instantly, i.e. the completion callback will be called 
before scsi_req_{buf,sgl} returns.  Only with scsi-generic you'll find 
INQUIRY being processed really asynchronously.

cheers,
   Gerd

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-11-17 16:40 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-11-12 11:48 [Qemu-devel] [PATCH] megasas: LSI MegaRAID SAS HBA emulation Hannes Reinecke
2009-11-17 10:22 ` [Qemu-devel] " Gerd Hoffmann
2009-11-17 10:51   ` Hannes Reinecke
2009-11-17 16:39     ` Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).