1/* $Id: vbva.c,v 1.2 2020/10/22 20:47:23 thorpej Exp $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver 2D acceleration functions
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC)
29# include "xf86_ansic.h"
30#endif
31#include "compiler.h"
32
33#include "vboxvideo_drv.h"
34
35#ifdef XORG_7X
36# include <stdlib.h>
37# include <string.h>
38#endif
39
40/**************************************************************************
41* Main functions                                                          *
42**************************************************************************/
43
44/**
45 * Callback function called by the X server to tell us about dirty
46 * rectangles in the video buffer.
47 *
48 * @param pScrn   pointer to the information structure for the current
49 *                screen
50 * @param iRects  Number of dirty rectangles to update
51 * @param aRects  Array of structures containing the coordinates of the
52 *                rectangles
53 */
54void vbvxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects)
55{
56    VBVACMDHDR cmdHdr;
57    VBOXPtr pVBox;
58    int i;
59    unsigned j;
60
61    pVBox = pScrn->driverPrivate;
62    if (!pScrn->vtSema)
63        return;
64
65    for (j = 0; j < pVBox->cScreens; ++j)
66    {
67        /* Just continue quietly if VBVA is not currently active. */
68        struct VBVABUFFER *pVBVA = pVBox->pScreens[j].aVbvaCtx.pVBVA;
69        if (   !pVBVA
70            || !(pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED))
71            continue;
72        for (i = 0; i < iRects; ++i)
73        {
74            if (   aRects[i].x1 >   pVBox->pScreens[j].aScreenLocation.x
75                                  + pVBox->pScreens[j].aScreenLocation.cx
76                || aRects[i].y1 >   pVBox->pScreens[j].aScreenLocation.y
77                                  + pVBox->pScreens[j].aScreenLocation.cy
78                || aRects[i].x2 <   pVBox->pScreens[j].aScreenLocation.x
79                || aRects[i].y2 <   pVBox->pScreens[j].aScreenLocation.y)
80                continue;
81            cmdHdr.x = (int16_t)aRects[i].x1 - pVBox->pScreens[0].aScreenLocation.x;
82            cmdHdr.y = (int16_t)aRects[i].y1 - pVBox->pScreens[0].aScreenLocation.y;
83            cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1);
84            cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1);
85
86#if 0
87            TRACE_LOG("display=%u, x=%d, y=%d, w=%d, h=%d\n",
88                      j, cmdHdr.x, cmdHdr.y, cmdHdr.w, cmdHdr.h);
89#endif
90
91            if (VBoxVBVABufferBeginUpdate(&pVBox->pScreens[j].aVbvaCtx,
92                                          &pVBox->guestCtx))
93            {
94                VBoxVBVAWrite(&pVBox->pScreens[j].aVbvaCtx, &pVBox->guestCtx, &cmdHdr,
95                              sizeof(cmdHdr));
96                VBoxVBVABufferEndUpdate(&pVBox->pScreens[j].aVbvaCtx);
97            }
98        }
99    }
100}
101
102static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb)
103{
104    RT_NOREF(pvEnv);
105    return calloc(1, cb);
106}
107
108static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv)
109{
110    RT_NOREF(pvEnv);
111    free(pv);
112}
113
114static HGSMIENV g_hgsmiEnv =
115{
116    NULL,
117    hgsmiEnvAlloc,
118    hgsmiEnvFree
119};
120
121/**
122 * Calculate the location in video RAM of and initialise the heap for guest to
123 * host messages.
124 */
125void vbvxSetUpHGSMIHeapInGuest(VBOXPtr pVBox, uint32_t cbVRAM)
126{
127    int rc;
128    uint32_t offVRAMBaseMapping, offGuestHeapMemory, cbGuestHeapMemory;
129    void *pvGuestHeapMemory;
130
131    VBoxHGSMIGetBaseMappingInfo(cbVRAM, &offVRAMBaseMapping, NULL, &offGuestHeapMemory, &cbGuestHeapMemory, NULL);
132    pvGuestHeapMemory = ((uint8_t *)pVBox->base) + offVRAMBaseMapping + offGuestHeapMemory;
133    rc = VBoxHGSMISetupGuestContext(&pVBox->guestCtx, pvGuestHeapMemory, cbGuestHeapMemory,
134                                    offVRAMBaseMapping + offGuestHeapMemory, &g_hgsmiEnv);
135    AssertMsg(RT_SUCCESS(rc), ("Failed to set up the guest-to-host message buffer heap, rc=%d\n", rc));
136    pVBox->cbView = offVRAMBaseMapping;
137}
138
139/** Callback to fill in the view structures */
140static DECLCALLBACK(int) vboxFillViewInfo(void *pvVBox, struct VBVAINFOVIEW *pViews, uint32_t cViews)
141{
142    VBOXPtr pVBox = (VBOXPtr)pvVBox;
143    unsigned i;
144    for (i = 0; i < cViews; ++i)
145    {
146        pViews[i].u32ViewIndex = i;
147        pViews[i].u32ViewOffset = 0;
148        pViews[i].u32ViewSize = pVBox->cbView;
149        pViews[i].u32MaxScreenSize = pVBox->cbFBMax;
150    }
151    return VINF_SUCCESS;
152}
153
154/**
155 * Initialise VirtualBox's accelerated video extensions.
156 *
157 * @returns TRUE on success, FALSE on failure
158 */
159static Bool vboxSetupVRAMVbva(VBOXPtr pVBox)
160{
161    int rc = VINF_SUCCESS;
162    unsigned i;
163
164    pVBox->cbFBMax = pVBox->cbView;
165    for (i = 0; i < pVBox->cScreens; ++i)
166    {
167        pVBox->cbFBMax -= VBVA_MIN_BUFFER_SIZE;
168        pVBox->pScreens[i].aoffVBVABuffer = pVBox->cbFBMax;
169        TRACE_LOG("VBVA buffer offset for screen %u: 0x%lx\n", i,
170                  (unsigned long) pVBox->cbFBMax);
171        VBoxVBVASetupBufferContext(&pVBox->pScreens[i].aVbvaCtx,
172                                   pVBox->pScreens[i].aoffVBVABuffer,
173                                   VBVA_MIN_BUFFER_SIZE);
174    }
175    TRACE_LOG("Maximum framebuffer size: %lu (0x%lx)\n",
176              (unsigned long) pVBox->cbFBMax,
177              (unsigned long) pVBox->cbFBMax);
178    rc = VBoxHGSMISendViewInfo(&pVBox->guestCtx, pVBox->cScreens,
179                               vboxFillViewInfo, (void *)pVBox);
180    AssertMsg(RT_SUCCESS(rc), ("Failed to send the view information to the host, rc=%d\n", rc));
181    return TRUE;
182}
183
184static Bool haveHGSMIModeHintAndCursorReportingInterface(VBOXPtr pVBox)
185{
186    uint32_t fModeHintReporting, fCursorReporting;
187
188    return    RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_MODE_HINT_REPORTING, &fModeHintReporting))
189           && RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, &fCursorReporting))
190           && fModeHintReporting == VINF_SUCCESS
191           && fCursorReporting == VINF_SUCCESS;
192}
193
194static Bool hostHasScreenBlankingFlag(VBOXPtr pVBox)
195{
196    uint32_t fScreenFlags;
197
198    return    RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &fScreenFlags))
199           && fScreenFlags & VBVA_SCREEN_F_BLANK;
200}
201
202/**
203 * Inform VBox that we will supply it with dirty rectangle information
204 * and install the dirty rectangle handler.
205 *
206 * @returns TRUE for success, FALSE for failure
207 * @param   pScrn   Pointer to a structure describing the X screen in use
208 */
209Bool
210vboxEnableVbva(ScrnInfoPtr pScrn)
211{
212    Bool rc = TRUE;
213    unsigned i;
214    VBOXPtr pVBox = pScrn->driverPrivate;
215
216    TRACE_ENTRY();
217    if (!vboxSetupVRAMVbva(pVBox))
218        return FALSE;
219    for (i = 0; i < pVBox->cScreens; ++i)
220    {
221        struct VBVABUFFER *pVBVA;
222
223        pVBVA = (struct VBVABUFFER *) (  ((uint8_t *)pVBox->base)
224                                       + pVBox->pScreens[i].aoffVBVABuffer);
225        if (!VBoxVBVAEnable(&pVBox->pScreens[i].aVbvaCtx, &pVBox->guestCtx,
226                            pVBVA, i))
227            rc = FALSE;
228    }
229    AssertMsg(rc, ("Failed to enable screen update reporting for at least one virtual monitor.\n"));
230    pVBox->fHaveHGSMIModeHints = haveHGSMIModeHintAndCursorReportingInterface(pVBox);
231    pVBox->fHostHasScreenBlankingFlag = hostHasScreenBlankingFlag(pVBox);
232    return rc;
233}
234
235/**
236 * Inform VBox that we will stop supplying it with dirty rectangle
237 * information. This function is intended to be called when an X
238 * virtual terminal is disabled, or the X server is terminated.
239 *
240 * @returns TRUE for success, FALSE for failure
241 * @param   pScrn   Pointer to a structure describing the X screen in use
242 */
243void
244vboxDisableVbva(ScrnInfoPtr pScrn)
245{
246    unsigned i;
247    VBOXPtr pVBox = pScrn->driverPrivate;
248
249    TRACE_ENTRY();
250    for (i = 0; i < pVBox->cScreens; ++i)
251        VBoxVBVADisable(&pVBox->pScreens[i].aVbvaCtx, &pVBox->guestCtx, i);
252}
253