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