diff options
Diffstat (limited to 'qemu/hw/scsi')
-rw-r--r-- | qemu/hw/scsi/Makefile.objs | 14 | ||||
-rw-r--r-- | qemu/hw/scsi/esp-pci.c | 531 | ||||
-rw-r--r-- | qemu/hw/scsi/esp.c | 745 | ||||
-rw-r--r-- | qemu/hw/scsi/lsi53c895a.c | 2163 | ||||
-rw-r--r-- | qemu/hw/scsi/megasas.c | 2548 | ||||
-rw-r--r-- | qemu/hw/scsi/mfi.h | 1272 | ||||
-rw-r--r-- | qemu/hw/scsi/mpi.h | 1153 | ||||
-rw-r--r-- | qemu/hw/scsi/mptconfig.c | 905 | ||||
-rw-r--r-- | qemu/hw/scsi/mptendian.c | 204 | ||||
-rw-r--r-- | qemu/hw/scsi/mptsas.c | 1442 | ||||
-rw-r--r-- | qemu/hw/scsi/mptsas.h | 100 | ||||
-rw-r--r-- | qemu/hw/scsi/scsi-bus.c | 2062 | ||||
-rw-r--r-- | qemu/hw/scsi/scsi-disk.c | 2828 | ||||
-rw-r--r-- | qemu/hw/scsi/scsi-generic.c | 616 | ||||
-rw-r--r-- | qemu/hw/scsi/spapr_vscsi.c | 1304 | ||||
-rw-r--r-- | qemu/hw/scsi/srp.h | 247 | ||||
-rw-r--r-- | qemu/hw/scsi/vhost-scsi.c | 352 | ||||
-rw-r--r-- | qemu/hw/scsi/viosrp.h | 216 | ||||
-rw-r--r-- | qemu/hw/scsi/virtio-scsi-dataplane.c | 208 | ||||
-rw-r--r-- | qemu/hw/scsi/virtio-scsi.c | 1054 | ||||
-rw-r--r-- | qemu/hw/scsi/vmw_pvscsi.c | 1305 | ||||
-rw-r--r-- | qemu/hw/scsi/vmw_pvscsi.h | 434 |
22 files changed, 0 insertions, 21703 deletions
diff --git a/qemu/hw/scsi/Makefile.objs b/qemu/hw/scsi/Makefile.objs deleted file mode 100644 index 5a2248be3..000000000 --- a/qemu/hw/scsi/Makefile.objs +++ /dev/null @@ -1,14 +0,0 @@ -common-obj-y += scsi-disk.o -common-obj-y += scsi-generic.o scsi-bus.o -common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o -common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o -common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o -common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o -common-obj-$(CONFIG_ESP) += esp.o -common-obj-$(CONFIG_ESP_PCI) += esp-pci.o -obj-$(CONFIG_PSERIES) += spapr_vscsi.o - -ifeq ($(CONFIG_VIRTIO),y) -obj-y += virtio-scsi.o virtio-scsi-dataplane.o -obj-$(CONFIG_VHOST_SCSI) += vhost-scsi.o -endif diff --git a/qemu/hw/scsi/esp-pci.c b/qemu/hw/scsi/esp-pci.c deleted file mode 100644 index 595f88b35..000000000 --- a/qemu/hw/scsi/esp-pci.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * QEMU ESP/NCR53C9x emulation - * - * Copyright (c) 2005-2006 Fabrice Bellard - * Copyright (c) 2012 Herve Poussineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci.h" -#include "hw/nvram/eeprom93xx.h" -#include "hw/scsi/esp.h" -#include "trace.h" -#include "qapi/error.h" -#include "qemu/log.h" - -#define TYPE_AM53C974_DEVICE "am53c974" - -#define PCI_ESP(obj) \ - OBJECT_CHECK(PCIESPState, (obj), TYPE_AM53C974_DEVICE) - -#define DMA_CMD 0x0 -#define DMA_STC 0x1 -#define DMA_SPA 0x2 -#define DMA_WBC 0x3 -#define DMA_WAC 0x4 -#define DMA_STAT 0x5 -#define DMA_SMDLA 0x6 -#define DMA_WMAC 0x7 - -#define DMA_CMD_MASK 0x03 -#define DMA_CMD_DIAG 0x04 -#define DMA_CMD_MDL 0x10 -#define DMA_CMD_INTE_P 0x20 -#define DMA_CMD_INTE_D 0x40 -#define DMA_CMD_DIR 0x80 - -#define DMA_STAT_PWDN 0x01 -#define DMA_STAT_ERROR 0x02 -#define DMA_STAT_ABORT 0x04 -#define DMA_STAT_DONE 0x08 -#define DMA_STAT_SCSIINT 0x10 -#define DMA_STAT_BCMBLT 0x20 - -#define SBAC_STATUS 0x1000 - -typedef struct PCIESPState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion io; - uint32_t dma_regs[8]; - uint32_t sbac; - ESPState esp; -} PCIESPState; - -static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_idle(val); - esp_dma_enable(&pci->esp, 0, 0); -} - -static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_blast(val); - qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n"); -} - -static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_abort(val); - if (pci->esp.current_req) { - scsi_req_cancel(pci->esp.current_req); - } -} - -static void esp_pci_handle_start(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_start(val); - - pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC]; - pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA]; - pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA]; - - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT - | DMA_STAT_DONE | DMA_STAT_ABORT - | DMA_STAT_ERROR | DMA_STAT_PWDN); - - esp_dma_enable(&pci->esp, 0, 1); -} - -static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) -{ - trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val); - switch (saddr) { - case DMA_CMD: - pci->dma_regs[saddr] = val; - switch (val & DMA_CMD_MASK) { - case 0x0: /* IDLE */ - esp_pci_handle_idle(pci, val); - break; - case 0x1: /* BLAST */ - esp_pci_handle_blast(pci, val); - break; - case 0x2: /* ABORT */ - esp_pci_handle_abort(pci, val); - break; - case 0x3: /* START */ - esp_pci_handle_start(pci, val); - break; - default: /* can't happen */ - abort(); - } - break; - case DMA_STC: - case DMA_SPA: - case DMA_SMDLA: - pci->dma_regs[saddr] = val; - break; - case DMA_STAT: - if (!(pci->sbac & SBAC_STATUS)) { - /* clear some bits on write */ - uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; - pci->dma_regs[DMA_STAT] &= ~(val & mask); - } - break; - default: - trace_esp_pci_error_invalid_write_dma(val, saddr); - return; - } -} - -static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) -{ - uint32_t val; - - val = pci->dma_regs[saddr]; - if (saddr == DMA_STAT) { - if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) { - val |= DMA_STAT_SCSIINT; - } - if (pci->sbac & SBAC_STATUS) { - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | - DMA_STAT_DONE); - } - } - - trace_esp_pci_dma_read(saddr, val); - return val; -} - -static void esp_pci_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - PCIESPState *pci = opaque; - - if (size < 4 || addr & 3) { - /* need to upgrade request: we only support 4-bytes accesses */ - uint32_t current = 0, mask; - int shift; - - if (addr < 0x40) { - current = pci->esp.wregs[addr >> 2]; - } else if (addr < 0x60) { - current = pci->dma_regs[(addr - 0x40) >> 2]; - } else if (addr < 0x74) { - current = pci->sbac; - } - - shift = (4 - size) * 8; - mask = (~(uint32_t)0 << shift) >> shift; - - shift = ((4 - (addr & 3)) & 3) * 8; - val <<= shift; - val |= current & ~(mask << shift); - addr &= ~3; - size = 4; - } - - if (addr < 0x40) { - /* SCSI core reg */ - esp_reg_write(&pci->esp, addr >> 2, val); - } else if (addr < 0x60) { - /* PCI DMA CCB */ - esp_pci_dma_write(pci, (addr - 0x40) >> 2, val); - } else if (addr == 0x70) { - /* DMA SCSI Bus and control */ - trace_esp_pci_sbac_write(pci->sbac, val); - pci->sbac = val; - } else { - trace_esp_pci_error_invalid_write((int)addr); - } -} - -static uint64_t esp_pci_io_read(void *opaque, hwaddr addr, - unsigned int size) -{ - PCIESPState *pci = opaque; - uint32_t ret; - - if (addr < 0x40) { - /* SCSI core reg */ - ret = esp_reg_read(&pci->esp, addr >> 2); - } else if (addr < 0x60) { - /* PCI DMA CCB */ - ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2); - } else if (addr == 0x70) { - /* DMA SCSI Bus and control */ - trace_esp_pci_sbac_read(pci->sbac); - ret = pci->sbac; - } else { - /* Invalid region */ - trace_esp_pci_error_invalid_read((int)addr); - ret = 0; - } - - /* give only requested data */ - ret >>= (addr & 3) * 8; - ret &= ~(~(uint64_t)0 << (8 * size)); - - return ret; -} - -static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, - DMADirection dir) -{ - dma_addr_t addr; - DMADirection expected_dir; - - if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) { - expected_dir = DMA_DIRECTION_FROM_DEVICE; - } else { - expected_dir = DMA_DIRECTION_TO_DEVICE; - } - - if (dir != expected_dir) { - trace_esp_pci_error_invalid_dma_direction(); - return; - } - - if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) { - qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n"); - } - - addr = pci->dma_regs[DMA_SPA]; - if (pci->dma_regs[DMA_WBC] < len) { - len = pci->dma_regs[DMA_WBC]; - } - - pci_dma_rw(PCI_DEVICE(pci), addr, buf, len, dir); - - /* update status registers */ - pci->dma_regs[DMA_WBC] -= len; - pci->dma_regs[DMA_WAC] += len; - if (pci->dma_regs[DMA_WBC] == 0) { - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; - } -} - -static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len) -{ - PCIESPState *pci = opaque; - esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE); -} - -static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len) -{ - PCIESPState *pci = opaque; - esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE); -} - -static const MemoryRegionOps esp_pci_io_ops = { - .read = esp_pci_io_read, - .write = esp_pci_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static void esp_pci_hard_reset(DeviceState *dev) -{ - PCIESPState *pci = PCI_ESP(dev); - esp_hard_reset(&pci->esp); - pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P - | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK); - pci->dma_regs[DMA_WBC] &= ~0xffff; - pci->dma_regs[DMA_WAC] = 0xffffffff; - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT - | DMA_STAT_DONE | DMA_STAT_ABORT - | DMA_STAT_ERROR); - pci->dma_regs[DMA_WMAC] = 0xfffffffd; -} - -static const VMStateDescription vmstate_esp_pci_scsi = { - .name = "pciespscsi", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCIESPState), - VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)), - VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -static void esp_pci_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - ESPState *s = req->hba_private; - PCIESPState *pci = container_of(s, PCIESPState, esp); - - esp_command_complete(req, status, resid); - pci->dma_regs[DMA_WBC] = 0; - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; -} - -static const struct SCSIBusInfo esp_pci_scsi_info = { - .tcq = false, - .max_target = ESP_MAX_DEVS, - .max_lun = 7, - - .transfer_data = esp_transfer_data, - .complete = esp_pci_command_complete, - .cancel = esp_request_cancelled, -}; - -static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) -{ - PCIESPState *pci = PCI_ESP(dev); - DeviceState *d = DEVICE(dev); - ESPState *s = &pci->esp; - uint8_t *pci_conf; - - pci_conf = dev->config; - - /* Interrupt pin A */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - s->dma_memory_read = esp_pci_dma_memory_read; - s->dma_memory_write = esp_pci_dma_memory_write; - s->dma_opaque = pci; - s->chip_id = TCHI_AM53C974; - memory_region_init_io(&pci->io, OBJECT(pci), &esp_pci_io_ops, pci, - "esp-io", 0x80); - - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); - s->irq = pci_allocate_irq(dev); - - scsi_bus_new(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static void esp_pci_scsi_uninit(PCIDevice *d) -{ - PCIESPState *pci = PCI_ESP(d); - - qemu_free_irq(pci->esp.irq); -} - -static void esp_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = esp_pci_scsi_realize; - k->exit = esp_pci_scsi_uninit; - k->vendor_id = PCI_VENDOR_ID_AMD; - k->device_id = PCI_DEVICE_ID_AMD_SCSI; - k->revision = 0x10; - k->class_id = PCI_CLASS_STORAGE_SCSI; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter"; - dc->reset = esp_pci_hard_reset; - dc->vmsd = &vmstate_esp_pci_scsi; -} - -static const TypeInfo esp_pci_info = { - .name = TYPE_AM53C974_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIESPState), - .class_init = esp_pci_class_init, -}; - -typedef struct { - PCIESPState pci; - eeprom_t *eeprom; -} DC390State; - -#define TYPE_DC390_DEVICE "dc390" -#define DC390(obj) \ - OBJECT_CHECK(DC390State, obj, TYPE_DC390_DEVICE) - -#define EE_ADAPT_SCSI_ID 64 -#define EE_MODE2 65 -#define EE_DELAY 66 -#define EE_TAG_CMD_NUM 67 -#define EE_ADAPT_OPTIONS 68 -#define EE_BOOT_SCSI_ID 69 -#define EE_BOOT_SCSI_LUN 70 -#define EE_CHKSUM1 126 -#define EE_CHKSUM2 127 - -#define EE_ADAPT_OPTION_F6_F8_AT_BOOT 0x01 -#define EE_ADAPT_OPTION_BOOT_FROM_CDROM 0x02 -#define EE_ADAPT_OPTION_INT13 0x04 -#define EE_ADAPT_OPTION_SCAM_SUPPORT 0x08 - - -static uint32_t dc390_read_config(PCIDevice *dev, uint32_t addr, int l) -{ - DC390State *pci = DC390(dev); - uint32_t val; - - val = pci_default_read_config(dev, addr, l); - - if (addr == 0x00 && l == 1) { - /* First byte of address space is AND-ed with EEPROM DO line */ - if (!eeprom93xx_read(pci->eeprom)) { - val &= ~0xff; - } - } - - return val; -} - -static void dc390_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int l) -{ - DC390State *pci = DC390(dev); - if (addr == 0x80) { - /* EEPROM write */ - int eesk = val & 0x80 ? 1 : 0; - int eedi = val & 0x40 ? 1 : 0; - eeprom93xx_write(pci->eeprom, 1, eesk, eedi); - } else if (addr == 0xc0) { - /* EEPROM CS low */ - eeprom93xx_write(pci->eeprom, 0, 0, 0); - } else { - pci_default_write_config(dev, addr, val, l); - } -} - -static void dc390_scsi_realize(PCIDevice *dev, Error **errp) -{ - DC390State *pci = DC390(dev); - Error *err = NULL; - uint8_t *contents; - uint16_t chksum = 0; - int i; - - /* init base class */ - esp_pci_scsi_realize(dev, &err); - if (err) { - error_propagate(errp, err); - return; - } - - /* EEPROM */ - pci->eeprom = eeprom93xx_new(DEVICE(dev), 64); - - /* set default eeprom values */ - contents = (uint8_t *)eeprom93xx_data(pci->eeprom); - - for (i = 0; i < 16; i++) { - contents[i * 2] = 0x57; - contents[i * 2 + 1] = 0x00; - } - contents[EE_ADAPT_SCSI_ID] = 7; - contents[EE_MODE2] = 0x0f; - contents[EE_TAG_CMD_NUM] = 0x04; - contents[EE_ADAPT_OPTIONS] = EE_ADAPT_OPTION_F6_F8_AT_BOOT - | EE_ADAPT_OPTION_BOOT_FROM_CDROM - | EE_ADAPT_OPTION_INT13; - - /* update eeprom checksum */ - for (i = 0; i < EE_CHKSUM1; i += 2) { - chksum += contents[i] + (((uint16_t)contents[i + 1]) << 8); - } - chksum = 0x1234 - chksum; - contents[EE_CHKSUM1] = chksum & 0xff; - contents[EE_CHKSUM2] = chksum >> 8; -} - -static void dc390_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = dc390_scsi_realize; - k->config_read = dc390_read_config; - k->config_write = dc390_write_config; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "Tekram DC-390 SCSI adapter"; -} - -static const TypeInfo dc390_info = { - .name = "dc390", - .parent = TYPE_AM53C974_DEVICE, - .instance_size = sizeof(DC390State), - .class_init = dc390_class_init, -}; - -static void esp_pci_register_types(void) -{ - type_register_static(&esp_pci_info); - type_register_static(&dc390_info); -} - -type_init(esp_pci_register_types) diff --git a/qemu/hw/scsi/esp.c b/qemu/hw/scsi/esp.c deleted file mode 100644 index 8961be2f3..000000000 --- a/qemu/hw/scsi/esp.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * QEMU ESP/NCR53C9x emulation - * - * Copyright (c) 2005-2006 Fabrice Bellard - * Copyright (c) 2012 Herve Poussineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/scsi/esp.h" -#include "trace.h" -#include "qapi/error.h" -#include "qemu/log.h" - -/* - * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O), - * also produced as NCR89C100. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt - * and - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt - */ - -static void esp_raise_irq(ESPState *s) -{ - if (!(s->rregs[ESP_RSTAT] & STAT_INT)) { - s->rregs[ESP_RSTAT] |= STAT_INT; - qemu_irq_raise(s->irq); - trace_esp_raise_irq(); - } -} - -static void esp_lower_irq(ESPState *s) -{ - if (s->rregs[ESP_RSTAT] & STAT_INT) { - s->rregs[ESP_RSTAT] &= ~STAT_INT; - qemu_irq_lower(s->irq); - trace_esp_lower_irq(); - } -} - -void esp_dma_enable(ESPState *s, int irq, int level) -{ - if (level) { - s->dma_enabled = 1; - trace_esp_dma_enable(); - if (s->dma_cb) { - s->dma_cb(s); - s->dma_cb = NULL; - } - } else { - trace_esp_dma_disable(); - s->dma_enabled = 0; - } -} - -void esp_request_cancelled(SCSIRequest *req) -{ - ESPState *s = req->hba_private; - - if (req == s->current_req) { - scsi_req_unref(s->current_req); - s->current_req = NULL; - s->current_dev = NULL; - } -} - -static uint32_t get_cmd(ESPState *s, uint8_t *buf) -{ - uint32_t dmalen; - int target; - - target = s->wregs[ESP_WBUSID] & BUSID_DID; - if (s->dma) { - dmalen = s->rregs[ESP_TCLO]; - dmalen |= s->rregs[ESP_TCMID] << 8; - dmalen |= s->rregs[ESP_TCHI] << 16; - s->dma_memory_read(s->dma_opaque, buf, dmalen); - } else { - dmalen = s->ti_size; - memcpy(buf, s->ti_buf, dmalen); - buf[0] = buf[2] >> 5; - } - trace_esp_get_cmd(dmalen, target); - - s->ti_size = 0; - s->ti_rptr = 0; - s->ti_wptr = 0; - - if (s->current_req) { - /* Started a new command before the old one finished. Cancel it. */ - scsi_req_cancel(s->current_req); - s->async_len = 0; - } - - s->current_dev = scsi_device_find(&s->bus, 0, target, 0); - if (!s->current_dev) { - // No such drive - s->rregs[ESP_RSTAT] = 0; - s->rregs[ESP_RINTR] = INTR_DC; - s->rregs[ESP_RSEQ] = SEQ_0; - esp_raise_irq(s); - return 0; - } - return dmalen; -} - -static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) -{ - int32_t datalen; - int lun; - SCSIDevice *current_lun; - - trace_esp_do_busid_cmd(busid); - lun = busid & 7; - current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun); - s->current_req = scsi_req_new(current_lun, 0, lun, buf, s); - datalen = scsi_req_enqueue(s->current_req); - s->ti_size = datalen; - if (datalen != 0) { - s->rregs[ESP_RSTAT] = STAT_TC; - s->dma_left = 0; - s->dma_counter = 0; - if (datalen > 0) { - s->rregs[ESP_RSTAT] |= STAT_DI; - } else { - s->rregs[ESP_RSTAT] |= STAT_DO; - } - scsi_req_continue(s->current_req); - } - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_raise_irq(s); -} - -static void do_cmd(ESPState *s, uint8_t *buf) -{ - uint8_t busid = buf[0]; - - do_busid_cmd(s, &buf[1], busid); -} - -static void handle_satn(ESPState *s) -{ - uint8_t buf[32]; - int len; - - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_satn; - return; - } - len = get_cmd(s, buf); - if (len) - do_cmd(s, buf); -} - -static void handle_s_without_atn(ESPState *s) -{ - uint8_t buf[32]; - int len; - - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_s_without_atn; - return; - } - len = get_cmd(s, buf); - if (len) { - do_busid_cmd(s, buf, 0); - } -} - -static void handle_satn_stop(ESPState *s) -{ - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_satn_stop; - return; - } - s->cmdlen = get_cmd(s, s->cmdbuf); - if (s->cmdlen) { - trace_esp_handle_satn_stop(s->cmdlen); - s->do_cmd = 1; - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_raise_irq(s); - } -} - -static void write_response(ESPState *s) -{ - trace_esp_write_response(s->status); - s->ti_buf[0] = s->status; - s->ti_buf[1] = 0; - if (s->dma) { - s->dma_memory_write(s->dma_opaque, s->ti_buf, 2); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - } else { - s->ti_size = 2; - s->ti_rptr = 0; - s->ti_wptr = 0; - s->rregs[ESP_RFLAGS] = 2; - } - esp_raise_irq(s); -} - -static void esp_dma_done(ESPState *s) -{ - s->rregs[ESP_RSTAT] |= STAT_TC; - s->rregs[ESP_RINTR] = INTR_BS; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - s->rregs[ESP_TCLO] = 0; - s->rregs[ESP_TCMID] = 0; - s->rregs[ESP_TCHI] = 0; - esp_raise_irq(s); -} - -static void esp_do_dma(ESPState *s) -{ - uint32_t len; - int to_device; - - to_device = (s->ti_size < 0); - len = s->dma_left; - if (s->do_cmd) { - trace_esp_do_dma(s->cmdlen, len); - s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); - s->ti_size = 0; - s->cmdlen = 0; - s->do_cmd = 0; - do_cmd(s, s->cmdbuf); - return; - } - if (s->async_len == 0) { - /* Defer until data is available. */ - return; - } - if (len > s->async_len) { - len = s->async_len; - } - if (to_device) { - s->dma_memory_read(s->dma_opaque, s->async_buf, len); - } else { - s->dma_memory_write(s->dma_opaque, s->async_buf, len); - } - s->dma_left -= len; - s->async_buf += len; - s->async_len -= len; - if (to_device) - s->ti_size += len; - else - s->ti_size -= len; - if (s->async_len == 0) { - scsi_req_continue(s->current_req); - /* If there is still data to be read from the device then - complete the DMA operation immediately. Otherwise defer - until the scsi layer has completed. */ - if (to_device || s->dma_left != 0 || s->ti_size == 0) { - return; - } - } - - /* Partially filled a scsi buffer. Complete immediately. */ - esp_dma_done(s); -} - -void esp_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - ESPState *s = req->hba_private; - - trace_esp_command_complete(); - if (s->ti_size != 0) { - trace_esp_command_complete_unexpected(); - } - s->ti_size = 0; - s->dma_left = 0; - s->async_len = 0; - if (status) { - trace_esp_command_complete_fail(); - } - s->status = status; - s->rregs[ESP_RSTAT] = STAT_ST; - esp_dma_done(s); - if (s->current_req) { - scsi_req_unref(s->current_req); - s->current_req = NULL; - s->current_dev = NULL; - } -} - -void esp_transfer_data(SCSIRequest *req, uint32_t len) -{ - ESPState *s = req->hba_private; - - trace_esp_transfer_data(s->dma_left, s->ti_size); - s->async_len = len; - s->async_buf = scsi_req_get_buf(req); - if (s->dma_left) { - esp_do_dma(s); - } else if (s->dma_counter != 0 && s->ti_size <= 0) { - /* If this was the last part of a DMA transfer then the - completion interrupt is deferred to here. */ - esp_dma_done(s); - } -} - -static void handle_ti(ESPState *s) -{ - uint32_t dmalen, minlen; - - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_ti; - return; - } - - dmalen = s->rregs[ESP_TCLO]; - dmalen |= s->rregs[ESP_TCMID] << 8; - dmalen |= s->rregs[ESP_TCHI] << 16; - if (dmalen==0) { - dmalen=0x10000; - } - s->dma_counter = dmalen; - - if (s->do_cmd) - minlen = (dmalen < 32) ? dmalen : 32; - else if (s->ti_size < 0) - minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; - else - minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size; - trace_esp_handle_ti(minlen); - if (s->dma) { - s->dma_left = minlen; - s->rregs[ESP_RSTAT] &= ~STAT_TC; - esp_do_dma(s); - } else if (s->do_cmd) { - trace_esp_handle_ti_cmd(s->cmdlen); - s->ti_size = 0; - s->cmdlen = 0; - s->do_cmd = 0; - do_cmd(s, s->cmdbuf); - return; - } -} - -void esp_hard_reset(ESPState *s) -{ - memset(s->rregs, 0, ESP_REGS); - memset(s->wregs, 0, ESP_REGS); - s->tchi_written = 0; - s->ti_size = 0; - s->ti_rptr = 0; - s->ti_wptr = 0; - s->dma = 0; - s->do_cmd = 0; - s->dma_cb = NULL; - - s->rregs[ESP_CFG1] = 7; -} - -static void esp_soft_reset(ESPState *s) -{ - qemu_irq_lower(s->irq); - esp_hard_reset(s); -} - -static void parent_esp_reset(ESPState *s, int irq, int level) -{ - if (level) { - esp_soft_reset(s); - } -} - -uint64_t esp_reg_read(ESPState *s, uint32_t saddr) -{ - uint32_t old_val; - - trace_esp_mem_readb(saddr, s->rregs[saddr]); - switch (saddr) { - case ESP_FIFO: - if (s->ti_size > 0) { - s->ti_size--; - if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { - /* Data out. */ - qemu_log_mask(LOG_UNIMP, - "esp: PIO data read not implemented\n"); - s->rregs[ESP_FIFO] = 0; - } else { - s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; - } - esp_raise_irq(s); - } - if (s->ti_size == 0) { - s->ti_rptr = 0; - s->ti_wptr = 0; - } - break; - case ESP_RINTR: - /* Clear sequence step, interrupt register and all status bits - except TC */ - old_val = s->rregs[ESP_RINTR]; - s->rregs[ESP_RINTR] = 0; - s->rregs[ESP_RSTAT] &= ~STAT_TC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_lower_irq(s); - - return old_val; - case ESP_TCHI: - /* Return the unique id if the value has never been written */ - if (!s->tchi_written) { - return s->chip_id; - } - default: - break; - } - return s->rregs[saddr]; -} - -void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) -{ - trace_esp_mem_writeb(saddr, s->wregs[saddr], val); - switch (saddr) { - case ESP_TCHI: - s->tchi_written = true; - /* fall through */ - case ESP_TCLO: - case ESP_TCMID: - s->rregs[ESP_RSTAT] &= ~STAT_TC; - break; - case ESP_FIFO: - if (s->do_cmd) { - s->cmdbuf[s->cmdlen++] = val & 0xff; - } else if (s->ti_size == TI_BUFSZ - 1) { - trace_esp_error_fifo_overrun(); - } else { - s->ti_size++; - s->ti_buf[s->ti_wptr++] = val & 0xff; - } - break; - case ESP_CMD: - s->rregs[saddr] = val; - if (val & CMD_DMA) { - s->dma = 1; - /* Reload DMA counter. */ - s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO]; - s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID]; - s->rregs[ESP_TCHI] = s->wregs[ESP_TCHI]; - } else { - s->dma = 0; - } - switch(val & CMD_CMD) { - case CMD_NOP: - trace_esp_mem_writeb_cmd_nop(val); - break; - case CMD_FLUSH: - trace_esp_mem_writeb_cmd_flush(val); - //s->ti_size = 0; - s->rregs[ESP_RINTR] = INTR_FC; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - break; - case CMD_RESET: - trace_esp_mem_writeb_cmd_reset(val); - esp_soft_reset(s); - break; - case CMD_BUSRESET: - trace_esp_mem_writeb_cmd_bus_reset(val); - s->rregs[ESP_RINTR] = INTR_RST; - if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) { - esp_raise_irq(s); - } - break; - case CMD_TI: - handle_ti(s); - break; - case CMD_ICCS: - trace_esp_mem_writeb_cmd_iccs(val); - write_response(s); - s->rregs[ESP_RINTR] = INTR_FC; - s->rregs[ESP_RSTAT] |= STAT_MI; - break; - case CMD_MSGACC: - trace_esp_mem_writeb_cmd_msgacc(val); - s->rregs[ESP_RINTR] = INTR_DC; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - esp_raise_irq(s); - break; - case CMD_PAD: - trace_esp_mem_writeb_cmd_pad(val); - s->rregs[ESP_RSTAT] = STAT_TC; - s->rregs[ESP_RINTR] = INTR_FC; - s->rregs[ESP_RSEQ] = 0; - break; - case CMD_SATN: - trace_esp_mem_writeb_cmd_satn(val); - break; - case CMD_RSTATN: - trace_esp_mem_writeb_cmd_rstatn(val); - break; - case CMD_SEL: - trace_esp_mem_writeb_cmd_sel(val); - handle_s_without_atn(s); - break; - case CMD_SELATN: - trace_esp_mem_writeb_cmd_selatn(val); - handle_satn(s); - break; - case CMD_SELATNS: - trace_esp_mem_writeb_cmd_selatns(val); - handle_satn_stop(s); - break; - case CMD_ENSEL: - trace_esp_mem_writeb_cmd_ensel(val); - s->rregs[ESP_RINTR] = 0; - break; - case CMD_DISSEL: - trace_esp_mem_writeb_cmd_dissel(val); - s->rregs[ESP_RINTR] = 0; - esp_raise_irq(s); - break; - default: - trace_esp_error_unhandled_command(val); - break; - } - break; - case ESP_WBUSID ... ESP_WSYNO: - break; - case ESP_CFG1: - case ESP_CFG2: case ESP_CFG3: - case ESP_RES3: case ESP_RES4: - s->rregs[saddr] = val; - break; - case ESP_WCCF ... ESP_WTEST: - break; - default: - trace_esp_error_invalid_write(val, saddr); - return; - } - s->wregs[saddr] = val; -} - -static bool esp_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) -{ - return (size == 1) || (is_write && size == 4); -} - -const VMStateDescription vmstate_esp = { - .name ="esp", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(rregs, ESPState), - VMSTATE_BUFFER(wregs, ESPState), - VMSTATE_INT32(ti_size, ESPState), - VMSTATE_UINT32(ti_rptr, ESPState), - VMSTATE_UINT32(ti_wptr, ESPState), - VMSTATE_BUFFER(ti_buf, ESPState), - VMSTATE_UINT32(status, ESPState), - VMSTATE_UINT32(dma, ESPState), - VMSTATE_BUFFER(cmdbuf, ESPState), - VMSTATE_UINT32(cmdlen, ESPState), - VMSTATE_UINT32(do_cmd, ESPState), - VMSTATE_UINT32(dma_left, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_ESP "esp" -#define ESP(obj) OBJECT_CHECK(SysBusESPState, (obj), TYPE_ESP) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t it_shift; - ESPState esp; -} SysBusESPState; - -static void sysbus_esp_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - SysBusESPState *sysbus = opaque; - uint32_t saddr; - - saddr = addr >> sysbus->it_shift; - esp_reg_write(&sysbus->esp, saddr, val); -} - -static uint64_t sysbus_esp_mem_read(void *opaque, hwaddr addr, - unsigned int size) -{ - SysBusESPState *sysbus = opaque; - uint32_t saddr; - - saddr = addr >> sysbus->it_shift; - return esp_reg_read(&sysbus->esp, saddr); -} - -static const MemoryRegionOps sysbus_esp_mem_ops = { - .read = sysbus_esp_mem_read, - .write = sysbus_esp_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.accepts = esp_mem_accepts, -}; - -void esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable) -{ - DeviceState *dev; - SysBusDevice *s; - SysBusESPState *sysbus; - ESPState *esp; - - dev = qdev_create(NULL, TYPE_ESP); - sysbus = ESP(dev); - esp = &sysbus->esp; - esp->dma_memory_read = dma_memory_read; - esp->dma_memory_write = dma_memory_write; - esp->dma_opaque = dma_opaque; - sysbus->it_shift = it_shift; - /* XXX for now until rc4030 has been changed to use DMA enable signal */ - esp->dma_enabled = 1; - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_mmio_map(s, 0, espaddr); - *reset = qdev_get_gpio_in(dev, 0); - *dma_enable = qdev_get_gpio_in(dev, 1); -} - -static const struct SCSIBusInfo esp_scsi_info = { - .tcq = false, - .max_target = ESP_MAX_DEVS, - .max_lun = 7, - - .transfer_data = esp_transfer_data, - .complete = esp_command_complete, - .cancel = esp_request_cancelled -}; - -static void sysbus_esp_gpio_demux(void *opaque, int irq, int level) -{ - SysBusESPState *sysbus = ESP(opaque); - ESPState *s = &sysbus->esp; - - switch (irq) { - case 0: - parent_esp_reset(s, irq, level); - break; - case 1: - esp_dma_enable(opaque, irq, level); - break; - } -} - -static void sysbus_esp_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - SysBusESPState *sysbus = ESP(dev); - ESPState *s = &sysbus->esp; - Error *err = NULL; - - sysbus_init_irq(sbd, &s->irq); - assert(sysbus->it_shift != -1); - - s->chip_id = TCHI_FAS100A; - memory_region_init_io(&sysbus->iomem, OBJECT(sysbus), &sysbus_esp_mem_ops, - sysbus, "esp", ESP_REGS << sysbus->it_shift); - sysbus_init_mmio(sbd, &sysbus->iomem); - - qdev_init_gpio_in(dev, sysbus_esp_gpio_demux, 2); - - scsi_bus_new(&s->bus, sizeof(s->bus), dev, &esp_scsi_info, NULL); - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } -} - -static void sysbus_esp_hard_reset(DeviceState *dev) -{ - SysBusESPState *sysbus = ESP(dev); - esp_hard_reset(&sysbus->esp); -} - -static const VMStateDescription vmstate_sysbus_esp_scsi = { - .name = "sysbusespscsi", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -static void sysbus_esp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_esp_realize; - dc->reset = sysbus_esp_hard_reset; - dc->vmsd = &vmstate_sysbus_esp_scsi; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sysbus_esp_info = { - .name = TYPE_ESP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusESPState), - .class_init = sysbus_esp_class_init, -}; - -static void esp_register_types(void) -{ - type_register_static(&sysbus_esp_info); -} - -type_init(esp_register_types) diff --git a/qemu/hw/scsi/lsi53c895a.c b/qemu/hw/scsi/lsi53c895a.c deleted file mode 100644 index df205cdaf..000000000 --- a/qemu/hw/scsi/lsi53c895a.c +++ /dev/null @@ -1,2163 +0,0 @@ -/* - * QEMU LSI53C895A SCSI Host Bus Adapter emulation - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -/* Note: - * LSI53C810 emulation is incorrect, in the sense that it supports - * features added in later evolutions. This should not be a problem, - * as well-behaved operating systems will not try to use them. - */ - -#include "qemu/osdep.h" - -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/scsi/scsi.h" -#include "sysemu/dma.h" - -//#define DEBUG_LSI -//#define DEBUG_LSI_REG - -#ifdef DEBUG_LSI -#define DPRINTF(fmt, ...) \ -do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -#define LSI_MAX_DEVS 7 - -#define LSI_SCNTL0_TRG 0x01 -#define LSI_SCNTL0_AAP 0x02 -#define LSI_SCNTL0_EPC 0x08 -#define LSI_SCNTL0_WATN 0x10 -#define LSI_SCNTL0_START 0x20 - -#define LSI_SCNTL1_SST 0x01 -#define LSI_SCNTL1_IARB 0x02 -#define LSI_SCNTL1_AESP 0x04 -#define LSI_SCNTL1_RST 0x08 -#define LSI_SCNTL1_CON 0x10 -#define LSI_SCNTL1_DHP 0x20 -#define LSI_SCNTL1_ADB 0x40 -#define LSI_SCNTL1_EXC 0x80 - -#define LSI_SCNTL2_WSR 0x01 -#define LSI_SCNTL2_VUE0 0x02 -#define LSI_SCNTL2_VUE1 0x04 -#define LSI_SCNTL2_WSS 0x08 -#define LSI_SCNTL2_SLPHBEN 0x10 -#define LSI_SCNTL2_SLPMD 0x20 -#define LSI_SCNTL2_CHM 0x40 -#define LSI_SCNTL2_SDU 0x80 - -#define LSI_ISTAT0_DIP 0x01 -#define LSI_ISTAT0_SIP 0x02 -#define LSI_ISTAT0_INTF 0x04 -#define LSI_ISTAT0_CON 0x08 -#define LSI_ISTAT0_SEM 0x10 -#define LSI_ISTAT0_SIGP 0x20 -#define LSI_ISTAT0_SRST 0x40 -#define LSI_ISTAT0_ABRT 0x80 - -#define LSI_ISTAT1_SI 0x01 -#define LSI_ISTAT1_SRUN 0x02 -#define LSI_ISTAT1_FLSH 0x04 - -#define LSI_SSTAT0_SDP0 0x01 -#define LSI_SSTAT0_RST 0x02 -#define LSI_SSTAT0_WOA 0x04 -#define LSI_SSTAT0_LOA 0x08 -#define LSI_SSTAT0_AIP 0x10 -#define LSI_SSTAT0_OLF 0x20 -#define LSI_SSTAT0_ORF 0x40 -#define LSI_SSTAT0_ILF 0x80 - -#define LSI_SIST0_PAR 0x01 -#define LSI_SIST0_RST 0x02 -#define LSI_SIST0_UDC 0x04 -#define LSI_SIST0_SGE 0x08 -#define LSI_SIST0_RSL 0x10 -#define LSI_SIST0_SEL 0x20 -#define LSI_SIST0_CMP 0x40 -#define LSI_SIST0_MA 0x80 - -#define LSI_SIST1_HTH 0x01 -#define LSI_SIST1_GEN 0x02 -#define LSI_SIST1_STO 0x04 -#define LSI_SIST1_SBMC 0x10 - -#define LSI_SOCL_IO 0x01 -#define LSI_SOCL_CD 0x02 -#define LSI_SOCL_MSG 0x04 -#define LSI_SOCL_ATN 0x08 -#define LSI_SOCL_SEL 0x10 -#define LSI_SOCL_BSY 0x20 -#define LSI_SOCL_ACK 0x40 -#define LSI_SOCL_REQ 0x80 - -#define LSI_DSTAT_IID 0x01 -#define LSI_DSTAT_SIR 0x04 -#define LSI_DSTAT_SSI 0x08 -#define LSI_DSTAT_ABRT 0x10 -#define LSI_DSTAT_BF 0x20 -#define LSI_DSTAT_MDPE 0x40 -#define LSI_DSTAT_DFE 0x80 - -#define LSI_DCNTL_COM 0x01 -#define LSI_DCNTL_IRQD 0x02 -#define LSI_DCNTL_STD 0x04 -#define LSI_DCNTL_IRQM 0x08 -#define LSI_DCNTL_SSM 0x10 -#define LSI_DCNTL_PFEN 0x20 -#define LSI_DCNTL_PFF 0x40 -#define LSI_DCNTL_CLSE 0x80 - -#define LSI_DMODE_MAN 0x01 -#define LSI_DMODE_BOF 0x02 -#define LSI_DMODE_ERMP 0x04 -#define LSI_DMODE_ERL 0x08 -#define LSI_DMODE_DIOM 0x10 -#define LSI_DMODE_SIOM 0x20 - -#define LSI_CTEST2_DACK 0x01 -#define LSI_CTEST2_DREQ 0x02 -#define LSI_CTEST2_TEOP 0x04 -#define LSI_CTEST2_PCICIE 0x08 -#define LSI_CTEST2_CM 0x10 -#define LSI_CTEST2_CIO 0x20 -#define LSI_CTEST2_SIGP 0x40 -#define LSI_CTEST2_DDIR 0x80 - -#define LSI_CTEST5_BL2 0x04 -#define LSI_CTEST5_DDIR 0x08 -#define LSI_CTEST5_MASR 0x10 -#define LSI_CTEST5_DFSN 0x20 -#define LSI_CTEST5_BBCK 0x40 -#define LSI_CTEST5_ADCK 0x80 - -#define LSI_CCNTL0_DILS 0x01 -#define LSI_CCNTL0_DISFC 0x10 -#define LSI_CCNTL0_ENNDJ 0x20 -#define LSI_CCNTL0_PMJCTL 0x40 -#define LSI_CCNTL0_ENPMJ 0x80 - -#define LSI_CCNTL1_EN64DBMV 0x01 -#define LSI_CCNTL1_EN64TIBMV 0x02 -#define LSI_CCNTL1_64TIMOD 0x04 -#define LSI_CCNTL1_DDAC 0x08 -#define LSI_CCNTL1_ZMOD 0x80 - -/* Enable Response to Reselection */ -#define LSI_SCID_RRE 0x60 - -#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD) - -#define PHASE_DO 0 -#define PHASE_DI 1 -#define PHASE_CMD 2 -#define PHASE_ST 3 -#define PHASE_MO 6 -#define PHASE_MI 7 -#define PHASE_MASK 7 - -/* Maximum length of MSG IN data. */ -#define LSI_MAX_MSGIN_LEN 8 - -/* Flag set if this is a tagged command. */ -#define LSI_TAG_VALID (1 << 16) - -typedef struct lsi_request { - SCSIRequest *req; - uint32_t tag; - uint32_t dma_len; - uint8_t *dma_buf; - uint32_t pending; - int out; - QTAILQ_ENTRY(lsi_request) next; -} lsi_request; - -typedef struct { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion mmio_io; - MemoryRegion ram_io; - MemoryRegion io_io; - - int carry; /* ??? Should this be an a visible register somewhere? */ - int status; - /* Action to take at the end of a MSG IN phase. - 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ - int msg_action; - int msg_len; - uint8_t msg[LSI_MAX_MSGIN_LEN]; - /* 0 if SCRIPTS are running or stopped. - * 1 if a Wait Reselect instruction has been issued. - * 2 if processing DMA from lsi_execute_script. - * 3 if a DMA operation is in progress. */ - int waiting; - SCSIBus bus; - int current_lun; - /* The tag is a combination of the device ID and the SCSI tag. */ - uint32_t select_tag; - int command_complete; - QTAILQ_HEAD(, lsi_request) queue; - lsi_request *current; - - uint32_t dsa; - uint32_t temp; - uint32_t dnad; - uint32_t dbc; - uint8_t istat0; - uint8_t istat1; - uint8_t dcmd; - uint8_t dstat; - uint8_t dien; - uint8_t sist0; - uint8_t sist1; - uint8_t sien0; - uint8_t sien1; - uint8_t mbox0; - uint8_t mbox1; - uint8_t dfifo; - uint8_t ctest2; - uint8_t ctest3; - uint8_t ctest4; - uint8_t ctest5; - uint8_t ccntl0; - uint8_t ccntl1; - uint32_t dsp; - uint32_t dsps; - uint8_t dmode; - uint8_t dcntl; - uint8_t scntl0; - uint8_t scntl1; - uint8_t scntl2; - uint8_t scntl3; - uint8_t sstat0; - uint8_t sstat1; - uint8_t scid; - uint8_t sxfer; - uint8_t socl; - uint8_t sdid; - uint8_t ssid; - uint8_t sfbr; - uint8_t stest1; - uint8_t stest2; - uint8_t stest3; - uint8_t sidl; - uint8_t stime0; - uint8_t respid0; - uint8_t respid1; - uint32_t mmrs; - uint32_t mmws; - uint32_t sfs; - uint32_t drs; - uint32_t sbms; - uint32_t dbms; - uint32_t dnad64; - uint32_t pmjad1; - uint32_t pmjad2; - uint32_t rbc; - uint32_t ua; - uint32_t ia; - uint32_t sbc; - uint32_t csbc; - uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */ - uint8_t sbr; - uint32_t adder; - - /* Script ram is stored as 32-bit words in host byteorder. */ - uint32_t script_ram[2048]; -} LSIState; - -#define TYPE_LSI53C810 "lsi53c810" -#define TYPE_LSI53C895A "lsi53c895a" - -#define LSI53C895A(obj) \ - OBJECT_CHECK(LSIState, (obj), TYPE_LSI53C895A) - -static inline int lsi_irq_on_rsl(LSIState *s) -{ - return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE); -} - -static void lsi_soft_reset(LSIState *s) -{ - DPRINTF("Reset\n"); - s->carry = 0; - - s->msg_action = 0; - s->msg_len = 0; - s->waiting = 0; - s->dsa = 0; - s->dnad = 0; - s->dbc = 0; - s->temp = 0; - memset(s->scratch, 0, sizeof(s->scratch)); - s->istat0 = 0; - s->istat1 = 0; - s->dcmd = 0x40; - s->dstat = LSI_DSTAT_DFE; - s->dien = 0; - s->sist0 = 0; - s->sist1 = 0; - s->sien0 = 0; - s->sien1 = 0; - s->mbox0 = 0; - s->mbox1 = 0; - s->dfifo = 0; - s->ctest2 = LSI_CTEST2_DACK; - s->ctest3 = 0; - s->ctest4 = 0; - s->ctest5 = 0; - s->ccntl0 = 0; - s->ccntl1 = 0; - s->dsp = 0; - s->dsps = 0; - s->dmode = 0; - s->dcntl = 0; - s->scntl0 = 0xc0; - s->scntl1 = 0; - s->scntl2 = 0; - s->scntl3 = 0; - s->sstat0 = 0; - s->sstat1 = 0; - s->scid = 7; - s->sxfer = 0; - s->socl = 0; - s->sdid = 0; - s->ssid = 0; - s->stest1 = 0; - s->stest2 = 0; - s->stest3 = 0; - s->sidl = 0; - s->stime0 = 0; - s->respid0 = 0x80; - s->respid1 = 0; - s->mmrs = 0; - s->mmws = 0; - s->sfs = 0; - s->drs = 0; - s->sbms = 0; - s->dbms = 0; - s->dnad64 = 0; - s->pmjad1 = 0; - s->pmjad2 = 0; - s->rbc = 0; - s->ua = 0; - s->ia = 0; - s->sbc = 0; - s->csbc = 0; - s->sbr = 0; - assert(QTAILQ_EMPTY(&s->queue)); - assert(!s->current); -} - -static int lsi_dma_40bit(LSIState *s) -{ - if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT) - return 1; - return 0; -} - -static int lsi_dma_ti64bit(LSIState *s) -{ - if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV) - return 1; - return 0; -} - -static int lsi_dma_64bit(LSIState *s) -{ - if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV) - return 1; - return 0; -} - -static uint8_t lsi_reg_readb(LSIState *s, int offset); -static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); -static void lsi_execute_script(LSIState *s); -static void lsi_reselect(LSIState *s, lsi_request *p); - -static inline uint32_t read_dword(LSIState *s, uint32_t addr) -{ - uint32_t buf; - - pci_dma_read(PCI_DEVICE(s), addr, &buf, 4); - return cpu_to_le32(buf); -} - -static void lsi_stop_script(LSIState *s) -{ - s->istat1 &= ~LSI_ISTAT1_SRUN; -} - -static void lsi_update_irq(LSIState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int level; - static int last_level; - lsi_request *p; - - /* It's unclear whether the DIP/SIP bits should be cleared when the - Interrupt Status Registers are cleared or when istat0 is read. - We currently do the formwer, which seems to work. */ - level = 0; - if (s->dstat) { - if (s->dstat & s->dien) - level = 1; - s->istat0 |= LSI_ISTAT0_DIP; - } else { - s->istat0 &= ~LSI_ISTAT0_DIP; - } - - if (s->sist0 || s->sist1) { - if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1)) - level = 1; - s->istat0 |= LSI_ISTAT0_SIP; - } else { - s->istat0 &= ~LSI_ISTAT0_SIP; - } - if (s->istat0 & LSI_ISTAT0_INTF) - level = 1; - - if (level != last_level) { - DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n", - level, s->dstat, s->sist1, s->sist0); - last_level = level; - } - pci_set_irq(d, level); - - if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { - DPRINTF("Handled IRQs & disconnected, looking for pending " - "processes\n"); - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } - } - } -} - -/* Stop SCRIPTS execution and raise a SCSI interrupt. */ -static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1) -{ - uint32_t mask0; - uint32_t mask1; - - DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n", - stat1, stat0, s->sist1, s->sist0); - s->sist0 |= stat0; - s->sist1 |= stat1; - /* Stop processor on fatal or unmasked interrupt. As a special hack - we don't stop processing when raising STO. Instead continue - execution and stop at the next insn that accesses the SCSI bus. */ - mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL); - mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH); - mask1 &= ~LSI_SIST1_STO; - if (s->sist0 & mask0 || s->sist1 & mask1) { - lsi_stop_script(s); - } - lsi_update_irq(s); -} - -/* Stop SCRIPTS execution and raise a DMA interrupt. */ -static void lsi_script_dma_interrupt(LSIState *s, int stat) -{ - DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat); - s->dstat |= stat; - lsi_update_irq(s); - lsi_stop_script(s); -} - -static inline void lsi_set_phase(LSIState *s, int phase) -{ - s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase; -} - -static void lsi_bad_phase(LSIState *s, int out, int new_phase) -{ - /* Trigger a phase mismatch. */ - if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { - if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { - s->dsp = out ? s->pmjad1 : s->pmjad2; - } else { - s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1); - } - DPRINTF("Data phase mismatch jump to %08x\n", s->dsp); - } else { - DPRINTF("Phase mismatch interrupt\n"); - lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); - lsi_stop_script(s); - } - lsi_set_phase(s, new_phase); -} - - -/* Resume SCRIPTS execution after a DMA operation. */ -static void lsi_resume_script(LSIState *s) -{ - if (s->waiting != 2) { - s->waiting = 0; - lsi_execute_script(s); - } else { - s->waiting = 0; - } -} - -static void lsi_disconnect(LSIState *s) -{ - s->scntl1 &= ~LSI_SCNTL1_CON; - s->sstat1 &= ~PHASE_MASK; -} - -static void lsi_bad_selection(LSIState *s, uint32_t id) -{ - DPRINTF("Selected absent target %d\n", id); - lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); - lsi_disconnect(s); -} - -/* Initiate a SCSI layer data transfer. */ -static void lsi_do_dma(LSIState *s, int out) -{ - PCIDevice *pci_dev; - uint32_t count; - dma_addr_t addr; - SCSIDevice *dev; - - assert(s->current); - if (!s->current->dma_len) { - /* Wait until data is available. */ - DPRINTF("DMA no data available\n"); - return; - } - - pci_dev = PCI_DEVICE(s); - dev = s->current->req->dev; - assert(dev); - - count = s->dbc; - if (count > s->current->dma_len) - count = s->current->dma_len; - - addr = s->dnad; - /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */ - if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s)) - addr |= ((uint64_t)s->dnad64 << 32); - else if (s->dbms) - addr |= ((uint64_t)s->dbms << 32); - else if (s->sbms) - addr |= ((uint64_t)s->sbms << 32); - - DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count); - s->csbc += count; - s->dnad += count; - s->dbc -= count; - if (s->current->dma_buf == NULL) { - s->current->dma_buf = scsi_req_get_buf(s->current->req); - } - /* ??? Set SFBR to first data byte. */ - if (out) { - pci_dma_read(pci_dev, addr, s->current->dma_buf, count); - } else { - pci_dma_write(pci_dev, addr, s->current->dma_buf, count); - } - s->current->dma_len -= count; - if (s->current->dma_len == 0) { - s->current->dma_buf = NULL; - scsi_req_continue(s->current->req); - } else { - s->current->dma_buf += count; - lsi_resume_script(s); - } -} - - -/* Add a command to the queue. */ -static void lsi_queue_command(LSIState *s) -{ - lsi_request *p = s->current; - - DPRINTF("Queueing tag=0x%x\n", p->tag); - assert(s->current != NULL); - assert(s->current->dma_len == 0); - QTAILQ_INSERT_TAIL(&s->queue, s->current, next); - s->current = NULL; - - p->pending = 0; - p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO; -} - -/* Queue a byte for a MSG IN phase. */ -static void lsi_add_msg_byte(LSIState *s, uint8_t data) -{ - if (s->msg_len >= LSI_MAX_MSGIN_LEN) { - BADF("MSG IN data too long\n"); - } else { - DPRINTF("MSG IN 0x%02x\n", data); - s->msg[s->msg_len++] = data; - } -} - -/* Perform reselection to continue a command. */ -static void lsi_reselect(LSIState *s, lsi_request *p) -{ - int id; - - assert(s->current == NULL); - QTAILQ_REMOVE(&s->queue, p, next); - s->current = p; - - id = (p->tag >> 8) & 0xf; - s->ssid = id | 0x80; - /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */ - if (!(s->dcntl & LSI_DCNTL_COM)) { - s->sfbr = 1 << (id & 0x7); - } - DPRINTF("Reselected target %d\n", id); - s->scntl1 |= LSI_SCNTL1_CON; - lsi_set_phase(s, PHASE_MI); - s->msg_action = p->out ? 2 : 3; - s->current->dma_len = p->pending; - lsi_add_msg_byte(s, 0x80); - if (s->current->tag & LSI_TAG_VALID) { - lsi_add_msg_byte(s, 0x20); - lsi_add_msg_byte(s, p->tag & 0xff); - } - - if (lsi_irq_on_rsl(s)) { - lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0); - } -} - -static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) -{ - lsi_request *p; - - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->tag == tag) { - return p; - } - } - - return NULL; -} - -static void lsi_request_free(LSIState *s, lsi_request *p) -{ - if (p == s->current) { - s->current = NULL; - } else { - QTAILQ_REMOVE(&s->queue, p, next); - } - g_free(p); -} - -static void lsi_request_cancelled(SCSIRequest *req) -{ - LSIState *s = LSI53C895A(req->bus->qbus.parent); - lsi_request *p = req->hba_private; - - req->hba_private = NULL; - lsi_request_free(s, p); - scsi_req_unref(req); -} - -/* Record that data is available for a queued command. Returns zero if - the device was reselected, nonzero if the IO is deferred. */ -static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) -{ - lsi_request *p = req->hba_private; - - if (p->pending) { - BADF("Multiple IO pending for request %p\n", p); - } - p->pending = len; - /* Reselect if waiting for it, or if reselection triggers an IRQ - and the bus is free. - Since no interrupt stacking is implemented in the emulation, it - is also required that there are no pending interrupts waiting - for service from the device driver. */ - if (s->waiting == 1 || - (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && - !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { - /* Reselect device. */ - lsi_reselect(s, p); - return 0; - } else { - DPRINTF("Queueing IO tag=0x%x\n", p->tag); - p->pending = len; - return 1; - } -} - - /* Callback to indicate that the SCSI layer has completed a command. */ -static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid) -{ - LSIState *s = LSI53C895A(req->bus->qbus.parent); - int out; - - out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - DPRINTF("Command complete status=%d\n", (int)status); - s->status = status; - s->command_complete = 2; - if (s->waiting && s->dbc != 0) { - /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); - } else { - lsi_set_phase(s, PHASE_ST); - } - - if (req->hba_private == s->current) { - req->hba_private = NULL; - lsi_request_free(s, s->current); - scsi_req_unref(req); - } - lsi_resume_script(s); -} - - /* Callback to indicate that the SCSI layer has completed a transfer. */ -static void lsi_transfer_data(SCSIRequest *req, uint32_t len) -{ - LSIState *s = LSI53C895A(req->bus->qbus.parent); - int out; - - assert(req->hba_private); - if (s->waiting == 1 || req->hba_private != s->current || - (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_req(s, req, len)) { - return; - } - } - - out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - - /* host adapter (re)connected */ - DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len); - s->current->dma_len = len; - s->command_complete = 1; - if (s->waiting) { - if (s->waiting == 1 || s->dbc == 0) { - lsi_resume_script(s); - } else { - lsi_do_dma(s, out); - } - } -} - -static void lsi_do_command(LSIState *s) -{ - SCSIDevice *dev; - uint8_t buf[16]; - uint32_t id; - int n; - - DPRINTF("Send command len=%d\n", s->dbc); - if (s->dbc > 16) - s->dbc = 16; - pci_dma_read(PCI_DEVICE(s), s->dnad, buf, s->dbc); - s->sfbr = buf[0]; - s->command_complete = 0; - - id = (s->select_tag >> 8) & 0xf; - dev = scsi_device_find(&s->bus, 0, id, s->current_lun); - if (!dev) { - lsi_bad_selection(s, id); - return; - } - - assert(s->current == NULL); - s->current = g_new0(lsi_request, 1); - s->current->tag = s->select_tag; - s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, buf, - s->current); - - n = scsi_req_enqueue(s->current->req); - if (n) { - if (n > 0) { - lsi_set_phase(s, PHASE_DI); - } else if (n < 0) { - lsi_set_phase(s, PHASE_DO); - } - scsi_req_continue(s->current->req); - } - if (!s->command_complete) { - if (n) { - /* Command did not complete immediately so disconnect. */ - lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */ - lsi_add_msg_byte(s, 4); /* DISCONNECT */ - /* wait data */ - lsi_set_phase(s, PHASE_MI); - s->msg_action = 1; - lsi_queue_command(s); - } else { - /* wait command complete */ - lsi_set_phase(s, PHASE_DI); - } - } -} - -static void lsi_do_status(LSIState *s) -{ - uint8_t status; - DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status); - if (s->dbc != 1) - BADF("Bad Status move\n"); - s->dbc = 1; - status = s->status; - s->sfbr = status; - pci_dma_write(PCI_DEVICE(s), s->dnad, &status, 1); - lsi_set_phase(s, PHASE_MI); - s->msg_action = 1; - lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ -} - -static void lsi_do_msgin(LSIState *s) -{ - int len; - DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len); - s->sfbr = s->msg[0]; - len = s->msg_len; - if (len > s->dbc) - len = s->dbc; - pci_dma_write(PCI_DEVICE(s), s->dnad, s->msg, len); - /* Linux drivers rely on the last byte being in the SIDL. */ - s->sidl = s->msg[len - 1]; - s->msg_len -= len; - if (s->msg_len) { - memmove(s->msg, s->msg + len, s->msg_len); - } else { - /* ??? Check if ATN (not yet implemented) is asserted and maybe - switch to PHASE_MO. */ - switch (s->msg_action) { - case 0: - lsi_set_phase(s, PHASE_CMD); - break; - case 1: - lsi_disconnect(s); - break; - case 2: - lsi_set_phase(s, PHASE_DO); - break; - case 3: - lsi_set_phase(s, PHASE_DI); - break; - default: - abort(); - } - } -} - -/* Read the next byte during a MSGOUT phase. */ -static uint8_t lsi_get_msgbyte(LSIState *s) -{ - uint8_t data; - pci_dma_read(PCI_DEVICE(s), s->dnad, &data, 1); - s->dnad++; - s->dbc--; - return data; -} - -/* Skip the next n bytes during a MSGOUT phase. */ -static void lsi_skip_msgbytes(LSIState *s, unsigned int n) -{ - s->dnad += n; - s->dbc -= n; -} - -static void lsi_do_msgout(LSIState *s) -{ - uint8_t msg; - int len; - uint32_t current_tag; - lsi_request *current_req, *p, *p_next; - - if (s->current) { - current_tag = s->current->tag; - current_req = s->current; - } else { - current_tag = s->select_tag; - current_req = lsi_find_by_tag(s, current_tag); - } - - DPRINTF("MSG out len=%d\n", s->dbc); - while (s->dbc) { - msg = lsi_get_msgbyte(s); - s->sfbr = msg; - - switch (msg) { - case 0x04: - DPRINTF("MSG: Disconnect\n"); - lsi_disconnect(s); - break; - case 0x08: - DPRINTF("MSG: No Operation\n"); - lsi_set_phase(s, PHASE_CMD); - break; - case 0x01: - len = lsi_get_msgbyte(s); - msg = lsi_get_msgbyte(s); - (void)len; /* avoid a warning about unused variable*/ - DPRINTF("Extended message 0x%x (len %d)\n", msg, len); - switch (msg) { - case 1: - DPRINTF("SDTR (ignored)\n"); - lsi_skip_msgbytes(s, 2); - break; - case 3: - DPRINTF("WDTR (ignored)\n"); - lsi_skip_msgbytes(s, 1); - break; - default: - goto bad; - } - break; - case 0x20: /* SIMPLE queue */ - s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff); - break; - case 0x21: /* HEAD of queue */ - BADF("HEAD queue not implemented\n"); - s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - break; - case 0x22: /* ORDERED queue */ - BADF("ORDERED queue not implemented\n"); - s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - break; - case 0x0d: - /* The ABORT TAG message clears the current I/O process only. */ - DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); - if (current_req) { - scsi_req_cancel(current_req->req); - } - lsi_disconnect(s); - break; - case 0x06: - case 0x0e: - case 0x0c: - /* The ABORT message clears all I/O processes for the selecting - initiator on the specified logical unit of the target. */ - if (msg == 0x06) { - DPRINTF("MSG: ABORT tag=0x%x\n", current_tag); - } - /* The CLEAR QUEUE message clears all I/O processes for all - initiators on the specified logical unit of the target. */ - if (msg == 0x0e) { - DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag); - } - /* The BUS DEVICE RESET message clears all I/O processes for all - initiators on all logical units of the target. */ - if (msg == 0x0c) { - DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag); - } - - /* clear the current I/O process */ - if (s->current) { - scsi_req_cancel(s->current->req); - } - - /* As the current implemented devices scsi_disk and scsi_generic - only support one LUN, we don't need to keep track of LUNs. - Clearing I/O processes for other initiators could be possible - for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX - device, but this is currently not implemented (and seems not - to be really necessary). So let's simply clear all queued - commands for the current device: */ - QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { - if ((p->tag & 0x0000ff00) == (current_tag & 0x0000ff00)) { - scsi_req_cancel(p->req); - } - } - - lsi_disconnect(s); - break; - default: - if ((msg & 0x80) == 0) { - goto bad; - } - s->current_lun = msg & 7; - DPRINTF("Select LUN %d\n", s->current_lun); - lsi_set_phase(s, PHASE_CMD); - break; - } - } - return; -bad: - BADF("Unimplemented message 0x%02x\n", msg); - lsi_set_phase(s, PHASE_MI); - lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */ - s->msg_action = 0; -} - -#define LSI_BUF_SIZE 4096 -static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) -{ - PCIDevice *d = PCI_DEVICE(s); - int n; - uint8_t buf[LSI_BUF_SIZE]; - - DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count); - while (count) { - n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count; - pci_dma_read(d, src, buf, n); - pci_dma_write(d, dest, buf, n); - src += n; - dest += n; - count -= n; - } -} - -static void lsi_wait_reselect(LSIState *s) -{ - lsi_request *p; - - DPRINTF("Wait Reselect\n"); - - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } - } - if (s->current == NULL) { - s->waiting = 1; - } -} - -static void lsi_execute_script(LSIState *s) -{ - PCIDevice *pci_dev = PCI_DEVICE(s); - uint32_t insn; - uint32_t addr, addr_high; - int opcode; - int insn_processed = 0; - - s->istat1 |= LSI_ISTAT1_SRUN; -again: - insn_processed++; - insn = read_dword(s, s->dsp); - if (!insn) { - /* If we receive an empty opcode increment the DSP by 4 bytes - instead of 8 and execute the next opcode at that location */ - s->dsp += 4; - goto again; - } - addr = read_dword(s, s->dsp + 4); - addr_high = 0; - DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr); - s->dsps = addr; - s->dcmd = insn >> 24; - s->dsp += 8; - switch (insn >> 30) { - case 0: /* Block move. */ - if (s->sist1 & LSI_SIST1_STO) { - DPRINTF("Delayed select timeout\n"); - lsi_stop_script(s); - break; - } - s->dbc = insn & 0xffffff; - s->rbc = s->dbc; - /* ??? Set ESA. */ - s->ia = s->dsp - 8; - if (insn & (1 << 29)) { - /* Indirect addressing. */ - addr = read_dword(s, addr); - } else if (insn & (1 << 28)) { - uint32_t buf[2]; - int32_t offset; - /* Table indirect addressing. */ - - /* 32-bit Table indirect */ - offset = sextract32(addr, 0, 24); - pci_dma_read(pci_dev, s->dsa + offset, buf, 8); - /* byte count is stored in bits 0:23 only */ - s->dbc = cpu_to_le32(buf[0]) & 0xffffff; - s->rbc = s->dbc; - addr = cpu_to_le32(buf[1]); - - /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of - * table, bits [31:24] */ - if (lsi_dma_40bit(s)) - addr_high = cpu_to_le32(buf[0]) >> 24; - else if (lsi_dma_ti64bit(s)) { - int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f; - switch (selector) { - case 0 ... 0x0f: - /* offset index into scratch registers since - * TI64 mode can use registers C to R */ - addr_high = s->scratch[2 + selector]; - break; - case 0x10: - addr_high = s->mmrs; - break; - case 0x11: - addr_high = s->mmws; - break; - case 0x12: - addr_high = s->sfs; - break; - case 0x13: - addr_high = s->drs; - break; - case 0x14: - addr_high = s->sbms; - break; - case 0x15: - addr_high = s->dbms; - break; - default: - BADF("Illegal selector specified (0x%x > 0x15)" - " for 64-bit DMA block move", selector); - break; - } - } - } else if (lsi_dma_64bit(s)) { - /* fetch a 3rd dword if 64-bit direct move is enabled and - only if we're not doing table indirect or indirect addressing */ - s->dbms = read_dword(s, s->dsp); - s->dsp += 4; - s->ia = s->dsp - 12; - } - if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { - DPRINTF("Wrong phase got %d expected %d\n", - s->sstat1 & PHASE_MASK, (insn >> 24) & 7); - lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); - break; - } - s->dnad = addr; - s->dnad64 = addr_high; - switch (s->sstat1 & 0x7) { - case PHASE_DO: - s->waiting = 2; - lsi_do_dma(s, 1); - if (s->waiting) - s->waiting = 3; - break; - case PHASE_DI: - s->waiting = 2; - lsi_do_dma(s, 0); - if (s->waiting) - s->waiting = 3; - break; - case PHASE_CMD: - lsi_do_command(s); - break; - case PHASE_ST: - lsi_do_status(s); - break; - case PHASE_MO: - lsi_do_msgout(s); - break; - case PHASE_MI: - lsi_do_msgin(s); - break; - default: - BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK); - exit(1); - } - s->dfifo = s->dbc & 0xff; - s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3); - s->sbc = s->dbc; - s->rbc -= s->dbc; - s->ua = addr + s->dbc; - break; - - case 1: /* IO or Read/Write instruction. */ - opcode = (insn >> 27) & 7; - if (opcode < 5) { - uint32_t id; - - if (insn & (1 << 25)) { - id = read_dword(s, s->dsa + sextract32(insn, 0, 24)); - } else { - id = insn; - } - id = (id >> 16) & 0xf; - if (insn & (1 << 26)) { - addr = s->dsp + sextract32(addr, 0, 24); - } - s->dnad = addr; - switch (opcode) { - case 0: /* Select */ - s->sdid = id; - if (s->scntl1 & LSI_SCNTL1_CON) { - DPRINTF("Already reselected, jumping to alternative address\n"); - s->dsp = s->dnad; - break; - } - s->sstat0 |= LSI_SSTAT0_WOA; - s->scntl1 &= ~LSI_SCNTL1_IARB; - if (!scsi_device_find(&s->bus, 0, id, 0)) { - lsi_bad_selection(s, id); - break; - } - DPRINTF("Selected target %d%s\n", - id, insn & (1 << 3) ? " ATN" : ""); - /* ??? Linux drivers compain when this is set. Maybe - it only applies in low-level mode (unimplemented). - lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ - s->select_tag = id << 8; - s->scntl1 |= LSI_SCNTL1_CON; - if (insn & (1 << 3)) { - s->socl |= LSI_SOCL_ATN; - } - lsi_set_phase(s, PHASE_MO); - break; - case 1: /* Disconnect */ - DPRINTF("Wait Disconnect\n"); - s->scntl1 &= ~LSI_SCNTL1_CON; - break; - case 2: /* Wait Reselect */ - if (!lsi_irq_on_rsl(s)) { - lsi_wait_reselect(s); - } - break; - case 3: /* Set */ - DPRINTF("Set%s%s%s%s\n", - insn & (1 << 3) ? " ATN" : "", - insn & (1 << 6) ? " ACK" : "", - insn & (1 << 9) ? " TM" : "", - insn & (1 << 10) ? " CC" : ""); - if (insn & (1 << 3)) { - s->socl |= LSI_SOCL_ATN; - lsi_set_phase(s, PHASE_MO); - } - if (insn & (1 << 9)) { - BADF("Target mode not implemented\n"); - exit(1); - } - if (insn & (1 << 10)) - s->carry = 1; - break; - case 4: /* Clear */ - DPRINTF("Clear%s%s%s%s\n", - insn & (1 << 3) ? " ATN" : "", - insn & (1 << 6) ? " ACK" : "", - insn & (1 << 9) ? " TM" : "", - insn & (1 << 10) ? " CC" : ""); - if (insn & (1 << 3)) { - s->socl &= ~LSI_SOCL_ATN; - } - if (insn & (1 << 10)) - s->carry = 0; - break; - } - } else { - uint8_t op0; - uint8_t op1; - uint8_t data8; - int reg; - int operator; -#ifdef DEBUG_LSI - static const char *opcode_names[3] = - {"Write", "Read", "Read-Modify-Write"}; - static const char *operator_names[8] = - {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"}; -#endif - - reg = ((insn >> 16) & 0x7f) | (insn & 0x80); - data8 = (insn >> 8) & 0xff; - opcode = (insn >> 27) & 7; - operator = (insn >> 24) & 7; - DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n", - opcode_names[opcode - 5], reg, - operator_names[operator], data8, s->sfbr, - (insn & (1 << 23)) ? " SFBR" : ""); - op0 = op1 = 0; - switch (opcode) { - case 5: /* From SFBR */ - op0 = s->sfbr; - op1 = data8; - break; - case 6: /* To SFBR */ - if (operator) - op0 = lsi_reg_readb(s, reg); - op1 = data8; - break; - case 7: /* Read-modify-write */ - if (operator) - op0 = lsi_reg_readb(s, reg); - if (insn & (1 << 23)) { - op1 = s->sfbr; - } else { - op1 = data8; - } - break; - } - - switch (operator) { - case 0: /* move */ - op0 = op1; - break; - case 1: /* Shift left */ - op1 = op0 >> 7; - op0 = (op0 << 1) | s->carry; - s->carry = op1; - break; - case 2: /* OR */ - op0 |= op1; - break; - case 3: /* XOR */ - op0 ^= op1; - break; - case 4: /* AND */ - op0 &= op1; - break; - case 5: /* SHR */ - op1 = op0 & 1; - op0 = (op0 >> 1) | (s->carry << 7); - s->carry = op1; - break; - case 6: /* ADD */ - op0 += op1; - s->carry = op0 < op1; - break; - case 7: /* ADC */ - op0 += op1 + s->carry; - if (s->carry) - s->carry = op0 <= op1; - else - s->carry = op0 < op1; - break; - } - - switch (opcode) { - case 5: /* From SFBR */ - case 7: /* Read-modify-write */ - lsi_reg_writeb(s, reg, op0); - break; - case 6: /* To SFBR */ - s->sfbr = op0; - break; - } - } - break; - - case 2: /* Transfer Control. */ - { - int cond; - int jmp; - - if ((insn & 0x002e0000) == 0) { - DPRINTF("NOP\n"); - break; - } - if (s->sist1 & LSI_SIST1_STO) { - DPRINTF("Delayed select timeout\n"); - lsi_stop_script(s); - break; - } - cond = jmp = (insn & (1 << 19)) != 0; - if (cond == jmp && (insn & (1 << 21))) { - DPRINTF("Compare carry %d\n", s->carry == jmp); - cond = s->carry != 0; - } - if (cond == jmp && (insn & (1 << 17))) { - DPRINTF("Compare phase %d %c= %d\n", - (s->sstat1 & PHASE_MASK), - jmp ? '=' : '!', - ((insn >> 24) & 7)); - cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7); - } - if (cond == jmp && (insn & (1 << 18))) { - uint8_t mask; - - mask = (~insn >> 8) & 0xff; - DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n", - s->sfbr, mask, jmp ? '=' : '!', insn & mask); - cond = (s->sfbr & mask) == (insn & mask); - } - if (cond == jmp) { - if (insn & (1 << 23)) { - /* Relative address. */ - addr = s->dsp + sextract32(addr, 0, 24); - } - switch ((insn >> 27) & 7) { - case 0: /* Jump */ - DPRINTF("Jump to 0x%08x\n", addr); - s->adder = addr; - s->dsp = addr; - break; - case 1: /* Call */ - DPRINTF("Call 0x%08x\n", addr); - s->temp = s->dsp; - s->dsp = addr; - break; - case 2: /* Return */ - DPRINTF("Return to 0x%08x\n", s->temp); - s->dsp = s->temp; - break; - case 3: /* Interrupt */ - DPRINTF("Interrupt 0x%08x\n", s->dsps); - if ((insn & (1 << 20)) != 0) { - s->istat0 |= LSI_ISTAT0_INTF; - lsi_update_irq(s); - } else { - lsi_script_dma_interrupt(s, LSI_DSTAT_SIR); - } - break; - default: - DPRINTF("Illegal transfer control\n"); - lsi_script_dma_interrupt(s, LSI_DSTAT_IID); - break; - } - } else { - DPRINTF("Control condition failed\n"); - } - } - break; - - case 3: - if ((insn & (1 << 29)) == 0) { - /* Memory move. */ - uint32_t dest; - /* ??? The docs imply the destination address is loaded into - the TEMP register. However the Linux drivers rely on - the value being presrved. */ - dest = read_dword(s, s->dsp); - s->dsp += 4; - lsi_memcpy(s, dest, addr, insn & 0xffffff); - } else { - uint8_t data[7]; - int reg; - int n; - int i; - - if (insn & (1 << 28)) { - addr = s->dsa + sextract32(addr, 0, 24); - } - n = (insn & 7); - reg = (insn >> 16) & 0xff; - if (insn & (1 << 24)) { - pci_dma_read(pci_dev, addr, data, n); - DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n, - addr, *(int *)data); - for (i = 0; i < n; i++) { - lsi_reg_writeb(s, reg + i, data[i]); - } - } else { - DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr); - for (i = 0; i < n; i++) { - data[i] = lsi_reg_readb(s, reg + i); - } - pci_dma_write(pci_dev, addr, data, n); - } - } - } - if (insn_processed > 10000 && !s->waiting) { - /* Some windows drivers make the device spin waiting for a memory - location to change. If we have been executed a lot of code then - assume this is the case and force an unexpected device disconnect. - This is apparently sufficient to beat the drivers into submission. - */ - if (!(s->sien0 & LSI_SIST0_UDC)) - fprintf(stderr, "inf. loop with UDC masked\n"); - lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); - lsi_disconnect(s); - } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) { - if (s->dcntl & LSI_DCNTL_SSM) { - lsi_script_dma_interrupt(s, LSI_DSTAT_SSI); - } else { - goto again; - } - } - DPRINTF("SCRIPTS execution stopped\n"); -} - -static uint8_t lsi_reg_readb(LSIState *s, int offset) -{ - uint8_t tmp; -#define CASE_GET_REG24(name, addr) \ - case addr: return s->name & 0xff; \ - case addr + 1: return (s->name >> 8) & 0xff; \ - case addr + 2: return (s->name >> 16) & 0xff; - -#define CASE_GET_REG32(name, addr) \ - case addr: return s->name & 0xff; \ - case addr + 1: return (s->name >> 8) & 0xff; \ - case addr + 2: return (s->name >> 16) & 0xff; \ - case addr + 3: return (s->name >> 24) & 0xff; - -#ifdef DEBUG_LSI_REG - DPRINTF("Read reg %x\n", offset); -#endif - switch (offset) { - case 0x00: /* SCNTL0 */ - return s->scntl0; - case 0x01: /* SCNTL1 */ - return s->scntl1; - case 0x02: /* SCNTL2 */ - return s->scntl2; - case 0x03: /* SCNTL3 */ - return s->scntl3; - case 0x04: /* SCID */ - return s->scid; - case 0x05: /* SXFER */ - return s->sxfer; - case 0x06: /* SDID */ - return s->sdid; - case 0x07: /* GPREG0 */ - return 0x7f; - case 0x08: /* Revision ID */ - return 0x00; - case 0x09: /* SOCL */ - return s->socl; - case 0xa: /* SSID */ - return s->ssid; - case 0xb: /* SBCL */ - /* ??? This is not correct. However it's (hopefully) only - used for diagnostics, so should be ok. */ - return 0; - case 0xc: /* DSTAT */ - tmp = s->dstat | LSI_DSTAT_DFE; - if ((s->istat0 & LSI_ISTAT0_INTF) == 0) - s->dstat = 0; - lsi_update_irq(s); - return tmp; - case 0x0d: /* SSTAT0 */ - return s->sstat0; - case 0x0e: /* SSTAT1 */ - return s->sstat1; - case 0x0f: /* SSTAT2 */ - return s->scntl1 & LSI_SCNTL1_CON ? 0 : 2; - CASE_GET_REG32(dsa, 0x10) - case 0x14: /* ISTAT0 */ - return s->istat0; - case 0x15: /* ISTAT1 */ - return s->istat1; - case 0x16: /* MBOX0 */ - return s->mbox0; - case 0x17: /* MBOX1 */ - return s->mbox1; - case 0x18: /* CTEST0 */ - return 0xff; - case 0x19: /* CTEST1 */ - return 0; - case 0x1a: /* CTEST2 */ - tmp = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM; - if (s->istat0 & LSI_ISTAT0_SIGP) { - s->istat0 &= ~LSI_ISTAT0_SIGP; - tmp |= LSI_CTEST2_SIGP; - } - return tmp; - case 0x1b: /* CTEST3 */ - return s->ctest3; - CASE_GET_REG32(temp, 0x1c) - case 0x20: /* DFIFO */ - return 0; - case 0x21: /* CTEST4 */ - return s->ctest4; - case 0x22: /* CTEST5 */ - return s->ctest5; - case 0x23: /* CTEST6 */ - return 0; - CASE_GET_REG24(dbc, 0x24) - case 0x27: /* DCMD */ - return s->dcmd; - CASE_GET_REG32(dnad, 0x28) - CASE_GET_REG32(dsp, 0x2c) - CASE_GET_REG32(dsps, 0x30) - CASE_GET_REG32(scratch[0], 0x34) - case 0x38: /* DMODE */ - return s->dmode; - case 0x39: /* DIEN */ - return s->dien; - case 0x3a: /* SBR */ - return s->sbr; - case 0x3b: /* DCNTL */ - return s->dcntl; - /* ADDER Output (Debug of relative jump address) */ - CASE_GET_REG32(adder, 0x3c) - case 0x40: /* SIEN0 */ - return s->sien0; - case 0x41: /* SIEN1 */ - return s->sien1; - case 0x42: /* SIST0 */ - tmp = s->sist0; - s->sist0 = 0; - lsi_update_irq(s); - return tmp; - case 0x43: /* SIST1 */ - tmp = s->sist1; - s->sist1 = 0; - lsi_update_irq(s); - return tmp; - case 0x46: /* MACNTL */ - return 0x0f; - case 0x47: /* GPCNTL0 */ - return 0x0f; - case 0x48: /* STIME0 */ - return s->stime0; - case 0x4a: /* RESPID0 */ - return s->respid0; - case 0x4b: /* RESPID1 */ - return s->respid1; - case 0x4d: /* STEST1 */ - return s->stest1; - case 0x4e: /* STEST2 */ - return s->stest2; - case 0x4f: /* STEST3 */ - return s->stest3; - case 0x50: /* SIDL */ - /* This is needed by the linux drivers. We currently only update it - during the MSG IN phase. */ - return s->sidl; - case 0x52: /* STEST4 */ - return 0xe0; - case 0x56: /* CCNTL0 */ - return s->ccntl0; - case 0x57: /* CCNTL1 */ - return s->ccntl1; - case 0x58: /* SBDL */ - /* Some drivers peek at the data bus during the MSG IN phase. */ - if ((s->sstat1 & PHASE_MASK) == PHASE_MI) - return s->msg[0]; - return 0; - case 0x59: /* SBDL high */ - return 0; - CASE_GET_REG32(mmrs, 0xa0) - CASE_GET_REG32(mmws, 0xa4) - CASE_GET_REG32(sfs, 0xa8) - CASE_GET_REG32(drs, 0xac) - CASE_GET_REG32(sbms, 0xb0) - CASE_GET_REG32(dbms, 0xb4) - CASE_GET_REG32(dnad64, 0xb8) - CASE_GET_REG32(pmjad1, 0xc0) - CASE_GET_REG32(pmjad2, 0xc4) - CASE_GET_REG32(rbc, 0xc8) - CASE_GET_REG32(ua, 0xcc) - CASE_GET_REG32(ia, 0xd4) - CASE_GET_REG32(sbc, 0xd8) - CASE_GET_REG32(csbc, 0xdc) - } - if (offset >= 0x5c && offset < 0xa0) { - int n; - int shift; - n = (offset - 0x58) >> 2; - shift = (offset & 3) * 8; - return (s->scratch[n] >> shift) & 0xff; - } - BADF("readb 0x%x\n", offset); - exit(1); -#undef CASE_GET_REG24 -#undef CASE_GET_REG32 -} - -static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) -{ -#define CASE_SET_REG24(name, addr) \ - case addr : s->name &= 0xffffff00; s->name |= val; break; \ - case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \ - case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; - -#define CASE_SET_REG32(name, addr) \ - case addr : s->name &= 0xffffff00; s->name |= val; break; \ - case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \ - case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \ - case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break; - -#ifdef DEBUG_LSI_REG - DPRINTF("Write reg %x = %02x\n", offset, val); -#endif - switch (offset) { - case 0x00: /* SCNTL0 */ - s->scntl0 = val; - if (val & LSI_SCNTL0_START) { - BADF("Start sequence not implemented\n"); - } - break; - case 0x01: /* SCNTL1 */ - s->scntl1 = val & ~LSI_SCNTL1_SST; - if (val & LSI_SCNTL1_IARB) { - BADF("Immediate Arbritration not implemented\n"); - } - if (val & LSI_SCNTL1_RST) { - if (!(s->sstat0 & LSI_SSTAT0_RST)) { - qbus_reset_all(&s->bus.qbus); - s->sstat0 |= LSI_SSTAT0_RST; - lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); - } - } else { - s->sstat0 &= ~LSI_SSTAT0_RST; - } - break; - case 0x02: /* SCNTL2 */ - val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS); - s->scntl2 = val; - break; - case 0x03: /* SCNTL3 */ - s->scntl3 = val; - break; - case 0x04: /* SCID */ - s->scid = val; - break; - case 0x05: /* SXFER */ - s->sxfer = val; - break; - case 0x06: /* SDID */ - if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) { - BADF("Destination ID does not match SSID\n"); - } - s->sdid = val & 0xf; - break; - case 0x07: /* GPREG0 */ - break; - case 0x08: /* SFBR */ - /* The CPU is not allowed to write to this register. However the - SCRIPTS register move instructions are. */ - s->sfbr = val; - break; - case 0x0a: case 0x0b: - /* Openserver writes to these readonly registers on startup */ - return; - case 0x0c: case 0x0d: case 0x0e: case 0x0f: - /* Linux writes to these readonly registers on startup. */ - return; - CASE_SET_REG32(dsa, 0x10) - case 0x14: /* ISTAT0 */ - s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0); - if (val & LSI_ISTAT0_ABRT) { - lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT); - } - if (val & LSI_ISTAT0_INTF) { - s->istat0 &= ~LSI_ISTAT0_INTF; - lsi_update_irq(s); - } - if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) { - DPRINTF("Woken by SIGP\n"); - s->waiting = 0; - s->dsp = s->dnad; - lsi_execute_script(s); - } - if (val & LSI_ISTAT0_SRST) { - qdev_reset_all(DEVICE(s)); - } - break; - case 0x16: /* MBOX0 */ - s->mbox0 = val; - break; - case 0x17: /* MBOX1 */ - s->mbox1 = val; - break; - case 0x18: /* CTEST0 */ - /* nothing to do */ - break; - case 0x1a: /* CTEST2 */ - s->ctest2 = val & LSI_CTEST2_PCICIE; - break; - case 0x1b: /* CTEST3 */ - s->ctest3 = val & 0x0f; - break; - CASE_SET_REG32(temp, 0x1c) - case 0x21: /* CTEST4 */ - if (val & 7) { - BADF("Unimplemented CTEST4-FBL 0x%x\n", val); - } - s->ctest4 = val; - break; - case 0x22: /* CTEST5 */ - if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) { - BADF("CTEST5 DMA increment not implemented\n"); - } - s->ctest5 = val; - break; - CASE_SET_REG24(dbc, 0x24) - CASE_SET_REG32(dnad, 0x28) - case 0x2c: /* DSP[0:7] */ - s->dsp &= 0xffffff00; - s->dsp |= val; - break; - case 0x2d: /* DSP[8:15] */ - s->dsp &= 0xffff00ff; - s->dsp |= val << 8; - break; - case 0x2e: /* DSP[16:23] */ - s->dsp &= 0xff00ffff; - s->dsp |= val << 16; - break; - case 0x2f: /* DSP[24:31] */ - s->dsp &= 0x00ffffff; - s->dsp |= val << 24; - if ((s->dmode & LSI_DMODE_MAN) == 0 - && (s->istat1 & LSI_ISTAT1_SRUN) == 0) - lsi_execute_script(s); - break; - CASE_SET_REG32(dsps, 0x30) - CASE_SET_REG32(scratch[0], 0x34) - case 0x38: /* DMODE */ - if (val & (LSI_DMODE_SIOM | LSI_DMODE_DIOM)) { - BADF("IO mappings not implemented\n"); - } - s->dmode = val; - break; - case 0x39: /* DIEN */ - s->dien = val; - lsi_update_irq(s); - break; - case 0x3a: /* SBR */ - s->sbr = val; - break; - case 0x3b: /* DCNTL */ - s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD); - if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0) - lsi_execute_script(s); - break; - case 0x40: /* SIEN0 */ - s->sien0 = val; - lsi_update_irq(s); - break; - case 0x41: /* SIEN1 */ - s->sien1 = val; - lsi_update_irq(s); - break; - case 0x47: /* GPCNTL0 */ - break; - case 0x48: /* STIME0 */ - s->stime0 = val; - break; - case 0x49: /* STIME1 */ - if (val & 0xf) { - DPRINTF("General purpose timer not implemented\n"); - /* ??? Raising the interrupt immediately seems to be sufficient - to keep the FreeBSD driver happy. */ - lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN); - } - break; - case 0x4a: /* RESPID0 */ - s->respid0 = val; - break; - case 0x4b: /* RESPID1 */ - s->respid1 = val; - break; - case 0x4d: /* STEST1 */ - s->stest1 = val; - break; - case 0x4e: /* STEST2 */ - if (val & 1) { - BADF("Low level mode not implemented\n"); - } - s->stest2 = val; - break; - case 0x4f: /* STEST3 */ - if (val & 0x41) { - BADF("SCSI FIFO test mode not implemented\n"); - } - s->stest3 = val; - break; - case 0x56: /* CCNTL0 */ - s->ccntl0 = val; - break; - case 0x57: /* CCNTL1 */ - s->ccntl1 = val; - break; - CASE_SET_REG32(mmrs, 0xa0) - CASE_SET_REG32(mmws, 0xa4) - CASE_SET_REG32(sfs, 0xa8) - CASE_SET_REG32(drs, 0xac) - CASE_SET_REG32(sbms, 0xb0) - CASE_SET_REG32(dbms, 0xb4) - CASE_SET_REG32(dnad64, 0xb8) - CASE_SET_REG32(pmjad1, 0xc0) - CASE_SET_REG32(pmjad2, 0xc4) - CASE_SET_REG32(rbc, 0xc8) - CASE_SET_REG32(ua, 0xcc) - CASE_SET_REG32(ia, 0xd4) - CASE_SET_REG32(sbc, 0xd8) - CASE_SET_REG32(csbc, 0xdc) - default: - if (offset >= 0x5c && offset < 0xa0) { - int n; - int shift; - n = (offset - 0x58) >> 2; - shift = (offset & 3) * 8; - s->scratch[n] = deposit32(s->scratch[n], shift, 8, val); - } else { - BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val); - } - } -#undef CASE_SET_REG24 -#undef CASE_SET_REG32 -} - -static void lsi_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - LSIState *s = opaque; - - lsi_reg_writeb(s, addr & 0xff, val); -} - -static uint64_t lsi_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - LSIState *s = opaque; - - return lsi_reg_readb(s, addr & 0xff); -} - -static const MemoryRegionOps lsi_mmio_ops = { - .read = lsi_mmio_read, - .write = lsi_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void lsi_ram_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - LSIState *s = opaque; - uint32_t newval; - uint32_t mask; - int shift; - - newval = s->script_ram[addr >> 2]; - shift = (addr & 3) * 8; - mask = ((uint64_t)1 << (size * 8)) - 1; - newval &= ~(mask << shift); - newval |= val << shift; - s->script_ram[addr >> 2] = newval; -} - -static uint64_t lsi_ram_read(void *opaque, hwaddr addr, - unsigned size) -{ - LSIState *s = opaque; - uint32_t val; - uint32_t mask; - - val = s->script_ram[addr >> 2]; - mask = ((uint64_t)1 << (size * 8)) - 1; - val >>= (addr & 3) * 8; - return val & mask; -} - -static const MemoryRegionOps lsi_ram_ops = { - .read = lsi_ram_read, - .write = lsi_ram_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t lsi_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - LSIState *s = opaque; - return lsi_reg_readb(s, addr & 0xff); -} - -static void lsi_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - LSIState *s = opaque; - lsi_reg_writeb(s, addr & 0xff, val); -} - -static const MemoryRegionOps lsi_io_ops = { - .read = lsi_io_read, - .write = lsi_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void lsi_scsi_reset(DeviceState *dev) -{ - LSIState *s = LSI53C895A(dev); - - lsi_soft_reset(s); -} - -static void lsi_pre_save(void *opaque) -{ - LSIState *s = opaque; - - if (s->current) { - assert(s->current->dma_buf == NULL); - assert(s->current->dma_len == 0); - } - assert(QTAILQ_EMPTY(&s->queue)); -} - -static const VMStateDescription vmstate_lsi_scsi = { - .name = "lsiscsi", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = lsi_pre_save, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, LSIState), - - VMSTATE_INT32(carry, LSIState), - VMSTATE_INT32(status, LSIState), - VMSTATE_INT32(msg_action, LSIState), - VMSTATE_INT32(msg_len, LSIState), - VMSTATE_BUFFER(msg, LSIState), - VMSTATE_INT32(waiting, LSIState), - - VMSTATE_UINT32(dsa, LSIState), - VMSTATE_UINT32(temp, LSIState), - VMSTATE_UINT32(dnad, LSIState), - VMSTATE_UINT32(dbc, LSIState), - VMSTATE_UINT8(istat0, LSIState), - VMSTATE_UINT8(istat1, LSIState), - VMSTATE_UINT8(dcmd, LSIState), - VMSTATE_UINT8(dstat, LSIState), - VMSTATE_UINT8(dien, LSIState), - VMSTATE_UINT8(sist0, LSIState), - VMSTATE_UINT8(sist1, LSIState), - VMSTATE_UINT8(sien0, LSIState), - VMSTATE_UINT8(sien1, LSIState), - VMSTATE_UINT8(mbox0, LSIState), - VMSTATE_UINT8(mbox1, LSIState), - VMSTATE_UINT8(dfifo, LSIState), - VMSTATE_UINT8(ctest2, LSIState), - VMSTATE_UINT8(ctest3, LSIState), - VMSTATE_UINT8(ctest4, LSIState), - VMSTATE_UINT8(ctest5, LSIState), - VMSTATE_UINT8(ccntl0, LSIState), - VMSTATE_UINT8(ccntl1, LSIState), - VMSTATE_UINT32(dsp, LSIState), - VMSTATE_UINT32(dsps, LSIState), - VMSTATE_UINT8(dmode, LSIState), - VMSTATE_UINT8(dcntl, LSIState), - VMSTATE_UINT8(scntl0, LSIState), - VMSTATE_UINT8(scntl1, LSIState), - VMSTATE_UINT8(scntl2, LSIState), - VMSTATE_UINT8(scntl3, LSIState), - VMSTATE_UINT8(sstat0, LSIState), - VMSTATE_UINT8(sstat1, LSIState), - VMSTATE_UINT8(scid, LSIState), - VMSTATE_UINT8(sxfer, LSIState), - VMSTATE_UINT8(socl, LSIState), - VMSTATE_UINT8(sdid, LSIState), - VMSTATE_UINT8(ssid, LSIState), - VMSTATE_UINT8(sfbr, LSIState), - VMSTATE_UINT8(stest1, LSIState), - VMSTATE_UINT8(stest2, LSIState), - VMSTATE_UINT8(stest3, LSIState), - VMSTATE_UINT8(sidl, LSIState), - VMSTATE_UINT8(stime0, LSIState), - VMSTATE_UINT8(respid0, LSIState), - VMSTATE_UINT8(respid1, LSIState), - VMSTATE_UINT32(mmrs, LSIState), - VMSTATE_UINT32(mmws, LSIState), - VMSTATE_UINT32(sfs, LSIState), - VMSTATE_UINT32(drs, LSIState), - VMSTATE_UINT32(sbms, LSIState), - VMSTATE_UINT32(dbms, LSIState), - VMSTATE_UINT32(dnad64, LSIState), - VMSTATE_UINT32(pmjad1, LSIState), - VMSTATE_UINT32(pmjad2, LSIState), - VMSTATE_UINT32(rbc, LSIState), - VMSTATE_UINT32(ua, LSIState), - VMSTATE_UINT32(ia, LSIState), - VMSTATE_UINT32(sbc, LSIState), - VMSTATE_UINT32(csbc, LSIState), - VMSTATE_BUFFER_UNSAFE(scratch, LSIState, 0, 18 * sizeof(uint32_t)), - VMSTATE_UINT8(sbr, LSIState), - - VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 2048 * sizeof(uint32_t)), - VMSTATE_END_OF_LIST() - } -}; - -static const struct SCSIBusInfo lsi_scsi_info = { - .tcq = true, - .max_target = LSI_MAX_DEVS, - .max_lun = 0, /* LUN support is buggy */ - - .transfer_data = lsi_transfer_data, - .complete = lsi_command_complete, - .cancel = lsi_request_cancelled -}; - -static void lsi_scsi_realize(PCIDevice *dev, Error **errp) -{ - LSIState *s = LSI53C895A(dev); - DeviceState *d = DEVICE(dev); - uint8_t *pci_conf; - - pci_conf = dev->config; - - /* PCI latency timer = 255 */ - pci_conf[PCI_LATENCY_TIMER] = 0xff; - /* Interrupt pin A */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - memory_region_init_io(&s->mmio_io, OBJECT(s), &lsi_mmio_ops, s, - "lsi-mmio", 0x400); - memory_region_init_io(&s->ram_io, OBJECT(s), &lsi_ram_ops, s, - "lsi-ram", 0x2000); - memory_region_init_io(&s->io_io, OBJECT(s), &lsi_io_ops, s, - "lsi-io", 256); - - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io); - QTAILQ_INIT(&s->queue); - - scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static void lsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = lsi_scsi_realize; - k->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - k->device_id = PCI_DEVICE_ID_LSI_53C895A; - k->class_id = PCI_CLASS_STORAGE_SCSI; - k->subsystem_id = 0x1000; - dc->reset = lsi_scsi_reset; - dc->vmsd = &vmstate_lsi_scsi; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo lsi_info = { - .name = TYPE_LSI53C895A, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(LSIState), - .class_init = lsi_class_init, -}; - -static void lsi53c810_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->device_id = PCI_DEVICE_ID_LSI_53C810; -} - -static TypeInfo lsi53c810_info = { - .name = TYPE_LSI53C810, - .parent = TYPE_LSI53C895A, - .class_init = lsi53c810_class_init, -}; - -static void lsi53c895a_register_types(void) -{ - type_register_static(&lsi_info); - type_register_static(&lsi53c810_info); -} - -type_init(lsi53c895a_register_types) diff --git a/qemu/hw/scsi/megasas.c b/qemu/hw/scsi/megasas.c deleted file mode 100644 index a63a58155..000000000 --- a/qemu/hw/scsi/megasas.c +++ /dev/null @@ -1,2548 +0,0 @@ -/* - * QEMU MegaRAID SAS 8708EM2 Host Bus Adapter emulation - * Based on the linux driver code at drivers/scsi/megaraid - * - * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "sysemu/dma.h" -#include "sysemu/block-backend.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "qemu/iov.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "trace.h" - -#include "mfi.h" - -#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 */ -#define MEGASAS_MAX_ARRAYS 128 - -#define MEGASAS_HBA_SERIAL "QEMU123456" -#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_MSI 1 -#define MEGASAS_MASK_USE_MSI (1 << MEGASAS_FLAG_USE_MSI) -#define MEGASAS_FLAG_USE_MSIX 2 -#define MEGASAS_MASK_USE_MSIX (1 << MEGASAS_FLAG_USE_MSIX) -#define MEGASAS_FLAG_USE_QUEUE64 3 -#define MEGASAS_MASK_USE_QUEUE64 (1 << MEGASAS_FLAG_USE_QUEUE64) - -static const char *mfi_frame_desc[] = { - "MFI init", "LD Read", "LD Write", "LD SCSI", "PD SCSI", - "MFI Doorbell", "MFI Abort", "MFI SMP", "MFI Stop"}; - -typedef struct MegasasCmd { - uint32_t index; - uint16_t flags; - uint16_t count; - uint64_t context; - - hwaddr pa; - hwaddr pa_size; - union mfi_frame *frame; - SCSIRequest *req; - QEMUSGList qsg; - void *iov_buf; - size_t iov_size; - size_t iov_offset; - struct MegasasState *state; -} MegasasCmd; - -typedef struct MegasasState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion mmio_io; - MemoryRegion port_io; - MemoryRegion queue_io; - uint32_t frame_hi; - - int fw_state; - uint32_t fw_sge; - uint32_t fw_cmds; - uint32_t flags; - int fw_luns; - int intr_mask; - int doorbell; - int busy; - int diag; - int adp_reset; - - MegasasCmd *event_cmd; - int event_locale; - int event_class; - int event_count; - int shutdown_event; - int boot_event; - - uint64_t sas_addr; - char *hba_serial; - - uint64_t reply_queue_pa; - void *reply_queue; - int reply_queue_len; - int reply_queue_head; - int reply_queue_tail; - uint64_t consumer_pa; - uint64_t producer_pa; - - MegasasCmd frames[MEGASAS_MAX_FRAMES]; - DECLARE_BITMAP(frame_map, MEGASAS_MAX_FRAMES); - SCSIBus bus; -} MegasasState; - -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_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 - -static bool megasas_intr_enabled(MegasasState *s) -{ - if ((s->intr_mask & MEGASAS_INTR_DISABLED_MASK) != - MEGASAS_INTR_DISABLED_MASK) { - return true; - } - return false; -} - -static bool megasas_use_queue64(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_QUEUE64; -} - -static bool megasas_use_msi(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_MSI; -} - -static bool megasas_use_msix(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_MSIX; -} - -static bool megasas_is_jbod(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_JBOD; -} - -static void megasas_frame_set_cmd_status(MegasasState *s, - unsigned long frame, uint8_t v) -{ - PCIDevice *pci = &s->parent_obj; - stb_pci_dma(pci, frame + offsetof(struct mfi_frame_header, cmd_status), v); -} - -static void megasas_frame_set_scsi_status(MegasasState *s, - unsigned long frame, uint8_t v) -{ - PCIDevice *pci = &s->parent_obj; - stb_pci_dma(pci, frame + offsetof(struct mfi_frame_header, scsi_status), v); -} - -/* - * Context is considered opaque, but the HBA firmware is running - * in little endian mode. So convert it to little endian, too. - */ -static uint64_t megasas_frame_get_context(MegasasState *s, - unsigned long frame) -{ - PCIDevice *pci = &s->parent_obj; - return ldq_le_pci_dma(pci, frame + offsetof(struct mfi_frame_header, context)); -} - -static bool megasas_frame_is_ieee_sgl(MegasasCmd *cmd) -{ - return cmd->flags & MFI_FRAME_IEEE_SGL; -} - -static bool megasas_frame_is_sgl64(MegasasCmd *cmd) -{ - return cmd->flags & MFI_FRAME_SGL64; -} - -static bool megasas_frame_is_sense64(MegasasCmd *cmd) -{ - return cmd->flags & MFI_FRAME_SENSE64; -} - -static uint64_t megasas_sgl_get_addr(MegasasCmd *cmd, - union mfi_sgl *sgl) -{ - uint64_t addr; - - if (megasas_frame_is_ieee_sgl(cmd)) { - addr = le64_to_cpu(sgl->sg_skinny->addr); - } else if (megasas_frame_is_sgl64(cmd)) { - addr = le64_to_cpu(sgl->sg64->addr); - } else { - addr = le32_to_cpu(sgl->sg32->addr); - } - return addr; -} - -static uint32_t megasas_sgl_get_len(MegasasCmd *cmd, - union mfi_sgl *sgl) -{ - uint32_t len; - - if (megasas_frame_is_ieee_sgl(cmd)) { - len = le32_to_cpu(sgl->sg_skinny->len); - } else if (megasas_frame_is_sgl64(cmd)) { - len = le32_to_cpu(sgl->sg64->len); - } else { - len = le32_to_cpu(sgl->sg32->len); - } - return len; -} - -static union mfi_sgl *megasas_sgl_next(MegasasCmd *cmd, - union mfi_sgl *sgl) -{ - uint8_t *next = (uint8_t *)sgl; - - if (megasas_frame_is_ieee_sgl(cmd)) { - next += sizeof(struct mfi_sg_skinny); - } else if (megasas_frame_is_sgl64(cmd)) { - next += sizeof(struct mfi_sg64); - } else { - next += sizeof(struct mfi_sg32); - } - - if (next >= (uint8_t *)cmd->frame + cmd->pa_size) { - return NULL; - } - return (union mfi_sgl *)next; -} - -static void megasas_soft_reset(MegasasState *s); - -static int megasas_map_sgl(MegasasState *s, MegasasCmd *cmd, union mfi_sgl *sgl) -{ - int i; - int iov_count = 0; - size_t iov_size = 0; - - cmd->flags = le16_to_cpu(cmd->frame->header.flags); - iov_count = cmd->frame->header.sge_count; - if (iov_count > MEGASAS_MAX_SGE) { - trace_megasas_iovec_sgl_overflow(cmd->index, iov_count, - MEGASAS_MAX_SGE); - return iov_count; - } - pci_dma_sglist_init(&cmd->qsg, PCI_DEVICE(s), iov_count); - for (i = 0; i < iov_count; i++) { - dma_addr_t iov_pa, iov_size_p; - - if (!sgl) { - trace_megasas_iovec_sgl_underflow(cmd->index, i); - goto unmap; - } - iov_pa = megasas_sgl_get_addr(cmd, sgl); - iov_size_p = megasas_sgl_get_len(cmd, sgl); - if (!iov_pa || !iov_size_p) { - trace_megasas_iovec_sgl_invalid(cmd->index, i, - iov_pa, iov_size_p); - goto unmap; - } - qemu_sglist_add(&cmd->qsg, iov_pa, iov_size_p); - sgl = megasas_sgl_next(cmd, sgl); - iov_size += (size_t)iov_size_p; - } - if (cmd->iov_size > iov_size) { - trace_megasas_iovec_overflow(cmd->index, iov_size, cmd->iov_size); - } else if (cmd->iov_size < iov_size) { - trace_megasas_iovec_underflow(cmd->iov_size, iov_size, cmd->iov_size); - } - cmd->iov_offset = 0; - return 0; -unmap: - qemu_sglist_destroy(&cmd->qsg); - return iov_count - i; -} - -static void megasas_unmap_sgl(MegasasCmd *cmd) -{ - qemu_sglist_destroy(&cmd->qsg); - cmd->iov_offset = 0; -} - -/* - * passthrough sense and io sense are at the same offset - */ -static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, - uint8_t sense_len) -{ - PCIDevice *pcid = PCI_DEVICE(cmd->state); - uint32_t pa_hi = 0, pa_lo; - hwaddr pa; - - if (sense_len > cmd->frame->header.sense_len) { - sense_len = cmd->frame->header.sense_len; - } - if (sense_len) { - pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo); - if (megasas_frame_is_sense64(cmd)) { - pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi); - } - pa = ((uint64_t) pa_hi << 32) | pa_lo; - pci_dma_write(pcid, pa, sense_ptr, sense_len); - cmd->frame->header.sense_len = sense_len; - } - return sense_len; -} - -static void megasas_write_sense(MegasasCmd *cmd, SCSISense sense) -{ - uint8_t sense_buf[SCSI_SENSE_BUF_SIZE]; - uint8_t sense_len = 18; - - memset(sense_buf, 0, sense_len); - sense_buf[0] = 0xf0; - sense_buf[2] = sense.key; - sense_buf[7] = 10; - sense_buf[12] = sense.asc; - sense_buf[13] = sense.ascq; - megasas_build_sense(cmd, sense_buf, sense_len); -} - -static void megasas_copy_sense(MegasasCmd *cmd) -{ - uint8_t sense_buf[SCSI_SENSE_BUF_SIZE]; - uint8_t sense_len; - - sense_len = scsi_req_get_sense(cmd->req, sense_buf, - SCSI_SENSE_BUF_SIZE); - megasas_build_sense(cmd, sense_buf, sense_len); -} - -/* - * Format an INQUIRY CDB - */ -static int megasas_setup_inquiry(uint8_t *cdb, int pg, int len) -{ - memset(cdb, 0, 6); - cdb[0] = INQUIRY; - if (pg > 0) { - cdb[1] = 0x1; - cdb[2] = pg; - } - cdb[3] = (len >> 8) & 0xff; - cdb[4] = (len & 0xff); - return len; -} - -/* - * Encode lba and len into a READ_16/WRITE_16 CDB - */ -static void megasas_encode_lba(uint8_t *cdb, uint64_t lba, - uint32_t len, bool is_write) -{ - memset(cdb, 0x0, 16); - if (is_write) { - cdb[0] = WRITE_16; - } else { - cdb[0] = READ_16; - } - cdb[2] = (lba >> 56) & 0xff; - cdb[3] = (lba >> 48) & 0xff; - cdb[4] = (lba >> 40) & 0xff; - cdb[5] = (lba >> 32) & 0xff; - cdb[6] = (lba >> 24) & 0xff; - cdb[7] = (lba >> 16) & 0xff; - cdb[8] = (lba >> 8) & 0xff; - cdb[9] = (lba) & 0xff; - cdb[10] = (len >> 24) & 0xff; - cdb[11] = (len >> 16) & 0xff; - cdb[12] = (len >> 8) & 0xff; - cdb[13] = (len) & 0xff; -} - -/* - * Utility functions - */ -static uint64_t megasas_fw_time(void) -{ - struct tm curtime; - uint64_t bcd_time; - - qemu_get_timedate(&curtime, 0); - bcd_time = ((uint64_t)curtime.tm_sec & 0xff) << 48 | - ((uint64_t)curtime.tm_min & 0xff) << 40 | - ((uint64_t)curtime.tm_hour & 0xff) << 32 | - ((uint64_t)curtime.tm_mday & 0xff) << 24 | - ((uint64_t)curtime.tm_mon & 0xff) << 16 | - ((uint64_t)(curtime.tm_year + 1900) & 0xffff); - - return bcd_time; -} - -/* - * 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 = (0x1221ULL << 48); - return addr | ((uint64_t)id << 24); -} - -/* - * Frame handling - */ -static int megasas_next_index(MegasasState *s, int index, int limit) -{ - index++; - if (index == limit) { - index = 0; - } - return index; -} - -static MegasasCmd *megasas_lookup_frame(MegasasState *s, - hwaddr frame) -{ - MegasasCmd *cmd = NULL; - int num = 0, index; - - index = s->reply_queue_head; - - while (num < s->fw_cmds) { - if (s->frames[index].pa && s->frames[index].pa == frame) { - cmd = &s->frames[index]; - break; - } - index = megasas_next_index(s, index, s->fw_cmds); - num++; - } - - return cmd; -} - -static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd) -{ - PCIDevice *p = PCI_DEVICE(s); - - pci_dma_unmap(p, cmd->frame, cmd->pa_size, 0, 0); - cmd->frame = NULL; - cmd->pa = 0; - clear_bit(cmd->index, s->frame_map); -} - -/* - * This absolutely needs to be locked if - * qemu ever goes multithreaded. - */ -static MegasasCmd *megasas_enqueue_frame(MegasasState *s, - hwaddr frame, uint64_t context, int count) -{ - PCIDevice *pcid = PCI_DEVICE(s); - MegasasCmd *cmd = NULL; - int frame_size = MFI_FRAME_SIZE * 16; - hwaddr frame_size_p = frame_size; - unsigned long index; - - index = 0; - while (index < s->fw_cmds) { - index = find_next_zero_bit(s->frame_map, s->fw_cmds, index); - if (!s->frames[index].pa) - break; - /* Busy frame found */ - trace_megasas_qf_mapped(index); - } - if (index >= s->fw_cmds) { - /* All frames busy */ - trace_megasas_qf_busy(frame); - return NULL; - } - cmd = &s->frames[index]; - set_bit(index, s->frame_map); - trace_megasas_qf_new(index, frame); - - cmd->pa = frame; - /* Map all possible frames */ - cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0); - if (frame_size_p != frame_size) { - trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame); - if (cmd->frame) { - megasas_unmap_frame(s, cmd); - } - s->event_count++; - return NULL; - } - cmd->pa_size = frame_size_p; - cmd->context = context; - if (!megasas_use_queue64(s)) { - cmd->context &= (uint64_t)0xFFFFFFFF; - } - cmd->count = count; - s->busy++; - - if (s->consumer_pa) { - s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa); - } - trace_megasas_qf_enqueue(cmd->index, cmd->count, cmd->context, - s->reply_queue_head, s->reply_queue_tail, s->busy); - - return cmd; -} - -static void megasas_complete_frame(MegasasState *s, uint64_t context) -{ - PCIDevice *pci_dev = PCI_DEVICE(s); - int tail, queue_offset; - - /* Decrement busy count */ - s->busy--; - if (s->reply_queue_pa) { - /* - * Put command on the reply queue. - * Context is opaque, but emulation is running in - * little endian. So convert it. - */ - if (megasas_use_queue64(s)) { - queue_offset = s->reply_queue_head * sizeof(uint64_t); - stq_le_pci_dma(pci_dev, s->reply_queue_pa + queue_offset, context); - } else { - queue_offset = s->reply_queue_head * sizeof(uint32_t); - stl_le_pci_dma(pci_dev, s->reply_queue_pa + queue_offset, context); - } - s->reply_queue_tail = ldl_le_pci_dma(pci_dev, s->consumer_pa); - trace_megasas_qf_complete(context, s->reply_queue_head, - s->reply_queue_tail, s->busy); - } - - if (megasas_intr_enabled(s)) { - /* Update reply queue pointer */ - s->reply_queue_tail = ldl_le_pci_dma(pci_dev, s->consumer_pa); - tail = s->reply_queue_head; - s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds); - trace_megasas_qf_update(s->reply_queue_head, s->reply_queue_tail, - s->busy); - stl_le_pci_dma(pci_dev, s->producer_pa, s->reply_queue_head); - /* Notify HBA */ - if (msix_enabled(pci_dev)) { - trace_megasas_msix_raise(0); - msix_notify(pci_dev, 0); - } else if (msi_enabled(pci_dev)) { - trace_megasas_msi_raise(0); - msi_notify(pci_dev, 0); - } else { - s->doorbell++; - if (s->doorbell == 1) { - trace_megasas_irq_raise(); - pci_irq_assert(pci_dev); - } - } - } else { - trace_megasas_qf_complete_noirq(context); - } -} - -static void megasas_reset_frames(MegasasState *s) -{ - int i; - MegasasCmd *cmd; - - for (i = 0; i < s->fw_cmds; i++) { - cmd = &s->frames[i]; - if (cmd->pa) { - megasas_unmap_frame(s, cmd); - } - } - bitmap_zero(s->frame_map, MEGASAS_MAX_FRAMES); -} - -static void megasas_abort_command(MegasasCmd *cmd) -{ - if (cmd->req) { - scsi_req_cancel(cmd->req); - cmd->req = NULL; - } -} - -static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) -{ - PCIDevice *pcid = PCI_DEVICE(s); - uint32_t pa_hi, pa_lo; - hwaddr iq_pa, initq_size = sizeof(struct mfi_init_qinfo); - struct mfi_init_qinfo *initq = NULL; - uint32_t flags; - int ret = MFI_STAT_OK; - - if (s->reply_queue_pa) { - trace_megasas_initq_mapped(s->reply_queue_pa); - goto out; - } - pa_lo = le32_to_cpu(cmd->frame->init.qinfo_new_addr_lo); - pa_hi = le32_to_cpu(cmd->frame->init.qinfo_new_addr_hi); - iq_pa = (((uint64_t) pa_hi << 32) | pa_lo); - trace_megasas_init_firmware((uint64_t)iq_pa); - initq = pci_dma_map(pcid, iq_pa, &initq_size, 0); - if (!initq || initq_size != sizeof(*initq)) { - trace_megasas_initq_map_failed(cmd->index); - s->event_count++; - ret = MFI_STAT_MEMORY_NOT_AVAILABLE; - goto out; - } - s->reply_queue_len = le32_to_cpu(initq->rq_entries) & 0xFFFF; - if (s->reply_queue_len > s->fw_cmds) { - trace_megasas_initq_mismatch(s->reply_queue_len, s->fw_cmds); - s->event_count++; - ret = MFI_STAT_INVALID_PARAMETER; - goto out; - } - pa_lo = le32_to_cpu(initq->rq_addr_lo); - pa_hi = le32_to_cpu(initq->rq_addr_hi); - s->reply_queue_pa = ((uint64_t) pa_hi << 32) | pa_lo; - pa_lo = le32_to_cpu(initq->ci_addr_lo); - pa_hi = le32_to_cpu(initq->ci_addr_hi); - s->consumer_pa = ((uint64_t) pa_hi << 32) | pa_lo; - pa_lo = le32_to_cpu(initq->pi_addr_lo); - pa_hi = le32_to_cpu(initq->pi_addr_hi); - s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo; - s->reply_queue_head = ldl_le_pci_dma(pcid, s->producer_pa); - s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa); - flags = le32_to_cpu(initq->flags); - if (flags & MFI_QUEUE_FLAG_CONTEXT64) { - s->flags |= MEGASAS_MASK_USE_QUEUE64; - } - trace_megasas_init_queue((unsigned long)s->reply_queue_pa, - s->reply_queue_len, s->reply_queue_head, - s->reply_queue_tail, flags); - megasas_reset_frames(s); - s->fw_state = MFI_FWSTATE_OPERATIONAL; -out: - if (initq) { - pci_dma_unmap(pcid, initq, initq_size, 0, 0); - } - return ret; -} - -static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd) -{ - dma_addr_t iov_pa, iov_size; - - cmd->flags = le16_to_cpu(cmd->frame->header.flags); - if (!cmd->frame->header.sge_count) { - trace_megasas_dcmd_zero_sge(cmd->index); - cmd->iov_size = 0; - return 0; - } else if (cmd->frame->header.sge_count > 1) { - trace_megasas_dcmd_invalid_sge(cmd->index, - cmd->frame->header.sge_count); - cmd->iov_size = 0; - return -1; - } - iov_pa = megasas_sgl_get_addr(cmd, &cmd->frame->dcmd.sgl); - iov_size = megasas_sgl_get_len(cmd, &cmd->frame->dcmd.sgl); - pci_dma_sglist_init(&cmd->qsg, PCI_DEVICE(s), 1); - qemu_sglist_add(&cmd->qsg, iov_pa, iov_size); - cmd->iov_size = iov_size; - return cmd->iov_size; -} - -static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size) -{ - trace_megasas_finish_dcmd(cmd->index, iov_size); - - if (cmd->frame->header.sge_count) { - qemu_sglist_destroy(&cmd->qsg); - } - if (iov_size > cmd->iov_size) { - if (megasas_frame_is_ieee_sgl(cmd)) { - cmd->frame->dcmd.sgl.sg_skinny->len = cpu_to_le32(iov_size); - } else if (megasas_frame_is_sgl64(cmd)) { - cmd->frame->dcmd.sgl.sg64->len = cpu_to_le32(iov_size); - } else { - cmd->frame->dcmd.sgl.sg32->len = cpu_to_le32(iov_size); - } - } - cmd->iov_size = 0; -} - -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; - int num_pd_disks = 0; - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - - 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 - * 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 = 8; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - uint16_t pd_id; - - if (num_pd_disks < 8) { - pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); - info.device.port_addr[num_pd_disks] = - cpu_to_le64(megasas_get_sata_addr(pd_id)); - } - num_pd_disks++; - } - - 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_hw_version()); - memcpy(info.image_component[0].name, "APP", 3); - 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; - if (pci_dev->has_rom) { - uint8_t biosver[32]; - uint8_t *ptr; - - ptr = memory_region_get_ram_ptr(&pci_dev->rom); - memcpy(biosver, ptr + 0x41, 31); - memcpy(info.image_component[1].name, "BIOS", 4); - memcpy(info.image_component[1].version, biosver, - strlen((const char *)biosver)); - info.image_component_count++; - } - info.current_fw_time = cpu_to_le32(megasas_fw_time()); - info.max_arms = 32; - info.max_spans = 8; - info.max_arrays = MEGASAS_MAX_ARRAYS; - info.max_lds = MFI_MAX_LD; - info.max_cmds = cpu_to_le16(s->fw_cmds); - info.max_sg_elements = cpu_to_le16(s->fw_sge); - info.max_request_size = cpu_to_le32(MEGASAS_MAX_SECTORS); - if (!megasas_is_jbod(s)) - info.lds_present = cpu_to_le16(num_pd_disks); - info.pd_present = cpu_to_le16(num_pd_disks); - info.pd_disks_present = cpu_to_le16(num_pd_disks); - info.hw_present = cpu_to_le32(MFI_INFO_HW_NVRAM | - MFI_INFO_HW_MEM | - MFI_INFO_HW_FLASH); - info.memory_size = cpu_to_le16(512); - info.nvram_size = cpu_to_le16(32); - info.flash_size = cpu_to_le16(16); - info.raid_levels = cpu_to_le32(MFI_INFO_RAID_0); - info.adapter_ops = cpu_to_le32(MFI_INFO_AOPS_RBLD_RATE | - MFI_INFO_AOPS_SELF_DIAGNOSTIC | - MFI_INFO_AOPS_MIXED_ARRAY); - info.ld_ops = cpu_to_le32(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.max_strips_per_io = cpu_to_le16(s->fw_sge); - info.stripe_sz_ops.min = 3; - info.stripe_sz_ops.max = ctz32(MEGASAS_MAX_SECTORS + 1); - info.properties.pred_fail_poll_interval = cpu_to_le16(300); - info.properties.intr_throttle_cnt = cpu_to_le16(16); - info.properties.intr_throttle_timeout = cpu_to_le16(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 = cpu_to_le16(1440); - info.properties.expose_encl_devices = 1; - info.properties.OnOffProperties = cpu_to_le32(MFI_CTRL_PROP_EnableJBOD); - info.pd_ops = cpu_to_le32(MFI_INFO_PDOPS_FORCE_ONLINE | - MFI_INFO_PDOPS_FORCE_OFFLINE); - info.pd_mix_support = cpu_to_le32(MFI_INFO_PDMIX_SAS | - MFI_INFO_PDMIX_SATA | - MFI_INFO_PDMIX_LD); - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_defaults info; - size_t dcmd_size = sizeof(struct mfi_defaults); - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - - info.sas_addr = cpu_to_le64(s->sas_addr); - info.stripe_size = 3; - 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; - /* Enable for BIOS support */ - info.bios_enumerate_lds = 1; - info.disable_ctrl_r = 1; - info.expose_enclosure_devices = 1; - info.disable_preboot_cli = 1; - info.cluster_disable = 1; - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_get_bios_info(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_bios_data info; - size_t dcmd_size = sizeof(info); - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - info.continue_on_error = 1; - info.verbose = 1; - if (megasas_is_jbod(s)) { - info.expose_all_drives = 1; - } - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_get_fw_time(MegasasState *s, MegasasCmd *cmd) -{ - uint64_t fw_time; - size_t dcmd_size = sizeof(fw_time); - - fw_time = cpu_to_le64(megasas_fw_time()); - - cmd->iov_size -= dma_buf_read((uint8_t *)&fw_time, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_set_fw_time(MegasasState *s, MegasasCmd *cmd) -{ - uint64_t fw_time; - - /* This is a dummy; setting of firmware time is not allowed */ - memcpy(&fw_time, cmd->frame->dcmd.mbox, sizeof(fw_time)); - - trace_megasas_dcmd_set_fw_time(cmd->index, fw_time); - fw_time = cpu_to_le64(megasas_fw_time()); - return MFI_STAT_OK; -} - -static int megasas_event_info(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_evt_log_state info; - size_t dcmd_size = sizeof(info); - - memset(&info, 0, dcmd_size); - - info.newest_seq_num = cpu_to_le32(s->event_count); - info.shutdown_seq_num = cpu_to_le32(s->shutdown_event); - info.boot_seq_num = cpu_to_le32(s->boot_event); - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_event_wait(MegasasState *s, MegasasCmd *cmd) -{ - union mfi_evt event; - - if (cmd->iov_size < sizeof(struct mfi_evt_detail)) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - sizeof(struct mfi_evt_detail)); - return MFI_STAT_INVALID_PARAMETER; - } - s->event_count = cpu_to_le32(cmd->frame->dcmd.mbox[0]); - event.word = cpu_to_le32(cmd->frame->dcmd.mbox[4]); - s->event_locale = event.members.locale; - s->event_class = event.members.class; - s->event_cmd = cmd; - /* Decrease busy count; event frame doesn't count here */ - s->busy--; - cmd->iov_size = sizeof(struct mfi_evt_detail); - return MFI_STAT_INVALID_STATUS; -} - -static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_pd_list info; - size_t dcmd_size = sizeof(info); - BusChild *kid; - uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks; - - memset(&info, 0, dcmd_size); - offset = 8; - dcmd_limit = offset + sizeof(struct mfi_pd_address); - if (cmd->iov_size < dcmd_limit) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_limit); - return MFI_STAT_INVALID_PARAMETER; - } - - max_pd_disks = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address); - if (max_pd_disks > MFI_MAX_SYS_PDS) { - max_pd_disks = MFI_MAX_SYS_PDS; - } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - uint16_t pd_id; - - if (num_pd_disks >= max_pd_disks) - break; - - pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); - info.addr[num_pd_disks].device_id = cpu_to_le16(pd_id); - info.addr[num_pd_disks].encl_device_id = 0xFFFF; - info.addr[num_pd_disks].encl_index = 0; - info.addr[num_pd_disks].slot_number = sdev->id & 0xFF; - 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_get_sata_addr(pd_id)); - num_pd_disks++; - offset += sizeof(struct mfi_pd_address); - } - trace_megasas_dcmd_pd_get_list(cmd->index, num_pd_disks, - max_pd_disks, offset); - - info.size = cpu_to_le32(offset); - info.count = cpu_to_le32(num_pd_disks); - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, offset, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_pd_list_query(MegasasState *s, MegasasCmd *cmd) -{ - uint16_t flags; - - /* mbox0 contains flags */ - flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_pd_list_query(cmd->index, flags); - if (flags == MR_PD_QUERY_TYPE_ALL || - megasas_is_jbod(s)) { - return megasas_dcmd_pd_get_list(s, cmd); - } - - return MFI_STAT_OK; -} - -static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, - MegasasCmd *cmd) -{ - struct mfi_pd_info *info = cmd->iov_buf; - size_t dcmd_size = sizeof(struct mfi_pd_info); - uint64_t pd_size; - uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); - uint8_t cmdbuf[6]; - SCSIRequest *req; - size_t len, resid; - - if (!cmd->iov_buf) { - cmd->iov_buf = g_malloc0(dcmd_size); - info = cmd->iov_buf; - info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */ - info->vpd_page83[0] = 0x7f; - megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "PD get info std inquiry"); - g_free(cmd->iov_buf); - cmd->iov_buf = NULL; - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "PD get info std inquiry", lun); - len = scsi_req_enqueue(req); - if (len > 0) { - cmd->iov_size = len; - scsi_req_continue(req); - } - return MFI_STAT_INVALID_STATUS; - } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) { - megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "PD get info vpd inquiry"); - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "PD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); - if (len > 0) { - cmd->iov_size = len; - scsi_req_continue(req); - } - return MFI_STAT_INVALID_STATUS; - } - /* Finished, set FW state */ - if ((info->inquiry_data[0] >> 5) == 0) { - if (megasas_is_jbod(cmd->state)) { - info->fw_state = cpu_to_le16(MFI_PD_STATE_SYSTEM); - } else { - info->fw_state = cpu_to_le16(MFI_PD_STATE_ONLINE); - } - } else { - info->fw_state = cpu_to_le16(MFI_PD_STATE_OFFLINE); - } - - info->ref.v.device_id = cpu_to_le16(pd_id); - info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD| - MFI_PD_DDF_TYPE_INTF_SAS); - blk_get_geometry(sdev->conf.blk, &pd_size); - info->raw_size = cpu_to_le64(pd_size); - info->non_coerced_size = cpu_to_le64(pd_size); - info->coerced_size = cpu_to_le64(pd_size); - info->encl_device_id = 0xFFFF; - info->slot_number = (sdev->id & 0xFF); - info->path_info.count = 1; - info->path_info.sas_addr[0] = - cpu_to_le64(megasas_get_sata_addr(pd_id)); - info->connected_port_bitmap = 0x1; - info->device_speed = 1; - info->link_speed = 1; - resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg); - g_free(cmd->iov_buf); - cmd->iov_size = dcmd_size - resid; - cmd->iov_buf = NULL; - return MFI_STAT_OK; -} - -static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd) -{ - size_t dcmd_size = sizeof(struct mfi_pd_info); - uint16_t pd_id; - uint8_t target_id, lun_id; - SCSIDevice *sdev = NULL; - int retval = MFI_STAT_DEVICE_NOT_FOUND; - - if (cmd->iov_size < dcmd_size) { - return MFI_STAT_INVALID_PARAMETER; - } - - /* mbox0 has the ID */ - pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - target_id = (pd_id >> 8) & 0xFF; - lun_id = pd_id & 0xFF; - sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); - trace_megasas_dcmd_pd_get_info(cmd->index, pd_id); - - if (sdev) { - /* Submit inquiry */ - retval = megasas_pd_get_info_submit(sdev, pd_id, cmd); - } - - return retval; -} - -static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ld_list info; - size_t dcmd_size = sizeof(info), resid; - uint32_t num_ld_disks = 0, max_ld_disks; - uint64_t ld_size; - BusChild *kid; - - memset(&info, 0, dcmd_size); - if (cmd->iov_size > dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - - max_ld_disks = (cmd->iov_size - 8) / 16; - if (megasas_is_jbod(s)) { - max_ld_disks = 0; - } - if (max_ld_disks > MFI_MAX_LD) { - max_ld_disks = MFI_MAX_LD; - } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - - if (num_ld_disks >= max_ld_disks) { - break; - } - /* Logical device size is in blocks */ - blk_get_geometry(sdev->conf.blk, &ld_size); - info.ld_list[num_ld_disks].ld.v.target_id = sdev->id; - info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL; - info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size); - num_ld_disks++; - } - info.ld_count = cpu_to_le32(num_ld_disks); - trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks); - - resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - cmd->iov_size = dcmd_size - resid; - return MFI_STAT_OK; -} - -static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd) -{ - uint16_t flags; - struct mfi_ld_targetid_list info; - size_t dcmd_size = sizeof(info), resid; - uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns; - BusChild *kid; - - /* mbox0 contains flags */ - flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_ld_list_query(cmd->index, flags); - if (flags != MR_LD_QUERY_TYPE_ALL && - flags != MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) { - max_ld_disks = 0; - } - - memset(&info, 0, dcmd_size); - if (cmd->iov_size < 12) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - dcmd_size = sizeof(uint32_t) * 2 + 3; - max_ld_disks = cmd->iov_size - dcmd_size; - if (megasas_is_jbod(s)) { - max_ld_disks = 0; - } - if (max_ld_disks > MFI_MAX_LD) { - max_ld_disks = MFI_MAX_LD; - } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - - if (num_ld_disks >= max_ld_disks) { - break; - } - info.targetid[num_ld_disks] = sdev->lun; - num_ld_disks++; - dcmd_size++; - } - info.ld_count = cpu_to_le32(num_ld_disks); - info.size = dcmd_size; - trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks); - - resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - cmd->iov_size = dcmd_size - resid; - return MFI_STAT_OK; -} - -static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, - MegasasCmd *cmd) -{ - struct mfi_ld_info *info = cmd->iov_buf; - size_t dcmd_size = sizeof(struct mfi_ld_info); - uint8_t cdb[6]; - SCSIRequest *req; - ssize_t len, resid; - uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); - uint64_t ld_size; - - if (!cmd->iov_buf) { - cmd->iov_buf = g_malloc0(dcmd_size); - info = cmd->iov_buf; - megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); - if (!req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "LD get info vpd inquiry"); - g_free(cmd->iov_buf); - cmd->iov_buf = NULL; - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "LD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); - if (len > 0) { - cmd->iov_size = len; - scsi_req_continue(req); - } - return MFI_STAT_INVALID_STATUS; - } - - info->ld_config.params.state = MFI_LD_STATE_OPTIMAL; - info->ld_config.properties.ld.v.target_id = lun; - info->ld_config.params.stripe_size = 3; - info->ld_config.params.num_drives = 1; - info->ld_config.params.is_consistent = 1; - /* Logical device size is in blocks */ - blk_get_geometry(sdev->conf.blk, &ld_size); - info->size = cpu_to_le64(ld_size); - memset(info->ld_config.span, 0, sizeof(info->ld_config.span)); - info->ld_config.span[0].start_block = 0; - info->ld_config.span[0].num_blocks = info->size; - info->ld_config.span[0].array_ref = cpu_to_le16(sdev_id); - - resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg); - g_free(cmd->iov_buf); - cmd->iov_size = dcmd_size - resid; - cmd->iov_buf = NULL; - return MFI_STAT_OK; -} - -static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ld_info info; - size_t dcmd_size = sizeof(info); - uint16_t ld_id; - uint32_t max_ld_disks = s->fw_luns; - SCSIDevice *sdev = NULL; - int retval = MFI_STAT_DEVICE_NOT_FOUND; - - if (cmd->iov_size < dcmd_size) { - return MFI_STAT_INVALID_PARAMETER; - } - - /* mbox0 has the ID */ - ld_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_ld_get_info(cmd->index, ld_id); - - if (megasas_is_jbod(s)) { - return MFI_STAT_DEVICE_NOT_FOUND; - } - - if (ld_id < max_ld_disks) { - sdev = scsi_device_find(&s->bus, 0, ld_id, 0); - } - - if (sdev) { - retval = megasas_ld_get_info_submit(sdev, ld_id, cmd); - } - - return retval; -} - -static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) -{ - uint8_t data[4096]; - struct mfi_config_data *info; - int num_pd_disks = 0, array_offset, ld_offset; - BusChild *kid; - - if (cmd->iov_size > 4096) { - return MFI_STAT_INVALID_PARAMETER; - } - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - num_pd_disks++; - } - info = (struct mfi_config_data *)&data; - /* - * Array mapping: - * - One array per SCSI device - * - One logical drive per SCSI device - * spanning the entire device - */ - info->array_count = num_pd_disks; - info->array_size = sizeof(struct mfi_array) * num_pd_disks; - info->log_drv_count = num_pd_disks; - info->log_drv_size = sizeof(struct mfi_ld_config) * num_pd_disks; - info->spares_count = 0; - info->spares_size = sizeof(struct mfi_spare); - info->size = sizeof(struct mfi_config_data) + info->array_size + - info->log_drv_size; - if (info->size > 4096) { - return MFI_STAT_INVALID_PARAMETER; - } - - array_offset = sizeof(struct mfi_config_data); - ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks; - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); - struct mfi_array *array; - struct mfi_ld_config *ld; - uint64_t pd_size; - int i; - - array = (struct mfi_array *)(data + array_offset); - blk_get_geometry(sdev->conf.blk, &pd_size); - array->size = cpu_to_le64(pd_size); - array->num_drives = 1; - array->array_ref = cpu_to_le16(sdev_id); - array->pd[0].ref.v.device_id = cpu_to_le16(sdev_id); - array->pd[0].ref.v.seq_num = 0; - array->pd[0].fw_state = MFI_PD_STATE_ONLINE; - array->pd[0].encl.pd = 0xFF; - array->pd[0].encl.slot = (sdev->id & 0xFF); - for (i = 1; i < MFI_MAX_ROW_SIZE; i++) { - array->pd[i].ref.v.device_id = 0xFFFF; - array->pd[i].ref.v.seq_num = 0; - array->pd[i].fw_state = MFI_PD_STATE_UNCONFIGURED_GOOD; - array->pd[i].encl.pd = 0xFF; - array->pd[i].encl.slot = 0xFF; - } - array_offset += sizeof(struct mfi_array); - ld = (struct mfi_ld_config *)(data + ld_offset); - memset(ld, 0, sizeof(struct mfi_ld_config)); - ld->properties.ld.v.target_id = sdev->id; - ld->properties.default_cache_policy = MR_LD_CACHE_READ_AHEAD | - MR_LD_CACHE_READ_ADAPTIVE; - ld->properties.current_cache_policy = MR_LD_CACHE_READ_AHEAD | - MR_LD_CACHE_READ_ADAPTIVE; - ld->params.state = MFI_LD_STATE_OPTIMAL; - ld->params.stripe_size = 3; - ld->params.num_drives = 1; - ld->params.span_depth = 1; - ld->params.is_consistent = 1; - ld->span[0].start_block = 0; - ld->span[0].num_blocks = cpu_to_le64(pd_size); - ld->span[0].array_ref = cpu_to_le16(sdev_id); - ld_offset += sizeof(struct mfi_ld_config); - } - - cmd->iov_size -= dma_buf_read((uint8_t *)data, info->size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ctrl_props info; - size_t dcmd_size = sizeof(info); - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - info.pred_fail_poll_interval = cpu_to_le16(300); - info.intr_throttle_cnt = cpu_to_le16(16); - info.intr_throttle_timeout = cpu_to_le16(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 = cpu_to_le16(1440); - info.expose_encl_devices = 1; - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_cache_flush(MegasasState *s, MegasasCmd *cmd) -{ - blk_drain_all(); - return MFI_STAT_OK; -} - -static int megasas_ctrl_shutdown(MegasasState *s, MegasasCmd *cmd) -{ - s->fw_state = MFI_FWSTATE_READY; - return MFI_STAT_OK; -} - -/* Some implementations use CLUSTER RESET LD to simulate a device reset */ -static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd) -{ - uint16_t target_id; - int i; - - /* mbox0 contains the device index */ - target_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_reset_ld(cmd->index, target_id); - for (i = 0; i < s->fw_cmds; i++) { - MegasasCmd *tmp_cmd = &s->frames[i]; - if (tmp_cmd->req && tmp_cmd->req->dev->id == target_id) { - SCSIDevice *d = tmp_cmd->req->dev; - qdev_reset_all(&d->qdev); - } - } - return MFI_STAT_OK; -} - -static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ctrl_props info; - size_t dcmd_size = sizeof(info); - - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg); - trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size); - return MFI_STAT_OK; -} - -static int megasas_dcmd_dummy(MegasasState *s, MegasasCmd *cmd) -{ - trace_megasas_dcmd_dummy(cmd->index, cmd->iov_size); - return MFI_STAT_OK; -} - -static const struct dcmd_cmd_tbl_t { - int opcode; - const char *desc; - int (*func)(MegasasState *s, MegasasCmd *cmd); -} dcmd_cmd_tbl[] = { - { MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC, "CTRL_HOST_MEM_ALLOC", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_GET_INFO, "CTRL_GET_INFO", - megasas_ctrl_get_info }, - { MFI_DCMD_CTRL_GET_PROPERTIES, "CTRL_GET_PROPERTIES", - megasas_dcmd_get_properties }, - { MFI_DCMD_CTRL_SET_PROPERTIES, "CTRL_SET_PROPERTIES", - megasas_dcmd_set_properties }, - { MFI_DCMD_CTRL_ALARM_GET, "CTRL_ALARM_GET", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_ENABLE, "CTRL_ALARM_ENABLE", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_DISABLE, "CTRL_ALARM_DISABLE", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_SILENCE, "CTRL_ALARM_SILENCE", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_TEST, "CTRL_ALARM_TEST", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_EVENT_GETINFO, "CTRL_EVENT_GETINFO", - megasas_event_info }, - { MFI_DCMD_CTRL_EVENT_GET, "CTRL_EVENT_GET", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_EVENT_WAIT, "CTRL_EVENT_WAIT", - megasas_event_wait }, - { MFI_DCMD_CTRL_SHUTDOWN, "CTRL_SHUTDOWN", - megasas_ctrl_shutdown }, - { MFI_DCMD_HIBERNATE_STANDBY, "CTRL_STANDBY", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_GET_TIME, "CTRL_GET_TIME", - megasas_dcmd_get_fw_time }, - { MFI_DCMD_CTRL_SET_TIME, "CTRL_SET_TIME", - megasas_dcmd_set_fw_time }, - { MFI_DCMD_CTRL_BIOS_DATA_GET, "CTRL_BIOS_DATA_GET", - megasas_dcmd_get_bios_info }, - { MFI_DCMD_CTRL_FACTORY_DEFAULTS, "CTRL_FACTORY_DEFAULTS", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_MFC_DEFAULTS_GET, "CTRL_MFC_DEFAULTS_GET", - megasas_mfc_get_defaults }, - { MFI_DCMD_CTRL_MFC_DEFAULTS_SET, "CTRL_MFC_DEFAULTS_SET", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_CACHE_FLUSH, "CTRL_CACHE_FLUSH", - megasas_cache_flush }, - { MFI_DCMD_PD_GET_LIST, "PD_GET_LIST", - megasas_dcmd_pd_get_list }, - { MFI_DCMD_PD_LIST_QUERY, "PD_LIST_QUERY", - megasas_dcmd_pd_list_query }, - { MFI_DCMD_PD_GET_INFO, "PD_GET_INFO", - megasas_dcmd_pd_get_info }, - { MFI_DCMD_PD_STATE_SET, "PD_STATE_SET", - megasas_dcmd_dummy }, - { MFI_DCMD_PD_REBUILD, "PD_REBUILD", - megasas_dcmd_dummy }, - { MFI_DCMD_PD_BLINK, "PD_BLINK", - megasas_dcmd_dummy }, - { MFI_DCMD_PD_UNBLINK, "PD_UNBLINK", - megasas_dcmd_dummy }, - { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST", - megasas_dcmd_ld_get_list}, - { MFI_DCMD_LD_LIST_QUERY, "LD_LIST_QUERY", - megasas_dcmd_ld_list_query }, - { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO", - megasas_dcmd_ld_get_info }, - { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP", - megasas_dcmd_dummy }, - { MFI_DCMD_LD_SET_PROP, "LD_SET_PROP", - megasas_dcmd_dummy }, - { MFI_DCMD_LD_DELETE, "LD_DELETE", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_READ, "CFG_READ", - megasas_dcmd_cfg_read }, - { MFI_DCMD_CFG_ADD, "CFG_ADD", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_CLEAR, "CFG_CLEAR", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_FOREIGN_READ, "CFG_FOREIGN_READ", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_FOREIGN_IMPORT, "CFG_FOREIGN_IMPORT", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_STATUS, "BBU_STATUS", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_CAPACITY_INFO, "BBU_CAPACITY_INFO", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_DESIGN_INFO, "BBU_DESIGN_INFO", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_PROP_GET, "BBU_PROP_GET", - megasas_dcmd_dummy }, - { MFI_DCMD_CLUSTER, "CLUSTER", - megasas_dcmd_dummy }, - { MFI_DCMD_CLUSTER_RESET_ALL, "CLUSTER_RESET_ALL", - megasas_dcmd_dummy }, - { MFI_DCMD_CLUSTER_RESET_LD, "CLUSTER_RESET_LD", - megasas_cluster_reset_ld }, - { -1, NULL, NULL } -}; - -static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) -{ - int opcode, len; - int retval = 0; - const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl; - - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - trace_megasas_handle_dcmd(cmd->index, opcode); - len = megasas_map_dcmd(s, cmd); - if (len < 0) { - return MFI_STAT_MEMORY_NOT_AVAILABLE; - } - while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) { - cmdptr++; - } - if (cmdptr->opcode == -1) { - trace_megasas_dcmd_unhandled(cmd->index, opcode, len); - retval = megasas_dcmd_dummy(s, cmd); - } else { - trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len); - retval = cmdptr->func(s, cmd); - } - if (retval != MFI_STAT_INVALID_STATUS) { - megasas_finish_dcmd(cmd, len); - } - return retval; -} - -static int megasas_finish_internal_dcmd(MegasasCmd *cmd, - SCSIRequest *req) -{ - int opcode; - int retval = MFI_STAT_OK; - int lun = req->lun; - - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - scsi_req_unref(req); - trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun); - switch (opcode) { - case MFI_DCMD_PD_GET_INFO: - retval = megasas_pd_get_info_submit(req->dev, lun, cmd); - break; - case MFI_DCMD_LD_GET_INFO: - retval = megasas_ld_get_info_submit(req->dev, lun, cmd); - break; - default: - trace_megasas_dcmd_internal_invalid(cmd->index, opcode); - retval = MFI_STAT_INVALID_DCMD; - break; - } - if (retval != MFI_STAT_INVALID_STATUS) { - megasas_finish_dcmd(cmd, cmd->iov_size); - } - return retval; -} - -static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write) -{ - int len; - - len = scsi_req_enqueue(cmd->req); - if (len < 0) { - len = -len; - } - if (len > 0) { - if (len > cmd->iov_size) { - if (is_write) { - trace_megasas_iov_write_overflow(cmd->index, len, - cmd->iov_size); - } else { - trace_megasas_iov_read_overflow(cmd->index, len, - cmd->iov_size); - } - } - if (len < cmd->iov_size) { - if (is_write) { - trace_megasas_iov_write_underflow(cmd->index, len, - cmd->iov_size); - } else { - trace_megasas_iov_read_underflow(cmd->index, len, - cmd->iov_size); - } - cmd->iov_size = len; - } - scsi_req_continue(cmd->req); - } - return len; -} - -static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, - bool is_logical) -{ - uint8_t *cdb; - bool is_write; - struct SCSIDevice *sdev = NULL; - - cdb = cmd->frame->pass.cdb; - - if (is_logical) { - if (cmd->frame->header.target_id >= MFI_MAX_LD || - cmd->frame->header.lun_id != 0) { - trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); - return MFI_STAT_DEVICE_NOT_FOUND; - } - } - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); - - cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len); - trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd], - is_logical, cmd->frame->header.target_id, - cmd->frame->header.lun_id, sdev, cmd->iov_size); - - if (!sdev || (megasas_is_jbod(s) && is_logical)) { - trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); - return MFI_STAT_DEVICE_NOT_FOUND; - } - - if (cmd->frame->header.cdb_len > 16) { - trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); - megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - if (megasas_map_sgl(s, cmd, &cmd->frame->pass.sgl)) { - megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); - if (!cmd->req) { - trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); - megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); - cmd->frame->header.scsi_status = BUSY; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - is_write = (cmd->req->cmd.mode == SCSI_XFER_TO_DEV); - if (cmd->iov_size) { - if (is_write) { - trace_megasas_scsi_write_start(cmd->index, cmd->iov_size); - } else { - trace_megasas_scsi_read_start(cmd->index, cmd->iov_size); - } - } else { - trace_megasas_scsi_nodata(cmd->index); - } - megasas_enqueue_req(cmd, is_write); - return MFI_STAT_INVALID_STATUS; -} - -static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) -{ - uint32_t lba_count, lba_start_hi, lba_start_lo; - uint64_t lba_start; - bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE); - uint8_t cdb[16]; - int len; - struct SCSIDevice *sdev = NULL; - - lba_count = le32_to_cpu(cmd->frame->io.header.data_len); - lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo); - lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi); - lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo; - - if (cmd->frame->header.target_id < MFI_MAX_LD && - cmd->frame->header.lun_id == 0) { - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); - } - - trace_megasas_handle_io(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, - cmd->frame->header.lun_id, - (unsigned long)lba_start, (unsigned long)lba_count); - if (!sdev) { - trace_megasas_io_target_not_present(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); - return MFI_STAT_DEVICE_NOT_FOUND; - } - - if (cmd->frame->header.cdb_len > 16) { - trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], 1, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); - megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - cmd->iov_size = lba_count * sdev->blocksize; - if (megasas_map_sgl(s, cmd, &cmd->frame->io.sgl)) { - megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - megasas_encode_lba(cdb, lba_start, lba_count, is_write); - cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); - if (!cmd->req) { - trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); - megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); - cmd->frame->header.scsi_status = BUSY; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - len = megasas_enqueue_req(cmd, is_write); - if (len > 0) { - if (is_write) { - trace_megasas_io_write_start(cmd->index, lba_start, lba_count, len); - } else { - trace_megasas_io_read_start(cmd->index, lba_start, lba_count, len); - } - } - return MFI_STAT_INVALID_STATUS; -} - -static int megasas_finish_internal_command(MegasasCmd *cmd, - SCSIRequest *req, size_t resid) -{ - int retval = MFI_STAT_INVALID_CMD; - - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { - cmd->iov_size -= resid; - retval = megasas_finish_internal_dcmd(cmd, req); - } - return retval; -} - -static QEMUSGList *megasas_get_sg_list(SCSIRequest *req) -{ - MegasasCmd *cmd = req->hba_private; - - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { - return NULL; - } else { - return &cmd->qsg; - } -} - -static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) -{ - MegasasCmd *cmd = req->hba_private; - uint8_t *buf; - uint32_t opcode; - - trace_megasas_io_complete(cmd->index, len); - - if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) { - scsi_req_continue(req); - return; - } - - buf = scsi_req_get_buf(req); - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) { - struct mfi_pd_info *info = cmd->iov_buf; - - if (info->inquiry_data[0] == 0x7f) { - memset(info->inquiry_data, 0, sizeof(info->inquiry_data)); - memcpy(info->inquiry_data, buf, len); - } else if (info->vpd_page83[0] == 0x7f) { - memset(info->vpd_page83, 0, sizeof(info->vpd_page83)); - memcpy(info->vpd_page83, buf, len); - } - scsi_req_continue(req); - } else if (opcode == MFI_DCMD_LD_GET_INFO) { - struct mfi_ld_info *info = cmd->iov_buf; - - if (cmd->iov_buf) { - memcpy(info->vpd_page83, buf, sizeof(info->vpd_page83)); - scsi_req_continue(req); - } - } -} - -static void megasas_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - MegasasCmd *cmd = req->hba_private; - uint8_t cmd_status = MFI_STAT_OK; - - trace_megasas_command_complete(cmd->index, status, resid); - - if (cmd->req != req) { - /* - * Internal command complete - */ - cmd_status = megasas_finish_internal_command(cmd, req, resid); - if (cmd_status == MFI_STAT_INVALID_STATUS) { - return; - } - } else { - req->status = status; - trace_megasas_scsi_complete(cmd->index, req->status, - cmd->iov_size, req->cmd.xfer); - if (req->status != GOOD) { - cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR; - } - if (req->status == CHECK_CONDITION) { - megasas_copy_sense(cmd); - } - - megasas_unmap_sgl(cmd); - cmd->frame->header.scsi_status = req->status; - scsi_req_unref(cmd->req); - cmd->req = NULL; - } - cmd->frame->header.cmd_status = cmd_status; - megasas_unmap_frame(cmd->state, cmd); - megasas_complete_frame(cmd->state, cmd->context); -} - -static void megasas_command_cancel(SCSIRequest *req) -{ - MegasasCmd *cmd = req->hba_private; - - if (cmd) { - megasas_abort_command(cmd); - } else { - scsi_req_unref(req); - } -} - -static int megasas_handle_abort(MegasasState *s, MegasasCmd *cmd) -{ - uint64_t abort_ctx = le64_to_cpu(cmd->frame->abort.abort_context); - hwaddr abort_addr, addr_hi, addr_lo; - MegasasCmd *abort_cmd; - - addr_hi = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_hi); - addr_lo = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_lo); - abort_addr = ((uint64_t)addr_hi << 32) | addr_lo; - - abort_cmd = megasas_lookup_frame(s, abort_addr); - if (!abort_cmd) { - trace_megasas_abort_no_cmd(cmd->index, abort_ctx); - s->event_count++; - return MFI_STAT_OK; - } - if (!megasas_use_queue64(s)) { - abort_ctx &= (uint64_t)0xFFFFFFFF; - } - if (abort_cmd->context != abort_ctx) { - trace_megasas_abort_invalid_context(cmd->index, abort_cmd->index, - abort_cmd->context); - s->event_count++; - return MFI_STAT_ABORT_NOT_POSSIBLE; - } - trace_megasas_abort_frame(cmd->index, abort_cmd->index); - megasas_abort_command(abort_cmd); - if (!s->event_cmd || abort_cmd != s->event_cmd) { - s->event_cmd = NULL; - } - s->event_count++; - return MFI_STAT_OK; -} - -static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, - uint32_t frame_count) -{ - uint8_t frame_status = MFI_STAT_INVALID_CMD; - uint64_t frame_context; - MegasasCmd *cmd; - - /* - * Always read 64bit context, top bits will be - * masked out if required in megasas_enqueue_frame() - */ - frame_context = megasas_frame_get_context(s, frame_addr); - - cmd = megasas_enqueue_frame(s, frame_addr, frame_context, frame_count); - if (!cmd) { - /* reply queue full */ - trace_megasas_frame_busy(frame_addr); - megasas_frame_set_scsi_status(s, frame_addr, BUSY); - megasas_frame_set_cmd_status(s, frame_addr, MFI_STAT_SCSI_DONE_WITH_ERROR); - megasas_complete_frame(s, frame_context); - s->event_count++; - return; - } - switch (cmd->frame->header.frame_cmd) { - case MFI_CMD_INIT: - frame_status = megasas_init_firmware(s, cmd); - break; - case MFI_CMD_DCMD: - frame_status = megasas_handle_dcmd(s, cmd); - break; - case MFI_CMD_ABORT: - 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, 1); - break; - case MFI_CMD_LD_READ: - case MFI_CMD_LD_WRITE: - frame_status = megasas_handle_io(s, cmd); - break; - default: - trace_megasas_unhandled_frame_cmd(cmd->index, - cmd->frame->header.frame_cmd); - s->event_count++; - break; - } - if (frame_status != MFI_STAT_INVALID_STATUS) { - if (cmd->frame) { - cmd->frame->header.cmd_status = frame_status; - } else { - megasas_frame_set_cmd_status(s, frame_addr, frame_status); - } - megasas_unmap_frame(s, cmd); - megasas_complete_frame(s, cmd->context); - } -} - -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) { - case MFI_IDB: - retval = 0; - trace_megasas_mmio_readl("MFI_IDB", retval); - break; - case MFI_OMSG0: - case MFI_OSP0: - 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); - trace_megasas_mmio_readl(addr == MFI_OMSG0 ? "MFI_OMSG0" : "MFI_OSP0", - retval); - break; - case MFI_OSTS: - if (megasas_intr_enabled(s) && s->doorbell) { - retval = base_class->osts; - } - trace_megasas_mmio_readl("MFI_OSTS", retval); - break; - case MFI_OMSK: - retval = s->intr_mask; - trace_megasas_mmio_readl("MFI_OMSK", retval); - break; - case MFI_ODCR0: - retval = s->doorbell ? 1 : 0; - trace_megasas_mmio_readl("MFI_ODCR0", retval); - break; - case MFI_DIAG: - retval = s->diag; - trace_megasas_mmio_readl("MFI_DIAG", retval); - break; - case MFI_OSP1: - retval = 15; - trace_megasas_mmio_readl("MFI_OSP1", retval); - break; - default: - trace_megasas_mmio_invalid_readl(addr); - break; - } - 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) -{ - MegasasState *s = opaque; - PCIDevice *pci_dev = PCI_DEVICE(s); - uint64_t frame_addr; - uint32_t frame_count; - int i; - - switch (addr) { - case MFI_IDB: - trace_megasas_mmio_writel("MFI_IDB", val); - if (val & MFI_FWINIT_ABORT) { - /* Abort all pending cmds */ - for (i = 0; i < s->fw_cmds; i++) { - megasas_abort_command(&s->frames[i]); - } - } - if (val & MFI_FWINIT_READY) { - /* move to FW READY */ - megasas_soft_reset(s); - } - 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: - trace_megasas_mmio_writel("MFI_OMSK", val); - s->intr_mask = val; - if (!megasas_intr_enabled(s) && - !msi_enabled(pci_dev) && - !msix_enabled(pci_dev)) { - trace_megasas_irq_lower(); - pci_irq_deassert(pci_dev); - } - if (megasas_intr_enabled(s)) { - if (msix_enabled(pci_dev)) { - trace_megasas_msix_enabled(0); - } else if (msi_enabled(pci_dev)) { - trace_megasas_msi_enabled(0); - } else { - trace_megasas_intr_enabled(); - } - } else { - trace_megasas_intr_disabled(); - megasas_soft_reset(s); - } - break; - case MFI_ODCR0: - trace_megasas_mmio_writel("MFI_ODCR0", val); - s->doorbell = 0; - if (megasas_intr_enabled(s)) { - if (!msix_enabled(pci_dev) && !msi_enabled(pci_dev)) { - trace_megasas_irq_lower(); - pci_irq_deassert(pci_dev); - } - } - break; - case MFI_IQPH: - trace_megasas_mmio_writel("MFI_IQPH", val); - /* Received high 32 bits of a 64 bit MFI frame address */ - s->frame_hi = val; - break; - case MFI_IQPL: - trace_megasas_mmio_writel("MFI_IQPL", val); - /* Received low 32 bits of a 64 bit MFI frame address */ - /* Fallthrough */ - case MFI_IQP: - if (addr == MFI_IQP) { - trace_megasas_mmio_writel("MFI_IQP", val); - /* Received 64 bit MFI frame address */ - s->frame_hi = 0; - } - frame_addr = (val & ~0x1F); - /* Add possible 64 bit offset */ - frame_addr |= ((uint64_t)s->frame_hi << 32); - s->frame_hi = 0; - frame_count = (val >> 1) & 0xF; - megasas_handle_frame(s, frame_addr, frame_count); - break; - case MFI_SEQ: - trace_megasas_mmio_writel("MFI_SEQ", val); - /* 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: - trace_megasas_mmio_writel("MFI_DIAG", val); - /* 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; - } -} - -static const MemoryRegionOps megasas_mmio_ops = { - .read = megasas_mmio_read, - .write = megasas_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - } -}; - -static uint64_t megasas_port_read(void *opaque, hwaddr addr, - unsigned size) -{ - return megasas_mmio_read(opaque, addr & 0xff, size); -} - -static void megasas_port_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - megasas_mmio_write(opaque, addr & 0xff, val, size); -} - -static const MemoryRegionOps megasas_port_ops = { - .read = megasas_port_read, - .write = megasas_port_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -static uint64_t megasas_queue_read(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void megasas_queue_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - return; -} - -static const MemoryRegionOps megasas_queue_ops = { - .read = megasas_queue_read, - .write = megasas_queue_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - } -}; - -static void megasas_soft_reset(MegasasState *s) -{ - int i; - MegasasCmd *cmd; - - trace_megasas_reset(s->fw_state); - for (i = 0; i < s->fw_cmds; i++) { - cmd = &s->frames[i]; - megasas_abort_command(cmd); - } - if (s->fw_state == MFI_FWSTATE_READY) { - BusChild *kid; - - /* - * The EFI firmware doesn't handle UA, - * so we need to clear the Power On/Reset UA - * after the initial reset. - */ - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - - sdev->unit_attention = SENSE_CODE(NO_SENSE); - scsi_device_unit_attention_reported(sdev); - } - } - megasas_reset_frames(s); - s->reply_queue_len = s->fw_cmds; - s->reply_queue_pa = 0; - s->consumer_pa = 0; - s->producer_pa = 0; - s->fw_state = MFI_FWSTATE_READY; - s->doorbell = 0; - s->intr_mask = MEGASAS_INTR_DISABLED_MASK; - s->frame_hi = 0; - s->flags &= ~MEGASAS_MASK_USE_QUEUE64; - s->event_count++; - s->boot_event = s->event_count; -} - -static void megasas_scsi_reset(DeviceState *dev) -{ - MegasasState *s = MEGASAS(dev); - - megasas_soft_reset(s); -} - -static const VMStateDescription vmstate_megasas_gen1 = { - .name = "megasas", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_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 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); - - if (megasas_use_msix(s)) { - msix_uninit(d, &s->mmio_io, &s->mmio_io); - } - if (megasas_use_msi(s)) { - msi_uninit(d); - } -} - -static const struct SCSIBusInfo megasas_scsi_info = { - .tcq = true, - .max_target = MFI_MAX_LD, - .max_lun = 255, - - .transfer_data = megasas_xfer_complete, - .get_sg_list = megasas_get_sg_list, - .complete = megasas_command_complete, - .cancel = megasas_command_cancel, -}; - -static void megasas_scsi_realize(PCIDevice *dev, Error **errp) -{ - DeviceState *d = DEVICE(dev); - MegasasState *s = MEGASAS(dev); - MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); - uint8_t *pci_conf; - int i, bar_type; - - pci_conf = dev->config; - - /* PCI latency timer = 0 */ - pci_conf[PCI_LATENCY_TIMER] = 0; - /* Interrupt pin 1 */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - memory_region_init_io(&s->mmio_io, OBJECT(s), &megasas_mmio_ops, s, - "megasas-mmio", 0x4000); - memory_region_init_io(&s->port_io, OBJECT(s), &megasas_port_ops, s, - "megasas-io", 256); - memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s, - "megasas-queue", 0x40000); - - if (megasas_use_msi(s) && - msi_init(dev, 0x50, 1, true, false)) { - s->flags &= ~MEGASAS_MASK_USE_MSI; - } - if (megasas_use_msix(s) && - 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, 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)) { - msix_vector_use(dev, 0); - } - - s->fw_state = MFI_FWSTATE_READY; - 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->hba_serial) { - s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL); - } - 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) { - s->fw_sge = 128 - MFI_PASS_FRAME_SIZE; - } else { - s->fw_sge = 64 - MFI_PASS_FRAME_SIZE; - } - if (s->fw_cmds > MEGASAS_MAX_FRAMES) { - s->fw_cmds = MEGASAS_MAX_FRAMES; - } - trace_megasas_init(s->fw_sge, s->fw_cmds, - megasas_is_jbod(s) ? "jbod" : "raid"); - - if (megasas_is_jbod(s)) { - s->fw_luns = MFI_MAX_SYS_PDS; - } else { - s->fw_luns = MFI_MAX_LD; - } - s->producer_pa = 0; - s->consumer_pa = 0; - for (i = 0; i < s->fw_cmds; i++) { - s->frames[i].index = i; - s->frames[i].context = -1; - s->frames[i].pa = 0; - s->frames[i].state = s; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &megasas_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static Property megasas_properties_gen1[] = { - DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, - MEGASAS_DEFAULT_SGE), - DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, - MEGASAS_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, false), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, false), - DEFINE_PROP_BIT("use_jbod", MegasasState, flags, - MEGASAS_FLAG_USE_JBOD, false), - 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->realize = megasas_scsi_realize; - pc->exit = megasas_scsi_uninit; - pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->device_id = info->device_id; - pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->subsystem_id = info->subsystem_id; - pc->class_id = PCI_CLASS_STORAGE_RAID; - 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 = info->vmsd; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = info->desc; -} - -static const TypeInfo megasas_info = { - .name = TYPE_MEGASAS_BASE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(MegasasState), - .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/qemu/hw/scsi/mfi.h b/qemu/hw/scsi/mfi.h deleted file mode 100644 index 29d41775d..000000000 --- a/qemu/hw/scsi/mfi.h +++ /dev/null @@ -1,1272 +0,0 @@ -/* - * NetBSD header file, copied from - * http://gitorious.org/freebsd/freebsd/blobs/HEAD/sys/dev/mfi/mfireg.h - */ -/*- - * Copyright (c) 2006 IronPort Systems - * Copyright (c) 2007 LSI Corp. - * Copyright (c) 2007 Rajesh Prabhakaran. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MFI_REG_H -#define MFI_REG_H - -/* - * MegaRAID SAS MFI firmware definitions - */ - -/* - * Start with the register set. All registers are 32 bits wide. - * The usual Intel IOP style setup. - */ -#define MFI_IMSG0 0x10 /* Inbound message 0 */ -#define MFI_IMSG1 0x14 /* Inbound message 1 */ -#define MFI_OMSG0 0x18 /* Outbound message 0 */ -#define MFI_OMSG1 0x1c /* Outbound message 1 */ -#define MFI_IDB 0x20 /* Inbound doorbell */ -#define MFI_ISTS 0x24 /* Inbound interrupt status */ -#define MFI_IMSK 0x28 /* Inbound interrupt mask */ -#define MFI_ODB 0x2c /* Outbound doorbell */ -#define MFI_OSTS 0x30 /* Outbound interrupt status */ -#define MFI_OMSK 0x34 /* Outbound interrupt mask */ -#define MFI_IQP 0x40 /* Inbound queue port */ -#define MFI_OQP 0x44 /* Outbound queue port */ - -/* - * 1078 specific related register - */ -#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 */ -#define MFI_SEQ 0xfc /* Sequencer offset */ -#define MFI_1078_EIM 0x80000004 /* 1078 enable intrrupt mask */ -#define MFI_RMI 0x2 /* reply message interrupt */ -#define MFI_1078_RM 0x80000000 /* reply 1078 message interrupt */ -#define MFI_ODC 0x4 /* outbound doorbell change interrupt */ - -/* - * gen2 specific changes - */ -#define MFI_GEN2_EIM 0x00000005 /* gen2 enable interrupt mask */ -#define MFI_GEN2_RM 0x00000001 /* reply gen2 message interrupt */ - -/* - * skinny specific changes - */ -#define MFI_SKINNY_IDB 0x00 /* Inbound doorbell is at 0x00 for skinny */ -#define MFI_SKINNY_RM 0x00000001 /* reply skinny message interrupt */ - -/* Bits for MFI_OSTS */ -#define MFI_OSTS_INTR_VALID 0x00000002 - -/* - * Firmware state values. Found in OMSG0 during initialization. - */ -#define MFI_FWSTATE_MASK 0xf0000000 -#define MFI_FWSTATE_UNDEFINED 0x00000000 -#define MFI_FWSTATE_BB_INIT 0x10000000 -#define MFI_FWSTATE_FW_INIT 0x40000000 -#define MFI_FWSTATE_WAIT_HANDSHAKE 0x60000000 -#define MFI_FWSTATE_FW_INIT_2 0x70000000 -#define MFI_FWSTATE_DEVICE_SCAN 0x80000000 -#define MFI_FWSTATE_BOOT_MSG_PENDING 0x90000000 -#define MFI_FWSTATE_FLUSH_CACHE 0xa0000000 -#define MFI_FWSTATE_READY 0xb0000000 -#define MFI_FWSTATE_OPERATIONAL 0xc0000000 -#define MFI_FWSTATE_FAULT 0xf0000000 -#define MFI_FWSTATE_MAXSGL_MASK 0x00ff0000 -#define MFI_FWSTATE_MAXCMD_MASK 0x0000ffff -#define MFI_FWSTATE_MSIX_SUPPORTED 0x04000000 -#define MFI_FWSTATE_HOSTMEMREQD_MASK 0x08000000 - -/* - * Control bits to drive the card to ready state. These go into the IDB - * register. - */ -#define MFI_FWINIT_ABORT 0x00000001 /* Abort all pending commands */ -#define MFI_FWINIT_READY 0x00000002 /* Move from operational to ready */ -#define MFI_FWINIT_MFIMODE 0x00000004 /* unknown */ -#define MFI_FWINIT_CLEAR_HANDSHAKE 0x00000008 /* Respond to WAIT_HANDSHAKE */ -#define MFI_FWINIT_HOTPLUG 0x00000010 -#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, - MFI_CMD_LD_READ, - MFI_CMD_LD_WRITE, - MFI_CMD_LD_SCSI_IO, - MFI_CMD_PD_SCSI_IO, - MFI_CMD_DCMD, - MFI_CMD_ABORT, - MFI_CMD_SMP, - MFI_CMD_STP -} mfi_cmd_t; - -/* Direct commands */ -typedef enum { - MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC = 0x0100e100, - MFI_DCMD_CTRL_GET_INFO = 0x01010000, - MFI_DCMD_CTRL_GET_PROPERTIES = 0x01020100, - MFI_DCMD_CTRL_SET_PROPERTIES = 0x01020200, - MFI_DCMD_CTRL_ALARM = 0x01030000, - MFI_DCMD_CTRL_ALARM_GET = 0x01030100, - MFI_DCMD_CTRL_ALARM_ENABLE = 0x01030200, - MFI_DCMD_CTRL_ALARM_DISABLE = 0x01030300, - MFI_DCMD_CTRL_ALARM_SILENCE = 0x01030400, - MFI_DCMD_CTRL_ALARM_TEST = 0x01030500, - MFI_DCMD_CTRL_EVENT_GETINFO = 0x01040100, - MFI_DCMD_CTRL_EVENT_CLEAR = 0x01040200, - MFI_DCMD_CTRL_EVENT_GET = 0x01040300, - MFI_DCMD_CTRL_EVENT_COUNT = 0x01040400, - MFI_DCMD_CTRL_EVENT_WAIT = 0x01040500, - MFI_DCMD_CTRL_SHUTDOWN = 0x01050000, - MFI_DCMD_HIBERNATE_STANDBY = 0x01060000, - MFI_DCMD_CTRL_GET_TIME = 0x01080101, - MFI_DCMD_CTRL_SET_TIME = 0x01080102, - MFI_DCMD_CTRL_BIOS_DATA_GET = 0x010c0100, - MFI_DCMD_CTRL_BIOS_DATA_SET = 0x010c0200, - MFI_DCMD_CTRL_FACTORY_DEFAULTS = 0x010d0000, - MFI_DCMD_CTRL_MFC_DEFAULTS_GET = 0x010e0201, - MFI_DCMD_CTRL_MFC_DEFAULTS_SET = 0x010e0202, - MFI_DCMD_CTRL_CACHE_FLUSH = 0x01101000, - MFI_DCMD_PD_GET_LIST = 0x02010000, - MFI_DCMD_PD_LIST_QUERY = 0x02010100, - MFI_DCMD_PD_GET_INFO = 0x02020000, - MFI_DCMD_PD_STATE_SET = 0x02030100, - MFI_DCMD_PD_REBUILD = 0x02040100, - MFI_DCMD_PD_BLINK = 0x02070100, - MFI_DCMD_PD_UNBLINK = 0x02070200, - MFI_DCMD_LD_GET_LIST = 0x03010000, - MFI_DCMD_LD_LIST_QUERY = 0x03010100, - MFI_DCMD_LD_GET_INFO = 0x03020000, - MFI_DCMD_LD_GET_PROP = 0x03030000, - MFI_DCMD_LD_SET_PROP = 0x03040000, - MFI_DCMD_LD_DELETE = 0x03090000, - MFI_DCMD_CFG_READ = 0x04010000, - MFI_DCMD_CFG_ADD = 0x04020000, - MFI_DCMD_CFG_CLEAR = 0x04030000, - MFI_DCMD_CFG_FOREIGN_READ = 0x04060100, - MFI_DCMD_CFG_FOREIGN_IMPORT = 0x04060400, - MFI_DCMD_BBU_STATUS = 0x05010000, - MFI_DCMD_BBU_CAPACITY_INFO = 0x05020000, - MFI_DCMD_BBU_DESIGN_INFO = 0x05030000, - MFI_DCMD_BBU_PROP_GET = 0x05050100, - MFI_DCMD_CLUSTER = 0x08000000, - MFI_DCMD_CLUSTER_RESET_ALL = 0x08010100, - MFI_DCMD_CLUSTER_RESET_LD = 0x08010200 -} mfi_dcmd_t; - -/* Modifiers for MFI_DCMD_CTRL_FLUSHCACHE */ -#define MFI_FLUSHCACHE_CTRL 0x01 -#define MFI_FLUSHCACHE_DISK 0x02 - -/* Modifiers for MFI_DCMD_CTRL_SHUTDOWN */ -#define MFI_SHUTDOWN_SPINDOWN 0x01 - -/* - * MFI Frame flags - */ -typedef enum { - MFI_FRAME_DONT_POST_IN_REPLY_QUEUE = 0x0001, - MFI_FRAME_SGL64 = 0x0002, - MFI_FRAME_SENSE64 = 0x0004, - MFI_FRAME_DIR_WRITE = 0x0008, - MFI_FRAME_DIR_READ = 0x0010, - MFI_FRAME_IEEE_SGL = 0x0020, -} mfi_frame_flags; - -/* MFI Status codes */ -typedef enum { - MFI_STAT_OK = 0x00, - MFI_STAT_INVALID_CMD, - MFI_STAT_INVALID_DCMD, - MFI_STAT_INVALID_PARAMETER, - MFI_STAT_INVALID_SEQUENCE_NUMBER, - MFI_STAT_ABORT_NOT_POSSIBLE, - MFI_STAT_APP_HOST_CODE_NOT_FOUND, - MFI_STAT_APP_IN_USE, - MFI_STAT_APP_NOT_INITIALIZED, - MFI_STAT_ARRAY_INDEX_INVALID, - MFI_STAT_ARRAY_ROW_NOT_EMPTY, - MFI_STAT_CONFIG_RESOURCE_CONFLICT, - MFI_STAT_DEVICE_NOT_FOUND, - MFI_STAT_DRIVE_TOO_SMALL, - MFI_STAT_FLASH_ALLOC_FAIL, - MFI_STAT_FLASH_BUSY, - MFI_STAT_FLASH_ERROR = 0x10, - MFI_STAT_FLASH_IMAGE_BAD, - MFI_STAT_FLASH_IMAGE_INCOMPLETE, - MFI_STAT_FLASH_NOT_OPEN, - MFI_STAT_FLASH_NOT_STARTED, - MFI_STAT_FLUSH_FAILED, - MFI_STAT_HOST_CODE_NOT_FOUNT, - MFI_STAT_LD_CC_IN_PROGRESS, - MFI_STAT_LD_INIT_IN_PROGRESS, - MFI_STAT_LD_LBA_OUT_OF_RANGE, - MFI_STAT_LD_MAX_CONFIGURED, - MFI_STAT_LD_NOT_OPTIMAL, - MFI_STAT_LD_RBLD_IN_PROGRESS, - MFI_STAT_LD_RECON_IN_PROGRESS, - MFI_STAT_LD_WRONG_RAID_LEVEL, - MFI_STAT_MAX_SPARES_EXCEEDED, - MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20, - MFI_STAT_MFC_HW_ERROR, - MFI_STAT_NO_HW_PRESENT, - MFI_STAT_NOT_FOUND, - MFI_STAT_NOT_IN_ENCL, - MFI_STAT_PD_CLEAR_IN_PROGRESS, - MFI_STAT_PD_TYPE_WRONG, - MFI_STAT_PR_DISABLED, - MFI_STAT_ROW_INDEX_INVALID, - MFI_STAT_SAS_CONFIG_INVALID_ACTION, - MFI_STAT_SAS_CONFIG_INVALID_DATA, - MFI_STAT_SAS_CONFIG_INVALID_PAGE, - MFI_STAT_SAS_CONFIG_INVALID_TYPE, - MFI_STAT_SCSI_DONE_WITH_ERROR, - MFI_STAT_SCSI_IO_FAILED, - MFI_STAT_SCSI_RESERVATION_CONFLICT, - MFI_STAT_SHUTDOWN_FAILED = 0x30, - MFI_STAT_TIME_NOT_SET, - MFI_STAT_WRONG_STATE, - MFI_STAT_LD_OFFLINE, - MFI_STAT_PEER_NOTIFICATION_REJECTED, - MFI_STAT_PEER_NOTIFICATION_FAILED, - MFI_STAT_RESERVATION_IN_PROGRESS, - MFI_STAT_I2C_ERRORS_DETECTED, - MFI_STAT_PCI_ERRORS_DETECTED, - MFI_STAT_DIAG_FAILED, - MFI_STAT_BOOT_MSG_PENDING, - MFI_STAT_FOREIGN_CONFIG_INCOMPLETE, - MFI_STAT_INVALID_SGL, - MFI_STAT_UNSUPPORTED_HW, - MFI_STAT_CC_SCHEDULE_DISABLED, - MFI_STAT_PD_COPYBACK_IN_PROGRESS, - MFI_STAT_MULTIPLE_PDS_IN_ARRAY = 0x40, - MFI_STAT_FW_DOWNLOAD_ERROR, - MFI_STAT_FEATURE_SECURITY_NOT_ENABLED, - MFI_STAT_LOCK_KEY_ALREADY_EXISTS, - MFI_STAT_LOCK_KEY_BACKUP_NOT_ALLOWED, - MFI_STAT_LOCK_KEY_VERIFY_NOT_ALLOWED, - MFI_STAT_LOCK_KEY_VERIFY_FAILED, - MFI_STAT_LOCK_KEY_REKEY_NOT_ALLOWED, - MFI_STAT_LOCK_KEY_INVALID, - MFI_STAT_LOCK_KEY_ESCROW_INVALID, - MFI_STAT_LOCK_KEY_BACKUP_REQUIRED, - MFI_STAT_SECURE_LD_EXISTS, - MFI_STAT_LD_SECURE_NOT_ALLOWED, - MFI_STAT_REPROVISION_NOT_ALLOWED, - MFI_STAT_PD_SECURITY_TYPE_WRONG, - MFI_STAT_LD_ENCRYPTION_TYPE_INVALID, - MFI_STAT_CONFIG_FDE_NON_FDE_MIX_NOT_ALLOWED = 0x50, - MFI_STAT_CONFIG_LD_ENCRYPTION_TYPE_MIX_NOT_ALLOWED, - MFI_STAT_SECRET_KEY_NOT_ALLOWED, - MFI_STAT_PD_HW_ERRORS_DETECTED, - MFI_STAT_LD_CACHE_PINNED, - MFI_STAT_POWER_STATE_SET_IN_PROGRESS, - MFI_STAT_POWER_STATE_SET_BUSY, - MFI_STAT_POWER_STATE_WRONG, - MFI_STAT_PR_NO_AVAILABLE_PD_FOUND, - MFI_STAT_CTRL_RESET_REQUIRED, - MFI_STAT_LOCK_KEY_EKM_NO_BOOT_AGENT, - MFI_STAT_SNAP_NO_SPACE, - MFI_STAT_SNAP_PARTIAL_FAILURE, - MFI_STAT_UPGRADE_KEY_INCOMPATIBLE, - MFI_STAT_PFK_INCOMPATIBLE, - MFI_STAT_PD_MAX_UNCONFIGURED, - MFI_STAT_IO_METRICS_DISABLED = 0x60, - MFI_STAT_AEC_NOT_STOPPED, - MFI_STAT_PI_TYPE_WRONG, - MFI_STAT_LD_PD_PI_INCOMPATIBLE, - MFI_STAT_PI_NOT_ENABLED, - MFI_STAT_LD_BLOCK_SIZE_MISMATCH, - MFI_STAT_INVALID_STATUS = 0xFF -} mfi_status_t; - -/* Event classes */ -typedef enum { - MFI_EVT_CLASS_DEBUG = -2, - MFI_EVT_CLASS_PROGRESS = -1, - MFI_EVT_CLASS_INFO = 0, - MFI_EVT_CLASS_WARNING = 1, - MFI_EVT_CLASS_CRITICAL = 2, - MFI_EVT_CLASS_FATAL = 3, - MFI_EVT_CLASS_DEAD = 4 -} mfi_evt_class_t; - -/* Event locales */ -typedef enum { - MFI_EVT_LOCALE_LD = 0x0001, - MFI_EVT_LOCALE_PD = 0x0002, - MFI_EVT_LOCALE_ENCL = 0x0004, - MFI_EVT_LOCALE_BBU = 0x0008, - MFI_EVT_LOCALE_SAS = 0x0010, - MFI_EVT_LOCALE_CTRL = 0x0020, - MFI_EVT_LOCALE_CONFIG = 0x0040, - MFI_EVT_LOCALE_CLUSTER = 0x0080, - MFI_EVT_LOCALE_ALL = 0xffff -} mfi_evt_locale_t; - -/* Event args */ -typedef enum { - MR_EVT_ARGS_NONE = 0x00, - MR_EVT_ARGS_CDB_SENSE, - MR_EVT_ARGS_LD, - MR_EVT_ARGS_LD_COUNT, - MR_EVT_ARGS_LD_LBA, - MR_EVT_ARGS_LD_OWNER, - MR_EVT_ARGS_LD_LBA_PD_LBA, - MR_EVT_ARGS_LD_PROG, - MR_EVT_ARGS_LD_STATE, - MR_EVT_ARGS_LD_STRIP, - MR_EVT_ARGS_PD, - MR_EVT_ARGS_PD_ERR, - MR_EVT_ARGS_PD_LBA, - MR_EVT_ARGS_PD_LBA_LD, - MR_EVT_ARGS_PD_PROG, - MR_EVT_ARGS_PD_STATE, - MR_EVT_ARGS_PCI, - MR_EVT_ARGS_RATE, - MR_EVT_ARGS_STR, - MR_EVT_ARGS_TIME, - MR_EVT_ARGS_ECC, - MR_EVT_ARGS_LD_PROP, - MR_EVT_ARGS_PD_SPARE, - MR_EVT_ARGS_PD_INDEX, - MR_EVT_ARGS_DIAG_PASS, - MR_EVT_ARGS_DIAG_FAIL, - MR_EVT_ARGS_PD_LBA_LBA, - MR_EVT_ARGS_PORT_PHY, - MR_EVT_ARGS_PD_MISSING, - MR_EVT_ARGS_PD_ADDRESS, - MR_EVT_ARGS_BITMAP, - MR_EVT_ARGS_CONNECTOR, - MR_EVT_ARGS_PD_PD, - MR_EVT_ARGS_PD_FRU, - MR_EVT_ARGS_PD_PATHINFO, - MR_EVT_ARGS_PD_POWER_STATE, - MR_EVT_ARGS_GENERIC, -} mfi_evt_args; - -/* Event codes */ -#define MR_EVT_CFG_CLEARED 0x0004 -#define MR_EVT_CTRL_SHUTDOWN 0x002a -#define MR_EVT_LD_STATE_CHANGE 0x0051 -#define MR_EVT_PD_INSERTED 0x005b -#define MR_EVT_PD_REMOVED 0x0070 -#define MR_EVT_PD_STATE_CHANGED 0x0072 -#define MR_EVT_LD_CREATED 0x008a -#define MR_EVT_LD_DELETED 0x008b -#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db -#define MR_EVT_LD_OFFLINE 0x00fc -#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 - -typedef enum { - MR_LD_CACHE_WRITE_BACK = 0x01, - MR_LD_CACHE_WRITE_ADAPTIVE = 0x02, - MR_LD_CACHE_READ_AHEAD = 0x04, - MR_LD_CACHE_READ_ADAPTIVE = 0x08, - MR_LD_CACHE_WRITE_CACHE_BAD_BBU = 0x10, - MR_LD_CACHE_ALLOW_WRITE_CACHE = 0x20, - MR_LD_CACHE_ALLOW_READ_CACHE = 0x40 -} mfi_ld_cache; - -typedef enum { - MR_PD_CACHE_UNCHANGED = 0, - MR_PD_CACHE_ENABLE = 1, - MR_PD_CACHE_DISABLE = 2 -} mfi_pd_cache; - -typedef enum { - MR_PD_QUERY_TYPE_ALL = 0, - MR_PD_QUERY_TYPE_STATE = 1, - MR_PD_QUERY_TYPE_POWER_STATE = 2, - MR_PD_QUERY_TYPE_MEDIA_TYPE = 3, - MR_PD_QUERY_TYPE_SPEED = 4, - MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, /*query for system drives */ -} mfi_pd_query_type; - -typedef enum { - MR_LD_QUERY_TYPE_ALL = 0, - MR_LD_QUERY_TYPE_EXPOSED_TO_HOST = 1, - MR_LD_QUERY_TYPE_USED_TGT_IDS = 2, - MR_LD_QUERY_TYPE_CLUSTER_ACCESS = 3, - MR_LD_QUERY_TYPE_CLUSTER_LOCALE = 4, -} mfi_ld_query_type; - -/* - * Other propertities and definitions - */ -#define MFI_MAX_PD_CHANNELS 2 -#define MFI_MAX_LD_CHANNELS 2 -#define MFI_MAX_CHANNELS (MFI_MAX_PD_CHANNELS + MFI_MAX_LD_CHANNELS) -#define MFI_MAX_CHANNEL_DEVS 128 -#define MFI_DEFAULT_ID -1 -#define MFI_MAX_LUN 8 -#define MFI_MAX_LD 64 - -#define MFI_FRAME_SIZE 64 -#define MFI_MBOX_SIZE 12 - -/* Firmware flashing can take 40s */ -#define MFI_POLL_TIMEOUT_SECS 50 - -/* Allow for speedier math calculations */ -#define MFI_SECTOR_LEN 512 - -/* Scatter Gather elements */ -struct mfi_sg32 { - uint32_t addr; - uint32_t len; -} QEMU_PACKED; - -struct mfi_sg64 { - uint64_t addr; - uint32_t len; -} QEMU_PACKED; - -struct mfi_sg_skinny { - uint64_t addr; - uint32_t len; - uint32_t flag; -} QEMU_PACKED; - -union mfi_sgl { - struct mfi_sg32 sg32[1]; - struct mfi_sg64 sg64[1]; - struct mfi_sg_skinny sg_skinny[1]; -} QEMU_PACKED; - -/* Message frames. All messages have a common header */ -struct mfi_frame_header { - uint8_t frame_cmd; - uint8_t sense_len; - uint8_t cmd_status; - uint8_t scsi_status; - uint8_t target_id; - uint8_t lun_id; - uint8_t cdb_len; - uint8_t sge_count; - uint64_t context; - uint16_t flags; - uint16_t timeout; - uint32_t data_len; -} QEMU_PACKED; - -struct mfi_init_frame { - struct mfi_frame_header header; - uint32_t qinfo_new_addr_lo; - uint32_t qinfo_new_addr_hi; - uint32_t qinfo_old_addr_lo; - uint32_t qinfo_old_addr_hi; - uint32_t reserved[6]; -}; - -#define MFI_IO_FRAME_SIZE 40 -struct mfi_io_frame { - struct mfi_frame_header header; - uint32_t sense_addr_lo; - uint32_t sense_addr_hi; - uint32_t lba_lo; - uint32_t lba_hi; - union mfi_sgl sgl; -} QEMU_PACKED; - -#define MFI_PASS_FRAME_SIZE 48 -struct mfi_pass_frame { - struct mfi_frame_header header; - uint32_t sense_addr_lo; - uint32_t sense_addr_hi; - uint8_t cdb[16]; - union mfi_sgl sgl; -} QEMU_PACKED; - -#define MFI_DCMD_FRAME_SIZE 40 -struct mfi_dcmd_frame { - struct mfi_frame_header header; - uint32_t opcode; - uint8_t mbox[MFI_MBOX_SIZE]; - union mfi_sgl sgl; -} QEMU_PACKED; - -struct mfi_abort_frame { - struct mfi_frame_header header; - uint64_t abort_context; - uint32_t abort_mfi_addr_lo; - uint32_t abort_mfi_addr_hi; - uint32_t reserved1[6]; -} QEMU_PACKED; - -struct mfi_smp_frame { - struct mfi_frame_header header; - uint64_t sas_addr; - union { - struct mfi_sg32 sg32[2]; - struct mfi_sg64 sg64[2]; - } sgl; -} QEMU_PACKED; - -struct mfi_stp_frame { - struct mfi_frame_header header; - uint16_t fis[10]; - uint32_t stp_flags; - union { - struct mfi_sg32 sg32[2]; - struct mfi_sg64 sg64[2]; - } sgl; -} QEMU_PACKED; - -union mfi_frame { - struct mfi_frame_header header; - struct mfi_init_frame init; - struct mfi_io_frame io; - struct mfi_pass_frame pass; - struct mfi_dcmd_frame dcmd; - struct mfi_abort_frame abort; - struct mfi_smp_frame smp; - struct mfi_stp_frame stp; - uint64_t raw[8]; - uint8_t bytes[MFI_FRAME_SIZE]; -}; - -#define MFI_SENSE_LEN 128 -struct mfi_sense { - uint8_t data[MFI_SENSE_LEN]; -}; - -#define MFI_QUEUE_FLAG_CONTEXT64 0x00000002 - -/* The queue init structure that is passed with the init message */ -struct mfi_init_qinfo { - uint32_t flags; - uint32_t rq_entries; - uint32_t rq_addr_lo; - uint32_t rq_addr_hi; - uint32_t pi_addr_lo; - uint32_t pi_addr_hi; - uint32_t ci_addr_lo; - uint32_t ci_addr_hi; -} QEMU_PACKED; - -/* Controller properties */ -struct mfi_ctrl_props { - uint16_t seq_num; - uint16_t pred_fail_poll_interval; - uint16_t intr_throttle_cnt; - uint16_t intr_throttle_timeout; - uint8_t rebuild_rate; - uint8_t patrol_read_rate; - uint8_t bgi_rate; - uint8_t cc_rate; - uint8_t recon_rate; - uint8_t cache_flush_interval; - uint8_t spinup_drv_cnt; - uint8_t spinup_delay; - uint8_t cluster_enable; - uint8_t coercion_mode; - uint8_t alarm_enable; - uint8_t disable_auto_rebuild; - uint8_t disable_battery_warn; - uint8_t ecc_bucket_size; - uint16_t ecc_bucket_leak_rate; - uint8_t restore_hotspare_on_insertion; - uint8_t expose_encl_devices; - uint8_t maintainPdFailHistory; - uint8_t disallowHostRequestReordering; - uint8_t abortCCOnError; - uint8_t loadBalanceMode; - uint8_t disableAutoDetectBackplane; - uint8_t snapVDSpace; - uint32_t OnOffProperties; -/* set TRUE to disable copyBack (0=copyback enabled) */ -#define MFI_CTRL_PROP_CopyBackDisabled (1 << 0) -#define MFI_CTRL_PROP_SMARTerEnabled (1 << 1) -#define MFI_CTRL_PROP_PRCorrectUnconfiguredAreas (1 << 2) -#define MFI_CTRL_PROP_UseFdeOnly (1 << 3) -#define MFI_CTRL_PROP_DisableNCQ (1 << 4) -#define MFI_CTRL_PROP_SSDSMARTerEnabled (1 << 5) -#define MFI_CTRL_PROP_SSDPatrolReadEnabled (1 << 6) -#define MFI_CTRL_PROP_EnableSpinDownUnconfigured (1 << 7) -#define MFI_CTRL_PROP_AutoEnhancedImport (1 << 8) -#define MFI_CTRL_PROP_EnableSecretKeyControl (1 << 9) -#define MFI_CTRL_PROP_DisableOnlineCtrlReset (1 << 10) -#define MFI_CTRL_PROP_AllowBootWithPinnedCache (1 << 11) -#define MFI_CTRL_PROP_DisableSpinDownHS (1 << 12) -#define MFI_CTRL_PROP_EnableJBOD (1 << 13) - - uint8_t autoSnapVDSpace; /* % of source LD to be - * reserved for auto snapshot - * in snapshot repository, for - * metadata and user data - * 1=5%, 2=10%, 3=15% and so on - */ - uint8_t viewSpace; /* snapshot writeable VIEWs - * capacity as a % of source LD - * capacity. 0=READ only - * 1=5%, 2=10%, 3=15% and so on - */ - uint16_t spinDownTime; /* # of idle minutes before device - * is spun down (0=use FW defaults) - */ - uint8_t reserved[24]; -} QEMU_PACKED; - -/* PCI information about the card. */ -struct mfi_info_pci { - uint16_t vendor; - uint16_t device; - uint16_t subvendor; - uint16_t subdevice; - uint8_t reserved[24]; -} QEMU_PACKED; - -/* Host (front end) interface information */ -struct mfi_info_host { - uint8_t type; -#define MFI_INFO_HOST_PCIX 0x01 -#define MFI_INFO_HOST_PCIE 0x02 -#define MFI_INFO_HOST_ISCSI 0x04 -#define MFI_INFO_HOST_SAS3G 0x08 - uint8_t reserved[6]; - uint8_t port_count; - uint64_t port_addr[8]; -} QEMU_PACKED; - -/* Device (back end) interface information */ -struct mfi_info_device { - uint8_t type; -#define MFI_INFO_DEV_SPI 0x01 -#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]; -} QEMU_PACKED; - -/* Firmware component information */ -struct mfi_info_component { - char name[8]; - char version[32]; - char build_date[16]; - char build_time[16]; -} QEMU_PACKED; - -/* Controller default settings */ -struct mfi_defaults { - uint64_t sas_addr; - uint8_t phy_polarity; - uint8_t background_rate; - uint8_t stripe_size; - uint8_t flush_time; - uint8_t write_back; - uint8_t read_ahead; - uint8_t cache_when_bbu_bad; - uint8_t cached_io; - uint8_t smart_mode; - uint8_t alarm_disable; - uint8_t coercion; - uint8_t zrc_config; - uint8_t dirty_led_shows_drive_activity; - uint8_t bios_continue_on_error; - uint8_t spindown_mode; - uint8_t allowed_device_types; - uint8_t allow_mix_in_enclosure; - uint8_t allow_mix_in_ld; - uint8_t allow_sata_in_cluster; - uint8_t max_chained_enclosures; - uint8_t disable_ctrl_r; - uint8_t enable_web_bios; - uint8_t phy_polarity_split; - uint8_t direct_pd_mapping; - uint8_t bios_enumerate_lds; - uint8_t restored_hot_spare_on_insertion; - uint8_t expose_enclosure_devices; - uint8_t maintain_pd_fail_history; - 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]; -} QEMU_PACKED; - -/* Controller default settings */ -struct mfi_bios_data { - uint16_t boot_target_id; - uint8_t do_not_int_13; - uint8_t continue_on_error; - uint8_t verbose; - uint8_t geometry; - uint8_t expose_all_drives; - uint8_t reserved[56]; - uint8_t check_sum; -} QEMU_PACKED; - -/* SAS (?) controller info, returned from MFI_DCMD_CTRL_GETINFO. */ -struct mfi_ctrl_info { - struct mfi_info_pci pci; - struct mfi_info_host host; - struct mfi_info_device device; - - /* Firmware components that are present and active. */ - uint32_t image_check_word; - uint32_t image_component_count; - struct mfi_info_component image_component[8]; - - /* Firmware components that have been flashed but are inactive */ - uint32_t pending_image_component_count; - struct mfi_info_component pending_image_component[8]; - - uint8_t max_arms; - uint8_t max_spans; - uint8_t max_arrays; - uint8_t max_lds; - char product_name[80]; - char serial_number[32]; - uint32_t hw_present; -#define MFI_INFO_HW_BBU 0x01 -#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; - uint32_t max_request_size; - uint16_t lds_present; - uint16_t lds_degraded; - uint16_t lds_offline; - uint16_t pd_present; - uint16_t pd_disks_present; - uint16_t pd_disks_pred_failure; - uint16_t pd_disks_failed; - uint16_t nvram_size; - uint16_t memory_size; - uint16_t flash_size; - uint16_t ram_correctable_errors; - uint16_t ram_uncorrectable_errors; - uint8_t cluster_allowed; - uint8_t cluster_active; - uint16_t max_strips_per_io; - - uint32_t raid_levels; -#define MFI_INFO_RAID_0 0x01 -#define MFI_INFO_RAID_1 0x02 -#define MFI_INFO_RAID_5 0x04 -#define MFI_INFO_RAID_1E 0x08 -#define MFI_INFO_RAID_6 0x10 - - uint32_t adapter_ops; -#define MFI_INFO_AOPS_RBLD_RATE 0x0001 -#define MFI_INFO_AOPS_CC_RATE 0x0002 -#define MFI_INFO_AOPS_BGI_RATE 0x0004 -#define MFI_INFO_AOPS_RECON_RATE 0x0008 -#define MFI_INFO_AOPS_PATROL_RATE 0x0010 -#define MFI_INFO_AOPS_ALARM_CONTROL 0x0020 -#define MFI_INFO_AOPS_CLUSTER_SUPPORTED 0x0040 -#define MFI_INFO_AOPS_BBU 0x0080 -#define MFI_INFO_AOPS_SPANNING_ALLOWED 0x0100 -#define MFI_INFO_AOPS_DEDICATED_SPARES 0x0200 -#define MFI_INFO_AOPS_REVERTIBLE_SPARES 0x0400 -#define MFI_INFO_AOPS_FOREIGN_IMPORT 0x0800 -#define MFI_INFO_AOPS_SELF_DIAGNOSTIC 0x1000 -#define MFI_INFO_AOPS_MIXED_ARRAY 0x2000 -#define MFI_INFO_AOPS_GLOBAL_SPARES 0x4000 - - uint32_t ld_ops; -#define MFI_INFO_LDOPS_READ_POLICY 0x01 -#define MFI_INFO_LDOPS_WRITE_POLICY 0x02 -#define MFI_INFO_LDOPS_IO_POLICY 0x04 -#define MFI_INFO_LDOPS_ACCESS_POLICY 0x08 -#define MFI_INFO_LDOPS_DISK_CACHE_POLICY 0x10 - - struct { - uint8_t min; - uint8_t max; - uint8_t reserved[2]; - } QEMU_PACKED stripe_sz_ops; - - uint32_t pd_ops; -#define MFI_INFO_PDOPS_FORCE_ONLINE 0x01 -#define MFI_INFO_PDOPS_FORCE_OFFLINE 0x02 -#define MFI_INFO_PDOPS_FORCE_REBUILD 0x04 - - uint32_t pd_mix_support; -#define MFI_INFO_PDMIX_SAS 0x01 -#define MFI_INFO_PDMIX_SATA 0x02 -#define MFI_INFO_PDMIX_ENCL 0x04 -#define MFI_INFO_PDMIX_LD 0x08 -#define MFI_INFO_PDMIX_SATA_CLUSTER 0x10 - - uint8_t ecc_bucket_count; - uint8_t reserved2[11]; - struct mfi_ctrl_props properties; - char package_version[0x60]; - uint8_t pad[0x800 - 0x6a0]; -} QEMU_PACKED; - -/* keep track of an event. */ -union mfi_evt { - struct { - uint16_t locale; - uint8_t reserved; - int8_t class; - } members; - uint32_t word; -} QEMU_PACKED; - -/* event log state. */ -struct mfi_evt_log_state { - uint32_t newest_seq_num; - uint32_t oldest_seq_num; - uint32_t clear_seq_num; - uint32_t shutdown_seq_num; - uint32_t boot_seq_num; -} QEMU_PACKED; - -struct mfi_progress { - uint16_t progress; - uint16_t elapsed_seconds; -} QEMU_PACKED; - -struct mfi_evt_ld { - uint16_t target_id; - uint8_t ld_index; - uint8_t reserved; -} QEMU_PACKED; - -struct mfi_evt_pd { - uint16_t device_id; - uint8_t enclosure_index; - uint8_t slot_number; -} QEMU_PACKED; - -/* event detail, returned from MFI_DCMD_CTRL_EVENT_WAIT. */ -struct mfi_evt_detail { - uint32_t seq; - uint32_t time; - uint32_t code; - union mfi_evt class; - uint8_t arg_type; - uint8_t reserved1[15]; - - union { - struct { - struct mfi_evt_pd pd; - uint8_t cdb_len; - uint8_t sense_len; - uint8_t reserved[2]; - uint8_t cdb[16]; - uint8_t sense[64]; - } cdb_sense; - - struct mfi_evt_ld ld; - - struct { - struct mfi_evt_ld ld; - uint64_t count; - } ld_count; - - struct { - uint64_t lba; - struct mfi_evt_ld ld; - } ld_lba; - - struct { - struct mfi_evt_ld ld; - uint32_t pre_owner; - uint32_t new_owner; - } ld_owner; - - struct { - uint64_t ld_lba; - uint64_t pd_lba; - struct mfi_evt_ld ld; - struct mfi_evt_pd pd; - } ld_lba_pd_lba; - - struct { - struct mfi_evt_ld ld; - struct mfi_progress prog; - } ld_prog; - - struct { - struct mfi_evt_ld ld; - uint32_t prev_state; - uint32_t new_state; - } ld_state; - - struct { - uint64_t strip; - struct mfi_evt_ld ld; - } ld_strip; - - struct mfi_evt_pd pd; - - struct { - struct mfi_evt_pd pd; - uint32_t err; - } pd_err; - - struct { - uint64_t lba; - struct mfi_evt_pd pd; - } pd_lba; - - struct { - uint64_t lba; - struct mfi_evt_pd pd; - struct mfi_evt_ld ld; - } pd_lba_ld; - - struct { - struct mfi_evt_pd pd; - struct mfi_progress prog; - } pd_prog; - - struct { - struct mfi_evt_pd ld; - uint32_t prev_state; - uint32_t new_state; - } pd_state; - - struct { - uint16_t venderId; - uint16_t deviceId; - uint16_t subVenderId; - uint16_t subDeviceId; - } pci; - - uint32_t rate; - - char str[96]; - - struct { - uint32_t rtc; - uint16_t elapsedSeconds; - } time; - - struct { - uint32_t ecar; - uint32_t elog; - char str[64]; - } ecc; - - uint8_t b[96]; - uint16_t s[48]; - uint32_t w[24]; - uint64_t d[12]; - } args; - - char description[128]; -} QEMU_PACKED; - -struct mfi_evt_list { - uint32_t count; - uint32_t reserved; - struct mfi_evt_detail event[1]; -} QEMU_PACKED; - -union mfi_pd_ref { - struct { - uint16_t device_id; - uint16_t seq_num; - } v; - uint32_t ref; -} QEMU_PACKED; - -union mfi_pd_ddf_type { - struct { - uint16_t pd_type; -#define MFI_PD_DDF_TYPE_FORCED_PD_GUID (1 << 0) -#define MFI_PD_DDF_TYPE_IN_VD (1 << 1) -#define MFI_PD_DDF_TYPE_IS_GLOBAL_SPARE (1 << 2) -#define MFI_PD_DDF_TYPE_IS_SPARE (1 << 3) -#define MFI_PD_DDF_TYPE_IS_FOREIGN (1 << 4) -#define MFI_PD_DDF_TYPE_INTF_SPI (1 << 12) -#define MFI_PD_DDF_TYPE_INTF_SAS (1 << 13) -#define MFI_PD_DDF_TYPE_INTF_SATA1 (1 << 14) -#define MFI_PD_DDF_TYPE_INTF_SATA3G (1 << 15) - uint16_t reserved; - } ddf; - struct { - uint32_t reserved; - } non_disk; - uint32_t type; -} QEMU_PACKED; - -struct mfi_pd_progress { - uint32_t active; -#define PD_PROGRESS_ACTIVE_REBUILD (1 << 0) -#define PD_PROGRESS_ACTIVE_PATROL (1 << 1) -#define PD_PROGRESS_ACTIVE_CLEAR (1 << 2) - struct mfi_progress rbld; - struct mfi_progress patrol; - struct mfi_progress clear; - struct mfi_progress reserved[4]; -} QEMU_PACKED; - -struct mfi_pd_info { - union mfi_pd_ref ref; - uint8_t inquiry_data[96]; - uint8_t vpd_page83[64]; - uint8_t not_supported; - uint8_t scsi_dev_type; - uint8_t connected_port_bitmap; - uint8_t device_speed; - uint32_t media_err_count; - uint32_t other_err_count; - uint32_t pred_fail_count; - uint32_t last_pred_fail_event_seq_num; - uint16_t fw_state; - uint8_t disable_for_removal; - uint8_t link_speed; - union mfi_pd_ddf_type state; - struct { - uint8_t count; - uint8_t is_path_broken; - uint8_t reserved[6]; - uint64_t sas_addr[4]; - } path_info; - uint64_t raw_size; - uint64_t non_coerced_size; - uint64_t coerced_size; - uint16_t encl_device_id; - uint8_t encl_index; - uint8_t slot_number; - struct mfi_pd_progress prog_info; - uint8_t bad_block_table_full; - uint8_t unusable_in_current_config; - uint8_t vpd_page83_ext[64]; - uint8_t reserved[512-358]; -} QEMU_PACKED; - -struct mfi_pd_address { - uint16_t device_id; - uint16_t encl_device_id; - uint8_t encl_index; - uint8_t slot_number; - uint8_t scsi_dev_type; - uint8_t connect_port_bitmap; - uint64_t sas_addr[2]; -} QEMU_PACKED; - -#define MFI_MAX_SYS_PDS 240 -struct mfi_pd_list { - uint32_t size; - uint32_t count; - struct mfi_pd_address addr[MFI_MAX_SYS_PDS]; -} QEMU_PACKED; - -union mfi_ld_ref { - struct { - uint8_t target_id; - uint8_t reserved; - uint16_t seq; - } v; - uint32_t ref; -} QEMU_PACKED; - -struct mfi_ld_list { - uint32_t ld_count; - uint32_t reserved1; - struct { - union mfi_ld_ref ld; - uint8_t state; - uint8_t reserved2[3]; - uint64_t size; - } ld_list[MFI_MAX_LD]; -} QEMU_PACKED; - -struct mfi_ld_targetid_list { - uint32_t size; - uint32_t ld_count; - uint8_t pad[3]; - uint8_t targetid[MFI_MAX_LD]; -} QEMU_PACKED; - -enum mfi_ld_access { - MFI_LD_ACCESS_RW = 0, - MFI_LD_ACCSSS_RO = 2, - MFI_LD_ACCESS_BLOCKED = 3, -}; -#define MFI_LD_ACCESS_MASK 3 - -enum mfi_ld_state { - MFI_LD_STATE_OFFLINE = 0, - MFI_LD_STATE_PARTIALLY_DEGRADED = 1, - MFI_LD_STATE_DEGRADED = 2, - MFI_LD_STATE_OPTIMAL = 3 -}; - -enum mfi_syspd_state { - MFI_PD_STATE_UNCONFIGURED_GOOD = 0x00, - MFI_PD_STATE_UNCONFIGURED_BAD = 0x01, - MFI_PD_STATE_HOT_SPARE = 0x02, - MFI_PD_STATE_OFFLINE = 0x10, - MFI_PD_STATE_FAILED = 0x11, - MFI_PD_STATE_REBUILD = 0x14, - MFI_PD_STATE_ONLINE = 0x18, - MFI_PD_STATE_COPYBACK = 0x20, - MFI_PD_STATE_SYSTEM = 0x40 -}; - -struct mfi_ld_props { - union mfi_ld_ref ld; - char name[16]; - uint8_t default_cache_policy; - uint8_t access_policy; - uint8_t disk_cache_policy; - uint8_t current_cache_policy; - uint8_t no_bgi; - uint8_t reserved[7]; -} QEMU_PACKED; - -struct mfi_ld_params { - uint8_t primary_raid_level; - uint8_t raid_level_qualifier; - uint8_t secondary_raid_level; - uint8_t stripe_size; - uint8_t num_drives; - uint8_t span_depth; - uint8_t state; - uint8_t init_state; - uint8_t is_consistent; - uint8_t reserved[23]; -} QEMU_PACKED; - -struct mfi_ld_progress { - uint32_t active; -#define MFI_LD_PROGRESS_CC (1<<0) -#define MFI_LD_PROGRESS_BGI (1<<1) -#define MFI_LD_PROGRESS_FGI (1<<2) -#define MFI_LD_PORGRESS_RECON (1<<3) - struct mfi_progress cc; - struct mfi_progress bgi; - struct mfi_progress fgi; - struct mfi_progress recon; - struct mfi_progress reserved[4]; -} QEMU_PACKED; - -struct mfi_span { - uint64_t start_block; - uint64_t num_blocks; - uint16_t array_ref; - uint8_t reserved[6]; -} QEMU_PACKED; - -#define MFI_MAX_SPAN_DEPTH 8 -struct mfi_ld_config { - struct mfi_ld_props properties; - struct mfi_ld_params params; - struct mfi_span span[MFI_MAX_SPAN_DEPTH]; -} QEMU_PACKED; - -struct mfi_ld_info { - struct mfi_ld_config ld_config; - uint64_t size; - struct mfi_ld_progress progress; - uint16_t cluster_owner; - uint8_t reconstruct_active; - uint8_t reserved1[1]; - uint8_t vpd_page83[64]; - uint8_t reserved2[16]; -} QEMU_PACKED; - -union mfi_spare_type { - uint8_t flags; -#define MFI_SPARE_IS_DEDICATED (1 << 0) -#define MFI_SPARE_IS_REVERTABLE (1 << 1) -#define MFI_SPARE_IS_ENCL_AFFINITY (1 << 2) - uint8_t type; -} QEMU_PACKED; - -#define MFI_MAX_ARRAYS 16 -struct mfi_spare { - union mfi_pd_ref ref; - union mfi_spare_type spare_type; - uint8_t reserved[2]; - uint8_t array_count; - uint16_t array_refd[MFI_MAX_ARRAYS]; -} QEMU_PACKED; - -#define MFI_MAX_ROW_SIZE 32 -struct mfi_array { - uint64_t size; - uint8_t num_drives; - uint8_t reserved; - uint16_t array_ref; - uint8_t pad[20]; - struct { - union mfi_pd_ref ref; - uint16_t fw_state; /* enum mfi_syspd_state */ - struct { - uint8_t pd; - uint8_t slot; - } encl; - } pd[MFI_MAX_ROW_SIZE]; -} QEMU_PACKED; - -struct mfi_config_data { - uint32_t size; - uint16_t array_count; - uint16_t array_size; - uint16_t log_drv_count; - uint16_t log_drv_size; - uint16_t spares_count; - uint16_t spares_size; - uint8_t reserved[16]; - /* - struct mfi_array array[]; - struct mfi_ld_config ld[]; - struct mfi_spare spare[]; - */ -} QEMU_PACKED; - -#define MFI_SCSI_MAX_TARGETS 128 -#define MFI_SCSI_MAX_LUNS 8 -#define MFI_SCSI_INITIATOR_ID 255 -#define MFI_SCSI_MAX_CMDS 8 -#define MFI_SCSI_MAX_CDB_LEN 16 - -#endif /* MFI_REG_H */ diff --git a/qemu/hw/scsi/mpi.h b/qemu/hw/scsi/mpi.h deleted file mode 100644 index 0568e1950..000000000 --- a/qemu/hw/scsi/mpi.h +++ /dev/null @@ -1,1153 +0,0 @@ -/*- - * Based on FreeBSD sys/dev/mpt/mpilib headers. - * - * Copyright (c) 2000-2010, LSI Logic Corporation and its contributors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon including - * a substantially similar Disclaimer requirement for further binary - * redistribution. - * 3. Neither the name of the LSI Logic Corporation nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF THE COPYRIGHT - * OWNER OR CONTRIBUTOR IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef MPI_H -#define MPI_H - -enum { - MPI_FUNCTION_SCSI_IO_REQUEST = 0x00, - MPI_FUNCTION_SCSI_TASK_MGMT = 0x01, - MPI_FUNCTION_IOC_INIT = 0x02, - MPI_FUNCTION_IOC_FACTS = 0x03, - MPI_FUNCTION_CONFIG = 0x04, - MPI_FUNCTION_PORT_FACTS = 0x05, - MPI_FUNCTION_PORT_ENABLE = 0x06, - MPI_FUNCTION_EVENT_NOTIFICATION = 0x07, - MPI_FUNCTION_EVENT_ACK = 0x08, - MPI_FUNCTION_FW_DOWNLOAD = 0x09, - MPI_FUNCTION_TARGET_CMD_BUFFER_POST = 0x0A, - MPI_FUNCTION_TARGET_ASSIST = 0x0B, - MPI_FUNCTION_TARGET_STATUS_SEND = 0x0C, - MPI_FUNCTION_TARGET_MODE_ABORT = 0x0D, - MPI_FUNCTION_FC_LINK_SRVC_BUF_POST = 0x0E, - MPI_FUNCTION_FC_LINK_SRVC_RSP = 0x0F, - MPI_FUNCTION_FC_EX_LINK_SRVC_SEND = 0x10, - MPI_FUNCTION_FC_ABORT = 0x11, - MPI_FUNCTION_FW_UPLOAD = 0x12, - MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND = 0x13, - MPI_FUNCTION_FC_PRIMITIVE_SEND = 0x14, - - MPI_FUNCTION_RAID_ACTION = 0x15, - MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH = 0x16, - - MPI_FUNCTION_TOOLBOX = 0x17, - - MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR = 0x18, - - MPI_FUNCTION_MAILBOX = 0x19, - - MPI_FUNCTION_SMP_PASSTHROUGH = 0x1A, - MPI_FUNCTION_SAS_IO_UNIT_CONTROL = 0x1B, - MPI_FUNCTION_SATA_PASSTHROUGH = 0x1C, - - MPI_FUNCTION_DIAG_BUFFER_POST = 0x1D, - MPI_FUNCTION_DIAG_RELEASE = 0x1E, - - MPI_FUNCTION_SCSI_IO_32 = 0x1F, - - MPI_FUNCTION_LAN_SEND = 0x20, - MPI_FUNCTION_LAN_RECEIVE = 0x21, - MPI_FUNCTION_LAN_RESET = 0x22, - - MPI_FUNCTION_TARGET_ASSIST_EXTENDED = 0x23, - MPI_FUNCTION_TARGET_CMD_BUF_BASE_POST = 0x24, - MPI_FUNCTION_TARGET_CMD_BUF_LIST_POST = 0x25, - - MPI_FUNCTION_INBAND_BUFFER_POST = 0x28, - MPI_FUNCTION_INBAND_SEND = 0x29, - MPI_FUNCTION_INBAND_RSP = 0x2A, - MPI_FUNCTION_INBAND_ABORT = 0x2B, - - MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET = 0x40, - MPI_FUNCTION_IO_UNIT_RESET = 0x41, - MPI_FUNCTION_HANDSHAKE = 0x42, - MPI_FUNCTION_REPLY_FRAME_REMOVAL = 0x43, - MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL = 0x44, -}; - -/****************************************************************************/ -/* Registers */ -/****************************************************************************/ - -enum { - MPI_IOC_STATE_RESET = 0x00000000, - MPI_IOC_STATE_READY = 0x10000000, - MPI_IOC_STATE_OPERATIONAL = 0x20000000, - MPI_IOC_STATE_FAULT = 0x40000000, - - MPI_DOORBELL_OFFSET = 0x00000000, - MPI_DOORBELL_ACTIVE = 0x08000000, /* DoorbellUsed */ - MPI_DOORBELL_WHO_INIT_MASK = 0x07000000, - MPI_DOORBELL_WHO_INIT_SHIFT = 24, - MPI_DOORBELL_FUNCTION_MASK = 0xFF000000, - MPI_DOORBELL_FUNCTION_SHIFT = 24, - MPI_DOORBELL_ADD_DWORDS_MASK = 0x00FF0000, - MPI_DOORBELL_ADD_DWORDS_SHIFT = 16, - MPI_DOORBELL_DATA_MASK = 0x0000FFFF, - MPI_DOORBELL_FUNCTION_SPECIFIC_MASK = 0x0000FFFF, - - MPI_DB_HPBAC_VALUE_MASK = 0x0000F000, - MPI_DB_HPBAC_ENABLE_ACCESS = 0x01, - MPI_DB_HPBAC_DISABLE_ACCESS = 0x02, - MPI_DB_HPBAC_FREE_BUFFER = 0x03, - - MPI_WRITE_SEQUENCE_OFFSET = 0x00000004, - MPI_WRSEQ_KEY_VALUE_MASK = 0x0000000F, - MPI_WRSEQ_1ST_KEY_VALUE = 0x04, - MPI_WRSEQ_2ND_KEY_VALUE = 0x0B, - MPI_WRSEQ_3RD_KEY_VALUE = 0x02, - MPI_WRSEQ_4TH_KEY_VALUE = 0x07, - MPI_WRSEQ_5TH_KEY_VALUE = 0x0D, - - MPI_DIAGNOSTIC_OFFSET = 0x00000008, - MPI_DIAG_CLEAR_FLASH_BAD_SIG = 0x00000400, - MPI_DIAG_PREVENT_IOC_BOOT = 0x00000200, - MPI_DIAG_DRWE = 0x00000080, - MPI_DIAG_FLASH_BAD_SIG = 0x00000040, - MPI_DIAG_RESET_HISTORY = 0x00000020, - MPI_DIAG_RW_ENABLE = 0x00000010, - MPI_DIAG_RESET_ADAPTER = 0x00000004, - MPI_DIAG_DISABLE_ARM = 0x00000002, - MPI_DIAG_MEM_ENABLE = 0x00000001, - - MPI_TEST_BASE_ADDRESS_OFFSET = 0x0000000C, - - MPI_DIAG_RW_DATA_OFFSET = 0x00000010, - - MPI_DIAG_RW_ADDRESS_OFFSET = 0x00000014, - - MPI_HOST_INTERRUPT_STATUS_OFFSET = 0x00000030, - MPI_HIS_IOP_DOORBELL_STATUS = 0x80000000, - MPI_HIS_REPLY_MESSAGE_INTERRUPT = 0x00000008, - MPI_HIS_DOORBELL_INTERRUPT = 0x00000001, - - MPI_HOST_INTERRUPT_MASK_OFFSET = 0x00000034, - MPI_HIM_RIM = 0x00000008, - MPI_HIM_DIM = 0x00000001, - - MPI_REQUEST_QUEUE_OFFSET = 0x00000040, - MPI_REQUEST_POST_FIFO_OFFSET = 0x00000040, - - MPI_REPLY_QUEUE_OFFSET = 0x00000044, - MPI_REPLY_POST_FIFO_OFFSET = 0x00000044, - MPI_REPLY_FREE_FIFO_OFFSET = 0x00000044, - - MPI_HI_PRI_REQUEST_QUEUE_OFFSET = 0x00000048, -}; - -#define MPI_ADDRESS_REPLY_A_BIT 0x80000000 - -/****************************************************************************/ -/* Scatter/gather elements */ -/****************************************************************************/ - -typedef struct MPISGEntry { - uint32_t FlagsLength; - union - { - uint32_t Address32; - uint64_t Address64; - } u; -} QEMU_PACKED MPISGEntry; - -/* Flags field bit definitions */ - -enum { - MPI_SGE_FLAGS_LAST_ELEMENT = 0x80000000, - MPI_SGE_FLAGS_END_OF_BUFFER = 0x40000000, - MPI_SGE_FLAGS_ELEMENT_TYPE_MASK = 0x30000000, - MPI_SGE_FLAGS_LOCAL_ADDRESS = 0x08000000, - MPI_SGE_FLAGS_DIRECTION = 0x04000000, - MPI_SGE_FLAGS_64_BIT_ADDRESSING = 0x02000000, - MPI_SGE_FLAGS_END_OF_LIST = 0x01000000, - - MPI_SGE_LENGTH_MASK = 0x00FFFFFF, - MPI_SGE_CHAIN_LENGTH_MASK = 0x0000FFFF, - - MPI_SGE_FLAGS_TRANSACTION_ELEMENT = 0x00000000, - MPI_SGE_FLAGS_SIMPLE_ELEMENT = 0x10000000, - MPI_SGE_FLAGS_CHAIN_ELEMENT = 0x30000000, - - /* Direction */ - - MPI_SGE_FLAGS_IOC_TO_HOST = 0x00000000, - MPI_SGE_FLAGS_HOST_TO_IOC = 0x04000000, - - MPI_SGE_CHAIN_OFFSET_MASK = 0x00FF0000, -}; - -#define MPI_SGE_CHAIN_OFFSET_SHIFT 16 - -/****************************************************************************/ -/* Standard message request header for all request messages */ -/****************************************************************************/ - -typedef struct MPIRequestHeader { - uint8_t Reserved[2]; /* function specific */ - uint8_t ChainOffset; - uint8_t Function; - uint8_t Reserved1[3]; /* function specific */ - uint8_t MsgFlags; - uint32_t MsgContext; -} QEMU_PACKED MPIRequestHeader; - - -typedef struct MPIDefaultReply { - uint8_t Reserved[2]; /* function specific */ - uint8_t MsgLength; - uint8_t Function; - uint8_t Reserved1[3]; /* function specific */ - uint8_t MsgFlags; - uint32_t MsgContext; - uint8_t Reserved2[2]; /* function specific */ - uint16_t IOCStatus; - uint32_t IOCLogInfo; -} QEMU_PACKED MPIDefaultReply; - -/* MsgFlags definition for all replies */ - -#define MPI_MSGFLAGS_CONTINUATION_REPLY (0x80) - -enum { - - /************************************************************************/ - /* Common IOCStatus values for all replies */ - /************************************************************************/ - - MPI_IOCSTATUS_SUCCESS = 0x0000, - MPI_IOCSTATUS_INVALID_FUNCTION = 0x0001, - MPI_IOCSTATUS_BUSY = 0x0002, - MPI_IOCSTATUS_INVALID_SGL = 0x0003, - MPI_IOCSTATUS_INTERNAL_ERROR = 0x0004, - MPI_IOCSTATUS_RESERVED = 0x0005, - MPI_IOCSTATUS_INSUFFICIENT_RESOURCES = 0x0006, - MPI_IOCSTATUS_INVALID_FIELD = 0x0007, - MPI_IOCSTATUS_INVALID_STATE = 0x0008, - MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED = 0x0009, - - /************************************************************************/ - /* Config IOCStatus values */ - /************************************************************************/ - - MPI_IOCSTATUS_CONFIG_INVALID_ACTION = 0x0020, - MPI_IOCSTATUS_CONFIG_INVALID_TYPE = 0x0021, - MPI_IOCSTATUS_CONFIG_INVALID_PAGE = 0x0022, - MPI_IOCSTATUS_CONFIG_INVALID_DATA = 0x0023, - MPI_IOCSTATUS_CONFIG_NO_DEFAULTS = 0x0024, - MPI_IOCSTATUS_CONFIG_CANT_COMMIT = 0x0025, - - /************************************************************************/ - /* SCSIIO Reply = SPI & FCP, initiator values */ - /************************************************************************/ - - MPI_IOCSTATUS_SCSI_RECOVERED_ERROR = 0x0040, - MPI_IOCSTATUS_SCSI_INVALID_BUS = 0x0041, - MPI_IOCSTATUS_SCSI_INVALID_TARGETID = 0x0042, - MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE = 0x0043, - MPI_IOCSTATUS_SCSI_DATA_OVERRUN = 0x0044, - MPI_IOCSTATUS_SCSI_DATA_UNDERRUN = 0x0045, - MPI_IOCSTATUS_SCSI_IO_DATA_ERROR = 0x0046, - MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR = 0x0047, - MPI_IOCSTATUS_SCSI_TASK_TERMINATED = 0x0048, - MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH = 0x0049, - MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED = 0x004A, - MPI_IOCSTATUS_SCSI_IOC_TERMINATED = 0x004B, - MPI_IOCSTATUS_SCSI_EXT_TERMINATED = 0x004C, - - /************************************************************************/ - /* For use by SCSI Initiator and SCSI Target end-to-end data protection*/ - /************************************************************************/ - - MPI_IOCSTATUS_EEDP_GUARD_ERROR = 0x004D, - MPI_IOCSTATUS_EEDP_REF_TAG_ERROR = 0x004E, - MPI_IOCSTATUS_EEDP_APP_TAG_ERROR = 0x004F, - - /************************************************************************/ - /* SCSI Target values */ - /************************************************************************/ - - MPI_IOCSTATUS_TARGET_PRIORITY_IO = 0x0060, - MPI_IOCSTATUS_TARGET_INVALID_PORT = 0x0061, - MPI_IOCSTATUS_TARGET_INVALID_IO_INDEX = 0x0062, - MPI_IOCSTATUS_TARGET_ABORTED = 0x0063, - MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE = 0x0064, - MPI_IOCSTATUS_TARGET_NO_CONNECTION = 0x0065, - MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH = 0x006A, - MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT = 0x006B, - MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR = 0x006D, - MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA = 0x006E, - MPI_IOCSTATUS_TARGET_IU_TOO_SHORT = 0x006F, - MPI_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT = 0x0070, - MPI_IOCSTATUS_TARGET_NAK_RECEIVED = 0x0071, - - /************************************************************************/ - /* Fibre Channel Direct Access values */ - /************************************************************************/ - - MPI_IOCSTATUS_FC_ABORTED = 0x0066, - MPI_IOCSTATUS_FC_RX_ID_INVALID = 0x0067, - MPI_IOCSTATUS_FC_DID_INVALID = 0x0068, - MPI_IOCSTATUS_FC_NODE_LOGGED_OUT = 0x0069, - MPI_IOCSTATUS_FC_EXCHANGE_CANCELED = 0x006C, - - /************************************************************************/ - /* LAN values */ - /************************************************************************/ - - MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND = 0x0080, - MPI_IOCSTATUS_LAN_DEVICE_FAILURE = 0x0081, - MPI_IOCSTATUS_LAN_TRANSMIT_ERROR = 0x0082, - MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED = 0x0083, - MPI_IOCSTATUS_LAN_RECEIVE_ERROR = 0x0084, - MPI_IOCSTATUS_LAN_RECEIVE_ABORTED = 0x0085, - MPI_IOCSTATUS_LAN_PARTIAL_PACKET = 0x0086, - MPI_IOCSTATUS_LAN_CANCELED = 0x0087, - - /************************************************************************/ - /* Serial Attached SCSI values */ - /************************************************************************/ - - MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED = 0x0090, - MPI_IOCSTATUS_SAS_SMP_DATA_OVERRUN = 0x0091, - - /************************************************************************/ - /* Inband values */ - /************************************************************************/ - - MPI_IOCSTATUS_INBAND_ABORTED = 0x0098, - MPI_IOCSTATUS_INBAND_NO_CONNECTION = 0x0099, - - /************************************************************************/ - /* Diagnostic Tools values */ - /************************************************************************/ - - MPI_IOCSTATUS_DIAGNOSTIC_RELEASED = 0x00A0, - - /************************************************************************/ - /* IOCStatus flag to indicate that log info is available */ - /************************************************************************/ - - MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE = 0x8000, - MPI_IOCSTATUS_MASK = 0x7FFF, - - /************************************************************************/ - /* LogInfo Types */ - /************************************************************************/ - - MPI_IOCLOGINFO_TYPE_MASK = 0xF0000000, - MPI_IOCLOGINFO_TYPE_SHIFT = 28, - MPI_IOCLOGINFO_TYPE_NONE = 0x0, - MPI_IOCLOGINFO_TYPE_SCSI = 0x1, - MPI_IOCLOGINFO_TYPE_FC = 0x2, - MPI_IOCLOGINFO_TYPE_SAS = 0x3, - MPI_IOCLOGINFO_TYPE_ISCSI = 0x4, - MPI_IOCLOGINFO_LOG_DATA_MASK = 0x0FFFFFFF, -}; - -/****************************************************************************/ -/* SCSI IO messages and associated structures */ -/****************************************************************************/ - -typedef struct MPIMsgSCSIIORequest { - uint8_t TargetID; /* 00h */ - uint8_t Bus; /* 01h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t CDBLength; /* 04h */ - uint8_t SenseBufferLength; /* 05h */ - uint8_t Reserved; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t LUN[8]; /* 0Ch */ - uint32_t Control; /* 14h */ - uint8_t CDB[16]; /* 18h */ - uint32_t DataLength; /* 28h */ - uint32_t SenseBufferLowAddr; /* 2Ch */ -} QEMU_PACKED MPIMsgSCSIIORequest; - -/* SCSI IO MsgFlags bits */ - -#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH (0x01) -#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 (0x00) -#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 (0x01) - -#define MPI_SCSIIO_MSGFLGS_SENSE_LOCATION (0x02) -#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00) -#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02) - -#define MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR (0x04) - -/* SCSI IO LUN fields */ - -#define MPI_SCSIIO_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) -#define MPI_SCSIIO_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) -#define MPI_SCSIIO_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) -#define MPI_SCSIIO_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) -#define MPI_SCSIIO_LUN_LEVEL_1_WORD (0xFF00) -#define MPI_SCSIIO_LUN_LEVEL_1_DWORD (0x0000FF00) - -/* SCSI IO Control bits */ - -#define MPI_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) -#define MPI_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) -#define MPI_SCSIIO_CONTROL_WRITE (0x01000000) -#define MPI_SCSIIO_CONTROL_READ (0x02000000) - -#define MPI_SCSIIO_CONTROL_ADDCDBLEN_MASK (0x3C000000) -#define MPI_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) - -#define MPI_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) -#define MPI_SCSIIO_CONTROL_SIMPLEQ (0x00000000) -#define MPI_SCSIIO_CONTROL_HEADOFQ (0x00000100) -#define MPI_SCSIIO_CONTROL_ORDEREDQ (0x00000200) -#define MPI_SCSIIO_CONTROL_ACAQ (0x00000400) -#define MPI_SCSIIO_CONTROL_UNTAGGED (0x00000500) -#define MPI_SCSIIO_CONTROL_NO_DISCONNECT (0x00000700) - -#define MPI_SCSIIO_CONTROL_TASKMANAGE_MASK (0x00FF0000) -#define MPI_SCSIIO_CONTROL_OBSOLETE (0x00800000) -#define MPI_SCSIIO_CONTROL_CLEAR_ACA_RSV (0x00400000) -#define MPI_SCSIIO_CONTROL_TARGET_RESET (0x00200000) -#define MPI_SCSIIO_CONTROL_LUN_RESET_RSV (0x00100000) -#define MPI_SCSIIO_CONTROL_RESERVED (0x00080000) -#define MPI_SCSIIO_CONTROL_CLR_TASK_SET_RSV (0x00040000) -#define MPI_SCSIIO_CONTROL_ABORT_TASK_SET (0x00020000) -#define MPI_SCSIIO_CONTROL_RESERVED2 (0x00010000) - -/* SCSI IO reply structure */ -typedef struct MPIMsgSCSIIOReply -{ - uint8_t TargetID; /* 00h */ - uint8_t Bus; /* 01h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t CDBLength; /* 04h */ - uint8_t SenseBufferLength; /* 05h */ - uint8_t Reserved; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t SCSIStatus; /* 0Ch */ - uint8_t SCSIState; /* 0Dh */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ - uint32_t TransferCount; /* 14h */ - uint32_t SenseCount; /* 18h */ - uint32_t ResponseInfo; /* 1Ch */ - uint16_t TaskTag; /* 20h */ - uint16_t Reserved1; /* 22h */ -} QEMU_PACKED MPIMsgSCSIIOReply; - -/* SCSI IO Reply SCSIStatus values (SAM-2 status codes) */ - -#define MPI_SCSI_STATUS_SUCCESS (0x00) -#define MPI_SCSI_STATUS_CHECK_CONDITION (0x02) -#define MPI_SCSI_STATUS_CONDITION_MET (0x04) -#define MPI_SCSI_STATUS_BUSY (0x08) -#define MPI_SCSI_STATUS_INTERMEDIATE (0x10) -#define MPI_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) -#define MPI_SCSI_STATUS_RESERVATION_CONFLICT (0x18) -#define MPI_SCSI_STATUS_COMMAND_TERMINATED (0x22) -#define MPI_SCSI_STATUS_TASK_SET_FULL (0x28) -#define MPI_SCSI_STATUS_ACA_ACTIVE (0x30) - -#define MPI_SCSI_STATUS_FCPEXT_DEVICE_LOGGED_OUT (0x80) -#define MPI_SCSI_STATUS_FCPEXT_NO_LINK (0x81) -#define MPI_SCSI_STATUS_FCPEXT_UNASSIGNED (0x82) - - -/* SCSI IO Reply SCSIState values */ - -#define MPI_SCSI_STATE_AUTOSENSE_VALID (0x01) -#define MPI_SCSI_STATE_AUTOSENSE_FAILED (0x02) -#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04) -#define MPI_SCSI_STATE_TERMINATED (0x08) -#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10) -#define MPI_SCSI_STATE_QUEUE_TAG_REJECTED (0x20) - -/* SCSI IO Reply ResponseInfo values */ -/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */ - -#define MPI_SCSI_RSP_INFO_FUNCTION_COMPLETE (0x00000000) -#define MPI_SCSI_RSP_INFO_FCP_BURST_LEN_ERROR (0x01000000) -#define MPI_SCSI_RSP_INFO_CMND_FIELDS_INVALID (0x02000000) -#define MPI_SCSI_RSP_INFO_FCP_DATA_RO_ERROR (0x03000000) -#define MPI_SCSI_RSP_INFO_TASK_MGMT_UNSUPPORTED (0x04000000) -#define MPI_SCSI_RSP_INFO_TASK_MGMT_FAILED (0x05000000) -#define MPI_SCSI_RSP_INFO_SPI_LQ_INVALID_TYPE (0x06000000) - -#define MPI_SCSI_TASKTAG_UNKNOWN (0xFFFF) - - -/****************************************************************************/ -/* SCSI Task Management messages */ -/****************************************************************************/ - -typedef struct MPIMsgSCSITaskMgmt { - uint8_t TargetID; /* 00h */ - uint8_t Bus; /* 01h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved; /* 04h */ - uint8_t TaskType; /* 05h */ - uint8_t Reserved1; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t LUN[8]; /* 0Ch */ - uint32_t Reserved2[7]; /* 14h */ - uint32_t TaskMsgContext; /* 30h */ -} QEMU_PACKED MPIMsgSCSITaskMgmt; - -enum { - /* TaskType values */ - - MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK = 0x01, - MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET = 0x02, - MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET = 0x03, - MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS = 0x04, - MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET = 0x05, - MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET = 0x06, - MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK = 0x07, - MPI_SCSITASKMGMT_TASKTYPE_CLR_ACA = 0x08, - - /* MsgFlags bits */ - - MPI_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU = 0x01, - - MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION = 0x00, - MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION = 0x02, - MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION = 0x04, - - MPI_SCSITASKMGMT_MSGFLAGS_SOFT_RESET_OPTION = 0x08, -}; - -/* SCSI Task Management Reply */ -typedef struct MPIMsgSCSITaskMgmtReply { - uint8_t TargetID; /* 00h */ - uint8_t Bus; /* 01h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t ResponseCode; /* 04h */ - uint8_t TaskType; /* 05h */ - uint8_t Reserved1; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t Reserved2[2]; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ - uint32_t TerminationCount; /* 14h */ -} QEMU_PACKED MPIMsgSCSITaskMgmtReply; - -/* ResponseCode values */ -enum { - MPI_SCSITASKMGMT_RSP_TM_COMPLETE = 0x00, - MPI_SCSITASKMGMT_RSP_INVALID_FRAME = 0x02, - MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED = 0x04, - MPI_SCSITASKMGMT_RSP_TM_FAILED = 0x05, - MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED = 0x08, - MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN = 0x09, - MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC = 0x80, -}; - -/****************************************************************************/ -/* IOCInit message */ -/****************************************************************************/ - -typedef struct MPIMsgIOCInit { - uint8_t WhoInit; /* 00h */ - uint8_t Reserved; /* 01h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Flags; /* 04h */ - uint8_t MaxDevices; /* 05h */ - uint8_t MaxBuses; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint16_t ReplyFrameSize; /* 0Ch */ - uint8_t Reserved1[2]; /* 0Eh */ - uint32_t HostMfaHighAddr; /* 10h */ - uint32_t SenseBufferHighAddr; /* 14h */ - uint32_t ReplyFifoHostSignalingAddr; /* 18h */ - MPISGEntry HostPageBufferSGE; /* 1Ch */ - uint16_t MsgVersion; /* 28h */ - uint16_t HeaderVersion; /* 2Ah */ -} QEMU_PACKED MPIMsgIOCInit; - -enum { - /* WhoInit values */ - - MPI_WHOINIT_NO_ONE = 0x00, - MPI_WHOINIT_SYSTEM_BIOS = 0x01, - MPI_WHOINIT_ROM_BIOS = 0x02, - MPI_WHOINIT_PCI_PEER = 0x03, - MPI_WHOINIT_HOST_DRIVER = 0x04, - MPI_WHOINIT_MANUFACTURER = 0x05, - - /* Flags values */ - - MPI_IOCINIT_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04, - MPI_IOCINIT_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02, - MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE = 0x01, - - /* MsgVersion */ - - MPI_IOCINIT_MSGVERSION_MAJOR_MASK = 0xFF00, - MPI_IOCINIT_MSGVERSION_MAJOR_SHIFT = 8, - MPI_IOCINIT_MSGVERSION_MINOR_MASK = 0x00FF, - MPI_IOCINIT_MSGVERSION_MINOR_SHIFT = 0, - - /* HeaderVersion */ - - MPI_IOCINIT_HEADERVERSION_UNIT_MASK = 0xFF00, - MPI_IOCINIT_HEADERVERSION_UNIT_SHIFT = 8, - MPI_IOCINIT_HEADERVERSION_DEV_MASK = 0x00FF, - MPI_IOCINIT_HEADERVERSION_DEV_SHIFT = 0, -}; - -typedef struct MPIMsgIOCInitReply { - uint8_t WhoInit; /* 00h */ - uint8_t Reserved; /* 01h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Flags; /* 04h */ - uint8_t MaxDevices; /* 05h */ - uint8_t MaxBuses; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint16_t Reserved2; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ -} QEMU_PACKED MPIMsgIOCInitReply; - - - -/****************************************************************************/ -/* IOC Facts message */ -/****************************************************************************/ - -typedef struct MPIMsgIOCFacts { - uint8_t Reserved[2]; /* 00h */ - uint8_t ChainOffset; /* 01h */ - uint8_t Function; /* 02h */ - uint8_t Reserved1[3]; /* 03h */ - uint8_t MsgFlags; /* 04h */ - uint32_t MsgContext; /* 08h */ -} QEMU_PACKED MPIMsgIOCFacts; - -/* IOC Facts Reply */ -typedef struct MPIMsgIOCFactsReply { - uint16_t MsgVersion; /* 00h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint16_t HeaderVersion; /* 04h */ - uint8_t IOCNumber; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint16_t IOCExceptions; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ - uint8_t MaxChainDepth; /* 14h */ - uint8_t WhoInit; /* 15h */ - uint8_t BlockSize; /* 16h */ - uint8_t Flags; /* 17h */ - uint16_t ReplyQueueDepth; /* 18h */ - uint16_t RequestFrameSize; /* 1Ah */ - uint16_t Reserved_0101_FWVersion; /* 1Ch */ /* obsolete 16-bit FWVersion */ - uint16_t ProductID; /* 1Eh */ - uint32_t CurrentHostMfaHighAddr; /* 20h */ - uint16_t GlobalCredits; /* 24h */ - uint8_t NumberOfPorts; /* 26h */ - uint8_t EventState; /* 27h */ - uint32_t CurrentSenseBufferHighAddr; /* 28h */ - uint16_t CurReplyFrameSize; /* 2Ch */ - uint8_t MaxDevices; /* 2Eh */ - uint8_t MaxBuses; /* 2Fh */ - uint32_t FWImageSize; /* 30h */ - uint32_t IOCCapabilities; /* 34h */ - uint8_t FWVersionDev; /* 38h */ - uint8_t FWVersionUnit; /* 39h */ - uint8_t FWVersionMinor; /* 3ah */ - uint8_t FWVersionMajor; /* 3bh */ - uint16_t HighPriorityQueueDepth; /* 3Ch */ - uint16_t Reserved2; /* 3Eh */ - MPISGEntry HostPageBufferSGE; /* 40h */ - uint32_t ReplyFifoHostSignalingAddr; /* 4Ch */ -} QEMU_PACKED MPIMsgIOCFactsReply; - -enum { - MPI_IOCFACTS_MSGVERSION_MAJOR_MASK = 0xFF00, - MPI_IOCFACTS_MSGVERSION_MAJOR_SHIFT = 8, - MPI_IOCFACTS_MSGVERSION_MINOR_MASK = 0x00FF, - MPI_IOCFACTS_MSGVERSION_MINOR_SHIFT = 0, - - MPI_IOCFACTS_HDRVERSION_UNIT_MASK = 0xFF00, - MPI_IOCFACTS_HDRVERSION_UNIT_SHIFT = 8, - MPI_IOCFACTS_HDRVERSION_DEV_MASK = 0x00FF, - MPI_IOCFACTS_HDRVERSION_DEV_SHIFT = 0, - - MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL = 0x0001, - MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID = 0x0002, - MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL = 0x0004, - MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL = 0x0008, - MPI_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED = 0x0010, - - MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT = 0x01, - MPI_IOCFACTS_FLAGS_REPLY_FIFO_HOST_SIGNAL = 0x02, - MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT = 0x04, - - MPI_IOCFACTS_EVENTSTATE_DISABLED = 0x00, - MPI_IOCFACTS_EVENTSTATE_ENABLED = 0x01, - - MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q = 0x00000001, - MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL = 0x00000002, - MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING = 0x00000004, - MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER = 0x00000008, - MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER = 0x00000010, - MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER = 0x00000020, - MPI_IOCFACTS_CAPABILITY_EEDP = 0x00000040, - MPI_IOCFACTS_CAPABILITY_BIDIRECTIONAL = 0x00000080, - MPI_IOCFACTS_CAPABILITY_MULTICAST = 0x00000100, - MPI_IOCFACTS_CAPABILITY_SCSIIO32 = 0x00000200, - MPI_IOCFACTS_CAPABILITY_NO_SCSIIO16 = 0x00000400, - MPI_IOCFACTS_CAPABILITY_TLR = 0x00000800, -}; - -/****************************************************************************/ -/* Port Facts message and Reply */ -/****************************************************************************/ - -typedef struct MPIMsgPortFacts { - uint8_t Reserved[2]; /* 00h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[2]; /* 04h */ - uint8_t PortNumber; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ -} QEMU_PACKED MPIMsgPortFacts; - -typedef struct MPIMsgPortFactsReply { - uint16_t Reserved; /* 00h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint16_t Reserved1; /* 04h */ - uint8_t PortNumber; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint16_t Reserved2; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ - uint8_t Reserved3; /* 14h */ - uint8_t PortType; /* 15h */ - uint16_t MaxDevices; /* 16h */ - uint16_t PortSCSIID; /* 18h */ - uint16_t ProtocolFlags; /* 1Ah */ - uint16_t MaxPostedCmdBuffers; /* 1Ch */ - uint16_t MaxPersistentIDs; /* 1Eh */ - uint16_t MaxLanBuckets; /* 20h */ - uint8_t MaxInitiators; /* 22h */ - uint8_t Reserved4; /* 23h */ - uint32_t Reserved5; /* 24h */ -} QEMU_PACKED MPIMsgPortFactsReply; - - -enum { - /* PortTypes values */ - MPI_PORTFACTS_PORTTYPE_INACTIVE = 0x00, - MPI_PORTFACTS_PORTTYPE_SCSI = 0x01, - MPI_PORTFACTS_PORTTYPE_FC = 0x10, - MPI_PORTFACTS_PORTTYPE_ISCSI = 0x20, - MPI_PORTFACTS_PORTTYPE_SAS = 0x30, - - /* ProtocolFlags values */ - MPI_PORTFACTS_PROTOCOL_LOGBUSADDR = 0x01, - MPI_PORTFACTS_PROTOCOL_LAN = 0x02, - MPI_PORTFACTS_PROTOCOL_TARGET = 0x04, - MPI_PORTFACTS_PROTOCOL_INITIATOR = 0x08, -}; - - -/****************************************************************************/ -/* Port Enable Message */ -/****************************************************************************/ - -typedef struct MPIMsgPortEnable { - uint8_t Reserved[2]; /* 00h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[2]; /* 04h */ - uint8_t PortNumber; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ -} QEMU_PACKED MPIMsgPortEnable; - -typedef struct MPIMsgPortEnableReply { - uint8_t Reserved[2]; /* 00h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[2]; /* 04h */ - uint8_t PortNumber; /* 05h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint16_t Reserved2; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ -} QEMU_PACKED MPIMsgPortEnableReply; - -/****************************************************************************/ -/* Event Notification messages */ -/****************************************************************************/ - -typedef struct MPIMsgEventNotify { - uint8_t Switch; /* 00h */ - uint8_t Reserved; /* 01h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[3]; /* 04h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ -} QEMU_PACKED MPIMsgEventNotify; - -/* Event Notification Reply */ - -typedef struct MPIMsgEventNotifyReply { - uint16_t EventDataLength; /* 00h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[2]; /* 04h */ - uint8_t AckRequired; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t Reserved2[2]; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ - uint32_t Event; /* 14h */ - uint32_t EventContext; /* 18h */ - uint32_t Data[1]; /* 1Ch */ -} QEMU_PACKED MPIMsgEventNotifyReply; - -/* Event Acknowledge */ - -typedef struct MPIMsgEventAck { - uint8_t Reserved[2]; /* 00h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[3]; /* 04h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint32_t Event; /* 0Ch */ - uint32_t EventContext; /* 10h */ -} QEMU_PACKED MPIMsgEventAck; - -typedef struct MPIMsgEventAckReply { - uint8_t Reserved[2]; /* 00h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint8_t Reserved1[3]; /* 04h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint16_t Reserved2; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ -} QEMU_PACKED MPIMsgEventAckReply; - -enum { - /* Switch */ - - MPI_EVENT_NOTIFICATION_SWITCH_OFF = 0x00, - MPI_EVENT_NOTIFICATION_SWITCH_ON = 0x01, - - /* Event */ - - MPI_EVENT_NONE = 0x00000000, - MPI_EVENT_LOG_DATA = 0x00000001, - MPI_EVENT_STATE_CHANGE = 0x00000002, - MPI_EVENT_UNIT_ATTENTION = 0x00000003, - MPI_EVENT_IOC_BUS_RESET = 0x00000004, - MPI_EVENT_EXT_BUS_RESET = 0x00000005, - MPI_EVENT_RESCAN = 0x00000006, - MPI_EVENT_LINK_STATUS_CHANGE = 0x00000007, - MPI_EVENT_LOOP_STATE_CHANGE = 0x00000008, - MPI_EVENT_LOGOUT = 0x00000009, - MPI_EVENT_EVENT_CHANGE = 0x0000000A, - MPI_EVENT_INTEGRATED_RAID = 0x0000000B, - MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE = 0x0000000C, - MPI_EVENT_ON_BUS_TIMER_EXPIRED = 0x0000000D, - MPI_EVENT_QUEUE_FULL = 0x0000000E, - MPI_EVENT_SAS_DEVICE_STATUS_CHANGE = 0x0000000F, - MPI_EVENT_SAS_SES = 0x00000010, - MPI_EVENT_PERSISTENT_TABLE_FULL = 0x00000011, - MPI_EVENT_SAS_PHY_LINK_STATUS = 0x00000012, - MPI_EVENT_SAS_DISCOVERY_ERROR = 0x00000013, - MPI_EVENT_IR_RESYNC_UPDATE = 0x00000014, - MPI_EVENT_IR2 = 0x00000015, - MPI_EVENT_SAS_DISCOVERY = 0x00000016, - MPI_EVENT_SAS_BROADCAST_PRIMITIVE = 0x00000017, - MPI_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE = 0x00000018, - MPI_EVENT_SAS_INIT_TABLE_OVERFLOW = 0x00000019, - MPI_EVENT_SAS_SMP_ERROR = 0x0000001A, - MPI_EVENT_SAS_EXPANDER_STATUS_CHANGE = 0x0000001B, - MPI_EVENT_LOG_ENTRY_ADDED = 0x00000021, - - /* AckRequired field values */ - - MPI_EVENT_NOTIFICATION_ACK_NOT_REQUIRED = 0x00, - MPI_EVENT_NOTIFICATION_ACK_REQUIRED = 0x01, -}; - -/**************************************************************************** -* Config Request Message -****************************************************************************/ - -typedef struct MPIMsgConfig { - uint8_t Action; /* 00h */ - uint8_t Reserved; /* 01h */ - uint8_t ChainOffset; /* 02h */ - uint8_t Function; /* 03h */ - uint16_t ExtPageLength; /* 04h */ - uint8_t ExtPageType; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t Reserved2[8]; /* 0Ch */ - uint8_t PageVersion; /* 14h */ - uint8_t PageLength; /* 15h */ - uint8_t PageNumber; /* 16h */ - uint8_t PageType; /* 17h */ - uint32_t PageAddress; /* 18h */ - MPISGEntry PageBufferSGE; /* 1Ch */ -} QEMU_PACKED MPIMsgConfig; - -/* Action field values */ - -enum { - MPI_CONFIG_ACTION_PAGE_HEADER = 0x00, - MPI_CONFIG_ACTION_PAGE_READ_CURRENT = 0x01, - MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT = 0x02, - MPI_CONFIG_ACTION_PAGE_DEFAULT = 0x03, - MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM = 0x04, - MPI_CONFIG_ACTION_PAGE_READ_DEFAULT = 0x05, - MPI_CONFIG_ACTION_PAGE_READ_NVRAM = 0x06, -}; - - -/* Config Reply Message */ -typedef struct MPIMsgConfigReply { - uint8_t Action; /* 00h */ - uint8_t Reserved; /* 01h */ - uint8_t MsgLength; /* 02h */ - uint8_t Function; /* 03h */ - uint16_t ExtPageLength; /* 04h */ - uint8_t ExtPageType; /* 06h */ - uint8_t MsgFlags; /* 07h */ - uint32_t MsgContext; /* 08h */ - uint8_t Reserved2[2]; /* 0Ch */ - uint16_t IOCStatus; /* 0Eh */ - uint32_t IOCLogInfo; /* 10h */ - uint8_t PageVersion; /* 14h */ - uint8_t PageLength; /* 15h */ - uint8_t PageNumber; /* 16h */ - uint8_t PageType; /* 17h */ -} QEMU_PACKED MPIMsgConfigReply; - -enum { - /* PageAddress field values */ - MPI_CONFIG_PAGEATTR_READ_ONLY = 0x00, - MPI_CONFIG_PAGEATTR_CHANGEABLE = 0x10, - MPI_CONFIG_PAGEATTR_PERSISTENT = 0x20, - MPI_CONFIG_PAGEATTR_RO_PERSISTENT = 0x30, - MPI_CONFIG_PAGEATTR_MASK = 0xF0, - - MPI_CONFIG_PAGETYPE_IO_UNIT = 0x00, - MPI_CONFIG_PAGETYPE_IOC = 0x01, - MPI_CONFIG_PAGETYPE_BIOS = 0x02, - MPI_CONFIG_PAGETYPE_SCSI_PORT = 0x03, - MPI_CONFIG_PAGETYPE_SCSI_DEVICE = 0x04, - MPI_CONFIG_PAGETYPE_FC_PORT = 0x05, - MPI_CONFIG_PAGETYPE_FC_DEVICE = 0x06, - MPI_CONFIG_PAGETYPE_LAN = 0x07, - MPI_CONFIG_PAGETYPE_RAID_VOLUME = 0x08, - MPI_CONFIG_PAGETYPE_MANUFACTURING = 0x09, - MPI_CONFIG_PAGETYPE_RAID_PHYSDISK = 0x0A, - MPI_CONFIG_PAGETYPE_INBAND = 0x0B, - MPI_CONFIG_PAGETYPE_EXTENDED = 0x0F, - MPI_CONFIG_PAGETYPE_MASK = 0x0F, - - MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT = 0x10, - MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER = 0x11, - MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE = 0x12, - MPI_CONFIG_EXTPAGETYPE_SAS_PHY = 0x13, - MPI_CONFIG_EXTPAGETYPE_LOG = 0x14, - MPI_CONFIG_EXTPAGETYPE_ENCLOSURE = 0x15, - - MPI_SCSI_PORT_PGAD_PORT_MASK = 0x000000FF, - - MPI_SCSI_DEVICE_FORM_MASK = 0xF0000000, - MPI_SCSI_DEVICE_FORM_BUS_TID = 0x00000000, - MPI_SCSI_DEVICE_TARGET_ID_MASK = 0x000000FF, - MPI_SCSI_DEVICE_TARGET_ID_SHIFT = 0, - MPI_SCSI_DEVICE_BUS_MASK = 0x0000FF00, - MPI_SCSI_DEVICE_BUS_SHIFT = 8, - MPI_SCSI_DEVICE_FORM_TARGET_MODE = 0x10000000, - MPI_SCSI_DEVICE_TM_RESPOND_ID_MASK = 0x000000FF, - MPI_SCSI_DEVICE_TM_RESPOND_ID_SHIFT = 0, - MPI_SCSI_DEVICE_TM_BUS_MASK = 0x0000FF00, - MPI_SCSI_DEVICE_TM_BUS_SHIFT = 8, - MPI_SCSI_DEVICE_TM_INIT_ID_MASK = 0x00FF0000, - MPI_SCSI_DEVICE_TM_INIT_ID_SHIFT = 16, - - MPI_FC_PORT_PGAD_PORT_MASK = 0xF0000000, - MPI_FC_PORT_PGAD_PORT_SHIFT = 28, - MPI_FC_PORT_PGAD_FORM_MASK = 0x0F000000, - MPI_FC_PORT_PGAD_FORM_INDEX = 0x01000000, - MPI_FC_PORT_PGAD_INDEX_MASK = 0x0000FFFF, - MPI_FC_PORT_PGAD_INDEX_SHIFT = 0, - - MPI_FC_DEVICE_PGAD_PORT_MASK = 0xF0000000, - MPI_FC_DEVICE_PGAD_PORT_SHIFT = 28, - MPI_FC_DEVICE_PGAD_FORM_MASK = 0x0F000000, - MPI_FC_DEVICE_PGAD_FORM_NEXT_DID = 0x00000000, - MPI_FC_DEVICE_PGAD_ND_PORT_MASK = 0xF0000000, - MPI_FC_DEVICE_PGAD_ND_PORT_SHIFT = 28, - MPI_FC_DEVICE_PGAD_ND_DID_MASK = 0x00FFFFFF, - MPI_FC_DEVICE_PGAD_ND_DID_SHIFT = 0, - MPI_FC_DEVICE_PGAD_FORM_BUS_TID = 0x01000000, - MPI_FC_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00, - MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT = 8, - MPI_FC_DEVICE_PGAD_BT_TID_MASK = 0x000000FF, - MPI_FC_DEVICE_PGAD_BT_TID_SHIFT = 0, - - MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK = 0x000000FF, - MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT = 0, - - MPI_SAS_EXPAND_PGAD_FORM_MASK = 0xF0000000, - MPI_SAS_EXPAND_PGAD_FORM_SHIFT = 28, - MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000, - MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM = 0x00000001, - MPI_SAS_EXPAND_PGAD_FORM_HANDLE = 0x00000002, - MPI_SAS_EXPAND_PGAD_GNH_MASK_HANDLE = 0x0000FFFF, - MPI_SAS_EXPAND_PGAD_GNH_SHIFT_HANDLE = 0, - MPI_SAS_EXPAND_PGAD_HPN_MASK_PHY = 0x00FF0000, - MPI_SAS_EXPAND_PGAD_HPN_SHIFT_PHY = 16, - MPI_SAS_EXPAND_PGAD_HPN_MASK_HANDLE = 0x0000FFFF, - MPI_SAS_EXPAND_PGAD_HPN_SHIFT_HANDLE = 0, - MPI_SAS_EXPAND_PGAD_H_MASK_HANDLE = 0x0000FFFF, - MPI_SAS_EXPAND_PGAD_H_SHIFT_HANDLE = 0, - - MPI_SAS_DEVICE_PGAD_FORM_MASK = 0xF0000000, - MPI_SAS_DEVICE_PGAD_FORM_SHIFT = 28, - MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000, - MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID = 0x00000001, - MPI_SAS_DEVICE_PGAD_FORM_HANDLE = 0x00000002, - MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK = 0x0000FFFF, - MPI_SAS_DEVICE_PGAD_GNH_HANDLE_SHIFT = 0, - MPI_SAS_DEVICE_PGAD_BT_BUS_MASK = 0x0000FF00, - MPI_SAS_DEVICE_PGAD_BT_BUS_SHIFT = 8, - MPI_SAS_DEVICE_PGAD_BT_TID_MASK = 0x000000FF, - MPI_SAS_DEVICE_PGAD_BT_TID_SHIFT = 0, - MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK = 0x0000FFFF, - MPI_SAS_DEVICE_PGAD_H_HANDLE_SHIFT = 0, - - MPI_SAS_PHY_PGAD_FORM_MASK = 0xF0000000, - MPI_SAS_PHY_PGAD_FORM_SHIFT = 28, - MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER = 0x0, - MPI_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX = 0x1, - MPI_SAS_PHY_PGAD_PHY_NUMBER_MASK = 0x000000FF, - MPI_SAS_PHY_PGAD_PHY_NUMBER_SHIFT = 0, - MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK = 0x0000FFFF, - MPI_SAS_PHY_PGAD_PHY_TBL_INDEX_SHIFT = 0, - - MPI_SAS_ENCLOS_PGAD_FORM_MASK = 0xF0000000, - MPI_SAS_ENCLOS_PGAD_FORM_SHIFT = 28, - MPI_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE = 0x00000000, - MPI_SAS_ENCLOS_PGAD_FORM_HANDLE = 0x00000001, - MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_MASK = 0x0000FFFF, - MPI_SAS_ENCLOS_PGAD_GNH_HANDLE_SHIFT = 0, - MPI_SAS_ENCLOS_PGAD_H_HANDLE_MASK = 0x0000FFFF, - MPI_SAS_ENCLOS_PGAD_H_HANDLE_SHIFT = 0, -}; - -/* Too many structs and definitions... see mptconfig.c for the few - * that are used. - */ - -/****************************************************************************/ -/* Firmware Upload message and associated structures */ -/****************************************************************************/ - -enum { - /* defines for using the ProductId field */ - MPI_FW_HEADER_PID_TYPE_MASK = 0xF000, - MPI_FW_HEADER_PID_TYPE_SCSI = 0x0000, - MPI_FW_HEADER_PID_TYPE_FC = 0x1000, - MPI_FW_HEADER_PID_TYPE_SAS = 0x2000, - - MPI_FW_HEADER_PID_PROD_MASK = 0x0F00, - MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI = 0x0100, - MPI_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI = 0x0200, - MPI_FW_HEADER_PID_PROD_TARGET_SCSI = 0x0300, - MPI_FW_HEADER_PID_PROD_IM_SCSI = 0x0400, - MPI_FW_HEADER_PID_PROD_IS_SCSI = 0x0500, - MPI_FW_HEADER_PID_PROD_CTX_SCSI = 0x0600, - MPI_FW_HEADER_PID_PROD_IR_SCSI = 0x0700, - - MPI_FW_HEADER_PID_FAMILY_MASK = 0x00FF, - - /* SCSI */ - MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI = 0x0001, - MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI = 0x0002, - MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI = 0x0003, - MPI_FW_HEADER_PID_FAMILY_1030C0_SCSI = 0x0004, - MPI_FW_HEADER_PID_FAMILY_1020A0_SCSI = 0x0005, - MPI_FW_HEADER_PID_FAMILY_1020B0_SCSI = 0x0006, - MPI_FW_HEADER_PID_FAMILY_1020B1_SCSI = 0x0007, - MPI_FW_HEADER_PID_FAMILY_1020C0_SCSI = 0x0008, - MPI_FW_HEADER_PID_FAMILY_1035A0_SCSI = 0x0009, - MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI = 0x000A, - MPI_FW_HEADER_PID_FAMILY_1030TA0_SCSI = 0x000B, - MPI_FW_HEADER_PID_FAMILY_1020TA0_SCSI = 0x000C, - - /* Fibre Channel */ - MPI_FW_HEADER_PID_FAMILY_909_FC = 0x0000, - MPI_FW_HEADER_PID_FAMILY_919_FC = 0x0001, /* 919 and 929 */ - MPI_FW_HEADER_PID_FAMILY_919X_FC = 0x0002, /* 919X and 929X */ - MPI_FW_HEADER_PID_FAMILY_919XL_FC = 0x0003, /* 919XL and 929XL */ - MPI_FW_HEADER_PID_FAMILY_939X_FC = 0x0004, /* 939X and 949X */ - MPI_FW_HEADER_PID_FAMILY_959_FC = 0x0005, - MPI_FW_HEADER_PID_FAMILY_949E_FC = 0x0006, - - /* SAS */ - MPI_FW_HEADER_PID_FAMILY_1064_SAS = 0x0001, - MPI_FW_HEADER_PID_FAMILY_1068_SAS = 0x0002, - MPI_FW_HEADER_PID_FAMILY_1078_SAS = 0x0003, - MPI_FW_HEADER_PID_FAMILY_106xE_SAS = 0x0004, /* 1068E, 1066E, and 1064E */ -}; - -#endif diff --git a/qemu/hw/scsi/mptconfig.c b/qemu/hw/scsi/mptconfig.c deleted file mode 100644 index 707185469..000000000 --- a/qemu/hw/scsi/mptconfig.c +++ /dev/null @@ -1,905 +0,0 @@ -/* - * QEMU LSI SAS1068 Host Bus Adapter emulation - configuration pages - * - * Copyright (c) 2016 Red Hat, Inc. - * - * Author: Paolo Bonzini - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/scsi/scsi.h" - -#include "mptsas.h" -#include "mpi.h" -#include "trace.h" - -/* Generic functions for marshaling and unmarshaling. */ - -#define repl1(x) x -#define repl2(x) x x -#define repl3(x) x x x -#define repl4(x) x x x x -#define repl5(x) x x x x x -#define repl6(x) x x x x x x -#define repl7(x) x x x x x x x -#define repl8(x) x x x x x x x x - -#define repl(n, x) glue(repl, n)(x) - -typedef union PackValue { - uint64_t ll; - char *str; -} PackValue; - -static size_t vfill(uint8_t *data, size_t size, const char *fmt, va_list ap) -{ - size_t ofs; - PackValue val; - const char *p; - - ofs = 0; - p = fmt; - while (*p) { - memset(&val, 0, sizeof(val)); - switch (*p) { - case '*': - p++; - break; - case 'b': - case 'w': - case 'l': - val.ll = va_arg(ap, int); - break; - case 'q': - val.ll = va_arg(ap, int64_t); - break; - case 's': - val.str = va_arg(ap, void *); - break; - } - switch (*p++) { - case 'b': - if (data) { - stb_p(data + ofs, val.ll); - } - ofs++; - break; - case 'w': - if (data) { - stw_le_p(data + ofs, val.ll); - } - ofs += 2; - break; - case 'l': - if (data) { - stl_le_p(data + ofs, val.ll); - } - ofs += 4; - break; - case 'q': - if (data) { - stq_le_p(data + ofs, val.ll); - } - ofs += 8; - break; - case 's': - { - int cnt = atoi(p); - if (data) { - if (val.str) { - strncpy((void *)data + ofs, val.str, cnt); - } else { - memset((void *)data + ofs, 0, cnt); - } - } - ofs += cnt; - break; - } - } - } - - return ofs; -} - -static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1) -{ - size_t size = 0; - uint8_t *data = NULL; - - if (p_data) { - va_list ap2; - - va_copy(ap2, ap1); - size = vfill(NULL, 0, fmt, ap2); - *p_data = data = g_malloc(size); - va_end(ap2); - } - return vfill(data, size, fmt, ap1); -} - -static size_t fill(uint8_t *data, size_t size, const char *fmt, ...) -{ - va_list ap; - size_t ret; - - va_start(ap, fmt); - ret = vfill(data, size, fmt, ap); - va_end(ap); - - return ret; -} - -/* Functions to build the page header and fill in the length, always used - * through the macros. - */ - -#define MPTSAS_CONFIG_PACK(number, type, version, fmt, ...) \ - mptsas_config_pack(data, "b*bbb" fmt, version, number, type, \ - ## __VA_ARGS__) - -static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...) -{ - va_list ap; - size_t ret; - - va_start(ap, fmt); - ret = vpack(data, fmt, ap); - va_end(ap); - - if (data) { - assert(ret < 256 && (ret % 4) == 0); - stb_p(*data + 1, ret / 4); - } - return ret; -} - -#define MPTSAS_CONFIG_PACK_EXT(number, type, version, fmt, ...) \ - mptsas_config_pack_ext(data, "b*bbb*wb*b" fmt, version, number, \ - MPI_CONFIG_PAGETYPE_EXTENDED, type, ## __VA_ARGS__) - -static size_t mptsas_config_pack_ext(uint8_t **data, const char *fmt, ...) -{ - va_list ap; - size_t ret; - - va_start(ap, fmt); - ret = vpack(data, fmt, ap); - va_end(ap); - - if (data) { - assert(ret < 65536 && (ret % 4) == 0); - stw_le_p(*data + 4, ret / 4); - } - return ret; -} - -/* Manufacturing pages */ - -static -size_t mptsas_config_manufacturing_0(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "s16s8s16s16s16", - "QEMU MPT Fusion", - "2.5", - "QEMU MPT Fusion", - "QEMU", - "0000111122223333"); -} - -static -size_t mptsas_config_manufacturing_1(MPTSASState *s, uint8_t **data, int address) -{ - /* VPD - all zeros */ - return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "s256"); -} - -static -size_t mptsas_config_manufacturing_2(MPTSASState *s, uint8_t **data, int address) -{ - PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); - return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "wb*b*l", - pcic->device_id, pcic->revision); -} - -static -size_t mptsas_config_manufacturing_3(MPTSASState *s, uint8_t **data, int address) -{ - PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); - return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "wb*b*l", - pcic->device_id, pcic->revision); -} - -static -size_t mptsas_config_manufacturing_4(MPTSASState *s, uint8_t **data, int address) -{ - /* All zeros */ - return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x05, - "*l*b*b*b*b*b*b*w*s56*l*l*l*l*l*l" - "*b*b*w*b*b*w*l*l"); -} - -static -size_t mptsas_config_manufacturing_5(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x02, - "q*b*b*w*l*l", s->sas_addr); -} - -static -size_t mptsas_config_manufacturing_6(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "*l"); -} - -static -size_t mptsas_config_manufacturing_7(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(7, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "*l*l*l*s16*b*b*w", MPTSAS_NUM_PORTS); -} - -static -size_t mptsas_config_manufacturing_8(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(8, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "*l"); -} - -static -size_t mptsas_config_manufacturing_9(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(9, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "*l"); -} - -static -size_t mptsas_config_manufacturing_10(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(10, MPI_CONFIG_PAGETYPE_MANUFACTURING, 0x00, - "*l"); -} - -/* I/O unit pages */ - -static -size_t mptsas_config_io_unit_0(MPTSASState *s, uint8_t **data, int address) -{ - PCIDevice *pci = PCI_DEVICE(s); - uint64_t unique_value = 0x53504D554D4551LL; /* "QEMUMPTx" */ - - unique_value |= (uint64_t)pci->devfn << 56; - return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, - "q", unique_value); -} - -static -size_t mptsas_config_io_unit_1(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, "l", - 0x41 /* single function, RAID disabled */ ); -} - -static -size_t mptsas_config_io_unit_2(MPTSASState *s, uint8_t **data, int address) -{ - PCIDevice *pci = PCI_DEVICE(s); - uint8_t devfn = pci->devfn; - return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x02, - "llbbw*b*b*w*b*b*w*b*b*w*l", - 0, 0x100, 0 /* pci bus? */, devfn, 0); -} - -static -size_t mptsas_config_io_unit_3(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x01, - "*b*b*w*l"); -} - -static -size_t mptsas_config_io_unit_4(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IO_UNIT, 0x00, "*l*l*q"); -} - -/* I/O controller pages */ - -static -size_t mptsas_config_ioc_0(MPTSASState *s, uint8_t **data, int address) -{ - PCIDeviceClass *pcic = PCI_DEVICE_GET_CLASS(s); - - return MPTSAS_CONFIG_PACK(0, MPI_CONFIG_PAGETYPE_IOC, 0x01, - "*l*lwwb*b*b*blww", - pcic->vendor_id, pcic->device_id, pcic->revision, - pcic->subsystem_vendor_id, - pcic->subsystem_id); -} - -static -size_t mptsas_config_ioc_1(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(1, MPI_CONFIG_PAGETYPE_IOC, 0x03, - "*l*l*b*b*b*b"); -} - -static -size_t mptsas_config_ioc_2(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(2, MPI_CONFIG_PAGETYPE_IOC, 0x04, - "*l*b*b*b*b"); -} - -static -size_t mptsas_config_ioc_3(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(3, MPI_CONFIG_PAGETYPE_IOC, 0x00, - "*b*b*w"); -} - -static -size_t mptsas_config_ioc_4(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(4, MPI_CONFIG_PAGETYPE_IOC, 0x00, - "*b*b*w"); -} - -static -size_t mptsas_config_ioc_5(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(5, MPI_CONFIG_PAGETYPE_IOC, 0x00, - "*l*b*b*w"); -} - -static -size_t mptsas_config_ioc_6(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK(6, MPI_CONFIG_PAGETYPE_IOC, 0x01, - "*l*b*b*b*b*b*b*b*b*b*b*w*l*l*l*l*b*b*w" - "*w*w*w*w*l*l*l"); -} - -/* SAS I/O unit pages (extended) */ - -#define MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE 16 - -#define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION 0x02 -#define MPI_SAS_IOUNIT0_RATE_1_5 0x08 -#define MPI_SAS_IOUNIT0_RATE_3_0 0x09 - -#define MPI_SAS_DEVICE_INFO_NO_DEVICE 0x00000000 -#define MPI_SAS_DEVICE_INFO_END_DEVICE 0x00000001 -#define MPI_SAS_DEVICE_INFO_SSP_TARGET 0x00000400 - -#define MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS 0x00 - -#define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT 0x0001 -#define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED 0x0002 -#define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT 0x0004 - - - -static SCSIDevice *mptsas_phy_get_device(MPTSASState *s, int i, - int *phy_handle, int *dev_handle) -{ - SCSIDevice *d = scsi_device_find(&s->bus, 0, i, 0); - - if (phy_handle) { - *phy_handle = i + 1; - } - if (dev_handle) { - *dev_handle = d ? i + 1 + MPTSAS_NUM_PORTS : 0; - } - return d; -} - -static -size_t mptsas_config_sas_io_unit_0(MPTSASState *s, uint8_t **data, int address) -{ - size_t size = MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x04, - "*w*wb*b*w" - repl(MPTSAS_NUM_PORTS, "*s16"), - MPTSAS_NUM_PORTS); - - if (data) { - size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; - int i; - - for (i = 0; i < MPTSAS_NUM_PORTS; i++) { - int phy_handle, dev_handle; - SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); - - fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE, - "bbbblwwl", i, 0, 0, - (dev - ? MPI_SAS_IOUNIT0_RATE_3_0 - : MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION), - (dev - ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET - : MPI_SAS_DEVICE_INFO_NO_DEVICE), - dev_handle, - dev_handle, - 0); - ofs += MPTSAS_CONFIG_SAS_IO_UNIT_0_SIZE; - } - assert(ofs == size); - } - return size; -} - -#define MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE 12 - -static -size_t mptsas_config_sas_io_unit_1(MPTSASState *s, uint8_t **data, int address) -{ - size_t size = MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x07, - "*w*w*w*wb*b*b*b" - repl(MPTSAS_NUM_PORTS, "*s12"), - MPTSAS_NUM_PORTS); - - if (data) { - size_t ofs = size - MPTSAS_NUM_PORTS * MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; - int i; - - for (i = 0; i < MPTSAS_NUM_PORTS; i++) { - SCSIDevice *dev = mptsas_phy_get_device(s, i, NULL, NULL); - fill(*data + ofs, MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE, - "bbbblww", i, 0, 0, - (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, - (dev - ? MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET - : MPI_SAS_DEVICE_INFO_NO_DEVICE), - 0, 0); - ofs += MPTSAS_CONFIG_SAS_IO_UNIT_1_SIZE; - } - assert(ofs == size); - } - return size; -} - -static -size_t mptsas_config_sas_io_unit_2(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, - "*b*b*w*w*w*b*b*w"); -} - -static -size_t mptsas_config_sas_io_unit_3(MPTSASState *s, uint8_t **data, int address) -{ - return MPTSAS_CONFIG_PACK_EXT(3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0x06, - "*l*l*l*l*l*l*l*l*l"); -} - -/* SAS PHY pages (extended) */ - -static int mptsas_phy_addr_get(MPTSASState *s, int address) -{ - int i; - if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 0) { - i = address & 255; - } else if ((address >> MPI_SAS_PHY_PGAD_FORM_SHIFT) == 1) { - i = address & 65535; - } else { - return -EINVAL; - } - - if (i >= MPTSAS_NUM_PORTS) { - return -EINVAL; - } - - return i; -} - -static -size_t mptsas_config_phy_0(MPTSASState *s, uint8_t **data, int address) -{ - int phy_handle = -1; - int dev_handle = -1; - int i = mptsas_phy_addr_get(s, address); - SCSIDevice *dev; - - if (i < 0) { - trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); - return i; - } - - dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); - trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 0); - - return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, - "w*wqwb*blbb*b*b*l", - dev_handle, s->sas_addr, dev_handle, i, - (dev - ? MPI_SAS_DEVICE_INFO_END_DEVICE /* | MPI_SAS_DEVICE_INFO_SSP_TARGET?? */ - : MPI_SAS_DEVICE_INFO_NO_DEVICE), - (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5, - (MPI_SAS_IOUNIT0_RATE_3_0 << 4) | MPI_SAS_IOUNIT0_RATE_1_5); -} - -static -size_t mptsas_config_phy_1(MPTSASState *s, uint8_t **data, int address) -{ - int phy_handle = -1; - int dev_handle = -1; - int i = mptsas_phy_addr_get(s, address); - - if (i < 0) { - trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); - return i; - } - - (void) mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); - trace_mptsas_config_sas_phy(s, address, i, phy_handle, dev_handle, 1); - - return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, 0x01, - "*l*l*l*l*l"); -} - -/* SAS device pages (extended) */ - -static int mptsas_device_addr_get(MPTSASState *s, int address) -{ - uint32_t handle, i; - uint32_t form = address >> MPI_SAS_PHY_PGAD_FORM_SHIFT; - if (form == MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) { - handle = address & MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK; - do { - if (handle == 65535) { - handle = MPTSAS_NUM_PORTS + 1; - } else { - ++handle; - } - i = handle - 1 - MPTSAS_NUM_PORTS; - } while (i < MPTSAS_NUM_PORTS && !scsi_device_find(&s->bus, 0, i, 0)); - - } else if (form == MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID) { - if (address & MPI_SAS_DEVICE_PGAD_BT_BUS_MASK) { - return -EINVAL; - } - i = address & MPI_SAS_DEVICE_PGAD_BT_TID_MASK; - - } else if (form == MPI_SAS_DEVICE_PGAD_FORM_HANDLE) { - handle = address & MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK; - i = handle - 1 - MPTSAS_NUM_PORTS; - - } else { - return -EINVAL; - } - - if (i >= MPTSAS_NUM_PORTS) { - return -EINVAL; - } - - return i; -} - -static -size_t mptsas_config_sas_device_0(MPTSASState *s, uint8_t **data, int address) -{ - int phy_handle = -1; - int dev_handle = -1; - int i = mptsas_device_addr_get(s, address); - SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); - - trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 0); - if (!dev) { - return -ENOENT; - } - - return MPTSAS_CONFIG_PACK_EXT(0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x05, - "*w*wqwbbwbblwb*b", - dev->wwn, phy_handle, i, - MPI_SAS_DEVICE0_ASTATUS_NO_ERRORS, - dev_handle, i, 0, - MPI_SAS_DEVICE_INFO_END_DEVICE | MPI_SAS_DEVICE_INFO_SSP_TARGET, - (MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT | - MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED | - MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT), i); -} - -static -size_t mptsas_config_sas_device_1(MPTSASState *s, uint8_t **data, int address) -{ - int phy_handle = -1; - int dev_handle = -1; - int i = mptsas_device_addr_get(s, address); - SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); - - trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 1); - if (!dev) { - return -ENOENT; - } - - return MPTSAS_CONFIG_PACK_EXT(1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x00, - "*lq*lwbb*s20", - dev->wwn, dev_handle, i, 0); -} - -static -size_t mptsas_config_sas_device_2(MPTSASState *s, uint8_t **data, int address) -{ - int phy_handle = -1; - int dev_handle = -1; - int i = mptsas_device_addr_get(s, address); - SCSIDevice *dev = mptsas_phy_get_device(s, i, &phy_handle, &dev_handle); - - trace_mptsas_config_sas_device(s, address, i, phy_handle, dev_handle, 2); - if (!dev) { - return -ENOENT; - } - - return MPTSAS_CONFIG_PACK_EXT(2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0x01, - "ql", dev->wwn, 0); -} - -typedef struct MPTSASConfigPage { - uint8_t number; - uint8_t type; - size_t (*mpt_config_build)(MPTSASState *s, uint8_t **data, int address); -} MPTSASConfigPage; - -static const MPTSASConfigPage mptsas_config_pages[] = { - { - 0, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_0, - }, { - 1, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_1, - }, { - 2, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_2, - }, { - 3, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_3, - }, { - 4, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_4, - }, { - 5, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_5, - }, { - 6, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_6, - }, { - 7, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_7, - }, { - 8, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_8, - }, { - 9, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_9, - }, { - 10, MPI_CONFIG_PAGETYPE_MANUFACTURING, - mptsas_config_manufacturing_10, - }, { - 0, MPI_CONFIG_PAGETYPE_IO_UNIT, - mptsas_config_io_unit_0, - }, { - 1, MPI_CONFIG_PAGETYPE_IO_UNIT, - mptsas_config_io_unit_1, - }, { - 2, MPI_CONFIG_PAGETYPE_IO_UNIT, - mptsas_config_io_unit_2, - }, { - 3, MPI_CONFIG_PAGETYPE_IO_UNIT, - mptsas_config_io_unit_3, - }, { - 4, MPI_CONFIG_PAGETYPE_IO_UNIT, - mptsas_config_io_unit_4, - }, { - 0, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_0, - }, { - 1, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_1, - }, { - 2, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_2, - }, { - 3, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_3, - }, { - 4, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_4, - }, { - 5, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_5, - }, { - 6, MPI_CONFIG_PAGETYPE_IOC, - mptsas_config_ioc_6, - }, { - 0, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, - mptsas_config_sas_io_unit_0, - }, { - 1, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, - mptsas_config_sas_io_unit_1, - }, { - 2, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, - mptsas_config_sas_io_unit_2, - }, { - 3, MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, - mptsas_config_sas_io_unit_3, - }, { - 0, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, - mptsas_config_phy_0, - }, { - 1, MPI_CONFIG_EXTPAGETYPE_SAS_PHY, - mptsas_config_phy_1, - }, { - 0, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, - mptsas_config_sas_device_0, - }, { - 1, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, - mptsas_config_sas_device_1, - }, { - 2, MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE, - mptsas_config_sas_device_2, - } -}; - -static const MPTSASConfigPage *mptsas_find_config_page(int type, int number) -{ - const MPTSASConfigPage *page; - int i; - - for (i = 0; i < ARRAY_SIZE(mptsas_config_pages); i++) { - page = &mptsas_config_pages[i]; - if (page->type == type && page->number == number) { - return page; - } - } - - return NULL; -} - -void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req) -{ - PCIDevice *pci = PCI_DEVICE(s); - - MPIMsgConfigReply reply; - const MPTSASConfigPage *page; - size_t length; - uint8_t type; - uint8_t *data = NULL; - uint32_t flags_and_length; - uint32_t dmalen; - uint64_t pa; - - mptsas_fix_config_endianness(req); - - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - /* Copy common bits from the request into the reply. */ - memset(&reply, 0, sizeof(reply)); - reply.Action = req->Action; - reply.Function = req->Function; - reply.MsgContext = req->MsgContext; - reply.MsgLength = sizeof(reply) / 4; - reply.PageType = req->PageType; - reply.PageNumber = req->PageNumber; - reply.PageLength = req->PageLength; - reply.PageVersion = req->PageVersion; - - type = req->PageType & MPI_CONFIG_PAGETYPE_MASK; - if (type == MPI_CONFIG_PAGETYPE_EXTENDED) { - type = req->ExtPageType; - if (type <= MPI_CONFIG_PAGETYPE_MASK) { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; - goto out; - } - - reply.ExtPageType = req->ExtPageType; - } - - page = mptsas_find_config_page(type, req->PageNumber); - - switch(req->Action) { - case MPI_CONFIG_ACTION_PAGE_DEFAULT: - case MPI_CONFIG_ACTION_PAGE_HEADER: - case MPI_CONFIG_ACTION_PAGE_READ_NVRAM: - case MPI_CONFIG_ACTION_PAGE_READ_CURRENT: - case MPI_CONFIG_ACTION_PAGE_READ_DEFAULT: - case MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT: - case MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM: - break; - - default: - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_ACTION; - goto out; - } - - if (!page) { - page = mptsas_find_config_page(type, 1); - if (page) { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; - } else { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_TYPE; - } - goto out; - } - - if (req->Action == MPI_CONFIG_ACTION_PAGE_DEFAULT || - req->Action == MPI_CONFIG_ACTION_PAGE_HEADER) { - length = page->mpt_config_build(s, NULL, req->PageAddress); - if ((ssize_t)length < 0) { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; - goto out; - } else { - goto done; - } - } - - if (req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT || - req->Action == MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM) { - length = page->mpt_config_build(s, NULL, req->PageAddress); - if ((ssize_t)length < 0) { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; - } else { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_CANT_COMMIT; - } - goto out; - } - - flags_and_length = req->PageBufferSGE.FlagsLength; - dmalen = flags_and_length & MPI_SGE_LENGTH_MASK; - if (dmalen == 0) { - length = page->mpt_config_build(s, NULL, req->PageAddress); - if ((ssize_t)length < 0) { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; - goto out; - } else { - goto done; - } - } - - if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { - pa = req->PageBufferSGE.u.Address64; - } else { - pa = req->PageBufferSGE.u.Address32; - } - - /* Only read actions left. */ - length = page->mpt_config_build(s, &data, req->PageAddress); - if ((ssize_t)length < 0) { - reply.IOCStatus = MPI_IOCSTATUS_CONFIG_INVALID_PAGE; - goto out; - } else { - assert(data[2] == page->number); - pci_dma_write(pci, pa, data, MIN(length, dmalen)); - goto done; - } - - abort(); - -done: - if (type > MPI_CONFIG_PAGETYPE_MASK) { - reply.ExtPageLength = length / 4; - reply.ExtPageType = req->ExtPageType; - } else { - reply.PageLength = length / 4; - } - -out: - mptsas_fix_config_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); - g_free(data); -} diff --git a/qemu/hw/scsi/mptendian.c b/qemu/hw/scsi/mptendian.c deleted file mode 100644 index b7fe2a2a3..000000000 --- a/qemu/hw/scsi/mptendian.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * QEMU LSI SAS1068 Host Bus Adapter emulation - * Endianness conversion for MPI data structures - * - * Copyright (c) 2016 Red Hat, Inc. - * - * Authors: Paolo Bonzini <pbonzini@redhat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "sysemu/dma.h" -#include "sysemu/block-backend.h" -#include "hw/pci/msi.h" -#include "qemu/iov.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "trace.h" - -#include "mptsas.h" -#include "mpi.h" - -static void mptsas_fix_sgentry_endianness(MPISGEntry *sge) -{ - le32_to_cpus(&sge->FlagsLength); - if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { - le64_to_cpus(&sge->u.Address64); - } else { - le32_to_cpus(&sge->u.Address32); - } -} - -static void mptsas_fix_sgentry_endianness_reply(MPISGEntry *sge) -{ - if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { - cpu_to_le64s(&sge->u.Address64); - } else { - cpu_to_le32s(&sge->u.Address32); - } - cpu_to_le32s(&sge->FlagsLength); -} - -void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req) -{ - le32_to_cpus(&req->MsgContext); - le32_to_cpus(&req->Control); - le32_to_cpus(&req->DataLength); - le32_to_cpus(&req->SenseBufferLowAddr); -} - -void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply) -{ - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); - cpu_to_le32s(&reply->TransferCount); - cpu_to_le32s(&reply->SenseCount); - cpu_to_le32s(&reply->ResponseInfo); - cpu_to_le16s(&reply->TaskTag); -} - -void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req) -{ - le32_to_cpus(&req->MsgContext); - le32_to_cpus(&req->TaskMsgContext); -} - -void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply) -{ - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); - cpu_to_le32s(&reply->TerminationCount); -} - -void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req) -{ - le32_to_cpus(&req->MsgContext); - le16_to_cpus(&req->ReplyFrameSize); - le32_to_cpus(&req->HostMfaHighAddr); - le32_to_cpus(&req->SenseBufferHighAddr); - le32_to_cpus(&req->ReplyFifoHostSignalingAddr); - mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE); - le16_to_cpus(&req->MsgVersion); - le16_to_cpus(&req->HeaderVersion); -} - -void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply) -{ - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); -} - -void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req) -{ - le32_to_cpus(&req->MsgContext); -} - -void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply) -{ - cpu_to_le16s(&reply->MsgVersion); - cpu_to_le16s(&reply->HeaderVersion); - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCExceptions); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); - cpu_to_le16s(&reply->ReplyQueueDepth); - cpu_to_le16s(&reply->RequestFrameSize); - cpu_to_le16s(&reply->ProductID); - cpu_to_le32s(&reply->CurrentHostMfaHighAddr); - cpu_to_le16s(&reply->GlobalCredits); - cpu_to_le32s(&reply->CurrentSenseBufferHighAddr); - cpu_to_le16s(&reply->CurReplyFrameSize); - cpu_to_le32s(&reply->FWImageSize); - cpu_to_le32s(&reply->IOCCapabilities); - cpu_to_le16s(&reply->HighPriorityQueueDepth); - mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE); - cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr); -} - -void mptsas_fix_config_endianness(MPIMsgConfig *req) -{ - le16_to_cpus(&req->ExtPageLength); - le32_to_cpus(&req->MsgContext); - le32_to_cpus(&req->PageAddress); - mptsas_fix_sgentry_endianness(&req->PageBufferSGE); -} - -void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply) -{ - cpu_to_le16s(&reply->ExtPageLength); - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); -} - -void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req) -{ - le32_to_cpus(&req->MsgContext); -} - -void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply) -{ - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); - cpu_to_le16s(&reply->MaxDevices); - cpu_to_le16s(&reply->PortSCSIID); - cpu_to_le16s(&reply->ProtocolFlags); - cpu_to_le16s(&reply->MaxPostedCmdBuffers); - cpu_to_le16s(&reply->MaxPersistentIDs); - cpu_to_le16s(&reply->MaxLanBuckets); -} - -void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req) -{ - le32_to_cpus(&req->MsgContext); -} - -void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply) -{ - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); -} - -void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req) -{ - le32_to_cpus(&req->MsgContext); -} - -void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply) -{ - int length = reply->EventDataLength; - int i; - - cpu_to_le16s(&reply->EventDataLength); - cpu_to_le32s(&reply->MsgContext); - cpu_to_le16s(&reply->IOCStatus); - cpu_to_le32s(&reply->IOCLogInfo); - cpu_to_le32s(&reply->Event); - cpu_to_le32s(&reply->EventContext); - - /* Really depends on the event kind. This will do for now. */ - for (i = 0; i < length; i++) { - cpu_to_le32s(&reply->Data[i]); - } -} - diff --git a/qemu/hw/scsi/mptsas.c b/qemu/hw/scsi/mptsas.c deleted file mode 100644 index 499c1465a..000000000 --- a/qemu/hw/scsi/mptsas.c +++ /dev/null @@ -1,1442 +0,0 @@ -/* - * QEMU LSI SAS1068 Host Bus Adapter emulation - * Based on the QEMU Megaraid emulator - * - * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs - * Copyright (c) 2012 Verizon, Inc. - * Copyright (c) 2016 Red Hat, Inc. - * - * Authors: Don Slutz, Paolo Bonzini - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "sysemu/dma.h" -#include "sysemu/block-backend.h" -#include "hw/pci/msi.h" -#include "qemu/iov.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "trace.h" - -#include "mptsas.h" -#include "mpi.h" - -#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL -#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400 - -#define TYPE_MPTSAS1068 "mptsas1068" - -#define MPT_SAS(obj) \ - OBJECT_CHECK(MPTSASState, (obj), TYPE_MPTSAS1068) - -#define MPTSAS1068_PRODUCT_ID \ - (MPI_FW_HEADER_PID_FAMILY_1068_SAS | \ - MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI | \ - MPI_FW_HEADER_PID_TYPE_SAS) - -struct MPTSASRequest { - MPIMsgSCSIIORequest scsi_io; - SCSIRequest *sreq; - QEMUSGList qsg; - MPTSASState *dev; - - QTAILQ_ENTRY(MPTSASRequest) next; -}; - -static void mptsas_update_interrupt(MPTSASState *s) -{ - PCIDevice *pci = (PCIDevice *) s; - uint32_t state = s->intr_status & ~(s->intr_mask | MPI_HIS_IOP_DOORBELL_STATUS); - - if (s->msi_in_use && msi_enabled(pci)) { - if (state) { - trace_mptsas_irq_msi(s); - msi_notify(pci, 0); - } - } - - trace_mptsas_irq_intx(s, !!state); - pci_set_irq(pci, !!state); -} - -static void mptsas_set_fault(MPTSASState *s, uint32_t code) -{ - if ((s->state & MPI_IOC_STATE_FAULT) == 0) { - s->state = MPI_IOC_STATE_FAULT | code; - } -} - -#define MPTSAS_FIFO_INVALID(s, name) \ - ((s)->name##_head > ARRAY_SIZE((s)->name) || \ - (s)->name##_tail > ARRAY_SIZE((s)->name)) - -#define MPTSAS_FIFO_EMPTY(s, name) \ - ((s)->name##_head == (s)->name##_tail) - -#define MPTSAS_FIFO_FULL(s, name) \ - ((s)->name##_head == ((s)->name##_tail + 1) % ARRAY_SIZE((s)->name)) - -#define MPTSAS_FIFO_GET(s, name) ({ \ - uint32_t _val = (s)->name[(s)->name##_head++]; \ - (s)->name##_head %= ARRAY_SIZE((s)->name); \ - _val; \ -}) - -#define MPTSAS_FIFO_PUT(s, name, val) do { \ - (s)->name[(s)->name##_tail++] = (val); \ - (s)->name##_tail %= ARRAY_SIZE((s)->name); \ -} while(0) - -static void mptsas_post_reply(MPTSASState *s, MPIDefaultReply *reply) -{ - PCIDevice *pci = (PCIDevice *) s; - uint32_t addr_lo; - - if (MPTSAS_FIFO_EMPTY(s, reply_free) || MPTSAS_FIFO_FULL(s, reply_post)) { - mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); - return; - } - - addr_lo = MPTSAS_FIFO_GET(s, reply_free); - - pci_dma_write(pci, addr_lo | s->host_mfa_high_addr, reply, - MIN(s->reply_frame_size, 4 * reply->MsgLength)); - - MPTSAS_FIFO_PUT(s, reply_post, MPI_ADDRESS_REPLY_A_BIT | (addr_lo >> 1)); - - s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT; - if (s->doorbell_state == DOORBELL_WRITE) { - s->doorbell_state = DOORBELL_NONE; - s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT; - } - mptsas_update_interrupt(s); -} - -void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply) -{ - if (s->doorbell_state == DOORBELL_WRITE) { - /* The reply is sent out in 16 bit chunks, while the size - * in the reply is in 32 bit units. - */ - s->doorbell_state = DOORBELL_READ; - s->doorbell_reply_idx = 0; - s->doorbell_reply_size = reply->MsgLength * 2; - memcpy(s->doorbell_reply, reply, s->doorbell_reply_size * 2); - s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT; - mptsas_update_interrupt(s); - } else { - mptsas_post_reply(s, reply); - } -} - -static void mptsas_turbo_reply(MPTSASState *s, uint32_t msgctx) -{ - if (MPTSAS_FIFO_FULL(s, reply_post)) { - mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); - return; - } - - /* The reply is just the message context ID (bit 31 = clear). */ - MPTSAS_FIFO_PUT(s, reply_post, msgctx); - - s->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT; - mptsas_update_interrupt(s); -} - -#define MPTSAS_MAX_REQUEST_SIZE 52 - -static const int mpi_request_sizes[] = { - [MPI_FUNCTION_SCSI_IO_REQUEST] = sizeof(MPIMsgSCSIIORequest), - [MPI_FUNCTION_SCSI_TASK_MGMT] = sizeof(MPIMsgSCSITaskMgmt), - [MPI_FUNCTION_IOC_INIT] = sizeof(MPIMsgIOCInit), - [MPI_FUNCTION_IOC_FACTS] = sizeof(MPIMsgIOCFacts), - [MPI_FUNCTION_CONFIG] = sizeof(MPIMsgConfig), - [MPI_FUNCTION_PORT_FACTS] = sizeof(MPIMsgPortFacts), - [MPI_FUNCTION_PORT_ENABLE] = sizeof(MPIMsgPortEnable), - [MPI_FUNCTION_EVENT_NOTIFICATION] = sizeof(MPIMsgEventNotify), -}; - -static dma_addr_t mptsas_ld_sg_base(MPTSASState *s, uint32_t flags_and_length, - dma_addr_t *sgaddr) -{ - PCIDevice *pci = (PCIDevice *) s; - dma_addr_t addr; - - if (flags_and_length & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { - addr = ldq_le_pci_dma(pci, *sgaddr + 4); - *sgaddr += 12; - } else { - addr = ldl_le_pci_dma(pci, *sgaddr + 4); - *sgaddr += 8; - } - return addr; -} - -static int mptsas_build_sgl(MPTSASState *s, MPTSASRequest *req, hwaddr addr) -{ - PCIDevice *pci = (PCIDevice *) s; - hwaddr next_chain_addr; - uint32_t left; - hwaddr sgaddr; - uint32_t chain_offset; - - chain_offset = req->scsi_io.ChainOffset; - next_chain_addr = addr + chain_offset * sizeof(uint32_t); - sgaddr = addr + sizeof(MPIMsgSCSIIORequest); - pci_dma_sglist_init(&req->qsg, pci, 4); - left = req->scsi_io.DataLength; - - for(;;) { - dma_addr_t addr, len; - uint32_t flags_and_length; - - flags_and_length = ldl_le_pci_dma(pci, sgaddr); - len = flags_and_length & MPI_SGE_LENGTH_MASK; - if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK) - != MPI_SGE_FLAGS_SIMPLE_ELEMENT || - (!len && - !(flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) && - !(flags_and_length & MPI_SGE_FLAGS_END_OF_BUFFER))) { - return MPI_IOCSTATUS_INVALID_SGL; - } - - len = MIN(len, left); - if (!len) { - /* We reached the desired transfer length, ignore extra - * elements of the s/g list. - */ - break; - } - - addr = mptsas_ld_sg_base(s, flags_and_length, &sgaddr); - qemu_sglist_add(&req->qsg, addr, len); - left -= len; - - if (flags_and_length & MPI_SGE_FLAGS_END_OF_LIST) { - break; - } - - if (flags_and_length & MPI_SGE_FLAGS_LAST_ELEMENT) { - if (!chain_offset) { - break; - } - - flags_and_length = ldl_le_pci_dma(pci, next_chain_addr); - if ((flags_and_length & MPI_SGE_FLAGS_ELEMENT_TYPE_MASK) - != MPI_SGE_FLAGS_CHAIN_ELEMENT) { - return MPI_IOCSTATUS_INVALID_SGL; - } - - sgaddr = mptsas_ld_sg_base(s, flags_and_length, &next_chain_addr); - chain_offset = - (flags_and_length & MPI_SGE_CHAIN_OFFSET_MASK) >> MPI_SGE_CHAIN_OFFSET_SHIFT; - next_chain_addr = sgaddr + chain_offset * sizeof(uint32_t); - } - } - return 0; -} - -static void mptsas_free_request(MPTSASRequest *req) -{ - MPTSASState *s = req->dev; - - if (req->sreq != NULL) { - req->sreq->hba_private = NULL; - scsi_req_unref(req->sreq); - req->sreq = NULL; - QTAILQ_REMOVE(&s->pending, req, next); - } - qemu_sglist_destroy(&req->qsg); - g_free(req); -} - -static int mptsas_scsi_device_find(MPTSASState *s, int bus, int target, - uint8_t *lun, SCSIDevice **sdev) -{ - if (bus != 0) { - return MPI_IOCSTATUS_SCSI_INVALID_BUS; - } - - if (target >= s->max_devices) { - return MPI_IOCSTATUS_SCSI_INVALID_TARGETID; - } - - *sdev = scsi_device_find(&s->bus, bus, target, lun[1]); - if (!*sdev) { - return MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE; - } - - return 0; -} - -static int mptsas_process_scsi_io_request(MPTSASState *s, - MPIMsgSCSIIORequest *scsi_io, - hwaddr addr) -{ - MPTSASRequest *req; - MPIMsgSCSIIOReply reply; - SCSIDevice *sdev; - int status; - - mptsas_fix_scsi_io_endianness(scsi_io); - - trace_mptsas_process_scsi_io_request(s, scsi_io->Bus, scsi_io->TargetID, - scsi_io->LUN[1], scsi_io->DataLength); - - status = mptsas_scsi_device_find(s, scsi_io->Bus, scsi_io->TargetID, - scsi_io->LUN, &sdev); - if (status) { - goto bad; - } - - req = g_new(MPTSASRequest, 1); - QTAILQ_INSERT_TAIL(&s->pending, req, next); - req->scsi_io = *scsi_io; - req->dev = s; - - status = mptsas_build_sgl(s, req, addr); - if (status) { - goto free_bad; - } - - if (req->qsg.size < scsi_io->DataLength) { - trace_mptsas_sgl_overflow(s, scsi_io->MsgContext, scsi_io->DataLength, - req->qsg.size); - status = MPI_IOCSTATUS_INVALID_SGL; - goto free_bad; - } - - req->sreq = scsi_req_new(sdev, scsi_io->MsgContext, - scsi_io->LUN[1], scsi_io->CDB, req); - - if (req->sreq->cmd.xfer > scsi_io->DataLength) { - goto overrun; - } - switch (scsi_io->Control & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK) { - case MPI_SCSIIO_CONTROL_NODATATRANSFER: - if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - goto overrun; - } - break; - - case MPI_SCSIIO_CONTROL_WRITE: - if (req->sreq->cmd.mode != SCSI_XFER_TO_DEV) { - goto overrun; - } - break; - - case MPI_SCSIIO_CONTROL_READ: - if (req->sreq->cmd.mode != SCSI_XFER_FROM_DEV) { - goto overrun; - } - break; - } - - if (scsi_req_enqueue(req->sreq)) { - scsi_req_continue(req->sreq); - } - return 0; - -overrun: - trace_mptsas_scsi_overflow(s, scsi_io->MsgContext, req->sreq->cmd.xfer, - scsi_io->DataLength); - status = MPI_IOCSTATUS_SCSI_DATA_OVERRUN; -free_bad: - mptsas_free_request(req); -bad: - memset(&reply, 0, sizeof(reply)); - reply.TargetID = scsi_io->TargetID; - reply.Bus = scsi_io->Bus; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = scsi_io->Function; - reply.CDBLength = scsi_io->CDBLength; - reply.SenseBufferLength = scsi_io->SenseBufferLength; - reply.MsgContext = scsi_io->MsgContext; - reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS; - reply.IOCStatus = status; - - mptsas_fix_scsi_io_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); - - return 0; -} - -typedef struct { - Notifier notifier; - MPTSASState *s; - MPIMsgSCSITaskMgmtReply *reply; -} MPTSASCancelNotifier; - -static void mptsas_cancel_notify(Notifier *notifier, void *data) -{ - MPTSASCancelNotifier *n = container_of(notifier, - MPTSASCancelNotifier, - notifier); - - /* Abusing IOCLogInfo to store the expected number of requests... */ - if (++n->reply->TerminationCount == n->reply->IOCLogInfo) { - n->reply->IOCLogInfo = 0; - mptsas_fix_scsi_task_mgmt_reply_endianness(n->reply); - mptsas_post_reply(n->s, (MPIDefaultReply *)n->reply); - g_free(n->reply); - } - g_free(n); -} - -static void mptsas_process_scsi_task_mgmt(MPTSASState *s, MPIMsgSCSITaskMgmt *req) -{ - MPIMsgSCSITaskMgmtReply reply; - MPIMsgSCSITaskMgmtReply *reply_async; - int status, count; - SCSIDevice *sdev; - SCSIRequest *r, *next; - BusChild *kid; - - mptsas_fix_scsi_task_mgmt_endianness(req); - - QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - memset(&reply, 0, sizeof(reply)); - reply.TargetID = req->TargetID; - reply.Bus = req->Bus; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->Function; - reply.TaskType = req->TaskType; - reply.MsgContext = req->MsgContext; - - switch (req->TaskType) { - case MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK: - case MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK: - status = mptsas_scsi_device_find(s, req->Bus, req->TargetID, - req->LUN, &sdev); - if (status) { - reply.IOCStatus = status; - goto out; - } - if (sdev->lun != req->LUN[1]) { - reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; - goto out; - } - - QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) { - MPTSASRequest *cmd_req = r->hba_private; - if (cmd_req && cmd_req->scsi_io.MsgContext == req->TaskMsgContext) { - break; - } - } - if (r) { - /* - * Assert that the request has not been completed yet, we - * check for it in the loop above. - */ - assert(r->hba_private); - if (req->TaskType == MPI_SCSITASKMGMT_TASKTYPE_QUERY_TASK) { - /* "If the specified command is present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED; - } else { - MPTSASCancelNotifier *notifier; - - reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply)); - reply_async->IOCLogInfo = INT_MAX; - - count = 1; - notifier = g_new(MPTSASCancelNotifier, 1); - notifier->s = s; - notifier->reply = reply_async; - notifier->notifier.notify = mptsas_cancel_notify; - scsi_req_cancel_async(r, ¬ifier->notifier); - goto reply_maybe_async; - } - } - break; - - case MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: - case MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET: - status = mptsas_scsi_device_find(s, req->Bus, req->TargetID, - req->LUN, &sdev); - if (status) { - reply.IOCStatus = status; - goto out; - } - if (sdev->lun != req->LUN[1]) { - reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; - goto out; - } - - reply_async = g_memdup(&reply, sizeof(MPIMsgSCSITaskMgmtReply)); - reply_async->IOCLogInfo = INT_MAX; - - count = 0; - QTAILQ_FOREACH_SAFE(r, &sdev->requests, next, next) { - if (r->hba_private) { - MPTSASCancelNotifier *notifier; - - count++; - notifier = g_new(MPTSASCancelNotifier, 1); - notifier->s = s; - notifier->reply = reply_async; - notifier->notifier.notify = mptsas_cancel_notify; - scsi_req_cancel_async(r, ¬ifier->notifier); - } - } - -reply_maybe_async: - if (reply_async->TerminationCount < count) { - reply_async->IOCLogInfo = count; - return; - } - g_free(reply_async); - reply.TerminationCount = count; - break; - - case MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: - status = mptsas_scsi_device_find(s, req->Bus, req->TargetID, - req->LUN, &sdev); - if (status) { - reply.IOCStatus = status; - goto out; - } - if (sdev->lun != req->LUN[1]) { - reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN; - goto out; - } - qdev_reset_all(&sdev->qdev); - break; - - case MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET: - if (req->Bus != 0) { - reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_BUS; - goto out; - } - if (req->TargetID > s->max_devices) { - reply.IOCStatus = MPI_IOCSTATUS_SCSI_INVALID_TARGETID; - goto out; - } - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - sdev = SCSI_DEVICE(kid->child); - if (sdev->channel == 0 && sdev->id == req->TargetID) { - qdev_reset_all(kid->child); - } - } - break; - - case MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS: - qbus_reset_all(&s->bus.qbus); - break; - - default: - reply.ResponseCode = MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED; - break; - } - -out: - mptsas_fix_scsi_task_mgmt_reply_endianness(&reply); - mptsas_post_reply(s, (MPIDefaultReply *)&reply); -} - -static void mptsas_process_ioc_init(MPTSASState *s, MPIMsgIOCInit *req) -{ - MPIMsgIOCInitReply reply; - - mptsas_fix_ioc_init_endianness(req); - - QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - s->who_init = req->WhoInit; - s->reply_frame_size = req->ReplyFrameSize; - s->max_buses = req->MaxBuses; - s->max_devices = req->MaxDevices ? req->MaxDevices : 256; - s->host_mfa_high_addr = (hwaddr)req->HostMfaHighAddr << 32; - s->sense_buffer_high_addr = (hwaddr)req->SenseBufferHighAddr << 32; - - if (s->state == MPI_IOC_STATE_READY) { - s->state = MPI_IOC_STATE_OPERATIONAL; - } - - memset(&reply, 0, sizeof(reply)); - reply.WhoInit = s->who_init; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->Function; - reply.MaxDevices = s->max_devices; - reply.MaxBuses = s->max_buses; - reply.MsgContext = req->MsgContext; - - mptsas_fix_ioc_init_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); -} - -static void mptsas_process_ioc_facts(MPTSASState *s, - MPIMsgIOCFacts *req) -{ - MPIMsgIOCFactsReply reply; - - mptsas_fix_ioc_facts_endianness(req); - - QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - memset(&reply, 0, sizeof(reply)); - reply.MsgVersion = 0x0105; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->Function; - reply.MsgContext = req->MsgContext; - reply.MaxChainDepth = MPTSAS_MAXIMUM_CHAIN_DEPTH; - reply.WhoInit = s->who_init; - reply.BlockSize = MPTSAS_MAX_REQUEST_SIZE / sizeof(uint32_t); - reply.ReplyQueueDepth = ARRAY_SIZE(s->reply_post) - 1; - QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->reply_post) != ARRAY_SIZE(s->reply_free)); - - reply.RequestFrameSize = 128; - reply.ProductID = MPTSAS1068_PRODUCT_ID; - reply.CurrentHostMfaHighAddr = s->host_mfa_high_addr >> 32; - reply.GlobalCredits = ARRAY_SIZE(s->request_post) - 1; - reply.NumberOfPorts = MPTSAS_NUM_PORTS; - reply.CurrentSenseBufferHighAddr = s->sense_buffer_high_addr >> 32; - reply.CurReplyFrameSize = s->reply_frame_size; - reply.MaxDevices = s->max_devices; - reply.MaxBuses = s->max_buses; - reply.FWVersionDev = 0; - reply.FWVersionUnit = 0x92; - reply.FWVersionMinor = 0x32; - reply.FWVersionMajor = 0x1; - - mptsas_fix_ioc_facts_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); -} - -static void mptsas_process_port_facts(MPTSASState *s, - MPIMsgPortFacts *req) -{ - MPIMsgPortFactsReply reply; - - mptsas_fix_port_facts_endianness(req); - - QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - memset(&reply, 0, sizeof(reply)); - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->Function; - reply.PortNumber = req->PortNumber; - reply.MsgContext = req->MsgContext; - - if (req->PortNumber < MPTSAS_NUM_PORTS) { - reply.PortType = MPI_PORTFACTS_PORTTYPE_SAS; - reply.MaxDevices = MPTSAS_NUM_PORTS; - reply.PortSCSIID = MPTSAS_NUM_PORTS; - reply.ProtocolFlags = MPI_PORTFACTS_PROTOCOL_LOGBUSADDR | MPI_PORTFACTS_PROTOCOL_INITIATOR; - } - - mptsas_fix_port_facts_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); -} - -static void mptsas_process_port_enable(MPTSASState *s, - MPIMsgPortEnable *req) -{ - MPIMsgPortEnableReply reply; - - mptsas_fix_port_enable_endianness(req); - - QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - memset(&reply, 0, sizeof(reply)); - reply.MsgLength = sizeof(reply) / 4; - reply.PortNumber = req->PortNumber; - reply.Function = req->Function; - reply.MsgContext = req->MsgContext; - - mptsas_fix_port_enable_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); -} - -static void mptsas_process_event_notification(MPTSASState *s, - MPIMsgEventNotify *req) -{ - MPIMsgEventNotifyReply reply; - - mptsas_fix_event_notification_endianness(req); - - QEMU_BUILD_BUG_ON(MPTSAS_MAX_REQUEST_SIZE < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_msg) < sizeof(*req)); - QEMU_BUILD_BUG_ON(sizeof(s->doorbell_reply) < sizeof(reply)); - - /* Don't even bother storing whether event notification is enabled, - * since it is not accessible. - */ - - memset(&reply, 0, sizeof(reply)); - reply.EventDataLength = sizeof(reply.Data) / 4; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->Function; - - /* This is set because events are sent through the reply FIFOs. */ - reply.MsgFlags = MPI_MSGFLAGS_CONTINUATION_REPLY; - - reply.MsgContext = req->MsgContext; - reply.Event = MPI_EVENT_EVENT_CHANGE; - reply.Data[0] = !!req->Switch; - - mptsas_fix_event_notification_reply_endianness(&reply); - mptsas_reply(s, (MPIDefaultReply *)&reply); -} - -static void mptsas_process_message(MPTSASState *s, MPIRequestHeader *req) -{ - trace_mptsas_process_message(s, req->Function, req->MsgContext); - switch (req->Function) { - case MPI_FUNCTION_SCSI_TASK_MGMT: - mptsas_process_scsi_task_mgmt(s, (MPIMsgSCSITaskMgmt *)req); - break; - - case MPI_FUNCTION_IOC_INIT: - mptsas_process_ioc_init(s, (MPIMsgIOCInit *)req); - break; - - case MPI_FUNCTION_IOC_FACTS: - mptsas_process_ioc_facts(s, (MPIMsgIOCFacts *)req); - break; - - case MPI_FUNCTION_PORT_FACTS: - mptsas_process_port_facts(s, (MPIMsgPortFacts *)req); - break; - - case MPI_FUNCTION_PORT_ENABLE: - mptsas_process_port_enable(s, (MPIMsgPortEnable *)req); - break; - - case MPI_FUNCTION_EVENT_NOTIFICATION: - mptsas_process_event_notification(s, (MPIMsgEventNotify *)req); - break; - - case MPI_FUNCTION_CONFIG: - mptsas_process_config(s, (MPIMsgConfig *)req); - break; - - default: - trace_mptsas_unhandled_cmd(s, req->Function, 0); - mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_FUNCTION); - break; - } -} - -static void mptsas_fetch_request(MPTSASState *s) -{ - PCIDevice *pci = (PCIDevice *) s; - char req[MPTSAS_MAX_REQUEST_SIZE]; - MPIRequestHeader *hdr = (MPIRequestHeader *)req; - hwaddr addr; - int size; - - if (s->state != MPI_IOC_STATE_OPERATIONAL) { - mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE); - return; - } - - /* Read the message header from the guest first. */ - addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post); - pci_dma_read(pci, addr, req, sizeof(hdr)); - - if (hdr->Function < ARRAY_SIZE(mpi_request_sizes) && - mpi_request_sizes[hdr->Function]) { - /* Read the rest of the request based on the type. Do not - * reread everything, as that could cause a TOC/TOU mismatch - * and leak data from the QEMU stack. - */ - size = mpi_request_sizes[hdr->Function]; - assert(size <= MPTSAS_MAX_REQUEST_SIZE); - pci_dma_read(pci, addr + sizeof(hdr), &req[sizeof(hdr)], - size - sizeof(hdr)); - } - - if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) { - /* SCSI I/O requests are separate from mptsas_process_message - * because they cannot be sent through the doorbell yet. - */ - mptsas_process_scsi_io_request(s, (MPIMsgSCSIIORequest *)req, addr); - } else { - mptsas_process_message(s, (MPIRequestHeader *)req); - } -} - -static void mptsas_fetch_requests(void *opaque) -{ - MPTSASState *s = opaque; - - while (!MPTSAS_FIFO_EMPTY(s, request_post)) { - mptsas_fetch_request(s); - } -} - -static void mptsas_soft_reset(MPTSASState *s) -{ - uint32_t save_mask; - - trace_mptsas_reset(s); - - /* Temporarily disable interrupts */ - save_mask = s->intr_mask; - s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM; - mptsas_update_interrupt(s); - - qbus_reset_all(&s->bus.qbus); - s->intr_status = 0; - s->intr_mask = save_mask; - - s->reply_free_tail = 0; - s->reply_free_head = 0; - s->reply_post_tail = 0; - s->reply_post_head = 0; - s->request_post_tail = 0; - s->request_post_head = 0; - qemu_bh_cancel(s->request_bh); - - s->state = MPI_IOC_STATE_READY; -} - -static uint32_t mptsas_doorbell_read(MPTSASState *s) -{ - uint32_t ret; - - ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_MASK; - ret |= s->state; - switch (s->doorbell_state) { - case DOORBELL_NONE: - break; - - case DOORBELL_WRITE: - ret |= MPI_DOORBELL_ACTIVE; - break; - - case DOORBELL_READ: - /* Get rid of the IOC fault code. */ - ret &= ~MPI_DOORBELL_DATA_MASK; - - assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT); - assert(s->doorbell_reply_idx <= s->doorbell_reply_size); - - ret |= MPI_DOORBELL_ACTIVE; - if (s->doorbell_reply_idx < s->doorbell_reply_size) { - /* For more information about this endian switch, see the - * commit message for commit 36b62ae ("fw_cfg: fix endianness in - * fw_cfg_data_mem_read() / _write()", 2015-01-16). - */ - ret |= le16_to_cpu(s->doorbell_reply[s->doorbell_reply_idx++]); - } - break; - - default: - abort(); - } - - return ret; -} - -static void mptsas_doorbell_write(MPTSASState *s, uint32_t val) -{ - if (s->doorbell_state == DOORBELL_WRITE) { - if (s->doorbell_idx < s->doorbell_cnt) { - /* For more information about this endian switch, see the - * commit message for commit 36b62ae ("fw_cfg: fix endianness in - * fw_cfg_data_mem_read() / _write()", 2015-01-16). - */ - s->doorbell_msg[s->doorbell_idx++] = cpu_to_le32(val); - if (s->doorbell_idx == s->doorbell_cnt) { - mptsas_process_message(s, (MPIRequestHeader *)s->doorbell_msg); - } - } - return; - } - - switch ((val & MPI_DOORBELL_FUNCTION_MASK) >> MPI_DOORBELL_FUNCTION_SHIFT) { - case MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET: - mptsas_soft_reset(s); - break; - case MPI_FUNCTION_IO_UNIT_RESET: - break; - case MPI_FUNCTION_HANDSHAKE: - s->doorbell_state = DOORBELL_WRITE; - s->doorbell_idx = 0; - s->doorbell_cnt = (val & MPI_DOORBELL_ADD_DWORDS_MASK) - >> MPI_DOORBELL_ADD_DWORDS_SHIFT; - s->intr_status |= MPI_HIS_DOORBELL_INTERRUPT; - mptsas_update_interrupt(s); - break; - default: - trace_mptsas_unhandled_doorbell_cmd(s, val); - break; - } -} - -static void mptsas_write_sequence_write(MPTSASState *s, uint32_t val) -{ - /* If the diagnostic register is enabled, any write to this register - * will disable it. Otherwise, the guest has to do a magic five-write - * sequence. - */ - if (s->diagnostic & MPI_DIAG_DRWE) { - goto disable; - } - - switch (s->diagnostic_idx) { - case 0: - if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_1ST_KEY_VALUE) { - goto disable; - } - break; - case 1: - if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_2ND_KEY_VALUE) { - goto disable; - } - break; - case 2: - if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_3RD_KEY_VALUE) { - goto disable; - } - break; - case 3: - if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_4TH_KEY_VALUE) { - goto disable; - } - break; - case 4: - if ((val & MPI_WRSEQ_KEY_VALUE_MASK) != MPI_WRSEQ_5TH_KEY_VALUE) { - goto disable; - } - /* Prepare Spaceball One for departure, and change the - * combination on my luggage! - */ - s->diagnostic |= MPI_DIAG_DRWE; - break; - } - s->diagnostic_idx++; - return; - -disable: - s->diagnostic &= ~MPI_DIAG_DRWE; - s->diagnostic_idx = 0; -} - -static int mptsas_hard_reset(MPTSASState *s) -{ - mptsas_soft_reset(s); - - s->intr_mask = MPI_HIM_DIM | MPI_HIM_RIM; - - s->host_mfa_high_addr = 0; - s->sense_buffer_high_addr = 0; - s->reply_frame_size = 0; - s->max_devices = MPTSAS_NUM_PORTS; - s->max_buses = 1; - - return 0; -} - -static void mptsas_interrupt_status_write(MPTSASState *s) -{ - switch (s->doorbell_state) { - case DOORBELL_NONE: - case DOORBELL_WRITE: - s->intr_status &= ~MPI_HIS_DOORBELL_INTERRUPT; - break; - - case DOORBELL_READ: - /* The reply can be read continuously, so leave the interrupt up. */ - assert(s->intr_status & MPI_HIS_DOORBELL_INTERRUPT); - if (s->doorbell_reply_idx == s->doorbell_reply_size) { - s->doorbell_state = DOORBELL_NONE; - } - break; - - default: - abort(); - } - mptsas_update_interrupt(s); -} - -static uint32_t mptsas_reply_post_read(MPTSASState *s) -{ - uint32_t ret; - - if (!MPTSAS_FIFO_EMPTY(s, reply_post)) { - ret = MPTSAS_FIFO_GET(s, reply_post); - } else { - ret = -1; - s->intr_status &= ~MPI_HIS_REPLY_MESSAGE_INTERRUPT; - mptsas_update_interrupt(s); - } - - return ret; -} - -static uint64_t mptsas_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - MPTSASState *s = opaque; - uint32_t ret = 0; - - switch (addr & ~3) { - case MPI_DOORBELL_OFFSET: - ret = mptsas_doorbell_read(s); - break; - - case MPI_DIAGNOSTIC_OFFSET: - ret = s->diagnostic; - break; - - case MPI_HOST_INTERRUPT_STATUS_OFFSET: - ret = s->intr_status; - break; - - case MPI_HOST_INTERRUPT_MASK_OFFSET: - ret = s->intr_mask; - break; - - case MPI_REPLY_POST_FIFO_OFFSET: - ret = mptsas_reply_post_read(s); - break; - - default: - trace_mptsas_mmio_unhandled_read(s, addr); - break; - } - trace_mptsas_mmio_read(s, addr, ret); - return ret; -} - -static void mptsas_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MPTSASState *s = opaque; - - trace_mptsas_mmio_write(s, addr, val); - switch (addr) { - case MPI_DOORBELL_OFFSET: - mptsas_doorbell_write(s, val); - break; - - case MPI_WRITE_SEQUENCE_OFFSET: - mptsas_write_sequence_write(s, val); - break; - - case MPI_DIAGNOSTIC_OFFSET: - if (val & MPI_DIAG_RESET_ADAPTER) { - mptsas_hard_reset(s); - } - break; - - case MPI_HOST_INTERRUPT_STATUS_OFFSET: - mptsas_interrupt_status_write(s); - break; - - case MPI_HOST_INTERRUPT_MASK_OFFSET: - s->intr_mask = val & (MPI_HIM_RIM | MPI_HIM_DIM); - mptsas_update_interrupt(s); - break; - - case MPI_REQUEST_POST_FIFO_OFFSET: - if (MPTSAS_FIFO_FULL(s, request_post)) { - mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); - } else { - MPTSAS_FIFO_PUT(s, request_post, val & ~0x03); - qemu_bh_schedule(s->request_bh); - } - break; - - case MPI_REPLY_FREE_FIFO_OFFSET: - if (MPTSAS_FIFO_FULL(s, reply_free)) { - mptsas_set_fault(s, MPI_IOCSTATUS_INSUFFICIENT_RESOURCES); - } else { - MPTSAS_FIFO_PUT(s, reply_free, val); - } - break; - - default: - trace_mptsas_mmio_unhandled_write(s, addr, val); - break; - } -} - -static const MemoryRegionOps mptsas_mmio_ops = { - .read = mptsas_mmio_read, - .write = mptsas_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -static const MemoryRegionOps mptsas_port_ops = { - .read = mptsas_mmio_read, - .write = mptsas_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -static uint64_t mptsas_diag_read(void *opaque, hwaddr addr, - unsigned size) -{ - MPTSASState *s = opaque; - trace_mptsas_diag_read(s, addr, 0); - return 0; -} - -static void mptsas_diag_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MPTSASState *s = opaque; - trace_mptsas_diag_write(s, addr, val); -} - -static const MemoryRegionOps mptsas_diag_ops = { - .read = mptsas_diag_read, - .write = mptsas_diag_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -static QEMUSGList *mptsas_get_sg_list(SCSIRequest *sreq) -{ - MPTSASRequest *req = sreq->hba_private; - - return &req->qsg; -} - -static void mptsas_command_complete(SCSIRequest *sreq, - uint32_t status, size_t resid) -{ - MPTSASRequest *req = sreq->hba_private; - MPTSASState *s = req->dev; - uint8_t sense_buf[SCSI_SENSE_BUF_SIZE]; - uint8_t sense_len; - - hwaddr sense_buffer_addr = req->dev->sense_buffer_high_addr | - req->scsi_io.SenseBufferLowAddr; - - trace_mptsas_command_complete(s, req->scsi_io.MsgContext, status, resid); - - sense_len = scsi_req_get_sense(sreq, sense_buf, SCSI_SENSE_BUF_SIZE); - if (sense_len > 0) { - pci_dma_write(PCI_DEVICE(s), sense_buffer_addr, sense_buf, - MIN(req->scsi_io.SenseBufferLength, sense_len)); - } - - if (sreq->status != GOOD || resid || - req->dev->doorbell_state == DOORBELL_WRITE) { - MPIMsgSCSIIOReply reply; - - memset(&reply, 0, sizeof(reply)); - reply.TargetID = req->scsi_io.TargetID; - reply.Bus = req->scsi_io.Bus; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->scsi_io.Function; - reply.CDBLength = req->scsi_io.CDBLength; - reply.SenseBufferLength = req->scsi_io.SenseBufferLength; - reply.MsgFlags = req->scsi_io.MsgFlags; - reply.MsgContext = req->scsi_io.MsgContext; - reply.SCSIStatus = sreq->status; - if (sreq->status == GOOD) { - reply.TransferCount = req->scsi_io.DataLength - resid; - if (resid) { - reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN; - } - } else { - reply.SCSIState = MPI_SCSI_STATE_AUTOSENSE_VALID; - reply.SenseCount = sense_len; - reply.IOCStatus = MPI_IOCSTATUS_SCSI_DATA_UNDERRUN; - } - - mptsas_fix_scsi_io_reply_endianness(&reply); - mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply); - } else { - mptsas_turbo_reply(req->dev, req->scsi_io.MsgContext); - } - - mptsas_free_request(req); -} - -static void mptsas_request_cancelled(SCSIRequest *sreq) -{ - MPTSASRequest *req = sreq->hba_private; - MPIMsgSCSIIOReply reply; - - memset(&reply, 0, sizeof(reply)); - reply.TargetID = req->scsi_io.TargetID; - reply.Bus = req->scsi_io.Bus; - reply.MsgLength = sizeof(reply) / 4; - reply.Function = req->scsi_io.Function; - reply.CDBLength = req->scsi_io.CDBLength; - reply.SenseBufferLength = req->scsi_io.SenseBufferLength; - reply.MsgFlags = req->scsi_io.MsgFlags; - reply.MsgContext = req->scsi_io.MsgContext; - reply.SCSIState = MPI_SCSI_STATE_NO_SCSI_STATUS; - reply.IOCStatus = MPI_IOCSTATUS_SCSI_TASK_TERMINATED; - - mptsas_fix_scsi_io_reply_endianness(&reply); - mptsas_post_reply(req->dev, (MPIDefaultReply *)&reply); - mptsas_free_request(req); -} - -static void mptsas_save_request(QEMUFile *f, SCSIRequest *sreq) -{ - MPTSASRequest *req = sreq->hba_private; - int i; - - qemu_put_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io)); - qemu_put_be32(f, req->qsg.nsg); - for (i = 0; i < req->qsg.nsg; i++) { - qemu_put_be64(f, req->qsg.sg[i].base); - qemu_put_be64(f, req->qsg.sg[i].len); - } -} - -static void *mptsas_load_request(QEMUFile *f, SCSIRequest *sreq) -{ - SCSIBus *bus = sreq->bus; - MPTSASState *s = container_of(bus, MPTSASState, bus); - PCIDevice *pci = PCI_DEVICE(s); - MPTSASRequest *req; - int i, n; - - req = g_new(MPTSASRequest, 1); - qemu_get_buffer(f, (unsigned char *)&req->scsi_io, sizeof(req->scsi_io)); - - n = qemu_get_be32(f); - /* TODO: add a way for SCSIBusInfo's load_request to fail, - * and fail migration instead of asserting here. - * When we do, we might be able to re-enable NDEBUG below. - */ -#ifdef NDEBUG -#error building with NDEBUG is not supported -#endif - assert(n >= 0); - - pci_dma_sglist_init(&req->qsg, pci, n); - for (i = 0; i < n; i++) { - uint64_t base = qemu_get_be64(f); - uint64_t len = qemu_get_be64(f); - qemu_sglist_add(&req->qsg, base, len); - } - - scsi_req_ref(sreq); - req->sreq = sreq; - req->dev = s; - - return req; -} - -static const struct SCSIBusInfo mptsas_scsi_info = { - .tcq = true, - .max_target = MPTSAS_NUM_PORTS, - .max_lun = 1, - - .get_sg_list = mptsas_get_sg_list, - .complete = mptsas_command_complete, - .cancel = mptsas_request_cancelled, - .save_request = mptsas_save_request, - .load_request = mptsas_load_request, -}; - -static void mptsas_scsi_init(PCIDevice *dev, Error **errp) -{ - DeviceState *d = DEVICE(dev); - MPTSASState *s = MPT_SAS(dev); - - dev->config[PCI_LATENCY_TIMER] = 0; - dev->config[PCI_INTERRUPT_PIN] = 0x01; - - memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s, - "mptsas-mmio", 0x4000); - memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s, - "mptsas-io", 256); - memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s, - "mptsas-diag", 0x10000); - - if (s->msi_available && - msi_init(dev, 0, 1, true, false) >= 0) { - s->msi_in_use = true; - } - - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_32, &s->diag_io); - - 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); - } - s->max_devices = MPTSAS_NUM_PORTS; - - s->request_bh = qemu_bh_new(mptsas_fetch_requests, s); - - QTAILQ_INIT(&s->pending); - - scsi_bus_new(&s->bus, sizeof(s->bus), &dev->qdev, &mptsas_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static void mptsas_scsi_uninit(PCIDevice *dev) -{ - MPTSASState *s = MPT_SAS(dev); - - qemu_bh_delete(s->request_bh); - if (s->msi_in_use) { - msi_uninit(dev); - } -} - -static void mptsas_reset(DeviceState *dev) -{ - MPTSASState *s = MPT_SAS(dev); - - mptsas_hard_reset(s); -} - -static int mptsas_post_load(void *opaque, int version_id) -{ - MPTSASState *s = opaque; - - if (s->doorbell_idx > s->doorbell_cnt || - s->doorbell_cnt > ARRAY_SIZE(s->doorbell_msg) || - s->doorbell_reply_idx > s->doorbell_reply_size || - s->doorbell_reply_size > ARRAY_SIZE(s->doorbell_reply) || - MPTSAS_FIFO_INVALID(s, request_post) || - MPTSAS_FIFO_INVALID(s, reply_post) || - MPTSAS_FIFO_INVALID(s, reply_free) || - s->diagnostic_idx > 4) { - return -EINVAL; - } - - return 0; -} - -static const VMStateDescription vmstate_mptsas = { - .name = "mptsas", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .post_load = mptsas_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, MPTSASState), - VMSTATE_BOOL(msi_in_use, MPTSASState), - - VMSTATE_UINT32(state, MPTSASState), - VMSTATE_UINT8(who_init, MPTSASState), - VMSTATE_UINT8(doorbell_state, MPTSASState), - VMSTATE_UINT32_ARRAY(doorbell_msg, MPTSASState, 256), - VMSTATE_INT32(doorbell_idx, MPTSASState), - VMSTATE_INT32(doorbell_cnt, MPTSASState), - - VMSTATE_UINT16_ARRAY(doorbell_reply, MPTSASState, 256), - VMSTATE_INT32(doorbell_reply_idx, MPTSASState), - VMSTATE_INT32(doorbell_reply_size, MPTSASState), - - VMSTATE_UINT32(diagnostic, MPTSASState), - VMSTATE_UINT8(diagnostic_idx, MPTSASState), - - VMSTATE_UINT32(intr_status, MPTSASState), - VMSTATE_UINT32(intr_mask, MPTSASState), - - VMSTATE_UINT32_ARRAY(request_post, MPTSASState, - MPTSAS_REQUEST_QUEUE_DEPTH + 1), - VMSTATE_UINT16(request_post_head, MPTSASState), - VMSTATE_UINT16(request_post_tail, MPTSASState), - - VMSTATE_UINT32_ARRAY(reply_post, MPTSASState, - MPTSAS_REPLY_QUEUE_DEPTH + 1), - VMSTATE_UINT16(reply_post_head, MPTSASState), - VMSTATE_UINT16(reply_post_tail, MPTSASState), - - VMSTATE_UINT32_ARRAY(reply_free, MPTSASState, - MPTSAS_REPLY_QUEUE_DEPTH + 1), - VMSTATE_UINT16(reply_free_head, MPTSASState), - VMSTATE_UINT16(reply_free_tail, MPTSASState), - - VMSTATE_UINT16(max_buses, MPTSASState), - VMSTATE_UINT16(max_devices, MPTSASState), - VMSTATE_UINT16(reply_frame_size, MPTSASState), - VMSTATE_UINT64(host_mfa_high_addr, MPTSASState), - VMSTATE_UINT64(sense_buffer_high_addr, MPTSASState), - VMSTATE_END_OF_LIST() - } -}; - -static Property mptsas_properties[] = { - DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), - /* TODO: test MSI support under Windows */ - DEFINE_PROP_BIT("msi", MPTSASState, msi_available, 0, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mptsas1068_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); - - pc->realize = mptsas_scsi_init; - pc->exit = mptsas_scsi_uninit; - pc->romfile = 0; - pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->device_id = PCI_DEVICE_ID_LSI_SAS1068; - pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->subsystem_id = 0x8000; - pc->class_id = PCI_CLASS_STORAGE_SCSI; - dc->props = mptsas_properties; - dc->reset = mptsas_reset; - dc->vmsd = &vmstate_mptsas; - dc->desc = "LSI SAS 1068"; -} - -static const TypeInfo mptsas_info = { - .name = TYPE_MPTSAS1068, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(MPTSASState), - .class_init = mptsas1068_class_init, -}; - -static void mptsas_register_types(void) -{ - type_register(&mptsas_info); -} - -type_init(mptsas_register_types) diff --git a/qemu/hw/scsi/mptsas.h b/qemu/hw/scsi/mptsas.h deleted file mode 100644 index 595f81fb5..000000000 --- a/qemu/hw/scsi/mptsas.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef MPTSAS_H -#define MPTSAS_H - -#include "mpi.h" - -#define MPTSAS_NUM_PORTS 8 -#define MPTSAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ - -#define MPTSAS_REQUEST_QUEUE_DEPTH 128 -#define MPTSAS_REPLY_QUEUE_DEPTH 128 - -#define MPTSAS_MAXIMUM_CHAIN_DEPTH 0x22 - -typedef struct MPTSASState MPTSASState; -typedef struct MPTSASRequest MPTSASRequest; - -enum { - DOORBELL_NONE, - DOORBELL_WRITE, - DOORBELL_READ -}; - -struct MPTSASState { - PCIDevice dev; - MemoryRegion mmio_io; - MemoryRegion port_io; - MemoryRegion diag_io; - QEMUBH *request_bh; - - uint32_t msi_available; - uint64_t sas_addr; - - bool msi_in_use; - - /* Doorbell register */ - uint32_t state; - uint8_t who_init; - uint8_t doorbell_state; - - /* Buffer for requests that are sent through the doorbell register. */ - uint32_t doorbell_msg[256]; - int doorbell_idx; - int doorbell_cnt; - - uint16_t doorbell_reply[256]; - int doorbell_reply_idx; - int doorbell_reply_size; - - /* Other registers */ - uint8_t diagnostic_idx; - uint32_t diagnostic; - uint32_t intr_mask; - uint32_t intr_status; - - /* Request queues */ - uint32_t request_post[MPTSAS_REQUEST_QUEUE_DEPTH + 1]; - uint16_t request_post_head; - uint16_t request_post_tail; - - uint32_t reply_post[MPTSAS_REPLY_QUEUE_DEPTH + 1]; - uint16_t reply_post_head; - uint16_t reply_post_tail; - - uint32_t reply_free[MPTSAS_REPLY_QUEUE_DEPTH + 1]; - uint16_t reply_free_head; - uint16_t reply_free_tail; - - /* IOC Facts */ - hwaddr host_mfa_high_addr; - hwaddr sense_buffer_high_addr; - uint16_t max_devices; - uint16_t max_buses; - uint16_t reply_frame_size; - - SCSIBus bus; - QTAILQ_HEAD(, MPTSASRequest) pending; -}; - -void mptsas_fix_scsi_io_endianness(MPIMsgSCSIIORequest *req); -void mptsas_fix_scsi_io_reply_endianness(MPIMsgSCSIIOReply *reply); -void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req); -void mptsas_fix_scsi_task_mgmt_reply_endianness(MPIMsgSCSITaskMgmtReply *reply); -void mptsas_fix_ioc_init_endianness(MPIMsgIOCInit *req); -void mptsas_fix_ioc_init_reply_endianness(MPIMsgIOCInitReply *reply); -void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req); -void mptsas_fix_ioc_facts_reply_endianness(MPIMsgIOCFactsReply *reply); -void mptsas_fix_config_endianness(MPIMsgConfig *req); -void mptsas_fix_config_reply_endianness(MPIMsgConfigReply *reply); -void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req); -void mptsas_fix_port_facts_reply_endianness(MPIMsgPortFactsReply *reply); -void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req); -void mptsas_fix_port_enable_reply_endianness(MPIMsgPortEnableReply *reply); -void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req); -void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply); - -void mptsas_reply(MPTSASState *s, MPIDefaultReply *reply); - -void mptsas_process_config(MPTSASState *s, MPIMsgConfig *req); - -#endif /* MPTSAS_H */ diff --git a/qemu/hw/scsi/scsi-bus.c b/qemu/hw/scsi/scsi-bus.c deleted file mode 100644 index ad6f398c3..000000000 --- a/qemu/hw/scsi/scsi-bus.c +++ /dev/null @@ -1,2062 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "hw/qdev.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "trace.h" -#include "sysemu/dma.h" -#include "qemu/cutils.h" - -static char *scsibus_get_dev_path(DeviceState *dev); -static char *scsibus_get_fw_dev_path(DeviceState *dev); -static void scsi_req_dequeue(SCSIRequest *req); -static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len); -static void scsi_target_free_buf(SCSIRequest *req); - -static Property scsi_props[] = { - DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), - DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), - DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - k->get_dev_path = scsibus_get_dev_path; - k->get_fw_dev_path = scsibus_get_fw_dev_path; - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo scsi_bus_info = { - .name = TYPE_SCSI_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(SCSIBus), - .class_init = scsi_bus_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; -static int next_scsi_bus; - -static void scsi_device_realize(SCSIDevice *s, Error **errp) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->realize) { - sc->realize(s, errp); - } -} - -int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, - void *hba_private) -{ - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - int rc; - - assert(cmd->len == 0); - rc = scsi_req_parse_cdb(dev, cmd, buf); - if (bus->info->parse_cdb) { - rc = bus->info->parse_cdb(dev, cmd, buf, hba_private); - } - return rc; -} - -static SCSIRequest *scsi_device_alloc_req(SCSIDevice *s, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->alloc_req) { - return sc->alloc_req(s, tag, lun, buf, hba_private); - } - - return NULL; -} - -void scsi_device_unit_attention_reported(SCSIDevice *s) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->unit_attention_reported) { - sc->unit_attention_reported(s); - } -} - -/* Create a scsi bus, and attach devices to it. */ -void scsi_bus_new(SCSIBus *bus, size_t bus_size, DeviceState *host, - const SCSIBusInfo *info, const char *bus_name) -{ - qbus_create_inplace(bus, bus_size, TYPE_SCSI_BUS, host, bus_name); - bus->busnr = next_scsi_bus++; - bus->info = info; - qbus_set_bus_hotplug_handler(BUS(bus), &error_abort); -} - -static void scsi_dma_restart_bh(void *opaque) -{ - SCSIDevice *s = opaque; - SCSIRequest *req, *next; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { - scsi_req_ref(req); - if (req->retry) { - req->retry = false; - switch (req->cmd.mode) { - case SCSI_XFER_FROM_DEV: - case SCSI_XFER_TO_DEV: - scsi_req_continue(req); - break; - case SCSI_XFER_NONE: - scsi_req_dequeue(req); - scsi_req_enqueue(req); - break; - } - } - scsi_req_unref(req); - } -} - -void scsi_req_retry(SCSIRequest *req) -{ - /* No need to save a reference, because scsi_dma_restart_bh just - * looks at the request list. */ - req->retry = true; -} - -static void scsi_dma_restart_cb(void *opaque, int running, RunState state) -{ - SCSIDevice *s = opaque; - - if (!running) { - return; - } - if (!s->bh) { - AioContext *ctx = blk_get_aio_context(s->conf.blk); - s->bh = aio_bh_new(ctx, scsi_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - -static void scsi_qdev_realize(DeviceState *qdev, Error **errp) -{ - SCSIDevice *dev = SCSI_DEVICE(qdev); - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - SCSIDevice *d; - Error *local_err = NULL; - - if (dev->channel > bus->info->max_channel) { - error_setg(errp, "bad scsi channel id: %d", dev->channel); - return; - } - if (dev->id != -1 && dev->id > bus->info->max_target) { - error_setg(errp, "bad scsi device id: %d", dev->id); - return; - } - if (dev->lun != -1 && dev->lun > bus->info->max_lun) { - error_setg(errp, "bad scsi device lun: %d", dev->lun); - return; - } - - if (dev->id == -1) { - int id = -1; - if (dev->lun == -1) { - dev->lun = 0; - } - do { - d = scsi_device_find(bus, dev->channel, ++id, dev->lun); - } while (d && d->lun == dev->lun && id < bus->info->max_target); - if (d && d->lun == dev->lun) { - error_setg(errp, "no free target"); - return; - } - dev->id = id; - } else if (dev->lun == -1) { - int lun = -1; - do { - d = scsi_device_find(bus, dev->channel, dev->id, ++lun); - } while (d && d->lun == lun && lun < bus->info->max_lun); - if (d && d->lun == lun) { - error_setg(errp, "no free lun"); - return; - } - dev->lun = lun; - } else { - d = scsi_device_find(bus, dev->channel, dev->id, dev->lun); - assert(d); - if (d->lun == dev->lun && dev != d) { - error_setg(errp, "lun already used by '%s'", d->qdev.id); - return; - } - } - - QTAILQ_INIT(&dev->requests); - scsi_device_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, - dev); -} - -static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) -{ - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->vmsentry) { - qemu_del_vm_change_state_handler(dev->vmsentry); - } - - scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE)); - blockdev_mark_auto_del(dev->conf.blk); -} - -/* handle legacy '-drive if=scsi,...' cmd line args */ -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, - int unit, bool removable, int bootindex, - const char *serial, Error **errp) -{ - const char *driver; - char *name; - DeviceState *dev; - Error *err = NULL; - - driver = blk_is_sg(blk) ? "scsi-generic" : "scsi-disk"; - dev = qdev_create(&bus->qbus, driver); - name = g_strdup_printf("legacy[%d]", unit); - object_property_add_child(OBJECT(bus), name, OBJECT(dev), NULL); - g_free(name); - - qdev_prop_set_uint32(dev, "scsi-id", unit); - if (bootindex >= 0) { - object_property_set_int(OBJECT(dev), bootindex, "bootindex", - &error_abort); - } - if (object_property_find(OBJECT(dev), "removable", NULL)) { - qdev_prop_set_bit(dev, "removable", removable); - } - if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { - qdev_prop_set_string(dev, "serial", serial); - } - qdev_prop_set_drive(dev, "drive", blk, &err); - if (err) { - error_propagate(errp, err); - object_unparent(OBJECT(dev)); - return NULL; - } - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - object_unparent(OBJECT(dev)); - return NULL; - } - return SCSI_DEVICE(dev); -} - -void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp) -{ - Location loc; - DriveInfo *dinfo; - int unit; - Error *err = NULL; - - loc_push_none(&loc); - for (unit = 0; unit <= bus->info->max_target; unit++) { - dinfo = drive_get(IF_SCSI, bus->busnr, unit); - if (dinfo == NULL) { - continue; - } - qemu_opts_loc_restore(dinfo->opts); - scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), - unit, false, -1, NULL, &err); - if (err != NULL) { - error_propagate(errp, err); - break; - } - } - loc_pop(&loc); -} - -static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf) -{ - scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; -} - -static const struct SCSIReqOps reqops_invalid_field = { - .size = sizeof(SCSIRequest), - .send_command = scsi_invalid_field -}; - -/* SCSIReqOps implementation for invalid commands. */ - -static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf) -{ - scsi_req_build_sense(req, SENSE_CODE(INVALID_OPCODE)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; -} - -static const struct SCSIReqOps reqops_invalid_opcode = { - .size = sizeof(SCSIRequest), - .send_command = scsi_invalid_command -}; - -/* SCSIReqOps implementation for unit attention conditions. */ - -static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf) -{ - if (req->dev->unit_attention.key == UNIT_ATTENTION) { - scsi_req_build_sense(req, req->dev->unit_attention); - } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { - scsi_req_build_sense(req, req->bus->unit_attention); - } - scsi_req_complete(req, CHECK_CONDITION); - return 0; -} - -static const struct SCSIReqOps reqops_unit_attention = { - .size = sizeof(SCSIRequest), - .send_command = scsi_unit_attention -}; - -/* SCSIReqOps implementation for REPORT LUNS and for commands sent to - an invalid LUN. */ - -typedef struct SCSITargetReq SCSITargetReq; - -struct SCSITargetReq { - SCSIRequest req; - int len; - uint8_t *buf; - int buf_len; -}; - -static void store_lun(uint8_t *outbuf, int lun) -{ - if (lun < 256) { - outbuf[1] = lun; - return; - } - outbuf[1] = (lun & 255); - outbuf[0] = (lun >> 8) | 0x40; -} - -static bool scsi_target_emulate_report_luns(SCSITargetReq *r) -{ - BusChild *kid; - int i, len, n; - int channel, id; - bool found_lun0; - - if (r->req.cmd.xfer < 16) { - return false; - } - if (r->req.cmd.buf[2] > 2) { - return false; - } - channel = r->req.dev->channel; - id = r->req.dev->id; - found_lun0 = false; - n = 0; - QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->channel == channel && dev->id == id) { - if (dev->lun == 0) { - found_lun0 = true; - } - n += 8; - } - } - if (!found_lun0) { - n += 8; - } - - scsi_target_alloc_buf(&r->req, n + 8); - - len = MIN(n + 8, r->req.cmd.xfer & ~7); - memset(r->buf, 0, len); - stl_be_p(&r->buf[0], n); - i = found_lun0 ? 8 : 16; - QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->channel == channel && dev->id == id) { - store_lun(&r->buf[i], dev->lun); - i += 8; - } - } - assert(i == n + 8); - r->len = len; - return true; -} - -static bool scsi_target_emulate_inquiry(SCSITargetReq *r) -{ - assert(r->req.dev->lun != r->req.lun); - - scsi_target_alloc_buf(&r->req, SCSI_INQUIRY_LEN); - - if (r->req.cmd.buf[1] & 0x2) { - /* Command support data - optional, not implemented */ - return false; - } - - if (r->req.cmd.buf[1] & 0x1) { - /* Vital product data */ - uint8_t page_code = r->req.cmd.buf[2]; - r->buf[r->len++] = page_code ; /* this page */ - r->buf[r->len++] = 0x00; - - switch (page_code) { - case 0x00: /* Supported page codes, mandatory */ - { - int pages; - pages = r->len++; - r->buf[r->len++] = 0x00; /* list of supported pages (this page) */ - r->buf[pages] = r->len - pages - 1; /* number of pages */ - break; - } - default: - return false; - } - /* done with EVPD */ - assert(r->len < r->buf_len); - r->len = MIN(r->req.cmd.xfer, r->len); - return true; - } - - /* Standard INQUIRY data */ - if (r->req.cmd.buf[2] != 0) { - return false; - } - - /* PAGE CODE == 0 */ - r->len = MIN(r->req.cmd.xfer, SCSI_INQUIRY_LEN); - memset(r->buf, 0, r->len); - if (r->req.lun != 0) { - r->buf[0] = TYPE_NO_LUN; - } else { - r->buf[0] = TYPE_NOT_PRESENT | TYPE_INACTIVE; - r->buf[2] = 5; /* Version */ - r->buf[3] = 2 | 0x10; /* HiSup, response data format */ - r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */ - r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */ - memcpy(&r->buf[8], "QEMU ", 8); - memcpy(&r->buf[16], "QEMU TARGET ", 16); - pstrcpy((char *) &r->buf[32], 4, qemu_hw_version()); - } - return true; -} - -static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - switch (buf[0]) { - case REPORT_LUNS: - if (!scsi_target_emulate_report_luns(r)) { - goto illegal_request; - } - break; - case INQUIRY: - if (!scsi_target_emulate_inquiry(r)) { - goto illegal_request; - } - break; - case REQUEST_SENSE: - scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN); - r->len = scsi_device_get_sense(r->req.dev, r->buf, - MIN(req->cmd.xfer, r->buf_len), - (req->cmd.buf[1] & 1) == 0); - if (r->req.dev->sense_is_ua) { - scsi_device_unit_attention_reported(req->dev); - r->req.dev->sense_len = 0; - r->req.dev->sense_is_ua = false; - } - break; - case TEST_UNIT_READY: - break; - default: - scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; - illegal_request: - scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; - } - - if (!r->len) { - scsi_req_complete(req, GOOD); - } - return r->len; -} - -static void scsi_target_read_data(SCSIRequest *req) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - uint32_t n; - - n = r->len; - if (n > 0) { - r->len = 0; - scsi_req_data(&r->req, n); - } else { - scsi_req_complete(&r->req, GOOD); - } -} - -static uint8_t *scsi_target_get_buf(SCSIRequest *req) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - return r->buf; -} - -static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - r->buf = g_malloc(len); - r->buf_len = len; - - return r->buf; -} - -static void scsi_target_free_buf(SCSIRequest *req) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - g_free(r->buf); -} - -static const struct SCSIReqOps reqops_target_command = { - .size = sizeof(SCSITargetReq), - .send_command = scsi_target_send_command, - .read_data = scsi_target_read_data, - .get_buf = scsi_target_get_buf, - .free_req = scsi_target_free_buf, -}; - - -SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, - uint32_t tag, uint32_t lun, void *hba_private) -{ - SCSIRequest *req; - SCSIBus *bus = scsi_bus_from_device(d); - BusState *qbus = BUS(bus); - const int memset_off = offsetof(SCSIRequest, sense) - + sizeof(req->sense); - - req = g_malloc(reqops->size); - memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off); - req->refcount = 1; - req->bus = bus; - req->dev = d; - req->tag = tag; - req->lun = lun; - req->hba_private = hba_private; - req->status = -1; - req->ops = reqops; - object_ref(OBJECT(d)); - object_ref(OBJECT(qbus->parent)); - notifier_list_init(&req->cancel_notifiers); - trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); - return req; -} - -SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); - const SCSIReqOps *ops; - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(d); - SCSIRequest *req; - SCSICommand cmd = { .len = 0 }; - int ret; - - if ((d->unit_attention.key == UNIT_ATTENTION || - bus->unit_attention.key == UNIT_ATTENTION) && - (buf[0] != INQUIRY && - buf[0] != REPORT_LUNS && - buf[0] != GET_CONFIGURATION && - buf[0] != GET_EVENT_STATUS_NOTIFICATION && - - /* - * If we already have a pending unit attention condition, - * report this one before triggering another one. - */ - !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) { - ops = &reqops_unit_attention; - } else if (lun != d->lun || - buf[0] == REPORT_LUNS || - (buf[0] == REQUEST_SENSE && d->sense_len)) { - ops = &reqops_target_command; - } else { - ops = NULL; - } - - if (ops != NULL || !sc->parse_cdb) { - ret = scsi_req_parse_cdb(d, &cmd, buf); - } else { - ret = sc->parse_cdb(d, &cmd, buf, hba_private); - } - - if (ret != 0) { - trace_scsi_req_parse_bad(d->id, lun, tag, buf[0]); - req = scsi_req_alloc(&reqops_invalid_opcode, d, tag, lun, hba_private); - } else { - assert(cmd.len != 0); - trace_scsi_req_parsed(d->id, lun, tag, buf[0], - cmd.mode, cmd.xfer); - if (cmd.lba != -1) { - trace_scsi_req_parsed_lba(d->id, lun, tag, buf[0], - cmd.lba); - } - - if (cmd.xfer > INT32_MAX) { - req = scsi_req_alloc(&reqops_invalid_field, d, tag, lun, hba_private); - } else if (ops) { - req = scsi_req_alloc(ops, d, tag, lun, hba_private); - } else { - req = scsi_device_alloc_req(d, tag, lun, buf, hba_private); - } - } - - req->cmd = cmd; - req->resid = req->cmd.xfer; - - switch (buf[0]) { - case INQUIRY: - trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]); - break; - case TEST_UNIT_READY: - trace_scsi_test_unit_ready(d->id, lun, tag); - break; - case REPORT_LUNS: - trace_scsi_report_luns(d->id, lun, tag); - break; - case REQUEST_SENSE: - trace_scsi_request_sense(d->id, lun, tag); - break; - default: - break; - } - - return req; -} - -uint8_t *scsi_req_get_buf(SCSIRequest *req) -{ - return req->ops->get_buf(req); -} - -static void scsi_clear_unit_attention(SCSIRequest *req) -{ - SCSISense *ua; - if (req->dev->unit_attention.key != UNIT_ATTENTION && - req->bus->unit_attention.key != UNIT_ATTENTION) { - return; - } - - /* - * If an INQUIRY command enters the enabled command state, - * the device server shall [not] clear any unit attention condition; - * See also MMC-6, paragraphs 6.5 and 6.6.2. - */ - if (req->cmd.buf[0] == INQUIRY || - req->cmd.buf[0] == GET_CONFIGURATION || - req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) { - return; - } - - if (req->dev->unit_attention.key == UNIT_ATTENTION) { - ua = &req->dev->unit_attention; - } else { - ua = &req->bus->unit_attention; - } - - /* - * If a REPORT LUNS command enters the enabled command state, [...] - * the device server shall clear any pending unit attention condition - * with an additional sense code of REPORTED LUNS DATA HAS CHANGED. - */ - if (req->cmd.buf[0] == REPORT_LUNS && - !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc && - ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) { - return; - } - - *ua = SENSE_CODE(NO_SENSE); -} - -int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) -{ - int ret; - - assert(len >= 14); - if (!req->sense_len) { - return 0; - } - - ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true); - - /* - * FIXME: clearing unit attention conditions upon autosense should be done - * only if the UA_INTLCK_CTRL field in the Control mode page is set to 00b - * (SAM-5, 5.14). - * - * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and - * 10b for HBAs that do not support it (do not call scsi_req_get_sense). - * Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b. - */ - if (req->dev->sense_is_ua) { - scsi_device_unit_attention_reported(req->dev); - req->dev->sense_len = 0; - req->dev->sense_is_ua = false; - } - return ret; -} - -int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) -{ - return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed); -} - -void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) -{ - trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag, - sense.key, sense.asc, sense.ascq); - memset(req->sense, 0, 18); - req->sense[0] = 0x70; - req->sense[2] = sense.key; - req->sense[7] = 10; - req->sense[12] = sense.asc; - req->sense[13] = sense.ascq; - req->sense_len = 18; -} - -static void scsi_req_enqueue_internal(SCSIRequest *req) -{ - assert(!req->enqueued); - scsi_req_ref(req); - if (req->bus->info->get_sg_list) { - req->sg = req->bus->info->get_sg_list(req); - } else { - req->sg = NULL; - } - req->enqueued = true; - QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); -} - -int32_t scsi_req_enqueue(SCSIRequest *req) -{ - int32_t rc; - - assert(!req->retry); - scsi_req_enqueue_internal(req); - scsi_req_ref(req); - rc = req->ops->send_command(req, req->cmd.buf); - scsi_req_unref(req); - return rc; -} - -static void scsi_req_dequeue(SCSIRequest *req) -{ - trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); - req->retry = false; - if (req->enqueued) { - QTAILQ_REMOVE(&req->dev->requests, req, next); - req->enqueued = false; - scsi_req_unref(req); - } -} - -static int scsi_get_performance_length(int num_desc, int type, int data_type) -{ - /* MMC-6, paragraph 6.7. */ - switch (type) { - case 0: - if ((data_type & 3) == 0) { - /* Each descriptor is as in Table 295 - Nominal performance. */ - return 16 * num_desc + 8; - } else { - /* Each descriptor is as in Table 296 - Exceptions. */ - return 6 * num_desc + 8; - } - case 1: - case 4: - case 5: - return 8 * num_desc + 8; - case 2: - return 2048 * num_desc + 8; - case 3: - return 16 * num_desc + 8; - default: - return 8; - } -} - -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(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. */ - default: - xfer = 0; - break; - case 1: - xfer = buf[3]; - break; - case 2: - xfer = buf[4]; - break; - } - - return xfer * unit; -} - -static int ata_passthrough_16_xfer(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. */ - default: - 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; -} - -uint32_t scsi_data_cdb_xfer(uint8_t *buf) -{ - if ((buf[0] >> 5) == 0 && buf[4] == 0) { - return 256; - } else { - return scsi_cdb_xfer(buf); - } -} - -uint32_t scsi_cdb_xfer(uint8_t *buf) -{ - switch (buf[0] >> 5) { - case 0: - return buf[4]; - break; - case 1: - case 2: - return lduw_be_p(&buf[7]); - break; - case 4: - return ldl_be_p(&buf[10]) & 0xffffffffULL; - break; - case 5: - return ldl_be_p(&buf[6]) & 0xffffffffULL; - break; - default: - return -1; - } -} - -static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) -{ - cmd->xfer = scsi_cdb_xfer(buf); - switch (buf[0]) { - case TEST_UNIT_READY: - case REWIND: - case START_STOP: - case SET_CAPACITY: - case WRITE_FILEMARKS: - case WRITE_FILEMARKS_16: - case SPACE: - case RESERVE: - case RELEASE: - case ERASE: - case ALLOW_MEDIUM_REMOVAL: - case SEEK_10: - case SYNCHRONIZE_CACHE: - case SYNCHRONIZE_CACHE_16: - case LOCATE_16: - case LOCK_UNLOCK_CACHE: - case SET_CD_SPEED: - case SET_LIMITS: - case WRITE_LONG_10: - case UPDATE_BLOCK: - case RESERVE_TRACK: - case SET_READ_AHEAD: - case PRE_FETCH: - case PRE_FETCH_16: - case ALLOW_OVERWRITE: - cmd->xfer = 0; - break; - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - if ((buf[1] & 2) == 0) { - cmd->xfer = 0; - } else if ((buf[1] & 4) != 0) { - cmd->xfer = 1; - } - cmd->xfer *= dev->blocksize; - break; - case MODE_SENSE: - break; - case WRITE_SAME_10: - case WRITE_SAME_16: - cmd->xfer = dev->blocksize; - break; - case READ_CAPACITY_10: - cmd->xfer = 8; - break; - case READ_BLOCK_LIMITS: - cmd->xfer = 6; - break; - case SEND_VOLUME_TAG: - /* GPCMD_SET_STREAMING from multimedia commands. */ - if (dev->type == TYPE_ROM) { - cmd->xfer = buf[10] | (buf[9] << 8); - } else { - cmd->xfer = buf[9] | (buf[8] << 8); - } - break; - case WRITE_6: - /* length 0 means 256 blocks */ - if (cmd->xfer == 0) { - cmd->xfer = 256; - } - /* fall through */ - case WRITE_10: - case WRITE_VERIFY_10: - case WRITE_12: - case WRITE_VERIFY_12: - case WRITE_16: - case WRITE_VERIFY_16: - cmd->xfer *= dev->blocksize; - break; - case READ_6: - case READ_REVERSE: - /* length 0 means 256 blocks */ - if (cmd->xfer == 0) { - cmd->xfer = 256; - } - /* fall through */ - case READ_10: - case READ_12: - case READ_16: - cmd->xfer *= dev->blocksize; - break; - case FORMAT_UNIT: - /* MMC mandates the parameter list to be 12-bytes long. Parameters - * for block devices are restricted to the header right now. */ - if (dev->type == TYPE_ROM && (buf[1] & 16)) { - cmd->xfer = 12; - } else { - cmd->xfer = (buf[1] & 16) == 0 ? 0 : (buf[1] & 32 ? 8 : 4); - } - break; - case INQUIRY: - case RECEIVE_DIAGNOSTIC: - case SEND_DIAGNOSTIC: - cmd->xfer = buf[4] | (buf[3] << 8); - break; - case READ_CD: - case READ_BUFFER: - case WRITE_BUFFER: - case SEND_CUE_SHEET: - cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16); - break; - case PERSISTENT_RESERVE_OUT: - cmd->xfer = ldl_be_p(&buf[5]) & 0xffffffffULL; - break; - case ERASE_12: - if (dev->type == TYPE_ROM) { - /* MMC command GET PERFORMANCE. */ - cmd->xfer = scsi_get_performance_length(buf[9] | (buf[8] << 8), - buf[10], buf[1] & 0x1f); - } - break; - case MECHANISM_STATUS: - case READ_DVD_STRUCTURE: - case SEND_DVD_STRUCTURE: - case MAINTENANCE_OUT: - case MAINTENANCE_IN: - if (dev->type == TYPE_ROM) { - /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */ - 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(dev, buf); - } - break; - case ATA_PASSTHROUGH_16: - cmd->xfer = ata_passthrough_16_xfer(dev, buf); - break; - } - return 0; -} - -static int scsi_req_stream_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) -{ - switch (buf[0]) { - /* stream commands */ - case ERASE_12: - case ERASE_16: - cmd->xfer = 0; - break; - case READ_6: - case READ_REVERSE: - case RECOVER_BUFFERED_DATA: - case WRITE_6: - cmd->xfer = buf[4] | (buf[3] << 8) | (buf[2] << 16); - if (buf[1] & 0x01) { /* fixed */ - cmd->xfer *= dev->blocksize; - } - break; - case READ_16: - case READ_REVERSE_16: - case VERIFY_16: - case WRITE_16: - cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16); - if (buf[1] & 0x01) { /* fixed */ - cmd->xfer *= dev->blocksize; - } - break; - case REWIND: - case LOAD_UNLOAD: - cmd->xfer = 0; - break; - case SPACE_16: - cmd->xfer = buf[13] | (buf[12] << 8); - break; - case READ_POSITION: - switch (buf[1] & 0x1f) /* operation code */ { - case SHORT_FORM_BLOCK_ID: - case SHORT_FORM_VENDOR_SPECIFIC: - cmd->xfer = 20; - break; - case LONG_FORM: - cmd->xfer = 32; - break; - case EXTENDED_FORM: - cmd->xfer = buf[8] | (buf[7] << 8); - break; - default: - return -1; - } - - break; - case FORMAT_UNIT: - cmd->xfer = buf[4] | (buf[3] << 8); - break; - /* generic commands */ - default: - return scsi_req_xfer(cmd, dev, buf); - } - return 0; -} - -static int scsi_req_medium_changer_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) -{ - switch (buf[0]) { - /* medium changer commands */ - case EXCHANGE_MEDIUM: - case INITIALIZE_ELEMENT_STATUS: - case INITIALIZE_ELEMENT_STATUS_WITH_RANGE: - case MOVE_MEDIUM: - case POSITION_TO_ELEMENT: - cmd->xfer = 0; - break; - case READ_ELEMENT_STATUS: - cmd->xfer = buf[9] | (buf[8] << 8) | (buf[7] << 16); - break; - - /* generic commands */ - default: - return scsi_req_xfer(cmd, dev, buf); - } - return 0; -} - - -static void scsi_cmd_xfer_mode(SCSICommand *cmd) -{ - if (!cmd->xfer) { - cmd->mode = SCSI_XFER_NONE; - return; - } - switch (cmd->buf[0]) { - case WRITE_6: - case WRITE_10: - case WRITE_VERIFY_10: - case WRITE_12: - case WRITE_VERIFY_12: - case WRITE_16: - case WRITE_VERIFY_16: - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - case COPY: - case COPY_VERIFY: - case COMPARE: - case CHANGE_DEFINITION: - case LOG_SELECT: - case MODE_SELECT: - case MODE_SELECT_10: - case SEND_DIAGNOSTIC: - case WRITE_BUFFER: - case FORMAT_UNIT: - case REASSIGN_BLOCKS: - case SEARCH_EQUAL: - case SEARCH_HIGH: - case SEARCH_LOW: - case UPDATE_BLOCK: - case WRITE_LONG_10: - case WRITE_SAME_10: - case WRITE_SAME_16: - case UNMAP: - case SEARCH_HIGH_12: - case SEARCH_EQUAL_12: - case SEARCH_LOW_12: - case MEDIUM_SCAN: - case SEND_VOLUME_TAG: - case SEND_CUE_SHEET: - case SEND_DVD_STRUCTURE: - case PERSISTENT_RESERVE_OUT: - case MAINTENANCE_OUT: - 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; - } -} - -static uint64_t scsi_cmd_lba(SCSICommand *cmd) -{ - uint8_t *buf = cmd->buf; - uint64_t lba; - - switch (buf[0] >> 5) { - case 0: - lba = ldl_be_p(&buf[0]) & 0x1fffff; - break; - case 1: - case 2: - case 5: - lba = ldl_be_p(&buf[2]) & 0xffffffffULL; - break; - case 4: - lba = ldq_be_p(&buf[2]); - break; - default: - lba = -1; - - } - return lba; -} - -int scsi_cdb_length(uint8_t *buf) { - int cdb_len; - - switch (buf[0] >> 5) { - case 0: - cdb_len = 6; - break; - case 1: - case 2: - cdb_len = 10; - break; - case 4: - cdb_len = 16; - break; - case 5: - cdb_len = 12; - break; - default: - cdb_len = -1; - } - return cdb_len; -} - -int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) -{ - int rc; - int len; - - cmd->lba = -1; - len = scsi_cdb_length(buf); - if (len < 0) { - return -1; - } - - cmd->len = len; - switch (dev->type) { - case TYPE_TAPE: - rc = scsi_req_stream_xfer(cmd, dev, buf); - break; - case TYPE_MEDIUM_CHANGER: - rc = scsi_req_medium_changer_xfer(cmd, dev, buf); - break; - default: - rc = scsi_req_xfer(cmd, dev, buf); - break; - } - - if (rc != 0) - return rc; - - memcpy(cmd->buf, buf, cmd->len); - scsi_cmd_xfer_mode(cmd); - cmd->lba = scsi_cmd_lba(cmd); - return 0; -} - -void scsi_device_report_change(SCSIDevice *dev, SCSISense sense) -{ - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - - scsi_device_set_ua(dev, sense); - if (bus->info->change) { - bus->info->change(bus, dev, sense); - } -} - -/* - * Predefined sense codes - */ - -/* No sense data available */ -const struct SCSISense sense_code_NO_SENSE = { - .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 -}; - -/* LUN not ready, Manual intervention required */ -const struct SCSISense sense_code_LUN_NOT_READY = { - .key = NOT_READY, .asc = 0x04, .ascq = 0x03 -}; - -/* LUN not ready, Medium not present */ -const struct SCSISense sense_code_NO_MEDIUM = { - .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 -}; - -/* LUN not ready, medium removal prevented */ -const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { - .key = NOT_READY, .asc = 0x53, .ascq = 0x02 -}; - -/* Hardware error, internal target failure */ -const struct SCSISense sense_code_TARGET_FAILURE = { - .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 -}; - -/* Illegal request, invalid command operation code */ -const struct SCSISense sense_code_INVALID_OPCODE = { - .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 -}; - -/* Illegal request, LBA out of range */ -const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { - .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 -}; - -/* Illegal request, Invalid field in CDB */ -const struct SCSISense sense_code_INVALID_FIELD = { - .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 -}; - -/* Illegal request, Invalid field in parameter list */ -const struct SCSISense sense_code_INVALID_PARAM = { - .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 -}; - -/* Illegal request, Parameter list length error */ -const struct SCSISense sense_code_INVALID_PARAM_LEN = { - .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 -}; - -/* Illegal request, LUN not supported */ -const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { - .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 -}; - -/* Illegal request, Saving parameters not supported */ -const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { - .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 -}; - -/* Illegal request, Incompatible medium installed */ -const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { - .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 -}; - -/* Illegal request, medium removal prevented */ -const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { - .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 -}; - -/* Illegal request, Invalid Transfer Tag */ -const struct SCSISense sense_code_INVALID_TAG = { - .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 -}; - -/* Command aborted, I/O process terminated */ -const struct SCSISense sense_code_IO_ERROR = { - .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 -}; - -/* Command aborted, I_T Nexus loss occurred */ -const struct SCSISense sense_code_I_T_NEXUS_LOSS = { - .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 -}; - -/* Command aborted, Logical Unit failure */ -const struct SCSISense sense_code_LUN_FAILURE = { - .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 -}; - -/* Command aborted, Overlapped Commands Attempted */ -const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { - .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 -}; - -/* Unit attention, Capacity data has changed */ -const struct SCSISense sense_code_CAPACITY_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 -}; - -/* Unit attention, Power on, reset or bus device reset occurred */ -const struct SCSISense sense_code_RESET = { - .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 -}; - -/* Unit attention, No medium */ -const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { - .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 -}; - -/* Unit attention, Medium may have changed */ -const struct SCSISense sense_code_MEDIUM_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 -}; - -/* Unit attention, Reported LUNs data has changed */ -const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e -}; - -/* Unit attention, Device internal reset */ -const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { - .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 -}; - -/* Data Protection, Write Protected */ -const struct SCSISense sense_code_WRITE_PROTECTED = { - .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 -}; - -/* Data Protection, Space Allocation Failed Write Protect */ -const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { - .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x07 -}; - -/* - * scsi_build_sense - * - * Convert between fixed and descriptor sense buffers - */ -int scsi_build_sense(uint8_t *in_buf, int in_len, - uint8_t *buf, int len, bool fixed) -{ - bool fixed_in; - SCSISense sense; - if (!fixed && len < 8) { - return 0; - } - - if (in_len == 0) { - sense.key = NO_SENSE; - sense.asc = 0; - sense.ascq = 0; - } else { - fixed_in = (in_buf[0] & 2) == 0; - - if (fixed == fixed_in) { - memcpy(buf, in_buf, MIN(len, in_len)); - return MIN(len, in_len); - } - - if (fixed_in) { - sense.key = in_buf[2]; - sense.asc = in_buf[12]; - sense.ascq = in_buf[13]; - } else { - sense.key = in_buf[1]; - sense.asc = in_buf[2]; - sense.ascq = in_buf[3]; - } - } - - memset(buf, 0, len); - if (fixed) { - /* Return fixed format sense buffer */ - buf[0] = 0x70; - buf[2] = sense.key; - buf[7] = 10; - buf[12] = sense.asc; - buf[13] = sense.ascq; - return MIN(len, SCSI_SENSE_LEN); - } else { - /* Return descriptor format sense buffer */ - buf[0] = 0x72; - buf[1] = sense.key; - buf[2] = sense.asc; - buf[3] = sense.ascq; - return 8; - } -} - -const char *scsi_command_name(uint8_t cmd) -{ - static const char *names[] = { - [ TEST_UNIT_READY ] = "TEST_UNIT_READY", - [ REWIND ] = "REWIND", - [ REQUEST_SENSE ] = "REQUEST_SENSE", - [ FORMAT_UNIT ] = "FORMAT_UNIT", - [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", - [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", - /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ - [ READ_6 ] = "READ_6", - [ WRITE_6 ] = "WRITE_6", - [ SET_CAPACITY ] = "SET_CAPACITY", - [ READ_REVERSE ] = "READ_REVERSE", - [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", - [ SPACE ] = "SPACE", - [ INQUIRY ] = "INQUIRY", - [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", - [ MAINTENANCE_IN ] = "MAINTENANCE_IN", - [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", - [ MODE_SELECT ] = "MODE_SELECT", - [ RESERVE ] = "RESERVE", - [ RELEASE ] = "RELEASE", - [ COPY ] = "COPY", - [ ERASE ] = "ERASE", - [ MODE_SENSE ] = "MODE_SENSE", - [ START_STOP ] = "START_STOP/LOAD_UNLOAD", - /* LOAD_UNLOAD and START_STOP use the same operation code */ - [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", - [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", - [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", - [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", - [ READ_10 ] = "READ_10", - [ WRITE_10 ] = "WRITE_10", - [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", - /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ - [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", - [ VERIFY_10 ] = "VERIFY_10", - [ SEARCH_HIGH ] = "SEARCH_HIGH", - [ SEARCH_EQUAL ] = "SEARCH_EQUAL", - [ SEARCH_LOW ] = "SEARCH_LOW", - [ SET_LIMITS ] = "SET_LIMITS", - [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION", - /* READ_POSITION and PRE_FETCH use the same operation code */ - [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", - [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", - [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", - /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ - [ MEDIUM_SCAN ] = "MEDIUM_SCAN", - [ COMPARE ] = "COMPARE", - [ COPY_VERIFY ] = "COPY_VERIFY", - [ WRITE_BUFFER ] = "WRITE_BUFFER", - [ READ_BUFFER ] = "READ_BUFFER", - [ UPDATE_BLOCK ] = "UPDATE_BLOCK", - [ READ_LONG_10 ] = "READ_LONG_10", - [ WRITE_LONG_10 ] = "WRITE_LONG_10", - [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", - [ WRITE_SAME_10 ] = "WRITE_SAME_10", - [ UNMAP ] = "UNMAP", - [ READ_TOC ] = "READ_TOC", - [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", - [ SANITIZE ] = "SANITIZE", - [ GET_CONFIGURATION ] = "GET_CONFIGURATION", - [ LOG_SELECT ] = "LOG_SELECT", - [ LOG_SENSE ] = "LOG_SENSE", - [ MODE_SELECT_10 ] = "MODE_SELECT_10", - [ RESERVE_10 ] = "RESERVE_10", - [ RELEASE_10 ] = "RELEASE_10", - [ MODE_SENSE_10 ] = "MODE_SENSE_10", - [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", - [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", - [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", - [ EXTENDED_COPY ] = "EXTENDED_COPY", - [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", - [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", - [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", - [ READ_16 ] = "READ_16", - [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", - [ WRITE_16 ] = "WRITE_16", - [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", - [ VERIFY_16 ] = "VERIFY_16", - [ PRE_FETCH_16 ] = "PRE_FETCH_16", - [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16", - /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ - [ LOCATE_16 ] = "LOCATE_16", - [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16", - /* ERASE_16 and WRITE_SAME_16 use the same operation code */ - [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", - [ WRITE_LONG_16 ] = "WRITE_LONG_16", - [ REPORT_LUNS ] = "REPORT_LUNS", - [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", - [ MOVE_MEDIUM ] = "MOVE_MEDIUM", - [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", - [ READ_12 ] = "READ_12", - [ WRITE_12 ] = "WRITE_12", - [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", - /* ERASE_12 and GET_PERFORMANCE use the same operation code */ - [ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12", - [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", - [ VERIFY_12 ] = "VERIFY_12", - [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", - [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", - [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", - [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", - [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING", - /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ - [ READ_CD ] = "READ_CD", - [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", - [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE", - [ RESERVE_TRACK ] = "RESERVE_TRACK", - [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET", - [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE", - [ SET_CD_SPEED ] = "SET_CD_SPEED", - [ SET_READ_AHEAD ] = "SET_READ_AHEAD", - [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", - [ MECHANISM_STATUS ] = "MECHANISM_STATUS", - [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", - [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", - }; - - if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) - return "*UNKNOWN*"; - return names[cmd]; -} - -SCSIRequest *scsi_req_ref(SCSIRequest *req) -{ - assert(req->refcount > 0); - req->refcount++; - return req; -} - -void scsi_req_unref(SCSIRequest *req) -{ - assert(req->refcount > 0); - if (--req->refcount == 0) { - BusState *qbus = req->dev->qdev.parent_bus; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, qbus); - - if (bus->info->free_request && req->hba_private) { - bus->info->free_request(bus, req->hba_private); - } - if (req->ops->free_req) { - req->ops->free_req(req); - } - object_unref(OBJECT(req->dev)); - object_unref(OBJECT(qbus->parent)); - g_free(req); - } -} - -/* Tell the device that we finished processing this chunk of I/O. It - will start the next chunk or complete the command. */ -void scsi_req_continue(SCSIRequest *req) -{ - if (req->io_canceled) { - trace_scsi_req_continue_canceled(req->dev->id, req->lun, req->tag); - return; - } - trace_scsi_req_continue(req->dev->id, req->lun, req->tag); - if (req->cmd.mode == SCSI_XFER_TO_DEV) { - req->ops->write_data(req); - } else { - req->ops->read_data(req); - } -} - -/* Called by the devices when data is ready for the HBA. The HBA should - start a DMA operation to read or fill the device's data buffer. - Once it completes, calling scsi_req_continue will restart I/O. */ -void scsi_req_data(SCSIRequest *req, int len) -{ - uint8_t *buf; - if (req->io_canceled) { - trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len); - return; - } - trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); - assert(req->cmd.mode != SCSI_XFER_NONE); - if (!req->sg) { - req->resid -= len; - req->bus->info->transfer_data(req, len); - return; - } - - /* If the device calls scsi_req_data and the HBA specified a - * scatter/gather list, the transfer has to happen in a single - * step. */ - assert(!req->dma_started); - req->dma_started = true; - - buf = scsi_req_get_buf(req); - if (req->cmd.mode == SCSI_XFER_FROM_DEV) { - req->resid = dma_buf_read(buf, len, req->sg); - } else { - req->resid = dma_buf_write(buf, len, req->sg); - } - scsi_req_continue(req); -} - -void scsi_req_print(SCSIRequest *req) -{ - FILE *fp = stderr; - int i; - - fprintf(fp, "[%s id=%d] %s", - req->dev->qdev.parent_bus->name, - req->dev->id, - scsi_command_name(req->cmd.buf[0])); - for (i = 1; i < req->cmd.len; i++) { - fprintf(fp, " 0x%02x", req->cmd.buf[i]); - } - switch (req->cmd.mode) { - case SCSI_XFER_NONE: - fprintf(fp, " - none\n"); - break; - case SCSI_XFER_FROM_DEV: - fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer); - break; - case SCSI_XFER_TO_DEV: - fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer); - break; - default: - fprintf(fp, " - Oops\n"); - break; - } -} - -void scsi_req_complete(SCSIRequest *req, int status) -{ - assert(req->status == -1); - req->status = status; - - assert(req->sense_len <= sizeof(req->sense)); - if (status == GOOD) { - req->sense_len = 0; - } - - if (req->sense_len) { - memcpy(req->dev->sense, req->sense, req->sense_len); - req->dev->sense_len = req->sense_len; - req->dev->sense_is_ua = (req->ops == &reqops_unit_attention); - } else { - req->dev->sense_len = 0; - req->dev->sense_is_ua = false; - } - - /* - * Unit attention state is now stored in the device's sense buffer - * if the HBA didn't do autosense. Clear the pending unit attention - * flags. - */ - scsi_clear_unit_attention(req); - - scsi_req_ref(req); - scsi_req_dequeue(req); - req->bus->info->complete(req, req->status, req->resid); - - /* Cancelled requests might end up being completed instead of cancelled */ - notifier_list_notify(&req->cancel_notifiers, req); - scsi_req_unref(req); -} - -/* Called by the devices when the request is canceled. */ -void scsi_req_cancel_complete(SCSIRequest *req) -{ - assert(req->io_canceled); - if (req->bus->info->cancel) { - req->bus->info->cancel(req); - } - notifier_list_notify(&req->cancel_notifiers, req); - scsi_req_unref(req); -} - -/* Cancel @req asynchronously. @notifier is added to @req's cancellation - * notifier list, the bus will be notified the requests cancellation is - * completed. - * */ -void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier) -{ - trace_scsi_req_cancel(req->dev->id, req->lun, req->tag); - if (notifier) { - notifier_list_add(&req->cancel_notifiers, notifier); - } - if (req->io_canceled) { - /* A blk_aio_cancel_async is pending; when it finishes, - * scsi_req_cancel_complete will be called and will - * call the notifier we just added. Just wait for that. - */ - assert(req->aiocb); - return; - } - /* Dropped in scsi_req_cancel_complete. */ - scsi_req_ref(req); - scsi_req_dequeue(req); - req->io_canceled = true; - if (req->aiocb) { - blk_aio_cancel_async(req->aiocb); - } else { - scsi_req_cancel_complete(req); - } -} - -void scsi_req_cancel(SCSIRequest *req) -{ - trace_scsi_req_cancel(req->dev->id, req->lun, req->tag); - if (!req->enqueued) { - return; - } - assert(!req->io_canceled); - /* Dropped in scsi_req_cancel_complete. */ - scsi_req_ref(req); - scsi_req_dequeue(req); - req->io_canceled = true; - if (req->aiocb) { - blk_aio_cancel(req->aiocb); - } else { - scsi_req_cancel_complete(req); - } -} - -static int scsi_ua_precedence(SCSISense sense) -{ - if (sense.key != UNIT_ATTENTION) { - return INT_MAX; - } - if (sense.asc == 0x29 && sense.ascq == 0x04) { - /* DEVICE INTERNAL RESET goes with POWER ON OCCURRED */ - return 1; - } else if (sense.asc == 0x3F && sense.ascq == 0x01) { - /* MICROCODE HAS BEEN CHANGED goes with SCSI BUS RESET OCCURRED */ - return 2; - } else if (sense.asc == 0x29 && (sense.ascq == 0x05 || sense.ascq == 0x06)) { - /* These two go with "all others". */ - ; - } else if (sense.asc == 0x29 && sense.ascq <= 0x07) { - /* POWER ON, RESET OR BUS DEVICE RESET OCCURRED = 0 - * POWER ON OCCURRED = 1 - * SCSI BUS RESET OCCURRED = 2 - * BUS DEVICE RESET FUNCTION OCCURRED = 3 - * I_T NEXUS LOSS OCCURRED = 7 - */ - return sense.ascq; - } else if (sense.asc == 0x2F && sense.ascq == 0x01) { - /* COMMANDS CLEARED BY POWER LOSS NOTIFICATION */ - return 8; - } - return (sense.asc << 8) | sense.ascq; -} - -void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense) -{ - int prec1, prec2; - if (sense.key != UNIT_ATTENTION) { - return; - } - trace_scsi_device_set_ua(sdev->id, sdev->lun, sense.key, - sense.asc, sense.ascq); - - /* - * Override a pre-existing unit attention condition, except for a more - * important reset condition. - */ - prec1 = scsi_ua_precedence(sdev->unit_attention); - prec2 = scsi_ua_precedence(sense); - if (prec2 < prec1) { - sdev->unit_attention = sense; - } -} - -void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) -{ - SCSIRequest *req; - - aio_context_acquire(blk_get_aio_context(sdev->conf.blk)); - while (!QTAILQ_EMPTY(&sdev->requests)) { - req = QTAILQ_FIRST(&sdev->requests); - scsi_req_cancel_async(req, NULL); - } - blk_drain(sdev->conf.blk); - aio_context_release(blk_get_aio_context(sdev->conf.blk)); - scsi_device_set_ua(sdev, sense); -} - -static char *scsibus_get_dev_path(DeviceState *dev) -{ - SCSIDevice *d = SCSI_DEVICE(dev); - DeviceState *hba = dev->parent_bus->parent; - char *id; - char *path; - - id = qdev_get_dev_path(hba); - if (id) { - path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); - } else { - path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); - } - g_free(id); - return path; -} - -static char *scsibus_get_fw_dev_path(DeviceState *dev) -{ - SCSIDevice *d = SCSI_DEVICE(dev); - return g_strdup_printf("channel@%x/%s@%x,%x", d->channel, - qdev_fw_name(dev), d->id, d->lun); -} - -SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) -{ - BusChild *kid; - SCSIDevice *target_dev = NULL; - - QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, ChildrenHead, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->channel == channel && dev->id == id) { - if (dev->lun == lun) { - return dev; - } - target_dev = dev; - } - } - return target_dev; -} - -/* SCSI request list. For simplicity, pv points to the whole device */ - -static void put_scsi_requests(QEMUFile *f, void *pv, size_t size) -{ - SCSIDevice *s = pv; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); - SCSIRequest *req; - - QTAILQ_FOREACH(req, &s->requests, next) { - assert(!req->io_canceled); - assert(req->status == -1); - assert(req->enqueued); - - qemu_put_sbyte(f, req->retry ? 1 : 2); - qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); - qemu_put_be32s(f, &req->tag); - qemu_put_be32s(f, &req->lun); - if (bus->info->save_request) { - bus->info->save_request(f, req); - } - if (req->ops->save_request) { - req->ops->save_request(f, req); - } - } - qemu_put_sbyte(f, 0); -} - -static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) -{ - SCSIDevice *s = pv; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); - int8_t sbyte; - - while ((sbyte = qemu_get_sbyte(f)) > 0) { - uint8_t buf[SCSI_CMD_BUF_SIZE]; - uint32_t tag; - uint32_t lun; - SCSIRequest *req; - - qemu_get_buffer(f, buf, sizeof(buf)); - qemu_get_be32s(f, &tag); - qemu_get_be32s(f, &lun); - req = scsi_req_new(s, tag, lun, buf, NULL); - req->retry = (sbyte == 1); - if (bus->info->load_request) { - req->hba_private = bus->info->load_request(f, req); - } - if (req->ops->load_request) { - req->ops->load_request(f, req); - } - - /* Just restart it later. */ - scsi_req_enqueue_internal(req); - - /* At this point, the request will be kept alive by the reference - * added by scsi_req_enqueue_internal, so we can release our reference. - * The HBA of course will add its own reference in the load_request - * callback if it needs to hold on the SCSIRequest. - */ - scsi_req_unref(req); - } - - return 0; -} - -static const VMStateInfo vmstate_info_scsi_requests = { - .name = "scsi-requests", - .get = get_scsi_requests, - .put = put_scsi_requests, -}; - -static bool scsi_sense_state_needed(void *opaque) -{ - SCSIDevice *s = opaque; - - return s->sense_len > SCSI_SENSE_BUF_SIZE_OLD; -} - -static const VMStateDescription vmstate_scsi_sense_state = { - .name = "SCSIDevice/sense", - .version_id = 1, - .minimum_version_id = 1, - .needed = scsi_sense_state_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice, - SCSI_SENSE_BUF_SIZE_OLD, - SCSI_SENSE_BUF_SIZE - SCSI_SENSE_BUF_SIZE_OLD), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_scsi_device = { - .name = "SCSIDevice", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(unit_attention.key, SCSIDevice), - VMSTATE_UINT8(unit_attention.asc, SCSIDevice), - VMSTATE_UINT8(unit_attention.ascq, SCSIDevice), - VMSTATE_BOOL(sense_is_ua, SCSIDevice), - VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice, 0, SCSI_SENSE_BUF_SIZE_OLD), - VMSTATE_UINT32(sense_len, SCSIDevice), - { - .name = "requests", - .version_id = 0, - .field_exists = NULL, - .size = 0, /* ouch */ - .info = &vmstate_info_scsi_requests, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_scsi_sense_state, - NULL - } -}; - -static void scsi_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - set_bit(DEVICE_CATEGORY_STORAGE, k->categories); - k->bus_type = TYPE_SCSI_BUS; - k->realize = scsi_qdev_realize; - k->unrealize = scsi_qdev_unrealize; - k->props = scsi_props; -} - -static void scsi_dev_instance_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - SCSIDevice *s = SCSI_DEVICE(dev); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", NULL, - &s->qdev, NULL); -} - -static const TypeInfo scsi_device_type_info = { - .name = TYPE_SCSI_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SCSIDevice), - .abstract = true, - .class_size = sizeof(SCSIDeviceClass), - .class_init = scsi_device_class_init, - .instance_init = scsi_dev_instance_init, -}; - -static void scsi_register_types(void) -{ - type_register_static(&scsi_bus_info); - type_register_static(&scsi_device_type_info); -} - -type_init(scsi_register_types) diff --git a/qemu/hw/scsi/scsi-disk.c b/qemu/hw/scsi/scsi-disk.c deleted file mode 100644 index c3ce54a20..000000000 --- a/qemu/hw/scsi/scsi-disk.c +++ /dev/null @@ -1,2828 +0,0 @@ -/* - * SCSI Device emulation - * - * Copyright (c) 2006 CodeSourcery. - * Based on code by Fabrice Bellard - * - * Written by Paul Brook - * Modifications: - * 2009-Dec-12 Artyom Tarasenko : implemented stamdard inquiry for the case - * when the allocation length of CDB is smaller - * than 36. - * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the - * MODE SENSE response. - * - * This code is licensed under the LGPL. - * - * Note that this file only handles the SCSI architecture model and device - * commands. Emulation of interface/link layer protocols is handled by - * the host adapter emulator. - */ - -//#define DEBUG_SCSI - -#ifdef DEBUG_SCSI -#define DPRINTF(fmt, ...) \ -do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/block/block.h" -#include "sysemu/dma.h" -#include "qemu/cutils.h" - -#ifdef __linux -#include <scsi/sg.h> -#endif - -#define SCSI_WRITE_SAME_MAX 524288 -#define SCSI_DMA_BUF_SIZE 131072 -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_MAX_MODE_LEN 256 - -#define DEFAULT_DISCARD_GRANULARITY 4096 -#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */ -#define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */ - -typedef struct SCSIDiskState SCSIDiskState; - -typedef struct SCSIDiskReq { - SCSIRequest req; - /* Both sector and sector_count are in terms of qemu 512 byte blocks. */ - uint64_t sector; - uint32_t sector_count; - uint32_t buflen; - bool started; - struct iovec iov; - QEMUIOVector qiov; - BlockAcctCookie acct; -} SCSIDiskReq; - -#define SCSI_DISK_F_REMOVABLE 0 -#define SCSI_DISK_F_DPOFUA 1 -#define SCSI_DISK_F_NO_REMOVABLE_DEVOPS 2 - -struct SCSIDiskState -{ - SCSIDevice qdev; - uint32_t features; - bool media_changed; - bool media_event; - bool eject_request; - uint16_t port_index; - uint64_t max_unmap_size; - uint64_t max_io_size; - QEMUBH *bh; - char *version; - char *serial; - char *vendor; - char *product; - bool tray_open; - bool tray_locked; -}; - -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed); - -static void scsi_free_request(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - qemu_vfree(r->iov.iov_base); -} - -/* Helper function for command completion with sense. */ -static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense) -{ - DPRINTF("Command complete tag=0x%x sense=%d/%d/%d\n", - r->req.tag, sense.key, sense.asc, sense.ascq); - scsi_req_build_sense(&r->req, sense); - scsi_req_complete(&r->req, CHECK_CONDITION); -} - -static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - if (!r->iov.iov_base) { - r->buflen = size; - r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen); - } - r->iov.iov_len = MIN(r->sector_count * 512, r->buflen); - qemu_iovec_init_external(&r->qiov, &r->iov, 1); - return r->qiov.size / 512; -} - -static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - qemu_put_be64s(f, &r->sector); - qemu_put_be32s(f, &r->sector_count); - qemu_put_be32s(f, &r->buflen); - if (r->buflen) { - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); - } else if (!req->retry) { - uint32_t len = r->iov.iov_len; - qemu_put_be32s(f, &len); - qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); - } - } -} - -static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - qemu_get_be64s(f, &r->sector); - qemu_get_be32s(f, &r->sector_count); - qemu_get_be32s(f, &r->buflen); - if (r->buflen) { - scsi_init_iovec(r, r->buflen); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len); - } else if (!r->req.retry) { - uint32_t len; - qemu_get_be32s(f, &len); - r->iov.iov_len = len; - assert(r->iov.iov_len <= r->buflen); - qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len); - } - } - - qemu_iovec_init_external(&r->qiov, &r->iov, 1); -} - -static void scsi_aio_complete(void *opaque, int ret) -{ - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, true)) { - goto done; - } - } - - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); -} - -static bool scsi_is_cmd_fua(SCSICommand *cmd) -{ - switch (cmd->buf[0]) { - case READ_10: - case READ_12: - case READ_16: - case WRITE_10: - case WRITE_12: - case WRITE_16: - return (cmd->buf[1] & 8) != 0; - - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - case WRITE_VERIFY_10: - case WRITE_VERIFY_12: - case WRITE_VERIFY_16: - return true; - - case READ_6: - case WRITE_6: - default: - return false; - } -} - -static void scsi_write_do_fua(SCSIDiskReq *r) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (scsi_is_cmd_fua(&r->req.cmd)) { - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) -{ - assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - - r->sector += r->sector_count; - r->sector_count = 0; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete(void *opaque, int ret) -{ - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - if (ret < 0) { - block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - } - scsi_dma_complete_noio(r, ret); -} - -static void scsi_read_complete(void * opaque, int ret) -{ - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - int n; - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, true)) { - goto done; - } - } - - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size); - - n = r->qiov.size / 512; - r->sector += n; - r->sector_count -= n; - scsi_req_data(&r->req, r->qiov.size); - -done: - scsi_req_unref(&r->req); -} - -/* Actually issue a read to the block device. */ -static void scsi_do_read(SCSIDiskReq *r, int ret) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; - - assert (r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - - if (r->req.sg) { - dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); - r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_blk_read(s->qdev.conf.blk, r->req.sg, r->sector, - scsi_dma_complete, r); - } else { - n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - r->req.aiocb = blk_aio_readv(s->qdev.conf.blk, r->sector, &r->qiov, n, - scsi_read_complete, r); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_do_read_cb(void *opaque, int ret) -{ - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert (r->req.aiocb != NULL); - r->req.aiocb = NULL; - - if (ret < 0) { - block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - } - scsi_do_read(opaque, ret); -} - -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - bool first; - - DPRINTF("Read sector_count=%d\n", r->sector_count); - if (r->sector_count == 0) { - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); - return; - } - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - DPRINTF("Data transfer direction invalid\n"); - scsi_read_complete(r, -EINVAL); - return; - } - - if (s->tray_open) { - scsi_read_complete(r, -ENOMEDIUM); - return; - } - - first = !r->started; - r->started = true; - if (first && scsi_is_cmd_fua(&r->req.cmd)) { - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r); - } else { - scsi_do_read(r, 0); - } -} - -/* - * scsi_handle_rw_error has two return values. 0 means that the error - * must be ignored, 1 means that the error has been processed and the - * caller should not do anything else for this request. Note that - * scsi_handle_rw_error always manages its reference counts, independent - * of the return value. - */ -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed) -{ - bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk, - is_read, error); - - if (action == BLOCK_ERROR_ACTION_REPORT) { - if (acct_failed) { - block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } - switch (error) { - case ENOMEDIUM: - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - break; - case ENOMEM: - scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); - break; - case EINVAL: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - break; - case ENOSPC: - scsi_check_condition(r, SENSE_CODE(SPACE_ALLOC_FAILED)); - break; - default: - scsi_check_condition(r, SENSE_CODE(IO_ERROR)); - break; - } - } - blk_error_action(s->qdev.conf.blk, action, is_read, error); - if (action == BLOCK_ERROR_ACTION_STOP) { - scsi_req_retry(&r->req); - } - return action != BLOCK_ERROR_ACTION_IGNORE; -} - -static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) -{ - uint32_t n; - - assert (r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - - n = r->qiov.size / 512; - r->sector += n; - r->sector_count -= n; - if (r->sector_count == 0) { - scsi_write_do_fua(r); - return; - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - DPRINTF("Write complete tag=0x%x more=%zd\n", r->req.tag, r->qiov.size); - scsi_req_data(&r->req, r->qiov.size); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_write_complete(void * opaque, int ret) -{ - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert (r->req.aiocb != NULL); - r->req.aiocb = NULL; - - if (ret < 0) { - block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - } - scsi_write_complete_noio(r, ret); -} - -static void scsi_write_data(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { - DPRINTF("Data transfer direction invalid\n"); - scsi_write_complete_noio(r, -EINVAL); - return; - } - - if (!r->req.sg && !r->qiov.size) { - /* Called for the first time. Ask the driver to send us more data. */ - r->started = true; - scsi_write_complete_noio(r, 0); - return; - } - if (s->tray_open) { - scsi_write_complete_noio(r, -ENOMEDIUM); - return; - } - - if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 || - r->req.cmd.buf[0] == VERIFY_16) { - if (r->req.sg) { - scsi_dma_complete_noio(r, 0); - } else { - scsi_write_complete_noio(r, 0); - } - return; - } - - if (r->req.sg) { - dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE); - r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_blk_write(s->qdev.conf.blk, r->req.sg, r->sector, - scsi_dma_complete, r); - } else { - n = r->qiov.size / 512; - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, r->sector, &r->qiov, n, - scsi_write_complete, r); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - return (uint8_t *)r->iov.iov_base; -} - -static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - int buflen = 0; - int start; - - if (req->cmd.buf[1] & 0x1) { - /* Vital product data */ - uint8_t page_code = req->cmd.buf[2]; - - outbuf[buflen++] = s->qdev.type & 0x1f; - outbuf[buflen++] = page_code ; // this page - outbuf[buflen++] = 0x00; - outbuf[buflen++] = 0x00; - start = buflen; - - switch (page_code) { - case 0x00: /* Supported page codes, mandatory */ - { - DPRINTF("Inquiry EVPD[Supported pages] " - "buffer size %zd\n", req->cmd.xfer); - outbuf[buflen++] = 0x00; // list of supported pages (this page) - if (s->serial) { - outbuf[buflen++] = 0x80; // unit serial number - } - outbuf[buflen++] = 0x83; // device identification - if (s->qdev.type == TYPE_DISK) { - outbuf[buflen++] = 0xb0; // block limits - outbuf[buflen++] = 0xb2; // thin provisioning - } - break; - } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!s->serial) { - DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); - return -1; - } - - l = strlen(s->serial); - if (l > 20) { - l = 20; - } - - DPRINTF("Inquiry EVPD[Serial number] " - "buffer size %zd\n", req->cmd.xfer); - memcpy(outbuf+buflen, s->serial, l); - buflen += l; - break; - } - - case 0x83: /* Device identification page, mandatory */ - { - const char *str = s->serial ?: blk_name(s->qdev.conf.blk); - int max_len = s->serial ? 20 : 255 - 8; - int id_len = strlen(str); - - if (id_len > max_len) { - id_len = max_len; - } - DPRINTF("Inquiry EVPD[Device identification] " - "buffer size %zd\n", req->cmd.xfer); - - outbuf[buflen++] = 0x2; // ASCII - outbuf[buflen++] = 0; // not officially assigned - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, str, id_len); - buflen += id_len; - - if (s->qdev.wwn) { - outbuf[buflen++] = 0x1; // Binary - outbuf[buflen++] = 0x3; // NAA - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->qdev.wwn); - buflen += 8; - } - - if (s->qdev.port_wwn) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x93; // PIV / Target port / NAA - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->qdev.port_wwn); - buflen += 8; - } - - if (s->port_index) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x94; // PIV / Target port / relative target port - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 4; - stw_be_p(&outbuf[buflen + 2], s->port_index); - buflen += 4; - } - break; - } - case 0xb0: /* block limits */ - { - unsigned int unmap_sectors = - s->qdev.conf.discard_granularity / s->qdev.blocksize; - unsigned int min_io_size = - s->qdev.conf.min_io_size / s->qdev.blocksize; - unsigned int opt_io_size = - s->qdev.conf.opt_io_size / s->qdev.blocksize; - unsigned int max_unmap_sectors = - s->max_unmap_size / s->qdev.blocksize; - unsigned int max_io_sectors = - s->max_io_size / s->qdev.blocksize; - - if (s->qdev.type == TYPE_ROM) { - DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", - page_code); - return -1; - } - /* required VPD size with unmap support */ - buflen = 0x40; - memset(outbuf + 4, 0, buflen - 4); - - outbuf[4] = 0x1; /* wsnz */ - - /* optimal transfer length granularity */ - outbuf[6] = (min_io_size >> 8) & 0xff; - outbuf[7] = min_io_size & 0xff; - - /* maximum transfer length */ - outbuf[8] = (max_io_sectors >> 24) & 0xff; - outbuf[9] = (max_io_sectors >> 16) & 0xff; - outbuf[10] = (max_io_sectors >> 8) & 0xff; - outbuf[11] = max_io_sectors & 0xff; - - /* optimal transfer length */ - outbuf[12] = (opt_io_size >> 24) & 0xff; - outbuf[13] = (opt_io_size >> 16) & 0xff; - outbuf[14] = (opt_io_size >> 8) & 0xff; - outbuf[15] = opt_io_size & 0xff; - - /* max unmap LBA count, default is 1GB */ - outbuf[20] = (max_unmap_sectors >> 24) & 0xff; - outbuf[21] = (max_unmap_sectors >> 16) & 0xff; - outbuf[22] = (max_unmap_sectors >> 8) & 0xff; - outbuf[23] = max_unmap_sectors & 0xff; - - /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */ - outbuf[24] = 0; - outbuf[25] = 0; - outbuf[26] = 0; - outbuf[27] = 255; - - /* optimal unmap granularity */ - outbuf[28] = (unmap_sectors >> 24) & 0xff; - outbuf[29] = (unmap_sectors >> 16) & 0xff; - outbuf[30] = (unmap_sectors >> 8) & 0xff; - outbuf[31] = unmap_sectors & 0xff; - - /* max write same size */ - outbuf[36] = 0; - outbuf[37] = 0; - outbuf[38] = 0; - outbuf[39] = 0; - - outbuf[40] = (max_io_sectors >> 24) & 0xff; - outbuf[41] = (max_io_sectors >> 16) & 0xff; - outbuf[42] = (max_io_sectors >> 8) & 0xff; - outbuf[43] = max_io_sectors & 0xff; - break; - } - case 0xb2: /* thin provisioning */ - { - buflen = 8; - outbuf[4] = 0; - outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ - outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; - outbuf[7] = 0; - break; - } - default: - return -1; - } - /* done with EVPD */ - assert(buflen - start <= 255); - outbuf[start - 1] = buflen - start; - return buflen; - } - - /* Standard INQUIRY data */ - if (req->cmd.buf[2] != 0) { - return -1; - } - - /* PAGE CODE == 0 */ - buflen = req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) { - buflen = SCSI_MAX_INQUIRY_LEN; - } - - outbuf[0] = s->qdev.type & 0x1f; - outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0; - - strpadcpy((char *) &outbuf[16], 16, s->product, ' '); - strpadcpy((char *) &outbuf[8], 8, s->vendor, ' '); - - memset(&outbuf[32], 0, 4); - memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version))); - /* - * We claim conformance to SPC-3, which is required for guests - * to ask for modern features like READ CAPACITY(16) or the - * block characteristics VPD page by default. Not all of SPC-3 - * is actually implemented, but we're good enough. - */ - outbuf[2] = 5; - outbuf[3] = 2 | 0x10; /* Format 2, HiSup */ - - if (buflen > 36) { - outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */ - } else { - /* If the allocation length of CDB is too small, - the additional length is not adjusted */ - outbuf[4] = 36 - 5; - } - - /* Sync data transfer and TCQ. */ - outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0); - return buflen; -} - -static inline bool media_is_dvd(SCSIDiskState *s) -{ - uint64_t nb_sectors; - if (s->qdev.type != TYPE_ROM) { - return false; - } - if (!blk_is_inserted(s->qdev.conf.blk)) { - return false; - } - if (s->tray_open) { - return false; - } - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - return nb_sectors > CD_MAX_SECTORS; -} - -static inline bool media_is_cd(SCSIDiskState *s) -{ - uint64_t nb_sectors; - if (s->qdev.type != TYPE_ROM) { - return false; - } - if (!blk_is_inserted(s->qdev.conf.blk)) { - return false; - } - if (s->tray_open) { - return false; - } - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - return nb_sectors <= CD_MAX_SECTORS; -} - -static int scsi_read_disc_information(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) -{ - uint8_t type = r->req.cmd.buf[1] & 7; - - if (s->qdev.type != TYPE_ROM) { - return -1; - } - - /* Types 1/2 are only defined for Blu-Ray. */ - if (type != 0) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return -1; - } - - memset(outbuf, 0, 34); - outbuf[1] = 32; - outbuf[2] = 0xe; /* last session complete, disc finalized */ - outbuf[3] = 1; /* first track on disc */ - outbuf[4] = 1; /* # of sessions */ - outbuf[5] = 1; /* first track of last session */ - outbuf[6] = 1; /* last track of last session */ - outbuf[7] = 0x20; /* unrestricted use */ - outbuf[8] = 0x00; /* CD-ROM or DVD-ROM */ - /* 9-10-11: most significant byte corresponding bytes 4-5-6 */ - /* 12-23: not meaningful for CD-ROM or DVD-ROM */ - /* 24-31: disc bar code */ - /* 32: disc application code */ - /* 33: number of OPC tables */ - - return 34; -} - -static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) -{ - static const int rds_caps_size[5] = { - [0] = 2048 + 4, - [1] = 4 + 4, - [3] = 188 + 4, - [4] = 2048 + 4, - }; - - uint8_t media = r->req.cmd.buf[1]; - uint8_t layer = r->req.cmd.buf[6]; - uint8_t format = r->req.cmd.buf[7]; - int size = -1; - - if (s->qdev.type != TYPE_ROM) { - return -1; - } - if (media != 0) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return -1; - } - - if (format != 0xff) { - if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return -1; - } - if (media_is_cd(s)) { - scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT)); - return -1; - } - if (format >= ARRAY_SIZE(rds_caps_size)) { - return -1; - } - size = rds_caps_size[format]; - memset(outbuf, 0, size); - } - - switch (format) { - case 0x00: { - /* Physical format information */ - uint64_t nb_sectors; - if (layer != 0) { - goto fail; - } - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - - outbuf[4] = 1; /* DVD-ROM, part version 1 */ - outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ - outbuf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ - outbuf[7] = 0; /* default densities */ - - stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */ - stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */ - break; - } - - case 0x01: /* DVD copyright information, all zeros */ - break; - - case 0x03: /* BCA information - invalid field for no BCA info */ - return -1; - - case 0x04: /* DVD disc manufacturing information, all zeros */ - break; - - case 0xff: { /* List capabilities */ - int i; - size = 4; - for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) { - if (!rds_caps_size[i]) { - continue; - } - outbuf[size] = i; - outbuf[size + 1] = 0x40; /* Not writable, readable */ - stw_be_p(&outbuf[size + 2], rds_caps_size[i]); - size += 4; - } - break; - } - - default: - return -1; - } - - /* Size of buffer, not including 2 byte size field */ - stw_be_p(outbuf, size - 2); - return size; - -fail: - return -1; -} - -static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf) -{ - uint8_t event_code, media_status; - - media_status = 0; - if (s->tray_open) { - media_status = MS_TRAY_OPEN; - } else if (blk_is_inserted(s->qdev.conf.blk)) { - media_status = MS_MEDIA_PRESENT; - } - - /* Event notification descriptor */ - event_code = MEC_NO_CHANGE; - if (media_status != MS_TRAY_OPEN) { - if (s->media_event) { - event_code = MEC_NEW_MEDIA; - s->media_event = false; - } else if (s->eject_request) { - event_code = MEC_EJECT_REQUESTED; - s->eject_request = false; - } - } - - outbuf[0] = event_code; - outbuf[1] = media_status; - - /* These fields are reserved, just clear them. */ - outbuf[2] = 0; - outbuf[3] = 0; - return 4; -} - -static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) -{ - int size; - uint8_t *buf = r->req.cmd.buf; - uint8_t notification_class_request = buf[4]; - if (s->qdev.type != TYPE_ROM) { - return -1; - } - if ((buf[1] & 1) == 0) { - /* asynchronous */ - return -1; - } - - size = 4; - outbuf[0] = outbuf[1] = 0; - outbuf[3] = 1 << GESN_MEDIA; /* supported events */ - if (notification_class_request & (1 << GESN_MEDIA)) { - outbuf[2] = GESN_MEDIA; - size += scsi_event_status_media(s, &outbuf[size]); - } else { - outbuf[2] = 0x80; - } - stw_be_p(outbuf, size - 4); - return size; -} - -static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf) -{ - int current; - - if (s->qdev.type != TYPE_ROM) { - return -1; - } - - if (media_is_dvd(s)) { - current = MMC_PROFILE_DVD_ROM; - } else if (media_is_cd(s)) { - current = MMC_PROFILE_CD_ROM; - } else { - current = MMC_PROFILE_NONE; - } - - memset(outbuf, 0, 40); - stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */ - stw_be_p(&outbuf[6], current); - /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */ - outbuf[10] = 0x03; /* persistent, current */ - outbuf[11] = 8; /* two profiles */ - stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM); - outbuf[14] = (current == MMC_PROFILE_DVD_ROM); - stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM); - outbuf[18] = (current == MMC_PROFILE_CD_ROM); - /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */ - stw_be_p(&outbuf[20], 1); - outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */ - outbuf[23] = 8; - stl_be_p(&outbuf[24], 1); /* SCSI */ - outbuf[28] = 1; /* DBE = 1, mandatory */ - /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */ - stw_be_p(&outbuf[32], 3); - outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */ - outbuf[35] = 4; - outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */ - /* TODO: Random readable, CD read, DVD read, drive serial number, - power management */ - return 40; -} - -static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf) -{ - if (s->qdev.type != TYPE_ROM) { - return -1; - } - memset(outbuf, 0, 8); - outbuf[5] = 1; /* CD-ROM */ - return 8; -} - -static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, - int page_control) -{ - static const int mode_sense_valid[0x3f] = { - [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), - [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), - [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), - [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM), - [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), - [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), - }; - - uint8_t *p = *p_outbuf + 2; - int length; - - if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) { - return -1; - } - - /* - * If Changeable Values are requested, a mask denoting those mode parameters - * that are changeable shall be returned. As we currently don't support - * parameter changes via MODE_SELECT all bits are returned set to zero. - * The buffer was already menset to zero by the caller of this function. - * - * The offsets here are off by two compared to the descriptions in the - * SCSI specs, because those include a 2-byte header. This is unfortunate, - * but it is done so that offsets are consistent within our implementation - * of MODE SENSE and MODE SELECT. MODE SELECT has to deal with both - * 2-byte and 4-byte headers. - */ - switch (page) { - case MODE_PAGE_HD_GEOMETRY: - length = 0x16; - if (page_control == 1) { /* Changeable Values */ - break; - } - /* if a geometry hint is available, use it */ - p[0] = (s->qdev.conf.cyls >> 16) & 0xff; - p[1] = (s->qdev.conf.cyls >> 8) & 0xff; - p[2] = s->qdev.conf.cyls & 0xff; - p[3] = s->qdev.conf.heads & 0xff; - /* Write precomp start cylinder, disabled */ - p[4] = (s->qdev.conf.cyls >> 16) & 0xff; - p[5] = (s->qdev.conf.cyls >> 8) & 0xff; - p[6] = s->qdev.conf.cyls & 0xff; - /* Reduced current start cylinder, disabled */ - p[7] = (s->qdev.conf.cyls >> 16) & 0xff; - p[8] = (s->qdev.conf.cyls >> 8) & 0xff; - p[9] = s->qdev.conf.cyls & 0xff; - /* Device step rate [ns], 200ns */ - p[10] = 0; - p[11] = 200; - /* Landing zone cylinder */ - p[12] = 0xff; - p[13] = 0xff; - p[14] = 0xff; - /* Medium rotation rate [rpm], 5400 rpm */ - p[18] = (5400 >> 8) & 0xff; - p[19] = 5400 & 0xff; - break; - - case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY: - length = 0x1e; - if (page_control == 1) { /* Changeable Values */ - break; - } - /* Transfer rate [kbit/s], 5Mbit/s */ - p[0] = 5000 >> 8; - p[1] = 5000 & 0xff; - /* if a geometry hint is available, use it */ - p[2] = s->qdev.conf.heads & 0xff; - p[3] = s->qdev.conf.secs & 0xff; - p[4] = s->qdev.blocksize >> 8; - p[6] = (s->qdev.conf.cyls >> 8) & 0xff; - p[7] = s->qdev.conf.cyls & 0xff; - /* Write precomp start cylinder, disabled */ - p[8] = (s->qdev.conf.cyls >> 8) & 0xff; - p[9] = s->qdev.conf.cyls & 0xff; - /* Reduced current start cylinder, disabled */ - p[10] = (s->qdev.conf.cyls >> 8) & 0xff; - p[11] = s->qdev.conf.cyls & 0xff; - /* Device step rate [100us], 100us */ - p[12] = 0; - p[13] = 1; - /* Device step pulse width [us], 1us */ - p[14] = 1; - /* Device head settle delay [100us], 100us */ - p[15] = 0; - p[16] = 1; - /* Motor on delay [0.1s], 0.1s */ - p[17] = 1; - /* Motor off delay [0.1s], 0.1s */ - p[18] = 1; - /* Medium rotation rate [rpm], 5400 rpm */ - p[26] = (5400 >> 8) & 0xff; - p[27] = 5400 & 0xff; - break; - - case MODE_PAGE_CACHING: - length = 0x12; - if (page_control == 1 || /* Changeable Values */ - blk_enable_write_cache(s->qdev.conf.blk)) { - p[0] = 4; /* WCE */ - } - break; - - case MODE_PAGE_R_W_ERROR: - length = 10; - if (page_control == 1) { /* Changeable Values */ - break; - } - p[0] = 0x80; /* Automatic Write Reallocation Enabled */ - if (s->qdev.type == TYPE_ROM) { - p[1] = 0x20; /* Read Retry Count */ - } - break; - - case MODE_PAGE_AUDIO_CTL: - length = 14; - break; - - case MODE_PAGE_CAPABILITIES: - length = 0x14; - if (page_control == 1) { /* Changeable Values */ - break; - } - - p[0] = 0x3b; /* CD-R & CD-RW read */ - p[1] = 0; /* Writing not supported */ - p[2] = 0x7f; /* Audio, composite, digital out, - mode 2 form 1&2, multi session */ - p[3] = 0xff; /* CD DA, DA accurate, RW supported, - RW corrected, C2 errors, ISRC, - UPC, Bar code */ - p[4] = 0x2d | (s->tray_locked ? 2 : 0); - /* Locking supported, jumper present, eject, tray */ - p[5] = 0; /* no volume & mute control, no - changer */ - p[6] = (50 * 176) >> 8; /* 50x read speed */ - p[7] = (50 * 176) & 0xff; - p[8] = 2 >> 8; /* Two volume levels */ - p[9] = 2 & 0xff; - p[10] = 2048 >> 8; /* 2M buffer */ - p[11] = 2048 & 0xff; - p[12] = (16 * 176) >> 8; /* 16x read speed current */ - p[13] = (16 * 176) & 0xff; - p[16] = (16 * 176) >> 8; /* 16x write speed */ - p[17] = (16 * 176) & 0xff; - p[18] = (16 * 176) >> 8; /* 16x write speed current */ - p[19] = (16 * 176) & 0xff; - break; - - default: - return -1; - } - - assert(length < 256); - (*p_outbuf)[0] = page; - (*p_outbuf)[1] = length; - *p_outbuf += length + 2; - return length + 2; -} - -static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint64_t nb_sectors; - bool dbd; - int page, buflen, ret, page_control; - uint8_t *p; - uint8_t dev_specific_param; - - dbd = (r->req.cmd.buf[1] & 0x8) != 0; - page = r->req.cmd.buf[2] & 0x3f; - page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; - DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", - (r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control); - memset(outbuf, 0, r->req.cmd.xfer); - p = outbuf; - - if (s->qdev.type == TYPE_DISK) { - dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0; - if (blk_is_read_only(s->qdev.conf.blk)) { - dev_specific_param |= 0x80; /* Readonly. */ - } - } else { - /* MMC prescribes that CD/DVD drives have no block descriptors, - * and defines no device-specific parameter. */ - dev_specific_param = 0x00; - dbd = true; - } - - if (r->req.cmd.buf[0] == MODE_SENSE) { - p[1] = 0; /* Default media type. */ - p[2] = dev_specific_param; - p[3] = 0; /* Block descriptor length. */ - p += 4; - } else { /* MODE_SENSE_10 */ - p[2] = 0; /* Default media type. */ - p[3] = dev_specific_param; - p[6] = p[7] = 0; /* Block descriptor length. */ - p += 8; - } - - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - if (!dbd && nb_sectors) { - if (r->req.cmd.buf[0] == MODE_SENSE) { - outbuf[3] = 8; /* Block descriptor length */ - } else { /* MODE_SENSE_10 */ - outbuf[7] = 8; /* Block descriptor length */ - } - nb_sectors /= (s->qdev.blocksize / 512); - if (nb_sectors > 0xffffff) { - nb_sectors = 0; - } - p[0] = 0; /* media density code */ - p[1] = (nb_sectors >> 16) & 0xff; - p[2] = (nb_sectors >> 8) & 0xff; - p[3] = nb_sectors & 0xff; - p[4] = 0; /* reserved */ - p[5] = 0; /* bytes 5-7 are the sector size in bytes */ - p[6] = s->qdev.blocksize >> 8; - p[7] = 0; - p += 8; - } - - if (page_control == 3) { - /* Saved Values */ - scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED)); - return -1; - } - - if (page == 0x3f) { - for (page = 0; page <= 0x3e; page++) { - mode_sense_page(s, page, &p, page_control); - } - } else { - ret = mode_sense_page(s, page, &p, page_control); - if (ret == -1) { - return -1; - } - } - - buflen = p - outbuf; - /* - * The mode data length field specifies the length in bytes of the - * following data that is available to be transferred. The mode data - * length does not include itself. - */ - if (r->req.cmd.buf[0] == MODE_SENSE) { - outbuf[0] = buflen - 1; - } else { /* MODE_SENSE_10 */ - outbuf[0] = ((buflen - 2) >> 8) & 0xff; - outbuf[1] = (buflen - 2) & 0xff; - } - return buflen; -} - -static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - int start_track, format, msf, toclen; - uint64_t nb_sectors; - - msf = req->cmd.buf[1] & 2; - format = req->cmd.buf[2] & 0xf; - start_track = req->cmd.buf[6]; - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); - nb_sectors /= s->qdev.blocksize / 512; - switch (format) { - case 0: - toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track); - break; - case 1: - /* multi session : only a single session defined */ - toclen = 12; - memset(outbuf, 0, 12); - outbuf[1] = 0x0a; - outbuf[2] = 0x01; - outbuf[3] = 0x01; - break; - case 2: - toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track); - break; - default: - return -1; - } - return toclen; -} - -static int scsi_disk_emulate_start_stop(SCSIDiskReq *r) -{ - SCSIRequest *req = &r->req; - 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) { - scsi_check_condition(r, - blk_is_inserted(s->qdev.conf.blk) - ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED) - : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED)); - return -1; - } - - if (s->tray_open != !start) { - blk_eject(s->qdev.conf.blk, !start); - s->tray_open = !start; - } - } - return 0; -} - -static void scsi_disk_emulate_read_data(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - int buflen = r->iov.iov_len; - - if (buflen) { - DPRINTF("Read buf_len=%d\n", buflen); - r->iov.iov_len = 0; - r->started = true; - scsi_req_data(&r->req, buflen); - return; - } - - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); -} - -static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, - uint8_t *inbuf, int inlen) -{ - uint8_t mode_current[SCSI_MAX_MODE_LEN]; - uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; - uint8_t *p; - int len, expected_len, changeable_len, i; - - /* The input buffer does not include the page header, so it is - * off by 2 bytes. - */ - expected_len = inlen + 2; - if (expected_len > SCSI_MAX_MODE_LEN) { - return -1; - } - - p = mode_current; - memset(mode_current, 0, inlen + 2); - len = mode_sense_page(s, page, &p, 0); - if (len < 0 || len != expected_len) { - return -1; - } - - p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); - changeable_len = mode_sense_page(s, page, &p, 1); - assert(changeable_len == len); - - /* Check that unchangeable bits are the same as what MODE SENSE - * would return. - */ - for (i = 2; i < len; i++) { - if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { - return -1; - } - } - return 0; -} - -static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p) -{ - switch (page) { - case MODE_PAGE_CACHING: - blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); - break; - - default: - break; - } -} - -static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - while (len > 0) { - int page, subpage, page_len; - - /* Parse both possible formats for the mode page headers. */ - page = p[0] & 0x3f; - if (p[0] & 0x40) { - if (len < 4) { - goto invalid_param_len; - } - subpage = p[1]; - page_len = lduw_be_p(&p[2]); - p += 4; - len -= 4; - } else { - if (len < 2) { - goto invalid_param_len; - } - subpage = 0; - page_len = p[1]; - p += 2; - len -= 2; - } - - if (subpage) { - goto invalid_param; - } - if (page_len > len) { - goto invalid_param_len; - } - - if (!change) { - if (scsi_disk_check_mode_select(s, page, p, page_len) < 0) { - goto invalid_param; - } - } else { - scsi_disk_apply_mode_select(s, page, p); - } - - p += page_len; - len -= page_len; - } - return 0; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return -1; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return -1; -} - -static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint8_t *p = inbuf; - int cmd = r->req.cmd.buf[0]; - int len = r->req.cmd.xfer; - int hdr_len = (cmd == MODE_SELECT ? 4 : 8); - int bd_len; - int pass; - - /* We only support PF=1, SP=0. */ - if ((r->req.cmd.buf[1] & 0x11) != 0x10) { - goto invalid_field; - } - - if (len < hdr_len) { - goto invalid_param_len; - } - - bd_len = (cmd == MODE_SELECT ? p[3] : lduw_be_p(&p[6])); - len -= hdr_len; - p += hdr_len; - if (len < bd_len) { - goto invalid_param_len; - } - if (bd_len != 0 && bd_len != 8) { - goto invalid_param; - } - - len -= bd_len; - p += bd_len; - - /* Ensure no change is made if there is an error! */ - for (pass = 0; pass < 2; pass++) { - if (mode_select_pages(r, p, len, pass == 1) < 0) { - assert(pass == 0); - return; - } - } - if (!blk_enable_write_cache(s->qdev.conf.blk)) { - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - return; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; - -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -static inline bool check_lba_range(SCSIDiskState *s, - uint64_t sector_num, uint32_t nb_sectors) -{ - /* - * The first line tests that no overflow happens when computing the last - * sector. The second line tests that the last accessed sector is in - * range. - * - * Careful, the computations should not underflow for nb_sectors == 0, - * and a 0-block read to the first LBA beyond the end of device is - * valid. - */ - return (sector_num <= sector_num + nb_sectors && - sector_num + nb_sectors <= s->qdev.max_lba + 1); -} - -typedef struct UnmapCBData { - SCSIDiskReq *r; - uint8_t *inbuf; - int count; -} UnmapCBData; - -static void scsi_unmap_complete(void *opaque, int ret); - -static void scsi_unmap_complete_noio(UnmapCBData *data, int ret) -{ - SCSIDiskReq *r = data->r; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint64_t sector_num; - uint32_t nb_sectors; - - assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - - if (data->count > 0) { - sector_num = ldq_be_p(&data->inbuf[0]); - nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; - if (!check_lba_range(s, sector_num, nb_sectors)) { - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - goto done; - } - - r->req.aiocb = blk_aio_discard(s->qdev.conf.blk, - sector_num * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), - scsi_unmap_complete, data); - data->count--; - data->inbuf += 16; - return; - } - - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); - g_free(data); -} - -static void scsi_unmap_complete(void *opaque, int ret) -{ - UnmapCBData *data = opaque; - SCSIDiskReq *r = data->r; - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - scsi_unmap_complete_noio(data, ret); -} - -static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint8_t *p = inbuf; - int len = r->req.cmd.xfer; - UnmapCBData *data; - - /* Reject ANCHOR=1. */ - if (r->req.cmd.buf[1] & 0x1) { - goto invalid_field; - } - - if (len < 8) { - goto invalid_param_len; - } - if (len < lduw_be_p(&p[0]) + 2) { - goto invalid_param_len; - } - if (len < lduw_be_p(&p[2]) + 8) { - goto invalid_param_len; - } - if (lduw_be_p(&p[2]) & 15) { - goto invalid_param_len; - } - - if (blk_is_read_only(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return; - } - - data = g_new0(UnmapCBData, 1); - data->r = r; - data->inbuf = &p[8]; - data->count = lduw_be_p(&p[2]) >> 4; - - /* The matching unref is in scsi_unmap_complete, before data is freed. */ - scsi_req_ref(&r->req); - scsi_unmap_complete_noio(data, 0); - return; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; - -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -typedef struct WriteSameCBData { - SCSIDiskReq *r; - int64_t sector; - int nb_sectors; - QEMUIOVector qiov; - struct iovec iov; -} WriteSameCBData; - -static void scsi_write_same_complete(void *opaque, int ret) -{ - WriteSameCBData *data = opaque; - SCSIDiskReq *r = data->r; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, true)) { - goto done; - } - } - - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); - - data->nb_sectors -= data->iov.iov_len / 512; - data->sector += data->iov.iov_len / 512; - data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len); - if (data->iov.iov_len) { - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - data->iov.iov_len, BLOCK_ACCT_WRITE); - /* blk_aio_write doesn't like the qiov size being different from - * nb_sectors, make sure they match. - */ - qemu_iovec_init_external(&data->qiov, &data->iov, 1); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector, - &data->qiov, data->iov.iov_len / 512, - scsi_write_same_complete, data); - return; - } - - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); - qemu_vfree(data->iov.iov_base); - g_free(data); -} - -static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) -{ - SCSIRequest *req = &r->req; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf); - WriteSameCBData *data; - uint8_t *buf; - int i; - - /* Fail if PBDATA=1 or LBDATA=1 or ANCHOR=1. */ - if (nb_sectors == 0 || (req->cmd.buf[1] & 0x16)) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return; - } - - if (blk_is_read_only(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return; - } - if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) { - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return; - } - - if (buffer_is_zero(inbuf, s->qdev.blocksize)) { - int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0; - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - nb_sectors * s->qdev.blocksize, - BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_write_zeroes(s->qdev.conf.blk, - r->req.cmd.lba * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), - flags, scsi_aio_complete, r); - return; - } - - data = g_new0(WriteSameCBData, 1); - data->r = r; - data->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); - data->nb_sectors = nb_sectors * (s->qdev.blocksize / 512); - data->iov.iov_len = MIN(data->nb_sectors * 512, SCSI_WRITE_SAME_MAX); - data->iov.iov_base = buf = blk_blockalign(s->qdev.conf.blk, - data->iov.iov_len); - qemu_iovec_init_external(&data->qiov, &data->iov, 1); - - for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) { - memcpy(&buf[i], inbuf, s->qdev.blocksize); - } - - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - data->iov.iov_len, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector, - &data->qiov, data->iov.iov_len / 512, - scsi_write_same_complete, data); -} - -static void scsi_disk_emulate_write_data(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - if (r->iov.iov_len) { - int buflen = r->iov.iov_len; - DPRINTF("Write buf_len=%d\n", buflen); - r->iov.iov_len = 0; - scsi_req_data(&r->req, buflen); - return; - } - - switch (req->cmd.buf[0]) { - case MODE_SELECT: - case MODE_SELECT_10: - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_disk_emulate_mode_select(r, r->iov.iov_base); - break; - - case UNMAP: - scsi_disk_emulate_unmap(r, r->iov.iov_base); - break; - - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - if (r->req.status == -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - } - break; - - case WRITE_SAME_10: - case WRITE_SAME_16: - scsi_disk_emulate_write_same(r, r->iov.iov_base); - break; - - default: - abort(); - } -} - -static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint64_t nb_sectors; - uint8_t *outbuf; - int buflen; - - switch (req->cmd.buf[0]) { - case INQUIRY: - case MODE_SENSE: - case MODE_SENSE_10: - case RESERVE: - case RESERVE_10: - case RELEASE: - case RELEASE_10: - case START_STOP: - case ALLOW_MEDIUM_REMOVAL: - case GET_CONFIGURATION: - case GET_EVENT_STATUS_NOTIFICATION: - case MECHANISM_STATUS: - case REQUEST_SENSE: - break; - - default: - if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - break; - } - - /* - * FIXME: we shouldn't return anything bigger than 4k, but the code - * requires the buffer to be as big as req->cmd.xfer in several - * places. So, do not allow CDBs with a very large ALLOCATION - * LENGTH. The real fix would be to modify scsi_read_data and - * dma_buf_read, so that they return data beyond the buflen - * as all zeros. - */ - if (req->cmd.xfer > 65536) { - goto illegal_request; - } - r->buflen = MAX(4096, req->cmd.xfer); - - if (!r->iov.iov_base) { - r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen); - } - - buflen = req->cmd.xfer; - outbuf = r->iov.iov_base; - memset(outbuf, 0, r->buflen); - switch (req->cmd.buf[0]) { - case TEST_UNIT_READY: - assert(!s->tray_open && blk_is_inserted(s->qdev.conf.blk)); - break; - case INQUIRY: - buflen = scsi_disk_emulate_inquiry(req, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case MODE_SENSE: - case MODE_SENSE_10: - buflen = scsi_disk_emulate_mode_sense(r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_TOC: - buflen = scsi_disk_emulate_read_toc(req, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case RESERVE: - if (req->cmd.buf[1] & 1) { - goto illegal_request; - } - break; - case RESERVE_10: - if (req->cmd.buf[1] & 3) { - goto illegal_request; - } - break; - case RELEASE: - if (req->cmd.buf[1] & 1) { - goto illegal_request; - } - break; - case RELEASE_10: - if (req->cmd.buf[1] & 3) { - goto illegal_request; - } - break; - case START_STOP: - if (scsi_disk_emulate_start_stop(r) < 0) { - return 0; - } - break; - case ALLOW_MEDIUM_REMOVAL: - s->tray_locked = req->cmd.buf[4] & 1; - blk_lock_medium(s->qdev.conf.blk, req->cmd.buf[4] & 1); - break; - case READ_CAPACITY_10: - /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - if (!nb_sectors) { - scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - return 0; - } - if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { - goto illegal_request; - } - nb_sectors /= s->qdev.blocksize / 512; - /* Returned value is the address of the last sector. */ - nb_sectors--; - /* Remember the new size for read/write sanity checking. */ - s->qdev.max_lba = nb_sectors; - /* Clip to 2TB, instead of returning capacity modulo 2TB. */ - if (nb_sectors > UINT32_MAX) { - nb_sectors = UINT32_MAX; - } - outbuf[0] = (nb_sectors >> 24) & 0xff; - outbuf[1] = (nb_sectors >> 16) & 0xff; - outbuf[2] = (nb_sectors >> 8) & 0xff; - outbuf[3] = nb_sectors & 0xff; - outbuf[4] = 0; - outbuf[5] = 0; - outbuf[6] = s->qdev.blocksize >> 8; - outbuf[7] = 0; - break; - case REQUEST_SENSE: - /* Just return "NO SENSE". */ - buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen, - (req->cmd.buf[1] & 1) == 0); - if (buflen < 0) { - goto illegal_request; - } - break; - case MECHANISM_STATUS: - buflen = scsi_emulate_mechanism_status(s, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case GET_CONFIGURATION: - buflen = scsi_get_configuration(s, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case GET_EVENT_STATUS_NOTIFICATION: - buflen = scsi_get_event_status_notification(s, r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_DISC_INFORMATION: - buflen = scsi_read_disc_information(s, r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_DVD_STRUCTURE: - buflen = scsi_read_dvd_structure(s, r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case SERVICE_ACTION_IN_16: - /* Service Action In subcommands. */ - if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - DPRINTF("SAI READ CAPACITY(16)\n"); - memset(outbuf, 0, req->cmd.xfer); - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - if (!nb_sectors) { - scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - return 0; - } - if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { - goto illegal_request; - } - nb_sectors /= s->qdev.blocksize / 512; - /* Returned value is the address of the last sector. */ - nb_sectors--; - /* Remember the new size for read/write sanity checking. */ - s->qdev.max_lba = nb_sectors; - outbuf[0] = (nb_sectors >> 56) & 0xff; - outbuf[1] = (nb_sectors >> 48) & 0xff; - outbuf[2] = (nb_sectors >> 40) & 0xff; - outbuf[3] = (nb_sectors >> 32) & 0xff; - outbuf[4] = (nb_sectors >> 24) & 0xff; - outbuf[5] = (nb_sectors >> 16) & 0xff; - outbuf[6] = (nb_sectors >> 8) & 0xff; - outbuf[7] = nb_sectors & 0xff; - outbuf[8] = 0; - outbuf[9] = 0; - outbuf[10] = s->qdev.blocksize >> 8; - outbuf[11] = 0; - outbuf[12] = 0; - outbuf[13] = get_physical_block_exp(&s->qdev.conf); - - /* set TPE bit if the format supports discard */ - if (s->qdev.conf.discard_granularity) { - outbuf[14] = 0x80; - } - - /* Protection, exponent and lowest lba field left blank. */ - break; - } - DPRINTF("Unsupported Service Action In\n"); - goto illegal_request; - case SYNCHRONIZE_CACHE: - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return 0; - case SEEK_10: - DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba); - if (r->req.cmd.lba > s->qdev.max_lba) { - goto illegal_lba; - } - break; - case MODE_SELECT: - DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); - break; - case MODE_SELECT_10: - DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer); - break; - case UNMAP: - DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); - break; - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - DPRINTF("Verify (bytchk %d)\n", (req->cmd.buf[1] >> 1) & 3); - if (req->cmd.buf[1] & 6) { - goto illegal_request; - } - break; - case WRITE_SAME_10: - case WRITE_SAME_16: - DPRINTF("WRITE SAME %d (len %lu)\n", - req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, - (long)r->req.cmd.xfer); - break; - default: - DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0], - scsi_command_name(buf[0])); - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); - return 0; - } - assert(!r->req.aiocb); - r->iov.iov_len = MIN(r->buflen, req->cmd.xfer); - if (r->iov.iov_len == 0) { - scsi_req_complete(&r->req, GOOD); - } - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(r->iov.iov_len == req->cmd.xfer); - return -r->iov.iov_len; - } else { - return r->iov.iov_len; - } - -illegal_request: - if (r->req.status == -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - } - return 0; - -illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; -} - -/* Execute a scsi command. Returns the length of the data expected by the - command. This will be Positive for data transfers from the device - (eg. disk reads), negative for transfers to the device (eg. disk writes), - and zero if the command does not transfer any data. */ - -static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint32_t len; - uint8_t command; - - command = buf[0]; - - if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - - len = scsi_data_cdb_xfer(r->req.cmd.buf); - switch (command) { - case READ_6: - case READ_10: - case READ_12: - case READ_16: - DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(s, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); - r->sector_count = len * (s->qdev.blocksize / 512); - break; - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - case WRITE_VERIFY_10: - case WRITE_VERIFY_12: - case WRITE_VERIFY_16: - if (blk_is_read_only(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return 0; - } - DPRINTF("Write %s(sector %" PRId64 ", count %u)\n", - (command & 0xe) == 0xe ? "And Verify " : "", - r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(s, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); - r->sector_count = len * (s->qdev.blocksize / 512); - break; - default: - abort(); - illegal_request: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return 0; - illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; - } - if (r->sector_count == 0) { - scsi_req_complete(&r->req, GOOD); - } - assert(r->iov.iov_len == 0); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - return -r->sector_count * 512; - } else { - return r->sector_count * 512; - } -} - -static void scsi_disk_reset(DeviceState *dev) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); - uint64_t nb_sectors; - - scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); - - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - nb_sectors /= s->qdev.blocksize / 512; - if (nb_sectors) { - nb_sectors--; - } - s->qdev.max_lba = nb_sectors; - /* reset tray statuses */ - s->tray_locked = 0; - s->tray_open = 0; -} - -static void scsi_disk_resize_cb(void *opaque) -{ - SCSIDiskState *s = opaque; - - /* SPC lists this sense code as available only for - * direct-access devices. - */ - if (s->qdev.type == TYPE_DISK) { - scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED)); - } -} - -static void scsi_cd_change_media_cb(void *opaque, bool load) -{ - SCSIDiskState *s = opaque; - - /* - * When a CD gets changed, we have to report an ejected state and - * then a loaded state to guests so that they detect tray - * open/close and media change events. Guests that do not use - * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close - * states rely on this behavior. - * - * media_changed governs the state machine used for unit attention - * report. media_event is used by GET EVENT STATUS NOTIFICATION. - */ - s->media_changed = load; - s->tray_open = !load; - scsi_device_set_ua(&s->qdev, SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM)); - s->media_event = true; - s->eject_request = false; -} - -static void scsi_cd_eject_request_cb(void *opaque, bool force) -{ - SCSIDiskState *s = opaque; - - s->eject_request = true; - if (force) { - s->tray_locked = false; - } -} - -static bool scsi_cd_is_tray_open(void *opaque) -{ - return ((SCSIDiskState *)opaque)->tray_open; -} - -static bool scsi_cd_is_medium_locked(void *opaque) -{ - return ((SCSIDiskState *)opaque)->tray_locked; -} - -static const BlockDevOps scsi_disk_removable_block_ops = { - .change_media_cb = scsi_cd_change_media_cb, - .eject_request_cb = scsi_cd_eject_request_cb, - .is_tray_open = scsi_cd_is_tray_open, - .is_medium_locked = scsi_cd_is_medium_locked, - - .resize_cb = scsi_disk_resize_cb, -}; - -static const BlockDevOps scsi_disk_block_ops = { - .resize_cb = scsi_disk_resize_cb, -}; - -static void scsi_disk_unit_attention_reported(SCSIDevice *dev) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - if (s->media_changed) { - s->media_changed = false; - scsi_device_set_ua(&s->qdev, SENSE_CODE(MEDIUM_CHANGED)); - } -} - -static void scsi_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - Error *err = NULL; - - if (!s->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - - if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) && - !blk_is_inserted(s->qdev.conf.blk)) { - error_setg(errp, "Device needs media, but drive is empty"); - return; - } - - blkconf_serial(&s->qdev.conf, &s->serial); - blkconf_blocksizes(&s->qdev.conf); - if (dev->type == TYPE_DISK) { - blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - if (s->qdev.conf.discard_granularity == -1) { - s->qdev.conf.discard_granularity = - MAX(s->qdev.conf.logical_block_size, DEFAULT_DISCARD_GRANULARITY); - } - - if (!s->version) { - s->version = g_strdup(qemu_hw_version()); - } - if (!s->vendor) { - s->vendor = g_strdup("QEMU"); - } - - if (blk_is_sg(s->qdev.conf.blk)) { - error_setg(errp, "unwanted /dev/sg*"); - return; - } - - if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && - !(s->features & (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS))) { - blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_removable_block_ops, s); - } else { - blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_block_ops, s); - } - blk_set_guest_block_size(s->qdev.conf.blk, s->qdev.blocksize); - - blk_iostatus_enable(s->qdev.conf.blk); -} - -static void scsi_hd_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - /* can happen for devices without drive. The error message for missing - * backend will be issued in scsi_realize - */ - if (s->qdev.conf.blk) { - blkconf_blocksizes(&s->qdev.conf); - } - s->qdev.blocksize = s->qdev.conf.logical_block_size; - s->qdev.type = TYPE_DISK; - if (!s->product) { - s->product = g_strdup("QEMU HARDDISK"); - } - scsi_realize(&s->qdev, errp); -} - -static void scsi_cd_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - s->qdev.blocksize = 2048; - s->qdev.type = TYPE_ROM; - s->features |= 1 << SCSI_DISK_F_REMOVABLE; - if (!s->product) { - s->product = g_strdup("QEMU CD-ROM"); - } - scsi_realize(&s->qdev, errp); -} - -static void scsi_disk_realize(SCSIDevice *dev, Error **errp) -{ - DriveInfo *dinfo; - Error *local_err = NULL; - - if (!dev->conf.blk) { - scsi_realize(dev, &local_err); - assert(local_err); - error_propagate(errp, local_err); - return; - } - - dinfo = blk_legacy_dinfo(dev->conf.blk); - if (dinfo && dinfo->media_cd) { - scsi_cd_realize(dev, errp); - } else { - scsi_hd_realize(dev, errp); - } -} - -static const SCSIReqOps scsi_disk_emulate_reqops = { - .size = sizeof(SCSIDiskReq), - .free_req = scsi_free_request, - .send_command = scsi_disk_emulate_command, - .read_data = scsi_disk_emulate_read_data, - .write_data = scsi_disk_emulate_write_data, - .get_buf = scsi_get_buf, -}; - -static const SCSIReqOps scsi_disk_dma_reqops = { - .size = sizeof(SCSIDiskReq), - .free_req = scsi_free_request, - .send_command = scsi_disk_dma_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .get_buf = scsi_get_buf, - .load_request = scsi_disk_load_request, - .save_request = scsi_disk_save_request, -}; - -static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { - [TEST_UNIT_READY] = &scsi_disk_emulate_reqops, - [INQUIRY] = &scsi_disk_emulate_reqops, - [MODE_SENSE] = &scsi_disk_emulate_reqops, - [MODE_SENSE_10] = &scsi_disk_emulate_reqops, - [START_STOP] = &scsi_disk_emulate_reqops, - [ALLOW_MEDIUM_REMOVAL] = &scsi_disk_emulate_reqops, - [READ_CAPACITY_10] = &scsi_disk_emulate_reqops, - [READ_TOC] = &scsi_disk_emulate_reqops, - [READ_DVD_STRUCTURE] = &scsi_disk_emulate_reqops, - [READ_DISC_INFORMATION] = &scsi_disk_emulate_reqops, - [GET_CONFIGURATION] = &scsi_disk_emulate_reqops, - [GET_EVENT_STATUS_NOTIFICATION] = &scsi_disk_emulate_reqops, - [MECHANISM_STATUS] = &scsi_disk_emulate_reqops, - [SERVICE_ACTION_IN_16] = &scsi_disk_emulate_reqops, - [REQUEST_SENSE] = &scsi_disk_emulate_reqops, - [SYNCHRONIZE_CACHE] = &scsi_disk_emulate_reqops, - [SEEK_10] = &scsi_disk_emulate_reqops, - [MODE_SELECT] = &scsi_disk_emulate_reqops, - [MODE_SELECT_10] = &scsi_disk_emulate_reqops, - [UNMAP] = &scsi_disk_emulate_reqops, - [WRITE_SAME_10] = &scsi_disk_emulate_reqops, - [WRITE_SAME_16] = &scsi_disk_emulate_reqops, - [VERIFY_10] = &scsi_disk_emulate_reqops, - [VERIFY_12] = &scsi_disk_emulate_reqops, - [VERIFY_16] = &scsi_disk_emulate_reqops, - - [READ_6] = &scsi_disk_dma_reqops, - [READ_10] = &scsi_disk_dma_reqops, - [READ_12] = &scsi_disk_dma_reqops, - [READ_16] = &scsi_disk_dma_reqops, - [WRITE_6] = &scsi_disk_dma_reqops, - [WRITE_10] = &scsi_disk_dma_reqops, - [WRITE_12] = &scsi_disk_dma_reqops, - [WRITE_16] = &scsi_disk_dma_reqops, - [WRITE_VERIFY_10] = &scsi_disk_dma_reqops, - [WRITE_VERIFY_12] = &scsi_disk_dma_reqops, - [WRITE_VERIFY_16] = &scsi_disk_dma_reqops, -}; - -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *req; - const SCSIReqOps *ops; - uint8_t command; - - 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 < scsi_cdb_length(buf); i++) { - printf(" 0x%02x", buf[i]); - } - printf("\n"); - } -#endif - - return req; -} - -#ifdef __linux__ -static int get_device_type(SCSIDiskState *s) -{ - uint8_t cmd[16]; - uint8_t buf[36]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; - int ret; - - memset(cmd, 0, sizeof(cmd)); - memset(buf, 0, sizeof(buf)); - cmd[0] = INQUIRY; - cmd[4] = sizeof(buf); - - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { - return -1; - } - s->qdev.type = buf[0]; - if (buf[1] & 0x80) { - s->features |= 1 << SCSI_DISK_F_REMOVABLE; - } - return 0; -} - -static void scsi_block_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - int sg_version; - int rc; - - if (!s->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - - /* check we are using a driver managing SG_IO (version 3 and after) */ - rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version); - if (rc < 0) { - error_setg(errp, "cannot get SG_IO version number: %s. " - "Is this a SCSI device?", - strerror(-rc)); - return; - } - if (sg_version < 30000) { - error_setg(errp, "scsi generic interface too old"); - return; - } - - /* get device type from INQUIRY data */ - rc = get_device_type(s); - if (rc < 0) { - error_setg(errp, "INQUIRY failed"); - return; - } - - /* Make a guess for the block size, we'll fix it when the guest sends. - * READ CAPACITY. If they don't, they likely would assume these sizes - * anyway. (TODO: check in /sys). - */ - if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) { - s->qdev.blocksize = 2048; - } else { - s->qdev.blocksize = 512; - } - - /* Makes the scsi-block device not removable by using HMP and QMP eject - * command. - */ - s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS); - - scsi_realize(&s->qdev, errp); - scsi_generic_read_device_identification(&s->qdev); -} - -static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf) -{ - switch (buf[0]) { - case READ_6: - case READ_10: - case READ_12: - case READ_16: - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - case WRITE_VERIFY_10: - case WRITE_VERIFY_12: - case WRITE_VERIFY_16: - /* If we are not using O_DIRECT, we might read stale data from the - * host cache if writes were made using other commands than these - * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without - * O_DIRECT everything must go through SG_IO. - */ - if (!(blk_get_flags(s->qdev.conf.blk) & BDRV_O_NOCACHE)) { - break; - } - - /* MMC writing cannot be done via pread/pwrite, because it sometimes - * involves writing beyond the maximum LBA or to negative LBA (lead-in). - * And once you do these writes, reading from the block device is - * unreliable, too. It is even possible that reads deliver random data - * from the host page cache (this is probably a Linux bug). - * - * We might use scsi_disk_dma_reqops as long as no writing commands are - * seen, but performance usually isn't paramount on optical media. So, - * just make scsi-block operate the same as scsi-generic for them. - */ - if (s->qdev.type != TYPE_ROM) { - return false; - } - break; - - default: - break; - } - - return true; -} - - -static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, - uint32_t lun, uint8_t *buf, - void *hba_private) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - - if (scsi_block_is_passthrough(s, buf)) { - return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, - hba_private); - } else { - return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun, - hba_private); - } -} - -static int scsi_block_parse_cdb(SCSIDevice *d, SCSICommand *cmd, - uint8_t *buf, void *hba_private) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - - if (scsi_block_is_passthrough(s, buf)) { - return scsi_bus_parse_cdb(&s->qdev, cmd, buf, hba_private); - } else { - return scsi_req_parse_cdb(&s->qdev, cmd, buf); - } -} - -#endif - -#define DEFINE_SCSI_DISK_PROPERTIES() \ - DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ - DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ - DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ - DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ - DEFINE_PROP_STRING("product", SCSIDiskState, product) - -static Property scsi_hd_properties[] = { - DEFINE_SCSI_DISK_PROPERTIES(), - DEFINE_PROP_BIT("removable", SCSIDiskState, features, - SCSI_DISK_F_REMOVABLE, false), - DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, - SCSI_DISK_F_DPOFUA, false), - DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), - DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), - DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), - DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, - DEFAULT_MAX_UNMAP_SIZE), - DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, - DEFAULT_MAX_IO_SIZE), - DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_scsi_disk_state = { - .name = "scsi-disk", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState), - VMSTATE_BOOL(media_changed, SCSIDiskState), - VMSTATE_BOOL(media_event, SCSIDiskState), - VMSTATE_BOOL(eject_request, SCSIDiskState), - VMSTATE_BOOL(tray_open, SCSIDiskState), - VMSTATE_BOOL(tray_locked, SCSIDiskState), - VMSTATE_END_OF_LIST() - } -}; - -static void scsi_hd_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_hd_realize; - sc->alloc_req = scsi_new_request; - sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; - dc->desc = "virtual SCSI disk"; - dc->reset = scsi_disk_reset; - dc->props = scsi_hd_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_hd_info = { - .name = "scsi-hd", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_hd_class_initfn, -}; - -static Property scsi_cd_properties[] = { - DEFINE_SCSI_DISK_PROPERTIES(), - DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), - DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), - DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), - DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, - DEFAULT_MAX_IO_SIZE), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_cd_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_cd_realize; - sc->alloc_req = scsi_new_request; - sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; - dc->desc = "virtual SCSI CD-ROM"; - dc->reset = scsi_disk_reset; - dc->props = scsi_cd_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_cd_info = { - .name = "scsi-cd", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_cd_class_initfn, -}; - -#ifdef __linux__ -static Property scsi_block_properties[] = { - DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_block_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_block_realize; - sc->alloc_req = scsi_block_new_request; - sc->parse_cdb = scsi_block_parse_cdb; - dc->fw_name = "disk"; - dc->desc = "SCSI block device passthrough"; - dc->reset = scsi_disk_reset; - dc->props = scsi_block_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_block_info = { - .name = "scsi-block", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_block_class_initfn, -}; -#endif - -static Property scsi_disk_properties[] = { - DEFINE_SCSI_DISK_PROPERTIES(), - DEFINE_PROP_BIT("removable", SCSIDiskState, features, - SCSI_DISK_F_REMOVABLE, false), - DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, - SCSI_DISK_F_DPOFUA, false), - DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), - DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), - DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), - DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, - DEFAULT_MAX_UNMAP_SIZE), - DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, - DEFAULT_MAX_IO_SIZE), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_disk_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_disk_realize; - sc->alloc_req = scsi_new_request; - sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; - dc->desc = "virtual SCSI disk or CD-ROM (legacy)"; - dc->reset = scsi_disk_reset; - dc->props = scsi_disk_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_disk_info = { - .name = "scsi-disk", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_disk_class_initfn, -}; - -static void scsi_disk_register_types(void) -{ - type_register_static(&scsi_hd_info); - type_register_static(&scsi_cd_info); -#ifdef __linux__ - type_register_static(&scsi_block_info); -#endif - type_register_static(&scsi_disk_info); -} - -type_init(scsi_disk_register_types) diff --git a/qemu/hw/scsi/scsi-generic.c b/qemu/hw/scsi/scsi-generic.c deleted file mode 100644 index 7459465f6..000000000 --- a/qemu/hw/scsi/scsi-generic.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Generic SCSI Device support - * - * Copyright (c) 2007 Bull S.A.S. - * Based on code by Paul Brook - * Based on code by Fabrice Bellard - * - * Written by Laurent Vivier <Laurent.Vivier@bull.net> - * - * This code is licensed under the LGPL. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "hw/scsi/scsi.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" - -#ifdef __linux__ - -//#define DEBUG_SCSI - -#ifdef DEBUG_SCSI -#define DPRINTF(fmt, ...) \ -do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define BADF(fmt, ...) \ -do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) - -#include <scsi/sg.h> -#include "block/scsi.h" - -#define SG_ERR_DRIVER_TIMEOUT 0x06 -#define SG_ERR_DRIVER_SENSE 0x08 - -#define SG_ERR_DID_OK 0x00 -#define SG_ERR_DID_NO_CONNECT 0x01 -#define SG_ERR_DID_BUS_BUSY 0x02 -#define SG_ERR_DID_TIME_OUT 0x03 - -#ifndef MAX_UINT -#define MAX_UINT ((unsigned int)-1) -#endif - -typedef struct SCSIGenericReq { - SCSIRequest req; - uint8_t *buf; - int buflen; - int len; - sg_io_hdr_t io_header; -} SCSIGenericReq; - -static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - qemu_put_sbe32s(f, &r->buflen); - if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(!r->req.sg); - qemu_put_buffer(f, r->buf, r->req.cmd.xfer); - } -} - -static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - qemu_get_sbe32s(f, &r->buflen); - if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(!r->req.sg); - qemu_get_buffer(f, r->buf, r->req.cmd.xfer); - } -} - -static void scsi_free_request(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - g_free(r->buf); -} - -/* Helper function for command completion. */ -static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) -{ - int status; - - assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - r->req.sense_len = r->io_header.sb_len_wr; - } - - if (ret != 0) { - switch (ret) { - case -EDOM: - status = TASK_SET_FULL; - break; - case -ENOMEM: - status = CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); - break; - default: - status = CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); - break; - } - } else { - if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || - r->io_header.host_status == SG_ERR_DID_BUS_BUSY || - r->io_header.host_status == SG_ERR_DID_TIME_OUT || - (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { - status = BUSY; - BADF("Driver Timeout\n"); - } else if (r->io_header.host_status) { - status = CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); - } else if (r->io_header.status) { - status = r->io_header.status; - } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - status = CHECK_CONDITION; - } else { - status = GOOD; - } - } - DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", - r, r->req.tag, status); - - scsi_req_complete(&r->req, status); -done: - scsi_req_unref(&r->req); -} - -static void scsi_command_complete(void *opaque, int ret) -{ - SCSIGenericReq *r = (SCSIGenericReq *)opaque; - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - scsi_command_complete_noio(r, ret); -} - -static int execute_command(BlockBackend *blk, - SCSIGenericReq *r, int direction, - BlockCompletionFunc *complete) -{ - r->io_header.interface_id = 'S'; - r->io_header.dxfer_direction = direction; - r->io_header.dxferp = r->buf; - r->io_header.dxfer_len = r->buflen; - r->io_header.cmdp = r->req.cmd.buf; - r->io_header.cmd_len = r->req.cmd.len; - r->io_header.mx_sb_len = sizeof(r->req.sense); - r->io_header.sbp = r->req.sense; - r->io_header.timeout = MAX_UINT; - r->io_header.usr_ptr = r; - r->io_header.flags |= SG_FLAG_DIRECT_IO; - - r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r); - if (r->req.aiocb == NULL) { - return -EIO; - } - - return 0; -} - -static void scsi_read_complete(void * opaque, int ret) -{ - SCSIGenericReq *r = (SCSIGenericReq *)opaque; - SCSIDevice *s = r->req.dev; - int len; - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - if (ret || r->req.io_canceled) { - scsi_command_complete_noio(r, ret); - return; - } - - len = r->io_header.dxfer_len - r->io_header.resid; - DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); - - r->len = -1; - if (len == 0) { - scsi_command_complete_noio(r, 0); - return; - } - - /* Snoop READ CAPACITY output to set the blocksize. */ - if (r->req.cmd.buf[0] == READ_CAPACITY_10 && - (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) { - s->blocksize = ldl_be_p(&r->buf[4]); - s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL; - } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && - (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - s->blocksize = ldl_be_p(&r->buf[8]); - s->max_lba = ldq_be_p(&r->buf[0]); - } - blk_set_guest_block_size(s->conf.blk, s->blocksize); - - /* Patch MODE SENSE device specific parameters if the BDS is opened - * readonly. - */ - if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) && - blk_is_read_only(s->conf.blk) && - (r->req.cmd.buf[0] == MODE_SENSE || - r->req.cmd.buf[0] == MODE_SENSE_10) && - (r->req.cmd.buf[1] & 0x8) == 0) { - if (r->req.cmd.buf[0] == MODE_SENSE) { - r->buf[2] |= 0x80; - } else { - r->buf[3] |= 0x80; - } - } - scsi_req_data(&r->req, len); - scsi_req_unref(&r->req); -} - -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIDevice *s = r->req.dev; - int ret; - - DPRINTF("scsi_read_data 0x%x\n", req->tag); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->len == -1) { - scsi_command_complete_noio(r, 0); - return; - } - - ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV, - scsi_read_complete); - if (ret < 0) { - scsi_command_complete_noio(r, ret); - } -} - -static void scsi_write_complete(void * opaque, int ret) -{ - SCSIGenericReq *r = (SCSIGenericReq *)opaque; - SCSIDevice *s = r->req.dev; - - DPRINTF("scsi_write_complete() ret = %d\n", ret); - - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - - if (ret || r->req.io_canceled) { - scsi_command_complete_noio(r, ret); - return; - } - - if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && - s->type == TYPE_TAPE) { - s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; - DPRINTF("block size %d\n", s->blocksize); - } - - scsi_command_complete_noio(r, ret); -} - -/* Write data to a scsi device. Returns nonzero on failure. - The transfer may complete asynchronously. */ -static void scsi_write_data(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIDevice *s = r->req.dev; - int ret; - - DPRINTF("scsi_write_data 0x%x\n", req->tag); - if (r->len == 0) { - r->len = r->buflen; - scsi_req_data(&r->req, r->len); - return; - } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete); - if (ret < 0) { - scsi_command_complete_noio(r, ret); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - return r->buf; -} - -/* Execute a scsi command. Returns the length of the data expected by the - command. This will be Positive for data transfers from the device - (eg. disk reads), negative for transfers to the device (eg. disk writes), - and zero if the command does not transfer any data. */ - -static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIDevice *s = r->req.dev; - int ret; - -#ifdef DEBUG_SCSI - { - int i; - for (i = 1; i < r->req.cmd.len; i++) { - printf(" 0x%02x", cmd[i]); - } - printf("\n"); - } -#endif - - if (r->req.cmd.xfer == 0) { - g_free(r->buf); - r->buflen = 0; - r->buf = NULL; - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - ret = execute_command(s->conf.blk, r, SG_DXFER_NONE, - scsi_command_complete); - if (ret < 0) { - scsi_command_complete_noio(r, ret); - return 0; - } - return 0; - } - - if (r->buflen != r->req.cmd.xfer) { - g_free(r->buf); - r->buf = g_malloc(r->req.cmd.xfer); - r->buflen = r->req.cmd.xfer; - } - - memset(r->buf, 0, r->buflen); - r->len = r->req.cmd.xfer; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - r->len = 0; - return -r->req.cmd.xfer; - } else { - return r->req.cmd.xfer; - } -} - -static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) -{ - int i; - - if ((p[1] & 0xF) == 3) { - /* NAA designator type */ - if (p[3] != 8) { - return -EINVAL; - } - *p_wwn = ldq_be_p(p + 4); - return 0; - } - - if ((p[1] & 0xF) == 8) { - /* SCSI name string designator type */ - if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) { - return -EINVAL; - } - if (p[3] > 20 && p[24] != ',') { - return -EINVAL; - } - *p_wwn = 0; - for (i = 8; i < 24; i++) { - char c = toupper(p[i]); - c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10); - *p_wwn = (*p_wwn << 4) | c; - } - return 0; - } - - return -EINVAL; -} - -void scsi_generic_read_device_identification(SCSIDevice *s) -{ - uint8_t cmd[6]; - uint8_t buf[250]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; - int ret; - int i, len; - - memset(cmd, 0, sizeof(cmd)); - memset(buf, 0, sizeof(buf)); - cmd[0] = INQUIRY; - cmd[1] = 1; - cmd[2] = 0x83; - cmd[4] = sizeof(buf); - - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(s->conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { - return; - } - - len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4); - for (i = 0; i + 3 <= len; ) { - const uint8_t *p = &buf[i + 4]; - uint64_t wwn; - - if (i + (p[3] + 4) > len) { - break; - } - - if ((p[1] & 0x10) == 0) { - /* Associated with the logical unit */ - if (read_naa_id(p, &wwn) == 0) { - s->wwn = wwn; - } - } else if ((p[1] & 0x10) == 0x10) { - /* Associated with the target port */ - if (read_naa_id(p, &wwn) == 0) { - s->port_wwn = wwn; - } - } - - i += p[3] + 4; - } -} - -static int get_stream_blocksize(BlockBackend *blk) -{ - uint8_t cmd[6]; - uint8_t buf[12]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; - int ret; - - memset(cmd, 0, sizeof(cmd)); - memset(buf, 0, sizeof(buf)); - cmd[0] = MODE_SENSE; - cmd[4] = sizeof(buf); - - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { - return -1; - } - return (buf[9] << 16) | (buf[10] << 8) | buf[11]; -} - -static void scsi_generic_reset(DeviceState *dev) -{ - SCSIDevice *s = SCSI_DEVICE(dev); - - scsi_device_purge_requests(s, SENSE_CODE(RESET)); -} - -static void scsi_generic_realize(SCSIDevice *s, Error **errp) -{ - int rc; - int sg_version; - struct sg_scsi_id scsiid; - - if (!s->conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - - if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_setg(errp, "Device doesn't support drive option werror"); - return; - } - if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "Device doesn't support drive option rerror"); - return; - } - - /* check we are using a driver managing SG_IO (version 3 and after */ - rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version); - if (rc < 0) { - error_setg(errp, "cannot get SG_IO version number: %s. " - "Is this a SCSI device?", - strerror(-rc)); - return; - } - if (sg_version < 30000) { - error_setg(errp, "scsi generic interface too old"); - return; - } - - /* get LUN of the /dev/sg? */ - if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) { - error_setg(errp, "SG_GET_SCSI_ID ioctl failed"); - return; - } - - /* define device state */ - s->type = scsiid.scsi_type; - DPRINTF("device type %d\n", s->type); - - switch (s->type) { - case TYPE_TAPE: - s->blocksize = get_stream_blocksize(s->conf.blk); - if (s->blocksize == -1) { - s->blocksize = 0; - } - break; - - /* Make a guess for block devices, we'll fix it when the guest sends. - * READ CAPACITY. If they don't, they likely would assume these sizes - * anyway. (TODO: they could also send MODE SENSE). - */ - case TYPE_ROM: - case TYPE_WORM: - s->blocksize = 2048; - break; - default: - s->blocksize = 512; - break; - } - - DPRINTF("block size %d\n", s->blocksize); - - scsi_generic_read_device_identification(s); -} - -const SCSIReqOps scsi_generic_req_ops = { - .size = sizeof(SCSIGenericReq), - .free_req = scsi_free_request, - .send_command = scsi_send_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .get_buf = scsi_get_buf, - .load_request = scsi_generic_load_request, - .save_request = scsi_generic_save_request, -}; - -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIRequest *req; - - req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); - return req; -} - -static Property scsi_generic_properties[] = { - DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, - uint8_t *buf, void *hba_private) -{ - return scsi_bus_parse_cdb(dev, cmd, buf, hba_private); -} - -static void scsi_generic_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_generic_realize; - sc->alloc_req = scsi_new_request; - sc->parse_cdb = scsi_generic_parse_cdb; - dc->fw_name = "disk"; - dc->desc = "pass through generic scsi device (/dev/sg*)"; - dc->reset = scsi_generic_reset; - dc->props = scsi_generic_properties; - dc->vmsd = &vmstate_scsi_device; -} - -static const TypeInfo scsi_generic_info = { - .name = "scsi-generic", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDevice), - .class_init = scsi_generic_class_initfn, -}; - -static void scsi_generic_register_types(void) -{ - type_register_static(&scsi_generic_info); -} - -type_init(scsi_generic_register_types) - -#endif /* __linux__ */ diff --git a/qemu/hw/scsi/spapr_vscsi.c b/qemu/hw/scsi/spapr_vscsi.c deleted file mode 100644 index b00edf7fd..000000000 --- a/qemu/hw/scsi/spapr_vscsi.c +++ /dev/null @@ -1,1304 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * PAPR Virtual SCSI, aka ibmvscsi - * - * Copyright (c) 2010,2011 Benjamin Herrenschmidt, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * TODO: - * - * - Cleanups :-) - * - Sort out better how to assign devices to VSCSI instances - * - Fix residual counts - * - Add indirect descriptors support - * - Maybe do autosense (PAPR seems to mandate it, linux doesn't care) - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "srp.h" -#include "hw/qdev.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#include "viosrp.h" - -#include <libfdt.h> - -/*#define DEBUG_VSCSI*/ - -#ifdef DEBUG_VSCSI -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -/* - * Virtual SCSI device - */ - -/* Random numbers */ -#define VSCSI_MAX_SECTORS 4096 -#define VSCSI_REQ_LIMIT 24 - -#define SRP_RSP_SENSE_DATA_LEN 18 - -#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL - -typedef union vscsi_crq { - struct viosrp_crq s; - uint8_t raw[16]; -} vscsi_crq; - -typedef struct vscsi_req { - vscsi_crq crq; - union viosrp_iu iu; - - /* SCSI request tracking */ - SCSIRequest *sreq; - uint32_t qtag; /* qemu tag != srp tag */ - bool active; - bool writing; - bool dma_error; - uint32_t data_len; - uint32_t senselen; - uint8_t sense[SCSI_SENSE_BUF_SIZE]; - - /* RDMA related bits */ - uint8_t dma_fmt; - uint16_t local_desc; - uint16_t total_desc; - uint16_t cdb_offset; - uint16_t cur_desc_num; - uint16_t cur_desc_offset; -} vscsi_req; - -#define TYPE_VIO_SPAPR_VSCSI_DEVICE "spapr-vscsi" -#define VIO_SPAPR_VSCSI_DEVICE(obj) \ - OBJECT_CHECK(VSCSIState, (obj), TYPE_VIO_SPAPR_VSCSI_DEVICE) - -typedef struct { - VIOsPAPRDevice vdev; - SCSIBus bus; - vscsi_req reqs[VSCSI_REQ_LIMIT]; -} VSCSIState; - -static struct vscsi_req *vscsi_get_req(VSCSIState *s) -{ - vscsi_req *req; - int i; - - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - req = &s->reqs[i]; - if (!req->active) { - memset(req, 0, sizeof(*req)); - req->qtag = i; - req->active = 1; - return req; - } - } - return NULL; -} - -static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag) -{ - vscsi_req *req; - int i; - - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - req = &s->reqs[i]; - if (req->iu.srp.cmd.tag == srp_tag) { - return req; - } - } - return NULL; -} - -static void vscsi_put_req(vscsi_req *req) -{ - if (req->sreq != NULL) { - scsi_req_unref(req->sreq); - } - req->sreq = NULL; - req->active = 0; -} - -static SCSIDevice *vscsi_device_find(SCSIBus *bus, uint64_t srp_lun, int *lun) -{ - int channel = 0, id = 0; - -retry: - switch (srp_lun >> 62) { - case 0: - if ((srp_lun >> 56) != 0) { - channel = (srp_lun >> 56) & 0x3f; - id = (srp_lun >> 48) & 0xff; - srp_lun <<= 16; - goto retry; - } - *lun = (srp_lun >> 48) & 0xff; - break; - - case 1: - *lun = (srp_lun >> 48) & 0x3fff; - break; - case 2: - channel = (srp_lun >> 53) & 0x7; - id = (srp_lun >> 56) & 0x3f; - *lun = (srp_lun >> 48) & 0x1f; - break; - case 3: - *lun = -1; - return NULL; - default: - abort(); - } - - return scsi_device_find(bus, channel, id, *lun); -} - -static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, - uint64_t length, uint8_t format) -{ - long rc, rc1; - - /* First copy the SRP */ - rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr, - &req->iu, length); - if (rc) { - fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); - } - - req->crq.s.valid = 0x80; - req->crq.s.format = format; - req->crq.s.reserved = 0x00; - req->crq.s.timeout = cpu_to_be16(0x0000); - req->crq.s.IU_length = cpu_to_be16(length); - req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */ - - if (rc == 0) { - req->crq.s.status = VIOSRP_OK; - } else { - req->crq.s.status = VIOSRP_ADAPTER_FAIL; - } - - rc1 = spapr_vio_send_crq(&s->vdev, req->crq.raw); - if (rc1) { - fprintf(stderr, "vscsi_send_iu: Error sending response\n"); - return rc1; - } - - return rc; -} - -static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req, - uint8_t key, uint8_t asc, uint8_t ascq) -{ - req->senselen = SRP_RSP_SENSE_DATA_LEN; - - /* Valid bit and 'current errors' */ - req->sense[0] = (0x1 << 7 | 0x70); - /* Sense key */ - req->sense[2] = key; - /* Additional sense length */ - req->sense[7] = 0xa; /* 10 bytes */ - /* Additional sense code */ - req->sense[12] = asc; - req->sense[13] = ascq; -} - -static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, - uint8_t status, int32_t res_in, int32_t res_out) -{ - union viosrp_iu *iu = &req->iu; - uint64_t tag = iu->srp.rsp.tag; - int total_len = sizeof(iu->srp.rsp); - uint8_t sol_not = iu->srp.cmd.sol_not; - - DPRINTF("VSCSI: Sending resp status: 0x%x, " - "res_in: %d, res_out: %d\n", status, res_in, res_out); - - memset(iu, 0, sizeof(struct srp_rsp)); - iu->srp.rsp.opcode = SRP_RSP; - iu->srp.rsp.req_lim_delta = cpu_to_be32(1); - iu->srp.rsp.tag = tag; - - /* Handle residuals */ - if (res_in < 0) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DIUNDER; - res_in = -res_in; - } else if (res_in) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; - } - if (res_out < 0) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DOUNDER; - res_out = -res_out; - } else if (res_out) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DOOVER; - } - iu->srp.rsp.data_in_res_cnt = cpu_to_be32(res_in); - iu->srp.rsp.data_out_res_cnt = cpu_to_be32(res_out); - - /* We don't do response data */ - /* iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; */ - iu->srp.rsp.resp_data_len = cpu_to_be32(0); - - /* Handle success vs. failure */ - iu->srp.rsp.status = status; - if (status) { - iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; - if (req->senselen) { - req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen); - memcpy(req->iu.srp.rsp.data, req->sense, req->senselen); - total_len += req->senselen; - } - } else { - iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; - } - - vscsi_send_iu(s, req, total_len, VIOSRP_SRP_FORMAT); - return 0; -} - -static inline struct srp_direct_buf vscsi_swap_desc(struct srp_direct_buf desc) -{ - desc.va = be64_to_cpu(desc.va); - desc.len = be32_to_cpu(desc.len); - return desc; -} - -static int vscsi_fetch_desc(VSCSIState *s, struct vscsi_req *req, - unsigned n, unsigned buf_offset, - struct srp_direct_buf *ret) -{ - struct srp_cmd *cmd = &req->iu.srp.cmd; - - switch (req->dma_fmt) { - case SRP_NO_DATA_DESC: { - DPRINTF("VSCSI: no data descriptor\n"); - return 0; - } - case SRP_DATA_DESC_DIRECT: { - memcpy(ret, cmd->add_data + req->cdb_offset, sizeof(*ret)); - assert(req->cur_desc_num == 0); - DPRINTF("VSCSI: direct segment\n"); - break; - } - case SRP_DATA_DESC_INDIRECT: { - struct srp_indirect_buf *tmp = (struct srp_indirect_buf *) - (cmd->add_data + req->cdb_offset); - if (n < req->local_desc) { - *ret = tmp->desc_list[n]; - DPRINTF("VSCSI: indirect segment local tag=0x%x desc#%d/%d\n", - req->qtag, n, req->local_desc); - - } else if (n < req->total_desc) { - int rc; - struct srp_direct_buf tbl_desc = vscsi_swap_desc(tmp->table_desc); - unsigned desc_offset = n * sizeof(struct srp_direct_buf); - - if (desc_offset >= tbl_desc.len) { - DPRINTF("VSCSI: #%d is ouf of range (%d bytes)\n", - n, desc_offset); - return -1; - } - rc = spapr_vio_dma_read(&s->vdev, tbl_desc.va + desc_offset, - ret, sizeof(struct srp_direct_buf)); - if (rc) { - DPRINTF("VSCSI: spapr_vio_dma_read -> %d reading ext_desc\n", - rc); - return -1; - } - DPRINTF("VSCSI: indirect segment ext. tag=0x%x desc#%d/%d { va=%"PRIx64" len=%x }\n", - req->qtag, n, req->total_desc, tbl_desc.va, tbl_desc.len); - } else { - DPRINTF("VSCSI: Out of descriptors !\n"); - return 0; - } - break; - } - default: - fprintf(stderr, "VSCSI: Unknown format %x\n", req->dma_fmt); - return -1; - } - - *ret = vscsi_swap_desc(*ret); - if (buf_offset > ret->len) { - DPRINTF(" offset=%x is out of a descriptor #%d boundary=%x\n", - buf_offset, req->cur_desc_num, ret->len); - return -1; - } - ret->va += buf_offset; - ret->len -= buf_offset; - - DPRINTF(" cur=%d offs=%x ret { va=%"PRIx64" len=%x }\n", - req->cur_desc_num, req->cur_desc_offset, ret->va, ret->len); - - return ret->len ? 1 : 0; -} - -static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req *req, - uint8_t *buf, uint32_t len) -{ - struct srp_direct_buf md; - uint32_t llen; - int rc = 0; - - rc = vscsi_fetch_desc(s, req, req->cur_desc_num, req->cur_desc_offset, &md); - if (rc < 0) { - return -1; - } else if (rc == 0) { - return 0; - } - - llen = MIN(len, md.len); - if (llen) { - if (req->writing) { /* writing = to device = reading from memory */ - rc = spapr_vio_dma_read(&s->vdev, md.va, buf, llen); - } else { - rc = spapr_vio_dma_write(&s->vdev, md.va, buf, llen); - } - } - - if (rc) { - return -1; - } - req->cur_desc_offset += llen; - - return llen; -} - -static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req, - uint8_t *buf, uint32_t len) -{ - struct srp_direct_buf md; - int rc = 0; - uint32_t llen, total = 0; - - DPRINTF("VSCSI: indirect segment 0x%x bytes\n", len); - - /* While we have data ... */ - while (len) { - rc = vscsi_fetch_desc(s, req, req->cur_desc_num, req->cur_desc_offset, &md); - if (rc < 0) { - return -1; - } else if (rc == 0) { - break; - } - - /* Perform transfer */ - llen = MIN(len, md.len); - if (req->writing) { /* writing = to device = reading from memory */ - rc = spapr_vio_dma_read(&s->vdev, md.va, buf, llen); - } else { - rc = spapr_vio_dma_write(&s->vdev, md.va, buf, llen); - } - if (rc) { - DPRINTF("VSCSI: spapr_vio_dma_r/w(%d) -> %d\n", req->writing, rc); - break; - } - DPRINTF("VSCSI: data: %02x %02x %02x %02x...\n", - buf[0], buf[1], buf[2], buf[3]); - - len -= llen; - buf += llen; - - total += llen; - - /* Update current position in the current descriptor */ - req->cur_desc_offset += llen; - if (md.len == llen) { - /* Go to the next descriptor if the current one finished */ - ++req->cur_desc_num; - req->cur_desc_offset = 0; - } - } - - return rc ? -1 : total; -} - -static int vscsi_srp_transfer_data(VSCSIState *s, vscsi_req *req, - int writing, uint8_t *buf, uint32_t len) -{ - int err = 0; - - switch (req->dma_fmt) { - case SRP_NO_DATA_DESC: - DPRINTF("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len); - break; - case SRP_DATA_DESC_DIRECT: - err = vscsi_srp_direct_data(s, req, buf, len); - break; - case SRP_DATA_DESC_INDIRECT: - err = vscsi_srp_indirect_data(s, req, buf, len); - break; - } - return err; -} - -/* Bits from linux srp */ -static int data_out_desc_size(struct srp_cmd *cmd) -{ - int size = 0; - uint8_t fmt = cmd->buf_fmt >> 4; - - switch (fmt) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - size = sizeof(struct srp_direct_buf); - break; - case SRP_DATA_DESC_INDIRECT: - size = sizeof(struct srp_indirect_buf) + - sizeof(struct srp_direct_buf)*cmd->data_out_desc_cnt; - break; - default: - break; - } - return size; -} - -static int vscsi_preprocess_desc(vscsi_req *req) -{ - struct srp_cmd *cmd = &req->iu.srp.cmd; - - req->cdb_offset = cmd->add_cdb_len & ~3; - - if (req->writing) { - req->dma_fmt = cmd->buf_fmt >> 4; - } else { - req->cdb_offset += data_out_desc_size(cmd); - req->dma_fmt = cmd->buf_fmt & ((1U << 4) - 1); - } - - switch (req->dma_fmt) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - req->total_desc = req->local_desc = 1; - break; - case SRP_DATA_DESC_INDIRECT: { - struct srp_indirect_buf *ind_tmp = (struct srp_indirect_buf *) - (cmd->add_data + req->cdb_offset); - - req->total_desc = be32_to_cpu(ind_tmp->table_desc.len) / - sizeof(struct srp_direct_buf); - req->local_desc = req->writing ? cmd->data_out_desc_cnt : - cmd->data_in_desc_cnt; - break; - } - default: - fprintf(stderr, - "vscsi_preprocess_desc: Unknown format %x\n", req->dma_fmt); - return -1; - } - - return 0; -} - -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent); - vscsi_req *req = sreq->hba_private; - uint8_t *buf; - int rc = 0; - - DPRINTF("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n", - sreq->tag, len, req); - if (req == NULL) { - fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); - return; - } - - if (len) { - buf = scsi_req_get_buf(sreq); - rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len); - } - if (rc < 0) { - fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc); - req->dma_error = true; - scsi_req_cancel(req->sreq); - return; - } - - /* Start next chunk */ - req->data_len -= rc; - scsi_req_continue(sreq); -} - -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status, size_t resid) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent); - vscsi_req *req = sreq->hba_private; - int32_t res_in = 0, res_out = 0; - - DPRINTF("VSCSI: SCSI cmd complete, tag=0x%x status=0x%x, req=%p\n", - sreq->tag, status, req); - if (req == NULL) { - fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); - return; - } - - if (status == CHECK_CONDITION) { - req->senselen = scsi_req_get_sense(req->sreq, req->sense, - sizeof(req->sense)); - DPRINTF("VSCSI: Sense data, %d bytes:\n", req->senselen); - DPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n", - req->sense[0], req->sense[1], req->sense[2], req->sense[3], - req->sense[4], req->sense[5], req->sense[6], req->sense[7]); - DPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n", - req->sense[8], req->sense[9], req->sense[10], req->sense[11], - req->sense[12], req->sense[13], req->sense[14], req->sense[15]); - } - - DPRINTF("VSCSI: Command complete err=%d\n", status); - if (status == 0) { - /* We handle overflows, not underflows for normal commands, - * but hopefully nobody cares - */ - if (req->writing) { - res_out = req->data_len; - } else { - res_in = req->data_len; - } - } - vscsi_send_rsp(s, req, status, res_in, res_out); - vscsi_put_req(req); -} - -static void vscsi_request_cancelled(SCSIRequest *sreq) -{ - vscsi_req *req = sreq->hba_private; - - if (req->dma_error) { - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent); - - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } - vscsi_put_req(req); -} - -static const VMStateDescription vmstate_spapr_vscsi_req = { - .name = "spapr_vscsi_req", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(crq.raw, vscsi_req), - VMSTATE_BUFFER(iu.srp.reserved, vscsi_req), - VMSTATE_UINT32(qtag, vscsi_req), - VMSTATE_BOOL(active, vscsi_req), - VMSTATE_UINT32(data_len, vscsi_req), - VMSTATE_BOOL(writing, vscsi_req), - VMSTATE_UINT32(senselen, vscsi_req), - VMSTATE_BUFFER(sense, vscsi_req), - VMSTATE_UINT8(dma_fmt, vscsi_req), - VMSTATE_UINT16(local_desc, vscsi_req), - VMSTATE_UINT16(total_desc, vscsi_req), - VMSTATE_UINT16(cdb_offset, vscsi_req), - /*Restart SCSI request from the beginning for now */ - /*VMSTATE_UINT16(cur_desc_num, vscsi_req), - VMSTATE_UINT16(cur_desc_offset, vscsi_req),*/ - VMSTATE_END_OF_LIST() - }, -}; - -static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq) -{ - vscsi_req *req = sreq->hba_private; - assert(req->active); - - vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL); - - DPRINTF("VSCSI: saving tag=%u, current desc#%d, offset=%x\n", - req->qtag, req->cur_desc_num, req->cur_desc_offset); -} - -static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq) -{ - SCSIBus *bus = sreq->bus; - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(bus->qbus.parent); - vscsi_req *req; - int rc; - - assert(sreq->tag < VSCSI_REQ_LIMIT); - req = &s->reqs[sreq->tag]; - assert(!req->active); - - memset(req, 0, sizeof(*req)); - rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1); - if (rc) { - fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag); - return NULL; - } - assert(req->active); - - req->sreq = scsi_req_ref(sreq); - - DPRINTF("VSCSI: restoring tag=%u, current desc#%d, offset=%x\n", - req->qtag, req->cur_desc_num, req->cur_desc_offset); - - return req; -} - -static void vscsi_process_login(VSCSIState *s, vscsi_req *req) -{ - union viosrp_iu *iu = &req->iu; - struct srp_login_rsp *rsp = &iu->srp.login_rsp; - uint64_t tag = iu->srp.rsp.tag; - - DPRINTF("VSCSI: Got login, sendin response !\n"); - - /* TODO handle case that requested size is wrong and - * buffer format is wrong - */ - memset(iu, 0, sizeof(struct srp_login_rsp)); - rsp->opcode = SRP_LOGIN_RSP; - /* Don't advertise quite as many request as we support to - * keep room for management stuff etc... - */ - rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2); - rsp->tag = tag; - rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); - rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); - /* direct and indirect */ - rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); - - vscsi_send_iu(s, req, sizeof(*rsp), VIOSRP_SRP_FORMAT); -} - -static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) -{ - uint8_t *cdb = req->iu.srp.cmd.cdb; - uint8_t resp_data[36]; - int rc, len, alen; - - /* We dont do EVPD. Also check that page_code is 0 */ - if ((cdb[1] & 0x01) || cdb[2] != 0) { - /* Send INVALID FIELD IN CDB */ - vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - return; - } - alen = cdb[3]; - alen = (alen << 8) | cdb[4]; - len = MIN(alen, 36); - - /* Fake up inquiry using PQ=3 */ - memset(resp_data, 0, 36); - resp_data[0] = 0x7f; /* Not capable of supporting a device here */ - resp_data[2] = 0x06; /* SPS-4 */ - resp_data[3] = 0x02; /* Resp data format */ - resp_data[4] = 36 - 5; /* Additional length */ - resp_data[7] = 0x10; /* Sync transfers */ - memcpy(&resp_data[16], "QEMU EMPTY ", 16); - memcpy(&resp_data[8], "QEMU ", 8); - - req->writing = 0; - vscsi_preprocess_desc(req); - rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); - if (rc < 0) { - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } else { - vscsi_send_rsp(s, req, 0, 36 - rc, 0); - } -} - -static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) -{ - BusChild *kid; - int i, len, n, rc; - uint8_t *resp_data; - bool found_lun0; - - n = 0; - found_lun0 = false; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *dev = SCSI_DEVICE(kid->child); - - n += 8; - if (dev->channel == 0 && dev->id == 0 && dev->lun == 0) { - found_lun0 = true; - } - } - if (!found_lun0) { - n += 8; - } - len = n+8; - - resp_data = g_malloc0(len); - stl_be_p(resp_data, n); - i = found_lun0 ? 8 : 16; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->id == 0 && dev->channel == 0) { - resp_data[i] = 0; /* Use simple LUN for 0 (SAM5 4.7.7.1) */ - } else { - resp_data[i] = (2 << 6); /* Otherwise LUN addressing (4.7.7.4) */ - } - resp_data[i] |= dev->id; - resp_data[i+1] = (dev->channel << 5); - resp_data[i+1] |= dev->lun; - i += 8; - } - - vscsi_preprocess_desc(req); - rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); - g_free(resp_data); - if (rc < 0) { - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } else { - vscsi_send_rsp(s, req, 0, len - rc, 0); - } -} - -static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) -{ - union srp_iu *srp = &req->iu.srp; - SCSIDevice *sdev; - int n, lun; - - if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN) - && srp->cmd.cdb[0] == REPORT_LUNS) { - vscsi_report_luns(s, req); - return 0; - } - - sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun); - if (!sdev) { - DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n", - be64_to_cpu(srp->cmd.lun)); - if (srp->cmd.cdb[0] == INQUIRY) { - vscsi_inquiry_no_target(s, req); - } else { - vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0x00); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } return 1; - } - - req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req); - n = scsi_req_enqueue(req->sreq); - - DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x=%s LUN %d ret: %d\n", - req->qtag, srp->cmd.cdb[0], scsi_command_name(srp->cmd.cdb[0]), - lun, n); - - if (n) { - /* Transfer direction must be set before preprocessing the - * descriptors - */ - req->writing = (n < 1); - - /* Preprocess RDMA descriptors */ - vscsi_preprocess_desc(req); - - /* Get transfer direction and initiate transfer */ - if (n > 0) { - req->data_len = n; - } else if (n < 0) { - req->data_len = -n; - } - scsi_req_continue(req->sreq); - } - /* Don't touch req here, it may have been recycled already */ - - return 0; -} - -static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) -{ - union viosrp_iu *iu = &req->iu; - vscsi_req *tmpreq; - int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE; - SCSIDevice *d; - uint64_t tag = iu->srp.rsp.tag; - uint8_t sol_not = iu->srp.cmd.sol_not; - - fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", - iu->srp.tsk_mgmt.tsk_mgmt_func); - - d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun); - if (!d) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - } else { - switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { - case SRP_TSK_ABORT_TASK: - if (d->lun != lun) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - - tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag); - if (tmpreq && tmpreq->sreq) { - assert(tmpreq->sreq->hba_private); - scsi_req_cancel(tmpreq->sreq); - } - break; - - case SRP_TSK_LUN_RESET: - if (d->lun != lun) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - - qdev_reset_all(&d->qdev); - break; - - case SRP_TSK_ABORT_TASK_SET: - case SRP_TSK_CLEAR_TASK_SET: - if (d->lun != lun) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - tmpreq = &s->reqs[i]; - if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) { - continue; - } - if (!tmpreq->active || !tmpreq->sreq) { - continue; - } - assert(tmpreq->sreq->hba_private); - scsi_req_cancel(tmpreq->sreq); - } - break; - - case SRP_TSK_CLEAR_ACA: - resp = SRP_TSK_MGMT_NOT_SUPPORTED; - break; - - default: - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - } - - /* Compose the response here as */ - memset(iu, 0, sizeof(struct srp_rsp) + 4); - iu->srp.rsp.opcode = SRP_RSP; - iu->srp.rsp.req_lim_delta = cpu_to_be32(1); - iu->srp.rsp.tag = tag; - iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID; - iu->srp.rsp.resp_data_len = cpu_to_be32(4); - if (resp) { - iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; - } else { - iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; - } - - iu->srp.rsp.status = GOOD; - iu->srp.rsp.data[3] = resp; - - vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT); - - return 1; -} - -static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) -{ - union srp_iu *srp = &req->iu.srp; - int done = 1; - uint8_t opcode = srp->rsp.opcode; - - switch (opcode) { - case SRP_LOGIN_REQ: - vscsi_process_login(s, req); - break; - case SRP_TSK_MGMT: - done = vscsi_process_tsk_mgmt(s, req); - break; - case SRP_CMD: - done = vscsi_queue_cmd(s, req); - break; - case SRP_LOGIN_RSP: - case SRP_I_LOGOUT: - case SRP_T_LOGOUT: - case SRP_RSP: - case SRP_CRED_REQ: - case SRP_CRED_RSP: - case SRP_AER_REQ: - case SRP_AER_RSP: - fprintf(stderr, "VSCSI: Unsupported opcode %02x\n", opcode); - break; - default: - fprintf(stderr, "VSCSI: Unknown type %02x\n", opcode); - } - - return done; -} - -static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) -{ - struct viosrp_adapter_info *sinfo; - struct mad_adapter_info_data info; - int rc; - - sinfo = &req->iu.mad.adapter_info; - -#if 0 /* What for ? */ - rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), - &info, be16_to_cpu(sinfo->common.length)); - if (rc) { - fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n"); - } -#endif - memset(&info, 0, sizeof(info)); - strcpy(info.srp_version, SRP_VERSION); - memcpy(info.partition_name, "qemu", sizeof("qemu")); - info.partition_number = cpu_to_be32(0); - info.mad_version = cpu_to_be32(1); - info.os_type = cpu_to_be32(2); - info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9); - - rc = spapr_vio_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer), - &info, be16_to_cpu(sinfo->common.length)); - if (rc) { - fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n"); - } - - sinfo->common.status = rc ? cpu_to_be32(1) : 0; - - return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT); -} - -static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) -{ - struct viosrp_capabilities *vcap; - struct capabilities cap = { }; - uint16_t len, req_len; - uint64_t buffer; - int rc; - - vcap = &req->iu.mad.capabilities; - req_len = len = be16_to_cpu(vcap->common.length); - buffer = be64_to_cpu(vcap->buffer); - if (len > sizeof(cap)) { - fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n"); - - /* - * Just read and populate the structure that is known. - * Zero rest of the structure. - */ - len = sizeof(cap); - } - rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len); - if (rc) { - fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n"); - } - - /* - * Current implementation does not suppport any migration or - * reservation capabilities. Construct the response telling the - * guest not to use them. - */ - cap.flags = 0; - cap.migration.ecl = 0; - cap.reserve.type = 0; - cap.migration.common.server_support = 0; - cap.reserve.common.server_support = 0; - - rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len); - if (rc) { - fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n"); - } - if (req_len > len) { - /* - * Being paranoid and lets not worry about the error code - * here. Actual write of the cap is done above. - */ - spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len)); - } - vcap->common.status = rc ? cpu_to_be32(1) : 0; - return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT); -} - -static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) -{ - union mad_iu *mad = &req->iu.mad; - bool request_handled = false; - uint64_t retlen = 0; - - switch (be32_to_cpu(mad->empty_iu.common.type)) { - case VIOSRP_EMPTY_IU_TYPE: - fprintf(stderr, "Unsupported EMPTY MAD IU\n"); - retlen = sizeof(mad->empty_iu); - break; - case VIOSRP_ERROR_LOG_TYPE: - fprintf(stderr, "Unsupported ERROR LOG MAD IU\n"); - retlen = sizeof(mad->error_log); - break; - case VIOSRP_ADAPTER_INFO_TYPE: - vscsi_send_adapter_info(s, req); - request_handled = true; - break; - case VIOSRP_HOST_CONFIG_TYPE: - retlen = sizeof(mad->host_config); - break; - case VIOSRP_CAPABILITIES_TYPE: - vscsi_send_capabilities(s, req); - request_handled = true; - break; - default: - fprintf(stderr, "VSCSI: Unknown MAD type %02x\n", - be32_to_cpu(mad->empty_iu.common.type)); - /* - * PAPR+ says that "The length field is set to the length - * of the data structure(s) used in the command". - * As we did not recognize the request type, put zero there. - */ - retlen = 0; - } - - if (!request_handled) { - mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); - vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT); - } - - return 1; -} - -static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) -{ - vscsi_req *req; - int done; - - req = vscsi_get_req(s); - if (req == NULL) { - fprintf(stderr, "VSCSI: Failed to get a request !\n"); - return; - } - - /* We only support a limited number of descriptors, we know - * the ibmvscsi driver uses up to 10 max, so it should fit - * in our 256 bytes IUs. If not we'll have to increase the size - * of the structure. - */ - if (crq->s.IU_length > sizeof(union viosrp_iu)) { - fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", - crq->s.IU_length); - vscsi_put_req(req); - return; - } - - /* XXX Handle failure differently ? */ - if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, - crq->s.IU_length)) { - fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); - vscsi_put_req(req); - return; - } - memcpy(&req->crq, crq, sizeof(vscsi_crq)); - - if (crq->s.format == VIOSRP_MAD_FORMAT) { - done = vscsi_handle_mad_req(s, req); - } else { - done = vscsi_handle_srp_req(s, req); - } - - if (done) { - vscsi_put_req(req); - } -} - - -static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - vscsi_crq crq; - - memcpy(crq.raw, crq_data, 16); - crq.s.timeout = be16_to_cpu(crq.s.timeout); - crq.s.IU_length = be16_to_cpu(crq.s.IU_length); - crq.s.IU_data_ptr = be64_to_cpu(crq.s.IU_data_ptr); - - DPRINTF("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]); - - switch (crq.s.valid) { - case 0xc0: /* Init command/response */ - - /* Respond to initialization request */ - if (crq.s.format == 0x01) { - memset(crq.raw, 0, 16); - crq.s.valid = 0xc0; - crq.s.format = 0x02; - spapr_vio_send_crq(dev, crq.raw); - } - - /* Note that in hotplug cases, we might get a 0x02 - * as a result of us emitting the init request - */ - - break; - case 0xff: /* Link event */ - - /* Not handled for now */ - - break; - case 0x80: /* Payloads */ - switch (crq.s.format) { - case VIOSRP_SRP_FORMAT: /* AKA VSCSI request */ - case VIOSRP_MAD_FORMAT: /* AKA VSCSI response */ - vscsi_got_payload(s, &crq); - break; - case VIOSRP_OS400_FORMAT: - case VIOSRP_AIX_FORMAT: - case VIOSRP_LINUX_FORMAT: - case VIOSRP_INLINE_FORMAT: - fprintf(stderr, "vscsi_do_srq: Unsupported payload format %02x\n", - crq.s.format); - break; - default: - fprintf(stderr, "vscsi_do_srq: Unknown payload format %02x\n", - crq.s.format); - } - break; - default: - fprintf(stderr, "vscsi_do_crq: unknown CRQ %02x %02x ...\n", - crq.raw[0], crq.raw[1]); - }; - - return 0; -} - -static const struct SCSIBusInfo vscsi_scsi_info = { - .tcq = true, - .max_channel = 7, /* logical unit addressing format */ - .max_target = 63, - .max_lun = 31, - - .transfer_data = vscsi_transfer_data, - .complete = vscsi_command_complete, - .cancel = vscsi_request_cancelled, - .save_request = vscsi_save_request, - .load_request = vscsi_load_request, -}; - -static void spapr_vscsi_reset(VIOsPAPRDevice *dev) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - int i; - - memset(s->reqs, 0, sizeof(s->reqs)); - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - s->reqs[i].qtag = i; - } -} - -static void spapr_vscsi_realize(VIOsPAPRDevice *dev, Error **errp) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - - dev->crq.SendFunc = vscsi_do_crq; - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &vscsi_scsi_info, NULL); - if (!dev->qdev.hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -void spapr_vscsi_create(VIOsPAPRBus *bus) -{ - DeviceState *dev; - - dev = qdev_create(&bus->bus, "spapr-vscsi"); - - qdev_init_nofail(dev); -} - -static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) -{ - int ret; - - ret = fdt_setprop_cell(fdt, node_off, "#address-cells", 2); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, node_off, "#size-cells", 0); - if (ret < 0) { - return ret; - } - - return 0; -} - -static Property spapr_vscsi_properties[] = { - DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_spapr_vscsi = { - .name = "spapr_vscsi", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SPAPR_VIO(vdev, VSCSIState), - /* VSCSI state */ - /* ???? */ - - VMSTATE_END_OF_LIST() - }, -}; - -static void spapr_vscsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - - k->realize = spapr_vscsi_realize; - k->reset = spapr_vscsi_reset; - k->devnode = spapr_vscsi_devnode; - k->dt_name = "v-scsi"; - k->dt_type = "vscsi"; - k->dt_compatible = "IBM,v-scsi"; - k->signal_mask = 0x00000001; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = spapr_vscsi_properties; - k->rtce_window_size = 0x10000000; - dc->vmsd = &vmstate_spapr_vscsi; -} - -static const TypeInfo spapr_vscsi_info = { - .name = TYPE_VIO_SPAPR_VSCSI_DEVICE, - .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(VSCSIState), - .class_init = spapr_vscsi_class_init, -}; - -static void spapr_vscsi_register_types(void) -{ - type_register_static(&spapr_vscsi_info); -} - -type_init(spapr_vscsi_register_types) diff --git a/qemu/hw/scsi/srp.h b/qemu/hw/scsi/srp.h deleted file mode 100644 index d27f31d2d..000000000 --- a/qemu/hw/scsi/srp.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2005 Cisco Systems. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#ifndef SCSI_SRP_H -#define SCSI_SRP_H - -/* - * Structures and constants for the SCSI RDMA Protocol (SRP) as - * defined by the INCITS T10 committee. This file was written using - * draft Revision 16a of the SRP standard. - */ - -enum { - - SRP_LOGIN_REQ = 0x00, - SRP_TSK_MGMT = 0x01, - SRP_CMD = 0x02, - SRP_I_LOGOUT = 0x03, - SRP_LOGIN_RSP = 0xc0, - SRP_RSP = 0xc1, - SRP_LOGIN_REJ = 0xc2, - SRP_T_LOGOUT = 0x80, - SRP_CRED_REQ = 0x81, - SRP_AER_REQ = 0x82, - SRP_CRED_RSP = 0x41, - SRP_AER_RSP = 0x42 -}; - -enum { - SRP_BUF_FORMAT_DIRECT = 1 << 1, - SRP_BUF_FORMAT_INDIRECT = 1 << 2 -}; - -enum { - SRP_NO_DATA_DESC = 0, - SRP_DATA_DESC_DIRECT = 1, - SRP_DATA_DESC_INDIRECT = 2 -}; - -enum { - SRP_TSK_ABORT_TASK = 0x01, - SRP_TSK_ABORT_TASK_SET = 0x02, - SRP_TSK_CLEAR_TASK_SET = 0x04, - SRP_TSK_LUN_RESET = 0x08, - SRP_TSK_CLEAR_ACA = 0x40 -}; - -enum srp_login_rej_reason { - SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL = 0x00010000, - SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES = 0x00010001, - SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE = 0x00010002, - SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL = 0x00010003, - SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT = 0x00010004, - SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED = 0x00010005, - SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED = 0x00010006 -}; - -enum { - SRP_REV10_IB_IO_CLASS = 0xff00, - SRP_REV16A_IB_IO_CLASS = 0x0100 -}; - -enum { - SRP_TSK_MGMT_COMPLETE = 0x00, - SRP_TSK_MGMT_FIELDS_INVALID = 0x02, - SRP_TSK_MGMT_NOT_SUPPORTED = 0x04, - SRP_TSK_MGMT_FAILED = 0x05 -}; - -struct srp_direct_buf { - uint64_t va; - uint32_t key; - uint32_t len; -}; - -/* - * We need the packed attribute because the SRP spec puts the list of - * descriptors at an offset of 20, which is not aligned to the size of - * struct srp_direct_buf. The whole structure must be packed to avoid - * having the 20-byte structure padded to 24 bytes on 64-bit architectures. - */ -struct srp_indirect_buf { - struct srp_direct_buf table_desc; - uint32_t len; - struct srp_direct_buf desc_list[0]; -} QEMU_PACKED; - -enum { - SRP_MULTICHAN_SINGLE = 0, - SRP_MULTICHAN_MULTI = 1 -}; - -struct srp_login_req { - uint8_t opcode; - uint8_t reserved1[7]; - uint64_t tag; - uint32_t req_it_iu_len; - uint8_t reserved2[4]; - uint16_t req_buf_fmt; - uint8_t req_flags; - uint8_t reserved3[5]; - uint8_t initiator_port_id[16]; - uint8_t target_port_id[16]; -}; - -/* - * The SRP spec defines the size of the LOGIN_RSP structure to be 52 - * bytes, so it needs to be packed to avoid having it padded to 56 - * bytes on 64-bit architectures. - */ -struct srp_login_rsp { - uint8_t opcode; - uint8_t reserved1[3]; - uint32_t req_lim_delta; - uint64_t tag; - uint32_t max_it_iu_len; - uint32_t max_ti_iu_len; - uint16_t buf_fmt; - uint8_t rsp_flags; - uint8_t reserved2[25]; -} QEMU_PACKED; - -struct srp_login_rej { - uint8_t opcode; - uint8_t reserved1[3]; - uint32_t reason; - uint64_t tag; - uint8_t reserved2[8]; - uint16_t buf_fmt; - uint8_t reserved3[6]; -}; - -struct srp_i_logout { - uint8_t opcode; - uint8_t reserved[7]; - uint64_t tag; -}; - -struct srp_t_logout { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved[2]; - uint32_t reason; - uint64_t tag; -}; - -/* - * We need the packed attribute because the SRP spec only aligns the - * 8-byte LUN field to 4 bytes. - */ -struct srp_tsk_mgmt { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved1[6]; - uint64_t tag; - uint8_t reserved2[4]; - uint64_t lun; - uint8_t reserved3[2]; - uint8_t tsk_mgmt_func; - uint8_t reserved4; - uint64_t task_tag; - uint8_t reserved5[8]; -} QEMU_PACKED; - -/* - * We need the packed attribute because the SRP spec only aligns the - * 8-byte LUN field to 4 bytes. - */ -struct srp_cmd { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved1[3]; - uint8_t buf_fmt; - uint8_t data_out_desc_cnt; - uint8_t data_in_desc_cnt; - uint64_t tag; - uint8_t reserved2[4]; - uint64_t lun; - uint8_t reserved3; - uint8_t task_attr; - uint8_t reserved4; - uint8_t add_cdb_len; - uint8_t cdb[16]; - uint8_t add_data[0]; -} QEMU_PACKED; - -enum { - SRP_RSP_FLAG_RSPVALID = 1 << 0, - SRP_RSP_FLAG_SNSVALID = 1 << 1, - SRP_RSP_FLAG_DOOVER = 1 << 2, - SRP_RSP_FLAG_DOUNDER = 1 << 3, - SRP_RSP_FLAG_DIOVER = 1 << 4, - SRP_RSP_FLAG_DIUNDER = 1 << 5 -}; - -/* - * The SRP spec defines the size of the RSP structure to be 36 bytes, - * so it needs to be packed to avoid having it padded to 40 bytes on - * 64-bit architectures. - */ -struct srp_rsp { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved1[2]; - uint32_t req_lim_delta; - uint64_t tag; - uint8_t reserved2[2]; - uint8_t flags; - uint8_t status; - uint32_t data_out_res_cnt; - uint32_t data_in_res_cnt; - uint32_t sense_data_len; - uint32_t resp_data_len; - uint8_t data[0]; -} QEMU_PACKED; - -#endif /* SCSI_SRP_H */ diff --git a/qemu/hw/scsi/vhost-scsi.c b/qemu/hw/scsi/vhost-scsi.c deleted file mode 100644 index 9261d51da..000000000 --- a/qemu/hw/scsi/vhost-scsi.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * vhost_scsi host device - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> - * - * Changes for QEMU mainline + tcm_vhost kernel upstream: - * Nicholas Bellinger <nab@risingtidesystems.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include <sys/ioctl.h> -#include "qemu/error-report.h" -#include "qemu/queue.h" -#include "monitor/monitor.h" -#include "migration/migration.h" -#include "hw/virtio/vhost-scsi.h" -#include "hw/virtio/vhost.h" -#include "hw/virtio/virtio-scsi.h" -#include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" -#include "hw/fw-path-provider.h" -#include "linux/vhost.h" -#include "qemu/cutils.h" - -/* Features supported by host kernel. */ -static const int kernel_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - VIRTIO_SCSI_F_HOTPLUG, - VHOST_INVALID_FEATURE_BIT -}; - -static int vhost_scsi_set_endpoint(VHostSCSI *s) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - const VhostOps *vhost_ops = s->dev.vhost_ops; - struct vhost_scsi_target backend; - int ret; - - memset(&backend, 0, sizeof(backend)); - pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - ret = vhost_ops->vhost_scsi_set_endpoint(&s->dev, &backend); - if (ret < 0) { - return -errno; - } - return 0; -} - -static void vhost_scsi_clear_endpoint(VHostSCSI *s) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - struct vhost_scsi_target backend; - const VhostOps *vhost_ops = s->dev.vhost_ops; - - memset(&backend, 0, sizeof(backend)); - pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - vhost_ops->vhost_scsi_clear_endpoint(&s->dev, &backend); -} - -static int vhost_scsi_start(VHostSCSI *s) -{ - int ret, abi_version, i; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - const VhostOps *vhost_ops = s->dev.vhost_ops; - - if (!k->set_guest_notifiers) { - error_report("binding does not support guest notifiers"); - return -ENOSYS; - } - - ret = vhost_ops->vhost_scsi_get_abi_version(&s->dev, &abi_version); - if (ret < 0) { - return -errno; - } - if (abi_version > VHOST_SCSI_ABI_VERSION) { - error_report("vhost-scsi: The running tcm_vhost kernel abi_version:" - " %d is greater than vhost_scsi userspace supports: %d, please" - " upgrade your version of QEMU", abi_version, - VHOST_SCSI_ABI_VERSION); - return -ENOSYS; - } - - ret = vhost_dev_enable_notifiers(&s->dev, vdev); - if (ret < 0) { - return ret; - } - - s->dev.acked_features = vdev->guest_features; - ret = vhost_dev_start(&s->dev, vdev); - if (ret < 0) { - error_report("Error start vhost dev"); - goto err_notifiers; - } - - ret = vhost_scsi_set_endpoint(s); - if (ret < 0) { - error_report("Error set vhost-scsi endpoint"); - goto err_vhost_stop; - } - - ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); - if (ret < 0) { - error_report("Error binding guest notifier"); - goto err_endpoint; - } - - /* guest_notifier_mask/pending not used yet, so just unmask - * everything here. virtio-pci will do the right thing by - * enabling/disabling irqfd. - */ - for (i = 0; i < s->dev.nvqs; i++) { - vhost_virtqueue_mask(&s->dev, vdev, s->dev.vq_index + i, false); - } - - return ret; - -err_endpoint: - vhost_scsi_clear_endpoint(s); -err_vhost_stop: - vhost_dev_stop(&s->dev, vdev); -err_notifiers: - vhost_dev_disable_notifiers(&s->dev, vdev); - return ret; -} - -static void vhost_scsi_stop(VHostSCSI *s) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(s); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret = 0; - - if (k->set_guest_notifiers) { - ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); - } - } - assert(ret >= 0); - - vhost_scsi_clear_endpoint(s); - vhost_dev_stop(&s->dev, vdev); - vhost_dev_disable_notifiers(&s->dev, vdev); -} - -static uint64_t vhost_scsi_get_features(VirtIODevice *vdev, - uint64_t features, - Error **errp) -{ - VHostSCSI *s = VHOST_SCSI(vdev); - - return vhost_get_features(&s->dev, kernel_feature_bits, features); -} - -static void vhost_scsi_set_config(VirtIODevice *vdev, - const uint8_t *config) -{ - VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) != vs->sense_size || - (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) != vs->cdb_size) { - error_report("vhost-scsi does not support changing the sense data and CDB sizes"); - exit(1); - } -} - -static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) -{ - VHostSCSI *s = (VHostSCSI *)vdev; - bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK); - - if (s->dev.started == start) { - return; - } - - if (start) { - int ret; - - ret = vhost_scsi_start(s); - if (ret < 0) { - error_report("virtio-scsi: unable to start vhost: %s", - strerror(-ret)); - - /* There is no userspace virtio-scsi fallback so exit */ - exit(1); - } - } else { - vhost_scsi_stop(s); - } -} - -static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ -} - -static void vhost_scsi_realize(DeviceState *dev, Error **errp) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); - VHostSCSI *s = VHOST_SCSI(dev); - Error *err = NULL; - int vhostfd = -1; - int ret; - - if (!vs->conf.wwpn) { - error_setg(errp, "vhost-scsi: missing wwpn"); - return; - } - - if (vs->conf.vhostfd) { - vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); - if (vhostfd == -1) { - error_prepend(errp, "vhost-scsi: unable to parse vhostfd: "); - return; - } - } else { - vhostfd = open("/dev/vhost-scsi", O_RDWR); - if (vhostfd < 0) { - error_setg(errp, "vhost-scsi: open vhost char device failed: %s", - strerror(errno)); - return; - } - } - - virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, - vhost_dummy_handle_output, - vhost_dummy_handle_output); - if (err != NULL) { - error_propagate(errp, err); - close(vhostfd); - return; - } - - s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; - s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); - s->dev.vq_index = 0; - s->dev.backend_features = 0; - - ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL); - if (ret < 0) { - error_setg(errp, "vhost-scsi: vhost initialization failed: %s", - strerror(-ret)); - return; - } - - /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ - s->channel = 0; - s->lun = 0; - /* Note: we can also get the minimum tpgt from kernel */ - s->target = vs->conf.boot_tpgt; - - error_setg(&s->migration_blocker, - "vhost-scsi does not support migration"); - migrate_add_blocker(s->migration_blocker); -} - -static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostSCSI *s = VHOST_SCSI(dev); - - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); - - /* This will stop vhost backend. */ - vhost_scsi_set_status(vdev, 0); - - vhost_dev_cleanup(&s->dev); - g_free(s->dev.vqs); - - virtio_scsi_common_unrealize(dev, errp); -} - -/* - * Implementation of an interface to adjust firmware path - * for the bootindex property handling. - */ -static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus, - DeviceState *dev) -{ - VHostSCSI *s = VHOST_SCSI(dev); - /* format: channel@channel/vhost-scsi@target,lun */ - return g_strdup_printf("/channel@%x/%s@%x,%x", s->channel, - qdev_fw_name(dev), s->target, s->lun); -} - -static Property vhost_scsi_properties[] = { - DEFINE_PROP_STRING("vhostfd", VHostSCSI, parent_obj.conf.vhostfd), - DEFINE_PROP_STRING("wwpn", VHostSCSI, parent_obj.conf.wwpn), - DEFINE_PROP_UINT32("boot_tpgt", VHostSCSI, parent_obj.conf.boot_tpgt, 0), - DEFINE_PROP_UINT32("num_queues", VHostSCSI, parent_obj.conf.num_queues, 1), - DEFINE_PROP_UINT32("max_sectors", VHostSCSI, parent_obj.conf.max_sectors, - 0xFFFF), - DEFINE_PROP_UINT32("cmd_per_lun", VHostSCSI, parent_obj.conf.cmd_per_lun, - 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); - - dc->props = vhost_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - vdc->realize = vhost_scsi_realize; - vdc->unrealize = vhost_scsi_unrealize; - vdc->get_features = vhost_scsi_get_features; - vdc->set_config = vhost_scsi_set_config; - vdc->set_status = vhost_scsi_set_status; - fwc->get_dev_path = vhost_scsi_get_fw_dev_path; -} - -static void vhost_scsi_instance_init(Object *obj) -{ - VHostSCSI *dev = VHOST_SCSI(obj); - - device_add_bootindex_property(obj, &dev->bootindex, "bootindex", NULL, - DEVICE(dev), NULL); -} - -static const TypeInfo vhost_scsi_info = { - .name = TYPE_VHOST_SCSI, - .parent = TYPE_VIRTIO_SCSI_COMMON, - .instance_size = sizeof(VHostSCSI), - .class_init = vhost_scsi_class_init, - .instance_init = vhost_scsi_instance_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_FW_PATH_PROVIDER }, - { } - }, -}; - -static void virtio_register_types(void) -{ - type_register_static(&vhost_scsi_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/scsi/viosrp.h b/qemu/hw/scsi/viosrp.h deleted file mode 100644 index d8e365db1..000000000 --- a/qemu/hw/scsi/viosrp.h +++ /dev/null @@ -1,216 +0,0 @@ -/*****************************************************************************/ -/* srp.h -- SCSI RDMA Protocol definitions */ -/* */ -/* Written By: Colin Devilbis, IBM Corporation */ -/* */ -/* Copyright (C) 2003 IBM Corporation */ -/* */ -/* This program is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* */ -/* */ -/* This file contains structures and definitions for IBM RPA (RS/6000 */ -/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */ -/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */ -/* commands between logical partitions. */ -/* */ -/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */ -/* between partitions. The definitions in this file are architected, */ -/* and cannot be changed without breaking compatibility with other versions */ -/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/ -/* between logical partitions */ -/*****************************************************************************/ -#ifndef PPC_VIOSRP_H -#define PPC_VIOSRP_H - -#define SRP_VERSION "16.a" -#define SRP_MAX_IU_LEN 256 -#define SRP_MAX_LOC_LEN 32 - -union srp_iu { - struct srp_login_req login_req; - struct srp_login_rsp login_rsp; - struct srp_login_rej login_rej; - struct srp_i_logout i_logout; - struct srp_t_logout t_logout; - struct srp_tsk_mgmt tsk_mgmt; - struct srp_cmd cmd; - struct srp_rsp rsp; - uint8_t reserved[SRP_MAX_IU_LEN]; -}; - -enum viosrp_crq_formats { - VIOSRP_SRP_FORMAT = 0x01, - VIOSRP_MAD_FORMAT = 0x02, - VIOSRP_OS400_FORMAT = 0x03, - VIOSRP_AIX_FORMAT = 0x04, - VIOSRP_LINUX_FORMAT = 0x06, - VIOSRP_INLINE_FORMAT = 0x07 -}; - -enum viosrp_crq_status { - VIOSRP_OK = 0x0, - VIOSRP_NONRECOVERABLE_ERR = 0x1, - VIOSRP_VIOLATES_MAX_XFER = 0x2, - VIOSRP_PARTNER_PANIC = 0x3, - VIOSRP_DEVICE_BUSY = 0x8, - VIOSRP_ADAPTER_FAIL = 0x10, - VIOSRP_OK2 = 0x99, -}; - -struct viosrp_crq { - uint8_t valid; /* used by RPA */ - uint8_t format; /* SCSI vs out-of-band */ - uint8_t reserved; - uint8_t status; /* non-scsi failure? (e.g. DMA failure) */ - uint16_t timeout; /* in seconds */ - uint16_t IU_length; /* in bytes */ - uint64_t IU_data_ptr; /* the TCE for transferring data */ -}; - -/* MADs are Management requests above and beyond the IUs defined in the SRP - * standard. - */ -enum viosrp_mad_types { - VIOSRP_EMPTY_IU_TYPE = 0x01, - VIOSRP_ERROR_LOG_TYPE = 0x02, - VIOSRP_ADAPTER_INFO_TYPE = 0x03, - VIOSRP_HOST_CONFIG_TYPE = 0x04, - VIOSRP_CAPABILITIES_TYPE = 0x05, - VIOSRP_ENABLE_FAST_FAIL = 0x08, -}; - -enum viosrp_mad_status { - VIOSRP_MAD_SUCCESS = 0x00, - VIOSRP_MAD_NOT_SUPPORTED = 0xF1, - VIOSRP_MAD_FAILED = 0xF7, -}; - -enum viosrp_capability_type { - MIGRATION_CAPABILITIES = 0x01, - RESERVATION_CAPABILITIES = 0x02, -}; - -enum viosrp_capability_support { - SERVER_DOES_NOT_SUPPORTS_CAP = 0x0, - SERVER_SUPPORTS_CAP = 0x01, - SERVER_CAP_DATA = 0x02, -}; - -enum viosrp_reserve_type { - CLIENT_RESERVE_SCSI_2 = 0x01, -}; - -enum viosrp_capability_flag { - CLIENT_MIGRATED = 0x01, - CLIENT_RECONNECT = 0x02, - CAP_LIST_SUPPORTED = 0x04, - CAP_LIST_DATA = 0x08, -}; - -/* - * Common MAD header - */ -struct mad_common { - uint32_t type; - uint16_t status; - uint16_t length; - uint64_t tag; -}; - -/* - * All SRP (and MAD) requests normally flow from the - * client to the server. There is no way for the server to send - * an asynchronous message back to the client. The Empty IU is used - * to hang out a meaningless request to the server so that it can respond - * asynchrouously with something like a SCSI AER - */ -struct viosrp_empty_iu { - struct mad_common common; - uint64_t buffer; - uint32_t port; -}; - -struct viosrp_error_log { - struct mad_common common; - uint64_t buffer; -}; - -struct viosrp_adapter_info { - struct mad_common common; - uint64_t buffer; -}; - -struct viosrp_host_config { - struct mad_common common; - uint64_t buffer; -}; - -struct viosrp_fast_fail { - struct mad_common common; -}; - -struct viosrp_capabilities { - struct mad_common common; - uint64_t buffer; -}; - -struct mad_capability_common { - uint32_t cap_type; - uint16_t length; - uint16_t server_support; -}; - -struct mad_reserve_cap { - struct mad_capability_common common; - uint32_t type; -}; - -struct mad_migration_cap { - struct mad_capability_common common; - uint32_t ecl; -}; - -struct capabilities { - uint32_t flags; - char name[SRP_MAX_LOC_LEN]; - char loc[SRP_MAX_LOC_LEN]; - struct mad_migration_cap migration; - struct mad_reserve_cap reserve; -}; - -union mad_iu { - struct viosrp_empty_iu empty_iu; - struct viosrp_error_log error_log; - struct viosrp_adapter_info adapter_info; - struct viosrp_host_config host_config; - struct viosrp_fast_fail fast_fail; - struct viosrp_capabilities capabilities; -}; - -union viosrp_iu { - union srp_iu srp; - union mad_iu mad; -}; - -struct mad_adapter_info_data { - char srp_version[8]; - char partition_name[96]; - uint32_t partition_number; - uint32_t mad_version; - uint32_t os_type; - uint32_t port_max_txu[8]; /* per-port maximum transfer */ -}; - -#endif diff --git a/qemu/hw/scsi/virtio-scsi-dataplane.c b/qemu/hw/scsi/virtio-scsi-dataplane.c deleted file mode 100644 index 1a49f1e4b..000000000 --- a/qemu/hw/scsi/virtio-scsi-dataplane.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Virtio SCSI dataplane - * - * Copyright Red Hat, Inc. 2014 - * - * Authors: - * Fam Zheng <famz@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/virtio/virtio-scsi.h" -#include "qemu/error-report.h" -#include "sysemu/block-backend.h" -#include <hw/scsi/scsi.h> -#include <block/scsi.h> -#include <hw/virtio/virtio-bus.h> -#include "hw/virtio/virtio-access.h" - -/* Context: QEMU global mutex held */ -void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - - assert(!s->ctx); - s->ctx = iothread_get_aio_context(vs->conf.iothread); - - /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || !k->set_host_notifier) { - fprintf(stderr, "virtio-scsi: Failed to set iothread " - "(transport does not support notifiers)"); - exit(1); - } -} - -static void virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOSCSI *s = (VirtIOSCSI *)vdev; - - assert(s->ctx && s->dataplane_started); - virtio_scsi_handle_cmd_vq(s, vq); -} - -static void virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - assert(s->ctx && s->dataplane_started); - virtio_scsi_handle_ctrl_vq(s, vq); -} - -static void virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - assert(s->ctx && s->dataplane_started); - virtio_scsi_handle_event_vq(s, vq); -} - -static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, - void (*fn)(VirtIODevice *vdev, VirtQueue *vq)) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int rc; - - /* Set up virtqueue notify */ - rc = k->set_host_notifier(qbus->parent, n, true); - if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", - rc); - s->dataplane_fenced = true; - return rc; - } - - virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); - return 0; -} - -void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req) -{ - if (virtio_should_notify(vdev, req->vq)) { - event_notifier_set(virtio_queue_get_guest_notifier(req->vq)); - } -} - -/* assumes s->ctx held */ -static void virtio_scsi_clear_aio(VirtIOSCSI *s) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - int i; - - virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); - virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); - for (i = 0; i < vs->conf.num_queues; i++) { - virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); - } -} - -/* Context: QEMU global mutex held */ -void virtio_scsi_dataplane_start(VirtIOSCSI *s) -{ - int i; - int rc; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - - if (s->dataplane_started || - s->dataplane_starting || - s->dataplane_fenced || - s->ctx != iothread_get_aio_context(vs->conf.iothread)) { - return; - } - - s->dataplane_starting = true; - - /* Set up guest notifier (irq) */ - rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); - if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " - "ensure -enable-kvm is set\n", rc); - goto fail_guest_notifiers; - } - - aio_context_acquire(s->ctx); - rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, - virtio_scsi_data_plane_handle_ctrl); - if (rc) { - goto fail_vrings; - } - rc = virtio_scsi_vring_init(s, vs->event_vq, 1, - virtio_scsi_data_plane_handle_event); - if (rc) { - goto fail_vrings; - } - for (i = 0; i < vs->conf.num_queues; i++) { - rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, - virtio_scsi_data_plane_handle_cmd); - if (rc) { - goto fail_vrings; - } - } - - s->dataplane_starting = false; - s->dataplane_started = true; - aio_context_release(s->ctx); - return; - -fail_vrings: - virtio_scsi_clear_aio(s); - aio_context_release(s->ctx); - for (i = 0; i < vs->conf.num_queues + 2; i++) { - k->set_host_notifier(qbus->parent, i, false); - } - k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); -fail_guest_notifiers: - s->dataplane_fenced = true; - s->dataplane_starting = false; - s->dataplane_started = true; -} - -/* Context: QEMU global mutex held */ -void virtio_scsi_dataplane_stop(VirtIOSCSI *s) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - int i; - - if (!s->dataplane_started || s->dataplane_stopping) { - return; - } - - /* Better luck next time. */ - if (s->dataplane_fenced) { - s->dataplane_fenced = false; - s->dataplane_started = false; - return; - } - s->dataplane_stopping = true; - assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); - - aio_context_acquire(s->ctx); - - virtio_scsi_clear_aio(s); - - blk_drain_all(); /* ensure there are no in-flight requests */ - - aio_context_release(s->ctx); - - for (i = 0; i < vs->conf.num_queues + 2; i++) { - k->set_host_notifier(qbus->parent, i, false); - } - - /* Clean up guest notifier (irq) */ - k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); - s->dataplane_stopping = false; - s->dataplane_started = false; -} diff --git a/qemu/hw/scsi/virtio-scsi.c b/qemu/hw/scsi/virtio-scsi.c deleted file mode 100644 index 30415c6a9..000000000 --- a/qemu/hw/scsi/virtio-scsi.c +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * Virtio SCSI HBA - * - * Copyright IBM, Corp. 2010 - * Copyright Red Hat, Inc. 2011 - * - * Authors: - * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> - * Paolo Bonzini <pbonzini@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "standard-headers/linux/virtio_ids.h" -#include "hw/virtio/virtio-scsi.h" -#include "qemu/error-report.h" -#include "qemu/iov.h" -#include "sysemu/block-backend.h" -#include <hw/scsi/scsi.h> -#include <block/scsi.h> -#include <hw/virtio/virtio-bus.h> -#include "hw/virtio/virtio-access.h" - -static inline int virtio_scsi_get_lun(uint8_t *lun) -{ - return ((lun[2] << 8) | lun[3]) & 0x3FFF; -} - -static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) -{ - if (lun[0] != 1) { - return NULL; - } - if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) { - return NULL; - } - return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); -} - -void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req) -{ - const size_t zero_skip = - offsetof(VirtIOSCSIReq, resp_iov) + sizeof(req->resp_iov); - - req->vq = vq; - req->dev = s; - qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory); - qemu_iovec_init(&req->resp_iov, 1); - memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip); -} - -void virtio_scsi_free_req(VirtIOSCSIReq *req) -{ - qemu_iovec_destroy(&req->resp_iov); - qemu_sglist_destroy(&req->qsgl); - g_free(req); -} - -static void virtio_scsi_complete_req(VirtIOSCSIReq *req) -{ - VirtIOSCSI *s = req->dev; - VirtQueue *vq = req->vq; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); - virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); - if (s->dataplane_started && !s->dataplane_fenced) { - virtio_scsi_dataplane_notify(vdev, req); - } else { - virtio_notify(vdev, vq); - } - - if (req->sreq) { - req->sreq->hba_private = NULL; - scsi_req_unref(req->sreq); - } - virtio_scsi_free_req(req); -} - -static void virtio_scsi_bad_req(void) -{ - error_report("wrong size for virtio-scsi headers"); - exit(1); -} - -static size_t qemu_sgl_concat(VirtIOSCSIReq *req, struct iovec *iov, - hwaddr *addr, int num, size_t skip) -{ - QEMUSGList *qsgl = &req->qsgl; - size_t copied = 0; - - while (num) { - if (skip >= iov->iov_len) { - skip -= iov->iov_len; - } else { - qemu_sglist_add(qsgl, *addr + skip, iov->iov_len - skip); - copied += iov->iov_len - skip; - skip = 0; - } - iov++; - addr++; - num--; - } - - assert(skip == 0); - return copied; -} - -static int virtio_scsi_parse_req(VirtIOSCSIReq *req, - unsigned req_size, unsigned resp_size) -{ - VirtIODevice *vdev = (VirtIODevice *) req->dev; - size_t in_size, out_size; - - if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, - &req->req, req_size) < req_size) { - return -EINVAL; - } - - if (qemu_iovec_concat_iov(&req->resp_iov, - req->elem.in_sg, req->elem.in_num, 0, - resp_size) < resp_size) { - return -EINVAL; - } - - req->resp_size = resp_size; - - /* Old BIOSes left some padding by mistake after the req_size/resp_size. - * As a workaround, always consider the first buffer as the virtio-scsi - * request/response, making the payload start at the second element - * of the iovec. - * - * The actual length of the response header, stored in req->resp_size, - * does not change. - * - * TODO: always disable this workaround for virtio 1.0 devices. - */ - if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) { - if (req->elem.out_num) { - req_size = req->elem.out_sg[0].iov_len; - } - if (req->elem.in_num) { - resp_size = req->elem.in_sg[0].iov_len; - } - } - - out_size = qemu_sgl_concat(req, req->elem.out_sg, - &req->elem.out_addr[0], req->elem.out_num, - req_size); - in_size = qemu_sgl_concat(req, req->elem.in_sg, - &req->elem.in_addr[0], req->elem.in_num, - resp_size); - - if (out_size && in_size) { - return -ENOTSUP; - } - - if (out_size) { - req->mode = SCSI_XFER_TO_DEV; - } else if (in_size) { - req->mode = SCSI_XFER_FROM_DEV; - } - - return 0; -} - -static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) -{ - VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s; - VirtIOSCSIReq *req; - - req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size); - if (!req) { - return NULL; - } - virtio_scsi_init_req(s, vq, req); - return req; -} - -static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) -{ - VirtIOSCSIReq *req = sreq->hba_private; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(req->dev); - uint32_t n = virtio_queue_get_id(req->vq) - 2; - - assert(n < vs->conf.num_queues); - qemu_put_be32s(f, &n); - qemu_put_virtqueue_element(f, &req->elem); -} - -static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) -{ - SCSIBus *bus = sreq->bus; - VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - VirtIOSCSIReq *req; - uint32_t n; - - qemu_get_be32s(f, &n); - assert(n < vs->conf.num_queues); - req = qemu_get_virtqueue_element(f, sizeof(VirtIOSCSIReq) + vs->cdb_size); - virtio_scsi_init_req(s, vs->cmd_vqs[n], req); - - if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, - sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) { - error_report("invalid SCSI request migration data"); - exit(1); - } - - scsi_req_ref(sreq); - req->sreq = sreq; - if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - assert(req->sreq->cmd.mode == req->mode); - } - return req; -} - -typedef struct { - Notifier notifier; - VirtIOSCSIReq *tmf_req; -} VirtIOSCSICancelNotifier; - -static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) -{ - VirtIOSCSICancelNotifier *n = container_of(notifier, - VirtIOSCSICancelNotifier, - notifier); - - if (--n->tmf_req->remaining == 0) { - virtio_scsi_complete_req(n->tmf_req); - } - g_free(n); -} - -/* Return 0 if the request is ready to be completed and return to guest; - * -EINPROGRESS if the request is submitted and will be completed later, in the - * case of async cancellation. */ -static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun); - SCSIRequest *r, *next; - BusChild *kid; - int target; - int ret = 0; - - if (s->dataplane_started && d) { - assert(blk_get_aio_context(d->conf.blk) == s->ctx); - } - /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ - req->resp.tmf.response = VIRTIO_SCSI_S_OK; - - virtio_tswap32s(VIRTIO_DEVICE(s), &req->req.tmf.subtype); - switch (req->req.tmf.subtype) { - case VIRTIO_SCSI_T_TMF_ABORT_TASK: - case VIRTIO_SCSI_T_TMF_QUERY_TASK: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - VirtIOSCSIReq *cmd_req = r->hba_private; - if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) { - break; - } - } - if (r) { - /* - * Assert that the request has not been completed yet, we - * check for it in the loop above. - */ - assert(r->hba_private); - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { - /* "If the specified command is present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - } else { - VirtIOSCSICancelNotifier *notifier; - - req->remaining = 1; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->tmf_req = req; - notifier->notifier.notify = virtio_scsi_cancel_notify; - scsi_req_cancel_async(r, ¬ifier->notifier); - ret = -EINPROGRESS; - } - } - break; - - case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - s->resetting++; - qdev_reset_all(&d->qdev); - s->resetting--; - break; - - case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: - case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: - case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - - /* Add 1 to "remaining" until virtio_scsi_do_tmf returns. - * This way, if the bus starts calling back to the notifiers - * even before we finish the loop, virtio_scsi_cancel_notify - * will not complete the TMF too early. - */ - req->remaining = 1; - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - if (r->hba_private) { - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { - /* "If there is any command present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - break; - } else { - VirtIOSCSICancelNotifier *notifier; - - req->remaining++; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->notifier.notify = virtio_scsi_cancel_notify; - notifier->tmf_req = req; - scsi_req_cancel_async(r, ¬ifier->notifier); - } - } - } - if (--req->remaining > 0) { - ret = -EINPROGRESS; - } - break; - - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf.lun[1]; - s->resetting++; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - d = SCSI_DEVICE(kid->child); - if (d->channel == 0 && d->id == target) { - qdev_reset_all(&d->qdev); - } - } - s->resetting--; - break; - - case VIRTIO_SCSI_T_TMF_CLEAR_ACA: - default: - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; - break; - } - - return ret; - -incorrect_lun: - req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; - return ret; - -fail: - req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; - return ret; -} - -static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - VirtIODevice *vdev = (VirtIODevice *)s; - uint32_t type; - int r = 0; - - if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, - &type, sizeof(type)) < sizeof(type)) { - virtio_scsi_bad_req(); - return; - } - - virtio_tswap32s(vdev, &type); - if (type == VIRTIO_SCSI_T_TMF) { - if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), - sizeof(VirtIOSCSICtrlTMFResp)) < 0) { - virtio_scsi_bad_req(); - } else { - r = virtio_scsi_do_tmf(s, req); - } - - } else if (type == VIRTIO_SCSI_T_AN_QUERY || - type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { - if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq), - sizeof(VirtIOSCSICtrlANResp)) < 0) { - virtio_scsi_bad_req(); - } else { - req->resp.an.event_actual = 0; - req->resp.an.response = VIRTIO_SCSI_S_OK; - } - } - if (r == 0) { - virtio_scsi_complete_req(req); - } else { - assert(r == -EINPROGRESS); - } -} - -void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) -{ - VirtIOSCSIReq *req; - - while ((req = virtio_scsi_pop_req(s, vq))) { - virtio_scsi_handle_ctrl_req(s, req); - } -} - -static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOSCSI *s = (VirtIOSCSI *)vdev; - - if (s->ctx) { - virtio_scsi_dataplane_start(s); - if (!s->dataplane_fenced) { - return; - } - } - virtio_scsi_handle_ctrl_vq(s, vq); -} - -static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) -{ - /* Sense data is not in req->resp and is copied separately - * in virtio_scsi_command_complete. - */ - req->resp_size = sizeof(VirtIOSCSICmdResp); - virtio_scsi_complete_req(req); -} - -static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, - size_t resid) -{ - VirtIOSCSIReq *req = r->hba_private; - uint8_t sense[SCSI_SENSE_BUF_SIZE]; - uint32_t sense_len; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - - if (r->io_canceled) { - return; - } - - req->resp.cmd.response = VIRTIO_SCSI_S_OK; - req->resp.cmd.status = status; - if (req->resp.cmd.status == GOOD) { - req->resp.cmd.resid = virtio_tswap32(vdev, resid); - } else { - req->resp.cmd.resid = 0; - sense_len = scsi_req_get_sense(r, sense, sizeof(sense)); - sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd)); - qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd), - sense, sense_len); - req->resp.cmd.sense_len = virtio_tswap32(vdev, sense_len); - } - virtio_scsi_complete_cmd_req(req); -} - -static int virtio_scsi_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, - uint8_t *buf, void *hba_private) -{ - VirtIOSCSIReq *req = hba_private; - - if (cmd->len == 0) { - cmd->len = MIN(VIRTIO_SCSI_CDB_DEFAULT_SIZE, SCSI_CMD_BUF_SIZE); - memcpy(cmd->buf, buf, cmd->len); - } - - /* Extract the direction and mode directly from the request, for - * host device passthrough. - */ - cmd->xfer = req->qsgl.size; - cmd->mode = req->mode; - return 0; -} - -static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r) -{ - VirtIOSCSIReq *req = r->hba_private; - - return &req->qsgl; -} - -static void virtio_scsi_request_cancelled(SCSIRequest *r) -{ - VirtIOSCSIReq *req = r->hba_private; - - if (!req) { - return; - } - if (req->dev->resetting) { - req->resp.cmd.response = VIRTIO_SCSI_S_RESET; - } else { - req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; - } - virtio_scsi_complete_cmd_req(req); -} - -static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) -{ - req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE; - virtio_scsi_complete_cmd_req(req); -} - -static bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - VirtIOSCSICommon *vs = &s->parent_obj; - SCSIDevice *d; - int rc; - - rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, - sizeof(VirtIOSCSICmdResp) + vs->sense_size); - if (rc < 0) { - if (rc == -ENOTSUP) { - virtio_scsi_fail_cmd_req(req); - } else { - virtio_scsi_bad_req(); - } - return false; - } - - d = virtio_scsi_device_find(s, req->req.cmd.lun); - if (!d) { - req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; - virtio_scsi_complete_cmd_req(req); - return false; - } - if (s->dataplane_started) { - assert(blk_get_aio_context(d->conf.blk) == s->ctx); - } - req->sreq = scsi_req_new(d, req->req.cmd.tag, - virtio_scsi_get_lun(req->req.cmd.lun), - req->req.cmd.cdb, req); - - if (req->sreq->cmd.mode != SCSI_XFER_NONE - && (req->sreq->cmd.mode != req->mode || - req->sreq->cmd.xfer > req->qsgl.size)) { - req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN; - virtio_scsi_complete_cmd_req(req); - return false; - } - scsi_req_ref(req->sreq); - blk_io_plug(d->conf.blk); - return true; -} - -static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - SCSIRequest *sreq = req->sreq; - if (scsi_req_enqueue(sreq)) { - scsi_req_continue(sreq); - } - blk_io_unplug(sreq->dev->conf.blk); - scsi_req_unref(sreq); -} - -void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) -{ - VirtIOSCSIReq *req, *next; - QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - - while ((req = virtio_scsi_pop_req(s, vq))) { - if (virtio_scsi_handle_cmd_req_prepare(s, req)) { - QTAILQ_INSERT_TAIL(&reqs, req, next); - } - } - - QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { - virtio_scsi_handle_cmd_req_submit(s, req); - } -} - -static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) -{ - /* use non-QOM casts in the data path */ - VirtIOSCSI *s = (VirtIOSCSI *)vdev; - - if (s->ctx) { - virtio_scsi_dataplane_start(s); - if (!s->dataplane_fenced) { - return; - } - } - virtio_scsi_handle_cmd_vq(s, vq); -} - -static void virtio_scsi_get_config(VirtIODevice *vdev, - uint8_t *config) -{ - VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; - VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(vdev); - - virtio_stl_p(vdev, &scsiconf->num_queues, s->conf.num_queues); - virtio_stl_p(vdev, &scsiconf->seg_max, 128 - 2); - virtio_stl_p(vdev, &scsiconf->max_sectors, s->conf.max_sectors); - virtio_stl_p(vdev, &scsiconf->cmd_per_lun, s->conf.cmd_per_lun); - virtio_stl_p(vdev, &scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); - virtio_stl_p(vdev, &scsiconf->sense_size, s->sense_size); - virtio_stl_p(vdev, &scsiconf->cdb_size, s->cdb_size); - virtio_stw_p(vdev, &scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); - virtio_stw_p(vdev, &scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); - virtio_stl_p(vdev, &scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); -} - -static void virtio_scsi_set_config(VirtIODevice *vdev, - const uint8_t *config) -{ - VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) >= 65536 || - (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) >= 256) { - error_report("bad data written to virtio-scsi configuration space"); - exit(1); - } - - vs->sense_size = virtio_ldl_p(vdev, &scsiconf->sense_size); - vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size); -} - -static uint64_t virtio_scsi_get_features(VirtIODevice *vdev, - uint64_t requested_features, - Error **errp) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - /* Firstly sync all virtio-scsi possible supported features */ - requested_features |= s->host_features; - return requested_features; -} - -static void virtio_scsi_reset(VirtIODevice *vdev) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if (s->ctx) { - virtio_scsi_dataplane_stop(s); - } - s->resetting++; - qbus_reset_all(&s->bus.qbus); - s->resetting--; - - vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; - vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - s->events_dropped = false; -} - -/* The device does not have anything to save beyond the virtio data. - * Request data is saved with callbacks from SCSI devices. - */ -static void virtio_scsi_save(QEMUFile *f, void *opaque) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - if (s->dataplane_started) { - virtio_scsi_dataplane_stop(s); - } - virtio_save(vdev, f); -} - -static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - int ret; - - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } - return 0; -} - -void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, - uint32_t event, uint32_t reason) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - VirtIOSCSIReq *req; - VirtIOSCSIEvent *evt; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - if (s->dataplane_started) { - assert(s->ctx); - aio_context_acquire(s->ctx); - } - - req = virtio_scsi_pop_req(s, vs->event_vq); - if (!req) { - s->events_dropped = true; - goto out; - } - - if (s->events_dropped) { - event |= VIRTIO_SCSI_T_EVENTS_MISSED; - s->events_dropped = false; - } - - if (virtio_scsi_parse_req(req, 0, sizeof(VirtIOSCSIEvent))) { - virtio_scsi_bad_req(); - } - - evt = &req->resp.event; - memset(evt, 0, sizeof(VirtIOSCSIEvent)); - evt->event = virtio_tswap32(vdev, event); - evt->reason = virtio_tswap32(vdev, reason); - if (!dev) { - assert(event == VIRTIO_SCSI_T_EVENTS_MISSED); - } else { - evt->lun[0] = 1; - evt->lun[1] = dev->id; - - /* Linux wants us to keep the same encoding we use for REPORT LUNS. */ - if (dev->lun >= 256) { - evt->lun[2] = (dev->lun >> 8) | 0x40; - } - evt->lun[3] = dev->lun & 0xFF; - } - virtio_scsi_complete_req(req); -out: - if (s->dataplane_started) { - aio_context_release(s->ctx); - } -} - -void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq) -{ - if (s->events_dropped) { - virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); - } -} - -static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - if (s->ctx) { - virtio_scsi_dataplane_start(s); - if (!s->dataplane_fenced) { - return; - } - } - virtio_scsi_handle_event_vq(s, vq); -} - -static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) -{ - VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) && - dev->type != TYPE_ROM) { - virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, - sense.asc | (sense.ascq << 8)); - } -} - -static void virtio_scsi_blk_insert_notifier(Notifier *n, void *data) -{ - VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, - n, n); - assert(cn->sd->conf.blk == data); - blk_op_block_all(cn->sd->conf.blk, cn->s->blocker); -} - -static void virtio_scsi_blk_remove_notifier(Notifier *n, void *data) -{ - VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, - n, n); - assert(cn->sd->conf.blk == data); - blk_op_unblock_all(cn->sd->conf.blk, cn->s->blocker); -} - -static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - SCSIDevice *sd = SCSI_DEVICE(dev); - - if (s->ctx && !s->dataplane_fenced) { - VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; - - if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { - return; - } - blk_op_block_all(sd->conf.blk, s->blocker); - aio_context_acquire(s->ctx); - blk_set_aio_context(sd->conf.blk, s->ctx); - aio_context_release(s->ctx); - - insert_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); - insert_notifier->n.notify = virtio_scsi_blk_insert_notifier; - insert_notifier->s = s; - insert_notifier->sd = sd; - blk_add_insert_bs_notifier(sd->conf.blk, &insert_notifier->n); - QTAILQ_INSERT_TAIL(&s->insert_notifiers, insert_notifier, next); - - remove_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); - remove_notifier->n.notify = virtio_scsi_blk_remove_notifier; - remove_notifier->s = s; - remove_notifier->sd = sd; - blk_add_remove_bs_notifier(sd->conf.blk, &remove_notifier->n); - QTAILQ_INSERT_TAIL(&s->remove_notifiers, remove_notifier, next); - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { - virtio_scsi_push_event(s, sd, - VIRTIO_SCSI_T_TRANSPORT_RESET, - VIRTIO_SCSI_EVT_RESET_RESCAN); - } -} - -static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - SCSIDevice *sd = SCSI_DEVICE(dev); - VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; - - if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { - virtio_scsi_push_event(s, sd, - VIRTIO_SCSI_T_TRANSPORT_RESET, - VIRTIO_SCSI_EVT_RESET_REMOVED); - } - - if (s->ctx) { - blk_op_unblock_all(sd->conf.blk, s->blocker); - } - - QTAILQ_FOREACH(insert_notifier, &s->insert_notifiers, next) { - if (insert_notifier->sd == sd) { - notifier_remove(&insert_notifier->n); - QTAILQ_REMOVE(&s->insert_notifiers, insert_notifier, next); - g_free(insert_notifier); - break; - } - } - - QTAILQ_FOREACH(remove_notifier, &s->remove_notifiers, next) { - if (remove_notifier->sd == sd) { - notifier_remove(&remove_notifier->n); - QTAILQ_REMOVE(&s->remove_notifiers, remove_notifier, next); - g_free(remove_notifier); - break; - } - } - - qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); -} - -static struct SCSIBusInfo virtio_scsi_scsi_info = { - .tcq = true, - .max_channel = VIRTIO_SCSI_MAX_CHANNEL, - .max_target = VIRTIO_SCSI_MAX_TARGET, - .max_lun = VIRTIO_SCSI_MAX_LUN, - - .complete = virtio_scsi_command_complete, - .cancel = virtio_scsi_request_cancelled, - .change = virtio_scsi_change, - .parse_cdb = virtio_scsi_parse_cdb, - .get_sg_list = virtio_scsi_get_sg_list, - .save_request = virtio_scsi_save_request, - .load_request = virtio_scsi_load_request, -}; - -void virtio_scsi_common_realize(DeviceState *dev, Error **errp, - HandleOutput ctrl, HandleOutput evt, - HandleOutput cmd) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); - int i; - - virtio_init(vdev, "virtio-scsi", VIRTIO_ID_SCSI, - sizeof(VirtIOSCSIConfig)); - - if (s->conf.num_queues == 0 || - s->conf.num_queues > VIRTIO_QUEUE_MAX - 2) { - error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " - "must be a positive integer less than %d.", - s->conf.num_queues, VIRTIO_QUEUE_MAX - 2); - virtio_cleanup(vdev); - return; - } - s->cmd_vqs = g_new0(VirtQueue *, s->conf.num_queues); - s->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; - s->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - - s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - ctrl); - s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - evt); - for (i = 0; i < s->conf.num_queues; i++) { - s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - cmd); - } - - if (s->conf.iothread) { - virtio_scsi_set_iothread(VIRTIO_SCSI(s), s->conf.iothread); - } -} - -static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSCSI *s = VIRTIO_SCSI(dev); - static int virtio_scsi_id; - Error *err = NULL; - - virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, - virtio_scsi_handle_event, - virtio_scsi_handle_cmd); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), dev, - &virtio_scsi_scsi_info, vdev->bus_name); - /* override default SCSI bus hotplug-handler, with virtio-scsi's one */ - qbus_set_hotplug_handler(BUS(&s->bus), dev, &error_abort); - - if (!dev->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - } - - register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, - virtio_scsi_save, virtio_scsi_load, s); - - error_setg(&s->blocker, "block device is in use by data plane"); - - QTAILQ_INIT(&s->insert_notifiers); - QTAILQ_INIT(&s->remove_notifiers); -} - -static void virtio_scsi_instance_init(Object *obj) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&vs->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); -} - -void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); - - g_free(vs->cmd_vqs); - virtio_cleanup(vdev); -} - -static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIOSCSI *s = VIRTIO_SCSI(dev); - - error_free(s->blocker); - - unregister_savevm(dev, "virtio-scsi", s); - virtio_scsi_common_unrealize(dev, errp); -} - -static Property virtio_scsi_properties[] = { - DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1), - DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors, - 0xFFFF), - DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSI, parent_obj.conf.cmd_per_lun, - 128), - DEFINE_PROP_BIT("hotplug", VirtIOSCSI, host_features, - VIRTIO_SCSI_F_HOTPLUG, true), - DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features, - VIRTIO_SCSI_F_CHANGE, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) -{ - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - vdc->get_config = virtio_scsi_get_config; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static void virtio_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - dc->props = virtio_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - vdc->realize = virtio_scsi_device_realize; - vdc->unrealize = virtio_scsi_device_unrealize; - vdc->set_config = virtio_scsi_set_config; - vdc->get_features = virtio_scsi_get_features; - vdc->reset = virtio_scsi_reset; - hc->plug = virtio_scsi_hotplug; - hc->unplug = virtio_scsi_hotunplug; -} - -static const TypeInfo virtio_scsi_common_info = { - .name = TYPE_VIRTIO_SCSI_COMMON, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOSCSICommon), - .abstract = true, - .class_init = virtio_scsi_common_class_init, -}; - -static const TypeInfo virtio_scsi_info = { - .name = TYPE_VIRTIO_SCSI, - .parent = TYPE_VIRTIO_SCSI_COMMON, - .instance_size = sizeof(VirtIOSCSI), - .instance_init = virtio_scsi_instance_init, - .class_init = virtio_scsi_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_scsi_common_info); - type_register_static(&virtio_scsi_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/scsi/vmw_pvscsi.c b/qemu/hw/scsi/vmw_pvscsi.c deleted file mode 100644 index e690b4ec0..000000000 --- a/qemu/hw/scsi/vmw_pvscsi.c +++ /dev/null @@ -1,1305 +0,0 @@ -/* - * QEMU VMWARE PVSCSI paravirtual SCSI bus - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Based on implementation by Paolo Bonzini - * http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg00729.html - * - * Authors: - * Paolo Bonzini <pbonzini@redhat.com> - * Dmitry Fleytman <dmitry@daynix.com> - * Yan Vugenfirer <yan@daynix.com> - * - * This work is licensed under the terms of the GNU GPL, version 2. - * See the COPYING file in the top-level directory. - * - * NOTE about MSI-X: - * MSI-X support has been removed for the moment because it leads Windows OS - * to crash on startup. The crash happens because Windows driver requires - * MSI-X shared memory to be part of the same BAR used for rings state - * registers, etc. This is not supported by QEMU infrastructure so separate - * BAR created from MSI-X purposes. Windows driver fails to deal with 2 BARs. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/scsi/scsi.h" -#include <block/scsi.h> -#include "hw/pci/msi.h" -#include "vmw_pvscsi.h" -#include "trace.h" - - -#define PVSCSI_USE_64BIT (true) -#define PVSCSI_PER_VECTOR_MASK (false) - -#define PVSCSI_MAX_DEVS (64) -#define PVSCSI_MSIX_NUM_VECTORS (1) - -#define PVSCSI_MAX_CMD_DATA_WORDS \ - (sizeof(PVSCSICmdDescSetupRings)/sizeof(uint32_t)) - -#define RS_GET_FIELD(m, field) \ - (ldl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \ - (m)->rs_pa + offsetof(struct PVSCSIRingsState, field))) -#define RS_SET_FIELD(m, field, val) \ - (stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \ - (m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val)) - -typedef struct PVSCSIClass { - PCIDeviceClass parent_class; - DeviceRealize parent_dc_realize; -} PVSCSIClass; - -#define TYPE_PVSCSI "pvscsi" -#define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI) - -#define PVSCSI_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(PVSCSIClass, (klass), TYPE_PVSCSI) -#define PVSCSI_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI) - -/* Compatability flags for migration */ -#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0 -#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \ - (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT) -#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1 -#define PVSCSI_COMPAT_DISABLE_PCIE \ - (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT) - -#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \ - ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION) -#define PVSCSI_MSI_OFFSET(s) \ - (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c) -#define PVSCSI_EXP_EP_OFFSET (0x40) - -typedef struct PVSCSIRingInfo { - uint64_t rs_pa; - uint32_t txr_len_mask; - uint32_t rxr_len_mask; - uint32_t msg_len_mask; - uint64_t req_ring_pages_pa[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; - uint64_t cmp_ring_pages_pa[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; - uint64_t msg_ring_pages_pa[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES]; - uint64_t consumed_ptr; - uint64_t filled_cmp_ptr; - uint64_t filled_msg_ptr; -} PVSCSIRingInfo; - -typedef struct PVSCSISGState { - hwaddr elemAddr; - hwaddr dataAddr; - uint32_t resid; -} PVSCSISGState; - -typedef QTAILQ_HEAD(, PVSCSIRequest) PVSCSIRequestList; - -typedef struct { - PCIDevice parent_obj; - MemoryRegion io_space; - SCSIBus bus; - QEMUBH *completion_worker; - PVSCSIRequestList pending_queue; - PVSCSIRequestList completion_queue; - - uint64_t reg_interrupt_status; /* Interrupt status register value */ - uint64_t reg_interrupt_enabled; /* Interrupt mask register value */ - uint64_t reg_command_status; /* Command status register value */ - - /* Command data adoption mechanism */ - uint64_t curr_cmd; /* Last command arrived */ - uint32_t curr_cmd_data_cntr; /* Amount of data for last command */ - - /* Collector for current command data */ - uint32_t curr_cmd_data[PVSCSI_MAX_CMD_DATA_WORDS]; - - uint8_t rings_info_valid; /* Whether data rings initialized */ - uint8_t msg_ring_info_valid; /* Whether message ring initialized */ - uint8_t use_msg; /* Whether to use message ring */ - - uint8_t msi_used; /* Whether MSI support was installed successfully */ - - PVSCSIRingInfo rings; /* Data transfer rings manager */ - uint32_t resetting; /* Reset in progress */ - - uint32_t compat_flags; -} PVSCSIState; - -typedef struct PVSCSIRequest { - SCSIRequest *sreq; - PVSCSIState *dev; - uint8_t sense_key; - uint8_t completed; - int lun; - QEMUSGList sgl; - PVSCSISGState sg; - struct PVSCSIRingReqDesc req; - struct PVSCSIRingCmpDesc cmp; - QTAILQ_ENTRY(PVSCSIRequest) next; -} PVSCSIRequest; - -/* Integer binary logarithm */ -static int -pvscsi_log2(uint32_t input) -{ - int log = 0; - assert(input > 0); - while (input >> ++log) { - } - return log; -} - -static void -pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri) -{ - int i; - uint32_t txr_len_log2, rxr_len_log2; - uint32_t req_ring_size, cmp_ring_size; - m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT; - - req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - txr_len_log2 = pvscsi_log2(req_ring_size - 1); - rxr_len_log2 = pvscsi_log2(cmp_ring_size - 1); - - m->txr_len_mask = MASK(txr_len_log2); - m->rxr_len_mask = MASK(rxr_len_log2); - - m->consumed_ptr = 0; - m->filled_cmp_ptr = 0; - - for (i = 0; i < ri->reqRingNumPages; i++) { - m->req_ring_pages_pa[i] = ri->reqRingPPNs[i] << VMW_PAGE_SHIFT; - } - - for (i = 0; i < ri->cmpRingNumPages; i++) { - m->cmp_ring_pages_pa[i] = ri->cmpRingPPNs[i] << VMW_PAGE_SHIFT; - } - - RS_SET_FIELD(m, reqProdIdx, 0); - RS_SET_FIELD(m, reqConsIdx, 0); - RS_SET_FIELD(m, reqNumEntriesLog2, txr_len_log2); - - RS_SET_FIELD(m, cmpProdIdx, 0); - RS_SET_FIELD(m, cmpConsIdx, 0); - RS_SET_FIELD(m, cmpNumEntriesLog2, rxr_len_log2); - - trace_pvscsi_ring_init_data(txr_len_log2, rxr_len_log2); - - /* Flush ring state page changes */ - smp_wmb(); -} - -static void -pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri) -{ - int i; - uint32_t len_log2; - uint32_t ring_size; - - ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; - len_log2 = pvscsi_log2(ring_size - 1); - - m->msg_len_mask = MASK(len_log2); - - m->filled_msg_ptr = 0; - - for (i = 0; i < ri->numPages; i++) { - m->msg_ring_pages_pa[i] = ri->ringPPNs[i] << VMW_PAGE_SHIFT; - } - - RS_SET_FIELD(m, msgProdIdx, 0); - RS_SET_FIELD(m, msgConsIdx, 0); - RS_SET_FIELD(m, msgNumEntriesLog2, len_log2); - - trace_pvscsi_ring_init_msg(len_log2); - - /* Flush ring state page changes */ - smp_wmb(); -} - -static void -pvscsi_ring_cleanup(PVSCSIRingInfo *mgr) -{ - mgr->rs_pa = 0; - mgr->txr_len_mask = 0; - mgr->rxr_len_mask = 0; - mgr->msg_len_mask = 0; - mgr->consumed_ptr = 0; - mgr->filled_cmp_ptr = 0; - mgr->filled_msg_ptr = 0; - memset(mgr->req_ring_pages_pa, 0, sizeof(mgr->req_ring_pages_pa)); - memset(mgr->cmp_ring_pages_pa, 0, sizeof(mgr->cmp_ring_pages_pa)); - memset(mgr->msg_ring_pages_pa, 0, sizeof(mgr->msg_ring_pages_pa)); -} - -static hwaddr -pvscsi_ring_pop_req_descr(PVSCSIRingInfo *mgr) -{ - uint32_t ready_ptr = RS_GET_FIELD(mgr, reqProdIdx); - - if (ready_ptr != mgr->consumed_ptr) { - uint32_t next_ready_ptr = - mgr->consumed_ptr++ & mgr->txr_len_mask; - uint32_t next_ready_page = - next_ready_ptr / PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - uint32_t inpage_idx = - next_ready_ptr % PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - - return mgr->req_ring_pages_pa[next_ready_page] + - inpage_idx * sizeof(PVSCSIRingReqDesc); - } else { - return 0; - } -} - -static void -pvscsi_ring_flush_req(PVSCSIRingInfo *mgr) -{ - RS_SET_FIELD(mgr, reqConsIdx, mgr->consumed_ptr); -} - -static hwaddr -pvscsi_ring_pop_cmp_descr(PVSCSIRingInfo *mgr) -{ - /* - * According to Linux driver code it explicitly verifies that number - * of requests being processed by device is less then the size of - * completion queue, so device may omit completion queue overflow - * conditions check. We assume that this is true for other (Windows) - * drivers as well. - */ - - uint32_t free_cmp_ptr = - mgr->filled_cmp_ptr++ & mgr->rxr_len_mask; - uint32_t free_cmp_page = - free_cmp_ptr / PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - uint32_t inpage_idx = - free_cmp_ptr % PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - return mgr->cmp_ring_pages_pa[free_cmp_page] + - inpage_idx * sizeof(PVSCSIRingCmpDesc); -} - -static hwaddr -pvscsi_ring_pop_msg_descr(PVSCSIRingInfo *mgr) -{ - uint32_t free_msg_ptr = - mgr->filled_msg_ptr++ & mgr->msg_len_mask; - uint32_t free_msg_page = - free_msg_ptr / PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; - uint32_t inpage_idx = - free_msg_ptr % PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; - return mgr->msg_ring_pages_pa[free_msg_page] + - inpage_idx * sizeof(PVSCSIRingMsgDesc); -} - -static void -pvscsi_ring_flush_cmp(PVSCSIRingInfo *mgr) -{ - /* Flush descriptor changes */ - smp_wmb(); - - trace_pvscsi_ring_flush_cmp(mgr->filled_cmp_ptr); - - RS_SET_FIELD(mgr, cmpProdIdx, mgr->filled_cmp_ptr); -} - -static bool -pvscsi_ring_msg_has_room(PVSCSIRingInfo *mgr) -{ - uint32_t prodIdx = RS_GET_FIELD(mgr, msgProdIdx); - uint32_t consIdx = RS_GET_FIELD(mgr, msgConsIdx); - - return (prodIdx - consIdx) < (mgr->msg_len_mask + 1); -} - -static void -pvscsi_ring_flush_msg(PVSCSIRingInfo *mgr) -{ - /* Flush descriptor changes */ - smp_wmb(); - - trace_pvscsi_ring_flush_msg(mgr->filled_msg_ptr); - - RS_SET_FIELD(mgr, msgProdIdx, mgr->filled_msg_ptr); -} - -static void -pvscsi_reset_state(PVSCSIState *s) -{ - s->curr_cmd = PVSCSI_CMD_FIRST; - s->curr_cmd_data_cntr = 0; - s->reg_command_status = PVSCSI_COMMAND_PROCESSING_SUCCEEDED; - s->reg_interrupt_status = 0; - pvscsi_ring_cleanup(&s->rings); - s->rings_info_valid = FALSE; - s->msg_ring_info_valid = FALSE; - QTAILQ_INIT(&s->pending_queue); - QTAILQ_INIT(&s->completion_queue); -} - -static void -pvscsi_update_irq_status(PVSCSIState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - bool should_raise = s->reg_interrupt_enabled & s->reg_interrupt_status; - - trace_pvscsi_update_irq_level(should_raise, s->reg_interrupt_enabled, - s->reg_interrupt_status); - - if (s->msi_used && msi_enabled(d)) { - if (should_raise) { - trace_pvscsi_update_irq_msi(); - msi_notify(d, PVSCSI_VECTOR_COMPLETION); - } - return; - } - - pci_set_irq(d, !!should_raise); -} - -static void -pvscsi_raise_completion_interrupt(PVSCSIState *s) -{ - s->reg_interrupt_status |= PVSCSI_INTR_CMPL_0; - - /* Memory barrier to flush interrupt status register changes*/ - smp_wmb(); - - pvscsi_update_irq_status(s); -} - -static void -pvscsi_raise_message_interrupt(PVSCSIState *s) -{ - s->reg_interrupt_status |= PVSCSI_INTR_MSG_0; - - /* Memory barrier to flush interrupt status register changes*/ - smp_wmb(); - - pvscsi_update_irq_status(s); -} - -static void -pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc) -{ - hwaddr cmp_descr_pa; - - cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings); - trace_pvscsi_cmp_ring_put(cmp_descr_pa); - cpu_physical_memory_write(cmp_descr_pa, (void *)cmp_desc, - sizeof(*cmp_desc)); -} - -static void -pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc) -{ - hwaddr msg_descr_pa; - - msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings); - trace_pvscsi_msg_ring_put(msg_descr_pa); - cpu_physical_memory_write(msg_descr_pa, (void *)msg_desc, - sizeof(*msg_desc)); -} - -static void -pvscsi_process_completion_queue(void *opaque) -{ - PVSCSIState *s = opaque; - PVSCSIRequest *pvscsi_req; - bool has_completed = false; - - while (!QTAILQ_EMPTY(&s->completion_queue)) { - pvscsi_req = QTAILQ_FIRST(&s->completion_queue); - QTAILQ_REMOVE(&s->completion_queue, pvscsi_req, next); - pvscsi_cmp_ring_put(s, &pvscsi_req->cmp); - g_free(pvscsi_req); - has_completed = true; - } - - if (has_completed) { - pvscsi_ring_flush_cmp(&s->rings); - pvscsi_raise_completion_interrupt(s); - } -} - -static void -pvscsi_reset_adapter(PVSCSIState *s) -{ - s->resetting++; - qbus_reset_all_fn(&s->bus); - s->resetting--; - pvscsi_process_completion_queue(s); - assert(QTAILQ_EMPTY(&s->pending_queue)); - pvscsi_reset_state(s); -} - -static void -pvscsi_schedule_completion_processing(PVSCSIState *s) -{ - /* Try putting more complete requests on the ring. */ - if (!QTAILQ_EMPTY(&s->completion_queue)) { - qemu_bh_schedule(s->completion_worker); - } -} - -static void -pvscsi_complete_request(PVSCSIState *s, PVSCSIRequest *r) -{ - assert(!r->completed); - - trace_pvscsi_complete_request(r->cmp.context, r->cmp.dataLen, - r->sense_key); - if (r->sreq != NULL) { - scsi_req_unref(r->sreq); - r->sreq = NULL; - } - r->completed = 1; - QTAILQ_REMOVE(&s->pending_queue, r, next); - QTAILQ_INSERT_TAIL(&s->completion_queue, r, next); - pvscsi_schedule_completion_processing(s); -} - -static QEMUSGList *pvscsi_get_sg_list(SCSIRequest *r) -{ - PVSCSIRequest *req = r->hba_private; - - trace_pvscsi_get_sg_list(req->sgl.nsg, req->sgl.size); - - return &req->sgl; -} - -static void -pvscsi_get_next_sg_elem(PVSCSISGState *sg) -{ - struct PVSCSISGElement elem; - - cpu_physical_memory_read(sg->elemAddr, (void *)&elem, sizeof(elem)); - if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) { - /* - * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in - * header file but its value is unknown. This flag requires - * additional processing, so we put warning here to catch it - * some day and make proper implementation - */ - trace_pvscsi_get_next_sg_elem(elem.flags); - } - - sg->elemAddr += sizeof(elem); - sg->dataAddr = elem.addr; - sg->resid = elem.length; -} - -static void -pvscsi_write_sense(PVSCSIRequest *r, uint8_t *sense, int len) -{ - r->cmp.senseLen = MIN(r->req.senseLen, len); - r->sense_key = sense[(sense[0] & 2) ? 1 : 2]; - cpu_physical_memory_write(r->req.senseAddr, sense, r->cmp.senseLen); -} - -static void -pvscsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid) -{ - PVSCSIRequest *pvscsi_req = req->hba_private; - PVSCSIState *s; - - if (!pvscsi_req) { - trace_pvscsi_command_complete_not_found(req->tag); - return; - } - s = pvscsi_req->dev; - - if (resid) { - /* Short transfer. */ - trace_pvscsi_command_complete_data_run(); - pvscsi_req->cmp.hostStatus = BTSTAT_DATARUN; - } - - pvscsi_req->cmp.scsiStatus = status; - if (pvscsi_req->cmp.scsiStatus == CHECK_CONDITION) { - uint8_t sense[SCSI_SENSE_BUF_SIZE]; - int sense_len = - scsi_req_get_sense(pvscsi_req->sreq, sense, sizeof(sense)); - - trace_pvscsi_command_complete_sense_len(sense_len); - pvscsi_write_sense(pvscsi_req, sense, sense_len); - } - qemu_sglist_destroy(&pvscsi_req->sgl); - pvscsi_complete_request(s, pvscsi_req); -} - -static void -pvscsi_send_msg(PVSCSIState *s, SCSIDevice *dev, uint32_t msg_type) -{ - if (s->msg_ring_info_valid && pvscsi_ring_msg_has_room(&s->rings)) { - PVSCSIMsgDescDevStatusChanged msg = {0}; - - msg.type = msg_type; - msg.bus = dev->channel; - msg.target = dev->id; - msg.lun[1] = dev->lun; - - pvscsi_msg_ring_put(s, (PVSCSIRingMsgDesc *)&msg); - pvscsi_ring_flush_msg(&s->rings); - pvscsi_raise_message_interrupt(s); - } -} - -static void -pvscsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) -{ - PVSCSIState *s = PVSCSI(hotplug_dev); - - pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_ADDED); -} - -static void -pvscsi_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) -{ - PVSCSIState *s = PVSCSI(hotplug_dev); - - pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_REMOVED); - qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); -} - -static void -pvscsi_request_cancelled(SCSIRequest *req) -{ - PVSCSIRequest *pvscsi_req = req->hba_private; - PVSCSIState *s = pvscsi_req->dev; - - if (pvscsi_req->completed) { - return; - } - - if (pvscsi_req->dev->resetting) { - pvscsi_req->cmp.hostStatus = BTSTAT_BUSRESET; - } else { - pvscsi_req->cmp.hostStatus = BTSTAT_ABORTQUEUE; - } - - pvscsi_complete_request(s, pvscsi_req); -} - -static SCSIDevice* -pvscsi_device_find(PVSCSIState *s, int channel, int target, - uint8_t *requested_lun, uint8_t *target_lun) -{ - if (requested_lun[0] || requested_lun[2] || requested_lun[3] || - requested_lun[4] || requested_lun[5] || requested_lun[6] || - requested_lun[7] || (target > PVSCSI_MAX_DEVS)) { - return NULL; - } else { - *target_lun = requested_lun[1]; - return scsi_device_find(&s->bus, channel, target, *target_lun); - } -} - -static PVSCSIRequest * -pvscsi_queue_pending_descriptor(PVSCSIState *s, SCSIDevice **d, - struct PVSCSIRingReqDesc *descr) -{ - PVSCSIRequest *pvscsi_req; - uint8_t lun; - - pvscsi_req = g_malloc0(sizeof(*pvscsi_req)); - pvscsi_req->dev = s; - pvscsi_req->req = *descr; - pvscsi_req->cmp.context = pvscsi_req->req.context; - QTAILQ_INSERT_TAIL(&s->pending_queue, pvscsi_req, next); - - *d = pvscsi_device_find(s, descr->bus, descr->target, descr->lun, &lun); - if (*d) { - pvscsi_req->lun = lun; - } - - return pvscsi_req; -} - -static void -pvscsi_convert_sglist(PVSCSIRequest *r) -{ - int chunk_size; - uint64_t data_length = r->req.dataLen; - PVSCSISGState sg = r->sg; - while (data_length) { - while (!sg.resid) { - pvscsi_get_next_sg_elem(&sg); - trace_pvscsi_convert_sglist(r->req.context, r->sg.dataAddr, - r->sg.resid); - } - assert(data_length > 0); - chunk_size = MIN((unsigned) data_length, sg.resid); - if (chunk_size) { - qemu_sglist_add(&r->sgl, sg.dataAddr, chunk_size); - } - - sg.dataAddr += chunk_size; - data_length -= chunk_size; - sg.resid -= chunk_size; - } -} - -static void -pvscsi_build_sglist(PVSCSIState *s, PVSCSIRequest *r) -{ - PCIDevice *d = PCI_DEVICE(s); - - pci_dma_sglist_init(&r->sgl, d, 1); - if (r->req.flags & PVSCSI_FLAG_CMD_WITH_SG_LIST) { - pvscsi_convert_sglist(r); - } else { - qemu_sglist_add(&r->sgl, r->req.dataAddr, r->req.dataLen); - } -} - -static void -pvscsi_process_request_descriptor(PVSCSIState *s, - struct PVSCSIRingReqDesc *descr) -{ - SCSIDevice *d; - PVSCSIRequest *r = pvscsi_queue_pending_descriptor(s, &d, descr); - int64_t n; - - trace_pvscsi_process_req_descr(descr->cdb[0], descr->context); - - if (!d) { - r->cmp.hostStatus = BTSTAT_SELTIMEO; - trace_pvscsi_process_req_descr_unknown_device(); - pvscsi_complete_request(s, r); - return; - } - - if (descr->flags & PVSCSI_FLAG_CMD_WITH_SG_LIST) { - r->sg.elemAddr = descr->dataAddr; - } - - r->sreq = scsi_req_new(d, descr->context, r->lun, descr->cdb, r); - if (r->sreq->cmd.mode == SCSI_XFER_FROM_DEV && - (descr->flags & PVSCSI_FLAG_CMD_DIR_TODEVICE)) { - r->cmp.hostStatus = BTSTAT_BADMSG; - trace_pvscsi_process_req_descr_invalid_dir(); - scsi_req_cancel(r->sreq); - return; - } - if (r->sreq->cmd.mode == SCSI_XFER_TO_DEV && - (descr->flags & PVSCSI_FLAG_CMD_DIR_TOHOST)) { - r->cmp.hostStatus = BTSTAT_BADMSG; - trace_pvscsi_process_req_descr_invalid_dir(); - scsi_req_cancel(r->sreq); - return; - } - - pvscsi_build_sglist(s, r); - n = scsi_req_enqueue(r->sreq); - - if (n) { - scsi_req_continue(r->sreq); - } -} - -static void -pvscsi_process_io(PVSCSIState *s) -{ - PVSCSIRingReqDesc descr; - hwaddr next_descr_pa; - - assert(s->rings_info_valid); - while ((next_descr_pa = pvscsi_ring_pop_req_descr(&s->rings)) != 0) { - - /* Only read after production index verification */ - smp_rmb(); - - trace_pvscsi_process_io(next_descr_pa); - cpu_physical_memory_read(next_descr_pa, &descr, sizeof(descr)); - pvscsi_process_request_descriptor(s, &descr); - } - - pvscsi_ring_flush_req(&s->rings); -} - -static void -pvscsi_dbg_dump_tx_rings_config(PVSCSICmdDescSetupRings *rc) -{ - int i; - trace_pvscsi_tx_rings_ppn("Rings State", rc->ringsStatePPN); - - trace_pvscsi_tx_rings_num_pages("Request Ring", rc->reqRingNumPages); - for (i = 0; i < rc->reqRingNumPages; i++) { - trace_pvscsi_tx_rings_ppn("Request Ring", rc->reqRingPPNs[i]); - } - - trace_pvscsi_tx_rings_num_pages("Confirm Ring", rc->cmpRingNumPages); - for (i = 0; i < rc->cmpRingNumPages; i++) { - trace_pvscsi_tx_rings_ppn("Confirm Ring", rc->reqRingPPNs[i]); - } -} - -static uint64_t -pvscsi_on_cmd_config(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_CONFIG"); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_unplug(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_DEVICE_UNPLUG"); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_issue_scsi(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_ISSUE_SCSI"); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_setup_rings(PVSCSIState *s) -{ - PVSCSICmdDescSetupRings *rc = - (PVSCSICmdDescSetupRings *) s->curr_cmd_data; - - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS"); - - pvscsi_dbg_dump_tx_rings_config(rc); - pvscsi_ring_init_data(&s->rings, rc); - s->rings_info_valid = TRUE; - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static uint64_t -pvscsi_on_cmd_abort(PVSCSIState *s) -{ - PVSCSICmdDescAbortCmd *cmd = (PVSCSICmdDescAbortCmd *) s->curr_cmd_data; - PVSCSIRequest *r, *next; - - trace_pvscsi_on_cmd_abort(cmd->context, cmd->target); - - QTAILQ_FOREACH_SAFE(r, &s->pending_queue, next, next) { - if (r->req.context == cmd->context) { - break; - } - } - if (r) { - assert(!r->completed); - r->cmp.hostStatus = BTSTAT_ABORTQUEUE; - scsi_req_cancel(r->sreq); - } - - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static uint64_t -pvscsi_on_cmd_unknown(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_unknown_data(s->curr_cmd_data[0]); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_reset_device(PVSCSIState *s) -{ - uint8_t target_lun = 0; - struct PVSCSICmdDescResetDevice *cmd = - (struct PVSCSICmdDescResetDevice *) s->curr_cmd_data; - SCSIDevice *sdev; - - sdev = pvscsi_device_find(s, 0, cmd->target, cmd->lun, &target_lun); - - trace_pvscsi_on_cmd_reset_dev(cmd->target, (int) target_lun, sdev); - - if (sdev != NULL) { - s->resetting++; - device_reset(&sdev->qdev); - s->resetting--; - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; - } - - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_reset_bus(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_RESET_BUS"); - - s->resetting++; - qbus_reset_all_fn(&s->bus); - s->resetting--; - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static uint64_t -pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s) -{ - PVSCSICmdDescSetupMsgRing *rc = - (PVSCSICmdDescSetupMsgRing *) s->curr_cmd_data; - - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_MSG_RING"); - - if (!s->use_msg) { - return PVSCSI_COMMAND_PROCESSING_FAILED; - } - - if (s->rings_info_valid) { - pvscsi_ring_init_msg(&s->rings, rc); - s->msg_ring_info_valid = TRUE; - } - return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t); -} - -static uint64_t -pvscsi_on_cmd_adapter_reset(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_ADAPTER_RESET"); - - pvscsi_reset_adapter(s); - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static const struct { - int data_size; - uint64_t (*handler_fn)(PVSCSIState *s); -} pvscsi_commands[] = { - [PVSCSI_CMD_FIRST] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_unknown, - }, - - /* Not implemented, data size defined based on what arrives on windows */ - [PVSCSI_CMD_CONFIG] = { - .data_size = 6 * sizeof(uint32_t), - .handler_fn = pvscsi_on_cmd_config, - }, - - /* Command not implemented, data size is unknown */ - [PVSCSI_CMD_ISSUE_SCSI] = { - .data_size = 0, - .handler_fn = pvscsi_on_issue_scsi, - }, - - /* Command not implemented, data size is unknown */ - [PVSCSI_CMD_DEVICE_UNPLUG] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_unplug, - }, - - [PVSCSI_CMD_SETUP_RINGS] = { - .data_size = sizeof(PVSCSICmdDescSetupRings), - .handler_fn = pvscsi_on_cmd_setup_rings, - }, - - [PVSCSI_CMD_RESET_DEVICE] = { - .data_size = sizeof(struct PVSCSICmdDescResetDevice), - .handler_fn = pvscsi_on_cmd_reset_device, - }, - - [PVSCSI_CMD_RESET_BUS] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_reset_bus, - }, - - [PVSCSI_CMD_SETUP_MSG_RING] = { - .data_size = sizeof(PVSCSICmdDescSetupMsgRing), - .handler_fn = pvscsi_on_cmd_setup_msg_ring, - }, - - [PVSCSI_CMD_ADAPTER_RESET] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_adapter_reset, - }, - - [PVSCSI_CMD_ABORT_CMD] = { - .data_size = sizeof(struct PVSCSICmdDescAbortCmd), - .handler_fn = pvscsi_on_cmd_abort, - }, -}; - -static void -pvscsi_do_command_processing(PVSCSIState *s) -{ - size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); - - assert(s->curr_cmd < PVSCSI_CMD_LAST); - if (bytes_arrived >= pvscsi_commands[s->curr_cmd].data_size) { - s->reg_command_status = pvscsi_commands[s->curr_cmd].handler_fn(s); - s->curr_cmd = PVSCSI_CMD_FIRST; - s->curr_cmd_data_cntr = 0; - } -} - -static void -pvscsi_on_command_data(PVSCSIState *s, uint32_t value) -{ - size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); - - assert(bytes_arrived < sizeof(s->curr_cmd_data)); - s->curr_cmd_data[s->curr_cmd_data_cntr++] = value; - - pvscsi_do_command_processing(s); -} - -static void -pvscsi_on_command(PVSCSIState *s, uint64_t cmd_id) -{ - if ((cmd_id > PVSCSI_CMD_FIRST) && (cmd_id < PVSCSI_CMD_LAST)) { - s->curr_cmd = cmd_id; - } else { - s->curr_cmd = PVSCSI_CMD_FIRST; - trace_pvscsi_on_cmd_unknown(cmd_id); - } - - s->curr_cmd_data_cntr = 0; - s->reg_command_status = PVSCSI_COMMAND_NOT_ENOUGH_DATA; - - pvscsi_do_command_processing(s); -} - -static void -pvscsi_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PVSCSIState *s = opaque; - - switch (addr) { - case PVSCSI_REG_OFFSET_COMMAND: - pvscsi_on_command(s, val); - break; - - case PVSCSI_REG_OFFSET_COMMAND_DATA: - pvscsi_on_command_data(s, (uint32_t) val); - break; - - case PVSCSI_REG_OFFSET_INTR_STATUS: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_INTR_STATUS", val); - s->reg_interrupt_status &= ~val; - pvscsi_update_irq_status(s); - pvscsi_schedule_completion_processing(s); - break; - - case PVSCSI_REG_OFFSET_INTR_MASK: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_INTR_MASK", val); - s->reg_interrupt_enabled = val; - pvscsi_update_irq_status(s); - break; - - case PVSCSI_REG_OFFSET_KICK_NON_RW_IO: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_KICK_NON_RW_IO", val); - pvscsi_process_io(s); - break; - - case PVSCSI_REG_OFFSET_KICK_RW_IO: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_KICK_RW_IO", val); - pvscsi_process_io(s); - break; - - case PVSCSI_REG_OFFSET_DEBUG: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_DEBUG", val); - break; - - default: - trace_pvscsi_io_write_unknown(addr, size, val); - break; - } - -} - -static uint64_t -pvscsi_io_read(void *opaque, hwaddr addr, unsigned size) -{ - PVSCSIState *s = opaque; - - switch (addr) { - case PVSCSI_REG_OFFSET_INTR_STATUS: - trace_pvscsi_io_read("PVSCSI_REG_OFFSET_INTR_STATUS", - s->reg_interrupt_status); - return s->reg_interrupt_status; - - case PVSCSI_REG_OFFSET_INTR_MASK: - trace_pvscsi_io_read("PVSCSI_REG_OFFSET_INTR_MASK", - s->reg_interrupt_status); - return s->reg_interrupt_enabled; - - case PVSCSI_REG_OFFSET_COMMAND_STATUS: - trace_pvscsi_io_read("PVSCSI_REG_OFFSET_COMMAND_STATUS", - s->reg_interrupt_status); - return s->reg_command_status; - - default: - trace_pvscsi_io_read_unknown(addr, size); - return 0; - } -} - - -static bool -pvscsi_init_msi(PVSCSIState *s) -{ - int res; - PCIDevice *d = PCI_DEVICE(s); - - res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS, - PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK); - if (res < 0) { - trace_pvscsi_init_msi_fail(res); - s->msi_used = false; - } else { - s->msi_used = true; - } - - return s->msi_used; -} - -static void -pvscsi_cleanup_msi(PVSCSIState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - - if (s->msi_used) { - msi_uninit(d); - } -} - -static const MemoryRegionOps pvscsi_ops = { - .read = pvscsi_io_read, - .write = pvscsi_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const struct SCSIBusInfo pvscsi_scsi_info = { - .tcq = true, - .max_target = PVSCSI_MAX_DEVS, - .max_channel = 0, - .max_lun = 0, - - .get_sg_list = pvscsi_get_sg_list, - .complete = pvscsi_command_complete, - .cancel = pvscsi_request_cancelled, -}; - -static int -pvscsi_init(PCIDevice *pci_dev) -{ - PVSCSIState *s = PVSCSI(pci_dev); - - trace_pvscsi_state("init"); - - /* PCI subsystem ID, subsystem vendor ID, revision */ - if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000); - } else { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - PCI_VENDOR_ID_VMWARE); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - PCI_DEVICE_ID_VMWARE_PVSCSI); - pci_config_set_revision(pci_dev->config, 0x2); - } - - /* PCI latency timer = 255 */ - pci_dev->config[PCI_LATENCY_TIMER] = 0xff; - - /* Interrupt pin A */ - pci_config_set_interrupt_pin(pci_dev->config, 1); - - memory_region_init_io(&s->io_space, OBJECT(s), &pvscsi_ops, s, - "pvscsi-io", PVSCSI_MEM_SPACE_SIZE); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io_space); - - pvscsi_init_msi(s); - - if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus)) { - pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET); - } - - s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s); - if (!s->completion_worker) { - pvscsi_cleanup_msi(s); - return -ENOMEM; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(pci_dev), - &pvscsi_scsi_info, NULL); - /* override default SCSI bus hotplug-handler, with pvscsi's one */ - qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(s), &error_abort); - pvscsi_reset_state(s); - - return 0; -} - -static void -pvscsi_uninit(PCIDevice *pci_dev) -{ - PVSCSIState *s = PVSCSI(pci_dev); - - trace_pvscsi_state("uninit"); - qemu_bh_delete(s->completion_worker); - - pvscsi_cleanup_msi(s); -} - -static void -pvscsi_reset(DeviceState *dev) -{ - PCIDevice *d = PCI_DEVICE(dev); - PVSCSIState *s = PVSCSI(d); - - trace_pvscsi_state("reset"); - pvscsi_reset_adapter(s); -} - -static void -pvscsi_pre_save(void *opaque) -{ - PVSCSIState *s = (PVSCSIState *) opaque; - - trace_pvscsi_state("presave"); - - assert(QTAILQ_EMPTY(&s->pending_queue)); - assert(QTAILQ_EMPTY(&s->completion_queue)); -} - -static int -pvscsi_post_load(void *opaque, int version_id) -{ - trace_pvscsi_state("postload"); - return 0; -} - -static bool pvscsi_vmstate_need_pcie_device(void *opaque) -{ - PVSCSIState *s = PVSCSI(opaque); - - return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE); -} - -static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id) -{ - return !pvscsi_vmstate_need_pcie_device(opaque); -} - -static const VMStateDescription vmstate_pvscsi_pcie_device = { - .name = "pvscsi/pcie", - .needed = pvscsi_vmstate_need_pcie_device, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj, PVSCSIState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pvscsi = { - .name = "pvscsi", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = pvscsi_pre_save, - .post_load = pvscsi_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState, - pvscsi_vmstate_test_pci_device, 0, - vmstate_pci_device, PCIDevice), - VMSTATE_UINT8(msi_used, PVSCSIState), - VMSTATE_UINT32(resetting, PVSCSIState), - VMSTATE_UINT64(reg_interrupt_status, PVSCSIState), - VMSTATE_UINT64(reg_interrupt_enabled, PVSCSIState), - VMSTATE_UINT64(reg_command_status, PVSCSIState), - VMSTATE_UINT64(curr_cmd, PVSCSIState), - VMSTATE_UINT32(curr_cmd_data_cntr, PVSCSIState), - VMSTATE_UINT32_ARRAY(curr_cmd_data, PVSCSIState, - ARRAY_SIZE(((PVSCSIState *)NULL)->curr_cmd_data)), - VMSTATE_UINT8(rings_info_valid, PVSCSIState), - VMSTATE_UINT8(msg_ring_info_valid, PVSCSIState), - VMSTATE_UINT8(use_msg, PVSCSIState), - - VMSTATE_UINT64(rings.rs_pa, PVSCSIState), - VMSTATE_UINT32(rings.txr_len_mask, PVSCSIState), - VMSTATE_UINT32(rings.rxr_len_mask, PVSCSIState), - VMSTATE_UINT64_ARRAY(rings.req_ring_pages_pa, PVSCSIState, - PVSCSI_SETUP_RINGS_MAX_NUM_PAGES), - VMSTATE_UINT64_ARRAY(rings.cmp_ring_pages_pa, PVSCSIState, - PVSCSI_SETUP_RINGS_MAX_NUM_PAGES), - VMSTATE_UINT64(rings.consumed_ptr, PVSCSIState), - VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState), - - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_pvscsi_pcie_device, - NULL - } -}; - -static Property pvscsi_properties[] = { - DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), - DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags, - PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false), - DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags, - PVSCSI_COMPAT_DISABLE_PCIE_BIT, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pvscsi_realize(DeviceState *qdev, Error **errp) -{ - PVSCSIClass *pvs_c = PVSCSI_DEVICE_GET_CLASS(qdev); - PCIDevice *pci_dev = PCI_DEVICE(qdev); - PVSCSIState *s = PVSCSI(qdev); - - if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } - - pvs_c->parent_dc_realize(qdev, errp); -} - -static void pvscsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - PVSCSIClass *pvs_k = PVSCSI_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - k->init = pvscsi_init; - k->exit = pvscsi_uninit; - k->vendor_id = PCI_VENDOR_ID_VMWARE; - k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI; - k->class_id = PCI_CLASS_STORAGE_SCSI; - k->subsystem_id = 0x1000; - pvs_k->parent_dc_realize = dc->realize; - dc->realize = pvscsi_realize; - dc->reset = pvscsi_reset; - dc->vmsd = &vmstate_pvscsi; - dc->props = pvscsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - hc->unplug = pvscsi_hot_unplug; - hc->plug = pvscsi_hotplug; -} - -static const TypeInfo pvscsi_info = { - .name = TYPE_PVSCSI, - .parent = TYPE_PCI_DEVICE, - .class_size = sizeof(PVSCSIClass), - .instance_size = sizeof(PVSCSIState), - .class_init = pvscsi_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void -pvscsi_register_types(void) -{ - type_register_static(&pvscsi_info); -} - -type_init(pvscsi_register_types); diff --git a/qemu/hw/scsi/vmw_pvscsi.h b/qemu/hw/scsi/vmw_pvscsi.h deleted file mode 100644 index 17fcf6627..000000000 --- a/qemu/hw/scsi/vmw_pvscsi.h +++ /dev/null @@ -1,434 +0,0 @@ -/* - * VMware PVSCSI header file - * - * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; version 2 of the License and no later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained by: Arvind Kumar <arvindkumar@vmware.com> - * - */ - -#ifndef VMW_PVSCSI_H -#define VMW_PVSCSI_H - -#define VMW_PAGE_SIZE (4096) -#define VMW_PAGE_SHIFT (12) - -#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */ - -/* - * host adapter status/error codes - */ -enum HostBusAdapterStatus { - BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */ - BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a, - BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b, - BTSTAT_DATA_UNDERRUN = 0x0c, - BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */ - BTSTAT_DATARUN = 0x12, /* data overrun/underrun */ - BTSTAT_BUSFREE = 0x13, /* unexpected bus free */ - BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence */ - /* requested by target */ - BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN */ - /* from first CCB */ - BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */ - BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message */ - /* rejected by target */ - BTSTAT_BADMSG = 0x1d, /* unsupported message received by */ - /* the host adapter */ - BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */ - BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, */ - /* sent a SCSI RST */ - BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */ - BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */ - BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly */ - /* (w/o tag) */ - BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */ - BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */ - BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */ - BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */ - BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */ -}; - -/* - * Register offsets. - * - * These registers are accessible both via i/o space and mm i/o. - */ - -enum PVSCSIRegOffset { - PVSCSI_REG_OFFSET_COMMAND = 0x0, - PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4, - PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8, - PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100, - PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104, - PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108, - PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c, - PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c, - PVSCSI_REG_OFFSET_INTR_MASK = 0x2010, - PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014, - PVSCSI_REG_OFFSET_DEBUG = 0x3018, - PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018, -}; - -/* - * Virtual h/w commands. - */ - -enum PVSCSICommands { - PVSCSI_CMD_FIRST = 0, /* has to be first */ - - PVSCSI_CMD_ADAPTER_RESET = 1, - PVSCSI_CMD_ISSUE_SCSI = 2, - PVSCSI_CMD_SETUP_RINGS = 3, - PVSCSI_CMD_RESET_BUS = 4, - PVSCSI_CMD_RESET_DEVICE = 5, - PVSCSI_CMD_ABORT_CMD = 6, - PVSCSI_CMD_CONFIG = 7, - PVSCSI_CMD_SETUP_MSG_RING = 8, - PVSCSI_CMD_DEVICE_UNPLUG = 9, - - PVSCSI_CMD_LAST = 10 /* has to be last */ -}; - -#define PVSCSI_COMMAND_PROCESSING_SUCCEEDED (0) -#define PVSCSI_COMMAND_PROCESSING_FAILED (-1) -#define PVSCSI_COMMAND_NOT_ENOUGH_DATA (-2) - -/* - * Command descriptor for PVSCSI_CMD_RESET_DEVICE -- - */ - -struct PVSCSICmdDescResetDevice { - uint32_t target; - uint8_t lun[8]; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescResetDevice PVSCSICmdDescResetDevice; - -/* - * Command descriptor for PVSCSI_CMD_ABORT_CMD -- - * - * - currently does not support specifying the LUN. - * - pad should be 0. - */ - -struct PVSCSICmdDescAbortCmd { - uint64_t context; - uint32_t target; - uint32_t pad; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescAbortCmd PVSCSICmdDescAbortCmd; - -/* - * Command descriptor for PVSCSI_CMD_SETUP_RINGS -- - * - * Notes: - * - reqRingNumPages and cmpRingNumPages need to be power of two. - * - reqRingNumPages and cmpRingNumPages need to be different from 0, - * - reqRingNumPages and cmpRingNumPages need to be inferior to - * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES. - */ - -#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32 -struct PVSCSICmdDescSetupRings { - uint32_t reqRingNumPages; - uint32_t cmpRingNumPages; - uint64_t ringsStatePPN; - uint64_t reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; - uint64_t cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescSetupRings PVSCSICmdDescSetupRings; - -/* - * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING -- - * - * Notes: - * - this command was not supported in the initial revision of the h/w - * interface. Before using it, you need to check that it is supported by - * writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then - * immediately after read the 'command status' register: - * * a value of -1 means that the cmd is NOT supported, - * * a value != -1 means that the cmd IS supported. - * If it's supported the 'command status' register should return: - * sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t). - * - this command should be issued _after_ the usual SETUP_RINGS so that the - * RingsState page is already setup. If not, the command is a nop. - * - numPages needs to be a power of two, - * - numPages needs to be different from 0, - * - pad should be zero. - */ - -#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16 - -struct PVSCSICmdDescSetupMsgRing { - uint32_t numPages; - uint32_t pad; - uint64_t ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES]; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescSetupMsgRing PVSCSICmdDescSetupMsgRing; - -enum PVSCSIMsgType { - PVSCSI_MSG_DEV_ADDED = 0, - PVSCSI_MSG_DEV_REMOVED = 1, - PVSCSI_MSG_LAST = 2, -}; - -/* - * Msg descriptor. - * - * sizeof(struct PVSCSIRingMsgDesc) == 128. - * - * - type is of type enum PVSCSIMsgType. - * - the content of args depend on the type of event being delivered. - */ - -struct PVSCSIRingMsgDesc { - uint32_t type; - uint32_t args[31]; -} QEMU_PACKED; - -typedef struct PVSCSIRingMsgDesc PVSCSIRingMsgDesc; - -struct PVSCSIMsgDescDevStatusChanged { - uint32_t type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */ - uint32_t bus; - uint32_t target; - uint8_t lun[8]; - uint32_t pad[27]; -} QEMU_PACKED; - -typedef struct PVSCSIMsgDescDevStatusChanged PVSCSIMsgDescDevStatusChanged; - -/* - * Rings state. - * - * - the fields: - * . msgProdIdx, - * . msgConsIdx, - * . msgNumEntriesLog2, - * .. are only used once the SETUP_MSG_RING cmd has been issued. - * - 'pad' helps to ensure that the msg related fields are on their own - * cache-line. - */ - -struct PVSCSIRingsState { - uint32_t reqProdIdx; - uint32_t reqConsIdx; - uint32_t reqNumEntriesLog2; - - uint32_t cmpProdIdx; - uint32_t cmpConsIdx; - uint32_t cmpNumEntriesLog2; - - uint8_t pad[104]; - - uint32_t msgProdIdx; - uint32_t msgConsIdx; - uint32_t msgNumEntriesLog2; -} QEMU_PACKED; - -typedef struct PVSCSIRingsState PVSCSIRingsState; - -/* - * Request descriptor. - * - * sizeof(RingReqDesc) = 128 - * - * - context: is a unique identifier of a command. It could normally be any - * 64bit value, however we currently store it in the serialNumber variable - * of struct SCSI_Command, so we have the following restrictions due to the - * way this field is handled in the vmkernel storage stack: - * * this value can't be 0, - * * the upper 32bit need to be 0 since serialNumber is as a uint32_t. - * Currently tracked as PR 292060. - * - dataLen: contains the total number of bytes that need to be transferred. - * - dataAddr: - * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first - * s/g table segment, each s/g segment is entirely contained on a single - * page of physical memory, - * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of - * the buffer used for the DMA transfer, - * - flags: - * * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above, - * * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved, - * * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory, - * * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device, - * * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than - * 16bytes. To be specified. - * - vcpuHint: vcpuId of the processor that will be most likely waiting for the - * completion of the i/o. For guest OSes that use lowest priority message - * delivery mode (such as windows), we use this "hint" to deliver the - * completion action to the proper vcpu. For now, we can use the vcpuId of - * the processor that initiated the i/o as a likely candidate for the vcpu - * that will be waiting for the completion.. - * - bus should be 0: we currently only support bus 0 for now. - * - unused should be zero'd. - */ - -#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0) -#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1) -#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2) -#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3) -#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4) - -#define PVSCSI_KNOWN_FLAGS \ - (PVSCSI_FLAG_CMD_WITH_SG_LIST | \ - PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB | \ - PVSCSI_FLAG_CMD_DIR_NONE | \ - PVSCSI_FLAG_CMD_DIR_TOHOST | \ - PVSCSI_FLAG_CMD_DIR_TODEVICE) - -struct PVSCSIRingReqDesc { - uint64_t context; - uint64_t dataAddr; - uint64_t dataLen; - uint64_t senseAddr; - uint32_t senseLen; - uint32_t flags; - uint8_t cdb[16]; - uint8_t cdbLen; - uint8_t lun[8]; - uint8_t tag; - uint8_t bus; - uint8_t target; - uint8_t vcpuHint; - uint8_t unused[59]; -} QEMU_PACKED; - -typedef struct PVSCSIRingReqDesc PVSCSIRingReqDesc; - -/* - * Scatter-gather list management. - * - * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the - * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g - * table segment. - * - * - each segment of the s/g table contain a succession of struct - * PVSCSISGElement. - * - each segment is entirely contained on a single physical page of memory. - * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in - * PVSCSISGElement.flags and in this case: - * * addr is the PA of the next s/g segment, - * * length is undefined, assumed to be 0. - */ - -struct PVSCSISGElement { - uint64_t addr; - uint32_t length; - uint32_t flags; -} QEMU_PACKED; - -typedef struct PVSCSISGElement PVSCSISGElement; - -/* - * Completion descriptor. - * - * sizeof(RingCmpDesc) = 32 - * - * - context: identifier of the command. The same thing that was specified - * under "context" as part of struct RingReqDesc at initiation time, - * - dataLen: number of bytes transferred for the actual i/o operation, - * - senseLen: number of bytes written into the sense buffer, - * - hostStatus: adapter status, - * - scsiStatus: device status, - * - pad should be zero. - */ - -struct PVSCSIRingCmpDesc { - uint64_t context; - uint64_t dataLen; - uint32_t senseLen; - uint16_t hostStatus; - uint16_t scsiStatus; - uint32_t pad[2]; -} QEMU_PACKED; - -typedef struct PVSCSIRingCmpDesc PVSCSIRingCmpDesc; - -/* - * Interrupt status / IRQ bits. - */ - -#define PVSCSI_INTR_CMPL_0 (1 << 0) -#define PVSCSI_INTR_CMPL_1 (1 << 1) -#define PVSCSI_INTR_CMPL_MASK MASK(2) - -#define PVSCSI_INTR_MSG_0 (1 << 2) -#define PVSCSI_INTR_MSG_1 (1 << 3) -#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2) - -#define PVSCSI_INTR_ALL_SUPPORTED MASK(4) - -/* - * Number of MSI-X vectors supported. - */ -#define PVSCSI_MAX_INTRS 24 - -/* - * Enumeration of supported MSI-X vectors - */ -#define PVSCSI_VECTOR_COMPLETION 0 - -/* - * Misc constants for the rings. - */ - -#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES -#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES -#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES - -#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \ - (VMW_PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc)) - -#define PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE \ - (VMW_PAGE_SIZE / sizeof(PVSCSIRingCmpDesc)) - -#define PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE \ - (VMW_PAGE_SIZE / sizeof(PVSCSIRingMsgDesc)) - -#define PVSCSI_MAX_REQ_QUEUE_DEPTH \ - (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE) - -#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1 -#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1 -#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2 -#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2 -#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2 - -enum PVSCSIMemSpace { - PVSCSI_MEM_SPACE_COMMAND_PAGE = 0, - PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1, - PVSCSI_MEM_SPACE_MISC_PAGE = 2, - PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4, - PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6, - PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7, -}; - -#define PVSCSI_MEM_SPACE_NUM_PAGES \ - (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \ - PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \ - PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \ - PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \ - PVSCSI_MEM_SPACE_MSIX_NUM_PAGES) - -#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * VMW_PAGE_SIZE) - -#endif /* VMW_PVSCSI_H */ |