VirtualBox

Changeset 67376 in vbox for trunk/src/VBox/Devices/Audio


Ignore:
Timestamp:
Jun 13, 2017 3:01:46 PM (8 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
116096
Message:

Audio/DevHDA: Forward ported / integrated (debug) DMA access handlers.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DevHDA.cpp

    r67362 r67376  
    5656//#define HDA_AS_PCI_EXPRESS
    5757#define VBOX_WITH_INTEL_HDA
     58
     59/* Installs a DMA access handler (via PGM callback) to monitor
     60 * HDA's DMA operations, that is, writing / reading audio stream data.
     61 *
     62 * !!! Note: Certain guests are *that* timing sensitive that when enabling  !!!
     63 * !!!       such a handler will mess up audio completely (e.g. Windows 7). !!! */
     64//#define HDA_USE_DMA_ACCESS_HANDLER
     65#ifdef HDA_USE_DMA_ACCESS_HANDLER
     66# include <VBox/vmm/pgm.h>
     67#endif
     68
     69/* Uses the DMA access handler to read the written DMA audio (output) data.
     70 * Only valid if HDA_USE_DMA_ACCESS_HANDLER is set.
     71 *
     72 * Also see the note / warning for HDA_USE_DMA_ACCESS_HANDLER. */
     73//# define HDA_USE_DMA_ACCESS_HANDLER_WRITING
    5874
    5975#if defined(VBOX_WITH_HP_HDA)
     
    551567    /** Circular buffer (FIFO) for holding DMA'ed data. */
    552568    R3PTRTYPE(PRTCIRCBUF)   pCircBuf;
     569# ifdef HDA_USE_DMA_ACCESS_HANDLER
     570    /** List of DMA handlers. */
     571    RTLISTANCHORR3          lstDMAHandlers;
     572#endif
    553573} HDASTREAMSTATE, *PHDASTREAMSTATE;
    554574
     
    574594    R3PTRTYPE(PAUDMIXSINK) pMixSink;
    575595} HDAMIXERSINK, *PHDAMIXERSINK;
     596
     597#if defined (DEBUG) || defined(HDA_USE_DMA_ACCESS_HANDLER)
     598typedef struct HDASTREAMDBGINFO
     599{
     600    /** Critical section to serialize access if needed. */
     601    RTCRITSECT              CritSect;
     602    uint32_t                Padding1[2];
     603    /** Number of total read accesses. */
     604    uint64_t                cReadsTotal;
     605    /** Number of total DMA bytes read. */
     606    uint64_t                cbReadTotal;
     607    /** Timestamp (in ns) of last read access. */
     608    uint64_t                tsLastReadNs;
     609    /** Number of total write accesses. */
     610    uint64_t                cWritesTotal;
     611    /** Number of total DMA bytes written. */
     612    uint64_t                cbWrittenTotal;
     613    /** Number of total write accesses since last iteration (Hz). */
     614    uint64_t                cWritesHz;
     615    /** Number of total DMA bytes written since last iteration (Hz). */
     616    uint64_t                cbWrittenHz;
     617    /** Timestamp (in ns) of beginning a new write slot. */
     618    uint64_t                tsWriteSlotBegin;
     619    /** Number of current silence samples in a (consecutive) row. */
     620    uint64_t                csSilence;
     621    /** Number of silent samples in a row to consider an audio block as audio gap (silence). */
     622    uint64_t                cSilenceThreshold;
     623    /** How many bytes to skip in an audio stream before detecting silence.
     624     *  (useful for intros and silence at the beginning of a song). */
     625    uint64_t                cbSilenceReadMin;
     626} HDASTREAMDBGINFO ,*PHDASTREAMDBGINFO;
     627#endif /* defined (DEBUG) || defined(HDA_USE_DMA_ACCESS_HANDLER) */
    576628
    577629/**
     
    614666    /** Internal state of this stream. */
    615667    HDASTREAMSTATE           State;
     668#ifdef DEBUG
     669    /** Debug information. */
     670    HDASTREAMDBGINFO         Dbg;
     671#endif
    616672} HDASTREAM, *PHDASTREAM;
    617673
     
    657713    R3PTRTYPE(PAUDMIXSTREAM)           pMixStrm;
    658714} HDADRIVERSTREAM, *PHDADRIVERSTREAM;
     715
     716#ifdef HDA_USE_DMA_ACCESS_HANDLER
     717typedef struct HDADMAACCESSHANDLER
     718{
     719    /** Node for storing this handler in our list in HDASTREAMSTATE. */
     720    RTLISTNODER3            Node;
     721    /** Pointer to stream to which this access handler is assigned to. */
     722    R3PTRTYPE(PHDASTREAM)   pStream;
     723    /** Access handler type handle. */
     724    PGMPHYSHANDLERTYPE      hAccessHandlerType;
     725    /** First address this handler uses. */
     726    RTGCPHYS                GCPhysFirst;
     727    /** Last address this handler uses. */
     728    RTGCPHYS                GCPhysLast;
     729    /** Actual BDLE address to handle. */
     730    RTGCPHYS                BDLEAddr;
     731    /** Actual BDLE buffer size to handle. */
     732    RTGCPHYS                BDLESize;
     733    /** Whether the access handler has been registered or not. */
     734    bool                    fRegistered;
     735    uint8_t                 Padding[3];
     736} HDADMAACCESSHANDLER, *PHDADMAACCESSHANDLER;
     737#endif
    659738
    660739/**
     
    887966static void          hdaStreamLock(PHDASTREAM pStream);
    888967static void          hdaStreamUnlock(PHDASTREAM pStream);
     968# ifdef HDA_USE_DMA_ACCESS_HANDLER
     969static bool          hdaStreamRegisterDMAHandlers(PHDASTATE pThis, PHDASTREAM pStream);
     970static void          hdaStreamUnregisterDMAHandlers(PHDASTATE pThis, PHDASTREAM pStream);
     971# endif
    889972#endif /* IN_RING3 */
    890973/** @} */
     
    9201003 */
    9211004#ifdef IN_RING3
    922 static void          hdaDoTransfers(PHDASTATE pThis);
     1005# ifdef HDA_USE_DMA_ACCESS_HANDLER
     1006static DECLCALLBACK(VBOXSTRICTRC) hdaDMAAccessHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser);
     1007# endif
     1008static void                       hdaDoTransfers(PHDASTATE pThis);
    9231009#endif /* IN_RING3 */
    9241010/** @} */
     
    16131699    AssertReturn(uSD <= HDA_MAX_STREAMS, VERR_INVALID_PARAMETER);
    16141700
     1701#ifdef HDA_USE_DMA_ACCESS_HANDLER
     1702    RTListInit(&pStream->State.lstDMAHandlers);
     1703#endif
     1704
    16151705    int rc = RTCritSectInit(&pStream->State.CritSect);
    16161706    if (RT_SUCCESS(rc))
     
    17461836    HDA_STREAM_REG(pThis, BDPL,  uSD) = 0;
    17471837
     1838#ifdef HDA_USE_DMA_ACCESS_HANDLER
     1839    hdaStreamUnregisterDMAHandlers(pThis, pStream);
     1840#endif
     1841
    17481842    int rc2 = hdaStreamInit(pThis, pStream, uSD);
    17491843    AssertRC(rc2);
     
    23322426    LogFunc(("[SD%RU8]: Updating LVI to %RU16\n", uSD, pStream->u16LVI));
    23332427
     2428# ifdef HDA_USE_DMA_ACCESS_HANDLER
     2429    if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT)
     2430    {
     2431        /* Try registering the DMA handlers.
     2432         * As we can't be sure in which order LVI + BDL base are set, try registering in both routines. */
     2433        if (hdaStreamRegisterDMAHandlers(pThis, pStream))
     2434            LogFunc(("[SD%RU8] DMA logging enabled\n", pStream->u8SD));
     2435    }
     2436# endif
     2437
    23342438    /* Reset BDLE state. */
    23352439    RT_ZERO(pStream->State.BDLE);
     
    28322936    RT_ZERO(pStream->State.BDLE);
    28332937    pStream->State.uCurBDLE = 0;
     2938
     2939# ifdef HDA_USE_DMA_ACCESS_HANDLER
     2940    if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_OUT)
     2941    {
     2942        /* Try registering the DMA handlers.
     2943         * As we can't be sure in which order LVI + BDL base are set, try registering in both routines. */
     2944        if (hdaStreamRegisterDMAHandlers(pThis, pStream))
     2945            LogFunc(("[SD%RU8] DMA logging enabled\n", pStream->u8SD));
     2946    }
     2947# endif
    28342948
    28352949    LogFlowFunc(("[SD%RU8]: BDLBase=0x%x\n", pStream->u8SD, pStream->u64BDLBase));
     
    29833097
    29843098#ifdef IN_RING3
     3099# ifdef HDA_USE_DMA_ACCESS_HANDLER
     3100/**
     3101 * Registers access handlers for a stream's BDLE DMA accesses.
     3102 *
     3103 * @returns @true if registration was successful, @false if not.
     3104 * @param   pThis               HDA state.
     3105 * @param   pStream             HDA stream to register BDLE access handlers for.
     3106 */
     3107static bool hdaStreamRegisterDMAHandlers(PHDASTATE pThis, PHDASTREAM pStream)
     3108{
     3109    /* At least LVI and the BDL base must be set. */
     3110    if (   !pStream->u16LVI
     3111        || !pStream->u64BDLBase)
     3112    {
     3113        return false;
     3114    }
     3115
     3116    hdaStreamUnregisterDMAHandlers(pThis, pStream);
     3117
     3118    LogFunc(("Registering ...\n"));
     3119
     3120    int rc = VINF_SUCCESS;
     3121
     3122    /*
     3123     * Create BDLE ranges.
     3124     */
     3125
     3126    struct BDLERANGE
     3127    {
     3128        RTGCPHYS uAddr;
     3129        uint32_t uSize;
     3130    } arrRanges[16]; /** @todo Use a define. */
     3131
     3132    size_t cRanges = 0;
     3133
     3134    for (uint16_t i = 0; i < pStream->u16LVI + 1; i++)
     3135    {
     3136        HDABDLE BDLE;
     3137        rc = hdaBDLEFetch(pThis, &BDLE, pStream->u64BDLBase, i /* Index */);
     3138        if (RT_FAILURE(rc))
     3139            break;
     3140
     3141        bool fAddRange = true;
     3142        BDLERANGE *pRange;
     3143
     3144        if (cRanges)
     3145        {
     3146            pRange = &arrRanges[cRanges - 1];
     3147
     3148            /* Is the current range a direct neighbor of the current BLDE? */
     3149            if ((pRange->uAddr + pRange->uSize) == BDLE.Desc.u64BufAdr)
     3150            {
     3151                /* Expand the current range by the current BDLE's size. */
     3152                pRange->uSize += BDLE.Desc.u32BufSize;
     3153
     3154                /* Adding a new range in this case is not needed anymore. */
     3155                fAddRange = false;
     3156
     3157                LogFunc(("Expanding range %zu by %RU32 (%RU32 total now)\n", cRanges - 1, BDLE.Desc.u32BufSize, pRange->uSize));
     3158            }
     3159        }
     3160
     3161        /* Do we need to add a new range? */
     3162        if (   fAddRange
     3163            && cRanges < RT_ELEMENTS(arrRanges))
     3164        {
     3165            pRange = &arrRanges[cRanges];
     3166
     3167            pRange->uAddr = BDLE.Desc.u64BufAdr;
     3168            pRange->uSize = BDLE.Desc.u32BufSize;
     3169
     3170            LogFunc(("Adding range %zu - 0x%x (%RU32)\n", cRanges, pRange->uAddr, pRange->uSize));
     3171
     3172            cRanges++;
     3173        }
     3174    }
     3175
     3176    LogFunc(("%zu ranges total\n", cRanges));
     3177
     3178    /*
     3179     * Register all ranges as DMA access handlers.
     3180     */
     3181
     3182    for (size_t i = 0; i < cRanges; i++)
     3183    {
     3184        BDLERANGE *pRange = &arrRanges[i];
     3185
     3186        PHDADMAACCESSHANDLER pHandler = (PHDADMAACCESSHANDLER)RTMemAllocZ(sizeof(HDADMAACCESSHANDLER));
     3187        if (!pHandler)
     3188        {
     3189            rc = VERR_NO_MEMORY;
     3190            break;
     3191        }
     3192
     3193        RTListAppend(&pStream->State.lstDMAHandlers, &pHandler->Node);
     3194
     3195        pHandler->pStream = pStream; /* Save a back reference to the owner. */
     3196
     3197        char szDesc[32];
     3198        RTStrPrintf(szDesc, sizeof(szDesc), "HDA[SD%RU8 - RANGE%02zu]", pStream->u8SD, i);
     3199
     3200        int rc2 = PGMR3HandlerPhysicalTypeRegister(PDMDevHlpGetVM(pThis->pDevInsR3), PGMPHYSHANDLERKIND_WRITE,
     3201                                                   hdaDMAAccessHandler,
     3202                                                   NULL, NULL, NULL,
     3203                                                   NULL, NULL, NULL,
     3204                                                   szDesc, &pHandler->hAccessHandlerType);
     3205        AssertRCBreak(rc2);
     3206
     3207        pHandler->BDLEAddr  = pRange->uAddr;
     3208        pHandler->BDLESize  = pRange->uSize;
     3209
     3210        /* Get first and last pages of the BDLE range. */
     3211        RTGCPHYS pgFirst = pRange->uAddr & ~PAGE_OFFSET_MASK;
     3212        RTGCPHYS pgLast  = RT_ALIGN(pgFirst + pRange->uSize, PAGE_SIZE);
     3213
     3214        /* Calculate the region size (in pages). */
     3215        RTGCPHYS regionSize = RT_ALIGN(pgLast - pgFirst, PAGE_SIZE);
     3216
     3217        pHandler->GCPhysFirst = pgFirst;
     3218        pHandler->GCPhysLast  = pHandler->GCPhysFirst + (regionSize - 1);
     3219
     3220        LogFunc(("\tRegistering region '%s': 0x%x - 0x%x (region size: %zu)\n",
     3221                 szDesc, pHandler->GCPhysFirst, pHandler->GCPhysLast, regionSize));
     3222        LogFunc(("\tBDLE @ 0x%x - 0x%x (%RU32)\n",
     3223                 pHandler->BDLEAddr, pHandler->BDLEAddr + pHandler->BDLESize, pHandler->BDLESize));
     3224
     3225        rc2 = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pThis->pDevInsR3),
     3226                                         pHandler->GCPhysFirst, pHandler->GCPhysLast,
     3227                                         pHandler->hAccessHandlerType, pHandler, NIL_RTR0PTR, NIL_RTRCPTR,
     3228                                         szDesc);
     3229        AssertRCBreak(rc2);
     3230
     3231        pHandler->fRegistered = true;
     3232    }
     3233
     3234    LogFunc(("Registration ended with rc=%Rrc\n", rc));
     3235
     3236    return RT_SUCCESS(rc);
     3237}
     3238
     3239/**
     3240 * Unregisters access handlers of a stream's BDLEs.
     3241 *
     3242 * @param   pThis               HDA state.
     3243 * @param   pStream             HDA stream to unregister BDLE access handlers for.
     3244 */
     3245static void hdaStreamUnregisterDMAHandlers(PHDASTATE pThis, PHDASTREAM pStream)
     3246{
     3247    LogFunc(("\n"));
     3248
     3249    PHDADMAACCESSHANDLER pHandler, pHandlerNext;
     3250    RTListForEachSafe(&pStream->State.lstDMAHandlers, pHandler, pHandlerNext, HDADMAACCESSHANDLER, Node)
     3251    {
     3252        if (!pHandler->fRegistered) /* Handler not registered? Skip. */
     3253            continue;
     3254
     3255        LogFunc(("Unregistering 0x%x - 0x%x (%zu)\n",
     3256                 pHandler->GCPhysFirst, pHandler->GCPhysLast, pHandler->GCPhysLast - pHandler->GCPhysFirst));
     3257
     3258        int rc2 = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pThis->pDevInsR3),
     3259                                               pHandler->GCPhysFirst);
     3260        AssertRC(rc2);
     3261
     3262        RTListNodeRemove(&pHandler->Node);
     3263
     3264        RTMemFree(pHandler);
     3265        pHandler = NULL;
     3266    }
     3267
     3268    Assert(RTListIsEmpty(&pStream->State.lstDMAHandlers));
     3269}
     3270# endif /* HDA_USE_DMA_ACCESS_HANDLER */
     3271
    29853272#ifdef LOG_ENABLED
    29863273static void hdaBDLEDumpAll(PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE)
     
    38084095    STAM_PROFILE_STOP(&pThis->StatTimer, a);
    38094096}
     4097
     4098#ifdef HDA_USE_DMA_ACCESS_HANDLER
     4099/**
     4100 * HC access handler for the FIFO.
     4101 *
     4102 * @returns VINF_SUCCESS if the handler have carried out the operation.
     4103 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
     4104 * @param   pVM             VM Handle.
     4105 * @param   pVCpu           The cross context CPU structure for the calling EMT.
     4106 * @param   GCPhys          The physical address the guest is writing to.
     4107 * @param   pvPhys          The HC mapping of that address.
     4108 * @param   pvBuf           What the guest is reading/writing.
     4109 * @param   cbBuf           How much it's reading/writing.
     4110 * @param   enmAccessType   The access type.
     4111 * @param   enmOrigin       Who is making the access.
     4112 * @param   pvUser          User argument.
     4113 */
     4114static DECLCALLBACK(VBOXSTRICTRC) hdaDMAAccessHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys,
     4115                                                      void *pvBuf, size_t cbBuf,
     4116                                                      PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
     4117{
     4118    RT_NOREF(pVM, pVCpu, pvPhys, pvBuf, enmOrigin);
     4119
     4120    PHDADMAACCESSHANDLER pHandler = (PHDADMAACCESSHANDLER)pvUser;
     4121    AssertPtr(pHandler);
     4122
     4123    PHDASTREAM pStream = pHandler->pStream;
     4124    AssertPtr(pStream);
     4125
     4126    Assert(GCPhys >= pHandler->GCPhysFirst);
     4127    Assert(GCPhys <= pHandler->GCPhysLast);
     4128    Assert(enmAccessType == PGMACCESSTYPE_WRITE);
     4129
     4130    /* Not within BDLE range? Bail out. */
     4131    if (   (GCPhys         < pHandler->BDLEAddr)
     4132        || (GCPhys + cbBuf > pHandler->BDLEAddr + pHandler->BDLESize))
     4133    {
     4134        return VINF_PGM_HANDLER_DO_DEFAULT;
     4135    }
     4136
     4137    switch(enmAccessType)
     4138    {
     4139        case PGMACCESSTYPE_WRITE:
     4140        {
     4141# ifdef DEBUG
     4142            PHDASTREAMDBGINFO pStreamDbg = &pStream->Dbg;
     4143
     4144            const uint64_t tsNowNs     = RTTimeNanoTS();
     4145            const uint32_t tsElapsedMs = (tsNowNs - pStreamDbg->tsWriteSlotBegin) / 1000 / 1000;
     4146
     4147            uint64_t cWritesHz   = ASMAtomicReadU64(&pStreamDbg->cWritesHz);
     4148            uint64_t cbWrittenHz = ASMAtomicReadU64(&pStreamDbg->cbWrittenHz);
     4149
     4150            if (tsElapsedMs >= (1000 / HDA_TIMER_HZ))
     4151            {
     4152                LogFunc(("[SD%RU8] %RU32ms elapsed, cbWritten=%RU64, cWritten=%RU64 -- %RU32 bytes on average per time slot (%zums)\n",
     4153                         pStream->u8SD, tsElapsedMs, cbWrittenHz, cWritesHz,
     4154                         ASMDivU64ByU32RetU32(cbWrittenHz, cWritesHz ? cWritesHz : 1), 1000 / HDA_TIMER_HZ));
     4155
     4156                pStreamDbg->tsWriteSlotBegin = tsNowNs;
     4157
     4158                cWritesHz   = 0;
     4159                cbWrittenHz = 0;
     4160            }
     4161
     4162            cWritesHz   += 1;
     4163            cbWrittenHz += cbBuf;
     4164
     4165            ASMAtomicIncU64(&pStreamDbg->cWritesTotal);
     4166            ASMAtomicAddU64(&pStreamDbg->cbWrittenTotal, cbBuf);
     4167
     4168            ASMAtomicWriteU64(&pStreamDbg->cWritesHz,   cWritesHz);
     4169            ASMAtomicWriteU64(&pStreamDbg->cbWrittenHz, cbWrittenHz);
     4170
     4171            LogFunc(("[SD%RU8] Writing %3zu @ 0x%x (off %zu)\n",
     4172                     pStream->u8SD, cbBuf, GCPhys, GCPhys - pHandler->BDLEAddr));
     4173
     4174            LogFunc(("[SD%RU8] cWrites=%RU64, cbWritten=%RU64 -> %RU32 bytes on average\n",
     4175                     pStream->u8SD, pStreamDbg->cWritesTotal, pStreamDbg->cbWrittenTotal,
     4176                     ASMDivU64ByU32RetU32(pStreamDbg->cbWrittenTotal, pStreamDbg->cWritesTotal)));
     4177# endif
     4178
     4179# ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
     4180            RTFILE fh;
     4181            RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "hdaDMAAccessWrite.pcm",
     4182                       RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
     4183            RTFileWrite(fh, pvBuf, cbBuf, NULL);
     4184            RTFileClose(fh);
     4185# endif
     4186
     4187# ifdef HDA_USE_DMA_ACCESS_HANDLER_WRITING
     4188            PRTCIRCBUF pCircBuf = pStream->State.pCircBuf;
     4189            AssertPtr(pCircBuf);
     4190
     4191            uint8_t *pbBuf = (uint8_t *)pvBuf;
     4192            while (cbBuf)
     4193            {
     4194                /* Make sure we only copy as much as the stream's FIFO can hold (SDFIFOS, 18.2.39). */
     4195                void *pvChunk;
     4196                size_t cbChunk;
     4197                RTCircBufAcquireWriteBlock(pCircBuf, cbBuf, &pvChunk, &cbChunk);
     4198
     4199                if (cbChunk)
     4200                {
     4201                    memcpy(pvChunk, pbBuf, cbChunk);
     4202
     4203                    pbBuf  += cbChunk;
     4204                    Assert(cbBuf >= cbChunk);
     4205                    cbBuf  -= cbChunk;
     4206                }
     4207                else
     4208                {
     4209                    //AssertMsg(RTCircBufFree(pCircBuf), ("No more space but still %zu bytes to write\n", cbBuf));
     4210                    break;
     4211                }
     4212
     4213                LogFunc(("[SD%RU8] cbChunk=%zu\n", pStream->u8SD, cbChunk));
     4214
     4215                RTCircBufReleaseWriteBlock(pCircBuf, cbChunk);
     4216            }
     4217# endif /* HDA_USE_DMA_ACCESS_HANDLER_WRITING */
     4218            break;
     4219        }
     4220
     4221        default:
     4222            AssertMsgFailed(("Access type not implemented\n"));
     4223            break;
     4224    }
     4225
     4226    return VINF_PGM_HANDLER_DO_DEFAULT;
     4227}
     4228#endif /* HDA_USE_DMA_ACCESS_HANDLER */
    38104229
    38114230/**
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette