VirtualBox

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

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