VirtualBox

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

Last change on this file since 33000 was 32139, checked in by vboxsync, 14 years ago

FTM checkpoint setting

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

© 2023 Oracle
ContactPrivacy policyTerms of Use