]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - ubuntu/vbox/vboxvideo/VBVABase.c
UBUNTU: ubuntu: vbox -- update to 5.1.6-dfsg-1
[mirror_ubuntu-zesty-kernel.git] / ubuntu / vbox / vboxvideo / VBVABase.c
1 /* $Id: VBVABase.cpp $ */
2 /** @file
3 * VirtualBox Video driver, common code - VBVA initialisation and helper
4 * functions.
5 */
6
7 /*
8 * Copyright (C) 2006-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19 #include <VBox/VBoxVideoGuest.h>
20 #include <VBox/VBoxVideo.h>
21 #include <VBox/err.h>
22 #include <VBox/log.h>
23 #include <iprt/assert.h>
24 #include <iprt/string.h>
25
26 /*
27 * There is a hardware ring buffer in the graphics device video RAM, formerly
28 * in the VBox VMMDev PCI memory space.
29 * All graphics commands go there serialized by VBoxVBVABufferBeginUpdate.
30 * and vboxHwBufferEndUpdate.
31 *
32 * off32Free is writing position. off32Data is reading position.
33 * off32Free == off32Data means buffer is empty.
34 * There must be always gap between off32Data and off32Free when data
35 * are in the buffer.
36 * Guest only changes off32Free, host changes off32Data.
37 */
38
39 /* Forward declarations of internal functions. */
40 static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx);
41 static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
42 uint32_t cb, uint32_t offset);
43 static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
44 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
45 const void *p, uint32_t cb);
46
47
48 static bool vboxVBVAInformHost(PVBVABUFFERCONTEXT pCtx,
49 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
50 int32_t cScreen, bool bEnable)
51 {
52 bool bRc = false;
53
54 #if 0 /* All callers check this */
55 if (ppdev->bHGSMISupported)
56 #endif
57 {
58 void *p = VBoxHGSMIBufferAlloc(pHGSMICtx,
59 sizeof (VBVAENABLE_EX),
60 HGSMI_CH_VBVA,
61 VBVA_ENABLE);
62 if (!p)
63 {
64 LogFunc(("HGSMIHeapAlloc failed\n"));
65 }
66 else
67 {
68 VBVAENABLE_EX *pEnable = (VBVAENABLE_EX *)p;
69
70 pEnable->Base.u32Flags = bEnable? VBVA_F_ENABLE: VBVA_F_DISABLE;
71 pEnable->Base.u32Offset = pCtx->offVRAMBuffer;
72 pEnable->Base.i32Result = VERR_NOT_SUPPORTED;
73 if (cScreen >= 0)
74 {
75 pEnable->Base.u32Flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET;
76 pEnable->u32ScreenId = cScreen;
77 }
78
79 VBoxHGSMIBufferSubmit(pHGSMICtx, p);
80
81 if (bEnable)
82 {
83 bRc = RT_SUCCESS(pEnable->Base.i32Result);
84 }
85 else
86 {
87 bRc = true;
88 }
89
90 VBoxHGSMIBufferFree(pHGSMICtx, p);
91 }
92 }
93
94 return bRc;
95 }
96
97 /*
98 * Public hardware buffer methods.
99 */
100 DECLHIDDEN(bool) VBoxVBVAEnable(PVBVABUFFERCONTEXT pCtx,
101 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
102 VBVABUFFER *pVBVA, int32_t cScreen)
103 {
104 bool bRc = false;
105
106 LogFlowFunc(("pVBVA %p\n", pVBVA));
107
108 #if 0 /* All callers check this */
109 if (ppdev->bHGSMISupported)
110 #endif
111 {
112 LogFunc(("pVBVA %p vbva off 0x%x\n", pVBVA, pCtx->offVRAMBuffer));
113
114 pVBVA->hostFlags.u32HostEvents = 0;
115 pVBVA->hostFlags.u32SupportedOrders = 0;
116 pVBVA->off32Data = 0;
117 pVBVA->off32Free = 0;
118 memset(pVBVA->aRecords, 0, sizeof (pVBVA->aRecords));
119 pVBVA->indexRecordFirst = 0;
120 pVBVA->indexRecordFree = 0;
121 pVBVA->cbPartialWriteThreshold = 256;
122 pVBVA->cbData = pCtx->cbBuffer - sizeof (VBVABUFFER) + sizeof (pVBVA->au8Data);
123
124 pCtx->fHwBufferOverflow = false;
125 pCtx->pRecord = NULL;
126 pCtx->pVBVA = pVBVA;
127
128 bRc = vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, true);
129 }
130
131 if (!bRc)
132 {
133 VBoxVBVADisable(pCtx, pHGSMICtx, cScreen);
134 }
135
136 return bRc;
137 }
138
139 DECLHIDDEN(void) VBoxVBVADisable(PVBVABUFFERCONTEXT pCtx,
140 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
141 int32_t cScreen)
142 {
143 LogFlowFunc(("\n"));
144
145 pCtx->fHwBufferOverflow = false;
146 pCtx->pRecord = NULL;
147 pCtx->pVBVA = NULL;
148
149 vboxVBVAInformHost(pCtx, pHGSMICtx, cScreen, false);
150
151 return;
152 }
153
154 DECLHIDDEN(bool) VBoxVBVABufferBeginUpdate(PVBVABUFFERCONTEXT pCtx,
155 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx)
156 {
157 bool bRc = false;
158
159 // LogFunc(("flags = 0x%08X\n", pCtx->pVBVA? pCtx->pVBVA->u32HostEvents: -1));
160
161 if ( pCtx->pVBVA
162 && (pCtx->pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED))
163 {
164 uint32_t indexRecordNext;
165
166 Assert(!pCtx->fHwBufferOverflow);
167 Assert(pCtx->pRecord == NULL);
168
169 indexRecordNext = (pCtx->pVBVA->indexRecordFree + 1) % VBVA_MAX_RECORDS;
170
171 if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
172 {
173 /* All slots in the records queue are used. */
174 vboxHwBufferFlush (pHGSMICtx);
175 }
176
177 if (indexRecordNext == pCtx->pVBVA->indexRecordFirst)
178 {
179 /* Even after flush there is no place. Fail the request. */
180 LogFunc(("no space in the queue of records!!! first %d, last %d\n",
181 pCtx->pVBVA->indexRecordFirst, pCtx->pVBVA->indexRecordFree));
182 }
183 else
184 {
185 /* Initialize the record. */
186 VBVARECORD *pRecord = &pCtx->pVBVA->aRecords[pCtx->pVBVA->indexRecordFree];
187
188 pRecord->cbRecord = VBVA_F_RECORD_PARTIAL;
189
190 pCtx->pVBVA->indexRecordFree = indexRecordNext;
191
192 // LogFunc(("indexRecordNext = %d\n", indexRecordNext));
193
194 /* Remember which record we are using. */
195 pCtx->pRecord = pRecord;
196
197 bRc = true;
198 }
199 }
200
201 return bRc;
202 }
203
204 DECLHIDDEN(void) VBoxVBVABufferEndUpdate(PVBVABUFFERCONTEXT pCtx)
205 {
206 VBVARECORD *pRecord;
207
208 // LogFunc(("\n"));
209
210 Assert(pCtx->pVBVA);
211
212 pRecord = pCtx->pRecord;
213 Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
214
215 /* Mark the record completed. */
216 pRecord->cbRecord &= ~VBVA_F_RECORD_PARTIAL;
217
218 pCtx->fHwBufferOverflow = false;
219 pCtx->pRecord = NULL;
220
221 return;
222 }
223
224 /*
225 * Private operations.
226 */
227 static uint32_t vboxHwBufferAvail (const VBVABUFFER *pVBVA)
228 {
229 int32_t i32Diff = pVBVA->off32Data - pVBVA->off32Free;
230
231 return i32Diff > 0? i32Diff: pVBVA->cbData + i32Diff;
232 }
233
234 static void vboxHwBufferFlush(PHGSMIGUESTCOMMANDCONTEXT pCtx)
235 {
236 /* Issue the flush command. */
237 void *p = VBoxHGSMIBufferAlloc(pCtx,
238 sizeof (VBVAFLUSH),
239 HGSMI_CH_VBVA,
240 VBVA_FLUSH);
241 if (!p)
242 {
243 LogFunc(("HGSMIHeapAlloc failed\n"));
244 }
245 else
246 {
247 VBVAFLUSH *pFlush = (VBVAFLUSH *)p;
248
249 pFlush->u32Reserved = 0;
250
251 VBoxHGSMIBufferSubmit(pCtx, p);
252
253 VBoxHGSMIBufferFree(pCtx, p);
254 }
255
256 return;
257 }
258
259 static void vboxHwBufferPlaceDataAt(PVBVABUFFERCONTEXT pCtx, const void *p,
260 uint32_t cb, uint32_t offset)
261 {
262 VBVABUFFER *pVBVA = pCtx->pVBVA;
263 uint32_t u32BytesTillBoundary = pVBVA->cbData - offset;
264 uint8_t *dst = &pVBVA->au8Data[offset];
265 int32_t i32Diff = cb - u32BytesTillBoundary;
266
267 if (i32Diff <= 0)
268 {
269 /* Chunk will not cross buffer boundary. */
270 memcpy (dst, p, cb);
271 }
272 else
273 {
274 /* Chunk crosses buffer boundary. */
275 memcpy (dst, p, u32BytesTillBoundary);
276 memcpy (&pVBVA->au8Data[0], (uint8_t *)p + u32BytesTillBoundary, i32Diff);
277 }
278
279 return;
280 }
281
282 static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx,
283 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
284 const void *p, uint32_t cb)
285 {
286 VBVARECORD *pRecord;
287 uint32_t cbHwBufferAvail;
288
289 uint32_t cbWritten = 0;
290
291 VBVABUFFER *pVBVA = pCtx->pVBVA;
292 Assert(pVBVA);
293
294 if (!pVBVA || pCtx->fHwBufferOverflow)
295 {
296 return false;
297 }
298
299 Assert(pVBVA->indexRecordFirst != pVBVA->indexRecordFree);
300
301 pRecord = pCtx->pRecord;
302 Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL));
303
304 // LogFunc(("%d\n", cb));
305
306 cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
307
308 while (cb > 0)
309 {
310 uint32_t cbChunk = cb;
311
312 // LogFunc(("pVBVA->off32Free %d, pRecord->cbRecord 0x%08X, cbHwBufferAvail %d, cb %d, cbWritten %d\n",
313 // pVBVA->off32Free, pRecord->cbRecord, cbHwBufferAvail, cb, cbWritten));
314
315 if (cbChunk >= cbHwBufferAvail)
316 {
317 LogFunc(("1) avail %d, chunk %d\n", cbHwBufferAvail, cbChunk));
318
319 vboxHwBufferFlush (pHGSMICtx);
320
321 cbHwBufferAvail = vboxHwBufferAvail (pVBVA);
322
323 if (cbChunk >= cbHwBufferAvail)
324 {
325 LogFunc(("no place for %d bytes. Only %d bytes available after flush. Going to partial writes.\n",
326 cb, cbHwBufferAvail));
327
328 if (cbHwBufferAvail <= pVBVA->cbPartialWriteThreshold)
329 {
330 LogFunc(("Buffer overflow!!!\n"));
331 pCtx->fHwBufferOverflow = true;
332 Assert(false);
333 return false;
334 }
335
336 cbChunk = cbHwBufferAvail - pVBVA->cbPartialWriteThreshold;
337 }
338 }
339
340 Assert(cbChunk <= cb);
341 Assert(cbChunk <= vboxHwBufferAvail (pVBVA));
342
343 vboxHwBufferPlaceDataAt (pCtx, (uint8_t *)p + cbWritten, cbChunk, pVBVA->off32Free);
344
345 pVBVA->off32Free = (pVBVA->off32Free + cbChunk) % pVBVA->cbData;
346 pRecord->cbRecord += cbChunk;
347 cbHwBufferAvail -= cbChunk;
348
349 cb -= cbChunk;
350 cbWritten += cbChunk;
351 }
352
353 return true;
354 }
355
356 /*
357 * Public writer to the hardware buffer.
358 */
359 DECLHIDDEN(bool) VBoxVBVAWrite(PVBVABUFFERCONTEXT pCtx,
360 PHGSMIGUESTCOMMANDCONTEXT pHGSMICtx,
361 const void *pv, uint32_t cb)
362 {
363 return vboxHwBufferWrite (pCtx, pHGSMICtx, pv, cb);
364 }
365
366 DECLHIDDEN(bool) VBoxVBVAOrderSupported(PVBVABUFFERCONTEXT pCtx, unsigned code)
367 {
368 VBVABUFFER *pVBVA = pCtx->pVBVA;
369
370 if (!pVBVA)
371 {
372 return false;
373 }
374
375 if (pVBVA->hostFlags.u32SupportedOrders & (1 << code))
376 {
377 return true;
378 }
379
380 return false;
381 }
382
383 DECLHIDDEN(void) VBoxVBVASetupBufferContext(PVBVABUFFERCONTEXT pCtx,
384 uint32_t offVRAMBuffer,
385 uint32_t cbBuffer)
386 {
387 pCtx->offVRAMBuffer = offVRAMBuffer;
388 pCtx->cbBuffer = cbBuffer;
389 }