VirtualBox

source: vbox/trunk/src/VBox/Devices/Graphics/DevVGA_VBVA.cpp@ 86009

Last change on this file since 86009 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 105.2 KB
Line 
1/* $Id: DevVGA_VBVA.cpp 85121 2020-07-08 19:33:26Z vboxsync $ */
2/** @file
3 * VirtualBox Video Acceleration (VBVA).
4 */
5
6/*
7 * Copyright (C) 2006-2020 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_VGA
23#include <VBox/vmm/pdmifs.h>
24#include <VBox/vmm/pdmdev.h>
25#include <VBox/vmm/pgm.h>
26#include <VBox/vmm/ssm.h>
27#include <VBox/VMMDev.h>
28#include <VBox/AssertGuest.h>
29#include <VBoxVideo.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/asm.h>
33#include <iprt/string.h>
34#include <iprt/param.h>
35#ifdef VBOX_WITH_VIDEOHWACCEL
36#include <iprt/semaphore.h>
37#endif
38
39#include "DevVGA.h"
40
41/* A very detailed logging. */
42#if 0 // def DEBUG_sunlover
43#define LOGVBVABUFFER(a) LogFlow(a)
44#else
45#define LOGVBVABUFFER(a) do {} while (0)
46#endif
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52typedef struct VBVAPARTIALRECORD
53{
54 uint8_t *pu8;
55 uint32_t cb;
56} VBVAPARTIALRECORD;
57
58typedef struct VBVADATA
59{
60 struct
61 {
62 VBVABUFFER RT_UNTRUSTED_VOLATILE_GUEST *pVBVA; /**< Pointer to the guest memory with the VBVABUFFER. */
63 uint8_t RT_UNTRUSTED_VOLATILE_GUEST *pu8Data; /**< For convenience, pointer to the guest ring buffer (VBVABUFFER::au8Data). */
64 } guest;
65 uint32_t u32VBVAOffset; /**< VBVABUFFER offset in the guest VRAM. */
66 VBVAPARTIALRECORD partialRecord; /**< Partial record temporary storage. */
67 uint32_t off32Data; /**< The offset where the data starts in the VBVABUFFER.
68 * The host code uses it instead of VBVABUFFER::off32Data. */
69 uint32_t indexRecordFirst; /**< Index of the first filled record in VBVABUFFER::aRecords. */
70 uint32_t cbPartialWriteThreshold; /**< Copy of VBVABUFFER::cbPartialWriteThreshold used by host code. */
71 uint32_t cbData; /**< Copy of VBVABUFFER::cbData used by host code. */
72} VBVADATA;
73
74typedef struct VBVAVIEW
75{
76 VBVAINFOVIEW view;
77 VBVAINFOSCREEN screen;
78 VBVADATA vbva;
79} VBVAVIEW;
80
81typedef struct VBVAMOUSESHAPEINFO
82{
83 bool fSet;
84 bool fVisible;
85 bool fAlpha;
86 uint32_t u32HotX;
87 uint32_t u32HotY;
88 uint32_t u32Width;
89 uint32_t u32Height;
90 uint32_t cbShape;
91 uint32_t cbAllocated;
92 uint8_t *pu8Shape;
93} VBVAMOUSESHAPEINFO;
94
95/** @todo saved state: save and restore VBVACONTEXT */
96typedef struct VBVACONTEXT
97{
98 uint32_t cViews;
99 VBVAVIEW aViews[VBOX_VIDEO_MAX_SCREENS];
100 VBVAMOUSESHAPEINFO mouseShapeInfo;
101 bool fPaused;
102 VBVAMODEHINT aModeHints[VBOX_VIDEO_MAX_SCREENS];
103} VBVACONTEXT;
104
105
106static void vbvaDataCleanup(VBVADATA *pVBVAData)
107{
108 if (pVBVAData->guest.pVBVA)
109 {
110 pVBVAData->guest.pVBVA->hostFlags.u32HostEvents = 0;
111 pVBVAData->guest.pVBVA->hostFlags.u32SupportedOrders = 0;
112 }
113
114 RTMemFreeZ(pVBVAData->partialRecord.pu8, pVBVAData->partialRecord.cb);
115
116 RT_ZERO(*pVBVAData);
117 pVBVAData->u32VBVAOffset = HGSMIOFFSET_VOID;
118}
119
120/** Copies @a cb bytes from the VBVA ring buffer to the @a pbDst.
121 * Used for partial records or for records which cross the ring boundary.
122 */
123static bool vbvaFetchBytes(VBVADATA *pVBVAData, uint8_t *pbDst, uint32_t cb)
124{
125 if (cb >= pVBVAData->cbData)
126 {
127 AssertMsgFailed(("cb = 0x%08X, ring buffer size 0x%08X", cb, pVBVAData->cbData));
128 return false;
129 }
130
131 const uint8_t RT_UNTRUSTED_VOLATILE_GUEST *pbSrc = &pVBVAData->guest.pu8Data[pVBVAData->off32Data];
132 const uint32_t u32BytesTillBoundary = pVBVAData->cbData - pVBVAData->off32Data;
133 const int32_t i32Diff = cb - u32BytesTillBoundary;
134
135 if (i32Diff <= 0)
136 {
137 /* Chunk will not cross buffer boundary. */
138 RT_BCOPY_VOLATILE(pbDst, pbSrc, cb);
139 }
140 else
141 {
142 /* Chunk crosses buffer boundary. */
143 RT_BCOPY_VOLATILE(pbDst, pbSrc, u32BytesTillBoundary);
144 RT_BCOPY_VOLATILE(pbDst + u32BytesTillBoundary, &pVBVAData->guest.pu8Data[0], i32Diff);
145 }
146
147 /* Advance data offset and sync with guest. */
148 pVBVAData->off32Data = (pVBVAData->off32Data + cb) % pVBVAData->cbData;
149 pVBVAData->guest.pVBVA->off32Data = pVBVAData->off32Data;
150 return true;
151}
152
153
154static bool vbvaPartialRead(uint32_t cbRecord, VBVADATA *pVBVAData)
155{
156 VBVAPARTIALRECORD *pPartialRecord = &pVBVAData->partialRecord;
157 uint8_t *pu8New;
158
159 LOGVBVABUFFER(("vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
160 pPartialRecord->pu8, pPartialRecord->cb, cbRecord));
161
162 Assert(cbRecord > pPartialRecord->cb); /* Caller ensures this. */
163
164 const uint32_t cbChunk = cbRecord - pPartialRecord->cb;
165 if (cbChunk >= pVBVAData->cbData)
166 {
167 return false;
168 }
169
170 if (pPartialRecord->pu8)
171 {
172 Assert(pPartialRecord->cb);
173 pu8New = (uint8_t *)RTMemRealloc(pPartialRecord->pu8, cbRecord);
174 }
175 else
176 {
177 Assert(!pPartialRecord->cb);
178 pu8New = (uint8_t *)RTMemAlloc(cbRecord);
179 }
180
181 if (!pu8New)
182 {
183 /* Memory allocation failed, fail the function. */
184 Log(("vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
185 cbRecord));
186
187 return false;
188 }
189
190 /* Fetch data from the ring buffer. */
191 if (!vbvaFetchBytes(pVBVAData, pu8New + pPartialRecord->cb, cbChunk))
192 {
193 return false;
194 }
195
196 pPartialRecord->pu8 = pu8New;
197 pPartialRecord->cb = cbRecord;
198
199 return true;
200}
201
202/**
203 * For contiguous chunks just return the address in the buffer. For crossing
204 * boundary - allocate a buffer from heap.
205 */
206static bool vbvaFetchCmd(VBVADATA *pVBVAData, VBVACMDHDR RT_UNTRUSTED_VOLATILE_GUEST **ppHdr, uint32_t *pcbCmd)
207{
208 VBVAPARTIALRECORD *pPartialRecord = &pVBVAData->partialRecord;
209 uint32_t indexRecordFirst = pVBVAData->indexRecordFirst;
210 const uint32_t indexRecordFree = ASMAtomicReadU32(&pVBVAData->guest.pVBVA->indexRecordFree);
211
212 LOGVBVABUFFER(("first = %d, free = %d\n",
213 indexRecordFirst, indexRecordFree));
214
215 if (indexRecordFree >= RT_ELEMENTS(pVBVAData->guest.pVBVA->aRecords))
216 {
217 return false;
218 }
219
220 if (indexRecordFirst == indexRecordFree)
221 {
222 /* No records to process. Return without assigning output variables. */
223 return true;
224 }
225
226 uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVBVAData->guest.pVBVA->aRecords[indexRecordFirst].cbRecord);
227
228 LOGVBVABUFFER(("cbRecord = 0x%08X, pPartialRecord->cb = 0x%08X\n", cbRecordCurrent, pPartialRecord->cb));
229
230 uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;
231
232 if (cbRecord > VBVA_MAX_RECORD_SIZE)
233 {
234 return false;
235 }
236
237 if (pPartialRecord->cb)
238 {
239 /* There is a partial read in process. Continue with it. */
240 Assert (pPartialRecord->pu8);
241
242 LOGVBVABUFFER(("continue partial record cb = %d cbRecord 0x%08X, first = %d, free = %d\n",
243 pPartialRecord->cb, cbRecordCurrent, indexRecordFirst, indexRecordFree));
244
245 if (cbRecord > pPartialRecord->cb)
246 {
247 /* New data has been added to the record. */
248 if (!vbvaPartialRead(cbRecord, pVBVAData))
249 {
250 return false;
251 }
252 }
253
254 if (!(cbRecordCurrent & VBVA_F_RECORD_PARTIAL))
255 {
256 /* The record is completed by guest. Return it to the caller. */
257 *ppHdr = (VBVACMDHDR *)pPartialRecord->pu8;
258 *pcbCmd = pPartialRecord->cb;
259
260 pPartialRecord->pu8 = NULL;
261 pPartialRecord->cb = 0;
262
263 /* Advance the record index and sync with guest. */
264 pVBVAData->indexRecordFirst = (indexRecordFirst + 1) % RT_ELEMENTS(pVBVAData->guest.pVBVA->aRecords);
265 pVBVAData->guest.pVBVA->indexRecordFirst = pVBVAData->indexRecordFirst;
266
267 LOGVBVABUFFER(("partial done ok, data = %d, free = %d\n",
268 pVBVAData->off32Data, pVBVAData->guest.pVBVA->off32Free));
269 }
270
271 return true;
272 }
273
274 /* A new record need to be processed. */
275 if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
276 {
277 /* Current record is being written by guest. '=' is important here,
278 * because the guest will do a FLUSH at this condition.
279 * This partial record is too large for the ring buffer and must
280 * be accumulated in an allocated buffer.
281 */
282 if (cbRecord >= pVBVAData->cbData - pVBVAData->cbPartialWriteThreshold)
283 {
284 /* Partial read must be started. */
285 if (!vbvaPartialRead(cbRecord, pVBVAData))
286 {
287 return false;
288 }
289
290 LOGVBVABUFFER(("started partial record cb = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
291 pPartialRecord->cb, cbRecordCurrent, indexRecordFirst, indexRecordFree));
292 }
293
294 return true;
295 }
296
297 /* Current record is complete. If it is not empty, process it. */
298 if (cbRecord >= pVBVAData->cbData)
299 {
300 return false;
301 }
302
303 if (cbRecord)
304 {
305 /* The size of largest contiguous chunk in the ring buffer. */
306 uint32_t u32BytesTillBoundary = pVBVAData->cbData - pVBVAData->off32Data;
307
308 /* The pointer to data in the ring buffer. */
309 uint8_t RT_UNTRUSTED_VOLATILE_GUEST *pbSrc = &pVBVAData->guest.pu8Data[pVBVAData->off32Data];
310
311 /* Fetch or point the data. */
312 if (u32BytesTillBoundary >= cbRecord)
313 {
314 /* The command does not cross buffer boundary. Return address in the buffer. */
315 *ppHdr = (VBVACMDHDR RT_UNTRUSTED_VOLATILE_GUEST *)pbSrc;
316
317 /* The data offset will be updated in vbvaReleaseCmd. */
318 }
319 else
320 {
321 /* The command crosses buffer boundary. Rare case, so not optimized. */
322 uint8_t *pbDst = (uint8_t *)RTMemAlloc(cbRecord);
323 if (!pbDst)
324 {
325 LogFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
326 return false;
327 }
328
329 vbvaFetchBytes(pVBVAData, pbDst, cbRecord);
330
331 *ppHdr = (VBVACMDHDR *)pbDst;
332
333 LOGVBVABUFFER(("Allocated from heap %p\n", pbDst));
334 }
335 }
336
337 *pcbCmd = cbRecord;
338
339 /* Advance the record index and sync with guest. */
340 pVBVAData->indexRecordFirst = (indexRecordFirst + 1) % RT_ELEMENTS(pVBVAData->guest.pVBVA->aRecords);
341 pVBVAData->guest.pVBVA->indexRecordFirst = pVBVAData->indexRecordFirst;
342
343 LOGVBVABUFFER(("done ok, data = %d, free = %d\n",
344 pVBVAData->off32Data, pVBVAData->guest.pVBVA->off32Free));
345
346 return true;
347}
348
349static void vbvaReleaseCmd(VBVADATA *pVBVAData, VBVACMDHDR RT_UNTRUSTED_VOLATILE_GUEST *pHdr, uint32_t cbCmd)
350{
351 VBVAPARTIALRECORD *pPartialRecord = &pVBVAData->partialRecord;
352 const uint8_t RT_UNTRUSTED_VOLATILE_GUEST *pbRingBuffer = pVBVAData->guest.pu8Data;
353
354 if ( (uintptr_t)pHdr >= (uintptr_t)pbRingBuffer
355 && (uintptr_t)pHdr < (uintptr_t)&pbRingBuffer[pVBVAData->cbData])
356 {
357 /* The pointer is inside ring buffer. Must be continuous chunk. */
358 Assert(pVBVAData->cbData - (uint32_t)((uint8_t *)pHdr - pbRingBuffer) >= cbCmd);
359
360 /* Advance data offset and sync with guest. */
361 pVBVAData->off32Data = (pVBVAData->off32Data + cbCmd) % pVBVAData->cbData;
362 pVBVAData->guest.pVBVA->off32Data = pVBVAData->off32Data;
363
364 Assert(!pPartialRecord->pu8 && pPartialRecord->cb == 0);
365 }
366 else
367 {
368 /* The pointer is outside. It is then an allocated copy. */
369 LOGVBVABUFFER(("Free heap %p\n", pHdr));
370
371 if ((uint8_t *)pHdr == pPartialRecord->pu8)
372 {
373 pPartialRecord->pu8 = NULL;
374 pPartialRecord->cb = 0;
375 }
376 else
377 {
378 Assert(!pPartialRecord->pu8 && pPartialRecord->cb == 0);
379 }
380
381 RTMemFree((void *)pHdr);
382 }
383}
384
385static int vbvaFlushProcess(PVGASTATECC pThisCC, VBVADATA *pVBVAData, unsigned uScreenId)
386{
387 LOGVBVABUFFER(("uScreenId %d, indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
388 uScreenId, pVBVAData->indexRecordFirst, pVBVAData->guest.pVBVA->indexRecordFree,
389 pVBVAData->off32Data, pVBVAData->guest.pVBVA->off32Free));
390 struct
391 {
392 /* The rectangle that includes all dirty rectangles. */
393 int32_t xLeft;
394 int32_t xRight;
395 int32_t yTop;
396 int32_t yBottom;
397 } dirtyRect;
398 RT_ZERO(dirtyRect);
399
400 bool fUpdate = false; /* Whether there were any updates. */
401 bool fDirtyEmpty = true;
402
403 for (;;)
404 {
405 /* Fetch the command data. */
406 VBVACMDHDR RT_UNTRUSTED_VOLATILE_GUEST *pHdr = NULL;
407 uint32_t cbCmd = UINT32_MAX;
408 if (!vbvaFetchCmd(pVBVAData, &pHdr, &cbCmd))
409 {
410 LogFunc(("unable to fetch command. off32Data = %d, off32Free = %d!!!\n",
411 pVBVAData->off32Data, pVBVAData->guest.pVBVA->off32Free));
412 return VERR_NOT_SUPPORTED;
413 }
414
415 if (cbCmd == UINT32_MAX)
416 {
417 /* No more commands yet in the queue. */
418 break;
419 }
420
421 if (cbCmd < sizeof(VBVACMDHDR))
422 {
423 LogFunc(("short command. off32Data = %d, off32Free = %d, cbCmd %d!!!\n",
424 pVBVAData->off32Data, pVBVAData->guest.pVBVA->off32Free, cbCmd));
425
426 return VERR_NOT_SUPPORTED;
427 }
428
429 if (cbCmd != 0)
430 {
431 if (!fUpdate)
432 {
433 pThisCC->pDrv->pfnVBVAUpdateBegin(pThisCC->pDrv, uScreenId);
434 fUpdate = true;
435 }
436
437 /* Updates the rectangle and sends the command to the VRDP server. */
438 pThisCC->pDrv->pfnVBVAUpdateProcess(pThisCC->pDrv, uScreenId, pHdr, cbCmd);
439
440 int32_t xRight = pHdr->x + pHdr->w;
441 int32_t yBottom = pHdr->y + pHdr->h;
442
443 /* These are global coords, relative to the primary screen. */
444
445 LOGVBVABUFFER(("cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n", cbCmd, pHdr->x, pHdr->y, pHdr->w, pHdr->h));
446 LogRel3(("%s: update command cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
447 __FUNCTION__, cbCmd, pHdr->x, pHdr->y, pHdr->w, pHdr->h));
448
449 /* Collect all rects into one. */
450 if (fDirtyEmpty)
451 {
452 /* This is the first rectangle to be added. */
453 dirtyRect.xLeft = pHdr->x;
454 dirtyRect.yTop = pHdr->y;
455 dirtyRect.xRight = xRight;
456 dirtyRect.yBottom = yBottom;
457 fDirtyEmpty = false;
458 }
459 else
460 {
461 /* Adjust region coordinates. */
462 if (dirtyRect.xLeft > pHdr->x)
463 {
464 dirtyRect.xLeft = pHdr->x;
465 }
466
467 if (dirtyRect.yTop > pHdr->y)
468 {
469 dirtyRect.yTop = pHdr->y;
470 }
471
472 if (dirtyRect.xRight < xRight)
473 {
474 dirtyRect.xRight = xRight;
475 }
476
477 if (dirtyRect.yBottom < yBottom)
478 {
479 dirtyRect.yBottom = yBottom;
480 }
481 }
482 }
483
484 vbvaReleaseCmd(pVBVAData, pHdr, cbCmd);
485 }
486
487 if (fUpdate)
488 {
489 if (dirtyRect.xRight - dirtyRect.xLeft)
490 {
491 LogRel3(("%s: sending update screen=%d, x=%d, y=%d, w=%d, h=%d\n",
492 __FUNCTION__, uScreenId, dirtyRect.xLeft,
493 dirtyRect.yTop, dirtyRect.xRight - dirtyRect.xLeft,
494 dirtyRect.yBottom - dirtyRect.yTop));
495 pThisCC->pDrv->pfnVBVAUpdateEnd(pThisCC->pDrv, uScreenId, dirtyRect.xLeft, dirtyRect.yTop,
496 dirtyRect.xRight - dirtyRect.xLeft, dirtyRect.yBottom - dirtyRect.yTop);
497 }
498 else
499 {
500 pThisCC->pDrv->pfnVBVAUpdateEnd(pThisCC->pDrv, uScreenId, 0, 0, 0, 0);
501 }
502 }
503
504 return VINF_SUCCESS;
505}
506
507static int vbvaFlush(PVGASTATE pThis, PVGASTATECC pThisCC, VBVACONTEXT *pCtx)
508{
509 int rc = VINF_SUCCESS;
510
511 unsigned uScreenId;
512 for (uScreenId = 0; uScreenId < pCtx->cViews; uScreenId++)
513 {
514 VBVADATA *pVBVAData = &pCtx->aViews[uScreenId].vbva;
515 if (pVBVAData->guest.pVBVA)
516 {
517 rc = vbvaFlushProcess(pThisCC, pVBVAData, uScreenId);
518 if (RT_FAILURE(rc))
519 break;
520 }
521 }
522
523 if (RT_FAILURE(rc))
524 {
525 /* Turn off VBVA processing. */
526 LogRel(("VBVA: Disabling (%Rrc)\n", rc));
527 pThis->fGuestCaps = 0;
528 pThisCC->pDrv->pfnVBVAGuestCapabilityUpdate(pThisCC->pDrv, pThis->fGuestCaps);
529 for (uScreenId = 0; uScreenId < pCtx->cViews; uScreenId++)
530 {
531 VBVADATA *pVBVAData = &pCtx->aViews[uScreenId].vbva;
532 if (pVBVAData->guest.pVBVA)
533 {
534 vbvaDataCleanup(pVBVAData);
535 pThisCC->pDrv->pfnVBVADisable(pThisCC->pDrv, uScreenId);
536 }
537 }
538 }
539
540 return rc;
541}
542
543static int vbvaResize(PVGASTATECC pThisCC, VBVAVIEW *pView, const VBVAINFOSCREEN *pNewScreen, bool fResetInputMapping)
544{
545 /* Callers ensure that pNewScreen contains valid data. */
546
547 /* Apply these changes. */
548 pView->screen = *pNewScreen;
549
550 uint8_t *pbVRam = pThisCC->pbVRam + pView->view.u32ViewOffset;
551 return pThisCC->pDrv->pfnVBVAResize(pThisCC->pDrv, &pView->view, &pView->screen, pbVRam, fResetInputMapping);
552}
553
554static int vbvaEnable(PVGASTATE pThis, PVGASTATECC pThisCC, VBVACONTEXT *pCtx, unsigned uScreenId,
555 VBVABUFFER RT_UNTRUSTED_VOLATILE_GUEST *pVBVA, uint32_t u32Offset, bool fRestored)
556{
557 /*
558 * Copy into non-volatile memory and validate its content.
559 */
560 VBVABUFFER VbgaSafe;
561 RT_COPY_VOLATILE(VbgaSafe, *pVBVA);
562 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
563
564 uint32_t const cbVBVABuffer = RT_UOFFSETOF(VBVABUFFER, au8Data) + VbgaSafe.cbData;
565 ASSERT_GUEST_RETURN( VbgaSafe.cbData <= UINT32_MAX - RT_UOFFSETOF(VBVABUFFER, au8Data)
566 && cbVBVABuffer <= pThis->vram_size
567 && u32Offset <= pThis->vram_size - cbVBVABuffer,
568 VERR_INVALID_PARAMETER);
569 if (!fRestored)
570 {
571 ASSERT_GUEST_RETURN(VbgaSafe.off32Data == 0, VERR_INVALID_PARAMETER);
572 ASSERT_GUEST_RETURN(VbgaSafe.off32Free == 0, VERR_INVALID_PARAMETER);
573 ASSERT_GUEST_RETURN(VbgaSafe.indexRecordFirst == 0, VERR_INVALID_PARAMETER);
574 ASSERT_GUEST_RETURN(VbgaSafe.indexRecordFree == 0, VERR_INVALID_PARAMETER);
575 }
576 ASSERT_GUEST_RETURN( VbgaSafe.cbPartialWriteThreshold < VbgaSafe.cbData
577 && VbgaSafe.cbPartialWriteThreshold != 0,
578 VERR_INVALID_PARAMETER);
579 RT_UNTRUSTED_VALIDATED_FENCE();
580
581 /*
582 * Okay, try do the job.
583 */
584 int rc;
585 if (pThisCC->pDrv->pfnVBVAEnable)
586 {
587 pVBVA->hostFlags.u32HostEvents = 0;
588 pVBVA->hostFlags.u32SupportedOrders = 0;
589 rc = pThisCC->pDrv->pfnVBVAEnable(pThisCC->pDrv, uScreenId, &pVBVA->hostFlags);
590 if (RT_SUCCESS(rc))
591 {
592 /* pVBVA->hostFlags has been set up by pfnVBVAEnable. */
593 LogFlowFunc(("u32HostEvents=0x%08x u32SupportedOrders=0x%08x\n",
594 pVBVA->hostFlags.u32HostEvents, pVBVA->hostFlags.u32SupportedOrders));
595
596 VBVADATA *pVBVAData = &pCtx->aViews[uScreenId].vbva;
597 pVBVAData->guest.pVBVA = pVBVA;
598 pVBVAData->guest.pu8Data = &pVBVA->au8Data[0];
599 pVBVAData->u32VBVAOffset = u32Offset;
600 pVBVAData->off32Data = VbgaSafe.off32Data;
601 pVBVAData->indexRecordFirst = VbgaSafe.indexRecordFirst;
602 pVBVAData->cbPartialWriteThreshold = VbgaSafe.cbPartialWriteThreshold;
603 pVBVAData->cbData = VbgaSafe.cbData;
604
605 if (!fRestored)
606 {
607 /** @todo Actually this function must not touch the partialRecord structure at all,
608 * because initially it is a zero and when VBVA is disabled this should be set to zero.
609 * But I'm not sure that no code depends on zeroing partialRecord here.
610 * So for now (a quick fix for 4.1) just do not do this if the VM was restored,
611 * when partialRecord might be loaded already from the saved state.
612 */
613 pVBVAData->partialRecord.pu8 = NULL;
614 pVBVAData->partialRecord.cb = 0;
615 }
616
617 /* VBVA is working so disable the pause. */
618 pCtx->fPaused = false;
619 }
620 }
621 else
622 rc = VERR_NOT_SUPPORTED;
623 return rc;
624}
625
626static int vbvaDisable(PVGASTATE pThis, PVGASTATECC pThisCC, VBVACONTEXT *pCtx, unsigned idScreen)
627{
628 /* Process any pending orders and empty the VBVA ring buffer. */
629 vbvaFlush(pThis, pThisCC, pCtx);
630
631 AssertReturn(idScreen < RT_ELEMENTS(pCtx->aViews), VERR_OUT_OF_RANGE);
632 VBVADATA *pVBVAData = &pCtx->aViews[idScreen].vbva;
633 vbvaDataCleanup(pVBVAData);
634
635 if (idScreen == 0)
636 {
637 pThis->fGuestCaps = 0;
638 pThisCC->pDrv->pfnVBVAGuestCapabilityUpdate(pThisCC->pDrv, pThis->fGuestCaps);
639 }
640 pThisCC->pDrv->pfnVBVADisable(pThisCC->pDrv, idScreen);
641 return VINF_SUCCESS;
642}
643
644#ifdef UNUSED_FUNCTION
645bool VBVAIsEnabled(PVGASTATECC pThisCC)
646{
647 PHGSMIINSTANCE pHGSMI = pThisCC->pHGSMI;
648 if (pHGSMI)
649 {
650 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pHGSMI);
651 if (pCtx)
652 {
653 if (pCtx->cViews)
654 {
655 VBVAVIEW * pView = &pCtx->aViews[0];
656 if (pView->vbva.guest.pVBVA)
657 return true;
658 }
659 }
660 }
661 return false;
662}
663#endif
664
665#ifdef DEBUG_sunlover
666void dumpMouseShapeInfo(const VBVAMOUSESHAPEINFO *pMouseShapeInfo)
667{
668 LogFlow(("fSet = %d, fVisible %d, fAlpha %d, @%d,%d %dx%d (%p, %d/%d)\n",
669 pMouseShapeInfo->fSet,
670 pMouseShapeInfo->fVisible,
671 pMouseShapeInfo->fAlpha,
672 pMouseShapeInfo->u32HotX,
673 pMouseShapeInfo->u32HotY,
674 pMouseShapeInfo->u32Width,
675 pMouseShapeInfo->u32Height,
676 pMouseShapeInfo->pu8Shape,
677 pMouseShapeInfo->cbShape,
678 pMouseShapeInfo->cbAllocated
679 ));
680}
681#endif
682
683static int vbvaUpdateMousePointerShape(PVGASTATECC pThisCC, VBVAMOUSESHAPEINFO *pMouseShapeInfo, bool fShape)
684{
685 LogFlowFunc(("pThisCC %p, pMouseShapeInfo %p, fShape %d\n", pThisCC, pMouseShapeInfo, fShape));
686#ifdef DEBUG_sunlover
687 dumpMouseShapeInfo(pMouseShapeInfo);
688#endif
689
690 if (pThisCC->pDrv->pfnVBVAMousePointerShape == NULL)
691 return VERR_NOT_SUPPORTED;
692
693 int rc;
694 if (fShape && pMouseShapeInfo->pu8Shape != NULL)
695 rc = pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv,
696 pMouseShapeInfo->fVisible,
697 pMouseShapeInfo->fAlpha,
698 pMouseShapeInfo->u32HotX,
699 pMouseShapeInfo->u32HotY,
700 pMouseShapeInfo->u32Width,
701 pMouseShapeInfo->u32Height,
702 pMouseShapeInfo->pu8Shape);
703 else
704 rc = pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv,
705 pMouseShapeInfo->fVisible,
706 false,
707 0, 0,
708 0, 0,
709 NULL);
710 return rc;
711}
712
713static int vbvaMousePointerShape(PVGASTATECC pThisCC, VBVACONTEXT *pCtx,
714 const VBVAMOUSEPOINTERSHAPE RT_UNTRUSTED_VOLATILE_GUEST *pShape, HGSMISIZE cbShape)
715{
716 /*
717 * Make non-volatile copy of the shape header and validate it.
718 */
719 VBVAMOUSEPOINTERSHAPE SafeShape;
720 RT_COPY_VOLATILE(SafeShape, *pShape);
721 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
722
723 LogFlowFunc(("VBVA_MOUSE_POINTER_SHAPE: i32Result 0x%x, fu32Flags 0x%x, hot spot %d,%d, size %dx%d\n",
724 SafeShape.i32Result, SafeShape.fu32Flags, SafeShape.u32HotX, SafeShape.u32HotY, SafeShape.u32Width, SafeShape.u32Height));
725
726 const bool fVisible = RT_BOOL(SafeShape.fu32Flags & VBOX_MOUSE_POINTER_VISIBLE);
727 const bool fAlpha = RT_BOOL(SafeShape.fu32Flags & VBOX_MOUSE_POINTER_ALPHA);
728 const bool fShape = RT_BOOL(SafeShape.fu32Flags & VBOX_MOUSE_POINTER_SHAPE);
729
730 HGSMISIZE cbPointerData = 0;
731 if (fShape)
732 {
733 static const uint32_t s_cxMax = 2048; //used to be: 8192;
734 static const uint32_t s_cyMax = 2048; //used to be: 8192;
735 ASSERT_GUEST_MSG_RETURN( SafeShape.u32Width <= s_cxMax
736 || SafeShape.u32Height <= s_cyMax,
737 ("Too large: %ux%u, max %ux%x\n", SafeShape.u32Width, SafeShape.u32Height, s_cxMax, s_cyMax),
738 VERR_INVALID_PARAMETER);
739
740 cbPointerData = ((((SafeShape.u32Width + 7) / 8) * SafeShape.u32Height + 3) & ~3)
741 + SafeShape.u32Width * 4 * SafeShape.u32Height;
742
743 ASSERT_GUEST_MSG_RETURN(cbPointerData <= cbShape - RT_UOFFSETOF(VBVAMOUSEPOINTERSHAPE, au8Data),
744 ("Insufficent pointer data: Expected %#x, got %#x\n",
745 cbPointerData, cbShape - RT_UOFFSETOF(VBVAMOUSEPOINTERSHAPE, au8Data) ),
746 VERR_INVALID_PARAMETER);
747 }
748 RT_UNTRUSTED_VALIDATED_FENCE();
749
750 /*
751 * Do the job.
752 */
753 /* Save mouse info it will be used to restore mouse pointer after restoring saved state. */
754 pCtx->mouseShapeInfo.fSet = true;
755 pCtx->mouseShapeInfo.fVisible = fVisible;
756 if (fShape)
757 {
758 /* Data related to shape. */
759 pCtx->mouseShapeInfo.u32HotX = SafeShape.u32HotX;
760 pCtx->mouseShapeInfo.u32HotY = SafeShape.u32HotY;
761 pCtx->mouseShapeInfo.u32Width = SafeShape.u32Width;
762 pCtx->mouseShapeInfo.u32Height = SafeShape.u32Height;
763 pCtx->mouseShapeInfo.fAlpha = fAlpha;
764
765 /* Reallocate memory buffer if necessary. */
766 if (cbPointerData > pCtx->mouseShapeInfo.cbAllocated)
767 {
768 RTMemFreeZ(pCtx->mouseShapeInfo.pu8Shape, pCtx->mouseShapeInfo.cbAllocated);
769 pCtx->mouseShapeInfo.pu8Shape = NULL;
770 pCtx->mouseShapeInfo.cbShape = 0;
771
772 uint8_t *pu8Shape = (uint8_t *)RTMemAlloc(cbPointerData);
773 if (pu8Shape)
774 {
775 pCtx->mouseShapeInfo.pu8Shape = pu8Shape;
776 pCtx->mouseShapeInfo.cbAllocated = cbPointerData;
777 }
778 }
779
780 /* Copy shape bitmaps. */
781 if (pCtx->mouseShapeInfo.pu8Shape)
782 {
783 RT_BCOPY_VOLATILE(pCtx->mouseShapeInfo.pu8Shape, &pShape->au8Data[0], cbPointerData);
784 pCtx->mouseShapeInfo.cbShape = cbPointerData;
785 }
786 }
787
788 return vbvaUpdateMousePointerShape(pThisCC, &pCtx->mouseShapeInfo, fShape);
789}
790
791static uint32_t vbvaViewFromBufferPtr(PHGSMIINSTANCE pIns, const VBVACONTEXT *pCtx,
792 const void RT_UNTRUSTED_VOLATILE_GUEST *pvBuffer)
793{
794 /* Check which view contains the buffer. */
795 HGSMIOFFSET offBuffer = HGSMIPointerToOffsetHost(pIns, pvBuffer);
796 if (offBuffer != HGSMIOFFSET_VOID)
797 {
798 unsigned uScreenId;
799 for (uScreenId = 0; uScreenId < pCtx->cViews; uScreenId++)
800 {
801 const VBVAINFOVIEW *pView = &pCtx->aViews[uScreenId].view;
802 if ((uint32_t)(offBuffer - pView->u32ViewOffset) < pView->u32ViewSize)
803 return pView->u32ViewIndex;
804 }
805 }
806 return UINT32_MAX;
807}
808
809#ifdef DEBUG_sunlover
810static void dumpctx(const VBVACONTEXT *pCtx)
811{
812 Log(("VBVACONTEXT dump: cViews %d\n", pCtx->cViews));
813
814 uint32_t iView;
815 for (iView = 0; iView < pCtx->cViews; iView++)
816 {
817 const VBVAVIEW *pView = &pCtx->aViews[iView];
818
819 Log((" view %d o 0x%x s 0x%x m 0x%x\n",
820 pView->view.u32ViewIndex,
821 pView->view.u32ViewOffset,
822 pView->view.u32ViewSize,
823 pView->view.u32MaxScreenSize));
824
825 Log((" screen %d @%d,%d s 0x%x l 0x%x %dx%d bpp %d f 0x%x\n",
826 pView->screen.u32ViewIndex,
827 pView->screen.i32OriginX,
828 pView->screen.i32OriginY,
829 pView->screen.u32StartOffset,
830 pView->screen.u32LineSize,
831 pView->screen.u32Width,
832 pView->screen.u32Height,
833 pView->screen.u16BitsPerPixel,
834 pView->screen.u16Flags));
835
836 Log((" VBVA o 0x%x p %p\n",
837 pView->vbva.u32VBVAOffset,
838 pView->vbva.guest.pVBVA));
839
840 Log((" PR cb 0x%x p %p\n",
841 pView->vbva.partialRecord.cb,
842 pView->vbva.partialRecord.pu8));
843 }
844
845 dumpMouseShapeInfo(&pCtx->mouseShapeInfo);
846}
847#endif /* DEBUG_sunlover */
848
849#define VBOXVBVASAVEDSTATE_VHWAAVAILABLE_MAGIC 0x12345678
850#define VBOXVBVASAVEDSTATE_VHWAUNAVAILABLE_MAGIC 0x9abcdef0
851
852#ifdef VBOX_WITH_VIDEOHWACCEL
853
854static void vbvaVHWAHHCommandReinit(VBOXVHWACMD* pHdr, VBOXVHWACMD_TYPE enmCmd, int32_t iDisplay)
855{
856 memset(pHdr, 0, VBOXVHWACMD_HEADSIZE());
857 pHdr->cRefs = 1;
858 pHdr->iDisplay = iDisplay;
859 pHdr->rc = VERR_NOT_IMPLEMENTED;
860 pHdr->enmCmd = enmCmd;
861 pHdr->Flags = VBOXVHWACMD_FLAG_HH_CMD;
862}
863
864static VBOXVHWACMD *vbvaVHWAHHCommandCreate(VBOXVHWACMD_TYPE enmCmd, int32_t iDisplay, VBOXVHWACMD_LENGTH cbCmd)
865{
866 VBOXVHWACMD *pHdr = (VBOXVHWACMD *)RTMemAllocZ(cbCmd + VBOXVHWACMD_HEADSIZE());
867 Assert(pHdr);
868 if (pHdr)
869 vbvaVHWAHHCommandReinit(pHdr, enmCmd, iDisplay);
870
871 return pHdr;
872}
873
874DECLINLINE(void) vbvaVHWAHHCommandRelease(VBOXVHWACMD *pCmd)
875{
876 uint32_t cRefs = ASMAtomicDecU32(&pCmd->cRefs);
877 if (!cRefs)
878 RTMemFree(pCmd);
879}
880
881DECLINLINE(void) vbvaVHWAHHCommandRetain(VBOXVHWACMD *pCmd)
882{
883 ASMAtomicIncU32(&pCmd->cRefs);
884}
885
886static void vbvaVHWACommandComplete(PVGASTATECC pThisCC, VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand, bool fAsyncCommand)
887{
888 if (fAsyncCommand)
889 {
890 Assert(pCommand->Flags & VBOXVHWACMD_FLAG_HG_ASYNCH);
891 vbvaR3VHWACommandCompleteAsync(&pThisCC->IVBVACallbacks, pCommand);
892 }
893 else
894 {
895 Log(("VGA Command <<< Sync rc %d %#p, %d\n", pCommand->rc, pCommand, pCommand->enmCmd));
896 pCommand->Flags &= ~VBOXVHWACMD_FLAG_HG_ASYNCH;
897 }
898
899}
900
901static void vbvaVHWACommandCompleteAllPending(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, int rc)
902{
903 if (!ASMAtomicUoReadU32(&pThis->pendingVhwaCommands.cPending))
904 return;
905
906 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
907
908 VBOX_VHWA_PENDINGCMD *pIter, *pNext;
909 RTListForEachSafe(&pThis->pendingVhwaCommands.PendingList, pIter, pNext, VBOX_VHWA_PENDINGCMD, Node)
910 {
911 pIter->pCommand->rc = rc;
912 vbvaVHWACommandComplete(pThisCC, pIter->pCommand, true);
913
914 /* the command is submitted/processed, remove from the pend list */
915 RTListNodeRemove(&pIter->Node);
916 ASMAtomicDecU32(&pThis->pendingVhwaCommands.cPending);
917 RTMemFree(pIter);
918 }
919
920 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
921}
922
923static void vbvaVHWACommandClearAllPending(PPDMDEVINS pDevIns, PVGASTATE pThis)
924{
925 if (!ASMAtomicUoReadU32(&pThis->pendingVhwaCommands.cPending))
926 return;
927
928 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
929
930 VBOX_VHWA_PENDINGCMD *pIter, *pNext;
931 RTListForEachSafe(&pThis->pendingVhwaCommands.PendingList, pIter, pNext, VBOX_VHWA_PENDINGCMD, Node)
932 {
933 RTListNodeRemove(&pIter->Node);
934 ASMAtomicDecU32(&pThis->pendingVhwaCommands.cPending);
935 RTMemFree(pIter);
936 }
937
938 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
939}
940
941static void vbvaVHWACommandPend(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
942 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
943{
944 int rc = VERR_BUFFER_OVERFLOW;
945
946 if (ASMAtomicUoReadU32(&pThis->pendingVhwaCommands.cPending) < VBOX_VHWA_MAX_PENDING_COMMANDS)
947 {
948 VBOX_VHWA_PENDINGCMD *pPend = (VBOX_VHWA_PENDINGCMD *)RTMemAlloc(sizeof(*pPend));
949 if (pPend)
950 {
951 pCommand->Flags |= VBOXVHWACMD_FLAG_HG_ASYNCH;
952 pPend->pCommand = pCommand;
953 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
954 if (ASMAtomicUoReadU32(&pThis->pendingVhwaCommands.cPending) < VBOX_VHWA_MAX_PENDING_COMMANDS)
955 {
956 RTListAppend(&pThis->pendingVhwaCommands.PendingList, &pPend->Node);
957 ASMAtomicIncU32(&pThis->pendingVhwaCommands.cPending);
958 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
959 return;
960 }
961 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
962 LogRel(("VBVA: Pending command count has reached its threshold.. completing them all.."));
963 RTMemFree(pPend);
964 }
965 else
966 rc = VERR_NO_MEMORY;
967 }
968 else
969 LogRel(("VBVA: Pending command count has reached its threshold, completing them all.."));
970
971 vbvaVHWACommandCompleteAllPending(pDevIns, pThis, pThisCC, rc);
972
973 pCommand->rc = rc;
974
975 vbvaVHWACommandComplete(pThisCC, pCommand, false);
976}
977
978static bool vbvaVHWACommandCanPend(VBOXVHWACMD_TYPE enmCmd)
979{
980 switch (enmCmd)
981 {
982 case VBOXVHWACMD_TYPE_HH_CONSTRUCT:
983 case VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEBEGIN:
984 case VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEEND:
985 case VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEPERFORM:
986 case VBOXVHWACMD_TYPE_HH_SAVESTATE_LOADPERFORM:
987 return false;
988 default:
989 return true;
990 }
991}
992
993static int vbvaVHWACommandSavePending(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PVGASTATECC pThisCC, PSSMHANDLE pSSM)
994{
995 int rc = pHlp->pfnSSMPutU32(pSSM, pThis->pendingVhwaCommands.cPending);
996 AssertRCReturn(rc, rc);
997
998 VBOX_VHWA_PENDINGCMD *pIter;
999 RTListForEach(&pThis->pendingVhwaCommands.PendingList, pIter, VBOX_VHWA_PENDINGCMD, Node)
1000 {
1001 AssertContinue((uintptr_t)pIter->pCommand - (uintptr_t)pThisCC->pbVRam < pThis->vram_size);
1002 rc = pHlp->pfnSSMPutU32(pSSM, (uint32_t)(((uint8_t *)pIter->pCommand) - ((uint8_t *)pThisCC->pbVRam)));
1003 AssertRCReturn(rc, rc);
1004 }
1005 return rc;
1006}
1007
1008static int vbvaVHWACommandLoadPending(PPDMDEVINS pDevIns, PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PVGASTATECC pThisCC,
1009 PSSMHANDLE pSSM, uint32_t u32Version)
1010{
1011 if (u32Version < VGA_SAVEDSTATE_VERSION_WITH_PENDVHWA)
1012 return VINF_SUCCESS;
1013
1014 uint32_t u32;
1015 int rc = pHlp->pfnSSMGetU32(pSSM, &u32);
1016 AssertRCReturn(rc, rc);
1017 for (uint32_t i = 0; i < u32; ++i)
1018 {
1019 uint32_t off32;
1020 rc = pHlp->pfnSSMGetU32(pSSM, &off32);
1021 AssertRCReturn(rc, rc);
1022 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand
1023 = (VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *)((uint8_t volatile *)pThisCC->pbVRam + off32);
1024 vbvaVHWACommandPend(pDevIns, pThis, pThisCC, pCommand);
1025 }
1026 return rc;
1027}
1028
1029
1030/** Worker for vbvaVHWACommandSubmit. */
1031static bool vbvaVHWACommandSubmitInner(PVGASTATE pThis, PVGASTATECC pThisCC,
1032 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand, bool *pfPending)
1033{
1034 *pfPending = false;
1035
1036 /*
1037 * Read the command type and validate it and our driver state.
1038 */
1039 VBOXVHWACMD_TYPE enmCmd = pCommand->enmCmd;
1040 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
1041
1042 bool fGuestCmd = (uintptr_t)pCommand - (uintptr_t)pThisCC->pbVRam < pThis->vram_size;
1043 ASSERT_GUEST_LOGREL_MSG_STMT_RETURN( !fGuestCmd
1044 || ( enmCmd != VBOXVHWACMD_TYPE_HH_CONSTRUCT
1045 && enmCmd != VBOXVHWACMD_TYPE_HH_RESET
1046 && enmCmd != VBOXVHWACMD_TYPE_HH_DISABLE
1047 && enmCmd != VBOXVHWACMD_TYPE_HH_ENABLE
1048 && enmCmd != VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEBEGIN
1049 && enmCmd != VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEEND
1050 && enmCmd != VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEPERFORM
1051 && enmCmd != VBOXVHWACMD_TYPE_HH_SAVESTATE_LOADPERFORM),
1052 ("enmCmd=%d\n", enmCmd),
1053 pCommand->rc = VERR_INVALID_PARAMETER,
1054 true);
1055 ASSERT_GUEST_STMT_RETURN(pThisCC->pDrv->pfnVHWACommandProcess, pCommand->rc = VERR_INVALID_STATE, true);
1056 RT_UNTRUSTED_VALIDATED_FENCE();
1057
1058 /*
1059 * Call the driver to process the command.
1060 */
1061 Log(("VGA Command >>> %#p, %d\n", pCommand, enmCmd));
1062 int rc = pThisCC->pDrv->pfnVHWACommandProcess(pThisCC->pDrv, enmCmd, fGuestCmd, pCommand);
1063 if (rc == VINF_CALLBACK_RETURN)
1064 {
1065 Log(("VGA Command --- Going Async %#p, %d\n", pCommand, enmCmd));
1066 *pfPending = true;
1067 return true; /* Command will be completed asynchronously by the driver and need not be put in the pending list. */
1068 }
1069
1070 if (rc == VERR_INVALID_STATE)
1071 {
1072 Log(("VGA Command --- Trying Pend %#p, %d\n", pCommand, enmCmd));
1073 if (vbvaVHWACommandCanPend(enmCmd))
1074 {
1075 Log(("VGA Command --- Can Pend %#p, %d\n", pCommand, enmCmd));
1076 *pfPending = true;
1077 return false; /* put on pending list so it can be retried?? */
1078 }
1079
1080 Log(("VGA Command --- Can NOT Pend %#p, %d\n", pCommand, enmCmd));
1081 }
1082 else
1083 Log(("VGA Command --- Going Complete Sync rc %d %#p, %d\n", rc, pCommand, enmCmd));
1084
1085 /* the command was completed, take a special care about it (see caller) */
1086 pCommand->rc = rc;
1087 return true;
1088}
1089
1090
1091static bool vbvaVHWACommandSubmit(PVGASTATE pThis, PVGASTATECC pThisCC,
1092 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand, bool fAsyncCommand)
1093{
1094 bool fPending = false;
1095 bool fRet = vbvaVHWACommandSubmitInner(pThis, pThisCC, pCommand, &fPending);
1096 if (!fPending)
1097 vbvaVHWACommandComplete(pThisCC, pCommand, fAsyncCommand);
1098 return fRet;
1099}
1100
1101
1102/**
1103 * @returns false if commands are pending, otherwise true.
1104 */
1105static bool vbvaVHWACheckPendingCommands(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
1106{
1107 if (!ASMAtomicUoReadU32(&pThis->pendingVhwaCommands.cPending))
1108 return true;
1109
1110 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1111
1112 VBOX_VHWA_PENDINGCMD *pIter, *pNext;
1113 RTListForEachSafe(&pThis->pendingVhwaCommands.PendingList, pIter, pNext, VBOX_VHWA_PENDINGCMD, Node)
1114 {
1115 if (!vbvaVHWACommandSubmit(pThis, pThisCC, pIter->pCommand, true))
1116 {
1117 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1118 return false; /* the command should be still pending */
1119 }
1120
1121 /* the command is submitted/processed, remove from the pend list */
1122 RTListNodeRemove(&pIter->Node);
1123 ASMAtomicDecU32(&pThis->pendingVhwaCommands.cPending);
1124 RTMemFree(pIter);
1125 }
1126
1127 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1128
1129 return true;
1130}
1131
1132void vbvaTimerCb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
1133{
1134 vbvaVHWACheckPendingCommands(pDevIns, pThis, pThisCC);
1135}
1136
1137static void vbvaVHWAHandleCommand(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
1138 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCmd)
1139{
1140 if (vbvaVHWACheckPendingCommands(pDevIns, pThis, pThisCC))
1141 {
1142 if (vbvaVHWACommandSubmit(pThis, pThisCC, pCmd, false))
1143 return;
1144 }
1145
1146 vbvaVHWACommandPend(pDevIns, pThis, pThisCC, pCmd);
1147}
1148
1149static DECLCALLBACK(void) vbvaVHWAHHCommandSetEventCallback(void * pContext)
1150{
1151 RTSemEventSignal((RTSEMEVENT)pContext);
1152}
1153
1154static int vbvaVHWAHHCommandPost(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, VBOXVHWACMD *pCmd)
1155{
1156 RTSEMEVENT hComplEvent;
1157 int rc = RTSemEventCreate(&hComplEvent);
1158 AssertRC(rc);
1159 if (RT_SUCCESS(rc))
1160 {
1161 /* ensure the cmd is not deleted until we process it */
1162 vbvaVHWAHHCommandRetain(pCmd);
1163
1164 VBOXVHWA_HH_CALLBACK_SET(pCmd, vbvaVHWAHHCommandSetEventCallback, (void *)hComplEvent);
1165 vbvaVHWAHandleCommand(pDevIns, pThis, pThisCC, pCmd);
1166
1167 if ((ASMAtomicReadU32((volatile uint32_t *)&pCmd->Flags) & VBOXVHWACMD_FLAG_HG_ASYNCH) != 0)
1168 rc = RTSemEventWaitNoResume(hComplEvent, RT_INDEFINITE_WAIT); /** @todo Why the NoResume and event leaking here? */
1169 /* else: the command is completed */
1170
1171 AssertRC(rc);
1172 if (RT_SUCCESS(rc))
1173 RTSemEventDestroy(hComplEvent);
1174
1175 vbvaVHWAHHCommandRelease(pCmd);
1176 }
1177 return rc;
1178}
1179
1180int vbvaVHWAConstruct(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
1181{
1182 pThis->pendingVhwaCommands.cPending = 0;
1183 RTListInit(&pThis->pendingVhwaCommands.PendingList);
1184
1185 VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(VBOXVHWACMD_TYPE_HH_CONSTRUCT, 0, sizeof(VBOXVHWACMD_HH_CONSTRUCT));
1186 Assert(pCmd);
1187 if(pCmd)
1188 {
1189 uint32_t iDisplay = 0;
1190 int rc = VINF_SUCCESS;
1191 VBOXVHWACMD_HH_CONSTRUCT *pBody = VBOXVHWACMD_BODY_HOST_HEAP(pCmd, VBOXVHWACMD_HH_CONSTRUCT);
1192
1193 for (;;)
1194 {
1195 memset(pBody, 0, sizeof(VBOXVHWACMD_HH_CONSTRUCT));
1196
1197 PVM pVM = PDMDevHlpGetVM(pDevIns);
1198
1199 pBody->pVM = pVM;
1200 pBody->pvVRAM = pThisCC->pbVRam;
1201 pBody->cbVRAM = pThis->vram_size;
1202
1203 rc = vbvaVHWAHHCommandPost(pDevIns, pThis, pThisCC, pCmd);
1204 ASMCompilerBarrier();
1205
1206 AssertRC(rc);
1207 if (RT_SUCCESS(rc))
1208 {
1209 rc = pCmd->rc;
1210 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
1211 if(rc == VERR_NOT_IMPLEMENTED)
1212 {
1213 /** @todo set some flag in pThis indicating VHWA is not supported */
1214 /* VERR_NOT_IMPLEMENTED is not a failure, we just do not support it */
1215 rc = VINF_SUCCESS;
1216 }
1217
1218 if (!RT_SUCCESS(rc))
1219 break;
1220 }
1221 else
1222 break;
1223
1224 ++iDisplay;
1225 if (iDisplay >= pThis->cMonitors)
1226 break;
1227 vbvaVHWAHHCommandReinit(pCmd, VBOXVHWACMD_TYPE_HH_CONSTRUCT, (int32_t)iDisplay);
1228 }
1229
1230 vbvaVHWAHHCommandRelease(pCmd);
1231
1232 return rc;
1233 }
1234 return VERR_OUT_OF_RESOURCES;
1235}
1236
1237static int vbvaVHWAReset(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
1238{
1239 vbvaVHWACommandClearAllPending(pDevIns, pThis);
1240
1241 /* ensure we have all pending cmds processed and h->g cmds disabled */
1242 VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(VBOXVHWACMD_TYPE_HH_RESET, 0, 0);
1243 Assert(pCmd);
1244 if (pCmd)
1245 {
1246 int rc = VINF_SUCCESS;
1247 uint32_t iDisplay = 0;
1248
1249 do
1250 {
1251 rc = vbvaVHWAHHCommandPost(pDevIns, pThis, pThisCC, pCmd);
1252 AssertRC(rc);
1253 if(RT_SUCCESS(rc))
1254 {
1255 rc = pCmd->rc;
1256 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
1257 if (rc == VERR_NOT_IMPLEMENTED)
1258 rc = VINF_SUCCESS;
1259 }
1260
1261 if (!RT_SUCCESS(rc))
1262 break;
1263
1264 ++iDisplay;
1265 if (iDisplay >= pThis->cMonitors)
1266 break;
1267 vbvaVHWAHHCommandReinit(pCmd, VBOXVHWACMD_TYPE_HH_RESET, (int32_t)iDisplay);
1268
1269 } while (true);
1270
1271 vbvaVHWAHHCommandRelease(pCmd);
1272
1273 return rc;
1274 }
1275 return VERR_OUT_OF_RESOURCES;
1276}
1277
1278typedef DECLCALLBACKTYPE(bool, FNVBOXVHWAHHCMDPRECB,(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, VBOXVHWACMD *pCmd,
1279 uint32_t iDisplay, void *pvContext));
1280typedef FNVBOXVHWAHHCMDPRECB *PFNVBOXVHWAHHCMDPRECB;
1281
1282typedef DECLCALLBACKTYPE(bool, FNVBOXVHWAHHCMDPOSTCB,(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, VBOXVHWACMD *pCmd,
1283 uint32_t iDisplay, int rc, void *pvContext));
1284typedef FNVBOXVHWAHHCMDPOSTCB *PFNVBOXVHWAHHCMDPOSTCB;
1285
1286static int vbvaVHWAHHPost(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, VBOXVHWACMD *pCmd,
1287 PFNVBOXVHWAHHCMDPRECB pfnPre, PFNVBOXVHWAHHCMDPOSTCB pfnPost, void *pvContext)
1288{
1289 const VBOXVHWACMD_TYPE enmType = pCmd->enmCmd;
1290 int rc = VINF_SUCCESS;
1291 uint32_t iDisplay = 0;
1292
1293 do
1294 {
1295 if (!pfnPre || pfnPre(pDevIns, pThis, pThisCC, pCmd, iDisplay, pvContext))
1296 {
1297 rc = vbvaVHWAHHCommandPost(pDevIns, pThis, pThisCC, pCmd);
1298 AssertRC(rc);
1299 if (pfnPost)
1300 {
1301 if (!pfnPost(pDevIns, pThis, pThisCC, pCmd, iDisplay, rc, pvContext))
1302 {
1303 rc = VINF_SUCCESS;
1304 break;
1305 }
1306 rc = VINF_SUCCESS;
1307 }
1308 else if(RT_SUCCESS(rc))
1309 {
1310 rc = pCmd->rc;
1311 AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
1312 if(rc == VERR_NOT_IMPLEMENTED)
1313 {
1314 rc = VINF_SUCCESS;
1315 }
1316 }
1317
1318 if (!RT_SUCCESS(rc))
1319 break;
1320 }
1321
1322 ++iDisplay;
1323 if (iDisplay >= pThis->cMonitors)
1324 break;
1325 vbvaVHWAHHCommandReinit(pCmd, enmType, (int32_t)iDisplay);
1326 } while (true);
1327
1328 return rc;
1329}
1330
1331/** @todo call this also on reset? */
1332static int vbvaVHWAEnable(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool bEnable)
1333{
1334 const VBOXVHWACMD_TYPE enmType = bEnable ? VBOXVHWACMD_TYPE_HH_ENABLE : VBOXVHWACMD_TYPE_HH_DISABLE;
1335 VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(enmType, 0, 0);
1336 Assert(pCmd);
1337 if(pCmd)
1338 {
1339 int rc = vbvaVHWAHHPost(pDevIns, pThis, pThisCC, pCmd, NULL, NULL, NULL);
1340 vbvaVHWAHHCommandRelease(pCmd);
1341 return rc;
1342 }
1343 return VERR_OUT_OF_RESOURCES;
1344}
1345
1346int vboxVBVASaveStatePrep(PPDMDEVINS pDevIns)
1347{
1348 /* ensure we have no pending commands */
1349 return vbvaVHWAEnable(pDevIns, PDMDEVINS_2_DATA(pDevIns, PVGASTATE), PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC), false);
1350}
1351
1352int vboxVBVASaveStateDone(PPDMDEVINS pDevIns)
1353{
1354 /* ensure we have no pending commands */
1355 return vbvaVHWAEnable(pDevIns, PDMDEVINS_2_DATA(pDevIns, PVGASTATE), PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC), true);
1356}
1357
1358
1359/**
1360 * @interface_method_impl{PDMIDISPLAYVBVACALLBACKS,pfnVHWACommandCompleteAsync}
1361 */
1362DECLCALLBACK(int) vbvaR3VHWACommandCompleteAsync(PPDMIDISPLAYVBVACALLBACKS pInterface,
1363 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCmd)
1364{
1365 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IVBVACallbacks);
1366 PPDMDEVINS pDevIns = pThisCC->pDevIns;
1367 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
1368 int rc;
1369
1370 Log(("VGA Command <<< Async rc %d %#p, %d\n", pCmd->rc, pCmd, pCmd->enmCmd));
1371
1372 if ((uintptr_t)pCmd - (uintptr_t)pThisCC->pbVRam < pThis->vram_size)
1373 {
1374 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
1375 Assert(!(pCmd->Flags & VBOXVHWACMD_FLAG_HH_CMD));
1376 Assert(pCmd->Flags & VBOXVHWACMD_FLAG_HG_ASYNCH);
1377#ifdef VBOX_WITH_WDDM
1378 if (pThis->fGuestCaps & VBVACAPS_COMPLETEGCMD_BY_IOREAD)
1379 {
1380 rc = HGSMICompleteGuestCommand(pIns, pCmd, !!(pCmd->Flags & VBOXVHWACMD_FLAG_GH_ASYNCH_IRQ));
1381 AssertRC(rc);
1382 }
1383 else
1384#endif
1385 {
1386 VBVAHOSTCMD RT_UNTRUSTED_VOLATILE_GUEST *pHostCmd = NULL; /* Shut up MSC. */
1387 if (pCmd->Flags & VBOXVHWACMD_FLAG_GH_ASYNCH_EVENT)
1388 {
1389 rc = HGSMIHostCommandAlloc(pIns,
1390 (void RT_UNTRUSTED_VOLATILE_GUEST **)&pHostCmd,
1391 VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDEVENT)),
1392 HGSMI_CH_VBVA,
1393 VBVAHG_EVENT);
1394 AssertRC(rc);
1395 if (RT_SUCCESS(rc))
1396 {
1397 memset((void *)pHostCmd, 0 , VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDEVENT)));
1398 pHostCmd->iDstID = pCmd->iDisplay;
1399 pHostCmd->customOpCode = 0;
1400 VBVAHOSTCMDEVENT RT_UNTRUSTED_VOLATILE_GUEST *pBody = VBVAHOSTCMD_BODY(pHostCmd, VBVAHOSTCMDEVENT);
1401 pBody->pEvent = pCmd->GuestVBVAReserved1;
1402 }
1403 }
1404 else
1405 {
1406 HGSMIOFFSET offCmd = HGSMIPointerToOffsetHost(pIns, pCmd);
1407 Assert(offCmd != HGSMIOFFSET_VOID);
1408 if (offCmd != HGSMIOFFSET_VOID)
1409 {
1410 rc = HGSMIHostCommandAlloc(pIns,
1411 (void RT_UNTRUSTED_VOLATILE_GUEST **)&pHostCmd,
1412 VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDVHWACMDCOMPLETE)),
1413 HGSMI_CH_VBVA,
1414 VBVAHG_DISPLAY_CUSTOM);
1415 AssertRC(rc);
1416 if (RT_SUCCESS(rc))
1417 {
1418 memset((void *)pHostCmd, 0 , VBVAHOSTCMD_SIZE(sizeof(VBVAHOSTCMDVHWACMDCOMPLETE)));
1419 pHostCmd->iDstID = pCmd->iDisplay;
1420 pHostCmd->customOpCode = VBVAHG_DCUSTOM_VHWA_CMDCOMPLETE;
1421 VBVAHOSTCMDVHWACMDCOMPLETE RT_UNTRUSTED_VOLATILE_GUEST *pBody
1422 = VBVAHOSTCMD_BODY(pHostCmd, VBVAHOSTCMDVHWACMDCOMPLETE);
1423 pBody->offCmd = offCmd;
1424 }
1425 }
1426 else
1427 rc = VERR_INVALID_PARAMETER;
1428 }
1429
1430 if (RT_SUCCESS(rc))
1431 {
1432 rc = HGSMIHostCommandSubmitAndFreeAsynch(pIns, pHostCmd, RT_BOOL(pCmd->Flags & VBOXVHWACMD_FLAG_GH_ASYNCH_IRQ));
1433 AssertRC(rc);
1434 if (RT_SUCCESS(rc))
1435 return rc;
1436
1437 HGSMIHostCommandFree (pIns, pHostCmd);
1438 }
1439 }
1440 }
1441 else
1442 {
1443 Assert(pCmd->Flags & VBOXVHWACMD_FLAG_HH_CMD);
1444 PFNVBOXVHWA_HH_CALLBACK pfn = VBOXVHWA_HH_CALLBACK_GET(pCmd);
1445 if (pfn)
1446 pfn(VBOXVHWA_HH_CALLBACK_GET_ARG(pCmd));
1447 rc = VINF_SUCCESS;
1448 }
1449 return rc;
1450}
1451
1452typedef struct VBOXVBVASAVEDSTATECBDATA
1453{
1454 PSSMHANDLE pSSM;
1455 int rc;
1456 bool ab2DOn[VBOX_VIDEO_MAX_SCREENS];
1457} VBOXVBVASAVEDSTATECBDATA, *PVBOXVBVASAVEDSTATECBDATA;
1458
1459/**
1460 * @callback_method_impl{FNVBOXVHWAHHCMDPOSTCB}
1461 */
1462static DECLCALLBACK(bool) vboxVBVASaveStateBeginPostCb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
1463 VBOXVHWACMD *pCmd, uint32_t iDisplay, int rc, void *pvContext)
1464{
1465 RT_NOREF(pDevIns, pThis, pThisCC, pCmd);
1466 PVBOXVBVASAVEDSTATECBDATA pData = (PVBOXVBVASAVEDSTATECBDATA)pvContext;
1467 if (RT_FAILURE(pData->rc))
1468 return false;
1469 if (RT_FAILURE(rc))
1470 {
1471 pData->rc = rc;
1472 return false;
1473 }
1474
1475 Assert(iDisplay < RT_ELEMENTS(pData->ab2DOn));
1476 if (iDisplay >= RT_ELEMENTS(pData->ab2DOn))
1477 {
1478 pData->rc = VERR_INVALID_PARAMETER;
1479 return false;
1480 }
1481
1482 Assert(RT_SUCCESS(pCmd->rc) || pCmd->rc == VERR_NOT_IMPLEMENTED);
1483 if (RT_SUCCESS(pCmd->rc))
1484 {
1485 pData->ab2DOn[iDisplay] = true;
1486 }
1487 else if (pCmd->rc != VERR_NOT_IMPLEMENTED)
1488 {
1489 pData->rc = pCmd->rc;
1490 return false;
1491 }
1492
1493 return true;
1494}
1495
1496/**
1497 * @callback_method_impl{FNVBOXVHWAHHCMDPRECB}
1498 */
1499static DECLCALLBACK(bool) vboxVBVASaveStatePerformPreCb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
1500 VBOXVHWACMD *pCmd, uint32_t iDisplay, void *pvContext)
1501{
1502 RT_NOREF(pThis, pThisCC, pCmd);
1503 PVBOXVBVASAVEDSTATECBDATA pData = (PVBOXVBVASAVEDSTATECBDATA)pvContext;
1504 if (RT_FAILURE(pData->rc))
1505 return false;
1506
1507 Assert(iDisplay < RT_ELEMENTS(pData->ab2DOn));
1508 if (iDisplay >= RT_ELEMENTS(pData->ab2DOn))
1509 {
1510 pData->rc = VERR_INVALID_PARAMETER;
1511 return false;
1512 }
1513
1514 int rc;
1515 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1516
1517 if (pData->ab2DOn[iDisplay])
1518 {
1519 rc = pHlp->pfnSSMPutU32(pData->pSSM, VBOXVBVASAVEDSTATE_VHWAAVAILABLE_MAGIC); AssertRC(rc);
1520 if (RT_FAILURE(rc))
1521 {
1522 pData->rc = rc;
1523 return false;
1524 }
1525 return true;
1526 }
1527
1528 rc = pHlp->pfnSSMPutU32(pData->pSSM, VBOXVBVASAVEDSTATE_VHWAUNAVAILABLE_MAGIC); AssertRC(rc);
1529 if (RT_FAILURE(rc))
1530 {
1531 pData->rc = rc;
1532 return false;
1533 }
1534
1535 return false;
1536}
1537
1538/**
1539 * @callback_method_impl{FNVBOXVHWAHHCMDPOSTCB}
1540 */
1541static DECLCALLBACK(bool) vboxVBVASaveStateEndPreCb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
1542 VBOXVHWACMD *pCmd, uint32_t iDisplay, void *pvContext)
1543{
1544 RT_NOREF(pDevIns, pThis, pThisCC, pCmd);
1545 PVBOXVBVASAVEDSTATECBDATA pData = (PVBOXVBVASAVEDSTATECBDATA)pvContext;
1546 Assert(iDisplay < RT_ELEMENTS(pData->ab2DOn));
1547 if (pData->ab2DOn[iDisplay])
1548 return true;
1549 return false;
1550}
1551
1552/**
1553 * @callback_method_impl{FNVBOXVHWAHHCMDPOSTCB}
1554 */
1555static DECLCALLBACK(bool) vboxVBVALoadStatePerformPostCb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
1556 VBOXVHWACMD *pCmd, uint32_t iDisplay, int rc, void *pvContext)
1557{
1558 RT_NOREF(pThis, pThisCC, pCmd);
1559 PVBOXVBVASAVEDSTATECBDATA pData = (PVBOXVBVASAVEDSTATECBDATA)pvContext;
1560 if (RT_FAILURE(pData->rc))
1561 return false;
1562 if (RT_FAILURE(rc))
1563 {
1564 pData->rc = rc;
1565 return false;
1566 }
1567
1568 Assert(iDisplay < RT_ELEMENTS(pData->ab2DOn));
1569 if (iDisplay >= RT_ELEMENTS(pData->ab2DOn))
1570 {
1571 pData->rc = VERR_INVALID_PARAMETER;
1572 return false;
1573 }
1574
1575 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1576 Assert(RT_SUCCESS(pCmd->rc) || pCmd->rc == VERR_NOT_IMPLEMENTED);
1577 if (pCmd->rc == VERR_NOT_IMPLEMENTED)
1578 {
1579 pData->rc = pHlp->pfnSSMSkipToEndOfUnit(pData->pSSM);
1580 AssertRC(pData->rc);
1581 return false;
1582 }
1583 if (RT_FAILURE(pCmd->rc))
1584 {
1585 pData->rc = pCmd->rc;
1586 return false;
1587 }
1588
1589 return true;
1590}
1591
1592/**
1593 * @callback_method_impl{FNVBOXVHWAHHCMDPOSTCB}
1594 */
1595static DECLCALLBACK(bool) vboxVBVALoadStatePerformPreCb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
1596 VBOXVHWACMD *pCmd, uint32_t iDisplay, void *pvContext)
1597{
1598 RT_NOREF(pThis, pThisCC, pCmd);
1599 PVBOXVBVASAVEDSTATECBDATA pData = (PVBOXVBVASAVEDSTATECBDATA)pvContext;
1600 if (RT_FAILURE(pData->rc))
1601 return false;
1602
1603 Assert(iDisplay < RT_ELEMENTS(pData->ab2DOn));
1604 if (iDisplay >= RT_ELEMENTS(pData->ab2DOn))
1605 {
1606 pData->rc = VERR_INVALID_PARAMETER;
1607 return false;
1608 }
1609
1610 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1611 int rc;
1612 uint32_t u32;
1613 rc = pHlp->pfnSSMGetU32(pData->pSSM, &u32); AssertRC(rc);
1614 if (RT_FAILURE(rc))
1615 {
1616 pData->rc = rc;
1617 return false;
1618 }
1619
1620 switch (u32)
1621 {
1622 case VBOXVBVASAVEDSTATE_VHWAAVAILABLE_MAGIC:
1623 pData->ab2DOn[iDisplay] = true;
1624 return true;
1625 case VBOXVBVASAVEDSTATE_VHWAUNAVAILABLE_MAGIC:
1626 pData->ab2DOn[iDisplay] = false;
1627 return false;
1628 default:
1629 pData->rc = VERR_INVALID_STATE;
1630 return false;
1631 }
1632}
1633
1634#endif /* VBOX_WITH_VIDEOHWACCEL */
1635
1636static int vboxVBVASaveDevStateExec(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PVGASTATECC pThisCC, PSSMHANDLE pSSM)
1637{
1638 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
1639 int rc = HGSMIHostSaveStateExec(pHlp, pIns, pSSM);
1640 if (RT_SUCCESS(rc))
1641 {
1642 VGA_SAVED_STATE_PUT_MARKER(pSSM, 2);
1643
1644 /* Save VBVACONTEXT. */
1645 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pIns);
1646
1647 if (!pCtx)
1648 {
1649 AssertFailed();
1650
1651 /* Still write a valid value to the SSM. */
1652 rc = pHlp->pfnSSMPutU32 (pSSM, 0);
1653 AssertRCReturn(rc, rc);
1654 }
1655 else
1656 {
1657#ifdef DEBUG_sunlover
1658 dumpctx(pCtx);
1659#endif
1660
1661 rc = pHlp->pfnSSMPutU32 (pSSM, pCtx->cViews);
1662 AssertRCReturn(rc, rc);
1663
1664 uint32_t iView;
1665 for (iView = 0; iView < pCtx->cViews; iView++)
1666 {
1667 VBVAVIEW *pView = &pCtx->aViews[iView];
1668
1669 rc = pHlp->pfnSSMPutU32(pSSM, pView->view.u32ViewIndex);
1670 AssertRCReturn(rc, rc);
1671 rc = pHlp->pfnSSMPutU32(pSSM, pView->view.u32ViewOffset);
1672 AssertRCReturn(rc, rc);
1673 rc = pHlp->pfnSSMPutU32(pSSM, pView->view.u32ViewSize);
1674 AssertRCReturn(rc, rc);
1675 rc = pHlp->pfnSSMPutU32(pSSM, pView->view.u32MaxScreenSize);
1676 AssertRCReturn(rc, rc);
1677
1678 rc = pHlp->pfnSSMPutU32(pSSM, pView->screen.u32ViewIndex);
1679 AssertRCReturn(rc, rc);
1680 rc = pHlp->pfnSSMPutS32(pSSM, pView->screen.i32OriginX);
1681 AssertRCReturn(rc, rc);
1682 rc = pHlp->pfnSSMPutS32(pSSM, pView->screen.i32OriginY);
1683 AssertRCReturn(rc, rc);
1684 rc = pHlp->pfnSSMPutU32(pSSM, pView->screen.u32StartOffset);
1685 AssertRCReturn(rc, rc);
1686 rc = pHlp->pfnSSMPutU32(pSSM, pView->screen.u32LineSize);
1687 AssertRCReturn(rc, rc);
1688 rc = pHlp->pfnSSMPutU32(pSSM, pView->screen.u32Width);
1689 AssertRCReturn(rc, rc);
1690 rc = pHlp->pfnSSMPutU32(pSSM, pView->screen.u32Height);
1691 AssertRCReturn(rc, rc);
1692 rc = pHlp->pfnSSMPutU16(pSSM, pView->screen.u16BitsPerPixel);
1693 AssertRCReturn(rc, rc);
1694 rc = pHlp->pfnSSMPutU16(pSSM, pView->screen.u16Flags);
1695 AssertRCReturn(rc, rc);
1696
1697 rc = pHlp->pfnSSMPutU32(pSSM, pView->vbva.guest.pVBVA? pView->vbva.u32VBVAOffset: HGSMIOFFSET_VOID);
1698 AssertRCReturn(rc, rc);
1699
1700 rc = pHlp->pfnSSMPutU32(pSSM, pView->vbva.partialRecord.cb);
1701 AssertRCReturn(rc, rc);
1702
1703 if (pView->vbva.partialRecord.cb > 0)
1704 {
1705 rc = pHlp->pfnSSMPutMem(pSSM, pView->vbva.partialRecord.pu8, pView->vbva.partialRecord.cb);
1706 AssertRCReturn(rc, rc);
1707 }
1708 }
1709
1710 /* Save mouse pointer shape information. */
1711 rc = pHlp->pfnSSMPutBool(pSSM, pCtx->mouseShapeInfo.fSet);
1712 AssertRCReturn(rc, rc);
1713 rc = pHlp->pfnSSMPutBool(pSSM, pCtx->mouseShapeInfo.fVisible);
1714 AssertRCReturn(rc, rc);
1715 rc = pHlp->pfnSSMPutBool(pSSM, pCtx->mouseShapeInfo.fAlpha);
1716 AssertRCReturn(rc, rc);
1717 rc = pHlp->pfnSSMPutU32(pSSM, pCtx->mouseShapeInfo.u32HotX);
1718 AssertRCReturn(rc, rc);
1719 rc = pHlp->pfnSSMPutU32(pSSM, pCtx->mouseShapeInfo.u32HotY);
1720 AssertRCReturn(rc, rc);
1721 rc = pHlp->pfnSSMPutU32(pSSM, pCtx->mouseShapeInfo.u32Width);
1722 AssertRCReturn(rc, rc);
1723 rc = pHlp->pfnSSMPutU32(pSSM, pCtx->mouseShapeInfo.u32Height);
1724 AssertRCReturn(rc, rc);
1725 rc = pHlp->pfnSSMPutU32(pSSM, pCtx->mouseShapeInfo.cbShape);
1726 AssertRCReturn(rc, rc);
1727 if (pCtx->mouseShapeInfo.cbShape)
1728 {
1729 rc = pHlp->pfnSSMPutMem(pSSM, pCtx->mouseShapeInfo.pu8Shape, pCtx->mouseShapeInfo.cbShape);
1730 AssertRCReturn(rc, rc);
1731 }
1732
1733#ifdef VBOX_WITH_WDDM
1734 /* Size of some additional data. For future extensions. */
1735 rc = pHlp->pfnSSMPutU32(pSSM, 4);
1736 AssertRCReturn(rc, rc);
1737 rc = pHlp->pfnSSMPutU32(pSSM, pThis->fGuestCaps);
1738 AssertRCReturn(rc, rc);
1739#else
1740 /* Size of some additional data. For future extensions. */
1741 rc = pHlp->pfnSSMPutU32(pSSM, 0);
1742 AssertRCReturn(rc, rc);
1743#endif
1744 rc = pHlp->pfnSSMPutU32(pSSM, RT_ELEMENTS(pCtx->aModeHints));
1745 AssertRCReturn(rc, rc);
1746 rc = pHlp->pfnSSMPutU32(pSSM, sizeof(VBVAMODEHINT));
1747 AssertRCReturn(rc, rc);
1748 for (unsigned i = 0; i < RT_ELEMENTS(pCtx->aModeHints); ++i)
1749 {
1750 rc = pHlp->pfnSSMPutMem(pSSM, &pCtx->aModeHints[i], sizeof(VBVAMODEHINT));
1751 AssertRCReturn(rc, rc);
1752 }
1753 }
1754 }
1755
1756 return rc;
1757}
1758
1759int vboxVBVASaveStateExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1760{
1761 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
1762 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
1763 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1764 int rc;
1765#ifdef VBOX_WITH_VIDEOHWACCEL
1766 VBOXVBVASAVEDSTATECBDATA VhwaData = {0};
1767 VhwaData.pSSM = pSSM;
1768 uint32_t cbCmd = sizeof (VBOXVHWACMD_HH_SAVESTATE_SAVEPERFORM); /* maximum cmd size */
1769 VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEBEGIN, 0, cbCmd);
1770 Assert(pCmd);
1771 if (pCmd)
1772 {
1773 vbvaVHWAHHPost(pDevIns, pThis, pThisCC, pCmd, NULL, vboxVBVASaveStateBeginPostCb, &VhwaData);
1774 rc = VhwaData.rc;
1775 AssertRC(rc);
1776 if (RT_SUCCESS(rc))
1777 {
1778#endif
1779 rc = vboxVBVASaveDevStateExec(pHlp, pThis, pThisCC, pSSM);
1780 AssertRC(rc);
1781#ifdef VBOX_WITH_VIDEOHWACCEL
1782 if (RT_SUCCESS(rc))
1783 {
1784 vbvaVHWAHHCommandReinit(pCmd, VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEPERFORM, 0);
1785 VBOXVHWACMD_HH_SAVESTATE_SAVEPERFORM *pSave = VBOXVHWACMD_BODY_HOST_HEAP(pCmd, VBOXVHWACMD_HH_SAVESTATE_SAVEPERFORM);
1786 pSave->pSSM = pSSM;
1787 vbvaVHWAHHPost(pDevIns, pThis, pThisCC, pCmd, vboxVBVASaveStatePerformPreCb, NULL, &VhwaData);
1788 rc = VhwaData.rc;
1789 AssertRC(rc);
1790 if (RT_SUCCESS(rc))
1791 {
1792 rc = vbvaVHWACommandSavePending(pHlp, pThis, pThisCC, pSSM);
1793 AssertRCReturn(rc, rc);
1794
1795 vbvaVHWAHHCommandReinit(pCmd, VBOXVHWACMD_TYPE_HH_SAVESTATE_SAVEEND, 0);
1796 vbvaVHWAHHPost(pDevIns, pThis, pThisCC, pCmd, vboxVBVASaveStateEndPreCb, NULL, &VhwaData);
1797 rc = VhwaData.rc;
1798 AssertRC(rc);
1799 }
1800 }
1801 }
1802
1803 vbvaVHWAHHCommandRelease(pCmd);
1804 }
1805 else
1806 rc = VERR_OUT_OF_RESOURCES;
1807#else
1808 if (RT_SUCCESS(rc))
1809 {
1810 for (uint32_t i = 0; i < pThis->cMonitors; ++i)
1811 {
1812 rc = pHlp->pfnSSMPutU32(pSSM, VBOXVBVASAVEDSTATE_VHWAUNAVAILABLE_MAGIC);
1813 AssertRCReturn(rc, rc);
1814 }
1815 }
1816
1817 /* no pending commands */
1818 pHlp->pfnSSMPutU32(pSSM, 0);
1819#endif
1820 return rc;
1821}
1822
1823int vboxVBVALoadStateExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion)
1824{
1825 if (uVersion < VGA_SAVEDSTATE_VERSION_HGSMI)
1826 {
1827 /* Nothing was saved. */
1828 return VINF_SUCCESS;
1829 }
1830
1831 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
1832 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
1833 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
1834 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1835 int rc = HGSMIHostLoadStateExec(pHlp, pIns, pSSM, uVersion);
1836 if (RT_SUCCESS(rc))
1837 {
1838 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 2);
1839
1840 /* Load VBVACONTEXT. */
1841 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pIns);
1842
1843 if (!pCtx)
1844 {
1845 /* This should not happen. */
1846 AssertFailed();
1847 rc = VERR_INVALID_PARAMETER;
1848 }
1849 else
1850 {
1851 uint32_t cViews = 0;
1852 rc = pHlp->pfnSSMGetU32 (pSSM, &cViews);
1853 AssertRCReturn(rc, rc);
1854
1855 uint32_t iView;
1856 for (iView = 0; iView < cViews; iView++)
1857 {
1858 VBVAVIEW *pView = &pCtx->aViews[iView];
1859
1860 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->view.u32ViewIndex);
1861 AssertRCReturn(rc, rc);
1862 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->view.u32ViewOffset);
1863 AssertRCReturn(rc, rc);
1864 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->view.u32ViewSize);
1865 AssertRCReturn(rc, rc);
1866 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->view.u32MaxScreenSize);
1867 AssertRCReturn(rc, rc);
1868
1869 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->screen.u32ViewIndex);
1870 AssertRCReturn(rc, rc);
1871 rc = pHlp->pfnSSMGetS32 (pSSM, &pView->screen.i32OriginX);
1872 AssertRCReturn(rc, rc);
1873 rc = pHlp->pfnSSMGetS32 (pSSM, &pView->screen.i32OriginY);
1874 AssertRCReturn(rc, rc);
1875 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->screen.u32StartOffset);
1876 AssertRCReturn(rc, rc);
1877 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->screen.u32LineSize);
1878 AssertRCReturn(rc, rc);
1879 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->screen.u32Width);
1880 AssertRCReturn(rc, rc);
1881 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->screen.u32Height);
1882 AssertRCReturn(rc, rc);
1883 rc = pHlp->pfnSSMGetU16 (pSSM, &pView->screen.u16BitsPerPixel);
1884 AssertRCReturn(rc, rc);
1885 rc = pHlp->pfnSSMGetU16 (pSSM, &pView->screen.u16Flags);
1886 AssertRCReturn(rc, rc);
1887
1888 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->vbva.u32VBVAOffset);
1889 AssertRCReturn(rc, rc);
1890
1891 rc = pHlp->pfnSSMGetU32 (pSSM, &pView->vbva.partialRecord.cb);
1892 AssertRCReturn(rc, rc);
1893
1894 if (pView->vbva.partialRecord.cb == 0)
1895 {
1896 pView->vbva.partialRecord.pu8 = NULL;
1897 }
1898 else
1899 {
1900 Assert(pView->vbva.partialRecord.pu8 == NULL); /* Should be it. */
1901
1902 uint8_t *pu8 = (uint8_t *)RTMemAlloc(pView->vbva.partialRecord.cb);
1903
1904 if (!pu8)
1905 {
1906 return VERR_NO_MEMORY;
1907 }
1908
1909 pView->vbva.partialRecord.pu8 = pu8;
1910
1911 rc = pHlp->pfnSSMGetMem (pSSM, pView->vbva.partialRecord.pu8, pView->vbva.partialRecord.cb);
1912 AssertRCReturn(rc, rc);
1913 }
1914
1915 if (pView->vbva.u32VBVAOffset == HGSMIOFFSET_VOID)
1916 {
1917 pView->vbva.guest.pVBVA = NULL;
1918 }
1919 else
1920 {
1921 pView->vbva.guest.pVBVA = (VBVABUFFER *)HGSMIOffsetToPointerHost(pIns, pView->vbva.u32VBVAOffset);
1922 }
1923 }
1924
1925 if (uVersion > VGA_SAVEDSTATE_VERSION_WITH_CONFIG)
1926 {
1927 /* Read mouse pointer shape information. */
1928 rc = pHlp->pfnSSMGetBool (pSSM, &pCtx->mouseShapeInfo.fSet);
1929 AssertRCReturn(rc, rc);
1930 rc = pHlp->pfnSSMGetBool (pSSM, &pCtx->mouseShapeInfo.fVisible);
1931 AssertRCReturn(rc, rc);
1932 rc = pHlp->pfnSSMGetBool (pSSM, &pCtx->mouseShapeInfo.fAlpha);
1933 AssertRCReturn(rc, rc);
1934 rc = pHlp->pfnSSMGetU32 (pSSM, &pCtx->mouseShapeInfo.u32HotX);
1935 AssertRCReturn(rc, rc);
1936 rc = pHlp->pfnSSMGetU32 (pSSM, &pCtx->mouseShapeInfo.u32HotY);
1937 AssertRCReturn(rc, rc);
1938 rc = pHlp->pfnSSMGetU32 (pSSM, &pCtx->mouseShapeInfo.u32Width);
1939 AssertRCReturn(rc, rc);
1940 rc = pHlp->pfnSSMGetU32 (pSSM, &pCtx->mouseShapeInfo.u32Height);
1941 AssertRCReturn(rc, rc);
1942 rc = pHlp->pfnSSMGetU32 (pSSM, &pCtx->mouseShapeInfo.cbShape);
1943 AssertRCReturn(rc, rc);
1944 if (pCtx->mouseShapeInfo.cbShape)
1945 {
1946 pCtx->mouseShapeInfo.pu8Shape = (uint8_t *)RTMemAlloc(pCtx->mouseShapeInfo.cbShape);
1947 if (pCtx->mouseShapeInfo.pu8Shape == NULL)
1948 {
1949 return VERR_NO_MEMORY;
1950 }
1951 pCtx->mouseShapeInfo.cbAllocated = pCtx->mouseShapeInfo.cbShape;
1952 rc = pHlp->pfnSSMGetMem (pSSM, pCtx->mouseShapeInfo.pu8Shape, pCtx->mouseShapeInfo.cbShape);
1953 AssertRCReturn(rc, rc);
1954 }
1955 else
1956 {
1957 pCtx->mouseShapeInfo.pu8Shape = NULL;
1958 }
1959
1960 /* Size of some additional data. For future extensions. */
1961 uint32_t cbExtra = 0;
1962 rc = pHlp->pfnSSMGetU32 (pSSM, &cbExtra);
1963 AssertRCReturn(rc, rc);
1964#ifdef VBOX_WITH_WDDM
1965 if (cbExtra >= 4)
1966 {
1967 rc = pHlp->pfnSSMGetU32 (pSSM, &pThis->fGuestCaps);
1968 AssertRCReturn(rc, rc);
1969 pThisCC->pDrv->pfnVBVAGuestCapabilityUpdate(pThisCC->pDrv, pThis->fGuestCaps);
1970 cbExtra -= 4;
1971 }
1972#endif
1973 if (cbExtra > 0)
1974 {
1975 rc = pHlp->pfnSSMSkip(pSSM, cbExtra);
1976 AssertRCReturn(rc, rc);
1977 }
1978
1979 if (uVersion >= VGA_SAVEDSTATE_VERSION_MODE_HINTS)
1980 {
1981 uint32_t cModeHints, cbModeHints;
1982 rc = pHlp->pfnSSMGetU32 (pSSM, &cModeHints);
1983 AssertRCReturn(rc, rc);
1984 rc = pHlp->pfnSSMGetU32 (pSSM, &cbModeHints);
1985 AssertRCReturn(rc, rc);
1986 memset(&pCtx->aModeHints, ~0, sizeof(pCtx->aModeHints));
1987 unsigned iHint;
1988 for (iHint = 0; iHint < cModeHints; ++iHint)
1989 {
1990 if ( cbModeHints <= sizeof(VBVAMODEHINT)
1991 && iHint < RT_ELEMENTS(pCtx->aModeHints))
1992 rc = pHlp->pfnSSMGetMem(pSSM, &pCtx->aModeHints[iHint],
1993 cbModeHints);
1994 else
1995 rc = pHlp->pfnSSMSkip(pSSM, cbModeHints);
1996 AssertRCReturn(rc, rc);
1997 }
1998 }
1999 }
2000
2001 pCtx->cViews = iView;
2002 LogFlowFunc(("%d views loaded\n", pCtx->cViews));
2003
2004 if (uVersion > VGA_SAVEDSTATE_VERSION_WDDM)
2005 {
2006 bool fLoadCommands;
2007
2008 if (uVersion < VGA_SAVEDSTATE_VERSION_FIXED_PENDVHWA)
2009 {
2010 const char *pcszOsArch = pHlp->pfnSSMHandleHostOSAndArch(pSSM);
2011 Assert(pcszOsArch);
2012 fLoadCommands = !pcszOsArch || RTStrNCmp(pcszOsArch, RT_STR_TUPLE("solaris"));
2013 }
2014 else
2015 fLoadCommands = true;
2016
2017#ifdef VBOX_WITH_VIDEOHWACCEL
2018 uint32_t cbCmd = sizeof (VBOXVHWACMD_HH_SAVESTATE_LOADPERFORM); /* maximum cmd size */
2019 VBOXVHWACMD *pCmd = vbvaVHWAHHCommandCreate(VBOXVHWACMD_TYPE_HH_SAVESTATE_LOADPERFORM, 0, cbCmd);
2020 Assert(pCmd);
2021 if(pCmd)
2022 {
2023 VBOXVBVASAVEDSTATECBDATA VhwaData = {0};
2024 VhwaData.pSSM = pSSM;
2025 VBOXVHWACMD_HH_SAVESTATE_LOADPERFORM *pLoad = VBOXVHWACMD_BODY_HOST_HEAP(pCmd, VBOXVHWACMD_HH_SAVESTATE_LOADPERFORM);
2026 pLoad->pSSM = pSSM;
2027 vbvaVHWAHHPost(pDevIns, pThis, pThisCC, pCmd, vboxVBVALoadStatePerformPreCb,
2028 vboxVBVALoadStatePerformPostCb, &VhwaData);
2029 rc = VhwaData.rc;
2030 vbvaVHWAHHCommandRelease(pCmd);
2031 AssertRCReturn(rc, rc);
2032
2033 if (fLoadCommands)
2034 {
2035 rc = vbvaVHWACommandLoadPending(pDevIns, pHlp, pThis, pThisCC, pSSM, uVersion);
2036 AssertRCReturn(rc, rc);
2037 }
2038 }
2039 else
2040 {
2041 rc = VERR_OUT_OF_RESOURCES;
2042 }
2043#else
2044 uint32_t u32;
2045
2046 for (uint32_t i = 0; i < pThis->cMonitors; ++i)
2047 {
2048 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
2049 AssertRCReturn(rc, rc);
2050
2051 if (u32 != VBOXVBVASAVEDSTATE_VHWAUNAVAILABLE_MAGIC)
2052 {
2053 LogRel(("VBVA: 2D data while 2D is not supported\n"));
2054 return VERR_NOT_SUPPORTED;
2055 }
2056 }
2057
2058 if (fLoadCommands)
2059 {
2060 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
2061 AssertRCReturn(rc, rc);
2062
2063 if (u32)
2064 {
2065 LogRel(("VBVA: 2D pending command while 2D is not supported\n"));
2066 return VERR_NOT_SUPPORTED;
2067 }
2068 }
2069#endif
2070 }
2071
2072#ifdef DEBUG_sunlover
2073 dumpctx(pCtx);
2074#endif
2075 }
2076 }
2077
2078 return rc;
2079}
2080
2081int vboxVBVALoadStateDone(PPDMDEVINS pDevIns)
2082{
2083 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
2084 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
2085 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2086 if (pCtx)
2087 {
2088 uint32_t iView;
2089 for (iView = 0; iView < pCtx->cViews; iView++)
2090 {
2091 VBVAVIEW *pView = &pCtx->aViews[iView];
2092 if (pView->vbva.guest.pVBVA)
2093 {
2094 int rc = vbvaEnable(pThis, pThisCC, pCtx, iView, pView->vbva.guest.pVBVA,
2095 pView->vbva.u32VBVAOffset, true /* fRestored */);
2096 if (RT_SUCCESS(rc))
2097 vbvaResize(pThisCC, pView, &pView->screen, false);
2098 else
2099 LogRel(("VBVA: can not restore: %Rrc\n", rc));
2100 }
2101 }
2102
2103 if (pCtx->mouseShapeInfo.fSet)
2104 vbvaUpdateMousePointerShape(pThisCC, &pCtx->mouseShapeInfo, true);
2105 }
2106
2107 return VINF_SUCCESS;
2108}
2109
2110void VBVARaiseIrq(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t fFlags)
2111{
2112 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSectIRQ, VERR_SEM_BUSY);
2113
2114 const uint32_t fu32CurrentGuestFlags = HGSMIGetHostGuestFlags(pThisCC->pHGSMI);
2115 if ((fu32CurrentGuestFlags & HGSMIHOSTFLAGS_IRQ) == 0)
2116 {
2117 /* No IRQ set yet. */
2118 Assert(pThis->fu32PendingGuestFlags == 0);
2119
2120 HGSMISetHostGuestFlags(pThisCC->pHGSMI, HGSMIHOSTFLAGS_IRQ | fFlags);
2121
2122 /* If VM is not running, the IRQ will be set in VBVAOnResume. */
2123 const VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
2124 if ( enmVMState == VMSTATE_RUNNING
2125 || enmVMState == VMSTATE_RUNNING_LS)
2126 PDMDevHlpPCISetIrqNoWait(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
2127 }
2128 else
2129 {
2130 /* IRQ already set, remember the new flags. */
2131 pThis->fu32PendingGuestFlags |= HGSMIHOSTFLAGS_IRQ | fFlags;
2132 }
2133
2134 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSectIRQ);
2135}
2136
2137void VBVAOnResume(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
2138{
2139 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSectIRQ, VERR_SEM_BUSY);
2140
2141 if (HGSMIGetHostGuestFlags(pThisCC->pHGSMI) & HGSMIHOSTFLAGS_IRQ)
2142 PDMDevHlpPCISetIrqNoWait(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
2143
2144 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSectIRQ);
2145}
2146
2147static int vbvaHandleQueryConf32(PVGASTATECC pThisCC, VBVACONF32 RT_UNTRUSTED_VOLATILE_GUEST *pConf32)
2148{
2149 uint32_t const idxQuery = pConf32->u32Index;
2150 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2151 LogFlowFunc(("VBVA_QUERY_CONF32: u32Index %d, u32Value 0x%x\n", idxQuery, pConf32->u32Value));
2152
2153 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2154 uint32_t uValue;
2155 if (idxQuery == VBOX_VBVA_CONF32_MONITOR_COUNT)
2156 uValue = pCtx->cViews;
2157 else if (idxQuery == VBOX_VBVA_CONF32_HOST_HEAP_SIZE)
2158 uValue = _64K; /** @todo a value calculated from the vram size */
2159 else if ( idxQuery == VBOX_VBVA_CONF32_MODE_HINT_REPORTING
2160 || idxQuery == VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING)
2161 uValue = VINF_SUCCESS;
2162 else if (idxQuery == VBOX_VBVA_CONF32_CURSOR_CAPABILITIES)
2163 uValue = VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE;
2164 else if (idxQuery == VBOX_VBVA_CONF32_SCREEN_FLAGS)
2165 uValue = VBVA_SCREEN_F_ACTIVE
2166 | VBVA_SCREEN_F_DISABLED
2167 | VBVA_SCREEN_F_BLANK
2168 | VBVA_SCREEN_F_BLANK2;
2169 else if (idxQuery == VBOX_VBVA_CONF32_MAX_RECORD_SIZE)
2170 uValue = VBVA_MAX_RECORD_SIZE;
2171 else if (idxQuery == UINT32_MAX)
2172 uValue = UINT32_MAX; /* Older GA uses this for sanity checking. See testQueryConf in HGSMIBase.cpp on branches. */
2173 else
2174 ASSERT_GUEST_MSG_FAILED_RETURN(("Invalid index %#x\n", idxQuery), VERR_INVALID_PARAMETER);
2175
2176 pConf32->u32Value = uValue;
2177 return VINF_SUCCESS;
2178}
2179
2180static int vbvaHandleSetConf32(VBVACONF32 RT_UNTRUSTED_VOLATILE_GUEST *pConf32)
2181{
2182 uint32_t const idxQuery = pConf32->u32Index;
2183 uint32_t const uValue = pConf32->u32Value;
2184 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2185 LogFlowFunc(("VBVA_SET_CONF32: u32Index %d, u32Value 0x%x\n", idxQuery, uValue));
2186
2187 if (idxQuery == VBOX_VBVA_CONF32_MONITOR_COUNT)
2188 { /* do nothing. this is a const. */ }
2189 else if (idxQuery == VBOX_VBVA_CONF32_HOST_HEAP_SIZE)
2190 { /* do nothing. this is a const. */ }
2191 else
2192 ASSERT_GUEST_MSG_FAILED_RETURN(("Invalid index %#x (value=%u)\n", idxQuery, uValue), VERR_INVALID_PARAMETER);
2193
2194 RT_NOREF_PV(uValue);
2195 return VINF_SUCCESS;
2196}
2197
2198static int vbvaHandleInfoHeap(PVGASTATECC pThisCC, const VBVAINFOHEAP RT_UNTRUSTED_VOLATILE_GUEST *pInfoHeap)
2199{
2200 uint32_t const offHeap = pInfoHeap->u32HeapOffset;
2201 uint32_t const cbHeap = pInfoHeap->u32HeapSize;
2202 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2203 LogFlowFunc(("VBVA_INFO_HEAP: offset 0x%x, size 0x%x\n", offHeap, cbHeap));
2204
2205 return HGSMIHostHeapSetup(pThisCC->pHGSMI, offHeap, cbHeap);
2206}
2207
2208static int vbvaInfoView(PVGASTATE pThis, PVGASTATER3 pThisCC, const VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_GUEST *pView)
2209{
2210 VBVAINFOVIEW view;
2211 RT_COPY_VOLATILE(view, *pView);
2212 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2213
2214 LogFlowFunc(("VBVA_INFO_VIEW: u32ViewIndex %d, u32ViewOffset 0x%x, u32ViewSize 0x%x, u32MaxScreenSize 0x%x\n",
2215 view.u32ViewIndex, view.u32ViewOffset, view.u32ViewSize, view.u32MaxScreenSize));
2216
2217 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2218 ASSERT_GUEST_LOGREL_MSG_RETURN( view.u32ViewIndex < pCtx->cViews
2219 && view.u32ViewOffset <= pThis->vram_size
2220 && view.u32ViewSize <= pThis->vram_size
2221 && view.u32ViewOffset <= pThis->vram_size - view.u32ViewSize
2222 && view.u32MaxScreenSize <= view.u32ViewSize,
2223 ("index %d(%d), offset 0x%x, size 0x%x, max 0x%x, vram size 0x%x\n",
2224 view.u32ViewIndex, pCtx->cViews, view.u32ViewOffset, view.u32ViewSize,
2225 view.u32MaxScreenSize, pThis->vram_size),
2226 VERR_INVALID_PARAMETER);
2227 RT_UNTRUSTED_VALIDATED_FENCE();
2228
2229 pCtx->aViews[view.u32ViewIndex].view = view;
2230 return VINF_SUCCESS;
2231}
2232
2233static int vbvaInfoScreen(PVGASTATECC pThisCC, const VBVAINFOSCREEN RT_UNTRUSTED_VOLATILE_GUEST *pScreen)
2234{
2235 /*
2236 * Copy input into non-volatile buffer.
2237 */
2238 VBVAINFOSCREEN screen;
2239 RT_COPY_VOLATILE(screen, *pScreen);
2240 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2241 LogRel2(("VBVA: InfoScreen: [%d] @%d,%d %dx%d, line 0x%x, BPP %d, flags 0x%x\n",
2242 screen.u32ViewIndex, screen.i32OriginX, screen.i32OriginY,
2243 screen.u32Width, screen.u32Height,
2244 screen.u32LineSize, screen.u16BitsPerPixel, screen.u16Flags));
2245
2246 /*
2247 * Validate input.
2248 */
2249 /* Allow screen.u16BitsPerPixel == 0 because legacy guest code used it for screen blanking. */
2250 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2251 ASSERT_GUEST_LOGREL_MSG_RETURN(screen.u32ViewIndex < pCtx->cViews,
2252 ("Screen index %#x is out of bound (cViews=%#x)\n", screen.u32ViewIndex, pCtx->cViews),
2253 VERR_INVALID_PARAMETER);
2254 ASSERT_GUEST_LOGREL_MSG_RETURN( screen.u16BitsPerPixel <= 32
2255 && screen.u32Width <= UINT16_MAX
2256 && screen.u32Height <= UINT16_MAX
2257 && screen.u32LineSize <= UINT16_MAX * UINT32_C(4),
2258 ("One or more values out of range: u16BitsPerPixel=%#x u32Width=%#x u32Height=%#x u32LineSize=%#x\n",
2259 screen.u16BitsPerPixel, screen.u32Width, screen.u32Height, screen.u32LineSize),
2260 VERR_INVALID_PARAMETER);
2261 RT_UNTRUSTED_VALIDATED_FENCE();
2262
2263 const VBVAINFOVIEW *pView = &pCtx->aViews[screen.u32ViewIndex].view;
2264 const uint32_t cbPerPixel = (screen.u16BitsPerPixel + 7) / 8;
2265 ASSERT_GUEST_LOGREL_MSG_RETURN(screen.u32Width <= screen.u32LineSize / (cbPerPixel ? cbPerPixel : 1),
2266 ("u32Width=%#x u32LineSize=%3x cbPerPixel=%#x\n",
2267 screen.u32Width, screen.u32LineSize, cbPerPixel),
2268 VERR_INVALID_PARAMETER);
2269
2270 const uint64_t u64ScreenSize = (uint64_t)screen.u32LineSize * screen.u32Height;
2271
2272 ASSERT_GUEST_LOGREL_MSG_RETURN( screen.u32StartOffset <= pView->u32ViewSize
2273 && u64ScreenSize <= pView->u32MaxScreenSize
2274 && screen.u32StartOffset <= pView->u32ViewSize - (uint32_t)u64ScreenSize,
2275 ("u32StartOffset=%#x u32ViewSize=%#x u64ScreenSize=%#RX64 u32MaxScreenSize=%#x\n",
2276 screen.u32StartOffset, pView->u32ViewSize, u64ScreenSize, pView->u32MaxScreenSize),
2277 VERR_INVALID_PARAMETER);
2278 RT_UNTRUSTED_VALIDATED_FENCE();
2279
2280 /*
2281 * Do the job.
2282 */
2283 vbvaResize(pThisCC, &pCtx->aViews[screen.u32ViewIndex], &screen, true);
2284 return VINF_SUCCESS;
2285}
2286
2287#ifdef UNUSED_FUNCTION
2288int VBVAGetInfoViewAndScreen(PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t u32ViewIndex, VBVAINFOVIEW *pView, VBVAINFOSCREEN *pScreen)
2289{
2290 if (u32ViewIndex >= pThis->cMonitors)
2291 return VERR_INVALID_PARAMETER;
2292
2293 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
2294 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext (pIns);
2295
2296 if (pView)
2297 *pView = pCtx->aViews[u32ViewIndex].view;
2298
2299 if (pScreen)
2300 *pScreen = pCtx->aViews[u32ViewIndex].screen;
2301
2302 return VINF_SUCCESS;
2303}
2304#endif
2305
2306static int vbvaHandleEnable(PVGASTATE pThis, PVGASTATER3 pThisCC, uint32_t fEnableFlags, uint32_t offEnable, uint32_t idScreen)
2307{
2308 LogFlowFunc(("VBVA_ENABLE[%u]: fEnableFlags=0x%x offEnable=%#x\n", idScreen, fEnableFlags, offEnable));
2309 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
2310 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pIns);
2311
2312 /*
2313 * Validate input.
2314 */
2315 ASSERT_GUEST_LOGREL_MSG_RETURN(idScreen < pCtx->cViews, ("idScreen=%#x cViews=%#x\n", idScreen, pCtx->cViews), VERR_INVALID_PARAMETER);
2316 ASSERT_GUEST_LOGREL_MSG_RETURN( (fEnableFlags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_ENABLE
2317 || (fEnableFlags & (VBVA_F_ENABLE | VBVA_F_DISABLE)) == VBVA_F_DISABLE,
2318 ("fEnableFlags=%#x\n", fEnableFlags),
2319 VERR_INVALID_PARAMETER);
2320 if (fEnableFlags & VBVA_F_ENABLE)
2321 {
2322 ASSERT_GUEST_LOGREL_MSG_RETURN(offEnable < pThis->vram_size,
2323 ("offEnable=%#x vram_size=%#x\n", offEnable, pThis->vram_size),
2324 VERR_INVALID_PARAMETER);
2325 if (fEnableFlags & VBVA_F_ABSOFFSET)
2326 /* Offset from VRAM start. */
2327 ASSERT_GUEST_LOGREL_MSG_RETURN( pThis->vram_size >= RT_UOFFSETOF(VBVABUFFER, au8Data)
2328 && offEnable <= pThis->vram_size - RT_UOFFSETOF(VBVABUFFER, au8Data),
2329 ("offEnable=%#x vram_size=%#x\n", offEnable, pThis->vram_size),
2330 VERR_INVALID_PARAMETER);
2331 else
2332 {
2333 /* Offset from the view start. We'd be using idScreen here to fence required. */
2334 RT_UNTRUSTED_VALIDATED_FENCE();
2335 const VBVAINFOVIEW *pView = &pCtx->aViews[idScreen].view;
2336 ASSERT_GUEST_LOGREL_MSG_RETURN( pThis->vram_size - offEnable >= pView->u32ViewOffset
2337 && pView->u32ViewSize >= RT_UOFFSETOF(VBVABUFFER, au8Data)
2338 && offEnable <= pView->u32ViewSize - RT_UOFFSETOF(VBVABUFFER, au8Data),
2339 ("offEnable=%#x vram_size=%#x view: %#x LB %#x\n",
2340 offEnable, pThis->vram_size, pView->u32ViewOffset, pView->u32ViewSize),
2341 VERR_INVALID_PARAMETER);
2342 offEnable += pView->u32ViewOffset;
2343 }
2344 ASSERT_GUEST_LOGREL_MSG_RETURN(HGSMIIsOffsetValid(pIns, offEnable),
2345 ("offEnable=%#x area %#x LB %#x\n",
2346 offEnable, HGSMIGetAreaOffset(pIns), HGSMIGetAreaSize(pIns)),
2347 VERR_INVALID_PARAMETER);
2348 }
2349 RT_UNTRUSTED_VALIDATED_FENCE();
2350
2351 /*
2352 * Execute.
2353 */
2354 int rc = VINF_SUCCESS;
2355 if (fEnableFlags & VBVA_F_ENABLE)
2356 {
2357 VBVABUFFER RT_UNTRUSTED_VOLATILE_GUEST *pVBVA
2358 = (VBVABUFFER RT_UNTRUSTED_VOLATILE_GUEST *)HGSMIOffsetToPointerHost(pIns, offEnable);
2359 ASSERT_GUEST_LOGREL_RETURN(pVBVA, VERR_INVALID_PARAMETER); /* already check above, but let's be careful. */
2360
2361 /* Process any pending orders and empty the VBVA ring buffer. */
2362 vbvaFlush(pThis, pThisCC, pCtx);
2363
2364 rc = vbvaEnable(pThis, pThisCC, pCtx, idScreen, pVBVA, offEnable, false /* fRestored */);
2365 if (RT_FAILURE(rc))
2366 LogRelMax(8, ("VBVA: can not enable: %Rrc\n", rc));
2367 }
2368 else
2369 rc = vbvaDisable(pThis, pThisCC, pCtx, idScreen);
2370 return rc;
2371}
2372
2373static int vbvaHandleQueryModeHints(PVGASTATECC pThisCC, VBVAQUERYMODEHINTS volatile *pQueryModeHints, HGSMISIZE cbBuffer)
2374{
2375 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
2376 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pIns);
2377
2378 /*
2379 * Copy and validate the request.
2380 */
2381 uint16_t const cHintsQueried = pQueryModeHints->cHintsQueried;
2382 uint16_t const cbHintStructureGuest = pQueryModeHints->cbHintStructureGuest;
2383 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2384
2385 LogRelFlowFunc(("VBVA: HandleQueryModeHints: cHintsQueried=%RU16, cbHintStructureGuest=%RU16\n",
2386 cHintsQueried, cbHintStructureGuest));
2387 ASSERT_GUEST_RETURN(cbBuffer >= sizeof(VBVAQUERYMODEHINTS) + (uint32_t)cHintsQueried * cbHintStructureGuest,
2388 VERR_INVALID_PARAMETER);
2389 RT_UNTRUSTED_VALIDATED_FENCE();
2390
2391 /*
2392 * Produce the requested data.
2393 */
2394 uint8_t *pbHint = (uint8_t *)(pQueryModeHints + 1);
2395 memset(pbHint, ~0, cbBuffer - sizeof(VBVAQUERYMODEHINTS));
2396
2397 for (unsigned iHint = 0; iHint < cHintsQueried && iHint < VBOX_VIDEO_MAX_SCREENS; ++iHint)
2398 {
2399 memcpy(pbHint, &pCtx->aModeHints[iHint], RT_MIN(cbHintStructureGuest, sizeof(VBVAMODEHINT)));
2400 pbHint += cbHintStructureGuest;
2401 Assert((uintptr_t)(pbHint - (uint8_t *)pQueryModeHints) <= cbBuffer);
2402 }
2403
2404 return VINF_SUCCESS;
2405}
2406
2407/*
2408 *
2409 * New VBVA uses a new interface id: #define VBE_DISPI_ID_VBOX_VIDEO 0xBE01
2410 *
2411 * VBVA uses two 32 bits IO ports to write VRAM offsets of shared memory blocks for commands.
2412 * Read Write
2413 * Host port 0x3b0 to process completed
2414 * Guest port 0x3d0 control value? to process
2415 *
2416 */
2417
2418static DECLCALLBACK(void) vbvaNotifyGuest(void *pvCallback)
2419{
2420#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
2421 PPDMDEVINS pDevIns = (PPDMDEVINS)pvCallback;
2422 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
2423 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
2424 VBVARaiseIrq(pDevIns, pThis, pThisCC, 0);
2425#else
2426 NOREF(pvCallback);
2427 /* Do nothing. Later the VMMDev/VGA IRQ can be used for the notification. */
2428#endif
2429}
2430
2431/**
2432 * The guest submitted a command buffer (hit VGA_PORT_HGSMI_GUEST).
2433 *
2434 * Verify the buffer size and invoke corresponding handler.
2435 *
2436 * @return VBox status code.
2437 * @param pvHandler The VBVA channel context.
2438 * @param u16ChannelInfo Command code.
2439 * @param pvBuffer HGSMI buffer with command data. Considered volatile!
2440 * @param cbBuffer Size of command data.
2441 *
2442 * @thread EMT
2443 */
2444static DECLCALLBACK(int) vbvaChannelHandler(void *pvHandler, uint16_t u16ChannelInfo,
2445 void RT_UNTRUSTED_VOLATILE_GUEST *pvBuffer, HGSMISIZE cbBuffer)
2446{
2447 int rc = VINF_SUCCESS;
2448
2449 LogFlowFunc(("pvHandler %p, u16ChannelInfo %d, pvBuffer %p, cbBuffer %u\n", pvHandler, u16ChannelInfo, pvBuffer, cbBuffer));
2450
2451 PPDMDEVINS pDevIns = (PPDMDEVINS)pvHandler;
2452 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
2453 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
2454 PHGSMIINSTANCE pIns = pThisCC->pHGSMI;
2455 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pIns);
2456
2457 switch (u16ChannelInfo)
2458 {
2459#ifdef VBOX_WITH_VDMA
2460 case VBVA_VDMA_CMD:
2461 if (cbBuffer >= VBoxSHGSMIBufferHeaderSize() + sizeof(VBOXVDMACBUF_DR))
2462 {
2463 VBOXVDMACBUF_DR RT_UNTRUSTED_VOLATILE_GUEST *pCmd
2464 = (VBOXVDMACBUF_DR RT_UNTRUSTED_VOLATILE_GUEST *)VBoxSHGSMIBufferData((VBOXSHGSMIHEADER RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2465 vboxVDMACommand(pThisCC->pVdma, pCmd, cbBuffer - VBoxSHGSMIBufferHeaderSize());
2466 rc = VINF_SUCCESS;
2467 }
2468 else
2469 rc = VERR_INVALID_PARAMETER;
2470 break;
2471
2472 case VBVA_VDMA_CTL:
2473 if (cbBuffer >= VBoxSHGSMIBufferHeaderSize() + sizeof(VBOXVDMA_CTL))
2474 {
2475 VBOXVDMA_CTL RT_UNTRUSTED_VOLATILE_GUEST *pCmd
2476 = (VBOXVDMA_CTL RT_UNTRUSTED_VOLATILE_GUEST *)VBoxSHGSMIBufferData((VBOXSHGSMIHEADER RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2477 vboxVDMAControl(pThisCC->pVdma, pCmd, cbBuffer - VBoxSHGSMIBufferHeaderSize());
2478 }
2479 else
2480 rc = VERR_INVALID_PARAMETER;
2481 break;
2482#endif /* VBOX_WITH_VDMA */
2483
2484 case VBVA_QUERY_CONF32:
2485 if (cbBuffer >= sizeof(VBVACONF32))
2486 rc = vbvaHandleQueryConf32(pThisCC, (VBVACONF32 RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2487 else
2488 rc = VERR_INVALID_PARAMETER;
2489 break;
2490
2491 case VBVA_SET_CONF32:
2492 if (cbBuffer >= sizeof(VBVACONF32))
2493 rc = vbvaHandleSetConf32((VBVACONF32 RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2494 else
2495 rc = VERR_INVALID_PARAMETER;
2496 break;
2497
2498 case VBVA_INFO_VIEW:
2499 /* Expect at least one VBVAINFOVIEW structure. */
2500 rc = VERR_INVALID_PARAMETER;
2501 if (cbBuffer >= sizeof(VBVAINFOVIEW))
2502 {
2503 /* Guest submits an array of VBVAINFOVIEW structures. */
2504 const VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_GUEST *pView = (VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2505 for (;
2506 cbBuffer >= sizeof(VBVAINFOVIEW);
2507 ++pView, cbBuffer -= sizeof(VBVAINFOVIEW))
2508 {
2509 rc = vbvaInfoView(pThis, pThisCC, pView);
2510 if (RT_FAILURE(rc))
2511 break;
2512 }
2513 }
2514 break;
2515
2516 case VBVA_INFO_HEAP:
2517 if (cbBuffer >= sizeof(VBVAINFOHEAP))
2518 rc = vbvaHandleInfoHeap(pThisCC, (VBVAINFOHEAP RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2519 else
2520 rc = VERR_INVALID_PARAMETER;
2521 break;
2522
2523 case VBVA_FLUSH:
2524 if (cbBuffer >= sizeof(VBVAFLUSH))
2525 rc = vbvaFlush(pThis, pThisCC, pCtx);
2526 else
2527 rc = VERR_INVALID_PARAMETER;
2528 break;
2529
2530 case VBVA_INFO_SCREEN:
2531 rc = VERR_INVALID_PARAMETER;
2532 if (cbBuffer >= sizeof(VBVAINFOSCREEN))
2533 rc = vbvaInfoScreen(pThisCC, (VBVAINFOSCREEN RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2534 break;
2535
2536 case VBVA_ENABLE:
2537 rc = VERR_INVALID_PARAMETER;
2538 if (cbBuffer >= sizeof(VBVAENABLE))
2539 {
2540 VBVAENABLE RT_UNTRUSTED_VOLATILE_GUEST *pVbvaEnable = (VBVAENABLE RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2541 uint32_t const fEnableFlags = pVbvaEnable->u32Flags;
2542 uint32_t const offEnable = pVbvaEnable->u32Offset;
2543 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2544
2545 uint32_t idScreen;
2546 if (fEnableFlags & VBVA_F_EXTENDED)
2547 {
2548 ASSERT_GUEST_STMT_BREAK(cbBuffer >= sizeof(VBVAENABLE_EX), rc = VERR_INVALID_PARAMETER);
2549 idScreen = ((VBVAENABLE_EX RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer)->u32ScreenId;
2550 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2551 }
2552 else
2553 idScreen = vbvaViewFromBufferPtr(pIns, pCtx, pvBuffer);
2554
2555 rc = vbvaHandleEnable(pThis, pThisCC, fEnableFlags, offEnable, idScreen);
2556 pVbvaEnable->i32Result = rc;
2557 }
2558 break;
2559
2560 case VBVA_MOUSE_POINTER_SHAPE:
2561 if (cbBuffer >= sizeof(VBVAMOUSEPOINTERSHAPE))
2562 {
2563 VBVAMOUSEPOINTERSHAPE RT_UNTRUSTED_VOLATILE_GUEST *pShape
2564 = (VBVAMOUSEPOINTERSHAPE RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2565 rc = vbvaMousePointerShape(pThisCC, pCtx, pShape, cbBuffer);
2566 pShape->i32Result = rc;
2567 }
2568 else
2569 rc = VERR_INVALID_PARAMETER;
2570 break;
2571
2572
2573#ifdef VBOX_WITH_VIDEOHWACCEL
2574 case VBVA_VHWA_CMD:
2575 if (cbBuffer >= VBOXVHWACMD_HEADSIZE())
2576 {
2577 vbvaVHWAHandleCommand(pDevIns, pThis, pThisCC, (VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer);
2578 rc = VINF_SUCCESS;
2579 }
2580 else
2581 rc = VERR_INVALID_PARAMETER;
2582 break;
2583#endif
2584
2585#ifdef VBOX_WITH_WDDM
2586 case VBVA_INFO_CAPS:
2587 if (cbBuffer >= sizeof(VBVACAPS))
2588 {
2589 VBVACAPS RT_UNTRUSTED_VOLATILE_GUEST *pCaps = (VBVACAPS RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2590 pThis->fGuestCaps = pCaps->fCaps;
2591 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2592
2593 pThisCC->pDrv->pfnVBVAGuestCapabilityUpdate(pThisCC->pDrv, pThis->fGuestCaps);
2594 pCaps->rc = rc = VINF_SUCCESS;
2595 }
2596 else
2597 rc = VERR_INVALID_PARAMETER;
2598 break;
2599#endif
2600
2601 case VBVA_SCANLINE_CFG:
2602 if (cbBuffer >= sizeof(VBVASCANLINECFG))
2603 {
2604 VBVASCANLINECFG RT_UNTRUSTED_VOLATILE_GUEST *pCfg = (VBVASCANLINECFG RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2605 pThis->fScanLineCfg = pCfg->fFlags;
2606 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2607
2608 pCfg->rc = rc = VINF_SUCCESS;
2609 }
2610 else
2611 rc = VERR_INVALID_PARAMETER;
2612 break;
2613
2614 case VBVA_QUERY_MODE_HINTS:
2615 if (cbBuffer >= sizeof(VBVAQUERYMODEHINTS))
2616 {
2617 VBVAQUERYMODEHINTS RT_UNTRUSTED_VOLATILE_GUEST *pQueryModeHints
2618 = (VBVAQUERYMODEHINTS RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2619 rc = vbvaHandleQueryModeHints(pThisCC, pQueryModeHints, cbBuffer);
2620 pQueryModeHints->rc = rc;
2621 }
2622 else
2623 rc = VERR_INVALID_PARAMETER;
2624 break;
2625
2626 case VBVA_REPORT_INPUT_MAPPING:
2627 if (cbBuffer >= sizeof(VBVAREPORTINPUTMAPPING))
2628 {
2629 VBVAREPORTINPUTMAPPING inputMapping;
2630 {
2631 VBVAREPORTINPUTMAPPING RT_UNTRUSTED_VOLATILE_GUEST *pInputMapping
2632 = (VBVAREPORTINPUTMAPPING RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2633 inputMapping.x = pInputMapping->x;
2634 inputMapping.y = pInputMapping->y;
2635 inputMapping.cx = pInputMapping->cx;
2636 inputMapping.cy = pInputMapping->cy;
2637 }
2638 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2639
2640 LogRelFlowFunc(("VBVA: ChannelHandler: VBVA_REPORT_INPUT_MAPPING: x=%RI32, y=%RI32, cx=%RU32, cy=%RU32\n",
2641 inputMapping.x, inputMapping.y, inputMapping.cx, inputMapping.cy));
2642 pThisCC->pDrv->pfnVBVAInputMappingUpdate(pThisCC->pDrv,
2643 inputMapping.x, inputMapping.y,
2644 inputMapping.cx, inputMapping.cy);
2645 rc = VINF_SUCCESS;
2646 }
2647 else
2648 rc = VERR_INVALID_PARAMETER;
2649 break;
2650
2651 case VBVA_CURSOR_POSITION:
2652 if (cbBuffer >= sizeof(VBVACURSORPOSITION))
2653 {
2654 VBVACURSORPOSITION RT_UNTRUSTED_VOLATILE_GUEST *pReport = (VBVACURSORPOSITION RT_UNTRUSTED_VOLATILE_GUEST *)pvBuffer;
2655 VBVACURSORPOSITION Report;
2656 Report.fReportPosition = pReport->fReportPosition;
2657 Report.x = pReport->x;
2658 Report.y = pReport->y;
2659 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2660
2661 LogRelFlowFunc(("VBVA: ChannelHandler: VBVA_CURSOR_POSITION: fReportPosition=%RTbool, Id=%RU32, x=%RU32, y=%RU32\n",
2662 RT_BOOL(Report.fReportPosition), vbvaViewFromBufferPtr(pIns, pCtx, pvBuffer), Report.x, Report.y));
2663
2664 pThisCC->pDrv->pfnVBVAReportCursorPosition(pThisCC->pDrv, RT_BOOL(Report.fReportPosition), vbvaViewFromBufferPtr(pIns, pCtx, pvBuffer), Report.x, Report.y);
2665 /* This was only ever briefly used by the guest, and a value
2666 * of zero in both was taken to mean "ignore". */
2667 pReport->x = 0;
2668 pReport->y = 0;
2669 rc = VINF_SUCCESS;
2670 }
2671 else
2672 rc = VERR_INVALID_PARAMETER;
2673 break;
2674
2675 default:
2676 Log(("Unsupported VBVA guest command %d (%#x)!!!\n", u16ChannelInfo, u16ChannelInfo));
2677 break;
2678 }
2679
2680 return rc;
2681}
2682
2683/** When VBVA is paused, the VGA device is allowed to work but
2684 * no HGSMI etc state is changed.
2685 */
2686static void vbvaPause(PVGASTATECC pThisCC, bool fPause)
2687{
2688 if (!pThisCC || !pThisCC->pHGSMI)
2689 return;
2690
2691 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2692 if (pCtx)
2693 pCtx->fPaused = fPause;
2694}
2695
2696bool VBVAIsPaused(PVGASTATECC pThisCC)
2697{
2698 if (pThisCC && pThisCC->pHGSMI)
2699 {
2700 const VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2701 if (pCtx && pCtx->cViews)
2702 {
2703 /* If VBVA is enabled at all. */
2704 const VBVAVIEW *pView = &pCtx->aViews[0];
2705 if (pView->vbva.guest.pVBVA)
2706 return pCtx->fPaused;
2707 }
2708 }
2709 /* VBVA is disabled. */
2710 return true;
2711}
2712
2713void VBVAOnVBEChanged(PVGASTATE pThis, PVGASTATECC pThisCC)
2714{
2715 /* The guest does not depend on host handling the VBE registers. */
2716 if (pThis->fGuestCaps & VBVACAPS_USE_VBVA_ONLY)
2717 return;
2718
2719 vbvaPause(pThisCC, (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0);
2720}
2721
2722void VBVAReset(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
2723{
2724 if (!pThis || !pThisCC->pHGSMI)
2725 return;
2726
2727 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2728
2729#ifdef VBOX_WITH_VIDEOHWACCEL
2730 vbvaVHWAReset(pDevIns, pThis, pThisCC);
2731#endif
2732
2733 HGSMIReset(pThisCC->pHGSMI);
2734 /* Make sure the IRQ is reset. */
2735 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
2736 pThis->fu32PendingGuestFlags = 0;
2737
2738 if (pCtx)
2739 {
2740 vbvaFlush(pThis, pThisCC, pCtx);
2741
2742 for (unsigned idScreen = 0; idScreen < pCtx->cViews; idScreen++)
2743 vbvaDisable(pThis, pThisCC, pCtx, idScreen);
2744
2745 pCtx->mouseShapeInfo.fSet = false;
2746 RTMemFreeZ(pCtx->mouseShapeInfo.pu8Shape, pCtx->mouseShapeInfo.cbAllocated);
2747 pCtx->mouseShapeInfo.pu8Shape = NULL;
2748 pCtx->mouseShapeInfo.cbAllocated = 0;
2749 pCtx->mouseShapeInfo.cbShape = 0;
2750 }
2751
2752}
2753
2754int VBVAUpdateDisplay(PVGASTATE pThis, PVGASTATECC pThisCC)
2755{
2756 int rc = VERR_NOT_SUPPORTED; /* Assuming that the VGA device will have to do updates. */
2757
2758 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2759 if (pCtx)
2760 {
2761 if (!pCtx->fPaused)
2762 {
2763 rc = vbvaFlush(pThis, pThisCC, pCtx);
2764 if (RT_SUCCESS(rc))
2765 {
2766 if (!pCtx->aViews[0].vbva.guest.pVBVA)
2767 {
2768 /* VBVA is not enabled for the first view, so VGA device must do updates. */
2769 rc = VERR_NOT_SUPPORTED;
2770 }
2771 }
2772 }
2773 }
2774
2775 return rc;
2776}
2777
2778static int vbvaSendModeHintWorker(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC,
2779 uint32_t cx, uint32_t cy, uint32_t cBPP, uint32_t iDisplay, uint32_t dx,
2780 uint32_t dy, uint32_t fEnabled,
2781 uint32_t fNotifyGuest)
2782{
2783 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2784 /** @note See Display::setVideoModeHint: "It is up to the guest to decide
2785 * whether the hint is valid. Therefore don't do any VRAM sanity checks
2786 * here! */
2787 if (iDisplay >= RT_MIN(pThis->cMonitors, RT_ELEMENTS(pCtx->aModeHints)))
2788 return VERR_OUT_OF_RANGE;
2789 pCtx->aModeHints[iDisplay].magic = VBVAMODEHINT_MAGIC;
2790 pCtx->aModeHints[iDisplay].cx = cx;
2791 pCtx->aModeHints[iDisplay].cy = cy;
2792 pCtx->aModeHints[iDisplay].cBPP = cBPP;
2793 pCtx->aModeHints[iDisplay].dx = dx;
2794 pCtx->aModeHints[iDisplay].dy = dy;
2795 pCtx->aModeHints[iDisplay].fEnabled = fEnabled;
2796 if (fNotifyGuest && pThis->fGuestCaps & VBVACAPS_IRQ && pThis->fGuestCaps & VBVACAPS_VIDEO_MODE_HINTS)
2797 VBVARaiseIrq(pDevIns, pThis, pThisCC, HGSMIHOSTFLAGS_HOTPLUG);
2798 return VINF_SUCCESS;
2799}
2800
2801
2802/**
2803 * @interface_method_impl{PDMIDISPLAYPORT,pfnSendModeHint}
2804 */
2805DECLCALLBACK(int) vbvaR3PortSendModeHint(PPDMIDISPLAYPORT pInterface, uint32_t cx, uint32_t cy, uint32_t cBPP,
2806 uint32_t iDisplay, uint32_t dx, uint32_t dy, uint32_t fEnabled, uint32_t fNotifyGuest)
2807{
2808 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
2809 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2810 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
2811 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2812 AssertRC(rc);
2813
2814 rc = vbvaSendModeHintWorker(pDevIns, pThis, pThisCC, cx, cy, cBPP, iDisplay, dx, dy, fEnabled, fNotifyGuest);
2815
2816 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2817 return rc;
2818}
2819
2820int VBVAInit(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC)
2821{
2822 PVM pVM = PDMDevHlpGetVM(pDevIns);
2823
2824 int rc = HGSMICreate(&pThisCC->pHGSMI,
2825 pVM,
2826 "VBVA",
2827 0,
2828 pThisCC->pbVRam,
2829 pThis->vram_size,
2830 vbvaNotifyGuest,
2831 pDevIns,
2832 sizeof(VBVACONTEXT));
2833 if (RT_SUCCESS(rc))
2834 {
2835 rc = HGSMIHostChannelRegister(pThisCC->pHGSMI,
2836 HGSMI_CH_VBVA,
2837 vbvaChannelHandler,
2838 pDevIns);
2839 if (RT_SUCCESS(rc))
2840 {
2841 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pThisCC->pHGSMI);
2842 pCtx->cViews = pThis->cMonitors;
2843 pCtx->fPaused = true;
2844 memset(pCtx->aModeHints, ~0, sizeof(pCtx->aModeHints));
2845 }
2846 }
2847
2848 return rc;
2849
2850}
2851
2852void VBVADestroy(PVGASTATECC pThisCC)
2853{
2854 PHGSMIINSTANCE pHgsmi = pThisCC->pHGSMI;
2855 if (pHgsmi)
2856 {
2857 VBVACONTEXT *pCtx = (VBVACONTEXT *)HGSMIContext(pHgsmi);
2858 pCtx->mouseShapeInfo.fSet = false;
2859 RTMemFreeZ(pCtx->mouseShapeInfo.pu8Shape, pCtx->mouseShapeInfo.cbAllocated);
2860 pCtx->mouseShapeInfo.pu8Shape = NULL;
2861 pCtx->mouseShapeInfo.cbAllocated = 0;
2862 pCtx->mouseShapeInfo.cbShape = 0;
2863
2864 HGSMIDestroy(pHgsmi);
2865 pThisCC->pHGSMI = NULL;
2866 }
2867}
2868
Note: See TracBrowser for help on using the repository browser.

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