root / hw / virtio-scsi.c @ 206e7f20
History | View | Annotate | Download (21.6 kB)
1 | 973abc7f | Stefan Hajnoczi | /*
|
---|---|---|---|
2 | 973abc7f | Stefan Hajnoczi | * Virtio SCSI HBA
|
3 | 973abc7f | Stefan Hajnoczi | *
|
4 | 973abc7f | Stefan Hajnoczi | * Copyright IBM, Corp. 2010
|
5 | 973abc7f | Stefan Hajnoczi | * Copyright Red Hat, Inc. 2011
|
6 | 973abc7f | Stefan Hajnoczi | *
|
7 | 973abc7f | Stefan Hajnoczi | * Authors:
|
8 | 973abc7f | Stefan Hajnoczi | * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
9 | 973abc7f | Stefan Hajnoczi | * Paolo Bonzini <pbonzini@redhat.com>
|
10 | 973abc7f | Stefan Hajnoczi | *
|
11 | 973abc7f | Stefan Hajnoczi | * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
12 | 973abc7f | Stefan Hajnoczi | * See the COPYING file in the top-level directory.
|
13 | 973abc7f | Stefan Hajnoczi | *
|
14 | 973abc7f | Stefan Hajnoczi | */
|
15 | 973abc7f | Stefan Hajnoczi | |
16 | 973abc7f | Stefan Hajnoczi | #include "virtio-scsi.h" |
17 | 973abc7f | Stefan Hajnoczi | #include <hw/scsi.h> |
18 | 973abc7f | Stefan Hajnoczi | #include <hw/scsi-defs.h> |
19 | 973abc7f | Stefan Hajnoczi | |
20 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_VQ_SIZE 128 |
21 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_CDB_SIZE 32 |
22 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_SENSE_SIZE 96 |
23 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_MAX_CHANNEL 0 |
24 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_MAX_TARGET 255 |
25 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_MAX_LUN 16383 |
26 | 973abc7f | Stefan Hajnoczi | |
27 | 973abc7f | Stefan Hajnoczi | /* Response codes */
|
28 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_OK 0 |
29 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_OVERRUN 1 |
30 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_ABORTED 2 |
31 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_BAD_TARGET 3 |
32 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_RESET 4 |
33 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_BUSY 5 |
34 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 |
35 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_TARGET_FAILURE 7 |
36 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_NEXUS_FAILURE 8 |
37 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_FAILURE 9 |
38 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 |
39 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 |
40 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_S_INCORRECT_LUN 12 |
41 | 973abc7f | Stefan Hajnoczi | |
42 | 973abc7f | Stefan Hajnoczi | /* Controlq type codes. */
|
43 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF 0 |
44 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_AN_QUERY 1 |
45 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 |
46 | 973abc7f | Stefan Hajnoczi | |
47 | 973abc7f | Stefan Hajnoczi | /* Valid TMF subtypes. */
|
48 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 |
49 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 |
50 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 |
51 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 |
52 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 |
53 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 |
54 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 |
55 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 |
56 | 973abc7f | Stefan Hajnoczi | |
57 | 973abc7f | Stefan Hajnoczi | /* Events. */
|
58 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 |
59 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_NO_EVENT 0 |
60 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_TRANSPORT_RESET 1 |
61 | 973abc7f | Stefan Hajnoczi | #define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 |
62 | feda01e4 | Paolo Bonzini | #define VIRTIO_SCSI_T_PARAM_CHANGE 3 |
63 | 973abc7f | Stefan Hajnoczi | |
64 | b6866fee | Cong Meng | /* Reasons for transport reset event */
|
65 | b6866fee | Cong Meng | #define VIRTIO_SCSI_EVT_RESET_HARD 0 |
66 | b6866fee | Cong Meng | #define VIRTIO_SCSI_EVT_RESET_RESCAN 1 |
67 | b6866fee | Cong Meng | #define VIRTIO_SCSI_EVT_RESET_REMOVED 2 |
68 | b6866fee | Cong Meng | |
69 | 973abc7f | Stefan Hajnoczi | /* SCSI command request, followed by data-out */
|
70 | 973abc7f | Stefan Hajnoczi | typedef struct { |
71 | 973abc7f | Stefan Hajnoczi | uint8_t lun[8]; /* Logical Unit Number */ |
72 | 973abc7f | Stefan Hajnoczi | uint64_t tag; /* Command identifier */
|
73 | 973abc7f | Stefan Hajnoczi | uint8_t task_attr; /* Task attribute */
|
74 | 973abc7f | Stefan Hajnoczi | uint8_t prio; |
75 | 973abc7f | Stefan Hajnoczi | uint8_t crn; |
76 | 973abc7f | Stefan Hajnoczi | uint8_t cdb[]; |
77 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSICmdReq; |
78 | 973abc7f | Stefan Hajnoczi | |
79 | 973abc7f | Stefan Hajnoczi | /* Response, followed by sense data and data-in */
|
80 | 973abc7f | Stefan Hajnoczi | typedef struct { |
81 | 973abc7f | Stefan Hajnoczi | uint32_t sense_len; /* Sense data length */
|
82 | 973abc7f | Stefan Hajnoczi | uint32_t resid; /* Residual bytes in data buffer */
|
83 | 973abc7f | Stefan Hajnoczi | uint16_t status_qualifier; /* Status qualifier */
|
84 | 973abc7f | Stefan Hajnoczi | uint8_t status; /* Command completion status */
|
85 | 973abc7f | Stefan Hajnoczi | uint8_t response; /* Response values */
|
86 | 973abc7f | Stefan Hajnoczi | uint8_t sense[]; |
87 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSICmdResp; |
88 | 973abc7f | Stefan Hajnoczi | |
89 | 973abc7f | Stefan Hajnoczi | /* Task Management Request */
|
90 | 973abc7f | Stefan Hajnoczi | typedef struct { |
91 | 973abc7f | Stefan Hajnoczi | uint32_t type; |
92 | 973abc7f | Stefan Hajnoczi | uint32_t subtype; |
93 | 973abc7f | Stefan Hajnoczi | uint8_t lun[8];
|
94 | 973abc7f | Stefan Hajnoczi | uint64_t tag; |
95 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSICtrlTMFReq; |
96 | 973abc7f | Stefan Hajnoczi | |
97 | 973abc7f | Stefan Hajnoczi | typedef struct { |
98 | 973abc7f | Stefan Hajnoczi | uint8_t response; |
99 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSICtrlTMFResp; |
100 | 973abc7f | Stefan Hajnoczi | |
101 | 973abc7f | Stefan Hajnoczi | /* Asynchronous notification query/subscription */
|
102 | 973abc7f | Stefan Hajnoczi | typedef struct { |
103 | 973abc7f | Stefan Hajnoczi | uint32_t type; |
104 | 973abc7f | Stefan Hajnoczi | uint8_t lun[8];
|
105 | 973abc7f | Stefan Hajnoczi | uint32_t event_requested; |
106 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSICtrlANReq; |
107 | 973abc7f | Stefan Hajnoczi | |
108 | 973abc7f | Stefan Hajnoczi | typedef struct { |
109 | 973abc7f | Stefan Hajnoczi | uint32_t event_actual; |
110 | 973abc7f | Stefan Hajnoczi | uint8_t response; |
111 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSICtrlANResp; |
112 | 973abc7f | Stefan Hajnoczi | |
113 | 973abc7f | Stefan Hajnoczi | typedef struct { |
114 | 973abc7f | Stefan Hajnoczi | uint32_t event; |
115 | 973abc7f | Stefan Hajnoczi | uint8_t lun[8];
|
116 | 973abc7f | Stefan Hajnoczi | uint32_t reason; |
117 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSIEvent; |
118 | 973abc7f | Stefan Hajnoczi | |
119 | 973abc7f | Stefan Hajnoczi | typedef struct { |
120 | 973abc7f | Stefan Hajnoczi | uint32_t num_queues; |
121 | 973abc7f | Stefan Hajnoczi | uint32_t seg_max; |
122 | 973abc7f | Stefan Hajnoczi | uint32_t max_sectors; |
123 | 973abc7f | Stefan Hajnoczi | uint32_t cmd_per_lun; |
124 | 973abc7f | Stefan Hajnoczi | uint32_t event_info_size; |
125 | 973abc7f | Stefan Hajnoczi | uint32_t sense_size; |
126 | 973abc7f | Stefan Hajnoczi | uint32_t cdb_size; |
127 | 973abc7f | Stefan Hajnoczi | uint16_t max_channel; |
128 | 973abc7f | Stefan Hajnoczi | uint16_t max_target; |
129 | 973abc7f | Stefan Hajnoczi | uint32_t max_lun; |
130 | 973abc7f | Stefan Hajnoczi | } QEMU_PACKED VirtIOSCSIConfig; |
131 | 973abc7f | Stefan Hajnoczi | |
132 | 973abc7f | Stefan Hajnoczi | typedef struct { |
133 | 973abc7f | Stefan Hajnoczi | VirtIODevice vdev; |
134 | 973abc7f | Stefan Hajnoczi | DeviceState *qdev; |
135 | 973abc7f | Stefan Hajnoczi | VirtIOSCSIConf *conf; |
136 | 973abc7f | Stefan Hajnoczi | |
137 | 2ccdcd8d | Paolo Bonzini | SCSIBus bus; |
138 | 973abc7f | Stefan Hajnoczi | uint32_t sense_size; |
139 | 973abc7f | Stefan Hajnoczi | uint32_t cdb_size; |
140 | 06114d72 | Paolo Bonzini | int resetting;
|
141 | 64f64855 | Paolo Bonzini | bool events_dropped;
|
142 | d2ad7dd4 | Paolo Bonzini | VirtQueue *ctrl_vq; |
143 | d2ad7dd4 | Paolo Bonzini | VirtQueue *event_vq; |
144 | d2ad7dd4 | Paolo Bonzini | VirtQueue *cmd_vqs[0];
|
145 | 973abc7f | Stefan Hajnoczi | } VirtIOSCSI; |
146 | 973abc7f | Stefan Hajnoczi | |
147 | 326799c0 | Stefan Hajnoczi | typedef struct VirtIOSCSIReq { |
148 | 326799c0 | Stefan Hajnoczi | VirtIOSCSI *dev; |
149 | 326799c0 | Stefan Hajnoczi | VirtQueue *vq; |
150 | 326799c0 | Stefan Hajnoczi | VirtQueueElement elem; |
151 | 326799c0 | Stefan Hajnoczi | QEMUSGList qsgl; |
152 | 326799c0 | Stefan Hajnoczi | SCSIRequest *sreq; |
153 | 326799c0 | Stefan Hajnoczi | union {
|
154 | 326799c0 | Stefan Hajnoczi | char *buf;
|
155 | 326799c0 | Stefan Hajnoczi | VirtIOSCSICmdReq *cmd; |
156 | 326799c0 | Stefan Hajnoczi | VirtIOSCSICtrlTMFReq *tmf; |
157 | 326799c0 | Stefan Hajnoczi | VirtIOSCSICtrlANReq *an; |
158 | 326799c0 | Stefan Hajnoczi | } req; |
159 | 326799c0 | Stefan Hajnoczi | union {
|
160 | 326799c0 | Stefan Hajnoczi | char *buf;
|
161 | 326799c0 | Stefan Hajnoczi | VirtIOSCSICmdResp *cmd; |
162 | 326799c0 | Stefan Hajnoczi | VirtIOSCSICtrlTMFResp *tmf; |
163 | 326799c0 | Stefan Hajnoczi | VirtIOSCSICtrlANResp *an; |
164 | 326799c0 | Stefan Hajnoczi | VirtIOSCSIEvent *event; |
165 | 326799c0 | Stefan Hajnoczi | } resp; |
166 | 326799c0 | Stefan Hajnoczi | } VirtIOSCSIReq; |
167 | 326799c0 | Stefan Hajnoczi | |
168 | 2ccdcd8d | Paolo Bonzini | static inline int virtio_scsi_get_lun(uint8_t *lun) |
169 | 2ccdcd8d | Paolo Bonzini | { |
170 | 2ccdcd8d | Paolo Bonzini | return ((lun[2] << 8) | lun[3]) & 0x3FFF; |
171 | 2ccdcd8d | Paolo Bonzini | } |
172 | 2ccdcd8d | Paolo Bonzini | |
173 | 2ccdcd8d | Paolo Bonzini | static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) |
174 | 2ccdcd8d | Paolo Bonzini | { |
175 | 2ccdcd8d | Paolo Bonzini | if (lun[0] != 1) { |
176 | 2ccdcd8d | Paolo Bonzini | return NULL; |
177 | 2ccdcd8d | Paolo Bonzini | } |
178 | 2ccdcd8d | Paolo Bonzini | if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) { |
179 | 2ccdcd8d | Paolo Bonzini | return NULL; |
180 | 2ccdcd8d | Paolo Bonzini | } |
181 | 2ccdcd8d | Paolo Bonzini | return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); |
182 | 2ccdcd8d | Paolo Bonzini | } |
183 | 2ccdcd8d | Paolo Bonzini | |
184 | 326799c0 | Stefan Hajnoczi | static void virtio_scsi_complete_req(VirtIOSCSIReq *req) |
185 | 326799c0 | Stefan Hajnoczi | { |
186 | 326799c0 | Stefan Hajnoczi | VirtIOSCSI *s = req->dev; |
187 | 326799c0 | Stefan Hajnoczi | VirtQueue *vq = req->vq; |
188 | 326799c0 | Stefan Hajnoczi | virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len);
|
189 | 326799c0 | Stefan Hajnoczi | qemu_sglist_destroy(&req->qsgl); |
190 | 326799c0 | Stefan Hajnoczi | if (req->sreq) {
|
191 | 326799c0 | Stefan Hajnoczi | req->sreq->hba_private = NULL;
|
192 | 326799c0 | Stefan Hajnoczi | scsi_req_unref(req->sreq); |
193 | 326799c0 | Stefan Hajnoczi | } |
194 | 326799c0 | Stefan Hajnoczi | g_free(req); |
195 | 326799c0 | Stefan Hajnoczi | virtio_notify(&s->vdev, vq); |
196 | 326799c0 | Stefan Hajnoczi | } |
197 | 326799c0 | Stefan Hajnoczi | |
198 | 326799c0 | Stefan Hajnoczi | static void virtio_scsi_bad_req(void) |
199 | 326799c0 | Stefan Hajnoczi | { |
200 | 326799c0 | Stefan Hajnoczi | error_report("wrong size for virtio-scsi headers");
|
201 | 326799c0 | Stefan Hajnoczi | exit(1);
|
202 | 326799c0 | Stefan Hajnoczi | } |
203 | 326799c0 | Stefan Hajnoczi | |
204 | 326799c0 | Stefan Hajnoczi | static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg, |
205 | 326799c0 | Stefan Hajnoczi | target_phys_addr_t *addr, int num)
|
206 | 326799c0 | Stefan Hajnoczi | { |
207 | 326799c0 | Stefan Hajnoczi | memset(qsgl, 0, sizeof(*qsgl)); |
208 | 326799c0 | Stefan Hajnoczi | while (num--) {
|
209 | 326799c0 | Stefan Hajnoczi | qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len); |
210 | 326799c0 | Stefan Hajnoczi | } |
211 | 326799c0 | Stefan Hajnoczi | } |
212 | 326799c0 | Stefan Hajnoczi | |
213 | 326799c0 | Stefan Hajnoczi | static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq, |
214 | 326799c0 | Stefan Hajnoczi | VirtIOSCSIReq *req) |
215 | 326799c0 | Stefan Hajnoczi | { |
216 | b6866fee | Cong Meng | assert(req->elem.in_num); |
217 | 326799c0 | Stefan Hajnoczi | req->vq = vq; |
218 | 326799c0 | Stefan Hajnoczi | req->dev = s; |
219 | 326799c0 | Stefan Hajnoczi | req->sreq = NULL;
|
220 | b6866fee | Cong Meng | if (req->elem.out_num) {
|
221 | b6866fee | Cong Meng | req->req.buf = req->elem.out_sg[0].iov_base;
|
222 | b6866fee | Cong Meng | } |
223 | 326799c0 | Stefan Hajnoczi | req->resp.buf = req->elem.in_sg[0].iov_base;
|
224 | 326799c0 | Stefan Hajnoczi | |
225 | 326799c0 | Stefan Hajnoczi | if (req->elem.out_num > 1) { |
226 | 326799c0 | Stefan Hajnoczi | qemu_sgl_init_external(&req->qsgl, &req->elem.out_sg[1],
|
227 | 326799c0 | Stefan Hajnoczi | &req->elem.out_addr[1],
|
228 | 326799c0 | Stefan Hajnoczi | req->elem.out_num - 1);
|
229 | 326799c0 | Stefan Hajnoczi | } else {
|
230 | 326799c0 | Stefan Hajnoczi | qemu_sgl_init_external(&req->qsgl, &req->elem.in_sg[1],
|
231 | 326799c0 | Stefan Hajnoczi | &req->elem.in_addr[1],
|
232 | 326799c0 | Stefan Hajnoczi | req->elem.in_num - 1);
|
233 | 326799c0 | Stefan Hajnoczi | } |
234 | 326799c0 | Stefan Hajnoczi | } |
235 | 326799c0 | Stefan Hajnoczi | |
236 | 326799c0 | Stefan Hajnoczi | static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
|
237 | 326799c0 | Stefan Hajnoczi | { |
238 | 326799c0 | Stefan Hajnoczi | VirtIOSCSIReq *req; |
239 | 326799c0 | Stefan Hajnoczi | req = g_malloc(sizeof(*req));
|
240 | 326799c0 | Stefan Hajnoczi | if (!virtqueue_pop(vq, &req->elem)) {
|
241 | 326799c0 | Stefan Hajnoczi | g_free(req); |
242 | 326799c0 | Stefan Hajnoczi | return NULL; |
243 | 326799c0 | Stefan Hajnoczi | } |
244 | 326799c0 | Stefan Hajnoczi | |
245 | 326799c0 | Stefan Hajnoczi | virtio_scsi_parse_req(s, vq, req); |
246 | 326799c0 | Stefan Hajnoczi | return req;
|
247 | 326799c0 | Stefan Hajnoczi | } |
248 | 326799c0 | Stefan Hajnoczi | |
249 | 5db1764c | Paolo Bonzini | static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) |
250 | 5db1764c | Paolo Bonzini | { |
251 | 5db1764c | Paolo Bonzini | VirtIOSCSIReq *req = sreq->hba_private; |
252 | d2ad7dd4 | Paolo Bonzini | uint32_t n = virtio_queue_get_id(req->vq) - 2;
|
253 | 5db1764c | Paolo Bonzini | |
254 | d2ad7dd4 | Paolo Bonzini | assert(n < req->dev->conf->num_queues); |
255 | fcf104a7 | Paolo Bonzini | qemu_put_be32s(f, &n); |
256 | 5db1764c | Paolo Bonzini | qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); |
257 | 5db1764c | Paolo Bonzini | } |
258 | 5db1764c | Paolo Bonzini | |
259 | 5db1764c | Paolo Bonzini | static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) |
260 | 5db1764c | Paolo Bonzini | { |
261 | 5db1764c | Paolo Bonzini | SCSIBus *bus = sreq->bus; |
262 | 5db1764c | Paolo Bonzini | VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); |
263 | 5db1764c | Paolo Bonzini | VirtIOSCSIReq *req; |
264 | fcf104a7 | Paolo Bonzini | uint32_t n; |
265 | 5db1764c | Paolo Bonzini | |
266 | 5db1764c | Paolo Bonzini | req = g_malloc(sizeof(*req));
|
267 | fcf104a7 | Paolo Bonzini | qemu_get_be32s(f, &n); |
268 | d2ad7dd4 | Paolo Bonzini | assert(n < s->conf->num_queues); |
269 | 5db1764c | Paolo Bonzini | qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); |
270 | d2ad7dd4 | Paolo Bonzini | virtio_scsi_parse_req(s, s->cmd_vqs[n], req); |
271 | 5db1764c | Paolo Bonzini | |
272 | 5db1764c | Paolo Bonzini | scsi_req_ref(sreq); |
273 | 5db1764c | Paolo Bonzini | req->sreq = sreq; |
274 | 5db1764c | Paolo Bonzini | if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
|
275 | 5db1764c | Paolo Bonzini | int req_mode =
|
276 | 5db1764c | Paolo Bonzini | (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
|
277 | 5db1764c | Paolo Bonzini | |
278 | 5db1764c | Paolo Bonzini | assert(req->sreq->cmd.mode == req_mode); |
279 | 5db1764c | Paolo Bonzini | } |
280 | 5db1764c | Paolo Bonzini | return req;
|
281 | 5db1764c | Paolo Bonzini | } |
282 | 5db1764c | Paolo Bonzini | |
283 | 06114d72 | Paolo Bonzini | static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) |
284 | 326799c0 | Stefan Hajnoczi | { |
285 | 06114d72 | Paolo Bonzini | SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun); |
286 | 06114d72 | Paolo Bonzini | SCSIRequest *r, *next; |
287 | 0866aca1 | Anthony Liguori | BusChild *kid; |
288 | 06114d72 | Paolo Bonzini | int target;
|
289 | 06114d72 | Paolo Bonzini | |
290 | 06114d72 | Paolo Bonzini | /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
|
291 | 06114d72 | Paolo Bonzini | req->resp.tmf->response = VIRTIO_SCSI_S_OK; |
292 | 06114d72 | Paolo Bonzini | |
293 | 06114d72 | Paolo Bonzini | switch (req->req.tmf->subtype) {
|
294 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_ABORT_TASK:
|
295 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_QUERY_TASK:
|
296 | 06114d72 | Paolo Bonzini | if (!d) {
|
297 | 06114d72 | Paolo Bonzini | goto fail;
|
298 | 06114d72 | Paolo Bonzini | } |
299 | 06114d72 | Paolo Bonzini | if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
|
300 | 06114d72 | Paolo Bonzini | goto incorrect_lun;
|
301 | 06114d72 | Paolo Bonzini | } |
302 | 06114d72 | Paolo Bonzini | QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { |
303 | 4dd7c82c | Paolo Bonzini | VirtIOSCSIReq *cmd_req = r->hba_private; |
304 | 4dd7c82c | Paolo Bonzini | if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) {
|
305 | 06114d72 | Paolo Bonzini | break;
|
306 | 06114d72 | Paolo Bonzini | } |
307 | 06114d72 | Paolo Bonzini | } |
308 | 4dd7c82c | Paolo Bonzini | if (r) {
|
309 | 4dd7c82c | Paolo Bonzini | /*
|
310 | 4dd7c82c | Paolo Bonzini | * Assert that the request has not been completed yet, we
|
311 | 4dd7c82c | Paolo Bonzini | * check for it in the loop above.
|
312 | 4dd7c82c | Paolo Bonzini | */
|
313 | 4dd7c82c | Paolo Bonzini | assert(r->hba_private); |
314 | 06114d72 | Paolo Bonzini | if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
|
315 | 06114d72 | Paolo Bonzini | /* "If the specified command is present in the task set, then
|
316 | 06114d72 | Paolo Bonzini | * return a service response set to FUNCTION SUCCEEDED".
|
317 | 06114d72 | Paolo Bonzini | */
|
318 | 06114d72 | Paolo Bonzini | req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; |
319 | 06114d72 | Paolo Bonzini | } else {
|
320 | 06114d72 | Paolo Bonzini | scsi_req_cancel(r); |
321 | 06114d72 | Paolo Bonzini | } |
322 | 06114d72 | Paolo Bonzini | } |
323 | 06114d72 | Paolo Bonzini | break;
|
324 | 06114d72 | Paolo Bonzini | |
325 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
|
326 | 06114d72 | Paolo Bonzini | if (!d) {
|
327 | 06114d72 | Paolo Bonzini | goto fail;
|
328 | 06114d72 | Paolo Bonzini | } |
329 | 06114d72 | Paolo Bonzini | if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
|
330 | 06114d72 | Paolo Bonzini | goto incorrect_lun;
|
331 | 06114d72 | Paolo Bonzini | } |
332 | 06114d72 | Paolo Bonzini | s->resetting++; |
333 | 06114d72 | Paolo Bonzini | qdev_reset_all(&d->qdev); |
334 | 06114d72 | Paolo Bonzini | s->resetting--; |
335 | 06114d72 | Paolo Bonzini | break;
|
336 | 06114d72 | Paolo Bonzini | |
337 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
|
338 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
|
339 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
|
340 | 06114d72 | Paolo Bonzini | if (!d) {
|
341 | 06114d72 | Paolo Bonzini | goto fail;
|
342 | 06114d72 | Paolo Bonzini | } |
343 | 06114d72 | Paolo Bonzini | if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
|
344 | 06114d72 | Paolo Bonzini | goto incorrect_lun;
|
345 | 06114d72 | Paolo Bonzini | } |
346 | 06114d72 | Paolo Bonzini | QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { |
347 | 06114d72 | Paolo Bonzini | if (r->hba_private) {
|
348 | 06114d72 | Paolo Bonzini | if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
|
349 | 06114d72 | Paolo Bonzini | /* "If there is any command present in the task set, then
|
350 | 06114d72 | Paolo Bonzini | * return a service response set to FUNCTION SUCCEEDED".
|
351 | 06114d72 | Paolo Bonzini | */
|
352 | 06114d72 | Paolo Bonzini | req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; |
353 | 06114d72 | Paolo Bonzini | break;
|
354 | 06114d72 | Paolo Bonzini | } else {
|
355 | 06114d72 | Paolo Bonzini | scsi_req_cancel(r); |
356 | 06114d72 | Paolo Bonzini | } |
357 | 06114d72 | Paolo Bonzini | } |
358 | 06114d72 | Paolo Bonzini | } |
359 | 06114d72 | Paolo Bonzini | break;
|
360 | 06114d72 | Paolo Bonzini | |
361 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
|
362 | 06114d72 | Paolo Bonzini | target = req->req.tmf->lun[1];
|
363 | 06114d72 | Paolo Bonzini | s->resetting++; |
364 | 0866aca1 | Anthony Liguori | QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { |
365 | 0866aca1 | Anthony Liguori | d = DO_UPCAST(SCSIDevice, qdev, kid->child); |
366 | 06114d72 | Paolo Bonzini | if (d->channel == 0 && d->id == target) { |
367 | 06114d72 | Paolo Bonzini | qdev_reset_all(&d->qdev); |
368 | 06114d72 | Paolo Bonzini | } |
369 | 06114d72 | Paolo Bonzini | } |
370 | 06114d72 | Paolo Bonzini | s->resetting--; |
371 | 06114d72 | Paolo Bonzini | break;
|
372 | 06114d72 | Paolo Bonzini | |
373 | 06114d72 | Paolo Bonzini | case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
|
374 | 06114d72 | Paolo Bonzini | default:
|
375 | 06114d72 | Paolo Bonzini | req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED; |
376 | 06114d72 | Paolo Bonzini | break;
|
377 | 326799c0 | Stefan Hajnoczi | } |
378 | 326799c0 | Stefan Hajnoczi | |
379 | 06114d72 | Paolo Bonzini | return;
|
380 | 06114d72 | Paolo Bonzini | |
381 | 06114d72 | Paolo Bonzini | incorrect_lun:
|
382 | 06114d72 | Paolo Bonzini | req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN; |
383 | 06114d72 | Paolo Bonzini | return;
|
384 | 06114d72 | Paolo Bonzini | |
385 | 06114d72 | Paolo Bonzini | fail:
|
386 | 06114d72 | Paolo Bonzini | req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET; |
387 | 326799c0 | Stefan Hajnoczi | } |
388 | 326799c0 | Stefan Hajnoczi | |
389 | 973abc7f | Stefan Hajnoczi | static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) |
390 | 973abc7f | Stefan Hajnoczi | { |
391 | 326799c0 | Stefan Hajnoczi | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
392 | 326799c0 | Stefan Hajnoczi | VirtIOSCSIReq *req; |
393 | 326799c0 | Stefan Hajnoczi | |
394 | 326799c0 | Stefan Hajnoczi | while ((req = virtio_scsi_pop_req(s, vq))) {
|
395 | 06114d72 | Paolo Bonzini | int out_size, in_size;
|
396 | 06114d72 | Paolo Bonzini | if (req->elem.out_num < 1 || req->elem.in_num < 1) { |
397 | 06114d72 | Paolo Bonzini | virtio_scsi_bad_req(); |
398 | 06114d72 | Paolo Bonzini | continue;
|
399 | 06114d72 | Paolo Bonzini | } |
400 | 06114d72 | Paolo Bonzini | |
401 | 06114d72 | Paolo Bonzini | out_size = req->elem.out_sg[0].iov_len;
|
402 | 06114d72 | Paolo Bonzini | in_size = req->elem.in_sg[0].iov_len;
|
403 | 06114d72 | Paolo Bonzini | if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) {
|
404 | 06114d72 | Paolo Bonzini | if (out_size < sizeof(VirtIOSCSICtrlTMFReq) || |
405 | 06114d72 | Paolo Bonzini | in_size < sizeof(VirtIOSCSICtrlTMFResp)) {
|
406 | 06114d72 | Paolo Bonzini | virtio_scsi_bad_req(); |
407 | 06114d72 | Paolo Bonzini | } |
408 | 06114d72 | Paolo Bonzini | virtio_scsi_do_tmf(s, req); |
409 | 06114d72 | Paolo Bonzini | |
410 | 06114d72 | Paolo Bonzini | } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY || |
411 | 06114d72 | Paolo Bonzini | req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { |
412 | 06114d72 | Paolo Bonzini | if (out_size < sizeof(VirtIOSCSICtrlANReq) || |
413 | 06114d72 | Paolo Bonzini | in_size < sizeof(VirtIOSCSICtrlANResp)) {
|
414 | 06114d72 | Paolo Bonzini | virtio_scsi_bad_req(); |
415 | 06114d72 | Paolo Bonzini | } |
416 | 06114d72 | Paolo Bonzini | req->resp.an->event_actual = 0;
|
417 | 06114d72 | Paolo Bonzini | req->resp.an->response = VIRTIO_SCSI_S_OK; |
418 | 06114d72 | Paolo Bonzini | } |
419 | 06114d72 | Paolo Bonzini | virtio_scsi_complete_req(req); |
420 | 326799c0 | Stefan Hajnoczi | } |
421 | 326799c0 | Stefan Hajnoczi | } |
422 | 326799c0 | Stefan Hajnoczi | |
423 | 2ccdcd8d | Paolo Bonzini | static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, |
424 | 2ccdcd8d | Paolo Bonzini | size_t resid) |
425 | 2ccdcd8d | Paolo Bonzini | { |
426 | 2ccdcd8d | Paolo Bonzini | VirtIOSCSIReq *req = r->hba_private; |
427 | 2ccdcd8d | Paolo Bonzini | |
428 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->response = VIRTIO_SCSI_S_OK; |
429 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->status = status; |
430 | 2ccdcd8d | Paolo Bonzini | if (req->resp.cmd->status == GOOD) {
|
431 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->resid = resid; |
432 | 2ccdcd8d | Paolo Bonzini | } else {
|
433 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->resid = 0;
|
434 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->sense_len = |
435 | 2ccdcd8d | Paolo Bonzini | scsi_req_get_sense(r, req->resp.cmd->sense, VIRTIO_SCSI_SENSE_SIZE); |
436 | 2ccdcd8d | Paolo Bonzini | } |
437 | 2ccdcd8d | Paolo Bonzini | virtio_scsi_complete_req(req); |
438 | 2ccdcd8d | Paolo Bonzini | } |
439 | 2ccdcd8d | Paolo Bonzini | |
440 | 2ccdcd8d | Paolo Bonzini | static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r)
|
441 | 2ccdcd8d | Paolo Bonzini | { |
442 | 2ccdcd8d | Paolo Bonzini | VirtIOSCSIReq *req = r->hba_private; |
443 | 2ccdcd8d | Paolo Bonzini | |
444 | 2ccdcd8d | Paolo Bonzini | return &req->qsgl;
|
445 | 2ccdcd8d | Paolo Bonzini | } |
446 | 2ccdcd8d | Paolo Bonzini | |
447 | 2ccdcd8d | Paolo Bonzini | static void virtio_scsi_request_cancelled(SCSIRequest *r) |
448 | 2ccdcd8d | Paolo Bonzini | { |
449 | 2ccdcd8d | Paolo Bonzini | VirtIOSCSIReq *req = r->hba_private; |
450 | 2ccdcd8d | Paolo Bonzini | |
451 | 2ccdcd8d | Paolo Bonzini | if (!req) {
|
452 | 2ccdcd8d | Paolo Bonzini | return;
|
453 | 2ccdcd8d | Paolo Bonzini | } |
454 | 06114d72 | Paolo Bonzini | if (req->dev->resetting) {
|
455 | 06114d72 | Paolo Bonzini | req->resp.cmd->response = VIRTIO_SCSI_S_RESET; |
456 | 06114d72 | Paolo Bonzini | } else {
|
457 | 06114d72 | Paolo Bonzini | req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED; |
458 | 06114d72 | Paolo Bonzini | } |
459 | 2ccdcd8d | Paolo Bonzini | virtio_scsi_complete_req(req); |
460 | 2ccdcd8d | Paolo Bonzini | } |
461 | 2ccdcd8d | Paolo Bonzini | |
462 | 2ccdcd8d | Paolo Bonzini | static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) |
463 | 326799c0 | Stefan Hajnoczi | { |
464 | 326799c0 | Stefan Hajnoczi | req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE; |
465 | 326799c0 | Stefan Hajnoczi | virtio_scsi_complete_req(req); |
466 | 973abc7f | Stefan Hajnoczi | } |
467 | 973abc7f | Stefan Hajnoczi | |
468 | 973abc7f | Stefan Hajnoczi | static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) |
469 | 973abc7f | Stefan Hajnoczi | { |
470 | 326799c0 | Stefan Hajnoczi | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
471 | 326799c0 | Stefan Hajnoczi | VirtIOSCSIReq *req; |
472 | 2ccdcd8d | Paolo Bonzini | int n;
|
473 | 326799c0 | Stefan Hajnoczi | |
474 | 326799c0 | Stefan Hajnoczi | while ((req = virtio_scsi_pop_req(s, vq))) {
|
475 | 2ccdcd8d | Paolo Bonzini | SCSIDevice *d; |
476 | 326799c0 | Stefan Hajnoczi | int out_size, in_size;
|
477 | 326799c0 | Stefan Hajnoczi | if (req->elem.out_num < 1 || req->elem.in_num < 1) { |
478 | 326799c0 | Stefan Hajnoczi | virtio_scsi_bad_req(); |
479 | 326799c0 | Stefan Hajnoczi | } |
480 | 326799c0 | Stefan Hajnoczi | |
481 | 326799c0 | Stefan Hajnoczi | out_size = req->elem.out_sg[0].iov_len;
|
482 | 326799c0 | Stefan Hajnoczi | in_size = req->elem.in_sg[0].iov_len;
|
483 | 326799c0 | Stefan Hajnoczi | if (out_size < sizeof(VirtIOSCSICmdReq) + s->cdb_size || |
484 | 326799c0 | Stefan Hajnoczi | in_size < sizeof(VirtIOSCSICmdResp) + s->sense_size) {
|
485 | 326799c0 | Stefan Hajnoczi | virtio_scsi_bad_req(); |
486 | 326799c0 | Stefan Hajnoczi | } |
487 | 326799c0 | Stefan Hajnoczi | |
488 | 326799c0 | Stefan Hajnoczi | if (req->elem.out_num > 1 && req->elem.in_num > 1) { |
489 | 2ccdcd8d | Paolo Bonzini | virtio_scsi_fail_cmd_req(req); |
490 | 326799c0 | Stefan Hajnoczi | continue;
|
491 | 326799c0 | Stefan Hajnoczi | } |
492 | 326799c0 | Stefan Hajnoczi | |
493 | 2ccdcd8d | Paolo Bonzini | d = virtio_scsi_device_find(s, req->req.cmd->lun); |
494 | 2ccdcd8d | Paolo Bonzini | if (!d) {
|
495 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET; |
496 | 2ccdcd8d | Paolo Bonzini | virtio_scsi_complete_req(req); |
497 | 2ccdcd8d | Paolo Bonzini | continue;
|
498 | 2ccdcd8d | Paolo Bonzini | } |
499 | 2ccdcd8d | Paolo Bonzini | req->sreq = scsi_req_new(d, req->req.cmd->tag, |
500 | 2ccdcd8d | Paolo Bonzini | virtio_scsi_get_lun(req->req.cmd->lun), |
501 | 2ccdcd8d | Paolo Bonzini | req->req.cmd->cdb, req); |
502 | 2ccdcd8d | Paolo Bonzini | |
503 | 2ccdcd8d | Paolo Bonzini | if (req->sreq->cmd.mode != SCSI_XFER_NONE) {
|
504 | 2ccdcd8d | Paolo Bonzini | int req_mode =
|
505 | 2ccdcd8d | Paolo Bonzini | (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV);
|
506 | 2ccdcd8d | Paolo Bonzini | |
507 | 2ccdcd8d | Paolo Bonzini | if (req->sreq->cmd.mode != req_mode ||
|
508 | 2ccdcd8d | Paolo Bonzini | req->sreq->cmd.xfer > req->qsgl.size) { |
509 | 2ccdcd8d | Paolo Bonzini | req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN; |
510 | 2ccdcd8d | Paolo Bonzini | virtio_scsi_complete_req(req); |
511 | 2ccdcd8d | Paolo Bonzini | continue;
|
512 | 2ccdcd8d | Paolo Bonzini | } |
513 | 2ccdcd8d | Paolo Bonzini | } |
514 | 2ccdcd8d | Paolo Bonzini | |
515 | 2ccdcd8d | Paolo Bonzini | n = scsi_req_enqueue(req->sreq); |
516 | 2ccdcd8d | Paolo Bonzini | if (n) {
|
517 | 2ccdcd8d | Paolo Bonzini | scsi_req_continue(req->sreq); |
518 | 2ccdcd8d | Paolo Bonzini | } |
519 | 326799c0 | Stefan Hajnoczi | } |
520 | 973abc7f | Stefan Hajnoczi | } |
521 | 973abc7f | Stefan Hajnoczi | |
522 | 973abc7f | Stefan Hajnoczi | static void virtio_scsi_get_config(VirtIODevice *vdev, |
523 | 973abc7f | Stefan Hajnoczi | uint8_t *config) |
524 | 973abc7f | Stefan Hajnoczi | { |
525 | 973abc7f | Stefan Hajnoczi | VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; |
526 | 973abc7f | Stefan Hajnoczi | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
527 | 973abc7f | Stefan Hajnoczi | |
528 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->num_queues, s->conf->num_queues); |
529 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->seg_max, 128 - 2); |
530 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->max_sectors, s->conf->max_sectors); |
531 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->cmd_per_lun, s->conf->cmd_per_lun); |
532 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent));
|
533 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->sense_size, s->sense_size); |
534 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->cdb_size, s->cdb_size); |
535 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); |
536 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); |
537 | 973abc7f | Stefan Hajnoczi | stl_raw(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); |
538 | 973abc7f | Stefan Hajnoczi | } |
539 | 973abc7f | Stefan Hajnoczi | |
540 | 973abc7f | Stefan Hajnoczi | static void virtio_scsi_set_config(VirtIODevice *vdev, |
541 | 973abc7f | Stefan Hajnoczi | const uint8_t *config)
|
542 | 973abc7f | Stefan Hajnoczi | { |
543 | 973abc7f | Stefan Hajnoczi | VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; |
544 | 973abc7f | Stefan Hajnoczi | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
545 | 973abc7f | Stefan Hajnoczi | |
546 | 973abc7f | Stefan Hajnoczi | if ((uint32_t) ldl_raw(&scsiconf->sense_size) >= 65536 || |
547 | 973abc7f | Stefan Hajnoczi | (uint32_t) ldl_raw(&scsiconf->cdb_size) >= 256) {
|
548 | 973abc7f | Stefan Hajnoczi | error_report("bad data written to virtio-scsi configuration space");
|
549 | 973abc7f | Stefan Hajnoczi | exit(1);
|
550 | 973abc7f | Stefan Hajnoczi | } |
551 | 973abc7f | Stefan Hajnoczi | |
552 | 973abc7f | Stefan Hajnoczi | s->sense_size = ldl_raw(&scsiconf->sense_size); |
553 | 973abc7f | Stefan Hajnoczi | s->cdb_size = ldl_raw(&scsiconf->cdb_size); |
554 | 973abc7f | Stefan Hajnoczi | } |
555 | 973abc7f | Stefan Hajnoczi | |
556 | 973abc7f | Stefan Hajnoczi | static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
|
557 | 973abc7f | Stefan Hajnoczi | uint32_t requested_features) |
558 | 973abc7f | Stefan Hajnoczi | { |
559 | 973abc7f | Stefan Hajnoczi | return requested_features;
|
560 | 973abc7f | Stefan Hajnoczi | } |
561 | 973abc7f | Stefan Hajnoczi | |
562 | 973abc7f | Stefan Hajnoczi | static void virtio_scsi_reset(VirtIODevice *vdev) |
563 | 973abc7f | Stefan Hajnoczi | { |
564 | 973abc7f | Stefan Hajnoczi | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
565 | 973abc7f | Stefan Hajnoczi | |
566 | 973abc7f | Stefan Hajnoczi | s->sense_size = VIRTIO_SCSI_SENSE_SIZE; |
567 | 973abc7f | Stefan Hajnoczi | s->cdb_size = VIRTIO_SCSI_CDB_SIZE; |
568 | 2baa1beb | Paolo Bonzini | s->events_dropped = false;
|
569 | 973abc7f | Stefan Hajnoczi | } |
570 | 973abc7f | Stefan Hajnoczi | |
571 | 5db1764c | Paolo Bonzini | /* The device does not have anything to save beyond the virtio data.
|
572 | 5db1764c | Paolo Bonzini | * Request data is saved with callbacks from SCSI devices.
|
573 | 5db1764c | Paolo Bonzini | */
|
574 | 5db1764c | Paolo Bonzini | static void virtio_scsi_save(QEMUFile *f, void *opaque) |
575 | 5db1764c | Paolo Bonzini | { |
576 | 5db1764c | Paolo Bonzini | VirtIOSCSI *s = opaque; |
577 | 5db1764c | Paolo Bonzini | virtio_save(&s->vdev, f); |
578 | 5db1764c | Paolo Bonzini | } |
579 | 5db1764c | Paolo Bonzini | |
580 | 5db1764c | Paolo Bonzini | static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) |
581 | 5db1764c | Paolo Bonzini | { |
582 | 5db1764c | Paolo Bonzini | VirtIOSCSI *s = opaque; |
583 | 2a633c46 | Orit Wassermann | int ret;
|
584 | 2a633c46 | Orit Wassermann | |
585 | 2a633c46 | Orit Wassermann | ret = virtio_load(&s->vdev, f); |
586 | 2a633c46 | Orit Wassermann | if (ret) {
|
587 | 2a633c46 | Orit Wassermann | return ret;
|
588 | 2a633c46 | Orit Wassermann | } |
589 | 5db1764c | Paolo Bonzini | return 0; |
590 | 5db1764c | Paolo Bonzini | } |
591 | 5db1764c | Paolo Bonzini | |
592 | b6866fee | Cong Meng | static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, |
593 | b6866fee | Cong Meng | uint32_t event, uint32_t reason) |
594 | b6866fee | Cong Meng | { |
595 | b6866fee | Cong Meng | VirtIOSCSIReq *req = virtio_scsi_pop_req(s, s->event_vq); |
596 | b6866fee | Cong Meng | VirtIOSCSIEvent *evt; |
597 | 64f64855 | Paolo Bonzini | int in_size;
|
598 | b6866fee | Cong Meng | |
599 | 64f64855 | Paolo Bonzini | if (!req) {
|
600 | 64f64855 | Paolo Bonzini | s->events_dropped = true;
|
601 | 64f64855 | Paolo Bonzini | return;
|
602 | 64f64855 | Paolo Bonzini | } |
603 | b6866fee | Cong Meng | |
604 | 64f64855 | Paolo Bonzini | if (req->elem.out_num || req->elem.in_num != 1) { |
605 | 64f64855 | Paolo Bonzini | virtio_scsi_bad_req(); |
606 | 64f64855 | Paolo Bonzini | } |
607 | b6866fee | Cong Meng | |
608 | 64f64855 | Paolo Bonzini | if (s->events_dropped) {
|
609 | 64f64855 | Paolo Bonzini | event |= VIRTIO_SCSI_T_EVENTS_MISSED; |
610 | 64f64855 | Paolo Bonzini | s->events_dropped = false;
|
611 | 64f64855 | Paolo Bonzini | } |
612 | 64f64855 | Paolo Bonzini | |
613 | 64f64855 | Paolo Bonzini | in_size = req->elem.in_sg[0].iov_len;
|
614 | 64f64855 | Paolo Bonzini | if (in_size < sizeof(VirtIOSCSIEvent)) { |
615 | 64f64855 | Paolo Bonzini | virtio_scsi_bad_req(); |
616 | 64f64855 | Paolo Bonzini | } |
617 | 64f64855 | Paolo Bonzini | |
618 | 64f64855 | Paolo Bonzini | evt = req->resp.event; |
619 | 64f64855 | Paolo Bonzini | memset(evt, 0, sizeof(VirtIOSCSIEvent)); |
620 | 64f64855 | Paolo Bonzini | evt->event = event; |
621 | 64f64855 | Paolo Bonzini | evt->reason = reason; |
622 | 64f64855 | Paolo Bonzini | if (!dev) {
|
623 | 64f64855 | Paolo Bonzini | assert(event == VIRTIO_SCSI_T_NO_EVENT); |
624 | 64f64855 | Paolo Bonzini | } else {
|
625 | b6866fee | Cong Meng | evt->lun[0] = 1; |
626 | b6866fee | Cong Meng | evt->lun[1] = dev->id;
|
627 | b6866fee | Cong Meng | |
628 | b6866fee | Cong Meng | /* Linux wants us to keep the same encoding we use for REPORT LUNS. */
|
629 | b6866fee | Cong Meng | if (dev->lun >= 256) { |
630 | b6866fee | Cong Meng | evt->lun[2] = (dev->lun >> 8) | 0x40; |
631 | b6866fee | Cong Meng | } |
632 | b6866fee | Cong Meng | evt->lun[3] = dev->lun & 0xFF; |
633 | 64f64855 | Paolo Bonzini | } |
634 | 64f64855 | Paolo Bonzini | virtio_scsi_complete_req(req); |
635 | 64f64855 | Paolo Bonzini | } |
636 | 64f64855 | Paolo Bonzini | |
637 | 64f64855 | Paolo Bonzini | static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq) |
638 | 64f64855 | Paolo Bonzini | { |
639 | 64f64855 | Paolo Bonzini | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
640 | 64f64855 | Paolo Bonzini | |
641 | 64f64855 | Paolo Bonzini | if (s->events_dropped) {
|
642 | 64f64855 | Paolo Bonzini | virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); |
643 | b6866fee | Cong Meng | } |
644 | b6866fee | Cong Meng | } |
645 | b6866fee | Cong Meng | |
646 | feda01e4 | Paolo Bonzini | static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) |
647 | feda01e4 | Paolo Bonzini | { |
648 | feda01e4 | Paolo Bonzini | VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); |
649 | feda01e4 | Paolo Bonzini | |
650 | feda01e4 | Paolo Bonzini | if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) && |
651 | feda01e4 | Paolo Bonzini | (s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) && |
652 | feda01e4 | Paolo Bonzini | dev->type != TYPE_ROM) { |
653 | feda01e4 | Paolo Bonzini | virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, |
654 | feda01e4 | Paolo Bonzini | sense.asc | (sense.ascq << 8));
|
655 | feda01e4 | Paolo Bonzini | } |
656 | feda01e4 | Paolo Bonzini | } |
657 | feda01e4 | Paolo Bonzini | |
658 | b6866fee | Cong Meng | static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev) |
659 | b6866fee | Cong Meng | { |
660 | b6866fee | Cong Meng | VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); |
661 | b6866fee | Cong Meng | |
662 | b6866fee | Cong Meng | if (((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) && |
663 | b6866fee | Cong Meng | (s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { |
664 | b6866fee | Cong Meng | virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, |
665 | b6866fee | Cong Meng | VIRTIO_SCSI_EVT_RESET_RESCAN); |
666 | b6866fee | Cong Meng | } |
667 | b6866fee | Cong Meng | } |
668 | b6866fee | Cong Meng | |
669 | b6866fee | Cong Meng | static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev) |
670 | b6866fee | Cong Meng | { |
671 | b6866fee | Cong Meng | VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); |
672 | b6866fee | Cong Meng | |
673 | b6866fee | Cong Meng | if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { |
674 | b6866fee | Cong Meng | virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, |
675 | b6866fee | Cong Meng | VIRTIO_SCSI_EVT_RESET_REMOVED); |
676 | b6866fee | Cong Meng | } |
677 | b6866fee | Cong Meng | } |
678 | b6866fee | Cong Meng | |
679 | 2ccdcd8d | Paolo Bonzini | static struct SCSIBusInfo virtio_scsi_scsi_info = { |
680 | 2ccdcd8d | Paolo Bonzini | .tcq = true,
|
681 | 2ccdcd8d | Paolo Bonzini | .max_channel = VIRTIO_SCSI_MAX_CHANNEL, |
682 | 2ccdcd8d | Paolo Bonzini | .max_target = VIRTIO_SCSI_MAX_TARGET, |
683 | 2ccdcd8d | Paolo Bonzini | .max_lun = VIRTIO_SCSI_MAX_LUN, |
684 | 2ccdcd8d | Paolo Bonzini | |
685 | 2ccdcd8d | Paolo Bonzini | .complete = virtio_scsi_command_complete, |
686 | 2ccdcd8d | Paolo Bonzini | .cancel = virtio_scsi_request_cancelled, |
687 | feda01e4 | Paolo Bonzini | .change = virtio_scsi_change, |
688 | b6866fee | Cong Meng | .hotplug = virtio_scsi_hotplug, |
689 | b6866fee | Cong Meng | .hot_unplug = virtio_scsi_hot_unplug, |
690 | 2ccdcd8d | Paolo Bonzini | .get_sg_list = virtio_scsi_get_sg_list, |
691 | 5db1764c | Paolo Bonzini | .save_request = virtio_scsi_save_request, |
692 | 5db1764c | Paolo Bonzini | .load_request = virtio_scsi_load_request, |
693 | 2ccdcd8d | Paolo Bonzini | }; |
694 | 2ccdcd8d | Paolo Bonzini | |
695 | 973abc7f | Stefan Hajnoczi | VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) |
696 | 973abc7f | Stefan Hajnoczi | { |
697 | 973abc7f | Stefan Hajnoczi | VirtIOSCSI *s; |
698 | 5db1764c | Paolo Bonzini | static int virtio_scsi_id; |
699 | d2ad7dd4 | Paolo Bonzini | size_t sz; |
700 | d2ad7dd4 | Paolo Bonzini | int i;
|
701 | 973abc7f | Stefan Hajnoczi | |
702 | d2ad7dd4 | Paolo Bonzini | sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *); |
703 | 973abc7f | Stefan Hajnoczi | s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
|
704 | d2ad7dd4 | Paolo Bonzini | sizeof(VirtIOSCSIConfig), sz);
|
705 | 973abc7f | Stefan Hajnoczi | |
706 | 973abc7f | Stefan Hajnoczi | s->qdev = dev; |
707 | 973abc7f | Stefan Hajnoczi | s->conf = proxyconf; |
708 | 973abc7f | Stefan Hajnoczi | |
709 | 973abc7f | Stefan Hajnoczi | /* TODO set up vdev function pointers */
|
710 | 973abc7f | Stefan Hajnoczi | s->vdev.get_config = virtio_scsi_get_config; |
711 | 973abc7f | Stefan Hajnoczi | s->vdev.set_config = virtio_scsi_set_config; |
712 | 973abc7f | Stefan Hajnoczi | s->vdev.get_features = virtio_scsi_get_features; |
713 | 973abc7f | Stefan Hajnoczi | s->vdev.reset = virtio_scsi_reset; |
714 | 973abc7f | Stefan Hajnoczi | |
715 | 973abc7f | Stefan Hajnoczi | s->ctrl_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, |
716 | 973abc7f | Stefan Hajnoczi | virtio_scsi_handle_ctrl); |
717 | 973abc7f | Stefan Hajnoczi | s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, |
718 | 619d7ae9 | Paolo Bonzini | virtio_scsi_handle_event); |
719 | d2ad7dd4 | Paolo Bonzini | for (i = 0; i < s->conf->num_queues; i++) { |
720 | d2ad7dd4 | Paolo Bonzini | s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, |
721 | d2ad7dd4 | Paolo Bonzini | virtio_scsi_handle_cmd); |
722 | d2ad7dd4 | Paolo Bonzini | } |
723 | 973abc7f | Stefan Hajnoczi | |
724 | 2ccdcd8d | Paolo Bonzini | scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info); |
725 | 2ccdcd8d | Paolo Bonzini | if (!dev->hotplugged) {
|
726 | 2ccdcd8d | Paolo Bonzini | scsi_bus_legacy_handle_cmdline(&s->bus); |
727 | 2ccdcd8d | Paolo Bonzini | } |
728 | 2ccdcd8d | Paolo Bonzini | |
729 | 5db1764c | Paolo Bonzini | register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, |
730 | 5db1764c | Paolo Bonzini | virtio_scsi_save, virtio_scsi_load, s); |
731 | 973abc7f | Stefan Hajnoczi | |
732 | 973abc7f | Stefan Hajnoczi | return &s->vdev;
|
733 | 973abc7f | Stefan Hajnoczi | } |
734 | 973abc7f | Stefan Hajnoczi | |
735 | 973abc7f | Stefan Hajnoczi | void virtio_scsi_exit(VirtIODevice *vdev)
|
736 | 973abc7f | Stefan Hajnoczi | { |
737 | eb2fa764 | Paolo Bonzini | VirtIOSCSI *s = (VirtIOSCSI *)vdev; |
738 | eb2fa764 | Paolo Bonzini | unregister_savevm(s->qdev, "virtio-scsi", s);
|
739 | 973abc7f | Stefan Hajnoczi | virtio_cleanup(vdev); |
740 | 973abc7f | Stefan Hajnoczi | } |