VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Miniport/wddm/VBoxVideoVdma.cpp@ 30795

Last change on this file since 30795 was 30795, checked in by vboxsync, 14 years ago

wddm/3d: bugfixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.6 KB
Line 
1/*
2 * Copyright (C) 2010 Oracle Corporation
3 *
4 * This file is part of VirtualBox Open Source Edition (OSE), as
5 * available from http://www.virtualbox.org. This file is free software;
6 * you can redistribute it and/or modify it under the terms of the GNU
7 * General Public License (GPL) as published by the Free Software
8 * Foundation, in version 2 as it comes in the "COPYING" file of the
9 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
10 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
11 */
12
13#include "../VBoxVideo.h"
14#include "../Helper.h"
15
16#include <VBox/VBoxGuestLib.h>
17#include <VBox/VBoxVideo.h>
18#include "VBoxVideoVdma.h"
19#include "../VBoxVideo.h"
20
21
22NTSTATUS vboxVdmaPipeConstruct(PVBOXVDMAPIPE pPipe)
23{
24 KeInitializeSpinLock(&pPipe->SinchLock);
25 KeInitializeEvent(&pPipe->Event, SynchronizationEvent, FALSE);
26 InitializeListHead(&pPipe->CmdListHead);
27 pPipe->enmState = VBOXVDMAPIPE_STATE_CREATED;
28 pPipe->bNeedNotify = true;
29 return STATUS_SUCCESS;
30}
31
32NTSTATUS vboxVdmaPipeSvrOpen(PVBOXVDMAPIPE pPipe)
33{
34 NTSTATUS Status = STATUS_SUCCESS;
35 KIRQL OldIrql;
36 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
37 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
38 switch (pPipe->enmState)
39 {
40 case VBOXVDMAPIPE_STATE_CREATED:
41 pPipe->enmState = VBOXVDMAPIPE_STATE_OPENNED;
42 pPipe->bNeedNotify = false;
43 break;
44 case VBOXVDMAPIPE_STATE_OPENNED:
45 pPipe->bNeedNotify = false;
46 break;
47 default:
48 AssertBreakpoint();
49 Status = STATUS_INVALID_PIPE_STATE;
50 break;
51 }
52
53 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
54 return Status;
55}
56
57NTSTATUS vboxVdmaPipeSvrClose(PVBOXVDMAPIPE pPipe)
58{
59 NTSTATUS Status = STATUS_SUCCESS;
60 KIRQL OldIrql;
61 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
62 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
63 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
64 switch (pPipe->enmState)
65 {
66 case VBOXVDMAPIPE_STATE_CLOSING:
67 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
68 break;
69 case VBOXVDMAPIPE_STATE_CLOSED:
70 break;
71 default:
72 AssertBreakpoint();
73 Status = STATUS_INVALID_PIPE_STATE;
74 break;
75 }
76
77 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
78 return Status;
79}
80
81NTSTATUS vboxVdmaPipeCltClose(PVBOXVDMAPIPE pPipe)
82{
83 NTSTATUS Status = STATUS_SUCCESS;
84 KIRQL OldIrql;
85 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
86 bool bNeedNotify = false;
87 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
88 || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED
89 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
90 switch (pPipe->enmState)
91 {
92 case VBOXVDMAPIPE_STATE_OPENNED:
93 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSING;
94 bNeedNotify = pPipe->bNeedNotify;
95 pPipe->bNeedNotify = false;
96 break;
97 case VBOXVDMAPIPE_STATE_CREATED:
98 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
99 pPipe->bNeedNotify = false;
100 break;
101 case VBOXVDMAPIPE_STATE_CLOSED:
102 break;
103 default:
104 AssertBreakpoint();
105 Status = STATUS_INVALID_PIPE_STATE;
106 break;
107 }
108
109 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
110
111 if (bNeedNotify)
112 {
113 KeSetEvent(&pPipe->Event, 0, FALSE);
114 }
115 return Status;
116}
117
118NTSTATUS vboxVdmaPipeDestruct(PVBOXVDMAPIPE pPipe)
119{
120 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
121 || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
122 /* ensure the pipe is closed */
123 NTSTATUS Status = vboxVdmaPipeCltClose(pPipe);
124 Assert(Status == STATUS_SUCCESS);
125
126 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
127
128 return Status;
129}
130
131NTSTATUS vboxVdmaPipeSvrCmdGetList(PVBOXVDMAPIPE pPipe, PLIST_ENTRY pDetachHead)
132{
133 PLIST_ENTRY pEntry = NULL;
134 KIRQL OldIrql;
135 NTSTATUS Status = STATUS_SUCCESS;
136 VBOXVDMAPIPE_STATE enmState = VBOXVDMAPIPE_STATE_CLOSED;
137 do
138 {
139 bool bListEmpty = true;
140 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
141 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
142 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
143 Assert(pPipe->enmState >= VBOXVDMAPIPE_STATE_OPENNED);
144 enmState = pPipe->enmState;
145 if (enmState >= VBOXVDMAPIPE_STATE_OPENNED)
146 {
147 vboxVideoLeDetach(&pPipe->CmdListHead, pDetachHead);
148 bListEmpty = IsListEmpty(pDetachHead);
149 pPipe->bNeedNotify = bListEmpty;
150 }
151 else
152 {
153 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
154 Status = STATUS_INVALID_PIPE_STATE;
155 break;
156 }
157
158 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
159
160 if (!bListEmpty)
161 {
162 Assert(Status == STATUS_SUCCESS);
163 break;
164 }
165
166 if (enmState == VBOXVDMAPIPE_STATE_OPENNED)
167 {
168 Status = KeWaitForSingleObject(&pPipe->Event, Executive, KernelMode, FALSE, NULL /* PLARGE_INTEGER Timeout */);
169 Assert(Status == STATUS_SUCCESS);
170 if (Status != STATUS_SUCCESS)
171 break;
172 }
173 else
174 {
175 Assert(enmState == VBOXVDMAPIPE_STATE_CLOSING);
176 Status = STATUS_PIPE_CLOSING;
177 break;
178 }
179 } while (1);
180
181 return Status;
182}
183
184NTSTATUS vboxVdmaPipeCltCmdPut(PVBOXVDMAPIPE pPipe, PVBOXVDMAPIPE_CMD_HDR pCmd)
185{
186 NTSTATUS Status = STATUS_SUCCESS;
187 KIRQL OldIrql;
188 bool bNeedNotify = false;
189
190 KeAcquireSpinLock(&pPipe->SinchLock, &OldIrql);
191
192 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED);
193 if (pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED)
194 {
195 bNeedNotify = pPipe->bNeedNotify;
196 InsertHeadList(&pPipe->CmdListHead, &pCmd->ListEntry);
197 pPipe->bNeedNotify = false;
198 }
199 else
200 Status = STATUS_INVALID_PIPE_STATE;
201
202 KeReleaseSpinLock(&pPipe->SinchLock, OldIrql);
203
204 if (bNeedNotify)
205 {
206 KeSetEvent(&pPipe->Event, 0, FALSE);
207 }
208
209 return Status;
210}
211
212PVBOXVDMAPIPE_CMD_DR vboxVdmaGgCmdCreate(PVBOXVDMAGG pVdma, VBOXVDMAPIPE_CMD_TYPE enmType, uint32_t cbCmd)
213{
214 PVBOXVDMAPIPE_CMD_DR pHdr = (PVBOXVDMAPIPE_CMD_DR)vboxWddmMemAllocZero(cbCmd);
215 Assert(pHdr);
216 if (pHdr)
217 {
218 pHdr->enmType = enmType;
219 return pHdr;
220 }
221 return NULL;
222}
223
224void vboxVdmaGgCmdDestroy(PVBOXVDMAPIPE_CMD_DR pDr)
225{
226 vboxWddmMemFree(pDr);
227}
228
229
230/**
231 * helper function used for system thread creation
232 */
233static NTSTATUS vboxVdmaGgThreadCreate(PKTHREAD * ppThread, PKSTART_ROUTINE pStartRoutine, PVOID pStartContext)
234{
235 NTSTATUS fStatus;
236 HANDLE hThread;
237 OBJECT_ATTRIBUTES fObjectAttributes;
238
239 Assert(KeGetCurrentIrql() == PASSIVE_LEVEL);
240
241 InitializeObjectAttributes(&fObjectAttributes, NULL, OBJ_KERNEL_HANDLE,
242 NULL, NULL);
243
244 fStatus = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS,
245 &fObjectAttributes, NULL, NULL,
246 (PKSTART_ROUTINE) pStartRoutine, pStartContext);
247 if (!NT_SUCCESS(fStatus))
248 return fStatus;
249
250 ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL,
251 KernelMode, (PVOID*) ppThread, NULL);
252 ZwClose(hThread);
253 return STATUS_SUCCESS;
254}
255
256DECLINLINE(void) vboxVdmaDirtyRectsCalcIntersection(const RECT *pArea, const PVBOXWDDM_RECTS_INFO pRects, PVBOXWDDM_RECTS_INFO pResult)
257{
258 pResult->cRects = 0;
259 for (uint32_t i = 0; i < pRects->cRects; ++i)
260 {
261 if (vboxWddmRectIntersection(pArea, &pRects->aRects[i], &pResult->aRects[pResult->cRects]))
262 {
263 ++pResult->cRects;
264 }
265 }
266}
267
268DECLINLINE(bool) vboxVdmaDirtyRectsHasIntersections(const RECT *paRects1, uint32_t cRects1, const RECT *paRects2, uint32_t cRects2)
269{
270 RECT tmpRect;
271 for (uint32_t i = 0; i < cRects1; ++i)
272 {
273 const RECT * pRect1 = &paRects1[i];
274 for (uint32_t j = 0; j < cRects2; ++j)
275 {
276 const RECT * pRect2 = &paRects2[j];
277 if (vboxWddmRectIntersection(pRect1, pRect2, &tmpRect))
278 return true;
279 }
280 }
281 return false;
282}
283
284DECLINLINE(bool) vboxVdmaDirtyRectsIsCover(const RECT *paRects, uint32_t cRects, const RECT *paRectsCovered, uint32_t cRectsCovered)
285{
286 for (uint32_t i = 0; i < cRectsCovered; ++i)
287 {
288 const RECT * pRectCovered = &paRectsCovered[i];
289 uint32_t j = 0;
290 for (; j < cRects; ++j)
291 {
292 const RECT * pRect = &paRects[j];
293 if (vboxWddmRectIsCoveres(pRect, pRectCovered))
294 break;
295 }
296 if (j == cRects)
297 return false;
298 }
299 return true;
300}
301
302/**
303 * @param pDevExt
304 */
305NTSTATUS vboxVdmaGgDirtyRectsProcess(VBOXVDMAPIPE_CMD_RECTSINFO *pRectsInfo)
306{
307 PVBOXWDDM_CONTEXT pContext = pRectsInfo->pContext;
308 PDEVICE_EXTENSION pDevExt = pContext->pDevice->pAdapter;
309 PVBOXWDDM_RECTS_INFO pRects = &pRectsInfo->ContextsRects.UpdateRects;
310 NTSTATUS Status = STATUS_SUCCESS;
311 PVBOXVIDEOCM_CMD_RECTS pCmd = NULL;
312 uint32_t cbCmd = VBOXVIDEOCM_CMD_RECTS_SIZE4CRECTS(pRects->cRects);
313 Assert(KeGetCurrentIrql() < DISPATCH_LEVEL);
314 ExAcquireFastMutex(&pDevExt->ContextMutex);
315 for (PLIST_ENTRY pCur = pDevExt->ContextList3D.Flink; pCur != &pDevExt->ContextList3D; pCur = pCur->Flink)
316 {
317 if (pCur != &pContext->ListEntry)
318 {
319 PVBOXWDDM_CONTEXT pCurContext = VBOXWDDMENTRY_2_CONTEXT(pCur);
320 if (!pCmd)
321 {
322 pCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdCreate(&pCurContext->CmContext, cbCmd);
323 Assert(pCmd);
324 if (!pCmd)
325 {
326 Status = STATUS_NO_MEMORY;
327 break;
328 }
329 }
330 else
331 {
332 pCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdReinitForContext(pCmd, &pCurContext->CmContext);
333 }
334
335 vboxVdmaDirtyRectsCalcIntersection(&pCurContext->ViewRect, pRects, &pCmd->RectsInfo);
336 if (pCmd->RectsInfo.cRects)
337 {
338 bool bSend = false;
339 pCmd->fFlags.Value = 0;
340 pCmd->fFlags.bAddHiddenRects = 1;
341 if (pCurContext->pLastReportedRects)
342 {
343 if (pCurContext->pLastReportedRects->fFlags.bSetVisibleRects)
344 {
345 RECT *paPrevRects;
346 uint32_t cPrevRects;
347 if (pCurContext->pLastReportedRects->fFlags.bSetViewRect)
348 {
349 paPrevRects = &pCurContext->pLastReportedRects->RectsInfo.aRects[1];
350 cPrevRects = pCurContext->pLastReportedRects->RectsInfo.cRects - 1;
351 }
352 else
353 {
354 paPrevRects = &pCurContext->pLastReportedRects->RectsInfo.aRects[0];
355 cPrevRects = pCurContext->pLastReportedRects->RectsInfo.cRects;
356 }
357
358 if (vboxVdmaDirtyRectsHasIntersections(paPrevRects, cPrevRects, pCmd->RectsInfo.aRects, pCmd->RectsInfo.cRects))
359 {
360 bSend = true;
361 }
362 }
363 else
364 {
365 Assert(pCurContext->pLastReportedRects->fFlags.bAddHiddenRects);
366 if (!vboxVdmaDirtyRectsIsCover(pCurContext->pLastReportedRects->RectsInfo.aRects,
367 pCurContext->pLastReportedRects->RectsInfo.cRects,
368 pCmd->RectsInfo.aRects, pCmd->RectsInfo.cRects))
369 {
370 bSend = true;
371 }
372 }
373 }
374 else
375 bSend = true;
376
377 if (bSend)
378 {
379 if (pCurContext->pLastReportedRects)
380 vboxVideoCmCmdRelease(pCurContext->pLastReportedRects);
381 vboxVideoCmCmdRetain(pCmd);
382 pCurContext->pLastReportedRects = pCmd;
383 vboxVideoCmCmdSubmit(pCmd, VBOXVIDEOCM_CMD_RECTS_SIZE4CRECTS(pCmd->RectsInfo.cRects));
384 pCmd = NULL;
385 }
386 }
387 }
388 else
389 {
390 RECT * pContextRect = &pRectsInfo->ContextsRects.ContextRect;
391 bool bRectShanged = (pContext->ViewRect.left != pContextRect->left
392 || pContext->ViewRect.top != pContextRect->top
393 || pContext->ViewRect.right != pContextRect->right
394 || pContext->ViewRect.bottom != pContextRect->bottom);
395 PVBOXVIDEOCM_CMD_RECTS pDrCmd;
396
397 bool bSend = false;
398
399 if (bRectShanged)
400 {
401 uint32_t cbDrCmd = VBOXVIDEOCM_CMD_RECTS_SIZE4CRECTS(pRects->cRects + 1);
402 pDrCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdCreate(&pContext->CmContext, cbDrCmd);
403 Assert(pDrCmd);
404 if (!pDrCmd)
405 {
406 Status = STATUS_NO_MEMORY;
407 break;
408 }
409 pDrCmd->fFlags.Value = 0;
410 pDrCmd->RectsInfo.cRects = pRects->cRects + 1;
411 pDrCmd->fFlags.bSetViewRect = 1;
412 pDrCmd->RectsInfo.aRects[0] = *pContextRect;
413 pContext->ViewRect = *pContextRect;
414 memcpy(&pDrCmd->RectsInfo.aRects[1], pRects->aRects, sizeof (RECT) * pRects->cRects);
415 bSend = true;
416 }
417 else
418 {
419 if (pCmd)
420 {
421 pDrCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdReinitForContext(pCmd, &pContext->CmContext);
422 pCmd = NULL;
423 }
424 else
425 {
426 pDrCmd = (PVBOXVIDEOCM_CMD_RECTS)vboxVideoCmCmdCreate(&pContext->CmContext, cbCmd);
427 Assert(pDrCmd);
428 if (!pDrCmd)
429 {
430 Status = STATUS_NO_MEMORY;
431 break;
432 }
433 }
434 pDrCmd->fFlags.Value = 0;
435 pDrCmd->RectsInfo.cRects = pRects->cRects;
436 memcpy(&pDrCmd->RectsInfo.aRects[0], pRects->aRects, sizeof (RECT) * pRects->cRects);
437
438 if (pContext->pLastReportedRects)
439 {
440 if (pContext->pLastReportedRects->fFlags.bSetVisibleRects)
441 {
442 RECT *paRects;
443 uint32_t cRects;
444 if (pContext->pLastReportedRects->fFlags.bSetViewRect)
445 {
446 paRects = &pContext->pLastReportedRects->RectsInfo.aRects[1];
447 cRects = pContext->pLastReportedRects->RectsInfo.cRects - 1;
448 }
449 else
450 {
451 paRects = &pContext->pLastReportedRects->RectsInfo.aRects[0];
452 cRects = pContext->pLastReportedRects->RectsInfo.cRects;
453 }
454 bSend = (pDrCmd->RectsInfo.cRects != cRects)
455 || memcmp(paRects, pDrCmd->RectsInfo.aRects, cRects * sizeof (RECT));
456 }
457 else
458 {
459 Assert(pContext->pLastReportedRects->fFlags.bAddHiddenRects);
460 bSend = true;
461 }
462 }
463 else
464 bSend = true;
465
466 }
467
468 Assert(pRects->cRects);
469 if (bSend)
470 {
471 if (pContext->pLastReportedRects)
472 vboxVideoCmCmdRelease(pContext->pLastReportedRects);
473
474 pDrCmd->fFlags.bSetVisibleRects = 1;
475
476 vboxVideoCmCmdRetain(pDrCmd);
477 pContext->pLastReportedRects = pDrCmd;
478 vboxVideoCmCmdSubmit(pDrCmd, VBOXVIDEOCM_SUBMITSIZE_DEFAULT);
479 }
480 else
481 {
482 if (!pCmd)
483 pCmd = pDrCmd;
484 else
485 vboxVideoCmCmdRelease(pDrCmd);
486 }
487 }
488 }
489 ExReleaseFastMutex(&pDevExt->ContextMutex);
490
491
492 if (pCmd)
493 vboxVideoCmCmdRelease(pCmd);
494
495 return Status;
496}
497
498
499static VOID vboxVdmaGgWorkerThread(PVOID pvUser)
500{
501 PVBOXVDMAGG pVdma = (PVBOXVDMAGG)pvUser;
502
503 NTSTATUS Status = vboxVdmaPipeSvrOpen(&pVdma->CmdPipe);
504 Assert(Status == STATUS_SUCCESS);
505 if (Status == STATUS_SUCCESS)
506 {
507 do
508 {
509 LIST_ENTRY CmdList;
510 Status = vboxVdmaPipeSvrCmdGetList(&pVdma->CmdPipe, &CmdList);
511 Assert(Status == STATUS_SUCCESS || Status == STATUS_PIPE_CLOSING);
512 if (Status == STATUS_SUCCESS)
513 {
514 for (PLIST_ENTRY pCur = CmdList.Blink; pCur != &CmdList; pCur = CmdList.Blink)
515 {
516 PVBOXVDMAPIPE_CMD_DR pDr = VBOXVDMAPIPE_CMD_DR_FROM_ENTRY(pCur);
517 switch (pDr->enmType)
518 {
519 case VBOXVDMAPIPE_CMD_TYPE_RECTSINFO:
520 {
521 PVBOXVDMAPIPE_CMD_RECTSINFO pRects = (PVBOXVDMAPIPE_CMD_RECTSINFO)pDr;
522 Status = vboxVdmaGgDirtyRectsProcess(pRects);
523 Assert(Status == STATUS_SUCCESS);
524 break;
525 }
526 case VBOXVDMAPIPE_CMD_TYPE_DMACMD:
527 default:
528 AssertBreakpoint();
529 }
530 RemoveEntryList(pCur);
531 vboxVdmaGgCmdDestroy(pDr);
532 }
533 }
534 else
535 break;
536 } while (1);
537 }
538
539 /* always try to close the pipe to make sure the client side is notified */
540 Status = vboxVdmaPipeSvrClose(&pVdma->CmdPipe);
541 Assert(Status == STATUS_SUCCESS);
542}
543
544NTSTATUS vboxVdmaGgConstruct(PVBOXVDMAGG pVdma)
545{
546 NTSTATUS Status = vboxVdmaPipeConstruct(&pVdma->CmdPipe);
547 Assert(Status == STATUS_SUCCESS);
548 if (Status == STATUS_SUCCESS)
549 {
550 Status = vboxVdmaGgThreadCreate(&pVdma->pThread, vboxVdmaGgWorkerThread, pVdma);
551 Assert(Status == STATUS_SUCCESS);
552 if (Status == STATUS_SUCCESS)
553 return STATUS_SUCCESS;
554
555 NTSTATUS tmpStatus = vboxVdmaPipeDestruct(&pVdma->CmdPipe);
556 Assert(tmpStatus == STATUS_SUCCESS);
557 }
558
559 /* we're here ONLY in case of an error */
560 Assert(Status != STATUS_SUCCESS);
561 return Status;
562}
563
564NTSTATUS vboxVdmaGgDestruct(PVBOXVDMAGG pVdma)
565{
566 /* this informs the server thread that it should complete all current commands and exit */
567 NTSTATUS Status = vboxVdmaPipeCltClose(&pVdma->CmdPipe);
568 Assert(Status == STATUS_SUCCESS);
569 if (Status == STATUS_SUCCESS)
570 {
571 Status = KeWaitForSingleObject(pVdma->pThread, Executive, KernelMode, FALSE, NULL /* PLARGE_INTEGER Timeout */);
572 Assert(Status == STATUS_SUCCESS);
573 if (Status == STATUS_SUCCESS)
574 {
575 Status = vboxVdmaPipeDestruct(&pVdma->CmdPipe);
576 Assert(Status == STATUS_SUCCESS);
577 }
578 }
579
580 return Status;
581}
582
583NTSTATUS vboxVdmaGgCmdSubmit(PVBOXVDMAGG pVdma, PVBOXVDMAPIPE_CMD_DR pCmd)
584{
585 return vboxVdmaPipeCltCmdPut(&pVdma->CmdPipe, &pCmd->PipeHdr);
586}
587
588/* end */
589
590/*
591 * This is currently used by VDMA. It is invisible for Vdma API clients since
592 * Vdma transport may change if we choose to use another (e.g. more light-weight)
593 * transport for DMA commands submission
594 */
595
596#ifdef VBOXVDMA_WITH_VBVA
597static int vboxWddmVdmaSubmitVbva(struct _DEVICE_EXTENSION* pDevExt, PVBOXVDMAINFO pInfo, HGSMIOFFSET offDr)
598{
599 int rc;
600 if (vboxVbvaBufferBeginUpdate (pDevExt, &pDevExt->u.primary.Vbva))
601 {
602 rc = vboxVbvaReportCmdOffset(pDevExt, &pDevExt->u.primary.Vbva, offDr);
603 vboxVbvaBufferEndUpdate (pDevExt, &pDevExt->u.primary.Vbva);
604 }
605 else
606 {
607 AssertBreakpoint();
608 rc = VERR_INVALID_STATE;
609 }
610 return rc;
611}
612#define vboxWddmVdmaSubmit vboxWddmVdmaSubmitVbva
613#else
614static int vboxWddmVdmaSubmitHgsmi(struct _DEVICE_EXTENSION* pDevExt, PVBOXVDMAINFO pInfo, HGSMIOFFSET offDr)
615{
616 VBoxHGSMIGuestWrite(pDevExt, offDr);
617 return VINF_SUCCESS;
618}
619#define vboxWddmVdmaSubmit vboxWddmVdmaSubmitHgsmi
620#endif
621
622static int vboxVdmaInformHost (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo, VBOXVDMA_CTL_TYPE enmCtl)
623{
624 int rc = VINF_SUCCESS;
625
626 PVBOXVDMA_CTL pCmd = (PVBOXVDMA_CTL)VBoxSHGSMICommandAlloc(&pDevExt->u.primary.hgsmiAdapterHeap, sizeof (VBOXVDMA_CTL), HGSMI_CH_VBVA, VBVA_VDMA_CTL);
627 if (pCmd)
628 {
629 pCmd->enmCtl = enmCtl;
630 pCmd->u32Offset = pInfo->CmdHeap.area.offBase;
631 pCmd->i32Result = VERR_NOT_SUPPORTED;
632
633 const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepSynch(&pDevExt->u.primary.hgsmiAdapterHeap, pCmd);
634 Assert(pHdr);
635 if (pHdr)
636 {
637 do
638 {
639 HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&pDevExt->u.primary.hgsmiAdapterHeap, pHdr);
640 Assert(offCmd != HGSMIOFFSET_VOID);
641 if (offCmd != HGSMIOFFSET_VOID)
642 {
643 rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
644 AssertRC(rc);
645 if (RT_SUCCESS(rc))
646 {
647 rc = VBoxSHGSMICommandDoneSynch(&pDevExt->u.primary.hgsmiAdapterHeap, pHdr);
648 AssertRC(rc);
649 if (RT_SUCCESS(rc))
650 {
651 rc = pCmd->i32Result;
652 AssertRC(rc);
653 }
654 break;
655 }
656 }
657 else
658 rc = VERR_INVALID_PARAMETER;
659 /* fail to submit, cancel it */
660 VBoxSHGSMICommandCancelSynch(&pDevExt->u.primary.hgsmiAdapterHeap, pHdr);
661 } while (0);
662 }
663
664 VBoxSHGSMICommandFree (&pDevExt->u.primary.hgsmiAdapterHeap, pCmd);
665 }
666 else
667 {
668 drprintf((__FUNCTION__": HGSMIHeapAlloc failed\n"));
669 rc = VERR_OUT_OF_RESOURCES;
670 }
671
672 return rc;
673}
674
675/* create a DMACommand buffer */
676int vboxVdmaCreate (PDEVICE_EXTENSION pDevExt, VBOXVDMAINFO *pInfo, ULONG offBuffer, ULONG cbBuffer)
677{
678 Assert((offBuffer & 0xfff) == 0);
679 Assert((cbBuffer & 0xfff) == 0);
680 Assert(offBuffer);
681 Assert(cbBuffer);
682
683 if((offBuffer & 0xfff)
684 || (cbBuffer & 0xfff)
685 || !offBuffer
686 || !cbBuffer)
687 {
688 drprintf((__FUNCTION__": invalid parameters: offBuffer(0x%x), cbBuffer(0x%x)", offBuffer, cbBuffer));
689 return VERR_INVALID_PARAMETER;
690 }
691
692 pInfo->fEnabled = FALSE;
693 PVOID pvBuffer;
694
695 int rc = VBoxMapAdapterMemory (pDevExt,
696 &pvBuffer,
697 offBuffer,
698 cbBuffer);
699 Assert(RT_SUCCESS(rc));
700 if (RT_SUCCESS(rc))
701 {
702 /* Setup a HGSMI heap within the adapter information area. */
703 rc = HGSMIHeapSetup (&pInfo->CmdHeap,
704 pvBuffer,
705 cbBuffer,
706 offBuffer,
707 false /*fOffsetBased*/);
708 Assert(RT_SUCCESS(rc));
709 if(RT_SUCCESS(rc))
710 {
711 NTSTATUS Status = vboxVdmaGgConstruct(&pInfo->DmaGg);
712 Assert(Status == STATUS_SUCCESS);
713 if (Status == STATUS_SUCCESS)
714 return VINF_SUCCESS;
715 rc = VERR_GENERAL_FAILURE;
716 }
717 else
718 drprintf((__FUNCTION__": HGSMIHeapSetup failed rc = 0x%x\n", rc));
719
720 VBoxUnmapAdapterMemory(pDevExt, &pvBuffer, cbBuffer);
721 }
722 else
723 drprintf((__FUNCTION__": VBoxMapAdapterMemory failed rc = 0x%x\n", rc));
724
725 return rc;
726}
727
728int vboxVdmaDisable (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
729{
730 dfprintf((__FUNCTION__"\n"));
731
732 Assert(pInfo->fEnabled);
733 if (!pInfo->fEnabled)
734 return VINF_ALREADY_INITIALIZED;
735
736 /* ensure nothing else is submitted */
737 pInfo->fEnabled = FALSE;
738
739 int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_DISABLE);
740 AssertRC(rc);
741 return rc;
742}
743
744int vboxVdmaEnable (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
745{
746 dfprintf((__FUNCTION__"\n"));
747
748 Assert(!pInfo->fEnabled);
749 if (pInfo->fEnabled)
750 return VINF_ALREADY_INITIALIZED;
751
752 int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_ENABLE);
753 Assert(RT_SUCCESS(rc));
754 if (RT_SUCCESS(rc))
755 pInfo->fEnabled = TRUE;
756
757 return rc;
758}
759
760int vboxVdmaFlush (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
761{
762 dfprintf((__FUNCTION__"\n"));
763
764 Assert(pInfo->fEnabled);
765 if (!pInfo->fEnabled)
766 return VINF_ALREADY_INITIALIZED;
767
768 int rc = vboxVdmaInformHost (pDevExt, pInfo, VBOXVDMA_CTL_TYPE_FLUSH);
769 Assert(RT_SUCCESS(rc));
770
771 return rc;
772}
773
774int vboxVdmaDestroy (PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo)
775{
776 int rc = VINF_SUCCESS;
777 NTSTATUS Status = vboxVdmaGgDestruct(&pInfo->DmaGg);
778 Assert(Status == STATUS_SUCCESS);
779 if (Status == STATUS_SUCCESS)
780 {
781 Assert(!pInfo->fEnabled);
782 if (pInfo->fEnabled)
783 rc = vboxVdmaDisable (pDevExt, pInfo);
784 VBoxUnmapAdapterMemory (pDevExt, (void**)&pInfo->CmdHeap.area.pu8Base, pInfo->CmdHeap.area.cbArea);
785 }
786 else
787 rc = VERR_GENERAL_FAILURE;
788 return rc;
789}
790
791void vboxVdmaCBufDrFree (PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
792{
793 VBoxSHGSMICommandFree (&pInfo->CmdHeap, pDr);
794}
795
796PVBOXVDMACBUF_DR vboxVdmaCBufDrCreate (PVBOXVDMAINFO pInfo, uint32_t cbTrailingData)
797{
798 uint32_t cbDr = VBOXVDMACBUF_DR_SIZE(cbTrailingData);
799 PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)VBoxSHGSMICommandAlloc (&pInfo->CmdHeap, cbDr, HGSMI_CH_VBVA, VBVA_VDMA_CMD);
800 Assert(pDr);
801 if (pDr)
802 memset (pDr, 0, cbDr);
803 else
804 drprintf((__FUNCTION__": VBoxSHGSMICommandAlloc returned NULL\n"));
805
806 return pDr;
807}
808
809static DECLCALLBACK(void) vboxVdmaCBufDrCompletion(struct _HGSMIHEAP * pHeap, void *pvCmd, void *pvContext)
810{
811 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pvContext;
812 PVBOXVDMAINFO pInfo = &pDevExt->u.primary.Vdma;
813
814 vboxVdmaCBufDrFree (pInfo, (PVBOXVDMACBUF_DR)pvCmd);
815}
816
817static DECLCALLBACK(void) vboxVdmaCBufDrCompletionIrq(struct _HGSMIHEAP * pHeap, void *pvCmd, void *pvContext,
818 PFNVBOXSHGSMICMDCOMPLETION *ppfnCompletion, void **ppvCompletion)
819{
820 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pvContext;
821 PVBOXVDMAINFO pVdma = &pDevExt->u.primary.Vdma;
822 DXGKARGCB_NOTIFY_INTERRUPT_DATA notify;
823 PVBOXVDMACBUF_DR pDr = (PVBOXVDMACBUF_DR)pvCmd;
824
825 memset(&notify, 0, sizeof(DXGKARGCB_NOTIFY_INTERRUPT_DATA));
826
827 PVBOXWDDM_CONTEXT pContext = (PVBOXWDDM_CONTEXT)pDr->u64GuestContext;
828
829 if (RT_SUCCESS(pDr->rc))
830 {
831 notify.InterruptType = DXGK_INTERRUPT_DMA_COMPLETED;
832 notify.DmaCompleted.SubmissionFenceId = pDr->u32FenceId;
833 if (pContext)
834 {
835 notify.DmaCompleted.NodeOrdinal = pContext->NodeOrdinal;
836 notify.DmaCompleted.EngineOrdinal = 0;
837 pContext->uLastCompletedCmdFenceId = pDr->u32FenceId;
838 }
839 else
840 pVdma->uLastCompletedPagingBufferCmdFenceId = pDr->u32FenceId;
841 pDevExt->bSetNotifyDxDpc = TRUE;
842 }
843 else if (pDr->rc == VERR_INTERRUPTED)
844 {
845 notify.InterruptType = DXGK_INTERRUPT_DMA_PREEMPTED;
846 notify.DmaPreempted.PreemptionFenceId = pDr->u32FenceId;
847 if (pContext)
848 {
849 notify.DmaPreempted.LastCompletedFenceId = pContext->uLastCompletedCmdFenceId;
850 notify.DmaPreempted.NodeOrdinal = pContext->NodeOrdinal;
851 notify.DmaPreempted.EngineOrdinal = 0;
852 }
853 else
854 notify.DmaPreempted.LastCompletedFenceId = pVdma->uLastCompletedPagingBufferCmdFenceId;
855
856 pDevExt->bSetNotifyDxDpc = TRUE;
857 }
858 else
859 {
860 AssertBreakpoint();
861 notify.InterruptType = DXGK_INTERRUPT_DMA_FAULTED;
862 notify.DmaFaulted.FaultedFenceId = pDr->u32FenceId;
863 notify.DmaFaulted.Status = STATUS_UNSUCCESSFUL; /* @todo: better status ? */
864 if (pContext)
865 {
866 notify.DmaFaulted.NodeOrdinal = pContext->NodeOrdinal;
867 notify.DmaFaulted.EngineOrdinal = 0;
868 }
869 pDevExt->bSetNotifyDxDpc = TRUE;
870 }
871
872 pDevExt->u.primary.DxgkInterface.DxgkCbNotifyInterrupt(pDevExt->u.primary.DxgkInterface.DeviceHandle, &notify);
873
874 /* inform SHGSMI we want to be called at DPC later */
875 *ppfnCompletion = vboxVdmaCBufDrCompletion;
876 *ppvCompletion = pvContext;
877}
878
879int vboxVdmaCBufDrSubmit(PDEVICE_EXTENSION pDevExt, PVBOXVDMAINFO pInfo, PVBOXVDMACBUF_DR pDr)
880{
881 const VBOXSHGSMIHEADER* pHdr = VBoxSHGSMICommandPrepAsynchIrq (&pInfo->CmdHeap, pDr, vboxVdmaCBufDrCompletionIrq, pDevExt, VBOXSHGSMI_FLAG_GH_ASYNCH_FORCE);
882 Assert(pHdr);
883 int rc = VERR_GENERAL_FAILURE;
884 if (pHdr)
885 {
886 do
887 {
888 HGSMIOFFSET offCmd = VBoxSHGSMICommandOffset(&pInfo->CmdHeap, pHdr);
889 Assert(offCmd != HGSMIOFFSET_VOID);
890 if (offCmd != HGSMIOFFSET_VOID)
891 {
892 rc = vboxWddmVdmaSubmit(pDevExt, pInfo, offCmd);
893 AssertRC(rc);
894 if (RT_SUCCESS(rc))
895 {
896 VBoxSHGSMICommandDoneAsynch(&pInfo->CmdHeap, pHdr);
897 AssertRC(rc);
898 break;
899 }
900 }
901 else
902 rc = VERR_INVALID_PARAMETER;
903 /* fail to submit, cancel it */
904 VBoxSHGSMICommandCancelAsynch(&pInfo->CmdHeap, pHdr);
905 } while (0);
906 }
907 else
908 rc = VERR_INVALID_PARAMETER;
909 return rc;
910}
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