VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase-darwin.cpp@ 64278

Last change on this file since 64278 was 64278, checked in by vboxsync, 8 years ago

Devices/Storage/DrvHost*: Move more host dependent code into the host specific source files, move validating the CFGM values into DRVHostBaseInitData() because it doesn't make much sense to check for unknown values after most of the values were already queried

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 KB
Line 
1/* $Id: DrvHostBase-darwin.cpp 64278 2016-10-14 12:17:45Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver, OS X specifics.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
18#include <mach/mach.h>
19#include <Carbon/Carbon.h>
20#include <IOKit/IOKitLib.h>
21#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
22#include <IOKit/scsi/SCSITaskLib.h>
23#include <IOKit/scsi/SCSICommandOperationCodes.h>
24#include <IOKit/IOBSD.h>
25#include <DiskArbitration/DiskArbitration.h>
26#include <mach/mach_error.h>
27#include <VBox/scsi.h>
28
29#include "DrvHostBase.h"
30
31DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
32 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
33{
34 /*
35 * Minimal input validation.
36 */
37 Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
38 Assert(!pvBuf || pcbBuf);
39 Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
40 Assert(pbSense || !cbSense);
41 AssertPtr(pbCmd);
42 Assert(cbCmd <= 16 && cbCmd >= 1);
43 const uint32_t cbBuf = pcbBuf ? *pcbBuf : 0;
44 if (pcbBuf)
45 *pcbBuf = 0;
46
47 Assert(pThis->ppScsiTaskDI);
48
49 int rc = VERR_GENERAL_FAILURE;
50 SCSITaskInterface **ppScsiTaskI = (*pThis->ppScsiTaskDI)->CreateSCSITask(pThis->ppScsiTaskDI);
51 if (!ppScsiTaskI)
52 return VERR_NO_MEMORY;
53 do
54 {
55 /* Setup the scsi command. */
56 SCSICommandDescriptorBlock cdb = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
57 memcpy(&cdb[0], pbCmd, cbCmd);
58 IOReturn irc = (*ppScsiTaskI)->SetCommandDescriptorBlock(ppScsiTaskI, cdb, cbCmd);
59 AssertBreak(irc == kIOReturnSuccess);
60
61 /* Setup the buffer. */
62 if (enmTxDir == PDMMEDIATXDIR_NONE)
63 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, NULL, 0, 0, kSCSIDataTransfer_NoDataTransfer);
64 else
65 {
66 IOVirtualRange Range = { (IOVirtualAddress)pvBuf, cbBuf };
67 irc = (*ppScsiTaskI)->SetScatterGatherEntries(ppScsiTaskI, &Range, 1, cbBuf,
68 enmTxDir == PDMMEDIATXDIR_FROM_DEVICE
69 ? kSCSIDataTransfer_FromTargetToInitiator
70 : kSCSIDataTransfer_FromInitiatorToTarget);
71 }
72 AssertBreak(irc == kIOReturnSuccess);
73
74 /* Set the timeout. */
75 irc = (*ppScsiTaskI)->SetTimeoutDuration(ppScsiTaskI, cTimeoutMillies ? cTimeoutMillies : 30000 /*ms*/);
76 AssertBreak(irc == kIOReturnSuccess);
77
78 /* Execute the command and get the response. */
79 SCSI_Sense_Data SenseData = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
80 SCSIServiceResponse ServiceResponse = kSCSIServiceResponse_Request_In_Process;
81 SCSITaskStatus TaskStatus = kSCSITaskStatus_GOOD;
82 UInt64 cbReturned = 0;
83 irc = (*ppScsiTaskI)->ExecuteTaskSync(ppScsiTaskI, &SenseData, &TaskStatus, &cbReturned);
84 AssertBreak(irc == kIOReturnSuccess);
85 if (pcbBuf)
86 *pcbBuf = (int32_t)cbReturned;
87
88 irc = (*ppScsiTaskI)->GetSCSIServiceResponse(ppScsiTaskI, &ServiceResponse);
89 AssertBreak(irc == kIOReturnSuccess);
90 AssertBreak(ServiceResponse == kSCSIServiceResponse_TASK_COMPLETE);
91
92 if (TaskStatus == kSCSITaskStatus_GOOD)
93 rc = VINF_SUCCESS;
94 else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
95 && pbSense)
96 {
97 memset(pbSense, 0, cbSense); /* lazy */
98 memcpy(pbSense, &SenseData, RT_MIN(sizeof(SenseData), cbSense));
99 rc = VERR_UNRESOLVED_ERROR;
100 }
101 /** @todo convert sense codes when caller doesn't wish to do this himself. */
102 /*else if ( TaskStatus == kSCSITaskStatus_CHECK_CONDITION
103 && SenseData.ADDITIONAL_SENSE_CODE == 0x3A)
104 rc = VERR_MEDIA_NOT_PRESENT; */
105 else
106 {
107 rc = enmTxDir == PDMMEDIATXDIR_NONE
108 ? VERR_DEV_IO_ERROR
109 : enmTxDir == PDMMEDIATXDIR_FROM_DEVICE
110 ? VERR_READ_ERROR
111 : VERR_WRITE_ERROR;
112 if (pThis->cLogRelErrors++ < 10)
113 LogRel(("DVD scsi error: cmd={%.*Rhxs} TaskStatus=%#x key=%#x ASC=%#x ASCQ=%#x (%Rrc)\n",
114 cbCmd, pbCmd, TaskStatus, SenseData.SENSE_KEY, SenseData.ADDITIONAL_SENSE_CODE,
115 SenseData.ADDITIONAL_SENSE_CODE_QUALIFIER, rc));
116 }
117 } while (0);
118
119 (*ppScsiTaskI)->Release(ppScsiTaskI);
120
121 return rc;
122}
123
124
125DECLHIDDEN(int) drvHostBaseGetMediaSizeOs(PDRVHOSTBASE pThis, uint64_t *pcb)
126{
127 /*
128 * Try a READ_CAPACITY command...
129 */
130 struct
131 {
132 uint32_t cBlocks;
133 uint32_t cbBlock;
134 } Buf = {0, 0};
135 uint32_t cbBuf = sizeof(Buf);
136 uint8_t abCmd[16] =
137 {
138 SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
139 0,0,0,0,0,0,0,0,0
140 };
141 int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
142 if (RT_SUCCESS(rc))
143 {
144 Assert(cbBuf == sizeof(Buf));
145 Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
146 Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
147 //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
148 // Buf.cbBlock = 2048;
149 pThis->cbBlock = Buf.cbBlock;
150
151 *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
152 }
153 return rc;
154}
155
156
157DECLHIDDEN(int) drvHostBaseReadOs(PDRVHOSTBASE pThis, uint64_t off, void *pvBuf, size_t cbRead)
158{
159 int rc = VINF_SUCCESS;
160
161 if ( pThis->ppScsiTaskDI
162 && pThis->cbBlock)
163 {
164 /*
165 * Issue a READ(12) request.
166 */
167 do
168 {
169 const uint32_t LBA = off / pThis->cbBlock;
170 AssertReturn(!(off % pThis->cbBlock), VERR_INVALID_PARAMETER);
171 uint32_t cbRead32 = cbRead > SCSI_MAX_BUFFER_SIZE
172 ? SCSI_MAX_BUFFER_SIZE
173 : (uint32_t)cbRead;
174 const uint32_t cBlocks = cbRead32 / pThis->cbBlock;
175 AssertReturn(!(cbRead % pThis->cbBlock), VERR_INVALID_PARAMETER);
176 uint8_t abCmd[16] =
177 {
178 SCSI_READ_12, 0,
179 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
180 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
181 0, 0, 0, 0, 0
182 };
183 rc = drvHostBaseScsiCmdOs(pThis, abCmd, 12, PDMMEDIATXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
184
185 off += cbRead32;
186 cbRead -= cbRead32;
187 pvBuf = (uint8_t *)pvBuf + cbRead32;
188 } while ((cbRead > 0) && RT_SUCCESS(rc));
189 }
190 else
191 rc = VERR_MEDIA_NOT_PRESENT;
192
193 return rc;
194}
195
196
197DECLHIDDEN(int) drvHostBaseWriteOs(PDRVHOSTBASE pThis, uint64_t off, const void *pvBuf, size_t cbWrite)
198{
199 RT_NOREF4(pThis, off, pvBuf, cbWrite);
200 return VERR_WRITE_PROTECT;
201}
202
203
204DECLHIDDEN(int) drvHostBaseFlushOs(PDRVHOSTBASE pThis)
205{
206 RT_NOREF1(pThis);
207 return VINF_SUCCESS;
208}
209
210
211DECLHIDDEN(int) drvHostBaseDoLockOs(PDRVHOSTBASE pThis, bool fLock)
212{
213 uint8_t abCmd[16] =
214 {
215 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
216 0,0,0,0,0,0,0,0,0,0
217 };
218 return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
219}
220
221
222DECLHIDDEN(int) drvHostBaseEjectOs(PDRVHOSTBASE pThis)
223{
224 uint8_t abCmd[16] =
225 {
226 SCSI_START_STOP_UNIT, 0, 0, 0, 2 /*eject+stop*/, 0,
227 0,0,0,0,0,0,0,0,0,0
228 };
229 return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
230}
231
232
233DECLHIDDEN(int) drvHostBaseQueryMediaStatusOs(PDRVHOSTBASE pThis, bool *pfMediaChanged, bool *pfMediaPresent)
234{
235 AssertReturn(pThis->ppScsiTaskDI, VERR_INTERNAL_ERROR);
236
237 /*
238 * Issue a TEST UNIT READY request.
239 */
240 *pfMediaChanged = false;
241 *pfMediaPresent = false;
242 uint8_t abCmd[16] = { SCSI_TEST_UNIT_READY, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
243 uint8_t abSense[32];
244 int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
245 if (RT_SUCCESS(rc))
246 *pfMediaPresent = true;
247 else if ( rc == VERR_UNRESOLVED_ERROR
248 && abSense[2] == 6 /* unit attention */
249 && ( (abSense[12] == 0x29 && abSense[13] < 5 /* reset */)
250 || (abSense[12] == 0x2a && abSense[13] == 0 /* parameters changed */) //???
251 || (abSense[12] == 0x3f && abSense[13] == 0 /* target operating conditions have changed */) //???
252 || (abSense[12] == 0x3f && abSense[13] == 2 /* changed operating definition */) //???
253 || (abSense[12] == 0x3f && abSense[13] == 3 /* inquiry parameters changed */)
254 || (abSense[12] == 0x3f && abSense[13] == 5 /* device identifier changed */)
255 )
256 )
257 {
258 *pfMediaPresent = false;
259 *pfMediaChanged = true;
260 rc = VINF_SUCCESS;
261 /** @todo check this media change stuff on Darwin. */
262 }
263
264 return rc;
265}
266
267
268DECLHIDDEN(int) drvHostBasePollerWakeupOs(PDRVHOSTBASE pThis)
269{
270 return RTSemEventSignal(pThis->EventPoller);
271}
272
273
274DECLHIDDEN(void) drvHostBaseDestructOs(PDRVHOSTBASE pThis)
275{
276 if (pThis->EventPoller != NULL)
277 {
278 RTSemEventDestroy(pThis->EventPoller);
279 pThis->EventPoller = NULL;
280 }
281
282 /*
283 * The unclaiming doesn't seem to mean much, the DVD is actually
284 * remounted when we release exclusive access. I'm not quite sure
285 * if I should put the unclaim first or not...
286 *
287 * Anyway, that it's automatically remounted very good news for us,
288 * because that means we don't have to mess with that ourselves. Of
289 * course there is the unlikely scenario that we've succeeded in claiming
290 * and umount the DVD but somehow failed to gain exclusive scsi access...
291 */
292 if (pThis->ppScsiTaskDI)
293 {
294 LogFlow(("%s-%d: releasing exclusive scsi access!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
295 (*pThis->ppScsiTaskDI)->ReleaseExclusiveAccess(pThis->ppScsiTaskDI);
296 (*pThis->ppScsiTaskDI)->Release(pThis->ppScsiTaskDI);
297 pThis->ppScsiTaskDI = NULL;
298 }
299 if (pThis->pDADisk)
300 {
301 LogFlow(("%s-%d: unclaiming the disk!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
302 DADiskUnclaim(pThis->pDADisk);
303 CFRelease(pThis->pDADisk);
304 pThis->pDADisk = NULL;
305 }
306 if (pThis->ppMMCDI)
307 {
308 LogFlow(("%s-%d: releasing the MMC object!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
309 (*pThis->ppMMCDI)->Release(pThis->ppMMCDI);
310 pThis->ppMMCDI = NULL;
311 }
312 if (pThis->MasterPort != IO_OBJECT_NULL)
313 {
314 mach_port_deallocate(mach_task_self(), pThis->MasterPort);
315 pThis->MasterPort = IO_OBJECT_NULL;
316 }
317 if (pThis->pDASession)
318 {
319 LogFlow(("%s-%d: releasing the DA session!\n", pThis->pDrvIns->pReg->szName, pThis->pDrvIns->iInstance));
320 CFRelease(pThis->pDASession);
321 pThis->pDASession = NULL;
322 }
323}
324
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