VirtualBox

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

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

Correct comment. By default IDE flush commands from the guest are
ignored.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * Generic block driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
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 /** Whether or not enmTranslation is valid. */
78 bool fTranslationSet;
79#ifdef VBOX_PERIODIC_FLUSH
80 /** HACK: Configuration value for number of bytes written after which to flush. */
81 uint32_t cbFlushInterval;
82 /** HACK: Current count for the number of bytes written since the last flush. */
83 uint32_t cbDataWritten;
84#endif /* VBOX_PERIODIC_FLUSH */
85#ifdef VBOX_IGNORE_FLUSH
86 /** HACK: Disable flushes for this drive. */
87 bool fIgnoreFlush;
88#endif /* VBOX_IGNORE_FLUSH */
89 /** Pointer to the media driver below us.
90 * This is NULL if the media is not mounted. */
91 PPDMIMEDIA pDrvMedia;
92 /** Pointer to the block port interface above us. */
93 PPDMIBLOCKPORT pDrvBlockPort;
94 /** Pointer to the mount notify interface above us. */
95 PPDMIMOUNTNOTIFY pDrvMountNotify;
96 /** Our block interface. */
97 PDMIBLOCK IBlock;
98 /** Our block interface. */
99 PDMIBLOCKBIOS IBlockBios;
100 /** Our mountable interface. */
101 PDMIMOUNT IMount;
102
103 /** Uuid of the drive. */
104 RTUUID Uuid;
105
106 /** BIOS Geometry: Translation mode. */
107 PDMBIOSTRANSLATION enmTranslation;
108 /** BIOS Geometry: Cylinders. */
109 uint32_t cCylinders;
110 /** BIOS Geometry: Heads. */
111 uint32_t cHeads;
112 /** BIOS Geometry: Sectors. */
113 uint32_t cSectors;
114} DRVBLOCK, *PDRVBLOCK;
115
116
117/* -=-=-=-=- IBlock -=-=-=-=- */
118
119/** Makes a PDRVBLOCK out of a PPDMIBLOCK. */
120#define PDMIBLOCK_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlock)) )
121
122
123/** @copydoc PDMIBLOCK::pfnRead */
124static DECLCALLBACK(int) drvblockRead(PPDMIBLOCK pInterface, uint64_t off, void *pvBuf, size_t cbRead)
125{
126 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
127
128 /*
129 * Check the state.
130 */
131 if (!pData->pDrvMedia)
132 {
133 AssertMsgFailed(("Invalid state! Not mounted!\n"));
134 return VERR_PDM_MEDIA_NOT_MOUNTED;
135 }
136
137 int rc = pData->pDrvMedia->pfnRead(pData->pDrvMedia, off, pvBuf, cbRead);
138 return rc;
139}
140
141
142/** @copydoc PDMIBLOCK::pfnWrite */
143static DECLCALLBACK(int) drvblockWrite(PPDMIBLOCK pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
144{
145 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
146
147 /*
148 * Check the state.
149 */
150 if (!pData->pDrvMedia)
151 {
152 AssertMsgFailed(("Invalid state! Not mounted!\n"));
153 return VERR_PDM_MEDIA_NOT_MOUNTED;
154 }
155
156 int rc = pData->pDrvMedia->pfnWrite(pData->pDrvMedia, off, pvBuf, cbWrite);
157#ifdef VBOX_PERIODIC_FLUSH
158 if (pData->cbFlushInterval)
159 {
160 pData->cbDataWritten += cbWrite;
161 if (pData->cbDataWritten > pData->cbFlushInterval)
162 {
163 pData->cbDataWritten = 0;
164 pData->pDrvMedia->pfnFlush(pData->pDrvMedia);
165 }
166 }
167#endif /* VBOX_PERIODIC_FLUSH */
168
169 return rc;
170}
171
172
173/** @copydoc PDMIBLOCK::pfnFlush */
174static DECLCALLBACK(int) drvblockFlush(PPDMIBLOCK pInterface)
175{
176 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
177
178 /*
179 * Check the state.
180 */
181 if (!pData->pDrvMedia)
182 {
183 AssertMsgFailed(("Invalid state! Not mounted!\n"));
184 return VERR_PDM_MEDIA_NOT_MOUNTED;
185 }
186
187#ifdef VBOX_IGNORE_FLUSH
188 if (pData->fIgnoreFlush)
189 return VINF_SUCCESS;
190#endif /* VBOX_IGNORE_FLUSH */
191
192 int rc = pData->pDrvMedia->pfnFlush(pData->pDrvMedia);
193 if (rc == VERR_NOT_IMPLEMENTED)
194 rc = VINF_SUCCESS;
195 return rc;
196}
197
198
199/** @copydoc PDMIBLOCK::pfnIsReadOnly */
200static DECLCALLBACK(bool) drvblockIsReadOnly(PPDMIBLOCK pInterface)
201{
202 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
203
204 /*
205 * Check the state.
206 */
207 if (!pData->pDrvMedia)
208 return false;
209
210 bool fRc = pData->pDrvMedia->pfnIsReadOnly(pData->pDrvMedia);
211 return fRc;
212}
213
214
215/** @copydoc PDMIBLOCK::pfnGetSize */
216static DECLCALLBACK(uint64_t) drvblockGetSize(PPDMIBLOCK pInterface)
217{
218 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
219
220 /*
221 * Check the state.
222 */
223 if (!pData->pDrvMedia)
224 return 0;
225
226 uint64_t cb = pData->pDrvMedia->pfnGetSize(pData->pDrvMedia);
227 LogFlow(("drvblockGetSize: returns %llu\n", cb));
228 return cb;
229}
230
231
232/** @copydoc PDMIBLOCK::pfnGetType */
233static DECLCALLBACK(PDMBLOCKTYPE) drvblockGetType(PPDMIBLOCK pInterface)
234{
235 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
236 LogFlow(("drvblockGetType: returns %d\n", pData->enmType));
237 return pData->enmType;
238}
239
240
241/** @copydoc PDMIBLOCK::pfnGetUuid */
242static DECLCALLBACK(int) drvblockGetUuid(PPDMIBLOCK pInterface, PRTUUID pUuid)
243{
244 PDRVBLOCK pData = PDMIBLOCK_2_DRVBLOCK(pInterface);
245
246 /*
247 * Copy the uuid.
248 */
249 *pUuid = pData->Uuid;
250 return VINF_SUCCESS;
251}
252
253
254/* -=-=-=-=- IBlockBios -=-=-=-=- */
255
256/** Makes a PDRVBLOCK out of a PPDMIBLOCKBIOS. */
257#define PDMIBLOCKBIOS_2_DRVBLOCK(pInterface) ( (PDRVBLOCK((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IBlockBios))) )
258
259
260/** @copydoc PDMIBLOCKBIOS::pfnGetGeometry */
261static DECLCALLBACK(int) drvblockGetGeometry(PPDMIBLOCKBIOS pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
262{
263 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
264
265 /*
266 * Check the state.
267 */
268 if (!pData->pDrvMedia)
269 return VERR_PDM_MEDIA_NOT_MOUNTED;
270
271 /*
272 * Use configured/cached values if present.
273 */
274 if ( pData->cCylinders > 0
275 && pData->cHeads > 0
276 && pData->cSectors > 0)
277 {
278 *pcCylinders = pData->cCylinders;
279 *pcHeads = pData->cHeads;
280 *pcSectors = pData->cSectors;
281 LogFlow(("drvblockGetGeometry: returns VINF_SUCCESS {%d,%d,%d}\n", pData->cCylinders, pData->cHeads, pData->cSectors));
282 return VINF_SUCCESS;
283 }
284
285 /*
286 * Call media.
287 */
288 int rc = pData->pDrvMedia->pfnBiosGetGeometry(pData->pDrvMedia, pcCylinders, pcHeads, pcSectors);
289 if (VBOX_SUCCESS(rc))
290 {
291 pData->cCylinders = *pcCylinders;
292 pData->cHeads = *pcHeads;
293 pData->cSectors = *pcSectors;
294 LogFlow(("drvblockGetGeometry: returns %Vrc {%d,%d,%d}\n", rc, pData->cCylinders, pData->cHeads, pData->cSectors));
295 }
296 else if (rc == VERR_NOT_IMPLEMENTED)
297 {
298 rc = VERR_PDM_GEOMETRY_NOT_SET;
299 LogFlow(("drvblockGetGeometry: returns %Vrc\n", rc));
300 }
301 return rc;
302}
303
304
305/** @copydoc PDMIBLOCKBIOS::pfnSetGeometry */
306static DECLCALLBACK(int) drvblockSetGeometry(PPDMIBLOCKBIOS pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
307{
308 LogFlow(("drvblockSetGeometry: cCylinders=%d cHeads=%d cSectors=%d\n", cCylinders, cHeads, cSectors));
309 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
310
311 /*
312 * Check the state.
313 */
314 if (!pData->pDrvMedia)
315 {
316 AssertMsgFailed(("Invalid state! Not mounted!\n"));
317 return VERR_PDM_MEDIA_NOT_MOUNTED;
318 }
319
320 /*
321 * Call media. Ignore the not implemented return code.
322 */
323 int rc = pData->pDrvMedia->pfnBiosSetGeometry(pData->pDrvMedia, cCylinders, cHeads, cSectors);
324 if ( VBOX_SUCCESS(rc)
325 || rc == VERR_NOT_IMPLEMENTED)
326 {
327 pData->cCylinders = cCylinders;
328 pData->cHeads = cHeads;
329 pData->cSectors = cSectors;
330 rc = VINF_SUCCESS;
331 }
332 return rc;
333}
334
335
336/** @copydoc PDMIBLOCKBIOS::pfnGetTranslation */
337static DECLCALLBACK(int) drvblockGetTranslation(PPDMIBLOCKBIOS pInterface, PPDMBIOSTRANSLATION penmTranslation)
338{
339 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
340
341 /*
342 * Check the state.
343 */
344 if (!pData->pDrvMedia)
345 {
346 LogFlow(("drvblockGetTranslation: returns VERR_PDM_MEDIA_NOT_MOUNTED\n"));
347 return VERR_PDM_MEDIA_NOT_MOUNTED;
348 }
349
350 /*
351 * Use configured/cached data if present.
352 */
353 if (pData->fTranslationSet)
354 {
355 *penmTranslation = pData->enmTranslation;
356 LogFlow(("drvblockGetTranslation: returns VINF_SUCCESS *penmTranslation=%d\n", *penmTranslation));
357 return VINF_SUCCESS;
358 }
359
360 /*
361 * Call media. Handle the not implemented status code.
362 */
363 int rc = pData->pDrvMedia->pfnBiosGetTranslation(pData->pDrvMedia, penmTranslation);
364 if (VBOX_SUCCESS(rc))
365 {
366 pData->enmTranslation = *penmTranslation;
367 pData->fTranslationSet = true;
368 }
369 else if (rc == VERR_NOT_IMPLEMENTED)
370 rc = VERR_PDM_TRANSLATION_NOT_SET;
371 LogFlow(("drvblockGetTranslation: returns %Vrc *penmTranslation=%d\n", rc, *penmTranslation));
372 return rc;
373}
374
375
376/** @copydoc PDMIBLOCKBIOS::pfnSetTranslation */
377static DECLCALLBACK(int) drvblockSetTranslation(PPDMIBLOCKBIOS pInterface, PDMBIOSTRANSLATION enmTranslation)
378{
379 LogFlow(("drvblockSetTranslation: enmTranslation=%d\n", enmTranslation));
380 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
381
382 /*
383 * Check the state.
384 */
385 if (!pData->pDrvMedia)
386 {
387 AssertMsgFailed(("Invalid state! Not mounted!\n"));
388 return VERR_PDM_MEDIA_NOT_MOUNTED;
389 }
390
391 /*
392 * Call media. Ignore the not implemented return code.
393 */
394 int rc = pData->pDrvMedia->pfnBiosSetTranslation(pData->pDrvMedia, enmTranslation);
395 if ( VBOX_SUCCESS(rc)
396 || rc == VERR_NOT_IMPLEMENTED)
397 {
398 pData->fTranslationSet = true;
399 pData->enmTranslation = enmTranslation;
400 rc = VINF_SUCCESS;
401 }
402 return rc;
403}
404
405
406/** @copydoc PDMIBLOCKBIOS::pfnIsVisible */
407static DECLCALLBACK(bool) drvblockIsVisible(PPDMIBLOCKBIOS pInterface)
408{
409 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
410 LogFlow(("drvblockIsVisible: returns %d\n", pData->fBiosVisible));
411 return pData->fBiosVisible;
412}
413
414
415/** @copydoc PDMIBLOCKBIOS::pfnGetType */
416static DECLCALLBACK(PDMBLOCKTYPE) drvblockBiosGetType(PPDMIBLOCKBIOS pInterface)
417{
418 PDRVBLOCK pData = PDMIBLOCKBIOS_2_DRVBLOCK(pInterface);
419 LogFlow(("drvblockBiosGetType: returns %d\n", pData->enmType));
420 return pData->enmType;
421}
422
423
424
425/* -=-=-=-=- IMount -=-=-=-=- */
426
427/** Makes a PDRVBLOCK out of a PPDMIMOUNT. */
428#define PDMIMOUNT_2_DRVBLOCK(pInterface) ( (PDRVBLOCK)((uintptr_t)pInterface - RT_OFFSETOF(DRVBLOCK, IMount)) )
429
430
431/** @copydoc PDMIMOUNT::pfnMount */
432static DECLCALLBACK(int) drvblockMount(PPDMIMOUNT pInterface, const char *pszFilename, const char *pszCoreDriver)
433{
434 LogFlow(("drvblockMount: pszFilename=%p:{%s} pszCoreDriver=%p:{%s}\n", pszFilename, pszFilename, pszCoreDriver, pszCoreDriver));
435 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
436
437 /*
438 * Validate state.
439 */
440 if (pData->pDrvMedia)
441 {
442 AssertMsgFailed(("Already mounted\n"));
443 return VERR_PDM_MEDIA_MOUNTED;
444 }
445
446 /*
447 * Prepare configuration.
448 */
449 if (pszFilename)
450 {
451 int rc = pData->pDrvIns->pDrvHlp->pfnMountPrepare(pData->pDrvIns, pszFilename, pszCoreDriver);
452 if (VBOX_FAILURE(rc))
453 {
454 Log(("drvblockMount: Prepare failed for \"%s\" rc=%Vrc\n", pszFilename, rc));
455 return rc;
456 }
457 }
458
459 /*
460 * Attach the media driver and query it's interface.
461 */
462 PPDMIBASE pBase;
463 int rc = pData->pDrvIns->pDrvHlp->pfnAttach(pData->pDrvIns, &pBase);
464 if (VBOX_FAILURE(rc))
465 {
466 Log(("drvblockMount: Attach failed rc=%Vrc\n", rc));
467 return rc;
468 }
469
470 pData->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
471 if (pData->pDrvMedia)
472 {
473 /*
474 * Initialize state.
475 */
476 pData->fLocked = false;
477 pData->enmTranslation = PDMBIOSTRANSLATION_NONE;
478 pData->cCylinders = pData->cHeads = pData->cSectors = 0;
479#ifdef VBOX_PERIODIC_FLUSH
480 pData->cbDataWritten = 0;
481#endif /* VBOX_PERIODIC_FLUSH */
482
483 /*
484 * Notify driver/device above us.
485 */
486 if (pData->pDrvMountNotify)
487 pData->pDrvMountNotify->pfnMountNotify(pData->pDrvMountNotify);
488 Log(("drvblockMount: Success\n"));
489 return VINF_SUCCESS;
490 }
491 else
492 rc = VERR_PDM_MISSING_INTERFACE_BELOW;
493
494 /*
495 * Failed, detatch the media driver.
496 */
497 AssertMsgFailed(("No media interface!\n"));
498 int rc2 = pData->pDrvIns->pDrvHlp->pfnDetach(pData->pDrvIns);
499 AssertRC(rc2);
500 pData->pDrvMedia = NULL;
501 return rc;
502}
503
504
505/** @copydoc PDMIMOUNT::pfnUnmount */
506static DECLCALLBACK(int) drvblockUnmount(PPDMIMOUNT pInterface, bool fForce)
507{
508 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
509
510 /*
511 * Validate state.
512 */
513 if (!pData->pDrvMedia)
514 {
515 Log(("drvblockUmount: Not mounted\n"));
516 return VERR_PDM_MEDIA_NOT_MOUNTED;
517 }
518 if (pData->fLocked && !fForce)
519 {
520 Log(("drvblockUmount: Locked\n"));
521 return VERR_PDM_MEDIA_LOCKED;
522 }
523
524 /* Media is no longer locked even if it was previously. */
525 pData->fLocked = false;
526
527 /*
528 * Detach the media driver and query it's interface.
529 */
530 int rc = pData->pDrvIns->pDrvHlp->pfnDetach(pData->pDrvIns);
531 if (VBOX_FAILURE(rc))
532 {
533 Log(("drvblockUnmount: Detach failed rc=%Vrc\n", rc));
534 return rc;
535 }
536 Assert(!pData->pDrvMedia);
537
538 /*
539 * Notify driver/device above us.
540 */
541 if (pData->pDrvMountNotify)
542 pData->pDrvMountNotify->pfnUnmountNotify(pData->pDrvMountNotify);
543 Log(("drvblockUnmount: success\n"));
544 return VINF_SUCCESS;
545}
546
547
548/** @copydoc PDMIMOUNT::pfnIsMounted */
549static DECLCALLBACK(bool) drvblockIsMounted(PPDMIMOUNT pInterface)
550{
551 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
552 return pData->pDrvMedia != NULL;
553}
554
555/** @copydoc PDMIMOUNT::pfnLock */
556static DECLCALLBACK(int) drvblockLock(PPDMIMOUNT pInterface)
557{
558 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
559 Log(("drvblockLock: %d -> %d\n", pData->fLocked, true));
560 pData->fLocked = true;
561 return VINF_SUCCESS;
562}
563
564/** @copydoc PDMIMOUNT::pfnUnlock */
565static DECLCALLBACK(int) drvblockUnlock(PPDMIMOUNT pInterface)
566{
567 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
568 Log(("drvblockUnlock: %d -> %d\n", pData->fLocked, false));
569 pData->fLocked = false;
570 return VINF_SUCCESS;
571}
572
573/** @copydoc PDMIMOUNT::pfnIsLocked */
574static DECLCALLBACK(bool) drvblockIsLocked(PPDMIMOUNT pInterface)
575{
576 PDRVBLOCK pData = PDMIMOUNT_2_DRVBLOCK(pInterface);
577 return pData->fLocked;
578}
579
580
581/* -=-=-=-=- IBase -=-=-=-=- */
582
583/** @copydoc PDMIBASE::pfnQueryInterface. */
584static DECLCALLBACK(void *) drvblockQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
585{
586 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
587 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
588 switch (enmInterface)
589 {
590 case PDMINTERFACE_BASE:
591 return &pDrvIns->IBase;
592 case PDMINTERFACE_BLOCK:
593 return &pData->IBlock;
594 case PDMINTERFACE_BLOCK_BIOS:
595 return pData->fBiosVisible ? &pData->IBlockBios : NULL;
596 case PDMINTERFACE_MOUNT:
597 return pData->fMountable ? &pData->IMount : NULL;
598 default:
599 return NULL;
600 }
601}
602
603
604/* -=-=-=-=- driver interface -=-=-=-=- */
605
606/** @copydoc FNPDMDRVDETACH. */
607static DECLCALLBACK(void) drvblockDetach(PPDMDRVINS pDrvIns)
608{
609 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
610 pData->pDrvMedia = NULL;
611}
612
613
614/**
615 * Construct a block driver instance.
616 *
617 * @returns VBox status.
618 * @param pDrvIns The driver instance data.
619 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
620 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
621 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
622 * iInstance it's expected to be used a bit in this function.
623 */
624static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
625{
626 PDRVBLOCK pData = PDMINS2DATA(pDrvIns, PDRVBLOCK);
627 LogFlow(("drvblockConstruct: iInstance=%d\n", pDrvIns->iInstance));
628
629 /*
630 * Validate configuration.
631 */
632#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
633 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Translation\0Mountable\0FlushInterval\0IgnoreFlush\0"))
634#else /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
635 if (!CFGMR3AreValuesValid(pCfgHandle, "Type\0Locked\0BIOSVisible\0AttachFailError\0Cylinders\0Heads\0Sectors\0Translation\0Mountable\0"))
636#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
637 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
638
639 /*
640 * Initialize most of the data members.
641 */
642 pData->pDrvIns = pDrvIns;
643
644 /* IBase. */
645 pDrvIns->IBase.pfnQueryInterface = drvblockQueryInterface;
646
647 /* IBlock. */
648 pData->IBlock.pfnRead = drvblockRead;
649 pData->IBlock.pfnWrite = drvblockWrite;
650 pData->IBlock.pfnFlush = drvblockFlush;
651 pData->IBlock.pfnIsReadOnly = drvblockIsReadOnly;
652 pData->IBlock.pfnGetSize = drvblockGetSize;
653 pData->IBlock.pfnGetType = drvblockGetType;
654 pData->IBlock.pfnGetUuid = drvblockGetUuid;
655
656 /* IBlockBios. */
657 pData->IBlockBios.pfnGetGeometry = drvblockGetGeometry;
658 pData->IBlockBios.pfnSetGeometry = drvblockSetGeometry;
659 pData->IBlockBios.pfnGetTranslation = drvblockGetTranslation;
660 pData->IBlockBios.pfnSetTranslation = drvblockSetTranslation;
661 pData->IBlockBios.pfnIsVisible = drvblockIsVisible;
662 pData->IBlockBios.pfnGetType = drvblockBiosGetType;
663
664 /* IMount. */
665 pData->IMount.pfnMount = drvblockMount;
666 pData->IMount.pfnUnmount = drvblockUnmount;
667 pData->IMount.pfnIsMounted = drvblockIsMounted;
668 pData->IMount.pfnLock = drvblockLock;
669 pData->IMount.pfnUnlock = drvblockUnlock;
670 pData->IMount.pfnIsLocked = drvblockIsLocked;
671
672 /*
673 * Get the IBlockPort & IMountNotify interfaces of the above driver/device.
674 */
675 pData->pDrvBlockPort = (PPDMIBLOCKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_BLOCK_PORT);
676 if (!pData->pDrvBlockPort)
677 {
678 AssertMsgFailed(("Configuration error: No block port interface above!\n"));
679 return VERR_PDM_MISSING_INTERFACE_ABOVE;
680 }
681 pData->pDrvMountNotify = (PPDMIMOUNTNOTIFY)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MOUNT_NOTIFY);
682
683 /*
684 * Query configuration.
685 */
686 /* type */
687 char *psz;
688 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Type", &psz);
689 if (VBOX_FAILURE(rc))
690 {
691 AssertMsgFailed(("Configuration error: Failed to obtain the type, rc=%Vrc.\n", rc));
692 return VERR_PDM_BLOCK_NO_TYPE;
693 }
694 if (!strcmp(psz, "HardDisk"))
695 pData->enmType = PDMBLOCKTYPE_HARD_DISK;
696 else if (!strcmp(psz, "DVD"))
697 pData->enmType = PDMBLOCKTYPE_DVD;
698 else if (!strcmp(psz, "CDROM"))
699 pData->enmType = PDMBLOCKTYPE_CDROM;
700 else if (!strcmp(psz, "Floppy 2.88"))
701 pData->enmType = PDMBLOCKTYPE_FLOPPY_2_88;
702 else if (!strcmp(psz, "Floppy 1.44"))
703 pData->enmType = PDMBLOCKTYPE_FLOPPY_1_44;
704 else if (!strcmp(psz, "Floppy 1.20"))
705 pData->enmType = PDMBLOCKTYPE_FLOPPY_1_20;
706 else if (!strcmp(psz, "Floppy 720"))
707 pData->enmType = PDMBLOCKTYPE_FLOPPY_720;
708 else if (!strcmp(psz, "Floppy 360"))
709 pData->enmType = PDMBLOCKTYPE_FLOPPY_360;
710 else
711 {
712 AssertMsgFailed(("Configuration error: Unknown type \"%s\".\n", psz));
713 MMR3HeapFree(psz);
714 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
715 }
716 Log2(("drvblockConstruct: enmType=%d\n", pData->enmType));
717 MMR3HeapFree(psz); psz = NULL;
718
719 /* Mountable */
720 rc = CFGMR3QueryBool(pCfgHandle, "Mountable", &pData->fMountable);
721 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
722 pData->fMountable = false;
723 else if (VBOX_FAILURE(rc))
724 {
725 AssertMsgFailed(("Configuration error: Query \"Mountable\" resulted in %Vrc.\n", rc));
726 return rc;
727 }
728
729 /* Locked */
730 rc = CFGMR3QueryBool(pCfgHandle, "Locked", &pData->fLocked);
731 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
732 pData->fLocked = false;
733 else if (VBOX_FAILURE(rc))
734 {
735 AssertMsgFailed(("Configuration error: Query \"Locked\" resulted in %Vrc.\n", rc));
736 return rc;
737 }
738
739 /* BIOS visible */
740 rc = CFGMR3QueryBool(pCfgHandle, "BIOSVisible", &pData->fBiosVisible);
741 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
742 pData->fBiosVisible = true;
743 else if (VBOX_FAILURE(rc))
744 {
745 AssertMsgFailed(("Configuration error: Query \"BIOSVisible\" resulted in %Vrc.\n", rc));
746 return rc;
747 }
748
749 /** @todo AttachFailError is currently completely ignored. */
750
751 /* Cylinders */
752 rc = CFGMR3QueryU32(pCfgHandle, "Cylinders", &pData->cCylinders);
753 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
754 pData->cCylinders = 0;
755 else if (VBOX_FAILURE(rc))
756 {
757 AssertMsgFailed(("Configuration error: Query \"Cylinders\" resulted in %Vrc.\n", rc));
758 return rc;
759 }
760
761 /* Heads */
762 rc = CFGMR3QueryU32(pCfgHandle, "Heads", &pData->cHeads);
763 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
764 pData->cHeads = 0;
765 else if (VBOX_FAILURE(rc))
766 {
767 AssertMsgFailed(("Configuration error: Query \"Heads\" resulted in %Vrc.\n", rc));
768 return rc;
769 }
770
771 /* Sectors */
772 rc = CFGMR3QueryU32(pCfgHandle, "Sectors", &pData->cSectors);
773 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
774 pData->cSectors = 0;
775 else if (VBOX_FAILURE(rc))
776 {
777 AssertMsgFailed(("Configuration error: Query \"Sectors\" resulted in %Vrc.\n", rc));
778 return rc;
779 }
780
781 /* Translation */
782 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Translation", &psz);
783 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
784 {
785 pData->enmTranslation = PDMBIOSTRANSLATION_NONE;
786 pData->fTranslationSet = false;
787 }
788 else if (VBOX_SUCCESS(rc))
789 {
790 if (!strcmp(psz, "None"))
791 pData->enmTranslation = PDMBIOSTRANSLATION_NONE;
792 else if (!strcmp(psz, "LBA"))
793 pData->enmTranslation = PDMBIOSTRANSLATION_LBA;
794 else if (!strcmp(psz, "Auto"))
795 pData->enmTranslation = PDMBIOSTRANSLATION_AUTO;
796 else
797 {
798 AssertMsgFailed(("Configuration error: Unknown translation \"%s\".\n", psz));
799 MMR3HeapFree(psz);
800 return VERR_PDM_BLOCK_UNKNOWN_TRANSLATION;
801 }
802 MMR3HeapFree(psz); psz = NULL;
803 pData->fTranslationSet = true;
804 }
805 else
806 {
807 AssertMsgFailed(("Configuration error: Failed to obtain the translation, rc=%Vrc.\n", rc));
808 return VERR_PDM_BLOCK_NO_TYPE;
809 }
810
811 /* Uuid */
812 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Uuid", &psz);
813 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
814 RTUuidClear(&pData->Uuid);
815 else if (VBOX_SUCCESS(rc))
816 {
817 rc = RTUuidFromStr(&pData->Uuid, psz);
818 if (VBOX_FAILURE(rc))
819 {
820 AssertMsgFailed(("Configuration error: Uuid from string failed on \"%s\", rc=%Vrc.\n", psz, rc));
821 MMR3HeapFree(psz);
822 return rc;
823 }
824 MMR3HeapFree(psz); psz = NULL;
825 }
826 else
827 {
828 AssertMsgFailed(("Configuration error: Failed to obtain the type, rc=%Vrc.\n", rc));
829 return VERR_PDM_BLOCK_NO_TYPE;
830 }
831
832#ifdef VBOX_PERIODIC_FLUSH
833 rc = CFGMR3QueryU32(pCfgHandle, "FlushInterval", &pData->cbFlushInterval);
834 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
835 pData->cbFlushInterval = 0;
836 else if (VBOX_FAILURE(rc))
837 {
838 AssertMsgFailed(("Configuration error: Query \"FlushInterval\" resulted in %Vrc.\n", rc));
839 return rc;
840 }
841#endif /* VBOX_PERIODIC_FLUSH */
842
843#ifdef VBOX_IGNORE_FLUSH
844 rc = CFGMR3QueryBool(pCfgHandle, "IgnoreFlush", &pData->fIgnoreFlush);
845 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
846 pData->fIgnoreFlush = true; /* The default is to ignore flushes. */
847 else if (VBOX_FAILURE(rc))
848 {
849 AssertMsgFailed(("Configuration error: Query \"IgnoreFlush\" resulted in %Vrc.\n", rc));
850 return rc;
851 }
852#endif /* VBOX_IGNORE_FLUSH */
853
854 /*
855 * Try attach driver below and query it's media interface.
856 */
857 PPDMIBASE pBase;
858 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
859 if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
860 && pData->enmType != PDMBLOCKTYPE_HARD_DISK)
861 return VINF_SUCCESS;
862 if (VBOX_FAILURE(rc))
863 {
864 AssertMsgFailed(("Failed to attach driver below us! rc=%Vra\n", rc));
865 return rc;
866 }
867 pData->pDrvMedia = (PPDMIMEDIA)pBase->pfnQueryInterface(pBase, PDMINTERFACE_MEDIA);
868 if (!pData->pDrvMedia)
869 {
870 AssertMsgFailed(("Configuration error: No media interface below!\n"));
871 return VERR_PDM_MISSING_INTERFACE_BELOW;
872 }
873 if (RTUuidIsNull(&pData->Uuid))
874 {
875 if (pData->enmType == PDMBLOCKTYPE_HARD_DISK)
876 pData->pDrvMedia->pfnGetUuid(pData->pDrvMedia, &pData->Uuid);
877 }
878
879 return VINF_SUCCESS;
880}
881
882
883/**
884 * Block driver registration record.
885 */
886const PDMDRVREG g_DrvBlock =
887{
888 /* u32Version */
889 PDM_DRVREG_VERSION,
890 /* szDriverName */
891 "Block",
892 /* pszDescription */
893 "Generic block driver.",
894 /* fFlags */
895 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
896 /* fClass. */
897 PDM_DRVREG_CLASS_BLOCK,
898 /* cMaxInstances */
899 ~0,
900 /* cbInstance */
901 sizeof(DRVBLOCK),
902 /* pfnConstruct */
903 drvblockConstruct,
904 /* pfnDestruct */
905 NULL,
906 /* pfnIOCtl */
907 NULL,
908 /* pfnPowerOn */
909 NULL,
910 /* pfnReset */
911 NULL,
912 /* pfnSuspend */
913 NULL,
914 /* pfnResume */
915 NULL,
916 /* pfnDetach */
917 drvblockDetach
918};
919
920
921
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