VirtualBox

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

Last change on this file since 91482 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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