VirtualBox

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

Last change on this file since 23319 was 23223, checked in by vboxsync, 15 years ago

API: big medium handling change and lots of assorted other cleanups and fixes

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