diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index fee8cde88b..2c78175ae7 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -25,4 +25,3 @@ CONFIG_HPET=y CONFIG_APPLESMC=y CONFIG_I8259=y CONFIG_PFLASH_CFI01=y -CONFIG_ESP=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 9d3e1dbda1..9febb47ae6 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -10,9 +10,11 @@ CONFIG_EEPRO100_PCI=y CONFIG_PCNET_PCI=y CONFIG_PCNET_COMMON=y CONFIG_LSI_SCSI_PCI=y +CONFIG_MEGASAS_SCSI_PCI=y CONFIG_RTL8139_PCI=y CONFIG_E1000_PCI=y CONFIG_IDE_CORE=y CONFIG_IDE_QDEV=y CONFIG_IDE_PCI=y CONFIG_AHCI=y +CONFIG_ESP=y diff --git a/hw/esp.c b/hw/esp.c index a011347b4f..77f57076c7 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -816,7 +816,7 @@ static void sysbus_esp_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_sysbus_esp_scsi; } -static TypeInfo sysbus_esp_info = { +static const TypeInfo sysbus_esp_info = { .name = "esp", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusESPState), @@ -1176,7 +1176,7 @@ static void esp_pci_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_esp_pci_scsi; } -static TypeInfo esp_pci_info = { +static const TypeInfo esp_pci_info = { .name = "am53c974", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIESPState), diff --git a/hw/megasas.c b/hw/megasas.c index 9a0eab1c98..c35a15db4f 100644 --- a/hw/megasas.c +++ b/hw/megasas.c @@ -38,6 +38,9 @@ #define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ #define MEGASAS_MAX_ARRAYS 128 +#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL +#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400 + #define MEGASAS_FLAG_USE_JBOD 0 #define MEGASAS_MASK_USE_JBOD (1 << MEGASAS_FLAG_USE_JBOD) #define MEGASAS_FLAG_USE_MSIX 1 @@ -89,6 +92,8 @@ typedef struct MegasasState { int shutdown_event; int boot_event; + uint64_t sas_addr; + uint64_t reply_queue_pa; void *reply_queue; int reply_queue_len; @@ -372,14 +377,16 @@ static uint64_t megasas_fw_time(void) return bcd_time; } -static uint64_t megasas_gen_sas_addr(uint64_t id) +/* + * Default disk sata address + * 0x1221 is the magic number as + * present in real hardware, + * so use it here, too. + */ +static uint64_t megasas_get_sata_addr(uint16_t id) { - uint64_t addr; - - addr = 0x5001a4aULL << 36; - addr |= id & 0xfffffffff; - - return addr; + uint64_t addr = (0x1221ULL << 48); + return addr & (id << 24); } /* @@ -652,10 +659,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) size_t dcmd_size = sizeof(info); BusChild *kid; int num_ld_disks = 0; - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - num_ld_disks++; - } + uint16_t sdev_id; memset(&info, 0x0, cmd->iov_size); if (cmd->iov_size < dcmd_size) { @@ -669,10 +673,29 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) info.pci.subvendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC); info.pci.subdevice = cpu_to_le16(0x1013); - info.host.type = MFI_INFO_HOST_PCIX; + /* + * For some reason the firmware supports + * only up to 8 device ports. + * Despite supporting a far larger number + * of devices for the physical devices. + * So just display the first 8 devices + * in the device port list, independent + * of how many logical devices are actually + * present. + */ + info.host.type = MFI_INFO_HOST_PCIE; info.device.type = MFI_INFO_DEV_SAS3G; - info.device.port_count = 2; - info.device.port_addr[0] = cpu_to_le64(megasas_gen_sas_addr((uint64_t)s)); + info.device.port_count = 8; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child); + + if (num_ld_disks < 8) { + sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF); + info.device.port_addr[num_ld_disks] = + cpu_to_le64(megasas_get_sata_addr(sdev_id)); + } + num_ld_disks++; + } memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20); snprintf(info.serial_number, 32, "QEMU%08lx", @@ -761,7 +784,7 @@ static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_INVALID_PARAMETER; } - info.sas_addr = cpu_to_le64(megasas_gen_sas_addr((uint64_t)s)); + info.sas_addr = cpu_to_le64(s->sas_addr); info.stripe_size = 3; info.flush_time = 4; info.background_rate = 30; @@ -891,7 +914,7 @@ static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) info.addr[num_pd_disks].scsi_dev_type = sdev->type; info.addr[num_pd_disks].connect_port_bitmap = 0x1; info.addr[num_pd_disks].sas_addr[0] = - cpu_to_le64(megasas_gen_sas_addr((uint64_t)sdev)); + cpu_to_le64(megasas_get_sata_addr(sdev_id)); num_pd_disks++; offset += sizeof(struct mfi_pd_address); } @@ -994,7 +1017,7 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, info->slot_number = (sdev->id & 0xFF); info->path_info.count = 1; info->path_info.sas_addr[0] = - cpu_to_le64(megasas_gen_sas_addr((uint64_t)sdev)); + cpu_to_le64(megasas_get_sata_addr(sdev_id)); info->connected_port_bitmap = 0x1; info->device_speed = 1; info->link_speed = 1; @@ -2040,7 +2063,7 @@ static const VMStateDescription vmstate_megasas = { } }; -static int megasas_scsi_uninit(PCIDevice *d) +static void megasas_scsi_uninit(PCIDevice *d) { MegasasState *s = DO_UPCAST(MegasasState, dev, d); @@ -2050,7 +2073,6 @@ static int megasas_scsi_uninit(PCIDevice *d) memory_region_destroy(&s->mmio_io); memory_region_destroy(&s->port_io); memory_region_destroy(&s->queue_io); - return 0; } static const struct SCSIBusInfo megasas_scsi_info = { @@ -2103,6 +2125,13 @@ static int megasas_scsi_init(PCIDevice *dev) msix_vector_use(&s->dev, 0); } + if (!s->sas_addr) { + s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) | + IEEE_COMPANY_LOCALLY_ASSIGNED) << 36; + s->sas_addr |= (pci_bus_num(dev->bus) << 16); + s->sas_addr |= (PCI_SLOT(dev->devfn) << 8); + s->sas_addr |= PCI_FUNC(dev->devfn); + } if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) { s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE; } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) { @@ -2137,6 +2166,7 @@ static Property megasas_properties[] = { MEGASAS_DEFAULT_SGE), DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, MEGASAS_DEFAULT_FRAMES), + DEFINE_PROP_HEX64("sas_address", MegasasState, sas_addr, 0), #ifdef USE_MSIX DEFINE_PROP_BIT("use_msix", MegasasState, flags, MEGASAS_FLAG_USE_MSIX, false), diff --git a/hw/mfi.h b/hw/mfi.h index 3045d4eb6e..436b6906b1 100644 --- a/hw/mfi.h +++ b/hw/mfi.h @@ -656,6 +656,7 @@ struct mfi_info_device { #define MFI_INFO_DEV_SAS3G 0x02 #define MFI_INFO_DEV_SATA1 0x04 #define MFI_INFO_DEV_SATA3G 0x08 +#define MFI_INFO_DEV_PCIE 0x10 uint8_t reserved[6]; uint8_t port_count; uint64_t port_addr[8]; diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index e4ec19e051..6120cc83c1 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -733,6 +733,72 @@ static int scsi_get_performance_length(int num_desc, int type, int data_type) } } +static int ata_passthrough_xfer_unit(SCSIDevice *dev, uint8_t *buf) +{ + int byte_block = (buf[2] >> 2) & 0x1; + int type = (buf[2] >> 4) & 0x1; + int xfer_unit; + + if (byte_block) { + if (type) { + xfer_unit = dev->blocksize; + } else { + xfer_unit = 512; + } + } else { + xfer_unit = 1; + } + + return xfer_unit; +} + +static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf) +{ + int length = buf[2] & 0x3; + int xfer; + int unit = ata_passthrough_xfer_unit(dev, buf); + + switch (length) { + case 0: + case 3: /* USB-specific. */ + xfer = 0; + break; + case 1: + xfer = buf[3]; + break; + case 2: + xfer = buf[4]; + break; + } + + return xfer * unit; +} + +static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf) +{ + int extend = buf[1] & 0x1; + int length = buf[2] & 0x3; + int xfer; + int unit = ata_passthrough_xfer_unit(dev, buf); + + switch (length) { + case 0: + case 3: /* USB-specific. */ + xfer = 0; + break; + case 1: + xfer = buf[4]; + xfer |= (extend ? buf[3] << 8 : 0); + break; + case 2: + xfer = buf[6]; + xfer |= (extend ? buf[5] << 8 : 0); + break; + } + + return xfer * unit; +} + static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) { switch (buf[0] >> 5) { @@ -867,6 +933,17 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) cmd->xfer = buf[9] | (buf[8] << 8); } break; + case ATA_PASSTHROUGH_12: + if (dev->type == TYPE_ROM) { + /* BLANK command of MMC */ + cmd->xfer = 0; + } else { + cmd->xfer = ata_passthrough_12_xfer_size(dev, buf); + } + break; + case ATA_PASSTHROUGH_16: + cmd->xfer = ata_passthrough_16_xfer_size(dev, buf); + break; } return 0; } @@ -996,9 +1073,14 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) case SEND_DVD_STRUCTURE: case PERSISTENT_RESERVE_OUT: case MAINTENANCE_OUT: - case ATA_PASSTHROUGH: cmd->mode = SCSI_XFER_TO_DEV; break; + case ATA_PASSTHROUGH_12: + case ATA_PASSTHROUGH_16: + /* T_DIR */ + cmd->mode = (cmd->buf[2] & 0x8) ? + SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV; + break; default: cmd->mode = SCSI_XFER_FROM_DEV; break; @@ -1103,7 +1185,7 @@ const struct SCSISense sense_code_NO_MEDIUM = { /* LUN not ready, medium removal prevented */ const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { - .key = NOT_READY, .asc = 0x53, .ascq = 0x00 + .key = NOT_READY, .asc = 0x53, .ascq = 0x02 }; /* Hardware error, internal target failure */ @@ -1153,7 +1235,7 @@ const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { /* Illegal request, medium removal prevented */ const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { - .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x00 + .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 }; /* Command aborted, I/O process terminated */ @@ -1335,7 +1417,7 @@ static const char *scsi_command_name(uint8_t cmd) [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", [ EXTENDED_COPY ] = "EXTENDED_COPY", - [ ATA_PASSTHROUGH ] = "ATA_PASSTHROUGH", + [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", [ READ_16 ] = "READ_16", @@ -1352,7 +1434,7 @@ static const char *scsi_command_name(uint8_t cmd) [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", [ WRITE_LONG_16 ] = "WRITE_LONG_16", [ REPORT_LUNS ] = "REPORT_LUNS", - [ BLANK ] = "BLANK", + [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", [ MOVE_MEDIUM ] = "MOVE_MEDIUM", [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", [ LOAD_UNLOAD ] = "LOAD_UNLOAD", diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 8a73f745ba..d7a401912b 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -100,7 +100,7 @@ #define READ_REVERSE_16 0x81 #define ALLOW_OVERWRITE 0x82 #define EXTENDED_COPY 0x83 -#define ATA_PASSTHROUGH 0x85 +#define ATA_PASSTHROUGH_16 0x85 #define ACCESS_CONTROL_IN 0x86 #define ACCESS_CONTROL_OUT 0x87 #define READ_16 0x88 @@ -117,7 +117,7 @@ #define SERVICE_ACTION_IN_16 0x9e #define WRITE_LONG_16 0x9f #define REPORT_LUNS 0xa0 -#define BLANK 0xa1 +#define ATA_PASSTHROUGH_12 0xa1 #define MAINTENANCE_IN 0xa3 #define MAINTENANCE_OUT 0xa4 #define MOVE_MEDIUM 0xa5 diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 84b63ffafb..a9c727905a 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -447,7 +447,7 @@ static void scsi_write_complete(void * opaque, int ret) return; } else { scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size); + DPRINTF("Write complete tag=0x%x more=%zd\n", r->req.tag, r->qiov.size); scsi_req_data(&r->req, r->qiov.size); } @@ -1247,6 +1247,12 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); bool start = req->cmd.buf[4] & 1; bool loej = req->cmd.buf[4] & 2; /* load on start, eject on !start */ + int pwrcnd = req->cmd.buf[4] & 0xf0; + + if (pwrcnd) { + /* eject/load only happens for power condition == 0 */ + return 0; + } if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && loej) { if (!start && !s->tray_open && s->tray_locked) { @@ -1271,7 +1277,7 @@ static void scsi_disk_emulate_read_data(SCSIRequest *req) int buflen = r->iov.iov_len; if (buflen) { - DPRINTF("Read buf_len=%zd\n", buflen); + DPRINTF("Read buf_len=%d\n", buflen); r->iov.iov_len = 0; r->started = true; scsi_req_data(&r->req, buflen); @@ -1449,7 +1455,7 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) if (r->iov.iov_len) { int buflen = r->iov.iov_len; - DPRINTF("Write buf_len=%zd\n", buflen); + DPRINTF("Write buf_len=%d\n", buflen); r->iov.iov_len = 0; scsi_req_data(&r->req, buflen); return; @@ -2087,23 +2093,24 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, const SCSIReqOps *ops; uint8_t command; -#ifdef DEBUG_SCSI - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, buf[0]); - { - int i; - for (i = 1; i < r->req.cmd.len; i++) { - printf(" 0x%02x", buf[i]); - } - printf("\n"); - } -#endif - command = buf[0]; ops = scsi_disk_reqops_dispatch[command]; if (!ops) { ops = &scsi_disk_emulate_reqops; } req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private); + +#ifdef DEBUG_SCSI + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); + { + int i; + for (i = 1; i < req->cmd.len; i++) { + printf(" 0x%02x", buf[i]); + } + printf("\n"); + } +#endif + return req; }