VirtualBox

source: vbox/trunk/src/VBox/Main/StorageControllerImpl.cpp@ 23012

Last change on this file since 23012 was 22173, checked in by vboxsync, 15 years ago

Main: the big XML settings rework. Move XML reading/writing out of interface implementation code into separate layer so it can handle individual settings versions in the future.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: StorageControllerImpl.cpp 22173 2009-08-11 15:38:59Z vboxsync $ */
2
3/** @file
4 *
5 * Implementation of IStorageController.
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "StorageControllerImpl.h"
25#include "MachineImpl.h"
26#include "VirtualBoxImpl.h"
27#include "Logging.h"
28
29#include <iprt/string.h>
30#include <iprt/cpputils.h>
31
32#include <VBox/err.h>
33#include <VBox/settings.h>
34
35#include <algorithm>
36
37// defines
38/////////////////////////////////////////////////////////////////////////////
39
40// constructor / destructor
41/////////////////////////////////////////////////////////////////////////////
42
43DEFINE_EMPTY_CTOR_DTOR (StorageController)
44
45HRESULT StorageController::FinalConstruct()
46{
47 return S_OK;
48}
49
50void StorageController::FinalRelease()
51{
52 uninit();
53}
54
55// public initializer/uninitializer for internal purposes only
56/////////////////////////////////////////////////////////////////////////////
57
58/**
59 * Initializes the storage controller object.
60 *
61 * @returns COM result indicator.
62 * @param aParent Pointer to our parent object.
63 * @param aName Name of the storage controller.
64 */
65HRESULT StorageController::init(Machine *aParent,
66 const Utf8Str &aName,
67 StorageBus_T aStorageBus)
68{
69 LogFlowThisFunc(("aParent=%p aName=\"%s\"\n", aParent, aName.raw()));
70
71 ComAssertRet(aParent && !aName.isEmpty(), E_INVALIDARG);
72 if ( (aStorageBus <= StorageBus_Null)
73 || (aStorageBus > StorageBus_SCSI))
74 return setError (E_INVALIDARG,
75 tr ("Invalid storage connection type"));
76
77 /* Enclose the state transition NotReady->InInit->Ready */
78 AutoInitSpan autoInitSpan(this);
79 AssertReturn(autoInitSpan.isOk(), E_FAIL);
80
81 unconst(mParent) = aParent;
82 /* mPeer is left null */
83
84 /* register with parent early, since uninit() will unconditionally
85 * unregister on failure */
86 mParent->addDependentChild (this);
87
88 mData.allocate();
89
90 mData->strName = aName;
91 mData->mStorageBus = aStorageBus;
92
93 switch (aStorageBus)
94 {
95 case StorageBus_IDE:
96 mData->mPortCount = 2;
97 mData->mStorageControllerType = StorageControllerType_PIIX4;
98 break;
99 case StorageBus_SATA:
100 mData->mPortCount = 30;
101 mData->mStorageControllerType = StorageControllerType_IntelAhci;
102 break;
103 case StorageBus_SCSI:
104 mData->mPortCount = 16;
105 mData->mStorageControllerType = StorageControllerType_LsiLogic;
106 break;
107 }
108
109 /* Confirm a successful initialization */
110 autoInitSpan.setSucceeded();
111
112 return S_OK;
113}
114
115/**
116 * Initializes the object given another object
117 * (a kind of copy constructor). This object shares data with
118 * the object passed as an argument.
119 *
120 * @param aReshare
121 * When false, the original object will remain a data owner.
122 * Otherwise, data ownership will be transferred from the original
123 * object to this one.
124 *
125 * @note This object must be destroyed before the original object
126 * it shares data with is destroyed.
127 *
128 * @note Locks @a aThat object for writing if @a aReshare is @c true, or for
129 * reading if @a aReshare is false.
130 */
131HRESULT StorageController::init (Machine *aParent,
132 StorageController *aThat,
133 bool aReshare /* = false */)
134{
135 LogFlowThisFunc(("aParent=%p, aThat=%p, aReshare=%RTbool\n",
136 aParent, aThat, aReshare));
137
138 ComAssertRet (aParent && aThat, E_INVALIDARG);
139
140 /* Enclose the state transition NotReady->InInit->Ready */
141 AutoInitSpan autoInitSpan(this);
142 AssertReturn(autoInitSpan.isOk(), E_FAIL);
143
144 unconst(mParent) = aParent;
145
146 /* register with parent early, since uninit() will unconditionally
147 * unregister on failure */
148 mParent->addDependentChild (this);
149
150 /* sanity */
151 AutoCaller thatCaller (aThat);
152 AssertComRCReturnRC(thatCaller.rc());
153
154 if (aReshare)
155 {
156 AutoWriteLock thatLock (aThat);
157
158 unconst(aThat->mPeer) = this;
159 mData.attach (aThat->mData);
160 }
161 else
162 {
163 unconst(mPeer) = aThat;
164
165 AutoReadLock thatLock (aThat);
166 mData.share (aThat->mData);
167 }
168
169 /* Confirm successful initialization */
170 autoInitSpan.setSucceeded();
171
172 return S_OK;
173}
174
175/**
176 * Initializes the storage controller object given another guest object
177 * (a kind of copy constructor). This object makes a private copy of data
178 * of the original object passed as an argument.
179 */
180HRESULT StorageController::initCopy (Machine *aParent, StorageController *aThat)
181{
182 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
183
184 ComAssertRet (aParent && aThat, E_INVALIDARG);
185
186 /* Enclose the state transition NotReady->InInit->Ready */
187 AutoInitSpan autoInitSpan(this);
188 AssertReturn(autoInitSpan.isOk(), E_FAIL);
189
190 unconst(mParent) = aParent;
191 /* mPeer is left null */
192
193 mParent->addDependentChild (this);
194
195 AutoCaller thatCaller (aThat);
196 AssertComRCReturnRC(thatCaller.rc());
197
198 AutoReadLock thatlock (aThat);
199 mData.attachCopy (aThat->mData);
200
201 /* Confirm a successful initialization */
202 autoInitSpan.setSucceeded();
203
204 return S_OK;
205}
206
207
208/**
209 * Uninitializes the instance and sets the ready flag to FALSE.
210 * Called either from FinalRelease() or by the parent when it gets destroyed.
211 */
212void StorageController::uninit()
213{
214 LogFlowThisFunc(("\n"));
215
216 /* Enclose the state transition Ready->InUninit->NotReady */
217 AutoUninitSpan autoUninitSpan(this);
218 if (autoUninitSpan.uninitDone())
219 return;
220
221 mData.free();
222
223 mParent->removeDependentChild (this);
224
225 unconst(mPeer).setNull();
226 unconst(mParent).setNull();
227}
228
229
230// IStorageController properties
231/////////////////////////////////////////////////////////////////////////////
232STDMETHODIMP StorageController::COMGETTER(Name) (BSTR *aName)
233{
234 CheckComArgOutPointerValid(aName);
235
236 AutoCaller autoCaller(this);
237 CheckComRCReturnRC(autoCaller.rc());
238
239 /* mName is constant during life time, no need to lock */
240 mData.data()->strName.cloneTo(aName);
241
242 return S_OK;
243}
244
245STDMETHODIMP StorageController::COMGETTER(Bus) (StorageBus_T *aBus)
246{
247 CheckComArgOutPointerValid(aBus);
248
249 AutoCaller autoCaller(this);
250 CheckComRCReturnRC(autoCaller.rc());
251
252 AutoReadLock alock(this);
253
254 *aBus = mData->mStorageBus;
255
256 return S_OK;
257}
258
259STDMETHODIMP StorageController::COMGETTER(ControllerType) (StorageControllerType_T *aControllerType)
260{
261 CheckComArgOutPointerValid(aControllerType);
262
263 AutoCaller autoCaller(this);
264 CheckComRCReturnRC(autoCaller.rc());
265
266 AutoReadLock alock(this);
267
268 *aControllerType = mData->mStorageControllerType;
269
270 return S_OK;
271}
272
273STDMETHODIMP StorageController::COMSETTER(ControllerType) (StorageControllerType_T aControllerType)
274{
275 AutoCaller autoCaller(this);
276 CheckComRCReturnRC(autoCaller.rc());
277
278 AutoWriteLock alock(this);
279
280 HRESULT rc = S_OK;
281
282 switch (mData->mStorageBus)
283 {
284 case StorageBus_IDE:
285 {
286 if ( (aControllerType != StorageControllerType_PIIX3)
287 && (aControllerType != StorageControllerType_PIIX4)
288 && (aControllerType != StorageControllerType_ICH6))
289 rc = E_INVALIDARG;
290 break;
291 }
292 case StorageBus_SATA:
293 {
294 if (aControllerType != StorageControllerType_IntelAhci)
295 rc = E_INVALIDARG;
296 break;
297 }
298 case StorageBus_SCSI:
299 {
300 if ( (aControllerType != StorageControllerType_LsiLogic)
301 && (aControllerType != StorageControllerType_BusLogic))
302 rc = E_INVALIDARG;
303 break;
304 }
305 default:
306 AssertMsgFailed(("Invalid controller type %d\n", mData->mStorageBus));
307 }
308
309 if (!SUCCEEDED(rc))
310 return setError(rc,
311 tr ("Invalid controller type %d"),
312 aControllerType);
313
314 mData->mStorageControllerType = aControllerType;
315
316 return S_OK;
317}
318
319STDMETHODIMP StorageController::COMGETTER(MaxDevicesPerPortCount) (ULONG *aMaxDevices)
320{
321 CheckComArgOutPointerValid(aMaxDevices);
322
323 AutoCaller autoCaller(this);
324 CheckComRCReturnRC(autoCaller.rc());
325
326 AutoReadLock alock(this);
327
328 switch (mData->mStorageBus)
329 {
330 case StorageBus_SATA:
331 case StorageBus_SCSI:
332 {
333 /* SATA and both SCSI controllers only support one device per port. */
334 *aMaxDevices = 1;
335 break;
336 }
337 case StorageBus_IDE:
338 {
339 /* The IDE controllers support 2 devices. One as master and one as slave. */
340 *aMaxDevices = 2;
341 break;
342 }
343 default:
344 AssertMsgFailed(("Invalid controller type %d\n", mData->mStorageBus));
345 }
346
347 return S_OK;
348}
349
350STDMETHODIMP StorageController::COMGETTER(MinPortCount) (ULONG *aMinPortCount)
351{
352 CheckComArgOutPointerValid(aMinPortCount);
353
354 AutoCaller autoCaller(this);
355 CheckComRCReturnRC(autoCaller.rc());
356
357 AutoReadLock alock(this);
358
359 switch (mData->mStorageBus)
360 {
361 case StorageBus_SATA:
362 {
363 *aMinPortCount = 1;
364 break;
365 }
366 case StorageBus_SCSI:
367 {
368 *aMinPortCount = 16;
369 break;
370 }
371 case StorageBus_IDE:
372 {
373 *aMinPortCount = 2;
374 break;
375 }
376 default:
377 AssertMsgFailed(("Invalid controller type %d\n", mData->mStorageBus));
378 }
379
380 return S_OK;
381}
382
383STDMETHODIMP StorageController::COMGETTER(MaxPortCount) (ULONG *aMaxPortCount)
384{
385 CheckComArgOutPointerValid(aMaxPortCount);
386
387 AutoCaller autoCaller(this);
388 CheckComRCReturnRC(autoCaller.rc());
389
390 AutoReadLock alock(this);
391
392 switch (mData->mStorageBus)
393 {
394 case StorageBus_SATA:
395 {
396 *aMaxPortCount = 30;
397 break;
398 }
399 case StorageBus_SCSI:
400 {
401 *aMaxPortCount = 16;
402 break;
403 }
404 case StorageBus_IDE:
405 {
406 *aMaxPortCount = 2;
407 break;
408 }
409 default:
410 AssertMsgFailed(("Invalid controller type %d\n", mData->mStorageBus));
411 }
412
413 return S_OK;
414}
415
416
417STDMETHODIMP StorageController::COMGETTER(PortCount) (ULONG *aPortCount)
418{
419 CheckComArgOutPointerValid(aPortCount);
420
421 AutoCaller autoCaller(this);
422 CheckComRCReturnRC(autoCaller.rc());
423
424 AutoReadLock alock(this);
425
426 *aPortCount = mData->mPortCount;
427
428 return S_OK;
429}
430
431
432STDMETHODIMP StorageController::COMSETTER(PortCount) (ULONG aPortCount)
433{
434 LogFlowThisFunc(("aPortCount=%u\n", aPortCount));
435
436 switch (mData->mStorageBus)
437 {
438 case StorageBus_SATA:
439 {
440 /* AHCI SATA supports a maximum of 30 ports. */
441 if ((aPortCount < 1) || (aPortCount > 30))
442 return setError (E_INVALIDARG,
443 tr ("Invalid port count: %lu (must be in range [%lu, %lu])"),
444 aPortCount, 1, 30);
445 break;
446 }
447 case StorageBus_SCSI:
448 {
449 /*
450 * SCSI does not support setting different ports.
451 * (doesn't make sense here either).
452 * The maximum and minimum is 16 and unless the callee
453 * tries to set a different value we return an error.
454 */
455 if (aPortCount != 16)
456 return setError (E_INVALIDARG,
457 tr ("Invalid port count: %lu (must be in range [%lu, %lu])"),
458 aPortCount, 16, 16);
459 break;
460 }
461 case StorageBus_IDE:
462 {
463 /*
464 * The port count is fixed to 2.
465 */
466 if (aPortCount != 2)
467 return setError (E_INVALIDARG,
468 tr ("Invalid port count: %lu (must be in range [%lu, %lu])"),
469 aPortCount, 2, 2);
470 break;
471 }
472 default:
473 AssertMsgFailed(("Invalid controller type %d\n", mData->mStorageBus));
474 }
475
476 AutoCaller autoCaller(this);
477 CheckComRCReturnRC(autoCaller.rc());
478
479 /* the machine needs to be mutable */
480 Machine::AutoMutableStateDependency adep (mParent);
481 CheckComRCReturnRC(adep.rc());
482
483 AutoWriteLock alock(this);
484
485 if (mData->mPortCount != aPortCount)
486 {
487 mData.backup();
488 mData->mPortCount = aPortCount;
489
490 /* leave the lock for safety */
491 alock.leave();
492
493 mParent->onStorageControllerChange ();
494 }
495
496 return S_OK;
497}
498
499STDMETHODIMP StorageController::COMGETTER(Instance) (ULONG *aInstance)
500{
501 AutoCaller autoCaller(this);
502 CheckComRCReturnRC(autoCaller.rc());
503
504 /* The machine doesn't need to be mutable. */
505
506 AutoReadLock alock(this);
507
508 *aInstance = mInstance;
509
510 return S_OK;
511}
512
513STDMETHODIMP StorageController::COMSETTER(Instance) (ULONG aInstance)
514{
515 AutoCaller autoCaller(this);
516 CheckComRCReturnRC(autoCaller.rc());
517
518 /* The machine doesn't need to be mutable. */
519
520 AutoWriteLock alock(this);
521
522 mInstance = aInstance;
523
524 return S_OK;
525}
526
527// IStorageController methods
528/////////////////////////////////////////////////////////////////////////////
529
530STDMETHODIMP StorageController::GetIDEEmulationPort(LONG DevicePosition, LONG *aPortNumber)
531{
532 CheckComArgOutPointerValid(aPortNumber);
533
534 AutoCaller autoCaller(this);
535 CheckComRCReturnRC(autoCaller.rc());
536
537 AutoReadLock alock(this);
538
539 if (mData->mStorageControllerType != StorageControllerType_IntelAhci)
540 return setError (E_NOTIMPL,
541 tr ("Invalid controller type"));
542
543 switch (DevicePosition)
544 {
545 case 0:
546 *aPortNumber = mData->mPortIde0Master;
547 break;
548 case 1:
549 *aPortNumber = mData->mPortIde0Slave;
550 break;
551 case 2:
552 *aPortNumber = mData->mPortIde1Master;
553 break;
554 case 3:
555 *aPortNumber = mData->mPortIde1Slave;
556 break;
557 default:
558 return E_INVALIDARG;
559 }
560
561 return S_OK;
562}
563
564STDMETHODIMP StorageController::SetIDEEmulationPort(LONG DevicePosition, LONG aPortNumber)
565{
566 AutoCaller autoCaller(this);
567 CheckComRCReturnRC(autoCaller.rc());
568
569 /* the machine needs to be mutable */
570 Machine::AutoMutableStateDependency adep (mParent);
571 CheckComRCReturnRC(adep.rc());
572 AutoWriteLock alock(this);
573
574 if (mData->mStorageControllerType != StorageControllerType_IntelAhci)
575 return setError (E_NOTIMPL,
576 tr ("Invalid controller type"));
577
578 if ((aPortNumber < 0) || (aPortNumber >= 30))
579 return setError (E_INVALIDARG,
580 tr ("Invalid port number: %l (must be in range [%lu, %lu])"),
581 aPortNumber, 0, 29);
582
583 switch (DevicePosition)
584 {
585 case 0:
586 mData->mPortIde0Master = aPortNumber;
587 break;
588 case 1:
589 mData->mPortIde0Slave = aPortNumber;
590 break;
591 case 2:
592 mData->mPortIde1Master = aPortNumber;
593 break;
594 case 3:
595 mData->mPortIde1Slave = aPortNumber;
596 break;
597 default:
598 return E_INVALIDARG;
599 }
600
601 return S_OK;
602}
603
604// public methods only for internal purposes
605/////////////////////////////////////////////////////////////////////////////
606
607/** @note Locks objects for writing! */
608bool StorageController::rollback()
609{
610 AutoCaller autoCaller(this);
611 AssertComRCReturn (autoCaller.rc(), false);
612
613 AutoWriteLock alock(this);
614
615 bool dataChanged = false;
616
617 if (mData.isBackedUp())
618 {
619 /* we need to check all data to see whether anything will be changed
620 * after rollback */
621 dataChanged = mData.hasActualChanges();
622 mData.rollback();
623 }
624
625 return dataChanged;
626}
627
628/**
629 * @note Locks this object for writing, together with the peer object (also
630 * for writing) if there is one.
631 */
632void StorageController::commit()
633{
634 /* sanity */
635 AutoCaller autoCaller(this);
636 AssertComRCReturnVoid (autoCaller.rc());
637
638 /* sanity too */
639 AutoCaller peerCaller (mPeer);
640 AssertComRCReturnVoid (peerCaller.rc());
641
642 /* lock both for writing since we modify both (mPeer is "master" so locked
643 * first) */
644 AutoMultiWriteLock2 alock (mPeer, this);
645
646 if (mData.isBackedUp())
647 {
648 mData.commit();
649 if (mPeer)
650 {
651 // attach new data to the peer and reshare it
652 mPeer->mData.attach (mData);
653 }
654 }
655}
656
657/**
658 * Cancels sharing (if any) by making an independent copy of data.
659 * This operation also resets this object's peer to NULL.
660 *
661 * @note Locks this object for writing, together with the peer object
662 * represented by @a aThat (locked for reading).
663 */
664void StorageController::unshare()
665{
666 /* sanity */
667 AutoCaller autoCaller(this);
668 AssertComRCReturnVoid (autoCaller.rc());
669
670 /* sanity too */
671 AutoCaller peerCaller (mPeer);
672 AssertComRCReturnVoid (peerCaller.rc());
673
674 /* peer is not modified, lock it for reading (mPeer is "master" so locked
675 * first) */
676 AutoMultiLock2 alock (mPeer->rlock(), this->wlock());
677
678 if (mData.isShared())
679 {
680 if (!mData.isBackedUp())
681 mData.backup();
682
683 mData.commit();
684 }
685
686 unconst(mPeer).setNull();
687}
688
689// private methods
690/////////////////////////////////////////////////////////////////////////////
691/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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