VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImplConfigCommon.cpp@ 103415

Last change on this file since 103415 was 101515, checked in by vboxsync, 14 months ago

Main: Don't apply the PCI slot swap hack for the ARMv8 virtual platform, bugref:10528

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 219.9 KB
Line 
1/* $Id: ConsoleImplConfigCommon.cpp 101515 2023-10-20 10:47:48Z 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-2023 Oracle and/or its affiliates.
13 *
14 * This file is part of VirtualBox base platform packages, as
15 * available from https://www.virtualbox.org.
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation, in version 3 of the
20 * License.
21 *
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, see <https://www.gnu.org/licenses>.
29 *
30 * SPDX-License-Identifier: GPL-3.0-only
31 */
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
38#include "LoggingNew.h"
39
40// VBoxNetCfg-win.h needs winsock2.h and thus MUST be included before any other
41// header file includes Windows.h.
42#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
43# include <VBox/VBoxNetCfg-win.h>
44#endif
45
46#include "ConsoleImpl.h"
47#include "DisplayImpl.h"
48#include "NvramStoreImpl.h"
49#include "BusAssignmentManager.h"
50#ifdef VBOX_WITH_SHARED_CLIPBOARD
51# include "GuestShClPrivate.h"
52#endif
53#ifdef VBOX_WITH_DRAG_AND_DROP
54# include "GuestImpl.h"
55# include "GuestDnDPrivate.h"
56#endif
57#include "VMMDev.h"
58#include "Global.h"
59#ifdef VBOX_WITH_PCI_PASSTHROUGH
60# include "PCIRawDevImpl.h"
61#endif
62
63// generated header
64#include "SchemaDefs.h"
65
66#include "AutoCaller.h"
67
68#include <iprt/base64.h>
69#include <iprt/buildconfig.h>
70#include <iprt/ctype.h>
71#include <iprt/dir.h>
72#include <iprt/file.h>
73#include <iprt/param.h>
74#include <iprt/path.h>
75#include <iprt/string.h>
76#include <iprt/system.h>
77#if 0 /* enable to play with lots of memory. */
78# include <iprt/env.h>
79#endif
80#include <iprt/stream.h>
81
82#include <iprt/http.h>
83#include <iprt/socket.h>
84#include <iprt/uri.h>
85
86#include <VBox/vmm/vmmr3vtable.h>
87#include <VBox/vmm/vmapi.h>
88#include <VBox/err.h>
89#include <VBox/param.h>
90#include <VBox/settings.h> /* For MachineConfigFile::getHostDefaultAudioDriver(). */
91#include <VBox/vmm/pdmapi.h> /* For PDMR3DriverAttach/PDMR3DriverDetach. */
92#include <VBox/vmm/pdmusb.h> /* For PDMR3UsbCreateEmulatedDevice. */
93#include <VBox/vmm/pdmdev.h> /* For PDMAPICMODE enum. */
94#include <VBox/vmm/pdmstorageifs.h>
95#include <VBox/version.h>
96#ifdef VBOX_WITH_SHARED_CLIPBOARD
97# include <VBox/HostServices/VBoxClipboardSvc.h>
98#endif
99#ifdef VBOX_WITH_GUEST_PROPS
100# include <VBox/HostServices/GuestPropertySvc.h>
101# include <VBox/com/defs.h>
102# include <VBox/com/array.h>
103# include <vector>
104#endif /* VBOX_WITH_GUEST_PROPS */
105#include <VBox/intnet.h>
106
107#include <VBox/com/com.h>
108#include <VBox/com/string.h>
109#include <VBox/com/array.h>
110
111#ifdef VBOX_WITH_NETFLT
112# if defined(RT_OS_SOLARIS)
113# include <zone.h>
114# elif defined(RT_OS_LINUX)
115# include <unistd.h>
116# include <sys/ioctl.h>
117# include <sys/socket.h>
118# include <linux/types.h>
119# include <linux/if.h>
120# elif defined(RT_OS_FREEBSD)
121# include <unistd.h>
122# include <sys/types.h>
123# include <sys/ioctl.h>
124# include <sys/socket.h>
125# include <net/if.h>
126# include <net80211/ieee80211_ioctl.h>
127# endif
128# if defined(RT_OS_WINDOWS)
129# include <iprt/win/ntddndis.h>
130# include <devguid.h>
131# else
132# include <HostNetworkInterfaceImpl.h>
133# include <netif.h>
134# include <stdlib.h>
135# endif
136#endif /* VBOX_WITH_NETFLT */
137
138#ifdef VBOX_WITH_AUDIO_VRDE
139# include "DrvAudioVRDE.h"
140#endif
141#ifdef VBOX_WITH_AUDIO_RECORDING
142# include "DrvAudioRec.h"
143#endif
144#include "NetworkServiceRunner.h"
145#ifdef VBOX_WITH_EXTPACK
146# include "ExtPackManagerImpl.h"
147#endif
148
149
150/*********************************************************************************************************************************
151* Internal Functions *
152*********************************************************************************************************************************/
153
154/* Darwin compile kludge */
155#undef PVM
156
157/**
158 * Helper that calls CFGMR3InsertString and throws an RTCError if that
159 * fails (C-string variant).
160 * @param pNode See CFGMR3InsertString.
161 * @param pcszName See CFGMR3InsertString.
162 * @param pcszValue The string value.
163 */
164void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const char *pcszValue)
165{
166 int vrc = mpVMM->pfnCFGMR3InsertString(pNode, pcszName, pcszValue);
167 if (RT_FAILURE(vrc))
168 throw ConfigError("CFGMR3InsertString", vrc, pcszName);
169}
170
171/**
172 * Helper that calls CFGMR3InsertString and throws an RTCError if that
173 * fails (Utf8Str variant).
174 * @param pNode See CFGMR3InsertStringN.
175 * @param pcszName See CFGMR3InsertStringN.
176 * @param rStrValue The string value.
177 */
178void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue)
179{
180 int vrc = mpVMM->pfnCFGMR3InsertStringN(pNode, pcszName, rStrValue.c_str(), rStrValue.length());
181 if (RT_FAILURE(vrc))
182 throw ConfigError("CFGMR3InsertStringLengthKnown", vrc, pcszName);
183}
184
185/**
186 * Helper that calls CFGMR3InsertString and throws an RTCError if that
187 * fails (Bstr variant).
188 *
189 * @param pNode See CFGMR3InsertStringN.
190 * @param pcszName See CFGMR3InsertStringN.
191 * @param rBstrValue The string value.
192 */
193void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Bstr &rBstrValue)
194{
195 InsertConfigString(pNode, pcszName, Utf8Str(rBstrValue));
196}
197
198/**
199 * Helper that calls CFGMR3InsertStringFV and throws an RTCError if that fails.
200 * @param pNode See CFGMR3InsertStringF.
201 * @param pcszName See CFGMR3InsertStringF.
202 * @param pszFormat See CFGMR3InsertStringF.
203 * @param ... See CFGMR3InsertStringF.
204 */
205void Console::InsertConfigStringF(PCFGMNODE pNode, const char *pcszName, const char *pszFormat, ...)
206{
207 va_list va;
208 va_start(va, pszFormat);
209 int vrc = mpVMM->pfnCFGMR3InsertStringFV(pNode, pcszName, pszFormat, va);
210 va_end(va);
211 if (RT_FAILURE(vrc))
212 throw ConfigError("CFGMR3InsertStringFV", vrc, pcszName);
213}
214
215
216/**
217 * Helper that calls CFGMR3InsertPassword and throws an RTCError if that
218 * fails (Utf8Str variant).
219 * @param pNode See CFGMR3InsertPasswordN.
220 * @param pcszName See CFGMR3InsertPasswordN.
221 * @param rStrValue The string value.
222 */
223void Console::InsertConfigPassword(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue)
224{
225 int vrc = mpVMM->pfnCFGMR3InsertPasswordN(pNode, pcszName, rStrValue.c_str(), rStrValue.length());
226 if (RT_FAILURE(vrc))
227 throw ConfigError("CFGMR3InsertPasswordLengthKnown", vrc, pcszName);
228}
229
230/**
231 * Helper that calls CFGMR3InsertBytes and throws an RTCError if that fails.
232 *
233 * @param pNode See CFGMR3InsertBytes.
234 * @param pcszName See CFGMR3InsertBytes.
235 * @param pvBytes See CFGMR3InsertBytes.
236 * @param cbBytes See CFGMR3InsertBytes.
237 */
238void Console::InsertConfigBytes(PCFGMNODE pNode, const char *pcszName, const void *pvBytes, size_t cbBytes)
239{
240 int vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pcszName, pvBytes, cbBytes);
241 if (RT_FAILURE(vrc))
242 throw ConfigError("CFGMR3InsertBytes", vrc, pcszName);
243}
244
245/**
246 * Helper that calls CFGMR3InsertInteger and throws an RTCError if that
247 * fails.
248 *
249 * @param pNode See CFGMR3InsertInteger.
250 * @param pcszName See CFGMR3InsertInteger.
251 * @param u64Integer See CFGMR3InsertInteger.
252 */
253void Console::InsertConfigInteger(PCFGMNODE pNode, const char *pcszName, uint64_t u64Integer)
254{
255 int vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pcszName, u64Integer);
256 if (RT_FAILURE(vrc))
257 throw ConfigError("CFGMR3InsertInteger", vrc, pcszName);
258}
259
260/**
261 * Helper that calls CFGMR3InsertNode and throws an RTCError if that fails.
262 *
263 * @param pNode See CFGMR3InsertNode.
264 * @param pcszName See CFGMR3InsertNode.
265 * @param ppChild See CFGMR3InsertNode.
266 */
267void Console::InsertConfigNode(PCFGMNODE pNode, const char *pcszName, PCFGMNODE *ppChild)
268{
269 int vrc = mpVMM->pfnCFGMR3InsertNode(pNode, pcszName, ppChild);
270 if (RT_FAILURE(vrc))
271 throw ConfigError("CFGMR3InsertNode", vrc, pcszName);
272}
273
274/**
275 * Helper that calls CFGMR3InsertNodeF and throws an RTCError if that fails.
276 *
277 * @param pNode See CFGMR3InsertNodeF.
278 * @param ppChild See CFGMR3InsertNodeF.
279 * @param pszNameFormat Name format string, see CFGMR3InsertNodeF.
280 * @param ... Format arguments.
281 */
282void Console::InsertConfigNodeF(PCFGMNODE pNode, PCFGMNODE *ppChild, const char *pszNameFormat, ...)
283{
284 va_list va;
285 va_start(va, pszNameFormat);
286 int vrc = mpVMM->pfnCFGMR3InsertNodeF(pNode, ppChild, "%N", pszNameFormat, &va);
287 va_end(va);
288 if (RT_FAILURE(vrc))
289 throw ConfigError("CFGMR3InsertNodeF", vrc, pszNameFormat);
290}
291
292/**
293 * Helper that calls CFGMR3RemoveValue and throws an RTCError if that fails.
294 *
295 * @param pNode See CFGMR3RemoveValue.
296 * @param pcszName See CFGMR3RemoveValue.
297 */
298void Console::RemoveConfigValue(PCFGMNODE pNode, const char *pcszName)
299{
300 int vrc = mpVMM->pfnCFGMR3RemoveValue(pNode, pcszName);
301 if (RT_FAILURE(vrc))
302 throw ConfigError("CFGMR3RemoveValue", vrc, pcszName);
303}
304
305/**
306 * Gets an extra data value, consulting both machine and global extra data.
307 *
308 * @throws HRESULT on failure
309 * @returns pStrValue for the callers convenience.
310 * @param pVirtualBox Pointer to the IVirtualBox interface.
311 * @param pMachine Pointer to the IMachine interface.
312 * @param pszName The value to get.
313 * @param pStrValue Where to return it's value (empty string if not
314 * found).
315 */
316DECL_HIDDEN_THROW(Utf8Str *) GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue)
317{
318 pStrValue->setNull();
319
320 Bstr bstrName(pszName);
321 Bstr bstrValue;
322 HRESULT hrc = pMachine->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
323 if (FAILED(hrc))
324 throw hrc;
325 if (bstrValue.isEmpty())
326 {
327 hrc = pVirtualBox->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
328 if (FAILED(hrc))
329 throw hrc;
330 }
331
332 if (bstrValue.isNotEmpty())
333 *pStrValue = bstrValue;
334 return pStrValue;
335}
336
337
338/**
339 * Updates the device type for a LED.
340 *
341 * @param penmSubTypeEntry The sub-type entry to update.
342 * @param enmNewType The new type.
343 */
344void Console::i_setLedType(DeviceType_T *penmSubTypeEntry, DeviceType_T enmNewType)
345{
346 /*
347 * ASSUMES no race conditions here wrt concurrent type updating.
348 */
349 if (*penmSubTypeEntry != enmNewType)
350 {
351 *penmSubTypeEntry = enmNewType;
352 ASMAtomicIncU32(&muLedGen);
353 }
354}
355
356
357/**
358 * Allocate a set of LEDs.
359 *
360 * This grabs a maLedSets entry and populates it with @a cLeds.
361 *
362 * @returns Index into maLedSets.
363 * @param cLeds The number of LEDs in the set.
364 * @param fTypes Bitmask of DeviceType_T values, e.g.
365 * RT_BIT_32(DeviceType_Network).
366 * @param ppaSubTypes When not NULL, subtypes for each LED and return the
367 * array pointer here.
368 */
369uint32_t Console::i_allocateDriverLeds(uint32_t cLeds, uint32_t fTypes, DeviceType_T **ppaSubTypes)
370{
371 Assert(cLeds > 0);
372 Assert(cLeds < 1024); /* Adjust if any driver supports >=1024 units! */
373 Assert(!(fTypes & (RT_BIT_32(DeviceType_Null) | ~(RT_BIT_32(DeviceType_End) - 1))));
374
375 /* Preallocate the arrays we need, bunching them together. */
376 AssertCompile((unsigned)DeviceType_Null == 0);
377 PPDMLED volatile *papLeds = (PPDMLED volatile *)RTMemAllocZ( (sizeof(PPDMLED) + (ppaSubTypes ? sizeof(**ppaSubTypes) : 0))
378 * cLeds);
379 AssertStmt(papLeds, throw E_OUTOFMEMORY);
380
381 /* Take the LED lock in allocation mode and see if there are more LED set entries availalbe. */
382 {
383 AutoWriteLock alock(mLedLock COMMA_LOCKVAL_SRC_POS);
384 uint32_t const idxLedSet = mcLedSets;
385 if (idxLedSet < RT_ELEMENTS(maLedSets))
386 {
387 /* Initialize the set and return the index. */
388 PLEDSET pLS = &maLedSets[idxLedSet];
389 pLS->papLeds = papLeds;
390 pLS->cLeds = cLeds;
391 pLS->fTypes = fTypes;
392 if (ppaSubTypes)
393 *ppaSubTypes = pLS->paSubTypes = (DeviceType_T *)&papLeds[cLeds];
394 else
395 pLS->paSubTypes = NULL;
396
397 mcLedSets = idxLedSet + 1;
398 LogRel2(("return idxLedSet=%d (mcLedSets=%u out of max %zu)\n", idxLedSet, mcLedSets, RT_ELEMENTS(maLedSets)));
399 return idxLedSet;
400 }
401 }
402
403 RTMemFree((void *)papLeds);
404 AssertFailed();
405 throw ConfigError("AllocateDriverPapLeds", VERR_OUT_OF_RANGE, "Too many LED sets");
406}
407
408
409/**
410 * @throws ConfigError and std::bad_alloc.
411 */
412void Console::i_attachStatusDriver(PCFGMNODE pCtlInst, uint32_t fTypes, uint32_t cLeds, DeviceType_T **ppaSubTypes,
413 Console::MediumAttachmentMap *pmapMediumAttachments,
414 const char *pcszDevice, unsigned uInstance)
415{
416 PCFGMNODE pLunL0;
417 InsertConfigNode(pCtlInst, "LUN#999", &pLunL0);
418 InsertConfigString(pLunL0, "Driver", "MainStatus");
419 PCFGMNODE pCfg;
420 InsertConfigNode(pLunL0, "Config", &pCfg);
421 uint32_t const iLedSet = i_allocateDriverLeds(cLeds, fTypes, ppaSubTypes);
422 InsertConfigInteger(pCfg, "iLedSet", iLedSet);
423
424 InsertConfigInteger(pCfg, "HasMediumAttachments", pmapMediumAttachments != NULL);
425 if (pmapMediumAttachments)
426 {
427 AssertPtr(pcszDevice);
428 InsertConfigStringF(pCfg, "DeviceInstance", "%s/%u", pcszDevice, uInstance);
429 }
430 InsertConfigInteger(pCfg, "First", 0);
431 InsertConfigInteger(pCfg, "Last", cLeds - 1);
432}
433
434
435/**
436 * @throws ConfigError and std::bad_alloc.
437 */
438void Console::i_attachStatusDriver(PCFGMNODE pCtlInst, DeviceType_T enmType, uint32_t cLeds /*= 1*/)
439{
440 Assert(enmType > DeviceType_Null && enmType < DeviceType_End);
441 i_attachStatusDriver(pCtlInst, RT_BIT_32(enmType), cLeds, NULL, NULL, NULL, 0);
442}
443
444
445/**
446 * Construct the VM configuration tree (CFGM).
447 *
448 * This is a callback for VMR3Create() call. It is called from CFGMR3Init() in
449 * the emulation thread (EMT). Any per thread COM/XPCOM initialization is done
450 * here.
451 *
452 * @returns VBox status code.
453 * @param pUVM The user mode VM handle.
454 * @param pVM The cross context VM handle.
455 * @param pVMM The VMM ring-3 vtable.
456 * @param pvConsole Pointer to the VMPowerUpTask object.
457 *
458 * @note Locks the Console object for writing.
459 */
460/*static*/ DECLCALLBACK(int)
461Console::i_configConstructor(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, void *pvConsole)
462{
463 LogFlowFuncEnter();
464
465 AssertReturn(pvConsole, VERR_INVALID_POINTER);
466 ComObjPtr<Console> pConsole = static_cast<Console *>(pvConsole);
467
468 AutoCaller autoCaller(pConsole);
469 AssertComRCReturn(autoCaller.hrc(), VERR_ACCESS_DENIED);
470
471 /* lock the console because we widely use internal fields and methods */
472 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
473
474 /*
475 * Set the VM handle and do the rest of the job in an worker method so we
476 * can easily reset the VM handle on failure.
477 */
478 pConsole->mpUVM = pUVM;
479 pVMM->pfnVMR3RetainUVM(pUVM);
480 int vrc;
481 try
482 {
483 vrc = pConsole->i_configConstructorInner(pUVM, pVM, pVMM, &alock);
484 }
485 catch (...)
486 {
487 vrc = VERR_UNEXPECTED_EXCEPTION;
488 }
489 if (RT_FAILURE(vrc))
490 {
491 pConsole->mpUVM = NULL;
492 pVMM->pfnVMR3ReleaseUVM(pUVM);
493 }
494
495 return vrc;
496}
497
498
499/**
500 * Worker for configConstructor.
501 *
502 * @return VBox status code.
503 * @return VERR_PLATFORM_ARCH_NOT_SUPPORTED if the machine's platform architecture is not supported.
504 * @param pUVM The user mode VM handle.
505 * @param pVM The cross context VM handle.
506 * @param pVMM The VMM vtable.
507 * @param pAlock The automatic lock instance. This is for when we have
508 * to leave it in order to avoid deadlocks (ext packs and
509 * more).
510 */
511int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, AutoWriteLock *pAlock)
512{
513 ComPtr<IMachine> const pMachine = i_machine();
514
515 ComPtr<IPlatform> pPlatform;
516 HRESULT hrc = pMachine->COMGETTER(Platform)(pPlatform.asOutParam());
517 AssertComRCReturn(hrc, VERR_COM_VM_ERROR);
518
519 PlatformArchitecture_T platformArch;
520 hrc = pPlatform->COMGETTER(Architecture)(&platformArch);
521 AssertComRCReturn(hrc, VERR_COM_VM_ERROR);
522
523 switch (platformArch)
524 {
525 case PlatformArchitecture_x86:
526 return i_configConstructorX86(pUVM, pVM, pVMM, pAlock);
527
528#ifdef VBOX_WITH_VIRT_ARMV8
529 case PlatformArchitecture_ARM:
530 return i_configConstructorArmV8(pUVM, pVM, pVMM, pAlock);
531#endif
532 default:
533 break;
534 }
535
536 return VERR_PLATFORM_ARCH_NOT_SUPPORTED;
537}
538
539
540/**
541 * Configures an audio driver via CFGM by getting (optional) values from extra data.
542 *
543 * @param pVirtualBox Pointer to IVirtualBox instance.
544 * @param pMachine Pointer to IMachine instance.
545 * @param pLUN Pointer to CFGM node of LUN (the driver) to configure.
546 * @param pszDrvName Name of the driver to configure.
547 * @param fAudioEnabledIn IAudioAdapter::enabledIn value.
548 * @param fAudioEnabledOut IAudioAdapter::enabledOut value.
549 *
550 * @throws ConfigError or HRESULT on if there is trouble.
551 */
552void Console::i_configAudioDriver(IVirtualBox *pVirtualBox, IMachine *pMachine, PCFGMNODE pLUN, const char *pszDrvName,
553 bool fAudioEnabledIn, bool fAudioEnabledOut)
554{
555#define H() AssertLogRelMsgStmt(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), \
556 throw ConfigError(__FUNCTION__, VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR, "line: " RT_XSTR(__LINE__)))
557
558 InsertConfigString(pLUN, "Driver", "AUDIO");
559
560 PCFGMNODE pCfg;
561 InsertConfigNode(pLUN, "Config", &pCfg);
562 InsertConfigString(pCfg, "DriverName", pszDrvName);
563 InsertConfigInteger(pCfg, "InputEnabled", fAudioEnabledIn);
564 InsertConfigInteger(pCfg, "OutputEnabled", fAudioEnabledOut);
565
566 Utf8Str strTmp;
567 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp);
568 const uint64_t fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
569 if (fDebugEnabled)
570 {
571 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
572
573 Utf8Str strDebugPathOut;
574 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut);
575 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut.c_str());
576 }
577
578 /*
579 * PCM input parameters (playback + recording).
580 * We have host driver specific ones as: VBoxInternal2/Audio/<DrvName>/<Value>
581 * And global ones for all host drivers: VBoxInternal2/Audio/<Value>
582 */
583 for (unsigned iDir = 0; iDir < 2; iDir++)
584 {
585 static const struct
586 {
587 const char *pszExtraName;
588 const char *pszCfgmName;
589 } s_aToCopy[] =
590 { /* PCM parameters: */
591 { "PCMSampleBit", "PCMSampleBit" },
592 { "PCMSampleHz", "PCMSampleHz" },
593 { "PCMSampleSigned", "PCMSampleSigned" },
594 { "PCMSampleSwapEndian", "PCMSampleSwapEndian" },
595 { "PCMSampleChannels", "PCMSampleChannels" },
596 /* Buffering stuff: */
597 { "PeriodSizeMs", "PeriodSizeMs" },
598 { "BufferSizeMs", "BufferSizeMs" },
599 { "PreBufferSizeMs", "PreBufferSizeMs" },
600 };
601
602 PCFGMNODE pDirNode = NULL;
603 const char *pszDir = iDir == 0 ? "In" : "Out";
604 for (size_t i = 0; i < RT_ELEMENTS(s_aToCopy); i++)
605 {
606 char szExtra[128];
607 RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s/%s%s", pszDrvName, s_aToCopy[i].pszExtraName, pszDir);
608 GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp); /* throws hrc */
609 if (strTmp.isEmpty())
610 {
611 RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s%s", s_aToCopy[i].pszExtraName, pszDir);
612 GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp);
613 if (strTmp.isEmpty())
614 continue;
615 }
616
617 uint32_t uValue;
618 int vrc = RTStrToUInt32Full(strTmp.c_str(), 0, &uValue);
619 if (RT_SUCCESS(vrc))
620 {
621 if (!pDirNode)
622 InsertConfigNode(pCfg, pszDir, &pDirNode);
623 InsertConfigInteger(pDirNode, s_aToCopy[i].pszCfgmName, uValue);
624 }
625 else
626 LogRel(("Ignoring malformed 32-bit unsigned integer config value '%s' = '%s': %Rrc\n", szExtra, strTmp.c_str(), vrc));
627 }
628 }
629
630 PCFGMNODE pLunL1;
631 InsertConfigNode(pLUN, "AttachedDriver", &pLunL1);
632 InsertConfigString(pLunL1, "Driver", pszDrvName);
633 InsertConfigNode(pLunL1, "Config", &pCfg);
634
635#ifdef RT_OS_WINDOWS
636 if (strcmp(pszDrvName, "HostAudioWas") == 0)
637 {
638 Bstr bstrTmp;
639 HRESULT hrc = pMachine->COMGETTER(Id)(bstrTmp.asOutParam()); H();
640 InsertConfigString(pCfg, "VmUuid", bstrTmp);
641 }
642#endif
643
644#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX)
645 if ( strcmp(pszDrvName, "HostAudioWas") == 0
646 || strcmp(pszDrvName, "PulseAudio") == 0)
647 {
648 Bstr bstrTmp;
649 HRESULT hrc = pMachine->COMGETTER(Name)(bstrTmp.asOutParam()); H();
650 InsertConfigString(pCfg, "VmName", bstrTmp);
651 }
652#endif
653
654 LogFlowFunc(("szDrivName=%s\n", pszDrvName));
655
656#undef H
657}
658
659/**
660 * Applies the CFGM overlay as specified by VBoxInternal/XXX extra data
661 * values.
662 *
663 * @returns VBox status code.
664 * @param pRoot The root of the configuration tree.
665 * @param pVirtualBox Pointer to the IVirtualBox interface.
666 * @param pMachine Pointer to the IMachine interface.
667 */
668/* static */
669int Console::i_configCfgmOverlay(PCFGMNODE pRoot, IVirtualBox *pVirtualBox, IMachine *pMachine)
670{
671 /*
672 * CFGM overlay handling.
673 *
674 * Here we check the extra data entries for CFGM values
675 * and create the nodes and insert the values on the fly. Existing
676 * values will be removed and reinserted. CFGM is typed, so by default
677 * we will guess whether it's a string or an integer (byte arrays are
678 * not currently supported). It's possible to override this autodetection
679 * by adding "string:", "integer:" or "bytes:" (future).
680 *
681 * We first perform a run on global extra data, then on the machine
682 * extra data to support global settings with local overrides.
683 */
684 int vrc = VINF_SUCCESS;
685 bool fFirst = true;
686 try
687 {
688 /** @todo add support for removing nodes and byte blobs. */
689 /*
690 * Get the next key
691 */
692 SafeArray<BSTR> aGlobalExtraDataKeys;
693 SafeArray<BSTR> aMachineExtraDataKeys;
694 HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
695 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
696
697 // remember the no. of global values so we can call the correct method below
698 size_t cGlobalValues = aGlobalExtraDataKeys.size();
699
700 hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
701 AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
702
703 // build a combined list from global keys...
704 std::list<Utf8Str> llExtraDataKeys;
705
706 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i)
707 llExtraDataKeys.push_back(Utf8Str(aGlobalExtraDataKeys[i]));
708 // ... and machine keys
709 for (size_t i = 0; i < aMachineExtraDataKeys.size(); ++i)
710 llExtraDataKeys.push_back(Utf8Str(aMachineExtraDataKeys[i]));
711
712 size_t i2 = 0;
713 for (std::list<Utf8Str>::const_iterator it = llExtraDataKeys.begin();
714 it != llExtraDataKeys.end();
715 ++it, ++i2)
716 {
717 const Utf8Str &strKey = *it;
718
719 /*
720 * We only care about keys starting with "VBoxInternal/" (skip "G:" or "M:")
721 */
722 if (!strKey.startsWith("VBoxInternal/"))
723 continue;
724
725 const char *pszExtraDataKey = strKey.c_str() + sizeof("VBoxInternal/") - 1;
726
727 // get the value
728 Bstr bstrExtraDataValue;
729 if (i2 < cGlobalValues)
730 // this is still one of the global values:
731 hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam());
732 else
733 hrc = pMachine->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam());
734 if (FAILED(hrc))
735 LogRel(("Warning: Cannot get extra data key %s, hrc = %Rhrc\n", strKey.c_str(), hrc));
736
737 if (fFirst)
738 {
739 fFirst = false;
740 LogRel(("Extradata overrides:\n"));
741 }
742 LogRel((" %s=\"%ls\"%s\n", strKey.c_str(), bstrExtraDataValue.raw(), i2 < cGlobalValues ? " (global)" : ""));
743
744 /*
745 * The key will be in the format "Node1/Node2/Value" or simply "Value".
746 * Split the two and get the node, delete the value and create the node
747 * if necessary.
748 */
749 PCFGMNODE pNode;
750 const char *pszCFGMValueName = strrchr(pszExtraDataKey, '/');
751 if (pszCFGMValueName)
752 {
753 /* terminate the node and advance to the value (Utf8Str might not
754 offically like this but wtf) */
755 *(char *)pszCFGMValueName = '\0';
756 ++pszCFGMValueName;
757
758 /* does the node already exist? */
759 pNode = mpVMM->pfnCFGMR3GetChild(pRoot, pszExtraDataKey);
760 if (pNode)
761 mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName);
762 else
763 {
764 /* create the node */
765 vrc = mpVMM->pfnCFGMR3InsertNode(pRoot, pszExtraDataKey, &pNode);
766 if (RT_FAILURE(vrc))
767 {
768 AssertLogRelMsgRC(vrc, ("failed to insert node '%s'\n", pszExtraDataKey));
769 continue;
770 }
771 Assert(pNode);
772 }
773 }
774 else
775 {
776 /* root value (no node path). */
777 pNode = pRoot;
778 pszCFGMValueName = pszExtraDataKey;
779 pszExtraDataKey--;
780 mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName);
781 }
782
783 /*
784 * Now let's have a look at the value.
785 * Empty strings means that we should remove the value, which we've
786 * already done above.
787 */
788 Utf8Str strCFGMValueUtf8(bstrExtraDataValue);
789 if (strCFGMValueUtf8.isNotEmpty())
790 {
791 uint64_t u64Value;
792
793 /* check for type prefix first. */
794 if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("string:")))
795 vrc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str() + sizeof("string:") - 1);
796 else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("integer:")))
797 {
798 vrc = RTStrToUInt64Full(strCFGMValueUtf8.c_str() + sizeof("integer:") - 1, 0, &u64Value);
799 if (RT_SUCCESS(vrc))
800 vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
801 }
802 else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("bytes:")))
803 {
804 char const *pszBase64 = strCFGMValueUtf8.c_str() + sizeof("bytes:") - 1;
805 ssize_t cbValue = RTBase64DecodedSize(pszBase64, NULL);
806 if (cbValue > 0)
807 {
808 void *pvBytes = RTMemTmpAlloc(cbValue);
809 if (pvBytes)
810 {
811 vrc = RTBase64Decode(pszBase64, pvBytes, cbValue, NULL, NULL);
812 if (RT_SUCCESS(vrc))
813 vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, pvBytes, cbValue);
814 RTMemTmpFree(pvBytes);
815 }
816 else
817 vrc = VERR_NO_TMP_MEMORY;
818 }
819 else if (cbValue == 0)
820 vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, NULL, 0);
821 else
822 vrc = VERR_INVALID_BASE64_ENCODING;
823 }
824 /* auto detect type. */
825 else if (RT_SUCCESS(RTStrToUInt64Full(strCFGMValueUtf8.c_str(), 0, &u64Value)))
826 vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
827 else
828 vrc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str());
829 AssertLogRelMsgRCBreak(vrc, ("failed to insert CFGM value '%s' to key '%s'\n",
830 strCFGMValueUtf8.c_str(), pszExtraDataKey));
831 }
832 }
833 }
834 catch (ConfigError &x)
835 {
836 // InsertConfig threw something:
837 return x.m_vrc;
838 }
839 return vrc;
840}
841
842/**
843 * Dumps the API settings tweaks as specified by VBoxInternal2/XXX extra data
844 * values.
845 *
846 * @returns VBox status code.
847 * @param pVirtualBox Pointer to the IVirtualBox interface.
848 * @param pMachine Pointer to the IMachine interface.
849 */
850/* static */
851int Console::i_configDumpAPISettingsTweaks(IVirtualBox *pVirtualBox, IMachine *pMachine)
852{
853 {
854 SafeArray<BSTR> aGlobalExtraDataKeys;
855 HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
856 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
857 bool hasKey = false;
858 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); i++)
859 {
860 Utf8Str strKey(aGlobalExtraDataKeys[i]);
861 if (!strKey.startsWith("VBoxInternal2/"))
862 continue;
863
864 Bstr bstrValue;
865 hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(),
866 bstrValue.asOutParam());
867 if (FAILED(hrc))
868 continue;
869 if (!hasKey)
870 LogRel(("Global extradata API settings:\n"));
871 LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
872 hasKey = true;
873 }
874 }
875
876 {
877 SafeArray<BSTR> aMachineExtraDataKeys;
878 HRESULT hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
879 AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
880 bool hasKey = false;
881 for (size_t i = 0; i < aMachineExtraDataKeys.size(); i++)
882 {
883 Utf8Str strKey(aMachineExtraDataKeys[i]);
884 if (!strKey.startsWith("VBoxInternal2/"))
885 continue;
886
887 Bstr bstrValue;
888 hrc = pMachine->GetExtraData(Bstr(strKey).raw(),
889 bstrValue.asOutParam());
890 if (FAILED(hrc))
891 continue;
892 if (!hasKey)
893 LogRel(("Per-VM extradata API settings:\n"));
894 LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
895 hasKey = true;
896 }
897 }
898
899 return VINF_SUCCESS;
900}
901
902
903/**
904 * Ellipsis to va_list wrapper for calling setVMRuntimeErrorCallback.
905 */
906void Console::i_atVMRuntimeErrorCallbackF(uint32_t fFlags, const char *pszErrorId, const char *pszFormat, ...)
907{
908 va_list va;
909 va_start(va, pszFormat);
910 i_atVMRuntimeErrorCallback(NULL, this, fFlags, pszErrorId, pszFormat, va);
911 va_end(va);
912}
913
914/* XXX introduce RT format specifier */
915static uint64_t formatDiskSize(uint64_t u64Size, const char **pszUnit)
916{
917 if (u64Size > INT64_C(5000)*_1G)
918 {
919 *pszUnit = "TB";
920 return u64Size / _1T;
921 }
922 else if (u64Size > INT64_C(5000)*_1M)
923 {
924 *pszUnit = "GB";
925 return u64Size / _1G;
926 }
927 else
928 {
929 *pszUnit = "MB";
930 return u64Size / _1M;
931 }
932}
933
934/**
935 * Checks the location of the given medium for known bugs affecting the usage
936 * of the host I/O cache setting.
937 *
938 * @returns VBox status code.
939 * @param pMedium The medium to check.
940 * @param pfUseHostIOCache Where to store the suggested host I/O cache setting.
941 */
942int Console::i_checkMediumLocation(IMedium *pMedium, bool *pfUseHostIOCache)
943{
944#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
945 /*
946 * Some sanity checks.
947 */
948 RT_NOREF(pfUseHostIOCache);
949 ComPtr<IMediumFormat> pMediumFormat;
950 HRESULT hrc = pMedium->COMGETTER(MediumFormat)(pMediumFormat.asOutParam()); H();
951 ULONG uCaps = 0;
952 com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
953 hrc = pMediumFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); H();
954
955 for (ULONG j = 0; j < mediumFormatCap.size(); j++)
956 uCaps |= mediumFormatCap[j];
957
958 if (uCaps & MediumFormatCapabilities_File)
959 {
960 Bstr bstrFile;
961 hrc = pMedium->COMGETTER(Location)(bstrFile.asOutParam()); H();
962 Utf8Str const strFile(bstrFile);
963
964 Bstr bstrSnap;
965 ComPtr<IMachine> pMachine = i_machine();
966 hrc = pMachine->COMGETTER(SnapshotFolder)(bstrSnap.asOutParam()); H();
967 Utf8Str const strSnap(bstrSnap);
968
969 RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN;
970 int vrc2 = RTFsQueryType(strFile.c_str(), &enmFsTypeFile);
971 AssertMsgRCReturn(vrc2, ("Querying the file type of '%s' failed!\n", strFile.c_str()), vrc2);
972
973 /* Any VM which hasn't created a snapshot or saved the current state of the VM
974 * won't have a Snapshot folder yet so no need to log anything about the file system
975 * type of the non-existent directory in such cases. */
976 RTFSTYPE enmFsTypeSnap = RTFSTYPE_UNKNOWN;
977 vrc2 = RTFsQueryType(strSnap.c_str(), &enmFsTypeSnap);
978 if (RT_SUCCESS(vrc2) && !mfSnapshotFolderDiskTypeShown)
979 {
980 LogRel(("File system of '%s' (snapshots) is %s\n", strSnap.c_str(), RTFsTypeName(enmFsTypeSnap)));
981 mfSnapshotFolderDiskTypeShown = true;
982 }
983 LogRel(("File system of '%s' is %s\n", strFile.c_str(), RTFsTypeName(enmFsTypeFile)));
984 LONG64 i64Size;
985 hrc = pMedium->COMGETTER(LogicalSize)(&i64Size); H();
986#ifdef RT_OS_WINDOWS
987 if ( enmFsTypeFile == RTFSTYPE_FAT
988 && i64Size >= _4G)
989 {
990 const char *pszUnit;
991 uint64_t u64Print = formatDiskSize((uint64_t)i64Size, &pszUnit);
992 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
993 N_("The medium '%s' has a logical size of %RU64%s "
994 "but the file system the medium is located on seems "
995 "to be FAT(32) which cannot handle files bigger than 4GB.\n"
996 "We strongly recommend to put all your virtual disk images and "
997 "the snapshot folder onto an NTFS partition"),
998 strFile.c_str(), u64Print, pszUnit);
999 }
1000#else /* !RT_OS_WINDOWS */
1001 if ( enmFsTypeFile == RTFSTYPE_FAT
1002 || enmFsTypeFile == RTFSTYPE_EXT
1003 || enmFsTypeFile == RTFSTYPE_EXT2
1004 || enmFsTypeFile == RTFSTYPE_EXT3
1005 || enmFsTypeFile == RTFSTYPE_EXT4)
1006 {
1007 RTFILE file;
1008 int vrc = RTFileOpen(&file, strFile.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1009 if (RT_SUCCESS(vrc))
1010 {
1011 RTFOFF maxSize;
1012 /* Careful: This function will work only on selected local file systems! */
1013 vrc = RTFileQueryMaxSizeEx(file, &maxSize);
1014 RTFileClose(file);
1015 if ( RT_SUCCESS(vrc)
1016 && maxSize > 0
1017 && i64Size > (LONG64)maxSize)
1018 {
1019 const char *pszUnitSiz;
1020 const char *pszUnitMax;
1021 uint64_t u64PrintSiz = formatDiskSize((LONG64)i64Size, &pszUnitSiz);
1022 uint64_t u64PrintMax = formatDiskSize(maxSize, &pszUnitMax);
1023 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected", /* <= not exact but ... */
1024 N_("The medium '%s' has a logical size of %RU64%s "
1025 "but the file system the medium is located on can "
1026 "only handle files up to %RU64%s in theory.\n"
1027 "We strongly recommend to put all your virtual disk "
1028 "images and the snapshot folder onto a proper "
1029 "file system (e.g. ext3) with a sufficient size"),
1030 strFile.c_str(), u64PrintSiz, pszUnitSiz, u64PrintMax, pszUnitMax);
1031 }
1032 }
1033 }
1034#endif /* !RT_OS_WINDOWS */
1035
1036 /*
1037 * Snapshot folder:
1038 * Here we test only for a FAT partition as we had to create a dummy file otherwise
1039 */
1040 if ( enmFsTypeSnap == RTFSTYPE_FAT
1041 && i64Size >= _4G
1042 && !mfSnapshotFolderSizeWarningShown)
1043 {
1044 const char *pszUnit;
1045 uint64_t u64Print = formatDiskSize(i64Size, &pszUnit);
1046 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
1047#ifdef RT_OS_WINDOWS
1048 N_("The snapshot folder of this VM '%s' seems to be located on "
1049 "a FAT(32) file system. The logical size of the medium '%s' "
1050 "(%RU64%s) is bigger than the maximum file size this file "
1051 "system can handle (4GB).\n"
1052 "We strongly recommend to put all your virtual disk images and "
1053 "the snapshot folder onto an NTFS partition"),
1054#else
1055 N_("The snapshot folder of this VM '%s' seems to be located on "
1056 "a FAT(32) file system. The logical size of the medium '%s' "
1057 "(%RU64%s) is bigger than the maximum file size this file "
1058 "system can handle (4GB).\n"
1059 "We strongly recommend to put all your virtual disk images and "
1060 "the snapshot folder onto a proper file system (e.g. ext3)"),
1061#endif
1062 strSnap.c_str(), strFile.c_str(), u64Print, pszUnit);
1063 /* Show this particular warning only once */
1064 mfSnapshotFolderSizeWarningShown = true;
1065 }
1066
1067#ifdef RT_OS_LINUX
1068 /*
1069 * Ext4 bug: Check if the host I/O cache is disabled and the disk image is located
1070 * on an ext4 partition.
1071 * This bug apparently applies to the XFS file system as well.
1072 * Linux 2.6.36 is known to be fixed (tested with 2.6.36-rc4).
1073 */
1074
1075 char szOsRelease[128];
1076 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOsRelease, sizeof(szOsRelease));
1077 bool fKernelHasODirectBug = RT_FAILURE(vrc)
1078 || (RTStrVersionCompare(szOsRelease, "2.6.36-rc4") < 0);
1079
1080 if ( (uCaps & MediumFormatCapabilities_Asynchronous)
1081 && !*pfUseHostIOCache
1082 && fKernelHasODirectBug)
1083 {
1084 if ( enmFsTypeFile == RTFSTYPE_EXT4
1085 || enmFsTypeFile == RTFSTYPE_XFS)
1086 {
1087 i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
1088 N_("The host I/O cache for at least one controller is disabled "
1089 "and the medium '%s' for this VM "
1090 "is located on an %s partition. There is a known Linux "
1091 "kernel bug which can lead to the corruption of the virtual "
1092 "disk image under these conditions.\n"
1093 "Either enable the host I/O cache permanently in the VM "
1094 "settings or put the disk image and the snapshot folder "
1095 "onto a different file system.\n"
1096 "The host I/O cache will now be enabled for this medium"),
1097 strFile.c_str(), enmFsTypeFile == RTFSTYPE_EXT4 ? "ext4" : "xfs");
1098 *pfUseHostIOCache = true;
1099 }
1100 else if ( ( enmFsTypeSnap == RTFSTYPE_EXT4
1101 || enmFsTypeSnap == RTFSTYPE_XFS)
1102 && !mfSnapshotFolderExt4WarningShown)
1103 {
1104 i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
1105 N_("The host I/O cache for at least one controller is disabled "
1106 "and the snapshot folder for this VM "
1107 "is located on an %s partition. There is a known Linux "
1108 "kernel bug which can lead to the corruption of the virtual "
1109 "disk image under these conditions.\n"
1110 "Either enable the host I/O cache permanently in the VM "
1111 "settings or put the disk image and the snapshot folder "
1112 "onto a different file system.\n"
1113 "The host I/O cache will now be enabled for this medium"),
1114 enmFsTypeSnap == RTFSTYPE_EXT4 ? "ext4" : "xfs");
1115 *pfUseHostIOCache = true;
1116 mfSnapshotFolderExt4WarningShown = true;
1117 }
1118 }
1119
1120 /*
1121 * 2.6.18 bug: Check if the host I/O cache is disabled and the host is running
1122 * Linux 2.6.18. See @bugref{8690}. Apparently the same problem as
1123 * documented in https://lkml.org/lkml/2007/2/1/14. We saw such
1124 * kernel oopses on Linux 2.6.18-416.el5. We don't know when this
1125 * was fixed but we _know_ that 2.6.18 EL5 kernels are affected.
1126 */
1127 bool fKernelAsyncUnreliable = RT_FAILURE(vrc)
1128 || (RTStrVersionCompare(szOsRelease, "2.6.19") < 0);
1129 if ( (uCaps & MediumFormatCapabilities_Asynchronous)
1130 && !*pfUseHostIOCache
1131 && fKernelAsyncUnreliable)
1132 {
1133 i_atVMRuntimeErrorCallbackF(0, "Linux2618TooOld",
1134 N_("The host I/O cache for at least one controller is disabled. "
1135 "There is a known Linux kernel bug which can lead to kernel "
1136 "oopses under heavy load. To our knowledge this bug affects "
1137 "all 2.6.18 kernels.\n"
1138 "Either enable the host I/O cache permanently in the VM "
1139 "settings or switch to a newer host kernel.\n"
1140 "The host I/O cache will now be enabled for this medium"));
1141 *pfUseHostIOCache = true;
1142 }
1143#endif
1144 }
1145#undef H
1146
1147 return VINF_SUCCESS;
1148}
1149
1150/**
1151 * Unmounts the specified medium from the specified device.
1152 *
1153 * @returns VBox status code.
1154 * @param pUVM The usermode VM handle.
1155 * @param pVMM The VMM vtable.
1156 * @param enmBus The storage bus.
1157 * @param enmDevType The device type.
1158 * @param pcszDevice The device emulation.
1159 * @param uInstance Instance of the device.
1160 * @param uLUN The LUN on the device.
1161 * @param fForceUnmount Whether to force unmounting.
1162 */
1163int Console::i_unmountMediumFromGuest(PUVM pUVM, PCVMMR3VTABLE pVMM, StorageBus_T enmBus, DeviceType_T enmDevType,
1164 const char *pcszDevice, unsigned uInstance, unsigned uLUN,
1165 bool fForceUnmount) RT_NOEXCEPT
1166{
1167 /* Unmount existing media only for floppy and DVD drives. */
1168 int vrc = VINF_SUCCESS;
1169 PPDMIBASE pBase;
1170 if (enmBus == StorageBus_USB)
1171 vrc = pVMM->pfnPDMR3UsbQueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase);
1172 else if ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI)
1173 || (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD))
1174 vrc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase);
1175 else /* IDE or Floppy */
1176 vrc = pVMM->pfnPDMR3QueryLun(pUVM, pcszDevice, uInstance, uLUN, &pBase);
1177
1178 if (RT_FAILURE(vrc))
1179 {
1180 if (vrc == VERR_PDM_LUN_NOT_FOUND || vrc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
1181 vrc = VINF_SUCCESS;
1182 AssertRC(vrc);
1183 }
1184 else
1185 {
1186 PPDMIMOUNT pIMount = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMOUNT);
1187 AssertReturn(pIMount, VERR_INVALID_POINTER);
1188
1189 /* Unmount the media (but do not eject the medium!) */
1190 vrc = pIMount->pfnUnmount(pIMount, fForceUnmount, false /*=fEject*/);
1191 if (vrc == VERR_PDM_MEDIA_NOT_MOUNTED)
1192 vrc = VINF_SUCCESS;
1193 /* for example if the medium is locked */
1194 else if (RT_FAILURE(vrc))
1195 return vrc;
1196 }
1197
1198 return vrc;
1199}
1200
1201/**
1202 * Removes the currently attached medium driver form the specified device
1203 * taking care of the controlelr specific configs wrt. to the attached driver chain.
1204 *
1205 * @returns VBox status code.
1206 * @param pCtlInst The controler instance node in the CFGM tree.
1207 * @param pcszDevice The device name.
1208 * @param uInstance The device instance.
1209 * @param uLUN The device LUN.
1210 * @param enmBus The storage bus.
1211 * @param fAttachDetach Flag whether this is a change while the VM is running
1212 * @param fHotplug Flag whether the guest should be notified about the device change.
1213 * @param fForceUnmount Flag whether to force unmounting the medium even if it is locked.
1214 * @param pUVM The usermode VM handle.
1215 * @param pVMM The VMM vtable.
1216 * @param enmDevType The device type.
1217 * @param ppLunL0 Where to store the node to attach the new config to on success.
1218 */
1219int Console::i_removeMediumDriverFromVm(PCFGMNODE pCtlInst,
1220 const char *pcszDevice,
1221 unsigned uInstance,
1222 unsigned uLUN,
1223 StorageBus_T enmBus,
1224 bool fAttachDetach,
1225 bool fHotplug,
1226 bool fForceUnmount,
1227 PUVM pUVM,
1228 PCVMMR3VTABLE pVMM,
1229 DeviceType_T enmDevType,
1230 PCFGMNODE *ppLunL0)
1231{
1232 int vrc = VINF_SUCCESS;
1233 bool fAddLun = false;
1234
1235 /* First check if the LUN already exists. */
1236 PCFGMNODE pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
1237 AssertReturn(!RT_VALID_PTR(pLunL0) || fAttachDetach, VERR_INTERNAL_ERROR);
1238
1239 if (pLunL0)
1240 {
1241 /*
1242 * Unmount the currently mounted medium if we don't just hot remove the
1243 * complete device (SATA) and it supports unmounting (DVD).
1244 */
1245 if ( (enmDevType != DeviceType_HardDisk)
1246 && !fHotplug)
1247 {
1248 vrc = i_unmountMediumFromGuest(pUVM, pVMM, enmBus, enmDevType, pcszDevice, uInstance, uLUN, fForceUnmount);
1249 if (RT_FAILURE(vrc))
1250 return vrc;
1251 }
1252
1253 /*
1254 * Don't detach the SCSI driver when unmounting the current medium
1255 * (we are not ripping out the device but only eject the medium).
1256 */
1257 char *pszDriverDetach = NULL;
1258 if ( !fHotplug
1259 && ( (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD)
1260 || enmBus == StorageBus_SAS
1261 || enmBus == StorageBus_SCSI
1262 || enmBus == StorageBus_VirtioSCSI
1263 || enmBus == StorageBus_USB))
1264 {
1265 /* Get the current attached driver we have to detach. */
1266 PCFGMNODE pDrvLun = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u/AttachedDriver/", uLUN);
1267 if (pDrvLun)
1268 {
1269 char szDriver[128];
1270 RT_ZERO(szDriver);
1271 vrc = pVMM->pfnCFGMR3QueryString(pDrvLun, "Driver", &szDriver[0], sizeof(szDriver));
1272 if (RT_SUCCESS(vrc))
1273 pszDriverDetach = RTStrDup(&szDriver[0]);
1274
1275 pLunL0 = pDrvLun;
1276 }
1277 }
1278
1279 if (enmBus == StorageBus_USB)
1280 vrc = pVMM->pfnPDMR3UsbDriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach,
1281 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
1282 else
1283 vrc = pVMM->pfnPDMR3DriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach,
1284 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
1285
1286 if (pszDriverDetach)
1287 {
1288 RTStrFree(pszDriverDetach);
1289 /* Remove the complete node and create new for the new config. */
1290 pVMM->pfnCFGMR3RemoveNode(pLunL0);
1291 pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
1292 if (pLunL0)
1293 {
1294 try
1295 {
1296 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
1297 }
1298 catch (ConfigError &x)
1299 {
1300 // InsertConfig threw something:
1301 return x.m_vrc;
1302 }
1303 }
1304 }
1305 if (vrc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
1306 vrc = VINF_SUCCESS;
1307 AssertRCReturn(vrc, vrc);
1308
1309 /*
1310 * Don't remove the LUN except for IDE/floppy/NVMe (which connects directly to the medium driver
1311 * even for DVD devices) or if there is a hotplug event which rips out the complete device.
1312 */
1313 if ( fHotplug
1314 || enmBus == StorageBus_IDE
1315 || enmBus == StorageBus_Floppy
1316 || enmBus == StorageBus_PCIe
1317 || (enmBus == StorageBus_SATA && enmDevType != DeviceType_DVD))
1318 {
1319 fAddLun = true;
1320 pVMM->pfnCFGMR3RemoveNode(pLunL0);
1321 }
1322 }
1323 else
1324 fAddLun = true;
1325
1326 try
1327 {
1328 if (fAddLun)
1329 InsertConfigNodeF(pCtlInst, &pLunL0, "LUN#%u", uLUN);
1330 }
1331 catch (ConfigError &x)
1332 {
1333 // InsertConfig threw something:
1334 return x.m_vrc;
1335 }
1336
1337 if (ppLunL0)
1338 *ppLunL0 = pLunL0;
1339
1340 return vrc;
1341}
1342
1343int Console::i_configMediumAttachment(const char *pcszDevice,
1344 unsigned uInstance,
1345 StorageBus_T enmBus,
1346 bool fUseHostIOCache,
1347 bool fBuiltinIOCache,
1348 bool fInsertDiskIntegrityDrv,
1349 bool fSetupMerge,
1350 unsigned uMergeSource,
1351 unsigned uMergeTarget,
1352 IMediumAttachment *pMediumAtt,
1353 MachineState_T aMachineState,
1354 HRESULT *phrc,
1355 bool fAttachDetach,
1356 bool fForceUnmount,
1357 bool fHotplug,
1358 PUVM pUVM,
1359 PCVMMR3VTABLE pVMM,
1360 DeviceType_T *paLedDevType,
1361 PCFGMNODE *ppLunL0)
1362{
1363 // InsertConfig* throws
1364 try
1365 {
1366 int vrc = VINF_SUCCESS;
1367 HRESULT hrc;
1368 Bstr bstr;
1369 PCFGMNODE pCtlInst = NULL;
1370
1371// #define RC_CHECK() AssertMsgReturn(RT_SUCCESS(vrc), ("vrc=%Rrc\n", vrc), vrc)
1372#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
1373
1374 LONG lDev;
1375 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
1376 LONG lPort;
1377 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
1378 DeviceType_T enmType;
1379 hrc = pMediumAtt->COMGETTER(Type)(&enmType); H();
1380 BOOL fNonRotational;
1381 hrc = pMediumAtt->COMGETTER(NonRotational)(&fNonRotational); H();
1382 BOOL fDiscard;
1383 hrc = pMediumAtt->COMGETTER(Discard)(&fDiscard); H();
1384
1385 if (enmType == DeviceType_DVD)
1386 fInsertDiskIntegrityDrv = false;
1387
1388 unsigned uLUN;
1389 PCFGMNODE pLunL0 = NULL;
1390 hrc = Console::i_storageBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
1391
1392 /* Determine the base path for the device instance. */
1393 if (enmBus != StorageBus_USB)
1394 pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
1395 else
1396 {
1397 /* If we hotplug a USB device create a new CFGM tree. */
1398 if (!fHotplug)
1399 pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "USB/%s/", pcszDevice);
1400 else
1401 pCtlInst = pVMM->pfnCFGMR3CreateTree(pUVM); /** @todo r=bird: Leaked in error paths! */
1402 }
1403 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
1404
1405 if (enmBus == StorageBus_USB)
1406 {
1407 PCFGMNODE pCfg = NULL;
1408
1409 /* Create correct instance. */
1410 if (!fHotplug)
1411 {
1412 if (!fAttachDetach)
1413 InsertConfigNodeF(pCtlInst, &pCtlInst, "%d", lPort);
1414 else
1415 pCtlInst = pVMM->pfnCFGMR3GetChildF(pCtlInst, "%d/", lPort);
1416 }
1417
1418 if (!fAttachDetach)
1419 InsertConfigNode(pCtlInst, "Config", &pCfg);
1420
1421 uInstance = lPort; /* Overwrite uInstance with the correct one. */
1422
1423 /** @todo No LED after hotplugging. */
1424 if (!fHotplug && !fAttachDetach)
1425 {
1426 USBStorageDevice UsbMsd;
1427 UsbMsd.iPort = uInstance;
1428 vrc = RTUuidCreate(&UsbMsd.mUuid);
1429 AssertRCReturn(vrc, vrc);
1430
1431 InsertConfigStringF(pCtlInst, "UUID", "%RTuuid", &UsbMsd.mUuid);
1432
1433 mUSBStorageDevices.push_back(UsbMsd);
1434
1435 /** @todo This LED set is not freed if the device is unplugged. We could
1436 * keep the LED set index in the UsbMsd structure and clean it up in
1437 * i_detachStorageDevice. */
1438 /* Attach the status driver */
1439 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk),
1440 8, &paLedDevType, &mapMediumAttachments, pcszDevice, 0);
1441 }
1442 }
1443
1444 vrc = i_removeMediumDriverFromVm(pCtlInst, pcszDevice, uInstance, uLUN, enmBus, fAttachDetach,
1445 fHotplug, fForceUnmount, pUVM, pVMM, enmType, &pLunL0);
1446 if (RT_FAILURE(vrc))
1447 return vrc;
1448 if (ppLunL0)
1449 *ppLunL0 = pLunL0;
1450
1451 Utf8StrFmt devicePath("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
1452 mapMediumAttachments[devicePath] = pMediumAtt;
1453
1454 ComPtr<IMedium> ptrMedium;
1455 hrc = pMediumAtt->COMGETTER(Medium)(ptrMedium.asOutParam()); H();
1456
1457 /*
1458 * 1. Only check this for hard disk images.
1459 * 2. Only check during VM creation and not later, especially not during
1460 * taking an online snapshot!
1461 */
1462 if ( enmType == DeviceType_HardDisk
1463 && ( aMachineState == MachineState_Starting
1464 || aMachineState == MachineState_Restoring))
1465 {
1466 vrc = i_checkMediumLocation(ptrMedium, &fUseHostIOCache);
1467 if (RT_FAILURE(vrc))
1468 return vrc;
1469 }
1470
1471 BOOL fPassthrough = FALSE;
1472 if (ptrMedium.isNotNull())
1473 {
1474 BOOL fHostDrive;
1475 hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H();
1476 if ( ( enmType == DeviceType_DVD
1477 || enmType == DeviceType_Floppy)
1478 && !fHostDrive)
1479 {
1480 /*
1481 * Informative logging.
1482 */
1483 Bstr bstrFile;
1484 hrc = ptrMedium->COMGETTER(Location)(bstrFile.asOutParam()); H();
1485 Utf8Str strFile(bstrFile);
1486 RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN;
1487 (void)RTFsQueryType(strFile.c_str(), &enmFsTypeFile);
1488 LogRel(("File system of '%s' (%s) is %s\n",
1489 strFile.c_str(), enmType == DeviceType_DVD ? "DVD" : "Floppy", RTFsTypeName(enmFsTypeFile)));
1490 }
1491
1492 if (fHostDrive)
1493 {
1494 hrc = pMediumAtt->COMGETTER(Passthrough)(&fPassthrough); H();
1495 }
1496 }
1497
1498 ComObjPtr<IBandwidthGroup> pBwGroup;
1499 Bstr bstrBwGroup;
1500 hrc = pMediumAtt->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H();
1501
1502 if (!pBwGroup.isNull())
1503 {
1504 hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H();
1505 }
1506
1507 /*
1508 * Insert the SCSI driver for hotplug events on the SCSI/USB based storage controllers
1509 * or for SATA if the new device is a CD/DVD drive.
1510 */
1511 if ( (fHotplug || !fAttachDetach)
1512 && ( enmBus == StorageBus_SCSI
1513 || enmBus == StorageBus_SAS
1514 || enmBus == StorageBus_USB
1515 || enmBus == StorageBus_VirtioSCSI
1516 || (enmBus == StorageBus_SATA && enmType == DeviceType_DVD && !fPassthrough)))
1517 {
1518 InsertConfigString(pLunL0, "Driver", "SCSI");
1519 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
1520 }
1521
1522 vrc = i_configMedium(pLunL0,
1523 !!fPassthrough,
1524 enmType,
1525 fUseHostIOCache,
1526 fBuiltinIOCache,
1527 fInsertDiskIntegrityDrv,
1528 fSetupMerge,
1529 uMergeSource,
1530 uMergeTarget,
1531 bstrBwGroup.isEmpty() ? NULL : Utf8Str(bstrBwGroup).c_str(),
1532 !!fDiscard,
1533 !!fNonRotational,
1534 ptrMedium,
1535 aMachineState,
1536 phrc);
1537 if (RT_FAILURE(vrc))
1538 return vrc;
1539
1540 if (fAttachDetach)
1541 {
1542 /* Attach the new driver. */
1543 if (enmBus == StorageBus_USB)
1544 {
1545 if (fHotplug)
1546 {
1547 USBStorageDevice UsbMsd;
1548 RTUuidCreate(&UsbMsd.mUuid);
1549 UsbMsd.iPort = uInstance;
1550 vrc = pVMM->pfnPDMR3UsbCreateEmulatedDevice(pUVM, pcszDevice, pCtlInst, &UsbMsd.mUuid, NULL);
1551 if (RT_SUCCESS(vrc))
1552 mUSBStorageDevices.push_back(UsbMsd);
1553 }
1554 else
1555 vrc = pVMM->pfnPDMR3UsbDriverAttach(pUVM, pcszDevice, uInstance, uLUN,
1556 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
1557 }
1558 else if ( !fHotplug
1559 && ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI)
1560 || (enmBus == StorageBus_SATA && enmType == DeviceType_DVD)))
1561 vrc = pVMM->pfnPDMR3DriverAttach(pUVM, pcszDevice, uInstance, uLUN,
1562 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
1563 else
1564 vrc = pVMM->pfnPDMR3DeviceAttach(pUVM, pcszDevice, uInstance, uLUN,
1565 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
1566 AssertRCReturn(vrc, vrc);
1567
1568 /*
1569 * Make the secret key helper interface known to the VD driver if it is attached,
1570 * so we can get notified about missing keys.
1571 */
1572 PPDMIBASE pIBase = NULL;
1573 vrc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
1574 if (RT_SUCCESS(vrc) && pIBase)
1575 {
1576 PPDMIMEDIA pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
1577 if (pIMedium)
1578 {
1579 vrc = pIMedium->pfnSetSecKeyIf(pIMedium, mpIfSecKey, mpIfSecKeyHlp);
1580 Assert(RT_SUCCESS(vrc) || vrc == VERR_NOT_SUPPORTED);
1581 }
1582 }
1583
1584 /* There is no need to handle removable medium mounting, as we
1585 * unconditionally replace everthing including the block driver level.
1586 * This means the new medium will be picked up automatically. */
1587 }
1588
1589 if (paLedDevType)
1590 i_setLedType(&paLedDevType[uLUN], enmType);
1591
1592 /* Dump the changed LUN if possible, dump the complete device otherwise */
1593 if ( aMachineState != MachineState_Starting
1594 && aMachineState != MachineState_Restoring)
1595 pVMM->pfnCFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst);
1596 }
1597 catch (ConfigError &x)
1598 {
1599 // InsertConfig threw something:
1600 return x.m_vrc;
1601 }
1602
1603#undef H
1604
1605 return VINF_SUCCESS;
1606}
1607
1608int Console::i_configMedium(PCFGMNODE pLunL0,
1609 bool fPassthrough,
1610 DeviceType_T enmType,
1611 bool fUseHostIOCache,
1612 bool fBuiltinIOCache,
1613 bool fInsertDiskIntegrityDrv,
1614 bool fSetupMerge,
1615 unsigned uMergeSource,
1616 unsigned uMergeTarget,
1617 const char *pcszBwGroup,
1618 bool fDiscard,
1619 bool fNonRotational,
1620 ComPtr<IMedium> ptrMedium,
1621 MachineState_T aMachineState,
1622 HRESULT *phrc)
1623{
1624 // InsertConfig* throws
1625 try
1626 {
1627 HRESULT hrc;
1628 Bstr bstr;
1629 PCFGMNODE pCfg = NULL;
1630
1631#define H() \
1632 AssertMsgReturnStmt(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc), if (phrc) *phrc = hrc, Global::vboxStatusCodeFromCOM(hrc))
1633
1634
1635 BOOL fHostDrive = FALSE;
1636 MediumType_T mediumType = MediumType_Normal;
1637 if (ptrMedium.isNotNull())
1638 {
1639 hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H();
1640 hrc = ptrMedium->COMGETTER(Type)(&mediumType); H();
1641 }
1642
1643 if (fHostDrive)
1644 {
1645 Assert(ptrMedium.isNotNull());
1646 if (enmType == DeviceType_DVD)
1647 {
1648 InsertConfigString(pLunL0, "Driver", "HostDVD");
1649 InsertConfigNode(pLunL0, "Config", &pCfg);
1650
1651 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1652 InsertConfigString(pCfg, "Path", bstr);
1653
1654 InsertConfigInteger(pCfg, "Passthrough", fPassthrough);
1655 }
1656 else if (enmType == DeviceType_Floppy)
1657 {
1658 InsertConfigString(pLunL0, "Driver", "HostFloppy");
1659 InsertConfigNode(pLunL0, "Config", &pCfg);
1660
1661 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1662 InsertConfigString(pCfg, "Path", bstr);
1663 }
1664 }
1665 else
1666 {
1667 if (fInsertDiskIntegrityDrv)
1668 {
1669 /*
1670 * The actual configuration is done through CFGM extra data
1671 * for each inserted driver separately.
1672 */
1673 InsertConfigString(pLunL0, "Driver", "DiskIntegrity");
1674 InsertConfigNode(pLunL0, "Config", &pCfg);
1675 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
1676 }
1677
1678 InsertConfigString(pLunL0, "Driver", "VD");
1679 InsertConfigNode(pLunL0, "Config", &pCfg);
1680 switch (enmType)
1681 {
1682 case DeviceType_DVD:
1683 InsertConfigString(pCfg, "Type", "DVD");
1684 InsertConfigInteger(pCfg, "Mountable", 1);
1685 break;
1686 case DeviceType_Floppy:
1687 InsertConfigString(pCfg, "Type", "Floppy 1.44");
1688 InsertConfigInteger(pCfg, "Mountable", 1);
1689 break;
1690 case DeviceType_HardDisk:
1691 default:
1692 InsertConfigString(pCfg, "Type", "HardDisk");
1693 InsertConfigInteger(pCfg, "Mountable", 0);
1694 }
1695
1696 if ( ptrMedium.isNotNull()
1697 && ( enmType == DeviceType_DVD
1698 || enmType == DeviceType_Floppy)
1699 )
1700 {
1701 // if this medium represents an ISO image and this image is inaccessible,
1702 // the ignore it instead of causing a failure; this can happen when we
1703 // restore a VM state and the ISO has disappeared, e.g. because the Guest
1704 // Additions were mounted and the user upgraded VirtualBox. Previously
1705 // we failed on startup, but that's not good because the only way out then
1706 // would be to discard the VM state...
1707 MediumState_T mediumState;
1708 hrc = ptrMedium->RefreshState(&mediumState); H();
1709 if (mediumState == MediumState_Inaccessible)
1710 {
1711 Bstr loc;
1712 hrc = ptrMedium->COMGETTER(Location)(loc.asOutParam()); H();
1713 i_atVMRuntimeErrorCallbackF(0, "DvdOrFloppyImageInaccessible",
1714 N_("The image file '%ls' is inaccessible and is being ignored. "
1715 "Please select a different image file for the virtual %s drive."),
1716 loc.raw(),
1717 enmType == DeviceType_DVD ? "DVD" : "floppy");
1718 ptrMedium.setNull();
1719 }
1720 }
1721
1722 if (ptrMedium.isNotNull())
1723 {
1724 /* Start with length of parent chain, as the list is reversed */
1725 unsigned uImage = 0;
1726 ComPtr<IMedium> ptrTmp = ptrMedium;
1727 while (ptrTmp.isNotNull())
1728 {
1729 uImage++;
1730 ComPtr<IMedium> ptrParent;
1731 hrc = ptrTmp->COMGETTER(Parent)(ptrParent.asOutParam()); H();
1732 ptrTmp = ptrParent;
1733 }
1734 /* Index of last image */
1735 uImage--;
1736
1737# ifdef VBOX_WITH_EXTPACK
1738 if (mptrExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
1739 {
1740 /* Configure loading the VDPlugin. */
1741 static const char s_szVDPlugin[] = "VDPluginCrypt";
1742 PCFGMNODE pCfgPlugins = NULL;
1743 PCFGMNODE pCfgPlugin = NULL;
1744 Utf8Str strPlugin;
1745 hrc = mptrExtPackManager->i_getLibraryPathForExtPack(s_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
1746 // Don't fail, this is optional!
1747 if (SUCCEEDED(hrc))
1748 {
1749 InsertConfigNode(pCfg, "Plugins", &pCfgPlugins);
1750 InsertConfigNode(pCfgPlugins, s_szVDPlugin, &pCfgPlugin);
1751 InsertConfigString(pCfgPlugin, "Path", strPlugin);
1752 }
1753 }
1754# endif
1755
1756 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1757 InsertConfigString(pCfg, "Path", bstr);
1758
1759 hrc = ptrMedium->COMGETTER(Format)(bstr.asOutParam()); H();
1760 InsertConfigString(pCfg, "Format", bstr);
1761
1762 if (mediumType == MediumType_Readonly)
1763 InsertConfigInteger(pCfg, "ReadOnly", 1);
1764 else if (enmType == DeviceType_Floppy)
1765 InsertConfigInteger(pCfg, "MaybeReadOnly", 1);
1766
1767 /* Start without exclusive write access to the images. */
1768 /** @todo Live Migration: I don't quite like this, we risk screwing up when
1769 * we're resuming the VM if some 3rd dude have any of the VDIs open
1770 * with write sharing denied. However, if the two VMs are sharing a
1771 * image it really is necessary....
1772 *
1773 * So, on the "lock-media" command, the target teleporter should also
1774 * make DrvVD undo TempReadOnly. It gets interesting if we fail after
1775 * that. Grumble. */
1776 if ( enmType == DeviceType_HardDisk
1777 && aMachineState == MachineState_TeleportingIn)
1778 InsertConfigInteger(pCfg, "TempReadOnly", 1);
1779
1780 /* Flag for opening the medium for sharing between VMs. This
1781 * is done at the moment only for the first (and only) medium
1782 * in the chain, as shared media can have no diffs. */
1783 if (mediumType == MediumType_Shareable)
1784 InsertConfigInteger(pCfg, "Shareable", 1);
1785
1786 if (!fUseHostIOCache)
1787 {
1788 InsertConfigInteger(pCfg, "UseNewIo", 1);
1789 /*
1790 * Activate the builtin I/O cache for harddisks only.
1791 * It caches writes only which doesn't make sense for DVD drives
1792 * and just increases the overhead.
1793 */
1794 if ( fBuiltinIOCache
1795 && (enmType == DeviceType_HardDisk))
1796 InsertConfigInteger(pCfg, "BlockCache", 1);
1797 }
1798
1799 if (fSetupMerge)
1800 {
1801 InsertConfigInteger(pCfg, "SetupMerge", 1);
1802 if (uImage == uMergeSource)
1803 InsertConfigInteger(pCfg, "MergeSource", 1);
1804 else if (uImage == uMergeTarget)
1805 InsertConfigInteger(pCfg, "MergeTarget", 1);
1806 }
1807
1808 if (pcszBwGroup)
1809 InsertConfigString(pCfg, "BwGroup", pcszBwGroup);
1810
1811 if (fDiscard)
1812 InsertConfigInteger(pCfg, "Discard", 1);
1813
1814 if (fNonRotational)
1815 InsertConfigInteger(pCfg, "NonRotationalMedium", 1);
1816
1817 /* Pass all custom parameters. */
1818 bool fHostIP = true;
1819 bool fEncrypted = false;
1820 hrc = i_configMediumProperties(pCfg, ptrMedium, &fHostIP, &fEncrypted); H();
1821
1822 /* Create an inverted list of parents. */
1823 uImage--;
1824 ComPtr<IMedium> ptrParentMedium = ptrMedium;
1825 for (PCFGMNODE pParent = pCfg;; uImage--)
1826 {
1827 ComPtr<IMedium> ptrCurMedium;
1828 hrc = ptrParentMedium->COMGETTER(Parent)(ptrCurMedium.asOutParam()); H();
1829 if (ptrCurMedium.isNull())
1830 break;
1831
1832 PCFGMNODE pCur;
1833 InsertConfigNode(pParent, "Parent", &pCur);
1834 hrc = ptrCurMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1835 InsertConfigString(pCur, "Path", bstr);
1836
1837 hrc = ptrCurMedium->COMGETTER(Format)(bstr.asOutParam()); H();
1838 InsertConfigString(pCur, "Format", bstr);
1839
1840 if (fSetupMerge)
1841 {
1842 if (uImage == uMergeSource)
1843 InsertConfigInteger(pCur, "MergeSource", 1);
1844 else if (uImage == uMergeTarget)
1845 InsertConfigInteger(pCur, "MergeTarget", 1);
1846 }
1847
1848 /* Configure medium properties. */
1849 hrc = i_configMediumProperties(pCur, ptrCurMedium, &fHostIP, &fEncrypted); H();
1850
1851 /* next */
1852 pParent = pCur;
1853 ptrParentMedium = ptrCurMedium;
1854 }
1855
1856 /* Custom code: put marker to not use host IP stack to driver
1857 * configuration node. Simplifies life of DrvVD a bit. */
1858 if (!fHostIP)
1859 InsertConfigInteger(pCfg, "HostIPStack", 0);
1860
1861 if (fEncrypted)
1862 m_cDisksEncrypted++;
1863 }
1864 else
1865 {
1866 /* Set empty drive flag for DVD or floppy without media. */
1867 if ( enmType == DeviceType_DVD
1868 || enmType == DeviceType_Floppy)
1869 InsertConfigInteger(pCfg, "EmptyDrive", 1);
1870 }
1871 }
1872#undef H
1873 }
1874 catch (ConfigError &x)
1875 {
1876 // InsertConfig threw something:
1877 return x.m_vrc;
1878 }
1879
1880 return VINF_SUCCESS;
1881}
1882
1883/**
1884 * Adds the medium properties to the CFGM tree.
1885 *
1886 * @returns VBox status code.
1887 * @param pCur The current CFGM node.
1888 * @param pMedium The medium object to configure.
1889 * @param pfHostIP Where to return the value of the \"HostIPStack\" property if found.
1890 * @param pfEncrypted Where to return whether the medium is encrypted.
1891 */
1892int Console::i_configMediumProperties(PCFGMNODE pCur, IMedium *pMedium, bool *pfHostIP, bool *pfEncrypted)
1893{
1894 /* Pass all custom parameters. */
1895 SafeArray<BSTR> aNames;
1896 SafeArray<BSTR> aValues;
1897 HRESULT hrc = pMedium->GetProperties(NULL, ComSafeArrayAsOutParam(aNames), ComSafeArrayAsOutParam(aValues));
1898 if ( SUCCEEDED(hrc)
1899 && aNames.size() != 0)
1900 {
1901 PCFGMNODE pVDC;
1902 InsertConfigNode(pCur, "VDConfig", &pVDC);
1903 for (size_t ii = 0; ii < aNames.size(); ++ii)
1904 {
1905 if (aValues[ii] && *aValues[ii])
1906 {
1907 Utf8Str const strName = aNames[ii];
1908 Utf8Str const strValue = aValues[ii];
1909 size_t offSlash = strName.find("/", 0);
1910 if ( offSlash != strName.npos
1911 && !strName.startsWith("Special/"))
1912 {
1913 com::Utf8Str strFilter;
1914 hrc = strFilter.assignEx(strName, 0, offSlash);
1915 if (FAILED(hrc))
1916 break;
1917
1918 com::Utf8Str strKey;
1919 hrc = strKey.assignEx(strName, offSlash + 1, strName.length() - offSlash - 1); /* Skip slash */
1920 if (FAILED(hrc))
1921 break;
1922
1923 PCFGMNODE pCfgFilterConfig = mpVMM->pfnCFGMR3GetChild(pVDC, strFilter.c_str());
1924 if (!pCfgFilterConfig)
1925 InsertConfigNode(pVDC, strFilter.c_str(), &pCfgFilterConfig);
1926
1927 InsertConfigString(pCfgFilterConfig, strKey.c_str(), strValue);
1928 }
1929 else
1930 {
1931 InsertConfigString(pVDC, strName.c_str(), strValue);
1932 if ( strName.compare("HostIPStack") == 0
1933 && strValue.compare("0") == 0)
1934 *pfHostIP = false;
1935 }
1936
1937 if ( strName.compare("CRYPT/KeyId") == 0
1938 && pfEncrypted)
1939 *pfEncrypted = true;
1940 }
1941 }
1942 }
1943
1944 return hrc;
1945}
1946
1947
1948/**
1949 * Configure proxy parameters the Network configuration tree.
1950 *
1951 * Parameters may differ depending on the IP address being accessed.
1952 *
1953 * @returns VBox status code.
1954 *
1955 * @param virtualBox The VirtualBox object.
1956 * @param pCfg Configuration node for the driver.
1957 * @param pcszPrefix The prefix for CFGM parameters: "Primary" or "Secondary".
1958 * @param strIpAddr The public IP address to be accessed via a proxy.
1959 *
1960 * @thread EMT
1961 */
1962int Console::i_configProxy(ComPtr<IVirtualBox> virtualBox, PCFGMNODE pCfg, const char *pcszPrefix, const com::Utf8Str &strIpAddr)
1963{
1964/** @todo r=bird: This code doesn't handle cleanup correctly and may leak
1965 * when hitting errors or throwing exceptions (bad_alloc). */
1966 RTHTTPPROXYINFO ProxyInfo;
1967 ComPtr<ISystemProperties> systemProperties;
1968 ProxyMode_T enmProxyMode;
1969 HRESULT hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
1970 if (FAILED(hrc))
1971 {
1972 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
1973 return false;
1974 }
1975 hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
1976 if (FAILED(hrc))
1977 {
1978 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
1979 return VERR_INTERNAL_ERROR;
1980 }
1981
1982 RTHTTP hHttp;
1983 int vrc = RTHttpCreate(&hHttp);
1984 if (RT_FAILURE(vrc))
1985 {
1986 LogRel(("CLOUD-NET: Failed to create HTTP context (vrc=%Rrc)\n", vrc));
1987 return vrc;
1988 }
1989
1990 char *pszProxyType = NULL;
1991
1992 if (enmProxyMode == ProxyMode_Manual)
1993 {
1994 /*
1995 * Unfortunately we cannot simply call RTHttpSetProxyByUrl because it never
1996 * exposes proxy settings. Calling RTHttpQueryProxyInfoForUrl afterward
1997 * won't help either as it uses system-wide proxy settings instead of
1998 * parameters we would have set with RTHttpSetProxyByUrl. Hence we parse
1999 * proxy URL ourselves here.
2000 */
2001 Bstr proxyUrl;
2002 hrc = systemProperties->COMGETTER(ProxyURL)(proxyUrl.asOutParam());
2003 if (FAILED(hrc))
2004 {
2005 LogRel(("CLOUD-NET: Failed to obtain proxy URL. hrc=%x\n", hrc));
2006 return false;
2007 }
2008 Utf8Str strProxyUrl = proxyUrl;
2009 if (!strProxyUrl.contains("://"))
2010 strProxyUrl = "http://" + strProxyUrl;
2011 const char *pcszProxyUrl = strProxyUrl.c_str();
2012 RTURIPARSED Parsed;
2013 vrc = RTUriParse(pcszProxyUrl, &Parsed);
2014 if (RT_FAILURE(vrc))
2015 {
2016 LogRel(("CLOUD-NET: Failed to parse proxy URL: %ls (vrc=%Rrc)\n", proxyUrl.raw(), vrc));
2017 return false;
2018 }
2019
2020 pszProxyType = RTUriParsedScheme(pcszProxyUrl, &Parsed);
2021 if (!pszProxyType)
2022 {
2023 LogRel(("CLOUD-NET: Failed to get proxy scheme from proxy URL: %s\n", pcszProxyUrl));
2024 return false;
2025 }
2026 RTStrToUpper(pszProxyType);
2027
2028 ProxyInfo.pszProxyHost = RTUriParsedAuthorityHost(pcszProxyUrl, &Parsed);
2029 if (!ProxyInfo.pszProxyHost)
2030 {
2031 LogRel(("CLOUD-NET: Failed to get proxy host name from proxy URL: %s\n", pcszProxyUrl));
2032 return false;
2033 }
2034 ProxyInfo.uProxyPort = RTUriParsedAuthorityPort(pcszProxyUrl, &Parsed);
2035 if (ProxyInfo.uProxyPort == UINT32_MAX)
2036 {
2037 LogRel(("CLOUD-NET: Failed to get proxy port from proxy URL: %s\n", pcszProxyUrl));
2038 return false;
2039 }
2040 ProxyInfo.pszProxyUsername = RTUriParsedAuthorityUsername(pcszProxyUrl, &Parsed);
2041 ProxyInfo.pszProxyPassword = RTUriParsedAuthorityPassword(pcszProxyUrl, &Parsed);
2042 }
2043 else if (enmProxyMode == ProxyMode_System)
2044 {
2045 vrc = RTHttpUseSystemProxySettings(hHttp);
2046 if (RT_FAILURE(vrc))
2047 {
2048 LogRel(("%s: RTHttpUseSystemProxySettings() failed: %Rrc", __FUNCTION__, vrc));
2049 RTHttpDestroy(hHttp);
2050 return vrc;
2051 }
2052 vrc = RTHttpQueryProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &ProxyInfo);
2053 RTHttpDestroy(hHttp);
2054 if (RT_FAILURE(vrc))
2055 {
2056 LogRel(("CLOUD-NET: Failed to get proxy for %s (vrc=%Rrc)\n", strIpAddr.c_str(), vrc));
2057 return vrc;
2058 }
2059
2060 switch (ProxyInfo.enmProxyType)
2061 {
2062 case RTHTTPPROXYTYPE_NOPROXY:
2063 /* Nothing to do */
2064 return VINF_SUCCESS;
2065 case RTHTTPPROXYTYPE_HTTP:
2066 pszProxyType = RTStrDup("HTTP");
2067 break;
2068 case RTHTTPPROXYTYPE_HTTPS:
2069 case RTHTTPPROXYTYPE_SOCKS4:
2070 case RTHTTPPROXYTYPE_SOCKS5:
2071 /* break; -- Fall through until support is implemented */
2072 case RTHTTPPROXYTYPE_UNKNOWN:
2073 case RTHTTPPROXYTYPE_INVALID:
2074 case RTHTTPPROXYTYPE_END:
2075 case RTHTTPPROXYTYPE_32BIT_HACK:
2076 LogRel(("CLOUD-NET: Unsupported proxy type %u\n", ProxyInfo.enmProxyType));
2077 RTHttpFreeProxyInfo(&ProxyInfo);
2078 return VERR_INVALID_PARAMETER;
2079 }
2080 }
2081 else
2082 {
2083 Assert(enmProxyMode == ProxyMode_NoProxy);
2084 return VINF_SUCCESS;
2085 }
2086
2087 /* Resolve proxy host name to IP address if necessary */
2088 RTNETADDR addr;
2089 RTSocketParseInetAddress(ProxyInfo.pszProxyHost, ProxyInfo.uProxyPort, &addr);
2090 if (addr.enmType != RTNETADDRTYPE_IPV4)
2091 {
2092 LogRel(("CLOUD-NET: Unsupported address type %u\n", addr.enmType));
2093 RTHttpFreeProxyInfo(&ProxyInfo);
2094 return VERR_INVALID_PARAMETER;
2095 }
2096
2097 InsertConfigString( pCfg, Utf8StrFmt("%sProxyType", pcszPrefix).c_str(), pszProxyType);
2098 InsertConfigInteger( pCfg, Utf8StrFmt("%sProxyPort", pcszPrefix).c_str(), ProxyInfo.uProxyPort);
2099 if (ProxyInfo.pszProxyHost)
2100 InsertConfigStringF( pCfg, Utf8StrFmt("%sProxyHost", pcszPrefix).c_str(), "%RTnaipv4", addr.uAddr.IPv4);
2101 if (ProxyInfo.pszProxyUsername)
2102 InsertConfigString( pCfg, Utf8StrFmt("%sProxyUser", pcszPrefix).c_str(), ProxyInfo.pszProxyUsername);
2103 if (ProxyInfo.pszProxyPassword)
2104 InsertConfigPassword(pCfg, Utf8StrFmt("%sProxyPassword", pcszPrefix).c_str(), ProxyInfo.pszProxyPassword);
2105
2106 RTHttpFreeProxyInfo(&ProxyInfo);
2107 RTStrFree(pszProxyType);
2108 return vrc;
2109}
2110
2111
2112/**
2113 * Construct the Network configuration tree
2114 *
2115 * @returns VBox status code.
2116 *
2117 * @param pszDevice The PDM device name.
2118 * @param uInstance The PDM device instance.
2119 * @param uLun The PDM LUN number of the drive.
2120 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
2121 * @param pCfg Configuration node for the device
2122 * @param pLunL0 To store the pointer to the LUN#0.
2123 * @param pInst The instance CFGM node
2124 * @param fAttachDetach To determine if the network attachment should
2125 * be attached/detached after/before
2126 * configuration.
2127 * @param fIgnoreConnectFailure
2128 * True if connection failures should be ignored
2129 * (makes only sense for bridged/host-only networks).
2130 * @param pUVM The usermode VM handle.
2131 * @param pVMM The VMM vtable.
2132 *
2133 * @note Locks this object for writing.
2134 * @thread EMT
2135 */
2136int Console::i_configNetwork(const char *pszDevice,
2137 unsigned uInstance,
2138 unsigned uLun,
2139 INetworkAdapter *aNetworkAdapter,
2140 PCFGMNODE pCfg,
2141 PCFGMNODE pLunL0,
2142 PCFGMNODE pInst,
2143 bool fAttachDetach,
2144 bool fIgnoreConnectFailure,
2145 PUVM pUVM,
2146 PCVMMR3VTABLE pVMM)
2147{
2148 RT_NOREF(fIgnoreConnectFailure);
2149 AutoCaller autoCaller(this);
2150 AssertComRCReturn(autoCaller.hrc(), VERR_ACCESS_DENIED);
2151
2152 // InsertConfig* throws
2153 try
2154 {
2155 int vrc = VINF_SUCCESS;
2156 HRESULT hrc;
2157 Bstr bstr;
2158
2159#ifdef VBOX_WITH_CLOUD_NET
2160 /* We'll need device's pCfg for cloud attachments */
2161 PCFGMNODE pDevCfg = pCfg;
2162#endif /* VBOX_WITH_CLOUD_NET */
2163
2164#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
2165
2166 /*
2167 * Locking the object before doing VMR3* calls is quite safe here, since
2168 * we're on EMT. Write lock is necessary because we indirectly modify the
2169 * meAttachmentType member.
2170 */
2171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 ComPtr<IMachine> pMachine = i_machine();
2174
2175 ComPtr<IVirtualBox> virtualBox;
2176 hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H();
2177
2178 ComPtr<IHost> host;
2179 hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H();
2180
2181 BOOL fSniffer;
2182 hrc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fSniffer); H();
2183
2184 NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
2185 hrc = aNetworkAdapter->COMGETTER(PromiscModePolicy)(&enmPromiscModePolicy); H();
2186 const char *pszPromiscuousGuestPolicy;
2187 switch (enmPromiscModePolicy)
2188 {
2189 case NetworkAdapterPromiscModePolicy_Deny: pszPromiscuousGuestPolicy = "deny"; break;
2190 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = "allow-network"; break;
2191 case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = "allow-all"; break;
2192 default: AssertFailedReturn(VERR_INTERNAL_ERROR_4);
2193 }
2194
2195 if (fAttachDetach)
2196 {
2197 vrc = pVMM->pfnPDMR3DeviceDetach(pUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/);
2198 if (vrc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)
2199 vrc = VINF_SUCCESS;
2200 AssertLogRelRCReturn(vrc, vrc);
2201
2202 /* Nuke anything which might have been left behind. */
2203 pVMM->pfnCFGMR3RemoveNode(pVMM->pfnCFGMR3GetChildF(pInst, "LUN#%u", uLun));
2204 }
2205
2206 Bstr networkName, trunkName, trunkType;
2207 NetworkAttachmentType_T eAttachmentType;
2208 hrc = aNetworkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H();
2209
2210#ifdef VBOX_WITH_NETSHAPER
2211 ComObjPtr<IBandwidthGroup> pBwGroup;
2212 Bstr bstrBwGroup;
2213 hrc = aNetworkAdapter->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H();
2214
2215 if (!pBwGroup.isNull())
2216 {
2217 hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H();
2218 }
2219#endif /* VBOX_WITH_NETSHAPER */
2220
2221 AssertMsg(uLun == 0, ("Network attachments with LUN > 0 are not supported yet\n"));
2222 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", uLun);
2223
2224 /*
2225 * Do not insert neither a shaper nor a sniffer if we are not attached to anything.
2226 * This way we can easily detect if we are attached to anything at the device level.
2227 */
2228#ifdef VBOX_WITH_NETSHAPER
2229 if (bstrBwGroup.isNotEmpty() && eAttachmentType != NetworkAttachmentType_Null)
2230 {
2231 InsertConfigString(pLunL0, "Driver", "NetShaper");
2232 InsertConfigNode(pLunL0, "Config", &pCfg);
2233 InsertConfigString(pCfg, "BwGroup", bstrBwGroup);
2234 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
2235 }
2236#endif /* VBOX_WITH_NETSHAPER */
2237
2238 if (fSniffer && eAttachmentType != NetworkAttachmentType_Null)
2239 {
2240 InsertConfigString(pLunL0, "Driver", "NetSniffer");
2241 InsertConfigNode(pLunL0, "Config", &pCfg);
2242 hrc = aNetworkAdapter->COMGETTER(TraceFile)(bstr.asOutParam()); H();
2243 if (!bstr.isEmpty()) /* check convention for indicating default file. */
2244 InsertConfigString(pCfg, "File", bstr);
2245 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
2246 }
2247
2248 switch (eAttachmentType)
2249 {
2250 case NetworkAttachmentType_Null:
2251 break;
2252
2253 case NetworkAttachmentType_NAT:
2254 {
2255 ComPtr<INATEngine> natEngine;
2256 hrc = aNetworkAdapter->COMGETTER(NATEngine)(natEngine.asOutParam()); H();
2257 InsertConfigString(pLunL0, "Driver", "NAT");
2258 InsertConfigNode(pLunL0, "Config", &pCfg);
2259
2260 /* Configure TFTP prefix and boot filename. */
2261 hrc = virtualBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H();
2262 if (!bstr.isEmpty())
2263 InsertConfigStringF(pCfg, "TFTPPrefix", "%ls%c%s", bstr.raw(), RTPATH_DELIMITER, "TFTP");
2264 hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H();
2265 InsertConfigStringF(pCfg, "BootFile", "%ls.pxe", bstr.raw());
2266
2267 hrc = natEngine->COMGETTER(Network)(bstr.asOutParam()); H();
2268 if (!bstr.isEmpty())
2269 InsertConfigString(pCfg, "Network", bstr);
2270 else
2271 {
2272 ULONG uSlot;
2273 hrc = aNetworkAdapter->COMGETTER(Slot)(&uSlot); H();
2274 InsertConfigStringF(pCfg, "Network", "10.0.%d.0/24", uSlot+2);
2275 }
2276 hrc = natEngine->COMGETTER(HostIP)(bstr.asOutParam()); H();
2277 if (!bstr.isEmpty())
2278 InsertConfigString(pCfg, "BindIP", bstr);
2279 ULONG mtu = 0;
2280 ULONG sockSnd = 0;
2281 ULONG sockRcv = 0;
2282 ULONG tcpSnd = 0;
2283 ULONG tcpRcv = 0;
2284 hrc = natEngine->GetNetworkSettings(&mtu, &sockSnd, &sockRcv, &tcpSnd, &tcpRcv); H();
2285 if (mtu)
2286 InsertConfigInteger(pCfg, "SlirpMTU", mtu);
2287 if (sockRcv)
2288 InsertConfigInteger(pCfg, "SockRcv", sockRcv);
2289 if (sockSnd)
2290 InsertConfigInteger(pCfg, "SockSnd", sockSnd);
2291 if (tcpRcv)
2292 InsertConfigInteger(pCfg, "TcpRcv", tcpRcv);
2293 if (tcpSnd)
2294 InsertConfigInteger(pCfg, "TcpSnd", tcpSnd);
2295 hrc = natEngine->COMGETTER(TFTPPrefix)(bstr.asOutParam()); H();
2296 if (!bstr.isEmpty())
2297 {
2298 RemoveConfigValue(pCfg, "TFTPPrefix");
2299 InsertConfigString(pCfg, "TFTPPrefix", bstr);
2300 }
2301 hrc = natEngine->COMGETTER(TFTPBootFile)(bstr.asOutParam()); H();
2302 if (!bstr.isEmpty())
2303 {
2304 RemoveConfigValue(pCfg, "BootFile");
2305 InsertConfigString(pCfg, "BootFile", bstr);
2306 }
2307 hrc = natEngine->COMGETTER(TFTPNextServer)(bstr.asOutParam()); H();
2308 if (!bstr.isEmpty())
2309 InsertConfigString(pCfg, "NextServer", bstr);
2310 BOOL fDNSFlag;
2311 hrc = natEngine->COMGETTER(DNSPassDomain)(&fDNSFlag); H();
2312 InsertConfigInteger(pCfg, "PassDomain", fDNSFlag);
2313 hrc = natEngine->COMGETTER(DNSProxy)(&fDNSFlag); H();
2314 InsertConfigInteger(pCfg, "DNSProxy", fDNSFlag);
2315 hrc = natEngine->COMGETTER(DNSUseHostResolver)(&fDNSFlag); H();
2316 InsertConfigInteger(pCfg, "UseHostResolver", fDNSFlag);
2317
2318 ULONG aliasMode;
2319 hrc = natEngine->COMGETTER(AliasMode)(&aliasMode); H();
2320 InsertConfigInteger(pCfg, "AliasMode", aliasMode);
2321
2322 BOOL fLocalhostReachable;
2323 hrc = natEngine->COMGETTER(LocalhostReachable)(&fLocalhostReachable); H();
2324 InsertConfigInteger(pCfg, "LocalhostReachable", fLocalhostReachable);
2325
2326 /* port-forwarding */
2327 SafeArray<BSTR> pfs;
2328 hrc = natEngine->COMGETTER(Redirects)(ComSafeArrayAsOutParam(pfs)); H();
2329
2330 PCFGMNODE pPFTree = NULL;
2331 if (pfs.size() > 0)
2332 InsertConfigNode(pCfg, "PortForwarding", &pPFTree);
2333
2334 for (unsigned int i = 0; i < pfs.size(); ++i)
2335 {
2336 PCFGMNODE pPF = NULL; /* /Devices/Dev/.../Config/PortForwarding/$n/ */
2337
2338 uint16_t port = 0;
2339 Utf8Str utf = pfs[i];
2340 Utf8Str strName;
2341 Utf8Str strProto;
2342 Utf8Str strHostPort;
2343 Utf8Str strHostIP;
2344 Utf8Str strGuestPort;
2345 Utf8Str strGuestIP;
2346 size_t pos, ppos;
2347 pos = ppos = 0;
2348#define ITERATE_TO_NEXT_TERM(res, str, pos, ppos) \
2349 { \
2350 pos = str.find(",", ppos); \
2351 if (pos == Utf8Str::npos) \
2352 { \
2353 Log(( #res " extracting from %s is failed\n", str.c_str())); \
2354 continue; \
2355 } \
2356 res = str.substr(ppos, pos - ppos); \
2357 Log2((#res " %s pos:%d, ppos:%d\n", res.c_str(), pos, ppos)); \
2358 ppos = pos + 1; \
2359 } /* no do { ... } while because of 'continue' */
2360 ITERATE_TO_NEXT_TERM(strName, utf, pos, ppos);
2361 ITERATE_TO_NEXT_TERM(strProto, utf, pos, ppos);
2362 ITERATE_TO_NEXT_TERM(strHostIP, utf, pos, ppos);
2363 ITERATE_TO_NEXT_TERM(strHostPort, utf, pos, ppos);
2364 ITERATE_TO_NEXT_TERM(strGuestIP, utf, pos, ppos);
2365 strGuestPort = utf.substr(ppos, utf.length() - ppos);
2366#undef ITERATE_TO_NEXT_TERM
2367
2368 uint32_t proto = strProto.toUInt32();
2369 bool fValid = true;
2370 switch (proto)
2371 {
2372 case NATProtocol_UDP:
2373 strProto = "UDP";
2374 break;
2375 case NATProtocol_TCP:
2376 strProto = "TCP";
2377 break;
2378 default:
2379 fValid = false;
2380 }
2381 /* continue with next rule if no valid proto was passed */
2382 if (!fValid)
2383 continue;
2384
2385 InsertConfigNodeF(pPFTree, &pPF, "%u", i);
2386
2387 if (!strName.isEmpty())
2388 InsertConfigString(pPF, "Name", strName);
2389
2390 InsertConfigString(pPF, "Protocol", strProto);
2391
2392 if (!strHostIP.isEmpty())
2393 InsertConfigString(pPF, "BindIP", strHostIP);
2394
2395 if (!strGuestIP.isEmpty())
2396 InsertConfigString(pPF, "GuestIP", strGuestIP);
2397
2398 port = RTStrToUInt16(strHostPort.c_str());
2399 if (port)
2400 InsertConfigInteger(pPF, "HostPort", port);
2401
2402 port = RTStrToUInt16(strGuestPort.c_str());
2403 if (port)
2404 InsertConfigInteger(pPF, "GuestPort", port);
2405 }
2406 break;
2407 }
2408
2409 case NetworkAttachmentType_Bridged:
2410 {
2411#if (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT)
2412 hrc = i_attachToTapInterface(aNetworkAdapter);
2413 if (FAILED(hrc))
2414 {
2415 switch (hrc)
2416 {
2417 case E_ACCESSDENIED:
2418 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
2419 "Failed to open '/dev/net/tun' for read/write access. Please check the "
2420 "permissions of that node. Either run 'chmod 0666 /dev/net/tun' or "
2421 "change the group of that node and make yourself a member of that group. "
2422 "Make sure that these changes are permanent, especially if you are "
2423 "using udev"));
2424 default:
2425 AssertMsgFailed(("Could not attach to host interface! Bad!\n"));
2426 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
2427 N_("Failed to initialize Host Interface Networking"));
2428 }
2429 }
2430
2431 Assert((intptr_t)maTapFD[uInstance] >= 0);
2432 if ((intptr_t)maTapFD[uInstance] >= 0)
2433 {
2434 InsertConfigString(pLunL0, "Driver", "HostInterface");
2435 InsertConfigNode(pLunL0, "Config", &pCfg);
2436 InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]);
2437 }
2438
2439#elif defined(VBOX_WITH_NETFLT)
2440 /*
2441 * This is the new VBoxNetFlt+IntNet stuff.
2442 */
2443 Bstr BridgedIfName;
2444 hrc = aNetworkAdapter->COMGETTER(BridgedInterface)(BridgedIfName.asOutParam());
2445 if (FAILED(hrc))
2446 {
2447 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(BridgedInterface) failed, hrc (0x%x)\n", hrc));
2448 H();
2449 }
2450
2451 Utf8Str BridgedIfNameUtf8(BridgedIfName);
2452 const char *pszBridgedIfName = BridgedIfNameUtf8.c_str();
2453
2454 ComPtr<IHostNetworkInterface> hostInterface;
2455 hrc = host->FindHostNetworkInterfaceByName(BridgedIfName.raw(),
2456 hostInterface.asOutParam());
2457 if (!SUCCEEDED(hrc))
2458 {
2459 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: FindByName failed, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2460 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2461 N_("Nonexistent host networking interface, name '%ls'"),
2462 BridgedIfName.raw());
2463 }
2464
2465# if defined(RT_OS_DARWIN)
2466 /* The name is in the format 'ifX: long name', chop it off at the colon. */
2467 char szTrunk[INTNET_MAX_TRUNK_NAME];
2468 RTStrCopy(szTrunk, sizeof(szTrunk), pszBridgedIfName);
2469 char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk));
2470// Quick fix for @bugref{5633}
2471// if (!pszColon)
2472// {
2473// /*
2474// * Dynamic changing of attachment causes an attempt to configure
2475// * network with invalid host adapter (as it is must be changed before
2476// * the attachment), calling Detach here will cause a deadlock.
2477// * See @bugref{4750}.
2478// * hrc = aNetworkAdapter->Detach(); H();
2479// */
2480// return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
2481// N_("Malformed host interface networking name '%ls'"),
2482// BridgedIfName.raw());
2483// }
2484 if (pszColon)
2485 *pszColon = '\0';
2486 const char *pszTrunk = szTrunk;
2487
2488# elif defined(RT_OS_SOLARIS)
2489 /* The name is in the format 'ifX[:1] - long name, chop it off at space. */
2490 char szTrunk[256];
2491 strlcpy(szTrunk, pszBridgedIfName, sizeof(szTrunk));
2492 char *pszSpace = (char *)memchr(szTrunk, ' ', sizeof(szTrunk));
2493
2494 /*
2495 * Currently don't bother about malformed names here for the sake of people using
2496 * VBoxManage and setting only the NIC name from there. If there is a space we
2497 * chop it off and proceed, otherwise just use whatever we've got.
2498 */
2499 if (pszSpace)
2500 *pszSpace = '\0';
2501
2502 /* Chop it off at the colon (zone naming eg: e1000g:1 we need only the e1000g) */
2503 char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk));
2504 if (pszColon)
2505 *pszColon = '\0';
2506
2507 const char *pszTrunk = szTrunk;
2508
2509# elif defined(RT_OS_WINDOWS)
2510 HostNetworkInterfaceType_T eIfType;
2511 hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
2512 if (FAILED(hrc))
2513 {
2514 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
2515 H();
2516 }
2517
2518 if (eIfType != HostNetworkInterfaceType_Bridged)
2519 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2520 N_("Interface ('%ls') is not a Bridged Adapter interface"),
2521 BridgedIfName.raw());
2522
2523 hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
2524 if (FAILED(hrc))
2525 {
2526 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
2527 H();
2528 }
2529 Guid hostIFGuid(bstr);
2530
2531 INetCfg *pNc;
2532 ComPtr<INetCfgComponent> pAdaptorComponent;
2533 LPWSTR pszApp;
2534
2535 hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp);
2536 Assert(hrc == S_OK);
2537 if (hrc != S_OK)
2538 {
2539 LogRel(("NetworkAttachmentType_Bridged: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2540 H();
2541 }
2542
2543 /* get the adapter's INetCfgComponent*/
2544 hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(),
2545 pAdaptorComponent.asOutParam());
2546 if (hrc != S_OK)
2547 {
2548 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2549 LogRel(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)\n", hrc));
2550 H();
2551 }
2552# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
2553 char szTrunkName[INTNET_MAX_TRUNK_NAME];
2554 char *pszTrunkName = szTrunkName;
2555 wchar_t * pswzBindName;
2556 hrc = pAdaptorComponent->GetBindName(&pswzBindName);
2557 Assert(hrc == S_OK);
2558 if (hrc == S_OK)
2559 {
2560 int cwBindName = (int)wcslen(pswzBindName) + 1;
2561 int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX);
2562 if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName)
2563 {
2564 strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX);
2565 pszTrunkName += cbFullBindNamePrefix-1;
2566 if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName,
2567 sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL))
2568 {
2569 DWORD err = GetLastError();
2570 hrc = HRESULT_FROM_WIN32(err);
2571 AssertMsgFailed(("hrc=%Rhrc %#x\n", hrc, hrc));
2572 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n",
2573 hrc, hrc, err));
2574 }
2575 }
2576 else
2577 {
2578 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: insufficient szTrunkName buffer space\n"));
2579 /** @todo set appropriate error code */
2580 hrc = E_FAIL;
2581 }
2582
2583 if (hrc != S_OK)
2584 {
2585 AssertFailed();
2586 CoTaskMemFree(pswzBindName);
2587 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2588 H();
2589 }
2590
2591 /* we're not freeing the bind name since we'll use it later for detecting wireless*/
2592 }
2593 else
2594 {
2595 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2596 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)",
2597 hrc));
2598 H();
2599 }
2600
2601 const char *pszTrunk = szTrunkName;
2602 /* we're not releasing the INetCfg stuff here since we use it later to figure out whether it is wireless */
2603
2604# elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
2605# if defined(RT_OS_FREEBSD)
2606 /*
2607 * If we bridge to a tap interface open it the `old' direct way.
2608 * This works and performs better than bridging a physical
2609 * interface via the current FreeBSD vboxnetflt implementation.
2610 */
2611 if (!strncmp(pszBridgedIfName, RT_STR_TUPLE("tap"))) {
2612 hrc = i_attachToTapInterface(aNetworkAdapter);
2613 if (FAILED(hrc))
2614 {
2615 switch (hrc)
2616 {
2617 case E_ACCESSDENIED:
2618 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
2619 "Failed to open '/dev/%s' for read/write access. Please check the "
2620 "permissions of that node, and that the net.link.tap.user_open "
2621 "sysctl is set. Either run 'chmod 0666 /dev/%s' or change the "
2622 "group of that node to vboxusers and make yourself a member of "
2623 "that group. Make sure that these changes are permanent."),
2624 pszBridgedIfName, pszBridgedIfName);
2625 default:
2626 AssertMsgFailed(("Could not attach to tap interface! Bad!\n"));
2627 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
2628 N_("Failed to initialize Host Interface Networking"));
2629 }
2630 }
2631
2632 Assert((intptr_t)maTapFD[uInstance] >= 0);
2633 if ((intptr_t)maTapFD[uInstance] >= 0)
2634 {
2635 InsertConfigString(pLunL0, "Driver", "HostInterface");
2636 InsertConfigNode(pLunL0, "Config", &pCfg);
2637 InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]);
2638 }
2639 break;
2640 }
2641# endif
2642 /** @todo Check for malformed names. */
2643 const char *pszTrunk = pszBridgedIfName;
2644
2645 /* Issue a warning if the interface is down */
2646 {
2647 int iSock = socket(AF_INET, SOCK_DGRAM, 0);
2648 if (iSock >= 0)
2649 {
2650 struct ifreq Req;
2651 RT_ZERO(Req);
2652 RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszBridgedIfName);
2653 if (ioctl(iSock, SIOCGIFFLAGS, &Req) >= 0)
2654 if ((Req.ifr_flags & IFF_UP) == 0)
2655 i_atVMRuntimeErrorCallbackF(0, "BridgedInterfaceDown",
2656 N_("Bridged interface %s is down. Guest will not be able to use this interface"),
2657 pszBridgedIfName);
2658
2659 close(iSock);
2660 }
2661 }
2662
2663# else
2664# error "PORTME (VBOX_WITH_NETFLT)"
2665# endif
2666
2667# if defined(RT_OS_DARWIN) && defined(VBOX_WITH_VMNET)
2668 InsertConfigString(pLunL0, "Driver", "VMNet");
2669 InsertConfigNode(pLunL0, "Config", &pCfg);
2670 InsertConfigString(pCfg, "Trunk", pszTrunk);
2671 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
2672# else
2673 InsertConfigString(pLunL0, "Driver", "IntNet");
2674 InsertConfigNode(pLunL0, "Config", &pCfg);
2675 InsertConfigString(pCfg, "Trunk", pszTrunk);
2676 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
2677 InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure);
2678 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
2679 char szNetwork[INTNET_MAX_NETWORK_NAME];
2680
2681# if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
2682 /*
2683 * 'pszTrunk' contains just the interface name required in ring-0, while 'pszBridgedIfName' contains
2684 * interface name + optional description. We must not pass any description to the VM as it can differ
2685 * for the same interface name, eg: "nge0 - ethernet" (GUI) vs "nge0" (VBoxManage).
2686 */
2687 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszTrunk);
2688# else
2689 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszBridgedIfName);
2690# endif
2691 InsertConfigString(pCfg, "Network", szNetwork);
2692 networkName = Bstr(szNetwork);
2693 trunkName = Bstr(pszTrunk);
2694 trunkType = Bstr(TRUNKTYPE_NETFLT);
2695
2696 BOOL fSharedMacOnWire = false;
2697 hrc = hostInterface->COMGETTER(Wireless)(&fSharedMacOnWire);
2698 if (FAILED(hrc))
2699 {
2700 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Wireless) failed, hrc (0x%x)\n", hrc));
2701 H();
2702 }
2703 else if (fSharedMacOnWire)
2704 {
2705 InsertConfigInteger(pCfg, "SharedMacOnWire", true);
2706 Log(("Set SharedMacOnWire\n"));
2707 }
2708
2709# if defined(RT_OS_SOLARIS)
2710# if 0 /* bird: this is a bit questionable and might cause more trouble than its worth. */
2711 /* Zone access restriction, don't allow snooping the global zone. */
2712 zoneid_t ZoneId = getzoneid();
2713 if (ZoneId != GLOBAL_ZONEID)
2714 {
2715 InsertConfigInteger(pCfg, "IgnoreAllPromisc", true);
2716 }
2717# endif
2718# endif
2719# endif
2720
2721#elif defined(RT_OS_WINDOWS) /* not defined NetFlt */
2722 /* NOTHING TO DO HERE */
2723#elif defined(RT_OS_LINUX)
2724/// @todo aleksey: is there anything to be done here?
2725#elif defined(RT_OS_FREEBSD)
2726/** @todo FreeBSD: Check out this later (HIF networking). */
2727#else
2728# error "Port me"
2729#endif
2730 break;
2731 }
2732
2733 case NetworkAttachmentType_Internal:
2734 {
2735 hrc = aNetworkAdapter->COMGETTER(InternalNetwork)(bstr.asOutParam()); H();
2736 if (!bstr.isEmpty())
2737 {
2738 InsertConfigString(pLunL0, "Driver", "IntNet");
2739 InsertConfigNode(pLunL0, "Config", &pCfg);
2740 InsertConfigString(pCfg, "Network", bstr);
2741 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
2742 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
2743 networkName = bstr;
2744 trunkType = Bstr(TRUNKTYPE_WHATEVER);
2745 }
2746 break;
2747 }
2748
2749 case NetworkAttachmentType_HostOnly:
2750 {
2751 InsertConfigString(pLunL0, "Driver", "IntNet");
2752 InsertConfigNode(pLunL0, "Config", &pCfg);
2753
2754 Bstr HostOnlyName;
2755 hrc = aNetworkAdapter->COMGETTER(HostOnlyInterface)(HostOnlyName.asOutParam());
2756 if (FAILED(hrc))
2757 {
2758 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(HostOnlyInterface) failed, hrc (0x%x)\n", hrc));
2759 H();
2760 }
2761
2762 Utf8Str HostOnlyNameUtf8(HostOnlyName);
2763 const char *pszHostOnlyName = HostOnlyNameUtf8.c_str();
2764#ifdef VBOX_WITH_VMNET
2765 /* Check if the matching host-only network has already been created. */
2766 Bstr bstrLowerIP, bstrUpperIP, bstrNetworkMask;
2767 BstrFmt bstrNetworkName("Legacy %s Network", pszHostOnlyName);
2768 ComPtr<IHostOnlyNetwork> hostOnlyNetwork;
2769 hrc = virtualBox->FindHostOnlyNetworkByName(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam());
2770 if (FAILED(hrc))
2771 {
2772 /*
2773 * With VMNET there is no VBoxNetAdp to create vboxnetX adapters,
2774 * which means that the Host object won't be able to re-create
2775 * them from extra data. Go through existing DHCP/adapter config
2776 * to derive the parameters for the new network.
2777 */
2778 BstrFmt bstrOldNetworkName("HostInterfaceNetworking-%s", pszHostOnlyName);
2779 ComPtr<IDHCPServer> dhcpServer;
2780 hrc = virtualBox->FindDHCPServerByNetworkName(bstrOldNetworkName.raw(),
2781 dhcpServer.asOutParam());
2782 if (SUCCEEDED(hrc))
2783 {
2784 /* There is a DHCP server available for this network. */
2785 hrc = dhcpServer->COMGETTER(LowerIP)(bstrLowerIP.asOutParam());
2786 if (FAILED(hrc))
2787 {
2788 LogRel(("Console::i_configNetwork: COMGETTER(LowerIP) failed, hrc (%Rhrc)\n", hrc));
2789 H();
2790 }
2791 hrc = dhcpServer->COMGETTER(UpperIP)(bstrUpperIP.asOutParam());
2792 if (FAILED(hrc))
2793 {
2794 LogRel(("Console::i_configNetwork: COMGETTER(UpperIP) failed, hrc (%Rhrc)\n", hrc));
2795 H();
2796 }
2797 hrc = dhcpServer->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam());
2798 if (FAILED(hrc))
2799 {
2800 LogRel(("Console::i_configNetwork: COMGETTER(NetworkMask) failed, hrc (%Rhrc)\n", hrc));
2801 H();
2802 }
2803 }
2804 else
2805 {
2806 /* No DHCP server for this hostonly interface, let's look at extra data */
2807 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress",
2808 pszHostOnlyName).raw(),
2809 bstrLowerIP.asOutParam());
2810 if (SUCCEEDED(hrc) && !bstrLowerIP.isEmpty())
2811 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask",
2812 pszHostOnlyName).raw(),
2813 bstrNetworkMask.asOutParam());
2814
2815 }
2816 RTNETADDRIPV4 ipAddr, ipMask;
2817 vrc = bstrLowerIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr);
2818 if (RT_FAILURE(vrc))
2819 {
2820 /* We failed to locate any valid config of this vboxnetX interface, assume defaults. */
2821 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing lower IP '%ls', using '%ls' instead.\n",
2822 bstrLowerIP.raw(), getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw()));
2823 bstrLowerIP = getDefaultIPv4Address(Bstr(pszHostOnlyName));
2824 bstrNetworkMask.setNull();
2825 bstrUpperIP.setNull();
2826 vrc = RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr);
2827 AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("RTNetStrToIPv4Addr(%ls) failed, vrc=%Rrc\n", bstrLowerIP.raw(), vrc),
2828 VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
2829 }
2830 vrc = bstrNetworkMask.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask);
2831 if (RT_FAILURE(vrc))
2832 {
2833 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing network mask '%ls', using '%s' instead.\n",
2834 bstrNetworkMask.raw(), VBOXNET_IPV4MASK_DEFAULT));
2835 bstrNetworkMask = VBOXNET_IPV4MASK_DEFAULT;
2836 vrc = RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask);
2837 AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("RTNetStrToIPv4Addr(%ls) failed, vrc=%Rrc\n", bstrNetworkMask.raw(), vrc),
2838 VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
2839 }
2840 vrc = bstrUpperIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrUpperIP).c_str(), &ipAddr);
2841 if (RT_FAILURE(vrc))
2842 {
2843 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? */
2844 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing upper IP '%ls', using '%RTnaipv4' instead.\n",
2845 bstrUpperIP.raw(), ipAddr));
2846 bstrUpperIP = BstrFmt("%RTnaipv4", ipAddr);
2847 }
2848
2849 /* All parameters are set, create the new network. */
2850 hrc = virtualBox->CreateHostOnlyNetwork(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam());
2851 if (FAILED(hrc))
2852 {
2853 LogRel(("NetworkAttachmentType_HostOnly: failed to create host-only network, hrc (0x%x)\n", hrc));
2854 H();
2855 }
2856 hrc = hostOnlyNetwork->COMSETTER(NetworkMask)(bstrNetworkMask.raw());
2857 if (FAILED(hrc))
2858 {
2859 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(NetworkMask) failed, hrc (0x%x)\n", hrc));
2860 H();
2861 }
2862 hrc = hostOnlyNetwork->COMSETTER(LowerIP)(bstrLowerIP.raw());
2863 if (FAILED(hrc))
2864 {
2865 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(LowerIP) failed, hrc (0x%x)\n", hrc));
2866 H();
2867 }
2868 hrc = hostOnlyNetwork->COMSETTER(UpperIP)(bstrUpperIP.raw());
2869 if (FAILED(hrc))
2870 {
2871 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(UpperIP) failed, hrc (0x%x)\n", hrc));
2872 H();
2873 }
2874 LogRel(("Console: created host-only network '%ls' with mask '%ls' and range '%ls'-'%ls'\n",
2875 bstrNetworkName.raw(), bstrNetworkMask.raw(), bstrLowerIP.raw(), bstrUpperIP.raw()));
2876 }
2877 else
2878 {
2879 /* The matching host-only network already exists. Tell the user to switch to it. */
2880 hrc = hostOnlyNetwork->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam());
2881 if (FAILED(hrc))
2882 {
2883 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(NetworkMask) failed, hrc (0x%x)\n", hrc));
2884 H();
2885 }
2886 hrc = hostOnlyNetwork->COMGETTER(LowerIP)(bstrLowerIP.asOutParam());
2887 if (FAILED(hrc))
2888 {
2889 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(LowerIP) failed, hrc (0x%x)\n", hrc));
2890 H();
2891 }
2892 hrc = hostOnlyNetwork->COMGETTER(UpperIP)(bstrUpperIP.asOutParam());
2893 if (FAILED(hrc))
2894 {
2895 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(UpperIP) failed, hrc (0x%x)\n", hrc));
2896 H();
2897 }
2898 }
2899 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
2900 N_("Host-only adapters are no longer supported!\n"
2901 "For your convenience a host-only network named '%ls' has been "
2902 "created with network mask '%ls' and IP address range '%ls' - '%ls'.\n"
2903 "To fix this problem, switch to 'Host-only Network' "
2904 "attachment type in the VM settings.\n"),
2905 bstrNetworkName.raw(), bstrNetworkMask.raw(),
2906 bstrLowerIP.raw(), bstrUpperIP.raw());
2907#endif /* VBOX_WITH_VMNET */
2908 ComPtr<IHostNetworkInterface> hostInterface;
2909 hrc = host->FindHostNetworkInterfaceByName(HostOnlyName.raw(),
2910 hostInterface.asOutParam());
2911 if (!SUCCEEDED(hrc))
2912 {
2913 LogRel(("NetworkAttachmentType_HostOnly: FindByName failed, vrc=%Rrc\n", vrc));
2914 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2915 N_("Nonexistent host networking interface, name '%ls'"), HostOnlyName.raw());
2916 }
2917
2918 char szNetwork[INTNET_MAX_NETWORK_NAME];
2919 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszHostOnlyName);
2920
2921#if defined(RT_OS_WINDOWS)
2922# ifndef VBOX_WITH_NETFLT
2923 hrc = E_NOTIMPL;
2924 LogRel(("NetworkAttachmentType_HostOnly: Not Implemented\n"));
2925 H();
2926# else /* defined VBOX_WITH_NETFLT*/
2927 /** @todo r=bird: Put this in a function. */
2928
2929 HostNetworkInterfaceType_T eIfType;
2930 hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
2931 if (FAILED(hrc))
2932 {
2933 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
2934 H();
2935 }
2936
2937 if (eIfType != HostNetworkInterfaceType_HostOnly)
2938 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2939 N_("Interface ('%ls') is not a Host-Only Adapter interface"),
2940 HostOnlyName.raw());
2941
2942 hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
2943 if (FAILED(hrc))
2944 {
2945 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
2946 H();
2947 }
2948 Guid hostIFGuid(bstr);
2949
2950 INetCfg *pNc;
2951 ComPtr<INetCfgComponent> pAdaptorComponent;
2952 LPWSTR pszApp;
2953 hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp);
2954 Assert(hrc == S_OK);
2955 if (hrc != S_OK)
2956 {
2957 LogRel(("NetworkAttachmentType_HostOnly: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2958 H();
2959 }
2960
2961 /* get the adapter's INetCfgComponent*/
2962 hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(),
2963 pAdaptorComponent.asOutParam());
2964 if (hrc != S_OK)
2965 {
2966 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2967 LogRel(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2968 H();
2969 }
2970# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
2971 char szTrunkName[INTNET_MAX_TRUNK_NAME];
2972 bool fNdis6 = false;
2973 wchar_t * pwszHelpText;
2974 hrc = pAdaptorComponent->GetHelpText(&pwszHelpText);
2975 Assert(hrc == S_OK);
2976 if (hrc == S_OK)
2977 {
2978 Log(("help-text=%ls\n", pwszHelpText));
2979 if (!wcscmp(pwszHelpText, L"VirtualBox NDIS 6.0 Miniport Driver"))
2980 fNdis6 = true;
2981 CoTaskMemFree(pwszHelpText);
2982 }
2983 if (fNdis6)
2984 {
2985 strncpy(szTrunkName, pszHostOnlyName, sizeof(szTrunkName) - 1);
2986 Log(("trunk=%s\n", szTrunkName));
2987 }
2988 else
2989 {
2990 char *pszTrunkName = szTrunkName;
2991 wchar_t * pswzBindName;
2992 hrc = pAdaptorComponent->GetBindName(&pswzBindName);
2993 Assert(hrc == S_OK);
2994 if (hrc == S_OK)
2995 {
2996 int cwBindName = (int)wcslen(pswzBindName) + 1;
2997 int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX);
2998 if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName)
2999 {
3000 strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX);
3001 pszTrunkName += cbFullBindNamePrefix-1;
3002 if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName,
3003 sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL))
3004 {
3005 DWORD err = GetLastError();
3006 hrc = HRESULT_FROM_WIN32(err);
3007 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n",
3008 hrc, hrc, err));
3009 }
3010 }
3011 else
3012 {
3013 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: insufficient szTrunkName buffer space\n"));
3014 /** @todo set appropriate error code */
3015 hrc = E_FAIL;
3016 }
3017
3018 if (hrc != S_OK)
3019 {
3020 AssertFailed();
3021 CoTaskMemFree(pswzBindName);
3022 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
3023 H();
3024 }
3025 }
3026 else
3027 {
3028 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
3029 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n",
3030 hrc, hrc));
3031 H();
3032 }
3033
3034
3035 CoTaskMemFree(pswzBindName);
3036 }
3037
3038 trunkType = TRUNKTYPE_NETADP;
3039 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
3040
3041 pAdaptorComponent.setNull();
3042 /* release the pNc finally */
3043 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
3044
3045 const char *pszTrunk = szTrunkName;
3046
3047 InsertConfigString(pCfg, "Trunk", pszTrunk);
3048 InsertConfigString(pCfg, "Network", szNetwork);
3049 InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure); /** @todo why is this
3050 windows only?? */
3051 networkName = Bstr(szNetwork);
3052 trunkName = Bstr(pszTrunk);
3053# endif /* defined VBOX_WITH_NETFLT*/
3054#elif defined(RT_OS_DARWIN)
3055 InsertConfigString(pCfg, "Trunk", pszHostOnlyName);
3056 InsertConfigString(pCfg, "Network", szNetwork);
3057 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
3058 networkName = Bstr(szNetwork);
3059 trunkName = Bstr(pszHostOnlyName);
3060 trunkType = TRUNKTYPE_NETADP;
3061#else
3062 InsertConfigString(pCfg, "Trunk", pszHostOnlyName);
3063 InsertConfigString(pCfg, "Network", szNetwork);
3064 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
3065 networkName = Bstr(szNetwork);
3066 trunkName = Bstr(pszHostOnlyName);
3067 trunkType = TRUNKTYPE_NETFLT;
3068#endif
3069 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
3070
3071#if !defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
3072
3073 Bstr tmpAddr, tmpMask;
3074
3075 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress",
3076 pszHostOnlyName).raw(),
3077 tmpAddr.asOutParam());
3078 if (SUCCEEDED(hrc) && !tmpAddr.isEmpty())
3079 {
3080 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask",
3081 pszHostOnlyName).raw(),
3082 tmpMask.asOutParam());
3083 if (SUCCEEDED(hrc) && !tmpMask.isEmpty())
3084 hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(),
3085 tmpMask.raw());
3086 else
3087 hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(),
3088 Bstr(VBOXNET_IPV4MASK_DEFAULT).raw());
3089 }
3090 else
3091 {
3092 /* Grab the IP number from the 'vboxnetX' instance number (see netif.h) */
3093 hrc = hostInterface->EnableStaticIPConfig(getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw(),
3094 Bstr(VBOXNET_IPV4MASK_DEFAULT).raw());
3095 }
3096
3097 ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */
3098
3099 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6Address",
3100 pszHostOnlyName).raw(),
3101 tmpAddr.asOutParam());
3102 if (SUCCEEDED(hrc))
3103 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6NetMask", pszHostOnlyName).raw(),
3104 tmpMask.asOutParam());
3105 if (SUCCEEDED(hrc) && !tmpAddr.isEmpty() && !tmpMask.isEmpty())
3106 {
3107 hrc = hostInterface->EnableStaticIPConfigV6(tmpAddr.raw(),
3108 Utf8Str(tmpMask).toUInt32());
3109 ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */
3110 }
3111#endif
3112 break;
3113 }
3114
3115 case NetworkAttachmentType_Generic:
3116 {
3117 hrc = aNetworkAdapter->COMGETTER(GenericDriver)(bstr.asOutParam()); H();
3118 SafeArray<BSTR> names;
3119 SafeArray<BSTR> values;
3120 hrc = aNetworkAdapter->GetProperties(Bstr().raw(),
3121 ComSafeArrayAsOutParam(names),
3122 ComSafeArrayAsOutParam(values)); H();
3123
3124 InsertConfigString(pLunL0, "Driver", bstr);
3125 InsertConfigNode(pLunL0, "Config", &pCfg);
3126 for (size_t ii = 0; ii < names.size(); ++ii)
3127 {
3128 if (values[ii] && *values[ii])
3129 {
3130 Utf8Str const strName(names[ii]);
3131 Utf8Str const strValue(values[ii]);
3132 InsertConfigString(pCfg, strName.c_str(), strValue);
3133 }
3134 }
3135 break;
3136 }
3137
3138 case NetworkAttachmentType_NATNetwork:
3139 {
3140 hrc = aNetworkAdapter->COMGETTER(NATNetwork)(bstr.asOutParam()); H();
3141 if (!bstr.isEmpty())
3142 {
3143 /** @todo add intnet prefix to separate namespaces, and add trunk if dealing with vboxnatX */
3144 InsertConfigString(pLunL0, "Driver", "IntNet");
3145 InsertConfigNode(pLunL0, "Config", &pCfg);
3146 InsertConfigString(pCfg, "Network", bstr);
3147 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
3148 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
3149 networkName = bstr;
3150 trunkType = Bstr(TRUNKTYPE_WHATEVER);
3151 }
3152 break;
3153 }
3154
3155#ifdef VBOX_WITH_CLOUD_NET
3156 case NetworkAttachmentType_Cloud:
3157 {
3158 static const char *s_pszCloudExtPackName = "Oracle VM VirtualBox Extension Pack";
3159 /*
3160 * Cloud network attachments do not work wihout installed extpack.
3161 * Without extpack support they won't work either.
3162 */
3163# ifdef VBOX_WITH_EXTPACK
3164 if (!mptrExtPackManager->i_isExtPackUsable(s_pszCloudExtPackName))
3165# endif
3166 {
3167 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
3168 N_("Implementation of the cloud network attachment not found!\n"
3169 "To fix this problem, either install the '%s' or switch to "
3170 "another network attachment type in the VM settings."),
3171 s_pszCloudExtPackName);
3172 }
3173
3174 ComPtr<ICloudNetwork> network;
3175 hrc = aNetworkAdapter->COMGETTER(CloudNetwork)(bstr.asOutParam()); H();
3176 hrc = pMachine->COMGETTER(Name)(mGateway.mTargetVM.asOutParam()); H();
3177 hrc = virtualBox->FindCloudNetworkByName(bstr.raw(), network.asOutParam()); H();
3178 hrc = generateKeys(mGateway);
3179 if (FAILED(hrc))
3180 {
3181 if (hrc == E_NOTIMPL)
3182 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
3183 N_("Failed to generate a key pair due to missing libssh\n"
3184 "To fix this problem, either build VirtualBox with libssh "
3185 "support or switch to another network attachment type in "
3186 "the VM settings."));
3187 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
3188 N_("Failed to generate a key pair due to libssh error!"));
3189 }
3190 hrc = startCloudGateway(virtualBox, network, mGateway);
3191 if (FAILED(hrc))
3192 {
3193 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
3194 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3195 N_("Failed to start cloud gateway instance.\nCould not find suitable "
3196 "standard cloud images. Make sure you ran 'VBoxManage cloud network setup' "
3197 "with correct '--gateway-os-name' and '--gateway-os-version' parameters. "
3198 "Check VBoxSVC.log for actual values used to look up cloud images."));
3199 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3200 N_("Failed to start cloud gateway instance.\nMake sure you set up "
3201 "cloud networking properly with 'VBoxManage cloud network setup'. "
3202 "Check VBoxSVC.log for details."));
3203 }
3204 InsertConfigBytes(pDevCfg, "MAC", &mGateway.mCloudMacAddress, sizeof(mGateway.mCloudMacAddress));
3205 if (!bstr.isEmpty())
3206 {
3207 InsertConfigString(pLunL0, "Driver", "CloudTunnel");
3208 InsertConfigNode(pLunL0, "Config", &pCfg);
3209 InsertConfigPassword(pCfg, "SshKey", mGateway.mPrivateSshKey);
3210 InsertConfigString(pCfg, "PrimaryIP", mGateway.mCloudPublicIp);
3211 InsertConfigString(pCfg, "SecondaryIP", mGateway.mCloudSecondaryPublicIp);
3212 InsertConfigBytes(pCfg, "TargetMAC", &mGateway.mLocalMacAddress, sizeof(mGateway.mLocalMacAddress));
3213 hrc = i_configProxy(virtualBox, pCfg, "Primary", mGateway.mCloudPublicIp);
3214 if (FAILED(hrc))
3215 {
3216 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3217 N_("Failed to configure proxy for accessing cloud gateway instance via primary VNIC.\n"
3218 "Check VirtualBox.log for details."));
3219 }
3220 hrc = i_configProxy(virtualBox, pCfg, "Secondary", mGateway.mCloudSecondaryPublicIp);
3221 if (FAILED(hrc))
3222 {
3223 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3224 N_("Failed to configure proxy for accessing cloud gateway instance via secondary VNIC.\n"
3225 "Check VirtualBox.log for details."));
3226 }
3227 networkName = bstr;
3228 trunkType = Bstr(TRUNKTYPE_WHATEVER);
3229 }
3230 break;
3231 }
3232#endif /* VBOX_WITH_CLOUD_NET */
3233
3234#ifdef VBOX_WITH_VMNET
3235 case NetworkAttachmentType_HostOnlyNetwork:
3236 {
3237 Bstr bstrId, bstrNetMask, bstrLowerIP, bstrUpperIP;
3238 ComPtr<IHostOnlyNetwork> network;
3239 hrc = aNetworkAdapter->COMGETTER(HostOnlyNetwork)(bstr.asOutParam()); H();
3240 hrc = virtualBox->FindHostOnlyNetworkByName(bstr.raw(), network.asOutParam());
3241 if (FAILED(hrc))
3242 {
3243 LogRel(("NetworkAttachmentType_HostOnlyNetwork: FindByName failed, hrc (0x%x)\n", hrc));
3244 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
3245 N_("Nonexistent host-only network '%ls'"), bstr.raw());
3246 }
3247 hrc = network->COMGETTER(Id)(bstrId.asOutParam()); H();
3248 hrc = network->COMGETTER(NetworkMask)(bstrNetMask.asOutParam()); H();
3249 hrc = network->COMGETTER(LowerIP)(bstrLowerIP.asOutParam()); H();
3250 hrc = network->COMGETTER(UpperIP)(bstrUpperIP.asOutParam()); H();
3251 if (!bstr.isEmpty())
3252 {
3253 InsertConfigString(pLunL0, "Driver", "VMNet");
3254 InsertConfigNode(pLunL0, "Config", &pCfg);
3255 // InsertConfigString(pCfg, "Trunk", bstr);
3256 // InsertConfigStringF(pCfg, "Network", "HostOnlyNetworking-%ls", bstr.raw());
3257 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
3258 InsertConfigString(pCfg, "Id", bstrId);
3259 InsertConfigString(pCfg, "NetworkMask", bstrNetMask);
3260 InsertConfigString(pCfg, "LowerIP", bstrLowerIP);
3261 InsertConfigString(pCfg, "UpperIP", bstrUpperIP);
3262 // InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
3263 networkName.setNull(); // We do not want DHCP server on our network!
3264 // trunkType = Bstr(TRUNKTYPE_WHATEVER);
3265 }
3266 break;
3267 }
3268#endif /* VBOX_WITH_VMNET */
3269
3270 default:
3271 AssertMsgFailed(("should not get here!\n"));
3272 break;
3273 }
3274
3275 /*
3276 * Attempt to attach the driver.
3277 */
3278 switch (eAttachmentType)
3279 {
3280 case NetworkAttachmentType_Null:
3281 break;
3282
3283 case NetworkAttachmentType_Bridged:
3284 case NetworkAttachmentType_Internal:
3285 case NetworkAttachmentType_HostOnly:
3286#ifdef VBOX_WITH_VMNET
3287 case NetworkAttachmentType_HostOnlyNetwork:
3288#endif /* VBOX_WITH_VMNET */
3289 case NetworkAttachmentType_NAT:
3290 case NetworkAttachmentType_Generic:
3291 case NetworkAttachmentType_NATNetwork:
3292#ifdef VBOX_WITH_CLOUD_NET
3293 case NetworkAttachmentType_Cloud:
3294#endif /* VBOX_WITH_CLOUD_NET */
3295 {
3296 if (SUCCEEDED(hrc) && RT_SUCCESS(vrc))
3297 {
3298 if (fAttachDetach)
3299 {
3300 vrc = pVMM->pfnPDMR3DriverAttach(mpUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/, NULL /* ppBase */);
3301 //AssertRC(vrc);
3302 }
3303
3304 {
3305 /** @todo pritesh: get the dhcp server name from the
3306 * previous network configuration and then stop the server
3307 * else it may conflict with the dhcp server running with
3308 * the current attachment type
3309 */
3310 /* Stop the hostonly DHCP Server */
3311 }
3312
3313 /*
3314 * NAT networks start their DHCP server theirself, see NATNetwork::Start()
3315 */
3316 if ( !networkName.isEmpty()
3317 && eAttachmentType != NetworkAttachmentType_NATNetwork)
3318 {
3319 /*
3320 * Until we implement service reference counters DHCP Server will be stopped
3321 * by DHCPServerRunner destructor.
3322 */
3323 ComPtr<IDHCPServer> dhcpServer;
3324 hrc = virtualBox->FindDHCPServerByNetworkName(networkName.raw(), dhcpServer.asOutParam());
3325 if (SUCCEEDED(hrc))
3326 {
3327 /* there is a DHCP server available for this network */
3328 BOOL fEnabledDhcp;
3329 hrc = dhcpServer->COMGETTER(Enabled)(&fEnabledDhcp);
3330 if (FAILED(hrc))
3331 {
3332 LogRel(("DHCP svr: COMGETTER(Enabled) failed, hrc (%Rhrc)\n", hrc));
3333 H();
3334 }
3335
3336 if (fEnabledDhcp)
3337 hrc = dhcpServer->Start(trunkName.raw(), trunkType.raw());
3338 }
3339 else
3340 hrc = S_OK;
3341 }
3342 }
3343
3344 break;
3345 }
3346
3347 default:
3348 AssertMsgFailed(("should not get here!\n"));
3349 break;
3350 }
3351
3352 meAttachmentType[uInstance] = eAttachmentType;
3353 }
3354 catch (ConfigError &x)
3355 {
3356 // InsertConfig threw something:
3357 return x.m_vrc;
3358 }
3359
3360#undef H
3361
3362 return VINF_SUCCESS;
3363}
3364
3365
3366/**
3367 * Configures the serial port at the given CFGM node with the supplied parameters.
3368 *
3369 * @returns VBox status code.
3370 * @param pInst The instance CFGM node.
3371 * @param ePortMode The port mode to sue.
3372 * @param pszPath The serial port path.
3373 * @param fServer Flag whether the port should act as a server
3374 * for the pipe and TCP mode or connect as a client.
3375 */
3376int Console::i_configSerialPort(PCFGMNODE pInst, PortMode_T ePortMode, const char *pszPath, bool fServer)
3377{
3378 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3379 PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */
3380 PCFGMNODE pLunL1Cfg = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/Config */
3381
3382 try
3383 {
3384 InsertConfigNode(pInst, "LUN#0", &pLunL0);
3385 if (ePortMode == PortMode_HostPipe)
3386 {
3387 InsertConfigString(pLunL0, "Driver", "Char");
3388 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3389 InsertConfigString(pLunL1, "Driver", "NamedPipe");
3390 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
3391 InsertConfigString(pLunL1Cfg, "Location", pszPath);
3392 InsertConfigInteger(pLunL1Cfg, "IsServer", fServer);
3393 }
3394 else if (ePortMode == PortMode_HostDevice)
3395 {
3396 InsertConfigString(pLunL0, "Driver", "Host Serial");
3397 InsertConfigNode(pLunL0, "Config", &pLunL1);
3398 InsertConfigString(pLunL1, "DevicePath", pszPath);
3399 }
3400 else if (ePortMode == PortMode_TCP)
3401 {
3402 InsertConfigString(pLunL0, "Driver", "Char");
3403 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3404 InsertConfigString(pLunL1, "Driver", "TCP");
3405 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
3406 InsertConfigString(pLunL1Cfg, "Location", pszPath);
3407 InsertConfigInteger(pLunL1Cfg, "IsServer", fServer);
3408 }
3409 else if (ePortMode == PortMode_RawFile)
3410 {
3411 InsertConfigString(pLunL0, "Driver", "Char");
3412 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3413 InsertConfigString(pLunL1, "Driver", "RawFile");
3414 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
3415 InsertConfigString(pLunL1Cfg, "Location", pszPath);
3416 }
3417 }
3418 catch (ConfigError &x)
3419 {
3420 /* InsertConfig threw something */
3421 return x.m_vrc;
3422 }
3423
3424 return VINF_SUCCESS;
3425}
3426
3427
3428#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
3429#define VRC() AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("vrc=%Rrc\n", vrc), vrc)
3430
3431int Console::i_configPdm(ComPtr<IMachine> pMachine, PCVMMR3VTABLE pVMM, PUVM pUVM, PCFGMNODE pRoot)
3432{
3433 PCFGMNODE pPDM;
3434 PCFGMNODE pNode;
3435 PCFGMNODE pMod;
3436 InsertConfigNode(pRoot, "PDM", &pPDM);
3437 InsertConfigNode(pPDM, "Devices", &pNode);
3438 InsertConfigNode(pPDM, "Drivers", &pNode);
3439 InsertConfigNode(pNode, "VBoxC", &pMod);
3440#ifdef VBOX_WITH_XPCOM
3441 // VBoxC is located in the components subdirectory
3442 char szPathVBoxC[RTPATH_MAX];
3443 int vrc = RTPathAppPrivateArch(szPathVBoxC, RTPATH_MAX); VRC();
3444 vrc = RTPathAppend(szPathVBoxC, RTPATH_MAX, "/components/VBoxC"); VRC();
3445 InsertConfigString(pMod, "Path", szPathVBoxC);
3446#else
3447 InsertConfigString(pMod, "Path", "VBoxC");
3448#endif
3449
3450
3451 /*
3452 * Block cache settings.
3453 */
3454 PCFGMNODE pPDMBlkCache;
3455 InsertConfigNode(pPDM, "BlkCache", &pPDMBlkCache);
3456
3457 /* I/O cache size */
3458 ULONG ioCacheSize = 5;
3459 HRESULT hrc = pMachine->COMGETTER(IOCacheSize)(&ioCacheSize); H();
3460 InsertConfigInteger(pPDMBlkCache, "CacheSize", ioCacheSize * _1M);
3461
3462 /*
3463 * Bandwidth groups.
3464 */
3465 ComPtr<IBandwidthControl> bwCtrl;
3466
3467 hrc = pMachine->COMGETTER(BandwidthControl)(bwCtrl.asOutParam()); H();
3468
3469 com::SafeIfaceArray<IBandwidthGroup> bwGroups;
3470 hrc = bwCtrl->GetAllBandwidthGroups(ComSafeArrayAsOutParam(bwGroups)); H();
3471
3472 PCFGMNODE pAc;
3473 InsertConfigNode(pPDM, "AsyncCompletion", &pAc);
3474 PCFGMNODE pAcFile;
3475 InsertConfigNode(pAc, "File", &pAcFile);
3476 PCFGMNODE pAcFileBwGroups;
3477 InsertConfigNode(pAcFile, "BwGroups", &pAcFileBwGroups);
3478#ifdef VBOX_WITH_NETSHAPER
3479 PCFGMNODE pNetworkShaper;
3480 InsertConfigNode(pPDM, "NetworkShaper", &pNetworkShaper);
3481 PCFGMNODE pNetworkBwGroups;
3482 InsertConfigNode(pNetworkShaper, "BwGroups", &pNetworkBwGroups);
3483#endif /* VBOX_WITH_NETSHAPER */
3484
3485 for (size_t i = 0; i < bwGroups.size(); i++)
3486 {
3487 Bstr strName;
3488 hrc = bwGroups[i]->COMGETTER(Name)(strName.asOutParam()); H();
3489 if (strName.isEmpty())
3490 return pVMM->pfnVMR3SetError(pUVM, VERR_CFGM_NO_NODE, RT_SRC_POS, N_("No bandwidth group name specified"));
3491
3492 BandwidthGroupType_T enmType = BandwidthGroupType_Null;
3493 hrc = bwGroups[i]->COMGETTER(Type)(&enmType); H();
3494 LONG64 cMaxBytesPerSec = 0;
3495 hrc = bwGroups[i]->COMGETTER(MaxBytesPerSec)(&cMaxBytesPerSec); H();
3496
3497 if (enmType == BandwidthGroupType_Disk)
3498 {
3499 PCFGMNODE pBwGroup;
3500 InsertConfigNode(pAcFileBwGroups, Utf8Str(strName).c_str(), &pBwGroup);
3501 InsertConfigInteger(pBwGroup, "Max", cMaxBytesPerSec);
3502 InsertConfigInteger(pBwGroup, "Start", cMaxBytesPerSec);
3503 InsertConfigInteger(pBwGroup, "Step", 0);
3504 }
3505#ifdef VBOX_WITH_NETSHAPER
3506 else if (enmType == BandwidthGroupType_Network)
3507 {
3508 /* Network bandwidth groups. */
3509 PCFGMNODE pBwGroup;
3510 InsertConfigNode(pNetworkBwGroups, Utf8Str(strName).c_str(), &pBwGroup);
3511 InsertConfigInteger(pBwGroup, "Max", cMaxBytesPerSec);
3512 }
3513#endif /* VBOX_WITH_NETSHAPER */
3514 }
3515
3516 /** @todo r=aeichner Looks like this setting is completely unused in VMM/PDM. */
3517 BOOL fAllowTracingToAccessVM;
3518 hrc = pMachine->COMGETTER(AllowTracingToAccessVM)(&fAllowTracingToAccessVM); H();
3519 if (fAllowTracingToAccessVM)
3520 InsertConfigInteger(pPDM, "AllowTracingToAccessVM", 1);
3521
3522 return VINF_SUCCESS;
3523}
3524
3525
3526int Console::i_configAudioCtrl(ComPtr<IVirtualBox> pVBox, ComPtr<IMachine> pMachine, BusAssignmentManager *pBusMgr, PCFGMNODE pDevices,
3527 bool fOsXGuest, bool *pfAudioEnabled)
3528{
3529 Utf8Str strTmp;
3530 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */
3531 PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */
3532 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3533 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3534
3535 /*
3536 * AC'97 ICH / SoundBlaster16 audio / Intel HD Audio.
3537 */
3538 ComPtr<IAudioSettings> audioSettings;
3539 HRESULT hrc = pMachine->COMGETTER(AudioSettings)(audioSettings.asOutParam()); H();
3540
3541 BOOL fAudioEnabled = FALSE;
3542 ComPtr<IAudioAdapter> audioAdapter;
3543 hrc = audioSettings->COMGETTER(Adapter)(audioAdapter.asOutParam()); H();
3544 if (audioAdapter)
3545 {
3546 hrc = audioAdapter->COMGETTER(Enabled)(&fAudioEnabled); H();
3547 }
3548
3549 if (fAudioEnabled)
3550 {
3551 *pfAudioEnabled = true;
3552
3553 AudioControllerType_T enmAudioController;
3554 hrc = audioAdapter->COMGETTER(AudioController)(&enmAudioController); H();
3555 AudioCodecType_T enmAudioCodec;
3556 hrc = audioAdapter->COMGETTER(AudioCodec)(&enmAudioCodec); H();
3557
3558 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/Device/TimerHz", &strTmp);
3559 const uint64_t uTimerHz = strTmp.toUInt64();
3560
3561 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/Device/BufSizeInMs", &strTmp);
3562 const uint64_t uBufSizeInMs = strTmp.toUInt64();
3563
3564 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/Device/BufSizeOutMs", &strTmp);
3565 const uint64_t uBufSizeOutMs = strTmp.toUInt64();
3566
3567 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp);
3568 const bool fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
3569
3570 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/Debug/Level", &strTmp);
3571 const uint32_t uDebugLevel = strTmp.toUInt32();
3572
3573 Utf8Str strDebugPathOut;
3574 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut);
3575
3576#ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3577 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/VaKit/Enabled", &strTmp); /* Deprecated; do not use! */
3578 if (strTmp.isEmpty())
3579 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/ValKit/Enabled", &strTmp);
3580 /* Whether the Validation Kit audio backend runs as the primary backend.
3581 * Can also be used with VBox release builds. */
3582 const bool fValKitEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
3583#endif
3584 /** @todo Implement an audio device class, similar to the audio backend class, to construct the common stuff
3585 * without duplicating (more) code. */
3586
3587 const char *pszAudioDevice;
3588 switch (enmAudioController)
3589 {
3590 case AudioControllerType_AC97:
3591 {
3592 /* ICH AC'97. */
3593 pszAudioDevice = "ichac97";
3594
3595 InsertConfigNode(pDevices, pszAudioDevice, &pDev);
3596 InsertConfigNode(pDev, "0", &pInst);
3597 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3598 hrc = pBusMgr->assignPCIDevice(pszAudioDevice, pInst); H();
3599 InsertConfigNode(pInst, "Config", &pCfg);
3600 switch (enmAudioCodec)
3601 {
3602 case AudioCodecType_STAC9700:
3603 InsertConfigString(pCfg, "Codec", "STAC9700");
3604 break;
3605 case AudioCodecType_AD1980:
3606 InsertConfigString(pCfg, "Codec", "AD1980");
3607 break;
3608 default: AssertFailedBreak();
3609 }
3610 if (uTimerHz)
3611 InsertConfigInteger(pCfg, "TimerHz", uTimerHz);
3612 if (uBufSizeInMs)
3613 InsertConfigInteger(pCfg, "BufSizeInMs", uBufSizeInMs);
3614 if (uBufSizeOutMs)
3615 InsertConfigInteger(pCfg, "BufSizeOutMs", uBufSizeOutMs);
3616 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3617 if (strDebugPathOut.isNotEmpty())
3618 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut);
3619 break;
3620 }
3621 case AudioControllerType_SB16:
3622 {
3623 /* Legacy SoundBlaster16. */
3624 pszAudioDevice = "sb16";
3625
3626 InsertConfigNode(pDevices, pszAudioDevice, &pDev);
3627 InsertConfigNode(pDev, "0", &pInst);
3628 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3629 InsertConfigNode(pInst, "Config", &pCfg);
3630 InsertConfigInteger(pCfg, "IRQ", 5);
3631 InsertConfigInteger(pCfg, "DMA", 1);
3632 InsertConfigInteger(pCfg, "DMA16", 5);
3633 InsertConfigInteger(pCfg, "Port", 0x220);
3634 InsertConfigInteger(pCfg, "Version", 0x0405);
3635 if (uTimerHz)
3636 InsertConfigInteger(pCfg, "TimerHz", uTimerHz);
3637 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3638 if (strDebugPathOut.isNotEmpty())
3639 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut);
3640 break;
3641 }
3642 case AudioControllerType_HDA:
3643 {
3644 /* Intel HD Audio. */
3645 pszAudioDevice = "hda";
3646
3647 InsertConfigNode(pDevices, pszAudioDevice, &pDev);
3648 InsertConfigNode(pDev, "0", &pInst);
3649 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3650 hrc = pBusMgr->assignPCIDevice(pszAudioDevice, pInst); H();
3651 InsertConfigNode(pInst, "Config", &pCfg);
3652 if (uBufSizeInMs)
3653 InsertConfigInteger(pCfg, "BufSizeInMs", uBufSizeInMs);
3654 if (uBufSizeOutMs)
3655 InsertConfigInteger(pCfg, "BufSizeOutMs", uBufSizeOutMs);
3656 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
3657 if (strDebugPathOut.isNotEmpty())
3658 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut);
3659
3660 /* macOS guests uses a different HDA variant to make 10.14+ (or maybe 10.13?) recognize the device. */
3661 if (fOsXGuest)
3662 InsertConfigString(pCfg, "DeviceName", "Intel Sunrise Point");
3663 break;
3664 }
3665 default:
3666 pszAudioDevice = "oops";
3667 AssertFailedBreak();
3668 }
3669
3670 PCFGMNODE pCfgAudioAdapter = NULL;
3671 InsertConfigNode(pInst, "AudioConfig", &pCfgAudioAdapter);
3672 SafeArray<BSTR> audioProps;
3673 hrc = audioAdapter->COMGETTER(PropertiesList)(ComSafeArrayAsOutParam(audioProps)); H();
3674
3675 std::list<Utf8Str> audioPropertyNamesList;
3676 for (size_t i = 0; i < audioProps.size(); ++i)
3677 {
3678 Bstr bstrValue;
3679 audioPropertyNamesList.push_back(Utf8Str(audioProps[i]));
3680 hrc = audioAdapter->GetProperty(audioProps[i], bstrValue.asOutParam());
3681 Utf8Str strKey(audioProps[i]);
3682 InsertConfigString(pCfgAudioAdapter, strKey.c_str(), bstrValue);
3683 }
3684
3685 /*
3686 * The audio driver.
3687 */
3688 const char *pszAudioDriver = NULL;
3689#ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3690 if (fValKitEnabled)
3691 {
3692 pszAudioDriver = "ValidationKitAudio";
3693 LogRel(("Audio: ValidationKit driver active\n"));
3694 }
3695#endif
3696 /* If nothing else was selected before, ask the API. */
3697 if (pszAudioDriver == NULL)
3698 {
3699 AudioDriverType_T enmAudioDriver;
3700 hrc = audioAdapter->COMGETTER(AudioDriver)(&enmAudioDriver); H();
3701
3702 /* The "Default" audio driver needs special treatment, as we need to figure out which driver to use
3703 * by default on the current platform. */
3704 bool const fUseDefaultDrv = enmAudioDriver == AudioDriverType_Default;
3705
3706 AudioDriverType_T const enmDefaultAudioDriver = settings::MachineConfigFile::getHostDefaultAudioDriver();
3707
3708 if (fUseDefaultDrv)
3709 {
3710 enmAudioDriver = enmDefaultAudioDriver;
3711 if (enmAudioDriver == AudioDriverType_Null)
3712 LogRel(("Audio: Warning: No default driver detected for current platform -- defaulting to Null audio backend\n"));
3713 }
3714
3715 switch (enmAudioDriver)
3716 {
3717 case AudioDriverType_Default: /* Can't happen, but handle it anyway. */
3718 RT_FALL_THROUGH();
3719 case AudioDriverType_Null:
3720 pszAudioDriver = "NullAudio";
3721 break;
3722#ifdef RT_OS_WINDOWS
3723# ifdef VBOX_WITH_WINMM
3724 case AudioDriverType_WinMM:
3725# error "Port WinMM audio backend!" /** @todo Still needed? */
3726 break;
3727# endif
3728 case AudioDriverType_DirectSound:
3729 /* Use the Windows Audio Session (WAS) API rather than Direct Sound on Windows
3730 versions we've tested it on (currently W7+). Since Vista, Direct Sound has
3731 been emulated on top of WAS according to the docs, so better use WAS directly.
3732
3733 Set extradata value "VBoxInternal2/Audio/WindowsDrv" "dsound" to no use WasAPI.
3734
3735 Keep this hack for backwards compatibility (introduced < 7.0).
3736 */
3737 GetExtraDataBoth(pVBox, pMachine, "VBoxInternal2/Audio/WindowsDrv", &strTmp); H();
3738 if ( enmDefaultAudioDriver == AudioDriverType_WAS
3739 && ( strTmp.isEmpty()
3740 || strTmp.equalsIgnoreCase("was")
3741 || strTmp.equalsIgnoreCase("wasapi")) )
3742 {
3743 /* Nothing to do here, fall through to WAS driver. */
3744 }
3745 else
3746 {
3747 pszAudioDriver = "DSoundAudio";
3748 break;
3749 }
3750 RT_FALL_THROUGH();
3751 case AudioDriverType_WAS:
3752 if (enmDefaultAudioDriver == AudioDriverType_WAS) /* WAS supported? */
3753 pszAudioDriver = "HostAudioWas";
3754 else if (enmDefaultAudioDriver == AudioDriverType_DirectSound)
3755 {
3756 LogRel(("Audio: Warning: Windows Audio Session (WAS) not supported, defaulting to DirectSound backend\n"));
3757 pszAudioDriver = "DSoundAudio";
3758 }
3759 break;
3760#endif /* RT_OS_WINDOWS */
3761#ifdef RT_OS_SOLARIS
3762 case AudioDriverType_SolAudio:
3763 /* Should not happen, as the Solaris Audio backend is not around anymore.
3764 * Remove this sometime later. */
3765 LogRel(("Audio: Warning: Solaris Audio is deprecated, please switch to OSS!\n"));
3766 LogRel(("Audio: Automatically setting host audio backend to OSS\n"));
3767
3768 /* Manually set backend to OSS for now. */
3769 pszAudioDriver = "OSSAudio";
3770 break;
3771#endif
3772#ifdef VBOX_WITH_AUDIO_OSS
3773 case AudioDriverType_OSS:
3774 pszAudioDriver = "OSSAudio";
3775 break;
3776#endif
3777#ifdef VBOX_WITH_AUDIO_ALSA
3778 case AudioDriverType_ALSA:
3779 pszAudioDriver = "ALSAAudio";
3780 break;
3781#endif
3782#ifdef VBOX_WITH_AUDIO_PULSE
3783 case AudioDriverType_Pulse:
3784 pszAudioDriver = "PulseAudio";
3785 break;
3786#endif
3787#ifdef RT_OS_DARWIN
3788 case AudioDriverType_CoreAudio:
3789 pszAudioDriver = "CoreAudio";
3790 break;
3791#endif
3792 default:
3793 pszAudioDriver = "oops";
3794 AssertFailedBreak();
3795 }
3796
3797 if (fUseDefaultDrv)
3798 LogRel(("Audio: Detected default audio driver type is '%s'\n", pszAudioDriver));
3799 }
3800
3801 BOOL fAudioEnabledIn = FALSE;
3802 hrc = audioAdapter->COMGETTER(EnabledIn)(&fAudioEnabledIn); H();
3803 BOOL fAudioEnabledOut = FALSE;
3804 hrc = audioAdapter->COMGETTER(EnabledOut)(&fAudioEnabledOut); H();
3805
3806 unsigned idxAudioLun = 0;
3807
3808 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3809 i_configAudioDriver(pVBox, pMachine, pLunL0, pszAudioDriver, !!fAudioEnabledIn, !!fAudioEnabledOut);
3810 idxAudioLun++;
3811
3812#ifdef VBOX_WITH_AUDIO_VRDE
3813 /* Insert dummy audio driver to have the LUN configured. */
3814 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3815 InsertConfigString(pLunL0, "Driver", "AUDIO");
3816 {
3817 AudioDriverCfg DrvCfgVRDE(pszAudioDevice, 0 /* Instance */, idxAudioLun, "AudioVRDE",
3818 !!fAudioEnabledIn, !!fAudioEnabledOut);
3819 int vrc = mAudioVRDE->InitializeConfig(&DrvCfgVRDE);
3820 AssertRCStmt(vrc, throw ConfigError(__FUNCTION__, vrc, "mAudioVRDE->InitializeConfig failed"));
3821 }
3822 idxAudioLun++;
3823#endif
3824
3825#ifdef VBOX_WITH_AUDIO_RECORDING
3826 /* Insert dummy audio driver to have the LUN configured. */
3827 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3828 InsertConfigString(pLunL0, "Driver", "AUDIO");
3829 {
3830 AudioDriverCfg DrvCfgVideoRec(pszAudioDevice, 0 /* Instance */, idxAudioLun, "AudioVideoRec",
3831 false /*a_fEnabledIn*/, true /*a_fEnabledOut*/);
3832 int vrc = mRecording.mAudioRec->InitializeConfig(&DrvCfgVideoRec);
3833 AssertRCStmt(vrc, throw ConfigError(__FUNCTION__, vrc, "Recording.mAudioRec->InitializeConfig failed"));
3834 }
3835 idxAudioLun++;
3836#endif
3837
3838 if (fDebugEnabled)
3839 {
3840#ifdef VBOX_WITH_AUDIO_DEBUG
3841# ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3842 /*
3843 * When both, ValidationKit and Debug mode (for audio) are enabled,
3844 * skip configuring the Debug audio driver, as both modes can
3845 * mess with the audio data and would lead to side effects.
3846 *
3847 * The ValidationKit audio driver has precedence over the Debug audio driver.
3848 *
3849 * This also can (and will) be used in VBox release builds.
3850 */
3851 if (fValKitEnabled)
3852 {
3853 LogRel(("Audio: Warning: ValidationKit running and Debug mode enabled -- disabling Debug driver\n"));
3854 }
3855 else /* Debug mode active -- run both (nice for catching errors / doing development). */
3856 {
3857 /*
3858 * The ValidationKit backend.
3859 */
3860 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3861 i_configAudioDriver(pVBox, pMachine, pLunL0, "ValidationKitAudio",
3862 !!fAudioEnabledIn, !!fAudioEnabledOut);
3863 idxAudioLun++;
3864# endif /* VBOX_WITH_AUDIO_VALIDATIONKIT */
3865 /*
3866 * The Debug audio backend.
3867 */
3868 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", idxAudioLun);
3869 i_configAudioDriver(pVBox, pMachine, pLunL0, "DebugAudio",
3870 !!fAudioEnabledIn, !!fAudioEnabledOut);
3871 idxAudioLun++;
3872# ifdef VBOX_WITH_AUDIO_VALIDATIONKIT
3873 }
3874# endif /* VBOX_WITH_AUDIO_VALIDATIONKIT */
3875#endif /* VBOX_WITH_AUDIO_DEBUG */
3876
3877 /*
3878 * Tweak the logging groups.
3879 */
3880 Utf8Str strGroups("drv_audio.e.l.l2.l3.f"
3881 " audio_mixer.e.l.l2.l3.f"
3882 " dev_hda_codec.e.l.l2.l3.f"
3883 " dev_hda.e.l.l2.l3.f"
3884 " dev_ac97.e.l.l2.l3.f"
3885 " dev_sb16.e.l.l2.l3.f");
3886
3887 LogRel(("Audio: Debug level set to %RU32\n", uDebugLevel));
3888
3889 switch (uDebugLevel)
3890 {
3891 case 0:
3892 strGroups += " drv_host_audio.e.l.l2.l3.f";
3893 break;
3894 case 1:
3895 RT_FALL_THROUGH();
3896 case 2:
3897 RT_FALL_THROUGH();
3898 case 3:
3899 strGroups += " drv_host_audio.e.l.l2.l3.f+audio_test.e.l.l2.l3.f";
3900 break;
3901 case 4:
3902 RT_FALL_THROUGH();
3903 default:
3904 strGroups += " drv_host_audio.e.l.l2.l3.l4.f+audio_test.e.l.l2.l3.l4.f";
3905 break;
3906 }
3907
3908 int vrc = RTLogGroupSettings(RTLogRelGetDefaultInstance(), strGroups.c_str());
3909 if (RT_FAILURE(vrc))
3910 LogRel(("Audio: Setting debug logging failed, vrc=%Rrc\n", vrc));
3911 }
3912 }
3913
3914 return VINF_SUCCESS;
3915}
3916
3917
3918int Console::i_configVmmDev(ComPtr<IMachine> pMachine, BusAssignmentManager *pBusMgr, PCFGMNODE pDevices, bool fMmioReq)
3919{
3920 VMMDev *pVMMDev = m_pVMMDev; Assert(pVMMDev);
3921
3922 int vrc = VINF_SUCCESS;
3923 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */
3924 PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */
3925 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3926 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3927
3928 /*
3929 * VMM Device
3930 */
3931 InsertConfigNode(pDevices, "VMMDev", &pDev);
3932 InsertConfigNode(pDev, "0", &pInst);
3933 InsertConfigNode(pInst, "Config", &pCfg);
3934 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
3935 HRESULT hrc = pBusMgr->assignPCIDevice("VMMDev", pInst); H();
3936 if (fMmioReq)
3937 InsertConfigInteger(pCfg, "MmioReq", 1);
3938
3939 Bstr hwVersion;
3940 hrc = pMachine->COMGETTER(HardwareVersion)(hwVersion.asOutParam()); H();
3941 if (hwVersion.compare(Bstr("1").raw()) == 0) /* <= 2.0.x */
3942 InsertConfigInteger(pCfg, "HeapEnabled", 0);
3943 Bstr snapshotFolder;
3944 hrc = pMachine->COMGETTER(SnapshotFolder)(snapshotFolder.asOutParam()); H();
3945 InsertConfigString(pCfg, "GuestCoreDumpDir", snapshotFolder);
3946
3947 /* the VMM device's Main driver */
3948 InsertConfigNode(pInst, "LUN#0", &pLunL0);
3949 InsertConfigString(pLunL0, "Driver", "HGCM");
3950 InsertConfigNode(pLunL0, "Config", &pCfg);
3951
3952 /*
3953 * Attach the status driver.
3954 */
3955 i_attachStatusDriver(pInst, DeviceType_SharedFolder);
3956
3957#ifdef VBOX_WITH_SHARED_CLIPBOARD
3958 /*
3959 * Shared Clipboard.
3960 */
3961 {
3962 ClipboardMode_T enmClipboardMode = ClipboardMode_Disabled;
3963 hrc = pMachine->COMGETTER(ClipboardMode)(&enmClipboardMode); H();
3964# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
3965 BOOL fFileTransfersEnabled;
3966 hrc = pMachine->COMGETTER(ClipboardFileTransfersEnabled)(&fFileTransfersEnabled); H();
3967#endif
3968
3969 /* Load the service */
3970 vrc = pVMMDev->hgcmLoadService("VBoxSharedClipboard", "VBoxSharedClipboard");
3971 if (RT_SUCCESS(vrc))
3972 {
3973 LogRel(("Shared Clipboard: Service loaded\n"));
3974
3975 /* Set initial clipboard mode. */
3976 vrc = i_changeClipboardMode(enmClipboardMode);
3977 AssertLogRelMsg(RT_SUCCESS(vrc), ("Shared Clipboard: Failed to set initial clipboard mode (%d): vrc=%Rrc\n",
3978 enmClipboardMode, vrc));
3979
3980 /* Setup the service. */
3981 VBOXHGCMSVCPARM parm;
3982 HGCMSvcSetU32(&parm, !i_useHostClipboard());
3983 vrc = pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, &parm);
3984 AssertLogRelMsg(RT_SUCCESS(vrc), ("Shared Clipboard: Failed to set initial headless mode (%RTbool): vrc=%Rrc\n",
3985 !i_useHostClipboard(), vrc));
3986
3987# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
3988 vrc = i_changeClipboardFileTransferMode(RT_BOOL(fFileTransfersEnabled));
3989 AssertLogRelMsg(RT_SUCCESS(vrc), ("Shared Clipboard: Failed to set initial file transfer mode (%u): vrc=%Rrc\n",
3990 fFileTransfersEnabled, vrc));
3991# endif
3992 GuestShCl::createInstance(this /* pConsole */);
3993 vrc = HGCMHostRegisterServiceExtension(&m_hHgcmSvcExtShCl, "VBoxSharedClipboard",
3994 &GuestShCl::hgcmDispatcher,
3995 GuestShClInst());
3996 if (RT_FAILURE(vrc))
3997 Log(("Cannot register VBoxSharedClipboard extension, vrc=%Rrc\n", vrc));
3998 }
3999 else
4000 LogRel(("Shared Clipboard: Not available, vrc=%Rrc\n", vrc));
4001 vrc = VINF_SUCCESS; /* None of the potential failures above are fatal. */
4002 }
4003#endif /* VBOX_WITH_SHARED_CLIPBOARD */
4004
4005 /*
4006 * HGCM HostChannel.
4007 */
4008 {
4009 Bstr value;
4010 hrc = pMachine->GetExtraData(Bstr("HGCM/HostChannel").raw(),
4011 value.asOutParam());
4012
4013 if ( hrc == S_OK
4014 && value == "1")
4015 {
4016 vrc = pVMMDev->hgcmLoadService("VBoxHostChannel", "VBoxHostChannel");
4017 if (RT_FAILURE(vrc))
4018 {
4019 LogRel(("VBoxHostChannel is not available, vrc=%Rrc\n", vrc));
4020 /* That is not a fatal failure. */
4021 vrc = VINF_SUCCESS;
4022 }
4023 }
4024 }
4025
4026#ifdef VBOX_WITH_DRAG_AND_DROP
4027 /*
4028 * Drag and Drop.
4029 */
4030 {
4031 DnDMode_T enmMode = DnDMode_Disabled;
4032 hrc = pMachine->COMGETTER(DnDMode)(&enmMode); H();
4033
4034 /* Load the service */
4035 vrc = pVMMDev->hgcmLoadService("VBoxDragAndDropSvc", "VBoxDragAndDropSvc");
4036 if (RT_FAILURE(vrc))
4037 {
4038 LogRel(("Drag and drop service is not available, vrc=%Rrc\n", vrc));
4039 /* That is not a fatal failure. */
4040 vrc = VINF_SUCCESS;
4041 }
4042 else
4043 {
4044 vrc = HGCMHostRegisterServiceExtension(&m_hHgcmSvcExtDragAndDrop, "VBoxDragAndDropSvc",
4045 &GuestDnD::notifyDnDDispatcher,
4046 GuestDnDInst());
4047 if (RT_FAILURE(vrc))
4048 Log(("Cannot register VBoxDragAndDropSvc extension, vrc=%Rrc\n", vrc));
4049 else
4050 {
4051 LogRel(("Drag and drop service loaded\n"));
4052 vrc = i_changeDnDMode(enmMode);
4053 }
4054 }
4055 }
4056#endif /* VBOX_WITH_DRAG_AND_DROP */
4057
4058 return vrc;
4059}
4060
4061
4062int Console::i_configUsb(ComPtr<IMachine> pMachine, BusAssignmentManager *pBusMgr, PCFGMNODE pRoot, PCFGMNODE pDevices,
4063 KeyboardHIDType_T enmKbdHid, PointingHIDType_T enmPointingHid, PCFGMNODE *ppUsbDevices)
4064{
4065 int vrc = VINF_SUCCESS;
4066 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */
4067 PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */
4068 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4069 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4070 PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */
4071
4072 com::SafeIfaceArray<IUSBController> usbCtrls;
4073 HRESULT hrc = pMachine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbCtrls));
4074 bool fOhciPresent = false; /**< Flag whether at least one OHCI controller is present. */
4075 bool fXhciPresent = false; /**< Flag whether at least one XHCI controller is present. */
4076
4077 if (SUCCEEDED(hrc))
4078 {
4079 for (size_t i = 0; i < usbCtrls.size(); ++i)
4080 {
4081 USBControllerType_T enmCtrlType;
4082 vrc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H();
4083 if (enmCtrlType == USBControllerType_OHCI)
4084 {
4085 fOhciPresent = true;
4086 break;
4087 }
4088 else if (enmCtrlType == USBControllerType_XHCI)
4089 {
4090 fXhciPresent = true;
4091 break;
4092 }
4093 }
4094 }
4095 else if (hrc != E_NOTIMPL)
4096 {
4097 H();
4098 }
4099
4100 /*
4101 * Currently EHCI is only enabled when an OHCI or XHCI controller is present as well.
4102 */
4103 if (fOhciPresent || fXhciPresent)
4104 mfVMHasUsbController = true;
4105
4106 if (mfVMHasUsbController)
4107 {
4108 for (size_t i = 0; i < usbCtrls.size(); ++i)
4109 {
4110 USBControllerType_T enmCtrlType;
4111 vrc = usbCtrls[i]->COMGETTER(Type)(&enmCtrlType); H();
4112
4113 if (enmCtrlType == USBControllerType_OHCI)
4114 {
4115 InsertConfigNode(pDevices, "usb-ohci", &pDev);
4116 InsertConfigNode(pDev, "0", &pInst);
4117 InsertConfigNode(pInst, "Config", &pCfg);
4118 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
4119 hrc = pBusMgr->assignPCIDevice("usb-ohci", pInst); H();
4120 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4121 InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
4122 InsertConfigNode(pLunL0, "Config", &pCfg);
4123
4124 /*
4125 * Attach the status driver.
4126 */
4127 i_attachStatusDriver(pInst, DeviceType_USB);
4128 }
4129#ifdef VBOX_WITH_EHCI
4130 else if (enmCtrlType == USBControllerType_EHCI)
4131 {
4132 InsertConfigNode(pDevices, "usb-ehci", &pDev);
4133 InsertConfigNode(pDev, "0", &pInst);
4134 InsertConfigNode(pInst, "Config", &pCfg);
4135 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
4136 hrc = pBusMgr->assignPCIDevice("usb-ehci", pInst); H();
4137
4138 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4139 InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
4140 InsertConfigNode(pLunL0, "Config", &pCfg);
4141
4142 /*
4143 * Attach the status driver.
4144 */
4145 i_attachStatusDriver(pInst, DeviceType_USB);
4146 }
4147#endif
4148 else if (enmCtrlType == USBControllerType_XHCI)
4149 {
4150 InsertConfigNode(pDevices, "usb-xhci", &pDev);
4151 InsertConfigNode(pDev, "0", &pInst);
4152 InsertConfigNode(pInst, "Config", &pCfg);
4153 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
4154 hrc = pBusMgr->assignPCIDevice("usb-xhci", pInst); H();
4155
4156 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4157 InsertConfigString(pLunL0, "Driver", "VUSBRootHub");
4158 InsertConfigNode(pLunL0, "Config", &pCfg);
4159
4160 InsertConfigNode(pInst, "LUN#1", &pLunL1);
4161 InsertConfigString(pLunL1, "Driver", "VUSBRootHub");
4162 InsertConfigNode(pLunL1, "Config", &pCfg);
4163
4164 /*
4165 * Attach the status driver.
4166 */
4167 i_attachStatusDriver(pInst, DeviceType_USB, 2);
4168 }
4169 } /* for every USB controller. */
4170
4171
4172 /*
4173 * Virtual USB Devices.
4174 */
4175 PCFGMNODE pUsbDevices = NULL;
4176 InsertConfigNode(pRoot, "USB", &pUsbDevices);
4177 *ppUsbDevices = pUsbDevices;
4178
4179#ifdef VBOX_WITH_USB
4180 {
4181 /*
4182 * Global USB options, currently unused as we'll apply the 2.0 -> 1.1 morphing
4183 * on a per device level now.
4184 */
4185 InsertConfigNode(pUsbDevices, "USBProxy", &pCfg);
4186 InsertConfigNode(pCfg, "GlobalConfig", &pCfg);
4187 // This globally enables the 2.0 -> 1.1 device morphing of proxied devices to keep windows quiet.
4188 //InsertConfigInteger(pCfg, "Force11Device", true);
4189 // The following breaks stuff, but it makes MSDs work in vista. (I include it here so
4190 // that it's documented somewhere.) Users needing it can use:
4191 // VBoxManage setextradata "myvm" "VBoxInternal/USB/USBProxy/GlobalConfig/Force11PacketSize" 1
4192 //InsertConfigInteger(pCfg, "Force11PacketSize", true);
4193 }
4194#endif
4195
4196#ifdef VBOX_WITH_USB_CARDREADER
4197 BOOL aEmulatedUSBCardReaderEnabled = FALSE;
4198 hrc = pMachine->COMGETTER(EmulatedUSBCardReaderEnabled)(&aEmulatedUSBCardReaderEnabled); H();
4199 if (aEmulatedUSBCardReaderEnabled)
4200 {
4201 InsertConfigNode(pUsbDevices, "CardReader", &pDev);
4202 InsertConfigNode(pDev, "0", &pInst);
4203 InsertConfigNode(pInst, "Config", &pCfg);
4204
4205 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4206# ifdef VBOX_WITH_USB_CARDREADER_TEST
4207 InsertConfigString(pLunL0, "Driver", "DrvDirectCardReader");
4208 InsertConfigNode(pLunL0, "Config", &pCfg);
4209# else
4210 InsertConfigString(pLunL0, "Driver", "UsbCardReader");
4211 InsertConfigNode(pLunL0, "Config", &pCfg);
4212# endif
4213 }
4214#endif
4215
4216 /* Virtual USB Mouse/Tablet */
4217 if ( enmPointingHid == PointingHIDType_USBMouse
4218 || enmPointingHid == PointingHIDType_USBTablet
4219 || enmPointingHid == PointingHIDType_USBMultiTouch
4220 || enmPointingHid == PointingHIDType_USBMultiTouchScreenPlusPad)
4221 {
4222 InsertConfigNode(pUsbDevices, "HidMouse", &pDev);
4223 InsertConfigNode(pDev, "0", &pInst);
4224 InsertConfigNode(pInst, "Config", &pCfg);
4225
4226 if (enmPointingHid == PointingHIDType_USBMouse)
4227 InsertConfigString(pCfg, "Mode", "relative");
4228 else
4229 InsertConfigString(pCfg, "Mode", "absolute");
4230 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4231 InsertConfigString(pLunL0, "Driver", "MouseQueue");
4232 InsertConfigNode(pLunL0, "Config", &pCfg);
4233 InsertConfigInteger(pCfg, "QueueSize", 128);
4234
4235 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
4236 InsertConfigString(pLunL1, "Driver", "MainMouse");
4237 }
4238 if ( enmPointingHid == PointingHIDType_USBMultiTouch
4239 || enmPointingHid == PointingHIDType_USBMultiTouchScreenPlusPad)
4240 {
4241 InsertConfigNode(pDev, "1", &pInst);
4242 InsertConfigNode(pInst, "Config", &pCfg);
4243
4244 InsertConfigString(pCfg, "Mode", "multitouch");
4245 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4246 InsertConfigString(pLunL0, "Driver", "MouseQueue");
4247 InsertConfigNode(pLunL0, "Config", &pCfg);
4248 InsertConfigInteger(pCfg, "QueueSize", 128);
4249
4250 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
4251 InsertConfigString(pLunL1, "Driver", "MainMouse");
4252 }
4253 if (enmPointingHid == PointingHIDType_USBMultiTouchScreenPlusPad)
4254 {
4255 InsertConfigNode(pDev, "2", &pInst);
4256 InsertConfigNode(pInst, "Config", &pCfg);
4257
4258 InsertConfigString(pCfg, "Mode", "touchpad");
4259 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4260 InsertConfigString(pLunL0, "Driver", "MouseQueue");
4261 InsertConfigNode(pLunL0, "Config", &pCfg);
4262 InsertConfigInteger(pCfg, "QueueSize", 128);
4263
4264 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
4265 InsertConfigString(pLunL1, "Driver", "MainMouse");
4266 }
4267
4268 /* Virtual USB Keyboard */
4269 if (enmKbdHid == KeyboardHIDType_USBKeyboard)
4270 {
4271 InsertConfigNode(pUsbDevices, "HidKeyboard", &pDev);
4272 InsertConfigNode(pDev, "0", &pInst);
4273 InsertConfigNode(pInst, "Config", &pCfg);
4274
4275 InsertConfigNode(pInst, "LUN#0", &pLunL0);
4276 InsertConfigString(pLunL0, "Driver", "KeyboardQueue");
4277 InsertConfigNode(pLunL0, "Config", &pCfg);
4278 InsertConfigInteger(pCfg, "QueueSize", 64);
4279
4280 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
4281 InsertConfigString(pLunL1, "Driver", "MainKeyboard");
4282 }
4283 }
4284
4285 return VINF_SUCCESS;
4286}
4287
4288
4289/**
4290 * Translate IDE StorageControllerType_T to string representation.
4291 */
4292static const char* controllerString(StorageControllerType_T enmType)
4293{
4294 switch (enmType)
4295 {
4296 case StorageControllerType_PIIX3:
4297 return "PIIX3";
4298 case StorageControllerType_PIIX4:
4299 return "PIIX4";
4300 case StorageControllerType_ICH6:
4301 return "ICH6";
4302 default:
4303 return "Unknown";
4304 }
4305}
4306
4307
4308int Console::i_configStorageCtrls(ComPtr<IMachine> pMachine, BusAssignmentManager *pBusMgr, PCVMMR3VTABLE pVMM, PUVM pUVM,
4309 PCFGMNODE pDevices, PCFGMNODE pUsbDevices, PCFGMNODE pBiosCfg, bool *pfFdcEnabled)
4310{
4311 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */
4312 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4313
4314 com::SafeIfaceArray<IStorageController> ctrls;
4315 PCFGMNODE aCtrlNodes[StorageControllerType_VirtioSCSI + 1] = {};
4316 HRESULT hrc = pMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls)); H();
4317
4318 for (size_t i = 0; i < ctrls.size(); ++i)
4319 {
4320 DeviceType_T *paLedDevType = NULL;
4321
4322 StorageControllerType_T enmCtrlType;
4323 hrc = ctrls[i]->COMGETTER(ControllerType)(&enmCtrlType); H();
4324 AssertRelease((unsigned)enmCtrlType < RT_ELEMENTS(aCtrlNodes)
4325 || enmCtrlType == StorageControllerType_USB);
4326
4327 StorageBus_T enmBus;
4328 hrc = ctrls[i]->COMGETTER(Bus)(&enmBus); H();
4329
4330 Bstr controllerName;
4331 hrc = ctrls[i]->COMGETTER(Name)(controllerName.asOutParam()); H();
4332
4333 ULONG ulInstance = 999;
4334 hrc = ctrls[i]->COMGETTER(Instance)(&ulInstance); H();
4335
4336 BOOL fUseHostIOCache;
4337 hrc = ctrls[i]->COMGETTER(UseHostIOCache)(&fUseHostIOCache); H();
4338
4339 BOOL fBootable;
4340 hrc = ctrls[i]->COMGETTER(Bootable)(&fBootable); H();
4341
4342 PCFGMNODE pCtlInst = NULL;
4343 const char *pszCtrlDev = i_storageControllerTypeToStr(enmCtrlType);
4344 if (enmCtrlType != StorageControllerType_USB)
4345 {
4346 /* /Devices/<ctrldev>/ */
4347 pDev = aCtrlNodes[enmCtrlType];
4348 if (!pDev)
4349 {
4350 InsertConfigNode(pDevices, pszCtrlDev, &pDev);
4351 aCtrlNodes[enmCtrlType] = pDev; /* IDE variants are handled in the switch */
4352 }
4353
4354 /* /Devices/<ctrldev>/<instance>/ */
4355 InsertConfigNode(pDev, Utf8StrFmt("%u", ulInstance).c_str(), &pCtlInst);
4356
4357 /* Device config: /Devices/<ctrldev>/<instance>/<values> & /ditto/Config/<values> */
4358 InsertConfigInteger(pCtlInst, "Trusted", 1);
4359 InsertConfigNode(pCtlInst, "Config", &pCfg);
4360 }
4361
4362#define MAX_BIOS_LUN_COUNT 4
4363
4364 static const char * const apszBiosConfigScsi[MAX_BIOS_LUN_COUNT] =
4365 { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
4366
4367 static const char * const apszBiosConfigSata[MAX_BIOS_LUN_COUNT] =
4368 { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
4369
4370#undef MAX_BIOS_LUN_COUNT
4371
4372 switch (enmCtrlType)
4373 {
4374 case StorageControllerType_LsiLogic:
4375 {
4376 hrc = pBusMgr->assignPCIDevice("lsilogic", pCtlInst); H();
4377
4378 InsertConfigInteger(pCfg, "Bootable", fBootable);
4379
4380 /* BIOS configuration values, first SCSI controller only. */
4381 if ( !pBusMgr->hasPCIDevice("lsilogic", 1)
4382 && !pBusMgr->hasPCIDevice("buslogic", 0)
4383 && !pBusMgr->hasPCIDevice("lsilogicsas", 0)
4384 && pBiosCfg)
4385 {
4386 InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicscsi");
4387 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
4388 }
4389
4390 /* Attach the status driver */
4391 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk) | RT_BIT_32(DeviceType_DVD),
4392 16, &paLedDevType, &mapMediumAttachments, pszCtrlDev, ulInstance);
4393 break;
4394 }
4395
4396 case StorageControllerType_BusLogic:
4397 {
4398 hrc = pBusMgr->assignPCIDevice("buslogic", pCtlInst); H();
4399
4400 InsertConfigInteger(pCfg, "Bootable", fBootable);
4401
4402 /* BIOS configuration values, first SCSI controller only. */
4403 if ( !pBusMgr->hasPCIDevice("lsilogic", 0)
4404 && !pBusMgr->hasPCIDevice("buslogic", 1)
4405 && !pBusMgr->hasPCIDevice("lsilogicsas", 0)
4406 && pBiosCfg)
4407 {
4408 InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "buslogic");
4409 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
4410 }
4411
4412 /* Attach the status driver */
4413 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk) | RT_BIT_32(DeviceType_DVD),
4414 16, &paLedDevType, &mapMediumAttachments, pszCtrlDev, ulInstance);
4415 break;
4416 }
4417
4418 case StorageControllerType_IntelAhci:
4419 {
4420 hrc = pBusMgr->assignPCIDevice("ahci", pCtlInst); H();
4421
4422 ULONG cPorts = 0;
4423 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
4424 InsertConfigInteger(pCfg, "PortCount", cPorts);
4425 InsertConfigInteger(pCfg, "Bootable", fBootable);
4426
4427 com::SafeIfaceArray<IMediumAttachment> atts;
4428 hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(),
4429 ComSafeArrayAsOutParam(atts)); H();
4430
4431 /* Configure the hotpluggable flag for the port. */
4432 for (unsigned idxAtt = 0; idxAtt < atts.size(); ++idxAtt)
4433 {
4434 IMediumAttachment *pMediumAtt = atts[idxAtt];
4435
4436 LONG lPortNum = 0;
4437 hrc = pMediumAtt->COMGETTER(Port)(&lPortNum); H();
4438
4439 BOOL fHotPluggable = FALSE;
4440 hrc = pMediumAtt->COMGETTER(HotPluggable)(&fHotPluggable); H();
4441 if (SUCCEEDED(hrc))
4442 {
4443 PCFGMNODE pPortCfg;
4444 char szName[24];
4445 RTStrPrintf(szName, sizeof(szName), "Port%d", lPortNum);
4446
4447 InsertConfigNode(pCfg, szName, &pPortCfg);
4448 InsertConfigInteger(pPortCfg, "Hotpluggable", fHotPluggable ? 1 : 0);
4449 }
4450 }
4451
4452 /* BIOS configuration values, first AHCI controller only. */
4453 if ( !pBusMgr->hasPCIDevice("ahci", 1)
4454 && pBiosCfg)
4455 {
4456 InsertConfigString(pBiosCfg, "SataHardDiskDevice", "ahci");
4457 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigSata); H();
4458 }
4459
4460 /* Attach the status driver */
4461 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk) | RT_BIT_32(DeviceType_DVD),
4462 cPorts, &paLedDevType, &mapMediumAttachments, pszCtrlDev, ulInstance);
4463 break;
4464 }
4465
4466 case StorageControllerType_PIIX3:
4467 case StorageControllerType_PIIX4:
4468 case StorageControllerType_ICH6:
4469 {
4470 /*
4471 * IDE (update this when the main interface changes)
4472 */
4473 hrc = pBusMgr->assignPCIDevice("piix3ide", pCtlInst); H();
4474 InsertConfigString(pCfg, "Type", controllerString(enmCtrlType));
4475
4476 /* Attach the status driver */
4477 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk) | RT_BIT_32(DeviceType_DVD),
4478 4, &paLedDevType, &mapMediumAttachments, pszCtrlDev, ulInstance);
4479
4480 /* IDE flavors */
4481 aCtrlNodes[StorageControllerType_PIIX3] = pDev;
4482 aCtrlNodes[StorageControllerType_PIIX4] = pDev;
4483 aCtrlNodes[StorageControllerType_ICH6] = pDev;
4484 break;
4485 }
4486
4487 case StorageControllerType_I82078:
4488 {
4489 /*
4490 * i82078 Floppy drive controller
4491 */
4492 *pfFdcEnabled = true;
4493 InsertConfigInteger(pCfg, "IRQ", 6);
4494 InsertConfigInteger(pCfg, "DMA", 2);
4495 InsertConfigInteger(pCfg, "MemMapped", 0 );
4496 InsertConfigInteger(pCfg, "IOBase", 0x3f0);
4497
4498 /* Attach the status driver */
4499 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_Floppy),
4500 2, NULL, &mapMediumAttachments, pszCtrlDev, ulInstance);
4501 break;
4502 }
4503
4504 case StorageControllerType_LsiLogicSas:
4505 {
4506 hrc = pBusMgr->assignPCIDevice("lsilogicsas", pCtlInst); H();
4507
4508 InsertConfigString(pCfg, "ControllerType", "SAS1068");
4509 InsertConfigInteger(pCfg, "Bootable", fBootable);
4510
4511 /* BIOS configuration values, first SCSI controller only. */
4512 if ( !pBusMgr->hasPCIDevice("lsilogic", 0)
4513 && !pBusMgr->hasPCIDevice("buslogic", 0)
4514 && !pBusMgr->hasPCIDevice("lsilogicsas", 1)
4515 && pBiosCfg)
4516 {
4517 InsertConfigString(pBiosCfg, "ScsiHardDiskDevice", "lsilogicsas");
4518 hrc = SetBiosDiskInfo(pMachine, pCfg, pBiosCfg, controllerName, apszBiosConfigScsi); H();
4519 }
4520
4521 ULONG cPorts = 0;
4522 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
4523 InsertConfigInteger(pCfg, "NumPorts", cPorts);
4524
4525 /* Attach the status driver */
4526 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk) | RT_BIT_32(DeviceType_DVD) /*?*/,
4527 8, &paLedDevType, &mapMediumAttachments, pszCtrlDev, ulInstance);
4528 break;
4529 }
4530
4531 case StorageControllerType_USB:
4532 {
4533 if (pUsbDevices)
4534 {
4535 /*
4536 * USB MSDs are handled a bit different as the device instance
4537 * doesn't match the storage controller instance but the port.
4538 */
4539 InsertConfigNode(pUsbDevices, "Msd", &pDev);
4540 pCtlInst = pDev;
4541 }
4542 else
4543 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
4544 N_("There is no USB controller enabled but there\n"
4545 "is at least one USB storage device configured for this VM.\n"
4546 "To fix this problem either enable the USB controller or remove\n"
4547 "the storage device from the VM"));
4548 break;
4549 }
4550
4551 case StorageControllerType_NVMe:
4552 {
4553 hrc = pBusMgr->assignPCIDevice("nvme", pCtlInst); H();
4554
4555 ULONG cPorts = 0;
4556 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
4557 InsertConfigInteger(pCfg, "NamespacesMax", cPorts);
4558
4559 /* Attach the status driver */
4560 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk),
4561 cPorts, NULL, &mapMediumAttachments, pszCtrlDev, ulInstance);
4562 break;
4563 }
4564
4565 case StorageControllerType_VirtioSCSI:
4566 {
4567 hrc = pBusMgr->assignPCIDevice("virtio-scsi", pCtlInst); H();
4568
4569 ULONG cPorts = 0;
4570 hrc = ctrls[i]->COMGETTER(PortCount)(&cPorts); H();
4571 InsertConfigInteger(pCfg, "NumTargets", cPorts);
4572 InsertConfigInteger(pCfg, "Bootable", fBootable);
4573
4574 /* Attach the status driver */
4575 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk) | RT_BIT_32(DeviceType_DVD) /*?*/,
4576 cPorts, &paLedDevType, &mapMediumAttachments, pszCtrlDev, ulInstance);
4577 break;
4578 }
4579
4580 default:
4581 AssertLogRelMsgFailedReturn(("invalid storage controller type: %d\n", enmCtrlType), VERR_MAIN_CONFIG_CONSTRUCTOR_IPE);
4582 }
4583
4584 /* Attach the media to the storage controllers. */
4585 com::SafeIfaceArray<IMediumAttachment> atts;
4586 hrc = pMachine->GetMediumAttachmentsOfController(controllerName.raw(),
4587 ComSafeArrayAsOutParam(atts)); H();
4588
4589 /* Builtin I/O cache - per device setting. */
4590 BOOL fBuiltinIOCache = true;
4591 hrc = pMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache); H();
4592
4593 bool fInsertDiskIntegrityDrv = false;
4594 Bstr strDiskIntegrityFlag;
4595 hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/EnableDiskIntegrityDriver").raw(),
4596 strDiskIntegrityFlag.asOutParam());
4597 if ( hrc == S_OK
4598 && strDiskIntegrityFlag == "1")
4599 fInsertDiskIntegrityDrv = true;
4600
4601 for (size_t j = 0; j < atts.size(); ++j)
4602 {
4603 IMediumAttachment *pMediumAtt = atts[j];
4604 int vrc = i_configMediumAttachment(pszCtrlDev,
4605 ulInstance,
4606 enmBus,
4607 !!fUseHostIOCache,
4608 enmCtrlType == StorageControllerType_NVMe ? false : !!fBuiltinIOCache,
4609 fInsertDiskIntegrityDrv,
4610 false /* fSetupMerge */,
4611 0 /* uMergeSource */,
4612 0 /* uMergeTarget */,
4613 pMediumAtt,
4614 mMachineState,
4615 NULL /* phrc */,
4616 false /* fAttachDetach */,
4617 false /* fForceUnmount */,
4618 false /* fHotplug */,
4619 pUVM,
4620 pVMM,
4621 paLedDevType,
4622 NULL /* ppLunL0 */);
4623 if (RT_FAILURE(vrc))
4624 return vrc;
4625 }
4626 H();
4627 }
4628 H();
4629
4630 return VINF_SUCCESS;
4631}
4632
4633
4634int Console::i_configNetworkCtrls(ComPtr<IMachine> pMachine, ComPtr<IPlatformProperties> pPlatformProperties,
4635 ChipsetType_T enmChipset, BusAssignmentManager *pBusMgr, PCVMMR3VTABLE pVMM, PUVM pUVM,
4636 PCFGMNODE pDevices, std::list<BootNic> &llBootNics)
4637{
4638/* Comment out the following line to remove VMWare compatibility hack. */
4639#define VMWARE_NET_IN_SLOT_11
4640
4641 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */
4642 PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */
4643 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4644 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4645
4646 ULONG maxNetworkAdapters;
4647 HRESULT hrc = pPlatformProperties->GetMaxNetworkAdapters(enmChipset, &maxNetworkAdapters); H();
4648
4649#ifdef VMWARE_NET_IN_SLOT_11
4650 bool fSwapSlots3and11 = false;
4651#endif
4652 PCFGMNODE pDevPCNet = NULL; /* PCNet-type devices */
4653 InsertConfigNode(pDevices, "pcnet", &pDevPCNet);
4654#ifdef VBOX_WITH_E1000
4655 PCFGMNODE pDevE1000 = NULL; /* E1000-type devices */
4656 InsertConfigNode(pDevices, "e1000", &pDevE1000);
4657#endif
4658#ifdef VBOX_WITH_VIRTIO
4659 PCFGMNODE pDevVirtioNet = NULL; /* Virtio network devices */
4660 InsertConfigNode(pDevices, "virtio-net", &pDevVirtioNet);
4661#endif /* VBOX_WITH_VIRTIO */
4662 PCFGMNODE pDevDP8390 = NULL; /* DP8390-type devices */
4663 InsertConfigNode(pDevices, "dp8390", &pDevDP8390);
4664 PCFGMNODE pDev3C501 = NULL; /* EtherLink-type devices */
4665 InsertConfigNode(pDevices, "3c501", &pDev3C501);
4666
4667 for (ULONG uInstance = 0; uInstance < maxNetworkAdapters; ++uInstance)
4668 {
4669 ComPtr<INetworkAdapter> networkAdapter;
4670 hrc = pMachine->GetNetworkAdapter(uInstance, networkAdapter.asOutParam()); H();
4671 BOOL fEnabledNetAdapter = FALSE;
4672 hrc = networkAdapter->COMGETTER(Enabled)(&fEnabledNetAdapter); H();
4673 if (!fEnabledNetAdapter)
4674 continue;
4675
4676 /*
4677 * The virtual hardware type. Create appropriate device first.
4678 */
4679 const char *pszAdapterName = "pcnet";
4680 NetworkAdapterType_T adapterType;
4681 hrc = networkAdapter->COMGETTER(AdapterType)(&adapterType); H();
4682 switch (adapterType)
4683 {
4684 case NetworkAdapterType_Am79C970A:
4685 case NetworkAdapterType_Am79C973:
4686 case NetworkAdapterType_Am79C960:
4687 pDev = pDevPCNet;
4688 break;
4689#ifdef VBOX_WITH_E1000
4690 case NetworkAdapterType_I82540EM:
4691 case NetworkAdapterType_I82543GC:
4692 case NetworkAdapterType_I82545EM:
4693 pDev = pDevE1000;
4694 pszAdapterName = "e1000";
4695 break;
4696#endif
4697#ifdef VBOX_WITH_VIRTIO
4698 case NetworkAdapterType_Virtio:
4699 pDev = pDevVirtioNet;
4700 pszAdapterName = "virtio-net";
4701 break;
4702#endif /* VBOX_WITH_VIRTIO */
4703 case NetworkAdapterType_NE1000:
4704 case NetworkAdapterType_NE2000:
4705 case NetworkAdapterType_WD8003:
4706 case NetworkAdapterType_WD8013:
4707 case NetworkAdapterType_ELNK2:
4708 pDev = pDevDP8390;
4709 break;
4710 case NetworkAdapterType_ELNK1:
4711 pDev = pDev3C501;
4712 break;
4713 default:
4714 AssertMsgFailed(("Invalid network adapter type '%d' for slot '%d'", adapterType, uInstance));
4715 return pVMM->pfnVMR3SetError(pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
4716 N_("Invalid network adapter type '%d' for slot '%d'"), adapterType, uInstance);
4717 }
4718
4719 InsertConfigNode(pDev, Utf8StrFmt("%u", uInstance).c_str(), &pInst);
4720 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
4721
4722 int iPCIDeviceNo;
4723 if (enmChipset == ChipsetType_ICH9 || enmChipset == ChipsetType_PIIX3)
4724 {
4725 /* the first network card gets the PCI ID 3, the next 3 gets 8..10,
4726 * next 4 get 16..19. */
4727 switch (uInstance)
4728 {
4729 case 0:
4730 iPCIDeviceNo = 3;
4731 break;
4732 case 1: case 2: case 3:
4733 iPCIDeviceNo = uInstance - 1 + 8;
4734 break;
4735 case 4: case 5: case 6: case 7:
4736 iPCIDeviceNo = uInstance - 4 + 16;
4737 break;
4738 default:
4739 /* auto assignment */
4740 iPCIDeviceNo = -1;
4741 break;
4742 }
4743#ifdef VMWARE_NET_IN_SLOT_11
4744 /*
4745 * Dirty hack for PCI slot compatibility with VMWare,
4746 * it assigns slot 0x11 to the first network controller.
4747 */
4748 if (iPCIDeviceNo == 3 && adapterType == NetworkAdapterType_I82545EM)
4749 {
4750 iPCIDeviceNo = 0x11;
4751 fSwapSlots3and11 = true;
4752 }
4753 else if (iPCIDeviceNo == 0x11 && fSwapSlots3and11)
4754 iPCIDeviceNo = 3;
4755#endif
4756 }
4757 else /* Platforms other than x86 just use the auto assignment, no slot swap hack there. */
4758 iPCIDeviceNo = -1;
4759
4760 PCIBusAddress PCIAddr = PCIBusAddress(0, iPCIDeviceNo, 0);
4761 hrc = pBusMgr->assignPCIDevice(pszAdapterName, pInst, PCIAddr); H();
4762
4763 InsertConfigNode(pInst, "Config", &pCfg);
4764#ifdef VBOX_WITH_2X_4GB_ADDR_SPACE /* not safe here yet. */ /** @todo Make PCNet ring-0 safe on 32-bit mac kernels! */
4765 if (pDev == pDevPCNet)
4766 InsertConfigInteger(pCfg, "R0Enabled", false);
4767#endif
4768 /*
4769 * Collect information needed for network booting and add it to the list.
4770 */
4771 BootNic nic;
4772
4773 nic.mInstance = uInstance;
4774 /* Could be updated by reference, if auto assigned */
4775 nic.mPCIAddress = PCIAddr;
4776
4777 hrc = networkAdapter->COMGETTER(BootPriority)(&nic.mBootPrio); H();
4778
4779 llBootNics.push_back(nic);
4780
4781 /*
4782 * The virtual hardware type. PCNet supports three types, E1000 three,
4783 * but VirtIO only one.
4784 */
4785 switch (adapterType)
4786 {
4787 case NetworkAdapterType_Am79C970A:
4788 InsertConfigString(pCfg, "ChipType", "Am79C970A");
4789 break;
4790 case NetworkAdapterType_Am79C973:
4791 InsertConfigString(pCfg, "ChipType", "Am79C973");
4792 break;
4793 case NetworkAdapterType_Am79C960:
4794 InsertConfigString(pCfg, "ChipType", "Am79C960");
4795 break;
4796 case NetworkAdapterType_I82540EM:
4797 InsertConfigInteger(pCfg, "AdapterType", 0);
4798 break;
4799 case NetworkAdapterType_I82543GC:
4800 InsertConfigInteger(pCfg, "AdapterType", 1);
4801 break;
4802 case NetworkAdapterType_I82545EM:
4803 InsertConfigInteger(pCfg, "AdapterType", 2);
4804 break;
4805 case NetworkAdapterType_Virtio:
4806 break;
4807 case NetworkAdapterType_NE1000:
4808 InsertConfigString(pCfg, "DeviceType", "NE1000");
4809 break;
4810 case NetworkAdapterType_NE2000:
4811 InsertConfigString(pCfg, "DeviceType", "NE2000");
4812 break;
4813 case NetworkAdapterType_WD8003:
4814 InsertConfigString(pCfg, "DeviceType", "WD8003");
4815 break;
4816 case NetworkAdapterType_WD8013:
4817 InsertConfigString(pCfg, "DeviceType", "WD8013");
4818 break;
4819 case NetworkAdapterType_ELNK2:
4820 InsertConfigString(pCfg, "DeviceType", "3C503");
4821 break;
4822 case NetworkAdapterType_ELNK1:
4823 break;
4824 case NetworkAdapterType_Null: AssertFailedBreak(); /* (compiler warnings) */
4825#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
4826 case NetworkAdapterType_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
4827#endif
4828 }
4829
4830 /*
4831 * Get the MAC address and convert it to binary representation
4832 */
4833 Bstr macAddr;
4834 hrc = networkAdapter->COMGETTER(MACAddress)(macAddr.asOutParam()); H();
4835 Assert(!macAddr.isEmpty());
4836 Utf8Str macAddrUtf8 = macAddr;
4837#ifdef VBOX_WITH_CLOUD_NET
4838 NetworkAttachmentType_T eAttachmentType;
4839 hrc = networkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H();
4840 if (eAttachmentType == NetworkAttachmentType_Cloud)
4841 {
4842 mGateway.setLocalMacAddress(macAddrUtf8);
4843 /* We'll insert cloud MAC later, when it becomes known. */
4844 }
4845 else
4846 {
4847#endif
4848 char *macStr = (char*)macAddrUtf8.c_str();
4849 Assert(strlen(macStr) == 12);
4850 RTMAC Mac;
4851 RT_ZERO(Mac);
4852 char *pMac = (char*)&Mac;
4853 for (uint32_t i = 0; i < 6; ++i)
4854 {
4855 int c1 = *macStr++ - '0';
4856 if (c1 > 9)
4857 c1 -= 7;
4858 int c2 = *macStr++ - '0';
4859 if (c2 > 9)
4860 c2 -= 7;
4861 *pMac++ = (char)(((c1 & 0x0f) << 4) | (c2 & 0x0f));
4862 }
4863 InsertConfigBytes(pCfg, "MAC", &Mac, sizeof(Mac));
4864#ifdef VBOX_WITH_CLOUD_NET
4865 }
4866#endif
4867 /*
4868 * Check if the cable is supposed to be unplugged
4869 */
4870 BOOL fCableConnected;
4871 hrc = networkAdapter->COMGETTER(CableConnected)(&fCableConnected); H();
4872 InsertConfigInteger(pCfg, "CableConnected", fCableConnected ? 1 : 0);
4873
4874 /*
4875 * Line speed to report from custom drivers
4876 */
4877 ULONG ulLineSpeed;
4878 hrc = networkAdapter->COMGETTER(LineSpeed)(&ulLineSpeed); H();
4879 InsertConfigInteger(pCfg, "LineSpeed", ulLineSpeed);
4880
4881 /*
4882 * Attach the status driver.
4883 */
4884 i_attachStatusDriver(pInst, DeviceType_Network);
4885
4886 /*
4887 * Configure the network card now
4888 */
4889 bool fIgnoreConnectFailure = mMachineState == MachineState_Restoring;
4890 int vrc = i_configNetwork(pszAdapterName,
4891 uInstance,
4892 0,
4893 networkAdapter,
4894 pCfg,
4895 pLunL0,
4896 pInst,
4897 false /*fAttachDetach*/,
4898 fIgnoreConnectFailure,
4899 pUVM,
4900 pVMM);
4901 if (RT_FAILURE(vrc))
4902 return vrc;
4903 }
4904
4905 return VINF_SUCCESS;
4906}
4907
4908
4909int Console::i_configGuestDbg(ComPtr<IVirtualBox> pVBox, ComPtr<IMachine> pMachine, PCFGMNODE pRoot)
4910{
4911 PCFGMNODE pDbgf;
4912 InsertConfigNode(pRoot, "DBGF", &pDbgf);
4913
4914 /* Paths to search for debug info and such things. */
4915 Bstr bstr;
4916 HRESULT hrc = pMachine->COMGETTER(SettingsFilePath)(bstr.asOutParam()); H();
4917 Utf8Str strSettingsPath(bstr);
4918 bstr.setNull();
4919 strSettingsPath.stripFilename();
4920 strSettingsPath.append("/");
4921
4922 char szHomeDir[RTPATH_MAX + 1];
4923 int vrc2 = RTPathUserHome(szHomeDir, sizeof(szHomeDir) - 1);
4924 if (RT_FAILURE(vrc2))
4925 szHomeDir[0] = '\0';
4926 RTPathEnsureTrailingSeparator(szHomeDir, sizeof(szHomeDir));
4927
4928
4929 Utf8Str strPath;
4930 strPath.append(strSettingsPath).append("debug/;");
4931 strPath.append(strSettingsPath).append(";");
4932 strPath.append("cache*").append(strSettingsPath).append("dbgcache/;"); /* handy for symlinking to actual cache */
4933 strPath.append(szHomeDir);
4934
4935 InsertConfigString(pDbgf, "Path", strPath.c_str());
4936
4937 /* Tracing configuration. */
4938 BOOL fTracingEnabled;
4939 hrc = pMachine->COMGETTER(TracingEnabled)(&fTracingEnabled); H();
4940 if (fTracingEnabled)
4941 InsertConfigInteger(pDbgf, "TracingEnabled", 1);
4942
4943 hrc = pMachine->COMGETTER(TracingConfig)(bstr.asOutParam()); H();
4944 if (fTracingEnabled)
4945 InsertConfigString(pDbgf, "TracingConfig", bstr);
4946
4947 /* Debugger console config. */
4948 PCFGMNODE pDbgc;
4949 InsertConfigNode(pRoot, "DBGC", &pDbgc);
4950
4951 hrc = pVBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H();
4952 Utf8Str strVBoxHome = bstr;
4953 bstr.setNull();
4954 if (strVBoxHome.isNotEmpty())
4955 strVBoxHome.append("/");
4956 else
4957 {
4958 strVBoxHome = szHomeDir;
4959 strVBoxHome.append("/.vbox");
4960 }
4961
4962 Utf8Str strFile(strVBoxHome);
4963 strFile.append("dbgc-history");
4964 InsertConfigString(pDbgc, "HistoryFile", strFile);
4965
4966 strFile = strSettingsPath;
4967 strFile.append("dbgc-init");
4968 InsertConfigString(pDbgc, "LocalInitScript", strFile);
4969
4970 strFile = strVBoxHome;
4971 strFile.append("dbgc-init");
4972 InsertConfigString(pDbgc, "GlobalInitScript", strFile);
4973
4974 /*
4975 * Configure guest debug settings.
4976 */
4977 ComObjPtr<IGuestDebugControl> ptrGstDbgCtrl;
4978 GuestDebugProvider_T enmGstDbgProvider = GuestDebugProvider_None;
4979
4980 hrc = pMachine->COMGETTER(GuestDebugControl)(ptrGstDbgCtrl.asOutParam()); H();
4981 hrc = ptrGstDbgCtrl->COMGETTER(DebugProvider)(&enmGstDbgProvider); H();
4982 if (enmGstDbgProvider != GuestDebugProvider_None)
4983 {
4984 GuestDebugIoProvider_T enmGstDbgIoProvider = GuestDebugIoProvider_None;
4985 hrc = ptrGstDbgCtrl->COMGETTER(DebugIoProvider)(&enmGstDbgIoProvider); H();
4986 hrc = ptrGstDbgCtrl->COMGETTER(DebugAddress)(bstr.asOutParam()); H();
4987 Utf8Str strAddress = bstr;
4988 bstr.setNull();
4989
4990 ULONG ulPort = 0;
4991 hrc = ptrGstDbgCtrl->COMGETTER(DebugPort)(&ulPort); H();
4992
4993 PCFGMNODE pDbgSettings;
4994 InsertConfigNode(pDbgc, "Dbg", &pDbgSettings);
4995 InsertConfigString(pDbgSettings, "Address", strAddress);
4996 InsertConfigInteger(pDbgSettings, "Port", ulPort);
4997
4998 switch (enmGstDbgProvider)
4999 {
5000 case GuestDebugProvider_Native:
5001 InsertConfigString(pDbgSettings, "StubType", "Native");
5002 break;
5003 case GuestDebugProvider_GDB:
5004 InsertConfigString(pDbgSettings, "StubType", "Gdb");
5005 break;
5006 case GuestDebugProvider_KD:
5007 InsertConfigString(pDbgSettings, "StubType", "Kd");
5008 break;
5009 default:
5010 AssertFailed();
5011 break;
5012 }
5013
5014 switch (enmGstDbgIoProvider)
5015 {
5016 case GuestDebugIoProvider_TCP:
5017 InsertConfigString(pDbgSettings, "Provider", "tcp");
5018 break;
5019 case GuestDebugIoProvider_UDP:
5020 InsertConfigString(pDbgSettings, "Provider", "udp");
5021 break;
5022 case GuestDebugIoProvider_IPC:
5023 InsertConfigString(pDbgSettings, "Provider", "ipc");
5024 break;
5025 default:
5026 AssertFailed();
5027 break;
5028 }
5029 }
5030
5031 return VINF_SUCCESS;
5032}
5033
5034
5035int Console::i_configGraphicsController(PCFGMNODE pDevices,
5036 const GraphicsControllerType_T enmGraphicsController,
5037 BusAssignmentManager *pBusMgr,
5038 const ComPtr<IMachine> &ptrMachine,
5039 const ComPtr<IGraphicsAdapter> &ptrGraphicsAdapter,
5040 const ComPtr<IFirmwareSettings> &ptrFirmwareSettings,
5041 bool fForceVmSvga3, bool fExposeLegacyVga)
5042{
5043 // InsertConfig* throws
5044 try
5045 {
5046 PCFGMNODE pDev, pInst, pCfg, pLunL0;
5047 HRESULT hrc;
5048 Bstr bstr;
5049 const char *pcszDevice = "vga";
5050
5051 InsertConfigNode(pDevices, pcszDevice, &pDev);
5052 InsertConfigNode(pDev, "0", &pInst);
5053 InsertConfigInteger(pInst, "Trusted", 1); /* boolean */
5054
5055 hrc = pBusMgr->assignPCIDevice(pcszDevice, pInst); H();
5056