VirtualBox

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

Last change on this file since 60404 was 58700, checked in by vboxsync, 9 years ago

DevVGA_VDMA.cpp: check that pVdma is already created.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 112.4 KB
Line 
1/* $Id: DevVGA_VDMA.cpp 58700 2015-11-16 16:02:07Z vboxsync $ */
2/** @file
3 * Video DMA (VDMA) support.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include <VBox/VMMDev.h>
18#include <VBox/vmm/pdmdev.h>
19#include <VBox/vmm/pgm.h>
20#include <VBox/VBoxVideo.h>
21#include <iprt/semaphore.h>
22#include <iprt/thread.h>
23#include <iprt/mem.h>
24#include <iprt/asm.h>
25#include <iprt/list.h>
26#include <iprt/param.h>
27
28#include "DevVGA.h"
29#include "HGSMI/SHGSMIHost.h"
30
31#include <VBox/VBoxVideo3D.h>
32#include <VBox/VBoxVideoHost3D.h>
33
34#ifdef DEBUG_misha
35# define VBOXVDBG_MEMCACHE_DISABLE
36#endif
37
38#ifndef VBOXVDBG_MEMCACHE_DISABLE
39# include <iprt/memcache.h>
40#endif
41
42#ifdef DEBUG_misha
43#define WARN_BP() do { AssertFailed(); } while (0)
44#else
45#define WARN_BP() do { } while (0)
46#endif
47#define WARN(_msg) do { \
48 LogRel(_msg); \
49 WARN_BP(); \
50 } while (0)
51
52#define VBOXVDMATHREAD_STATE_TERMINATED 0
53#define VBOXVDMATHREAD_STATE_CREATING 1
54#define VBOXVDMATHREAD_STATE_CREATED 3
55#define VBOXVDMATHREAD_STATE_TERMINATING 4
56
57struct VBOXVDMATHREAD;
58
59typedef DECLCALLBACKPTR(void, PFNVBOXVDMATHREAD_CHANGED)(struct VBOXVDMATHREAD *pThread, int rc, void *pvThreadContext, void *pvChangeContext);
60
61static DECLCALLBACK(int) vboxCmdVBVACmdCallout(struct VBOXVDMAHOST *pVdma, struct VBOXCRCMDCTL* pCmd, VBOXCRCMDCTL_CALLOUT_LISTENTRY *pEntry, PFNVBOXCRCMDCTL_CALLOUT_CB pfnCb);
62
63
64typedef struct VBOXVDMATHREAD
65{
66 RTTHREAD hWorkerThread;
67 RTSEMEVENT hEvent;
68 volatile uint32_t u32State;
69 PFNVBOXVDMATHREAD_CHANGED pfnChanged;
70 void *pvChanged;
71} VBOXVDMATHREAD, *PVBOXVDMATHREAD;
72
73
74/* state transformations:
75 *
76 * submitter | processor
77 *
78 * LISTENING ---> PROCESSING
79 *
80 * */
81#define VBVAEXHOSTCONTEXT_STATE_LISTENING 0
82#define VBVAEXHOSTCONTEXT_STATE_PROCESSING 1
83
84#define VBVAEXHOSTCONTEXT_ESTATE_DISABLED -1
85#define VBVAEXHOSTCONTEXT_ESTATE_PAUSED 0
86#define VBVAEXHOSTCONTEXT_ESTATE_ENABLED 1
87
88typedef struct VBVAEXHOSTCONTEXT
89{
90 VBVABUFFER *pVBVA;
91 volatile int32_t i32State;
92 volatile int32_t i32EnableState;
93 volatile uint32_t u32cCtls;
94 /* critical section for accessing ctl lists */
95 RTCRITSECT CltCritSect;
96 RTLISTANCHOR GuestCtlList;
97 RTLISTANCHOR HostCtlList;
98#ifndef VBOXVDBG_MEMCACHE_DISABLE
99 RTMEMCACHE CtlCache;
100#endif
101} VBVAEXHOSTCONTEXT;
102
103typedef enum
104{
105 VBVAEXHOSTCTL_TYPE_UNDEFINED = 0,
106 VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE,
107 VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME,
108 VBVAEXHOSTCTL_TYPE_HH_SAVESTATE,
109 VBVAEXHOSTCTL_TYPE_HH_LOADSTATE,
110 VBVAEXHOSTCTL_TYPE_HH_LOADSTATE_DONE,
111 VBVAEXHOSTCTL_TYPE_HH_BE_OPAQUE,
112 VBVAEXHOSTCTL_TYPE_HH_ON_HGCM_UNLOAD,
113 VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE,
114 VBVAEXHOSTCTL_TYPE_GHH_ENABLE,
115 VBVAEXHOSTCTL_TYPE_GHH_ENABLE_PAUSED,
116 VBVAEXHOSTCTL_TYPE_GHH_DISABLE,
117 VBVAEXHOSTCTL_TYPE_GHH_RESIZE
118} VBVAEXHOSTCTL_TYPE;
119
120struct VBVAEXHOSTCTL;
121
122typedef DECLCALLBACKPTR(void, PFNVBVAEXHOSTCTL_COMPLETE)(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvComplete);
123
124typedef struct VBVAEXHOSTCTL
125{
126 RTLISTNODE Node;
127 VBVAEXHOSTCTL_TYPE enmType;
128 union
129 {
130 struct
131 {
132 uint8_t * pu8Cmd;
133 uint32_t cbCmd;
134 } cmd;
135
136 struct
137 {
138 PSSMHANDLE pSSM;
139 uint32_t u32Version;
140 } state;
141 } u;
142 PFNVBVAEXHOSTCTL_COMPLETE pfnComplete;
143 void *pvComplete;
144} VBVAEXHOSTCTL;
145
146/* VBoxVBVAExHP**, i.e. processor functions, can NOT be called concurrently with each other,
147 * but can be called with other VBoxVBVAExS** (submitter) functions except Init/Start/Term aparently.
148 * Can only be called be the processor, i.e. the entity that acquired the processor state by direct or indirect call to the VBoxVBVAExHSCheckCommands
149 * see mor edetailed comments in headers for function definitions */
150typedef enum
151{
152 VBVAEXHOST_DATA_TYPE_NO_DATA = 0,
153 VBVAEXHOST_DATA_TYPE_CMD,
154 VBVAEXHOST_DATA_TYPE_HOSTCTL,
155 VBVAEXHOST_DATA_TYPE_GUESTCTL
156} VBVAEXHOST_DATA_TYPE;
157
158static DECLCALLBACK(int) vdmaVBVANotifyDisable(PVGASTATE pVGAState);
159
160
161static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd);
162
163static void VBoxVBVAExHPDataCompleteCmd(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint32_t cbCmd);
164static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc);
165
166/* VBoxVBVAExHP**, i.e. processor functions, can NOT be called concurrently with each other,
167 * can be called concurrently with istelf as well as with other VBoxVBVAEx** functions except Init/Start/Term aparently */
168static int VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva);
169
170static int VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva);
171static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA);
172static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva);
173static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva);
174static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM);
175static int VBoxVBVAExHSLoadState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version);
176
177static VBVAEXHOSTCTL* VBoxVBVAExHCtlAlloc(VBVAEXHOSTCONTEXT *pCmdVbva)
178{
179#ifndef VBOXVDBG_MEMCACHE_DISABLE
180 return (VBVAEXHOSTCTL*)RTMemCacheAlloc(pCmdVbva->CtlCache);
181#else
182 return (VBVAEXHOSTCTL*)RTMemAlloc(sizeof (VBVAEXHOSTCTL));
183#endif
184}
185
186static void VBoxVBVAExHCtlFree(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl)
187{
188#ifndef VBOXVDBG_MEMCACHE_DISABLE
189 RTMemCacheFree(pCmdVbva->CtlCache, pCtl);
190#else
191 RTMemFree(pCtl);
192#endif
193}
194
195static VBVAEXHOSTCTL* VBoxVBVAExHCtlCreate(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL_TYPE enmType)
196{
197 VBVAEXHOSTCTL* pCtl = VBoxVBVAExHCtlAlloc(pCmdVbva);
198 if (!pCtl)
199 {
200 WARN(("VBoxVBVAExHCtlAlloc failed\n"));
201 return NULL;
202 }
203
204 pCtl->enmType = enmType;
205 return pCtl;
206}
207
208static int vboxVBVAExHSProcessorAcquire(struct VBVAEXHOSTCONTEXT *pCmdVbva)
209{
210 Assert(pCmdVbva->i32State >= VBVAEXHOSTCONTEXT_STATE_LISTENING);
211
212 if (ASMAtomicCmpXchgS32(&pCmdVbva->i32State, VBVAEXHOSTCONTEXT_STATE_PROCESSING, VBVAEXHOSTCONTEXT_STATE_LISTENING))
213 return VINF_SUCCESS;
214 return VERR_SEM_BUSY;
215}
216
217static VBVAEXHOSTCTL* vboxVBVAExHPCheckCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, bool *pfHostCtl, bool fHostOnlyMode)
218{
219 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
220
221 if(!fHostOnlyMode && !ASMAtomicUoReadU32(&pCmdVbva->u32cCtls))
222 return NULL;
223
224 int rc = RTCritSectEnter(&pCmdVbva->CltCritSect);
225 if (RT_SUCCESS(rc))
226 {
227 VBVAEXHOSTCTL* pCtl = RTListGetFirst(&pCmdVbva->HostCtlList, VBVAEXHOSTCTL, Node);
228 if (pCtl)
229 *pfHostCtl = true;
230 else if (!fHostOnlyMode)
231 {
232 if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) != VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
233 {
234 pCtl = RTListGetFirst(&pCmdVbva->GuestCtlList, VBVAEXHOSTCTL, Node);
235 /* pCtl can not be null here since pCmdVbva->u32cCtls is not null,
236 * and there are no HostCtl commands*/
237 Assert(pCtl);
238 *pfHostCtl = false;
239 }
240 }
241
242 if (pCtl)
243 {
244 RTListNodeRemove(&pCtl->Node);
245 ASMAtomicDecU32(&pCmdVbva->u32cCtls);
246 }
247
248 RTCritSectLeave(&pCmdVbva->CltCritSect);
249
250 return pCtl;
251 }
252 else
253 WARN(("RTCritSectEnter failed %d\n", rc));
254
255 return NULL;
256}
257
258static VBVAEXHOSTCTL* VBoxVBVAExHPCheckHostCtlOnDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva)
259{
260 bool fHostCtl = false;
261 VBVAEXHOSTCTL* pCtl = vboxVBVAExHPCheckCtl(pCmdVbva, &fHostCtl, true);
262 Assert(!pCtl || fHostCtl);
263 return pCtl;
264}
265
266static int VBoxVBVAExHPPause(struct VBVAEXHOSTCONTEXT *pCmdVbva)
267{
268 if (pCmdVbva->i32EnableState < VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
269 {
270 WARN(("Invalid state\n"));
271 return VERR_INVALID_STATE;
272 }
273
274 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
275 return VINF_SUCCESS;
276}
277
278static int VBoxVBVAExHPResume(struct VBVAEXHOSTCONTEXT *pCmdVbva)
279{
280 if (pCmdVbva->i32EnableState != VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
281 {
282 WARN(("Invalid state\n"));
283 return VERR_INVALID_STATE;
284 }
285
286 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_ENABLED);
287 return VINF_SUCCESS;
288}
289
290
291static bool vboxVBVAExHPCheckProcessCtlInternal(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl)
292{
293 switch (pCtl->enmType)
294 {
295 case VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE:
296 {
297 int rc = VBoxVBVAExHPPause(pCmdVbva);
298 VBoxVBVAExHPDataCompleteCtl(pCmdVbva, pCtl, VINF_SUCCESS);
299 return true;
300 }
301 case VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME:
302 {
303 int rc = VBoxVBVAExHPResume(pCmdVbva);
304 VBoxVBVAExHPDataCompleteCtl(pCmdVbva, pCtl, VINF_SUCCESS);
305 return true;
306 }
307 default:
308 return false;
309 }
310}
311
312static void vboxVBVAExHPProcessorRelease(struct VBVAEXHOSTCONTEXT *pCmdVbva)
313{
314 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
315
316 ASMAtomicWriteS32(&pCmdVbva->i32State, VBVAEXHOSTCONTEXT_STATE_LISTENING);
317}
318
319static void vboxVBVAExHPHgEventSet(struct VBVAEXHOSTCONTEXT *pCmdVbva)
320{
321 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
322 if (pCmdVbva->pVBVA)
323 ASMAtomicOrU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, VBVA_F_STATE_PROCESSING);
324}
325
326static void vboxVBVAExHPHgEventClear(struct VBVAEXHOSTCONTEXT *pCmdVbva)
327{
328 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
329 if (pCmdVbva->pVBVA)
330 ASMAtomicAndU32(&pCmdVbva->pVBVA->hostFlags.u32HostEvents, ~VBVA_F_STATE_PROCESSING);
331}
332
333static int vboxVBVAExHPCmdGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
334{
335 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
336 Assert(pCmdVbva->i32EnableState > VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
337
338 VBVABUFFER *pVBVA = pCmdVbva->pVBVA;
339
340 uint32_t indexRecordFirst = pVBVA->indexRecordFirst;
341 uint32_t indexRecordFree = pVBVA->indexRecordFree;
342
343 Log(("first = %d, free = %d\n",
344 indexRecordFirst, indexRecordFree));
345
346 if (indexRecordFirst == indexRecordFree)
347 {
348 /* No records to process. Return without assigning output variables. */
349 return VINF_EOF;
350 }
351
352 uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVBVA->aRecords[indexRecordFirst].cbRecord);
353
354 /* A new record need to be processed. */
355 if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
356 {
357 /* the record is being recorded, try again */
358 return VINF_TRY_AGAIN;
359 }
360
361 uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;
362
363 if (!cbRecord)
364 {
365 /* the record is being recorded, try again */
366 return VINF_TRY_AGAIN;
367 }
368
369 /* we should not get partial commands here actually */
370 Assert(cbRecord);
371
372 /* The size of largest contiguous chunk in the ring biffer. */
373 uint32_t u32BytesTillBoundary = pVBVA->cbData - pVBVA->off32Data;
374
375 /* The pointer to data in the ring buffer. */
376 uint8_t *pSrc = &pVBVA->au8Data[pVBVA->off32Data];
377
378 /* Fetch or point the data. */
379 if (u32BytesTillBoundary >= cbRecord)
380 {
381 /* The command does not cross buffer boundary. Return address in the buffer. */
382 *ppCmd = pSrc;
383 *pcbCmd = cbRecord;
384 return VINF_SUCCESS;
385 }
386
387 LogRel(("CmdVbva: cross-bound writes unsupported\n"));
388 return VERR_INVALID_STATE;
389}
390
391static void VBoxVBVAExHPDataCompleteCmd(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint32_t cbCmd)
392{
393 VBVABUFFER *pVBVA = pCmdVbva->pVBVA;
394 pVBVA->off32Data = (pVBVA->off32Data + cbCmd) % pVBVA->cbData;
395
396 pVBVA->indexRecordFirst = (pVBVA->indexRecordFirst + 1) % RT_ELEMENTS(pVBVA->aRecords);
397}
398
399static void VBoxVBVAExHPDataCompleteCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL *pCtl, int rc)
400{
401 if (pCtl->pfnComplete)
402 pCtl->pfnComplete(pCmdVbva, pCtl, rc, pCtl->pvComplete);
403 else
404 VBoxVBVAExHCtlFree(pCmdVbva, pCtl);
405}
406
407static VBVAEXHOST_DATA_TYPE vboxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
408{
409 Assert(pCmdVbva->i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
410 VBVAEXHOSTCTL*pCtl;
411 bool fHostClt;
412
413 for(;;)
414 {
415 pCtl = vboxVBVAExHPCheckCtl(pCmdVbva, &fHostClt, false);
416 if (pCtl)
417 {
418 if (fHostClt)
419 {
420 if (!vboxVBVAExHPCheckProcessCtlInternal(pCmdVbva, pCtl))
421 {
422 *ppCmd = (uint8_t*)pCtl;
423 *pcbCmd = sizeof (*pCtl);
424 return VBVAEXHOST_DATA_TYPE_HOSTCTL;
425 }
426 continue;
427 }
428 else
429 {
430 *ppCmd = (uint8_t*)pCtl;
431 *pcbCmd = sizeof (*pCtl);
432 return VBVAEXHOST_DATA_TYPE_GUESTCTL;
433 }
434 }
435
436 if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) <= VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
437 return VBVAEXHOST_DATA_TYPE_NO_DATA;
438
439 int rc = vboxVBVAExHPCmdGet(pCmdVbva, ppCmd, pcbCmd);
440 switch (rc)
441 {
442 case VINF_SUCCESS:
443 return VBVAEXHOST_DATA_TYPE_CMD;
444 case VINF_EOF:
445 return VBVAEXHOST_DATA_TYPE_NO_DATA;
446 case VINF_TRY_AGAIN:
447 RTThreadSleep(1);
448 continue;
449 default:
450 /* this is something really unexpected, i.e. most likely guest has written something incorrect to the VBVA buffer */
451 WARN(("Warning: vboxVBVAExHCmdGet returned unexpected status %d\n", rc));
452 return VBVAEXHOST_DATA_TYPE_NO_DATA;
453 }
454 }
455
456 WARN(("Warning: VBoxVBVAExHCmdGet unexpected state\n"));
457 return VBVAEXHOST_DATA_TYPE_NO_DATA;
458}
459
460static VBVAEXHOST_DATA_TYPE VBoxVBVAExHPDataGet(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t **ppCmd, uint32_t *pcbCmd)
461{
462 VBVAEXHOST_DATA_TYPE enmType = vboxVBVAExHPDataGet(pCmdVbva, ppCmd, pcbCmd);
463 if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA)
464 {
465 vboxVBVAExHPHgEventClear(pCmdVbva);
466 vboxVBVAExHPProcessorRelease(pCmdVbva);
467 /* we need to prevent racing between us clearing the flag and command check/submission thread, i.e.
468 * 1. we check the queue -> and it is empty
469 * 2. submitter adds command to the queue
470 * 3. submitter checks the "processing" -> and it is true , thus it does not submit a notification
471 * 4. we clear the "processing" state
472 * 5. ->here we need to re-check the queue state to ensure we do not leak the notification of the above command
473 * 6. if the queue appears to be not-empty set the "processing" state back to "true"
474 **/
475 int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva);
476 if (RT_SUCCESS(rc))
477 {
478 /* we are the processor now */
479 enmType = vboxVBVAExHPDataGet(pCmdVbva, ppCmd, pcbCmd);
480 if (enmType == VBVAEXHOST_DATA_TYPE_NO_DATA)
481 {
482 vboxVBVAExHPProcessorRelease(pCmdVbva);
483 return VBVAEXHOST_DATA_TYPE_NO_DATA;
484 }
485
486 vboxVBVAExHPHgEventSet(pCmdVbva);
487 }
488 }
489
490 return enmType;
491}
492
493DECLINLINE(bool) vboxVBVAExHSHasCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva)
494{
495 VBVABUFFER *pVBVA = pCmdVbva->pVBVA;
496
497 if (pVBVA)
498 {
499 uint32_t indexRecordFirst = pVBVA->indexRecordFirst;
500 uint32_t indexRecordFree = pVBVA->indexRecordFree;
501
502 if (indexRecordFirst != indexRecordFree)
503 return true;
504 }
505
506 return !!ASMAtomicReadU32(&pCmdVbva->u32cCtls);
507}
508
509/* Checks whether the new commands are ready for processing
510 * @returns
511 * 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)
512 * VINF_EOF - no commands in a queue
513 * VINF_ALREADY_INITIALIZED - another thread already processing the commands
514 * VERR_INVALID_STATE - the VBVA is paused or pausing */
515static int VBoxVBVAExHSCheckCommands(struct VBVAEXHOSTCONTEXT *pCmdVbva)
516{
517 int rc = vboxVBVAExHSProcessorAcquire(pCmdVbva);
518 if (RT_SUCCESS(rc))
519 {
520 /* we are the processor now */
521 if (vboxVBVAExHSHasCommands(pCmdVbva))
522 {
523 vboxVBVAExHPHgEventSet(pCmdVbva);
524 return VINF_SUCCESS;
525 }
526
527 vboxVBVAExHPProcessorRelease(pCmdVbva);
528 return VINF_EOF;
529 }
530 if (rc == VERR_SEM_BUSY)
531 return VINF_ALREADY_INITIALIZED;
532 return VERR_INVALID_STATE;
533}
534
535static int VBoxVBVAExHSInit(struct VBVAEXHOSTCONTEXT *pCmdVbva)
536{
537 memset(pCmdVbva, 0, sizeof (*pCmdVbva));
538 int rc = RTCritSectInit(&pCmdVbva->CltCritSect);
539 if (RT_SUCCESS(rc))
540 {
541#ifndef VBOXVDBG_MEMCACHE_DISABLE
542 rc = RTMemCacheCreate(&pCmdVbva->CtlCache, sizeof (VBVAEXHOSTCTL),
543 0, /* size_t cbAlignment */
544 UINT32_MAX, /* uint32_t cMaxObjects */
545 NULL, /* PFNMEMCACHECTOR pfnCtor*/
546 NULL, /* PFNMEMCACHEDTOR pfnDtor*/
547 NULL, /* void *pvUser*/
548 0 /* uint32_t fFlags*/
549 );
550 if (RT_SUCCESS(rc))
551#endif
552 {
553 RTListInit(&pCmdVbva->GuestCtlList);
554 RTListInit(&pCmdVbva->HostCtlList);
555 pCmdVbva->i32State = VBVAEXHOSTCONTEXT_STATE_PROCESSING;
556 pCmdVbva->i32EnableState = VBVAEXHOSTCONTEXT_ESTATE_DISABLED;
557 return VINF_SUCCESS;
558 }
559#ifndef VBOXVDBG_MEMCACHE_DISABLE
560 else
561 WARN(("RTMemCacheCreate failed %d\n", rc));
562#endif
563 }
564 else
565 WARN(("RTCritSectInit failed %d\n", rc));
566
567 return rc;
568}
569
570DECLINLINE(bool) VBoxVBVAExHSIsEnabled(struct VBVAEXHOSTCONTEXT *pCmdVbva)
571{
572 return (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) >= VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
573}
574
575DECLINLINE(bool) VBoxVBVAExHSIsDisabled(struct VBVAEXHOSTCONTEXT *pCmdVbva)
576{
577 return (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) == VBVAEXHOSTCONTEXT_ESTATE_DISABLED);
578}
579
580static int VBoxVBVAExHSEnable(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVABUFFER *pVBVA)
581{
582 if (VBoxVBVAExHSIsEnabled(pCmdVbva))
583 {
584 WARN(("VBVAEx is enabled already\n"));
585 return VERR_INVALID_STATE;
586 }
587
588 pCmdVbva->pVBVA = pVBVA;
589 pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0;
590 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_ENABLED);
591 return VINF_SUCCESS;
592}
593
594static int VBoxVBVAExHSDisable(struct VBVAEXHOSTCONTEXT *pCmdVbva)
595{
596 if (VBoxVBVAExHSIsDisabled(pCmdVbva))
597 return VINF_SUCCESS;
598
599 ASMAtomicWriteS32(&pCmdVbva->i32EnableState, VBVAEXHOSTCONTEXT_ESTATE_DISABLED);
600 return VINF_SUCCESS;
601}
602
603static void VBoxVBVAExHSTerm(struct VBVAEXHOSTCONTEXT *pCmdVbva)
604{
605 /* ensure the processor is stopped */
606 Assert(pCmdVbva->i32State >= VBVAEXHOSTCONTEXT_STATE_LISTENING);
607
608 /* ensure no one tries to submit the command */
609 if (pCmdVbva->pVBVA)
610 pCmdVbva->pVBVA->hostFlags.u32HostEvents = 0;
611
612 Assert(RTListIsEmpty(&pCmdVbva->GuestCtlList));
613 Assert(RTListIsEmpty(&pCmdVbva->HostCtlList));
614
615 RTCritSectDelete(&pCmdVbva->CltCritSect);
616
617#ifndef VBOXVDBG_MEMCACHE_DISABLE
618 RTMemCacheDestroy(pCmdVbva->CtlCache);
619#endif
620
621 memset(pCmdVbva, 0, sizeof (*pCmdVbva));
622}
623
624static int vboxVBVAExHSSaveGuestCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl, uint8_t* pu8VramBase, PSSMHANDLE pSSM)
625{
626 int rc = SSMR3PutU32(pSSM, pCtl->enmType);
627 AssertRCReturn(rc, rc);
628 rc = SSMR3PutU32(pSSM, pCtl->u.cmd.cbCmd);
629 AssertRCReturn(rc, rc);
630 rc = SSMR3PutU32(pSSM, (uint32_t)(pCtl->u.cmd.pu8Cmd - pu8VramBase));
631 AssertRCReturn(rc, rc);
632
633 return VINF_SUCCESS;
634}
635
636static int vboxVBVAExHSSaveStateLocked(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM)
637{
638 if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) != VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
639 {
640 WARN(("vbva not paused\n"));
641 return VERR_INVALID_STATE;
642 }
643
644 VBVAEXHOSTCTL* pCtl;
645 int rc;
646 RTListForEach(&pCmdVbva->GuestCtlList, pCtl, VBVAEXHOSTCTL, Node)
647 {
648 rc = vboxVBVAExHSSaveGuestCtl(pCmdVbva, pCtl, pu8VramBase, pSSM);
649 AssertRCReturn(rc, rc);
650 }
651
652 rc = SSMR3PutU32(pSSM, 0);
653 AssertRCReturn(rc, rc);
654
655 return VINF_SUCCESS;
656}
657/* Saves state
658 * @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail
659 */
660static int VBoxVBVAExHSSaveState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM)
661{
662 int rc = RTCritSectEnter(&pCmdVbva->CltCritSect);
663 if (RT_FAILURE(rc))
664 {
665 WARN(("RTCritSectEnter failed %d\n", rc));
666 return rc;
667 }
668
669 rc = vboxVBVAExHSSaveStateLocked(pCmdVbva, pu8VramBase, pSSM);
670 if (RT_FAILURE(rc))
671 WARN(("vboxVBVAExHSSaveStateLocked failed %d\n", rc));
672
673 RTCritSectLeave(&pCmdVbva->CltCritSect);
674
675 return rc;
676}
677
678static int vboxVBVAExHSLoadGuestCtl(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version)
679{
680 uint32_t u32;
681 int rc = SSMR3GetU32(pSSM, &u32);
682 AssertLogRelRCReturn(rc, rc);
683
684 if (!u32)
685 return VINF_EOF;
686
687 VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(pCmdVbva, (VBVAEXHOSTCTL_TYPE)u32);
688 if (!pHCtl)
689 {
690 WARN(("VBoxVBVAExHCtlCreate failed\n"));
691 return VERR_NO_MEMORY;
692 }
693
694 rc = SSMR3GetU32(pSSM, &u32);
695 AssertLogRelRCReturn(rc, rc);
696 pHCtl->u.cmd.cbCmd = u32;
697
698 rc = SSMR3GetU32(pSSM, &u32);
699 AssertLogRelRCReturn(rc, rc);
700 pHCtl->u.cmd.pu8Cmd = pu8VramBase + u32;
701
702 RTListAppend(&pCmdVbva->GuestCtlList, &pHCtl->Node);
703 ++pCmdVbva->u32cCtls;
704
705 return VINF_SUCCESS;
706}
707
708
709static int vboxVBVAExHSLoadStateLocked(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version)
710{
711 if (ASMAtomicUoReadS32(&pCmdVbva->i32EnableState) != VBVAEXHOSTCONTEXT_ESTATE_PAUSED)
712 {
713 WARN(("vbva not stopped\n"));
714 return VERR_INVALID_STATE;
715 }
716
717 int rc;
718
719 do {
720 rc = vboxVBVAExHSLoadGuestCtl(pCmdVbva, pu8VramBase, pSSM, u32Version);
721 AssertLogRelRCReturn(rc, rc);
722 } while (VINF_EOF != rc);
723
724 return VINF_SUCCESS;
725}
726
727/* Loads state
728 * @returns - same as VBoxVBVAExHSCheckCommands, or failure on load state fail
729 */
730static int VBoxVBVAExHSLoadState(struct VBVAEXHOSTCONTEXT *pCmdVbva, uint8_t* pu8VramBase, PSSMHANDLE pSSM, uint32_t u32Version)
731{
732 Assert(VGA_SAVEDSTATE_VERSION_3D <= u32Version);
733 int rc = RTCritSectEnter(&pCmdVbva->CltCritSect);
734 if (RT_FAILURE(rc))
735 {
736 WARN(("RTCritSectEnter failed %d\n", rc));
737 return rc;
738 }
739
740 rc = vboxVBVAExHSLoadStateLocked(pCmdVbva, pu8VramBase, pSSM, u32Version);
741 if (RT_FAILURE(rc))
742 WARN(("vboxVBVAExHSSaveStateLocked failed %d\n", rc));
743
744 RTCritSectLeave(&pCmdVbva->CltCritSect);
745
746 return rc;
747}
748
749typedef enum
750{
751 VBVAEXHOSTCTL_SOURCE_GUEST = 0,
752 VBVAEXHOSTCTL_SOURCE_HOST
753} VBVAEXHOSTCTL_SOURCE;
754
755
756static int VBoxVBVAExHCtlSubmit(VBVAEXHOSTCONTEXT *pCmdVbva, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
757{
758 if (!VBoxVBVAExHSIsEnabled(pCmdVbva))
759 {
760 Log(("cmd vbva not enabled\n"));
761 return VERR_INVALID_STATE;
762 }
763
764 pCtl->pfnComplete = pfnComplete;
765 pCtl->pvComplete = pvComplete;
766
767 int rc = RTCritSectEnter(&pCmdVbva->CltCritSect);
768 if (RT_SUCCESS(rc))
769 {
770 if (!VBoxVBVAExHSIsEnabled(pCmdVbva))
771 {
772 Log(("cmd vbva not enabled\n"));
773 RTCritSectLeave(&pCmdVbva->CltCritSect);
774 return VERR_INVALID_STATE;
775 }
776
777 if (enmSource > VBVAEXHOSTCTL_SOURCE_GUEST)
778 {
779 RTListAppend(&pCmdVbva->HostCtlList, &pCtl->Node);
780 }
781 else
782 RTListAppend(&pCmdVbva->GuestCtlList, &pCtl->Node);
783
784 ASMAtomicIncU32(&pCmdVbva->u32cCtls);
785
786 RTCritSectLeave(&pCmdVbva->CltCritSect);
787
788 rc = VBoxVBVAExHSCheckCommands(pCmdVbva);
789 }
790 else
791 WARN(("RTCritSectEnter failed %d\n", rc));
792
793 return rc;
794}
795
796#ifdef VBOX_WITH_CRHGSMI
797typedef struct VBOXVDMA_SOURCE
798{
799 VBVAINFOSCREEN Screen;
800 VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);
801} VBOXVDMA_SOURCE;
802#endif
803
804typedef struct VBOXVDMAHOST
805{
806 PHGSMIINSTANCE pHgsmi;
807 PVGASTATE pVGAState;
808#ifdef VBOX_WITH_CRHGSMI
809 VBVAEXHOSTCONTEXT CmdVbva;
810 VBOXVDMATHREAD Thread;
811 VBOXCRCMD_SVRINFO CrSrvInfo;
812 VBVAEXHOSTCTL* pCurRemainingHostCtl;
813 RTSEMEVENTMULTI HostCrCtlCompleteEvent;
814 int32_t volatile i32cHostCrCtlCompleted;
815 RTCRITSECT CalloutCritSect;
816// VBOXVDMA_SOURCE aSources[VBOX_VIDEO_MAX_SCREENS];
817#endif
818#ifdef VBOX_VDMA_WITH_WATCHDOG
819 PTMTIMERR3 WatchDogTimer;
820#endif
821} VBOXVDMAHOST, *PVBOXVDMAHOST;
822
823#ifdef VBOX_WITH_CRHGSMI
824
825void VBoxVDMAThreadNotifyConstructSucceeded(PVBOXVDMATHREAD pThread, void *pvThreadContext)
826{
827 Assert(pThread->u32State == VBOXVDMATHREAD_STATE_CREATING);
828 PFNVBOXVDMATHREAD_CHANGED pfnChanged = pThread->pfnChanged;
829 void *pvChanged = pThread->pvChanged;
830
831 pThread->pfnChanged = NULL;
832 pThread->pvChanged = NULL;
833
834 ASMAtomicWriteU32(&pThread->u32State, VBOXVDMATHREAD_STATE_CREATED);
835
836 if (pfnChanged)
837 pfnChanged(pThread, VINF_SUCCESS, pvThreadContext, pvChanged);
838}
839
840void VBoxVDMAThreadNotifyTerminatingSucceeded(PVBOXVDMATHREAD pThread, void *pvThreadContext)
841{
842 Assert(pThread->u32State == VBOXVDMATHREAD_STATE_TERMINATING);
843 PFNVBOXVDMATHREAD_CHANGED pfnChanged = pThread->pfnChanged;
844 void *pvChanged = pThread->pvChanged;
845
846 pThread->pfnChanged = NULL;
847 pThread->pvChanged = NULL;
848
849 if (pfnChanged)
850 pfnChanged(pThread, VINF_SUCCESS, pvThreadContext, pvChanged);
851}
852
853DECLINLINE(bool) VBoxVDMAThreadIsTerminating(PVBOXVDMATHREAD pThread)
854{
855 return ASMAtomicUoReadU32(&pThread->u32State) == VBOXVDMATHREAD_STATE_TERMINATING;
856}
857
858void VBoxVDMAThreadInit(PVBOXVDMATHREAD pThread)
859{
860 memset(pThread, 0, sizeof (*pThread));
861 pThread->u32State = VBOXVDMATHREAD_STATE_TERMINATED;
862}
863
864int VBoxVDMAThreadCleanup(PVBOXVDMATHREAD pThread)
865{
866 uint32_t u32State = ASMAtomicUoReadU32(&pThread->u32State);
867 switch (u32State)
868 {
869 case VBOXVDMATHREAD_STATE_TERMINATED:
870 return VINF_SUCCESS;
871 case VBOXVDMATHREAD_STATE_TERMINATING:
872 {
873 int rc = RTThreadWait(pThread->hWorkerThread, RT_INDEFINITE_WAIT, NULL);
874 if (!RT_SUCCESS(rc))
875 {
876 WARN(("RTThreadWait failed %d\n", rc));
877 return rc;
878 }
879
880 RTSemEventDestroy(pThread->hEvent);
881
882 ASMAtomicWriteU32(&pThread->u32State, VBOXVDMATHREAD_STATE_TERMINATED);
883 return VINF_SUCCESS;
884 }
885 default:
886 WARN(("invalid state"));
887 return VERR_INVALID_STATE;
888 }
889}
890
891int VBoxVDMAThreadCreate(PVBOXVDMATHREAD pThread, PFNRTTHREAD pfnThread, void *pvThread, PFNVBOXVDMATHREAD_CHANGED pfnCreated, void*pvCreated)
892{
893 int rc = VBoxVDMAThreadCleanup(pThread);
894 if (RT_FAILURE(rc))
895 {
896 WARN(("VBoxVDMAThreadCleanup failed %d\n", rc));
897 return rc;
898 }
899
900 rc = RTSemEventCreate(&pThread->hEvent);
901 if (RT_SUCCESS(rc))
902 {
903 pThread->u32State = VBOXVDMATHREAD_STATE_CREATING;
904 pThread->pfnChanged = pfnCreated;
905 pThread->pvChanged = pvCreated;
906 rc = RTThreadCreate(&pThread->hWorkerThread, pfnThread, pvThread, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "VDMA");
907 if (RT_SUCCESS(rc))
908 return VINF_SUCCESS;
909 else
910 WARN(("RTThreadCreate failed %d\n", rc));
911
912 RTSemEventDestroy(pThread->hEvent);
913 }
914 else
915 WARN(("RTSemEventCreate failed %d\n", rc));
916
917 pThread->u32State = VBOXVDMATHREAD_STATE_TERMINATED;
918
919 return rc;
920}
921
922DECLINLINE(int) VBoxVDMAThreadEventNotify(PVBOXVDMATHREAD pThread)
923{
924 int rc = RTSemEventSignal(pThread->hEvent);
925 AssertRC(rc);
926 return rc;
927}
928
929DECLINLINE(int) VBoxVDMAThreadEventWait(PVBOXVDMATHREAD pThread, RTMSINTERVAL cMillies)
930{
931 int rc = RTSemEventWait(pThread->hEvent, cMillies);
932 AssertRC(rc);
933 return rc;
934}
935
936int VBoxVDMAThreadTerm(PVBOXVDMATHREAD pThread, PFNVBOXVDMATHREAD_CHANGED pfnTerminated, void*pvTerminated, bool fNotify)
937{
938 int rc;
939 do
940 {
941 uint32_t u32State = ASMAtomicUoReadU32(&pThread->u32State);
942 switch (u32State)
943 {
944 case VBOXVDMATHREAD_STATE_CREATED:
945 pThread->pfnChanged = pfnTerminated;
946 pThread->pvChanged = pvTerminated;
947 ASMAtomicWriteU32(&pThread->u32State, VBOXVDMATHREAD_STATE_TERMINATING);
948 if (fNotify)
949 {
950 rc = VBoxVDMAThreadEventNotify(pThread);
951 AssertRC(rc);
952 }
953 return VINF_SUCCESS;
954 case VBOXVDMATHREAD_STATE_TERMINATING:
955 case VBOXVDMATHREAD_STATE_TERMINATED:
956 {
957 WARN(("thread is marked to termination or terminated\nn"));
958 return VERR_INVALID_STATE;
959 }
960 case VBOXVDMATHREAD_STATE_CREATING:
961 {
962 /* wait till the thread creation is completed */
963 WARN(("concurrent thread create/destron\n"));
964 RTThreadYield();
965 continue;
966 }
967 default:
968 WARN(("invalid state"));
969 return VERR_INVALID_STATE;
970 }
971 } while (1);
972
973 WARN(("should never be here\n"));
974 return VERR_INTERNAL_ERROR;
975}
976
977static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource);
978
979typedef DECLCALLBACK(void) FNVBOXVDMACRCTL_CALLBACK(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext);
980typedef FNVBOXVDMACRCTL_CALLBACK *PFNVBOXVDMACRCTL_CALLBACK;
981
982typedef struct VBOXVDMACMD_CHROMIUM_CTL_PRIVATE
983{
984 uint32_t cRefs;
985 int32_t rc;
986 PFNVBOXVDMACRCTL_CALLBACK pfnCompletion;
987 void *pvCompletion;
988 VBOXVDMACMD_CHROMIUM_CTL Cmd;
989} VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, *PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE;
990
991#define VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(_p) ((PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE)(((uint8_t*)(_p)) - RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, Cmd)))
992
993static PVBOXVDMACMD_CHROMIUM_CTL vboxVDMACrCtlCreate(VBOXVDMACMD_CHROMIUM_CTL_TYPE enmCmd, uint32_t cbCmd)
994{
995 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = (PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE)RTMemAllocZ(cbCmd + RT_OFFSETOF(VBOXVDMACMD_CHROMIUM_CTL_PRIVATE, Cmd));
996 Assert(pHdr);
997 if (pHdr)
998 {
999 pHdr->cRefs = 1;
1000 pHdr->rc = VERR_NOT_IMPLEMENTED;
1001 pHdr->Cmd.enmType = enmCmd;
1002 pHdr->Cmd.cbCmd = cbCmd;
1003 return &pHdr->Cmd;
1004 }
1005
1006 return NULL;
1007}
1008
1009DECLINLINE(void) vboxVDMACrCtlRelease (PVBOXVDMACMD_CHROMIUM_CTL pCmd)
1010{
1011 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
1012 uint32_t cRefs = ASMAtomicDecU32(&pHdr->cRefs);
1013 if(!cRefs)
1014 {
1015 RTMemFree(pHdr);
1016 }
1017}
1018
1019DECLINLINE(void) vboxVDMACrCtlRetain (PVBOXVDMACMD_CHROMIUM_CTL pCmd)
1020{
1021 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
1022 ASMAtomicIncU32(&pHdr->cRefs);
1023}
1024
1025DECLINLINE(int) vboxVDMACrCtlGetRc (PVBOXVDMACMD_CHROMIUM_CTL pCmd)
1026{
1027 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
1028 return pHdr->rc;
1029}
1030
1031static DECLCALLBACK(void) vboxVDMACrCtlCbSetEvent(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext)
1032{
1033 RTSemEventSignal((RTSEMEVENT)pvContext);
1034}
1035
1036static DECLCALLBACK(void) vboxVDMACrCtlCbReleaseCmd(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, void* pvContext)
1037{
1038 vboxVDMACrCtlRelease(pCmd);
1039}
1040
1041
1042static int vboxVDMACrCtlPostAsync (PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd, PFNVBOXVDMACRCTL_CALLBACK pfnCompletion, void *pvCompletion)
1043{
1044 if ( pVGAState->pDrv
1045 && pVGAState->pDrv->pfnCrHgsmiControlProcess)
1046 {
1047 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pHdr = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
1048 pHdr->pfnCompletion = pfnCompletion;
1049 pHdr->pvCompletion = pvCompletion;
1050 pVGAState->pDrv->pfnCrHgsmiControlProcess(pVGAState->pDrv, pCmd, cbCmd);
1051 return VINF_SUCCESS;
1052 }
1053#ifdef DEBUG_misha
1054 Assert(0);
1055#endif
1056 return VERR_NOT_SUPPORTED;
1057}
1058
1059static int vboxVDMACrCtlPost(PVGASTATE pVGAState, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd)
1060{
1061 RTSEMEVENT hComplEvent;
1062 int rc = RTSemEventCreate(&hComplEvent);
1063 AssertRC(rc);
1064 if(RT_SUCCESS(rc))
1065 {
1066 rc = vboxVDMACrCtlPostAsync(pVGAState, pCmd, cbCmd, vboxVDMACrCtlCbSetEvent, (void*)hComplEvent);
1067#ifdef DEBUG_misha
1068 AssertRC(rc);
1069#endif
1070 if (RT_SUCCESS(rc))
1071 {
1072 rc = RTSemEventWaitNoResume(hComplEvent, RT_INDEFINITE_WAIT);
1073 AssertRC(rc);
1074 if(RT_SUCCESS(rc))
1075 {
1076 RTSemEventDestroy(hComplEvent);
1077 }
1078 }
1079 else
1080 {
1081 /* the command is completed */
1082 RTSemEventDestroy(hComplEvent);
1083 }
1084 }
1085 return rc;
1086}
1087
1088typedef struct VDMA_VBVA_CTL_CYNC_COMPLETION
1089{
1090 int rc;
1091 RTSEMEVENT hEvent;
1092} VDMA_VBVA_CTL_CYNC_COMPLETION;
1093
1094static DECLCALLBACK(void) vboxVDMACrHgcmSubmitSyncCompletion(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
1095{
1096 VDMA_VBVA_CTL_CYNC_COMPLETION *pData = (VDMA_VBVA_CTL_CYNC_COMPLETION*)pvCompletion;
1097 pData->rc = rc;
1098 rc = RTSemEventSignal(pData->hEvent);
1099 if (!RT_SUCCESS(rc))
1100 WARN(("RTSemEventSignal failed %d\n", rc));
1101}
1102
1103static int vboxVDMACrHgcmSubmitSync(struct VBOXVDMAHOST *pVdma, VBOXCRCMDCTL* pCtl, uint32_t cbCtl)
1104{
1105 VDMA_VBVA_CTL_CYNC_COMPLETION Data;
1106 Data.rc = VERR_NOT_IMPLEMENTED;
1107 int rc = RTSemEventCreate(&Data.hEvent);
1108 if (!RT_SUCCESS(rc))
1109 {
1110 WARN(("RTSemEventCreate failed %d\n", rc));
1111 return rc;
1112 }
1113
1114 pCtl->CalloutList.List.pNext = NULL;
1115
1116 PVGASTATE pVGAState = pVdma->pVGAState;
1117 rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCtl, cbCtl, vboxVDMACrHgcmSubmitSyncCompletion, &Data);
1118 if (RT_SUCCESS(rc))
1119 {
1120 rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT);
1121 if (RT_SUCCESS(rc))
1122 {
1123 rc = Data.rc;
1124 if (!RT_SUCCESS(rc))
1125 {
1126 WARN(("pfnCrHgcmCtlSubmit command failed %d\n", rc));
1127 }
1128
1129 }
1130 else
1131 WARN(("RTSemEventWait failed %d\n", rc));
1132 }
1133 else
1134 WARN(("pfnCrHgcmCtlSubmit failed %d\n", rc));
1135
1136
1137 RTSemEventDestroy(Data.hEvent);
1138
1139 return rc;
1140}
1141
1142static int vdmaVBVACtlDisableSync(PVBOXVDMAHOST pVdma)
1143{
1144 VBVAEXHOSTCTL HCtl;
1145 HCtl.enmType = VBVAEXHOSTCTL_TYPE_GHH_DISABLE;
1146 int rc = vdmaVBVACtlSubmitSync(pVdma, &HCtl, VBVAEXHOSTCTL_SOURCE_HOST);
1147 if (RT_FAILURE(rc))
1148 {
1149 Log(("vdmaVBVACtlSubmitSync failed %d\n", rc));
1150 return rc;
1151 }
1152
1153 vgaUpdateDisplayAll(pVdma->pVGAState, /* fFailOnResize = */ false);
1154
1155 return VINF_SUCCESS;
1156}
1157
1158static DECLCALLBACK(uint8_t*) vboxVDMACrHgcmHandleEnableRemainingHostCommand(HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hClient, uint32_t *pcbCtl, int prevCmdRc)
1159{
1160 struct VBOXVDMAHOST *pVdma = hClient;
1161 if (!pVdma->pCurRemainingHostCtl)
1162 {
1163 /* disable VBVA, all subsequent host commands will go HGCM way */
1164 VBoxVBVAExHSDisable(&pVdma->CmdVbva);
1165 }
1166 else
1167 {
1168 VBoxVBVAExHPDataCompleteCtl(&pVdma->CmdVbva, pVdma->pCurRemainingHostCtl, prevCmdRc);
1169 }
1170
1171 pVdma->pCurRemainingHostCtl = VBoxVBVAExHPCheckHostCtlOnDisable(&pVdma->CmdVbva);
1172 if (pVdma->pCurRemainingHostCtl)
1173 {
1174 *pcbCtl = pVdma->pCurRemainingHostCtl->u.cmd.cbCmd;
1175 return pVdma->pCurRemainingHostCtl->u.cmd.pu8Cmd;
1176 }
1177
1178 *pcbCtl = 0;
1179 return NULL;
1180}
1181
1182static DECLCALLBACK(void) vboxVDMACrHgcmNotifyTerminatingDoneCb(HVBOXCRCMDCTL_NOTIFY_TERMINATING hClient)
1183{
1184 struct VBOXVDMAHOST *pVdma = hClient;
1185 Assert(pVdma->CmdVbva.i32State == VBVAEXHOSTCONTEXT_STATE_PROCESSING);
1186 Assert(pVdma->Thread.u32State == VBOXVDMATHREAD_STATE_TERMINATING);
1187}
1188
1189static DECLCALLBACK(int) vboxVDMACrHgcmNotifyTerminatingCb(HVBOXCRCMDCTL_NOTIFY_TERMINATING hClient, VBOXCRCMDCTL_HGCMENABLE_DATA *pHgcmEnableData)
1190{
1191 struct VBOXVDMAHOST *pVdma = hClient;
1192 VBVAEXHOSTCTL HCtl;
1193 HCtl.enmType = VBVAEXHOSTCTL_TYPE_HH_ON_HGCM_UNLOAD;
1194 int rc = vdmaVBVACtlSubmitSync(pVdma, &HCtl, VBVAEXHOSTCTL_SOURCE_HOST);
1195
1196 pHgcmEnableData->hRHCmd = pVdma;
1197 pHgcmEnableData->pfnRHCmd = vboxVDMACrHgcmHandleEnableRemainingHostCommand;
1198
1199 if (RT_FAILURE(rc))
1200 {
1201 if (rc == VERR_INVALID_STATE)
1202 rc = VINF_SUCCESS;
1203 else
1204 WARN(("vdmaVBVACtlSubmitSync failed %d\n", rc));
1205 }
1206
1207 return rc;
1208}
1209
1210static int vboxVDMACrHgcmHandleEnable(struct VBOXVDMAHOST *pVdma)
1211{
1212 VBOXCRCMDCTL_ENABLE Enable;
1213 Enable.Hdr.enmType = VBOXCRCMDCTL_TYPE_ENABLE;
1214 Enable.Data.hRHCmd = pVdma;
1215 Enable.Data.pfnRHCmd = vboxVDMACrHgcmHandleEnableRemainingHostCommand;
1216
1217 int rc = vboxVDMACrHgcmSubmitSync(pVdma, &Enable.Hdr, sizeof (Enable));
1218 Assert(!pVdma->pCurRemainingHostCtl);
1219 if (RT_SUCCESS(rc))
1220 {
1221 Assert(!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva));
1222 return VINF_SUCCESS;
1223 }
1224
1225 Assert(VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva));
1226 WARN(("vboxVDMACrHgcmSubmitSync failed %d\n", rc));
1227
1228 return rc;
1229}
1230
1231static int vdmaVBVAEnableProcess(struct VBOXVDMAHOST *pVdma, uint32_t u32Offset)
1232{
1233 if (VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1234 {
1235 WARN(("vdma VBVA is already enabled\n"));
1236 return VERR_INVALID_STATE;
1237 }
1238
1239 VBVABUFFER *pVBVA = (VBVABUFFER *)HGSMIOffsetToPointerHost(pVdma->pHgsmi, u32Offset);
1240 if (!pVBVA)
1241 {
1242 WARN(("invalid offset %d\n", u32Offset));
1243 return VERR_INVALID_PARAMETER;
1244 }
1245
1246 if (!pVdma->CrSrvInfo.pfnEnable)
1247 {
1248#ifdef DEBUG_misha
1249 WARN(("pfnEnable is NULL\n"));
1250 return VERR_NOT_SUPPORTED;
1251#endif
1252 }
1253
1254 int rc = VBoxVBVAExHSEnable(&pVdma->CmdVbva, pVBVA);
1255 if (RT_SUCCESS(rc))
1256 {
1257 VBOXCRCMDCTL_DISABLE Disable;
1258 Disable.Hdr.enmType = VBOXCRCMDCTL_TYPE_DISABLE;
1259 Disable.Data.hNotifyTerm = pVdma;
1260 Disable.Data.pfnNotifyTerm = vboxVDMACrHgcmNotifyTerminatingCb;
1261 Disable.Data.pfnNotifyTermDone = vboxVDMACrHgcmNotifyTerminatingDoneCb;
1262 rc = vboxVDMACrHgcmSubmitSync(pVdma, &Disable.Hdr, sizeof (Disable));
1263 if (RT_SUCCESS(rc))
1264 {
1265 PVGASTATE pVGAState = pVdma->pVGAState;
1266 VBOXCRCMD_SVRENABLE_INFO Info;
1267 Info.hCltScr = pVGAState->pDrv;
1268 Info.pfnCltScrUpdateBegin = pVGAState->pDrv->pfnVBVAUpdateBegin;
1269 Info.pfnCltScrUpdateProcess = pVGAState->pDrv->pfnVBVAUpdateProcess;
1270 Info.pfnCltScrUpdateEnd = pVGAState->pDrv->pfnVBVAUpdateEnd;
1271 rc = pVdma->CrSrvInfo.pfnEnable(pVdma->CrSrvInfo.hSvr, &Info);
1272 if (RT_SUCCESS(rc))
1273 return VINF_SUCCESS;
1274 else
1275 WARN(("pfnEnable failed %d\n", rc));
1276
1277 vboxVDMACrHgcmHandleEnable(pVdma);
1278 }
1279 else
1280 WARN(("vboxVDMACrHgcmSubmitSync failed %d\n", rc));
1281
1282 VBoxVBVAExHSDisable(&pVdma->CmdVbva);
1283 }
1284 else
1285 WARN(("VBoxVBVAExHSEnable failed %d\n", rc));
1286
1287 return rc;
1288}
1289
1290static int vdmaVBVADisableProcess(struct VBOXVDMAHOST *pVdma, bool fDoHgcmEnable)
1291{
1292 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1293 {
1294 Log(("vdma VBVA is already disabled\n"));
1295 return VINF_SUCCESS;
1296 }
1297
1298 int rc = pVdma->CrSrvInfo.pfnDisable(pVdma->CrSrvInfo.hSvr);
1299 if (RT_SUCCESS(rc))
1300 {
1301 if (fDoHgcmEnable)
1302 {
1303 PVGASTATE pVGAState = pVdma->pVGAState;
1304
1305 /* disable is a bit tricky
1306 * we need to ensure the host ctl commands do not come out of order
1307 * and do not come over HGCM channel until after it is enabled */
1308 rc = vboxVDMACrHgcmHandleEnable(pVdma);
1309 if (RT_SUCCESS(rc))
1310 {
1311 vdmaVBVANotifyDisable(pVGAState);
1312 return VINF_SUCCESS;
1313 }
1314
1315 VBOXCRCMD_SVRENABLE_INFO Info;
1316 Info.hCltScr = pVGAState->pDrv;
1317 Info.pfnCltScrUpdateBegin = pVGAState->pDrv->pfnVBVAUpdateBegin;
1318 Info.pfnCltScrUpdateProcess = pVGAState->pDrv->pfnVBVAUpdateProcess;
1319 Info.pfnCltScrUpdateEnd = pVGAState->pDrv->pfnVBVAUpdateEnd;
1320 pVdma->CrSrvInfo.pfnEnable(pVdma->CrSrvInfo.hSvr, &Info);
1321 }
1322 }
1323 else
1324 WARN(("pfnDisable failed %d\n", rc));
1325
1326 return rc;
1327}
1328
1329static int vboxVDMACrHostCtlProcess(struct VBOXVDMAHOST *pVdma, VBVAEXHOSTCTL *pCmd, bool *pfContinue)
1330{
1331 *pfContinue = true;
1332
1333 switch (pCmd->enmType)
1334 {
1335 case VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE:
1336 {
1337 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1338 {
1339 WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
1340 return VERR_INVALID_STATE;
1341 }
1342 return pVdma->CrSrvInfo.pfnHostCtl(pVdma->CrSrvInfo.hSvr, pCmd->u.cmd.pu8Cmd, pCmd->u.cmd.cbCmd);
1343 }
1344 case VBVAEXHOSTCTL_TYPE_GHH_DISABLE:
1345 {
1346 int rc = vdmaVBVADisableProcess(pVdma, true);
1347 if (RT_FAILURE(rc))
1348 {
1349 WARN(("vdmaVBVADisableProcess failed %d\n", rc));
1350 return rc;
1351 }
1352
1353 return VBoxVDMAThreadTerm(&pVdma->Thread, NULL, NULL, false);
1354 }
1355 case VBVAEXHOSTCTL_TYPE_HH_ON_HGCM_UNLOAD:
1356 {
1357 int rc = vdmaVBVADisableProcess(pVdma, false);
1358 if (RT_FAILURE(rc))
1359 {
1360 WARN(("vdmaVBVADisableProcess failed %d\n", rc));
1361 return rc;
1362 }
1363
1364 rc = VBoxVDMAThreadTerm(&pVdma->Thread, NULL, NULL, true);
1365 if (RT_FAILURE(rc))
1366 {
1367 WARN(("VBoxVDMAThreadTerm failed %d\n", rc));
1368 return rc;
1369 }
1370
1371 *pfContinue = false;
1372 return VINF_SUCCESS;
1373 }
1374 case VBVAEXHOSTCTL_TYPE_HH_SAVESTATE:
1375 {
1376 PVGASTATE pVGAState = pVdma->pVGAState;
1377 uint8_t * pu8VramBase = pVGAState->vram_ptrR3;
1378 int rc = VBoxVBVAExHSSaveState(&pVdma->CmdVbva, pu8VramBase, pCmd->u.state.pSSM);
1379 if (RT_FAILURE(rc))
1380 {
1381 WARN(("VBoxVBVAExHSSaveState failed %d\n", rc));
1382 return rc;
1383 }
1384 VGA_SAVED_STATE_PUT_MARKER(pCmd->u.state.pSSM, 4);
1385
1386 return pVdma->CrSrvInfo.pfnSaveState(pVdma->CrSrvInfo.hSvr, pCmd->u.state.pSSM);
1387 }
1388 case VBVAEXHOSTCTL_TYPE_HH_LOADSTATE:
1389 {
1390 PVGASTATE pVGAState = pVdma->pVGAState;
1391 uint8_t * pu8VramBase = pVGAState->vram_ptrR3;
1392
1393 int rc = VBoxVBVAExHSLoadState(&pVdma->CmdVbva, pu8VramBase, pCmd->u.state.pSSM, pCmd->u.state.u32Version);
1394 if (RT_FAILURE(rc))
1395 {
1396 WARN(("VBoxVBVAExHSSaveState failed %d\n", rc));
1397 return rc;
1398 }
1399
1400 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pCmd->u.state.pSSM, pCmd->u.state.u32Version, 4);
1401 rc = pVdma->CrSrvInfo.pfnLoadState(pVdma->CrSrvInfo.hSvr, pCmd->u.state.pSSM, pCmd->u.state.u32Version);
1402 if (RT_FAILURE(rc))
1403 {
1404 WARN(("pfnLoadState failed %d\n", rc));
1405 return rc;
1406 }
1407
1408 return VINF_SUCCESS;
1409 }
1410 case VBVAEXHOSTCTL_TYPE_HH_LOADSTATE_DONE:
1411 {
1412 PVGASTATE pVGAState = pVdma->pVGAState;
1413
1414 for (uint32_t i = 0; i < pVGAState->cMonitors; ++i)
1415 {
1416 VBVAINFOSCREEN CurScreen;
1417 VBVAINFOVIEW CurView;
1418
1419 int rc = VBVAGetInfoViewAndScreen(pVGAState, i, &CurView, &CurScreen);
1420 if (RT_FAILURE(rc))
1421 {
1422 WARN(("VBVAGetInfoViewAndScreen failed %d\n", rc));
1423 return rc;
1424 }
1425
1426 rc = VBVAInfoScreen(pVGAState, &CurScreen);
1427 if (RT_FAILURE(rc))
1428 {
1429 WARN(("VBVAInfoScreen failed %d\n", rc));
1430 return rc;
1431 }
1432 }
1433
1434 return VINF_SUCCESS;
1435 }
1436 default:
1437 WARN(("unexpected host ctl type %d\n", pCmd->enmType));
1438 return VERR_INVALID_PARAMETER;
1439 }
1440}
1441
1442static int vboxVDMASetupScreenInfo(PVGASTATE pVGAState, VBVAINFOSCREEN *pScreen)
1443{
1444 const uint32_t u32ViewIndex = pScreen->u32ViewIndex;
1445 const bool fDisabled = RT_BOOL(pScreen->u16Flags & VBVA_SCREEN_F_DISABLED);
1446
1447 if (fDisabled)
1448 {
1449 if ( u32ViewIndex < pVGAState->cMonitors
1450 || u32ViewIndex == UINT32_C(0xFFFFFFFF))
1451 {
1452 RT_ZERO(*pScreen);
1453 pScreen->u32ViewIndex = u32ViewIndex;
1454 pScreen->u16Flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED;
1455 return VINF_SUCCESS;
1456 }
1457 }
1458 else
1459 {
1460 if ( u32ViewIndex < pVGAState->cMonitors
1461 && pScreen->u16BitsPerPixel <= 32
1462 && pScreen->u32Width <= UINT16_MAX
1463 && pScreen->u32Height <= UINT16_MAX
1464 && pScreen->u32LineSize <= UINT16_MAX * 4)
1465 {
1466 const uint32_t u32BytesPerPixel = (pScreen->u16BitsPerPixel + 7) / 8;
1467 if (pScreen->u32Width <= pScreen->u32LineSize / (u32BytesPerPixel? u32BytesPerPixel: 1))
1468 {
1469 const uint64_t u64ScreenSize = (uint64_t)pScreen->u32LineSize * pScreen->u32Height;
1470 if ( pScreen->u32StartOffset <= pVGAState->vram_size
1471 && u64ScreenSize <= pVGAState->vram_size
1472 && pScreen->u32StartOffset <= pVGAState->vram_size - (uint32_t)u64ScreenSize)
1473 {
1474 return VINF_SUCCESS;
1475 }
1476 }
1477 }
1478 }
1479
1480 return VERR_INVALID_PARAMETER;
1481}
1482
1483static int vboxVDMACrGuestCtlResizeEntryProcess(struct VBOXVDMAHOST *pVdma, VBOXCMDVBVA_RESIZE_ENTRY *pEntry)
1484{
1485 PVGASTATE pVGAState = pVdma->pVGAState;
1486 VBVAINFOSCREEN Screen = pEntry->Screen;
1487
1488 /* Verify and cleanup local copy of the input data. */
1489 int rc = vboxVDMASetupScreenInfo(pVGAState, &Screen);
1490 if (RT_FAILURE(rc))
1491 {
1492 WARN(("invalid screen data\n"));
1493 return rc;
1494 }
1495
1496 VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap);
1497 memcpy(aTargetMap, pEntry->aTargetMap, sizeof(aTargetMap));
1498 ASMBitClearRange(aTargetMap, pVGAState->cMonitors, VBOX_VIDEO_MAX_SCREENS);
1499
1500 rc = pVdma->CrSrvInfo.pfnResize(pVdma->CrSrvInfo.hSvr, &Screen, aTargetMap);
1501 if (RT_FAILURE(rc))
1502 {
1503 WARN(("pfnResize failed %d\n", rc));
1504 return rc;
1505 }
1506
1507 /* A fake view which contains the current screen for the 2D VBVAInfoView. */
1508 VBVAINFOVIEW View;
1509 View.u32ViewOffset = 0;
1510 View.u32ViewSize = Screen.u32LineSize * Screen.u32Height + Screen.u32StartOffset;
1511 View.u32MaxScreenSize = Screen.u32LineSize * Screen.u32Height;
1512
1513 const bool fDisable = RT_BOOL(Screen.u16Flags & VBVA_SCREEN_F_DISABLED);
1514
1515 for (int i = ASMBitFirstSet(aTargetMap, pVGAState->cMonitors);
1516 i >= 0;
1517 i = ASMBitNextSet(aTargetMap, pVGAState->cMonitors, i))
1518 {
1519 Screen.u32ViewIndex = i;
1520
1521 VBVAINFOSCREEN CurScreen;
1522 VBVAINFOVIEW CurView;
1523
1524 rc = VBVAGetInfoViewAndScreen(pVGAState, i, &CurView, &CurScreen);
1525 AssertRC(rc);
1526
1527 if (!memcmp(&Screen, &CurScreen, sizeof (CurScreen)))
1528 continue;
1529
1530 if (!fDisable || !CurView.u32ViewSize)
1531 {
1532 View.u32ViewIndex = Screen.u32ViewIndex;
1533
1534 rc = VBVAInfoView(pVGAState, &View);
1535 if (RT_FAILURE(rc))
1536 {
1537 WARN(("VBVAInfoView failed %d\n", rc));
1538 break;
1539 }
1540 }
1541
1542 rc = VBVAInfoScreen(pVGAState, &Screen);
1543 if (RT_FAILURE(rc))
1544 {
1545 WARN(("VBVAInfoScreen failed %d\n", rc));
1546 break;
1547 }
1548 }
1549
1550 return rc;
1551}
1552
1553static int vboxVDMACrGuestCtlProcess(struct VBOXVDMAHOST *pVdma, VBVAEXHOSTCTL *pCmd)
1554{
1555 VBVAEXHOSTCTL_TYPE enmType = pCmd->enmType;
1556 switch (enmType)
1557 {
1558 case VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE:
1559 {
1560 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1561 {
1562 WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
1563 return VERR_INVALID_STATE;
1564 }
1565 return pVdma->CrSrvInfo.pfnGuestCtl(pVdma->CrSrvInfo.hSvr, pCmd->u.cmd.pu8Cmd, pCmd->u.cmd.cbCmd);
1566 }
1567 case VBVAEXHOSTCTL_TYPE_GHH_RESIZE:
1568 {
1569 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
1570 {
1571 WARN(("VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE for disabled vdma VBVA\n"));
1572 return VERR_INVALID_STATE;
1573 }
1574
1575 uint32_t cbCmd = pCmd->u.cmd.cbCmd;
1576
1577 if (cbCmd % sizeof (VBOXCMDVBVA_RESIZE_ENTRY))
1578 {
1579 WARN(("invalid buffer size\n"));
1580 return VERR_INVALID_PARAMETER;
1581 }
1582
1583 uint32_t cElements = cbCmd / sizeof (VBOXCMDVBVA_RESIZE_ENTRY);
1584 if (!cElements)
1585 {
1586 WARN(("invalid buffer size\n"));
1587 return VERR_INVALID_PARAMETER;
1588 }
1589
1590 VBOXCMDVBVA_RESIZE *pResize = (VBOXCMDVBVA_RESIZE*)pCmd->u.cmd.pu8Cmd;
1591
1592 int rc = VINF_SUCCESS;
1593
1594 for (uint32_t i = 0; i < cElements; ++i)
1595 {
1596 VBOXCMDVBVA_RESIZE_ENTRY *pEntry = &pResize->aEntries[i];
1597 rc = vboxVDMACrGuestCtlResizeEntryProcess(pVdma, pEntry);
1598 if (RT_FAILURE(rc))
1599 {
1600 WARN(("vboxVDMACrGuestCtlResizeEntryProcess failed %d\n", rc));
1601 break;
1602 }
1603 }
1604 return rc;
1605 }
1606 case VBVAEXHOSTCTL_TYPE_GHH_ENABLE:
1607 case VBVAEXHOSTCTL_TYPE_GHH_ENABLE_PAUSED:
1608 {
1609 VBVAENABLE *pEnable = (VBVAENABLE *)pCmd->u.cmd.pu8Cmd;
1610 Assert(pCmd->u.cmd.cbCmd == sizeof (VBVAENABLE));
1611 uint32_t u32Offset = pEnable->u32Offset;
1612 int rc = vdmaVBVAEnableProcess(pVdma, u32Offset);
1613 if (!RT_SUCCESS(rc))
1614 {
1615 WARN(("vdmaVBVAEnableProcess failed %d\n", rc));
1616 return rc;
1617 }
1618
1619 if (enmType == VBVAEXHOSTCTL_TYPE_GHH_ENABLE_PAUSED)
1620 {
1621 rc = VBoxVBVAExHPPause(&pVdma->CmdVbva);
1622 if (!RT_SUCCESS(rc))
1623 {
1624 WARN(("VBoxVBVAExHPPause failed %d\n", rc));
1625 return rc;
1626 }
1627 }
1628
1629 return VINF_SUCCESS;
1630 }
1631 case VBVAEXHOSTCTL_TYPE_GHH_DISABLE:
1632 {
1633 int rc = vdmaVBVADisableProcess(pVdma, true);
1634 if (RT_FAILURE(rc))
1635 {
1636 WARN(("vdmaVBVADisableProcess failed %d\n", rc));
1637 return rc;
1638 }
1639
1640 /* do vgaUpdateDisplayAll right away */
1641 VMR3ReqCallNoWait(PDMDevHlpGetVM(pVdma->pVGAState->pDevInsR3), VMCPUID_ANY,
1642 (PFNRT)vgaUpdateDisplayAll, 2, pVdma->pVGAState, /* fFailOnResize = */ false);
1643
1644 return VBoxVDMAThreadTerm(&pVdma->Thread, NULL, NULL, false);
1645 }
1646 default:
1647 WARN(("unexpected ctl type %d\n", pCmd->enmType));
1648 return VERR_INVALID_PARAMETER;
1649 }
1650}
1651
1652/**
1653 * @param fIn - whether this is a page in or out op.
1654 * the direction is VRA#M - related, so fIn == true - transfer to VRAM); false - transfer from VRAM
1655 */
1656static int vboxVDMACrCmdVbvaProcessPagingEl(PPDMDEVINS pDevIns, VBOXCMDVBVAPAGEIDX iPage, uint8_t *pu8Vram, bool fIn)
1657{
1658 RTGCPHYS phPage = (RTGCPHYS)iPage << PAGE_SHIFT;
1659 PGMPAGEMAPLOCK Lock;
1660 int rc;
1661
1662 if (fIn)
1663 {
1664 const void * pvPage;
1665 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvPage, &Lock);
1666 if (!RT_SUCCESS(rc))
1667 {
1668 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d", rc));
1669 return rc;
1670 }
1671
1672 memcpy(pu8Vram, pvPage, PAGE_SIZE);
1673
1674 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1675 }
1676 else
1677 {
1678 void * pvPage;
1679 rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, phPage, 0, &pvPage, &Lock);
1680 if (!RT_SUCCESS(rc))
1681 {
1682 WARN(("PDMDevHlpPhysGCPhys2CCPtr failed %d", rc));
1683 return rc;
1684 }
1685
1686 memcpy(pvPage, pu8Vram, PAGE_SIZE);
1687
1688 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1689 }
1690
1691 return VINF_SUCCESS;
1692}
1693
1694static int vboxVDMACrCmdVbvaProcessPagingEls(PPDMDEVINS pDevIns, const VBOXCMDVBVAPAGEIDX *piPages, uint32_t cPages, uint8_t *pu8Vram, bool fIn)
1695{
1696 for (uint32_t i = 0; i < cPages; ++i, pu8Vram += PAGE_SIZE)
1697 {
1698 int rc = vboxVDMACrCmdVbvaProcessPagingEl(pDevIns, piPages[i], pu8Vram, fIn);
1699 if (!RT_SUCCESS(rc))
1700 {
1701 WARN(("vboxVDMACrCmdVbvaProcessPagingEl failed %d", rc));
1702 return rc;
1703 }
1704 }
1705
1706 return VINF_SUCCESS;
1707}
1708
1709static int8_t vboxVDMACrCmdVbvaPagingDataInit(PVGASTATE pVGAState, const VBOXCMDVBVA_HDR *pHdr, const VBOXCMDVBVA_PAGING_TRANSFER_DATA *pData, uint32_t cbCmd,
1710 const VBOXCMDVBVAPAGEIDX **ppPages, VBOXCMDVBVAPAGEIDX *pcPages,
1711 uint8_t **ppu8Vram, bool *pfIn)
1712{
1713 if (cbCmd < sizeof (VBOXCMDVBVA_PAGING_TRANSFER))
1714 {
1715 WARN(("cmd too small"));
1716 return -1;
1717 }
1718
1719 VBOXCMDVBVAPAGEIDX cPages = cbCmd - RT_OFFSETOF(VBOXCMDVBVA_PAGING_TRANSFER, Data.aPageNumbers);
1720 if (cPages % sizeof (VBOXCMDVBVAPAGEIDX))
1721 {
1722 WARN(("invalid cmd size"));
1723 return -1;
1724 }
1725 cPages /= sizeof (VBOXCMDVBVAPAGEIDX);
1726
1727 VBOXCMDVBVAOFFSET offVRAM = pData->Alloc.u.offVRAM;
1728 if (offVRAM & PAGE_OFFSET_MASK)
1729 {
1730 WARN(("offVRAM address is not on page boundary\n"));
1731 return -1;
1732 }
1733 const VBOXCMDVBVAPAGEIDX *pPages = pData->aPageNumbers;
1734
1735 uint8_t * pu8VramBase = pVGAState->vram_ptrR3;
1736 uint8_t *pu8VramMax = pu8VramBase + pVGAState->vram_size;
1737 if (offVRAM >= pVGAState->vram_size)
1738 {
1739 WARN(("invalid vram offset"));
1740 return -1;
1741 }
1742
1743 if (~(~(VBOXCMDVBVAPAGEIDX)0 >> PAGE_SHIFT) & cPages)
1744 {
1745 WARN(("invalid cPages %d", cPages));
1746 return -1;
1747 }
1748
1749 if (offVRAM + ((VBOXCMDVBVAOFFSET)cPages << PAGE_SHIFT) >= pVGAState->vram_size)
1750 {
1751 WARN(("invalid cPages %d, exceeding vram size", cPages));
1752 return -1;
1753 }
1754
1755 uint8_t *pu8Vram = pu8VramBase + offVRAM;
1756 bool fIn = !!(pHdr->u8Flags & VBOXCMDVBVA_OPF_PAGING_TRANSFER_IN);
1757
1758 *ppPages = pPages;
1759 *pcPages = cPages;
1760 *ppu8Vram = pu8Vram;
1761 *pfIn = fIn;
1762 return 0;
1763}
1764
1765static int8_t vboxVDMACrCmdVbvaPagingFill(PVGASTATE pVGAState, VBOXCMDVBVA_PAGING_FILL *pFill)
1766{
1767 VBOXCMDVBVAOFFSET offVRAM = pFill->offVRAM;
1768 if (offVRAM & PAGE_OFFSET_MASK)
1769 {
1770 WARN(("offVRAM address is not on page boundary\n"));
1771 return -1;
1772 }
1773
1774 uint8_t * pu8VramBase = pVGAState->vram_ptrR3;
1775 uint8_t *pu8VramMax = pu8VramBase + pVGAState->vram_size;
1776 if (offVRAM >= pVGAState->vram_size)
1777 {
1778 WARN(("invalid vram offset"));
1779 return -1;
1780 }
1781
1782 uint32_t cbFill = pFill->u32CbFill;
1783
1784 if (offVRAM + cbFill >= pVGAState->vram_size)
1785 {
1786 WARN(("invalid cPages"));
1787 return -1;
1788 }
1789
1790 uint32_t *pu32Vram = (uint32_t*)(pu8VramBase + offVRAM);
1791 uint32_t u32Color = pFill->u32Pattern;
1792
1793 Assert(!(cbFill % 4));
1794 for (uint32_t i = 0; i < cbFill / 4; ++i)
1795 {
1796 pu32Vram[i] = u32Color;
1797 }
1798
1799 return 0;
1800}
1801
1802static int8_t vboxVDMACrCmdVbvaProcessCmdData(struct VBOXVDMAHOST *pVdma, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd)
1803{
1804 switch (pCmd->u8OpCode)
1805 {
1806 case VBOXCMDVBVA_OPTYPE_NOPCMD:
1807 return 0;
1808 case VBOXCMDVBVA_OPTYPE_PAGING_TRANSFER:
1809 {
1810 PVGASTATE pVGAState = pVdma->pVGAState;
1811 const VBOXCMDVBVAPAGEIDX *pPages;
1812 uint32_t cPages;
1813 uint8_t *pu8Vram;
1814 bool fIn;
1815 int8_t i8Result = vboxVDMACrCmdVbvaPagingDataInit(pVGAState, pCmd, &((VBOXCMDVBVA_PAGING_TRANSFER*)pCmd)->Data, cbCmd,
1816 &pPages, &cPages,
1817 &pu8Vram, &fIn);
1818 if (i8Result < 0)
1819 {
1820 WARN(("vboxVDMACrCmdVbvaPagingDataInit failed %d", i8Result));
1821 return i8Result;
1822 }
1823
1824 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
1825 int rc = vboxVDMACrCmdVbvaProcessPagingEls(pDevIns, pPages, cPages, pu8Vram, fIn);
1826 if (!RT_SUCCESS(rc))
1827 {
1828 WARN(("vboxVDMACrCmdVbvaProcessPagingEls failed %d", rc));
1829 return -1;
1830 }
1831
1832 return 0;
1833 }
1834 case VBOXCMDVBVA_OPTYPE_PAGING_FILL:
1835 {
1836 PVGASTATE pVGAState = pVdma->pVGAState;
1837 if (cbCmd != sizeof (VBOXCMDVBVA_PAGING_FILL))
1838 {
1839 WARN(("cmd too small"));
1840 return -1;
1841 }
1842
1843 return vboxVDMACrCmdVbvaPagingFill(pVGAState, (VBOXCMDVBVA_PAGING_FILL*)pCmd);
1844 }
1845 default:
1846 return pVdma->CrSrvInfo.pfnCmd(pVdma->CrSrvInfo.hSvr, pCmd, cbCmd);
1847 }
1848}
1849
1850#if 0
1851typedef struct VBOXCMDVBVA_PAGING_TRANSFER
1852{
1853 VBOXCMDVBVA_HDR Hdr;
1854 /* for now can only contain offVRAM.
1855 * paging transfer can NOT be initiated for allocations having host 3D object (hostID) associated */
1856 VBOXCMDVBVA_ALLOCINFO Alloc;
1857 uint32_t u32Reserved;
1858 VBOXCMDVBVA_SYSMEMEL aSysMem[1];
1859} VBOXCMDVBVA_PAGING_TRANSFER;
1860#endif
1861
1862AssertCompile(sizeof (VBOXCMDVBVA_HDR) == 8);
1863AssertCompile(sizeof (VBOXCMDVBVA_ALLOCINFO) == 4);
1864AssertCompile(sizeof (VBOXCMDVBVAPAGEIDX) == 4);
1865AssertCompile(!(PAGE_SIZE % sizeof (VBOXCMDVBVAPAGEIDX)));
1866
1867#define VBOXCMDVBVA_NUM_SYSMEMEL_PER_PAGE (PAGE_SIZE / sizeof (VBOXCMDVBVA_SYSMEMEL))
1868
1869static int8_t vboxVDMACrCmdVbvaProcess(struct VBOXVDMAHOST *pVdma, const VBOXCMDVBVA_HDR *pCmd, uint32_t cbCmd)
1870{
1871 switch (pCmd->u8OpCode)
1872 {
1873 case VBOXCMDVBVA_OPTYPE_SYSMEMCMD:
1874 {
1875 if (cbCmd < sizeof (VBOXCMDVBVA_SYSMEMCMD))
1876 {
1877 WARN(("invalid command size"));
1878 return -1;
1879 }
1880 VBOXCMDVBVA_SYSMEMCMD *pSysmemCmd = (VBOXCMDVBVA_SYSMEMCMD*)pCmd;
1881 const VBOXCMDVBVA_HDR *pRealCmdHdr;
1882 uint32_t cbRealCmd = pCmd->u8Flags;
1883 cbRealCmd |= (uint32_t)pCmd->u.u8PrimaryID << 8;
1884 if (cbRealCmd < sizeof (VBOXCMDVBVA_HDR))
1885 {
1886 WARN(("invalid sysmem cmd size"));
1887 return -1;
1888 }
1889
1890 RTGCPHYS phCmd = (RTGCPHYS)pSysmemCmd->phCmd;
1891
1892 PGMPAGEMAPLOCK Lock;
1893 PVGASTATE pVGAState = pVdma->pVGAState;
1894 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
1895 const void * pvCmd;
1896 int rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phCmd, 0, &pvCmd, &Lock);
1897 if (!RT_SUCCESS(rc))
1898 {
1899 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d\n", rc));
1900 return -1;
1901 }
1902
1903 Assert((phCmd & PAGE_OFFSET_MASK) == (((uintptr_t)pvCmd) & PAGE_OFFSET_MASK));
1904
1905 uint32_t cbCmdPart = PAGE_SIZE - (((uintptr_t)pvCmd) & PAGE_OFFSET_MASK);
1906
1907 if (cbRealCmd <= cbCmdPart)
1908 {
1909 pRealCmdHdr = (const VBOXCMDVBVA_HDR *)pvCmd;
1910 uint8_t i8Result = vboxVDMACrCmdVbvaProcessCmdData(pVdma, pRealCmdHdr, cbRealCmd);
1911 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1912 return i8Result;
1913 }
1914
1915 VBOXCMDVBVA_HDR Hdr;
1916 const void *pvCurCmdTail;
1917 uint32_t cbCurCmdTail;
1918 if (cbCmdPart >= sizeof (*pRealCmdHdr))
1919 {
1920 pRealCmdHdr = (const VBOXCMDVBVA_HDR *)pvCmd;
1921 pvCurCmdTail = (const void*)(pRealCmdHdr + 1);
1922 cbCurCmdTail = cbCmdPart - sizeof (*pRealCmdHdr);
1923 }
1924 else
1925 {
1926 memcpy(&Hdr, pvCmd, cbCmdPart);
1927 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1928 phCmd += cbCmdPart;
1929 Assert(!(phCmd & PAGE_OFFSET_MASK));
1930 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phCmd, 0, &pvCmd, &Lock);
1931 if (!RT_SUCCESS(rc))
1932 {
1933 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d\n", rc));
1934 return -1;
1935 }
1936
1937 cbCmdPart = sizeof (*pRealCmdHdr) - cbCmdPart;
1938 memcpy(((uint8_t*)(&Hdr)) + cbCmdPart, pvCmd, cbCmdPart);
1939 pRealCmdHdr = &Hdr;
1940 pvCurCmdTail = (const void*)(((uint8_t*)pvCmd) + cbCmdPart);
1941 cbCurCmdTail = PAGE_SIZE - cbCmdPart;
1942 }
1943
1944 if (cbCurCmdTail > cbRealCmd - sizeof (*pRealCmdHdr))
1945 cbCurCmdTail = cbRealCmd - sizeof (*pRealCmdHdr);
1946
1947 int8_t i8Result = 0;
1948
1949 switch (pRealCmdHdr->u8OpCode)
1950 {
1951 case VBOXCMDVBVA_OPTYPE_PAGING_TRANSFER:
1952 {
1953 const uint32_t *pPages;
1954 uint32_t cPages;
1955 uint8_t *pu8Vram;
1956 bool fIn;
1957 i8Result = vboxVDMACrCmdVbvaPagingDataInit(pVGAState, pRealCmdHdr, (const VBOXCMDVBVA_PAGING_TRANSFER_DATA*)pvCurCmdTail, cbRealCmd,
1958 &pPages, &cPages,
1959 &pu8Vram, &fIn);
1960 if (i8Result < 0)
1961 {
1962 WARN(("vboxVDMACrCmdVbvaPagingDataInit failed %d", i8Result));
1963 /* we need to break, not return, to ensure currently locked page is released */
1964 break;
1965 }
1966
1967 if (cbCurCmdTail & 3)
1968 {
1969 WARN(("command is not alligned properly %d", cbCurCmdTail));
1970 i8Result = -1;
1971 /* we need to break, not return, to ensure currently locked page is released */
1972 break;
1973 }
1974
1975 uint32_t cCurPages = cbCurCmdTail / sizeof (VBOXCMDVBVAPAGEIDX);
1976 Assert(cCurPages < cPages);
1977
1978 do
1979 {
1980 rc = vboxVDMACrCmdVbvaProcessPagingEls(pDevIns, pPages, cCurPages, pu8Vram, fIn);
1981 if (!RT_SUCCESS(rc))
1982 {
1983 WARN(("vboxVDMACrCmdVbvaProcessPagingEls failed %d", rc));
1984 i8Result = -1;
1985 /* we need to break, not return, to ensure currently locked page is released */
1986 break;
1987 }
1988
1989 Assert(cPages >= cCurPages);
1990 cPages -= cCurPages;
1991
1992 if (!cPages)
1993 break;
1994
1995 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
1996
1997 Assert(!(phCmd & PAGE_OFFSET_MASK));
1998
1999 phCmd += PAGE_SIZE;
2000 pu8Vram += (VBOXCMDVBVAOFFSET)cCurPages << PAGE_SHIFT;
2001
2002 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phCmd, 0, &pvCmd, &Lock);
2003 if (!RT_SUCCESS(rc))
2004 {
2005 WARN(("PDMDevHlpPhysGCPhys2CCPtrReadOnly failed %d\n", rc));
2006 /* the page is not locked, return */
2007 return -1;
2008 }
2009
2010 cCurPages = PAGE_SIZE / sizeof (VBOXCMDVBVAPAGEIDX);
2011 if (cCurPages > cPages)
2012 cCurPages = cPages;
2013 } while (1);
2014 break;
2015 }
2016 default:
2017 WARN(("command can not be splitted"));
2018 i8Result = -1;
2019 break;
2020 }
2021
2022 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
2023 return i8Result;
2024 }
2025 case VBOXCMDVBVA_OPTYPE_COMPLEXCMD:
2026 {
2027 Assert(cbCmd >= sizeof (VBOXCMDVBVA_HDR));
2028 ++pCmd;
2029 cbCmd -= sizeof (*pCmd);
2030 uint32_t cbCurCmd = 0;
2031 for ( ; cbCmd; cbCmd -= cbCurCmd, pCmd = (VBOXCMDVBVA_HDR*)(((uint8_t*)pCmd) + cbCurCmd))
2032 {
2033 if (cbCmd < sizeof (VBOXCMDVBVA_HDR))
2034 {
2035 WARN(("invalid command size"));
2036 return -1;
2037 }
2038
2039 cbCurCmd = pCmd->u2.complexCmdEl.u16CbCmdHost;
2040 if (cbCmd < cbCurCmd)
2041 {
2042 WARN(("invalid command size"));
2043 return -1;
2044 }
2045
2046 int8_t i8Result = vboxVDMACrCmdVbvaProcess(pVdma, pCmd, cbCurCmd);
2047 if (i8Result < 0)
2048 {
2049 WARN(("vboxVDMACrCmdVbvaProcess failed"));
2050 return i8Result;
2051 }
2052 }
2053 return 0;
2054 }
2055 default:
2056 return vboxVDMACrCmdVbvaProcessCmdData(pVdma, pCmd, cbCmd);
2057 }
2058}
2059
2060static void vboxVDMACrCmdProcess(struct VBOXVDMAHOST *pVdma, uint8_t* pu8Cmd, uint32_t cbCmd)
2061{
2062 if (*pu8Cmd == VBOXCMDVBVA_OPTYPE_NOP)
2063 return;
2064
2065 if (cbCmd < sizeof (VBOXCMDVBVA_HDR))
2066 {
2067 WARN(("invalid command size"));
2068 return;
2069 }
2070
2071 PVBOXCMDVBVA_HDR pCmd = (PVBOXCMDVBVA_HDR)pu8Cmd;
2072
2073 /* check if the command is cancelled */
2074 if (!ASMAtomicCmpXchgU8(&pCmd->u8State, VBOXCMDVBVA_STATE_IN_PROGRESS, VBOXCMDVBVA_STATE_SUBMITTED))
2075 {
2076 Assert(pCmd->u8State == VBOXCMDVBVA_STATE_CANCELLED);
2077 return;
2078 }
2079
2080 pCmd->u.i8Result = vboxVDMACrCmdVbvaProcess(pVdma, pCmd, cbCmd);
2081}
2082
2083static int vboxVDMACrCtlHgsmiSetup(struct VBOXVDMAHOST *pVdma)
2084{
2085 PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pCmd = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)
2086 vboxVDMACrCtlCreate (VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP, sizeof (*pCmd));
2087 int rc = VERR_NO_MEMORY;
2088 if (pCmd)
2089 {
2090 PVGASTATE pVGAState = pVdma->pVGAState;
2091 pCmd->pvVRamBase = pVGAState->vram_ptrR3;
2092 pCmd->cbVRam = pVGAState->vram_size;
2093 pCmd->pLed = &pVGAState->Led3D;
2094 pCmd->CrClientInfo.hClient = pVdma;
2095 pCmd->CrClientInfo.pfnCallout = vboxCmdVBVACmdCallout;
2096 rc = vboxVDMACrCtlPost(pVGAState, &pCmd->Hdr, sizeof (*pCmd));
2097 if (RT_SUCCESS(rc))
2098 {
2099 rc = vboxVDMACrCtlGetRc(&pCmd->Hdr);
2100 if (RT_SUCCESS(rc))
2101 pVdma->CrSrvInfo = pCmd->CrCmdServerInfo;
2102 else if (rc != VERR_NOT_SUPPORTED)
2103 WARN(("vboxVDMACrCtlGetRc returned %d\n", rc));
2104 }
2105 else
2106 WARN(("vboxVDMACrCtlPost failed %d\n", rc));
2107
2108 vboxVDMACrCtlRelease(&pCmd->Hdr);
2109 }
2110
2111 if (!RT_SUCCESS(rc))
2112 memset(&pVdma->CrSrvInfo, 0, sizeof (pVdma->CrSrvInfo));
2113
2114 return rc;
2115}
2116
2117static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer);
2118
2119/* check if this is external cmd to be passed to chromium backend */
2120static int vboxVDMACmdCheckCrCmd(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmdDr, uint32_t cbCmdDr)
2121{
2122 PVBOXVDMACMD pDmaCmd = NULL;
2123 uint32_t cbDmaCmd = 0;
2124 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
2125 int rc = VINF_NOT_SUPPORTED;
2126
2127 cbDmaCmd = pCmdDr->cbBuf;
2128
2129 if (pCmdDr->fFlags & VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR)
2130 {
2131 if (cbCmdDr < sizeof (*pCmdDr) + VBOXVDMACMD_HEADER_SIZE())
2132 {
2133 AssertMsgFailed(("invalid buffer data!"));
2134 return VERR_INVALID_PARAMETER;
2135 }
2136
2137 if (cbDmaCmd < cbCmdDr - sizeof (*pCmdDr) - VBOXVDMACMD_HEADER_SIZE())
2138 {
2139 AssertMsgFailed(("invalid command buffer data!"));
2140 return VERR_INVALID_PARAMETER;
2141 }
2142
2143 pDmaCmd = VBOXVDMACBUF_DR_TAIL(pCmdDr, VBOXVDMACMD);
2144 }
2145 else if (pCmdDr->fFlags & VBOXVDMACBUF_FLAG_BUF_VRAM_OFFSET)
2146 {
2147 VBOXVIDEOOFFSET offBuf = pCmdDr->Location.offVramBuf;
2148 if (offBuf + cbDmaCmd > pVdma->pVGAState->vram_size)
2149 {
2150 AssertMsgFailed(("invalid command buffer data from offset!"));
2151 return VERR_INVALID_PARAMETER;
2152 }
2153 pDmaCmd = (VBOXVDMACMD*)(pvRam + offBuf);
2154 }
2155
2156 if (pDmaCmd)
2157 {
2158 Assert(cbDmaCmd >= VBOXVDMACMD_HEADER_SIZE());
2159 uint32_t cbBody = VBOXVDMACMD_BODY_SIZE(cbDmaCmd);
2160
2161 switch (pDmaCmd->enmType)
2162 {
2163 case VBOXVDMACMD_TYPE_CHROMIUM_CMD:
2164 {
2165 PVBOXVDMACMD_CHROMIUM_CMD pCrCmd = VBOXVDMACMD_BODY(pDmaCmd, VBOXVDMACMD_CHROMIUM_CMD);
2166 if (cbBody < sizeof (*pCrCmd))
2167 {
2168 AssertMsgFailed(("invalid chromium command buffer size!"));
2169 return VERR_INVALID_PARAMETER;
2170 }
2171 PVGASTATE pVGAState = pVdma->pVGAState;
2172 rc = VINF_SUCCESS;
2173 if (pVGAState->pDrv->pfnCrHgsmiCommandProcess)
2174 {
2175 VBoxSHGSMICommandMarkAsynchCompletion(pCmdDr);
2176 pVGAState->pDrv->pfnCrHgsmiCommandProcess(pVGAState->pDrv, pCrCmd, cbBody);
2177 break;
2178 }
2179 else
2180 {
2181 Assert(0);
2182 }
2183
2184 int tmpRc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmdDr);
2185 AssertRC(tmpRc);
2186 break;
2187 }
2188 case VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER:
2189 {
2190 PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer = VBOXVDMACMD_BODY(pDmaCmd, VBOXVDMACMD_DMA_BPB_TRANSFER);
2191 if (cbBody < sizeof (*pTransfer))
2192 {
2193 AssertMsgFailed(("invalid bpb transfer buffer size!"));
2194 return VERR_INVALID_PARAMETER;
2195 }
2196
2197 rc = vboxVDMACmdExecBpbTransfer(pVdma, pTransfer, sizeof (*pTransfer));
2198 AssertRC(rc);
2199 if (RT_SUCCESS(rc))
2200 {
2201 pCmdDr->rc = VINF_SUCCESS;
2202 rc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmdDr);
2203 AssertRC(rc);
2204 rc = VINF_SUCCESS;
2205 }
2206 break;
2207 }
2208 default:
2209 break;
2210 }
2211 }
2212 return rc;
2213}
2214
2215int vboxVDMACrHgsmiCommandCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, int rc)
2216{
2217 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
2218 PHGSMIINSTANCE pIns = pVGAState->pHGSMI;
2219 VBOXVDMACMD *pDmaHdr = VBOXVDMACMD_FROM_BODY(pCmd);
2220 VBOXVDMACBUF_DR *pDr = VBOXVDMACBUF_DR_FROM_TAIL(pDmaHdr);
2221 AssertRC(rc);
2222 pDr->rc = rc;
2223
2224 Assert(pVGAState->fGuestCaps & VBVACAPS_COMPLETEGCMD_BY_IOREAD);
2225 rc = VBoxSHGSMICommandComplete(pIns, pDr);
2226 AssertRC(rc);
2227 return rc;
2228}
2229
2230int vboxVDMACrHgsmiControlCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd, int rc)
2231{
2232 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
2233 PVBOXVDMACMD_CHROMIUM_CTL_PRIVATE pCmdPrivate = VBOXVDMACMD_CHROMIUM_CTL_PRIVATE_FROM_CTL(pCmd);
2234 pCmdPrivate->rc = rc;
2235 if (pCmdPrivate->pfnCompletion)
2236 {
2237 pCmdPrivate->pfnCompletion(pVGAState, pCmd, pCmdPrivate->pvCompletion);
2238 }
2239 return VINF_SUCCESS;
2240}
2241
2242static int vboxVDMACmdExecBltPerform(PVBOXVDMAHOST pVdma,
2243 uint8_t *pvDstSurf, const uint8_t *pvSrcSurf,
2244 const PVBOXVDMA_SURF_DESC pDstDesc, const PVBOXVDMA_SURF_DESC pSrcDesc,
2245 const VBOXVDMA_RECTL * pDstRectl, const VBOXVDMA_RECTL * pSrcRectl)
2246{
2247 /* we do not support color conversion */
2248 Assert(pDstDesc->format == pSrcDesc->format);
2249 /* we do not support stretching */
2250 Assert(pDstRectl->height == pSrcRectl->height);
2251 Assert(pDstRectl->width == pSrcRectl->width);
2252 if (pDstDesc->format != pSrcDesc->format)
2253 return VERR_INVALID_FUNCTION;
2254 if (pDstDesc->width == pDstRectl->width
2255 && pSrcDesc->width == pSrcRectl->width
2256 && pSrcDesc->width == pDstDesc->width)
2257 {
2258 Assert(!pDstRectl->left);
2259 Assert(!pSrcRectl->left);
2260 uint32_t cbOff = pDstDesc->pitch * pDstRectl->top;
2261 uint32_t cbSize = pDstDesc->pitch * pDstRectl->height;
2262 memcpy(pvDstSurf + cbOff, pvSrcSurf + cbOff, cbSize);
2263 }
2264 else
2265 {
2266 uint32_t offDstLineStart = pDstRectl->left * pDstDesc->bpp >> 3;
2267 uint32_t offDstLineEnd = ((pDstRectl->left * pDstDesc->bpp + 7) >> 3) + ((pDstDesc->bpp * pDstRectl->width + 7) >> 3);
2268 uint32_t cbDstLine = offDstLineEnd - offDstLineStart;
2269 uint32_t offDstStart = pDstDesc->pitch * pDstRectl->top + offDstLineStart;
2270 Assert(cbDstLine <= pDstDesc->pitch);
2271 uint32_t cbDstSkip = pDstDesc->pitch;
2272 uint8_t * pvDstStart = pvDstSurf + offDstStart;
2273
2274 uint32_t offSrcLineStart = pSrcRectl->left * pSrcDesc->bpp >> 3;
2275 uint32_t offSrcLineEnd = ((pSrcRectl->left * pSrcDesc->bpp + 7) >> 3) + ((pSrcDesc->bpp * pSrcRectl->width + 7) >> 3);
2276 uint32_t cbSrcLine = offSrcLineEnd - offSrcLineStart;
2277 uint32_t offSrcStart = pSrcDesc->pitch * pSrcRectl->top + offSrcLineStart;
2278 Assert(cbSrcLine <= pSrcDesc->pitch);
2279 uint32_t cbSrcSkip = pSrcDesc->pitch;
2280 const uint8_t * pvSrcStart = pvSrcSurf + offSrcStart;
2281
2282 Assert(cbDstLine == cbSrcLine);
2283
2284 for (uint32_t i = 0; ; ++i)
2285 {
2286 memcpy (pvDstStart, pvSrcStart, cbDstLine);
2287 if (i == pDstRectl->height)
2288 break;
2289 pvDstStart += cbDstSkip;
2290 pvSrcStart += cbSrcSkip;
2291 }
2292 }
2293 return VINF_SUCCESS;
2294}
2295
2296static void vboxVDMARectlUnite(VBOXVDMA_RECTL * pRectl1, const VBOXVDMA_RECTL * pRectl2)
2297{
2298 if (!pRectl1->width)
2299 *pRectl1 = *pRectl2;
2300 else
2301 {
2302 int16_t x21 = pRectl1->left + pRectl1->width;
2303 int16_t x22 = pRectl2->left + pRectl2->width;
2304 if (pRectl1->left > pRectl2->left)
2305 {
2306 pRectl1->left = pRectl2->left;
2307 pRectl1->width = x21 < x22 ? x22 - pRectl1->left : x21 - pRectl1->left;
2308 }
2309 else if (x21 < x22)
2310 pRectl1->width = x22 - pRectl1->left;
2311
2312 x21 = pRectl1->top + pRectl1->height;
2313 x22 = pRectl2->top + pRectl2->height;
2314 if (pRectl1->top > pRectl2->top)
2315 {
2316 pRectl1->top = pRectl2->top;
2317 pRectl1->height = x21 < x22 ? x22 - pRectl1->top : x21 - pRectl1->top;
2318 }
2319 else if (x21 < x22)
2320 pRectl1->height = x22 - pRectl1->top;
2321 }
2322}
2323
2324/*
2325 * @return on success the number of bytes the command contained, otherwise - VERR_xxx error code
2326 */
2327static int vboxVDMACmdExecBlt(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt, uint32_t cbBuffer)
2328{
2329 const uint32_t cbBlt = VBOXVDMACMD_BODY_FIELD_OFFSET(uint32_t, VBOXVDMACMD_DMA_PRESENT_BLT, aDstSubRects[pBlt->cDstSubRects]);
2330 Assert(cbBlt <= cbBuffer);
2331 if (cbBuffer < cbBlt)
2332 return VERR_INVALID_FUNCTION;
2333
2334 /* we do not support stretching for now */
2335 Assert(pBlt->srcRectl.width == pBlt->dstRectl.width);
2336 Assert(pBlt->srcRectl.height == pBlt->dstRectl.height);
2337 if (pBlt->srcRectl.width != pBlt->dstRectl.width)
2338 return VERR_INVALID_FUNCTION;
2339 if (pBlt->srcRectl.height != pBlt->dstRectl.height)
2340 return VERR_INVALID_FUNCTION;
2341 Assert(pBlt->cDstSubRects);
2342
2343 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
2344 VBOXVDMA_RECTL updateRectl = {0, 0, 0, 0};
2345
2346 if (pBlt->cDstSubRects)
2347 {
2348 VBOXVDMA_RECTL dstRectl, srcRectl;
2349 const VBOXVDMA_RECTL *pDstRectl, *pSrcRectl;
2350 for (uint32_t i = 0; i < pBlt->cDstSubRects; ++i)
2351 {
2352 pDstRectl = &pBlt->aDstSubRects[i];
2353 if (pBlt->dstRectl.left || pBlt->dstRectl.top)
2354 {
2355 dstRectl.left = pDstRectl->left + pBlt->dstRectl.left;
2356 dstRectl.top = pDstRectl->top + pBlt->dstRectl.top;
2357 dstRectl.width = pDstRectl->width;
2358 dstRectl.height = pDstRectl->height;
2359 pDstRectl = &dstRectl;
2360 }
2361
2362 pSrcRectl = &pBlt->aDstSubRects[i];
2363 if (pBlt->srcRectl.left || pBlt->srcRectl.top)
2364 {
2365 srcRectl.left = pSrcRectl->left + pBlt->srcRectl.left;
2366 srcRectl.top = pSrcRectl->top + pBlt->srcRectl.top;
2367 srcRectl.width = pSrcRectl->width;
2368 srcRectl.height = pSrcRectl->height;
2369 pSrcRectl = &srcRectl;
2370 }
2371
2372 int rc = vboxVDMACmdExecBltPerform(pVdma, pvRam + pBlt->offDst, pvRam + pBlt->offSrc,
2373 &pBlt->dstDesc, &pBlt->srcDesc,
2374 pDstRectl,
2375 pSrcRectl);
2376 AssertRC(rc);
2377 if (!RT_SUCCESS(rc))
2378 return rc;
2379
2380 vboxVDMARectlUnite(&updateRectl, pDstRectl);
2381 }
2382 }
2383 else
2384 {
2385 int rc = vboxVDMACmdExecBltPerform(pVdma, pvRam + pBlt->offDst, pvRam + pBlt->offSrc,
2386 &pBlt->dstDesc, &pBlt->srcDesc,
2387 &pBlt->dstRectl,
2388 &pBlt->srcRectl);
2389 AssertRC(rc);
2390 if (!RT_SUCCESS(rc))
2391 return rc;
2392
2393 vboxVDMARectlUnite(&updateRectl, &pBlt->dstRectl);
2394 }
2395
2396 return cbBlt;
2397}
2398
2399static int vboxVDMACmdExecBpbTransfer(PVBOXVDMAHOST pVdma, const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer, uint32_t cbBuffer)
2400{
2401 if (cbBuffer < sizeof (*pTransfer))
2402 return VERR_INVALID_PARAMETER;
2403
2404 PVGASTATE pVGAState = pVdma->pVGAState;
2405 uint8_t * pvRam = pVGAState->vram_ptrR3;
2406 PGMPAGEMAPLOCK SrcLock;
2407 PGMPAGEMAPLOCK DstLock;
2408 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
2409 const void * pvSrc;
2410 void * pvDst;
2411 int rc = VINF_SUCCESS;
2412 uint32_t cbTransfer = pTransfer->cbTransferSize;
2413 uint32_t cbTransfered = 0;
2414 bool bSrcLocked = false;
2415 bool bDstLocked = false;
2416 do
2417 {
2418 uint32_t cbSubTransfer = cbTransfer;
2419 if (pTransfer->fFlags & VBOXVDMACMD_DMA_BPB_TRANSFER_F_SRC_VRAMOFFSET)
2420 {
2421 pvSrc = pvRam + pTransfer->Src.offVramBuf + cbTransfered;
2422 }
2423 else
2424 {
2425 RTGCPHYS phPage = pTransfer->Src.phBuf;
2426 phPage += cbTransfered;
2427 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvSrc, &SrcLock);
2428 AssertRC(rc);
2429 if (RT_SUCCESS(rc))
2430 {
2431 bSrcLocked = true;
2432 cbSubTransfer = RT_MIN(cbSubTransfer, 0x1000);
2433 }
2434 else
2435 {
2436 break;
2437 }
2438 }
2439
2440 if (pTransfer->fFlags & VBOXVDMACMD_DMA_BPB_TRANSFER_F_DST_VRAMOFFSET)
2441 {
2442 pvDst = pvRam + pTransfer->Dst.offVramBuf + cbTransfered;
2443 }
2444 else
2445 {
2446 RTGCPHYS phPage = pTransfer->Dst.phBuf;
2447 phPage += cbTransfered;
2448 rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, phPage, 0, &pvDst, &DstLock);
2449 AssertRC(rc);
2450 if (RT_SUCCESS(rc))
2451 {
2452 bDstLocked = true;
2453 cbSubTransfer = RT_MIN(cbSubTransfer, 0x1000);
2454 }
2455 else
2456 {
2457 break;
2458 }
2459 }
2460
2461 if (RT_SUCCESS(rc))
2462 {
2463 memcpy(pvDst, pvSrc, cbSubTransfer);
2464 cbTransfer -= cbSubTransfer;
2465 cbTransfered += cbSubTransfer;
2466 }
2467 else
2468 {
2469 cbTransfer = 0; /* to break */
2470 }
2471
2472 if (bSrcLocked)
2473 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &SrcLock);
2474 if (bDstLocked)
2475 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &DstLock);
2476 } while (cbTransfer);
2477
2478 if (RT_SUCCESS(rc))
2479 return sizeof (*pTransfer);
2480 return rc;
2481}
2482
2483static int vboxVDMACmdExec(PVBOXVDMAHOST pVdma, const uint8_t *pvBuffer, uint32_t cbBuffer)
2484{
2485 do
2486 {
2487 Assert(pvBuffer);
2488 Assert(cbBuffer >= VBOXVDMACMD_HEADER_SIZE());
2489
2490 if (!pvBuffer)
2491 return VERR_INVALID_PARAMETER;
2492 if (cbBuffer < VBOXVDMACMD_HEADER_SIZE())
2493 return VERR_INVALID_PARAMETER;
2494
2495 PVBOXVDMACMD pCmd = (PVBOXVDMACMD)pvBuffer;
2496 uint32_t cbCmd = 0;
2497 switch (pCmd->enmType)
2498 {
2499 case VBOXVDMACMD_TYPE_CHROMIUM_CMD:
2500 {
2501#ifdef VBOXWDDM_TEST_UHGSMI
2502 static int count = 0;
2503 static uint64_t start, end;
2504 if (count==0)
2505 {
2506 start = RTTimeNanoTS();
2507 }
2508 ++count;
2509 if (count==100000)
2510 {
2511 end = RTTimeNanoTS();
2512 float ems = (end-start)/1000000.f;
2513 LogRel(("100000 calls took %i ms, %i cps\n", (int)ems, (int)(100000.f*1000.f/ems) ));
2514 }
2515#endif
2516 /* todo: post the buffer to chromium */
2517 return VINF_SUCCESS;
2518 }
2519 case VBOXVDMACMD_TYPE_DMA_PRESENT_BLT:
2520 {
2521 const PVBOXVDMACMD_DMA_PRESENT_BLT pBlt = VBOXVDMACMD_BODY(pCmd, VBOXVDMACMD_DMA_PRESENT_BLT);
2522 int cbBlt = vboxVDMACmdExecBlt(pVdma, pBlt, cbBuffer);
2523 Assert(cbBlt >= 0);
2524 Assert((uint32_t)cbBlt <= cbBuffer);
2525 if (cbBlt >= 0)
2526 {
2527 if ((uint32_t)cbBlt == cbBuffer)
2528 return VINF_SUCCESS;
2529 else
2530 {
2531 cbBuffer -= (uint32_t)cbBlt;
2532 pvBuffer -= cbBlt;
2533 }
2534 }
2535 else
2536 return cbBlt; /* error */
2537 break;
2538 }
2539 case VBOXVDMACMD_TYPE_DMA_BPB_TRANSFER:
2540 {
2541 const PVBOXVDMACMD_DMA_BPB_TRANSFER pTransfer = VBOXVDMACMD_BODY(pCmd, VBOXVDMACMD_DMA_BPB_TRANSFER);
2542 int cbTransfer = vboxVDMACmdExecBpbTransfer(pVdma, pTransfer, cbBuffer);
2543 Assert(cbTransfer >= 0);
2544 Assert((uint32_t)cbTransfer <= cbBuffer);
2545 if (cbTransfer >= 0)
2546 {
2547 if ((uint32_t)cbTransfer == cbBuffer)
2548 return VINF_SUCCESS;
2549 else
2550 {
2551 cbBuffer -= (uint32_t)cbTransfer;
2552 pvBuffer -= cbTransfer;
2553 }
2554 }
2555 else
2556 return cbTransfer; /* error */
2557 break;
2558 }
2559 case VBOXVDMACMD_TYPE_DMA_NOP:
2560 return VINF_SUCCESS;
2561 case VBOXVDMACMD_TYPE_CHILD_STATUS_IRQ:
2562 return VINF_SUCCESS;
2563 default:
2564 AssertBreakpoint();
2565 return VERR_INVALID_FUNCTION;
2566 }
2567 } while (1);
2568
2569 /* we should not be here */
2570 AssertBreakpoint();
2571 return VERR_INVALID_STATE;
2572}
2573
2574static DECLCALLBACK(int) vboxVDMAWorkerThread(RTTHREAD ThreadSelf, void *pvUser)
2575{
2576 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvUser;
2577 PVGASTATE pVGAState = pVdma->pVGAState;
2578 VBVAEXHOSTCONTEXT *pCmdVbva = &pVdma->CmdVbva;
2579 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
2580 uint8_t *pCmd;
2581 uint32_t cbCmd;
2582 int rc;
2583
2584 VBoxVDMAThreadNotifyConstructSucceeded(&pVdma->Thread, pvUser);
2585
2586 while (!VBoxVDMAThreadIsTerminating(&pVdma->Thread))
2587 {
2588 VBVAEXHOST_DATA_TYPE enmType = VBoxVBVAExHPDataGet(pCmdVbva, &pCmd, &cbCmd);
2589 switch (enmType)
2590 {
2591 case VBVAEXHOST_DATA_TYPE_CMD:
2592 vboxVDMACrCmdProcess(pVdma, pCmd, cbCmd);
2593 VBoxVBVAExHPDataCompleteCmd(pCmdVbva, cbCmd);
2594 VBVARaiseIrqNoWait(pVGAState, 0);
2595 break;
2596 case VBVAEXHOST_DATA_TYPE_GUESTCTL:
2597 rc = vboxVDMACrGuestCtlProcess(pVdma, (VBVAEXHOSTCTL*)pCmd);
2598 VBoxVBVAExHPDataCompleteCtl(pCmdVbva, (VBVAEXHOSTCTL*)pCmd, rc);
2599 break;
2600 case VBVAEXHOST_DATA_TYPE_HOSTCTL:
2601 {
2602 bool fContinue = true;
2603 rc = vboxVDMACrHostCtlProcess(pVdma, (VBVAEXHOSTCTL*)pCmd, &fContinue);
2604 VBoxVBVAExHPDataCompleteCtl(pCmdVbva, (VBVAEXHOSTCTL*)pCmd, rc);
2605 if (fContinue)
2606 break;
2607 }
2608 case VBVAEXHOST_DATA_TYPE_NO_DATA:
2609 rc = VBoxVDMAThreadEventWait(&pVdma->Thread, RT_INDEFINITE_WAIT);
2610 AssertRC(rc);
2611 break;
2612 default:
2613 WARN(("unexpected type %d\n", enmType));
2614 break;
2615 }
2616 }
2617
2618 VBoxVDMAThreadNotifyTerminatingSucceeded(&pVdma->Thread, pvUser);
2619
2620 return VINF_SUCCESS;
2621}
2622
2623static void vboxVDMACommandProcess(PVBOXVDMAHOST pVdma, PVBOXVDMACBUF_DR pCmd, uint32_t cbCmd)
2624{
2625 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
2626 const uint8_t * pvBuf;
2627 PGMPAGEMAPLOCK Lock;
2628 int rc;
2629 bool bReleaseLocked = false;
2630
2631 do
2632 {
2633 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
2634
2635 if (pCmd->fFlags & VBOXVDMACBUF_FLAG_BUF_FOLLOWS_DR)
2636 pvBuf = VBOXVDMACBUF_DR_TAIL(pCmd, const uint8_t);
2637 else if (pCmd->fFlags & VBOXVDMACBUF_FLAG_BUF_VRAM_OFFSET)
2638 {
2639 uint8_t * pvRam = pVdma->pVGAState->vram_ptrR3;
2640 pvBuf = pvRam + pCmd->Location.offVramBuf;
2641 }
2642 else
2643 {
2644 RTGCPHYS phPage = pCmd->Location.phBuf & ~0xfffULL;
2645 uint32_t offset = pCmd->Location.phBuf & 0xfff;
2646 Assert(offset + pCmd->cbBuf <= 0x1000);
2647 if (offset + pCmd->cbBuf > 0x1000)
2648 {
2649 /* @todo: more advanced mechanism of command buffer proc is actually needed */
2650 rc = VERR_INVALID_PARAMETER;
2651 break;
2652 }
2653
2654 const void * pvPageBuf;
2655 rc = PDMDevHlpPhysGCPhys2CCPtrReadOnly(pDevIns, phPage, 0, &pvPageBuf, &Lock);
2656 AssertRC(rc);
2657 if (!RT_SUCCESS(rc))
2658 {
2659 /* @todo: if (rc == VERR_PGM_PHYS_PAGE_RESERVED) -> fall back on using PGMPhysRead ?? */
2660 break;
2661 }
2662
2663 pvBuf = (const uint8_t *)pvPageBuf;
2664 pvBuf += offset;
2665
2666 bReleaseLocked = true;
2667 }
2668
2669 rc = vboxVDMACmdExec(pVdma, pvBuf, pCmd->cbBuf);
2670 AssertRC(rc);
2671
2672 if (bReleaseLocked)
2673 PDMDevHlpPhysReleasePageMappingLock(pDevIns, &Lock);
2674 } while (0);
2675
2676 pCmd->rc = rc;
2677
2678 rc = VBoxSHGSMICommandComplete (pHgsmi, pCmd);
2679 AssertRC(rc);
2680}
2681
2682static void vboxVDMAControlProcess(PVBOXVDMAHOST pVdma, PVBOXVDMA_CTL pCmd)
2683{
2684 PHGSMIINSTANCE pHgsmi = pVdma->pHgsmi;
2685 pCmd->i32Result = VINF_SUCCESS;
2686 int rc = VBoxSHGSMICommandComplete (pHgsmi, pCmd);
2687 AssertRC(rc);
2688}
2689
2690#endif /* #ifdef VBOX_WITH_CRHGSMI */
2691
2692#ifdef VBOX_VDMA_WITH_WATCHDOG
2693static DECLCALLBACK(void) vboxVDMAWatchDogTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
2694{
2695 VBOXVDMAHOST *pVdma = (VBOXVDMAHOST *)pvUser;
2696 PVGASTATE pVGAState = pVdma->pVGAState;
2697 VBVARaiseIrq(pVGAState, HGSMIHOSTFLAGS_WATCHDOG);
2698}
2699
2700static int vboxVDMAWatchDogCtl(struct VBOXVDMAHOST *pVdma, uint32_t cMillis)
2701{
2702 PPDMDEVINS pDevIns = pVdma->pVGAState->pDevInsR3;
2703 if (cMillis)
2704 TMTimerSetMillies(pVdma->WatchDogTimer, cMillis);
2705 else
2706 TMTimerStop(pVdma->WatchDogTimer);
2707 return VINF_SUCCESS;
2708}
2709#endif
2710
2711int vboxVDMAConstruct(PVGASTATE pVGAState, uint32_t cPipeElements)
2712{
2713 int rc;
2714 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)RTMemAllocZ(sizeof(*pVdma));
2715 Assert(pVdma);
2716 if (pVdma)
2717 {
2718 pVdma->pHgsmi = pVGAState->pHGSMI;
2719 pVdma->pVGAState = pVGAState;
2720
2721#ifdef VBOX_VDMA_WITH_WATCHDOG
2722 rc = PDMDevHlpTMTimerCreate(pVGAState->pDevInsR3, TMCLOCK_REAL, vboxVDMAWatchDogTimer,
2723 pVdma, TMTIMER_FLAGS_NO_CRIT_SECT,
2724 "VDMA WatchDog Timer", &pVdma->WatchDogTimer);
2725 AssertRC(rc);
2726#endif
2727
2728#ifdef VBOX_WITH_CRHGSMI
2729 VBoxVDMAThreadInit(&pVdma->Thread);
2730
2731 rc = RTSemEventMultiCreate(&pVdma->HostCrCtlCompleteEvent);
2732 if (RT_SUCCESS(rc))
2733 {
2734 rc = VBoxVBVAExHSInit(&pVdma->CmdVbva);
2735 if (RT_SUCCESS(rc))
2736 {
2737 rc = RTCritSectInit(&pVdma->CalloutCritSect);
2738 if (RT_SUCCESS(rc))
2739 {
2740 pVGAState->pVdma = pVdma;
2741 int rcIgnored = vboxVDMACrCtlHgsmiSetup(pVdma); NOREF(rcIgnored); /** @todo is this ignoring intentional? */
2742 return VINF_SUCCESS;
2743
2744 RTCritSectDelete(&pVdma->CalloutCritSect);
2745 }
2746 else
2747 WARN(("RTCritSectInit failed %d\n", rc));
2748
2749 VBoxVBVAExHSTerm(&pVdma->CmdVbva);
2750 }
2751 else
2752 WARN(("VBoxVBVAExHSInit failed %d\n", rc));
2753
2754 RTSemEventMultiDestroy(pVdma->HostCrCtlCompleteEvent);
2755 }
2756 else
2757 WARN(("RTSemEventMultiCreate failed %d\n", rc));
2758
2759
2760 RTMemFree(pVdma);
2761#else
2762 pVGAState->pVdma = pVdma;
2763 return VINF_SUCCESS;
2764#endif
2765 }
2766 else
2767 rc = VERR_OUT_OF_RESOURCES;
2768
2769 return rc;
2770}
2771
2772int vboxVDMAReset(struct VBOXVDMAHOST *pVdma)
2773{
2774#ifdef VBOX_WITH_CRHGSMI
2775 vdmaVBVACtlDisableSync(pVdma);
2776#endif
2777 return VINF_SUCCESS;
2778}
2779
2780int vboxVDMADestruct(struct VBOXVDMAHOST *pVdma)
2781{
2782 if (!pVdma)
2783 return VINF_SUCCESS;
2784#ifdef VBOX_WITH_CRHGSMI
2785 vdmaVBVACtlDisableSync(pVdma);
2786 VBoxVDMAThreadCleanup(&pVdma->Thread);
2787 VBoxVBVAExHSTerm(&pVdma->CmdVbva);
2788 RTSemEventMultiDestroy(pVdma->HostCrCtlCompleteEvent);
2789 RTCritSectDelete(&pVdma->CalloutCritSect);
2790#endif
2791 RTMemFree(pVdma);
2792 return VINF_SUCCESS;
2793}
2794
2795void vboxVDMAControl(struct VBOXVDMAHOST *pVdma, PVBOXVDMA_CTL pCmd, uint32_t cbCmd)
2796{
2797 PHGSMIINSTANCE pIns = pVdma->pHgsmi;
2798
2799 switch (pCmd->enmCtl)
2800 {
2801 case VBOXVDMA_CTL_TYPE_ENABLE:
2802 pCmd->i32Result = VINF_SUCCESS;
2803 break;
2804 case VBOXVDMA_CTL_TYPE_DISABLE:
2805 pCmd->i32Result = VINF_SUCCESS;
2806 break;
2807 case VBOXVDMA_CTL_TYPE_FLUSH:
2808 pCmd->i32Result = VINF_SUCCESS;
2809 break;
2810#ifdef VBOX_VDMA_WITH_WATCHDOG
2811 case VBOXVDMA_CTL_TYPE_WATCHDOG:
2812 pCmd->i32Result = vboxVDMAWatchDogCtl(pVdma, pCmd->u32Offset);
2813 break;
2814#endif
2815 default:
2816 WARN(("cmd not supported"));
2817 pCmd->i32Result = VERR_NOT_SUPPORTED;
2818 }
2819
2820 int rc = VBoxSHGSMICommandComplete (pIns, pCmd);
2821 AssertRC(rc);
2822}
2823
2824void vboxVDMACommand(struct VBOXVDMAHOST *pVdma, PVBOXVDMACBUF_DR pCmd, uint32_t cbCmd)
2825{
2826 int rc = VERR_NOT_IMPLEMENTED;
2827
2828#ifdef VBOX_WITH_CRHGSMI
2829 /* chromium commands are processed by crhomium hgcm thread independently from our internal cmd processing pipeline
2830 * this is why we process them specially */
2831 rc = vboxVDMACmdCheckCrCmd(pVdma, pCmd, cbCmd);
2832 if (rc == VINF_SUCCESS)
2833 return;
2834
2835 if (RT_FAILURE(rc))
2836 {
2837 pCmd->rc = rc;
2838 rc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmd);
2839 AssertRC(rc);
2840 return;
2841 }
2842
2843 vboxVDMACommandProcess(pVdma, pCmd, cbCmd);
2844#else
2845 pCmd->rc = rc;
2846 rc = VBoxSHGSMICommandComplete (pVdma->pHgsmi, pCmd);
2847 AssertRC(rc);
2848#endif
2849}
2850
2851/**/
2852#ifdef VBOX_WITH_CRHGSMI
2853
2854static DECLCALLBACK(void) vdmaVBVACtlSubmitSyncCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext);
2855
2856static int vdmaVBVACtlSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
2857{
2858 int rc = VBoxVBVAExHCtlSubmit(&pVdma->CmdVbva, pCtl, enmSource, pfnComplete, pvComplete);
2859 if (RT_SUCCESS(rc))
2860 {
2861 if (rc == VINF_SUCCESS)
2862 return VBoxVDMAThreadEventNotify(&pVdma->Thread);
2863 else
2864 Assert(rc == VINF_ALREADY_INITIALIZED);
2865 }
2866 else
2867 Log(("VBoxVBVAExHCtlSubmit failed %d\n", rc));
2868
2869 return rc;
2870}
2871
2872static DECLCALLBACK(void) vboxCmdVBVACmdCtlGuestCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext)
2873{
2874 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvContext;
2875 VBOXCMDVBVA_CTL *pGCtl = (VBOXCMDVBVA_CTL*)(pCtl->u.cmd.pu8Cmd - sizeof (VBOXCMDVBVA_CTL));
2876 AssertRC(rc);
2877 pGCtl->i32Result = rc;
2878
2879 Assert(pVdma->pVGAState->fGuestCaps & VBVACAPS_COMPLETEGCMD_BY_IOREAD);
2880 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pGCtl);
2881 AssertRC(rc);
2882
2883 VBoxVBVAExHCtlFree(pVbva, pCtl);
2884}
2885
2886static int vdmaVBVACtlGenericSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL_SOURCE enmSource, VBVAEXHOSTCTL_TYPE enmType, uint8_t* pu8Cmd, uint32_t cbCmd, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
2887{
2888 VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, enmType);
2889 if (!pHCtl)
2890 {
2891 WARN(("VBoxVBVAExHCtlCreate failed\n"));
2892 return VERR_NO_MEMORY;
2893 }
2894
2895 pHCtl->u.cmd.pu8Cmd = pu8Cmd;
2896 pHCtl->u.cmd.cbCmd = cbCmd;
2897 int rc = vdmaVBVACtlSubmit(pVdma, pHCtl, enmSource, pfnComplete, pvComplete);
2898 if (RT_FAILURE(rc))
2899 {
2900 VBoxVBVAExHCtlFree(&pVdma->CmdVbva, pHCtl);
2901 Log(("vdmaVBVACtlSubmit failed rc %d\n", rc));
2902 return rc;;
2903 }
2904 return VINF_SUCCESS;
2905}
2906
2907static int vdmaVBVACtlGenericGuestSubmit(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL_TYPE enmType, VBOXCMDVBVA_CTL *pCtl, uint32_t cbCtl)
2908{
2909 Assert(cbCtl >= sizeof (VBOXCMDVBVA_CTL));
2910 VBoxSHGSMICommandMarkAsynchCompletion(pCtl);
2911 int rc = vdmaVBVACtlGenericSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_GUEST, enmType, (uint8_t*)(pCtl+1), cbCtl - sizeof (VBOXCMDVBVA_CTL), vboxCmdVBVACmdCtlGuestCompletion, pVdma);
2912 if (RT_SUCCESS(rc))
2913 return VINF_SUCCESS;
2914
2915 WARN(("vdmaVBVACtlGenericSubmit failed %d\n", rc));
2916 pCtl->i32Result = rc;
2917 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pCtl);
2918 AssertRC(rc);
2919 return VINF_SUCCESS;
2920}
2921
2922static DECLCALLBACK(void) vboxCmdVBVACmdCtlHostCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvCompletion)
2923{
2924 VBOXCRCMDCTL* pVboxCtl = (VBOXCRCMDCTL*)pCtl->u.cmd.pu8Cmd;
2925 if (pVboxCtl->u.pfnInternal)
2926 ((PFNCRCTLCOMPLETION)pVboxCtl->u.pfnInternal)(pVboxCtl, pCtl->u.cmd.cbCmd, rc, pvCompletion);
2927 VBoxVBVAExHCtlFree(pVbva, pCtl);
2928}
2929
2930static int vdmaVBVACtlOpaqueHostSubmit(PVBOXVDMAHOST pVdma, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
2931 PFNCRCTLCOMPLETION pfnCompletion,
2932 void *pvCompletion)
2933{
2934 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
2935 int rc = vdmaVBVACtlGenericSubmit(pVdma, VBVAEXHOSTCTL_SOURCE_HOST, VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE, (uint8_t*)pCmd, cbCmd, vboxCmdVBVACmdCtlHostCompletion, pvCompletion);
2936 if (RT_FAILURE(rc))
2937 {
2938 if (rc == VERR_INVALID_STATE)
2939 {
2940 pCmd->u.pfnInternal = NULL;
2941 PVGASTATE pVGAState = pVdma->pVGAState;
2942 rc = pVGAState->pDrv->pfnCrHgcmCtlSubmit(pVGAState->pDrv, pCmd, cbCmd, pfnCompletion, pvCompletion);
2943 if (!RT_SUCCESS(rc))
2944 WARN(("pfnCrHgsmiControlProcess failed %d\n", rc));
2945
2946 return rc;
2947 }
2948 WARN(("vdmaVBVACtlGenericSubmit failed %d\n", rc));
2949 return rc;
2950 }
2951
2952 return VINF_SUCCESS;
2953}
2954
2955static DECLCALLBACK(int) vdmaVBVANotifyEnable(PVGASTATE pVGAState)
2956{
2957 for (uint32_t i = 0; i < pVGAState->cMonitors; i++)
2958 {
2959 int rc = pVGAState->pDrv->pfnVBVAEnable (pVGAState->pDrv, i, NULL, true);
2960 if (!RT_SUCCESS(rc))
2961 {
2962 WARN(("pfnVBVAEnable failed %d\n", rc));
2963 for (uint32_t j = 0; j < i; j++)
2964 {
2965 pVGAState->pDrv->pfnVBVADisable (pVGAState->pDrv, j);
2966 }
2967
2968 return rc;
2969 }
2970 }
2971 return VINF_SUCCESS;
2972}
2973
2974static DECLCALLBACK(int) vdmaVBVANotifyDisable(PVGASTATE pVGAState)
2975{
2976 for (uint32_t i = 0; i < pVGAState->cMonitors; i++)
2977 {
2978 pVGAState->pDrv->pfnVBVADisable (pVGAState->pDrv, i);
2979 }
2980 return VINF_SUCCESS;
2981}
2982
2983static DECLCALLBACK(void) vdmaVBVACtlThreadCreatedEnable(struct VBOXVDMATHREAD *pThread, int rc, void *pvThreadContext, void *pvContext)
2984{
2985 PVBOXVDMAHOST pVdma = (PVBOXVDMAHOST)pvThreadContext;
2986 VBVAEXHOSTCTL* pHCtl = (VBVAEXHOSTCTL*)pvContext;
2987
2988 if (RT_SUCCESS(rc))
2989 {
2990 rc = vboxVDMACrGuestCtlProcess(pVdma, pHCtl);
2991 /* rc == VINF_SUCCESS would mean the actual state change has occcured */
2992 if (rc == VINF_SUCCESS)
2993 {
2994 /* we need to inform Main about VBVA enable/disable
2995 * main expects notifications to be done from the main thread
2996 * submit it there */
2997 PVGASTATE pVGAState = pVdma->pVGAState;
2998
2999 if (VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
3000 vdmaVBVANotifyEnable(pVGAState);
3001 else
3002 vdmaVBVANotifyDisable(pVGAState);
3003 }
3004 else if (RT_FAILURE(rc))
3005 WARN(("vboxVDMACrGuestCtlProcess failed %d\n", rc));
3006 }
3007 else
3008 WARN(("vdmaVBVACtlThreadCreatedEnable is passed %d\n", rc));
3009
3010 VBoxVBVAExHPDataCompleteCtl(&pVdma->CmdVbva, pHCtl, rc);
3011}
3012
3013static int vdmaVBVACtlEnableSubmitInternal(PVBOXVDMAHOST pVdma, VBVAENABLE *pEnable, bool fPaused, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
3014{
3015 int rc;
3016 VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, fPaused ? VBVAEXHOSTCTL_TYPE_GHH_ENABLE_PAUSED : VBVAEXHOSTCTL_TYPE_GHH_ENABLE);
3017 if (pHCtl)
3018 {
3019 pHCtl->u.cmd.pu8Cmd = (uint8_t*)pEnable;
3020 pHCtl->u.cmd.cbCmd = sizeof (*pEnable);
3021 pHCtl->pfnComplete = pfnComplete;
3022 pHCtl->pvComplete = pvComplete;
3023
3024 rc = VBoxVDMAThreadCreate(&pVdma->Thread, vboxVDMAWorkerThread, pVdma, vdmaVBVACtlThreadCreatedEnable, pHCtl);
3025 if (RT_SUCCESS(rc))
3026 return VINF_SUCCESS;
3027 else
3028 WARN(("VBoxVDMAThreadCreate failed %d\n", rc));
3029
3030 VBoxVBVAExHCtlFree(&pVdma->CmdVbva, pHCtl);
3031 }
3032 else
3033 {
3034 WARN(("VBoxVBVAExHCtlCreate failed\n"));
3035 rc = VERR_NO_MEMORY;
3036 }
3037
3038 return rc;
3039}
3040
3041static int vdmaVBVACtlEnableSubmitSync(PVBOXVDMAHOST pVdma, uint32_t offVram, bool fPaused)
3042{
3043 VBVAENABLE Enable = {0};
3044 Enable.u32Flags = VBVA_F_ENABLE;
3045 Enable.u32Offset = offVram;
3046
3047 VDMA_VBVA_CTL_CYNC_COMPLETION Data;
3048 Data.rc = VERR_NOT_IMPLEMENTED;
3049 int rc = RTSemEventCreate(&Data.hEvent);
3050 if (!RT_SUCCESS(rc))
3051 {
3052 WARN(("RTSemEventCreate failed %d\n", rc));
3053 return rc;
3054 }
3055
3056 rc = vdmaVBVACtlEnableSubmitInternal(pVdma, &Enable, fPaused, vdmaVBVACtlSubmitSyncCompletion, &Data);
3057 if (RT_SUCCESS(rc))
3058 {
3059 rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT);
3060 if (RT_SUCCESS(rc))
3061 {
3062 rc = Data.rc;
3063 if (!RT_SUCCESS(rc))
3064 WARN(("vdmaVBVACtlSubmitSyncCompletion returned %d\n", rc));
3065 }
3066 else
3067 WARN(("RTSemEventWait failed %d\n", rc));
3068 }
3069 else
3070 WARN(("vdmaVBVACtlSubmit failed %d\n", rc));
3071
3072 RTSemEventDestroy(Data.hEvent);
3073
3074 return rc;
3075}
3076
3077static int vdmaVBVACtlDisableSubmitInternal(PVBOXVDMAHOST pVdma, VBVAENABLE *pEnable, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
3078{
3079 int rc;
3080 VBVAEXHOSTCTL* pHCtl;
3081 if (VBoxVBVAExHSIsDisabled(&pVdma->CmdVbva))
3082 {
3083 WARN(("VBoxVBVAExHSIsDisabled: disabled"));
3084 return VINF_SUCCESS;
3085 }
3086
3087 pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, VBVAEXHOSTCTL_TYPE_GHH_DISABLE);
3088 if (!pHCtl)
3089 {
3090 WARN(("VBoxVBVAExHCtlCreate failed\n"));
3091 return VERR_NO_MEMORY;
3092 }
3093
3094 pHCtl->u.cmd.pu8Cmd = (uint8_t*)pEnable;
3095 pHCtl->u.cmd.cbCmd = sizeof (*pEnable);
3096 rc = vdmaVBVACtlSubmit(pVdma, pHCtl, VBVAEXHOSTCTL_SOURCE_GUEST, pfnComplete, pvComplete);
3097 if (RT_SUCCESS(rc))
3098 return VINF_SUCCESS;
3099
3100 WARN(("vdmaVBVACtlSubmit failed rc %d\n", rc));
3101 VBoxVBVAExHCtlFree(&pVdma->CmdVbva, pHCtl);
3102 return rc;
3103}
3104
3105static int vdmaVBVACtlEnableDisableSubmitInternal(PVBOXVDMAHOST pVdma, VBVAENABLE *pEnable, PFNVBVAEXHOSTCTL_COMPLETE pfnComplete, void *pvComplete)
3106{
3107 bool fEnable = ((pEnable->u32Flags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_ENABLE);
3108 if (fEnable)
3109 return vdmaVBVACtlEnableSubmitInternal(pVdma, pEnable, false, pfnComplete, pvComplete);
3110 return vdmaVBVACtlDisableSubmitInternal(pVdma, pEnable, pfnComplete, pvComplete);
3111}
3112
3113static int vdmaVBVACtlEnableDisableSubmit(PVBOXVDMAHOST pVdma, VBOXCMDVBVA_CTL_ENABLE *pEnable)
3114{
3115 VBoxSHGSMICommandMarkAsynchCompletion(&pEnable->Hdr);
3116 int rc = vdmaVBVACtlEnableDisableSubmitInternal(pVdma, &pEnable->Enable, vboxCmdVBVACmdCtlGuestCompletion, pVdma);
3117 if (RT_SUCCESS(rc))
3118 return VINF_SUCCESS;
3119
3120 WARN(("vdmaVBVACtlEnableDisableSubmitInternal failed %d\n", rc));
3121 pEnable->Hdr.i32Result = rc;
3122 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, &pEnable->Hdr);
3123 AssertRC(rc);
3124 return VINF_SUCCESS;
3125}
3126
3127static DECLCALLBACK(void) vdmaVBVACtlSubmitSyncCompletion(VBVAEXHOSTCONTEXT *pVbva, struct VBVAEXHOSTCTL *pCtl, int rc, void *pvContext)
3128{
3129 VDMA_VBVA_CTL_CYNC_COMPLETION *pData = (VDMA_VBVA_CTL_CYNC_COMPLETION*)pvContext;
3130 pData->rc = rc;
3131 rc = RTSemEventSignal(pData->hEvent);
3132 if (!RT_SUCCESS(rc))
3133 WARN(("RTSemEventSignal failed %d\n", rc));
3134}
3135
3136static int vdmaVBVACtlSubmitSync(PVBOXVDMAHOST pVdma, VBVAEXHOSTCTL* pCtl, VBVAEXHOSTCTL_SOURCE enmSource)
3137{
3138 VDMA_VBVA_CTL_CYNC_COMPLETION Data;
3139 Data.rc = VERR_NOT_IMPLEMENTED;
3140 int rc = RTSemEventCreate(&Data.hEvent);
3141 if (!RT_SUCCESS(rc))
3142 {
3143 WARN(("RTSemEventCreate failed %d\n", rc));
3144 return rc;
3145 }
3146
3147 rc = vdmaVBVACtlSubmit(pVdma, pCtl, enmSource, vdmaVBVACtlSubmitSyncCompletion, &Data);
3148 if (RT_SUCCESS(rc))
3149 {
3150 rc = RTSemEventWait(Data.hEvent, RT_INDEFINITE_WAIT);
3151 if (RT_SUCCESS(rc))
3152 {
3153 rc = Data.rc;
3154 if (!RT_SUCCESS(rc))
3155 WARN(("vdmaVBVACtlSubmitSyncCompletion returned %d\n", rc));
3156 }
3157 else
3158 WARN(("RTSemEventWait failed %d\n", rc));
3159 }
3160 else
3161 Log(("vdmaVBVACtlSubmit failed %d\n", rc));
3162
3163 RTSemEventDestroy(Data.hEvent);
3164
3165 return rc;
3166}
3167
3168static int vdmaVBVAPause(PVBOXVDMAHOST pVdma)
3169{
3170 VBVAEXHOSTCTL Ctl;
3171 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_INTERNAL_PAUSE;
3172 return vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST);
3173}
3174
3175static int vdmaVBVAResume(PVBOXVDMAHOST pVdma)
3176{
3177 VBVAEXHOSTCTL Ctl;
3178 Ctl.enmType = VBVAEXHOSTCTL_TYPE_HH_INTERNAL_RESUME;
3179 return vdmaVBVACtlSubmitSync(pVdma, &Ctl, VBVAEXHOSTCTL_SOURCE_HOST);
3180}
3181
3182static int vboxVDMACmdSubmitPerform(struct VBOXVDMAHOST *pVdma)
3183{
3184 int rc = VBoxVBVAExHSCheckCommands(&pVdma->CmdVbva);
3185 switch (rc)
3186 {
3187 case VINF_SUCCESS:
3188 return VBoxVDMAThreadEventNotify(&pVdma->Thread);
3189 case VINF_ALREADY_INITIALIZED:
3190 case VINF_EOF:
3191 case VERR_INVALID_STATE:
3192 return VINF_SUCCESS;
3193 default:
3194 Assert(!RT_FAILURE(rc));
3195 return RT_FAILURE(rc) ? rc : VERR_INTERNAL_ERROR;
3196 }
3197}
3198
3199
3200int vboxCmdVBVACmdHostCtl(PPDMIDISPLAYVBVACALLBACKS pInterface,
3201 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3202 PFNCRCTLCOMPLETION pfnCompletion,
3203 void *pvCompletion)
3204{
3205 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
3206 struct VBOXVDMAHOST *pVdma = pVGAState->pVdma;
3207 if (pVdma == NULL)
3208 return VERR_INVALID_STATE;
3209 pCmd->CalloutList.List.pNext = NULL;
3210 return vdmaVBVACtlOpaqueHostSubmit(pVdma, pCmd, cbCmd, pfnCompletion, pvCompletion);
3211}
3212
3213typedef struct VBOXCMDVBVA_CMDHOSTCTL_SYNC
3214{
3215 struct VBOXVDMAHOST *pVdma;
3216 uint32_t fProcessing;
3217 int rc;
3218} VBOXCMDVBVA_CMDHOSTCTL_SYNC;
3219
3220static DECLCALLBACK(void) vboxCmdVBVACmdHostCtlSyncCb(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
3221{
3222 VBOXCMDVBVA_CMDHOSTCTL_SYNC *pData = (VBOXCMDVBVA_CMDHOSTCTL_SYNC*)pvCompletion;
3223
3224 pData->rc = rc;
3225
3226 struct VBOXVDMAHOST *pVdma = pData->pVdma;
3227
3228 ASMAtomicIncS32(&pVdma->i32cHostCrCtlCompleted);
3229
3230 pData->fProcessing = 0;
3231
3232 RTSemEventMultiSignal(pVdma->HostCrCtlCompleteEvent);
3233}
3234
3235static DECLCALLBACK(int) vboxCmdVBVACmdCallout(struct VBOXVDMAHOST *pVdma, struct VBOXCRCMDCTL* pCmd, VBOXCRCMDCTL_CALLOUT_LISTENTRY *pEntry, PFNVBOXCRCMDCTL_CALLOUT_CB pfnCb)
3236{
3237 pEntry->pfnCb = pfnCb;
3238 int rc = RTCritSectEnter(&pVdma->CalloutCritSect);
3239 if (RT_SUCCESS(rc))
3240 {
3241 RTListAppend(&pCmd->CalloutList.List, &pEntry->Node);
3242 RTCritSectLeave(&pVdma->CalloutCritSect);
3243
3244 RTSemEventMultiSignal(pVdma->HostCrCtlCompleteEvent);
3245 }
3246 else
3247 WARN(("RTCritSectEnter failed %d\n", rc));
3248
3249 return rc;
3250}
3251
3252
3253static int vboxCmdVBVACmdCalloutProcess(struct VBOXVDMAHOST *pVdma, struct VBOXCRCMDCTL* pCmd)
3254{
3255 int rc = VINF_SUCCESS;
3256 for(;;)
3257 {
3258 rc = RTCritSectEnter(&pVdma->CalloutCritSect);
3259 if (RT_SUCCESS(rc))
3260 {
3261 VBOXCRCMDCTL_CALLOUT_LISTENTRY* pEntry = RTListGetFirst(&pCmd->CalloutList.List, VBOXCRCMDCTL_CALLOUT_LISTENTRY, Node);
3262 if (pEntry)
3263 RTListNodeRemove(&pEntry->Node);
3264 RTCritSectLeave(&pVdma->CalloutCritSect);
3265
3266 if (!pEntry)
3267 break;
3268
3269 pEntry->pfnCb(pEntry);
3270 }
3271 else
3272 {
3273 WARN(("RTCritSectEnter failed %d\n", rc));
3274 break;
3275 }
3276 }
3277
3278 return rc;
3279}
3280
3281DECLCALLBACK(int) vboxCmdVBVACmdHostCtlSync(PPDMIDISPLAYVBVACALLBACKS pInterface,
3282 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3283{
3284 PVGASTATE pVGAState = PPDMIDISPLAYVBVACALLBACKS_2_PVGASTATE(pInterface);
3285 struct VBOXVDMAHOST *pVdma = pVGAState->pVdma;
3286 if (pVdma == NULL)
3287 return VERR_INVALID_STATE;
3288 VBOXCMDVBVA_CMDHOSTCTL_SYNC Data;
3289 Data.pVdma = pVdma;
3290 Data.fProcessing = 1;
3291 Data.rc = VERR_INTERNAL_ERROR;
3292 RTListInit(&pCmd->CalloutList.List);
3293 int rc = vdmaVBVACtlOpaqueHostSubmit(pVdma, pCmd, cbCmd, vboxCmdVBVACmdHostCtlSyncCb, &Data);
3294 if (!RT_SUCCESS(rc))
3295 {
3296 WARN(("vdmaVBVACtlOpaqueHostSubmit failed %d", rc));
3297 return rc;
3298 }
3299
3300 while (Data.fProcessing)
3301 {
3302 /* Poll infrequently to make sure no completed message has been missed. */
3303 RTSemEventMultiWait(pVdma->HostCrCtlCompleteEvent, 500);
3304
3305 vboxCmdVBVACmdCalloutProcess(pVdma, pCmd);
3306
3307 if (Data.fProcessing)
3308 RTThreadYield();
3309 }
3310
3311 /* extra check callouts */
3312 vboxCmdVBVACmdCalloutProcess(pVdma, pCmd);
3313
3314 /* 'Our' message has been processed, so should reset the semaphore.
3315 * There is still possible that another message has been processed
3316 * and the semaphore has been signalled again.
3317 * Reset only if there are no other messages completed.
3318 */
3319 int32_t c = ASMAtomicDecS32(&pVdma->i32cHostCrCtlCompleted);
3320 Assert(c >= 0);
3321 if (!c)
3322 RTSemEventMultiReset(pVdma->HostCrCtlCompleteEvent);
3323
3324 rc = Data.rc;
3325 if (!RT_SUCCESS(rc))
3326 WARN(("host call failed %d", rc));
3327
3328 return rc;
3329}
3330
3331int vboxCmdVBVACmdCtl(PVGASTATE pVGAState, VBOXCMDVBVA_CTL *pCtl, uint32_t cbCtl)
3332{
3333 struct VBOXVDMAHOST *pVdma = pVGAState->pVdma;
3334 int rc = VINF_SUCCESS;
3335 switch (pCtl->u32Type)
3336 {
3337 case VBOXCMDVBVACTL_TYPE_3DCTL:
3338 return vdmaVBVACtlGenericGuestSubmit(pVdma, VBVAEXHOSTCTL_TYPE_GHH_BE_OPAQUE, pCtl, cbCtl);
3339 case VBOXCMDVBVACTL_TYPE_RESIZE:
3340 return vdmaVBVACtlGenericGuestSubmit(pVdma, VBVAEXHOSTCTL_TYPE_GHH_RESIZE, pCtl, cbCtl);
3341 case VBOXCMDVBVACTL_TYPE_ENABLE:
3342 if (cbCtl != sizeof (VBOXCMDVBVA_CTL_ENABLE))
3343 {
3344 WARN(("incorrect enable size\n"));
3345 rc = VERR_INVALID_PARAMETER;
3346 break;
3347 }
3348 return vdmaVBVACtlEnableDisableSubmit(pVdma, (VBOXCMDVBVA_CTL_ENABLE*)pCtl);
3349 default:
3350 WARN(("unsupported type\n"));
3351 rc = VERR_INVALID_PARAMETER;
3352 break;
3353 }
3354
3355 pCtl->i32Result = rc;
3356 rc = VBoxSHGSMICommandComplete(pVdma->pHgsmi, pCtl);
3357 AssertRC(rc);
3358 return VINF_SUCCESS;
3359}
3360
3361int vboxCmdVBVACmdSubmit(PVGASTATE pVGAState)
3362{
3363 if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva))
3364 {
3365 WARN(("vdma VBVA is disabled\n"));
3366 return VERR_INVALID_STATE;
3367 }
3368
3369 return vboxVDMACmdSubmitPerform(pVGAState->pVdma);
3370}
3371
3372int vboxCmdVBVACmdFlush(PVGASTATE pVGAState)
3373{
3374 WARN(("flush\n"));
3375 if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva))
3376 {
3377 WARN(("vdma VBVA is disabled\n"));
3378 return VERR_INVALID_STATE;
3379 }
3380 return vboxVDMACmdSubmitPerform(pVGAState->pVdma);
3381}
3382
3383void vboxCmdVBVACmdTimer(PVGASTATE pVGAState)
3384{
3385 if (!VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva))
3386 return;
3387 vboxVDMACmdSubmitPerform(pVGAState->pVdma);
3388}
3389
3390bool vboxCmdVBVAIsEnabled(PVGASTATE pVGAState)
3391{
3392 return VBoxVBVAExHSIsEnabled(&pVGAState->pVdma->CmdVbva);
3393}
3394#endif
3395
3396int vboxVDMASaveStateExecPrep(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM)
3397{
3398#ifdef VBOX_WITH_CRHGSMI
3399 int rc = vdmaVBVAPause(pVdma);
3400 if (RT_SUCCESS(rc))
3401 return VINF_SUCCESS;
3402
3403 if (rc != VERR_INVALID_STATE)
3404 {
3405 WARN(("vdmaVBVAPause failed %d\n", rc));
3406 return rc;
3407 }
3408
3409#ifdef DEBUG_misha
3410 WARN(("debug prep"));
3411#endif
3412
3413 PVGASTATE pVGAState = pVdma->pVGAState;
3414 PVBOXVDMACMD_CHROMIUM_CTL pCmd = (PVBOXVDMACMD_CHROMIUM_CTL)vboxVDMACrCtlCreate(
3415 VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN, sizeof (*pCmd));
3416 Assert(pCmd);
3417 if (pCmd)
3418 {
3419 rc = vboxVDMACrCtlPost(pVGAState, pCmd, sizeof (*pCmd));
3420 AssertRC(rc);
3421 if (RT_SUCCESS(rc))
3422 {
3423 rc = vboxVDMACrCtlGetRc(pCmd);
3424 }
3425 vboxVDMACrCtlRelease(pCmd);
3426 return rc;
3427 }
3428 return VERR_NO_MEMORY;
3429#else
3430 return VINF_SUCCESS;
3431#endif
3432}
3433
3434int vboxVDMASaveStateExecDone(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM)
3435{
3436#ifdef VBOX_WITH_CRHGSMI
3437 int rc = vdmaVBVAResume(pVdma);
3438 if (RT_SUCCESS(rc))
3439 return VINF_SUCCESS;
3440
3441 if (rc != VERR_INVALID_STATE)
3442 {
3443 WARN(("vdmaVBVAResume failed %d\n", rc));
3444 return rc;
3445 }
3446
3447#ifdef DEBUG_misha
3448 WARN(("debug done"));
3449#endif
3450
3451 PVGASTATE pVGAState = pVdma->pVGAState;
3452 PVBOXVDMACMD_CHROMIUM_CTL pCmd = (PVBOXVDMACMD_CHROMIUM_CTL)vboxVDMACrCtlCreate(
3453 VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END, sizeof (*pCmd));
3454 Assert(pCmd);
3455 if (pCmd)
3456 {
3457 rc = vboxVDMACrCtlPost(pVGAState, pCmd, sizeof (*pCmd));
3458 AssertRC(rc);
3459 if (RT_SUCCESS(rc))
3460 {
3461 rc = vboxVDMACrCtlGetRc(pCmd);
3462 }
3463 vboxVDMACrCtlRelease(pCmd);
3464 return rc;
3465 }
3466 return VERR_NO_MEMORY;
3467#else
3468 return VINF_SUCCESS;
3469#endif
3470}
3471
3472int vboxVDMASaveStateExecPerform(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM)
3473{
3474 int rc;
3475
3476#ifdef VBOX_WITH_CRHGSMI
3477 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
3478#endif
3479 {
3480 rc = SSMR3PutU32(pSSM, 0xffffffff);
3481 AssertRCReturn(rc, rc);
3482 return VINF_SUCCESS;
3483 }
3484
3485#ifdef VBOX_WITH_CRHGSMI
3486 PVGASTATE pVGAState = pVdma->pVGAState;
3487 uint8_t * pu8VramBase = pVGAState->vram_ptrR3;
3488
3489 rc = SSMR3PutU32(pSSM, (uint32_t)(((uint8_t*)pVdma->CmdVbva.pVBVA) - pu8VramBase));
3490 AssertRCReturn(rc, rc);
3491
3492 VBVAEXHOSTCTL HCtl;
3493 HCtl.enmType = VBVAEXHOSTCTL_TYPE_HH_SAVESTATE;
3494 HCtl.u.state.pSSM = pSSM;
3495 HCtl.u.state.u32Version = 0;
3496 return vdmaVBVACtlSubmitSync(pVdma, &HCtl, VBVAEXHOSTCTL_SOURCE_HOST);
3497#endif
3498}
3499
3500int vboxVDMASaveLoadExecPerform(struct VBOXVDMAHOST *pVdma, PSSMHANDLE pSSM, uint32_t u32Version)
3501{
3502 uint32_t u32;
3503 int rc = SSMR3GetU32(pSSM, &u32);
3504 AssertLogRelRCReturn(rc, rc);
3505
3506 if (u32 != 0xffffffff)
3507 {
3508#ifdef VBOX_WITH_CRHGSMI
3509 rc = vdmaVBVACtlEnableSubmitSync(pVdma, u32, true);
3510 AssertLogRelRCReturn(rc, rc);
3511
3512 Assert(pVdma->CmdVbva.i32State == VBVAEXHOSTCONTEXT_ESTATE_PAUSED);
3513
3514 VBVAEXHOSTCTL HCtl;
3515 HCtl.enmType = VBVAEXHOSTCTL_TYPE_HH_LOADSTATE;
3516 HCtl.u.state.pSSM = pSSM;
3517 HCtl.u.state.u32Version = u32Version;
3518 rc = vdmaVBVACtlSubmitSync(pVdma, &HCtl, VBVAEXHOSTCTL_SOURCE_HOST);
3519 AssertLogRelRCReturn(rc, rc);
3520
3521 rc = vdmaVBVAResume(pVdma);
3522 AssertLogRelRCReturn(rc, rc);
3523
3524 return VINF_SUCCESS;
3525#else
3526 WARN(("Unsupported VBVACtl info!\n"));
3527 return VERR_VERSION_MISMATCH;
3528#endif
3529 }
3530
3531 return VINF_SUCCESS;
3532}
3533
3534int vboxVDMASaveLoadDone(struct VBOXVDMAHOST *pVdma)
3535{
3536#ifdef VBOX_WITH_CRHGSMI
3537 if (!VBoxVBVAExHSIsEnabled(&pVdma->CmdVbva))
3538 return VINF_SUCCESS;
3539
3540/** @todo r=bird: BTW. would be great if you put in a couple of comments here and there explaining what
3541 * the purpose of this code is. */
3542 VBVAEXHOSTCTL* pHCtl = VBoxVBVAExHCtlCreate(&pVdma->CmdVbva, VBVAEXHOSTCTL_TYPE_HH_LOADSTATE_DONE);
3543 if (!pHCtl)
3544 {
3545 WARN(("VBoxVBVAExHCtlCreate failed\n"));
3546 return VERR_NO_MEMORY;
3547 }
3548
3549 /* sanity */
3550 pHCtl->u.cmd.pu8Cmd = NULL;
3551 pHCtl->u.cmd.cbCmd = 0;
3552
3553 /* NULL completion will just free the ctl up */
3554 int rc = vdmaVBVACtlSubmit(pVdma, pHCtl, VBVAEXHOSTCTL_SOURCE_HOST, NULL, NULL);
3555 if (RT_FAILURE(rc))
3556 {
3557 Log(("vdmaVBVACtlSubmit failed rc %d\n", rc));
3558 VBoxVBVAExHCtlFree(&pVdma->CmdVbva, pHCtl);
3559 return rc;
3560 }
3561#endif
3562 return VINF_SUCCESS;
3563}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use