VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use