DECLINLINE(uint64_t) hpetGetTicks(HpetState const *pThis) { /* * We can use any timer to get current time, they all go * with the same speed. */ return nsToHpetTicks(pThis, TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer)) + pThis->u64HpetOffset); }
/** * 32-bit write to a config register. * * @returns Strict VBox status code. * * @param pThis The HPET state. * @param idxReg The register being written to. * @param u32NewValue The value being written. * * @remarks The caller should not hold the device lock, unless it also holds * the TM lock. */ static int hpetConfigRegWrite32(HPET *pThis, uint32_t idxReg, uint32_t u32NewValue) { Assert(!PDMCritSectIsOwner(&pThis->CritSect) || TMTimerIsLockOwner(pThis->aTimers[0].CTX_SUFF(pTimer))); int rc = VINF_SUCCESS; switch (idxReg) { case HPET_ID: case HPET_ID + 4: { Log(("write HPET_ID, useless\n")); break; } case HPET_CFG: { DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE); uint32_t const iOldValue = (uint32_t)(pThis->u64HpetConfig); Log(("write HPET_CFG: %x (old %x)\n", u32NewValue, iOldValue)); /* * This check must be here, before actual update, as hpetLegacyMode * may request retry in R3 - so we must keep state intact. */ if ( ((iOldValue ^ u32NewValue) & HPET_CFG_LEGACY) && pThis->pHpetHlpR3 != NIL_RTR3PTR) { #ifdef IN_RING3 rc = pThis->pHpetHlpR3->pfnSetLegacyMode(pThis->pDevInsR3, RT_BOOL(u32NewValue & HPET_CFG_LEGACY)); if (rc != VINF_SUCCESS) #else rc = VINF_IOM_R3_MMIO_WRITE; #endif { DEVHPET_UNLOCK_BOTH(pThis); break; } } pThis->u64HpetConfig = hpetUpdateMasked(u32NewValue, iOldValue, HPET_CFG_WRITE_MASK); uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities); if (hpetBitJustSet(iOldValue, u32NewValue, HPET_CFG_ENABLE)) { /** @todo Only get the time stamp once when reprogramming? */ /* Enable main counter and interrupt generation. */ pThis->u64HpetOffset = hpetTicksToNs(pThis, pThis->u64HpetCounter) - TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer)); for (uint32_t i = 0; i < cTimers; i++) if (pThis->aTimers[i].u64Cmp != hpetInvalidValue(&pThis->aTimers[i])) hpetProgramTimer(&pThis->aTimers[i]); } else if (hpetBitJustCleared(iOldValue, u32NewValue, HPET_CFG_ENABLE)) { /* Halt main counter and disable interrupt generation. */ pThis->u64HpetCounter = hpetGetTicks(pThis); for (uint32_t i = 0; i < cTimers; i++) TMTimerStop(pThis->aTimers[i].CTX_SUFF(pTimer)); } DEVHPET_UNLOCK_BOTH(pThis); break; } case HPET_CFG + 4: { DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE); pThis->u64HpetConfig = hpetUpdateMasked((uint64_t)u32NewValue << 32, pThis->u64HpetConfig, UINT64_C(0xffffffff00000000)); Log(("write HPET_CFG + 4: %x -> %#llx\n", u32NewValue, pThis->u64HpetConfig)); DEVHPET_UNLOCK(pThis); break; } case HPET_STATUS: { DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE); /* Clear ISR for all set bits in u32NewValue, see p. 14 of the HPET spec. */ pThis->u64Isr &= ~((uint64_t)u32NewValue); Log(("write HPET_STATUS: %x -> ISR=%#llx\n", u32NewValue, pThis->u64Isr)); DEVHPET_UNLOCK(pThis); break; } case HPET_STATUS + 4: { Log(("write HPET_STATUS + 4: %x\n", u32NewValue)); if (u32NewValue != 0) { static unsigned s_cOccurrences = 0; if (s_cOccurrences++ < 10) LogRel(("Writing HPET_STATUS + 4 with non-zero, ignored\n")); } break; } case HPET_COUNTER: { DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE); pThis->u64HpetCounter = RT_MAKE_U64(u32NewValue, RT_HI_U32(pThis->u64HpetCounter)); Log(("write HPET_COUNTER: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter)); DEVHPET_UNLOCK(pThis); break; } case HPET_COUNTER + 4: { DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE); pThis->u64HpetCounter = RT_MAKE_U64(RT_LO_U32(pThis->u64HpetCounter), u32NewValue); Log(("write HPET_COUNTER + 4: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter)); DEVHPET_UNLOCK(pThis); break; } default: { static unsigned s_cOccurences = 0; if (s_cOccurences++ < 10) LogRel(("invalid HPET config write: %x\n", idxReg)); break; } } return rc; }