VirtualBox

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

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