VirtualBox

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

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

PDM: s/pCfgHandle/pCfg/g - part 2.

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