diff options
Diffstat (limited to 'src/VBox/Main/src-client/ConsoleImpl2.cpp')
-rw-r--r-- | src/VBox/Main/src-client/ConsoleImpl2.cpp | 6915 |
1 files changed, 6915 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/ConsoleImpl2.cpp b/src/VBox/Main/src-client/ConsoleImpl2.cpp new file mode 100644 index 00000000..71e1bda2 --- /dev/null +++ b/src/VBox/Main/src-client/ConsoleImpl2.cpp @@ -0,0 +1,6915 @@ +/* $Id: ConsoleImpl2.cpp $ */ +/** @file + * VBox Console COM Class implementation - VM Configuration Bits. + * + * @remark We've split out the code that the 64-bit VC++ v8 compiler finds + * problematic to optimize so we can disable optimizations and later, + * perhaps, find a real solution for it (like rewriting the code and + * to stop resemble a tonne of spaghetti). + */ + +/* + * Copyright (C) 2006-2022 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_CONSOLE +#include "LoggingNew.h" + +// VBoxNetCfg-win.h needs winsock2.h and thus MUST be included before any other +// header file includes Windows.h. +#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) +# include <VBox/VBoxNetCfg-win.h> +#endif + +#include "ConsoleImpl.h" +#include "DisplayImpl.h" +#include "NvramStoreImpl.h" +#ifdef VBOX_WITH_DRAG_AND_DROP +# include "GuestImpl.h" +# include "GuestDnDPrivate.h" +#endif +#include "VMMDev.h" +#include "Global.h" +#ifdef VBOX_WITH_PCI_PASSTHROUGH +# include "PCIRawDevImpl.h" +#endif + +// generated header +#include "SchemaDefs.h" + +#include "AutoCaller.h" + +#include <iprt/base64.h> +#include <iprt/buildconfig.h> +#include <iprt/ctype.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/system.h> +#include <iprt/cpp/exception.h> +#if 0 /* enable to play with lots of memory. */ +# include <iprt/env.h> +#endif +#include <iprt/stream.h> + +#include <iprt/http.h> +#include <iprt/socket.h> +#include <iprt/uri.h> + +#include <VBox/vmm/vmmr3vtable.h> +#include <VBox/vmm/vmapi.h> +#include <VBox/err.h> +#include <VBox/param.h> +#include <VBox/settings.h> /* For MachineConfigFile::getHostDefaultAudioDriver(). */ +#include <VBox/vmm/pdmapi.h> /* For PDMR3DriverAttach/PDMR3DriverDetach. */ +#include <VBox/vmm/pdmusb.h> /* For PDMR3UsbCreateEmulatedDevice. */ +#include <VBox/vmm/pdmdev.h> /* For PDMAPICMODE enum. */ +#include <VBox/vmm/pdmstorageifs.h> +#include <VBox/vmm/gcm.h> +#include <VBox/version.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD +# include <VBox/HostServices/VBoxClipboardSvc.h> +#endif +#ifdef VBOX_WITH_GUEST_PROPS +# include <VBox/HostServices/GuestPropertySvc.h> +# include <VBox/com/defs.h> +# include <VBox/com/array.h> +# include <vector> +#endif /* VBOX_WITH_GUEST_PROPS */ +#include <VBox/intnet.h> + +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/array.h> + +#ifdef VBOX_WITH_NETFLT +# if defined(RT_OS_SOLARIS) +# include <zone.h> +# elif defined(RT_OS_LINUX) +# include <unistd.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <linux/types.h> +# include <linux/if.h> +# elif defined(RT_OS_FREEBSD) +# include <unistd.h> +# include <sys/types.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <net/if.h> +# include <net80211/ieee80211_ioctl.h> +# endif +# if defined(RT_OS_WINDOWS) +# include <iprt/win/ntddndis.h> +# include <devguid.h> +# else +# include <HostNetworkInterfaceImpl.h> +# include <netif.h> +# include <stdlib.h> +# endif +#endif /* VBOX_WITH_NETFLT */ + +#ifdef VBOX_WITH_AUDIO_VRDE +# include "DrvAudioVRDE.h" +#endif +#ifdef VBOX_WITH_AUDIO_RECORDING +# include "DrvAudioRec.h" +#endif +#include "NetworkServiceRunner.h" +#include "BusAssignmentManager.h" +#ifdef VBOX_WITH_EXTPACK +# include "ExtPackManagerImpl.h" +#endif + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static Utf8Str *GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue); + + +/* Darwin compile kludge */ +#undef PVM + +/* Comment out the following line to remove VMWare compatibility hack. */ +#define VMWARE_NET_IN_SLOT_11 + +/** + * Translate IDE StorageControllerType_T to string representation. + */ +static const char* controllerString(StorageControllerType_T enmType) +{ + switch (enmType) + { + case StorageControllerType_PIIX3: + return "PIIX3"; + case StorageControllerType_PIIX4: + return "PIIX4"; + case StorageControllerType_ICH6: + return "ICH6"; + default: + return "Unknown"; + } +} + +/** + * Simple class for storing network boot information. + */ +struct BootNic +{ + ULONG mInstance; + PCIBusAddress mPCIAddress; + + ULONG mBootPrio; + bool operator < (const BootNic &rhs) const + { + ULONG lval = mBootPrio - 1; /* 0 will wrap around and get the lowest priority. */ + ULONG rval = rhs.mBootPrio - 1; + return lval < rval; /* Zero compares as highest number (lowest prio). */ + } +}; + +#ifndef VBOX_WITH_EFI_IN_DD2 +static int findEfiRom(IVirtualBox* vbox, FirmwareType_T aFirmwareType, Utf8Str *pEfiRomFile) +{ + Bstr aFilePath, empty; + BOOL fPresent = FALSE; + HRESULT hrc = vbox->CheckFirmwarePresent(aFirmwareType, empty.raw(), + empty.asOutParam(), aFilePath.asOutParam(), &fPresent); + AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc)); + + if (!fPresent) + { + LogRel(("Failed to find an EFI ROM file.\n")); + return VERR_FILE_NOT_FOUND; + } + + *pEfiRomFile = Utf8Str(aFilePath); + + return VINF_SUCCESS; +} +#endif + +/** + * @throws HRESULT on extra data retrival error. + */ +static int getSmcDeviceKey(IVirtualBox *pVirtualBox, IMachine *pMachine, Utf8Str *pStrKey, bool *pfGetKeyFromRealSMC) +{ + *pfGetKeyFromRealSMC = false; + + /* + * The extra data takes precedence (if non-zero). + */ + GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/SmcDeviceKey", pStrKey); + if (pStrKey->isNotEmpty()) + return VINF_SUCCESS; + +#ifdef RT_OS_DARWIN + + /* + * Work done in EFI/DevSmc + */ + *pfGetKeyFromRealSMC = true; + int vrc = VINF_SUCCESS; + +#else + /* + * Is it apple hardware in bootcamp? + */ + /** @todo implement + test RTSYSDMISTR_MANUFACTURER on all hosts. + * Currently falling back on the product name. */ + char szManufacturer[256]; + szManufacturer[0] = '\0'; + RTSystemQueryDmiString(RTSYSDMISTR_MANUFACTURER, szManufacturer, sizeof(szManufacturer)); + if (szManufacturer[0] != '\0') + { + if ( !strcmp(szManufacturer, "Apple Computer, Inc.") + || !strcmp(szManufacturer, "Apple Inc.") + ) + *pfGetKeyFromRealSMC = true; + } + else + { + char szProdName[256]; + szProdName[0] = '\0'; + RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szProdName, sizeof(szProdName)); + if ( ( !strncmp(szProdName, RT_STR_TUPLE("Mac")) + || !strncmp(szProdName, RT_STR_TUPLE("iMac")) + || !strncmp(szProdName, RT_STR_TUPLE("Xserve")) + ) + && !strchr(szProdName, ' ') /* no spaces */ + && RT_C_IS_DIGIT(szProdName[strlen(szProdName) - 1]) /* version number */ + ) + *pfGetKeyFromRealSMC = true; + } + + int vrc = VINF_SUCCESS; +#endif + + return vrc; +} + + +/* + * VC++ 8 / amd64 has some serious trouble with the next functions. + * As a temporary measure, we'll drop global optimizations. + */ +#if defined(_MSC_VER) && defined(RT_ARCH_AMD64) +# if _MSC_VER >= RT_MSC_VER_VC80 && _MSC_VER < RT_MSC_VER_VC100 +# pragma optimize("g", off) +# endif +#endif + +class ConfigError : public RTCError +{ +public: + + ConfigError(const char *pcszFunction, + int vrc, + const char *pcszName) + : RTCError(Utf8StrFmt(Console::tr("%s failed: rc=%Rrc, pcszName=%s"), pcszFunction, vrc, pcszName)), + m_vrc(vrc) + { + AssertMsgFailed(("%s\n", what())); // in strict mode, hit a breakpoint here + } + + int m_vrc; +}; + + +/** + * Helper that calls CFGMR3InsertString and throws an RTCError if that + * fails (C-string variant). + * @param pNode See CFGMR3InsertStringN. + * @param pcszName See CFGMR3InsertStringN. + * @param pcszValue The string value. + */ +void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const char *pcszValue) +{ + int vrc = mpVMM->pfnCFGMR3InsertString(pNode, pcszName, pcszValue); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertString", vrc, pcszName); +} + +/** + * Helper that calls CFGMR3InsertString and throws an RTCError if that + * fails (Utf8Str variant). + * @param pNode See CFGMR3InsertStringN. + * @param pcszName See CFGMR3InsertStringN. + * @param rStrValue The string value. + */ +void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue) +{ + int vrc = mpVMM->pfnCFGMR3InsertStringN(pNode, pcszName, rStrValue.c_str(), rStrValue.length()); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertStringLengthKnown", vrc, pcszName); +} + +/** + * Helper that calls CFGMR3InsertString and throws an RTCError if that + * fails (Bstr variant). + * + * @param pNode See CFGMR3InsertStringN. + * @param pcszName See CFGMR3InsertStringN. + * @param rBstrValue The string value. + */ +void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Bstr &rBstrValue) +{ + InsertConfigString(pNode, pcszName, Utf8Str(rBstrValue)); +} + +/** + * Helper that calls CFGMR3InsertPassword and throws an RTCError if that + * fails (Utf8Str variant). + * @param pNode See CFGMR3InsertPasswordN. + * @param pcszName See CFGMR3InsertPasswordN. + * @param rStrValue The string value. + */ +void Console::InsertConfigPassword(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue) +{ + int vrc = mpVMM->pfnCFGMR3InsertPasswordN(pNode, pcszName, rStrValue.c_str(), rStrValue.length()); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertPasswordLengthKnown", vrc, pcszName); +} + +/** + * Helper that calls CFGMR3InsertBytes and throws an RTCError if that fails. + * + * @param pNode See CFGMR3InsertBytes. + * @param pcszName See CFGMR3InsertBytes. + * @param pvBytes See CFGMR3InsertBytes. + * @param cbBytes See CFGMR3InsertBytes. + */ +void Console::InsertConfigBytes(PCFGMNODE pNode, const char *pcszName, const void *pvBytes, size_t cbBytes) +{ + int vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pcszName, pvBytes, cbBytes); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertBytes", vrc, pcszName); +} + +/** + * Helper that calls CFGMR3InsertInteger and throws an RTCError if that + * fails. + * + * @param pNode See CFGMR3InsertInteger. + * @param pcszName See CFGMR3InsertInteger. + * @param u64Integer See CFGMR3InsertInteger. + */ +void Console::InsertConfigInteger(PCFGMNODE pNode, const char *pcszName, uint64_t u64Integer) +{ + int vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pcszName, u64Integer); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertInteger", vrc, pcszName); +} + +/** + * Helper that calls CFGMR3InsertNode and throws an RTCError if that fails. + * + * @param pNode See CFGMR3InsertNode. + * @param pcszName See CFGMR3InsertNode. + * @param ppChild See CFGMR3InsertNode. + */ +void Console::InsertConfigNode(PCFGMNODE pNode, const char *pcszName, PCFGMNODE *ppChild) +{ + int vrc = mpVMM->pfnCFGMR3InsertNode(pNode, pcszName, ppChild); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertNode", vrc, pcszName); +} + +/** + * Helper that calls CFGMR3InsertNodeF and throws an RTCError if that fails. + * + * @param pNode See CFGMR3InsertNodeF. + * @param ppChild See CFGMR3InsertNodeF. + * @param pszNameFormat Name format string, see CFGMR3InsertNodeF. + * @param ... Format arguments. + */ +void Console::InsertConfigNodeF(PCFGMNODE pNode, PCFGMNODE *ppChild, const char *pszNameFormat, ...) +{ + va_list va; + va_start(va, pszNameFormat); + int vrc = mpVMM->pfnCFGMR3InsertNodeF(pNode, ppChild, "%N", pszNameFormat, &va); + va_end(va); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3InsertNodeF", vrc, pszNameFormat); +} + +/** + * Helper that calls CFGMR3RemoveValue and throws an RTCError if that fails. + * + * @param pNode See CFGMR3RemoveValue. + * @param pcszName See CFGMR3RemoveValue. + */ +void Console::RemoveConfigValue(PCFGMNODE pNode, const char *pcszName) +{ + int vrc = mpVMM->pfnCFGMR3RemoveValue(pNode, pcszName); + if (RT_FAILURE(vrc)) + throw ConfigError("CFGMR3RemoveValue", vrc, pcszName); +} + +/** + * Gets an extra data value, consulting both machine and global extra data. + * + * @throws HRESULT on failure + * @returns pStrValue for the callers convenience. + * @param pVirtualBox Pointer to the IVirtualBox interface. + * @param pMachine Pointer to the IMachine interface. + * @param pszName The value to get. + * @param pStrValue Where to return it's value (empty string if not + * found). + */ +static Utf8Str *GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue) +{ + pStrValue->setNull(); + + Bstr bstrName(pszName); + Bstr bstrValue; + HRESULT hrc = pMachine->GetExtraData(bstrName.raw(), bstrValue.asOutParam()); + if (FAILED(hrc)) + throw hrc; + if (bstrValue.isEmpty()) + { + hrc = pVirtualBox->GetExtraData(bstrName.raw(), bstrValue.asOutParam()); + if (FAILED(hrc)) + throw hrc; + } + + if (bstrValue.isNotEmpty()) + *pStrValue = bstrValue; + return pStrValue; +} + + +/** Helper that finds out the next HBA port used + */ +static LONG GetNextUsedPort(LONG aPortUsed[30], LONG lBaseVal, uint32_t u32Size) +{ + LONG lNextPortUsed = 30; + for (size_t j = 0; j < u32Size; ++j) + { + if ( aPortUsed[j] > lBaseVal + && aPortUsed[j] <= lNextPortUsed) + lNextPortUsed = aPortUsed[j]; + } + return lNextPortUsed; +} + +#define MAX_BIOS_LUN_COUNT 4 + +int Console::SetBiosDiskInfo(ComPtr<IMachine> pMachine, PCFGMNODE pCfg, PCFGMNODE pBiosCfg, + Bstr controllerName, const char * const s_apszBiosConfig[4]) +{ + RT_NOREF(pCfg); + HRESULT hrc; +#define MAX_DEVICES 30 +#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR) + + LONG lPortLUN[MAX_BIOS_LUN_COUNT]; + LONG lPortUsed[MAX_DEVICES]; + uint32_t u32HDCount = 0; + + /* init to max value */ + lPortLUN[0] = MAX_DEVICES; + + com::SafeIfaceArray<IMediumAttachment> atts; + hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(), + ComSafeArrayAsOutParam(atts)); H(); + size_t uNumAttachments = atts.size(); + if (uNumAttachments > MAX_DEVICES) + { + LogRel(("Number of Attachments > Max=%d.\n", uNumAttachments)); + uNumAttachments = MAX_DEVICES; + } + + /* Find the relevant ports/IDs, i.e the ones to which a HD is attached. */ + for (size_t j = 0; j < uNumAttachments; ++j) + { + IMediumAttachment *pMediumAtt = atts[j]; + LONG lPortNum = 0; + hrc = pMediumAtt->COMGETTER(Port)(&lPortNum); H(); + if (SUCCEEDED(hrc)) + { + DeviceType_T lType; + hrc = pMediumAtt->COMGETTER(Type)(&lType); H(); + if (SUCCEEDED(hrc) && lType == DeviceType_HardDisk) + { + /* find min port number used for HD */ + if (lPortNum < lPortLUN[0]) + lPortLUN[0] = lPortNum; + lPortUsed[u32HDCount++] = lPortNum; + LogFlowFunc(("HD port Count=%d\n", u32HDCount)); + } + } + } + + + /* Pick only the top 4 used HD Ports as CMOS doesn't have space + * to save details for all 30 ports + */ + uint32_t u32MaxPortCount = MAX_BIOS_LUN_COUNT; + if (u32HDCount < MAX_BIOS_LUN_COUNT) + u32MaxPortCount = u32HDCount; + for (size_t j = 1; j < u32MaxPortCount; j++) + lPortLUN[j] = GetNextUsedPort(lPortUsed, lPortLUN[j-1], u32HDCount); + if (pBiosCfg) + { + for (size_t j = 0; j < u32MaxPortCount; j++) + { + InsertConfigInteger(pBiosCfg, s_apszBiosConfig[j], lPortLUN[j]); + LogFlowFunc(("Top %d HBA ports = %s, %d\n", j, s_apszBiosConfig[j], lPortLUN[j])); + } + } + return VINF_SUCCESS; +} + +#ifdef VBOX_WITH_PCI_PASSTHROUGH +HRESULT Console::i_attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *pBusMgr, PCFGMNODE pDevices) +{ +# ifndef VBOX_WITH_EXTPACK + RT_NOREF(pUVM); +# endif + HRESULT hrc = S_OK; + PCFGMNODE pInst, pCfg, pLunL0, pLunL1; + + SafeIfaceArray<IPCIDeviceAttachment> assignments; + ComPtr<IMachine> aMachine = i_machine(); + + hrc = aMachine->COMGETTER(PCIDeviceAssignments)(ComSafeArrayAsOutParam(assignments)); + if ( hrc != S_OK + || assignments.size() < 1) + return hrc; + + /* + * PCI passthrough is only available if the proper ExtPack is installed. + * + * Note. Configuring PCI passthrough here and providing messages about + * the missing extpack isn't exactly clean, but it is a necessary evil + * to patch over legacy compatability issues introduced by the new + * distribution model. + */ +# ifdef VBOX_WITH_EXTPACK + static const char *s_pszPCIRawExtPackName = "Oracle VM VirtualBox Extension Pack"; + if (!mptrExtPackManager->i_isExtPackUsable(s_pszPCIRawExtPackName)) + /* Always fatal! */ + return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, + N_("Implementation of the PCI passthrough framework not found!\n" + "The VM cannot be started. To fix this problem, either " + "install the '%s' or disable PCI passthrough via VBoxManage"), + s_pszPCIRawExtPackName); +# endif + + /* Now actually add devices */ + PCFGMNODE pPCIDevs = NULL; + + if (assignments.size() > 0) + { + InsertConfigNode(pDevices, "pciraw", &pPCIDevs); + + PCFGMNODE pRoot = CFGMR3GetParent(pDevices); Assert(pRoot); + + /* Tell PGM to tell GPCIRaw about guest mappings. */ + CFGMR3InsertNode(pRoot, "PGM", NULL); + InsertConfigInteger(CFGMR3GetChild(pRoot, "PGM"), "PciPassThrough", 1); + + /* + * Currently, using IOMMU needed for PCI passthrough + * requires RAM preallocation. + */ + /** @todo check if we can lift this requirement */ + CFGMR3RemoveValue(pRoot, "RamPreAlloc"); + InsertConfigInteger(pRoot, "RamPreAlloc", 1); + } + + for (size_t iDev = 0; iDev < assignments.size(); iDev++) + { + PCIBusAddress HostPCIAddress, GuestPCIAddress; + ComPtr<IPCIDeviceAttachment> assignment = assignments[iDev]; + LONG host, guest; + Bstr aDevName; + + hrc = assignment->COMGETTER(HostAddress)(&host); H(); + hrc = assignment->COMGETTER(GuestAddress)(&guest); H(); + hrc = assignment->COMGETTER(Name)(aDevName.asOutParam()); H(); + + InsertConfigNode(pPCIDevs, Utf8StrFmt("%d", iDev).c_str(), &pInst); + InsertConfigInteger(pInst, "Trusted", 1); + + HostPCIAddress.fromLong(host); + Assert(HostPCIAddress.valid()); + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigString(pCfg, "DeviceName", aDevName); + + InsertConfigInteger(pCfg, "DetachHostDriver", 1); + InsertConfigInteger(pCfg, "HostPCIBusNo", HostPCIAddress.miBus); + InsertConfigInteger(pCfg, "HostPCIDeviceNo", HostPCIAddress.miDevice); + InsertConfigInteger(pCfg, "HostPCIFunctionNo", HostPCIAddress.miFn); + + GuestPCIAddress.fromLong(guest); + Assert(GuestPCIAddress.valid()); + hrc = pBusMgr->assignHostPCIDevice("pciraw", pInst, HostPCIAddress, GuestPCIAddress, true); + if (hrc != S_OK) + return hrc; + + InsertConfigInteger(pCfg, "GuestPCIBusNo", GuestPCIAddress.miBus); + InsertConfigInteger(pCfg, "GuestPCIDeviceNo", GuestPCIAddress.miDevice); + InsertConfigInteger(pCfg, "GuestPCIFunctionNo", GuestPCIAddress.miFn); + + /* the driver */ + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "pciraw"); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + + /* the Main driver */ + InsertConfigString(pLunL1, "Driver", "MainPciRaw"); + InsertConfigNode(pLunL1, "Config", &pCfg); + PCIRawDev* pMainDev = new PCIRawDev(this); + InsertConfigInteger(pCfg, "Object", (uintptr_t)pMainDev); + } + + return hrc; +} +#endif + + +/** + * Allocate a set of LEDs. + * + * This grabs a maLedSets entry and populates it with @a cLeds. + * + * @returns Index into maLedSets. + * @param cLeds The number of LEDs in the set. + * @param enmType The device type. + * @param ppaSubTypes When not NULL, subtypes for each LED and return the + * array pointer here. + */ +uint32_t Console::i_allocateDriverLeds(uint32_t cLeds, DeviceType_T enmType, DeviceType_T **ppaSubTypes) +{ + Assert(cLeds > 0); + Assert(cLeds < 1024); /* Adjust if any driver supports >=1024 units! */ + + /* Grab a LED set entry before we start allocating anything so the destructor can do the cleanups. */ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Caller should have this already. Need protect mcLedSets check and update. */ + AssertStmt(mcLedSets < RT_ELEMENTS(maLedSets), + throw ConfigError("AllocateDriverPapLeds", VERR_OUT_OF_RANGE, "Too many LED sets")); + uint32_t const idxLedSet = mcLedSets++; + PLEDSET pLS = &maLedSets[idxLedSet]; + pLS->papLeds = (PPDMLED *)RTMemAllocZ(sizeof(PPDMLED) * cLeds); + AssertStmt(pLS->papLeds, throw E_OUTOFMEMORY); + pLS->cLeds = cLeds; + pLS->enmType = enmType; + pLS->paSubTypes = NULL; + + if (ppaSubTypes) + { + *ppaSubTypes = pLS->paSubTypes = (DeviceType_T *)RTMemAlloc(sizeof(DeviceType_T) * cLeds); + AssertStmt(pLS->paSubTypes, throw E_OUTOFMEMORY); + for (size_t idxSub = 0; idxSub < cLeds; ++idxSub) + pLS->paSubTypes[idxSub] = DeviceType_Null; + } + + LogRel2(("mcLedSets = %d, RT_ELEMENTS(maLedSets) = %d\n", mcLedSets, RT_ELEMENTS(maLedSets))); + return idxLedSet; +} + + +/** @todo r=bird: Drop uFirst as it's always zero? Then s/uLast/cLeds/g. */ +void Console::i_attachStatusDriver(PCFGMNODE pCtlInst, DeviceType_T enmType, + uint32_t uFirst, uint32_t uLast, + DeviceType_T **ppaSubTypes, + Console::MediumAttachmentMap *pmapMediumAttachments, + const char *pcszDevice, unsigned uInstance) +{ + Assert(uFirst <= uLast); + PCFGMNODE pLunL0; + InsertConfigNode(pCtlInst, "LUN#999", &pLunL0); + InsertConfigString(pLunL0, "Driver", "MainStatus"); + PCFGMNODE pCfg; + InsertConfigNode(pLunL0, "Config", &pCfg); + uint32_t const iLedSet = i_allocateDriverLeds(uLast - uFirst + 1, enmType, ppaSubTypes); + InsertConfigInteger(pCfg, "iLedSet", iLedSet); + + InsertConfigInteger(pCfg, "HasMediumAttachments", pmapMediumAttachments != NULL); + if (pmapMediumAttachments) + { + AssertPtr(pcszDevice); + Utf8StrFmt deviceInstance("%s/%u", pcszDevice, uInstance); + InsertConfigString(pCfg, "DeviceInstance", deviceInstance.c_str()); + } + InsertConfigInteger(pCfg, "First", uFirst); + InsertConfigInteger(pCfg, "Last", uLast); +} + + +/** + * Construct the VM configuration tree (CFGM). + * + * This is a callback for VMR3Create() call. It is called from CFGMR3Init() in + * the emulation thread (EMT). Any per thread COM/XPCOM initialization is done + * here. + * + * @returns VBox status code. + * @param pUVM The user mode VM handle. + * @param pVM The cross context VM handle. + * @param pVMM The VMM ring-3 vtable. + * @param pvConsole Pointer to the VMPowerUpTask object. + * + * @note Locks the Console object for writing. + */ +/*static*/ DECLCALLBACK(int) +Console::i_configConstructor(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, void *pvConsole) +{ + LogFlowFuncEnter(); + + AssertReturn(pvConsole, VERR_INVALID_POINTER); + ComObjPtr<Console> pConsole = static_cast<Console *>(pvConsole); + + AutoCaller autoCaller(pConsole); + AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED); + + /* lock the console because we widely use internal fields and methods */ + AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS); + + /* + * Set the VM handle and do the rest of the job in an worker method so we + * can easily reset the VM handle on failure. + */ + pConsole->mpUVM = pUVM; + pVMM->pfnVMR3RetainUVM(pUVM); + int vrc; + try + { + vrc = pConsole->i_configConstructorInner(pUVM, pVM, pVMM, &alock); + } + catch (...) + { + vrc = VERR_UNEXPECTED_EXCEPTION; + } + if (RT_FAILURE(vrc)) + { + pConsole->mpUVM = NULL; + pVMM->pfnVMR3ReleaseUVM(pUVM); + } + + return vrc; +} + + +/** + * Worker for configConstructor. + * + * @return VBox status code. + * @param pUVM The user mode VM handle. + * @param pVM The cross context VM handle. + * @param pVMM The VMM vtable. + * @param pAlock The automatic lock instance. This is for when we have + * to leave it in order to avoid deadlocks (ext packs and + * more). + */ +int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, AutoWriteLock *pAlock) +{ + RT_NOREF(pVM /* when everything is disabled */); + VMMDev *pVMMDev = m_pVMMDev; Assert(pVMMDev); + ComPtr<IMachine> pMachine = i_machine(); + + int vrc; + HRESULT hrc; + Utf8Str strTmp; + Bstr bstr; + +#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR) + + /* + * Get necessary objects and frequently used parameters. + */ + ComPtr<IVirtualBox> virtualBox; + hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H(); + + ComPtr<IHost> host; + hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H(); + + ComPtr<ISystemProperties> systemProperties; + hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam()); H(); + + ComPtr<IBIOSSettings> biosSettings; + hrc = pMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam()); H(); + + ComPtr<INvramStore> nvramStore; + hrc = pMachine->COMGETTER(NonVolatileStore)(nvramStore.asOutParam()); H(); + + hrc = pMachine->COMGETTER(HardwareUUID)(bstr.asOutParam()); H(); + RTUUID HardwareUuid; + vrc = RTUuidFromUtf16(&HardwareUuid, bstr.raw()); + AssertRCReturn(vrc, vrc); + + ULONG cRamMBs; + hrc = pMachine->COMGETTER(MemorySize)(&cRamMBs); H(); +#if 0 /* enable to play with lots of memory. */ + if (RTEnvExist("VBOX_RAM_SIZE")) + cRamMBs = RTStrToUInt64(RTEnvGet("VBOX_RAM_SIZE")); +#endif + uint64_t const cbRam = cRamMBs * (uint64_t)_1M; + uint32_t cbRamHole = MM_RAM_HOLE_SIZE_DEFAULT; + uint64_t uMcfgBase = 0; + uint32_t cbMcfgLength = 0; + + ParavirtProvider_T enmParavirtProvider; + hrc = pMachine->GetEffectiveParavirtProvider(&enmParavirtProvider); H(); + + Bstr strParavirtDebug; + hrc = pMachine->COMGETTER(ParavirtDebug)(strParavirtDebug.asOutParam()); H(); + + BOOL fIOAPIC; + uint32_t uIoApicPciAddress = NIL_PCIBDF; + hrc = biosSettings->COMGETTER(IOAPICEnabled)(&fIOAPIC); H(); + + ChipsetType_T chipsetType; + hrc = pMachine->COMGETTER(ChipsetType)(&chipsetType); H(); + if (chipsetType == ChipsetType_ICH9) + { + /* We'd better have 0x10000000 region, to cover 256 buses but this put + * too much load on hypervisor heap. Linux 4.8 currently complains with + * ``acpi PNP0A03:00: [Firmware Info]: MMCONFIG for domain 0000 [bus 00-3f] + * only partially covers this bridge'' */ + cbMcfgLength = 0x4000000; //0x10000000; + cbRamHole += cbMcfgLength; + uMcfgBase = _4G - cbRamHole; + } + + /* Get the CPU profile name. */ + Bstr bstrCpuProfile; + hrc = pMachine->COMGETTER(CPUProfile)(bstrCpuProfile.asOutParam()); H(); + + /* Check if long mode is enabled. */ + BOOL fIsGuest64Bit; + hrc = pMachine->GetCPUProperty(CPUPropertyType_LongMode, &fIsGuest64Bit); H(); + + /* + * Figure out the IOMMU config. + */ +#if defined(VBOX_WITH_IOMMU_AMD) || defined(VBOX_WITH_IOMMU_INTEL) + IommuType_T enmIommuType; + hrc = pMachine->COMGETTER(IommuType)(&enmIommuType); H(); + + /* Resolve 'automatic' type to an Intel or AMD IOMMU based on the host CPU. */ + if (enmIommuType == IommuType_Automatic) + { + if ( bstrCpuProfile.startsWith("AMD") + || bstrCpuProfile.startsWith("Quad-Core AMD") + || bstrCpuProfile.startsWith("Hygon")) + enmIommuType = IommuType_AMD; + else if (bstrCpuProfile.startsWith("Intel")) + { + if ( bstrCpuProfile.equals("Intel 8086") + || bstrCpuProfile.equals("Intel 80186") + || bstrCpuProfile.equals("Intel 80286") + || bstrCpuProfile.equals("Intel 80386") + || bstrCpuProfile.equals("Intel 80486")) + enmIommuType = IommuType_None; + else + enmIommuType = IommuType_Intel; + } +# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) + else if (ASMIsAmdCpu()) + enmIommuType = IommuType_AMD; + else if (ASMIsIntelCpu()) + enmIommuType = IommuType_Intel; +# endif + else + { + /** @todo Should we handle other CPUs like Shanghai, VIA etc. here? */ + LogRel(("WARNING! Unrecognized CPU type, IOMMU disabled.\n")); + enmIommuType = IommuType_None; + } + } + + if (enmIommuType == IommuType_AMD) + { +# ifdef VBOX_WITH_IOMMU_AMD + /* + * Reserve the specific PCI address of the "SB I/O APIC" when using + * an AMD IOMMU. Required by Linux guests, see @bugref{9654#c23}. + */ + uIoApicPciAddress = VBOX_PCI_BDF_SB_IOAPIC; +# else + LogRel(("WARNING! AMD IOMMU not supported, IOMMU disabled.\n")); + enmIommuType = IommuType_None; +# endif + } + + if (enmIommuType == IommuType_Intel) + { +# ifdef VBOX_WITH_IOMMU_INTEL + /* + * Reserve a unique PCI address for the I/O APIC when using + * an Intel IOMMU. For convenience we use the same address as + * we do on AMD, see @bugref{9967#c13}. + */ + uIoApicPciAddress = VBOX_PCI_BDF_SB_IOAPIC; +# else + LogRel(("WARNING! Intel IOMMU not supported, IOMMU disabled.\n")); + enmIommuType = IommuType_None; +# endif + } + + if ( enmIommuType == IommuType_AMD + || enmIommuType == IommuType_Intel) + { + if (chipsetType != ChipsetType_ICH9) + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("IOMMU uses MSIs which requires the ICH9 chipset implementation.")); + if (!fIOAPIC) + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("IOMMU requires an I/O APIC for remapping interrupts.")); + } +#else + IommuType_T const enmIommuType = IommuType_None; +#endif + + /* Instantiate the bus assignment manager. */ + Assert(enmIommuType != IommuType_Automatic); + BusAssignmentManager *pBusMgr = mBusMgr = BusAssignmentManager::createInstance(pVMM, chipsetType, enmIommuType); + + ULONG cCpus = 1; + hrc = pMachine->COMGETTER(CPUCount)(&cCpus); H(); + + ULONG ulCpuExecutionCap = 100; + hrc = pMachine->COMGETTER(CPUExecutionCap)(&ulCpuExecutionCap); H(); + + Bstr osTypeId; + hrc = pMachine->COMGETTER(OSTypeId)(osTypeId.asOutParam()); H(); + LogRel(("Guest OS type: '%s'\n", Utf8Str(osTypeId).c_str())); + + APICMode_T apicMode; + hrc = biosSettings->COMGETTER(APICMode)(&apicMode); H(); + uint32_t uFwAPIC; + switch (apicMode) + { + case APICMode_Disabled: + uFwAPIC = 0; + break; + case APICMode_APIC: + uFwAPIC = 1; + break; + case APICMode_X2APIC: + uFwAPIC = 2; + break; + default: + AssertMsgFailed(("Invalid APICMode=%d\n", apicMode)); + uFwAPIC = 1; + break; + } + + ComPtr<IGuestOSType> pGuestOSType; + virtualBox->GetGuestOSType(osTypeId.raw(), pGuestOSType.asOutParam()); + + BOOL fOsXGuest = FALSE; + BOOL fWinGuest = FALSE; + BOOL fOs2Guest = FALSE; + BOOL fW9xGuest = FALSE; + BOOL fDosGuest = FALSE; + if (!pGuestOSType.isNull()) + { + Bstr guestTypeFamilyId; + hrc = pGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam()); H(); + fOsXGuest = guestTypeFamilyId == Bstr("MacOS"); + fWinGuest = guestTypeFamilyId == Bstr("Windows"); + fOs2Guest = osTypeId.startsWith("OS2"); + fW9xGuest = osTypeId.startsWith("Windows9"); /* Does not include Windows Me. */ + fDosGuest = osTypeId.startsWith("DOS") || osTypeId.startsWith("Windows31"); + } + + ULONG maxNetworkAdapters; + hrc = systemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters); H(); + + /* + * Get root node first. + * This is the only node in the tree. + */ + PCFGMNODE pRoot = pVMM->pfnCFGMR3GetRootU(pUVM); + Assert(pRoot); + + // InsertConfigString throws + try + { + + /* + * Set the root (and VMM) level values. + */ + hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H(); + InsertConfigString(pRoot, "Name", bstr); + InsertConfigBytes(pRoot, "UUID", &HardwareUuid, sizeof(HardwareUuid)); + InsertConfigInteger(pRoot, "RamSize", cbRam); + InsertConfigInteger(pRoot, "RamHoleSize", cbRamHole); + InsertConfigInteger(pRoot, "NumCPUs", cCpus); + InsertConfigInteger(pRoot, "CpuExecutionCap", ulCpuExecutionCap); + InsertConfigInteger(pRoot, "TimerMillies", 10); + + BOOL fPageFusion = FALSE; + hrc = pMachine->COMGETTER(PageFusionEnabled)(&fPageFusion); H(); + InsertConfigInteger(pRoot, "PageFusionAllowed", fPageFusion); /* boolean */ + + /* Not necessary, but makes sure this setting ends up in the release log. */ + ULONG ulBalloonSize = 0; + hrc = pMachine->COMGETTER(MemoryBalloonSize)(&ulBalloonSize); H(); + InsertConfigInteger(pRoot, "MemBalloonSize", ulBalloonSize); + + /* + * EM values (before CPUM as it may need to set IemExecutesAll). + */ + PCFGMNODE pEM; + InsertConfigNode(pRoot, "EM", &pEM); + + /* Triple fault behavior. */ + BOOL fTripleFaultReset = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_TripleFaultReset, &fTripleFaultReset); H(); + InsertConfigInteger(pEM, "TripleFaultReset", fTripleFaultReset); + + /* + * CPUM values. + */ + PCFGMNODE pCPUM; + InsertConfigNode(pRoot, "CPUM", &pCPUM); + PCFGMNODE pIsaExts; + InsertConfigNode(pCPUM, "IsaExts", &pIsaExts); + + /* Host CPUID leaf overrides. */ + for (uint32_t iOrdinal = 0; iOrdinal < _4K; iOrdinal++) + { + ULONG uLeaf, uSubLeaf, uEax, uEbx, uEcx, uEdx; + hrc = pMachine->GetCPUIDLeafByOrdinal(iOrdinal, &uLeaf, &uSubLeaf, &uEax, &uEbx, &uEcx, &uEdx); + if (hrc == E_INVALIDARG) + break; + H(); + PCFGMNODE pLeaf; + InsertConfigNode(pCPUM, Utf8StrFmt("HostCPUID/%RX32", uLeaf).c_str(), &pLeaf); + /** @todo Figure out how to tell the VMM about uSubLeaf */ + InsertConfigInteger(pLeaf, "eax", uEax); + InsertConfigInteger(pLeaf, "ebx", uEbx); + InsertConfigInteger(pLeaf, "ecx", uEcx); + InsertConfigInteger(pLeaf, "edx", uEdx); + } + + /* We must limit CPUID count for Windows NT 4, as otherwise it stops + with error 0x3e (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED). */ + if (osTypeId == "WindowsNT4") + { + LogRel(("Limiting CPUID leaf count for NT4 guests\n")); + InsertConfigInteger(pCPUM, "NT4LeafLimit", true); + } + + if (fOsXGuest) + { + /* Expose extended MWAIT features to Mac OS X guests. */ + LogRel(("Using MWAIT extensions\n")); + InsertConfigInteger(pIsaExts, "MWaitExtensions", true); + + /* Fake the CPU family/model so the guest works. This is partly + because older mac releases really doesn't work on newer cpus, + and partly because mac os x expects more from systems with newer + cpus (MSRs, power features, whatever). */ + uint32_t uMaxIntelFamilyModelStep = UINT32_MAX; + if ( osTypeId == "MacOS" + || osTypeId == "MacOS_64") + uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482. */ + else if ( osTypeId == "MacOS106" + || osTypeId == "MacOS106_64") + uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ + else if ( osTypeId == "MacOS107" + || osTypeId == "MacOS107_64") + uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ /** @todo figure out + what is required here. */ + else if ( osTypeId == "MacOS108" + || osTypeId == "MacOS108_64") + uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ /** @todo figure out + what is required here. */ + else if ( osTypeId == "MacOS109" + || osTypeId == "MacOS109_64") + uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ /** @todo figure + out what is required here. */ + if (uMaxIntelFamilyModelStep != UINT32_MAX) + InsertConfigInteger(pCPUM, "MaxIntelFamilyModelStep", uMaxIntelFamilyModelStep); + } + + /* CPU Portability level, */ + ULONG uCpuIdPortabilityLevel = 0; + hrc = pMachine->COMGETTER(CPUIDPortabilityLevel)(&uCpuIdPortabilityLevel); H(); + InsertConfigInteger(pCPUM, "PortableCpuIdLevel", uCpuIdPortabilityLevel); + + /* Physical Address Extension (PAE) */ + BOOL fEnablePAE = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_PAE, &fEnablePAE); H(); + fEnablePAE |= fIsGuest64Bit; + InsertConfigInteger(pRoot, "EnablePAE", fEnablePAE); + + /* 64-bit guests (long mode) */ + InsertConfigInteger(pCPUM, "Enable64bit", fIsGuest64Bit); + + /* APIC/X2APIC configuration */ + BOOL fEnableAPIC = true; + BOOL fEnableX2APIC = true; + hrc = pMachine->GetCPUProperty(CPUPropertyType_APIC, &fEnableAPIC); H(); + hrc = pMachine->GetCPUProperty(CPUPropertyType_X2APIC, &fEnableX2APIC); H(); + if (fEnableX2APIC) + Assert(fEnableAPIC); + + /* CPUM profile name. */ + InsertConfigString(pCPUM, "GuestCpuName", bstrCpuProfile); + + /* + * Temporary(?) hack to make sure we emulate the ancient 16-bit CPUs + * correctly. There are way too many #UDs we'll miss using VT-x, + * raw-mode or qemu for the 186 and 286, while we'll get undefined opcodes + * dead wrong on 8086 (see http://www.os2museum.com/wp/undocumented-8086-opcodes/). + */ + if ( bstrCpuProfile.equals("Intel 80386") /* just for now */ + || bstrCpuProfile.equals("Intel 80286") + || bstrCpuProfile.equals("Intel 80186") + || bstrCpuProfile.equals("Nec V20") + || bstrCpuProfile.equals("Intel 8086") ) + { + InsertConfigInteger(pEM, "IemExecutesAll", true); + if (!bstrCpuProfile.equals("Intel 80386")) + { + fEnableAPIC = false; + fIOAPIC = false; + } + fEnableX2APIC = false; + } + + /* Adjust firmware APIC handling to stay within the VCPU limits. */ + if (uFwAPIC == 2 && !fEnableX2APIC) + { + if (fEnableAPIC) + uFwAPIC = 1; + else + uFwAPIC = 0; + LogRel(("Limiting the firmware APIC level from x2APIC to %s\n", fEnableAPIC ? "APIC" : "Disabled")); + } + else if (uFwAPIC == 1 && !fEnableAPIC) + { + uFwAPIC = 0; + LogRel(("Limiting the firmware APIC level from APIC to Disabled\n")); + } + + /* Speculation Control. */ + BOOL fSpecCtrl = FALSE; + hrc = pMachine->GetCPUProperty(CPUPropertyType_SpecCtrl, &fSpecCtrl); H(); + InsertConfigInteger(pCPUM, "SpecCtrl", fSpecCtrl); + + /* Nested VT-x / AMD-V. */ + BOOL fNestedHWVirt = FALSE; + hrc = pMachine->GetCPUProperty(CPUPropertyType_HWVirt, &fNestedHWVirt); H(); + InsertConfigInteger(pCPUM, "NestedHWVirt", fNestedHWVirt ? true : false); + + /* + * Hardware virtualization extensions. + */ + /* Sanitize valid/useful APIC combinations, see @bugref{8868}. */ + if (!fEnableAPIC) + { + if (fIsGuest64Bit) + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Cannot disable the APIC for a 64-bit guest.")); + if (cCpus > 1) + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Cannot disable the APIC for an SMP guest.")); + if (fIOAPIC) + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Cannot disable the APIC when the I/O APIC is present.")); + } + + BOOL fHMEnabled; + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Enabled, &fHMEnabled); H(); + if (cCpus > 1 && !fHMEnabled) + { + LogRel(("Forced fHMEnabled to TRUE by SMP guest.\n")); + fHMEnabled = TRUE; + } + + BOOL fHMForced; + fHMEnabled = fHMForced = TRUE; + LogRel(("fHMForced=true - No raw-mode support in this build!\n")); + if (!fHMForced) /* No need to query if already forced above. */ + { + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Force, &fHMForced); H(); + if (fHMForced) + LogRel(("fHMForced=true - HWVirtExPropertyType_Force\n")); + } + InsertConfigInteger(pRoot, "HMEnabled", fHMEnabled); + + /* /HM/xyz */ + PCFGMNODE pHM; + InsertConfigNode(pRoot, "HM", &pHM); + InsertConfigInteger(pHM, "HMForced", fHMForced); + if (fHMEnabled) + { + /* Indicate whether 64-bit guests are supported or not. */ + InsertConfigInteger(pHM, "64bitEnabled", fIsGuest64Bit); + + /** @todo Not exactly pretty to check strings; VBOXOSTYPE would be better, + but that requires quite a bit of API change in Main. */ + if ( fIOAPIC + && ( osTypeId == "WindowsNT4" + || osTypeId == "Windows2000" + || osTypeId == "WindowsXP" + || osTypeId == "Windows2003")) + { + /* Only allow TPR patching for NT, Win2k, XP and Windows Server 2003. (32 bits mode) + * We may want to consider adding more guest OSes (Solaris) later on. + */ + InsertConfigInteger(pHM, "TPRPatchingEnabled", 1); + } + } + + /* HWVirtEx exclusive mode */ + BOOL fHMExclusive = true; + hrc = systemProperties->COMGETTER(ExclusiveHwVirt)(&fHMExclusive); H(); + InsertConfigInteger(pHM, "Exclusive", fHMExclusive); + + /* Nested paging (VT-x/AMD-V) */ + BOOL fEnableNestedPaging = false; + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_NestedPaging, &fEnableNestedPaging); H(); + InsertConfigInteger(pHM, "EnableNestedPaging", fEnableNestedPaging); + + /* Large pages; requires nested paging */ + BOOL fEnableLargePages = false; + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_LargePages, &fEnableLargePages); H(); + InsertConfigInteger(pHM, "EnableLargePages", fEnableLargePages); + + /* VPID (VT-x) */ + BOOL fEnableVPID = false; + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_VPID, &fEnableVPID); H(); + InsertConfigInteger(pHM, "EnableVPID", fEnableVPID); + + /* Unrestricted execution aka UX (VT-x) */ + BOOL fEnableUX = false; + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, &fEnableUX); H(); + InsertConfigInteger(pHM, "EnableUX", fEnableUX); + + /* Virtualized VMSAVE/VMLOAD (AMD-V) */ + BOOL fVirtVmsaveVmload = true; + hrc = host->GetProcessorFeature(ProcessorFeature_VirtVmsaveVmload, &fVirtVmsaveVmload); H(); + InsertConfigInteger(pHM, "SvmVirtVmsaveVmload", fVirtVmsaveVmload); + + /* Indirect branch prediction boundraries. */ + BOOL fIBPBOnVMExit = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_IBPBOnVMExit, &fIBPBOnVMExit); H(); + InsertConfigInteger(pHM, "IBPBOnVMExit", fIBPBOnVMExit); + + BOOL fIBPBOnVMEntry = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_IBPBOnVMEntry, &fIBPBOnVMEntry); H(); + InsertConfigInteger(pHM, "IBPBOnVMEntry", fIBPBOnVMEntry); + + BOOL fSpecCtrlByHost = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_SpecCtrlByHost, &fSpecCtrlByHost); H(); + InsertConfigInteger(pHM, "SpecCtrlByHost", fSpecCtrlByHost); + + BOOL fL1DFlushOnSched = true; + hrc = pMachine->GetCPUProperty(CPUPropertyType_L1DFlushOnEMTScheduling, &fL1DFlushOnSched); H(); + InsertConfigInteger(pHM, "L1DFlushOnSched", fL1DFlushOnSched); + + BOOL fL1DFlushOnVMEntry = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_L1DFlushOnVMEntry, &fL1DFlushOnVMEntry); H(); + InsertConfigInteger(pHM, "L1DFlushOnVMEntry", fL1DFlushOnVMEntry); + + BOOL fMDSClearOnSched = true; + hrc = pMachine->GetCPUProperty(CPUPropertyType_MDSClearOnEMTScheduling, &fMDSClearOnSched); H(); + InsertConfigInteger(pHM, "MDSClearOnSched", fMDSClearOnSched); + + BOOL fMDSClearOnVMEntry = false; + hrc = pMachine->GetCPUProperty(CPUPropertyType_MDSClearOnVMEntry, &fMDSClearOnVMEntry); H(); + InsertConfigInteger(pHM, "MDSClearOnVMEntry", fMDSClearOnVMEntry); + + /* Reset overwrite. */ + mfTurnResetIntoPowerOff = GetExtraDataBoth(virtualBox, pMachine, + "VBoxInternal2/TurnResetIntoPowerOff", &strTmp)->equals("1"); + if (mfTurnResetIntoPowerOff) + InsertConfigInteger(pRoot, "PowerOffInsteadOfReset", 1); + + /* Use NEM rather than HM. */ + BOOL fUseNativeApi = false; + hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_UseNativeApi, &fUseNativeApi); H(); + InsertConfigInteger(pHM, "UseNEMInstead", fUseNativeApi); + + /* Enable workaround for missing TLB flush for OS/2 guests, see ticketref:20625. */ + if (osTypeId.startsWith("OS2")) + InsertConfigInteger(pHM, "MissingOS2TlbFlushWorkaround", 1); + + /* + * NEM + */ + PCFGMNODE pNEM; + InsertConfigNode(pRoot, "NEM", &pNEM); + InsertConfigInteger(pNEM, "Allow64BitGuests", fIsGuest64Bit); + + /* + * Paravirt. provider. + */ + PCFGMNODE pParavirtNode; + InsertConfigNode(pRoot, "GIM", &pParavirtNode); + const char *pcszParavirtProvider; + bool fGimDeviceNeeded = true; + switch (enmParavirtProvider) + { + case ParavirtProvider_None: + pcszParavirtProvider = "None"; + fGimDeviceNeeded = false; + break; + + case ParavirtProvider_Minimal: + pcszParavirtProvider = "Minimal"; + break; + + case ParavirtProvider_HyperV: + pcszParavirtProvider = "HyperV"; + break; + + case ParavirtProvider_KVM: + pcszParavirtProvider = "KVM"; + break; + + default: + AssertMsgFailed(("Invalid enmParavirtProvider=%d\n", enmParavirtProvider)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("Invalid paravirt. provider '%d'"), + enmParavirtProvider); + } + InsertConfigString(pParavirtNode, "Provider", pcszParavirtProvider); + + /* + * Parse paravirt. debug options. + */ + bool fGimDebug = false; + com::Utf8Str strGimDebugAddress = "127.0.0.1"; + uint32_t uGimDebugPort = 50000; + if (strParavirtDebug.isNotEmpty()) + { + /* Hyper-V debug options. */ + if (enmParavirtProvider == ParavirtProvider_HyperV) + { + bool fGimHvDebug = false; + com::Utf8Str strGimHvVendor; + bool fGimHvVsIf = false; + bool fGimHvHypercallIf = false; + + size_t uPos = 0; + com::Utf8Str strDebugOptions = strParavirtDebug; + com::Utf8Str strKey; + com::Utf8Str strVal; + while ((uPos = strDebugOptions.parseKeyValue(strKey, strVal, uPos)) != com::Utf8Str::npos) + { + if (strKey == "enabled") + { + if (strVal.toUInt32() == 1) + { + /* Apply defaults. + The defaults are documented in the user manual, + changes need to be reflected accordingly. */ + fGimHvDebug = true; + strGimHvVendor = "Microsoft Hv"; + fGimHvVsIf = true; + fGimHvHypercallIf = false; + } + /* else: ignore, i.e. don't assert below with 'enabled=0'. */ + } + else if (strKey == "address") + strGimDebugAddress = strVal; + else if (strKey == "port") + uGimDebugPort = strVal.toUInt32(); + else if (strKey == "vendor") + strGimHvVendor = strVal; + else if (strKey == "vsinterface") + fGimHvVsIf = RT_BOOL(strVal.toUInt32()); + else if (strKey == "hypercallinterface") + fGimHvHypercallIf = RT_BOOL(strVal.toUInt32()); + else + { + AssertMsgFailed(("Unrecognized Hyper-V debug option '%s'\n", strKey.c_str())); + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Unrecognized Hyper-V debug option '%s' in '%s'"), strKey.c_str(), + strDebugOptions.c_str()); + } + } + + /* Update HyperV CFGM node with active debug options. */ + if (fGimHvDebug) + { + PCFGMNODE pHvNode; + InsertConfigNode(pParavirtNode, "HyperV", &pHvNode); + InsertConfigString(pHvNode, "VendorID", strGimHvVendor); + InsertConfigInteger(pHvNode, "VSInterface", fGimHvVsIf ? 1 : 0); + InsertConfigInteger(pHvNode, "HypercallDebugInterface", fGimHvHypercallIf ? 1 : 0); + fGimDebug = true; + } + } + } + + /* + * Guest Compatibility Manager. + */ + PCFGMNODE pGcmNode; + uint32_t u32FixerSet = 0; + InsertConfigNode(pRoot, "GCM", &pGcmNode); + /* OS/2 and Win9x guests can run DOS apps so they get + * the DOS specific fixes as well. + */ + if (fOs2Guest) + u32FixerSet = GCMFIXER_DBZ_DOS | GCMFIXER_DBZ_OS2; + else if (fW9xGuest) + u32FixerSet = GCMFIXER_DBZ_DOS | GCMFIXER_DBZ_WIN9X; + else if (fDosGuest) + u32FixerSet = GCMFIXER_DBZ_DOS; + InsertConfigInteger(pGcmNode, "FixerSet", u32FixerSet); + + + /* + * MM values. + */ + PCFGMNODE pMM; + InsertConfigNode(pRoot, "MM", &pMM); + InsertConfigInteger(pMM, "CanUseLargerHeap", chipsetType == ChipsetType_ICH9); + + /* + * PDM config. + * Load drivers in VBoxC.[so|dll] + */ + PCFGMNODE pPDM; + PCFGMNODE pNode; + PCFGMNODE pMod; + InsertConfigNode(pRoot, "PDM", &pPDM); + InsertConfigNode(pPDM, "Devices", &pNode); + InsertConfigNode(pPDM, "Drivers", &pNode); + InsertConfigNode(pNode, "VBoxC", &pMod); +#ifdef VBOX_WITH_XPCOM + // VBoxC is located in the components subdirectory + char szPathVBoxC[RTPATH_MAX]; + vrc = RTPathAppPrivateArch(szPathVBoxC, RTPATH_MAX - sizeof("/components/VBoxC")); AssertRC(vrc); + strcat(szPathVBoxC, "/components/VBoxC"); + InsertConfigString(pMod, "Path", szPathVBoxC); +#else + InsertConfigString(pMod, "Path", "VBoxC"); +#endif + + + /* + * Block cache settings. + */ + PCFGMNODE pPDMBlkCache; + InsertConfigNode(pPDM, "BlkCache", &pPDMBlkCache); + + /* I/O cache size */ + ULONG ioCacheSize = 5; + hrc = pMachine->COMGETTER(IOCacheSize)(&ioCacheSize); H(); + InsertConfigInteger(pPDMBlkCache, "CacheSize", ioCacheSize * _1M); + + /* + * Bandwidth groups. + */ + ComPtr<IBandwidthControl> bwCtrl; + + hrc = pMachine->COMGETTER(BandwidthControl)(bwCtrl.asOutParam()); H(); + + com::SafeIfaceArray<IBandwidthGroup> bwGroups; + hrc = bwCtrl->GetAllBandwidthGroups(ComSafeArrayAsOutParam(bwGroups)); H(); + + PCFGMNODE pAc; + InsertConfigNode(pPDM, "AsyncCompletion", &pAc); + PCFGMNODE pAcFile; + InsertConfigNode(pAc, "File", &pAcFile); + PCFGMNODE pAcFileBwGroups; + InsertConfigNode(pAcFile, "BwGroups", &pAcFileBwGroups); +#ifdef VBOX_WITH_NETSHAPER + PCFGMNODE pNetworkShaper; + InsertConfigNode(pPDM, "NetworkShaper", &pNetworkShaper); + PCFGMNODE pNetworkBwGroups; + InsertConfigNode(pNetworkShaper, "BwGroups", &pNetworkBwGroups); +#endif /* VBOX_WITH_NETSHAPER */ + + for (size_t i = 0; i < bwGroups.size(); i++) + { + Bstr strName; + hrc = bwGroups[i]->COMGETTER(Name)(strName.asOutParam()); H(); + if (strName.isEmpty()) + return pVMM->pfnVMR3SetError(pUVM, VERR_CFGM_NO_NODE, RT_SRC_POS, N_("No bandwidth group name specified")); + + BandwidthGroupType_T enmType = BandwidthGroupType_Null; + hrc = bwGroups[i]->COMGETTER(Type)(&enmType); H(); + LONG64 cMaxBytesPerSec = 0; + hrc = bwGroups[i]->COMGETTER(MaxBytesPerSec)(&cMaxBytesPerSec); H(); + + if (enmType == BandwidthGroupType_Disk) + { + PCFGMNODE pBwGroup; + InsertConfigNode(pAcFileBwGroups, Utf8Str(strName).c_str(), &pBwGroup); + InsertConfigInteger(pBwGroup, "Max", cMaxBytesPerSec); + InsertConfigInteger(pBwGroup, "Start", cMaxBytesPerSec); + InsertConfigInteger(pBwGroup, "Step", 0); + } +#ifdef VBOX_WITH_NETSHAPER + else if (enmType == BandwidthGroupType_Network) + { + /* Network bandwidth groups. */ + PCFGMNODE pBwGroup; + InsertConfigNode(pNetworkBwGroups, Utf8Str(strName).c_str(), &pBwGroup); + InsertConfigInteger(pBwGroup, "Max", cMaxBytesPerSec); + } +#endif /* VBOX_WITH_NETSHAPER */ + } + + /* + * Devices + */ + PCFGMNODE pDevices = NULL; /* /Devices */ + PCFGMNODE pDev = NULL; /* /Devices/Dev/ */ + PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */ + PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */ + PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */ + PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */ + PCFGMNODE pBiosCfg = NULL; /* /Devices/pcbios/0/Config/ */ + PCFGMNODE pNetBootCfg = NULL; /* /Devices/pcbios/0/Config/NetBoot/ */ + + InsertConfigNode(pRoot, "Devices", &pDevices); + + /* + * GIM Device + */ + if (fGimDeviceNeeded) + { + InsertConfigNode(pDevices, "GIMDev", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + //InsertConfigNode(pInst, "Config", &pCfg); + + if (fGimDebug) + { + InsertConfigNode(pInst, "LUN#998", &pLunL0); + InsertConfigString(pLunL0, "Driver", "UDP"); + InsertConfigNode(pLunL0, "Config", &pLunL1); + InsertConfigString(pLunL1, "ServerAddress", strGimDebugAddress); + InsertConfigInteger(pLunL1, "ServerPort", uGimDebugPort); + } + } + + /* + * PC Arch. + */ + InsertConfigNode(pDevices, "pcarch", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + + /* + * The time offset + */ + LONG64 timeOffset; + hrc = biosSettings->COMGETTER(TimeOffset)(&timeOffset); H(); + PCFGMNODE pTMNode; + InsertConfigNode(pRoot, "TM", &pTMNode); + InsertConfigInteger(pTMNode, "UTCOffset", timeOffset * 1000000); + + /* + * DMA + */ + InsertConfigNode(pDevices, "8237A", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + + /* + * PCI buses. + */ + uint32_t uIocPCIAddress, uHbcPCIAddress; + switch (chipsetType) + { + default: + AssertFailed(); + RT_FALL_THRU(); + case ChipsetType_PIIX3: + /* Create the base for adding bridges on demand */ + InsertConfigNode(pDevices, "pcibridge", NULL); + + InsertConfigNode(pDevices, "pci", &pDev); + uHbcPCIAddress = (0x0 << 16) | 0; + uIocPCIAddress = (0x1 << 16) | 0; // ISA controller + break; + case ChipsetType_ICH9: + /* Create the base for adding bridges on demand */ + InsertConfigNode(pDevices, "ich9pcibridge", NULL); + + InsertConfigNode(pDevices, "ich9pci", &pDev); + uHbcPCIAddress = (0x1e << 16) | 0; + uIocPCIAddress = (0x1f << 16) | 0; // LPC controller + break; + } + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC); + if (chipsetType == ChipsetType_ICH9) + { + /* Provide MCFG info */ + InsertConfigInteger(pCfg, "McfgBase", uMcfgBase); + InsertConfigInteger(pCfg, "McfgLength", cbMcfgLength); + +#ifdef VBOX_WITH_PCI_PASSTHROUGH + /* Add PCI passthrough devices */ + hrc = i_attachRawPCIDevices(pUVM, pBusMgr, pDevices); H(); +#endif + + if (enmIommuType == IommuType_AMD) + { + /* AMD IOMMU. */ + InsertConfigNode(pDevices, "iommu-amd", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + hrc = pBusMgr->assignPCIDevice("iommu-amd", pInst); H(); + + /* The AMD IOMMU device needs to know which PCI slot it's in, see @bugref{9654#c104}. */ + { + PCIBusAddress Address; + if (pBusMgr->findPCIAddress("iommu-amd", 0, Address)) + { + uint32_t const u32IommuAddress = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "PCIAddress", u32IommuAddress); + } + else + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Failed to find PCI address of the assigned IOMMU device!")); + } + + PCIBusAddress PCIAddr = PCIBusAddress((int32_t)uIoApicPciAddress); + hrc = pBusMgr->assignPCIDevice("sb-ioapic", NULL /* pCfg */, PCIAddr, true /*fGuestAddressRequired*/); H(); + } + else if (enmIommuType == IommuType_Intel) + { + /* Intel IOMMU. */ + InsertConfigNode(pDevices, "iommu-intel", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + hrc = pBusMgr->assignPCIDevice("iommu-intel", pInst); H(); + + PCIBusAddress PCIAddr = PCIBusAddress((int32_t)uIoApicPciAddress); + hrc = pBusMgr->assignPCIDevice("sb-ioapic", NULL /* pCfg */, PCIAddr, true /*fGuestAddressRequired*/); H(); + } + } + + /* + * Enable the following devices: HPET, SMC and LPC on MacOS X guests or on ICH9 chipset + */ + + /* + * High Precision Event Timer (HPET) + */ + BOOL fHPETEnabled; + /* Other guests may wish to use HPET too, but MacOS X not functional without it */ + hrc = pMachine->COMGETTER(HPETEnabled)(&fHPETEnabled); H(); + /* so always enable HPET in extended profile */ + fHPETEnabled |= fOsXGuest; + /* HPET is always present on ICH9 */ + fHPETEnabled |= (chipsetType == ChipsetType_ICH9); + if (fHPETEnabled) + { + InsertConfigNode(pDevices, "hpet", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pCfg, "ICH9", (chipsetType == ChipsetType_ICH9) ? 1 : 0); /* boolean */ + } + + /* + * System Management Controller (SMC) + */ + BOOL fSmcEnabled; + fSmcEnabled = fOsXGuest; + if (fSmcEnabled) + { + InsertConfigNode(pDevices, "smc", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + + bool fGetKeyFromRealSMC; + Utf8Str strKey; + vrc = getSmcDeviceKey(virtualBox, pMachine, &strKey, &fGetKeyFromRealSMC); + AssertRCReturn(vrc, vrc); + + if (!fGetKeyFromRealSMC) + InsertConfigString(pCfg, "DeviceKey", strKey); + InsertConfigInteger(pCfg, "GetKeyFromRealSMC", fGetKeyFromRealSMC); + } + + /* + * Low Pin Count (LPC) bus + */ + BOOL fLpcEnabled; + /** @todo implement appropriate getter */ + fLpcEnabled = fOsXGuest || (chipsetType == ChipsetType_ICH9); + if (fLpcEnabled) + { + InsertConfigNode(pDevices, "lpc", &pDev); + InsertConfigNode(pDev, "0", &pInst); + hrc = pBusMgr->assignPCIDevice("lpc", pInst); H(); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + } + + BOOL fShowRtc; + fShowRtc = fOsXGuest || (chipsetType == ChipsetType_ICH9); + + /* + * PS/2 keyboard & mouse. + */ + InsertConfigNode(pDevices, "pckbd", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + + KeyboardHIDType_T aKbdHID; + hrc = pMachine->COMGETTER(KeyboardHIDType)(&aKbdHID); H(); + if (aKbdHID != KeyboardHIDType_None) + { + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "KeyboardQueue"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "QueueSize", 64); + + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "MainKeyboard"); + } + + PointingHIDType_T aPointingHID; + hrc = pMachine->COMGETTER(PointingHIDType)(&aPointingHID); H(); + if (aPointingHID != PointingHIDType_None) + { + InsertConfigNode(pInst, "LUN#1", &pLunL0); + InsertConfigString(pLunL0, "Driver", "MouseQueue"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "QueueSize", 128); + + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "MainMouse"); + } + + /* + * i8254 Programmable Interval Timer And Dummy Speaker + */ + InsertConfigNode(pDevices, "i8254", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); +#ifdef DEBUG + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ +#endif + + /* + * i8259 Programmable Interrupt Controller. + */ + InsertConfigNode(pDevices, "i8259", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + + /* + * Advanced Programmable Interrupt Controller. + * SMP: Each CPU has a LAPIC, but we have a single device representing all LAPICs states, + * thus only single insert + */ + if (fEnableAPIC) + { + InsertConfigNode(pDevices, "apic", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC); + PDMAPICMODE enmAPICMode = PDMAPICMODE_APIC; + if (fEnableX2APIC) + enmAPICMode = PDMAPICMODE_X2APIC; + else if (!fEnableAPIC) + enmAPICMode = PDMAPICMODE_NONE; + InsertConfigInteger(pCfg, "Mode", enmAPICMode); + InsertConfigInteger(pCfg, "NumCPUs", cCpus); + + if (fIOAPIC) + { + /* + * I/O Advanced Programmable Interrupt Controller. + */ + InsertConfigNode(pDevices, "ioapic", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pCfg, "NumCPUs", cCpus); + if (enmIommuType == IommuType_AMD) + InsertConfigInteger(pCfg, "PCIAddress", uIoApicPciAddress); + else if (enmIommuType == IommuType_Intel) + { + InsertConfigString(pCfg, "ChipType", "DMAR"); + InsertConfigInteger(pCfg, "PCIAddress", uIoApicPciAddress); + } + } + } + + /* + * RTC MC146818. + */ + InsertConfigNode(pDevices, "mc146818", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + BOOL fRTCUseUTC; + hrc = pMachine->COMGETTER(RTCUseUTC)(&fRTCUseUTC); H(); + InsertConfigInteger(pCfg, "UseUTC", fRTCUseUTC ? 1 : 0); + + /* + * VGA. + */ + ComPtr<IGraphicsAdapter> pGraphicsAdapter; + hrc = pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()); H(); + GraphicsControllerType_T enmGraphicsController; + hrc = pGraphicsAdapter->COMGETTER(GraphicsControllerType)(&enmGraphicsController); H(); + switch (enmGraphicsController) + { + case GraphicsControllerType_Null: + break; +#ifdef VBOX_WITH_VMSVGA + case GraphicsControllerType_VMSVGA: + InsertConfigInteger(pHM, "LovelyMesaDrvWorkaround", 1); /* hits someone else logging backdoor. */ + InsertConfigInteger(pNEM, "LovelyMesaDrvWorkaround", 1); /* hits someone else logging backdoor. */ + RT_FALL_THROUGH(); + case GraphicsControllerType_VBoxSVGA: +#endif + case GraphicsControllerType_VBoxVGA: + vrc = i_configGraphicsController(pDevices, enmGraphicsController, pBusMgr, pMachine, pGraphicsAdapter, biosSettings, + RT_BOOL(fHMEnabled)); + if (FAILED(vrc)) + return vrc; + break; + default: + AssertMsgFailed(("Invalid graphicsController=%d\n", enmGraphicsController)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Invalid graphics controller type '%d'"), enmGraphicsController); + } + + /* + * Firmware. + */ + FirmwareType_T eFwType = FirmwareType_BIOS; + hrc = pMachine->COMGETTER(FirmwareType)(&eFwType); H(); + +#ifdef VBOX_WITH_EFI + BOOL fEfiEnabled = (eFwType >= FirmwareType_EFI) && (eFwType <= FirmwareType_EFIDUAL); +#else + BOOL fEfiEnabled = false; +#endif + if (!fEfiEnabled) + { + /* + * PC Bios. + */ + InsertConfigNode(pDevices, "pcbios", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pBiosCfg); + InsertConfigInteger(pBiosCfg, "NumCPUs", cCpus); + InsertConfigString(pBiosCfg, "HardDiskDevice", "piix3ide"); + InsertConfigString(pBiosCfg, "FloppyDevice", "i82078"); + InsertConfigInteger(pBiosCfg, "IOAPIC", fIOAPIC); + InsertConfigInteger(pBiosCfg, "APIC", uFwAPIC); + BOOL fPXEDebug; + hrc = biosSettings->COMGETTER(PXEDebugEnabled)(&fPXEDebug); H(); + InsertConfigInteger(pBiosCfg, "PXEDebug", fPXEDebug); + InsertConfigBytes(pBiosCfg, "UUID", &HardwareUuid,sizeof(HardwareUuid)); + BOOL fUuidLe; + hrc = biosSettings->COMGETTER(SMBIOSUuidLittleEndian)(&fUuidLe); H(); + InsertConfigInteger(pBiosCfg, "UuidLe", fUuidLe); + InsertConfigNode(pBiosCfg, "NetBoot", &pNetBootCfg); + InsertConfigInteger(pBiosCfg, "McfgBase", uMcfgBase); + InsertConfigInteger(pBiosCfg, "McfgLength", cbMcfgLength); + + AssertMsgReturn(SchemaDefs::MaxBootPosition <= 9, ("Too many boot devices %d\n", SchemaDefs::MaxBootPosition), + VERR_INVALID_PARAMETER); + + for (ULONG pos = 1; pos <= SchemaDefs::MaxBootPosition; ++pos) + { + DeviceType_T enmBootDevice; + hrc = pMachine->GetBootOrder(pos, &enmBootDevice); H(); + + char szParamName[] = "BootDeviceX"; + szParamName[sizeof(szParamName) - 2] = (char)(pos - 1 + '0'); + + const char *pszBootDevice; + switch (enmBootDevice) + { + case DeviceType_Null: + pszBootDevice = "NONE"; + break; + case DeviceType_HardDisk: + pszBootDevice = "IDE"; + break; + case DeviceType_DVD: + pszBootDevice = "DVD"; + break; + case DeviceType_Floppy: + pszBootDevice = "FLOPPY"; + break; + case DeviceType_Network: + pszBootDevice = "LAN"; + break; + default: + AssertMsgFailed(("Invalid enmBootDevice=%d\n", enmBootDevice)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Invalid boot device '%d'"), enmBootDevice); + } + InsertConfigString(pBiosCfg, szParamName, pszBootDevice); + } + + /** @todo @bugref{7145}: We might want to enable this by default for new VMs. For now, + * this is required for Windows 2012 guests. */ + if (osTypeId == "Windows2012_64") + InsertConfigInteger(pBiosCfg, "DmiExposeMemoryTable", 1); /* boolean */ + } + else + { + /* Autodetect firmware type, basing on guest type */ + if (eFwType == FirmwareType_EFI) + eFwType = fIsGuest64Bit ? FirmwareType_EFI64 : FirmwareType_EFI32; + bool const f64BitEntry = eFwType == FirmwareType_EFI64; + + Assert(eFwType == FirmwareType_EFI64 || eFwType == FirmwareType_EFI32 || eFwType == FirmwareType_EFIDUAL); +#ifdef VBOX_WITH_EFI_IN_DD2 + const char *pszEfiRomFile = eFwType == FirmwareType_EFIDUAL ? "VBoxEFIDual.fd" + : eFwType == FirmwareType_EFI32 ? "VBoxEFI32.fd" + : "VBoxEFI64.fd"; +#else + Utf8Str efiRomFile; + vrc = findEfiRom(virtualBox, eFwType, &efiRomFile); + AssertRCReturn(vrc, vrc); + const char *pszEfiRomFile = efiRomFile.c_str(); +#endif + + /* Get boot args */ + Utf8Str bootArgs; + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiBootArgs", &bootArgs); + + /* Get device props */ + Utf8Str deviceProps; + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiDeviceProps", &deviceProps); + + /* Get NVRAM file name */ + Utf8Str strNvram = mptrNvramStore->i_getNonVolatileStorageFile(); + + BOOL fUuidLe; + hrc = biosSettings->COMGETTER(SMBIOSUuidLittleEndian)(&fUuidLe); H(); + + /* Get graphics mode settings */ + uint32_t u32GraphicsMode = UINT32_MAX; + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGraphicsMode", &strTmp); + if (strTmp.isEmpty()) + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGopMode", &strTmp); + if (!strTmp.isEmpty()) + u32GraphicsMode = strTmp.toUInt32(); + + /* Get graphics resolution settings, with some sanity checking */ + Utf8Str strResolution; + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGraphicsResolution", &strResolution); + if (!strResolution.isEmpty()) + { + size_t pos = strResolution.find("x"); + if (pos != strResolution.npos) + { + Utf8Str strH, strV; + strH.assignEx(strResolution, 0, pos); + strV.assignEx(strResolution, pos+1, strResolution.length()-pos-1); + uint32_t u32H = strH.toUInt32(); + uint32_t u32V = strV.toUInt32(); + if (u32H == 0 || u32V == 0) + strResolution.setNull(); + } + else + strResolution.setNull(); + } + else + { + uint32_t u32H = 0; + uint32_t u32V = 0; + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiHorizontalResolution", &strTmp); + if (strTmp.isEmpty()) + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiUgaHorizontalResolution", &strTmp); + if (!strTmp.isEmpty()) + u32H = strTmp.toUInt32(); + + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiVerticalResolution", &strTmp); + if (strTmp.isEmpty()) + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiUgaVerticalResolution", &strTmp); + if (!strTmp.isEmpty()) + u32V = strTmp.toUInt32(); + if (u32H != 0 && u32V != 0) + strResolution = Utf8StrFmt("%ux%u", u32H, u32V); + } + + /* + * EFI subtree. + */ + InsertConfigNode(pDevices, "efi", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pCfg, "NumCPUs", cCpus); + InsertConfigInteger(pCfg, "McfgBase", uMcfgBase); + InsertConfigInteger(pCfg, "McfgLength", cbMcfgLength); + InsertConfigString(pCfg, "EfiRom", pszEfiRomFile); + InsertConfigString(pCfg, "BootArgs", bootArgs); + InsertConfigString(pCfg, "DeviceProps", deviceProps); + InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC); + InsertConfigInteger(pCfg, "APIC", uFwAPIC); + InsertConfigBytes(pCfg, "UUID", &HardwareUuid,sizeof(HardwareUuid)); + InsertConfigInteger(pCfg, "UuidLe", fUuidLe); + InsertConfigInteger(pCfg, "64BitEntry", f64BitEntry); /* boolean */ + InsertConfigString(pCfg, "NvramFile", strNvram); + if (u32GraphicsMode != UINT32_MAX) + InsertConfigInteger(pCfg, "GraphicsMode", u32GraphicsMode); + if (!strResolution.isEmpty()) + InsertConfigString(pCfg, "GraphicsResolution", strResolution); + + /* For OS X guests we'll force passing host's DMI info to the guest */ + if (fOsXGuest) + { + InsertConfigInteger(pCfg, "DmiUseHostInfo", 1); + InsertConfigInteger(pCfg, "DmiExposeMemoryTable", 1); + } + + /* Attach the NVRAM storage driver. */ + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "NvramStore"); + } + + /* + * The USB Controllers. + */ + com::SafeIfaceArray<IUSBController> usbCtrls; + hrc = pMachine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbCtrls)); + bool fOhciPresent = false; /**< Flag whether at least one OHCI controller is present. */ + bool fXhciPresent = false; /**< Flag whether at least one XHCI controller is present. */ + + if (SUCCEEDED(hrc)) + { + for (size_t i = 0; i < usbCtrls.size(); ++i) + { + USBControllerType_T enmCtrlType; + vrc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H(); + if (enmCtrlType == USBControllerType_OHCI) + { + fOhciPresent = true; + break; + } + else if (enmCtrlType == USBControllerType_XHCI) + { + fXhciPresent = true; + break; + } + } + } + else if (hrc != E_NOTIMPL) + { + H(); + } + + /* + * Currently EHCI is only enabled when an OHCI or XHCI controller is present as well. + */ + if (fOhciPresent || fXhciPresent) + mfVMHasUsbController = true; + + PCFGMNODE pUsbDevices = NULL; /**< Required for USB storage controller later. */ + if (mfVMHasUsbController) + { + for (size_t i = 0; i < usbCtrls.size(); ++i) + { + USBControllerType_T enmCtrlType; + vrc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H(); + + if (enmCtrlType == USBControllerType_OHCI) + { + InsertConfigNode(pDevices, "usb-ohci", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + hrc = pBusMgr->assignPCIDevice("usb-ohci", pInst); H(); + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "VUSBRootHub"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + /* + * Attach the status driver. + */ + i_attachStatusDriver(pInst, DeviceType_USB, 0, 0, NULL, NULL, NULL, 0); + } +#ifdef VBOX_WITH_EHCI + else if (enmCtrlType == USBControllerType_EHCI) + { + InsertConfigNode(pDevices, "usb-ehci", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + hrc = pBusMgr->assignPCIDevice("usb-ehci", pInst); H(); + + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "VUSBRootHub"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + /* + * Attach the status driver. + */ + i_attachStatusDriver(pInst, DeviceType_USB, 0, 0, NULL, NULL, NULL, 0); + } +#endif + else if (enmCtrlType == USBControllerType_XHCI) + { + InsertConfigNode(pDevices, "usb-xhci", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + hrc = pBusMgr->assignPCIDevice("usb-xhci", pInst); H(); + + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "VUSBRootHub"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + InsertConfigNode(pInst, "LUN#1", &pLunL1); + InsertConfigString(pLunL1, "Driver", "VUSBRootHub"); + InsertConfigNode(pLunL1, "Config", &pCfg); + + /* + * Attach the status driver. + */ + i_attachStatusDriver(pInst, DeviceType_USB, 0, 1, NULL, NULL, NULL, 0); + } + } /* for every USB controller. */ + + + /* + * Virtual USB Devices. + */ + InsertConfigNode(pRoot, "USB", &pUsbDevices); + +#ifdef VBOX_WITH_USB + { + /* + * Global USB options, currently unused as we'll apply the 2.0 -> 1.1 morphing + * on a per device level now. + */ + InsertConfigNode(pUsbDevices, "USBProxy", &pCfg); + InsertConfigNode(pCfg, "GlobalConfig", &pCfg); + // This globally enables the 2.0 -> 1.1 device morphing of proxied devices to keep windows quiet. + //InsertConfigInteger(pCfg, "Force11Device", true); + // The following breaks stuff, but it makes MSDs work in vista. (I include it here so + // that it's documented somewhere.) Users needing it can use: + // VBoxManage setextradata "myvm" "VBoxInternal/USB/USBProxy/GlobalConfig/Force11PacketSize" 1 + //InsertConfigInteger(pCfg, "Force11PacketSize", true); + } +#endif + +#ifdef VBOX_WITH_USB_CARDREADER + BOOL aEmulatedUSBCardReaderEnabled = FALSE; + hrc = pMachine->COMGETTER(EmulatedUSBCardReaderEnabled)(&aEmulatedUSBCardReaderEnabled); H(); + if (aEmulatedUSBCardReaderEnabled) + { + InsertConfigNode(pUsbDevices, "CardReader", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + + InsertConfigNode(pInst, "LUN#0", &pLunL0); +# ifdef VBOX_WITH_USB_CARDREADER_TEST + InsertConfigString(pLunL0, "Driver", "DrvDirectCardReader"); + InsertConfigNode(pLunL0, "Config", &pCfg); +# else + InsertConfigString(pLunL0, "Driver", "UsbCardReader"); + InsertConfigNode(pLunL0, "Config", &pCfg); +# endif + } +#endif + + /* Virtual USB Mouse/Tablet */ + if ( aPointingHID == PointingHIDType_USBMouse + || aPointingHID == PointingHIDType_USBTablet + || aPointingHID == PointingHIDType_USBMultiTouch + || aPointingHID == PointingHIDType_USBMultiTouchScreenPlusPad) + { + InsertConfigNode(pUsbDevices, "HidMouse", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + + if (aPointingHID == PointingHIDType_USBMouse) + InsertConfigString(pCfg, "Mode", "relative"); + else + InsertConfigString(pCfg, "Mode", "absolute"); + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "MouseQueue"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "QueueSize", 128); + + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "MainMouse"); + } + if ( aPointingHID == PointingHIDType_USBMultiTouch + || aPointingHID == PointingHIDType_USBMultiTouchScreenPlusPad) + { + InsertConfigNode(pDev, "1", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + + InsertConfigString(pCfg, "Mode", "multitouch"); + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "MouseQueue"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "QueueSize", 128); + + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "MainMouse"); + } + if (aPointingHID == PointingHIDType_USBMultiTouchScreenPlusPad) + { + InsertConfigNode(pDev, "2", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + + InsertConfigString(pCfg, "Mode", "touchpad"); + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "MouseQueue"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "QueueSize", 128); + + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "MainMouse"); + } + + /* Virtual USB Keyboard */ + if (aKbdHID == KeyboardHIDType_USBKeyboard) + { + InsertConfigNode(pUsbDevices, "HidKeyboard", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "KeyboardQueue"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "QueueSize", 64); + + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "MainKeyboard"); + } + } + + /* + * Storage controllers. + */ + com::SafeIfaceArray<IStorageController> ctrls; + PCFGMNODE aCtrlNodes[StorageControllerType_VirtioSCSI + 1] = {}; + hrc = pMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls)); H(); + + bool fFdcEnabled = false; + for (size_t i = 0; i < ctrls.size(); ++i) + { + DeviceType_T *paLedDevType = NULL; + + StorageControllerType_T enmCtrlType; + hrc = ctrls[i]->COMGETTER(ControllerType)(&enmCtrlType); H(); + AssertRelease((unsigned)enmCtrlType < RT_ELEMENTS(aCtrlNodes) + || enmCtrlType == StorageControllerType_USB); + + StorageBus_T enmBus; + hrc = ctrls[i]->COMGETTER(Bus)(&enmBus); H(); + + Bstr controllerName; + hrc = ctrls[i]->COMGETTER(Name)(controllerName.asOutParam()); H(); + + ULONG ulInstance = 999; + hrc = ctrls[i]->COMGETTER(Instance)(&ulInstance); H(); + + BOOL fUseHostIOCache; + hrc = ctrls[i]->COMGETTER(UseHostIOCache)(&fUseHostIOCache); H(); + + BOOL fBootable; + hrc = ctrls[i]->COMGETTER(Bootable)(&fBootable); H(); + + PCFGMNODE pCtlInst = NULL; + const char *pszCtrlDev = i_storageControllerTypeToStr(enmCtrlType); + if (enmCtrlType != StorageControllerType_USB) + { + /* /Devices/<ctrldev>/ */ + pDev = aCtrlNodes[enmCtrlType]; + if (!pDev) + { + InsertConfigNode(pDevices, pszCtrlDev, &pDev); + aCtrlNodes[enmCtrlType] = pDev; /* IDE variants are handled in the switch */ + } + + /* /Devices/<ctrldev>/<instance>/ */ + InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pCtlInst); + + /* Device config: /Devices/<ctrldev>/<instance>/<values> & /ditto/Config/<values> */ + InsertConfigInteger(pCtlInst, "Trusted", 1); + InsertConfigNode(pCtlInst, "Config", &pCfg); + } + + static const char * const apszBiosConfigScsi[MAX_BIOS_LUN_COUNT] = + { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" }; + + static const char * const apszBiosConfigSata[MAX_BIOS_LUN_COUNT] = + { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" }; + + switch (enmCtrlType) + { + case StorageControllerType_LsiLogic: + { + hrc = pBusMgr->assignPCIDevice("lsilogic", pCtlInst); H(); + + InsertConfigInteger(pCfg, "Bootable", fBootable); + + /* BIOS configuration values, first SCSI controller only. */ + if ( !pBusMgr->hasPCIDevice("lsilogic", 1) + && !pBusMgr->hasPCIDevice("buslogic", 0) + && !pBusMgr->hasPCIDevice("lsilogicsas", 0) + && pBiosCfg) + { + InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicscsi"); + hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H(); + } + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 15, &paLedDevType, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + case StorageControllerType_BusLogic: + { + hrc = pBusMgr->assignPCIDevice("buslogic", pCtlInst); H(); + + InsertConfigInteger(pCfg, "Bootable", fBootable); + + /* BIOS configuration values, first SCSI controller only. */ + if ( !pBusMgr->hasPCIDevice("lsilogic", 0) + && !pBusMgr->hasPCIDevice("buslogic", 1) + && !pBusMgr->hasPCIDevice("lsilogicsas", 0) + && pBiosCfg) + { + InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "buslogic"); + hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H(); + } + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 15, &paLedDevType, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + case StorageControllerType_IntelAhci: + { + hrc = pBusMgr->assignPCIDevice("ahci", pCtlInst); H(); + + ULONG cPorts = 0; + hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H(); + InsertConfigInteger(pCfg, "PortCount", cPorts); + InsertConfigInteger(pCfg, "Bootable", fBootable); + + com::SafeIfaceArray<IMediumAttachment> atts; + hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(), + ComSafeArrayAsOutParam(atts)); H(); + + /* Configure the hotpluggable flag for the port. */ + for (unsigned idxAtt = 0; idxAtt < atts.size(); ++idxAtt) + { + IMediumAttachment *pMediumAtt = atts[idxAtt]; + + LONG lPortNum = 0; + hrc = pMediumAtt->COMGETTER(Port)(&lPortNum); H(); + + BOOL fHotPluggable = FALSE; + hrc = pMediumAtt->COMGETTER(HotPluggable)(&fHotPluggable); H(); + if (SUCCEEDED(hrc)) + { + PCFGMNODE pPortCfg; + char szName[24]; + RTStrPrintf(szName, sizeof(szName), "Port%d", lPortNum); + + InsertConfigNode(pCfg, szName, &pPortCfg); + InsertConfigInteger(pPortCfg, "Hotpluggable", fHotPluggable ? 1 : 0); + } + } + + /* BIOS configuration values, first AHCI controller only. */ + if ( !pBusMgr->hasPCIDevice("ahci", 1) + && pBiosCfg) + { + InsertConfigString(pBiosCfg, "SataHardDiskDevice", "ahci"); + hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigSata); H(); + } + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, cPorts - 1, &paLedDevType, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + case StorageControllerType_PIIX3: + case StorageControllerType_PIIX4: + case StorageControllerType_ICH6: + { + /* + * IDE (update this when the main interface changes) + */ + hrc = pBusMgr->assignPCIDevice("piix3ide", pCtlInst); H(); + InsertConfigString(pCfg, "Type", controllerString(enmCtrlType)); + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 3, &paLedDevType, + &mapMediumAttachments, pszCtrlDev, ulInstance); + + /* IDE flavors */ + aCtrlNodes[StorageControllerType_PIIX3] = pDev; + aCtrlNodes[StorageControllerType_PIIX4] = pDev; + aCtrlNodes[StorageControllerType_ICH6] = pDev; + break; + } + + case StorageControllerType_I82078: + { + /* + * i82078 Floppy drive controller + */ + fFdcEnabled = true; + InsertConfigInteger(pCfg, "IRQ", 6); + InsertConfigInteger(pCfg, "DMA", 2); + InsertConfigInteger(pCfg, "MemMapped", 0 ); + InsertConfigInteger(pCfg, "IOBase", 0x3f0); + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_Floppy, 0, 1, NULL, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + case StorageControllerType_LsiLogicSas: + { + hrc = pBusMgr->assignPCIDevice("lsilogicsas", pCtlInst); H(); + + InsertConfigString(pCfg, "ControllerType", "SAS1068"); + InsertConfigInteger(pCfg, "Bootable", fBootable); + + /* BIOS configuration values, first SCSI controller only. */ + if ( !pBusMgr->hasPCIDevice("lsilogic", 0) + && !pBusMgr->hasPCIDevice("buslogic", 0) + && !pBusMgr->hasPCIDevice("lsilogicsas", 1) + && pBiosCfg) + { + InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicsas"); + hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H(); + } + + ULONG cPorts = 0; + hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H(); + InsertConfigInteger(pCfg, "NumPorts", cPorts); + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 7, &paLedDevType, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + case StorageControllerType_USB: + { + if (pUsbDevices) + { + /* + * USB MSDs are handled a bit different as the device instance + * doesn't match the storage controller instance but the port. + */ + InsertConfigNode(pUsbDevices, "Msd", &pDev); + pCtlInst = pDev; + } + else + return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, + N_("There is no USB controller enabled but there\n" + "is at least one USB storage device configured for this VM.\n" + "To fix this problem either enable the USB controller or remove\n" + "the storage device from the VM")); + break; + } + + case StorageControllerType_NVMe: + { + hrc = pBusMgr->assignPCIDevice("nvme", pCtlInst); H(); + + ULONG cPorts = 0; + hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H(); + InsertConfigInteger(pCfg, "NamespacesMax", cPorts); + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, cPorts - 1, NULL, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + case StorageControllerType_VirtioSCSI: + { + hrc = pBusMgr->assignPCIDevice("virtio-scsi", pCtlInst); H(); + + ULONG cPorts = 0; + hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H(); + InsertConfigInteger(pCfg, "NumTargets", cPorts); + InsertConfigInteger(pCfg, "Bootable", fBootable); + + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, cPorts - 1, &paLedDevType, + &mapMediumAttachments, pszCtrlDev, ulInstance); + break; + } + + default: + AssertLogRelMsgFailedReturn(("invalid storage controller type: %d\n", enmCtrlType), VERR_MAIN_CONFIG_CONSTRUCTOR_IPE); + } + + /* Attach the media to the storage controllers. */ + com::SafeIfaceArray<IMediumAttachment> atts; + hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(), + ComSafeArrayAsOutParam(atts)); H(); + + /* Builtin I/O cache - per device setting. */ + BOOL fBuiltinIOCache = true; + hrc = pMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache); H(); + + bool fInsertDiskIntegrityDrv = false; + Bstr strDiskIntegrityFlag; + hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EnableDiskIntegrityDriver").raw(), + strDiskIntegrityFlag.asOutParam()); + if ( hrc == S_OK + && strDiskIntegrityFlag == "1") + fInsertDiskIntegrityDrv = true; + + for (size_t j = 0; j < atts.size(); ++j) + { + IMediumAttachment *pMediumAtt = atts[j]; + vrc = i_configMediumAttachment(pszCtrlDev, + ulInstance, + enmBus, + !!fUseHostIOCache, + enmCtrlType == StorageControllerType_NVMe ? false : !!fBuiltinIOCache, + fInsertDiskIntegrityDrv, + false /* fSetupMerge */, + 0 /* uMergeSource */, + 0 /* uMergeTarget */, + pMediumAtt, + mMachineState, + NULL /* phrc */, + false /* fAttachDetach */, + false /* fForceUnmount */, + false /* fHotplug */, + pUVM, + pVMM, + paLedDevType, + NULL /* ppLunL0 */); + if (RT_FAILURE(vrc)) + return vrc; + } + H(); + } + H(); + + /* + * Network adapters + */ +#ifdef VMWARE_NET_IN_SLOT_11 + bool fSwapSlots3and11 = false; +#endif + PCFGMNODE pDevPCNet = NULL; /* PCNet-type devices */ + InsertConfigNode(pDevices, "pcnet", &pDevPCNet); +#ifdef VBOX_WITH_E1000 + PCFGMNODE pDevE1000 = NULL; /* E1000-type devices */ + InsertConfigNode(pDevices, "e1000", &pDevE1000); +#endif +#ifdef VBOX_WITH_VIRTIO + PCFGMNODE pDevVirtioNet = NULL; /* Virtio network devices */ + InsertConfigNode(pDevices, "virtio-net", &pDevVirtioNet); +#endif /* VBOX_WITH_VIRTIO */ + PCFGMNODE pDevDP8390 = NULL; /* DP8390-type devices */ + InsertConfigNode(pDevices, "dp8390", &pDevDP8390); + PCFGMNODE pDev3C501 = NULL; /* EtherLink-type devices */ + InsertConfigNode(pDevices, "3c501", &pDev3C501); + + std::list<BootNic> llBootNics; + for (ULONG uInstance = 0; uInstance < maxNetworkAdapters; ++uInstance) + { + ComPtr<INetworkAdapter> networkAdapter; + hrc = pMachine->GetNetworkAdapter(uInstance, networkAdapter.asOutParam()); H(); + BOOL fEnabledNetAdapter = FALSE; + hrc = networkAdapter->COMGETTER(Enabled)(&fEnabledNetAdapter); H(); + if (!fEnabledNetAdapter) + continue; + + /* + * The virtual hardware type. Create appropriate device first. + */ + const char *pszAdapterName = "pcnet"; + NetworkAdapterType_T adapterType; + hrc = networkAdapter->COMGETTER(AdapterType)(&adapterType); H(); + switch (adapterType) + { + case NetworkAdapterType_Am79C970A: + case NetworkAdapterType_Am79C973: + case NetworkAdapterType_Am79C960: + pDev = pDevPCNet; + break; +#ifdef VBOX_WITH_E1000 + case NetworkAdapterType_I82540EM: + case NetworkAdapterType_I82543GC: + case NetworkAdapterType_I82545EM: + pDev = pDevE1000; + pszAdapterName = "e1000"; + break; +#endif +#ifdef VBOX_WITH_VIRTIO + case NetworkAdapterType_Virtio: + pDev = pDevVirtioNet; + pszAdapterName = "virtio-net"; + break; +#endif /* VBOX_WITH_VIRTIO */ + case NetworkAdapterType_NE1000: + case NetworkAdapterType_NE2000: + case NetworkAdapterType_WD8003: + case NetworkAdapterType_WD8013: + case NetworkAdapterType_ELNK2: + pDev = pDevDP8390; + break; + case NetworkAdapterType_ELNK1: + pDev = pDev3C501; + break; + default: + AssertMsgFailed(("Invalid network adapter type '%d' for slot '%d'", adapterType, uInstance)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Invalid network adapter type '%d' for slot '%d'"), adapterType, uInstance); + } + + InsertConfigNode(pDev, Utf8StrFmt("%u", uInstance).c_str(), &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + /* the first network card gets the PCI ID 3, the next 3 gets 8..10, + * next 4 get 16..19. */ + int iPCIDeviceNo; + switch (uInstance) + { + case 0: + iPCIDeviceNo = 3; + break; + case 1: case 2: case 3: + iPCIDeviceNo = uInstance - 1 + 8; + break; + case 4: case 5: case 6: case 7: + iPCIDeviceNo = uInstance - 4 + 16; + break; + default: + /* auto assignment */ + iPCIDeviceNo = -1; + break; + } +#ifdef VMWARE_NET_IN_SLOT_11 + /* + * Dirty hack for PCI slot compatibility with VMWare, + * it assigns slot 0x11 to the first network controller. + */ + if (iPCIDeviceNo == 3 && adapterType == NetworkAdapterType_I82545EM) + { + iPCIDeviceNo = 0x11; + fSwapSlots3and11 = true; + } + else if (iPCIDeviceNo == 0x11 && fSwapSlots3and11) + iPCIDeviceNo = 3; +#endif + PCIBusAddress PCIAddr = PCIBusAddress(0, iPCIDeviceNo, 0); + hrc = pBusMgr->assignPCIDevice(pszAdapterName, pInst, PCIAddr); H(); + + InsertConfigNode(pInst, "Config", &pCfg); +#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE /* not safe here yet. */ /** @todo Make PCNet ring-0 safe on 32-bit mac kernels! */ + if (pDev == pDevPCNet) + InsertConfigInteger(pCfg, "R0Enabled", false); +#endif + /* + * Collect information needed for network booting and add it to the list. + */ + BootNic nic; + + nic.mInstance = uInstance; + /* Could be updated by reference, if auto assigned */ + nic.mPCIAddress = PCIAddr; + + hrc = networkAdapter->COMGETTER(BootPriority)(&nic.mBootPrio); H(); + + llBootNics.push_back(nic); + + /* + * The virtual hardware type. PCNet supports three types, E1000 three, + * but VirtIO only one. + */ + switch (adapterType) + { + case NetworkAdapterType_Am79C970A: + InsertConfigString(pCfg, "ChipType", "Am79C970A"); + break; + case NetworkAdapterType_Am79C973: + InsertConfigString(pCfg, "ChipType", "Am79C973"); + break; + case NetworkAdapterType_Am79C960: + InsertConfigString(pCfg, "ChipType", "Am79C960"); + break; + case NetworkAdapterType_I82540EM: + InsertConfigInteger(pCfg, "AdapterType", 0); + break; + case NetworkAdapterType_I82543GC: + InsertConfigInteger(pCfg, "AdapterType", 1); + break; + case NetworkAdapterType_I82545EM: + InsertConfigInteger(pCfg, "AdapterType", 2); + break; + case NetworkAdapterType_Virtio: + break; + case NetworkAdapterType_NE1000: + InsertConfigString(pCfg, "DeviceType", "NE1000"); + break; + case NetworkAdapterType_NE2000: + InsertConfigString(pCfg, "DeviceType", "NE2000"); + break; + case NetworkAdapterType_WD8003: + InsertConfigString(pCfg, "DeviceType", "WD8003"); + break; + case NetworkAdapterType_WD8013: + InsertConfigString(pCfg, "DeviceType", "WD8013"); + break; + case NetworkAdapterType_ELNK2: + InsertConfigString(pCfg, "DeviceType", "3C503"); + break; + case NetworkAdapterType_ELNK1: + break; + case NetworkAdapterType_Null: AssertFailedBreak(); /* (compiler warnings) */ +#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK + case NetworkAdapterType_32BitHack: AssertFailedBreak(); /* (compiler warnings) */ +#endif + } + + /* + * Get the MAC address and convert it to binary representation + */ + Bstr macAddr; + hrc = networkAdapter->COMGETTER(MACAddress)(macAddr.asOutParam()); H(); + Assert(!macAddr.isEmpty()); + Utf8Str macAddrUtf8 = macAddr; +#ifdef VBOX_WITH_CLOUD_NET + NetworkAttachmentType_T eAttachmentType; + hrc = networkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H(); + if (eAttachmentType == NetworkAttachmentType_Cloud) + { + mGateway.setLocalMacAddress(macAddrUtf8); + /* We'll insert cloud MAC later, when it becomes known. */ + } + else + { +#endif + char *macStr = (char*)macAddrUtf8.c_str(); + Assert(strlen(macStr) == 12); + RTMAC Mac; + RT_ZERO(Mac); + char *pMac = (char*)&Mac; + for (uint32_t i = 0; i < 6; ++i) + { + int c1 = *macStr++ - '0'; + if (c1 > 9) + c1 -= 7; + int c2 = *macStr++ - '0'; + if (c2 > 9) + c2 -= 7; + *pMac++ = (char)(((c1 & 0x0f) << 4) | (c2 & 0x0f)); + } + InsertConfigBytes(pCfg, "MAC", &Mac, sizeof(Mac)); +#ifdef VBOX_WITH_CLOUD_NET + } +#endif + /* + * Check if the cable is supposed to be unplugged + */ + BOOL fCableConnected; + hrc = networkAdapter->COMGETTER(CableConnected)(&fCableConnected); H(); + InsertConfigInteger(pCfg, "CableConnected", fCableConnected ? 1 : 0); + + /* + * Line speed to report from custom drivers + */ + ULONG ulLineSpeed; + hrc = networkAdapter->COMGETTER(LineSpeed)(&ulLineSpeed); H(); + InsertConfigInteger(pCfg, "LineSpeed", ulLineSpeed); + + /* + * Attach the status driver. + */ + i_attachStatusDriver(pInst, DeviceType_Network, 0, 0, NULL, NULL, NULL, 0); + + /* + * Configure the network card now + */ + bool fIgnoreConnectFailure = mMachineState == MachineState_Restoring; + vrc = i_configNetwork(pszAdapterName, + uInstance, + 0, + networkAdapter, + pCfg, + pLunL0, + pInst, + false /*fAttachDetach*/, + fIgnoreConnectFailure, + pUVM, + pVMM); + if (RT_FAILURE(vrc)) + return vrc; + } + + /* + * Build network boot information and transfer it to the BIOS. + */ + if (pNetBootCfg && !llBootNics.empty()) /* NetBoot node doesn't exist for EFI! */ + { + llBootNics.sort(); /* Sort the list by boot priority. */ + + char achBootIdx[] = "0"; + unsigned uBootIdx = 0; + + for (std::list<BootNic>::iterator it = llBootNics.begin(); it != llBootNics.end(); ++it) + { + /* A NIC with priority 0 is only used if it's first in the list. */ + if (it->mBootPrio == 0 && uBootIdx != 0) + break; + + PCFGMNODE pNetBtDevCfg; + achBootIdx[0] = (char)('0' + uBootIdx++); /* Boot device order. */ + InsertConfigNode(pNetBootCfg, achBootIdx, &pNetBtDevCfg); + InsertConfigInteger(pNetBtDevCfg, "NIC", it->mInstance); + InsertConfigInteger(pNetBtDevCfg, "PCIBusNo", it->mPCIAddress.miBus); + InsertConfigInteger(pNetBtDevCfg, "PCIDeviceNo", it->mPCIAddress.miDevice); + InsertConfigInteger(pNetBtDevCfg, "PCIFunctionNo", it->mPCIAddress.miFn); + } + } + + /* + * Serial (UART) Ports + */ + /* serial enabled mask to be passed to dev ACPI */ + uint16_t auSerialIoPortBase[SchemaDefs::SerialPortCount] = {0}; + uint8_t auSerialIrq[SchemaDefs::SerialPortCount] = {0}; + InsertConfigNode(pDevices, "serial", &pDev); + for (ULONG ulInstance = 0; ulInstance < SchemaDefs::SerialPortCount; ++ulInstance) + { + ComPtr<ISerialPort> serialPort; + hrc = pMachine->GetSerialPort(ulInstance, serialPort.asOutParam()); H(); + BOOL fEnabledSerPort = FALSE; + if (serialPort) + { + hrc = serialPort->COMGETTER(Enabled)(&fEnabledSerPort); H(); + } + if (!fEnabledSerPort) + { + m_aeSerialPortMode[ulInstance] = PortMode_Disconnected; + continue; + } + + InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + + ULONG ulIRQ; + hrc = serialPort->COMGETTER(IRQ)(&ulIRQ); H(); + InsertConfigInteger(pCfg, "IRQ", ulIRQ); + auSerialIrq[ulInstance] = (uint8_t)ulIRQ; + + ULONG ulIOBase; + hrc = serialPort->COMGETTER(IOBase)(&ulIOBase); H(); + InsertConfigInteger(pCfg, "IOBase", ulIOBase); + auSerialIoPortBase[ulInstance] = (uint16_t)ulIOBase; + + BOOL fServer; + hrc = serialPort->COMGETTER(Server)(&fServer); H(); + hrc = serialPort->COMGETTER(Path)(bstr.asOutParam()); H(); + UartType_T eUartType; + const char *pszUartType; + hrc = serialPort->COMGETTER(UartType)(&eUartType); H(); + switch (eUartType) + { + case UartType_U16450: pszUartType = "16450"; break; + case UartType_U16750: pszUartType = "16750"; break; + default: AssertFailed(); RT_FALL_THRU(); + case UartType_U16550A: pszUartType = "16550A"; break; + } + InsertConfigString(pCfg, "UartType", pszUartType); + + PortMode_T eHostMode; + hrc = serialPort->COMGETTER(HostMode)(&eHostMode); H(); + + m_aeSerialPortMode[ulInstance] = eHostMode; + if (eHostMode != PortMode_Disconnected) + { + vrc = i_configSerialPort(pInst, eHostMode, Utf8Str(bstr).c_str(), RT_BOOL(fServer)); + if (RT_FAILURE(vrc)) + return vrc; + } + } + + /* + * Parallel (LPT) Ports + */ + /* parallel enabled mask to be passed to dev ACPI */ + uint16_t auParallelIoPortBase[SchemaDefs::ParallelPortCount] = {0}; + uint8_t auParallelIrq[SchemaDefs::ParallelPortCount] = {0}; + InsertConfigNode(pDevices, "parallel", &pDev); + for (ULONG ulInstance = 0; ulInstance < SchemaDefs::ParallelPortCount; ++ulInstance) + { + ComPtr<IParallelPort> parallelPort; + hrc = pMachine->GetParallelPort(ulInstance, parallelPort.asOutParam()); H(); + BOOL fEnabledParPort = FALSE; + if (parallelPort) + { + hrc = parallelPort->COMGETTER(Enabled)(&fEnabledParPort); H(); + } + if (!fEnabledParPort) + continue; + + InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + + ULONG ulIRQ; + hrc = parallelPort->COMGETTER(IRQ)(&ulIRQ); H(); + InsertConfigInteger(pCfg, "IRQ", ulIRQ); + auParallelIrq[ulInstance] = (uint8_t)ulIRQ; + ULONG ulIOBase; + hrc = parallelPort->COMGETTER(IOBase)(&ulIOBase); H(); + InsertConfigInteger(pCfg, "IOBase", ulIOBase); + auParallelIoPortBase[ulInstance] = (uint16_t)ulIOBase; + + hrc = parallelPort->COMGETTER(Path)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + { + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "HostParallel"); + InsertConfigNode(pLunL0, "Config", &pLunL1); + InsertConfigString(pLunL1, "DevicePath", bstr); + } + } + + /* + * VMM Device + */ + InsertConfigNode(pDevices, "VMMDev", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + hrc = pBusMgr->assignPCIDevice("VMMDev", pInst); H(); + + Bstr hwVersion; + hrc = pMachine->COMGETTER(HardwareVersion)(hwVersion.asOutParam()); H(); + if (hwVersion.compare(Bstr("1").raw()) == 0) /* <= 2.0.x */ + InsertConfigInteger(pCfg, "HeapEnabled", 0); + Bstr snapshotFolder; + hrc = pMachine->COMGETTER(SnapshotFolder)(snapshotFolder.asOutParam()); H(); + InsertConfigString(pCfg, "GuestCoreDumpDir", snapshotFolder); + + /* the VMM device's Main driver */ + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "HGCM"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + /* + * Attach the status driver. + */ + i_attachStatusDriver(pInst, DeviceType_SharedFolder, 0, 0, NULL, NULL, NULL, 0); + + /* + * Audio configuration. + */ + + /* + * AC'97 ICH / SoundBlaster16 audio / Intel HD Audio. + */ + ComPtr<IAudioSettings> audioSettings; + hrc = pMachine->COMGETTER(AudioSettings)(audioSettings.asOutParam()); H(); + + BOOL fAudioEnabled = FALSE; + ComPtr<IAudioAdapter> audioAdapter; + hrc = audioSettings->COMGETTER(Adapter)(audioAdapter.asOutParam()); H(); + if (audioAdapter) + { + hrc = audioAdapter->COMGETTER(Enabled)(&fAudioEnabled); H(); + } + + if (fAudioEnabled) + { + AudioControllerType_T enmAudioController; + hrc = audioAdapter->COMGETTER(AudioController)(&enmAudioController); H(); + AudioCodecType_T enmAudioCodec; + hrc = audioAdapter->COMGETTER(AudioCodec)(&enmAudioCodec); H(); + + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Device/TimerHz", &strTmp); + const uint64_t uTimerHz = strTmp.toUInt64(); + + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Device/BufSizeInMs", &strTmp); + const uint64_t uBufSizeInMs = strTmp.toUInt64(); + + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Device/BufSizeOutMs", &strTmp); + const uint64_t uBufSizeOutMs = strTmp.toUInt64(); + + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp); + const bool fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1"); + + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Debug/Level", &strTmp); + const uint32_t uDebugLevel = strTmp.toUInt32(); + + Utf8Str strDebugPathOut; + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut); + +#ifdef VBOX_WITH_AUDIO_VALIDATIONKIT + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/VaKit/Enabled", &strTmp); /* Deprecated; do not use! */ + if (strTmp.isEmpty()) + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/ValKit/Enabled", &strTmp); + /* Whether the Validation Kit audio backend runs as the primary backend. + * Can also be used with VBox release builds. */ + const bool fValKitEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1"); +#endif + /** @todo Implement an audio device class, similar to the audio backend class, to construct the common stuff + * without duplicating (more) code. */ + + const char *pszAudioDevice; + switch (enmAudioController) + { + case AudioControllerType_AC97: + { + /* ICH AC'97. */ + pszAudioDevice = "ichac97"; + + InsertConfigNode(pDevices, pszAudioDevice, &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + hrc = pBusMgr->assignPCIDevice(pszAudioDevice, pInst); H(); + InsertConfigNode(pInst, "Config", &pCfg); + switch (enmAudioCodec) + { + case AudioCodecType_STAC9700: + InsertConfigString(pCfg, "Codec", "STAC9700"); + break; + case AudioCodecType_AD1980: + InsertConfigString(pCfg, "Codec", "AD1980"); + break; + default: AssertFailedBreak(); + } + if (uTimerHz) + InsertConfigInteger(pCfg, "TimerHz", uTimerHz); + if (uBufSizeInMs) + InsertConfigInteger(pCfg, "BufSizeInMs", uBufSizeInMs); + if (uBufSizeOutMs) + InsertConfigInteger(pCfg, "BufSizeOutMs", uBufSizeOutMs); + InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled); + if (strDebugPathOut.isNotEmpty()) + InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut); + break; + } + case AudioControllerType_SB16: + { + /* Legacy SoundBlaster16. */ + pszAudioDevice = "sb16"; + + InsertConfigNode(pDevices, pszAudioDevice, &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigInteger(pCfg, "IRQ", 5); + InsertConfigInteger(pCfg, "DMA", 1); + InsertConfigInteger(pCfg, "DMA16", 5); + InsertConfigInteger(pCfg, "Port", 0x220); + InsertConfigInteger(pCfg, "Version", 0x0405); + if (uTimerHz) + InsertConfigInteger(pCfg, "TimerHz", uTimerHz); + InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled); + if (strDebugPathOut.isNotEmpty()) + InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut); + break; + } + case AudioControllerType_HDA: + { + /* Intel HD Audio. */ + pszAudioDevice = "hda"; + + InsertConfigNode(pDevices, pszAudioDevice, &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + hrc = pBusMgr->assignPCIDevice(pszAudioDevice, pInst); H(); + InsertConfigNode(pInst, "Config", &pCfg); + if (uBufSizeInMs) + InsertConfigInteger(pCfg, "BufSizeInMs", uBufSizeInMs); + if (uBufSizeOutMs) + InsertConfigInteger(pCfg, "BufSizeOutMs", uBufSizeOutMs); + InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled); + if (strDebugPathOut.isNotEmpty()) + InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut); + + /* macOS guests uses a different HDA variant to make 10.14+ (or maybe 10.13?) recognize the device. */ + if (fOsXGuest) + InsertConfigString(pCfg, "DeviceName", "Intel Sunrise Point"); + break; + } + default: + pszAudioDevice = "oops"; + AssertFailedBreak(); + } + + PCFGMNODE pCfgAudioAdapter = NULL; + InsertConfigNode(pInst, "AudioConfig", &pCfgAudioAdapter); + SafeArray<BSTR> audioProps; + hrc = audioAdapter->COMGETTER(PropertiesList)(ComSafeArrayAsOutParam(audioProps)); H(); + + std::list<Utf8Str> audioPropertyNamesList; + for (size_t i = 0; i < audioProps.size(); ++i) + { + Bstr bstrValue; + audioPropertyNamesList.push_back(Utf8Str(audioProps[i])); + hrc = audioAdapter->GetProperty(audioProps[i], bstrValue.asOutParam()); + Utf8Str strKey(audioProps[i]); + InsertConfigString(pCfgAudioAdapter, strKey.c_str(), bstrValue); + } + + /* + * The audio driver. + */ + const char *pszAudioDriver = NULL; +#ifdef VBOX_WITH_AUDIO_VALIDATIONKIT + if (fValKitEnabled) + { + pszAudioDriver = "ValidationKitAudio"; + LogRel(("Audio: ValidationKit driver active\n")); + } +#endif + /* If nothing else was selected before, ask the API. */ + if (pszAudioDriver == NULL) + { + AudioDriverType_T enmAudioDriver; + hrc = audioAdapter->COMGETTER(AudioDriver)(&enmAudioDriver); H(); + + /* The "Default" audio driver needs special treatment, as we need to figure out which driver to use + * by default on the current platform. */ + bool const fUseDefaultDrv = enmAudioDriver == AudioDriverType_Default; + + AudioDriverType_T const enmDefaultAudioDriver = settings::MachineConfigFile::getHostDefaultAudioDriver(); + + if (fUseDefaultDrv) + { + enmAudioDriver = enmDefaultAudioDriver; + if (enmAudioDriver == AudioDriverType_Null) + LogRel(("Audio: Warning: No default driver detected for current platform -- defaulting to Null audio backend\n")); + } + + switch (enmAudioDriver) + { + case AudioDriverType_Default: /* Can't happen, but handle it anyway. */ + RT_FALL_THROUGH(); + case AudioDriverType_Null: + pszAudioDriver = "NullAudio"; + break; +#ifdef RT_OS_WINDOWS +# ifdef VBOX_WITH_WINMM + case AudioDriverType_WinMM: +# error "Port WinMM audio backend!" /** @todo Still needed? */ + break; +# endif + case AudioDriverType_DirectSound: + /* Use the Windows Audio Session (WAS) API rather than Direct Sound on Windows + versions we've tested it on (currently W7+). Since Vista, Direct Sound has + been emulated on top of WAS according to the docs, so better use WAS directly. + + Set extradata value "VBoxInternal2/Audio/WindowsDrv" "dsound" to no use WasAPI. + + Keep this hack for backwards compatibility (introduced < 7.0). + */ + GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/WindowsDrv", &strTmp); H(); + if ( enmDefaultAudioDriver == AudioDriverType_WAS + && ( strTmp.isEmpty() + || strTmp.equalsIgnoreCase("was") + || strTmp.equalsIgnoreCase("wasapi")) ) + { + /* Nothing to do here, fall through to WAS driver. */ + } + else + { + pszAudioDriver = "DSoundAudio"; + break; + } + RT_FALL_THROUGH(); + case AudioDriverType_WAS: + if (enmDefaultAudioDriver == AudioDriverType_WAS) /* WAS supported? */ + pszAudioDriver = "HostAudioWas"; + else if (enmDefaultAudioDriver == AudioDriverType_DirectSound) + { + LogRel(("Audio: Warning: Windows Audio Session (WAS) not supported, defaulting to DirectSound backend\n")); + pszAudioDriver = "DSoundAudio"; + } + break; +#endif /* RT_OS_WINDOWS */ +#ifdef RT_OS_SOLARIS + case AudioDriverType_SolAudio: + /* Should not happen, as the Solaris Audio backend is not around anymore. + * Remove this sometime later. */ + LogRel(("Audio: Warning: Solaris Audio is deprecated, please switch to OSS!\n")); + LogRel(("Audio: Automatically setting host audio backend to OSS\n")); + + /* Manually set backend to OSS for now. */ + pszAudioDriver = "OSSAudio"; + break; +#endif +#ifdef VBOX_WITH_AUDIO_OSS + case AudioDriverType_OSS: + pszAudioDriver = "OSSAudio"; + break; +#endif +#ifdef VBOX_WITH_AUDIO_ALSA + case AudioDriverType_ALSA: + pszAudioDriver = "ALSAAudio"; + break; +#endif +#ifdef VBOX_WITH_AUDIO_PULSE + case AudioDriverType_Pulse: + pszAudioDriver = "PulseAudio"; + break; +#endif +#ifdef RT_OS_DARWIN + case AudioDriverType_CoreAudio: + pszAudioDriver = "CoreAudio"; + break; +#endif + default: + pszAudioDriver = "oops"; + AssertFailedBreak(); + } + + if (fUseDefaultDrv) + LogRel(("Audio: Detected default audio driver type is '%s'\n", pszAudioDriver)); + } + + BOOL fAudioEnabledIn = FALSE; + hrc = audioAdapter->COMGETTER(EnabledIn)(&fAudioEnabledIn); H(); + BOOL fAudioEnabledOut = FALSE; + hrc = audioAdapter->COMGETTER(EnabledOut)(&fAudioEnabledOut); H(); + + unsigned idxAudioLun = 0; + + InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun); + i_configAudioDriver(virtualBox, pMachine, pLunL0, pszAudioDriver, !!fAudioEnabledIn, !!fAudioEnabledOut); + idxAudioLun++; + +#ifdef VBOX_WITH_AUDIO_VRDE + /* Insert dummy audio driver to have the LUN configured. */ + InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun); + InsertConfigString(pLunL0, "Driver", "AUDIO"); + AudioDriverCfg DrvCfgVRDE(pszAudioDevice, 0 /* Instance */, idxAudioLun, "AudioVRDE", + !!fAudioEnabledIn, !!fAudioEnabledOut); + vrc = mAudioVRDE->InitializeConfig(&DrvCfgVRDE); + AssertRCStmt(vrc, throw ConfigError(__FUNCTION__, vrc, "mAudioVRDE->InitializeConfig failed")); + idxAudioLun++; +#endif + +#ifdef VBOX_WITH_AUDIO_RECORDING + /* Insert dummy audio driver to have the LUN configured. */ + InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun); + InsertConfigString(pLunL0, "Driver", "AUDIO"); + AudioDriverCfg DrvCfgVideoRec(pszAudioDevice, 0 /* Instance */, idxAudioLun, "AudioVideoRec", + false /*a_fEnabledIn*/, true /*a_fEnabledOut*/); + vrc = mRecording.mAudioRec->InitializeConfig(&DrvCfgVideoRec); + AssertRCStmt(vrc, throw ConfigError(__FUNCTION__, vrc, "Recording.mAudioRec->InitializeConfig failed")); + idxAudioLun++; +#endif + + if (fDebugEnabled) + { +#ifdef VBOX_WITH_AUDIO_DEBUG +# ifdef VBOX_WITH_AUDIO_VALIDATIONKIT + /* + * When both, ValidationKit and Debug mode (for audio) are enabled, + * skip configuring the Debug audio driver, as both modes can + * mess with the audio data and would lead to side effects. + * + * The ValidationKit audio driver has precedence over the Debug audio driver. + * + * This also can (and will) be used in VBox release builds. + */ + if (fValKitEnabled) + { + LogRel(("Audio: Warning: ValidationKit running and Debug mode enabled -- disabling Debug driver\n")); + } + else /* Debug mode active -- run both (nice for catching errors / doing development). */ + { + /* + * The ValidationKit backend. + */ + InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun); + i_configAudioDriver(virtualBox, pMachine, pLunL0, "ValidationKitAudio", + !!fAudioEnabledIn, !!fAudioEnabledOut); + idxAudioLun++; +# endif /* VBOX_WITH_AUDIO_VALIDATIONKIT */ + /* + * The Debug audio backend. + */ + InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun); + i_configAudioDriver(virtualBox, pMachine, pLunL0, "DebugAudio", + !!fAudioEnabledIn, !!fAudioEnabledOut); + idxAudioLun++; +# ifdef VBOX_WITH_AUDIO_VALIDATIONKIT + } +# endif /* VBOX_WITH_AUDIO_VALIDATIONKIT */ +#endif /* VBOX_WITH_AUDIO_DEBUG */ + + /* + * Tweak the logging groups. + */ + Utf8Str strGroups("drv_audio.e.l.l2.l3.f" + " audio_mixer.e.l.l2.l3.f" + " dev_hda_codec.e.l.l2.l3.f" + " dev_hda.e.l.l2.l3.f" + " dev_ac97.e.l.l2.l3.f" + " dev_sb16.e.l.l2.l3.f"); + + LogRel(("Audio: Debug level set to %RU32\n", uDebugLevel)); + + switch (uDebugLevel) + { + case 0: + strGroups += " drv_host_audio.e.l.l2.l3.f"; + break; + case 1: + RT_FALL_THROUGH(); + case 2: + RT_FALL_THROUGH(); + case 3: + strGroups += " drv_host_audio.e.l.l2.l3.f+audio_test.e.l.l2.l3.f"; + break; + case 4: + RT_FALL_THROUGH(); + default: + strGroups += " drv_host_audio.e.l.l2.l3.l4.f+audio_test.e.l.l2.l3.l4.f"; + break; + } + + vrc = RTLogGroupSettings(RTLogRelGetDefaultInstance(), strGroups.c_str()); + if (RT_FAILURE(vrc)) + LogRel(("Audio: Setting debug logging failed, vrc=%Rrc\n", vrc)); + } + } + +#ifdef VBOX_WITH_SHARED_CLIPBOARD + /* + * Shared Clipboard. + */ + { + ClipboardMode_T enmClipboardMode = ClipboardMode_Disabled; + hrc = pMachine->COMGETTER(ClipboardMode)(&enmClipboardMode); H(); +# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + BOOL fFileTransfersEnabled; + hrc = pMachine->COMGETTER(ClipboardFileTransfersEnabled)(&fFileTransfersEnabled); H(); +#endif + + /* Load the service */ + vrc = pVMMDev->hgcmLoadService("VBoxSharedClipboard", "VBoxSharedClipboard"); + if (RT_SUCCESS(vrc)) + { + LogRel(("Shared Clipboard: Service loaded\n")); + + /* Set initial clipboard mode. */ + vrc = i_changeClipboardMode(enmClipboardMode); + AssertLogRelMsg(RT_SUCCESS(vrc), ("Shared Clipboard: Failed to set initial clipboard mode (%d): vrc=%Rrc\n", + enmClipboardMode, vrc)); + + /* Setup the service. */ + VBOXHGCMSVCPARM parm; + HGCMSvcSetU32(&parm, !i_useHostClipboard()); + vrc = pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, &parm); + AssertLogRelMsg(RT_SUCCESS(vrc), ("Shared Clipboard: Failed to set initial headless mode (%RTbool): vrc=%Rrc\n", + !i_useHostClipboard(), vrc)); + +# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + vrc = i_changeClipboardFileTransferMode(RT_BOOL(fFileTransfersEnabled)); + AssertLogRelMsg(RT_SUCCESS(vrc), ("Shared Clipboard: Failed to set initial file transfers mode (%u): vrc=%Rrc\n", + fFileTransfersEnabled, vrc)); + + /** @todo Register area callbacks? (See also deregistration todo in Console::i_powerDown.) */ +# endif + } + else + LogRel(("Shared Clipboard: Not available, vrc=%Rrc\n", vrc)); + vrc = VINF_SUCCESS; /* None of the potential failures above are fatal. */ + } +#endif /* VBOX_WITH_SHARED_CLIPBOARD */ + + /* + * HGCM HostChannel. + */ + { + Bstr value; + hrc = pMachine->GetExtraData(Bstr("HGCM/HostChannel").raw(), + value.asOutParam()); + + if ( hrc == S_OK + && value == "1") + { + vrc = pVMMDev->hgcmLoadService("VBoxHostChannel", "VBoxHostChannel"); + if (RT_FAILURE(vrc)) + { + LogRel(("VBoxHostChannel is not available, vrc=%Rrc\n", vrc)); + /* That is not a fatal failure. */ + vrc = VINF_SUCCESS; + } + } + } + +#ifdef VBOX_WITH_DRAG_AND_DROP + /* + * Drag and Drop. + */ + { + DnDMode_T enmMode = DnDMode_Disabled; + hrc = pMachine->COMGETTER(DnDMode)(&enmMode); H(); + + /* Load the service */ + vrc = pVMMDev->hgcmLoadService("VBoxDragAndDropSvc", "VBoxDragAndDropSvc"); + if (RT_FAILURE(vrc)) + { + LogRel(("Drag and drop service is not available, vrc=%Rrc\n", vrc)); + /* That is not a fatal failure. */ + vrc = VINF_SUCCESS; + } + else + { + vrc = HGCMHostRegisterServiceExtension(&m_hHgcmSvcExtDragAndDrop, "VBoxDragAndDropSvc", + &GuestDnD::notifyDnDDispatcher, + GuestDnDInst()); + if (RT_FAILURE(vrc)) + Log(("Cannot register VBoxDragAndDropSvc extension, vrc=%Rrc\n", vrc)); + else + { + LogRel(("Drag and drop service loaded\n")); + vrc = i_changeDnDMode(enmMode); + } + } + } +#endif /* VBOX_WITH_DRAG_AND_DROP */ + +#if defined(VBOX_WITH_TPM) + /* + * Configure the Trusted Platform Module. + */ + ComObjPtr<ITrustedPlatformModule> ptrTpm; + TpmType_T enmTpmType = TpmType_None; + + hrc = pMachine->COMGETTER(TrustedPlatformModule)(ptrTpm.asOutParam()); H(); + hrc = ptrTpm->COMGETTER(Type)(&enmTpmType); H(); + if (enmTpmType != TpmType_None) + { + InsertConfigNode(pDevices, "tpm", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + InsertConfigNode(pInst, "LUN#0", &pLunL0); + + switch (enmTpmType) + { + case TpmType_v1_2: + case TpmType_v2_0: + { + InsertConfigString(pLunL0, "Driver", "TpmEmuTpms"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "TpmVersion", enmTpmType == TpmType_v1_2 ? 1 : 2); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "NvramStore"); + break; + } + case TpmType_Host: + { +#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS) + InsertConfigString(pLunL0, "Driver", "TpmHost"); + InsertConfigNode(pLunL0, "Config", &pCfg); +#endif + break; + } + case TpmType_Swtpm: + { + Bstr location; + hrc = ptrTpm->COMGETTER(Location)(location.asOutParam()); H(); + + InsertConfigString(pLunL0, "Driver", "TpmEmu"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigString(pCfg, "Location", location); + break; + } + default: + AssertFailedBreak(); + } + } +#endif + + /* + * ACPI + */ + BOOL fACPI; + hrc = biosSettings->COMGETTER(ACPIEnabled)(&fACPI); H(); + if (fACPI) + { + /* Always show the CPU leafs when we have multiple VCPUs or when the IO-APIC is enabled. + * The Windows SMP kernel needs a CPU leaf or else its idle loop will burn cpu cycles; the + * intelppm driver refuses to register an idle state handler. + * Always show CPU leafs for OS X guests. */ + BOOL fShowCpu = fOsXGuest; + if (cCpus > 1 || fIOAPIC) + fShowCpu = true; + + BOOL fCpuHotPlug; + hrc = pMachine->COMGETTER(CPUHotPlugEnabled)(&fCpuHotPlug); H(); + + InsertConfigNode(pDevices, "acpi", &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + InsertConfigNode(pInst, "Config", &pCfg); + hrc = pBusMgr->assignPCIDevice("acpi", pInst); H(); + + InsertConfigInteger(pCfg, "NumCPUs", cCpus); + + InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC); + InsertConfigInteger(pCfg, "FdcEnabled", fFdcEnabled); + InsertConfigInteger(pCfg, "HpetEnabled", fHPETEnabled); + InsertConfigInteger(pCfg, "SmcEnabled", fSmcEnabled); + InsertConfigInteger(pCfg, "ShowRtc", fShowRtc); + if (fOsXGuest && !llBootNics.empty()) + { + BootNic aNic = llBootNics.front(); + uint32_t u32NicPCIAddr = (aNic.mPCIAddress.miDevice << 16) | aNic.mPCIAddress.miFn; + InsertConfigInteger(pCfg, "NicPciAddress", u32NicPCIAddr); + } + if (fOsXGuest && fAudioEnabled) + { + PCIBusAddress Address; + if (pBusMgr->findPCIAddress("hda", 0, Address)) + { + uint32_t u32AudioPCIAddr = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "AudioPciAddress", u32AudioPCIAddr); + } + } + if (fOsXGuest) + { + PCIBusAddress Address; + if (pBusMgr->findPCIAddress("nvme", 0, Address)) + { + uint32_t u32NvmePCIAddr = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "NvmePciAddress", u32NvmePCIAddr); + } + } + if (enmIommuType == IommuType_AMD) + { + PCIBusAddress Address; + if (pBusMgr->findPCIAddress("iommu-amd", 0, Address)) + { + uint32_t u32IommuAddress = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "IommuAmdEnabled", true); + InsertConfigInteger(pCfg, "IommuPciAddress", u32IommuAddress); + if (pBusMgr->findPCIAddress("sb-ioapic", 0, Address)) + { + uint32_t const u32SbIoapicAddress = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "SbIoApicPciAddress", u32SbIoapicAddress); + } + else + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("AMD IOMMU is enabled, but the I/O APIC is not assigned a PCI address!")); + } + } + else if (enmIommuType == IommuType_Intel) + { + PCIBusAddress Address; + if (pBusMgr->findPCIAddress("iommu-intel", 0, Address)) + { + uint32_t u32IommuAddress = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "IommuIntelEnabled", true); + InsertConfigInteger(pCfg, "IommuPciAddress", u32IommuAddress); + if (pBusMgr->findPCIAddress("sb-ioapic", 0, Address)) + { + uint32_t const u32SbIoapicAddress = (Address.miDevice << 16) | Address.miFn; + InsertConfigInteger(pCfg, "SbIoApicPciAddress", u32SbIoapicAddress); + } + else + return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, + N_("Intel IOMMU is enabled, but the I/O APIC is not assigned a PCI address!")); + } + } + + InsertConfigInteger(pCfg, "IocPciAddress", uIocPCIAddress); + if (chipsetType == ChipsetType_ICH9) + { + InsertConfigInteger(pCfg, "McfgBase", uMcfgBase); + InsertConfigInteger(pCfg, "McfgLength", cbMcfgLength); + /* 64-bit prefetch window root resource: Only for ICH9 and if PAE or Long Mode is enabled (@bugref{5454}). */ + if (fIsGuest64Bit || fEnablePAE) + InsertConfigInteger(pCfg, "PciPref64Enabled", 1); + } + InsertConfigInteger(pCfg, "HostBusPciAddress", uHbcPCIAddress); + InsertConfigInteger(pCfg, "ShowCpu", fShowCpu); + InsertConfigInteger(pCfg, "CpuHotPlug", fCpuHotPlug); + + InsertConfigInteger(pCfg, "Serial0IoPortBase", auSerialIoPortBase[0]); + InsertConfigInteger(pCfg, "Serial0Irq", auSerialIrq[0]); + + InsertConfigInteger(pCfg, "Serial1IoPortBase", auSerialIoPortBase[1]); + InsertConfigInteger(pCfg, "Serial1Irq", auSerialIrq[1]); + + if (auSerialIoPortBase[2]) + { + InsertConfigInteger(pCfg, "Serial2IoPortBase", auSerialIoPortBase[2]); + InsertConfigInteger(pCfg, "Serial2Irq", auSerialIrq[2]); + } + + if (auSerialIoPortBase[3]) + { + InsertConfigInteger(pCfg, "Serial3IoPortBase", auSerialIoPortBase[3]); + InsertConfigInteger(pCfg, "Serial3Irq", auSerialIrq[3]); + } + + InsertConfigInteger(pCfg, "Parallel0IoPortBase", auParallelIoPortBase[0]); + InsertConfigInteger(pCfg, "Parallel0Irq", auParallelIrq[0]); + + InsertConfigInteger(pCfg, "Parallel1IoPortBase", auParallelIoPortBase[1]); + InsertConfigInteger(pCfg, "Parallel1Irq", auParallelIrq[1]); + +#if defined(VBOX_WITH_TPM) + switch (enmTpmType) + { + case TpmType_v1_2: + InsertConfigString(pCfg, "TpmMode", "tis1.2"); + break; + case TpmType_v2_0: + InsertConfigString(pCfg, "TpmMode", "fifo2.0"); + break; + /** @todo Host and swtpm. */ + default: + break; + } +#endif + + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "ACPIHost"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + /* Attach the dummy CPU drivers */ + for (ULONG iCpuCurr = 1; iCpuCurr < cCpus; iCpuCurr++) + { + BOOL fCpuAttached = true; + + if (fCpuHotPlug) + { + hrc = pMachine->GetCPUStatus(iCpuCurr, &fCpuAttached); H(); + } + + if (fCpuAttached) + { + InsertConfigNode(pInst, Utf8StrFmt("LUN#%u", iCpuCurr).c_str(), &pLunL0); + InsertConfigString(pLunL0, "Driver", "ACPICpu"); + InsertConfigNode(pLunL0, "Config", &pCfg); + } + } + } + + /* + * Configure DBGF (Debug(ger) Facility) and DBGC (Debugger Console). + */ + { + PCFGMNODE pDbgf; + InsertConfigNode(pRoot, "DBGF", &pDbgf); + + /* Paths to search for debug info and such things. */ + hrc = pMachine->COMGETTER(SettingsFilePath)(bstr.asOutParam()); H(); + Utf8Str strSettingsPath(bstr); + bstr.setNull(); + strSettingsPath.stripFilename(); + strSettingsPath.append("/"); + + char szHomeDir[RTPATH_MAX + 1]; + int vrc2 = RTPathUserHome(szHomeDir, sizeof(szHomeDir) - 1); + if (RT_FAILURE(vrc2)) + szHomeDir[0] = '\0'; + RTPathEnsureTrailingSeparator(szHomeDir, sizeof(szHomeDir)); + + + Utf8Str strPath; + strPath.append(strSettingsPath).append("debug/;"); + strPath.append(strSettingsPath).append(";"); + strPath.append("cache*").append(strSettingsPath).append("dbgcache/;"); /* handy for symlinking to actual cache */ + strPath.append(szHomeDir); + + InsertConfigString(pDbgf, "Path", strPath.c_str()); + + /* Tracing configuration. */ + BOOL fTracingEnabled; + hrc = pMachine->COMGETTER(TracingEnabled)(&fTracingEnabled); H(); + if (fTracingEnabled) + InsertConfigInteger(pDbgf, "TracingEnabled", 1); + + hrc = pMachine->COMGETTER(TracingConfig)(bstr.asOutParam()); H(); + if (fTracingEnabled) + InsertConfigString(pDbgf, "TracingConfig", bstr); + + BOOL fAllowTracingToAccessVM; + hrc = pMachine->COMGETTER(AllowTracingToAccessVM)(&fAllowTracingToAccessVM); H(); + if (fAllowTracingToAccessVM) + InsertConfigInteger(pPDM, "AllowTracingToAccessVM", 1); + + /* Debugger console config. */ + PCFGMNODE pDbgc; + InsertConfigNode(pRoot, "DBGC", &pDbgc); + + hrc = virtualBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H(); + Utf8Str strVBoxHome = bstr; + bstr.setNull(); + if (strVBoxHome.isNotEmpty()) + strVBoxHome.append("/"); + else + { + strVBoxHome = szHomeDir; + strVBoxHome.append("/.vbox"); + } + + Utf8Str strFile(strVBoxHome); + strFile.append("dbgc-history"); + InsertConfigString(pDbgc, "HistoryFile", strFile); + + strFile = strSettingsPath; + strFile.append("dbgc-init"); + InsertConfigString(pDbgc, "LocalInitScript", strFile); + + strFile = strVBoxHome; + strFile.append("dbgc-init"); + InsertConfigString(pDbgc, "GlobalInitScript", strFile); + + /* + * Configure guest debug settings. + */ + ComObjPtr<IGuestDebugControl> ptrGstDbgCtrl; + GuestDebugProvider_T enmGstDbgProvider = GuestDebugProvider_None; + + hrc = pMachine->COMGETTER(GuestDebugControl)(ptrGstDbgCtrl.asOutParam()); H(); + hrc = ptrGstDbgCtrl->COMGETTER(DebugProvider)(&enmGstDbgProvider); H(); + if (enmGstDbgProvider != GuestDebugProvider_None) + { + GuestDebugIoProvider_T enmGstDbgIoProvider = GuestDebugIoProvider_None; + hrc = ptrGstDbgCtrl->COMGETTER(DebugIoProvider)(&enmGstDbgIoProvider); H(); + hrc = ptrGstDbgCtrl->COMGETTER(DebugAddress)(bstr.asOutParam()); H(); + Utf8Str strAddress = bstr; + bstr.setNull(); + + ULONG ulPort = 0; + hrc = ptrGstDbgCtrl->COMGETTER(DebugPort)(&ulPort); H(); + + PCFGMNODE pDbgSettings; + InsertConfigNode(pDbgc, "Dbg", &pDbgSettings); + InsertConfigString(pDbgSettings, "Address", strAddress); + InsertConfigInteger(pDbgSettings, "Port", ulPort); + + switch (enmGstDbgProvider) + { + case GuestDebugProvider_Native: + InsertConfigString(pDbgSettings, "StubType", "Native"); + break; + case GuestDebugProvider_GDB: + InsertConfigString(pDbgSettings, "StubType", "Gdb"); + break; + case GuestDebugProvider_KD: + InsertConfigString(pDbgSettings, "StubType", "Kd"); + break; + default: + AssertFailed(); + break; + } + + switch (enmGstDbgIoProvider) + { + case GuestDebugIoProvider_TCP: + InsertConfigString(pDbgSettings, "Provider", "tcp"); + break; + case GuestDebugIoProvider_UDP: + InsertConfigString(pDbgSettings, "Provider", "udp"); + break; + case GuestDebugIoProvider_IPC: + InsertConfigString(pDbgSettings, "Provider", "ipc"); + break; + default: + AssertFailed(); + break; + } + } + } + } + catch (ConfigError &x) + { + // InsertConfig threw something: + pVMM->pfnVMR3SetError(pUVM, x.m_vrc, RT_SRC_POS, "Caught ConfigError: %Rrc - %s", x.m_vrc, x.what()); + return x.m_vrc; + } + catch (HRESULT hrcXcpt) + { + AssertLogRelMsgFailedReturn(("hrc=%Rhrc\n", hrcXcpt), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR); + } + +#ifdef VBOX_WITH_EXTPACK + /* + * Call the extension pack hooks if everything went well thus far. + */ + if (RT_SUCCESS(vrc)) + { + pAlock->release(); + vrc = mptrExtPackManager->i_callAllVmConfigureVmmHooks(this, pVM, pVMM); + pAlock->acquire(); + } +#endif + + /* + * Apply the CFGM overlay. + */ + if (RT_SUCCESS(vrc)) + vrc = i_configCfgmOverlay(pRoot, virtualBox, pMachine); + + /* + * Dump all extradata API settings tweaks, both global and per VM. + */ + if (RT_SUCCESS(vrc)) + vrc = i_configDumpAPISettingsTweaks(virtualBox, pMachine); + +#undef H + + pAlock->release(); /* Avoid triggering the lock order inversion check. */ + + /* + * Register VM state change handler. + */ + int vrc2 = pVMM->pfnVMR3AtStateRegister(pUVM, Console::i_vmstateChangeCallback, this); + AssertRC(vrc2); + if (RT_SUCCESS(vrc)) + vrc = vrc2; + + /* + * Register VM runtime error handler. + */ + vrc2 = pVMM->pfnVMR3AtRuntimeErrorRegister(pUVM, Console::i_atVMRuntimeErrorCallback, this); + AssertRC(vrc2); + if (RT_SUCCESS(vrc)) + vrc = vrc2; + + pAlock->acquire(); + + LogFlowFunc(("vrc = %Rrc\n", vrc)); + LogFlowFuncLeave(); + + return vrc; +} + +/** + * Configures an audio driver via CFGM by getting (optional) values from extra data. + * + * @param pVirtualBox Pointer to IVirtualBox instance. + * @param pMachine Pointer to IMachine instance. + * @param pLUN Pointer to CFGM node of LUN (the driver) to configure. + * @param pszDrvName Name of the driver to configure. + * @param fAudioEnabledIn IAudioAdapter::enabledIn value. + * @param fAudioEnabledOut IAudioAdapter::enabledOut value. + * + * @throws ConfigError or HRESULT on if there is trouble. + */ +void Console::i_configAudioDriver(IVirtualBox *pVirtualBox, IMachine *pMachine, PCFGMNODE pLUN, const char *pszDrvName, + bool fAudioEnabledIn, bool fAudioEnabledOut) +{ +#define H() AssertLogRelMsgStmt(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), \ + throw ConfigError(__FUNCTION__, VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR, "line: " RT_XSTR(__LINE__))) + + InsertConfigString(pLUN, "Driver", "AUDIO"); + + PCFGMNODE pCfg; + InsertConfigNode(pLUN, "Config", &pCfg); + InsertConfigString(pCfg, "DriverName", pszDrvName); + InsertConfigInteger(pCfg, "InputEnabled", fAudioEnabledIn); + InsertConfigInteger(pCfg, "OutputEnabled", fAudioEnabledOut); + + Utf8Str strTmp; + GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp); + const uint64_t fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1"); + if (fDebugEnabled) + { + InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled); + + Utf8Str strDebugPathOut; + GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut); + InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut.c_str()); + } + + /* + * PCM input parameters (playback + recording). + * We have host driver specific ones as: VBoxInternal2/Audio/<DrvName>/<Value> + * And global ones for all host drivers: VBoxInternal2/Audio/<Value> + */ + for (unsigned iDir = 0; iDir < 2; iDir++) + { + static const struct + { + const char *pszExtraName; + const char *pszCfgmName; + } s_aToCopy[] = + { /* PCM parameters: */ + { "PCMSampleBit", "PCMSampleBit" }, + { "PCMSampleHz", "PCMSampleHz" }, + { "PCMSampleSigned", "PCMSampleSigned" }, + { "PCMSampleSwapEndian", "PCMSampleSwapEndian" }, + { "PCMSampleChannels", "PCMSampleChannels" }, + /* Buffering stuff: */ + { "PeriodSizeMs", "PeriodSizeMs" }, + { "BufferSizeMs", "BufferSizeMs" }, + { "PreBufferSizeMs", "PreBufferSizeMs" }, + }; + + PCFGMNODE pDirNode = NULL; + const char *pszDir = iDir == 0 ? "In" : "Out"; + for (size_t i = 0; i < RT_ELEMENTS(s_aToCopy); i++) + { + char szExtra[128]; + RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s/%s%s", pszDrvName, s_aToCopy[i].pszExtraName, pszDir); + GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp); /* throws hrc */ + if (strTmp.isEmpty()) + { + RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s%s", s_aToCopy[i].pszExtraName, pszDir); + GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp); + if (strTmp.isEmpty()) + continue; + } + + uint32_t uValue; + int vrc = RTStrToUInt32Full(strTmp.c_str(), 0, &uValue); + if (RT_SUCCESS(vrc)) + { + if (!pDirNode) + InsertConfigNode(pCfg, pszDir, &pDirNode); + InsertConfigInteger(pDirNode, s_aToCopy[i].pszCfgmName, uValue); + } + else + LogRel(("Ignoring malformed 32-bit unsigned integer config value '%s' = '%s': %Rrc\n", szExtra, strTmp.c_str(), vrc)); + } + } + + PCFGMNODE pLunL1; + InsertConfigNode(pLUN, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", pszDrvName); + InsertConfigNode(pLunL1, "Config", &pCfg); + +#ifdef RT_OS_WINDOWS + if (strcmp(pszDrvName, "HostAudioWas") == 0) + { + Bstr bstrTmp; + HRESULT hrc = pMachine->COMGETTER(Id)(bstrTmp.asOutParam()); H(); + InsertConfigString(pCfg, "VmUuid", bstrTmp); + } +#endif + +#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX) + if ( strcmp(pszDrvName, "HostAudioWas") == 0 + || strcmp(pszDrvName, "PulseAudio") == 0) + { + Bstr bstrTmp; + HRESULT hrc = pMachine->COMGETTER(Name)(bstrTmp.asOutParam()); H(); + InsertConfigString(pCfg, "VmName", bstrTmp); + } +#endif + + LogFlowFunc(("szDrivName=%s\n", pszDrvName)); + +#undef H +} + +/** + * Applies the CFGM overlay as specified by VBoxInternal/XXX extra data + * values. + * + * @returns VBox status code. + * @param pRoot The root of the configuration tree. + * @param pVirtualBox Pointer to the IVirtualBox interface. + * @param pMachine Pointer to the IMachine interface. + */ +/* static */ +int Console::i_configCfgmOverlay(PCFGMNODE pRoot, IVirtualBox *pVirtualBox, IMachine *pMachine) +{ + /* + * CFGM overlay handling. + * + * Here we check the extra data entries for CFGM values + * and create the nodes and insert the values on the fly. Existing + * values will be removed and reinserted. CFGM is typed, so by default + * we will guess whether it's a string or an integer (byte arrays are + * not currently supported). It's possible to override this autodetection + * by adding "string:", "integer:" or "bytes:" (future). + * + * We first perform a run on global extra data, then on the machine + * extra data to support global settings with local overrides. + */ + int vrc = VINF_SUCCESS; + bool fFirst = true; + try + { + /** @todo add support for removing nodes and byte blobs. */ + /* + * Get the next key + */ + SafeArray<BSTR> aGlobalExtraDataKeys; + SafeArray<BSTR> aMachineExtraDataKeys; + HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys)); + AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc)); + + // remember the no. of global values so we can call the correct method below + size_t cGlobalValues = aGlobalExtraDataKeys.size(); + + hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys)); + AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc)); + + // build a combined list from global keys... + std::list<Utf8Str> llExtraDataKeys; + + for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i) + llExtraDataKeys.push_back(Utf8Str(aGlobalExtraDataKeys[i])); + // ... and machine keys + for (size_t i = 0; i < aMachineExtraDataKeys.size(); ++i) + llExtraDataKeys.push_back(Utf8Str(aMachineExtraDataKeys[i])); + + size_t i2 = 0; + for (std::list<Utf8Str>::const_iterator it = llExtraDataKeys.begin(); + it != llExtraDataKeys.end(); + ++it, ++i2) + { + const Utf8Str &strKey = *it; + + /* + * We only care about keys starting with "VBoxInternal/" (skip "G:" or "M:") + */ + if (!strKey.startsWith("VBoxInternal/")) + continue; + + const char *pszExtraDataKey = strKey.c_str() + sizeof("VBoxInternal/") - 1; + + // get the value + Bstr bstrExtraDataValue; + if (i2 < cGlobalValues) + // this is still one of the global values: + hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam()); + else + hrc = pMachine->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam()); + if (FAILED(hrc)) + LogRel(("Warning: Cannot get extra data key %s, rc = %Rhrc\n", strKey.c_str(), hrc)); + + if (fFirst) + { + fFirst = false; + LogRel(("Extradata overrides:\n")); + } + LogRel((" %s=\"%ls\"%s\n", strKey.c_str(), bstrExtraDataValue.raw(), i2 < cGlobalValues ? " (global)" : "")); + + /* + * The key will be in the format "Node1/Node2/Value" or simply "Value". + * Split the two and get the node, delete the value and create the node + * if necessary. + */ + PCFGMNODE pNode; + const char *pszCFGMValueName = strrchr(pszExtraDataKey, '/'); + if (pszCFGMValueName) + { + /* terminate the node and advance to the value (Utf8Str might not + offically like this but wtf) */ + *(char *)pszCFGMValueName = '\0'; + ++pszCFGMValueName; + + /* does the node already exist? */ + pNode = mpVMM->pfnCFGMR3GetChild(pRoot, pszExtraDataKey); + if (pNode) + mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName); + else + { + /* create the node */ + vrc = mpVMM->pfnCFGMR3InsertNode(pRoot, pszExtraDataKey, &pNode); + if (RT_FAILURE(vrc)) + { + AssertLogRelMsgRC(vrc, ("failed to insert node '%s'\n", pszExtraDataKey)); + continue; + } + Assert(pNode); + } + } + else + { + /* root value (no node path). */ + pNode = pRoot; + pszCFGMValueName = pszExtraDataKey; + pszExtraDataKey--; + mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName); + } + + /* + * Now let's have a look at the value. + * Empty strings means that we should remove the value, which we've + * already done above. + */ + Utf8Str strCFGMValueUtf8(bstrExtraDataValue); + if (strCFGMValueUtf8.isNotEmpty()) + { + uint64_t u64Value; + + /* check for type prefix first. */ + if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("string:"))) + vrc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str() + sizeof("string:") - 1); + else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("integer:"))) + { + vrc = RTStrToUInt64Full(strCFGMValueUtf8.c_str() + sizeof("integer:") - 1, 0, &u64Value); + if (RT_SUCCESS(vrc)) + vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value); + } + else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("bytes:"))) + { + char const *pszBase64 = strCFGMValueUtf8.c_str() + sizeof("bytes:") - 1; + ssize_t cbValue = RTBase64DecodedSize(pszBase64, NULL); + if (cbValue > 0) + { + void *pvBytes = RTMemTmpAlloc(cbValue); + if (pvBytes) + { + vrc = RTBase64Decode(pszBase64, pvBytes, cbValue, NULL, NULL); + if (RT_SUCCESS(vrc)) + vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, pvBytes, cbValue); + RTMemTmpFree(pvBytes); + } + else + vrc = VERR_NO_TMP_MEMORY; + } + else if (cbValue == 0) + vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, NULL, 0); + else + vrc = VERR_INVALID_BASE64_ENCODING; + } + /* auto detect type. */ + else if (RT_SUCCESS(RTStrToUInt64Full(strCFGMValueUtf8.c_str(), 0, &u64Value))) + vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value); + else + vrc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str()); + AssertLogRelMsgRCBreak(vrc, ("failed to insert CFGM value '%s' to key '%s'\n", + strCFGMValueUtf8.c_str(), pszExtraDataKey)); + } + } + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + return vrc; +} + +/** + * Dumps the API settings tweaks as specified by VBoxInternal2/XXX extra data + * values. + * + * @returns VBox status code. + * @param pVirtualBox Pointer to the IVirtualBox interface. + * @param pMachine Pointer to the IMachine interface. + */ +/* static */ +int Console::i_configDumpAPISettingsTweaks(IVirtualBox *pVirtualBox, IMachine *pMachine) +{ + { + SafeArray<BSTR> aGlobalExtraDataKeys; + HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys)); + AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc)); + bool hasKey = false; + for (size_t i = 0; i < aGlobalExtraDataKeys.size(); i++) + { + Utf8Str strKey(aGlobalExtraDataKeys[i]); + if (!strKey.startsWith("VBoxInternal2/")) + continue; + + Bstr bstrValue; + hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(), + bstrValue.asOutParam()); + if (FAILED(hrc)) + continue; + if (!hasKey) + LogRel(("Global extradata API settings:\n")); + LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw())); + hasKey = true; + } + } + + { + SafeArray<BSTR> aMachineExtraDataKeys; + HRESULT hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys)); + AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc)); + bool hasKey = false; + for (size_t i = 0; i < aMachineExtraDataKeys.size(); i++) + { + Utf8Str strKey(aMachineExtraDataKeys[i]); + if (!strKey.startsWith("VBoxInternal2/")) + continue; + + Bstr bstrValue; + hrc = pMachine->GetExtraData(Bstr(strKey).raw(), + bstrValue.asOutParam()); + if (FAILED(hrc)) + continue; + if (!hasKey) + LogRel(("Per-VM extradata API settings:\n")); + LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw())); + hasKey = true; + } + } + + return VINF_SUCCESS; +} + +int Console::i_configGraphicsController(PCFGMNODE pDevices, + const GraphicsControllerType_T enmGraphicsController, + BusAssignmentManager *pBusMgr, + const ComPtr<IMachine> &ptrMachine, + const ComPtr<IGraphicsAdapter> &ptrGraphicsAdapter, + const ComPtr<IBIOSSettings> &ptrBiosSettings, + bool fHMEnabled) +{ + // InsertConfig* throws + try + { + PCFGMNODE pDev, pInst, pCfg, pLunL0; + HRESULT hrc; + Bstr bstr; + const char *pcszDevice = "vga"; + +#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR) + InsertConfigNode(pDevices, pcszDevice, &pDev); + InsertConfigNode(pDev, "0", &pInst); + InsertConfigInteger(pInst, "Trusted", 1); /* boolean */ + + hrc = pBusMgr->assignPCIDevice(pcszDevice, pInst); H(); + InsertConfigNode(pInst, "Config", &pCfg); + ULONG cVRamMBs; + hrc = ptrGraphicsAdapter->COMGETTER(VRAMSize)(&cVRamMBs); H(); + InsertConfigInteger(pCfg, "VRamSize", cVRamMBs * _1M); + ULONG cMonitorCount; + hrc = ptrGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitorCount); H(); + InsertConfigInteger(pCfg, "MonitorCount", cMonitorCount); +#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE + InsertConfigInteger(pCfg, "R0Enabled", fHMEnabled); +#else + NOREF(fHMEnabled); +#endif + BOOL f3DEnabled; + hrc = ptrGraphicsAdapter->COMGETTER(Accelerate3DEnabled)(&f3DEnabled); H(); + InsertConfigInteger(pCfg, "3DEnabled", f3DEnabled); + + i_attachStatusDriver(pInst, DeviceType_Graphics3D, 0, 0, NULL, NULL, NULL, 0); + +#ifdef VBOX_WITH_VMSVGA + if ( enmGraphicsController == GraphicsControllerType_VMSVGA + || enmGraphicsController == GraphicsControllerType_VBoxSVGA) + { + InsertConfigInteger(pCfg, "VMSVGAEnabled", true); + if (enmGraphicsController == GraphicsControllerType_VMSVGA) + { + InsertConfigInteger(pCfg, "VMSVGAPciBarLayout", true); + InsertConfigInteger(pCfg, "VMSVGAPciId", true); + } +# ifdef VBOX_WITH_VMSVGA3D + InsertConfigInteger(pCfg, "VMSVGA3dEnabled", f3DEnabled); +# else + LogRel(("VMSVGA3d not available in this build!\n")); +# endif /* VBOX_WITH_VMSVGA3D */ + } +#else + RT_NOREF(enmGraphicsController); +#endif /* VBOX_WITH_VMSVGA */ + + /* Custom VESA mode list */ + unsigned cModes = 0; + for (unsigned iMode = 1; iMode <= 16; ++iMode) + { + char szExtraDataKey[sizeof("CustomVideoModeXX")]; + RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%u", iMode); + hrc = ptrMachine->GetExtraData(Bstr(szExtraDataKey).raw(), bstr.asOutParam()); H(); + if (bstr.isEmpty()) + break; + InsertConfigString(pCfg, szExtraDataKey, bstr); + ++cModes; + } + InsertConfigInteger(pCfg, "CustomVideoModes", cModes); + + /* VESA height reduction */ + ULONG ulHeightReduction; + IFramebuffer *pFramebuffer = NULL; + hrc = i_getDisplay()->QueryFramebuffer(0, &pFramebuffer); + if (SUCCEEDED(hrc) && pFramebuffer) + { + hrc = pFramebuffer->COMGETTER(HeightReduction)(&ulHeightReduction); H(); + pFramebuffer->Release(); + pFramebuffer = NULL; + } + else + { + /* If framebuffer is not available, there is no height reduction. */ + ulHeightReduction = 0; + } + InsertConfigInteger(pCfg, "HeightReduction", ulHeightReduction); + + /* + * BIOS logo + */ + BOOL fFadeIn; + hrc = ptrBiosSettings->COMGETTER(LogoFadeIn)(&fFadeIn); H(); + InsertConfigInteger(pCfg, "FadeIn", fFadeIn ? 1 : 0); + BOOL fFadeOut; + hrc = ptrBiosSettings->COMGETTER(LogoFadeOut)(&fFadeOut); H(); + InsertConfigInteger(pCfg, "FadeOut", fFadeOut ? 1: 0); + ULONG logoDisplayTime; + hrc = ptrBiosSettings->COMGETTER(LogoDisplayTime)(&logoDisplayTime); H(); + InsertConfigInteger(pCfg, "LogoTime", logoDisplayTime); + Bstr logoImagePath; + hrc = ptrBiosSettings->COMGETTER(LogoImagePath)(logoImagePath.asOutParam()); H(); + InsertConfigString(pCfg, "LogoFile", Utf8Str(!logoImagePath.isEmpty() ? logoImagePath : "") ); + + /* + * Boot menu + */ + BIOSBootMenuMode_T eBootMenuMode; + int iShowBootMenu; + hrc = ptrBiosSettings->COMGETTER(BootMenuMode)(&eBootMenuMode); H(); + switch (eBootMenuMode) + { + case BIOSBootMenuMode_Disabled: iShowBootMenu = 0; break; + case BIOSBootMenuMode_MenuOnly: iShowBootMenu = 1; break; + default: iShowBootMenu = 2; break; + } + InsertConfigInteger(pCfg, "ShowBootMenu", iShowBootMenu); + + /* Attach the display. */ + InsertConfigNode(pInst, "LUN#0", &pLunL0); + InsertConfigString(pLunL0, "Driver", "MainDisplay"); + InsertConfigNode(pLunL0, "Config", &pCfg); + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + +#undef H + + return VINF_SUCCESS; +} + + +/** + * Ellipsis to va_list wrapper for calling setVMRuntimeErrorCallback. + */ +void Console::i_atVMRuntimeErrorCallbackF(uint32_t fFlags, const char *pszErrorId, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + i_atVMRuntimeErrorCallback(NULL, this, fFlags, pszErrorId, pszFormat, va); + va_end(va); +} + +/* XXX introduce RT format specifier */ +static uint64_t formatDiskSize(uint64_t u64Size, const char **pszUnit) +{ + if (u64Size > INT64_C(5000)*_1G) + { + *pszUnit = "TB"; + return u64Size / _1T; + } + else if (u64Size > INT64_C(5000)*_1M) + { + *pszUnit = "GB"; + return u64Size / _1G; + } + else + { + *pszUnit = "MB"; + return u64Size / _1M; + } +} + +/** + * Checks the location of the given medium for known bugs affecting the usage + * of the host I/O cache setting. + * + * @returns VBox status code. + * @param pMedium The medium to check. + * @param pfUseHostIOCache Where to store the suggested host I/O cache setting. + */ +int Console::i_checkMediumLocation(IMedium *pMedium, bool *pfUseHostIOCache) +{ +#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR) + /* + * Some sanity checks. + */ + RT_NOREF(pfUseHostIOCache); + ComPtr<IMediumFormat> pMediumFormat; + HRESULT hrc = pMedium->COMGETTER(MediumFormat)(pMediumFormat.asOutParam()); H(); + ULONG uCaps = 0; + com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap; + hrc = pMediumFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); H(); + + for (ULONG j = 0; j < mediumFormatCap.size(); j++) + uCaps |= mediumFormatCap[j]; + + if (uCaps & MediumFormatCapabilities_File) + { + Bstr bstrFile; + hrc = pMedium->COMGETTER(Location)(bstrFile.asOutParam()); H(); + Utf8Str const strFile(bstrFile); + + Bstr bstrSnap; + ComPtr<IMachine> pMachine = i_machine(); + hrc = pMachine->COMGETTER(SnapshotFolder)(bstrSnap.asOutParam()); H(); + Utf8Str const strSnap(bstrSnap); + + RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN; + int vrc2 = RTFsQueryType(strFile.c_str(), &enmFsTypeFile); + AssertMsgRCReturn(vrc2, ("Querying the file type of '%s' failed!\n", strFile.c_str()), vrc2); + + /* Any VM which hasn't created a snapshot or saved the current state of the VM + * won't have a Snapshot folder yet so no need to log anything about the file system + * type of the non-existent directory in such cases. */ + RTFSTYPE enmFsTypeSnap = RTFSTYPE_UNKNOWN; + vrc2 = RTFsQueryType(strSnap.c_str(), &enmFsTypeSnap); + if (RT_SUCCESS(vrc2) && !mfSnapshotFolderDiskTypeShown) + { + LogRel(("File system of '%s' (snapshots) is %s\n", strSnap.c_str(), RTFsTypeName(enmFsTypeSnap))); + mfSnapshotFolderDiskTypeShown = true; + } + LogRel(("File system of '%s' is %s\n", strFile.c_str(), RTFsTypeName(enmFsTypeFile))); + LONG64 i64Size; + hrc = pMedium->COMGETTER(LogicalSize)(&i64Size); H(); +#ifdef RT_OS_WINDOWS + if ( enmFsTypeFile == RTFSTYPE_FAT + && i64Size >= _4G) + { + const char *pszUnit; + uint64_t u64Print = formatDiskSize((uint64_t)i64Size, &pszUnit); + i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected", + N_("The medium '%s' has a logical size of %RU64%s " + "but the file system the medium is located on seems " + "to be FAT(32) which cannot handle files bigger than 4GB.\n" + "We strongly recommend to put all your virtual disk images and " + "the snapshot folder onto an NTFS partition"), + strFile.c_str(), u64Print, pszUnit); + } +#else /* !RT_OS_WINDOWS */ + if ( enmFsTypeFile == RTFSTYPE_FAT + || enmFsTypeFile == RTFSTYPE_EXT + || enmFsTypeFile == RTFSTYPE_EXT2 + || enmFsTypeFile == RTFSTYPE_EXT3 + || enmFsTypeFile == RTFSTYPE_EXT4) + { + RTFILE file; + int vrc = RTFileOpen(&file, strFile.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(vrc)) + { + RTFOFF maxSize; + /* Careful: This function will work only on selected local file systems! */ + vrc = RTFileQueryMaxSizeEx(file, &maxSize); + RTFileClose(file); + if ( RT_SUCCESS(vrc) + && maxSize > 0 + && i64Size > (LONG64)maxSize) + { + const char *pszUnitSiz; + const char *pszUnitMax; + uint64_t u64PrintSiz = formatDiskSize((LONG64)i64Size, &pszUnitSiz); + uint64_t u64PrintMax = formatDiskSize(maxSize, &pszUnitMax); + i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected", /* <= not exact but ... */ + N_("The medium '%s' has a logical size of %RU64%s " + "but the file system the medium is located on can " + "only handle files up to %RU64%s in theory.\n" + "We strongly recommend to put all your virtual disk " + "images and the snapshot folder onto a proper " + "file system (e.g. ext3) with a sufficient size"), + strFile.c_str(), u64PrintSiz, pszUnitSiz, u64PrintMax, pszUnitMax); + } + } + } +#endif /* !RT_OS_WINDOWS */ + + /* + * Snapshot folder: + * Here we test only for a FAT partition as we had to create a dummy file otherwise + */ + if ( enmFsTypeSnap == RTFSTYPE_FAT + && i64Size >= _4G + && !mfSnapshotFolderSizeWarningShown) + { + const char *pszUnit; + uint64_t u64Print = formatDiskSize(i64Size, &pszUnit); + i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected", +#ifdef RT_OS_WINDOWS + N_("The snapshot folder of this VM '%s' seems to be located on " + "a FAT(32) file system. The logical size of the medium '%s' " + "(%RU64%s) is bigger than the maximum file size this file " + "system can handle (4GB).\n" + "We strongly recommend to put all your virtual disk images and " + "the snapshot folder onto an NTFS partition"), +#else + N_("The snapshot folder of this VM '%s' seems to be located on " + "a FAT(32) file system. The logical size of the medium '%s' " + "(%RU64%s) is bigger than the maximum file size this file " + "system can handle (4GB).\n" + "We strongly recommend to put all your virtual disk images and " + "the snapshot folder onto a proper file system (e.g. ext3)"), +#endif + strSnap.c_str(), strFile.c_str(), u64Print, pszUnit); + /* Show this particular warning only once */ + mfSnapshotFolderSizeWarningShown = true; + } + +#ifdef RT_OS_LINUX + /* + * Ext4 bug: Check if the host I/O cache is disabled and the disk image is located + * on an ext4 partition. + * This bug apparently applies to the XFS file system as well. + * Linux 2.6.36 is known to be fixed (tested with 2.6.36-rc4). + */ + + char szOsRelease[128]; + int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOsRelease, sizeof(szOsRelease)); + bool fKernelHasODirectBug = RT_FAILURE(vrc) + || (RTStrVersionCompare(szOsRelease, "2.6.36-rc4") < 0); + + if ( (uCaps & MediumFormatCapabilities_Asynchronous) + && !*pfUseHostIOCache + && fKernelHasODirectBug) + { + if ( enmFsTypeFile == RTFSTYPE_EXT4 + || enmFsTypeFile == RTFSTYPE_XFS) + { + i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected", + N_("The host I/O cache for at least one controller is disabled " + "and the medium '%s' for this VM " + "is located on an %s partition. There is a known Linux " + "kernel bug which can lead to the corruption of the virtual " + "disk image under these conditions.\n" + "Either enable the host I/O cache permanently in the VM " + "settings or put the disk image and the snapshot folder " + "onto a different file system.\n" + "The host I/O cache will now be enabled for this medium"), + strFile.c_str(), enmFsTypeFile == RTFSTYPE_EXT4 ? "ext4" : "xfs"); + *pfUseHostIOCache = true; + } + else if ( ( enmFsTypeSnap == RTFSTYPE_EXT4 + || enmFsTypeSnap == RTFSTYPE_XFS) + && !mfSnapshotFolderExt4WarningShown) + { + i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected", + N_("The host I/O cache for at least one controller is disabled " + "and the snapshot folder for this VM " + "is located on an %s partition. There is a known Linux " + "kernel bug which can lead to the corruption of the virtual " + "disk image under these conditions.\n" + "Either enable the host I/O cache permanently in the VM " + "settings or put the disk image and the snapshot folder " + "onto a different file system.\n" + "The host I/O cache will now be enabled for this medium"), + enmFsTypeSnap == RTFSTYPE_EXT4 ? "ext4" : "xfs"); + *pfUseHostIOCache = true; + mfSnapshotFolderExt4WarningShown = true; + } + } + + /* + * 2.6.18 bug: Check if the host I/O cache is disabled and the host is running + * Linux 2.6.18. See @bugref{8690}. Apparently the same problem as + * documented in https://lkml.org/lkml/2007/2/1/14. We saw such + * kernel oopses on Linux 2.6.18-416.el5. We don't know when this + * was fixed but we _know_ that 2.6.18 EL5 kernels are affected. + */ + bool fKernelAsyncUnreliable = RT_FAILURE(vrc) + || (RTStrVersionCompare(szOsRelease, "2.6.19") < 0); + if ( (uCaps & MediumFormatCapabilities_Asynchronous) + && !*pfUseHostIOCache + && fKernelAsyncUnreliable) + { + i_atVMRuntimeErrorCallbackF(0, "Linux2618TooOld", + N_("The host I/O cache for at least one controller is disabled. " + "There is a known Linux kernel bug which can lead to kernel " + "oopses under heavy load. To our knowledge this bug affects " + "all 2.6.18 kernels.\n" + "Either enable the host I/O cache permanently in the VM " + "settings or switch to a newer host kernel.\n" + "The host I/O cache will now be enabled for this medium")); + *pfUseHostIOCache = true; + } +#endif + } +#undef H + + return VINF_SUCCESS; +} + +/** + * Unmounts the specified medium from the specified device. + * + * @returns VBox status code. + * @param pUVM The usermode VM handle. + * @param pVMM The VMM vtable. + * @param enmBus The storage bus. + * @param enmDevType The device type. + * @param pcszDevice The device emulation. + * @param uInstance Instance of the device. + * @param uLUN The LUN on the device. + * @param fForceUnmount Whether to force unmounting. + */ +int Console::i_unmountMediumFromGuest(PUVM pUVM, PCVMMR3VTABLE pVMM, StorageBus_T enmBus, DeviceType_T enmDevType, + const char *pcszDevice, unsigned uInstance, unsigned uLUN, + bool fForceUnmount) RT_NOEXCEPT +{ + /* Unmount existing media only for floppy and DVD drives. */ + int vrc = VINF_SUCCESS; + PPDMIBASE pBase; + if (enmBus == StorageBus_USB) + vrc = pVMM->pfnPDMR3UsbQueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase); + else if ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI) + || (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD)) + vrc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase); + else /* IDE or Floppy */ + vrc = pVMM->pfnPDMR3QueryLun(pUVM, pcszDevice, uInstance, uLUN, &pBase); + + if (RT_FAILURE(vrc)) + { + if (vrc == VERR_PDM_LUN_NOT_FOUND || vrc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN) + vrc = VINF_SUCCESS; + AssertRC(vrc); + } + else + { + PPDMIMOUNT pIMount = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMOUNT); + AssertReturn(pIMount, VERR_INVALID_POINTER); + + /* Unmount the media (but do not eject the medium!) */ + vrc = pIMount->pfnUnmount(pIMount, fForceUnmount, false /*=fEject*/); + if (vrc == VERR_PDM_MEDIA_NOT_MOUNTED) + vrc = VINF_SUCCESS; + /* for example if the medium is locked */ + else if (RT_FAILURE(vrc)) + return vrc; + } + + return vrc; +} + +/** + * Removes the currently attached medium driver form the specified device + * taking care of the controlelr specific configs wrt. to the attached driver chain. + * + * @returns VBox status code. + * @param pCtlInst The controler instance node in the CFGM tree. + * @param pcszDevice The device name. + * @param uInstance The device instance. + * @param uLUN The device LUN. + * @param enmBus The storage bus. + * @param fAttachDetach Flag whether this is a change while the VM is running + * @param fHotplug Flag whether the guest should be notified about the device change. + * @param fForceUnmount Flag whether to force unmounting the medium even if it is locked. + * @param pUVM The usermode VM handle. + * @param pVMM The VMM vtable. + * @param enmDevType The device type. + * @param ppLunL0 Where to store the node to attach the new config to on success. + */ +int Console::i_removeMediumDriverFromVm(PCFGMNODE pCtlInst, + const char *pcszDevice, + unsigned uInstance, + unsigned uLUN, + StorageBus_T enmBus, + bool fAttachDetach, + bool fHotplug, + bool fForceUnmount, + PUVM pUVM, + PCVMMR3VTABLE pVMM, + DeviceType_T enmDevType, + PCFGMNODE *ppLunL0) +{ + int vrc = VINF_SUCCESS; + bool fAddLun = false; + + /* First check if the LUN already exists. */ + PCFGMNODE pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN); + AssertReturn(!RT_VALID_PTR(pLunL0) || fAttachDetach, VERR_INTERNAL_ERROR); + + if (pLunL0) + { + /* + * Unmount the currently mounted medium if we don't just hot remove the + * complete device (SATA) and it supports unmounting (DVD). + */ + if ( (enmDevType != DeviceType_HardDisk) + && !fHotplug) + { + vrc = i_unmountMediumFromGuest(pUVM, pVMM, enmBus, enmDevType, pcszDevice, uInstance, uLUN, fForceUnmount); + if (RT_FAILURE(vrc)) + return vrc; + } + + /* + * Don't detach the SCSI driver when unmounting the current medium + * (we are not ripping out the device but only eject the medium). + */ + char *pszDriverDetach = NULL; + if ( !fHotplug + && ( (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD) + || enmBus == StorageBus_SAS + || enmBus == StorageBus_SCSI + || enmBus == StorageBus_VirtioSCSI + || enmBus == StorageBus_USB)) + { + /* Get the current attached driver we have to detach. */ + PCFGMNODE pDrvLun = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u/AttachedDriver/", uLUN); + if (pDrvLun) + { + char szDriver[128]; + RT_ZERO(szDriver); + vrc = pVMM->pfnCFGMR3QueryString(pDrvLun, "Driver", &szDriver[0], sizeof(szDriver)); + if (RT_SUCCESS(vrc)) + pszDriverDetach = RTStrDup(&szDriver[0]); + + pLunL0 = pDrvLun; + } + } + + if (enmBus == StorageBus_USB) + vrc = pVMM->pfnPDMR3UsbDriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach, + 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG); + else + vrc = pVMM->pfnPDMR3DriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach, + 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG); + + if (pszDriverDetach) + { + RTStrFree(pszDriverDetach); + /* Remove the complete node and create new for the new config. */ + pVMM->pfnCFGMR3RemoveNode(pLunL0); + pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN); + if (pLunL0) + { + try + { + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0); + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + } + } + if (vrc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN) + vrc = VINF_SUCCESS; + AssertRCReturn(vrc, vrc); + + /* + * Don't remove the LUN except for IDE/floppy/NVMe (which connects directly to the medium driver + * even for DVD devices) or if there is a hotplug event which rips out the complete device. + */ + if ( fHotplug + || enmBus == StorageBus_IDE + || enmBus == StorageBus_Floppy + || enmBus == StorageBus_PCIe + || (enmBus == StorageBus_SATA && enmDevType != DeviceType_DVD)) + { + fAddLun = true; + pVMM->pfnCFGMR3RemoveNode(pLunL0); + } + } + else + fAddLun = true; + + try + { + if (fAddLun) + InsertConfigNodeF(pCtlInst, &pLunL0, "LUN#%u", uLUN); + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + + if (ppLunL0) + *ppLunL0 = pLunL0; + + return vrc; +} + +int Console::i_configMediumAttachment(const char *pcszDevice, + unsigned uInstance, + StorageBus_T enmBus, + bool fUseHostIOCache, + bool fBuiltinIOCache, + bool fInsertDiskIntegrityDrv, + bool fSetupMerge, + unsigned uMergeSource, + unsigned uMergeTarget, + IMediumAttachment *pMediumAtt, + MachineState_T aMachineState, + HRESULT *phrc, + bool fAttachDetach, + bool fForceUnmount, + bool fHotplug, + PUVM pUVM, + PCVMMR3VTABLE pVMM, + DeviceType_T *paLedDevType, + PCFGMNODE *ppLunL0) +{ + // InsertConfig* throws + try + { + int vrc = VINF_SUCCESS; + HRESULT hrc; + Bstr bstr; + PCFGMNODE pCtlInst = NULL; + +// #define RC_CHECK() AssertMsgReturn(RT_SUCCESS(rc), ("rc=%Rrc\n", rc), rc) +#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR) + + LONG lDev; + hrc = pMediumAtt->COMGETTER(Device)(&lDev); H(); + LONG lPort; + hrc = pMediumAtt->COMGETTER(Port)(&lPort); H(); + DeviceType_T lType; + hrc = pMediumAtt->COMGETTER(Type)(&lType); H(); + BOOL fNonRotational; + hrc = pMediumAtt->COMGETTER(NonRotational)(&fNonRotational); H(); + BOOL fDiscard; + hrc = pMediumAtt->COMGETTER(Discard)(&fDiscard); H(); + + if (lType == DeviceType_DVD) + fInsertDiskIntegrityDrv = false; + + unsigned uLUN; + PCFGMNODE pLunL0 = NULL; + hrc = Console::i_storageBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H(); + + /* Determine the base path for the device instance. */ + if (enmBus != StorageBus_USB) + pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance); + else + { + /* If we hotplug a USB device create a new CFGM tree. */ + if (!fHotplug) + pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "USB/%s/", pcszDevice); + else + pCtlInst = pVMM->pfnCFGMR3CreateTree(pUVM); + } + AssertReturn(pCtlInst, VERR_INTERNAL_ERROR); + + if (enmBus == StorageBus_USB) + { + PCFGMNODE pCfg = NULL; + + /* Create correct instance. */ + if (!fHotplug) + { + if (!fAttachDetach) + InsertConfigNodeF(pCtlInst, &pCtlInst, "%d", lPort); + else + pCtlInst = pVMM->pfnCFGMR3GetChildF(pCtlInst, "%d/", lPort); + } + + if (!fAttachDetach) + InsertConfigNode(pCtlInst, "Config", &pCfg); + + uInstance = lPort; /* Overwrite uInstance with the correct one. */ + + if (!fHotplug && !fAttachDetach) + { + char aszUuid[RTUUID_STR_LENGTH + 1]; + USBStorageDevice UsbMsd = USBStorageDevice(); + + memset(aszUuid, 0, sizeof(aszUuid)); + vrc = RTUuidCreate(&UsbMsd.mUuid); + AssertRCReturn(vrc, vrc); + vrc = RTUuidToStr(&UsbMsd.mUuid, aszUuid, sizeof(aszUuid)); + AssertRCReturn(vrc, vrc); + + UsbMsd.iPort = uInstance; + + InsertConfigString(pCtlInst, "UUID", aszUuid); + mUSBStorageDevices.push_back(UsbMsd); + + /** @todo No LED after hotplugging. */ + /* Attach the status driver */ + i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 7, &paLedDevType, + &mapMediumAttachments, pcszDevice, 0); + } + } + + vrc = i_removeMediumDriverFromVm(pCtlInst, pcszDevice, uInstance, uLUN, enmBus, fAttachDetach, + fHotplug, fForceUnmount, pUVM, pVMM, lType, &pLunL0); + if (RT_FAILURE(vrc)) + return vrc; + if (ppLunL0) + *ppLunL0 = pLunL0; + + Utf8StrFmt devicePath("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN); + mapMediumAttachments[devicePath] = pMediumAtt; + + ComPtr<IMedium> ptrMedium; + hrc = pMediumAtt->COMGETTER(Medium)(ptrMedium.asOutParam()); H(); + + /* + * 1. Only check this for hard disk images. + * 2. Only check during VM creation and not later, especially not during + * taking an online snapshot! + */ + if ( lType == DeviceType_HardDisk + && ( aMachineState == MachineState_Starting + || aMachineState == MachineState_Restoring)) + { + vrc = i_checkMediumLocation(ptrMedium, &fUseHostIOCache); + if (RT_FAILURE(vrc)) + return vrc; + } + + BOOL fPassthrough = FALSE; + if (ptrMedium.isNotNull()) + { + BOOL fHostDrive; + hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H(); + if ( ( lType == DeviceType_DVD + || lType == DeviceType_Floppy) + && !fHostDrive) + { + /* + * Informative logging. + */ + Bstr bstrFile; + hrc = ptrMedium->COMGETTER(Location)(bstrFile.asOutParam()); H(); + Utf8Str strFile(bstrFile); + RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN; + (void)RTFsQueryType(strFile.c_str(), &enmFsTypeFile); + LogRel(("File system of '%s' (%s) is %s\n", + strFile.c_str(), lType == DeviceType_DVD ? "DVD" : "Floppy", RTFsTypeName(enmFsTypeFile))); + } + + if (fHostDrive) + { + hrc = pMediumAtt->COMGETTER(Passthrough)(&fPassthrough); H(); + } + } + + ComObjPtr<IBandwidthGroup> pBwGroup; + Bstr bstrBwGroup; + hrc = pMediumAtt->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H(); + + if (!pBwGroup.isNull()) + { + hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H(); + } + + /* + * Insert the SCSI driver for hotplug events on the SCSI/USB based storage controllers + * or for SATA if the new device is a CD/DVD drive. + */ + if ( (fHotplug || !fAttachDetach) + && ( (enmBus == StorageBus_SCSI || enmBus == StorageBus_SAS || enmBus == StorageBus_USB || enmBus == StorageBus_VirtioSCSI) + || (enmBus == StorageBus_SATA && lType == DeviceType_DVD && !fPassthrough))) + { + InsertConfigString(pLunL0, "Driver", "SCSI"); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0); + } + + vrc = i_configMedium(pLunL0, + !!fPassthrough, + lType, + fUseHostIOCache, + fBuiltinIOCache, + fInsertDiskIntegrityDrv, + fSetupMerge, + uMergeSource, + uMergeTarget, + bstrBwGroup.isEmpty() ? NULL : Utf8Str(bstrBwGroup).c_str(), + !!fDiscard, + !!fNonRotational, + ptrMedium, + aMachineState, + phrc); + if (RT_FAILURE(vrc)) + return vrc; + + if (fAttachDetach) + { + /* Attach the new driver. */ + if (enmBus == StorageBus_USB) + { + if (fHotplug) + { + USBStorageDevice UsbMsd = USBStorageDevice(); + RTUuidCreate(&UsbMsd.mUuid); + UsbMsd.iPort = uInstance; + vrc = pVMM->pfnPDMR3UsbCreateEmulatedDevice(pUVM, pcszDevice, pCtlInst, &UsbMsd.mUuid, NULL); + if (RT_SUCCESS(vrc)) + mUSBStorageDevices.push_back(UsbMsd); + } + else + vrc = pVMM->pfnPDMR3UsbDriverAttach(pUVM, pcszDevice, uInstance, uLUN, + fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); + } + else if ( !fHotplug + && ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI) + || (enmBus == StorageBus_SATA && lType == DeviceType_DVD))) + vrc = pVMM->pfnPDMR3DriverAttach(pUVM, pcszDevice, uInstance, uLUN, + fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); + else + vrc = pVMM->pfnPDMR3DeviceAttach(pUVM, pcszDevice, uInstance, uLUN, + fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); + AssertRCReturn(vrc, vrc); + + /* + * Make the secret key helper interface known to the VD driver if it is attached, + * so we can get notified about missing keys. + */ + PPDMIBASE pIBase = NULL; + vrc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "VD", &pIBase); + if (RT_SUCCESS(vrc) && pIBase) + { + PPDMIMEDIA pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID); + if (pIMedium) + { + vrc = pIMedium->pfnSetSecKeyIf(pIMedium, mpIfSecKey, mpIfSecKeyHlp); + Assert(RT_SUCCESS(vrc) || vrc == VERR_NOT_SUPPORTED); + } + } + + /* There is no need to handle removable medium mounting, as we + * unconditionally replace everthing including the block driver level. + * This means the new medium will be picked up automatically. */ + } + + if (paLedDevType) + paLedDevType[uLUN] = lType; + + /* Dump the changed LUN if possible, dump the complete device otherwise */ + if ( aMachineState != MachineState_Starting + && aMachineState != MachineState_Restoring) + pVMM->pfnCFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst); + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + +#undef H + + return VINF_SUCCESS; +} + +int Console::i_configMedium(PCFGMNODE pLunL0, + bool fPassthrough, + DeviceType_T enmType, + bool fUseHostIOCache, + bool fBuiltinIOCache, + bool fInsertDiskIntegrityDrv, + bool fSetupMerge, + unsigned uMergeSource, + unsigned uMergeTarget, + const char *pcszBwGroup, + bool fDiscard, + bool fNonRotational, + ComPtr<IMedium> ptrMedium, + MachineState_T aMachineState, + HRESULT *phrc) +{ + // InsertConfig* throws + try + { + HRESULT hrc; + Bstr bstr; + PCFGMNODE pCfg = NULL; + +#define H() \ + AssertMsgReturnStmt(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc), if (phrc) *phrc = hrc, Global::vboxStatusCodeFromCOM(hrc)) + + + BOOL fHostDrive = FALSE; + MediumType_T mediumType = MediumType_Normal; + if (ptrMedium.isNotNull()) + { + hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H(); + hrc = ptrMedium->COMGETTER(Type)(&mediumType); H(); + } + + if (fHostDrive) + { + Assert(ptrMedium.isNotNull()); + if (enmType == DeviceType_DVD) + { + InsertConfigString(pLunL0, "Driver", "HostDVD"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H(); + InsertConfigString(pCfg, "Path", bstr); + + InsertConfigInteger(pCfg, "Passthrough", fPassthrough); + } + else if (enmType == DeviceType_Floppy) + { + InsertConfigString(pLunL0, "Driver", "HostFloppy"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H(); + InsertConfigString(pCfg, "Path", bstr); + } + } + else + { + if (fInsertDiskIntegrityDrv) + { + /* + * The actual configuration is done through CFGM extra data + * for each inserted driver separately. + */ + InsertConfigString(pLunL0, "Driver", "DiskIntegrity"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0); + } + + InsertConfigString(pLunL0, "Driver", "VD"); + InsertConfigNode(pLunL0, "Config", &pCfg); + switch (enmType) + { + case DeviceType_DVD: + InsertConfigString(pCfg, "Type", "DVD"); + InsertConfigInteger(pCfg, "Mountable", 1); + break; + case DeviceType_Floppy: + InsertConfigString(pCfg, "Type", "Floppy 1.44"); + InsertConfigInteger(pCfg, "Mountable", 1); + break; + case DeviceType_HardDisk: + default: + InsertConfigString(pCfg, "Type", "HardDisk"); + InsertConfigInteger(pCfg, "Mountable", 0); + } + + if ( ptrMedium.isNotNull() + && ( enmType == DeviceType_DVD + || enmType == DeviceType_Floppy) + ) + { + // if this medium represents an ISO image and this image is inaccessible, + // the ignore it instead of causing a failure; this can happen when we + // restore a VM state and the ISO has disappeared, e.g. because the Guest + // Additions were mounted and the user upgraded VirtualBox. Previously + // we failed on startup, but that's not good because the only way out then + // would be to discard the VM state... + MediumState_T mediumState; + hrc = ptrMedium->RefreshState(&mediumState); H(); + if (mediumState == MediumState_Inaccessible) + { + Bstr loc; + hrc = ptrMedium->COMGETTER(Location)(loc.asOutParam()); H(); + i_atVMRuntimeErrorCallbackF(0, "DvdOrFloppyImageInaccessible", + N_("The image file '%ls' is inaccessible and is being ignored. " + "Please select a different image file for the virtual %s drive."), + loc.raw(), + enmType == DeviceType_DVD ? "DVD" : "floppy"); + ptrMedium.setNull(); + } + } + + if (ptrMedium.isNotNull()) + { + /* Start with length of parent chain, as the list is reversed */ + unsigned uImage = 0; + ComPtr<IMedium> ptrTmp = ptrMedium; + while (ptrTmp.isNotNull()) + { + uImage++; + ComPtr<IMedium> ptrParent; + hrc = ptrTmp->COMGETTER(Parent)(ptrParent.asOutParam()); H(); + ptrTmp = ptrParent; + } + /* Index of last image */ + uImage--; + +# ifdef VBOX_WITH_EXTPACK + if (mptrExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME)) + { + /* Configure loading the VDPlugin. */ + static const char s_szVDPlugin[] = "VDPluginCrypt"; + PCFGMNODE pCfgPlugins = NULL; + PCFGMNODE pCfgPlugin = NULL; + Utf8Str strPlugin; + hrc = mptrExtPackManager->i_getLibraryPathForExtPack(s_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin); + // Don't fail, this is optional! + if (SUCCEEDED(hrc)) + { + InsertConfigNode(pCfg, "Plugins", &pCfgPlugins); + InsertConfigNode(pCfgPlugins, s_szVDPlugin, &pCfgPlugin); + InsertConfigString(pCfgPlugin, "Path", strPlugin.c_str()); + } + } +# endif + + hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H(); + InsertConfigString(pCfg, "Path", bstr); + + hrc = ptrMedium->COMGETTER(Format)(bstr.asOutParam()); H(); + InsertConfigString(pCfg, "Format", bstr); + + if (mediumType == MediumType_Readonly) + InsertConfigInteger(pCfg, "ReadOnly", 1); + else if (enmType == DeviceType_Floppy) + InsertConfigInteger(pCfg, "MaybeReadOnly", 1); + + /* Start without exclusive write access to the images. */ + /** @todo Live Migration: I don't quite like this, we risk screwing up when + * we're resuming the VM if some 3rd dude have any of the VDIs open + * with write sharing denied. However, if the two VMs are sharing a + * image it really is necessary.... + * + * So, on the "lock-media" command, the target teleporter should also + * make DrvVD undo TempReadOnly. It gets interesting if we fail after + * that. Grumble. */ + if ( enmType == DeviceType_HardDisk + && aMachineState == MachineState_TeleportingIn) + InsertConfigInteger(pCfg, "TempReadOnly", 1); + + /* Flag for opening the medium for sharing between VMs. This + * is done at the moment only for the first (and only) medium + * in the chain, as shared media can have no diffs. */ + if (mediumType == MediumType_Shareable) + InsertConfigInteger(pCfg, "Shareable", 1); + + if (!fUseHostIOCache) + { + InsertConfigInteger(pCfg, "UseNewIo", 1); + /* + * Activate the builtin I/O cache for harddisks only. + * It caches writes only which doesn't make sense for DVD drives + * and just increases the overhead. + */ + if ( fBuiltinIOCache + && (enmType == DeviceType_HardDisk)) + InsertConfigInteger(pCfg, "BlockCache", 1); + } + + if (fSetupMerge) + { + InsertConfigInteger(pCfg, "SetupMerge", 1); + if (uImage == uMergeSource) + InsertConfigInteger(pCfg, "MergeSource", 1); + else if (uImage == uMergeTarget) + InsertConfigInteger(pCfg, "MergeTarget", 1); + } + + if (pcszBwGroup) + InsertConfigString(pCfg, "BwGroup", pcszBwGroup); + + if (fDiscard) + InsertConfigInteger(pCfg, "Discard", 1); + + if (fNonRotational) + InsertConfigInteger(pCfg, "NonRotationalMedium", 1); + + /* Pass all custom parameters. */ + bool fHostIP = true; + bool fEncrypted = false; + hrc = i_configMediumProperties(pCfg, ptrMedium, &fHostIP, &fEncrypted); H(); + + /* Create an inverted list of parents. */ + uImage--; + ComPtr<IMedium> ptrParentMedium = ptrMedium; + for (PCFGMNODE pParent = pCfg;; uImage--) + { + ComPtr<IMedium> ptrCurMedium; + hrc = ptrParentMedium->COMGETTER(Parent)(ptrCurMedium.asOutParam()); H(); + if (ptrCurMedium.isNull()) + break; + + PCFGMNODE pCur; + InsertConfigNode(pParent, "Parent", &pCur); + hrc = ptrCurMedium->COMGETTER(Location)(bstr.asOutParam()); H(); + InsertConfigString(pCur, "Path", bstr); + + hrc = ptrCurMedium->COMGETTER(Format)(bstr.asOutParam()); H(); + InsertConfigString(pCur, "Format", bstr); + + if (fSetupMerge) + { + if (uImage == uMergeSource) + InsertConfigInteger(pCur, "MergeSource", 1); + else if (uImage == uMergeTarget) + InsertConfigInteger(pCur, "MergeTarget", 1); + } + + /* Configure medium properties. */ + hrc = i_configMediumProperties(pCur, ptrCurMedium, &fHostIP, &fEncrypted); H(); + + /* next */ + pParent = pCur; + ptrParentMedium = ptrCurMedium; + } + + /* Custom code: put marker to not use host IP stack to driver + * configuration node. Simplifies life of DrvVD a bit. */ + if (!fHostIP) + InsertConfigInteger(pCfg, "HostIPStack", 0); + + if (fEncrypted) + m_cDisksEncrypted++; + } + else + { + /* Set empty drive flag for DVD or floppy without media. */ + if ( enmType == DeviceType_DVD + || enmType == DeviceType_Floppy) + InsertConfigInteger(pCfg, "EmptyDrive", 1); + } + } +#undef H + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + + return VINF_SUCCESS; +} + +/** + * Adds the medium properties to the CFGM tree. + * + * @returns VBox status code. + * @param pCur The current CFGM node. + * @param pMedium The medium object to configure. + * @param pfHostIP Where to return the value of the \"HostIPStack\" property if found. + * @param pfEncrypted Where to return whether the medium is encrypted. + */ +int Console::i_configMediumProperties(PCFGMNODE pCur, IMedium *pMedium, bool *pfHostIP, bool *pfEncrypted) +{ + /* Pass all custom parameters. */ + SafeArray<BSTR> aNames; + SafeArray<BSTR> aValues; + HRESULT hrc = pMedium->GetProperties(NULL, ComSafeArrayAsOutParam(aNames), + ComSafeArrayAsOutParam(aValues)); + + if ( SUCCEEDED(hrc) + && aNames.size() != 0) + { + PCFGMNODE pVDC; + InsertConfigNode(pCur, "VDConfig", &pVDC); + for (size_t ii = 0; ii < aNames.size(); ++ii) + { + if (aValues[ii] && *aValues[ii]) + { + Utf8Str name = aNames[ii]; + Utf8Str value = aValues[ii]; + size_t offSlash = name.find("/", 0); + if ( offSlash != name.npos + && !name.startsWith("Special/")) + { + com::Utf8Str strFilter; + com::Utf8Str strKey; + + hrc = strFilter.assignEx(name, 0, offSlash); + if (FAILED(hrc)) + break; + + hrc = strKey.assignEx(name, offSlash + 1, name.length() - offSlash - 1); /* Skip slash */ + if (FAILED(hrc)) + break; + + PCFGMNODE pCfgFilterConfig = mpVMM->pfnCFGMR3GetChild(pVDC, strFilter.c_str()); + if (!pCfgFilterConfig) + InsertConfigNode(pVDC, strFilter.c_str(), &pCfgFilterConfig); + + InsertConfigString(pCfgFilterConfig, strKey.c_str(), value); + } + else + { + InsertConfigString(pVDC, name.c_str(), value); + if ( name.compare("HostIPStack") == 0 + && value.compare("0") == 0) + *pfHostIP = false; + } + + if ( name.compare("CRYPT/KeyId") == 0 + && pfEncrypted) + *pfEncrypted = true; + } + } + } + + return hrc; +} + + +/** + * Configure proxy parameters the Network configuration tree. + * Parameters may differ depending on the IP address being accessed. + * + * @returns VBox status code. + * + * @param virtualBox The VirtualBox object. + * @param pCfg Configuration node for the driver. + * @param pcszPrefix The prefix for CFGM parameters: "Primary" or "Secondary". + * @param strIpAddr The public IP address to be accessed via a proxy. + * + * @thread EMT + */ +int Console::i_configProxy(ComPtr<IVirtualBox> virtualBox, PCFGMNODE pCfg, const char *pcszPrefix, const com::Utf8Str &strIpAddr) +{ + RTHTTPPROXYINFO ProxyInfo; + ComPtr<ISystemProperties> systemProperties; + ProxyMode_T enmProxyMode; + HRESULT hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc)); + return false; + } + hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode); + if (FAILED(hrc)) + { + LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc)); + return VERR_INTERNAL_ERROR; + } + + RTHTTP hHttp; + int vrc = RTHttpCreate(&hHttp); + if (RT_FAILURE(vrc)) + { + LogRel(("CLOUD-NET: Failed to create HTTP context (rc=%Rrc)\n", vrc)); + return vrc; + } + + char *pszProxyType = NULL; + + if (enmProxyMode == ProxyMode_Manual) + { + /* + * Unfortunately we cannot simply call RTHttpSetProxyByUrl because it never + * exposes proxy settings. Calling RTHttpQueryProxyInfoForUrl afterward + * won't help either as it uses system-wide proxy settings instead of + * parameters we would have set with RTHttpSetProxyByUrl. Hence we parse + * proxy URL ourselves here. + */ + Bstr proxyUrl; + hrc = systemProperties->COMGETTER(ProxyURL)(proxyUrl.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("CLOUD-NET: Failed to obtain proxy URL. hrc=%x\n", hrc)); + return false; + } + Utf8Str strProxyUrl = proxyUrl; + if (!strProxyUrl.contains("://")) + strProxyUrl = "http://" + strProxyUrl; + const char *pcszProxyUrl = strProxyUrl.c_str(); + RTURIPARSED Parsed; + vrc = RTUriParse(pcszProxyUrl, &Parsed); + if (RT_FAILURE(vrc)) + { + LogRel(("CLOUD-NET: Failed to parse proxy URL: %ls (vrc=%Rrc)\n", proxyUrl.raw(), vrc)); + return false; + } + + pszProxyType = RTUriParsedScheme(pcszProxyUrl, &Parsed); + if (!pszProxyType) + { + LogRel(("CLOUD-NET: Failed to get proxy scheme from proxy URL: %s\n", pcszProxyUrl)); + return false; + } + RTStrToUpper(pszProxyType); + + ProxyInfo.pszProxyHost = RTUriParsedAuthorityHost(pcszProxyUrl, &Parsed); + if (!ProxyInfo.pszProxyHost) + { + LogRel(("CLOUD-NET: Failed to get proxy host name from proxy URL: %s\n", pcszProxyUrl)); + return false; + } + ProxyInfo.uProxyPort = RTUriParsedAuthorityPort(pcszProxyUrl, &Parsed); + if (ProxyInfo.uProxyPort == UINT32_MAX) + { + LogRel(("CLOUD-NET: Failed to get proxy port from proxy URL: %s\n", pcszProxyUrl)); + return false; + } + ProxyInfo.pszProxyUsername = RTUriParsedAuthorityUsername(pcszProxyUrl, &Parsed); + ProxyInfo.pszProxyPassword = RTUriParsedAuthorityPassword(pcszProxyUrl, &Parsed); + } + else if (enmProxyMode == ProxyMode_System) + { + vrc = RTHttpUseSystemProxySettings(hHttp); + if (RT_FAILURE(vrc)) + { + LogRel(("%s: RTHttpUseSystemProxySettings() failed: %Rrc", __FUNCTION__, vrc)); + RTHttpDestroy(hHttp); + return vrc; + } + vrc = RTHttpQueryProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &ProxyInfo); + RTHttpDestroy(hHttp); + if (RT_FAILURE(vrc)) + { + LogRel(("CLOUD-NET: Failed to get proxy for %s (rc=%Rrc)\n", strIpAddr.c_str(), vrc)); + return vrc; + } + + switch (ProxyInfo.enmProxyType) + { + case RTHTTPPROXYTYPE_NOPROXY: + /* Nothing to do */ + return VINF_SUCCESS; + case RTHTTPPROXYTYPE_HTTP: + pszProxyType = RTStrDup("HTTP"); + break; + case RTHTTPPROXYTYPE_HTTPS: + case RTHTTPPROXYTYPE_SOCKS4: + case RTHTTPPROXYTYPE_SOCKS5: + /* break; -- Fall through until support is implemented */ + case RTHTTPPROXYTYPE_UNKNOWN: + case RTHTTPPROXYTYPE_INVALID: + case RTHTTPPROXYTYPE_END: + case RTHTTPPROXYTYPE_32BIT_HACK: + LogRel(("CLOUD-NET: Unsupported proxy type %u\n", ProxyInfo.enmProxyType)); + RTHttpFreeProxyInfo(&ProxyInfo); + return VERR_INVALID_PARAMETER; + } + } + else + { + Assert(enmProxyMode == ProxyMode_NoProxy); + return VINF_SUCCESS; + } + + /* Resolve proxy host name to IP address if necessary */ + RTNETADDR addr; + RTSocketParseInetAddress(ProxyInfo.pszProxyHost, ProxyInfo.uProxyPort, &addr); + if (addr.enmType != RTNETADDRTYPE_IPV4) + { + LogRel(("CLOUD-NET: Unsupported address type %u\n", addr.enmType)); + RTHttpFreeProxyInfo(&ProxyInfo); + return VERR_INVALID_PARAMETER; + } + + InsertConfigString(pCfg, Utf8StrFmt("%sProxyType", pcszPrefix).c_str(), pszProxyType); + InsertConfigInteger(pCfg, Utf8StrFmt("%sProxyPort", pcszPrefix).c_str(), ProxyInfo.uProxyPort); + if (ProxyInfo.pszProxyHost) + InsertConfigString(pCfg, Utf8StrFmt("%sProxyHost", pcszPrefix).c_str(), Utf8StrFmt("%RTnaipv4", addr.uAddr.IPv4)); + if (ProxyInfo.pszProxyUsername) + InsertConfigString(pCfg, Utf8StrFmt("%sProxyUser", pcszPrefix).c_str(), ProxyInfo.pszProxyUsername); + if (ProxyInfo.pszProxyPassword) + InsertConfigPassword(pCfg, Utf8StrFmt("%sProxyPassword", pcszPrefix).c_str(), ProxyInfo.pszProxyPassword); + + RTHttpFreeProxyInfo(&ProxyInfo); + RTStrFree(pszProxyType); + return vrc; +} + + +/** + * Construct the Network configuration tree + * + * @returns VBox status code. + * + * @param pszDevice The PDM device name. + * @param uInstance The PDM device instance. + * @param uLun The PDM LUN number of the drive. + * @param aNetworkAdapter The network adapter whose attachment needs to be changed + * @param pCfg Configuration node for the device + * @param pLunL0 To store the pointer to the LUN#0. + * @param pInst The instance CFGM node + * @param fAttachDetach To determine if the network attachment should + * be attached/detached after/before + * configuration. + * @param fIgnoreConnectFailure + * True if connection failures should be ignored + * (makes only sense for bridged/host-only networks). + * @param pUVM The usermode VM handle. + * @param pVMM The VMM vtable. + * + * @note Locks this object for writing. + * @thread EMT + */ +int Console::i_configNetwork(const char *pszDevice, + unsigned uInstance, + unsigned uLun, + INetworkAdapter *aNetworkAdapter, + PCFGMNODE pCfg, + PCFGMNODE pLunL0, + PCFGMNODE pInst, + bool fAttachDetach, + bool fIgnoreConnectFailure, + PUVM pUVM, + PCVMMR3VTABLE pVMM) +{ + RT_NOREF(fIgnoreConnectFailure); + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED); + + // InsertConfig* throws + try + { + int vrc = VINF_SUCCESS; + HRESULT hrc; + Bstr bstr; + +#ifdef VBOX_WITH_CLOUD_NET + /* We'll need device's pCfg for cloud attachments */ + PCFGMNODE pDevCfg = pCfg; +#endif /* VBOX_WITH_CLOUD_NET */ + +#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR) + + /* + * Locking the object before doing VMR3* calls is quite safe here, since + * we're on EMT. Write lock is necessary because we indirectly modify the + * meAttachmentType member. + */ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + ComPtr<IMachine> pMachine = i_machine(); + + ComPtr<IVirtualBox> virtualBox; + hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H(); + + ComPtr<IHost> host; + hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H(); + + BOOL fSniffer; + hrc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fSniffer); H(); + + NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy; + hrc = aNetworkAdapter->COMGETTER(PromiscModePolicy)(&enmPromiscModePolicy); H(); + const char *pszPromiscuousGuestPolicy; + switch (enmPromiscModePolicy) + { + case NetworkAdapterPromiscModePolicy_Deny: pszPromiscuousGuestPolicy = "deny"; break; + case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = "allow-network"; break; + case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = "allow-all"; break; + default: AssertFailedReturn(VERR_INTERNAL_ERROR_4); + } + + if (fAttachDetach) + { + vrc = pVMM->pfnPDMR3DeviceDetach(pUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/); + if (vrc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN) + vrc = VINF_SUCCESS; + AssertLogRelRCReturn(vrc, vrc); + + /* Nuke anything which might have been left behind. */ + pVMM->pfnCFGMR3RemoveNode(pVMM->pfnCFGMR3GetChildF(pInst, "LUN#%u", uLun)); + } + + Bstr networkName, trunkName, trunkType; + NetworkAttachmentType_T eAttachmentType; + hrc = aNetworkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H(); + +#ifdef VBOX_WITH_NETSHAPER + ComObjPtr<IBandwidthGroup> pBwGroup; + Bstr bstrBwGroup; + hrc = aNetworkAdapter->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H(); + + if (!pBwGroup.isNull()) + { + hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H(); + } +#endif /* VBOX_WITH_NETSHAPER */ + + AssertMsg(uLun == 0, ("Network attachments with LUN > 0 are not supported yet\n")); + InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", uLun); + + /* + * Do not insert neither a shaper nor a sniffer if we are not attached to anything. + * This way we can easily detect if we are attached to anything at the device level. + */ +#ifdef VBOX_WITH_NETSHAPER + if (bstrBwGroup.isNotEmpty() && eAttachmentType != NetworkAttachmentType_Null) + { + InsertConfigString(pLunL0, "Driver", "NetShaper"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigString(pCfg, "BwGroup", bstrBwGroup); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0); + } +#endif /* VBOX_WITH_NETSHAPER */ + + if (fSniffer && eAttachmentType != NetworkAttachmentType_Null) + { + InsertConfigString(pLunL0, "Driver", "NetSniffer"); + InsertConfigNode(pLunL0, "Config", &pCfg); + hrc = aNetworkAdapter->COMGETTER(TraceFile)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) /* check convention for indicating default file. */ + InsertConfigString(pCfg, "File", bstr); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0); + } + + switch (eAttachmentType) + { + case NetworkAttachmentType_Null: + break; + + case NetworkAttachmentType_NAT: + { + ComPtr<INATEngine> natEngine; + hrc = aNetworkAdapter->COMGETTER(NATEngine)(natEngine.asOutParam()); H(); + InsertConfigString(pLunL0, "Driver", "NAT"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + /* Configure TFTP prefix and boot filename. */ + hrc = virtualBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + InsertConfigString(pCfg, "TFTPPrefix", Utf8StrFmt("%ls%c%s", bstr.raw(), RTPATH_DELIMITER, "TFTP")); + hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H(); + InsertConfigString(pCfg, "BootFile", Utf8StrFmt("%ls.pxe", bstr.raw())); + + hrc = natEngine->COMGETTER(Network)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + InsertConfigString(pCfg, "Network", bstr); + else + { + ULONG uSlot; + hrc = aNetworkAdapter->COMGETTER(Slot)(&uSlot); H(); + InsertConfigString(pCfg, "Network", Utf8StrFmt("10.0.%d.0/24", uSlot+2)); + } + hrc = natEngine->COMGETTER(HostIP)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + InsertConfigString(pCfg, "BindIP", bstr); + ULONG mtu = 0; + ULONG sockSnd = 0; + ULONG sockRcv = 0; + ULONG tcpSnd = 0; + ULONG tcpRcv = 0; + hrc = natEngine->GetNetworkSettings(&mtu, &sockSnd, &sockRcv, &tcpSnd, &tcpRcv); H(); + if (mtu) + InsertConfigInteger(pCfg, "SlirpMTU", mtu); + if (sockRcv) + InsertConfigInteger(pCfg, "SockRcv", sockRcv); + if (sockSnd) + InsertConfigInteger(pCfg, "SockSnd", sockSnd); + if (tcpRcv) + InsertConfigInteger(pCfg, "TcpRcv", tcpRcv); + if (tcpSnd) + InsertConfigInteger(pCfg, "TcpSnd", tcpSnd); + hrc = natEngine->COMGETTER(TFTPPrefix)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + { + RemoveConfigValue(pCfg, "TFTPPrefix"); + InsertConfigString(pCfg, "TFTPPrefix", bstr); + } + hrc = natEngine->COMGETTER(TFTPBootFile)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + { + RemoveConfigValue(pCfg, "BootFile"); + InsertConfigString(pCfg, "BootFile", bstr); + } + hrc = natEngine->COMGETTER(TFTPNextServer)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + InsertConfigString(pCfg, "NextServer", bstr); + BOOL fDNSFlag; + hrc = natEngine->COMGETTER(DNSPassDomain)(&fDNSFlag); H(); + InsertConfigInteger(pCfg, "PassDomain", fDNSFlag); + hrc = natEngine->COMGETTER(DNSProxy)(&fDNSFlag); H(); + InsertConfigInteger(pCfg, "DNSProxy", fDNSFlag); + hrc = natEngine->COMGETTER(DNSUseHostResolver)(&fDNSFlag); H(); + InsertConfigInteger(pCfg, "UseHostResolver", fDNSFlag); + + ULONG aliasMode; + hrc = natEngine->COMGETTER(AliasMode)(&aliasMode); H(); + InsertConfigInteger(pCfg, "AliasMode", aliasMode); + + BOOL fLocalhostReachable; + hrc = natEngine->COMGETTER(LocalhostReachable)(&fLocalhostReachable); H(); + InsertConfigInteger(pCfg, "LocalhostReachable", fLocalhostReachable); + + /* port-forwarding */ + SafeArray<BSTR> pfs; + hrc = natEngine->COMGETTER(Redirects)(ComSafeArrayAsOutParam(pfs)); H(); + + PCFGMNODE pPFTree = NULL; + if (pfs.size() > 0) + InsertConfigNode(pCfg, "PortForwarding", &pPFTree); + + for (unsigned int i = 0; i < pfs.size(); ++i) + { + PCFGMNODE pPF = NULL; /* /Devices/Dev/.../Config/PortForwarding/$n/ */ + + uint16_t port = 0; + Utf8Str utf = pfs[i]; + Utf8Str strName; + Utf8Str strProto; + Utf8Str strHostPort; + Utf8Str strHostIP; + Utf8Str strGuestPort; + Utf8Str strGuestIP; + size_t pos, ppos; + pos = ppos = 0; +#define ITERATE_TO_NEXT_TERM(res, str, pos, ppos) \ + { \ + pos = str.find(",", ppos); \ + if (pos == Utf8Str::npos) \ + { \ + Log(( #res " extracting from %s is failed\n", str.c_str())); \ + continue; \ + } \ + res = str.substr(ppos, pos - ppos); \ + Log2((#res " %s pos:%d, ppos:%d\n", res.c_str(), pos, ppos)); \ + ppos = pos + 1; \ + } /* no do { ... } while because of 'continue' */ + ITERATE_TO_NEXT_TERM(strName, utf, pos, ppos); + ITERATE_TO_NEXT_TERM(strProto, utf, pos, ppos); + ITERATE_TO_NEXT_TERM(strHostIP, utf, pos, ppos); + ITERATE_TO_NEXT_TERM(strHostPort, utf, pos, ppos); + ITERATE_TO_NEXT_TERM(strGuestIP, utf, pos, ppos); + strGuestPort = utf.substr(ppos, utf.length() - ppos); +#undef ITERATE_TO_NEXT_TERM + + uint32_t proto = strProto.toUInt32(); + bool fValid = true; + switch (proto) + { + case NATProtocol_UDP: + strProto = "UDP"; + break; + case NATProtocol_TCP: + strProto = "TCP"; + break; + default: + fValid = false; + } + /* continue with next rule if no valid proto was passed */ + if (!fValid) + continue; + + InsertConfigNode(pPFTree, Utf8StrFmt("%u", i).c_str(), &pPF); + + if (!strName.isEmpty()) + InsertConfigString(pPF, "Name", strName); + + InsertConfigString(pPF, "Protocol", strProto); + + if (!strHostIP.isEmpty()) + InsertConfigString(pPF, "BindIP", strHostIP); + + if (!strGuestIP.isEmpty()) + InsertConfigString(pPF, "GuestIP", strGuestIP); + + port = RTStrToUInt16(strHostPort.c_str()); + if (port) + InsertConfigInteger(pPF, "HostPort", port); + + port = RTStrToUInt16(strGuestPort.c_str()); + if (port) + InsertConfigInteger(pPF, "GuestPort", port); + } + break; + } + + case NetworkAttachmentType_Bridged: + { +#if (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT) + hrc = i_attachToTapInterface(aNetworkAdapter); + if (FAILED(hrc)) + { + switch (hrc) + { + case E_ACCESSDENIED: + return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_( + "Failed to open '/dev/net/tun' for read/write access. Please check the " + "permissions of that node. Either run 'chmod 0666 /dev/net/tun' or " + "change the group of that node and make yourself a member of that group. " + "Make sure that these changes are permanent, especially if you are " + "using udev")); + default: + AssertMsgFailed(("Could not attach to host interface! Bad!\n")); + return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, + N_("Failed to initialize Host Interface Networking")); + } + } + + Assert((intptr_t)maTapFD[uInstance] >= 0); + if ((intptr_t)maTapFD[uInstance] >= 0) + { + InsertConfigString(pLunL0, "Driver", "HostInterface"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]); + } + +#elif defined(VBOX_WITH_NETFLT) + /* + * This is the new VBoxNetFlt+IntNet stuff. + */ + Bstr BridgedIfName; + hrc = aNetworkAdapter->COMGETTER(BridgedInterface)(BridgedIfName.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_Bridged: COMGETTER(BridgedInterface) failed, hrc (0x%x)\n", hrc)); + H(); + } + + Utf8Str BridgedIfNameUtf8(BridgedIfName); + const char *pszBridgedIfName = BridgedIfNameUtf8.c_str(); + + ComPtr<IHostNetworkInterface> hostInterface; + hrc = host->FindHostNetworkInterfaceByName(BridgedIfName.raw(), + hostInterface.asOutParam()); + if (!SUCCEEDED(hrc)) + { + AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: FindByName failed, rc=%Rhrc (0x%x)\n", hrc, hrc)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS, + N_("Nonexistent host networking interface, name '%ls'"), + BridgedIfName.raw()); + } + +# if defined(RT_OS_DARWIN) + /* The name is in the format 'ifX: long name', chop it off at the colon. */ + char szTrunk[INTNET_MAX_TRUNK_NAME]; + RTStrCopy(szTrunk, sizeof(szTrunk), pszBridgedIfName); + char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk)); +// Quick fix for @bugref{5633} +// if (!pszColon) +// { +// /* +// * Dynamic changing of attachment causes an attempt to configure +// * network with invalid host adapter (as it is must be changed before +// * the attachment), calling Detach here will cause a deadlock. +// * See @bugref{4750}. +// * hrc = aNetworkAdapter->Detach(); H(); +// */ +// return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS, +// N_("Malformed host interface networking name '%ls'"), +// BridgedIfName.raw()); +// } + if (pszColon) + *pszColon = '\0'; + const char *pszTrunk = szTrunk; + +# elif defined(RT_OS_SOLARIS) + /* The name is in the format 'ifX[:1] - long name, chop it off at space. */ + char szTrunk[256]; + strlcpy(szTrunk, pszBridgedIfName, sizeof(szTrunk)); + char *pszSpace = (char *)memchr(szTrunk, ' ', sizeof(szTrunk)); + + /* + * Currently don't bother about malformed names here for the sake of people using + * VBoxManage and setting only the NIC name from there. If there is a space we + * chop it off and proceed, otherwise just use whatever we've got. + */ + if (pszSpace) + *pszSpace = '\0'; + + /* Chop it off at the colon (zone naming eg: e1000g:1 we need only the e1000g) */ + char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk)); + if (pszColon) + *pszColon = '\0'; + + const char *pszTrunk = szTrunk; + +# elif defined(RT_OS_WINDOWS) + HostNetworkInterfaceType_T eIfType; + hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_Bridged: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc)); + H(); + } + + if (eIfType != HostNetworkInterfaceType_Bridged) + return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS, + N_("Interface ('%ls') is not a Bridged Adapter interface"), + BridgedIfName.raw()); + + hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Id) failed, hrc (0x%x)\n", hrc)); + H(); + } + Guid hostIFGuid(bstr); + + INetCfg *pNc; + ComPtr<INetCfgComponent> pAdaptorComponent; + LPWSTR pszApp; + + hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp); + Assert(hrc == S_OK); + if (hrc != S_OK) + { + LogRel(("NetworkAttachmentType_Bridged: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc)); + H(); + } + + /* get the adapter's INetCfgComponent*/ + hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(), + pAdaptorComponent.asOutParam()); + if (hrc != S_OK) + { + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + LogRel(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)\n", hrc)); + H(); + } +# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\" + char szTrunkName[INTNET_MAX_TRUNK_NAME]; + char *pszTrunkName = szTrunkName; + wchar_t * pswzBindName; + hrc = pAdaptorComponent->GetBindName(&pswzBindName); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + int cwBindName = (int)wcslen(pswzBindName) + 1; + int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX); + if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName) + { + strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX); + pszTrunkName += cbFullBindNamePrefix-1; + if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName, + sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL)) + { + DWORD err = GetLastError(); + hrc = HRESULT_FROM_WIN32(err); + AssertMsgFailed(("hrc=%Rhrc %#x\n", hrc, hrc)); + AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n", + hrc, hrc, err)); + } + } + else + { + AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: insufficient szTrunkName buffer space\n")); + /** @todo set appropriate error code */ + hrc = E_FAIL; + } + + if (hrc != S_OK) + { + AssertFailed(); + CoTaskMemFree(pswzBindName); + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + H(); + } + + /* we're not freeing the bind name since we'll use it later for detecting wireless*/ + } + else + { + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)", + hrc)); + H(); + } + + const char *pszTrunk = szTrunkName; + /* we're not releasing the INetCfg stuff here since we use it later to figure out whether it is wireless */ + +# elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) +# if defined(RT_OS_FREEBSD) + /* + * If we bridge to a tap interface open it the `old' direct way. + * This works and performs better than bridging a physical + * interface via the current FreeBSD vboxnetflt implementation. + */ + if (!strncmp(pszBridgedIfName, RT_STR_TUPLE("tap"))) { + hrc = i_attachToTapInterface(aNetworkAdapter); + if (FAILED(hrc)) + { + switch (hrc) + { + case E_ACCESSDENIED: + return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_( + "Failed to open '/dev/%s' for read/write access. Please check the " + "permissions of that node, and that the net.link.tap.user_open " + "sysctl is set. Either run 'chmod 0666 /dev/%s' or change the " + "group of that node to vboxusers and make yourself a member of " + "that group. Make sure that these changes are permanent."), + pszBridgedIfName, pszBridgedIfName); + default: + AssertMsgFailed(("Could not attach to tap interface! Bad!\n")); + return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, + N_("Failed to initialize Host Interface Networking")); + } + } + + Assert((intptr_t)maTapFD[uInstance] >= 0); + if ((intptr_t)maTapFD[uInstance] >= 0) + { + InsertConfigString(pLunL0, "Driver", "HostInterface"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]); + } + break; + } +# endif + /** @todo Check for malformed names. */ + const char *pszTrunk = pszBridgedIfName; + + /* Issue a warning if the interface is down */ + { + int iSock = socket(AF_INET, SOCK_DGRAM, 0); + if (iSock >= 0) + { + struct ifreq Req; + RT_ZERO(Req); + RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszBridgedIfName); + if (ioctl(iSock, SIOCGIFFLAGS, &Req) >= 0) + if ((Req.ifr_flags & IFF_UP) == 0) + i_atVMRuntimeErrorCallbackF(0, "BridgedInterfaceDown", + N_("Bridged interface %s is down. Guest will not be able to use this interface"), + pszBridgedIfName); + + close(iSock); + } + } + +# else +# error "PORTME (VBOX_WITH_NETFLT)" +# endif + +# if defined(RT_OS_DARWIN) && defined(VBOX_WITH_VMNET) + InsertConfigString(pLunL0, "Driver", "VMNet"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigString(pCfg, "Trunk", pszTrunk); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt); +# else + InsertConfigString(pLunL0, "Driver", "IntNet"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigString(pCfg, "Trunk", pszTrunk); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt); + InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure); + InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy); + char szNetwork[INTNET_MAX_NETWORK_NAME]; + +# if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN) + /* + * 'pszTrunk' contains just the interface name required in ring-0, while 'pszBridgedIfName' contains + * interface name + optional description. We must not pass any description to the VM as it can differ + * for the same interface name, eg: "nge0 - ethernet" (GUI) vs "nge0" (VBoxManage). + */ + RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszTrunk); +# else + RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszBridgedIfName); +# endif + InsertConfigString(pCfg, "Network", szNetwork); + networkName = Bstr(szNetwork); + trunkName = Bstr(pszTrunk); + trunkType = Bstr(TRUNKTYPE_NETFLT); + + BOOL fSharedMacOnWire = false; + hrc = hostInterface->COMGETTER(Wireless)(&fSharedMacOnWire); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Wireless) failed, hrc (0x%x)\n", hrc)); + H(); + } + else if (fSharedMacOnWire) + { + InsertConfigInteger(pCfg, "SharedMacOnWire", true); + Log(("Set SharedMacOnWire\n")); + } + +# if defined(RT_OS_SOLARIS) +# if 0 /* bird: this is a bit questionable and might cause more trouble than its worth. */ + /* Zone access restriction, don't allow snooping the global zone. */ + zoneid_t ZoneId = getzoneid(); + if (ZoneId != GLOBAL_ZONEID) + { + InsertConfigInteger(pCfg, "IgnoreAllPromisc", true); + } +# endif +# endif +# endif + +#elif defined(RT_OS_WINDOWS) /* not defined NetFlt */ + /* NOTHING TO DO HERE */ +#elif defined(RT_OS_LINUX) +/// @todo aleksey: is there anything to be done here? +#elif defined(RT_OS_FREEBSD) +/** @todo FreeBSD: Check out this later (HIF networking). */ +#else +# error "Port me" +#endif + break; + } + + case NetworkAttachmentType_Internal: + { + hrc = aNetworkAdapter->COMGETTER(InternalNetwork)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + { + InsertConfigString(pLunL0, "Driver", "IntNet"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigString(pCfg, "Network", bstr); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone); + InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy); + networkName = bstr; + trunkType = Bstr(TRUNKTYPE_WHATEVER); + } + break; + } + + case NetworkAttachmentType_HostOnly: + { + InsertConfigString(pLunL0, "Driver", "IntNet"); + InsertConfigNode(pLunL0, "Config", &pCfg); + + Bstr HostOnlyName; + hrc = aNetworkAdapter->COMGETTER(HostOnlyInterface)(HostOnlyName.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(HostOnlyInterface) failed, hrc (0x%x)\n", hrc)); + H(); + } + + Utf8Str HostOnlyNameUtf8(HostOnlyName); + const char *pszHostOnlyName = HostOnlyNameUtf8.c_str(); +#ifdef VBOX_WITH_VMNET + /* Check if the matching host-only network has already been created. */ + Bstr bstrLowerIP, bstrUpperIP, bstrNetworkMask; + BstrFmt bstrNetworkName("Legacy %s Network", pszHostOnlyName); + ComPtr<IHostOnlyNetwork> hostOnlyNetwork; + hrc = virtualBox->FindHostOnlyNetworkByName(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam()); + if (FAILED(hrc)) + { + /* + * With VMNET there is no VBoxNetAdp to create vboxnetX adapters, + * which means that the Host object won't be able to re-create + * them from extra data. Go through existing DHCP/adapter config + * to derive the parameters for the new network. + */ + BstrFmt bstrOldNetworkName("HostInterfaceNetworking-%s", pszHostOnlyName); + ComPtr<IDHCPServer> dhcpServer; + hrc = virtualBox->FindDHCPServerByNetworkName(bstrOldNetworkName.raw(), + dhcpServer.asOutParam()); + if (SUCCEEDED(hrc)) + { + /* There is a DHCP server available for this network. */ + hrc = dhcpServer->COMGETTER(LowerIP)(bstrLowerIP.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("Console::i_configNetwork: COMGETTER(LowerIP) failed, hrc (%Rhrc)\n", hrc)); + H(); + } + hrc = dhcpServer->COMGETTER(UpperIP)(bstrUpperIP.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("Console::i_configNetwork: COMGETTER(UpperIP) failed, hrc (%Rhrc)\n", hrc)); + H(); + } + hrc = dhcpServer->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("Console::i_configNetwork: COMGETTER(NetworkMask) failed, hrc (%Rhrc)\n", hrc)); + H(); + } + } + else + { + /* No DHCP server for this hostonly interface, let's look at extra data */ + hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress", + pszHostOnlyName).raw(), + bstrLowerIP.asOutParam()); + if (SUCCEEDED(hrc) && !bstrLowerIP.isEmpty()) + hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask", + pszHostOnlyName).raw(), + bstrNetworkMask.asOutParam()); + + } + RTNETADDRIPV4 ipAddr, ipMask; + vrc = bstrLowerIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr); + if (RT_FAILURE(vrc)) + { + /* We failed to locate any valid config of this vboxnetX interface, assume defaults. */ + LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing lower IP '%ls', using '%ls' instead.\n", + bstrLowerIP.raw(), getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw())); + bstrLowerIP = getDefaultIPv4Address(Bstr(pszHostOnlyName)); + bstrNetworkMask.setNull(); + bstrUpperIP.setNull(); + vrc = RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr); + AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("RTNetStrToIPv4Addr(%ls) failed, vrc=%Rrc\n", bstrLowerIP.raw(), vrc), + VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR); + } + vrc = bstrNetworkMask.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask); + if (RT_FAILURE(vrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing network mask '%ls', using '%s' instead.\n", + bstrNetworkMask.raw(), VBOXNET_IPV4MASK_DEFAULT)); + bstrNetworkMask = VBOXNET_IPV4MASK_DEFAULT; + vrc = RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask); + AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("RTNetStrToIPv4Addr(%ls) failed, vrc=%Rrc\n", bstrNetworkMask.raw(), vrc), + VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR); + } + vrc = bstrUpperIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrUpperIP).c_str(), &ipAddr); + if (RT_FAILURE(vrc)) + { + ipAddr.au32[0] = RT_H2N_U32((RT_N2H_U32(ipAddr.au32[0]) | ~RT_N2H_U32(ipMask.au32[0])) - 1); /* Do we need to exlude the last IP? */ + LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing upper IP '%ls', using '%RTnaipv4' instead.\n", + bstrUpperIP.raw(), ipAddr)); + bstrUpperIP = BstrFmt("%RTnaipv4", ipAddr); + } + + /* All parameters are set, create the new network. */ + hrc = virtualBox->CreateHostOnlyNetwork(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: failed to create host-only network, hrc (0x%x)\n", hrc)); + H(); + } + hrc = hostOnlyNetwork->COMSETTER(NetworkMask)(bstrNetworkMask.raw()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(NetworkMask) failed, hrc (0x%x)\n", hrc)); + H(); + } + hrc = hostOnlyNetwork->COMSETTER(LowerIP)(bstrLowerIP.raw()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(LowerIP) failed, hrc (0x%x)\n", hrc)); + H(); + } + hrc = hostOnlyNetwork->COMSETTER(UpperIP)(bstrUpperIP.raw()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(UpperIP) failed, hrc (0x%x)\n", hrc)); + H(); + } + LogRel(("Console: created host-only network '%ls' with mask '%ls' and range '%ls'-'%ls'\n", + bstrNetworkName.raw(), bstrNetworkMask.raw(), bstrLowerIP.raw(), bstrUpperIP.raw())); + } + else + { + /* The matching host-only network already exists. Tell the user to switch to it. */ + hrc = hostOnlyNetwork->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(NetworkMask) failed, hrc (0x%x)\n", hrc)); + H(); + } + hrc = hostOnlyNetwork->COMGETTER(LowerIP)(bstrLowerIP.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(LowerIP) failed, hrc (0x%x)\n", hrc)); + H(); + } + hrc = hostOnlyNetwork->COMGETTER(UpperIP)(bstrUpperIP.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(UpperIP) failed, hrc (0x%x)\n", hrc)); + H(); + } + } + return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, + N_("Host-only adapters are no longer supported!\n" + "For your convenience a host-only network named '%ls' has been " + "created with network mask '%ls' and IP address range '%ls' - '%ls'.\n" + "To fix this problem, switch to 'Host-only Network' " + "attachment type in the VM settings.\n"), + bstrNetworkName.raw(), bstrNetworkMask.raw(), + bstrLowerIP.raw(), bstrUpperIP.raw()); +#endif /* VBOX_WITH_VMNET */ + ComPtr<IHostNetworkInterface> hostInterface; + hrc = host->FindHostNetworkInterfaceByName(HostOnlyName.raw(), + hostInterface.asOutParam()); + if (!SUCCEEDED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: FindByName failed, vrc=%Rrc\n", vrc)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS, + N_("Nonexistent host networking interface, name '%ls'"), HostOnlyName.raw()); + } + + char szNetwork[INTNET_MAX_NETWORK_NAME]; + RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszHostOnlyName); + +#if defined(RT_OS_WINDOWS) +# ifndef VBOX_WITH_NETFLT + hrc = E_NOTIMPL; + LogRel(("NetworkAttachmentType_HostOnly: Not Implemented\n")); + H(); +# else /* defined VBOX_WITH_NETFLT*/ + /** @todo r=bird: Put this in a function. */ + + HostNetworkInterfaceType_T eIfType; + hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc)); + H(); + } + + if (eIfType != HostNetworkInterfaceType_HostOnly) + return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS, + N_("Interface ('%ls') is not a Host-Only Adapter interface"), + HostOnlyName.raw()); + + hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(Id) failed, hrc (0x%x)\n", hrc)); + H(); + } + Guid hostIFGuid(bstr); + + INetCfg *pNc; + ComPtr<INetCfgComponent> pAdaptorComponent; + LPWSTR pszApp; + hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp); + Assert(hrc == S_OK); + if (hrc != S_OK) + { + LogRel(("NetworkAttachmentType_HostOnly: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc)); + H(); + } + + /* get the adapter's INetCfgComponent*/ + hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(), + pAdaptorComponent.asOutParam()); + if (hrc != S_OK) + { + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + LogRel(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n", hrc, hrc)); + H(); + } +# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\" + char szTrunkName[INTNET_MAX_TRUNK_NAME]; + bool fNdis6 = false; + wchar_t * pwszHelpText; + hrc = pAdaptorComponent->GetHelpText(&pwszHelpText); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + Log(("help-text=%ls\n", pwszHelpText)); + if (!wcscmp(pwszHelpText, L"VirtualBox NDIS 6.0 Miniport Driver")) + fNdis6 = true; + CoTaskMemFree(pwszHelpText); + } + if (fNdis6) + { + strncpy(szTrunkName, pszHostOnlyName, sizeof(szTrunkName) - 1); + Log(("trunk=%s\n", szTrunkName)); + } + else + { + char *pszTrunkName = szTrunkName; + wchar_t * pswzBindName; + hrc = pAdaptorComponent->GetBindName(&pswzBindName); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + int cwBindName = (int)wcslen(pswzBindName) + 1; + int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX); + if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName) + { + strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX); + pszTrunkName += cbFullBindNamePrefix-1; + if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName, + sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL)) + { + DWORD err = GetLastError(); + hrc = HRESULT_FROM_WIN32(err); + AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n", + hrc, hrc, err)); + } + } + else + { + AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: insufficient szTrunkName buffer space\n")); + /** @todo set appropriate error code */ + hrc = E_FAIL; + } + + if (hrc != S_OK) + { + AssertFailed(); + CoTaskMemFree(pswzBindName); + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + H(); + } + } + else + { + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n", + hrc, hrc)); + H(); + } + + + CoTaskMemFree(pswzBindName); + } + + trunkType = TRUNKTYPE_NETADP; + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp); + + pAdaptorComponent.setNull(); + /* release the pNc finally */ + VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/); + + const char *pszTrunk = szTrunkName; + + InsertConfigString(pCfg, "Trunk", pszTrunk); + InsertConfigString(pCfg, "Network", szNetwork); + InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure); /** @todo why is this + windows only?? */ + networkName = Bstr(szNetwork); + trunkName = Bstr(pszTrunk); +# endif /* defined VBOX_WITH_NETFLT*/ +#elif defined(RT_OS_DARWIN) + InsertConfigString(pCfg, "Trunk", pszHostOnlyName); + InsertConfigString(pCfg, "Network", szNetwork); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp); + networkName = Bstr(szNetwork); + trunkName = Bstr(pszHostOnlyName); + trunkType = TRUNKTYPE_NETADP; +#else + InsertConfigString(pCfg, "Trunk", pszHostOnlyName); + InsertConfigString(pCfg, "Network", szNetwork); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt); + networkName = Bstr(szNetwork); + trunkName = Bstr(pszHostOnlyName); + trunkType = TRUNKTYPE_NETFLT; +#endif + InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy); + +#if !defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) + + Bstr tmpAddr, tmpMask; + + hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress", + pszHostOnlyName).raw(), + tmpAddr.asOutParam()); + if (SUCCEEDED(hrc) && !tmpAddr.isEmpty()) + { + hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask", + pszHostOnlyName).raw(), + tmpMask.asOutParam()); + if (SUCCEEDED(hrc) && !tmpMask.isEmpty()) + hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(), + tmpMask.raw()); + else + hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(), + Bstr(VBOXNET_IPV4MASK_DEFAULT).raw()); + } + else + { + /* Grab the IP number from the 'vboxnetX' instance number (see netif.h) */ + hrc = hostInterface->EnableStaticIPConfig(getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw(), + Bstr(VBOXNET_IPV4MASK_DEFAULT).raw()); + } + + ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */ + + hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6Address", + pszHostOnlyName).raw(), + tmpAddr.asOutParam()); + if (SUCCEEDED(hrc)) + hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6NetMask", pszHostOnlyName).raw(), + tmpMask.asOutParam()); + if (SUCCEEDED(hrc) && !tmpAddr.isEmpty() && !tmpMask.isEmpty()) + { + hrc = hostInterface->EnableStaticIPConfigV6(tmpAddr.raw(), + Utf8Str(tmpMask).toUInt32()); + ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */ + } +#endif + break; + } + + case NetworkAttachmentType_Generic: + { + hrc = aNetworkAdapter->COMGETTER(GenericDriver)(bstr.asOutParam()); H(); + SafeArray<BSTR> names; + SafeArray<BSTR> values; + hrc = aNetworkAdapter->GetProperties(Bstr().raw(), + ComSafeArrayAsOutParam(names), + ComSafeArrayAsOutParam(values)); H(); + + InsertConfigString(pLunL0, "Driver", bstr); + InsertConfigNode(pLunL0, "Config", &pCfg); + for (size_t ii = 0; ii < names.size(); ++ii) + { + if (values[ii] && *values[ii]) + { + Utf8Str name = names[ii]; + Utf8Str value = values[ii]; + InsertConfigString(pCfg, name.c_str(), value); + } + } + break; + } + + case NetworkAttachmentType_NATNetwork: + { + hrc = aNetworkAdapter->COMGETTER(NATNetwork)(bstr.asOutParam()); H(); + if (!bstr.isEmpty()) + { + /** @todo add intnet prefix to separate namespaces, and add trunk if dealing with vboxnatX */ + InsertConfigString(pLunL0, "Driver", "IntNet"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigString(pCfg, "Network", bstr); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone); + InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy); + networkName = bstr; + trunkType = Bstr(TRUNKTYPE_WHATEVER); + } + break; + } + +#ifdef VBOX_WITH_CLOUD_NET + case NetworkAttachmentType_Cloud: + { + static const char *s_pszCloudExtPackName = "Oracle VM VirtualBox Extension Pack"; + /* + * Cloud network attachments do not work wihout installed extpack. + * Without extpack support they won't work either. + */ +# ifdef VBOX_WITH_EXTPACK + if (!mptrExtPackManager->i_isExtPackUsable(s_pszCloudExtPackName)) +# endif + { + return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, + N_("Implementation of the cloud network attachment not found!\n" + "To fix this problem, either install the '%s' or switch to " + "another network attachment type in the VM settings."), + s_pszCloudExtPackName); + } + + ComPtr<ICloudNetwork> network; + hrc = aNetworkAdapter->COMGETTER(CloudNetwork)(bstr.asOutParam()); H(); + hrc = pMachine->COMGETTER(Name)(mGateway.mTargetVM.asOutParam()); H(); + hrc = virtualBox->FindCloudNetworkByName(bstr.raw(), network.asOutParam()); H(); + hrc = generateKeys(mGateway); + if (FAILED(hrc)) + { + if (hrc == E_NOTIMPL) + return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS, + N_("Failed to generate a key pair due to missing libssh\n" + "To fix this problem, either build VirtualBox with libssh " + "support or switch to another network attachment type in " + "the VM settings.")); + return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS, + N_("Failed to generate a key pair due to libssh error!")); + } + hrc = startCloudGateway(virtualBox, network, mGateway); + if (FAILED(hrc)) + { + if (hrc == VBOX_E_OBJECT_NOT_FOUND) + return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS, + N_("Failed to start cloud gateway instance.\nCould not find suitable " + "standard cloud images. Make sure you ran 'VBoxManage cloud network setup' " + "with correct '--gateway-os-name' and '--gateway-os-version' parameters. " + "Check VBoxSVC.log for actual values used to look up cloud images.")); + return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS, + N_("Failed to start cloud gateway instance.\nMake sure you set up " + "cloud networking properly with 'VBoxManage cloud network setup'. " + "Check VBoxSVC.log for details.")); + } + InsertConfigBytes(pDevCfg, "MAC", &mGateway.mCloudMacAddress, sizeof(mGateway.mCloudMacAddress)); + if (!bstr.isEmpty()) + { + InsertConfigString(pLunL0, "Driver", "CloudTunnel"); + InsertConfigNode(pLunL0, "Config", &pCfg); + InsertConfigPassword(pCfg, "SshKey", mGateway.mPrivateSshKey); + InsertConfigString(pCfg, "PrimaryIP", mGateway.mCloudPublicIp); + InsertConfigString(pCfg, "SecondaryIP", mGateway.mCloudSecondaryPublicIp); + InsertConfigBytes(pCfg, "TargetMAC", &mGateway.mLocalMacAddress, sizeof(mGateway.mLocalMacAddress)); + hrc = i_configProxy(virtualBox, pCfg, "Primary", mGateway.mCloudPublicIp); + if (FAILED(hrc)) + { + return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS, + N_("Failed to configure proxy for accessing cloud gateway instance via primary VNIC.\n" + "Check VirtualBox.log for details.")); + } + hrc = i_configProxy(virtualBox, pCfg, "Secondary", mGateway.mCloudSecondaryPublicIp); + if (FAILED(hrc)) + { + return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS, + N_("Failed to configure proxy for accessing cloud gateway instance via secondary VNIC.\n" + "Check VirtualBox.log for details.")); + } + networkName = bstr; + trunkType = Bstr(TRUNKTYPE_WHATEVER); + } + break; + } +#endif /* VBOX_WITH_CLOUD_NET */ + +#ifdef VBOX_WITH_VMNET + case NetworkAttachmentType_HostOnlyNetwork: + { + Bstr bstrId, bstrNetMask, bstrLowerIP, bstrUpperIP; + ComPtr<IHostOnlyNetwork> network; + hrc = aNetworkAdapter->COMGETTER(HostOnlyNetwork)(bstr.asOutParam()); H(); + hrc = virtualBox->FindHostOnlyNetworkByName(bstr.raw(), network.asOutParam()); + if (FAILED(hrc)) + { + LogRel(("NetworkAttachmentType_HostOnlyNetwork: FindByName failed, hrc (0x%x)\n", hrc)); + return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS, + N_("Nonexistent host-only network '%ls'"), bstr.raw()); + } + hrc = network->COMGETTER(Id)(bstrId.asOutParam()); H(); + hrc = network->COMGETTER(NetworkMask)(bstrNetMask.asOutParam()); H(); + hrc = network->COMGETTER(LowerIP)(bstrLowerIP.asOutParam()); H(); + hrc = network->COMGETTER(UpperIP)(bstrUpperIP.asOutParam()); H(); + if (!bstr.isEmpty()) + { + InsertConfigString(pLunL0, "Driver", "VMNet"); + InsertConfigNode(pLunL0, "Config", &pCfg); + // InsertConfigString(pCfg, "Trunk", Utf8Str(bstr).c_str()); + // InsertConfigString(pCfg, "Network", BstrFmt("HostOnlyNetworking-%ls", bstr.raw())); + InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp); + InsertConfigString(pCfg, "Id", Utf8Str(bstrId).c_str()); + InsertConfigString(pCfg, "NetworkMask", Utf8Str(bstrNetMask).c_str()); + InsertConfigString(pCfg, "LowerIP", Utf8Str(bstrLowerIP).c_str()); + InsertConfigString(pCfg, "UpperIP", Utf8Str(bstrUpperIP).c_str()); + // InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy); + networkName.setNull(); // We do not want DHCP server on our network! + // trunkType = Bstr(TRUNKTYPE_WHATEVER); + } + break; + } +#endif /* VBOX_WITH_VMNET */ + + default: + AssertMsgFailed(("should not get here!\n")); + break; + } + + /* + * Attempt to attach the driver. + */ + switch (eAttachmentType) + { + case NetworkAttachmentType_Null: + break; + + case NetworkAttachmentType_Bridged: + case NetworkAttachmentType_Internal: + case NetworkAttachmentType_HostOnly: +#ifdef VBOX_WITH_VMNET + case NetworkAttachmentType_HostOnlyNetwork: +#endif /* VBOX_WITH_VMNET */ + case NetworkAttachmentType_NAT: + case NetworkAttachmentType_Generic: + case NetworkAttachmentType_NATNetwork: +#ifdef VBOX_WITH_CLOUD_NET + case NetworkAttachmentType_Cloud: +#endif /* VBOX_WITH_CLOUD_NET */ + { + if (SUCCEEDED(hrc) && RT_SUCCESS(vrc)) + { + if (fAttachDetach) + { + vrc = pVMM->pfnPDMR3DriverAttach(mpUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/, NULL /* ppBase */); + //AssertRC(vrc); + } + + { + /** @todo pritesh: get the dhcp server name from the + * previous network configuration and then stop the server + * else it may conflict with the dhcp server running with + * the current attachment type + */ + /* Stop the hostonly DHCP Server */ + } + + /* + * NAT networks start their DHCP server theirself, see NATNetwork::Start() + */ + if ( !networkName.isEmpty() + && eAttachmentType != NetworkAttachmentType_NATNetwork) + { + /* + * Until we implement service reference counters DHCP Server will be stopped + * by DHCPServerRunner destructor. + */ + ComPtr<IDHCPServer> dhcpServer; + hrc = virtualBox->FindDHCPServerByNetworkName(networkName.raw(), dhcpServer.asOutParam()); + if (SUCCEEDED(hrc)) + { + /* there is a DHCP server available for this network */ + BOOL fEnabledDhcp; + hrc = dhcpServer->COMGETTER(Enabled)(&fEnabledDhcp); + if (FAILED(hrc)) + { + LogRel(("DHCP svr: COMGETTER(Enabled) failed, hrc (%Rhrc)\n", hrc)); + H(); + } + + if (fEnabledDhcp) + hrc = dhcpServer->Start(trunkName.raw(), trunkType.raw()); + } + else + hrc = S_OK; + } + } + + break; + } + + default: + AssertMsgFailed(("should not get here!\n")); + break; + } + + meAttachmentType[uInstance] = eAttachmentType; + } + catch (ConfigError &x) + { + // InsertConfig threw something: + return x.m_vrc; + } + +#undef H + + return VINF_SUCCESS; +} + + +/** + * Configures the serial port at the given CFGM node with the supplied parameters. + * + * @returns VBox status code. + * @param pInst The instance CFGM node. + * @param ePortMode The port mode to sue. + * @param pszPath The serial port path. + * @param fServer Flag whether the port should act as a server + * for the pipe and TCP mode or connect as a client. + */ +int Console::i_configSerialPort(PCFGMNODE pInst, PortMode_T ePortMode, const char *pszPath, bool fServer) +{ + PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */ + PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */ + PCFGMNODE pLunL1Cfg = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/Config */ + + try + { + InsertConfigNode(pInst, "LUN#0", &pLunL0); + if (ePortMode == PortMode_HostPipe) + { + InsertConfigString(pLunL0, "Driver", "Char"); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "NamedPipe"); + InsertConfigNode(pLunL1, "Config", &pLunL1Cfg); + InsertConfigString(pLunL1Cfg, "Location", pszPath); + InsertConfigInteger(pLunL1Cfg, "IsServer", fServer); + } + else if (ePortMode == PortMode_HostDevice) + { + InsertConfigString(pLunL0, "Driver", "Host Serial"); + InsertConfigNode(pLunL0, "Config", &pLunL1); + InsertConfigString(pLunL1, "DevicePath", pszPath); + } + else if (ePortMode == PortMode_TCP) + { + InsertConfigString(pLunL0, "Driver", "Char"); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "TCP"); + InsertConfigNode(pLunL1, "Config", &pLunL1Cfg); + InsertConfigString(pLunL1Cfg, "Location", pszPath); + InsertConfigInteger(pLunL1Cfg, "IsServer", fServer); + } + else if (ePortMode == PortMode_RawFile) + { + InsertConfigString(pLunL0, "Driver", "Char"); + InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1); + InsertConfigString(pLunL1, "Driver", "RawFile"); + InsertConfigNode(pLunL1, "Config", &pLunL1Cfg); + InsertConfigString(pLunL1Cfg, "Location", pszPath); + } + } + catch (ConfigError &x) + { + /* InsertConfig threw something */ + return x.m_vrc; + } + + return VINF_SUCCESS; +} + |