qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] megasas: Update to version 1.01


From: Hannes Reinecke
Subject: [Qemu-devel] [PATCH] megasas: Update to version 1.01
Date: Tue, 08 Jun 2010 16:15:06 +0200
User-agent: Heirloom mailx 12.2 01/07/07

This patch updates the megasas HBA emulation to version 1.01.
It fixes the following issues:

- Remove hand-crafted inquiry command
- Remove bounce-buffer for direct commands
- Implements qdev properties to set 'max_sge', 'max_cmds'.
- Implement JBOD mode
- Improve direct command handling
- Minor cleanups

Signed-off-by: Hannes Reinecke <address@hidden>

diff --git a/hw/megasas.c b/hw/megasas.c
index 250c3fb..19569a8 100644
--- a/hw/megasas.c
+++ b/hw/megasas.c
@@ -40,38 +40,17 @@ do { fprintf(stderr, "megasas: error: " fmt , ## 
__VA_ARGS__);} while (0)
 #endif
 
 /* Static definitions */
-#define MEGASAS_MAX_FRAMES 1000
-#define MEGASAS_MAX_SGE 8
-#define MEGASAS_MAX_LUNS 128
-
-/* Frame definitions */
-#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_DCMD_SGL_OFFSET                        0x28
-
-#define MEGASAS_PTHRU_SGL_OFFSET               0x30
-
-#define MEGASAS_IO_SGL_OFFSET                  0x28
+#define MEGASAS_VERSION "1.01"
+#define MEGASAS_MAX_FRAMES 2048                /* Firmware limit at 65535 */
+#define MEGASAS_DEFAULT_FRAMES 1000    /* Windows requires this */
+#define MEGASAS_MAX_SGE 255            /* Firmware limit */
+#define MEGASAS_DEFAULT_SGE 80
+#define MEGASAS_MAX_ARRAYS 128
 
 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;
 
@@ -81,8 +60,8 @@ struct megasas_cmd_t {
     QEMUSGList sg;
     void *iov_buf;
     long iov_size;
+    SCSIDevice *sdev;
     struct megasas_state_t *state;
-    struct megasas_lun_t *lun;
 };
 
 typedef struct megasas_state_t {
@@ -93,12 +72,14 @@ typedef struct megasas_state_t {
     uint32_t frame_hi;
 
     int fw_state;
-    int fw_sge;
-    int fw_cmds;
+    uint32_t fw_sge;
+    uint32_t fw_cmds;
     int fw_luns;
     int intr_mask;
     int doorbell;
     int busy;
+    char *raid_mode_str;
+    int is_jbod;
 
     int event_count;
     int shutdown_event;
@@ -113,8 +94,6 @@ typedef struct megasas_state_t {
 
     struct megasas_cmd_t frames[MEGASAS_MAX_FRAMES];
 
-    struct megasas_lun_t luns[MEGASAS_MAX_LUNS];
-
     SCSIBus bus;
 } MPTState;
 
@@ -123,13 +102,19 @@ typedef struct megasas_state_t {
 #define MEGASAS_INTR_ENABLED(s) (((s)->intr_mask & MEGASAS_INTR_DISABLED_MASK 
) != MEGASAS_INTR_DISABLED_MASK)
 
 #define megasas_frame_set_cmd_status(f,v)              \
-    stb_phys((f) + MEGASAS_FRAME_CMD_STATUS_OFFSET, v);
+    stb_phys((f) + offsetof(struct mfi_frame_header, cmd_status), v);
 
 #define megasas_frame_set_sense_len(f,v)               \
-    stb_phys((f) + MEGASAS_FRAME_SENSE_LEN_OFFSET, v);
+    stb_phys((f) + offsetof(struct mfi_frame_header, sense_len), v);
 
 #define megasas_frame_set_scsi_status(f,v)             \
-    stb_phys((f) + MEGASAS_FRAME_SCSI_STATUS_OFFSET, v);
+    stb_phys((f) + offsetof(struct mfi_frame_header, scsi_status), v);
+
+#define megasas_frame_get_cmd(f)                       \
+    ldub_phys((f) + offsetof(struct mfi_frame_header, frame_cmd))
+
+#define megasas_frame_get_context(f)                   \
+    ldl_phys(frame_addr + offsetof(struct mfi_frame_header, context));
 
 static void megasas_soft_reset(MPTState *s);
 
@@ -181,37 +166,20 @@ static void megasas_build_sense(struct megasas_cmd_t 
*cmd, SCSISense sense)
     qemu_free(sense_ptr);
 }
 
-static int megasas_get_inq(SCSIDevice *sdev, int pg, uint8_t *buf, int buflen)
-{
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, sdev->qdev.parent_bus);
-
-    memset(buf, 0, buflen);
-    if (pg == 0) {
-       memcpy(&buf[16], "QEMU HARDDISK  ", 16);
-       memcpy(&buf[8], "QEMU   ", 8);
-       memcpy(&buf[32], QEMU_VERSION, 4);
-       /* Identify device as SCSI-3 rev 1 */
-       buf[2] = 3;
-       buf[3] = 2; /* Format 2 */
-       buf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
-       /* Sync data transfer and TCQ.  */
-       buf[7] = 0x10 | (bus->tcq ? 0x02 : 0);
-    } else if (pg == 0x83) {
-       int id_len = strlen(bdrv_get_device_name(sdev->conf.dinfo->bdrv));
-
-       buflen = 0;
-       buf[buflen++] = 0;
-       buf[buflen++] = pg;
-       buf[buflen++] = 0x00;
-       buf[buflen++] = 3 + id_len;
-       buf[buflen++] = 0x2; // ASCII
-       buf[buflen++] = 0;   // not officially assigned
-       buf[buflen++] = 0;   // reserved
-       buf[buflen++] = id_len; // length of data following
-       memcpy(buf + buflen, bdrv_get_device_name(sdev->conf.dinfo->bdrv), 
id_len);
-       buflen += id_len;
+static int megasas_setup_inquiry(SCSIRequest *req, int pg,
+                                uint8_t *buf, int len)
+{
+    uint8_t cdb[6] = { INQUIRY, 0, 0, 0, 0, 0};
+
+    if (pg > 0) {
+       cdb[1] = 0x1;
+       cdb[2] = pg;
     }
-    return buflen;
+    cdb[3] = (len >> 8) & 0xff;
+    cdb[4] = (len & 0xff);
+    scsi_req_parse(req, cdb);
+    scsi_req_buf(req, buf);
+    return len;
 }
 
 /*
@@ -234,6 +202,16 @@ static uint64_t megasas_fw_time(void)
     return bcd_time;
 }
 
+static uint64_t megasas_gen_sas_addr(unsigned long id)
+{
+    uint64_t addr;
+
+    addr = ((uint64_t)0x5001a4a << 36);
+    addr |= ((uint64_t)id & 0xfffffffff);
+
+    return addr;
+}
+
 /*
  * Frame handling
  */
@@ -254,7 +232,7 @@ static inline struct megasas_cmd_t 
*megasas_lookup_frame(MPTState *s,
 
     index = s->reply_queue_index;
 
-    while (num < MEGASAS_MAX_FRAMES) {
+    while (num < s->fw_cmds) {
        if (s->frames[index].pa && s->frames[index].pa == frame) {
            cmd = &s->frames[index];
            break;
@@ -282,7 +260,7 @@ static inline struct megasas_cmd_t 
*megasas_next_frame(MPTState *s,
     }
     index = s->reply_queue_index;
     num = 0;
-    while (num < MEGASAS_MAX_FRAMES) {
+    while (num < s->fw_cmds) {
        if (!s->frames[index].pa) {
            cmd = &s->frames[index];
            break;
@@ -377,7 +355,7 @@ static void megasas_dump_frame(struct megasas_cmd_t *cmd)
 
 static int megasas_finish_command(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    int context;
+    int context = -1;
 
     if (!cmd) {
 #ifdef DEBUG_MEGASAS_QUEUE
@@ -386,8 +364,8 @@ static int megasas_finish_command(MPTState *s, struct 
megasas_cmd_t *cmd)
        s->event_count++;
        return -1;
     }
-    context = cmd->frame->header.context;
-    cmd->lun = NULL;
+    if (cmd->frame)
+       context = cmd->frame->header.context;
 
     return context;
 }
@@ -483,14 +461,14 @@ static int megasas_finish_dcmd(struct megasas_cmd_t *cmd, 
uint32_t size)
     }
     cpu_physical_memory_unmap(cmd->iov_buf, cmd->iov_size, 1, size);
     if (cmd->iov_size > size)
-       stl_phys(cmd->pa + MEGASAS_DCMD_SGL_OFFSET + sgl_addr_size, size);
+       stl_phys(cmd->pa + offsetof(struct mfi_dcmd_frame,sgl) + sgl_addr_size, 
size);
 
     return size;
 }
 
 static int megasas_ctrl_get_info(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_ctrl_info info;
+    struct mfi_ctrl_info *info = cmd->iov_buf;
     int n, num_ld_disks = 0;
 
     for (n = 0; n < s->fw_luns; n++) {
@@ -498,134 +476,189 @@ static int megasas_ctrl_get_info(MPTState *s, struct 
megasas_cmd_t *cmd)
            num_ld_disks++;
     }
 
-    memset(&info, 0x0, sizeof(info));
-    info.pci.vendor = PCI_VENDOR_ID_LSI_LOGIC;
-    info.pci.device = PCI_DEVICE_ID_LSI_SAS1078;
-    info.pci.subvendor = PCI_VENDOR_ID_LSI_LOGIC;
-    info.pci.subdevice = 0x1013;
-
-    info.host.type = MFI_INFO_HOST_PCIX;
-    info.device.type = MFI_INFO_DEV_SAS3G;
-    info.device.port_count = 2;
-
-    memcpy(info.product_name,"MegaRAID SAS 8708EM2", 20);
-    sprintf(info.package_version,"%s-QEMU", QEMU_VERSION);
-    info.current_fw_time = megasas_fw_time();
-    info.max_arms = 32;
-    info.max_spans = 8;
-    info.max_arrays = MEGASAS_MAX_LUNS;
-    info.max_lds = s->fw_luns;
-    info.max_cmds = s->fw_cmds;
-    info.max_sg_elements = s->fw_sge;
-    info.max_request_size = 8192;
-    info.lds_present = num_ld_disks;
-    info.pd_present = num_ld_disks + 1;
-    info.pd_disks_present = num_ld_disks;
-    info.memory_size = 512;
-    info.nvram_size = 32;
-    info.flash_size = 16;
-    info.raid_levels = MFI_INFO_RAID_0;
-    info.adapter_ops = MFI_INFO_AOPS_RBLD_RATE |
+    memset(cmd->iov_buf, 0x0, cmd->iov_size);
+    if (cmd->iov_size != sizeof(struct mfi_ctrl_info)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("Ctrl Get Info: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
+
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("MFI DCMD get controller info\n");
+#endif
+    info->pci.vendor = PCI_VENDOR_ID_LSI_LOGIC;
+    info->pci.device = PCI_DEVICE_ID_LSI_SAS1078;
+    info->pci.subvendor = PCI_VENDOR_ID_LSI_LOGIC;
+    info->pci.subdevice = 0x1013;
+
+    info->host.type = MFI_INFO_HOST_PCIX;
+    info->device.type = MFI_INFO_DEV_SAS3G;
+    info->device.port_count = 2;
+    info->device.port_addr[0] = megasas_gen_sas_addr((unsigned long)s);
+
+    memcpy(info->product_name,"MegaRAID SAS 8708EM2", 20);
+    sprintf(info->serial_number,"QEMU%08lx",(unsigned long)s & 0xFFFFFFFF);
+    sprintf(info->package_version,"%s-QEMU", QEMU_VERSION);
+    strcpy(info->image_component[0].name, "APP");
+    strcpy(info->image_component[0].version, MEGASAS_VERSION "-QEMU");
+    strcpy(info->image_component[0].build_date, __DATE__);
+    strcpy(info->image_component[0].build_time, __TIME__);
+    info->image_component_count = 1;
+    info->current_fw_time = megasas_fw_time();
+    info->max_arms = 32;
+    info->max_spans = 8;
+    info->max_arrays = MEGASAS_MAX_ARRAYS;
+    info->max_lds = s->fw_luns;
+    info->max_cmds = s->fw_cmds;
+    info->max_sg_elements = s->fw_sge;
+    info->max_request_size = 8192;
+    info->lds_present = num_ld_disks;
+    info->pd_present = num_ld_disks + 1;
+    info->pd_disks_present = num_ld_disks;
+    info->hw_present = MFI_INFO_HW_NVRAM | MFI_INFO_HW_MEM | MFI_INFO_HW_FLASH;
+    info->memory_size = 512;
+    info->nvram_size = 32;
+    info->flash_size = 16;
+    info->raid_levels = MFI_INFO_RAID_0;
+    info->adapter_ops = MFI_INFO_AOPS_RBLD_RATE |
         MFI_INFO_AOPS_SELF_DIAGNOSTIC |
         MFI_INFO_AOPS_MIXED_ARRAY;
-    info.ld_ops = MFI_INFO_LDOPS_DISK_CACHE_POLICY |
+    info->ld_ops = MFI_INFO_LDOPS_DISK_CACHE_POLICY |
         MFI_INFO_LDOPS_ACCESS_POLICY |
         MFI_INFO_LDOPS_IO_POLICY |
         MFI_INFO_LDOPS_WRITE_POLICY |
         MFI_INFO_LDOPS_READ_POLICY;
-    info.stripe_sz_ops.min = 4;
-    info.stripe_sz_ops.max = 0xf;
-    info.properties.pred_fail_poll_interval = 300;
-    info.properties.intr_throttle_cnt = 16;
-    info.pd_ops = 0x3;
-    info.pd_mix_support = MFI_INFO_PDMIX_SAS | MFI_INFO_PDMIX_LD;
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
+    info->max_strips_per_io = 42;
+    info->stripe_sz_ops.min = 4;
+    info->stripe_sz_ops.max = 0xf;
+    info->properties.pred_fail_poll_interval = 300;
+    info->properties.intr_throttle_cnt = 16;
+    info->properties.intr_throttle_timeout = 50;
+    info->properties.rebuild_rate = 30;
+    info->properties.patrol_read_rate = 30;
+    info->properties.bgi_rate = 30;
+    info->properties.cc_rate = 30;
+    info->properties.recon_rate = 30;
+    info->properties.cache_flush_interval = 4;
+    info->properties.spinup_drv_cnt = 2;
+    info->properties.spinup_delay = 6;
+    info->properties.ecc_bucket_size = 15;
+    info->properties.ecc_bucket_leak_rate = 1440;
+    info->properties.expose_encl_devices = 1;
+    info->pd_ops = MFI_INFO_PDOPS_FORCE_ONLINE | MFI_INFO_PDOPS_FORCE_OFFLINE;
+    info->pd_mix_support = MFI_INFO_PDMIX_SAS | MFI_INFO_PDMIX_SATA | 
MFI_INFO_PDMIX_LD;
 
-    return sizeof(info);
+    return MFI_STAT_OK;
 }
 
 static int megasas_mfc_get_defaults(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_defaults info;
+    struct mfi_defaults *info = cmd->iov_buf;
 
-    memset(&info, 0x0, sizeof(info));
+    memset(cmd->iov_buf, 0x0, cmd->iov_size);
+    if (cmd->iov_size != sizeof(struct mfi_defaults)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("MFC Get defaults: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
 
-    info.stripe_size = 8;
-    info.flush_time = 4;
-    info.background_rate = 30;
-    info.allow_mix_in_enclosure = 1;
-    info.allow_mix_in_ld = 1;
-    info.direct_pd_mapping = 1;
-    info.bios_enumerate_lds = 1;
-    info.disable_ctrl_r = 1;
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
+    info->stripe_size = 8;
+    info->flush_time = 4;
+    info->background_rate = 30;
+    info->allow_mix_in_enclosure = 1;
+    info->allow_mix_in_ld = 1;
+    info->direct_pd_mapping = 1;
+    info->bios_enumerate_lds = 1;
+    info->disable_ctrl_r = 1;
+    info->expose_enclosure_devices = 1;
+    info->disable_preboot_cli = 1;
+    info->cluster_disable = 1;
 
-    return sizeof(info);
+    return MFI_STAT_OK;
 }
 
-static int megasas_dcmd_get_bios_info(MPTState *d, struct megasas_cmd_t *cmd)
+static int megasas_dcmd_get_bios_info(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_bios_data info;
+    struct mfi_bios_data *info = cmd->iov_buf;
 
-    memset(&info, 0x0, sizeof(info));
-    info.continue_on_error = 1;
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
+    memset(cmd->iov_buf, 0x0, cmd->iov_size);
+    if (cmd->iov_size != sizeof(struct mfi_bios_data)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("Get BIOS info: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
+    info->continue_on_error = 1;
 
-return sizeof(info);
+    return MFI_STAT_OK;
 }
 
-static int megasas_dcmd_get_fw_time(MPTState *d, struct megasas_cmd_t *cmd)
+static int megasas_dcmd_get_fw_time(MPTState *s, struct megasas_cmd_t *cmd)
 {
     uint64_t fw_time;
 
     fw_time = megasas_fw_time();
-    memcpy(cmd->iov_buf, (uint8_t *)&fw_time, sizeof(fw_time));
 
-    return sizeof(fw_time);
+    memcpy(cmd->iov_buf, &fw_time, sizeof(fw_time));
+    return MFI_STAT_OK;
 }
 
-static int megasas_dcmd_set_fw_time(MPTState *d, struct megasas_cmd_t *cmd)
+static int megasas_dcmd_set_fw_time(MPTState *s, struct megasas_cmd_t *cmd)
 {
     uint64_t fw_time;
 
+    memset(cmd->iov_buf, 0x0, cmd->iov_size);
     memcpy(&fw_time, cmd->frame->dcmd.mbox, sizeof(fw_time));
     DPRINTF("set fw time %lx\n", fw_time);
     fw_time = megasas_fw_time();
-    memcpy(cmd->iov_buf, (uint8_t *)&fw_time, sizeof(fw_time));
-
-    return sizeof(fw_time);
+    memcpy(cmd->iov_buf, &fw_time, sizeof(fw_time));
+    return MFI_STAT_OK;
 }
 
-
 static int megasas_event_info(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_evt_log_state info;
+    struct mfi_evt_log_state *info = cmd->iov_buf;
 
-    memset(&info, 0, sizeof(info));
-    info.newest_seq_num = s->event_count;
-    info.shutdown_seq_num = s->shutdown_event;
-    info.boot_seq_num = s->boot_event;
+    memset(info, 0, cmd->iov_size);
+    info->newest_seq_num = s->event_count;
+    info->shutdown_seq_num = s->shutdown_event;
+    info->boot_seq_num = s->boot_event;
 
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
-
-    return sizeof(info);
+    return MFI_STAT_OK;
 }
 
 static int megasas_dcmd_pd_get_list(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_pd_list info;
-    uint32_t offset, num_pd_disks = 0;
+    struct mfi_pd_list *info = cmd->iov_buf;
+    uint32_t offset, num_pd_disks = 0, max_luns;
     uint16_t dev_id;
 
-    memset(&info, 0x0, sizeof(info));
+    memset(cmd->iov_buf, 0, cmd->iov_size);
     offset = 8;
-    for (dev_id = 0; dev_id < s->fw_luns; dev_id++) {
+    if (cmd->iov_size < (offset + sizeof(struct mfi_pd_address))) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("PD get list: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    max_luns = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address);
+    if (max_luns > s->fw_luns)
+       max_luns = s->fw_luns;
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("PD get list: returning info for %d PDs\n", max_luns);
+#endif
+
+    for (dev_id = 0; dev_id < max_luns; dev_id++) {
        SCSIDevice *sdev;
 
        sdev = s->bus.devs[dev_id];
        if (sdev) {
-           info.addr[num_pd_disks].device_id = dev_id;
-           info.addr[num_pd_disks].encl_device_id = dev_id;
+           info->addr[num_pd_disks].device_id = dev_id;
+           info->addr[num_pd_disks].encl_device_id = dev_id;
+           info->addr[num_pd_disks].sas_addr[0] = 
megasas_gen_sas_addr((unsigned long)sdev);
            num_pd_disks ++;
            offset += sizeof(struct mfi_pd_address);
        }
@@ -634,19 +667,87 @@ static int megasas_dcmd_pd_get_list(MPTState *s, struct 
megasas_cmd_t *cmd)
     DPRINTF("PD get list: %d PDs, size %d\n", num_pd_disks, offset);
 #endif
 
-    info.size = offset;
-    info.count = num_pd_disks;
-    memcpy(cmd->iov_buf, (uint8_t *)&info, offset);
+    info->size = offset;
+    info->count = num_pd_disks;
 
-    return offset;
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_pd_list_query(MPTState *s, struct megasas_cmd_t *cmd)
+{
+    uint16_t flags;
+
+    /* mbox0 contains flags */
+    flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
+
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("PD query list: flags %x\n", flags);
+#endif
+
+    if (flags == MR_PD_QUERY_TYPE_ALL || s->is_jbod)
+       return megasas_dcmd_pd_get_list(s, cmd);
+
+    return MFI_STAT_OK;
+}
+
+static int megasas_pd_get_info_submit(SCSIDevice * sdev, int lun,
+                                     struct megasas_cmd_t *cmd)
+{
+    struct mfi_pd_info * info = cmd->iov_buf;
+    SCSIRequest *req;
+
+    if (info->inquiry_data[4] == 0) {
+       /* Additional length is zero, resubmit */
+       req = scsi_req_get(sdev, (uint32_t) -1, lun);
+       if (!req)
+           return MFI_STAT_FLASH_ALLOC_FAIL;
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("PD get info submit std inquiry to dev %d\n", lun);
+#endif
+       req->hba_private = cmd;
+       megasas_setup_inquiry(req, 0, info->inquiry_data,
+                             sizeof(info->inquiry_data));
+       return MFI_STAT_INVALID_STATUS;
+    } else if (info->vpd_page83[3] == 0) {
+       /* Additional length is zero, resubmit */
+       req = scsi_req_get(sdev, (uint32_t) -1, lun);
+       if (!req)
+           return MFI_STAT_FLASH_ALLOC_FAIL;
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("PD get info submit vpd inquiry to dev %d\n", lun);
+#endif
+       req->hba_private = cmd;
+       megasas_setup_inquiry(req, 0x83,(uint8_t *)info->vpd_page83,
+                             sizeof(info->vpd_page83));
+       return MFI_STAT_INVALID_STATUS;
+    }
+
+    /* Finished, set FW state */
+    if (cmd->state->is_jbod)
+       info->fw_state = MFI_PD_STATE_SYSTEM;
+    else
+       info->fw_state = MFI_PD_STATE_ONLINE;
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("PD get info set state for dev %d to %x\n", lun, info->fw_state);
+#endif
+    return MFI_STAT_OK;
 }
 
 static int megasas_dcmd_pd_get_info(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_pd_info info;
+    struct mfi_pd_info *info = cmd->iov_buf;
     uint64_t pd_size;
     uint16_t pd_id;
     SCSIDevice *sdev = NULL;
+    int retval = MFI_STAT_OK;
+
+    memset(cmd->iov_buf, 0, cmd->iov_size);
+    if (cmd->iov_size != sizeof(struct mfi_pd_info)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("PD get info: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
 
     /* mbox0 has the ID */
     pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
@@ -655,64 +756,106 @@ static int megasas_dcmd_pd_get_info(MPTState *s, struct 
megasas_cmd_t *cmd)
     DPRINTF("PD get info for dev %d\n", pd_id);
 #endif
     sdev = s->bus.devs[pd_id];
-    memset((uint8_t *)&info, 0x0, sizeof(info));
-    info.ref.v.device_id = pd_id;
+    info->ref.v.device_id = pd_id;
 
     if (sdev) {
-       /* Submit inquiry */
-       megasas_get_inq(sdev, 0, (uint8_t *)&info.inquiry_data,
-                       sizeof(info.inquiry_data));
-       megasas_get_inq(sdev, 0x83, (uint8_t *)&info.vpd_page83,
-                       sizeof(info.vpd_page83));
-       info.fw_state = 0;
-       info.state.ddf.v.pd_type.in_vd = 1;
-       info.state.ddf.v.pd_type.intf = 0x2;
+       info->state.ddf.v.pd_type.in_vd = 1;
+       info->state.ddf.v.pd_type.intf = 0x2;
        bdrv_get_geometry(sdev->conf.dinfo->bdrv, &pd_size);
-       info.raw_size = pd_size;
-       info.non_coerced_size = pd_size;
-       info.coerced_size = pd_size;
+       info->raw_size = pd_size;
+       info->non_coerced_size = pd_size;
+       info->coerced_size = pd_size;
+       info->fw_state = MFI_PD_STATE_OFFLINE;
+       info->path_info.count = 1;
+       info->path_info.sas_addr[0] = megasas_gen_sas_addr((unsigned long)sdev);
+       /* Submit inquiry */
+       retval = megasas_pd_get_info_submit(cmd->sdev, pd_id, cmd);
     }
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
 
-    return sizeof(info);
+    return retval;
 }
 
 static int megasas_dcmd_ld_get_list(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_ld_list info;
-    uint32_t num_ld_disks = 0;
+    struct mfi_ld_list *info = cmd->iov_buf;
+    uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
     uint64_t ld_size;
     uint8_t n;
     int offset;
 
-    memset(&info, 0x0, sizeof(info));
-    offset = 8;
-    for (n = 0; n < s->fw_luns; n++) {
+    memset(cmd->iov_buf, 0, cmd->iov_size);
+    if (cmd->iov_size != sizeof(struct mfi_ld_list)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("LD get list: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    if (s->is_jbod)
+       max_ld_disks = 0;
+
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("LD get list: returning info for %d LDs\n", max_ld_disks);
+#endif
+    for (n = 0; n < max_ld_disks; n++) {
        SCSIDevice *sdev;
 
        sdev = s->bus.devs[n];
        if (sdev) {
            bdrv_get_geometry(sdev->conf.dinfo->bdrv, &ld_size);
            ld_size *= 512;
-           info.ld_list[num_ld_disks].ld.v.target_id = n;
-           info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
-           info.ld_list[num_ld_disks].size = ld_size;
+           info->ld_list[num_ld_disks].ld.v.target_id = n;
+           info->ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
+           info->ld_list[num_ld_disks].size = ld_size;
            num_ld_disks ++;
            offset += 18;
        }
     }
-    info.ld_count = num_ld_disks;
-    memcpy(cmd->iov_buf, (uint8_t *)&info, offset);
+    info->ld_count = num_ld_disks;
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("LD get list: found %d LDs\n", num_ld_disks);
+#endif
+
+    return MFI_STAT_OK;
+}
+
+static int megasas_ld_get_info_submit(SCSIDevice * sdev, int lun,
+                                     struct megasas_cmd_t *cmd)
+{
+    struct mfi_ld_info * info = cmd->iov_buf;
+    SCSIRequest *req;
 
-    return offset;
+    if (info->vpd_page83[3] == 0) {
+       req = scsi_req_get(sdev, (uint32_t) -1, lun);
+       if (!req)
+           return MFI_STAT_FLASH_ALLOC_FAIL;
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("LD get info submit vpd inquiry to dev %d\n", lun);
+#endif
+       req->hba_private = cmd;
+       megasas_setup_inquiry(req, 0x83,(uint8_t *)info->vpd_page83,
+                             sizeof(info->vpd_page83));
+       return MFI_STAT_INVALID_STATUS;
+    }
+    info->ld_config.params.state = MFI_LD_STATE_OPTIMAL;
+    return MFI_STAT_OK;
 }
 
 static int megasas_dcmd_ld_get_info(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_ld_info info;
+    struct mfi_ld_info *info = cmd->iov_buf;
     uint64_t ld_size;
     uint16_t ld_id;
     SCSIDevice *sdev = NULL;
+    int retval = MFI_STAT_OK;
+
+    memset(cmd->iov_buf, 0, cmd->iov_size);
+    if (cmd->iov_size != sizeof(struct mfi_ld_info)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("LD get info: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       return MFI_STAT_INVALID_PARAMETER;
+    }
 
     /* mbox0 has the ID */
     ld_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
@@ -721,51 +864,71 @@ static int megasas_dcmd_ld_get_info(MPTState *s, struct 
megasas_cmd_t *cmd)
     DPRINTF("LD get info for dev %d\n", ld_id);
 #endif
     sdev = s->bus.devs[ld_id];
-    memset((void *)&info, 0x0, sizeof(info));
-    info.ld_config.properties.ld.v.target_id = ld_id;
+    info->ld_config.properties.ld.v.target_id = ld_id;
 
     if (sdev) {
-       memcpy(&info.ld_config.properties.name, "QEMU HARDDISK  ", 16);
-       info.ld_config.params.stripe_size = 64;
-       info.ld_config.params.num_drives = 1;
-       info.ld_config.params.state = MFI_LD_STATE_OPTIMAL;
-       info.ld_config.params.is_consistent = 1;
+       info->ld_config.params.stripe_size = 64;
+       info->ld_config.params.num_drives = 1;
+       info->ld_config.params.state = MFI_LD_STATE_OFFLINE;
+       info->ld_config.params.is_consistent = 1;
        bdrv_get_geometry(sdev->conf.dinfo->bdrv, &ld_size);
-       info.size = ld_size;
-       megasas_get_inq(sdev, 0x83, (uint8_t *)&info.vpd_page83,
-            sizeof(info.vpd_page83));
+       info->size = ld_size;
+       retval = megasas_ld_get_info_submit(cmd->sdev, ld_id, cmd);
     }
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
 
-    return sizeof(info);
+    return retval;
 }
 
 static int megasas_dcmd_get_properties(MPTState *s, struct megasas_cmd_t *cmd)
 {
-    struct mfi_ctrl_props info;
+    struct mfi_ctrl_props *info = cmd->iov_buf;
 
-#ifdef DEBUG_MEGASAS_MFI
+    if (cmd->iov_size != sizeof(struct mfi_ctrl_props)) {
+#ifdef DEBUG_MEGASAS_DCMD
+       DPRINTF("DCMD get properties: invalid xfer_len %ld\n", cmd->iov_size);
+#endif
+       memset(cmd->iov_buf, 0, cmd->iov_size);
+       return MFI_STAT_INVALID_PARAMETER;
+    }
+
+#ifdef DEBUG_MEGASAS_DCMD
     DPRINTF("DCMD get properties: xfer_len %d sge_count %d\n",
            cmd->frame->header.data_len, cmd->frame->header.sge_count);
 #endif
-    info.pred_fail_poll_interval = 300;
-    info.intr_throttle_cnt = 16;
-    info.intr_throttle_timeout = 50;
-    info.rebuild_rate = 30;
-    info.patrol_read_rate = 30;
-    info.bgi_rate = 30;
-    info.cc_rate = 30;
-    info.recon_rate = 30;
-    info.cache_flush_interval = 4;
-    info.spinup_drv_cnt = 2;
-    info.spinup_delay = 6;
-    info.ecc_bucket_size = 15;
-    info.ecc_bucket_leak_rate = 1440;
-    info.expose_encl_devices = 1;
-    
-    memcpy(cmd->iov_buf, (uint8_t *)&info, sizeof(info));
-
-    return sizeof(info);
+    info->pred_fail_poll_interval = 300;
+    info->intr_throttle_cnt = 16;
+    info->intr_throttle_timeout = 50;
+    info->rebuild_rate = 30;
+    info->patrol_read_rate = 30;
+    info->bgi_rate = 30;
+    info->cc_rate = 30;
+    info->recon_rate = 30;
+    info->cache_flush_interval = 4;
+    info->spinup_drv_cnt = 2;
+    info->spinup_delay = 6;
+    info->ecc_bucket_size = 15;
+    info->ecc_bucket_leak_rate = 1440;
+    info->expose_encl_devices = 1;
+
+    return MFI_STAT_OK;
+}
+
+static int megasas_cache_flush(MPTState *s, struct megasas_cmd_t *cmd)
+{
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("MFI DCMD Cache flush\n");
+#endif
+    qemu_aio_flush();
+    return MFI_STAT_OK;
+}
+
+static int megasas_ctrl_shutdown(MPTState *s, struct megasas_cmd_t *cmd)
+{
+#ifdef DEBUG_MEGASAS_DCMD
+    DPRINTF("MFI DCMD Controller shutdown\n");
+#endif
+    s->fw_state = MFI_FWSTATE_READY;
+    return MFI_STAT_OK;
 }
 
 static int megasas_dcmd_set_properties(MPTState *s, struct megasas_cmd_t *cmd)
@@ -814,7 +977,7 @@ static int megasas_dcmd_set_properties(MPTState *s, struct 
megasas_cmd_t *cmd)
     DPRINTF("%02x %02x %02x %0x2 %02x %02x %02x %02x\n",
            dummy[0x38], dummy[0x39], dummy[0x3a], dummy[0x3b],
            dummy[0x3c], dummy[0x3d], dummy[0x3e], dummy[0x3f]);
-    return cmd->frame->header.data_len;
+    return MFI_STAT_OK;
 }
 
 static int megasas_dcmd_dummy(MPTState *s, struct megasas_cmd_t *cmd)
@@ -825,13 +988,67 @@ static int megasas_dcmd_dummy(MPTState *s, struct 
megasas_cmd_t *cmd)
 #endif
     memset(cmd->iov_buf, 0, cmd->frame->header.data_len);
 
-    return cmd->frame->header.data_len;
+    return MFI_STAT_OK;
 }
 
+
+struct dcmd_cmd_tbl_t {
+    int opcode;
+    int (*func)(MPTState *s, struct megasas_cmd_t *cmd);
+} dcmd_cmd_tbl[] = {
+    {MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_GET_INFO, megasas_ctrl_get_info},
+    {MFI_DCMD_CTRL_GET_PROPERTIES, megasas_dcmd_get_properties},
+    {MFI_DCMD_CTRL_SET_PROPERTIES, megasas_dcmd_set_properties},
+    {MFI_DCMD_SPEAKER_GET, megasas_dcmd_dummy},
+    {MFI_DCMD_SPEAKER_ENABLE, megasas_dcmd_dummy},
+    {MFI_DCMD_SPEAKER_DISABLE, megasas_dcmd_dummy},
+    {MFI_DCMD_SPEAKER_SILENCE, megasas_dcmd_dummy},
+    {MFI_DCMD_SPEAKER_TEST, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_EVENT_GETINFO, megasas_event_info},
+    {MFI_DCMD_CTRL_EVENT_GET, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_EVENT_WAIT, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_SHUTDOWN, megasas_ctrl_shutdown},
+    {MFI_DCMD_HIBERNATE_SHUTDOWN, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_GET_TIME, megasas_dcmd_get_fw_time},
+    {MFI_DCMD_CTRL_SET_TIME, megasas_dcmd_set_fw_time},
+    {MFI_DCMD_CTRL_GET_BIOS_INFO, megasas_dcmd_get_bios_info},
+    {MFI_DCMD_CTRL_FACTORY_DEFAULTS, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_MFC_DEFAULTS_GET, megasas_mfc_get_defaults},
+    {MFI_DCMD_CTRL_MFC_DEFAULTS_SET, megasas_dcmd_dummy},
+    {MFI_DCMD_CTRL_CACHE_FLUSH, megasas_cache_flush},
+    {MFI_DCMD_PD_GET_LIST, megasas_dcmd_pd_get_list},
+    {MFI_DCMD_PD_LIST_QUERY, megasas_dcmd_pd_list_query},
+    {MFI_DCMD_PD_GET_INFO, megasas_dcmd_pd_get_info},
+    {MFI_DCMD_PD_STATE_SET, megasas_dcmd_dummy},
+    {MFI_DCMD_PD_REBUILD, megasas_dcmd_dummy},
+    {MFI_DCMD_PD_BLINK, megasas_dcmd_dummy},
+    {MFI_DCMD_PD_UNBLINK, megasas_dcmd_dummy},
+    {MFI_DCMD_LD_GET_LIST, megasas_dcmd_ld_get_list},
+    {MFI_DCMD_LD_GET_INFO, megasas_dcmd_ld_get_info},
+    {MFI_DCMD_LD_GET_PROP, megasas_dcmd_dummy},
+    {MFI_DCMD_LD_SET_PROP, megasas_dcmd_dummy},
+    {MFI_DCMD_LD_DELETE, megasas_dcmd_dummy},
+    {MFI_DCMD_CFG_READ, megasas_dcmd_dummy},
+    {MFI_DCMD_CFG_ADD, megasas_dcmd_dummy},
+    {MFI_DCMD_CFG_CLEAR, megasas_dcmd_dummy},
+    {MFI_DCMD_CFG_FOREIGN_READ, megasas_dcmd_dummy},
+    {MFI_DCMD_CFG_FOREIGN_IMPORT, megasas_dcmd_dummy},
+    {MFI_DCMD_BBU_STATUS, megasas_dcmd_dummy},
+    {MFI_DCMD_BBU_CAPACITY_INFO, megasas_dcmd_dummy},
+    {MFI_DCMD_BBU_DESIGN_INFO, megasas_dcmd_dummy},
+    {MFI_DCMD_BBU_PROP_GET, megasas_dcmd_dummy},
+    {MFI_DCMD_CLUSTER, megasas_dcmd_dummy},
+    {MFI_DCMD_CLUSTER_RESET_ALL, megasas_dcmd_dummy},
+    {MFI_DCMD_CLUSTER_RESET_LD, megasas_dcmd_dummy},
+    {-1, NULL}
+};
+
 static int megasas_handle_dcmd(MPTState *s, struct megasas_cmd_t *cmd)
 {
     int opcode, size = 0, len;
     int retval = 0;
+    struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
 
     opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
 #ifdef DEBUG_MEGASAS_DCMD
@@ -841,158 +1058,83 @@ static int megasas_handle_dcmd(MPTState *s, struct 
megasas_cmd_t *cmd)
     if (len < 0) {
        return MFI_STAT_MEMORY_NOT_AVAILABLE;
     }
-    switch (opcode) {
-       case MFI_DCMD_CTRL_MFC_DEFAULTS_GET:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD get MFC defaults\n");
-#endif
-           size = megasas_mfc_get_defaults(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_GET_INFO:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD get controller info\n");
-#endif
-           size = megasas_ctrl_get_info(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_CACHE_FLUSH:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD Cache flush\n");
-#endif
-           qemu_aio_flush();
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_SHUTDOWN:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD Controller shutdown\n");
-#endif
-           s->fw_state = MFI_FWSTATE_READY;
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_PD_LIST_QUERY:
+    while (cmdptr->opcode != -1 && cmdptr->opcode != opcode)
+       cmdptr++;
+    if (cmdptr->opcode == -1) {
+       DPRINTF("MFI DCMD %x unhandled (len %d)\n", opcode, len);
+       retval = megasas_dcmd_dummy(s, cmd);
+    } else {
+       retval = cmdptr->func(s, cmd);
+    }
+    if (retval != MFI_STAT_INVALID_STATUS) {
+       size = megasas_finish_dcmd(cmd, cmd->iov_size);
 #ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD query physical devices\n");
+       DPRINTF("MFI DCMD wrote %d bytes\n", size);
 #endif
-           retval = MFI_STAT_INVALID_DCMD;
-           break;
-       case MFI_DCMD_PD_GET_LIST:
+    }
+    return retval;
+}
+
+static int megasas_finish_internal_dcmd(struct megasas_cmd_t *cmd,
+                                       SCSIRequest *req)
+{
+    int opcode;
+    int retval = MFI_STAT_OK;
+    int lun = req->lun;
+
+    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
+    scsi_req_put(req);
 #ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD PD get list\n");
+    DPRINTF("DCMD finish internal cmd %x lun %d\n", opcode, lun);
 #endif
-           size = megasas_dcmd_pd_get_list(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
+    switch (opcode) {
        case MFI_DCMD_PD_GET_INFO:
 #ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD PD get info\n");
-#endif
-           size = megasas_dcmd_pd_get_info(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_LD_GET_LIST:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD LD get list\n");
+           DPRINTF("Internal DCMD PD get info\n");
 #endif
-           size = megasas_dcmd_ld_get_list(s, cmd);
-           retval = MFI_STAT_OK;
+           retval = megasas_pd_get_info_submit(cmd->sdev, lun, cmd);
            break;
        case MFI_DCMD_LD_GET_INFO:
 #ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD LD get info\n");
+           DPRINTF("Internal DCMD LD get info\n");
 #endif
-           size = megasas_dcmd_ld_get_info(s, cmd);
-           retval = MFI_STAT_OK;
+           retval = megasas_ld_get_info_submit(cmd->sdev, lun, cmd);
            break;
-       case MFI_DCMD_CTRL_GET_PROPERTIES:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD Get Properties\n");
-#endif
-           size = megasas_dcmd_get_properties(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_SET_PROPERTIES:
-#ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD Set Properties\n");
-#endif
-           size = megasas_dcmd_set_properties(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_EVENT_GETINFO:
-           size = megasas_event_info(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CFG_READ:
-       case MFI_DCMD_CFG_FOREIGN_READ:
-           size = megasas_dcmd_dummy(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_EVENT_WAIT:
+       default:
 #ifdef DEBUG_MEGASAS_DCMD
-           DPRINTF("MFI DCMD controller event wait\n");
+           DPRINTF("Invalid internal DCMD\n");
 #endif
            retval = MFI_STAT_INVALID_DCMD;
            break;
-    case MFI_DCMD_CLUSTER_RESET_ALL:
-    case MFI_DCMD_CLUSTER_RESET_LD:
-        /* Cluster reset commands have a size of 0 */
-        size = 0;
-        retval = MFI_STAT_OK;
-        break;
-       case MFI_DCMD_CTRL_GET_BIOS_INFO:
-           size = megasas_dcmd_get_bios_info(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       case MFI_DCMD_CTRL_GET_TIME:
-           size = megasas_dcmd_get_fw_time(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-    case MFI_DCMD_CTRL_SET_TIME:
-        size = megasas_dcmd_set_fw_time(s, cmd);
-        retval = MFI_STAT_OK;
-        break;
-       case 0x010e0301:
-       case 0x010e0302:
-       case 0x010e8481:
-           DPRINTF("MFI DCMD %x dummy return %d bytes\n", opcode, len);
-           size = megasas_dcmd_dummy(s, cmd);
-           retval = MFI_STAT_OK;
-           break;
-       default:
-           DPRINTF("MFI DCMD %x unhandled (len %d)\n", opcode, len);
-           retval = MFI_STAT_INVALID_DCMD;
-           break;
     }
-    size = megasas_finish_dcmd(cmd, size);
-#ifdef DEBUG_MEGASAS_DCMD
-    DPRINTF("MFI DCMD wrote %d bytes\n", size);
-#endif
+    if (retval != MFI_STAT_INVALID_STATUS)
+       megasas_finish_dcmd(cmd, cmd->iov_size);
     return retval;
 }
 
-static int megasas_handle_scsi(MPTState *s, struct megasas_cmd_t *cmd)
+static int megasas_handle_scsi(MPTState *s, struct megasas_cmd_t *cmd, int 
is_logical)
 {
     uint8_t *cdb;
 
     cdb = cmd->frame->pass.cdb;
 
     if (cmd->frame->header.target_id < s->fw_luns)
-       cmd->lun = &s->luns[cmd->frame->header.target_id];
-
-    if (cmd->lun)
-       cmd->lun->sdev = s->bus.devs[cmd->frame->header.target_id];
+       cmd->sdev = s->bus.devs[cmd->frame->header.target_id];
 
 #ifdef DEBUG_MEGASAS_IO
-    DPRINTF("%s dev %x lun %x sdev %p xfer %d\n",
+    DPRINTF("%s %s dev %x lun %x sdev %p xfer %d\n",
            mfi_frame_desc[cmd->frame->header.frame_cmd],
+           is_logical?"logical":"physical",
            cmd->frame->header.target_id, cmd->frame->header.lun_id,
-           cmd->lun?cmd->lun->sdev:NULL, cmd->frame->header.data_len);
+           cmd->sdev, cmd->frame->header.data_len);
 #endif
 
-    if (!cmd->lun || !cmd->lun->sdev) {
+    if (!cmd->sdev || (s->is_jbod && is_logical)) {
 #ifdef DEBUG_MEGASAS_IO
-       DPRINTF("%s dev %x/%x target not present\n",
-               mfi_frame_desc[cmd->frame->header.frame_cmd], 
cmd->frame->header.target_id,
+       DPRINTF("%s %s dev %x/%x target not present\n",
+               mfi_frame_desc[cmd->frame->header.frame_cmd],
+               is_logical?"logical":"physical",
+               cmd->frame->header.target_id,
                cmd->frame->header.lun_id);
 #endif
        return MFI_STAT_DEVICE_NOT_FOUND;
@@ -1009,7 +1151,7 @@ static int megasas_handle_scsi(MPTState *s, struct 
megasas_cmd_t *cmd)
        return MFI_STAT_SCSI_DONE_WITH_ERROR;
     }
 
-    cmd->req = scsi_req_get(cmd->lun->sdev, cmd->frame->header.context, 
cmd->frame->header.lun_id);
+    cmd->req = scsi_req_get(cmd->sdev, cmd->frame->header.context, 
cmd->frame->header.lun_id);
     cmd->req->hba_private = cmd;
     scsi_req_parse(cmd->req, cdb);
     if (cmd->frame->header.data_len != cmd->req->cmd.xfer) {
@@ -1019,7 +1161,7 @@ static int megasas_handle_scsi(MPTState *s, struct 
megasas_cmd_t *cmd)
        s->event_count++;
     }
 
-    megasas_map_sgl(cmd, MEGASAS_PTHRU_SGL_OFFSET);
+    megasas_map_sgl(cmd, offsetof(struct mfi_pass_frame, sgl));
     scsi_req_sgl(cmd->req, &cmd->sg);
 
     return MFI_STAT_INVALID_STATUS;
@@ -1037,10 +1179,7 @@ static int megasas_handle_io(MPTState *s, struct 
megasas_cmd_t *cmd)
     lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
 
     if (cmd->frame->header.target_id < s->fw_luns)
-       cmd->lun = &s->luns[cmd->frame->header.target_id];
-
-    if (cmd->lun)
-       cmd->lun->sdev = s->bus.devs[cmd->frame->header.target_id];
+       cmd->sdev = s->bus.devs[cmd->frame->header.target_id];
 
 #ifdef DEBUG_MEGASAS_IO
     DPRINTF("%s dev %x lun %x lba %lx count %lx\n",
@@ -1048,7 +1187,7 @@ static int megasas_handle_io(MPTState *s, struct 
megasas_cmd_t *cmd)
            cmd->frame->header.target_id, cmd->frame->header.lun_id,
            (unsigned long)lba_start, (unsigned long)lba_count);
 #endif
-    if (!cmd->lun || !cmd->lun->sdev) {
+    if (!cmd->sdev) {
 #ifdef DEBUG_MEGSAS_IO
        DPRINTF("%s dev %x/%x LUN not present\n",
                mfi_frame_desc[cmd->frame->header.frame_cmd],
@@ -1068,9 +1207,9 @@ static int megasas_handle_io(MPTState *s, struct 
megasas_cmd_t *cmd)
        return MFI_STAT_SCSI_DONE_WITH_ERROR;
     }
 
-    cmd->req = scsi_req_get(cmd->lun->sdev, cmd->frame->header.context, 
cmd->frame->header.lun_id);
+    cmd->req = scsi_req_get(cmd->sdev, cmd->frame->header.context, 
cmd->frame->header.lun_id);
     cmd->req->hba_private = cmd;
-    megasas_map_sgl(cmd, MEGASAS_IO_SGL_OFFSET);
+    megasas_map_sgl(cmd, offsetof(struct mfi_io_frame, sgl));
 
     scsi_req_setup(cmd->req, write, lba_start, lba_count);
     scsi_req_sgl(cmd->req, &cmd->sg);
@@ -1078,6 +1217,21 @@ static int megasas_handle_io(MPTState *s, struct 
megasas_cmd_t *cmd)
     return MFI_STAT_INVALID_STATUS;
 }
 
+static int megasas_finish_internal_command(struct megasas_cmd_t *cmd,
+                                          SCSIRequest *req)
+{
+    int retval = MFI_STAT_INVALID_CMD;
+
+    switch (cmd->frame->header.frame_cmd) {
+       case MFI_CMD_DCMD:
+           retval = megasas_finish_internal_dcmd(cmd, req);
+           break;
+       default:
+           break;
+    }
+    return retval;
+}
+
 static void megasas_command_complete(SCSIRequest *req)
 {
     struct megasas_cmd_t *cmd;
@@ -1095,24 +1249,38 @@ static void megasas_command_complete(SCSIRequest *req)
        return;
     }
 
+    if (cmd->req != req) {
+       /*
+        * Internal command complete
+        */
+       cmd_status = megasas_finish_internal_command(cmd, req);
+       if (cmd_status == MFI_STAT_INVALID_STATUS)
+           return;
+    } else {
+
 #ifdef DEBUG_MEGASAS_IO
-    DPRINTF("%s req %p cmd %p lun %p finished with status %x len %u\n",
-           mfi_frame_desc[cmd->frame->header.frame_cmd], req, cmd, 
cmd->lun->sdev,
-           req->status, (unsigned)req->xferlen);
-#endif
-    if (req->status == CHECK_CONDITION) {
-       megasas_build_sense(cmd, cmd->lun->sdev->sense);
-       cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR;
-       scsi_dev_clear_sense(cmd->lun->sdev);
-    }
+       DPRINTF("%s req %p cmd %p lun %p finished with status %x len %u\n",
+               mfi_frame_desc[cmd->frame->header.frame_cmd], req, cmd, 
cmd->sdev,
+               req->status, (unsigned)req->xferlen);
+#endif
+       if (req->status == CHECK_CONDITION) {
+           megasas_build_sense(cmd, cmd->sdev->sense);
+           cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR;
+           scsi_dev_clear_sense(cmd->sdev);
+       }
 
-    megasas_unmap_sgl(cmd);
-    megasas_frame_set_scsi_status(cmd->pa, cmd->req->status);
-    scsi_req_put(cmd->req);
-    cmd->req = NULL;
+       megasas_unmap_sgl(cmd);
+       megasas_frame_set_scsi_status(cmd->pa, cmd->req->status);
+       scsi_req_put(cmd->req);
+       cmd->req = NULL;
+    }
     context = megasas_finish_command(cmd->state, cmd);
-    megasas_frame_set_cmd_status(cmd->pa, cmd_status);
-    megasas_dequeue_frame(cmd->state, context);
+    if (context == -1) {
+       DPRINTF("Invalid context for cmd %p\n", cmd);
+    } else {
+       megasas_frame_set_cmd_status(cmd->pa, cmd_status);
+       megasas_dequeue_frame(cmd->state, context);
+    }
 }
 
 static int megasas_handle_abort(MPTState *s, struct megasas_cmd_t *cmd)
@@ -1152,8 +1320,8 @@ static void megasas_handle_frame(MPTState *s, 
target_phys_addr_t frame_addr,
     uint32_t frame_context = 0;
     struct megasas_cmd_t *cmd;
 
-    frame_cmd = ldub_phys(frame_addr + MEGASAS_FRAME_CMD_OFFSET);
-    frame_context = ldl_phys(frame_addr + MEGASAS_FRAME_CONTEXT_OFFSET);
+    frame_cmd = megasas_frame_get_cmd(frame_addr);
+    frame_context = megasas_frame_get_context(frame_addr);
 
 #ifdef DEBUG_MEGASAS_MFI
     DPRINTF("MFI cmd %x context %x count %d\n",
@@ -1180,8 +1348,10 @@ static void megasas_handle_frame(MPTState *s, 
target_phys_addr_t frame_addr,
            frame_status = megasas_handle_abort(s, cmd);
            break;
        case MFI_CMD_PD_SCSI_IO:
+           frame_status = megasas_handle_scsi(s, cmd, 0);
+           break;
        case MFI_CMD_LD_SCSI_IO:
-           frame_status = megasas_handle_scsi(s, cmd);
+           frame_status = megasas_handle_scsi(s, cmd, 1);
            break;
        case MFI_CMD_LD_READ:
        case MFI_CMD_LD_WRITE:
@@ -1445,7 +1615,9 @@ static void megasas_mmio_mapfunc(PCIDevice *pci_dev, int 
region_num,
 {
     MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
 
+#ifdef DEBUG_MEGASAS_REG
     DPRINTF("Mapping MMIO region %d at %08lx\n", region_num, (unsigned 
long)addr);
+#endif
     cpu_register_physical_memory(addr, size, s->mmio_io_addr);
     s->event_count++;
 }
@@ -1455,8 +1627,9 @@ static void megasas_io_mapfunc(PCIDevice *pci_dev, int 
region_num,
 {
     MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
 
+#ifdef DEBUG_MEGASAS_REG
     DPRINTF("Mapping IO region %d at %08lx\n", region_num, (unsigned 
long)addr);
-
+#endif
     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);
@@ -1471,7 +1644,9 @@ static void megasas_queue_mapfunc(PCIDevice *pci_dev, int 
region_num,
 {
     MPTState *s = DO_UPCAST(MPTState, dev, pci_dev);
 
+#ifdef DEBUG_MEGASAS_REG
     DPRINTF("Mapping QUEUE region %d at %08lx\n", region_num, (unsigned 
long)addr);
+#endif
     cpu_register_physical_memory(addr, size, s->queue_addr);
     s->event_count++;
 }
@@ -1555,10 +1730,20 @@ static int megasas_scsi_init(PCIDevice *dev)
                            PCI_BASE_ADDRESS_SPACE_IO, megasas_io_mapfunc);
     pci_register_bar((struct PCIDevice *)s, 3, 0x40000,
                            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) ?
-       MAX_SCSI_DEVS : MEGASAS_MAX_LUNS;
+    if (s->fw_sge > MEGASAS_MAX_SGE)
+       s->fw_sge = MEGASAS_MAX_SGE;
+    if (s->fw_cmds > MEGASAS_MAX_FRAMES)
+       s->fw_cmds = MEGASAS_MAX_FRAMES;
+    if (s->raid_mode_str) {
+       if (!strcmp(s->raid_mode_str, "jbod"))
+           s->is_jbod = 1;
+       else
+           s->is_jbod = 0;
+    }
+    DPRINTF("Using %d sges, %d cmds, %s mode\n",
+           s->fw_sge, s->fw_cmds, s->is_jbod?"jbod":"raid");
+    s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
+       MAX_SCSI_DEVS : MFI_MAX_LD;
     s->producer_pa = 0;
     s->consumer_pa = 0;
     for (i = 0; i < s->fw_cmds; i++) {
@@ -1581,6 +1766,12 @@ static PCIDeviceInfo megasas_info = {
     .qdev.size  = sizeof(MPTState),
     .init       = megasas_scsi_init,
     .exit       = megasas_scsi_uninit,
+    .qdev.props = (Property[]) {
+       DEFINE_PROP_UINT32("max_sge", MPTState, fw_sge, MEGASAS_DEFAULT_SGE),
+       DEFINE_PROP_UINT32("max_cmds", MPTState, fw_cmds, 
MEGASAS_DEFAULT_FRAMES),
+       DEFINE_PROP_STRING("mode", MPTState, raid_mode_str),
+       DEFINE_PROP_END_OF_LIST(),
+    },
 };
 
 static void megaraid1078_register_devices(void)
diff --git a/hw/mfi.h b/hw/mfi.h
index e7a5fde..90334b1 100644
--- a/hw/mfi.h
+++ b/hw/mfi.h
@@ -167,7 +167,7 @@ typedef enum {
        MFI_DCMD_CTRL_SHUTDOWN =                0x01050000,
        MFI_DCMD_HIBERNATE_SHUTDOWN =           0x01060000,
        MFI_DCMD_CTRL_GET_TIME =                0x01080101,
-    MFI_DCMD_CTRL_SET_TIME =        0x01080102,
+       MFI_DCMD_CTRL_SET_TIME =                0x01080102,
        MFI_DCMD_CTRL_GET_BIOS_INFO =           0x010c0100,
        MFI_DCMD_CTRL_FACTORY_DEFAULTS =        0x010d0000,
        MFI_DCMD_CTRL_MFC_DEFAULTS_GET =        0x010e0201,
@@ -644,7 +644,16 @@ struct mfi_defaults {
        uint8_t         restored_hot_spare_on_insertion;
        uint8_t         expose_enclosure_devices;
        uint8_t         maintain_pd_fail_history;
-       uint8_t         resv[28];
+       uint8_t         disable_puncture;
+       uint8_t         zero_based_enumeration;
+       uint8_t         disable_preboot_cli;
+       uint8_t         show_drive_led_on_activity;
+       uint8_t         cluster_disable;
+       uint8_t         sas_disable;
+       uint8_t         auto_detect_backplane;
+       uint8_t         fde_only;
+       uint8_t         delay_during_post;
+       uint8_t         resv[19];
 } __attribute__ ((packed));
 
 /* Controller default settings */
@@ -685,6 +694,8 @@ struct mfi_ctrl_info {
 #define MFI_INFO_HW_ALARM      0x02
 #define MFI_INFO_HW_NVRAM      0x04
 #define MFI_INFO_HW_UART       0x08
+#define MFI_INFO_HW_MEM                0x10
+#define MFI_INFO_HW_FLASH      0x20
        uint32_t                current_fw_time;
        uint16_t                max_cmds;
        uint16_t                max_sg_elements;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]