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