VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/BusAssignmentManager.cpp@ 70772

Last change on this file since 70772 was 70238, checked in by vboxsync, 6 years ago

Main/Console+BusAssignmentManager: Move from manual PCI bridge creation to automatic bridge creation on demand (also fix bridge structure to make sure that the correct instantiation order is used). Coding style whitespace fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
RevLine 
[33687]1/* $Id: BusAssignmentManager.cpp 70238 2017-12-20 11:55:05Z vboxsync $ */
2/** @file
3 * VirtualBox bus slots assignment manager
4 */
5
6/*
[69500]7 * Copyright (C) 2010-2017 Oracle Corporation
[33687]8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
[67914]17
18#define LOG_GROUP LOG_GROUP_MAIN
19#include "LoggingNew.h"
20
[33687]21#include "BusAssignmentManager.h"
22
23#include <iprt/asm.h>
[34123]24#include <iprt/string.h>
[33687]25
[35346]26#include <VBox/vmm/cfgm.h>
[34331]27#include <VBox/com/array.h>
[33687]28
29#include <map>
[33690]30#include <vector>
[33722]31#include <algorithm>
[33687]32
[33722]33struct DeviceAssignmentRule
34{
[70238]35 const char *pszName;
[33722]36 int iBus;
37 int iDevice;
38 int iFn;
39 int iPriority;
40};
41
42struct DeviceAliasRule
43{
[70238]44 const char *pszDevName;
45 const char *pszDevAlias;
[33722]46};
47
48/* Those rules define PCI slots assignment */
[68371]49/** @note
50 * The EFI takes assumptions about PCI slot assignments which are different
51 * from the following tables in certain cases, for example the IDE device
52 * is assumed to be 00:01.1! */
[33722]53
[34014]54/* Device Bus Device Function Priority */
[34013]55
[33722]56/* Generic rules */
57static const DeviceAssignmentRule aGenericRules[] =
58{
59 /* VGA controller */
60 {"vga", 0, 2, 0, 0},
61
62 /* VMM device */
63 {"VMMDev", 0, 4, 0, 0},
64
65 /* Audio controllers */
66 {"ichac97", 0, 5, 0, 0},
67 {"hda", 0, 5, 0, 0},
68
69 /* Storage controllers */
[34013]70 {"lsilogic", 0, 20, 0, 1},
71 {"buslogic", 0, 21, 0, 1},
72 {"lsilogicsas", 0, 22, 0, 1},
[57524]73 {"nvme", 0, 14, 0, 1},
[33722]74
75 /* USB controllers */
76 {"usb-ohci", 0, 6, 0, 0},
77 {"usb-ehci", 0, 11, 0, 0},
[50721]78 {"usb-xhci", 0, 12, 0, 0},
[33722]79
80 /* ACPI controller */
81 {"acpi", 0, 7, 0, 0},
82
83 /* Network controllers */
84 /* the first network card gets the PCI ID 3, the next 3 gets 8..10,
[47223]85 * next 4 get 16..19. In "VMWare compatibility" mode the IDs 3 and 17
86 * swap places, i.e. the first card goes to ID 17=0x11. */
[34013]87 {"nic", 0, 3, 0, 1},
88 {"nic", 0, 8, 0, 1},
89 {"nic", 0, 9, 0, 1},
90 {"nic", 0, 10, 0, 1},
91 {"nic", 0, 16, 0, 1},
92 {"nic", 0, 17, 0, 1},
93 {"nic", 0, 18, 0, 1},
94 {"nic", 0, 19, 0, 1},
[33722]95
96 /* ISA/LPC controller */
97 {"lpc", 0, 31, 0, 0},
98
99 { NULL, -1, -1, -1, 0}
100};
101
102/* PIIX3 chipset rules */
103static const DeviceAssignmentRule aPiix3Rules[] =
104{
105 {"piix3ide", 0, 1, 1, 0},
[42826]106 {"ahci", 0, 13, 0, 1},
[33722]107 {"pcibridge", 0, 24, 0, 0},
108 {"pcibridge", 0, 25, 0, 0},
109 { NULL, -1, -1, -1, 0}
110};
111
112
113/* ICH9 chipset rules */
114static const DeviceAssignmentRule aIch9Rules[] =
115{
116 /* Host Controller */
117 {"i82801", 0, 30, 0, 0},
118
119 /* Those are functions of LPC at 00:1e:00 */
120 /**
121 * Please note, that for devices being functions, like we do here, device 0
122 * must be multifunction, i.e. have header type 0x80. Our LPC device is.
123 * Alternative approach is to assign separate slot to each device.
124 */
[34266]125 {"piix3ide", 0, 31, 1, 2},
126 {"ahci", 0, 31, 2, 2},
127 {"smbus", 0, 31, 3, 2},
128 {"usb-ohci", 0, 31, 4, 2},
129 {"usb-ehci", 0, 31, 5, 2},
130 {"thermal", 0, 31, 6, 2},
[33722]131
132 /* to make sure rule never used before rules assigning devices on it */
133 {"ich9pcibridge", 0, 24, 0, 10},
134 {"ich9pcibridge", 0, 25, 0, 10},
[70238]135 {"ich9pcibridge", 2, 24, 0, 9}, /* Bridges must be instantiated depth */
136 {"ich9pcibridge", 2, 25, 0, 9}, /* first (assumption in PDM and other */
137 {"ich9pcibridge", 4, 24, 0, 8}, /* places), so make sure that nested */
138 {"ich9pcibridge", 4, 25, 0, 8}, /* bridges are added to the last bridge */
139 {"ich9pcibridge", 6, 24, 0, 7}, /* only, avoiding the need to re-sort */
140 {"ich9pcibridge", 6, 25, 0, 7}, /* everything before starting the VM. */
141 {"ich9pcibridge", 8, 24, 0, 6},
142 {"ich9pcibridge", 8, 25, 0, 6},
143 {"ich9pcibridge", 10, 24, 0, 5},
144 {"ich9pcibridge", 10, 25, 0, 5},
[33907]145
146 /* Storage controllers */
147 {"ahci", 1, 0, 0, 0},
148 {"ahci", 1, 1, 0, 0},
149 {"ahci", 1, 2, 0, 0},
150 {"ahci", 1, 3, 0, 0},
151 {"ahci", 1, 4, 0, 0},
152 {"ahci", 1, 5, 0, 0},
153 {"ahci", 1, 6, 0, 0},
154 {"lsilogic", 1, 7, 0, 0},
155 {"lsilogic", 1, 8, 0, 0},
156 {"lsilogic", 1, 9, 0, 0},
157 {"lsilogic", 1, 10, 0, 0},
158 {"lsilogic", 1, 11, 0, 0},
159 {"lsilogic", 1, 12, 0, 0},
160 {"lsilogic", 1, 13, 0, 0},
161 {"buslogic", 1, 14, 0, 0},
162 {"buslogic", 1, 15, 0, 0},
163 {"buslogic", 1, 16, 0, 0},
164 {"buslogic", 1, 17, 0, 0},
165 {"buslogic", 1, 18, 0, 0},
166 {"buslogic", 1, 19, 0, 0},
167 {"buslogic", 1, 20, 0, 0},
168 {"lsilogicsas", 1, 21, 0, 0},
169 {"lsilogicsas", 1, 26, 0, 0},
170 {"lsilogicsas", 1, 27, 0, 0},
171 {"lsilogicsas", 1, 28, 0, 0},
172 {"lsilogicsas", 1, 29, 0, 0},
173 {"lsilogicsas", 1, 30, 0, 0},
174 {"lsilogicsas", 1, 31, 0, 0},
175
176 /* NICs */
177 {"nic", 2, 0, 0, 0},
178 {"nic", 2, 1, 0, 0},
179 {"nic", 2, 2, 0, 0},
180 {"nic", 2, 3, 0, 0},
181 {"nic", 2, 4, 0, 0},
182 {"nic", 2, 5, 0, 0},
183 {"nic", 2, 6, 0, 0},
184 {"nic", 2, 7, 0, 0},
185 {"nic", 2, 8, 0, 0},
186 {"nic", 2, 9, 0, 0},
187 {"nic", 2, 10, 0, 0},
188 {"nic", 2, 11, 0, 0},
189 {"nic", 2, 12, 0, 0},
190 {"nic", 2, 13, 0, 0},
191 {"nic", 2, 14, 0, 0},
192 {"nic", 2, 15, 0, 0},
193 {"nic", 2, 16, 0, 0},
194 {"nic", 2, 17, 0, 0},
195 {"nic", 2, 18, 0, 0},
196 {"nic", 2, 19, 0, 0},
197 {"nic", 2, 20, 0, 0},
198 {"nic", 2, 21, 0, 0},
199 {"nic", 2, 26, 0, 0},
200 {"nic", 2, 27, 0, 0},
201 {"nic", 2, 28, 0, 0},
202 {"nic", 2, 29, 0, 0},
203 {"nic", 2, 30, 0, 0},
204 {"nic", 2, 31, 0, 0},
205
[63747]206 /* Storage controller #2 (NVMe) */
207 {"nvme", 3, 0, 0, 0},
208 {"nvme", 3, 1, 0, 0},
209 {"nvme", 3, 2, 0, 0},
210 {"nvme", 3, 3, 0, 0},
211 {"nvme", 3, 4, 0, 0},
212 {"nvme", 3, 5, 0, 0},
213 {"nvme", 3, 6, 0, 0},
214
[33722]215 { NULL, -1, -1, -1, 0}
216};
217
218/* Aliasing rules */
219static const DeviceAliasRule aDeviceAliases[] =
220{
[33907]221 {"e1000", "nic"},
222 {"pcnet", "nic"},
223 {"virtio-net", "nic"},
224 {"ahci", "storage"},
225 {"lsilogic", "storage"},
226 {"buslogic", "storage"},
[57524]227 {"lsilogicsas", "storage"},
228 {"nvme", "storage"}
[33722]229};
230
[33687]231struct BusAssignmentManager::State
232{
[42551]233 struct PCIDeviceRecord
[33687]234 {
[36107]235 char szDevName[32];
[42551]236 PCIBusAddress HostAddress;
[33688]237
[70238]238 PCIDeviceRecord(const char *pszName, PCIBusAddress aHostAddress)
[36107]239 {
240 RTStrCopy(this->szDevName, sizeof(szDevName), pszName);
241 this->HostAddress = aHostAddress;
242 }
243
[70238]244 PCIDeviceRecord(const char *pszName)
[33688]245 {
[36107]246 RTStrCopy(this->szDevName, sizeof(szDevName), pszName);
[33688]247 }
[33690]248
[42551]249 bool operator<(const PCIDeviceRecord &a) const
[33690]250 {
[34123]251 return RTStrNCmp(szDevName, a.szDevName, sizeof(szDevName)) < 0;
[33690]252 }
253
[42551]254 bool operator==(const PCIDeviceRecord &a) const
[33690]255 {
[34123]256 return RTStrNCmp(szDevName, a.szDevName, sizeof(szDevName)) == 0;
[33690]257 }
[33687]258 };
259
[61009]260 typedef std::map<PCIBusAddress,PCIDeviceRecord> PCIMap;
[42551]261 typedef std::vector<PCIBusAddress> PCIAddrList;
[61009]262 typedef std::vector<const DeviceAssignmentRule *> PCIRulesList;
263 typedef std::map<PCIDeviceRecord,PCIAddrList> ReversePCIMap;
[33687]264
265 volatile int32_t cRefCnt;
266 ChipsetType_T mChipsetType;
[70238]267 const char * mpszBridgeName;
[42551]268 PCIMap mPCIMap;
269 ReversePCIMap mReversePCIMap;
[33687]270
271 State()
[70238]272 : cRefCnt(1), mChipsetType(ChipsetType_Null), mpszBridgeName("unknownbridge")
[33687]273 {}
274 ~State()
275 {}
276
277 HRESULT init(ChipsetType_T chipsetType);
278
[70238]279 HRESULT record(const char *pszName, PCIBusAddress& GuestAddress, PCIBusAddress HostAddress);
280 HRESULT autoAssign(const char *pszName, PCIBusAddress& Address);
[42551]281 bool checkAvailable(PCIBusAddress& Address);
[70238]282 bool findPCIAddress(const char *pszDevName, int iInstance, PCIBusAddress& Address);
[33722]283
[70238]284 const char *findAlias(const char *pszName);
285 void addMatchingRules(const char *pszName, PCIRulesList& aList);
[61009]286 void listAttachedPCIDevices(std::vector<PCIDeviceInfo> &aAttached);
[33687]287};
288
289HRESULT BusAssignmentManager::State::init(ChipsetType_T chipsetType)
290{
291 mChipsetType = chipsetType;
[70238]292 switch (chipsetType)
293 {
294 case ChipsetType_PIIX3:
295 mpszBridgeName = "pcibridge";
296 break;
297 case ChipsetType_ICH9:
298 mpszBridgeName = "ich9pcibridge";
299 break;
300 default:
301 mpszBridgeName = "unknownbridge";
302 AssertFailed();
303 break;
304 }
[33687]305 return S_OK;
306}
307
[70238]308HRESULT BusAssignmentManager::State::record(const char *pszName, PCIBusAddress& Address, PCIBusAddress HostAddress)
[33688]309{
[42551]310 PCIDeviceRecord devRec(pszName, HostAddress);
[33690]311
312 /* Remember address -> device mapping */
[42551]313 mPCIMap.insert(PCIMap::value_type(Address, devRec));
[33690]314
[42551]315 ReversePCIMap::iterator it = mReversePCIMap.find(devRec);
316 if (it == mReversePCIMap.end())
[33690]317 {
[42551]318 mReversePCIMap.insert(ReversePCIMap::value_type(devRec, PCIAddrList()));
319 it = mReversePCIMap.find(devRec);
[33690]320 }
321
322 /* Remember device name -> addresses mapping */
323 it->second.push_back(Address);
324
[33688]325 return S_OK;
326}
327
[70238]328bool BusAssignmentManager::State::findPCIAddress(const char *pszDevName, int iInstance, PCIBusAddress& Address)
[33690]329{
[42551]330 PCIDeviceRecord devRec(pszDevName);
[33690]331
[42551]332 ReversePCIMap::iterator it = mReversePCIMap.find(devRec);
333 if (it == mReversePCIMap.end())
[33690]334 return false;
335
336 if (iInstance >= (int)it->second.size())
337 return false;
338
339 Address = it->second[iInstance];
340 return true;
341}
342
[70238]343void BusAssignmentManager::State::addMatchingRules(const char *pszName, PCIRulesList& aList)
[33722]344{
345 size_t iRuleset, iRule;
[70238]346 const DeviceAssignmentRule *aArrays[2] = {aGenericRules, NULL};
[33722]347
348 switch (mChipsetType)
349 {
350 case ChipsetType_PIIX3:
351 aArrays[1] = aPiix3Rules;
352 break;
353 case ChipsetType_ICH9:
354 aArrays[1] = aIch9Rules;
355 break;
356 default:
[70238]357 AssertFailed();
[33722]358 break;
359 }
360
361 for (iRuleset = 0; iRuleset < RT_ELEMENTS(aArrays); iRuleset++)
362 {
363 if (aArrays[iRuleset] == NULL)
364 continue;
365
366 for (iRule = 0; aArrays[iRuleset][iRule].pszName != NULL; iRule++)
367 {
[34123]368 if (RTStrCmp(pszName, aArrays[iRuleset][iRule].pszName) == 0)
[33722]369 aList.push_back(&aArrays[iRuleset][iRule]);
370 }
371 }
372}
373
[70238]374const char *BusAssignmentManager::State::findAlias(const char *pszDev)
[33722]375{
376 for (size_t iAlias = 0; iAlias < RT_ELEMENTS(aDeviceAliases); iAlias++)
377 {
378 if (strcmp(pszDev, aDeviceAliases[iAlias].pszDevName) == 0)
379 return aDeviceAliases[iAlias].pszDevAlias;
380 }
381 return NULL;
382}
383
[70238]384static bool RuleComparator(const DeviceAssignmentRule *r1, const DeviceAssignmentRule *r2)
[33722]385{
386 return (r1->iPriority > r2->iPriority);
387}
388
[70238]389HRESULT BusAssignmentManager::State::autoAssign(const char *pszName, PCIBusAddress& Address)
[33687]390{
[42551]391 PCIRulesList matchingRules;
[33722]392
393 addMatchingRules(pszName, matchingRules);
[70238]394 const char *pszAlias = findAlias(pszName);
[33722]395 if (pszAlias)
396 addMatchingRules(pszAlias, matchingRules);
397
398 AssertMsg(matchingRules.size() > 0, ("No rule for %s(%s)\n", pszName, pszAlias));
399
[34266]400 stable_sort(matchingRules.begin(), matchingRules.end(), RuleComparator);
[33722]401
402 for (size_t iRule = 0; iRule < matchingRules.size(); iRule++)
403 {
[70238]404 const DeviceAssignmentRule *rule = matchingRules[iRule];
[33722]405
[36630]406 Address.miBus = rule->iBus;
407 Address.miDevice = rule->iDevice;
408 Address.miFn = rule->iFn;
[33722]409
410 if (checkAvailable(Address))
411 return S_OK;
412 }
[70238]413 AssertLogRelMsgFailed(("BusAssignmentManager: All possible candidate positions for %s exhausted\n", pszName));
[33722]414
415 return E_INVALIDARG;
[33687]416}
417
[42551]418bool BusAssignmentManager::State::checkAvailable(PCIBusAddress& Address)
[33687]419{
[42551]420 PCIMap::const_iterator it = mPCIMap.find(Address);
[33688]421
[42551]422 return (it == mPCIMap.end());
[33687]423}
424
[61009]425void BusAssignmentManager::State::listAttachedPCIDevices(std::vector<PCIDeviceInfo> &aAttached)
[34331]426{
[51612]427 aAttached.resize(mPCIMap.size());
[34331]428
[51612]429 size_t i = 0;
[61009]430 PCIDeviceInfo dev;
[51612]431 for (PCIMap::const_iterator it = mPCIMap.begin(); it != mPCIMap.end(); ++it, ++i)
[34331]432 {
[61009]433 dev.strDeviceName = it->second.szDevName;
434 dev.guestAddress = it->first;
435 dev.hostAddress = it->second.HostAddress;
436 aAttached[i] = dev;
[34331]437 }
438}
439
[33687]440BusAssignmentManager::BusAssignmentManager()
441 : pState(NULL)
442{
443 pState = new State();
444 Assert(pState);
445}
446
447BusAssignmentManager::~BusAssignmentManager()
448{
449 if (pState)
450 {
451 delete pState;
452 pState = NULL;
453 }
454}
455
[70238]456BusAssignmentManager *BusAssignmentManager::createInstance(ChipsetType_T chipsetType)
[33687]457{
[70238]458 BusAssignmentManager *pInstance = new BusAssignmentManager();
[34331]459 pInstance->pState->init(chipsetType);
460 Assert(pInstance);
[33687]461 return pInstance;
462}
463
464void BusAssignmentManager::AddRef()
465{
466 ASMAtomicIncS32(&pState->cRefCnt);
467}
468void BusAssignmentManager::Release()
469{
[34044]470 if (ASMAtomicDecS32(&pState->cRefCnt) == 0)
[33687]471 delete this;
472}
473
[70238]474DECLINLINE(HRESULT) InsertConfigInteger(PCFGMNODE pCfg, const char *pszName, uint64_t u64)
[33687]475{
476 int vrc = CFGMR3InsertInteger(pCfg, pszName, u64);
477 if (RT_FAILURE(vrc))
478 return E_INVALIDARG;
479
480 return S_OK;
481}
482
[70238]483DECLINLINE(HRESULT) InsertConfigNode(PCFGMNODE pNode, const char *pcszName, PCFGMNODE *ppChild)
484{
485 int vrc = CFGMR3InsertNode(pNode, pcszName, ppChild);
486 if (RT_FAILURE(vrc))
487 return E_INVALIDARG;
488
489 return S_OK;
490}
491
492
493HRESULT BusAssignmentManager::assignPCIDeviceImpl(const char *pszDevName,
[36107]494 PCFGMNODE pCfg,
[42551]495 PCIBusAddress& GuestAddress,
496 PCIBusAddress HostAddress,
[36107]497 bool fGuestAddressRequired)
[33687]498{
499 HRESULT rc = S_OK;
500
[36107]501 if (!GuestAddress.valid())
502 rc = pState->autoAssign(pszDevName, GuestAddress);
[33687]503 else
504 {
[36107]505 bool fAvailable = pState->checkAvailable(GuestAddress);
[33687]506
507 if (!fAvailable)
508 {
[36107]509 if (fGuestAddressRequired)
[33964]510 rc = E_ACCESSDENIED;
[33687]511 else
[36107]512 rc = pState->autoAssign(pszDevName, GuestAddress);
[33687]513 }
514 }
515
516 if (FAILED(rc))
517 return rc;
518
[36107]519 Assert(GuestAddress.valid() && pState->checkAvailable(GuestAddress));
[33687]520
[36107]521 rc = pState->record(pszDevName, GuestAddress, HostAddress);
[33688]522 if (FAILED(rc))
523 return rc;
524
[36630]525 rc = InsertConfigInteger(pCfg, "PCIBusNo", GuestAddress.miBus);
[33687]526 if (FAILED(rc))
527 return rc;
[36630]528 rc = InsertConfigInteger(pCfg, "PCIDeviceNo", GuestAddress.miDevice);
[33687]529 if (FAILED(rc))
530 return rc;
[36630]531 rc = InsertConfigInteger(pCfg, "PCIFunctionNo", GuestAddress.miFn);
[33687]532 if (FAILED(rc))
533 return rc;
534
[70238]535 /* Check if the bus is still unknown, i.e. the bridge to it is missing */
536 if ( GuestAddress.miBus > 0
537 && !hasPCIDevice(pState->mpszBridgeName, GuestAddress.miBus - 1))
538 {
539 PCFGMNODE pDevices = CFGMR3GetParent(CFGMR3GetParent(pCfg));
540 AssertLogRelMsgReturn(pDevices, ("BusAssignmentManager: cannot find base device configuration\n"), E_UNEXPECTED);
541 PCFGMNODE pBridges = CFGMR3GetChild(pDevices, "ich9pcibridge");
542 AssertLogRelMsgReturn(pBridges, ("BusAssignmentManager: cannot find bridge configuration base\n"), E_UNEXPECTED);
543
544 /* Device should be on a not yet existing bus, add it automatically */
545 for (int iBridge = 0; iBridge <= GuestAddress.miBus - 1; iBridge++)
546 {
547 if (!hasPCIDevice(pState->mpszBridgeName, iBridge))
548 {
549 PCIBusAddress BridgeGuestAddress;
550 rc = pState->autoAssign(pState->mpszBridgeName, BridgeGuestAddress);
551 if (FAILED(rc))
552 return rc;
553 if (BridgeGuestAddress.miBus > iBridge)
554 AssertLogRelMsgFailedReturn(("BusAssignmentManager: cannot create bridge for bus %i because the possible parent bus positions are exhausted\n", iBridge + 1), E_UNEXPECTED);
555
556 PCFGMNODE pInst;
557 InsertConfigNode(pBridges, Utf8StrFmt("%d", iBridge).c_str(), &pInst);
558 InsertConfigInteger(pInst, "Trusted", 1);
559 rc = assignPCIDevice(pState->mpszBridgeName, pInst);
560 if (FAILED(rc))
561 return rc;
562 }
563 }
564 }
565
[33687]566 return S_OK;
567}
[33690]568
569
[70238]570bool BusAssignmentManager::findPCIAddress(const char *pszDevName, int iInstance, PCIBusAddress& Address)
[33690]571{
[42551]572 return pState->findPCIAddress(pszDevName, iInstance, Address);
[33690]573}
[61009]574void BusAssignmentManager::listAttachedPCIDevices(std::vector<PCIDeviceInfo> &aAttached)
[34331]575{
[51612]576 pState->listAttachedPCIDevices(aAttached);
[34331]577}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use