VirtualBox

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

Last change on this file since 20532 was 17768, checked in by vboxsync, 16 years ago

export

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