diff options
Diffstat (limited to 'qemu/hw/scsi/vmw_pvscsi.c')
-rw-r--r-- | qemu/hw/scsi/vmw_pvscsi.c | 1305 |
1 files changed, 0 insertions, 1305 deletions
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); |