VirtualBox

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

Last change on this file since 79133 was 78509, checked in by vboxsync, 6 years ago

Main/Machine+StorageController+SystemProperties+Console: Add basic support for virtio-scsi storage controller.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette