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