VirtualBox

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

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

DrvSCSI: Need to synchronize all pending requests before any driver below us gets the PowerOff or Suspend notification. DrvVD will change the image to read-only mode.

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

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