VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvVD.cpp@ 13303

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

Storage: Replace the assertions in the construction code with runtime errors because it is possible that hard disks are not accessible. Also fix a assertion because of a unitiallized semaphore in the IDE controller code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.0 KB
Line 
1/* $Id: DrvVD.cpp 13303 2008-10-15 20:55:16Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_VD
27#include <VBox/VBoxHDD-new.h>
28#include <VBox/pdmdrv.h>
29#include <iprt/alloc.h>
30#include <iprt/assert.h>
31#include <iprt/uuid.h>
32#include <iprt/file.h>
33#include <iprt/string.h>
34#include <iprt/cache.h>
35
36#include "Builtins.h"
37
38
39/*******************************************************************************
40* Defined types, constants and macros *
41*******************************************************************************/
42
43/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
44#define PDMIMEDIA_2_VBOXDISK(pInterface) \
45 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
46
47/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
48#define PDMIBASE_2_DRVINS(pInterface) \
49 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
50
51/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
52#define PDMIBASE_2_VBOXDISK(pInterface) \
53 ( PDMINS_2_DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
54
55/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
56#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
57 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
58
59/** Converts a pointer to VBOXDISK::ITransportAsyncPort to a PVBOXDISK. */
60#define PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface) \
61 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, ITransportAsyncPort)) )
62
63/**
64 * Structure for an async I/O task.
65 */
66typedef struct DRVVDASYNCTASK
67{
68 /** Callback which is called on completion. */
69 PFNVDCOMPLETED pfnCompleted;
70 /** Opqaue user data which is passed on completion. */
71 void *pvUser;
72 /** Opaque user data the caller passed on transfer initiation. */
73 void *pvUserCaller;
74} DRVVDASYNCTASK, *PDRVVDASYNCTASK;
75
76/**
77 * VBox disk container, image information, private part.
78 */
79
80typedef struct VBOXIMAGE
81{
82 /** Pointer to next image. */
83 struct VBOXIMAGE *pNext;
84 /** Pointer to list of VD interfaces. Per-image. */
85 PVDINTERFACE pVDIfsImage;
86 /** Common structure for the configuration information interface. */
87 VDINTERFACE VDIConfig;
88} VBOXIMAGE, *PVBOXIMAGE;
89
90/**
91 * VBox disk container media main structure, private part.
92 */
93typedef struct VBOXDISK
94{
95 /** The VBox disk container. */
96 PVBOXHDD pDisk;
97 /** The media interface. */
98 PDMIMEDIA IMedia;
99 /** Pointer to the driver instance. */
100 PPDMDRVINS pDrvIns;
101 /** Flag whether suspend has changed image open mode to read only. */
102 bool fTempReadOnly;
103 /** Pointer to list of VD interfaces. Per-disk. */
104 PVDINTERFACE pVDIfsDisk;
105 /** Common structure for the supported error interface. */
106 VDINTERFACE VDIError;
107 /** Callback table for error interface. */
108 VDINTERFACEERROR VDIErrorCallbacks;
109 /** Common structure for the supported async I/O interface. */
110 VDINTERFACE VDIAsyncIO;
111 /** Callback table for async I/O interface. */
112 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
113 /** Callback table for the configuration information interface. */
114 VDINTERFACECONFIG VDIConfigCallbacks;
115 /** Flag whether opened disk suppports async I/O operations. */
116 bool fAsyncIOSupported;
117 /** The async media interface. */
118 PDMIMEDIAASYNC IMediaAsync;
119 /** The async media port interface above. */
120 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
121 /** Pointer to the asynchronous media driver below. */
122 PPDMITRANSPORTASYNC pDrvTransportAsync;
123 /** Async transport port interface. */
124 PDMITRANSPORTASYNCPORT ITransportAsyncPort;
125 /** Our cache to reduce allocation overhead. */
126 PRTOBJCACHE pCache;
127 /** Pointer to the list of data we need to keep per image. */
128 PVBOXIMAGE pImages;
129} VBOXDISK, *PVBOXDISK;
130
131/*******************************************************************************
132* Error reporting callback *
133*******************************************************************************/
134
135static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
136 const char *pszFormat, va_list va)
137{
138 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
139 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
140}
141
142
143/**
144 * Internal: allocate new image descriptor and put it in the list
145 */
146static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
147{
148 AssertPtr(pThis);
149 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
150 if (pImage)
151 {
152 pImage->pVDIfsImage = NULL;
153 PVBOXIMAGE *pp = &pThis->pImages;
154 while (*pp != NULL)
155 pp = &(*pp)->pNext;
156 *pp = pImage;
157 pImage->pNext = NULL;
158 }
159
160 return pImage;
161}
162
163/**
164 * Internal: free the list of images descriptors.
165 */
166static void drvvdFreeImages(PVBOXDISK pThis)
167{
168 while (pThis->pImages != NULL)
169 {
170 PVBOXIMAGE p = pThis->pImages;
171 pThis->pImages = pThis->pImages->pNext;
172 RTMemFree(p);
173 }
174}
175
176/*******************************************************************************
177* VD Async I/O interface implementation *
178*******************************************************************************/
179
180static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
181{
182 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
183
184 return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
185}
186
187static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
188{
189 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
190
191 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
192
193 return pDrvVD->pDrvTransportAsync->pfnClose(pDrvVD->pDrvTransportAsync, pStorage);
194}
195
196static DECLCALLBACK(int) drvvdAsyncIORead(void *pvUser, void *pStorage, uint64_t uOffset,
197 size_t cbRead, void *pvBuf, size_t *pcbRead)
198{
199 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
200
201 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
202
203 return pDrvVD->pDrvTransportAsync->pfnReadSynchronous(pDrvVD->pDrvTransportAsync,
204 pStorage,
205 uOffset, pvBuf, cbRead, pcbRead);
206}
207
208static DECLCALLBACK(int) drvvdAsyncIOWrite(void *pvUser, void *pStorage, uint64_t uOffset,
209 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
210{
211 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
212
213 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
214
215 return pDrvVD->pDrvTransportAsync->pfnWriteSynchronous(pDrvVD->pDrvTransportAsync,
216 pStorage,
217 uOffset, pvBuf, cbWrite, pcbWritten);
218}
219
220static DECLCALLBACK(int) drvvdAsyncIOFlush(void *pvUser, void *pStorage)
221{
222 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
223
224 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
225
226 return pDrvVD->pDrvTransportAsync->pfnFlushSynchronous(pDrvVD->pDrvTransportAsync, pStorage);
227}
228
229static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
230{
231 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
232
233 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
234
235 return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
236}
237
238static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
239{
240 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
241
242 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
243
244 return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
245}
246
247static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
248 void *pvUserCaller, PFNVDCOMPLETED pfnTasksCompleted)
249{
250 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
251 PDRVVDASYNCTASK pDrvVDAsyncTask;
252 int rc;
253
254 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
255
256 rc = RTCacheRequest(pDrvVD->pCache, (void **)&pDrvVDAsyncTask);
257
258 if (RT_FAILURE(rc))
259 return rc;
260
261 pDrvVDAsyncTask->pfnCompleted = pfnTasksCompleted;
262 pDrvVDAsyncTask->pvUser = pvUser2;
263 pDrvVDAsyncTask->pvUserCaller = pvUserCaller;
264
265 return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
266}
267
268/*******************************************************************************
269* VD Configuration interface implementation *
270*******************************************************************************/
271
272static bool drvvdCfgAreValuesValid(PVDCFGNODE pNode, const char *pszzValid)
273{
274 return CFGMR3AreValuesValid((PCFGMNODE)pNode, pszzValid);
275}
276
277static int drvvdCfgQueryType(PVDCFGNODE pNode, const char *pszName, PVDCFGVALUETYPE penmType)
278{
279 Assert(VDCFGVALUETYPE_INTEGER == (VDCFGVALUETYPE)CFGMVALUETYPE_INTEGER);
280 Assert(VDCFGVALUETYPE_STRING == (VDCFGVALUETYPE)CFGMVALUETYPE_STRING);
281 Assert(VDCFGVALUETYPE_BYTES == (VDCFGVALUETYPE)CFGMVALUETYPE_BYTES);
282 return CFGMR3QueryType((PCFGMNODE)pNode, pszName, (PCFGMVALUETYPE)penmType);
283}
284
285static int drvvdCfgQuerySize(PVDCFGNODE pNode, const char *pszName, size_t *pcb)
286{
287 return CFGMR3QuerySize((PCFGMNODE)pNode, pszName, pcb);
288}
289
290static int drvvdCfgQueryInteger(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64)
291{
292 return CFGMR3QueryInteger((PCFGMNODE)pNode, pszName, pu64);
293}
294
295static int drvvdCfgQueryIntegerDef(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64, uint64_t u64Def)
296{
297 return CFGMR3QueryInteger((PCFGMNODE)pNode, pszName, pu64);
298}
299
300static int drvvdCfgQueryString(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString)
301{
302 return CFGMR3QueryString((PCFGMNODE)pNode, pszName, pszString, cchString);
303}
304
305static int drvvdCfgQueryStringDef(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
306{
307 return CFGMR3QueryStringDef((PCFGMNODE)pNode, pszName, pszString, cchString, pszDef);
308}
309
310static int drvvdCfgQueryBytes(PVDCFGNODE pNode, const char *pszName, void *pvData, size_t cbData)
311{
312 return CFGMR3QueryBytes((PCFGMNODE)pNode, pszName, pvData, cbData);
313}
314
315
316/*******************************************************************************
317* Media interface methods *
318*******************************************************************************/
319
320/** @copydoc PDMIMEDIA::pfnRead */
321static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
322 uint64_t off, void *pvBuf, size_t cbRead)
323{
324 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
325 off, pvBuf, cbRead));
326 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
327 int rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
328 if (RT_SUCCESS(rc))
329 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
330 off, pvBuf, cbRead, cbRead, pvBuf));
331 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
332 return rc;
333}
334
335/** @copydoc PDMIMEDIA::pfnWrite */
336static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
337 uint64_t off, const void *pvBuf,
338 size_t cbWrite)
339{
340 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
341 off, pvBuf, cbWrite));
342 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
343 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
344 off, pvBuf, cbWrite, cbWrite, pvBuf));
345 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
346 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
347 return rc;
348}
349
350/** @copydoc PDMIMEDIA::pfnFlush */
351static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
352{
353 LogFlow(("%s:\n", __FUNCTION__));
354 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
355 int rc = VDFlush(pThis->pDisk);
356 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
357 return rc;
358}
359
360/** @copydoc PDMIMEDIA::pfnGetSize */
361static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
362{
363 LogFlow(("%s:\n", __FUNCTION__));
364 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
365 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
366 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
367 return cb;
368}
369
370/** @copydoc PDMIMEDIA::pfnIsReadOnly */
371static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
372{
373 LogFlow(("%s:\n", __FUNCTION__));
374 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
375 bool f = VDIsReadOnly(pThis->pDisk);
376 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
377 return f;
378}
379
380/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
381static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
382 PPDMMEDIAGEOMETRY pPCHSGeometry)
383{
384 LogFlow(("%s:\n", __FUNCTION__));
385 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
386 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
387 if (RT_FAILURE(rc))
388 {
389 Log(("%s: geometry not available.\n", __FUNCTION__));
390 rc = VERR_PDM_GEOMETRY_NOT_SET;
391 }
392 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
393 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
394 return rc;
395}
396
397/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
398static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
399 PCPDMMEDIAGEOMETRY pPCHSGeometry)
400{
401 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
402 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
403 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
404 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
405 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
406 return rc;
407}
408
409/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
410static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
411 PPDMMEDIAGEOMETRY pLCHSGeometry)
412{
413 LogFlow(("%s:\n", __FUNCTION__));
414 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
415 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
416 if (RT_FAILURE(rc))
417 {
418 Log(("%s: geometry not available.\n", __FUNCTION__));
419 rc = VERR_PDM_GEOMETRY_NOT_SET;
420 }
421 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
422 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
423 return rc;
424}
425
426/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
427static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
428 PCPDMMEDIAGEOMETRY pLCHSGeometry)
429{
430 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
431 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
432 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
433 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
434 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
435 return rc;
436}
437
438/** @copydoc PDMIMEDIA::pfnGetUuid */
439static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
440{
441 LogFlow(("%s:\n", __FUNCTION__));
442 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
443 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
444 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
445 return rc;
446}
447
448/*******************************************************************************
449* Async Media interface methods *
450*******************************************************************************/
451
452static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
453 PPDMDATASEG paSeg, unsigned cSeg,
454 size_t cbRead, void *pvUser)
455{
456 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
457 uOffset, paSeg, cSeg, cbRead, pvUser));
458 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
459 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
460 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
461 return rc;
462}
463
464static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
465 PPDMDATASEG paSeg, unsigned cSeg,
466 size_t cbWrite, void *pvUser)
467{
468 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
469 uOffset, paSeg, cSeg, cbWrite, pvUser));
470 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
471 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
472 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
473 return rc;
474}
475
476/*******************************************************************************
477* Async transport port interface methods *
478*******************************************************************************/
479
480static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
481{
482 PVBOXDISK pThis = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
483 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
484 int rc = VINF_VDI_ASYNC_IO_FINISHED;
485
486 /* Having a completion callback for a task is not mandatory. */
487 if (pDrvVDAsyncTask->pfnCompleted)
488 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
489
490 /* Check if the request is finished. */
491 if (rc == VINF_VDI_ASYNC_IO_FINISHED)
492 {
493 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
494 }
495 else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
496 rc = VINF_SUCCESS;
497
498 rc = RTCacheInsert(pThis->pCache, pDrvVDAsyncTask);
499 AssertRC(rc);
500
501 return rc;
502}
503
504
505/*******************************************************************************
506* Base interface methods *
507*******************************************************************************/
508
509/** @copydoc PDMIBASE::pfnQueryInterface */
510static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
511 PDMINTERFACE enmInterface)
512{
513 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
514 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
515 switch (enmInterface)
516 {
517 case PDMINTERFACE_BASE:
518 return &pDrvIns->IBase;
519 case PDMINTERFACE_MEDIA:
520 return &pThis->IMedia;
521 case PDMINTERFACE_MEDIA_ASYNC:
522 return pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL;
523 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
524 return &pThis->ITransportAsyncPort;
525 default:
526 return NULL;
527 }
528}
529
530
531/*******************************************************************************
532* Driver methods *
533*******************************************************************************/
534
535
536/**
537 * Construct a VBox disk media driver instance.
538 *
539 * @returns VBox status.
540 * @param pDrvIns The driver instance data.
541 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
542 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
543 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
544 * to be used frequently in this function.
545 */
546static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
547 PCFGMNODE pCfgHandle)
548{
549 LogFlow(("%s:\n", __FUNCTION__));
550 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
551 int rc = VINF_SUCCESS;
552 char *pszName = NULL; /**< The path of the disk image file. */
553 char *pszFormat = NULL; /**< The format backed to use for this image. */
554 bool fReadOnly; /**< True if the media is readonly. */
555 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
556
557 /*
558 * Init the static parts.
559 */
560 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
561 pThis->pDrvIns = pDrvIns;
562 pThis->fTempReadOnly = false;
563 pThis->pDisk = NULL;
564 pThis->fAsyncIOSupported = false;
565
566 /* IMedia */
567 pThis->IMedia.pfnRead = drvvdRead;
568 pThis->IMedia.pfnWrite = drvvdWrite;
569 pThis->IMedia.pfnFlush = drvvdFlush;
570 pThis->IMedia.pfnGetSize = drvvdGetSize;
571 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
572 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
573 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
574 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
575 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
576 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
577
578 /* IMediaAsync */
579 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
580 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
581
582 /* ITransportAsyncPort */
583 pThis->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
584
585 /* Initialize supported VD interfaces. */
586 pThis->pVDIfsDisk = NULL;
587
588 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
589 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
590 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
591
592 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
593 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
594 AssertRC(rc);
595
596 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
597 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
598 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
599 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
600 pThis->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
601 pThis->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
602 pThis->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
603 pThis->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
604 pThis->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
605 pThis->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
606
607 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
608 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
609 AssertRC(rc);
610
611 /* This is just prepared here, the actual interface is per-image, so it's
612 * added later. No need to have separate callback tables. */
613 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
614 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
615 pThis->VDIConfigCallbacks.pfnAreValuesValid = drvvdCfgAreValuesValid;
616 pThis->VDIConfigCallbacks.pfnQueryType = drvvdCfgQueryType;
617 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
618 pThis->VDIConfigCallbacks.pfnQueryInteger = drvvdCfgQueryInteger;
619 pThis->VDIConfigCallbacks.pfnQueryIntegerDef = drvvdCfgQueryIntegerDef;
620 pThis->VDIConfigCallbacks.pfnQueryString = drvvdCfgQueryString;
621 pThis->VDIConfigCallbacks.pfnQueryStringDef = drvvdCfgQueryStringDef;
622 pThis->VDIConfigCallbacks.pfnQueryBytes = drvvdCfgQueryBytes;
623
624 /* List of images is empty now. */
625 pThis->pImages = NULL;
626
627 /* Try to attach async media port interface above.*/
628 pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
629
630 /*
631 * Attach the async transport driver below of the device above us implements the
632 * async interface.
633 */
634 if (pThis->pDrvMediaAsyncPort)
635 {
636 /* Try to attach the driver. */
637 PPDMIBASE pBase;
638
639 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
640 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
641 {
642 /*
643 * Though the device supports async I/O there is no transport driver
644 * which processes async requests.
645 * Revert to non async I/O.
646 */
647 rc = VINF_SUCCESS;
648 pThis->pDrvMediaAsyncPort = NULL;
649 pThis->fAsyncIOSupported = false;
650 }
651 else if (RT_FAILURE(rc))
652 {
653 AssertMsgFailed(("Failed to attach async transport driver below rc=%Rrc\n", rc));
654 }
655 else
656 {
657 /*
658 * The device supports async I/O and we successfully attached the transport driver.
659 * Indicate that async I/O is supported for now as we check if the image backend supports
660 * it later.
661 */
662 pThis->fAsyncIOSupported = true;
663
664 /* Success query the async transport interface. */
665 pThis->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
666 if (!pThis->pDrvTransportAsync)
667 {
668 /* An attached driver without an async transport interface - impossible. */
669 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
670 return VERR_PDM_MISSING_INTERFACE_ABOVE;
671 }
672 }
673 }
674
675 /*
676 * Validate configuration and find all parent images.
677 * It's sort of up side down from the image dependency tree.
678 */
679 unsigned iLevel = 0;
680 PCFGMNODE pCurNode = pCfgHandle;
681
682 for (;;)
683 {
684 bool fValid;
685
686 if (pCurNode == pCfgHandle)
687 {
688 /* Toplevel configuration additionally contains the global image
689 * open flags. Some might be converted to per-image flags later. */
690 fValid = CFGMR3AreValuesValid(pCurNode,
691 "Format\0Path\0"
692 "ReadOnly\0HonorZeroWrites\0");
693 }
694 else
695 {
696 /* All other image configurations only contain image name and
697 * the format information. */
698 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
699 }
700 if (!fValid)
701 {
702 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
703 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
704 break;
705 }
706
707 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
708 if (!pParent)
709 break;
710 pCurNode = pParent;
711 iLevel++;
712 }
713
714 /*
715 * Open the images.
716 */
717 if (RT_SUCCESS(rc))
718 {
719 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
720 /* Error message is already set correctly. */
721 }
722
723 while (pCurNode && RT_SUCCESS(rc))
724 {
725 /* Allocate per-image data. */
726 PVBOXIMAGE pImage = drvvdNewImage(pThis);
727 if (!pImage)
728 {
729 rc = VERR_NO_MEMORY;
730 break;
731 }
732
733 /*
734 * Read the image configuration.
735 */
736 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
737 if (RT_FAILURE(rc))
738 {
739 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
740 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
741 break;
742 }
743
744 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
745 if (RT_FAILURE(rc))
746 {
747 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
748 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
749 break;
750 }
751
752 if (iLevel == 0)
753 {
754 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
755 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
756 fReadOnly = false;
757 else if (RT_FAILURE(rc))
758 {
759 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
760 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
761 break;
762 }
763
764 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
765 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
766 fHonorZeroWrites = false;
767 else if (RT_FAILURE(rc))
768 {
769 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
770 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
771 break;
772 }
773 }
774 else
775 {
776 fReadOnly = true;
777 fHonorZeroWrites = false;
778 }
779
780 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
781 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
782 &pThis->VDIConfigCallbacks, pCfg, &pImage->pVDIfsImage);
783 AssertRC(rc);
784
785 /*
786 * Open the image.
787 */
788 unsigned uOpenFlags;
789 if (fReadOnly)
790 uOpenFlags = VD_OPEN_FLAGS_READONLY;
791 else
792 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
793 if (fHonorZeroWrites)
794 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
795 if (pThis->pDrvMediaAsyncPort)
796 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
797
798 /** Try to open backend in asyc I/O mode first. */
799 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
800 if (rc == VERR_NOT_SUPPORTED)
801 {
802 /* Seems async I/O is not supported by the backend, open in normal mode. */
803 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
804 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
805 }
806
807 if (RT_SUCCESS(rc))
808 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
809 iLevel, pszName,
810 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
811 else
812 {
813 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
814 N_("Failed to open image '%s' in %s mode rc=%Rrc\n"), pszName,
815 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "readonly" : "read-write", rc);
816 break;
817 }
818
819
820 MMR3HeapFree(pszName);
821 pszName = NULL;
822 MMR3HeapFree(pszFormat);
823 pszFormat = NULL;
824
825 /* next */
826 iLevel--;
827 pCurNode = CFGMR3GetParent(pCurNode);
828 }
829
830 if (RT_FAILURE(rc))
831 {
832 if (VALID_PTR(pThis->pDisk))
833 {
834 VDDestroy(pThis->pDisk);
835 pThis->pDisk = NULL;
836 }
837 drvvdFreeImages(pThis);
838 if (VALID_PTR(pszName))
839 MMR3HeapFree(pszName);
840 if (VALID_PTR(pszFormat))
841 MMR3HeapFree(pszFormat);
842
843 return rc;
844 }
845 else
846 {
847 /*
848 * Check if every opened image supports async I/O.
849 * If not we revert to non async I/O.
850 */
851 if (pThis->fAsyncIOSupported)
852 {
853 for (unsigned i = 0; i < VDGetCount(pThis->pDisk); i++)
854 {
855 VDBACKENDINFO vdBackendInfo;
856
857 rc = VDBackendInfoSingle(pThis->pDisk, i, &vdBackendInfo);
858 AssertRC(rc);
859
860 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
861 {
862 /*
863 * Backend indicates support for at least some files.
864 * Check if current file is supported with async I/O)
865 */
866 rc = VDImageIsAsyncIOSupported(pThis->pDisk, i, &pThis->fAsyncIOSupported);
867 AssertRC(rc);
868
869 /*
870 * Check if current image is supported.
871 * If not we can stop checking because
872 * at least one does not support it.
873 */
874 if (!pThis->fAsyncIOSupported)
875 break;
876 }
877 else
878 {
879 pThis->fAsyncIOSupported = false;
880 break;
881 }
882 }
883 }
884
885 /*
886 * We know definitly if async I/O is supported now.
887 * Create cache if it is supported.
888 */
889 if (pThis->fAsyncIOSupported)
890 {
891 rc = RTCacheCreate(&pThis->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
892 AssertMsgRC(rc, ("Failed to create cache rc=%Rrc\n", rc));
893 }
894 }
895
896 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
897 return rc;
898}
899
900/**
901 * Destruct a driver instance.
902 *
903 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
904 * resources can be freed correctly.
905 *
906 * @param pDrvIns The driver instance data.
907 */
908static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
909{
910 int rc;
911 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
912 LogFlow(("%s:\n", __FUNCTION__));
913
914 drvvdFreeImages(pThis);
915 if (pThis->pCache)
916 {
917 rc = RTCacheDestroy(pThis->pCache);
918 AssertRC(rc);
919 }
920}
921
922
923/**
924 * When the VM has been suspended we'll change the image mode to read-only
925 * so that main and others can read the VDIs. This is important when
926 * saving state and so forth.
927 *
928 * @param pDrvIns The driver instance data.
929 */
930static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
931{
932 LogFlow(("%s:\n", __FUNCTION__));
933 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
934 if (!VDIsReadOnly(pThis->pDisk))
935 {
936 unsigned uOpenFlags;
937 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
938 AssertRC(rc);
939 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
940 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
941 AssertRC(rc);
942 pThis->fTempReadOnly = true;
943 }
944}
945
946/**
947 * Before the VM resumes we'll have to undo the read-only mode change
948 * done in drvvdSuspend.
949 *
950 * @param pDrvIns The driver instance data.
951 */
952static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
953{
954 LogFlow(("%s:\n", __FUNCTION__));
955 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
956 if (pThis->fTempReadOnly)
957 {
958 unsigned uOpenFlags;
959 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
960 AssertRC(rc);
961 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
962 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
963 AssertRC(rc);
964 pThis->fTempReadOnly = false;
965 }
966}
967
968static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
969{
970 LogFlow(("%s:\n", __FUNCTION__));
971 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
972
973 /*
974 * We must close the disk here to ensure that
975 * the backend closes all files before the
976 * async transport driver is destructed.
977 */
978 int rc = VDCloseAll(pThis->pDisk);
979 AssertRC(rc);
980}
981
982/**
983 * VBox disk container media driver registration record.
984 */
985const PDMDRVREG g_DrvVD =
986{
987 /* u32Version */
988 PDM_DRVREG_VERSION,
989 /* szDriverName */
990 "VD",
991 /* pszDescription */
992 "Generic VBox disk media driver.",
993 /* fFlags */
994 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
995 /* fClass. */
996 PDM_DRVREG_CLASS_MEDIA,
997 /* cMaxInstances */
998 ~0,
999 /* cbInstance */
1000 sizeof(VBOXDISK),
1001 /* pfnConstruct */
1002 drvvdConstruct,
1003 /* pfnDestruct */
1004 drvvdDestruct,
1005 /* pfnIOCtl */
1006 NULL,
1007 /* pfnPowerOn */
1008 NULL,
1009 /* pfnReset */
1010 NULL,
1011 /* pfnSuspend */
1012 drvvdSuspend,
1013 /* pfnResume */
1014 drvvdResume,
1015 /* pfnDetach */
1016 NULL,
1017 /* pfnPowerOff */
1018 drvvdPowerOff
1019};
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