This patch add an emulation for the LSI MegaRAID SAS HBA. It is using SG_IO to forward / pass through SCSI commands to the underlying block driver, so no emulation is done currently. Signed-off-by: Hannes Reinecke <hare at suse.de> --- Makefile.hw | 2 +- hw/megasas.c | 1134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/pci_ids.h | 2 + 3 files changed, 1137 insertions(+), 1 deletions(-) create mode 100644 hw/megasas.c diff --git a/Makefile.hw b/Makefile.hw index de1db31..cae35f9 100644 --- a/Makefile.hw +++ b/Makefile.hw @@ -33,7 +33,7 @@ obj-y += wdt_i6300esb.o obj-y += ne2000.o # SCSI layer -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..a57e8e0 --- /dev/null +++ b/hw/megasas.c @@ -0,0 +1,1134 @@ +/* + * 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 "scsi.h" +#include "scsi-disk.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 + +/* SCSI definitions */ +#define SAM_STAT_GOOD 0x00 +#define SAM_STAT_CHECK_CONDITION 0x02 +#define SAM_STAT_CONDITION_MET 0x04 +#define SAM_STAT_BUSY 0x08 +#define SAM_STAT_TASK_ABORTED 0x40 + +/* 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 + +struct megasas_lun_t { + SCSIDevice *sdev; + BlockDriverState *bdrv; + BlockDriverAIOCB *aiocb; +}; + +struct megasas_cmd_t { + int index; + + uint32_t context; + target_phys_addr_t pa; + uint16_t flags; + uint8_t sge_count; + uint32_t sge_size; + uint8_t *sense; + uint8_t sense_len; + struct iovec iov[MEGASAS_MAX_SGE]; + QEMUIOVector qiov; + struct sg_io_hdr hdr; + + struct megasas_state_t *state; + struct megasas_lun_t *lun; +}; + +typedef struct megasas_state_t { + PCIDevice dev; + int mmio_io_addr; + + int fw_state; + int fw_sge; + int fw_cmds; + int intr_mask; + int doorbell; + + target_phys_addr_t reply_queue_pa; + void *reply_queue; + int reply_queue_len; + target_phys_addr_t consumer_pa; + target_phys_addr_t producer_pa; + + struct megasas_cmd_t frames[MEGASAS_MAX_FRAMES]; + struct megasas_cmd_t *next_fw_cmd; + + struct megasas_lun_t luns[MAX_RAID_DEVS]; + + 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 offset, int dir) +{ + int i; + int is_sgl64 = (cmd->flags & MFI_FRAME_SGL64) ? 1 : 0; + int sgl_addr_size = is_sgl64 ? sizeof(uint64_t) : sizeof(uint32_t); + + for (i = 0; i < cmd->sge_count; i++) { + target_phys_addr_t pa, iov_pa; + + pa = cmd->pa + offset; + if (is_sgl64) + iov_pa = ldq_phys(pa); + else + iov_pa = ldl_phys(pa); + cmd->iov[i].iov_len = ldl_phys(pa + sgl_addr_size); + cmd->iov[i].iov_base = cpu_physical_memory_map(iov_pa, + &cmd->iov[i].iov_len, + dir); + offset += sgl_addr_size + sizeof(uint32_t); + } +} + +static void megasas_unmap_sgl(struct megasas_cmd_t *cmd) +{ + int dir = (cmd->flags & MFI_FRAME_DIR_WRITE) ? 1 : 0; + int i, offset = 0; + + for (i = 0; i < cmd->sge_count; i++) { + size_t size = MIN(cmd->sge_size - offset, cmd->iov[i].iov_len); + + cpu_physical_memory_unmap(cmd->iov[i].iov_base, cmd->iov[i].iov_len, + dir, size); + offset += cmd->iov[i].iov_len; + cmd->iov[i].iov_len = 0; + cmd->iov[i].iov_base = NULL; + } +} + +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 struct megasas_cmd_t * +megasas_get_next_frame(MPTState *s, int index) +{ + struct megasas_cmd_t *cmd; + + if (index == (s->fw_cmds - 1)) + cmd = &s->frames[0]; + else + cmd = s->next_fw_cmd + 1; + + return cmd; +} + +/* + * megasas_check_queue_full + * + * Check if the host frame queue is full. + * We always have an empty frame between + * the 'next' frame (as indicated by s->next_fw_cmd) + * and the tail of the reply queue. + * This enables us to always terminate a command + * with the proper SAM_STAT_BUSY status. + */ +static inline int +megasas_check_queue_full(MPTState *s) +{ + int tail; + struct megasas_cmd_t *cmd; + + tail = ldl_phys(s->consumer_pa); + cmd = megasas_get_next_frame(s, s->next_fw_cmd->index); + + return (cmd->index == tail); +} + +static struct megasas_cmd_t * +megasas_enqueue_frame(MPTState *s, target_phys_addr_t frame) +{ +#ifdef DEBUG_MEGASAS_QUEUE + int head = 0, tail = 0; +#endif + struct megasas_cmd_t *cmd = s->next_fw_cmd; + + /* Classical 'this shouldn't happen' check */ + if (cmd->pa) + BADF("Frame %d still active\n", cmd->index); + + cmd->pa = frame; + cmd->context = ldl_phys(cmd->pa + MEGASAS_FRAME_CONTEXT_OFFSET); + s->next_fw_cmd = megasas_get_next_frame(s, cmd->index); + +#ifdef DEBUG_MEGASAS_QUEUE + tail = ldl_phys(s->consumer_pa); + head = ldl_phys(s->producer_pa); + + DPRINTF("Enqueue frame %d to reply queue, head %d tail %d next %d\n", + cmd->index, head, tail, s->next_fw_cmd->index); +#endif + + return cmd; +} + +static void megasas_dequeue_frame(MPTState *s, struct megasas_cmd_t *cmd) +{ + int head, tail, index, cmds_active = 0, cmds_deferred = 0, num = 0; +#ifdef DEBUG_MEGASAS_QUEUE + uint8_t status; +#endif + + if (!cmd) { +#ifdef DEBUG_MEGASAS_QUEUE + DPRINTF("No frame to complete\n"); +#endif + return; + } + tail = index = ldl_phys(s->producer_pa); + head = s->next_fw_cmd->index; + /* check outstanding cmds */ + while (index != head && num < MEGASAS_MAX_FRAMES) { + if (index == cmd->index) { + cmds_deferred = cmds_active; + cmds_active = 0; + } else if(s->frames[index].pa) + cmds_active++; + + index++; + if (index == s->fw_cmds) + index = 0; + num++; + } + +#ifdef DEBUG_MEGASAS_QUEUE + status = megasas_frame_get_cmd_status(cmd->pa); + DPRINTF("Complete frame %d: context %x status %x deferred %d active %d\n", + cmd->index, cmd->context, status, cmds_deferred, + cmds_active); +#endif + stl_phys(s->reply_queue_pa + cmd->index * sizeof(uint32_t), cmd->context); + + /* Free up command */ + if (cmd->sge_count) { + megasas_unmap_sgl(cmd); + cmd->sge_count = 0; + cmd->sge_size = 0; + } + cmd->context = 0; + cmd->pa = 0; + cmd->lun = NULL; + + /* + * If there are outstanding deferred commands + * (ie commands with a lower index that ours) + * do now acknowledge this. + */ + if (cmds_deferred) + return; + + /* Update reply queue pointer and notify HBA */ + if (MEGASAS_INTR_ENABLED(s)) { + /* Do not acknowledge outstanding commands */ + if (cmds_active) + head = (cmd->index + 1) & 0x3F; +#ifdef DEBUG_MEGASAS_QUEUE + DPRINTF("Update reply queue head %d this %d tail %d\n", + head, cmd->index, tail); +#endif + stl_phys(s->producer_pa, head); + s->doorbell++; + qemu_irq_raise(s->dev.irq[0]); + } else { + /* Reset fw command pointer */ + s->next_fw_cmd = cmd; + } +} + +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->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; +} + +static int megasas_handle_scsi(MPTState *s, uint8_t fcmd, + struct megasas_cmd_t *cmd) +{ + uint8_t target, lun, cdb_len, sense_len; + uint8_t cdb[16], cmd_status = MFI_STAT_INVALID_STATUS; + uint8_t scsi_status = SAM_STAT_GOOD; + int n, dir, ret; + + 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); + cdb_len = megasas_frame_get(cmd->pa, CDB_LEN); + megasas_map_sense(cmd); + + if (cdb_len > 16) { + DPRINTF("SCSI %s dev %x/%x invalid cdb len %d\n", + (fcmd == MFI_CMD_PD_SCSI_IO) ? "PD" : "LD", + target, lun, cdb_len); + sense_len = scsi_build_sense(cmd->sense, SENSE_INVALID_OPCODE); + megasas_unmap_sense(cmd, sense_len); + megasas_frame_set_scsi_status(cmd->pa, SAM_STAT_CHECK_CONDITION); + return MFI_STAT_SCSI_DONE_WITH_ERROR; + } + + cpu_physical_memory_read(cmd->pa + 0x20, (uint8_t *)cdb, 16); + + /* The current Qemu infrastructure allows only for LUN 0 */ + if (lun == 0 && target < MAX_RAID_DEVS) + cmd->lun = &s->luns[target]; + + DPRINTF("SCSI %s dev %x lun %x sdev %p\n", + (fcmd == MFI_CMD_PD_SCSI_IO) ? "PD" : "LD", + target, lun, cmd->lun); + DPRINTF("SCSI %s len %d cdb %02x %02x %02x %02x %02x %02x\n", + (fcmd == MFI_CMD_PD_SCSI_IO) ? "PD" : "LD", + cdb_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5]); + + if (!cmd->lun || !cmd->lun->sdev) { + megasas_unmap_sense(cmd, 0); + return MFI_STAT_DEVICE_NOT_FOUND; + } + + dir = (cmd->flags & MFI_FRAME_DIR_WRITE) ? 1 : 0; + megasas_map_sgl(cmd, MEGASAS_PTHRU_SGL_OFFSET, dir); + + /* Internally emulated commands */ + switch (cdb[0]) { + case 0x35: + DPRINTF("Synchronise cache\n"); + bdrv_flush(cmd->lun->bdrv); + sense_len = 0; + goto out; + break; + case 0xa0: + { + uint8_t *outbuf; + + DPRINTF("Report LUNs (len %d)\n", (int)cmd->iov[0].iov_len); + if (cmd->iov[0].iov_len < 16) { + sense_len = scsi_build_sense(cmd->sense, SENSE_INVALID_FIELD); + scsi_status = SAM_STAT_CHECK_CONDITION; + goto out; + } + outbuf = cmd->iov[0].iov_base; + memset(outbuf, 0, 16); + outbuf[3] = 8; + cmd->iov[0].iov_len = 16; + sense_len = 0; + goto out; + break; + } + } + + memset(&cmd->hdr, 0, sizeof(struct sg_io_hdr)); + cmd->hdr.interface_id = 'S'; + cmd->hdr.cmd_len = cdb_len; + cmd->hdr.cmdp = cdb; + cmd->hdr.iovec_count = cmd->sge_count; + cmd->hdr.dxferp = cmd->iov; + for (n = 0; n < cmd->sge_count; n++) + cmd->hdr.dxfer_len += cmd->iov[n].iov_len; + if (cmd->sge_count) { + if (dir) + cmd->hdr.dxfer_direction = SG_DXFER_TO_DEV; + else + cmd->hdr.dxfer_direction = SG_DXFER_FROM_DEV; + } else { + cmd->hdr.dxfer_direction = SG_DXFER_NONE; + } + cmd->hdr.sbp = cmd->sense; + cmd->hdr.mx_sb_len = cmd->sense_len; + + ret = bdrv_ioctl(cmd->lun->bdrv, SG_IO, &cmd->hdr); + if (ret) { + DPRINTF("SCSI pthru dev %x lun %x failed with %d\n", + target, lun, errno); + sense_len = scsi_build_sense(cmd->sense, SENSE_IO_ERROR); + cmd->sge_size = 0; + scsi_status = SAM_STAT_CHECK_CONDITION; + } else if (cmd->hdr.status) { + sense_len = cmd->hdr.sb_len_wr; + scsi_status = cmd->hdr.status; + cmd->sge_size = cmd->hdr.dxfer_len; + scsi_status = SAM_STAT_CHECK_CONDITION; + } else { + sense_len = 0; + cmd->sge_size = cmd->hdr.dxfer_len; + } +out: + megasas_unmap_sense(cmd, sense_len); + if (scsi_status != SAM_STAT_GOOD) { + megasas_frame_set_scsi_status(cmd->pa, scsi_status); + cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR; + } else { + megasas_frame_set_scsi_status(cmd->pa, SAM_STAT_GOOD); + cmd_status = MFI_STAT_OK; + } + return cmd_status; +} + +static void megasas_read_complete(void * opaque, int ret) +{ + struct megasas_cmd_t *cmd = opaque; + uint8_t cmd_status, scsi_status; + + if (ret) { + DPRINTF("SCSI LD read error %x\n", ret); + cmd->sense_len = scsi_build_sense(cmd->sense, 0x040006); + cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR; + scsi_status = SAM_STAT_CHECK_CONDITION; + } else { + DPRINTF("SCSI LD read finished, len %ld\n", + (unsigned long)cmd->qiov.size); + cmd_status = MFI_STAT_OK; + scsi_status = SAM_STAT_GOOD; + } + + cmd->sge_size = cmd->qiov.size; + + megasas_frame_set_scsi_status(cmd->pa, scsi_status); + megasas_frame_set_cmd_status(cmd->pa, cmd_status); + megasas_dequeue_frame(cmd->state, cmd); +} + +static void megasas_write_complete(void * opaque, int ret) +{ + struct megasas_cmd_t *cmd = opaque; + uint8_t cmd_status, scsi_status; + + if (ret) { + DPRINTF("SCSI LD write error %x\n", ret); + cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR; + scsi_status = SAM_STAT_CHECK_CONDITION; + } else { + DPRINTF("SCSI LD write finished, len %ld\n", + (unsigned long)cmd->qiov.size); + cmd_status = MFI_STAT_OK; + scsi_status = SAM_STAT_GOOD; + } + + cmd->sge_size = cmd->qiov.size; + + megasas_frame_set_scsi_status(cmd->pa, scsi_status); + megasas_frame_set_cmd_status(cmd->pa, cmd_status); + megasas_dequeue_frame(cmd->state, cmd); +} + +static int megasas_handle_io(MPTState *s, struct megasas_cmd_t *cmd, int write) +{ + uint32_t lba_count, lba_start_hi, lba_start_lo; + uint64_t lba_start; + int target, lun; + + 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; + + if (lun == 0) + cmd->lun = &s->luns[target]; + + DPRINTF("SCSI %s dev %x lun %x lba %lx count %lx\n", + write?"write":"read", target, lun, + (unsigned long)lba_start, (unsigned long)lba_count); + + if (!cmd->lun || !cmd->lun->sdev) + return MFI_STAT_DEVICE_NOT_FOUND; + + megasas_map_sgl(cmd, MEGASAS_IO_SGL_OFFSET, write); + megasas_map_sense(cmd); + + qemu_iovec_init_external(&cmd->qiov, cmd->iov, cmd->sge_count); + if (write) + cmd->lun->aiocb = bdrv_aio_writev(cmd->lun->bdrv, lba_start, + &cmd->qiov, lba_count, + megasas_write_complete, cmd); + else + cmd->lun->aiocb = bdrv_aio_readv(cmd->lun->bdrv, lba_start, + &cmd->qiov, lba_count, + megasas_read_complete, cmd); + + if (!cmd->lun->aiocb) { + DPRINTF("SCSI %s dev %x lun %x aio failed\n", + write?"write":"read", target, lun); + megasas_unmap_sense(cmd, 0); + megasas_frame_set_scsi_status(cmd->pa, SAM_STAT_TASK_ABORTED); + cmd->sge_size = 0; + return MFI_STAT_SCSI_IO_FAILED; + } + return MFI_STAT_INVALID_STATUS; +} + +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; + struct megasas_cmd_t *cmd; + + frame_cmd = ldub_phys(frame_addr); + frame_status = ldub_phys(frame_addr + 2); + +#ifdef DEBUG_MEGASAS_MFI + DPRINTF("MFI cmd %x count %d status %x\n", + frame_cmd, 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); + if (megasas_check_queue_full(s)) { + /* reply queue full */ + megasas_frame_set_scsi_status(frame_addr, SAM_STAT_BUSY); + frame_status = MFI_STAT_SCSI_DONE_WITH_ERROR; + goto frame_done; + } + switch (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, frame_cmd, cmd); + break; + case MFI_CMD_LD_READ: + frame_status = megasas_handle_io(s, cmd, 0); + break; + case MFI_CMD_LD_WRITE: + frame_status = megasas_handle_io(s, cmd, 1); + 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_dequeue_frame(s, cmd); + } +} + +static uint32_t megasas_mmio_readl(void *opaque, target_phys_addr_t addr) +{ + MPTState *s = opaque; + +#ifdef DEBUG_MEGASAS_REG + DPRINTF("Read reg 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_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("Write reg %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_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] = { + NULL, + NULL, + megasas_mmio_readl, +}; + +static CPUWriteMemoryFunc * const megasas_mmio_writefn[3] = { + NULL, + NULL, + megasas_mmio_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) +{ + DPRINTF("Mapping IO region %d at %08x\n", region_num, 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 unit, i, ret; + struct sg_io_hdr hdr; + uint8_t tur_cdb[6]; + + /* Format sg_IO hdr */ + memset(tur_cdb, 0, 6); + memset(&hdr, 0, sizeof(struct sg_io_hdr)); + hdr.interface_id = 'S'; + hdr.cmd_len = 6; + hdr.cmdp = tur_cdb; + hdr.dxfer_direction = SG_DXFER_NONE; + + 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); + + 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_mmio_mapfunc); + s->fw_sge = MEGASAS_MAX_SGE; + s->fw_cmds = MEGASAS_MAX_FRAMES; + 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 = 0; + s->frames[i].pa = 0; + memset(s->frames[i].iov, 0, sizeof(struct iovec) * MEGASAS_MAX_SGE); + s->frames[i].state = s; + } + s->next_fw_cmd = &s->frames[0]; + + megasas_soft_reset(s); + + scsi_bus_new(&s->bus, &dev->qdev, 1, MAX_RAID_DEVS, NULL); + memset(s->luns, 0, sizeof(struct megasas_lun_t) * MAX_RAID_DEVS); + for (unit = 0; unit < MAX_RAID_DEVS; unit++) { + struct megasas_lun_t *lun = &s->luns[unit]; + SCSIBus *bus = &s->bus; + DriveInfo *dinfo; + + dinfo = drive_get(IF_RAID, bus->busnr, unit); + if (dinfo == NULL) + continue; + lun->bdrv = dinfo->bdrv; + lun->sdev = scsi_bus_legacy_add_drive(bus, dinfo, unit); + if (!lun->sdev) { + DPRINTF("Cannot allocate drive %d\n", unit); + lun->bdrv = NULL; + continue; + } + /* check if we can use SG_IO */ + ret = bdrv_ioctl(lun->bdrv, SG_IO, &hdr); + if (ret) { + DPRINTF("SCSI cmd passthrough not available on dev %d (error %d)\n", + unit, ret); + lun->sdev = NULL; + lun->bdrv = NULL; + } + } + 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 3afe674..104f3a3 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 -- 1.6.0.2
Gerd Hoffmann
2009-Oct-27 16:59 UTC
[Qemu-devel] [PATCH 2/4] megasas: LSI MegaRAID SAS HBA emulation
Hi,> + /* The current Qemu infrastructure allows only for LUN 0 */ > + if (lun == 0&& target< MAX_RAID_DEVS) > + cmd->lun =&s->luns[target];One more thing which should be fixed in qemu instead of worked around in your driver. After all even good old parallel scsi knows multiple luns. Guess this is how you end up with 128 scsi devices (16 ids with 8 luns each)? cheers, Gerd
Possibly Parallel Threads
- [PATCH 2/4] megasas: LSI MegaRAID SAS HBA emulation
- [PATCH 4/4] megasas: Add SCSI command emulation
- [PATCH 4/4] megasas: Add SCSI command emulation
- [PATCH RFC v6 11/20] s390x/virtio-ccw: support virtio-1 set_vq format
- [PATCH RFC v6 11/20] s390x/virtio-ccw: support virtio-1 set_vq format