summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp')
-rw-r--r--src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp448
1 files changed, 448 insertions, 0 deletions
diff --git a/src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp b/src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp
new file mode 100644
index 00000000..8aaaaa12
--- /dev/null
+++ b/src/VBox/Devices/Storage/DrvHostBase-freebsd.cpp
@@ -0,0 +1,448 @@
+/* $Id: DrvHostBase-freebsd.cpp $ */
+/** @file
+ * DrvHostBase - Host base drive access driver, FreeBSD specifics.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_HOST_BASE
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <stdio.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_pass.h>
+#include <VBox/err.h>
+
+#include <VBox/scsi.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Host backend specific data.
+ */
+typedef struct DRVHOSTBASEOS
+{
+ /** The filehandle of the device. */
+ RTFILE hFileDevice;
+ /** The block size. Set when querying the media size. */
+ uint32_t cbBlock;
+ /** SCSI bus number. */
+ path_id_t ScsiBus;
+ /** target ID of the passthrough device. */
+ target_id_t ScsiTargetID;
+ /** LUN of the passthrough device. */
+ lun_id_t ScsiLunID;
+} DRVHOSTBASEOS;
+/** Pointer to the host backend specific data. */
+typedef DRVHOSTBASEOS *PDRVHOSBASEOS;
+AssertCompile(sizeof(DRVHOSTBASEOS) <= 64);
+
+#define DRVHOSTBASE_OS_INT_DECLARED
+#include "DrvHostBase.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Maximum buffer size supported by the CAM subsystem. */
+#define FBSD_SCSI_MAX_BUFFER_SIZE (64 * _1K)
+
+
+
+DECLHIDDEN(int) drvHostBaseScsiCmdOs(PDRVHOSTBASE pThis, const uint8_t *pbCmd, size_t cbCmd, PDMMEDIATXDIR enmTxDir,
+ void *pvBuf, uint32_t *pcbBuf, uint8_t *pbSense, size_t cbSense, uint32_t cTimeoutMillies)
+{
+ /*
+ * Minimal input validation.
+ */
+ Assert(enmTxDir == PDMMEDIATXDIR_NONE || enmTxDir == PDMMEDIATXDIR_FROM_DEVICE || enmTxDir == PDMMEDIATXDIR_TO_DEVICE);
+ Assert(!pvBuf || pcbBuf);
+ Assert(pvBuf || enmTxDir == PDMMEDIATXDIR_NONE);
+ Assert(pbSense || !cbSense);
+ AssertPtr(pbCmd);
+ Assert(cbCmd <= 16 && cbCmd >= 1);
+ const uint32_t cbBuf = pcbBuf ? *pcbBuf : 0;
+ if (pcbBuf)
+ *pcbBuf = 0;
+
+ int rc = VINF_SUCCESS;
+ int rcBSD = 0;
+ union ccb DeviceCCB;
+ union ccb *pDeviceCCB = &DeviceCCB;
+ u_int32_t fFlags;
+
+ memset(pDeviceCCB, 0, sizeof(DeviceCCB));
+ pDeviceCCB->ccb_h.path_id = pThis->Os.ScsiBus;
+ pDeviceCCB->ccb_h.target_id = pThis->Os.ScsiTargetID;
+ pDeviceCCB->ccb_h.target_lun = pThis->Os.ScsiLunID;
+
+ /* The SCSI INQUIRY command can't be passed through directly. */
+ if (pbCmd[0] == SCSI_INQUIRY)
+ {
+ pDeviceCCB->ccb_h.func_code = XPT_GDEV_TYPE;
+
+ rcBSD = ioctl(RTFileToNative(pThis->Os.hFileDevice), CAMIOCOMMAND, pDeviceCCB);
+ if (!rcBSD)
+ {
+ uint32_t cbCopy = cbBuf < sizeof(struct scsi_inquiry_data)
+ ? cbBuf
+ : sizeof(struct scsi_inquiry_data);;
+ memcpy(pvBuf, &pDeviceCCB->cgd.inq_data, cbCopy);
+ memset(pbSense, 0, cbSense);
+
+ if (pcbBuf)
+ *pcbBuf = cbCopy;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ {
+ /* Copy the CDB. */
+ memcpy(&pDeviceCCB->csio.cdb_io.cdb_bytes, pbCmd, cbCmd);
+
+ /* Set direction. */
+ if (enmTxDir == PDMMEDIATXDIR_NONE)
+ fFlags = CAM_DIR_NONE;
+ else if (enmTxDir == PDMMEDIATXDIR_FROM_DEVICE)
+ fFlags = CAM_DIR_IN;
+ else
+ fFlags = CAM_DIR_OUT;
+
+ fFlags |= CAM_DEV_QFRZDIS;
+
+ cam_fill_csio(&pDeviceCCB->csio, 1, NULL, fFlags, MSG_SIMPLE_Q_TAG,
+ (u_int8_t *)pvBuf, cbBuf, cbSense, cbCmd,
+ cTimeoutMillies ? cTimeoutMillies : 30000/* timeout */);
+
+ /* Send command */
+ rcBSD = ioctl(RTFileToNative(pThis->Os.hFileDevice), CAMIOCOMMAND, pDeviceCCB);
+ if (!rcBSD)
+ {
+ switch (pDeviceCCB->ccb_h.status & CAM_STATUS_MASK)
+ {
+ case CAM_REQ_CMP:
+ rc = VINF_SUCCESS;
+ break;
+ case CAM_SEL_TIMEOUT:
+ rc = VERR_DEV_IO_ERROR;
+ break;
+ case CAM_CMD_TIMEOUT:
+ rc = VERR_TIMEOUT;
+ break;
+ default:
+ rc = VERR_DEV_IO_ERROR;
+ }
+
+ if (pcbBuf)
+ *pcbBuf = cbBuf - pDeviceCCB->csio.resid;
+
+ if (pbSense)
+ memcpy(pbSense, &pDeviceCCB->csio.sense_data,
+ cbSense - pDeviceCCB->csio.sense_resid);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(size_t) drvHostBaseScsiCmdGetBufLimitOs(PDRVHOSTBASE pThis)
+{
+ RT_NOREF(pThis);
+
+ return FBSD_SCSI_MAX_BUFFER_SIZE;
+}
+
+
+DECLHIDDEN(int) drvHostBaseGetMediaSizeOs(PDRVHOSTBASE pThis, uint64_t *pcb)
+{
+ /*
+ * Try a READ_CAPACITY command...
+ */
+ struct
+ {
+ uint32_t cBlocks;
+ uint32_t cbBlock;
+ } Buf = {0, 0};
+ uint32_t cbBuf = sizeof(Buf);
+ uint8_t abCmd[16] =
+ {
+ SCSI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
+ 0,0,0,0,0,0,0,0,0
+ };
+ int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_FROM_DEVICE, &Buf, &cbBuf, NULL, 0, 0);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbBuf == sizeof(Buf));
+ Buf.cBlocks = RT_BE2H_U32(Buf.cBlocks);
+ Buf.cbBlock = RT_BE2H_U32(Buf.cbBlock);
+ //if (Buf.cbBlock > 2048) /* everyone else is doing this... check if it needed/right.*/
+ // Buf.cbBlock = 2048;
+ pThis->Os.cbBlock = Buf.cbBlock;
+
+ *pcb = (uint64_t)Buf.cBlocks * Buf.cbBlock;
+ }
+ return rc;
+}
+
+
+DECLHIDDEN(int) drvHostBaseReadOs(PDRVHOSTBASE pThis, uint64_t off, void *pvBuf, size_t cbRead)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pThis->Os.cbBlock)
+ {
+ /*
+ * Issue a READ(12) request.
+ */
+ do
+ {
+ const uint32_t LBA = off / pThis->Os.cbBlock;
+ AssertReturn(!(off % pThis->Os.cbBlock), VERR_INVALID_PARAMETER);
+ uint32_t cbRead32 = cbRead > FBSD_SCSI_MAX_BUFFER_SIZE
+ ? FBSD_SCSI_MAX_BUFFER_SIZE
+ : (uint32_t)cbRead;
+ const uint32_t cBlocks = cbRead32 / pThis->Os.cbBlock;
+ AssertReturn(!(cbRead % pThis->Os.cbBlock), VERR_INVALID_PARAMETER);
+ uint8_t abCmd[16] =
+ {
+ SCSI_READ_12, 0,
+ RT_BYTE4(LBA), RT_BYTE3(LBA), RT_BYTE2(LBA), RT_BYTE1(LBA),
+ RT_BYTE4(cBlocks), RT_BYTE3(cBlocks), RT_BYTE2(cBlocks), RT_BYTE1(cBlocks),
+ 0, 0, 0, 0, 0
+ };
+ rc = drvHostBaseScsiCmdOs(pThis, abCmd, 12, PDMMEDIATXDIR_FROM_DEVICE, pvBuf, &cbRead32, NULL, 0, 0);
+
+ off += cbRead32;
+ cbRead -= cbRead32;
+ pvBuf = (uint8_t *)pvBuf + cbRead32;
+ } while ((cbRead > 0) && RT_SUCCESS(rc));
+ }
+ else
+ rc = VERR_MEDIA_NOT_PRESENT;
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) drvHostBaseWriteOs(PDRVHOSTBASE pThis, uint64_t off, const void *pvBuf, size_t cbWrite)
+{
+ RT_NOREF4(pThis, off, pvBuf, cbWrite);
+ return VERR_WRITE_PROTECT;
+}
+
+
+DECLHIDDEN(int) drvHostBaseFlushOs(PDRVHOSTBASE pThis)
+{
+ RT_NOREF1(pThis);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) drvHostBaseDoLockOs(PDRVHOSTBASE pThis, bool fLock)
+{
+ uint8_t abCmd[16] =
+ {
+ SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
+ 0,0,0,0,0,0,0,0,0,0
+ };
+ return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
+}
+
+
+DECLHIDDEN(int) drvHostBaseEjectOs(PDRVHOSTBASE pThis)
+{
+ uint8_t abCmd[16] =
+ {
+ SCSI_START_STOP_UNIT, 0, 0, 0, 2 /*eject+stop*/, 0,
+ 0,0,0,0,0,0,0,0,0,0
+ };
+ return drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, NULL, 0, 0);
+}
+
+
+DECLHIDDEN(int) drvHostBaseQueryMediaStatusOs(PDRVHOSTBASE pThis, bool *pfMediaChanged, bool *pfMediaPresent)
+{
+ /*
+ * Issue a TEST UNIT READY request.
+ */
+ *pfMediaChanged = false;
+ *pfMediaPresent = false;
+ uint8_t abCmd[16] = { SCSI_TEST_UNIT_READY, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+ uint8_t abSense[32];
+ int rc = drvHostBaseScsiCmdOs(pThis, abCmd, 6, PDMMEDIATXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
+ if (RT_SUCCESS(rc))
+ *pfMediaPresent = true;
+ else if ( rc == VERR_UNRESOLVED_ERROR
+ && abSense[2] == 6 /* unit attention */
+ && ( (abSense[12] == 0x29 && abSense[13] < 5 /* reset */)
+ || (abSense[12] == 0x2a && abSense[13] == 0 /* parameters changed */) //???
+ || (abSense[12] == 0x3f && abSense[13] == 0 /* target operating conditions have changed */) //???
+ || (abSense[12] == 0x3f && abSense[13] == 2 /* changed operating definition */) //???
+ || (abSense[12] == 0x3f && abSense[13] == 3 /* inquiry parameters changed */)
+ || (abSense[12] == 0x3f && abSense[13] == 5 /* device identifier changed */)
+ )
+ )
+ {
+ *pfMediaPresent = false;
+ *pfMediaChanged = true;
+ rc = VINF_SUCCESS;
+ /** @todo check this media change stuff on Darwin. */
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(void) drvHostBaseInitOs(PDRVHOSTBASE pThis)
+{
+ pThis->Os.hFileDevice = NIL_RTFILE;
+}
+
+
+DECLHIDDEN(int) drvHostBaseOpenOs(PDRVHOSTBASE pThis, bool fReadOnly)
+{
+ RT_NOREF(fReadOnly);
+ RTFILE hFileDevice;
+ int rc = RTFileOpen(&hFileDevice, pThis->pszDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * The current device handle can't passthrough SCSI commands.
+ * We have to get he passthrough device path and open this.
+ */
+ union ccb DeviceCCB;
+ memset(&DeviceCCB, 0, sizeof(DeviceCCB));
+
+ DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
+ int rcBSD = ioctl(RTFileToNative(hFileDevice), CAMGETPASSTHRU, &DeviceCCB);
+ if (!rcBSD)
+ {
+ char *pszPassthroughDevice = NULL;
+ rc = RTStrAPrintf(&pszPassthroughDevice, "/dev/%s%u",
+ DeviceCCB.cgdl.periph_name, DeviceCCB.cgdl.unit_number);
+ if (rc >= 0)
+ {
+ RTFILE hPassthroughDevice;
+ rc = RTFileOpen(&hPassthroughDevice, pszPassthroughDevice, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ RTStrFree(pszPassthroughDevice);
+ if (RT_SUCCESS(rc))
+ {
+ /* Get needed device parameters. */
+
+ /*
+ * The device path, target id and lun id. Those are
+ * needed for the SCSI passthrough ioctl.
+ */
+ memset(&DeviceCCB, 0, sizeof(DeviceCCB));
+ DeviceCCB.ccb_h.func_code = XPT_GDEVLIST;
+
+ rcBSD = ioctl(RTFileToNative(hPassthroughDevice), CAMGETPASSTHRU, &DeviceCCB);
+ if (!rcBSD)
+ {
+ if (DeviceCCB.cgdl.status != CAM_GDEVLIST_ERROR)
+ {
+ pThis->Os.ScsiBus = DeviceCCB.ccb_h.path_id;
+ pThis->Os.ScsiTargetID = DeviceCCB.ccb_h.target_id;
+ pThis->Os.ScsiLunID = DeviceCCB.ccb_h.target_lun;
+ pThis->Os.hFileDevice = hPassthroughDevice;
+ }
+ else
+ {
+ /* The passthrough device wasn't found. */
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ if (RT_FAILURE(rc))
+ RTFileClose(hPassthroughDevice);
+ }
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ RTFileClose(hFileDevice);
+ return rc;
+}
+
+
+DECLHIDDEN(int) drvHostBaseMediaRefreshOs(PDRVHOSTBASE pThis)
+{
+ RT_NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(bool) drvHostBaseIsMediaPollingRequiredOs(PDRVHOSTBASE pThis)
+{
+ if (pThis->enmType == PDMMEDIATYPE_CDROM || pThis->enmType == PDMMEDIATYPE_DVD)
+ return true;
+
+ AssertMsgFailed(("FreeBSD supports only CD/DVD host drive access\n"));
+ return false;
+}
+
+
+DECLHIDDEN(void) drvHostBaseDestructOs(PDRVHOSTBASE pThis)
+{
+ /*
+ * Unlock the drive if we've locked it or we're in passthru mode.
+ */
+ if ( pThis->fLocked
+ && pThis->Os.hFileDevice != NIL_RTFILE
+ && pThis->pfnDoLock)
+ {
+ int rc = pThis->pfnDoLock(pThis, false);
+ if (RT_SUCCESS(rc))
+ pThis->fLocked = false;
+ }
+
+ if (pThis->Os.hFileDevice != NIL_RTFILE)
+ {
+ int rc = RTFileClose(pThis->Os.hFileDevice);
+ AssertRC(rc);
+ pThis->Os.hFileDevice = NIL_RTFILE;
+ }
+}
+