VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Miniport/VBoxVideoHGSMI.cpp@ 33022

Last change on this file since 33022 was 33022, checked in by vboxsync, 15 years ago

Additions/WINNT/Graphics: more refactoring

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.3 KB
Line 
1/* $Id: VBoxVideoHGSMI.cpp 33022 2010-10-09 16:01:01Z vboxsync $ */
2/** @file
3 * VirtualBox Video miniport driver for NT/2k/XP - HGSMI related functions.
4 */
5
6/*
7 * Copyright (C) 2006-2009 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#include "VBoxVideo.h"
20#include "Helper.h"
21
22#include <iprt/asm.h>
23#include <iprt/log.h>
24#include <iprt/thread.h>
25#include <VBox/VMMDev.h>
26#include <VBox/VBoxGuest.h>
27#include <VBox/VBoxVideo.h>
28
29#include <VBoxDisplay.h>
30
31#include "vboxioctl.h"
32
33#define MEM_TAG 'HVBV'
34
35void HGSMINotifyHostCmdComplete (PVBOXVIDEO_COMMON pCommon, HGSMIOFFSET offt)
36{
37 VBoxHGSMIHostWrite(pCommon, offt);
38}
39
40void HGSMIClearIrq (PVBOXVIDEO_COMMON pCommon)
41{
42 VBoxHGSMIHostWrite(pCommon, HGSMIOFFSET_VOID);
43}
44
45static void HGSMIHostCmdComplete (PVBOXVIDEO_COMMON pCommon, void * pvMem)
46{
47 HGSMIOFFSET offMem = HGSMIPointerToOffset (&pCommon->areaHostHeap, HGSMIBufferHeaderFromData (pvMem));
48 Assert(offMem != HGSMIOFFSET_VOID);
49 if(offMem != HGSMIOFFSET_VOID)
50 {
51 HGSMINotifyHostCmdComplete (pCommon, offMem);
52 }
53}
54
55static void hgsmiHostCmdProcess(PVBOXVIDEO_COMMON pCommon, HGSMIOFFSET offBuffer)
56{
57 int rc = HGSMIBufferProcess (&pCommon->areaHostHeap,
58 &pCommon->channels,
59 offBuffer);
60 Assert(!RT_FAILURE(rc));
61 if(RT_FAILURE(rc))
62 {
63 /* failure means the command was not submitted to the handler for some reason
64 * it's our responsibility to notify its completion in this case */
65 HGSMINotifyHostCmdComplete(pCommon, offBuffer);
66 }
67 /* if the cmd succeeded it's responsibility of the callback to complete it */
68}
69
70static HGSMIOFFSET hgsmiGetHostBuffer (PVBOXVIDEO_COMMON pCommon)
71{
72 return VBoxHGSMIHostRead(pCommon);
73}
74
75static void hgsmiHostCommandQueryProcess (PVBOXVIDEO_COMMON pCommon)
76{
77 HGSMIOFFSET offset = hgsmiGetHostBuffer (pCommon);
78 Assert(offset != HGSMIOFFSET_VOID);
79 if(offset != HGSMIOFFSET_VOID)
80 {
81 hgsmiHostCmdProcess(pCommon, offset);
82 }
83}
84
85#define VBOX_HGSMI_LOCK(_pe, _plock, _dpc, _pold) \
86 do { \
87 if(_dpc) \
88 { \
89 VBoxVideoCmnSpinLockAcquireAtDpcLevel(_pe, _plock); \
90 } \
91 else \
92 {\
93 VBoxVideoCmnSpinLockAcquire(_pe, _plock, _pold); \
94 }\
95 } while(0)
96
97#define VBOX_HGSMI_UNLOCK(_pe, _plock, _dpc, _pold) \
98 do { \
99 if(_dpc) \
100 { \
101 VBoxVideoCmnSpinLockReleaseFromDpcLevel(_pe, _plock); \
102 } \
103 else \
104 {\
105 VBoxVideoCmnSpinLockRelease(_pe, _plock, _pold); \
106 }\
107 } while(0)
108
109VOID VBoxVideoHGSMIDpc(
110 IN PVOID HwDeviceExtension,
111 IN PVOID Context
112 )
113{
114 PDEVICE_EXTENSION PrimaryExtension = (PDEVICE_EXTENSION)HwDeviceExtension;
115 uint32_t flags = (uint32_t)Context;
116 bool bProcessing = false;
117 VBOXVCMNIRQL OldIrql;
118 /* we check if another thread is processing the queue and exit if so */
119 do
120 {
121 bool bLock = false;
122 if(!(commonFromDeviceExt(PrimaryExtension)->pHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING))
123 {
124 if(!bProcessing)
125 {
126 break;
127 }
128 VBOX_HGSMI_LOCK(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock, flags, &OldIrql);
129 if(!(commonFromDeviceExt(PrimaryExtension)->pHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING))
130 {
131 Assert(commonFromDeviceExt(PrimaryExtension)->bHostCmdProcessing);
132 commonFromDeviceExt(PrimaryExtension)->bHostCmdProcessing = false;
133 VBOX_HGSMI_UNLOCK(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock, flags, OldIrql);
134 break;
135 }
136 VBOX_HGSMI_UNLOCK(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock, flags, OldIrql);
137 }
138 else
139 {
140 if(!bProcessing)
141 {
142 VBOX_HGSMI_LOCK(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock, flags, &OldIrql);
143 if(!(commonFromDeviceExt(PrimaryExtension)->pHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING)
144 || commonFromDeviceExt(PrimaryExtension)->bHostCmdProcessing)
145 {
146 VBOX_HGSMI_UNLOCK(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock, flags, OldIrql);
147 break;
148 }
149 Assert(!commonFromDeviceExt(PrimaryExtension)->bHostCmdProcessing);
150 commonFromDeviceExt(PrimaryExtension)->bHostCmdProcessing = true;
151 VBOX_HGSMI_UNLOCK(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock, flags, OldIrql);
152 bProcessing = true;
153 }
154 }
155
156 Assert(bProcessing);
157 Assert(commonFromDeviceExt(PrimaryExtension)->bHostCmdProcessing);
158 Assert((commonFromDeviceExt(PrimaryExtension)->pHostFlags->u32HostFlags & HGSMIHOSTFLAGS_COMMANDS_PENDING) != 0);
159 bProcessing = true;
160
161 hgsmiHostCommandQueryProcess (commonFromDeviceExt(PrimaryExtension));
162 } while(true/*!PrimaryExtension->u.primary.bPollingStop*/);
163}
164
165/* Detect whether HGSMI is supported by the host. */
166BOOLEAN VBoxHGSMIIsSupported (void)
167{
168 USHORT DispiId;
169
170 VBoxVideoCmnPortWriteUshort((PUSHORT)VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
171 VBoxVideoCmnPortWriteUshort((PUSHORT)VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI);
172
173 DispiId = VBoxVideoCmnPortReadUshort((PUSHORT)VBE_DISPI_IOPORT_DATA);
174
175 return (DispiId == VBE_DISPI_ID_HGSMI);
176}
177
178typedef int FNHGSMICALLINIT (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData);
179typedef FNHGSMICALLINIT *PFNHGSMICALLINIT;
180
181typedef int FNHGSMICALLFINALIZE (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData);
182typedef FNHGSMICALLFINALIZE *PFNHGSMICALLFINALIZE;
183
184void* vboxHGSMIBufferAlloc(PVBOXVIDEO_COMMON pCommon,
185 HGSMISIZE cbData,
186 uint8_t u8Ch,
187 uint16_t u16Op)
188{
189#ifdef VBOX_WITH_WDDM
190 /* @todo: add synchronization */
191#endif
192 return HGSMIHeapAlloc (&pCommon->hgsmiAdapterHeap, cbData, u8Ch, u16Op);
193}
194
195void vboxHGSMIBufferFree (PVBOXVIDEO_COMMON pCommon, void *pvBuffer)
196{
197#ifdef VBOX_WITH_WDDM
198 /* @todo: add synchronization */
199#endif
200 HGSMIHeapFree (&pCommon->hgsmiAdapterHeap, pvBuffer);
201}
202
203int vboxHGSMIBufferSubmit (PVBOXVIDEO_COMMON pCommon, void *pvBuffer)
204{
205 /* Initialize the buffer and get the offset for port IO. */
206 HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&pCommon->hgsmiAdapterHeap, pvBuffer);
207
208 Assert(offBuffer != HGSMIOFFSET_VOID);
209 if (offBuffer != HGSMIOFFSET_VOID)
210 {
211 /* Submit the buffer to the host. */
212 VBoxHGSMIGuestWrite(pCommon, offBuffer);
213 return VINF_SUCCESS;
214 }
215
216 return VERR_INVALID_PARAMETER;
217}
218
219static int vboxCallChannel (PDEVICE_EXTENSION PrimaryExtension,
220 uint8_t u8Ch,
221 uint16_t u16Op,
222 HGSMISIZE cbData,
223 PFNHGSMICALLINIT pfnInit,
224 PFNHGSMICALLFINALIZE pfnFinalize,
225 void *pvContext)
226{
227 int rc = VINF_SUCCESS;
228
229 /* Allocate the IO buffer. */
230#ifndef VBOX_WITH_WDDM
231 if (PrimaryExtension->pPrimary != PrimaryExtension)
232 {
233 dprintf(("VBoxVideo::vboxCallChannel: not primary extension %p!!!\n", PrimaryExtension));
234 return VERR_INVALID_PARAMETER;
235 }
236#endif
237
238 void *p = HGSMIHeapAlloc (&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap, cbData, u8Ch, u16Op);
239
240 if (!p)
241 {
242 rc = VERR_NO_MEMORY;
243 }
244 else
245 {
246 /* Prepare data to be sent to the host. */
247 if (pfnInit)
248 {
249 rc = pfnInit (PrimaryExtension, pvContext, p);
250 }
251
252 if (RT_SUCCESS (rc))
253 {
254 /* Initialize the buffer and get the offset for port IO. */
255 HGSMIOFFSET offBuffer = HGSMIHeapBufferOffset (&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap,
256 p);
257
258 /* Submit the buffer to the host. */
259 VBoxHGSMIGuestWrite(commonFromDeviceExt(PrimaryExtension), offBuffer);
260
261 if (pfnFinalize)
262 {
263 rc = pfnFinalize (PrimaryExtension, pvContext, p);
264 }
265 }
266 else
267 {
268 AssertFailed ();
269 rc = VERR_INTERNAL_ERROR;
270 }
271
272 /* Free the IO buffer. */
273 HGSMIHeapFree (&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap, p);
274 }
275
276 return rc;
277}
278
279static int vboxCallVBVA (PDEVICE_EXTENSION PrimaryExtension,
280 uint16_t u16Op,
281 HGSMISIZE cbData,
282 PFNHGSMICALLINIT pfnInit,
283 PFNHGSMICALLFINALIZE pfnFinalize,
284 void *pvContext)
285{
286 return vboxCallChannel (PrimaryExtension,
287 HGSMI_CH_VBVA,
288 u16Op,
289 cbData,
290 pfnInit,
291 pfnFinalize,
292 pvContext);
293}
294
295typedef struct _QUERYCONFCTX
296{
297 uint32_t u32Index;
298 ULONG *pulValue;
299} QUERYCONFCTX;
300
301static int vbvaInitQueryConf (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
302{
303 NOREF (PrimaryExtension);
304
305 QUERYCONFCTX *pCtx = (QUERYCONFCTX *)pvContext;
306 VBVACONF32 *p = (VBVACONF32 *)pvData;
307
308 p->u32Index = pCtx->u32Index;
309 p->u32Value = 0;
310
311 return VINF_SUCCESS;
312}
313
314static int vbvaFinalizeQueryConf (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
315{
316 NOREF (PrimaryExtension);
317
318 QUERYCONFCTX *pCtx = (QUERYCONFCTX *)pvContext;
319 VBVACONF32 *p = (VBVACONF32 *)pvData;
320
321 if (pCtx->pulValue)
322 {
323 *pCtx->pulValue = p->u32Value;
324 }
325
326 dprintf(("VBoxVideo::vboxQueryConf: u32Value = %d\n", p->u32Value));
327 return VINF_SUCCESS;
328}
329
330static int vboxQueryConfHGSMI (PDEVICE_EXTENSION PrimaryExtension, uint32_t u32Index, ULONG *pulValue)
331{
332 dprintf(("VBoxVideo::vboxQueryConf: u32Index = %d\n", u32Index));
333
334 QUERYCONFCTX context;
335
336 context.u32Index = u32Index;
337 context.pulValue = pulValue;
338
339 int rc = vboxCallVBVA (PrimaryExtension,
340 VBVA_QUERY_CONF32,
341 sizeof (VBVACONF32),
342 vbvaInitQueryConf,
343 vbvaFinalizeQueryConf,
344 &context);
345
346 dprintf(("VBoxVideo::vboxQueryConf: rc = %d\n", rc));
347
348 return rc;
349}
350#ifndef VBOX_WITH_WDDM
351static int vbvaInitInfoDisplay (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
352{
353 NOREF (pvContext);
354 VBVAINFOVIEW *p = (VBVAINFOVIEW *)pvData;
355
356 int i;
357 PDEVICE_EXTENSION Extension;
358
359 for (i = 0, Extension = PrimaryExtension;
360 i < PrimaryExtension->u.primary.cDisplays && Extension;
361 i++, Extension = Extension->pNext)
362 {
363 p[i].u32ViewIndex = Extension->iDevice;
364 p[i].u32ViewOffset = Extension->ulFrameBufferOffset;
365 p[i].u32ViewSize = PrimaryExtension->u.primary.ulMaxFrameBufferSize;
366
367 /* How much VRAM should be reserved for the guest drivers to use VBVA. */
368 const uint32_t cbReservedVRAM = VBVA_DISPLAY_INFORMATION_SIZE + VBVA_MIN_BUFFER_SIZE;
369
370 p[i].u32MaxScreenSize = p[i].u32ViewSize > cbReservedVRAM?
371 p[i].u32ViewSize - cbReservedVRAM:
372 0;
373 }
374
375 if (i == PrimaryExtension->u.primary.cDisplays && Extension == NULL)
376 {
377 return VINF_SUCCESS;
378 }
379
380 AssertFailed ();
381 return VERR_INTERNAL_ERROR;
382}
383#else
384int vbvaInitInfoCaps (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
385{
386 VBVACAPS *pCaps = (VBVACAPS*)pvData;
387 pCaps->rc = VERR_NOT_IMPLEMENTED;
388 pCaps->fCaps = VBVACAPS_COMPLETEGCMD_BY_IOREAD | VBVACAPS_IRQ;
389 return VINF_SUCCESS;
390}
391
392
393int vbvaFinalizeInfoCaps (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
394{
395 VBVACAPS *pCaps = (VBVACAPS*)pvData;
396 AssertRC(pCaps->rc);
397 return pCaps->rc;
398}
399#endif
400
401static int vbvaInitInfoHeap (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
402{
403 NOREF (pvContext);
404 VBVAINFOHEAP *p = (VBVAINFOHEAP *)pvData;
405
406 p->u32HeapOffset = commonFromDeviceExt(PrimaryExtension)->cbVRAM
407 - commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap
408 - VBVA_ADAPTER_INFORMATION_SIZE;
409 p->u32HeapSize = commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap;
410
411 return VINF_SUCCESS;
412}
413
414static int hgsmiInitFlagsLocation (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
415{
416 NOREF (pvContext);
417 HGSMIBUFFERLOCATION *p = (HGSMIBUFFERLOCATION *)pvData;
418
419 p->offLocation = commonFromDeviceExt(PrimaryExtension)->cbVRAM - sizeof (HGSMIHOSTFLAGS);
420 p->cbLocation = sizeof (HGSMIHOSTFLAGS);
421
422 return VINF_SUCCESS;
423}
424
425
426static int vboxSetupAdapterInfoHGSMI (PDEVICE_EXTENSION PrimaryExtension)
427{
428 dprintf(("VBoxVideo::vboxSetupAdapterInfo\n"));
429
430 /* setup the flags first to ensure they are initialized by the time the host heap is ready */
431 int rc = vboxCallChannel(PrimaryExtension,
432 HGSMI_CH_HGSMI,
433 HGSMI_CC_HOST_FLAGS_LOCATION,
434 sizeof (HGSMIBUFFERLOCATION),
435 hgsmiInitFlagsLocation,
436 NULL,
437 NULL);
438 AssertRC(rc);
439 if(RT_SUCCESS (rc))
440 {
441#ifndef VBOX_WITH_WDDM
442 rc = vboxCallVBVA (PrimaryExtension,
443 VBVA_INFO_VIEW,
444 sizeof (VBVAINFOVIEW) * PrimaryExtension->u.primary.cDisplays,
445 vbvaInitInfoDisplay,
446 NULL,
447 NULL);
448 AssertRC(rc);
449 if (RT_SUCCESS (rc))
450#else
451 /* in case of WDDM we do not control the framebuffer location,
452 * i.e. it is assigned by Video Memory Manager,
453 * The FB information should be passed to guest from our DxgkDdiSetVidPnSourceAddress callback */
454
455 /* Inform about caps */
456 rc = vboxCallVBVA (PrimaryExtension,
457 VBVA_INFO_CAPS,
458 sizeof (VBVACAPS),
459 vbvaInitInfoCaps,
460 vbvaFinalizeInfoCaps,
461 NULL);
462 AssertRC(rc);
463 if (RT_SUCCESS (rc))
464#endif
465 {
466 /* Report the host heap location. */
467 rc = vboxCallVBVA (PrimaryExtension,
468 VBVA_INFO_HEAP,
469 sizeof (VBVAINFOHEAP),
470 vbvaInitInfoHeap,
471 NULL,
472 NULL);
473 AssertRC(rc);
474 }
475 }
476
477
478 dprintf(("VBoxVideo::vboxSetupAdapterInfo finished rc = %d\n", rc));
479
480 return rc;
481}
482
483#ifndef VBOX_WITH_WDDM
484VP_STATUS vboxWaitForSingleObjectVoid(IN PVOID HwDeviceExtension, IN PVOID Object, IN PLARGE_INTEGER Timeout OPTIONAL)
485{
486 return ERROR_INVALID_FUNCTION;
487}
488
489LONG vboxSetEventVoid(IN PVOID HwDeviceExtension, IN PEVENT pEvent)
490{
491 return 0;
492}
493
494VOID vboxClearEventVoid (IN PVOID HwDeviceExtension, IN PEVENT pEvent)
495{
496}
497
498VP_STATUS vboxCreateEventVoid(IN PVOID HwDeviceExtension, IN ULONG EventFlag, IN PVOID Unused, OUT PEVENT *ppEvent)
499{
500 return ERROR_INVALID_FUNCTION;
501}
502
503VP_STATUS vboxDeleteEventVoid(IN PVOID HwDeviceExtension, IN PEVENT pEvent)
504{
505 return ERROR_INVALID_FUNCTION;
506}
507
508VP_STATUS vboxCreateSpinLockVoid (IN PVOID HwDeviceExtension, OUT PSPIN_LOCK *SpinLock)
509{
510 return ERROR_INVALID_FUNCTION;
511}
512
513VP_STATUS vboxDeleteSpinLockVoid (IN PVOID HwDeviceExtension, IN PSPIN_LOCK SpinLock)
514{
515 return ERROR_INVALID_FUNCTION;
516}
517
518VOID vboxAcquireSpinLockVoid (IN PVOID HwDeviceExtension, IN PSPIN_LOCK SpinLock, OUT PUCHAR OldIrql)
519{
520}
521
522VOID vboxReleaseSpinLockVoid (IN PVOID HwDeviceExtension, IN PSPIN_LOCK SpinLock, IN UCHAR NewIrql)
523{
524}
525
526VOID vboxAcquireSpinLockAtDpcLevelVoid (IN PVOID HwDeviceExtension, IN PSPIN_LOCK SpinLock)
527{
528}
529
530VOID vboxReleaseSpinLockFromDpcLevelVoid (IN PVOID HwDeviceExtension, IN PSPIN_LOCK SpinLock)
531{
532}
533
534PVOID vboxAllocatePoolVoid(IN PVOID HwDeviceExtension, IN VBOXVP_POOL_TYPE PoolType, IN size_t NumberOfBytes, IN ULONG Tag)
535{
536 return NULL;
537}
538
539VOID vboxFreePoolVoid(IN PVOID HwDeviceExtension, IN PVOID Ptr)
540{
541}
542
543BOOLEAN vboxQueueDpcVoid(IN PVOID HwDeviceExtension, IN PMINIPORT_DPC_ROUTINE CallbackRoutine, IN PVOID Context)
544{
545 return FALSE;
546}
547
548void VBoxSetupVideoPortFunctions(PDEVICE_EXTENSION PrimaryExtension, VBOXVIDEOPORTPROCS *pCallbacks, PVIDEO_PORT_CONFIG_INFO pConfigInfo)
549{
550 memset(pCallbacks, 0, sizeof(VBOXVIDEOPORTPROCS));
551
552 if (vboxQueryWinVersion() <= WINNT4)
553 {
554 /* VideoPortGetProcAddress is available for >= win2k */
555 pCallbacks->pfnWaitForSingleObject = vboxWaitForSingleObjectVoid;
556 pCallbacks->pfnSetEvent = vboxSetEventVoid;
557 pCallbacks->pfnClearEvent = vboxClearEventVoid;
558 pCallbacks->pfnCreateEvent = vboxCreateEventVoid;
559 pCallbacks->pfnDeleteEvent = vboxDeleteEventVoid;
560 pCallbacks->pfnCreateSpinLock = vboxCreateSpinLockVoid;
561 pCallbacks->pfnDeleteSpinLock = vboxDeleteSpinLockVoid;
562 pCallbacks->pfnAcquireSpinLock = vboxAcquireSpinLockVoid;
563 pCallbacks->pfnReleaseSpinLock = vboxReleaseSpinLockVoid;
564 pCallbacks->pfnAcquireSpinLockAtDpcLevel = vboxAcquireSpinLockAtDpcLevelVoid;
565 pCallbacks->pfnReleaseSpinLockFromDpcLevel = vboxReleaseSpinLockFromDpcLevelVoid;
566 pCallbacks->pfnAllocatePool = vboxAllocatePoolVoid;
567 pCallbacks->pfnFreePool = vboxFreePoolVoid;
568 pCallbacks->pfnQueueDpc = vboxQueueDpcVoid;
569 return;
570 }
571
572 pCallbacks->pfnWaitForSingleObject = (PFNWAITFORSINGLEOBJECT)(pConfigInfo->VideoPortGetProcAddress)
573 (PrimaryExtension,
574 (PUCHAR)"VideoPortWaitForSingleObject");
575 Assert(pCallbacks->pfnWaitForSingleObject);
576
577 pCallbacks->pfnSetEvent = (PFNSETEVENT)(pConfigInfo->VideoPortGetProcAddress)
578 (PrimaryExtension,
579 (PUCHAR)"VideoPortSetEvent");
580 Assert(pCallbacks->pfnSetEvent);
581
582 pCallbacks->pfnClearEvent = (PFNCLEAREVENT)(pConfigInfo->VideoPortGetProcAddress)
583 (PrimaryExtension,
584 (PUCHAR)"VideoPortClearEvent");
585 Assert(pCallbacks->pfnClearEvent);
586
587 pCallbacks->pfnCreateEvent = (PFNCREATEEVENT)(pConfigInfo->VideoPortGetProcAddress)
588 (PrimaryExtension,
589 (PUCHAR)"VideoPortCreateEvent");
590 Assert(pCallbacks->pfnCreateEvent);
591
592 pCallbacks->pfnDeleteEvent = (PFNDELETEEVENT)(pConfigInfo->VideoPortGetProcAddress)
593 (PrimaryExtension,
594 (PUCHAR)"VideoPortDeleteEvent");
595 Assert(pCallbacks->pfnDeleteEvent);
596
597 if(pCallbacks->pfnWaitForSingleObject
598 && pCallbacks->pfnSetEvent
599 && pCallbacks->pfnClearEvent
600 && pCallbacks->pfnCreateEvent
601 && pCallbacks->pfnDeleteEvent)
602 {
603 pCallbacks->fSupportedTypes |= VBOXVIDEOPORTPROCS_EVENT;
604 }
605 else
606 {
607 pCallbacks->pfnWaitForSingleObject = vboxWaitForSingleObjectVoid;
608 pCallbacks->pfnSetEvent = vboxSetEventVoid;
609 pCallbacks->pfnClearEvent = vboxClearEventVoid;
610 pCallbacks->pfnCreateEvent = vboxCreateEventVoid;
611 pCallbacks->pfnDeleteEvent = vboxDeleteEventVoid;
612 }
613
614 pCallbacks->pfnCreateSpinLock = (PFNCREATESPINLOCK)(pConfigInfo->VideoPortGetProcAddress)
615 (PrimaryExtension,
616 (PUCHAR)"VideoPortCreateSpinLock");
617 Assert(pCallbacks->pfnCreateSpinLock);
618
619 pCallbacks->pfnDeleteSpinLock = (PFNDELETESPINLOCK)(pConfigInfo->VideoPortGetProcAddress)
620 (PrimaryExtension,
621 (PUCHAR)"VideoPortDeleteSpinLock");
622 Assert(pCallbacks->pfnDeleteSpinLock);
623
624 pCallbacks->pfnAcquireSpinLock = (PFNACQUIRESPINLOCK)(pConfigInfo->VideoPortGetProcAddress)
625 (PrimaryExtension,
626 (PUCHAR)"VideoPortAcquireSpinLock");
627 Assert(pCallbacks->pfnAcquireSpinLock);
628
629 pCallbacks->pfnReleaseSpinLock = (PFNRELEASESPINLOCK)(pConfigInfo->VideoPortGetProcAddress)
630 (PrimaryExtension,
631 (PUCHAR)"VideoPortReleaseSpinLock");
632 Assert(pCallbacks->pfnReleaseSpinLock);
633
634 pCallbacks->pfnAcquireSpinLockAtDpcLevel = (PFNACQUIRESPINLOCKATDPCLEVEL)(pConfigInfo->VideoPortGetProcAddress)
635 (PrimaryExtension,
636 (PUCHAR)"VideoPortAcquireSpinLockAtDpcLevel");
637 Assert(pCallbacks->pfnAcquireSpinLockAtDpcLevel);
638
639 pCallbacks->pfnReleaseSpinLockFromDpcLevel = (PFNRELEASESPINLOCKFROMDPCLEVEL)(pConfigInfo->VideoPortGetProcAddress)
640 (PrimaryExtension,
641 (PUCHAR)"VideoPortReleaseSpinLockFromDpcLevel");
642 Assert(pCallbacks->pfnReleaseSpinLockFromDpcLevel);
643
644 if(pCallbacks->pfnCreateSpinLock
645 && pCallbacks->pfnDeleteSpinLock
646 && pCallbacks->pfnAcquireSpinLock
647 && pCallbacks->pfnReleaseSpinLock
648 && pCallbacks->pfnAcquireSpinLockAtDpcLevel
649 && pCallbacks->pfnReleaseSpinLockFromDpcLevel)
650 {
651 pCallbacks->fSupportedTypes |= VBOXVIDEOPORTPROCS_SPINLOCK;
652 }
653 else
654 {
655 pCallbacks->pfnCreateSpinLock = vboxCreateSpinLockVoid;
656 pCallbacks->pfnDeleteSpinLock = vboxDeleteSpinLockVoid;
657 pCallbacks->pfnAcquireSpinLock = vboxAcquireSpinLockVoid;
658 pCallbacks->pfnReleaseSpinLock = vboxReleaseSpinLockVoid;
659 pCallbacks->pfnAcquireSpinLockAtDpcLevel = vboxAcquireSpinLockAtDpcLevelVoid;
660 pCallbacks->pfnReleaseSpinLockFromDpcLevel = vboxReleaseSpinLockFromDpcLevelVoid;
661 }
662
663 pCallbacks->pfnAllocatePool = (PFNALLOCATEPOOL)(pConfigInfo->VideoPortGetProcAddress)
664 (PrimaryExtension,
665 (PUCHAR)"VideoPortAllocatePool");
666 Assert(pCallbacks->pfnAllocatePool);
667
668 pCallbacks->pfnFreePool = (PFNFREEPOOL)(pConfigInfo->VideoPortGetProcAddress)
669 (PrimaryExtension,
670 (PUCHAR)"VideoPortFreePool");
671 Assert(pCallbacks->pfnFreePool);
672
673 if(pCallbacks->pfnAllocatePool
674 && pCallbacks->pfnFreePool)
675 {
676 pCallbacks->fSupportedTypes |= VBOXVIDEOPORTPROCS_POOL;
677 }
678 else
679 {
680 pCallbacks->pfnAllocatePool = vboxAllocatePoolVoid;
681 pCallbacks->pfnFreePool = vboxFreePoolVoid;
682 }
683
684 pCallbacks->pfnQueueDpc = (PFNQUEUEDPC)(pConfigInfo->VideoPortGetProcAddress)
685 (PrimaryExtension,
686 (PUCHAR)"VideoPortQueueDpc");
687 Assert(pCallbacks->pfnQueueDpc);
688
689 if(pCallbacks->pfnQueueDpc)
690 {
691 pCallbacks->fSupportedTypes |= VBOXVIDEOPORTPROCS_DPC;
692 }
693 else
694 {
695 pCallbacks->pfnQueueDpc = vboxQueueDpcVoid;
696 }
697
698#ifdef DEBUG_misha
699 Assert(pCallbacks->fSupportedTypes & VBOXVIDEOPORTPROCS_EVENT);
700 Assert(pCallbacks->fSupportedTypes & VBOXVIDEOPORTPROCS_SPINLOCK);
701#endif
702}
703#endif
704
705/**
706 * Helper function to register secondary displays (DualView). Note that this will not
707 * be available on pre-XP versions, and some editions on XP will fail because they are
708 * intentionally crippled.
709 *
710 * HGSMI variant is a bit different because it uses only HGSMI interface (VBVA channel)
711 * to talk to the host.
712 */
713VOID VBoxSetupDisplaysHGSMI(PDEVICE_EXTENSION PrimaryExtension,
714#ifndef VBOX_WITH_WDDM
715 PVIDEO_PORT_CONFIG_INFO pConfigInfo,
716#endif
717 ULONG AdapterMemorySize)
718{
719 VP_STATUS rc = NO_ERROR;
720
721 dprintf(("VBoxVideo::VBoxSetupDisplays: PrimaryExtension = %p\n",
722 PrimaryExtension));
723
724 /* Preinitialize the primary extension.
725 * Note: bVBoxVideoSupported is set to FALSE, because HGSMI is active instead.
726 * Note 2: shouldn't be needed for WDDM.
727 */
728 PrimaryExtension->pNext = NULL;
729#ifndef VBOX_WITH_WDDM
730 PrimaryExtension->pPrimary = PrimaryExtension;
731 PrimaryExtension->iDevice = 0;
732 PrimaryExtension->ulFrameBufferOffset = 0;
733 PrimaryExtension->ulFrameBufferSize = 0;
734#endif
735 PrimaryExtension->u.primary.ulVbvaEnabled = 0;
736 PrimaryExtension->u.primary.bVBoxVideoSupported = FALSE;
737#ifndef VBOX_WITH_WDDM
738 PrimaryExtension->u.primary.cDisplays = 1;
739#endif
740 commonFromDeviceExt(PrimaryExtension)->cbVRAM = AdapterMemorySize;
741 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = 0;
742 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap = NULL;
743 commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation = NULL;
744 commonFromDeviceExt(PrimaryExtension)->pHostFlags = NULL;
745 PrimaryExtension->u.primary.ulMaxFrameBufferSize = 0;
746 commonFromDeviceExt(PrimaryExtension)->bHGSMI = VBoxHGSMIIsSupported ();
747 VBoxVideoCmnMemZero(&commonFromDeviceExt(PrimaryExtension)->areaHostHeap, sizeof(HGSMIAREA));
748 VBoxVideoCmnMemZero(&PrimaryExtension->areaDisplay, sizeof(HGSMIAREA));
749
750 if (commonFromDeviceExt(PrimaryExtension)->IOPortGuest == 0)
751 {
752 commonFromDeviceExt(PrimaryExtension)->bHGSMI = false;
753 }
754
755 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
756 {
757 /* Map the adapter information. It will be needed for HGSMI IO. */
758 rc = VBoxMapAdapterMemory (PrimaryExtension,
759 &commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation,
760 commonFromDeviceExt(PrimaryExtension)->cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE,
761 VBVA_ADAPTER_INFORMATION_SIZE
762 );
763 if (rc != NO_ERROR)
764 {
765 dprintf(("VBoxVideo::VBoxSetupDisplays: VBoxMapAdapterMemory pvAdapterInfoirrmation failed rc = %d\n",
766 rc));
767
768 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
769 }
770 else
771 {
772 /* Setup a HGSMI heap within the adapter information area. */
773 rc = HGSMIHeapSetup (&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap,
774 commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation,
775 VBVA_ADAPTER_INFORMATION_SIZE - sizeof(HGSMIHOSTFLAGS),
776 commonFromDeviceExt(PrimaryExtension)->cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE,
777 false /*fOffsetBased*/);
778
779 if (RT_FAILURE (rc))
780 {
781 dprintf(("VBoxVideo::VBoxSetupDisplays: HGSMIHeapSetup failed rc = %d\n",
782 rc));
783
784 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
785 }
786 else
787 {
788 commonFromDeviceExt(PrimaryExtension)->pHostFlags = (HGSMIHOSTFLAGS*)(((uint8_t*)commonFromDeviceExt(PrimaryExtension)->pvAdapterInformation)
789 + VBVA_ADAPTER_INFORMATION_SIZE - sizeof(HGSMIHOSTFLAGS));
790 }
791 }
792 }
793
794 /* Setup the host heap and the adapter memory. */
795 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
796 {
797 /* The miniport heap is used for the host buffers. */
798 ULONG cbMiniportHeap = 0;
799 vboxQueryConfHGSMI (PrimaryExtension, VBOX_VBVA_CONF32_HOST_HEAP_SIZE, &cbMiniportHeap);
800
801 if (cbMiniportHeap != 0)
802 {
803 /* Do not allow too big heap. No more than 25% of VRAM is allowed. */
804 ULONG cbMiniportHeapMaxSize = AdapterMemorySize / 4;
805
806 if (cbMiniportHeapMaxSize >= VBVA_ADAPTER_INFORMATION_SIZE)
807 {
808 cbMiniportHeapMaxSize -= VBVA_ADAPTER_INFORMATION_SIZE;
809 }
810
811 if (cbMiniportHeap > cbMiniportHeapMaxSize)
812 {
813 cbMiniportHeap = cbMiniportHeapMaxSize;
814 }
815
816 /* Round up to 4096 bytes. */
817 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = (cbMiniportHeap + 0xFFF) & ~0xFFF;
818
819 dprintf(("VBoxVideo::VBoxSetupDisplays: cbMiniportHeap = 0x%08X, PrimaryExtension->u.primary.cbMiniportHeap = 0x%08X, cbMiniportHeapMaxSize = 0x%08X\n",
820 cbMiniportHeap, commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap, cbMiniportHeapMaxSize));
821
822 /* Map the heap region.
823 *
824 * Note: the heap will be used for the host buffers submitted to the guest.
825 * The miniport driver is responsible for reading FIFO and notifying
826 * display drivers.
827 */
828 rc = VBoxMapAdapterMemory (PrimaryExtension,
829 &commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap,
830 commonFromDeviceExt(PrimaryExtension)->cbVRAM
831 - VBVA_ADAPTER_INFORMATION_SIZE
832 - commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap,
833 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap
834 );
835 if (rc != NO_ERROR)
836 {
837 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap = NULL;
838 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = 0;
839 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
840 }
841 else
842 {
843 HGSMIOFFSET offBase = commonFromDeviceExt(PrimaryExtension)->cbVRAM
844 - VBVA_ADAPTER_INFORMATION_SIZE
845 - commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap;
846
847 /* Init the host hap area. Buffers from the host will be placed there. */
848 HGSMIAreaInitialize (&commonFromDeviceExt(PrimaryExtension)->areaHostHeap,
849 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap,
850 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap,
851 offBase);
852 }
853 }
854 else
855 {
856 /* Host has not requested a heap. */
857 commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap = NULL;
858 commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap = 0;
859 }
860 }
861
862 /* Check whether the guest supports multimonitors. */
863 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
864 {
865#ifndef VBOX_WITH_WDDM
866 typedef VP_STATUS (*PFNCREATESECONDARYDISPLAY)(PVOID, PVOID *, ULONG);
867 PFNCREATESECONDARYDISPLAY pfnCreateSecondaryDisplay = NULL;
868
869 /* Dynamically query the VideoPort import to be binary compatible across Windows versions */
870 if (vboxQueryWinVersion() > WINNT4)
871 {
872 /* This bluescreens on NT4, hence the above version check */
873 pfnCreateSecondaryDisplay = (PFNCREATESECONDARYDISPLAY)(pConfigInfo->VideoPortGetProcAddress)
874 (PrimaryExtension,
875 (PUCHAR)"VideoPortCreateSecondaryDisplay");
876 }
877
878 if (pfnCreateSecondaryDisplay != NULL)
879#endif
880 {
881 /* Query the configured number of displays. */
882 ULONG cDisplays = 0;
883 vboxQueryConfHGSMI (PrimaryExtension, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
884
885 dprintf(("VBoxVideo::VBoxSetupDisplays: cDisplays = %d\n",
886 cDisplays));
887
888 if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS)
889 {
890 /* Host reported some bad value. Continue in the 1 screen mode. */
891 cDisplays = 1;
892 }
893
894#ifndef VBOX_WITH_WDDM
895 PDEVICE_EXTENSION pPrev = PrimaryExtension;
896
897 ULONG iDisplay;
898 for (iDisplay = 1; iDisplay < cDisplays; iDisplay++)
899 {
900 PDEVICE_EXTENSION SecondaryExtension = NULL;
901 rc = pfnCreateSecondaryDisplay (PrimaryExtension, (PVOID*)&SecondaryExtension, VIDEO_DUALVIEW_REMOVABLE);
902
903 dprintf(("VBoxVideo::VBoxSetupDisplays: VideoPortCreateSecondaryDisplay returned %#x, SecondaryExtension = %p\n",
904 rc, SecondaryExtension));
905
906 if (rc != NO_ERROR)
907 {
908 break;
909 }
910
911 SecondaryExtension->pNext = NULL;
912 SecondaryExtension->pPrimary = PrimaryExtension;
913 SecondaryExtension->iDevice = iDisplay;
914 SecondaryExtension->ulFrameBufferOffset = 0;
915 SecondaryExtension->ulFrameBufferSize = 0;
916 SecondaryExtension->u.secondary.bEnabled = FALSE;
917
918 /* Update the list pointers. */
919 pPrev->pNext = SecondaryExtension;
920 pPrev = SecondaryExtension;
921
922 /* Take the successfully created display into account. */
923 PrimaryExtension->u.primary.cDisplays++;
924 }
925#else
926 /* simply store the number of monitors, we will deal with VidPN stuff later */
927 PrimaryExtension->u.primary.cDisplays = cDisplays;
928#endif
929 }
930
931 /* Failure to create secondary displays is not fatal */
932 rc = NO_ERROR;
933 }
934
935#ifndef VBOX_WITH_WDDM
936 /* Now when the number of monitors is known and extensions are created,
937 * calculate the layout of framebuffers.
938 */
939 VBoxComputeFrameBufferSizes (PrimaryExtension);
940#endif
941
942 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
943 {
944 /* Setup the information for the host. */
945 rc = vboxSetupAdapterInfoHGSMI (PrimaryExtension);
946
947 if (RT_FAILURE (rc))
948 {
949 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
950 }
951 }
952
953#ifdef VBOX_WITH_WDDM
954 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
955 {
956 ULONG ulAvailable = commonFromDeviceExt(PrimaryExtension)->cbVRAM
957 - commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap
958 - VBVA_ADAPTER_INFORMATION_SIZE;
959
960 ULONG ulSize;
961 ULONG offset;
962#ifdef VBOX_WITH_VDMA
963 ulSize = ulAvailable / 2;
964 if (ulSize > VBOXWDDM_C_VDMA_BUFFER_SIZE)
965 ulSize = VBOXWDDM_C_VDMA_BUFFER_SIZE;
966
967 /* Align down to 4096 bytes. */
968 ulSize &= ~0xFFF;
969 offset = ulAvailable - ulSize;
970
971 Assert(!(offset & 0xFFF));
972#else
973 offset = ulAvailable;
974#endif
975 rc = vboxVdmaCreate (PrimaryExtension, &PrimaryExtension->u.primary.Vdma
976#ifdef VBOX_WITH_VDMA
977 , offset, ulSize
978#endif
979 );
980 AssertRC(rc);
981 if (RT_SUCCESS(rc))
982 {
983 /* can enable it right away since the host does not need any screen/FB info
984 * for basic DMA functionality */
985 rc = vboxVdmaEnable(PrimaryExtension, &PrimaryExtension->u.primary.Vdma);
986 AssertRC(rc);
987 if (RT_FAILURE(rc))
988 vboxVdmaDestroy(PrimaryExtension, &PrimaryExtension->u.primary.Vdma);
989 }
990
991#ifdef VBOXWDDM_RENDER_FROM_SHADOW
992 if (RT_SUCCESS(rc))
993 {
994 ulAvailable = offset;
995 ulSize = ulAvailable / 2;
996 ulSize /= PrimaryExtension->u.primary.cDisplays;
997 Assert(ulSize > VBVA_MIN_BUFFER_SIZE);
998 if (ulSize > VBVA_MIN_BUFFER_SIZE)
999 {
1000 ULONG ulRatio = ulSize/VBVA_MIN_BUFFER_SIZE;
1001 ulRatio >>= 4; /* /= 16; */
1002 if (ulRatio)
1003 ulSize = VBVA_MIN_BUFFER_SIZE * ulRatio;
1004 else
1005 ulSize = VBVA_MIN_BUFFER_SIZE;
1006 }
1007 else
1008 {
1009 /* todo: ?? */
1010 }
1011
1012 ulSize &= ~0xFFF;
1013 Assert(ulSize);
1014
1015 Assert(ulSize * PrimaryExtension->u.primary.cDisplays < ulAvailable);
1016
1017 for (int i = PrimaryExtension->u.primary.cDisplays-1; i >= 0; --i)
1018 {
1019 offset -= ulSize;
1020 rc = vboxVbvaCreate(PrimaryExtension, &PrimaryExtension->aSources[i].Vbva, offset, ulSize, i);
1021 AssertRC(rc);
1022 if (RT_SUCCESS(rc))
1023 {
1024 rc = vboxVbvaEnable(PrimaryExtension, &PrimaryExtension->aSources[i].Vbva);
1025 AssertRC(rc);
1026 if (RT_FAILURE(rc))
1027 {
1028 /* @todo: de-initialize */
1029 }
1030 }
1031 }
1032 }
1033#endif
1034
1035 rc = VBoxMapAdapterMemory(PrimaryExtension, (void**)&PrimaryExtension->pvVisibleVram,
1036 0,
1037 vboxWddmVramCpuVisibleSize(PrimaryExtension));
1038 Assert(rc == VINF_SUCCESS);
1039 if (rc != VINF_SUCCESS)
1040 PrimaryExtension->pvVisibleVram = NULL;
1041
1042 if (RT_FAILURE(rc))
1043 commonFromDeviceExt(PrimaryExtension)->bHGSMI = FALSE;
1044 }
1045#endif
1046
1047 if (!commonFromDeviceExt(PrimaryExtension)->bHGSMI)
1048 {
1049 /* Unmap the memory if VBoxVideo is not supported. */
1050 VBoxUnmapAdapterMemory (PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap, commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap);
1051 VBoxUnmapAdapterInformation (PrimaryExtension);
1052
1053 HGSMIHeapDestroy (&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap);
1054 }
1055
1056 if (commonFromDeviceExt(PrimaryExtension)->bHGSMI)
1057 {
1058 VBoxVideoCmnSpinLockCreate(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pSynchLock);
1059 }
1060
1061 dprintf(("VBoxVideo::VBoxSetupDisplays: finished\n"));
1062}
1063
1064#ifdef VBOX_WITH_WDDM
1065int VBoxFreeDisplaysHGSMI(PDEVICE_EXTENSION PrimaryExtension)
1066{
1067 int rc = VINF_SUCCESS;
1068
1069 Assert(PrimaryExtension->pvVisibleVram);
1070 if (PrimaryExtension->pvVisibleVram)
1071 VBoxUnmapAdapterMemory(PrimaryExtension, (void**)&PrimaryExtension->pvVisibleVram, vboxWddmVramCpuVisibleSize(PrimaryExtension));
1072
1073 for (int i = PrimaryExtension->u.primary.cDisplays-1; i >= 0; --i)
1074 {
1075 rc = vboxVbvaDisable(PrimaryExtension, &PrimaryExtension->aSources[i].Vbva);
1076 AssertRC(rc);
1077 if (RT_SUCCESS(rc))
1078 {
1079 rc = vboxVbvaDestroy(PrimaryExtension, &PrimaryExtension->aSources[i].Vbva);
1080 AssertRC(rc);
1081 if (RT_FAILURE(rc))
1082 {
1083 /* @todo: */
1084 }
1085 }
1086 }
1087
1088 rc = vboxVdmaDisable(PrimaryExtension, &PrimaryExtension->u.primary.Vdma);
1089 AssertRC(rc);
1090 if (RT_SUCCESS(rc))
1091 {
1092 rc = vboxVdmaDestroy(PrimaryExtension, &PrimaryExtension->u.primary.Vdma);
1093 AssertRC(rc);
1094 if (RT_SUCCESS(rc))
1095 {
1096 /*rc = */VBoxUnmapAdapterMemory(PrimaryExtension, &commonFromDeviceExt(PrimaryExtension)->pvMiniportHeap, commonFromDeviceExt(PrimaryExtension)->cbMiniportHeap);
1097/*
1098 AssertRC(rc);
1099 if (RT_SUCCESS(rc))
1100*/
1101 {
1102 HGSMIHeapDestroy(&commonFromDeviceExt(PrimaryExtension)->hgsmiAdapterHeap);
1103
1104 /* Map the adapter information. It will be needed for HGSMI IO. */
1105 /*rc = */VBoxUnmapAdapterInformation(PrimaryExtension);
1106/*
1107 AssertRC(rc);
1108 if (RT_FAILURE(rc))
1109 drprintf((__FUNCTION__"VBoxUnmapAdapterMemory PrimaryExtension->u.primary.pvAdapterInformation failed, rc(%d)\n", rc));
1110*/
1111
1112 }
1113 }
1114 }
1115
1116 return rc;
1117}
1118#endif
1119
1120/*
1121 * Send the pointer shape to the host.
1122 */
1123typedef struct _MOUSEPOINTERSHAPECTX
1124{
1125 VIDEO_POINTER_ATTRIBUTES *pPointerAttr;
1126 uint32_t cbData;
1127 int32_t i32Result;
1128} MOUSEPOINTERSHAPECTX;
1129
1130static int vbvaInitMousePointerShape (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
1131{
1132 NOREF (PrimaryExtension);
1133
1134 MOUSEPOINTERSHAPECTX *pCtx = (MOUSEPOINTERSHAPECTX *)pvContext;
1135 VBVAMOUSEPOINTERSHAPE *p = (VBVAMOUSEPOINTERSHAPE *)pvData;
1136
1137 /* Will be updated by the host. */
1138 p->i32Result = VINF_SUCCESS;
1139
1140 /* We have our custom flags in the field */
1141 p->fu32Flags = pCtx->pPointerAttr->Enable & 0x0000FFFF;
1142
1143 p->u32HotX = (pCtx->pPointerAttr->Enable >> 16) & 0xFF;
1144 p->u32HotY = (pCtx->pPointerAttr->Enable >> 24) & 0xFF;
1145 p->u32Width = pCtx->pPointerAttr->Width;
1146 p->u32Height = pCtx->pPointerAttr->Height;
1147
1148 if (p->fu32Flags & VBOX_MOUSE_POINTER_SHAPE)
1149 {
1150 /* If shape is supplied, then alway create the pointer visible.
1151 * See comments in 'vboxUpdatePointerShape'
1152 */
1153 p->fu32Flags |= VBOX_MOUSE_POINTER_VISIBLE;
1154
1155 /* Copy the actual pointer data. */
1156 memcpy (p->au8Data, pCtx->pPointerAttr->Pixels, pCtx->cbData);
1157 }
1158
1159 return VINF_SUCCESS;
1160}
1161
1162static int vbvaFinalizeMousePointerShape (PDEVICE_EXTENSION PrimaryExtension, void *pvContext, void *pvData)
1163{
1164 NOREF (PrimaryExtension);
1165
1166 MOUSEPOINTERSHAPECTX *pCtx = (MOUSEPOINTERSHAPECTX *)pvContext;
1167 VBVAMOUSEPOINTERSHAPE *p = (VBVAMOUSEPOINTERSHAPE *)pvData;
1168
1169 pCtx->i32Result = p->i32Result;
1170
1171 return VINF_SUCCESS;
1172}
1173
1174BOOLEAN vboxUpdatePointerShape (PDEVICE_EXTENSION DeviceExtension,
1175 PVIDEO_POINTER_ATTRIBUTES pointerAttr,
1176 uint32_t cbLength)
1177{
1178#ifndef VBOX_WITH_WDDM
1179 PDEVICE_EXTENSION PrimaryExtension = DeviceExtension->pPrimary;
1180
1181 /* In multimonitor case the HW mouse pointer is the same on all screens,
1182 * and Windows calls each display driver with the same pointer data: visible for
1183 * the screen where the pointer is and invisible for other screens.
1184 *
1185 * This driver passes the shape to the host only from primary screen and makes
1186 * the pointer always visible (in vbvaInitMousePointerShape).
1187 *
1188 * The simple solution makes it impossible to create the shape and keep the mouse
1189 * pointer invisible. New shapes will be created visible.
1190 * But:
1191 * 1) VBox frontends actually ignore the visibility flag if VBOX_MOUSE_POINTER_SHAPE
1192 * is set and always create new pointers visible.
1193 * 2) Windows uses DrvMovePointer to hide the pointer, which will still work.
1194 */
1195
1196 if (DeviceExtension->iDevice != PrimaryExtension->iDevice)
1197 {
1198 dprintf(("vboxUpdatePointerShape: ignore non primary device %d(%d)\n",
1199 DeviceExtension->iDevice, PrimaryExtension->iDevice));
1200 /* Success. */
1201 return TRUE;
1202 }
1203#else
1204 PDEVICE_EXTENSION PrimaryExtension = DeviceExtension;
1205#endif
1206
1207 uint32_t cbData = 0;
1208
1209 if (pointerAttr->Enable & VBOX_MOUSE_POINTER_SHAPE)
1210 {
1211 /* Size of the pointer data: sizeof (AND mask) + sizeof (XOR_MASK) */
1212 cbData = ((((pointerAttr->Width + 7) / 8) * pointerAttr->Height + 3) & ~3)
1213 + pointerAttr->Width * 4 * pointerAttr->Height;
1214 }
1215
1216#ifndef DEBUG_misha
1217 dprintf(("vboxUpdatePointerShape: cbData %d, %dx%d\n",
1218 cbData, pointerAttr->Width, pointerAttr->Height));
1219#endif
1220
1221 if (cbData > cbLength - sizeof(VIDEO_POINTER_ATTRIBUTES))
1222 {
1223 dprintf(("vboxUpdatePointerShape: calculated pointer data size is too big (%d bytes, limit %d)\n",
1224 cbData, cbLength - sizeof(VIDEO_POINTER_ATTRIBUTES)));
1225 return FALSE;
1226 }
1227
1228 MOUSEPOINTERSHAPECTX ctx;
1229
1230 ctx.pPointerAttr = pointerAttr;
1231 ctx.cbData = cbData;
1232 ctx.i32Result = VERR_NOT_SUPPORTED;
1233
1234 int rc = vboxCallVBVA (PrimaryExtension,
1235 VBVA_MOUSE_POINTER_SHAPE,
1236 sizeof (VBVAMOUSEPOINTERSHAPE) + cbData,
1237 vbvaInitMousePointerShape,
1238 vbvaFinalizeMousePointerShape,
1239 &ctx);
1240#ifndef DEBUG_misha
1241 dprintf(("VBoxVideo::vboxMousePointerShape: rc %d, i32Result = %d\n", rc, ctx.i32Result));
1242#endif
1243
1244 return RT_SUCCESS(rc) && RT_SUCCESS(ctx.i32Result);
1245}
1246
1247typedef struct _VBVAMINIPORT_CHANNELCONTEXT
1248{
1249 PFNHGSMICHANNELHANDLER pfnChannelHandler;
1250 void *pvChannelHandler;
1251}VBVAMINIPORT_CHANNELCONTEXT;
1252
1253typedef struct _VBVADISP_CHANNELCONTEXT
1254{
1255 struct _VBVAHOSTCMD * pFirstCmd;
1256 struct _VBVAHOSTCMD * pLastCmd;
1257 VBOXVCMNSPIN_LOCK pSynchLock;
1258#ifdef DEBUG
1259 int cCmds;
1260#endif
1261 bool bValid;
1262}VBVADISP_CHANNELCONTEXT;
1263
1264#ifdef DEBUG
1265void dbgCheckListLocked(const VBVADISP_CHANNELCONTEXT *pList, struct _VBVAHOSTCMD * pCmd)
1266{
1267 int counter = 0;
1268 for(struct _VBVAHOSTCMD * pCur = pList->pFirstCmd; pCur; pCur=pCur->u.pNext)
1269 {
1270 Assert(pCur != pCmd);
1271 if(pCur == pList->pLastCmd)
1272 {
1273 Assert(pCur->u.pNext == NULL);
1274 }
1275 if(pCur->u.pNext == NULL)
1276 {
1277 Assert(pCur == pList->pLastCmd);
1278 }
1279 counter++;
1280 }
1281
1282 Assert(counter == pList->cCmds);
1283}
1284
1285void dbgCheckList(PDEVICE_EXTENSION PrimaryExtension, VBVADISP_CHANNELCONTEXT *pList, struct _VBVAHOSTCMD * pCmd)
1286{
1287 VBOXVCMNIRQL oldIrql;
1288 VBoxVideoCmnSpinLockAcquire(PrimaryExtension, &pList->pSynchLock, &oldIrql);
1289
1290 dbgCheckListLocked(pList, pCmd);
1291
1292 VBoxVideoCmnSpinLockRelease(PrimaryExtension, &pList->pSynchLock, oldIrql);
1293}
1294
1295#define DBG_CHECKLIST_LOCKED(_pl, pc) dbgCheckListLocked(_pl, pc)
1296#define DBG_CHECKLIST(_pe, _pl, pc) dbgCheckList(_pe, _pl, pc)
1297
1298#else
1299#define DBG_CHECKLIST_LOCKED(_pl, pc) do{}while(0)
1300#define DBG_CHECKLIST(_pe, _pl, pc) do{}while(0)
1301#endif
1302
1303
1304typedef struct _VBVA_CHANNELCONTEXTS
1305{
1306 PDEVICE_EXTENSION PrimaryExtension;
1307 uint32_t cUsed;
1308 uint32_t cContexts;
1309 VBVAMINIPORT_CHANNELCONTEXT mpContext;
1310 VBVADISP_CHANNELCONTEXT aContexts[1];
1311}VBVA_CHANNELCONTEXTS;
1312
1313static int vboxVBVADeleteChannelContexts(PDEVICE_EXTENSION PrimaryExtension, VBVA_CHANNELCONTEXTS * pContext)
1314{
1315 VBoxVideoCmnMemFree(PrimaryExtension,pContext);
1316 return VINF_SUCCESS;
1317}
1318
1319static int vboxVBVACreateChannelContexts(PDEVICE_EXTENSION PrimaryExtension, VBVA_CHANNELCONTEXTS ** ppContext)
1320{
1321#ifndef VBOX_WITH_WDDM
1322 uint32_t cDisplays = (uint32_t)PrimaryExtension->u.primary.cDisplays;
1323#else
1324 uint32_t cDisplays = (uint32_t)PrimaryExtension->u.primary.cDisplays;
1325#endif
1326 const size_t size = RT_OFFSETOF(VBVA_CHANNELCONTEXTS, aContexts[cDisplays]);
1327 VBVA_CHANNELCONTEXTS * pContext = (VBVA_CHANNELCONTEXTS*)VBoxVideoCmnMemAllocNonPaged(PrimaryExtension, size, MEM_TAG);
1328 if(pContext)
1329 {
1330 memset(pContext, 0, size);
1331 pContext->cContexts = cDisplays;
1332 pContext->PrimaryExtension = PrimaryExtension;
1333 *ppContext = pContext;
1334 return VINF_SUCCESS;
1335 }
1336 return VERR_GENERAL_FAILURE;
1337}
1338
1339static VBVADISP_CHANNELCONTEXT* vboxVBVAFindHandlerInfo(VBVA_CHANNELCONTEXTS *pCallbacks, int iId)
1340{
1341 if(iId < 0)
1342 {
1343 return NULL;
1344 }
1345 else if(pCallbacks->cContexts > (uint32_t)iId)
1346 {
1347 return &pCallbacks->aContexts[iId];
1348 }
1349 return NULL;
1350}
1351
1352#ifndef VBOX_WITH_WDDM
1353DECLCALLBACK(void) hgsmiHostCmdComplete (HVBOXVIDEOHGSMI hHGSMI, struct _VBVAHOSTCMD * pCmd)
1354{
1355 PDEVICE_EXTENSION PrimaryExtension = ((PDEVICE_EXTENSION)hHGSMI)->pPrimary;
1356 HGSMIHostCmdComplete (commonFromDeviceExt(PrimaryExtension), pCmd);
1357}
1358
1359DECLCALLBACK(int) hgsmiHostCmdRequest (HVBOXVIDEOHGSMI hHGSMI, uint8_t u8Channel, struct _VBVAHOSTCMD ** ppCmd)
1360{
1361// if(display < 0)
1362// return VERR_INVALID_PARAMETER;
1363 if(!ppCmd)
1364 return VERR_INVALID_PARAMETER;
1365
1366 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)hHGSMI;
1367 PDEVICE_EXTENSION PrimaryExtension = pDevExt->pPrimary;
1368
1369 /* pick up the host commands */
1370 VBoxVideoHGSMIDpc(PrimaryExtension, NULL);
1371
1372 HGSMICHANNEL * pChannel = HGSMIChannelFindById (&commonFromDeviceExt(PrimaryExtension)->channels, u8Channel);
1373 if(pChannel)
1374 {
1375 VBVA_CHANNELCONTEXTS * pContexts = (VBVA_CHANNELCONTEXTS *)pChannel->handler.pvHandler;
1376 VBVADISP_CHANNELCONTEXT *pDispContext = vboxVBVAFindHandlerInfo(pContexts, pDevExt->iDevice);
1377 Assert(pDispContext);
1378 if(pDispContext)
1379 {
1380 UCHAR oldIrql;
1381 VBoxVideoCmnSpinLockAcquire(PrimaryExtension, &pDispContext->pSynchLock, &oldIrql);
1382
1383 DBG_CHECKLIST_LOCKED(pDispContext, NULL);
1384
1385 *ppCmd = pDispContext->pFirstCmd;
1386 pDispContext->pFirstCmd = NULL;
1387 pDispContext->pLastCmd = NULL;
1388#ifdef DEBUG
1389 pDispContext->cCmds = 0;
1390#endif
1391 VBoxVideoCmnSpinLockRelease(PrimaryExtension, &pDispContext->pSynchLock, oldIrql);
1392
1393 DBG_CHECKLIST(PrimaryExtension, pDispContext, NULL);
1394
1395 return VINF_SUCCESS;
1396 }
1397 }
1398
1399 return VERR_INVALID_PARAMETER;
1400}
1401#endif
1402
1403static DECLCALLBACK(int) vboxVBVAChannelGenericHandler(void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
1404{
1405 VBVA_CHANNELCONTEXTS *pCallbacks = (VBVA_CHANNELCONTEXTS*)pvHandler;
1406// Assert(0);
1407 Assert(cbBuffer > VBVAHOSTCMD_HDRSIZE);
1408 if(cbBuffer > VBVAHOSTCMD_HDRSIZE)
1409 {
1410 VBVAHOSTCMD *pHdr = (VBVAHOSTCMD*)pvBuffer;
1411 Assert(pHdr->iDstID >= 0);
1412 if(pHdr->iDstID >= 0)
1413 {
1414 VBVADISP_CHANNELCONTEXT* pHandler = vboxVBVAFindHandlerInfo(pCallbacks, pHdr->iDstID);
1415 Assert(pHandler && pHandler->bValid);
1416 if(pHandler && pHandler->bValid)
1417 {
1418 VBVAHOSTCMD *pFirst = NULL, *pLast = NULL;
1419 for(VBVAHOSTCMD *pCur = pHdr; pCur; )
1420 {
1421 Assert(!pCur->u.Data);
1422 Assert(!pFirst);
1423 Assert(!pLast);
1424
1425 switch(u16ChannelInfo)
1426 {
1427 case VBVAHG_DISPLAY_CUSTOM:
1428 {
1429 if(pLast)
1430 {
1431 pLast->u.pNext = pCur;
1432 pLast = pCur;
1433 }
1434 else
1435 {
1436 pFirst = pCur;
1437 pLast = pCur;
1438 }
1439 Assert(!pCur->u.Data);
1440 //TODO: use offset here
1441 pCur = pCur->u.pNext;
1442 Assert(!pCur);
1443 Assert(pFirst);
1444 Assert(pFirst == pLast);
1445 break;
1446 }
1447 case VBVAHG_EVENT:
1448 {
1449 VBVAHOSTCMDEVENT *pEventCmd = VBVAHOSTCMD_BODY(pCur, VBVAHOSTCMDEVENT);
1450#ifndef VBOX_WITH_WDDM
1451 PEVENT pEvent = (PEVENT)pEventCmd->pEvent;
1452 pCallbacks->PrimaryExtension->u.primary.VideoPortProcs.pfnSetEvent(
1453 pCallbacks->PrimaryExtension,
1454 pEvent);
1455#else
1456 PKEVENT pEvent = (PKEVENT)pEventCmd->pEvent;
1457 KeSetEvent(pEvent, 0, FALSE);
1458#endif
1459 }
1460 default:
1461 {
1462 DBG_CHECKLIST(pCallbacks->PrimaryExtension, pHandler, pCur);
1463 Assert(u16ChannelInfo==VBVAHG_EVENT);
1464 Assert(!pCur->u.Data);
1465 //TODO: use offset here
1466 if(pLast)
1467 pLast->u.pNext = pCur->u.pNext;
1468 VBVAHOSTCMD * pNext = pCur->u.pNext;
1469 pCur->u.pNext = NULL;
1470 HGSMIHostCmdComplete(commonFromDeviceExt(pCallbacks->PrimaryExtension), pCur);
1471 pCur = pNext;
1472 Assert(!pCur);
1473 Assert(!pFirst);
1474 Assert(pFirst == pLast);
1475 break;
1476 }
1477 }
1478 }
1479
1480 DBG_CHECKLIST(pCallbacks->PrimaryExtension, pHandler, pFirst);
1481
1482 /* we do not support lists currently */
1483 Assert(pFirst == pLast);
1484 if(pLast)
1485 {
1486 Assert(pLast->u.pNext == NULL);
1487 }
1488
1489 if(pFirst)
1490 {
1491 Assert(pLast);
1492 UCHAR oldIrql;
1493 VBoxVideoCmnSpinLockAcquire(pCallbacks->PrimaryExtension,
1494 &pHandler->pSynchLock,
1495 &oldIrql);
1496
1497 DBG_CHECKLIST_LOCKED(pHandler, pFirst);
1498
1499 if(pHandler->pLastCmd)
1500 {
1501 pHandler->pLastCmd->u.pNext = pFirst;
1502 Assert(pHandler->pFirstCmd);
1503 }
1504 else
1505 {
1506 Assert(!pHandler->pFirstCmd);
1507 pHandler->pFirstCmd = pFirst;
1508 }
1509 pHandler->pLastCmd = pLast;
1510#ifdef DEBUG
1511 pHandler->cCmds++;
1512#endif
1513 DBG_CHECKLIST_LOCKED(pHandler, NULL);
1514
1515 VBoxVideoCmnSpinLockRelease(pCallbacks->PrimaryExtension,
1516 &pHandler->pSynchLock,
1517 oldIrql);
1518 }
1519 else
1520 {
1521 Assert(!pLast);
1522 }
1523 return VINF_SUCCESS;
1524 }
1525 }
1526 else
1527 {
1528 //TODO: impl
1529// HGSMIMINIPORT_CHANNELCONTEXT *pHandler = vboxVideoHGSMIFindHandler;
1530// if(pHandler && pHandler->pfnChannelHandler)
1531// {
1532// pHandler->pfnChannelHandler(pHandler->pvChannelHandler, u16ChannelInfo, pHdr, cbBuffer);
1533//
1534// return VINF_SUCCESS;
1535// }
1536 }
1537 }
1538 /* no handlers were found, need to complete the command here */
1539 HGSMIHostCmdComplete(commonFromDeviceExt(pCallbacks->PrimaryExtension), pvBuffer);
1540 return VINF_SUCCESS;
1541}
1542
1543static HGSMICHANNELHANDLER g_OldHandler;
1544
1545int vboxVBVAChannelDisplayEnable(PDEVICE_EXTENSION PrimaryExtension,
1546 int iDisplay, /* negative would mean this is a miniport handler */
1547 uint8_t u8Channel)
1548{
1549 VBVA_CHANNELCONTEXTS * pContexts;
1550 HGSMICHANNEL * pChannel = HGSMIChannelFindById (&commonFromDeviceExt(PrimaryExtension)->channels, u8Channel);
1551 if(!pChannel)
1552 {
1553 int rc = vboxVBVACreateChannelContexts(PrimaryExtension, &pContexts);
1554 if(RT_FAILURE(rc))
1555 {
1556 return rc;
1557 }
1558 }
1559 else
1560 {
1561 pContexts = (VBVA_CHANNELCONTEXTS *)pChannel->handler.pvHandler;
1562 }
1563
1564 VBVADISP_CHANNELCONTEXT *pDispContext = vboxVBVAFindHandlerInfo(pContexts, iDisplay);
1565 Assert(pDispContext);
1566 if(pDispContext)
1567 {
1568#ifdef DEBUGVHWASTRICT
1569 Assert(!pDispContext->bValid);
1570#endif
1571 Assert(!pDispContext->pFirstCmd);
1572 Assert(!pDispContext->pLastCmd);
1573 if(!pDispContext->bValid)
1574 {
1575 pDispContext->bValid = true;
1576 pDispContext->pFirstCmd = NULL;
1577 pDispContext->pLastCmd= NULL;
1578#ifdef DEBUG
1579 pDispContext->cCmds = 0;
1580#endif
1581
1582 VBoxVideoCmnSpinLockCreate(PrimaryExtension, &pDispContext->pSynchLock);
1583
1584 int rc = VINF_SUCCESS;
1585 if(!pChannel)
1586 {
1587 rc = HGSMIChannelRegister (&commonFromDeviceExt(PrimaryExtension)->channels,
1588 u8Channel,
1589 "VGA Miniport HGSMI channel",
1590 vboxVBVAChannelGenericHandler,
1591 pContexts,
1592 &g_OldHandler);
1593 }
1594
1595 if(RT_SUCCESS(rc))
1596 {
1597 pContexts->cUsed++;
1598 return VINF_SUCCESS;
1599 }
1600 }
1601 }
1602
1603 if(!pChannel)
1604 {
1605 vboxVBVADeleteChannelContexts(PrimaryExtension, pContexts);
1606 }
1607
1608 return VERR_GENERAL_FAILURE;
1609}
1610
1611/** @todo Mouse pointer position to be read from VMMDev memory, address of the memory region
1612 * can be queried from VMMDev via an IOCTL. This VMMDev memory region will contain
1613 * host information which is needed by the guest.
1614 *
1615 * Reading will not cause a switch to the host.
1616 *
1617 * Have to take into account:
1618 * * synchronization: host must write to the memory only from EMT,
1619 * large structures must be read under flag, which tells the host
1620 * that the guest is currently reading the memory (OWNER flag?).
1621 * * guest writes: may be allocate a page for the host info and make
1622 * the page readonly for the guest.
1623 * * the information should be available only for additions drivers.
1624 * * VMMDev additions driver will inform the host which version of the info it expects,
1625 * host must support all versions.
1626 *
1627 */
1628
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette