VirtualBox

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

Last change on this file since 38867 was 38622, checked in by vboxsync, 13 years ago

AHCI+DrvBlock+DrvVD: Add support for the TRIM command and connect with the VD discard support.

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