VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Video/mp/wddm/VBoxMPMisc.cpp@ 49591

Last change on this file since 49591 was 49591, checked in by vboxsync, 11 years ago

wddm: more on new comand mechanism, guest side almost done, some cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.0 KB
Line 
1/* $Id: VBoxMPMisc.cpp 49591 2013-11-20 17:53:55Z vboxsync $ */
2
3/** @file
4 * VBox WDDM Miniport driver
5 */
6
7/*
8 * Copyright (C) 2011-2013 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxMPWddm.h"
20#include <VBox/Hardware/VBoxVideoVBE.h>
21#include <stdio.h>
22
23/* simple handle -> value table API */
24NTSTATUS vboxWddmHTableCreate(PVBOXWDDM_HTABLE pTbl, uint32_t cSize)
25{
26 memset(pTbl, 0, sizeof (*pTbl));
27 pTbl->paData = (PVOID*)vboxWddmMemAllocZero(sizeof (pTbl->paData[0]) * cSize);
28 if (pTbl->paData)
29 {
30 pTbl->cSize = cSize;
31 return STATUS_SUCCESS;
32 }
33 return STATUS_NO_MEMORY;
34}
35
36VOID vboxWddmHTableDestroy(PVBOXWDDM_HTABLE pTbl)
37{
38 if (!pTbl->paData)
39 return;
40
41 vboxWddmMemFree(pTbl->paData);
42}
43
44DECLINLINE(VBOXWDDM_HANDLE) vboxWddmHTableIndex2Handle(uint32_t iIndex)
45{
46 return iIndex+1;
47}
48
49DECLINLINE(uint32_t) vboxWddmHTableHandle2Index(VBOXWDDM_HANDLE hHandle)
50{
51 return hHandle-1;
52}
53
54NTSTATUS vboxWddmHTableRealloc(PVBOXWDDM_HTABLE pTbl, uint32_t cNewSize)
55{
56 Assert(cNewSize > pTbl->cSize);
57 if (cNewSize > pTbl->cSize)
58 {
59 PVOID *pvNewData = (PVOID*)vboxWddmMemAllocZero(sizeof (pTbl->paData[0]) * cNewSize);
60 if (!pvNewData)
61 {
62 WARN(("vboxWddmMemAllocZero failed for size (%d)", sizeof (pTbl->paData[0]) * cNewSize));
63 return STATUS_NO_MEMORY;
64 }
65 memcpy(pvNewData, pTbl->paData, sizeof (pTbl->paData[0]) * pTbl->cSize);
66 vboxWddmMemFree(pTbl->paData);
67 pTbl->iNext2Search = pTbl->cSize;
68 pTbl->cSize = cNewSize;
69 pTbl->paData = pvNewData;
70 return STATUS_SUCCESS;
71 }
72 else if (cNewSize >= pTbl->cData)
73 {
74 AssertFailed();
75 return STATUS_NOT_IMPLEMENTED;
76 }
77 return STATUS_INVALID_PARAMETER;
78
79}
80VBOXWDDM_HANDLE vboxWddmHTablePut(PVBOXWDDM_HTABLE pTbl, PVOID pvData)
81{
82 if (pTbl->cSize == pTbl->cData)
83 {
84 NTSTATUS Status = vboxWddmHTableRealloc(pTbl, pTbl->cSize + RT_MAX(10, pTbl->cSize/4));
85 Assert(Status == STATUS_SUCCESS);
86 if (Status != STATUS_SUCCESS)
87 return VBOXWDDM_HANDLE_INVALID;
88 }
89 for (UINT i = pTbl->iNext2Search; ; ++i, i %= pTbl->cSize)
90 {
91 Assert(i < pTbl->cSize);
92 if (!pTbl->paData[i])
93 {
94 pTbl->paData[i] = pvData;
95 ++pTbl->cData;
96 Assert(pTbl->cData <= pTbl->cSize);
97 ++pTbl->iNext2Search;
98 pTbl->iNext2Search %= pTbl->cSize;
99 return vboxWddmHTableIndex2Handle(i);
100 }
101 }
102 Assert(0);
103 return VBOXWDDM_HANDLE_INVALID;
104}
105
106PVOID vboxWddmHTableRemove(PVBOXWDDM_HTABLE pTbl, VBOXWDDM_HANDLE hHandle)
107{
108 uint32_t iIndex = vboxWddmHTableHandle2Index(hHandle);
109 Assert(iIndex < pTbl->cSize);
110 if (iIndex < pTbl->cSize)
111 {
112 PVOID pvData = pTbl->paData[iIndex];
113 pTbl->paData[iIndex] = NULL;
114 --pTbl->cData;
115 Assert(pTbl->cData <= pTbl->cSize);
116 pTbl->iNext2Search = iIndex;
117 return pvData;
118 }
119 return NULL;
120}
121
122PVOID vboxWddmHTableGet(PVBOXWDDM_HTABLE pTbl, VBOXWDDM_HANDLE hHandle)
123{
124 uint32_t iIndex = vboxWddmHTableHandle2Index(hHandle);
125 Assert(iIndex < pTbl->cSize);
126 if (iIndex < pTbl->cSize)
127 return pTbl->paData[iIndex];
128 return NULL;
129}
130
131VOID vboxWddmHTableIterInit(PVBOXWDDM_HTABLE pTbl, PVBOXWDDM_HTABLE_ITERATOR pIter)
132{
133 pIter->pTbl = pTbl;
134 pIter->iCur = ~0UL;
135 pIter->cLeft = pTbl->cData;
136}
137
138BOOL vboxWddmHTableIterHasNext(PVBOXWDDM_HTABLE_ITERATOR pIter)
139{
140 return pIter->cLeft;
141}
142
143
144PVOID vboxWddmHTableIterNext(PVBOXWDDM_HTABLE_ITERATOR pIter, VBOXWDDM_HANDLE *phHandle)
145{
146 if (vboxWddmHTableIterHasNext(pIter))
147 {
148 for (uint32_t i = pIter->iCur+1; i < pIter->pTbl->cSize ; ++i)
149 {
150 if (pIter->pTbl->paData[i])
151 {
152 pIter->iCur = i;
153 --pIter->cLeft;
154 VBOXWDDM_HANDLE hHandle = vboxWddmHTableIndex2Handle(i);
155 Assert(hHandle);
156 if (phHandle)
157 *phHandle = hHandle;
158 return pIter->pTbl->paData[i];
159 }
160 }
161 }
162
163 Assert(!vboxWddmHTableIterHasNext(pIter));
164 if (phHandle)
165 *phHandle = VBOXWDDM_HANDLE_INVALID;
166 return NULL;
167}
168
169
170PVOID vboxWddmHTableIterRemoveCur(PVBOXWDDM_HTABLE_ITERATOR pIter)
171{
172 VBOXWDDM_HANDLE hHandle = vboxWddmHTableIndex2Handle(pIter->iCur);
173 Assert(hHandle);
174 if (hHandle)
175 {
176 PVOID pRet = vboxWddmHTableRemove(pIter->pTbl, hHandle);
177 Assert(pRet);
178 return pRet;
179 }
180 return NULL;
181}
182
183PVBOXWDDM_SWAPCHAIN vboxWddmSwapchainCreate(UINT w, UINT h)
184{
185 PVBOXWDDM_SWAPCHAIN pSwapchain = (PVBOXWDDM_SWAPCHAIN)vboxWddmMemAllocZero(sizeof (VBOXWDDM_SWAPCHAIN));
186 Assert(pSwapchain);
187 if (pSwapchain)
188 {
189 InitializeListHead(&pSwapchain->AllocList);
190 pSwapchain->enmState = VBOXWDDM_OBJSTATE_TYPE_INITIALIZED;
191 pSwapchain->cRefs = 1;
192 /* init to some invalid value so that the pos get submitted */
193 pSwapchain->Pos.x = pSwapchain->Pos.y = VBOXWDDM_INVALID_COORD;
194 pSwapchain->width = w;
195 pSwapchain->height = h;
196 VBoxVrListInit(&pSwapchain->VisibleRegions);
197 }
198 return pSwapchain;
199}
200
201DECLINLINE(BOOLEAN) vboxWddmSwapchainRetainLocked(PVBOXWDDM_SWAPCHAIN pSwapchain)
202{
203 if (pSwapchain->enmState == VBOXWDDM_OBJSTATE_TYPE_INITIALIZED)
204 {
205 ASMAtomicIncU32(&pSwapchain->cRefs);
206 return TRUE;
207 }
208 return FALSE;
209}
210
211BOOLEAN vboxWddmSwapchainRetain(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_SWAPCHAIN pSwapchain)
212{
213 KIRQL OldIrql;
214 BOOLEAN bRc;
215 KeAcquireSpinLock(&pDevExt->SynchLock, &OldIrql);
216 bRc = vboxWddmSwapchainRetainLocked(pSwapchain);
217 KeReleaseSpinLock(&pDevExt->SynchLock, OldIrql);
218 return bRc;
219}
220
221VOID vboxWddmSwapchainRelease(PVBOXWDDM_SWAPCHAIN pSwapchain)
222{
223 const uint32_t cRefs = ASMAtomicDecU32(&pSwapchain->cRefs);
224 Assert(cRefs < UINT32_MAX/2);
225 if (!cRefs)
226 {
227 VBoxVrListClear(&pSwapchain->VisibleRegions);
228 vboxWddmMemFree(pSwapchain);
229 }
230}
231
232PVBOXWDDM_SWAPCHAIN vboxWddmSwapchainRetainByAllocData(PVBOXMP_DEVEXT pDevExt, const struct VBOXWDDM_ALLOC_DATA *pAllocData)
233{
234 KIRQL OldIrql;
235 PVBOXWDDM_SWAPCHAIN pSwapchain;
236 KeAcquireSpinLock(&pDevExt->SynchLock, &OldIrql);
237 pSwapchain = pAllocData->pSwapchain;
238 if (pSwapchain && !vboxWddmSwapchainRetainLocked(pSwapchain))
239 pSwapchain = NULL;
240 KeReleaseSpinLock(&pDevExt->SynchLock, OldIrql);
241 return pSwapchain;
242}
243
244PVBOXWDDM_SWAPCHAIN vboxWddmSwapchainRetainByAlloc(PVBOXMP_DEVEXT pDevExt, const VBOXWDDM_ALLOCATION *pAlloc)
245{
246 return vboxWddmSwapchainRetainByAllocData(pDevExt, &pAlloc->AllocData);
247}
248
249VOID vboxWddmSwapchainAllocRemove(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_SWAPCHAIN pSwapchain, PVBOXWDDM_ALLOCATION pAlloc)
250{
251 KIRQL OldIrql;
252 KeAcquireSpinLock(&pDevExt->SynchLock, &OldIrql);
253 Assert(pAlloc->AllocData.pSwapchain == pSwapchain);
254 pAlloc->AllocData.pSwapchain = NULL;
255 RemoveEntryList(&pAlloc->SwapchainEntry);
256 KeReleaseSpinLock(&pDevExt->SynchLock, OldIrql);
257 vboxWddmSwapchainRelease(pSwapchain);
258}
259
260BOOLEAN vboxWddmSwapchainAllocAdd(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_SWAPCHAIN pSwapchain, PVBOXWDDM_ALLOCATION pAlloc)
261{
262 KIRQL OldIrql;
263 BOOLEAN bRc;
264 Assert(!pAlloc->AllocData.pSwapchain);
265 KeAcquireSpinLock(&pDevExt->SynchLock, &OldIrql);
266 bRc = vboxWddmSwapchainRetainLocked(pSwapchain);
267 if (bRc)
268 {
269 if (pAlloc->AllocData.pSwapchain)
270 {
271 RemoveEntryList(&pAlloc->SwapchainEntry);
272 }
273 InsertTailList(&pSwapchain->AllocList, &pAlloc->SwapchainEntry);
274 pAlloc->AllocData.pSwapchain = pSwapchain;
275 }
276 KeReleaseSpinLock(&pDevExt->SynchLock, OldIrql);
277 return bRc;
278}
279
280#define VBOXSCENTRY_2_ALLOC(_pE) ((PVBOXWDDM_ALLOCATION)((uint8_t*)(_pE) - RT_OFFSETOF(VBOXWDDM_ALLOCATION, SwapchainEntry)))
281
282static VOID vboxWddmSwapchainAllocRemoveAllInternal(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_SWAPCHAIN pSwapchain, BOOLEAN bOnDestroy)
283{
284 KIRQL OldIrql;
285 UINT cRemoved = 0;
286 KeAcquireSpinLock(&pDevExt->SynchLock, &OldIrql);
287 PLIST_ENTRY pEntry = pSwapchain->AllocList.Flink;
288 do
289 {
290 if (pEntry != &pSwapchain->AllocList)
291 {
292 PVBOXWDDM_ALLOCATION pAlloc = VBOXSCENTRY_2_ALLOC(pEntry);
293 pEntry = pEntry->Flink;
294 Assert(pAlloc->AllocData.pSwapchain == pSwapchain);
295 pAlloc->AllocData.pSwapchain = NULL;
296 RemoveEntryList(&pAlloc->SwapchainEntry);
297 ++cRemoved;
298 }
299 else
300 break;
301 } while (1);
302
303 if (bOnDestroy)
304 pSwapchain->enmState = VBOXWDDM_OBJSTATE_TYPE_TERMINATED;
305 KeReleaseSpinLock(&pDevExt->SynchLock, OldIrql);
306
307 for (UINT i = 0; i < cRemoved; ++i)
308 vboxWddmSwapchainRelease(pSwapchain);
309}
310
311VOID vboxWddmSwapchainAllocRemoveAll(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_SWAPCHAIN pSwapchain)
312{
313 vboxWddmSwapchainAllocRemoveAllInternal(pDevExt, pSwapchain, FALSE);
314}
315
316VOID vboxWddmSwapchainDestroy(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_SWAPCHAIN pSwapchain)
317{
318 vboxWddmSwapchainAllocRemoveAllInternal(pDevExt, pSwapchain, TRUE);
319
320 vboxWddmSwapchainRelease(pSwapchain);
321}
322
323static BOOLEAN vboxWddmSwapchainCtxAddLocked(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext, PVBOXWDDM_SWAPCHAIN pSwapchain)
324{
325 if (vboxWddmSwapchainRetain(pDevExt, pSwapchain))
326 {
327 Assert(!pSwapchain->hSwapchainKm);
328 Assert(!pSwapchain->pContext);
329 pSwapchain->pContext = pContext;
330 pSwapchain->hSwapchainKm = vboxWddmHTablePut(&pContext->Swapchains, pSwapchain);
331 InsertHeadList(&pDevExt->SwapchainList3D, &pSwapchain->DevExtListEntry);
332 return TRUE;
333 }
334 return FALSE;
335}
336
337static VOID vboxWddmSwapchainCtxRemoveLocked(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext, PVBOXWDDM_SWAPCHAIN pSwapchain)
338{
339 Assert(pSwapchain->hSwapchainKm);
340 Assert(pSwapchain->pContext);
341 void * pTst = vboxWddmHTableRemove(&pContext->Swapchains, pSwapchain->hSwapchainKm);
342 Assert(pTst == pSwapchain);
343 RemoveEntryList(&pSwapchain->DevExtListEntry);
344 pSwapchain->hSwapchainKm = NULL;
345 VBoxVrListClear(&pSwapchain->VisibleRegions);
346 vboxWddmSwapchainRelease(pSwapchain);
347}
348
349/* adds the given swapchain to the context's swapchain list
350 * @return true on success */
351BOOLEAN vboxWddmSwapchainCtxAdd(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext, PVBOXWDDM_SWAPCHAIN pSwapchain)
352{
353 BOOLEAN bRc;
354 VBOXWDDM_CTXLOCK_DATA
355 VBOXWDDM_CTXLOCK_LOCK(pDevExt);
356 bRc = vboxWddmSwapchainCtxAddLocked(pDevExt, pContext, pSwapchain);
357 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
358 return bRc;
359}
360
361/* removes the given swapchain from the context's swapchain list
362 * */
363VOID vboxWddmSwapchainCtxRemove(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext, PVBOXWDDM_SWAPCHAIN pSwapchain)
364{
365 VBOXWDDM_CTXLOCK_DATA
366 VBOXWDDM_CTXLOCK_LOCK(pDevExt);
367 vboxWddmSwapchainCtxRemoveLocked(pDevExt, pContext, pSwapchain);
368 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
369}
370
371/* destroys all swapchains for the given context
372 * */
373VOID vboxWddmSwapchainCtxDestroyAll(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext)
374{
375 VBOXWDDM_HTABLE_ITERATOR Iter;
376 VBOXWDDM_CTXLOCK_DATA
377 do
378 {
379 VBOXWDDM_CTXLOCK_LOCK(pDevExt);
380 vboxWddmHTableIterInit(&pContext->Swapchains, &Iter);
381 PVBOXWDDM_SWAPCHAIN pSwapchain = (PVBOXWDDM_SWAPCHAIN)vboxWddmHTableIterNext(&Iter, NULL);
382 if (!pSwapchain)
383 break;
384
385 /* yes, we can call remove locked even when using iterator */
386 vboxWddmSwapchainCtxRemoveLocked(pDevExt, pContext, pSwapchain);
387
388 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
389 /* we must not do vboxWddmSwapchainDestroy inside a context mutex */
390 vboxWddmSwapchainDestroy(pDevExt, pSwapchain);
391 /* start from the very beginning, we will quit the loop when no swapchains left */
392 } while (1);
393
394 /* no swapchains left, we exiteed the while loop via the "break", and we still owning the mutex */
395 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
396}
397
398/* process the swapchain info passed from user-mode display driver & synchronizes the driver state with it */
399NTSTATUS vboxWddmSwapchainCtxEscape(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext, PVBOXDISPIFESCAPE_SWAPCHAININFO pSwapchainInfo, UINT cbSize)
400{
401 if (cbSize < RT_OFFSETOF(VBOXDISPIFESCAPE_SWAPCHAININFO, SwapchainInfo.ahAllocs[0]))
402 {
403 WARN(("invalid cbSize1 %d", cbSize));
404 return STATUS_INVALID_PARAMETER;
405 }
406
407 if (cbSize < RT_OFFSETOF(VBOXDISPIFESCAPE_SWAPCHAININFO, SwapchainInfo.ahAllocs[pSwapchainInfo->SwapchainInfo.cAllocs]))
408 {
409 return STATUS_INVALID_PARAMETER;
410 WARN(("invalid cbSize2 %d", cbSize));
411 }
412
413 if (!pSwapchainInfo->SwapchainInfo.winHostID)
414 {
415 WARN(("Zero winHostID specified!"));
416 return STATUS_INVALID_PARAMETER;
417 }
418
419 if (!pContext)
420 {
421 WARN(("vboxWddmSwapchainCtxEscape: no context specified"));
422 return STATUS_INVALID_PARAMETER;
423 }
424
425 PVBOXWDDM_SWAPCHAIN pSwapchain = NULL;
426 PVBOXWDDM_ALLOCATION *apAlloc = NULL;
427 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
428 NTSTATUS Status = STATUS_SUCCESS;
429 VBOXWDDM_CTXLOCK_DATA
430
431 do {
432 if (pSwapchainInfo->SwapchainInfo.cAllocs)
433 {
434 /* ensure we do not overflow the 32bit buffer size value */
435 if (VBOXWDDM_ARRAY_MAXELEMENTSU32(VBOXWDDM_ALLOCATION) < pSwapchainInfo->SwapchainInfo.cAllocs)
436 {
437 WARN(("number of allocations passed in too big (%d), max is (%d)", pSwapchainInfo->SwapchainInfo.cAllocs, VBOXWDDM_ARRAY_MAXELEMENTSU32(VBOXWDDM_ALLOCATION)));
438 Status = STATUS_INVALID_PARAMETER;
439 break;
440 }
441
442 apAlloc = (PVBOXWDDM_ALLOCATION *)vboxWddmMemAlloc(sizeof (PVBOXWDDM_ALLOCATION) * pSwapchainInfo->SwapchainInfo.cAllocs);
443 Assert(apAlloc);
444 if (!apAlloc)
445 {
446 Status = STATUS_NO_MEMORY;
447 break;
448 }
449 for (UINT i = 0; i < pSwapchainInfo->SwapchainInfo.cAllocs; ++i)
450 {
451 DXGKARGCB_GETHANDLEDATA GhData;
452 GhData.hObject = pSwapchainInfo->SwapchainInfo.ahAllocs[i];
453 GhData.Type = DXGK_HANDLE_ALLOCATION;
454 GhData.Flags.Value = 0;
455 PVBOXWDDM_ALLOCATION pAlloc = (PVBOXWDDM_ALLOCATION)pDevExt->u.primary.DxgkInterface.DxgkCbGetHandleData(&GhData);
456 Assert(pAlloc);
457 if (!pAlloc)
458 {
459 Status = STATUS_INVALID_PARAMETER;
460 break;
461 }
462 apAlloc[i] = pAlloc;
463 }
464
465 if (!NT_SUCCESS(Status))
466 break;
467 }
468
469 if (pSwapchainInfo->SwapchainInfo.hSwapchainKm)
470 {
471 VBOXWDDM_CTXLOCK_LOCK(pDevExt);
472 pSwapchain = (PVBOXWDDM_SWAPCHAIN)vboxWddmHTableGet(&pContext->Swapchains, (VBOXWDDM_HANDLE)pSwapchainInfo->SwapchainInfo.hSwapchainKm);
473 Assert(pSwapchain);
474 if (!pSwapchain)
475 {
476 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
477 Status = STATUS_INVALID_PARAMETER;
478 break;
479 }
480 Assert(pSwapchain->hSwapchainKm == pSwapchainInfo->SwapchainInfo.hSwapchainKm);
481 Assert(pSwapchain->pContext == pContext);
482 if (pSwapchain->pContext != pContext)
483 {
484 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
485 Status = STATUS_INVALID_PARAMETER;
486 break;
487 }
488 }
489 else if (pSwapchainInfo->SwapchainInfo.cAllocs)
490 {
491 pSwapchain = vboxWddmSwapchainCreate(apAlloc[0]->AllocData.SurfDesc.width, apAlloc[0]->AllocData.SurfDesc.height);
492 if (!pSwapchain)
493 {
494 Status = STATUS_NO_MEMORY;
495 break;
496 }
497
498 VBOXWDDM_CTXLOCK_LOCK(pDevExt);
499 BOOLEAN bRc = vboxWddmSwapchainCtxAddLocked(pDevExt, pContext, pSwapchain);
500 Assert(bRc);
501 }
502 else
503 {
504 Status = STATUS_INVALID_PARAMETER;
505 break;
506 }
507
508 /* do not zero up the view rect since it may still be valid */
509// memset(&pSwapchain->ViewRect, 0, sizeof (pSwapchain->ViewRect));
510 /* @todo: do we really need to zero this up here ? */
511 VBoxVrListClear(&pSwapchain->VisibleRegions);
512
513 vboxWddmSwapchainAllocRemoveAll(pDevExt, pSwapchain);
514
515 if (pSwapchainInfo->SwapchainInfo.cAllocs)
516 {
517 for (UINT i = 0; i < pSwapchainInfo->SwapchainInfo.cAllocs; ++i)
518 {
519 vboxWddmSwapchainAllocAdd(pDevExt, pSwapchain, apAlloc[i]);
520 }
521 pSwapchain->hSwapchainUm = pSwapchainInfo->SwapchainInfo.hSwapchainUm;
522 if (pSwapchain->winHostID != pSwapchainInfo->SwapchainInfo.winHostID)
523 {
524 pSwapchain->fExposed = FALSE;
525 pSwapchain->winHostID = pSwapchainInfo->SwapchainInfo.winHostID;
526 }
527 }
528 else
529 {
530 vboxWddmSwapchainCtxRemoveLocked(pDevExt, pContext, pSwapchain);
531 }
532
533 VBOXWDDM_CTXLOCK_UNLOCK(pDevExt);
534
535 if (pSwapchainInfo->SwapchainInfo.cAllocs)
536 {
537 Assert(pSwapchain->pContext);
538 Assert(pSwapchain->hSwapchainKm);
539 pSwapchainInfo->SwapchainInfo.hSwapchainKm = pSwapchain->hSwapchainKm;
540 }
541 else
542 {
543 vboxWddmSwapchainDestroy(pDevExt, pSwapchain);
544 pSwapchainInfo->SwapchainInfo.hSwapchainKm = 0;
545 }
546
547 Assert(Status == STATUS_SUCCESS);
548 } while (0);
549
550 /* cleanup */
551 if (apAlloc)
552 vboxWddmMemFree(apAlloc);
553
554 return Status;
555}
556
557NTSTATUS vboxWddmSwapchainCtxInit(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext)
558{
559 NTSTATUS Status = vboxWddmHTableCreate(&pContext->Swapchains, 4);
560 if (!NT_SUCCESS(Status))
561 {
562 WARN(("vboxWddmHTableCreate failes, Status (x%x)", Status));
563 return Status;
564 }
565
566 return STATUS_SUCCESS;
567}
568
569VOID vboxWddmSwapchainCtxTerm(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_CONTEXT pContext)
570{
571 vboxWddmSwapchainCtxDestroyAll(pDevExt, pContext);
572 vboxWddmHTableDestroy(&pContext->Swapchains);
573}
574
575NTSTATUS vboxWddmRegQueryDrvKeyName(PVBOXMP_DEVEXT pDevExt, ULONG cbBuf, PWCHAR pBuf, PULONG pcbResult)
576{
577 WCHAR fallBackBuf[2];
578 PWCHAR pSuffix;
579 bool bFallback = false;
580
581 if (cbBuf > sizeof(VBOXWDDM_REG_DRVKEY_PREFIX))
582 {
583 memcpy(pBuf, VBOXWDDM_REG_DRVKEY_PREFIX, sizeof (VBOXWDDM_REG_DRVKEY_PREFIX));
584 pSuffix = pBuf + (sizeof (VBOXWDDM_REG_DRVKEY_PREFIX)-2)/2;
585 cbBuf -= sizeof (VBOXWDDM_REG_DRVKEY_PREFIX)-2;
586 }
587 else
588 {
589 pSuffix = fallBackBuf;
590 cbBuf = sizeof (fallBackBuf);
591 bFallback = true;
592 }
593
594 NTSTATUS Status = IoGetDeviceProperty (pDevExt->pPDO,
595 DevicePropertyDriverKeyName,
596 cbBuf,
597 pSuffix,
598 &cbBuf);
599 if (Status == STATUS_SUCCESS && bFallback)
600 Status = STATUS_BUFFER_TOO_SMALL;
601 if (Status == STATUS_BUFFER_TOO_SMALL)
602 *pcbResult = cbBuf + sizeof (VBOXWDDM_REG_DRVKEY_PREFIX)-2;
603
604 return Status;
605}
606
607NTSTATUS vboxWddmRegQueryDisplaySettingsKeyName(PVBOXMP_DEVEXT pDevExt, D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId,
608 ULONG cbBuf, PWCHAR pBuf, PULONG pcbResult)
609{
610 NTSTATUS Status = STATUS_SUCCESS;
611 PWCHAR pSuffix;
612 bool bFallback = false;
613 const WCHAR* pKeyPrefix;
614 UINT cbKeyPrefix;
615 UNICODE_STRING* pVGuid = vboxWddmVGuidGet(pDevExt);
616 Assert(pVGuid);
617 if (!pVGuid)
618 return STATUS_UNSUCCESSFUL;
619
620 vboxWinVersion_t ver = VBoxQueryWinVersion();
621 if (ver == WINVISTA)
622 {
623 pKeyPrefix = VBOXWDDM_REG_DISPLAYSETTINGSKEY_PREFIX_VISTA;
624 cbKeyPrefix = sizeof (VBOXWDDM_REG_DISPLAYSETTINGSKEY_PREFIX_VISTA);
625 }
626 else
627 {
628 Assert(ver == WIN7 || ver == WIN8 || ver == WIN81);
629 pKeyPrefix = VBOXWDDM_REG_DISPLAYSETTINGSKEY_PREFIX_WIN7;
630 cbKeyPrefix = sizeof (VBOXWDDM_REG_DISPLAYSETTINGSKEY_PREFIX_WIN7);
631 }
632
633 ULONG cbResult = cbKeyPrefix + pVGuid->Length + 2 + 8; // L"\\" + "XXXX"
634 if (cbBuf >= cbResult)
635 {
636 wcscpy(pBuf, pKeyPrefix);
637 pSuffix = pBuf + (cbKeyPrefix-2)/2;
638 memcpy(pSuffix, pVGuid->Buffer, pVGuid->Length);
639 pSuffix += pVGuid->Length/2;
640 pSuffix[0] = L'\\';
641 pSuffix += 1;
642 swprintf(pSuffix, L"%04d", VidPnSourceId);
643 }
644 else
645 {
646 Status = STATUS_BUFFER_TOO_SMALL;
647 }
648
649 *pcbResult = cbResult;
650
651 return Status;
652}
653
654NTSTATUS vboxWddmRegQueryVideoGuidString(PVBOXMP_DEVEXT pDevExt, ULONG cbBuf, PWCHAR pBuf, PULONG pcbResult)
655{
656 BOOLEAN fNewMethodSucceeded = FALSE;
657 HANDLE hKey = NULL;
658 NTSTATUS Status = IoOpenDeviceRegistryKey(pDevExt->pPDO, PLUGPLAY_REGKEY_DEVICE, GENERIC_READ, &hKey);
659 if (NT_SUCCESS(Status))
660 {
661 struct
662 {
663 KEY_VALUE_PARTIAL_INFORMATION Info;
664 UCHAR Buf[1024]; /* should be enough */
665 } KeyData;
666 ULONG cbResult;
667 UNICODE_STRING RtlStr;
668 RtlInitUnicodeString(&RtlStr, L"VideoID");
669 Status = ZwQueryValueKey(hKey,
670 &RtlStr,
671 KeyValuePartialInformation,
672 &KeyData.Info,
673 sizeof(KeyData),
674 &cbResult);
675 if (NT_SUCCESS(Status))
676 {
677 if (KeyData.Info.Type == REG_SZ)
678 {
679 fNewMethodSucceeded = TRUE;
680 *pcbResult = KeyData.Info.DataLength + 2;
681 if (cbBuf >= KeyData.Info.DataLength)
682 {
683 memcpy(pBuf, KeyData.Info.Data, KeyData.Info.DataLength + 2);
684 Status = STATUS_SUCCESS;
685 }
686 else
687 Status = STATUS_BUFFER_TOO_SMALL;
688 }
689 }
690 else
691 {
692 WARN(("ZwQueryValueKey failed, Status 0x%x", Status));
693 }
694
695 NTSTATUS tmpStatus = ZwClose(hKey);
696 Assert(tmpStatus == STATUS_SUCCESS);
697 }
698 else
699 {
700 WARN(("IoOpenDeviceRegistryKey failed Status 0x%x", Status));
701 }
702
703 if (fNewMethodSucceeded)
704 return Status;
705 else
706 WARN(("failed to acquire the VideoID, falling back to the old impl"));
707
708 Status = vboxWddmRegOpenKey(&hKey, VBOXWDDM_REG_DISPLAYSETTINGSVIDEOKEY, GENERIC_READ);
709 Assert(Status == STATUS_SUCCESS);
710 if (Status == STATUS_SUCCESS)
711 {
712 struct
713 {
714 KEY_BASIC_INFORMATION Name;
715 WCHAR Buf[256];
716 } Buf;
717 WCHAR KeyBuf[sizeof (VBOXWDDM_REG_DISPLAYSETTINGSVIDEOKEY)/2 + 256 + 64];
718 wcscpy(KeyBuf, VBOXWDDM_REG_DISPLAYSETTINGSVIDEOKEY);
719 ULONG ResultLength;
720 BOOL bFound = FALSE;
721 for (ULONG i = 0; !bFound; ++i)
722 {
723 RtlZeroMemory(&Buf, sizeof (Buf));
724 Status = ZwEnumerateKey(hKey, i, KeyBasicInformation, &Buf, sizeof (Buf), &ResultLength);
725 Assert(Status == STATUS_SUCCESS);
726 /* we should not encounter STATUS_NO_MORE_ENTRIES here since this would mean we did not find our entry */
727 if (Status != STATUS_SUCCESS)
728 break;
729
730 HANDLE hSubKey;
731 PWCHAR pSubBuf = KeyBuf + (sizeof (VBOXWDDM_REG_DISPLAYSETTINGSVIDEOKEY) - 2)/2;
732 memcpy(pSubBuf, Buf.Name.Name, Buf.Name.NameLength);
733 pSubBuf += Buf.Name.NameLength/2;
734 memcpy(pSubBuf, VBOXWDDM_REG_DISPLAYSETTINGSVIDEOKEY_SUBKEY, sizeof (VBOXWDDM_REG_DISPLAYSETTINGSVIDEOKEY_SUBKEY));
735 Status = vboxWddmRegOpenKey(&hSubKey, KeyBuf, GENERIC_READ);
736 Assert(Status == STATUS_SUCCESS);
737 if (Status == STATUS_SUCCESS)
738 {
739 struct
740 {
741 KEY_VALUE_PARTIAL_INFORMATION Info;
742 UCHAR Buf[sizeof (VBOX_WDDM_DRIVERNAME)]; /* should be enough */
743 } KeyData;
744 ULONG cbResult;
745 UNICODE_STRING RtlStr;
746 RtlInitUnicodeString(&RtlStr, L"Service");
747 Status = ZwQueryValueKey(hSubKey,
748 &RtlStr,
749 KeyValuePartialInformation,
750 &KeyData.Info,
751 sizeof(KeyData),
752 &cbResult);
753 Assert(Status == STATUS_SUCCESS || STATUS_BUFFER_TOO_SMALL || STATUS_BUFFER_OVERFLOW);
754 if (Status == STATUS_SUCCESS)
755 {
756 if (KeyData.Info.Type == REG_SZ)
757 {
758 if (KeyData.Info.DataLength == sizeof (VBOX_WDDM_DRIVERNAME))
759 {
760 if (!wcscmp(VBOX_WDDM_DRIVERNAME, (PWCHAR)KeyData.Info.Data))
761 {
762 bFound = TRUE;
763 *pcbResult = Buf.Name.NameLength + 2;
764 if (cbBuf >= Buf.Name.NameLength + 2)
765 {
766 memcpy(pBuf, Buf.Name.Name, Buf.Name.NameLength + 2);
767 }
768 else
769 {
770 Status = STATUS_BUFFER_TOO_SMALL;
771 }
772 }
773 }
774 }
775 }
776
777 NTSTATUS tmpStatus = ZwClose(hSubKey);
778 Assert(tmpStatus == STATUS_SUCCESS);
779 }
780 else
781 break;
782 }
783 NTSTATUS tmpStatus = ZwClose(hKey);
784 Assert(tmpStatus == STATUS_SUCCESS);
785 }
786
787 return Status;
788}
789
790NTSTATUS vboxWddmRegOpenKeyEx(OUT PHANDLE phKey, IN HANDLE hRootKey, IN PWCHAR pName, IN ACCESS_MASK fAccess)
791{
792 OBJECT_ATTRIBUTES ObjAttr;
793 UNICODE_STRING RtlStr;
794
795 RtlInitUnicodeString(&RtlStr, pName);
796 InitializeObjectAttributes(&ObjAttr, &RtlStr, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, hRootKey, NULL);
797
798 return ZwOpenKey(phKey, fAccess, &ObjAttr);
799}
800
801NTSTATUS vboxWddmRegOpenKey(OUT PHANDLE phKey, IN PWCHAR pName, IN ACCESS_MASK fAccess)
802{
803 return vboxWddmRegOpenKeyEx(phKey, NULL, pName, fAccess);
804}
805
806NTSTATUS vboxWddmRegOpenDisplaySettingsKey(IN PVBOXMP_DEVEXT pDeviceExtension, D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId, OUT PHANDLE phKey)
807{
808 WCHAR Buf[512];
809 ULONG cbBuf = sizeof(Buf);
810 NTSTATUS Status = vboxWddmRegQueryDisplaySettingsKeyName(pDeviceExtension, VidPnSourceId, cbBuf, Buf, &cbBuf);
811 Assert(Status == STATUS_SUCCESS);
812 if (Status == STATUS_SUCCESS)
813 {
814 Status = vboxWddmRegOpenKey(phKey, Buf, GENERIC_READ);
815 Assert(Status == STATUS_SUCCESS);
816 if(Status == STATUS_SUCCESS)
817 return STATUS_SUCCESS;
818 }
819
820 /* fall-back to make the subsequent VBoxVideoCmnRegXxx calls treat the fail accordingly
821 * basically needed to make as less modifications to the current XPDM code as possible */
822 *phKey = NULL;
823
824 return Status;
825}
826
827NTSTATUS vboxWddmRegDisplaySettingsQueryRelX(HANDLE hKey, int * pResult)
828{
829 DWORD dwVal;
830 NTSTATUS Status = vboxWddmRegQueryValueDword(hKey, VBOXWDDM_REG_DISPLAYSETTINGS_ATTACH_RELX, &dwVal);
831 Assert(Status == STATUS_SUCCESS);
832 if (Status == STATUS_SUCCESS)
833 {
834 *pResult = (int)dwVal;
835 }
836
837 return Status;
838}
839
840NTSTATUS vboxWddmRegDisplaySettingsQueryRelY(HANDLE hKey, int * pResult)
841{
842 DWORD dwVal;
843 NTSTATUS Status = vboxWddmRegQueryValueDword(hKey, VBOXWDDM_REG_DISPLAYSETTINGS_ATTACH_RELY, &dwVal);
844 Assert(Status == STATUS_SUCCESS);
845 if (Status == STATUS_SUCCESS)
846 {
847 *pResult = (int)dwVal;
848 }
849
850 return Status;
851}
852
853NTSTATUS vboxWddmDisplaySettingsQueryPos(IN PVBOXMP_DEVEXT pDeviceExtension, D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId, POINT * pPos)
854{
855 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
856 HANDLE hKey;
857 NTSTATUS Status = vboxWddmRegOpenDisplaySettingsKey(pDeviceExtension, VidPnSourceId, &hKey);
858 Assert(Status == STATUS_SUCCESS);
859 if (Status == STATUS_SUCCESS)
860 {
861 int x, y;
862 Status = vboxWddmRegDisplaySettingsQueryRelX(hKey, &x);
863 Assert(Status == STATUS_SUCCESS);
864 if (Status == STATUS_SUCCESS)
865 {
866 Status = vboxWddmRegDisplaySettingsQueryRelY(hKey, &y);
867 Assert(Status == STATUS_SUCCESS);
868 if (Status == STATUS_SUCCESS)
869 {
870 pPos->x = x;
871 pPos->y = y;
872 }
873 }
874 NTSTATUS tmpStatus = ZwClose(hKey);
875 Assert(tmpStatus == STATUS_SUCCESS);
876 }
877
878 return Status;
879}
880
881NTSTATUS vboxWddmRegDrvFlagsSet(PVBOXMP_DEVEXT pDevExt, DWORD fVal)
882{
883 HANDLE hKey = NULL;
884 NTSTATUS Status = IoOpenDeviceRegistryKey(pDevExt->pPDO, PLUGPLAY_REGKEY_DRIVER, GENERIC_WRITE, &hKey);
885 if (!NT_SUCCESS(Status))
886 {
887 WARN(("IoOpenDeviceRegistryKey failed, Status = 0x%x", Status));
888 return Status;
889 }
890
891 Status = vboxWddmRegSetValueDword(hKey, VBOXWDDM_REG_DRV_FLAGS_NAME, fVal);
892 if (!NT_SUCCESS(Status))
893 WARN(("vboxWddmRegSetValueDword failed, Status = 0x%x", Status));
894
895 NTSTATUS tmpStatus = ZwClose(hKey);
896 Assert(tmpStatus == STATUS_SUCCESS);
897
898 return Status;
899}
900
901DWORD vboxWddmRegDrvFlagsGet(PVBOXMP_DEVEXT pDevExt, DWORD fDefault)
902{
903 HANDLE hKey = NULL;
904 NTSTATUS Status = IoOpenDeviceRegistryKey(pDevExt->pPDO, PLUGPLAY_REGKEY_DRIVER, GENERIC_READ, &hKey);
905 if (!NT_SUCCESS(Status))
906 {
907 WARN(("IoOpenDeviceRegistryKey failed, Status = 0x%x", Status));
908 return fDefault;
909 }
910
911 DWORD dwVal = 0;
912 Status = vboxWddmRegQueryValueDword(hKey, VBOXWDDM_REG_DRV_FLAGS_NAME, &dwVal);
913 if (!NT_SUCCESS(Status))
914 {
915 WARN(("vboxWddmRegQueryValueDword failed, Status = 0x%x", Status));
916 dwVal = fDefault;
917 }
918
919 NTSTATUS tmpStatus = ZwClose(hKey);
920 Assert(tmpStatus == STATUS_SUCCESS);
921
922 return dwVal;
923}
924
925NTSTATUS vboxWddmRegQueryValueDword(IN HANDLE hKey, IN PWCHAR pName, OUT PDWORD pDword)
926{
927 struct
928 {
929 KEY_VALUE_PARTIAL_INFORMATION Info;
930 UCHAR Buf[32]; /* should be enough */
931 } Buf;
932 ULONG cbBuf;
933 UNICODE_STRING RtlStr;
934 RtlInitUnicodeString(&RtlStr, pName);
935 NTSTATUS Status = ZwQueryValueKey(hKey,
936 &RtlStr,
937 KeyValuePartialInformation,
938 &Buf.Info,
939 sizeof(Buf),
940 &cbBuf);
941 if (Status == STATUS_SUCCESS)
942 {
943 if (Buf.Info.Type == REG_DWORD)
944 {
945 Assert(Buf.Info.DataLength == 4);
946 *pDword = *((PULONG)Buf.Info.Data);
947 return STATUS_SUCCESS;
948 }
949 }
950
951 return STATUS_INVALID_PARAMETER;
952}
953
954NTSTATUS vboxWddmRegSetValueDword(IN HANDLE hKey, IN PWCHAR pName, IN DWORD val)
955{
956 UNICODE_STRING RtlStr;
957 RtlInitUnicodeString(&RtlStr, pName);
958 return ZwSetValueKey(hKey, &RtlStr,
959 NULL, /* IN ULONG TitleIndex OPTIONAL, reserved */
960 REG_DWORD,
961 &val,
962 sizeof(val));
963}
964
965UNICODE_STRING* vboxWddmVGuidGet(PVBOXMP_DEVEXT pDevExt)
966{
967 if (pDevExt->VideoGuid.Buffer)
968 return &pDevExt->VideoGuid;
969
970 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
971 WCHAR VideoGuidBuf[512];
972 ULONG cbVideoGuidBuf = sizeof (VideoGuidBuf);
973 NTSTATUS Status = vboxWddmRegQueryVideoGuidString(pDevExt ,cbVideoGuidBuf, VideoGuidBuf, &cbVideoGuidBuf);
974 Assert(Status == STATUS_SUCCESS);
975 if (Status == STATUS_SUCCESS)
976 {
977 PWCHAR pBuf = (PWCHAR)vboxWddmMemAllocZero(cbVideoGuidBuf);
978 Assert(pBuf);
979 if (pBuf)
980 {
981 memcpy(pBuf, VideoGuidBuf, cbVideoGuidBuf);
982 RtlInitUnicodeString(&pDevExt->VideoGuid, pBuf);
983 return &pDevExt->VideoGuid;
984 }
985 }
986
987 return NULL;
988}
989
990VOID vboxWddmVGuidFree(PVBOXMP_DEVEXT pDevExt)
991{
992 if (pDevExt->VideoGuid.Buffer)
993 {
994 vboxWddmMemFree(pDevExt->VideoGuid.Buffer);
995 pDevExt->VideoGuid.Buffer = NULL;
996 }
997}
998
999/* mm */
1000
1001NTSTATUS vboxMmInit(PVBOXWDDM_MM pMm, UINT cPages)
1002{
1003 UINT cbBuffer = VBOXWDDM_ROUNDBOUND(cPages, 8) >> 3;
1004 cbBuffer = VBOXWDDM_ROUNDBOUND(cbBuffer, 4);
1005 PULONG pBuf = (PULONG)vboxWddmMemAllocZero(cbBuffer);
1006 if (!pBuf)
1007 {
1008 Assert(0);
1009 return STATUS_NO_MEMORY;
1010 }
1011 RtlInitializeBitMap(&pMm->BitMap, pBuf, cPages);
1012 pMm->cPages = cPages;
1013 pMm->cAllocs = 0;
1014 pMm->pBuffer = pBuf;
1015 return STATUS_SUCCESS;
1016}
1017
1018ULONG vboxMmAlloc(PVBOXWDDM_MM pMm, UINT cPages)
1019{
1020 ULONG iPage = RtlFindClearBitsAndSet(&pMm->BitMap, cPages, 0);
1021 if (iPage == 0xFFFFFFFF)
1022 {
1023 Assert(0);
1024 return VBOXWDDM_MM_VOID;
1025 }
1026
1027 ++pMm->cAllocs;
1028 return iPage;
1029}
1030
1031VOID vboxMmFree(PVBOXWDDM_MM pMm, UINT iPage, UINT cPages)
1032{
1033 Assert(RtlAreBitsSet(&pMm->BitMap, iPage, cPages));
1034 RtlClearBits(&pMm->BitMap, iPage, cPages);
1035 --pMm->cAllocs;
1036 Assert(pMm->cAllocs < UINT32_MAX);
1037}
1038
1039NTSTATUS vboxMmTerm(PVBOXWDDM_MM pMm)
1040{
1041 Assert(!pMm->cAllocs);
1042 vboxWddmMemFree(pMm->pBuffer);
1043 pMm->pBuffer = NULL;
1044 return STATUS_SUCCESS;
1045}
1046
1047
1048
1049typedef struct VBOXVIDEOCM_ALLOC
1050{
1051 VBOXWDDM_HANDLE hGlobalHandle;
1052 uint32_t offData;
1053 uint32_t cbData;
1054} VBOXVIDEOCM_ALLOC, *PVBOXVIDEOCM_ALLOC;
1055
1056typedef struct VBOXVIDEOCM_ALLOC_REF
1057{
1058 PVBOXVIDEOCM_ALLOC_CONTEXT pContext;
1059 VBOXWDDM_HANDLE hSessionHandle;
1060 PVBOXVIDEOCM_ALLOC pAlloc;
1061 PKEVENT pSynchEvent;
1062 VBOXUHGSMI_BUFFER_TYPE_FLAGS fUhgsmiType;
1063 volatile uint32_t cRefs;
1064 MDL Mdl;
1065} VBOXVIDEOCM_ALLOC_REF, *PVBOXVIDEOCM_ALLOC_REF;
1066
1067
1068NTSTATUS vboxVideoCmAllocAlloc(PVBOXVIDEOCM_ALLOC_MGR pMgr, PVBOXVIDEOCM_ALLOC pAlloc)
1069{
1070 NTSTATUS Status = STATUS_UNSUCCESSFUL;
1071 UINT cbSize = pAlloc->cbData;
1072 UINT cPages = BYTES_TO_PAGES(cbSize);
1073 ExAcquireFastMutex(&pMgr->Mutex);
1074 UINT iPage = vboxMmAlloc(&pMgr->Mm, cPages);
1075 if (iPage != VBOXWDDM_MM_VOID)
1076 {
1077 uint32_t offData = pMgr->offData + (iPage << PAGE_SHIFT);
1078 Assert(offData + cbSize <= pMgr->offData + pMgr->cbData);
1079 pAlloc->offData = offData;
1080 pAlloc->hGlobalHandle = vboxWddmHTablePut(&pMgr->AllocTable, pAlloc);
1081 ExReleaseFastMutex(&pMgr->Mutex);
1082 if (VBOXWDDM_HANDLE_INVALID != pAlloc->hGlobalHandle)
1083 return STATUS_SUCCESS;
1084
1085 Assert(0);
1086 Status = STATUS_NO_MEMORY;
1087 vboxMmFree(&pMgr->Mm, iPage, cPages);
1088 }
1089 else
1090 {
1091 Assert(0);
1092 ExReleaseFastMutex(&pMgr->Mutex);
1093 Status = STATUS_INSUFFICIENT_RESOURCES;
1094 }
1095 return Status;
1096}
1097
1098VOID vboxVideoCmAllocDealloc(PVBOXVIDEOCM_ALLOC_MGR pMgr, PVBOXVIDEOCM_ALLOC pAlloc)
1099{
1100 UINT cbSize = pAlloc->cbData;
1101 UINT cPages = BYTES_TO_PAGES(cbSize);
1102 UINT iPage = BYTES_TO_PAGES(pAlloc->offData - pMgr->offData);
1103 ExAcquireFastMutex(&pMgr->Mutex);
1104 vboxWddmHTableRemove(&pMgr->AllocTable, pAlloc->hGlobalHandle);
1105 vboxMmFree(&pMgr->Mm, iPage, cPages);
1106 ExReleaseFastMutex(&pMgr->Mutex);
1107}
1108
1109
1110NTSTATUS vboxVideoAMgrAllocCreate(PVBOXVIDEOCM_ALLOC_MGR pMgr, UINT cbSize, PVBOXVIDEOCM_ALLOC *ppAlloc)
1111{
1112 NTSTATUS Status = STATUS_SUCCESS;
1113 PVBOXVIDEOCM_ALLOC pAlloc = (PVBOXVIDEOCM_ALLOC)vboxWddmMemAllocZero(sizeof (*pAlloc));
1114 if (pAlloc)
1115 {
1116 pAlloc->cbData = cbSize;
1117 Status = vboxVideoCmAllocAlloc(pMgr, pAlloc);
1118 if (Status == STATUS_SUCCESS)
1119 {
1120 *ppAlloc = pAlloc;
1121 return STATUS_SUCCESS;
1122 }
1123
1124 Assert(0);
1125 vboxWddmMemFree(pAlloc);
1126 }
1127 else
1128 {
1129 Assert(0);
1130 Status = STATUS_NO_MEMORY;
1131 }
1132
1133 return Status;
1134}
1135
1136VOID vboxVideoAMgrAllocDestroy(PVBOXVIDEOCM_ALLOC_MGR pMgr, PVBOXVIDEOCM_ALLOC pAlloc)
1137{
1138 vboxVideoCmAllocDealloc(pMgr, pAlloc);
1139 vboxWddmMemFree(pAlloc);
1140}
1141
1142NTSTATUS vboxVideoAMgrCtxAllocMap(PVBOXVIDEOCM_ALLOC_CONTEXT pContext, PVBOXVIDEOCM_ALLOC pAlloc, PVBOXVIDEOCM_UM_ALLOC pUmAlloc)
1143{
1144 PVBOXVIDEOCM_ALLOC_MGR pMgr = pContext->pMgr;
1145 NTSTATUS Status = STATUS_SUCCESS;
1146 PKEVENT pSynchEvent = NULL;
1147
1148 if (pUmAlloc->hSynch)
1149 {
1150 Status = ObReferenceObjectByHandle((HANDLE)pUmAlloc->hSynch, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode,
1151 (PVOID*)&pSynchEvent,
1152 NULL);
1153 Assert(Status == STATUS_SUCCESS);
1154 Assert(pSynchEvent);
1155 }
1156
1157 if (Status == STATUS_SUCCESS)
1158 {
1159 PVOID BaseVa = pMgr->pvData + pAlloc->offData - pMgr->offData;
1160 SIZE_T cbLength = pAlloc->cbData;
1161
1162 PVBOXVIDEOCM_ALLOC_REF pAllocRef = (PVBOXVIDEOCM_ALLOC_REF)vboxWddmMemAllocZero(sizeof (*pAllocRef) + sizeof (PFN_NUMBER) * ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseVa, cbLength));
1163 if (pAllocRef)
1164 {
1165 pAllocRef->cRefs = 1;
1166 MmInitializeMdl(&pAllocRef->Mdl, BaseVa, cbLength);
1167 __try
1168 {
1169 MmProbeAndLockPages(&pAllocRef->Mdl, KernelMode, IoWriteAccess);
1170 }
1171 __except(EXCEPTION_EXECUTE_HANDLER)
1172 {
1173 Assert(0);
1174 Status = STATUS_UNSUCCESSFUL;
1175 }
1176
1177 if (Status == STATUS_SUCCESS)
1178 {
1179 PVOID pvUm = MmMapLockedPagesSpecifyCache(&pAllocRef->Mdl, UserMode, MmNonCached,
1180 NULL, /* PVOID BaseAddress */
1181 FALSE, /* ULONG BugCheckOnFailure */
1182 NormalPagePriority);
1183 if (pvUm)
1184 {
1185 pAllocRef->pContext = pContext;
1186 pAllocRef->pAlloc = pAlloc;
1187 pAllocRef->fUhgsmiType = pUmAlloc->fUhgsmiType;
1188 pAllocRef->pSynchEvent = pSynchEvent;
1189 ExAcquireFastMutex(&pContext->Mutex);
1190 pAllocRef->hSessionHandle = vboxWddmHTablePut(&pContext->AllocTable, pAllocRef);
1191 ExReleaseFastMutex(&pContext->Mutex);
1192 if (VBOXWDDM_HANDLE_INVALID != pAllocRef->hSessionHandle)
1193 {
1194 pUmAlloc->hAlloc = pAllocRef->hSessionHandle;
1195 pUmAlloc->cbData = pAlloc->cbData;
1196 pUmAlloc->pvData = (uint64_t)pvUm;
1197 return STATUS_SUCCESS;
1198 }
1199 }
1200 else
1201 {
1202 Assert(0);
1203 Status = STATUS_INSUFFICIENT_RESOURCES;
1204 }
1205
1206 MmUnlockPages(&pAllocRef->Mdl);
1207 }
1208
1209 vboxWddmMemFree(pAllocRef);
1210 }
1211 else
1212 {
1213 Assert(0);
1214 Status = STATUS_NO_MEMORY;
1215 }
1216
1217 if (pSynchEvent)
1218 ObDereferenceObject(pSynchEvent);
1219 }
1220 else
1221 {
1222 Assert(0);
1223 }
1224
1225
1226 return Status;
1227}
1228
1229NTSTATUS vboxVideoAMgrCtxAllocUnmap(PVBOXVIDEOCM_ALLOC_CONTEXT pContext, VBOXDISP_KMHANDLE hSesionHandle, PVBOXVIDEOCM_ALLOC *ppAlloc)
1230{
1231 NTSTATUS Status = STATUS_SUCCESS;
1232 ExAcquireFastMutex(&pContext->Mutex);
1233 PVBOXVIDEOCM_ALLOC_REF pAllocRef = (PVBOXVIDEOCM_ALLOC_REF)vboxWddmHTableRemove(&pContext->AllocTable, hSesionHandle);
1234 ExReleaseFastMutex(&pContext->Mutex);
1235 if (pAllocRef)
1236 {
1237 /* wait for the dereference, i.e. for all commands involving this allocation to complete */
1238 vboxWddmCounterU32Wait(&pAllocRef->cRefs, 1);
1239
1240 MmUnlockPages(&pAllocRef->Mdl);
1241 *ppAlloc = pAllocRef->pAlloc;
1242 if (pAllocRef->pSynchEvent)
1243 ObDereferenceObject(pAllocRef->pSynchEvent);
1244 vboxWddmMemFree(pAllocRef);
1245 }
1246 else
1247 {
1248 Assert(0);
1249 Status = STATUS_INVALID_PARAMETER;
1250 }
1251
1252 return Status;
1253}
1254
1255static PVBOXVIDEOCM_ALLOC_REF vboxVideoAMgrCtxAllocRefAcquire(PVBOXVIDEOCM_ALLOC_CONTEXT pContext, VBOXDISP_KMHANDLE hSesionHandle)
1256{
1257 ExAcquireFastMutex(&pContext->Mutex);
1258 PVBOXVIDEOCM_ALLOC_REF pAllocRef = (PVBOXVIDEOCM_ALLOC_REF)vboxWddmHTableGet(&pContext->AllocTable, hSesionHandle);
1259 ASMAtomicIncU32(&pAllocRef->cRefs);
1260 ExReleaseFastMutex(&pContext->Mutex);
1261 return pAllocRef;
1262}
1263
1264static VOID vboxVideoAMgrCtxAllocRefRelease(PVBOXVIDEOCM_ALLOC_REF pRef)
1265{
1266 uint32_t cRefs = ASMAtomicDecU32(&pRef->cRefs);
1267 Assert(cRefs < UINT32_MAX/2);
1268 Assert(cRefs >= 1); /* we do not do cleanup-on-zero here, instead we wait for the cRefs to reach 1 in vboxVideoAMgrCtxAllocUnmap before unmapping */
1269}
1270
1271
1272
1273NTSTATUS vboxVideoAMgrCtxAllocCreate(PVBOXVIDEOCM_ALLOC_CONTEXT pContext, PVBOXVIDEOCM_UM_ALLOC pUmAlloc)
1274{
1275 PVBOXVIDEOCM_ALLOC pAlloc;
1276 PVBOXVIDEOCM_ALLOC_MGR pMgr = pContext->pMgr;
1277 NTSTATUS Status = vboxVideoAMgrAllocCreate(pMgr, pUmAlloc->cbData, &pAlloc);
1278 if (Status == STATUS_SUCCESS)
1279 {
1280 Status = vboxVideoAMgrCtxAllocMap(pContext, pAlloc, pUmAlloc);
1281 if (Status == STATUS_SUCCESS)
1282 return STATUS_SUCCESS;
1283 else
1284 {
1285 Assert(0);
1286 }
1287 vboxVideoAMgrAllocDestroy(pMgr, pAlloc);
1288 }
1289 else
1290 {
1291 Assert(0);
1292 }
1293 return Status;
1294}
1295
1296NTSTATUS vboxVideoAMgrCtxAllocDestroy(PVBOXVIDEOCM_ALLOC_CONTEXT pContext, VBOXDISP_KMHANDLE hSesionHandle)
1297{
1298 PVBOXVIDEOCM_ALLOC pAlloc;
1299 PVBOXVIDEOCM_ALLOC_MGR pMgr = pContext->pMgr;
1300 NTSTATUS Status = vboxVideoAMgrCtxAllocUnmap(pContext, hSesionHandle, &pAlloc);
1301 if (Status == STATUS_SUCCESS)
1302 {
1303 vboxVideoAMgrAllocDestroy(pMgr, pAlloc);
1304 }
1305 else
1306 {
1307 Assert(0);
1308 }
1309 return Status;
1310}
1311
1312#ifdef VBOX_WITH_CRHGSMI
1313static DECLCALLBACK(VOID) vboxVideoAMgrAllocSubmitCompletion(PVBOXMP_DEVEXT pDevExt, PVBOXVDMADDI_CMD pCmd, PVOID pvContext)
1314{
1315 /* we should be called from our DPC routine */
1316 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
1317
1318 PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)pvContext;
1319 PVBOXVDMACMD pHdr = VBOXVDMACBUF_DR_TAIL(pDr, VBOXVDMACMD);
1320 VBOXVDMACMD_CHROMIUM_CMD *pBody = VBOXVDMACMD_BODY(pHdr, VBOXVDMACMD_CHROMIUM_CMD);
1321 UINT cBufs = pBody->cBuffers;
1322 for (UINT i = 0; i < cBufs; ++i)
1323 {
1324 VBOXVDMACMD_CHROMIUM_BUFFER *pBufCmd = &pBody->aBuffers[i];
1325 PVBOXVIDEOCM_ALLOC_REF pRef = (PVBOXVIDEOCM_ALLOC_REF)pBufCmd->u64GuestData;
1326 if (!pBufCmd->u32GuestData)
1327 {
1328 /* signal completion */
1329 if (pRef->pSynchEvent)
1330 KeSetEvent(pRef->pSynchEvent, 3, FALSE);
1331 }
1332
1333 vboxVideoAMgrCtxAllocRefRelease(pRef);
1334 }
1335
1336 vboxVdmaCBufDrFree(&pDevExt->u.primary.Vdma, pDr);
1337}
1338
1339/* submits a set of chromium uhgsmi buffers to host for processing */
1340NTSTATUS vboxVideoAMgrCtxAllocSubmit(PVBOXMP_DEVEXT pDevExt, PVBOXVIDEOCM_ALLOC_CONTEXT pContext, UINT cBuffers, VBOXWDDM_UHGSMI_BUFFER_UI_INFO_ESCAPE *paBuffers)
1341{
1342 /* ensure we do not overflow the 32bit buffer size value */
1343 if (VBOXWDDM_TRAILARRAY_MAXELEMENTSU32(VBOXVDMACMD_CHROMIUM_CMD, aBuffers) < cBuffers)
1344 {
1345 WARN(("number of buffers passed too big (%d), max is (%d)", cBuffers, VBOXWDDM_TRAILARRAY_MAXELEMENTSU32(VBOXVDMACMD_CHROMIUM_CMD, aBuffers)));
1346 return STATUS_INVALID_PARAMETER;
1347 }
1348
1349 NTSTATUS Status = STATUS_SUCCESS;
1350 UINT cbCmd = VBOXVDMACMD_SIZE_FROMBODYSIZE(RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CMD, aBuffers[cBuffers]));
1351
1352 PVBOXVDMACBUF_DR pDr = vboxVdmaCBufDrCreate(&pDevExt->u.primary.Vdma, cbCmd);
1353 if (pDr)
1354 {
1355 // vboxVdmaCBufDrCreate zero initializes the pDr
1356 pDr->fFlags = VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR;
1357 pDr->cbBuf = cbCmd;
1358 pDr->rc = VERR_NOT_IMPLEMENTED;
1359
1360 PVBOXVDMACMD pHdr = VBOXVDMACBUF_DR_TAIL(pDr, VBOXVDMACMD);
1361 pHdr->enmType = VBOXVDMACMD_TYPE_CHROMIUM_CMD;
1362 pHdr->u32CmdSpecific = 0;
1363 VBOXVDMACMD_CHROMIUM_CMD *pBody = VBOXVDMACMD_BODY(pHdr, VBOXVDMACMD_CHROMIUM_CMD);
1364 pBody->cBuffers = cBuffers;
1365 for (UINT i = 0; i < cBuffers; ++i)
1366 {
1367 VBOXVDMACMD_CHROMIUM_BUFFER *pBufCmd = &pBody->aBuffers[i];
1368 VBOXWDDM_UHGSMI_BUFFER_UI_INFO_ESCAPE *pBufInfo = &paBuffers[i];
1369 PVBOXVIDEOCM_ALLOC_REF pRef = vboxVideoAMgrCtxAllocRefAcquire(pContext, pBufInfo->hAlloc);
1370 if (pRef)
1371 {
1372#ifdef DEBUG_misha
1373 Assert(pRef->cRefs == 2);
1374#endif
1375 pBufCmd->offBuffer = pRef->pAlloc->offData + pBufInfo->Info.offData;
1376 pBufCmd->cbBuffer = pBufInfo->Info.cbData;
1377 pBufCmd->u32GuestData = 0;
1378 pBufCmd->u64GuestData = (uint64_t)pRef;
1379 }
1380 else
1381 {
1382 WARN(("vboxVideoAMgrCtxAllocRefAcquire failed for hAlloc(0x%x)\n", pBufInfo->hAlloc));
1383 /* release all previously acquired aloc references */
1384 for (UINT j = 0; j < i; ++j)
1385 {
1386 VBOXVDMACMD_CHROMIUM_BUFFER *pBufCmdJ = &pBody->aBuffers[j];
1387 PVBOXVIDEOCM_ALLOC_REF pRefJ = (PVBOXVIDEOCM_ALLOC_REF)pBufCmdJ;
1388 vboxVideoAMgrCtxAllocRefRelease(pRefJ);
1389 }
1390 Status = STATUS_INVALID_PARAMETER;
1391 break;
1392 }
1393 }
1394
1395 if (Status == STATUS_SUCCESS)
1396 {
1397 PVBOXVDMADDI_CMD pDdiCmd = VBOXVDMADDI_CMD_FROM_BUF_DR(pDr);
1398 vboxVdmaDdiCmdInit(pDdiCmd, 0, 0, vboxVideoAMgrAllocSubmitCompletion, pDr);
1399 /* mark command as submitted & invisible for the dx runtime since dx did not originate it */
1400 vboxVdmaDdiCmdSubmittedNotDx(pDdiCmd);
1401 int rc = vboxVdmaCBufDrSubmit(pDevExt, &pDevExt->u.primary.Vdma, pDr);
1402 if (RT_SUCCESS(rc))
1403 {
1404 return STATUS_SUCCESS;
1405 }
1406
1407 WARN(("vboxVdmaCBufDrSubmit failed with rc (%d)\n", rc));
1408
1409 /* failure branch */
1410 /* release all previously acquired aloc references */
1411 for (UINT i = 0; i < cBuffers; ++i)
1412 {
1413 VBOXVDMACMD_CHROMIUM_BUFFER *pBufCmd = &pBody->aBuffers[i];
1414 PVBOXVIDEOCM_ALLOC_REF pRef = (PVBOXVIDEOCM_ALLOC_REF)pBufCmd;
1415 vboxVideoAMgrCtxAllocRefRelease(pRef);
1416 }
1417 }
1418
1419 vboxVdmaCBufDrFree(&pDevExt->u.primary.Vdma, pDr);
1420 }
1421 else
1422 {
1423 Assert(0);
1424 /* @todo: try flushing.. */
1425 LOGREL(("vboxVdmaCBufDrCreate returned NULL"));
1426 Status = STATUS_INSUFFICIENT_RESOURCES;
1427 }
1428
1429 return Status;
1430}
1431#endif
1432
1433NTSTATUS vboxVideoAMgrCreate(PVBOXMP_DEVEXT pDevExt, PVBOXVIDEOCM_ALLOC_MGR pMgr, uint32_t offData, uint32_t cbData)
1434{
1435 Assert(!(offData & (PAGE_SIZE -1)));
1436 Assert(!(cbData & (PAGE_SIZE -1)));
1437 offData = VBOXWDDM_ROUNDBOUND(offData, PAGE_SIZE);
1438 cbData &= (~(PAGE_SIZE -1));
1439 Assert(cbData);
1440 if (!cbData)
1441 return STATUS_INVALID_PARAMETER;
1442
1443 ExInitializeFastMutex(&pMgr->Mutex);
1444 NTSTATUS Status = vboxWddmHTableCreate(&pMgr->AllocTable, 64);
1445 Assert(Status == STATUS_SUCCESS);
1446 if (Status == STATUS_SUCCESS)
1447 {
1448 Status = vboxMmInit(&pMgr->Mm, BYTES_TO_PAGES(cbData));
1449 Assert(Status == STATUS_SUCCESS);
1450 if (Status == STATUS_SUCCESS)
1451 {
1452 PHYSICAL_ADDRESS PhysicalAddress = {0};
1453 PhysicalAddress.QuadPart = VBoxCommonFromDeviceExt(pDevExt)->phVRAM.QuadPart + offData;
1454 pMgr->pvData = (uint8_t*)MmMapIoSpace(PhysicalAddress, cbData, MmNonCached);
1455 Assert(pMgr->pvData);
1456 if (pMgr->pvData)
1457 {
1458 pMgr->offData = offData;
1459 pMgr->cbData = cbData;
1460 return STATUS_SUCCESS;
1461 }
1462 else
1463 {
1464 Status = STATUS_UNSUCCESSFUL;
1465 }
1466 vboxMmTerm(&pMgr->Mm);
1467 }
1468 vboxWddmHTableDestroy(&pMgr->AllocTable);
1469 }
1470
1471 return Status;
1472}
1473
1474NTSTATUS vboxVideoAMgrDestroy(PVBOXMP_DEVEXT pDevExt, PVBOXVIDEOCM_ALLOC_MGR pMgr)
1475{
1476 MmUnmapIoSpace(pMgr->pvData, pMgr->cbData);
1477 vboxMmTerm(&pMgr->Mm);
1478 vboxWddmHTableDestroy(&pMgr->AllocTable);
1479 return STATUS_SUCCESS;
1480}
1481
1482NTSTATUS vboxVideoAMgrCtxCreate(PVBOXVIDEOCM_ALLOC_MGR pMgr, PVBOXVIDEOCM_ALLOC_CONTEXT pCtx)
1483{
1484 NTSTATUS Status = STATUS_NOT_SUPPORTED;
1485 if (pMgr->pvData)
1486 {
1487 ExInitializeFastMutex(&pCtx->Mutex);
1488 Status = vboxWddmHTableCreate(&pCtx->AllocTable, 32);
1489 Assert(Status == STATUS_SUCCESS);
1490 if (Status == STATUS_SUCCESS)
1491 {
1492 pCtx->pMgr = pMgr;
1493 return STATUS_SUCCESS;
1494 }
1495 }
1496 return Status;
1497}
1498
1499NTSTATUS vboxVideoAMgrCtxDestroy(PVBOXVIDEOCM_ALLOC_CONTEXT pCtx)
1500{
1501 if (!pCtx->pMgr)
1502 return STATUS_SUCCESS;
1503
1504 VBOXWDDM_HTABLE_ITERATOR Iter;
1505 NTSTATUS Status = STATUS_SUCCESS;
1506
1507 vboxWddmHTableIterInit(&pCtx->AllocTable, &Iter);
1508 do
1509 {
1510 PVBOXVIDEOCM_ALLOC_REF pRef = (PVBOXVIDEOCM_ALLOC_REF)vboxWddmHTableIterNext(&Iter, NULL);
1511 if (!pRef)
1512 break;
1513
1514 Assert(0);
1515
1516 Status = vboxVideoAMgrCtxAllocDestroy(pCtx, pRef->hSessionHandle);
1517 Assert(Status == STATUS_SUCCESS);
1518 if (Status != STATUS_SUCCESS)
1519 break;
1520 // vboxWddmHTableIterRemoveCur(&Iter);
1521 } while (1);
1522
1523 if (Status == STATUS_SUCCESS)
1524 {
1525 vboxWddmHTableDestroy(&pCtx->AllocTable);
1526 }
1527
1528 return Status;
1529}
1530
1531
1532VOID vboxWddmSleep(uint32_t u32Val)
1533{
1534 LARGE_INTEGER Interval;
1535 Interval.QuadPart = -(int64_t) 2 /* ms */ * 10000;
1536
1537 KeDelayExecutionThread(KernelMode, FALSE, &Interval);
1538}
1539
1540VOID vboxWddmCounterU32Wait(uint32_t volatile * pu32, uint32_t u32Val)
1541{
1542 LARGE_INTEGER Interval;
1543 Interval.QuadPart = -(int64_t) 2 /* ms */ * 10000;
1544 uint32_t u32CurVal;
1545
1546 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
1547
1548 while ((u32CurVal = ASMAtomicReadU32(pu32)) != u32Val)
1549 {
1550 Assert(u32CurVal >= u32Val);
1551 Assert(u32CurVal < UINT32_MAX/2);
1552
1553 KeDelayExecutionThread(KernelMode, FALSE, &Interval);
1554 }
1555}
1556
1557/* dump user-mode driver debug info */
1558static char g_aVBoxUmdD3DCAPS9[304];
1559static VBOXDISPIFESCAPE_DBGDUMPBUF_FLAGS g_VBoxUmdD3DCAPS9Flags;
1560static BOOLEAN g_bVBoxUmdD3DCAPS9IsInited = FALSE;
1561
1562static void vboxUmdDumpDword(DWORD *pvData, DWORD cData)
1563{
1564 char aBuf[16*4];
1565 DWORD dw1, dw2, dw3, dw4;
1566 for (UINT i = 0; i < (cData & (~3)); i+=4)
1567 {
1568 dw1 = *pvData++;
1569 dw2 = *pvData++;
1570 dw3 = *pvData++;
1571 dw4 = *pvData++;
1572 sprintf(aBuf, "0x%08x, 0x%08x, 0x%08x, 0x%08x,\n", dw1, dw2, dw3, dw4);
1573 LOGREL(("%s", aBuf));
1574 }
1575
1576 cData = cData % 4;
1577 switch (cData)
1578 {
1579 case 3:
1580 dw1 = *pvData++;
1581 dw2 = *pvData++;
1582 dw3 = *pvData++;
1583 sprintf(aBuf, "0x%08x, 0x%08x, 0x%08x\n", dw1, dw2, dw3);
1584 LOGREL(("%s", aBuf));
1585 break;
1586 case 2:
1587 dw1 = *pvData++;
1588 dw2 = *pvData++;
1589 sprintf(aBuf, "0x%08x, 0x%08x\n", dw1, dw2);
1590 LOGREL(("%s", aBuf));
1591 break;
1592 case 1:
1593 dw1 = *pvData++;
1594 sprintf(aBuf, "0x%8x\n", dw1);
1595 LOGREL(("%s", aBuf));
1596 break;
1597 default:
1598 break;
1599 }
1600}
1601
1602static void vboxUmdDumpD3DCAPS9(void *pvData, PVBOXDISPIFESCAPE_DBGDUMPBUF_FLAGS pFlags)
1603{
1604 AssertCompile(!(sizeof (g_aVBoxUmdD3DCAPS9) % sizeof (DWORD)));
1605 LOGREL(("*****Start Dumping D3DCAPS9:*******"));
1606 LOGREL(("WoW64 flag(%d)", (UINT)pFlags->WoW64));
1607 vboxUmdDumpDword((DWORD*)pvData, sizeof (g_aVBoxUmdD3DCAPS9) / sizeof (DWORD));
1608 LOGREL(("*****End Dumping D3DCAPS9**********"));
1609}
1610
1611NTSTATUS vboxUmdDumpBuf(PVBOXDISPIFESCAPE_DBGDUMPBUF pBuf, uint32_t cbBuffer)
1612{
1613 if (cbBuffer < RT_OFFSETOF(VBOXDISPIFESCAPE_DBGDUMPBUF, aBuf[0]))
1614 {
1615 WARN(("Buffer too small"));
1616 return STATUS_BUFFER_TOO_SMALL;
1617 }
1618
1619 NTSTATUS Status = STATUS_SUCCESS;
1620 uint32_t cbString = cbBuffer - RT_OFFSETOF(VBOXDISPIFESCAPE_DBGDUMPBUF, aBuf[0]);
1621 switch (pBuf->enmType)
1622 {
1623 case VBOXDISPIFESCAPE_DBGDUMPBUF_TYPE_D3DCAPS9:
1624 {
1625 if (cbString != sizeof (g_aVBoxUmdD3DCAPS9))
1626 {
1627 WARN(("wrong caps size, expected %d, but was %d", sizeof (g_aVBoxUmdD3DCAPS9), cbString));
1628 Status = STATUS_INVALID_PARAMETER;
1629 break;
1630 }
1631
1632 if (g_bVBoxUmdD3DCAPS9IsInited)
1633 {
1634 if (!memcmp(g_aVBoxUmdD3DCAPS9, pBuf->aBuf, sizeof (g_aVBoxUmdD3DCAPS9)))
1635 break;
1636
1637 WARN(("caps do not match!"));
1638 vboxUmdDumpD3DCAPS9(pBuf->aBuf, &pBuf->Flags);
1639 break;
1640 }
1641
1642 memcpy(g_aVBoxUmdD3DCAPS9, pBuf->aBuf, sizeof (g_aVBoxUmdD3DCAPS9));
1643 g_VBoxUmdD3DCAPS9Flags = pBuf->Flags;
1644 g_bVBoxUmdD3DCAPS9IsInited = TRUE;
1645 vboxUmdDumpD3DCAPS9(pBuf->aBuf, &pBuf->Flags);
1646 }
1647 }
1648
1649 return Status;
1650}
1651
1652#if 0
1653VOID vboxShRcTreeInit(PVBOXMP_DEVEXT pDevExt)
1654{
1655 ExInitializeFastMutex(&pDevExt->ShRcTreeMutex);
1656 pDevExt->ShRcTree = NULL;
1657}
1658
1659VOID vboxShRcTreeTerm(PVBOXMP_DEVEXT pDevExt)
1660{
1661 Assert(!pDevExt->ShRcTree);
1662 pDevExt->ShRcTree = NULL;
1663}
1664
1665BOOLEAN vboxShRcTreePut(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_ALLOCATION pAlloc)
1666{
1667 HANDLE hSharedRc = pAlloc->hSharedHandle;
1668 if (!hSharedRc)
1669 {
1670 WARN(("invalid call with zero shared handle!"));
1671 return FALSE;
1672 }
1673 pAlloc->ShRcTreeEntry.Key = (AVLPVKEY)hSharedRc;
1674 ExAcquireFastMutex(&pDevExt->ShRcTreeMutex);
1675 bool bRc = RTAvlPVInsert(&pDevExt->ShRcTree, &pAlloc->ShRcTreeEntry);
1676 ExReleaseFastMutex(&pDevExt->ShRcTreeMutex);
1677 Assert(bRc);
1678 return (BOOLEAN)bRc;
1679}
1680
1681#define PVBOXWDDM_ALLOCATION_FROM_SHRCTREENODE(_p) ((PVBOXWDDM_ALLOCATION)(((uint8_t*)(_p)) - RT_OFFSETOF(VBOXWDDM_ALLOCATION, ShRcTreeEntry)))
1682PVBOXWDDM_ALLOCATION vboxShRcTreeGet(PVBOXMP_DEVEXT pDevExt, HANDLE hSharedRc)
1683{
1684 ExAcquireFastMutex(&pDevExt->ShRcTreeMutex);
1685 PAVLPVNODECORE pNode = RTAvlPVGet(&pDevExt->ShRcTree, (AVLPVKEY)hSharedRc);
1686 ExReleaseFastMutex(&pDevExt->ShRcTreeMutex);
1687 if (!pNode)
1688 return NULL;
1689 PVBOXWDDM_ALLOCATION pAlloc = PVBOXWDDM_ALLOCATION_FROM_SHRCTREENODE(pNode);
1690 return pAlloc;
1691}
1692
1693BOOLEAN vboxShRcTreeRemove(PVBOXMP_DEVEXT pDevExt, PVBOXWDDM_ALLOCATION pAlloc)
1694{
1695 HANDLE hSharedRc = pAlloc->hSharedHandle;
1696 if (!hSharedRc)
1697 {
1698 WARN(("invalid call with zero shared handle!"));
1699 return FALSE;
1700 }
1701 ExAcquireFastMutex(&pDevExt->ShRcTreeMutex);
1702 PAVLPVNODECORE pNode = RTAvlPVRemove(&pDevExt->ShRcTree, (AVLPVKEY)hSharedRc);
1703 ExReleaseFastMutex(&pDevExt->ShRcTreeMutex);
1704 if (!pNode)
1705 return NULL;
1706 PVBOXWDDM_ALLOCATION pRetAlloc = PVBOXWDDM_ALLOCATION_FROM_SHRCTREENODE(pNode);
1707 Assert(pRetAlloc == pAlloc);
1708 return !!pRetAlloc;
1709}
1710#endif
1711
1712NTSTATUS vboxWddmDrvCfgInit(PUNICODE_STRING pRegStr)
1713{
1714 HANDLE hKey;
1715 OBJECT_ATTRIBUTES ObjAttr;
1716
1717 InitializeObjectAttributes(&ObjAttr, pRegStr, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
1718
1719 NTSTATUS Status = ZwOpenKey(&hKey, GENERIC_READ, &ObjAttr);
1720 if (!NT_SUCCESS(Status))
1721 {
1722 WARN(("ZwOpenKey for settings key failed, Status 0x%x", Status));
1723 return Status;
1724 }
1725
1726 DWORD dwValue = 0;
1727 Status = vboxWddmRegQueryValueDword(hKey, VBOXWDDM_CFG_STR_LOG_UM, &dwValue);
1728 if (NT_SUCCESS(Status))
1729 g_VBoxLogUm = dwValue;
1730
1731 ZwClose(hKey);
1732
1733 return Status;
1734}
1735
1736NTSTATUS vboxWddmThreadCreate(PKTHREAD * ppThread, PKSTART_ROUTINE pStartRoutine, PVOID pStartContext)
1737{
1738 NTSTATUS fStatus;
1739 HANDLE hThread;
1740 OBJECT_ATTRIBUTES fObjectAttributes;
1741
1742 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
1743
1744 InitializeObjectAttributes(&fObjectAttributes, NULL, OBJ_KERNEL_HANDLE,
1745 NULL, NULL);
1746
1747 fStatus = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS,
1748 &fObjectAttributes, NULL, NULL,
1749 (PKSTART_ROUTINE) pStartRoutine, pStartContext);
1750 if (!NT_SUCCESS(fStatus))
1751 return fStatus;
1752
1753 ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL,
1754 KernelMode, (PVOID*) ppThread, NULL);
1755 ZwClose(hThread);
1756 return STATUS_SUCCESS;
1757}
1758
1759#ifdef VBOX_VDMA_WITH_WATCHDOG
1760static int vboxWddmWdProgram(PVBOXMP_DEVEXT pDevExt, uint32_t cMillis)
1761{
1762 int rc = VINF_SUCCESS;
1763 PVBOXVDMA_CTL pCmd = (PVBOXVDMA_CTL)VBoxSHGSMICommandAlloc(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, sizeof (VBOXVDMA_CTL), HGSMI_CH_VBVA, VBVA_VDMA_CTL);
1764 if (pCmd)
1765 {
1766 pCmd->enmCtl = VBOXVDMA_CTL_TYPE_WATCHDOG;
1767 pCmd->u32Offset = cMillis;
1768 pCmd->i32Result = VERR_NOT_SUPPORTED;
1769
1770 const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepSynch(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pCmd);
1771 Assert(pHdr);
1772 if (pHdr)
1773 {
1774 do
1775 {
1776 HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pHdr);
1777 Assert(offCmd != HGSMIOFFSET_VOID);
1778 if (offCmd != HGSMIOFFSET_VOID)
1779 {
1780 VBoxVideoCmnPortWriteUlong(VBoxCommonFromDeviceExt(pDevExt)->guestCtx.port, offCmd);
1781 rc = VBoxSHGSMICommandDoneSynch(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pHdr);
1782 AssertRC(rc);
1783 if (RT_SUCCESS(rc))
1784 {
1785 rc = pCmd->i32Result;
1786 AssertRC(rc);
1787 }
1788 break;
1789 }
1790 else
1791 rc = VERR_INVALID_PARAMETER;
1792 /* fail to submit, cancel it */
1793 VBoxSHGSMICommandCancelSynch(&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pHdr);
1794 } while (0);
1795 }
1796
1797 VBoxSHGSMICommandFree (&VBoxCommonFromDeviceExt(pDevExt)->guestCtx.heapCtx, pCmd);
1798 }
1799 else
1800 {
1801 LOGREL(("HGSMIHeapAlloc failed"));
1802 rc = VERR_OUT_OF_RESOURCES;
1803 }
1804 return rc;
1805}
1806
1807static uint32_t g_VBoxWdTimeout = 4000;
1808/* if null g_VBoxWdTimeout / 2 is used */
1809static uint32_t g_VBoxWdTimerPeriod = 0;
1810
1811static VOID vboxWddmWdThread(PVOID pvUser)
1812{
1813 PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)pvUser;
1814 BOOLEAN bExit = FALSE;
1815 int rc;
1816 while (1)
1817 {
1818 if (!bExit)
1819 {
1820 rc = vboxWddmWdProgram(pDevExt, g_VBoxWdTimeout /* ms */);
1821 AssertRC(rc);
1822 }
1823 else
1824 {
1825 rc = vboxWddmWdProgram(pDevExt, 0 /* to disable WatchDog */);
1826 AssertRC(rc);
1827 break;
1828 }
1829 LARGE_INTEGER Timeout;
1830 uint32_t timerTimeOut = g_VBoxWdTimerPeriod ? g_VBoxWdTimerPeriod : g_VBoxWdTimeout / 2;
1831 Timeout.QuadPart = 10000ULL * timerTimeOut /* ms */;
1832 NTSTATUS Status = KeWaitForSingleObject(&pDevExt->WdEvent, Executive, KernelMode, FALSE, &Timeout);
1833 if (Status != STATUS_TIMEOUT)
1834 bExit = TRUE;
1835 }
1836}
1837
1838NTSTATUS vboxWddmWdInit(PVBOXMP_DEVEXT pDevExt)
1839{
1840 KeInitializeEvent(&pDevExt->WdEvent, NotificationEvent, FALSE);
1841
1842 NTSTATUS Status = vboxWddmThreadCreate(&pDevExt->pWdThread, vboxWddmWdThread, pDevExt);
1843 if (!NT_SUCCESS(Status))
1844 {
1845 WARN(("vboxWddmThreadCreate failed, Status 0x%x", Status));
1846 pDevExt->pWdThread = NULL;
1847 }
1848 return Status;
1849}
1850
1851NTSTATUS vboxWddmWdTerm(PVBOXMP_DEVEXT pDevExt)
1852{
1853 if (!pDevExt->pWdThread)
1854 return STATUS_SUCCESS;
1855
1856 KeSetEvent(&pDevExt->WdEvent, 0, FALSE);
1857
1858 KeWaitForSingleObject(pDevExt->pWdThread, Executive, KernelMode, FALSE, NULL);
1859 ObDereferenceObject(pDevExt->pWdThread);
1860 pDevExt->pWdThread = NULL;
1861 return STATUS_SUCCESS;
1862}
1863#endif
1864
1865static int vboxWddmSlConfigure(PVBOXMP_DEVEXT pDevExt, uint32_t fFlags)
1866{
1867 PHGSMIGUESTCOMMANDCONTEXT pCtx = &VBoxCommonFromDeviceExt(pDevExt)->guestCtx;
1868 VBVASCANLINECFG *pCfg;
1869 int rc = VINF_SUCCESS;
1870
1871 /* Allocate the IO buffer. */
1872 pCfg = (VBVASCANLINECFG *)VBoxHGSMIBufferAlloc(pCtx,
1873 sizeof (VBVASCANLINECFG), HGSMI_CH_VBVA,
1874 VBVA_SCANLINE_CFG);
1875
1876 if (pCfg)
1877 {
1878 /* Prepare data to be sent to the host. */
1879 pCfg->rc = VERR_NOT_IMPLEMENTED;
1880 pCfg->fFlags = fFlags;
1881 rc = VBoxHGSMIBufferSubmit(pCtx, pCfg);
1882 if (RT_SUCCESS(rc))
1883 {
1884 AssertRC(pCfg->rc);
1885 rc = pCfg->rc;
1886 }
1887 /* Free the IO buffer. */
1888 VBoxHGSMIBufferFree(pCtx, pCfg);
1889 }
1890 else
1891 rc = VERR_NO_MEMORY;
1892 return rc;
1893}
1894
1895NTSTATUS VBoxWddmSlEnableVSyncNotification(PVBOXMP_DEVEXT pDevExt, BOOLEAN fEnable)
1896{
1897 if (!fEnable)
1898 {
1899 KeCancelTimer(&pDevExt->VSyncTimer);
1900 }
1901 else
1902 {
1903 LARGE_INTEGER DueTime;
1904 DueTime.QuadPart = -166666LL; /* 60 Hz */
1905 KeSetTimerEx(&pDevExt->VSyncTimer, DueTime, 16, &pDevExt->VSyncDpc);
1906 }
1907 return STATUS_SUCCESS;
1908}
1909
1910NTSTATUS VBoxWddmSlGetScanLine(PVBOXMP_DEVEXT pDevExt, DXGKARG_GETSCANLINE *pGetScanLine)
1911{
1912 Assert((UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays > pGetScanLine->VidPnTargetId);
1913 VBOXWDDM_TARGET *pTarget = &pDevExt->aTargets[pGetScanLine->VidPnTargetId];
1914 Assert(pTarget->HeightTotal);
1915 Assert(pTarget->HeightVisible);
1916 Assert(pTarget->HeightTotal >= pTarget->HeightVisible);
1917 Assert(pTarget->ScanLineState < pTarget->HeightTotal);
1918 if (pTarget->HeightTotal)
1919 {
1920 uint32_t curScanLine = pTarget->ScanLineState;
1921 ++pTarget->ScanLineState;
1922 if (pTarget->ScanLineState >= pTarget->HeightTotal)
1923 pTarget->ScanLineState = 0;
1924
1925
1926 BOOL bVBlank = (!curScanLine || curScanLine > pTarget->HeightVisible);
1927 pGetScanLine->ScanLine = curScanLine;
1928 pGetScanLine->InVerticalBlank = bVBlank;
1929 }
1930 else
1931 {
1932 pGetScanLine->InVerticalBlank = TRUE;
1933 pGetScanLine->ScanLine = 0;
1934 }
1935 return STATUS_SUCCESS;
1936}
1937
1938static VOID vboxWddmSlVSyncDpc(
1939 __in struct _KDPC *Dpc,
1940 __in_opt PVOID DeferredContext,
1941 __in_opt PVOID SystemArgument1,
1942 __in_opt PVOID SystemArgument2
1943)
1944{
1945 PVBOXMP_DEVEXT pDevExt = (PVBOXMP_DEVEXT)DeferredContext;
1946 DXGKARGCB_NOTIFY_INTERRUPT_DATA notify;
1947 BOOLEAN bNeedDpc = FALSE;
1948 for (UINT i = 0; i < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
1949 {
1950 PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[i];
1951 PVBOXWDDM_ALLOCATION pPrimary = vboxWddmAquirePrimary(pDevExt, pSource, i);
1952 if (pPrimary)
1953 {
1954 VBOXVIDEOOFFSET offVram = pPrimary->AllocData.Addr.offVram;
1955 if (offVram != VBOXVIDEOOFFSET_VOID)
1956 {
1957 memset(&notify, 0, sizeof(DXGKARGCB_NOTIFY_INTERRUPT_DATA));
1958 notify.InterruptType = DXGK_INTERRUPT_CRTC_VSYNC;
1959 /* @todo: !!!this is not correct in case we want source[i]->target[i!=j] mapping */
1960 notify.CrtcVsync.VidPnTargetId = i;
1961 notify.CrtcVsync.PhysicalAddress.QuadPart = offVram;
1962 /* yes, we can report VSync at dispatch */
1963 pDevExt->u.primary.DxgkInterface.DxgkCbNotifyInterrupt(pDevExt->u.primary.DxgkInterface.DeviceHandle, &notify);
1964 bNeedDpc = TRUE;
1965 }
1966
1967 vboxWddmAllocationRelease(pPrimary);
1968 }
1969 }
1970
1971 if (bNeedDpc)
1972 {
1973 pDevExt->u.primary.DxgkInterface.DxgkCbQueueDpc(pDevExt->u.primary.DxgkInterface.DeviceHandle);
1974 }
1975}
1976
1977NTSTATUS VBoxWddmSlInit(PVBOXMP_DEVEXT pDevExt)
1978{
1979 KeInitializeTimer(&pDevExt->VSyncTimer);
1980 KeInitializeDpc(&pDevExt->VSyncDpc, vboxWddmSlVSyncDpc, pDevExt);
1981 return STATUS_SUCCESS;
1982}
1983
1984NTSTATUS VBoxWddmSlTerm(PVBOXMP_DEVEXT pDevExt)
1985{
1986 KeCancelTimer(&pDevExt->VSyncTimer);
1987 return STATUS_SUCCESS;
1988}
1989
1990#ifdef VBOX_WDDM_WIN8
1991void vboxWddmDiInitDefault(DXGK_DISPLAY_INFORMATION *pInfo, PHYSICAL_ADDRESS PhAddr, D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId)
1992{
1993 pInfo->Width = 1024;
1994 pInfo->Height = 768;
1995 pInfo->Pitch = pInfo->Width * 4;
1996 pInfo->ColorFormat = D3DDDIFMT_A8R8G8B8;
1997 pInfo->PhysicAddress = PhAddr;
1998 pInfo->TargetId = VidPnSourceId;
1999 pInfo->AcpiId = 0;
2000}
2001
2002void vboxWddmDiToAllocData(PVBOXMP_DEVEXT pDevExt, const DXGK_DISPLAY_INFORMATION *pInfo, PVBOXWDDM_ALLOC_DATA pAllocData)
2003{
2004 pAllocData->SurfDesc.width = pInfo->Width;
2005 pAllocData->SurfDesc.height = pInfo->Height;
2006 pAllocData->SurfDesc.format = pInfo->ColorFormat;
2007 pAllocData->SurfDesc.bpp = vboxWddmCalcBitsPerPixel(pInfo->ColorFormat);
2008 pAllocData->SurfDesc.pitch = pInfo->Pitch;
2009 pAllocData->SurfDesc.depth = 1;
2010 pAllocData->SurfDesc.slicePitch = pInfo->Pitch;
2011 pAllocData->SurfDesc.cbSize = pInfo->Pitch * pInfo->Height;
2012 pAllocData->SurfDesc.VidPnSourceId = pInfo->TargetId;
2013 pAllocData->SurfDesc.RefreshRate.Numerator = 60000;
2014 pAllocData->SurfDesc.RefreshRate.Denominator = 1000;
2015
2016 /* the address here is not a VRAM offset! so convert it to offset */
2017 vboxWddmAddrSetVram(&pAllocData->Addr, 1,
2018 vboxWddmVramAddrToOffset(pDevExt, pInfo->PhysicAddress));
2019}
2020
2021void vboxWddmDmAdjustDefaultVramLocations(PVBOXMP_DEVEXT pDevExt, D3DDDI_VIDEO_PRESENT_SOURCE_ID ModifiedVidPnSourceId)
2022{
2023 PVBOXWDDM_SOURCE pSource = &pDevExt->aSources[ModifiedVidPnSourceId];
2024 PHYSICAL_ADDRESS PhAddr;
2025 AssertRelease(pSource->AllocData.Addr.SegmentId);
2026 AssertRelease(pSource->AllocData.Addr.offVram != VBOXVIDEOOFFSET_VOID);
2027 PhAddr.QuadPart = pSource->AllocData.Addr.offVram;
2028
2029 for (UINT i = ModifiedVidPnSourceId + 1; i < (UINT)VBoxCommonFromDeviceExt(pDevExt)->cDisplays; ++i)
2030 {
2031 /* increaze the phaddr based on the previous source size info */
2032 PhAddr.QuadPart += pSource->AllocData.SurfDesc.cbSize;
2033 PhAddr.QuadPart = ROUND_TO_PAGES(PhAddr.QuadPart);
2034 pSource = &pDevExt->aSources[i];
2035 if (pSource->AllocData.Addr.offVram != PhAddr.QuadPart
2036 || pSource->AllocData.Addr.SegmentId != 1)
2037 pSource->fGhSynced = 0;
2038 pSource->AllocData.Addr.SegmentId = 1;
2039 pSource->AllocData.Addr.offVram = PhAddr.QuadPart;
2040 }
2041}
2042#endif
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