VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp@ 94070

Last change on this file since 94070 was 94070, checked in by vboxsync, 2 years ago

CloudNet: ​bugref:9469 Dropped local gateway parameters in VBoxManage cloud network setup, added support for cloud network attachment in VBoxManage modifyvm, updated help for both, bug fixes and minor improvements in error reporting.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 285.9 KB
Line 
1/* $Id: ConsoleImpl2.cpp 94070 2022-03-03 11:54:06Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - VM Configuration Bits.
4 *
5 * @remark We've split out the code that the 64-bit VC++ v8 compiler finds
6 * problematic to optimize so we can disable optimizations and later,
7 * perhaps, find a real solution for it (like rewriting the code and
8 * to stop resemble a tonne of spaghetti).
9 */
10
11/*
12 * Copyright (C) 2006-2022 Oracle Corporation
13 *
14 * This file is part of VirtualBox Open Source Edition (OSE), as
15 * available from http://www.virtualbox.org. This file is free software;
16 * you can redistribute it and/or modify it under the terms of the GNU
17 * General Public License (GPL) as published by the Free Software
18 * Foundation, in version 2 as it comes in the "COPYING" file of the
19 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
20 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
21 */
22
23
24/*********************************************************************************************************************************
25* Header Files *
26*********************************************************************************************************************************/
27#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
28#include "LoggingNew.h"
29
30// VBoxNetCfg-win.h needs winsock2.h and thus MUST be included before any other
31// header file includes Windows.h.
32#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
33# include <VBox/VBoxNetCfg-win.h>
34#endif
35
36#include "ConsoleImpl.h"
37#include "DisplayImpl.h"
38#include "NvramStoreImpl.h"
39#ifdef VBOX_WITH_DRAG_AND_DROP
40# include "GuestImpl.h"
41# include "GuestDnDPrivate.h"
42#endif
43#include "VMMDev.h"
44#include "Global.h"
45#ifdef VBOX_WITH_PCI_PASSTHROUGH
46# include "PCIRawDevImpl.h"
47#endif
48
49// generated header
50#include "SchemaDefs.h"
51
52#include "AutoCaller.h"
53
54#include <iprt/base64.h>
55#include <iprt/buildconfig.h>
56#include <iprt/ctype.h>
57#include <iprt/dir.h>
58#include <iprt/file.h>
59#include <iprt/param.h>
60#include <iprt/path.h>
61#include <iprt/string.h>
62#include <iprt/system.h>
63#include <iprt/cpp/exception.h>
64#if 0 /* enable to play with lots of memory. */
65# include <iprt/env.h>
66#endif
67#include <iprt/stream.h>
68
69#include <VBox/vmm/vmmr3vtable.h>
70#include <VBox/vmm/vmapi.h>
71#include <VBox/err.h>
72#include <VBox/param.h>
73#include <VBox/vmm/pdmapi.h> /* For PDMR3DriverAttach/PDMR3DriverDetach. */
74#include <VBox/vmm/pdmusb.h> /* For PDMR3UsbCreateEmulatedDevice. */
75#include <VBox/vmm/pdmdev.h> /* For PDMAPICMODE enum. */
76#include <VBox/vmm/pdmstorageifs.h>
77#include <VBox/version.h>
78#ifdef VBOX_WITH_SHARED_CLIPBOARD
79# include <VBox/HostServices/VBoxClipboardSvc.h>
80#endif
81#ifdef VBOX_WITH_GUEST_PROPS
82# include <VBox/HostServices/GuestPropertySvc.h>
83# include <VBox/com/defs.h>
84# include <VBox/com/array.h>
85# include <vector>
86#endif /* VBOX_WITH_GUEST_PROPS */
87#include <VBox/intnet.h>
88
89#include <VBox/com/com.h>
90#include <VBox/com/string.h>
91#include <VBox/com/array.h>
92
93#ifdef VBOX_WITH_NETFLT
94# if defined(RT_OS_SOLARIS)
95# include <zone.h>
96# elif defined(RT_OS_LINUX)
97# include <unistd.h>
98# include <sys/ioctl.h>
99# include <sys/socket.h>
100# include <linux/types.h>
101# include <linux/if.h>
102# elif defined(RT_OS_FREEBSD)
103# include <unistd.h>
104# include <sys/types.h>
105# include <sys/ioctl.h>
106# include <sys/socket.h>
107# include <net/if.h>
108# include <net80211/ieee80211_ioctl.h>
109# endif
110# if defined(RT_OS_WINDOWS)
111# include <iprt/win/ntddndis.h>
112# include <devguid.h>
113# else
114# include <HostNetworkInterfaceImpl.h>
115# include <netif.h>
116# include <stdlib.h>
117# endif
118#endif /* VBOX_WITH_NETFLT */
119
120#ifdef VBOX_WITH_AUDIO_VRDE
121# include "DrvAudioVRDE.h"
122#endif
123#ifdef VBOX_WITH_AUDIO_RECORDING
124# include "DrvAudioRec.h"
125#endif
126#include "NetworkServiceRunner.h"
127#include "BusAssignmentManager.h"
128#ifdef VBOX_WITH_EXTPACK
129# include "ExtPackManagerImpl.h"
130#endif
131
132
133/*********************************************************************************************************************************
134* Internal Functions *
135*********************************************************************************************************************************/
136static Utf8Str *GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue);
137
138
139/* Darwin compile kludge */
140#undef PVM
141
142/* Comment out the following line to remove VMWare compatibility hack. */
143#define VMWARE_NET_IN_SLOT_11
144
145/**
146 * Translate IDE StorageControllerType_T to string representation.
147 */
148const char* controllerString(StorageControllerType_T enmType)
149{
150 switch (enmType)
151 {
152 case StorageControllerType_PIIX3:
153 return "PIIX3";
154 case StorageControllerType_PIIX4:
155 return "PIIX4";
156 case StorageControllerType_ICH6:
157 return "ICH6";
158 default:
159 return "Unknown";
160 }
161}
162
163/**
164 * Simple class for storing network boot information.
165 */
166struct BootNic
167{
168 ULONG mInstance;
169 PCIBusAddress mPCIAddress;
170
171 ULONG mBootPrio;
172 bool operator < (const BootNic &rhs) const
173 {
174 ULONG lval = mBootPrio - 1; /* 0 will wrap around and get the lowest priority. */
175 ULONG rval = rhs.mBootPrio - 1;
176 return lval < rval; /* Zero compares as highest number (lowest prio). */
177 }
178};
179
180#ifndef VBOX_WITH_EFI_IN_DD2
181static int findEfiRom(IVirtualBox* vbox, FirmwareType_T aFirmwareType, Utf8Str *pEfiRomFile)
182{
183 Bstr aFilePath, empty;
184 BOOL fPresent = FALSE;
185 HRESULT hrc = vbox->CheckFirmwarePresent(aFirmwareType, empty.raw(),
186 empty.asOutParam(), aFilePath.asOutParam(), &fPresent);
187 AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc));
188
189 if (!fPresent)
190 {
191 LogRel(("Failed to find an EFI ROM file.\n"));
192 return VERR_FILE_NOT_FOUND;
193 }
194
195 *pEfiRomFile = Utf8Str(aFilePath);
196
197 return VINF_SUCCESS;
198}
199#endif
200
201/**
202 * @throws HRESULT on extra data retrival error.
203 */
204static int getSmcDeviceKey(IVirtualBox *pVirtualBox, IMachine *pMachine, Utf8Str *pStrKey, bool *pfGetKeyFromRealSMC)
205{
206 *pfGetKeyFromRealSMC = false;
207
208 /*
209 * The extra data takes precedence (if non-zero).
210 */
211 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/SmcDeviceKey", pStrKey);
212 if (pStrKey->isNotEmpty())
213 return VINF_SUCCESS;
214
215#ifdef RT_OS_DARWIN
216
217 /*
218 * Work done in EFI/DevSmc
219 */
220 *pfGetKeyFromRealSMC = true;
221 int rc = VINF_SUCCESS;
222
223#else
224 /*
225 * Is it apple hardware in bootcamp?
226 */
227 /** @todo implement + test RTSYSDMISTR_MANUFACTURER on all hosts.
228 * Currently falling back on the product name. */
229 char szManufacturer[256];
230 szManufacturer[0] = '\0';
231 RTSystemQueryDmiString(RTSYSDMISTR_MANUFACTURER, szManufacturer, sizeof(szManufacturer));
232 if (szManufacturer[0] != '\0')
233 {
234 if ( !strcmp(szManufacturer, "Apple Computer, Inc.")
235 || !strcmp(szManufacturer, "Apple Inc.")
236 )
237 *pfGetKeyFromRealSMC = true;
238 }
239 else
240 {
241 char szProdName[256];
242 szProdName[0] = '\0';
243 RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szProdName, sizeof(szProdName));
244 if ( ( !strncmp(szProdName, RT_STR_TUPLE("Mac"))
245 || !strncmp(szProdName, RT_STR_TUPLE("iMac"))
246 || !strncmp(szProdName, RT_STR_TUPLE("Xserve"))
247 )
248 && !strchr(szProdName, ' ') /* no spaces */
249 && RT_C_IS_DIGIT(szProdName[strlen(szProdName) - 1]) /* version number */
250 )
251 *pfGetKeyFromRealSMC = true;
252 }
253
254 int rc = VINF_SUCCESS;
255#endif
256
257 return rc;
258}
259
260
261/*
262 * VC++ 8 / amd64 has some serious trouble with the next functions.
263 * As a temporary measure, we'll drop global optimizations.
264 */
265#if defined(_MSC_VER) && defined(RT_ARCH_AMD64)
266# if _MSC_VER >= RT_MSC_VER_VC80 && _MSC_VER < RT_MSC_VER_VC100
267# pragma optimize("g", off)
268# endif
269#endif
270
271class ConfigError : public RTCError
272{
273public:
274
275 ConfigError(const char *pcszFunction,
276 int vrc,
277 const char *pcszName)
278 : RTCError(Utf8StrFmt(Console::tr("%s failed: rc=%Rrc, pcszName=%s"), pcszFunction, vrc, pcszName)),
279 m_vrc(vrc)
280 {
281 AssertMsgFailed(("%s\n", what())); // in strict mode, hit a breakpoint here
282 }
283
284 int m_vrc;
285};
286
287
288/**
289 * Helper that calls CFGMR3InsertString and throws an RTCError if that
290 * fails (C-string variant).
291 * @param pNode See CFGMR3InsertStringN.
292 * @param pcszName See CFGMR3InsertStringN.
293 * @param pcszValue The string value.
294 */
295void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const char *pcszValue)
296{
297 int vrc = mpVMM->pfnCFGMR3InsertString(pNode, pcszName, pcszValue);
298 if (RT_FAILURE(vrc))
299 throw ConfigError("CFGMR3InsertString", vrc, pcszName);
300}
301
302/**
303 * Helper that calls CFGMR3InsertString and throws an RTCError if that
304 * fails (Utf8Str variant).
305 * @param pNode See CFGMR3InsertStringN.
306 * @param pcszName See CFGMR3InsertStringN.
307 * @param rStrValue The string value.
308 */
309void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue)
310{
311 int vrc = mpVMM->pfnCFGMR3InsertStringN(pNode, pcszName, rStrValue.c_str(), rStrValue.length());
312 if (RT_FAILURE(vrc))
313 throw ConfigError("CFGMR3InsertStringLengthKnown", vrc, pcszName);
314}
315
316/**
317 * Helper that calls CFGMR3InsertString and throws an RTCError if that
318 * fails (Bstr variant).
319 *
320 * @param pNode See CFGMR3InsertStringN.
321 * @param pcszName See CFGMR3InsertStringN.
322 * @param rBstrValue The string value.
323 */
324void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Bstr &rBstrValue)
325{
326 InsertConfigString(pNode, pcszName, Utf8Str(rBstrValue));
327}
328
329/**
330 * Helper that calls CFGMR3InsertPassword and throws an RTCError if that
331 * fails (Utf8Str variant).
332 * @param pNode See CFGMR3InsertPasswordN.
333 * @param pcszName See CFGMR3InsertPasswordN.
334 * @param rStrValue The string value.
335 */
336void Console::InsertConfigPassword(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue)
337{
338 int vrc = mpVMM->pfnCFGMR3InsertPasswordN(pNode, pcszName, rStrValue.c_str(), rStrValue.length());
339 if (RT_FAILURE(vrc))
340 throw ConfigError("CFGMR3InsertPasswordLengthKnown", vrc, pcszName);
341}
342
343/**
344 * Helper that calls CFGMR3InsertBytes and throws an RTCError if that fails.
345 *
346 * @param pNode See CFGMR3InsertBytes.
347 * @param pcszName See CFGMR3InsertBytes.
348 * @param pvBytes See CFGMR3InsertBytes.
349 * @param cbBytes See CFGMR3InsertBytes.
350 */
351void Console::InsertConfigBytes(PCFGMNODE pNode, const char *pcszName, const void *pvBytes, size_t cbBytes)
352{
353 int vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pcszName, pvBytes, cbBytes);
354 if (RT_FAILURE(vrc))
355 throw ConfigError("CFGMR3InsertBytes", vrc, pcszName);
356}
357
358/**
359 * Helper that calls CFGMR3InsertInteger and throws an RTCError if that
360 * fails.
361 *
362 * @param pNode See CFGMR3InsertInteger.
363 * @param pcszName See CFGMR3InsertInteger.
364 * @param u64Integer See CFGMR3InsertInteger.
365 */
366void Console::InsertConfigInteger(PCFGMNODE pNode, const char *pcszName, uint64_t u64Integer)
367{
368 int vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pcszName, u64Integer);
369 if (RT_FAILURE(vrc))
370 throw ConfigError("CFGMR3InsertInteger", vrc, pcszName);
371}
372
373/**
374 * Helper that calls CFGMR3InsertNode and throws an RTCError if that fails.
375 *
376 * @param pNode See CFGMR3InsertNode.
377 * @param pcszName See CFGMR3InsertNode.
378 * @param ppChild See CFGMR3InsertNode.
379 */
380void Console::InsertConfigNode(PCFGMNODE pNode, const char *pcszName, PCFGMNODE *ppChild)
381{
382 int vrc = mpVMM->pfnCFGMR3InsertNode(pNode, pcszName, ppChild);
383 if (RT_FAILURE(vrc))
384 throw ConfigError("CFGMR3InsertNode", vrc, pcszName);
385}
386
387/**
388 * Helper that calls CFGMR3InsertNodeF and throws an RTCError if that fails.
389 *
390 * @param pNode See CFGMR3InsertNodeF.
391 * @param ppChild See CFGMR3InsertNodeF.
392 * @param pszNameFormat Name format string, see CFGMR3InsertNodeF.
393 * @param ... Format arguments.
394 */
395void Console::InsertConfigNodeF(PCFGMNODE pNode, PCFGMNODE *ppChild, const char *pszNameFormat, ...)
396{
397 va_list va;
398 va_start(va, pszNameFormat);
399 int vrc = mpVMM->pfnCFGMR3InsertNodeF(pNode, ppChild, "%N", pszNameFormat, &va);
400 va_end(va);
401 if (RT_FAILURE(vrc))
402 throw ConfigError("CFGMR3InsertNodeF", vrc, pszNameFormat);
403}
404
405/**
406 * Helper that calls CFGMR3RemoveValue and throws an RTCError if that fails.
407 *
408 * @param pNode See CFGMR3RemoveValue.
409 * @param pcszName See CFGMR3RemoveValue.
410 */
411void Console::RemoveConfigValue(PCFGMNODE pNode, const char *pcszName)
412{
413 int vrc = mpVMM->pfnCFGMR3RemoveValue(pNode, pcszName);
414 if (RT_FAILURE(vrc))
415 throw ConfigError("CFGMR3RemoveValue", vrc, pcszName);
416}
417
418/**
419 * Gets an extra data value, consulting both machine and global extra data.
420 *
421 * @throws HRESULT on failure
422 * @returns pStrValue for the callers convenience.
423 * @param pVirtualBox Pointer to the IVirtualBox interface.
424 * @param pMachine Pointer to the IMachine interface.
425 * @param pszName The value to get.
426 * @param pStrValue Where to return it's value (empty string if not
427 * found).
428 */
429static Utf8Str *GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue)
430{
431 pStrValue->setNull();
432
433 Bstr bstrName(pszName);
434 Bstr bstrValue;
435 HRESULT hrc = pMachine->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
436 if (FAILED(hrc))
437 throw hrc;
438 if (bstrValue.isEmpty())
439 {
440 hrc = pVirtualBox->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
441 if (FAILED(hrc))
442 throw hrc;
443 }
444
445 if (bstrValue.isNotEmpty())
446 *pStrValue = bstrValue;
447 return pStrValue;
448}
449
450
451/** Helper that finds out the next HBA port used
452 */
453static LONG GetNextUsedPort(LONG aPortUsed[30], LONG lBaseVal, uint32_t u32Size)
454{
455 LONG lNextPortUsed = 30;
456 for (size_t j = 0; j < u32Size; ++j)
457 {
458 if ( aPortUsed[j] > lBaseVal
459 && aPortUsed[j] <= lNextPortUsed)
460 lNextPortUsed = aPortUsed[j];
461 }
462 return lNextPortUsed;
463}
464
465#define MAX_BIOS_LUN_COUNT 4
466
467int Console::SetBiosDiskInfo(ComPtr<IMachine> pMachine, PCFGMNODE pCfg, PCFGMNODE pBiosCfg,
468 Bstr controllerName, const char * const s_apszBiosConfig[4])
469{
470 RT_NOREF(pCfg);
471 HRESULT hrc;
472#define MAX_DEVICES 30
473#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
474
475 LONG lPortLUN[MAX_BIOS_LUN_COUNT];
476 LONG lPortUsed[MAX_DEVICES];
477 uint32_t u32HDCount = 0;
478
479 /* init to max value */
480 lPortLUN[0] = MAX_DEVICES;
481
482 com::SafeIfaceArray<IMediumAttachment> atts;
483 hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(),
484 ComSafeArrayAsOutParam(atts)); H();
485 size_t uNumAttachments = atts.size();
486 if (uNumAttachments > MAX_DEVICES)
487 {
488 LogRel(("Number of Attachments > Max=%d.\n", uNumAttachments));
489 uNumAttachments = MAX_DEVICES;
490 }
491
492 /* Find the relevant ports/IDs, i.e the ones to which a HD is attached. */
493 for (size_t j = 0; j < uNumAttachments; ++j)
494 {
495 IMediumAttachment *pMediumAtt = atts[j];
496 LONG lPortNum = 0;
497 hrc = pMediumAtt->COMGETTER(Port)(&lPortNum); H();
498 if (SUCCEEDED(hrc))
499 {
500 DeviceType_T lType;
501 hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
502 if (SUCCEEDED(hrc) && lType == DeviceType_HardDisk)
503 {
504 /* find min port number used for HD */
505 if (lPortNum < lPortLUN[0])
506 lPortLUN[0] = lPortNum;
507 lPortUsed[u32HDCount++] = lPortNum;
508 LogFlowFunc(("HD port Count=%d\n", u32HDCount));
509 }
510 }
511 }
512
513
514 /* Pick only the top 4 used HD Ports as CMOS doesn't have space
515 * to save details for all 30 ports
516 */
517 uint32_t u32MaxPortCount = MAX_BIOS_LUN_COUNT;
518 if (u32HDCount < MAX_BIOS_LUN_COUNT)
519 u32MaxPortCount = u32HDCount;
520 for (size_t j = 1; j < u32MaxPortCount; j++)
521 lPortLUN[j] = GetNextUsedPort(lPortUsed, lPortLUN[j-1], u32HDCount);
522 if (pBiosCfg)
523 {
524 for (size_t j = 0; j < u32MaxPortCount; j++)
525 {
526 InsertConfigInteger(pBiosCfg, s_apszBiosConfig[j], lPortLUN[j]);
527 LogFlowFunc(("Top %d HBA ports = %s, %d\n", j, s_apszBiosConfig[j], lPortLUN[j]));
528 }
529 }
530 return VINF_SUCCESS;
531}
532
533#ifdef VBOX_WITH_PCI_PASSTHROUGH
534HRESULT Console::i_attachRawPCIDevices(PUVM pUVM, BusAssignmentManager *pBusMgr, PCFGMNODE pDevices)
535{
536# ifndef VBOX_WITH_EXTPACK
537 RT_NOREF(pUVM);
538# endif
539 HRESULT hrc = S_OK;
540 PCFGMNODE pInst, pCfg, pLunL0, pLunL1;
541
542 SafeIfaceArray<IPCIDeviceAttachment> assignments;
543 ComPtr<IMachine> aMachine = i_machine();
544
545 hrc = aMachine->COMGETTER(PCIDeviceAssignments)(ComSafeArrayAsOutParam(assignments));
546 if ( hrc != S_OK
547 || assignments.size() < 1)
548 return hrc;
549
550 /*
551 * PCI passthrough is only available if the proper ExtPack is installed.
552 *
553 * Note. Configuring PCI passthrough here and providing messages about
554 * the missing extpack isn't exactly clean, but it is a necessary evil
555 * to patch over legacy compatability issues introduced by the new
556 * distribution model.
557 */
558# ifdef VBOX_WITH_EXTPACK
559 static const char *s_pszPCIRawExtPackName = "Oracle VM VirtualBox Extension Pack";
560 if (!mptrExtPackManager->i_isExtPackUsable(s_pszPCIRawExtPackName))
561 /* Always fatal! */
562 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
563 N_("Implementation of the PCI passthrough framework not found!\n"
564 "The VM cannot be started. To fix this problem, either "
565 "install the '%s' or disable PCI passthrough via VBoxManage"),
566 s_pszPCIRawExtPackName);
567# endif
568
569 /* Now actually add devices */
570 PCFGMNODE pPCIDevs = NULL;
571
572 if (assignments.size() > 0)
573 {
574 InsertConfigNode(pDevices, "pciraw", &pPCIDevs);
575
576 PCFGMNODE pRoot = CFGMR3GetParent(pDevices); Assert(pRoot);
577
578 /* Tell PGM to tell GPCIRaw about guest mappings. */
579 CFGMR3InsertNode(pRoot, "PGM", NULL);
580 InsertConfigInteger(CFGMR3GetChild(pRoot, "PGM"), "PciPassThrough", 1);
581
582 /*
583 * Currently, using IOMMU needed for PCI passthrough
584 * requires RAM preallocation.
585 */
586 /** @todo check if we can lift this requirement */
587 CFGMR3RemoveValue(pRoot, "RamPreAlloc");
588 InsertConfigInteger(pRoot, "RamPreAlloc", 1);
589 }
590
591 for (size_t iDev = 0; iDev < assignments.size(); iDev++)
592 {
593 PCIBusAddress HostPCIAddress, GuestPCIAddress;
594 ComPtr<IPCIDeviceAttachment> assignment = assignments[iDev];
595 LONG host, guest;
596 Bstr aDevName;
597
598 hrc = assignment->COMGETTER(HostAddress)(&host); H();
599 hrc = assignment->COMGETTER(GuestAddress)(&guest); H();
600 hrc = assignment->COMGETTER(Name)(aDevName.asOutParam()); H();
601
602 InsertConfigNode(pPCIDevs, Utf8StrFmt("%d", iDev).c_str(), &pInst);
603 InsertConfigInteger(pInst, "Trusted", 1);
604
605 HostPCIAddress.fromLong(host);
606 Assert(HostPCIAddress.valid());
607 InsertConfigNode(pInst, "Config", &pCfg);
608 InsertConfigString(pCfg, "DeviceName", aDevName);
609
610 InsertConfigInteger(pCfg, "DetachHostDriver", 1);
611 InsertConfigInteger(pCfg, "HostPCIBusNo", HostPCIAddress.miBus);
612 InsertConfigInteger(pCfg, "HostPCIDeviceNo", HostPCIAddress.miDevice);
613 InsertConfigInteger(pCfg, "HostPCIFunctionNo", HostPCIAddress.miFn);
614
615 GuestPCIAddress.fromLong(guest);
616 Assert(GuestPCIAddress.valid());
617 hrc = pBusMgr->assignHostPCIDevice("pciraw", pInst, HostPCIAddress, GuestPCIAddress, true);
618 if (hrc != S_OK)
619 return hrc;
620
621 InsertConfigInteger(pCfg, "GuestPCIBusNo", GuestPCIAddress.miBus);
622 InsertConfigInteger(pCfg, "GuestPCIDeviceNo", GuestPCIAddress.miDevice);
623 InsertConfigInteger(pCfg, "GuestPCIFunctionNo", GuestPCIAddress.miFn);
624
625 /* the driver */
626 InsertConfigNode(pInst, "LUN#0", &pLunL0);
627 InsertConfigString(pLunL0, "Driver", "pciraw");
628 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
629
630 /* the Main driver */
631 InsertConfigString(pLunL1, "Driver", "MainPciRaw");
632 InsertConfigNode(pLunL1, "Config", &pCfg);
633 PCIRawDev* pMainDev = new PCIRawDev(this);
634 InsertConfigInteger(pCfg, "Object", (uintptr_t)pMainDev);
635 }
636
637 return hrc;
638}
639#endif
640
641
642/**
643 * Allocate a set of LEDs.
644 *
645 * This grabs a maLedSets entry and populates it with @a cLeds.
646 *
647 * @returns Index into maLedSets.
648 * @param cLeds The number of LEDs in the set.
649 * @param enmType The device type.
650 * @param ppaSubTypes When not NULL, subtypes for each LED and return the
651 * array pointer here.
652 */
653uint32_t Console::i_allocateDriverLeds(uint32_t cLeds, DeviceType_T enmType, DeviceType_T **ppaSubTypes)
654{
655 Assert(cLeds > 0);
656 Assert(cLeds < 1024); /* Adjust if any driver supports >=1024 units! */
657
658 /* Grab a LED set entry before we start allocating anything so the destructor can do the cleanups. */
659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Caller should have this already. Need protect mcLedSets check and update. */
660 AssertStmt(mcLedSets < RT_ELEMENTS(maLedSets),
661 throw ConfigError("AllocateDriverPapLeds", VERR_OUT_OF_RANGE, "Too many LED sets"));
662 uint32_t const idxLedSet = mcLedSets++;
663 PLEDSET pLS = &maLedSets[idxLedSet];
664 pLS->papLeds = (PPDMLED *)RTMemAllocZ(sizeof(PPDMLED) * cLeds);
665 AssertStmt(pLS->papLeds, throw E_OUTOFMEMORY);
666 pLS->cLeds = cLeds;
667 pLS->enmType = enmType;
668 pLS->paSubTypes = NULL;
669
670 if (ppaSubTypes)
671 {
672 *ppaSubTypes = pLS->paSubTypes = (DeviceType_T *)RTMemAlloc(sizeof(DeviceType_T) * cLeds);
673 AssertStmt(pLS->paSubTypes, throw E_OUTOFMEMORY);
674 for (size_t idxSub = 0; idxSub < cLeds; ++idxSub)
675 pLS->paSubTypes[idxSub] = DeviceType_Null;
676 }
677
678 LogRel2(("mcLedSets = %d, RT_ELEMENTS(maLedSets) = %d\n", mcLedSets, RT_ELEMENTS(maLedSets)));
679 return idxLedSet;
680}
681
682
683/** @todo r=bird: Drop uFirst as it's always zero? Then s/uLast/cLeds/g. */
684void Console::i_attachStatusDriver(PCFGMNODE pCtlInst, DeviceType_T enmType,
685 uint32_t uFirst, uint32_t uLast,
686 DeviceType_T **ppaSubTypes,
687 Console::MediumAttachmentMap *pmapMediumAttachments,
688 const char *pcszDevice, unsigned uInstance)
689{
690 Assert(uFirst <= uLast);
691 PCFGMNODE pLunL0;
692 InsertConfigNode(pCtlInst, "LUN#999", &pLunL0);
693 InsertConfigString(pLunL0, "Driver", "MainStatus");
694 PCFGMNODE pCfg;
695 InsertConfigNode(pLunL0, "Config", &pCfg);
696 uint32_t const iLedSet = i_allocateDriverLeds(uLast - uFirst + 1, enmType, ppaSubTypes);
697 InsertConfigInteger(pCfg, "iLedSet", iLedSet);
698
699 InsertConfigInteger(pCfg, "HasMediumAttachments", pmapMediumAttachments != NULL);
700 if (pmapMediumAttachments)
701 {
702 AssertPtr(pcszDevice);
703 Utf8StrFmt deviceInstance("%s/%u", pcszDevice, uInstance);
704 InsertConfigString(pCfg, "DeviceInstance", deviceInstance.c_str());
705 }
706 InsertConfigInteger(pCfg, "First", uFirst);
707 InsertConfigInteger(pCfg, "Last", uLast);
708}
709
710
711/**
712 * Construct the VM configuration tree (CFGM).
713 *
714 * This is a callback for VMR3Create() call. It is called from CFGMR3Init() in
715 * the emulation thread (EMT). Any per thread COM/XPCOM initialization is done
716 * here.
717 *
718 * @returns VBox status code.
719 * @param pUVM The user mode VM handle.
720 * @param pVM The cross context VM handle.
721 * @param pVMM The VMM ring-3 vtable.
722 * @param pvConsole Pointer to the VMPowerUpTask object.
723 *
724 * @note Locks the Console object for writing.
725 */
726/*static*/ DECLCALLBACK(int)
727Console::i_configConstructor(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, void *pvConsole)
728{
729 LogFlowFuncEnter();
730
731 AssertReturn(pvConsole, VERR_INVALID_POINTER);
732 ComObjPtr<Console> pConsole = static_cast<Console *>(pvConsole);
733
734 AutoCaller autoCaller(pConsole);
735 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
736
737 /* lock the console because we widely use internal fields and methods */
738 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
739
740 /*
741 * Set the VM handle and do the rest of the job in an worker method so we
742 * can easily reset the VM handle on failure.
743 */
744 pConsole->mpUVM = pUVM;
745 pVMM->pfnVMR3RetainUVM(pUVM);
746 int vrc;
747 try
748 {
749 vrc = pConsole->i_configConstructorInner(pUVM, pVM, pVMM, &alock);
750 }
751 catch (...)
752 {
753 vrc = VERR_UNEXPECTED_EXCEPTION;
754 }
755 if (RT_FAILURE(vrc))
756 {
757 pConsole->mpUVM = NULL;
758 pVMM->pfnVMR3ReleaseUVM(pUVM);
759 }
760
761 return vrc;
762}
763
764
765/**
766 * Worker for configConstructor.
767 *
768 * @return VBox status code.
769 * @param pUVM The user mode VM handle.
770 * @param pVM The cross context VM handle.
771 * @param pVMM The VMM vtable.
772 * @param pAlock The automatic lock instance. This is for when we have
773 * to leave it in order to avoid deadlocks (ext packs and
774 * more).
775 */
776int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, AutoWriteLock *pAlock)
777{
778 RT_NOREF(pVM /* when everything is disabled */);
779 VMMDev *pVMMDev = m_pVMMDev; Assert(pVMMDev);
780 ComPtr<IMachine> pMachine = i_machine();
781
782 int rc;
783 HRESULT hrc;
784 Utf8Str strTmp;
785 Bstr bstr;
786
787#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
788
789 /*
790 * Get necessary objects and frequently used parameters.
791 */
792 ComPtr<IVirtualBox> virtualBox;
793 hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H();
794
795 ComPtr<IHost> host;
796 hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H();
797
798 ComPtr<ISystemProperties> systemProperties;
799 hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam()); H();
800
801 ComPtr<IBIOSSettings> biosSettings;
802 hrc = pMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam()); H();
803
804 ComPtr<INvramStore> nvramStore;
805 hrc = pMachine->COMGETTER(NonVolatileStore)(nvramStore.asOutParam()); H();
806
807 hrc = pMachine->COMGETTER(HardwareUUID)(bstr.asOutParam()); H();
808 RTUUID HardwareUuid;
809 rc = RTUuidFromUtf16(&HardwareUuid, bstr.raw());
810 AssertRCReturn(rc, rc);
811
812 ULONG cRamMBs;
813 hrc = pMachine->COMGETTER(MemorySize)(&cRamMBs); H();
814#if 0 /* enable to play with lots of memory. */
815 if (RTEnvExist("VBOX_RAM_SIZE"))
816 cRamMBs = RTStrToUInt64(RTEnvGet("VBOX_RAM_SIZE"));
817#endif
818 uint64_t const cbRam = cRamMBs * (uint64_t)_1M;
819 uint32_t cbRamHole = MM_RAM_HOLE_SIZE_DEFAULT;
820 uint64_t uMcfgBase = 0;
821 uint32_t cbMcfgLength = 0;
822
823 ParavirtProvider_T enmParavirtProvider;
824 hrc = pMachine->GetEffectiveParavirtProvider(&enmParavirtProvider); H();
825
826 Bstr strParavirtDebug;
827 hrc = pMachine->COMGETTER(ParavirtDebug)(strParavirtDebug.asOutParam()); H();
828
829 BOOL fIOAPIC;
830 uint32_t uIoApicPciAddress = NIL_PCIBDF;
831 hrc = biosSettings->COMGETTER(IOAPICEnabled)(&fIOAPIC); H();
832
833 ChipsetType_T chipsetType;
834 hrc = pMachine->COMGETTER(ChipsetType)(&chipsetType); H();
835 if (chipsetType == ChipsetType_ICH9)
836 {
837 /* We'd better have 0x10000000 region, to cover 256 buses but this put
838 * too much load on hypervisor heap. Linux 4.8 currently complains with
839 * ``acpi PNP0A03:00: [Firmware Info]: MMCONFIG for domain 0000 [bus 00-3f]
840 * only partially covers this bridge'' */
841 cbMcfgLength = 0x4000000; //0x10000000;
842 cbRamHole += cbMcfgLength;
843 uMcfgBase = _4G - cbRamHole;
844 }
845
846 /* Get the CPU profile name. */
847 Bstr bstrCpuProfile;
848 hrc = pMachine->COMGETTER(CPUProfile)(bstrCpuProfile.asOutParam()); H();
849
850 /*
851 * Figure out the IOMMU config.
852 */
853#if defined(VBOX_WITH_IOMMU_AMD) || defined(VBOX_WITH_IOMMU_INTEL)
854 IommuType_T enmIommuType;
855 hrc = pMachine->COMGETTER(IommuType)(&enmIommuType); H();
856
857 /* Resolve 'automatic' type to an Intel or AMD IOMMU based on the host CPU. */
858 if (enmIommuType == IommuType_Automatic)
859 {
860 if ( bstrCpuProfile.startsWith("AMD")
861 || bstrCpuProfile.startsWith("Quad-Core AMD")
862 || bstrCpuProfile.startsWith("Hygon"))
863 enmIommuType = IommuType_AMD;
864 else if (bstrCpuProfile.startsWith("Intel"))
865 {
866 if ( bstrCpuProfile.equals("Intel 8086")
867 || bstrCpuProfile.equals("Intel 80186")
868 || bstrCpuProfile.equals("Intel 80286")
869 || bstrCpuProfile.equals("Intel 80386")
870 || bstrCpuProfile.equals("Intel 80486"))
871 enmIommuType = IommuType_None;
872 else
873 enmIommuType = IommuType_Intel;
874 }
875# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
876 else if (ASMIsAmdCpu())
877 enmIommuType = IommuType_AMD;
878 else if (ASMIsIntelCpu())
879 enmIommuType = IommuType_Intel;
880# endif
881 else
882 {
883 /** @todo Should we handle other CPUs like Shanghai, VIA etc. here? */
884 LogRel(("WARNING! Unrecognized CPU type, IOMMU disabled.\n"));
885 enmIommuType = IommuType_None;
886 }
887 }
888
889 if (enmIommuType == IommuType_AMD)
890 {
891# ifdef VBOX_WITH_IOMMU_AMD
892 /*
893 * Reserve the specific PCI address of the "SB I/O APIC" when using
894 * an AMD IOMMU. Required by Linux guests, see @bugref{9654#c23}.
895 */
896 uIoApicPciAddress = VBOX_PCI_BDF_SB_IOAPIC;
897# else
898 LogRel(("WARNING! AMD IOMMU not supported, IOMMU disabled.\n"));
899 enmIommuType = IommuType_None;
900# endif
901 }
902
903 if (enmIommuType == IommuType_Intel)
904 {
905# ifdef VBOX_WITH_IOMMU_INTEL
906 /*
907 * Reserve a unique PCI address for the I/O APIC when using
908 * an Intel IOMMU. For convenience we use the same address as
909 * we do on AMD, see @bugref{9967#c13}.
910 */
911 uIoApicPciAddress = VBOX_PCI_BDF_SB_IOAPIC;
912# else
913 LogRel(("WARNING! Intel IOMMU not supported, IOMMU disabled.\n"));
914 enmIommuType = IommuType_None;
915# endif
916 }
917
918 if ( enmIommuType == IommuType_AMD
919 || enmIommuType == IommuType_Intel)
920 {
921 if (chipsetType != ChipsetType_ICH9)
922 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
923 N_("IOMMU uses MSIs which requires the ICH9 chipset implementation."));
924 if (!fIOAPIC)
925 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
926 N_("IOMMU requires an I/O APIC for remapping interrupts."));
927 }
928#else
929 IommuType_T const enmIommuType = IommuType_None;
930#endif
931
932 /* Instantiate the bus assignment manager. */
933 Assert(enmIommuType != IommuType_Automatic);
934 BusAssignmentManager *pBusMgr = mBusMgr = BusAssignmentManager::createInstance(pVMM, chipsetType, enmIommuType);
935
936 ULONG cCpus = 1;
937 hrc = pMachine->COMGETTER(CPUCount)(&cCpus); H();
938
939 ULONG ulCpuExecutionCap = 100;
940 hrc = pMachine->COMGETTER(CPUExecutionCap)(&ulCpuExecutionCap); H();
941
942 Bstr osTypeId;
943 hrc = pMachine->COMGETTER(OSTypeId)(osTypeId.asOutParam()); H();
944 LogRel(("Guest OS type: '%s'\n", Utf8Str(osTypeId).c_str()));
945
946 APICMode_T apicMode;
947 hrc = biosSettings->COMGETTER(APICMode)(&apicMode); H();
948 uint32_t uFwAPIC;
949 switch (apicMode)
950 {
951 case APICMode_Disabled:
952 uFwAPIC = 0;
953 break;
954 case APICMode_APIC:
955 uFwAPIC = 1;
956 break;
957 case APICMode_X2APIC:
958 uFwAPIC = 2;
959 break;
960 default:
961 AssertMsgFailed(("Invalid APICMode=%d\n", apicMode));
962 uFwAPIC = 1;
963 break;
964 }
965
966 ComPtr<IGuestOSType> pGuestOSType;
967 virtualBox->GetGuestOSType(osTypeId.raw(), pGuestOSType.asOutParam());
968
969 BOOL fOsXGuest = FALSE;
970 BOOL fWinGuest = FALSE;
971 if (!pGuestOSType.isNull())
972 {
973 Bstr guestTypeFamilyId;
974 hrc = pGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam()); H();
975 fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
976 fWinGuest = guestTypeFamilyId == Bstr("Windows");
977 }
978
979 ULONG maxNetworkAdapters;
980 hrc = systemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters); H();
981
982 /*
983 * Get root node first.
984 * This is the only node in the tree.
985 */
986 PCFGMNODE pRoot = pVMM->pfnCFGMR3GetRootU(pUVM);
987 Assert(pRoot);
988
989 // InsertConfigString throws
990 try
991 {
992
993 /*
994 * Set the root (and VMM) level values.
995 */
996 hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H();
997 InsertConfigString(pRoot, "Name", bstr);
998 InsertConfigBytes(pRoot, "UUID", &HardwareUuid, sizeof(HardwareUuid));
999 InsertConfigInteger(pRoot, "RamSize", cbRam);
1000 InsertConfigInteger(pRoot, "RamHoleSize", cbRamHole);
1001 InsertConfigInteger(pRoot, "NumCPUs", cCpus);
1002 InsertConfigInteger(pRoot, "CpuExecutionCap", ulCpuExecutionCap);
1003 InsertConfigInteger(pRoot, "TimerMillies", 10);
1004
1005 BOOL fPageFusion = FALSE;
1006 hrc = pMachine->COMGETTER(PageFusionEnabled)(&fPageFusion); H();
1007 InsertConfigInteger(pRoot, "PageFusionAllowed", fPageFusion); /* boolean */
1008
1009 /* Not necessary, but makes sure this setting ends up in the release log. */
1010 ULONG ulBalloonSize = 0;
1011 hrc = pMachine->COMGETTER(MemoryBalloonSize)(&ulBalloonSize); H();
1012 InsertConfigInteger(pRoot, "MemBalloonSize", ulBalloonSize);
1013
1014 /*
1015 * EM values (before CPUM as it may need to set IemExecutesAll).
1016 */
1017 PCFGMNODE pEM;
1018 InsertConfigNode(pRoot, "EM", &pEM);
1019
1020 /* Triple fault behavior. */
1021 BOOL fTripleFaultReset = false;
1022 hrc = pMachine->GetCPUProperty(CPUPropertyType_TripleFaultReset, &fTripleFaultReset); H();
1023 InsertConfigInteger(pEM, "TripleFaultReset", fTripleFaultReset);
1024
1025 /*
1026 * CPUM values.
1027 */
1028 PCFGMNODE pCPUM;
1029 InsertConfigNode(pRoot, "CPUM", &pCPUM);
1030 PCFGMNODE pIsaExts;
1031 InsertConfigNode(pCPUM, "IsaExts", &pIsaExts);
1032
1033 /* Host CPUID leaf overrides. */
1034 for (uint32_t iOrdinal = 0; iOrdinal < _4K; iOrdinal++)
1035 {
1036 ULONG uLeaf, uSubLeaf, uEax, uEbx, uEcx, uEdx;
1037 hrc = pMachine->GetCPUIDLeafByOrdinal(iOrdinal, &uLeaf, &uSubLeaf, &uEax, &uEbx, &uEcx, &uEdx);
1038 if (hrc == E_INVALIDARG)
1039 break;
1040 H();
1041 PCFGMNODE pLeaf;
1042 InsertConfigNode(pCPUM, Utf8StrFmt("HostCPUID/%RX32", uLeaf).c_str(), &pLeaf);
1043 /** @todo Figure out how to tell the VMM about uSubLeaf */
1044 InsertConfigInteger(pLeaf, "eax", uEax);
1045 InsertConfigInteger(pLeaf, "ebx", uEbx);
1046 InsertConfigInteger(pLeaf, "ecx", uEcx);
1047 InsertConfigInteger(pLeaf, "edx", uEdx);
1048 }
1049
1050 /* We must limit CPUID count for Windows NT 4, as otherwise it stops
1051 with error 0x3e (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED). */
1052 if (osTypeId == "WindowsNT4")
1053 {
1054 LogRel(("Limiting CPUID leaf count for NT4 guests\n"));
1055 InsertConfigInteger(pCPUM, "NT4LeafLimit", true);
1056 }
1057
1058 if (fOsXGuest)
1059 {
1060 /* Expose extended MWAIT features to Mac OS X guests. */
1061 LogRel(("Using MWAIT extensions\n"));
1062 InsertConfigInteger(pIsaExts, "MWaitExtensions", true);
1063
1064 /* Fake the CPU family/model so the guest works. This is partly
1065 because older mac releases really doesn't work on newer cpus,
1066 and partly because mac os x expects more from systems with newer
1067 cpus (MSRs, power features, whatever). */
1068 uint32_t uMaxIntelFamilyModelStep = UINT32_MAX;
1069 if ( osTypeId == "MacOS"
1070 || osTypeId == "MacOS_64")
1071 uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482. */
1072 else if ( osTypeId == "MacOS106"
1073 || osTypeId == "MacOS106_64")
1074 uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */
1075 else if ( osTypeId == "MacOS107"
1076 || osTypeId == "MacOS107_64")
1077 uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ /** @todo figure out
1078 what is required here. */
1079 else if ( osTypeId == "MacOS108"
1080 || osTypeId == "MacOS108_64")
1081 uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ /** @todo figure out
1082 what is required here. */
1083 else if ( osTypeId == "MacOS109"
1084 || osTypeId == "MacOS109_64")
1085 uMaxIntelFamilyModelStep = RT_MAKE_U32_FROM_U8(1, 23, 6, 0); /* Penryn / X5482 */ /** @todo figure
1086 out what is required here. */
1087 if (uMaxIntelFamilyModelStep != UINT32_MAX)
1088 InsertConfigInteger(pCPUM, "MaxIntelFamilyModelStep", uMaxIntelFamilyModelStep);
1089 }
1090
1091 /* CPU Portability level, */
1092 ULONG uCpuIdPortabilityLevel = 0;
1093 hrc = pMachine->COMGETTER(CPUIDPortabilityLevel)(&uCpuIdPortabilityLevel); H();
1094 InsertConfigInteger(pCPUM, "PortableCpuIdLevel", uCpuIdPortabilityLevel);
1095
1096 /* Physical Address Extension (PAE) */
1097 BOOL fEnablePAE = false;
1098 hrc = pMachine->GetCPUProperty(CPUPropertyType_PAE, &fEnablePAE); H();
1099 InsertConfigInteger(pRoot, "EnablePAE", fEnablePAE);
1100
1101 /* APIC/X2APIC configuration */
1102 BOOL fEnableAPIC = true;
1103 BOOL fEnableX2APIC = true;
1104 hrc = pMachine->GetCPUProperty(CPUPropertyType_APIC, &fEnableAPIC); H();
1105 hrc = pMachine->GetCPUProperty(CPUPropertyType_X2APIC, &fEnableX2APIC); H();
1106 if (fEnableX2APIC)
1107 Assert(fEnableAPIC);
1108
1109 /* CPUM profile name. */
1110 InsertConfigString(pCPUM, "GuestCpuName", bstrCpuProfile);
1111
1112 /*
1113 * Temporary(?) hack to make sure we emulate the ancient 16-bit CPUs
1114 * correctly. There are way too many #UDs we'll miss using VT-x,
1115 * raw-mode or qemu for the 186 and 286, while we'll get undefined opcodes
1116 * dead wrong on 8086 (see http://www.os2museum.com/wp/undocumented-8086-opcodes/).
1117 */
1118 if ( bstrCpuProfile.equals("Intel 80386") /* just for now */
1119 || bstrCpuProfile.equals("Intel 80286")
1120 || bstrCpuProfile.equals("Intel 80186")
1121 || bstrCpuProfile.equals("Nec V20")
1122 || bstrCpuProfile.equals("Intel 8086") )
1123 {
1124 InsertConfigInteger(pEM, "IemExecutesAll", true);
1125 if (!bstrCpuProfile.equals("Intel 80386"))
1126 {
1127 fEnableAPIC = false;
1128 fIOAPIC = false;
1129 }
1130 fEnableX2APIC = false;
1131 }
1132
1133 /* Adjust firmware APIC handling to stay within the VCPU limits. */
1134 if (uFwAPIC == 2 && !fEnableX2APIC)
1135 {
1136 if (fEnableAPIC)
1137 uFwAPIC = 1;
1138 else
1139 uFwAPIC = 0;
1140 LogRel(("Limiting the firmware APIC level from x2APIC to %s\n", fEnableAPIC ? "APIC" : "Disabled"));
1141 }
1142 else if (uFwAPIC == 1 && !fEnableAPIC)
1143 {
1144 uFwAPIC = 0;
1145 LogRel(("Limiting the firmware APIC level from APIC to Disabled\n"));
1146 }
1147
1148 /* Speculation Control. */
1149 BOOL fSpecCtrl = FALSE;
1150 hrc = pMachine->GetCPUProperty(CPUPropertyType_SpecCtrl, &fSpecCtrl); H();
1151 InsertConfigInteger(pCPUM, "SpecCtrl", fSpecCtrl);
1152
1153 /* Nested VT-x / AMD-V. */
1154 BOOL fNestedHWVirt = FALSE;
1155 hrc = pMachine->GetCPUProperty(CPUPropertyType_HWVirt, &fNestedHWVirt); H();
1156 InsertConfigInteger(pCPUM, "NestedHWVirt", fNestedHWVirt ? true : false);
1157
1158 /*
1159 * Hardware virtualization extensions.
1160 */
1161 BOOL fSupportsHwVirtEx;
1162 hrc = host->GetProcessorFeature(ProcessorFeature_HWVirtEx, &fSupportsHwVirtEx); H();
1163
1164 BOOL fIsGuest64Bit;
1165 hrc = pMachine->GetCPUProperty(CPUPropertyType_LongMode, &fIsGuest64Bit); H();
1166 if (fIsGuest64Bit)
1167 {
1168 BOOL fSupportsLongMode;
1169 hrc = host->GetProcessorFeature(ProcessorFeature_LongMode, &fSupportsLongMode); H();
1170 if (!fSupportsLongMode)
1171 {
1172 LogRel(("WARNING! 64-bit guest type selected but the host CPU does NOT support 64-bit.\n"));
1173 fIsGuest64Bit = FALSE;
1174 }
1175 if (!fSupportsHwVirtEx)
1176 {
1177 LogRel(("WARNING! 64-bit guest type selected but the host CPU does NOT support HW virtualization.\n"));
1178 fIsGuest64Bit = FALSE;
1179 }
1180 }
1181
1182 /* Sanitize valid/useful APIC combinations, see @bugref{8868}. */
1183 if (!fEnableAPIC)
1184 {
1185 if (fIsGuest64Bit)
1186 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1187 N_("Cannot disable the APIC for a 64-bit guest."));
1188 if (cCpus > 1)
1189 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1190 N_("Cannot disable the APIC for an SMP guest."));
1191 if (fIOAPIC)
1192 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1193 N_("Cannot disable the APIC when the I/O APIC is present."));
1194 }
1195
1196 BOOL fHMEnabled;
1197 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Enabled, &fHMEnabled); H();
1198 if (cCpus > 1 && !fHMEnabled)
1199 {
1200 LogRel(("Forced fHMEnabled to TRUE by SMP guest.\n"));
1201 fHMEnabled = TRUE;
1202 }
1203
1204 BOOL fHMForced;
1205 fHMEnabled = fHMForced = TRUE;
1206 LogRel(("fHMForced=true - No raw-mode support in this build!\n"));
1207 if (!fHMForced) /* No need to query if already forced above. */
1208 {
1209 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_Force, &fHMForced); H();
1210 if (fHMForced)
1211 LogRel(("fHMForced=true - HWVirtExPropertyType_Force\n"));
1212 }
1213 InsertConfigInteger(pRoot, "HMEnabled", fHMEnabled);
1214
1215 /* /HM/xyz */
1216 PCFGMNODE pHM;
1217 InsertConfigNode(pRoot, "HM", &pHM);
1218 InsertConfigInteger(pHM, "HMForced", fHMForced);
1219 if (fHMEnabled)
1220 {
1221 /* Indicate whether 64-bit guests are supported or not. */
1222 InsertConfigInteger(pHM, "64bitEnabled", fIsGuest64Bit);
1223#if ARCH_BITS == 32 /* The recompiler must use VBoxREM64 (32-bit host only). */
1224 PCFGMNODE pREM;
1225 InsertConfigNode(pRoot, "REM", &pREM);
1226 InsertConfigInteger(pREM, "64bitEnabled", 1);
1227#endif
1228
1229 /** @todo Not exactly pretty to check strings; VBOXOSTYPE would be better,
1230 but that requires quite a bit of API change in Main. */
1231 if ( fIOAPIC
1232 && ( osTypeId == "WindowsNT4"
1233 || osTypeId == "Windows2000"
1234 || osTypeId == "WindowsXP"
1235 || osTypeId == "Windows2003"))
1236 {
1237 /* Only allow TPR patching for NT, Win2k, XP and Windows Server 2003. (32 bits mode)
1238 * We may want to consider adding more guest OSes (Solaris) later on.
1239 */
1240 InsertConfigInteger(pHM, "TPRPatchingEnabled", 1);
1241 }
1242 }
1243
1244 /* HWVirtEx exclusive mode */
1245 BOOL fHMExclusive = true;
1246 hrc = systemProperties->COMGETTER(ExclusiveHwVirt)(&fHMExclusive); H();
1247 InsertConfigInteger(pHM, "Exclusive", fHMExclusive);
1248
1249 /* Nested paging (VT-x/AMD-V) */
1250 BOOL fEnableNestedPaging = false;
1251 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_NestedPaging, &fEnableNestedPaging); H();
1252 InsertConfigInteger(pHM, "EnableNestedPaging", fEnableNestedPaging);
1253
1254 /* Large pages; requires nested paging */
1255 BOOL fEnableLargePages = false;
1256 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_LargePages, &fEnableLargePages); H();
1257 InsertConfigInteger(pHM, "EnableLargePages", fEnableLargePages);
1258
1259 /* VPID (VT-x) */
1260 BOOL fEnableVPID = false;
1261 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_VPID, &fEnableVPID); H();
1262 InsertConfigInteger(pHM, "EnableVPID", fEnableVPID);
1263
1264 /* Unrestricted execution aka UX (VT-x) */
1265 BOOL fEnableUX = false;
1266 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, &fEnableUX); H();
1267 InsertConfigInteger(pHM, "EnableUX", fEnableUX);
1268
1269 /* Virtualized VMSAVE/VMLOAD (AMD-V) */
1270 BOOL fVirtVmsaveVmload = true;
1271 hrc = host->GetProcessorFeature(ProcessorFeature_VirtVmsaveVmload, &fVirtVmsaveVmload); H();
1272 InsertConfigInteger(pHM, "SvmVirtVmsaveVmload", fVirtVmsaveVmload);
1273
1274 /* Indirect branch prediction boundraries. */
1275 BOOL fIBPBOnVMExit = false;
1276 hrc = pMachine->GetCPUProperty(CPUPropertyType_IBPBOnVMExit, &fIBPBOnVMExit); H();
1277 InsertConfigInteger(pHM, "IBPBOnVMExit", fIBPBOnVMExit);
1278
1279 BOOL fIBPBOnVMEntry = false;
1280 hrc = pMachine->GetCPUProperty(CPUPropertyType_IBPBOnVMEntry, &fIBPBOnVMEntry); H();
1281 InsertConfigInteger(pHM, "IBPBOnVMEntry", fIBPBOnVMEntry);
1282
1283 BOOL fSpecCtrlByHost = false;
1284 hrc = pMachine->GetCPUProperty(CPUPropertyType_SpecCtrlByHost, &fSpecCtrlByHost); H();
1285 InsertConfigInteger(pHM, "SpecCtrlByHost", fSpecCtrlByHost);
1286
1287 BOOL fL1DFlushOnSched = true;
1288 hrc = pMachine->GetCPUProperty(CPUPropertyType_L1DFlushOnEMTScheduling, &fL1DFlushOnSched); H();
1289 InsertConfigInteger(pHM, "L1DFlushOnSched", fL1DFlushOnSched);
1290
1291 BOOL fL1DFlushOnVMEntry = false;
1292 hrc = pMachine->GetCPUProperty(CPUPropertyType_L1DFlushOnVMEntry, &fL1DFlushOnVMEntry); H();
1293 InsertConfigInteger(pHM, "L1DFlushOnVMEntry", fL1DFlushOnVMEntry);
1294
1295 BOOL fMDSClearOnSched = true;
1296 hrc = pMachine->GetCPUProperty(CPUPropertyType_MDSClearOnEMTScheduling, &fMDSClearOnSched); H();
1297 InsertConfigInteger(pHM, "MDSClearOnSched", fMDSClearOnSched);
1298
1299 BOOL fMDSClearOnVMEntry = false;
1300 hrc = pMachine->GetCPUProperty(CPUPropertyType_MDSClearOnVMEntry, &fMDSClearOnVMEntry); H();
1301 InsertConfigInteger(pHM, "MDSClearOnVMEntry", fMDSClearOnVMEntry);
1302
1303 /* Reset overwrite. */
1304 mfTurnResetIntoPowerOff = GetExtraDataBoth(virtualBox, pMachine,
1305 "VBoxInternal2/TurnResetIntoPowerOff", &strTmp)->equals("1");
1306 if (mfTurnResetIntoPowerOff)
1307 InsertConfigInteger(pRoot, "PowerOffInsteadOfReset", 1);
1308
1309 /* Use NEM rather than HM. */
1310 BOOL fUseNativeApi = false;
1311 hrc = pMachine->GetHWVirtExProperty(HWVirtExPropertyType_UseNativeApi, &fUseNativeApi); H();
1312 InsertConfigInteger(pHM, "UseNEMInstead", fUseNativeApi);
1313
1314 /* Enable workaround for missing TLB flush for OS/2 guests, see ticketref:20625. */
1315 if (osTypeId.startsWith("OS2"))
1316 InsertConfigInteger(pHM, "MissingOS2TlbFlushWorkaround", 1);
1317
1318 /*
1319 * NEM
1320 */
1321 PCFGMNODE pNEM;
1322 InsertConfigNode(pRoot, "NEM", &pNEM);
1323 InsertConfigInteger(pNEM, "Allow64BitGuests", fIsGuest64Bit);
1324
1325 /*
1326 * Paravirt. provider.
1327 */
1328 PCFGMNODE pParavirtNode;
1329 InsertConfigNode(pRoot, "GIM", &pParavirtNode);
1330 const char *pcszParavirtProvider;
1331 bool fGimDeviceNeeded = true;
1332 switch (enmParavirtProvider)
1333 {
1334 case ParavirtProvider_None:
1335 pcszParavirtProvider = "None";
1336 fGimDeviceNeeded = false;
1337 break;
1338
1339 case ParavirtProvider_Minimal:
1340 pcszParavirtProvider = "Minimal";
1341 break;
1342
1343 case ParavirtProvider_HyperV:
1344 pcszParavirtProvider = "HyperV";
1345 break;
1346
1347 case ParavirtProvider_KVM:
1348 pcszParavirtProvider = "KVM";
1349 break;
1350
1351 default:
1352 AssertMsgFailed(("Invalid enmParavirtProvider=%d\n", enmParavirtProvider));
1353 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("Invalid paravirt. provider '%d'"),
1354 enmParavirtProvider);
1355 }
1356 InsertConfigString(pParavirtNode, "Provider", pcszParavirtProvider);
1357
1358 /*
1359 * Parse paravirt. debug options.
1360 */
1361 bool fGimDebug = false;
1362 com::Utf8Str strGimDebugAddress = "127.0.0.1";
1363 uint32_t uGimDebugPort = 50000;
1364 if (strParavirtDebug.isNotEmpty())
1365 {
1366 /* Hyper-V debug options. */
1367 if (enmParavirtProvider == ParavirtProvider_HyperV)
1368 {
1369 bool fGimHvDebug = false;
1370 com::Utf8Str strGimHvVendor;
1371 bool fGimHvVsIf = false;
1372 bool fGimHvHypercallIf = false;
1373
1374 size_t uPos = 0;
1375 com::Utf8Str strDebugOptions = strParavirtDebug;
1376 com::Utf8Str strKey;
1377 com::Utf8Str strVal;
1378 while ((uPos = strDebugOptions.parseKeyValue(strKey, strVal, uPos)) != com::Utf8Str::npos)
1379 {
1380 if (strKey == "enabled")
1381 {
1382 if (strVal.toUInt32() == 1)
1383 {
1384 /* Apply defaults.
1385 The defaults are documented in the user manual,
1386 changes need to be reflected accordingly. */
1387 fGimHvDebug = true;
1388 strGimHvVendor = "Microsoft Hv";
1389 fGimHvVsIf = true;
1390 fGimHvHypercallIf = false;
1391 }
1392 /* else: ignore, i.e. don't assert below with 'enabled=0'. */
1393 }
1394 else if (strKey == "address")
1395 strGimDebugAddress = strVal;
1396 else if (strKey == "port")
1397 uGimDebugPort = strVal.toUInt32();
1398 else if (strKey == "vendor")
1399 strGimHvVendor = strVal;
1400 else if (strKey == "vsinterface")
1401 fGimHvVsIf = RT_BOOL(strVal.toUInt32());
1402 else if (strKey == "hypercallinterface")
1403 fGimHvHypercallIf = RT_BOOL(strVal.toUInt32());
1404 else
1405 {
1406 AssertMsgFailed(("Unrecognized Hyper-V debug option '%s'\n", strKey.c_str()));
1407 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1408 N_("Unrecognized Hyper-V debug option '%s' in '%s'"), strKey.c_str(),
1409 strDebugOptions.c_str());
1410 }
1411 }
1412
1413 /* Update HyperV CFGM node with active debug options. */
1414 if (fGimHvDebug)
1415 {
1416 PCFGMNODE pHvNode;
1417 InsertConfigNode(pParavirtNode, "HyperV", &pHvNode);
1418 InsertConfigString(pHvNode, "VendorID", strGimHvVendor);
1419 InsertConfigInteger(pHvNode, "VSInterface", fGimHvVsIf ? 1 : 0);
1420 InsertConfigInteger(pHvNode, "HypercallDebugInterface", fGimHvHypercallIf ? 1 : 0);
1421 fGimDebug = true;
1422 }
1423 }
1424 }
1425
1426 /*
1427 * MM values.
1428 */
1429 PCFGMNODE pMM;
1430 InsertConfigNode(pRoot, "MM", &pMM);
1431 InsertConfigInteger(pMM, "CanUseLargerHeap", chipsetType == ChipsetType_ICH9);
1432
1433 /*
1434 * PDM config.
1435 * Load drivers in VBoxC.[so|dll]
1436 */
1437 PCFGMNODE pPDM;
1438 PCFGMNODE pNode;
1439 PCFGMNODE pMod;
1440 InsertConfigNode(pRoot, "PDM", &pPDM);
1441 InsertConfigNode(pPDM, "Devices", &pNode);
1442 InsertConfigNode(pPDM, "Drivers", &pNode);
1443 InsertConfigNode(pNode, "VBoxC", &pMod);
1444#ifdef VBOX_WITH_XPCOM
1445 // VBoxC is located in the components subdirectory
1446 char szPathVBoxC[RTPATH_MAX];
1447 rc = RTPathAppPrivateArch(szPathVBoxC, RTPATH_MAX - sizeof("/components/VBoxC")); AssertRC(rc);
1448 strcat(szPathVBoxC, "/components/VBoxC");
1449 InsertConfigString(pMod, "Path", szPathVBoxC);
1450#else
1451 InsertConfigString(pMod, "Path", "VBoxC");
1452#endif
1453
1454
1455 /*
1456 * Block cache settings.
1457 */
1458 PCFGMNODE pPDMBlkCache;
1459 InsertConfigNode(pPDM, "BlkCache", &pPDMBlkCache);
1460
1461 /* I/O cache size */
1462 ULONG ioCacheSize = 5;
1463 hrc = pMachine->COMGETTER(IOCacheSize)(&ioCacheSize); H();
1464 InsertConfigInteger(pPDMBlkCache, "CacheSize", ioCacheSize * _1M);
1465
1466 /*
1467 * Bandwidth groups.
1468 */
1469 ComPtr<IBandwidthControl> bwCtrl;
1470
1471 hrc = pMachine->COMGETTER(BandwidthControl)(bwCtrl.asOutParam()); H();
1472
1473 com::SafeIfaceArray<IBandwidthGroup> bwGroups;
1474 hrc = bwCtrl->GetAllBandwidthGroups(ComSafeArrayAsOutParam(bwGroups)); H();
1475
1476 PCFGMNODE pAc;
1477 InsertConfigNode(pPDM, "AsyncCompletion", &pAc);
1478 PCFGMNODE pAcFile;
1479 InsertConfigNode(pAc, "File", &pAcFile);
1480 PCFGMNODE pAcFileBwGroups;
1481 InsertConfigNode(pAcFile, "BwGroups", &pAcFileBwGroups);
1482#ifdef VBOX_WITH_NETSHAPER
1483 PCFGMNODE pNetworkShaper;
1484 InsertConfigNode(pPDM, "NetworkShaper", &pNetworkShaper);
1485 PCFGMNODE pNetworkBwGroups;
1486 InsertConfigNode(pNetworkShaper, "BwGroups", &pNetworkBwGroups);
1487#endif /* VBOX_WITH_NETSHAPER */
1488
1489 for (size_t i = 0; i < bwGroups.size(); i++)
1490 {
1491 Bstr strName;
1492 hrc = bwGroups[i]->COMGETTER(Name)(strName.asOutParam()); H();
1493 if (strName.isEmpty())
1494 return pVMM->pfnVMR3SetError(pUVM, VERR_CFGM_NO_NODE, RT_SRC_POS, N_("No bandwidth group name specified"));
1495
1496 BandwidthGroupType_T enmType = BandwidthGroupType_Null;
1497 hrc = bwGroups[i]->COMGETTER(Type)(&enmType); H();
1498 LONG64 cMaxBytesPerSec = 0;
1499 hrc = bwGroups[i]->COMGETTER(MaxBytesPerSec)(&cMaxBytesPerSec); H();
1500
1501 if (enmType == BandwidthGroupType_Disk)
1502 {
1503 PCFGMNODE pBwGroup;
1504 InsertConfigNode(pAcFileBwGroups, Utf8Str(strName).c_str(), &pBwGroup);
1505 InsertConfigInteger(pBwGroup, "Max", cMaxBytesPerSec);
1506 InsertConfigInteger(pBwGroup, "Start", cMaxBytesPerSec);
1507 InsertConfigInteger(pBwGroup, "Step", 0);
1508 }
1509#ifdef VBOX_WITH_NETSHAPER
1510 else if (enmType == BandwidthGroupType_Network)
1511 {
1512 /* Network bandwidth groups. */
1513 PCFGMNODE pBwGroup;
1514 InsertConfigNode(pNetworkBwGroups, Utf8Str(strName).c_str(), &pBwGroup);
1515 InsertConfigInteger(pBwGroup, "Max", cMaxBytesPerSec);
1516 }
1517#endif /* VBOX_WITH_NETSHAPER */
1518 }
1519
1520 /*
1521 * Devices
1522 */
1523 PCFGMNODE pDevices = NULL; /* /Devices */
1524 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */
1525 PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */
1526 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
1527 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
1528 PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */
1529 PCFGMNODE pBiosCfg = NULL; /* /Devices/pcbios/0/Config/ */
1530 PCFGMNODE pNetBootCfg = NULL; /* /Devices/pcbios/0/Config/NetBoot/ */
1531
1532 InsertConfigNode(pRoot, "Devices", &pDevices);
1533
1534 /*
1535 * GIM Device
1536 */
1537 if (fGimDeviceNeeded)
1538 {
1539 InsertConfigNode(pDevices, "GIMDev", &pDev);
1540 InsertConfigNode(pDev, "0", &pInst);
1541 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1542 //InsertConfigNode(pInst, "Config", &pCfg);
1543
1544 if (fGimDebug)
1545 {
1546 InsertConfigNode(pInst, "LUN#998", &pLunL0);
1547 InsertConfigString(pLunL0, "Driver", "UDP");
1548 InsertConfigNode(pLunL0, "Config", &pLunL1);
1549 InsertConfigString(pLunL1, "ServerAddress", strGimDebugAddress);
1550 InsertConfigInteger(pLunL1, "ServerPort", uGimDebugPort);
1551 }
1552 }
1553
1554 /*
1555 * PC Arch.
1556 */
1557 InsertConfigNode(pDevices, "pcarch", &pDev);
1558 InsertConfigNode(pDev, "0", &pInst);
1559 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1560 InsertConfigNode(pInst, "Config", &pCfg);
1561
1562 /*
1563 * The time offset
1564 */
1565 LONG64 timeOffset;
1566 hrc = biosSettings->COMGETTER(TimeOffset)(&timeOffset); H();
1567 PCFGMNODE pTMNode;
1568 InsertConfigNode(pRoot, "TM", &pTMNode);
1569 InsertConfigInteger(pTMNode, "UTCOffset", timeOffset * 1000000);
1570
1571 /*
1572 * DMA
1573 */
1574 InsertConfigNode(pDevices, "8237A", &pDev);
1575 InsertConfigNode(pDev, "0", &pInst);
1576 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1577
1578 /*
1579 * PCI buses.
1580 */
1581 uint32_t uIocPCIAddress, uHbcPCIAddress;
1582 switch (chipsetType)
1583 {
1584 default:
1585 AssertFailed();
1586 RT_FALL_THRU();
1587 case ChipsetType_PIIX3:
1588 /* Create the base for adding bridges on demand */
1589 InsertConfigNode(pDevices, "pcibridge", NULL);
1590
1591 InsertConfigNode(pDevices, "pci", &pDev);
1592 uHbcPCIAddress = (0x0 << 16) | 0;
1593 uIocPCIAddress = (0x1 << 16) | 0; // ISA controller
1594 break;
1595 case ChipsetType_ICH9:
1596 /* Create the base for adding bridges on demand */
1597 InsertConfigNode(pDevices, "ich9pcibridge", NULL);
1598
1599 InsertConfigNode(pDevices, "ich9pci", &pDev);
1600 uHbcPCIAddress = (0x1e << 16) | 0;
1601 uIocPCIAddress = (0x1f << 16) | 0; // LPC controller
1602 break;
1603 }
1604 InsertConfigNode(pDev, "0", &pInst);
1605 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1606 InsertConfigNode(pInst, "Config", &pCfg);
1607 InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC);
1608 if (chipsetType == ChipsetType_ICH9)
1609 {
1610 /* Provide MCFG info */
1611 InsertConfigInteger(pCfg, "McfgBase", uMcfgBase);
1612 InsertConfigInteger(pCfg, "McfgLength", cbMcfgLength);
1613
1614#ifdef VBOX_WITH_PCI_PASSTHROUGH
1615 /* Add PCI passthrough devices */
1616 hrc = i_attachRawPCIDevices(pUVM, pBusMgr, pDevices); H();
1617#endif
1618
1619 if (enmIommuType == IommuType_AMD)
1620 {
1621 /* AMD IOMMU. */
1622 InsertConfigNode(pDevices, "iommu-amd", &pDev);
1623 InsertConfigNode(pDev, "0", &pInst);
1624 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1625 InsertConfigNode(pInst, "Config", &pCfg);
1626 hrc = pBusMgr->assignPCIDevice("iommu-amd", pInst); H();
1627
1628 /* The AMD IOMMU device needs to know which PCI slot it's in, see @bugref{9654#c104}. */
1629 {
1630 PCIBusAddress Address;
1631 if (pBusMgr->findPCIAddress("iommu-amd", 0, Address))
1632 {
1633 uint32_t const u32IommuAddress = (Address.miDevice << 16) | Address.miFn;
1634 InsertConfigInteger(pCfg, "PCIAddress", u32IommuAddress);
1635 }
1636 else
1637 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1638 N_("Failed to find PCI address of the assigned IOMMU device!"));
1639 }
1640
1641 PCIBusAddress PCIAddr = PCIBusAddress((int32_t)uIoApicPciAddress);
1642 hrc = pBusMgr->assignPCIDevice("sb-ioapic", NULL /* pCfg */, PCIAddr, true /*fGuestAddressRequired*/); H();
1643 }
1644 else if (enmIommuType == IommuType_Intel)
1645 {
1646 /* Intel IOMMU. */
1647 InsertConfigNode(pDevices, "iommu-intel", &pDev);
1648 InsertConfigNode(pDev, "0", &pInst);
1649 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1650 InsertConfigNode(pInst, "Config", &pCfg);
1651 hrc = pBusMgr->assignPCIDevice("iommu-intel", pInst); H();
1652
1653 PCIBusAddress PCIAddr = PCIBusAddress((int32_t)uIoApicPciAddress);
1654 hrc = pBusMgr->assignPCIDevice("sb-ioapic", NULL /* pCfg */, PCIAddr, true /*fGuestAddressRequired*/); H();
1655 }
1656 }
1657
1658 /*
1659 * Enable the following devices: HPET, SMC and LPC on MacOS X guests or on ICH9 chipset
1660 */
1661
1662 /*
1663 * High Precision Event Timer (HPET)
1664 */
1665 BOOL fHPETEnabled;
1666 /* Other guests may wish to use HPET too, but MacOS X not functional without it */
1667 hrc = pMachine->COMGETTER(HPETEnabled)(&fHPETEnabled); H();
1668 /* so always enable HPET in extended profile */
1669 fHPETEnabled |= fOsXGuest;
1670 /* HPET is always present on ICH9 */
1671 fHPETEnabled |= (chipsetType == ChipsetType_ICH9);
1672 if (fHPETEnabled)
1673 {
1674 InsertConfigNode(pDevices, "hpet", &pDev);
1675 InsertConfigNode(pDev, "0", &pInst);
1676 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1677 InsertConfigNode(pInst, "Config", &pCfg);
1678 InsertConfigInteger(pCfg, "ICH9", (chipsetType == ChipsetType_ICH9) ? 1 : 0); /* boolean */
1679 }
1680
1681 /*
1682 * System Management Controller (SMC)
1683 */
1684 BOOL fSmcEnabled;
1685 fSmcEnabled = fOsXGuest;
1686 if (fSmcEnabled)
1687 {
1688 InsertConfigNode(pDevices, "smc", &pDev);
1689 InsertConfigNode(pDev, "0", &pInst);
1690 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1691 InsertConfigNode(pInst, "Config", &pCfg);
1692
1693 bool fGetKeyFromRealSMC;
1694 Utf8Str strKey;
1695 rc = getSmcDeviceKey(virtualBox, pMachine, &strKey, &fGetKeyFromRealSMC);
1696 AssertRCReturn(rc, rc);
1697
1698 if (!fGetKeyFromRealSMC)
1699 InsertConfigString(pCfg, "DeviceKey", strKey);
1700 InsertConfigInteger(pCfg, "GetKeyFromRealSMC", fGetKeyFromRealSMC);
1701 }
1702
1703 /*
1704 * Low Pin Count (LPC) bus
1705 */
1706 BOOL fLpcEnabled;
1707 /** @todo implement appropriate getter */
1708 fLpcEnabled = fOsXGuest || (chipsetType == ChipsetType_ICH9);
1709 if (fLpcEnabled)
1710 {
1711 InsertConfigNode(pDevices, "lpc", &pDev);
1712 InsertConfigNode(pDev, "0", &pInst);
1713 hrc = pBusMgr->assignPCIDevice("lpc", pInst); H();
1714 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1715 }
1716
1717 BOOL fShowRtc;
1718 fShowRtc = fOsXGuest || (chipsetType == ChipsetType_ICH9);
1719
1720 /*
1721 * PS/2 keyboard & mouse.
1722 */
1723 InsertConfigNode(pDevices, "pckbd", &pDev);
1724 InsertConfigNode(pDev, "0", &pInst);
1725 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1726 InsertConfigNode(pInst, "Config", &pCfg);
1727
1728 KeyboardHIDType_T aKbdHID;
1729 hrc = pMachine->COMGETTER(KeyboardHIDType)(&aKbdHID); H();
1730 if (aKbdHID != KeyboardHIDType_None)
1731 {
1732 InsertConfigNode(pInst, "LUN#0", &pLunL0);
1733 InsertConfigString(pLunL0, "Driver", "KeyboardQueue");
1734 InsertConfigNode(pLunL0, "Config", &pCfg);
1735 InsertConfigInteger(pCfg, "QueueSize", 64);
1736
1737 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
1738 InsertConfigString(pLunL1, "Driver", "MainKeyboard");
1739 }
1740
1741 PointingHIDType_T aPointingHID;
1742 hrc = pMachine->COMGETTER(PointingHIDType)(&aPointingHID); H();
1743 if (aPointingHID != PointingHIDType_None)
1744 {
1745 InsertConfigNode(pInst, "LUN#1", &pLunL0);
1746 InsertConfigString(pLunL0, "Driver", "MouseQueue");
1747 InsertConfigNode(pLunL0, "Config", &pCfg);
1748 InsertConfigInteger(pCfg, "QueueSize", 128);
1749
1750 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
1751 InsertConfigString(pLunL1, "Driver", "MainMouse");
1752 }
1753
1754 /*
1755 * i8254 Programmable Interval Timer And Dummy Speaker
1756 */
1757 InsertConfigNode(pDevices, "i8254", &pDev);
1758 InsertConfigNode(pDev, "0", &pInst);
1759 InsertConfigNode(pInst, "Config", &pCfg);
1760#ifdef DEBUG
1761 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1762#endif
1763
1764 /*
1765 * i8259 Programmable Interrupt Controller.
1766 */
1767 InsertConfigNode(pDevices, "i8259", &pDev);
1768 InsertConfigNode(pDev, "0", &pInst);
1769 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1770 InsertConfigNode(pInst, "Config", &pCfg);
1771
1772 /*
1773 * Advanced Programmable Interrupt Controller.
1774 * SMP: Each CPU has a LAPIC, but we have a single device representing all LAPICs states,
1775 * thus only single insert
1776 */
1777 if (fEnableAPIC)
1778 {
1779 InsertConfigNode(pDevices, "apic", &pDev);
1780 InsertConfigNode(pDev, "0", &pInst);
1781 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1782 InsertConfigNode(pInst, "Config", &pCfg);
1783 InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC);
1784 PDMAPICMODE enmAPICMode = PDMAPICMODE_APIC;
1785 if (fEnableX2APIC)
1786 enmAPICMode = PDMAPICMODE_X2APIC;
1787 else if (!fEnableAPIC)
1788 enmAPICMode = PDMAPICMODE_NONE;
1789 InsertConfigInteger(pCfg, "Mode", enmAPICMode);
1790 InsertConfigInteger(pCfg, "NumCPUs", cCpus);
1791
1792 if (fIOAPIC)
1793 {
1794 /*
1795 * I/O Advanced Programmable Interrupt Controller.
1796 */
1797 InsertConfigNode(pDevices, "ioapic", &pDev);
1798 InsertConfigNode(pDev, "0", &pInst);
1799 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1800 InsertConfigNode(pInst, "Config", &pCfg);
1801 InsertConfigInteger(pCfg, "NumCPUs", cCpus);
1802 if (enmIommuType == IommuType_AMD)
1803 InsertConfigInteger(pCfg, "PCIAddress", uIoApicPciAddress);
1804 else if (enmIommuType == IommuType_Intel)
1805 {
1806 InsertConfigString(pCfg, "ChipType", "DMAR");
1807 InsertConfigInteger(pCfg, "PCIAddress", uIoApicPciAddress);
1808 }
1809 }
1810 }
1811
1812 /*
1813 * RTC MC146818.
1814 */
1815 InsertConfigNode(pDevices, "mc146818", &pDev);
1816 InsertConfigNode(pDev, "0", &pInst);
1817 InsertConfigNode(pInst, "Config", &pCfg);
1818 BOOL fRTCUseUTC;
1819 hrc = pMachine->COMGETTER(RTCUseUTC)(&fRTCUseUTC); H();
1820 InsertConfigInteger(pCfg, "UseUTC", fRTCUseUTC ? 1 : 0);
1821
1822 /*
1823 * VGA.
1824 */
1825 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
1826 hrc = pMachine->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()); H();
1827 GraphicsControllerType_T enmGraphicsController;
1828 hrc = pGraphicsAdapter->COMGETTER(GraphicsControllerType)(&enmGraphicsController); H();
1829 switch (enmGraphicsController)
1830 {
1831 case GraphicsControllerType_Null:
1832 break;
1833#ifdef VBOX_WITH_VMSVGA
1834 case GraphicsControllerType_VMSVGA:
1835 InsertConfigInteger(pHM, "LovelyMesaDrvWorkaround", 1); /* hits someone else logging backdoor. */
1836 InsertConfigInteger(pNEM, "LovelyMesaDrvWorkaround", 1); /* hits someone else logging backdoor. */
1837 RT_FALL_THROUGH();
1838 case GraphicsControllerType_VBoxSVGA:
1839#endif
1840 case GraphicsControllerType_VBoxVGA:
1841 rc = i_configGraphicsController(pDevices, enmGraphicsController, pBusMgr, pMachine, pGraphicsAdapter, biosSettings,
1842 RT_BOOL(fHMEnabled));
1843 if (FAILED(rc))
1844 return rc;
1845 break;
1846 default:
1847 AssertMsgFailed(("Invalid graphicsController=%d\n", enmGraphicsController));
1848 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1849 N_("Invalid graphics controller type '%d'"), enmGraphicsController);
1850 }
1851
1852 /*
1853 * Firmware.
1854 */
1855 FirmwareType_T eFwType = FirmwareType_BIOS;
1856 hrc = pMachine->COMGETTER(FirmwareType)(&eFwType); H();
1857
1858#ifdef VBOX_WITH_EFI
1859 BOOL fEfiEnabled = (eFwType >= FirmwareType_EFI) && (eFwType <= FirmwareType_EFIDUAL);
1860#else
1861 BOOL fEfiEnabled = false;
1862#endif
1863 if (!fEfiEnabled)
1864 {
1865 /*
1866 * PC Bios.
1867 */
1868 InsertConfigNode(pDevices, "pcbios", &pDev);
1869 InsertConfigNode(pDev, "0", &pInst);
1870 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
1871 InsertConfigNode(pInst, "Config", &pBiosCfg);
1872 InsertConfigInteger(pBiosCfg, "NumCPUs", cCpus);
1873 InsertConfigString(pBiosCfg, "HardDiskDevice", "piix3ide");
1874 InsertConfigString(pBiosCfg, "FloppyDevice", "i82078");
1875 InsertConfigInteger(pBiosCfg, "IOAPIC", fIOAPIC);
1876 InsertConfigInteger(pBiosCfg, "APIC", uFwAPIC);
1877 BOOL fPXEDebug;
1878 hrc = biosSettings->COMGETTER(PXEDebugEnabled)(&fPXEDebug); H();
1879 InsertConfigInteger(pBiosCfg, "PXEDebug", fPXEDebug);
1880 InsertConfigBytes(pBiosCfg, "UUID", &HardwareUuid,sizeof(HardwareUuid));
1881 BOOL fUuidLe;
1882 hrc = biosSettings->COMGETTER(SMBIOSUuidLittleEndian)(&fUuidLe); H();
1883 InsertConfigInteger(pBiosCfg, "UuidLe", fUuidLe);
1884 InsertConfigNode(pBiosCfg, "NetBoot", &pNetBootCfg);
1885 InsertConfigInteger(pBiosCfg, "McfgBase", uMcfgBase);
1886 InsertConfigInteger(pBiosCfg, "McfgLength", cbMcfgLength);
1887
1888 AssertMsgReturn(SchemaDefs::MaxBootPosition <= 9, ("Too many boot devices %d\n", SchemaDefs::MaxBootPosition),
1889 VERR_INVALID_PARAMETER);
1890
1891 for (ULONG pos = 1; pos <= SchemaDefs::MaxBootPosition; ++pos)
1892 {
1893 DeviceType_T enmBootDevice;
1894 hrc = pMachine->GetBootOrder(pos, &enmBootDevice); H();
1895
1896 char szParamName[] = "BootDeviceX";
1897 szParamName[sizeof(szParamName) - 2] = (char)(pos - 1 + '0');
1898
1899 const char *pszBootDevice;
1900 switch (enmBootDevice)
1901 {
1902 case DeviceType_Null:
1903 pszBootDevice = "NONE";
1904 break;
1905 case DeviceType_HardDisk:
1906 pszBootDevice = "IDE";
1907 break;
1908 case DeviceType_DVD:
1909 pszBootDevice = "DVD";
1910 break;
1911 case DeviceType_Floppy:
1912 pszBootDevice = "FLOPPY";
1913 break;
1914 case DeviceType_Network:
1915 pszBootDevice = "LAN";
1916 break;
1917 default:
1918 AssertMsgFailed(("Invalid enmBootDevice=%d\n", enmBootDevice));
1919 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
1920 N_("Invalid boot device '%d'"), enmBootDevice);
1921 }
1922 InsertConfigString(pBiosCfg, szParamName, pszBootDevice);
1923 }
1924
1925 /** @todo @bugref{7145}: We might want to enable this by default for new VMs. For now,
1926 * this is required for Windows 2012 guests. */
1927 if (osTypeId == "Windows2012_64")
1928 InsertConfigInteger(pBiosCfg, "DmiExposeMemoryTable", 1); /* boolean */
1929 }
1930 else
1931 {
1932 /* Autodetect firmware type, basing on guest type */
1933 if (eFwType == FirmwareType_EFI)
1934 eFwType = fIsGuest64Bit ? FirmwareType_EFI64 : FirmwareType_EFI32;
1935 bool const f64BitEntry = eFwType == FirmwareType_EFI64;
1936
1937 Assert(eFwType == FirmwareType_EFI64 || eFwType == FirmwareType_EFI32 || eFwType == FirmwareType_EFIDUAL);
1938#ifdef VBOX_WITH_EFI_IN_DD2
1939 const char *pszEfiRomFile = eFwType == FirmwareType_EFIDUAL ? "VBoxEFIDual.fd"
1940 : eFwType == FirmwareType_EFI32 ? "VBoxEFI32.fd"
1941 : "VBoxEFI64.fd";
1942#else
1943 Utf8Str efiRomFile;
1944 rc = findEfiRom(virtualBox, eFwType, &efiRomFile);
1945 AssertRCReturn(rc, rc);
1946 const char *pszEfiRomFile = efiRomFile.c_str();
1947#endif
1948
1949 /* Get boot args */
1950 Utf8Str bootArgs;
1951 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiBootArgs", &bootArgs);
1952
1953 /* Get device props */
1954 Utf8Str deviceProps;
1955 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiDeviceProps", &deviceProps);
1956
1957 /* Get NVRAM file name */
1958 Utf8Str strNvram = mptrNvramStore->i_getNonVolatileStorageFile();
1959
1960 BOOL fUuidLe;
1961 hrc = biosSettings->COMGETTER(SMBIOSUuidLittleEndian)(&fUuidLe); H();
1962
1963 /* Get graphics mode settings */
1964 uint32_t u32GraphicsMode = UINT32_MAX;
1965 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGraphicsMode", &strTmp);
1966 if (strTmp.isEmpty())
1967 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGopMode", &strTmp);
1968 if (!strTmp.isEmpty())
1969 u32GraphicsMode = strTmp.toUInt32();
1970
1971 /* Get graphics resolution settings, with some sanity checking */
1972 Utf8Str strResolution;
1973 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiGraphicsResolution", &strResolution);
1974 if (!strResolution.isEmpty())
1975 {
1976 size_t pos = strResolution.find("x");
1977 if (pos != strResolution.npos)
1978 {
1979 Utf8Str strH, strV;
1980 strH.assignEx(strResolution, 0, pos);
1981 strV.assignEx(strResolution, pos+1, strResolution.length()-pos-1);
1982 uint32_t u32H = strH.toUInt32();
1983 uint32_t u32V = strV.toUInt32();
1984 if (u32H == 0 || u32V == 0)
1985 strResolution.setNull();
1986 }
1987 else
1988 strResolution.setNull();
1989 }
1990 else
1991 {
1992 uint32_t u32H = 0;
1993 uint32_t u32V = 0;
1994 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiHorizontalResolution", &strTmp);
1995 if (strTmp.isEmpty())
1996 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiUgaHorizontalResolution", &strTmp);
1997 if (!strTmp.isEmpty())
1998 u32H = strTmp.toUInt32();
1999
2000 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiVerticalResolution", &strTmp);
2001 if (strTmp.isEmpty())
2002 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/EfiUgaVerticalResolution", &strTmp);
2003 if (!strTmp.isEmpty())
2004 u32V = strTmp.toUInt32();
2005 if (u32H != 0 && u32V != 0)
2006 strResolution = Utf8StrFmt("%ux%u", u32H, u32V);
2007 }
2008
2009 /*
2010 * EFI subtree.
2011 */
2012 InsertConfigNode(pDevices, "efi", &pDev);
2013 InsertConfigNode(pDev, "0", &pInst);
2014 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
2015 InsertConfigNode(pInst, "Config", &pCfg);
2016 InsertConfigInteger(pCfg, "NumCPUs", cCpus);
2017 InsertConfigInteger(pCfg, "McfgBase", uMcfgBase);
2018 InsertConfigInteger(pCfg, "McfgLength", cbMcfgLength);
2019 InsertConfigString(pCfg, "EfiRom", pszEfiRomFile);
2020 InsertConfigString(pCfg, "BootArgs", bootArgs);
2021 InsertConfigString(pCfg, "DeviceProps", deviceProps);
2022 InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC);
2023 InsertConfigInteger(pCfg, "APIC", uFwAPIC);
2024 InsertConfigBytes(pCfg, "UUID", &HardwareUuid,sizeof(HardwareUuid));
2025 InsertConfigInteger(pCfg, "UuidLe", fUuidLe);
2026 InsertConfigInteger(pCfg, "64BitEntry", f64BitEntry); /* boolean */
2027 InsertConfigString(pCfg, "NvramFile", strNvram);
2028 if (u32GraphicsMode != UINT32_MAX)
2029 InsertConfigInteger(pCfg, "GraphicsMode", u32GraphicsMode);
2030 if (!strResolution.isEmpty())
2031 InsertConfigString(pCfg, "GraphicsResolution", strResolution);
2032
2033 /* For OS X guests we'll force passing host's DMI info to the guest */
2034 if (fOsXGuest)
2035 {
2036 InsertConfigInteger(pCfg, "DmiUseHostInfo", 1);
2037 InsertConfigInteger(pCfg, "DmiExposeMemoryTable", 1);
2038 }
2039
2040 /* Attach the NVRAM storage driver. */
2041 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2042 InsertConfigString(pLunL0, "Driver", "NvramStore");
2043 }
2044
2045 /*
2046 * The USB Controllers.
2047 */
2048 com::SafeIfaceArray<IUSBController> usbCtrls;
2049 hrc = pMachine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbCtrls));
2050 bool fOhciPresent = false; /**< Flag whether at least one OHCI controller is present. */
2051 bool fXhciPresent = false; /**< Flag whether at least one XHCI controller is present. */
2052
2053 if (SUCCEEDED(hrc))
2054 {
2055 for (size_t i = 0; i < usbCtrls.size(); ++i)
2056 {
2057 USBControllerType_T enmCtrlType;
2058 rc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H();
2059 if (enmCtrlType == USBControllerType_OHCI)
2060 {
2061 fOhciPresent = true;
2062 break;
2063 }
2064 else if (enmCtrlType == USBControllerType_XHCI)
2065 {
2066 fXhciPresent = true;
2067 break;
2068 }
2069 }
2070 }
2071 else if (hrc != E_NOTIMPL)
2072 {
2073 H();
2074 }
2075
2076 /*
2077 * Currently EHCI is only enabled when an OHCI or XHCI controller is present as well.
2078 */
2079 if (fOhciPresent || fXhciPresent)
2080 mfVMHasUsbController = true;
2081
2082 PCFGMNODE pUsbDevices = NULL; /**< Required for USB storage controller later. */
2083 if (mfVMHasUsbController)
2084 {
2085 for (size_t i = 0; i < usbCtrls.size(); ++i)
2086 {
2087 USBControllerType_T enmCtrlType;
2088 rc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H();
2089
2090 if (enmCtrlType == USBControllerType_OHCI)
2091 {
2092 InsertConfigNode(pDevices, "usb-ohci", &pDev);
2093 InsertConfigNode(pDev, "0", &pInst);
2094 InsertConfigNode(pInst, "Config", &pCfg);
2095 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
2096 hrc = pBusMgr->assignPCIDevice("usb-ohci", pInst); H();
2097 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2098 InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
2099 InsertConfigNode(pLunL0, "Config", &pCfg);
2100
2101 /*
2102 * Attach the status driver.
2103 */
2104 i_attachStatusDriver(pInst, DeviceType_USB, 0, 0, NULL, NULL, NULL, 0);
2105 }
2106#ifdef VBOX_WITH_EHCI
2107 else if (enmCtrlType == USBControllerType_EHCI)
2108 {
2109 /*
2110 * USB 2.0 is only available if the proper ExtPack is installed.
2111 *
2112 * Note. Configuring EHCI here and providing messages about
2113 * the missing extpack isn't exactly clean, but it is a
2114 * necessary evil to patch over legacy compatability issues
2115 * introduced by the new distribution model.
2116 */
2117# ifdef VBOX_WITH_EXTPACK
2118 static const char *s_pszUsbExtPackName = "Oracle VM VirtualBox Extension Pack";
2119 if (mptrExtPackManager->i_isExtPackUsable(s_pszUsbExtPackName))
2120# endif
2121 {
2122 InsertConfigNode(pDevices, "usb-ehci", &pDev);
2123 InsertConfigNode(pDev, "0", &pInst);
2124 InsertConfigNode(pInst, "Config", &pCfg);
2125 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
2126 hrc = pBusMgr->assignPCIDevice("usb-ehci", pInst); H();
2127
2128 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2129 InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
2130 InsertConfigNode(pLunL0, "Config", &pCfg);
2131
2132 /*
2133 * Attach the status driver.
2134 */
2135 i_attachStatusDriver(pInst, DeviceType_USB, 0, 0, NULL, NULL, NULL, 0);
2136 }
2137# ifdef VBOX_WITH_EXTPACK
2138 else
2139 {
2140 /* Always fatal! Up to VBox 4.0.4 we allowed to start the VM anyway
2141 * but this induced problems when the user saved + restored the VM! */
2142 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
2143 N_("Implementation of the USB 2.0 controller not found!\n"
2144 "Because the USB 2.0 controller state is part of the saved "
2145 "VM state, the VM cannot be started. To fix "
2146 "this problem, either install the '%s' or disable USB 2.0 "
2147 "support in the VM settings.\n"
2148 "Note! This error could also mean that an incompatible version of "
2149 "the '%s' is installed"),
2150 s_pszUsbExtPackName, s_pszUsbExtPackName);
2151 }
2152# endif
2153 }
2154#endif
2155 else if (enmCtrlType == USBControllerType_XHCI)
2156 {
2157 /*
2158 * USB 3.0 is only available if the proper ExtPack is installed.
2159 *
2160 * Note. Configuring EHCI here and providing messages about
2161 * the missing extpack isn't exactly clean, but it is a
2162 * necessary evil to patch over legacy compatability issues
2163 * introduced by the new distribution model.
2164 */
2165# ifdef VBOX_WITH_EXTPACK
2166 static const char *s_pszUsbExtPackName = "Oracle VM VirtualBox Extension Pack";
2167 if (mptrExtPackManager->i_isExtPackUsable(s_pszUsbExtPackName))
2168# endif
2169 {
2170 InsertConfigNode(pDevices, "usb-xhci", &pDev);
2171 InsertConfigNode(pDev, "0", &pInst);
2172 InsertConfigNode(pInst, "Config", &pCfg);
2173 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
2174 hrc = pBusMgr->assignPCIDevice("usb-xhci", pInst); H();
2175
2176 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2177 InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
2178 InsertConfigNode(pLunL0, "Config", &pCfg);
2179
2180 InsertConfigNode(pInst, "LUN#1", &pLunL1);
2181 InsertConfigString(pLunL1, "Driver", "VUSBRootHub");
2182 InsertConfigNode(pLunL1, "Config", &pCfg);
2183
2184 /*
2185 * Attach the status driver.
2186 */
2187 i_attachStatusDriver(pInst, DeviceType_USB, 0, 1, NULL, NULL, NULL, 0);
2188 }
2189# ifdef VBOX_WITH_EXTPACK
2190 else
2191 {
2192 /* Always fatal. */
2193 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
2194 N_("Implementation of the USB 3.0 controller not found!\n"
2195 "Because the USB 3.0 controller state is part of the saved "
2196 "VM state, the VM cannot be started. To fix "
2197 "this problem, either install the '%s' or disable USB 3.0 "
2198 "support in the VM settings"),
2199 s_pszUsbExtPackName);
2200 }
2201# endif
2202 }
2203 } /* for every USB controller. */
2204
2205
2206 /*
2207 * Virtual USB Devices.
2208 */
2209 InsertConfigNode(pRoot, "USB", &pUsbDevices);
2210
2211#ifdef VBOX_WITH_USB
2212 {
2213 /*
2214 * Global USB options, currently unused as we'll apply the 2.0 -> 1.1 morphing
2215 * on a per device level now.
2216 */
2217 InsertConfigNode(pUsbDevices, "USBProxy", &pCfg);
2218 InsertConfigNode(pCfg, "GlobalConfig", &pCfg);
2219 // This globally enables the 2.0 -> 1.1 device morphing of proxied devices to keep windows quiet.
2220 //InsertConfigInteger(pCfg, "Force11Device", true);
2221 // The following breaks stuff, but it makes MSDs work in vista. (I include it here so
2222 // that it's documented somewhere.) Users needing it can use:
2223 // VBoxManage setextradata "myvm" "VBoxInternal/USB/USBProxy/GlobalConfig/Force11PacketSize" 1
2224 //InsertConfigInteger(pCfg, "Force11PacketSize", true);
2225 }
2226#endif
2227
2228#ifdef VBOX_WITH_USB_CARDREADER
2229 BOOL aEmulatedUSBCardReaderEnabled = FALSE;
2230 hrc = pMachine->COMGETTER(EmulatedUSBCardReaderEnabled)(&aEmulatedUSBCardReaderEnabled); H();
2231 if (aEmulatedUSBCardReaderEnabled)
2232 {
2233 InsertConfigNode(pUsbDevices, "CardReader", &pDev);
2234 InsertConfigNode(pDev, "0", &pInst);
2235 InsertConfigNode(pInst, "Config", &pCfg);
2236
2237 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2238# ifdef VBOX_WITH_USB_CARDREADER_TEST
2239 InsertConfigString(pLunL0, "Driver", "DrvDirectCardReader");
2240 InsertConfigNode(pLunL0, "Config", &pCfg);
2241# else
2242 InsertConfigString(pLunL0, "Driver", "UsbCardReader");
2243 InsertConfigNode(pLunL0, "Config", &pCfg);
2244 InsertConfigInteger(pCfg, "Object", (uintptr_t)mUsbCardReader);
2245# endif
2246 }
2247#endif
2248
2249 /* Virtual USB Mouse/Tablet */
2250 if ( aPointingHID == PointingHIDType_USBMouse
2251 || aPointingHID == PointingHIDType_USBTablet
2252 || aPointingHID == PointingHIDType_USBMultiTouch)
2253 {
2254 InsertConfigNode(pUsbDevices, "HidMouse", &pDev);
2255 InsertConfigNode(pDev, "0", &pInst);
2256 InsertConfigNode(pInst, "Config", &pCfg);
2257
2258 if (aPointingHID == PointingHIDType_USBMouse)
2259 InsertConfigString(pCfg, "Mode", "relative");
2260 else
2261 InsertConfigString(pCfg, "Mode", "absolute");
2262 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2263 InsertConfigString(pLunL0, "Driver", "MouseQueue");
2264 InsertConfigNode(pLunL0, "Config", &pCfg);
2265 InsertConfigInteger(pCfg, "QueueSize", 128);
2266
2267 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
2268 InsertConfigString(pLunL1, "Driver", "MainMouse");
2269 }
2270 if (aPointingHID == PointingHIDType_USBMultiTouch)
2271 {
2272 InsertConfigNode(pDev, "1", &pInst);
2273 InsertConfigNode(pInst, "Config", &pCfg);
2274
2275 InsertConfigString(pCfg, "Mode", "multitouch");
2276 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2277 InsertConfigString(pLunL0, "Driver", "MouseQueue");
2278 InsertConfigNode(pLunL0, "Config", &pCfg);
2279 InsertConfigInteger(pCfg, "QueueSize", 128);
2280
2281 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
2282 InsertConfigString(pLunL1, "Driver", "MainMouse");
2283 }
2284
2285 /* Virtual USB Keyboard */
2286 if (aKbdHID == KeyboardHIDType_USBKeyboard)
2287 {
2288 InsertConfigNode(pUsbDevices, "HidKeyboard", &pDev);
2289 InsertConfigNode(pDev, "0", &pInst);
2290 InsertConfigNode(pInst, "Config", &pCfg);
2291
2292 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2293 InsertConfigString(pLunL0, "Driver", "KeyboardQueue");
2294 InsertConfigNode(pLunL0, "Config", &pCfg);
2295 InsertConfigInteger(pCfg, "QueueSize", 64);
2296
2297 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
2298 InsertConfigString(pLunL1, "Driver", "MainKeyboard");
2299 }
2300 }
2301
2302 /*
2303 * Storage controllers.
2304 */
2305 com::SafeIfaceArray<IStorageController> ctrls;
2306 PCFGMNODE aCtrlNodes[StorageControllerType_VirtioSCSI + 1] = {};
2307 hrc = pMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls)); H();
2308
2309 bool fFdcEnabled = false;
2310 for (size_t i = 0; i < ctrls.size(); ++i)
2311 {
2312 DeviceType_T *paLedDevType = NULL;
2313
2314 StorageControllerType_T enmCtrlType;
2315 rc = ctrls[i]->COMGETTER(ControllerType)(&enmCtrlType); H();
2316 AssertRelease((unsigned)enmCtrlType < RT_ELEMENTS(aCtrlNodes)
2317 || enmCtrlType == StorageControllerType_USB);
2318
2319 StorageBus_T enmBus;
2320 rc = ctrls[i]->COMGETTER(Bus)(&enmBus); H();
2321
2322 Bstr controllerName;
2323 rc = ctrls[i]->COMGETTER(Name)(controllerName.asOutParam()); H();
2324
2325 ULONG ulInstance = 999;
2326 rc = ctrls[i]->COMGETTER(Instance)(&ulInstance); H();
2327
2328 BOOL fUseHostIOCache;
2329 rc = ctrls[i]->COMGETTER(UseHostIOCache)(&fUseHostIOCache); H();
2330
2331 BOOL fBootable;
2332 rc = ctrls[i]->COMGETTER(Bootable)(&fBootable); H();
2333
2334 PCFGMNODE pCtlInst = NULL;
2335 const char *pszCtrlDev = i_storageControllerTypeToStr(enmCtrlType);
2336 if (enmCtrlType != StorageControllerType_USB)
2337 {
2338 /* /Devices/<ctrldev>/ */
2339 pDev = aCtrlNodes[enmCtrlType];
2340 if (!pDev)
2341 {
2342 InsertConfigNode(pDevices, pszCtrlDev, &pDev);
2343 aCtrlNodes[enmCtrlType] = pDev; /* IDE variants are handled in the switch */
2344 }
2345
2346 /* /Devices/<ctrldev>/<instance>/ */
2347 InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pCtlInst);
2348
2349 /* Device config: /Devices/<ctrldev>/<instance>/<values> & /ditto/Config/<values> */
2350 InsertConfigInteger(pCtlInst, "Trusted", 1);
2351 InsertConfigNode(pCtlInst, "Config", &pCfg);
2352 }
2353
2354 static const char * const apszBiosConfigScsi[MAX_BIOS_LUN_COUNT] =
2355 { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
2356
2357 static const char * const apszBiosConfigSata[MAX_BIOS_LUN_COUNT] =
2358 { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
2359
2360 switch (enmCtrlType)
2361 {
2362 case StorageControllerType_LsiLogic:
2363 {
2364 hrc = pBusMgr->assignPCIDevice("lsilogic", pCtlInst); H();
2365
2366 InsertConfigInteger(pCfg, "Bootable", fBootable);
2367
2368 /* BIOS configuration values, first SCSI controller only. */
2369 if ( !pBusMgr->hasPCIDevice("lsilogic", 1)
2370 && !pBusMgr->hasPCIDevice("buslogic", 0)
2371 && !pBusMgr->hasPCIDevice("lsilogicsas", 0)
2372 && pBiosCfg)
2373 {
2374 InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicscsi");
2375 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
2376 }
2377
2378 /* Attach the status driver */
2379 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 15, &paLedDevType,
2380 &mapMediumAttachments, pszCtrlDev, ulInstance);
2381 break;
2382 }
2383
2384 case StorageControllerType_BusLogic:
2385 {
2386 hrc = pBusMgr->assignPCIDevice("buslogic", pCtlInst); H();
2387
2388 InsertConfigInteger(pCfg, "Bootable", fBootable);
2389
2390 /* BIOS configuration values, first SCSI controller only. */
2391 if ( !pBusMgr->hasPCIDevice("lsilogic", 0)
2392 && !pBusMgr->hasPCIDevice("buslogic", 1)
2393 && !pBusMgr->hasPCIDevice("lsilogicsas", 0)
2394 && pBiosCfg)
2395 {
2396 InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "buslogic");
2397 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
2398 }
2399
2400 /* Attach the status driver */
2401 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 15, &paLedDevType,
2402 &mapMediumAttachments, pszCtrlDev, ulInstance);
2403 break;
2404 }
2405
2406 case StorageControllerType_IntelAhci:
2407 {
2408 hrc = pBusMgr->assignPCIDevice("ahci", pCtlInst); H();
2409
2410 ULONG cPorts = 0;
2411 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
2412 InsertConfigInteger(pCfg, "PortCount", cPorts);
2413 InsertConfigInteger(pCfg, "Bootable", fBootable);
2414
2415 com::SafeIfaceArray<IMediumAttachment> atts;
2416 hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(),
2417 ComSafeArrayAsOutParam(atts)); H();
2418
2419 /* Configure the hotpluggable flag for the port. */
2420 for (unsigned idxAtt = 0; idxAtt < atts.size(); ++idxAtt)
2421 {
2422 IMediumAttachment *pMediumAtt = atts[idxAtt];
2423
2424 LONG lPortNum = 0;
2425 hrc = pMediumAtt->COMGETTER(Port)(&lPortNum); H();
2426
2427 BOOL fHotPluggable = FALSE;
2428 hrc = pMediumAtt->COMGETTER(HotPluggable)(&fHotPluggable); H();
2429 if (SUCCEEDED(hrc))
2430 {
2431 PCFGMNODE pPortCfg;
2432 char szName[24];
2433 RTStrPrintf(szName, sizeof(szName), "Port%d", lPortNum);
2434
2435 InsertConfigNode(pCfg, szName, &pPortCfg);
2436 InsertConfigInteger(pPortCfg, "Hotpluggable", fHotPluggable ? 1 : 0);
2437 }
2438 }
2439
2440 /* BIOS configuration values, first AHCI controller only. */
2441 if ( !pBusMgr->hasPCIDevice("ahci", 1)
2442 && pBiosCfg)
2443 {
2444 InsertConfigString(pBiosCfg, "SataHardDiskDevice", "ahci");
2445 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigSata); H();
2446 }
2447
2448 /* Attach the status driver */
2449 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, cPorts - 1, &paLedDevType,
2450 &mapMediumAttachments, pszCtrlDev, ulInstance);
2451 break;
2452 }
2453
2454 case StorageControllerType_PIIX3:
2455 case StorageControllerType_PIIX4:
2456 case StorageControllerType_ICH6:
2457 {
2458 /*
2459 * IDE (update this when the main interface changes)
2460 */
2461 hrc = pBusMgr->assignPCIDevice("piix3ide", pCtlInst); H();
2462 InsertConfigString(pCfg, "Type", controllerString(enmCtrlType));
2463 /* Attach the status driver */
2464 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 3, &paLedDevType,
2465 &mapMediumAttachments, pszCtrlDev, ulInstance);
2466
2467 /* IDE flavors */
2468 aCtrlNodes[StorageControllerType_PIIX3] = pDev;
2469 aCtrlNodes[StorageControllerType_PIIX4] = pDev;
2470 aCtrlNodes[StorageControllerType_ICH6] = pDev;
2471 break;
2472 }
2473
2474 case StorageControllerType_I82078:
2475 {
2476 /*
2477 * i82078 Floppy drive controller
2478 */
2479 fFdcEnabled = true;
2480 InsertConfigInteger(pCfg, "IRQ", 6);
2481 InsertConfigInteger(pCfg, "DMA", 2);
2482 InsertConfigInteger(pCfg, "MemMapped", 0 );
2483 InsertConfigInteger(pCfg, "IOBase", 0x3f0);
2484
2485 /* Attach the status driver */
2486 i_attachStatusDriver(pCtlInst, DeviceType_Floppy, 0, 1, NULL,
2487 &mapMediumAttachments, pszCtrlDev, ulInstance);
2488 break;
2489 }
2490
2491 case StorageControllerType_LsiLogicSas:
2492 {
2493 hrc = pBusMgr->assignPCIDevice("lsilogicsas", pCtlInst); H();
2494
2495 InsertConfigString(pCfg, "ControllerType", "SAS1068");
2496 InsertConfigInteger(pCfg, "Bootable", fBootable);
2497
2498 /* BIOS configuration values, first SCSI controller only. */
2499 if ( !pBusMgr->hasPCIDevice("lsilogic", 0)
2500 && !pBusMgr->hasPCIDevice("buslogic", 0)
2501 && !pBusMgr->hasPCIDevice("lsilogicsas", 1)
2502 && pBiosCfg)
2503 {
2504 InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicsas");
2505 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
2506 }
2507
2508 ULONG cPorts = 0;
2509 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
2510 InsertConfigInteger(pCfg, "NumPorts", cPorts);
2511
2512 /* Attach the status driver */
2513 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 7, &paLedDevType,
2514 &mapMediumAttachments, pszCtrlDev, ulInstance);
2515 break;
2516 }
2517
2518 case StorageControllerType_USB:
2519 {
2520 if (pUsbDevices)
2521 {
2522 /*
2523 * USB MSDs are handled a bit different as the device instance
2524 * doesn't match the storage controller instance but the port.
2525 */
2526 InsertConfigNode(pUsbDevices, "Msd", &pDev);
2527 pCtlInst = pDev;
2528 }
2529 else
2530 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
2531 N_("There is no USB controller enabled but there\n"
2532 "is at least one USB storage device configured for this VM.\n"
2533 "To fix this problem either enable the USB controller or remove\n"
2534 "the storage device from the VM"));
2535 break;
2536 }
2537
2538 case StorageControllerType_NVMe:
2539 {
2540 hrc = pBusMgr->assignPCIDevice("nvme", pCtlInst); H();
2541
2542 ULONG cPorts = 0;
2543 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
2544 InsertConfigInteger(pCfg, "NamespacesMax", cPorts);
2545
2546 /* Attach the status driver */
2547 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, cPorts - 1, NULL,
2548 &mapMediumAttachments, pszCtrlDev, ulInstance);
2549 break;
2550 }
2551
2552 case StorageControllerType_VirtioSCSI:
2553 {
2554 hrc = pBusMgr->assignPCIDevice("virtio-scsi", pCtlInst); H();
2555
2556 ULONG cPorts = 0;
2557 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
2558 InsertConfigInteger(pCfg, "NumTargets", cPorts);
2559 InsertConfigInteger(pCfg, "Bootable", fBootable);
2560
2561 /* Attach the status driver */
2562 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, cPorts - 1, &paLedDevType,
2563 &mapMediumAttachments, pszCtrlDev, ulInstance);
2564 break;
2565 }
2566
2567 default:
2568 AssertLogRelMsgFailedReturn(("invalid storage controller type: %d\n", enmCtrlType), VERR_MAIN_CONFIG_CONSTRUCTOR_IPE);
2569 }
2570
2571 /* Attach the media to the storage controllers. */
2572 com::SafeIfaceArray<IMediumAttachment> atts;
2573 hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(),
2574 ComSafeArrayAsOutParam(atts)); H();
2575
2576 /* Builtin I/O cache - per device setting. */
2577 BOOL fBuiltinIOCache = true;
2578 hrc = pMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache); H();
2579
2580 bool fInsertDiskIntegrityDrv = false;
2581 Bstr strDiskIntegrityFlag;
2582 hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EnableDiskIntegrityDriver").raw(),
2583 strDiskIntegrityFlag.asOutParam());
2584 if ( hrc == S_OK
2585 && strDiskIntegrityFlag == "1")
2586 fInsertDiskIntegrityDrv = true;
2587
2588 for (size_t j = 0; j < atts.size(); ++j)
2589 {
2590 IMediumAttachment *pMediumAtt = atts[j];
2591 rc = i_configMediumAttachment(pszCtrlDev,
2592 ulInstance,
2593 enmBus,
2594 !!fUseHostIOCache,
2595 enmCtrlType == StorageControllerType_NVMe ? false : !!fBuiltinIOCache,
2596 fInsertDiskIntegrityDrv,
2597 false /* fSetupMerge */,
2598 0 /* uMergeSource */,
2599 0 /* uMergeTarget */,
2600 pMediumAtt,
2601 mMachineState,
2602 NULL /* phrc */,
2603 false /* fAttachDetach */,
2604 false /* fForceUnmount */,
2605 false /* fHotplug */,
2606 pUVM,
2607 pVMM,
2608 paLedDevType,
2609 NULL /* ppLunL0 */);
2610 if (RT_FAILURE(rc))
2611 return rc;
2612 }
2613 H();
2614 }
2615 H();
2616
2617 /*
2618 * Network adapters
2619 */
2620#ifdef VMWARE_NET_IN_SLOT_11
2621 bool fSwapSlots3and11 = false;
2622#endif
2623 PCFGMNODE pDevPCNet = NULL; /* PCNet-type devices */
2624 InsertConfigNode(pDevices, "pcnet", &pDevPCNet);
2625#ifdef VBOX_WITH_E1000
2626 PCFGMNODE pDevE1000 = NULL; /* E1000-type devices */
2627 InsertConfigNode(pDevices, "e1000", &pDevE1000);
2628#endif
2629#ifdef VBOX_WITH_VIRTIO
2630 PCFGMNODE pDevVirtioNet = NULL; /* Virtio network devices */
2631 InsertConfigNode(pDevices, "virtio-net", &pDevVirtioNet);
2632#endif /* VBOX_WITH_VIRTIO */
2633 PCFGMNODE pDevDP8390 = NULL; /* DP8390-type devices */
2634 InsertConfigNode(pDevices, "dp8390", &pDevDP8390);
2635 PCFGMNODE pDev3C501 = NULL; /* EtherLink-type devices */
2636 InsertConfigNode(pDevices, "3c501", &pDev3C501);
2637
2638 std::list<BootNic> llBootNics;
2639 for (ULONG uInstance = 0; uInstance < maxNetworkAdapters; ++uInstance)
2640 {
2641 ComPtr<INetworkAdapter> networkAdapter;
2642 hrc = pMachine->GetNetworkAdapter(uInstance, networkAdapter.asOutParam()); H();
2643 BOOL fEnabledNetAdapter = FALSE;
2644 hrc = networkAdapter->COMGETTER(Enabled)(&fEnabledNetAdapter); H();
2645 if (!fEnabledNetAdapter)
2646 continue;
2647
2648 /*
2649 * The virtual hardware type. Create appropriate device first.
2650 */
2651 const char *pszAdapterName = "pcnet";
2652 NetworkAdapterType_T adapterType;
2653 hrc = networkAdapter->COMGETTER(AdapterType)(&adapterType); H();
2654 switch (adapterType)
2655 {
2656 case NetworkAdapterType_Am79C970A:
2657 case NetworkAdapterType_Am79C973:
2658 case NetworkAdapterType_Am79C960:
2659 pDev = pDevPCNet;
2660 break;
2661#ifdef VBOX_WITH_E1000
2662 case NetworkAdapterType_I82540EM:
2663 case NetworkAdapterType_I82543GC:
2664 case NetworkAdapterType_I82545EM:
2665 pDev = pDevE1000;
2666 pszAdapterName = "e1000";
2667 break;
2668#endif
2669#ifdef VBOX_WITH_VIRTIO
2670 case NetworkAdapterType_Virtio:
2671 pDev = pDevVirtioNet;
2672 pszAdapterName = "virtio-net";
2673 break;
2674#endif /* VBOX_WITH_VIRTIO */
2675 case NetworkAdapterType_NE1000:
2676 case NetworkAdapterType_NE2000:
2677 case NetworkAdapterType_WD8003:
2678 case NetworkAdapterType_WD8013:
2679 case NetworkAdapterType_ELNK2:
2680 pDev = pDevDP8390;
2681 break;
2682 case NetworkAdapterType_ELNK1:
2683 pDev = pDev3C501;
2684 break;
2685 default:
2686 AssertMsgFailed(("Invalid network adapter type '%d' for slot '%d'", adapterType, uInstance));
2687 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
2688 N_("Invalid network adapter type '%d' for slot '%d'"), adapterType, uInstance);
2689 }
2690
2691 InsertConfigNode(pDev, Utf8StrFmt("%u", uInstance).c_str(), &pInst);
2692 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
2693 /* the first network card gets the PCI ID 3, the next 3 gets 8..10,
2694 * next 4 get 16..19. */
2695 int iPCIDeviceNo;
2696 switch (uInstance)
2697 {
2698 case 0:
2699 iPCIDeviceNo = 3;
2700 break;
2701 case 1: case 2: case 3:
2702 iPCIDeviceNo = uInstance - 1 + 8;
2703 break;
2704 case 4: case 5: case 6: case 7:
2705 iPCIDeviceNo = uInstance - 4 + 16;
2706 break;
2707 default:
2708 /* auto assignment */
2709 iPCIDeviceNo = -1;
2710 break;
2711 }
2712#ifdef VMWARE_NET_IN_SLOT_11
2713 /*
2714 * Dirty hack for PCI slot compatibility with VMWare,
2715 * it assigns slot 0x11 to the first network controller.
2716 */
2717 if (iPCIDeviceNo == 3 && adapterType == NetworkAdapterType_I82545EM)
2718 {
2719 iPCIDeviceNo = 0x11;
2720 fSwapSlots3and11 = true;
2721 }
2722 else if (iPCIDeviceNo == 0x11 && fSwapSlots3and11)
2723 iPCIDeviceNo = 3;
2724#endif
2725 PCIBusAddress PCIAddr = PCIBusAddress(0, iPCIDeviceNo, 0);
2726 hrc = pBusMgr->assignPCIDevice(pszAdapterName, pInst, PCIAddr); H();
2727
2728 InsertConfigNode(pInst, "Config", &pCfg);
2729#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE /* not safe here yet. */ /** @todo Make PCNet ring-0 safe on 32-bit mac kernels! */
2730 if (pDev == pDevPCNet)
2731 InsertConfigInteger(pCfg, "R0Enabled", false);
2732#endif
2733 /*
2734 * Collect information needed for network booting and add it to the list.
2735 */
2736 BootNic nic;
2737
2738 nic.mInstance = uInstance;
2739 /* Could be updated by reference, if auto assigned */
2740 nic.mPCIAddress = PCIAddr;
2741
2742 hrc = networkAdapter->COMGETTER(BootPriority)(&nic.mBootPrio); H();
2743
2744 llBootNics.push_back(nic);
2745
2746 /*
2747 * The virtual hardware type. PCNet supports three types, E1000 three,
2748 * but VirtIO only one.
2749 */
2750 switch (adapterType)
2751 {
2752 case NetworkAdapterType_Am79C970A:
2753 InsertConfigString(pCfg, "ChipType", "Am79C970A");
2754 break;
2755 case NetworkAdapterType_Am79C973:
2756 InsertConfigString(pCfg, "ChipType", "Am79C973");
2757 break;
2758 case NetworkAdapterType_Am79C960:
2759 InsertConfigString(pCfg, "ChipType", "Am79C960");
2760 break;
2761 case NetworkAdapterType_I82540EM:
2762 InsertConfigInteger(pCfg, "AdapterType", 0);
2763 break;
2764 case NetworkAdapterType_I82543GC:
2765 InsertConfigInteger(pCfg, "AdapterType", 1);
2766 break;
2767 case NetworkAdapterType_I82545EM:
2768 InsertConfigInteger(pCfg, "AdapterType", 2);
2769 break;
2770 case NetworkAdapterType_Virtio:
2771 break;
2772 case NetworkAdapterType_NE1000:
2773 InsertConfigString(pCfg, "DeviceType", "NE1000");
2774 break;
2775 case NetworkAdapterType_NE2000:
2776 InsertConfigString(pCfg, "DeviceType", "NE2000");
2777 break;
2778 case NetworkAdapterType_WD8003:
2779 InsertConfigString(pCfg, "DeviceType", "WD8003");
2780 break;
2781 case NetworkAdapterType_WD8013:
2782 InsertConfigString(pCfg, "DeviceType", "WD8013");
2783 break;
2784 case NetworkAdapterType_ELNK2:
2785 InsertConfigString(pCfg, "DeviceType", "3C503");
2786 break;
2787 case NetworkAdapterType_ELNK1:
2788 break;
2789 case NetworkAdapterType_Null: AssertFailedBreak(); /* (compiler warnings) */
2790#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
2791 case NetworkAdapterType_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
2792#endif
2793 }
2794
2795 /*
2796 * Get the MAC address and convert it to binary representation
2797 */
2798 Bstr macAddr;
2799 hrc = networkAdapter->COMGETTER(MACAddress)(macAddr.asOutParam()); H();
2800 Assert(!macAddr.isEmpty());
2801 Utf8Str macAddrUtf8 = macAddr;
2802#ifdef VBOX_WITH_CLOUD_NET
2803 NetworkAttachmentType_T eAttachmentType;
2804 hrc = networkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H();
2805 if (eAttachmentType == NetworkAttachmentType_Cloud)
2806 {
2807 mGateway.setLocalMacAddress(macAddrUtf8);
2808 /* We'll insert cloud MAC later, when it becomes known. */
2809 }
2810 else
2811 {
2812#endif
2813 char *macStr = (char*)macAddrUtf8.c_str();
2814 Assert(strlen(macStr) == 12);
2815 RTMAC Mac;
2816 RT_ZERO(Mac);
2817 char *pMac = (char*)&Mac;
2818 for (uint32_t i = 0; i < 6; ++i)
2819 {
2820 int c1 = *macStr++ - '0';
2821 if (c1 > 9)
2822 c1 -= 7;
2823 int c2 = *macStr++ - '0';
2824 if (c2 > 9)
2825 c2 -= 7;
2826 *pMac++ = (char)(((c1 & 0x0f) << 4) | (c2 & 0x0f));
2827 }
2828 InsertConfigBytes(pCfg, "MAC", &Mac, sizeof(Mac));
2829#ifdef VBOX_WITH_CLOUD_NET
2830 }
2831#endif
2832 /*
2833 * Check if the cable is supposed to be unplugged
2834 */
2835 BOOL fCableConnected;
2836 hrc = networkAdapter->COMGETTER(CableConnected)(&fCableConnected); H();
2837 InsertConfigInteger(pCfg, "CableConnected", fCableConnected ? 1 : 0);
2838
2839 /*
2840 * Line speed to report from custom drivers
2841 */
2842 ULONG ulLineSpeed;
2843 hrc = networkAdapter->COMGETTER(LineSpeed)(&ulLineSpeed); H();
2844 InsertConfigInteger(pCfg, "LineSpeed", ulLineSpeed);
2845
2846 /*
2847 * Attach the status driver.
2848 */
2849 i_attachStatusDriver(pInst, DeviceType_Network, 0, 0, NULL, NULL, NULL, 0);
2850
2851 /*
2852 * Configure the network card now
2853 */
2854 bool fIgnoreConnectFailure = mMachineState == MachineState_Restoring;
2855 rc = i_configNetwork(pszAdapterName,
2856 uInstance,
2857 0,
2858 networkAdapter,
2859 pCfg,
2860 pLunL0,
2861 pInst,
2862 false /*fAttachDetach*/,
2863 fIgnoreConnectFailure,
2864 pUVM,
2865 pVMM);
2866 if (RT_FAILURE(rc))
2867 return rc;
2868 }
2869
2870 /*
2871 * Build network boot information and transfer it to the BIOS.
2872 */
2873 if (pNetBootCfg && !llBootNics.empty()) /* NetBoot node doesn't exist for EFI! */
2874 {
2875 llBootNics.sort(); /* Sort the list by boot priority. */
2876
2877 char achBootIdx[] = "0";
2878 unsigned uBootIdx = 0;
2879
2880 for (std::list<BootNic>::iterator it = llBootNics.begin(); it != llBootNics.end(); ++it)
2881 {
2882 /* A NIC with priority 0 is only used if it's first in the list. */
2883 if (it->mBootPrio == 0 && uBootIdx != 0)
2884 break;
2885
2886 PCFGMNODE pNetBtDevCfg;
2887 achBootIdx[0] = (char)('0' + uBootIdx++); /* Boot device order. */
2888 InsertConfigNode(pNetBootCfg, achBootIdx, &pNetBtDevCfg);
2889 InsertConfigInteger(pNetBtDevCfg, "NIC", it->mInstance);
2890 InsertConfigInteger(pNetBtDevCfg, "PCIBusNo", it->mPCIAddress.miBus);
2891 InsertConfigInteger(pNetBtDevCfg, "PCIDeviceNo", it->mPCIAddress.miDevice);
2892 InsertConfigInteger(pNetBtDevCfg, "PCIFunctionNo", it->mPCIAddress.miFn);
2893 }
2894 }
2895
2896 /*
2897 * Serial (UART) Ports
2898 */
2899 /* serial enabled mask to be passed to dev ACPI */
2900 uint16_t auSerialIoPortBase[SchemaDefs::SerialPortCount] = {0};
2901 uint8_t auSerialIrq[SchemaDefs::SerialPortCount] = {0};
2902 InsertConfigNode(pDevices, "serial", &pDev);
2903 for (ULONG ulInstance = 0; ulInstance < SchemaDefs::SerialPortCount; ++ulInstance)
2904 {
2905 ComPtr<ISerialPort> serialPort;
2906 hrc = pMachine->GetSerialPort(ulInstance, serialPort.asOutParam()); H();
2907 BOOL fEnabledSerPort = FALSE;
2908 if (serialPort)
2909 {
2910 hrc = serialPort->COMGETTER(Enabled)(&fEnabledSerPort); H();
2911 }
2912 if (!fEnabledSerPort)
2913 {
2914 m_aeSerialPortMode[ulInstance] = PortMode_Disconnected;
2915 continue;
2916 }
2917
2918 InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pInst);
2919 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
2920 InsertConfigNode(pInst, "Config", &pCfg);
2921
2922 ULONG ulIRQ;
2923 hrc = serialPort->COMGETTER(IRQ)(&ulIRQ); H();
2924 InsertConfigInteger(pCfg, "IRQ", ulIRQ);
2925 auSerialIrq[ulInstance] = (uint8_t)ulIRQ;
2926
2927 ULONG ulIOBase;
2928 hrc = serialPort->COMGETTER(IOBase)(&ulIOBase); H();
2929 InsertConfigInteger(pCfg, "IOBase", ulIOBase);
2930 auSerialIoPortBase[ulInstance] = (uint16_t)ulIOBase;
2931
2932 BOOL fServer;
2933 hrc = serialPort->COMGETTER(Server)(&fServer); H();
2934 hrc = serialPort->COMGETTER(Path)(bstr.asOutParam()); H();
2935 UartType_T eUartType;
2936 const char *pszUartType;
2937 hrc = serialPort->COMGETTER(UartType)(&eUartType); H();
2938 switch (eUartType)
2939 {
2940 case UartType_U16450: pszUartType = "16450"; break;
2941 case UartType_U16750: pszUartType = "16750"; break;
2942 default: AssertFailed(); RT_FALL_THRU();
2943 case UartType_U16550A: pszUartType = "16550A"; break;
2944 }
2945 InsertConfigString(pCfg, "UartType", pszUartType);
2946
2947 PortMode_T eHostMode;
2948 hrc = serialPort->COMGETTER(HostMode)(&eHostMode); H();
2949
2950 m_aeSerialPortMode[ulInstance] = eHostMode;
2951 if (eHostMode != PortMode_Disconnected)
2952 {
2953 rc = i_configSerialPort(pInst, eHostMode, Utf8Str(bstr).c_str(), RT_BOOL(fServer));
2954 if (RT_FAILURE(rc))
2955 return rc;
2956 }
2957 }
2958
2959 /*
2960 * Parallel (LPT) Ports
2961 */
2962 /* parallel enabled mask to be passed to dev ACPI */
2963 uint16_t auParallelIoPortBase[SchemaDefs::ParallelPortCount] = {0};
2964 uint8_t auParallelIrq[SchemaDefs::ParallelPortCount] = {0};
2965 InsertConfigNode(pDevices, "parallel", &pDev);
2966 for (ULONG ulInstance = 0; ulInstance < SchemaDefs::ParallelPortCount; ++ulInstance)
2967 {
2968 ComPtr<IParallelPort> parallelPort;
2969 hrc = pMachine->GetParallelPort(ulInstance, parallelPort.asOutParam()); H();
2970 BOOL fEnabledParPort = FALSE;
2971 if (parallelPort)
2972 {
2973 hrc = parallelPort->COMGETTER(Enabled)(&fEnabledParPort); H();
2974 }
2975 if (!fEnabledParPort)
2976 continue;
2977
2978 InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pInst);
2979 InsertConfigNode(pInst, "Config", &pCfg);
2980
2981 ULONG ulIRQ;
2982 hrc = parallelPort->COMGETTER(IRQ)(&ulIRQ); H();
2983 InsertConfigInteger(pCfg, "IRQ", ulIRQ);
2984 auParallelIrq[ulInstance] = (uint8_t)ulIRQ;
2985 ULONG ulIOBase;
2986 hrc = parallelPort->COMGETTER(IOBase)(&ulIOBase); H();
2987 InsertConfigInteger(pCfg, "IOBase", ulIOBase);
2988 auParallelIoPortBase[ulInstance] = (uint16_t)ulIOBase;
2989
2990 hrc = parallelPort->COMGETTER(Path)(bstr.asOutParam()); H();
2991 if (!bstr.isEmpty())
2992 {
2993 InsertConfigNode(pInst, "LUN#0", &pLunL0);
2994 InsertConfigString(pLunL0, "Driver", "HostParallel");
2995 InsertConfigNode(pLunL0, "Config", &pLunL1);
2996 InsertConfigString(pLunL1, "DevicePath", bstr);
2997 }
2998 }
2999
3000 /*
3001 * VMM Device
3002 */
3003 InsertConfigNode(pDevices, "VMMDev", &pDev);
3004 InsertConfigNode(pDev, "0", &pInst);
3005 InsertConfigNode(pInst, "Config", &pCfg);
3006 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3007 hrc = pBusMgr->assignPCIDevice("VMMDev", pInst); H();
3008
3009 Bstr hwVersion;
3010 hrc = pMachine->COMGETTER(HardwareVersion)(hwVersion.asOutParam()); H();
3011 if (hwVersion.compare(Bstr("1").raw()) == 0) /* <= 2.0.x */
3012 InsertConfigInteger(pCfg, "HeapEnabled", 0);
3013 Bstr snapshotFolder;
3014 hrc = pMachine->COMGETTER(SnapshotFolder)(snapshotFolder.asOutParam()); H();
3015 InsertConfigString(pCfg, "GuestCoreDumpDir", snapshotFolder);
3016
3017 /* the VMM device's Main driver */
3018 InsertConfigNode(pInst, "LUN#0", &pLunL0);
3019 InsertConfigString(pLunL0, "Driver", "HGCM");
3020 InsertConfigNode(pLunL0, "Config", &pCfg);
3021
3022 /*
3023 * Attach the status driver.
3024 */
3025 i_attachStatusDriver(pInst, DeviceType_SharedFolder, 0, 0, NULL, NULL, NULL, 0);
3026
3027 /*
3028 * Audio configuration.
3029 */
3030
3031 /*
3032 * AC'97 ICH / SoundBlaster16 audio / Intel HD Audio.
3033 */
3034 BOOL fAudioEnabled = FALSE;
3035 ComPtr<IAudioAdapter> audioAdapter;
3036 hrc = pMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); H();
3037 if (audioAdapter)
3038 {
3039 hrc = audioAdapter->COMGETTER(Enabled)(&fAudioEnabled); H();
3040 }
3041
3042 if (fAudioEnabled)
3043 {
3044 AudioControllerType_T enmAudioController;
3045 hrc = audioAdapter->COMGETTER(AudioController)(&enmAudioController); H();
3046 AudioCodecType_T enmAudioCodec;
3047 hrc = audioAdapter->COMGETTER(AudioCodec)(&enmAudioCodec); H();
3048
3049 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Device/TimerHz", &strTmp);
3050 const uint64_t uTimerHz = strTmp.toUInt64();
3051
3052 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Device/BufSizeInMs", &strTmp);
3053 const uint64_t uBufSizeInMs = strTmp.toUInt64();
3054
3055 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Device/BufSizeOutMs", &strTmp);
3056 const uint64_t uBufSizeOutMs = strTmp.toUInt64();
3057
3058 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp);
3059 const bool fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
3060
3061 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Debug/Level", &strTmp);
3062 const uint32_t uDebugLevel = strTmp.toUInt32();
3063
3064 Utf8Str strDebugPathOut;
3065 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut);
3066
3067#ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3068 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/VaKit/Enabled", &strTmp); /* Deprecated; do not use! */
3069 if (strTmp.isEmpty())
3070 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/ValKit/Enabled", &strTmp);
3071 /* Whether the Validation Kit audio backend runs as the primary backend.
3072 * Can also be used with VBox release builds. */
3073 const bool fValKitEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
3074#endif
3075 /** @todo Implement an audio device class, similar to the audio backend class, to construct the common stuff
3076 * without duplicating (more) code. */
3077
3078 const char *pszAudioDevice;
3079 switch (enmAudioController)
3080 {
3081 case AudioControllerType_AC97:
3082 {
3083 /* ICH AC'97. */
3084 pszAudioDevice = "ichac97";
3085
3086 InsertConfigNode(pDevices, pszAudioDevice, &pDev);
3087 InsertConfigNode(pDev, "0", &pInst);
3088 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3089 hrc = pBusMgr->assignPCIDevice(pszAudioDevice, pInst); H();
3090 InsertConfigNode(pInst, "Config", &pCfg);
3091 switch (enmAudioCodec)
3092 {
3093 case AudioCodecType_STAC9700:
3094 InsertConfigString(pCfg, "Codec", "STAC9700");
3095 break;
3096 case AudioCodecType_AD1980:
3097 InsertConfigString(pCfg, "Codec", "AD1980");
3098 break;
3099 default: AssertFailedBreak();
3100 }
3101 if (uTimerHz)
3102 InsertConfigInteger(pCfg, "TimerHz", uTimerHz);
3103 if (uBufSizeInMs)
3104 InsertConfigInteger(pCfg, "BufSizeInMs", uBufSizeInMs);
3105 if (uBufSizeOutMs)
3106 InsertConfigInteger(pCfg, "BufSizeOutMs", uBufSizeOutMs);
3107 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3108 if (strDebugPathOut.isNotEmpty())
3109 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut);
3110 break;
3111 }
3112 case AudioControllerType_SB16:
3113 {
3114 /* Legacy SoundBlaster16. */
3115 pszAudioDevice = "sb16";
3116
3117 InsertConfigNode(pDevices, pszAudioDevice, &pDev);
3118 InsertConfigNode(pDev, "0", &pInst);
3119 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3120 InsertConfigNode(pInst, "Config", &pCfg);
3121 InsertConfigInteger(pCfg, "IRQ", 5);
3122 InsertConfigInteger(pCfg, "DMA", 1);
3123 InsertConfigInteger(pCfg, "DMA16", 5);
3124 InsertConfigInteger(pCfg, "Port", 0x220);
3125 InsertConfigInteger(pCfg, "Version", 0x0405);
3126 if (uTimerHz)
3127 InsertConfigInteger(pCfg, "TimerHz", uTimerHz);
3128 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3129 if (strDebugPathOut.isNotEmpty())
3130 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut);
3131 break;
3132 }
3133 case AudioControllerType_HDA:
3134 {
3135 /* Intel HD Audio. */
3136 pszAudioDevice = "hda";
3137
3138 InsertConfigNode(pDevices, pszAudioDevice, &pDev);
3139 InsertConfigNode(pDev, "0", &pInst);
3140 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3141 hrc = pBusMgr->assignPCIDevice(pszAudioDevice, pInst); H();
3142 InsertConfigNode(pInst, "Config", &pCfg);
3143 if (uBufSizeInMs)
3144 InsertConfigInteger(pCfg, "BufSizeInMs", uBufSizeInMs);
3145 if (uBufSizeOutMs)
3146 InsertConfigInteger(pCfg, "BufSizeOutMs", uBufSizeOutMs);
3147 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3148 if (strDebugPathOut.isNotEmpty())
3149 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut);
3150
3151 /* macOS guests uses a different HDA variant to make 10.14+ (or maybe 10.13?) recognize the device. */
3152 if (fOsXGuest)
3153 InsertConfigString(pCfg, "DeviceName", "Intel Sunrise Point");
3154 break;
3155 }
3156 default:
3157 pszAudioDevice = "oops";
3158 AssertFailedBreak();
3159 }
3160
3161 PCFGMNODE pCfgAudioAdapter = NULL;
3162 InsertConfigNode(pInst, "AudioConfig", &pCfgAudioAdapter);
3163 SafeArray<BSTR> audioProps;
3164 hrc = audioAdapter->COMGETTER(PropertiesList)(ComSafeArrayAsOutParam(audioProps)); H();
3165
3166 std::list<Utf8Str> audioPropertyNamesList;
3167 for (size_t i = 0; i < audioProps.size(); ++i)
3168 {
3169 Bstr bstrValue;
3170 audioPropertyNamesList.push_back(Utf8Str(audioProps[i]));
3171 hrc = audioAdapter->GetProperty(audioProps[i], bstrValue.asOutParam());
3172 Utf8Str strKey(audioProps[i]);
3173 InsertConfigString(pCfgAudioAdapter, strKey.c_str(), bstrValue);
3174 }
3175
3176 /*
3177 * The audio driver.
3178 */
3179 const char *pszAudioDriver = NULL;
3180#ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3181 if (fValKitEnabled)
3182 {
3183 pszAudioDriver = "ValidationKitAudio";
3184 LogRel(("Audio: ValidationKit driver active\n"));
3185 }
3186#endif
3187 /* If nothing else was selected before, ask the API. */
3188 if (pszAudioDriver == NULL)
3189 {
3190 AudioDriverType_T enmAudioDriver;
3191 hrc = audioAdapter->COMGETTER(AudioDriver)(&enmAudioDriver); H();
3192 switch (enmAudioDriver)
3193 {
3194 case AudioDriverType_Null:
3195 pszAudioDriver = "NullAudio";
3196 break;
3197#ifdef RT_OS_WINDOWS
3198# ifdef VBOX_WITH_WINMM
3199 case AudioDriverType_WinMM:
3200# error "Port WinMM audio backend!" /** @todo Still needed? */
3201 break;
3202# endif
3203 case AudioDriverType_DirectSound:
3204 /* Use the windows audio session (WAS) API rather than Direct Sound on windows
3205 versions we've tested it on (currently W7+). Since Vista, Direct Sound has
3206 been emulated on top of WAS according to the docs, so better use WAS directly.
3207
3208 Set extradata value "VBoxInternal2/Audio/WindowsDrv" "dsound" to no use WasAPI. */
3209 pszAudioDriver = "DSoundAudio";
3210 GetExtraDataBoth(virtualBox, pMachine, "VBoxInternal2/Audio/WindowsDrv", &strTmp); H();
3211 if ( RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6,1,0)
3212 && ( strTmp.isEmpty()
3213 || strTmp.equalsIgnoreCase("was")
3214 || strTmp.equalsIgnoreCase("wasapi")) )
3215 pszAudioDriver = "HostAudioWas";
3216 break;
3217#endif /* RT_OS_WINDOWS */
3218#ifdef RT_OS_SOLARIS
3219 case AudioDriverType_SolAudio:
3220 /* Should not happen, as the Solaris Audio backend is not around anymore.
3221 * Remove this sometime later. */
3222 LogRel(("Audio: WARNING: Solaris Audio is deprecated, please switch to OSS!\n"));
3223 LogRel(("Audio: Automatically setting host audio backend to OSS\n"));
3224
3225 /* Manually set backend to OSS for now. */
3226 pszAudioDriver = "OSSAudio";
3227 break;
3228#endif
3229#ifdef VBOX_WITH_AUDIO_OSS
3230 case AudioDriverType_OSS:
3231 pszAudioDriver = "OSSAudio";
3232 break;
3233#endif
3234#ifdef VBOX_WITH_AUDIO_ALSA
3235 case AudioDriverType_ALSA:
3236 pszAudioDriver = "ALSAAudio";
3237 break;
3238#endif
3239#ifdef VBOX_WITH_AUDIO_PULSE
3240 case AudioDriverType_Pulse:
3241 pszAudioDriver = "PulseAudio";
3242 break;
3243#endif
3244#ifdef RT_OS_DARWIN
3245 case AudioDriverType_CoreAudio:
3246 pszAudioDriver = "CoreAudio";
3247 break;
3248#endif
3249 default:
3250 pszAudioDriver = "oops";
3251 AssertFailedBreak();
3252 }
3253 }
3254
3255 BOOL fAudioEnabledIn = FALSE;
3256 hrc = audioAdapter->COMGETTER(EnabledIn)(&fAudioEnabledIn); H();
3257 BOOL fAudioEnabledOut = FALSE;
3258 hrc = audioAdapter->COMGETTER(EnabledOut)(&fAudioEnabledOut); H();
3259
3260 unsigned idxAudioLun = 0;
3261
3262 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3263 i_configAudioDriver(virtualBox, pMachine, pLunL0, pszAudioDriver, !!fAudioEnabledIn, !!fAudioEnabledOut);
3264 idxAudioLun++;
3265
3266#ifdef VBOX_WITH_AUDIO_VRDE
3267 /* Insert dummy audio driver to have the LUN configured. */
3268 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3269 InsertConfigString(pLunL0, "Driver", "AUDIO");
3270 AudioDriverCfg DrvCfgVRDE(pszAudioDevice, 0 /* Instance */, idxAudioLun, "AudioVRDE",
3271 !!fAudioEnabledIn, !!fAudioEnabledOut);
3272 rc = mAudioVRDE->InitializeConfig(&DrvCfgVRDE);
3273 AssertRCStmt(rc, throw ConfigError(__FUNCTION__, rc, "mAudioVRDE->InitializeConfig failed"));
3274 idxAudioLun++;
3275#endif
3276
3277#ifdef VBOX_WITH_AUDIO_RECORDING
3278 /* Insert dummy audio driver to have the LUN configured. */
3279 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3280 InsertConfigString(pLunL0, "Driver", "AUDIO");
3281 AudioDriverCfg DrvCfgVideoRec(pszAudioDevice, 0 /* Instance */, idxAudioLun, "AudioVideoRec",
3282 false /*a_fEnabledIn*/, true /*a_fEnabledOut*/);
3283 rc = mRecording.mAudioRec->InitializeConfig(&DrvCfgVideoRec);
3284 AssertRCStmt(rc, throw ConfigError(__FUNCTION__, rc, "Recording.mAudioRec->InitializeConfig failed"));
3285 idxAudioLun++;
3286#endif
3287
3288 if (fDebugEnabled)
3289 {
3290#ifdef VBOX_WITH_AUDIO_DEBUG
3291# ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3292 /*
3293 * When both, ValidationKit and Debug mode (for audio) are enabled,
3294 * skip configuring the Debug audio driver, as both modes can
3295 * mess with the audio data and would lead to side effects.
3296 *
3297 * The ValidationKit audio driver has precedence over the Debug audio driver.
3298 *
3299 * This also can (and will) be used in VBox release builds.
3300 */
3301 if (fValKitEnabled)
3302 {
3303 LogRel(("Audio: Warning: ValidationKit running and Debug mode enabled -- disabling Debug driver\n"));
3304 }
3305 else /* Debug mode active -- run both (nice for catching errors / doing development). */
3306 {
3307 /*
3308 * The ValidationKit backend.
3309 */
3310 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3311 i_configAudioDriver(virtualBox, pMachine, pLunL0, "ValidationKitAudio",
3312 !!fAudioEnabledIn, !!fAudioEnabledOut);
3313 idxAudioLun++;
3314# endif /* VBOX_WITH_AUDIO_VALIDATIONKIT */
3315 /*
3316 * The Debug audio backend.
3317 */
3318 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3319 i_configAudioDriver(virtualBox, pMachine, pLunL0, "DebugAudio",
3320 !!fAudioEnabledIn, !!fAudioEnabledOut);
3321 idxAudioLun++;
3322# ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3323 }
3324# endif /* VBOX_WITH_AUDIO_VALIDATIONKIT */
3325#endif /* VBOX_WITH_AUDIO_DEBUG */
3326
3327 /*
3328 * Tweak the logging groups.
3329 */
3330 Utf8Str strGroups("drv_audio.e.l.l2.l3.f"
3331 " audio_mixer.e.l.l2.l3.f"
3332 " dev_hda_codec.e.l.l2.l3.f"
3333 " dev_hda.e.l.l2.l3.f"
3334 " dev_ac97.e.l.l2.l3.f"
3335 " dev_sb16.e.l.l2.l3.f");
3336
3337 LogRel(("Audio: Debug level set to %RU32\n", uDebugLevel));
3338
3339 switch (uDebugLevel)
3340 {
3341 case 0:
3342 strGroups += " drv_host_audio.e.l.l2.l3.f";
3343 break;
3344 case 1:
3345 RT_FALL_THROUGH();
3346 case 2:
3347 RT_FALL_THROUGH();
3348 case 3:
3349 strGroups += " drv_host_audio.e.l.l2.l3.f+audio_test.e.l.l2.l3.f";
3350 break;
3351 case 4:
3352 RT_FALL_THROUGH();
3353 default:
3354 strGroups += " drv_host_audio.e.l.l2.l3.l4.f+audio_test.e.l.l2.l3.l4.f";
3355 break;
3356 }
3357
3358 rc = RTLogGroupSettings(RTLogRelGetDefaultInstance(), strGroups.c_str());
3359 if (RT_FAILURE(rc))
3360 LogRel(("Audio: Setting debug logging failed, rc=%Rrc\n", rc));
3361 }
3362 }
3363
3364#ifdef VBOX_WITH_SHARED_CLIPBOARD
3365 /*
3366 * Shared Clipboard.
3367 */
3368 {
3369 ClipboardMode_T enmClipboardMode = ClipboardMode_Disabled;
3370 hrc = pMachine->COMGETTER(ClipboardMode)(&enmClipboardMode); H();
3371# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
3372 BOOL fFileTransfersEnabled;
3373 hrc = pMachine->COMGETTER(ClipboardFileTransfersEnabled)(&fFileTransfersEnabled); H();
3374#endif
3375
3376 /* Load the service */
3377 rc = pVMMDev->hgcmLoadService("VBoxSharedClipboard", "VBoxSharedClipboard");
3378 if (RT_SUCCESS(rc))
3379 {
3380 LogRel(("Shared Clipboard: Service loaded\n"));
3381
3382 /* Set initial clipboard mode. */
3383 rc = i_changeClipboardMode(enmClipboardMode);
3384 AssertLogRelMsg(RT_SUCCESS(rc), ("Shared Clipboard: Failed to set initial clipboard mode (%d): rc=%Rrc\n",
3385 enmClipboardMode, rc));
3386
3387 /* Setup the service. */
3388 VBOXHGCMSVCPARM parm;
3389 HGCMSvcSetU32(&parm, !i_useHostClipboard());
3390 rc = pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, &parm);
3391 AssertLogRelMsg(RT_SUCCESS(rc), ("Shared Clipboard: Failed to set initial headless mode (%RTbool): rc=%Rrc\n",
3392 !i_useHostClipboard(), rc));
3393
3394# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
3395 rc = i_changeClipboardFileTransferMode(RT_BOOL(fFileTransfersEnabled));
3396 AssertLogRelMsg(RT_SUCCESS(rc), ("Shared Clipboard: Failed to set initial file transfers mode (%u): rc=%Rrc\n",
3397 fFileTransfersEnabled, rc));
3398
3399 /** @todo Register area callbacks? (See also deregistration todo in Console::i_powerDown.) */
3400# endif
3401 }
3402 else
3403 LogRel(("Shared Clipboard: Not available, rc=%Rrc\n", rc));
3404 rc = VINF_SUCCESS; /* None of the potential failures above are fatal. */
3405 }
3406#endif /* VBOX_WITH_SHARED_CLIPBOARD */
3407
3408 /*
3409 * HGCM HostChannel.
3410 */
3411 {
3412 Bstr value;
3413 hrc = pMachine->GetExtraData(Bstr("HGCM/HostChannel").raw(),
3414 value.asOutParam());
3415
3416 if ( hrc == S_OK
3417 && value == "1")
3418 {
3419 rc = pVMMDev->hgcmLoadService("VBoxHostChannel", "VBoxHostChannel");
3420 if (RT_FAILURE(rc))
3421 {
3422 LogRel(("VBoxHostChannel is not available, rc=%Rrc\n", rc));
3423 /* That is not a fatal failure. */
3424 rc = VINF_SUCCESS;
3425 }
3426 }
3427 }
3428
3429#ifdef VBOX_WITH_DRAG_AND_DROP
3430 /*
3431 * Drag and Drop.
3432 */
3433 {
3434 DnDMode_T enmMode = DnDMode_Disabled;
3435 hrc = pMachine->COMGETTER(DnDMode)(&enmMode); H();
3436
3437 /* Load the service */
3438 rc = pVMMDev->hgcmLoadService("VBoxDragAndDropSvc", "VBoxDragAndDropSvc");
3439 if (RT_FAILURE(rc))
3440 {
3441 LogRel(("Drag and drop service is not available, rc=%Rrc\n", rc));
3442 /* That is not a fatal failure. */
3443 rc = VINF_SUCCESS;
3444 }
3445 else
3446 {
3447 rc = HGCMHostRegisterServiceExtension(&m_hHgcmSvcExtDragAndDrop, "VBoxDragAndDropSvc",
3448 &GuestDnD::notifyDnDDispatcher,
3449 GuestDnDInst());
3450 if (RT_FAILURE(rc))
3451 Log(("Cannot register VBoxDragAndDropSvc extension, rc=%Rrc\n", rc));
3452 else
3453 {
3454 LogRel(("Drag and drop service loaded\n"));
3455 rc = i_changeDnDMode(enmMode);
3456 }
3457 }
3458 }
3459#endif /* VBOX_WITH_DRAG_AND_DROP */
3460
3461#if defined(VBOX_WITH_TPM)
3462 /*
3463 * Configure the Trusted Platform Module.
3464 */
3465 ComObjPtr<ITrustedPlatformModule> ptrTpm;
3466 TpmType_T enmTpmType = TpmType_None;
3467
3468 hrc = pMachine->COMGETTER(TrustedPlatformModule)(ptrTpm.asOutParam()); H();
3469 hrc = ptrTpm->COMGETTER(Type)(&enmTpmType); H();
3470 if (enmTpmType != TpmType_None)
3471 {
3472 InsertConfigNode(pDevices, "tpm", &pDev);
3473 InsertConfigNode(pDev, "0", &pInst);
3474 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3475 InsertConfigNode(pInst, "Config", &pCfg);
3476 InsertConfigNode(pInst, "LUN#0", &pLunL0);
3477
3478 switch (enmTpmType)
3479 {
3480 case TpmType_v1_2:
3481 case TpmType_v2_0:
3482 {
3483 InsertConfigString(pLunL0, "Driver", "TpmEmuTpms");
3484 InsertConfigNode(pLunL0, "Config", &pCfg);
3485 InsertConfigInteger(pCfg, "TpmVersion", enmTpmType == TpmType_v1_2 ? 1 : 2);
3486 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3487 InsertConfigString(pLunL1, "Driver", "NvramStore");
3488 break;
3489 }
3490 case TpmType_Host:
3491 {
3492#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS)
3493 InsertConfigString(pLunL0, "Driver", "TpmHost");
3494 InsertConfigNode(pLunL0, "Config", &pCfg);
3495#endif
3496 break;
3497 }
3498 case TpmType_Swtpm:
3499 {
3500 Bstr location;
3501 hrc = ptrTpm->COMGETTER(Location)(location.asOutParam()); H();
3502
3503 InsertConfigString(pLunL0, "Driver", "TpmEmu");
3504 InsertConfigNode(pLunL0, "Config", &pCfg);
3505 InsertConfigString(pCfg, "Location", location);
3506 break;
3507 }
3508 default:
3509 AssertFailedBreak();
3510 }
3511 }
3512#endif
3513
3514 /*
3515 * ACPI
3516 */
3517 BOOL fACPI;
3518 hrc = biosSettings->COMGETTER(ACPIEnabled)(&fACPI); H();
3519 if (fACPI)
3520 {
3521 /* Always show the CPU leafs when we have multiple VCPUs or when the IO-APIC is enabled.
3522 * The Windows SMP kernel needs a CPU leaf or else its idle loop will burn cpu cycles; the
3523 * intelppm driver refuses to register an idle state handler.
3524 * Always show CPU leafs for OS X guests. */
3525 BOOL fShowCpu = fOsXGuest;
3526 if (cCpus > 1 || fIOAPIC)
3527 fShowCpu = true;
3528
3529 BOOL fCpuHotPlug;
3530 hrc = pMachine->COMGETTER(CPUHotPlugEnabled)(&fCpuHotPlug); H();
3531
3532 InsertConfigNode(pDevices, "acpi", &pDev);
3533 InsertConfigNode(pDev, "0", &pInst);
3534 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3535 InsertConfigNode(pInst, "Config", &pCfg);
3536 hrc = pBusMgr->assignPCIDevice("acpi", pInst); H();
3537
3538 InsertConfigInteger(pCfg, "NumCPUs", cCpus);
3539
3540 InsertConfigInteger(pCfg, "IOAPIC", fIOAPIC);
3541 InsertConfigInteger(pCfg, "FdcEnabled", fFdcEnabled);
3542 InsertConfigInteger(pCfg, "HpetEnabled", fHPETEnabled);
3543 InsertConfigInteger(pCfg, "SmcEnabled", fSmcEnabled);
3544 InsertConfigInteger(pCfg, "ShowRtc", fShowRtc);
3545 if (fOsXGuest && !llBootNics.empty())
3546 {
3547 BootNic aNic = llBootNics.front();
3548 uint32_t u32NicPCIAddr = (aNic.mPCIAddress.miDevice << 16) | aNic.mPCIAddress.miFn;
3549 InsertConfigInteger(pCfg, "NicPciAddress", u32NicPCIAddr);
3550 }
3551 if (fOsXGuest && fAudioEnabled)
3552 {
3553 PCIBusAddress Address;
3554 if (pBusMgr->findPCIAddress("hda", 0, Address))
3555 {
3556 uint32_t u32AudioPCIAddr = (Address.miDevice << 16) | Address.miFn;
3557 InsertConfigInteger(pCfg, "AudioPciAddress", u32AudioPCIAddr);
3558 }
3559 }
3560 if (fOsXGuest)
3561 {
3562 PCIBusAddress Address;
3563 if (pBusMgr->findPCIAddress("nvme", 0, Address))
3564 {
3565 uint32_t u32NvmePCIAddr = (Address.miDevice << 16) | Address.miFn;
3566 InsertConfigInteger(pCfg, "NvmePciAddress", u32NvmePCIAddr);
3567 }
3568 }
3569 if (enmIommuType == IommuType_AMD)
3570 {
3571 PCIBusAddress Address;
3572 if (pBusMgr->findPCIAddress("iommu-amd", 0, Address))
3573 {
3574 uint32_t u32IommuAddress = (Address.miDevice << 16) | Address.miFn;
3575 InsertConfigInteger(pCfg, "IommuAmdEnabled", true);
3576 InsertConfigInteger(pCfg, "IommuPciAddress", u32IommuAddress);
3577 if (pBusMgr->findPCIAddress("sb-ioapic", 0, Address))
3578 {
3579 uint32_t const u32SbIoapicAddress = (Address.miDevice << 16) | Address.miFn;
3580 InsertConfigInteger(pCfg, "SbIoApicPciAddress", u32SbIoapicAddress);
3581 }
3582 else
3583 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
3584 N_("AMD IOMMU is enabled, but the I/O APIC is not assigned a PCI address!"));
3585 }
3586 }
3587 else if (enmIommuType == IommuType_Intel)
3588 {
3589 PCIBusAddress Address;
3590 if (pBusMgr->findPCIAddress("iommu-intel", 0, Address))
3591 {
3592 uint32_t u32IommuAddress = (Address.miDevice << 16) | Address.miFn;
3593 InsertConfigInteger(pCfg, "IommuIntelEnabled", true);
3594 InsertConfigInteger(pCfg, "IommuPciAddress", u32IommuAddress);
3595 if (pBusMgr->findPCIAddress("sb-ioapic", 0, Address))
3596 {
3597 uint32_t const u32SbIoapicAddress = (Address.miDevice << 16) | Address.miFn;
3598 InsertConfigInteger(pCfg, "SbIoApicPciAddress", u32SbIoapicAddress);
3599 }
3600 else
3601 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
3602 N_("Intel IOMMU is enabled, but the I/O APIC is not assigned a PCI address!"));
3603 }
3604 }
3605
3606 InsertConfigInteger(pCfg, "IocPciAddress", uIocPCIAddress);
3607 if (chipsetType == ChipsetType_ICH9)
3608 {
3609 InsertConfigInteger(pCfg, "McfgBase", uMcfgBase);
3610 InsertConfigInteger(pCfg, "McfgLength", cbMcfgLength);
3611 /* 64-bit prefetch window root resource:
3612 * Only for ICH9 and if PAE or Long Mode is enabled.
3613 * And only with hardware virtualization (@bugref{5454}). */
3614 if ( (fEnablePAE || fIsGuest64Bit)
3615 && fSupportsHwVirtEx /* HwVirt needs to be supported by the host
3616 otherwise VMM falls back to raw mode */
3617 && fHMEnabled /* HwVirt needs to be enabled in VM config */)
3618 InsertConfigInteger(pCfg, "PciPref64Enabled", 1);
3619 }
3620 InsertConfigInteger(pCfg, "HostBusPciAddress", uHbcPCIAddress);
3621 InsertConfigInteger(pCfg, "ShowCpu", fShowCpu);
3622 InsertConfigInteger(pCfg, "CpuHotPlug", fCpuHotPlug);
3623
3624 InsertConfigInteger(pCfg, "Serial0IoPortBase", auSerialIoPortBase[0]);
3625 InsertConfigInteger(pCfg, "Serial0Irq", auSerialIrq[0]);
3626
3627 InsertConfigInteger(pCfg, "Serial1IoPortBase", auSerialIoPortBase[1]);
3628 InsertConfigInteger(pCfg, "Serial1Irq", auSerialIrq[1]);
3629
3630 if (auSerialIoPortBase[2])
3631 {
3632 InsertConfigInteger(pCfg, "Serial2IoPortBase", auSerialIoPortBase[2]);
3633 InsertConfigInteger(pCfg, "Serial2Irq", auSerialIrq[2]);
3634 }
3635
3636 if (auSerialIoPortBase[3])
3637 {
3638 InsertConfigInteger(pCfg, "Serial3IoPortBase", auSerialIoPortBase[3]);
3639 InsertConfigInteger(pCfg, "Serial3Irq", auSerialIrq[3]);
3640 }
3641
3642 InsertConfigInteger(pCfg, "Parallel0IoPortBase", auParallelIoPortBase[0]);
3643 InsertConfigInteger(pCfg, "Parallel0Irq", auParallelIrq[0]);
3644
3645 InsertConfigInteger(pCfg, "Parallel1IoPortBase", auParallelIoPortBase[1]);
3646 InsertConfigInteger(pCfg, "Parallel1Irq", auParallelIrq[1]);
3647
3648#if defined(VBOX_WITH_TPM)
3649 switch (enmTpmType)
3650 {
3651 case TpmType_v1_2:
3652 InsertConfigString(pCfg, "TpmMode", "tis1.2");
3653 break;
3654 case TpmType_v2_0:
3655 InsertConfigString(pCfg, "TpmMode", "fifo2.0");
3656 break;
3657 /** @todo Host and swtpm. */
3658 default:
3659 break;
3660 }
3661#endif
3662
3663 InsertConfigNode(pInst, "LUN#0", &pLunL0);
3664 InsertConfigString(pLunL0, "Driver", "ACPIHost");
3665 InsertConfigNode(pLunL0, "Config", &pCfg);
3666
3667 /* Attach the dummy CPU drivers */
3668 for (ULONG iCpuCurr = 1; iCpuCurr < cCpus; iCpuCurr++)
3669 {
3670 BOOL fCpuAttached = true;
3671
3672 if (fCpuHotPlug)
3673 {
3674 hrc = pMachine->GetCPUStatus(iCpuCurr, &fCpuAttached); H();
3675 }
3676
3677 if (fCpuAttached)
3678 {
3679 InsertConfigNode(pInst, Utf8StrFmt("LUN#%u", iCpuCurr).c_str(), &pLunL0);
3680 InsertConfigString(pLunL0, "Driver", "ACPICpu");
3681 InsertConfigNode(pLunL0, "Config", &pCfg);
3682 }
3683 }
3684 }
3685
3686 /*
3687 * Configure DBGF (Debug(ger) Facility) and DBGC (Debugger Console).
3688 */
3689 {
3690 PCFGMNODE pDbgf;
3691 InsertConfigNode(pRoot, "DBGF", &pDbgf);
3692
3693 /* Paths to search for debug info and such things. */
3694 hrc = pMachine->COMGETTER(SettingsFilePath)(bstr.asOutParam()); H();
3695 Utf8Str strSettingsPath(bstr);
3696 bstr.setNull();
3697 strSettingsPath.stripFilename();
3698 strSettingsPath.append("/");
3699
3700 char szHomeDir[RTPATH_MAX + 1];
3701 int rc2 = RTPathUserHome(szHomeDir, sizeof(szHomeDir) - 1);
3702 if (RT_FAILURE(rc2))
3703 szHomeDir[0] = '\0';
3704 RTPathEnsureTrailingSeparator(szHomeDir, sizeof(szHomeDir));
3705
3706
3707 Utf8Str strPath;
3708 strPath.append(strSettingsPath).append("debug/;");
3709 strPath.append(strSettingsPath).append(";");
3710 strPath.append("cache*").append(strSettingsPath).append("dbgcache/;"); /* handy for symlinking to actual cache */
3711 strPath.append(szHomeDir);
3712
3713 InsertConfigString(pDbgf, "Path", strPath.c_str());
3714
3715 /* Tracing configuration. */
3716 BOOL fTracingEnabled;
3717 hrc = pMachine->COMGETTER(TracingEnabled)(&fTracingEnabled); H();
3718 if (fTracingEnabled)
3719 InsertConfigInteger(pDbgf, "TracingEnabled", 1);
3720
3721 hrc = pMachine->COMGETTER(TracingConfig)(bstr.asOutParam()); H();
3722 if (fTracingEnabled)
3723 InsertConfigString(pDbgf, "TracingConfig", bstr);
3724
3725 BOOL fAllowTracingToAccessVM;
3726 hrc = pMachine->COMGETTER(AllowTracingToAccessVM)(&fAllowTracingToAccessVM); H();
3727 if (fAllowTracingToAccessVM)
3728 InsertConfigInteger(pPDM, "AllowTracingToAccessVM", 1);
3729
3730 /* Debugger console config. */
3731 PCFGMNODE pDbgc;
3732 InsertConfigNode(pRoot, "DBGC", &pDbgc);
3733
3734 hrc = virtualBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H();
3735 Utf8Str strVBoxHome = bstr;
3736 bstr.setNull();
3737 if (strVBoxHome.isNotEmpty())
3738 strVBoxHome.append("/");
3739 else
3740 {
3741 strVBoxHome = szHomeDir;
3742 strVBoxHome.append("/.vbox");
3743 }
3744
3745 Utf8Str strFile(strVBoxHome);
3746 strFile.append("dbgc-history");
3747 InsertConfigString(pDbgc, "HistoryFile", strFile);
3748
3749 strFile = strSettingsPath;
3750 strFile.append("dbgc-init");
3751 InsertConfigString(pDbgc, "LocalInitScript", strFile);
3752
3753 strFile = strVBoxHome;
3754 strFile.append("dbgc-init");
3755 InsertConfigString(pDbgc, "GlobalInitScript", strFile);
3756 }
3757 }
3758 catch (ConfigError &x)
3759 {
3760 // InsertConfig threw something:
3761 pVMM->pfnVMR3SetError(pUVM, x.m_vrc, RT_SRC_POS, "Caught ConfigError: %Rrc - %s", x.m_vrc, x.what());
3762 return x.m_vrc;
3763 }
3764 catch (HRESULT hrcXcpt)
3765 {
3766 AssertLogRelMsgFailedReturn(("hrc=%Rhrc\n", hrcXcpt), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
3767 }
3768
3769#ifdef VBOX_WITH_EXTPACK
3770 /*
3771 * Call the extension pack hooks if everything went well thus far.
3772 */
3773 if (RT_SUCCESS(rc))
3774 {
3775 pAlock->release();
3776 rc = mptrExtPackManager->i_callAllVmConfigureVmmHooks(this, pVM, pVMM);
3777 pAlock->acquire();
3778 }
3779#endif
3780
3781 /*
3782 * Apply the CFGM overlay.
3783 */
3784 if (RT_SUCCESS(rc))
3785 rc = i_configCfgmOverlay(pRoot, virtualBox, pMachine);
3786
3787 /*
3788 * Dump all extradata API settings tweaks, both global and per VM.
3789 */
3790 if (RT_SUCCESS(rc))
3791 rc = i_configDumpAPISettingsTweaks(virtualBox, pMachine);
3792
3793#undef H
3794
3795 pAlock->release(); /* Avoid triggering the lock order inversion check. */
3796
3797 /*
3798 * Register VM state change handler.
3799 */
3800 int rc2 = pVMM->pfnVMR3AtStateRegister(pUVM, Console::i_vmstateChangeCallback, this);
3801 AssertRC(rc2);
3802 if (RT_SUCCESS(rc))
3803 rc = rc2;
3804
3805 /*
3806 * Register VM runtime error handler.
3807 */
3808 rc2 = pVMM->pfnVMR3AtRuntimeErrorRegister(pUVM, Console::i_atVMRuntimeErrorCallback, this);
3809 AssertRC(rc2);
3810 if (RT_SUCCESS(rc))
3811 rc = rc2;
3812
3813 pAlock->acquire();
3814
3815 LogFlowFunc(("vrc = %Rrc\n", rc));
3816 LogFlowFuncLeave();
3817
3818 return rc;
3819}
3820
3821/**
3822 * Configures an audio driver via CFGM by getting (optional) values from extra data.
3823 *
3824 * @param pVirtualBox Pointer to IVirtualBox instance.
3825 * @param pMachine Pointer to IMachine instance.
3826 * @param pLUN Pointer to CFGM node of LUN (the driver) to configure.
3827 * @param pszDrvName Name of the driver to configure.
3828 * @param fAudioEnabledIn IAudioAdapter::enabledIn value.
3829 * @param fAudioEnabledOut IAudioAdapter::enabledOut value.
3830 *
3831 * @throws ConfigError or HRESULT on if there is trouble.
3832 */
3833void Console::i_configAudioDriver(IVirtualBox *pVirtualBox, IMachine *pMachine, PCFGMNODE pLUN, const char *pszDrvName,
3834 bool fAudioEnabledIn, bool fAudioEnabledOut)
3835{
3836#define H() AssertLogRelMsgStmt(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), \
3837 throw ConfigError(__FUNCTION__, VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR, "line: " RT_XSTR(__LINE__)))
3838
3839 InsertConfigString(pLUN, "Driver", "AUDIO");
3840
3841 PCFGMNODE pCfg;
3842 InsertConfigNode(pLUN, "Config", &pCfg);
3843 InsertConfigString(pCfg, "DriverName", pszDrvName);
3844 InsertConfigInteger(pCfg, "InputEnabled", fAudioEnabledIn);
3845 InsertConfigInteger(pCfg, "OutputEnabled", fAudioEnabledOut);
3846
3847 Utf8Str strTmp;
3848 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp);
3849 const uint64_t fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
3850 if (fDebugEnabled)
3851 {
3852 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3853
3854 Utf8Str strDebugPathOut;
3855 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut);
3856 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut.c_str());
3857 }
3858
3859 /*
3860 * PCM input parameters (playback + recording).
3861 * We have host driver specific ones as: VBoxInternal2/Audio/<DrvName>/<Value>
3862 * And global ones for all host drivers: VBoxInternal2/Audio/<Value>
3863 */
3864 for (unsigned iDir = 0; iDir < 2; iDir++)
3865 {
3866 static const struct
3867 {
3868 const char *pszExtraName;
3869 const char *pszCfgmName;
3870 } s_aToCopy[] =
3871 { /* PCM parameters: */
3872 { "PCMSampleBit", "PCMSampleBit" },
3873 { "PCMSampleHz", "PCMSampleHz" },
3874 { "PCMSampleSigned", "PCMSampleSigned" },
3875 { "PCMSampleSwapEndian", "PCMSampleSwapEndian" },
3876 { "PCMSampleChannels", "PCMSampleChannels" },
3877 /* Buffering stuff: */
3878 { "PeriodSizeMs", "PeriodSizeMs" },
3879 { "BufferSizeMs", "BufferSizeMs" },
3880 { "PreBufferSizeMs", "PreBufferSizeMs" },
3881 };
3882
3883 PCFGMNODE pDirNode = NULL;
3884 const char *pszDir = iDir == 0 ? "In" : "Out";
3885 for (size_t i = 0; i < RT_ELEMENTS(s_aToCopy); i++)
3886 {
3887 char szExtra[128];
3888 RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s/%s%s", pszDrvName, s_aToCopy[i].pszExtraName, pszDir);
3889 GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp); /* throws hrc */
3890 if (strTmp.isEmpty())
3891 {
3892 RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s%s", s_aToCopy[i].pszExtraName, pszDir);
3893 GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp);
3894 if (strTmp.isEmpty())
3895 continue;
3896 }
3897
3898 uint32_t uValue;
3899 int vrc = RTStrToUInt32Full(strTmp.c_str(), 0, &uValue);
3900 if (RT_SUCCESS(vrc))
3901 {
3902 if (!pDirNode)
3903 InsertConfigNode(pCfg, pszDir, &pDirNode);
3904 InsertConfigInteger(pDirNode, s_aToCopy[i].pszCfgmName, uValue);
3905 }
3906 else
3907 LogRel(("Ignoring malformed 32-bit unsigned integer config value '%s' = '%s': %Rrc\n", szExtra, strTmp.c_str(), vrc));
3908 }
3909 }
3910
3911 PCFGMNODE pLunL1;
3912 InsertConfigNode(pLUN, "AttachedDriver", &pLunL1);
3913 InsertConfigString(pLunL1, "Driver", pszDrvName);
3914 InsertConfigNode(pLunL1, "Config", &pCfg);
3915
3916#ifdef RT_OS_WINDOWS
3917 if (strcmp(pszDrvName, "HostAudioWas") == 0)
3918 {
3919 Bstr bstrTmp;
3920 HRESULT hrc = pMachine->COMGETTER(Id)(bstrTmp.asOutParam()); H();
3921 InsertConfigString(pCfg, "VmUuid", bstrTmp);
3922 }
3923#endif
3924
3925#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX)
3926 if ( strcmp(pszDrvName, "HostAudioWas") == 0
3927 || strcmp(pszDrvName, "PulseAudio") == 0)
3928 {
3929 Bstr bstrTmp;
3930 HRESULT hrc = pMachine->COMGETTER(Name)(bstrTmp.asOutParam()); H();
3931 InsertConfigString(pCfg, "VmName", bstrTmp);
3932 }
3933#endif
3934
3935 LogFlowFunc(("szDrivName=%s\n", pszDrvName));
3936
3937#undef H
3938}
3939
3940/**
3941 * Applies the CFGM overlay as specified by VBoxInternal/XXX extra data
3942 * values.
3943 *
3944 * @returns VBox status code.
3945 * @param pRoot The root of the configuration tree.
3946 * @param pVirtualBox Pointer to the IVirtualBox interface.
3947 * @param pMachine Pointer to the IMachine interface.
3948 */
3949/* static */
3950int Console::i_configCfgmOverlay(PCFGMNODE pRoot, IVirtualBox *pVirtualBox, IMachine *pMachine)
3951{
3952 /*
3953 * CFGM overlay handling.
3954 *
3955 * Here we check the extra data entries for CFGM values
3956 * and create the nodes and insert the values on the fly. Existing
3957 * values will be removed and reinserted. CFGM is typed, so by default
3958 * we will guess whether it's a string or an integer (byte arrays are
3959 * not currently supported). It's possible to override this autodetection
3960 * by adding "string:", "integer:" or "bytes:" (future).
3961 *
3962 * We first perform a run on global extra data, then on the machine
3963 * extra data to support global settings with local overrides.
3964 */
3965 int rc = VINF_SUCCESS;
3966 bool fFirst = true;
3967 try
3968 {
3969 /** @todo add support for removing nodes and byte blobs. */
3970 /*
3971 * Get the next key
3972 */
3973 SafeArray<BSTR> aGlobalExtraDataKeys;
3974 SafeArray<BSTR> aMachineExtraDataKeys;
3975 HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
3976 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
3977
3978 // remember the no. of global values so we can call the correct method below
3979 size_t cGlobalValues = aGlobalExtraDataKeys.size();
3980
3981 hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
3982 AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
3983
3984 // build a combined list from global keys...
3985 std::list<Utf8Str> llExtraDataKeys;
3986
3987 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i)
3988 llExtraDataKeys.push_back(Utf8Str(aGlobalExtraDataKeys[i]));
3989 // ... and machine keys
3990 for (size_t i = 0; i < aMachineExtraDataKeys.size(); ++i)
3991 llExtraDataKeys.push_back(Utf8Str(aMachineExtraDataKeys[i]));
3992
3993 size_t i2 = 0;
3994 for (std::list<Utf8Str>::const_iterator it = llExtraDataKeys.begin();
3995 it != llExtraDataKeys.end();
3996 ++it, ++i2)
3997 {
3998 const Utf8Str &strKey = *it;
3999
4000 /*
4001 * We only care about keys starting with "VBoxInternal/" (skip "G:" or "M:")
4002 */
4003 if (!strKey.startsWith("VBoxInternal/"))
4004 continue;
4005
4006 const char *pszExtraDataKey = strKey.c_str() + sizeof("VBoxInternal/") - 1;
4007
4008 // get the value
4009 Bstr bstrExtraDataValue;
4010 if (i2 < cGlobalValues)
4011 // this is still one of the global values:
4012 hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam());
4013 else
4014 hrc = pMachine->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam());
4015 if (FAILED(hrc))
4016 LogRel(("Warning: Cannot get extra data key %s, rc = %Rhrc\n", strKey.c_str(), hrc));
4017
4018 if (fFirst)
4019 {
4020 fFirst = false;
4021 LogRel(("Extradata overrides:\n"));
4022 }
4023 LogRel((" %s=\"%ls\"%s\n", strKey.c_str(), bstrExtraDataValue.raw(), i2 < cGlobalValues ? " (global)" : ""));
4024
4025 /*
4026 * The key will be in the format "Node1/Node2/Value" or simply "Value".
4027 * Split the two and get the node, delete the value and create the node
4028 * if necessary.
4029 */
4030 PCFGMNODE pNode;
4031 const char *pszCFGMValueName = strrchr(pszExtraDataKey, '/');
4032 if (pszCFGMValueName)
4033 {
4034 /* terminate the node and advance to the value (Utf8Str might not
4035 offically like this but wtf) */
4036 *(char *)pszCFGMValueName = '\0';
4037 ++pszCFGMValueName;
4038
4039 /* does the node already exist? */
4040 pNode = mpVMM->pfnCFGMR3GetChild(pRoot, pszExtraDataKey);
4041 if (pNode)
4042 mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName);
4043 else
4044 {
4045 /* create the node */
4046 rc = mpVMM->pfnCFGMR3InsertNode(pRoot, pszExtraDataKey, &pNode);
4047 if (RT_FAILURE(rc))
4048 {
4049 AssertLogRelMsgRC(rc, ("failed to insert node '%s'\n", pszExtraDataKey));
4050 continue;
4051 }
4052 Assert(pNode);
4053 }
4054 }
4055 else
4056 {
4057 /* root value (no node path). */
4058 pNode = pRoot;
4059 pszCFGMValueName = pszExtraDataKey;
4060 pszExtraDataKey--;
4061 mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName);
4062 }
4063
4064 /*
4065 * Now let's have a look at the value.
4066 * Empty strings means that we should remove the value, which we've
4067 * already done above.
4068 */
4069 Utf8Str strCFGMValueUtf8(bstrExtraDataValue);
4070 if (strCFGMValueUtf8.isNotEmpty())
4071 {
4072 uint64_t u64Value;
4073
4074 /* check for type prefix first. */
4075 if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("string:")))
4076 rc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str() + sizeof("string:") - 1);
4077 else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("integer:")))
4078 {
4079 rc = RTStrToUInt64Full(strCFGMValueUtf8.c_str() + sizeof("integer:") - 1, 0, &u64Value);
4080 if (RT_SUCCESS(rc))
4081 rc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
4082 }
4083 else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("bytes:")))
4084 {
4085 char const *pszBase64 = strCFGMValueUtf8.c_str() + sizeof("bytes:") - 1;
4086 ssize_t cbValue = RTBase64DecodedSize(pszBase64, NULL);
4087 if (cbValue > 0)
4088 {
4089 void *pvBytes = RTMemTmpAlloc(cbValue);
4090 if (pvBytes)
4091 {
4092 rc = RTBase64Decode(pszBase64, pvBytes, cbValue, NULL, NULL);
4093 if (RT_SUCCESS(rc))
4094 rc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, pvBytes, cbValue);
4095 RTMemTmpFree(pvBytes);
4096 }
4097 else
4098 rc = VERR_NO_TMP_MEMORY;
4099 }
4100 else if (cbValue == 0)
4101 rc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, NULL, 0);
4102 else
4103 rc = VERR_INVALID_BASE64_ENCODING;
4104 }
4105 /* auto detect type. */
4106 else if (RT_SUCCESS(RTStrToUInt64Full(strCFGMValueUtf8.c_str(), 0, &u64Value)))
4107 rc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
4108 else
4109 rc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str());
4110 AssertLogRelMsgRCBreak(rc, ("failed to insert CFGM value '%s' to key '%s'\n",
4111 strCFGMValueUtf8.c_str(), pszExtraDataKey));
4112 }
4113 }
4114 }
4115 catch (ConfigError &x)
4116 {
4117 // InsertConfig threw something:
4118 return x.m_vrc;
4119 }
4120 return rc;
4121}
4122
4123/**
4124 * Dumps the API settings tweaks as specified by VBoxInternal2/XXX extra data
4125 * values.
4126 *
4127 * @returns VBox status code.
4128 * @param pVirtualBox Pointer to the IVirtualBox interface.
4129 * @param pMachine Pointer to the IMachine interface.
4130 */
4131/* static */
4132int Console::i_configDumpAPISettingsTweaks(IVirtualBox *pVirtualBox, IMachine *pMachine)
4133{
4134 {
4135 SafeArray<BSTR> aGlobalExtraDataKeys;
4136 HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
4137 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
4138 bool hasKey = false;
4139 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); i++)
4140 {
4141 Utf8Str strKey(aGlobalExtraDataKeys[i]);
4142 if (!strKey.startsWith("VBoxInternal2/"))
4143 continue;
4144
4145 Bstr bstrValue;
4146 hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(),
4147 bstrValue.asOutParam());
4148 if (FAILED(hrc))
4149 continue;
4150 if (!hasKey)
4151 LogRel(("Global extradata API settings:\n"));
4152 LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
4153 hasKey = true;
4154 }
4155 }
4156
4157 {
4158 SafeArray<BSTR> aMachineExtraDataKeys;
4159 HRESULT hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
4160 AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
4161 bool hasKey = false;
4162 for (size_t i = 0; i < aMachineExtraDataKeys.size(); i++)
4163 {
4164 Utf8Str strKey(aMachineExtraDataKeys[i]);
4165 if (!strKey.startsWith("VBoxInternal2/"))
4166 continue;
4167
4168 Bstr bstrValue;
4169 hrc = pMachine->GetExtraData(Bstr(strKey).raw(),
4170 bstrValue.asOutParam());
4171 if (FAILED(hrc))
4172 continue;
4173 if (!hasKey)
4174 LogRel(("Per-VM extradata API settings:\n"));
4175 LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
4176 hasKey = true;
4177 }
4178 }
4179
4180 return VINF_SUCCESS;
4181}
4182
4183int Console::i_configGraphicsController(PCFGMNODE pDevices,
4184 const GraphicsControllerType_T enmGraphicsController,
4185 BusAssignmentManager *pBusMgr,
4186 const ComPtr<IMachine> &ptrMachine,
4187 const ComPtr<IGraphicsAdapter> &ptrGraphicsAdapter,
4188 const ComPtr<IBIOSSettings> &ptrBiosSettings,
4189 bool fHMEnabled)
4190{
4191 // InsertConfig* throws
4192 try
4193 {
4194 PCFGMNODE pDev, pInst, pCfg, pLunL0;
4195 HRESULT hrc;
4196 Bstr bstr;
4197 const char *pcszDevice = "vga";
4198
4199#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
4200 InsertConfigNode(pDevices, pcszDevice, &pDev);
4201 InsertConfigNode(pDev, "0", &pInst);
4202 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
4203
4204 hrc = pBusMgr->assignPCIDevice(pcszDevice, pInst); H();
4205 InsertConfigNode(pInst, "Config", &pCfg);
4206 ULONG cVRamMBs;
4207 hrc = ptrGraphicsAdapter->COMGETTER(VRAMSize)(&cVRamMBs); H();
4208 InsertConfigInteger(pCfg, "VRamSize", cVRamMBs * _1M);
4209 ULONG cMonitorCount;
4210 hrc = ptrGraphicsAdapter->COMGETTER(MonitorCount)(&cMonitorCount); H();
4211 InsertConfigInteger(pCfg, "MonitorCount", cMonitorCount);
4212#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE
4213 InsertConfigInteger(pCfg, "R0Enabled", fHMEnabled);
4214#else
4215 NOREF(fHMEnabled);
4216#endif
4217 BOOL f3DEnabled;
4218 hrc = ptrGraphicsAdapter->COMGETTER(Accelerate3DEnabled)(&f3DEnabled); H();
4219 InsertConfigInteger(pCfg, "3DEnabled", f3DEnabled);
4220
4221 i_attachStatusDriver(pInst, DeviceType_Graphics3D, 0, 0, NULL, NULL, NULL, 0);
4222
4223#ifdef VBOX_WITH_VMSVGA
4224 if ( enmGraphicsController == GraphicsControllerType_VMSVGA
4225 || enmGraphicsController == GraphicsControllerType_VBoxSVGA)
4226 {
4227 InsertConfigInteger(pCfg, "VMSVGAEnabled", true);
4228 if (enmGraphicsController == GraphicsControllerType_VMSVGA)
4229 {
4230 InsertConfigInteger(pCfg, "VMSVGAPciBarLayout", true);
4231 InsertConfigInteger(pCfg, "VMSVGAPciId", true);
4232 }
4233# ifdef VBOX_WITH_VMSVGA3D
4234 InsertConfigInteger(pCfg, "VMSVGA3dEnabled", f3DEnabled);
4235# else
4236 LogRel(("VMSVGA3d not available in this build!\n"));
4237# endif /* VBOX_WITH_VMSVGA3D */
4238 }
4239#else
4240 RT_NOREF(enmGraphicsController);
4241#endif /* VBOX_WITH_VMSVGA */
4242
4243 /* Custom VESA mode list */
4244 unsigned cModes = 0;
4245 for (unsigned iMode = 1; iMode <= 16; ++iMode)
4246 {
4247 char szExtraDataKey[sizeof("CustomVideoModeXX")];
4248 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%u", iMode);
4249 hrc = ptrMachine->GetExtraData(Bstr(szExtraDataKey).raw(), bstr.asOutParam()); H();
4250 if (bstr.isEmpty())
4251 break;
4252 InsertConfigString(pCfg, szExtraDataKey, bstr);
4253 ++cModes;
4254 }
4255 InsertConfigInteger(pCfg, "CustomVideoModes", cModes);
4256
4257 /* VESA height reduction */
4258 ULONG ulHeightReduction;
4259 IFramebuffer *pFramebuffer = NULL;
4260 hrc = i_getDisplay()->QueryFramebuffer(0, &pFramebuffer);
4261 if (SUCCEEDED(hrc) && pFramebuffer)
4262 {
4263 hrc = pFramebuffer->COMGETTER(HeightReduction)(&ulHeightReduction); H();
4264 pFramebuffer->Release();
4265 pFramebuffer = NULL;
4266 }
4267 else
4268 {
4269 /* If framebuffer is not available, there is no height reduction. */
4270 ulHeightReduction = 0;
4271 }
4272 InsertConfigInteger(pCfg, "HeightReduction", ulHeightReduction);
4273
4274 /*
4275 * BIOS logo
4276 */
4277 BOOL fFadeIn;
4278 hrc = ptrBiosSettings->COMGETTER(LogoFadeIn)(&fFadeIn); H();
4279 InsertConfigInteger(pCfg, "FadeIn", fFadeIn ? 1 : 0);
4280 BOOL fFadeOut;
4281 hrc = ptrBiosSettings->COMGETTER(LogoFadeOut)(&fFadeOut); H();
4282 InsertConfigInteger(pCfg, "FadeOut", fFadeOut ? 1: 0);
4283 ULONG logoDisplayTime;
4284 hrc = ptrBiosSettings->COMGETTER(LogoDisplayTime)(&logoDisplayTime); H();
4285 InsertConfigInteger(pCfg, "LogoTime", logoDisplayTime);
4286 Bstr logoImagePath;
4287 hrc = ptrBiosSettings->COMGETTER(LogoImagePath)(logoImagePath.asOutParam()); H();
4288 InsertConfigString(pCfg, "LogoFile", Utf8Str(!logoImagePath.isEmpty() ? logoImagePath : "") );
4289
4290 /*
4291 * Boot menu
4292 */
4293 BIOSBootMenuMode_T eBootMenuMode;
4294 int iShowBootMenu;
4295 hrc = ptrBiosSettings->COMGETTER(BootMenuMode)(&eBootMenuMode); H();
4296 switch (eBootMenuMode)
4297 {
4298 case BIOSBootMenuMode_Disabled: iShowBootMenu = 0; break;
4299 case BIOSBootMenuMode_MenuOnly: iShowBootMenu = 1; break;
4300 default: iShowBootMenu = 2; break;
4301 }
4302 InsertConfigInteger(pCfg, "ShowBootMenu", iShowBootMenu);
4303
4304 /* Attach the display. */
4305 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4306 InsertConfigString(pLunL0, "Driver", "MainDisplay");
4307 InsertConfigNode(pLunL0, "Config", &pCfg);
4308 }
4309 catch (ConfigError &x)
4310 {
4311 // InsertConfig threw something:
4312 return x.m_vrc;
4313 }
4314
4315#undef H
4316
4317 return VINF_SUCCESS;
4318}
4319
4320
4321/**
4322 * Ellipsis to va_list wrapper for calling setVMRuntimeErrorCallback.
4323 */
4324void Console::i_atVMRuntimeErrorCallbackF(uint32_t fFlags, const char *pszErrorId, const char *pszFormat, ...)
4325{
4326 va_list va;
4327 va_start(va, pszFormat);
4328 i_atVMRuntimeErrorCallback(NULL, this, fFlags, pszErrorId, pszFormat, va);
4329 va_end(va);
4330}
4331
4332/* XXX introduce RT format specifier */
4333static uint64_t formatDiskSize(uint64_t u64Size, const char **pszUnit)
4334{
4335 if (u64Size > INT64_C(5000)*_1G)
4336 {
4337 *pszUnit = "TB";
4338 return u64Size / _1T;
4339 }
4340 else if (u64Size > INT64_C(5000)*_1M)
4341 {
4342 *pszUnit = "GB";
4343 return u64Size / _1G;
4344 }
4345 else
4346 {
4347 *pszUnit = "MB";
4348 return u64Size / _1M;
4349 }
4350}
4351
4352/**
4353 * Checks the location of the given medium for known bugs affecting the usage
4354 * of the host I/O cache setting.
4355 *
4356 * @returns VBox status code.
4357 * @param pMedium The medium to check.
4358 * @param pfUseHostIOCache Where to store the suggested host I/O cache setting.
4359 */
4360int Console::i_checkMediumLocation(IMedium *pMedium, bool *pfUseHostIOCache)
4361{
4362#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
4363 /*
4364 * Some sanity checks.
4365 */
4366 RT_NOREF(pfUseHostIOCache);
4367 ComPtr<IMediumFormat> pMediumFormat;
4368 HRESULT hrc = pMedium->COMGETTER(MediumFormat)(pMediumFormat.asOutParam()); H();
4369 ULONG uCaps = 0;
4370 com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
4371 hrc = pMediumFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); H();
4372
4373 for (ULONG j = 0; j < mediumFormatCap.size(); j++)
4374 uCaps |= mediumFormatCap[j];
4375
4376 if (uCaps & MediumFormatCapabilities_File)
4377 {
4378 Bstr bstrFile;
4379 hrc = pMedium->COMGETTER(Location)(bstrFile.asOutParam()); H();
4380 Utf8Str const strFile(bstrFile);
4381
4382 Bstr bstrSnap;
4383 ComPtr<IMachine> pMachine = i_machine();
4384 hrc = pMachine->COMGETTER(SnapshotFolder)(bstrSnap.asOutParam()); H();
4385 Utf8Str const strSnap(bstrSnap);
4386
4387 RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN;
4388 int rc2 = RTFsQueryType(strFile.c_str(), &enmFsTypeFile);
4389 AssertMsgRCReturn(rc2, ("Querying the file type of '%s' failed!\n", strFile.c_str()), rc2);
4390
4391 /* Ignore the error code. On error, the file system type is still 'unknown' so
4392 * none of the following paths are taken. This can happen for new VMs which
4393 * still don't have a snapshot folder. */
4394 RTFSTYPE enmFsTypeSnap = RTFSTYPE_UNKNOWN;
4395 (void)RTFsQueryType(strSnap.c_str(), &enmFsTypeSnap);
4396 if (!mfSnapshotFolderDiskTypeShown)
4397 {
4398 LogRel(("File system of '%s' (snapshots) is %s\n", strSnap.c_str(), RTFsTypeName(enmFsTypeSnap)));
4399 mfSnapshotFolderDiskTypeShown = true;
4400 }
4401 LogRel(("File system of '%s' is %s\n", strFile.c_str(), RTFsTypeName(enmFsTypeFile)));
4402 LONG64 i64Size;
4403 hrc = pMedium->COMGETTER(LogicalSize)(&i64Size); H();
4404#ifdef RT_OS_WINDOWS
4405 if ( enmFsTypeFile == RTFSTYPE_FAT
4406 && i64Size >= _4G)
4407 {
4408 const char *pszUnit;
4409 uint64_t u64Print = formatDiskSize((uint64_t)i64Size, &pszUnit);
4410 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
4411 N_("The medium '%s' has a logical size of %RU64%s "
4412 "but the file system the medium is located on seems "
4413 "to be FAT(32) which cannot handle files bigger than 4GB.\n"
4414 "We strongly recommend to put all your virtual disk images and "
4415 "the snapshot folder onto an NTFS partition"),
4416 strFile.c_str(), u64Print, pszUnit);
4417 }
4418#else /* !RT_OS_WINDOWS */
4419 if ( enmFsTypeFile == RTFSTYPE_FAT
4420 || enmFsTypeFile == RTFSTYPE_EXT
4421 || enmFsTypeFile == RTFSTYPE_EXT2
4422 || enmFsTypeFile == RTFSTYPE_EXT3
4423 || enmFsTypeFile == RTFSTYPE_EXT4)
4424 {
4425 RTFILE file;
4426 int rc = RTFileOpen(&file, strFile.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
4427 if (RT_SUCCESS(rc))
4428 {
4429 RTFOFF maxSize;
4430 /* Careful: This function will work only on selected local file systems! */
4431 rc = RTFileQueryMaxSizeEx(file, &maxSize);
4432 RTFileClose(file);
4433 if ( RT_SUCCESS(rc)
4434 && maxSize > 0
4435 && i64Size > (LONG64)maxSize)
4436 {
4437 const char *pszUnitSiz;
4438 const char *pszUnitMax;
4439 uint64_t u64PrintSiz = formatDiskSize((LONG64)i64Size, &pszUnitSiz);
4440 uint64_t u64PrintMax = formatDiskSize(maxSize, &pszUnitMax);
4441 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected", /* <= not exact but ... */
4442 N_("The medium '%s' has a logical size of %RU64%s "
4443 "but the file system the medium is located on can "
4444 "only handle files up to %RU64%s in theory.\n"
4445 "We strongly recommend to put all your virtual disk "
4446 "images and the snapshot folder onto a proper "
4447 "file system (e.g. ext3) with a sufficient size"),
4448 strFile.c_str(), u64PrintSiz, pszUnitSiz, u64PrintMax, pszUnitMax);
4449 }
4450 }
4451 }
4452#endif /* !RT_OS_WINDOWS */
4453
4454 /*
4455 * Snapshot folder:
4456 * Here we test only for a FAT partition as we had to create a dummy file otherwise
4457 */
4458 if ( enmFsTypeSnap == RTFSTYPE_FAT
4459 && i64Size >= _4G
4460 && !mfSnapshotFolderSizeWarningShown)
4461 {
4462 const char *pszUnit;
4463 uint64_t u64Print = formatDiskSize(i64Size, &pszUnit);
4464 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
4465#ifdef RT_OS_WINDOWS
4466 N_("The snapshot folder of this VM '%s' seems to be located on "
4467 "a FAT(32) file system. The logical size of the medium '%s' "
4468 "(%RU64%s) is bigger than the maximum file size this file "
4469 "system can handle (4GB).\n"
4470 "We strongly recommend to put all your virtual disk images and "
4471 "the snapshot folder onto an NTFS partition"),
4472#else
4473 N_("The snapshot folder of this VM '%s' seems to be located on "
4474 "a FAT(32) file system. The logical size of the medium '%s' "
4475 "(%RU64%s) is bigger than the maximum file size this file "
4476 "system can handle (4GB).\n"
4477 "We strongly recommend to put all your virtual disk images and "
4478 "the snapshot folder onto a proper file system (e.g. ext3)"),
4479#endif
4480 strSnap.c_str(), strFile.c_str(), u64Print, pszUnit);
4481 /* Show this particular warning only once */
4482 mfSnapshotFolderSizeWarningShown = true;
4483 }
4484
4485#ifdef RT_OS_LINUX
4486 /*
4487 * Ext4 bug: Check if the host I/O cache is disabled and the disk image is located
4488 * on an ext4 partition.
4489 * This bug apparently applies to the XFS file system as well.
4490 * Linux 2.6.36 is known to be fixed (tested with 2.6.36-rc4).
4491 */
4492
4493 char szOsRelease[128];
4494 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOsRelease, sizeof(szOsRelease));
4495 bool fKernelHasODirectBug = RT_FAILURE(rc)
4496 || (RTStrVersionCompare(szOsRelease, "2.6.36-rc4") < 0);
4497
4498 if ( (uCaps & MediumFormatCapabilities_Asynchronous)
4499 && !*pfUseHostIOCache
4500 && fKernelHasODirectBug)
4501 {
4502 if ( enmFsTypeFile == RTFSTYPE_EXT4
4503 || enmFsTypeFile == RTFSTYPE_XFS)
4504 {
4505 i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
4506 N_("The host I/O cache for at least one controller is disabled "
4507 "and the medium '%s' for this VM "
4508 "is located on an %s partition. There is a known Linux "
4509 "kernel bug which can lead to the corruption of the virtual "
4510 "disk image under these conditions.\n"
4511 "Either enable the host I/O cache permanently in the VM "
4512 "settings or put the disk image and the snapshot folder "
4513 "onto a different file system.\n"
4514 "The host I/O cache will now be enabled for this medium"),
4515 strFile.c_str(), enmFsTypeFile == RTFSTYPE_EXT4 ? "ext4" : "xfs");
4516 *pfUseHostIOCache = true;
4517 }
4518 else if ( ( enmFsTypeSnap == RTFSTYPE_EXT4
4519 || enmFsTypeSnap == RTFSTYPE_XFS)
4520 && !mfSnapshotFolderExt4WarningShown)
4521 {
4522 i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
4523 N_("The host I/O cache for at least one controller is disabled "
4524 "and the snapshot folder for this VM "
4525 "is located on an %s partition. There is a known Linux "
4526 "kernel bug which can lead to the corruption of the virtual "
4527 "disk image under these conditions.\n"
4528 "Either enable the host I/O cache permanently in the VM "
4529 "settings or put the disk image and the snapshot folder "
4530 "onto a different file system.\n"
4531 "The host I/O cache will now be enabled for this medium"),
4532 enmFsTypeSnap == RTFSTYPE_EXT4 ? "ext4" : "xfs");
4533 *pfUseHostIOCache = true;
4534 mfSnapshotFolderExt4WarningShown = true;
4535 }
4536 }
4537
4538 /*
4539 * 2.6.18 bug: Check if the host I/O cache is disabled and the host is running
4540 * Linux 2.6.18. See @bugref{8690}. Apparently the same problem as
4541 * documented in https://lkml.org/lkml/2007/2/1/14. We saw such
4542 * kernel oopses on Linux 2.6.18-416.el5. We don't know when this
4543 * was fixed but we _know_ that 2.6.18 EL5 kernels are affected.
4544 */
4545 bool fKernelAsyncUnreliable = RT_FAILURE(rc)
4546 || (RTStrVersionCompare(szOsRelease, "2.6.19") < 0);
4547 if ( (uCaps & MediumFormatCapabilities_Asynchronous)
4548 && !*pfUseHostIOCache
4549 && fKernelAsyncUnreliable)
4550 {
4551 i_atVMRuntimeErrorCallbackF(0, "Linux2618TooOld",
4552 N_("The host I/O cache for at least one controller is disabled. "
4553 "There is a known Linux kernel bug which can lead to kernel "
4554 "oopses under heavy load. To our knowledge this bug affects "
4555 "all 2.6.18 kernels.\n"
4556 "Either enable the host I/O cache permanently in the VM "
4557 "settings or switch to a newer host kernel.\n"
4558 "The host I/O cache will now be enabled for this medium"));
4559 *pfUseHostIOCache = true;
4560 }
4561#endif
4562 }
4563#undef H
4564
4565 return VINF_SUCCESS;
4566}
4567
4568/**
4569 * Unmounts the specified medium from the specified device.
4570 *
4571 * @returns VBox status code.
4572 * @param pUVM The usermode VM handle.
4573 * @param pVMM The VMM vtable.
4574 * @param enmBus The storage bus.
4575 * @param enmDevType The device type.
4576 * @param pcszDevice The device emulation.
4577 * @param uInstance Instance of the device.
4578 * @param uLUN The LUN on the device.
4579 * @param fForceUnmount Whether to force unmounting.
4580 */
4581int Console::i_unmountMediumFromGuest(PUVM pUVM, PCVMMR3VTABLE pVMM, StorageBus_T enmBus, DeviceType_T enmDevType,
4582 const char *pcszDevice, unsigned uInstance, unsigned uLUN,
4583 bool fForceUnmount) RT_NOEXCEPT
4584{
4585 /* Unmount existing media only for floppy and DVD drives. */
4586 int rc = VINF_SUCCESS;
4587 PPDMIBASE pBase;
4588 if (enmBus == StorageBus_USB)
4589 rc = pVMM->pfnPDMR3UsbQueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase);
4590 else if ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI)
4591 || (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD))
4592 rc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase);
4593 else /* IDE or Floppy */
4594 rc = pVMM->pfnPDMR3QueryLun(pUVM, pcszDevice, uInstance, uLUN, &pBase);
4595
4596 if (RT_FAILURE(rc))
4597 {
4598 if (rc == VERR_PDM_LUN_NOT_FOUND || rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
4599 rc = VINF_SUCCESS;
4600 AssertRC(rc);
4601 }
4602 else
4603 {
4604 PPDMIMOUNT pIMount = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMOUNT);
4605 AssertReturn(pIMount, VERR_INVALID_POINTER);
4606
4607 /* Unmount the media (but do not eject the medium!) */
4608 rc = pIMount->pfnUnmount(pIMount, fForceUnmount, false /*=fEject*/);
4609 if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
4610 rc = VINF_SUCCESS;
4611 /* for example if the medium is locked */
4612 else if (RT_FAILURE(rc))
4613 return rc;
4614 }
4615
4616 return rc;
4617}
4618
4619/**
4620 * Removes the currently attached medium driver form the specified device
4621 * taking care of the controlelr specific configs wrt. to the attached driver chain.
4622 *
4623 * @returns VBox status code.
4624 * @param pCtlInst The controler instance node in the CFGM tree.
4625 * @param pcszDevice The device name.
4626 * @param uInstance The device instance.
4627 * @param uLUN The device LUN.
4628 * @param enmBus The storage bus.
4629 * @param fAttachDetach Flag whether this is a change while the VM is running
4630 * @param fHotplug Flag whether the guest should be notified about the device change.
4631 * @param fForceUnmount Flag whether to force unmounting the medium even if it is locked.
4632 * @param pUVM The usermode VM handle.
4633 * @param pVMM The VMM vtable.
4634 * @param enmDevType The device type.
4635 * @param ppLunL0 Where to store the node to attach the new config to on success.
4636 */
4637int Console::i_removeMediumDriverFromVm(PCFGMNODE pCtlInst,
4638 const char *pcszDevice,
4639 unsigned uInstance,
4640 unsigned uLUN,
4641 StorageBus_T enmBus,
4642 bool fAttachDetach,
4643 bool fHotplug,
4644 bool fForceUnmount,
4645 PUVM pUVM,
4646 PCVMMR3VTABLE pVMM,
4647 DeviceType_T enmDevType,
4648 PCFGMNODE *ppLunL0)
4649{
4650 int rc = VINF_SUCCESS;
4651 bool fAddLun = false;
4652
4653 /* First check if the LUN already exists. */
4654 PCFGMNODE pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
4655 AssertReturn(!RT_VALID_PTR(pLunL0) || fAttachDetach, VERR_INTERNAL_ERROR);
4656
4657 if (pLunL0)
4658 {
4659 /*
4660 * Unmount the currently mounted medium if we don't just hot remove the
4661 * complete device (SATA) and it supports unmounting (DVD).
4662 */
4663 if ( (enmDevType != DeviceType_HardDisk)
4664 && !fHotplug)
4665 {
4666 rc = i_unmountMediumFromGuest(pUVM, pVMM, enmBus, enmDevType, pcszDevice, uInstance, uLUN, fForceUnmount);
4667 if (RT_FAILURE(rc))
4668 return rc;
4669 }
4670
4671 /*
4672 * Don't detach the SCSI driver when unmounting the current medium
4673 * (we are not ripping out the device but only eject the medium).
4674 */
4675 char *pszDriverDetach = NULL;
4676 if ( !fHotplug
4677 && ( (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD)
4678 || enmBus == StorageBus_SAS
4679 || enmBus == StorageBus_SCSI
4680 || enmBus == StorageBus_VirtioSCSI
4681 || enmBus == StorageBus_USB))
4682 {
4683 /* Get the current attached driver we have to detach. */
4684 PCFGMNODE pDrvLun = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u/AttachedDriver/", uLUN);
4685 if (pDrvLun)
4686 {
4687 char szDriver[128];
4688 RT_ZERO(szDriver);
4689 rc = pVMM->pfnCFGMR3QueryString(pDrvLun, "Driver", &szDriver[0], sizeof(szDriver));
4690 if (RT_SUCCESS(rc))
4691 pszDriverDetach = RTStrDup(&szDriver[0]);
4692
4693 pLunL0 = pDrvLun;
4694 }
4695 }
4696
4697 if (enmBus == StorageBus_USB)
4698 rc = pVMM->pfnPDMR3UsbDriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach,
4699 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
4700 else
4701 rc = pVMM->pfnPDMR3DriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach,
4702 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
4703
4704 if (pszDriverDetach)
4705 {
4706 RTStrFree(pszDriverDetach);
4707 /* Remove the complete node and create new for the new config. */
4708 pVMM->pfnCFGMR3RemoveNode(pLunL0);
4709 pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
4710 if (pLunL0)
4711 {
4712 try
4713 {
4714 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
4715 }
4716 catch (ConfigError &x)
4717 {
4718 // InsertConfig threw something:
4719 return x.m_vrc;
4720 }
4721 }
4722 }
4723 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
4724 rc = VINF_SUCCESS;
4725 AssertRCReturn(rc, rc);
4726
4727 /*
4728 * Don't remove the LUN except for IDE/floppy/NVMe (which connects directly to the medium driver
4729 * even for DVD devices) or if there is a hotplug event which rips out the complete device.
4730 */
4731 if ( fHotplug
4732 || enmBus == StorageBus_IDE
4733 || enmBus == StorageBus_Floppy
4734 || enmBus == StorageBus_PCIe
4735 || (enmBus == StorageBus_SATA && enmDevType != DeviceType_DVD))
4736 {
4737 fAddLun = true;
4738 pVMM->pfnCFGMR3RemoveNode(pLunL0);
4739 }
4740 }
4741 else
4742 fAddLun = true;
4743
4744 try
4745 {
4746 if (fAddLun)
4747 InsertConfigNodeF(pCtlInst, &pLunL0, "LUN#%u", uLUN);
4748 }
4749 catch (ConfigError &x)
4750 {
4751 // InsertConfig threw something:
4752 return x.m_vrc;
4753 }
4754
4755 if (ppLunL0)
4756 *ppLunL0 = pLunL0;
4757
4758 return rc;
4759}
4760
4761int Console::i_configMediumAttachment(const char *pcszDevice,
4762 unsigned uInstance,
4763 StorageBus_T enmBus,
4764 bool fUseHostIOCache,
4765 bool fBuiltinIOCache,
4766 bool fInsertDiskIntegrityDrv,
4767 bool fSetupMerge,
4768 unsigned uMergeSource,
4769 unsigned uMergeTarget,
4770 IMediumAttachment *pMediumAtt,
4771 MachineState_T aMachineState,
4772 HRESULT *phrc,
4773 bool fAttachDetach,
4774 bool fForceUnmount,
4775 bool fHotplug,
4776 PUVM pUVM,
4777 PCVMMR3VTABLE pVMM,
4778 DeviceType_T *paLedDevType,
4779 PCFGMNODE *ppLunL0)
4780{
4781 // InsertConfig* throws
4782 try
4783 {
4784 int rc = VINF_SUCCESS;
4785 HRESULT hrc;
4786 Bstr bstr;
4787 PCFGMNODE pCtlInst = NULL;
4788
4789// #define RC_CHECK() AssertMsgReturn(RT_SUCCESS(rc), ("rc=%Rrc\n", rc), rc)
4790#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
4791
4792 LONG lDev;
4793 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
4794 LONG lPort;
4795 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
4796 DeviceType_T lType;
4797 hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
4798 BOOL fNonRotational;
4799 hrc = pMediumAtt->COMGETTER(NonRotational)(&fNonRotational); H();
4800 BOOL fDiscard;
4801 hrc = pMediumAtt->COMGETTER(Discard)(&fDiscard); H();
4802
4803 if (lType == DeviceType_DVD)
4804 fInsertDiskIntegrityDrv = false;
4805
4806 unsigned uLUN;
4807 PCFGMNODE pLunL0 = NULL;
4808 hrc = Console::i_storageBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
4809
4810 /* Determine the base path for the device instance. */
4811 if (enmBus != StorageBus_USB)
4812 pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
4813 else
4814 {
4815 /* If we hotplug a USB device create a new CFGM tree. */
4816 if (!fHotplug)
4817 pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "USB/%s/", pcszDevice);
4818 else
4819 pCtlInst = pVMM->pfnCFGMR3CreateTree(pUVM);
4820 }
4821 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4822
4823 if (enmBus == StorageBus_USB)
4824 {
4825 PCFGMNODE pCfg = NULL;
4826
4827 /* Create correct instance. */
4828 if (!fHotplug)
4829 {
4830 if (!fAttachDetach)
4831 InsertConfigNodeF(pCtlInst, &pCtlInst, "%d", lPort);
4832 else
4833 pCtlInst = pVMM->pfnCFGMR3GetChildF(pCtlInst, "%d/", lPort);
4834 }
4835
4836 if (!fAttachDetach)
4837 InsertConfigNode(pCtlInst, "Config", &pCfg);
4838
4839 uInstance = lPort; /* Overwrite uInstance with the correct one. */
4840
4841 if (!fHotplug && !fAttachDetach)
4842 {
4843 char aszUuid[RTUUID_STR_LENGTH + 1];
4844 USBStorageDevice UsbMsd = USBStorageDevice();
4845
4846 memset(aszUuid, 0, sizeof(aszUuid));
4847 rc = RTUuidCreate(&UsbMsd.mUuid);
4848 AssertRCReturn(rc, rc);
4849 rc = RTUuidToStr(&UsbMsd.mUuid, aszUuid, sizeof(aszUuid));
4850 AssertRCReturn(rc, rc);
4851
4852 UsbMsd.iPort = uInstance;
4853
4854 InsertConfigString(pCtlInst, "UUID", aszUuid);
4855 mUSBStorageDevices.push_back(UsbMsd);
4856
4857 /** @todo No LED after hotplugging. */
4858 /* Attach the status driver */
4859 i_attachStatusDriver(pCtlInst, DeviceType_HardDisk, 0, 7, &paLedDevType,
4860 &mapMediumAttachments, pcszDevice, 0);
4861 }
4862 }
4863
4864 rc = i_removeMediumDriverFromVm(pCtlInst, pcszDevice, uInstance, uLUN, enmBus, fAttachDetach,
4865 fHotplug, fForceUnmount, pUVM, pVMM, lType, &pLunL0);
4866 if (RT_FAILURE(rc))
4867 return rc;
4868 if (ppLunL0)
4869 *ppLunL0 = pLunL0;
4870
4871 Utf8StrFmt devicePath("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
4872 mapMediumAttachments[devicePath] = pMediumAtt;
4873
4874 ComPtr<IMedium> ptrMedium;
4875 hrc = pMediumAtt->COMGETTER(Medium)(ptrMedium.asOutParam()); H();
4876
4877 /*
4878 * 1. Only check this for hard disk images.
4879 * 2. Only check during VM creation and not later, especially not during
4880 * taking an online snapshot!
4881 */
4882 if ( lType == DeviceType_HardDisk
4883 && ( aMachineState == MachineState_Starting
4884 || aMachineState == MachineState_Restoring))
4885 {
4886 rc = i_checkMediumLocation(ptrMedium, &fUseHostIOCache);
4887 if (RT_FAILURE(rc))
4888 return rc;
4889 }
4890
4891 BOOL fPassthrough = FALSE;
4892 if (ptrMedium.isNotNull())
4893 {
4894 BOOL fHostDrive;
4895 hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H();
4896 if ( ( lType == DeviceType_DVD
4897 || lType == DeviceType_Floppy)
4898 && !fHostDrive)
4899 {
4900 /*
4901 * Informative logging.
4902 */
4903 Bstr bstrFile;
4904 hrc = ptrMedium->COMGETTER(Location)(bstrFile.asOutParam()); H();
4905 Utf8Str strFile(bstrFile);
4906 RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN;
4907 (void)RTFsQueryType(strFile.c_str(), &enmFsTypeFile);
4908 LogRel(("File system of '%s' (%s) is %s\n",
4909 strFile.c_str(), lType == DeviceType_DVD ? "DVD" : "Floppy", RTFsTypeName(enmFsTypeFile)));
4910 }
4911
4912 if (fHostDrive)
4913 {
4914 hrc = pMediumAtt->COMGETTER(Passthrough)(&fPassthrough); H();
4915 }
4916 }
4917
4918 ComObjPtr<IBandwidthGroup> pBwGroup;
4919 Bstr bstrBwGroup;
4920 hrc = pMediumAtt->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H();
4921
4922 if (!pBwGroup.isNull())
4923 {
4924 hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H();
4925 }
4926
4927 /*
4928 * Insert the SCSI driver for hotplug events on the SCSI/USB based storage controllers
4929 * or for SATA if the new device is a CD/DVD drive.
4930 */
4931 if ( (fHotplug || !fAttachDetach)
4932 && ( (enmBus == StorageBus_SCSI || enmBus == StorageBus_SAS || enmBus == StorageBus_USB || enmBus == StorageBus_VirtioSCSI)
4933 || (enmBus == StorageBus_SATA && lType == DeviceType_DVD && !fPassthrough)))
4934 {
4935 InsertConfigString(pLunL0, "Driver", "SCSI");
4936 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
4937 }
4938
4939 rc = i_configMedium(pLunL0,
4940 !!fPassthrough,
4941 lType,
4942 fUseHostIOCache,
4943 fBuiltinIOCache,
4944 fInsertDiskIntegrityDrv,
4945 fSetupMerge,
4946 uMergeSource,
4947 uMergeTarget,
4948 bstrBwGroup.isEmpty() ? NULL : Utf8Str(bstrBwGroup).c_str(),
4949 !!fDiscard,
4950 !!fNonRotational,
4951 ptrMedium,
4952 aMachineState,
4953 phrc);
4954 if (RT_FAILURE(rc))
4955 return rc;
4956
4957 if (fAttachDetach)
4958 {
4959 /* Attach the new driver. */
4960 if (enmBus == StorageBus_USB)
4961 {
4962 if (fHotplug)
4963 {
4964 USBStorageDevice UsbMsd = USBStorageDevice();
4965 RTUuidCreate(&UsbMsd.mUuid);
4966 UsbMsd.iPort = uInstance;
4967 rc = pVMM->pfnPDMR3UsbCreateEmulatedDevice(pUVM, pcszDevice, pCtlInst, &UsbMsd.mUuid, NULL);
4968 if (RT_SUCCESS(rc))
4969 mUSBStorageDevices.push_back(UsbMsd);
4970 }
4971 else
4972 rc = pVMM->pfnPDMR3UsbDriverAttach(pUVM, pcszDevice, uInstance, uLUN,
4973 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
4974 }
4975 else if ( !fHotplug
4976 && ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI)
4977 || (enmBus == StorageBus_SATA && lType == DeviceType_DVD)))
4978 rc = pVMM->pfnPDMR3DriverAttach(pUVM, pcszDevice, uInstance, uLUN,
4979 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
4980 else
4981 rc = pVMM->pfnPDMR3DeviceAttach(pUVM, pcszDevice, uInstance, uLUN,
4982 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
4983 AssertRCReturn(rc, rc);
4984
4985 /*
4986 * Make the secret key helper interface known to the VD driver if it is attached,
4987 * so we can get notified about missing keys.
4988 */
4989 PPDMIBASE pIBase = NULL;
4990 rc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
4991 if (RT_SUCCESS(rc) && pIBase)
4992 {
4993 PPDMIMEDIA pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
4994 if (pIMedium)
4995 {
4996 rc = pIMedium->pfnSetSecKeyIf(pIMedium, mpIfSecKey, mpIfSecKeyHlp);
4997 Assert(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED);
4998 }
4999 }
5000
5001 /* There is no need to handle removable medium mounting, as we
5002 * unconditionally replace everthing including the block driver level.
5003 * This means the new medium will be picked up automatically. */
5004 }
5005
5006 if (paLedDevType)
5007 paLedDevType[uLUN] = lType;
5008
5009 /* Dump the changed LUN if possible, dump the complete device otherwise */
5010 if ( aMachineState != MachineState_Starting
5011 && aMachineState != MachineState_Restoring)
5012 pVMM->pfnCFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst);
5013 }
5014 catch (ConfigError &x)
5015 {
5016 // InsertConfig threw something:
5017 return x.m_vrc;
5018 }
5019
5020#undef H
5021
5022 return VINF_SUCCESS;
5023}
5024
5025int Console::i_configMedium(PCFGMNODE pLunL0,
5026 bool fPassthrough,
5027 DeviceType_T enmType,
5028 bool fUseHostIOCache,
5029 bool fBuiltinIOCache,
5030 bool fInsertDiskIntegrityDrv,
5031 bool fSetupMerge,
5032 unsigned uMergeSource,
5033 unsigned uMergeTarget,
5034 const char *pcszBwGroup,
5035 bool fDiscard,
5036 bool fNonRotational,
5037 ComPtr<IMedium> ptrMedium,
5038 MachineState_T aMachineState,
5039 HRESULT *phrc)
5040{
5041 // InsertConfig* throws
5042 try
5043 {
5044 HRESULT hrc;
5045 Bstr bstr;
5046 PCFGMNODE pCfg = NULL;
5047
5048#define H() \
5049 AssertMsgReturnStmt(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc), if (phrc) *phrc = hrc, Global::vboxStatusCodeFromCOM(hrc))
5050
5051
5052 BOOL fHostDrive = FALSE;
5053 MediumType_T mediumType = MediumType_Normal;
5054 if (ptrMedium.isNotNull())
5055 {
5056 hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H();
5057 hrc = ptrMedium->COMGETTER(Type)(&mediumType); H();
5058 }
5059
5060 if (fHostDrive)
5061 {
5062 Assert(ptrMedium.isNotNull());
5063 if (enmType == DeviceType_DVD)
5064 {
5065 InsertConfigString(pLunL0, "Driver", "HostDVD");
5066 InsertConfigNode(pLunL0, "Config", &pCfg);
5067
5068 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
5069 InsertConfigString(pCfg, "Path", bstr);
5070
5071 InsertConfigInteger(pCfg, "Passthrough", fPassthrough);
5072 }
5073 else if (enmType == DeviceType_Floppy)
5074 {
5075 InsertConfigString(pLunL0, "Driver", "HostFloppy");
5076 InsertConfigNode(pLunL0, "Config", &pCfg);
5077
5078 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
5079 InsertConfigString(pCfg, "Path", bstr);
5080 }
5081 }
5082 else
5083 {
5084 if (fInsertDiskIntegrityDrv)
5085 {
5086 /*
5087 * The actual configuration is done through CFGM extra data
5088 * for each inserted driver separately.
5089 */
5090 InsertConfigString(pLunL0, "Driver", "DiskIntegrity");
5091 InsertConfigNode(pLunL0, "Config", &pCfg);
5092 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
5093 }
5094
5095 InsertConfigString(pLunL0, "Driver", "VD");
5096 InsertConfigNode(pLunL0, "Config", &pCfg);
5097 switch (enmType)
5098 {
5099 case DeviceType_DVD:
5100 InsertConfigString(pCfg, "Type", "DVD");
5101 InsertConfigInteger(pCfg, "Mountable", 1);
5102 break;
5103 case DeviceType_Floppy:
5104 InsertConfigString(pCfg, "Type", "Floppy 1.44");
5105 InsertConfigInteger(pCfg, "Mountable", 1);
5106 break;
5107 case DeviceType_HardDisk:
5108 default:
5109 InsertConfigString(pCfg, "Type", "HardDisk");
5110 InsertConfigInteger(pCfg, "Mountable", 0);
5111 }
5112
5113 if ( ptrMedium.isNotNull()
5114 && ( enmType == DeviceType_DVD
5115 || enmType == DeviceType_Floppy)
5116 )
5117 {
5118 // if this medium represents an ISO image and this image is inaccessible,
5119 // the ignore it instead of causing a failure; this can happen when we
5120 // restore a VM state and the ISO has disappeared, e.g. because the Guest
5121 // Additions were mounted and the user upgraded VirtualBox. Previously
5122 // we failed on startup, but that's not good because the only way out then
5123 // would be to discard the VM state...
5124 MediumState_T mediumState;
5125 hrc = ptrMedium->RefreshState(&mediumState); H();
5126 if (mediumState == MediumState_Inaccessible)
5127 {
5128 Bstr loc;
5129 hrc = ptrMedium->COMGETTER(Location)(loc.asOutParam()); H();
5130 i_atVMRuntimeErrorCallbackF(0, "DvdOrFloppyImageInaccessible",
5131 N_("The image file '%ls' is inaccessible and is being ignored. "
5132 "Please select a different image file for the virtual %s drive."),
5133 loc.raw(),
5134 enmType == DeviceType_DVD ? "DVD" : "floppy");
5135 ptrMedium.setNull();
5136 }
5137 }
5138
5139 if (ptrMedium.isNotNull())
5140 {
5141 /* Start with length of parent chain, as the list is reversed */
5142 unsigned uImage = 0;
5143 ComPtr<IMedium> ptrTmp = ptrMedium;
5144 while (ptrTmp.isNotNull())
5145 {
5146 uImage++;
5147 ComPtr<IMedium> ptrParent;
5148 hrc = ptrTmp->COMGETTER(Parent)(ptrParent.asOutParam()); H();
5149 ptrTmp = ptrParent;
5150 }
5151 /* Index of last image */
5152 uImage--;
5153
5154# ifdef VBOX_WITH_EXTPACK
5155 if (mptrExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
5156 {
5157 /* Configure loading the VDPlugin. */
5158 static const char s_szVDPlugin[] = "VDPluginCrypt";
5159 PCFGMNODE pCfgPlugins = NULL;
5160 PCFGMNODE pCfgPlugin = NULL;
5161 Utf8Str strPlugin;
5162 hrc = mptrExtPackManager->i_getLibraryPathForExtPack(s_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
5163 // Don't fail, this is optional!
5164 if (SUCCEEDED(hrc))
5165 {
5166 InsertConfigNode(pCfg, "Plugins", &pCfgPlugins);
5167 InsertConfigNode(pCfgPlugins, s_szVDPlugin, &pCfgPlugin);
5168 InsertConfigString(pCfgPlugin, "Path", strPlugin.c_str());
5169 }
5170 }
5171# endif
5172
5173 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
5174 InsertConfigString(pCfg, "Path", bstr);
5175
5176 hrc = ptrMedium->COMGETTER(Format)(bstr.asOutParam()); H();
5177 InsertConfigString(pCfg, "Format", bstr);
5178
5179 if (mediumType == MediumType_Readonly)
5180 InsertConfigInteger(pCfg, "ReadOnly", 1);
5181 else if (enmType == DeviceType_Floppy)
5182 InsertConfigInteger(pCfg, "MaybeReadOnly", 1);
5183
5184 /* Start without exclusive write access to the images. */
5185 /** @todo Live Migration: I don't quite like this, we risk screwing up when
5186 * we're resuming the VM if some 3rd dude have any of the VDIs open
5187 * with write sharing denied. However, if the two VMs are sharing a
5188 * image it really is necessary....
5189 *
5190 * So, on the "lock-media" command, the target teleporter should also
5191 * make DrvVD undo TempReadOnly. It gets interesting if we fail after
5192 * that. Grumble. */
5193 if ( enmType == DeviceType_HardDisk
5194 && aMachineState == MachineState_TeleportingIn)
5195 InsertConfigInteger(pCfg, "TempReadOnly", 1);
5196
5197 /* Flag for opening the medium for sharing between VMs. This
5198 * is done at the moment only for the first (and only) medium
5199 * in the chain, as shared media can have no diffs. */
5200 if (mediumType == MediumType_Shareable)
5201 InsertConfigInteger(pCfg, "Shareable", 1);
5202
5203 if (!fUseHostIOCache)
5204 {
5205 InsertConfigInteger(pCfg, "UseNewIo", 1);
5206 /*
5207 * Activate the builtin I/O cache for harddisks only.
5208 * It caches writes only which doesn't make sense for DVD drives
5209 * and just increases the overhead.
5210 */
5211 if ( fBuiltinIOCache
5212 && (enmType == DeviceType_HardDisk))
5213 InsertConfigInteger(pCfg, "BlockCache", 1);
5214 }
5215
5216 if (fSetupMerge)
5217 {
5218 InsertConfigInteger(pCfg, "SetupMerge", 1);
5219 if (uImage == uMergeSource)
5220 InsertConfigInteger(pCfg, "MergeSource", 1);
5221 else if (uImage == uMergeTarget)
5222 InsertConfigInteger(pCfg, "MergeTarget", 1);
5223 }
5224
5225 if (pcszBwGroup)
5226 InsertConfigString(pCfg, "BwGroup", pcszBwGroup);
5227
5228 if (fDiscard)
5229 InsertConfigInteger(pCfg, "Discard", 1);
5230
5231 if (fNonRotational)
5232 InsertConfigInteger(pCfg, "NonRotationalMedium", 1);
5233
5234 /* Pass all custom parameters. */
5235 bool fHostIP = true;
5236 bool fEncrypted = false;
5237 hrc = i_configMediumProperties(pCfg, ptrMedium, &fHostIP, &fEncrypted); H();
5238
5239 /* Create an inverted list of parents. */
5240 uImage--;
5241 ComPtr<IMedium> ptrParentMedium = ptrMedium;
5242 for (PCFGMNODE pParent = pCfg;; uImage--)
5243 {
5244 ComPtr<IMedium> ptrCurMedium;
5245 hrc = ptrParentMedium->COMGETTER(Parent)(ptrCurMedium.asOutParam()); H();
5246 if (ptrCurMedium.isNull())
5247 break;
5248
5249 PCFGMNODE pCur;
5250 InsertConfigNode(pParent, "Parent", &pCur);
5251 hrc = ptrCurMedium->COMGETTER(Location)(bstr.asOutParam()); H();
5252 InsertConfigString(pCur, "Path", bstr);
5253
5254 hrc = ptrCurMedium->COMGETTER(Format)(bstr.asOutParam()); H();
5255 InsertConfigString(pCur, "Format", bstr);
5256
5257 if (fSetupMerge)
5258 {
5259 if (uImage == uMergeSource)
5260 InsertConfigInteger(pCur, "MergeSource", 1);
5261 else if (uImage == uMergeTarget)
5262 InsertConfigInteger(pCur, "MergeTarget", 1);
5263 }
5264
5265 /* Configure medium properties. */
5266 hrc = i_configMediumProperties(pCur, ptrCurMedium, &fHostIP, &fEncrypted); H();
5267
5268 /* next */
5269 pParent = pCur;
5270 ptrParentMedium = ptrCurMedium;
5271 }
5272
5273 /* Custom code: put marker to not use host IP stack to driver
5274 * configuration node. Simplifies life of DrvVD a bit. */
5275 if (!fHostIP)
5276 InsertConfigInteger(pCfg, "HostIPStack", 0);
5277
5278 if (fEncrypted)
5279 m_cDisksEncrypted++;
5280 }
5281 else
5282 {
5283 /* Set empty drive flag for DVD or floppy without media. */
5284 if ( enmType == DeviceType_DVD
5285 || enmType == DeviceType_Floppy)
5286 InsertConfigInteger(pCfg, "EmptyDrive", 1);
5287 }
5288 }
5289#undef H
5290 }
5291 catch (ConfigError &x)
5292 {
5293 // InsertConfig threw something:
5294 return x.m_vrc;
5295 }
5296
5297 return VINF_SUCCESS;
5298}
5299
5300/**
5301 * Adds the medium properties to the CFGM tree.
5302 *
5303 * @returns VBox status code.
5304 * @param pCur The current CFGM node.
5305 * @param pMedium The medium object to configure.
5306 * @param pfHostIP Where to return the value of the \"HostIPStack\" property if found.
5307 * @param pfEncrypted Where to return whether the medium is encrypted.
5308 */
5309int Console::i_configMediumProperties(PCFGMNODE pCur, IMedium *pMedium, bool *pfHostIP, bool *pfEncrypted)
5310{
5311 /* Pass all custom parameters. */
5312 SafeArray<BSTR> aNames;
5313 SafeArray<BSTR> aValues;
5314 HRESULT hrc = pMedium->GetProperties(NULL, ComSafeArrayAsOutParam(aNames),
5315 ComSafeArrayAsOutParam(aValues));
5316
5317 if ( SUCCEEDED(hrc)
5318 && aNames.size() != 0)
5319 {
5320 PCFGMNODE pVDC;
5321 InsertConfigNode(pCur, "VDConfig", &pVDC);
5322 for (size_t ii = 0; ii < aNames.size(); ++ii)
5323 {
5324 if (aValues[ii] && *aValues[ii])
5325 {
5326 Utf8Str name = aNames[ii];
5327 Utf8Str value = aValues[ii];
5328 size_t offSlash = name.find("/", 0);
5329 if ( offSlash != name.npos
5330 && !name.startsWith("Special/"))
5331 {
5332 com::Utf8Str strFilter;
5333 com::Utf8Str strKey;
5334
5335 hrc = strFilter.assignEx(name, 0, offSlash);
5336 if (FAILED(hrc))
5337 break;
5338
5339 hrc = strKey.assignEx(name, offSlash + 1, name.length() - offSlash - 1); /* Skip slash */
5340 if (FAILED(hrc))
5341 break;
5342
5343 PCFGMNODE pCfgFilterConfig = mpVMM->pfnCFGMR3GetChild(pVDC, strFilter.c_str());
5344 if (!pCfgFilterConfig)
5345 InsertConfigNode(pVDC, strFilter.c_str(), &pCfgFilterConfig);
5346
5347 InsertConfigString(pCfgFilterConfig, strKey.c_str(), value);
5348 }
5349 else
5350 {
5351 InsertConfigString(pVDC, name.c_str(), value);
5352 if ( name.compare("HostIPStack") == 0
5353 && value.compare("0") == 0)
5354 *pfHostIP = false;
5355 }
5356
5357 if ( name.compare("CRYPT/KeyId") == 0
5358 && pfEncrypted)
5359 *pfEncrypted = true;
5360 }
5361 }
5362 }
5363
5364 return hrc;
5365}
5366
5367
5368/**
5369 * Construct the Network configuration tree
5370 *
5371 * @returns VBox status code.
5372 *
5373 * @param pszDevice The PDM device name.
5374 * @param uInstance The PDM device instance.
5375 * @param uLun The PDM LUN number of the drive.
5376 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
5377 * @param pCfg Configuration node for the device
5378 * @param pLunL0 To store the pointer to the LUN#0.
5379 * @param pInst The instance CFGM node
5380 * @param fAttachDetach To determine if the network attachment should
5381 * be attached/detached after/before
5382 * configuration.
5383 * @param fIgnoreConnectFailure
5384 * True if connection failures should be ignored
5385 * (makes only sense for bridged/host-only networks).
5386 * @param pUVM The usermode VM handle.
5387 * @param pVMM The VMM vtable.
5388 *
5389 * @note Locks this object for writing.
5390 * @thread EMT
5391 */
5392int Console::i_configNetwork(const char *pszDevice,
5393 unsigned uInstance,
5394 unsigned uLun,
5395 INetworkAdapter *aNetworkAdapter,
5396 PCFGMNODE pCfg,
5397 PCFGMNODE pLunL0,
5398 PCFGMNODE pInst,
5399 bool fAttachDetach,
5400 bool fIgnoreConnectFailure,
5401 PUVM pUVM,
5402 PCVMMR3VTABLE pVMM)
5403{
5404 RT_NOREF(fIgnoreConnectFailure);
5405 AutoCaller autoCaller(this);
5406 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
5407
5408 // InsertConfig* throws
5409 try
5410 {
5411 int rc = VINF_SUCCESS;
5412 HRESULT hrc;
5413 Bstr bstr;
5414
5415#ifdef VBOX_WITH_CLOUD_NET
5416 /* We'll need device's pCfg for cloud attachments */
5417 PCFGMNODE pDevCfg = pCfg;
5418#endif /* VBOX_WITH_CLOUD_NET */
5419
5420#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
5421
5422 /*
5423 * Locking the object before doing VMR3* calls is quite safe here, since
5424 * we're on EMT. Write lock is necessary because we indirectly modify the
5425 * meAttachmentType member.
5426 */
5427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5428
5429 ComPtr<IMachine> pMachine = i_machine();
5430
5431 ComPtr<IVirtualBox> virtualBox;
5432 hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H();
5433
5434 ComPtr<IHost> host;
5435 hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H();
5436
5437 BOOL fSniffer;
5438 hrc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fSniffer); H();
5439
5440 NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
5441 hrc = aNetworkAdapter->COMGETTER(PromiscModePolicy)(&enmPromiscModePolicy); H();
5442 const char *pszPromiscuousGuestPolicy;
5443 switch (enmPromiscModePolicy)
5444 {
5445 case NetworkAdapterPromiscModePolicy_Deny: pszPromiscuousGuestPolicy = "deny"; break;
5446 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = "allow-network"; break;
5447 case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = "allow-all"; break;
5448 default: AssertFailedReturn(VERR_INTERNAL_ERROR_4);
5449 }
5450
5451 if (fAttachDetach)
5452 {
5453 rc = pVMM->pfnPDMR3DeviceDetach(pUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/);
5454 if (rc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)
5455 rc = VINF_SUCCESS;
5456 AssertLogRelRCReturn(rc, rc);
5457
5458 /* Nuke anything which might have been left behind. */
5459 pVMM->pfnCFGMR3RemoveNode(pVMM->pfnCFGMR3GetChildF(pInst, "LUN#%u", uLun));
5460 }
5461
5462 Bstr networkName, trunkName, trunkType;
5463 NetworkAttachmentType_T eAttachmentType;
5464 hrc = aNetworkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H();
5465
5466#ifdef VBOX_WITH_NETSHAPER
5467 ComObjPtr<IBandwidthGroup> pBwGroup;
5468 Bstr bstrBwGroup;
5469 hrc = aNetworkAdapter->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H();
5470
5471 if (!pBwGroup.isNull())
5472 {
5473 hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H();
5474 }
5475#endif /* VBOX_WITH_NETSHAPER */
5476
5477 AssertMsg(uLun == 0, ("Network attachments with LUN > 0 are not supported yet\n"));
5478 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", uLun);
5479
5480 /*
5481 * Do not insert neither a shaper nor a sniffer if we are not attached to anything.
5482 * This way we can easily detect if we are attached to anything at the device level.
5483 */
5484#ifdef VBOX_WITH_NETSHAPER
5485 if (bstrBwGroup.isNotEmpty() && eAttachmentType != NetworkAttachmentType_Null)
5486 {
5487 InsertConfigString(pLunL0, "Driver", "NetShaper");
5488 InsertConfigNode(pLunL0, "Config", &pCfg);
5489 InsertConfigString(pCfg, "BwGroup", bstrBwGroup);
5490 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
5491 }
5492#endif /* VBOX_WITH_NETSHAPER */
5493
5494 if (fSniffer && eAttachmentType != NetworkAttachmentType_Null)
5495 {
5496 InsertConfigString(pLunL0, "Driver", "NetSniffer");
5497 InsertConfigNode(pLunL0, "Config", &pCfg);
5498 hrc = aNetworkAdapter->COMGETTER(TraceFile)(bstr.asOutParam()); H();
5499 if (!bstr.isEmpty()) /* check convention for indicating default file. */
5500 InsertConfigString(pCfg, "File", bstr);
5501 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
5502 }
5503
5504 switch (eAttachmentType)
5505 {
5506 case NetworkAttachmentType_Null:
5507 break;
5508
5509 case NetworkAttachmentType_NAT:
5510 {
5511 ComPtr<INATEngine> natEngine;
5512 hrc = aNetworkAdapter->COMGETTER(NATEngine)(natEngine.asOutParam()); H();
5513 InsertConfigString(pLunL0, "Driver", "NAT");
5514 InsertConfigNode(pLunL0, "Config", &pCfg);
5515
5516 /* Configure TFTP prefix and boot filename. */
5517 hrc = virtualBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H();
5518 if (!bstr.isEmpty())
5519 InsertConfigString(pCfg, "TFTPPrefix", Utf8StrFmt("%ls%c%s", bstr.raw(), RTPATH_DELIMITER, "TFTP"));
5520 hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H();
5521 InsertConfigString(pCfg, "BootFile", Utf8StrFmt("%ls.pxe", bstr.raw()));
5522
5523 hrc = natEngine->COMGETTER(Network)(bstr.asOutParam()); H();
5524 if (!bstr.isEmpty())
5525 InsertConfigString(pCfg, "Network", bstr);
5526 else
5527 {
5528 ULONG uSlot;
5529 hrc = aNetworkAdapter->COMGETTER(Slot)(&uSlot); H();
5530 InsertConfigString(pCfg, "Network", Utf8StrFmt("10.0.%d.0/24", uSlot+2));
5531 }
5532 hrc = natEngine->COMGETTER(HostIP)(bstr.asOutParam()); H();
5533 if (!bstr.isEmpty())
5534 InsertConfigString(pCfg, "BindIP", bstr);
5535 ULONG mtu = 0;
5536 ULONG sockSnd = 0;
5537 ULONG sockRcv = 0;
5538 ULONG tcpSnd = 0;
5539 ULONG tcpRcv = 0;
5540 hrc = natEngine->GetNetworkSettings(&mtu, &sockSnd, &sockRcv, &tcpSnd, &tcpRcv); H();
5541 if (mtu)
5542 InsertConfigInteger(pCfg, "SlirpMTU", mtu);
5543 if (sockRcv)
5544 InsertConfigInteger(pCfg, "SockRcv", sockRcv);
5545 if (sockSnd)
5546 InsertConfigInteger(pCfg, "SockSnd", sockSnd);
5547 if (tcpRcv)
5548 InsertConfigInteger(pCfg, "TcpRcv", tcpRcv);
5549 if (tcpSnd)
5550 InsertConfigInteger(pCfg, "TcpSnd", tcpSnd);
5551 hrc = natEngine->COMGETTER(TFTPPrefix)(bstr.asOutParam()); H();
5552 if (!bstr.isEmpty())
5553 {
5554 RemoveConfigValue(pCfg, "TFTPPrefix");
5555 InsertConfigString(pCfg, "TFTPPrefix", bstr);
5556 }
5557 hrc = natEngine->COMGETTER(TFTPBootFile)(bstr.asOutParam()); H();
5558 if (!bstr.isEmpty())
5559 {
5560 RemoveConfigValue(pCfg, "BootFile");
5561 InsertConfigString(pCfg, "BootFile", bstr);
5562 }
5563 hrc = natEngine->COMGETTER(TFTPNextServer)(bstr.asOutParam()); H();
5564 if (!bstr.isEmpty())
5565 InsertConfigString(pCfg, "NextServer", bstr);
5566 BOOL fDNSFlag;
5567 hrc = natEngine->COMGETTER(DNSPassDomain)(&fDNSFlag); H();
5568 InsertConfigInteger(pCfg, "PassDomain", fDNSFlag);
5569 hrc = natEngine->COMGETTER(DNSProxy)(&fDNSFlag); H();
5570 InsertConfigInteger(pCfg, "DNSProxy", fDNSFlag);
5571 hrc = natEngine->COMGETTER(DNSUseHostResolver)(&fDNSFlag); H();
5572 InsertConfigInteger(pCfg, "UseHostResolver", fDNSFlag);
5573
5574 ULONG aliasMode;
5575 hrc = natEngine->COMGETTER(AliasMode)(&aliasMode); H();
5576 InsertConfigInteger(pCfg, "AliasMode", aliasMode);
5577
5578 BOOL fLocalhostReachable;
5579 hrc = natEngine->COMGETTER(LocalhostReachable)(&fLocalhostReachable); H();
5580 InsertConfigInteger(pCfg, "LocalhostReachable", fLocalhostReachable);
5581
5582 /* port-forwarding */
5583 SafeArray<BSTR> pfs;
5584 hrc = natEngine->COMGETTER(Redirects)(ComSafeArrayAsOutParam(pfs)); H();
5585
5586 PCFGMNODE pPFTree = NULL;
5587 if (pfs.size() > 0)
5588 InsertConfigNode(pCfg, "PortForwarding", &pPFTree);
5589
5590 for (unsigned int i = 0; i < pfs.size(); ++i)
5591 {
5592 PCFGMNODE pPF = NULL; /* /Devices/Dev/.../Config/PortForwarding/$n/ */
5593
5594 uint16_t port = 0;
5595 Utf8Str utf = pfs[i];
5596 Utf8Str strName;
5597 Utf8Str strProto;
5598 Utf8Str strHostPort;
5599 Utf8Str strHostIP;
5600 Utf8Str strGuestPort;
5601 Utf8Str strGuestIP;
5602 size_t pos, ppos;
5603 pos = ppos = 0;
5604#define ITERATE_TO_NEXT_TERM(res, str, pos, ppos) \
5605 { \
5606 pos = str.find(",", ppos); \
5607 if (pos == Utf8Str::npos) \
5608 { \
5609 Log(( #res " extracting from %s is failed\n", str.c_str())); \
5610 continue; \
5611 } \
5612 res = str.substr(ppos, pos - ppos); \
5613 Log2((#res " %s pos:%d, ppos:%d\n", res.c_str(), pos, ppos)); \
5614 ppos = pos + 1; \
5615 } /* no do { ... } while because of 'continue' */
5616 ITERATE_TO_NEXT_TERM(strName, utf, pos, ppos);
5617 ITERATE_TO_NEXT_TERM(strProto, utf, pos, ppos);
5618 ITERATE_TO_NEXT_TERM(strHostIP, utf, pos, ppos);
5619 ITERATE_TO_NEXT_TERM(strHostPort, utf, pos, ppos);
5620 ITERATE_TO_NEXT_TERM(strGuestIP, utf, pos, ppos);
5621 strGuestPort = utf.substr(ppos, utf.length() - ppos);
5622#undef ITERATE_TO_NEXT_TERM
5623
5624 uint32_t proto = strProto.toUInt32();
5625 bool fValid = true;
5626 switch (proto)
5627 {
5628 case NATProtocol_UDP:
5629 strProto = "UDP";
5630 break;
5631 case NATProtocol_TCP:
5632 strProto = "TCP";
5633 break;
5634 default:
5635 fValid = false;
5636 }
5637 /* continue with next rule if no valid proto was passed */
5638 if (!fValid)
5639 continue;
5640
5641 InsertConfigNode(pPFTree, Utf8StrFmt("%u", i).c_str(), &pPF);
5642
5643 if (!strName.isEmpty())
5644 InsertConfigString(pPF, "Name", strName);
5645
5646 InsertConfigString(pPF, "Protocol", strProto);
5647
5648 if (!strHostIP.isEmpty())
5649 InsertConfigString(pPF, "BindIP", strHostIP);
5650
5651 if (!strGuestIP.isEmpty())
5652 InsertConfigString(pPF, "GuestIP", strGuestIP);
5653
5654 port = RTStrToUInt16(strHostPort.c_str());
5655 if (port)
5656 InsertConfigInteger(pPF, "HostPort", port);
5657
5658 port = RTStrToUInt16(strGuestPort.c_str());
5659 if (port)
5660 InsertConfigInteger(pPF, "GuestPort", port);
5661 }
5662 break;
5663 }
5664
5665 case NetworkAttachmentType_Bridged:
5666 {
5667#if (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT)
5668 hrc = i_attachToTapInterface(aNetworkAdapter);
5669 if (FAILED(hrc))
5670 {
5671 switch (hrc)
5672 {
5673 case E_ACCESSDENIED:
5674 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
5675 "Failed to open '/dev/net/tun' for read/write access. Please check the "
5676 "permissions of that node. Either run 'chmod 0666 /dev/net/tun' or "
5677 "change the group of that node and make yourself a member of that group. "
5678 "Make sure that these changes are permanent, especially if you are "
5679 "using udev"));
5680 default:
5681 AssertMsgFailed(("Could not attach to host interface! Bad!\n"));
5682 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
5683 N_("Failed to initialize Host Interface Networking"));
5684 }
5685 }
5686
5687 Assert((intptr_t)maTapFD[uInstance] >= 0);
5688 if ((intptr_t)maTapFD[uInstance] >= 0)
5689 {
5690 InsertConfigString(pLunL0, "Driver", "HostInterface");
5691 InsertConfigNode(pLunL0, "Config", &pCfg);
5692 InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]);
5693 }
5694
5695#elif defined(VBOX_WITH_NETFLT)
5696 /*
5697 * This is the new VBoxNetFlt+IntNet stuff.
5698 */
5699 Bstr BridgedIfName;
5700 hrc = aNetworkAdapter->COMGETTER(BridgedInterface)(BridgedIfName.asOutParam());
5701 if (FAILED(hrc))
5702 {
5703 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(BridgedInterface) failed, hrc (0x%x)\n", hrc));
5704 H();
5705 }
5706
5707 Utf8Str BridgedIfNameUtf8(BridgedIfName);
5708 const char *pszBridgedIfName = BridgedIfNameUtf8.c_str();
5709
5710 ComPtr<IHostNetworkInterface> hostInterface;
5711 hrc = host->FindHostNetworkInterfaceByName(BridgedIfName.raw(),
5712 hostInterface.asOutParam());
5713 if (!SUCCEEDED(hrc))
5714 {
5715 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: FindByName failed, rc=%Rhrc (0x%x)\n", hrc, hrc));
5716 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
5717 N_("Nonexistent host networking interface, name '%ls'"),
5718 BridgedIfName.raw());
5719 }
5720
5721# if defined(RT_OS_DARWIN)
5722 /* The name is in the format 'ifX: long name', chop it off at the colon. */
5723 char szTrunk[INTNET_MAX_TRUNK_NAME];
5724 RTStrCopy(szTrunk, sizeof(szTrunk), pszBridgedIfName);
5725 char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk));
5726// Quick fix for @bugref{5633}
5727// if (!pszColon)
5728// {
5729// /*
5730// * Dynamic changing of attachment causes an attempt to configure
5731// * network with invalid host adapter (as it is must be changed before
5732// * the attachment), calling Detach here will cause a deadlock.
5733// * See @bugref{4750}.
5734// * hrc = aNetworkAdapter->Detach(); H();
5735// */
5736// return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
5737// N_("Malformed host interface networking name '%ls'"),
5738// BridgedIfName.raw());
5739// }
5740 if (pszColon)
5741 *pszColon = '\0';
5742 const char *pszTrunk = szTrunk;
5743
5744# elif defined(RT_OS_SOLARIS)
5745 /* The name is in the format 'ifX[:1] - long name, chop it off at space. */
5746 char szTrunk[256];
5747 strlcpy(szTrunk, pszBridgedIfName, sizeof(szTrunk));
5748 char *pszSpace = (char *)memchr(szTrunk, ' ', sizeof(szTrunk));
5749
5750 /*
5751 * Currently don't bother about malformed names here for the sake of people using
5752 * VBoxManage and setting only the NIC name from there. If there is a space we
5753 * chop it off and proceed, otherwise just use whatever we've got.
5754 */
5755 if (pszSpace)
5756 *pszSpace = '\0';
5757
5758 /* Chop it off at the colon (zone naming eg: e1000g:1 we need only the e1000g) */
5759 char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk));
5760 if (pszColon)
5761 *pszColon = '\0';
5762
5763 const char *pszTrunk = szTrunk;
5764
5765# elif defined(RT_OS_WINDOWS)
5766 HostNetworkInterfaceType_T eIfType;
5767 hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
5768 if (FAILED(hrc))
5769 {
5770 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
5771 H();
5772 }
5773
5774 if (eIfType != HostNetworkInterfaceType_Bridged)
5775 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
5776 N_("Interface ('%ls') is not a Bridged Adapter interface"),
5777 BridgedIfName.raw());
5778
5779 hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
5780 if (FAILED(hrc))
5781 {
5782 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
5783 H();
5784 }
5785 Guid hostIFGuid(bstr);
5786
5787 INetCfg *pNc;
5788 ComPtr<INetCfgComponent> pAdaptorComponent;
5789 LPWSTR pszApp;
5790
5791 hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp);
5792 Assert(hrc == S_OK);
5793 if (hrc != S_OK)
5794 {
5795 LogRel(("NetworkAttachmentType_Bridged: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc));
5796 H();
5797 }
5798
5799 /* get the adapter's INetCfgComponent*/
5800 hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(),
5801 pAdaptorComponent.asOutParam());
5802 if (hrc != S_OK)
5803 {
5804 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
5805 LogRel(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)\n", hrc));
5806 H();
5807 }
5808# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
5809 char szTrunkName[INTNET_MAX_TRUNK_NAME];
5810 char *pszTrunkName = szTrunkName;
5811 wchar_t * pswzBindName;
5812 hrc = pAdaptorComponent->GetBindName(&pswzBindName);
5813 Assert(hrc == S_OK);
5814 if (hrc == S_OK)
5815 {
5816 int cwBindName = (int)wcslen(pswzBindName) + 1;
5817 int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX);
5818 if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName)
5819 {
5820 strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX);
5821 pszTrunkName += cbFullBindNamePrefix-1;
5822 if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName,
5823 sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL))
5824 {
5825 DWORD err = GetLastError();
5826 hrc = HRESULT_FROM_WIN32(err);
5827 AssertMsgFailed(("hrc=%Rhrc %#x\n", hrc, hrc));
5828 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n",
5829 hrc, hrc, err));
5830 }
5831 }
5832 else
5833 {
5834 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: insufficient szTrunkName buffer space\n"));
5835 /** @todo set appropriate error code */
5836 hrc = E_FAIL;
5837 }
5838
5839 if (hrc != S_OK)
5840 {
5841 AssertFailed();
5842 CoTaskMemFree(pswzBindName);
5843 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
5844 H();
5845 }
5846
5847 /* we're not freeing the bind name since we'll use it later for detecting wireless*/
5848 }
5849 else
5850 {
5851 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
5852 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)",
5853 hrc));
5854 H();
5855 }
5856
5857 const char *pszTrunk = szTrunkName;
5858 /* we're not releasing the INetCfg stuff here since we use it later to figure out whether it is wireless */
5859
5860# elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
5861# if defined(RT_OS_FREEBSD)
5862 /*
5863 * If we bridge to a tap interface open it the `old' direct way.
5864 * This works and performs better than bridging a physical
5865 * interface via the current FreeBSD vboxnetflt implementation.
5866 */
5867 if (!strncmp(pszBridgedIfName, RT_STR_TUPLE("tap"))) {
5868 hrc = i_attachToTapInterface(aNetworkAdapter);
5869 if (FAILED(hrc))
5870 {
5871 switch (hrc)
5872 {
5873 case E_ACCESSDENIED:
5874 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
5875 "Failed to open '/dev/%s' for read/write access. Please check the "
5876 "permissions of that node, and that the net.link.tap.user_open "
5877 "sysctl is set. Either run 'chmod 0666 /dev/%s' or change the "
5878 "group of that node to vboxusers and make yourself a member of "
5879 "that group. Make sure that these changes are permanent."),
5880 pszBridgedIfName, pszBridgedIfName);
5881 default:
5882 AssertMsgFailed(("Could not attach to tap interface! Bad!\n"));
5883 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
5884 N_("Failed to initialize Host Interface Networking"));
5885 }
5886 }
5887
5888 Assert((intptr_t)maTapFD[uInstance] >= 0);
5889 if ((intptr_t)maTapFD[uInstance] >= 0)
5890 {
5891 InsertConfigString(pLunL0, "Driver", "HostInterface");
5892 InsertConfigNode(pLunL0, "Config", &pCfg);
5893 InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]);
5894 }
5895 break;
5896 }
5897# endif
5898 /** @todo Check for malformed names. */
5899 const char *pszTrunk = pszBridgedIfName;
5900
5901 /* Issue a warning if the interface is down */
5902 {
5903 int iSock = socket(AF_INET, SOCK_DGRAM, 0);
5904 if (iSock >= 0)
5905 {
5906 struct ifreq Req;
5907 RT_ZERO(Req);
5908 RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszBridgedIfName);
5909 if (ioctl(iSock, SIOCGIFFLAGS, &Req) >= 0)
5910 if ((Req.ifr_flags & IFF_UP) == 0)
5911 i_atVMRuntimeErrorCallbackF(0, "BridgedInterfaceDown",
5912 N_("Bridged interface %s is down. Guest will not be able to use this interface"),
5913 pszBridgedIfName);
5914
5915 close(iSock);
5916 }
5917 }
5918
5919# else
5920# error "PORTME (VBOX_WITH_NETFLT)"
5921# endif
5922
5923# if defined(RT_OS_DARWIN) && defined(VBOX_WITH_VMNET)
5924 InsertConfigString(pLunL0, "Driver", "VMNet");
5925 InsertConfigNode(pLunL0, "Config", &pCfg);
5926 InsertConfigString(pCfg, "Trunk", pszTrunk);
5927 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
5928# else
5929 InsertConfigString(pLunL0, "Driver", "IntNet");
5930 InsertConfigNode(pLunL0, "Config", &pCfg);
5931 InsertConfigString(pCfg, "Trunk", pszTrunk);
5932 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
5933 InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure);
5934 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
5935 char szNetwork[INTNET_MAX_NETWORK_NAME];
5936
5937# if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
5938 /*
5939 * 'pszTrunk' contains just the interface name required in ring-0, while 'pszBridgedIfName' contains
5940 * interface name + optional description. We must not pass any description to the VM as it can differ
5941 * for the same interface name, eg: "nge0 - ethernet" (GUI) vs "nge0" (VBoxManage).
5942 */
5943 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszTrunk);
5944# else
5945 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszBridgedIfName);
5946# endif
5947 InsertConfigString(pCfg, "Network", szNetwork);
5948 networkName = Bstr(szNetwork);
5949 trunkName = Bstr(pszTrunk);
5950 trunkType = Bstr(TRUNKTYPE_NETFLT);
5951
5952 BOOL fSharedMacOnWire = false;
5953 hrc = hostInterface->COMGETTER(Wireless)(&fSharedMacOnWire);
5954 if (FAILED(hrc))
5955 {
5956 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Wireless) failed, hrc (0x%x)\n", hrc));
5957 H();
5958 }
5959 else if (fSharedMacOnWire)
5960 {
5961 InsertConfigInteger(pCfg, "SharedMacOnWire", true);
5962 Log(("Set SharedMacOnWire\n"));
5963 }
5964
5965# if defined(RT_OS_SOLARIS)
5966# if 0 /* bird: this is a bit questionable and might cause more trouble than its worth. */
5967 /* Zone access restriction, don't allow snooping the global zone. */
5968 zoneid_t ZoneId = getzoneid();
5969 if (ZoneId != GLOBAL_ZONEID)
5970 {
5971 InsertConfigInteger(pCfg, "IgnoreAllPromisc", true);
5972 }
5973# endif
5974# endif
5975# endif
5976
5977#elif defined(RT_OS_WINDOWS) /* not defined NetFlt */
5978 /* NOTHING TO DO HERE */
5979#elif defined(RT_OS_LINUX)
5980/// @todo aleksey: is there anything to be done here?
5981#elif defined(RT_OS_FREEBSD)
5982/** @todo FreeBSD: Check out this later (HIF networking). */
5983#else
5984# error "Port me"
5985#endif
5986 break;
5987 }
5988
5989 case NetworkAttachmentType_Internal:
5990 {
5991 hrc = aNetworkAdapter->COMGETTER(InternalNetwork)(bstr.asOutParam()); H();
5992 if (!bstr.isEmpty())
5993 {
5994 InsertConfigString(pLunL0, "Driver", "IntNet");
5995 InsertConfigNode(pLunL0, "Config", &pCfg);
5996 InsertConfigString(pCfg, "Network", bstr);
5997 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
5998 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
5999 networkName = bstr;
6000 trunkType = Bstr(TRUNKTYPE_WHATEVER);
6001 }
6002 break;
6003 }
6004
6005 case NetworkAttachmentType_HostOnly:
6006 {
6007 InsertConfigString(pLunL0, "Driver", "IntNet");
6008 InsertConfigNode(pLunL0, "Config", &pCfg);
6009
6010 Bstr HostOnlyName;
6011 hrc = aNetworkAdapter->COMGETTER(HostOnlyInterface)(HostOnlyName.asOutParam());
6012 if (FAILED(hrc))
6013 {
6014 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(HostOnlyInterface) failed, hrc (0x%x)\n", hrc));
6015 H();
6016 }
6017
6018 Utf8Str HostOnlyNameUtf8(HostOnlyName);
6019 const char *pszHostOnlyName = HostOnlyNameUtf8.c_str();
6020#ifdef VBOX_WITH_VMNET
6021 /* Check if the matching host-only network has already been created. */
6022 Bstr bstrLowerIP, bstrUpperIP, bstrNetworkMask;
6023 BstrFmt bstrNetworkName("Legacy %s Network", pszHostOnlyName);
6024 ComPtr<IHostOnlyNetwork> hostOnlyNetwork;
6025 hrc = virtualBox->FindHostOnlyNetworkByName(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam());
6026 if (FAILED(hrc))
6027 {
6028 /*
6029 * With VMNET there is no VBoxNetAdp to create vboxnetX adapters,
6030 * which means that the Host object won't be able to re-create
6031 * them from extra data. Go through existing DHCP/adapter config
6032 * to derive the parameters for the new network.
6033 */
6034 BstrFmt bstrOldNetworkName("HostInterfaceNetworking-%s", pszHostOnlyName);
6035 ComPtr<IDHCPServer> dhcpServer;
6036 hrc = virtualBox->FindDHCPServerByNetworkName(bstrOldNetworkName.raw(),
6037 dhcpServer.asOutParam());
6038 if (SUCCEEDED(hrc))
6039 {
6040 /* There is a DHCP server available for this network. */
6041 hrc = dhcpServer->COMGETTER(LowerIP)(bstrLowerIP.asOutParam());
6042 if (FAILED(hrc))
6043 {
6044 LogRel(("Console::i_configNetwork: COMGETTER(LowerIP) failed, hrc (%Rhrc)\n", hrc));
6045 H();
6046 }
6047 hrc = dhcpServer->COMGETTER(UpperIP)(bstrUpperIP.asOutParam());
6048 if (FAILED(hrc))
6049 {
6050 LogRel(("Console::i_configNetwork: COMGETTER(UpperIP) failed, hrc (%Rhrc)\n", hrc));
6051 H();
6052 }
6053 hrc = dhcpServer->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam());
6054 if (FAILED(hrc))
6055 {
6056 LogRel(("Console::i_configNetwork: COMGETTER(NetworkMask) failed, hrc (%Rhrc)\n", hrc));
6057 H();
6058 }
6059 }
6060 else
6061 {
6062 /* No DHCP server for this hostonly interface, let's look at extra data */
6063 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress",
6064 pszHostOnlyName).raw(),
6065 bstrLowerIP.asOutParam());
6066 if (SUCCEEDED(hrc) && !bstrLowerIP.isEmpty())
6067 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask",
6068 pszHostOnlyName).raw(),
6069 bstrNetworkMask.asOutParam());
6070
6071 }
6072 RTNETADDRIPV4 ipAddr, ipMask;
6073 rc = bstrLowerIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr);
6074 if (RT_FAILURE(rc))
6075 {
6076 /* We failed to locate any valid config of this vboxnetX interface, assume defaults. */
6077 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing lower IP '%ls', using '%ls' instead.\n",
6078 bstrLowerIP.raw(), getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw()));
6079 bstrLowerIP = getDefaultIPv4Address(Bstr(pszHostOnlyName));
6080 bstrNetworkMask.setNull();
6081 bstrUpperIP.setNull();
6082 rc = RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr);
6083 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("RTNetStrToIPv4Addr(%ls) failed, rc=%Rrc\n", bstrLowerIP.raw(), rc),
6084 VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
6085 }
6086 rc = bstrNetworkMask.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask);
6087 if (RT_FAILURE(rc))
6088 {
6089 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing network mask '%ls', using '%s' instead.\n",
6090 bstrNetworkMask.raw(), VBOXNET_IPV4MASK_DEFAULT));
6091 bstrNetworkMask = VBOXNET_IPV4MASK_DEFAULT;
6092 rc = RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask);
6093 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("RTNetStrToIPv4Addr(%ls) failed, rc=%Rrc\n", bstrNetworkMask.raw(), rc),
6094 VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
6095 }
6096 rc = bstrUpperIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrUpperIP).c_str(), &ipAddr);
6097 if (RT_FAILURE(rc))
6098 {
6099 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? */
6100 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing upper IP '%ls', using '%RTnaipv4' instead.\n",
6101 bstrUpperIP.raw(), ipAddr));
6102 bstrUpperIP = BstrFmt("%RTnaipv4", ipAddr);
6103 }
6104
6105 /* All parameters are set, create the new network. */
6106 hrc = virtualBox->CreateHostOnlyNetwork(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam());
6107 if (FAILED(hrc))
6108 {
6109 LogRel(("NetworkAttachmentType_HostOnly: failed to create host-only network, hrc (0x%x)\n", hrc));
6110 H();
6111 }
6112 hrc = hostOnlyNetwork->COMSETTER(NetworkMask)(bstrNetworkMask.raw());
6113 if (FAILED(hrc))
6114 {
6115 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(NetworkMask) failed, hrc (0x%x)\n", hrc));
6116 H();
6117 }
6118 hrc = hostOnlyNetwork->COMSETTER(LowerIP)(bstrLowerIP.raw());
6119 if (FAILED(hrc))
6120 {
6121 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(LowerIP) failed, hrc (0x%x)\n", hrc));
6122 H();
6123 }
6124 hrc = hostOnlyNetwork->COMSETTER(UpperIP)(bstrUpperIP.raw());
6125 if (FAILED(hrc))
6126 {
6127 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(UpperIP) failed, hrc (0x%x)\n", hrc));
6128 H();
6129 }
6130 LogRel(("Console: created host-only network '%ls' with mask '%ls' and range '%ls'-'%ls'\n",
6131 bstrNetworkName.raw(), bstrNetworkMask.raw(), bstrLowerIP.raw(), bstrUpperIP.raw()));
6132 }
6133 else
6134 {
6135 /* The matching host-only network already exists. Tell the user to switch to it. */
6136 hrc = hostOnlyNetwork->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam());
6137 if (FAILED(hrc))
6138 {
6139 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(NetworkMask) failed, hrc (0x%x)\n", hrc));
6140 H();
6141 }
6142 hrc = hostOnlyNetwork->COMGETTER(LowerIP)(bstrLowerIP.asOutParam());
6143 if (FAILED(hrc))
6144 {
6145 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(LowerIP) failed, hrc (0x%x)\n", hrc));
6146 H();
6147 }
6148 hrc = hostOnlyNetwork->COMGETTER(UpperIP)(bstrUpperIP.asOutParam());
6149 if (FAILED(hrc))
6150 {
6151 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(UpperIP) failed, hrc (0x%x)\n", hrc));
6152 H();
6153 }
6154 }
6155 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
6156 N_("Host-only adapters are no longer supported!\n"
6157 "For your convenience a host-only network named '%ls' has been "
6158 "created with network mask '%ls' and IP address range '%ls' - '%ls'.\n"
6159 "To fix this problem, switch to 'Host-only Network' "
6160 "attachment type in the VM settings.\n"),
6161 bstrNetworkName.raw(), bstrNetworkMask.raw(),
6162 bstrLowerIP.raw(), bstrUpperIP.raw());
6163#endif /* VBOX_WITH_VMNET */
6164 ComPtr<IHostNetworkInterface> hostInterface;
6165 rc = host->FindHostNetworkInterfaceByName(HostOnlyName.raw(),
6166 hostInterface.asOutParam());
6167 if (!SUCCEEDED(rc))
6168 {
6169 LogRel(("NetworkAttachmentType_HostOnly: FindByName failed, rc (0x%x)\n", rc));
6170 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
6171 N_("Nonexistent host networking interface, name '%ls'"), HostOnlyName.raw());
6172 }
6173
6174 char szNetwork[INTNET_MAX_NETWORK_NAME];
6175 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszHostOnlyName);
6176
6177#if defined(RT_OS_WINDOWS)
6178# ifndef VBOX_WITH_NETFLT
6179 hrc = E_NOTIMPL;
6180 LogRel(("NetworkAttachmentType_HostOnly: Not Implemented\n"));
6181 H();
6182# else /* defined VBOX_WITH_NETFLT*/
6183 /** @todo r=bird: Put this in a function. */
6184
6185 HostNetworkInterfaceType_T eIfType;
6186 hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
6187 if (FAILED(hrc))
6188 {
6189 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
6190 H();
6191 }
6192
6193 if (eIfType != HostNetworkInterfaceType_HostOnly)
6194 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
6195 N_("Interface ('%ls') is not a Host-Only Adapter interface"),
6196 HostOnlyName.raw());
6197
6198 hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
6199 if (FAILED(hrc))
6200 {
6201 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
6202 H();
6203 }
6204 Guid hostIFGuid(bstr);
6205
6206 INetCfg *pNc;
6207 ComPtr<INetCfgComponent> pAdaptorComponent;
6208 LPWSTR pszApp;
6209 hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp);
6210 Assert(hrc == S_OK);
6211 if (hrc != S_OK)
6212 {
6213 LogRel(("NetworkAttachmentType_HostOnly: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc));
6214 H();
6215 }
6216
6217 /* get the adapter's INetCfgComponent*/
6218 hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(),
6219 pAdaptorComponent.asOutParam());
6220 if (hrc != S_OK)
6221 {
6222 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
6223 LogRel(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n", hrc, hrc));
6224 H();
6225 }
6226# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
6227 char szTrunkName[INTNET_MAX_TRUNK_NAME];
6228 bool fNdis6 = false;
6229 wchar_t * pwszHelpText;
6230 hrc = pAdaptorComponent->GetHelpText(&pwszHelpText);
6231 Assert(hrc == S_OK);
6232 if (hrc == S_OK)
6233 {
6234 Log(("help-text=%ls\n", pwszHelpText));
6235 if (!wcscmp(pwszHelpText, L"VirtualBox NDIS 6.0 Miniport Driver"))
6236 fNdis6 = true;
6237 CoTaskMemFree(pwszHelpText);
6238 }
6239 if (fNdis6)
6240 {
6241 strncpy(szTrunkName, pszHostOnlyName, sizeof(szTrunkName) - 1);
6242 Log(("trunk=%s\n", szTrunkName));
6243 }
6244 else
6245 {
6246 char *pszTrunkName = szTrunkName;
6247 wchar_t * pswzBindName;
6248 hrc = pAdaptorComponent->GetBindName(&pswzBindName);
6249 Assert(hrc == S_OK);
6250 if (hrc == S_OK)
6251 {
6252 int cwBindName = (int)wcslen(pswzBindName) + 1;
6253 int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX);
6254 if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName)
6255 {
6256 strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX);
6257 pszTrunkName += cbFullBindNamePrefix-1;
6258 if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName,
6259 sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL))
6260 {
6261 DWORD err = GetLastError();
6262 hrc = HRESULT_FROM_WIN32(err);
6263 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n",
6264 hrc, hrc, err));
6265 }
6266 }
6267 else
6268 {
6269 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: insufficient szTrunkName buffer space\n"));
6270 /** @todo set appropriate error code */
6271 hrc = E_FAIL;
6272 }
6273
6274 if (hrc != S_OK)
6275 {
6276 AssertFailed();
6277 CoTaskMemFree(pswzBindName);
6278 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
6279 H();
6280 }
6281 }
6282 else
6283 {
6284 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
6285 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n",
6286 hrc, hrc));
6287 H();
6288 }
6289
6290
6291 CoTaskMemFree(pswzBindName);
6292 }
6293
6294 trunkType = TRUNKTYPE_NETADP;
6295 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
6296
6297 pAdaptorComponent.setNull();
6298 /* release the pNc finally */
6299 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
6300
6301 const char *pszTrunk = szTrunkName;
6302
6303 InsertConfigString(pCfg, "Trunk", pszTrunk);
6304 InsertConfigString(pCfg, "Network", szNetwork);
6305 InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure); /** @todo why is this
6306 windows only?? */
6307 networkName = Bstr(szNetwork);
6308 trunkName = Bstr(pszTrunk);
6309# endif /* defined VBOX_WITH_NETFLT*/
6310#elif defined(RT_OS_DARWIN)
6311 InsertConfigString(pCfg, "Trunk", pszHostOnlyName);
6312 InsertConfigString(pCfg, "Network", szNetwork);
6313 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
6314 networkName = Bstr(szNetwork);
6315 trunkName = Bstr(pszHostOnlyName);
6316 trunkType = TRUNKTYPE_NETADP;
6317#else
6318 InsertConfigString(pCfg, "Trunk", pszHostOnlyName);
6319 InsertConfigString(pCfg, "Network", szNetwork);
6320 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
6321 networkName = Bstr(szNetwork);
6322 trunkName = Bstr(pszHostOnlyName);
6323 trunkType = TRUNKTYPE_NETFLT;
6324#endif
6325 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
6326
6327#if !defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
6328
6329 Bstr tmpAddr, tmpMask;
6330
6331 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress",
6332 pszHostOnlyName).raw(),
6333 tmpAddr.asOutParam());
6334 if (SUCCEEDED(hrc) && !tmpAddr.isEmpty())
6335 {
6336 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask",
6337 pszHostOnlyName).raw(),
6338 tmpMask.asOutParam());
6339 if (SUCCEEDED(hrc) && !tmpMask.isEmpty())
6340 hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(),
6341 tmpMask.raw());
6342 else
6343 hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(),
6344 Bstr(VBOXNET_IPV4MASK_DEFAULT).raw());
6345 }
6346 else
6347 {
6348 /* Grab the IP number from the 'vboxnetX' instance number (see netif.h) */
6349 hrc = hostInterface->EnableStaticIPConfig(getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw(),
6350 Bstr(VBOXNET_IPV4MASK_DEFAULT).raw());
6351 }
6352
6353 ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */
6354
6355 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6Address",
6356 pszHostOnlyName).raw(),
6357 tmpAddr.asOutParam());
6358 if (SUCCEEDED(hrc))
6359 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6NetMask", pszHostOnlyName).raw(),
6360 tmpMask.asOutParam());
6361 if (SUCCEEDED(hrc) && !tmpAddr.isEmpty() && !tmpMask.isEmpty())
6362 {
6363 hrc = hostInterface->EnableStaticIPConfigV6(tmpAddr.raw(),
6364 Utf8Str(tmpMask).toUInt32());
6365 ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */
6366 }
6367#endif
6368 break;
6369 }
6370
6371 case NetworkAttachmentType_Generic:
6372 {
6373 hrc = aNetworkAdapter->COMGETTER(GenericDriver)(bstr.asOutParam()); H();
6374 SafeArray<BSTR> names;
6375 SafeArray<BSTR> values;
6376 hrc = aNetworkAdapter->GetProperties(Bstr().raw(),
6377 ComSafeArrayAsOutParam(names),
6378 ComSafeArrayAsOutParam(values)); H();
6379
6380 InsertConfigString(pLunL0, "Driver", bstr);
6381 InsertConfigNode(pLunL0, "Config", &pCfg);
6382 for (size_t ii = 0; ii < names.size(); ++ii)
6383 {
6384 if (values[ii] && *values[ii])
6385 {
6386 Utf8Str name = names[ii];
6387 Utf8Str value = values[ii];
6388 InsertConfigString(pCfg, name.c_str(), value);
6389 }
6390 }
6391 break;
6392 }
6393
6394 case NetworkAttachmentType_NATNetwork:
6395 {
6396 hrc = aNetworkAdapter->COMGETTER(NATNetwork)(bstr.asOutParam()); H();
6397 if (!bstr.isEmpty())
6398 {
6399 /** @todo add intnet prefix to separate namespaces, and add trunk if dealing with vboxnatX */
6400 InsertConfigString(pLunL0, "Driver", "IntNet");
6401 InsertConfigNode(pLunL0, "Config", &pCfg);
6402 InsertConfigString(pCfg, "Network", bstr);
6403 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
6404 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
6405 networkName = bstr;
6406 trunkType = Bstr(TRUNKTYPE_WHATEVER);
6407 }
6408 break;
6409 }
6410
6411#ifdef VBOX_WITH_CLOUD_NET
6412 case NetworkAttachmentType_Cloud:
6413 {
6414 static const char *s_pszCloudExtPackName = "Oracle VM VirtualBox Extension Pack";
6415 /*
6416 * Cloud network attachments do not work wihout installed extpack.
6417 * Without extpack support they won't work either.
6418 */
6419# ifdef VBOX_WITH_EXTPACK
6420 if (!mptrExtPackManager->i_isExtPackUsable(s_pszCloudExtPackName))
6421# endif
6422 {
6423 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
6424 N_("Implementation of the cloud network attachment not found!\n"
6425 "To fix this problem, either install the '%s' or switch to "
6426 "another network attachment type in the VM settings."),
6427 s_pszCloudExtPackName);
6428 }
6429
6430 ComPtr<ICloudNetwork> network;
6431 hrc = aNetworkAdapter->COMGETTER(CloudNetwork)(bstr.asOutParam()); H();
6432 hrc = pMachine->COMGETTER(Name)(mGateway.mTargetVM.asOutParam()); H();
6433 hrc = virtualBox->FindCloudNetworkByName(bstr.raw(), network.asOutParam()); H();
6434 hrc = generateKeys(mGateway);
6435 if (FAILED(hrc))
6436 {
6437 if (hrc == E_NOTIMPL)
6438 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
6439 N_("Failed to generate a key pair due to missing libssh\n"
6440 "To fix this problem, either build VirtualBox with libssh "
6441 "support or switch to another network attachment type in "
6442 "the VM settings."));
6443 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
6444 N_("Failed to generate a key pair due to libssh error!"));
6445 }
6446 hrc = startCloudGateway(virtualBox, network, mGateway);
6447 if (FAILED(hrc))
6448 {
6449 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
6450 N_("Failed to start cloud gateway instance.\nMake sure you set up "
6451 "cloud networking properly with 'VBoxManage network setup'. "
6452 "Check VBoxSVC.log for details."));
6453 }
6454 InsertConfigBytes(pDevCfg, "MAC", &mGateway.mCloudMacAddress, sizeof(mGateway.mCloudMacAddress));
6455 if (!bstr.isEmpty())
6456 {
6457 InsertConfigString(pLunL0, "Driver", "CloudTunnel");
6458 InsertConfigNode(pLunL0, "Config", &pCfg);
6459 InsertConfigPassword(pCfg, "SshKey", mGateway.mPrivateSshKey);
6460 InsertConfigString(pCfg, "PrimaryIP", mGateway.mCloudPublicIp);
6461 InsertConfigString(pCfg, "SecondaryIP", mGateway.mCloudSecondaryPublicIp);
6462 InsertConfigBytes(pCfg, "TargetMAC", &mGateway.mLocalMacAddress, sizeof(mGateway.mLocalMacAddress));
6463 networkName = bstr;
6464 trunkType = Bstr(TRUNKTYPE_WHATEVER);
6465 }
6466 break;
6467 }
6468#endif /* VBOX_WITH_CLOUD_NET */
6469
6470#ifdef VBOX_WITH_VMNET
6471 case NetworkAttachmentType_HostOnlyNetwork:
6472 {
6473 Bstr bstrId, bstrNetMask, bstrLowerIP, bstrUpperIP;
6474 ComPtr<IHostOnlyNetwork> network;
6475 hrc = aNetworkAdapter->COMGETTER(HostOnlyNetwork)(bstr.asOutParam()); H();
6476 hrc = virtualBox->FindHostOnlyNetworkByName(bstr.raw(), network.asOutParam());
6477 if (FAILED(hrc))
6478 {
6479 LogRel(("NetworkAttachmentType_HostOnlyNetwork: FindByName failed, hrc (0x%x)\n", hrc));
6480 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
6481 N_("Nonexistent host-only network '%ls'"), bstr.raw());
6482 }
6483 hrc = network->COMGETTER(Id)(bstrId.asOutParam()); H();
6484 hrc = network->COMGETTER(NetworkMask)(bstrNetMask.asOutParam()); H();
6485 hrc = network->COMGETTER(LowerIP)(bstrLowerIP.asOutParam()); H();
6486 hrc = network->COMGETTER(UpperIP)(bstrUpperIP.asOutParam()); H();
6487 if (!bstr.isEmpty())
6488 {
6489 InsertConfigString(pLunL0, "Driver", "VMNet");
6490 InsertConfigNode(pLunL0, "Config", &pCfg);
6491 // InsertConfigString(pCfg, "Trunk", Utf8Str(bstr).c_str());
6492 // InsertConfigString(pCfg, "Network", BstrFmt("HostOnlyNetworking-%ls", bstr.raw()));
6493 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
6494 InsertConfigString(pCfg, "Id", Utf8Str(bstrId).c_str());
6495 InsertConfigString(pCfg, "NetworkMask", Utf8Str(bstrNetMask).c_str());
6496 InsertConfigString(pCfg, "LowerIP", Utf8Str(bstrLowerIP).c_str());
6497 InsertConfigString(pCfg, "UpperIP", Utf8Str(bstrUpperIP).c_str());
6498 // InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
6499 networkName.setNull(); // We do not want DHCP server on our network!
6500 // trunkType = Bstr(TRUNKTYPE_WHATEVER);
6501 }
6502 break;
6503 }
6504#endif /* VBOX_WITH_VMNET */
6505
6506 default:
6507 AssertMsgFailed(("should not get here!\n"));
6508 break;
6509 }
6510
6511 /*
6512 * Attempt to attach the driver.
6513 */
6514 switch (eAttachmentType)
6515 {
6516 case NetworkAttachmentType_Null:
6517 break;
6518
6519 case NetworkAttachmentType_Bridged:
6520 case NetworkAttachmentType_Internal:
6521 case NetworkAttachmentType_HostOnly:
6522#ifdef VBOX_WITH_VMNET
6523 case NetworkAttachmentType_HostOnlyNetwork:
6524#endif /* VBOX_WITH_VMNET */
6525 case NetworkAttachmentType_NAT:
6526 case NetworkAttachmentType_Generic:
6527 case NetworkAttachmentType_NATNetwork:
6528#ifdef VBOX_WITH_CLOUD_NET
6529 case NetworkAttachmentType_Cloud:
6530#endif /* VBOX_WITH_CLOUD_NET */
6531 {
6532 if (SUCCEEDED(hrc) && RT_SUCCESS(rc))
6533 {
6534 if (fAttachDetach)
6535 {
6536 rc = pVMM->pfnPDMR3DriverAttach(mpUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/, NULL /* ppBase */);
6537 //AssertRC(rc);
6538 }
6539
6540 {
6541 /** @todo pritesh: get the dhcp server name from the
6542 * previous network configuration and then stop the server
6543 * else it may conflict with the dhcp server running with
6544 * the current attachment type
6545 */
6546 /* Stop the hostonly DHCP Server */
6547 }
6548
6549 /*
6550 * NAT networks start their DHCP server theirself, see NATNetwork::Start()
6551 */
6552 if ( !networkName.isEmpty()
6553 && eAttachmentType != NetworkAttachmentType_NATNetwork)
6554 {
6555 /*
6556 * Until we implement service reference counters DHCP Server will be stopped
6557 * by DHCPServerRunner destructor.
6558 */
6559 ComPtr<IDHCPServer> dhcpServer;
6560 hrc = virtualBox->FindDHCPServerByNetworkName(networkName.raw(), dhcpServer.asOutParam());
6561 if (SUCCEEDED(hrc))
6562 {
6563 /* there is a DHCP server available for this network */
6564 BOOL fEnabledDhcp;
6565 hrc = dhcpServer->COMGETTER(Enabled)(&fEnabledDhcp);
6566 if (FAILED(hrc))
6567 {
6568 LogRel(("DHCP svr: COMGETTER(Enabled) failed, hrc (%Rhrc)\n", hrc));
6569 H();
6570 }
6571
6572 if (fEnabledDhcp)
6573 hrc = dhcpServer->Start(trunkName.raw(), trunkType.raw());
6574 }
6575 else
6576 hrc = S_OK;
6577 }
6578 }
6579
6580 break;
6581 }
6582
6583 default:
6584 AssertMsgFailed(("should not get here!\n"));
6585 break;
6586 }
6587
6588 meAttachmentType[uInstance] = eAttachmentType;
6589 }
6590 catch (ConfigError &x)
6591 {
6592 // InsertConfig threw something:
6593 return x.m_vrc;
6594 }
6595
6596#undef H
6597
6598 return VINF_SUCCESS;
6599}
6600
6601
6602/**
6603 * Configures the serial port at the given CFGM node with the supplied parameters.
6604 *
6605 * @returns VBox status code.
6606 * @param pInst The instance CFGM node.
6607 * @param ePortMode The port mode to sue.
6608 * @param pszPath The serial port path.
6609 * @param fServer Flag whether the port should act as a server
6610 * for the pipe and TCP mode or connect as a client.
6611 */
6612int Console::i_configSerialPort(PCFGMNODE pInst, PortMode_T ePortMode, const char *pszPath, bool fServer)
6613{
6614 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
6615 PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */
6616 PCFGMNODE pLunL1Cfg = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/Config */
6617
6618 try
6619 {
6620 InsertConfigNode(pInst, "LUN#0", &pLunL0);
6621 if (ePortMode == PortMode_HostPipe)
6622 {
6623 InsertConfigString(pLunL0, "Driver", "Char");
6624 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
6625 InsertConfigString(pLunL1, "Driver", "NamedPipe");
6626 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
6627 InsertConfigString(pLunL1Cfg, "Location", pszPath);
6628 InsertConfigInteger(pLunL1Cfg, "IsServer", fServer);
6629 }
6630 else if (ePortMode == PortMode_HostDevice)
6631 {
6632 InsertConfigString(pLunL0, "Driver", "Host Serial");
6633 InsertConfigNode(pLunL0, "Config", &pLunL1);
6634 InsertConfigString(pLunL1, "DevicePath", pszPath);
6635 }
6636 else if (ePortMode == PortMode_TCP)
6637 {
6638 InsertConfigString(pLunL0, "Driver", "Char");
6639 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
6640 InsertConfigString(pLunL1, "Driver", "TCP");
6641 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
6642 InsertConfigString(pLunL1Cfg, "Location", pszPath);
6643 InsertConfigInteger(pLunL1Cfg, "IsServer", fServer);
6644 }
6645 else if (ePortMode == PortMode_RawFile)
6646 {
6647 InsertConfigString(pLunL0, "Driver", "Char");
6648 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
6649 InsertConfigString(pLunL1, "Driver", "RawFile");
6650 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
6651 InsertConfigString(pLunL1Cfg, "Location", pszPath);
6652 }
6653 }
6654 catch (ConfigError &x)
6655 {
6656 /* InsertConfig threw something */
6657 return x.m_vrc;
6658 }
6659
6660 return VINF_SUCCESS;
6661}
6662
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use