VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvSCSI.cpp@ 25893

Last change on this file since 25893 was 25893, checked in by vboxsync, 15 years ago

PDMDrv,*: multi context drivers, part 2.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.7 KB
Line 
1/* $Id: DrvSCSI.cpp 25893 2010-01-18 14:08:39Z vboxsync $ */
2/** @file
3 *
4 * VBox storage drivers:
5 * Generic SCSI command parser and execution driver
6 */
7
8/*
9 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27//#define DEBUG
28#define LOG_GROUP LOG_GROUP_DRV_SCSI
29#include <VBox/pdmdrv.h>
30#include <VBox/pdmifs.h>
31#include <VBox/pdmthread.h>
32#include <VBox/scsi.h>
33#include <iprt/assert.h>
34#include <iprt/string.h>
35#include <iprt/alloc.h>
36#include <iprt/req.h>
37#include <iprt/semaphore.h>
38
39#include "Builtins.h"
40
41/**
42 * SCSI driver instance data.
43 */
44typedef struct DRVSCSI
45{
46 /** Pointer driver instance. */
47 PPDMDRVINS pDrvIns;
48
49 /** Pointer to the attached driver's base interface. */
50 PPDMIBASE pDrvBase;
51 /** Pointer to the attached driver's block interface. */
52 PPDMIBLOCK pDrvBlock;
53 /** Pointer to the attached driver's async block interface. */
54 PPDMIBLOCKASYNC pDrvBlockAsync;
55 /** Pointer to the attached driver's block bios interface. */
56 PPDMIBLOCKBIOS pDrvBlockBios;
57 /** Pointer to the attached driver's mount interface. */
58 PPDMIMOUNT pDrvMount;
59 /** Pointer to the SCSI port interface of the device above. */
60 PPDMISCSIPORT pDevScsiPort;
61 /** pointer to the Led port interface of the dveice above. */
62 PPDMILEDPORTS pLedPort;
63 /** The scsi connector interface .*/
64 PDMISCSICONNECTOR ISCSIConnector;
65 /** The block port interface. */
66 PDMIBLOCKPORT IPort;
67 /** The optional block async port interface. */
68 PDMIBLOCKASYNCPORT IPortAsync;
69 /** The mount notify interface. */
70 PDMIMOUNTNOTIFY IMountNotify;
71 /** The status LED state for this drive.
72 * used in case the device doesn't has a Led interface
73 * so we can use this to avoid if checks later on. */
74 PDMLED Led;
75 /** pointer to the Led to use. */
76 PPDMLED pLed;
77
78 /** Device type. */
79 PDMBLOCKTYPE enmType;
80 /** BIOS PCHS Geometry. */
81 PDMMEDIAGEOMETRY PCHSGeometry;
82 /** BIOS LCHS Geometry. */
83 PDMMEDIAGEOMETRY LCHSGeometry;
84 /** Number of sectors this device has. */
85 uint64_t cSectors;
86
87 /** The dedicated I/O thread for the non async approach. */
88 PPDMTHREAD pAsyncIOThread;
89 /** Queue for passing the requests to the thread. */
90 PRTREQQUEUE pQueueRequests;
91 /** Request that we've left pending on wakeup or reset. */
92 PRTREQ pPendingDummyReq;
93 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
94 * any of the dummy functions. */
95 bool volatile fDummySignal;
96 /** Release statistics: number of bytes written. */
97 STAMCOUNTER StatBytesWritten;
98 /** Release statistics: number of bytes read. */
99 STAMCOUNTER StatBytesRead;
100} DRVSCSI, *PDRVSCSI;
101
102/** Converts a pointer to DRVSCSI::ISCSIConnecotr to a PDRVSCSI. */
103#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
104
105#ifdef DEBUG
106/**
107 * Dumps a SCSI request structure for debugging purposes.
108 *
109 * @returns nothing.
110 * @param pRequest Pointer to the request to dump.
111 */
112static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
113{
114 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
115 Log(("cbCDB=%u\n", pRequest->cbCDB));
116 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
117 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
118 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
119 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
120 /* Print all scatter gather entries. */
121 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
122 {
123 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
124 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
125 }
126 Log(("pvUser=%#p\n", pRequest->pvUser));
127}
128#endif
129
130/**
131 * Copy the content of a buffer to a scatter gather list only
132 * copying only the amount of data which fits into the
133 * scatter gather list.
134 *
135 * @returns VBox status code.
136 * @param pRequest Pointer to the request which contains the S/G list entries.
137 * @param pvBuf Pointer to the buffer which should be copied.
138 * @param cbBuf Size of the buffer.
139 */
140static int drvscsiScatterGatherListCopyFromBuffer(PPDMSCSIREQUEST pRequest, void *pvBuf, size_t cbBuf)
141{
142 unsigned cSGEntry = 0;
143 PPDMDATASEG pSGEntry = &pRequest->paScatterGatherHead[cSGEntry];
144 uint8_t *pu8Buf = (uint8_t *)pvBuf;
145
146 LogFlowFunc(("pRequest=%#p pvBuf=%#p cbBuf=%u\n", pRequest, pvBuf, cbBuf));
147
148#ifdef DEBUG
149 for (unsigned i = 0; i < cbBuf; i++)
150 Log(("%s: pvBuf[%u]=%#x\n", __FUNCTION__, i, pu8Buf[i]));
151#endif
152
153 while (cSGEntry < pRequest->cScatterGatherEntries)
154 {
155 size_t cbToCopy = (cbBuf < pSGEntry->cbSeg) ? cbBuf : pSGEntry->cbSeg;
156
157 memcpy(pSGEntry->pvSeg, pu8Buf, cbToCopy);
158
159 cbBuf -= cbToCopy;
160 /* We finished. */
161 if (!cbBuf)
162 break;
163
164 /* Advance the buffer. */
165 pu8Buf += cbToCopy;
166
167 /* Go to the next entry in the list. */
168 pSGEntry++;
169 cSGEntry++;
170 }
171
172 return VINF_SUCCESS;
173}
174
175static void drvscsiPadStr(int8_t *pbDst, const char *pbSrc, uint32_t cbSize)
176{
177 for (uint32_t i = 0; i < cbSize; i++)
178 {
179 if (*pbSrc)
180 pbDst[i] = *pbSrc++;
181 else
182 pbDst[i] = ' ';
183 }
184}
185
186/**
187 * Set the sense and advanced sense key in the buffer for error conditions.
188 *
189 * @returns SCSI status code.
190 * @param pRequest Pointer to the request which contains the sense buffer.
191 * @param uSCSISenseKey The sense key to set.
192 * @param uSCSIASC The advanced sense key to set.
193 */
194DECLINLINE(int) drvscsiCmdError(PPDMSCSIREQUEST pRequest, uint8_t uSCSISenseKey, uint8_t uSCSIASC)
195{
196 AssertMsgReturn(pRequest->cbSenseBuffer >= 18, ("Sense buffer is not big enough\n"), SCSI_STATUS_OK);
197 AssertMsgReturn(pRequest->pbSenseBuffer, ("Sense buffer pointer is NULL\n"), SCSI_STATUS_OK);
198 memset(pRequest->pbSenseBuffer, 0, pRequest->cbSenseBuffer);
199 pRequest->pbSenseBuffer[0] = (1 << 7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED; /* Fixed format */
200 pRequest->pbSenseBuffer[2] = uSCSISenseKey;
201 pRequest->pbSenseBuffer[7] = 10;
202 pRequest->pbSenseBuffer[12] = uSCSIASC;
203 pRequest->pbSenseBuffer[13] = 0x00; /** @todo: Provide more info. */
204 return SCSI_STATUS_CHECK_CONDITION;
205}
206
207/**
208 * Sets the sense key for a status good condition.
209 *
210 * @returns SCSI status code.
211 * @param pRequest Pointer to the request which contains the sense buffer.
212 */
213DECLINLINE(int) drvscsiCmdOk(PPDMSCSIREQUEST pRequest)
214{
215 AssertMsgReturn(pRequest->cbSenseBuffer >= 18, ("Sense buffer is not big enough\n"), SCSI_STATUS_OK);
216 AssertMsgReturn(pRequest->pbSenseBuffer, ("Sense buffer pointer is NULL\n"), SCSI_STATUS_OK);
217 memset(pRequest->pbSenseBuffer, 0, pRequest->cbSenseBuffer);
218 /*
219 * Setting this breaks Linux guests on the BusLogic controller.
220 * According to the SCSI SPC spec sense data is returned after a
221 * CHECK CONDITION status or a REQUEST SENSE command.
222 * Both SCSI controllers have a feature called Auto Sense which
223 * fetches the sense data automatically from the device
224 * with REQUEST SENSE. So the SCSI subsystem in Linux should
225 * find this sense data even if the command finishes successfully
226 * but if it finds valid sense data it will let the command fail
227 * and it doesn't detect attached disks anymore.
228 * Disabling makes it work again and no other guest shows errors
229 * so I will leave it disabled for now.
230 *
231 * On the other hand it is possible that the devices fetch the sense data
232 * only after a command failed so the content is really invalid if
233 * the command succeeds.
234 */
235#if 0
236 pRequest->pbSenseBuffer[0] = (1 << 7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED; /* Fixed format */
237 pRequest->pbSenseBuffer[2] = SCSI_SENSE_NONE;
238 pRequest->pbSenseBuffer[7] = 10;
239 pRequest->pbSenseBuffer[12] = SCSI_ASC_NONE;
240 pRequest->pbSenseBuffer[13] = SCSI_ASC_NONE; /* Should be ASCQ but it has the same value for success. */
241#endif
242 return SCSI_STATUS_OK;
243}
244
245DECLINLINE(void) drvscsiH2BE_U16(uint8_t *pbBuf, uint16_t val)
246{
247 pbBuf[0] = val >> 8;
248 pbBuf[1] = val;
249}
250
251
252DECLINLINE(void) drvscsiH2BE_U24(uint8_t *pbBuf, uint32_t val)
253{
254 pbBuf[0] = val >> 16;
255 pbBuf[1] = val >> 8;
256 pbBuf[2] = val;
257}
258
259
260DECLINLINE(void) drvscsiH2BE_U32(uint8_t *pbBuf, uint32_t val)
261{
262 pbBuf[0] = val >> 24;
263 pbBuf[1] = val >> 16;
264 pbBuf[2] = val >> 8;
265 pbBuf[3] = val;
266}
267
268DECLINLINE(void) drvscsiH2BE_U64(uint8_t *pbBuf, uint64_t val)
269{
270 pbBuf[0] = val >> 56;
271 pbBuf[1] = val >> 48;
272 pbBuf[2] = val >> 40;
273 pbBuf[3] = val >> 32;
274 pbBuf[4] = val >> 24;
275 pbBuf[5] = val >> 16;
276 pbBuf[6] = val >> 8;
277 pbBuf[7] = val;
278}
279
280DECLINLINE(uint16_t) drvscsiBE2H_U16(const uint8_t *pbBuf)
281{
282 return (pbBuf[0] << 8) | pbBuf[1];
283}
284
285
286DECLINLINE(uint32_t) drvscsiBE2H_U24(const uint8_t *pbBuf)
287{
288 return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
289}
290
291
292DECLINLINE(uint32_t) drvscsiBE2H_U32(const uint8_t *pbBuf)
293{
294 return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
295}
296
297DECLINLINE(uint64_t) drvscsiBE2H_U64(const uint8_t *pbBuf)
298{
299 return ((uint64_t)pbBuf[0] << 56)
300 | ((uint64_t)pbBuf[1] << 48)
301 | ((uint64_t)pbBuf[2] << 40)
302 | ((uint64_t)pbBuf[3] << 32)
303 | ((uint64_t)pbBuf[4] << 24)
304 | ((uint64_t)pbBuf[5] << 16)
305 | ((uint64_t)pbBuf[6] << 8)
306 | (uint64_t)pbBuf[7];
307}
308
309/**
310 * Parses the CDB of a request and acts accordingly.
311 *
312 * @returns transfer direction type.
313 * @param pThis Pointer to the SCSI driver instance data.
314 * @param pRequest Pointer to the request to process.
315 * @param puOffset Where to store the start offset to start data transfer from.
316 * @param pcbToTransfer Where to store the number of bytes to transfer.
317 * @param piTxDir Where to store the data transfer direction.
318 */
319static int drvscsiProcessCDB(PDRVSCSI pThis, PPDMSCSIREQUEST pRequest, uint64_t *puOffset, uint32_t *pcbToTransfer, int *piTxDir)
320{
321 int iTxDir = PDMBLOCKTXDIR_NONE;
322 int rc = SCSI_STATUS_OK;
323
324 /* We check for a command which needs to be handled even for non existant LUNs. */
325 switch (pRequest->pbCDB[0])
326 {
327 case SCSI_INQUIRY:
328 {
329 SCSIINQUIRYDATA ScsiInquiryReply;
330
331 memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
332
333 ScsiInquiryReply.cbAdditional = 31;
334
335 /* We support only one attached device at LUN0 at the moment. */
336 if (pRequest->uLogicalUnit != 0)
337 {
338 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
339 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
340 }
341 else
342 {
343 switch (pThis->enmType)
344 {
345 case PDMBLOCKTYPE_HARD_DISK:
346 ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
347 break;
348 default:
349 AssertMsgFailed(("Device type %u not supported\n", pThis->enmType));
350 }
351
352 ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
353 ScsiInquiryReply.u3AnsiVersion = 0x05; /* SPC-4 compliant */
354 drvscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
355 drvscsiPadStr(ScsiInquiryReply.achProductId, "HARDDISK", 16);
356 drvscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);
357 }
358
359 drvscsiScatterGatherListCopyFromBuffer(pRequest, &ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
360 rc = drvscsiCmdOk(pRequest);
361 break;
362 }
363 case SCSI_REPORT_LUNS:
364 {
365 /*
366 * If allocation length is less than 16 bytes SPC compliant devices have
367 * to return an error.
368 */
369 if (drvscsiBE2H_U32(&pRequest->pbCDB[6]) < 16)
370 rc = drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
371 else
372 {
373 uint8_t aReply[16]; /* We report only one LUN. */
374
375 memset(aReply, 0, sizeof(aReply));
376 drvscsiH2BE_U32(&aReply[0], 8); /* List length starts at position 0. */
377 drvscsiScatterGatherListCopyFromBuffer(pRequest, aReply, sizeof(aReply));
378 rc = drvscsiCmdOk(pRequest);
379 }
380 break;
381 }
382 case SCSI_TEST_UNIT_READY:
383 {
384 rc = drvscsiCmdOk(pRequest);
385 break;
386 }
387 default:
388 {
389 /* Now for commands which are only implemented for existant LUNs. */
390 if (RT_LIKELY(pRequest->uLogicalUnit == 0))
391 {
392 switch(pRequest->pbCDB[0])
393 {
394 case SCSI_READ_CAPACITY:
395 {
396 uint8_t aReply[8];
397 memset(aReply, 0, sizeof(aReply));
398
399 /*
400 * If sector size exceeds the maximum value that is
401 * able to be stored in 4 bytes return 0xffffffff in this field
402 */
403 if (pThis->cSectors > UINT32_C(0xffffffff))
404 drvscsiH2BE_U32(aReply, UINT32_C(0xffffffff));
405 else
406 drvscsiH2BE_U32(aReply, pThis->cSectors - 1);
407 drvscsiH2BE_U32(&aReply[4], 512);
408 drvscsiScatterGatherListCopyFromBuffer(pRequest, aReply, sizeof(aReply));
409 rc = drvscsiCmdOk(pRequest);
410 break;
411 }
412 case SCSI_MODE_SENSE_6:
413 {
414 uint8_t uModePage = pRequest->pbCDB[2] & 0x3f;
415 uint8_t aReply[24];
416 uint8_t *pu8ReplyPos;
417
418 memset(aReply, 0, sizeof(aReply));
419 aReply[0] = 4; /* Reply length 4. */
420 aReply[1] = 0; /* Default media type. */
421 aReply[2] = RT_BIT(4); /* Caching supported. */
422 aReply[3] = 0; /* Block descriptor length. */
423
424 pu8ReplyPos = aReply + 4;
425
426 if ((uModePage == 0x08) || (uModePage == 0x3f))
427 {
428 memset(pu8ReplyPos, 0, 20);
429 *pu8ReplyPos++ = 0x08; /* Page code. */
430 *pu8ReplyPos++ = 0x12; /* Size of the page. */
431 *pu8ReplyPos++ = 0x4; /* Write cache enabled. */
432 }
433
434 drvscsiScatterGatherListCopyFromBuffer(pRequest, aReply, sizeof(aReply));
435 rc = drvscsiCmdOk(pRequest);
436 break;
437 }
438 case SCSI_READ_6:
439 {
440 iTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
441 *puOffset = ((uint64_t) pRequest->pbCDB[3]
442 | (pRequest->pbCDB[2] << 8)
443 | ((pRequest->pbCDB[1] & 0x1f) << 16)) * 512;
444 *pcbToTransfer = ((uint32_t)pRequest->pbCDB[4]) * 512;
445 break;
446 }
447 case SCSI_READ_10:
448 {
449 iTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
450 *puOffset = ((uint64_t)drvscsiBE2H_U32(&pRequest->pbCDB[2])) * 512;
451 *pcbToTransfer = ((uint32_t)drvscsiBE2H_U16(&pRequest->pbCDB[7])) * 512;
452 break;
453 }
454 case SCSI_READ_12:
455 {
456 iTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
457 *puOffset = ((uint64_t)drvscsiBE2H_U32(&pRequest->pbCDB[2])) * 512;
458 *pcbToTransfer = ((uint32_t)drvscsiBE2H_U32(&pRequest->pbCDB[6])) * 512;
459 break;
460 }
461 case SCSI_READ_16:
462 {
463 iTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
464 *puOffset = drvscsiBE2H_U64(&pRequest->pbCDB[2]) * 512;
465 *pcbToTransfer = ((uint32_t)drvscsiBE2H_U32(&pRequest->pbCDB[10])) * 512;
466 break;
467 }
468 case SCSI_WRITE_6:
469 {
470 iTxDir = PDMBLOCKTXDIR_TO_DEVICE;
471 *puOffset = ((uint64_t) pRequest->pbCDB[3]
472 | (pRequest->pbCDB[2] << 8)
473 | ((pRequest->pbCDB[1] & 0x1f) << 16)) * 512;
474 *pcbToTransfer = ((uint32_t)pRequest->pbCDB[4]) * 512;
475 break;
476 }
477 case SCSI_WRITE_10:
478 {
479 iTxDir = PDMBLOCKTXDIR_TO_DEVICE;
480 *puOffset = ((uint64_t)drvscsiBE2H_U32(&pRequest->pbCDB[2])) * 512;
481 *pcbToTransfer = ((uint32_t)drvscsiBE2H_U16(&pRequest->pbCDB[7])) * 512;
482 break;
483 }
484 case SCSI_WRITE_12:
485 {
486 iTxDir = PDMBLOCKTXDIR_TO_DEVICE;
487 *puOffset = ((uint64_t)drvscsiBE2H_U32(&pRequest->pbCDB[2])) * 512;
488 *pcbToTransfer = ((uint32_t)drvscsiBE2H_U32(&pRequest->pbCDB[6])) * 512;
489 break;
490 }
491 case SCSI_WRITE_16:
492 {
493 iTxDir = PDMBLOCKTXDIR_TO_DEVICE;
494 *puOffset = drvscsiBE2H_U64(&pRequest->pbCDB[2]) * 512;
495 *pcbToTransfer = ((uint32_t)drvscsiBE2H_U32(&pRequest->pbCDB[10])) * 512;
496 break;
497 }
498 case SCSI_SYNCHRONIZE_CACHE:
499 {
500 /* @todo When async mode implemented we have to move this out here. */
501 int rc2 = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
502 AssertMsgRC(rc2, ("Flushing data failed rc=%Rrc\n", rc2));
503 break;
504 }
505 case SCSI_READ_BUFFER:
506 {
507 uint8_t uDataMode = pRequest->pbCDB[1] & 0x1f;
508
509 switch (uDataMode)
510 {
511 case 0x00:
512 case 0x01:
513 case 0x02:
514 case 0x03:
515 case 0x0a:
516 break;
517 case 0x0b:
518 {
519 uint8_t aReply[4];
520
521 /* We do not implement an echo buffer. */
522 memset(aReply, 0, sizeof(aReply));
523
524 drvscsiScatterGatherListCopyFromBuffer(pRequest, aReply, sizeof(aReply));
525 rc = drvscsiCmdOk(pRequest);
526 break;
527 }
528 case 0x1a:
529 case 0x1c:
530 break;
531 default:
532 AssertMsgFailed(("Invalid data mode\n"));
533 }
534 break;
535 }
536 case SCSI_START_STOP_UNIT:
537 {
538 /* Nothing to do. */
539 break;
540 }
541 case SCSI_LOG_SENSE:
542 {
543 uint16_t cbMax = drvscsiBE2H_U16(&pRequest->pbCDB[7]);
544 uint8_t uPageCode = pRequest->pbCDB[2] & 0x3f;
545 uint8_t uSubPageCode = pRequest->pbCDB[3];
546
547 switch (uPageCode)
548 {
549 case 0x00:
550 {
551 if (uSubPageCode == 0)
552 {
553 uint8_t aReply[4];
554
555 aReply[0] = 0;
556 aReply[1] = 0;
557 aReply[2] = 0;
558 aReply[3] = 0;
559 drvscsiScatterGatherListCopyFromBuffer(pRequest, aReply, sizeof(aReply));
560 rc = drvscsiCmdOk(pRequest);
561 break;
562 }
563 }
564 default:
565 rc = drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
566 }
567 break;
568 }
569 case SCSI_SERVICE_ACTION_IN_16:
570 {
571 switch (pRequest->pbCDB[1] & 0x1f)
572 {
573 case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
574 {
575 uint8_t aReply[32];
576
577 memset(aReply, 0, sizeof(aReply));
578 drvscsiH2BE_U64(aReply, pThis->cSectors - 1);
579 drvscsiH2BE_U32(&aReply[8], 512);
580 /* Leave the rest 0 */
581
582 drvscsiScatterGatherListCopyFromBuffer(pRequest, aReply, sizeof(aReply));
583 rc = drvscsiCmdOk(pRequest);
584 break;
585 }
586 default:
587 rc = drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); /* Don't know if this is correct */
588 }
589 break;
590 }
591 default:
592 //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
593 rc = drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
594 }
595 }
596 else
597 {
598 /* Report an error. */
599 rc = drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION);
600 }
601 break;
602 }
603 }
604
605 *piTxDir = iTxDir;
606
607 return rc;
608}
609
610static int drvscsiProcessRequestOne(PDRVSCSI pThis, PPDMSCSIREQUEST pRequest)
611{
612 int rc = VINF_SUCCESS;
613 int iTxDir;
614 int rcCompletion;
615 uint64_t uOffset = UINT64_MAX; /* initialized to shut up gcc warnings. */
616 uint32_t cbToTransfer = UINT32_MAX; /* ditto */
617 uint32_t cSegmentsLeft = UINT32_MAX; /* ditto */
618
619 LogFlowFunc(("Entered\n"));
620
621#ifdef DEBUG
622 drvscsiDumpScsiRequest(pRequest);
623#endif
624 rcCompletion = drvscsiProcessCDB(pThis, pRequest, &uOffset, &cbToTransfer, &iTxDir);
625 if ((rcCompletion == SCSI_STATUS_OK) && (iTxDir != PDMBLOCKTXDIR_NONE))
626 {
627 PPDMDATASEG pSegActual;
628
629 pSegActual = &pRequest->paScatterGatherHead[0];
630 cSegmentsLeft = pRequest->cScatterGatherEntries;
631
632 while(cbToTransfer && cSegmentsLeft)
633 {
634 uint32_t cbProcess = (cbToTransfer < pSegActual->cbSeg) ? cbToTransfer : (uint32_t)pSegActual->cbSeg;
635
636 Log(("%s: uOffset=%llu cbToTransfer=%u\n", __FUNCTION__, uOffset, cbToTransfer));
637
638 if (iTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
639 {
640 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
641 rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
642 pSegActual->pvSeg, cbProcess);
643 pThis->pLed->Actual.s.fReading = 0;
644 if (RT_FAILURE(rc))
645 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
646 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
647 }
648 else
649 {
650 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
651 rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
652 pSegActual->pvSeg, cbProcess);
653 pThis->pLed->Actual.s.fWriting = 0;
654 if (RT_FAILURE(rc))
655 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
656 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
657 }
658
659 /* Go to the next entry. */
660 uOffset += cbProcess;
661 cbToTransfer -= cbProcess;
662 pSegActual++;
663 cSegmentsLeft--;
664 }
665 AssertMsg(!cbToTransfer && !cSegmentsLeft,
666 ("Transfer incomplete cbToTransfer=%u cSegmentsLeft=%u\n", cbToTransfer, cSegmentsLeft));
667 drvscsiCmdOk(pRequest);
668 }
669
670 /* Notify device. */
671 rc = pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, pRequest, rcCompletion);
672 AssertMsgRC(rc, ("Error while notifying device rc=%Rrc\n", rc));
673
674 return rc;
675}
676
677/**
678 * Dummy request function used by drvscsiReset to wait for all pending requests
679 * to complete prior to the device reset.
680 *
681 * @param pThis Pointer to the instace data.
682 * @returns VINF_SUCCESS.
683 */
684static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
685{
686 if (pThis->fDummySignal)
687 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
688 return VINF_SUCCESS;
689}
690
691/**
692 * Request function to wakeup the thread.
693 *
694 * @param pThis Pointer to the instace data.
695 * @returns VWRN_STATE_CHANGED.
696 */
697static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
698{
699 if (pThis->fDummySignal)
700 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
701 return VWRN_STATE_CHANGED;
702}
703
704/**
705 * The thread function which processes the requests asynchronously.
706 *
707 * @returns VBox status code.
708 * @param pDrvIns Pointer to the driver instance data.
709 * @param pThread Pointer to the thread instance data.
710 */
711static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
712{
713 int rc = VINF_SUCCESS;
714 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
715
716 LogFlowFunc(("Entering async IO loop.\n"));
717
718 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
719 return VINF_SUCCESS;
720
721 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
722 {
723 rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
724 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
725 }
726
727 return VINF_SUCCESS;
728}
729
730/**
731 * Deals with any pending dummy request
732 *
733 * @returns true if no pending dummy request, false if still pending.
734 * @param pThis The instance data.
735 * @param cMillies The number of milliseconds to wait for any
736 * pending request to finish.
737 */
738static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
739{
740 if (!pThis->pPendingDummyReq)
741 return true;
742 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
743 if (RT_FAILURE(rc))
744 return false;
745 RTReqFree(pThis->pPendingDummyReq);
746 pThis->pPendingDummyReq = NULL;
747 return true;
748}
749
750static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
751{
752 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
753 PRTREQ pReq;
754 int rc;
755
756 AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
757
758 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
759 {
760 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
761 return VERR_TIMEOUT;
762 }
763
764 rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
765 if (RT_SUCCESS(rc))
766 RTReqFree(pReq);
767 else
768 {
769 pThis->pPendingDummyReq = pReq;
770 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
771 }
772
773 return rc;
774}
775
776/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
777
778/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
779static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
780{
781 int rc;
782 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
783 PRTREQ pReq;
784
785 AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
786
787 rc = RTReqCallEx(pThis->pQueueRequests, &pReq, 0, RTREQFLAGS_NO_WAIT, (PFNRT)drvscsiProcessRequestOne, 2, pThis, pSCSIRequest);
788 AssertMsgReturn(RT_SUCCESS(rc), ("Inserting request into queue failed rc=%Rrc\n", rc), rc);
789
790 return VINF_SUCCESS;
791}
792
793/* -=-=-=-=- IBase -=-=-=-=- */
794
795/** @copydoc PDMIBASE::pfnQueryInterface. */
796static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
797{
798 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
799 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
800 switch (enmInterface)
801 {
802 case PDMINTERFACE_BASE:
803 return &pDrvIns->IBase;
804 case PDMINTERFACE_SCSI_CONNECTOR:
805 return &pThis->ISCSIConnector;
806 case PDMINTERFACE_BLOCK_PORT:
807 return &pThis->IPort;
808 default:
809 return NULL;
810 }
811}
812
813/**
814 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
815 *
816 * @param pDrvIns The driver instance.
817 * @param pfnAsyncNotify The async callback.
818 */
819static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
820{
821 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
822
823 if (!pThis->pQueueRequests)
824 return;
825
826 ASMAtomicWriteBool(&pThis->fDummySignal, true);
827 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
828 {
829 if (!RTReqIsBusy(pThis->pQueueRequests))
830 {
831 ASMAtomicWriteBool(&pThis->fDummySignal, false);
832 return;
833 }
834
835 PRTREQ pReq;
836 int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
837 if (RT_SUCCESS(rc))
838 {
839 ASMAtomicWriteBool(&pThis->fDummySignal, false);
840 RTReqFree(pReq);
841 return;
842 }
843
844 pThis->pPendingDummyReq = pReq;
845 }
846 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
847}
848
849/**
850 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
851 *
852 * @returns true if we've quiesced, false if we're still working.
853 * @param pDrvIns The driver instance.
854 */
855static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
856{
857 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
858
859 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
860 return false;
861 ASMAtomicWriteBool(&pThis->fDummySignal, false);
862 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
863 return true;
864}
865
866/**
867 * @copydoc FNPDMDRVPOWEROFF
868 */
869static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
870{
871 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
872}
873
874/**
875 * @copydoc FNPDMDRVSUSPEND
876 */
877static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
878{
879 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
880}
881
882/**
883 * Callback employed by drvscsiReset.
884 *
885 * @returns true if we've quiesced, false if we're still working.
886 * @param pDrvIns The driver instance.
887 */
888static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
889{
890 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
891
892 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
893 return false;
894 ASMAtomicWriteBool(&pThis->fDummySignal, false);
895 return true;
896}
897
898/**
899 * @copydoc FNPDMDRVRESET
900 */
901static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
902{
903 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
904}
905
906/**
907 * Destruct a driver instance.
908 *
909 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
910 * resources can be freed correctly.
911 *
912 * @param pDrvIns The driver instance data.
913 */
914static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
915{
916 int rc;
917 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
918
919 if (pThis->pQueueRequests)
920 {
921 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
922 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
923
924 rc = RTReqDestroyQueue(pThis->pQueueRequests);
925 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
926 }
927
928}
929
930/**
931 * Construct a block driver instance.
932 *
933 * @copydoc FNPDMDRVCONSTRUCT
934 */
935static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
936{
937 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
938
939 LogFlowFunc(("pDrvIns=%#p pCfgHandle=%#p\n", pDrvIns, pCfgHandle));
940
941 /*
942 * Initialize the instance data.
943 */
944 pThis->pDrvIns = pDrvIns;
945 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
946
947 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
948
949 /*
950 * Try attach driver below and query it's block interface.
951 */
952 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
953 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
954
955 /*
956 * Query the block and blockbios interfaces.
957 */
958 pThis->pDrvBlock = (PDMIBLOCK *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK);
959 if (!pThis->pDrvBlock)
960 {
961 AssertMsgFailed(("Configuration error: No block interface!\n"));
962 return VERR_PDM_MISSING_INTERFACE;
963 }
964 pThis->pDrvBlockBios = (PDMIBLOCKBIOS *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK_BIOS);
965 if (!pThis->pDrvBlockBios)
966 {
967 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
968 return VERR_PDM_MISSING_INTERFACE;
969 }
970
971 /* Query the SCSI port interface above. */
972 pThis->pDevScsiPort = (PPDMISCSIPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_SCSI_PORT);
973 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
974
975 pThis->pDrvMount = (PDMIMOUNT *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_MOUNT);
976
977 /* Query the optional LED interface above. */
978 pThis->pLedPort = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
979 if (pThis->pLedPort != NULL)
980 {
981 /* Get The Led. */
982 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
983 if (RT_FAILURE(rc))
984 pThis->pLed = &pThis->Led;
985 }
986 else
987 pThis->pLed = &pThis->Led;
988
989 /* Try to get the optional async block interface. */
990 pThis->pDrvBlockAsync = (PDMIBLOCKASYNC *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK_ASYNC);
991
992 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
993 if (enmType != PDMBLOCKTYPE_HARD_DISK)
994 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
995 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
996 enmType);
997 pThis->enmType = enmType;
998 pThis->cSectors = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock) / 512;
999
1000 /* Create request queue. */
1001 rc = RTReqCreateQueue(&pThis->pQueueRequests);
1002 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
1003
1004 /* Register statistics counter. */
1005 /** @todo aeichner: Find a way to put the instance number of the attached
1006 * controller device when we support more than one controller of the same type.
1007 * At the moment we have the 0 hardcoded. */
1008 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1009 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
1010 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1011 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
1012
1013 /* Create I/O thread. */
1014 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
1015 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
1016 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
1017
1018 return VINF_SUCCESS;
1019}
1020
1021/**
1022 * SCSI driver registration record.
1023 */
1024const PDMDRVREG g_DrvSCSI =
1025{
1026 /* u32Version */
1027 PDM_DRVREG_VERSION,
1028 /* szDriverName */
1029 "SCSI",
1030 /* szRCMod */
1031 "",
1032 /* szR0Mod */
1033 "",
1034 /* pszDescription */
1035 "Generic SCSI driver.",
1036 /* fFlags */
1037 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1038 /* fClass. */
1039 PDM_DRVREG_CLASS_SCSI,
1040 /* cMaxInstances */
1041 ~0,
1042 /* cbInstance */
1043 sizeof(DRVSCSI),
1044 /* pfnConstruct */
1045 drvscsiConstruct,
1046 /* pfnDestruct */
1047 drvscsiDestruct,
1048 /* pfnRelocate */
1049 NULL,
1050 /* pfnIOCtl */
1051 NULL,
1052 /* pfnPowerOn */
1053 NULL,
1054 /* pfnReset */
1055 drvscsiReset,
1056 /* pfnSuspend */
1057 drvscsiSuspend,
1058 /* pfnResume */
1059 NULL,
1060 /* pfnAttach */
1061 NULL,
1062 /* pfnDetach */
1063 NULL,
1064 /* pfnPowerOff */
1065 drvscsiPowerOff,
1066 /* pfnSoftReset */
1067 NULL,
1068 /* u32EndVersion */
1069 PDM_DRVREG_VERSION
1070};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette