Revision 2ec749cb
b/hw/scsi-bus.c | ||
---|---|---|
1 | 1 |
#include "hw.h" |
2 | 2 |
#include "sysemu.h" |
3 | 3 |
#include "scsi.h" |
4 |
#include "scsi-defs.h" |
|
4 | 5 |
#include "block.h" |
5 | 6 |
#include "qdev.h" |
6 | 7 |
|
... | ... | |
142 | 143 |
QTAILQ_REMOVE(&req->dev->requests, req, next); |
143 | 144 |
qemu_free(req); |
144 | 145 |
} |
146 |
|
|
147 |
static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) |
|
148 |
{ |
|
149 |
switch (cmd[0] >> 5) { |
|
150 |
case 0: |
|
151 |
req->cmd.xfer = cmd[4]; |
|
152 |
req->cmd.len = 6; |
|
153 |
/* length 0 means 256 blocks */ |
|
154 |
if (req->cmd.xfer == 0) |
|
155 |
req->cmd.xfer = 256; |
|
156 |
break; |
|
157 |
case 1: |
|
158 |
case 2: |
|
159 |
req->cmd.xfer = cmd[8] | (cmd[7] << 8); |
|
160 |
req->cmd.len = 10; |
|
161 |
break; |
|
162 |
case 4: |
|
163 |
req->cmd.xfer = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24); |
|
164 |
req->cmd.len = 16; |
|
165 |
break; |
|
166 |
case 5: |
|
167 |
req->cmd.xfer = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24); |
|
168 |
req->cmd.len = 12; |
|
169 |
break; |
|
170 |
default: |
|
171 |
return -1; |
|
172 |
} |
|
173 |
|
|
174 |
switch(cmd[0]) { |
|
175 |
case TEST_UNIT_READY: |
|
176 |
case REZERO_UNIT: |
|
177 |
case START_STOP: |
|
178 |
case SEEK_6: |
|
179 |
case WRITE_FILEMARKS: |
|
180 |
case SPACE: |
|
181 |
case ERASE: |
|
182 |
case ALLOW_MEDIUM_REMOVAL: |
|
183 |
case VERIFY: |
|
184 |
case SEEK_10: |
|
185 |
case SYNCHRONIZE_CACHE: |
|
186 |
case LOCK_UNLOCK_CACHE: |
|
187 |
case LOAD_UNLOAD: |
|
188 |
case SET_CD_SPEED: |
|
189 |
case SET_LIMITS: |
|
190 |
case WRITE_LONG: |
|
191 |
case MOVE_MEDIUM: |
|
192 |
case UPDATE_BLOCK: |
|
193 |
req->cmd.xfer = 0; |
|
194 |
break; |
|
195 |
case MODE_SENSE: |
|
196 |
break; |
|
197 |
case WRITE_SAME: |
|
198 |
req->cmd.xfer = 1; |
|
199 |
break; |
|
200 |
case READ_CAPACITY: |
|
201 |
req->cmd.xfer = 8; |
|
202 |
break; |
|
203 |
case READ_BLOCK_LIMITS: |
|
204 |
req->cmd.xfer = 6; |
|
205 |
break; |
|
206 |
case READ_POSITION: |
|
207 |
req->cmd.xfer = 20; |
|
208 |
break; |
|
209 |
case SEND_VOLUME_TAG: |
|
210 |
req->cmd.xfer *= 40; |
|
211 |
break; |
|
212 |
case MEDIUM_SCAN: |
|
213 |
req->cmd.xfer *= 8; |
|
214 |
break; |
|
215 |
case WRITE_10: |
|
216 |
case WRITE_VERIFY: |
|
217 |
case WRITE_6: |
|
218 |
case WRITE_12: |
|
219 |
case WRITE_VERIFY_12: |
|
220 |
req->cmd.xfer *= req->dev->blocksize; |
|
221 |
break; |
|
222 |
case READ_10: |
|
223 |
case READ_6: |
|
224 |
case READ_REVERSE: |
|
225 |
case RECOVER_BUFFERED_DATA: |
|
226 |
case READ_12: |
|
227 |
req->cmd.xfer *= req->dev->blocksize; |
|
228 |
break; |
|
229 |
case INQUIRY: |
|
230 |
req->cmd.xfer = cmd[4] | (cmd[3] << 8); |
|
231 |
break; |
|
232 |
} |
|
233 |
return 0; |
|
234 |
} |
|
235 |
|
|
236 |
static int scsi_req_stream_length(SCSIRequest *req, uint8_t *cmd) |
|
237 |
{ |
|
238 |
switch(cmd[0]) { |
|
239 |
/* stream commands */ |
|
240 |
case READ_6: |
|
241 |
case READ_REVERSE: |
|
242 |
case RECOVER_BUFFERED_DATA: |
|
243 |
case WRITE_6: |
|
244 |
req->cmd.len = 6; |
|
245 |
req->cmd.xfer = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16); |
|
246 |
if (cmd[1] & 0x01) /* fixed */ |
|
247 |
req->cmd.xfer *= req->dev->blocksize; |
|
248 |
break; |
|
249 |
case REWIND: |
|
250 |
case START_STOP: |
|
251 |
req->cmd.len = 6; |
|
252 |
req->cmd.xfer = 0; |
|
253 |
break; |
|
254 |
/* generic commands */ |
|
255 |
default: |
|
256 |
return scsi_req_length(req, cmd); |
|
257 |
} |
|
258 |
return 0; |
|
259 |
} |
|
260 |
|
|
261 |
static uint64_t scsi_req_lba(SCSIRequest *req) |
|
262 |
{ |
|
263 |
uint8_t *buf = req->cmd.buf; |
|
264 |
uint64_t lba; |
|
265 |
|
|
266 |
switch (buf[0] >> 5) { |
|
267 |
case 0: |
|
268 |
lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) | |
|
269 |
(((uint64_t) buf[1] & 0x1f) << 16); |
|
270 |
break; |
|
271 |
case 1: |
|
272 |
case 2: |
|
273 |
lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | |
|
274 |
((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); |
|
275 |
break; |
|
276 |
case 4: |
|
277 |
lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) | |
|
278 |
((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) | |
|
279 |
((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) | |
|
280 |
((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56); |
|
281 |
break; |
|
282 |
case 5: |
|
283 |
lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | |
|
284 |
((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); |
|
285 |
break; |
|
286 |
default: |
|
287 |
lba = -1; |
|
288 |
|
|
289 |
} |
|
290 |
return lba; |
|
291 |
} |
|
292 |
|
|
293 |
int scsi_req_parse(SCSIRequest *req, uint8_t *buf) |
|
294 |
{ |
|
295 |
int rc; |
|
296 |
|
|
297 |
if (req->dev->type == TYPE_TAPE) { |
|
298 |
rc = scsi_req_stream_length(req, buf); |
|
299 |
} else { |
|
300 |
rc = scsi_req_length(req, buf); |
|
301 |
} |
|
302 |
if (rc != 0) |
|
303 |
return rc; |
|
304 |
|
|
305 |
memcpy(req->cmd.buf, buf, req->cmd.len); |
|
306 |
req->cmd.lba = scsi_req_lba(req); |
|
307 |
return 0; |
|
308 |
} |
b/hw/scsi-generic.c | ||
---|---|---|
288 | 288 |
return r->buf; |
289 | 289 |
} |
290 | 290 |
|
291 |
static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len)
|
|
291 |
static void scsi_req_fixup(SCSIRequest *req)
|
|
292 | 292 |
{ |
293 |
switch (cmd[0] >> 5) { |
|
294 |
case 0: |
|
295 |
*len = cmd[4]; |
|
296 |
*cmdlen = 6; |
|
297 |
/* length 0 means 256 blocks */ |
|
298 |
if (*len == 0) |
|
299 |
*len = 256; |
|
300 |
break; |
|
301 |
case 1: |
|
302 |
case 2: |
|
303 |
*len = cmd[8] | (cmd[7] << 8); |
|
304 |
*cmdlen = 10; |
|
305 |
break; |
|
306 |
case 4: |
|
307 |
*len = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24); |
|
308 |
*cmdlen = 16; |
|
309 |
break; |
|
310 |
case 5: |
|
311 |
*len = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24); |
|
312 |
*cmdlen = 12; |
|
313 |
break; |
|
314 |
default: |
|
315 |
return -1; |
|
316 |
} |
|
317 |
|
|
318 |
switch(cmd[0]) { |
|
319 |
case TEST_UNIT_READY: |
|
320 |
case REZERO_UNIT: |
|
321 |
case START_STOP: |
|
322 |
case SEEK_6: |
|
323 |
case WRITE_FILEMARKS: |
|
324 |
case SPACE: |
|
325 |
case ERASE: |
|
326 |
case ALLOW_MEDIUM_REMOVAL: |
|
327 |
case VERIFY: |
|
328 |
case SEEK_10: |
|
329 |
case SYNCHRONIZE_CACHE: |
|
330 |
case LOCK_UNLOCK_CACHE: |
|
331 |
case LOAD_UNLOAD: |
|
332 |
case SET_CD_SPEED: |
|
333 |
case SET_LIMITS: |
|
334 |
case WRITE_LONG: |
|
335 |
case MOVE_MEDIUM: |
|
336 |
case UPDATE_BLOCK: |
|
337 |
*len = 0; |
|
338 |
break; |
|
339 |
case MODE_SENSE: |
|
340 |
break; |
|
341 |
case WRITE_SAME: |
|
342 |
*len = 1; |
|
343 |
break; |
|
344 |
case READ_CAPACITY: |
|
345 |
*len = 8; |
|
346 |
break; |
|
347 |
case READ_BLOCK_LIMITS: |
|
348 |
*len = 6; |
|
349 |
break; |
|
350 |
case READ_POSITION: |
|
351 |
*len = 20; |
|
352 |
break; |
|
353 |
case SEND_VOLUME_TAG: |
|
354 |
*len *= 40; |
|
355 |
break; |
|
356 |
case MEDIUM_SCAN: |
|
357 |
*len *= 8; |
|
358 |
break; |
|
293 |
switch(req->cmd.buf[0]) { |
|
359 | 294 |
case WRITE_10: |
360 |
cmd[1] &= ~0x08; /* disable FUA */ |
|
361 |
case WRITE_VERIFY: |
|
362 |
case WRITE_6: |
|
363 |
case WRITE_12: |
|
364 |
case WRITE_VERIFY_12: |
|
365 |
*len *= blocksize; |
|
295 |
req->cmd.buf[1] &= ~0x08; /* disable FUA */ |
|
366 | 296 |
break; |
367 | 297 |
case READ_10: |
368 |
cmd[1] &= ~0x08; /* disable FUA */ |
|
369 |
case READ_6: |
|
370 |
case READ_REVERSE: |
|
371 |
case RECOVER_BUFFERED_DATA: |
|
372 |
case READ_12: |
|
373 |
*len *= blocksize; |
|
374 |
break; |
|
375 |
case INQUIRY: |
|
376 |
*len = cmd[4] | (cmd[3] << 8); |
|
377 |
break; |
|
378 |
} |
|
379 |
return 0; |
|
380 |
} |
|
381 |
|
|
382 |
static int scsi_stream_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) |
|
383 |
{ |
|
384 |
switch(cmd[0]) { |
|
385 |
/* stream commands */ |
|
386 |
case READ_6: |
|
387 |
case READ_REVERSE: |
|
388 |
case RECOVER_BUFFERED_DATA: |
|
389 |
case WRITE_6: |
|
390 |
*cmdlen = 6; |
|
391 |
*len = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16); |
|
392 |
if (cmd[1] & 0x01) /* fixed */ |
|
393 |
*len *= blocksize; |
|
298 |
req->cmd.buf[1] &= ~0x08; /* disable FUA */ |
|
394 | 299 |
break; |
395 | 300 |
case REWIND: |
396 | 301 |
case START_STOP: |
397 |
*cmdlen = 6; |
|
398 |
*len = 0; |
|
399 |
cmd[1] = 0x01; /* force IMMED, otherwise qemu waits end of command */ |
|
302 |
if (req->dev->type == TYPE_TAPE) { |
|
303 |
/* force IMMED, otherwise qemu waits end of command */ |
|
304 |
req->cmd.buf[1] = 0x01; |
|
305 |
} |
|
400 | 306 |
break; |
401 |
/* generic commands */ |
|
402 |
default: |
|
403 |
return scsi_length(cmd, blocksize, cmdlen, len); |
|
404 | 307 |
} |
405 |
return 0; |
|
406 | 308 |
} |
407 | 309 |
|
408 | 310 |
static int is_write(int command) |
... | ... | |
452 | 354 |
uint8_t *cmd, int lun) |
453 | 355 |
{ |
454 | 356 |
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); |
455 |
uint32_t len=0; |
|
456 |
int cmdlen=0; |
|
457 | 357 |
SCSIGenericReq *r; |
458 | 358 |
SCSIBus *bus; |
459 | 359 |
int ret; |
460 | 360 |
|
461 |
if (s->qdev.type == TYPE_TAPE) { |
|
462 |
if (scsi_stream_length(cmd, s->qdev.blocksize, &cmdlen, &len) == -1) { |
|
463 |
BADF("Unsupported command length, command %x\n", cmd[0]); |
|
464 |
return 0; |
|
465 |
} |
|
466 |
} else { |
|
467 |
if (scsi_length(cmd, s->qdev.blocksize, &cmdlen, &len) == -1) { |
|
468 |
BADF("Unsupported command length, command %x\n", cmd[0]); |
|
469 |
return 0; |
|
470 |
} |
|
471 |
} |
|
472 |
|
|
473 |
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, |
|
474 |
cmd[0], len); |
|
475 |
|
|
476 | 361 |
if (cmd[0] != REQUEST_SENSE && |
477 | 362 |
(lun != s->lun || (cmd[1] >> 5) != s->lun)) { |
478 | 363 |
DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); |
... | ... | |
498 | 383 |
} |
499 | 384 |
r = scsi_new_request(d, tag, lun); |
500 | 385 |
|
501 |
memcpy(r->req.cmd.buf, cmd, cmdlen); |
|
502 |
r->req.cmd.len = cmdlen; |
|
386 |
if (-1 == scsi_req_parse(&r->req, cmd)) { |
|
387 |
BADF("Unsupported command length, command %x\n", cmd[0]); |
|
388 |
scsi_remove_request(r); |
|
389 |
return 0; |
|
390 |
} |
|
391 |
scsi_req_fixup(&r->req); |
|
392 |
|
|
393 |
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, |
|
394 |
cmd[0], r->req.cmd.xfer); |
|
503 | 395 |
|
504 |
if (len == 0) {
|
|
396 |
if (r->req.cmd.xfer == 0) {
|
|
505 | 397 |
if (r->buf != NULL) |
506 | 398 |
qemu_free(r->buf); |
507 | 399 |
r->buflen = 0; |
... | ... | |
514 | 406 |
return 0; |
515 | 407 |
} |
516 | 408 |
|
517 |
if (r->buflen != len) {
|
|
409 |
if (r->buflen != r->req.cmd.xfer) {
|
|
518 | 410 |
if (r->buf != NULL) |
519 | 411 |
qemu_free(r->buf); |
520 |
r->buf = qemu_malloc(len);
|
|
521 |
r->buflen = len;
|
|
412 |
r->buf = qemu_malloc(r->req.cmd.xfer);
|
|
413 |
r->buflen = r->req.cmd.xfer;
|
|
522 | 414 |
} |
523 | 415 |
|
524 | 416 |
memset(r->buf, 0, r->buflen); |
525 |
r->len = len;
|
|
417 |
r->len = r->req.cmd.xfer;
|
|
526 | 418 |
if (is_write(cmd[0])) { |
527 | 419 |
r->len = 0; |
528 |
return -len;
|
|
420 |
return -r->req.cmd.xfer;
|
|
529 | 421 |
} |
530 | 422 |
|
531 |
return len;
|
|
423 |
return r->req.cmd.xfer;
|
|
532 | 424 |
} |
533 | 425 |
|
534 | 426 |
static int get_blocksize(BlockDriverState *bdrv) |
b/hw/scsi.h | ||
---|---|---|
26 | 26 |
struct { |
27 | 27 |
uint8_t buf[SCSI_CMD_BUF_SIZE]; |
28 | 28 |
int len; |
29 |
size_t xfer; |
|
30 |
uint64_t lba; |
|
29 | 31 |
} cmd; |
30 | 32 |
BlockDriverAIOCB *aiocb; |
31 | 33 |
QTAILQ_ENTRY(SCSIRequest) next; |
... | ... | |
86 | 88 |
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); |
87 | 89 |
SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag); |
88 | 90 |
void scsi_req_free(SCSIRequest *req); |
91 |
int scsi_req_parse(SCSIRequest *req, uint8_t *buf); |
|
89 | 92 |
|
90 | 93 |
#endif |
Also available in: Unified diff