From e23d04984a78490d8aaa5c45724a3a334933331f Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 29 Oct 2014 13:00:08 +0100 Subject: [PATCH] megasas: add MegaRAID SAS 2108 emulation The 2108 chip supports MSI and MSI-X, so update the emulation to support both chips. Signed-off-by: Hannes Reinecke [Make VMStateDescription const. - Paolo] Signed-off-by: Paolo Bonzini --- hw/scsi/megasas.c | 218 +++++++++++++++++++++++++++++++++++++++++------ hw/scsi/mfi.h | 7 ++ include/hw/pci/pci_ids.h | 1 + 3 files changed, 201 insertions(+), 25 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 3a7e85e75e..7401b6b778 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -31,9 +31,11 @@ #include "mfi.h" -#define MEGASAS_VERSION "1.70" +#define MEGASAS_VERSION_GEN1 "1.70" +#define MEGASAS_VERSION_GEN2 "1.80" #define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ #define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */ +#define MEGASAS_GEN2_DEFAULT_FRAMES 1008 /* Windows requires this */ #define MEGASAS_MAX_SGE 128 /* Firmware limit */ #define MEGASAS_DEFAULT_SGE 80 #define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ @@ -91,6 +93,8 @@ typedef struct MegasasState { int intr_mask; int doorbell; int busy; + int diag; + int adp_reset; MegasasCmd *event_cmd; int event_locale; @@ -115,10 +119,26 @@ typedef struct MegasasState { SCSIBus bus; } MegasasState; -#define TYPE_MEGASAS "megasas" +typedef struct MegasasBaseClass { + PCIDeviceClass parent_class; + const char *product_name; + const char *product_version; + int mmio_bar; + int ioport_bar; + int osts; +} MegasasBaseClass; + +#define TYPE_MEGASAS_BASE "megasas-base" +#define TYPE_MEGASAS_GEN1 "megasas" +#define TYPE_MEGASAS_GEN2 "megasas-gen2" #define MEGASAS(obj) \ - OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS) + OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS_BASE) + +#define MEGASAS_DEVICE_CLASS(oc) \ + OBJECT_CLASS_CHECK(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE) +#define MEGASAS_DEVICE_GET_CLASS(oc) \ + OBJECT_GET_CLASS(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE) #define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF @@ -686,6 +706,8 @@ static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size) static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) { PCIDevice *pci_dev = PCI_DEVICE(s); + PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(pci_dev); + MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s); struct mfi_ctrl_info info; size_t dcmd_size = sizeof(info); BusChild *kid; @@ -698,10 +720,10 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_INVALID_PARAMETER; } - info.pci.vendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC); - info.pci.device = cpu_to_le16(PCI_DEVICE_ID_LSI_SAS1078); - info.pci.subvendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC); - info.pci.subdevice = cpu_to_le16(0x1013); + info.pci.vendor = cpu_to_le16(pci_class->vendor_id); + info.pci.device = cpu_to_le16(pci_class->device_id); + info.pci.subvendor = cpu_to_le16(pci_class->subsystem_vendor_id); + info.pci.subdevice = cpu_to_le16(pci_class->subsystem_id); /* * For some reason the firmware supports @@ -728,11 +750,12 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) num_pd_disks++; } - memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20); + memcpy(info.product_name, base_class->product_name, 24); snprintf(info.serial_number, 32, "%s", s->hba_serial); snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION); memcpy(info.image_component[0].name, "APP", 3); - memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9); + snprintf(info.image_component[0].version, 10, "%s-QEMU", + base_class->product_version); memcpy(info.image_component[0].build_date, "Apr 1 2014", 11); memcpy(info.image_component[0].build_time, "12:34:56", 8); info.image_component_count = 1; @@ -1960,6 +1983,8 @@ static uint64_t megasas_mmio_read(void *opaque, hwaddr addr, unsigned size) { MegasasState *s = opaque; + PCIDevice *pci_dev = PCI_DEVICE(s); + MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s); uint32_t retval = 0; switch (addr) { @@ -1968,14 +1993,14 @@ static uint64_t megasas_mmio_read(void *opaque, hwaddr addr, break; case MFI_OMSG0: case MFI_OSP0: - retval = (megasas_use_msix(s) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) | + retval = (msix_present(pci_dev) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) | (s->fw_state & MFI_FWSTATE_MASK) | ((s->fw_sge & 0xff) << 16) | (s->fw_cmds & 0xFFFF); break; case MFI_OSTS: if (megasas_intr_enabled(s) && s->doorbell) { - retval = MFI_1078_RM | 1; + retval = base_class->osts; } break; case MFI_OMSK: @@ -1984,6 +2009,12 @@ static uint64_t megasas_mmio_read(void *opaque, hwaddr addr, case MFI_ODCR0: retval = s->doorbell; break; + case MFI_DIAG: + retval = s->diag; + break; + case MFI_OSP1: + retval = 15; + break; default: trace_megasas_mmio_invalid_readl(addr); break; @@ -1992,6 +2023,8 @@ static uint64_t megasas_mmio_read(void *opaque, hwaddr addr, return retval; } +static int adp_reset_seq[] = {0x00, 0x04, 0x0b, 0x02, 0x07, 0x0d}; + static void megasas_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -2017,6 +2050,10 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, if (val & MFI_FWINIT_MFIMODE) { /* discard MFIs */ } + if (val & MFI_FWINIT_STOP_ADP) { + /* Terminal error, stop processing */ + s->fw_state = MFI_FWSTATE_FAULT; + } break; case MFI_OMSK: s->intr_mask = val; @@ -2036,6 +2073,7 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, } } else { trace_megasas_intr_disabled(); + megasas_soft_reset(s); } break; case MFI_ODCR0: @@ -2057,8 +2095,9 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, break; case MFI_IQPL: /* Received low 32 bits of a 64 bit MFI frame address */ + /* Fallthrough */ case MFI_IQP: - /* Received 32 bit MFI frame address */ + /* Received 64 bit MFI frame address */ frame_addr = (val & ~0x1F); /* Add possible 64 bit offset */ frame_addr |= ((uint64_t)s->frame_hi << 32); @@ -2066,6 +2105,28 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, frame_count = (val >> 1) & 0xF; megasas_handle_frame(s, frame_addr, frame_count); break; + case MFI_SEQ: + /* Magic sequence to start ADP reset */ + if (adp_reset_seq[s->adp_reset] == val) { + s->adp_reset++; + } else { + s->adp_reset = 0; + s->diag = 0; + } + if (s->adp_reset == 6) { + s->diag = MFI_DIAG_WRITE_ENABLE; + } + break; + case MFI_DIAG: + /* ADP reset */ + if ((s->diag & MFI_DIAG_WRITE_ENABLE) && + (val & MFI_DIAG_RESET_ADP)) { + s->diag |= MFI_DIAG_RESET_ADP; + megasas_soft_reset(s); + s->adp_reset = 0; + s->diag = 0; + } + break; default: trace_megasas_mmio_invalid_writel(addr, val); break; @@ -2150,7 +2211,7 @@ static void megasas_scsi_reset(DeviceState *dev) megasas_soft_reset(s); } -static const VMStateDescription vmstate_megasas = { +static const VMStateDescription vmstate_megasas_gen1 = { .name = "megasas", .version_id = 0, .minimum_version_id = 0, @@ -2168,6 +2229,25 @@ static const VMStateDescription vmstate_megasas = { } }; +static const VMStateDescription vmstate_megasas_gen2 = { + .name = "megasas-gen2", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(parent_obj, MegasasState), + VMSTATE_MSIX(parent_obj, MegasasState), + + VMSTATE_INT32(fw_state, MegasasState), + VMSTATE_INT32(intr_mask, MegasasState), + VMSTATE_INT32(doorbell, MegasasState), + VMSTATE_UINT64(reply_queue_pa, MegasasState), + VMSTATE_UINT64(consumer_pa, MegasasState), + VMSTATE_UINT64(producer_pa, MegasasState), + VMSTATE_END_OF_LIST() + } +}; + static void megasas_scsi_uninit(PCIDevice *d) { MegasasState *s = MEGASAS(d); @@ -2195,6 +2275,7 @@ static int megasas_scsi_init(PCIDevice *dev) { DeviceState *d = DEVICE(dev); MegasasState *s = MEGASAS(dev); + MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); uint8_t *pci_conf; int i, bar_type; Error *err = NULL; @@ -2218,14 +2299,18 @@ static int megasas_scsi_init(PCIDevice *dev) s->flags &= ~MEGASAS_MASK_USE_MSI; } if (megasas_use_msix(s) && - msix_init(dev, 15, &s->mmio_io, 0, 0x2000, - &s->mmio_io, 0, 0x3800, 0x68)) { + msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000, + &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) { s->flags &= ~MEGASAS_MASK_USE_MSIX; } + if (pci_is_express(dev)) { + pcie_endpoint_cap_init(dev, 0xa0); + } bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64; - pci_register_bar(dev, 0, bar_type, &s->mmio_io); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); + pci_register_bar(dev, b->ioport_bar, + PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); + pci_register_bar(dev, b->mmio_bar, bar_type, &s->mmio_io); pci_register_bar(dev, 3, bar_type, &s->queue_io); if (megasas_use_msix(s)) { @@ -2288,7 +2373,7 @@ megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len) msi_write_config(pci, addr, val, len); } -static Property megasas_properties[] = { +static Property megasas_properties_gen1[] = { DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, @@ -2304,36 +2389,119 @@ static Property megasas_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static Property megasas_properties_gen2[] = { + DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, + MEGASAS_DEFAULT_SGE), + DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, + MEGASAS_GEN2_DEFAULT_FRAMES), + DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), + DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), + DEFINE_PROP_BIT("use_msi", MegasasState, flags, + MEGASAS_FLAG_USE_MSI, true), + DEFINE_PROP_BIT("use_msix", MegasasState, flags, + MEGASAS_FLAG_USE_MSIX, true), + DEFINE_PROP_BIT("use_jbod", MegasasState, flags, + MEGASAS_FLAG_USE_JBOD, false), + DEFINE_PROP_END_OF_LIST(), +}; + +typedef struct MegasasInfo { + const char *name; + const char *desc; + const char *product_name; + const char *product_version; + uint16_t device_id; + uint16_t subsystem_id; + int ioport_bar; + int mmio_bar; + bool is_express; + int osts; + const VMStateDescription *vmsd; + Property *props; +} MegasasInfo; + +static struct MegasasInfo megasas_devices[] = { + { + .name = TYPE_MEGASAS_GEN1, + .desc = "LSI MegaRAID SAS 1078", + .product_name = "LSI MegaRAID SAS 8708EM2", + .product_version = MEGASAS_VERSION_GEN1, + .device_id = PCI_DEVICE_ID_LSI_SAS1078, + .subsystem_id = 0x1013, + .ioport_bar = 2, + .mmio_bar = 0, + .osts = MFI_1078_RM | 1, + .is_express = false, + .vmsd = &vmstate_megasas_gen1, + .props = megasas_properties_gen1, + },{ + .name = TYPE_MEGASAS_GEN2, + .desc = "LSI MegaRAID SAS 2108", + .product_name = "LSI MegaRAID SAS 9260-8i", + .product_version = MEGASAS_VERSION_GEN2, + .device_id = PCI_DEVICE_ID_LSI_SAS0079, + .subsystem_id = 0x9261, + .ioport_bar = 0, + .mmio_bar = 1, + .osts = MFI_GEN2_RM, + .is_express = true, + .vmsd = &vmstate_megasas_gen2, + .props = megasas_properties_gen2, + } +}; + static void megasas_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc); + const MegasasInfo *info = data; pc->init = megasas_scsi_init; pc->exit = megasas_scsi_uninit; pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->device_id = PCI_DEVICE_ID_LSI_SAS1078; + pc->device_id = info->device_id; pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->subsystem_id = 0x1013; + pc->subsystem_id = info->subsystem_id; pc->class_id = PCI_CLASS_STORAGE_RAID; - dc->props = megasas_properties; + pc->is_express = info->is_express; + e->mmio_bar = info->mmio_bar; + e->ioport_bar = info->ioport_bar; + e->osts = info->osts; + e->product_name = info->product_name; + e->product_version = info->product_version; + dc->props = info->props; dc->reset = megasas_scsi_reset; - dc->vmsd = &vmstate_megasas; + dc->vmsd = info->vmsd; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "LSI MegaRAID SAS 1078"; + dc->desc = info->desc; pc->config_write = megasas_write_config; } static const TypeInfo megasas_info = { - .name = TYPE_MEGASAS, + .name = TYPE_MEGASAS_BASE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MegasasState), - .class_init = megasas_class_init, + .class_size = sizeof(MegasasBaseClass), + .abstract = true, }; static void megasas_register_types(void) { + int i; + type_register_static(&megasas_info); + for (i = 0; i < ARRAY_SIZE(megasas_devices); i++) { + const MegasasInfo *info = &megasas_devices[i]; + TypeInfo type_info = {}; + + type_info.name = info->name; + type_info.parent = TYPE_MEGASAS_BASE; + type_info.class_data = (void *)info; + type_info.class_init = megasas_class_init; + + type_register(&type_info); + } } type_init(megasas_register_types) diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h index 455c96b782..29d41775d4 100644 --- a/hw/scsi/mfi.h +++ b/hw/scsi/mfi.h @@ -60,6 +60,7 @@ #define MFI_ODR0 0x9c /* outbound doorbell register0 */ #define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */ #define MFI_OSP0 0xb0 /* outbound scratch pad0 */ +#define MFI_OSP1 0xb4 /* outbound scratch pad1 */ #define MFI_IQPL 0xc0 /* Inbound queue port (low bytes) */ #define MFI_IQPH 0xc4 /* Inbound queue port (high bytes) */ #define MFI_DIAG 0xf8 /* Host diag */ @@ -116,6 +117,12 @@ #define MFI_FWINIT_STOP_ADP 0x00000020 /* Move to operational, stop */ #define MFI_FWINIT_ADP_RESET 0x00000040 /* Reset ADP */ +/* + * Control bits for the DIAG register + */ +#define MFI_DIAG_WRITE_ENABLE 0x00000080 +#define MFI_DIAG_RESET_ADP 0x00000004 + /* MFI Commands */ typedef enum { MFI_CMD_INIT = 0x00, diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index e597070ab8..321d622b78 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -56,6 +56,7 @@ #define PCI_DEVICE_ID_LSI_53C810 0x0001 #define PCI_DEVICE_ID_LSI_53C895A 0x0012 #define PCI_DEVICE_ID_LSI_SAS1078 0x0060 +#define PCI_DEVICE_ID_LSI_SAS0079 0x0079 #define PCI_VENDOR_ID_DEC 0x1011 #define PCI_DEVICE_ID_DEC_21154 0x0026 -- 2.11.0