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