VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvBlock.cpp@ 9152

Last change on this file since 9152 was 8529, checked in by vboxsync, 17 years ago

Unlock media on VM reset.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * Generic block driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_BLOCK
28#include <VBox/pdmdrv.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31
32#include <string.h>
33
34#include "Builtins.h"
35
36
37/** @def VBOX_PERIODIC_FLUSH
38 * Enable support for periodically flushing the VDI to disk. This may prove
39 * useful for those nasty problems with the ultra-slow host filesystems.
40 * If this is enabled, it can be configured via the CFGM key
41 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/FlushInterval". <x>
42 * must be replaced with the correct LUN number of the disk that should
43 * do the periodic flushes. The value of the key is the number of bytes
44 * written between flushes. A value of 0 (the default) denotes no flushes. */
45#define VBOX_PERIODIC_FLUSH
46
47/** @def VBOX_IGNORE_FLUSH
48 * Enable support for ignoring VDI flush requests. This can be useful for
49 * filesystems that show bad guest IDE write performance (especially with
50 * Windows guests). NOTE that this does not disable the flushes caused by
51 * the periodic flush cache feature above.
52 * If this feature is enabled, it can be configured via the CFGM key
53 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/IgnoreFlush". <x>
54 * must be replaced with the correct LUN number of the disk that should
55 * ignore flush requests. The value of the key is a boolean. The default
56 * is to ignore flushes, i.e. true. */
57#define VBOX_IGNORE_FLUSH
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62/**
63 * Block driver instance data.
64 */
65typedef struct DRVBLOCK
66{
67 /** Pointer driver instance. */
68 PPDMDRVINS pDrvIns;
69 /** Drive type. */
70 PDMBLOCKTYPE enmType;
71 /** Locked indicator. */
72 bool fLocked;
73 /** Mountable indicator. */
74 bool fMountable;
75 /** Visible to the BIOS. */
76 bool fBiosVisible;
77#ifdef VBOX_PERIODIC_FLUSH
78 /** HACK: Configuration value for number of bytes written after which to flush. */
79 uint32_t cbFlushInterval;
80 /** HACK: Current count for the number of bytes written since the last flush. */
81 uint32_t cbDataWritten;
82#endif /* VBOX_PERIODIC_FLUSH */
83#ifdef VBOX_IGNORE_FLUSH
84 /** HACK: Disable flushes for this drive. */
85 bool fIgnoreFlush;
86#endif /* VBOX_IGNORE_FLUSH */
87 /** Pointer to the media driver below us.
88 * This is NULL if the media is not mounted. */
89 PPDMIMEDIA pDrvMedia;
90 /** Pointer to the block port interface above us. */
91 PPDMIBLOCKPORT pDrvBlockPort;
92 /** Pointer to the mount notify interface above us. */
93 PPDMIMOUNTNOTIFY pDrvMountNotify;
94 /** Our block interface. */
95 PDMIBLOCK IBlock;
96 /** Our block interface. */
97 PDMIBLOCKBIOS IBlockBios;
98 /** Our mountable interface. */
99 PDMIMOUNT IMount;
100
101 /** Pointer to the async media driver below us.
102 * This is NULL if the media is not mounted. */
103 PPDMIMEDIAASYNC pDrvMediaAsync;
104 /** Our media async port. */
105 PDMIMEDIAASYNCPORT IMediaAsyncPort;
106 /** Pointer to the async block port interface above us. */
107 PPDMIBLOCKASYNCPORT pDrvBlockAsyncPort;
108 /** Our async block interface. */
109 PDMIBLOCKASYNC IBlockAsync;
110
111 /** Uuid of the drive. */
112 RTUUID Uuid;
113
114 /** BIOS PCHS Geometry. */
115 PDMMEDIAGEOMETRY PCHSGeometry;
116 /** BIOS LCHS Geometry. */
117 PDMMEDIAGEOMETRY LCHSGeometry;
118} DRVBLOCK, *PDRVBLOCK;
119
120
121/* -=-=-=-=- IBlock -=-=-=-=- */
122
123/** Makes a PDRVBLOCK out of a PPDMIBLOCK. */
124#define PDMIBLOCK_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlock)) )
125
126/** @copydoc PDMIBLOCK::pfnRead */
127static DECLCALLBACK(int) drvblockRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
128{
129 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
130
131 /*
132 * Check the state.
133 */
134 if (!pData->pDrvMedia)
135 {
136 AssertMsgFailed(("Invalid state! Not mounted!\n"));
137 return VERR_PDM_MEDIA_NOT_MOUNTED;
138 }
139
140 int rc = pData->pDrvMedia->pfnRead(pData->pDrvMedia, off, pvBuf, cbRead);
141 return rc;
142}
143
144
145/** @copydoc PDMIBLOCK::pfnWrite */
146static DECLCALLBACK(int) drvblockWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
147{
148 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
149
150 /*
151 * Check the state.
152 */
153 if (!pData->pDrvMedia)
154 {
155 AssertMsgFailed(("Invalid state! Not mounted!\n"));
156 return VERR_PDM_MEDIA_NOT_MOUNTED;
157 }
158
159 int rc = pData->pDrvMedia->pfnWrite(pData->pDrvMedia, off, pvBuf, cbWrite);
160#ifdef VBOX_PERIODIC_FLUSH
161 if (pData->cbFlushInterval)
162 {
163 pData->cbDataWritten += cbWrite;
164 if (pData->cbDataWritten > pData->cbFlushInterval)
165 {
166 pData->cbDataWritten = 0;
167 pData->pDrvMedia->pfnFlush(pData->pDrvMedia);
168 }
169 }
170#endif /* VBOX_PERIODIC_FLUSH */
171
172 return rc;
173}
174
175
176/** @copydoc PDMIBLOCK::pfnFlush */
177static DECLCALLBACK(int) drvblockFlush(PPDMIBLOCK pInterface)
178{
179 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
180
181 /*
182 * Check the state.
183 */
184 if (!pData->pDrvMedia)
185 {
186 AssertMsgFailed(("Invalid state! Not mounted!\n"));
187 return VERR_PDM_MEDIA_NOT_MOUNTED;
188 }
189
190#ifdef VBOX_IGNORE_FLUSH
191 if (pData->fIgnoreFlush)
192 return VINF_SUCCESS;
193#endif /* VBOX_IGNORE_FLUSH */
194
195 int rc = pData->pDrvMedia->pfnFlush(pData->pDrvMedia);
196 if (rc == VERR_NOT_IMPLEMENTED)
197 rc = VINF_SUCCESS;
198 return rc;
199}
200
201
202/** @copydoc PDMIBLOCK::pfnIsReadOnly */
203static DECLCALLBACK(bool) drvblockIsReadOnly(PPDMIBLOCK pInterface)
204{
205 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
206
207 /*
208 * Check the state.
209 */
210 if (!pData->pDrvMedia)
211 return false;
212
213 bool fRc = pData->pDrvMedia->pfnIsReadOnly(pData->pDrvMedia);
214 return fRc;
215}
216
217
218/** @copydoc PDMIBLOCK::pfnGetSize */
219static DECLCALLBACK(uint64_t) drvblockGetSize(PPDMIBLOCK pInterface)
220{
221 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
222
223 /*
224 * Check the state.
225 */
226 if (!pData->pDrvMedia)
227 return 0;
228
229 uint64_t cb = pData->pDrvMedia->pfnGetSize(pData->pDrvMedia);
230 LogFlow(("drvblockGetSize: returns %llu\n", cb));
231 return cb;
232}
233
234
235/** @copydoc PDMIBLOCK::pfnGetType */
236static DECLCALLBACK(PDMBLOCKTYPE) drvblockGetType(PPDMIBLOCK pInterface)
237{
238 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
239 LogFlow(("drvblockGetType: returns %d\n", pData->enmType));
240 return pData->enmType;
241}
242
243
244/** @copydoc PDMIBLOCK::pfnGetUuid */
245static DECLCALLBACK(int) drvblockGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
246{
247 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
248
249 /*
250 * Copy the uuid.
251 */
252 *pUuid = pData->Uuid;
253 return VINF_SUCCESS;
254}
255
256/* -=-=-=-=- IBlockAsync -=-=-=-=- */
257
258/** Makes a PDRVBLOCK out of a PPDMIBLOCKASYNC. */
259#define PDMIBLOCKASYNC_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockAsync)) )
260
261/** @copydoc PDMIBLOCKASYNC::pfnRead */
262static DECLCALLBACK(int) drvblockAsyncReadStart(PPDMIBLOCKASYNC pInterface, uint64_t off, PPDMIDATATRANSPORTSEG pSeg, unsigned cSeg, size_t cbRead, void *pvUser)
263{
264 PDRVBLOCK pData = PDMIBLOCKASYNC_2_DRVBLOCK(pInterface);
265
266 /*
267 * Check the state.
268 */
269 if (!pData->pDrvMediaAsync)
270 {
271 AssertMsgFailed(("Invalid state! Not mounted!\n"));
272 return VERR_PDM_MEDIA_NOT_MOUNTED;
273 }
274
275 int rc = pData->pDrvMediaAsync->pfnStartRead(pData->pDrvMediaAsync, off, pSeg, cSeg, cbRead, pvUser);
276 return rc;
277}
278
279
280/** @copydoc PDMIBLOCKASYNC::pfnWrite */
281static DECLCALLBACK(int) drvblockAsyncWriteStart(PPDMIBLOCKASYNC pInterface, uint64_t off, PPDMIDATATRANSPORTSEG pSeg, unsigned cSeg, size_t cbWrite, void *pvUser)
282{
283 PDRVBLOCK pData = PDMIBLOCKASYNC_2_DRVBLOCK(pInterface);
284
285 /*
286 * Check the state.
287 */
288 if (!pData->pDrvMediaAsync)
289 {
290 AssertMsgFailed(("Invalid state! Not mounted!\n"));
291 return VERR_PDM_MEDIA_NOT_MOUNTED;
292 }
293
294 int rc = pData->pDrvMediaAsync->pfnStartWrite(pData->pDrvMediaAsync, off, pSeg, cSeg, cbWrite, pvUser);
295
296 return rc;
297}
298
299/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
300
301/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
302#define PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMediaAsyncPort))) )
303
304
305static DECLCALLBACK(int) drvblockAsyncReadCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, uint64_t uOffset, PPDMIDATATRANSPORTSEG pSeg, unsigned cSeg, size_t cbRead, void *pvUser)
306{
307 PDRVBLOCK pData = PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface);
308
309 return pData->pDrvBlockAsyncPort->pfnReadCompleteNotify(pData->pDrvBlockAsyncPort, uOffset, pSeg, cSeg, cbRead, pvUser);
310}
311
312static DECLCALLBACK(int) drvblockAsyncWriteCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, uint64_t uOffset, PPDMIDATATRANSPORTSEG pSeg, unsigned cSeg, size_t cbWritten, void *pvUser)
313{
314 PDRVBLOCK pData = PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface);
315
316#ifdef VBOX_PERIODIC_FLUSH
317 if (pData->cbFlushInterval)
318 {
319 pData->cbDataWritten += cbWritten;
320 if (pData->cbDataWritten > pData->cbFlushInterval)
321 {
322 pData->cbDataWritten = 0;
323 pData->pDrvMedia->pfnFlush(pData->pDrvMedia);
324 }
325 }
326#endif /* VBOX_PERIODIC_FLUSH */
327
328 return pData->pDrvBlockAsyncPort->pfnWriteCompleteNotify(pData->pDrvBlockAsyncPort, uOffset, pSeg, cSeg, cbWritten, pvUser);
329}
330
331/* -=-=-=-=- IBlockBios -=-=-=-=- */
332
333/** Makes a PDRVBLOCK out of a PPDMIBLOCKBIOS. */
334#define PDMIBLOCKBIOS_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockBios))) )
335
336
337/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
338static DECLCALLBACK(int) drvblockGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
339{
340 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
341
342 /*
343 * Check the state.
344 */
345 if (!pData->pDrvMedia)
346 return VERR_PDM_MEDIA_NOT_MOUNTED;
347
348 /*
349 * Use configured/cached values if present.
350 */
351 if ( pData->PCHSGeometry.cCylinders > 0
352 && pData->PCHSGeometry.cHeads > 0
353 && pData->PCHSGeometry.cSectors > 0)
354 {
355 *pPCHSGeometry = pData->PCHSGeometry;
356 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pData->PCHSGeometry.cCylinders, pData->PCHSGeometry.cHeads, pData->PCHSGeometry.cSectors));
357 return VINF_SUCCESS;
358 }
359
360 /*
361 * Call media.
362 */
363 int rc = pData->pDrvMedia->pfnBiosGetPCHSGeometry(pData->pDrvMedia, &pData->PCHSGeometry);
364
365 if (VBOX_SUCCESS(rc))
366 {
367 *pPCHSGeometry = pData->PCHSGeometry;
368 LogFlow(("%s: returns %Vrc {%d,%d,%d}\n", __FUNCTION__, rc, pData->PCHSGeometry.cCylinders, pData->PCHSGeometry.cHeads, pData->PCHSGeometry.cSectors));
369 }
370 else if (rc == VERR_NOT_IMPLEMENTED)
371 {
372 rc = VERR_PDM_GEOMETRY_NOT_SET;
373 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
374 }
375 return rc;
376}
377
378
379/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
380static DECLCALLBACK(int) drvblockSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
381{
382 LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
383 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
384
385 /*
386 * Check the state.
387 */
388 if (!pData->pDrvMedia)
389 {
390 AssertMsgFailed(("Invalid state! Not mounted!\n"));
391 return VERR_PDM_MEDIA_NOT_MOUNTED;
392 }
393
394 /*
395 * Call media. Ignore the not implemented return code.
396 */
397 int rc = pData->pDrvMedia->pfnBiosSetPCHSGeometry(pData->pDrvMedia, pPCHSGeometry);
398
399 if ( VBOX_SUCCESS(rc)
400 || rc == VERR_NOT_IMPLEMENTED)
401 {
402 pData->PCHSGeometry = *pPCHSGeometry;
403 rc = VINF_SUCCESS;
404 }
405 return rc;
406}
407
408
409/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
410static DECLCALLBACK(int) drvblockGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
411{
412 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
413
414 /*
415 * Check the state.
416 */
417 if (!pData->pDrvMedia)
418 return VERR_PDM_MEDIA_NOT_MOUNTED;
419
420 /*
421 * Use configured/cached values if present.
422 */
423 if ( pData->LCHSGeometry.cCylinders > 0
424 && pData->LCHSGeometry.cHeads > 0
425 && pData->LCHSGeometry.cSectors > 0)
426 {
427 *pLCHSGeometry = pData->LCHSGeometry;
428 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pData->LCHSGeometry.cCylinders, pData->LCHSGeometry.cHeads, pData->LCHSGeometry.cSectors));
429 return VINF_SUCCESS;
430 }
431
432 /*
433 * Call media.
434 */
435 int rc = pData->pDrvMedia->pfnBiosGetLCHSGeometry(pData->pDrvMedia, &pData->LCHSGeometry);
436
437 if (VBOX_SUCCESS(rc))
438 {
439 *pLCHSGeometry = pData->LCHSGeometry;
440 LogFlow(("%s: returns %Vrc {%d,%d,%d}\n", __FUNCTION__, rc, pData->LCHSGeometry.cCylinders, pData->LCHSGeometry.cHeads, pData->LCHSGeometry.cSectors));
441 }
442 else if (rc == VERR_NOT_IMPLEMENTED)
443 {
444 rc = VERR_PDM_GEOMETRY_NOT_SET;
445 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
446 }
447 return rc;
448}
449
450
451/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
452static DECLCALLBACK(int) drvblockSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
453{
454 LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
455 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
456
457 /*
458 * Check the state.
459 */
460 if (!pData->pDrvMedia)
461 {
462 AssertMsgFailed(("Invalid state! Not mounted!\n"));
463 return VERR_PDM_MEDIA_NOT_MOUNTED;
464 }
465
466 /*
467 * Call media. Ignore the not implemented return code.
468 */
469 int rc = pData->pDrvMedia->pfnBiosSetLCHSGeometry(pData->pDrvMedia, pLCHSGeometry);
470
471 if ( VBOX_SUCCESS(rc)
472 || rc == VERR_NOT_IMPLEMENTED)
473 {
474 pData->LCHSGeometry = *pLCHSGeometry;
475 rc = VINF_SUCCESS;
476 }
477 return rc;
478}
479
480
481/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
482static DECLCALLBACK(bool) drvblockIsVisible(PPDMIBLOCKBIOS pInterface)
483{
484 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
485 LogFlow(("drvblockIsVisible: returns %d\n", pData->fBiosVisible));
486 return pData->fBiosVisible;
487}
488
489
490/** @copydoc PDMIBLOCKBIOS::pfnGetType */
491static DECLCALLBACK(PDMBLOCKTYPE) drvblockBiosGetType(PPDMIBLOCKBIOS pInterface)
492{
493 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
494 LogFlow(("drvblockBiosGetType: returns %d\n", pData->enmType));
495 return pData->enmType;
496}
497
498
499
500/* -=-=-=-=- IMount -=-=-=-=- */
501
502/** Makes a PDRVBLOCK out of a PPDMIMOUNT. */
503#define PDMIMOUNT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMount)) )
504
505
506/** @copydoc PDMIMOUNT::pfnMount */
507static DECLCALLBACK(int) drvblockMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
508{
509 LogFlow(("drvblockMount: pszFilename=%p:{%s} pszCoreDriver=%p:{%s}\n", pszFilename, pszFilename, pszCoreDriver, pszCoreDriver));
510 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
511
512 /*
513 * Validate state.
514 */
515 if (pData->pDrvMedia)
516 {
517 AssertMsgFailed(("Already mounted\n"));
518 return VERR_PDM_MEDIA_MOUNTED;
519 }
520
521 /*
522 * Prepare configuration.
523 */
524 if (pszFilename)
525 {
526 int rc = pData->pDrvIns->pDrvHlp->pfnMountPrepare(pData->pDrvIns, pszFilename, pszCoreDriver);
527 if (VBOX_FAILURE(rc))
528 {
529 Log(("drvblockMount: Prepare failed for \"%s\" rc=%Vrc\n", pszFilename, rc));
530 return rc;
531 }
532 }
533
534 /*
535 * Attach the media driver and query it's interface.
536 */
537 PPDMIBASE pBase;
538 int rc = pData->pDrvIns->pDrvHlp->pfnAttach(pData->pDrvIns, &pBase);
539 if (VBOX_FAILURE(rc))
540 {
541 Log(("drvblockMount: Attach failed rc=%Vrc\n", rc));
542 return rc;
543 }
544
545 pData->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
546 if (pData->pDrvMedia)
547 {
548 /*
549 * Initialize state.
550 */
551 pData->fLocked = false;
552 pData->PCHSGeometry.cCylinders = 0;
553 pData->PCHSGeometry.cHeads = 0;
554 pData->PCHSGeometry.cSectors = 0;
555 pData->LCHSGeometry.cCylinders = 0;
556 pData->LCHSGeometry.cHeads = 0;
557 pData->LCHSGeometry.cSectors = 0;
558#ifdef VBOX_PERIODIC_FLUSH
559 pData->cbDataWritten = 0;
560#endif /* VBOX_PERIODIC_FLUSH */
561
562 /*
563 * Notify driver/device above us.
564 */
565 if (pData->pDrvMountNotify)
566 pData->pDrvMountNotify->pfnMountNotify(pData->pDrvMountNotify);
567 Log(("drvblockMount: Success\n"));
568 return VINF_SUCCESS;
569 }
570 else
571 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
572
573 /*
574 * Failed, detatch the media driver.
575 */
576 AssertMsgFailed(("No media interface!\n"));
577 int rc2 = pData->pDrvIns->pDrvHlp->pfnDetach(pData->pDrvIns);
578 AssertRC(rc2);
579 pData->pDrvMedia = NULL;
580 return rc;
581}
582
583
584/** @copydoc PDMIMOUNT::pfnUnmount */
585static DECLCALLBACK(int) drvblockUnmount(PPDMIMOUNT pInterface, bool fForce)
586{
587 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
588
589 /*
590 * Validate state.
591 */
592 if (!pData->pDrvMedia)
593 {
594 Log(("drvblockUmount: Not mounted\n"));
595 return VERR_PDM_MEDIA_NOT_MOUNTED;
596 }
597 if (pData->fLocked && !fForce)
598 {
599 Log(("drvblockUmount: Locked\n"));
600 return VERR_PDM_MEDIA_LOCKED;
601 }
602
603 /* Media is no longer locked even if it was previously. */
604 pData->fLocked = false;
605
606 /*
607 * Detach the media driver and query it's interface.
608 */
609 int rc = pData->pDrvIns->pDrvHlp->pfnDetach(pData->pDrvIns);
610 if (VBOX_FAILURE(rc))
611 {
612 Log(("drvblockUnmount: Detach failed rc=%Vrc\n", rc));
613 return rc;
614 }
615 Assert(!pData->pDrvMedia);
616
617 /*
618 * Notify driver/device above us.
619 */
620 if (pData->pDrvMountNotify)
621 pData->pDrvMountNotify->pfnUnmountNotify(pData->pDrvMountNotify);
622 Log(("drvblockUnmount: success\n"));
623 return VINF_SUCCESS;
624}
625
626
627/** @copydoc PDMIMOUNT::pfnIsMounted */
628static DECLCALLBACK(bool) drvblockIsMounted(PPDMIMOUNT pInterface)
629{
630 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
631 return pData->pDrvMedia != NULL;
632}
633
634/** @copydoc PDMIMOUNT::pfnLock */
635static DECLCALLBACK(int) drvblockLock(PPDMIMOUNT pInterface)
636{
637 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
638 Log(("drvblockLock: %d -> %d\n", pData->fLocked, true));
639 pData->fLocked = true;
640 return VINF_SUCCESS;
641}
642
643/** @copydoc PDMIMOUNT::pfnUnlock */
644static DECLCALLBACK(int) drvblockUnlock(PPDMIMOUNT pInterface)
645{
646 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
647 Log(("drvblockUnlock: %d -> %d\n", pData->fLocked, false));
648 pData->fLocked = false;
649 return VINF_SUCCESS;
650}
651
652/** @copydoc PDMIMOUNT::pfnIsLocked */
653static DECLCALLBACK(bool) drvblockIsLocked(PPDMIMOUNT pInterface)
654{
655 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
656 return pData->fLocked;
657}
658
659
660/* -=-=-=-=- IBase -=-=-=-=- */
661
662/** @copydoc PDMIBASE::pfnQueryInterface. */
663static DECLCALLBACK(void *) drvblockQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
664{
665 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
666 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
667 switch (enmInterface)
668 {
669 case PDMINTERFACE_BASE:
670 return &pDrvIns->IBase;
671 case PDMINTERFACE_BLOCK:
672 return &pData->IBlock;
673 case PDMINTERFACE_BLOCK_BIOS:
674 return pData->fBiosVisible ? &pData->IBlockBios : NULL;
675 case PDMINTERFACE_MOUNT:
676 return pData->fMountable ? &pData->IMount : NULL;
677 case PDMINTERFACE_BLOCK_ASYNC:
678 return pData->pDrvMediaAsync ? &pData->IBlockAsync : NULL;
679 case PDMINTERFACE_MEDIA_ASYNC_PORT:
680 return &pData->IMediaAsyncPort;
681 default:
682 return NULL;
683 }
684}
685
686
687/* -=-=-=-=- driver interface -=-=-=-=- */
688
689/** @copydoc FNPDMDRVDETACH. */
690static DECLCALLBACK(void) drvblockDetach(PPDMDRVINS pDrvIns)
691{
692 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
693 pData->pDrvMedia = NULL;
694 pData->pDrvMediaAsync = NULL;
695}
696
697/**
698 * Reset notification.
699 *
700 * @returns VBox status.
701 * @param pDevIns The driver instance data.
702 */
703static DECLCALLBACK(void) drvblockReset(PPDMDRVINS pDrvIns)
704{
705 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
706
707 pData->fLocked = false;
708}
709
710/**
711 * Construct a block driver instance.
712 *
713 * @returns VBox status.
714 * @param pDrvIns The driver instance data.
715 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
716 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
717 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
718 * iInstance it's expected to be used a bit in this function.
719 */
720static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
721{
722 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
723 LogFlow(("drvblockConstruct: iInstance=%d\n", pDrvIns->iInstance));
724
725 /*
726 * Validate configuration.
727 */
728#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
729 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0FlushInterval\0IgnoreFlush\0"))
730#else /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
731 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0"))
732#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
733 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
734
735 /*
736 * Initialize most of the data members.
737 */
738 pData->pDrvIns = pDrvIns;
739
740 /* IBase. */
741 pDrvIns->IBase.pfnQueryInterface = drvblockQueryInterface;
742
743 /* IBlock. */
744 pData->IBlock.pfnRead = drvblockRead;
745 pData->IBlock.pfnWrite = drvblockWrite;
746 pData->IBlock.pfnFlush = drvblockFlush;
747 pData->IBlock.pfnIsReadOnly = drvblockIsReadOnly;
748 pData->IBlock.pfnGetSize = drvblockGetSize;
749 pData->IBlock.pfnGetType = drvblockGetType;
750 pData->IBlock.pfnGetUuid = drvblockGetUuid;
751
752 /* IBlockBios. */
753 pData->IBlockBios.pfnGetPCHSGeometry = drvblockGetPCHSGeometry;
754 pData->IBlockBios.pfnSetPCHSGeometry = drvblockSetPCHSGeometry;
755 pData->IBlockBios.pfnGetLCHSGeometry = drvblockGetLCHSGeometry;
756 pData->IBlockBios.pfnSetLCHSGeometry = drvblockSetLCHSGeometry;
757 pData->IBlockBios.pfnIsVisible = drvblockIsVisible;
758 pData->IBlockBios.pfnGetType = drvblockBiosGetType;
759
760 /* IMount. */
761 pData->IMount.pfnMount = drvblockMount;
762 pData->IMount.pfnUnmount = drvblockUnmount;
763 pData->IMount.pfnIsMounted = drvblockIsMounted;
764 pData->IMount.pfnLock = drvblockLock;
765 pData->IMount.pfnUnlock = drvblockUnlock;
766 pData->IMount.pfnIsLocked = drvblockIsLocked;
767
768 /* IBlockAsync. */
769 pData->IBlockAsync.pfnStartRead = drvblockAsyncReadStart;
770 pData->IBlockAsync.pfnStartWrite = drvblockAsyncWriteStart;
771
772 /* IMediaAsyncPort. */
773 pData->IMediaAsyncPort.pfnReadCompleteNotify = drvblockAsyncReadCompleteNotify;
774 pData->IMediaAsyncPort.pfnWriteCompleteNotify = drvblockAsyncWriteCompleteNotify;
775
776 /*
777 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
778 */
779 pData->pDrvBlockPort = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
780 if (!pData->pDrvBlockPort)
781 {
782 AssertMsgFailed(("Configuration error: No block port interface above!\n"));
783 return VERR_PDM_MISSING_INTERFACE_ABOVE;
784 }
785
786 /* Try to get the optional async block port interface above. */
787 pData->pDrvBlockAsyncPort = (PPDMIBLOCKASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_ASYNC_PORT);
788
789 pData->pDrvMountNotify = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
790
791 /*
792 * Query configuration.
793 */
794 /* type */
795 char *psz;
796 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Type", &psz);
797 if (VBOX_FAILURE(rc))
798 {
799 AssertMsgFailed(("Configuration error: Failed to obtain the type, rc=%Vrc.\n", rc));
800 return VERR_PDM_BLOCK_NO_TYPE;
801 }
802 if (!strcmp(psz, "HardDisk"))
803 pData->enmType = PDMBLOCKTYPE_HARD_DISK;
804 else if (!strcmp(psz, "DVD"))
805 pData->enmType = PDMBLOCKTYPE_DVD;
806 else if (!strcmp(psz, "CDROM"))
807 pData->enmType = PDMBLOCKTYPE_CDROM;
808 else if (!strcmp(psz, "Floppy 2.88"))
809 pData->enmType = PDMBLOCKTYPE_FLOPPY_2_88;
810 else if (!strcmp(psz, "Floppy 1.44"))
811 pData->enmType = PDMBLOCKTYPE_FLOPPY_1_44;
812 else if (!strcmp(psz, "Floppy 1.20"))
813 pData->enmType = PDMBLOCKTYPE_FLOPPY_1_20;
814 else if (!strcmp(psz, "Floppy 720"))
815 pData->enmType = PDMBLOCKTYPE_FLOPPY_720;
816 else if (!strcmp(psz, "Floppy 360"))
817 pData->enmType = PDMBLOCKTYPE_FLOPPY_360;
818 else
819 {
820 AssertMsgFailed(("Configuration error: Unknown type \"%s\".\n", psz));
821 MMR3HeapFree(psz);
822 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
823 }
824 Log2(("drvblockConstruct: enmType=%d\n", pData->enmType));
825 MMR3HeapFree(psz); psz = NULL;
826
827 /* Mountable */
828 rc = CFGMR3QueryBool(pCfgHandle, "Mountable", &pData->fMountable);
829 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
830 pData->fMountable = false;
831 else if (VBOX_FAILURE(rc))
832 {
833 AssertMsgFailed(("Configuration error: Query \"Mountable\" resulted in %Vrc.\n", rc));
834 return rc;
835 }
836
837 /* Locked */
838 rc = CFGMR3QueryBool(pCfgHandle, "Locked", &pData->fLocked);
839 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
840 pData->fLocked = false;
841 else if (VBOX_FAILURE(rc))
842 {
843 AssertMsgFailed(("Configuration error: Query \"Locked\" resulted in %Vrc.\n", rc));
844 return rc;
845 }
846
847 /* BIOS visible */
848 rc = CFGMR3QueryBool(pCfgHandle, "BIOSVisible", &pData->fBiosVisible);
849 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
850 pData->fBiosVisible = true;
851 else if (VBOX_FAILURE(rc))
852 {
853 AssertMsgFailed(("Configuration error: Query \"BIOSVisible\" resulted in %Vrc.\n", rc));
854 return rc;
855 }
856
857 /** @todo AttachFailError is currently completely ignored. */
858
859 /* Cylinders */
860 rc = CFGMR3QueryU32(pCfgHandle, "Cylinders", &pData->LCHSGeometry.cCylinders);
861 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
862 pData->LCHSGeometry.cCylinders = 0;
863 else if (VBOX_FAILURE(rc))
864 {
865 AssertMsgFailed(("Configuration error: Query \"Cylinders\" resulted in %Vrc.\n", rc));
866 return rc;
867 }
868
869 /* Heads */
870 rc = CFGMR3QueryU32(pCfgHandle, "Heads", &pData->LCHSGeometry.cHeads);
871 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
872 pData->LCHSGeometry.cHeads = 0;
873 else if (VBOX_FAILURE(rc))
874 {
875 AssertMsgFailed(("Configuration error: Query \"Heads\" resulted in %Vrc.\n", rc));
876 return rc;
877 }
878
879 /* Sectors */
880 rc = CFGMR3QueryU32(pCfgHandle, "Sectors", &pData->LCHSGeometry.cSectors);
881 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
882 pData->LCHSGeometry.cSectors = 0;
883 else if (VBOX_FAILURE(rc))
884 {
885 AssertMsgFailed(("Configuration error: Query \"Sectors\" resulted in %Vrc.\n", rc));
886 return rc;
887 }
888
889 /* Uuid */
890 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Uuid", &psz);
891 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
892 RTUuidClear(&pData->Uuid);
893 else if (VBOX_SUCCESS(rc))
894 {
895 rc = RTUuidFromStr(&pData->Uuid, psz);
896 if (VBOX_FAILURE(rc))
897 {
898 AssertMsgFailed(("Configuration error: Uuid from string failed on \"%s\", rc=%Vrc.\n", psz, rc));
899 MMR3HeapFree(psz);
900 return rc;
901 }
902 MMR3HeapFree(psz); psz = NULL;
903 }
904 else
905 {
906 AssertMsgFailed(("Configuration error: Failed to obtain the type, rc=%Vrc.\n", rc));
907 return VERR_PDM_BLOCK_NO_TYPE;
908 }
909
910#ifdef VBOX_PERIODIC_FLUSH
911 rc = CFGMR3QueryU32(pCfgHandle, "FlushInterval", &pData->cbFlushInterval);
912 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
913 pData->cbFlushInterval = 0;
914 else if (VBOX_FAILURE(rc))
915 {
916 AssertMsgFailed(("Configuration error: Query \"FlushInterval\" resulted in %Vrc.\n", rc));
917 return rc;
918 }
919#endif /* VBOX_PERIODIC_FLUSH */
920
921#ifdef VBOX_IGNORE_FLUSH
922 rc = CFGMR3QueryBool(pCfgHandle, "IgnoreFlush", &pData->fIgnoreFlush);
923 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
924 pData->fIgnoreFlush = true; /* The default is to ignore flushes. */
925 else if (VBOX_FAILURE(rc))
926 {
927 AssertMsgFailed(("Configuration error: Query \"IgnoreFlush\" resulted in %Vrc.\n", rc));
928 return rc;
929 }
930#endif /* VBOX_IGNORE_FLUSH */
931
932 /*
933 * Try attach driver below and query it's media interface.
934 */
935 PPDMIBASE pBase;
936 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
937 if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
938 && pData->enmType != PDMBLOCKTYPE_HARD_DISK)
939 return VINF_SUCCESS;
940 if (VBOX_FAILURE(rc))
941 {
942 AssertMsgFailed(("Failed to attach driver below us! rc=%Vra\n", rc));
943 return rc;
944 }
945 pData->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
946 if (!pData->pDrvMedia)
947 {
948 AssertMsgFailed(("Configuration error: No media or async media interface below!\n"));
949 return VERR_PDM_MISSING_INTERFACE_BELOW;
950 }
951
952 /* Try to get the optional async interface. */
953 pData->pDrvMediaAsync = (PPDMIMEDIAASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA_ASYNC);
954
955 if (RTUuidIsNull(&pData->Uuid))
956 {
957 if (pData->enmType == PDMBLOCKTYPE_HARD_DISK)
958 pData->pDrvMedia->pfnGetUuid(pData->pDrvMedia, &pData->Uuid);
959 }
960
961 return VINF_SUCCESS;
962}
963
964
965/**
966 * Block driver registration record.
967 */
968const PDMDRVREG g_DrvBlock =
969{
970 /* u32Version */
971 PDM_DRVREG_VERSION,
972 /* szDriverName */
973 "Block",
974 /* pszDescription */
975 "Generic block driver.",
976 /* fFlags */
977 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
978 /* fClass. */
979 PDM_DRVREG_CLASS_BLOCK,
980 /* cMaxInstances */
981 ~0,
982 /* cbInstance */
983 sizeof(DRVBLOCK),
984 /* pfnConstruct */
985 drvblockConstruct,
986 /* pfnDestruct */
987 NULL,
988 /* pfnIOCtl */
989 NULL,
990 /* pfnPowerOn */
991 NULL,
992 /* pfnReset */
993 drvblockReset,
994 /* pfnSuspend */
995 NULL,
996 /* pfnResume */
997 NULL,
998 /* pfnDetach */
999 drvblockDetach
1000};
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