VirtualBox

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

Last change on this file since 107911 was 107630, checked in by vboxsync, 5 weeks ago

Devices/Graphics/DevVGA_VBVA.cpp: Remove redundant if condition, cbCmd is always >= sizeof(VBVACMDHDR) at this point due to the check before, bugref:3409

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

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