summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/freebsd
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/Main/src-server/freebsd
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/Main/src-server/freebsd')
-rw-r--r--src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp560
-rw-r--r--src/VBox/Main/src-server/freebsd/Makefile.kup0
-rw-r--r--src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp458
-rw-r--r--src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp128
-rw-r--r--src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp353
5 files changed, 1499 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp
new file mode 100644
index 00000000..546810b8
--- /dev/null
+++ b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp
@@ -0,0 +1,560 @@
+/* $Id: HostHardwareFreeBSD.cpp $ */
+/** @file
+ * VirtualBox Main - Code for handling hardware detection under FreeBSD, VBoxSVC.
+ */
+
+/*
+ * Copyright (C) 2008-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_MAIN
+#include "HostHardwareLinux.h"
+
+#include <VBox/log.h>
+
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_pass.h>
+
+#include <vector>
+
+
+/*********************************************************************************************************************************
+* Typedefs and Defines *
+*********************************************************************************************************************************/
+typedef enum DriveType_T
+{
+ Fixed,
+ DVD,
+ Any
+} DriveType_T;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF;
+static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_DEF;
+
+
+/** Find the length of a string, ignoring trailing non-ascii or control
+ * characters
+ * @note Code duplicated in HostHardwareLinux.cpp */
+static size_t strLenStripped(const char *pcsz) RT_NOTHROW_DEF
+{
+ size_t cch = 0;
+ for (size_t i = 0; pcsz[i] != '\0'; ++i)
+ if (pcsz[i] > 32 /*space*/ && pcsz[i] < 127 /*delete*/)
+ cch = i;
+ return cch + 1;
+}
+
+
+/**
+ * Initialize the device description for a drive based on vendor and model name
+ * strings.
+ *
+ * @param pcszVendor The raw vendor ID string.
+ * @param pcszModel The raw product ID string.
+ * @param pszDesc Where to store the description string (optional)
+ * @param cbDesc The size of the buffer in @pszDesc
+ *
+ * @note Used for disks as well as DVDs.
+ */
+/* static */
+void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+ AssertPtrReturnVoid(pcszVendor);
+ AssertPtrReturnVoid(pcszModel);
+ AssertPtrNullReturnVoid(pszDesc);
+ AssertReturnVoid(!pszDesc || cbDesc > 0);
+ size_t cchVendor = strLenStripped(pcszVendor);
+ size_t cchModel = strLenStripped(pcszModel);
+
+ /* Construct the description string as "Vendor Product" */
+ if (pszDesc)
+ {
+ if (cchVendor > 0)
+ RTStrPrintf(pszDesc, cbDesc, "%.*s %s", cchVendor, pcszVendor,
+ cchModel > 0 ? pcszModel : "(unknown drive model)");
+ else
+ RTStrPrintf(pszDesc, cbDesc, "%s", pcszModel);
+ RTStrPurgeEncoding(pszDesc);
+ }
+}
+
+
+int VBoxMainDriveInfo::updateDVDs() RT_NOEXCEPT
+{
+ LogFlowThisFunc(("entered\n"));
+ int vrc;
+ try
+ {
+ mDVDList.clear();
+ /* Always allow the user to override our auto-detection using an
+ * environment variable. */
+ bool fSuccess = false; /* Have we succeeded in finding anything yet? */
+ vrc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */, &fSuccess);
+ if (RT_SUCCESS(vrc) && !fSuccess)
+ vrc = getDriveInfoFromCAM(&mDVDList, DVD, &fSuccess);
+ }
+ catch (std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ LogFlowThisFunc(("vrc=%Rrc\n", vrc));
+ return vrc;
+}
+
+int VBoxMainDriveInfo::updateFloppies() RT_NOEXCEPT
+{
+ LogFlowThisFunc(("entered\n"));
+ int vrc;
+ try
+ {
+ /* Only got the enviornment variable here... */
+ mFloppyList.clear();
+ bool fSuccess = false; /* ignored */
+ vrc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */, &fSuccess);
+ }
+ catch (std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ LogFlowThisFunc(("vrc=%Rrc\n", vrc));
+ return vrc;
+}
+
+int VBoxMainDriveInfo::updateFixedDrives() RT_NOEXCEPT
+{
+ LogFlowThisFunc(("entered\n"));
+ int vrc;
+ try
+ {
+ mFixedDriveList.clear();
+ bool fSuccess = false; /* ignored */
+ vrc = getDriveInfoFromCAM(&mFixedDriveList, Fixed, &fSuccess);
+ }
+ catch (std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ LogFlowThisFunc(("vrc=%Rrc\n", vrc));
+ return vrc;
+}
+
+static void strDeviceStringSCSI(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+ char szVendor[128];
+ cam_strvis((uint8_t *)szVendor, (const uint8_t *)pDevResult->inq_data.vendor,
+ sizeof(pDevResult->inq_data.vendor), sizeof(szVendor));
+ char szProduct[128];
+ cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->inq_data.product,
+ sizeof(pDevResult->inq_data.product), sizeof(szProduct));
+ dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
+}
+
+static void strDeviceStringATA(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+ char szProduct[256];
+ cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->ident_data.model,
+ sizeof(pDevResult->ident_data.model), sizeof(szProduct));
+ dvdCreateDeviceString("", szProduct, pszDesc, cbDesc);
+}
+
+static void strDeviceStringSEMB(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+ sep_identify_data *pSid = (sep_identify_data *)&pDevResult->ident_data;
+
+ char szVendor[128];
+ cam_strvis((uint8_t *)szVendor, (const uint8_t *)pSid->vendor_id,
+ sizeof(pSid->vendor_id), sizeof(szVendor));
+ char szProduct[128];
+ cam_strvis((uint8_t *)szProduct, (const uint8_t *)pSid->product_id,
+ sizeof(pSid->product_id), sizeof(szProduct));
+ dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
+}
+
+static void strDeviceStringMMCSD(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+ struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id,
+ pDevResult->target_lun, O_RDWR, NULL);
+ if (pDev == NULL)
+ {
+ Log(("Error while opening drive device. Error: %s\n", cam_errbuf));
+ return;
+ }
+
+ union ccb *pCcb = cam_getccb(pDev);
+ if (pCcb != NULL)
+ {
+ struct mmc_params mmcIdentData;
+ RT_ZERO(mmcIdentData);
+
+ struct ccb_dev_advinfo *pAdvi = &pCcb->cdai;
+ pAdvi->ccb_h.flags = CAM_DIR_IN;
+ pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO;
+ pAdvi->flags = CDAI_FLAG_NONE;
+ pAdvi->buftype = CDAI_TYPE_MMC_PARAMS;
+ pAdvi->bufsiz = sizeof(mmcIdentData);
+ pAdvi->buf = (uint8_t *)&mmcIdentData;
+
+ if (cam_send_ccb(pDev, pCcb) >= 0)
+ {
+ if (strlen((char *)mmcIdentData.model) > 0)
+ dvdCreateDeviceString("", (const char *)mmcIdentData.model, pszDesc, cbDesc);
+ else
+ dvdCreateDeviceString("", mmcIdentData.card_features & CARD_FEATURE_SDIO ? "SDIO card" : "Unknown card",
+ pszDesc, cbDesc);
+ }
+ else
+ Log(("error sending XPT_DEV_ADVINFO CCB\n"));
+
+ cam_freeccb(pCcb);
+ }
+ else
+ Log(("Could not allocate CCB\n"));
+ cam_close_device(pDev);
+}
+
+/** @returns boolean success indicator (true/false). */
+static int nvmeGetCData(struct cam_device *pDev, struct nvme_controller_data *pCData) RT_NOTHROW_DEF
+{
+ bool fSuccess = false;
+ union ccb *pCcb = cam_getccb(pDev);
+ if (pCcb != NULL)
+ {
+ struct ccb_dev_advinfo *pAdvi = &pCcb->cdai;
+ pAdvi->ccb_h.flags = CAM_DIR_IN;
+ pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO;
+ pAdvi->flags = CDAI_FLAG_NONE;
+ pAdvi->buftype = CDAI_TYPE_NVME_CNTRL;
+ pAdvi->bufsiz = sizeof(struct nvme_controller_data);
+ pAdvi->buf = (uint8_t *)pCData;
+ RT_BZERO(pAdvi->buf, pAdvi->bufsiz);
+
+ if (cam_send_ccb(pDev, pCcb) >= 0)
+ {
+ if (pAdvi->ccb_h.status == CAM_REQ_CMP)
+ fSuccess = true;
+ else
+ Log(("Got CAM error %#x\n", pAdvi->ccb_h.status));
+ }
+ else
+ Log(("Error sending XPT_DEV_ADVINFO CC\n"));
+ cam_freeccb(pCcb);
+ }
+ else
+ Log(("Could not allocate CCB\n"));
+ return fSuccess;
+}
+
+static void strDeviceStringNVME(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+ struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id,
+ pDevResult->target_lun, O_RDWR, NULL);
+ if (pDev)
+ {
+ struct nvme_controller_data CData;
+ if (nvmeGetCData(pDev, &CData))
+ {
+ char szVendor[128];
+ cam_strvis((uint8_t *)szVendor, CData.mn, sizeof(CData.mn), sizeof(szVendor));
+ char szProduct[128];
+ cam_strvis((uint8_t *)szProduct, CData.fr, sizeof(CData.fr), sizeof(szProduct));
+ dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
+ }
+ else
+ Log(("Error while getting NVME drive info\n"));
+ cam_close_device(pDev);
+ }
+ else
+ Log(("Error while opening drive device. Error: %s\n", cam_errbuf));
+}
+
+
+/**
+ * Search for available drives using the CAM layer.
+ *
+ * @returns iprt status code
+ * @param pList the list to append the drives found to
+ * @param enmDriveType search drives of specified type
+ * @param pfSuccess this will be set to true if we found at least one drive
+ * and to false otherwise. Optional.
+ */
+static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_DEF
+{
+ RTFILE hFileXpt = NIL_RTFILE;
+ int vrc = RTFileOpen(&hFileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(vrc))
+ {
+ union ccb DeviceCCB;
+ struct dev_match_pattern DeviceMatchPattern;
+ struct dev_match_result *paMatches = NULL;
+
+ RT_ZERO(DeviceCCB);
+ RT_ZERO(DeviceMatchPattern);
+
+ /* We want to get all devices. */
+ DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH;
+ DeviceCCB.ccb_h.path_id = CAM_XPT_PATH_ID;
+ DeviceCCB.ccb_h.target_id = CAM_TARGET_WILDCARD;
+ DeviceCCB.ccb_h.target_lun = CAM_LUN_WILDCARD;
+
+ /* Setup the pattern */
+ DeviceMatchPattern.type = DEV_MATCH_DEVICE;
+ DeviceMatchPattern.pattern.device_pattern.path_id = CAM_XPT_PATH_ID;
+ DeviceMatchPattern.pattern.device_pattern.target_id = CAM_TARGET_WILDCARD;
+ DeviceMatchPattern.pattern.device_pattern.target_lun = CAM_LUN_WILDCARD;
+ DeviceMatchPattern.pattern.device_pattern.flags = DEV_MATCH_INQUIRY;
+
+#if __FreeBSD_version >= 900000
+# define INQ_PAT data.inq_pat
+#else
+ #define INQ_PAT inq_pat
+#endif
+ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = enmDriveType == Fixed ? T_DIRECT
+ : enmDriveType == DVD ? T_CDROM : T_ANY;
+ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.media_type = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED;
+ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.vendor[0] = '*'; /* Matches anything */
+ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.product[0] = '*'; /* Matches anything */
+ DeviceMatchPattern.pattern.device_pattern.INQ_PAT.revision[0] = '*'; /* Matches anything */
+#undef INQ_PAT
+ DeviceCCB.cdm.num_patterns = 1;
+ DeviceCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
+ DeviceCCB.cdm.patterns = &DeviceMatchPattern;
+
+ /*
+ * Allocate the buffer holding the matches.
+ * We will allocate for 10 results and call
+ * CAM multiple times if we have more results.
+ */
+ paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result));
+ if (paMatches)
+ {
+ DeviceCCB.cdm.num_matches = 0;
+ DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result);
+ DeviceCCB.cdm.matches = paMatches;
+
+ do
+ {
+ vrc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
+ if (RT_FAILURE(vrc))
+ {
+ Log(("Error while querying available CD/DVD devices vrc=%Rrc\n", vrc));
+ break;
+ }
+
+ for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++)
+ {
+ if (paMatches[i].type == DEV_MATCH_DEVICE)
+ {
+ /*
+ * The result list can contain some empty entries with DEV_RESULT_UNCONFIGURED
+ * flag set, e.g. in case of T_DIRECT. Ignore them.
+ */
+ if ( (paMatches[i].result.device_result.flags & DEV_RESULT_UNCONFIGURED)
+ == DEV_RESULT_UNCONFIGURED)
+ continue;
+
+ /* We have the drive now but need the appropriate device node */
+ struct device_match_result *pDevResult = &paMatches[i].result.device_result;
+ union ccb PeriphCCB;
+ struct dev_match_pattern PeriphMatchPattern;
+ struct dev_match_result aPeriphMatches[2];
+ struct periph_match_result *pPeriphResult = NULL;
+ unsigned iPeriphMatch = 0;
+
+ RT_ZERO(PeriphCCB);
+ RT_ZERO(PeriphMatchPattern);
+ RT_ZERO(aPeriphMatches);
+
+ /* This time we only want the specific nodes for the device. */
+ PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH;
+ PeriphCCB.ccb_h.path_id = paMatches[i].result.device_result.path_id;
+ PeriphCCB.ccb_h.target_id = paMatches[i].result.device_result.target_id;
+ PeriphCCB.ccb_h.target_lun = paMatches[i].result.device_result.target_lun;
+
+ /* Setup the pattern */
+ PeriphMatchPattern.type = DEV_MATCH_PERIPH;
+ PeriphMatchPattern.pattern.periph_pattern.path_id = paMatches[i].result.device_result.path_id;
+ PeriphMatchPattern.pattern.periph_pattern.target_id = paMatches[i].result.device_result.target_id;
+ PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun;
+ PeriphMatchPattern.pattern.periph_pattern.flags = (periph_pattern_flags)( PERIPH_MATCH_PATH
+ | PERIPH_MATCH_TARGET
+ | PERIPH_MATCH_LUN);
+ PeriphCCB.cdm.num_patterns = 1;
+ PeriphCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
+ PeriphCCB.cdm.patterns = &PeriphMatchPattern;
+ PeriphCCB.cdm.num_matches = 0;
+ PeriphCCB.cdm.match_buf_len = sizeof(aPeriphMatches);
+ PeriphCCB.cdm.matches = aPeriphMatches;
+
+ do
+ {
+ vrc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
+ if (RT_FAILURE(vrc))
+ {
+ Log(("Error while querying available periph devices vrc=%Rrc\n", vrc));
+ break;
+ }
+
+ for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
+ {
+ /* Ignore "passthrough mode" paths */
+ if ( aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH
+ && strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "pass"))
+ {
+ pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
+ break; /* We found the periph device */
+ }
+ }
+
+ if (iPeriphMatch < PeriphCCB.cdm.num_matches)
+ break;
+
+ } while ( DeviceCCB.ccb_h.status == CAM_REQ_CMP
+ && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE);
+
+ if (pPeriphResult)
+ {
+ char szPath[RTPATH_MAX];
+ RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
+ pPeriphResult->periph_name, pPeriphResult->unit_number);
+
+ char szDesc[256] = { 0 };
+ switch (pDevResult->protocol)
+ {
+ case PROTO_SCSI: strDeviceStringSCSI( pDevResult, szDesc, sizeof(szDesc)); break;
+ case PROTO_ATA: strDeviceStringATA( pDevResult, szDesc, sizeof(szDesc)); break;
+ case PROTO_MMCSD: strDeviceStringMMCSD(pDevResult, szDesc, sizeof(szDesc)); break;
+ case PROTO_SEMB: strDeviceStringSEMB( pDevResult, szDesc, sizeof(szDesc)); break;
+ case PROTO_NVME: strDeviceStringNVME( pDevResult, szDesc, sizeof(szDesc)); break;
+ default: break;
+ }
+
+ try
+ {
+ pList->push_back(DriveInfo(szPath, "", szDesc));
+ }
+ catch (std::bad_alloc &)
+ {
+ pList->clear();
+ vrc = VERR_NO_MEMORY;
+ break;
+ }
+ if (pfSuccess)
+ *pfSuccess = true;
+ }
+ }
+ }
+ } while ( DeviceCCB.ccb_h.status == CAM_REQ_CMP
+ && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE
+ && RT_SUCCESS(vrc));
+
+ RTMemFree(paMatches);
+ }
+ else
+ vrc = VERR_NO_MEMORY;
+
+ RTFileClose(hFileXpt);
+ }
+
+ return vrc;
+}
+
+
+/**
+ * Extract the names of drives from an environment variable and add them to a
+ * list if they are valid.
+ *
+ * @returns iprt status code
+ * @param pcszVar the name of the environment variable. The variable
+ * value should be a list of device node names, separated
+ * by ':' characters.
+ * @param pList the list to append the drives found to
+ * @param isDVD are we looking for DVD drives or for floppies?
+ * @param pfSuccess this will be set to true if we found at least one drive
+ * and to false otherwise. Optional.
+ *
+ * @note This is duplicated in HostHardwareLinux.cpp.
+ */
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF
+{
+ AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
+ AssertPtrReturn(pList, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
+ LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar, pList, isDVD, pfSuccess));
+ int vrc = VINF_SUCCESS;
+ bool success = false;
+ char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
+
+ try
+ {
+ char *pszCurrent = pszFreeMe;
+ while (pszCurrent && *pszCurrent != '\0')
+ {
+ char *pszNext = strchr(pszCurrent, ':');
+ if (pszNext)
+ *pszNext++ = '\0';
+
+ char szReal[RTPATH_MAX];
+ char szDesc[1] = "", szUdi[1] = ""; /* differs on freebsd because no devValidateDevice */
+ if ( RT_SUCCESS(RTPathReal(pszCurrent, szReal, sizeof(szReal)))
+ /*&& devValidateDevice(szReal, isDVD, NULL, szDesc, sizeof(szDesc), szUdi, sizeof(szUdi)) - linux only */)
+ {
+ pList->push_back(DriveInfo(szReal, szUdi, szDesc));
+ success = true;
+ }
+ pszCurrent = pszNext;
+ }
+ if (pfSuccess != NULL)
+ *pfSuccess = success;
+ }
+ catch (std::bad_alloc &)
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+ RTStrFree(pszFreeMe);
+ LogFlowFunc(("vrc=%Rrc, success=%d\n", vrc, success));
+ return vrc;
+}
+
diff --git a/src/VBox/Main/src-server/freebsd/Makefile.kup b/src/VBox/Main/src-server/freebsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Main/src-server/freebsd/Makefile.kup
diff --git a/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp b/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp
new file mode 100644
index 00000000..68fe2485
--- /dev/null
+++ b/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp
@@ -0,0 +1,458 @@
+/* $Id: NetIf-freebsd.cpp $ */
+/** @file
+ * Main - NetIfList, FreeBSD implementation.
+ */
+
+/*
+ * Copyright (C) 2008-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
+ */
+
+/*
+ * Original (C) 2009 Fredrik Lindberg <fli@shapeshifter.se>. Contributed
+ * to VirtualBox under the MIT license by the author.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_HOST
+#include <sys/types.h>
+
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include <net/route.h>
+/*
+ * route.h includes net/radix.h which for some reason defines Free as a wrapper
+ * around free. This collides with Free defined in xpcom/include/nsIMemory.h
+ * Undefine it and hope for the best
+ */
+#undef Free
+
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <list>
+
+#include "HostNetworkInterfaceImpl.h"
+#include "netif.h"
+#include "LoggingNew.h"
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+void extractAddresses(int iAddrMask, caddr_t cp, caddr_t cplim, struct sockaddr **pAddresses)
+{
+ struct sockaddr *sa;
+
+ for (int i = 0; i < RTAX_MAX && cp < cplim; i++) {
+ if (!(iAddrMask & (1 << i)))
+ continue;
+
+ sa = (struct sockaddr *)cp;
+
+ pAddresses[i] = sa;
+
+ ADVANCE(cp, sa);
+ }
+}
+
+static int getDefaultIfaceIndex(unsigned short *pu16Index, int family)
+{
+ size_t cbNeeded;
+ char *pBuf, *pNext;
+ int aiMib[6];
+ struct sockaddr *addresses[RTAX_MAX];
+ aiMib[0] = CTL_NET;
+ aiMib[1] = PF_ROUTE;
+ aiMib[2] = 0;
+ aiMib[3] = family; /* address family */
+ aiMib[4] = NET_RT_DUMP;
+ aiMib[5] = 0;
+
+ if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0)
+ {
+ Log(("getDefaultIfaceIndex: Failed to get estimate for list size (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((pBuf = (char*)malloc(cbNeeded)) == NULL)
+ return VERR_NO_MEMORY;
+ if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0)
+ {
+ free(pBuf);
+ Log(("getDefaultIfaceIndex: Failed to retrieve interface table (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ char *pEnd = pBuf + cbNeeded;
+ struct rt_msghdr *pRtMsg;
+ for (pNext = pBuf; pNext < pEnd; pNext += pRtMsg->rtm_msglen)
+ {
+ pRtMsg = (struct rt_msghdr *)pNext;
+
+ if (pRtMsg->rtm_type != RTM_GET)
+ {
+ Log(("getDefaultIfaceIndex: Got message %u while expecting %u.\n",
+ pRtMsg->rtm_type, RTM_GET));
+ //vrc = VERR_INTERNAL_ERROR;
+ continue;
+ }
+ if ((char*)(pRtMsg + 1) < pEnd)
+ {
+ /* Extract addresses from the message. */
+ extractAddresses(pRtMsg->rtm_addrs, (char *)(pRtMsg + 1),
+ pRtMsg->rtm_msglen + (char *)pRtMsg, addresses);
+ if ((pRtMsg->rtm_addrs & RTA_DST))
+ {
+ if (addresses[RTAX_DST]->sa_family != AF_INET)
+ continue;
+ struct sockaddr_in *addr = (struct sockaddr_in *)addresses[RTAX_DST];
+ struct sockaddr_in *mask = (struct sockaddr_in *)addresses[RTAX_NETMASK];
+ if ((addr->sin_addr.s_addr == INADDR_ANY) &&
+ mask &&
+ (ntohl(mask->sin_addr.s_addr) == 0L ||
+ mask->sin_len == 0))
+ {
+ *pu16Index = pRtMsg->rtm_index;
+ free(pBuf);
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ free(pBuf);
+ return VERR_INTERNAL_ERROR;
+
+}
+
+void extractAddressesToNetInfo(int iAddrMask, caddr_t cp, caddr_t cplim, PNETIFINFO pInfo)
+{
+ struct sockaddr *addresses[RTAX_MAX];
+
+ extractAddresses(iAddrMask, cp, cplim, addresses);
+ switch (addresses[RTAX_IFA]->sa_family)
+ {
+ case AF_INET:
+ if (!pInfo->IPAddress.u)
+ {
+ pInfo->IPAddress.u = ((struct sockaddr_in *)addresses[RTAX_IFA])->sin_addr.s_addr;
+ pInfo->IPNetMask.u = ((struct sockaddr_in *)addresses[RTAX_NETMASK])->sin_addr.s_addr;
+ }
+ break;
+ case AF_INET6:
+ if (!pInfo->IPv6Address.s.Lo && !pInfo->IPv6Address.s.Hi)
+ {
+ memcpy(pInfo->IPv6Address.au8,
+ ((struct sockaddr_in6 *)addresses[RTAX_IFA])->sin6_addr.__u6_addr.__u6_addr8,
+ sizeof(pInfo->IPv6Address));
+ memcpy(pInfo->IPv6NetMask.au8,
+ ((struct sockaddr_in6 *)addresses[RTAX_NETMASK])->sin6_addr.__u6_addr.__u6_addr8,
+ sizeof(pInfo->IPv6NetMask));
+ }
+ break;
+ default:
+ Log(("NetIfList: Unsupported address family: %u\n", addresses[RTAX_IFA]->sa_family));
+ break;
+ }
+}
+
+
+static bool isWireless(const char *pszName)
+{
+ bool fWireless = false;
+ int iSock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (iSock >= 0)
+ {
+ struct ieee80211req WReq;
+ uint8_t abData[32];
+
+ RT_ZERO(WReq);
+ strncpy(WReq.i_name, pszName, sizeof(WReq.i_name));
+ WReq.i_type = IEEE80211_IOC_SSID;
+ WReq.i_val = -1;
+ WReq.i_data = abData;
+ WReq.i_len = sizeof(abData);
+
+ fWireless = ioctl(iSock, SIOCG80211, &WReq) >= 0;
+ close(iSock);
+ }
+
+ return fWireless;
+}
+
+int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list)
+{
+ int vrc = VINF_SUCCESS;
+ size_t cbNeeded;
+ char *pBuf, *pNext;
+ int aiMib[6];
+ unsigned short u16DefaultIface = 0; /* shut up gcc. */
+ bool fDefaultIfaceExistent = true;
+
+ /* Get the index of the interface associated with default route. */
+ vrc = getDefaultIfaceIndex(&u16DefaultIface, PF_INET);
+ if (RT_FAILURE(vrc))
+ {
+ fDefaultIfaceExistent = false;
+ vrc = VINF_SUCCESS;
+ }
+
+ aiMib[0] = CTL_NET;
+ aiMib[1] = PF_ROUTE;
+ aiMib[2] = 0;
+ aiMib[3] = 0; /* address family */
+ aiMib[4] = NET_RT_IFLIST;
+ aiMib[5] = 0;
+
+ if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0)
+ {
+ Log(("NetIfList: Failed to get estimate for list size (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((pBuf = (char*)malloc(cbNeeded)) == NULL)
+ return VERR_NO_MEMORY;
+ if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0)
+ {
+ free(pBuf);
+ Log(("NetIfList: Failed to retrieve interface table (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock < 0)
+ {
+ free(pBuf);
+ Log(("NetIfList: socket() -> %d\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ char *pEnd = pBuf + cbNeeded;
+ for (pNext = pBuf; pNext < pEnd;)
+ {
+ struct if_msghdr *pIfMsg = (struct if_msghdr *)pNext;
+
+ if (pIfMsg->ifm_type != RTM_IFINFO)
+ {
+ Log(("NetIfList: Got message %u while expecting %u.\n",
+ pIfMsg->ifm_type, RTM_IFINFO));
+ vrc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ struct sockaddr_dl *pSdl = (struct sockaddr_dl *)(pIfMsg + 1);
+
+ size_t cbNameLen = pSdl->sdl_nlen + 1;
+ PNETIFINFO pNew = (PNETIFINFO)RTMemAllocZ(RT_UOFFSETOF_DYN(NETIFINFO, szName[cbNameLen]));
+ if (!pNew)
+ {
+ vrc = VERR_NO_MEMORY;
+ break;
+ }
+ memcpy(pNew->MACAddress.au8, LLADDR(pSdl), sizeof(pNew->MACAddress.au8));
+ pNew->enmMediumType = NETIF_T_ETHERNET;
+ Assert(sizeof(pNew->szShortName) >= cbNameLen);
+ strlcpy(pNew->szShortName, pSdl->sdl_data, cbNameLen);
+ strlcpy(pNew->szName, pSdl->sdl_data, cbNameLen);
+ /* Generate UUID from name and MAC address. */
+ RTUUID uuid;
+ RTUuidClear(&uuid);
+ memcpy(&uuid, pNew->szShortName, RT_MIN(cbNameLen, sizeof(uuid)));
+ uuid.Gen.u8ClockSeqHiAndReserved = (uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ uuid.Gen.u16TimeHiAndVersion = (uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ memcpy(uuid.Gen.au8Node, pNew->MACAddress.au8, sizeof(uuid.Gen.au8Node));
+ pNew->Uuid = uuid;
+
+ pNext += pIfMsg->ifm_msglen;
+ while (pNext < pEnd)
+ {
+ struct ifa_msghdr *pIfAddrMsg = (struct ifa_msghdr *)pNext;
+
+ if (pIfAddrMsg->ifam_type != RTM_NEWADDR)
+ break;
+ extractAddressesToNetInfo(pIfAddrMsg->ifam_addrs,
+ (char *)(pIfAddrMsg + 1),
+ pIfAddrMsg->ifam_msglen + (char *)pIfAddrMsg,
+ pNew);
+ pNext += pIfAddrMsg->ifam_msglen;
+ }
+
+ if (pSdl->sdl_type == IFT_ETHER || pSdl->sdl_type == IFT_L2VLAN)
+ {
+ struct ifreq IfReq;
+ RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName);
+ if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0)
+ {
+ Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno));
+ pNew->enmStatus = NETIF_S_UNKNOWN;
+ }
+ else
+ pNew->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN;
+
+ HostNetworkInterfaceType_T enmType;
+ if (strncmp(pNew->szName, RT_STR_TUPLE("vboxnet")))
+ enmType = HostNetworkInterfaceType_Bridged;
+ else
+ enmType = HostNetworkInterfaceType_HostOnly;
+
+ pNew->fWireless = isWireless(pNew->szName);
+
+ ComObjPtr<HostNetworkInterface> IfObj;
+ IfObj.createObject();
+ if (SUCCEEDED(IfObj->init(Bstr(pNew->szName), enmType, pNew)))
+ {
+ /* Make sure the default interface gets to the beginning. */
+ if ( fDefaultIfaceExistent
+ && pIfMsg->ifm_index == u16DefaultIface)
+ list.push_front(IfObj);
+ else
+ list.push_back(IfObj);
+ }
+ }
+ RTMemFree(pNew);
+ }
+
+ close(sock);
+ free(pBuf);
+ return vrc;
+}
+
+int NetIfGetConfigByName(PNETIFINFO pInfo)
+{
+ int vrc = VINF_SUCCESS;
+ size_t cbNeeded;
+ char *pBuf, *pNext;
+ int aiMib[6];
+
+ aiMib[0] = CTL_NET;
+ aiMib[1] = PF_ROUTE;
+ aiMib[2] = 0;
+ aiMib[3] = 0; /* address family */
+ aiMib[4] = NET_RT_IFLIST;
+ aiMib[5] = 0;
+
+ if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0)
+ {
+ Log(("NetIfList: Failed to get estimate for list size (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((pBuf = (char*)malloc(cbNeeded)) == NULL)
+ return VERR_NO_MEMORY;
+ if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0)
+ {
+ free(pBuf);
+ Log(("NetIfList: Failed to retrieve interface table (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock < 0)
+ {
+ free(pBuf);
+ Log(("NetIfList: socket() -> %d\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ char *pEnd = pBuf + cbNeeded;
+ for (pNext = pBuf; pNext < pEnd;)
+ {
+ struct if_msghdr *pIfMsg = (struct if_msghdr *)pNext;
+
+ if (pIfMsg->ifm_type != RTM_IFINFO)
+ {
+ Log(("NetIfList: Got message %u while expecting %u.\n",
+ pIfMsg->ifm_type, RTM_IFINFO));
+ vrc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ struct sockaddr_dl *pSdl = (struct sockaddr_dl *)(pIfMsg + 1);
+
+ bool fSkip = !!strcmp(pInfo->szShortName, pSdl->sdl_data);
+
+ pNext += pIfMsg->ifm_msglen;
+ while (pNext < pEnd)
+ {
+ struct ifa_msghdr *pIfAddrMsg = (struct ifa_msghdr *)pNext;
+
+ if (pIfAddrMsg->ifam_type != RTM_NEWADDR)
+ break;
+ if (!fSkip)
+ extractAddressesToNetInfo(pIfAddrMsg->ifam_addrs,
+ (char *)(pIfAddrMsg + 1),
+ pIfAddrMsg->ifam_msglen + (char *)pIfAddrMsg,
+ pInfo);
+ pNext += pIfAddrMsg->ifam_msglen;
+ }
+
+ if (!fSkip && (pSdl->sdl_type == IFT_ETHER || pSdl->sdl_type == IFT_L2VLAN))
+ {
+ size_t cbNameLen = pSdl->sdl_nlen + 1;
+ memcpy(pInfo->MACAddress.au8, LLADDR(pSdl), sizeof(pInfo->MACAddress.au8));
+ pInfo->enmMediumType = NETIF_T_ETHERNET;
+ /* Generate UUID from name and MAC address. */
+ RTUUID uuid;
+ RTUuidClear(&uuid);
+ memcpy(&uuid, pInfo->szShortName, RT_MIN(cbNameLen, sizeof(uuid)));
+ uuid.Gen.u8ClockSeqHiAndReserved = (uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ uuid.Gen.u16TimeHiAndVersion = (uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ memcpy(uuid.Gen.au8Node, pInfo->MACAddress.au8, sizeof(uuid.Gen.au8Node));
+ pInfo->Uuid = uuid;
+
+ struct ifreq IfReq;
+ RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pInfo->szShortName);
+ if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0)
+ {
+ Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno));
+ pInfo->enmStatus = NETIF_S_UNKNOWN;
+ }
+ else
+ pInfo->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN;
+
+ return VINF_SUCCESS;
+ }
+ }
+ close(sock);
+ free(pBuf);
+ return vrc;
+}
+
+/**
+ * Retrieve the physical link speed in megabits per second. If the interface is
+ * not up or otherwise unavailable the zero speed is returned.
+ *
+ * @returns VBox status code.
+ *
+ * @param pcszIfName Interface name.
+ * @param puMbits Where to store the link speed.
+ */
+int NetIfGetLinkSpeed(const char * /*pcszIfName*/, uint32_t * /*puMbits*/)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
diff --git a/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp
new file mode 100644
index 00000000..fc55de17
--- /dev/null
+++ b/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp
@@ -0,0 +1,128 @@
+/* $Id: PerformanceFreeBSD.cpp $ */
+/** @file
+ * VirtualBox Performance Collector, FreeBSD Specialization.
+ */
+
+/*
+ * Copyright (C) 2008-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
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include "Performance.h"
+
+namespace pm {
+
+class CollectorFreeBSD : public CollectorHAL
+{
+public:
+ virtual int getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle);
+ virtual int getHostCpuMHz(ULONG *mhz);
+ virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
+ virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel);
+ virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
+};
+
+
+CollectorHAL *createHAL()
+{
+ return new CollectorFreeBSD();
+}
+
+int CollectorFreeBSD::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+int CollectorFreeBSD::getHostCpuMHz(ULONG *mhz)
+{
+ int CpuMHz = 0;
+ size_t cbParameter = sizeof(CpuMHz);
+
+ /** @todo Howto support more than one CPU? */
+ if (sysctlbyname("dev.cpu.0.freq", &CpuMHz, &cbParameter, NULL, 0))
+ return VERR_NOT_SUPPORTED;
+
+ *mhz = CpuMHz;
+
+ return VINF_SUCCESS;
+}
+
+int CollectorFreeBSD::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
+{
+ int vrc = VINF_SUCCESS;
+ u_long cbMemPhys = 0;
+ u_int cPagesMemFree = 0;
+ u_int cPagesMemInactive = 0;
+ u_int cPagesMemCached = 0;
+ u_int cPagesMemUsed = 0;
+ int cbPage = 0;
+ size_t cbParameter = sizeof(cbMemPhys);
+ int cProcessed = 0;
+
+ if (!sysctlbyname("hw.physmem", &cbMemPhys, &cbParameter, NULL, 0))
+ cProcessed++;
+
+ cbParameter = sizeof(cPagesMemFree);
+ if (!sysctlbyname("vm.stats.vm.v_free_count", &cPagesMemFree, &cbParameter, NULL, 0))
+ cProcessed++;
+ cbParameter = sizeof(cPagesMemUsed);
+ if (!sysctlbyname("vm.stats.vm.v_active_count", &cPagesMemUsed, &cbParameter, NULL, 0))
+ cProcessed++;
+ cbParameter = sizeof(cPagesMemInactive);
+ if (!sysctlbyname("vm.stats.vm.v_inactive_count", &cPagesMemInactive, &cbParameter, NULL, 0))
+ cProcessed++;
+ cbParameter = sizeof(cPagesMemCached);
+ if (!sysctlbyname("vm.stats.vm.v_cache_count", &cPagesMemCached, &cbParameter, NULL, 0))
+ cProcessed++;
+ cbParameter = sizeof(cbPage);
+ if (!sysctlbyname("hw.pagesize", &cbPage, &cbParameter, NULL, 0))
+ cProcessed++;
+
+ if (cProcessed == 6)
+ {
+ *total = cbMemPhys / _1K;
+ *used = cPagesMemUsed * (cbPage / _1K);
+ *available = (cPagesMemFree + cPagesMemInactive + cPagesMemCached ) * (cbPage / _1K);
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+
+ return vrc;
+}
+
+int CollectorFreeBSD::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+int CollectorFreeBSD::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+int getDiskListByFs(const char *name, DiskList& list)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+} /* namespace pm */
+
diff --git a/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp
new file mode 100644
index 00000000..79543717
--- /dev/null
+++ b/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp
@@ -0,0 +1,353 @@
+/* $Id: USBProxyBackendFreeBSD.cpp $ */
+/** @file
+ * VirtualBox USB Proxy Service, FreeBSD Specialization.
+ */
+
+/*
+ * Copyright (C) 2005-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_MAIN_USBPROXYBACKEND
+#include "USBProxyBackend.h"
+#include "LoggingNew.h"
+
+#include <VBox/usb.h>
+#include <VBox/usblib.h>
+#include <iprt/errcore.h>
+
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+
+
+/**
+ * Initialize data members.
+ */
+USBProxyBackendFreeBSD::USBProxyBackendFreeBSD()
+ : USBProxyBackend(), mNotifyEventSem(NIL_RTSEMEVENT)
+{
+ LogFlowThisFunc(("\n"));
+}
+
+USBProxyBackendFreeBSD::~USBProxyBackendFreeBSD()
+{
+ LogFlowThisFunc(("\n"));
+}
+
+/**
+ * Initializes the object (called right after construction).
+ *
+ * @returns S_OK on success and non-fatal failures, some COM error otherwise.
+ */
+int USBProxyBackendFreeBSD::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
+ const com::Utf8Str &strAddress, bool fLoadingSettings)
+{
+ USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
+
+ unconst(m_strBackend) = Utf8Str("host");
+
+ /*
+ * Create semaphore.
+ */
+ int vrc = RTSemEventCreate(&mNotifyEventSem);
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /*
+ * Start the poller thread.
+ */
+ start();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Stop all service threads and free the device chain.
+ */
+void USBProxyBackendFreeBSD::uninit()
+{
+ LogFlowThisFunc(("\n"));
+
+ /*
+ * Stop the service.
+ */
+ if (isActive())
+ stop();
+
+ RTSemEventDestroy(mNotifyEventSem);
+ mNotifyEventSem = NULL;
+ USBProxyBackend::uninit();
+}
+
+
+int USBProxyBackendFreeBSD::captureDevice(HostUSBDevice *aDevice)
+{
+ AssertReturn(aDevice, VERR_GENERAL_FAILURE);
+ AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
+
+ AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
+
+ /*
+ * Don't think we need to do anything when the device is held... fake it.
+ */
+ Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
+ devLock.release();
+ interruptWait();
+
+ return VINF_SUCCESS;
+}
+
+
+int USBProxyBackendFreeBSD::releaseDevice(HostUSBDevice *aDevice)
+{
+ AssertReturn(aDevice, VERR_GENERAL_FAILURE);
+ AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
+
+ AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
+
+ /*
+ * We're not really holding it atm., just fake it.
+ */
+ Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
+ devLock.release();
+ interruptWait();
+
+ return VINF_SUCCESS;
+}
+
+
+bool USBProxyBackendFreeBSD::isFakeUpdateRequired()
+{
+ return true;
+}
+
+
+int USBProxyBackendFreeBSD::wait(RTMSINTERVAL aMillies)
+{
+ return RTSemEventWait(mNotifyEventSem, aMillies < 1000 ? 1000 : 5000);
+}
+
+
+int USBProxyBackendFreeBSD::interruptWait(void)
+{
+ return RTSemEventSignal(mNotifyEventSem);
+}
+
+
+/**
+ * Dumps a USBDEVICE structure to the log using LogLevel 3.
+ * @param pDev The structure to log.
+ * @todo This is really common code.
+ */
+DECLINLINE(void) usbLogDevice(PUSBDEVICE pDev)
+{
+ NOREF(pDev);
+
+ Log3(("USB device:\n"));
+ Log3(("Product: %s (%x)\n", pDev->pszProduct, pDev->idProduct));
+ Log3(("Manufacturer: %s (Vendor ID %x)\n", pDev->pszManufacturer, pDev->idVendor));
+ Log3(("Serial number: %s (%llx)\n", pDev->pszSerialNumber, pDev->u64SerialHash));
+ Log3(("Device revision: %d\n", pDev->bcdDevice));
+ Log3(("Device class: %x\n", pDev->bDeviceClass));
+ Log3(("Device subclass: %x\n", pDev->bDeviceSubClass));
+ Log3(("Device protocol: %x\n", pDev->bDeviceProtocol));
+ Log3(("USB version number: %d\n", pDev->bcdUSB));
+ Log3(("Device speed: %s\n",
+ pDev->enmSpeed == USBDEVICESPEED_UNKNOWN ? "unknown"
+ : pDev->enmSpeed == USBDEVICESPEED_LOW ? "1.5 MBit/s"
+ : pDev->enmSpeed == USBDEVICESPEED_FULL ? "12 MBit/s"
+ : pDev->enmSpeed == USBDEVICESPEED_HIGH ? "480 MBit/s"
+ : pDev->enmSpeed == USBDEVICESPEED_VARIABLE ? "variable"
+ : "invalid"));
+ Log3(("Number of configurations: %d\n", pDev->bNumConfigurations));
+ Log3(("Bus number: %d\n", pDev->bBus));
+ Log3(("Port number: %d\n", pDev->bPort));
+ Log3(("Device number: %d\n", pDev->bDevNum));
+ Log3(("Device state: %s\n",
+ pDev->enmState == USBDEVICESTATE_UNSUPPORTED ? "unsupported"
+ : pDev->enmState == USBDEVICESTATE_USED_BY_HOST ? "in use by host"
+ : pDev->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "in use by host, possibly capturable"
+ : pDev->enmState == USBDEVICESTATE_UNUSED ? "not in use"
+ : pDev->enmState == USBDEVICESTATE_HELD_BY_PROXY ? "held by proxy"
+ : pDev->enmState == USBDEVICESTATE_USED_BY_GUEST ? "used by guest"
+ : "invalid"));
+ Log3(("OS device address: %s\n", pDev->pszAddress));
+}
+
+
+PUSBDEVICE USBProxyBackendFreeBSD::getDevices(void)
+{
+ PUSBDEVICE pDevices = NULL;
+ int FileUsb = 0;
+ int iBus = 0;
+ int iAddr = 1;
+ int vrc = VINF_SUCCESS;
+ char *pszDevicePath = NULL;
+ uint32_t PlugTime = 0;
+
+ for (;;)
+ {
+ vrc = RTStrAPrintf(&pszDevicePath, "/dev/%s%d.%d", USB_GENERIC_NAME, iBus, iAddr);
+ if (RT_FAILURE(vrc))
+ break;
+
+ LogFlowFunc((": Opening %s\n", pszDevicePath));
+
+ FileUsb = open(pszDevicePath, O_RDONLY);
+ if (FileUsb < 0)
+ {
+ RTStrFree(pszDevicePath);
+
+ if ((errno == ENOENT) && (iAddr > 1))
+ {
+ iAddr = 1;
+ iBus++;
+ continue;
+ }
+ else if (errno == EACCES)
+ {
+ /* Skip devices without the right permission. */
+ iAddr++;
+ continue;
+ }
+ else
+ break;
+ }
+
+ LogFlowFunc((": %s opened successfully\n", pszDevicePath));
+
+ struct usb_device_info UsbDevInfo;
+ RT_ZERO(UsbDevInfo);
+
+ vrc = ioctl(FileUsb, USB_GET_DEVICEINFO, &UsbDevInfo);
+ if (vrc < 0)
+ {
+ LogFlowFunc((": Error querying device info vrc=%Rrc\n", RTErrConvertFromErrno(errno)));
+ close(FileUsb);
+ RTStrFree(pszDevicePath);
+ break;
+ }
+
+ /* Filter out hubs */
+ if (UsbDevInfo.udi_class != 0x09)
+ {
+ PUSBDEVICE pDevice = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
+ if (!pDevice)
+ {
+ close(FileUsb);
+ RTStrFree(pszDevicePath);
+ break;
+ }
+
+ pDevice->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
+ pDevice->bBus = UsbDevInfo.udi_bus;
+ pDevice->bPort = UsbDevInfo.udi_hubport;
+ pDevice->bDeviceClass = UsbDevInfo.udi_class;
+ pDevice->bDeviceSubClass = UsbDevInfo.udi_subclass;
+ pDevice->bDeviceProtocol = UsbDevInfo.udi_protocol;
+ pDevice->bNumConfigurations = UsbDevInfo.udi_config_no;
+ pDevice->idVendor = UsbDevInfo.udi_vendorNo;
+ pDevice->idProduct = UsbDevInfo.udi_productNo;
+ pDevice->bDevNum = UsbDevInfo.udi_index;
+
+ switch (UsbDevInfo.udi_speed)
+ {
+ case USB_SPEED_LOW:
+ pDevice->enmSpeed = USBDEVICESPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ pDevice->enmSpeed = USBDEVICESPEED_FULL;
+ break;
+ case USB_SPEED_HIGH:
+ pDevice->enmSpeed = USBDEVICESPEED_HIGH;
+ break;
+ case USB_SPEED_SUPER:
+ pDevice->enmSpeed = USBDEVICESPEED_SUPER;
+ break;
+ case USB_SPEED_VARIABLE:
+ pDevice->enmSpeed = USBDEVICESPEED_VARIABLE;
+ break;
+ default:
+ pDevice->enmSpeed = USBDEVICESPEED_UNKNOWN;
+ break;
+ }
+
+ if (UsbDevInfo.udi_vendor[0] != '\0')
+ {
+ USBLibPurgeEncoding(UsbDevInfo.udi_vendor);
+ pDevice->pszManufacturer = RTStrDupN(UsbDevInfo.udi_vendor, sizeof(UsbDevInfo.udi_vendor));
+ }
+
+ if (UsbDevInfo.udi_product[0] != '\0')
+ {
+ USBLibPurgeEncoding(UsbDevInfo.udi_product);
+ pDevice->pszProduct = RTStrDupN(UsbDevInfo.udi_product, sizeof(UsbDevInfo.udi_product));
+ }
+
+ if (UsbDevInfo.udi_serial[0] != '\0')
+ {
+ USBLibPurgeEncoding(UsbDevInfo.udi_serial);
+ pDevice->pszSerialNumber = RTStrDupN(UsbDevInfo.udi_serial, sizeof(UsbDevInfo.udi_serial));
+ pDevice->u64SerialHash = USBLibHashSerial(UsbDevInfo.udi_serial);
+ }
+ vrc = ioctl(FileUsb, USB_GET_PLUGTIME, &PlugTime);
+ if (vrc == 0)
+ pDevice->u64SerialHash += PlugTime;
+
+ pDevice->pszAddress = RTStrDup(pszDevicePath);
+ pDevice->pszBackend = RTStrDup("host");
+
+ usbLogDevice(pDevice);
+
+ pDevice->pNext = pDevices;
+ if (pDevices)
+ pDevices->pPrev = pDevice;
+ pDevices = pDevice;
+ }
+ close(FileUsb);
+ RTStrFree(pszDevicePath);
+ iAddr++;
+ }
+
+ return pDevices;
+}