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