1e07dc26bSmrg/*
2e07dc26bSmrg * Copyright (C) 2006-2017 Oracle Corporation
3e07dc26bSmrg *
4e07dc26bSmrg * Permission is hereby granted, free of charge, to any person obtaining a
5e07dc26bSmrg * copy of this software and associated documentation files (the "Software"),
6e07dc26bSmrg * to deal in the Software without restriction, including without limitation
7e07dc26bSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8e07dc26bSmrg * and/or sell copies of the Software, and to permit persons to whom the
9e07dc26bSmrg * Software is furnished to do so, subject to the following conditions:
10e07dc26bSmrg *
11e07dc26bSmrg * The above copyright notice and this permission notice shall be included in
12e07dc26bSmrg * all copies or substantial portions of the Software.
13e07dc26bSmrg *
14e07dc26bSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15e07dc26bSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16e07dc26bSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17e07dc26bSmrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18e07dc26bSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19e07dc26bSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20e07dc26bSmrg * OTHER DEALINGS IN THE SOFTWARE.
21e07dc26bSmrg */
22e07dc26bSmrg
23e07dc26bSmrg#include <VBoxVideoGuest.h>
24e07dc26bSmrg#include <VBoxVideoIPRT.h>
25e07dc26bSmrg#include <HGSMIChannels.h>
26e07dc26bSmrg
27e07dc26bSmrg/*
28e07dc26bSmrg * There is a hardware ring buffer in the graphics device video RAM, formerly
29e07dc26bSmrg * in the VBox VMMDev PCI memory space.
30e07dc26bSmrg * All graphics commands go there serialized by VBoxVBVABufferBeginUpdate.
31e07dc26bSmrg * and vboxHwBufferEndUpdate.
32e07dc26bSmrg *
33e07dc26bSmrg * off32Free is writing position. off32Data is reading position.
34e07dc26bSmrg * off32Free == off32Data means buffer is empty.
35e07dc26bSmrg * There must be always gap between off32Data and off32Free when data
36e07dc26bSmrg * are in the buffer.
37e07dc26bSmrg * Guest only changes off32Free, host changes off32Data.
38e07dc26bSmrg */
39e07dc26bSmrg
40e07dc26bSmrg/* Forward declarations of internal functions. */
41e07dc26bSmrgstatic void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx);
42e07dc26bSmrgstatic void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
43e07dc26bSmrg                                    uint32_t cb, uint32_t offset);
44e07dc26bSmrgstatic bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
45e07dc26bSmrg                              PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
46e07dc26bSmrg                              const void *p, uint32_t cb);
47e07dc26bSmrg
48e07dc26bSmrg
49e07dc26bSmrgstatic bool vboxVBVAInformHost(PVBVABUFFERCONTEXT pCtx,
50e07dc26bSmrg                               PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
51e07dc26bSmrg                               int32_t cScreen, bool bEnable)
52e07dc26bSmrg{
53e07dc26bSmrg    bool bRc = false;
54e07dc26bSmrg
55e07dc26bSmrg#if 0  /* All callers check this */
56e07dc26bSmrg    if (ppdev->bHGSMISupported)
57e07dc26bSmrg#endif
58e07dc26bSmrg    {
59e07dc26bSmrg        void *p = VBoxHGSMIBufferAlloc(pHGSMICtx,
60e07dc26bSmrg                                       sizeof (VBVAENABLE_EX),
61e07dc26bSmrg                                       HGSMI_CH_VBVA,
62e07dc26bSmrg                                       VBVA_ENABLE);
63e07dc26bSmrg        if (!p)
64e07dc26bSmrg        {
65e07dc26bSmrg            // LogFunc(("HGSMIHeapAlloc failed\n"));
66e07dc26bSmrg        }
67e07dc26bSmrg        else
68e07dc26bSmrg        {
69e07dc26bSmrg            VBVAENABLE_EX *pEnable = (VBVAENABLE_EX *)p;
70e07dc26bSmrg
71e07dc26bSmrg            pEnable->Base.u32Flags  = bEnable? VBVA_F_ENABLE: VBVA_F_DISABLE;
72e07dc26bSmrg            pEnable->Base.u32Offset = pCtx->offVRAMBuffer;
73e07dc26bSmrg            pEnable->Base.i32Result = VERR_NOT_SUPPORTED;
74e07dc26bSmrg            if (cScreen >= 0)
75e07dc26bSmrg            {
76e07dc26bSmrg                pEnable->Base.u32Flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET;
77e07dc26bSmrg                pEnable->u32ScreenId    = cScreen;
78e07dc26bSmrg            }
79e07dc26bSmrg
80e07dc26bSmrg            VBoxHGSMIBufferSubmit(pHGSMICtx, p);
81e07dc26bSmrg
82e07dc26bSmrg            if (bEnable)
83e07dc26bSmrg            {
84e07dc26bSmrg                bRc = RT_SUCCESS(pEnable->Base.i32Result);
85e07dc26bSmrg            }
86e07dc26bSmrg            else
87e07dc26bSmrg            {
88e07dc26bSmrg                bRc = true;
89e07dc26bSmrg            }
90e07dc26bSmrg
91e07dc26bSmrg            VBoxHGSMIBufferFree(pHGSMICtx, p);
92e07dc26bSmrg        }
93e07dc26bSmrg    }
94e07dc26bSmrg
95e07dc26bSmrg    return bRc;
96e07dc26bSmrg}
97e07dc26bSmrg
98e07dc26bSmrg/*
99e07dc26bSmrg * Public hardware buffer methods.
100e07dc26bSmrg */
101e07dc26bSmrgDECLHIDDEN(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx,
102e07dc26bSmrg                                PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
103e07dc26bSmrg                                VBVABUFFER *pVBVA, int32_t cScreen)
104e07dc26bSmrg{
105e07dc26bSmrg    bool bRc = false;
106e07dc26bSmrg
107e07dc26bSmrg    // LogFlowFunc(("pVBVA %p\n", pVBVA));
108e07dc26bSmrg
109e07dc26bSmrg#if 0  /* All callers check this */
110e07dc26bSmrg    if (ppdev->bHGSMISupported)
111e07dc26bSmrg#endif
112e07dc26bSmrg    {
113e07dc26bSmrg        // LogFunc(("pVBVA %p vbva off 0x%x\n", pVBVA, pCtx->offVRAMBuffer));
114e07dc26bSmrg
115e07dc26bSmrg        pVBVA->hostFlags.u32HostEvents      = 0;
116e07dc26bSmrg        pVBVA->hostFlags.u32SupportedOrders = 0;
117e07dc26bSmrg        pVBVA->off32Data          = 0;
118e07dc26bSmrg        pVBVA->off32Free          = 0;
119e07dc26bSmrg        memset(pVBVA->aRecords, 0, sizeof (pVBVA->aRecords));
120e07dc26bSmrg        pVBVA->indexRecordFirst   = 0;
121e07dc26bSmrg        pVBVA->indexRecordFree    = 0;
122e07dc26bSmrg        pVBVA->cbPartialWriteThreshold = 256;
123e07dc26bSmrg        pVBVA->cbData             = pCtx->cbBuffer - sizeof (VBVABUFFER) + sizeof (pVBVA->au8Data);
124e07dc26bSmrg
125e07dc26bSmrg        pCtx->fHwBufferOverflow = false;
126e07dc26bSmrg        pCtx->pRecord    = NULL;
127e07dc26bSmrg        pCtx->pVBVA      = pVBVA;
128e07dc26bSmrg
129e07dc26bSmrg        bRc = vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, true);
130e07dc26bSmrg    }
131e07dc26bSmrg
132e07dc26bSmrg    if (!bRc)
133e07dc26bSmrg    {
134e07dc26bSmrg        VBoxVBVADisable(pCtx, pHGSMICtx, cScreen);
135e07dc26bSmrg    }
136e07dc26bSmrg
137e07dc26bSmrg    return bRc;
138e07dc26bSmrg}
139e07dc26bSmrg
140e07dc26bSmrgDECLHIDDEN(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx,
141e07dc26bSmrg                                 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
142e07dc26bSmrg                                 int32_t cScreen)
143e07dc26bSmrg{
144e07dc26bSmrg    // LogFlowFunc(("\n"));
145e07dc26bSmrg
146e07dc26bSmrg    pCtx->fHwBufferOverflow = false;
147e07dc26bSmrg    pCtx->pRecord           = NULL;
148e07dc26bSmrg    pCtx->pVBVA             = NULL;
149e07dc26bSmrg
150e07dc26bSmrg    vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, false);
151e07dc26bSmrg
152e07dc26bSmrg    return;
153e07dc26bSmrg}
154e07dc26bSmrg
155e07dc26bSmrgDECLHIDDEN(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx,
156e07dc26bSmrg                                           PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx)
157e07dc26bSmrg{
158e07dc26bSmrg    bool bRc = false;
159e07dc26bSmrg
160e07dc26bSmrg    // LogFunc(("flags = 0x%08X\n", pCtx->pVBVA? pCtx->pVBVA->u32HostEvents: -1));
161e07dc26bSmrg
162e07dc26bSmrg    if (   pCtx->pVBVA
163e07dc26bSmrg        && (pCtx->pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED))
164e07dc26bSmrg    {
165e07dc26bSmrg        uint32_t indexRecordNext;
166e07dc26bSmrg
167e07dc26bSmrg        Assert(!pCtx->fHwBufferOverflow);
168e07dc26bSmrg        Assert(pCtx->pRecord == NULL);
169e07dc26bSmrg
170e07dc26bSmrg        indexRecordNext = (pCtx->pVBVA->indexRecordFree + 1) % VBVA_MAX_RECORDS;
171e07dc26bSmrg
172e07dc26bSmrg        if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
173e07dc26bSmrg        {
174e07dc26bSmrg            /* All slots in the records queue are used. */
175e07dc26bSmrg            vboxHwBufferFlush (pHGSMICtx);
176e07dc26bSmrg        }
177e07dc26bSmrg
178e07dc26bSmrg        if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
179e07dc26bSmrg        {
180e07dc26bSmrg            /* Even after flush there is no place. Fail the request. */
181e07dc26bSmrg            // LogFunc(("no space in the queue of records!!! first %d, last %d\n",
182e07dc26bSmrg            //          pCtx->pVBVA->indexRecordFirst, pCtx->pVBVA->indexRecordFree));
183e07dc26bSmrg        }
184e07dc26bSmrg        else
185e07dc26bSmrg        {
186e07dc26bSmrg            /* Initialize the record. */
187e07dc26bSmrg            VBVARECORD *pRecord = &pCtx->pVBVA->aRecords[pCtx->pVBVA->indexRecordFree];
188e07dc26bSmrg
189e07dc26bSmrg            pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
190e07dc26bSmrg
191e07dc26bSmrg            pCtx->pVBVA->indexRecordFree = indexRecordNext;
192e07dc26bSmrg
193e07dc26bSmrg            // LogFunc(("indexRecordNext = %d\n", indexRecordNext));
194e07dc26bSmrg
195e07dc26bSmrg            /* Remember which record we are using. */
196e07dc26bSmrg            pCtx->pRecord = pRecord;
197e07dc26bSmrg
198e07dc26bSmrg            bRc = true;
199e07dc26bSmrg        }
200e07dc26bSmrg    }
201e07dc26bSmrg
202e07dc26bSmrg    return bRc;
203e07dc26bSmrg}
204e07dc26bSmrg
205e07dc26bSmrgDECLHIDDEN(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx)
206e07dc26bSmrg{
207e07dc26bSmrg    VBVARECORD *pRecord;
208e07dc26bSmrg
209e07dc26bSmrg    // LogFunc(("\n"));
210e07dc26bSmrg
211e07dc26bSmrg    Assert(pCtx->pVBVA);
212e07dc26bSmrg
213e07dc26bSmrg    pRecord = pCtx->pRecord;
214e07dc26bSmrg    Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
215e07dc26bSmrg
216e07dc26bSmrg    /* Mark the record completed. */
217e07dc26bSmrg    pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
218e07dc26bSmrg
219e07dc26bSmrg    pCtx->fHwBufferOverflow = false;
220e07dc26bSmrg    pCtx->pRecord = NULL;
221e07dc26bSmrg
222e07dc26bSmrg    return;
223e07dc26bSmrg}
224e07dc26bSmrg
225e07dc26bSmrg/*
226e07dc26bSmrg * Private operations.
227e07dc26bSmrg */
228e07dc26bSmrgstatic uint32_t vboxHwBufferAvail (const VBVABUFFER *pVBVA)
229e07dc26bSmrg{
230e07dc26bSmrg    int32_t i32Diff = pVBVA->off32Data - pVBVA->off32Free;
231e07dc26bSmrg
232e07dc26bSmrg    return i32Diff > 0? i32Diff: pVBVA->cbData + i32Diff;
233e07dc26bSmrg}
234e07dc26bSmrg
235e07dc26bSmrgstatic void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx)
236e07dc26bSmrg{
237e07dc26bSmrg    /* Issue the flush command. */
238e07dc26bSmrg    void *p = VBoxHGSMIBufferAlloc(pCtx,
239e07dc26bSmrg                                   sizeof (VBVAFLUSH),
240e07dc26bSmrg                                   HGSMI_CH_VBVA,
241e07dc26bSmrg                                   VBVA_FLUSH);
242e07dc26bSmrg    if (!p)
243e07dc26bSmrg    {
244e07dc26bSmrg        // LogFunc(("HGSMIHeapAlloc failed\n"));
245e07dc26bSmrg    }
246e07dc26bSmrg    else
247e07dc26bSmrg    {
248e07dc26bSmrg        VBVAFLUSH *pFlush = (VBVAFLUSH *)p;
249e07dc26bSmrg
250e07dc26bSmrg        pFlush->u32Reserved = 0;
251e07dc26bSmrg
252e07dc26bSmrg        VBoxHGSMIBufferSubmit(pCtx, p);
253e07dc26bSmrg
254e07dc26bSmrg        VBoxHGSMIBufferFree(pCtx, p);
255e07dc26bSmrg    }
256e07dc26bSmrg
257e07dc26bSmrg    return;
258e07dc26bSmrg}
259e07dc26bSmrg
260e07dc26bSmrgstatic void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
261e07dc26bSmrg                                    uint32_t cb, uint32_t offset)
262e07dc26bSmrg{
263e07dc26bSmrg    VBVABUFFER *pVBVA = pCtx->pVBVA;
264e07dc26bSmrg    uint32_t u32BytesTillBoundary = pVBVA->cbData - offset;
265e07dc26bSmrg    uint8_t  *dst                 = &pVBVA->au8Data[offset];
266e07dc26bSmrg    int32_t i32Diff               = cb - u32BytesTillBoundary;
267e07dc26bSmrg
268e07dc26bSmrg    if (i32Diff <= 0)
269e07dc26bSmrg    {
270e07dc26bSmrg        /* Chunk will not cross buffer boundary. */
271e07dc26bSmrg        memcpy (dst, p, cb);
272e07dc26bSmrg    }
273e07dc26bSmrg    else
274e07dc26bSmrg    {
275e07dc26bSmrg        /* Chunk crosses buffer boundary. */
276e07dc26bSmrg        memcpy (dst, p, u32BytesTillBoundary);
277e07dc26bSmrg        memcpy (&pVBVA->au8Data[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff);
278e07dc26bSmrg    }
279e07dc26bSmrg
280e07dc26bSmrg    return;
281e07dc26bSmrg}
282e07dc26bSmrg
283e07dc26bSmrgstatic bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
284e07dc26bSmrg                              PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
285e07dc26bSmrg                              const void *p, uint32_t cb)
286e07dc26bSmrg{
287e07dc26bSmrg    VBVARECORD *pRecord;
288e07dc26bSmrg    uint32_t cbHwBufferAvail;
289e07dc26bSmrg
290e07dc26bSmrg    uint32_t cbWritten = 0;
291e07dc26bSmrg
292e07dc26bSmrg    VBVABUFFER *pVBVA = pCtx->pVBVA;
293e07dc26bSmrg    Assert(pVBVA);
294e07dc26bSmrg
295e07dc26bSmrg    if (!pVBVA || pCtx->fHwBufferOverflow)
296e07dc26bSmrg    {
297e07dc26bSmrg        return false;
298e07dc26bSmrg    }
299e07dc26bSmrg
300e07dc26bSmrg    Assert(pVBVA->indexRecordFirst != pVBVA->indexRecordFree);
301e07dc26bSmrg
302e07dc26bSmrg    pRecord = pCtx->pRecord;
303e07dc26bSmrg    Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
304e07dc26bSmrg
305e07dc26bSmrg    // LogFunc(("%d\n", cb));
306e07dc26bSmrg
307e07dc26bSmrg    cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
308e07dc26bSmrg
309e07dc26bSmrg    while (cb > 0)
310e07dc26bSmrg    {
311e07dc26bSmrg        uint32_t cbChunk = cb;
312e07dc26bSmrg
313e07dc26bSmrg        // LogFunc(("pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n",
314e07dc26bSmrg        //             pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
315e07dc26bSmrg
316e07dc26bSmrg        if (cbChunk >= cbHwBufferAvail)
317e07dc26bSmrg        {
318e07dc26bSmrg            // LogFunc(("1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk));
319e07dc26bSmrg
320e07dc26bSmrg            vboxHwBufferFlush (pHGSMICtx);
321e07dc26bSmrg
322e07dc26bSmrg            cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
323e07dc26bSmrg
324e07dc26bSmrg            if (cbChunk >= cbHwBufferAvail)
325e07dc26bSmrg            {
326e07dc26bSmrg                // LogFunc(("no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n",
327e07dc26bSmrg                //             cb, cbHwBufferAvail));
328e07dc26bSmrg
329e07dc26bSmrg                if (cbHwBufferAvail <= pVBVA->cbPartialWriteThreshold)
330e07dc26bSmrg                {
331e07dc26bSmrg                    // LogFunc(("Buffer overflow!!!\n"));
332e07dc26bSmrg                    pCtx->fHwBufferOverflow = true;
333e07dc26bSmrg                    Assert(false);
334e07dc26bSmrg                    return false;
335e07dc26bSmrg                }
336e07dc26bSmrg
337e07dc26bSmrg                cbChunk = cbHwBufferAvail - pVBVA->cbPartialWriteThreshold;
338e07dc26bSmrg            }
339e07dc26bSmrg        }
340e07dc26bSmrg
341e07dc26bSmrg        Assert(cbChunk <= cb);
342e07dc26bSmrg        Assert(cbChunk <= vboxHwBufferAvail (pVBVA));
343e07dc26bSmrg
344e07dc26bSmrg        vboxHwBufferPlaceDataAt (pCtx, (uint8_t *)p + cbWritten, cbChunk, pVBVA->off32Free);
345e07dc26bSmrg
346e07dc26bSmrg        pVBVA->off32Free   = (pVBVA->off32Free + cbChunk) % pVBVA->cbData;
347e07dc26bSmrg        pRecord->cbRecord += cbChunk;
348e07dc26bSmrg        cbHwBufferAvail -= cbChunk;
349e07dc26bSmrg
350e07dc26bSmrg        cb        -= cbChunk;
351e07dc26bSmrg        cbWritten += cbChunk;
352e07dc26bSmrg    }
353e07dc26bSmrg
354e07dc26bSmrg    return true;
355e07dc26bSmrg}
356e07dc26bSmrg
357e07dc26bSmrg/*
358e07dc26bSmrg * Public writer to the hardware buffer.
359e07dc26bSmrg */
360e07dc26bSmrgDECLHIDDEN(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx,
361e07dc26bSmrg                               PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
362e07dc26bSmrg                               const void *pv, uint32_t cb)
363e07dc26bSmrg{
364e07dc26bSmrg    return vboxHwBufferWrite (pCtx, pHGSMICtx, pv, cb);
365e07dc26bSmrg}
366e07dc26bSmrg
367e07dc26bSmrgDECLHIDDEN(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code)
368e07dc26bSmrg{
369e07dc26bSmrg    VBVABUFFER *pVBVA = pCtx->pVBVA;
370e07dc26bSmrg
371e07dc26bSmrg    if (!pVBVA)
372e07dc26bSmrg    {
373e07dc26bSmrg        return false;
374e07dc26bSmrg    }
375e07dc26bSmrg
376e07dc26bSmrg    if (pVBVA->hostFlags.u32SupportedOrders & (1 << code))
377e07dc26bSmrg    {
378e07dc26bSmrg        return true;
379e07dc26bSmrg    }
380e07dc26bSmrg
381e07dc26bSmrg    return false;
382e07dc26bSmrg}
383e07dc26bSmrg
384e07dc26bSmrgDECLHIDDEN(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx,
385e07dc26bSmrg                                            uint32_t offVRAMBuffer,
386e07dc26bSmrg                                            uint32_t cbBuffer)
387e07dc26bSmrg{
388e07dc26bSmrg    pCtx->offVRAMBuffer = offVRAMBuffer;
389e07dc26bSmrg    pCtx->cbBuffer      = cbBuffer;
390e07dc26bSmrg}
391