VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp@ 65078

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

Devices/Storage/DrvHost*: Avoid using SCSI_MAX_BUFFER_SIZE (will be removed in the future) but query the maximum supported buffer size through a dedicated method from the platform specific code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.0 KB
Line 
1/* $Id: DrvHostBase-freebsd.cpp 65078 2017-01-03 13:24:58Z vboxsync $ */
2/** @file
3 * DrvHostBase - Host base drive access driver, FreeBSD 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 <sys/cdefs.h>
19#include <sys/param.h>
20#include <errno.h>
21#include <stdio.h>
22#include <cam/cam.h>
23#include <cam/cam_ccb.h>
24#include <cam/scsi/scsi_message.h>
25#include <cam/scsi/scsi_pass.h>
26#include <VBox/scsi.h>
27#include <iprt/log.h>
28
29/** Maximum buffer size supported by the CAM subsystem. */
30#define FBSD_SCSI_MAX_BUFFER_SIZE (64 * _1K)
31
32/**
33 * Host backend specific data.
34 */
35typedef struct DRVHOSTBASEOS
36{
37 /** The filehandle of the device. */
38 RTFILE hFileDevice;
39 /** The block size. Set when querying the media size. */
40 uint32_t cbBlock;
41 /** SCSI bus number. */
42 path_id_t ScsiBus;
43 /** target ID of the passthrough device. */
44 target_id_t ScsiTargetID;
45 /** LUN of the passthrough device. */
46 lun_id_t ScsiLunID;
47} DRVHOSTBASEOS;
48/** Pointer to the host backend specific data. */
49typedef DRVHOSTBASEOS *PDRVHOSBASEOS;
50AssertCompile(sizeof(DRVHOSTBASEOS) <= 64);
51
52#define DRVHOSTBASE_OS_INT_DECLARED
53#include "DrvHostBase.h"
54
55
56DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
57 void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
58{
59 /*
60 * Minimal input validation.
61 */
62 Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
63 Assert(!pvBuf || pcbBuf);
64 Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
65 Assert(pbSense || !cbSense);
66 AssertPtr(pbCmd);
67 Assert(cbCmd <= 16 && cbCmd >= 1);
68 const uint32_t cbBuf = pcbBuf ? *pcbBuf : 0;
69 if (pcbBuf)
70 *pcbBuf = 0;
71
72 int rc = VINF_SUCCESS;
73 int rcBSD = 0;
74 union ccb DeviceCCB;
75 union ccb *pDeviceCCB = &DeviceCCB;
76 u_int32_t fFlags;
77
78 memset(pDeviceCCB, 0, sizeof(DeviceCCB));
79 pDeviceCCB->ccb_h.path_id = pThis->Os.ScsiBus;
80 pDeviceCCB->ccb_h.target_id = pThis->Os.ScsiTargetID;
81 pDeviceCCB->ccb_h.target_lun = pThis->Os.ScsiLunID;
82
83 /* The SCSI INQUIRY command can't be passed through directly. */
84 if (pbCmd[0] == SCSI_INQUIRY)
85 {
86 pDeviceCCB->ccb_h.func_code = XPT_GDEV_TYPE;
87
88 rcBSD = ioctl(RTFileToNative(pThis->Os.hFileDevice), CAMIOCOMMAND, pDeviceCCB);
89 if (!rcBSD)
90 {
91 uint32_t cbCopy = cbBuf < sizeof(struct scsi_inquiry_data)
92 ? cbBuf
93 : sizeof(struct scsi_inquiry_data);;
94 memcpy(pvBuf, &pDeviceCCB->cgd.inq_data, cbCopy);
95 memset(pbSense, 0, cbSense);
96
97 if (pcbBuf)
98 *pcbBuf = cbCopy;
99 }
100 else
101 rc = RTErrConvertFromErrno(errno);
102 }
103 else
104 {
105 /* Copy the CDB. */
106 memcpy(&pDeviceCCB->csio.cdb_io.cdb_bytes, pbCmd, cbCmd);
107
108 /* Set direction. */
109 if (enmTxDir == PDMMEDIATXDIR_NONE)
110 fFlags = CAM_DIR_NONE;
111 else if (enmTxDir == PDMMEDIATXDIR_FROM_DEVICE)
112 fFlags = CAM_DIR_IN;
113 else
114 fFlags = CAM_DIR_OUT;
115
116 fFlags |= CAM_DEV_QFRZDIS;
117
118 cam_fill_csio(&pDeviceCCB->csio, 1, NULL, fFlags, MSG_SIMPLE_Q_TAG,
119 (u_int8_t *)pvBuf, cbBuf, cbSense, cbCmd,
120 cTimeoutMillies ? cTimeoutMillies : 30000/* timeout */);
121
122 /* Send command */
123 rcBSD = ioctl(RTFileToNative(pThis->Os.hFileDevice), CAMIOCOMMAND, pDeviceCCB);
124 if (!rcBSD)
125 {
126 switch (pDeviceCCB->ccb_h.status & CAM_STATUS_MASK)
127 {
128 case CAM_REQ_CMP:
129 rc = VINF_SUCCESS;
130 break;
131 case CAM_SEL_TIMEOUT:
132 rc = VERR_DEV_IO_ERROR;
133 break;
134 case CAM_CMD_TIMEOUT:
135 rc = VERR_TIMEOUT;
136 break;
137 default:
138 rc = VERR_DEV_IO_ERROR;
139 }
140
141 if (pcbBuf)
142 *pcbBuf = cbBuf - pDeviceCCB->csio.resid;
143
144 if (pbSense)
145 memcpy(pbSense, &pDeviceCCB->csio.sense_data,
146 cbSense - pDeviceCCB->csio.sense_resid);
147 }
148 else
149 rc = RTErrConvertFromErrno(errno);
150 }
151}
152
153
154DECLHIDDEN(size_t) drvHostBaseScsiCmdGetBufLimitOs(PDRVHOSTBASE pThis)
155{
156 RT_NOREF(pThis);
157
158 return FBSD_SCSI_MAX_BUFFER_SIZE;
159}
160
161
162DECLHIDDEN(int) drvHostBaseGetMediaSizeOs(PDRVHOSTBASE pThis, uint64_t *pcb)
163{
164 /*
165 * Try a READ_CAPACITY command...
166 */
167 struct
168 {
169 uint32_t cBlocks;
170 uint32_t cbBlock;
171 } Buf = {0, 0};
172 uint32_t cbBuf = sizeof(Buf);
173 uint8_t abCmd[16] =
174 {
175 SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
176 0,0,0,0,0,0,0,0,0
177 };
178 int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
179 if (RT_SUCCESS(rc))
180 {
181 Assert(cbBuf == sizeof(Buf));
182 Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
183 Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
184 //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
185 // Buf.cbBlock = 2048;
186 pThis->Os.cbBlock = Buf.cbBlock;
187
188 *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
189 }
190 return rc;
191}
192
193
194DECLHIDDEN(int) drvHostBaseReadOs(PDRVHOSTBASE pThis, uint64_t off, void *pvBuf, size_t cbRead)
195{
196 int rc = VINF_SUCCESS;
197
198 if (pThis->Os.cbBlock)
199 {
200 /*
201 * Issue a READ(12) request.
202 */
203 do
204 {
205 const uint32_t LBA = off / pThis->Os.cbBlock;
206 AssertReturn(!(off % pThis->Os.cbBlock), VERR_INVALID_PARAMETER);
207 uint32_t cbRead32 = cbRead > FBSD_SCSI_MAX_BUFFER_SIZE
208 ? FBSD_SCSI_MAX_BUFFER_SIZE
209 : (uint32_t)cbRead;
210 const uint32_t cBlocks = cbRead32 / pThis->Os.cbBlock;
211 AssertReturn(!(cbRead % pThis->Os.cbBlock), VERR_INVALID_PARAMETER);
212 uint8_t abCmd[16] =
213 {
214 SCSI_READ_12, 0,
215 RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
216 RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
217 0, 0, 0, 0, 0
218 };
219 rc = drvHostBaseScsiCmdOs(pThis, abCmd, 12, PDMMEDIATXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
220
221 off += cbRead32;
222 cbRead -= cbRead32;
223 pvBuf = (uint8_t *)pvBuf + cbRead32;
224 } while ((cbRead > 0) && RT_SUCCESS(rc));
225 }
226 else
227 rc = VERR_MEDIA_NOT_PRESENT;
228
229 return rc;
230}
231
232
233DECLHIDDEN(int) drvHostBaseWriteOs(PDRVHOSTBASE pThis, uint64_t off, const void *pvBuf, size_t cbWrite)
234{
235 RT_NOREF4(pThis, off, pvBuf, cbWrite);
236 return VERR_WRITE_PROTECT;
237}
238
239
240DECLHIDDEN(int) drvHostBaseFlushOs(PDRVHOSTBASE pThis)
241{
242 RT_NOREF1(pThis);
243 return VINF_SUCCESS;
244}
245
246
247DECLHIDDEN(int) drvHostBaseDoLockOs(PDRVHOSTBASE pThis, bool fLock)
248{
249 uint8_t abCmd[16] =
250 {
251 SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
252 0,0,0,0,0,0,0,0,0,0
253 };
254 return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
255}
256
257
258DECLHIDDEN(int) drvHostBaseEjectOs(PDRVHOSTBASE pThis)
259{
260 uint8_t abCmd[16] =
261 {
262 SCSI_START_STOP_UNIT, 0, 0, 0, 2 /*eject+stop*/, 0,
263 0,0,0,0,0,0,0,0,0,0
264 };
265 return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
266}
267
268
269DECLHIDDEN(int) drvHostBaseQueryMediaStatusOs(PDRVHOSTBASE pThis, bool *pfMediaChanged, bool *pfMediaPresent)
270{
271 /*
272 * Issue a TEST UNIT READY request.
273 */
274 *pfMediaChanged = false;
275 *pfMediaPresent = false;
276 uint8_t abCmd[16] = { SCSI_TEST_UNIT_READY, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
277 uint8_t abSense[32];
278 int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
279 if (RT_SUCCESS(rc))
280 *pfMediaPresent = true;
281 else if ( rc == VERR_UNRESOLVED_ERROR
282 && abSense[2] == 6 /* unit attention */
283 && ( (abSense[12] == 0x29 && abSense[13] < 5 /* reset */)
284 || (abSense[12] == 0x2a && abSense[13] == 0 /* parameters changed */) //???
285 || (abSense[12] == 0x3f && abSense[13] == 0 /* target operating conditions have changed */) //???
286 || (abSense[12] == 0x3f && abSense[13] == 2 /* changed operating definition */) //???
287 || (abSense[12] == 0x3f && abSense[13] == 3 /* inquiry parameters changed */)
288 || (abSense[12] == 0x3f && abSense[13] == 5 /* device identifier changed */)
289 )
290 )
291 {
292 *pfMediaPresent = false;
293 *pfMediaChanged = true;
294 rc = VINF_SUCCESS;
295 /** @todo check this media change stuff on Darwin. */
296 }
297
298 return rc;
299}
300
301
302DECLHIDDEN(void) drvHostBaseInitOs(PDRVHOSTBASE pThis)
303{
304 pThis->Os.hFileDevice = NIL_RTFILE;
305}
306
307
308DECLHIDDEN(int) drvHostBaseOpenOs(PDRVHOSTBASE pThis, bool fReadOnly)
309{
310 RT_NOREF(fReadOnly);
311 RTFILE hFileDevice;
312 int rc = RTFileOpen(&hFileDevice, pThis->pszDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
313 if (RT_FAILURE(rc))
314 return rc;
315
316 /*
317 * The current device handle can't passthrough SCSI commands.
318 * We have to get he passthrough device path and open this.
319 */
320 union ccb DeviceCCB;
321 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
322
323 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
324 int rcBSD = ioctl(RTFileToNative(hFileDevice), CAMGETPASSTHRU, &DeviceCCB);
325 if (!rcBSD)
326 {
327 char *pszPassthroughDevice = NULL;
328 rc = RTStrAPrintf(&pszPassthroughDevice, "/dev/%s%u",
329 DeviceCCB.cgdl.periph_name, DeviceCCB.cgdl.unit_number);
330 if (rc >= 0)
331 {
332 RTFILE hPassthroughDevice;
333 rc = RTFileOpen(&hPassthroughDevice, pszPassthroughDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
334 RTStrFree(pszPassthroughDevice);
335 if (RT_SUCCESS(rc))
336 {
337 /* Get needed device parameters. */
338
339 /*
340 * The device path, target id and lun id. Those are
341 * needed for the SCSI passthrough ioctl.
342 */
343 memset(&DeviceCCB, 0, sizeof(DeviceCCB));
344 DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
345
346 rcBSD = ioctl(RTFileToNative(hPassthroughDevice), CAMGETPASSTHRU, &DeviceCCB);
347 if (!rcBSD)
348 {
349 if (DeviceCCB.cgdl.status != CAM_GDEVLIST_ERROR)
350 {
351 pThis->Os.ScsiBus = DeviceCCB.ccb_h.path_id;
352 pThis->Os.ScsiTargetID = DeviceCCB.ccb_h.target_id;
353 pThis->Os.ScsiLunID = DeviceCCB.ccb_h.target_lun;
354 pThis->Os.hFileDevice = hPassthroughDevice;
355 }
356 else
357 {
358 /* The passthrough device wasn't found. */
359 rc = VERR_NOT_FOUND;
360 }
361 }
362 else
363 rc = RTErrConvertFromErrno(errno);
364
365 if (RT_FAILURE(rc))
366 RTFileClose(hPassthroughDevice);
367 }
368 }
369 else
370 rc = VERR_NO_STR_MEMORY;
371 }
372 else
373 rc = RTErrConvertFromErrno(errno);
374
375 RTFileClose(hFileDevice);
376 return rc;
377}
378
379
380DECLHIDDEN(int) drvHostBaseMediaRefreshOs(PDRVHOSTBASE pThis)
381{
382 RT_NOREF(pThis);
383 return VINF_SUCCESS;
384}
385
386
387DECLHIDDEN(bool) drvHostBaseIsMediaPollingRequiredOs(PDRVHOSTBASE pThis)
388{
389 if (pThis->enmType == PDMMEDIATYPE_CDROM || pThis->enmType == PDMMEDIATYPE_DVD)
390 return true;
391
392 AssertMsgFailed(("FreeBSD supports only CD/DVD host drive access\n"));
393 return false;
394}
395
396
397DECLHIDDEN(void) drvHostBaseDestructOs(PDRVHOSTBASE pThis)
398{
399 /*
400 * Unlock the drive if we've locked it or we're in passthru mode.
401 */
402 if ( pThis->fLocked
403 && pThis->Os.hFileDevice != NIL_RTFILE
404 && pThis->pfnDoLock)
405 {
406 int rc = pThis->pfnDoLock(pThis, false);
407 if (RT_SUCCESS(rc))
408 pThis->fLocked = false;
409 }
410
411 if (pThis->Os.hFileDevice != NIL_RTFILE)
412 {
413 int rc = RTFileClose(pThis->Os.hFileDevice);
414 AssertRC(rc);
415 pThis->Os.hFileDevice = NIL_RTFILE;
416 }
417}
418
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