VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase.cpp@ 5126

Last change on this file since 5126 was 5126, checked in by vboxsync, 17 years ago

Solaris

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.5 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * Host base drive access driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
24#ifdef RT_OS_DARWIN
25# include <mach/mach.h>
26# include <Carbon/Carbon.h>
27# include <IOKit/IOKitLib.h>
28# include <IOKit/storage/IOStorageDeviceCharacteristics.h>
29# include <IOKit/scsi-commands/SCSITaskLib.h>
30# include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
31# include <IOKit/IOBSD.h>
32# include <DiskArbitration/DiskArbitration.h>
33# include <mach/mach_error.h>
34# include <VBox/scsi.h>
35
36#elif defined(RT_OS_L4)
37 /* Nothing special requires... yeah, right. */
38
39#elif defined(RT_OS_LINUX)
40# include <sys/ioctl.h>
41# include <sys/fcntl.h>
42# include <errno.h>
43
44#elif defined(RT_OS_SOLARIS)
45# include <fcntl.h>
46# include <errno.h>
47
48#elif defined(RT_OS_WINDOWS)
49# define WIN32_NO_STATUS
50# include <Windows.h>
51# include <dbt.h>
52# undef WIN32_NO_STATUS
53# include <ntstatus.h>
54
55/* from ntdef.h */
56typedef LONG NTSTATUS;
57
58/* from ntddk.h */
59typedef struct _IO_STATUS_BLOCK {
60 union {
61 NTSTATUS Status;
62 PVOID Pointer;
63 };
64 ULONG_PTR Information;
65} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
66
67
68/* from ntinternals.com */
69typedef enum _FS_INFORMATION_CLASS {
70 FileFsVolumeInformation=1,
71 FileFsLabelInformation,
72 FileFsSizeInformation,
73 FileFsDeviceInformation,
74 FileFsAttributeInformation,
75 FileFsControlInformation,
76 FileFsFullSizeInformation,
77 FileFsObjectIdInformation,
78 FileFsMaximumInformation
79} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
80
81typedef struct _FILE_FS_SIZE_INFORMATION {
82 LARGE_INTEGER TotalAllocationUnits;
83 LARGE_INTEGER AvailableAllocationUnits;
84 ULONG SectorsPerAllocationUnit;
85 ULONG BytesPerSector;
86} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
87
88extern "C"
89NTSTATUS __stdcall NtQueryVolumeInformationFile(
90 /*IN*/ HANDLE FileHandle,
91 /*OUT*/ PIO_STATUS_BLOCK IoStatusBlock,
92 /*OUT*/ PVOID FileSystemInformation,
93 /*IN*/ ULONG Length,
94 /*IN*/ FS_INFORMATION_CLASS FileSystemInformationClass );
95
96#else
97# error "Unsupported Platform."
98#endif
99
100#include <VBox/pdmdrv.h>
101#include <iprt/assert.h>
102#include <iprt/file.h>
103#include <iprt/path.h>
104#include <iprt/string.h>
105#include <iprt/thread.h>
106#include <iprt/semaphore.h>
107#include <iprt/uuid.h>
108#include <iprt/asm.h>
109#include <iprt/critsect.h>
110#include <iprt/ctype.h>
111
112#include "DrvHostBase.h"
113
114
115
116
117/* -=-=-=-=- IBlock -=-=-=-=- */
118
119/** @copydoc PDMIBLOCK::pfnRead */
120static DECLCALLBACK(int) drvHostBaseRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
121{
122 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
123 LogFlow(("%s-%d: drvHostBaseRead: off=%#llx pvBuf=%p cbRead=%#x (%s)\n",
124 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, pvBuf, cbRead, pThis->pszDevice));
125 RTCritSectEnter(&pThis->CritSect);
126
127 /*
128 * Check the state.
129 */
130 int rc;
131#ifdef RT_OS_DARWIN
132 if ( pThis->fMediaPresent
133 && pThis->ppScsiTaskDI
134 && pThis->cbBlock)
135#else
136 if (pThis->fMediaPresent)
137#endif
138 {
139#ifdef RT_OS_DARWIN
140 /*
141 * Issue a READ(12) request.
142 */
143 const uint32_t LBA = off / pThis->cbBlock;
144 AssertReturn(!(off % pThis->cbBlock), VERR_INVALID_PARAMETER);
145 const uint32_t cBlocks = cbRead / pThis->cbBlock;
146 AssertReturn(!(cbRead % pThis->cbBlock), VERR_INVALID_PARAMETER);
147 uint8_t abCmd[16] =
148 {
149 SCSI_READ_12, 0,
150 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
151 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
152 0, 0, 0, 0, 0
153 };
154 rc = DRVHostBaseScsiCmd(pThis, abCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, &cbRead, NULL, 0, 0);
155
156#else
157 /*
158 * Seek and read.
159 */
160 rc = RTFileSeek(pThis->FileDevice, off, RTFILE_SEEK_BEGIN, NULL);
161 if (VBOX_SUCCESS(rc))
162 {
163 rc = RTFileRead(pThis->FileDevice, pvBuf, cbRead, NULL);
164 if (VBOX_SUCCESS(rc))
165 {
166 Log2(("%s-%d: drvHostBaseRead: off=%#llx cbRead=%#x\n"
167 "%16.*Vhxd\n",
168 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, cbRead, cbRead, pvBuf));
169 }
170 else
171 Log(("%s-%d: drvHostBaseRead: RTFileRead(%d, %p, %#x) -> %Vrc (off=%#llx '%s')\n",
172 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->FileDevice,
173 pvBuf, cbRead, rc, off, pThis->pszDevice));
174 }
175 else
176 Log(("%s-%d: drvHostBaseRead: RTFileSeek(%d,%#llx,) -> %Vrc\n", pThis->pDrvIns->pDrvReg->szDriverName,
177 pThis->pDrvIns->iInstance, pThis->FileDevice, off, rc));
178#endif
179 }
180 else
181 rc = VERR_MEDIA_NOT_PRESENT;
182
183 RTCritSectLeave(&pThis->CritSect);
184 LogFlow(("%s-%d: drvHostBaseRead: returns %Vrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
185 return rc;
186}
187
188
189/** @copydoc PDMIBLOCK::pfnWrite */
190static DECLCALLBACK(int) drvHostBaseWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
191{
192 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
193 LogFlow(("%s-%d: drvHostBaseWrite: off=%#llx pvBuf=%p cbWrite=%#x (%s)\n",
194 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, pvBuf, cbWrite, pThis->pszDevice));
195 Log2(("%s-%d: drvHostBaseWrite: off=%#llx cbWrite=%#x\n"
196 "%16.*Vhxd\n",
197 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, off, cbWrite, cbWrite, pvBuf));
198 RTCritSectEnter(&pThis->CritSect);
199
200 /*
201 * Check the state.
202 */
203 int rc;
204 if (!pThis->fReadOnly)
205 {
206 if (pThis->fMediaPresent)
207 {
208#ifdef RT_OS_DARWIN
209 /** @todo write support... */
210 rc = VERR_WRITE_PROTECT;
211
212#else
213 /*
214 * Seek and write.
215 */
216 rc = RTFileSeek(pThis->FileDevice, off, RTFILE_SEEK_BEGIN, NULL);
217 if (VBOX_SUCCESS(rc))
218 {
219 rc = RTFileWrite(pThis->FileDevice, pvBuf, cbWrite, NULL);
220 if (VBOX_FAILURE(rc))
221 Log(("%s-%d: drvHostBaseWrite: RTFileWrite(%d, %p, %#x) -> %Vrc (off=%#llx '%s')\n",
222 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->FileDevice,
223 pvBuf, cbWrite, rc, off, pThis->pszDevice));
224 }
225 else
226 Log(("%s-%d: drvHostBaseWrite: RTFileSeek(%d,%#llx,) -> %Vrc\n",
227 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->FileDevice, off, rc));
228#endif
229 }
230 else
231 rc = VERR_MEDIA_NOT_PRESENT;
232 }
233 else
234 rc = VERR_WRITE_PROTECT;
235
236 RTCritSectLeave(&pThis->CritSect);
237 LogFlow(("%s-%d: drvHostBaseWrite: returns %Vrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
238 return rc;
239}
240
241
242/** @copydoc PDMIBLOCK::pfnFlush */
243static DECLCALLBACK(int) drvHostBaseFlush(PPDMIBLOCK pInterface)
244{
245 int rc;
246 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
247 LogFlow(("%s-%d: drvHostBaseFlush: (%s)\n",
248 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDevice));
249 RTCritSectEnter(&pThis->CritSect);
250
251 if (pThis->fMediaPresent)
252 {
253#ifdef RT_OS_DARWIN
254 rc = VINF_SUCCESS;
255 /** @todo scsi device buffer flush... */
256#else
257 rc = RTFileFlush(pThis->FileDevice);
258#endif
259 }
260 else
261 rc = VERR_MEDIA_NOT_PRESENT;
262
263 RTCritSectLeave(&pThis->CritSect);
264 LogFlow(("%s-%d: drvHostBaseFlush: returns %Vrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
265 return rc;
266}
267
268
269/** @copydoc PDMIBLOCK::pfnIsReadOnly */
270static DECLCALLBACK(bool) drvHostBaseIsReadOnly(PPDMIBLOCK pInterface)
271{
272 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
273 return pThis->fReadOnly;
274}
275
276
277/** @copydoc PDMIBLOCK::pfnGetSize */
278static DECLCALLBACK(uint64_t) drvHostBaseGetSize(PPDMIBLOCK pInterface)
279{
280 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
281 RTCritSectEnter(&pThis->CritSect);
282
283 uint64_t cb = 0;
284 if (pThis->fMediaPresent)
285 cb = pThis->cbSize;
286
287 RTCritSectLeave(&pThis->CritSect);
288 LogFlow(("%s-%d: drvHostBaseGetSize: returns %llu\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, cb));
289 return cb;
290}
291
292
293/** @copydoc PDMIBLOCK::pfnGetType */
294static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseGetType(PPDMIBLOCK pInterface)
295{
296 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
297 LogFlow(("%s-%d: drvHostBaseGetType: returns %d\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->enmType));
298 return pThis->enmType;
299}
300
301
302/** @copydoc PDMIBLOCK::pfnGetUuid */
303static DECLCALLBACK(int) drvHostBaseGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
304{
305 PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
306
307 *pUuid = pThis->Uuid;
308
309 LogFlow(("%s-%d: drvHostBaseGetUuid: returns VINF_SUCCESS *pUuid=%Vuuid\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pUuid));
310 return VINF_SUCCESS;
311}
312
313
314/* -=-=-=-=- IBlockBios -=-=-=-=- */
315
316/** Makes a PDRVHOSTBASE out of a PPDMIBLOCKBIOS. */
317#define PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface) ( (PDRVHOSTBASE((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTBASE, IBlockBios))) )
318
319
320/** @copydoc PDMIBLOCKBIOS::pfnGetGeometry */
321static DECLCALLBACK(int) drvHostBaseGetGeometry(PPDMIBLOCKBIOS pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
322{
323 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
324 RTCritSectEnter(&pThis->CritSect);
325
326 int rc = VINF_SUCCESS;
327 if (pThis->fMediaPresent)
328 {
329 if ( pThis->cCylinders > 0
330 && pThis->cHeads > 0
331 && pThis->cSectors > 0)
332 {
333 *pcCylinders = pThis->cCylinders;
334 *pcHeads = pThis->cHeads;
335 *pcSectors = pThis->cSectors;
336 }
337 else
338 rc = VERR_PDM_GEOMETRY_NOT_SET;
339 }
340 else
341 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
342
343 RTCritSectLeave(&pThis->CritSect);
344 LogFlow(("%s-%d: drvHostBaseGetGeometry: returns %Vrc CHS={%d,%d,%d}\n",
345 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc, *pcCylinders, *pcHeads, *pcSectors));
346 return rc;
347}
348
349
350/** @copydoc PDMIBLOCKBIOS::pfnSetGeometry */
351static DECLCALLBACK(int) drvHostBaseSetGeometry(PPDMIBLOCKBIOS pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
352{
353 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
354 LogFlow(("%s-%d: drvHostBaseSetGeometry: cCylinders=%d cHeads=%d cSectors=%d\n",
355 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, cCylinders, cHeads, cSectors));
356 RTCritSectEnter(&pThis->CritSect);
357
358 int rc = VINF_SUCCESS;
359 if (pThis->fMediaPresent)
360 {
361 pThis->cCylinders = cCylinders;
362 pThis->cHeads = cHeads;
363 pThis->cSectors = cSectors;
364 }
365 else
366 {
367 AssertMsgFailed(("Invalid state! Not mounted!\n"));
368 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
369 }
370
371 RTCritSectLeave(&pThis->CritSect);
372 return rc;
373}
374
375
376/** @copydoc PDMIBLOCKBIOS::pfnGetTranslation */
377static DECLCALLBACK(int) drvHostBaseGetTranslation(PPDMIBLOCKBIOS pInterface, PPDMBIOSTRANSLATION penmTranslation)
378{
379 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
380 RTCritSectEnter(&pThis->CritSect);
381
382 int rc = VINF_SUCCESS;
383 if (pThis->fMediaPresent)
384 {
385 if (pThis->fTranslationSet)
386 *penmTranslation = pThis->enmTranslation;
387 else
388 rc = VERR_PDM_TRANSLATION_NOT_SET;
389 }
390 else
391 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
392
393 RTCritSectLeave(&pThis->CritSect);
394 LogFlow(("%s-%d: drvHostBaseGetTranslation: returns %Vrc *penmTranslation=%d\n",
395 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc, *penmTranslation));
396 return rc;
397}
398
399
400/** @copydoc PDMIBLOCKBIOS::pfnSetTranslation */
401static DECLCALLBACK(int) drvHostBaseSetTranslation(PPDMIBLOCKBIOS pInterface, PDMBIOSTRANSLATION enmTranslation)
402{
403 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
404 LogFlow(("%s-%d: drvHostBaseSetTranslation: enmTranslation=%d\n",
405 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, enmTranslation));
406 RTCritSectEnter(&pThis->CritSect);
407
408 int rc = VINF_SUCCESS;
409 if (pThis->fMediaPresent)
410 {
411 pThis->fTranslationSet = true;
412 pThis->enmTranslation = enmTranslation;
413 }
414 else
415 {
416 AssertMsgFailed(("Invalid state! Not mounted!\n"));
417 rc = VERR_PDM_MEDIA_NOT_MOUNTED;
418 }
419
420 RTCritSectLeave(&pThis->CritSect);
421 return rc;
422}
423
424
425/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
426static DECLCALLBACK(bool) drvHostBaseIsVisible(PPDMIBLOCKBIOS pInterface)
427{
428 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
429 return pThis->fBiosVisible;
430}
431
432
433/** @copydoc PDMIBLOCKBIOS::pfnGetType */
434static DECLCALLBACK(PDMBLOCKTYPE) drvHostBaseBiosGetType(PPDMIBLOCKBIOS pInterface)
435{
436 PDRVHOSTBASE pThis = PDMIBLOCKBIOS_2_DRVHOSTBASE(pInterface);
437 return pThis->enmType;
438}
439
440
441
442/* -=-=-=-=- IMount -=-=-=-=- */
443
444/** @copydoc PDMIMOUNT::pfnMount */
445static DECLCALLBACK(int) drvHostBaseMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
446{
447 /* We're not mountable. */
448 AssertMsgFailed(("drvHostBaseMount: This shouldn't be called!\n"));
449 return VERR_PDM_MEDIA_MOUNTED;
450}
451
452
453/** @copydoc PDMIMOUNT::pfnUnmount */
454static DECLCALLBACK(int) drvHostBaseUnmount(PPDMIMOUNT pInterface, bool fForce)
455{
456 LogFlow(("drvHostBaseUnmount: returns VERR_NOT_SUPPORTED\n"));
457 return VERR_NOT_SUPPORTED;
458}
459
460
461/** @copydoc PDMIMOUNT::pfnIsMounted */
462static DECLCALLBACK(bool) drvHostBaseIsMounted(PPDMIMOUNT pInterface)
463{
464 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
465 RTCritSectEnter(&pThis->CritSect);
466
467 bool fRc = pThis->fMediaPresent;
468
469 RTCritSectLeave(&pThis->CritSect);
470 return fRc;
471}
472
473
474/** @copydoc PDMIMOUNT::pfnIsLocked */
475static DECLCALLBACK(int) drvHostBaseLock(PPDMIMOUNT pInterface)
476{
477 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
478 RTCritSectEnter(&pThis->CritSect);
479
480 int rc = VINF_SUCCESS;
481 if (!pThis->fLocked)
482 {
483 if (pThis->pfnDoLock)
484 rc = pThis->pfnDoLock(pThis, true);
485 if (VBOX_SUCCESS(rc))
486 pThis->fLocked = true;
487 }
488 else
489 LogFlow(("%s-%d: drvHostBaseLock: already locked\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
490
491 RTCritSectLeave(&pThis->CritSect);
492 LogFlow(("%s-%d: drvHostBaseLock: returns %Vrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
493 return rc;
494}
495
496
497/** @copydoc PDMIMOUNT::pfnIsLocked */
498static DECLCALLBACK(int) drvHostBaseUnlock(PPDMIMOUNT pInterface)
499{
500 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
501 RTCritSectEnter(&pThis->CritSect);
502
503 int rc = VINF_SUCCESS;
504 if (pThis->fLocked)
505 {
506 if (pThis->pfnDoLock)
507 rc = pThis->pfnDoLock(pThis, false);
508 if (VBOX_SUCCESS(rc))
509 pThis->fLocked = false;
510 }
511 else
512 LogFlow(("%s-%d: drvHostBaseUnlock: not locked\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
513
514 RTCritSectLeave(&pThis->CritSect);
515 LogFlow(("%s-%d: drvHostBaseUnlock: returns %Vrc\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, rc));
516 return rc;
517}
518
519
520/** @copydoc PDMIMOUNT::pfnIsLocked */
521static DECLCALLBACK(bool) drvHostBaseIsLocked(PPDMIMOUNT pInterface)
522{
523 PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
524 RTCritSectEnter(&pThis->CritSect);
525
526 bool fRc = pThis->fLocked;
527
528 RTCritSectLeave(&pThis->CritSect);
529 return fRc;
530}
531
532
533/* -=-=-=-=- IBase -=-=-=-=- */
534
535/** @copydoc PDMIBASE::pfnQueryInterface. */
536static DECLCALLBACK(void *) drvHostBaseQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
537{
538 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
539 PDRVHOSTBASE pThis = PDMINS2DATA(pDrvIns, PDRVHOSTBASE);
540 switch (enmInterface)
541 {
542 case PDMINTERFACE_BASE:
543 return &pDrvIns->IBase;
544 case PDMINTERFACE_BLOCK:
545 return &pThis->IBlock;
546 case PDMINTERFACE_BLOCK_BIOS:
547 return pThis->fBiosVisible ? &pThis->IBlockBios : NULL;
548 case PDMINTERFACE_MOUNT:
549 return &pThis->IMount;
550 default:
551 return NULL;
552 }
553}
554
555
556/* -=-=-=-=- poller thread -=-=-=-=- */
557
558#ifdef RT_OS_DARWIN
559/** The runloop input source name for the disk arbitration events. */
560#define MY_RUN_LOOP_MODE CFSTR("drvHostBaseDA")
561
562/**
563 * Gets the BSD Name (/dev/disc[0-9]+) for the service.
564 *
565 * This is done by recursing down the I/O registry until we hit upon an entry
566 * with a BSD Name. Usually we find it two levels down. (Further down under
567 * the IOCDPartitionScheme, the volume (slices) BSD Name is found. We don't
568 * seem to have to go this far fortunately.)
569 *
570 * @return VINF_SUCCESS if found, VERR_FILE_NOT_FOUND otherwise.
571 * @param Entry The current I/O registry entry reference.
572 * @param pszName Where to store the name. 128 bytes.
573 * @param cRecursions Number of recursions. This is used as an precation
574 * just to limit the depth and avoid blowing the stack
575 * should we hit a bug or something.
576 */
577static int drvHostBaseGetBSDName(io_registry_entry_t Entry, char *pszName, unsigned cRecursions)
578{
579 int rc = VERR_FILE_NOT_FOUND;
580 io_iterator_t Children = 0;
581 kern_return_t krc = IORegistryEntryGetChildIterator(Entry, kIOServicePlane, &Children);
582 if (krc == KERN_SUCCESS)
583 {
584 io_object_t Child;
585 while ( rc == VERR_FILE_NOT_FOUND
586 && (Child = IOIteratorNext(Children)) != 0)
587 {
588 CFStringRef BSDNameStrRef = (CFStringRef)IORegistryEntryCreateCFProperty(Child, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
589 if (BSDNameStrRef)
590 {
591 if (CFStringGetCString(BSDNameStrRef, pszName, 128, kCFStringEncodingUTF8))
592 rc = VINF_SUCCESS;
593 else
594 AssertFailed();
595 CFRelease(BSDNameStrRef);
596 }
597 if (rc == VERR_FILE_NOT_FOUND && cRecursions < 10)
598 rc = drvHostBaseGetBSDName(Child, pszName, cRecursions + 1);
599 IOObjectRelease(Child);
600 }
601 IOObjectRelease(Children);
602 }
603 return rc;
604}
605
606
607/**
608 * Callback notifying us that the async DADiskClaim()/DADiskUnmount call has completed.
609 *
610 * @param DiskRef The disk that was attempted claimed / unmounted.
611 * @param DissenterRef NULL on success, contains details on failure.
612 * @param pvContext Pointer to the return code variable.
613 */
614static void drvHostBaseDADoneCallback(DADiskRef DiskRef, DADissenterRef DissenterRef, void *pvContext)
615{
616 int *prc = (int *)pvContext;
617 if (!DissenterRef)
618 *prc = 0;
619 else
620 *prc = DADissenterGetStatus(DissenterRef) ? DADissenterGetStatus(DissenterRef) : -1;
621 CFRunLoopStop(CFRunLoopGetCurrent());
622}
623
624
625/**
626 * Obtain exclusive access to the DVD device, umount it if necessary.
627 *
628 * @return VBox status code.
629 * @param pThis The driver instance.
630 * @param DVDService The DVD service object.
631 */
632static int drvHostBaseObtainExclusiveAccess(PDRVHOSTBASE pThis, io_object_t DVDService)
633{
634 PPDMDRVINS pDrvIns = pThis->pDrvIns; NOREF(pDrvIns);
635
636 for (unsigned iTry = 0;; iTry++)
637 {
638 IOReturn irc = (*pThis->ppScsiTaskDI)->ObtainExclusiveAccess(pThis->ppScsiTaskDI);
639 if (irc == kIOReturnSuccess)
640 {
641 /*
642 * This is a bit weird, but if we unmounted the DVD drive we also need to
643 * unlock it afterwards or the guest won't be able to eject it later on.
644 */
645 if (pThis->pDADisk)
646 {
647 uint8_t abCmd[16] =
648 {
649 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, false, 0,
650 0,0,0,0,0,0,0,0,0,0
651 };
652 DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
653 }
654 return VINF_SUCCESS;
655 }
656 if (irc == kIOReturnExclusiveAccess)
657 return VERR_SHARING_VIOLATION; /* already used exclusivly. */
658 if (irc != kIOReturnBusy)
659 return VERR_GENERAL_FAILURE; /* not mounted */
660
661 /*
662 * Attempt to the unmount all volumes of the device.
663 * It seems we can can do this all in one go without having to enumerate the
664 * volumes (sessions) and deal with them one by one. This is very fortuitous
665 * as the disk arbitration API is a bit cumbersome to deal with.
666 */
667 if (iTry > 2)
668 return VERR_DRIVE_LOCKED;
669 char szName[128];
670 int rc = drvHostBaseGetBSDName(DVDService, &szName[0], 0);
671 if (VBOX_SUCCESS(rc))
672 {
673 pThis->pDASession = DASessionCreate(kCFAllocatorDefault);
674 if (pThis->pDASession)
675 {
676 DASessionScheduleWithRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
677 pThis->pDADisk = DADiskCreateFromBSDName(kCFAllocatorDefault, pThis->pDASession, szName);
678 if (pThis->pDADisk)
679 {
680 /*
681 * Try claim the device.
682 */
683 Log(("%s-%d: calling DADiskClaim on '%s'.\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, szName));
684 int rcDA = -2;
685 DADiskClaim(pThis->pDADisk, kDADiskClaimOptionDefault, NULL, NULL, drvHostBaseDADoneCallback, &rcDA);
686 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
687 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
688 if ( rc32 == kCFRunLoopRunStopped
689 && !rcDA)
690 {
691 /*
692 * Try unmount the device.
693 */
694 Log(("%s-%d: calling DADiskUnmount on '%s'.\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, szName));
695 rcDA = -2;
696 DADiskUnmount(pThis->pDADisk, kDADiskUnmountOptionWhole, drvHostBaseDADoneCallback, &rcDA);
697 SInt32 rc32 = CFRunLoopRunInMode(MY_RUN_LOOP_MODE, 120.0, FALSE);
698 AssertMsg(rc32 == kCFRunLoopRunStopped, ("rc32=%RI32 (%RX32)\n", rc32, rc32));
699 if ( rc32 == kCFRunLoopRunStopped
700 && !rcDA)
701 {
702 iTry = 99;
703 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
704 Log(("%s-%d: unmount succeed - retrying.\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
705 continue;
706 }
707 Log(("%s-%d: umount => rc32=%d & rcDA=%#x\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, rc32, rcDA));
708
709 /* failed - cleanup */
710 DADiskUnclaim(pThis->pDADisk);
711 }
712 else
713 Log(("%s-%d: claim => rc32=%d & rcDA=%#x\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, rc32, rcDA));
714
715 CFRelease(pThis->pDADisk);
716 pThis->pDADisk = NULL;
717 }
718 else
719 Log(("%s-%d: failed to open disk '%s'!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, szName));
720
721 DASessionUnscheduleFromRunLoop(pThis->pDASession, CFRunLoopGetCurrent(), MY_RUN_LOOP_MODE);
722 CFRelease(pThis->pDASession);
723 pThis->pDASession = NULL;
724 }
725 else
726 Log(("%s-%d: failed to create DA session!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
727 }
728 RTThreadSleep(10);
729 }
730}
731#endif /* RT_OS_DARWIN */
732
733
734/**
735 * Wrapper for open / RTFileOpen / IOKit.
736 *
737 * @remark The Darwin code must correspond exactly to the enumeration
738 * done in Main/darwin/iokit.c.
739 */
740static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileDevice, bool fReadOnly)
741{
742#ifdef RT_OS_DARWIN
743 /* Darwin is kind of special... */
744 Assert(!pFileDevice); NOREF(pFileDevice);
745 Assert(!pThis->cbBlock);
746 Assert(!pThis->MasterPort);
747 Assert(!pThis->ppMMCDI);
748 Assert(!pThis->ppScsiTaskDI);
749
750 /*
751 * Open the master port on the first invocation.
752 */
753 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &pThis->MasterPort);
754 AssertReturn(krc == KERN_SUCCESS, VERR_GENERAL_FAILURE);
755
756 /*
757 * Create a matching dictionary for searching for DVD services in the IOKit.
758 *
759 * [If I understand this correctly, plain CDROMs doesn't show up as
760 * IODVDServices. Too keep things simple, we will only support DVDs
761 * until somebody complains about it and we get hardware to test it on.
762 * (Unless I'm much mistaken, there aren't any (orignal) intel macs with
763 * plain cdroms.)]
764 */
765 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
766 AssertReturn(RefMatchingDict, NULL);
767
768 /*
769 * do the search and get a collection of keyboards.
770 */
771 io_iterator_t DVDServices = NULL;
772 IOReturn irc = IOServiceGetMatchingServices(pThis->MasterPort, RefMatchingDict, &DVDServices);
773 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
774 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
775
776 /*
777 * Enumerate the DVD drives (services).
778 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
779 */
780 int rc = VERR_FILE_NOT_FOUND;
781 unsigned i = 0;
782 io_object_t DVDService;
783 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
784 {
785 /*
786 * Get the properties we use to identify the DVD drive.
787 *
788 * While there is a (weird 12 byte) GUID, it isn't persistent
789 * accross boots. So, we have to use a combination of the
790 * vendor name and product name properties with an optional
791 * sequence number for identification.
792 */
793 CFMutableDictionaryRef PropsRef = 0;
794 kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
795 if (krc == KERN_SUCCESS)
796 {
797 /* Get the Device Characteristics dictionary. */
798 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
799 if (DevCharRef)
800 {
801 /* The vendor name. */
802 char szVendor[128];
803 char *pszVendor = &szVendor[0];
804 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
805 if ( ValueRef
806 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
807 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
808 pszVendor = RTStrStrip(szVendor);
809 else
810 *pszVendor = '\0';
811
812 /* The product name. */
813 char szProduct[128];
814 char *pszProduct = &szProduct[0];
815 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
816 if ( ValueRef
817 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
818 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
819 pszProduct = RTStrStrip(szProduct);
820 else
821 *pszProduct = '\0';
822
823 /* Construct the two names and compare thwm with the one we're searching for. */
824 char szName1[256 + 32];
825 char szName2[256 + 32];
826 if (*pszVendor || *pszProduct)
827 {
828 if (*pszVendor && *pszProduct)
829 {
830 RTStrPrintf(szName1, sizeof(szName1), "%s %s", pszVendor, pszProduct);
831 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", pszVendor, pszProduct, i);
832 }
833 else
834 {
835 strcpy(szName1, *pszVendor ? pszVendor : pszProduct);
836 RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
837 }
838 }
839 else
840 {
841 RTStrPrintf(szName1, sizeof(szName1), "(#%u)", i);
842 strcpy(szName2, szName1);
843 }
844
845 if ( !strcmp(szName1, pThis->pszDeviceOpen)
846 || !strcmp(szName2, pThis->pszDeviceOpen))
847 {
848 /*
849 * Found it! Now, get the client interface and stuff.
850 * Note that we could also query kIOSCSITaskDeviceUserClientTypeID here if the
851 * MMC client plugin is missing. For now we assume this won't be necessary.
852 */
853 SInt32 Score = 0;
854 IOCFPlugInInterface **ppPlugInInterface = NULL;
855 krc = IOCreatePlugInInterfaceForService(DVDService, kIOMMCDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
856 &ppPlugInInterface, &Score);
857 if (krc == KERN_SUCCESS)
858 {
859 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
860 CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
861 (LPVOID *)&pThis->ppMMCDI);
862 (*ppPlugInInterface)->Release(ppPlugInInterface);
863 ppPlugInInterface = NULL;
864 if (hrc == S_OK)
865 {
866 pThis->ppScsiTaskDI = (*pThis->ppMMCDI)->GetSCSITaskDeviceInterface(pThis->ppMMCDI);
867 if (pThis->ppScsiTaskDI)
868 rc = VINF_SUCCESS;
869 else
870 {
871 LogRel(("GetSCSITaskDeviceInterface failed on '%s'\n", pThis->pszDeviceOpen));
872 rc = VERR_NOT_SUPPORTED;
873 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
874 }
875 }
876 else
877 {
878 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinCOM(krc);
879 pThis->ppMMCDI = NULL;
880 }
881 }
882 else /* Check for kIOSCSITaskDeviceUserClientTypeID? */
883 rc = VERR_GENERAL_FAILURE;//RTErrConvertFromDarwinKern(krc);
884
885 /* Obtain exclusive access to the device so we can send SCSI commands. */
886 if (VBOX_SUCCESS(rc))
887 rc = drvHostBaseObtainExclusiveAccess(pThis, DVDService);
888
889 /* Cleanup on failure. */
890 if (VBOX_FAILURE(rc))
891 {
892 if (pThis->ppScsiTaskDI)
893 {
894 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
895 pThis->ppScsiTaskDI = NULL;
896 }
897 if (pThis->ppMMCDI)
898 {
899 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
900 pThis->ppMMCDI = NULL;
901 }
902 }
903
904 IOObjectRelease(DVDService);
905 break;
906 }
907 }
908 CFRelease(PropsRef);
909 }
910 else
911 AssertMsgFailed(("krc=%#x\n", krc));
912
913 IOObjectRelease(DVDService);
914 i++;
915 }
916
917 IOObjectRelease(DVDServices);
918 return rc;
919
920#elif defined(RT_OS_LINUX)
921 /** @todo we've got RTFILE_O_NON_BLOCK now. Change the code to use RTFileOpen. */
922 int FileDevice = open(pThis->pszDeviceOpen, (pThis->fReadOnlyConfig ? O_RDONLY : O_RDWR) | O_NONBLOCK);
923 if (FileDevice < 0)
924 return RTErrConvertFromErrno(errno);
925 *pFileDevice = FileDevice;
926 return VINF_SUCCESS;
927
928#else
929 return RTFileOpen(pFileDevice, pThis->pszDeviceOpen,
930 (fReadOnly ? RTFILE_O_READ : RTFILE_O_READWRITE) | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
931#endif
932}
933
934
935/**
936 * (Re)opens the device.
937 *
938 * This is used to open the device during construction, but it's also used to re-open
939 * the device when a media is inserted. This re-open will kill off any cached data
940 * that Linux for some peculiar reason thinks should survive a media change...
941 *
942 * @returns VBOX status code.
943 * @param pThis Instance data.
944 */
945static int drvHostBaseReopen(PDRVHOSTBASE pThis)
946{
947#ifndef RT_OS_DARWIN /* Only *one* open for darwin. */
948 LogFlow(("%s-%d: drvHostBaseReopen: '%s'\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen));
949
950 RTFILE FileDevice;
951 int rc = drvHostBaseOpen(pThis, &FileDevice, pThis->fReadOnlyConfig);
952 if (VBOX_FAILURE(rc))
953 {
954 if (!pThis->fReadOnlyConfig)
955 {
956 LogFlow(("%s-%d: drvHostBaseReopen: '%s' - retry readonly (%Vrc)\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDeviceOpen, rc));
957 rc = drvHostBaseOpen(pThis, &FileDevice, false);
958 }
959 if (VBOX_FAILURE(rc))
960 {
961 LogFlow(("%s-%d: failed to open device '%s', rc=%Vrc\n",
962 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
963 return rc;
964 }
965 pThis->fReadOnly = true;
966 }
967 else
968 pThis->fReadOnly = pThis->fReadOnlyConfig;
969
970 if (pThis->FileDevice != NIL_RTFILE)
971 RTFileClose(pThis->FileDevice);
972 pThis->FileDevice = FileDevice;
973#endif /* !RT_OS_DARWIN */
974 return VINF_SUCCESS;
975}
976
977
978/**
979 * Queries the media size.
980 *
981 * @returns VBox status code.
982 * @param pThis Pointer to the instance data.
983 * @param pcb Where to store the media size in bytes.
984 */
985static int drvHostBaseGetMediaSize(PDRVHOSTBASE pThis, uint64_t *pcb)
986{
987#ifdef RT_OS_DARWIN
988 /*
989 * Try a READ_CAPACITY command...
990 */
991 struct
992 {
993 uint32_t cBlocks;
994 uint32_t cbBlock;
995 } Buf = {0, 0};
996 size_t cbBuf = sizeof(Buf);
997 uint8_t abCmd[16] =
998 {
999 SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
1000 0,0,0,0,0,0,0,0,0
1001 };
1002 int rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
1003 if (VBOX_SUCCESS(rc))
1004 {
1005 Assert(cbBuf == sizeof(Buf));
1006 Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
1007 Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
1008 //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
1009 // Buf.cbBlock = 2048;
1010 pThis->cbBlock = Buf.cbBlock;
1011
1012 *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
1013 }
1014 return rc;
1015
1016#elif defined(RT_OS_WINDOWS)
1017 /* use NT api, retry a few times if the media is being verified. */
1018 IO_STATUS_BLOCK IoStatusBlock = {0};
1019 FILE_FS_SIZE_INFORMATION FsSize= {0};
1020 NTSTATUS rcNt = NtQueryVolumeInformationFile((HANDLE)pThis->FileDevice, &IoStatusBlock,
1021 &FsSize, sizeof(FsSize), FileFsSizeInformation);
1022 int cRetries = 5;
1023 while (rcNt == STATUS_VERIFY_REQUIRED && cRetries-- > 0)
1024 {
1025 RTThreadSleep(10);
1026 rcNt = NtQueryVolumeInformationFile((HANDLE)pThis->FileDevice, &IoStatusBlock,
1027 &FsSize, sizeof(FsSize), FileFsSizeInformation);
1028 }
1029 if (rcNt >= 0)
1030 {
1031 *pcb = FsSize.TotalAllocationUnits.QuadPart * FsSize.BytesPerSector;
1032 return VINF_SUCCESS;
1033 }
1034
1035 /* convert nt status code to VBox status code. */
1036 /** @todo Make convertion function!. */
1037 int rc = VERR_GENERAL_FAILURE;
1038 switch (rcNt)
1039 {
1040 case STATUS_NO_MEDIA_IN_DEVICE: rc = VERR_MEDIA_NOT_PRESENT; break;
1041 case STATUS_VERIFY_REQUIRED: rc = VERR_TRY_AGAIN; break;
1042 }
1043 LogFlow(("drvHostBaseGetMediaSize: NtQueryVolumeInformationFile -> %#lx\n", rcNt, rc));
1044 return rc;
1045#else
1046 return RTFileSeek(pThis->FileDevice, 0, RTFILE_SEEK_END, pcb);
1047#endif
1048}
1049
1050
1051#ifdef RT_OS_DARWIN
1052/**
1053 * Execute a SCSI command.
1054 *
1055 * @param pThis The instance data.
1056 * @param pbCmd Pointer to the SCSI command.
1057 * @param cbCmd The size of the SCSI command.
1058 * @param enmTxDir The transfer direction.
1059 * @param pvBuf The buffer. Can be NULL if enmTxDir is PDMBLOCKTXDIR_NONE.
1060 * @param pcbBuf Where to get the buffer size from and put the actual transfer size. Can be NULL.
1061 * @param pbSense Where to put the sense data. Can be NULL.
1062 * @param cbSense Size of the sense data buffer.
1063 * @param cTimeoutMillies The timeout. 0 mean the default timeout.
1064 *
1065 * @returns VINF_SUCCESS on success (no sense code).
1066 * @returns VERR_UNRESOLVED_ERROR if sense code is present.
1067 * @returns Some other VBox status code on failures without sense code.
1068 *
1069 * @todo Fix VERR_UNRESOLVED_ERROR abuse.
1070 */
1071DECLCALLBACK(int) DRVHostBaseScsiCmd(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMBLOCKTXDIR enmTxDir,
1072 void *pvBuf, size_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
1073{
1074 /*
1075 * Minimal input validation.
1076 */
1077 Assert(enmTxDir == PDMBLOCKTXDIR_NONE || enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE || enmTxDir == PDMBLOCKTXDIR_TO_DEVICE);
1078 Assert(!pvBuf || pcbBuf);
1079 Assert(pvBuf || enmTxDir == PDMBLOCKTXDIR_NONE);
1080 Assert(pbSense || !cbSense);
1081 AssertPtr(pbCmd);
1082 Assert(cbCmd <= 16 && cbCmd >= 1);
1083 const size_t cbBuf = pcbBuf ? *pcbBuf : 0;
1084 if (pcbBuf)
1085 *pcbBuf = 0;
1086
1087# ifdef RT_OS_DARWIN
1088 Assert(pThis->ppScsiTaskDI);
1089
1090 int rc = VERR_GENERAL_FAILURE;
1091 SCSITaskInterface **ppScsiTaskI = (*pThis->ppScsiTaskDI)->CreateSCSITask(pThis->ppScsiTaskDI);
1092 if (!ppScsiTaskI)
1093 return VERR_NO_MEMORY;
1094 do
1095 {
1096 /* Setup the scsi command. */
1097 SCSICommandDescriptorBlock cdb = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1098 memcpy(&cdb[0], pbCmd, cbCmd);
1099 IOReturn irc = (*ppScsiTaskI)->SetCommandDescriptorBlock(ppScsiTaskI, cdb, cbCmd);
1100 AssertBreak(irc == kIOReturnSuccess,);
1101
1102 /* Setup the buffer. */
1103 if (enmTxDir == PDMBLOCKTXDIR_NONE)
1104 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, NULL, 0, 0, kSCSIDataTransfer_NoDataTransfer);
1105 else
1106 {
1107 IOVirtualRange Range = { (IOVirtualAddress)pvBuf, cbBuf };
1108 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, &Range, 1, cbBuf,
1109 enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE
1110 ? kSCSIDataTransfer_FromTargetToInitiator
1111 : kSCSIDataTransfer_FromInitiatorToTarget);
1112 }
1113 AssertBreak(irc == kIOReturnSuccess,);
1114
1115 /* Set the timeout. */
1116 irc = (*ppScsiTaskI)->SetTimeoutDuration(ppScsiTaskI, cTimeoutMillies ? cTimeoutMillies : 30000 /*ms*/);
1117 AssertBreak(irc == kIOReturnSuccess,);
1118
1119 /* Execute the command and get the response. */
1120 SCSI_Sense_Data SenseData = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1121 SCSIServiceResponse ServiceResponse = kSCSIServiceResponse_Request_In_Process;
1122 SCSITaskStatus TaskStatus = kSCSITaskStatus_GOOD;
1123 UInt64 cbReturned = 0;
1124 irc = (*ppScsiTaskI)->ExecuteTaskSync(ppScsiTaskI, &SenseData, &TaskStatus, &cbReturned);
1125 AssertBreak(irc == kIOReturnSuccess,);
1126 if (pcbBuf)
1127 *pcbBuf = cbReturned;
1128
1129 irc = (*ppScsiTaskI)->GetSCSIServiceResponse(ppScsiTaskI, &ServiceResponse);
1130 AssertBreak(irc == kIOReturnSuccess,);
1131 AssertBreak(ServiceResponse == kSCSIServiceResponse_TASK_COMPLETE,);
1132
1133 if (TaskStatus == kSCSITaskStatus_GOOD)
1134 rc = VINF_SUCCESS;
1135 else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
1136 && pbSense)
1137 {
1138 memset(pbSense, 0, cbSense); /* lazy */
1139 memcpy(pbSense, &SenseData, RT_MIN(sizeof(SenseData), cbSense));
1140 rc = VERR_UNRESOLVED_ERROR;
1141 }
1142 /** @todo convert sense codes when caller doesn't wish to do this himself. */
1143 /*else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
1144 && SenseData.ADDITIONAL_SENSE_CODE == 0x3A)
1145 rc = VERR_MEDIA_NOT_PRESENT; */
1146 else
1147 {
1148 rc = enmTxDir == PDMBLOCKTXDIR_NONE
1149 ? VERR_DEV_IO_ERROR
1150 : enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE
1151 ? VERR_READ_ERROR
1152 : VERR_WRITE_ERROR;
1153 if (pThis->cLogRelErrors++ < 10)
1154 LogRel(("DVD scsi error: cmd={%.*Rhxs} TaskStatus=%#x key=%#x ASC=%#x ASCQ=%#x (%Vrc)\n",
1155 cbCmd, pbCmd, TaskStatus, SenseData.SENSE_KEY, SenseData.ADDITIONAL_SENSE_CODE,
1156 SenseData.ADDITIONAL_SENSE_CODE_QUALIFIER, rc));
1157 }
1158 } while (0);
1159
1160 (*ppScsiTaskI)->Release(ppScsiTaskI);
1161
1162# endif
1163
1164 return rc;
1165}
1166#endif
1167
1168
1169/**
1170 * Media present.
1171 * Query the size and notify the above driver / device.
1172 *
1173 * @param pThis The instance data.
1174 */
1175int DRVHostBaseMediaPresent(PDRVHOSTBASE pThis)
1176{
1177 /*
1178 * Open the drive.
1179 */
1180 int rc = drvHostBaseReopen(pThis);
1181 if (VBOX_FAILURE(rc))
1182 return rc;
1183
1184 /*
1185 * Determin the size.
1186 */
1187 uint64_t cb;
1188 rc = pThis->pfnGetMediaSize(pThis, &cb);
1189 if (VBOX_FAILURE(rc))
1190 {
1191 LogFlow(("%s-%d: failed to figure media size of %s, rc=%Vrc\n",
1192 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->pszDevice, rc));
1193 return rc;
1194 }
1195
1196 /*
1197 * Update the data and inform the unit.
1198 */
1199 pThis->cbSize = cb;
1200 pThis->fMediaPresent = true;
1201 if (pThis->pDrvMountNotify)
1202 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
1203 LogFlow(("%s-%d: drvHostBaseMediaPresent: cbSize=%lld (%#llx)\n",
1204 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, pThis->cbSize, pThis->cbSize));
1205 return VINF_SUCCESS;
1206}
1207
1208
1209/**
1210 * Media no longer present.
1211 * @param pThis The instance data.
1212 */
1213void DRVHostBaseMediaNotPresent(PDRVHOSTBASE pThis)
1214{
1215 pThis->fMediaPresent = false;
1216 pThis->fLocked = false;
1217 pThis->fTranslationSet = false;
1218 pThis->cSectors = 0;
1219 if (pThis->pDrvMountNotify)
1220 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
1221}
1222
1223
1224#ifdef RT_OS_WINDOWS
1225
1226/**
1227 * Window procedure for the invisible window used to catch the WM_DEVICECHANGE broadcasts.
1228 */
1229static LRESULT CALLBACK DeviceChangeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1230{
1231 Log2(("DeviceChangeWindowProc: hwnd=%08x uMsg=%08x\n", hwnd, uMsg));
1232 if (uMsg == WM_DESTROY)
1233 {
1234 PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLong(hwnd, GWLP_USERDATA);
1235 if (pThis)
1236 ASMAtomicXchgSize(&pThis->hwndDeviceChange, NULL);
1237 PostQuitMessage(0);
1238 }
1239
1240 if (uMsg != WM_DEVICECHANGE)
1241 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1242
1243 PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
1244 PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
1245 Assert(pThis);
1246 if (pThis == NULL)
1247 return 0;
1248
1249 switch (wParam)
1250 {
1251 case DBT_DEVICEARRIVAL:
1252 case DBT_DEVICEREMOVECOMPLETE:
1253 // Check whether a CD or DVD was inserted into or removed from a drive.
1254 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
1255 {
1256 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
1257 if ( (lpdbv->dbcv_flags & DBTF_MEDIA)
1258 && (pThis->fUnitMask & lpdbv->dbcv_unitmask))
1259 {
1260 RTCritSectEnter(&pThis->CritSect);
1261 if (wParam == DBT_DEVICEARRIVAL)
1262 {
1263 int cRetries = 10;
1264 int rc = DRVHostBaseMediaPresent(pThis);
1265 while (VBOX_FAILURE(rc) && cRetries-- > 0)
1266 {
1267 RTThreadSleep(50);
1268 rc = DRVHostBaseMediaPresent(pThis);
1269 }
1270 }
1271 else
1272 DRVHostBaseMediaNotPresent(pThis);
1273 RTCritSectLeave(&pThis->CritSect);
1274 }
1275 }
1276 break;
1277 }
1278 return TRUE;
1279}
1280
1281#endif /* RT_OS_WINDOWS */
1282
1283
1284/**
1285 * This thread will periodically poll the device for media presence.
1286 *
1287 * @returns Ignored.
1288 * @param ThreadSelf Handle of this thread. Ignored.
1289 * @param pvUser Pointer to the driver instance structure.
1290 */
1291static DECLCALLBACK(int) drvHostBaseMediaThread(RTTHREAD ThreadSelf, void *pvUser)
1292{
1293 PDRVHOSTBASE pThis = (PDRVHOSTBASE)pvUser;
1294 LogFlow(("%s-%d: drvHostBaseMediaThread: ThreadSelf=%p pvUser=%p\n",
1295 pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, ThreadSelf, pvUser));
1296#ifdef RT_OS_WINDOWS
1297 static WNDCLASS s_classDeviceChange = {0};
1298 static ATOM s_hAtomDeviceChange = 0;
1299
1300 /*
1301 * Register custom window class.
1302 */
1303 if (s_hAtomDeviceChange == 0)
1304 {
1305 memset(&s_classDeviceChange, 0, sizeof(s_classDeviceChange));
1306 s_classDeviceChange.lpfnWndProc = DeviceChangeWindowProc;
1307 s_classDeviceChange.lpszClassName = "VBOX_DeviceChangeClass";
1308 s_classDeviceChange.hInstance = GetModuleHandle("VBOXDD.DLL");
1309 Assert(s_classDeviceChange.hInstance);
1310 s_hAtomDeviceChange = RegisterClassA(&s_classDeviceChange);
1311 Assert(s_hAtomDeviceChange);
1312 }
1313
1314 /*
1315 * Create Window w/ the pThis as user data.
1316 */
1317 HWND hwnd = CreateWindow((LPCTSTR)s_hAtomDeviceChange, "", WS_POPUP, 0, 0, 0, 0, 0, 0, s_classDeviceChange.hInstance, 0);
1318 AssertMsg(hwnd, ("CreateWindow failed with %d\n", GetLastError()));
1319 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
1320
1321 /*
1322 * Signal the waiting EMT thread that everything went fine.
1323 */
1324 ASMAtomicXchgSize(&pThis->hwndDeviceChange, hwnd);
1325 RTThreadUserSignal(ThreadSelf);
1326 if (!hwnd)
1327 {
1328 LogFlow(("%s-%d: drvHostBaseMediaThread: returns VERR_GENERAL_FAILURE\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
1329 return VERR_GENERAL_FAILURE;
1330 }
1331 LogFlow(("%s-%d: drvHostBaseMediaThread: Created hwndDeviceChange=%p\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance, hwnd));
1332
1333 /*
1334 * Message pump.
1335 */
1336 MSG Msg;
1337 BOOL fRet;
1338 while ((fRet = GetMessage(&Msg, NULL, 0, 0)) != FALSE)
1339 {
1340 if (fRet != -1)
1341 {
1342 TranslateMessage(&Msg);
1343 DispatchMessage(&Msg);
1344 }
1345 //else: handle the error and possibly exit
1346 }
1347 Assert(!pThis->hwndDeviceChange);
1348
1349#else /* !RT_OS_WINDOWS */
1350 bool fFirst = true;
1351 int cRetries = 10;
1352 while (!pThis->fShutdownPoller)
1353 {
1354 /*
1355 * Perform the polling (unless we've run out of 50ms retries).
1356 */
1357 if ( pThis->pfnPoll
1358 && cRetries-- > 0)
1359 {
1360
1361 int rc = pThis->pfnPoll(pThis);
1362 if (VBOX_FAILURE(rc))
1363 {
1364 RTSemEventWait(pThis->EventPoller, 50);
1365 continue;
1366 }
1367 }
1368
1369 /*
1370 * Signal EMT after the first go.
1371 */
1372 if (fFirst)
1373 {
1374 RTThreadUserSignal(ThreadSelf);
1375 fFirst = false;
1376 }
1377
1378 /*
1379 * Sleep.
1380 */
1381 int rc = RTSemEventWait(pThis->EventPoller, pThis->cMilliesPoller);
1382 if ( VBOX_FAILURE(rc)
1383 && rc != VERR_TIMEOUT)
1384 {
1385 AssertMsgFailed(("rc=%Vrc\n", rc));
1386 pThis->ThreadPoller = NIL_RTTHREAD;
1387 LogFlow(("drvHostBaseMediaThread: returns %Vrc\n", rc));
1388 return rc;
1389 }
1390 cRetries = 10;
1391 }
1392
1393#endif /* !RT_OS_WINDOWS */
1394
1395 /* (Don't clear the thread handle here, the destructor thread is using it to wait.) */
1396 LogFlow(("%s-%d: drvHostBaseMediaThread: returns VINF_SUCCESS\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
1397 return VINF_SUCCESS;
1398}
1399
1400/* -=-=-=-=- driver interface -=-=-=-=- */
1401
1402
1403/**
1404 * Done state load operation.
1405 *
1406 * @returns VBox load code.
1407 * @param pDrvIns Driver instance of the driver which registered the data unit.
1408 * @param pSSM SSM operation handle.
1409 */
1410static DECLCALLBACK(int) drvHostBaseLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1411{
1412 PDRVHOSTBASE pThis = PDMINS2DATA(pDrvIns, PDRVHOSTBASE);
1413 LogFlow(("%s-%d: drvHostBaseMediaThread:\n", pThis->pDrvIns->pDrvReg->szDriverName, pThis->pDrvIns->iInstance));
1414 RTCritSectEnter(&pThis->CritSect);
1415
1416 /*
1417 * Tell the device/driver above us that the media status is uncertain.
1418 */
1419 if (pThis->pDrvMountNotify)
1420 {
1421 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
1422 if (pThis->fMediaPresent)
1423 pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
1424 }
1425
1426 RTCritSectLeave(&pThis->CritSect);
1427 return VINF_SUCCESS;
1428}
1429
1430
1431/** @copydoc FNPDMDRVDESTRUCT */
1432DECLCALLBACK(void) DRVHostBaseDestruct(PPDMDRVINS pDrvIns)
1433{
1434 PDRVHOSTBASE pThis = PDMINS2DATA(pDrvIns, PDRVHOSTBASE);
1435 LogFlow(("%s-%d: drvHostBaseDestruct: iInstance=%d\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, pDrvIns->iInstance));
1436
1437 /*
1438 * Terminate the thread.
1439 */
1440 if (pThis->ThreadPoller != NIL_RTTHREAD)
1441 {
1442 pThis->fShutdownPoller = true;
1443 int rc;
1444 int cTimes = 50;
1445 do
1446 {
1447#ifdef RT_OS_WINDOWS
1448 if (pThis->hwndDeviceChange)
1449 PostMessage(pThis->hwndDeviceChange, WM_CLOSE, 0, 0); /* default win proc will destroy the window */
1450#else
1451 RTSemEventSignal(pThis->EventPoller);
1452#endif
1453 rc = RTThreadWait(pThis->ThreadPoller, 100, NULL);
1454 } while (cTimes-- > 0 && rc == VERR_TIMEOUT);
1455
1456 if (!rc)
1457 pThis->ThreadPoller = NIL_RTTHREAD;
1458 }
1459
1460 /*
1461 * Unlock the drive if we've locked it or we're in passthru mode.
1462 */
1463#ifdef RT_OS_DARWIN
1464 if ( ( pThis->fLocked
1465 || pThis->IBlock.pfnSendCmd)
1466 && pThis->ppScsiTaskDI
1467#else /** @todo Check if the other guys can mix pfnDoLock with scsi passthru.
1468 * (We're currently not unlocking the device after use. See todo in DevATA.cpp.) */
1469 if ( pThis->fLocked
1470 && pThis->FileDevice != NIL_RTFILE
1471#endif
1472 && pThis->pfnDoLock)
1473 {
1474 int rc = pThis->pfnDoLock(pThis, false);
1475 if (VBOX_SUCCESS(rc))
1476 pThis->fLocked = false;
1477 }
1478
1479 /*
1480 * Cleanup the other resources.
1481 */
1482#ifdef RT_OS_WINDOWS
1483 if (pThis->hwndDeviceChange)
1484 {
1485 if (SetWindowLongPtr(pThis->hwndDeviceChange, GWLP_USERDATA, 0) == (LONG_PTR)pThis)
1486 PostMessage(pThis->hwndDeviceChange, WM_CLOSE, 0, 0); /* default win proc will destroy the window */
1487 pThis->hwndDeviceChange = NULL;
1488 }
1489#else
1490 if (pThis->EventPoller != NULL)
1491 {
1492 RTSemEventDestroy(pThis->EventPoller);
1493 pThis->EventPoller = NULL;
1494 }
1495#endif
1496
1497#ifdef RT_OS_DARWIN
1498 /*
1499 * The unclaiming doesn't seem to mean much, the DVD is actaully
1500 * remounted when we release exclusive access. I'm not quite sure
1501 * if I should put the unclaim first or not...
1502 *
1503 * Anyway, that it's automatically remounted very good news for us,
1504 * because that means we don't have to mess with that ourselves. Of
1505 * course there is the unlikely scenario that we've succeeded in claiming
1506 * and umount the DVD but somehow failed to gain exclusive scsi access...
1507 */
1508 if (pThis->ppScsiTaskDI)
1509 {
1510 LogFlow(("%s-%d: releasing exclusive scsi access!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
1511 (*pThis->ppScsiTaskDI)->ReleaseExclusiveAccess(pThis->ppScsiTaskDI);
1512 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
1513 pThis->ppScsiTaskDI = NULL;
1514 }
1515 if (pThis->pDADisk)
1516 {
1517 LogFlow(("%s-%d: unclaiming the disk!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
1518 DADiskUnclaim(pThis->pDADisk);
1519 CFRelease(pThis->pDADisk);
1520 pThis->pDADisk = NULL;
1521 }
1522 if (pThis->ppMMCDI)
1523 {
1524 LogFlow(("%s-%d: releasing the MMC object!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
1525 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
1526 pThis->ppMMCDI = NULL;
1527 }
1528 if (pThis->MasterPort)
1529 {
1530 mach_port_deallocate(mach_task_self(), pThis->MasterPort);
1531 pThis->MasterPort = NULL;
1532 }
1533 if (pThis->pDASession)
1534 {
1535 LogFlow(("%s-%d: releasing the DA session!\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
1536 CFRelease(pThis->pDASession);
1537 pThis->pDASession = NULL;
1538 }
1539#else
1540 if (pThis->FileDevice != NIL_RTFILE)
1541 {
1542 int rc = RTFileClose(pThis->FileDevice);
1543 AssertRC(rc);
1544 pThis->FileDevice = NIL_RTFILE;
1545 }
1546#endif
1547
1548 if (pThis->pszDevice)
1549 {
1550 MMR3HeapFree(pThis->pszDevice);
1551 pThis->pszDevice = NULL;
1552 }
1553
1554 if (pThis->pszDeviceOpen)
1555 {
1556 RTStrFree(pThis->pszDeviceOpen);
1557 pThis->pszDeviceOpen = NULL;
1558 }
1559
1560 /* Forget about the notifications. */
1561 pThis->pDrvMountNotify = NULL;
1562
1563 /* Leave the instance operational if this is just a cleanup of the state
1564 * after an attach error happened. So don't destry the critsect then. */
1565 if (!pThis->fKeepInstance && RTCritSectIsInitialized(&pThis->CritSect))
1566 RTCritSectDelete(&pThis->CritSect);
1567 LogFlow(("%s-%d: drvHostBaseDestruct completed\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance));
1568}
1569
1570
1571/**
1572 * Initializes the instance data (init part 1).
1573 *
1574 * The driver which derives from this base driver will override function pointers after
1575 * calling this method, and complete the construction by calling DRVHostBaseInitFinish().
1576 *
1577 * On failure call DRVHostBaseDestruct().
1578 *
1579 * @returns VBox status code.
1580 * @param pDrvIns Driver instance.
1581 * @param pCfgHandle Configuration handle.
1582 * @param enmType Device type.
1583 */
1584int DRVHostBaseInitData(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, PDMBLOCKTYPE enmType)
1585{
1586 PDRVHOSTBASE pThis = PDMINS2DATA(pDrvIns, PDRVHOSTBASE);
1587 LogFlow(("%s-%d: DRVHostBaseInitData: iInstance=%d\n", pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, pDrvIns->iInstance));
1588
1589 /*
1590 * Initialize most of the data members.
1591 */
1592 pThis->pDrvIns = pDrvIns;
1593 pThis->fKeepInstance = false;
1594 pThis->ThreadPoller = NIL_RTTHREAD;
1595#ifdef RT_OS_DARWIN
1596 pThis->MasterPort = NULL;
1597 pThis->ppMMCDI = NULL;
1598 pThis->ppScsiTaskDI = NULL;
1599 pThis->cbBlock = 0;
1600 pThis->pDADisk = NULL;
1601 pThis->pDASession = NULL;
1602#else
1603 pThis->FileDevice = NIL_RTFILE;
1604#endif
1605 pThis->enmType = enmType;
1606 //pThis->cErrors = 0;
1607
1608 pThis->pfnGetMediaSize = drvHostBaseGetMediaSize;
1609
1610 /* IBase. */
1611 pDrvIns->IBase.pfnQueryInterface = drvHostBaseQueryInterface;
1612
1613 /* IBlock. */
1614 pThis->IBlock.pfnRead = drvHostBaseRead;
1615 pThis->IBlock.pfnWrite = drvHostBaseWrite;
1616 pThis->IBlock.pfnFlush = drvHostBaseFlush;
1617 pThis->IBlock.pfnIsReadOnly = drvHostBaseIsReadOnly;
1618 pThis->IBlock.pfnGetSize = drvHostBaseGetSize;
1619 pThis->IBlock.pfnGetType = drvHostBaseGetType;
1620 pThis->IBlock.pfnGetUuid = drvHostBaseGetUuid;
1621
1622 /* IBlockBios. */
1623 pThis->IBlockBios.pfnGetGeometry = drvHostBaseGetGeometry;
1624 pThis->IBlockBios.pfnSetGeometry = drvHostBaseSetGeometry;
1625 pThis->IBlockBios.pfnGetTranslation = drvHostBaseGetTranslation;
1626 pThis->IBlockBios.pfnSetTranslation = drvHostBaseSetTranslation;
1627 pThis->IBlockBios.pfnIsVisible = drvHostBaseIsVisible;
1628 pThis->IBlockBios.pfnGetType = drvHostBaseBiosGetType;
1629
1630 /* IMount. */
1631 pThis->IMount.pfnMount = drvHostBaseMount;
1632 pThis->IMount.pfnUnmount = drvHostBaseUnmount;
1633 pThis->IMount.pfnIsMounted = drvHostBaseIsMounted;
1634 pThis->IMount.pfnLock = drvHostBaseLock;
1635 pThis->IMount.pfnUnlock = drvHostBaseUnlock;
1636 pThis->IMount.pfnIsLocked = drvHostBaseIsLocked;
1637
1638 /*
1639 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
1640 */
1641 pThis->pDrvBlockPort = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
1642 if (!pThis->pDrvBlockPort)
1643 {
1644 AssertMsgFailed(("Configuration error: No block port interface above!\n"));
1645 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1646 }
1647 pThis->pDrvMountNotify = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
1648
1649 /*
1650 * Query configuration.
1651 */
1652 /* Device */
1653 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Path", &pThis->pszDevice);
1654 if (VBOX_FAILURE(rc))
1655 {
1656 AssertMsgFailed(("Configuration error: query for \"Path\" string returned %Vra.\n", rc));
1657 return rc;
1658 }
1659
1660 /* Mountable */
1661 uint32_t u32;
1662 rc = CFGMR3QueryU32(pCfgHandle, "Interval", &u32);
1663 if (VBOX_SUCCESS(rc))
1664 pThis->cMilliesPoller = u32;
1665 else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1666 pThis->cMilliesPoller = 1000;
1667 else if (VBOX_FAILURE(rc))
1668 {
1669 AssertMsgFailed(("Configuration error: Query \"Mountable\" resulted in %Vrc.\n", rc));
1670 return rc;
1671 }
1672
1673 /* ReadOnly */
1674 rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &pThis->fReadOnlyConfig);
1675 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1676 pThis->fReadOnlyConfig = enmType == PDMBLOCKTYPE_DVD || enmType == PDMBLOCKTYPE_CDROM ? true : false;
1677 else if (VBOX_FAILURE(rc))
1678 {
1679 AssertMsgFailed(("Configuration error: Query \"ReadOnly\" resulted in %Vrc.\n", rc));
1680 return rc;
1681 }
1682
1683 /* Locked */
1684 rc = CFGMR3QueryBool(pCfgHandle, "Locked", &pThis->fLocked);
1685 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1686 pThis->fLocked = false;
1687 else if (VBOX_FAILURE(rc))
1688 {
1689 AssertMsgFailed(("Configuration error: Query \"Locked\" resulted in %Vrc.\n", rc));
1690 return rc;
1691 }
1692
1693 /* BIOS visible */
1694 rc = CFGMR3QueryBool(pCfgHandle, "BIOSVisible", &pThis->fBiosVisible);
1695 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1696 pThis->fBiosVisible = true;
1697 else if (VBOX_FAILURE(rc))
1698 {
1699 AssertMsgFailed(("Configuration error: Query \"BIOSVisible\" resulted in %Vrc.\n", rc));
1700 return rc;
1701 }
1702
1703 /* Uuid */
1704 char *psz;
1705 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Uuid", &psz);
1706 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1707 RTUuidClear(&pThis->Uuid);
1708 else if (VBOX_SUCCESS(rc))
1709 {
1710 rc = RTUuidFromStr(&pThis->Uuid, psz);
1711 if (VBOX_FAILURE(rc))
1712 {
1713 AssertMsgFailed(("Configuration error: Uuid from string failed on \"%s\", rc=%Vrc.\n", psz, rc));
1714 MMR3HeapFree(psz);
1715 return rc;
1716 }
1717 MMR3HeapFree(psz);
1718 }
1719 else
1720 {
1721 AssertMsgFailed(("Configuration error: Failed to obtain the uuid, rc=%Vrc.\n", rc));
1722 return rc;
1723 }
1724
1725 /* Define whether attach failure is an error (default) or not. */
1726 bool fAttachFailError;
1727 rc = CFGMR3QueryBool(pCfgHandle, "AttachFailError", &fAttachFailError);
1728 if (VBOX_FAILURE(rc))
1729 fAttachFailError = true;
1730 pThis->fAttachFailError = fAttachFailError;
1731
1732 /* name to open & watch for */
1733#ifdef RT_OS_WINDOWS
1734 int iBit = toupper(pThis->pszDevice[0]) - 'A';
1735 if ( iBit > 'Z' - 'A'
1736 || pThis->pszDevice[1] != ':'
1737 || pThis->pszDevice[2])
1738 {
1739 AssertMsgFailed(("Configuration error: Invalid drive specification: '%s'\n", pThis->pszDevice));
1740 return VERR_INVALID_PARAMETER;
1741 }
1742 pThis->fUnitMask = 1 << iBit;
1743 RTStrAPrintf(&pThis->pszDeviceOpen, "\\\\.\\%s", pThis->pszDevice);
1744#else
1745 pThis->pszDeviceOpen = RTStrDup(pThis->pszDevice);
1746#endif
1747 if (!pThis->pszDeviceOpen)
1748 return VERR_NO_MEMORY;
1749
1750 return VINF_SUCCESS;
1751}
1752
1753
1754/**
1755 * Do the 2nd part of the init after the derived driver has overridden the defaults.
1756 *
1757 * On failure call DRVHostBaseDestruct().
1758 *
1759 * @returns VBox status code.
1760 * @param pThis Pointer to the instance data.
1761 */
1762int DRVHostBaseInitFinish(PDRVHOSTBASE pThis)
1763{
1764 int src = VINF_SUCCESS;
1765 PPDMDRVINS pDrvIns = pThis->pDrvIns;
1766
1767 /* log config summary */
1768 Log(("%s-%d: pszDevice='%s' (%s) cMilliesPoller=%d fReadOnlyConfig=%d fLocked=%d fBIOSVisible=%d Uuid=%Vuuid\n",
1769 pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, pThis->pszDevice, pThis->pszDeviceOpen, pThis->cMilliesPoller,
1770 pThis->fReadOnlyConfig, pThis->fLocked, pThis->fBiosVisible, &pThis->Uuid));
1771
1772 /*
1773 * Check that there are no drivers below us.
1774 */
1775 PPDMIBASE pBase;
1776 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
1777 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
1778 {
1779 AssertMsgFailed(("Configuration error: No attached driver, please! (rc=%Vrc)\n", rc));
1780 return VERR_PDM_DRVINS_NO_ATTACH;
1781 }
1782
1783 /*
1784 * Register saved state.
1785 */
1786 rc = pDrvIns->pDrvHlp->pfnSSMRegister(pDrvIns, pDrvIns->pDrvReg->szDriverName, pDrvIns->iInstance, 1, 0,
1787 NULL, NULL, NULL,
1788 NULL, NULL, drvHostBaseLoadDone);
1789 if (VBOX_FAILURE(rc))
1790 return rc;
1791
1792 /*
1793 * Verify type.
1794 */
1795#ifdef RT_OS_WINDOWS
1796 UINT uDriveType = GetDriveType(pThis->pszDevice);
1797 switch (pThis->enmType)
1798 {
1799 case PDMBLOCKTYPE_FLOPPY_360:
1800 case PDMBLOCKTYPE_FLOPPY_720:
1801 case PDMBLOCKTYPE_FLOPPY_1_20:
1802 case PDMBLOCKTYPE_FLOPPY_1_44:
1803 case PDMBLOCKTYPE_FLOPPY_2_88:
1804 if (uDriveType != DRIVE_REMOVABLE)
1805 {
1806 AssertMsgFailed(("Configuration error: '%s' is not a floppy (type=%d)\n",
1807 pThis->pszDevice, uDriveType));
1808 return VERR_INVALID_PARAMETER;
1809 }
1810 break;
1811 case PDMBLOCKTYPE_CDROM:
1812 case PDMBLOCKTYPE_DVD:
1813 if (uDriveType != DRIVE_CDROM)
1814 {
1815 AssertMsgFailed(("Configuration error: '%s' is not a cdrom (type=%d)\n",
1816 pThis->pszDevice, uDriveType));
1817 return VERR_INVALID_PARAMETER;
1818 }
1819 break;
1820 case PDMBLOCKTYPE_HARD_DISK:
1821 default:
1822 AssertMsgFailed(("enmType=%d\n", pThis->enmType));
1823 return VERR_INVALID_PARAMETER;
1824 }
1825#endif
1826
1827 /*
1828 * Open the device.
1829 */
1830#ifdef RT_OS_DARWIN
1831 rc = drvHostBaseOpen(pThis, NULL, pThis->fReadOnlyConfig);
1832#else
1833 rc = drvHostBaseReopen(pThis);
1834#endif
1835 if (VBOX_FAILURE(rc))
1836 {
1837 char *pszDevice = pThis->pszDevice;
1838#ifndef RT_OS_DARWIN
1839 char szPathReal[256];
1840 if ( RTPathExists(pszDevice)
1841 && RT_SUCCESS(RTPathReal(pszDevice, szPathReal, sizeof(szPathReal))))
1842 pszDevice = szPathReal;
1843 pThis->FileDevice = NIL_RTFILE;
1844#endif
1845 /* Disable CD/DVD passthrough in case it was enabled. Would cause
1846 * weird failures later when the guest issues commands. These would
1847 * all fail because of the invalid file handle. So use the normal
1848 * virtual CD/DVD code, which deals more gracefully with unavailable
1849 * "media" - actually a complete drive in this case. */
1850 pThis->IBlock.pfnSendCmd = NULL;
1851 AssertMsgFailed(("Could not open host device %s, rc=%Vrc\n", pszDevice, rc));
1852 switch (rc)
1853 {
1854 case VERR_ACCESS_DENIED:
1855 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1856#ifdef RT_OS_LINUX
1857 N_("Cannot open host device '%s' for %s access. Check the permissions "
1858 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
1859 "of the device group. Make sure that you logout/login after changing "
1860 "the group settings of the current user"),
1861#else
1862 N_("Cannot open host device '%s' for %s access. Check the permissions "
1863 "of that device"),
1864#endif
1865 pszDevice, pThis->fReadOnlyConfig ? "readonly" : "read/write",
1866 pszDevice);
1867 default:
1868 {
1869 if (pThis->fAttachFailError)
1870 return rc;
1871 int erc = PDMDrvHlpVMSetRuntimeError(pDrvIns,
1872 false, "DrvHost_MOUNTFAIL",
1873 N_("Cannot attach to host device '%s'"), pszDevice);
1874 AssertRC(erc);
1875 src = rc;
1876 }
1877 }
1878 }
1879#ifdef RT_OS_WINDOWS
1880 if (VBOX_SUCCESS(src))
1881 DRVHostBaseMediaPresent(pThis);
1882#endif
1883
1884 /*
1885 * Lock the drive if that's required by the configuration.
1886 */
1887 if (pThis->fLocked)
1888 {
1889 if (pThis->pfnDoLock)
1890 rc = pThis->pfnDoLock(pThis, true);
1891 if (VBOX_FAILURE(rc))
1892 {
1893 AssertMsgFailed(("Failed to lock the dvd drive. rc=%Vrc\n", rc));
1894 return rc;
1895 }
1896 }
1897
1898#ifndef RT_OS_WINDOWS
1899 if (VBOX_SUCCESS(src))
1900 {
1901 /*
1902 * Create the event semaphore which the poller thread will wait on.
1903 */
1904 rc = RTSemEventCreate(&pThis->EventPoller);
1905 if (VBOX_FAILURE(rc))
1906 return rc;
1907 }
1908#endif
1909
1910 /*
1911 * Initialize the critical section used for serializing the access to the media.
1912 */
1913 rc = RTCritSectInit(&pThis->CritSect);
1914 if (VBOX_FAILURE(rc))
1915 return rc;
1916
1917 if (VBOX_SUCCESS(src))
1918 {
1919 /*
1920 * Start the thread which will poll for the media.
1921 */
1922 rc = RTThreadCreate(&pThis->ThreadPoller, drvHostBaseMediaThread, pThis, 0,
1923 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "DVDMEDIA");
1924 if (VBOX_FAILURE(rc))
1925 {
1926 AssertMsgFailed(("Failed to create poller thread. rc=%Vrc\n", rc));
1927 return rc;
1928 }
1929
1930 /*
1931 * Wait for the thread to start up (!w32:) and do one detection loop.
1932 */
1933 rc = RTThreadUserWait(pThis->ThreadPoller, 10000);
1934 AssertRC(rc);
1935#ifdef RT_OS_WINDOWS
1936 if (!pThis->hwndDeviceChange)
1937 return VERR_GENERAL_FAILURE;
1938#endif
1939 }
1940
1941 if (VBOX_FAILURE(src))
1942 return src;
1943 return rc;
1944}
1945
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