VirtualBox

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

Last change on this file since 61541 was 60404, checked in by vboxsync, 9 years ago

VMM,Devices,Main: Implemented soft/warm reset for shutdown status codes 05h, 09h and 0Ah.

This is a shot at adjusting our VM reset handling to handle the ancient way of
getting a 286 out of protected mode and back to real mode. Our exiting reset
code (XXXR3Reset, PDMDEVREG::pfnReset, and so on) is doing a cold reset of the
system and then some additional device & memory initialization that the firmware
is usually responsible for doing. When the guest triggers a reset via the
keyboard controller, system control port A, CPU triple fault, and possibly ACPI,
only the CPU is supposed to be reset. The BIOS would then decide whether memory
and devices needed resetting as well, or if the resetter justed wanted to get out
protected mode and resume executing some real mode code pointed to by 467h.

  • New states SOFT_RESETTING and SOFT_RESETTING_LS. The latter returns to RUNNING_LS, not SUSPENDED_LS like for hard reset.
  • Added a firmware interface so the VMM/PDM can ask it whether we're supposed to do a hard reset or a soft(/warm) one.
  • Implemented firmware interface for the PC BIOS (but not EFI). It indicates soft(/warm) reset when CMOS[0xf] is 5, 9 or 10.
  • Moved the CMOS[0xf] resetting from the RTC device to the PC BIOS since it's firmware thing, not RTC.
  • Added a flag parameter to PDMDevHlpVMReset for specifying the source of the reset operation. One class of sources (GIM) will always trigger hard resets, whereas the others will check with the firmware first.
  • Added PDMR3GetResetInfo for query the flags passed to PDMDevHlpVMReset and for asking the firmware whether it's a hard or soft reset. The latter, however, is only done if only CPU 0 is active. Systems with more than one CPU in a state other than EMSTATE_WAIT_SIPI status will always be hard reset.
  • Added internal VMR3ResetFF and VMR3ResetTripleFault APIs for handling the VM_FF_RESET and VINF_EM_TRIPLE_FAULT conditions.
  • Added PMDR3ResetSoft and had it call pfnSoftReset (which is now defined).

Warning! Major PDM_DEVHLPR3_VERSION change, minor PDM_DEVREG_VERSION change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 188.5 KB
Line 
1/* $Id: DrvVD.cpp 60404 2016-04-09 23:45:55Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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_VD
23#include <VBox/vd.h>
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmstorageifs.h>
26#include <VBox/vmm/pdmasynccompletion.h>
27#include <VBox/vmm/pdmblkcache.h>
28#include <VBox/vmm/ssm.h>
29#include <iprt/asm.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/tcp.h>
36#include <iprt/semaphore.h>
37#include <iprt/sg.h>
38#include <iprt/poll.h>
39#include <iprt/pipe.h>
40#include <iprt/system.h>
41#include <iprt/memsafer.h>
42#include <iprt/memcache.h>
43#include <iprt/list.h>
44
45#ifdef VBOX_WITH_INIP
46/* All lwip header files are not C++ safe. So hack around this. */
47RT_C_DECLS_BEGIN
48#include <lwip/opt.h>
49#include <lwip/inet.h>
50#include <lwip/tcp.h>
51#include <lwip/sockets.h>
52# if LWIP_IPV6
53# include <lwip/inet6.h>
54# endif
55RT_C_DECLS_END
56#endif /* VBOX_WITH_INIP */
57
58#include "HBDMgmt.h"
59#include "IOBufMgmt.h"
60
61#include "VBoxDD.h"
62
63#ifdef VBOX_WITH_INIP
64/* Small hack to get at lwIP initialized status */
65extern bool DevINIPConfigured(void);
66#endif /* VBOX_WITH_INIP */
67
68
69/** @def VBOX_PERIODIC_FLUSH
70 * Enable support for periodically flushing the VDI to disk. This may prove
71 * useful for those nasty problems with the ultra-slow host filesystems.
72 * If this is enabled, it can be configured via the CFGM key
73 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/FlushInterval". <x>
74 * must be replaced with the correct LUN number of the disk that should
75 * do the periodic flushes. The value of the key is the number of bytes
76 * written between flushes. A value of 0 (the default) denotes no flushes. */
77#define VBOX_PERIODIC_FLUSH
78
79/** @def VBOX_IGNORE_FLUSH
80 * Enable support for ignoring VDI flush requests. This can be useful for
81 * filesystems that show bad guest IDE write performance (especially with
82 * Windows guests). NOTE that this does not disable the flushes caused by
83 * the periodic flush cache feature above.
84 * If this feature is enabled, it can be configured via the CFGM key
85 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/IgnoreFlush". <x>
86 * must be replaced with the correct LUN number of the disk that should
87 * ignore flush requests. The value of the key is a boolean. The default
88 * is to ignore flushes, i.e. true. */
89#define VBOX_IGNORE_FLUSH
90
91/*********************************************************************************************************************************
92* Defined types, constants and macros *
93*********************************************************************************************************************************/
94
95/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
96#define PDMIMEDIA_2_VBOXDISK(pInterface) \
97 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
98
99/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
100#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
101 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
102
103/** Saved state version of an I/O request .*/
104#define DRVVD_IOREQ_SAVED_STATE_VERSION UINT32_C(1)
105/** Maximum number of request errors in the release log before muting. */
106#define DRVVD_MAX_LOG_REL_ERRORS 100
107
108/** Forward declaration for the dis kcontainer. */
109typedef struct VBOXDISK *PVBOXDISK;
110
111/**
112 * VBox disk container, image information, private part.
113 */
114
115typedef struct VBOXIMAGE
116{
117 /** Pointer to next image. */
118 struct VBOXIMAGE *pNext;
119 /** Pointer to list of VD interfaces. Per-image. */
120 PVDINTERFACE pVDIfsImage;
121 /** Configuration information interface. */
122 VDINTERFACECONFIG VDIfConfig;
123 /** TCP network stack interface. */
124 VDINTERFACETCPNET VDIfTcpNet;
125 /** I/O interface. */
126 VDINTERFACEIO VDIfIo;
127} VBOXIMAGE, *PVBOXIMAGE;
128
129/**
130 * Storage backend data.
131 */
132typedef struct DRVVDSTORAGEBACKEND
133{
134 /** PDM async completion end point. */
135 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
136 /** The template. */
137 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
138 /** Event semaphore for synchronous operations. */
139 RTSEMEVENT EventSem;
140 /** Flag whether a synchronous operation is currently pending. */
141 volatile bool fSyncIoPending;
142 /** Return code of the last completed request. */
143 int rcReqLast;
144 /** Callback routine */
145 PFNVDCOMPLETED pfnCompleted;
146} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
147
148/**
149 * VD I/O request state.
150 */
151typedef enum VDIOREQSTATE
152{
153 /** Invalid. */
154 VDIOREQSTATE_INVALID = 0,
155 /** The request is not in use and resides on the free list. */
156 VDIOREQSTATE_FREE,
157 /** The request was just allocated and is not active. */
158 VDIOREQSTATE_ALLOCATED,
159 /** The request was allocated and is in use. */
160 VDIOREQSTATE_ACTIVE,
161 /** The request was suspended and is not actively processed. */
162 VDIOREQSTATE_SUSPENDED,
163 /** The request is in the last step of completion and syncs memory. */
164 VDIOREQSTATE_COMPLETING,
165 /** The request completed. */
166 VDIOREQSTATE_COMPLETED,
167 /** The request was aborted but wasn't returned as complete from the storage
168 * layer below us. */
169 VDIOREQSTATE_CANCELED,
170 /** 32bit hack. */
171 VDIOREQSTATE_32BIT_HACK = 0x7fffffff
172} VDIOREQSTATE;
173
174/**
175 * VD I/O Request.
176 */
177typedef struct PDMMEDIAEXIOREQINT
178{
179 /** List node for the list of allocated requests. */
180 RTLISTNODE NdAllocatedList;
181 /** List for requests waiting for I/O memory or on the redo list. */
182 RTLISTNODE NdLstWait;
183 /** I/O request type. */
184 PDMMEDIAEXIOREQTYPE enmType;
185 /** Request state. */
186 volatile VDIOREQSTATE enmState;
187 /** I/O request ID. */
188 PDMMEDIAEXIOREQID uIoReqId;
189 /** Pointer to the disk container. */
190 PVBOXDISK pDisk;
191 /** Flags. */
192 uint32_t fFlags;
193 /** Timestamp when the request was submitted. */
194 uint64_t tsSubmit;
195 /** Type dependent data. */
196 union
197 {
198 /** Read/Write request sepcific data. */
199 struct
200 {
201 /** Start offset of the request. */
202 uint64_t offStart;
203 /** Size of the request. */
204 size_t cbReq;
205 /** Size left for this request. */
206 size_t cbReqLeft;
207 /** Size of the allocated I/O buffer. */
208 size_t cbIoBuf;
209 /** I/O buffer descriptor. */
210 IOBUFDESC IoBuf;
211 } ReadWrite;
212 /** Discard specific data. */
213 struct
214 {
215 /** Pointer to array of ranges to discard. */
216 PRTRANGE paRanges;
217 /** Number of ranges to discard. */
218 unsigned cRanges;
219 } Discard;
220 };
221 /** Allocator specific memory - variable size. */
222 uint8_t abAlloc[1];
223} PDMMEDIAEXIOREQINT;
224/** Pointer to a VD I/O request. */
225typedef PDMMEDIAEXIOREQINT *PPDMMEDIAEXIOREQINT;
226
227/**
228 * Structure for holding a list of allocated requests.
229 */
230typedef struct VDLSTIOREQALLOC
231{
232 /** Mutex protecting the table of allocated requests. */
233 RTSEMFASTMUTEX hMtxLstIoReqAlloc;
234 /** List anchor. */
235 RTLISTANCHOR LstIoReqAlloc;
236} VDLSTIOREQALLOC;
237typedef VDLSTIOREQALLOC *PVDLSTIOREQALLOC;
238
239/** Number of bins for allocated requests. */
240#define DRVVD_VDIOREQ_ALLOC_BINS 8
241
242/**
243 * VBox disk container media main structure, private part.
244 *
245 * @implements PDMIMEDIA
246 * @implements PDMIMEDIAASYNC
247 * @implements PDMIMEDIAEX
248 * @implements PDMIMOUNT
249 * @implements VDINTERFACEERROR
250 * @implements VDINTERFACETCPNET
251 * @implements VDINTERFACEASYNCIO
252 * @implements VDINTERFACECONFIG
253 */
254typedef struct VBOXDISK
255{
256 /** The VBox disk container. */
257 PVBOXHDD pDisk;
258 /** The media interface. */
259 PDMIMEDIA IMedia;
260 /** Media port. */
261 PPDMIMEDIAPORT pDrvMediaPort;
262 /** Pointer to the driver instance. */
263 PPDMDRVINS pDrvIns;
264 /** Flag whether suspend has changed image open mode to read only. */
265 bool fTempReadOnly;
266 /** Flag whether to use the runtime (true) or startup error facility. */
267 bool fErrorUseRuntime;
268 /** Pointer to list of VD interfaces. Per-disk. */
269 PVDINTERFACE pVDIfsDisk;
270 /** Error interface. */
271 VDINTERFACEERROR VDIfError;
272 /** Thread synchronization interface. */
273 VDINTERFACETHREADSYNC VDIfThreadSync;
274
275 /** Flag whether opened disk supports async I/O operations. */
276 bool fAsyncIOSupported;
277 /** The async media interface. */
278 PDMIMEDIAASYNC IMediaAsync;
279 /** The async media port interface above. */
280 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
281 /** Pointer to the list of data we need to keep per image. */
282 PVBOXIMAGE pImages;
283 /** Flag whether the media should allow concurrent open for writing. */
284 bool fShareable;
285 /** Flag whether a merge operation has been set up. */
286 bool fMergePending;
287 /** Synchronization to prevent destruction before merge finishes. */
288 RTSEMFASTMUTEX MergeCompleteMutex;
289 /** Synchronization between merge and other image accesses. */
290 RTSEMRW MergeLock;
291 /** Source image index for merging. */
292 unsigned uMergeSource;
293 /** Target image index for merging. */
294 unsigned uMergeTarget;
295
296 /** Flag whether boot acceleration is enabled. */
297 bool fBootAccelEnabled;
298 /** Flag whether boot acceleration is currently active. */
299 bool fBootAccelActive;
300 /** Size of the disk, used for read truncation. */
301 uint64_t cbDisk;
302 /** Size of the configured buffer. */
303 size_t cbBootAccelBuffer;
304 /** Start offset for which the buffer holds data. */
305 uint64_t offDisk;
306 /** Number of valid bytes in the buffer. */
307 size_t cbDataValid;
308 /** The disk buffer. */
309 uint8_t *pbData;
310 /** Bandwidth group the disk is assigned to. */
311 char *pszBwGroup;
312 /** Flag whether async I/O using the host cache is enabled. */
313 bool fAsyncIoWithHostCache;
314
315 /** I/O interface for a cache image. */
316 VDINTERFACEIO VDIfIoCache;
317 /** Interface list for the cache image. */
318 PVDINTERFACE pVDIfsCache;
319
320 /** The block cache handle if configured. */
321 PPDMBLKCACHE pBlkCache;
322 /** Host block device manager. */
323 HBDMGR hHbdMgr;
324
325 /** Drive type. */
326 PDMMEDIATYPE enmType;
327 /** Locked indicator. */
328 bool fLocked;
329 /** Mountable indicator. */
330 bool fMountable;
331 /** Visible to the BIOS. */
332 bool fBiosVisible;
333#ifdef VBOX_PERIODIC_FLUSH
334 /** HACK: Configuration value for number of bytes written after which to flush. */
335 uint32_t cbFlushInterval;
336 /** HACK: Current count for the number of bytes written since the last flush. */
337 uint32_t cbDataWritten;
338#endif /* VBOX_PERIODIC_FLUSH */
339#ifdef VBOX_IGNORE_FLUSH
340 /** HACK: Disable flushes for this drive. */
341 bool fIgnoreFlush;
342 /** Disable async flushes for this drive. */
343 bool fIgnoreFlushAsync;
344#endif /* VBOX_IGNORE_FLUSH */
345 /** Our mountable interface. */
346 PDMIMOUNT IMount;
347 /** Pointer to the mount notify interface above us. */
348 PPDMIMOUNTNOTIFY pDrvMountNotify;
349 /** Uuid of the drive. */
350 RTUUID Uuid;
351 /** BIOS PCHS Geometry. */
352 PDMMEDIAGEOMETRY PCHSGeometry;
353 /** BIOS LCHS Geometry. */
354 PDMMEDIAGEOMETRY LCHSGeometry;
355
356 /** Cryptographic support
357 * @{ */
358 /** Pointer to the CFGM node containing the config of the crypto filter
359 * if enable. */
360 PCFGMNODE pCfgCrypto;
361 /** Config interface for the encryption filter. */
362 VDINTERFACECONFIG VDIfCfg;
363 /** Crypto interface for the encryption filter. */
364 VDINTERFACECRYPTO VDIfCrypto;
365 /** The secret key interface used to retrieve keys. */
366 PPDMISECKEY pIfSecKey;
367 /** The secret key helper interface used to notify about missing keys. */
368 PPDMISECKEYHLP pIfSecKeyHlp;
369 /** @} */
370
371 /** @name IMEDIAEX interface support specific members.
372 * @{ */
373 /** Pointer to the IMEDIAEXPORT interface above us. */
374 PPDMIMEDIAEXPORT pDrvMediaExPort;
375 /** Our extended media interface. */
376 PDMIMEDIAEX IMediaEx;
377 /** Memory cache for the I/O requests. */
378 RTMEMCACHE hIoReqCache;
379 /** I/O buffer manager. */
380 IOBUFMGR hIoBufMgr;
381 /** Active request counter. */
382 volatile uint32_t cIoReqsActive;
383 /** Bins for allocated requests. */
384 VDLSTIOREQALLOC aIoReqAllocBins[DRVVD_VDIOREQ_ALLOC_BINS];
385 /** List of requests for I/O memory to be available - VDIOREQ::NdLstWait. */
386 RTLISTANCHOR LstIoReqIoBufWait;
387 /** Critical section protecting the list of requests waiting for I/O memory. */
388 RTCRITSECT CritSectIoReqsIoBufWait;
389 /** Number of requests waiting for a I/O buffer. */
390 volatile uint32_t cIoReqsWaiting;
391 /** Flag whether we have to resubmit requests on resume because the
392 * VM was suspended due to a recoverable I/O error.
393 */
394 volatile bool fRedo;
395 /** List of requests we have to redo. */
396 RTLISTANCHOR LstIoReqRedo;
397 /** Criticial section protecting the list of waiting requests. */
398 RTCRITSECT CritSectIoReqRedo;
399 /** Number of errors logged so far. */
400 unsigned cErrors;
401 /** @} */
402} VBOXDISK;
403
404
405/*********************************************************************************************************************************
406* Internal Functions *
407*********************************************************************************************************************************/
408
409static DECLCALLBACK(void) drvvdMediaExIoReqComplete(void *pvUser1, void *pvUser2, int rcReq);
410static void drvvdPowerOffOrDestructOrUnmount(PPDMDRVINS pDrvIns);
411
412/**
413 * Internal: allocate new image descriptor and put it in the list
414 */
415static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
416{
417 AssertPtr(pThis);
418 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
419 if (pImage)
420 {
421 pImage->pVDIfsImage = NULL;
422 PVBOXIMAGE *pp = &pThis->pImages;
423 while (*pp != NULL)
424 pp = &(*pp)->pNext;
425 *pp = pImage;
426 pImage->pNext = NULL;
427 }
428
429 return pImage;
430}
431
432/**
433 * Internal: free the list of images descriptors.
434 */
435static void drvvdFreeImages(PVBOXDISK pThis)
436{
437 while (pThis->pImages != NULL)
438 {
439 PVBOXIMAGE p = pThis->pImages;
440 pThis->pImages = pThis->pImages->pNext;
441 RTMemFree(p);
442 }
443}
444
445
446/**
447 * Make the image temporarily read-only.
448 *
449 * @returns VBox status code.
450 * @param pThis The driver instance data.
451 */
452static int drvvdSetReadonly(PVBOXDISK pThis)
453{
454 int rc = VINF_SUCCESS;
455 if ( pThis->pDisk
456 && !VDIsReadOnly(pThis->pDisk))
457 {
458 unsigned uOpenFlags;
459 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
460 AssertRC(rc);
461 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
462 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
463 AssertRC(rc);
464 pThis->fTempReadOnly = true;
465 }
466 return rc;
467}
468
469
470/**
471 * Undo the temporary read-only status of the image.
472 *
473 * @returns VBox status code.
474 * @param pThis The driver instance data.
475 */
476static int drvvdSetWritable(PVBOXDISK pThis)
477{
478 int rc = VINF_SUCCESS;
479 if (pThis->fTempReadOnly)
480 {
481 unsigned uOpenFlags;
482 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
483 AssertRC(rc);
484 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
485 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
486 if (RT_SUCCESS(rc))
487 pThis->fTempReadOnly = false;
488 else
489 AssertRC(rc);
490 }
491 return rc;
492}
493
494
495/*********************************************************************************************************************************
496* Error reporting callback *
497*********************************************************************************************************************************/
498
499static DECLCALLBACK(void) drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
500 const char *pszFormat, va_list va)
501{
502 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
503 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
504 if (pThis->fErrorUseRuntime)
505 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
506 * deadlock: We are probably executed in a thread context != EMT
507 * and the EM thread would wait until every thread is suspended
508 * but we would wait for the EM thread ... */
509
510 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
511 else
512 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
513}
514
515
516/*********************************************************************************************************************************
517* VD Async I/O interface implementation *
518*********************************************************************************************************************************/
519
520#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
521
522static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser, int rcReq)
523{
524 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
525 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
526
527 LogFlowFunc(("pDrvIns=%#p pvTemplateUser=%#p pvUser=%#p rcReq=%d\n",
528 pDrvIns, pvTemplateUser, pvUser, rcReq));
529
530 if (pStorageBackend->fSyncIoPending)
531 {
532 Assert(!pvUser);
533 pStorageBackend->rcReqLast = rcReq;
534 ASMAtomicWriteBool(&pStorageBackend->fSyncIoPending, false);
535 RTSemEventSignal(pStorageBackend->EventSem);
536 }
537 else
538 {
539 int rc;
540
541 AssertPtr(pvUser);
542
543 AssertPtr(pStorageBackend->pfnCompleted);
544 rc = pStorageBackend->pfnCompleted(pvUser, rcReq);
545 AssertRC(rc);
546 }
547}
548
549static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
550 uint32_t fOpen,
551 PFNVDCOMPLETED pfnCompleted,
552 void **ppStorage)
553{
554 PVBOXDISK pThis = (PVBOXDISK)pvUser;
555 PDRVVDSTORAGEBACKEND pStorageBackend = NULL;
556 int rc = VINF_SUCCESS;
557
558 /*
559 * Check whether the backend wants to open a block device and try to prepare it
560 * if we didn't claim it yet.
561 *
562 * We only create a block device manager on demand to not waste any resources.
563 */
564 if (HBDMgrIsBlockDevice(pszLocation))
565 {
566 if (pThis->hHbdMgr == NIL_HBDMGR)
567 rc = HBDMgrCreate(&pThis->hHbdMgr);
568
569 if ( RT_SUCCESS(rc)
570 && !HBDMgrIsBlockDeviceClaimed(pThis->hHbdMgr, pszLocation))
571 rc = HBDMgrClaimBlockDevice(pThis->hHbdMgr, pszLocation);
572
573 if (RT_FAILURE(rc))
574 return rc;
575 }
576
577 pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
578 if (pStorageBackend)
579 {
580 pStorageBackend->fSyncIoPending = false;
581 pStorageBackend->rcReqLast = VINF_SUCCESS;
582 pStorageBackend->pfnCompleted = pfnCompleted;
583
584 rc = RTSemEventCreate(&pStorageBackend->EventSem);
585 if (RT_SUCCESS(rc))
586 {
587 rc = PDMDrvHlpAsyncCompletionTemplateCreate(pThis->pDrvIns, &pStorageBackend->pTemplate,
588 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
589 if (RT_SUCCESS(rc))
590 {
591 uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
592 ? PDMACEP_FILE_FLAGS_READ_ONLY
593 : 0;
594 if (pThis->fShareable)
595 {
596 Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE);
597
598 fFlags |= PDMACEP_FILE_FLAGS_DONT_LOCK;
599 }
600 if (pThis->fAsyncIoWithHostCache)
601 fFlags |= PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED;
602
603 rc = PDMR3AsyncCompletionEpCreateForFile(&pStorageBackend->pEndpoint,
604 pszLocation, fFlags,
605 pStorageBackend->pTemplate);
606
607 if (RT_SUCCESS(rc))
608 {
609 if (pThis->pszBwGroup)
610 rc = PDMR3AsyncCompletionEpSetBwMgr(pStorageBackend->pEndpoint, pThis->pszBwGroup);
611
612 if (RT_SUCCESS(rc))
613 {
614 LogFlow(("drvvdAsyncIOOpen: Successfully opened '%s'; fOpen=%#x pStorage=%p\n",
615 pszLocation, fOpen, pStorageBackend));
616 *ppStorage = pStorageBackend;
617 return VINF_SUCCESS;
618 }
619
620 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
621 }
622
623 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
624 }
625 RTSemEventDestroy(pStorageBackend->EventSem);
626 }
627 RTMemFree(pStorageBackend);
628 }
629 else
630 rc = VERR_NO_MEMORY;
631
632 return rc;
633}
634
635static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
636{
637 PVBOXDISK pThis = (PVBOXDISK)pvUser;
638 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
639
640 /*
641 * We don't unclaim any block devices on purpose here because they
642 * might get reopened shortly (switching to readonly during suspend)
643 *
644 * Block devices will get unclaimed during destruction of the driver.
645 */
646
647 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
648 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
649 RTSemEventDestroy(pStorageBackend->EventSem);
650 RTMemFree(pStorageBackend);
651 return VINF_SUCCESS;;
652}
653
654static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
655 void *pvBuf, size_t cbRead, size_t *pcbRead)
656{
657 PVBOXDISK pThis = (PVBOXDISK)pvUser;
658 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
659 RTSGSEG DataSeg;
660 PPDMASYNCCOMPLETIONTASK pTask;
661
662 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
663 Assert(!fOld);
664 DataSeg.cbSeg = cbRead;
665 DataSeg.pvSeg = pvBuf;
666
667 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
668 if (RT_FAILURE(rc))
669 return rc;
670
671 if (rc == VINF_AIO_TASK_PENDING)
672 {
673 /* Wait */
674 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
675 AssertRC(rc);
676 }
677 else
678 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
679
680 if (pcbRead)
681 *pcbRead = cbRead;
682
683 return pStorageBackend->rcReqLast;
684}
685
686static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
687 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
688{
689 PVBOXDISK pThis = (PVBOXDISK)pvUser;
690 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
691 RTSGSEG DataSeg;
692 PPDMASYNCCOMPLETIONTASK pTask;
693
694 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
695 Assert(!fOld);
696 DataSeg.cbSeg = cbWrite;
697 DataSeg.pvSeg = (void *)pvBuf;
698
699 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
700 if (RT_FAILURE(rc))
701 return rc;
702
703 if (rc == VINF_AIO_TASK_PENDING)
704 {
705 /* Wait */
706 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
707 AssertRC(rc);
708 }
709 else
710 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
711
712 if (pcbWritten)
713 *pcbWritten = cbWrite;
714
715 return pStorageBackend->rcReqLast;
716}
717
718static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
719{
720 PVBOXDISK pThis = (PVBOXDISK)pvUser;
721 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
722 PPDMASYNCCOMPLETIONTASK pTask;
723
724 LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage));
725
726 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
727 Assert(!fOld);
728
729 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
730 if (RT_FAILURE(rc))
731 return rc;
732
733 if (rc == VINF_AIO_TASK_PENDING)
734 {
735 /* Wait */
736 LogFlowFunc(("Waiting for flush to complete\n"));
737 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
738 AssertRC(rc);
739 }
740 else
741 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
742
743 return pStorageBackend->rcReqLast;
744}
745
746static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
747 PCRTSGSEG paSegments, size_t cSegments,
748 size_t cbRead, void *pvCompletion,
749 void **ppTask)
750{
751 PVBOXDISK pThis = (PVBOXDISK)pvUser;
752 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
753
754 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbRead,
755 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
756 if (rc == VINF_AIO_TASK_PENDING)
757 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
758
759 return rc;
760}
761
762static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
763 PCRTSGSEG paSegments, size_t cSegments,
764 size_t cbWrite, void *pvCompletion,
765 void **ppTask)
766{
767 PVBOXDISK pThis = (PVBOXDISK)pvUser;
768 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
769
770 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbWrite,
771 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
772 if (rc == VINF_AIO_TASK_PENDING)
773 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
774
775 return rc;
776}
777
778static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
779 void *pvCompletion, void **ppTask)
780{
781 PVBOXDISK pThis = (PVBOXDISK)pvUser;
782 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
783
784 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, pvCompletion,
785 (PPPDMASYNCCOMPLETIONTASK)ppTask);
786 if (rc == VINF_AIO_TASK_PENDING)
787 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
788
789 return rc;
790}
791
792static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
793{
794 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
795 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
796
797 return PDMR3AsyncCompletionEpGetSize(pStorageBackend->pEndpoint, pcbSize);
798}
799
800static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
801{
802 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
803 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
804
805 return PDMR3AsyncCompletionEpSetSize(pStorageBackend->pEndpoint, cbSize);
806}
807
808static DECLCALLBACK(int) drvvdAsyncIOSetAllocationSize(void *pvUser, void *pStorage, uint64_t cbSize,
809 uint32_t fFlags)
810{
811 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
812 return VERR_NOT_SUPPORTED;
813}
814
815#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
816
817
818/*********************************************************************************************************************************
819* VD Thread Synchronization interface implementation *
820*********************************************************************************************************************************/
821
822static DECLCALLBACK(int) drvvdThreadStartRead(void *pvUser)
823{
824 PVBOXDISK pThis = (PVBOXDISK)pvUser;
825
826 return RTSemRWRequestRead(pThis->MergeLock, RT_INDEFINITE_WAIT);
827}
828
829static DECLCALLBACK(int) drvvdThreadFinishRead(void *pvUser)
830{
831 PVBOXDISK pThis = (PVBOXDISK)pvUser;
832
833 return RTSemRWReleaseRead(pThis->MergeLock);
834}
835
836static DECLCALLBACK(int) drvvdThreadStartWrite(void *pvUser)
837{
838 PVBOXDISK pThis = (PVBOXDISK)pvUser;
839
840 return RTSemRWRequestWrite(pThis->MergeLock, RT_INDEFINITE_WAIT);
841}
842
843static DECLCALLBACK(int) drvvdThreadFinishWrite(void *pvUser)
844{
845 PVBOXDISK pThis = (PVBOXDISK)pvUser;
846
847 return RTSemRWReleaseWrite(pThis->MergeLock);
848}
849
850
851/*********************************************************************************************************************************
852* VD Configuration interface implementation *
853*********************************************************************************************************************************/
854
855static DECLCALLBACK(bool) drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
856{
857 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
858}
859
860static DECLCALLBACK(int) drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
861{
862 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
863}
864
865static DECLCALLBACK(int) drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
866{
867 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
868}
869
870static DECLCALLBACK(int) drvvdCfgQueryBytes(void *pvUser, const char *pszName, void *ppvData, size_t cbData)
871{
872 return CFGMR3QueryBytes((PCFGMNODE)pvUser, pszName, ppvData, cbData);
873}
874
875
876/*******************************************************************************
877* VD Crypto interface implementation for the encryption support *
878*******************************************************************************/
879
880static DECLCALLBACK(int) drvvdCryptoKeyRetain(void *pvUser, const char *pszId, const uint8_t **ppbKey, size_t *pcbKey)
881{
882 PVBOXDISK pThis = (PVBOXDISK)pvUser;
883 int rc = VINF_SUCCESS;
884
885 AssertPtr(pThis->pIfSecKey);
886 if (pThis->pIfSecKey)
887 rc = pThis->pIfSecKey->pfnKeyRetain(pThis->pIfSecKey, pszId, ppbKey, pcbKey);
888 else
889 rc = VERR_NOT_SUPPORTED;
890
891 return rc;
892}
893
894static DECLCALLBACK(int) drvvdCryptoKeyRelease(void *pvUser, const char *pszId)
895{
896 PVBOXDISK pThis = (PVBOXDISK)pvUser;
897 int rc = VINF_SUCCESS;
898
899 AssertPtr(pThis->pIfSecKey);
900 if (pThis->pIfSecKey)
901 rc = pThis->pIfSecKey->pfnKeyRelease(pThis->pIfSecKey, pszId);
902 else
903 rc = VERR_NOT_SUPPORTED;
904
905 return rc;
906}
907
908static DECLCALLBACK(int) drvvdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
909{
910 PVBOXDISK pThis = (PVBOXDISK)pvUser;
911 int rc = VINF_SUCCESS;
912
913 AssertPtr(pThis->pIfSecKey);
914 if (pThis->pIfSecKey)
915 rc = pThis->pIfSecKey->pfnPasswordRetain(pThis->pIfSecKey, pszId, ppszPassword);
916 else
917 rc = VERR_NOT_SUPPORTED;
918
919 return rc;
920}
921
922static DECLCALLBACK(int) drvvdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
923{
924 PVBOXDISK pThis = (PVBOXDISK)pvUser;
925 int rc = VINF_SUCCESS;
926
927 AssertPtr(pThis->pIfSecKey);
928 if (pThis->pIfSecKey)
929 rc = pThis->pIfSecKey->pfnPasswordRelease(pThis->pIfSecKey, pszId);
930 else
931 rc = VERR_NOT_SUPPORTED;
932
933 return rc;
934}
935
936#ifdef VBOX_WITH_INIP
937
938
939/*********************************************************************************************************************************
940* VD TCP network stack interface implementation - INIP case *
941*********************************************************************************************************************************/
942
943/**
944 * vvl: this structure duplicate meaning of sockaddr,
945 * perhaps it'd be better to get rid of it.
946 */
947typedef union INIPSOCKADDRUNION
948{
949 struct sockaddr Addr;
950 struct sockaddr_in Ipv4;
951#if LWIP_IPV6
952 struct sockaddr_in6 Ipv6;
953#endif
954} INIPSOCKADDRUNION;
955
956typedef struct INIPSOCKET
957{
958 int hSock;
959} INIPSOCKET, *PINIPSOCKET;
960
961static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock);
962
963/** @interface_method_impl{VDINTERFACETCPNET,pfnSocketCreate} */
964static DECLCALLBACK(int) drvvdINIPSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
965{
966 PINIPSOCKET pSocketInt = NULL;
967
968 /*
969 * The extended select method is not supported because it is impossible to wakeup
970 * the thread.
971 */
972 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
973 return VERR_NOT_SUPPORTED;
974
975 pSocketInt = (PINIPSOCKET)RTMemAllocZ(sizeof(INIPSOCKET));
976 if (pSocketInt)
977 {
978 pSocketInt->hSock = INT32_MAX;
979 *pSock = (VDSOCKET)pSocketInt;
980 return VINF_SUCCESS;
981 }
982
983 return VERR_NO_MEMORY;
984}
985
986/** @interface_method_impl{VDINTERFACETCPNET,pfnSocketCreate} */
987static DECLCALLBACK(int) drvvdINIPSocketDestroy(VDSOCKET Sock)
988{
989 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
990
991 RTMemFree(pSocketInt);
992 return VINF_SUCCESS;
993}
994
995/** @interface_method_impl{VDINTERFACETCPNET,pfnClientConnect} */
996static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
997 RTMSINTERVAL cMillies)
998{
999 int rc = VINF_SUCCESS;
1000 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1001 int iInetFamily = PF_INET;
1002 struct in_addr ip;
1003#if LWIP_IPV6
1004 ip6_addr_t ip6;
1005#endif
1006
1007 NOREF(cMillies); /** LwIP doesn't support connect timeout. */
1008
1009 /* Check whether lwIP is set up in this VM instance. */
1010 if (!DevINIPConfigured())
1011 {
1012 LogRelFunc(("no IP stack\n"));
1013 return VERR_NET_HOST_UNREACHABLE;
1014 }
1015 /* Resolve hostname. As there is no standard resolver for lwIP yet,
1016 * just accept numeric IP addresses for now. */
1017#if LWIP_IPV6
1018 if (inet6_aton(pszAddress, &ip6))
1019 iInetFamily = PF_INET6;
1020 else /* concatination with if */
1021#endif
1022 if (!lwip_inet_aton(pszAddress, &ip))
1023 {
1024 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
1025 return VERR_NET_HOST_UNREACHABLE;
1026 }
1027 /* Create socket and connect. */
1028 int iSock = lwip_socket(iInetFamily, SOCK_STREAM, 0);
1029 if (iSock != -1)
1030 {
1031 struct sockaddr *pSockAddr = NULL;
1032 struct sockaddr_in InAddr = {0};
1033#if LWIP_IPV6
1034 struct sockaddr_in6 In6Addr = {0};
1035#endif
1036 if (iInetFamily == PF_INET)
1037 {
1038 InAddr.sin_family = AF_INET;
1039 InAddr.sin_port = htons(uPort);
1040 InAddr.sin_addr = ip;
1041 InAddr.sin_len = sizeof(InAddr);
1042 pSockAddr = (struct sockaddr *)&InAddr;
1043 }
1044#if LWIP_IPV6
1045 else
1046 {
1047 In6Addr.sin6_family = AF_INET6;
1048 In6Addr.sin6_port = htons(uPort);
1049 memcpy(&In6Addr.sin6_addr, &ip6, sizeof(ip6));
1050 In6Addr.sin6_len = sizeof(In6Addr);
1051 pSockAddr = (struct sockaddr *)&In6Addr;
1052 }
1053#endif
1054 if ( pSockAddr
1055 && !lwip_connect(iSock, pSockAddr, pSockAddr->sa_len))
1056 {
1057 pSocketInt->hSock = iSock;
1058 return VINF_SUCCESS;
1059 }
1060 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
1061 lwip_close(iSock);
1062 }
1063 else
1064 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
1065 return rc;
1066}
1067
1068/** @interface_method_impl{VDINTERFACETCPNET,pfnClientClose} */
1069static DECLCALLBACK(int) drvvdINIPClientClose(VDSOCKET Sock)
1070{
1071 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1072
1073 lwip_close(pSocketInt->hSock);
1074 pSocketInt->hSock = INT32_MAX;
1075 return VINF_SUCCESS; /** @todo real solution needed */
1076}
1077
1078/** @interface_method_impl{VDINTERFACETCPNET,pfnIsClientConnected} */
1079static DECLCALLBACK(bool) drvvdINIPIsClientConnected(VDSOCKET Sock)
1080{
1081 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1082
1083 return pSocketInt->hSock != INT32_MAX;
1084}
1085
1086/** @interface_method_impl{VDINTERFACETCPNET,pfnSelectOne} */
1087static DECLCALLBACK(int) drvvdINIPSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
1088{
1089 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1090 fd_set fdsetR;
1091 FD_ZERO(&fdsetR);
1092 FD_SET((uintptr_t)pSocketInt->hSock, &fdsetR);
1093 fd_set fdsetE = fdsetR;
1094
1095 int rc;
1096 if (cMillies == RT_INDEFINITE_WAIT)
1097 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, NULL);
1098 else
1099 {
1100 struct timeval timeout;
1101 timeout.tv_sec = cMillies / 1000;
1102 timeout.tv_usec = (cMillies % 1000) * 1000;
1103 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, &timeout);
1104 }
1105 if (rc > 0)
1106 return VINF_SUCCESS;
1107 if (rc == 0)
1108 return VERR_TIMEOUT;
1109 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
1110}
1111
1112/** @interface_method_impl{VDINTERFACETCPNET,pfnRead} */
1113static DECLCALLBACK(int) drvvdINIPRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1114{
1115 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1116
1117 /* Do params checking */
1118 if (!pvBuffer || !cbBuffer)
1119 {
1120 AssertMsgFailed(("Invalid params\n"));
1121 return VERR_INVALID_PARAMETER;
1122 }
1123
1124 /*
1125 * Read loop.
1126 * If pcbRead is NULL we have to fill the entire buffer!
1127 */
1128 size_t cbRead = 0;
1129 size_t cbToRead = cbBuffer;
1130 for (;;)
1131 {
1132 /** @todo this clipping here is just in case (the send function
1133 * needed it, so I added it here, too). Didn't investigate if this
1134 * really has issues. Better be safe than sorry. */
1135 ssize_t cbBytesRead = lwip_recv(pSocketInt->hSock, (char *)pvBuffer + cbRead,
1136 RT_MIN(cbToRead, 32768), 0);
1137 if (cbBytesRead < 0)
1138 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
1139 if (cbBytesRead == 0 && errno) /** @todo r=bird: lwip_recv will not touch errno on Windows. This may apply to other hosts as well */
1140 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
1141 if (pcbRead)
1142 {
1143 /* return partial data */
1144 *pcbRead = cbBytesRead;
1145 break;
1146 }
1147
1148 /* read more? */
1149 cbRead += cbBytesRead;
1150 if (cbRead == cbBuffer)
1151 break;
1152
1153 /* next */
1154 cbToRead = cbBuffer - cbRead;
1155 }
1156
1157 return VINF_SUCCESS;
1158}
1159
1160/** @interface_method_impl{VDINTERFACETCPNET,pfnWrite} */
1161static DECLCALLBACK(int) drvvdINIPWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
1162{
1163 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1164
1165 do
1166 {
1167 /** @todo lwip send only supports up to 65535 bytes in a single
1168 * send (stupid limitation buried in the code), so make sure we
1169 * don't get any wraparounds. This should be moved to DevINIP
1170 * stack interface once that's implemented. */
1171 ssize_t cbWritten = lwip_send(pSocketInt->hSock, (void *)pvBuffer,
1172 RT_MIN(cbBuffer, 32768), 0);
1173 if (cbWritten < 0)
1174 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
1175 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
1176 cbWritten, cbBuffer));
1177 cbBuffer -= cbWritten;
1178 pvBuffer = (const char *)pvBuffer + cbWritten;
1179 } while (cbBuffer);
1180
1181 return VINF_SUCCESS;
1182}
1183
1184/** @interface_method_impl{VDINTERFACETCPNET,pfnSgWrite} */
1185static DECLCALLBACK(int) drvvdINIPSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
1186{
1187 int rc = VINF_SUCCESS;
1188
1189 /* This is an extremely crude emulation, however it's good enough
1190 * for our iSCSI code. INIP has no sendmsg(). */
1191 for (unsigned i = 0; i < pSgBuf->cSegs; i++)
1192 {
1193 rc = drvvdINIPWrite(Sock, pSgBuf->paSegs[i].pvSeg,
1194 pSgBuf->paSegs[i].cbSeg);
1195 if (RT_FAILURE(rc))
1196 break;
1197 }
1198 if (RT_SUCCESS(rc))
1199 drvvdINIPFlush(Sock);
1200
1201 return rc;
1202}
1203
1204/** @interface_method_impl{VDINTERFACETCPNET,pfnFlush} */
1205static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock)
1206{
1207 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1208
1209 int fFlag = 1;
1210 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
1211 (const char *)&fFlag, sizeof(fFlag));
1212 fFlag = 0;
1213 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
1214 (const char *)&fFlag, sizeof(fFlag));
1215 return VINF_SUCCESS;
1216}
1217
1218/** @interface_method_impl{VDINTERFACETCPNET,pfnSetSendCoalescing} */
1219static DECLCALLBACK(int) drvvdINIPSetSendCoalescing(VDSOCKET Sock, bool fEnable)
1220{
1221 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1222
1223 int fFlag = fEnable ? 0 : 1;
1224 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
1225 (const char *)&fFlag, sizeof(fFlag));
1226 return VINF_SUCCESS;
1227}
1228
1229/** @interface_method_impl{VDINTERFACETCPNET,pfnGetLocalAddress} */
1230static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1231{
1232 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1233 INIPSOCKADDRUNION u;
1234 socklen_t cbAddr = sizeof(u);
1235 RT_ZERO(u);
1236 if (!lwip_getsockname(pSocketInt->hSock, &u.Addr, &cbAddr))
1237 {
1238 /*
1239 * Convert the address.
1240 */
1241 if ( cbAddr == sizeof(struct sockaddr_in)
1242 && u.Addr.sa_family == AF_INET)
1243 {
1244 RT_ZERO(*pAddr);
1245 pAddr->enmType = RTNETADDRTYPE_IPV4;
1246 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
1247 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
1248 }
1249#if LWIP_IPV6
1250 else if ( cbAddr == sizeof(struct sockaddr_in6)
1251 && u.Addr.sa_family == AF_INET6)
1252 {
1253 RT_ZERO(*pAddr);
1254 pAddr->enmType = RTNETADDRTYPE_IPV6;
1255 pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
1256 memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
1257 }
1258#endif
1259 else
1260 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
1261 return VINF_SUCCESS;
1262 }
1263 return VERR_NET_OPERATION_NOT_SUPPORTED;
1264}
1265
1266/** @interface_method_impl{VDINTERFACETCPNET,pfnGetPeerAddress} */
1267static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1268{
1269 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1270 INIPSOCKADDRUNION u;
1271 socklen_t cbAddr = sizeof(u);
1272 RT_ZERO(u);
1273 if (!lwip_getpeername(pSocketInt->hSock, &u.Addr, &cbAddr))
1274 {
1275 /*
1276 * Convert the address.
1277 */
1278 if ( cbAddr == sizeof(struct sockaddr_in)
1279 && u.Addr.sa_family == AF_INET)
1280 {
1281 RT_ZERO(*pAddr);
1282 pAddr->enmType = RTNETADDRTYPE_IPV4;
1283 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
1284 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
1285 }
1286#if LWIP_IPV6
1287 else if ( cbAddr == sizeof(struct sockaddr_in6)
1288 && u.Addr.sa_family == AF_INET6)
1289 {
1290 RT_ZERO(*pAddr);
1291 pAddr->enmType = RTNETADDRTYPE_IPV6;
1292 pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
1293 memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
1294 }
1295#endif
1296 else
1297 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
1298 return VINF_SUCCESS;
1299 }
1300 return VERR_NET_OPERATION_NOT_SUPPORTED;
1301}
1302
1303/** @interface_method_impl{VDINTERFACETCPNET,pfnSelectOneEx} */
1304static DECLCALLBACK(int) drvvdINIPSelectOneEx(VDSOCKET Sock, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
1305{
1306 AssertMsgFailed(("Not supported!\n"));
1307 return VERR_NOT_SUPPORTED;
1308}
1309
1310/** @interface_method_impl{VDINTERFACETCPNET,pfnPoke} */
1311static DECLCALLBACK(int) drvvdINIPPoke(VDSOCKET Sock)
1312{
1313 AssertMsgFailed(("Not supported!\n"));
1314 return VERR_NOT_SUPPORTED;
1315}
1316
1317#endif /* VBOX_WITH_INIP */
1318
1319
1320/*********************************************************************************************************************************
1321* VD TCP network stack interface implementation - Host TCP case *
1322*********************************************************************************************************************************/
1323
1324/**
1325 * Socket data.
1326 */
1327typedef struct VDSOCKETINT
1328{
1329 /** IPRT socket handle. */
1330 RTSOCKET hSocket;
1331 /** Pollset with the wakeup pipe and socket. */
1332 RTPOLLSET hPollSet;
1333 /** Pipe endpoint - read (in the pollset). */
1334 RTPIPE hPipeR;
1335 /** Pipe endpoint - write. */
1336 RTPIPE hPipeW;
1337 /** Flag whether the thread was woken up. */
1338 volatile bool fWokenUp;
1339 /** Flag whether the thread is waiting in the select call. */
1340 volatile bool fWaiting;
1341 /** Old event mask. */
1342 uint32_t fEventsOld;
1343} VDSOCKETINT, *PVDSOCKETINT;
1344
1345/** Pollset id of the socket. */
1346#define VDSOCKET_POLL_ID_SOCKET 0
1347/** Pollset id of the pipe. */
1348#define VDSOCKET_POLL_ID_PIPE 1
1349
1350/** @interface_method_impl{VDINTERFACETCPNET,pfnSocketCreate} */
1351static DECLCALLBACK(int) drvvdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
1352{
1353 int rc = VINF_SUCCESS;
1354 int rc2 = VINF_SUCCESS;
1355 PVDSOCKETINT pSockInt = NULL;
1356
1357 pSockInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
1358 if (!pSockInt)
1359 return VERR_NO_MEMORY;
1360
1361 pSockInt->hSocket = NIL_RTSOCKET;
1362 pSockInt->hPollSet = NIL_RTPOLLSET;
1363 pSockInt->hPipeR = NIL_RTPIPE;
1364 pSockInt->hPipeW = NIL_RTPIPE;
1365 pSockInt->fWokenUp = false;
1366 pSockInt->fWaiting = false;
1367
1368 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
1369 {
1370 /* Init pipe and pollset. */
1371 rc = RTPipeCreate(&pSockInt->hPipeR, &pSockInt->hPipeW, 0);
1372 if (RT_SUCCESS(rc))
1373 {
1374 rc = RTPollSetCreate(&pSockInt->hPollSet);
1375 if (RT_SUCCESS(rc))
1376 {
1377 rc = RTPollSetAddPipe(pSockInt->hPollSet, pSockInt->hPipeR,
1378 RTPOLL_EVT_READ, VDSOCKET_POLL_ID_PIPE);
1379 if (RT_SUCCESS(rc))
1380 {
1381 *pSock = pSockInt;
1382 return VINF_SUCCESS;
1383 }
1384
1385 RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1386 rc2 = RTPollSetDestroy(pSockInt->hPollSet);
1387 AssertRC(rc2);
1388 }
1389
1390 rc2 = RTPipeClose(pSockInt->hPipeR);
1391 AssertRC(rc2);
1392 rc2 = RTPipeClose(pSockInt->hPipeW);
1393 AssertRC(rc2);
1394 }
1395 }
1396 else
1397 {
1398 *pSock = pSockInt;
1399 return VINF_SUCCESS;
1400 }
1401
1402 RTMemFree(pSockInt);
1403
1404 return rc;
1405}
1406
1407/** @interface_method_impl{VDINTERFACETCPNET,pfnSocketDestroy} */
1408static DECLCALLBACK(int) drvvdTcpSocketDestroy(VDSOCKET Sock)
1409{
1410 int rc = VINF_SUCCESS;
1411 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1412
1413 /* Destroy the pipe and pollset if necessary. */
1414 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1415 {
1416 if (pSockInt->hSocket != NIL_RTSOCKET)
1417 {
1418 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1419 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1420 }
1421 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1422 AssertRC(rc);
1423 rc = RTPollSetDestroy(pSockInt->hPollSet);
1424 AssertRC(rc);
1425 rc = RTPipeClose(pSockInt->hPipeR);
1426 AssertRC(rc);
1427 rc = RTPipeClose(pSockInt->hPipeW);
1428 AssertRC(rc);
1429 }
1430
1431 if (pSockInt->hSocket != NIL_RTSOCKET)
1432 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1433
1434 RTMemFree(pSockInt);
1435
1436 return rc;
1437}
1438
1439/** @interface_method_impl{VDINTERFACETCPNET,pfnClientConnect} */
1440static DECLCALLBACK(int) drvvdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
1441 RTMSINTERVAL cMillies)
1442{
1443 int rc = VINF_SUCCESS;
1444 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1445
1446 rc = RTTcpClientConnectEx(pszAddress, uPort, &pSockInt->hSocket, cMillies, NULL);
1447 if (RT_SUCCESS(rc))
1448 {
1449 /* Add to the pollset if required. */
1450 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1451 {
1452 pSockInt->fEventsOld = RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR;
1453
1454 rc = RTPollSetAddSocket(pSockInt->hPollSet, pSockInt->hSocket,
1455 pSockInt->fEventsOld, VDSOCKET_POLL_ID_SOCKET);
1456 }
1457
1458 if (RT_SUCCESS(rc))
1459 return VINF_SUCCESS;
1460
1461 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1462 }
1463
1464 return rc;
1465}
1466
1467/** @interface_method_impl{VDINTERFACETCPNET,pfnClientClose} */
1468static DECLCALLBACK(int) drvvdTcpClientClose(VDSOCKET Sock)
1469{
1470 int rc = VINF_SUCCESS;
1471 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1472
1473 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1474 {
1475 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1476 AssertRC(rc);
1477 }
1478
1479 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1480 pSockInt->hSocket = NIL_RTSOCKET;
1481
1482 return rc;
1483}
1484
1485/** @interface_method_impl{VDINTERFACETCPNET,pfnIsClientConnected} */
1486static DECLCALLBACK(bool) drvvdTcpIsClientConnected(VDSOCKET Sock)
1487{
1488 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1489
1490 return pSockInt->hSocket != NIL_RTSOCKET;
1491}
1492
1493/** @interface_method_impl{VDINTERFACETCPNET,pfnSelectOne} */
1494static DECLCALLBACK(int) drvvdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
1495{
1496 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1497
1498 return RTTcpSelectOne(pSockInt->hSocket, cMillies);
1499}
1500
1501/** @interface_method_impl{VDINTERFACETCPNET,pfnRead} */
1502static DECLCALLBACK(int) drvvdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1503{
1504 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1505
1506 return RTTcpRead(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1507}
1508
1509/** @interface_method_impl{VDINTERFACETCPNET,pfnWrite} */
1510static DECLCALLBACK(int) drvvdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
1511{
1512 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1513
1514 return RTTcpWrite(pSockInt->hSocket, pvBuffer, cbBuffer);
1515}
1516
1517/** @interface_method_impl{VDINTERFACETCPNET,pfnSgWrite} */
1518static DECLCALLBACK(int) drvvdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
1519{
1520 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1521
1522 return RTTcpSgWrite(pSockInt->hSocket, pSgBuf);
1523}
1524
1525/** @interface_method_impl{VDINTERFACETCPNET,pfnReadNB} */
1526static DECLCALLBACK(int) drvvdTcpReadNB(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1527{
1528 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1529
1530 return RTTcpReadNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1531}
1532
1533/** @interface_method_impl{VDINTERFACETCPNET,pfnWriteNB} */
1534static DECLCALLBACK(int) drvvdTcpWriteNB(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1535{
1536 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1537
1538 return RTTcpWriteNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbWritten);
1539}
1540
1541/** @interface_method_impl{VDINTERFACETCPNET,pfnSgWriteNB} */
1542static DECLCALLBACK(int) drvvdTcpSgWriteNB(VDSOCKET Sock, PRTSGBUF pSgBuf, size_t *pcbWritten)
1543{
1544 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1545
1546 return RTTcpSgWriteNB(pSockInt->hSocket, pSgBuf, pcbWritten);
1547}
1548
1549/** @interface_method_impl{VDINTERFACETCPNET,pfnFlush} */
1550static DECLCALLBACK(int) drvvdTcpFlush(VDSOCKET Sock)
1551{
1552 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1553
1554 return RTTcpFlush(pSockInt->hSocket);
1555}
1556
1557/** @interface_method_impl{VDINTERFACETCPNET,pfnSetSendCoalescing} */
1558static DECLCALLBACK(int) drvvdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
1559{
1560 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1561
1562 return RTTcpSetSendCoalescing(pSockInt->hSocket, fEnable);
1563}
1564
1565/** @interface_method_impl{VDINTERFACETCPNET,pfnGetLocalAddress} */
1566static DECLCALLBACK(int) drvvdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1567{
1568 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1569
1570 return RTTcpGetLocalAddress(pSockInt->hSocket, pAddr);
1571}
1572
1573/** @interface_method_impl{VDINTERFACETCPNET,pfnGetPeerAddress} */
1574static DECLCALLBACK(int) drvvdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1575{
1576 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1577
1578 return RTTcpGetPeerAddress(pSockInt->hSocket, pAddr);
1579}
1580
1581static DECLCALLBACK(int) drvvdTcpSelectOneExPoll(VDSOCKET Sock, uint32_t fEvents,
1582 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1583{
1584 int rc = VINF_SUCCESS;
1585 uint32_t id = 0;
1586 uint32_t fEventsRecv = 0;
1587 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1588
1589 *pfEvents = 0;
1590
1591 if ( pSockInt->fEventsOld != fEvents
1592 && pSockInt->hSocket != NIL_RTSOCKET)
1593 {
1594 uint32_t fPollEvents = 0;
1595
1596 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1597 fPollEvents |= RTPOLL_EVT_READ;
1598 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1599 fPollEvents |= RTPOLL_EVT_WRITE;
1600 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1601 fPollEvents |= RTPOLL_EVT_ERROR;
1602
1603 rc = RTPollSetEventsChange(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET, fPollEvents);
1604 if (RT_FAILURE(rc))
1605 return rc;
1606
1607 pSockInt->fEventsOld = fEvents;
1608 }
1609
1610 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1611 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1612 {
1613 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1614 return VERR_INTERRUPTED;
1615 }
1616
1617 rc = RTPoll(pSockInt->hPollSet, cMillies, &fEventsRecv, &id);
1618 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
1619
1620 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1621
1622 if (RT_SUCCESS(rc))
1623 {
1624 if (id == VDSOCKET_POLL_ID_SOCKET)
1625 {
1626 fEventsRecv &= RTPOLL_EVT_VALID_MASK;
1627
1628 if (fEventsRecv & RTPOLL_EVT_READ)
1629 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1630 if (fEventsRecv & RTPOLL_EVT_WRITE)
1631 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1632 if (fEventsRecv & RTPOLL_EVT_ERROR)
1633 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1634 }
1635 else
1636 {
1637 size_t cbRead = 0;
1638 uint8_t abBuf[10];
1639 Assert(id == VDSOCKET_POLL_ID_PIPE);
1640 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1641
1642 /* We got interrupted, drain the pipe. */
1643 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1644 AssertRC(rc);
1645
1646 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1647
1648 rc = VERR_INTERRUPTED;
1649 }
1650 }
1651
1652 return rc;
1653}
1654
1655/** @interface_method_impl{VDINTERFACETCPNET,pfnSelectOneEx} */
1656static DECLCALLBACK(int) drvvdTcpSelectOneExNoPoll(VDSOCKET Sock, uint32_t fEvents,
1657 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1658{
1659 int rc = VINF_SUCCESS;
1660 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1661
1662 *pfEvents = 0;
1663
1664 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1665 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1666 {
1667 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1668 return VERR_INTERRUPTED;
1669 }
1670
1671 if ( pSockInt->hSocket == NIL_RTSOCKET
1672 || !fEvents)
1673 {
1674 /*
1675 * Only the pipe is configured or the caller doesn't wait for a socket event,
1676 * wait until there is something to read from the pipe.
1677 */
1678 size_t cbRead = 0;
1679 char ch = 0;
1680 rc = RTPipeReadBlocking(pSockInt->hPipeR, &ch, 1, &cbRead);
1681 if (RT_SUCCESS(rc))
1682 {
1683 Assert(cbRead == 1);
1684 rc = VERR_INTERRUPTED;
1685 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1686 }
1687 }
1688 else
1689 {
1690 uint32_t fSelectEvents = 0;
1691
1692 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1693 fSelectEvents |= RTSOCKET_EVT_READ;
1694 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1695 fSelectEvents |= RTSOCKET_EVT_WRITE;
1696 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1697 fSelectEvents |= RTSOCKET_EVT_ERROR;
1698
1699 if (fEvents & VD_INTERFACETCPNET_HINT_INTERRUPT)
1700 {
1701 uint32_t fEventsRecv = 0;
1702
1703 /* Make sure the socket is not in the pollset. */
1704 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1705 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1706
1707 for (;;)
1708 {
1709 uint32_t id = 0;
1710 rc = RTPoll(pSockInt->hPollSet, 5, &fEvents, &id);
1711 if (rc == VERR_TIMEOUT)
1712 {
1713 /* Check the socket. */
1714 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 0);
1715 if (RT_SUCCESS(rc))
1716 {
1717 if (fEventsRecv & RTSOCKET_EVT_READ)
1718 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1719 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1720 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1721 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1722 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1723 break; /* Quit */
1724 }
1725 else if (rc != VERR_TIMEOUT)
1726 break;
1727 }
1728 else if (RT_SUCCESS(rc))
1729 {
1730 size_t cbRead = 0;
1731 uint8_t abBuf[10];
1732 Assert(id == VDSOCKET_POLL_ID_PIPE);
1733 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1734
1735 /* We got interrupted, drain the pipe. */
1736 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1737 AssertRC(rc);
1738
1739 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1740
1741 rc = VERR_INTERRUPTED;
1742 break;
1743 }
1744 else
1745 break;
1746 }
1747 }
1748 else /* The caller waits for a socket event. */
1749 {
1750 uint32_t fEventsRecv = 0;
1751
1752 /* Loop until we got woken up or a socket event occurred. */
1753 for (;;)
1754 {
1755 /** @todo find an adaptive wait algorithm based on the
1756 * number of wakeups in the past. */
1757 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 5);
1758 if (rc == VERR_TIMEOUT)
1759 {
1760 /* Check if there is an event pending. */
1761 size_t cbRead = 0;
1762 char ch = 0;
1763 rc = RTPipeRead(pSockInt->hPipeR, &ch, 1, &cbRead);
1764 if (RT_SUCCESS(rc) && rc != VINF_TRY_AGAIN)
1765 {
1766 Assert(cbRead == 1);
1767 rc = VERR_INTERRUPTED;
1768 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1769 break; /* Quit */
1770 }
1771 else
1772 Assert(rc == VINF_TRY_AGAIN);
1773 }
1774 else if (RT_SUCCESS(rc))
1775 {
1776 if (fEventsRecv & RTSOCKET_EVT_READ)
1777 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1778 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1779 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1780 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1781 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1782 break; /* Quit */
1783 }
1784 else
1785 break;
1786 }
1787 }
1788 }
1789
1790 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1791
1792 return rc;
1793}
1794
1795/** @interface_method_impl{VDINTERFACETCPNET,pfnPoke} */
1796static DECLCALLBACK(int) drvvdTcpPoke(VDSOCKET Sock)
1797{
1798 int rc = VINF_SUCCESS;
1799 size_t cbWritten = 0;
1800 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1801
1802 ASMAtomicXchgBool(&pSockInt->fWokenUp, true);
1803
1804 if (ASMAtomicReadBool(&pSockInt->fWaiting))
1805 {
1806 rc = RTPipeWrite(pSockInt->hPipeW, "", 1, &cbWritten);
1807 Assert(RT_SUCCESS(rc) || cbWritten == 0);
1808 }
1809
1810 return VINF_SUCCESS;
1811}
1812
1813/**
1814 * Checks the prerequisites for encrypted I/O.
1815 *
1816 * @returns VBox status code.
1817 * @param pThis The VD driver instance data.
1818 */
1819static int drvvdKeyCheckPrereqs(PVBOXDISK pThis)
1820{
1821 if ( pThis->pCfgCrypto
1822 && !pThis->pIfSecKey)
1823 {
1824 AssertPtr(pThis->pIfSecKeyHlp);
1825 pThis->pIfSecKeyHlp->pfnKeyMissingNotify(pThis->pIfSecKeyHlp);
1826
1827 int rc = PDMDrvHlpVMSetRuntimeError(pThis->pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_DEKMISSING",
1828 N_("VD: The DEK for this disk is missing"));
1829 AssertRC(rc);
1830 return VERR_VD_DEK_MISSING;
1831 }
1832
1833 return VINF_SUCCESS;
1834}
1835
1836
1837/*********************************************************************************************************************************
1838* Media interface methods *
1839*********************************************************************************************************************************/
1840
1841/** @interface_method_impl{PDMIMEDIA,pfnRead} */
1842static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
1843 uint64_t off, void *pvBuf, size_t cbRead)
1844{
1845 int rc = VINF_SUCCESS;
1846
1847 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1848 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1849
1850 /*
1851 * Check the state.
1852 */
1853 if (!pThis->pDisk)
1854 {
1855 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1856 return VERR_PDM_MEDIA_NOT_MOUNTED;
1857 }
1858
1859 rc = drvvdKeyCheckPrereqs(pThis);
1860 if (RT_FAILURE(rc))
1861 return rc;
1862
1863 if (!pThis->fBootAccelActive)
1864 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1865 else
1866 {
1867 /* Can we serve the request from the buffer? */
1868 if ( off >= pThis->offDisk
1869 && off - pThis->offDisk < pThis->cbDataValid)
1870 {
1871 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1872
1873 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1874 cbRead -= cbToCopy;
1875 off += cbToCopy;
1876 pvBuf = (char *)pvBuf + cbToCopy;
1877 }
1878
1879 if ( cbRead > 0
1880 && cbRead < pThis->cbBootAccelBuffer)
1881 {
1882 /* Increase request to the buffer size and read. */
1883 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1884 pThis->offDisk = off;
1885 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1886 if (RT_FAILURE(rc))
1887 pThis->cbDataValid = 0;
1888 else
1889 memcpy(pvBuf, pThis->pbData, cbRead);
1890 }
1891 else if (cbRead >= pThis->cbBootAccelBuffer)
1892 {
1893 pThis->fBootAccelActive = false; /* Deactiviate */
1894 }
1895 }
1896
1897 if (RT_SUCCESS(rc))
1898 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__,
1899 off, pvBuf, cbRead, cbRead, pvBuf));
1900 LogFlowFunc(("returns %Rrc\n", rc));
1901 return rc;
1902}
1903
1904/** @interface_method_impl{PDMIMEDIA,pfnRead} */
1905static DECLCALLBACK(int) drvvdReadPcBios(PPDMIMEDIA pInterface,
1906 uint64_t off, void *pvBuf, size_t cbRead)
1907{
1908 int rc = VINF_SUCCESS;
1909
1910 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1911 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1912
1913 /*
1914 * Check the state.
1915 */
1916 if (!pThis->pDisk)
1917 {
1918 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1919 return VERR_PDM_MEDIA_NOT_MOUNTED;
1920 }
1921
1922 if ( pThis->pCfgCrypto
1923 && !pThis->pIfSecKey)
1924 return VERR_VD_DEK_MISSING;
1925
1926 if (!pThis->fBootAccelActive)
1927 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1928 else
1929 {
1930 /* Can we serve the request from the buffer? */
1931 if ( off >= pThis->offDisk
1932 && off - pThis->offDisk < pThis->cbDataValid)
1933 {
1934 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1935
1936 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1937 cbRead -= cbToCopy;
1938 off += cbToCopy;
1939 pvBuf = (char *)pvBuf + cbToCopy;
1940 }
1941
1942 if ( cbRead > 0
1943 && cbRead < pThis->cbBootAccelBuffer)
1944 {
1945 /* Increase request to the buffer size and read. */
1946 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1947 pThis->offDisk = off;
1948 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1949 if (RT_FAILURE(rc))
1950 pThis->cbDataValid = 0;
1951 else
1952 memcpy(pvBuf, pThis->pbData, cbRead);
1953 }
1954 else if (cbRead >= pThis->cbBootAccelBuffer)
1955 {
1956 pThis->fBootAccelActive = false; /* Deactiviate */
1957 }
1958 }
1959
1960 if (RT_SUCCESS(rc))
1961 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__,
1962 off, pvBuf, cbRead, cbRead, pvBuf));
1963 LogFlowFunc(("returns %Rrc\n", rc));
1964 return rc;
1965}
1966
1967
1968/** @interface_method_impl{PDMIMEDIA,pfnWrite} */
1969static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
1970 uint64_t off, const void *pvBuf,
1971 size_t cbWrite)
1972{
1973 LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
1974 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1975 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d\n%.*Rhxd\n", __FUNCTION__,
1976 off, pvBuf, cbWrite, cbWrite, pvBuf));
1977
1978 /*
1979 * Check the state.
1980 */
1981 if (!pThis->pDisk)
1982 {
1983 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1984 return VERR_PDM_MEDIA_NOT_MOUNTED;
1985 }
1986
1987 /* Set an FTM checkpoint as this operation changes the state permanently. */
1988 PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_STORAGE);
1989
1990 int rc = drvvdKeyCheckPrereqs(pThis);
1991 if (RT_FAILURE(rc))
1992 return rc;
1993
1994 /* Invalidate any buffer if boot acceleration is enabled. */
1995 if (pThis->fBootAccelActive)
1996 {
1997 pThis->cbDataValid = 0;
1998 pThis->offDisk = 0;
1999 }
2000
2001 rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
2002#ifdef VBOX_PERIODIC_FLUSH
2003 if (pThis->cbFlushInterval)
2004 {
2005 pThis->cbDataWritten += (uint32_t)cbWrite;
2006 if (pThis->cbDataWritten > pThis->cbFlushInterval)
2007 {
2008 pThis->cbDataWritten = 0;
2009 VDFlush(pThis->pDisk);
2010 }
2011 }
2012#endif /* VBOX_PERIODIC_FLUSH */
2013
2014 LogFlowFunc(("returns %Rrc\n", rc));
2015 return rc;
2016}
2017
2018/** @interface_method_impl{PDMIMEDIA,pfnFlush} */
2019static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
2020{
2021 LogFlowFunc(("\n"));
2022 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2023
2024 /*
2025 * Check the state.
2026 */
2027 if (!pThis->pDisk)
2028 {
2029 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2030 return VERR_PDM_MEDIA_NOT_MOUNTED;
2031 }
2032
2033#ifdef VBOX_IGNORE_FLUSH
2034 if (pThis->fIgnoreFlush)
2035 return VINF_SUCCESS;
2036#endif /* VBOX_IGNORE_FLUSH */
2037
2038 int rc = VDFlush(pThis->pDisk);
2039 LogFlowFunc(("returns %Rrc\n", rc));
2040 return rc;
2041}
2042
2043/** @interface_method_impl{PDMIMEDIA,pfnMerge} */
2044static DECLCALLBACK(int) drvvdMerge(PPDMIMEDIA pInterface,
2045 PFNSIMPLEPROGRESS pfnProgress,
2046 void *pvUser)
2047{
2048 LogFlowFunc(("\n"));
2049 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2050 int rc = VINF_SUCCESS;
2051
2052 /*
2053 * Check the state.
2054 */
2055 if (!pThis->pDisk)
2056 {
2057 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2058 return VERR_PDM_MEDIA_NOT_MOUNTED;
2059 }
2060
2061 /* Note: There is an unavoidable race between destruction and another
2062 * thread invoking this function. This is handled safely and gracefully by
2063 * atomically invalidating the lock handle in drvvdDestruct. */
2064 int rc2 = RTSemFastMutexRequest(pThis->MergeCompleteMutex);
2065 AssertRC(rc2);
2066 if (RT_SUCCESS(rc2) && pThis->fMergePending)
2067 {
2068 /* Take shortcut: PFNSIMPLEPROGRESS is exactly the same type as
2069 * PFNVDPROGRESS, so there's no need for a conversion function. */
2070 /** @todo maybe introduce a conversion which limits update frequency. */
2071 PVDINTERFACE pVDIfsOperation = NULL;
2072 VDINTERFACEPROGRESS VDIfProgress;
2073 VDIfProgress.pfnProgress = pfnProgress;
2074 rc2 = VDInterfaceAdd(&VDIfProgress.Core, "DrvVD_VDIProgress", VDINTERFACETYPE_PROGRESS,
2075 pvUser, sizeof(VDINTERFACEPROGRESS), &pVDIfsOperation);
2076 AssertRC(rc2);
2077 pThis->fMergePending = false;
2078 rc = VDMerge(pThis->pDisk, pThis->uMergeSource,
2079 pThis->uMergeTarget, pVDIfsOperation);
2080 }
2081 rc2 = RTSemFastMutexRelease(pThis->MergeCompleteMutex);
2082 AssertRC(rc2);
2083 LogFlowFunc(("returns %Rrc\n", rc));
2084 return rc;
2085}
2086
2087/** @interface_method_impl{PDMIMEDIA,pfnSetSecKeyIf} */
2088static DECLCALLBACK(int) drvvdSetSecKeyIf(PPDMIMEDIA pInterface, PPDMISECKEY pIfSecKey, PPDMISECKEYHLP pIfSecKeyHlp)
2089{
2090 LogFlowFunc(("\n"));
2091 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2092 int rc = VINF_SUCCESS;
2093
2094 if (pThis->pCfgCrypto)
2095 {
2096 PVDINTERFACE pVDIfFilter = NULL;
2097
2098 pThis->pIfSecKeyHlp = pIfSecKeyHlp;
2099
2100 if ( pThis->pIfSecKey
2101 && !pIfSecKey)
2102 {
2103 /* Unload the crypto filter first to make sure it doesn't access the keys anymore. */
2104 rc = VDFilterRemove(pThis->pDisk, VD_FILTER_FLAGS_DEFAULT);
2105 AssertRC(rc);
2106
2107 pThis->pIfSecKey = NULL;
2108 }
2109
2110 if ( pIfSecKey
2111 && RT_SUCCESS(rc))
2112 {
2113 pThis->pIfSecKey = pIfSecKey;
2114
2115 rc = VDInterfaceAdd(&pThis->VDIfCfg.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
2116 pThis->pCfgCrypto, sizeof(VDINTERFACECONFIG), &pVDIfFilter);
2117 AssertRC(rc);
2118
2119 rc = VDInterfaceAdd(&pThis->VDIfCrypto.Core, "DrvVD_Crypto", VDINTERFACETYPE_CRYPTO,
2120 pThis, sizeof(VDINTERFACECRYPTO), &pVDIfFilter);
2121 AssertRC(rc);
2122
2123 /* Load the crypt filter plugin. */
2124 rc = VDFilterAdd(pThis->pDisk, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pVDIfFilter);
2125 if (RT_FAILURE(rc))
2126 pThis->pIfSecKey = NULL;
2127 }
2128 }
2129 else
2130 rc = VERR_NOT_SUPPORTED;
2131
2132 LogFlowFunc(("returns %Rrc\n", rc));
2133 return rc;
2134}
2135
2136/** @interface_method_impl{PDMIMEDIA,pfnGetSize} */
2137static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
2138{
2139 LogFlowFunc(("\n"));
2140 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2141
2142 /*
2143 * Check the state.
2144 */
2145 if (!pThis->pDisk)
2146 return 0;
2147
2148 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
2149 LogFlowFunc(("returns %#llx (%llu)\n", cb, cb));
2150 return cb;
2151}
2152
2153/** @interface_method_impl{PDMIMEDIA,pfnGetSectorSize} */
2154static DECLCALLBACK(uint32_t) drvvdGetSectorSize(PPDMIMEDIA pInterface)
2155{
2156 LogFlowFunc(("\n"));
2157 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2158
2159 /*
2160 * Check the state.
2161 */
2162 if (!pThis->pDisk)
2163 return 0;
2164
2165 uint32_t cb = VDGetSectorSize(pThis->pDisk, VD_LAST_IMAGE);
2166 LogFlowFunc(("returns %u\n", cb));
2167 return cb;
2168}
2169
2170/** @interface_method_impl{PDMIMEDIA,pfnIsReadOnly} */
2171static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
2172{
2173 LogFlowFunc(("\n"));
2174 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2175
2176 /*
2177 * Check the state.
2178 */
2179 if (!pThis->pDisk)
2180 return false;
2181
2182 bool f = VDIsReadOnly(pThis->pDisk);
2183 LogFlowFunc(("returns %d\n", f));
2184 return f;
2185}
2186
2187/** @interface_method_impl{PDMIMEDIA,pfnBiosGetPCHSGeometry} */
2188static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
2189 PPDMMEDIAGEOMETRY pPCHSGeometry)
2190{
2191 LogFlowFunc(("\n"));
2192 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2193 VDGEOMETRY geo;
2194
2195 /*
2196 * Check the state.
2197 */
2198 if (!pThis->pDisk)
2199 return VERR_PDM_MEDIA_NOT_MOUNTED;
2200
2201 /*
2202 * Use configured/cached values if present.
2203 */
2204 if ( pThis->PCHSGeometry.cCylinders > 0
2205 && pThis->PCHSGeometry.cHeads > 0
2206 && pThis->PCHSGeometry.cSectors > 0)
2207 {
2208 *pPCHSGeometry = pThis->PCHSGeometry;
2209 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
2210 return VINF_SUCCESS;
2211 }
2212
2213 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
2214 if (RT_SUCCESS(rc))
2215 {
2216 pPCHSGeometry->cCylinders = geo.cCylinders;
2217 pPCHSGeometry->cHeads = geo.cHeads;
2218 pPCHSGeometry->cSectors = geo.cSectors;
2219 pThis->PCHSGeometry = *pPCHSGeometry;
2220 }
2221 else
2222 {
2223 LogFunc(("geometry not available.\n"));
2224 rc = VERR_PDM_GEOMETRY_NOT_SET;
2225 }
2226 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
2227 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2228 return rc;
2229}
2230
2231/** @interface_method_impl{PDMIMEDIA,pfnBiosSetPCHSGeometry} */
2232static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
2233 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2234{
2235 LogFlowFunc(("CHS=%d/%d/%d\n",
2236 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2237 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2238 VDGEOMETRY geo;
2239
2240 /*
2241 * Check the state.
2242 */
2243 if (!pThis->pDisk)
2244 {
2245 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2246 return VERR_PDM_MEDIA_NOT_MOUNTED;
2247 }
2248
2249 geo.cCylinders = pPCHSGeometry->cCylinders;
2250 geo.cHeads = pPCHSGeometry->cHeads;
2251 geo.cSectors = pPCHSGeometry->cSectors;
2252 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
2253 if (rc == VERR_VD_GEOMETRY_NOT_SET)
2254 rc = VERR_PDM_GEOMETRY_NOT_SET;
2255 if (RT_SUCCESS(rc))
2256 pThis->PCHSGeometry = *pPCHSGeometry;
2257 LogFlowFunc(("returns %Rrc\n", rc));
2258 return rc;
2259}
2260
2261/** @interface_method_impl{PDMIMEDIA,pfnBiosGetLCHSGeometry} */
2262static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
2263 PPDMMEDIAGEOMETRY pLCHSGeometry)
2264{
2265 LogFlowFunc(("\n"));
2266 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2267 VDGEOMETRY geo;
2268
2269 /*
2270 * Check the state.
2271 */
2272 if (!pThis->pDisk)
2273 return VERR_PDM_MEDIA_NOT_MOUNTED;
2274
2275 /*
2276 * Use configured/cached values if present.
2277 */
2278 if ( pThis->LCHSGeometry.cCylinders > 0
2279 && pThis->LCHSGeometry.cHeads > 0
2280 && pThis->LCHSGeometry.cSectors > 0)
2281 {
2282 *pLCHSGeometry = pThis->LCHSGeometry;
2283 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
2284 return VINF_SUCCESS;
2285 }
2286
2287 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
2288 if (RT_SUCCESS(rc))
2289 {
2290 pLCHSGeometry->cCylinders = geo.cCylinders;
2291 pLCHSGeometry->cHeads = geo.cHeads;
2292 pLCHSGeometry->cSectors = geo.cSectors;
2293 pThis->LCHSGeometry = *pLCHSGeometry;
2294 }
2295 else
2296 {
2297 LogFunc(("geometry not available.\n"));
2298 rc = VERR_PDM_GEOMETRY_NOT_SET;
2299 }
2300 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
2301 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2302 return rc;
2303}
2304
2305/** @interface_method_impl{PDMIMEDIA,pfnBiosSetLCHSGeometry} */
2306static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
2307 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2308{
2309 LogFlowFunc(("CHS=%d/%d/%d\n",
2310 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2311 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2312 VDGEOMETRY geo;
2313
2314 /*
2315 * Check the state.
2316 */
2317 if (!pThis->pDisk)
2318 {
2319 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2320 return VERR_PDM_MEDIA_NOT_MOUNTED;
2321 }
2322
2323 geo.cCylinders = pLCHSGeometry->cCylinders;
2324 geo.cHeads = pLCHSGeometry->cHeads;
2325 geo.cSectors = pLCHSGeometry->cSectors;
2326 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
2327 if (rc == VERR_VD_GEOMETRY_NOT_SET)
2328 rc = VERR_PDM_GEOMETRY_NOT_SET;
2329 if (RT_SUCCESS(rc))
2330 pThis->LCHSGeometry = *pLCHSGeometry;
2331 LogFlowFunc(("returns %Rrc\n", rc));
2332 return rc;
2333}
2334
2335/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
2336static DECLCALLBACK(bool) drvvdBiosIsVisible(PPDMIMEDIA pInterface)
2337{
2338 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2339 LogFlow(("drvvdBiosIsVisible: returns %d\n", pThis->fBiosVisible));
2340 return pThis->fBiosVisible;
2341}
2342
2343/** @interface_method_impl{PDMIMEDIA,pfnGetType} */
2344static DECLCALLBACK(PDMMEDIATYPE) drvvdGetType(PPDMIMEDIA pInterface)
2345{
2346 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2347 LogFlow(("drvvdBiosIsVisible: returns %d\n", pThis->fBiosVisible));
2348 return pThis->enmType;
2349}
2350
2351/** @interface_method_impl{PDMIMEDIA,pfnGetUuid} */
2352static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
2353{
2354 LogFlowFunc(("\n"));
2355 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2356
2357 /*
2358 * Copy the uuid.
2359 */
2360 *pUuid = pThis->Uuid;
2361 LogFlowFunc(("returns {%RTuuid}\n", pUuid));
2362 return VINF_SUCCESS;
2363}
2364
2365static DECLCALLBACK(int) drvvdDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
2366{
2367 LogFlowFunc(("\n"));
2368 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2369
2370 int rc = VDDiscardRanges(pThis->pDisk, paRanges, cRanges);
2371 LogFlowFunc(("returns %Rrc\n", rc));
2372 return rc;
2373}
2374
2375/** @interface_method_impl{PDMIMEDIA,pfnIoBufAlloc} */
2376static DECLCALLBACK(int) drvvdIoBufAlloc(PPDMIMEDIA pInterface, size_t cb, void **ppvNew)
2377{
2378 LogFlowFunc(("\n"));
2379 int rc;
2380 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2381
2382 /* Configured encryption requires locked down memory. */
2383 if (pThis->pCfgCrypto)
2384 rc = RTMemSaferAllocZEx(ppvNew, cb, RTMEMSAFER_F_REQUIRE_NOT_PAGABLE);
2385 else
2386 {
2387 cb = RT_ALIGN_Z(cb, _4K);
2388 void *pvNew = RTMemPageAlloc(cb);
2389 if (RT_LIKELY(pvNew))
2390 {
2391 *ppvNew = pvNew;
2392 rc = VINF_SUCCESS;
2393 }
2394 else
2395 rc = VERR_NO_MEMORY;
2396 }
2397
2398 LogFlowFunc(("returns %Rrc\n", rc));
2399 return rc;
2400}
2401
2402/** @interface_method_impl{PDMIMEDIA,pfnIoBufFree} */
2403static DECLCALLBACK(int) drvvdIoBufFree(PPDMIMEDIA pInterface, void *pv, size_t cb)
2404{
2405 LogFlowFunc(("\n"));
2406 int rc = VINF_SUCCESS;
2407 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2408
2409 if (pThis->pCfgCrypto)
2410 RTMemSaferFree(pv, cb);
2411 else
2412 {
2413 cb = RT_ALIGN_Z(cb, _4K);
2414 RTMemPageFree(pv, cb);
2415 }
2416
2417 LogFlowFunc(("returns %Rrc\n", rc));
2418 return rc;
2419}
2420
2421
2422/* -=-=-=-=- IMount -=-=-=-=- */
2423
2424/** @copydoc PDMIMOUNT::pfnUnmount */
2425static DECLCALLBACK(int) drvvdUnmount(PPDMIMOUNT pInterface, bool fForce, bool fEject)
2426{
2427 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2428
2429 /*
2430 * Validate state.
2431 */
2432 if (!pThis->pDisk)
2433 {
2434 Log(("drvvdUnmount: Not mounted\n"));
2435 return VERR_PDM_MEDIA_NOT_MOUNTED;
2436 }
2437 if (pThis->fLocked && !fForce)
2438 {
2439 Log(("drvvdUnmount: Locked\n"));
2440 return VERR_PDM_MEDIA_LOCKED;
2441 }
2442
2443 /* Media is no longer locked even if it was previously. */
2444 pThis->fLocked = false;
2445 drvvdPowerOffOrDestructOrUnmount(pThis->pDrvIns);
2446
2447 /*
2448 * Notify driver/device above us.
2449 */
2450 if (pThis->pDrvMountNotify)
2451 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
2452 Log(("drvblockUnmount: success\n"));
2453 return VINF_SUCCESS;
2454}
2455
2456
2457/** @copydoc PDMIMOUNT::pfnIsMounted */
2458static DECLCALLBACK(bool) drvvdIsMounted(PPDMIMOUNT pInterface)
2459{
2460 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2461 return pThis->pDisk != NULL;
2462}
2463
2464/** @copydoc PDMIMOUNT::pfnLock */
2465static DECLCALLBACK(int) drvvdLock(PPDMIMOUNT pInterface)
2466{
2467 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2468 Log(("drvblockLock: %d -> %d\n", pThis->fLocked, true));
2469 pThis->fLocked = true;
2470 return VINF_SUCCESS;
2471}
2472
2473/** @copydoc PDMIMOUNT::pfnUnlock */
2474static DECLCALLBACK(int) drvvdUnlock(PPDMIMOUNT pInterface)
2475{
2476 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2477 Log(("drvblockUnlock: %d -> %d\n", pThis->fLocked, false));
2478 pThis->fLocked = false;
2479 return VINF_SUCCESS;
2480}
2481
2482/** @copydoc PDMIMOUNT::pfnIsLocked */
2483static DECLCALLBACK(bool) drvvdIsLocked(PPDMIMOUNT pInterface)
2484{
2485 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2486 return pThis->fLocked;
2487}
2488
2489/*********************************************************************************************************************************
2490* Async Media interface methods *
2491*********************************************************************************************************************************/
2492
2493static DECLCALLBACK(void) drvvdAsyncReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2494{
2495 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
2496
2497 if (!pThis->pBlkCache)
2498 {
2499 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
2500 pvUser2, rcReq);
2501 AssertRC(rc);
2502 }
2503 else
2504 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, (PPDMBLKCACHEIOXFER)pvUser2, rcReq);
2505}
2506
2507static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
2508 PCRTSGSEG paSeg, unsigned cSeg,
2509 size_t cbRead, void *pvUser)
2510{
2511 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n",
2512 uOffset, paSeg, cSeg, cbRead, pvUser));
2513 int rc = VINF_SUCCESS;
2514 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
2515
2516 /*
2517 * Check the state.
2518 */
2519 if (!pThis->pDisk)
2520 {
2521 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2522 return VERR_PDM_MEDIA_NOT_MOUNTED;
2523 }
2524
2525 rc = drvvdKeyCheckPrereqs(pThis);
2526 if (RT_FAILURE(rc))
2527 return rc;
2528
2529 pThis->fBootAccelActive = false;
2530
2531 RTSGBUF SgBuf;
2532 RTSgBufInit(&SgBuf, paSeg, cSeg);
2533 if (!pThis->pBlkCache)
2534 rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, &SgBuf,
2535 drvvdAsyncReqComplete, pThis, pvUser);
2536 else
2537 {
2538 rc = PDMR3BlkCacheRead(pThis->pBlkCache, uOffset, &SgBuf, cbRead, pvUser);
2539 if (rc == VINF_AIO_TASK_PENDING)
2540 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2541 else if (rc == VINF_SUCCESS)
2542 rc = VINF_VD_ASYNC_IO_FINISHED;
2543 }
2544
2545 LogFlowFunc(("returns %Rrc\n", rc));
2546 return rc;
2547}
2548
2549static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
2550 PCRTSGSEG paSeg, unsigned cSeg,
2551 size_t cbWrite, void *pvUser)
2552{
2553 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n",
2554 uOffset, paSeg, cSeg, cbWrite, pvUser));
2555 int rc = VINF_SUCCESS;
2556 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
2557
2558 /*
2559 * Check the state.
2560 */
2561 if (!pThis->pDisk)
2562 {
2563 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2564 return VERR_PDM_MEDIA_NOT_MOUNTED;
2565 }
2566
2567 rc = drvvdKeyCheckPrereqs(pThis);
2568 if (RT_FAILURE(rc))
2569 return rc;
2570
2571 pThis->fBootAccelActive = false;
2572
2573 RTSGBUF SgBuf;
2574 RTSgBufInit(&SgBuf, paSeg, cSeg);
2575
2576 if (!pThis->pBlkCache)
2577 rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, &SgBuf,
2578 drvvdAsyncReqComplete, pThis, pvUser);
2579 else
2580 {
2581 rc = PDMR3BlkCacheWrite(pThis->pBlkCache, uOffset, &SgBuf, cbWrite, pvUser);
2582 if (rc == VINF_AIO_TASK_PENDING)
2583 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2584 else if (rc == VINF_SUCCESS)
2585 rc = VINF_VD_ASYNC_IO_FINISHED;
2586 }
2587
2588 LogFlowFunc(("returns %Rrc\n", rc));
2589 return rc;
2590}
2591
2592static DECLCALLBACK(int) drvvdStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
2593{
2594 LogFlowFunc(("pvUser=%#p\n", pvUser));
2595 int rc = VINF_SUCCESS;
2596 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
2597
2598 /*
2599 * Check the state.
2600 */
2601 if (!pThis->pDisk)
2602 {
2603 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2604 return VERR_PDM_MEDIA_NOT_MOUNTED;
2605 }
2606
2607#ifdef VBOX_IGNORE_FLUSH
2608 if (pThis->fIgnoreFlushAsync)
2609 return VINF_VD_ASYNC_IO_FINISHED;
2610#endif /* VBOX_IGNORE_FLUSH */
2611
2612 if (!pThis->pBlkCache)
2613 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, pvUser);
2614 else
2615 {
2616 rc = PDMR3BlkCacheFlush(pThis->pBlkCache, pvUser);
2617 if (rc == VINF_AIO_TASK_PENDING)
2618 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2619 else if (rc == VINF_SUCCESS)
2620 rc = VINF_VD_ASYNC_IO_FINISHED;
2621 }
2622 LogFlowFunc(("returns %Rrc\n", rc));
2623 return rc;
2624}
2625
2626static DECLCALLBACK(int) drvvdStartDiscard(PPDMIMEDIAASYNC pInterface, PCRTRANGE paRanges,
2627 unsigned cRanges, void *pvUser)
2628{
2629 int rc = VINF_SUCCESS;
2630 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
2631
2632 LogFlowFunc(("paRanges=%#p cRanges=%u pvUser=%#p\n",
2633 paRanges, cRanges, pvUser));
2634
2635 /*
2636 * Check the state.
2637 */
2638 if (!pThis->pDisk)
2639 {
2640 AssertMsgFailed(("Invalid state! Not mounted!\n"));
2641 return VERR_PDM_MEDIA_NOT_MOUNTED;
2642 }
2643
2644 if (!pThis->pBlkCache)
2645 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges, drvvdAsyncReqComplete,
2646 pThis, pvUser);
2647 else
2648 {
2649 rc = PDMR3BlkCacheDiscard(pThis->pBlkCache, paRanges, cRanges, pvUser);
2650 if (rc == VINF_AIO_TASK_PENDING)
2651 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2652 else if (rc == VINF_SUCCESS)
2653 rc = VINF_VD_ASYNC_IO_FINISHED;
2654 }
2655 LogFlowFunc(("returns %Rrc\n", rc));
2656 return rc;
2657}
2658
2659/** @copydoc FNPDMBLKCACHEXFERCOMPLETEDRV */
2660static DECLCALLBACK(void) drvvdBlkCacheXferComplete(PPDMDRVINS pDrvIns, void *pvUser, int rcReq)
2661{
2662 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2663
2664 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
2665 pvUser, rcReq);
2666 AssertRC(rc);
2667}
2668
2669/** @copydoc FNPDMBLKCACHEXFERENQUEUEDRV */
2670static DECLCALLBACK(int) drvvdBlkCacheXferEnqueue(PPDMDRVINS pDrvIns,
2671 PDMBLKCACHEXFERDIR enmXferDir,
2672 uint64_t off, size_t cbXfer,
2673 PCRTSGBUF pcSgBuf, PPDMBLKCACHEIOXFER hIoXfer)
2674{
2675 int rc = VINF_SUCCESS;
2676 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2677
2678 Assert (!pThis->pCfgCrypto);
2679
2680 switch (enmXferDir)
2681 {
2682 case PDMBLKCACHEXFERDIR_READ:
2683 rc = VDAsyncRead(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
2684 pThis, hIoXfer);
2685 break;
2686 case PDMBLKCACHEXFERDIR_WRITE:
2687 rc = VDAsyncWrite(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
2688 pThis, hIoXfer);
2689 break;
2690 case PDMBLKCACHEXFERDIR_FLUSH:
2691 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, hIoXfer);
2692 break;
2693 default:
2694 AssertMsgFailed(("Invalid transfer type %d\n", enmXferDir));
2695 rc = VERR_INVALID_PARAMETER;
2696 }
2697
2698 if (rc == VINF_VD_ASYNC_IO_FINISHED)
2699 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
2700 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2701 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
2702
2703 return VINF_SUCCESS;
2704}
2705
2706/** @copydoc FNPDMBLKCACHEXFERENQUEUEDISCARDDRV */
2707static DECLCALLBACK(int) drvvdBlkCacheXferEnqueueDiscard(PPDMDRVINS pDrvIns, PCRTRANGE paRanges,
2708 unsigned cRanges, PPDMBLKCACHEIOXFER hIoXfer)
2709{
2710 int rc = VINF_SUCCESS;
2711 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2712
2713 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges,
2714 drvvdAsyncReqComplete, pThis, hIoXfer);
2715
2716 if (rc == VINF_VD_ASYNC_IO_FINISHED)
2717 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
2718 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2719 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
2720
2721 return VINF_SUCCESS;
2722}
2723
2724/*********************************************************************************************************************************
2725* Extended media interface methods *
2726*********************************************************************************************************************************/
2727
2728static void drvvdMediaExIoReqWarningDiskFull(PPDMDRVINS pDrvIns)
2729{
2730 int rc;
2731 LogRel(("VD#%u: Host disk full\n", pDrvIns->iInstance));
2732 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_DISKFULL",
2733 N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
2734 AssertRC(rc);
2735}
2736
2737static void drvvdMediaExIoReqWarningFileTooBig(PPDMDRVINS pDrvIns)
2738{
2739 int rc;
2740 LogRel(("VD#%u: File too big\n", pDrvIns->iInstance));
2741 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_FILETOOBIG",
2742 N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
2743 AssertRC(rc);
2744}
2745
2746static void drvvdMediaExIoReqWarningISCSI(PPDMDRVINS pDrvIns)
2747{
2748 int rc;
2749 LogRel(("VD#%u: iSCSI target unavailable\n", pDrvIns->iInstance));
2750 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_ISCSIDOWN",
2751 N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
2752 AssertRC(rc);
2753}
2754
2755/**
2756 * Checks whether a given status code indicates a recoverable error
2757 * suspending the VM if it is.
2758 *
2759 * @returns Flag indicating whether the status code is a recoverable error
2760 * (full disk, broken network connection).
2761 * @param pThis VBox disk container instance data.
2762 * @param rc Status code to check.
2763 */
2764bool drvvdMediaExIoReqIsRedoSetWarning(PVBOXDISK pThis, int rc)
2765{
2766 if (rc == VERR_DISK_FULL)
2767 {
2768 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2769 drvvdMediaExIoReqWarningDiskFull(pThis->pDrvIns);
2770 return true;
2771 }
2772 if (rc == VERR_FILE_TOO_BIG)
2773 {
2774 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2775 drvvdMediaExIoReqWarningFileTooBig(pThis->pDrvIns);
2776 return true;
2777 }
2778 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2779 {
2780 /* iSCSI connection abort (first error) or failure to reestablish
2781 * connection (second error). Pause VM. On resume we'll retry. */
2782 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2783 drvvdMediaExIoReqWarningISCSI(pThis->pDrvIns);
2784 return true;
2785 }
2786 if (rc == VERR_VD_DEK_MISSING)
2787 {
2788 /* Error message already set. */
2789 ASMAtomicCmpXchgBool(&pThis->fRedo, true, false);
2790 return true;
2791 }
2792
2793 return false;
2794}
2795
2796/**
2797 * Syncs the memory buffers between the I/O request allocator and the internal buffer.
2798 *
2799 * @returns VBox status code.
2800 * @param pThis VBox disk container instance data.
2801 * @param pIoReq I/O request to sync.
2802 * @param fToIoBuf Flag indicating the sync direction.
2803 * true to copy data from the allocators buffer to our internal buffer.
2804 * false for the other direction.
2805 */
2806DECLINLINE(int) drvvdMediaExIoReqBufSync(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fToIoBuf)
2807{
2808 int rc = VINF_SUCCESS;
2809
2810 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
2811
2812 /* Make sure the buffer is reset. */
2813 RTSgBufReset(&pIoReq->ReadWrite.IoBuf.SgBuf);
2814
2815 if (fToIoBuf)
2816 rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2817 pIoReq->ReadWrite.cbReq - pIoReq->ReadWrite.cbReqLeft,
2818 &pIoReq->ReadWrite.IoBuf.SgBuf,
2819 RT_MIN(pIoReq->ReadWrite.cbIoBuf, pIoReq->ReadWrite.cbReqLeft));
2820 else
2821 rc = pThis->pDrvMediaExPort->pfnIoReqCopyFromBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2822 pIoReq->ReadWrite.cbReq - pIoReq->ReadWrite.cbReqLeft,
2823 &pIoReq->ReadWrite.IoBuf.SgBuf,
2824 RT_MIN(pIoReq->ReadWrite.cbIoBuf, pIoReq->ReadWrite.cbReqLeft));
2825
2826 RTSgBufReset(&pIoReq->ReadWrite.IoBuf.SgBuf);
2827 return rc;
2828}
2829
2830/**
2831 * Hashes the I/O request ID to an index for the allocated I/O request bin.
2832 */
2833DECLINLINE(unsigned) drvvdMediaExIoReqIdHash(PDMMEDIAEXIOREQID uIoReqId)
2834{
2835 return uIoReqId % DRVVD_VDIOREQ_ALLOC_BINS; /** @todo: Find something better? */
2836}
2837
2838/**
2839 * Inserts the given I/O request in to the list of allocated I/O requests.
2840 *
2841 * @returns VBox status code.
2842 * @param pThis VBox disk container instance data.
2843 * @param pIoReq I/O request to insert.
2844 */
2845static int drvvdMediaExIoReqInsert(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2846{
2847 int rc = VINF_SUCCESS;
2848 unsigned idxBin = drvvdMediaExIoReqIdHash(pIoReq->uIoReqId);
2849
2850 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2851 if (RT_SUCCESS(rc))
2852 {
2853 /* Search for conflicting I/O request ID. */
2854 PPDMMEDIAEXIOREQINT pIt;
2855 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
2856 {
2857 if (RT_UNLIKELY(pIt->uIoReqId == pIoReq->uIoReqId))
2858 {
2859 rc = VERR_PDM_MEDIAEX_IOREQID_CONFLICT;
2860 break;
2861 }
2862 }
2863 if (RT_SUCCESS(rc))
2864 RTListAppend(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, &pIoReq->NdAllocatedList);
2865 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2866 }
2867
2868 return rc;
2869}
2870
2871/**
2872 * Removes the given I/O request from the list of allocated I/O requests.
2873 *
2874 * @returns VBox status code.
2875 * @param pThis VBox disk container instance data.
2876 * @param pIoReq I/O request to insert.
2877 */
2878static int drvvdMediaExIoReqRemove(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2879{
2880 int rc = VINF_SUCCESS;
2881 unsigned idxBin = drvvdMediaExIoReqIdHash(pIoReq->uIoReqId);
2882
2883 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2884 if (RT_SUCCESS(rc))
2885 {
2886 RTListNodeRemove(&pIoReq->NdAllocatedList);
2887 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2888 }
2889
2890 return rc;
2891}
2892
2893/**
2894 * I/O request completion worker.
2895 *
2896 * @returns VBox status code.
2897 * @param pThis VBox disk container instance data.
2898 * @param pIoReq I/O request to complete.
2899 * @param rcReq The status code the request completed with.
2900 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2901 */
2902static int drvvdMediaExIoReqCompleteWorker(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify)
2903{
2904 int rc;
2905 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETING, VDIOREQSTATE_ACTIVE);
2906 if (fXchg)
2907 ASMAtomicDecU32(&pThis->cIoReqsActive);
2908 else
2909 {
2910 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
2911 rcReq = VERR_PDM_MEDIAEX_IOREQ_CANCELED;
2912 }
2913
2914 ASMAtomicXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETED);
2915
2916 /*
2917 * Leave a release log entry if the request was active for more than 25 seconds
2918 * (30 seconds is the timeout of the guest).
2919 */
2920 uint64_t tsNow = RTTimeMilliTS();
2921 if (tsNow - pIoReq->tsSubmit >= 25 * 1000)
2922 {
2923 const char *pcszReq = NULL;
2924
2925 switch (pIoReq->enmType)
2926 {
2927 case PDMMEDIAEXIOREQTYPE_READ:
2928 pcszReq = "Read";
2929 break;
2930 case PDMMEDIAEXIOREQTYPE_WRITE:
2931 pcszReq = "Write";
2932 break;
2933 case PDMMEDIAEXIOREQTYPE_FLUSH:
2934 pcszReq = "Flush";
2935 break;
2936 case PDMMEDIAEXIOREQTYPE_DISCARD:
2937 pcszReq = "Discard";
2938 break;
2939 default:
2940 pcszReq = "<Invalid>";
2941 }
2942
2943 LogRel(("VD#%u: %s request was active for %llu seconds\n",
2944 pThis->pDrvIns->iInstance, pcszReq, (tsNow - pIoReq->tsSubmit) / 1000));
2945 }
2946
2947 if (RT_FAILURE(rcReq))
2948 {
2949 /* Log the error. */
2950 if (pThis->cErrors++ < DRVVD_MAX_LOG_REL_ERRORS)
2951 {
2952 if (rcReq == VERR_PDM_MEDIAEX_IOREQ_CANCELED)
2953 {
2954 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
2955 LogRel(("VD#%u: Aborted flush returned rc=%Rrc\n",
2956 pThis->pDrvIns->iInstance, rcReq));
2957 else
2958 LogRel(("VD#%u: Aborted %s (%u bytes left) returned rc=%Rrc\n",
2959 pThis->pDrvIns->iInstance,
2960 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2961 ? "read"
2962 : "write",
2963 pIoReq->ReadWrite.cbReqLeft, rcReq));
2964 }
2965 else
2966 {
2967 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
2968 LogRel(("VD#%u: Flush returned rc=%Rrc\n",
2969 pThis->pDrvIns->iInstance, rcReq));
2970 else
2971 LogRel(("VD#%u: %s (%u bytes left) returned rc=%Rrc\n",
2972 pThis->pDrvIns->iInstance,
2973 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2974 ? "Read"
2975 : "Write",
2976 pIoReq->ReadWrite.cbReqLeft, rcReq));
2977 }
2978 }
2979 }
2980
2981 if (fUpNotify)
2982 {
2983 rc = pThis->pDrvMediaExPort->pfnIoReqCompleteNotify(pThis->pDrvMediaExPort,
2984 pIoReq, &pIoReq->abAlloc[0], rcReq);
2985 AssertRC(rc);
2986 }
2987
2988 return rcReq;
2989}
2990
2991
2992/**
2993 * Allocates a memory buffer suitable for I/O for the given request.
2994 *
2995 * @returns VBox status code.
2996 * @param VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS if there is no I/O memory available to allocate and
2997 * the request was placed on a waiting list.
2998 * @param pThis VBox disk container instance data.
2999 * @param pIoReq I/O request to allocate memory for.
3000 * @param cb Size of the buffer.
3001 */
3002DECLINLINE(int) drvvdMediaExIoReqBufAlloc(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cb)
3003{
3004 int rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, cb, &pIoReq->ReadWrite.cbIoBuf);
3005 if (rc == VERR_NO_MEMORY)
3006 {
3007 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3008 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
3009 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3010 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
3011 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3012 }
3013
3014 return rc;
3015}
3016
3017
3018/**
3019 * Processes a read/write request.
3020 *
3021 * @returns VBox status code.
3022 * @param pThis VBox disk container instance data.
3023 * @param pIoReq I/O request to process.
3024 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
3025 */
3026static int drvvdMediaExIoReqReadWriteProcess(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fUpNotify)
3027{
3028 int rc = VINF_SUCCESS;
3029
3030 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
3031
3032 while ( pIoReq->ReadWrite.cbReqLeft
3033 && rc == VINF_SUCCESS)
3034 {
3035 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
3036
3037 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
3038 rc = VDAsyncRead(pThis->pDisk, pIoReq->ReadWrite.offStart, cbReqIo, &pIoReq->ReadWrite.IoBuf.SgBuf,
3039 drvvdMediaExIoReqComplete, pThis, pIoReq);
3040 else
3041 {
3042 /* Sync memory buffer from the request initiator. */
3043 rc = drvvdMediaExIoReqBufSync(pThis, pIoReq, true /* fToIoBuf */);
3044 if (RT_SUCCESS(rc))
3045 {
3046 rc = VDAsyncWrite(pThis->pDisk, pIoReq->ReadWrite.offStart, cbReqIo,
3047 &pIoReq->ReadWrite.IoBuf.SgBuf,
3048 drvvdMediaExIoReqComplete, pThis, pIoReq);
3049 }
3050 }
3051
3052 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3053 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3054 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3055 {
3056 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
3057 rc = drvvdMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
3058 else
3059 rc = VINF_SUCCESS;
3060 pIoReq->ReadWrite.offStart += cbReqIo;
3061 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
3062 }
3063 }
3064
3065 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3066 {
3067 Assert(!pIoReq->ReadWrite.cbReqLeft || RT_FAILURE(rc));
3068 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, fUpNotify);
3069 }
3070
3071 return rc;
3072}
3073
3074
3075/**
3076 * Frees a I/O memory buffer allocated previously.
3077 *
3078 * @returns nothing.
3079 * @param pThis VBox disk container instance data.
3080 * @param pIoReq I/O request for which to free memory.
3081 */
3082DECLINLINE(void) drvvdMediaExIoReqBufFree(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3083{
3084 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3085 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3086 {
3087 IOBUFMgrFreeBuf(&pIoReq->ReadWrite.IoBuf);
3088
3089 if (ASMAtomicReadU32(&pThis->cIoReqsWaiting) > 0)
3090 {
3091 /* Try to process as many requests as possible. */
3092 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3093 PPDMMEDIAEXIOREQINT pIoReqCur, pIoReqNext;
3094
3095 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3096 {
3097 /* Allocate a suitable I/O buffer for this request. */
3098 int rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReqCur->ReadWrite.IoBuf, pIoReqCur->ReadWrite.cbReq,
3099 &pIoReqCur->ReadWrite.cbIoBuf);
3100 if (rc == VINF_SUCCESS)
3101 {
3102 ASMAtomicDecU32(&pThis->cIoReqsWaiting);
3103 RTListNodeRemove(&pIoReqCur->NdLstWait);
3104
3105 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReqCur->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3106 if (RT_UNLIKELY(!fXchg))
3107 {
3108 /* Must have been canceled inbetween. */
3109 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3110 drvvdMediaExIoReqCompleteWorker(pThis, pIoReqCur, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
3111 }
3112 ASMAtomicIncU32(&pThis->cIoReqsActive);
3113 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReqCur, true /* fUpNotify */);
3114 }
3115 else
3116 {
3117 Assert(rc == VERR_NO_MEMORY);
3118 break;
3119 }
3120 }
3121 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3122 }
3123 }
3124}
3125
3126
3127/**
3128 * Returns whether the VM is in a running state.
3129 *
3130 * @returns Flag indicating whether the VM is currently in a running state.
3131 * @param pThis VBox disk container instance data.
3132 */
3133DECLINLINE(bool) drvvdMediaExIoReqIsVmRunning(PVBOXDISK pThis)
3134{
3135 VMSTATE enmVmState = PDMDrvHlpVMState(pThis->pDrvIns);
3136 if ( enmVmState == VMSTATE_RESUMING
3137 || enmVmState == VMSTATE_RUNNING
3138 || enmVmState == VMSTATE_RUNNING_LS
3139 || enmVmState == VMSTATE_RUNNING_FT
3140 || enmVmState == VMSTATE_RESETTING
3141 || enmVmState == VMSTATE_RESETTING_LS
3142 || enmVmState == VMSTATE_SOFT_RESETTING
3143 || enmVmState == VMSTATE_SOFT_RESETTING_LS
3144 || enmVmState == VMSTATE_SUSPENDING
3145 || enmVmState == VMSTATE_SUSPENDING_LS
3146 || enmVmState == VMSTATE_SUSPENDING_EXT_LS)
3147 return true;
3148
3149 return false;
3150}
3151
3152/**
3153 * @copydoc FNVDASYNCTRANSFERCOMPLETE
3154 */
3155static DECLCALLBACK(void) drvvdMediaExIoReqComplete(void *pvUser1, void *pvUser2, int rcReq)
3156{
3157 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
3158 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)pvUser2;
3159
3160 /*
3161 * For a read we need to sync the memory before continuing to process
3162 * the request further.
3163 */
3164 if ( RT_SUCCESS(rcReq)
3165 && pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
3166 rcReq = drvvdMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
3167
3168 /*
3169 * When the request owner instructs us to handle recoverable errors like full disks
3170 * do it. Mark the request as suspended, notify the owner and put the request on the
3171 * redo list.
3172 */
3173 if ( RT_FAILURE(rcReq)
3174 && (pIoReq->fFlags & PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR)
3175 && drvvdMediaExIoReqIsRedoSetWarning(pThis, rcReq))
3176 {
3177 int rc;
3178 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_SUSPENDED, VDIOREQSTATE_ACTIVE);
3179 if (fXchg)
3180 {
3181 /* Put on redo list and adjust active request counter. */
3182 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3183 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
3184 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3185 ASMAtomicDecU32(&pThis->cIoReqsActive);
3186 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
3187 PDMMEDIAEXIOREQSTATE_SUSPENDED);
3188 }
3189 else
3190 {
3191 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
3192 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3193 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rcReq, true /* fUpNotify */);
3194 }
3195 }
3196 else
3197 {
3198 /* Adjust the remaining amount to transfer. */
3199 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
3200 pIoReq->ReadWrite.offStart += cbReqIo;
3201 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
3202
3203 if ( RT_FAILURE(rcReq)
3204 || !pIoReq->ReadWrite.cbReqLeft
3205 || ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ
3206 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE))
3207 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rcReq, true /* fUpNotify */);
3208 else
3209 drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, true /* fUpNotify */);
3210 }
3211}
3212
3213/**
3214 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet}
3215 */
3216static DECLCALLBACK(int) drvvdIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
3217{
3218 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3219 int rc = VINF_SUCCESS;
3220
3221 if (RT_UNLIKELY(pThis->hIoReqCache != NIL_RTMEMCACHE))
3222 return VERR_INVALID_STATE;
3223
3224 return RTMemCacheCreate(&pThis->hIoReqCache, sizeof(PDMMEDIAEXIOREQINT) + cbIoReqAlloc, 0, UINT32_MAX,
3225 NULL, NULL, NULL, 0);
3226}
3227
3228/**
3229 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc}
3230 */
3231static DECLCALLBACK(int) drvvdIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
3232 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
3233{
3234 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3235
3236 AssertReturn(!(fFlags & ~PDMIMEDIAEX_F_VALID), VERR_INVALID_PARAMETER);
3237
3238 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)RTMemCacheAlloc(pThis->hIoReqCache);
3239
3240 if (RT_UNLIKELY(!pIoReq))
3241 return VERR_NO_MEMORY;
3242
3243 pIoReq->uIoReqId = uIoReqId;
3244 pIoReq->fFlags = fFlags;
3245 pIoReq->pDisk = pThis;
3246 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
3247 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_INVALID;
3248
3249 int rc = drvvdMediaExIoReqInsert(pThis, pIoReq);
3250 if (RT_SUCCESS(rc))
3251 {
3252 *phIoReq = pIoReq;
3253 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
3254 }
3255 else
3256 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
3257
3258 return rc;
3259}
3260
3261/**
3262 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree}
3263 */
3264static DECLCALLBACK(int) drvvdIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
3265{
3266 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3267 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3268
3269 if ( pIoReq->enmState != VDIOREQSTATE_COMPLETED
3270 && pIoReq->enmState != VDIOREQSTATE_ALLOCATED)
3271 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3272
3273 /* Remove from allocated list. */
3274 int rc = drvvdMediaExIoReqRemove(pThis, pIoReq);
3275 if (RT_FAILURE(rc))
3276 return rc;
3277
3278 /* Free any associated I/O memory. */
3279 drvvdMediaExIoReqBufFree(pThis, pIoReq);
3280
3281 /* For discard request discard the range array. */
3282 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD
3283 && pIoReq->Discard.paRanges)
3284 {
3285 RTMemFree(pIoReq->Discard.paRanges);
3286 pIoReq->Discard.paRanges = NULL;
3287 }
3288
3289 pIoReq->enmState = VDIOREQSTATE_FREE;
3290 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
3291 return VINF_SUCCESS;
3292}
3293
3294/**
3295 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel}
3296 */
3297static DECLCALLBACK(int) drvvdIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
3298{
3299 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3300 unsigned idxBin = drvvdMediaExIoReqIdHash(uIoReqId);
3301
3302 int rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3303 if (RT_SUCCESS(rc))
3304 {
3305 /* Search for I/O request with ID. */
3306 PPDMMEDIAEXIOREQINT pIt;
3307 rc = VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
3308
3309 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
3310 {
3311 if (pIt->uIoReqId == uIoReqId)
3312 {
3313 bool fXchg = true;
3314 VDIOREQSTATE enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIt->enmState);
3315
3316 /*
3317 * We might have to try canceling the request multiple times if it transitioned from
3318 * ALLOCATED to ACTIVE or to SUSPENDED between reading the state and trying to change it.
3319 */
3320 while ( ( enmStateOld == VDIOREQSTATE_ALLOCATED
3321 || enmStateOld == VDIOREQSTATE_ACTIVE
3322 || enmStateOld == VDIOREQSTATE_SUSPENDED)
3323 && !fXchg)
3324 {
3325 fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIt->enmState, VDIOREQSTATE_CANCELED, enmStateOld);
3326 if (!fXchg)
3327 enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIt->enmState);
3328 }
3329
3330 if (fXchg)
3331 {
3332 ASMAtomicDecU32(&pThis->cIoReqsActive);
3333 rc = VINF_SUCCESS;
3334 }
3335 break;
3336 }
3337 }
3338 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3339 }
3340
3341 return rc;
3342}
3343
3344/**
3345 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead}
3346 */
3347static DECLCALLBACK(int) drvvdIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
3348{
3349 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3350 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3351 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3352
3353 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3354 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3355
3356 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3357 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3358
3359 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_READ;
3360 pIoReq->tsSubmit = RTTimeMilliTS();
3361 pIoReq->ReadWrite.offStart = off;
3362 pIoReq->ReadWrite.cbReq = cbRead;
3363 pIoReq->ReadWrite.cbReqLeft = cbRead;
3364 /* Allocate a suitable I/O buffer for this request. */
3365 int rc = drvvdMediaExIoReqBufAlloc(pThis, pIoReq, cbRead);
3366 if (rc == VINF_SUCCESS)
3367 {
3368 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3369 if (RT_UNLIKELY(!fXchg))
3370 {
3371 /* Must have been canceled inbetween. */
3372 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3373 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3374 }
3375 ASMAtomicIncU32(&pThis->cIoReqsActive);
3376
3377 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
3378 }
3379
3380 return rc;
3381}
3382
3383/**
3384 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite}
3385 */
3386static DECLCALLBACK(int) drvvdIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
3387{
3388 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3389 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3390 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3391
3392 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3393 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3394
3395 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3396 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3397
3398 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_WRITE;
3399 pIoReq->tsSubmit = RTTimeMilliTS();
3400 pIoReq->ReadWrite.offStart = off;
3401 pIoReq->ReadWrite.cbReq = cbWrite;
3402 pIoReq->ReadWrite.cbReqLeft = cbWrite;
3403 /* Allocate a suitable I/O buffer for this request. */
3404 int rc = drvvdMediaExIoReqBufAlloc(pThis, pIoReq, cbWrite);
3405 if (rc == VINF_SUCCESS)
3406 {
3407 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3408 if (RT_UNLIKELY(!fXchg))
3409 {
3410 /* Must have been canceled inbetween. */
3411 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3412 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3413 }
3414 ASMAtomicIncU32(&pThis->cIoReqsActive);
3415
3416 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
3417 }
3418
3419 return rc;
3420}
3421
3422/**
3423 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush}
3424 */
3425static DECLCALLBACK(int) drvvdIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
3426{
3427 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3428 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3429 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3430
3431 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3432 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3433
3434 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3435 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3436
3437 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_FLUSH;
3438 pIoReq->tsSubmit = RTTimeMilliTS();
3439 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3440 if (RT_UNLIKELY(!fXchg))
3441 {
3442 /* Must have been canceled inbetween. */
3443 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3444 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3445 }
3446
3447 ASMAtomicIncU32(&pThis->cIoReqsActive);
3448 int rc = VDAsyncFlush(pThis->pDisk, drvvdMediaExIoReqComplete, pThis, pIoReq);
3449 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3450 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3451 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3452 rc = VINF_SUCCESS;
3453
3454 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3455 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, false /* fUpNotify */);
3456
3457 return rc;
3458}
3459
3460/**
3461 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard}
3462 */
3463static DECLCALLBACK(int) drvvdIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, PCRTRANGE paRanges, unsigned cRanges)
3464{
3465 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3466 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3467 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3468
3469 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3470 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3471
3472 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3473 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3474
3475 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_DISCARD;
3476 pIoReq->tsSubmit = RTTimeMilliTS();
3477 /* Copy the ranges over because they might not be valid anymore when this method returns. */
3478 pIoReq->Discard.paRanges = (PRTRANGE)RTMemDup(paRanges, cRanges * sizeof(RTRANGE));
3479 if (RT_UNLIKELY(!pIoReq->Discard.paRanges))
3480 return VERR_NO_MEMORY;
3481
3482 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3483 if (RT_UNLIKELY(!fXchg))
3484 {
3485 /* Must have been canceled inbetween. */
3486 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3487 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3488 }
3489
3490 ASMAtomicIncU32(&pThis->cIoReqsActive);
3491 int rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges,
3492 drvvdMediaExIoReqComplete, pThis, pIoReq);
3493 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3494 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3495 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3496 rc = VINF_SUCCESS;
3497
3498 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3499 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, false /* fUpNotify */);
3500
3501 return rc;
3502}
3503
3504/**
3505 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount}
3506 */
3507static DECLCALLBACK(uint32_t) drvvdIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
3508{
3509 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3510 return ASMAtomicReadU32(&pThis->cIoReqsActive);
3511}
3512
3513/**
3514 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount}
3515 */
3516static DECLCALLBACK(uint32_t) drvvdIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
3517{
3518 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3519
3520 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), 0);
3521
3522 uint32_t cIoReqSuspended = 0;
3523 PPDMMEDIAEXIOREQINT pIoReq;
3524 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3525 RTListForEach(&pThis->LstIoReqRedo, pIoReq, PDMMEDIAEXIOREQINT, NdLstWait)
3526 {
3527 cIoReqSuspended++;
3528 }
3529 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3530
3531 return cIoReqSuspended;
3532}
3533
3534/**
3535 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedFirst}
3536 */
3537static DECLCALLBACK(int) drvvdIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq,
3538 void **ppvIoReqAlloc)
3539{
3540 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3541
3542 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3543 AssertReturn(!RTListIsEmpty(&pThis->LstIoReqRedo), VERR_NOT_FOUND);
3544
3545 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3546 PPDMMEDIAEXIOREQINT pIoReq = RTListGetFirst(&pThis->LstIoReqRedo, PDMMEDIAEXIOREQINT, NdLstWait);
3547 *phIoReq = pIoReq;
3548 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
3549 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3550
3551 return VINF_SUCCESS;
3552}
3553
3554/**
3555 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext}
3556 */
3557static DECLCALLBACK(int) drvvdIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
3558 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
3559{
3560 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3561 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3562
3563 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3564 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3565 AssertReturn(!RTListNodeIsLast(&pThis->LstIoReqRedo, &pIoReq->NdLstWait), VERR_NOT_FOUND);
3566
3567 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3568 PPDMMEDIAEXIOREQINT pIoReqNext = RTListNodeGetNext(&pIoReq->NdLstWait, PDMMEDIAEXIOREQINT, NdLstWait);
3569 *phIoReqNext = pIoReqNext;
3570 *ppvIoReqAllocNext = &pIoReqNext->abAlloc[0];
3571 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3572
3573 return VINF_SUCCESS;
3574}
3575
3576/**
3577 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave}
3578 */
3579static DECLCALLBACK(int) drvvdIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
3580{
3581 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3582 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3583
3584 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3585 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3586 AssertReturn(pIoReq->enmState == VDIOREQSTATE_SUSPENDED, VERR_INVALID_STATE);
3587
3588 SSMR3PutU32(pSSM, DRVVD_IOREQ_SAVED_STATE_VERSION);
3589 SSMR3PutU32(pSSM, (uint32_t)pIoReq->enmType);
3590 SSMR3PutU32(pSSM, pIoReq->uIoReqId);
3591 SSMR3PutU32(pSSM, pIoReq->fFlags);
3592 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3593 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3594 {
3595 SSMR3PutU64(pSSM, pIoReq->ReadWrite.offStart);
3596 SSMR3PutU64(pSSM, pIoReq->ReadWrite.cbReq);
3597 SSMR3PutU64(pSSM, pIoReq->ReadWrite.cbReqLeft);
3598 }
3599 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
3600 {
3601 SSMR3PutU32(pSSM, pIoReq->Discard.cRanges);
3602 for (unsigned i = 0; i < pIoReq->Discard.cRanges; i++)
3603 {
3604 SSMR3PutU64(pSSM, pIoReq->Discard.paRanges[i].offStart);
3605 SSMR3PutU64(pSSM, pIoReq->Discard.paRanges[i].cbRange);
3606 }
3607 }
3608
3609 return SSMR3PutU32(pSSM, UINT32_MAX); /* sanity/terminator */
3610}
3611
3612/**
3613 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad}
3614 */
3615static DECLCALLBACK(int) drvvdIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
3616{
3617 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3618 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3619
3620 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3621 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3622 AssertReturn(pIoReq->enmState == VDIOREQSTATE_ALLOCATED, VERR_INVALID_STATE);
3623
3624 uint32_t u32;
3625 uint64_t u64;
3626 int rc = VINF_SUCCESS;
3627 bool fPlaceOnRedoList = true;
3628
3629 SSMR3GetU32(pSSM, &u32);
3630 if (u32 <= DRVVD_IOREQ_SAVED_STATE_VERSION)
3631 {
3632 SSMR3GetU32(pSSM, &u32);
3633 AssertReturn( u32 == PDMMEDIAEXIOREQTYPE_WRITE
3634 || u32 == PDMMEDIAEXIOREQTYPE_READ
3635 || u32 == PDMMEDIAEXIOREQTYPE_DISCARD
3636 || u32 == PDMMEDIAEXIOREQTYPE_FLUSH,
3637 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3638 pIoReq->enmType = (PDMMEDIAEXIOREQTYPE)u32;
3639
3640 SSMR3GetU32(pSSM, &u32);
3641 AssertReturn(u32 == pIoReq->uIoReqId, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3642
3643 SSMR3GetU32(pSSM, &u32);
3644 AssertReturn(u32 == pIoReq->fFlags, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3645
3646 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3647 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3648 {
3649 SSMR3GetU64(pSSM, &pIoReq->ReadWrite.offStart);
3650 SSMR3GetU64(pSSM, &u64);
3651 pIoReq->ReadWrite.cbReq = (size_t)u64;
3652 SSMR3GetU64(pSSM, &u64);
3653 pIoReq->ReadWrite.cbReqLeft = (size_t)u64;
3654
3655 /*
3656 * Try to allocate enough I/O buffer, if this fails for some reason put it onto the
3657 * waitign list instead of the redo list.
3658 */
3659 pIoReq->ReadWrite.cbIoBuf = 0;
3660 rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, pIoReq->ReadWrite.cbReqLeft,
3661 &pIoReq->ReadWrite.cbIoBuf);
3662 if (rc == VERR_NO_MEMORY)
3663 {
3664 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
3665 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
3666 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
3667 fPlaceOnRedoList = false;
3668 rc = VINF_SUCCESS;
3669 }
3670 }
3671 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
3672 {
3673 rc = SSMR3GetU32(pSSM, &pIoReq->Discard.cRanges);
3674 if (RT_SUCCESS(rc))
3675 {
3676 pIoReq->Discard.paRanges = (PRTRANGE)RTMemAllocZ(pIoReq->Discard.cRanges * sizeof(RTRANGE));
3677 if (RT_LIKELY(pIoReq->Discard.paRanges))
3678 {
3679 for (unsigned i = 0; i < pIoReq->Discard.cRanges; i++)
3680 {
3681 SSMR3GetU64(pSSM, &pIoReq->Discard.paRanges[i].offStart);
3682 SSMR3GetU64(pSSM, &u64);
3683 pIoReq->Discard.paRanges[i].cbRange = (size_t)u64;
3684 }
3685 }
3686 else
3687 rc = VERR_NO_MEMORY;
3688 }
3689 }
3690
3691 if (RT_SUCCESS(rc))
3692 rc = SSMR3GetU32(pSSM, &u32); /* sanity/terminator */
3693 if (RT_SUCCESS(rc))
3694 AssertReturn(u32 == UINT32_MAX, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3695 if ( RT_SUCCESS(rc)
3696 && fPlaceOnRedoList)
3697 {
3698 /* Mark as suspended */
3699 pIoReq->enmState = VDIOREQSTATE_SUSPENDED;
3700
3701 /* Link into suspended list so it gets kicked off again when we resume. */
3702 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3703 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
3704 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3705 }
3706 }
3707
3708 return rc;
3709}
3710
3711/**
3712 * Loads all configured plugins.
3713 *
3714 * @returns VBox status code.
3715 * @param pThis The disk instance.
3716 * @param pCfg CFGM node holding plugin list.
3717 */
3718static int drvvdLoadPlugins(PVBOXDISK pThis, PCFGMNODE pCfg)
3719{
3720 int rc = VINF_SUCCESS;
3721 PCFGMNODE pCfgPlugins = CFGMR3GetChild(pCfg, "Plugins");
3722
3723 if (pCfgPlugins)
3724 {
3725 PCFGMNODE pPluginCur = CFGMR3GetFirstChild(pCfgPlugins);
3726 while ( pPluginCur
3727 && RT_SUCCESS(rc))
3728 {
3729 char *pszPluginFilename = NULL;
3730 rc = CFGMR3QueryStringAlloc(pPluginCur, "Path", &pszPluginFilename);
3731 if (RT_SUCCESS(rc))
3732 rc = VDPluginLoadFromFilename(pszPluginFilename);
3733
3734 pPluginCur = CFGMR3GetNextChild(pPluginCur);
3735 }
3736 }
3737
3738 return rc;
3739}
3740
3741
3742/**
3743 * Sets up the disk filter chain.
3744 *
3745 * @returns VBox status code.
3746 * @param pThis The disk instance.
3747 * @param pCfg CFGM node holding the filter parameters.
3748 */
3749static int drvvdSetupFilters(PVBOXDISK pThis, PCFGMNODE pCfg)
3750{
3751 int rc = VINF_SUCCESS;
3752 PCFGMNODE pCfgFilter = CFGMR3GetChild(pCfg, "Filters");
3753
3754 if (pCfgFilter)
3755 {
3756 PCFGMNODE pCfgFilterConfig = CFGMR3GetChild(pCfgFilter, "VDConfig");
3757 char *pszFilterName = NULL;
3758 VDINTERFACECONFIG VDIfConfig;
3759 PVDINTERFACE pVDIfsFilter = NULL;
3760
3761 rc = CFGMR3QueryStringAlloc(pCfgFilter, "FilterName", &pszFilterName);
3762 if (RT_SUCCESS(rc))
3763 {
3764 VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
3765 VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
3766 VDIfConfig.pfnQuery = drvvdCfgQuery;
3767 VDIfConfig.pfnQueryBytes = drvvdCfgQueryBytes;
3768 rc = VDInterfaceAdd(&VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
3769 pCfgFilterConfig, sizeof(VDINTERFACECONFIG), &pVDIfsFilter);
3770 AssertRC(rc);
3771
3772 rc = VDFilterAdd(pThis->pDisk, pszFilterName, VD_FILTER_FLAGS_DEFAULT, pVDIfsFilter);
3773
3774 MMR3HeapFree(pszFilterName);
3775 }
3776 }
3777
3778 return rc;
3779}
3780
3781
3782/**
3783 * Translates a PDMMEDIATYPE value into a string.
3784 *
3785 * @returns Read only string.
3786 * @param enmType The type value.
3787 */
3788static const char *drvvdGetTypeName(PDMMEDIATYPE enmType)
3789{
3790 switch (enmType)
3791 {
3792 case PDMMEDIATYPE_ERROR: return "ERROR";
3793 case PDMMEDIATYPE_FLOPPY_360: return "FLOPPY_360";
3794 case PDMMEDIATYPE_FLOPPY_720: return "FLOPPY_720";
3795 case PDMMEDIATYPE_FLOPPY_1_20: return "FLOPPY_1_20";
3796 case PDMMEDIATYPE_FLOPPY_1_44: return "FLOPPY_1_44";
3797 case PDMMEDIATYPE_FLOPPY_2_88: return "FLOPPY_2_88";
3798 case PDMMEDIATYPE_FLOPPY_FAKE_15_6: return "FLOPPY_FAKE_15_6";
3799 case PDMMEDIATYPE_FLOPPY_FAKE_63_5: return "FLOPPY_FAKE_63_5";
3800 case PDMMEDIATYPE_CDROM: return "CDROM";
3801 case PDMMEDIATYPE_DVD: return "DVD";
3802 case PDMMEDIATYPE_HARD_DISK: return "HARD_DISK";
3803 default: return "Unknown";
3804 }
3805}
3806
3807/**
3808 * Returns the appropriate PDMMEDIATYPE for t he given string.
3809 *
3810 * @returns PDMMEDIATYPE
3811 * @param pszType The string representation of the media type.
3812 */
3813static PDMMEDIATYPE drvvdGetMediaTypeFromString(const char *pszType)
3814{
3815 PDMMEDIATYPE enmType = PDMMEDIATYPE_ERROR;
3816
3817 if (!strcmp(pszType, "HardDisk"))
3818 enmType = PDMMEDIATYPE_HARD_DISK;
3819 else if (!strcmp(pszType, "DVD"))
3820 enmType = PDMMEDIATYPE_DVD;
3821 else if (!strcmp(pszType, "CDROM"))
3822 enmType = PDMMEDIATYPE_CDROM;
3823 else if (!strcmp(pszType, "Floppy 2.88"))
3824 enmType = PDMMEDIATYPE_FLOPPY_2_88;
3825 else if (!strcmp(pszType, "Floppy 1.44"))
3826 enmType = PDMMEDIATYPE_FLOPPY_1_44;
3827 else if (!strcmp(pszType, "Floppy 1.20"))
3828 enmType = PDMMEDIATYPE_FLOPPY_1_20;
3829 else if (!strcmp(pszType, "Floppy 720"))
3830 enmType = PDMMEDIATYPE_FLOPPY_720;
3831 else if (!strcmp(pszType, "Floppy 360"))
3832 enmType = PDMMEDIATYPE_FLOPPY_360;
3833 else if (!strcmp(pszType, "Floppy 15.6"))
3834 enmType = PDMMEDIATYPE_FLOPPY_FAKE_15_6;
3835 else if (!strcmp(pszType, "Floppy 63.5"))
3836 enmType = PDMMEDIATYPE_FLOPPY_FAKE_63_5;
3837
3838 return enmType;
3839}
3840
3841/**
3842 * Converts PDMMEDIATYPE to the appropriate VDTYPE.
3843 *
3844 * @returns The VDTYPE.
3845 * @param enmType The PDMMEDIATYPE to convert from.
3846 */
3847static VDTYPE drvvdGetVDFromMediaType(PDMMEDIATYPE enmType)
3848{
3849 if (PDMMEDIATYPE_IS_FLOPPY(enmType))
3850 return VDTYPE_FLOPPY;
3851 else if (enmType == PDMMEDIATYPE_DVD || enmType == PDMMEDIATYPE_CDROM)
3852 return VDTYPE_DVD;
3853 else if (enmType == PDMMEDIATYPE_HARD_DISK)
3854 return VDTYPE_HDD;
3855
3856 AssertMsgFailed(("Invalid media type %d{%s} given!\n", enmType, drvvdGetTypeName(enmType)));
3857 return VDTYPE_HDD;
3858}
3859
3860/*********************************************************************************************************************************
3861* Base interface methods *
3862*********************************************************************************************************************************/
3863
3864/**
3865 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3866 */
3867static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3868{
3869 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3870 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
3871
3872 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3873 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
3874 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->fMountable ? &pThis->IMount : NULL);
3875 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL);
3876 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDrvMediaExPort ? &pThis->IMediaEx : NULL);
3877 return NULL;
3878}
3879
3880
3881/*********************************************************************************************************************************
3882* Saved state notification methods *
3883*********************************************************************************************************************************/
3884
3885/**
3886 * Load done callback for re-opening the image writable during teleportation.
3887 *
3888 * This is called both for successful and failed load runs, we only care about
3889 * successful ones.
3890 *
3891 * @returns VBox status code.
3892 * @param pDrvIns The driver instance.
3893 * @param pSSM The saved state handle.
3894 */
3895static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
3896{
3897 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
3898 Assert(!pThis->fErrorUseRuntime);
3899
3900 /* Drop out if we don't have any work to do or if it's a failed load. */
3901 if ( !pThis->fTempReadOnly
3902 || RT_FAILURE(SSMR3HandleGetStatus(pSSM)))
3903 return VINF_SUCCESS;
3904
3905 int rc = drvvdSetWritable(pThis);
3906 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
3907 return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
3908 N_("Failed to write lock the images"));
3909 return VINF_SUCCESS;
3910}
3911
3912
3913/*********************************************************************************************************************************
3914* Driver methods *
3915*********************************************************************************************************************************/
3916
3917/**
3918 * Worker for the power off or destruct callback.
3919 *
3920 * @returns nothing.
3921 * @param pDrvIns The driver instance.
3922 */
3923static void drvvdPowerOffOrDestructOrUnmount(PPDMDRVINS pDrvIns)
3924{
3925 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
3926 LogFlowFunc(("\n"));
3927
3928 RTSEMFASTMUTEX mutex;
3929 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
3930 if (mutex != NIL_RTSEMFASTMUTEX)
3931 {
3932 /* Request the semaphore to wait until a potentially running merge
3933 * operation has been finished. */
3934 int rc = RTSemFastMutexRequest(mutex);
3935 AssertRC(rc);
3936 pThis->fMergePending = false;
3937 rc = RTSemFastMutexRelease(mutex);
3938 AssertRC(rc);
3939 rc = RTSemFastMutexDestroy(mutex);
3940 AssertRC(rc);
3941 }
3942
3943 if (RT_VALID_PTR(pThis->pBlkCache))
3944 {
3945 PDMR3BlkCacheRelease(pThis->pBlkCache);
3946 pThis->pBlkCache = NULL;
3947 }
3948
3949 if (RT_VALID_PTR(pThis->pDisk))
3950 {
3951 VDDestroy(pThis->pDisk);
3952 pThis->pDisk = NULL;
3953 }
3954 drvvdFreeImages(pThis);
3955}
3956
3957/**
3958 * @copydoc FNPDMDRVPOWEROFF
3959 */
3960static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
3961{
3962 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3963 drvvdPowerOffOrDestructOrUnmount(pDrvIns);
3964}
3965
3966/**
3967 * VM resume notification that we use to undo what the temporary read-only image
3968 * mode set by drvvdSuspend.
3969 *
3970 * Also switch to runtime error mode if we're resuming after a state load
3971 * without having been powered on first.
3972 *
3973 * @param pDrvIns The driver instance data.
3974 *
3975 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
3976 * we're making assumptions about Main behavior here!
3977 */
3978static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
3979{
3980 LogFlowFunc(("\n"));
3981 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
3982
3983 drvvdSetWritable(pThis);
3984 pThis->fErrorUseRuntime = true;
3985
3986 if (pThis->pBlkCache)
3987 {
3988 int rc = PDMR3BlkCacheResume(pThis->pBlkCache);
3989 AssertRC(rc);
3990 }
3991
3992 if (pThis->pDrvMediaExPort)
3993 {
3994 /* Kick of any request we have to redo. */
3995 PPDMMEDIAEXIOREQINT pIoReq, pIoReqNext;
3996 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3997 RTListForEachSafe(&pThis->LstIoReqRedo, pIoReq, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3998 {
3999 int rc = VINF_SUCCESS;
4000 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_SUSPENDED);
4001
4002 RTListNodeRemove(&pIoReq->NdLstWait);
4003 ASMAtomicIncU32(&pThis->cIoReqsActive);
4004
4005 if (fXchg)
4006 {
4007 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
4008 PDMMEDIAEXIOREQSTATE_ACTIVE);
4009 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
4010 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
4011 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
4012 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
4013 {
4014 rc = VDAsyncFlush(pThis->pDisk, drvvdMediaExIoReqComplete, pThis, pIoReq);
4015 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4016 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
4017 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
4018 rc = VINF_SUCCESS;
4019 }
4020 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
4021 {
4022 rc = VDAsyncDiscardRanges(pThis->pDisk, pIoReq->Discard.paRanges, pIoReq->Discard.cRanges,
4023 drvvdMediaExIoReqComplete, pThis, pIoReq);
4024 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4025 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
4026 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
4027 rc = VINF_SUCCESS;
4028 }
4029 else
4030 AssertMsgFailed(("Invalid request type %u\n", pIoReq->enmType));
4031
4032 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
4033 {
4034 Assert( ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE
4035 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ)
4036 || !pIoReq->ReadWrite.cbReqLeft
4037 || RT_FAILURE(rc));
4038 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, true /* fUpNotify */);
4039 }
4040
4041 }
4042 else
4043 {
4044 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
4045 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
4046 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
4047 }
4048 }
4049 Assert(RTListIsEmpty(&pThis->LstIoReqRedo));
4050 RTCritSectLeave(&pThis->CritSectIoReqRedo);
4051 }
4052}
4053
4054/**
4055 * The VM is being suspended, temporarily change to read-only image mode.
4056 *
4057 * This is important for several reasons:
4058 * -# It makes sure that there are no pending writes to the image. Most
4059 * backends implements this by closing and reopening the image in read-only
4060 * mode.
4061 * -# It allows Main to read the images during snapshotting without having
4062 * to account for concurrent writes.
4063 * -# This is essential for making teleportation targets sharing images work
4064 * right. Both with regards to caching and with regards to file sharing
4065 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
4066 *
4067 * @param pDrvIns The driver instance data.
4068 */
4069static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
4070{
4071 LogFlowFunc(("\n"));
4072 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4073
4074 if (pThis->pBlkCache)
4075 {
4076 int rc = PDMR3BlkCacheSuspend(pThis->pBlkCache);
4077 AssertRC(rc);
4078 }
4079
4080 drvvdSetReadonly(pThis);
4081}
4082
4083/**
4084 * VM PowerOn notification for undoing the TempReadOnly config option and
4085 * changing to runtime error mode.
4086 *
4087 * @param pDrvIns The driver instance data.
4088 *
4089 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
4090 * we're making assumptions about Main behavior here!
4091 */
4092static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
4093{
4094 LogFlowFunc(("\n"));
4095 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4096 drvvdSetWritable(pThis);
4097 pThis->fErrorUseRuntime = true;
4098}
4099
4100/**
4101 * @copydoc FNPDMDRVRESET
4102 */
4103static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
4104{
4105 LogFlowFunc(("\n"));
4106 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4107
4108 if (pThis->pBlkCache)
4109 {
4110 int rc = PDMR3BlkCacheClear(pThis->pBlkCache);
4111 AssertRC(rc);
4112 }
4113
4114 if (pThis->fBootAccelEnabled)
4115 {
4116 pThis->fBootAccelActive = true;
4117 pThis->cbDataValid = 0;
4118 pThis->offDisk = 0;
4119 }
4120}
4121
4122/**
4123 * @copydoc FNPDMDRVDESTRUCT
4124 */
4125static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
4126{
4127 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4128 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4129 LogFlowFunc(("\n"));
4130
4131 /*
4132 * Make sure the block cache and disks are closed when this driver is
4133 * destroyed. This method will get called without calling the power off
4134 * callback first when we reconfigure the driver chain after a snapshot.
4135 */
4136 drvvdPowerOffOrDestructOrUnmount(pDrvIns);
4137 if (pThis->MergeLock != NIL_RTSEMRW)
4138 {
4139 int rc = RTSemRWDestroy(pThis->MergeLock);
4140 AssertRC(rc);
4141 pThis->MergeLock = NIL_RTSEMRW;
4142 }
4143 if (pThis->pbData)
4144 {
4145 RTMemFree(pThis->pbData);
4146 pThis->pbData = NULL;
4147 }
4148 if (pThis->pszBwGroup)
4149 {
4150 MMR3HeapFree(pThis->pszBwGroup);
4151 pThis->pszBwGroup = NULL;
4152 }
4153 if (pThis->hHbdMgr != NIL_HBDMGR)
4154 HBDMgrDestroy(pThis->hHbdMgr);
4155 if (pThis->hIoReqCache != NIL_RTMEMCACHE)
4156 RTMemCacheDestroy(pThis->hIoReqCache);
4157 if (pThis->hIoBufMgr != NIL_IOBUFMGR)
4158 IOBUFMgrDestroy(pThis->hIoBufMgr);
4159 if (RTCritSectIsInitialized(&pThis->CritSectIoReqsIoBufWait))
4160 RTCritSectDelete(&pThis->CritSectIoReqsIoBufWait);
4161 if (RTCritSectIsInitialized(&pThis->CritSectIoReqRedo))
4162 RTCritSectDelete(&pThis->CritSectIoReqRedo);
4163 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4164 {
4165 if (pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc != NIL_RTSEMFASTMUTEX)
4166 RTSemFastMutexDestroy(pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
4167 }
4168}
4169
4170/**
4171 * Construct a VBox disk media driver instance.
4172 *
4173 * @copydoc FNPDMDRVCONSTRUCT
4174 */
4175static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4176{
4177 LogFlowFunc(("\n"));
4178 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4179 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4180 int rc = VINF_SUCCESS;
4181 char *pszName = NULL; /**< The path of the disk image file. */
4182 char *pszFormat = NULL; /**< The format backed to use for this image. */
4183 char *pszCachePath = NULL; /**< The path to the cache image. */
4184 char *pszCacheFormat = NULL; /**< The format backend to use for the cache image. */
4185 bool fReadOnly; /**< True if the media is read-only. */
4186 bool fMaybeReadOnly; /**< True if the media may or may not be read-only. */
4187 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
4188
4189 /*
4190 * Init the static parts.
4191 */
4192 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
4193 pThis->pDrvIns = pDrvIns;
4194 pThis->fTempReadOnly = false;
4195 pThis->pDisk = NULL;
4196 pThis->fAsyncIOSupported = false;
4197 pThis->fShareable = false;
4198 pThis->fMergePending = false;
4199 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
4200 pThis->MergeLock = NIL_RTSEMRW;
4201 pThis->uMergeSource = VD_LAST_IMAGE;
4202 pThis->uMergeTarget = VD_LAST_IMAGE;
4203 pThis->pCfgCrypto = NULL;
4204 pThis->pIfSecKey = NULL;
4205 pThis->hIoReqCache = NIL_RTMEMCACHE;
4206 pThis->hIoBufMgr = NIL_IOBUFMGR;
4207
4208 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4209 pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc = NIL_RTSEMFASTMUTEX;
4210
4211 /* IMedia */
4212 pThis->IMedia.pfnRead = drvvdRead;
4213 pThis->IMedia.pfnReadPcBios = drvvdReadPcBios;
4214 pThis->IMedia.pfnWrite = drvvdWrite;
4215 pThis->IMedia.pfnFlush = drvvdFlush;
4216 pThis->IMedia.pfnMerge = drvvdMerge;
4217 pThis->IMedia.pfnSetSecKeyIf = drvvdSetSecKeyIf;
4218 pThis->IMedia.pfnGetSize = drvvdGetSize;
4219 pThis->IMedia.pfnGetSectorSize = drvvdGetSectorSize;
4220 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
4221 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
4222 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
4223 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
4224 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
4225 pThis->IMedia.pfnBiosIsVisible = drvvdBiosIsVisible;
4226 pThis->IMedia.pfnGetType = drvvdGetType;
4227 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
4228 pThis->IMedia.pfnDiscard = drvvdDiscard;
4229 pThis->IMedia.pfnIoBufAlloc = drvvdIoBufAlloc;
4230 pThis->IMedia.pfnIoBufFree = drvvdIoBufFree;
4231 pThis->IMedia.pfnSendCmd = NULL;
4232
4233 /* IMount */
4234 pThis->IMount.pfnUnmount = drvvdUnmount;
4235 pThis->IMount.pfnIsMounted = drvvdIsMounted;
4236 pThis->IMount.pfnLock = drvvdLock;
4237 pThis->IMount.pfnUnlock = drvvdUnlock;
4238 pThis->IMount.pfnIsLocked = drvvdIsLocked;
4239
4240 /* IMediaAsync */
4241 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
4242 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
4243 pThis->IMediaAsync.pfnStartFlush = drvvdStartFlush;
4244 pThis->IMediaAsync.pfnStartDiscard = drvvdStartDiscard;
4245
4246 /* IMediaEx */
4247 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvvdIoReqAllocSizeSet;
4248 pThis->IMediaEx.pfnIoReqAlloc = drvvdIoReqAlloc;
4249 pThis->IMediaEx.pfnIoReqFree = drvvdIoReqFree;
4250 pThis->IMediaEx.pfnIoReqCancel = drvvdIoReqCancel;
4251 pThis->IMediaEx.pfnIoReqRead = drvvdIoReqRead;
4252 pThis->IMediaEx.pfnIoReqWrite = drvvdIoReqWrite;
4253 pThis->IMediaEx.pfnIoReqFlush = drvvdIoReqFlush;
4254 pThis->IMediaEx.pfnIoReqDiscard = drvvdIoReqDiscard;
4255 pThis->IMediaEx.pfnIoReqGetActiveCount = drvvdIoReqGetActiveCount;
4256 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvvdIoReqGetSuspendedCount;
4257 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvvdIoReqQuerySuspendedStart;
4258 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvvdIoReqQuerySuspendedNext;
4259 pThis->IMediaEx.pfnIoReqSuspendedSave = drvvdIoReqSuspendedSave;
4260 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvvdIoReqSuspendedLoad;
4261
4262 /* Initialize supported VD interfaces. */
4263 pThis->pVDIfsDisk = NULL;
4264
4265 pThis->VDIfError.pfnError = drvvdErrorCallback;
4266 pThis->VDIfError.pfnMessage = NULL;
4267 rc = VDInterfaceAdd(&pThis->VDIfError.Core, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
4268 pDrvIns, sizeof(VDINTERFACEERROR), &pThis->pVDIfsDisk);
4269 AssertRC(rc);
4270
4271 /* List of images is empty now. */
4272 pThis->pImages = NULL;
4273
4274 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
4275 if (!pThis->pDrvMediaPort)
4276 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
4277 N_("No media port interface above"));
4278
4279 /* Try to attach async media port interface above.*/
4280 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
4281 pThis->pDrvMountNotify = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMOUNTNOTIFY);
4282
4283 /*
4284 * Try to attach the optional extended media interface port above and initialize associated
4285 * structures if available.
4286 */
4287 pThis->pDrvMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
4288 if (pThis->pDrvMediaExPort)
4289 {
4290 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4291 {
4292 rc = RTSemFastMutexCreate(&pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
4293 if (RT_FAILURE(rc))
4294 break;
4295 RTListInit(&pThis->aIoReqAllocBins[i].LstIoReqAlloc);
4296 }
4297
4298 if (RT_SUCCESS(rc))
4299 rc = RTCritSectInit(&pThis->CritSectIoReqsIoBufWait);
4300
4301 if (RT_SUCCESS(rc))
4302 rc = RTCritSectInit(&pThis->CritSectIoReqRedo);
4303
4304 if (RT_FAILURE(rc))
4305 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Creating Mutex failed"));
4306
4307 RTListInit(&pThis->LstIoReqIoBufWait);
4308 RTListInit(&pThis->LstIoReqRedo);
4309 }
4310
4311 /* Before we access any VD API load all given plugins. */
4312 rc = drvvdLoadPlugins(pThis, pCfg);
4313 if (RT_FAILURE(rc))
4314 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Loading VD plugins failed"));
4315
4316 /*
4317 * Validate configuration and find all parent images.
4318 * It's sort of up side down from the image dependency tree.
4319 */
4320 bool fHostIP = false;
4321 bool fUseNewIo = false;
4322 bool fUseBlockCache = false;
4323 bool fDiscard = false;
4324 bool fInformAboutZeroBlocks = false;
4325 bool fSkipConsistencyChecks = false;
4326 bool fEmptyDrive = false;
4327 unsigned iLevel = 0;
4328 PCFGMNODE pCurNode = pCfg;
4329 uint32_t cbIoBufMax = 0;
4330
4331 for (;;)
4332 {
4333 bool fValid;
4334
4335 if (pCurNode == pCfg)
4336 {
4337 /* Toplevel configuration additionally contains the global image
4338 * open flags. Some might be converted to per-image flags later. */
4339 fValid = CFGMR3AreValuesValid(pCurNode,
4340 "Format\0Path\0"
4341 "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
4342 "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
4343 "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
4344 "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"
4345 "SkipConsistencyChecks\0"
4346 "Locked\0BIOSVisible\0Cylinders\0Heads\0Sectors\0Mountable\0"
4347 "EmptyDrive\0IoBufMax\0"
4348#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
4349 "FlushInterval\0IgnoreFlush\0IgnoreFlushAsync\0"
4350#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
4351 );
4352 }
4353 else
4354 {
4355 /* All other image configurations only contain image name and
4356 * the format information. */
4357 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0"
4358 "MergeSource\0MergeTarget\0");
4359 }
4360 if (!fValid)
4361 {
4362 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
4363 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
4364 break;
4365 }
4366
4367 if (pCurNode == pCfg)
4368 {
4369 rc = CFGMR3QueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
4370 if (RT_FAILURE(rc))
4371 {
4372 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4373 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
4374 break;
4375 }
4376
4377 rc = CFGMR3QueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
4378 if (RT_FAILURE(rc))
4379 {
4380 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4381 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
4382 break;
4383 }
4384
4385 rc = CFGMR3QueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
4386 if (RT_FAILURE(rc))
4387 {
4388 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4389 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
4390 break;
4391 }
4392
4393 rc = CFGMR3QueryBoolDef(pCurNode, "MaybeReadOnly", &fMaybeReadOnly, false);
4394 if (RT_FAILURE(rc))
4395 {
4396 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4397 N_("DrvVD: Configuration error: Querying \"MaybeReadOnly\" as boolean failed"));
4398 break;
4399 }
4400
4401 rc = CFGMR3QueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
4402 if (RT_FAILURE(rc))
4403 {
4404 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4405 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
4406 break;
4407 }
4408 if (fReadOnly && pThis->fTempReadOnly)
4409 {
4410 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4411 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
4412 break;
4413 }
4414
4415 rc = CFGMR3QueryBoolDef(pCurNode, "Shareable", &pThis->fShareable, false);
4416 if (RT_FAILURE(rc))
4417 {
4418 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4419 N_("DrvVD: Configuration error: Querying \"Shareable\" as boolean failed"));
4420 break;
4421 }
4422
4423 rc = CFGMR3QueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
4424 if (RT_FAILURE(rc))
4425 {
4426 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4427 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
4428 break;
4429 }
4430 rc = CFGMR3QueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
4431 if (RT_FAILURE(rc))
4432 {
4433 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4434 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
4435 break;
4436 }
4437 if (fReadOnly && pThis->fMergePending)
4438 {
4439 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4440 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
4441 break;
4442 }
4443 rc = CFGMR3QueryBoolDef(pCurNode, "BootAcceleration", &pThis->fBootAccelEnabled, false);
4444 if (RT_FAILURE(rc))
4445 {
4446 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4447 N_("DrvVD: Configuration error: Querying \"BootAcceleration\" as boolean failed"));
4448 break;
4449 }
4450 rc = CFGMR3QueryU32Def(pCurNode, "BootAccelerationBuffer", (uint32_t *)&pThis->cbBootAccelBuffer, 16 * _1K);
4451 if (RT_FAILURE(rc))
4452 {
4453 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4454 N_("DrvVD: Configuration error: Querying \"BootAccelerationBuffer\" as integer failed"));
4455 break;
4456 }
4457 rc = CFGMR3QueryBoolDef(pCurNode, "BlockCache", &fUseBlockCache, false);
4458 if (RT_FAILURE(rc))
4459 {
4460 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4461 N_("DrvVD: Configuration error: Querying \"BlockCache\" as boolean failed"));
4462 break;
4463 }
4464 rc = CFGMR3QueryStringAlloc(pCurNode, "BwGroup", &pThis->pszBwGroup);
4465 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
4466 {
4467 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4468 N_("DrvVD: Configuration error: Querying \"BwGroup\" as string failed"));
4469 break;
4470 }
4471 else
4472 rc = VINF_SUCCESS;
4473 rc = CFGMR3QueryBoolDef(pCurNode, "Discard", &fDiscard, false);
4474 if (RT_FAILURE(rc))
4475 {
4476 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4477 N_("DrvVD: Configuration error: Querying \"Discard\" as boolean failed"));
4478 break;
4479 }
4480 if (fReadOnly && fDiscard)
4481 {
4482 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4483 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"Discard\" are set"));
4484 break;
4485 }
4486 rc = CFGMR3QueryBoolDef(pCurNode, "InformAboutZeroBlocks", &fInformAboutZeroBlocks, false);
4487 if (RT_FAILURE(rc))
4488 {
4489 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4490 N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed"));
4491 break;
4492 }
4493 rc = CFGMR3QueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true);
4494 if (RT_FAILURE(rc))
4495 {
4496 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4497 N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed"));
4498 break;
4499 }
4500
4501 char *psz = NULL;
4502 rc = CFGMR3QueryStringAlloc(pCfg, "Type", &psz);
4503 if (RT_FAILURE(rc))
4504 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the sub type"));
4505 pThis->enmType = drvvdGetMediaTypeFromString(psz);
4506 if (pThis->enmType == PDMMEDIATYPE_ERROR)
4507 {
4508 PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
4509 N_("Unknown type \"%s\""), psz);
4510 MMR3HeapFree(psz);
4511 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
4512 }
4513 MMR3HeapFree(psz); psz = NULL;
4514
4515 rc = CFGMR3QueryStringAlloc(pCurNode, "CachePath", &pszCachePath);
4516 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
4517 {
4518 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4519 N_("DrvVD: Configuration error: Querying \"CachePath\" as string failed"));
4520 break;
4521 }
4522 else
4523 rc = VINF_SUCCESS;
4524
4525 if (pszCachePath)
4526 {
4527 rc = CFGMR3QueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
4528 if (RT_FAILURE(rc))
4529 {
4530 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4531 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
4532 break;
4533 }
4534 }
4535
4536 /* Mountable */
4537 rc = CFGMR3QueryBoolDef(pCfg, "Mountable", &pThis->fMountable, false);
4538 if (RT_FAILURE(rc))
4539 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Mountable\" from the config"));
4540
4541 /* Locked */
4542 rc = CFGMR3QueryBoolDef(pCfg, "Locked", &pThis->fLocked, false);
4543 if (RT_FAILURE(rc))
4544 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Locked\" from the config"));
4545
4546 /* BIOS visible */
4547 rc = CFGMR3QueryBoolDef(pCfg, "BIOSVisible", &pThis->fBiosVisible, true);
4548 if (RT_FAILURE(rc))
4549 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"BIOSVisible\" from the config"));
4550
4551 /* Cylinders */
4552 rc = CFGMR3QueryU32Def(pCfg, "Cylinders", &pThis->LCHSGeometry.cCylinders, 0);
4553 if (RT_FAILURE(rc))
4554 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Cylinders\" from the config"));
4555
4556 /* Heads */
4557 rc = CFGMR3QueryU32Def(pCfg, "Heads", &pThis->LCHSGeometry.cHeads, 0);
4558 if (RT_FAILURE(rc))
4559 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Heads\" from the config"));
4560
4561 /* Sectors */
4562 rc = CFGMR3QueryU32Def(pCfg, "Sectors", &pThis->LCHSGeometry.cSectors, 0);
4563 if (RT_FAILURE(rc))
4564 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Sectors\" from the config"));
4565
4566 /* Uuid */
4567 rc = CFGMR3QueryStringAlloc(pCfg, "Uuid", &psz);
4568 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4569 RTUuidClear(&pThis->Uuid);
4570 else if (RT_SUCCESS(rc))
4571 {
4572 rc = RTUuidFromStr(&pThis->Uuid, psz);
4573 if (RT_FAILURE(rc))
4574 {
4575 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Uuid from string failed on \"%s\""), psz);
4576 MMR3HeapFree(psz);
4577 return rc;
4578 }
4579 MMR3HeapFree(psz); psz = NULL;
4580 }
4581 else
4582 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Uuid\" from the config"));
4583
4584#ifdef VBOX_PERIODIC_FLUSH
4585 rc = CFGMR3QueryU32Def(pCfg, "FlushInterval", &pThis->cbFlushInterval, 0);
4586 if (RT_FAILURE(rc))
4587 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"FlushInterval\" from the config"));
4588#endif /* VBOX_PERIODIC_FLUSH */
4589
4590#ifdef VBOX_IGNORE_FLUSH
4591 rc = CFGMR3QueryBoolDef(pCfg, "IgnoreFlush", &pThis->fIgnoreFlush, true);
4592 if (RT_FAILURE(rc))
4593 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlush\" from the config"));
4594
4595 if (pThis->fIgnoreFlush)
4596 LogRel(("DrvVD: Flushes will be ignored\n"));
4597 else
4598 LogRel(("DrvVD: Flushes will be passed to the disk\n"));
4599
4600 rc = CFGMR3QueryBoolDef(pCfg, "IgnoreFlushAsync", &pThis->fIgnoreFlushAsync, false);
4601 if (RT_FAILURE(rc))
4602 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlushAsync\" from the config"));
4603
4604 if (pThis->fIgnoreFlushAsync)
4605 LogRel(("DrvVD: Async flushes will be ignored\n"));
4606 else
4607 LogRel(("DrvVD: Async flushes will be passed to the disk\n"));
4608#endif /* VBOX_IGNORE_FLUSH */
4609
4610 rc = CFGMR3QueryBoolDef(pCurNode, "EmptyDrive", &fEmptyDrive, false);
4611 if (RT_FAILURE(rc))
4612 {
4613 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4614 N_("DrvVD: Configuration error: Querying \"EmptyDrive\" as boolean failed"));
4615 break;
4616 }
4617
4618 rc = CFGMR3QueryU32Def(pCfg, "IoBufMax", &cbIoBufMax, 5 * _1M);
4619 if (RT_FAILURE(rc))
4620 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IoBufMax\" from the config"));
4621 }
4622
4623 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
4624 if (!pParent)
4625 break;
4626 pCurNode = pParent;
4627 iLevel++;
4628 }
4629
4630 if (pThis->pDrvMediaExPort)
4631 rc = IOBUFMgrCreate(&pThis->hIoBufMgr, cbIoBufMax, pThis->pCfgCrypto ? IOBUFMGR_F_REQUIRE_NOT_PAGABLE : IOBUFMGR_F_DEFAULT);
4632
4633 if ( !fEmptyDrive
4634 && RT_SUCCESS(rc))
4635 {
4636 /*
4637 * Create the image container and the necessary interfaces.
4638 */
4639 if (RT_SUCCESS(rc))
4640 {
4641 /*
4642 * The image has a bandwidth group but the host cache is enabled.
4643 * Use the async I/O framework but tell it to enable the host cache.
4644 */
4645 if (!fUseNewIo && pThis->pszBwGroup)
4646 {
4647 pThis->fAsyncIoWithHostCache = true;
4648 fUseNewIo = true;
4649 }
4650
4651 /** @todo quick hack to work around problems in the async I/O
4652 * implementation (rw semaphore thread ownership problem)
4653 * while a merge is running. Remove once this is fixed. */
4654 if (pThis->fMergePending)
4655 fUseNewIo = false;
4656
4657 if (RT_SUCCESS(rc) && pThis->fMergePending)
4658 {
4659 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
4660 if (RT_SUCCESS(rc))
4661 rc = RTSemRWCreate(&pThis->MergeLock);
4662 if (RT_SUCCESS(rc))
4663 {
4664 pThis->VDIfThreadSync.pfnStartRead = drvvdThreadStartRead;
4665 pThis->VDIfThreadSync.pfnFinishRead = drvvdThreadFinishRead;
4666 pThis->VDIfThreadSync.pfnStartWrite = drvvdThreadStartWrite;
4667 pThis->VDIfThreadSync.pfnFinishWrite = drvvdThreadFinishWrite;
4668
4669 rc = VDInterfaceAdd(&pThis->VDIfThreadSync.Core, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
4670 pThis, sizeof(VDINTERFACETHREADSYNC), &pThis->pVDIfsDisk);
4671 }
4672 else
4673 {
4674 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4675 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
4676 }
4677 }
4678
4679 if (RT_SUCCESS(rc))
4680 {
4681 rc = VDCreate(pThis->pVDIfsDisk, drvvdGetVDFromMediaType(pThis->enmType), &pThis->pDisk);
4682 /* Error message is already set correctly. */
4683 }
4684 }
4685
4686 if (pThis->pDrvMediaAsyncPort && fUseNewIo)
4687 pThis->fAsyncIOSupported = true;
4688
4689 uint64_t tsStart = RTTimeNanoTS();
4690
4691 unsigned iImageIdx = 0;
4692 while (pCurNode && RT_SUCCESS(rc))
4693 {
4694 /* Allocate per-image data. */
4695 PVBOXIMAGE pImage = drvvdNewImage(pThis);
4696 if (!pImage)
4697 {
4698 rc = VERR_NO_MEMORY;
4699 break;
4700 }
4701
4702 /*
4703 * Read the image configuration.
4704 */
4705 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
4706 if (RT_FAILURE(rc))
4707 {
4708 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4709 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
4710 break;
4711 }
4712
4713 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
4714 if (RT_FAILURE(rc))
4715 {
4716 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4717 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
4718 break;
4719 }
4720
4721 bool fMergeSource;
4722 rc = CFGMR3QueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
4723 if (RT_FAILURE(rc))
4724 {
4725 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4726 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
4727 break;
4728 }
4729 if (fMergeSource)
4730 {
4731 if (pThis->uMergeSource == VD_LAST_IMAGE)
4732 pThis->uMergeSource = iImageIdx;
4733 else
4734 {
4735 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4736 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
4737 break;
4738 }
4739 }
4740
4741 bool fMergeTarget;
4742 rc = CFGMR3QueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
4743 if (RT_FAILURE(rc))
4744 {
4745 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4746 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
4747 break;
4748 }
4749 if (fMergeTarget)
4750 {
4751 if (pThis->uMergeTarget == VD_LAST_IMAGE)
4752 pThis->uMergeTarget = iImageIdx;
4753 else
4754 {
4755 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4756 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
4757 break;
4758 }
4759 }
4760
4761 PCFGMNODE pCfgVDConfig = CFGMR3GetChild(pCurNode, "VDConfig");
4762 pImage->VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
4763 pImage->VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
4764 pImage->VDIfConfig.pfnQuery = drvvdCfgQuery;
4765 pImage->VDIfConfig.pfnQueryBytes = NULL;
4766 rc = VDInterfaceAdd(&pImage->VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
4767 pCfgVDConfig, sizeof(VDINTERFACECONFIG), &pImage->pVDIfsImage);
4768 AssertRC(rc);
4769
4770 /* Check VDConfig for encryption config. */
4771 if (pCfgVDConfig)
4772 pThis->pCfgCrypto = CFGMR3GetChild(pCfgVDConfig, "CRYPT");
4773
4774 if (pThis->pCfgCrypto)
4775 {
4776 /* Setup VDConfig interface for disk encryption support. */
4777 pThis->VDIfCfg.pfnAreKeysValid = drvvdCfgAreKeysValid;
4778 pThis->VDIfCfg.pfnQuerySize = drvvdCfgQuerySize;
4779 pThis->VDIfCfg.pfnQuery = drvvdCfgQuery;
4780 pThis->VDIfCfg.pfnQueryBytes = NULL;
4781
4782 pThis->VDIfCrypto.pfnKeyRetain = drvvdCryptoKeyRetain;
4783 pThis->VDIfCrypto.pfnKeyRelease = drvvdCryptoKeyRelease;
4784 pThis->VDIfCrypto.pfnKeyStorePasswordRetain = drvvdCryptoKeyStorePasswordRetain;
4785 pThis->VDIfCrypto.pfnKeyStorePasswordRelease = drvvdCryptoKeyStorePasswordRelease;
4786 }
4787
4788 /* Unconditionally insert the TCPNET interface, don't bother to check
4789 * if an image really needs it. Will be ignored. Since the TCPNET
4790 * interface is per image we could make this more flexible in the
4791 * future if we want to. */
4792 /* Construct TCPNET callback table depending on the config. This is
4793 * done unconditionally, as uninterested backends will ignore it. */
4794 if (fHostIP)
4795 {
4796 pImage->VDIfTcpNet.pfnSocketCreate = drvvdTcpSocketCreate;
4797 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdTcpSocketDestroy;
4798 pImage->VDIfTcpNet.pfnClientConnect = drvvdTcpClientConnect;
4799 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdTcpIsClientConnected;
4800 pImage->VDIfTcpNet.pfnClientClose = drvvdTcpClientClose;
4801 pImage->VDIfTcpNet.pfnSelectOne = drvvdTcpSelectOne;
4802 pImage->VDIfTcpNet.pfnRead = drvvdTcpRead;
4803 pImage->VDIfTcpNet.pfnWrite = drvvdTcpWrite;
4804 pImage->VDIfTcpNet.pfnSgWrite = drvvdTcpSgWrite;
4805 pImage->VDIfTcpNet.pfnReadNB = drvvdTcpReadNB;
4806 pImage->VDIfTcpNet.pfnWriteNB = drvvdTcpWriteNB;
4807 pImage->VDIfTcpNet.pfnSgWriteNB = drvvdTcpSgWriteNB;
4808 pImage->VDIfTcpNet.pfnFlush = drvvdTcpFlush;
4809 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdTcpSetSendCoalescing;
4810 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdTcpGetLocalAddress;
4811 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdTcpGetPeerAddress;
4812
4813 /*
4814 * There is a 15ms delay between receiving the data and marking the socket
4815 * as readable on Windows XP which hurts async I/O performance of
4816 * TCP backends badly. Provide a different select method without
4817 * using poll on XP.
4818 * This is only used on XP because it is not as efficient as the one using poll
4819 * and all other Windows versions are working fine.
4820 */
4821 char szOS[64];
4822 memset(szOS, 0, sizeof(szOS));
4823 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, &szOS[0], sizeof(szOS));
4824
4825 if (RT_SUCCESS(rc) && !strncmp(szOS, "Windows XP", 10))
4826 {
4827 LogRel(("VD: Detected Windows XP, disabled poll based waiting for TCP\n"));
4828 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdTcpSelectOneExNoPoll;
4829 }
4830 else
4831 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdTcpSelectOneExPoll;
4832
4833 pImage->VDIfTcpNet.pfnPoke = drvvdTcpPoke;
4834 }
4835 else
4836 {
4837#ifndef VBOX_WITH_INIP
4838 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
4839 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
4840#else /* VBOX_WITH_INIP */
4841 pImage->VDIfTcpNet.pfnSocketCreate = drvvdINIPSocketCreate;
4842 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdINIPSocketDestroy;
4843 pImage->VDIfTcpNet.pfnClientConnect = drvvdINIPClientConnect;
4844 pImage->VDIfTcpNet.pfnClientClose = drvvdINIPClientClose;
4845 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdINIPIsClientConnected;
4846 pImage->VDIfTcpNet.pfnSelectOne = drvvdINIPSelectOne;
4847 pImage->VDIfTcpNet.pfnRead = drvvdINIPRead;
4848 pImage->VDIfTcpNet.pfnWrite = drvvdINIPWrite;
4849 pImage->VDIfTcpNet.pfnSgWrite = drvvdINIPSgWrite;
4850 pImage->VDIfTcpNet.pfnFlush = drvvdINIPFlush;
4851 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
4852 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
4853 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
4854 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdINIPSelectOneEx;
4855 pImage->VDIfTcpNet.pfnPoke = drvvdINIPPoke;
4856#endif /* VBOX_WITH_INIP */
4857 }
4858 rc = VDInterfaceAdd(&pImage->VDIfTcpNet.Core, "DrvVD_TCPNET",
4859 VDINTERFACETYPE_TCPNET, NULL,
4860 sizeof(VDINTERFACETCPNET), &pImage->pVDIfsImage);
4861 AssertRC(rc);
4862
4863 /* Insert the custom I/O interface only if we're told to use new IO.
4864 * Since the I/O interface is per image we could make this more
4865 * flexible in the future if we want to. */
4866 if (fUseNewIo)
4867 {
4868#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
4869 pImage->VDIfIo.pfnOpen = drvvdAsyncIOOpen;
4870 pImage->VDIfIo.pfnClose = drvvdAsyncIOClose;
4871 pImage->VDIfIo.pfnGetSize = drvvdAsyncIOGetSize;
4872 pImage->VDIfIo.pfnSetSize = drvvdAsyncIOSetSize;
4873 pImage->VDIfIo.pfnSetAllocationSize = drvvdAsyncIOSetAllocationSize;
4874 pImage->VDIfIo.pfnReadSync = drvvdAsyncIOReadSync;
4875 pImage->VDIfIo.pfnWriteSync = drvvdAsyncIOWriteSync;
4876 pImage->VDIfIo.pfnFlushSync = drvvdAsyncIOFlushSync;
4877 pImage->VDIfIo.pfnReadAsync = drvvdAsyncIOReadAsync;
4878 pImage->VDIfIo.pfnWriteAsync = drvvdAsyncIOWriteAsync;
4879 pImage->VDIfIo.pfnFlushAsync = drvvdAsyncIOFlushAsync;
4880#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
4881 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
4882 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
4883#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
4884 if (RT_SUCCESS(rc))
4885 rc = VDInterfaceAdd(&pImage->VDIfIo.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
4886 pThis, sizeof(VDINTERFACEIO), &pImage->pVDIfsImage);
4887 AssertRC(rc);
4888 }
4889
4890 /*
4891 * Open the image.
4892 */
4893 unsigned uOpenFlags;
4894 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
4895 uOpenFlags = VD_OPEN_FLAGS_READONLY;
4896 else
4897 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
4898 if (fHonorZeroWrites)
4899 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
4900 if (pThis->fAsyncIOSupported)
4901 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
4902 if (pThis->fShareable)
4903 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
4904 if (fDiscard && iLevel == 0)
4905 uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
4906 if (fInformAboutZeroBlocks)
4907 uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS;
4908 if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY)
4909 && fSkipConsistencyChecks)
4910 uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
4911
4912 /* Try to open backend in async I/O mode first. */
4913 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
4914 if (rc == VERR_NOT_SUPPORTED)
4915 {
4916 pThis->fAsyncIOSupported = false;
4917 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
4918 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
4919 }
4920
4921 if (rc == VERR_VD_DISCARD_NOT_SUPPORTED)
4922 {
4923 fDiscard = false;
4924 uOpenFlags &= ~VD_OPEN_FLAGS_DISCARD;
4925 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
4926 }
4927
4928 if (!fDiscard)
4929 {
4930 pThis->IMedia.pfnDiscard = NULL;
4931 pThis->IMediaAsync.pfnStartDiscard = NULL;
4932 }
4933
4934 if (RT_SUCCESS(rc))
4935 {
4936 LogFunc(("%d - Opened '%s' in %s mode\n",
4937 iLevel, pszName,
4938 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
4939 if ( VDIsReadOnly(pThis->pDisk)
4940 && !fReadOnly
4941 && !fMaybeReadOnly
4942 && !pThis->fTempReadOnly
4943 && iLevel == 0)
4944 {
4945 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
4946 N_("Failed to open image '%s' for writing due to wrong permissions"),
4947 pszName);
4948 break;
4949 }
4950 }
4951 else
4952 {
4953 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
4954 N_("Failed to open image '%s' in %s mode"), pszName,
4955 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write");
4956 break;
4957 }
4958
4959 MMR3HeapFree(pszName);
4960 pszName = NULL;
4961 MMR3HeapFree(pszFormat);
4962 pszFormat = NULL;
4963
4964 /* next */
4965 iLevel--;
4966 iImageIdx++;
4967 pCurNode = CFGMR3GetParent(pCurNode);
4968 }
4969
4970 LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart));
4971
4972 /* Open the cache image if set. */
4973 if ( RT_SUCCESS(rc)
4974 && RT_VALID_PTR(pszCachePath))
4975 {
4976 /* Insert the custom I/O interface only if we're told to use new IO.
4977 * Since the I/O interface is per image we could make this more
4978 * flexible in the future if we want to. */
4979 if (fUseNewIo)
4980 {
4981#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
4982 pThis->VDIfIoCache.pfnOpen = drvvdAsyncIOOpen;
4983 pThis->VDIfIoCache.pfnClose = drvvdAsyncIOClose;
4984 pThis->VDIfIoCache.pfnGetSize = drvvdAsyncIOGetSize;
4985 pThis->VDIfIoCache.pfnSetSize = drvvdAsyncIOSetSize;
4986 pThis->VDIfIoCache.pfnReadSync = drvvdAsyncIOReadSync;
4987 pThis->VDIfIoCache.pfnWriteSync = drvvdAsyncIOWriteSync;
4988 pThis->VDIfIoCache.pfnFlushSync = drvvdAsyncIOFlushSync;
4989 pThis->VDIfIoCache.pfnReadAsync = drvvdAsyncIOReadAsync;
4990 pThis->VDIfIoCache.pfnWriteAsync = drvvdAsyncIOWriteAsync;
4991 pThis->VDIfIoCache.pfnFlushAsync = drvvdAsyncIOFlushAsync;
4992#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
4993 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
4994 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
4995#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
4996 if (RT_SUCCESS(rc))
4997 rc = VDInterfaceAdd(&pThis->VDIfIoCache.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
4998 pThis, sizeof(VDINTERFACEIO), &pThis->pVDIfsCache);
4999 AssertRC(rc);
5000 }
5001
5002 rc = VDCacheOpen(pThis->pDisk, pszCacheFormat, pszCachePath, VD_OPEN_FLAGS_NORMAL, pThis->pVDIfsCache);
5003 if (RT_FAILURE(rc))
5004 rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
5005 }
5006
5007 if (RT_VALID_PTR(pszCachePath))
5008 MMR3HeapFree(pszCachePath);
5009 if (RT_VALID_PTR(pszCacheFormat))
5010 MMR3HeapFree(pszCacheFormat);
5011
5012 if ( RT_SUCCESS(rc)
5013 && pThis->fMergePending
5014 && ( pThis->uMergeSource == VD_LAST_IMAGE
5015 || pThis->uMergeTarget == VD_LAST_IMAGE))
5016 {
5017 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5018 N_("DrvVD: Configuration error: Inconsistent image merge data"));
5019 }
5020
5021 /* Create the block cache if enabled. */
5022 if ( fUseBlockCache
5023 && !pThis->fShareable
5024 && !fDiscard
5025 && !pThis->pCfgCrypto /* Disk encryption disables the block cache for security reasons */
5026 && RT_SUCCESS(rc))
5027 {
5028 /*
5029 * We need a unique ID for the block cache (to identify the owner of data
5030 * blocks in a saved state). UUIDs are not really suitable because
5031 * there are image formats which don't support them. Furthermore it is
5032 * possible that a new diff image was attached after a saved state
5033 * which changes the UUID.
5034 * However the device "name + device instance + LUN" triple the disk is
5035 * attached to is always constant for saved states.
5036 */
5037 char *pszId = NULL;
5038 uint32_t iInstance, iLUN;
5039 const char *pcszController;
5040
5041 rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController,
5042 &iInstance, &iLUN);
5043 if (RT_FAILURE(rc))
5044 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5045 N_("DrvVD: Configuration error: Could not query device data"));
5046 else
5047 {
5048 int cbStr = RTStrAPrintf(&pszId, "%s-%d-%d", pcszController, iInstance, iLUN);
5049
5050 if (cbStr > 0)
5051 {
5052 rc = PDMDrvHlpBlkCacheRetain(pDrvIns, &pThis->pBlkCache,
5053 drvvdBlkCacheXferComplete,
5054 drvvdBlkCacheXferEnqueue,
5055 drvvdBlkCacheXferEnqueueDiscard,
5056 pszId);
5057 if (rc == VERR_NOT_SUPPORTED)
5058 {
5059 LogRel(("VD: Block cache is not supported\n"));
5060 rc = VINF_SUCCESS;
5061 }
5062 else
5063 AssertRC(rc);
5064
5065 RTStrFree(pszId);
5066 }
5067 else
5068 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5069 N_("DrvVD: Out of memory when creating block cache"));
5070 }
5071 }
5072
5073 if (RT_SUCCESS(rc))
5074 rc = drvvdSetupFilters(pThis, pCfg);
5075
5076 /*
5077 * Register a load-done callback so we can undo TempReadOnly config before
5078 * we get to drvvdResume. Autoamtically deregistered upon destruction.
5079 */
5080 if (RT_SUCCESS(rc))
5081 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
5082 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
5083 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
5084 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
5085
5086 /* Setup the boot acceleration stuff if enabled. */
5087 if (RT_SUCCESS(rc) && pThis->fBootAccelEnabled)
5088 {
5089 pThis->cbDisk = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
5090 Assert(pThis->cbDisk > 0);
5091 pThis->pbData = (uint8_t *)RTMemAllocZ(pThis->cbBootAccelBuffer);
5092 if (pThis->pbData)
5093 {
5094 pThis->fBootAccelActive = true;
5095 pThis->offDisk = 0;
5096 pThis->cbDataValid = 0;
5097 LogRel(("VD: Boot acceleration enabled\n"));
5098 }
5099 else
5100 LogRel(("VD: Boot acceleration, out of memory, disabled\n"));
5101 }
5102
5103 if ( RTUuidIsNull(&pThis->Uuid)
5104 && pThis->enmType == PDMMEDIATYPE_HARD_DISK)
5105 VDGetUuid(pThis->pDisk, 0, &pThis->Uuid);
5106
5107 /*
5108 * Automatically upgrade the floppy drive if the specified one is too
5109 * small to represent the whole boot time image. (We cannot do this later
5110 * since the BIOS (and others) gets the info via CMOS.)
5111 *
5112 * This trick should make 2.88 images as well as the fake 15.6 and 63.5 MB
5113 * images despite the hardcoded default 1.44 drive.
5114 */
5115 if ( PDMMEDIATYPE_IS_FLOPPY(pThis->enmType)
5116 && pThis->pDisk)
5117 {
5118 uint64_t const cbFloppyImg = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
5119 PDMMEDIATYPE const enmCfgType = pThis->enmType;
5120 switch (enmCfgType)
5121 {
5122 default:
5123 AssertFailed();
5124 case PDMMEDIATYPE_FLOPPY_360:
5125 if (cbFloppyImg > 40 * 2 * 9 * 512)
5126 pThis->enmType = PDMMEDIATYPE_FLOPPY_720;
5127 /* fall thru */
5128 case PDMMEDIATYPE_FLOPPY_720:
5129 if (cbFloppyImg > 80 * 2 * 14 * 512)
5130 pThis->enmType = PDMMEDIATYPE_FLOPPY_1_20;
5131 /* fall thru */
5132 case PDMMEDIATYPE_FLOPPY_1_20:
5133 if (cbFloppyImg > 80 * 2 * 20 * 512)
5134 pThis->enmType = PDMMEDIATYPE_FLOPPY_1_44;
5135 /* fall thru */
5136 case PDMMEDIATYPE_FLOPPY_1_44:
5137 if (cbFloppyImg > 80 * 2 * 24 * 512)
5138 pThis->enmType = PDMMEDIATYPE_FLOPPY_2_88;
5139 /* fall thru */
5140 case PDMMEDIATYPE_FLOPPY_2_88:
5141 if (cbFloppyImg > 80 * 2 * 48 * 512)
5142 pThis->enmType = PDMMEDIATYPE_FLOPPY_FAKE_15_6;
5143 /* fall thru */
5144 case PDMMEDIATYPE_FLOPPY_FAKE_15_6:
5145 if (cbFloppyImg > 255 * 2 * 63 * 512)
5146 pThis->enmType = PDMMEDIATYPE_FLOPPY_FAKE_63_5;
5147 case PDMMEDIATYPE_FLOPPY_FAKE_63_5:
5148 if (cbFloppyImg > 255 * 2 * 255 * 512)
5149 LogRel(("Warning: Floppy image is larger that 63.5 MB! (%llu bytes)\n", cbFloppyImg));
5150 break;
5151 }
5152 if (pThis->enmType != enmCfgType)
5153 LogRel(("DrvVD: Automatically upgraded floppy drive from %s to %s to better support the %u byte image\n",
5154 drvvdGetTypeName(enmCfgType), drvvdGetTypeName(pThis->enmType), cbFloppyImg));
5155 }
5156 } /* !fEmptyDrive */
5157
5158 if (RT_FAILURE(rc))
5159 {
5160 if (RT_VALID_PTR(pszName))
5161 MMR3HeapFree(pszName);
5162 if (RT_VALID_PTR(pszFormat))
5163 MMR3HeapFree(pszFormat);
5164 /* drvvdDestruct does the rest. */
5165 }
5166
5167 LogFlowFunc(("returns %Rrc\n", rc));
5168 return rc;
5169}
5170
5171/**
5172 * VBox disk container media driver registration record.
5173 */
5174const PDMDRVREG g_DrvVD =
5175{
5176 /* u32Version */
5177 PDM_DRVREG_VERSION,
5178 /* szName */
5179 "VD",
5180 /* szRCMod */
5181 "",
5182 /* szR0Mod */
5183 "",
5184 /* pszDescription */
5185 "Generic VBox disk media driver.",
5186 /* fFlags */
5187 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5188 /* fClass. */
5189 PDM_DRVREG_CLASS_MEDIA,
5190 /* cMaxInstances */
5191 ~0U,
5192 /* cbInstance */
5193 sizeof(VBOXDISK),
5194 /* pfnConstruct */
5195 drvvdConstruct,
5196 /* pfnDestruct */
5197 drvvdDestruct,
5198 /* pfnRelocate */
5199 NULL,
5200 /* pfnIOCtl */
5201 NULL,
5202 /* pfnPowerOn */
5203 drvvdPowerOn,
5204 /* pfnReset */
5205 drvvdReset,
5206 /* pfnSuspend */
5207 drvvdSuspend,
5208 /* pfnResume */
5209 drvvdResume,
5210 /* pfnAttach */
5211 NULL,
5212 /* pfnDetach */
5213 NULL,
5214 /* pfnPowerOff */
5215 drvvdPowerOff,
5216 /* pfnSoftReset */
5217 NULL,
5218 /* u32EndVersion */
5219 PDM_DRVREG_VERSION
5220};
5221
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