VirtualBox

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

最後變更 在這個檔案從50928是 50928,由 vboxsync 提交於 11 年 前

wddm/DevVga/crOpenGL: new command submission working for 3D

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 90.5 KB
 
1/** @file
2 * Video DMA (VDMA) support.
3 */
4
5/*
6 * Copyright (C) 2006-2012 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.alldomusa.eu.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/vmm/pdmdev.h>
18#include <VBox/VBoxVideo.h>
19#include <iprt/semaphore.h>
20#include <iprt/thread.h>
21#include <iprt/mem.h>
22#include <iprt/asm.h>
23#include <iprt/list.h>
24#include <iprt/param.h>
25
26#include "DevVGA.h"
27#include "HGSMI/SHGSMIHost.h"
28
29#include <VBox/VBoxVideo3D.h>
30#include <VBox/VBoxVideoHost3D.h>
31
32#ifdef DEBUG_misha
33# define VBOXVDBG_MEMCACHE_DISABLE
34#endif
35
36#ifndef VBOXVDBG_MEMCACHE_DISABLE
37# include <iprt/memcache.h>
38#endif
39
40#ifdef DEBUG_misha
41#define WARN_BP() do { AssertFailed(); } while (0)
42#else
43#define WARN_BP() do { } while (0)
44#endif
45#define WARN(_msg) do { \
46 LogRel(_msg); \
47 WARN_BP(); \
48 } while (0)
49
50#define VBOXVDMATHREAD_STATE_TERMINATED 0
51#define VBOXVDMATHREAD_STATE_CREATED 1
52#define VBOXVDMATHREAD_STATE_TERMINATING 2
53
54typedef struct VBOXVDMATHREAD
55{
56 RTTHREAD hWorkerThread;
57 RTSEMEVENT hEvent;
58 RTSEMEVENT hClientEvent;
59 volatile uint32_t u32State;
60} VBOXVDMATHREAD, *PVBOXVDMATHREAD;
61
62
63/* state transformations:
64 *
65 * submitter | processor
66 *
67 * LISTENING ---> PROCESSING
68 *
69 * */
70#define VBVAEXHOSTCONTEXT_STATE_LISTENING 0
71#define VBVAEXHOSTCONTEXT_STATE_PROCESSING 1
72
73#define VBVAEXHOSTCONTEXT_ESTATE_DISABLED -1
74#define VBVAEXHOSTCONTEXT_ESTATE_PAUSED 0
75#define VBVAEXHOSTCONTEXT_ESTATE_ENABLED 1
76
77typedef struct VBVAEXHOSTCONTEXT
78{
79 VBVABUFFER *pVBVA;
80 volatile int32_t i32State;
81 volatile int32_t i32EnableState;
82 volatile uint32_t u32cCtls;
83 /* critical section for accessing ctl lists */
84 RTCRITSECT CltCritSect;
85 RTLISTANCHOR GuestCtlList;
86 RTLISTANCHOR HostCtlList;
87#ifndef VBOXVDBG_MEMCACHE_DISABLE
88 RTMEMCACHE CtlCache;
89#endif
90} VBVAEXHOSTCONTEXT;
91
92typedef enum
93{
94 VBVAEXHOSTCTL_TYPE_UNDEFINED = 0,
95 VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE,
96 VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME,
97 VBVAEXHOSTCTL_TYPE_HH_ENABLE,
98 VBVAEXHOSTCTL_TYPE_HH_TERM,
99 VBVAEXHOSTCTL_TYPE_HH_ON_HGCM_TERM,
100 VBVAEXHOSTCTL_TYPE_HH_RESET,
101 VBVAEXHOSTCTL_TYPE_HH_SAVESTATE,
102 VBVAEXHOSTCTL_TYPE_HH_LOADSTATE,
103 VBVAEXHOSTCTL_TYPE_HH_BE_OPAQUE,
104 VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE,
105 VBVAEXHOSTCTL_TYPE_GH_ENABLE_DISABLE
106} VBVAEXHOSTCTL_TYPE;
107
108struct VBVAEXHOSTCTL;
109
110typedef DECLCALLBACKPTR(void, PFNVBVAEXHOSTCTL_COMPLETE)(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvComplete);
111
112typedef struct VBVAEXHOSTCTL
113{
114 RTLISTNODE Node;
115 VBVAEXHOSTCTL_TYPE enmType;
116 union
117 {
118 struct
119 {
120 uint8_t * pu8Cmd;
121 uint32_t cbCmd;
122 } cmd;
123
124 struct
125 {
126 PSSMHANDLE pSSM;
127 uint32_t u32Version;
128 } state;
129 } u;
130 PFNVBVAEXHOSTCTL_COMPLETE pfnComplete;
131 void *pvComplete;
132} VBVAEXHOSTCTL;
133
134/* VBoxVBVAExHP**, i.e. processor functions, can NOT be called concurrently with each other,
135 * but can be called with other VBoxVBVAExS** (submitter) functions except Init/Start/Term aparently.
136 * Can only be called be the processor, i.e. the entity that acquired the processor state by direct or indirect call to the VBoxVBVAExHSCheckCommands
137 * see mor edetailed comments in headers for function definitions */
138typedef enum
139{
140 VBVAEXHOST_DATA_TYPE_NO_DATA = 0,
141 VBVAEXHOST_DATA_TYPE_CMD,
142 VBVAEXHOST_DATA_TYPE_HOSTCTL,
143 VBVAEXHOST_DATA_TYPE_GUESTCTL
144} VBVAEXHOST_DATA_TYPE;
145static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd);
146
147static void VBoxVBVAExHPDataCompleteCmd(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint32_t cbCmd);
148static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc);
149
150/* VBoxVBVAExHP**, i.e. processor functions, can NOT be called concurrently with each other,
151 * can be called concurrently with istelf as well as with other VBoxVBVAEx** functions except Init/Start/Term aparently */
152static int VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva);
153
154static int VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva);
155static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA);
156static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva);
157static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva);
158static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM);
159static int VBoxVBVAExHSLoadState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version);
160
161static VBVAEXHOSTCTL* VBoxVBVAExHCtlAlloc(VBVAEXHOSTCONTEXT *pCmdVbva)
162{
163#ifndef VBOXVDBG_MEMCACHE_DISABLE
164 return (VBVAEXHOSTCTL*)RTMemCacheAlloc(pCmdVbva->CtlCache);
165#else
166 return (VBVAEXHOSTCTL*)RTMemAlloc(sizeof (VBVAEXHOSTCTL));
167#endif
168}
169
170static void VBoxVBVAExHCtlFree(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl)
171{
172#ifndef VBOXVDBG_MEMCACHE_DISABLE
173 RTMemCacheFree(pCmdVbva->CtlCache, pCtl);
174#else
175 RTMemFree(pCtl);
176#endif
177}
178
179static VBVAEXHOSTCTL* VBoxVBVAExHCtlCreate(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL_TYPE enmType)
180{
181 VBVAEXHOSTCTL* pCtl = VBoxVBVAExHCtlAlloc(pCmdVbva);
182 if (!pCtl)
183 {
184 WARN(("VBoxVBVAExHCtlAlloc failed\n"));
185 return NULL;
186 }
187
188 pCtl->enmType = enmType;
189 return pCtl;
190}
191
192static int vboxVBVAExHSProcessorAcquire(struct VBVAEXHOSTCONTEXT *pCmdVbva)
193{
194 Assert(pCmdVbva->i32State >= VBVAEXHOSTCONTEXT_STATE_LISTENING);
195
196 if (ASMAtomicCmpXchgS32(&pCmdVbva->i32State, VBVAEXHOSTCONTEXT_STATE_PROCESSING, VBVAEXHOSTCONTEXT_STATE_LISTENING))
197 return VINF_SUCCESS;
198 return VERR_SEM_BUSY;
199}
200
201static VBVAEXHOSTCTL* vboxVBVAExHPCheckCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, bool *pfHostCtl, bool fHostOnlyMode)
202{
203 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
204
205 if(!fHostOnlyMode && !ASMAtomicUoReadU32(&pCmdVbva->u32cCtls))
206 return NULL;
207
208 int rc = RTCritSectEnter(&pCmdVbva->CltCritSect);
209 if (RT_SUCCESS(rc))
210 {
211 VBVAEXHOSTCTL* pCtl = RTListGetFirst(&pCmdVbva->HostCtlList, VBVAEXHOSTCTL, Node);
212 if (pCtl)
213 *pfHostCtl = true;
214 else if (!fHostOnlyMode)
215 {
216 if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) != VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
217 {
218 pCtl = RTListGetFirst(&pCmdVbva->GuestCtlList, VBVAEXHOSTCTL, Node);
219 /* pCtl can not be null here since pCmdVbva->u32cCtls is not null,
220 * and there are no HostCtl commands*/
221 Assert(pCtl);
222 *pfHostCtl = false;
223 }
224 }
225
226 if (pCtl)
227 {
228 RTListNodeRemove(&pCtl->Node);
229 ASMAtomicDecU32(&pCmdVbva->u32cCtls);
230 }
231
232 RTCritSectLeave(&pCmdVbva->CltCritSect);
233
234 return pCtl;
235 }
236 else
237 WARN(("RTCritSectEnter failed %d\n", rc));
238
239 return NULL;
240}
241
242static VBVAEXHOSTCTL* VBoxVBVAExHPCheckHostCtlOnDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva)
243{
244 bool fHostCtl;
245 return vboxVBVAExHPCheckCtl(pCmdVbva, &fHostCtl, true);
246}
247
248
249static bool vboxVBVAExHPCheckProcessCtlInternal(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl)
250{
251 switch (pCtl->enmType)
252 {
253 case VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE:
254 if (pCmdVbva->i32EnableState > VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
255 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
256 return true;
257 case VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME:
258 if (pCmdVbva->i32EnableState == VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
259 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_ENABLED);
260 return true;
261 default:
262 return false;
263 }
264}
265
266static void vboxVBVAExHPProcessorRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva)
267{
268 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
269
270 ASMAtomicWriteS32(&pCmdVbva->i32State, VBVAEXHOSTCONTEXT_STATE_LISTENING);
271}
272
273static void vboxVBVAExHPHgEventSet(struct VBVAEXHOSTCONTEXT *pCmdVbva)
274{
275 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
276 if (pCmdVbva->pVBVA)
277 ASMAtomicOrU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, VBVA_F_STATE_PROCESSING);
278}
279
280static void vboxVBVAExHPHgEventClear(struct VBVAEXHOSTCONTEXT *pCmdVbva)
281{
282 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
283 if (pCmdVbva->pVBVA)
284 ASMAtomicAndU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, ~VBVA_F_STATE_PROCESSING);
285}
286
287static int vboxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
288{
289 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
290 Assert(pCmdVbva->i32EnableState > VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
291
292 VBVABUFFER *pVBVA = pCmdVbva->pVBVA;
293
294 uint32_t indexRecordFirst = pVBVA->indexRecordFirst;
295 uint32_t indexRecordFree = pVBVA->indexRecordFree;
296
297 Log(("first = %d, free = %d\n",
298 indexRecordFirst, indexRecordFree));
299
300 if (indexRecordFirst == indexRecordFree)
301 {
302 /* No records to process. Return without assigning output variables. */
303 return VINF_EOF;
304 }
305
306 uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVBVA->aRecords[indexRecordFirst].cbRecord);
307
308 /* A new record need to be processed. */
309 if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
310 {
311 /* the record is being recorded, try again */
312 return VINF_TRY_AGAIN;
313 }
314
315 uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;
316
317 if (!cbRecord)
318 {
319 /* the record is being recorded, try again */
320 return VINF_TRY_AGAIN;
321 }
322
323 /* we should not get partial commands here actually */
324 Assert(cbRecord);
325
326 /* The size of largest contiguous chunk in the ring biffer. */
327 uint32_t u32BytesTillBoundary = pVBVA->cbData - pVBVA->off32Data;
328
329 /* The pointer to data in the ring buffer. */
330 uint8_t *pSrc = &pVBVA->au8Data[pVBVA->off32Data];
331
332 /* Fetch or point the data. */
333 if (u32BytesTillBoundary >= cbRecord)
334 {
335 /* The command does not cross buffer boundary. Return address in the buffer. */
336 *ppCmd = pSrc;
337 *pcbCmd = cbRecord;
338 return VINF_SUCCESS;
339 }
340
341 LogRel(("CmdVbva: cross-bound writes unsupported\n"));
342 return VERR_INVALID_STATE;
343}
344
345static void VBoxVBVAExHPDataCompleteCmd(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint32_t cbCmd)
346{
347 VBVABUFFER *pVBVA = pCmdVbva->pVBVA;
348 pVBVA->off32Data = (pVBVA->off32Data + cbCmd) % pVBVA->cbData;
349
350 pVBVA->indexRecordFirst = (pVBVA->indexRecordFirst + 1) % RT_ELEMENTS(pVBVA->aRecords);
351}
352
353static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc)
354{
355 if (pCtl->pfnComplete)
356 pCtl->pfnComplete(pCmdVbva, pCtl, rc, pCtl->pvComplete);
357 else
358 VBoxVBVAExHCtlFree(pCmdVbva, pCtl);
359}
360
361static VBVAEXHOST_DATA_TYPE vboxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
362{
363 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
364 VBVAEXHOSTCTL*pCtl;
365 bool fHostClt;
366
367 for(;;)
368 {
369 pCtl = vboxVBVAExHPCheckCtl(pCmdVbva, &fHostClt, false);
370 if (pCtl)
371 {
372 if (fHostClt)
373 {
374 if (!vboxVBVAExHPCheckProcessCtlInternal(pCmdVbva, pCtl))
375 {
376 *ppCmd = (uint8_t*)pCtl;
377 *pcbCmd = sizeof (*pCtl);
378 return VBVAEXHOST_DATA_TYPE_HOSTCTL;
379 }
380 }
381 else
382 {
383 *ppCmd = (uint8_t*)pCtl;
384 *pcbCmd = sizeof (*pCtl);
385 return VBVAEXHOST_DATA_TYPE_GUESTCTL;
386 }
387 }
388
389 if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) <= VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
390 return VBVAEXHOST_DATA_TYPE_NO_DATA;
391
392 int rc = vboxVBVAExHPCmdGet(pCmdVbva, ppCmd, pcbCmd);
393 switch (rc)
394 {
395 case VINF_SUCCESS:
396 return VBVAEXHOST_DATA_TYPE_CMD;
397 case VINF_EOF:
398 return VBVAEXHOST_DATA_TYPE_NO_DATA;
399 case VINF_TRY_AGAIN:
400 RTThreadSleep(1);
401 continue;
402 default:
403 /* this is something really unexpected, i.e. most likely guest has written something incorrect to the VBVA buffer */
404 WARN(("Warning: vboxVBVAExHCmdGet returned unexpected status %d\n", rc));
405 return VBVAEXHOST_DATA_TYPE_NO_DATA;
406 }
407 }
408
409 WARN(("Warning: VBoxVBVAExHCmdGet unexpected state\n"));
410 return VBVAEXHOST_DATA_TYPE_NO_DATA;
411}
412
413static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
414{
415 VBVAEXHOST_DATA_TYPE enmType = vboxVBVAExHPDataGet(pCmdVbva, ppCmd, pcbCmd);
416 if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA)
417 {
418 vboxVBVAExHPHgEventClear(pCmdVbva);
419 vboxVBVAExHPProcessorRelease(pCmdVbva);
420 /* we need to prevent racing between us clearing the flag and command check/submission thread, i.e.
421 * 1. we check the queue -> and it is empty
422 * 2. submitter adds command to the queue
423 * 3. submitter checks the "processing" -> and it is true , thus it does not submit a notification
424 * 4. we clear the "processing" state
425 * 5. ->here we need to re-check the queue state to ensure we do not leak the notification of the above command
426 * 6. if the queue appears to be not-empty set the "processing" state back to "true"
427 **/
428 int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva);
429 if (RT_SUCCESS(rc))
430 {
431 /* we are the processor now */
432 enmType = vboxVBVAExHPDataGet(pCmdVbva, ppCmd, pcbCmd);
433 if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA)
434 {
435 vboxVBVAExHPProcessorRelease(pCmdVbva);
436 return VBVAEXHOST_DATA_TYPE_NO_DATA;
437 }
438
439 vboxVBVAExHPHgEventSet(pCmdVbva);
440 }
441 }
442
443 return enmType;
444}
445
446DECLINLINE(bool) vboxVBVAExHSHasCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva)
447{
448 VBVABUFFER *pVBVA = pCmdVbva->pVBVA;
449
450 if (pVBVA)
451 {
452 uint32_t indexRecordFirst = pVBVA->indexRecordFirst;
453 uint32_t indexRecordFree = pVBVA->indexRecordFree;
454
455 if (indexRecordFirst != indexRecordFree)
456 return true;
457 }
458
459 return !!ASMAtomicReadU32(&pCmdVbva->u32cCtls);
460}
461
462/* Checks whether the new commands are ready for processing
463 * @returns
464 * VINF_SUCCESS - there are commands are in a queue, and the given thread is now the processor (i.e. typically it would delegate processing to a worker thread)
465 * VINF_EOF - no commands in a queue
466 * VINF_ALREADY_INITIALIZED - another thread already processing the commands
467 * VERR_INVALID_STATE - the VBVA is paused or pausing */
468static int VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva)
469{
470 int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva);
471 if (RT_SUCCESS(rc))
472 {
473 /* we are the processor now */
474 if (vboxVBVAExHSHasCommands(pCmdVbva))
475 {
476 vboxVBVAExHPHgEventSet(pCmdVbva);
477 return VINF_SUCCESS;
478 }
479
480 vboxVBVAExHPProcessorRelease(pCmdVbva);
481 return VINF_EOF;
482 }
483 if (rc == VERR_SEM_BUSY)
484 return VINF_ALREADY_INITIALIZED;
485 return VERR_INVALID_STATE;
486}
487
488static int VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva)
489{
490 memset(pCmdVbva, 0, sizeof (*pCmdVbva));
491 int rc = RTCritSectInit(&pCmdVbva->CltCritSect);
492 if (RT_SUCCESS(rc))
493 {
494#ifndef VBOXVDBG_MEMCACHE_DISABLE
495 rc = RTMemCacheCreate(&pCmdVbva->CtlCache, sizeof (VBVAEXHOSTCTL),
496 0, /* size_t cbAlignment */
497 UINT32_MAX, /* uint32_t cMaxObjects */
498 NULL, /* PFNMEMCACHECTOR pfnCtor*/
499 NULL, /* PFNMEMCACHEDTOR pfnDtor*/
500 NULL, /* void *pvUser*/
501 0 /* uint32_t fFlags*/
502 );
503 if (RT_SUCCESS(rc))
504#endif
505 {
506 RTListInit(&pCmdVbva->GuestCtlList);
507 RTListInit(&pCmdVbva->HostCtlList);
508 pCmdVbva->i32State = VBVAEXHOSTCONTEXT_STATE_PROCESSING;
509 pCmdVbva->i32EnableState = VBVAEXHOSTCONTEXT_ESTATE_DISABLED;
510 return VINF_SUCCESS;
511 }
512#ifndef VBOXVDBG_MEMCACHE_DISABLE
513 else
514 WARN(("RTMemCacheCreate failed %d\n", rc));
515#endif
516 }
517 else
518 WARN(("RTCritSectInit failed %d\n", rc));
519
520 return rc;
521}
522
523DECLINLINE(bool) VBoxVBVAExHSIsEnabled(struct VBVAEXHOSTCONTEXT *pCmdVbva)
524{
525 return (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) >= VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
526}
527
528static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA)
529{
530 if (VBoxVBVAExHSIsEnabled(pCmdVbva))
531 return VINF_ALREADY_INITIALIZED;
532
533 pCmdVbva->pVBVA = pVBVA;
534 pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0;
535 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_ENABLED);
536 return VINF_SUCCESS;
537}
538
539static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva)
540{
541 if (!VBoxVBVAExHSIsEnabled(pCmdVbva))
542 return VINF_SUCCESS;
543
544 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_DISABLED);
545 return VINF_SUCCESS;
546}
547
548static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva)
549{
550 /* ensure the processor is stopped */
551 Assert(pCmdVbva->i32State >= VBVAEXHOSTCONTEXT_STATE_LISTENING);
552
553 /* ensure no one tries to submit the command */
554 if (pCmdVbva->pVBVA)
555 pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0;
556
557 Assert(RTListIsEmpty(&pCmdVbva->GuestCtlList));
558 Assert(RTListIsEmpty(&pCmdVbva->HostCtlList));
559
560 RTCritSectDelete(&pCmdVbva->CltCritSect);
561
562#ifndef VBOXVDBG_MEMCACHE_DISABLE
563 RTMemCacheDestroy(pCmdVbva->CtlCache);
564#endif
565
566 memset(pCmdVbva, 0, sizeof (*pCmdVbva));
567}
568
569/* Saves state
570 * @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail
571 */
572static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM)
573{
574 int rc;
575
576 int32_t i32EnableState = ASMAtomicUoReadS32(&pCmdVbva->i32EnableState);
577 if (i32EnableState >= VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
578 {
579 if (i32EnableState != VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
580 {
581 WARN(("vbva not paused\n"));
582 return VERR_INVALID_STATE;
583 }
584
585 rc = SSMR3PutU32(pSSM, (uint32_t)(((uint8_t*)pCmdVbva->pVBVA) - pu8VramBase));
586 AssertRCReturn(rc, rc);
587 return VINF_SUCCESS;
588 }
589
590 rc = SSMR3PutU32(pSSM, 0xffffffff);
591 AssertRCReturn(rc, rc);
592
593 return VINF_SUCCESS;
594}
595
596typedef enum
597{
598 VBVAEXHOSTCTL_SOURCE_GUEST = 0,
599 VBVAEXHOSTCTL_SOURCE_HOST_ANY,
600 VBVAEXHOSTCTL_SOURCE_HOST_ENABLED
601} VBVAEXHOSTCTL_SOURCE;
602
603
604static int VBoxVBVAExHCtlSubmit(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
605{
606 if ((enmSource == VBVAEXHOSTCTL_SOURCE_HOST_ENABLED) && !VBoxVBVAExHSIsEnabled(pCmdVbva))
607 {
608 Log(("cmd vbva not enabled\n"));
609 return VERR_INVALID_STATE;
610 }
611
612 pCtl->pfnComplete = pfnComplete;
613 pCtl->pvComplete = pvComplete;
614
615 int rc = RTCritSectEnter(&pCmdVbva->CltCritSect);
616 if (RT_SUCCESS(rc))
617 {
618 if (enmSource > VBVAEXHOSTCTL_SOURCE_GUEST)
619 {
620 if ((enmSource == VBVAEXHOSTCTL_SOURCE_HOST_ENABLED) && !VBoxVBVAExHSIsEnabled(pCmdVbva))
621 {
622 Log(("cmd vbva not enabled\n"));
623 RTCritSectLeave(&pCmdVbva->CltCritSect);
624 return VERR_INVALID_STATE;
625 }
626 RTListAppend(&pCmdVbva->HostCtlList, &pCtl->Node);
627 }
628 else
629 RTListAppend(&pCmdVbva->GuestCtlList, &pCtl->Node);
630
631 ASMAtomicIncU32(&pCmdVbva->u32cCtls);
632
633 RTCritSectLeave(&pCmdVbva->CltCritSect);
634
635 rc = VBoxVBVAExHSCheckCommands(pCmdVbva);
636 }
637 else
638 WARN(("RTCritSectEnter failed %d\n", rc));
639
640 return rc;
641}
642
643
644/* Loads state
645 * @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail
646 */
647static int VBoxVBVAExHSLoadState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version)
648{
649 AssertMsgFailed(("implement!\n"));
650 uint32_t u32;
651 int rc = SSMR3GetU32(pSSM, &u32);
652 AssertRCReturn(rc, rc);
653 if (u32 != 0xffffffff)
654 {
655 VBVABUFFER *pVBVA = (VBVABUFFER*)pu8VramBase + u32;
656 rc = VBoxVBVAExHSEnable(pCmdVbva, pVBVA);
657 AssertRCReturn(rc, rc);
658 return VBoxVBVAExHSCheckCommands(pCmdVbva);
659 }
660
661 return VINF_SUCCESS;
662}
663
664typedef struct VBOXVDMAHOST
665{
666 PHGSMIINSTANCE pHgsmi;
667 PVGASTATE pVGAState;
668 VBVAEXHOSTCONTEXT CmdVbva;
669 VBOXVDMATHREAD Thread;
670 VBOXCRCMD_SVRINFO CrSrvInfo;
671 VBVAEXHOSTCTL* pCurRemainingHostCtl;
672 RTSEMEVENTMULTI HostCrCtlCompleteEvent;
673 int32_t volatile i32cHostCrCtlCompleted;
674#ifdef VBOX_VDMA_WITH_WATCHDOG
675 PTMTIMERR3 WatchDogTimer;
676#endif
677} VBOXVDMAHOST, *PVBOXVDMAHOST;
678
679int VBoxVDMAThreadNotifyConstructSucceeded(PVBOXVDMATHREAD pThread)
680{
681 Assert(pThread->u32State == VBOXVDMATHREAD_STATE_TERMINATED);
682 pThread->u32State = VBOXVDMATHREAD_STATE_CREATED;
683 int rc = RTSemEventSignal(pThread->hClientEvent);
684 AssertRC(rc);
685 return VINF_SUCCESS;
686}
687
688int VBoxVDMAThreadNotifyConstructFailed(PVBOXVDMATHREAD pThread)
689{
690 Assert(pThread->u32State == VBOXVDMATHREAD_STATE_TERMINATED);
691 int rc = RTSemEventSignal(pThread->hClientEvent);
692 AssertRC(rc);
693 if (RT_SUCCESS(rc))
694 return VINF_SUCCESS;
695 return rc;
696}
697
698DECLINLINE(bool) VBoxVDMAThreadIsTerminating(PVBOXVDMATHREAD pThread)
699{
700 return ASMAtomicUoReadU32(&pThread->u32State) == VBOXVDMATHREAD_STATE_TERMINATING;
701}
702
703int VBoxVDMAThreadCreate(PVBOXVDMATHREAD pThread, PFNRTTHREAD pfnThread, void *pvThread)
704{
705 int rc = RTSemEventCreate(&pThread->hEvent);
706 if (RT_SUCCESS(rc))
707 {
708 rc = RTSemEventCreate(&pThread->hClientEvent);
709 if (RT_SUCCESS(rc))
710 {
711 pThread->u32State = VBOXVDMATHREAD_STATE_TERMINATED;
712 rc = RTThreadCreate(&pThread->hWorkerThread, pfnThread, pvThread, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "VDMA");
713 if (RT_SUCCESS(rc))
714 {
715 rc = RTSemEventWait(pThread->hClientEvent, RT_INDEFINITE_WAIT);
716 if (RT_SUCCESS(rc))
717 {
718 if (pThread->u32State == VBOXVDMATHREAD_STATE_CREATED)
719 return VINF_SUCCESS;
720 WARN(("thread routine failed the initialization\n"));
721 rc = VERR_INVALID_STATE;
722 }
723 else
724 WARN(("RTSemEventWait failed %d\n", rc));
725
726 RTThreadWait(pThread->hWorkerThread, RT_INDEFINITE_WAIT, NULL);
727 }
728 else
729 WARN(("RTThreadCreate failed %d\n", rc));
730
731 RTSemEventDestroy(pThread->hClientEvent);
732 }
733 else
734 WARN(("RTSemEventCreate failed %d\n", rc));
735
736 RTSemEventDestroy(pThread->hEvent);
737 }
738 else
739 WARN(("RTSemEventCreate failed %d\n", rc));
740
741 return rc;
742}
743
744DECLINLINE(int) VBoxVDMAThreadEventNotify(PVBOXVDMATHREAD pThread)
745{
746 int rc = RTSemEventSignal(pThread->hEvent);
747 AssertRC(rc);
748 return rc;
749}
750
751DECLINLINE(int) VBoxVDMAThreadEventWait(PVBOXVDMATHREAD pThread, RTMSINTERVAL cMillies)
752{
753 int rc = RTSemEventWait(pThread->hEvent, cMillies);
754 AssertRC(rc);
755 return rc;
756}
757
758void VBoxVDMAThreadMarkTerminating(PVBOXVDMATHREAD pThread)
759{
760 Assert(pThread->u32State == VBOXVDMATHREAD_STATE_CREATED);
761 ASMAtomicWriteU32(&pThread->u32State, VBOXVDMATHREAD_STATE_TERMINATING);
762}
763
764void VBoxVDMAThreadTerm(PVBOXVDMATHREAD pThread)
765{
766 int rc;
767 if (ASMAtomicReadU32(&pThread->u32State) != VBOXVDMATHREAD_STATE_TERMINATING)
768 {
769 VBoxVDMAThreadMarkTerminating(pThread);
770 rc = VBoxVDMAThreadEventNotify(pThread);
771 AssertRC(rc);
772 }
773 rc = RTThreadWait(pThread->hWorkerThread, RT_INDEFINITE_WAIT, NULL);
774 AssertRC(rc);
775 RTSemEventDestroy(pThread->hClientEvent);
776 RTSemEventDestroy(pThread->hEvent);
777}
778
779static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource);
780
781#ifdef VBOX_WITH_CRHGSMI
782
783typedef DECLCALLBACK(void) FNVBOXVDMACRCTL_CALLBACK(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext);
784typedef FNVBOXVDMACRCTL_CALLBACK *PFNVBOXVDMACRCTL_CALLBACK;
785
786typedef struct VBOXVDMACMD_CHROMIUM_CTL_PRIVATE
787{
788 uint32_t cRefs;
789 int32_t rc;
790 PFNVBOXVDMACRCTL_CALLBACK pfnCompletion;
791 void *pvCompletion;
792 VBOXVDMACMD_CHROMIUM_CTL Cmd;
793} VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, *PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE;
794
795#define VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(_p) ((PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE)(((uint8_t*)(_p)) - RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, Cmd)))
796
797static PVBOXVDMACMD_CHROMIUM_CTL vboxVDMACrCtlCreate(VBOXVDMACMD_CHROMIUM_CTL_TYPE enmCmd, uint32_t cbCmd)
798{
799 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = (PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE)RTMemAllocZ(cbCmd + RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, Cmd));
800 Assert(pHdr);
801 if (pHdr)
802 {
803 pHdr->cRefs = 1;
804 pHdr->rc = VERR_NOT_IMPLEMENTED;
805 pHdr->Cmd.enmType = enmCmd;
806 pHdr->Cmd.cbCmd = cbCmd;
807 return &pHdr->Cmd;
808 }
809
810 return NULL;
811}
812
813DECLINLINE(void) vboxVDMACrCtlRelease (PVBOXVDMACMD_CHROMIUM_CTL pCmd)
814{
815 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
816 uint32_t cRefs = ASMAtomicDecU32(&pHdr->cRefs);
817 if(!cRefs)
818 {
819 RTMemFree(pHdr);
820 }
821}
822
823DECLINLINE(void) vboxVDMACrCtlRetain (PVBOXVDMACMD_CHROMIUM_CTL pCmd)
824{
825 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
826 ASMAtomicIncU32(&pHdr->cRefs);
827}
828
829DECLINLINE(int) vboxVDMACrCtlGetRc (PVBOXVDMACMD_CHROMIUM_CTL pCmd)
830{
831 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
832 return pHdr->rc;
833}
834
835static DECLCALLBACK(void) vboxVDMACrCtlCbSetEvent(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext)
836{
837 RTSemEventSignal((RTSEMEVENT)pvContext);
838}
839
840static DECLCALLBACK(void) vboxVDMACrCtlCbReleaseCmd(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext)
841{
842 vboxVDMACrCtlRelease(pCmd);
843}
844
845
846static int vboxVDMACrCtlPostAsync (PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd, PFNVBOXVDMACRCTL_CALLBACK pfnCompletion, void *pvCompletion)
847{
848 if ( pVGAState->pDrv
849 && pVGAState->pDrv->pfnCrHgsmiControlProcess)
850 {
851 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
852 pHdr->pfnCompletion = pfnCompletion;
853 pHdr->pvCompletion = pvCompletion;
854 pVGAState->pDrv->pfnCrHgsmiControlProcess(pVGAState->pDrv, pCmd, cbCmd);
855 return VINF_SUCCESS;
856 }
857#ifdef DEBUG_misha
858 Assert(0);
859#endif
860 return VERR_NOT_SUPPORTED;
861}
862
863static int vboxVDMACrCtlPost(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd)
864{
865 RTSEMEVENT hComplEvent;
866 int rc = RTSemEventCreate(&hComplEvent);
867 AssertRC(rc);
868 if(RT_SUCCESS(rc))
869 {
870 rc = vboxVDMACrCtlPostAsync(pVGAState, pCmd, cbCmd, vboxVDMACrCtlCbSetEvent, (void*)hComplEvent);
871#ifdef DEBUG_misha
872 AssertRC(rc);
873#endif
874 if (RT_SUCCESS(rc))
875 {
876 rc = RTSemEventWaitNoResume(hComplEvent, RT_INDEFINITE_WAIT);
877 AssertRC(rc);
878 if(RT_SUCCESS(rc))
879 {
880 RTSemEventDestroy(hComplEvent);
881 }
882 }
883 else
884 {
885 /* the command is completed */
886 RTSemEventDestroy(hComplEvent);
887 }
888 }
889 return rc;
890}
891
892typedef struct VDMA_VBVA_CTL_CYNC_COMPLETION
893{
894 int rc;
895 RTSEMEVENT hEvent;
896} VDMA_VBVA_CTL_CYNC_COMPLETION;
897
898static DECLCALLBACK(void) vboxVDMACrHgcmSubmitSyncCompletion(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
899{
900 VDMA_VBVA_CTL_CYNC_COMPLETION *pData = (VDMA_VBVA_CTL_CYNC_COMPLETION*)pvCompletion;
901 pData->rc = rc;
902 rc = RTSemEventSignal(pData->hEvent);
903 if (!RT_SUCCESS(rc))
904 WARN(("RTSemEventSignal failed %d\n", rc));
905}
906
907static int vboxVDMACrHgcmSubmitSync(struct VBOXVDMAHOST *pVdma, VBOXCRCMDCTL* pCtl, uint32_t cbCtl)
908{
909 VDMA_VBVA_CTL_CYNC_COMPLETION Data;
910 Data.rc = VERR_NOT_IMPLEMENTED;
911 int rc = RTSemEventCreate(&Data.hEvent);
912 if (!RT_SUCCESS(rc))
913 {
914 WARN(("RTSemEventCreate failed %d\n", rc));
915 return rc;
916 }
917
918 PVGASTATE pVGAState = pVdma->pVGAState;
919 rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCtl, cbCtl, vboxVDMACrHgcmSubmitSyncCompletion, &Data);
920 if (RT_SUCCESS(rc))
921 {
922 rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT);
923 if (RT_SUCCESS(rc))
924 {
925 rc = Data.rc;
926 if (!RT_SUCCESS(rc))
927 {
928 WARN(("pfnCrHgcmCtlSubmit command failed %d\n", rc));
929 }
930
931 }
932 else
933 WARN(("RTSemEventWait failed %d\n", rc));
934 }
935 else
936 WARN(("pfnCrHgcmCtlSubmit failed %d\n", rc));
937
938
939 RTSemEventDestroy(Data.hEvent);
940
941 return rc;
942}
943
944static DECLCALLBACK(uint8_t*) vboxVDMACrHgcmHandleEnableRemainingHostCommand(HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hClient, uint32_t *pcbCtl, int prevCmdRc)
945{
946 struct VBOXVDMAHOST *pVdma = hClient;
947 if (!pVdma->pCurRemainingHostCtl)
948 {
949 /* disable VBVA, all subsequent host commands will go HGCM way */
950 VBoxVBVAExHSDisable(&pVdma->CmdVbva);
951 }
952 else
953 {
954 VBoxVBVAExHPDataCompleteCtl(&pVdma->CmdVbva, pVdma->pCurRemainingHostCtl, prevCmdRc);
955 }
956
957 pVdma->pCurRemainingHostCtl = VBoxVBVAExHPCheckHostCtlOnDisable(&pVdma->CmdVbva);
958 if (pVdma->pCurRemainingHostCtl)
959 {
960 *pcbCtl = pVdma->pCurRemainingHostCtl->u.cmd.cbCmd;
961 return pVdma->pCurRemainingHostCtl->u.cmd.pu8Cmd;
962 }
963
964 *pcbCtl = 0;
965 return NULL;
966}
967
968static DECLCALLBACK(void) vboxVDMACrHgcmNotifyTerminatingDoneCb(HVBOXCRCMDCTL_NOTIFY_TERMINATING hClient)
969{
970 struct VBOXVDMAHOST *pVdma = hClient;
971 Assert(pVdma->CmdVbva.i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
972 int rc = VBoxVDMAThreadEventNotify(&pVdma->Thread);
973 AssertRC(rc);
974}
975
976static DECLCALLBACK(int) vboxVDMACrHgcmNotifyTerminatingCb(HVBOXCRCMDCTL_NOTIFY_TERMINATING hClient, VBOXCRCMDCTL_HGCMENABLE_DATA *pHgcmEnableData)
977{
978 struct VBOXVDMAHOST *pVdma = hClient;
979 VBVAEXHOSTCTL Ctl;
980 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_ON_HGCM_TERM;
981 int rc = vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY);
982 if (!RT_SUCCESS(rc))
983 {
984 WARN(("vdmaVBVACtlSubmitSync failed %d\n", rc));
985 return rc;
986 }
987
988 Assert(pVdma->CmdVbva.i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
989
990 pHgcmEnableData->hRHCmd = pVdma;
991 pHgcmEnableData->pfnRHCmd = vboxVDMACrHgcmHandleEnableRemainingHostCommand;
992
993 return VINF_SUCCESS;
994}
995
996static int vboxVDMACrHgcmHandleEnable(struct VBOXVDMAHOST *pVdma)
997{
998 VBOXCRCMDCTL_ENABLE Enable;
999 Enable.Hdr.enmType = VBOXCRCMDCTL_TYPE_ENABLE;
1000 Enable.Data.hRHCmd = pVdma;
1001 Enable.Data.pfnRHCmd = vboxVDMACrHgcmHandleEnableRemainingHostCommand;
1002
1003 int rc = vboxVDMACrHgcmSubmitSync(pVdma, &Enable.Hdr, sizeof (Enable));
1004 Assert(!pVdma->pCurRemainingHostCtl);
1005 if (RT_SUCCESS(rc))
1006 {
1007 Assert(!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva));
1008 return VINF_SUCCESS;
1009 }
1010
1011 Assert(VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva));
1012 WARN(("vboxVDMACrHgcmSubmitSync failed %d\n", rc));
1013
1014 return rc;
1015}
1016
1017static int vdmaVBVAEnableProcess(struct VBOXVDMAHOST *pVdma, uint32_t u32Offset)
1018{
1019 if (VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1020 {
1021 WARN(("vdma VBVA is already enabled\n"));
1022 return VERR_INVALID_STATE;
1023 }
1024
1025 VBVABUFFER *pVBVA = (VBVABUFFER *)HGSMIOffsetToPointerHost(pVdma->pHgsmi, u32Offset);
1026 if (!pVBVA)
1027 {
1028 WARN(("invalid offset %d\n", u32Offset));
1029 return VERR_INVALID_PARAMETER;
1030 }
1031
1032 if (!pVdma->CrSrvInfo.pfnEnable)
1033 {
1034#ifdef DEBUG_misha
1035 WARN(("pfnEnable is NULL\n"));
1036 return VERR_NOT_SUPPORTED;
1037#endif
1038 }
1039
1040 int rc = VBoxVBVAExHSEnable(&pVdma->CmdVbva, pVBVA);
1041 if (RT_SUCCESS(rc))
1042 {
1043 VBOXCRCMDCTL_DISABLE Disable;
1044 Disable.Hdr.enmType = VBOXCRCMDCTL_TYPE_DISABLE;
1045 Disable.Data.hNotifyTerm = pVdma;
1046 Disable.Data.pfnNotifyTerm = vboxVDMACrHgcmNotifyTerminatingCb;
1047 Disable.Data.pfnNotifyTermDone = vboxVDMACrHgcmNotifyTerminatingDoneCb;
1048 rc = vboxVDMACrHgcmSubmitSync(pVdma, &Disable.Hdr, sizeof (Disable));
1049 if (RT_SUCCESS(rc))
1050 {
1051 PVGASTATE pVGAState = pVdma->pVGAState;
1052 VBOXCRCMD_SVRENABLE_INFO Info;
1053 Info.hCltScr = pVGAState->pDrv;
1054 Info.pfnCltScrUpdateBegin = pVGAState->pDrv->pfnVBVAUpdateBegin;
1055 Info.pfnCltScrUpdateProcess = pVGAState->pDrv->pfnVBVAUpdateProcess;
1056 Info.pfnCltScrUpdateEnd = pVGAState->pDrv->pfnVBVAUpdateEnd;
1057 rc = pVdma->CrSrvInfo.pfnEnable(pVdma->CrSrvInfo.hSvr, &Info);
1058 if (RT_SUCCESS(rc))
1059 return VINF_SUCCESS;
1060 else
1061 WARN(("pfnEnable failed %d\n", rc));
1062
1063 vboxVDMACrHgcmHandleEnable(pVdma);
1064 }
1065 else
1066 WARN(("vboxVDMACrHgcmSubmitSync failed %d\n", rc));
1067
1068 VBoxVBVAExHSDisable(&pVdma->CmdVbva);
1069 }
1070 else
1071 WARN(("VBoxVBVAExHSEnable failed %d\n", rc));
1072
1073 return rc;
1074}
1075
1076static int vdmaVBVADisableProcess(struct VBOXVDMAHOST *pVdma, bool fDoHgcmEnable)
1077{
1078 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1079 {
1080 Log(("vdma VBVA is already disabled\n"));
1081 return VINF_SUCCESS;
1082 }
1083
1084 int rc = pVdma->CrSrvInfo.pfnDisable(pVdma->CrSrvInfo.hSvr);
1085 if (RT_SUCCESS(rc))
1086 {
1087 if (fDoHgcmEnable)
1088 {
1089 /* disable is a bit tricky
1090 * we need to ensure the host ctl commands do not come out of order
1091 * and do not come over HGCM channel until after it is enabled */
1092 rc = vboxVDMACrHgcmHandleEnable(pVdma);
1093 if (RT_SUCCESS(rc))
1094 return rc;
1095
1096 PVGASTATE pVGAState = pVdma->pVGAState;
1097 VBOXCRCMD_SVRENABLE_INFO Info;
1098 Info.hCltScr = pVGAState->pDrv;
1099 Info.pfnCltScrUpdateBegin = pVGAState->pDrv->pfnVBVAUpdateBegin;
1100 Info.pfnCltScrUpdateProcess = pVGAState->pDrv->pfnVBVAUpdateProcess;
1101 Info.pfnCltScrUpdateEnd = pVGAState->pDrv->pfnVBVAUpdateEnd;
1102 pVdma->CrSrvInfo.pfnEnable(pVdma->CrSrvInfo.hSvr, &Info);
1103 }
1104 }
1105 else
1106 WARN(("pfnDisable failed %d\n", rc));
1107
1108 return rc;
1109}
1110
1111static int vboxVDMACrHostCtlProcess(struct VBOXVDMAHOST *pVdma, VBVAEXHOSTCTL *pCmd, bool *pfContinue)
1112{
1113 *pfContinue = true;
1114
1115 switch (pCmd->enmType)
1116 {
1117 case VBVAEXHOSTCTL_TYPE_HH_SAVESTATE:
1118 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1119 {
1120 WARN(("VBVAEXHOSTCTL_TYPE_HH_SAVESTATE for disabled vdma VBVA\n"));
1121 return VERR_INVALID_STATE;
1122 }
1123 return pVdma->CrSrvInfo.pfnSaveState(pVdma->CrSrvInfo.hSvr, pCmd->u.state.pSSM);
1124 case VBVAEXHOSTCTL_TYPE_HH_LOADSTATE:
1125 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1126 {
1127 WARN(("VBVAEXHOSTCTL_TYPE_HH_LOADSTATE for disabled vdma VBVA\n"));
1128 return VERR_INVALID_STATE;
1129 }
1130 return pVdma->CrSrvInfo.pfnLoadState(pVdma->CrSrvInfo.hSvr, pCmd->u.state.pSSM, pCmd->u.state.u32Version);
1131 case VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE:
1132 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1133 {
1134 WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
1135 return VERR_INVALID_STATE;
1136 }
1137 return pVdma->CrSrvInfo.pfnHostCtl(pVdma->CrSrvInfo.hSvr, pCmd->u.cmd.pu8Cmd, pCmd->u.cmd.cbCmd);
1138 case VBVAEXHOSTCTL_TYPE_HH_TERM:
1139 {
1140 int rc = vdmaVBVADisableProcess(pVdma, true);
1141 if (!RT_SUCCESS(rc))
1142 {
1143 WARN(("vdmaVBVADisableProcess failed %d\n", rc));
1144 return rc;
1145 }
1146
1147 VBoxVDMAThreadMarkTerminating(&pVdma->Thread);
1148 return VINF_SUCCESS;
1149 }
1150 case VBVAEXHOSTCTL_TYPE_HH_ON_HGCM_TERM:
1151 {
1152 int rc = vdmaVBVADisableProcess(pVdma, false);
1153 if (!RT_SUCCESS(rc))
1154 {
1155 WARN(("vdmaVBVADisableProcess failed %d\n", rc));
1156 return rc;
1157 }
1158
1159 *pfContinue = false;
1160
1161 return VINF_SUCCESS;
1162 }
1163 case VBVAEXHOSTCTL_TYPE_HH_RESET:
1164 {
1165 int rc = vdmaVBVADisableProcess(pVdma, true);
1166 if (!RT_SUCCESS(rc))
1167 {
1168 WARN(("vdmaVBVADisableProcess failed %d\n", rc));
1169 return rc;
1170 }
1171 return VINF_SUCCESS;
1172 }
1173 default:
1174 WARN(("unexpected host ctl type %d\n", pCmd->enmType));
1175 return VERR_INVALID_PARAMETER;
1176 }
1177}
1178
1179static int vboxVDMACrGuestCtlProcess(struct VBOXVDMAHOST *pVdma, VBVAEXHOSTCTL *pCmd)
1180{
1181 switch (pCmd->enmType)
1182 {
1183 case VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE:
1184 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1185 {
1186 WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
1187 return VERR_INVALID_STATE;
1188 }
1189 return pVdma->CrSrvInfo.pfnGuestCtl(pVdma->CrSrvInfo.hSvr, pCmd->u.cmd.pu8Cmd, pCmd->u.cmd.cbCmd);
1190 case VBVAEXHOSTCTL_TYPE_GH_ENABLE_DISABLE:
1191 {
1192 VBVAENABLE *pEnable = (VBVAENABLE *)pCmd->u.cmd.pu8Cmd;
1193 Assert(pCmd->u.cmd.cbCmd == sizeof (VBVAENABLE));
1194 if ((pEnable->u32Flags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_ENABLE)
1195 {
1196 uint32_t u32Offset = pEnable->u32Offset;
1197 return vdmaVBVAEnableProcess(pVdma, u32Offset);
1198 }
1199
1200 return vdmaVBVADisableProcess(pVdma, true);
1201 }
1202 default:
1203 WARN(("unexpected ctl type %d\n", pCmd->enmType));
1204 return VERR_INVALID_PARAMETER;
1205 }
1206}
1207
1208/**
1209 * @param fIn - whether this is a page in or out op.
1210 * the direction is VRA#M - related, so fIn == true - transfer to VRAM); false - transfer from VRAM
1211 */
1212static int vboxVDMACrCmdVbvaProcessPagingEl(PPDMDEVINS pDevIns, VBOXCMDVBVAPAGEIDX iPage, uint8_t *pu8Vram, bool fIn)
1213{
1214 RTGCPHYS phPage = (RTGCPHYS)(iPage << PAGE_SHIFT);
1215 PGMPAGEMAPLOCK Lock;
1216 int rc;
1217
1218 if (fIn)
1219 {
1220 const void * pvPage;
1221 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvPage, &Lock);
1222 if (!RT_SUCCESS(rc))
1223 {
1224 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d", rc));
1225 return rc;
1226 }
1227
1228 memcpy(pu8Vram, pvPage, PAGE_SIZE);
1229
1230 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1231 }
1232 else
1233 {
1234 void * pvPage;
1235 rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, phPage, 0, &pvPage, &Lock);
1236 if (!RT_SUCCESS(rc))
1237 {
1238 WARN(("PDMDevHlpPhysGCPhys2CCPtr failed %d", rc));
1239 return rc;
1240 }
1241
1242 memcpy(pvPage, pu8Vram, PAGE_SIZE);
1243
1244 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1245 }
1246
1247 return VINF_SUCCESS;
1248}
1249
1250static int vboxVDMACrCmdVbvaProcessPagingEls(PPDMDEVINS pDevIns, const VBOXCMDVBVAPAGEIDX *piPages, uint32_t cPages, uint8_t *pu8Vram, bool fIn)
1251{
1252 for (uint32_t i = 0; i < cPages; ++i, pu8Vram += PAGE_SIZE)
1253 {
1254 int rc = vboxVDMACrCmdVbvaProcessPagingEl(pDevIns, piPages[i], pu8Vram, fIn);
1255 if (!RT_SUCCESS(rc))
1256 {
1257 WARN(("vboxVDMACrCmdVbvaProcessPagingEl failed %d", rc));
1258 return rc;
1259 }
1260 }
1261
1262 return VINF_SUCCESS;
1263}
1264
1265static int8_t vboxVDMACrCmdVbvaPagingDataInit(PVGASTATE pVGAState, const VBOXCMDVBVA_HDR *pHdr, const VBOXCMDVBVA_PAGING_TRANSFER_DATA *pData, uint32_t cbCmd,
1266 const VBOXCMDVBVAPAGEIDX **ppPages, VBOXCMDVBVAPAGEIDX *pcPages,
1267 uint8_t **ppu8Vram, bool *pfIn)
1268{
1269 if (cbCmd < sizeof (VBOXCMDVBVA_PAGING_TRANSFER))
1270 {
1271 WARN(("cmd too small"));
1272 return -1;
1273 }
1274
1275 VBOXCMDVBVAPAGEIDX cPages = cbCmd - RT_OFFSETOF(VBOXCMDVBVA_PAGING_TRANSFER, Data.aPageNumbers);
1276 if (cPages % sizeof (VBOXCMDVBVAPAGEIDX))
1277 {
1278 WARN(("invalid cmd size"));
1279 return -1;
1280 }
1281 cPages /= sizeof (VBOXCMDVBVAPAGEIDX);
1282
1283 VBOXCMDVBVAOFFSET offVRAM = pData->Alloc.u.offVRAM;
1284 if (offVRAM & PAGE_OFFSET_MASK)
1285 {
1286 WARN(("offVRAM address is not on page boundary\n"));
1287 return -1;
1288 }
1289 const VBOXCMDVBVAPAGEIDX *pPages = pData->aPageNumbers;
1290
1291 uint8_t * pu8VramBase = pVGAState->vram_ptrR3;
1292 uint8_t *pu8VramMax = pu8VramBase + pVGAState->vram_size;
1293 if (offVRAM >= pVGAState->vram_size)
1294 {
1295 WARN(("invalid vram offset"));
1296 return -1;
1297 }
1298
1299 if (offVRAM + (cPages << PAGE_SHIFT) >= pVGAState->vram_size)
1300 {
1301 WARN(("invalid cPages"));
1302 return -1;
1303 }
1304
1305 uint8_t *pu8Vram = pu8VramBase + offVRAM;
1306 bool fIn = !!(pHdr->u8Flags & VBOXCMDVBVA_OPF_PAGING_TRANSFER_IN);
1307
1308 *ppPages = pPages;
1309 *pcPages = cPages;
1310 *ppu8Vram = pu8Vram;
1311 *pfIn = fIn;
1312 return 0;
1313}
1314
1315static int8_t vboxVDMACrCmdVbvaProcessCmdData(struct VBOXVDMAHOST *pVdma, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd)
1316{
1317 switch (pCmd->u8OpCode)
1318 {
1319 case VBOXCMDVBVA_OPTYPE_NOPCMD:
1320 return 0;
1321 case VBOXCMDVBVA_OPTYPE_PAGING_TRANSFER:
1322 {
1323 PVGASTATE pVGAState = pVdma->pVGAState;
1324 const VBOXCMDVBVAPAGEIDX *pPages;
1325 uint32_t cPages;
1326 uint8_t *pu8Vram;
1327 bool fIn;
1328 int8_t i8Result = vboxVDMACrCmdVbvaPagingDataInit(pVGAState, pCmd, &((VBOXCMDVBVA_PAGING_TRANSFER*)pCmd)->Data, cbCmd,
1329 &pPages, &cPages,
1330 &pu8Vram, &fIn);
1331 if (i8Result < 0)
1332 {
1333 WARN(("vboxVDMACrCmdVbvaPagingDataInit failed %d", i8Result));
1334 return i8Result;
1335 }
1336
1337 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
1338 int rc = vboxVDMACrCmdVbvaProcessPagingEls(pDevIns, pPages, cPages, pu8Vram, fIn);
1339 if (!RT_SUCCESS(rc))
1340 {
1341 WARN(("vboxVDMACrCmdVbvaProcessPagingEls failed %d", rc));
1342 return -1;
1343 }
1344
1345 return 0;
1346 }
1347 case VBOXCMDVBVA_OPTYPE_PAGING_FILL:
1348 WARN(("VBOXCMDVBVA_OPTYPE_PAGING_FILL not implemented"));
1349 return -1;
1350 default:
1351 return pVdma->CrSrvInfo.pfnCmd(pVdma->CrSrvInfo.hSvr, pCmd, cbCmd);
1352 }
1353}
1354
1355#if 0
1356typedef struct VBOXCMDVBVA_PAGING_TRANSFER
1357{
1358 VBOXCMDVBVA_HDR Hdr;
1359 /* for now can only contain offVRAM.
1360 * paging transfer can NOT be initiated for allocations having host 3D object (hostID) associated */
1361 VBOXCMDVBVA_ALLOCINFO Alloc;
1362 uint32_t u32Reserved;
1363 VBOXCMDVBVA_SYSMEMEL aSysMem[1];
1364} VBOXCMDVBVA_PAGING_TRANSFER;
1365#endif
1366
1367AssertCompile(sizeof (VBOXCMDVBVA_HDR) == 8);
1368AssertCompile(sizeof (VBOXCMDVBVA_ALLOCINFO) == 4);
1369AssertCompile(sizeof (VBOXCMDVBVAPAGEIDX) == 4);
1370AssertCompile(!(PAGE_SIZE % sizeof (VBOXCMDVBVAPAGEIDX)));
1371
1372#define VBOXCMDVBVA_NUM_SYSMEMEL_PER_PAGE (PAGE_SIZE / sizeof (VBOXCMDVBVA_SYSMEMEL))
1373
1374static int8_t vboxVDMACrCmdVbvaProcess(struct VBOXVDMAHOST *pVdma, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd)
1375{
1376 switch (pCmd->u8OpCode)
1377 {
1378 case VBOXCMDVBVA_OPTYPE_SYSMEMCMD:
1379 {
1380 VBOXCMDVBVA_SYSMEMCMD *pSysmemCmd = (VBOXCMDVBVA_SYSMEMCMD*)pCmd;
1381 const VBOXCMDVBVA_HDR *pRealCmdHdr;
1382 uint32_t cbRealCmd = pCmd->u8Flags;
1383 cbRealCmd |= (pCmd->u.u8PrimaryID << 8);
1384 if (cbRealCmd < sizeof (VBOXCMDVBVA_HDR))
1385 {
1386 WARN(("invalid sysmem cmd size"));
1387 return -1;
1388 }
1389
1390 RTGCPHYS phCmd = (RTGCPHYS)pSysmemCmd->phCmd;
1391
1392 PGMPAGEMAPLOCK Lock;
1393 PVGASTATE pVGAState = pVdma->pVGAState;
1394 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
1395 const void * pvCmd;
1396 int rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phCmd, 0, &pvCmd, &Lock);
1397 if (!RT_SUCCESS(rc))
1398 {
1399 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d\n", rc));
1400 return -1;
1401 }
1402
1403 Assert((phCmd & PAGE_OFFSET_MASK) == (((uintptr_t)pvCmd) & PAGE_OFFSET_MASK));
1404
1405 uint32_t cbCmdPart = PAGE_SIZE - (((uintptr_t)pvCmd) & PAGE_OFFSET_MASK);
1406
1407 if (cbRealCmd <= cbCmdPart)
1408 {
1409 pRealCmdHdr = (const VBOXCMDVBVA_HDR *)pvCmd;
1410 uint8_t i8Result = vboxVDMACrCmdVbvaProcessCmdData(pVdma, pRealCmdHdr, cbRealCmd);
1411 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1412 return i8Result;
1413 }
1414
1415 VBOXCMDVBVA_HDR Hdr;
1416 const void *pvCurCmdTail;
1417 uint32_t cbCurCmdTail;
1418 if (cbCmdPart >= sizeof (*pRealCmdHdr))
1419 {
1420 pRealCmdHdr = (const VBOXCMDVBVA_HDR *)pvCmd;
1421 pvCurCmdTail = (const void*)(pRealCmdHdr + 1);
1422 cbCurCmdTail = cbCmdPart - sizeof (*pRealCmdHdr);
1423 }
1424 else
1425 {
1426 memcpy(&Hdr, pvCmd, cbCmdPart);
1427 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1428 phCmd += cbCmdPart;
1429 Assert(!(phCmd & PAGE_OFFSET_MASK));
1430 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phCmd, 0, &pvCmd, &Lock);
1431 if (!RT_SUCCESS(rc))
1432 {
1433 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d\n", rc));
1434 return -1;
1435 }
1436
1437 cbCmdPart = sizeof (*pRealCmdHdr) - cbCmdPart;
1438 memcpy(((uint8_t*)(&Hdr)) + cbCmdPart, pvCmd, cbCmdPart);
1439 pvCurCmdTail = (const void*)(((uint8_t*)pvCmd) + cbCmdPart);
1440 cbCurCmdTail = PAGE_SIZE - cbCmdPart;
1441 }
1442
1443 if (cbCurCmdTail > cbRealCmd - sizeof (*pRealCmdHdr))
1444 cbCurCmdTail = cbRealCmd - sizeof (*pRealCmdHdr);
1445
1446 int8_t i8Result = 0;
1447
1448 switch (pRealCmdHdr->u8OpCode)
1449 {
1450 case VBOXCMDVBVA_OPTYPE_PAGING_TRANSFER:
1451 {
1452 const uint32_t *pPages;
1453 uint32_t cPages;
1454 uint8_t *pu8Vram;
1455 bool fIn;
1456 i8Result = vboxVDMACrCmdVbvaPagingDataInit(pVGAState, pRealCmdHdr, (const VBOXCMDVBVA_PAGING_TRANSFER_DATA*)pvCurCmdTail, cbRealCmd,
1457 &pPages, &cPages,
1458 &pu8Vram, &fIn);
1459 if (i8Result < 0)
1460 {
1461 WARN(("vboxVDMACrCmdVbvaPagingDataInit failed %d", i8Result));
1462 /* we need to break, not return, to ensure currently locked page is released */
1463 break;
1464 }
1465
1466 if (cbCurCmdTail & 3)
1467 {
1468 WARN(("command is not alligned properly %d", cbCurCmdTail));
1469 i8Result = -1;
1470 /* we need to break, not return, to ensure currently locked page is released */
1471 break;
1472 }
1473
1474 uint32_t cCurPages = cbCurCmdTail / sizeof (VBOXCMDVBVAPAGEIDX);
1475 Assert(cCurPages < cPages);
1476
1477 do
1478 {
1479 rc = vboxVDMACrCmdVbvaProcessPagingEls(pDevIns, pPages, cCurPages, pu8Vram, fIn);
1480 if (!RT_SUCCESS(rc))
1481 {
1482 WARN(("vboxVDMACrCmdVbvaProcessPagingEls failed %d", rc));
1483 i8Result = -1;
1484 /* we need to break, not return, to ensure currently locked page is released */
1485 break;
1486 }
1487
1488 Assert(cPages >= cCurPages);
1489 cPages -= cCurPages;
1490
1491 if (!cPages)
1492 break;
1493
1494 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1495
1496 Assert(!(phCmd & PAGE_OFFSET_MASK));
1497
1498 phCmd += PAGE_SIZE;
1499 pu8Vram += (cCurPages << PAGE_SHIFT);
1500
1501 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phCmd, 0, &pvCmd, &Lock);
1502 if (!RT_SUCCESS(rc))
1503 {
1504 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d\n", rc));
1505 /* the page is not locked, return */
1506 return -1;
1507 }
1508
1509 cCurPages = PAGE_SIZE / sizeof (VBOXCMDVBVAPAGEIDX);
1510 if (cCurPages > cPages)
1511 cCurPages = cPages;
1512 } while (1);
1513 break;
1514 }
1515 default:
1516 WARN(("command can not be splitted"));
1517 i8Result = -1;
1518 break;
1519 }
1520
1521 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1522 return i8Result;
1523 }
1524 default:
1525 return vboxVDMACrCmdVbvaProcessCmdData(pVdma, pCmd, cbCmd);
1526 }
1527}
1528
1529static void vboxVDMACrCmdProcess(struct VBOXVDMAHOST *pVdma, uint8_t* pu8Cmd, uint32_t cbCmd)
1530{
1531 if (*pu8Cmd == VBOXCMDVBVA_OPTYPE_NOP)
1532 return;
1533
1534 if (cbCmd < sizeof (VBOXCMDVBVA_HDR))
1535 {
1536 WARN(("invalid command size"));
1537 return;
1538 }
1539
1540 PVBOXCMDVBVA_HDR pCmd = (PVBOXCMDVBVA_HDR)pu8Cmd;
1541
1542 /* check if the command is cancelled */
1543 if (!ASMAtomicCmpXchgU8(&pCmd->u8State, VBOXCMDVBVA_STATE_IN_PROGRESS, VBOXCMDVBVA_STATE_SUBMITTED))
1544 {
1545 Assert(pCmd->u8State == VBOXCMDVBVA_STATE_CANCELLED);
1546 return;
1547 }
1548
1549 pCmd->u.i8Result = vboxVDMACrCmdVbvaProcess(pVdma, pCmd, cbCmd);
1550}
1551
1552static int vboxVDMACrCtlHgsmiSetup(struct VBOXVDMAHOST *pVdma)
1553{
1554 PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pCmd = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)
1555 vboxVDMACrCtlCreate (VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP, sizeof (*pCmd));
1556 int rc = VERR_NO_MEMORY;
1557 if (pCmd)
1558 {
1559 PVGASTATE pVGAState = pVdma->pVGAState;
1560 pCmd->pvVRamBase = pVGAState->vram_ptrR3;
1561 pCmd->cbVRam = pVGAState->vram_size;
1562 rc = vboxVDMACrCtlPost(pVGAState, &pCmd->Hdr, sizeof (*pCmd));
1563 if (RT_SUCCESS(rc))
1564 {
1565 rc = vboxVDMACrCtlGetRc(&pCmd->Hdr);
1566 if (RT_SUCCESS(rc))
1567 pVdma->CrSrvInfo = pCmd->CrCmdServerInfo;
1568 else if (rc != VERR_NOT_SUPPORTED)
1569 WARN(("vboxVDMACrCtlGetRc returned %d\n", rc));
1570 }
1571 else
1572 WARN(("vboxVDMACrCtlPost failed %d\n", rc));
1573
1574 vboxVDMACrCtlRelease(&pCmd->Hdr);
1575 }
1576
1577 if (!RT_SUCCESS(rc))
1578 memset(&pVdma->CrSrvInfo, 0, sizeof (pVdma->CrSrvInfo));
1579
1580 return rc;
1581}
1582
1583static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer);
1584
1585/* check if this is external cmd to be passed to chromium backend */
1586static int vboxVDMACmdCheckCrCmd(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmdDr, uint32_t cbCmdDr)
1587{
1588 PVBOXVDMACMD pDmaCmd = NULL;
1589 uint32_t cbDmaCmd = 0;
1590 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
1591 int rc = VINF_NOT_SUPPORTED;
1592
1593 cbDmaCmd = pCmdDr->cbBuf;
1594
1595 if (pCmdDr->fFlags & VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR)
1596 {
1597 if (cbCmdDr < sizeof (*pCmdDr) + VBOXVDMACMD_HEADER_SIZE())
1598 {
1599 AssertMsgFailed(("invalid buffer data!"));
1600 return VERR_INVALID_PARAMETER;
1601 }
1602
1603 if (cbDmaCmd < cbCmdDr - sizeof (*pCmdDr) - VBOXVDMACMD_HEADER_SIZE())
1604 {
1605 AssertMsgFailed(("invalid command buffer data!"));
1606 return VERR_INVALID_PARAMETER;
1607 }
1608
1609 pDmaCmd = VBOXVDMACBUF_DR_TAIL(pCmdDr, VBOXVDMACMD);
1610 }
1611 else if (pCmdDr->fFlags & VBOXVDMACBUF_FLAG_BUF_VRAM_OFFSET)
1612 {
1613 VBOXVIDEOOFFSET offBuf = pCmdDr->Location.offVramBuf;
1614 if (offBuf + cbDmaCmd > pVdma->pVGAState->vram_size)
1615 {
1616 AssertMsgFailed(("invalid command buffer data from offset!"));
1617 return VERR_INVALID_PARAMETER;
1618 }
1619 pDmaCmd = (VBOXVDMACMD*)(pvRam + offBuf);
1620 }
1621
1622 if (pDmaCmd)
1623 {
1624 Assert(cbDmaCmd >= VBOXVDMACMD_HEADER_SIZE());
1625 uint32_t cbBody = VBOXVDMACMD_BODY_SIZE(cbDmaCmd);
1626
1627 switch (pDmaCmd->enmType)
1628 {
1629 case VBOXVDMACMD_TYPE_CHROMIUM_CMD:
1630 {
1631 PVBOXVDMACMD_CHROMIUM_CMD pCrCmd = VBOXVDMACMD_BODY(pDmaCmd, VBOXVDMACMD_CHROMIUM_CMD);
1632 if (cbBody < sizeof (*pCrCmd))
1633 {
1634 AssertMsgFailed(("invalid chromium command buffer size!"));
1635 return VERR_INVALID_PARAMETER;
1636 }
1637 PVGASTATE pVGAState = pVdma->pVGAState;
1638 rc = VINF_SUCCESS;
1639 if (pVGAState->pDrv->pfnCrHgsmiCommandProcess)
1640 {
1641 VBoxSHGSMICommandMarkAsynchCompletion(pCmdDr);
1642 pVGAState->pDrv->pfnCrHgsmiCommandProcess(pVGAState->pDrv, pCrCmd, cbBody);
1643 break;
1644 }
1645 else
1646 {
1647 Assert(0);
1648 }
1649
1650 int tmpRc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmdDr);
1651 AssertRC(tmpRc);
1652 break;
1653 }
1654 case VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER:
1655 {
1656 PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer = VBOXVDMACMD_BODY(pDmaCmd, VBOXVDMACMD_DMA_BPB_TRANSFER);
1657 if (cbBody < sizeof (*pTransfer))
1658 {
1659 AssertMsgFailed(("invalid bpb transfer buffer size!"));
1660 return VERR_INVALID_PARAMETER;
1661 }
1662
1663 rc = vboxVDMACmdExecBpbTransfer(pVdma, pTransfer, sizeof (*pTransfer));
1664 AssertRC(rc);
1665 if (RT_SUCCESS(rc))
1666 {
1667 pCmdDr->rc = VINF_SUCCESS;
1668 rc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmdDr);
1669 AssertRC(rc);
1670 rc = VINF_SUCCESS;
1671 }
1672 break;
1673 }
1674 default:
1675 break;
1676 }
1677 }
1678 return rc;
1679}
1680
1681int vboxVDMACrHgsmiCommandCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, int rc)
1682{
1683 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
1684 PHGSMIINSTANCE pIns = pVGAState->pHGSMI;
1685 VBOXVDMACMD *pDmaHdr = VBOXVDMACMD_FROM_BODY(pCmd);
1686 VBOXVDMACBUF_DR *pDr = VBOXVDMACBUF_DR_FROM_TAIL(pDmaHdr);
1687 AssertRC(rc);
1688 pDr->rc = rc;
1689
1690 Assert(pVGAState->fGuestCaps & VBVACAPS_COMPLETEGCMD_BY_IOREAD);
1691 rc = VBoxSHGSMICommandComplete(pIns, pDr);
1692 AssertRC(rc);
1693 return rc;
1694}
1695
1696int vboxVDMACrHgsmiControlCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd, int rc)
1697{
1698 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
1699 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pCmdPrivate = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
1700 pCmdPrivate->rc = rc;
1701 if (pCmdPrivate->pfnCompletion)
1702 {
1703 pCmdPrivate->pfnCompletion(pVGAState, pCmd, pCmdPrivate->pvCompletion);
1704 }
1705 return VINF_SUCCESS;
1706}
1707
1708#endif
1709
1710#ifdef VBOX_VDMA_WITH_WORKERTHREAD
1711/* to simplify things and to avoid extra backend if modifications we assume the VBOXVDMA_RECTL is the same as VBVACMDHDR */
1712AssertCompile(sizeof(VBOXVDMA_RECTL) == sizeof(VBVACMDHDR));
1713AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, left) == RT_SIZEOFMEMB(VBVACMDHDR, x));
1714AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, top) == RT_SIZEOFMEMB(VBVACMDHDR, y));
1715AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, width) == RT_SIZEOFMEMB(VBVACMDHDR, w));
1716AssertCompile(RT_SIZEOFMEMB(VBOXVDMA_RECTL, height) == RT_SIZEOFMEMB(VBVACMDHDR, h));
1717AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, left) == RT_OFFSETOF(VBVACMDHDR, x));
1718AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, top) == RT_OFFSETOF(VBVACMDHDR, y));
1719AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, width) == RT_OFFSETOF(VBVACMDHDR, w));
1720AssertCompile(RT_OFFSETOF(VBOXVDMA_RECTL, height) == RT_OFFSETOF(VBVACMDHDR, h));
1721
1722static int vboxVDMANotifyPrimaryUpdate (PVGASTATE pVGAState, unsigned uScreenId, const VBOXVDMA_RECTL * pRectl)
1723{
1724 pVGAState->pDrv->pfnVBVAUpdateBegin (pVGAState->pDrv, uScreenId);
1725
1726 /* Updates the rectangle and sends the command to the VRDP server. */
1727 pVGAState->pDrv->pfnVBVAUpdateProcess (pVGAState->pDrv, uScreenId,
1728 (const PVBVACMDHDR)pRectl /* <- see above AssertCompile's and comments */,
1729 sizeof (VBOXVDMA_RECTL));
1730
1731 pVGAState->pDrv->pfnVBVAUpdateEnd (pVGAState->pDrv, uScreenId, pRectl->left, pRectl->top,
1732 pRectl->width, pRectl->height);
1733
1734 return VINF_SUCCESS;
1735}
1736#endif
1737
1738static int vboxVDMACmdExecBltPerform(PVBOXVDMAHOST pVdma,
1739 uint8_t *pvDstSurf, const uint8_t *pvSrcSurf,
1740 const PVBOXVDMA_SURF_DESC pDstDesc, const PVBOXVDMA_SURF_DESC pSrcDesc,
1741 const VBOXVDMA_RECTL * pDstRectl, const VBOXVDMA_RECTL * pSrcRectl)
1742{
1743 /* we do not support color conversion */
1744 Assert(pDstDesc->format == pSrcDesc->format);
1745 /* we do not support stretching */
1746 Assert(pDstRectl->height == pSrcRectl->height);
1747 Assert(pDstRectl->width == pSrcRectl->width);
1748 if (pDstDesc->format != pSrcDesc->format)
1749 return VERR_INVALID_FUNCTION;
1750 if (pDstDesc->width == pDstRectl->width
1751 && pSrcDesc->width == pSrcRectl->width
1752 && pSrcDesc->width == pDstDesc->width)
1753 {
1754 Assert(!pDstRectl->left);
1755 Assert(!pSrcRectl->left);
1756 uint32_t cbOff = pDstDesc->pitch * pDstRectl->top;
1757 uint32_t cbSize = pDstDesc->pitch * pDstRectl->height;
1758 memcpy(pvDstSurf + cbOff, pvSrcSurf + cbOff, cbSize);
1759 }
1760 else
1761 {
1762 uint32_t offDstLineStart = pDstRectl->left * pDstDesc->bpp >> 3;
1763 uint32_t offDstLineEnd = ((pDstRectl->left * pDstDesc->bpp + 7) >> 3) + ((pDstDesc->bpp * pDstRectl->width + 7) >> 3);
1764 uint32_t cbDstLine = offDstLineEnd - offDstLineStart;
1765 uint32_t offDstStart = pDstDesc->pitch * pDstRectl->top + offDstLineStart;
1766 Assert(cbDstLine <= pDstDesc->pitch);
1767 uint32_t cbDstSkip = pDstDesc->pitch;
1768 uint8_t * pvDstStart = pvDstSurf + offDstStart;
1769
1770 uint32_t offSrcLineStart = pSrcRectl->left * pSrcDesc->bpp >> 3;
1771 uint32_t offSrcLineEnd = ((pSrcRectl->left * pSrcDesc->bpp + 7) >> 3) + ((pSrcDesc->bpp * pSrcRectl->width + 7) >> 3);
1772 uint32_t cbSrcLine = offSrcLineEnd - offSrcLineStart;
1773 uint32_t offSrcStart = pSrcDesc->pitch * pSrcRectl->top + offSrcLineStart;
1774 Assert(cbSrcLine <= pSrcDesc->pitch);
1775 uint32_t cbSrcSkip = pSrcDesc->pitch;
1776 const uint8_t * pvSrcStart = pvSrcSurf + offSrcStart;
1777
1778 Assert(cbDstLine == cbSrcLine);
1779
1780 for (uint32_t i = 0; ; ++i)
1781 {
1782 memcpy (pvDstStart, pvSrcStart, cbDstLine);
1783 if (i == pDstRectl->height)
1784 break;
1785 pvDstStart += cbDstSkip;
1786 pvSrcStart += cbSrcSkip;
1787 }
1788 }
1789 return VINF_SUCCESS;
1790}
1791
1792static void vboxVDMARectlUnite(VBOXVDMA_RECTL * pRectl1, const VBOXVDMA_RECTL * pRectl2)
1793{
1794 if (!pRectl1->width)
1795 *pRectl1 = *pRectl2;
1796 else
1797 {
1798 int16_t x21 = pRectl1->left + pRectl1->width;
1799 int16_t x22 = pRectl2->left + pRectl2->width;
1800 if (pRectl1->left > pRectl2->left)
1801 {
1802 pRectl1->left = pRectl2->left;
1803 pRectl1->width = x21 < x22 ? x22 - pRectl1->left : x21 - pRectl1->left;
1804 }
1805 else if (x21 < x22)
1806 pRectl1->width = x22 - pRectl1->left;
1807
1808 x21 = pRectl1->top + pRectl1->height;
1809 x22 = pRectl2->top + pRectl2->height;
1810 if (pRectl1->top > pRectl2->top)
1811 {
1812 pRectl1->top = pRectl2->top;
1813 pRectl1->height = x21 < x22 ? x22 - pRectl1->top : x21 - pRectl1->top;
1814 }
1815 else if (x21 < x22)
1816 pRectl1->height = x22 - pRectl1->top;
1817 }
1818}
1819
1820/*
1821 * @return on success the number of bytes the command contained, otherwise - VERR_xxx error code
1822 */
1823static int vboxVDMACmdExecBlt(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt, uint32_t cbBuffer)
1824{
1825 const uint32_t cbBlt = VBOXVDMACMD_BODY_FIELD_OFFSET(uint32_t, VBOXVDMACMD_DMA_PRESENT_BLT, aDstSubRects[pBlt->cDstSubRects]);
1826 Assert(cbBlt <= cbBuffer);
1827 if (cbBuffer < cbBlt)
1828 return VERR_INVALID_FUNCTION;
1829
1830 /* we do not support stretching for now */
1831 Assert(pBlt->srcRectl.width == pBlt->dstRectl.width);
1832 Assert(pBlt->srcRectl.height == pBlt->dstRectl.height);
1833 if (pBlt->srcRectl.width != pBlt->dstRectl.width)
1834 return VERR_INVALID_FUNCTION;
1835 if (pBlt->srcRectl.height != pBlt->dstRectl.height)
1836 return VERR_INVALID_FUNCTION;
1837 Assert(pBlt->cDstSubRects);
1838
1839 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
1840 VBOXVDMA_RECTL updateRectl = {0, 0, 0, 0};
1841
1842 if (pBlt->cDstSubRects)
1843 {
1844 VBOXVDMA_RECTL dstRectl, srcRectl;
1845 const VBOXVDMA_RECTL *pDstRectl, *pSrcRectl;
1846 for (uint32_t i = 0; i < pBlt->cDstSubRects; ++i)
1847 {
1848 pDstRectl = &pBlt->aDstSubRects[i];
1849 if (pBlt->dstRectl.left || pBlt->dstRectl.top)
1850 {
1851 dstRectl.left = pDstRectl->left + pBlt->dstRectl.left;
1852 dstRectl.top = pDstRectl->top + pBlt->dstRectl.top;
1853 dstRectl.width = pDstRectl->width;
1854 dstRectl.height = pDstRectl->height;
1855 pDstRectl = &dstRectl;
1856 }
1857
1858 pSrcRectl = &pBlt->aDstSubRects[i];
1859 if (pBlt->srcRectl.left || pBlt->srcRectl.top)
1860 {
1861 srcRectl.left = pSrcRectl->left + pBlt->srcRectl.left;
1862 srcRectl.top = pSrcRectl->top + pBlt->srcRectl.top;
1863 srcRectl.width = pSrcRectl->width;
1864 srcRectl.height = pSrcRectl->height;
1865 pSrcRectl = &srcRectl;
1866 }
1867
1868 int rc = vboxVDMACmdExecBltPerform(pVdma, pvRam + pBlt->offDst, pvRam + pBlt->offSrc,
1869 &pBlt->dstDesc, &pBlt->srcDesc,
1870 pDstRectl,
1871 pSrcRectl);
1872 AssertRC(rc);
1873 if (!RT_SUCCESS(rc))
1874 return rc;
1875
1876 vboxVDMARectlUnite(&updateRectl, pDstRectl);
1877 }
1878 }
1879 else
1880 {
1881 int rc = vboxVDMACmdExecBltPerform(pVdma, pvRam + pBlt->offDst, pvRam + pBlt->offSrc,
1882 &pBlt->dstDesc, &pBlt->srcDesc,
1883 &pBlt->dstRectl,
1884 &pBlt->srcRectl);
1885 AssertRC(rc);
1886 if (!RT_SUCCESS(rc))
1887 return rc;
1888
1889 vboxVDMARectlUnite(&updateRectl, &pBlt->dstRectl);
1890 }
1891
1892#ifdef VBOX_VDMA_WITH_WORKERTHREAD
1893 int iView = 0;
1894 /* @todo: fixme: check if update is needed and get iView */
1895 vboxVDMANotifyPrimaryUpdate (pVdma->pVGAState, iView, &updateRectl);
1896#endif
1897
1898 return cbBlt;
1899}
1900
1901static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer)
1902{
1903 if (cbBuffer < sizeof (*pTransfer))
1904 return VERR_INVALID_PARAMETER;
1905
1906 PVGASTATE pVGAState = pVdma->pVGAState;
1907 uint8_t * pvRam = pVGAState->vram_ptrR3;
1908 PGMPAGEMAPLOCK SrcLock;
1909 PGMPAGEMAPLOCK DstLock;
1910 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
1911 const void * pvSrc;
1912 void * pvDst;
1913 int rc = VINF_SUCCESS;
1914 uint32_t cbTransfer = pTransfer->cbTransferSize;
1915 uint32_t cbTransfered = 0;
1916 bool bSrcLocked = false;
1917 bool bDstLocked = false;
1918 do
1919 {
1920 uint32_t cbSubTransfer = cbTransfer;
1921 if (pTransfer->fFlags & VBOXVDMACMD_DMA_BPB_TRANSFER_F_SRC_VRAMOFFSET)
1922 {
1923 pvSrc = pvRam + pTransfer->Src.offVramBuf + cbTransfered;
1924 }
1925 else
1926 {
1927 RTGCPHYS phPage = pTransfer->Src.phBuf;
1928 phPage += cbTransfered;
1929 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvSrc, &SrcLock);
1930 AssertRC(rc);
1931 if (RT_SUCCESS(rc))
1932 {
1933 bSrcLocked = true;
1934 cbSubTransfer = RT_MIN(cbSubTransfer, 0x1000);
1935 }
1936 else
1937 {
1938 break;
1939 }
1940 }
1941
1942 if (pTransfer->fFlags & VBOXVDMACMD_DMA_BPB_TRANSFER_F_DST_VRAMOFFSET)
1943 {
1944 pvDst = pvRam + pTransfer->Dst.offVramBuf + cbTransfered;
1945 }
1946 else
1947 {
1948 RTGCPHYS phPage = pTransfer->Dst.phBuf;
1949 phPage += cbTransfered;
1950 rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, phPage, 0, &pvDst, &DstLock);
1951 AssertRC(rc);
1952 if (RT_SUCCESS(rc))
1953 {
1954 bDstLocked = true;
1955 cbSubTransfer = RT_MIN(cbSubTransfer, 0x1000);
1956 }
1957 else
1958 {
1959 break;
1960 }
1961 }
1962
1963 if (RT_SUCCESS(rc))
1964 {
1965 memcpy(pvDst, pvSrc, cbSubTransfer);
1966 cbTransfer -= cbSubTransfer;
1967 cbTransfered += cbSubTransfer;
1968 }
1969 else
1970 {
1971 cbTransfer = 0; /* to break */
1972 }
1973
1974 if (bSrcLocked)
1975 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &SrcLock);
1976 if (bDstLocked)
1977 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &DstLock);
1978 } while (cbTransfer);
1979
1980 if (RT_SUCCESS(rc))
1981 return sizeof (*pTransfer);
1982 return rc;
1983}
1984
1985static int vboxVDMACmdExec(PVBOXVDMAHOST pVdma, const uint8_t *pvBuffer, uint32_t cbBuffer)
1986{
1987 do
1988 {
1989 Assert(pvBuffer);
1990 Assert(cbBuffer >= VBOXVDMACMD_HEADER_SIZE());
1991
1992 if (!pvBuffer)
1993 return VERR_INVALID_PARAMETER;
1994 if (cbBuffer < VBOXVDMACMD_HEADER_SIZE())
1995 return VERR_INVALID_PARAMETER;
1996
1997 PVBOXVDMACMD pCmd = (PVBOXVDMACMD)pvBuffer;
1998 uint32_t cbCmd = 0;
1999 switch (pCmd->enmType)
2000 {
2001 case VBOXVDMACMD_TYPE_CHROMIUM_CMD:
2002 {
2003#ifdef VBOXWDDM_TEST_UHGSMI
2004 static int count = 0;
2005 static uint64_t start, end;
2006 if (count==0)
2007 {
2008 start = RTTimeNanoTS();
2009 }
2010 ++count;
2011 if (count==100000)
2012 {
2013 end = RTTimeNanoTS();
2014 float ems = (end-start)/1000000.f;
2015 LogRel(("100000 calls took %i ms, %i cps\n", (int)ems, (int)(100000.f*1000.f/ems) ));
2016 }
2017#endif
2018 /* todo: post the buffer to chromium */
2019 return VINF_SUCCESS;
2020 }
2021 case VBOXVDMACMD_TYPE_DMA_PRESENT_BLT:
2022 {
2023 const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt = VBOXVDMACMD_BODY(pCmd, VBOXVDMACMD_DMA_PRESENT_BLT);
2024 int cbBlt = vboxVDMACmdExecBlt(pVdma, pBlt, cbBuffer);
2025 Assert(cbBlt >= 0);
2026 Assert((uint32_t)cbBlt <= cbBuffer);
2027 if (cbBlt >= 0)
2028 {
2029 if ((uint32_t)cbBlt == cbBuffer)
2030 return VINF_SUCCESS;
2031 else
2032 {
2033 cbBuffer -= (uint32_t)cbBlt;
2034 pvBuffer -= cbBlt;
2035 }
2036 }
2037 else
2038 return cbBlt; /* error */
2039 break;
2040 }
2041 case VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER:
2042 {
2043 const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer = VBOXVDMACMD_BODY(pCmd, VBOXVDMACMD_DMA_BPB_TRANSFER);
2044 int cbTransfer = vboxVDMACmdExecBpbTransfer(pVdma, pTransfer, cbBuffer);
2045 Assert(cbTransfer >= 0);
2046 Assert((uint32_t)cbTransfer <= cbBuffer);
2047 if (cbTransfer >= 0)
2048 {
2049 if ((uint32_t)cbTransfer == cbBuffer)
2050 return VINF_SUCCESS;
2051 else
2052 {
2053 cbBuffer -= (uint32_t)cbTransfer;
2054 pvBuffer -= cbTransfer;
2055 }
2056 }
2057 else
2058 return cbTransfer; /* error */
2059 break;
2060 }
2061 case VBOXVDMACMD_TYPE_DMA_NOP:
2062 return VINF_SUCCESS;
2063 case VBOXVDMACMD_TYPE_CHILD_STATUS_IRQ:
2064 return VINF_SUCCESS;
2065 default:
2066 AssertBreakpoint();
2067 return VERR_INVALID_FUNCTION;
2068 }
2069 } while (1);
2070
2071 /* we should not be here */
2072 AssertBreakpoint();
2073 return VERR_INVALID_STATE;
2074}
2075
2076static DECLCALLBACK(int) vboxVDMAWorkerThread(RTTHREAD ThreadSelf, void *pvUser)
2077{
2078 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvUser;
2079 PVGASTATE pVGAState = pVdma->pVGAState;
2080 VBVAEXHOSTCONTEXT *pCmdVbva = &pVdma->CmdVbva;
2081 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
2082 uint8_t *pCmd;
2083 uint32_t cbCmd;
2084
2085 int rc = VBoxVDMAThreadNotifyConstructSucceeded(&pVdma->Thread);
2086 if (!RT_SUCCESS(rc))
2087 {
2088 WARN(("VBoxVDMAThreadNotifyConstructSucceeded failed %d\n", rc));
2089 return rc;
2090 }
2091
2092 while (!VBoxVDMAThreadIsTerminating(&pVdma->Thread))
2093 {
2094 VBVAEXHOST_DATA_TYPE enmType = VBoxVBVAExHPDataGet(pCmdVbva, &pCmd, &cbCmd);
2095 switch (enmType)
2096 {
2097 case VBVAEXHOST_DATA_TYPE_CMD:
2098 vboxVDMACrCmdProcess(pVdma, pCmd, cbCmd);
2099 VBoxVBVAExHPDataCompleteCmd(pCmdVbva, cbCmd);
2100 VBVARaiseIrqNoWait(pVGAState, 0);
2101 break;
2102 case VBVAEXHOST_DATA_TYPE_GUESTCTL:
2103 rc = vboxVDMACrGuestCtlProcess(pVdma, (VBVAEXHOSTCTL*)pCmd);
2104 VBoxVBVAExHPDataCompleteCtl(pCmdVbva, (VBVAEXHOSTCTL*)pCmd, rc);
2105 break;
2106 case VBVAEXHOST_DATA_TYPE_HOSTCTL:
2107 {
2108 bool fContinue = true;
2109 rc = vboxVDMACrHostCtlProcess(pVdma, (VBVAEXHOSTCTL*)pCmd, &fContinue);
2110 VBoxVBVAExHPDataCompleteCtl(pCmdVbva, (VBVAEXHOSTCTL*)pCmd, rc);
2111 if (fContinue)
2112 break;
2113 }
2114 case VBVAEXHOST_DATA_TYPE_NO_DATA:
2115 rc = VBoxVDMAThreadEventWait(&pVdma->Thread, RT_INDEFINITE_WAIT);
2116 AssertRC(rc);
2117 break;
2118 default:
2119 WARN(("unexpected type %d\n", enmType));
2120 break;
2121 }
2122 }
2123
2124 return VINF_SUCCESS;
2125}
2126
2127static void vboxVDMACommandProcess(PVBOXVDMAHOST pVdma, PVBOXVDMACBUF_DR pCmd, uint32_t cbCmd)
2128{
2129 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
2130 const uint8_t * pvBuf;
2131 PGMPAGEMAPLOCK Lock;
2132 int rc;
2133 bool bReleaseLocked = false;
2134
2135 do
2136 {
2137 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
2138
2139 if (pCmd->fFlags & VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR)
2140 pvBuf = VBOXVDMACBUF_DR_TAIL(pCmd, const uint8_t);
2141 else if (pCmd->fFlags & VBOXVDMACBUF_FLAG_BUF_VRAM_OFFSET)
2142 {
2143 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
2144 pvBuf = pvRam + pCmd->Location.offVramBuf;
2145 }
2146 else
2147 {
2148 RTGCPHYS phPage = pCmd->Location.phBuf & ~0xfffULL;
2149 uint32_t offset = pCmd->Location.phBuf & 0xfff;
2150 Assert(offset + pCmd->cbBuf <= 0x1000);
2151 if (offset + pCmd->cbBuf > 0x1000)
2152 {
2153 /* @todo: more advanced mechanism of command buffer proc is actually needed */
2154 rc = VERR_INVALID_PARAMETER;
2155 break;
2156 }
2157
2158 const void * pvPageBuf;
2159 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvPageBuf, &Lock);
2160 AssertRC(rc);
2161 if (!RT_SUCCESS(rc))
2162 {
2163 /* @todo: if (rc == VERR_PGM_PHYS_PAGE_RESERVED) -> fall back on using PGMPhysRead ?? */
2164 break;
2165 }
2166
2167 pvBuf = (const uint8_t *)pvPageBuf;
2168 pvBuf += offset;
2169
2170 bReleaseLocked = true;
2171 }
2172
2173 rc = vboxVDMACmdExec(pVdma, pvBuf, pCmd->cbBuf);
2174 AssertRC(rc);
2175
2176 if (bReleaseLocked)
2177 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
2178 } while (0);
2179
2180 pCmd->rc = rc;
2181
2182 rc = VBoxSHGSMICommandComplete (pHgsmi, pCmd);
2183 AssertRC(rc);
2184}
2185
2186static void vboxVDMAControlProcess(PVBOXVDMAHOST pVdma, PVBOXVDMA_CTL pCmd)
2187{
2188 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
2189 pCmd->i32Result = VINF_SUCCESS;
2190 int rc = VBoxSHGSMICommandComplete (pHgsmi, pCmd);
2191 AssertRC(rc);
2192}
2193
2194#ifdef VBOX_VDMA_WITH_WATCHDOG
2195static DECLCALLBACK(void) vboxVDMAWatchDogTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
2196{
2197 VBOXVDMAHOST *pVdma = (VBOXVDMAHOST *)pvUser;
2198 PVGASTATE pVGAState = pVdma->pVGAState;
2199 VBVARaiseIrq(pVGAState, HGSMIHOSTFLAGS_WATCHDOG);
2200}
2201
2202static int vboxVDMAWatchDogCtl(struct VBOXVDMAHOST *pVdma, uint32_t cMillis)
2203{
2204 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
2205 if (cMillis)
2206 TMTimerSetMillies(pVdma->WatchDogTimer, cMillis);
2207 else
2208 TMTimerStop(pVdma->WatchDogTimer);
2209 return VINF_SUCCESS;
2210}
2211#endif
2212
2213int vboxVDMAConstruct(PVGASTATE pVGAState, uint32_t cPipeElements)
2214{
2215 int rc;
2216#ifdef VBOX_VDMA_WITH_WORKERTHREAD
2217 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)RTMemAllocZ(RT_OFFSETOF(VBOXVDMAHOST, CmdPool.aCmds[cPipeElements]));
2218#else
2219 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)RTMemAllocZ(sizeof(*pVdma));
2220#endif
2221 Assert(pVdma);
2222 if (pVdma)
2223 {
2224 pVdma->pHgsmi = pVGAState->pHGSMI;
2225 pVdma->pVGAState = pVGAState;
2226
2227 rc = RTSemEventMultiCreate(&pVdma->HostCrCtlCompleteEvent);
2228 if (RT_SUCCESS(rc))
2229 {
2230#ifdef VBOX_VDMA_WITH_WATCHDOG
2231 rc = PDMDevHlpTMTimerCreate(pVGAState->pDevInsR3, TMCLOCK_REAL, vboxVDMAWatchDogTimer,
2232 pVdma, TMTIMER_FLAGS_NO_CRIT_SECT,
2233 "VDMA WatchDog Timer", &pVdma->WatchDogTimer);
2234 AssertRC(rc);
2235#endif
2236 rc = VBoxVBVAExHSInit(&pVdma->CmdVbva);
2237 if (RT_SUCCESS(rc))
2238 {
2239 rc = VBoxVDMAThreadCreate(&pVdma->Thread, vboxVDMAWorkerThread, pVdma);
2240 if (RT_SUCCESS(rc))
2241 {
2242 pVGAState->pVdma = pVdma;
2243#ifdef VBOX_WITH_CRHGSMI
2244 int rcIgnored = vboxVDMACrCtlHgsmiSetup(pVdma); NOREF(rcIgnored); /** @todo is this ignoring intentional? */
2245#endif
2246 return VINF_SUCCESS;
2247 }
2248 else
2249 WARN(("VBoxVDMAThreadCreate faile %d\n", rc));
2250
2251 VBoxVBVAExHSTerm(&pVdma->CmdVbva);
2252 }
2253 else
2254 WARN(("VBoxVBVAExHSInit failed %d\n", rc));
2255
2256 RTSemEventMultiDestroy(pVdma->HostCrCtlCompleteEvent);
2257 }
2258 else
2259 WARN(("RTSemEventMultiCreate failed %d\n", rc));
2260
2261
2262 RTMemFree(pVdma);
2263 }
2264 else
2265 rc = VERR_OUT_OF_RESOURCES;
2266
2267 return rc;
2268}
2269
2270int vboxVDMAReset(struct VBOXVDMAHOST *pVdma)
2271{
2272 VBVAEXHOSTCTL Ctl;
2273 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_RESET;
2274 int rc = vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY);
2275 if (!RT_SUCCESS(rc))
2276 {
2277 WARN(("vdmaVBVACtlSubmitSync failed %d\n", rc));
2278 return rc;
2279 }
2280 return VINF_SUCCESS;
2281}
2282
2283int vboxVDMADestruct(struct VBOXVDMAHOST *pVdma)
2284{
2285 VBVAEXHOSTCTL Ctl;
2286 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_TERM;
2287 int rc = vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY);
2288 if (!RT_SUCCESS(rc))
2289 {
2290 WARN(("vdmaVBVACtlSubmitSync failed %d\n", rc));
2291 return rc;
2292 }
2293 VBoxVDMAThreadTerm(&pVdma->Thread);
2294 VBoxVBVAExHSTerm(&pVdma->CmdVbva);
2295 RTSemEventMultiDestroy(pVdma->HostCrCtlCompleteEvent);
2296 RTMemFree(pVdma);
2297 return VINF_SUCCESS;
2298}
2299
2300int vboxVDMASaveStateExecPrep(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM)
2301{
2302#ifdef VBOX_WITH_CRHGSMI
2303 PVGASTATE pVGAState = pVdma->pVGAState;
2304 PVBOXVDMACMD_CHROMIUM_CTL pCmd = (PVBOXVDMACMD_CHROMIUM_CTL)vboxVDMACrCtlCreate(
2305 VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN, sizeof (*pCmd));
2306 Assert(pCmd);
2307 if (pCmd)
2308 {
2309 int rc = vboxVDMACrCtlPost(pVGAState, pCmd, sizeof (*pCmd));
2310 AssertRC(rc);
2311 if (RT_SUCCESS(rc))
2312 {
2313 rc = vboxVDMACrCtlGetRc(pCmd);
2314 }
2315 vboxVDMACrCtlRelease(pCmd);
2316 return rc;
2317 }
2318 return VERR_NO_MEMORY;
2319#else
2320 return VINF_SUCCESS;
2321#endif
2322}
2323
2324int vboxVDMASaveStateExecDone(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM)
2325{
2326#ifdef VBOX_WITH_CRHGSMI
2327 PVGASTATE pVGAState = pVdma->pVGAState;
2328 PVBOXVDMACMD_CHROMIUM_CTL pCmd = (PVBOXVDMACMD_CHROMIUM_CTL)vboxVDMACrCtlCreate(
2329 VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END, sizeof (*pCmd));
2330 Assert(pCmd);
2331 if (pCmd)
2332 {
2333 int rc = vboxVDMACrCtlPost(pVGAState, pCmd, sizeof (*pCmd));
2334 AssertRC(rc);
2335 if (RT_SUCCESS(rc))
2336 {
2337 rc = vboxVDMACrCtlGetRc(pCmd);
2338 }
2339 vboxVDMACrCtlRelease(pCmd);
2340 return rc;
2341 }
2342 return VERR_NO_MEMORY;
2343#else
2344 return VINF_SUCCESS;
2345#endif
2346}
2347
2348void vboxVDMAControl(struct VBOXVDMAHOST *pVdma, PVBOXVDMA_CTL pCmd, uint32_t cbCmd)
2349{
2350#if 1
2351 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
2352
2353 switch (pCmd->enmCtl)
2354 {
2355 case VBOXVDMA_CTL_TYPE_ENABLE:
2356 pCmd->i32Result = VINF_SUCCESS;
2357 break;
2358 case VBOXVDMA_CTL_TYPE_DISABLE:
2359 pCmd->i32Result = VINF_SUCCESS;
2360 break;
2361 case VBOXVDMA_CTL_TYPE_FLUSH:
2362 pCmd->i32Result = VINF_SUCCESS;
2363 break;
2364#ifdef VBOX_VDMA_WITH_WATCHDOG
2365 case VBOXVDMA_CTL_TYPE_WATCHDOG:
2366 pCmd->i32Result = vboxVDMAWatchDogCtl(pVdma, pCmd->u32Offset);
2367 break;
2368#endif
2369 default:
2370 AssertBreakpoint();
2371 pCmd->i32Result = VERR_NOT_SUPPORTED;
2372 }
2373
2374 int rc = VBoxSHGSMICommandComplete (pIns, pCmd);
2375 AssertRC(rc);
2376#else
2377 /* test asinch completion */
2378 VBOXVDMACMD_SUBMIT_CONTEXT Context;
2379 Context.pVdma = pVdma;
2380 Context.Cmd.enmType = VBOXVDMAPIPE_CMD_TYPE_DMACTL;
2381 Context.Cmd.u.pCtl = pCmd;
2382
2383 int rc = vboxVDMAPipeModifyClient(&pVdma->Pipe, vboxVDMACommandSubmitCb, &Context);
2384 AssertRC(rc);
2385 if (RT_SUCCESS(rc))
2386 {
2387 Assert(Context.bQueued);
2388 if (Context.bQueued)
2389 {
2390 /* success */
2391 return;
2392 }
2393 rc = VERR_OUT_OF_RESOURCES;
2394 }
2395
2396 /* failure */
2397 Assert(RT_FAILURE(rc));
2398 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
2399 pCmd->i32Result = rc;
2400 int tmpRc = VBoxSHGSMICommandComplete (pIns, pCmd);
2401 AssertRC(tmpRc);
2402
2403#endif
2404}
2405
2406void vboxVDMACommand(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmd, uint32_t cbCmd)
2407{
2408 int rc = VERR_NOT_IMPLEMENTED;
2409
2410#ifdef VBOX_WITH_CRHGSMI
2411 /* chromium commands are processed by crhomium hgcm thread independently from our internal cmd processing pipeline
2412 * this is why we process them specially */
2413 rc = vboxVDMACmdCheckCrCmd(pVdma, pCmd, cbCmd);
2414 if (rc == VINF_SUCCESS)
2415 return;
2416
2417 if (RT_FAILURE(rc))
2418 {
2419 pCmd->rc = rc;
2420 rc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmd);
2421 AssertRC(rc);
2422 return;
2423 }
2424#endif
2425
2426#ifndef VBOX_VDMA_WITH_WORKERTHREAD
2427 vboxVDMACommandProcess(pVdma, pCmd, cbCmd);
2428#else
2429
2430# ifdef DEBUG_misha
2431 Assert(0);
2432# endif
2433
2434 VBOXVDMACMD_SUBMIT_CONTEXT Context;
2435 Context.pVdma = pVdma;
2436 Context.Cmd.enmType = VBOXVDMAPIPE_CMD_TYPE_DMACMD;
2437 Context.Cmd.u.pDr = pCmd;
2438
2439 rc = vboxVDMAPipeModifyClient(&pVdma->Pipe, vboxVDMACommandSubmitCb, &Context);
2440 AssertRC(rc);
2441 if (RT_SUCCESS(rc))
2442 {
2443 Assert(Context.bQueued);
2444 if (Context.bQueued)
2445 {
2446 /* success */
2447 return;
2448 }
2449 rc = VERR_OUT_OF_RESOURCES;
2450 }
2451 /* failure */
2452 Assert(RT_FAILURE(rc));
2453 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
2454 pCmd->rc = rc;
2455 int tmpRc = VBoxSHGSMICommandComplete (pIns, pCmd);
2456 AssertRC(tmpRc);
2457#endif
2458}
2459
2460/**/
2461
2462static int vdmaVBVACtlSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
2463{
2464 int rc = VBoxVBVAExHCtlSubmit(&pVdma->CmdVbva, pCtl, enmSource, pfnComplete, pvComplete);
2465 if (RT_SUCCESS(rc))
2466 {
2467 if (rc == VINF_SUCCESS)
2468 return VBoxVDMAThreadEventNotify(&pVdma->Thread);
2469 else
2470 Assert(rc == VINF_ALREADY_INITIALIZED);
2471 }
2472 else
2473 Log(("VBoxVBVAExHCtlSubmit failed %d\n", rc));
2474
2475 return rc;
2476}
2477
2478static DECLCALLBACK(void) vboxCmdVBVACmdCtlGuestCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext)
2479{
2480 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvContext;
2481 VBOXCMDVBVA_CTL *pGCtl = (VBOXCMDVBVA_CTL*)(pCtl->u.cmd.pu8Cmd - sizeof (VBOXCMDVBVA_CTL));
2482 AssertRC(rc);
2483 pGCtl->i32Result = rc;
2484
2485 Assert(pVdma->pVGAState->fGuestCaps & VBVACAPS_COMPLETEGCMD_BY_IOREAD);
2486 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pGCtl);
2487 AssertRC(rc);
2488
2489 VBoxVBVAExHCtlFree(pVbva, pCtl);
2490}
2491
2492static int vdmaVBVACtlOpaqueSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL_SOURCE enmSource, uint8_t* pu8Cmd, uint32_t cbCmd, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
2493{
2494 VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE);
2495 if (!pHCtl)
2496 {
2497 WARN(("VBoxVBVAExHCtlCreate failed\n"));
2498 return VERR_NO_MEMORY;
2499 }
2500
2501 pHCtl->u.cmd.pu8Cmd = pu8Cmd;
2502 pHCtl->u.cmd.cbCmd = cbCmd;
2503 int rc = vdmaVBVACtlSubmit(pVdma, pHCtl, enmSource, pfnComplete, pvComplete);
2504 if (!RT_SUCCESS(rc))
2505 {
2506 Log(("vdmaVBVACtlSubmit failed rc %d\n", rc));
2507 return rc;;
2508 }
2509 return VINF_SUCCESS;
2510}
2511
2512static int vdmaVBVACtlOpaqueGuestSubmit(PVBOXVDMAHOST pVdma, VBOXCMDVBVA_CTL *pCtl, uint32_t cbCtl)
2513{
2514 Assert(cbCtl >= sizeof (VBOXCMDVBVA_CTL));
2515 VBoxSHGSMICommandMarkAsynchCompletion(pCtl);
2516 int rc = vdmaVBVACtlOpaqueSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_GUEST, (uint8_t*)(pCtl+1), cbCtl - sizeof (VBOXCMDVBVA_CTL), vboxCmdVBVACmdCtlGuestCompletion, pVdma);
2517 if (RT_SUCCESS(rc))
2518 return VINF_SUCCESS;
2519
2520 WARN(("vdmaVBVACtlOpaqueSubmit failed %d\n", rc));
2521 pCtl->i32Result = rc;
2522 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pCtl);
2523 AssertRC(rc);
2524 return VINF_SUCCESS;
2525}
2526
2527static DECLCALLBACK(void) vboxCmdVBVACmdCtlHostCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvCompletion)
2528{
2529 VBOXCRCMDCTL* pVboxCtl = (VBOXCRCMDCTL*)pCtl->u.cmd.pu8Cmd;
2530 if (pVboxCtl->u.pfnInternal)
2531 ((PFNCRCTLCOMPLETION)pVboxCtl->u.pfnInternal)(pVboxCtl, pCtl->u.cmd.cbCmd, rc, pvCompletion);
2532 VBoxVBVAExHCtlFree(pVbva, pCtl);
2533}
2534
2535static int vdmaVBVACtlOpaqueHostSubmit(PVBOXVDMAHOST pVdma, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
2536 PFNCRCTLCOMPLETION pfnCompletion,
2537 void *pvCompletion)
2538{
2539 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
2540 int rc = vdmaVBVACtlOpaqueSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_HOST_ENABLED, (uint8_t*)pCmd, cbCmd, vboxCmdVBVACmdCtlHostCompletion, pvCompletion);
2541 if (!RT_SUCCESS(rc))
2542 {
2543 if (rc == VERR_INVALID_STATE)
2544 {
2545 pCmd->u.pfnInternal = NULL;
2546 PVGASTATE pVGAState = pVdma->pVGAState;
2547 rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCmd, cbCmd, pfnCompletion, pvCompletion);
2548 if (!RT_SUCCESS(rc))
2549 WARN(("pfnCrHgsmiControlProcess failed %d\n", rc));
2550
2551 return rc;
2552 }
2553 WARN(("vdmaVBVACtlOpaqueSubmit failed %d\n", rc));
2554 return rc;
2555 }
2556
2557 return VINF_SUCCESS;
2558}
2559
2560static int vdmaVBVACtlEnableDisableSubmitInternal(PVBOXVDMAHOST pVdma, VBVAENABLE *pEnable, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
2561{
2562 VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, VBVAEXHOSTCTL_TYPE_GH_ENABLE_DISABLE);
2563 if (!pHCtl)
2564 {
2565 WARN(("VBoxVBVAExHCtlCreate failed\n"));
2566 return VERR_NO_MEMORY;
2567 }
2568
2569 pHCtl->u.cmd.pu8Cmd = (uint8_t*)pEnable;
2570 pHCtl->u.cmd.cbCmd = sizeof (*pEnable);
2571 int rc = vdmaVBVACtlSubmit(pVdma, pHCtl, VBVAEXHOSTCTL_SOURCE_GUEST, pfnComplete, pvComplete);
2572 if (!RT_SUCCESS(rc))
2573 {
2574 WARN(("vdmaVBVACtlSubmit failed rc %d\n", rc));
2575 return rc;;
2576 }
2577 return VINF_SUCCESS;
2578}
2579
2580static int vdmaVBVACtlEnableDisableSubmit(PVBOXVDMAHOST pVdma, VBOXCMDVBVA_CTL_ENABLE *pEnable)
2581{
2582 VBoxSHGSMICommandMarkAsynchCompletion(&pEnable->Hdr);
2583 int rc = vdmaVBVACtlEnableDisableSubmitInternal(pVdma, &pEnable->Enable, vboxCmdVBVACmdCtlGuestCompletion, pVdma);
2584 if (RT_SUCCESS(rc))
2585 return VINF_SUCCESS;
2586
2587 WARN(("vdmaVBVACtlEnableDisableSubmitInternal failed %d\n", rc));
2588 pEnable->Hdr.i32Result = rc;
2589 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, &pEnable->Hdr);
2590 AssertRC(rc);
2591 return VINF_SUCCESS;
2592}
2593
2594static DECLCALLBACK(void) vdmaVBVACtlSubmitSyncCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext)
2595{
2596 VDMA_VBVA_CTL_CYNC_COMPLETION *pData = (VDMA_VBVA_CTL_CYNC_COMPLETION*)pvContext;
2597 pData->rc = rc;
2598 rc = RTSemEventSignal(pData->hEvent);
2599 if (!RT_SUCCESS(rc))
2600 WARN(("RTSemEventSignal failed %d\n", rc));
2601}
2602
2603static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource)
2604{
2605 VDMA_VBVA_CTL_CYNC_COMPLETION Data;
2606 Data.rc = VERR_NOT_IMPLEMENTED;
2607 int rc = RTSemEventCreate(&Data.hEvent);
2608 if (!RT_SUCCESS(rc))
2609 {
2610 WARN(("RTSemEventCreate failed %d\n", rc));
2611 return rc;
2612 }
2613
2614 rc = vdmaVBVACtlSubmit(pVdma, pCtl, enmSource, vdmaVBVACtlSubmitSyncCompletion, &Data);
2615 if (RT_SUCCESS(rc))
2616 {
2617 rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT);
2618 if (RT_SUCCESS(rc))
2619 {
2620 rc = Data.rc;
2621 if (!RT_SUCCESS(rc))
2622 WARN(("vdmaVBVACtlSubmitSyncCompletion returned %d\n", rc));
2623 }
2624 else
2625 WARN(("RTSemEventWait failed %d\n", rc));
2626 }
2627 else
2628 WARN(("vdmaVBVACtlSubmit failed %d\n", rc));
2629
2630 RTSemEventDestroy(Data.hEvent);
2631
2632 return rc;
2633}
2634
2635static int vdmaVBVAPause(PVBOXVDMAHOST pVdma)
2636{
2637 VBVAEXHOSTCTL Ctl;
2638 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE;
2639 return vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY);
2640}
2641
2642static int vdmaVBVAResume(PVBOXVDMAHOST pVdma)
2643{
2644 VBVAEXHOSTCTL Ctl;
2645 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME;
2646 return vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST_ANY);
2647}
2648
2649static int vboxVDMACmdSubmitPerform(struct VBOXVDMAHOST *pVdma)
2650{
2651 int rc = VBoxVBVAExHSCheckCommands(&pVdma->CmdVbva);
2652 switch (rc)
2653 {
2654 case VINF_SUCCESS:
2655 return VBoxVDMAThreadEventNotify(&pVdma->Thread);
2656 case VINF_ALREADY_INITIALIZED:
2657 case VINF_EOF:
2658 case VERR_INVALID_STATE:
2659 return VINF_SUCCESS;
2660 default:
2661 Assert(!RT_FAILURE(rc));
2662 return RT_FAILURE(rc) ? rc : VERR_INTERNAL_ERROR;
2663 }
2664}
2665
2666
2667int vboxCmdVBVACmdHostCtl(PPDMIDISPLAYVBVACALLBACKS pInterface,
2668 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
2669 PFNCRCTLCOMPLETION pfnCompletion,
2670 void *pvCompletion)
2671{
2672 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
2673 struct VBOXVDMAHOST *pVdma = pVGAState->pVdma;
2674 return vdmaVBVACtlOpaqueHostSubmit(pVdma, pCmd, cbCmd, pfnCompletion, pvCompletion);
2675}
2676
2677typedef struct VBOXCMDVBVA_CMDHOSTCTL_SYNC
2678{
2679 struct VBOXVDMAHOST *pVdma;
2680 uint32_t fProcessing;
2681 int rc;
2682} VBOXCMDVBVA_CMDHOSTCTL_SYNC;
2683
2684static DECLCALLBACK(void) vboxCmdVBVACmdHostCtlSyncCb(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
2685{
2686 VBOXCMDVBVA_CMDHOSTCTL_SYNC *pData = (VBOXCMDVBVA_CMDHOSTCTL_SYNC*)pvCompletion;
2687
2688 pData->rc = rc;
2689 pData->fProcessing = 0;
2690
2691 struct VBOXVDMAHOST *pVdma = pData->pVdma;
2692
2693 ASMAtomicIncS32(&pVdma->i32cHostCrCtlCompleted);
2694
2695 RTSemEventMultiSignal(pVdma->HostCrCtlCompleteEvent);
2696}
2697
2698int vboxCmdVBVACmdHostCtlSync(PPDMIDISPLAYVBVACALLBACKS pInterface,
2699 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
2700{
2701 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
2702 struct VBOXVDMAHOST *pVdma = pVGAState->pVdma;
2703 VBOXCMDVBVA_CMDHOSTCTL_SYNC Data;
2704 Data.pVdma = pVdma;
2705 Data.fProcessing = 1;
2706 Data.rc = VERR_INTERNAL_ERROR;
2707 int rc = vdmaVBVACtlOpaqueHostSubmit(pVdma, pCmd, cbCmd, vboxCmdVBVACmdHostCtlSyncCb, &Data);
2708 if (!RT_SUCCESS(rc))
2709 {
2710 WARN(("vdmaVBVACtlOpaqueHostSubmit failed %d", rc));
2711 return rc;
2712 }
2713
2714 while (Data.fProcessing)
2715 {
2716 /* Poll infrequently to make sure no completed message has been missed. */
2717 RTSemEventMultiWait(pVdma->HostCrCtlCompleteEvent, 500);
2718
2719 if (Data.fProcessing)
2720 RTThreadYield();
2721 }
2722
2723 /* 'Our' message has been processed, so should reset the semaphore.
2724 * There is still possible that another message has been processed
2725 * and the semaphore has been signalled again.
2726 * Reset only if there are no other messages completed.
2727 */
2728 int32_t c = ASMAtomicDecS32(&pVdma->i32cHostCrCtlCompleted);
2729 Assert(c >= 0);
2730 if (!c)
2731 RTSemEventMultiReset(pVdma->HostCrCtlCompleteEvent);
2732
2733 rc = Data.rc;
2734 if (!RT_SUCCESS(rc))
2735 WARN(("host call failed %d", rc));
2736
2737 return rc;
2738}
2739
2740int vboxCmdVBVACmdCtl(PVGASTATE pVGAState, VBOXCMDVBVA_CTL *pCtl, uint32_t cbCtl)
2741{
2742 struct VBOXVDMAHOST *pVdma = pVGAState->pVdma;
2743 int rc = VINF_SUCCESS;
2744 switch (pCtl->u32Type)
2745 {
2746 case VBOXCMDVBVACTL_TYPE_3DCTL:
2747 return vdmaVBVACtlOpaqueGuestSubmit(pVdma, pCtl, cbCtl);
2748 case VBOXCMDVBVACTL_TYPE_ENABLE:
2749 if (cbCtl != sizeof (VBOXCMDVBVA_CTL_ENABLE))
2750 {
2751 WARN(("incorrect enable size\n"));
2752 rc = VERR_INVALID_PARAMETER;
2753 break;
2754 }
2755 return vdmaVBVACtlEnableDisableSubmit(pVdma, (VBOXCMDVBVA_CTL_ENABLE*)pCtl);
2756 default:
2757 WARN(("unsupported type\n"));
2758 rc = VERR_INVALID_PARAMETER;
2759 break;
2760 }
2761
2762 pCtl->i32Result = rc;
2763 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pCtl);
2764 AssertRC(rc);
2765 return VINF_SUCCESS;
2766}
2767
2768int vboxCmdVBVACmdSubmit(PVGASTATE pVGAState)
2769{
2770 if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva))
2771 {
2772 WARN(("vdma VBVA is disabled\n"));
2773 return VERR_INVALID_STATE;
2774 }
2775
2776 return vboxVDMACmdSubmitPerform(pVGAState->pVdma);
2777}
2778
2779int vboxCmdVBVACmdFlush(PVGASTATE pVGAState)
2780{
2781 WARN(("flush\n"));
2782 if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva))
2783 {
2784 WARN(("vdma VBVA is disabled\n"));
2785 return VERR_INVALID_STATE;
2786 }
2787 return vboxVDMACmdSubmitPerform(pVGAState->pVdma);
2788}
2789
2790void vboxCmdVBVACmdTimer(PVGASTATE pVGAState)
2791{
2792 if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva))
2793 return;
2794 vboxVDMACmdSubmitPerform(pVGAState->pVdma);
2795}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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