xref: /openbmc/qemu/hw/scsi/vmw_pvscsi.c (revision 739e95f5)
1881d588aSDmitry Fleytman /*
2881d588aSDmitry Fleytman  * QEMU VMWARE PVSCSI paravirtual SCSI bus
3881d588aSDmitry Fleytman  *
4881d588aSDmitry Fleytman  * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
5881d588aSDmitry Fleytman  *
6881d588aSDmitry Fleytman  * Developed by Daynix Computing LTD (http://www.daynix.com)
7881d588aSDmitry Fleytman  *
8881d588aSDmitry Fleytman  * Based on implementation by Paolo Bonzini
9881d588aSDmitry Fleytman  * http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg00729.html
10881d588aSDmitry Fleytman  *
11881d588aSDmitry Fleytman  * Authors:
12881d588aSDmitry Fleytman  * Paolo Bonzini <pbonzini@redhat.com>
13881d588aSDmitry Fleytman  * Dmitry Fleytman <dmitry@daynix.com>
14881d588aSDmitry Fleytman  * Yan Vugenfirer <yan@daynix.com>
15881d588aSDmitry Fleytman  *
16881d588aSDmitry Fleytman  * This work is licensed under the terms of the GNU GPL, version 2.
17881d588aSDmitry Fleytman  * See the COPYING file in the top-level directory.
18881d588aSDmitry Fleytman  *
19881d588aSDmitry Fleytman  * NOTE about MSI-X:
20881d588aSDmitry Fleytman  * MSI-X support has been removed for the moment because it leads Windows OS
21881d588aSDmitry Fleytman  * to crash on startup. The crash happens because Windows driver requires
22881d588aSDmitry Fleytman  * MSI-X shared memory to be part of the same BAR used for rings state
23881d588aSDmitry Fleytman  * registers, etc. This is not supported by QEMU infrastructure so separate
24881d588aSDmitry Fleytman  * BAR created from MSI-X purposes. Windows driver fails to deal with 2 BARs.
25881d588aSDmitry Fleytman  *
26881d588aSDmitry Fleytman  */
27881d588aSDmitry Fleytman 
28a4ab4792SPeter Maydell #include "qemu/osdep.h"
29da34e65cSMarkus Armbruster #include "qapi/error.h"
30db725815SMarkus Armbruster #include "qemu/main-loop.h"
310b8fa32fSMarkus Armbruster #include "qemu/module.h"
32881d588aSDmitry Fleytman #include "hw/scsi/scsi.h"
33d6454270SMarkus Armbruster #include "migration/vmstate.h"
3408e2c9f1SPaolo Bonzini #include "scsi/constants.h"
35881d588aSDmitry Fleytman #include "hw/pci/msi.h"
36a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
37881d588aSDmitry Fleytman #include "vmw_pvscsi.h"
38881d588aSDmitry Fleytman #include "trace.h"
39db1015e9SEduardo Habkost #include "qom/object.h"
40881d588aSDmitry Fleytman 
41881d588aSDmitry Fleytman 
42881d588aSDmitry Fleytman #define PVSCSI_USE_64BIT         (true)
43881d588aSDmitry Fleytman #define PVSCSI_PER_VECTOR_MASK   (false)
44881d588aSDmitry Fleytman 
45881d588aSDmitry Fleytman #define PVSCSI_MAX_DEVS                   (64)
46881d588aSDmitry Fleytman #define PVSCSI_MSIX_NUM_VECTORS           (1)
47881d588aSDmitry Fleytman 
4849adc5d3SPrasad J Pandit #define PVSCSI_MAX_SG_ELEM                2048
4949adc5d3SPrasad J Pandit 
50881d588aSDmitry Fleytman #define PVSCSI_MAX_CMD_DATA_WORDS \
51881d588aSDmitry Fleytman     (sizeof(PVSCSICmdDescSetupRings)/sizeof(uint32_t))
52881d588aSDmitry Fleytman 
530dc40f28SPaolo Bonzini #define RS_GET_FIELD(m, field) \
540dc40f28SPaolo Bonzini     (ldl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \
550dc40f28SPaolo Bonzini                  (m)->rs_pa + offsetof(struct PVSCSIRingsState, field)))
560dc40f28SPaolo Bonzini #define RS_SET_FIELD(m, field, val) \
570dc40f28SPaolo Bonzini     (stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \
580dc40f28SPaolo Bonzini                  (m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val))
59881d588aSDmitry Fleytman 
60db1015e9SEduardo Habkost struct PVSCSIClass {
61e2d4f3f7SShmulik Ladkani     PCIDeviceClass parent_class;
621dd1305eSShmulik Ladkani     DeviceRealize parent_dc_realize;
63db1015e9SEduardo Habkost };
64e2d4f3f7SShmulik Ladkani 
65881d588aSDmitry Fleytman #define TYPE_PVSCSI "pvscsi"
66a489d195SEduardo Habkost OBJECT_DECLARE_TYPE(PVSCSIState, PVSCSIClass, PVSCSI)
67881d588aSDmitry Fleytman 
68e2d4f3f7SShmulik Ladkani 
69cb8d4c8fSStefan Weil /* Compatibility flags for migration */
70d29d4ff8SShmulik Ladkani #define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0
71d29d4ff8SShmulik Ladkani #define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \
72d29d4ff8SShmulik Ladkani     (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT)
731dd1305eSShmulik Ladkani #define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1
741dd1305eSShmulik Ladkani #define PVSCSI_COMPAT_DISABLE_PCIE \
751dd1305eSShmulik Ladkani     (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT)
76d29d4ff8SShmulik Ladkani 
77d29d4ff8SShmulik Ladkani #define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \
78d29d4ff8SShmulik Ladkani     ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION)
79836fc48cSShmulik Ladkani #define PVSCSI_MSI_OFFSET(s) \
80836fc48cSShmulik Ladkani     (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c)
811dd1305eSShmulik Ladkani #define PVSCSI_EXP_EP_OFFSET (0x40)
82d29d4ff8SShmulik Ladkani 
83881d588aSDmitry Fleytman typedef struct PVSCSIRingInfo {
84881d588aSDmitry Fleytman     uint64_t            rs_pa;
85881d588aSDmitry Fleytman     uint32_t            txr_len_mask;
86881d588aSDmitry Fleytman     uint32_t            rxr_len_mask;
87881d588aSDmitry Fleytman     uint32_t            msg_len_mask;
88881d588aSDmitry Fleytman     uint64_t            req_ring_pages_pa[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
89881d588aSDmitry Fleytman     uint64_t            cmp_ring_pages_pa[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
90881d588aSDmitry Fleytman     uint64_t            msg_ring_pages_pa[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
91881d588aSDmitry Fleytman     uint64_t            consumed_ptr;
92881d588aSDmitry Fleytman     uint64_t            filled_cmp_ptr;
93881d588aSDmitry Fleytman     uint64_t            filled_msg_ptr;
94881d588aSDmitry Fleytman } PVSCSIRingInfo;
95881d588aSDmitry Fleytman 
96881d588aSDmitry Fleytman typedef struct PVSCSISGState {
97881d588aSDmitry Fleytman     hwaddr elemAddr;
98881d588aSDmitry Fleytman     hwaddr dataAddr;
99881d588aSDmitry Fleytman     uint32_t resid;
100881d588aSDmitry Fleytman } PVSCSISGState;
101881d588aSDmitry Fleytman 
102881d588aSDmitry Fleytman typedef QTAILQ_HEAD(, PVSCSIRequest) PVSCSIRequestList;
103881d588aSDmitry Fleytman 
104db1015e9SEduardo Habkost struct PVSCSIState {
105881d588aSDmitry Fleytman     PCIDevice parent_obj;
106881d588aSDmitry Fleytman     MemoryRegion io_space;
107881d588aSDmitry Fleytman     SCSIBus bus;
108881d588aSDmitry Fleytman     QEMUBH *completion_worker;
109881d588aSDmitry Fleytman     PVSCSIRequestList pending_queue;
110881d588aSDmitry Fleytman     PVSCSIRequestList completion_queue;
111881d588aSDmitry Fleytman 
112881d588aSDmitry Fleytman     uint64_t reg_interrupt_status;        /* Interrupt status register value */
113881d588aSDmitry Fleytman     uint64_t reg_interrupt_enabled;       /* Interrupt mask register value   */
114881d588aSDmitry Fleytman     uint64_t reg_command_status;          /* Command status register value   */
115881d588aSDmitry Fleytman 
116881d588aSDmitry Fleytman     /* Command data adoption mechanism */
117881d588aSDmitry Fleytman     uint64_t curr_cmd;                   /* Last command arrived             */
118881d588aSDmitry Fleytman     uint32_t curr_cmd_data_cntr;         /* Amount of data for last command  */
119881d588aSDmitry Fleytman 
120881d588aSDmitry Fleytman     /* Collector for current command data */
121881d588aSDmitry Fleytman     uint32_t curr_cmd_data[PVSCSI_MAX_CMD_DATA_WORDS];
122881d588aSDmitry Fleytman 
123881d588aSDmitry Fleytman     uint8_t rings_info_valid;            /* Whether data rings initialized   */
124881d588aSDmitry Fleytman     uint8_t msg_ring_info_valid;         /* Whether message ring initialized */
125881d588aSDmitry Fleytman     uint8_t use_msg;                     /* Whether to use message ring      */
126881d588aSDmitry Fleytman 
127269fe4c3SCao jin     uint8_t msi_used;                    /* For migration compatibility      */
128881d588aSDmitry Fleytman     PVSCSIRingInfo rings;                /* Data transfer rings manager      */
129881d588aSDmitry Fleytman     uint32_t resetting;                  /* Reset in progress                */
130d29d4ff8SShmulik Ladkani 
131d29d4ff8SShmulik Ladkani     uint32_t compat_flags;
132db1015e9SEduardo Habkost };
133881d588aSDmitry Fleytman 
134881d588aSDmitry Fleytman typedef struct PVSCSIRequest {
135881d588aSDmitry Fleytman     SCSIRequest *sreq;
136881d588aSDmitry Fleytman     PVSCSIState *dev;
137881d588aSDmitry Fleytman     uint8_t sense_key;
138881d588aSDmitry Fleytman     uint8_t completed;
139881d588aSDmitry Fleytman     int lun;
140881d588aSDmitry Fleytman     QEMUSGList sgl;
141881d588aSDmitry Fleytman     PVSCSISGState sg;
142881d588aSDmitry Fleytman     struct PVSCSIRingReqDesc req;
143881d588aSDmitry Fleytman     struct PVSCSIRingCmpDesc cmp;
144881d588aSDmitry Fleytman     QTAILQ_ENTRY(PVSCSIRequest) next;
145881d588aSDmitry Fleytman } PVSCSIRequest;
146881d588aSDmitry Fleytman 
147881d588aSDmitry Fleytman /* Integer binary logarithm */
148881d588aSDmitry Fleytman static int
149881d588aSDmitry Fleytman pvscsi_log2(uint32_t input)
150881d588aSDmitry Fleytman {
151881d588aSDmitry Fleytman     int log = 0;
152881d588aSDmitry Fleytman     assert(input > 0);
153881d588aSDmitry Fleytman     while (input >> ++log) {
154881d588aSDmitry Fleytman     }
155881d588aSDmitry Fleytman     return log;
156881d588aSDmitry Fleytman }
157881d588aSDmitry Fleytman 
1587f61f469SPrasad J Pandit static void
159881d588aSDmitry Fleytman pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
160881d588aSDmitry Fleytman {
161881d588aSDmitry Fleytman     int i;
162881d588aSDmitry Fleytman     uint32_t txr_len_log2, rxr_len_log2;
163881d588aSDmitry Fleytman     uint32_t req_ring_size, cmp_ring_size;
164881d588aSDmitry Fleytman     m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT;
165881d588aSDmitry Fleytman 
166881d588aSDmitry Fleytman     req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
167881d588aSDmitry Fleytman     cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
168881d588aSDmitry Fleytman     txr_len_log2 = pvscsi_log2(req_ring_size - 1);
169881d588aSDmitry Fleytman     rxr_len_log2 = pvscsi_log2(cmp_ring_size - 1);
170881d588aSDmitry Fleytman 
171881d588aSDmitry Fleytman     m->txr_len_mask = MASK(txr_len_log2);
172881d588aSDmitry Fleytman     m->rxr_len_mask = MASK(rxr_len_log2);
173881d588aSDmitry Fleytman 
174881d588aSDmitry Fleytman     m->consumed_ptr = 0;
175881d588aSDmitry Fleytman     m->filled_cmp_ptr = 0;
176881d588aSDmitry Fleytman 
177881d588aSDmitry Fleytman     for (i = 0; i < ri->reqRingNumPages; i++) {
178881d588aSDmitry Fleytman         m->req_ring_pages_pa[i] = ri->reqRingPPNs[i] << VMW_PAGE_SHIFT;
179881d588aSDmitry Fleytman     }
180881d588aSDmitry Fleytman 
181881d588aSDmitry Fleytman     for (i = 0; i < ri->cmpRingNumPages; i++) {
182881d588aSDmitry Fleytman         m->cmp_ring_pages_pa[i] = ri->cmpRingPPNs[i] << VMW_PAGE_SHIFT;
183881d588aSDmitry Fleytman     }
184881d588aSDmitry Fleytman 
1850dc40f28SPaolo Bonzini     RS_SET_FIELD(m, reqProdIdx, 0);
1860dc40f28SPaolo Bonzini     RS_SET_FIELD(m, reqConsIdx, 0);
1870dc40f28SPaolo Bonzini     RS_SET_FIELD(m, reqNumEntriesLog2, txr_len_log2);
188881d588aSDmitry Fleytman 
1890dc40f28SPaolo Bonzini     RS_SET_FIELD(m, cmpProdIdx, 0);
1900dc40f28SPaolo Bonzini     RS_SET_FIELD(m, cmpConsIdx, 0);
1910dc40f28SPaolo Bonzini     RS_SET_FIELD(m, cmpNumEntriesLog2, rxr_len_log2);
192881d588aSDmitry Fleytman 
193881d588aSDmitry Fleytman     trace_pvscsi_ring_init_data(txr_len_log2, rxr_len_log2);
194881d588aSDmitry Fleytman 
195881d588aSDmitry Fleytman     /* Flush ring state page changes */
196881d588aSDmitry Fleytman     smp_wmb();
197881d588aSDmitry Fleytman }
198881d588aSDmitry Fleytman 
1993e831b40SPrasad J Pandit static int
200881d588aSDmitry Fleytman pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri)
201881d588aSDmitry Fleytman {
202881d588aSDmitry Fleytman     int i;
203881d588aSDmitry Fleytman     uint32_t len_log2;
204881d588aSDmitry Fleytman     uint32_t ring_size;
205881d588aSDmitry Fleytman 
206f6882698SP J P     if (!ri->numPages || ri->numPages > PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES) {
2073e831b40SPrasad J Pandit         return -1;
2083e831b40SPrasad J Pandit     }
209881d588aSDmitry Fleytman     ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE;
210881d588aSDmitry Fleytman     len_log2 = pvscsi_log2(ring_size - 1);
211881d588aSDmitry Fleytman 
212881d588aSDmitry Fleytman     m->msg_len_mask = MASK(len_log2);
213881d588aSDmitry Fleytman 
214881d588aSDmitry Fleytman     m->filled_msg_ptr = 0;
215881d588aSDmitry Fleytman 
216881d588aSDmitry Fleytman     for (i = 0; i < ri->numPages; i++) {
217881d588aSDmitry Fleytman         m->msg_ring_pages_pa[i] = ri->ringPPNs[i] << VMW_PAGE_SHIFT;
218881d588aSDmitry Fleytman     }
219881d588aSDmitry Fleytman 
2200dc40f28SPaolo Bonzini     RS_SET_FIELD(m, msgProdIdx, 0);
2210dc40f28SPaolo Bonzini     RS_SET_FIELD(m, msgConsIdx, 0);
2220dc40f28SPaolo Bonzini     RS_SET_FIELD(m, msgNumEntriesLog2, len_log2);
223881d588aSDmitry Fleytman 
224881d588aSDmitry Fleytman     trace_pvscsi_ring_init_msg(len_log2);
225881d588aSDmitry Fleytman 
226881d588aSDmitry Fleytman     /* Flush ring state page changes */
227881d588aSDmitry Fleytman     smp_wmb();
2283e831b40SPrasad J Pandit 
2293e831b40SPrasad J Pandit     return 0;
230881d588aSDmitry Fleytman }
231881d588aSDmitry Fleytman 
232881d588aSDmitry Fleytman static void
233881d588aSDmitry Fleytman pvscsi_ring_cleanup(PVSCSIRingInfo *mgr)
234881d588aSDmitry Fleytman {
235881d588aSDmitry Fleytman     mgr->rs_pa = 0;
236881d588aSDmitry Fleytman     mgr->txr_len_mask = 0;
237881d588aSDmitry Fleytman     mgr->rxr_len_mask = 0;
238881d588aSDmitry Fleytman     mgr->msg_len_mask = 0;
239881d588aSDmitry Fleytman     mgr->consumed_ptr = 0;
240881d588aSDmitry Fleytman     mgr->filled_cmp_ptr = 0;
241881d588aSDmitry Fleytman     mgr->filled_msg_ptr = 0;
242881d588aSDmitry Fleytman     memset(mgr->req_ring_pages_pa, 0, sizeof(mgr->req_ring_pages_pa));
243881d588aSDmitry Fleytman     memset(mgr->cmp_ring_pages_pa, 0, sizeof(mgr->cmp_ring_pages_pa));
244881d588aSDmitry Fleytman     memset(mgr->msg_ring_pages_pa, 0, sizeof(mgr->msg_ring_pages_pa));
245881d588aSDmitry Fleytman }
246881d588aSDmitry Fleytman 
247881d588aSDmitry Fleytman static hwaddr
248881d588aSDmitry Fleytman pvscsi_ring_pop_req_descr(PVSCSIRingInfo *mgr)
249881d588aSDmitry Fleytman {
2500dc40f28SPaolo Bonzini     uint32_t ready_ptr = RS_GET_FIELD(mgr, reqProdIdx);
251d251157aSPrasad J Pandit     uint32_t ring_size = PVSCSI_MAX_NUM_PAGES_REQ_RING
252d251157aSPrasad J Pandit                             * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
253881d588aSDmitry Fleytman 
254d251157aSPrasad J Pandit     if (ready_ptr != mgr->consumed_ptr
255d251157aSPrasad J Pandit         && ready_ptr - mgr->consumed_ptr < ring_size) {
256881d588aSDmitry Fleytman         uint32_t next_ready_ptr =
257881d588aSDmitry Fleytman             mgr->consumed_ptr++ & mgr->txr_len_mask;
258881d588aSDmitry Fleytman         uint32_t next_ready_page =
259881d588aSDmitry Fleytman             next_ready_ptr / PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
260881d588aSDmitry Fleytman         uint32_t inpage_idx =
261881d588aSDmitry Fleytman             next_ready_ptr % PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
262881d588aSDmitry Fleytman 
263881d588aSDmitry Fleytman         return mgr->req_ring_pages_pa[next_ready_page] +
264881d588aSDmitry Fleytman                inpage_idx * sizeof(PVSCSIRingReqDesc);
265881d588aSDmitry Fleytman     } else {
266881d588aSDmitry Fleytman         return 0;
267881d588aSDmitry Fleytman     }
268881d588aSDmitry Fleytman }
269881d588aSDmitry Fleytman 
270881d588aSDmitry Fleytman static void
271881d588aSDmitry Fleytman pvscsi_ring_flush_req(PVSCSIRingInfo *mgr)
272881d588aSDmitry Fleytman {
2730dc40f28SPaolo Bonzini     RS_SET_FIELD(mgr, reqConsIdx, mgr->consumed_ptr);
274881d588aSDmitry Fleytman }
275881d588aSDmitry Fleytman 
276881d588aSDmitry Fleytman static hwaddr
277881d588aSDmitry Fleytman pvscsi_ring_pop_cmp_descr(PVSCSIRingInfo *mgr)
278881d588aSDmitry Fleytman {
279881d588aSDmitry Fleytman     /*
280881d588aSDmitry Fleytman      * According to Linux driver code it explicitly verifies that number
281881d588aSDmitry Fleytman      * of requests being processed by device is less then the size of
282881d588aSDmitry Fleytman      * completion queue, so device may omit completion queue overflow
283881d588aSDmitry Fleytman      * conditions check. We assume that this is true for other (Windows)
284881d588aSDmitry Fleytman      * drivers as well.
285881d588aSDmitry Fleytman      */
286881d588aSDmitry Fleytman 
287881d588aSDmitry Fleytman     uint32_t free_cmp_ptr =
288881d588aSDmitry Fleytman         mgr->filled_cmp_ptr++ & mgr->rxr_len_mask;
289881d588aSDmitry Fleytman     uint32_t free_cmp_page =
290881d588aSDmitry Fleytman         free_cmp_ptr / PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
291881d588aSDmitry Fleytman     uint32_t inpage_idx =
292881d588aSDmitry Fleytman         free_cmp_ptr % PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
293881d588aSDmitry Fleytman     return mgr->cmp_ring_pages_pa[free_cmp_page] +
294881d588aSDmitry Fleytman            inpage_idx * sizeof(PVSCSIRingCmpDesc);
295881d588aSDmitry Fleytman }
296881d588aSDmitry Fleytman 
297881d588aSDmitry Fleytman static hwaddr
298881d588aSDmitry Fleytman pvscsi_ring_pop_msg_descr(PVSCSIRingInfo *mgr)
299881d588aSDmitry Fleytman {
300881d588aSDmitry Fleytman     uint32_t free_msg_ptr =
301881d588aSDmitry Fleytman         mgr->filled_msg_ptr++ & mgr->msg_len_mask;
302881d588aSDmitry Fleytman     uint32_t free_msg_page =
303881d588aSDmitry Fleytman         free_msg_ptr / PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE;
304881d588aSDmitry Fleytman     uint32_t inpage_idx =
305881d588aSDmitry Fleytman         free_msg_ptr % PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE;
306881d588aSDmitry Fleytman     return mgr->msg_ring_pages_pa[free_msg_page] +
307881d588aSDmitry Fleytman            inpage_idx * sizeof(PVSCSIRingMsgDesc);
308881d588aSDmitry Fleytman }
309881d588aSDmitry Fleytman 
310881d588aSDmitry Fleytman static void
311881d588aSDmitry Fleytman pvscsi_ring_flush_cmp(PVSCSIRingInfo *mgr)
312881d588aSDmitry Fleytman {
313881d588aSDmitry Fleytman     /* Flush descriptor changes */
314881d588aSDmitry Fleytman     smp_wmb();
315881d588aSDmitry Fleytman 
316881d588aSDmitry Fleytman     trace_pvscsi_ring_flush_cmp(mgr->filled_cmp_ptr);
317881d588aSDmitry Fleytman 
3180dc40f28SPaolo Bonzini     RS_SET_FIELD(mgr, cmpProdIdx, mgr->filled_cmp_ptr);
319881d588aSDmitry Fleytman }
320881d588aSDmitry Fleytman 
321881d588aSDmitry Fleytman static bool
322881d588aSDmitry Fleytman pvscsi_ring_msg_has_room(PVSCSIRingInfo *mgr)
323881d588aSDmitry Fleytman {
3240dc40f28SPaolo Bonzini     uint32_t prodIdx = RS_GET_FIELD(mgr, msgProdIdx);
3250dc40f28SPaolo Bonzini     uint32_t consIdx = RS_GET_FIELD(mgr, msgConsIdx);
326881d588aSDmitry Fleytman 
327881d588aSDmitry Fleytman     return (prodIdx - consIdx) < (mgr->msg_len_mask + 1);
328881d588aSDmitry Fleytman }
329881d588aSDmitry Fleytman 
330881d588aSDmitry Fleytman static void
331881d588aSDmitry Fleytman pvscsi_ring_flush_msg(PVSCSIRingInfo *mgr)
332881d588aSDmitry Fleytman {
333881d588aSDmitry Fleytman     /* Flush descriptor changes */
334881d588aSDmitry Fleytman     smp_wmb();
335881d588aSDmitry Fleytman 
336881d588aSDmitry Fleytman     trace_pvscsi_ring_flush_msg(mgr->filled_msg_ptr);
337881d588aSDmitry Fleytman 
3380dc40f28SPaolo Bonzini     RS_SET_FIELD(mgr, msgProdIdx, mgr->filled_msg_ptr);
339881d588aSDmitry Fleytman }
340881d588aSDmitry Fleytman 
341881d588aSDmitry Fleytman static void
342881d588aSDmitry Fleytman pvscsi_reset_state(PVSCSIState *s)
343881d588aSDmitry Fleytman {
344881d588aSDmitry Fleytman     s->curr_cmd = PVSCSI_CMD_FIRST;
345881d588aSDmitry Fleytman     s->curr_cmd_data_cntr = 0;
346881d588aSDmitry Fleytman     s->reg_command_status = PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
347881d588aSDmitry Fleytman     s->reg_interrupt_status = 0;
348881d588aSDmitry Fleytman     pvscsi_ring_cleanup(&s->rings);
349881d588aSDmitry Fleytman     s->rings_info_valid = FALSE;
350881d588aSDmitry Fleytman     s->msg_ring_info_valid = FALSE;
351881d588aSDmitry Fleytman     QTAILQ_INIT(&s->pending_queue);
352881d588aSDmitry Fleytman     QTAILQ_INIT(&s->completion_queue);
353881d588aSDmitry Fleytman }
354881d588aSDmitry Fleytman 
355881d588aSDmitry Fleytman static void
356881d588aSDmitry Fleytman pvscsi_update_irq_status(PVSCSIState *s)
357881d588aSDmitry Fleytman {
358881d588aSDmitry Fleytman     PCIDevice *d = PCI_DEVICE(s);
359881d588aSDmitry Fleytman     bool should_raise = s->reg_interrupt_enabled & s->reg_interrupt_status;
360881d588aSDmitry Fleytman 
361881d588aSDmitry Fleytman     trace_pvscsi_update_irq_level(should_raise, s->reg_interrupt_enabled,
362881d588aSDmitry Fleytman                                   s->reg_interrupt_status);
363881d588aSDmitry Fleytman 
364269fe4c3SCao jin     if (msi_enabled(d)) {
365881d588aSDmitry Fleytman         if (should_raise) {
366881d588aSDmitry Fleytman             trace_pvscsi_update_irq_msi();
367881d588aSDmitry Fleytman             msi_notify(d, PVSCSI_VECTOR_COMPLETION);
368881d588aSDmitry Fleytman         }
369881d588aSDmitry Fleytman         return;
370881d588aSDmitry Fleytman     }
371881d588aSDmitry Fleytman 
3729e64f8a3SMarcel Apfelbaum     pci_set_irq(d, !!should_raise);
373881d588aSDmitry Fleytman }
374881d588aSDmitry Fleytman 
375881d588aSDmitry Fleytman static void
376881d588aSDmitry Fleytman pvscsi_raise_completion_interrupt(PVSCSIState *s)
377881d588aSDmitry Fleytman {
378881d588aSDmitry Fleytman     s->reg_interrupt_status |= PVSCSI_INTR_CMPL_0;
379881d588aSDmitry Fleytman 
380881d588aSDmitry Fleytman     /* Memory barrier to flush interrupt status register changes*/
381881d588aSDmitry Fleytman     smp_wmb();
382881d588aSDmitry Fleytman 
383881d588aSDmitry Fleytman     pvscsi_update_irq_status(s);
384881d588aSDmitry Fleytman }
385881d588aSDmitry Fleytman 
386881d588aSDmitry Fleytman static void
387881d588aSDmitry Fleytman pvscsi_raise_message_interrupt(PVSCSIState *s)
388881d588aSDmitry Fleytman {
389881d588aSDmitry Fleytman     s->reg_interrupt_status |= PVSCSI_INTR_MSG_0;
390881d588aSDmitry Fleytman 
391881d588aSDmitry Fleytman     /* Memory barrier to flush interrupt status register changes*/
392881d588aSDmitry Fleytman     smp_wmb();
393881d588aSDmitry Fleytman 
394881d588aSDmitry Fleytman     pvscsi_update_irq_status(s);
395881d588aSDmitry Fleytman }
396881d588aSDmitry Fleytman 
397881d588aSDmitry Fleytman static void
398881d588aSDmitry Fleytman pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc)
399881d588aSDmitry Fleytman {
400881d588aSDmitry Fleytman     hwaddr cmp_descr_pa;
401881d588aSDmitry Fleytman 
402881d588aSDmitry Fleytman     cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings);
403881d588aSDmitry Fleytman     trace_pvscsi_cmp_ring_put(cmp_descr_pa);
4040eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_write(cmp_descr_pa, cmp_desc, sizeof(*cmp_desc));
405881d588aSDmitry Fleytman }
406881d588aSDmitry Fleytman 
407881d588aSDmitry Fleytman static void
408881d588aSDmitry Fleytman pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc)
409881d588aSDmitry Fleytman {
410881d588aSDmitry Fleytman     hwaddr msg_descr_pa;
411881d588aSDmitry Fleytman 
412881d588aSDmitry Fleytman     msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings);
413881d588aSDmitry Fleytman     trace_pvscsi_msg_ring_put(msg_descr_pa);
4140eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_write(msg_descr_pa, msg_desc, sizeof(*msg_desc));
415881d588aSDmitry Fleytman }
416881d588aSDmitry Fleytman 
417881d588aSDmitry Fleytman static void
418881d588aSDmitry Fleytman pvscsi_process_completion_queue(void *opaque)
419881d588aSDmitry Fleytman {
420881d588aSDmitry Fleytman     PVSCSIState *s = opaque;
421881d588aSDmitry Fleytman     PVSCSIRequest *pvscsi_req;
422881d588aSDmitry Fleytman     bool has_completed = false;
423881d588aSDmitry Fleytman 
424881d588aSDmitry Fleytman     while (!QTAILQ_EMPTY(&s->completion_queue)) {
425881d588aSDmitry Fleytman         pvscsi_req = QTAILQ_FIRST(&s->completion_queue);
426881d588aSDmitry Fleytman         QTAILQ_REMOVE(&s->completion_queue, pvscsi_req, next);
427881d588aSDmitry Fleytman         pvscsi_cmp_ring_put(s, &pvscsi_req->cmp);
428881d588aSDmitry Fleytman         g_free(pvscsi_req);
429dcb07809SStefan Weil         has_completed = true;
430881d588aSDmitry Fleytman     }
431881d588aSDmitry Fleytman 
432881d588aSDmitry Fleytman     if (has_completed) {
433881d588aSDmitry Fleytman         pvscsi_ring_flush_cmp(&s->rings);
434881d588aSDmitry Fleytman         pvscsi_raise_completion_interrupt(s);
435881d588aSDmitry Fleytman     }
436881d588aSDmitry Fleytman }
437881d588aSDmitry Fleytman 
438881d588aSDmitry Fleytman static void
439881d588aSDmitry Fleytman pvscsi_reset_adapter(PVSCSIState *s)
440881d588aSDmitry Fleytman {
441881d588aSDmitry Fleytman     s->resetting++;
442573c3e07SPhilippe Mathieu-Daudé     qbus_reset_all(BUS(&s->bus));
443881d588aSDmitry Fleytman     s->resetting--;
444881d588aSDmitry Fleytman     pvscsi_process_completion_queue(s);
445881d588aSDmitry Fleytman     assert(QTAILQ_EMPTY(&s->pending_queue));
446881d588aSDmitry Fleytman     pvscsi_reset_state(s);
447881d588aSDmitry Fleytman }
448881d588aSDmitry Fleytman 
449881d588aSDmitry Fleytman static void
450881d588aSDmitry Fleytman pvscsi_schedule_completion_processing(PVSCSIState *s)
451881d588aSDmitry Fleytman {
452881d588aSDmitry Fleytman     /* Try putting more complete requests on the ring. */
453881d588aSDmitry Fleytman     if (!QTAILQ_EMPTY(&s->completion_queue)) {
454881d588aSDmitry Fleytman         qemu_bh_schedule(s->completion_worker);
455881d588aSDmitry Fleytman     }
456881d588aSDmitry Fleytman }
457881d588aSDmitry Fleytman 
458881d588aSDmitry Fleytman static void
459881d588aSDmitry Fleytman pvscsi_complete_request(PVSCSIState *s, PVSCSIRequest *r)
460881d588aSDmitry Fleytman {
461881d588aSDmitry Fleytman     assert(!r->completed);
462881d588aSDmitry Fleytman 
463881d588aSDmitry Fleytman     trace_pvscsi_complete_request(r->cmp.context, r->cmp.dataLen,
464881d588aSDmitry Fleytman                                   r->sense_key);
465881d588aSDmitry Fleytman     if (r->sreq != NULL) {
466881d588aSDmitry Fleytman         scsi_req_unref(r->sreq);
467881d588aSDmitry Fleytman         r->sreq = NULL;
468881d588aSDmitry Fleytman     }
469881d588aSDmitry Fleytman     r->completed = 1;
470881d588aSDmitry Fleytman     QTAILQ_REMOVE(&s->pending_queue, r, next);
471881d588aSDmitry Fleytman     QTAILQ_INSERT_TAIL(&s->completion_queue, r, next);
472881d588aSDmitry Fleytman     pvscsi_schedule_completion_processing(s);
473881d588aSDmitry Fleytman }
474881d588aSDmitry Fleytman 
475881d588aSDmitry Fleytman static QEMUSGList *pvscsi_get_sg_list(SCSIRequest *r)
476881d588aSDmitry Fleytman {
477881d588aSDmitry Fleytman     PVSCSIRequest *req = r->hba_private;
478881d588aSDmitry Fleytman 
479881d588aSDmitry Fleytman     trace_pvscsi_get_sg_list(req->sgl.nsg, req->sgl.size);
480881d588aSDmitry Fleytman 
481881d588aSDmitry Fleytman     return &req->sgl;
482881d588aSDmitry Fleytman }
483881d588aSDmitry Fleytman 
484881d588aSDmitry Fleytman static void
485881d588aSDmitry Fleytman pvscsi_get_next_sg_elem(PVSCSISGState *sg)
486881d588aSDmitry Fleytman {
487881d588aSDmitry Fleytman     struct PVSCSISGElement elem;
488881d588aSDmitry Fleytman 
4890eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_read(sg->elemAddr, &elem, sizeof(elem));
490881d588aSDmitry Fleytman     if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) {
491881d588aSDmitry Fleytman         /*
492881d588aSDmitry Fleytman             * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in
493881d588aSDmitry Fleytman             * header file but its value is unknown. This flag requires
494881d588aSDmitry Fleytman             * additional processing, so we put warning here to catch it
495881d588aSDmitry Fleytman             * some day and make proper implementation
496881d588aSDmitry Fleytman             */
497881d588aSDmitry Fleytman         trace_pvscsi_get_next_sg_elem(elem.flags);
498881d588aSDmitry Fleytman     }
499881d588aSDmitry Fleytman 
500881d588aSDmitry Fleytman     sg->elemAddr += sizeof(elem);
501881d588aSDmitry Fleytman     sg->dataAddr = elem.addr;
502881d588aSDmitry Fleytman     sg->resid = elem.length;
503881d588aSDmitry Fleytman }
504881d588aSDmitry Fleytman 
505881d588aSDmitry Fleytman static void
506881d588aSDmitry Fleytman pvscsi_write_sense(PVSCSIRequest *r, uint8_t *sense, int len)
507881d588aSDmitry Fleytman {
508881d588aSDmitry Fleytman     r->cmp.senseLen = MIN(r->req.senseLen, len);
509881d588aSDmitry Fleytman     r->sense_key = sense[(sense[0] & 2) ? 1 : 2];
510881d588aSDmitry Fleytman     cpu_physical_memory_write(r->req.senseAddr, sense, r->cmp.senseLen);
511881d588aSDmitry Fleytman }
512881d588aSDmitry Fleytman 
513881d588aSDmitry Fleytman static void
514f3126d65SHannes Reinecke pvscsi_command_failed(SCSIRequest *req)
515f3126d65SHannes Reinecke {
516f3126d65SHannes Reinecke     PVSCSIRequest *pvscsi_req = req->hba_private;
517f3126d65SHannes Reinecke     PVSCSIState *s;
518f3126d65SHannes Reinecke 
519f3126d65SHannes Reinecke     if (!pvscsi_req) {
520f3126d65SHannes Reinecke         trace_pvscsi_command_complete_not_found(req->tag);
521f3126d65SHannes Reinecke         return;
522f3126d65SHannes Reinecke     }
523f3126d65SHannes Reinecke     s = pvscsi_req->dev;
524f3126d65SHannes Reinecke 
525f3126d65SHannes Reinecke     switch (req->host_status) {
526f3126d65SHannes Reinecke     case SCSI_HOST_NO_LUN:
527f3126d65SHannes Reinecke         pvscsi_req->cmp.hostStatus = BTSTAT_LUNMISMATCH;
528f3126d65SHannes Reinecke         break;
529f3126d65SHannes Reinecke     case SCSI_HOST_BUSY:
530f3126d65SHannes Reinecke         pvscsi_req->cmp.hostStatus = BTSTAT_ABORTQUEUE;
531f3126d65SHannes Reinecke         break;
532f3126d65SHannes Reinecke     case SCSI_HOST_TIME_OUT:
533f3126d65SHannes Reinecke     case SCSI_HOST_ABORTED:
534f3126d65SHannes Reinecke         pvscsi_req->cmp.hostStatus = BTSTAT_SENTRST;
535f3126d65SHannes Reinecke         break;
536f3126d65SHannes Reinecke     case SCSI_HOST_BAD_RESPONSE:
537f3126d65SHannes Reinecke         pvscsi_req->cmp.hostStatus = BTSTAT_SELTIMEO;
538f3126d65SHannes Reinecke         break;
539f3126d65SHannes Reinecke     case SCSI_HOST_RESET:
540f3126d65SHannes Reinecke         pvscsi_req->cmp.hostStatus = BTSTAT_BUSRESET;
541f3126d65SHannes Reinecke         break;
542f3126d65SHannes Reinecke     default:
543f3126d65SHannes Reinecke         pvscsi_req->cmp.hostStatus = BTSTAT_HASOFTWARE;
544f3126d65SHannes Reinecke         break;
545f3126d65SHannes Reinecke     }
546f3126d65SHannes Reinecke     pvscsi_req->cmp.scsiStatus = GOOD;
547f3126d65SHannes Reinecke     qemu_sglist_destroy(&pvscsi_req->sgl);
548f3126d65SHannes Reinecke     pvscsi_complete_request(s, pvscsi_req);
549f3126d65SHannes Reinecke }
550f3126d65SHannes Reinecke 
551f3126d65SHannes Reinecke static void
55217ea26c2SHannes Reinecke pvscsi_command_complete(SCSIRequest *req, size_t resid)
553881d588aSDmitry Fleytman {
554881d588aSDmitry Fleytman     PVSCSIRequest *pvscsi_req = req->hba_private;
555b0f49d13SPrasad Joshi     PVSCSIState *s;
556881d588aSDmitry Fleytman 
557881d588aSDmitry Fleytman     if (!pvscsi_req) {
558881d588aSDmitry Fleytman         trace_pvscsi_command_complete_not_found(req->tag);
559881d588aSDmitry Fleytman         return;
560881d588aSDmitry Fleytman     }
561b0f49d13SPrasad Joshi     s = pvscsi_req->dev;
562881d588aSDmitry Fleytman 
563881d588aSDmitry Fleytman     if (resid) {
564881d588aSDmitry Fleytman         /* Short transfer.  */
565881d588aSDmitry Fleytman         trace_pvscsi_command_complete_data_run();
566881d588aSDmitry Fleytman         pvscsi_req->cmp.hostStatus = BTSTAT_DATARUN;
567881d588aSDmitry Fleytman     }
568881d588aSDmitry Fleytman 
56917ea26c2SHannes Reinecke     pvscsi_req->cmp.scsiStatus = req->status;
570881d588aSDmitry Fleytman     if (pvscsi_req->cmp.scsiStatus == CHECK_CONDITION) {
571881d588aSDmitry Fleytman         uint8_t sense[SCSI_SENSE_BUF_SIZE];
572881d588aSDmitry Fleytman         int sense_len =
573881d588aSDmitry Fleytman             scsi_req_get_sense(pvscsi_req->sreq, sense, sizeof(sense));
574881d588aSDmitry Fleytman 
575881d588aSDmitry Fleytman         trace_pvscsi_command_complete_sense_len(sense_len);
576881d588aSDmitry Fleytman         pvscsi_write_sense(pvscsi_req, sense, sense_len);
577881d588aSDmitry Fleytman     }
578881d588aSDmitry Fleytman     qemu_sglist_destroy(&pvscsi_req->sgl);
579881d588aSDmitry Fleytman     pvscsi_complete_request(s, pvscsi_req);
580881d588aSDmitry Fleytman }
581881d588aSDmitry Fleytman 
582881d588aSDmitry Fleytman static void
583881d588aSDmitry Fleytman pvscsi_send_msg(PVSCSIState *s, SCSIDevice *dev, uint32_t msg_type)
584881d588aSDmitry Fleytman {
585881d588aSDmitry Fleytman     if (s->msg_ring_info_valid && pvscsi_ring_msg_has_room(&s->rings)) {
586881d588aSDmitry Fleytman         PVSCSIMsgDescDevStatusChanged msg = {0};
587881d588aSDmitry Fleytman 
588881d588aSDmitry Fleytman         msg.type = msg_type;
589881d588aSDmitry Fleytman         msg.bus = dev->channel;
590881d588aSDmitry Fleytman         msg.target = dev->id;
591881d588aSDmitry Fleytman         msg.lun[1] = dev->lun;
592881d588aSDmitry Fleytman 
593881d588aSDmitry Fleytman         pvscsi_msg_ring_put(s, (PVSCSIRingMsgDesc *)&msg);
594881d588aSDmitry Fleytman         pvscsi_ring_flush_msg(&s->rings);
595881d588aSDmitry Fleytman         pvscsi_raise_message_interrupt(s);
596881d588aSDmitry Fleytman     }
597881d588aSDmitry Fleytman }
598881d588aSDmitry Fleytman 
599881d588aSDmitry Fleytman static void
60091c8daadSIgor Mammedov pvscsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp)
601881d588aSDmitry Fleytman {
60291c8daadSIgor Mammedov     PVSCSIState *s = PVSCSI(hotplug_dev);
60391c8daadSIgor Mammedov 
60491c8daadSIgor Mammedov     pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_ADDED);
605881d588aSDmitry Fleytman }
606881d588aSDmitry Fleytman 
607881d588aSDmitry Fleytman static void
60891c8daadSIgor Mammedov pvscsi_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp)
609881d588aSDmitry Fleytman {
61091c8daadSIgor Mammedov     PVSCSIState *s = PVSCSI(hotplug_dev);
61191c8daadSIgor Mammedov 
61291c8daadSIgor Mammedov     pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_REMOVED);
61391c8daadSIgor Mammedov     qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
614881d588aSDmitry Fleytman }
615881d588aSDmitry Fleytman 
616881d588aSDmitry Fleytman static void
617881d588aSDmitry Fleytman pvscsi_request_cancelled(SCSIRequest *req)
618881d588aSDmitry Fleytman {
619881d588aSDmitry Fleytman     PVSCSIRequest *pvscsi_req = req->hba_private;
620881d588aSDmitry Fleytman     PVSCSIState *s = pvscsi_req->dev;
621881d588aSDmitry Fleytman 
622881d588aSDmitry Fleytman     if (pvscsi_req->completed) {
623881d588aSDmitry Fleytman         return;
624881d588aSDmitry Fleytman     }
625881d588aSDmitry Fleytman 
626881d588aSDmitry Fleytman    if (pvscsi_req->dev->resetting) {
627881d588aSDmitry Fleytman        pvscsi_req->cmp.hostStatus = BTSTAT_BUSRESET;
628881d588aSDmitry Fleytman     } else {
629881d588aSDmitry Fleytman        pvscsi_req->cmp.hostStatus = BTSTAT_ABORTQUEUE;
630881d588aSDmitry Fleytman     }
631881d588aSDmitry Fleytman 
632881d588aSDmitry Fleytman     pvscsi_complete_request(s, pvscsi_req);
633881d588aSDmitry Fleytman }
634881d588aSDmitry Fleytman 
635881d588aSDmitry Fleytman static SCSIDevice*
636881d588aSDmitry Fleytman pvscsi_device_find(PVSCSIState *s, int channel, int target,
637881d588aSDmitry Fleytman                    uint8_t *requested_lun, uint8_t *target_lun)
638881d588aSDmitry Fleytman {
639881d588aSDmitry Fleytman     if (requested_lun[0] || requested_lun[2] || requested_lun[3] ||
640881d588aSDmitry Fleytman         requested_lun[4] || requested_lun[5] || requested_lun[6] ||
641881d588aSDmitry Fleytman         requested_lun[7] || (target > PVSCSI_MAX_DEVS)) {
642881d588aSDmitry Fleytman         return NULL;
643881d588aSDmitry Fleytman     } else {
644881d588aSDmitry Fleytman         *target_lun = requested_lun[1];
645881d588aSDmitry Fleytman         return scsi_device_find(&s->bus, channel, target, *target_lun);
646881d588aSDmitry Fleytman     }
647881d588aSDmitry Fleytman }
648881d588aSDmitry Fleytman 
649881d588aSDmitry Fleytman static PVSCSIRequest *
650881d588aSDmitry Fleytman pvscsi_queue_pending_descriptor(PVSCSIState *s, SCSIDevice **d,
651881d588aSDmitry Fleytman                                 struct PVSCSIRingReqDesc *descr)
652881d588aSDmitry Fleytman {
653881d588aSDmitry Fleytman     PVSCSIRequest *pvscsi_req;
654881d588aSDmitry Fleytman     uint8_t lun;
655881d588aSDmitry Fleytman 
656881d588aSDmitry Fleytman     pvscsi_req = g_malloc0(sizeof(*pvscsi_req));
657881d588aSDmitry Fleytman     pvscsi_req->dev = s;
658881d588aSDmitry Fleytman     pvscsi_req->req = *descr;
659881d588aSDmitry Fleytman     pvscsi_req->cmp.context = pvscsi_req->req.context;
660881d588aSDmitry Fleytman     QTAILQ_INSERT_TAIL(&s->pending_queue, pvscsi_req, next);
661881d588aSDmitry Fleytman 
662881d588aSDmitry Fleytman     *d = pvscsi_device_find(s, descr->bus, descr->target, descr->lun, &lun);
663881d588aSDmitry Fleytman     if (*d) {
664881d588aSDmitry Fleytman         pvscsi_req->lun = lun;
665881d588aSDmitry Fleytman     }
666881d588aSDmitry Fleytman 
667881d588aSDmitry Fleytman     return pvscsi_req;
668881d588aSDmitry Fleytman }
669881d588aSDmitry Fleytman 
670881d588aSDmitry Fleytman static void
671881d588aSDmitry Fleytman pvscsi_convert_sglist(PVSCSIRequest *r)
672881d588aSDmitry Fleytman {
67349adc5d3SPrasad J Pandit     uint32_t chunk_size, elmcnt = 0;
674881d588aSDmitry Fleytman     uint64_t data_length = r->req.dataLen;
675881d588aSDmitry Fleytman     PVSCSISGState sg = r->sg;
67649adc5d3SPrasad J Pandit     while (data_length && elmcnt < PVSCSI_MAX_SG_ELEM) {
67749adc5d3SPrasad J Pandit         while (!sg.resid && elmcnt++ < PVSCSI_MAX_SG_ELEM) {
678881d588aSDmitry Fleytman             pvscsi_get_next_sg_elem(&sg);
679881d588aSDmitry Fleytman             trace_pvscsi_convert_sglist(r->req.context, r->sg.dataAddr,
680881d588aSDmitry Fleytman                                         r->sg.resid);
681881d588aSDmitry Fleytman         }
68249adc5d3SPrasad J Pandit         chunk_size = MIN(data_length, sg.resid);
683881d588aSDmitry Fleytman         if (chunk_size) {
684881d588aSDmitry Fleytman             qemu_sglist_add(&r->sgl, sg.dataAddr, chunk_size);
685881d588aSDmitry Fleytman         }
686881d588aSDmitry Fleytman 
687881d588aSDmitry Fleytman         sg.dataAddr += chunk_size;
688881d588aSDmitry Fleytman         data_length -= chunk_size;
689881d588aSDmitry Fleytman         sg.resid -= chunk_size;
690881d588aSDmitry Fleytman     }
691881d588aSDmitry Fleytman }
692881d588aSDmitry Fleytman 
693881d588aSDmitry Fleytman static void
694881d588aSDmitry Fleytman pvscsi_build_sglist(PVSCSIState *s, PVSCSIRequest *r)
695881d588aSDmitry Fleytman {
696881d588aSDmitry Fleytman     PCIDevice *d = PCI_DEVICE(s);
697881d588aSDmitry Fleytman 
698df32fd1cSPaolo Bonzini     pci_dma_sglist_init(&r->sgl, d, 1);
699881d588aSDmitry Fleytman     if (r->req.flags & PVSCSI_FLAG_CMD_WITH_SG_LIST) {
700881d588aSDmitry Fleytman         pvscsi_convert_sglist(r);
701881d588aSDmitry Fleytman     } else {
702881d588aSDmitry Fleytman         qemu_sglist_add(&r->sgl, r->req.dataAddr, r->req.dataLen);
703881d588aSDmitry Fleytman     }
704881d588aSDmitry Fleytman }
705881d588aSDmitry Fleytman 
706881d588aSDmitry Fleytman static void
707881d588aSDmitry Fleytman pvscsi_process_request_descriptor(PVSCSIState *s,
708881d588aSDmitry Fleytman                                   struct PVSCSIRingReqDesc *descr)
709881d588aSDmitry Fleytman {
710881d588aSDmitry Fleytman     SCSIDevice *d;
711881d588aSDmitry Fleytman     PVSCSIRequest *r = pvscsi_queue_pending_descriptor(s, &d, descr);
712881d588aSDmitry Fleytman     int64_t n;
713881d588aSDmitry Fleytman 
714881d588aSDmitry Fleytman     trace_pvscsi_process_req_descr(descr->cdb[0], descr->context);
715881d588aSDmitry Fleytman 
716881d588aSDmitry Fleytman     if (!d) {
717881d588aSDmitry Fleytman         r->cmp.hostStatus = BTSTAT_SELTIMEO;
718881d588aSDmitry Fleytman         trace_pvscsi_process_req_descr_unknown_device();
719881d588aSDmitry Fleytman         pvscsi_complete_request(s, r);
720881d588aSDmitry Fleytman         return;
721881d588aSDmitry Fleytman     }
722881d588aSDmitry Fleytman 
723881d588aSDmitry Fleytman     if (descr->flags & PVSCSI_FLAG_CMD_WITH_SG_LIST) {
724881d588aSDmitry Fleytman         r->sg.elemAddr = descr->dataAddr;
725881d588aSDmitry Fleytman     }
726881d588aSDmitry Fleytman 
727881d588aSDmitry Fleytman     r->sreq = scsi_req_new(d, descr->context, r->lun, descr->cdb, r);
728881d588aSDmitry Fleytman     if (r->sreq->cmd.mode == SCSI_XFER_FROM_DEV &&
729881d588aSDmitry Fleytman         (descr->flags & PVSCSI_FLAG_CMD_DIR_TODEVICE)) {
730881d588aSDmitry Fleytman         r->cmp.hostStatus = BTSTAT_BADMSG;
731881d588aSDmitry Fleytman         trace_pvscsi_process_req_descr_invalid_dir();
732881d588aSDmitry Fleytman         scsi_req_cancel(r->sreq);
733881d588aSDmitry Fleytman         return;
734881d588aSDmitry Fleytman     }
735881d588aSDmitry Fleytman     if (r->sreq->cmd.mode == SCSI_XFER_TO_DEV &&
736881d588aSDmitry Fleytman         (descr->flags & PVSCSI_FLAG_CMD_DIR_TOHOST)) {
737881d588aSDmitry Fleytman         r->cmp.hostStatus = BTSTAT_BADMSG;
738881d588aSDmitry Fleytman         trace_pvscsi_process_req_descr_invalid_dir();
739881d588aSDmitry Fleytman         scsi_req_cancel(r->sreq);
740881d588aSDmitry Fleytman         return;
741881d588aSDmitry Fleytman     }
742881d588aSDmitry Fleytman 
743881d588aSDmitry Fleytman     pvscsi_build_sglist(s, r);
744881d588aSDmitry Fleytman     n = scsi_req_enqueue(r->sreq);
745881d588aSDmitry Fleytman 
746881d588aSDmitry Fleytman     if (n) {
747881d588aSDmitry Fleytman         scsi_req_continue(r->sreq);
748881d588aSDmitry Fleytman     }
749881d588aSDmitry Fleytman }
750881d588aSDmitry Fleytman 
751881d588aSDmitry Fleytman static void
752881d588aSDmitry Fleytman pvscsi_process_io(PVSCSIState *s)
753881d588aSDmitry Fleytman {
754881d588aSDmitry Fleytman     PVSCSIRingReqDesc descr;
755881d588aSDmitry Fleytman     hwaddr next_descr_pa;
756881d588aSDmitry Fleytman 
757e7ebf057SElazar Leibovich     if (!s->rings_info_valid) {
758e7ebf057SElazar Leibovich         return;
759e7ebf057SElazar Leibovich     }
760e7ebf057SElazar Leibovich 
761881d588aSDmitry Fleytman     while ((next_descr_pa = pvscsi_ring_pop_req_descr(&s->rings)) != 0) {
762881d588aSDmitry Fleytman 
763881d588aSDmitry Fleytman         /* Only read after production index verification */
764881d588aSDmitry Fleytman         smp_rmb();
765881d588aSDmitry Fleytman 
766881d588aSDmitry Fleytman         trace_pvscsi_process_io(next_descr_pa);
767881d588aSDmitry Fleytman         cpu_physical_memory_read(next_descr_pa, &descr, sizeof(descr));
768881d588aSDmitry Fleytman         pvscsi_process_request_descriptor(s, &descr);
769881d588aSDmitry Fleytman     }
770881d588aSDmitry Fleytman 
771881d588aSDmitry Fleytman     pvscsi_ring_flush_req(&s->rings);
772881d588aSDmitry Fleytman }
773881d588aSDmitry Fleytman 
774881d588aSDmitry Fleytman static void
775881d588aSDmitry Fleytman pvscsi_dbg_dump_tx_rings_config(PVSCSICmdDescSetupRings *rc)
776881d588aSDmitry Fleytman {
777881d588aSDmitry Fleytman     int i;
778881d588aSDmitry Fleytman     trace_pvscsi_tx_rings_ppn("Rings State", rc->ringsStatePPN);
779881d588aSDmitry Fleytman 
780881d588aSDmitry Fleytman     trace_pvscsi_tx_rings_num_pages("Request Ring", rc->reqRingNumPages);
781881d588aSDmitry Fleytman     for (i = 0; i < rc->reqRingNumPages; i++) {
782881d588aSDmitry Fleytman         trace_pvscsi_tx_rings_ppn("Request Ring", rc->reqRingPPNs[i]);
783881d588aSDmitry Fleytman     }
784881d588aSDmitry Fleytman 
785881d588aSDmitry Fleytman     trace_pvscsi_tx_rings_num_pages("Confirm Ring", rc->cmpRingNumPages);
786881d588aSDmitry Fleytman     for (i = 0; i < rc->cmpRingNumPages; i++) {
7877f61f469SPrasad J Pandit         trace_pvscsi_tx_rings_ppn("Confirm Ring", rc->cmpRingPPNs[i]);
788881d588aSDmitry Fleytman     }
789881d588aSDmitry Fleytman }
790881d588aSDmitry Fleytman 
791881d588aSDmitry Fleytman static uint64_t
792881d588aSDmitry Fleytman pvscsi_on_cmd_config(PVSCSIState *s)
793881d588aSDmitry Fleytman {
794881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_CONFIG");
795881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_FAILED;
796881d588aSDmitry Fleytman }
797881d588aSDmitry Fleytman 
798881d588aSDmitry Fleytman static uint64_t
799881d588aSDmitry Fleytman pvscsi_on_cmd_unplug(PVSCSIState *s)
800881d588aSDmitry Fleytman {
801881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_DEVICE_UNPLUG");
802881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_FAILED;
803881d588aSDmitry Fleytman }
804881d588aSDmitry Fleytman 
805881d588aSDmitry Fleytman static uint64_t
806881d588aSDmitry Fleytman pvscsi_on_issue_scsi(PVSCSIState *s)
807881d588aSDmitry Fleytman {
808881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_ISSUE_SCSI");
809881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_FAILED;
810881d588aSDmitry Fleytman }
811881d588aSDmitry Fleytman 
812881d588aSDmitry Fleytman static uint64_t
813881d588aSDmitry Fleytman pvscsi_on_cmd_setup_rings(PVSCSIState *s)
814881d588aSDmitry Fleytman {
815881d588aSDmitry Fleytman     PVSCSICmdDescSetupRings *rc =
816881d588aSDmitry Fleytman         (PVSCSICmdDescSetupRings *) s->curr_cmd_data;
817881d588aSDmitry Fleytman 
818881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS");
819881d588aSDmitry Fleytman 
8207f61f469SPrasad J Pandit     if (!rc->reqRingNumPages
8217f61f469SPrasad J Pandit         || rc->reqRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
8227f61f469SPrasad J Pandit         || !rc->cmpRingNumPages
8237f61f469SPrasad J Pandit         || rc->cmpRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES) {
8243e831b40SPrasad J Pandit         return PVSCSI_COMMAND_PROCESSING_FAILED;
8253e831b40SPrasad J Pandit     }
8263e831b40SPrasad J Pandit 
8277f61f469SPrasad J Pandit     pvscsi_dbg_dump_tx_rings_config(rc);
8287f61f469SPrasad J Pandit     pvscsi_ring_init_data(&s->rings, rc);
8297f61f469SPrasad J Pandit 
830881d588aSDmitry Fleytman     s->rings_info_valid = TRUE;
831881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
832881d588aSDmitry Fleytman }
833881d588aSDmitry Fleytman 
834881d588aSDmitry Fleytman static uint64_t
835881d588aSDmitry Fleytman pvscsi_on_cmd_abort(PVSCSIState *s)
836881d588aSDmitry Fleytman {
837881d588aSDmitry Fleytman     PVSCSICmdDescAbortCmd *cmd = (PVSCSICmdDescAbortCmd *) s->curr_cmd_data;
838881d588aSDmitry Fleytman     PVSCSIRequest *r, *next;
839881d588aSDmitry Fleytman 
840881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_abort(cmd->context, cmd->target);
841881d588aSDmitry Fleytman 
842881d588aSDmitry Fleytman     QTAILQ_FOREACH_SAFE(r, &s->pending_queue, next, next) {
843881d588aSDmitry Fleytman         if (r->req.context == cmd->context) {
844881d588aSDmitry Fleytman             break;
845881d588aSDmitry Fleytman         }
846881d588aSDmitry Fleytman     }
847881d588aSDmitry Fleytman     if (r) {
848881d588aSDmitry Fleytman         assert(!r->completed);
849881d588aSDmitry Fleytman         r->cmp.hostStatus = BTSTAT_ABORTQUEUE;
850881d588aSDmitry Fleytman         scsi_req_cancel(r->sreq);
851881d588aSDmitry Fleytman     }
852881d588aSDmitry Fleytman 
853881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
854881d588aSDmitry Fleytman }
855881d588aSDmitry Fleytman 
856881d588aSDmitry Fleytman static uint64_t
857881d588aSDmitry Fleytman pvscsi_on_cmd_unknown(PVSCSIState *s)
858881d588aSDmitry Fleytman {
859881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_unknown_data(s->curr_cmd_data[0]);
860881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_FAILED;
861881d588aSDmitry Fleytman }
862881d588aSDmitry Fleytman 
863881d588aSDmitry Fleytman static uint64_t
864881d588aSDmitry Fleytman pvscsi_on_cmd_reset_device(PVSCSIState *s)
865881d588aSDmitry Fleytman {
866881d588aSDmitry Fleytman     uint8_t target_lun = 0;
867881d588aSDmitry Fleytman     struct PVSCSICmdDescResetDevice *cmd =
868881d588aSDmitry Fleytman         (struct PVSCSICmdDescResetDevice *) s->curr_cmd_data;
869881d588aSDmitry Fleytman     SCSIDevice *sdev;
870881d588aSDmitry Fleytman 
871881d588aSDmitry Fleytman     sdev = pvscsi_device_find(s, 0, cmd->target, cmd->lun, &target_lun);
872881d588aSDmitry Fleytman 
873881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_reset_dev(cmd->target, (int) target_lun, sdev);
874881d588aSDmitry Fleytman 
875881d588aSDmitry Fleytman     if (sdev != NULL) {
876881d588aSDmitry Fleytman         s->resetting++;
877f703a04cSDamien Hedde         device_legacy_reset(&sdev->qdev);
878881d588aSDmitry Fleytman         s->resetting--;
879881d588aSDmitry Fleytman         return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
880881d588aSDmitry Fleytman     }
881881d588aSDmitry Fleytman 
882881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_FAILED;
883881d588aSDmitry Fleytman }
884881d588aSDmitry Fleytman 
885881d588aSDmitry Fleytman static uint64_t
886881d588aSDmitry Fleytman pvscsi_on_cmd_reset_bus(PVSCSIState *s)
887881d588aSDmitry Fleytman {
888881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_RESET_BUS");
889881d588aSDmitry Fleytman 
890881d588aSDmitry Fleytman     s->resetting++;
891573c3e07SPhilippe Mathieu-Daudé     qbus_reset_all(BUS(&s->bus));
892881d588aSDmitry Fleytman     s->resetting--;
893881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
894881d588aSDmitry Fleytman }
895881d588aSDmitry Fleytman 
896881d588aSDmitry Fleytman static uint64_t
897881d588aSDmitry Fleytman pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s)
898881d588aSDmitry Fleytman {
899881d588aSDmitry Fleytman     PVSCSICmdDescSetupMsgRing *rc =
900881d588aSDmitry Fleytman         (PVSCSICmdDescSetupMsgRing *) s->curr_cmd_data;
901881d588aSDmitry Fleytman 
902881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_MSG_RING");
903881d588aSDmitry Fleytman 
904881d588aSDmitry Fleytman     if (!s->use_msg) {
905881d588aSDmitry Fleytman         return PVSCSI_COMMAND_PROCESSING_FAILED;
906881d588aSDmitry Fleytman     }
907881d588aSDmitry Fleytman 
908881d588aSDmitry Fleytman     if (s->rings_info_valid) {
9093e831b40SPrasad J Pandit         if (pvscsi_ring_init_msg(&s->rings, rc) < 0) {
9103e831b40SPrasad J Pandit             return PVSCSI_COMMAND_PROCESSING_FAILED;
9113e831b40SPrasad J Pandit         }
912881d588aSDmitry Fleytman         s->msg_ring_info_valid = TRUE;
913881d588aSDmitry Fleytman     }
914881d588aSDmitry Fleytman     return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t);
915881d588aSDmitry Fleytman }
916881d588aSDmitry Fleytman 
917881d588aSDmitry Fleytman static uint64_t
918881d588aSDmitry Fleytman pvscsi_on_cmd_adapter_reset(PVSCSIState *s)
919881d588aSDmitry Fleytman {
920881d588aSDmitry Fleytman     trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_ADAPTER_RESET");
921881d588aSDmitry Fleytman 
922881d588aSDmitry Fleytman     pvscsi_reset_adapter(s);
923881d588aSDmitry Fleytman     return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
924881d588aSDmitry Fleytman }
925881d588aSDmitry Fleytman 
926881d588aSDmitry Fleytman static const struct {
927881d588aSDmitry Fleytman     int       data_size;
928881d588aSDmitry Fleytman     uint64_t  (*handler_fn)(PVSCSIState *s);
929881d588aSDmitry Fleytman } pvscsi_commands[] = {
930881d588aSDmitry Fleytman     [PVSCSI_CMD_FIRST] = {
931881d588aSDmitry Fleytman         .data_size = 0,
932881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_unknown,
933881d588aSDmitry Fleytman     },
934881d588aSDmitry Fleytman 
935881d588aSDmitry Fleytman     /* Not implemented, data size defined based on what arrives on windows */
936881d588aSDmitry Fleytman     [PVSCSI_CMD_CONFIG] = {
937881d588aSDmitry Fleytman         .data_size = 6 * sizeof(uint32_t),
938881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_config,
939881d588aSDmitry Fleytman     },
940881d588aSDmitry Fleytman 
941881d588aSDmitry Fleytman     /* Command not implemented, data size is unknown */
942881d588aSDmitry Fleytman     [PVSCSI_CMD_ISSUE_SCSI] = {
943881d588aSDmitry Fleytman         .data_size = 0,
944881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_issue_scsi,
945881d588aSDmitry Fleytman     },
946881d588aSDmitry Fleytman 
947881d588aSDmitry Fleytman     /* Command not implemented, data size is unknown */
948881d588aSDmitry Fleytman     [PVSCSI_CMD_DEVICE_UNPLUG] = {
949881d588aSDmitry Fleytman         .data_size = 0,
950881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_unplug,
951881d588aSDmitry Fleytman     },
952881d588aSDmitry Fleytman 
953881d588aSDmitry Fleytman     [PVSCSI_CMD_SETUP_RINGS] = {
954881d588aSDmitry Fleytman         .data_size = sizeof(PVSCSICmdDescSetupRings),
955881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_setup_rings,
956881d588aSDmitry Fleytman     },
957881d588aSDmitry Fleytman 
958881d588aSDmitry Fleytman     [PVSCSI_CMD_RESET_DEVICE] = {
959881d588aSDmitry Fleytman         .data_size = sizeof(struct PVSCSICmdDescResetDevice),
960881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_reset_device,
961881d588aSDmitry Fleytman     },
962881d588aSDmitry Fleytman 
963881d588aSDmitry Fleytman     [PVSCSI_CMD_RESET_BUS] = {
964881d588aSDmitry Fleytman         .data_size = 0,
965881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_reset_bus,
966881d588aSDmitry Fleytman     },
967881d588aSDmitry Fleytman 
968881d588aSDmitry Fleytman     [PVSCSI_CMD_SETUP_MSG_RING] = {
969881d588aSDmitry Fleytman         .data_size = sizeof(PVSCSICmdDescSetupMsgRing),
970881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_setup_msg_ring,
971881d588aSDmitry Fleytman     },
972881d588aSDmitry Fleytman 
973881d588aSDmitry Fleytman     [PVSCSI_CMD_ADAPTER_RESET] = {
974881d588aSDmitry Fleytman         .data_size = 0,
975881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_adapter_reset,
976881d588aSDmitry Fleytman     },
977881d588aSDmitry Fleytman 
978881d588aSDmitry Fleytman     [PVSCSI_CMD_ABORT_CMD] = {
979881d588aSDmitry Fleytman         .data_size = sizeof(struct PVSCSICmdDescAbortCmd),
980881d588aSDmitry Fleytman         .handler_fn = pvscsi_on_cmd_abort,
981881d588aSDmitry Fleytman     },
982881d588aSDmitry Fleytman };
983881d588aSDmitry Fleytman 
984881d588aSDmitry Fleytman static void
985881d588aSDmitry Fleytman pvscsi_do_command_processing(PVSCSIState *s)
986881d588aSDmitry Fleytman {
987881d588aSDmitry Fleytman     size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t);
988881d588aSDmitry Fleytman 
989881d588aSDmitry Fleytman     assert(s->curr_cmd < PVSCSI_CMD_LAST);
990881d588aSDmitry Fleytman     if (bytes_arrived >= pvscsi_commands[s->curr_cmd].data_size) {
991881d588aSDmitry Fleytman         s->reg_command_status = pvscsi_commands[s->curr_cmd].handler_fn(s);
992881d588aSDmitry Fleytman         s->curr_cmd = PVSCSI_CMD_FIRST;
993881d588aSDmitry Fleytman         s->curr_cmd_data_cntr   = 0;
994881d588aSDmitry Fleytman     }
995881d588aSDmitry Fleytman }
996881d588aSDmitry Fleytman 
997881d588aSDmitry Fleytman static void
998881d588aSDmitry Fleytman pvscsi_on_command_data(PVSCSIState *s, uint32_t value)
999881d588aSDmitry Fleytman {
1000881d588aSDmitry Fleytman     size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t);
1001881d588aSDmitry Fleytman 
1002881d588aSDmitry Fleytman     assert(bytes_arrived < sizeof(s->curr_cmd_data));
1003881d588aSDmitry Fleytman     s->curr_cmd_data[s->curr_cmd_data_cntr++] = value;
1004881d588aSDmitry Fleytman 
1005881d588aSDmitry Fleytman     pvscsi_do_command_processing(s);
1006881d588aSDmitry Fleytman }
1007881d588aSDmitry Fleytman 
1008881d588aSDmitry Fleytman static void
1009881d588aSDmitry Fleytman pvscsi_on_command(PVSCSIState *s, uint64_t cmd_id)
1010881d588aSDmitry Fleytman {
1011881d588aSDmitry Fleytman     if ((cmd_id > PVSCSI_CMD_FIRST) && (cmd_id < PVSCSI_CMD_LAST)) {
1012881d588aSDmitry Fleytman         s->curr_cmd = cmd_id;
1013881d588aSDmitry Fleytman     } else {
1014881d588aSDmitry Fleytman         s->curr_cmd = PVSCSI_CMD_FIRST;
1015881d588aSDmitry Fleytman         trace_pvscsi_on_cmd_unknown(cmd_id);
1016881d588aSDmitry Fleytman     }
1017881d588aSDmitry Fleytman 
1018881d588aSDmitry Fleytman     s->curr_cmd_data_cntr = 0;
1019881d588aSDmitry Fleytman     s->reg_command_status = PVSCSI_COMMAND_NOT_ENOUGH_DATA;
1020881d588aSDmitry Fleytman 
1021881d588aSDmitry Fleytman     pvscsi_do_command_processing(s);
1022881d588aSDmitry Fleytman }
1023881d588aSDmitry Fleytman 
1024881d588aSDmitry Fleytman static void
1025881d588aSDmitry Fleytman pvscsi_io_write(void *opaque, hwaddr addr,
1026881d588aSDmitry Fleytman                 uint64_t val, unsigned size)
1027881d588aSDmitry Fleytman {
1028881d588aSDmitry Fleytman     PVSCSIState *s = opaque;
1029881d588aSDmitry Fleytman 
1030881d588aSDmitry Fleytman     switch (addr) {
1031881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_COMMAND:
1032881d588aSDmitry Fleytman         pvscsi_on_command(s, val);
1033881d588aSDmitry Fleytman         break;
1034881d588aSDmitry Fleytman 
1035881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_COMMAND_DATA:
1036881d588aSDmitry Fleytman         pvscsi_on_command_data(s, (uint32_t) val);
1037881d588aSDmitry Fleytman         break;
1038881d588aSDmitry Fleytman 
1039881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_INTR_STATUS:
1040881d588aSDmitry Fleytman         trace_pvscsi_io_write("PVSCSI_REG_OFFSET_INTR_STATUS", val);
1041881d588aSDmitry Fleytman         s->reg_interrupt_status &= ~val;
1042881d588aSDmitry Fleytman         pvscsi_update_irq_status(s);
1043881d588aSDmitry Fleytman         pvscsi_schedule_completion_processing(s);
1044881d588aSDmitry Fleytman         break;
1045881d588aSDmitry Fleytman 
1046881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_INTR_MASK:
1047881d588aSDmitry Fleytman         trace_pvscsi_io_write("PVSCSI_REG_OFFSET_INTR_MASK", val);
1048881d588aSDmitry Fleytman         s->reg_interrupt_enabled = val;
1049881d588aSDmitry Fleytman         pvscsi_update_irq_status(s);
1050881d588aSDmitry Fleytman         break;
1051881d588aSDmitry Fleytman 
1052881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_KICK_NON_RW_IO:
1053881d588aSDmitry Fleytman         trace_pvscsi_io_write("PVSCSI_REG_OFFSET_KICK_NON_RW_IO", val);
1054881d588aSDmitry Fleytman         pvscsi_process_io(s);
1055881d588aSDmitry Fleytman         break;
1056881d588aSDmitry Fleytman 
1057881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_KICK_RW_IO:
1058881d588aSDmitry Fleytman         trace_pvscsi_io_write("PVSCSI_REG_OFFSET_KICK_RW_IO", val);
1059881d588aSDmitry Fleytman         pvscsi_process_io(s);
1060881d588aSDmitry Fleytman         break;
1061881d588aSDmitry Fleytman 
1062881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_DEBUG:
1063881d588aSDmitry Fleytman         trace_pvscsi_io_write("PVSCSI_REG_OFFSET_DEBUG", val);
1064881d588aSDmitry Fleytman         break;
1065881d588aSDmitry Fleytman 
1066881d588aSDmitry Fleytman     default:
1067881d588aSDmitry Fleytman         trace_pvscsi_io_write_unknown(addr, size, val);
1068881d588aSDmitry Fleytman         break;
1069881d588aSDmitry Fleytman     }
1070881d588aSDmitry Fleytman 
1071881d588aSDmitry Fleytman }
1072881d588aSDmitry Fleytman 
1073881d588aSDmitry Fleytman static uint64_t
1074881d588aSDmitry Fleytman pvscsi_io_read(void *opaque, hwaddr addr, unsigned size)
1075881d588aSDmitry Fleytman {
1076881d588aSDmitry Fleytman     PVSCSIState *s = opaque;
1077881d588aSDmitry Fleytman 
1078881d588aSDmitry Fleytman     switch (addr) {
1079881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_INTR_STATUS:
1080881d588aSDmitry Fleytman         trace_pvscsi_io_read("PVSCSI_REG_OFFSET_INTR_STATUS",
1081881d588aSDmitry Fleytman                              s->reg_interrupt_status);
1082881d588aSDmitry Fleytman         return s->reg_interrupt_status;
1083881d588aSDmitry Fleytman 
1084881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_INTR_MASK:
1085881d588aSDmitry Fleytman         trace_pvscsi_io_read("PVSCSI_REG_OFFSET_INTR_MASK",
1086881d588aSDmitry Fleytman                              s->reg_interrupt_status);
1087881d588aSDmitry Fleytman         return s->reg_interrupt_enabled;
1088881d588aSDmitry Fleytman 
1089881d588aSDmitry Fleytman     case PVSCSI_REG_OFFSET_COMMAND_STATUS:
1090881d588aSDmitry Fleytman         trace_pvscsi_io_read("PVSCSI_REG_OFFSET_COMMAND_STATUS",
1091881d588aSDmitry Fleytman                              s->reg_interrupt_status);
1092881d588aSDmitry Fleytman         return s->reg_command_status;
1093881d588aSDmitry Fleytman 
1094881d588aSDmitry Fleytman     default:
1095881d588aSDmitry Fleytman         trace_pvscsi_io_read_unknown(addr, size);
1096881d588aSDmitry Fleytman         return 0;
1097881d588aSDmitry Fleytman     }
1098881d588aSDmitry Fleytman }
1099881d588aSDmitry Fleytman 
1100881d588aSDmitry Fleytman 
1101b2e1fffbSCao jin static void
1102881d588aSDmitry Fleytman pvscsi_init_msi(PVSCSIState *s)
1103881d588aSDmitry Fleytman {
1104881d588aSDmitry Fleytman     int res;
1105881d588aSDmitry Fleytman     PCIDevice *d = PCI_DEVICE(s);
1106881d588aSDmitry Fleytman 
1107836fc48cSShmulik Ladkani     res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
11081108b2f8SCao jin                    PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL);
1109881d588aSDmitry Fleytman     if (res < 0) {
1110881d588aSDmitry Fleytman         trace_pvscsi_init_msi_fail(res);
1111881d588aSDmitry Fleytman         s->msi_used = false;
1112881d588aSDmitry Fleytman     } else {
1113881d588aSDmitry Fleytman         s->msi_used = true;
1114881d588aSDmitry Fleytman     }
1115881d588aSDmitry Fleytman }
1116881d588aSDmitry Fleytman 
1117881d588aSDmitry Fleytman static void
1118881d588aSDmitry Fleytman pvscsi_cleanup_msi(PVSCSIState *s)
1119881d588aSDmitry Fleytman {
1120881d588aSDmitry Fleytman     PCIDevice *d = PCI_DEVICE(s);
1121881d588aSDmitry Fleytman 
1122881d588aSDmitry Fleytman     msi_uninit(d);
1123881d588aSDmitry Fleytman }
1124881d588aSDmitry Fleytman 
1125881d588aSDmitry Fleytman static const MemoryRegionOps pvscsi_ops = {
1126881d588aSDmitry Fleytman         .read = pvscsi_io_read,
1127881d588aSDmitry Fleytman         .write = pvscsi_io_write,
1128881d588aSDmitry Fleytman         .endianness = DEVICE_LITTLE_ENDIAN,
1129881d588aSDmitry Fleytman         .impl = {
1130881d588aSDmitry Fleytman                 .min_access_size = 4,
1131881d588aSDmitry Fleytman                 .max_access_size = 4,
1132881d588aSDmitry Fleytman         },
1133881d588aSDmitry Fleytman };
1134881d588aSDmitry Fleytman 
1135881d588aSDmitry Fleytman static const struct SCSIBusInfo pvscsi_scsi_info = {
1136881d588aSDmitry Fleytman         .tcq = true,
1137881d588aSDmitry Fleytman         .max_target = PVSCSI_MAX_DEVS,
1138881d588aSDmitry Fleytman         .max_channel = 0,
1139881d588aSDmitry Fleytman         .max_lun = 0,
1140881d588aSDmitry Fleytman 
1141881d588aSDmitry Fleytman         .get_sg_list = pvscsi_get_sg_list,
1142881d588aSDmitry Fleytman         .complete = pvscsi_command_complete,
1143881d588aSDmitry Fleytman         .cancel = pvscsi_request_cancelled,
1144f3126d65SHannes Reinecke         .fail = pvscsi_command_failed,
1145881d588aSDmitry Fleytman };
1146881d588aSDmitry Fleytman 
1147fafeb41cSMao Zhongyi static void
1148fafeb41cSMao Zhongyi pvscsi_realizefn(PCIDevice *pci_dev, Error **errp)
1149881d588aSDmitry Fleytman {
1150881d588aSDmitry Fleytman     PVSCSIState *s = PVSCSI(pci_dev);
1151881d588aSDmitry Fleytman 
1152881d588aSDmitry Fleytman     trace_pvscsi_state("init");
1153881d588aSDmitry Fleytman 
1154d29d4ff8SShmulik Ladkani     /* PCI subsystem ID, subsystem vendor ID, revision */
1155d29d4ff8SShmulik Ladkani     if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) {
1156d29d4ff8SShmulik Ladkani         pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000);
1157d29d4ff8SShmulik Ladkani     } else {
1158d29d4ff8SShmulik Ladkani         pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
1159d29d4ff8SShmulik Ladkani                      PCI_VENDOR_ID_VMWARE);
1160d29d4ff8SShmulik Ladkani         pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
1161d29d4ff8SShmulik Ladkani                      PCI_DEVICE_ID_VMWARE_PVSCSI);
1162d29d4ff8SShmulik Ladkani         pci_config_set_revision(pci_dev->config, 0x2);
1163d29d4ff8SShmulik Ladkani     }
1164881d588aSDmitry Fleytman 
1165881d588aSDmitry Fleytman     /* PCI latency timer = 255 */
1166881d588aSDmitry Fleytman     pci_dev->config[PCI_LATENCY_TIMER] = 0xff;
1167881d588aSDmitry Fleytman 
1168881d588aSDmitry Fleytman     /* Interrupt pin A */
1169881d588aSDmitry Fleytman     pci_config_set_interrupt_pin(pci_dev->config, 1);
1170881d588aSDmitry Fleytman 
117129776739SPaolo Bonzini     memory_region_init_io(&s->io_space, OBJECT(s), &pvscsi_ops, s,
1172881d588aSDmitry Fleytman                           "pvscsi-io", PVSCSI_MEM_SPACE_SIZE);
1173881d588aSDmitry Fleytman     pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io_space);
1174881d588aSDmitry Fleytman 
1175881d588aSDmitry Fleytman     pvscsi_init_msi(s);
1176881d588aSDmitry Fleytman 
1177fd56e061SDavid Gibson     if (pci_is_express(pci_dev) && pci_bus_is_express(pci_get_bus(pci_dev))) {
11781dd1305eSShmulik Ladkani         pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET);
11791dd1305eSShmulik Ladkani     }
11801dd1305eSShmulik Ladkani 
1181881d588aSDmitry Fleytman     s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s);
1182881d588aSDmitry Fleytman 
1183*739e95f5SPeter Maydell     scsi_bus_init(&s->bus, sizeof(s->bus), DEVICE(pci_dev), &pvscsi_scsi_info);
118491c8daadSIgor Mammedov     /* override default SCSI bus hotplug-handler, with pvscsi's one */
11859bc6bfdfSMarkus Armbruster     qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(s));
1186881d588aSDmitry Fleytman     pvscsi_reset_state(s);
1187881d588aSDmitry Fleytman }
1188881d588aSDmitry Fleytman 
1189881d588aSDmitry Fleytman static void
1190881d588aSDmitry Fleytman pvscsi_uninit(PCIDevice *pci_dev)
1191881d588aSDmitry Fleytman {
1192881d588aSDmitry Fleytman     PVSCSIState *s = PVSCSI(pci_dev);
1193881d588aSDmitry Fleytman 
1194881d588aSDmitry Fleytman     trace_pvscsi_state("uninit");
1195881d588aSDmitry Fleytman     qemu_bh_delete(s->completion_worker);
1196881d588aSDmitry Fleytman 
1197881d588aSDmitry Fleytman     pvscsi_cleanup_msi(s);
1198881d588aSDmitry Fleytman }
1199881d588aSDmitry Fleytman 
1200881d588aSDmitry Fleytman static void
1201881d588aSDmitry Fleytman pvscsi_reset(DeviceState *dev)
1202881d588aSDmitry Fleytman {
1203881d588aSDmitry Fleytman     PCIDevice *d = PCI_DEVICE(dev);
1204881d588aSDmitry Fleytman     PVSCSIState *s = PVSCSI(d);
1205881d588aSDmitry Fleytman 
1206881d588aSDmitry Fleytman     trace_pvscsi_state("reset");
1207881d588aSDmitry Fleytman     pvscsi_reset_adapter(s);
1208881d588aSDmitry Fleytman }
1209881d588aSDmitry Fleytman 
121044b1ff31SDr. David Alan Gilbert static int
1211881d588aSDmitry Fleytman pvscsi_pre_save(void *opaque)
1212881d588aSDmitry Fleytman {
1213881d588aSDmitry Fleytman     PVSCSIState *s = (PVSCSIState *) opaque;
1214881d588aSDmitry Fleytman 
1215881d588aSDmitry Fleytman     trace_pvscsi_state("presave");
1216881d588aSDmitry Fleytman 
1217881d588aSDmitry Fleytman     assert(QTAILQ_EMPTY(&s->pending_queue));
1218881d588aSDmitry Fleytman     assert(QTAILQ_EMPTY(&s->completion_queue));
121944b1ff31SDr. David Alan Gilbert 
122044b1ff31SDr. David Alan Gilbert     return 0;
1221881d588aSDmitry Fleytman }
1222881d588aSDmitry Fleytman 
1223881d588aSDmitry Fleytman static int
1224881d588aSDmitry Fleytman pvscsi_post_load(void *opaque, int version_id)
1225881d588aSDmitry Fleytman {
1226881d588aSDmitry Fleytman     trace_pvscsi_state("postload");
1227881d588aSDmitry Fleytman     return 0;
1228881d588aSDmitry Fleytman }
1229881d588aSDmitry Fleytman 
12301dd1305eSShmulik Ladkani static bool pvscsi_vmstate_need_pcie_device(void *opaque)
12311dd1305eSShmulik Ladkani {
12321dd1305eSShmulik Ladkani     PVSCSIState *s = PVSCSI(opaque);
12331dd1305eSShmulik Ladkani 
12341dd1305eSShmulik Ladkani     return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE);
12351dd1305eSShmulik Ladkani }
12361dd1305eSShmulik Ladkani 
12371dd1305eSShmulik Ladkani static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id)
12381dd1305eSShmulik Ladkani {
12391dd1305eSShmulik Ladkani     return !pvscsi_vmstate_need_pcie_device(opaque);
12401dd1305eSShmulik Ladkani }
12411dd1305eSShmulik Ladkani 
12421dd1305eSShmulik Ladkani static const VMStateDescription vmstate_pvscsi_pcie_device = {
12431dd1305eSShmulik Ladkani     .name = "pvscsi/pcie",
12441dd1305eSShmulik Ladkani     .needed = pvscsi_vmstate_need_pcie_device,
12451dd1305eSShmulik Ladkani     .fields = (VMStateField[]) {
124620daa90aSDr. David Alan Gilbert         VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState),
12471dd1305eSShmulik Ladkani         VMSTATE_END_OF_LIST()
12481dd1305eSShmulik Ladkani     }
12491dd1305eSShmulik Ladkani };
12501dd1305eSShmulik Ladkani 
1251881d588aSDmitry Fleytman static const VMStateDescription vmstate_pvscsi = {
12526783ecf1SPeter Maydell     .name = "pvscsi",
1253881d588aSDmitry Fleytman     .version_id = 0,
1254881d588aSDmitry Fleytman     .minimum_version_id = 0,
1255881d588aSDmitry Fleytman     .pre_save = pvscsi_pre_save,
1256881d588aSDmitry Fleytman     .post_load = pvscsi_post_load,
1257881d588aSDmitry Fleytman     .fields = (VMStateField[]) {
12581dd1305eSShmulik Ladkani         VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState,
12591dd1305eSShmulik Ladkani                             pvscsi_vmstate_test_pci_device, 0,
12601dd1305eSShmulik Ladkani                             vmstate_pci_device, PCIDevice),
1261881d588aSDmitry Fleytman         VMSTATE_UINT8(msi_used, PVSCSIState),
1262881d588aSDmitry Fleytman         VMSTATE_UINT32(resetting, PVSCSIState),
1263881d588aSDmitry Fleytman         VMSTATE_UINT64(reg_interrupt_status, PVSCSIState),
1264881d588aSDmitry Fleytman         VMSTATE_UINT64(reg_interrupt_enabled, PVSCSIState),
1265881d588aSDmitry Fleytman         VMSTATE_UINT64(reg_command_status, PVSCSIState),
1266881d588aSDmitry Fleytman         VMSTATE_UINT64(curr_cmd, PVSCSIState),
1267881d588aSDmitry Fleytman         VMSTATE_UINT32(curr_cmd_data_cntr, PVSCSIState),
1268881d588aSDmitry Fleytman         VMSTATE_UINT32_ARRAY(curr_cmd_data, PVSCSIState,
1269881d588aSDmitry Fleytman                              ARRAY_SIZE(((PVSCSIState *)NULL)->curr_cmd_data)),
1270881d588aSDmitry Fleytman         VMSTATE_UINT8(rings_info_valid, PVSCSIState),
1271881d588aSDmitry Fleytman         VMSTATE_UINT8(msg_ring_info_valid, PVSCSIState),
1272881d588aSDmitry Fleytman         VMSTATE_UINT8(use_msg, PVSCSIState),
1273881d588aSDmitry Fleytman 
1274881d588aSDmitry Fleytman         VMSTATE_UINT64(rings.rs_pa, PVSCSIState),
1275881d588aSDmitry Fleytman         VMSTATE_UINT32(rings.txr_len_mask, PVSCSIState),
1276881d588aSDmitry Fleytman         VMSTATE_UINT32(rings.rxr_len_mask, PVSCSIState),
1277881d588aSDmitry Fleytman         VMSTATE_UINT64_ARRAY(rings.req_ring_pages_pa, PVSCSIState,
1278881d588aSDmitry Fleytman                              PVSCSI_SETUP_RINGS_MAX_NUM_PAGES),
1279881d588aSDmitry Fleytman         VMSTATE_UINT64_ARRAY(rings.cmp_ring_pages_pa, PVSCSIState,
1280881d588aSDmitry Fleytman                              PVSCSI_SETUP_RINGS_MAX_NUM_PAGES),
1281881d588aSDmitry Fleytman         VMSTATE_UINT64(rings.consumed_ptr, PVSCSIState),
1282881d588aSDmitry Fleytman         VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState),
1283881d588aSDmitry Fleytman 
1284881d588aSDmitry Fleytman         VMSTATE_END_OF_LIST()
12851dd1305eSShmulik Ladkani     },
12861dd1305eSShmulik Ladkani     .subsections = (const VMStateDescription*[]) {
12871dd1305eSShmulik Ladkani         &vmstate_pvscsi_pcie_device,
12881dd1305eSShmulik Ladkani         NULL
1289881d588aSDmitry Fleytman     }
1290881d588aSDmitry Fleytman };
1291881d588aSDmitry Fleytman 
1292881d588aSDmitry Fleytman static Property pvscsi_properties[] = {
1293881d588aSDmitry Fleytman     DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1),
1294952970baSShmulik Ladkani     DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags,
1295952970baSShmulik Ladkani                     PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false),
1296d5da3ef2SShmulik Ladkani     DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags,
1297d5da3ef2SShmulik Ladkani                     PVSCSI_COMPAT_DISABLE_PCIE_BIT, false),
1298881d588aSDmitry Fleytman     DEFINE_PROP_END_OF_LIST(),
1299881d588aSDmitry Fleytman };
1300881d588aSDmitry Fleytman 
13011dd1305eSShmulik Ladkani static void pvscsi_realize(DeviceState *qdev, Error **errp)
13021dd1305eSShmulik Ladkani {
1303bd7dff94SEduardo Habkost     PVSCSIClass *pvs_c = PVSCSI_GET_CLASS(qdev);
13041dd1305eSShmulik Ladkani     PCIDevice *pci_dev = PCI_DEVICE(qdev);
13051dd1305eSShmulik Ladkani     PVSCSIState *s = PVSCSI(qdev);
13061dd1305eSShmulik Ladkani 
13071dd1305eSShmulik Ladkani     if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) {
13081dd1305eSShmulik Ladkani         pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
13091dd1305eSShmulik Ladkani     }
13101dd1305eSShmulik Ladkani 
13111dd1305eSShmulik Ladkani     pvs_c->parent_dc_realize(qdev, errp);
13121dd1305eSShmulik Ladkani }
13131dd1305eSShmulik Ladkani 
1314881d588aSDmitry Fleytman static void pvscsi_class_init(ObjectClass *klass, void *data)
1315881d588aSDmitry Fleytman {
1316881d588aSDmitry Fleytman     DeviceClass *dc = DEVICE_CLASS(klass);
1317881d588aSDmitry Fleytman     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
1318bd7dff94SEduardo Habkost     PVSCSIClass *pvs_k = PVSCSI_CLASS(klass);
131991c8daadSIgor Mammedov     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
1320881d588aSDmitry Fleytman 
1321fafeb41cSMao Zhongyi     k->realize = pvscsi_realizefn;
1322881d588aSDmitry Fleytman     k->exit = pvscsi_uninit;
1323881d588aSDmitry Fleytman     k->vendor_id = PCI_VENDOR_ID_VMWARE;
1324881d588aSDmitry Fleytman     k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI;
1325881d588aSDmitry Fleytman     k->class_id = PCI_CLASS_STORAGE_SCSI;
1326881d588aSDmitry Fleytman     k->subsystem_id = 0x1000;
1327bf853881SPhilippe Mathieu-Daudé     device_class_set_parent_realize(dc, pvscsi_realize,
1328bf853881SPhilippe Mathieu-Daudé                                     &pvs_k->parent_dc_realize);
1329881d588aSDmitry Fleytman     dc->reset = pvscsi_reset;
1330881d588aSDmitry Fleytman     dc->vmsd = &vmstate_pvscsi;
13314f67d30bSMarc-André Lureau     device_class_set_props(dc, pvscsi_properties);
1332125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
133391c8daadSIgor Mammedov     hc->unplug = pvscsi_hot_unplug;
133491c8daadSIgor Mammedov     hc->plug = pvscsi_hotplug;
1335881d588aSDmitry Fleytman }
1336881d588aSDmitry Fleytman 
1337881d588aSDmitry Fleytman static const TypeInfo pvscsi_info = {
13386783ecf1SPeter Maydell     .name          = TYPE_PVSCSI,
1339881d588aSDmitry Fleytman     .parent        = TYPE_PCI_DEVICE,
1340e2d4f3f7SShmulik Ladkani     .class_size    = sizeof(PVSCSIClass),
1341881d588aSDmitry Fleytman     .instance_size = sizeof(PVSCSIState),
1342881d588aSDmitry Fleytman     .class_init    = pvscsi_class_init,
134391c8daadSIgor Mammedov     .interfaces = (InterfaceInfo[]) {
134491c8daadSIgor Mammedov         { TYPE_HOTPLUG_HANDLER },
1345a5fa336fSEduardo Habkost         { INTERFACE_PCIE_DEVICE },
1346a5fa336fSEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
134791c8daadSIgor Mammedov         { }
134891c8daadSIgor Mammedov     }
1349881d588aSDmitry Fleytman };
1350881d588aSDmitry Fleytman 
1351881d588aSDmitry Fleytman static void
1352881d588aSDmitry Fleytman pvscsi_register_types(void)
1353881d588aSDmitry Fleytman {
1354881d588aSDmitry Fleytman     type_register_static(&pvscsi_info);
1355881d588aSDmitry Fleytman }
1356881d588aSDmitry Fleytman 
1357881d588aSDmitry Fleytman type_init(pvscsi_register_types);
1358