summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-client/BusAssignmentManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client/BusAssignmentManager.cpp')
-rw-r--r--src/VBox/Main/src-client/BusAssignmentManager.cpp711
1 files changed, 711 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/BusAssignmentManager.cpp b/src/VBox/Main/src-client/BusAssignmentManager.cpp
new file mode 100644
index 00000000..c21f2f53
--- /dev/null
+++ b/src/VBox/Main/src-client/BusAssignmentManager.cpp
@@ -0,0 +1,711 @@
+/* $Id: BusAssignmentManager.cpp $ */
+/** @file
+ * VirtualBox bus slots assignment manager
+ */
+
+/*
+ * Copyright (C) 2010-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
+#include "LoggingNew.h"
+
+#include "BusAssignmentManager.h"
+
+#include <iprt/asm.h>
+#include <iprt/string.h>
+
+#include <VBox/vmm/cfgm.h>
+#include <VBox/vmm/vmmr3vtable.h>
+#include <VBox/com/array.h>
+
+#include <map>
+#include <vector>
+#include <algorithm>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+struct DeviceAssignmentRule
+{
+ const char *pszName;
+ int iBus;
+ int iDevice;
+ int iFn;
+ int iPriority;
+};
+
+struct DeviceAliasRule
+{
+ const char *pszDevName;
+ const char *pszDevAlias;
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/* Those rules define PCI slots assignment */
+/** @note
+ * The EFI takes assumptions about PCI slot assignments which are different
+ * from the following tables in certain cases, for example the IDE device
+ * is assumed to be 00:01.1! */
+
+/* Device Bus Device Function Priority */
+
+/* Generic rules */
+static const DeviceAssignmentRule g_aGenericRules[] =
+{
+ /* VGA controller */
+ {"vga", 0, 2, 0, 0},
+
+ /* VMM device */
+ {"VMMDev", 0, 4, 0, 0},
+
+ /* Audio controllers */
+ {"ichac97", 0, 5, 0, 0},
+ {"hda", 0, 5, 0, 0},
+
+ /* Storage controllers */
+ {"buslogic", 0, 21, 0, 1},
+ {"lsilogicsas", 0, 22, 0, 1},
+ {"nvme", 0, 14, 0, 1},
+ {"virtio-scsi", 0, 15, 0, 1},
+
+ /* USB controllers */
+ {"usb-ohci", 0, 6, 0, 0},
+ {"usb-ehci", 0, 11, 0, 0},
+ {"usb-xhci", 0, 12, 0, 0},
+
+ /* ACPI controller */
+#if 0
+ // It really should be this for 440FX chipset (part of PIIX4 actually)
+ {"acpi", 0, 1, 3, 0},
+#else
+ {"acpi", 0, 7, 0, 0},
+#endif
+
+ /* Network controllers */
+ /* the first network card gets the PCI ID 3, the next 3 gets 8..10,
+ * next 4 get 16..19. In "VMWare compatibility" mode the IDs 3 and 17
+ * swap places, i.e. the first card goes to ID 17=0x11. */
+ {"nic", 0, 3, 0, 1},
+ {"nic", 0, 8, 0, 1},
+ {"nic", 0, 9, 0, 1},
+ {"nic", 0, 10, 0, 1},
+ {"nic", 0, 16, 0, 1},
+ {"nic", 0, 17, 0, 1},
+ {"nic", 0, 18, 0, 1},
+ {"nic", 0, 19, 0, 1},
+
+ /* ISA/LPC controller */
+ {"lpc", 0, 31, 0, 0},
+
+ { NULL, -1, -1, -1, 0}
+};
+
+/* PIIX3 chipset rules */
+static const DeviceAssignmentRule g_aPiix3Rules[] =
+{
+ {"piix3ide", 0, 1, 1, 0},
+ {"ahci", 0, 13, 0, 1},
+ {"lsilogic", 0, 20, 0, 1},
+ {"pcibridge", 0, 24, 0, 0},
+ {"pcibridge", 0, 25, 0, 0},
+ { NULL, -1, -1, -1, 0}
+};
+
+
+/* ICH9 chipset rules */
+static const DeviceAssignmentRule g_aIch9Rules[] =
+{
+ /* Host Controller */
+ {"i82801", 0, 30, 0, 0},
+
+ /* Those are functions of LPC at 00:1e:00 */
+ /**
+ * Please note, that for devices being functions, like we do here, device 0
+ * must be multifunction, i.e. have header type 0x80. Our LPC device is.
+ * Alternative approach is to assign separate slot to each device.
+ */
+ {"piix3ide", 0, 31, 1, 2},
+ {"ahci", 0, 31, 2, 2},
+ {"smbus", 0, 31, 3, 2},
+ {"usb-ohci", 0, 31, 4, 2},
+ {"usb-ehci", 0, 31, 5, 2},
+ {"thermal", 0, 31, 6, 2},
+
+ /* to make sure rule never used before rules assigning devices on it */
+ {"ich9pcibridge", 0, 24, 0, 10},
+ {"ich9pcibridge", 0, 25, 0, 10},
+ {"ich9pcibridge", 2, 24, 0, 9}, /* Bridges must be instantiated depth */
+ {"ich9pcibridge", 2, 25, 0, 9}, /* first (assumption in PDM and other */
+ {"ich9pcibridge", 4, 24, 0, 8}, /* places), so make sure that nested */
+ {"ich9pcibridge", 4, 25, 0, 8}, /* bridges are added to the last bridge */
+ {"ich9pcibridge", 6, 24, 0, 7}, /* only, avoiding the need to re-sort */
+ {"ich9pcibridge", 6, 25, 0, 7}, /* everything before starting the VM. */
+ {"ich9pcibridge", 8, 24, 0, 6},
+ {"ich9pcibridge", 8, 25, 0, 6},
+ {"ich9pcibridge", 10, 24, 0, 5},
+ {"ich9pcibridge", 10, 25, 0, 5},
+
+ /* Storage controllers */
+ {"ahci", 1, 0, 0, 0},
+ {"ahci", 1, 1, 0, 0},
+ {"ahci", 1, 2, 0, 0},
+ {"ahci", 1, 3, 0, 0},
+ {"ahci", 1, 4, 0, 0},
+ {"ahci", 1, 5, 0, 0},
+ {"ahci", 1, 6, 0, 0},
+ {"lsilogic", 1, 7, 0, 0},
+ {"lsilogic", 1, 8, 0, 0},
+ {"lsilogic", 1, 9, 0, 0},
+ {"lsilogic", 1, 10, 0, 0},
+ {"lsilogic", 1, 11, 0, 0},
+ {"lsilogic", 1, 12, 0, 0},
+ {"lsilogic", 1, 13, 0, 0},
+ {"buslogic", 1, 14, 0, 0},
+ {"buslogic", 1, 15, 0, 0},
+ {"buslogic", 1, 16, 0, 0},
+ {"buslogic", 1, 17, 0, 0},
+ {"buslogic", 1, 18, 0, 0},
+ {"buslogic", 1, 19, 0, 0},
+ {"buslogic", 1, 20, 0, 0},
+ {"lsilogicsas", 1, 21, 0, 0},
+ {"lsilogicsas", 1, 26, 0, 0},
+ {"lsilogicsas", 1, 27, 0, 0},
+ {"lsilogicsas", 1, 28, 0, 0},
+ {"lsilogicsas", 1, 29, 0, 0},
+ {"lsilogicsas", 1, 30, 0, 0},
+ {"lsilogicsas", 1, 31, 0, 0},
+
+ /* NICs */
+ {"nic", 2, 0, 0, 0},
+ {"nic", 2, 1, 0, 0},
+ {"nic", 2, 2, 0, 0},
+ {"nic", 2, 3, 0, 0},
+ {"nic", 2, 4, 0, 0},
+ {"nic", 2, 5, 0, 0},
+ {"nic", 2, 6, 0, 0},
+ {"nic", 2, 7, 0, 0},
+ {"nic", 2, 8, 0, 0},
+ {"nic", 2, 9, 0, 0},
+ {"nic", 2, 10, 0, 0},
+ {"nic", 2, 11, 0, 0},
+ {"nic", 2, 12, 0, 0},
+ {"nic", 2, 13, 0, 0},
+ {"nic", 2, 14, 0, 0},
+ {"nic", 2, 15, 0, 0},
+ {"nic", 2, 16, 0, 0},
+ {"nic", 2, 17, 0, 0},
+ {"nic", 2, 18, 0, 0},
+ {"nic", 2, 19, 0, 0},
+ {"nic", 2, 20, 0, 0},
+ {"nic", 2, 21, 0, 0},
+ {"nic", 2, 26, 0, 0},
+ {"nic", 2, 27, 0, 0},
+ {"nic", 2, 28, 0, 0},
+ {"nic", 2, 29, 0, 0},
+ {"nic", 2, 30, 0, 0},
+ {"nic", 2, 31, 0, 0},
+
+ /* Storage controller #2 (NVMe, virtio-scsi) */
+ {"nvme", 3, 0, 0, 0},
+ {"nvme", 3, 1, 0, 0},
+ {"nvme", 3, 2, 0, 0},
+ {"nvme", 3, 3, 0, 0},
+ {"nvme", 3, 4, 0, 0},
+ {"nvme", 3, 5, 0, 0},
+ {"nvme", 3, 6, 0, 0},
+ {"virtio-scsi", 3, 7, 0, 0},
+ {"virtio-scsi", 3, 8, 0, 0},
+ {"virtio-scsi", 3, 9, 0, 0},
+ {"virtio-scsi", 3, 10, 0, 0},
+ {"virtio-scsi", 3, 11, 0, 0},
+ {"virtio-scsi", 3, 12, 0, 0},
+ {"virtio-scsi", 3, 13, 0, 0},
+
+ { NULL, -1, -1, -1, 0}
+};
+
+
+#ifdef VBOX_WITH_IOMMU_AMD
+/*
+ * AMD IOMMU and LSI Logic controller rules.
+ *
+ * Since the PCI slot (BDF=00:20.0) of the LSI Logic controller
+ * conflicts with the SB I/O APIC, we assign the LSI Logic controller
+ * to device number 23 when the VM is configured for an AMD IOMMU.
+ */
+static const DeviceAssignmentRule g_aIch9IommuAmdRules[] =
+{
+ /* AMD IOMMU. */
+ {"iommu-amd", 0, 0, 0, 0},
+ /* AMD IOMMU: Reserved for southbridge I/O APIC. */
+ {"sb-ioapic", 0, 20, 0, 0},
+
+ /* Storage controller */
+ {"lsilogic", 0, 23, 0, 1},
+ { NULL, -1, -1, -1, 0}
+};
+#endif
+
+#ifdef VBOX_WITH_IOMMU_INTEL
+/*
+ * Intel IOMMU.
+ * The VT-d misc, address remapping, system management device is
+ * located at BDF 0:5:0 on real hardware but we use 0:1:0 since that
+ * slot isn't used for anything else.
+ *
+ * While we could place the I/O APIC anywhere, we keep it consistent
+ * with the AMD IOMMU and we assign the LSI Logic controller to
+ * device number 23 (and I/O APIC at device 20).
+ */
+static const DeviceAssignmentRule g_aIch9IommuIntelRules[] =
+{
+ /* Intel IOMMU. */
+ {"iommu-intel", 0, 1, 0, 0},
+ /* Intel IOMMU: Reserved for I/O APIC. */
+ {"sb-ioapic", 0, 20, 0, 0},
+
+ /* Storage controller */
+ {"lsilogic", 0, 23, 0, 1},
+ { NULL, -1, -1, -1, 0}
+};
+#endif
+
+/* LSI Logic Controller. */
+static const DeviceAssignmentRule g_aIch9LsiRules[] =
+{
+ /* Storage controller */
+ {"lsilogic", 0, 20, 0, 1},
+ { NULL, -1, -1, -1, 0}
+};
+
+/* Aliasing rules */
+static const DeviceAliasRule g_aDeviceAliases[] =
+{
+ {"e1000", "nic"},
+ {"pcnet", "nic"},
+ {"virtio-net", "nic"},
+ {"ahci", "storage"},
+ {"lsilogic", "storage"},
+ {"buslogic", "storage"},
+ {"lsilogicsas", "storage"},
+ {"nvme", "storage"},
+ {"virtio-scsi", "storage"}
+};
+
+
+
+/**
+ * Bus assignment manage state data.
+ * @internal
+ */
+struct BusAssignmentManager::State
+{
+ struct PCIDeviceRecord
+ {
+ char szDevName[32];
+ PCIBusAddress HostAddress;
+
+ PCIDeviceRecord(const char *pszName, PCIBusAddress aHostAddress)
+ {
+ RTStrCopy(this->szDevName, sizeof(szDevName), pszName);
+ this->HostAddress = aHostAddress;
+ }
+
+ PCIDeviceRecord(const char *pszName)
+ {
+ RTStrCopy(this->szDevName, sizeof(szDevName), pszName);
+ }
+
+ bool operator<(const PCIDeviceRecord &a) const
+ {
+ return RTStrNCmp(szDevName, a.szDevName, sizeof(szDevName)) < 0;
+ }
+
+ bool operator==(const PCIDeviceRecord &a) const
+ {
+ return RTStrNCmp(szDevName, a.szDevName, sizeof(szDevName)) == 0;
+ }
+ };
+
+ typedef std::map<PCIBusAddress,PCIDeviceRecord> PCIMap;
+ typedef std::vector<PCIBusAddress> PCIAddrList;
+ typedef std::vector<const DeviceAssignmentRule *> PCIRulesList;
+ typedef std::map<PCIDeviceRecord,PCIAddrList> ReversePCIMap;
+
+ volatile int32_t cRefCnt;
+ ChipsetType_T mChipsetType;
+ const char * mpszBridgeName;
+ IommuType_T mIommuType;
+ PCIMap mPCIMap;
+ ReversePCIMap mReversePCIMap;
+ PCVMMR3VTABLE mpVMM;
+
+ State()
+ : cRefCnt(1), mChipsetType(ChipsetType_Null), mpszBridgeName("unknownbridge"), mpVMM(NULL)
+ {}
+ ~State()
+ {}
+
+ HRESULT init(PCVMMR3VTABLE pVMM, ChipsetType_T chipsetType, IommuType_T iommuType);
+
+ HRESULT record(const char *pszName, PCIBusAddress& GuestAddress, PCIBusAddress HostAddress);
+ HRESULT autoAssign(const char *pszName, PCIBusAddress& Address);
+ bool checkAvailable(PCIBusAddress& Address);
+ bool findPCIAddress(const char *pszDevName, int iInstance, PCIBusAddress& Address);
+
+ const char *findAlias(const char *pszName);
+ void addMatchingRules(const char *pszName, PCIRulesList& aList);
+ void listAttachedPCIDevices(std::vector<PCIDeviceInfo> &aAttached);
+};
+
+
+HRESULT BusAssignmentManager::State::init(PCVMMR3VTABLE pVMM, ChipsetType_T chipsetType, IommuType_T iommuType)
+{
+ mpVMM = pVMM;
+
+ if (iommuType != IommuType_None)
+ {
+#if defined(VBOX_WITH_IOMMU_AMD) && defined(VBOX_WITH_IOMMU_INTEL)
+ Assert(iommuType == IommuType_AMD || iommuType == IommuType_Intel);
+#elif defined(VBOX_WITH_IOMMU_AMD)
+ Assert(iommuType == IommuType_AMD);
+#elif defined(VBOX_WITH_IOMMU_INTEL)
+ Assert(iommuType == IommuType_Intel);
+#endif
+ }
+
+ mChipsetType = chipsetType;
+ mIommuType = iommuType;
+ switch (chipsetType)
+ {
+ case ChipsetType_PIIX3:
+ mpszBridgeName = "pcibridge";
+ break;
+ case ChipsetType_ICH9:
+ mpszBridgeName = "ich9pcibridge";
+ break;
+ default:
+ mpszBridgeName = "unknownbridge";
+ AssertFailed();
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT BusAssignmentManager::State::record(const char *pszName, PCIBusAddress& Address, PCIBusAddress HostAddress)
+{
+ PCIDeviceRecord devRec(pszName, HostAddress);
+
+ /* Remember address -> device mapping */
+ mPCIMap.insert(PCIMap::value_type(Address, devRec));
+
+ ReversePCIMap::iterator it = mReversePCIMap.find(devRec);
+ if (it == mReversePCIMap.end())
+ {
+ mReversePCIMap.insert(ReversePCIMap::value_type(devRec, PCIAddrList()));
+ it = mReversePCIMap.find(devRec);
+ }
+
+ /* Remember device name -> addresses mapping */
+ it->second.push_back(Address);
+
+ return S_OK;
+}
+
+bool BusAssignmentManager::State::findPCIAddress(const char *pszDevName, int iInstance, PCIBusAddress& Address)
+{
+ PCIDeviceRecord devRec(pszDevName);
+
+ ReversePCIMap::iterator it = mReversePCIMap.find(devRec);
+ if (it == mReversePCIMap.end())
+ return false;
+
+ if (iInstance >= (int)it->second.size())
+ return false;
+
+ Address = it->second[iInstance];
+ return true;
+}
+
+void BusAssignmentManager::State::addMatchingRules(const char *pszName, PCIRulesList& aList)
+{
+ size_t iRuleset, iRule;
+ const DeviceAssignmentRule *aArrays[3] = {g_aGenericRules, NULL, NULL};
+
+ switch (mChipsetType)
+ {
+ case ChipsetType_PIIX3:
+ aArrays[1] = g_aPiix3Rules;
+ break;
+ case ChipsetType_ICH9:
+ {
+ aArrays[1] = g_aIch9Rules;
+#ifdef VBOX_WITH_IOMMU_AMD
+ if (mIommuType == IommuType_AMD)
+ aArrays[2] = g_aIch9IommuAmdRules;
+ else
+#endif
+#ifdef VBOX_WITH_IOMMU_INTEL
+ if (mIommuType == IommuType_Intel)
+ aArrays[2] = g_aIch9IommuIntelRules;
+ else
+#endif
+ {
+ aArrays[2] = g_aIch9LsiRules;
+ }
+ break;
+ }
+ default:
+ AssertFailed();
+ break;
+ }
+
+ for (iRuleset = 0; iRuleset < RT_ELEMENTS(aArrays); iRuleset++)
+ {
+ if (aArrays[iRuleset] == NULL)
+ continue;
+
+ for (iRule = 0; aArrays[iRuleset][iRule].pszName != NULL; iRule++)
+ {
+ if (RTStrCmp(pszName, aArrays[iRuleset][iRule].pszName) == 0)
+ aList.push_back(&aArrays[iRuleset][iRule]);
+ }
+ }
+}
+
+const char *BusAssignmentManager::State::findAlias(const char *pszDev)
+{
+ for (size_t iAlias = 0; iAlias < RT_ELEMENTS(g_aDeviceAliases); iAlias++)
+ {
+ if (strcmp(pszDev, g_aDeviceAliases[iAlias].pszDevName) == 0)
+ return g_aDeviceAliases[iAlias].pszDevAlias;
+ }
+ return NULL;
+}
+
+static bool RuleComparator(const DeviceAssignmentRule *r1, const DeviceAssignmentRule *r2)
+{
+ return (r1->iPriority > r2->iPriority);
+}
+
+HRESULT BusAssignmentManager::State::autoAssign(const char *pszName, PCIBusAddress& Address)
+{
+ PCIRulesList matchingRules;
+
+ addMatchingRules(pszName, matchingRules);
+ const char *pszAlias = findAlias(pszName);
+ if (pszAlias)
+ addMatchingRules(pszAlias, matchingRules);
+
+ AssertMsg(matchingRules.size() > 0, ("No rule for %s(%s)\n", pszName, pszAlias));
+
+ stable_sort(matchingRules.begin(), matchingRules.end(), RuleComparator);
+
+ for (size_t iRule = 0; iRule < matchingRules.size(); iRule++)
+ {
+ const DeviceAssignmentRule *rule = matchingRules[iRule];
+
+ Address.miBus = rule->iBus;
+ Address.miDevice = rule->iDevice;
+ Address.miFn = rule->iFn;
+
+ if (checkAvailable(Address))
+ return S_OK;
+ }
+ AssertLogRelMsgFailed(("BusAssignmentManager: All possible candidate positions for %s exhausted\n", pszName));
+
+ return E_INVALIDARG;
+}
+
+bool BusAssignmentManager::State::checkAvailable(PCIBusAddress& Address)
+{
+ PCIMap::const_iterator it = mPCIMap.find(Address);
+
+ return (it == mPCIMap.end());
+}
+
+void BusAssignmentManager::State::listAttachedPCIDevices(std::vector<PCIDeviceInfo> &aAttached)
+{
+ aAttached.resize(mPCIMap.size());
+
+ size_t i = 0;
+ PCIDeviceInfo dev;
+ for (PCIMap::const_iterator it = mPCIMap.begin(); it != mPCIMap.end(); ++it, ++i)
+ {
+ dev.strDeviceName = it->second.szDevName;
+ dev.guestAddress = it->first;
+ dev.hostAddress = it->second.HostAddress;
+ aAttached[i] = dev;
+ }
+}
+
+BusAssignmentManager::BusAssignmentManager()
+ : pState(NULL)
+{
+ pState = new State();
+ Assert(pState);
+}
+
+BusAssignmentManager::~BusAssignmentManager()
+{
+ if (pState)
+ {
+ delete pState;
+ pState = NULL;
+ }
+}
+
+BusAssignmentManager *BusAssignmentManager::createInstance(PCVMMR3VTABLE pVMM, ChipsetType_T chipsetType, IommuType_T iommuType)
+{
+ BusAssignmentManager *pInstance = new BusAssignmentManager();
+ pInstance->pState->init(pVMM, chipsetType, iommuType);
+ Assert(pInstance);
+ return pInstance;
+}
+
+void BusAssignmentManager::AddRef()
+{
+ ASMAtomicIncS32(&pState->cRefCnt);
+}
+
+void BusAssignmentManager::Release()
+{
+ if (ASMAtomicDecS32(&pState->cRefCnt) == 0)
+ delete this;
+}
+
+DECLINLINE(HRESULT) InsertConfigInteger(PCVMMR3VTABLE pVMM, PCFGMNODE pCfg, const char *pszName, uint64_t u64)
+{
+ int vrc = pVMM->pfnCFGMR3InsertInteger(pCfg, pszName, u64);
+ if (RT_FAILURE(vrc))
+ return E_INVALIDARG;
+
+ return S_OK;
+}
+
+DECLINLINE(HRESULT) InsertConfigNode(PCVMMR3VTABLE pVMM, PCFGMNODE pNode, const char *pcszName, PCFGMNODE *ppChild)
+{
+ int vrc = pVMM->pfnCFGMR3InsertNode(pNode, pcszName, ppChild);
+ if (RT_FAILURE(vrc))
+ return E_INVALIDARG;
+
+ return S_OK;
+}
+
+
+HRESULT BusAssignmentManager::assignPCIDeviceImpl(const char *pszDevName,
+ PCFGMNODE pCfg,
+ PCIBusAddress& GuestAddress,
+ PCIBusAddress HostAddress,
+ bool fGuestAddressRequired)
+{
+ HRESULT hrc = S_OK;
+
+ if (!GuestAddress.valid())
+ hrc = pState->autoAssign(pszDevName, GuestAddress);
+ else
+ {
+ bool fAvailable = pState->checkAvailable(GuestAddress);
+
+ if (!fAvailable)
+ {
+ if (fGuestAddressRequired)
+ hrc = E_ACCESSDENIED;
+ else
+ hrc = pState->autoAssign(pszDevName, GuestAddress);
+ }
+ }
+
+ if (FAILED(hrc))
+ return hrc;
+
+ Assert(GuestAddress.valid() && pState->checkAvailable(GuestAddress));
+
+ hrc = pState->record(pszDevName, GuestAddress, HostAddress);
+ if (FAILED(hrc))
+ return hrc;
+
+ PCVMMR3VTABLE const pVMM = pState->mpVMM;
+ if (pCfg)
+ {
+ hrc = InsertConfigInteger(pVMM, pCfg, "PCIBusNo", GuestAddress.miBus);
+ if (FAILED(hrc))
+ return hrc;
+ hrc = InsertConfigInteger(pVMM, pCfg, "PCIDeviceNo", GuestAddress.miDevice);
+ if (FAILED(hrc))
+ return hrc;
+ hrc = InsertConfigInteger(pVMM, pCfg, "PCIFunctionNo", GuestAddress.miFn);
+ if (FAILED(hrc))
+ return hrc;
+ }
+
+ /* Check if the bus is still unknown, i.e. the bridge to it is missing */
+ if ( GuestAddress.miBus > 0
+ && !hasPCIDevice(pState->mpszBridgeName, GuestAddress.miBus - 1))
+ {
+ PCFGMNODE pDevices = pVMM->pfnCFGMR3GetParent(pVMM->pfnCFGMR3GetParent(pCfg));
+ AssertLogRelMsgReturn(pDevices, ("BusAssignmentManager: cannot find base device configuration\n"), E_UNEXPECTED);
+ PCFGMNODE pBridges = pVMM->pfnCFGMR3GetChild(pDevices, "ich9pcibridge");
+ AssertLogRelMsgReturn(pBridges, ("BusAssignmentManager: cannot find bridge configuration base\n"), E_UNEXPECTED);
+
+ /* Device should be on a not yet existing bus, add it automatically */
+ for (int iBridge = 0; iBridge <= GuestAddress.miBus - 1; iBridge++)
+ {
+ if (!hasPCIDevice(pState->mpszBridgeName, iBridge))
+ {
+ PCIBusAddress BridgeGuestAddress;
+ hrc = pState->autoAssign(pState->mpszBridgeName, BridgeGuestAddress);
+ if (FAILED(hrc))
+ return hrc;
+ if (BridgeGuestAddress.miBus > iBridge)
+ AssertLogRelMsgFailedReturn(("BusAssignmentManager: cannot create bridge for bus %i because the possible parent bus positions are exhausted\n", iBridge + 1), E_UNEXPECTED);
+
+ PCFGMNODE pInst;
+ InsertConfigNode(pVMM, pBridges, Utf8StrFmt("%d", iBridge).c_str(), &pInst);
+ InsertConfigInteger(pVMM, pInst, "Trusted", 1);
+ hrc = assignPCIDevice(pState->mpszBridgeName, pInst);
+ if (FAILED(hrc))
+ return hrc;
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+
+bool BusAssignmentManager::findPCIAddress(const char *pszDevName, int iInstance, PCIBusAddress& Address)
+{
+ return pState->findPCIAddress(pszDevName, iInstance, Address);
+}
+void BusAssignmentManager::listAttachedPCIDevices(std::vector<PCIDeviceInfo> &aAttached)
+{
+ pState->listAttachedPCIDevices(aAttached);
+}