VirtualBox

source: vbox/trunk/src/VBox/Devices/Graphics/DevVGA_VDMA.cpp@ 31193

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

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.2 KB
Line 
1/** @file
2 * Video DMA (VDMA) support.
3 */
4
5/*
6 * Copyright (C) 2006-2009 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16//#include <VBox/VMMDev.h>
17#include <VBox/pdmdev.h>
18#include <VBox/VBoxVideo.h>
19#include <iprt/semaphore.h>
20#include <iprt/thread.h>
21#include <iprt/mem.h>
22
23#include "DevVGA.h"
24#include "HGSMI/SHGSMIHost.h"
25#include "HGSMI/HGSMIHostHlp.h"
26
27typedef enum
28{
29 VBOXVDMAPIPE_STATE_CLOSED = 0,
30 VBOXVDMAPIPE_STATE_CREATED = 1,
31 VBOXVDMAPIPE_STATE_OPENNED = 2,
32 VBOXVDMAPIPE_STATE_CLOSING = 3
33} VBOXVDMAPIPE_STATE;
34
35typedef struct VBOXVDMAPIPE
36{
37 RTSEMEVENT hEvent;
38 /* critical section for accessing pipe properties */
39 RTCRITSECT hCritSect;
40 VBOXVDMAPIPE_STATE enmState;
41 /* true iff the other end needs Event notification */
42 bool bNeedNotify;
43} VBOXVDMAPIPE, *PVBOXVDMAPIPE;
44
45typedef enum
46{
47 VBOXVDMAPIPE_CMD_TYPE_UNDEFINED = 0,
48 VBOXVDMAPIPE_CMD_TYPE_DMACMD = 1,
49 VBOXVDMAPIPE_CMD_TYPE_DMACTL = 2
50} VBOXVDMAPIPE_CMD_TYPE;
51
52typedef struct VBOXVDMAPIPE_CMD_BODY
53{
54 VBOXVDMAPIPE_CMD_TYPE enmType;
55 union
56 {
57 PVBOXVDMACBUF_DR pDr;
58 PVBOXVDMA_CTL pCtl;
59 void *pvCmd;
60 } u;
61}VBOXVDMAPIPE_CMD_BODY, *PVBOXVDMAPIPE_CMD_BODY;
62
63typedef struct VBOXVDMAPIPE_CMD
64{
65 HGSMILISTENTRY Entry;
66 VBOXVDMAPIPE_CMD_BODY Cmd;
67} VBOXVDMAPIPE_CMD, *PVBOXVDMAPIPE_CMD;
68
69#define VBOXVDMAPIPE_CMD_FROM_ENTRY(_pE) ( (PVBOXVDMAPIPE_CMD)((uint8_t *)(_pE) - RT_OFFSETOF(VBOXVDMAPIPE_CMD, Entry)) )
70
71typedef struct VBOXVDMAPIPE_CMD_POOL
72{
73 HGSMILIST List;
74 uint32_t cCmds;
75 VBOXVDMAPIPE_CMD aCmds[1];
76} VBOXVDMAPIPE_CMD_POOL, *PVBOXVDMAPIPE_CMD_POOL;
77
78typedef struct VBOXVDMAHOST
79{
80 VBOXVDMAPIPE Pipe;
81 HGSMILIST PendingList;
82 RTTHREAD hWorkerThread;
83 PHGSMIINSTANCE pHgsmi;
84 PVGASTATE pVGAState;
85 bool bEnabled;
86 VBOXVDMAPIPE_CMD_POOL CmdPool;
87} VBOXVDMAHOST, *PVBOXVDMAHOST;
88
89/* to simplify things and to avoid extra backend if modifications we assume the VBOXVDMA_RECTL is the same as VBVACMDHDR */
90AssertCompile(sizeof(VBOXVDMA_RECTL) == sizeof(VBVACMDHDR));
91AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, left) == RT_SIZEOFMEMB(VBVACMDHDR, x));
92AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, top) == RT_SIZEOFMEMB(VBVACMDHDR, y));
93AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, width) == RT_SIZEOFMEMB(VBVACMDHDR, w));
94AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, height) == RT_SIZEOFMEMB(VBVACMDHDR, h));
95AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, left) == RT_OFFSETOF(VBVACMDHDR, x));
96AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, top) == RT_OFFSETOF(VBVACMDHDR, y));
97AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, width) == RT_OFFSETOF(VBVACMDHDR, w));
98AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, height) == RT_OFFSETOF(VBVACMDHDR, h));
99
100static int vboxVDMANotifyPrimaryUpdate (PVGASTATE pVGAState, unsigned uScreenId, const VBOXVDMA_RECTL * pRectl)
101{
102 pVGAState->pDrv->pfnVBVAUpdateBegin (pVGAState->pDrv, uScreenId);
103
104 /* Updates the rectangle and sends the command to the VRDP server. */
105 pVGAState->pDrv->pfnVBVAUpdateProcess (pVGAState->pDrv, uScreenId,
106 (const PVBVACMDHDR)pRectl /* <- see above AssertCompile's and comments */,
107 sizeof (VBOXVDMA_RECTL));
108
109 pVGAState->pDrv->pfnVBVAUpdateEnd (pVGAState->pDrv, uScreenId, pRectl->left, pRectl->top,
110 pRectl->width, pRectl->height);
111
112 return VINF_SUCCESS;
113}
114
115static int vboxVDMACmdExecBltPerform(PVBOXVDMAHOST pVdma,
116 uint8_t *pvDstSurf, const uint8_t *pvSrcSurf,
117 const PVBOXVDMA_SURF_DESC pDstDesc, const PVBOXVDMA_SURF_DESC pSrcDesc,
118 const VBOXVDMA_RECTL * pDstRectl, const VBOXVDMA_RECTL * pSrcRectl)
119{
120 /* we do not support color conversion */
121 Assert(pDstDesc->format == pSrcDesc->format);
122 /* we do not support stretching */
123 Assert(pDstRectl->height == pSrcRectl->height);
124 Assert(pDstRectl->width == pSrcRectl->width);
125 if (pDstDesc->format != pSrcDesc->format)
126 return VERR_INVALID_FUNCTION;
127 if (pDstDesc->width == pDstRectl->width
128 && pSrcDesc->width == pSrcRectl->width
129 && pSrcDesc->width == pDstDesc->width)
130 {
131 Assert(!pDstRectl->left);
132 Assert(!pSrcRectl->left);
133 uint32_t cbOff = pDstDesc->pitch * pDstRectl->top;
134 uint32_t cbSize = pDstDesc->pitch * pDstRectl->height;
135 memcpy(pvDstSurf + cbOff, pvSrcSurf + cbOff, cbSize);
136 }
137 else
138 {
139 uint32_t offDstLineStart = pDstRectl->left * pDstDesc->bpp >> 3;
140 uint32_t offDstLineEnd = ((pDstRectl->left * pDstDesc->bpp + 7) >> 3) + ((pDstDesc->bpp * pDstRectl->width + 7) >> 3);
141 uint32_t cbDstLine = offDstLineEnd - offDstLineStart;
142 uint32_t offDstStart = pDstDesc->pitch * pDstRectl->top + offDstLineStart;
143 Assert(cbDstLine <= pDstDesc->pitch);
144 uint32_t cbDstSkip = pDstDesc->pitch;
145 uint8_t * pvDstStart = pvDstSurf + offDstStart;
146
147 uint32_t offSrcLineStart = pSrcRectl->left * pSrcDesc->bpp >> 3;
148 uint32_t offSrcLineEnd = ((pSrcRectl->left * pSrcDesc->bpp + 7) >> 3) + ((pSrcDesc->bpp * pSrcRectl->width + 7) >> 3);
149 uint32_t cbSrcLine = offSrcLineEnd - offSrcLineStart;
150 uint32_t offSrcStart = pSrcDesc->pitch * pSrcRectl->top + offSrcLineStart;
151 Assert(cbSrcLine <= pSrcDesc->pitch);
152 uint32_t cbSrcSkip = pSrcDesc->pitch;
153 const uint8_t * pvSrcStart = pvSrcSurf + offSrcStart;
154
155 Assert(cbDstLine == cbSrcLine);
156
157 for (uint32_t i = 0; ; ++i)
158 {
159 memcpy (pvDstStart, pvSrcStart, cbDstLine);
160 if (i == pDstRectl->height)
161 break;
162 pvDstStart += cbDstSkip;
163 pvSrcStart += cbSrcSkip;
164 }
165 }
166 return VINF_SUCCESS;
167}
168
169static void vboxVDMARectlUnite(VBOXVDMA_RECTL * pRectl1, const VBOXVDMA_RECTL * pRectl2)
170{
171 if (!pRectl1->width)
172 *pRectl1 = *pRectl2;
173 else
174 {
175 int16_t x21 = pRectl1->left + pRectl1->width;
176 int16_t x22 = pRectl2->left + pRectl2->width;
177 if (pRectl1->left > pRectl2->left)
178 {
179 pRectl1->left = pRectl2->left;
180 pRectl1->width = x21 < x22 ? x22 - pRectl1->left : x21 - pRectl1->left;
181 }
182 else if (x21 < x22)
183 pRectl1->width = x22 - pRectl1->left;
184
185 x21 = pRectl1->top + pRectl1->height;
186 x22 = pRectl2->top + pRectl2->height;
187 if (pRectl1->top > pRectl2->top)
188 {
189 pRectl1->top = pRectl2->top;
190 pRectl1->height = x21 < x22 ? x22 - pRectl1->top : x21 - pRectl1->top;
191 }
192 else if (x21 < x22)
193 pRectl1->height = x22 - pRectl1->top;
194 }
195}
196
197/*
198 * @return on success the number of bytes the command contained, otherwise - VERR_xxx error code
199 */
200static int vboxVDMACmdExecBlt(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt, uint32_t cbBuffer)
201{
202 const uint32_t cbBlt = VBOXVDMACMD_BODY_FIELD_OFFSET(uint32_t, VBOXVDMACMD_DMA_PRESENT_BLT, aDstSubRects[pBlt->cDstSubRects]);
203 Assert(cbBlt <= cbBuffer);
204 if (cbBuffer < cbBlt)
205 return VERR_INVALID_FUNCTION;
206
207 /* we do not support stretching for now */
208 Assert(pBlt->srcRectl.width == pBlt->dstRectl.width);
209 Assert(pBlt->srcRectl.height == pBlt->dstRectl.height);
210 if (pBlt->srcRectl.width != pBlt->dstRectl.width)
211 return VERR_INVALID_FUNCTION;
212 if (pBlt->srcRectl.height != pBlt->dstRectl.height)
213 return VERR_INVALID_FUNCTION;
214 Assert(pBlt->cDstSubRects);
215
216 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
217 VBOXVDMA_RECTL updateRectl = {0};
218
219 if (pBlt->cDstSubRects)
220 {
221 VBOXVDMA_RECTL dstRectl, srcRectl;
222 const VBOXVDMA_RECTL *pDstRectl, *pSrcRectl;
223 for (uint32_t i = 0; i < pBlt->cDstSubRects; ++i)
224 {
225 pDstRectl = &pBlt->aDstSubRects[i];
226 if (pBlt->dstRectl.left || pBlt->dstRectl.top)
227 {
228 dstRectl.left = pDstRectl->left + pBlt->dstRectl.left;
229 dstRectl.top = pDstRectl->top + pBlt->dstRectl.top;
230 dstRectl.width = pDstRectl->width;
231 dstRectl.height = pDstRectl->height;
232 pDstRectl = &dstRectl;
233 }
234
235 pSrcRectl = &pBlt->aDstSubRects[i];
236 if (pBlt->srcRectl.left || pBlt->srcRectl.top)
237 {
238 srcRectl.left = pSrcRectl->left + pBlt->srcRectl.left;
239 srcRectl.top = pSrcRectl->top + pBlt->srcRectl.top;
240 srcRectl.width = pSrcRectl->width;
241 srcRectl.height = pSrcRectl->height;
242 pSrcRectl = &srcRectl;
243 }
244
245 int rc = vboxVDMACmdExecBltPerform(pVdma, pvRam + pBlt->offDst, pvRam + pBlt->offSrc,
246 &pBlt->dstDesc, &pBlt->srcDesc,
247 pDstRectl,
248 pSrcRectl);
249 AssertRC(rc);
250 if (!RT_SUCCESS(rc))
251 return rc;
252
253 vboxVDMARectlUnite(&updateRectl, pDstRectl);
254 }
255 }
256 else
257 {
258 int rc = vboxVDMACmdExecBltPerform(pVdma, pvRam + pBlt->offDst, pvRam + pBlt->offSrc,
259 &pBlt->dstDesc, &pBlt->srcDesc,
260 &pBlt->dstRectl,
261 &pBlt->srcRectl);
262 AssertRC(rc);
263 if (!RT_SUCCESS(rc))
264 return rc;
265
266 vboxVDMARectlUnite(&updateRectl, &pBlt->dstRectl);
267 }
268
269 int iView = 0;
270 /* @todo: fixme: check if update is needed and get iView */
271 vboxVDMANotifyPrimaryUpdate (pVdma->pVGAState, iView, &updateRectl);
272
273 return cbBlt;
274}
275
276static int vboxVDMACmdExec(PVBOXVDMAHOST pVdma, const uint8_t *pvBuffer, uint32_t cbBuffer)
277{
278 do
279 {
280 Assert(pvBuffer);
281 Assert(cbBuffer >= VBOXVDMACMD_HEADER_SIZE());
282
283 if (!pvBuffer)
284 return VERR_INVALID_PARAMETER;
285 if (cbBuffer < VBOXVDMACMD_HEADER_SIZE())
286 return VERR_INVALID_PARAMETER;
287
288 PVBOXVDMACMD pCmd = (PVBOXVDMACMD)pvBuffer;
289 uint32_t cbCmd = 0;
290 switch (pCmd->enmType)
291 {
292 case VBOXVDMACMD_TYPE_DMA_PRESENT_BLT:
293 {
294 const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt = VBOXVDMACMD_BODY(pCmd, VBOXVDMACMD_DMA_PRESENT_BLT);
295 int cbBlt = vboxVDMACmdExecBlt(pVdma, pBlt, cbBuffer);
296 Assert(cbBlt >= 0);
297 Assert((uint32_t)cbBlt <= cbBuffer);
298 if (cbBlt >= 0)
299 {
300 if (cbBlt == cbBuffer)
301 return VINF_SUCCESS;
302 else
303 {
304 cbBuffer -= (uint32_t)cbBlt;
305 pvBuffer -= cbBlt;
306 }
307 }
308 else
309 return cbBlt; /* error */
310 break;
311 }
312 default:
313 AssertBreakpoint();
314 return VERR_INVALID_FUNCTION;
315 }
316 } while (1);
317
318 /* we should not be here */
319 AssertBreakpoint();
320 return VERR_INVALID_STATE;
321}
322
323int vboxVDMAPipeConstruct(PVBOXVDMAPIPE pPipe)
324{
325 int rc = RTSemEventCreate(&pPipe->hEvent);
326 AssertRC(rc);
327 if (RT_SUCCESS(rc))
328 {
329 rc = RTCritSectInit(&pPipe->hCritSect);
330 AssertRC(rc);
331 if (RT_SUCCESS(rc))
332 {
333 pPipe->enmState = VBOXVDMAPIPE_STATE_CREATED;
334 pPipe->bNeedNotify = true;
335 return VINF_SUCCESS;
336// RTCritSectDelete(pPipe->hCritSect);
337 }
338 RTSemEventDestroy(pPipe->hEvent);
339 }
340 return rc;
341}
342
343int vboxVDMAPipeOpenServer(PVBOXVDMAPIPE pPipe)
344{
345 int rc = RTCritSectEnter(&pPipe->hCritSect);
346 AssertRC(rc);
347 if (RT_SUCCESS(rc))
348 {
349 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
350 switch (pPipe->enmState)
351 {
352 case VBOXVDMAPIPE_STATE_CREATED:
353 pPipe->enmState = VBOXVDMAPIPE_STATE_OPENNED;
354 pPipe->bNeedNotify = false;
355 rc = VINF_SUCCESS;
356 break;
357 case VBOXVDMAPIPE_STATE_OPENNED:
358 pPipe->bNeedNotify = false;
359 rc = VINF_ALREADY_INITIALIZED;
360 break;
361 default:
362 AssertBreakpoint();
363 rc = VERR_INVALID_STATE;
364 break;
365 }
366
367 RTCritSectLeave(&pPipe->hCritSect);
368 }
369 return rc;
370}
371
372int vboxVDMAPipeCloseServer(PVBOXVDMAPIPE pPipe)
373{
374 int rc = RTCritSectEnter(&pPipe->hCritSect);
375 AssertRC(rc);
376 if (RT_SUCCESS(rc))
377 {
378 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
379 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
380 switch (pPipe->enmState)
381 {
382 case VBOXVDMAPIPE_STATE_CLOSING:
383 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
384 rc = VINF_SUCCESS;
385 break;
386 case VBOXVDMAPIPE_STATE_CLOSED:
387 rc = VINF_ALREADY_INITIALIZED;
388 break;
389 default:
390 AssertBreakpoint();
391 rc = VERR_INVALID_STATE;
392 break;
393 }
394
395 RTCritSectLeave(&pPipe->hCritSect);
396 }
397 return rc;
398}
399
400int vboxVDMAPipeCloseClient(PVBOXVDMAPIPE pPipe)
401{
402 int rc = RTCritSectEnter(&pPipe->hCritSect);
403 AssertRC(rc);
404 if (RT_SUCCESS(rc))
405 {
406 bool bNeedNotify = false;
407 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
408 || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED
409 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
410 switch (pPipe->enmState)
411 {
412 case VBOXVDMAPIPE_STATE_OPENNED:
413 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSING;
414 bNeedNotify = pPipe->bNeedNotify;
415 pPipe->bNeedNotify = false;
416 break;
417 case VBOXVDMAPIPE_STATE_CREATED:
418 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
419 pPipe->bNeedNotify = false;
420 break;
421 case VBOXVDMAPIPE_STATE_CLOSED:
422 rc = VINF_ALREADY_INITIALIZED;
423 break;
424 default:
425 AssertBreakpoint();
426 rc = VERR_INVALID_STATE;
427 break;
428 }
429
430 RTCritSectLeave(&pPipe->hCritSect);
431
432 if (bNeedNotify)
433 {
434 rc = RTSemEventSignal(pPipe->hEvent);
435 AssertRC(rc);
436 }
437 }
438 return rc;
439}
440
441
442typedef DECLCALLBACK(bool) FNHVBOXVDMARWCB(PVBOXVDMAPIPE pPipe, void *pvCallback);
443typedef FNHVBOXVDMARWCB *PFNHVBOXVDMARWCB;
444
445int vboxVDMAPipeModifyServer(PVBOXVDMAPIPE pPipe, PFNHVBOXVDMARWCB pfnCallback, void * pvCallback)
446{
447 int rc = RTCritSectEnter(&pPipe->hCritSect);
448 AssertRC(rc);
449 if (RT_SUCCESS(rc))
450 {
451 do
452 {
453 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED
454 || pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING);
455
456 if (pPipe->enmState >= VBOXVDMAPIPE_STATE_OPENNED)
457 {
458 bool bProcessing = pfnCallback(pPipe, pvCallback);
459 pPipe->bNeedNotify = !bProcessing;
460 if (bProcessing)
461 {
462 RTCritSectLeave(&pPipe->hCritSect);
463 rc = VINF_SUCCESS;
464 break;
465 }
466 else if (pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSING)
467 {
468 pPipe->enmState = VBOXVDMAPIPE_STATE_CLOSED;
469 RTCritSectLeave(&pPipe->hCritSect);
470 rc = VINF_EOF;
471 break;
472 }
473 }
474 else
475 {
476 AssertBreakpoint();
477 rc = VERR_INVALID_STATE;
478 RTCritSectLeave(&pPipe->hCritSect);
479 break;
480 }
481
482 RTCritSectLeave(&pPipe->hCritSect);
483
484 rc = RTSemEventWait(pPipe->hEvent, RT_INDEFINITE_WAIT);
485 AssertRC(rc);
486 if (!RT_SUCCESS(rc))
487 break;
488
489 rc = RTCritSectEnter(&pPipe->hCritSect);
490 AssertRC(rc);
491 if (!RT_SUCCESS(rc))
492 break;
493 } while (1);
494 }
495
496 return rc;
497}
498
499int vboxVDMAPipeModifyClient(PVBOXVDMAPIPE pPipe, PFNHVBOXVDMARWCB pfnCallback, void * pvCallback)
500{
501 int rc = RTCritSectEnter(&pPipe->hCritSect);
502 AssertRC(rc);
503 if (RT_SUCCESS(rc))
504 {
505 bool bNeedNotify = false;
506 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED);
507 if (pPipe->enmState == VBOXVDMAPIPE_STATE_OPENNED)
508 {
509 bool bModified = pfnCallback(pPipe, pvCallback);
510 if (bModified)
511 {
512 bNeedNotify = pPipe->bNeedNotify;
513 pPipe->bNeedNotify = false;
514 }
515 }
516 else
517 rc = VERR_INVALID_STATE;
518
519 RTCritSectLeave(&pPipe->hCritSect);
520
521 if (bNeedNotify)
522 {
523 rc = RTSemEventSignal(pPipe->hEvent);
524 AssertRC(rc);
525 }
526 }
527 return rc;
528}
529
530int vboxVDMAPipeDestruct(PVBOXVDMAPIPE pPipe)
531{
532 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED
533 || pPipe->enmState == VBOXVDMAPIPE_STATE_CREATED);
534 /* ensure the pipe is closed */
535 vboxVDMAPipeCloseClient(pPipe);
536
537 Assert(pPipe->enmState == VBOXVDMAPIPE_STATE_CLOSED);
538
539 if (pPipe->enmState != VBOXVDMAPIPE_STATE_CLOSED)
540 return VERR_INVALID_STATE;
541
542 int rc = RTCritSectDelete(&pPipe->hCritSect);
543 AssertRC(rc);
544
545 rc = RTSemEventDestroy(pPipe->hEvent);
546 AssertRC(rc);
547
548 return VINF_SUCCESS;
549}
550
551static void vboxVDMACommandProcess(PVBOXVDMAHOST pVdma, PVBOXVDMACBUF_DR pCmd)
552{
553 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
554 const uint8_t * pvBuf;
555 PGMPAGEMAPLOCK Lock;
556 int rc;
557 bool bReleaseLocked = false;
558
559 do
560 {
561 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
562
563 if (pCmd->fFlags & VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR)
564 pvBuf = VBOXVDMACBUF_DR_TAIL(pCmd, const uint8_t);
565 else if (pCmd->fFlags & VBOXVDMACBUF_FLAG_BUF_VRAM_OFFSET)
566 {
567 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
568 pvBuf = pvRam + pCmd->Location.offVramBuf;
569 }
570 else
571 {
572 RTGCPHYS phPage = pCmd->Location.phBuf & ~0xfffULL;
573 uint32_t offset = pCmd->Location.phBuf & 0xfff;
574 Assert(offset + pCmd->cbBuf <= 0x1000);
575 if (offset + pCmd->cbBuf > 0x1000)
576 {
577 /* @todo: more advanced mechanism of command buffer proc is actually needed */
578 rc = VERR_INVALID_PARAMETER;
579 break;
580 }
581
582 const void * pvPageBuf;
583 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvPageBuf, &Lock);
584 AssertRC(rc);
585 if (!RT_SUCCESS(rc))
586 {
587 /* @todo: if (rc == VERR_PGM_PHYS_PAGE_RESERVED) -> fall back on using PGMPhysRead ?? */
588 break;
589 }
590
591 pvBuf = (const uint8_t *)pvPageBuf;
592 pvBuf += offset;
593
594 bReleaseLocked = true;
595 }
596
597 rc = vboxVDMACmdExec(pVdma, pvBuf, pCmd->cbBuf);
598 AssertRC(rc);
599
600 if (bReleaseLocked)
601 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
602 } while (0);
603
604 pCmd->rc = rc;
605
606 rc = VBoxSHGSMICommandComplete (pHgsmi, pCmd);
607 AssertRC(rc);
608}
609
610static void vboxVDMAControlProcess(PVBOXVDMAHOST pVdma, PVBOXVDMA_CTL pCmd)
611{
612 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
613 pCmd->i32Result = VINF_SUCCESS;
614 int rc = VBoxSHGSMICommandComplete (pHgsmi, pCmd);
615 AssertRC(rc);
616}
617
618typedef struct
619{
620 struct VBOXVDMAHOST *pVdma;
621 VBOXVDMAPIPE_CMD_BODY Cmd;
622 bool bHasCmd;
623} VBOXVDMACMD_PROCESS_CONTEXT, *PVBOXVDMACMD_PROCESS_CONTEXT;
624
625static DECLCALLBACK(bool) vboxVDMACommandProcessCb(PVBOXVDMAPIPE pPipe, void *pvCallback)
626{
627 PVBOXVDMACMD_PROCESS_CONTEXT pContext = (PVBOXVDMACMD_PROCESS_CONTEXT)pvCallback;
628 struct VBOXVDMAHOST *pVdma = pContext->pVdma;
629 HGSMILISTENTRY *pEntry = hgsmiListRemoveHead(&pVdma->PendingList);
630 if (pEntry)
631 {
632 PVBOXVDMAPIPE_CMD pPipeCmd = VBOXVDMAPIPE_CMD_FROM_ENTRY(pEntry);
633 Assert(pPipeCmd);
634 pContext->Cmd = pPipeCmd->Cmd;
635 hgsmiListPrepend(&pVdma->CmdPool.List, pEntry);
636 pContext->bHasCmd = true;
637 return true;
638 }
639
640 pContext->bHasCmd = false;
641 return false;
642}
643
644static DECLCALLBACK(int) vboxVDMAWorkerThread(RTTHREAD ThreadSelf, void *pvUser)
645{
646 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvUser;
647 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
648 VBOXVDMACMD_PROCESS_CONTEXT Context;
649 Context.pVdma = pVdma;
650
651 int rc = vboxVDMAPipeOpenServer(&pVdma->Pipe);
652 AssertRC(rc);
653 if (RT_SUCCESS(rc))
654 {
655 do
656 {
657 rc = vboxVDMAPipeModifyServer(&pVdma->Pipe, vboxVDMACommandProcessCb, &Context);
658 AssertRC(rc);
659 if (RT_SUCCESS(rc))
660 {
661 switch (Context.Cmd.enmType)
662 {
663 case VBOXVDMAPIPE_CMD_TYPE_DMACMD:
664 {
665 PVBOXVDMACBUF_DR pDr = Context.Cmd.u.pDr;
666 vboxVDMACommandProcess(pVdma, pDr);
667 break;
668 }
669 case VBOXVDMAPIPE_CMD_TYPE_DMACTL:
670 {
671 PVBOXVDMA_CTL pCtl = Context.Cmd.u.pCtl;
672 vboxVDMAControlProcess(pVdma, pCtl);
673 break;
674 }
675 default:
676 AssertBreakpoint();
677 break;
678 }
679
680 if (rc == VINF_EOF)
681 {
682 rc = VINF_SUCCESS;
683 break;
684 }
685 }
686 else
687 break;
688 } while (1);
689 }
690
691 /* always try to close the pipe to make sure the client side is notified */
692 int tmpRc = vboxVDMAPipeCloseServer(&pVdma->Pipe);
693 AssertRC(tmpRc);
694 return rc;
695}
696
697int vboxVDMAConstruct(PVGASTATE pVGAState, struct VBOXVDMAHOST **ppVdma, uint32_t cPipeElements)
698{
699 int rc;
700 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)RTMemAllocZ (RT_OFFSETOF(VBOXVDMAHOST, CmdPool.aCmds[cPipeElements]));
701 Assert(pVdma);
702 if (pVdma)
703 {
704 hgsmiListInit(&pVdma->PendingList);
705 pVdma->pHgsmi = pVGAState->pHGSMI;
706 pVdma->pVGAState = pVGAState;
707 rc = vboxVDMAPipeConstruct(&pVdma->Pipe);
708 AssertRC(rc);
709 if (RT_SUCCESS(rc))
710 {
711 rc = RTThreadCreate(&pVdma->hWorkerThread, vboxVDMAWorkerThread, pVdma, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "VDMA");
712 AssertRC(rc);
713 if (RT_SUCCESS(rc))
714 {
715 hgsmiListInit(&pVdma->CmdPool.List);
716 pVdma->CmdPool.cCmds = cPipeElements;
717 for (uint32_t i = 0; i < cPipeElements; ++i)
718 {
719 hgsmiListAppend(&pVdma->CmdPool.List, &pVdma->CmdPool.aCmds[i].Entry);
720 }
721 *ppVdma = pVdma;
722 return VINF_SUCCESS;
723 }
724
725 int tmpRc = vboxVDMAPipeDestruct(&pVdma->Pipe);
726 AssertRC(tmpRc);
727 }
728
729 RTMemFree(pVdma);
730 }
731 else
732 rc = VERR_OUT_OF_RESOURCES;
733
734 return rc;
735}
736
737int vboxVDMADestruct(struct VBOXVDMAHOST **pVdma)
738{
739 AssertBreakpoint();
740 return VINF_SUCCESS;
741}
742
743typedef struct
744{
745 struct VBOXVDMAHOST *pVdma;
746 VBOXVDMAPIPE_CMD_BODY Cmd;
747 bool bQueued;
748} VBOXVDMACMD_SUBMIT_CONTEXT, *PVBOXVDMACMD_SUBMIT_CONTEXT;
749
750DECLCALLBACK(bool) vboxVDMACommandSubmitCb(PVBOXVDMAPIPE pPipe, void *pvCallback)
751{
752 PVBOXVDMACMD_SUBMIT_CONTEXT pContext = (PVBOXVDMACMD_SUBMIT_CONTEXT)pvCallback;
753 struct VBOXVDMAHOST *pVdma = pContext->pVdma;
754 HGSMILISTENTRY *pEntry = hgsmiListRemoveHead(&pVdma->CmdPool.List);
755 Assert(pEntry);
756 if (pEntry)
757 {
758 PVBOXVDMAPIPE_CMD pPipeCmd = VBOXVDMAPIPE_CMD_FROM_ENTRY(pEntry);
759 pPipeCmd->Cmd = pContext->Cmd;
760 VBoxSHGSMICommandMarkAsynchCompletion(pContext->Cmd.u.pvCmd);
761 pContext->bQueued = true;
762 hgsmiListAppend(&pVdma->PendingList, pEntry);
763 return true;
764 }
765
766 /* @todo: should we try to flush some commands here? */
767 pContext->bQueued = false;
768 return false;
769}
770
771void vboxVDMAControl(struct VBOXVDMAHOST *pVdma, PVBOXVDMA_CTL pCmd)
772{
773#if 1
774 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
775
776 switch (pCmd->enmCtl)
777 {
778 case VBOXVDMA_CTL_TYPE_ENABLE:
779 pVdma->bEnabled = true;
780 pCmd->i32Result = VINF_SUCCESS;
781 break;
782 case VBOXVDMA_CTL_TYPE_DISABLE:
783 pVdma->bEnabled = false;
784 pCmd->i32Result = VINF_SUCCESS;
785 break;
786 case VBOXVDMA_CTL_TYPE_FLUSH:
787 pCmd->i32Result = VINF_SUCCESS;
788 break;
789 default:
790 AssertBreakpoint();
791 pCmd->i32Result = VERR_NOT_SUPPORTED;
792 }
793
794 int rc = VBoxSHGSMICommandComplete (pIns, pCmd);
795 AssertRC(rc);
796#else
797 /* test asinch completion */
798 VBOXVDMACMD_SUBMIT_CONTEXT Context;
799 Context.pVdma = pVdma;
800 Context.Cmd.enmType = VBOXVDMAPIPE_CMD_TYPE_DMACTL;
801 Context.Cmd.u.pCtl = pCmd;
802
803 int rc = vboxVDMAPipeModifyClient(&pVdma->Pipe, vboxVDMACommandSubmitCb, &Context);
804 AssertRC(rc);
805 if (RT_SUCCESS(rc))
806 {
807 Assert(Context.bQueued);
808 if (Context.bQueued)
809 {
810 /* success */
811 return;
812 }
813 rc = VERR_OUT_OF_RESOURCES;
814 }
815
816 /* failure */
817 Assert(RT_FAILURE(rc));
818 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
819 pCmd->i32Result = rc;
820 int tmpRc = VBoxSHGSMICommandComplete (pIns, pCmd);
821 AssertRC(tmpRc);
822
823#endif
824}
825
826void vboxVDMACommand(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmd)
827{
828 VBOXVDMACMD_SUBMIT_CONTEXT Context;
829 Context.pVdma = pVdma;
830 Context.Cmd.enmType = VBOXVDMAPIPE_CMD_TYPE_DMACMD;
831 Context.Cmd.u.pDr = pCmd;
832
833 int rc = vboxVDMAPipeModifyClient(&pVdma->Pipe, vboxVDMACommandSubmitCb, &Context);
834 AssertRC(rc);
835 if (RT_SUCCESS(rc))
836 {
837 Assert(Context.bQueued);
838 if (Context.bQueued)
839 {
840 /* success */
841 return;
842 }
843 rc = VERR_OUT_OF_RESOURCES;
844 }
845
846 /* failure */
847 Assert(RT_FAILURE(rc));
848 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
849 pCmd->rc = rc;
850 int tmpRc = VBoxSHGSMICommandComplete (pIns, pCmd);
851 AssertRC(tmpRc);
852}
853
854bool vboxVDMAIsEnabled(PVBOXVDMAHOST pVdma)
855{
856 return pVdma->bEnabled;
857}
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