VirtualBox

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

Last change on this file since 10839 was 10715, checked in by vboxsync, 16 years ago

Merge async I/O for VMDK backend from private branch

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 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, PPDMDATASEG 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, PPDMDATASEG 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
304static DECLCALLBACK(int) drvblockAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser)
305{
306 PDRVBLOCK pData = PDMIMEDIAASYNCPORT_2_DRVBLOCK(pInterface);
307
308 return pData->pDrvBlockAsyncPort->pfnTransferCompleteNotify(pData->pDrvBlockAsyncPort, pvUser);
309}
310
311/* -=-=-=-=- IBlockBios -=-=-=-=- */
312
313/** Makes a PDRVBLOCK out of a PPDMIBLOCKBIOS. */
314#define PDMIBLOCKBIOS_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockBios))) )
315
316
317/** @copydoc PDMIBLOCKBIOS::pfnGetPCHSGeometry */
318static DECLCALLBACK(int) drvblockGetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pPCHSGeometry)
319{
320 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
321
322 /*
323 * Check the state.
324 */
325 if (!pData->pDrvMedia)
326 return VERR_PDM_MEDIA_NOT_MOUNTED;
327
328 /*
329 * Use configured/cached values if present.
330 */
331 if ( pData->PCHSGeometry.cCylinders > 0
332 && pData->PCHSGeometry.cHeads > 0
333 && pData->PCHSGeometry.cSectors > 0)
334 {
335 *pPCHSGeometry = pData->PCHSGeometry;
336 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pData->PCHSGeometry.cCylinders, pData->PCHSGeometry.cHeads, pData->PCHSGeometry.cSectors));
337 return VINF_SUCCESS;
338 }
339
340 /*
341 * Call media.
342 */
343 int rc = pData->pDrvMedia->pfnBiosGetPCHSGeometry(pData->pDrvMedia, &pData->PCHSGeometry);
344
345 if (VBOX_SUCCESS(rc))
346 {
347 *pPCHSGeometry = pData->PCHSGeometry;
348 LogFlow(("%s: returns %Vrc {%d,%d,%d}\n", __FUNCTION__, rc, pData->PCHSGeometry.cCylinders, pData->PCHSGeometry.cHeads, pData->PCHSGeometry.cSectors));
349 }
350 else if (rc == VERR_NOT_IMPLEMENTED)
351 {
352 rc = VERR_PDM_GEOMETRY_NOT_SET;
353 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
354 }
355 return rc;
356}
357
358
359/** @copydoc PDMIBLOCKBIOS::pfnSetPCHSGeometry */
360static DECLCALLBACK(int) drvblockSetPCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pPCHSGeometry)
361{
362 LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
363 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
364
365 /*
366 * Check the state.
367 */
368 if (!pData->pDrvMedia)
369 {
370 AssertMsgFailed(("Invalid state! Not mounted!\n"));
371 return VERR_PDM_MEDIA_NOT_MOUNTED;
372 }
373
374 /*
375 * Call media. Ignore the not implemented return code.
376 */
377 int rc = pData->pDrvMedia->pfnBiosSetPCHSGeometry(pData->pDrvMedia, pPCHSGeometry);
378
379 if ( VBOX_SUCCESS(rc)
380 || rc == VERR_NOT_IMPLEMENTED)
381 {
382 pData->PCHSGeometry = *pPCHSGeometry;
383 rc = VINF_SUCCESS;
384 }
385 return rc;
386}
387
388
389/** @copydoc PDMIBLOCKBIOS::pfnGetLCHSGeometry */
390static DECLCALLBACK(int) drvblockGetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PPDMMEDIAGEOMETRY pLCHSGeometry)
391{
392 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
393
394 /*
395 * Check the state.
396 */
397 if (!pData->pDrvMedia)
398 return VERR_PDM_MEDIA_NOT_MOUNTED;
399
400 /*
401 * Use configured/cached values if present.
402 */
403 if ( pData->LCHSGeometry.cCylinders > 0
404 && pData->LCHSGeometry.cHeads > 0
405 && pData->LCHSGeometry.cSectors > 0)
406 {
407 *pLCHSGeometry = pData->LCHSGeometry;
408 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pData->LCHSGeometry.cCylinders, pData->LCHSGeometry.cHeads, pData->LCHSGeometry.cSectors));
409 return VINF_SUCCESS;
410 }
411
412 /*
413 * Call media.
414 */
415 int rc = pData->pDrvMedia->pfnBiosGetLCHSGeometry(pData->pDrvMedia, &pData->LCHSGeometry);
416
417 if (VBOX_SUCCESS(rc))
418 {
419 *pLCHSGeometry = pData->LCHSGeometry;
420 LogFlow(("%s: returns %Vrc {%d,%d,%d}\n", __FUNCTION__, rc, pData->LCHSGeometry.cCylinders, pData->LCHSGeometry.cHeads, pData->LCHSGeometry.cSectors));
421 }
422 else if (rc == VERR_NOT_IMPLEMENTED)
423 {
424 rc = VERR_PDM_GEOMETRY_NOT_SET;
425 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
426 }
427 return rc;
428}
429
430
431/** @copydoc PDMIBLOCKBIOS::pfnSetLCHSGeometry */
432static DECLCALLBACK(int) drvblockSetLCHSGeometry(PPDMIBLOCKBIOS pInterface, PCPDMMEDIAGEOMETRY pLCHSGeometry)
433{
434 LogFlow(("%s: cCylinders=%d cHeads=%d cSectors=%d\n", __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
435 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
436
437 /*
438 * Check the state.
439 */
440 if (!pData->pDrvMedia)
441 {
442 AssertMsgFailed(("Invalid state! Not mounted!\n"));
443 return VERR_PDM_MEDIA_NOT_MOUNTED;
444 }
445
446 /*
447 * Call media. Ignore the not implemented return code.
448 */
449 int rc = pData->pDrvMedia->pfnBiosSetLCHSGeometry(pData->pDrvMedia, pLCHSGeometry);
450
451 if ( VBOX_SUCCESS(rc)
452 || rc == VERR_NOT_IMPLEMENTED)
453 {
454 pData->LCHSGeometry = *pLCHSGeometry;
455 rc = VINF_SUCCESS;
456 }
457 return rc;
458}
459
460
461/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
462static DECLCALLBACK(bool) drvblockIsVisible(PPDMIBLOCKBIOS pInterface)
463{
464 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
465 LogFlow(("drvblockIsVisible: returns %d\n", pData->fBiosVisible));
466 return pData->fBiosVisible;
467}
468
469
470/** @copydoc PDMIBLOCKBIOS::pfnGetType */
471static DECLCALLBACK(PDMBLOCKTYPE) drvblockBiosGetType(PPDMIBLOCKBIOS pInterface)
472{
473 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
474 LogFlow(("drvblockBiosGetType: returns %d\n", pData->enmType));
475 return pData->enmType;
476}
477
478
479
480/* -=-=-=-=- IMount -=-=-=-=- */
481
482/** Makes a PDRVBLOCK out of a PPDMIMOUNT. */
483#define PDMIMOUNT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMount)) )
484
485
486/** @copydoc PDMIMOUNT::pfnMount */
487static DECLCALLBACK(int) drvblockMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
488{
489 LogFlow(("drvblockMount: pszFilename=%p:{%s} pszCoreDriver=%p:{%s}\n", pszFilename, pszFilename, pszCoreDriver, pszCoreDriver));
490 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
491
492 /*
493 * Validate state.
494 */
495 if (pData->pDrvMedia)
496 {
497 AssertMsgFailed(("Already mounted\n"));
498 return VERR_PDM_MEDIA_MOUNTED;
499 }
500
501 /*
502 * Prepare configuration.
503 */
504 if (pszFilename)
505 {
506 int rc = pData->pDrvIns->pDrvHlp->pfnMountPrepare(pData->pDrvIns, pszFilename, pszCoreDriver);
507 if (VBOX_FAILURE(rc))
508 {
509 Log(("drvblockMount: Prepare failed for \"%s\" rc=%Vrc\n", pszFilename, rc));
510 return rc;
511 }
512 }
513
514 /*
515 * Attach the media driver and query it's interface.
516 */
517 PPDMIBASE pBase;
518 int rc = pData->pDrvIns->pDrvHlp->pfnAttach(pData->pDrvIns, &pBase);
519 if (VBOX_FAILURE(rc))
520 {
521 Log(("drvblockMount: Attach failed rc=%Vrc\n", rc));
522 return rc;
523 }
524
525 pData->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
526 if (pData->pDrvMedia)
527 {
528 /*
529 * Initialize state.
530 */
531 pData->fLocked = false;
532 pData->PCHSGeometry.cCylinders = 0;
533 pData->PCHSGeometry.cHeads = 0;
534 pData->PCHSGeometry.cSectors = 0;
535 pData->LCHSGeometry.cCylinders = 0;
536 pData->LCHSGeometry.cHeads = 0;
537 pData->LCHSGeometry.cSectors = 0;
538#ifdef VBOX_PERIODIC_FLUSH
539 pData->cbDataWritten = 0;
540#endif /* VBOX_PERIODIC_FLUSH */
541
542 /*
543 * Notify driver/device above us.
544 */
545 if (pData->pDrvMountNotify)
546 pData->pDrvMountNotify->pfnMountNotify(pData->pDrvMountNotify);
547 Log(("drvblockMount: Success\n"));
548 return VINF_SUCCESS;
549 }
550 else
551 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
552
553 /*
554 * Failed, detatch the media driver.
555 */
556 AssertMsgFailed(("No media interface!\n"));
557 int rc2 = pData->pDrvIns->pDrvHlp->pfnDetach(pData->pDrvIns);
558 AssertRC(rc2);
559 pData->pDrvMedia = NULL;
560 return rc;
561}
562
563
564/** @copydoc PDMIMOUNT::pfnUnmount */
565static DECLCALLBACK(int) drvblockUnmount(PPDMIMOUNT pInterface, bool fForce)
566{
567 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
568
569 /*
570 * Validate state.
571 */
572 if (!pData->pDrvMedia)
573 {
574 Log(("drvblockUmount: Not mounted\n"));
575 return VERR_PDM_MEDIA_NOT_MOUNTED;
576 }
577 if (pData->fLocked && !fForce)
578 {
579 Log(("drvblockUmount: Locked\n"));
580 return VERR_PDM_MEDIA_LOCKED;
581 }
582
583 /* Media is no longer locked even if it was previously. */
584 pData->fLocked = false;
585
586 /*
587 * Detach the media driver and query it's interface.
588 */
589 int rc = pData->pDrvIns->pDrvHlp->pfnDetach(pData->pDrvIns);
590 if (VBOX_FAILURE(rc))
591 {
592 Log(("drvblockUnmount: Detach failed rc=%Vrc\n", rc));
593 return rc;
594 }
595 Assert(!pData->pDrvMedia);
596
597 /*
598 * Notify driver/device above us.
599 */
600 if (pData->pDrvMountNotify)
601 pData->pDrvMountNotify->pfnUnmountNotify(pData->pDrvMountNotify);
602 Log(("drvblockUnmount: success\n"));
603 return VINF_SUCCESS;
604}
605
606
607/** @copydoc PDMIMOUNT::pfnIsMounted */
608static DECLCALLBACK(bool) drvblockIsMounted(PPDMIMOUNT pInterface)
609{
610 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
611 return pData->pDrvMedia != NULL;
612}
613
614/** @copydoc PDMIMOUNT::pfnLock */
615static DECLCALLBACK(int) drvblockLock(PPDMIMOUNT pInterface)
616{
617 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
618 Log(("drvblockLock: %d -> %d\n", pData->fLocked, true));
619 pData->fLocked = true;
620 return VINF_SUCCESS;
621}
622
623/** @copydoc PDMIMOUNT::pfnUnlock */
624static DECLCALLBACK(int) drvblockUnlock(PPDMIMOUNT pInterface)
625{
626 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
627 Log(("drvblockUnlock: %d -> %d\n", pData->fLocked, false));
628 pData->fLocked = false;
629 return VINF_SUCCESS;
630}
631
632/** @copydoc PDMIMOUNT::pfnIsLocked */
633static DECLCALLBACK(bool) drvblockIsLocked(PPDMIMOUNT pInterface)
634{
635 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
636 return pData->fLocked;
637}
638
639
640/* -=-=-=-=- IBase -=-=-=-=- */
641
642/** @copydoc PDMIBASE::pfnQueryInterface. */
643static DECLCALLBACK(void *) drvblockQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
644{
645 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
646 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
647 switch (enmInterface)
648 {
649 case PDMINTERFACE_BASE:
650 return &pDrvIns->IBase;
651 case PDMINTERFACE_BLOCK:
652 return &pData->IBlock;
653 case PDMINTERFACE_BLOCK_BIOS:
654 return pData->fBiosVisible ? &pData->IBlockBios : NULL;
655 case PDMINTERFACE_MOUNT:
656 return pData->fMountable ? &pData->IMount : NULL;
657 case PDMINTERFACE_BLOCK_ASYNC:
658 return pData->pDrvMediaAsync ? &pData->IBlockAsync : NULL;
659 case PDMINTERFACE_MEDIA_ASYNC_PORT:
660 return pData->pDrvBlockAsyncPort ? &pData->IMediaAsyncPort : NULL;
661 default:
662 return NULL;
663 }
664}
665
666
667/* -=-=-=-=- driver interface -=-=-=-=- */
668
669/** @copydoc FNPDMDRVDETACH. */
670static DECLCALLBACK(void) drvblockDetach(PPDMDRVINS pDrvIns)
671{
672 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
673 pData->pDrvMedia = NULL;
674 pData->pDrvMediaAsync = NULL;
675}
676
677/**
678 * Reset notification.
679 *
680 * @returns VBox status.
681 * @param pDevIns The driver instance data.
682 */
683static DECLCALLBACK(void) drvblockReset(PPDMDRVINS pDrvIns)
684{
685 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
686
687 pData->fLocked = false;
688}
689
690/**
691 * Construct a block driver instance.
692 *
693 * @returns VBox status.
694 * @param pDrvIns The driver instance data.
695 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
696 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
697 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
698 * iInstance it's expected to be used a bit in this function.
699 */
700static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
701{
702 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
703 LogFlow(("drvblockConstruct: iInstance=%d\n", pDrvIns->iInstance));
704
705 /*
706 * Validate configuration.
707 */
708#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
709 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0FlushInterval\0IgnoreFlush\0"))
710#else /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
711 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Mountable\0"))
712#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
713 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
714
715 /*
716 * Initialize most of the data members.
717 */
718 pData->pDrvIns = pDrvIns;
719
720 /* IBase. */
721 pDrvIns->IBase.pfnQueryInterface = drvblockQueryInterface;
722
723 /* IBlock. */
724 pData->IBlock.pfnRead = drvblockRead;
725 pData->IBlock.pfnWrite = drvblockWrite;
726 pData->IBlock.pfnFlush = drvblockFlush;
727 pData->IBlock.pfnIsReadOnly = drvblockIsReadOnly;
728 pData->IBlock.pfnGetSize = drvblockGetSize;
729 pData->IBlock.pfnGetType = drvblockGetType;
730 pData->IBlock.pfnGetUuid = drvblockGetUuid;
731
732 /* IBlockBios. */
733 pData->IBlockBios.pfnGetPCHSGeometry = drvblockGetPCHSGeometry;
734 pData->IBlockBios.pfnSetPCHSGeometry = drvblockSetPCHSGeometry;
735 pData->IBlockBios.pfnGetLCHSGeometry = drvblockGetLCHSGeometry;
736 pData->IBlockBios.pfnSetLCHSGeometry = drvblockSetLCHSGeometry;
737 pData->IBlockBios.pfnIsVisible = drvblockIsVisible;
738 pData->IBlockBios.pfnGetType = drvblockBiosGetType;
739
740 /* IMount. */
741 pData->IMount.pfnMount = drvblockMount;
742 pData->IMount.pfnUnmount = drvblockUnmount;
743 pData->IMount.pfnIsMounted = drvblockIsMounted;
744 pData->IMount.pfnLock = drvblockLock;
745 pData->IMount.pfnUnlock = drvblockUnlock;
746 pData->IMount.pfnIsLocked = drvblockIsLocked;
747
748 /* IBlockAsync. */
749 pData->IBlockAsync.pfnStartRead = drvblockAsyncReadStart;
750 pData->IBlockAsync.pfnStartWrite = drvblockAsyncWriteStart;
751
752 /* IMediaAsyncPort. */
753 pData->IMediaAsyncPort.pfnTransferCompleteNotify = drvblockAsyncTransferCompleteNotify;
754
755 /*
756 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
757 */
758 pData->pDrvBlockPort = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
759 if (!pData->pDrvBlockPort)
760 {
761 AssertMsgFailed(("Configuration error: No block port interface above!\n"));
762 return VERR_PDM_MISSING_INTERFACE_ABOVE;
763 }
764
765 /* Try to get the optional async block port interface above. */
766 pData->pDrvBlockAsyncPort = (PPDMIBLOCKASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_ASYNC_PORT);
767
768 pData->pDrvMountNotify = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
769
770 /*
771 * Query configuration.
772 */
773 /* type */
774 char *psz;
775 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Type", &psz);
776 if (VBOX_FAILURE(rc))
777 {
778 AssertMsgFailed(("Configuration error: Failed to obtain the type, rc=%Vrc.\n", rc));
779 return VERR_PDM_BLOCK_NO_TYPE;
780 }
781 if (!strcmp(psz, "HardDisk"))
782 pData->enmType = PDMBLOCKTYPE_HARD_DISK;
783 else if (!strcmp(psz, "DVD"))
784 pData->enmType = PDMBLOCKTYPE_DVD;
785 else if (!strcmp(psz, "CDROM"))
786 pData->enmType = PDMBLOCKTYPE_CDROM;
787 else if (!strcmp(psz, "Floppy 2.88"))
788 pData->enmType = PDMBLOCKTYPE_FLOPPY_2_88;
789 else if (!strcmp(psz, "Floppy 1.44"))
790 pData->enmType = PDMBLOCKTYPE_FLOPPY_1_44;
791 else if (!strcmp(psz, "Floppy 1.20"))
792 pData->enmType = PDMBLOCKTYPE_FLOPPY_1_20;
793 else if (!strcmp(psz, "Floppy 720"))
794 pData->enmType = PDMBLOCKTYPE_FLOPPY_720;
795 else if (!strcmp(psz, "Floppy 360"))
796 pData->enmType = PDMBLOCKTYPE_FLOPPY_360;
797 else
798 {
799 AssertMsgFailed(("Configuration error: Unknown type \"%s\".\n", psz));
800 MMR3HeapFree(psz);
801 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
802 }
803 Log2(("drvblockConstruct: enmType=%d\n", pData->enmType));
804 MMR3HeapFree(psz); psz = NULL;
805
806 /* Mountable */
807 rc = CFGMR3QueryBool(pCfgHandle, "Mountable", &pData->fMountable);
808 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
809 pData->fMountable = false;
810 else if (VBOX_FAILURE(rc))
811 {
812 AssertMsgFailed(("Configuration error: Query \"Mountable\" resulted in %Vrc.\n", rc));
813 return rc;
814 }
815
816 /* Locked */
817 rc = CFGMR3QueryBool(pCfgHandle, "Locked", &pData->fLocked);
818 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
819 pData->fLocked = false;
820 else if (VBOX_FAILURE(rc))
821 {
822 AssertMsgFailed(("Configuration error: Query \"Locked\" resulted in %Vrc.\n", rc));
823 return rc;
824 }
825
826 /* BIOS visible */
827 rc = CFGMR3QueryBool(pCfgHandle, "BIOSVisible", &pData->fBiosVisible);
828 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
829 pData->fBiosVisible = true;
830 else if (VBOX_FAILURE(rc))
831 {
832 AssertMsgFailed(("Configuration error: Query \"BIOSVisible\" resulted in %Vrc.\n", rc));
833 return rc;
834 }
835
836 /** @todo AttachFailError is currently completely ignored. */
837
838 /* Cylinders */
839 rc = CFGMR3QueryU32(pCfgHandle, "Cylinders", &pData->LCHSGeometry.cCylinders);
840 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
841 pData->LCHSGeometry.cCylinders = 0;
842 else if (VBOX_FAILURE(rc))
843 {
844 AssertMsgFailed(("Configuration error: Query \"Cylinders\" resulted in %Vrc.\n", rc));
845 return rc;
846 }
847
848 /* Heads */
849 rc = CFGMR3QueryU32(pCfgHandle, "Heads", &pData->LCHSGeometry.cHeads);
850 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
851 pData->LCHSGeometry.cHeads = 0;
852 else if (VBOX_FAILURE(rc))
853 {
854 AssertMsgFailed(("Configuration error: Query \"Heads\" resulted in %Vrc.\n", rc));
855 return rc;
856 }
857
858 /* Sectors */
859 rc = CFGMR3QueryU32(pCfgHandle, "Sectors", &pData->LCHSGeometry.cSectors);
860 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
861 pData->LCHSGeometry.cSectors = 0;
862 else if (VBOX_FAILURE(rc))
863 {
864 AssertMsgFailed(("Configuration error: Query \"Sectors\" resulted in %Vrc.\n", rc));
865 return rc;
866 }
867
868 /* Uuid */
869 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Uuid", &psz);
870 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
871 RTUuidClear(&pData->Uuid);
872 else if (VBOX_SUCCESS(rc))
873 {
874 rc = RTUuidFromStr(&pData->Uuid, psz);
875 if (VBOX_FAILURE(rc))
876 {
877 AssertMsgFailed(("Configuration error: Uuid from string failed on \"%s\", rc=%Vrc.\n", psz, rc));
878 MMR3HeapFree(psz);
879 return rc;
880 }
881 MMR3HeapFree(psz); psz = NULL;
882 }
883 else
884 {
885 AssertMsgFailed(("Configuration error: Failed to obtain the type, rc=%Vrc.\n", rc));
886 return VERR_PDM_BLOCK_NO_TYPE;
887 }
888
889#ifdef VBOX_PERIODIC_FLUSH
890 rc = CFGMR3QueryU32(pCfgHandle, "FlushInterval", &pData->cbFlushInterval);
891 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
892 pData->cbFlushInterval = 0;
893 else if (VBOX_FAILURE(rc))
894 {
895 AssertMsgFailed(("Configuration error: Query \"FlushInterval\" resulted in %Vrc.\n", rc));
896 return rc;
897 }
898#endif /* VBOX_PERIODIC_FLUSH */
899
900#ifdef VBOX_IGNORE_FLUSH
901 rc = CFGMR3QueryBool(pCfgHandle, "IgnoreFlush", &pData->fIgnoreFlush);
902 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
903 pData->fIgnoreFlush = true; /* The default is to ignore flushes. */
904 else if (VBOX_FAILURE(rc))
905 {
906 AssertMsgFailed(("Configuration error: Query \"IgnoreFlush\" resulted in %Vrc.\n", rc));
907 return rc;
908 }
909#endif /* VBOX_IGNORE_FLUSH */
910
911 /*
912 * Try attach driver below and query it's media interface.
913 */
914 PPDMIBASE pBase;
915 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
916 if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
917 && pData->enmType != PDMBLOCKTYPE_HARD_DISK)
918 return VINF_SUCCESS;
919 if (VBOX_FAILURE(rc))
920 {
921 AssertMsgFailed(("Failed to attach driver below us! rc=%Vra\n", rc));
922 return rc;
923 }
924 pData->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
925 if (!pData->pDrvMedia)
926 {
927 AssertMsgFailed(("Configuration error: No media or async media interface below!\n"));
928 return VERR_PDM_MISSING_INTERFACE_BELOW;
929 }
930
931 /* Try to get the optional async interface. */
932 pData->pDrvMediaAsync = (PPDMIMEDIAASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA_ASYNC);
933
934 if (RTUuidIsNull(&pData->Uuid))
935 {
936 if (pData->enmType == PDMBLOCKTYPE_HARD_DISK)
937 pData->pDrvMedia->pfnGetUuid(pData->pDrvMedia, &pData->Uuid);
938 }
939
940 return VINF_SUCCESS;
941}
942
943
944/**
945 * Block driver registration record.
946 */
947const PDMDRVREG g_DrvBlock =
948{
949 /* u32Version */
950 PDM_DRVREG_VERSION,
951 /* szDriverName */
952 "Block",
953 /* pszDescription */
954 "Generic block driver.",
955 /* fFlags */
956 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
957 /* fClass. */
958 PDM_DRVREG_CLASS_BLOCK,
959 /* cMaxInstances */
960 ~0,
961 /* cbInstance */
962 sizeof(DRVBLOCK),
963 /* pfnConstruct */
964 drvblockConstruct,
965 /* pfnDestruct */
966 NULL,
967 /* pfnIOCtl */
968 NULL,
969 /* pfnPowerOn */
970 NULL,
971 /* pfnReset */
972 drvblockReset,
973 /* pfnSuspend */
974 NULL,
975 /* pfnResume */
976 NULL,
977 /* pfnDetach */
978 drvblockDetach
979};
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