VirtualBox

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

最後變更 在這個檔案從24242是 24242,由 vboxsync 提交於 15 年 前

drvscsiAsyncIOLoopNoPendingDummy: fixed true/false mixup causing trouble on pause and power off.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 37.9 KB
 
1/* $Id: DrvSCSI.cpp 24242 2009-11-02 10:17:38Z 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.alldomusa.eu.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 true;
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 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
894 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
895
896 rc = RTReqDestroyQueue(pThis->pQueueRequests);
897 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
898 }
899
900}
901
902/**
903 * Construct a block driver instance.
904 *
905 * @copydoc FNPDMDRVCONSTRUCT
906 */
907static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
908{
909 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
910
911 LogFlowFunc(("pDrvIns=%#p pCfgHandle=%#p\n", pDrvIns, pCfgHandle));
912
913 /*
914 * Initialize the instance data.
915 */
916 pThis->pDrvIns = pDrvIns;
917 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
918
919 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
920
921 /*
922 * Try attach driver below and query it's block interface.
923 */
924 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
925 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
926
927 /*
928 * Query the block and blockbios interfaces.
929 */
930 pThis->pDrvBlock = (PDMIBLOCK *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK);
931 if (!pThis->pDrvBlock)
932 {
933 AssertMsgFailed(("Configuration error: No block interface!\n"));
934 return VERR_PDM_MISSING_INTERFACE;
935 }
936 pThis->pDrvBlockBios = (PDMIBLOCKBIOS *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK_BIOS);
937 if (!pThis->pDrvBlockBios)
938 {
939 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
940 return VERR_PDM_MISSING_INTERFACE;
941 }
942
943 /* Query the SCSI port interface above. */
944 pThis->pDevScsiPort = (PPDMISCSIPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_SCSI_PORT);
945 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
946
947 pThis->pDrvMount = (PDMIMOUNT *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_MOUNT);
948
949 /* Query the optional LED interface above. */
950 pThis->pLedPort = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
951 if (pThis->pLedPort != NULL)
952 {
953 /* Get The Led. */
954 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
955 if (RT_FAILURE(rc))
956 pThis->pLed = &pThis->Led;
957 }
958 else
959 pThis->pLed = &pThis->Led;
960
961 /* Try to get the optional async block interface. */
962 pThis->pDrvBlockAsync = (PDMIBLOCKASYNC *)pThis->pDrvBase->pfnQueryInterface(pThis->pDrvBase, PDMINTERFACE_BLOCK_ASYNC);
963
964 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
965 if (enmType != PDMBLOCKTYPE_HARD_DISK)
966 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
967 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
968 enmType);
969 pThis->enmType = enmType;
970 pThis->cSectors = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock) / 512;
971
972 /* Create request queue. */
973 rc = RTReqCreateQueue(&pThis->pQueueRequests);
974 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
975
976 /* Register statistics counter. */
977 /** @todo aeichner: Find a way to put the instance number of the attached
978 * controller device when we support more than one controller of the same type.
979 * At the moment we have the 0 hardcoded. */
980 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
981 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
982 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
983 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
984
985 /* Create I/O thread. */
986 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
987 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
988 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
989
990 return VINF_SUCCESS;
991}
992
993/**
994 * SCSI driver registration record.
995 */
996const PDMDRVREG g_DrvSCSI =
997{
998 /* u32Version */
999 PDM_DRVREG_VERSION,
1000 /* szDriverName */
1001 "SCSI",
1002 /* pszDescription */
1003 "Generic SCSI driver.",
1004 /* fFlags */
1005 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1006 /* fClass. */
1007 PDM_DRVREG_CLASS_SCSI,
1008 /* cMaxInstances */
1009 ~0,
1010 /* cbInstance */
1011 sizeof(DRVSCSI),
1012 /* pfnConstruct */
1013 drvscsiConstruct,
1014 /* pfnDestruct */
1015 drvscsiDestruct,
1016 /* pfnIOCtl */
1017 NULL,
1018 /* pfnPowerOn */
1019 NULL,
1020 /* pfnReset */
1021 drvscsiReset,
1022 /* pfnSuspend */
1023 drvscsiSuspend,
1024 /* pfnResume */
1025 NULL,
1026 /* pfnAttach */
1027 NULL,
1028 /* pfnDetach */
1029 NULL,
1030 /* pfnPowerOff */
1031 drvscsiPowerOff,
1032 /* pfnSoftReset */
1033 NULL,
1034 /* u32EndVersion */
1035 PDM_DRVREG_VERSION
1036};
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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